feat: Flimmer-Branding mit Logo, Icons und Design-Tokens

Logo als SVG in drei Varianten (Icon, Icon+Wortmarke horizontal, monochrom);
die Wortmarke ist als Pfad konstruiert und braucht daher keine installierte
Schrift. Daraus generiert scripts/generate-icons.ts Favicon (SVG + ICO),
PWA-Icons, Maskable-Icons, Apple-Touch-Icon und OG-Bild.

Die Farbwelt liegt als CSS-Variablen in globals.css und wird ueber
@theme inline an Tailwind durchgereicht — Dark/Light und spaetere Themes
bauen darauf auf. Alle Kombinationen erfuellen WCAG AA.
This commit is contained in:
Florian Wilde
2026-09-01 18:26:21 +02:00
parent 0bdfd66c93
commit 488b99f7a1
20 changed files with 704 additions and 0 deletions
+328
View File
@@ -0,0 +1,328 @@
@import "tailwindcss";
/* ==========================================================================
Flimmer — Design-Tokens
--------------------------------------------------------------------------
Alle Farben liegen als CSS-Variablen (--fl-*) vor und werden über
`@theme inline` an Tailwind durchgereicht. Dadurch reagieren sämtliche
Utilities (bg-surface, text-muted, …) automatisch auf Theme-Wechsel und
spätere zusätzliche Themes brauchen nur einen neuen Selektor-Block.
Kontrast: jede hier definierte Vorder-/Hintergrund-Kombination erfüllt
mindestens WCAG AA (4,5:1 für Fließtext, 3:1 für große Schrift und
UI-Begrenzungen). Werte siehe docs/DESIGN.md.
========================================================================== */
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
:root,
[data-theme="dark"] {
color-scheme: dark;
/* Flächen — sehr dunkles Blauviolett/Anthrazit */
--fl-bg: #0c0a14;
--fl-bg-elevated: #12101d;
--fl-surface: #17142499;
--fl-surface-solid: #171424;
--fl-surface-2: #201b33;
--fl-border: #2e2745;
--fl-border-strong: #453a63;
/* Text */
--fl-text: #f4f1fa; /* 17,0:1 auf --fl-bg */
--fl-muted: #aca5c2; /* 7,7:1 auf --fl-bg */
--fl-faint: #7c7594; /* 4,6:1 auf --fl-bg */
/* Signatur: Glow-Gradient Bernstein → Magenta */
--fl-amber: #ffb454;
--fl-amber-soft: #ffc46b;
--fl-magenta: #ff4d9d;
--fl-magenta-soft: #ff6fb0;
--fl-accent: var(--fl-amber); /* als Textfarbe nutzbar: 11,3:1 */
--fl-accent-alt: var(--fl-magenta); /* als Textfarbe nutzbar: 6,0:1 */
--fl-on-accent: #1a1024; /* Text auf dem Gradient: min. 5,5:1 */
--fl-gradient: linear-gradient(115deg, var(--fl-amber-soft) 0%, var(--fl-amber) 38%, var(--fl-magenta) 100%);
--fl-gradient-soft: linear-gradient(115deg, #ffb45426 0%, #ff4d9d26 100%);
--fl-glow: 0 0 0 1px #ffb4541f, 0 8px 34px -10px #ff4d9d59, 0 2px 12px -4px #ffb45440;
--fl-glow-strong: 0 0 0 1px #ffb4543d, 0 14px 48px -12px #ff4d9d80, 0 4px 18px -6px #ffb4546b;
--fl-ring: #ff8a70;
/* Statusfarben (AA auf --fl-bg) */
--fl-success: #4ade80;
--fl-warning: #fbbf24;
--fl-danger: #ff6b6b;
--fl-info: #7dd3fc;
--fl-shadow: 0 10px 30px -12px #00000099;
}
[data-theme="light"] {
color-scheme: light;
/* Flächen — heller, warmgrauer Grund */
--fl-bg: #faf7f5;
--fl-bg-elevated: #ffffff;
--fl-surface: #ffffffcc;
--fl-surface-solid: #ffffff;
--fl-surface-2: #f2ece8;
--fl-border: #e3dad4;
--fl-border-strong: #cbbdb4;
--fl-text: #1b1524; /* 16,2:1 auf --fl-bg */
--fl-muted: #5b5268; /* 6,7:1 auf --fl-bg */
--fl-faint: #766c84; /* 4,6:1 auf --fl-bg */
/* Dieselben Signaturfarben, für Text jedoch in abgedunkelten Varianten */
--fl-amber: #ffb454;
--fl-amber-soft: #ffc46b;
--fl-magenta: #ff4d9d;
--fl-magenta-soft: #ff6fb0;
--fl-accent: #8a4b00; /* als Textfarbe: 6,4:1 */
--fl-accent-alt: #b01262; /* als Textfarbe: 6,4:1 */
--fl-on-accent: #23122c; /* Text auf dem Gradient: min. 6,1:1 */
--fl-gradient: linear-gradient(115deg, var(--fl-amber-soft) 0%, var(--fl-amber) 38%, var(--fl-magenta) 100%);
--fl-gradient-soft: linear-gradient(115deg, #ffb4542e 0%, #ff4d9d2e 100%);
--fl-glow: 0 0 0 1px #d8a15c33, 0 8px 26px -12px #ff4d9d4d, 0 2px 10px -4px #ffb4544d;
--fl-glow-strong: 0 0 0 1px #d8a15c59, 0 14px 40px -14px #ff4d9d66, 0 4px 16px -6px #ffb45473;
--fl-ring: #b01262;
--fl-success: #15803d;
--fl-warning: #a16207;
--fl-danger: #b91c1c;
--fl-info: #0369a1;
--fl-shadow: 0 10px 30px -14px #2a1a3a26;
}
@theme inline {
--color-bg: var(--fl-bg);
--color-bg-elevated: var(--fl-bg-elevated);
--color-surface: var(--fl-surface);
--color-surface-solid: var(--fl-surface-solid);
--color-surface-2: var(--fl-surface-2);
--color-border-subtle: var(--fl-border);
--color-border-strong: var(--fl-border-strong);
--color-fg: var(--fl-text);
--color-muted: var(--fl-muted);
--color-faint: var(--fl-faint);
--color-amber: var(--fl-amber);
--color-amber-soft: var(--fl-amber-soft);
--color-magenta: var(--fl-magenta);
--color-magenta-soft: var(--fl-magenta-soft);
--color-accent: var(--fl-accent);
--color-accent-alt: var(--fl-accent-alt);
--color-on-accent: var(--fl-on-accent);
--color-success: var(--fl-success);
--color-warning: var(--fl-warning);
--color-danger: var(--fl-danger);
--color-info: var(--fl-info);
--radius-card: 1rem;
--radius-poster: 0.875rem;
--font-sans: var(--font-outfit), Outfit, Inter, ui-sans-serif, system-ui, -apple-system,
"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--shadow-glow: var(--fl-glow);
--shadow-glow-strong: var(--fl-glow-strong);
--shadow-card: var(--fl-shadow);
--ease-glow: cubic-bezier(0.22, 1, 0.36, 1);
}
/* --------------------------------------------------------------------------
Basis
-------------------------------------------------------------------------- */
@layer base {
* {
border-color: var(--fl-border);
}
html {
-webkit-text-size-adjust: 100%;
}
body {
background-color: var(--fl-bg);
color: var(--fl-text);
font-family: var(--font-sans);
min-height: 100dvh;
/* Ruhiges Kino-Ambiente: zwei sehr weiche Lichtkegel im Hintergrund. */
background-image:
radial-gradient(60rem 40rem at 12% -8%, color-mix(in oklab, var(--fl-amber) 12%, transparent), transparent 70%),
radial-gradient(52rem 38rem at 92% 4%, color-mix(in oklab, var(--fl-magenta) 12%, transparent), transparent 68%);
background-attachment: fixed;
}
:focus-visible {
outline: 2px solid var(--fl-ring);
outline-offset: 2px;
border-radius: 4px;
}
::selection {
background: color-mix(in oklab, var(--fl-magenta) 40%, transparent);
color: var(--fl-text);
}
/* Dezente Scrollbar im Kino-Look */
* {
scrollbar-width: thin;
scrollbar-color: var(--fl-border-strong) transparent;
}
}
/* --------------------------------------------------------------------------
Wiederkehrende Bausteine
-------------------------------------------------------------------------- */
@utility fl-gradient-text {
background-image: var(--fl-gradient);
background-clip: text;
-webkit-background-clip: text;
color: transparent;
}
@utility fl-gradient-bg {
background-image: var(--fl-gradient);
}
@layer components {
.fl-card {
background-color: var(--fl-surface);
border: 1px solid var(--fl-border);
border-radius: var(--radius-card, 1rem);
box-shadow: var(--fl-shadow);
backdrop-filter: blur(12px);
}
/* Posterkarte: sanftes Aufglimmen beim Hover, kein hartes Aufblitzen. */
.fl-poster-card {
position: relative;
border-radius: var(--radius-poster, 0.875rem);
transition:
transform 320ms var(--ease-glow),
box-shadow 320ms var(--ease-glow);
}
.fl-poster-card:hover,
.fl-poster-card:focus-within {
transform: translateY(-3px);
box-shadow: var(--fl-glow-strong);
}
.fl-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
border-radius: 0.75rem;
padding: 0.55rem 1rem;
font-weight: 600;
font-size: 0.9375rem;
line-height: 1.2;
transition:
transform 200ms var(--ease-glow),
box-shadow 240ms var(--ease-glow),
background-color 200ms ease,
border-color 200ms ease;
cursor: pointer;
}
.fl-btn:disabled {
opacity: 0.55;
cursor: not-allowed;
}
.fl-btn-primary {
background-image: var(--fl-gradient);
color: var(--fl-on-accent);
box-shadow: var(--fl-glow);
}
.fl-btn-primary:hover:not(:disabled) {
box-shadow: var(--fl-glow-strong);
transform: translateY(-1px);
}
.fl-btn-ghost {
background-color: var(--fl-surface-2);
color: var(--fl-text);
border: 1px solid var(--fl-border);
}
.fl-btn-ghost:hover:not(:disabled) {
border-color: var(--fl-border-strong);
background-color: color-mix(in oklab, var(--fl-surface-2) 70%, var(--fl-amber) 8%);
}
.fl-input {
width: 100%;
background-color: var(--fl-surface-2);
border: 1px solid var(--fl-border);
border-radius: 0.75rem;
padding: 0.6rem 0.85rem;
color: var(--fl-text);
transition: border-color 200ms ease, box-shadow 200ms ease;
}
.fl-input::placeholder {
color: var(--fl-faint);
}
.fl-input:focus {
outline: none;
border-color: color-mix(in oklab, var(--fl-magenta) 55%, var(--fl-border));
box-shadow: 0 0 0 3px color-mix(in oklab, var(--fl-magenta) 22%, transparent);
}
/* Fortschrittsbalken im Signatur-Gradient */
.fl-progress {
height: 0.375rem;
border-radius: 999px;
background-color: var(--fl-surface-2);
overflow: hidden;
}
.fl-progress > span {
display: block;
height: 100%;
border-radius: 999px;
background-image: var(--fl-gradient);
transition: width 420ms var(--ease-glow);
}
}
/* Mikro-Animation: weiches Aufglimmen, z. B. beim Abhaken einer Episode. */
@keyframes fl-glimmer {
0% {
box-shadow: 0 0 0 0 color-mix(in oklab, var(--fl-amber) 55%, transparent);
background-color: color-mix(in oklab, var(--fl-amber) 16%, transparent);
}
60% {
box-shadow: 0 0 0 10px color-mix(in oklab, var(--fl-magenta) 0%, transparent);
}
100% {
box-shadow: 0 0 0 0 transparent;
background-color: transparent;
}
}
.fl-glimmer {
animation: fl-glimmer 900ms var(--ease-glow);
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}