From 3f679b83f64b1a48688328f474c8f749986673c0 Mon Sep 17 00:00:00 2001 From: loki5512344 Date: Tue, 29 Sep 2026 10:55:18 +0200 Subject: [PATCH] feat(frontend): default site theme to Amber instead of Classic New visitors (no saved lv_theme) now get the Amber accent; reconciled the static CSS accent, demo scene colors and console greeting to match so there is no blue/emerald flash before the theme provider applies. Updated the default-theme assertions in the theme tests. --- frontend/src/features/themes/editor/tests/editor.test.tsx | 3 ++- frontend/src/features/themes/tests/activeTheme.test.tsx | 4 ++-- frontend/src/features/themes/useActiveTheme.ts | 6 ++++-- frontend/src/index.css | 7 ++++--- frontend/src/pages/app/ConfigsPage.tsx | 2 +- frontend/src/pages/showcase/ListingPage.tsx | 2 +- frontend/src/pages/showcase/ShowcasePage.tsx | 2 +- frontend/src/shared/easter/consoleGreeting.ts | 2 +- 8 files changed, 16 insertions(+), 12 deletions(-) diff --git a/frontend/src/features/themes/editor/tests/editor.test.tsx b/frontend/src/features/themes/editor/tests/editor.test.tsx index b5eb95a..c05a674 100644 --- a/frontend/src/features/themes/editor/tests/editor.test.tsx +++ b/frontend/src/features/themes/editor/tests/editor.test.tsx @@ -36,6 +36,7 @@ test('a theme card inside the preview loads that preset into the draft, not the await user.click(within(gui).getByRole('option', { name: 'Тема Nitro' })) expect(screen.getByDisplayValue('Nitro')).toBeInTheDocument() expect(within(gui).getByRole('option', { name: 'Тема Nitro' })).toHaveAttribute('aria-selected', 'true') - expect(screen.getByRole('status', { name: 'site theme' })).toHaveTextContent(PRESETS[0].id) + // The site keeps its default theme (amber) even though the draft loaded Classic. + expect(screen.getByRole('status', { name: 'site theme' })).toHaveTextContent('amber') expect(window.localStorage.getItem('lv_theme')).toBeNull() }) diff --git a/frontend/src/features/themes/tests/activeTheme.test.tsx b/frontend/src/features/themes/tests/activeTheme.test.tsx index 6b10b90..bc23410 100644 --- a/frontend/src/features/themes/tests/activeTheme.test.tsx +++ b/frontend/src/features/themes/tests/activeTheme.test.tsx @@ -21,7 +21,7 @@ afterEach(() => { test('a user pick persists to lv_theme, re-accents the page and locks out demo switches', () => { render() - expect(screen.getByTestId('id')).toHaveTextContent('classic') + expect(screen.getByTestId('id')).toHaveTextContent('amber') act(() => screen.getByText('demo').click()) expect(screen.getByTestId('id')).toHaveTextContent('sunset') expect(window.localStorage.getItem('lv_theme')).toBeNull() @@ -47,7 +47,7 @@ test('restores the stored theme and survives blocked storage', () => { throw new Error('blocked') }) render() - expect(screen.getByTestId('id')).toHaveTextContent('classic') + expect(screen.getByTestId('id')).toHaveTextContent('amber') act(() => screen.getByText('user').click()) expect(screen.getByTestId('id')).toHaveTextContent('nitro') vi.restoreAllMocks() diff --git a/frontend/src/features/themes/useActiveTheme.ts b/frontend/src/features/themes/useActiveTheme.ts index 53a9be2..68c805b 100644 --- a/frontend/src/features/themes/useActiveTheme.ts +++ b/frontend/src/features/themes/useActiveTheme.ts @@ -3,7 +3,9 @@ import { PRESETS } from './presets.generated' import { parseArgb, type ThemeEntry, withAlpha } from './types' const STORAGE_KEY = 'lv_theme' -const FALLBACK = PRESETS[0] +// Amber is the site default for visitors who never picked a theme; Classic stays at PRESETS[0] +// (mirroring the mod's own default) and is only reached if the amber preset ever goes missing. +const FALLBACK = PRESETS.find((p) => p.id === 'amber') ?? PRESETS[0] type ActiveTheme = { theme: ThemeEntry @@ -66,7 +68,7 @@ export function ActiveThemeProvider({ children, applyToPage = true }: { children return createElement(ActiveThemeContext.Provider, { value }, children) } -/** The active ClickGui theme. Outside a provider it falls back to Classic with a no-op setter. */ +/** The active ClickGui theme. Outside a provider it falls back to the default with a no-op setter. */ export function useActiveTheme(): ActiveTheme { return useContext(ActiveThemeContext) ?? { theme: FALLBACK, userPicked: false, setTheme: () => {} } } diff --git a/frontend/src/index.css b/frontend/src/index.css index 34d11b1..36b5ad2 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -9,8 +9,9 @@ --color-shoal: #1f3238; --color-frost: #9fb3b0; --color-snow: #eef5f3; - --color-ice: #34d399; --color-ember: #e8835a; + /* Matches the Amber preset (#FFE69A43) so first paint agrees with the default theme. */ + --color-ice: #e69a43; --font-display: "Unbounded Variable", system-ui, sans-serif; --font-sans: "Onest Variable", system-ui, sans-serif; @@ -29,12 +30,12 @@ @property --color-ice { syntax: ""; inherits: true; - initial-value: #34d399; + initial-value: #e69a43; } @property --page-accent-glow { syntax: ""; inherits: true; - initial-value: rgb(52 211 153 / 0.35); + initial-value: rgb(230 154 67 / 0.35); } @layer base { diff --git a/frontend/src/pages/app/ConfigsPage.tsx b/frontend/src/pages/app/ConfigsPage.tsx index e3c4e4c..0ec28f5 100644 --- a/frontend/src/pages/app/ConfigsPage.tsx +++ b/frontend/src/pages/app/ConfigsPage.tsx @@ -4,7 +4,7 @@ import { VoxelScene } from '@features/landing/effects/VoxelScene' import { SlotGrid } from '@features/configs/components/SlotGrid' import { PageTitle } from '@shared/layout/PageTitle' -const ACCENT = 'rgb(52, 211, 153)' +const ACCENT = 'rgb(230, 154, 67)' export default function ConfigsPage() { const { t } = useTranslation('configs') diff --git a/frontend/src/pages/showcase/ListingPage.tsx b/frontend/src/pages/showcase/ListingPage.tsx index 5e4f424..04890e9 100644 --- a/frontend/src/pages/showcase/ListingPage.tsx +++ b/frontend/src/pages/showcase/ListingPage.tsx @@ -3,7 +3,7 @@ import { ParticleField } from '@features/landing/effects/ParticleField' import { VoxelScene } from '@features/landing/effects/VoxelScene' import { ListingDetail } from '@features/showcase/components/ListingDetail' -const ACCENT = 'rgb(52, 211, 153)' +const ACCENT = 'rgb(230, 154, 67)' export default function ListingPage() { const { id } = useParams<{ id: string }>() diff --git a/frontend/src/pages/showcase/ShowcasePage.tsx b/frontend/src/pages/showcase/ShowcasePage.tsx index c0af471..ae1ecbf 100644 --- a/frontend/src/pages/showcase/ShowcasePage.tsx +++ b/frontend/src/pages/showcase/ShowcasePage.tsx @@ -4,7 +4,7 @@ import { VoxelScene } from '@features/landing/effects/VoxelScene' import { ListingList } from '@features/showcase/components/ListingList' import { PageTitle } from '@shared/layout/PageTitle' -const ACCENT = 'rgb(52, 211, 153)' +const ACCENT = 'rgb(230, 154, 67)' export default function ShowcasePage() { const { t } = useTranslation('showcase') diff --git a/frontend/src/shared/easter/consoleGreeting.ts b/frontend/src/shared/easter/consoleGreeting.ts index 3716b97..5959959 100644 --- a/frontend/src/shared/easter/consoleGreeting.ts +++ b/frontend/src/shared/easter/consoleGreeting.ts @@ -2,7 +2,7 @@ import { LOGO_ART } from './art.generated' export function printConsoleGreeting(lang: string): void { const ru = lang.startsWith('ru') - console.log(`%c${LOGO_ART}`, 'color:#34d399;font-family:monospace;font-weight:bold') + console.log(`%c${LOGO_ART}`, 'color:#e69a43;font-family:monospace;font-weight:bold') console.log( `%c${ru ? 'Открыл DevTools? Значит, тебе тут рады.' : 'Opened DevTools? You are welcome here.'}`, 'color:#eef5f3;font-size:13px',