/** * Shared Theme CSS Variables — shadcn / Tailwind v4 convention. * * Design: * - Single source-of-truth layer: `--color-X` defined as raw HSL channels * (e.g. `0 0% 17%`, NOT `hsl(0 0% 17%)`) in :root, .dark, and variant * [data-theme="..."] blocks. The runtime store in @mana/shared-theme * writes the same names with the same format, so static and runtime * rendering stay consistent (no flash of unstyled content). * * - `@theme inline` uses self-reference + `` placeholder so * Tailwind v4 generates utility classes (`text-foreground`, `bg-card`, * etc.) AND opacity modifiers (`text-foreground/50`) work. * * Consumer conventions: * * 1. Markup (preferred when possible): *

*
* * * 2. Scoped CSS (when utilities don't fit, e.g. complex selectors): * .my-text { color: hsl(var(--color-foreground)); } * .my-bg { background: hsl(var(--color-card) / 0.5); } * * 3. NEVER write raw `var(--color-X)` without wrapping with `hsl()`. * The variable holds raw channels, not a valid CSS color. A bare * `color: var(--color-foreground)` is NOT an error — the browser * silently falls back to `inherit`, which is how the early P5 quotes * regression produced white-on-white text in light mode. Always wrap. * * ❌ color: var(--color-foreground); * ❌ background: var(--color-primary, #6366f1); // fallback wrong format * ❌ color-mix(in srgb, var(--color-primary) 12%, transparent); * ✅ color: hsl(var(--color-foreground)); * ✅ background: hsl(var(--color-primary) / 0.12); * * 4. Brand-literal colors (period pink, observatory cosmic scenes, the * automations/spiral indigo→violet ramp, sport/category palettes, the * photo viewer's near-black backdrop, etc.) deliberately stay as * literal hex/rgba/hsl. They are NOT theme intent — they encode brand * or domain semantics that must look the same under every theme * variant. Don't migrate them to tokens. * * 5. Token names are stable. There is intentionally NO `--color-info`, * `--color-text`, `--color-destructive`, `--color-surface`, or * `--color-input` (use `--color-error`, `--color-foreground`, * `--color-error`, `--color-muted`, `--color-background` instead). * Adding a new token? Define it in :root + .dark + every * [data-theme="..."] block, or it will be undefined under at least * one variant. * * Runtime: the `createThemeStore` factory in @mana/shared-theme writes the * same `--color-X` names as inline styles on document.documentElement when * the active variant changes. Static defaults in :root mean first paint is * already themed (no FOUC); the runtime store only takes over after * hydration to honor the user's stored preference. * * Usage in app.css (Tailwind v4): * @import "tailwindcss"; * @import "@mana/shared-tailwind/themes.css"; */ /* ===== Tailwind v4 Theme Configuration ===== */ @theme inline { /* * Self-referencing wrappers — `` is a Tailwind v4 placeholder * that gets substituted to `1` for unmodified utilities and to `0.5`, * `0.25`, etc. for opacity modifiers (`text-foreground/50`). */ --color-primary: hsl(var(--color-primary) / ); --color-primary-foreground: hsl(var(--color-primary-foreground) / ); --color-secondary: hsl(var(--color-secondary) / ); --color-secondary-foreground: hsl(var(--color-secondary-foreground) / ); --color-background: hsl(var(--color-background) / ); --color-foreground: hsl(var(--color-foreground) / ); --color-surface: hsl(var(--color-surface) / ); --color-surface-hover: hsl(var(--color-surface-hover) / ); --color-surface-elevated: hsl(var(--color-surface-elevated) / ); --color-surface-elevated-1: hsl(var(--color-surface-elevated-1) / ); --color-surface-elevated-2: hsl(var(--color-surface-elevated-2) / ); --color-surface-elevated-3: hsl(var(--color-surface-elevated-3) / ); --color-muted: hsl(var(--color-muted) / ); --color-muted-foreground: hsl(var(--color-muted-foreground) / ); --color-border: hsl(var(--color-border) / ); --color-border-strong: hsl(var(--color-border-strong) / ); --color-error: hsl(var(--color-error) / ); --color-success: hsl(var(--color-success) / ); --color-warning: hsl(var(--color-warning) / ); --color-input: hsl(var(--color-input) / ); --color-ring: hsl(var(--color-ring) / ); /* ShadCN-compatible aliases */ --color-card: hsl(var(--color-card) / ); --color-card-foreground: hsl(var(--color-card-foreground) / ); --color-accent: hsl(var(--color-accent) / ); --color-accent-foreground: hsl(var(--color-accent-foreground) / ); } @theme { /* Brand color (literal, not theme-aware) */ --color-mana: #4287f5; /* Border radius */ --radius-none: 0; --radius-sm: 0.25rem; --radius-DEFAULT: 0.375rem; --radius-md: 0.5rem; --radius-lg: 0.75rem; --radius-xl: 1rem; --radius-2xl: 1.5rem; --radius-3xl: 2rem; --radius-full: 9999px; /* Box shadows */ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); --shadow-DEFAULT: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / 0.25); --shadow-inner: inset 0 2px 4px 0 rgb(0 0 0 / 0.05); --shadow-none: none; /* Font families */ --font-sans: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; --font-mono: 'JetBrains Mono', 'Fira Code', Menlo, Monaco, Consolas, monospace; /* Animations */ --animate-spin-slow: spin 3s linear infinite; --animate-pulse-slow: pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite; --animate-bounce-slow: bounce 2s infinite; --animate-fade-in: fadeIn 0.2s ease-out; --animate-fade-out: fadeOut 0.2s ease-in; --animate-slide-in: slideIn 0.2s ease-out; --animate-slide-out: slideOut 0.2s ease-in; } /* ===== Keyframes ===== */ @keyframes fadeIn { 0% { opacity: 0; } 100% { opacity: 1; } } @keyframes fadeOut { 0% { opacity: 1; } 100% { opacity: 0; } } @keyframes slideIn { 0% { transform: translateY(-10px); opacity: 0; } 100% { transform: translateY(0); opacity: 1; } } @keyframes slideOut { 0% { transform: translateY(0); opacity: 1; } 100% { transform: translateY(-10px); opacity: 0; } } /* ===== Domain Accent Colors ===== Module-internal brand accents that should NOT track the active theme variant. Defined once at :root, never overridden by .dark or variant blocks — they look the same regardless of light/dark/lume/ocean/etc. Consumers wrap with hsl(): `color: hsl(var(--color-branch-intellect));`. */ :root { /* Skilltree — 6 skill category accents */ --color-branch-intellect: 217 91% 60%; /* blue — knowledge, thinking */ --color-branch-body: 0 84% 60%; /* red — physical, energy */ --color-branch-creativity: 271 91% 65%; /* violet — art, expression */ --color-branch-social: 38 92% 50%; /* amber — warmth, relationships */ --color-branch-practical: 173 80% 40%; /* teal — craft, tools */ --color-branch-mindset: 142 71% 45%; /* green — calm, growth */ } /* ===== Default Theme (Lume Light) ===== */ :root { --color-primary: 47 95% 58%; --color-primary-foreground: 0 0% 0%; --color-secondary: 47 100% 41%; --color-secondary-foreground: 0 0% 0%; --color-background: 0 0% 87%; --color-foreground: 0 0% 17%; --color-surface: 0 0% 100%; --color-surface-hover: 0 0% 96%; --color-surface-elevated: 0 0% 100%; --color-surface-elevated-1: 0 0% 100%; --color-surface-elevated-2: 0 0% 100%; --color-surface-elevated-3: 0 0% 100%; --color-muted: 0 0% 90%; --color-muted-foreground: 0 0% 40%; --color-border: 0 0% 90%; --color-border-strong: 0 0% 80%; --color-error: 6 78% 57%; --color-success: 145 63% 42%; --color-warning: 36 100% 50%; --color-input: 0 0% 100%; --color-ring: 47 95% 58%; --color-card: 0 0% 100%; --color-card-foreground: 0 0% 17%; --color-accent: 0 0% 96%; --color-accent-foreground: 0 0% 17%; /* Border radius (legacy alias used by component utilities below) */ --radius-sm: 0.25rem; --radius: 0.375rem; --radius-md: 0.5rem; --radius-lg: 0.75rem; --radius-xl: 1rem; --radius-2xl: 1.5rem; --radius-3xl: 2rem; } /* ===== Default Dark Mode ===== */ .dark, :root.dark { --color-primary: 47 95% 58%; --color-primary-foreground: 0 0% 0%; --color-secondary: 47 70% 29%; --color-secondary-foreground: 0 0% 100%; --color-background: 0 0% 6%; --color-foreground: 0 0% 100%; --color-surface: 0 0% 12%; --color-surface-hover: 0 0% 16%; --color-surface-elevated: 0 0% 14%; --color-surface-elevated-1: 0 0% 16%; --color-surface-elevated-2: 0 0% 20%; --color-surface-elevated-3: 0 0% 24%; --color-muted: 0 0% 20%; --color-muted-foreground: 0 0% 60%; --color-border: 0 0% 26%; --color-border-strong: 0 0% 35%; --color-error: 6 78% 57%; --color-success: 145 63% 49%; --color-warning: 48 100% 50%; --color-input: 0 0% 14%; --color-ring: 47 95% 58%; --color-card: 0 0% 12%; --color-card-foreground: 0 0% 100%; --color-accent: 0 0% 16%; --color-accent-foreground: 0 0% 100%; } /* ===== Lume Theme (Gold) ===== */ [data-theme='lume'] { --color-primary: 47 95% 58%; --color-primary-foreground: 0 0% 0%; --color-secondary: 47 100% 41%; --color-secondary-foreground: 0 0% 0%; --color-background: 0 0% 87%; --color-foreground: 0 0% 17%; --color-surface: 0 0% 100%; --color-surface-hover: 0 0% 96%; --color-surface-elevated: 0 0% 100%; --color-surface-elevated-1: 0 0% 100%; --color-surface-elevated-2: 0 0% 100%; --color-surface-elevated-3: 0 0% 100%; --color-muted: 0 0% 90%; --color-muted-foreground: 0 0% 40%; --color-border: 0 0% 90%; --color-border-strong: 0 0% 80%; --color-error: 6 78% 57%; --color-success: 145 63% 42%; --color-warning: 36 100% 50%; --color-input: 0 0% 100%; --color-ring: 47 95% 58%; --color-card: 0 0% 100%; --color-card-foreground: 0 0% 17%; --color-accent: 0 0% 96%; --color-accent-foreground: 0 0% 17%; } [data-theme='lume'].dark, .dark[data-theme='lume'] { --color-primary: 47 95% 58%; --color-primary-foreground: 0 0% 0%; --color-secondary: 47 70% 29%; --color-secondary-foreground: 0 0% 100%; --color-background: 0 0% 6%; --color-foreground: 0 0% 100%; --color-surface: 0 0% 12%; --color-surface-hover: 0 0% 16%; --color-surface-elevated: 0 0% 14%; --color-surface-elevated-1: 0 0% 16%; --color-surface-elevated-2: 0 0% 20%; --color-surface-elevated-3: 0 0% 24%; --color-muted: 0 0% 20%; --color-muted-foreground: 0 0% 60%; --color-border: 0 0% 26%; --color-border-strong: 0 0% 35%; --color-error: 6 78% 57%; --color-success: 145 63% 49%; --color-warning: 48 100% 50%; --color-input: 0 0% 14%; --color-ring: 47 95% 58%; --color-card: 0 0% 12%; --color-card-foreground: 0 0% 100%; --color-accent: 0 0% 16%; --color-accent-foreground: 0 0% 100%; } /* ===== Nature Theme (Green) ===== */ [data-theme='nature'] { --color-primary: 122 39% 49%; --color-primary-foreground: 0 0% 100%; --color-secondary: 122 38% 63%; --color-secondary-foreground: 0 0% 0%; --color-background: 80 33% 97%; --color-foreground: 122 56% 24%; --color-surface: 0 0% 100%; --color-surface-hover: 120 25% 95%; --color-surface-elevated: 0 0% 100%; --color-surface-elevated-1: 0 0% 100%; --color-surface-elevated-2: 0 0% 100%; --color-surface-elevated-3: 0 0% 100%; --color-muted: 120 25% 95%; --color-muted-foreground: 122 20% 40%; --color-border: 120 25% 91%; --color-border-strong: 120 26% 79%; --color-error: 0 65% 67%; --color-success: 122 39% 49%; --color-warning: 36 100% 50%; --color-input: 0 0% 100%; --color-ring: 122 39% 49%; --color-card: 0 0% 100%; --color-card-foreground: 122 56% 24%; --color-accent: 120 25% 95%; --color-accent-foreground: 122 56% 24%; } [data-theme='nature'].dark, .dark[data-theme='nature'] { --color-primary: 122 39% 49%; --color-primary-foreground: 0 0% 100%; --color-secondary: 122 30% 35%; --color-secondary-foreground: 0 0% 100%; --color-background: 0 0% 7%; --color-foreground: 0 0% 100%; --color-surface: 120 10% 12%; --color-surface-hover: 120 10% 16%; --color-surface-elevated: 120 10% 14%; --color-surface-elevated-1: 120 10% 16%; --color-surface-elevated-2: 120 10% 20%; --color-surface-elevated-3: 120 10% 24%; --color-muted: 120 10% 20%; --color-muted-foreground: 120 10% 60%; --color-border: 120 10% 25%; --color-border-strong: 120 10% 35%; --color-error: 0 65% 57%; --color-success: 122 50% 55%; --color-warning: 48 100% 50%; --color-input: 120 10% 14%; --color-ring: 122 39% 49%; --color-card: 120 10% 12%; --color-card-foreground: 0 0% 100%; --color-accent: 120 10% 16%; --color-accent-foreground: 0 0% 100%; } /* ===== Stone Theme (Blue Gray) ===== */ [data-theme='stone'] { --color-primary: 200 18% 46%; --color-primary-foreground: 0 0% 100%; --color-secondary: 200 15% 62%; --color-secondary-foreground: 0 0% 0%; --color-background: 210 17% 97%; --color-foreground: 200 19% 18%; --color-surface: 0 0% 100%; --color-surface-hover: 200 10% 94%; --color-surface-elevated: 0 0% 100%; --color-surface-elevated-1: 0 0% 100%; --color-surface-elevated-2: 0 0% 100%; --color-surface-elevated-3: 0 0% 100%; --color-muted: 200 10% 94%; --color-muted-foreground: 200 10% 45%; --color-border: 200 10% 88%; --color-border-strong: 200 12% 75%; --color-error: 4 90% 63%; --color-success: 145 63% 42%; --color-warning: 36 100% 50%; --color-input: 0 0% 100%; --color-ring: 200 18% 46%; --color-card: 0 0% 100%; --color-card-foreground: 200 19% 18%; --color-accent: 200 10% 94%; --color-accent-foreground: 200 19% 18%; } [data-theme='stone'].dark, .dark[data-theme='stone'] { --color-primary: 200 15% 52%; --color-primary-foreground: 0 0% 0%; --color-secondary: 200 12% 35%; --color-secondary-foreground: 0 0% 100%; --color-background: 0 0% 7%; --color-foreground: 0 0% 100%; --color-surface: 200 10% 12%; --color-surface-hover: 200 10% 16%; --color-surface-elevated: 200 10% 14%; --color-surface-elevated-1: 200 10% 16%; --color-surface-elevated-2: 200 10% 20%; --color-surface-elevated-3: 200 10% 24%; --color-muted: 200 10% 20%; --color-muted-foreground: 200 10% 60%; --color-border: 200 10% 25%; --color-border-strong: 200 10% 35%; --color-error: 4 90% 58%; --color-success: 145 63% 49%; --color-warning: 48 100% 50%; --color-input: 200 10% 14%; --color-ring: 200 15% 52%; --color-card: 200 10% 12%; --color-card-foreground: 0 0% 100%; --color-accent: 200 10% 16%; --color-accent-foreground: 0 0% 100%; } /* ===== Ocean Theme (Blue) ===== */ [data-theme='ocean'] { --color-primary: 199 98% 45%; --color-primary-foreground: 0 0% 100%; --color-secondary: 199 92% 64%; --color-secondary-foreground: 0 0% 0%; --color-background: 199 100% 97%; --color-foreground: 199 100% 18%; --color-surface: 0 0% 100%; --color-surface-hover: 199 100% 94%; --color-surface-elevated: 0 0% 100%; --color-surface-elevated-1: 0 0% 100%; --color-surface-elevated-2: 0 0% 100%; --color-surface-elevated-3: 0 0% 100%; --color-muted: 199 100% 94%; --color-muted-foreground: 199 50% 40%; --color-border: 199 71% 87%; --color-border-strong: 199 79% 76%; --color-error: 4 90% 63%; --color-success: 145 63% 42%; --color-warning: 36 100% 50%; --color-input: 0 0% 100%; --color-ring: 199 98% 45%; --color-card: 0 0% 100%; --color-card-foreground: 199 100% 18%; --color-accent: 199 100% 94%; --color-accent-foreground: 199 100% 18%; } [data-theme='ocean'].dark, .dark[data-theme='ocean'] { --color-primary: 199 98% 48%; --color-primary-foreground: 0 0% 0%; --color-secondary: 199 60% 35%; --color-secondary-foreground: 0 0% 100%; --color-background: 0 0% 7%; --color-foreground: 0 0% 100%; --color-surface: 199 30% 12%; --color-surface-hover: 199 30% 16%; --color-surface-elevated: 199 30% 14%; --color-surface-elevated-1: 199 30% 16%; --color-surface-elevated-2: 199 30% 20%; --color-surface-elevated-3: 199 30% 24%; --color-muted: 199 20% 20%; --color-muted-foreground: 199 20% 60%; --color-border: 199 20% 25%; --color-border-strong: 199 20% 35%; --color-error: 4 90% 58%; --color-success: 145 63% 49%; --color-warning: 48 100% 50%; --color-input: 199 30% 14%; --color-ring: 199 98% 48%; --color-card: 199 30% 12%; --color-card-foreground: 0 0% 100%; --color-accent: 199 30% 16%; --color-accent-foreground: 0 0% 100%; } /* ===== Dark mode via media query (fallback for users without explicit preference) ===== */ @media (prefers-color-scheme: dark) { :root:not(.dark):not([data-theme]) { --color-primary: 47 95% 58%; --color-primary-foreground: 0 0% 0%; --color-secondary: 47 70% 29%; --color-secondary-foreground: 0 0% 100%; --color-background: 0 0% 6%; --color-foreground: 0 0% 100%; --color-surface: 0 0% 12%; --color-surface-hover: 0 0% 16%; --color-surface-elevated: 0 0% 14%; --color-surface-elevated-1: 0 0% 16%; --color-surface-elevated-2: 0 0% 20%; --color-surface-elevated-3: 0 0% 24%; --color-muted: 0 0% 20%; --color-muted-foreground: 0 0% 60%; --color-border: 0 0% 26%; --color-border-strong: 0 0% 35%; --color-error: 6 78% 57%; --color-success: 145 63% 49%; --color-warning: 48 100% 50%; --color-input: 0 0% 14%; --color-ring: 47 95% 58%; --color-card: 0 0% 12%; --color-card-foreground: 0 0% 100%; --color-accent: 0 0% 16%; --color-accent-foreground: 0 0% 100%; } } /* ===== Base Styles ===== */ @layer base { * { border-color: hsl(var(--color-border)); } body { background-color: hsl(var(--color-background)); color: hsl(var(--color-foreground)); font-family: 'Inter', system-ui, -apple-system, sans-serif; } html { color-scheme: light; } html.dark { color-scheme: dark; } } /* ===== Component Utilities ===== */ @layer components { /* Primary Button */ .btn-primary { background-color: hsl(var(--color-primary)); color: hsl(var(--color-primary-foreground)); padding: 0.5rem 1rem; border-radius: var(--radius-md); font-weight: 500; transition: all 0.2s ease; } .btn-primary:hover { filter: brightness(0.9); } .btn-primary:focus-visible { outline: 2px solid hsl(var(--color-ring)); outline-offset: 2px; } /* Secondary Button */ .btn-secondary { background-color: hsl(var(--color-secondary)); color: hsl(var(--color-secondary-foreground)); padding: 0.5rem 1rem; border-radius: var(--radius-md); font-weight: 500; transition: all 0.2s ease; } .btn-secondary:hover { filter: brightness(0.95); } /* Ghost Button */ .btn-ghost { background-color: transparent; color: hsl(var(--color-foreground)); padding: 0.5rem 1rem; border-radius: var(--radius-md); font-weight: 500; transition: all 0.2s ease; } .btn-ghost:hover { background-color: hsl(var(--color-surface-hover)); } /* Card */ .card { background-color: hsl(var(--color-surface)); border: 1px solid hsl(var(--color-border)); border-radius: var(--radius-lg); padding: 1rem; } .card-elevated { background-color: hsl(var(--color-surface-elevated)); box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); } /* Input */ .input { background-color: hsl(var(--color-input)); border: 1px solid hsl(var(--color-border)); border-radius: var(--radius-md); padding: 0.5rem 0.75rem; color: hsl(var(--color-foreground)); transition: border-color 0.2s ease, box-shadow 0.2s ease; } .input:focus { border-color: hsl(var(--color-ring)); box-shadow: 0 0 0 2px hsl(var(--color-ring) / 0.2); outline: none; } .input::placeholder { color: hsl(var(--color-muted-foreground)); } /* Badge */ .badge { display: inline-flex; align-items: center; padding: 0.25rem 0.5rem; border-radius: var(--radius); font-size: 0.75rem; font-weight: 500; background-color: hsl(var(--color-muted)); color: hsl(var(--color-muted-foreground)); } .badge-primary { background-color: hsl(var(--color-primary) / 0.1); color: hsl(var(--color-primary)); } .badge-success { background-color: hsl(var(--color-success) / 0.1); color: hsl(var(--color-success)); } .badge-error { background-color: hsl(var(--color-error) / 0.1); color: hsl(var(--color-error)); } .badge-warning { background-color: hsl(var(--color-warning) / 0.1); color: hsl(var(--color-warning)); } }