Add harness workflow and Material showcase design system

This commit is contained in:
2026-03-23 17:30:30 +08:00
parent c570431dba
commit 5d02bf9df4
46 changed files with 3343 additions and 1068 deletions
+296 -47
View File
@@ -1,22 +1,25 @@
export const themeNames = ["morandi", "earth", "brand"] as const;
export const themeNames = ["violet", "jade", "sunset"] as const;
export type ThemeName = (typeof themeNames)[number];
export const defaultTheme: ThemeName = "morandi";
export const defaultTheme: ThemeName = "violet";
export const themeDetails = {
morandi: {
label: "Morandi",
note: "Muted dusty neutrals with a calm, understated luxury mood"
violet: {
label: "Violet Seed",
note: "A Material baseline seeded from a soft violet, close to the reference M3 demos.",
seed: "#6750A4"
},
earth: {
label: "Earth",
note: "Organic browns, terracotta warmth, olive depth, and sandstone calm"
jade: {
label: "Jade Seed",
note: "Cool green-blue tonal families for calmer product surfaces and lower visual heat.",
seed: "#0B8F83"
},
brand: {
label: "Brand",
note: "Verdant accent scaffold"
sunset: {
label: "Sunset Seed",
note: "Warm coral tonal families for more human, expressive Material palettes.",
seed: "#B75A46"
}
} as const satisfies Record<ThemeName, { label: string; note: string }>;
} as const satisfies Record<ThemeName, { label: string; note: string; seed: string }>;
export const motionModeNames = ["interactive", "static"] as const;
export type MotionModeName = (typeof motionModeNames)[number];
@@ -25,12 +28,12 @@ export const defaultMotionMode: MotionModeName = "interactive";
export const motionModeDetails = {
interactive: {
label: "Interactive",
note: "Micro-interactions with hover lift, press feedback, focus transitions, and animated state changes"
label: "Standard",
note: "The default Material motion language for state, depth, and spatial feedback."
},
static: {
label: "Static",
note: "Keep visual states readable while removing motion-heavy feedback and animation"
note: "Preserve clarity while removing motion-heavy transitions and hover choreography."
}
} as const satisfies Record<MotionModeName, { label: string; note: string }>;
@@ -44,66 +47,84 @@ export const motionScale = {
export const colorTokens = [
{ name: "background", cssVar: "--color-background", role: "Application canvas" },
{ name: "surface", cssVar: "--color-surface", role: "Base surface container" },
{
name: "surface-container",
cssVar: "--color-surface-container",
role: "Default tonal container for cards and supporting panels"
},
{
name: "surface-container-high",
cssVar: "--color-surface-container-high",
role: "Raised tonal container for overlays and prominent groups"
},
{
name: "surface-container-highest",
cssVar: "--color-surface-container-highest",
role: "Highest emphasis surface used for fields and selected chips"
},
{ name: "foreground", cssVar: "--color-foreground", role: "Primary text and icons" },
{ name: "surface", cssVar: "--color-surface", role: "Secondary surface backgrounds" },
{
name: "surface-strong",
cssVar: "--color-surface-strong",
role: "Elevated surface emphasis"
name: "on-surface-variant",
cssVar: "--color-on-surface-variant",
role: "Supporting text, dividers, and lower-emphasis iconography"
},
{ name: "card", cssVar: "--color-card", role: "Cards and floating panels" },
{ name: "border", cssVar: "--color-border", role: "Default dividers and input borders" },
{ name: "primary", cssVar: "--color-primary", role: "Filled actions and active emphasis" },
{
name: "border-strong",
cssVar: "--color-border-strong",
role: "Higher emphasis dividers"
name: "primary-container",
cssVar: "--color-primary-container",
role: "Tonal action backgrounds and highlighted containers"
},
{ name: "primary", cssVar: "--color-primary", role: "Primary actions and highlights" },
{
name: "secondary",
cssVar: "--color-secondary",
role: "Secondary fills and supporting actions"
name: "secondary-container",
cssVar: "--color-secondary-container",
role: "Filled tonal surfaces for secondary emphasis"
},
{ name: "muted", cssVar: "--color-muted", role: "Subtle supporting surfaces" },
{
name: "muted-foreground",
cssVar: "--color-muted-foreground",
role: "Secondary text and captions"
name: "tertiary-container",
cssVar: "--color-tertiary-container",
role: "Expressive accent container for supportive highlights"
},
{ name: "accent", cssVar: "--color-accent", role: "Moments of emphasis or delight" },
{ name: "success", cssVar: "--color-success", role: "Success feedback" },
{ name: "warning", cssVar: "--color-warning", role: "Warning feedback" },
{ name: "destructive", cssVar: "--color-destructive", role: "Destructive actions" }
{ name: "outline", cssVar: "--color-outline", role: "Primary stroke and input outline color" },
{
name: "outline-variant",
cssVar: "--color-outline-variant",
role: "Lower-emphasis border and separator color"
},
{ name: "surface-tint", cssVar: "--color-surface-tint", role: "Tint color for tonal elevation" },
{ name: "error", cssVar: "--color-error", role: "Error and destructive feedback" },
{ name: "success", cssVar: "--color-success", role: "Positive validation and confirmation" },
{ name: "warning", cssVar: "--color-warning", role: "Cautionary feedback" }
] as const;
export const typographyTokens = [
{
name: "caption",
fontVar: "--text-xs",
lineHeightVar: "--leading-normal",
name: "label",
fontVar: "--text-sm",
lineHeightVar: "--leading-snug",
familyVar: "--font-sans",
sample: "Small labels, metadata, and supporting notes."
sample: "Labels stay crisp, compact, and readable across controls."
},
{
name: "body",
fontVar: "--text-base",
lineHeightVar: "--leading-normal",
familyVar: "--font-sans",
sample: "Body copy stays warm, readable, and stable across themes."
sample: "Body copy stays clear and quiet so color and hierarchy do the expressive work."
},
{
name: "lead",
name: "title",
fontVar: "--text-xl",
lineHeightVar: "--leading-loose",
familyVar: "--font-sans",
sample: "Lead text introduces a surface without becoming display copy."
lineHeightVar: "--leading-snug",
familyVar: "--font-display",
sample: "Titles feel warm and rounded, without drifting into editorial flourish."
},
{
name: "display",
fontVar: "--text-4xl",
lineHeightVar: "--leading-tight",
familyVar: "--font-display",
sample: "Display text carries the editorial voice of the system."
sample: "Display copy should feel optimistic, human, and distinctly Material."
}
] as const;
@@ -149,6 +170,61 @@ export const motionTokens = {
]
} as const;
type DynamicColorVariableName =
| "--color-background"
| "--color-foreground"
| "--color-surface"
| "--color-surface-strong"
| "--color-surface-contrast"
| "--color-surface-dim"
| "--color-surface-bright"
| "--color-surface-container-low"
| "--color-surface-container"
| "--color-surface-container-high"
| "--color-surface-container-highest"
| "--color-border"
| "--color-border-strong"
| "--color-input"
| "--color-ring"
| "--color-primary"
| "--color-primary-foreground"
| "--color-primary-container"
| "--color-on-primary-container"
| "--color-secondary"
| "--color-secondary-foreground"
| "--color-secondary-container"
| "--color-on-secondary-container"
| "--color-tertiary"
| "--color-tertiary-foreground"
| "--color-tertiary-container"
| "--color-on-tertiary-container"
| "--color-muted"
| "--color-muted-foreground"
| "--color-accent"
| "--color-accent-foreground"
| "--color-success"
| "--color-success-foreground"
| "--color-warning"
| "--color-warning-foreground"
| "--color-destructive"
| "--color-destructive-foreground"
| "--color-card"
| "--color-card-foreground"
| "--color-overlay"
| "--color-outline"
| "--color-outline-variant"
| "--color-on-surface"
| "--color-on-surface-variant"
| "--color-surface-tint"
| "--color-error"
| "--color-on-error"
| "--color-error-container"
| "--color-on-error-container"
| "--color-inverse-surface"
| "--color-inverse-on-surface";
export type DynamicColorVariables = Record<DynamicColorVariableName, string>;
function getTargetElement(root?: HTMLElement) {
if (root) {
return root;
@@ -161,6 +237,163 @@ function getTargetElement(root?: HTMLElement) {
return document.documentElement;
}
function clamp(value: number, min: number, max: number) {
return Math.min(Math.max(value, min), max);
}
function normalizeHexColor(seed: string) {
const normalized = seed.trim().replace(/^#/, "");
if (/^[0-9a-f]{3}$/i.test(normalized)) {
return `#${normalized
.split("")
.map((char) => `${char}${char}`)
.join("")
.toLowerCase()}`;
}
if (/^[0-9a-f]{6}$/i.test(normalized)) {
return `#${normalized.toLowerCase()}`;
}
throw new Error(`Expected a hex seed color such as #6750A4. Received "${seed}".`);
}
function hexToRgb(seed: string) {
const normalized = normalizeHexColor(seed).slice(1);
return {
b: Number.parseInt(normalized.slice(4, 6), 16),
g: Number.parseInt(normalized.slice(2, 4), 16),
r: Number.parseInt(normalized.slice(0, 2), 16)
};
}
function rgbToHsl({ b, g, r }: { b: number; g: number; r: number }) {
const red = r / 255;
const green = g / 255;
const blue = b / 255;
const max = Math.max(red, green, blue);
const min = Math.min(red, green, blue);
const lightness = (max + min) / 2;
const delta = max - min;
if (delta === 0) {
return { h: 0, l: lightness * 100, s: 0 };
}
const saturation =
lightness > 0.5 ? delta / (2 - max - min) : delta / (max + min);
let hue = 0;
switch (max) {
case red:
hue = (green - blue) / delta + (green < blue ? 6 : 0);
break;
case green:
hue = (blue - red) / delta + 2;
break;
default:
hue = (red - green) / delta + 4;
break;
}
return {
h: hue * 60,
l: lightness * 100,
s: saturation * 100
};
}
function shiftHue(hue: number, delta: number) {
return (hue + delta + 360) % 360;
}
function toColor(hue: number, saturation: number, lightness: number) {
return `hsl(${Math.round(hue)} ${Math.round(clamp(saturation, 0, 100))}% ${Math.round(
clamp(lightness, 0, 100)
)}%)`;
}
export function createDynamicColorVariables(seed: string): DynamicColorVariables {
const { h, s } = rgbToHsl(hexToRgb(seed));
const accentSaturation = clamp(Math.max(42, s * 0.92), 42, 82);
const neutralHue = shiftHue(h, 8);
const secondaryHue = shiftHue(h, 12);
const tertiaryHue = shiftHue(h, -118);
const neutralSaturation = clamp(s * 0.1, 5, 13);
const neutralVariantSaturation = clamp(s * 0.22, 9, 20);
const containerSaturation = clamp(s * 0.24, 12, 22);
return {
"--color-background": toColor(neutralHue, neutralSaturation, 97),
"--color-foreground": toColor(neutralHue, neutralSaturation + 2, 15),
"--color-surface": toColor(neutralHue, neutralSaturation, 95),
"--color-surface-strong": toColor(secondaryHue, clamp(neutralVariantSaturation * 1.15, 10, 24), 89),
"--color-surface-contrast": toColor(neutralHue, neutralVariantSaturation + 2, 28),
"--color-surface-dim": toColor(neutralHue, neutralSaturation, 87),
"--color-surface-bright": toColor(neutralHue, neutralSaturation, 99),
"--color-surface-container-low": toColor(neutralHue, neutralSaturation, 96),
"--color-surface-container": toColor(secondaryHue, clamp(neutralVariantSaturation * 0.8, 8, 18), 92),
"--color-surface-container-high": toColor(h, clamp(neutralVariantSaturation * 0.95, 9, 20), 88),
"--color-surface-container-highest": toColor(tertiaryHue, clamp(s * 0.14, 9, 18), 84),
"--color-border": toColor(secondaryHue, clamp(neutralVariantSaturation * 0.9, 8, 18), 80),
"--color-border-strong": toColor(secondaryHue, clamp(neutralVariantSaturation * 1.2, 10, 24), 54),
"--color-input": toColor(secondaryHue, clamp(neutralVariantSaturation * 1.2, 10, 24), 54),
"--color-ring": toColor(h, accentSaturation, 40),
"--color-primary": toColor(h, accentSaturation, 44),
"--color-primary-foreground": toColor(h, 26, 98),
"--color-primary-container": toColor(h, clamp(s * 0.56, 24, 58), 86),
"--color-on-primary-container": toColor(h, accentSaturation, 18),
"--color-secondary": toColor(secondaryHue, containerSaturation, 90),
"--color-secondary-foreground": toColor(secondaryHue, clamp(s * 0.28, 14, 22), 22),
"--color-secondary-container": toColor(secondaryHue, containerSaturation, 89),
"--color-on-secondary-container": toColor(secondaryHue, clamp(s * 0.28, 14, 22), 22),
"--color-tertiary": toColor(tertiaryHue, clamp(s * 0.26, 14, 24), 40),
"--color-tertiary-foreground": toColor(tertiaryHue, 20, 98),
"--color-tertiary-container": toColor(tertiaryHue, clamp(s * 0.18, 12, 18), 86),
"--color-on-tertiary-container": toColor(tertiaryHue, clamp(s * 0.22, 14, 22), 18),
"--color-muted": toColor(neutralHue, neutralSaturation, 93),
"--color-muted-foreground": toColor(neutralHue, neutralVariantSaturation, 34),
"--color-accent": toColor(tertiaryHue, clamp(s * 0.18, 12, 18), 86),
"--color-accent-foreground": toColor(tertiaryHue, clamp(s * 0.22, 14, 22), 18),
"--color-success": toColor(152, 35, 42),
"--color-success-foreground": toColor(152, 18, 98),
"--color-warning": toColor(76, 62, 48),
"--color-warning-foreground": toColor(76, 20, 14),
"--color-destructive": toColor(12, 72, 44),
"--color-destructive-foreground": toColor(12, 20, 98),
"--color-card": toColor(secondaryHue, clamp(neutralSaturation * 0.9, 5, 14), 96),
"--color-card-foreground": toColor(neutralHue, neutralSaturation + 2, 15),
"--color-overlay": "color-mix(in oklch, black 24%, transparent)",
"--color-outline": toColor(secondaryHue, clamp(neutralVariantSaturation * 1.1, 10, 22), 54),
"--color-outline-variant": toColor(secondaryHue, clamp(neutralVariantSaturation * 0.95, 9, 20), 82),
"--color-on-surface": toColor(neutralHue, neutralSaturation + 2, 15),
"--color-on-surface-variant": toColor(neutralHue, neutralVariantSaturation, 34),
"--color-surface-tint": toColor(h, accentSaturation, 40),
"--color-error": toColor(12, 72, 44),
"--color-on-error": toColor(12, 20, 98),
"--color-error-container": toColor(12, 58, 88),
"--color-on-error-container": toColor(12, 72, 20),
"--color-inverse-surface": toColor(neutralHue, neutralSaturation + 2, 20),
"--color-inverse-on-surface": toColor(neutralHue, neutralSaturation, 96)
};
}
function applyDynamicColorVariables(
variables: DynamicColorVariables,
target: HTMLElement,
metadata: { seed: string; theme: string }
) {
for (const [name, value] of Object.entries(variables)) {
target.style.setProperty(name, value);
}
target.dataset.theme = metadata.theme;
target.dataset.seedColor = normalizeHexColor(metadata.seed);
}
export function setTheme(theme: ThemeName, root?: HTMLElement) {
const target = getTargetElement(root);
@@ -168,7 +401,23 @@ export function setTheme(theme: ThemeName, root?: HTMLElement) {
return;
}
target.dataset.theme = theme;
applyDynamicColorVariables(createDynamicColorVariables(themeDetails[theme].seed), target, {
seed: themeDetails[theme].seed,
theme
});
}
export function setDynamicColor(seed: string, root?: HTMLElement) {
const target = getTargetElement(root);
if (!target) {
return;
}
applyDynamicColorVariables(createDynamicColorVariables(seed), target, {
seed,
theme: "dynamic"
});
}
export function setMotionMode(mode: MotionModeName, root?: HTMLElement) {
+108 -6
View File
@@ -2,14 +2,14 @@
:root[data-motion="interactive"],
[data-motion="interactive"] {
--dur-instant: 1ms;
--dur-fast: 140ms;
--dur-base: 200ms;
--dur-fast: 120ms;
--dur-base: 180ms;
--dur-slow: 280ms;
--dur-deliberate: 300ms;
--dur-deliberate: 360ms;
--ease-standard: cubic-bezier(0.25, 1, 0.5, 1);
--ease-emphasized: cubic-bezier(0.22, 1, 0.36, 1);
--ease-exit: cubic-bezier(0.3, 1, 0.5, 1);
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
--ease-emphasized: cubic-bezier(0.2, 0, 0, 1);
--ease-exit: cubic-bezier(0.4, 0, 1, 1);
--distance-xs: 4px;
--distance-sm: 8px;
@@ -105,6 +105,81 @@
}
}
@keyframes aiui-float-soft {
0%,
100% {
transform: translate3d(0, 0, 0) rotate(0deg);
}
50% {
transform: translate3d(calc(var(--distance-xs) * 0.5), calc(var(--distance-sm) * -1), 0)
rotate(-0.8deg);
}
}
@keyframes aiui-breathe {
0%,
100% {
opacity: 0.8;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(var(--scale-pop));
}
}
@keyframes aiui-drift {
0%,
100% {
transform: translate3d(0, 0, 0) scale(1);
}
33% {
transform: translate3d(2.5%, -3%, 0) scale(1.025);
}
66% {
transform: translate3d(-2%, 1.8%, 0) scale(0.992);
}
}
@keyframes aiui-float-hero {
0%,
100% {
transform: translate3d(0, 0, 0) rotate(0deg) scale(1);
}
25% {
transform: translate3d(1.2%, calc(var(--distance-sm) * -0.7), 0) rotate(-1deg)
scale(1.01);
}
50% {
transform: translate3d(-0.8%, calc(var(--distance-md) * -0.85), 0) rotate(0.8deg)
scale(1.02);
}
75% {
transform: translate3d(-1.2%, calc(var(--distance-sm) * -0.35), 0) rotate(-0.4deg)
scale(1.005);
}
}
@keyframes aiui-glimmer {
0% {
opacity: 0;
transform: translateX(-120%);
}
20%,
100% {
opacity: 1;
transform: translateX(120%);
}
}
.motion-transition {
transition-duration: var(--dur-base);
transition-property: color, background-color, border-color, box-shadow, opacity,
@@ -166,6 +241,33 @@
both;
}
.motion-float {
animation: aiui-float-soft calc(var(--dur-deliberate) * 8) var(--ease-emphasized) infinite;
}
.motion-float-delayed {
animation: aiui-float-soft calc(var(--dur-deliberate) * 9) var(--ease-emphasized) infinite;
animation-delay: 180ms;
}
.motion-float-hero {
animation: aiui-float-hero calc(var(--dur-deliberate) * 11) var(--ease-emphasized)
infinite;
}
.motion-breathe {
animation: aiui-breathe calc(var(--dur-deliberate) * 6) var(--ease-standard) infinite;
}
.motion-drift {
animation: aiui-drift calc(var(--dur-deliberate) * 14) var(--ease-standard) infinite;
transform-origin: center;
}
.motion-glimmer {
animation: aiui-glimmer calc(var(--dur-deliberate) * 3.5) var(--ease-standard) infinite;
}
.motion-ring {
transition-duration: var(--dur-fast);
transition-property: box-shadow, outline-color, border-color;
+76 -106
View File
@@ -1,130 +1,100 @@
:root {
--font-sans: "Avenir Next", "Segoe UI", sans-serif;
--font-display: "Iowan Old Style", "Palatino Linotype", "Book Antiqua", Georgia,
serif;
color-scheme: light;
--font-sans:
"Google Sans Text", "Google Sans", "Roboto Flex", "Roboto", "Segoe UI", sans-serif;
--font-display:
"Google Sans Display", "Google Sans", "Roboto Flex", "Roboto", "Segoe UI", sans-serif;
--font-mono: "SF Mono", "SFMono-Regular", "Consolas", monospace;
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.125rem;
--text-xl: 1.25rem;
--text-2xl: 1.5rem;
--text-3xl: 2rem;
--text-4xl: clamp(2.5rem, 4vw, 4rem);
--text-xl: 1.375rem;
--text-2xl: 1.75rem;
--text-3xl: 2.25rem;
--text-4xl: clamp(2.75rem, 4vw, 4.75rem);
--leading-tight: 1.1;
--leading-snug: 1.25;
--leading-snug: 1.3;
--leading-normal: 1.5;
--leading-loose: 1.7;
--leading-loose: 1.65;
--tracking-tight: -0.03em;
--tracking-tight: -0.02em;
--tracking-normal: 0;
--tracking-caps: 0.18em;
--tracking-caps: 0.12em;
--border-width-thin: 1px;
--border-width-strong: 1.5px;
--border-width-strong: 1px;
--radius-xs: 8px;
--radius-sm: 12px;
--radius-md: 18px;
--radius-sm: 16px;
--radius-md: 20px;
--radius-lg: 28px;
--radius-xl: 40px;
--radius-xl: 36px;
--radius-full: 999px;
--shadow-xs: 0 1px 2px oklch(0.28 0.02 55 / 0.06);
--shadow-sm: 0 8px 24px oklch(0.28 0.02 55 / 0.08);
--shadow-md: 0 18px 48px oklch(0.28 0.03 55 / 0.12);
--shadow-lg: 0 32px 72px oklch(0.2 0.02 55 / 0.16);
}
--shadow-xs: 0 1px 2px rgb(25 18 42 / 0.08), 0 4px 10px rgb(94 74 145 / 0.05);
--shadow-sm: 0 8px 22px rgb(83 63 128 / 0.12), 0 2px 8px rgb(25 18 42 / 0.06);
--shadow-md: 0 18px 42px rgb(83 63 128 / 0.16), 0 6px 18px rgb(25 18 42 / 0.1);
--shadow-lg: 0 30px 70px rgb(83 63 128 / 0.18), 0 14px 32px rgb(25 18 42 / 0.12);
:root,
[data-theme="morandi"] {
color-scheme: light;
--color-background: color-mix(in oklch, #d4b5a0 10%, white 90%);
--color-foreground: #544c46;
--color-surface: color-mix(in oklch, #a6b3a7 12%, white 88%);
--color-surface-strong: color-mix(in oklch, #d4b5a0 20%, white 80%);
--color-surface-contrast: #6a615a;
--color-border: color-mix(in oklch, #9b8e82 34%, white 66%);
--color-border-strong: #9b8e82;
--color-input: var(--color-border);
--color-ring: #8e9aaf;
--color-primary: #6f7785;
--color-primary-foreground: #f7f3ef;
--color-secondary: #a6b3a7;
--color-secondary-foreground: #495247;
--color-muted: color-mix(in oklch, #d4b5a0 18%, white 82%);
--color-muted-foreground: #776c64;
--color-accent: #c4a882;
--color-accent-foreground: #4f4334;
--color-success: #879686;
--color-success-foreground: #f6f1ec;
--color-warning: #ba9c73;
--color-warning-foreground: #4d4031;
--color-destructive: #a7837c;
--color-destructive-foreground: #f9f4ef;
--color-card: color-mix(in oklch, var(--color-surface) 82%, white 18%);
--color-card-foreground: var(--color-foreground);
--color-overlay: rgb(84 76 70 / 0.42);
}
--color-background: hsl(22 18% 96%);
--color-foreground: hsl(259 6% 15%);
--color-surface: hsl(22 12% 94%);
--color-surface-strong: hsl(278 24% 87%);
--color-surface-contrast: hsl(259 10% 28%);
--color-surface-dim: hsl(22 10% 87%);
--color-surface-bright: hsl(22 18% 98%);
--color-surface-container-low: hsl(18 20% 97%);
--color-surface-container: hsl(278 20% 91%);
--color-surface-container-high: hsl(274 24% 88%);
--color-surface-container-highest: hsl(112 22% 84%);
--color-outline: hsl(259 10% 56%);
--color-outline-variant: hsl(259 12% 82%);
--color-border: var(--color-outline-variant);
--color-border-strong: var(--color-outline);
--color-input: var(--color-outline);
--color-ring: hsl(259 40% 42%);
[data-theme="earth"] {
color-scheme: light;
--color-background: color-mix(in oklch, #d4c5a9 34%, white 66%);
--color-foreground: #4f3c27;
--color-surface: color-mix(in oklch, #d4c5a9 52%, white 48%);
--color-surface-strong: color-mix(in oklch, #c4956a 24%, white 76%);
--color-surface-contrast: #6f5437;
--color-border: color-mix(in oklch, #8b6f47 40%, white 60%);
--color-border-strong: #8b6f47;
--color-input: var(--color-border);
--color-ring: #a0522d;
--color-primary: #8b6f47;
--color-primary-foreground: #f7f1e7;
--color-secondary: #6b7c3f;
--color-secondary-foreground: #f4efe6;
--color-muted: color-mix(in oklch, #c4956a 18%, white 82%);
--color-muted-foreground: #78644a;
--color-accent: #a0522d;
--color-accent-foreground: #f8efe6;
--color-success: #72864c;
--color-success-foreground: #f5f1e8;
--color-warning: #c4956a;
--color-warning-foreground: #4a3826;
--color-destructive: #93492b;
--color-destructive-foreground: #faefe7;
--color-card: color-mix(in oklch, var(--color-surface) 84%, white 16%);
--color-card-foreground: var(--color-foreground);
--color-overlay: rgb(79 60 39 / 0.4);
}
--color-primary: hsl(264 38% 45%);
--color-primary-foreground: hsl(259 24% 98%);
--color-primary-container: hsl(272 38% 86%);
--color-on-primary-container: hsl(264 30% 20%);
[data-theme="brand"] {
color-scheme: light;
--color-background: oklch(0.972 0.016 172);
--color-foreground: oklch(0.24 0.03 182);
--color-surface: oklch(0.946 0.018 172);
--color-surface-strong: oklch(0.91 0.024 172);
--color-surface-contrast: oklch(0.29 0.034 182);
--color-border: oklch(0.83 0.026 172);
--color-border-strong: oklch(0.67 0.045 176);
--color-input: var(--color-border);
--color-ring: oklch(0.53 0.12 190);
--color-primary: oklch(0.48 0.12 188);
--color-primary-foreground: oklch(0.97 0.008 172);
--color-secondary: oklch(0.82 0.066 156);
--color-secondary-foreground: oklch(0.2 0.02 178);
--color-muted: oklch(0.91 0.018 172);
--color-muted-foreground: oklch(0.42 0.03 180);
--color-accent: oklch(0.75 0.105 130);
--color-accent-foreground: oklch(0.2 0.02 160);
--color-success: oklch(0.6 0.12 155);
--color-success-foreground: oklch(0.98 0.006 170);
--color-warning: oklch(0.76 0.13 86);
--color-warning-foreground: oklch(0.22 0.02 74);
--color-destructive: oklch(0.53 0.16 30);
--color-destructive-foreground: oklch(0.98 0.01 80);
--color-card: color-mix(in oklch, var(--color-surface) 88%, white 12%);
--color-secondary: hsl(286 24% 90%);
--color-secondary-foreground: hsl(284 24% 22%);
--color-secondary-container: hsl(286 24% 90%);
--color-on-secondary-container: hsl(284 24% 22%);
--color-tertiary: hsl(128 18% 40%);
--color-tertiary-foreground: hsl(128 16% 98%);
--color-tertiary-container: hsl(112 24% 84%);
--color-on-tertiary-container: hsl(128 18% 22%);
--color-muted: var(--color-surface-container);
--color-muted-foreground: hsl(259 10% 34%);
--color-accent: var(--color-tertiary-container);
--color-accent-foreground: var(--color-on-tertiary-container);
--color-success: hsl(152 35% 42%);
--color-success-foreground: hsl(152 18% 98%);
--color-warning: hsl(76 62% 48%);
--color-warning-foreground: hsl(76 20% 14%);
--color-destructive: hsl(12 72% 44%);
--color-destructive-foreground: hsl(12 20% 98%);
--color-error: var(--color-destructive);
--color-on-error: var(--color-destructive-foreground);
--color-error-container: hsl(12 58% 88%);
--color-on-error-container: hsl(12 72% 20%);
--color-card: var(--color-surface-container-low);
--color-card-foreground: var(--color-foreground);
--color-overlay: oklch(0.13 0.015 185 / 0.5);
--color-overlay: color-mix(in oklch, black 24%, transparent);
--color-on-surface: var(--color-foreground);
--color-on-surface-variant: var(--color-muted-foreground);
--color-surface-tint: var(--color-primary);
--color-inverse-surface: hsl(259 6% 20%);
--color-inverse-on-surface: hsl(259 4% 96%);
}