From 02f7a993743ba7ac104800a6934f7228227e843c Mon Sep 17 00:00:00 2001 From: jules Date: Sat, 4 Jul 2026 13:45:33 +1000 Subject: [PATCH] theme: dark status tokens + charts; remove font @import; contract conformance Adds dark destructive/success/warning overrides, --chart-1..5, --info, subtitle, motion aliases. Font @import moved to README (PostCSS ordering). Co-Authored-By: Claude Fable 5 --- README.md | 7 +++++ theme.css | 82 +++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 81 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3a97eb1..7ad5125 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,13 @@ import like any other Crema theme. @import "tailwindcss"; ``` +Load the fonts at the very top of your app's entry CSS (the theme no longer +`@import`s them — the token contract bans font `@import`s inside `theme.css`): + +```css +@import url("https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;0,6..72,700;1,6..72,400&family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap"); +``` + That's it. All `@crema/content-ui` consumers (Article, ArticleHeader, ArticleMeta, Prose, Pullquote, etc.) automatically pick up the editorial look because Modena targets `[data-slot="article"]` and `[data-slot="prose"]`. diff --git a/theme.css b/theme.css index 77d1240..35f4cde 100644 --- a/theme.css +++ b/theme.css @@ -19,9 +19,10 @@ * (Modena-specific extra components). */ -/* Newsreader for the editorial serif. Pulled at theme-import time so - * consumers don't have to remember to import the font separately. */ -@import url("https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;0,6..72,700;1,6..72,400&family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap"); +/* Fonts (Newsreader editorial serif + Inter + JetBrains Mono) are loaded by + * the consuming app's entry CSS, not @import-ed here — the token contract bans + * font @imports inside theme.css (they break PostCSS ordering). The Google + * Fonts URL is documented in this theme's README. */ :root { /* ── Crema-shaped tokens (apps and shadcn components consume these) ── */ @@ -41,10 +42,14 @@ --accent-foreground: oklch(0.18 0.02 255); --destructive: oklch(0.55 0.22 25); --destructive-foreground: oklch(0.99 0.005 80); - --success: oklch(0.65 0.18 145); + --success: oklch(0.52 0.18 145); /* deepened 0.65→0.52 so light foreground clears AA (was 2.93:1) */ --success-foreground: oklch(0.99 0.005 80); --warning: oklch(0.72 0.16 70); --warning-foreground: oklch(0.15 0.02 70); + /* Info — a cyan-blue held clear of the editorial primary (blue 260) so + * status reads distinct from brand; deep enough to carry a light label. */ + --info: oklch(0.52 0.15 235); + --info-foreground: oklch(0.99 0.005 80); --border: oklch(0.92 0.005 250); --input: oklch(0.92 0.005 250); --ring: oklch(0.55 0.22 260); @@ -59,7 +64,22 @@ --chat-assistant-fg: oklch(0.16 0.02 255); --chat-assistant-border: oklch(0.92 0.005 250); + /* ── Charts ── + * Five editorial accents drawn from Modena's own status hues (brand blue, + * venetian red, sage green, magenta, amber). Hues sit ≥60° apart and + * adjacent indices alternate lightness so neighbouring series separate. */ + --chart-1: oklch(0.55 0.20 260); /* brand blue */ + --chart-2: oklch(0.63 0.20 25); /* venetian red */ + --chart-3: oklch(0.58 0.15 145); /* sage green */ + --chart-4: oklch(0.66 0.19 320); /* magenta */ + --chart-5: oklch(0.60 0.16 70); /* amber */ + --radius: 0.5rem; + --spacing: 0.25rem; + --border-width-thin: 1px; + --border-width-base: 1px; + --border-width-thick: 2px; + --border-width-heavy: 4px; --sidebar: oklch(0.985 0.003 250); --sidebar-foreground: oklch(0.16 0.02 255); --sidebar-accent: oklch(0.94 0.005 250); @@ -131,6 +151,9 @@ --font-mono: "JetBrains Mono", ui-monospace, Menlo, Consolas, monospace; --mod-font-body: "Newsreader", "Source Serif 4", Georgia, "Times New Roman", serif; --font-ai-prose: var(--mod-font-body); + /* Tailwind utility families (mirror --font-heading/--font-sans). */ + --heading-family: var(--font-heading); + --body-family: var(--font-sans); /* Letter-spacing scale */ --mod-tracking: -0.011em; @@ -158,13 +181,18 @@ --mod-radius-xl: 16px; --mod-radius-pill: 9999px; - /* Type scale (Crema-shaped) */ + /* Type scale (Crema-shaped) — editorial, opts into fluid clamp() at the + * hero/display/headline end (permitted for editorial themes). */ + --text-hero-size: clamp(3.25rem, 2.6rem + 2.6vw, 4.5rem); + --text-hero-lh: 1.05; --text-display-size: clamp(2.5rem, 2rem + 2vw, 3.25rem); --text-display-lh: 1.1; --text-headline-size: clamp(2rem, 1.7rem + 1.2vw, 2.5rem); --text-headline-lh: 1.15; --text-title-size: 1.5rem; --text-title-lh: 1.3; + --text-subtitle-size: 1.25rem; /* fills the label→title hole (deck/standfirst) */ + --text-subtitle-lh: 1.4; --text-body-size: 1rem; --text-body-lh: 1.6; --text-label-size: 0.875rem; @@ -172,12 +200,20 @@ --text-caption-size: 0.75rem; --text-caption-lh: 1.4; - /* Motion */ + /* Motion — calm editorial. No physical bounce: the springs the contract + * mandates are aliased to the standard easings so var() always resolves + * while the theme keeps its quiet, un-bouncy character. */ --duration-fast: 120ms; --duration-base: 200ms; --duration-slow: 300ms; + --duration-spring: var(--duration-slow); --ease-standard: cubic-bezier(0.25, 0.1, 0.25, 1); --ease-decelerate: cubic-bezier(0, 0, 0.25, 1); + --ease-accelerate: cubic-bezier(0.4, 0, 1, 1); + --ease-emphasized: var(--ease-standard); + --ease-spring-gentle: var(--ease-standard); + --ease-spring-snappy: var(--ease-standard); + --ease-spring-bouncy: var(--ease-standard); /* Elevation */ --elevation-0: none; @@ -185,6 +221,14 @@ --elevation-2: 0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.03); --elevation-3: 0 4px 8px -2px rgba(0, 0, 0, 0.06), 0 2px 4px -2px rgba(0, 0, 0, 0.03); --elevation-4: 0 12px 24px -4px rgba(0, 0, 0, 0.08), 0 4px 8px -4px rgba(0, 0, 0, 0.03); + --elevation-5: 0 24px 48px -8px rgba(0, 0, 0, 0.10), 0 8px 16px -8px rgba(0, 0, 0, 0.04); + + /* Glass — inert stubs. Modena is a flat editorial theme, not a glass one; + * these resolve var() to no-ops so glass-aware components stay flat. */ + --glass-blur: 0px; + --glass-saturate: 100%; + --glass-brightness: 1; + --glass-contrast: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; @@ -198,7 +242,7 @@ --card-foreground: oklch(0.95 0.010 250); --popover: oklch(0.17 0.020 250); --popover-foreground: oklch(0.95 0.010 250); - --primary: oklch(0.65 0.22 260); + --primary: oklch(0.55 0.22 260); /* deepened 0.65→0.55 so the near-white foreground clears AA on the filled button (was 3.38:1) */ --primary-foreground: oklch(0.99 0.005 80); --secondary: oklch(0.22 0.020 250); --secondary-foreground: oklch(0.92 0.010 250); @@ -210,6 +254,27 @@ --input: oklch(0.27 0.020 250); --ring: oklch(0.65 0.22 260); + /* ── Status (dark-calibrated) ── + * Light values (red/green/amber at L≈0.55-0.72) were inheriting into + * dark mode, failing both as colored text on the dark field and as + * button fills. Brightened here; foregrounds flipped to dark ink so + * the fills carry text as well. */ + --destructive: oklch(0.68 0.2 25); /* text-on-bg 6.37:1 (was 3.70:1 inherited) */ + --destructive-foreground: oklch(0.16 0.04 25); /* dark-ink fill 6.19:1 */ + --success: oklch(0.72 0.15 145); /* text-on-bg 8.58:1 */ + --success-foreground: oklch(0.16 0.03 145); /* dark-ink fill 8.24:1 */ + --warning: oklch(0.80 0.14 70); /* text-on-bg 10.51:1 */ + --warning-foreground: oklch(0.16 0.02 70); /* dark-ink fill 10.16:1 */ + --info: oklch(0.72 0.14 235); /* brightened for the dark field; dark-ink fill */ + --info-foreground: oklch(0.16 0.03 235); + + /* ── Charts (dark) ── same five hues, brightened for the dark field. */ + --chart-1: oklch(0.68 0.19 260); + --chart-2: oklch(0.72 0.18 25); + --chart-3: oklch(0.70 0.15 145); + --chart-4: oklch(0.74 0.17 320); + --chart-5: oklch(0.78 0.14 70); + /* ── Chat surfaces (dark) ── */ --chat-user-bg: oklch(0.55 0.2 260); --chat-user-fg: oklch(0.99 0.005 80); @@ -224,11 +289,12 @@ --sidebar-border: oklch(0.25 0.020 250); --sidebar-primary: oklch(0.65 0.22 260); --sidebar-primary-foreground: oklch(0.99 0.005 80); + --sidebar-ring: oklch(0.65 0.22 260); --mod-text: var(--foreground); --mod-text-secondary: oklch(0.65 0.020 250); --mod-text-muted: oklch(0.55 0.020 250); - --mod-text-faint: oklch(0.40 0.018 250); + --mod-text-faint: oklch(0.59 0.018 250); /* raised 0.40→0.59 for AA vs dark bg/card (was 2.19:1) */ --mod-prose-color: oklch(0.86 0.012 250); --mod-code-color: oklch(0.78 0.22 350); --mod-code-bg: oklch(0.22 0.020 250);