feat: Light/dark modes
All checks were successful
Build and Push App Image / build-and-push (push) Successful in 1m48s

This commit is contained in:
domrichardson
2026-03-26 17:01:34 +00:00
parent 005a8f4cf0
commit d793b5ccf2
10 changed files with 375 additions and 4 deletions

View File

@@ -44,9 +44,20 @@
<!-- Search --> <!-- Search -->
<div class="search-box nav-search" v-if="!isAdminRoute"> <div class="search-box nav-search" v-if="!isAdminRoute">
<input type="text" class="form-control form-control-sm" placeholder="Search notes..." v-model="searchQuery" @keyup.enter="performSearch" /> <input type="text" class="form-control" placeholder="Search notes..." v-model="searchQuery" @keyup.enter="performSearch" />
</div> </div>
<!-- Theme Toggle -->
<button
class="btn btn-outline-light theme-toggle"
type="button"
:aria-label="isDarkMode ? 'Switch to light mode' : 'Switch to dark mode'"
:title="isDarkMode ? 'Switch to light mode' : 'Switch to dark mode'"
@click="isDarkMode = !isDarkMode"
>
<i :class="isDarkMode ? 'mdi mdi-weather-sunny' : 'mdi mdi-weather-night'" aria-hidden="true"></i>
</button>
<!-- User Menu --> <!-- User Menu -->
<div ref="userDropdownRef" class="dropdown nav-user-menu" v-if="currentUser" @mouseleave="showUserMenu = false"> <div ref="userDropdownRef" class="dropdown nav-user-menu" v-if="currentUser" @mouseleave="showUserMenu = false">
<button class="btn btn-outline-light dropdown-toggle" type="button" @click="toggleUserMenu"> <button class="btn btn-outline-light dropdown-toggle" type="button" @click="toggleUserMenu">
@@ -322,6 +333,13 @@ const shareCopied = ref(false);
const shareCopyTimeout = ref(null); const shareCopyTimeout = ref(null);
const noteViewMode = ref(localStorage.getItem("noteViewMode") || "grid"); const noteViewMode = ref(localStorage.getItem("noteViewMode") || "grid");
watch(noteViewMode, (val) => localStorage.setItem("noteViewMode", val)); watch(noteViewMode, (val) => localStorage.setItem("noteViewMode", val));
const isDarkMode = ref(localStorage.getItem("theme") === "dark");
const applyTheme = (dark) => {
document.documentElement.setAttribute("data-bs-theme", dark ? "dark" : "light");
localStorage.setItem("theme", dark ? "dark" : "light");
};
watch(isDarkMode, applyTheme);
applyTheme(isDarkMode.value);
const showUnlockModal = ref(false); const showUnlockModal = ref(false);
const unlockTargetNote = ref(null); const unlockTargetNote = ref(null);
const unlockPassword = ref(""); const unlockPassword = ref("");
@@ -1191,4 +1209,21 @@ const logout = () => {
width: 100%; width: 100%;
} }
} }
/* Dark mode overrides */
:root[data-bs-theme="dark"] .sidebar-header {
border-bottom-color: #3a3f4b;
}
:root[data-bs-theme="dark"] .breadcrumb-title {
color: #94a3b8;
}
:root[data-bs-theme="dark"] .breadcrumb-link {
color: #7aa2f7;
}
:root[data-bs-theme="dark"] .breadcrumb-separator {
color: #4a5568;
}
</style> </style>

View File

@@ -6,6 +6,75 @@
--border-color: #dee2e6; --border-color: #dee2e6;
} }
[data-bs-theme="dark"] {
--text-color: #e2e8f0;
--bg-color: #1a1d23;
--border-color: #3a3f4b;
}
[data-bs-theme="dark"] body {
background-color: #1a1d23;
color: #e2e8f0;
}
[data-bs-theme="dark"] .sidebar {
background-color: #21252e !important;
border-color: #3a3f4b !important;
}
[data-bs-theme="dark"] .toolbar {
background-color: #21252e;
border-color: #3a3f4b !important;
}
[data-bs-theme="dark"] .main-content {
background-color: #1a1d23;
}
[data-bs-theme="dark"] .markdown-body table {
background: #21252e;
}
[data-bs-theme="dark"] .markdown-body th {
background: #2a2f3a;
}
[data-bs-theme="dark"] .markdown-body tr:nth-child(even) td {
background: #232830;
}
[data-bs-theme="dark"] .markdown-body blockquote {
background: #1e2430;
color: #a0aec0;
}
[data-bs-theme="dark"] .markdown-body :not(pre) > code {
background: #2d3748;
color: #e2e8f0;
}
[data-bs-theme="dark"] .markdown-body pre code {
background: transparent;
color: inherit;
}
[data-bs-theme="dark"] .markdown-body pre {
background: #2d3748;
color: #e2e8f0;
}
[data-bs-theme="dark"] ::-webkit-scrollbar-track {
background: #2d3748;
}
[data-bs-theme="dark"] ::-webkit-scrollbar-thumb {
background: #4a5568;
}
[data-bs-theme="dark"] ::-webkit-scrollbar-thumb:hover {
background: #718096;
}
* { * {
margin: 0; margin: 0;
padding: 0; padding: 0;
@@ -70,7 +139,7 @@ body,
margin: 1rem 0; margin: 1rem 0;
padding: 1rem; padding: 1rem;
border-radius: 0.75rem; border-radius: 0.75rem;
background: #111827; background: #353943;
color: #f9fafb; color: #f9fafb;
overflow-x: auto; overflow-x: auto;
} }

View File

@@ -275,4 +275,53 @@ const handleDeleteCategory = (category) => {
.subcategories { .subcategories {
margin-top: 0.25rem; margin-top: 0.25rem;
} }
/* Dark mode overrides */
:root[data-bs-theme="dark"] .category-header:hover {
background-color: #2d3748;
}
:root[data-bs-theme="dark"] .menu-button {
color: #94a3b8;
}
:root[data-bs-theme="dark"] .menu-button:hover {
background-color: rgba(255, 255, 255, 0.08);
}
:root[data-bs-theme="dark"] .menu-dropdown {
background: #2d3748;
border-color: #4a5568;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.4);
}
:root[data-bs-theme="dark"] .menu-item {
color: #e2e8f0;
}
:root[data-bs-theme="dark"] .menu-item:hover {
background-color: #374151;
}
:root[data-bs-theme="dark"] .note-item:hover {
background-color: #2d3748;
}
:root[data-bs-theme="dark"] .note-item.is-pinned {
background: #1a3a5c;
border-color: #2d6a9f;
}
:root[data-bs-theme="dark"] .note-item.is-pinned:hover {
background: #1e4470;
}
:root[data-bs-theme="dark"] .note-item.is-featured {
background: #3a2e0a;
border-color: #7a5a0a;
}
:root[data-bs-theme="dark"] .note-item.is-featured:hover {
background: #453710;
}
</style> </style>

View File

@@ -328,4 +328,23 @@ watch(showNewFolderInput, async (v) => {
.file-item:hover .btn-delete { .file-item:hover .btn-delete {
opacity: 1; opacity: 1;
} }
/* Dark mode overrides */
:root[data-bs-theme="dark"] .file-explorer {
background: #21252e;
}
:root[data-bs-theme="dark"] .file-explorer-header {
background: #21252e;
border-color: #3a3f4b;
}
:root[data-bs-theme="dark"] .file-item {
border-bottom-color: #3a3f4b;
color: #e2e8f0;
}
:root[data-bs-theme="dark"] .file-item:hover {
background-color: #2d3748;
}
</style> </style>

View File

@@ -262,4 +262,20 @@ onMounted(loadProviders);
padding-right: 0.85rem; padding-right: 0.85rem;
} }
} }
/* Dark mode overrides */
:root[data-bs-theme="dark"] .modal-panel {
background: #21252e;
border-color: #3a3f4b;
}
:root[data-bs-theme="dark"] .provider-modal-header {
background: linear-gradient(180deg, #2a2f3a 0%, #21252e 100%);
border-bottom-color: #3a3f4b;
}
:root[data-bs-theme="dark"] .provider-section {
background: #2a2f3a;
border-color: #3a3f4b;
}
</style> </style>

View File

@@ -346,4 +346,31 @@ onMounted(async () => {
color: #7a2727; color: #7a2727;
font-size: 0.9rem; font-size: 0.9rem;
} }
/* Dark mode overrides */
:root[data-bs-theme="dark"] .editor-toolbar {
border-bottom-color: #3a3f4b;
}
:root[data-bs-theme="dark"] .flag-check {
background: #2d3748;
border-color: #4a5568;
}
:root[data-bs-theme="dark"] .preview-pane {
background-color: #21252e;
}
:root[data-bs-theme="dark"] .danger-zone {
background: #2d1a1a;
border-color: #7a3030;
}
:root[data-bs-theme="dark"] .danger-zone-title {
color: #fc8181;
}
:root[data-bs-theme="dark"] .danger-zone-copy {
color: #fca5a5;
}
</style> </style>

View File

@@ -180,6 +180,10 @@ const getDescription = (note) => {
.note-list--list .note-card:hover { .note-list--list .note-card:hover {
transform: none; transform: none;
box-shadow: none;
background-color: #eef2ff;
border-color: #667eea;
border-left: 3px solid #667eea;
} }
.note-list--list .note-title { .note-list--list .note-title {
@@ -218,4 +222,51 @@ const getDescription = (note) => {
font-size: 1.45rem; font-size: 1.45rem;
} }
} }
/* Dark mode overrides */
:root[data-bs-theme="dark"] .empty-notes-state {
border-color: #3a3f4b;
background: linear-gradient(180deg, #1e2430 0%, #21252e 100%);
}
:root[data-bs-theme="dark"] .empty-notes-title {
color: #e2e8f0;
}
:root[data-bs-theme="dark"] .empty-notes-message {
color: #94a3b8;
}
:root[data-bs-theme="dark"] .note-card {
border-color: #3a3f4b;
background-color: #21252e;
}
:root[data-bs-theme="dark"] .note-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
:root[data-bs-theme="dark"] .note-list--list .note-card:hover {
background-color: #2a2f3a;
border-color: #7aa2f7;
border-left-color: #7aa2f7;
}
:root[data-bs-theme="dark"] .note-title {
color: #e2e8f0;
}
:root[data-bs-theme="dark"] .note-preview {
color: #94a3b8;
}
:root[data-bs-theme="dark"] .note-card.is-pinned {
background: #1a3a5c;
border-color: #2d6a9f;
}
:root[data-bs-theme="dark"] .note-card.is-featured {
background: #3a2e0a;
border-color: #7a5a0a;
}
</style> </style>

View File

@@ -161,8 +161,6 @@ const formatDateTime = (dateString) => new Date(dateString).toLocaleString();
.markdown-body :deep(pre) { .markdown-body :deep(pre) {
padding: 1rem; padding: 1rem;
border-radius: 0.75rem; border-radius: 0.75rem;
background: #111827;
color: #f9fafb;
overflow-x: auto; overflow-x: auto;
} }
@@ -176,4 +174,49 @@ const formatDateTime = (dateString) => new Date(dateString).toLocaleString();
border-left: 4px solid #748ffc; border-left: 4px solid #748ffc;
background: #f8f9ff; background: #f8f9ff;
} }
/* Dark mode overrides */
:root[data-bs-theme="dark"] .note-meta {
border-bottom-color: #3a3f4b;
}
:root[data-bs-theme="dark"] .tag-chip {
background: #1e2d5f;
color: #93b4ff;
}
:root[data-bs-theme="dark"] .pinned-chip {
color: #7dd3fc;
background: #1a3a5c;
border-color: #2d6a9f;
}
:root[data-bs-theme="dark"] .featured-chip {
color: #fbbf24;
background: #3a2e0a;
border-color: #7a5a0a;
}
:root[data-bs-theme="dark"] .public-chip {
color: #67e8f9;
background: #0c2a3a;
border-color: #1d6a7a;
}
:root[data-bs-theme="dark"] .private-chip {
color: #c4b5fd;
background: #2d1f5e;
border-color: #5b3f9a;
}
:root[data-bs-theme="dark"] .protected-chip {
color: #fdba74;
background: #3a1f0a;
border-color: #7a4f1a;
}
:root[data-bs-theme="dark"] .markdown-body :deep(blockquote) {
background: #1e2430;
color: #94a3b8;
}
</style> </style>

View File

@@ -151,4 +151,34 @@ const goToPage = (page) => {
flex-direction: column; flex-direction: column;
} }
} }
/* Dark mode overrides */
:root[data-bs-theme="dark"] .search-results-header h2 {
color: #e2e8f0;
}
:root[data-bs-theme="dark"] .search-meta {
color: #94a3b8;
}
:root[data-bs-theme="dark"] .page-indicator {
color: #94a3b8;
}
:root[data-bs-theme="dark"] .empty-state {
border-color: #3a3f4b;
background: radial-gradient(circle at 20% 20%, #1a2035 0%, #1e2430 70%);
}
:root[data-bs-theme="dark"] .empty-state h3 {
color: #e2e8f0;
}
:root[data-bs-theme="dark"] .empty-state p {
color: #94a3b8;
}
:root[data-bs-theme="dark"] .empty-state-icon {
color: #4a6fa5;
}
</style> </style>

View File

@@ -919,4 +919,36 @@ onMounted(async () => {
gap: 0.65rem; gap: 0.65rem;
} }
} }
/* Dark mode overrides */
:root[data-bs-theme="dark"] .admin-topbar {
border-bottom-color: #3a3f4b;
}
:root[data-bs-theme="dark"] .admin-sidebar {
background: #21252e;
border-right-color: #3a3f4b;
}
:root[data-bs-theme="dark"] .admin-nav .nav-link {
color: #94a3b8;
}
:root[data-bs-theme="dark"] .admin-nav .nav-link:hover {
background: #2d3748;
color: #e2e8f0;
}
:root[data-bs-theme="dark"] .admin-nav .nav-link.active {
background: #e2e8f0;
color: #1a1d23;
}
:root[data-bs-theme="dark"] .user-meta-value {
color: #94a3b8;
}
:root[data-bs-theme="dark"] .admin-section {
background-color: #21252e;
}
</style> </style>