From af8f55976f8a43ab8d748bca26e39e14935f26ae Mon Sep 17 00:00:00 2001 From: neon <18559658+neonsolstice@users.noreply.github.com> Date: Fri, 9 Oct 2026 01:01:57 +0000 Subject: [PATCH] feat(client): add the Nostalgia appearance style Add a Style setting beside mode, accent, radius and background. Nostalgia restyles the app through tokens only (neutral surface palettes, system type, flat shell, top glow) and pins background, opacity and brightness. Add a top glow reach slider for both styles. Tidy the theme layer first: charts read live tokens, bare `rounded` follows the radius scale, one shared sign-in toolbar. Also carries the settings page header redesign. --- client/index.html | 1 + client/public/theme-init.js | 35 +- client/public/topbar.png | Bin 14499 -> 0 bytes client/src/App.vue | 10 +- client/src/assets/main.css | 71 +++- client/src/assets/theme/accents.css | 259 +++++++-------- client/src/assets/theme/bridge.css | 7 +- client/src/assets/theme/styles/nostalgia.css | 119 +++++++ client/src/assets/theme/surfaces.css | 128 ++++++++ client/src/assets/theme/tokens.css | 120 ++++--- client/src/components/AppHeader.vue | 64 +++- client/src/components/AppLayout.vue | 8 +- client/src/components/BackgroundPicker.vue | 9 +- client/src/components/GlowReachPicker.vue | 37 +++ client/src/components/IconPicker.vue | 2 +- client/src/components/RadiusPicker.vue | 42 ++- client/src/components/SelectionActionBar.vue | 2 +- client/src/components/StylePicker.vue | 35 ++ ...acePicker.vue => SurfaceOpacityPicker.vue} | 0 .../src/components/SurfacePalettePicker.vue | 54 ++++ client/src/components/ThemePicker.vue | 6 +- .../components/__tests__/AppHeader.spec.ts | 7 +- .../__tests__/GlowReachPicker.spec.ts | 40 +++ .../components/__tests__/StylePicker.spec.ts | 39 +++ .../__tests__/SurfacePalettePicker.spec.ts | 52 +++ .../src/components/sidebar/SidebarBadge.vue | 4 +- .../src/components/sidebar/SidebarNavItem.vue | 2 +- .../sidebar/SidebarSectionHeader.vue | 9 +- .../sidebar/SidebarSectionPopover.vue | 2 +- client/src/components/sidebar/SidebarZone.vue | 5 +- client/src/components/ui/TokenSelect.vue | 2 +- client/src/components/ui/sidebar/Sidebar.vue | 2 +- .../view-header/ViewHeaderDisplayControls.vue | 4 +- .../__tests__/useThemeSync.spec.ts | 83 +++++ client/src/composables/useAppStyleScope.ts | 9 + client/src/composables/useChartTokens.ts | 9 + client/src/composables/useThemeSync.ts | 47 ++- client/src/features/admin/UserFormDrawer.vue | 4 +- client/src/features/admin/UsersPage.vue | 2 +- .../admin/components/NewAccountDefaults.vue | 2 +- .../components/UserPermissionsSection.vue | 8 +- .../components/UserRestrictionsSection.vue | 4 +- .../components/AnnotationBookCombobox.vue | 2 +- .../components/AnnotationBookThumb.vue | 2 +- .../components/AnnotationListItem.vue | 24 +- .../components/AnnotationSyncDetailPanel.vue | 6 +- .../__tests__/AnnotationListItem.spec.ts | 2 +- .../components/hub/HubSideRail.vue | 6 +- .../components/shared/AnnotationEntry.vue | 2 +- .../shared/AnnotationEntryDetail.vue | 2 +- .../components/shared/AnnotationStream.vue | 6 +- .../features/annotations/lib/pill-styles.ts | 2 +- .../src/features/auth/ForgotPasswordPage.vue | 211 +----------- client/src/features/auth/LoginPage.vue | 212 +----------- client/src/features/auth/RegisterPage.vue | 217 +------------ .../src/features/auth/ResetPasswordPage.vue | 211 +----------- client/src/features/auth/SetupPage.vue | 213 +----------- .../features/auth/__tests__/LoginPage.spec.ts | 17 +- .../auth/__tests__/RegisterPage.spec.ts | 17 +- .../auth/components/AuthAppearanceToolbar.vue | 124 +++++++ .../__tests__/AuthAppearanceToolbar.spec.ts | 88 +++++ .../author/components/AuthorPortrait.vue | 2 +- .../src/features/author/views/AuthorsView.vue | 4 +- .../components/BookDockBulkEditDialog.vue | 4 +- .../components/BookDockCoverPeek.vue | 4 +- .../components/BookDockFileListView.vue | 8 +- .../components/AuthorEnrichmentExpanded.vue | 10 +- .../AuthorEnrichmentReportModal.vue | 6 +- .../components/BookMetadataFetchExpanded.vue | 10 +- .../BookMetadataFetchReportModal.vue | 6 +- .../components/BookMetadataFetchWidget.vue | 4 +- .../book-requests/BookRequestsPage.vue | 2 +- .../components/BookMissingFormatRequest.vue | 4 +- .../components/ReleasePickerPanel.vue | 4 +- .../book-requests/components/RequestCover.vue | 4 +- .../components/RequestDetailPanel.vue | 2 +- .../components/RequestListSkeleton.vue | 8 +- .../components/RequestListTable.vue | 16 +- .../components/RequestSeedPanel.vue | 2 +- .../book/components/BookCoverCard.vue | 12 +- .../book/components/BookDownloadButton.vue | 22 +- .../book/components/BookFilterBuilder.vue | 4 +- .../features/book/components/BookListRow.vue | 10 +- .../book/components/BookQuickView.vue | 26 +- .../book/components/BookSortBuilder.vue | 4 +- .../components/BulkEditMetadataDialog.vue | 6 +- .../book/components/CollapsedSeriesCard.vue | 2 +- .../book/components/CoverMediumSwitch.vue | 2 +- .../book/components/FilterChipTypeahead.vue | 2 +- .../book/components/FilterSummary.vue | 2 +- .../components/KeyboardShortcutOverlay.vue | 2 +- .../book/components/TableColumnPanel.vue | 26 +- .../book/components/VirtualBookTable.vue | 10 +- .../__tests__/BookCoverCard.spec.ts | 4 +- .../book/components/detail/BookCarousel.vue | 2 +- .../detail/WriteAndRenameResultPanel.vue | 2 +- .../detail/details/BookEditionsCard.vue | 2 +- .../details/BookReadingActivityCard.vue | 2 +- .../detail/files/FileDetailCard.vue | 10 +- .../components/detail/files/FileListCard.vue | 6 +- .../components/detail/files/FileRoleBadge.vue | 2 +- .../components/detail/files/FilesHeroBar.vue | 2 +- .../components/detail/files/WriteBackCard.vue | 8 +- .../detail/highlights/HighlightsSideRail.vue | 2 +- .../detail/reading-log/ReadingLogAttempts.vue | 12 +- .../detail/reading-log/ReadingLogLedger.vue | 10 +- .../reading-log/ReadingLogProgressChart.vue | 14 +- .../detail/reading-log/ReadingLogRecords.vue | 2 +- .../reading-log/ReadingLogTraceChart.vue | 16 +- .../detail/reading-log/ReadingLogVitals.vue | 4 +- .../detail/tabs/AddBookFileModal.vue | 8 +- .../components/detail/tabs/DetailsTab.vue | 64 ++-- .../detail/tabs/EditMetadataTab.vue | 10 +- .../detail/tabs/HighlightsExportMenu.vue | 9 +- .../detail/tabs/MetadataSearchDrawer.vue | 2 +- .../detail/tabs/MetadataSourceCard.vue | 2 +- .../detail/tabs/ReadingLogExportMenu.vue | 4 +- .../components/detail/tabs/ReadingLogTab.vue | 2 +- .../metadata-match/MetadataMatchRail.vue | 6 +- .../metadata-match/MetadataMatchShortcuts.vue | 4 +- .../components/table/BookTableActionsCell.vue | 2 +- .../components/table/BookTableBooleanCell.vue | 2 +- .../table/BookTableCellDispatcher.vue | 2 +- .../components/table/BookTableChipsCell.vue | 2 +- .../components/table/BookTableContextMenu.vue | 19 +- .../components/table/BookTableFormatCell.vue | 2 +- .../table/BookTableHeaderContextMenu.vue | 20 +- .../table/BookTableMetadataScoreCell.vue | 2 +- .../components/table/BookTableNumberCell.vue | 4 +- .../table/BookTableReadButtonCell.vue | 2 +- .../table/BookTableReadStatusCell.vue | 2 +- .../components/table/BookTableTextCell.vue | 6 +- client/src/features/book/lib/format-colors.ts | 9 +- .../components/IconUploadDialog.vue | 2 +- .../components/DashboardPodcastScroller.vue | 4 +- .../components/DashboardSettingsSheet.vue | 14 +- .../widgets/CurrentlyReadingWidget.vue | 8 +- .../widgets/DiversityScoreWidget.vue | 2 +- .../widgets/HighlightOfTheDayWidget.vue | 6 +- .../widgets/LibraryOverviewWidget.vue | 4 +- .../components/widgets/LongWaitWidget.vue | 6 +- .../widgets/MonthlyChallengeWidget.vue | 2 +- .../widgets/NeglectedGemsWidget.vue | 6 +- .../components/widgets/ReadingDnaWidget.vue | 2 +- .../components/widgets/ReadingGoalWidget.vue | 7 +- .../widgets/ReadingRhythmWidget.vue | 4 +- .../widgets/ReadingStreakWidget.vue | 2 +- .../widgets/YearProjectionWidget.vue | 4 +- .../features/email/components/HistoryTab.vue | 2 +- .../email/components/ProvidersTab.vue | 14 +- .../email/components/RecipientsTab.vue | 6 +- .../email/components/SendBookDialog.vue | 6 +- .../email/components/TemplatesTab.vue | 8 +- .../components/HardcoverImportReviewModal.vue | 4 +- .../components/HardcoverImportStatus.vue | 2 +- .../library/components/BookUploadModal.vue | 6 +- .../library/components/FolderPickerModal.vue | 8 +- .../components/LibraryCreatorScanner.vue | 2 +- .../migration/components/MigrationModal.vue | 40 +-- .../components/MigrationSourceFields.vue | 4 +- .../components/NotificationLevelSegmented.vue | 2 +- .../components/PodcastAddFeedSheet.vue | 2 +- .../podcast/components/PodcastEpisodeRow.vue | 2 +- .../components/PodcastShortcutsDialog.vue | 2 +- .../podcast/components/PodcastShowCard.vue | 2 +- .../podcast/components/PodcastStatusChip.vue | 2 +- .../reader/audiobook/AudiobookReaderView.vue | 2 +- .../epub/components/DictionaryPopover.vue | 2 +- .../reader/epub/components/ReaderFooter.vue | 2 +- .../epub/components/ReaderSearchPanel.vue | 4 +- .../reader/epub/components/ReaderSidebar.vue | 6 +- .../reader/epub/components/SelectionPopup.vue | 4 +- .../epub/components/TranslationPanel.vue | 4 +- .../components/MediaOverlayMiniPlayer.vue | 2 +- .../reader/pdf-v4/components/PdfNoteCard.vue | 2 +- .../pdf-v4/components/PdfOutlineTree.vue | 2 +- .../pdf-v4/components/PdfSearchPanel.vue | 2 +- .../pdf-v4/components/PdfSelectionPopup.vue | 4 +- .../pdf-v4/components/PdfThumbnailGrid.vue | 4 +- .../src/features/series/views/SeriesView.vue | 12 +- .../src/features/settings/AccountSettings.vue | 2 +- .../settings/AppearanceIconsSettings.vue | 6 +- .../settings/AppearancePreferenceStorage.vue | 6 +- .../settings/AppearanceThemeSettings.vue | 64 +++- .../settings/AuthorEnrichmentSettings.vue | 6 +- .../features/settings/FontLibraryPanel.vue | 4 +- .../settings/IndexerManagersPanel.vue | 2 +- .../features/settings/MagicLinksSettings.vue | 4 +- .../features/settings/MigrationSettings.vue | 38 +-- client/src/features/settings/OidcSettings.vue | 8 +- .../src/features/settings/ReaderSettings.vue | 6 +- .../settings/RequestIndexersPanel.vue | 2 +- .../components/AuthorMetadataPreferences.vue | 2 +- .../components/NamingRuleEditor.vue | 4 +- .../file-naming/components/NamingRuleRail.vue | 2 +- .../CustomMetadataSettings.vue | 2 +- .../components/FieldRuleDetail.vue | 4 +- .../components/FieldRuleRow.vue | 2 +- .../components/FieldRulesScopeTabs.vue | 2 +- .../components/FieldRulesToolbar.vue | 6 +- .../components/MergeStrategySegmented.vue | 2 +- .../components/ProviderAddMenu.vue | 4 +- .../components/ProviderBulkMenu.vue | 2 +- .../components/ProviderChipList.vue | 2 +- .../components/ScoreRescoreProgress.vue | 2 +- .../components/ScoreWeightRow.vue | 4 +- .../components/ScoreWeightsToolbar.vue | 2 +- .../components/CreateSmartScopeDialog.vue | 4 +- .../statistics/components/StatisticsPage.vue | 10 +- .../library/GenreDistributionChart.vue | 9 +- .../library/LibraryIntegrityGaugeChart.vue | 5 +- ...ibraryMetadataCompletenessHeatmapChart.vue | 10 +- .../library/MetadataFreshnessGaugeChart.vue | 5 +- .../components/library/TopAuthorsChart.vue | 9 +- .../components/library/TopSeriesChart.vue | 9 +- .../user/FavoriteReadingDaysChart.vue | 6 +- .../user/GenreReadingTimeTreemapChart.vue | 9 +- .../components/user/PeakReadingHoursChart.vue | 6 +- .../components/user/ReadingClockChart.vue | 6 +- .../components/user/ReadingHeatmapChart.vue | 8 +- .../user/ReadingPaceScatterChart.vue | 6 +- .../user/ReadingSessionTimelineChart.vue | 6 +- .../user/SourceDistributionChart.vue | 6 +- .../features/statistics/lib/breakdown.test.ts | 4 +- .../lib/source-bucket-colors.test.ts | 66 +--- .../statistics/lib/source-bucket-colors.ts | 36 +-- .../components/StorygraphConnectionCard.vue | 4 +- .../components/StorygraphLinkedBooks.vue | 2 +- .../components/DismissedDuplicatesSection.vue | 2 +- .../components/DuplicateCopyCard.vue | 4 +- .../components/DuplicateLedgerRow.vue | 2 +- .../views/BookDuplicatesView.vue | 2 +- .../components/BulkRenameDetail.vue | 6 +- .../bulk-rename/components/PathDiffLine.vue | 2 +- .../components/BrowseMergeModal.vue | 2 +- .../components/BulkDeleteModal.vue | 2 +- .../entity-manager/components/DeleteModal.vue | 2 +- .../components/DismissedPairsSection.vue | 2 +- .../components/DuplicateCompare.vue | 6 +- .../components/EntityDataGrid.vue | 6 +- .../entity-manager/components/RenameModal.vue | 2 +- .../entity-manager/components/SplitModal.vue | 2 +- .../components/BrokenCoverRow.vue | 4 +- .../components/MissingBookRow.vue | 8 +- .../components/MissingResourceTable.vue | 2 +- .../components/OrphanedCoverRow.vue | 2 +- .../features/tts/TtsOpenAiVoiceCuration.vue | 18 +- .../tts/components/TtsAdminProviderCard.vue | 4 +- .../tts/components/TtsAdminProviderForm.vue | 2 +- .../features/tts/components/TtsMiniPlayer.vue | 2 +- .../tts/components/TtsVoicePicker.vue | 4 +- client/src/lib/css-color.test.ts | 39 +++ client/src/lib/css-color.ts | 91 ++++++ client/src/lib/echarts.test.ts | 70 ++-- client/src/lib/echarts.ts | 120 +++---- client/src/lib/heatmap-palette.ts | 2 +- client/src/lib/theme-accent-meta.test.ts | 4 +- client/src/lib/theme-styles.spec.ts | 44 +++ client/src/lib/theme-styles.ts | 91 ++++++ client/src/locales/en.json | 79 ++++- .../src/stores/__tests__/theme-init.spec.ts | 93 +++++- client/src/stores/__tests__/theme.spec.ts | 186 +++++++++++ client/src/stores/theme.ts | 303 +++++++++++------- client/src/views/BookDetailView.vue | 14 +- client/src/views/HomeView.vue | 2 +- client/src/views/SettingsView.vue | 39 ++- .../src/views/__tests__/SettingsView.spec.ts | 46 +-- packages/types/src/theme.ts | 43 +++ .../user-preferences.service.test.ts | 49 ++- .../user-preferences.service.ts | 7 + 270 files changed, 2972 insertions(+), 2317 deletions(-) delete mode 100644 client/public/topbar.png create mode 100644 client/src/assets/theme/styles/nostalgia.css create mode 100644 client/src/assets/theme/surfaces.css create mode 100644 client/src/components/GlowReachPicker.vue create mode 100644 client/src/components/StylePicker.vue rename client/src/components/{SurfacePicker.vue => SurfaceOpacityPicker.vue} (100%) create mode 100644 client/src/components/SurfacePalettePicker.vue create mode 100644 client/src/components/__tests__/GlowReachPicker.spec.ts create mode 100644 client/src/components/__tests__/StylePicker.spec.ts create mode 100644 client/src/components/__tests__/SurfacePalettePicker.spec.ts create mode 100644 client/src/composables/useAppStyleScope.ts create mode 100644 client/src/composables/useChartTokens.ts create mode 100644 client/src/features/auth/components/AuthAppearanceToolbar.vue create mode 100644 client/src/features/auth/components/__tests__/AuthAppearanceToolbar.spec.ts create mode 100644 client/src/lib/css-color.test.ts create mode 100644 client/src/lib/css-color.ts create mode 100644 client/src/lib/theme-styles.spec.ts create mode 100644 client/src/lib/theme-styles.ts diff --git a/client/index.html b/client/index.html index 3467bc698..aeb6517ce 100644 --- a/client/index.html +++ b/client/index.html @@ -10,6 +10,7 @@ + BookOrbit diff --git a/client/public/theme-init.js b/client/public/theme-init.js index 7b60aa368..d52260175 100644 --- a/client/public/theme-init.js +++ b/client/public/theme-init.js @@ -2,19 +2,42 @@ function readStoredValue(key, fallback) { var stored = localStorage.getItem(key) if (stored === null) return fallback + var value = stored try { - return JSON.parse(stored) + value = JSON.parse(stored) } catch { - return stored + value = stored } + return typeof value === 'string' ? value : fallback } + // Mirrors STANDALONE_ROUTE_PREFIXES in src/lib/theme-styles.ts; theme-init.spec.ts keeps them equal. + var STANDALONE_ROUTE_PREFIXES = ['/read/', '/login', '/register', '/setup', '/forgot-password', '/reset-password', '/oauth2-callback', '/magic'] + + function isAppShellPath(path) { + for (var i = 0; i < STANDALONE_ROUTE_PREFIXES.length; i++) { + var prefix = STANDALONE_ROUTE_PREFIXES[i] + if (path === prefix || path.indexOf(prefix.charAt(prefix.length - 1) === '/' ? prefix : prefix + '/') === 0) return false + } + return true + } + + var root = document.documentElement var theme = readStoredValue('theme', 'system') - var accent = readStoredValue('accent', 'neutral') + var accent = readStoredValue('accent', 'blue') var radius = readStoredValue('radius', 'default') + var style = readStoredValue('style', 'bookorbit') + var surfacePalette = readStoredValue('surfacePalette', 'steel') + var rawReach = localStorage.getItem('glowReach') + var storedReach = rawReach === null ? 80 : Number(rawReach) + var glowReach = isFinite(storedReach) ? Math.min(200, Math.max(0, storedReach)) : 80 var systemDark = typeof window.matchMedia === 'function' && window.matchMedia('(prefers-color-scheme: dark)').matches var isDark = theme === 'dark' || (theme !== 'light' && systemDark) - if (isDark) document.documentElement.classList.add('dark') - if (accent !== 'neutral') document.documentElement.classList.add('accent-' + accent) - if (radius !== 'default') document.documentElement.classList.add('radius-' + radius) + if (isDark) root.classList.add('dark') + root.classList.add('accent-' + accent) + if (radius !== 'default') root.classList.add('radius-' + radius) + root.setAttribute('data-style', isAppShellPath(window.location.pathname) ? style : 'bookorbit') + root.setAttribute('data-surface', surfacePalette) + root.style.setProperty('--glow-reach', String(glowReach / 100)) + root.setAttribute('data-glow', glowReach > 0 ? 'on' : 'off') })() diff --git a/client/public/topbar.png b/client/public/topbar.png deleted file mode 100644 index 6fb203c3bbfa41488b708e2ed28547f8943e2778..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14499 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 diff --git a/client/src/App.vue b/client/src/App.vue index 708dd650f..1afbd619a 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -4,8 +4,8 @@ import { useRoute } from 'vue-router' import { INIT_OPTIONS_KEY, THEME_KEY } from 'vue-echarts' import { APP_FEATURES } from '@bookorbit/types' import { useChangePasswordDialog } from '@/composables/useChangePasswordDialog' -import { useThemeStore } from '@/stores/theme' -import { getBookorbitThemeName, initChartThemes } from '@/lib/echarts' +import { useChartTokens } from '@/composables/useChartTokens' +import { buildChartTheme } from '@/lib/echarts' import ChangePasswordDialog from '@/features/auth/ChangePasswordDialog.vue' import WhatsNewDialog from '@/features/whats-new/WhatsNewDialog.vue' import { useWhatsNew } from '@/features/whats-new/composables/useWhatsNew' @@ -40,7 +40,7 @@ const TOASTER_MOBILE_OFFSET = { } const { isOpen } = useChangePasswordDialog() -const themeStore = useThemeStore() +const chartTokens = useChartTokens() const route = useRoute() const { user } = useAuth() @@ -79,12 +79,10 @@ watch( (name) => syncPopup(name as string | undefined), ) -initChartThemes() - provide(INIT_OPTIONS_KEY, { renderer: 'svg' }) provide( THEME_KEY, - computed(() => getBookorbitThemeName(themeStore.resolvedTheme, themeStore.accent)), + computed(() => buildChartTheme(chartTokens.value)), ) diff --git a/client/src/assets/main.css b/client/src/assets/main.css index e8538a074..b3c4d4e97 100644 --- a/client/src/assets/main.css +++ b/client/src/assets/main.css @@ -6,6 +6,8 @@ @import './theme/tokens.css'; @import './theme/accents.css'; @import './theme/radius.css'; +@import './theme/surfaces.css'; +@import './theme/styles/nostalgia.css'; @import './theme/bridge.css'; @import './theme/cover-effects.css'; @@ -252,11 +254,34 @@ } .glow-wrapper { - background-image: radial-gradient(50% 50% at center -25px, color-mix(in oklch, var(--primary) 35%, transparent) 0%, transparent 100%); - background-blend-mode: hard-light, color-dodge; + background-image: var(--glow-image); background-position: top; background-repeat: no-repeat; - background-size: auto 12rem; + background-size: auto calc(var(--glow-band) * var(--glow-reach)); +} + +/* The texture sits on its own layer so its tiles keep their size; the reach only moves the fade. Negative + z-index keeps it under the shell's content and the glow, like the background patterns. */ +.glow-wrapper::after { + content: ''; + position: absolute; + inset: 0 0 auto; + z-index: -1; + height: calc(var(--glow-band) * var(--glow-reach)); + pointer-events: none; + background-image: var(--glow-texture); + background-position: top center; + background-size: var(--glow-texture-size); + -webkit-mask-image: radial-gradient(ellipse 62% 100% at 50% 0%, #000 0%, transparent 100%); + mask-image: radial-gradient(ellipse 62% 100% at 50% 0%, #000 0%, transparent 100%); +} + +html[data-glow='off'] .glow-wrapper { + background-image: none; +} + +html[data-glow='off'] .glow-wrapper::after { + content: none; } @layer base { @@ -358,7 +383,7 @@ @layer components { .viewer-btn { - @apply flex items-center justify-center w-8 h-8 rounded text-foreground hover:bg-muted transition-colors shrink-0 disabled:opacity-30 disabled:cursor-not-allowed; + @apply flex items-center justify-center w-8 h-8 rounded-xs text-foreground hover:bg-muted transition-colors shrink-0 disabled:opacity-30 disabled:cursor-not-allowed; } .viewer-sep { @apply w-px h-5 mx-1 bg-border shrink-0; @@ -385,6 +410,44 @@ .settings-value { @apply text-sm font-medium tabular-nums text-foreground; } + /* ── App shell ───────────────────────────────────────────── + The glass behind the sidebar and header cards. A style swaps + --shell-backdrop to change it. + ────────────────────────────────────────────────────────────── */ + .shell-backdrop { + backdrop-filter: var(--shell-backdrop); + -webkit-backdrop-filter: var(--shell-backdrop); + } + .sidebar-section-label { + font-size: var(--sidebar-heading-size); + font-weight: var(--sidebar-heading-weight); + letter-spacing: var(--sidebar-heading-tracking); + color: var(--sidebar-heading-foreground); + text-transform: uppercase; + } + /* Count chips beside sidebar rows and collapsed sections. Colour utilities on the element still win. */ + .sidebar-count { + background-color: var(--sidebar-count-bg); + font-size: var(--sidebar-count-size); + font-weight: var(--sidebar-count-weight); + color: var(--sidebar-count-foreground); + } + + /* ── Book metadata chips ─────────────────────────────────── + Genres and tags on the book detail page. The ring is inset so + a style can add one without moving the genre fold measurement. + ────────────────────────────────────────────────────────────── */ + .chip-genre { + background-color: var(--chip-genre-bg); + color: var(--chip-genre-fg); + box-shadow: inset 0 0 0 1px var(--chip-genre-border); + } + .chip-tag { + background-color: var(--chip-tag-bg); + color: var(--chip-tag-fg); + border-color: var(--chip-tag-border); + } + /* ── Provider chips ──────────────────────────────────────── Callers set --provider-color; the ink/fill/ring mixes come from the theme so light mode darkens the brand hue enough to pass contrast. See tokens.css. diff --git a/client/src/assets/theme/accents.css b/client/src/assets/theme/accents.css index e59f4ac6f..c7eea518f 100644 --- a/client/src/assets/theme/accents.css +++ b/client/src/assets/theme/accents.css @@ -1,6 +1,7 @@ /* ── Accent presets ────────────────────────────────────────── Applied as a class on . Overrides --tint-h (surfaces) - and --primary (interactive elements). + and --accent-primary, which --primary reads unless a style + derives its own shade from it. Vivid: high chroma. Pastel: low chroma, muted feel. Yellow and yellow-green accents use dark foreground text where needed. ────────────────────────────────────────────────────────────── */ @@ -10,7 +11,7 @@ html.accent-white { --tint-h: 0; --tint-c: 0; - --primary: oklch(0.2 0 0); + --accent-primary: oklch(0.2 0 0); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.2 0 0); --sidebar: oklch(0.985 0 0); @@ -20,7 +21,7 @@ html.accent-white { html.accent-white.dark { --tint-h: 0; --tint-c: 0; - --primary: oklch(0.985 0 0); + --accent-primary: oklch(0.985 0 0); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.985 0 0); --sidebar: oklch(0.205 0 0); @@ -31,7 +32,7 @@ html.accent-white.dark { html.accent-grey { --tint-h: 0; --tint-c: 0; - --primary: oklch(0.55 0 0); + --accent-primary: oklch(0.55 0 0); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.55 0 0); --sidebar: oklch(0.96 0 0); @@ -41,7 +42,7 @@ html.accent-grey { html.accent-grey.dark { --tint-h: 0; --tint-c: 0; - --primary: oklch(0.75 0 0); + --accent-primary: oklch(0.75 0 0); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.75 0 0); --sidebar: oklch(0.22 0 0); @@ -51,14 +52,14 @@ html.accent-grey.dark { html.accent-scarlet { --tint-h: 2; - --primary: oklch(0.58 0.25 2); + --accent-primary: oklch(0.58 0.25 2); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.58 0.25 2); --sidebar-primary: oklch(0.58 0.25 2); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-scarlet.dark { - --primary: oklch(0.74 0.2 2); + --accent-primary: oklch(0.74 0.2 2); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.74 0.2 2); --sidebar-primary: oklch(0.74 0.2 2); @@ -67,14 +68,14 @@ html.accent-scarlet.dark { html.accent-rose { --tint-h: 15; - --primary: oklch(0.57 0.24 15); + --accent-primary: oklch(0.57 0.24 15); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.57 0.24 15); --sidebar-primary: oklch(0.57 0.24 15); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-rose.dark { - --primary: oklch(0.73 0.2 15); + --accent-primary: oklch(0.73 0.2 15); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.73 0.2 15); --sidebar-primary: oklch(0.73 0.2 15); @@ -83,14 +84,14 @@ html.accent-rose.dark { html.accent-orange { --tint-h: 42; - --primary: oklch(0.64 0.22 42); + --accent-primary: oklch(0.64 0.22 42); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.64 0.22 42); --sidebar-primary: oklch(0.64 0.22 42); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-orange.dark { - --primary: oklch(0.78 0.18 42); + --accent-primary: oklch(0.78 0.18 42); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.78 0.18 42); --sidebar-primary: oklch(0.78 0.18 42); @@ -99,14 +100,14 @@ html.accent-orange.dark { html.accent-marigold { --tint-h: 82; - --primary: oklch(0.76 0.18 82); + --accent-primary: oklch(0.76 0.18 82); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.76 0.18 82); --sidebar-primary: oklch(0.76 0.18 82); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-marigold.dark { - --primary: oklch(0.84 0.15 82); + --accent-primary: oklch(0.84 0.15 82); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.84 0.15 82); --sidebar-primary: oklch(0.84 0.15 82); @@ -115,14 +116,14 @@ html.accent-marigold.dark { html.accent-amber { --tint-h: 70; - --primary: oklch(0.72 0.17 70); + --accent-primary: oklch(0.72 0.17 70); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.72 0.17 70); --sidebar-primary: oklch(0.72 0.17 70); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-amber.dark { - --primary: oklch(0.82 0.17 70); + --accent-primary: oklch(0.82 0.17 70); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.82 0.17 70); --sidebar-primary: oklch(0.82 0.17 70); @@ -131,14 +132,14 @@ html.accent-amber.dark { html.accent-yellow { --tint-h: 95; - --primary: oklch(0.75 0.18 95); + --accent-primary: oklch(0.75 0.18 95); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.75 0.18 95); --sidebar-primary: oklch(0.75 0.18 95); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-yellow.dark { - --primary: oklch(0.84 0.16 95); + --accent-primary: oklch(0.84 0.16 95); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.84 0.16 95); --sidebar-primary: oklch(0.84 0.16 95); @@ -147,14 +148,14 @@ html.accent-yellow.dark { html.accent-viridian { --tint-h: 171; - --primary: oklch(0.54 0.17 171); + --accent-primary: oklch(0.54 0.17 171); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.54 0.17 171); --sidebar-primary: oklch(0.54 0.17 171); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-viridian.dark { - --primary: oklch(0.73 0.14 171); + --accent-primary: oklch(0.73 0.14 171); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.73 0.14 171); --sidebar-primary: oklch(0.73 0.14 171); @@ -163,14 +164,14 @@ html.accent-viridian.dark { html.accent-lime { --tint-h: 118; - --primary: oklch(0.64 0.2 118); + --accent-primary: oklch(0.64 0.2 118); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.64 0.2 118); --sidebar-primary: oklch(0.64 0.2 118); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-lime.dark { - --primary: oklch(0.77 0.18 118); + --accent-primary: oklch(0.77 0.18 118); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.77 0.18 118); --sidebar-primary: oklch(0.77 0.18 118); @@ -179,14 +180,14 @@ html.accent-lime.dark { html.accent-green { --tint-h: 142; - --primary: oklch(0.527 0.18 142); + --accent-primary: oklch(0.527 0.18 142); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.527 0.18 142); --sidebar-primary: oklch(0.527 0.18 142); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-green.dark { - --primary: oklch(0.72 0.18 142); + --accent-primary: oklch(0.72 0.18 142); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.72 0.18 142); --sidebar-primary: oklch(0.72 0.18 142); @@ -195,14 +196,14 @@ html.accent-green.dark { html.accent-emerald { --tint-h: 162; - --primary: oklch(0.52 0.17 162); + --accent-primary: oklch(0.52 0.17 162); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.52 0.17 162); --sidebar-primary: oklch(0.52 0.17 162); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-emerald.dark { - --primary: oklch(0.72 0.15 162); + --accent-primary: oklch(0.72 0.15 162); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.72 0.15 162); --sidebar-primary: oklch(0.72 0.15 162); @@ -211,14 +212,14 @@ html.accent-emerald.dark { html.accent-teal { --tint-h: 180; - --primary: oklch(0.52 0.18 180); + --accent-primary: oklch(0.52 0.18 180); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.52 0.18 180); --sidebar-primary: oklch(0.52 0.18 180); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-teal.dark { - --primary: oklch(0.74 0.16 180); + --accent-primary: oklch(0.74 0.16 180); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.74 0.16 180); --sidebar-primary: oklch(0.74 0.16 180); @@ -227,14 +228,14 @@ html.accent-teal.dark { html.accent-cyan { --tint-h: 197; - --primary: oklch(0.52 0.2 197); + --accent-primary: oklch(0.52 0.2 197); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.52 0.2 197); --sidebar-primary: oklch(0.52 0.2 197); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-cyan.dark { - --primary: oklch(0.75 0.17 197); + --accent-primary: oklch(0.75 0.17 197); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.75 0.17 197); --sidebar-primary: oklch(0.75 0.17 197); @@ -243,14 +244,14 @@ html.accent-cyan.dark { html.accent-jade { --tint-h: 155; - --primary: oklch(0.62 0.2 155); + --accent-primary: oklch(0.62 0.2 155); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.62 0.2 155); --sidebar-primary: oklch(0.62 0.2 155); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-jade.dark { - --primary: oklch(0.78 0.17 155); + --accent-primary: oklch(0.78 0.17 155); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.78 0.17 155); --sidebar-primary: oklch(0.78 0.17 155); @@ -259,14 +260,14 @@ html.accent-jade.dark { html.accent-iris { --tint-h: 270; - --primary: oklch(0.52 0.25 270); + --accent-primary: oklch(0.52 0.25 270); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.52 0.25 270); --sidebar-primary: oklch(0.52 0.25 270); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-iris.dark { - --primary: oklch(0.72 0.21 270); + --accent-primary: oklch(0.72 0.21 270); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.72 0.21 270); --sidebar-primary: oklch(0.72 0.21 270); @@ -275,14 +276,14 @@ html.accent-iris.dark { html.accent-blue { --tint-h: 263; - --primary: oklch(0.487 0.25 263); + --accent-primary: oklch(0.487 0.25 263); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.487 0.25 263); --sidebar-primary: oklch(0.487 0.25 263); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-blue.dark { - --primary: oklch(0.72 0.2 263); + --accent-primary: oklch(0.72 0.2 263); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.72 0.2 263); --sidebar-primary: oklch(0.72 0.2 263); @@ -291,14 +292,14 @@ html.accent-blue.dark { html.accent-indigo { --tint-h: 276; - --primary: oklch(0.51 0.26 276); + --accent-primary: oklch(0.51 0.26 276); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.51 0.26 276); --sidebar-primary: oklch(0.51 0.26 276); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-indigo.dark { - --primary: oklch(0.72 0.22 276); + --accent-primary: oklch(0.72 0.22 276); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.72 0.22 276); --sidebar-primary: oklch(0.72 0.22 276); @@ -307,14 +308,14 @@ html.accent-indigo.dark { html.accent-violet { --tint-h: 292; - --primary: oklch(0.491 0.27 292); + --accent-primary: oklch(0.491 0.27 292); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.491 0.27 292); --sidebar-primary: oklch(0.491 0.27 292); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-violet.dark { - --primary: oklch(0.72 0.23 292); + --accent-primary: oklch(0.72 0.23 292); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.72 0.23 292); --sidebar-primary: oklch(0.72 0.23 292); @@ -323,14 +324,14 @@ html.accent-violet.dark { html.accent-fuchsia { --tint-h: 312; - --primary: oklch(0.56 0.27 312); + --accent-primary: oklch(0.56 0.27 312); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.56 0.27 312); --sidebar-primary: oklch(0.56 0.27 312); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-fuchsia.dark { - --primary: oklch(0.75 0.22 312); + --accent-primary: oklch(0.75 0.22 312); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.75 0.22 312); --sidebar-primary: oklch(0.75 0.22 312); @@ -339,14 +340,14 @@ html.accent-fuchsia.dark { html.accent-pink { --tint-h: 328; - --primary: oklch(0.56 0.26 328); + --accent-primary: oklch(0.56 0.26 328); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.56 0.26 328); --sidebar-primary: oklch(0.56 0.26 328); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-pink.dark { - --primary: oklch(0.75 0.22 328); + --accent-primary: oklch(0.75 0.22 328); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.75 0.22 328); --sidebar-primary: oklch(0.75 0.22 328); @@ -357,14 +358,14 @@ html.accent-pink.dark { html.accent-rosewater { --tint-h: 2; - --primary: oklch(0.7 0.125 2); + --accent-primary: oklch(0.7 0.125 2); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.7 0.125 2); --sidebar-primary: oklch(0.7 0.125 2); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-rosewater.dark { - --primary: oklch(0.8 0.11 2); + --accent-primary: oklch(0.8 0.11 2); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.8 0.11 2); --sidebar-primary: oklch(0.8 0.11 2); @@ -373,14 +374,14 @@ html.accent-rosewater.dark { html.accent-coral { --tint-h: 15; - --primary: oklch(0.69 0.12 15); + --accent-primary: oklch(0.69 0.12 15); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.69 0.12 15); --sidebar-primary: oklch(0.69 0.12 15); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-coral.dark { - --primary: oklch(0.79 0.11 15); + --accent-primary: oklch(0.79 0.11 15); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.79 0.11 15); --sidebar-primary: oklch(0.79 0.11 15); @@ -389,14 +390,14 @@ html.accent-coral.dark { html.accent-peach { --tint-h: 42; - --primary: oklch(0.76 0.11 42); + --accent-primary: oklch(0.76 0.11 42); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.76 0.11 42); --sidebar-primary: oklch(0.76 0.11 42); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-peach.dark { - --primary: oklch(0.84 0.1 42); + --accent-primary: oklch(0.84 0.1 42); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.84 0.1 42); --sidebar-primary: oklch(0.84 0.1 42); @@ -405,14 +406,14 @@ html.accent-peach.dark { html.accent-flax { --tint-h: 82; - --primary: oklch(0.82 0.09 82); + --accent-primary: oklch(0.82 0.09 82); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.82 0.09 82); --sidebar-primary: oklch(0.82 0.09 82); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-flax.dark { - --primary: oklch(0.88 0.08 82); + --accent-primary: oklch(0.88 0.08 82); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.88 0.08 82); --sidebar-primary: oklch(0.88 0.08 82); @@ -421,14 +422,14 @@ html.accent-flax.dark { html.accent-butter { --tint-h: 70; - --primary: oklch(0.82 0.085 70); + --accent-primary: oklch(0.82 0.085 70); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.82 0.085 70); --sidebar-primary: oklch(0.82 0.085 70); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-butter.dark { - --primary: oklch(0.88 0.09 70); + --accent-primary: oklch(0.88 0.09 70); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.88 0.09 70); --sidebar-primary: oklch(0.88 0.09 70); @@ -437,14 +438,14 @@ html.accent-butter.dark { html.accent-lemon { --tint-h: 95; - --primary: oklch(0.82 0.09 95); + --accent-primary: oklch(0.82 0.09 95); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.82 0.09 95); --sidebar-primary: oklch(0.82 0.09 95); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-lemon.dark { - --primary: oklch(0.88 0.09 95); + --accent-primary: oklch(0.88 0.09 95); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.88 0.09 95); --sidebar-primary: oklch(0.88 0.09 95); @@ -453,14 +454,14 @@ html.accent-lemon.dark { html.accent-foam { --tint-h: 171; - --primary: oklch(0.68 0.085 171); + --accent-primary: oklch(0.68 0.085 171); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.68 0.085 171); --sidebar-primary: oklch(0.68 0.085 171); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-foam.dark { - --primary: oklch(0.79 0.08 171); + --accent-primary: oklch(0.79 0.08 171); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.79 0.08 171); --sidebar-primary: oklch(0.79 0.08 171); @@ -469,14 +470,14 @@ html.accent-foam.dark { html.accent-celadon { --tint-h: 118; - --primary: oklch(0.76 0.1 118); + --accent-primary: oklch(0.76 0.1 118); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.76 0.1 118); --sidebar-primary: oklch(0.76 0.1 118); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-celadon.dark { - --primary: oklch(0.83 0.1 118); + --accent-primary: oklch(0.83 0.1 118); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.83 0.1 118); --sidebar-primary: oklch(0.83 0.1 118); @@ -485,14 +486,14 @@ html.accent-celadon.dark { html.accent-sage { --tint-h: 142; - --primary: oklch(0.68 0.09 142); + --accent-primary: oklch(0.68 0.09 142); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.68 0.09 142); --sidebar-primary: oklch(0.68 0.09 142); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-sage.dark { - --primary: oklch(0.78 0.1 142); + --accent-primary: oklch(0.78 0.1 142); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.78 0.1 142); --sidebar-primary: oklch(0.78 0.1 142); @@ -501,14 +502,14 @@ html.accent-sage.dark { html.accent-mint { --tint-h: 162; - --primary: oklch(0.68 0.085 162); + --accent-primary: oklch(0.68 0.085 162); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.68 0.085 162); --sidebar-primary: oklch(0.68 0.085 162); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-mint.dark { - --primary: oklch(0.78 0.08 162); + --accent-primary: oklch(0.78 0.08 162); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.78 0.08 162); --sidebar-primary: oklch(0.78 0.08 162); @@ -517,14 +518,14 @@ html.accent-mint.dark { html.accent-seafoam { --tint-h: 180; - --primary: oklch(0.68 0.09 180); + --accent-primary: oklch(0.68 0.09 180); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.68 0.09 180); --sidebar-primary: oklch(0.68 0.09 180); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-seafoam.dark { - --primary: oklch(0.8 0.09 180); + --accent-primary: oklch(0.8 0.09 180); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.8 0.09 180); --sidebar-primary: oklch(0.8 0.09 180); @@ -533,14 +534,14 @@ html.accent-seafoam.dark { html.accent-powder { --tint-h: 197; - --primary: oklch(0.68 0.1 197); + --accent-primary: oklch(0.68 0.1 197); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.68 0.1 197); --sidebar-primary: oklch(0.68 0.1 197); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-powder.dark { - --primary: oklch(0.81 0.09 197); + --accent-primary: oklch(0.81 0.09 197); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.81 0.09 197); --sidebar-primary: oklch(0.81 0.09 197); @@ -549,14 +550,14 @@ html.accent-powder.dark { html.accent-sea-glass { --tint-h: 155; - --primary: oklch(0.74 0.1 155); + --accent-primary: oklch(0.74 0.1 155); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.74 0.1 155); --sidebar-primary: oklch(0.74 0.1 155); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-sea-glass.dark { - --primary: oklch(0.84 0.09 155); + --accent-primary: oklch(0.84 0.09 155); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.84 0.09 155); --sidebar-primary: oklch(0.84 0.09 155); @@ -565,14 +566,14 @@ html.accent-sea-glass.dark { html.accent-cornflower { --tint-h: 247; - --primary: oklch(0.68 0.14 247); + --accent-primary: oklch(0.68 0.14 247); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.68 0.14 247); --sidebar-primary: oklch(0.68 0.14 247); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-cornflower.dark { - --primary: oklch(0.76 0.12 247); + --accent-primary: oklch(0.76 0.12 247); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.76 0.12 247); --sidebar-primary: oklch(0.76 0.12 247); @@ -581,14 +582,14 @@ html.accent-cornflower.dark { html.accent-periwinkle { --tint-h: 263; - --primary: oklch(0.68 0.125 263); + --accent-primary: oklch(0.68 0.125 263); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.68 0.125 263); --sidebar-primary: oklch(0.68 0.125 263); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-periwinkle.dark { - --primary: oklch(0.78 0.11 263); + --accent-primary: oklch(0.78 0.11 263); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.78 0.11 263); --sidebar-primary: oklch(0.78 0.11 263); @@ -597,14 +598,14 @@ html.accent-periwinkle.dark { html.accent-wisteria { --tint-h: 276; - --primary: oklch(0.68 0.13 276); + --accent-primary: oklch(0.68 0.13 276); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.68 0.13 276); --sidebar-primary: oklch(0.68 0.13 276); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-wisteria.dark { - --primary: oklch(0.78 0.12 276); + --accent-primary: oklch(0.78 0.12 276); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.78 0.12 276); --sidebar-primary: oklch(0.78 0.12 276); @@ -613,14 +614,14 @@ html.accent-wisteria.dark { html.accent-lavender { --tint-h: 292; - --primary: oklch(0.68 0.135 292); + --accent-primary: oklch(0.68 0.135 292); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.68 0.135 292); --sidebar-primary: oklch(0.68 0.135 292); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-lavender.dark { - --primary: oklch(0.78 0.13 292); + --accent-primary: oklch(0.78 0.13 292); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.78 0.13 292); --sidebar-primary: oklch(0.78 0.13 292); @@ -629,14 +630,14 @@ html.accent-lavender.dark { html.accent-orchid { --tint-h: 312; - --primary: oklch(0.68 0.135 312); + --accent-primary: oklch(0.68 0.135 312); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.68 0.135 312); --sidebar-primary: oklch(0.68 0.135 312); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-orchid.dark { - --primary: oklch(0.81 0.12 312); + --accent-primary: oklch(0.81 0.12 312); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.81 0.12 312); --sidebar-primary: oklch(0.81 0.12 312); @@ -645,14 +646,14 @@ html.accent-orchid.dark { html.accent-blush { --tint-h: 328; - --primary: oklch(0.68 0.13 328); + --accent-primary: oklch(0.68 0.13 328); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.68 0.13 328); --sidebar-primary: oklch(0.68 0.13 328); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-blush.dark { - --primary: oklch(0.81 0.12 328); + --accent-primary: oklch(0.81 0.12 328); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.81 0.12 328); --sidebar-primary: oklch(0.81 0.12 328); @@ -661,14 +662,14 @@ html.accent-blush.dark { html.accent-vermilion { --tint-h: 28; - --primary: oklch(0.62 0.24 28); + --accent-primary: oklch(0.62 0.24 28); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.62 0.24 28); --sidebar-primary: oklch(0.62 0.24 28); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-vermilion.dark { - --primary: oklch(0.77 0.19 28); + --accent-primary: oklch(0.77 0.19 28); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.77 0.19 28); --sidebar-primary: oklch(0.77 0.19 28); @@ -677,14 +678,14 @@ html.accent-vermilion.dark { html.accent-salmon { --tint-h: 28; - --primary: oklch(0.74 0.12 28); + --accent-primary: oklch(0.74 0.12 28); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.74 0.12 28); --sidebar-primary: oklch(0.74 0.12 28); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-salmon.dark { - --primary: oklch(0.83 0.1 28); + --accent-primary: oklch(0.83 0.1 28); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.83 0.1 28); --sidebar-primary: oklch(0.83 0.1 28); @@ -693,14 +694,14 @@ html.accent-salmon.dark { html.accent-copper { --tint-h: 56; - --primary: oklch(0.65 0.19 56); + --accent-primary: oklch(0.65 0.19 56); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.65 0.19 56); --sidebar-primary: oklch(0.65 0.19 56); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-copper.dark { - --primary: oklch(0.79 0.16 56); + --accent-primary: oklch(0.79 0.16 56); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.79 0.16 56); --sidebar-primary: oklch(0.79 0.16 56); @@ -709,14 +710,14 @@ html.accent-copper.dark { html.accent-sand { --tint-h: 56; - --primary: oklch(0.77 0.095 56); + --accent-primary: oklch(0.77 0.095 56); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.77 0.095 56); --sidebar-primary: oklch(0.77 0.095 56); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-sand.dark { - --primary: oklch(0.85 0.09 56); + --accent-primary: oklch(0.85 0.09 56); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.85 0.09 56); --sidebar-primary: oklch(0.85 0.09 56); @@ -725,14 +726,14 @@ html.accent-sand.dark { html.accent-chartreuse { --tint-h: 106; - --primary: oklch(0.76 0.2 106); + --accent-primary: oklch(0.76 0.2 106); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.76 0.2 106); --sidebar-primary: oklch(0.76 0.2 106); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-chartreuse.dark { - --primary: oklch(0.83 0.17 106); + --accent-primary: oklch(0.83 0.17 106); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.83 0.17 106); --sidebar-primary: oklch(0.83 0.17 106); @@ -741,14 +742,14 @@ html.accent-chartreuse.dark { html.accent-pear { --tint-h: 106; - --primary: oklch(0.82 0.1 106); + --accent-primary: oklch(0.82 0.1 106); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.82 0.1 106); --sidebar-primary: oklch(0.82 0.1 106); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-pear.dark { - --primary: oklch(0.88 0.09 106); + --accent-primary: oklch(0.88 0.09 106); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.88 0.09 106); --sidebar-primary: oklch(0.88 0.09 106); @@ -757,14 +758,14 @@ html.accent-pear.dark { html.accent-wasabi { --tint-h: 130; - --primary: oklch(0.68 0.19 130); + --accent-primary: oklch(0.68 0.19 130); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.68 0.19 130); --sidebar-primary: oklch(0.68 0.19 130); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-wasabi.dark { - --primary: oklch(0.79 0.16 130); + --accent-primary: oklch(0.79 0.16 130); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.79 0.16 130); --sidebar-primary: oklch(0.79 0.16 130); @@ -773,14 +774,14 @@ html.accent-wasabi.dark { html.accent-sprout { --tint-h: 130; - --primary: oklch(0.8 0.095 130); + --accent-primary: oklch(0.8 0.095 130); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.8 0.095 130); --sidebar-primary: oklch(0.8 0.095 130); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-sprout.dark { - --primary: oklch(0.85 0.09 130); + --accent-primary: oklch(0.85 0.09 130); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.85 0.09 130); --sidebar-primary: oklch(0.85 0.09 130); @@ -789,14 +790,14 @@ html.accent-sprout.dark { html.accent-malachite { --tint-h: 152; - --primary: oklch(0.56 0.18 152); + --accent-primary: oklch(0.56 0.18 152); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.56 0.18 152); --sidebar-primary: oklch(0.56 0.18 152); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-malachite.dark { - --primary: oklch(0.73 0.15 152); + --accent-primary: oklch(0.73 0.15 152); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.73 0.15 152); --sidebar-primary: oklch(0.73 0.15 152); @@ -805,14 +806,14 @@ html.accent-malachite.dark { html.accent-aloe { --tint-h: 152; - --primary: oklch(0.68 0.09 152); + --accent-primary: oklch(0.68 0.09 152); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.68 0.09 152); --sidebar-primary: oklch(0.68 0.09 152); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-aloe.dark { - --primary: oklch(0.79 0.08 152); + --accent-primary: oklch(0.79 0.08 152); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.79 0.08 152); --sidebar-primary: oklch(0.79 0.08 152); @@ -821,14 +822,14 @@ html.accent-aloe.dark { html.accent-turquoise { --tint-h: 188; - --primary: oklch(0.58 0.18 188); + --accent-primary: oklch(0.58 0.18 188); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.58 0.18 188); --sidebar-primary: oklch(0.58 0.18 188); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-turquoise.dark { - --primary: oklch(0.75 0.15 188); + --accent-primary: oklch(0.75 0.15 188); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.75 0.15 188); --sidebar-primary: oklch(0.75 0.15 188); @@ -837,14 +838,14 @@ html.accent-turquoise.dark { html.accent-aqua { --tint-h: 188; - --primary: oklch(0.7 0.09 188); + --accent-primary: oklch(0.7 0.09 188); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.7 0.09 188); --sidebar-primary: oklch(0.7 0.09 188); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-aqua.dark { - --primary: oklch(0.81 0.08 188); + --accent-primary: oklch(0.81 0.08 188); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.81 0.08 188); --sidebar-primary: oklch(0.81 0.08 188); @@ -853,14 +854,14 @@ html.accent-aqua.dark { html.accent-acid-green { --tint-h: 112; - --primary: oklch(0.7 0.2 112); + --accent-primary: oklch(0.7 0.2 112); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.7 0.2 112); --sidebar-primary: oklch(0.7 0.2 112); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-acid-green.dark { - --primary: oklch(0.82 0.17 112); + --accent-primary: oklch(0.82 0.17 112); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.82 0.17 112); --sidebar-primary: oklch(0.82 0.17 112); @@ -869,14 +870,14 @@ html.accent-acid-green.dark { html.accent-pistachio { --tint-h: 112; - --primary: oklch(0.8 0.11 112); + --accent-primary: oklch(0.8 0.11 112); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.8 0.11 112); --sidebar-primary: oklch(0.8 0.11 112); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-pistachio.dark { - --primary: oklch(0.88 0.09 112); + --accent-primary: oklch(0.88 0.09 112); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.88 0.09 112); --sidebar-primary: oklch(0.88 0.09 112); @@ -885,14 +886,14 @@ html.accent-pistachio.dark { html.accent-electric-blue { --tint-h: 230; - --primary: oklch(0.54 0.24 230); + --accent-primary: oklch(0.54 0.24 230); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.54 0.24 230); --sidebar-primary: oklch(0.54 0.24 230); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-electric-blue.dark { - --primary: oklch(0.73 0.19 230); + --accent-primary: oklch(0.73 0.19 230); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.73 0.19 230); --sidebar-primary: oklch(0.73 0.19 230); @@ -901,14 +902,14 @@ html.accent-electric-blue.dark { html.accent-baby-blue { --tint-h: 230; - --primary: oklch(0.68 0.12 230); + --accent-primary: oklch(0.68 0.12 230); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.68 0.12 230); --sidebar-primary: oklch(0.68 0.12 230); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-baby-blue.dark { - --primary: oklch(0.79 0.1 230); + --accent-primary: oklch(0.79 0.1 230); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.79 0.1 230); --sidebar-primary: oklch(0.79 0.1 230); @@ -917,14 +918,14 @@ html.accent-baby-blue.dark { html.accent-ultramarine { --tint-h: 247; - --primary: oklch(0.5 0.28 247); + --accent-primary: oklch(0.5 0.28 247); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.5 0.28 247); --sidebar-primary: oklch(0.5 0.28 247); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-ultramarine.dark { - --primary: oklch(0.7 0.22 247); + --accent-primary: oklch(0.7 0.22 247); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.7 0.22 247); --sidebar-primary: oklch(0.7 0.22 247); @@ -933,14 +934,14 @@ html.accent-ultramarine.dark { html.accent-bluebell { --tint-h: 270; - --primary: oklch(0.68 0.125 270); + --accent-primary: oklch(0.68 0.125 270); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.68 0.125 270); --sidebar-primary: oklch(0.68 0.125 270); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-bluebell.dark { - --primary: oklch(0.78 0.12 270); + --accent-primary: oklch(0.78 0.12 270); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.78 0.12 270); --sidebar-primary: oklch(0.78 0.12 270); @@ -949,14 +950,14 @@ html.accent-bluebell.dark { html.accent-purple { --tint-h: 284; - --primary: oklch(0.51 0.25 284); + --accent-primary: oklch(0.51 0.25 284); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.51 0.25 284); --sidebar-primary: oklch(0.51 0.25 284); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-purple.dark { - --primary: oklch(0.72 0.21 284); + --accent-primary: oklch(0.72 0.21 284); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.72 0.21 284); --sidebar-primary: oklch(0.72 0.21 284); @@ -965,14 +966,14 @@ html.accent-purple.dark { html.accent-thistle { --tint-h: 284; - --primary: oklch(0.68 0.125 284); + --accent-primary: oklch(0.68 0.125 284); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.68 0.125 284); --sidebar-primary: oklch(0.68 0.125 284); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-thistle.dark { - --primary: oklch(0.78 0.12 284); + --accent-primary: oklch(0.78 0.12 284); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.78 0.12 284); --sidebar-primary: oklch(0.78 0.12 284); @@ -981,14 +982,14 @@ html.accent-thistle.dark { html.accent-amethyst { --tint-h: 302; - --primary: oklch(0.54 0.24 302); + --accent-primary: oklch(0.54 0.24 302); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.54 0.24 302); --sidebar-primary: oklch(0.54 0.24 302); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-amethyst.dark { - --primary: oklch(0.73 0.2 302); + --accent-primary: oklch(0.73 0.2 302); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.73 0.2 302); --sidebar-primary: oklch(0.73 0.2 302); @@ -997,14 +998,14 @@ html.accent-amethyst.dark { html.accent-mauve { --tint-h: 302; - --primary: oklch(0.68 0.12 302); + --accent-primary: oklch(0.68 0.12 302); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.68 0.12 302); --sidebar-primary: oklch(0.68 0.12 302); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-mauve.dark { - --primary: oklch(0.79 0.11 302); + --accent-primary: oklch(0.79 0.11 302); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.79 0.11 302); --sidebar-primary: oklch(0.79 0.11 302); @@ -1013,14 +1014,14 @@ html.accent-mauve.dark { html.accent-raspberry { --tint-h: 344; - --primary: oklch(0.56 0.23 344); + --accent-primary: oklch(0.56 0.23 344); --primary-foreground: oklch(0.985 0 0); --ring: oklch(0.56 0.23 344); --sidebar-primary: oklch(0.56 0.23 344); --sidebar-primary-foreground: oklch(0.985 0 0); } html.accent-raspberry.dark { - --primary: oklch(0.74 0.19 344); + --accent-primary: oklch(0.74 0.19 344); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.74 0.19 344); --sidebar-primary: oklch(0.74 0.19 344); @@ -1029,14 +1030,14 @@ html.accent-raspberry.dark { html.accent-rose-quartz { --tint-h: 344; - --primary: oklch(0.68 0.115 344); + --accent-primary: oklch(0.68 0.115 344); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.68 0.115 344); --sidebar-primary: oklch(0.68 0.115 344); --sidebar-primary-foreground: oklch(0.145 0 0); } html.accent-rose-quartz.dark { - --primary: oklch(0.8 0.1 344); + --accent-primary: oklch(0.8 0.1 344); --primary-foreground: oklch(0.145 0 0); --ring: oklch(0.8 0.1 344); --sidebar-primary: oklch(0.8 0.1 344); diff --git a/client/src/assets/theme/bridge.css b/client/src/assets/theme/bridge.css index daf165b8c..02e0b6af7 100644 --- a/client/src/assets/theme/bridge.css +++ b/client/src/assets/theme/bridge.css @@ -2,6 +2,7 @@ Maps CSS variables → Tailwind utility classes. ────────────────────────────────────────────────────────────── */ @theme inline { + --radius-xs: calc(var(--radius) - 6px); --radius-sm: calc(var(--radius) - 4px); --radius-md: calc(var(--radius) - 2px); --radius-lg: var(--radius); @@ -9,9 +10,9 @@ --radius-2xl: calc(var(--radius) + 8px); --radius-3xl: calc(var(--radius) + 12px); - --font-sans: 'Inter Variable', ui-sans-serif, system-ui, -apple-system, sans-serif; - --font-serif: 'Fraunces Variable', ui-serif, Georgia, serif; - --font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace; + --font-sans: var(--app-font-sans); + --font-serif: var(--app-font-serif); + --font-mono: var(--app-font-mono); --color-background: var(--background); --color-foreground: var(--foreground); diff --git a/client/src/assets/theme/styles/nostalgia.css b/client/src/assets/theme/styles/nostalgia.css new file mode 100644 index 000000000..79abbc92b --- /dev/null +++ b/client/src/assets/theme/styles/nostalgia.css @@ -0,0 +1,119 @@ +/* ── Nostalgia style ───────────────────────────────────────── + Neutral grounds from the surface palette, system type, a flat + shell and a tall accent glow over a faint grid of squares. + Only reassigns tokens, so components never ask which style is + active. Accent, radius and background stay the user's: the + accent is only re-shaded (from --accent-primary), never replaced. + + Same specificity as the accent classes and imported after them, + so where both set a token (--sidebar) the style wins. +────────────────────────────────────────────────────────────── */ +html[data-style='nostalgia'] { + --app-font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji'; + --app-font-serif: var(--app-font-sans); + + --background: var(--surface-ground); + --foreground: oklch(0.2 calc(var(--surface-c) * 0.6) var(--surface-h)); + --card: var(--surface-card); + --card-foreground: var(--foreground); + --popover: var(--card); + --popover-foreground: var(--foreground); + --secondary: oklch(calc(0.94 + var(--surface-l) * 0.4) calc(var(--surface-c) * 0.6) var(--surface-h)); + --secondary-foreground: var(--foreground); + --muted: oklch(calc(0.94 + var(--surface-l) * 0.4) calc(var(--surface-c) * 0.6) var(--surface-h)); + --muted-foreground: oklch(0.5 calc(var(--surface-c) * 0.6) var(--surface-h)); + --accent: oklch(calc(0.94 + var(--surface-l) * 0.4) calc(var(--surface-c) * 0.6) var(--surface-h)); + --accent-foreground: var(--foreground); + --border: var(--surface-edge); + --input: oklch(calc(0.85 + var(--surface-l) * 0.4) calc(var(--surface-c) * 1.2) var(--surface-h)); + + --surface-1: var(--card); + --surface-2: oklch(calc(0.965 + var(--surface-l) * 0.4) calc(var(--surface-c) * 0.6) var(--surface-h)); + --surface-3: var(--muted); + --surface-4: oklch(calc(0.915 + var(--surface-l) * 0.4) calc(var(--surface-c) * 0.6) var(--surface-h)); + --volume-unread: oklch(calc(0.86 + var(--surface-l) * 0.4) calc(var(--surface-c) * 0.8) var(--surface-h)); + --dot-color: oklch(calc(0.83 + var(--surface-l) * 0.4) calc(var(--surface-c) * 0.8) var(--surface-h)); + --scrollbar-thumb: oklch(0.8 calc(var(--surface-c) * 0.8) var(--surface-h)); + --scrollbar-thumb-hover: oklch(0.7 calc(var(--surface-c) * 0.8) var(--surface-h)); + + --sidebar: var(--card); + --sidebar-foreground: var(--foreground); + --sidebar-accent: var(--muted); + --sidebar-accent-foreground: var(--foreground); + --sidebar-border: var(--border); + /* Book counts wear the accent hue, held dark enough to clear AA on the sidebar card. */ + --sidebar-count-foreground: oklch(from var(--primary) min(l, 0.5) c h); + --sidebar-heading-foreground: var(--foreground); + --sidebar-heading-size: 12px; + --sidebar-heading-weight: 700; + --sidebar-heading-tracking: 0.06em; + --sidebar-count-bg: transparent; + --sidebar-count-size: 14px; + --sidebar-count-weight: 400; + /* The classic active row is a faint wash, so the accent text and count on it keep their full contrast. */ + --sidebar-active-bg: color-mix(in oklch, var(--primary) 7%, transparent); + --sidebar-active-weight: 700; + + --shell-border: var(--border); + --shell-backdrop: none; + --shell-shadow: 0 3px 5px oklch(0 0 0 / 0.02), 0 0 2px oklch(0 0 0 / 0.05), 0 1px 4px oklch(0 0 0 / 0.08); + --shell-accent-wash: color-mix(in oklch, var(--primary) 8%, transparent); + --shell-accent-tint: color-mix(in oklch, var(--primary) 16%, transparent); + + --glow-image: radial-gradient(50% 50% at center -25px, color-mix(in oklch, var(--primary) 38%, transparent) 0%, transparent 100%); + --glow-band: 20rem; + --glow-texture: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 84 84' fill='%23000'%3E%3Crect x='1' y='1' width='19' height='19' fill-opacity='0.044'/%3E%3Crect x='22' y='1' width='19' height='19' fill-opacity='0.058'/%3E%3Crect x='43' y='1' width='19' height='19' fill-opacity='0.042'/%3E%3Crect x='64' y='1' width='19' height='19' fill-opacity='0.035'/%3E%3Crect x='1' y='22' width='19' height='19' fill-opacity='0.059'/%3E%3Crect x='22' y='22' width='19' height='19' fill-opacity='0.02'/%3E%3Crect x='43' y='22' width='19' height='19' fill-opacity='0.054'/%3E%3Crect x='64' y='22' width='19' height='19' fill-opacity='0.03'/%3E%3Crect x='1' y='43' width='19' height='19' fill-opacity='0.024'/%3E%3Crect x='22' y='43' width='19' height='19' fill-opacity='0.023'/%3E%3Crect x='43' y='43' width='19' height='19' fill-opacity='0.031'/%3E%3Crect x='64' y='43' width='19' height='19' fill-opacity='0.052'/%3E%3Crect x='1' y='64' width='19' height='19' fill-opacity='0.026'/%3E%3Crect x='22' y='64' width='19' height='19' fill-opacity='0.042'/%3E%3Crect x='43' y='64' width='19' height='19' fill-opacity='0.045'/%3E%3Crect x='64' y='64' width='19' height='19' fill-opacity='0.034'/%3E%3C/svg%3E"); + --glow-texture-size: 107px 107px; + + --cover-hover-transform: translateY(-3px); + + --format-chip-fill: 18%; + --format-chip-border: 55%; + + --chip-genre-bg: oklch(0.6 0.2 25 / 0.12); + --chip-genre-fg: oklch(0.5 0.17 25); + --chip-genre-border: oklch(0.6 0.2 25 / 0.35); + --chip-tag-bg: oklch(0.65 0.17 150 / 0.12); + --chip-tag-fg: oklch(0.47 0.13 150); + --chip-tag-border: oklch(0.65 0.17 150 / 0.35); +} + +/* The classic ash ladder, shifted by the palette's --surface-l. Ground, card and edge come from surfaces.css. The style pins surface brightness to 0, so + --bg-lift adds nothing here. Light mode takes 40% of the shift: enough to tell greys apart, not to dim a page. */ +html[data-style='nostalgia'].dark { + /* Accents at their classic 400-shade brightness: the darker ones (greens, lime) lift, the rest already sit there. */ + --primary: oklch(from var(--accent-primary) max(l, 0.78) c h); + + --foreground: oklch(1 0 0); + --secondary: oklch(calc(0.307 + var(--bg-lift) + var(--surface-l)) var(--surface-c) var(--surface-h)); + --muted: oklch(calc(0.307 + var(--bg-lift) + var(--surface-l)) var(--surface-c) var(--surface-h)); + --muted-foreground: oklch(0.76 calc(var(--surface-c) * 0.6) var(--surface-h)); + --accent: oklch(calc(0.307 + var(--bg-lift) + var(--surface-l)) var(--surface-c) var(--surface-h)); + --input: oklch(calc(0.432 + var(--bg-lift) + var(--surface-l)) calc(var(--surface-c) * 1.4) var(--surface-h)); + + --surface-2: oklch(calc(0.262 + var(--bg-lift) + var(--surface-l)) var(--surface-c) var(--surface-h)); + --surface-4: oklch(calc(0.362 + var(--bg-lift) + var(--surface-l)) var(--surface-c) var(--surface-h)); + --volume-unread: oklch(calc(0.362 + var(--bg-lift) + var(--surface-l)) var(--surface-c) var(--surface-h)); + --dot-color: oklch(calc(0.302 + var(--bg-lift) + var(--surface-l)) var(--surface-c) var(--surface-h)); + --scrollbar-thumb: oklch(0.38 var(--surface-c) var(--surface-h)); + --scrollbar-thumb-hover: oklch(0.48 var(--surface-c) var(--surface-h)); + + --sidebar-count-foreground: oklch(from var(--primary) max(l, 0.72) c h); + + --glow-image: radial-gradient(50% 50% at center -25px, color-mix(in oklch, var(--primary) 60%, transparent) 0%, transparent 100%); + --glow-texture: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 84 84' fill='%23fff'%3E%3Crect x='1' y='1' width='19' height='19' fill-opacity='0.056'/%3E%3Crect x='22' y='1' width='19' height='19' fill-opacity='0.042'/%3E%3Crect x='43' y='1' width='19' height='19' fill-opacity='0.082'/%3E%3Crect x='64' y='1' width='19' height='19' fill-opacity='0.036'/%3E%3Crect x='1' y='22' width='19' height='19' fill-opacity='0.073'/%3E%3Crect x='22' y='22' width='19' height='19' fill-opacity='0.059'/%3E%3Crect x='43' y='22' width='19' height='19' fill-opacity='0.035'/%3E%3Crect x='64' y='22' width='19' height='19' fill-opacity='0.071'/%3E%3Crect x='1' y='43' width='19' height='19' fill-opacity='0.033'/%3E%3Crect x='22' y='43' width='19' height='19' fill-opacity='0.065'/%3E%3Crect x='43' y='43' width='19' height='19' fill-opacity='0.036'/%3E%3Crect x='64' y='43' width='19' height='19' fill-opacity='0.037'/%3E%3Crect x='1' y='64' width='19' height='19' fill-opacity='0.064'/%3E%3Crect x='22' y='64' width='19' height='19' fill-opacity='0.096'/%3E%3Crect x='43' y='64' width='19' height='19' fill-opacity='0.04'/%3E%3Crect x='64' y='64' width='19' height='19' fill-opacity='0.048'/%3E%3C/svg%3E"); + + /* Solid chips in a deep shade of each format's hue with white ink, as the classic look wore them; at this + lightness every format clears 5.4:1. */ + --format-chip-fill: 100%; + --format-chip-border: 100%; + --format-chip-fill-l: 0.5; + --format-chip-ink: oklch(1 0 0); + + --chip-genre-bg: oklch(0.64 0.2 25 / 0.18); + --chip-genre-fg: oklch(0.82 0.1 25); + --chip-genre-border: oklch(0.64 0.2 25 / 0.35); + --chip-tag-bg: oklch(0.72 0.17 150 / 0.18); + --chip-tag-fg: oklch(0.84 0.12 150); + --chip-tag-border: oklch(0.72 0.17 150 / 0.35); +} diff --git a/client/src/assets/theme/surfaces.css b/client/src/assets/theme/surfaces.css new file mode 100644 index 000000000..b19143d8f --- /dev/null +++ b/client/src/assets/theme/surfaces.css @@ -0,0 +1,128 @@ +/* ── Surface palettes ──────────────────────────────────────── + Neutral grounds for styles that declare a surface palette + control. A palette is a hue, a chroma and a small lightness + offset on the style's ladder (darker for onyx, lighter for + charcoal), bounded so every palette keeps the ladder's AA + contrast. Applied as data-surface on , or on a swatch + to preview a palette in place. +────────────────────────────────────────────────────────────── */ +[data-surface='onyx'] { + --surface-h: 0; + --surface-c: 0; + --surface-l: -0.06; +} +[data-surface='carbon'] { + --surface-h: 248; + --surface-c: 0.005; + --surface-l: -0.035; +} +[data-surface='neutral'] { + --surface-h: 0; + --surface-c: 0; + --surface-l: -0.015; +} +[data-surface='zinc'] { + --surface-h: 286; + --surface-c: 0.006; + --surface-l: -0.025; +} +[data-surface='iron'] { + --surface-h: 248; + --surface-c: 0.007; + --surface-l: 0; +} +[data-surface='steel'] { + --surface-h: 258; + --surface-c: 0.01; + --surface-l: -0.005; +} +[data-surface='ash'] { + --surface-h: 245; + --surface-c: 0.012; + --surface-l: 0; +} +[data-surface='smoke'] { + --surface-h: 270; + --surface-c: 0.006; + --surface-l: 0.005; +} +[data-surface='charcoal'] { + --surface-h: 0; + --surface-c: 0; + --surface-l: 0.025; +} +[data-surface='slate'] { + --surface-h: 262; + --surface-c: 0.03; + --surface-l: -0.038; +} +[data-surface='midnight-blue'] { + --surface-h: 252; + --surface-c: 0.022; + --surface-l: 0.027; +} +[data-surface='ocean'] { + --surface-h: 228; + --surface-c: 0.034; + --surface-l: 0.038; +} +[data-surface='pine'] { + --surface-h: 165; + --surface-c: 0.022; + --surface-l: 0; +} +[data-surface='olive'] { + --surface-h: 135; + --surface-c: 0.018; + --surface-l: 0.03; +} +[data-surface='stone'] { + --surface-h: 58; + --surface-c: 0.009; + --surface-l: -0.02; +} +[data-surface='sepia'] { + --surface-h: 70; + --surface-c: 0.024; + --surface-l: 0; +} +[data-surface='crimson'] { + --surface-h: 18; + --surface-c: 0.016; + --surface-l: 0.018; +} +[data-surface='plum'] { + --surface-h: 330; + --surface-c: 0.02; + --surface-l: 0; +} + +/* The ground, card and edge of the ladder, in the active mode, for whichever palette the element carries. The + Nostalgia style paints its surfaces from these, and a picker tile previews a palette with the very same values. */ +[data-surface] { + --surface-ground: oklch(calc(0.955 + var(--surface-l) * 0.4) calc(var(--surface-c) * 0.6) var(--surface-h)); + --surface-card: oklch(calc(0.99 + var(--surface-l) * 0.4) calc(var(--surface-c) * 0.3) var(--surface-h)); + --surface-edge: oklch(calc(0.87 + var(--surface-l) * 0.4) calc(var(--surface-c) * 1.2) var(--surface-h)); +} +.dark[data-surface], +.dark [data-surface] { + --surface-ground: oklch(calc(0.167 + var(--bg-lift) + var(--surface-l)) var(--surface-c) var(--surface-h)); + --surface-card: oklch(calc(0.232 + var(--bg-lift) + var(--surface-l)) var(--surface-c) var(--surface-h)); + --surface-edge: oklch(calc(0.402 + var(--bg-lift) + var(--surface-l)) calc(var(--surface-c) * 1.4) var(--surface-h)); +} + +/* Picker tile: a small window, ground behind a card panel. */ +.surface-tile { + position: relative; + overflow: hidden; + background-color: var(--surface-ground); + border: 1px solid var(--surface-edge); +} +.surface-tile::before { + content: ''; + position: absolute; + inset: 34% -1px -1px 26%; + border: 1px solid var(--surface-edge); + border-top-left-radius: 4px; + background-color: var(--surface-card); +} diff --git a/client/src/assets/theme/tokens.css b/client/src/assets/theme/tokens.css index 09674cd08..bed8aa13f 100644 --- a/client/src/assets/theme/tokens.css +++ b/client/src/assets/theme/tokens.css @@ -25,7 +25,9 @@ --popover: oklch(0.975 var(--tint-c-surface) var(--tint-h)); --popover-foreground: oklch(0.145 var(--tint-c-surface) var(--tint-h)); - --primary: oklch(0.21 var(--tint-c-surface) var(--tint-h)); + /* The accent class sets --accent-primary; a style may derive --primary from it instead. */ + --accent-primary: oklch(0.21 var(--tint-c-surface) var(--tint-h)); + --primary: var(--accent-primary); --primary-foreground: oklch(0.985 0.004 var(--tint-h)); --secondary: oklch(0.955 var(--tint-c-surface) var(--tint-h)); @@ -39,12 +41,6 @@ --destructive: oklch(0.577 0.245 27.325); --destructive-foreground: oklch(0.985 0 0); - --success: oklch(0.52 0.16 150); - --success-foreground: oklch(0.985 0 0); - --warning: oklch(0.57 0.15 75); - --warning-foreground: oklch(0.145 0 0); - --info: oklch(0.52 0.17 245); - --info-foreground: oklch(0.985 0 0); --success: oklch(0.52 0.14 148); --success-foreground: oklch(0.985 0 0); @@ -56,6 +52,7 @@ /* In flight: neither finished nor broken. Tuned per theme for the same reason. */ --info: oklch(0.55 0.12 255); + --info-foreground: oklch(0.985 0 0); --star-highlight: oklch(0.76 0.18 90); @@ -129,6 +126,16 @@ /* Shape */ --radius: 0.625rem; + /* The default surface palette (steel, as in surfaces.css), so a ladder that reads these is valid before data-surface lands. */ + --surface-h: 258; + --surface-c: 0.01; + --surface-l: -0.005; + + /* Type. The bridge maps the Tailwind font utilities onto these, so a style can swap them at runtime. */ + --app-font-sans: 'Inter Variable', ui-sans-serif, system-ui, -apple-system, sans-serif; + --app-font-serif: 'Fraunces Variable', ui-serif, Georgia, serif; + --app-font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace; + /* App shell (sidebar + header): one card gap, one material. --shell-surface-opacity is user-controlled and overwritten on . */ --shell-gap: 0.75rem; @@ -140,6 +147,43 @@ --shell-accent-tint: color-mix(in oklch, var(--primary) 12%, transparent); --shell-accent-line: color-mix(in oklch, var(--primary) 30%, transparent); --shell-border: var(--shell-accent-line); + --shell-backdrop: blur(24px) saturate(150%); + --shell-shadow: var(--elevation-lg); + + /* Accent glow behind the top of the shell (.glow-wrapper). --glow-band is the style's own reach and + --glow-reach the user's multiplier on it, written on . A texture keeps its tile size at any reach; + only the fade that reveals it follows the band. */ + --glow-reach: 0.8; + --glow-band: 12rem; + --glow-image: radial-gradient(50% 50% at center -25px, color-mix(in oklch, var(--primary) 35%, transparent) 0%, transparent 100%); + --glow-texture: none; + --glow-texture-size: auto; + + /* How a grid cover answers the pointer. */ + --cover-hover-transform: scale(1.02); + + /* Sidebar section labels, counts and the active navigation row. */ + --sidebar-heading-foreground: var(--muted-foreground); + --sidebar-heading-size: 11px; + --sidebar-heading-weight: 500; + --sidebar-heading-tracking: 0.14em; + --sidebar-count-bg: var(--shell-accent-tint); + --sidebar-count-size: 11px; + --sidebar-count-weight: 600; + --sidebar-active-bg: var(--shell-accent-tint); + --sidebar-active-weight: 500; + + /* How strongly a format chip wears its format colour (see formatChipStyle). */ + --format-chip-fill: 10%; + --format-chip-border: 40%; + + /* Book detail metadata chips. Tag ink is tuned like --warning so it clears AA on a light card. */ + --chip-genre-bg: var(--muted); + --chip-genre-fg: var(--foreground); + --chip-genre-border: transparent; + --chip-tag-bg: oklch(0.769 0.188 70.08 / 0.1); + --chip-tag-fg: oklch(0.52 0.13 68); + --chip-tag-border: oklch(0.769 0.188 70.08 / 0.3); --dot-color: oklch(0.82 var(--tint-c-surface) var(--tint-h)); @@ -250,31 +294,6 @@ --audit-collections: oklch(0.55 0.09 255); --audit-integrations: oklch(0.58 0.15 150); --audit-settings: oklch(0.55 0.15 25); - - /* File format, worn as a chip whose own text carries the colour. Tuned per theme for the same - reason every other text colour is: one set of hex values for both themes left the brightest - (yellow on white, around 1.9:1) unreadable in light mode and the darkest invisible in dark. */ - --pill-format-default: oklch(0.55 0.02 260); - --pill-format-epub: oklch(0.55 0.14 150); - --pill-format-kepub: oklch(0.55 0.12 178); - --pill-format-mobi: oklch(0.55 0.18 275); - --pill-format-azw3: oklch(0.53 0.11 185); - --pill-format-azw: oklch(0.55 0.14 235); - --pill-format-fb2: oklch(0.55 0.17 355); - --pill-format-djvu: oklch(0.55 0.04 255); - --pill-format-txt: oklch(0.55 0.03 60); - --pill-format-pdf: oklch(0.55 0.17 25); - --pill-format-cbz: oklch(0.55 0.16 260); - --pill-format-cbr: oklch(0.58 0.15 55); - --pill-format-cb7: oklch(0.55 0.19 300); - --pill-format-cbx: oklch(0.55 0.17 325); - --pill-format-m4b: oklch(0.58 0.13 75); - --pill-format-m4a: oklch(0.58 0.13 95); - --pill-format-mp3: oklch(0.56 0.15 145); - --pill-format-opus: oklch(0.55 0.11 205); - --pill-format-ogg: oklch(0.56 0.15 130); - --pill-format-flac: oklch(0.55 0.13 165); - --pill-format-readalong: oklch(0.52 0.1 186); } @media (min-width: 40rem) { @@ -304,7 +323,7 @@ --popover: oklch(calc(0.18 + var(--bg-lift)) var(--tint-c-surface) var(--tint-h)); --popover-foreground: oklch(0.985 0.001 var(--tint-h)); - --primary: oklch(0.91 var(--tint-c-surface) var(--tint-h)); + --accent-primary: oklch(0.91 var(--tint-c-surface) var(--tint-h)); --primary-foreground: oklch(0.18 var(--tint-c-surface) var(--tint-h)); --secondary: oklch(calc(0.245 + var(--bg-lift)) var(--tint-c-surface) var(--tint-h)); @@ -318,12 +337,6 @@ --destructive: oklch(0.704 0.191 22.216); --destructive-foreground: oklch(0.985 0 0); - --success: oklch(0.74 0.15 150); - --success-foreground: oklch(0.145 0 0); - --warning: oklch(0.78 0.14 75); - --warning-foreground: oklch(0.145 0 0); - --info: oklch(0.75 0.14 245); - --info-foreground: oklch(0.145 0 0); --success: oklch(0.72 0.15 148); --success-foreground: oklch(0.145 0 0); @@ -332,6 +345,7 @@ --warning-foreground: oklch(0.145 0 0); --info: oklch(0.72 0.12 255); + --info-foreground: oklch(0.145 0 0); --star-highlight: oklch(0.84 0.17 95); @@ -390,6 +404,8 @@ --scrollbar-thumb: oklch(0.35 var(--tint-c) var(--tint-h)); --scrollbar-thumb-hover: oklch(0.45 var(--tint-c) var(--tint-h)); + --chip-tag-fg: oklch(0.828 0.189 84.429); + /* Lifted lightness so pills read on dark surfaces */ --pill-web: oklch(0.72 0.14 245); --pill-ios: oklch(0.75 0.14 150); @@ -474,30 +490,4 @@ --audit-collections: oklch(0.72 0.09 255); --audit-integrations: oklch(0.75 0.13 150); --audit-settings: oklch(0.72 0.15 25); - - /* File format, worn as a chip whose own text carries the colour. Tuned per theme for the same - reason every other text colour is: one set of hex values for both themes left the brightest - (yellow on white, around 1.9:1) unreadable in light mode and the darkest invisible in dark. */ - - --pill-format-default: oklch(0.75 0.02 260); - --pill-format-epub: oklch(0.75 0.13 150); - --pill-format-kepub: oklch(0.75 0.11 178); - --pill-format-mobi: oklch(0.72 0.16 275); - --pill-format-azw3: oklch(0.76 0.11 185); - --pill-format-azw: oklch(0.72 0.13 235); - --pill-format-fb2: oklch(0.73 0.15 355); - --pill-format-djvu: oklch(0.75 0.04 255); - --pill-format-txt: oklch(0.76 0.03 60); - --pill-format-pdf: oklch(0.72 0.15 25); - --pill-format-cbz: oklch(0.72 0.14 260); - --pill-format-cbr: oklch(0.76 0.13 55); - --pill-format-cb7: oklch(0.72 0.17 300); - --pill-format-cbx: oklch(0.73 0.15 325); - --pill-format-m4b: oklch(0.79 0.13 75); - --pill-format-m4a: oklch(0.8 0.13 95); - --pill-format-mp3: oklch(0.78 0.15 145); - --pill-format-opus: oklch(0.76 0.11 205); - --pill-format-ogg: oklch(0.78 0.15 130); - --pill-format-flac: oklch(0.75 0.13 165); - --pill-format-readalong: oklch(0.78 0.11 186); } diff --git a/client/src/components/AppHeader.vue b/client/src/components/AppHeader.vue index ad913d0cf..979c45859 100644 --- a/client/src/components/AppHeader.vue +++ b/client/src/components/AppHeader.vue @@ -42,7 +42,10 @@ import RadiusPicker from '@/components/RadiusPicker.vue' import BackgroundPicker from '@/components/BackgroundPicker.vue' import ThemePicker from '@/components/ThemePicker.vue' import SurfaceBrightnessPicker from '@/components/SurfaceBrightnessPicker.vue' -import SurfacePicker from '@/components/SurfacePicker.vue' +import SurfaceOpacityPicker from '@/components/SurfaceOpacityPicker.vue' +import StylePicker from '@/components/StylePicker.vue' +import SurfacePalettePicker from '@/components/SurfacePalettePicker.vue' +import GlowReachPicker from '@/components/GlowReachPicker.vue' import { useGlobalSearch, type GlobalSearchResult } from '@/features/book/composables/useGlobalSearch' import BookCoverImage from '@/features/book/components/BookCoverImage.vue' import { useAuth } from '@/features/auth/composables/useAuth' @@ -71,6 +74,8 @@ const { onLibraryUploadCompleted } = useLibraryUploadEvents() const { subscribe: subscribeNotifications } = useNotifications() const { hasUnseen: hasUnseenWhatsNew } = useWhatsNew() const themeStore = useThemeStore() +const showSurfaceOpacity = computed(() => themeStore.appearanceControls.surfaceOpacity) +const showSurfaceBrightness = computed(() => themeStore.resolvedTheme === 'dark' && themeStore.appearanceControls.brightness) const localeStore = useLocaleStore() const { openLegalNotices } = useLegalNotices() const currentLanguageLabel = computed(() => LOCALE_LABELS[localeStore.locale]) @@ -343,7 +348,7 @@ function resultFormatEntries(result: GlobalSearchResult) {