# Design System Reusable design tokens + scales extracted from the Luxam reference build. Drop into `globals.css` (Tailwind v4 `@theme`) of any new project to start with the same DNA. --- ## Color palette ```css @theme { /* Backgrounds obsidian → graphite layering */ --color-obsidian: #08080A; /* page background */ --color-graphite: #15161A; /* dark glass card */ --color-graphite-2: #1C1D22; /* slightly lifted card variant */ /* Foreground */ --color-bone: #F4F0E6; /* primary text on dark */ --color-mist: #C7C2B5; /* secondary/muted text */ /* Brand gold spectrum (use sparingly + structurally) */ --color-gold: #D4A437; /* core brand gold */ --color-gold-light: #F2C25B; /* hover, highlight, gradient stops */ --color-gold-deep: #7A4A12; /* gradient base */ --color-ember: #B5731A; /* warm secondary */ /* Sustainability accent (use only where ecology theme applies) */ --color-emerald: #1F8F6B; --color-emerald-glow: #3FD8A4; } ``` **Usage rules** - **Background:** `bg-obsidian` body, `bg-graphite/40 backdrop-blur-sm` cards. - **Borders:** `border-bone/10` default, `border-gold/30` on hover, `border-gold/40` active. - **Text:** `text-bone` titles, `text-mist` body/secondary, `text-gold-light` brand accents. - **Never use** raw `#fff` or pure `#000`. Always `bone` or `obsidian`. - **Gradients:** single-axis brand gradient `from-gold-light via-gold to-gold-deep`. No rainbow. --- ## Typography ```ts // app/layout.tsx import { Cormorant_Garamond, Geist, Geist_Mono } from "next/font/google"; const display = Cormorant_Garamond({ subsets: ["latin"], variable: "--font-display", weight: ["400", "500", "600"], display: "swap", }); const sans = Geist({ subsets: ["latin"], variable: "--font-sans", display: "swap" }); const mono = Geist_Mono({ subsets: ["latin"], variable: "--font-mono", display: "swap" }); ``` **Hierarchy** | Role | Font | Size | |---|---|---| | Hero H1 | `font-display` (serif) | `text-[clamp(2.6rem,7vw,6.4rem)] leading-[1.0] tracking-[-0.02em]` | | Section H2 | `font-display` | `text-[clamp(2.4rem,5vw,4.6rem)] leading-[1.02] tracking-[-0.02em]` | | Card H3/H4 | `font-display` | `text-2xl md:text-3xl` | | Body | `font-sans` (inherited default) | `text-base md:text-lg leading-[1.7]` | | UI label | `font-sans` | `text-[10px] md:text-[11px] font-medium uppercase tracking-[0.22em]` | | Nav link | `font-sans` | `text-sm font-medium tracking-[0.005em]` | | Stats number | `font-display` | `text-5xl md:text-6xl` (responsive smaller on mobile) | **Avoid** - `font-mono` on long values or body text. - `tracking-[0.3em]+` on small labels. - `leading-[0.95]` on serif headlines (clips ascenders). **Helper class** for one-color brand text: ```css .gold-text { background: linear-gradient(90deg, #FFEFC5, #F2C25B 40%, #D4A437 80%, #7A4A12); -webkit-background-clip: text; background-clip: text; color: transparent; } ``` --- ## Spacing scale Section padding lives **only** on the `Section` primitive: ```tsx // components/ui/Section.tsx className={cn("relative scroll-mt-24 py-16 md:scroll-mt-28 md:py-20 lg:py-24", className)} ``` - Mobile: `py-16` (64 px) - Tablet: `md:py-20` (80 px) - Desktop: `lg:py-24` (96 px) - All anchored sections get `scroll-mt-24 md:scroll-mt-28` for sticky-nav clearance. **Don't** override per-section unless there's a structural reason. Centralize. Card padding: `p-6 md:p-7` standard, `p-7 md:p-10` for hero/feature cards. Grid gaps: `gap-4 md:gap-5` standard, `gap-6 md:gap-8` for large cards. --- ## Border radius scale ```css --radius-xs: 4px; --radius-sm: 8px; --radius-md: 14px; --radius-lg: 22px; --radius-xl: 32px; ``` Tailwind shortcuts to match: `rounded-2xl` (cards), `rounded-3xl` (hero panels), `rounded-full` (pills + buttons). --- ## Motion ```ts // shared variants const cardGrid: Variants = { hidden: {}, visible: { transition: { staggerChildren: 0.08, delayChildren: 0.15 } }, }; const cardCell: Variants = { hidden: { opacity: 0, y: 24, filter: "blur(6px)" }, visible: { opacity: 1, y: 0, filter: "blur(0px)", transition: { duration: 0.8, ease: [0.16, 1, 0.3, 1] }, }, }; ``` - Entrance ease: `[0.16, 1, 0.3, 1]` (cubic-bezier "out-quart-ish"). - Duration: hero 1.3s, cards 0.8s, hover 0.3-0.5s. - Hover lift: `-translate-y-1` max. No `-translate-y-2`+ (jumpy). - Halo pulse: `[0.5, 0.85, 0.5]` opacity, 6s loop. Wrap in `!reduce && (...)`. - `useReducedMotion()` must be read in any animated component. **Forbidden** - Bouncy springs (`stiffness > 200`) on entrance. - Scale jumps > 1.05 on hover for cards. - Parallax > 200 px translate range. - Marquees without `mask-image` edge fade. --- ## Visual textures | Class | Purpose | |---|---| | `noise-bg` | Subtle film-grain background overlay | | `grain` | Animated grain (use sparingly on cards) | | `metallic-divider` | Thin gold-tinted divider line | | `radial-gold` | Faint amber radial wash (positioned absolute, blur-3xl) | | `radial-emerald` | Same, emerald variant | | `hairline-t`, `hairline-b` | 1 px top/bottom hairline borders | | `mask-fade-y`, `mask-fade-x` | Edge fade masks | Define these as utilities in `globals.css` per the Luxam reference. --- ## Hover state pattern Every card/button uses the same hover vocabulary: 1. `-translate-y-1` lift (cards) or `-translate-y-0.5` (buttons). 2. Border step up: `border-bone/10 → border-gold/30 (or /35 /40)`. 3. Background step up: `bg-graphite/40 → bg-graphite/60`. 4. Amber radial glow bottom-right: `radial-gradient(circle, rgba(212,164,55,0.28), transparent 70%)` blurred. 5. Gold underline sweep (cards): `absolute bottom-0 left-0 h-px w-0 ... group-hover:w-full`. 6. Optional: top hairline gold gradient fade-in. 7. Icon disc gets `rotate-[8deg]` + amber halo shadow. Apply via `group` + `group-hover:` Tailwind variants never per-element JS listeners. --- ## Container widths ```tsx // components/ui/Container.tsx three variants size="default" → max-w-7xl size="wide" → max-w-[1480px] size="narrow" → max-w-3xl ``` All variants: `mx-auto px-6 md:px-10`. --- ## When in doubt - More dark, less gold. - More space, less content. - Less motion, more restraint. - Real photography > illustration. - Editorial typography > corporate sans.