Files
immich-folio/app/tokens.css
Ralksta ad8b09700b release: v0.19.0
Release v0.19.0. See the `## [0.19.0] — 2026-09-29` section of
[CHANGELOG.md](https://github.com/ralksta/immich-folio/blob/dev/CHANGELOG.md)
for the full list.

Highlights: client proofing links (#683), custom content pages (#738),
asset picker search (#740), concurrent-edit detection (#742), env-locked
settings (#739), an accessibility baseline (#741), three security fixes
with advisories (#737, #755, #759), and a pre-release quality pass
(#780–#797).

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: ralksta <ralksta@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Ralf <rohlenbo79@gmail.com>
Co-authored-by: Paul Thies <110185070+ImScheinox@users.noreply.github.com>
Co-authored-by: lancetm714 <108419425+lancetm714@users.noreply.github.com>
Co-authored-by: Lance Martin <lancetaylor95@gmail.com>
2026-09-29 22:50:30 +02:00

358 lines
10 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Fonts are loaded dynamically via layout.tsx based on theme config */
/* --text-muted missed the 4.5:1 that WCAG AA requires for text of this size
in every preset, ranging from 2.33:1 (monograph) to 3.50:1 (studio-modern).
It is not a decorative colour: it carries the hero subtitle, section
labels, album metadata and the footer links, all between 0.6 and 0.9 rem.
Only lightness was raised; hue and saturation are unchanged, so the warm
greys of editorial, classic and noir stay warm instead of drifting to
neutral. Each value is checked against --bg-primary AND --bg-card, and the
worse of the two counts.
In minimal and monograph this pushed --text-muted so close to
--text-secondary that the two steps became indistinguishable (#828282 vs
#888888). --text-secondary moved along there, restoring two distinct
levels: about 7:1 for secondary, about 4.7:1 for muted. */
/* ── Design Tokens (Fallback Defaults) ───────────── */
/* These are overridden at runtime by CSS vars injected from gallery.yaml theme config. */
:root {
--bg-primary: #1a1a1a;
--bg-secondary: #141414;
--bg-card: #1e1e1e;
--bg-card-hover: #242424;
--bg-overlay: rgba(0, 0, 0, 0.98);
--text-primary: #f5f5f0;
--text-secondary: #a0a09a;
--text-muted: #86857f;
--border-subtle: #2a2a28;
--border-hover: #3a3a38;
/* The layout sets --accent-dark/--accent-light (and --on-accent-*) from the
theme; the fallbacks only cover a page rendered without it. */
--accent: var(--accent-dark, #e60012);
--accent-dim: color-mix(in srgb, var(--accent) 12%, transparent);
--on-accent: var(--on-accent-dark, #ffffff);
/* Error and destructive text. One value per mode: no single red clears
4.5:1 on both a near-black and a near-white page (the fixed reds these
replace measured 3.2–3.8:1 on the light presets). #ff6b6b is at least
5.6:1 on every dark surface token, #c62828 (below) at least 4.66:1 on
every light one. */
--error: #ff6b6b;
/* The lightbox paints its own dark overlay in both modes, so its controls
cannot use --text-*: in light mode those are near-black, and the close
button, arrows and counter came out at 1.1–2:1. Lightbox.module.css
points the text tokens at these inside the overlay in light mode; dark
mode keeps each preset's tinted greys. */
--lightbox-text: rgba(255, 255, 255, 0.92);
--lightbox-text-secondary: rgba(255, 255, 255, 0.72);
--lightbox-text-muted: rgba(255, 255, 255, 0.62);
--lightbox-border: rgba(255, 255, 255, 0.12);
/* Round controls float over the photo on narrow screens; a dark fill keeps
them visible on a white sky, where the old white-alpha disc vanished. */
--lightbox-control-bg: rgba(0, 0, 0, 0.35);
--lightbox-control-bg-hover: rgba(0, 0, 0, 0.55);
--lightbox-control-border: rgba(255, 255, 255, 0.18);
/* A saved favourite. Not the accent: minimal's is white and a custom one
may be dark navy, neither of which marks anything on the overlay. */
--lightbox-fav: #ff6b6b;
--font-sans: 'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--font-serif: 'Playfair Display', Georgia, 'Times New Roman', serif;
--font-caption: 'EB Garamond', Georgia, serif;
--radius-sm: 0px;
--radius-md: 0px;
--radius-lg: 0px;
--transition-fast: 200ms ease;
--transition-normal: 400ms ease;
--transition-slow: 600ms ease;
--max-width: 1400px;
--header-height: 64px;
}
/* ── Light Theme (Studio Light) ──────────────────── */
[data-theme='light'] {
--bg-primary: #f5f5f0;
--bg-secondary: #eaeae5;
--bg-card: #ffffff;
--bg-card-hover: #f0f0eb;
--bg-overlay: rgba(245, 245, 240, 0.96);
--text-primary: #1a1a18;
--text-secondary: #4a4a45;
--text-muted: #6a6a65;
--border-subtle: #ddddd8;
--border-hover: #c5c5c0;
--accent: var(--accent-light, #c8000f);
--accent-dim: color-mix(in srgb, var(--accent) 12%, transparent);
--on-accent: var(--on-accent-light, #ffffff);
--error: #c62828;
}
/* ── Studio Modern: Leica Precision Palette ──────── */
[data-preset='studio-modern'] {
--bg-primary: #121212;
--bg-secondary: #0d0d0d;
--bg-card: #191919;
--bg-card-hover: #202020;
--bg-overlay: rgba(10, 10, 10, 0.92);
--text-primary: #f4f4f2;
--text-secondary: #9c9c96;
--text-muted: #82827c;
--border-subtle: rgba(255, 255, 255, 0.08);
--border-hover: rgba(255, 255, 255, 0.2);
--radius-sm: 0px;
--radius-md: 0px;
--radius-lg: 0px;
--max-width: 1440px;
--header-height: 64px;
/* Signal red. Light mode darkens whatever accent is configured so it
still carries on a near-white background (#e60012 -> ~#c4000f). */
--sm-accent: var(--accent);
/* Gutter + hairline, reused across app/themes/studio-modern.css */
--sm-gutter: 48px;
--sm-hairline: 1px solid var(--border-subtle);
--sm-chip-bg: rgba(0, 0, 0, 0.75);
}
[data-preset='studio-modern'][data-theme='light'] {
--sm-accent: color-mix(in srgb, var(--accent) 86%, #000);
--bg-primary: #fafaf8;
--bg-secondary: #f0f0ec;
--bg-card: #ffffff;
--bg-card-hover: #f5f5f1;
--bg-overlay: rgba(250, 250, 248, 0.96);
--text-primary: #161614;
--text-secondary: #5a5a55;
--text-muted: #73736e;
--border-subtle: rgba(0, 0, 0, 0.09);
--border-hover: rgba(0, 0, 0, 0.25);
}
/* ── Minimal: Swiss Brutalist Palette ────────────── */
[data-preset='minimal'] {
--bg-primary: #000000;
--bg-secondary: #0a0a0a;
--bg-card: #111111;
--bg-card-hover: #1a1a1a;
--bg-overlay: rgba(0, 0, 0, 0.98);
--text-primary: #ffffff;
--text-secondary: #9e9e9e;
--text-muted: #7c7c7c;
--border-subtle: #1a1a1a;
--border-hover: #333333;
}
[data-preset='minimal'][data-theme='light'] {
--bg-primary: #ffffff;
--bg-secondary: #f7f7f7;
--bg-card: #ffffff;
--bg-card-hover: #fafafa;
--bg-overlay: rgba(255, 255, 255, 0.98);
--text-primary: #000000;
--text-secondary: #475569;
--text-muted: #627083; /* 4.65:1 on the tinted header (#f6f6f1), not just on white */
--border-subtle: #eeeeee;
--border-hover: #cccccc;
}
/* ── Editorial: Cinematic Magazine Palette ────────── */
[data-preset='editorial'] {
--bg-primary: #1a1714;
--bg-secondary: #141210;
--bg-card: #1e1b17;
--bg-card-hover: #252119;
--bg-overlay: rgba(20, 18, 16, 0.98);
--text-primary: #ede8e0;
--text-secondary: #9a9086;
--text-muted: #8c8275;
--border-subtle: #2a2620;
--border-hover: #3a3530;
}
[data-preset='editorial'][data-theme='light'] {
--bg-primary: #faf8f4;
--bg-secondary: #f0ece4;
--bg-card: #ffffff;
--bg-card-hover: #f5f2ec;
--bg-overlay: rgba(250, 248, 244, 0.96);
--text-primary: #1a1714;
--text-secondary: #4a4540;
--text-muted: #6a655f;
--border-subtle: #e5e0d8;
--border-hover: #ccc7c0;
}
/* ── Classic: Gilded Gallery Palette ─────────────── */
[data-preset='classic'] {
--bg-primary: #121210;
--bg-secondary: #0e0e0c;
--bg-card: #181816;
--bg-card-hover: #1e1e1a;
--bg-overlay: rgba(14, 14, 12, 0.98);
--text-primary: #f0ece4;
--text-secondary: #a09888;
--text-muted: #897f6c;
--border-subtle: #2a2822;
--border-hover: #3a3830;
}
[data-preset='classic'][data-theme='light'] {
--bg-primary: #fdfbf5;
--bg-secondary: #f5f0e6;
--bg-card: #ffffff;
--bg-card-hover: #faf6ee;
--bg-overlay: rgba(253, 251, 245, 0.96);
--text-primary: #1a1810;
--text-secondary: #4a4535;
--text-muted: #6a6555;
--border-subtle: #e5dfcf;
--border-hover: #d0c8b5;
}
/* ── Noir: Darkroom Analog Palette ───────────────── */
[data-preset='noir'] {
--bg-primary: #111014;
--bg-secondary: #0c0b0f;
--bg-card: #16141a;
--bg-card-hover: #1e1c22;
--bg-overlay: rgba(11, 10, 15, 0.98);
--text-primary: #f0ebe0;
--text-secondary: #998f80;
--text-muted: #8a7c6c;
--border-subtle: #252228;
--border-hover: #3a3540;
}
[data-preset='noir'][data-theme='light'] {
--bg-primary: #faf5ef;
--bg-secondary: #f0ebe3;
--bg-card: #ffffff;
--bg-card-hover: #f5f0e8;
--bg-overlay: rgba(250, 245, 239, 0.96);
--text-primary: #1a1610;
--text-secondary: #4a4538;
--text-muted: #6a6558;
--border-subtle: #e5dfd5;
--border-hover: #ccc5b8;
}
/* ── Monograph: Typographic / Book Palette ────────── */
[data-preset='monograph'] {
--bg-primary: #151515;
--bg-secondary: #0f0f0f;
--bg-card: #1a1a1a;
--bg-card-hover: #222222;
--bg-overlay: rgba(15, 15, 15, 0.98);
--text-primary: #e8e8e8;
--text-secondary: #a5a5a5;
--text-muted: #828282;
--border-subtle: #222222;
--border-hover: #3a3a3a;
}
[data-preset='monograph'][data-theme='light'] {
--bg-primary: #fafafa;
--bg-secondary: #f2f2f2;
--bg-card: #ffffff;
--bg-card-hover: #f7f7f7;
--bg-overlay: rgba(250, 250, 250, 0.98);
--text-primary: #111111;
--text-secondary: #475569;
--text-muted: #627083; /* 4.65:1 on the tinted header (#f6f6f1), not just on white */
--border-subtle: #e5e5e5;
--border-hover: #cccccc;
}
/* ── Reset ────────────────────────────────────────── */
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
font-family: var(--font-sans);
background: var(--bg-primary);
color: var(--text-primary);
min-height: 100vh;
line-height: 1.6;
}
a {
color: inherit;
text-decoration: none;
}
button {
cursor: pointer;
font-family: inherit;
}
img {
display: block;
max-width: 100%;
}
/* [tabindex='-1'] is left out: those are focus targets for scripts and skip
links (<main>, the lightbox dialog), and outlining a whole region there
helps nobody. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
summary:focus-visible,
[role='button']:focus-visible,
[tabindex]:not([tabindex='-1']):focus-visible {
outline: 2px solid var(--text-primary);
outline-offset: 2px;
}