Files
EventSnap/frontend/src/tailwind-theme.css
MechaCat02 f243bfe89a feat(ui): elegant wedding white/silver/gold redesign
Reskin the whole app via design tokens in tailwind-theme.css (remapped
color ramps) plus a define-once component layer in lib/styles/components.css
(.btn, .card, .input, .chip, .badge, .sheet, …). Buttons are muted/outlined
gold rather than flat fills. Self-host Inter + Fraunces (woff2) under the
existing font-src 'self' CSP. Restyle the shared components and the account,
admin, host, join, recover and upload screens against the new tokens.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 17:28:42 +02:00

165 lines
7.2 KiB
CSS

/* Shared design tokens for the live app and the offline HTML-viewer export.
* Both `frontend/src/app.css` and `frontend/export-viewer/src/app.css` import this file
* so the keepsake stays visually in sync with the live app. Edit tokens here, rebuild
* the export-viewer, and re-commit `backend/static/export-viewer/`.
*
* Tailwind v4 reads `@theme` blocks to populate utility classes; everything declared
* here becomes a `bg-primary`, `text-accent`, `rounded-card`, etc.
*
* ── DESIGN LANGUAGE: "Warm & celebratory" ──────────────────────────────────
* The whole product exists to relive an event together, so the palette is a
* wedding-invitation one: warm ivory paper, warm-charcoal ink, a dusty-rose
* brand and champagne-gold accent. Headings are set in Fraunces (an elegant
* display serif); body copy in Inter. Both are self-hosted (see @font-face
* below) because the CSP is `font-src: 'self'` and the keepsake runs offline.
*
* The bulk of the app was authored in raw Tailwind classes (`bg-gray-900`,
* `text-blue-600`, …). Rather than touch 900+ class usages, we REMAP the
* underlying Tailwind ramps here so the warmth cascades everywhere:
* gray → warm "sand" neutrals
* blue → dusty-rose brand
* purple → champagne gold (hashtag chips / accents)
* Semantic red / green / amber keep their meaning and stay close to default.
*/
@import 'tailwindcss';
/* Class-based dark variant. Tailwind v4 defaults to `prefers-color-scheme`; we want
* the user's explicit selection (saved in `theme-store.ts`) to win, so we re-bind
* the `dark:` variant to apply whenever `<html>` has the `dark` class.
* `:where(...)` keeps the specificity low so existing utilities still override. */
@custom-variant dark (&:where(.dark, .dark *));
/* ── Self-hosted fonts (variable woff2, latin subset covers German umlauts) ── */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url('/fonts/Inter.woff2') format('woff2');
}
@font-face {
font-family: 'Fraunces';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url('/fonts/Fraunces.woff2') format('woff2');
}
@theme {
/* ── Type ─────────────────────────────────────────────────────────────── */
--font-sans: 'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
--font-display: 'Fraunces', ui-serif, Georgia, 'Times New Roman', serif;
--font-mono: ui-monospace, SFMono-Regular, 'SF Mono', 'Courier New', monospace;
/* ── Brand: champagne / antique gold — the "signal" colour (remaps `blue-*`)
* Wedding research: treat gold as the signal (brand, primary CTA, active
* state, key highlights); let silver/greys carry the system. `600` is a deep
* antique gold so white button text clears WCAG AA. */
--color-blue-50: #faf6ea;
--color-blue-100: #f3ead0;
--color-blue-200: #e8d6a3;
--color-blue-300: #d8bd73;
--color-blue-400: #c6a24a;
--color-blue-500: #ab8433;
--color-blue-600: #8a6a2b; /* primary buttons / FAB / active tab — white text passes AA */
--color-blue-700: #6f5523;
--color-blue-800: #59441e;
--color-blue-900: #493819;
--color-blue-950: #29200d;
/* Semantic aliases so new work can use `bg-primary` intentionally. Full ramp
* (mirrors the gold `blue-*` values) so `@apply` in the component layer can
* reference any shade — a partial ramp hard-fails `@apply bg-primary-900`. */
--color-primary-50: #faf6ea;
--color-primary-100: #f3ead0;
--color-primary-200: #e8d6a3;
--color-primary-300: #d8bd73;
--color-primary-400: #c6a24a;
--color-primary-500: #ab8433;
--color-primary-600: #8a6a2b;
--color-primary-700: #6f5523;
--color-primary-800: #59441e;
--color-primary-900: #493819;
--color-primary-950: #29200d;
/* ── Accent: same champagne gold (remaps `purple-*` / `violet-*`) so hashtag
* chips + badges share the one signal colour rather than a competing hue. */
--color-purple-50: #faf6ea;
--color-purple-100: #f3ead0;
--color-purple-200: #e8d6a3;
--color-purple-300: #d8bd73;
--color-purple-400: #c6a24a;
--color-purple-500: #ab8433;
--color-purple-600: #8a6a2b;
--color-purple-700: #6f5523;
--color-purple-800: #59441e;
--color-purple-900: #493819;
--color-violet-500: #ab8433;
--color-violet-600: #8a6a2b;
--color-accent-500: #ab8433;
--color-accent-600: #8a6a2b;
/* ── Neutrals: pearl → silver → graphite (remaps `gray-*`). Whisper-warm
* pearl at the light end (research: "warm pearl, not stark white"), turning
* neutral-cool through the mids/darks so structure reads as silver, not sand. */
--color-gray-50: #faf9f7; /* soft pearl — light page background */
--color-gray-100: #f2f1ef; /* light silver — muted surfaces */
--color-gray-200: #e5e4e1; /* silver borders / dividers */
--color-gray-300: #d1cfcc;
--color-gray-400: #a6a4a1; /* silver — placeholder / disabled icon */
--color-gray-500: #767471; /* secondary text (AA on pearl) */
--color-gray-600: #545350;
--color-gray-700: #3b3a38; /* graphite — dark-mode borders */
--color-gray-800: #262524; /* dark-mode cards */
--color-gray-900: #1a1918; /* ink (light) / dark surface */
--color-gray-950: #100f0f; /* darkest — dark-mode page background */
/* Surface aliases (kept from the original tokens for the export viewer). */
--color-surface-0: #ffffff;
--color-surface-50: #faf9f7;
--color-surface-100: #f2f1ef;
--color-surface-200: #e5e4e1;
/* ── Radii — softer, keepsake-y corners ───────────────────────────────── */
--radius-card: 1rem; /* rounded-card */
--radius-sheet: 1.5rem; /* rounded-sheet */
/* ── Elevation — soft, cool-neutral shadows (remaps Tailwind shadow ramp) ─ */
--shadow-2xs: 0 1px 2px 0 rgba(28, 28, 35, 0.05);
--shadow-xs: 0 1px 3px 0 rgba(28, 28, 35, 0.06);
--shadow-sm: 0 2px 6px -1px rgba(28, 28, 35, 0.07), 0 1px 2px -1px rgba(28, 28, 35, 0.05);
--shadow-md: 0 6px 16px -4px rgba(28, 28, 35, 0.09), 0 2px 6px -2px rgba(28, 28, 35, 0.06);
--shadow-lg: 0 12px 28px -6px rgba(28, 28, 35, 0.1), 0 4px 10px -4px rgba(28, 28, 35, 0.07);
--shadow-xl: 0 24px 48px -12px rgba(28, 28, 35, 0.16), 0 8px 16px -8px rgba(28, 28, 35, 0.09);
}
/* Baseline body background + text colour so pages that haven't been re-themed yet
* at least don't render light-on-light or dark-on-dark. Pages and cards still set
* their own backgrounds via `bg-*` utilities, but this catches any gaps. */
@layer base {
html {
background-color: #faf9f7; /* soft pearl (bg-gray-50) */
color: #1a1918; /* ink (text-gray-900) */
color-scheme: light;
font-family: var(--font-sans);
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
html.dark {
background-color: #100f0f; /* graphite near-black (bg-gray-950) */
color: #f2f1ef; /* light silver (text-gray-100) */
color-scheme: dark;
}
/* Headings wear the display serif by default. Individual headings can still
* opt out with `font-sans`; the brand wordmark opts in via `.font-display`. */
h1,
h2,
h3 {
font-family: var(--font-display);
font-weight: 600;
letter-spacing: -0.015em;
}
}