feat(design): v2 opt-in (atmospheric dusks) — Settings toggle, cookie-mirrored
CI / test (push) Has been cancelled
CI / test (push) Has been cancelled
Lets users opt into the new atmospheric design without affecting users on v1.
Adds a beta-flag toggle in Settings → Design. Server-side preference persists
across devices; a cookie mirrors it so unauthenticated Twig pages do correct
first-paint without an extra DB roundtrip.
Backend:
- User.designVersion column (nullable VARCHAR(10); null defaults to 'v1')
- Migration Version20260515120000
- PATCH /api/user/design endpoint accepting 'v1'|'v2', sets wevisto_design cookie
- SpaController injects data-design on <html> + refreshes the cookie on every
SPA load (keeps cross-device pref in sync)
- Twig templates (base, login, register, help, setup, token-*) read the
cookie via {{ app.request.cookies.get('wevisto_design')|default('v1') }}
so login/setup pages also respect the user's design choice
Frontend:
- design-v2.scss — opt-in overlay scoped under [data-design="v2"]. Overrides
--color-* tokens to dusk variants per theme (warm-craft → amber, ocean-dusk
stays, etc.), adds harbor photo backdrop via body::before with theme tint
via body::after. Glass-card blur on existing surfaces. v1 untouched.
- harbor.jpg shipped as a public asset (270KB, single-fetch, cached)
- User type gains designVersion ('v1' | 'v2')
- SettingsView toggle (Original / Atmospheric) calls the API, updates the
data-design attribute optimistically, reverts on failure
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
// ─── Design v2 — atmospheric dusks ──────────────────────────────────────
|
||||
// Opt-in overlay activated via [data-design="v2"] on <html>. The user's
|
||||
// theme preference (warm-craft, ocean-dusk, etc.) chooses *which* dusk;
|
||||
// all six get a tinted-photo backdrop and a darker, glass-friendly token set.
|
||||
//
|
||||
// The yellow V (brand) stays #f0d000 across every dusk.
|
||||
|
||||
[data-design="v2"] {
|
||||
// Brand constants — survive every theme/dusk
|
||||
--brand-yellow: #f0d000;
|
||||
|
||||
// Default dusk = warm-craft → "amber dusk"
|
||||
--color-bg: #1a0d05;
|
||||
--color-surface: rgba(50, 22, 8, 0.55);
|
||||
--color-surface-2: rgba(80, 38, 14, 0.55);
|
||||
--color-border: rgba(230, 180, 130, 0.20);
|
||||
--color-text: #faecd0;
|
||||
--color-text-muted: #c8a880;
|
||||
--color-primary: #e89048;
|
||||
--color-primary-fg: #1a0d05;
|
||||
--color-secondary: rgba(80, 38, 14, 0.55);
|
||||
--color-secondary-fg:#faecd0;
|
||||
--color-destructive: #e08070;
|
||||
--color-destructive-fg: #ffffff;
|
||||
--color-focus-ring: var(--brand-yellow);
|
||||
|
||||
// Per-theme dusk overrides — only the bg-tint + accent change between dusks
|
||||
&[data-theme="ocean-dusk"] {
|
||||
--color-bg: #06121f;
|
||||
--color-surface: rgba(10, 28, 48, 0.55);
|
||||
--color-surface-2: rgba(18, 42, 70, 0.55);
|
||||
--color-border: rgba(180, 210, 235, 0.18);
|
||||
--color-text: #f4eed8;
|
||||
--color-text-muted: #b0c4d8;
|
||||
--color-primary: #4e9fc8;
|
||||
--color-primary-fg: #06121f;
|
||||
}
|
||||
&[data-theme="sage-cream"] {
|
||||
--color-bg: #081208;
|
||||
--color-surface: rgba(18, 38, 22, 0.55);
|
||||
--color-surface-2: rgba(28, 60, 32, 0.55);
|
||||
--color-border: rgba(180, 220, 180, 0.18);
|
||||
--color-text: #ecf3e0;
|
||||
--color-text-muted: #a8c0a0;
|
||||
--color-primary: #88c068;
|
||||
--color-primary-fg: #081208;
|
||||
}
|
||||
&[data-theme="playful-pop"] {
|
||||
--color-bg: #1a060f;
|
||||
--color-surface: rgba(48, 14, 36, 0.55);
|
||||
--color-surface-2: rgba(72, 22, 54, 0.55);
|
||||
--color-border: rgba(230, 180, 200, 0.20);
|
||||
--color-text: #f8e8ec;
|
||||
--color-text-muted: #d0a0b8;
|
||||
--color-primary: #d878a0;
|
||||
--color-primary-fg: #1a060f;
|
||||
}
|
||||
&[data-theme="dusty-mauve"] {
|
||||
--color-bg: #100618;
|
||||
--color-surface: rgba(36, 14, 50, 0.55);
|
||||
--color-surface-2: rgba(54, 22, 74, 0.55);
|
||||
--color-border: rgba(210, 190, 230, 0.18);
|
||||
--color-text: #f0e8f8;
|
||||
--color-text-muted: #c0b0d0;
|
||||
--color-primary: #b890d8;
|
||||
--color-primary-fg: #100618;
|
||||
}
|
||||
&[data-theme="honey-slate"] {
|
||||
--color-bg: #18120a;
|
||||
--color-surface: rgba(42, 32, 12, 0.55);
|
||||
--color-surface-2: rgba(62, 50, 22, 0.55);
|
||||
--color-border: rgba(232, 200, 130, 0.22);
|
||||
--color-text: #faf0d8;
|
||||
--color-text-muted: #d0b888;
|
||||
--color-primary: #e8c050;
|
||||
--color-primary-fg: #18120a;
|
||||
}
|
||||
|
||||
// Harbor photo backdrop — fixed under everything, theme-tinted
|
||||
body {
|
||||
position: relative;
|
||||
}
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: url('/build/assets/harbor.jpg') center/cover no-repeat;
|
||||
filter: brightness(0.55) saturate(0.9);
|
||||
z-index: -3;
|
||||
}
|
||||
body::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: var(--color-bg);
|
||||
opacity: 0.55;
|
||||
mix-blend-mode: multiply;
|
||||
z-index: -2;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// Frosted-glass surfaces — anywhere v1 used --color-surface as a solid bg
|
||||
// becomes a backdrop-blurred semi-translucent panel.
|
||||
// (The v1 components don't change their structure; they just inherit
|
||||
// the new token values + glass effect via this overlay.)
|
||||
.frame-card,
|
||||
.library__tile,
|
||||
.settings__section-card,
|
||||
.home-view__empty-card {
|
||||
backdrop-filter: saturate(160%) blur(20px);
|
||||
-webkit-backdrop-filter: saturate(160%) blur(20px);
|
||||
}
|
||||
|
||||
// The yellow V in the wordmark needs to survive even when the V is
|
||||
// technically part of the brand glyph (no Vue change required — handled
|
||||
// by tokens).
|
||||
}
|
||||
Reference in New Issue
Block a user