design system token v0.2

This commit is contained in:
Juan
2026-04-13 15:46:38 -05:00
parent c3215945f2
commit c9209a6271
30 changed files with 1190 additions and 159 deletions

View File

@@ -9,22 +9,60 @@
This skill gives you full context to generate pixel-perfect, on-brand UI using the Greyhaven Design System. Every component lives in `components/ui/`. Use semantic tokens, never raw colors. Follow the patterns exactly.
---
## 1. Design Philosophy
## Design Philosophy
- **Minimal and restrained**: Off-white + off-black + warm greys. One single accent color (orange). No gradients, no decorative color, no multiple accent hues.
- **Typography-driven**: Source Serif 4/Pro (serif) for headings and body content. Aspekta/Inter (sans) for UI labels, buttons, navigation, and form elements.
- **Typography-driven**: Source Serif 4/Pro (serif) for headings and body content. Aspekta (sans, self-hosted) for UI labels, buttons, navigation, and form elements.
- **Calm, professional aesthetic**: Tight border-radii, subtle shadows, generous whitespace.
- **Accessibility-first**: Built on Radix UI primitives for keyboard navigation, focus management, screen reader support. Visible focus rings, disabled states, ARIA attributes.
- **Dark mode native**: Thoughtful dark theme using inverted warm greys. Orange accent persists across both modes. Toggled via `.dark` class.
- **Framework-agnostic**: Pure React + Radix + Tailwind. No Next.js, no framework-specific imports.
---
## Font Setup
This design system uses two typefaces:
| Role | Font | Usage |
|------|------|-------|
| **Sans (UI)** | Aspekta (self-hosted) | Buttons, nav, labels, forms, metadata |
| **Serif (Content)** | Source Serif 4/Pro | Headings, body text, reading content |
### Aspekta (required)
Aspekta font files live in `public/fonts/`. Add `@font-face` declarations to your global CSS:
```css
/* Minimum set (covers font-weight 400-700) */
@font-face { font-family: 'Aspekta'; font-weight: 400; font-display: swap; src: url('/fonts/Aspekta-400.woff2') format('woff2'); }
@font-face { font-family: 'Aspekta'; font-weight: 500; font-display: swap; src: url('/fonts/Aspekta-500.woff2') format('woff2'); }
@font-face { font-family: 'Aspekta'; font-weight: 600; font-display: swap; src: url('/fonts/Aspekta-600.woff2') format('woff2'); }
@font-face { font-family: 'Aspekta'; font-weight: 700; font-display: swap; src: url('/fonts/Aspekta-700.woff2') format('woff2'); }
/* Or import all weights: */
@import url('/fonts/font-face.css');
```
### Font stack CSS variables
```css
--font-sans: 'Aspekta', ui-sans-serif, system-ui, sans-serif;
--font-serif: 'Source Serif 4', 'Source Serif Pro', Georgia, serif;
```
### Tailwind usage
- `font-sans` — Aspekta (UI elements)
- `font-serif` — Source Serif (content)
Install fonts via: `./skill/install.sh /path/to/your/project`
---
## 2. Token Quick Reference
## Token Quick Reference
Source of truth: `tokens/*.json` (W3C DTCG format).
@@ -112,7 +150,7 @@ Source of truth: `tokens/*.json` (W3C DTCG format).
| Token | Value | Description |
|-------|-------|-------------|
| `typography.fontFamily.sans` | `["Aspekta","Inter","ui-sans-serif","system-ui","sans-serif"]` | UI labels, buttons, nav, forms — Aspekta primary, Inter fallback |
| `typography.fontFamily.sans` | `["Aspekta","ui-sans-serif","system-ui","sans-serif"]` | UI labels, buttons, nav, forms — Aspekta self-hosted |
| `typography.fontFamily.serif` | `["Source Serif 4","Source Serif Pro","Georgia","serif"]` | Headings, body content, reading — Source Serif primary |
| `typography.fontFamily.mono` | `["ui-monospace","SFMono-Regular","Menlo","Monaco","Consolas","monospace"]` | Code blocks and monospaced content |
| `typography.fontSize.xs` | `0.75rem` | 12px — metadata, fine print |
@@ -192,10 +230,9 @@ Source of truth: `tokens/*.json` (W3C DTCG format).
| `motion.easing.in` | `[0.4,0,1,1]` | Ease-in for exits |
| `motion.easing.out` | `[0,0,0.2,1]` | Ease-out for entrances |
---
## 3. Component Catalog (37 components)
## Component Catalog (37 components)
All components live in `components/ui/`. Import with `@/components/ui/<name>`.
@@ -585,10 +622,9 @@ All components live in `components/ui/`. Import with `@/components/ui/<name>`.
<PageLayout navbar={<Navbar />} footer={<Footer />}>Main content</PageLayout>
```
---
## 4. Composition Rules
## Composition Rules
- **Card spacing**: `gap-6` between cards, `p-6` internal padding
- **Section rhythm**: `py-16` between major page sections
@@ -603,10 +639,9 @@ All components live in `components/ui/`. Import with `@/components/ui/<name>`.
- **Slot naming**: All components use `data-slot="component-name"`
- **Icon sizing**: `[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0`
---
## 5. Extension Protocol
## Extension Protocol
When adding new components to the system:
@@ -630,24 +665,14 @@ import { cn } from '@/lib/utils'
const myComponentVariants = cva('base-classes', {
variants: {
variant: {
default: 'default-classes',
},
size: {
default: 'size-classes',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
variant: { default: 'default-classes' },
size: { default: 'size-classes' },
},
defaultVariants: { variant: 'default', size: 'default' },
})
function MyComponent({
className,
variant,
size,
...props
className, variant, size, ...props
}: React.ComponentProps<'div'> & VariantProps<typeof myComponentVariants>) {
return (
<div