Claude Design handoff: Handler mobile app design

This commit is contained in:
Claude Design
2026-07-20 18:34:27 +00:00
commit f143959b52
15 changed files with 5905 additions and 0 deletions
@@ -0,0 +1,432 @@
{
"plugins": [
"react",
"import"
],
"rules": {
"react/forbid-elements": [
"warn",
{
"forbid": []
}
],
"no-restricted-imports": [
"warn",
{
"patterns": [
{
"group": [
"components/display/**",
"components/feedback/**",
"components/forms/**",
"components/navigation/**",
"ui_kits/dashboard/**",
"ui_kits/mobile/**",
"ui_kits/website/**"
],
"message": "Import design-system components from 'index.js', not component internals."
}
]
}
],
"no-restricted-syntax": [
"warn",
{
"selector": "Literal[value=/#[0-9a-fA-F]{3,8}\\b/]",
"message": "Raw hex color — use a design-system color token via var()."
},
{
"selector": "Literal[value=/\\b\\d+px\\b/]",
"message": "Raw px value — use a design-system spacing token via var()."
},
{
"selector": "Literal[value=/font-family\\s*:\\s*(?!['\\\"]?(?:Outfit|Figtree|Spline Sans Mono))/i]",
"message": "Font not provided by the design system. Available: Outfit, Figtree, Spline Sans Mono."
},
{
"selector": "JSXOpeningElement[name.name='Badge'] > JSXAttribute > JSXIdentifier[name!=/^(?:tone|children|style|key|ref|className|style|children)$/]",
"message": "<Badge> doesn't accept that prop. Declared props: tone, children, style."
},
{
"selector": "JSXOpeningElement[name.name='Badge'] > JSXAttribute[name.name='tone'] > Literal[value!=/^(?:neutral|positive|warning|danger|inverse)$/]",
"message": "<Badge> tone must be one of 'neutral' | 'positive' | 'warning' | 'danger' | 'inverse'."
},
{
"selector": "JSXOpeningElement[name.name='Button'] > JSXAttribute > JSXIdentifier[name!=/^(?:variant|size|disabled|icon|children|onClick|style|key|ref|className|style|children)$/]",
"message": "<Button> doesn't accept that prop. Declared props: variant, size, disabled, icon, children, onClick, style."
},
{
"selector": "JSXOpeningElement[name.name='Button'] > JSXAttribute[name.name='variant'] > Literal[value!=/^(?:primary|secondary|ghost|danger)$/]",
"message": "<Button> variant must be one of 'primary' | 'secondary' | 'ghost' | 'danger'."
},
{
"selector": "JSXOpeningElement[name.name='Button'] > JSXAttribute[name.name='size'] > Literal[value!=/^(?:sm|md|lg)$/]",
"message": "<Button> size must be one of 'sm' | 'md' | 'lg'."
},
{
"selector": "JSXOpeningElement[name.name='Card'] > JSXAttribute > JSXIdentifier[name!=/^(?:title|subtitle|actions|footer|variant|padding|children|style|key|ref|className|style|children)$/]",
"message": "<Card> doesn't accept that prop. Declared props: title, subtitle, actions, footer, variant, padding, children, style."
},
{
"selector": "JSXOpeningElement[name.name='Card'] > JSXAttribute[name.name='variant'] > Literal[value!=/^(?:default|sunken|outline)$/]",
"message": "<Card> variant must be one of 'default' | 'sunken' | 'outline'."
},
{
"selector": "JSXOpeningElement[name.name='Checkbox'] > JSXAttribute > JSXIdentifier[name!=/^(?:label|checked|defaultChecked|onChange|disabled|style|key|ref|className|style|children)$/]",
"message": "<Checkbox> doesn't accept that prop. Declared props: label, checked, defaultChecked, onChange, disabled, style."
},
{
"selector": "JSXOpeningElement[name.name='Dialog'] > JSXAttribute > JSXIdentifier[name!=/^(?:open|title|description|actions|onClose|width|children|style|key|ref|className|style|children)$/]",
"message": "<Dialog> doesn't accept that prop. Declared props: open, title, description, actions, onClose, width, children, style."
},
{
"selector": "JSXOpeningElement[name.name='Icon'] > JSXAttribute > JSXIdentifier[name!=/^(?:name|size|strokeWidth|style|key|ref|className|style|children)$/]",
"message": "<Icon> doesn't accept that prop. Declared props: name, size, strokeWidth, style."
},
{
"selector": "JSXOpeningElement[name.name='Icon'] > JSXAttribute[name.name='name'] > Literal[value!=/^(?:dashboard|file|users|settings|search|bell|plus|check|chart|folder|chevronDown|chevronRight|more|arrowUpRight|x|menu|home|inbox|user)$/]",
"message": "<Icon> name must be one of 'dashboard' | 'file' | 'users' | 'settings' | 'search' | 'bell' | 'plus' | 'check' | 'chart' | 'folder' | 'chevronDown' | 'chevronRight' | 'more' | 'arrowUpRight' | 'x' | 'menu' | 'home' | 'inbox' | 'user'."
},
{
"selector": "JSXOpeningElement[name.name='IconButton'] > JSXAttribute > JSXIdentifier[name!=/^(?:size|variant|disabled|label|children|onClick|style|key|ref|className|style|children)$/]",
"message": "<IconButton> doesn't accept that prop. Declared props: size, variant, disabled, label, children, onClick, style."
},
{
"selector": "JSXOpeningElement[name.name='IconButton'] > JSXAttribute[name.name='size'] > Literal[value!=/^(?:sm|md|lg)$/]",
"message": "<IconButton> size must be one of 'sm' | 'md' | 'lg'."
},
{
"selector": "JSXOpeningElement[name.name='IconButton'] > JSXAttribute[name.name='variant'] > Literal[value!=/^(?:ghost|outline|primary)$/]",
"message": "<IconButton> variant must be one of 'ghost' | 'outline' | 'primary'."
},
{
"selector": "JSXOpeningElement[name.name='Input'] > JSXAttribute > JSXIdentifier[name!=/^(?:label|hint|error|size|prefix|suffix|placeholder|value|onChange|type|style|key|ref|className|style|children)$/]",
"message": "<Input> doesn't accept that prop. Declared props: label, hint, error, size, prefix, suffix, placeholder, value, onChange, type, style."
},
{
"selector": "JSXOpeningElement[name.name='Input'] > JSXAttribute[name.name='size'] > Literal[value!=/^(?:sm|md|lg)$/]",
"message": "<Input> size must be one of 'sm' | 'md' | 'lg'."
},
{
"selector": "JSXOpeningElement[name.name='Radio'] > JSXAttribute > JSXIdentifier[name!=/^(?:options|value|defaultValue|onChange|name|disabled|style|key|ref|className|style|children)$/]",
"message": "<Radio> doesn't accept that prop. Declared props: options, value, defaultValue, onChange, name, disabled, style."
},
{
"selector": "JSXOpeningElement[name.name='Select'] > JSXAttribute > JSXIdentifier[name!=/^(?:label|hint|options|size|value|onChange|style|key|ref|className|style|children)$/]",
"message": "<Select> doesn't accept that prop. Declared props: label, hint, options, size, value, onChange, style."
},
{
"selector": "JSXOpeningElement[name.name='Select'] > JSXAttribute[name.name='size'] > Literal[value!=/^(?:sm|md|lg)$/]",
"message": "<Select> size must be one of 'sm' | 'md' | 'lg'."
},
{
"selector": "JSXOpeningElement[name.name='Switch'] > JSXAttribute > JSXIdentifier[name!=/^(?:label|checked|defaultChecked|onChange|disabled|style|key|ref|className|style|children)$/]",
"message": "<Switch> doesn't accept that prop. Declared props: label, checked, defaultChecked, onChange, disabled, style."
},
{
"selector": "JSXOpeningElement[name.name='Tabs'] > JSXAttribute > JSXIdentifier[name!=/^(?:tabs|value|defaultValue|onChange|variant|style|key|ref|className|style|children)$/]",
"message": "<Tabs> doesn't accept that prop. Declared props: tabs, value, defaultValue, onChange, variant, style."
},
{
"selector": "JSXOpeningElement[name.name='Tabs'] > JSXAttribute[name.name='variant'] > Literal[value!=/^(?:underline|segmented)$/]",
"message": "<Tabs> variant must be one of 'underline' | 'segmented'."
},
{
"selector": "JSXOpeningElement[name.name='Tag'] > JSXAttribute > JSXIdentifier[name!=/^(?:onRemove|children|style|key|ref|className|style|children)$/]",
"message": "<Tag> doesn't accept that prop. Declared props: onRemove, children, style."
},
{
"selector": "JSXOpeningElement[name.name='Toast'] > JSXAttribute > JSXIdentifier[name!=/^(?:tone|title|description|onDismiss|style|key|ref|className|style|children)$/]",
"message": "<Toast> doesn't accept that prop. Declared props: tone, title, description, onDismiss, style."
},
{
"selector": "JSXOpeningElement[name.name='Toast'] > JSXAttribute[name.name='tone'] > Literal[value!=/^(?:neutral|positive|danger)$/]",
"message": "<Toast> tone must be one of 'neutral' | 'positive' | 'danger'."
},
{
"selector": "JSXOpeningElement[name.name='Tooltip'] > JSXAttribute > JSXIdentifier[name!=/^(?:label|side|children|style|key|ref|className|style|children)$/]",
"message": "<Tooltip> doesn't accept that prop. Declared props: label, side, children, style."
},
{
"selector": "JSXOpeningElement[name.name='Tooltip'] > JSXAttribute[name.name='side'] > Literal[value!=/^(?:top|bottom|left|right)$/]",
"message": "<Tooltip> side must be one of 'top' | 'bottom' | 'left' | 'right'."
}
]
},
"overrides": [
{
"files": [
"**/index.js"
],
"rules": {
"no-restricted-imports": "off"
}
}
],
"x-omelette": {
"components": {
"Badge": {
"replaces": []
},
"Button": {
"replaces": []
},
"Card": {
"replaces": []
},
"Checkbox": {
"replaces": []
},
"Dialog": {
"replaces": []
},
"Icon": {
"replaces": []
},
"IconButton": {
"replaces": []
},
"Input": {
"replaces": []
},
"Radio": {
"replaces": []
},
"Select": {
"replaces": []
},
"Switch": {
"replaces": []
},
"Tabs": {
"replaces": []
},
"Tag": {
"replaces": []
},
"Toast": {
"replaces": []
},
"Tooltip": {
"replaces": []
}
},
"tokens": [
"--border-default",
"--border-strong",
"--border-subtle",
"--content-max",
"--control-h-lg",
"--control-h-md",
"--control-h-sm",
"--control-pad-x-lg",
"--control-pad-x-md",
"--control-pad-x-sm",
"--danger",
"--danger-tint",
"--dur-base",
"--dur-fast",
"--dur-slow",
"--ease-out",
"--focus-ring",
"--font-body",
"--font-display",
"--font-mono",
"--gutter",
"--ink-0",
"--ink-1",
"--ink-2",
"--ink-3",
"--ink-4",
"--ink-5",
"--ink-6",
"--ink-7",
"--ink-8",
"--ink-9",
"--interactive",
"--interactive-hover",
"--interactive-press",
"--leading-normal",
"--leading-snug",
"--leading-tight",
"--page-max",
"--positive",
"--positive-tint",
"--radius-lg",
"--radius-md",
"--radius-pill",
"--radius-sm",
"--radius-xl",
"--shadow-card",
"--shadow-focus",
"--shadow-overlay",
"--shadow-raised",
"--signal",
"--signal-strong",
"--signal-tint",
"--space-1",
"--space-10",
"--space-11",
"--space-2",
"--space-3",
"--space-4",
"--space-5",
"--space-6",
"--space-7",
"--space-8",
"--space-9",
"--surface-card",
"--surface-inverse",
"--surface-page",
"--surface-raised",
"--surface-sunken",
"--text-2xl",
"--text-3xl",
"--text-4xl",
"--text-base",
"--text-body",
"--text-heading",
"--text-inverse",
"--text-lg",
"--text-link",
"--text-md",
"--text-muted",
"--text-sm",
"--text-xl",
"--text-xs",
"--tracking-normal",
"--tracking-tight",
"--tracking-wide",
"--type-body",
"--type-body-sm",
"--type-caption",
"--type-display",
"--type-h1",
"--type-h2",
"--type-h3",
"--type-h4",
"--type-label",
"--type-mono",
"--type-overline",
"--warning",
"--warning-tint",
"--weight-bold",
"--weight-heavy",
"--weight-medium",
"--weight-regular",
"--weight-semibold",
"--white"
],
"tokenKinds": {
"--white": "color",
"--ink-0": "color",
"--ink-1": "color",
"--ink-2": "color",
"--ink-3": "color",
"--ink-4": "color",
"--ink-5": "color",
"--ink-6": "color",
"--ink-7": "color",
"--ink-8": "color",
"--ink-9": "color",
"--signal": "color",
"--signal-strong": "color",
"--signal-tint": "color",
"--positive": "color",
"--positive-tint": "color",
"--warning": "color",
"--warning-tint": "color",
"--danger": "color",
"--danger-tint": "color",
"--surface-page": "color",
"--surface-raised": "color",
"--surface-sunken": "color",
"--surface-card": "color",
"--surface-inverse": "color",
"--text-heading": "font",
"--text-body": "font",
"--text-muted": "font",
"--text-inverse": "font",
"--text-link": "font",
"--border-subtle": "color",
"--border-default": "color",
"--border-strong": "color",
"--interactive": "color",
"--interactive-hover": "color",
"--interactive-press": "color",
"--focus-ring": "color",
"--font-display": "font",
"--font-body": "font",
"--font-mono": "font",
"--text-xs": "font",
"--text-sm": "font",
"--text-base": "font",
"--text-md": "font",
"--text-lg": "font",
"--text-xl": "font",
"--text-2xl": "font",
"--text-3xl": "font",
"--text-4xl": "font",
"--leading-tight": "font",
"--leading-snug": "font",
"--leading-normal": "font",
"--tracking-tight": "font",
"--tracking-normal": "font",
"--tracking-wide": "font",
"--weight-regular": "font",
"--weight-medium": "font",
"--weight-semibold": "font",
"--weight-bold": "font",
"--weight-heavy": "font",
"--type-display": "font",
"--type-h1": "font",
"--type-h2": "font",
"--type-h3": "font",
"--type-h4": "font",
"--type-body": "font",
"--type-body-sm": "font",
"--type-label": "font",
"--type-caption": "font",
"--type-overline": "font",
"--type-mono": "font",
"--space-1": "spacing",
"--space-2": "spacing",
"--space-3": "spacing",
"--space-4": "spacing",
"--space-5": "spacing",
"--space-6": "spacing",
"--space-7": "spacing",
"--space-8": "spacing",
"--space-9": "spacing",
"--space-10": "spacing",
"--space-11": "spacing",
"--control-h-sm": "spacing",
"--control-h-md": "spacing",
"--control-h-lg": "spacing",
"--control-pad-x-sm": "spacing",
"--control-pad-x-md": "spacing",
"--control-pad-x-lg": "spacing",
"--page-max": "spacing",
"--content-max": "spacing",
"--gutter": "spacing",
"--radius-sm": "radius",
"--radius-md": "radius",
"--radius-lg": "radius",
"--radius-xl": "radius",
"--radius-pill": "radius",
"--shadow-card": "shadow",
"--shadow-raised": "shadow",
"--shadow-overlay": "shadow",
"--shadow-focus": "shadow",
"--ease-out": "other",
"--dur-fast": "other",
"--dur-base": "other",
"--dur-slow": "other"
},
"fontFamilies": [
"Figtree",
"Outfit",
"Spline Sans Mono"
]
}
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,57 @@
# Leeworks Design System
**Leeworks Systems** — a from-scratch design system built July 2026. No existing codebase, Figma, or brand assets were provided; everything here was authored fresh from the brief: *modern, bright whites, near-monochrome, explicitly avoiding common "AI" color themes (no bluish-purple gradients, no glassmorphism)*.
Personality: **crisp & professional**, with a **geometric & friendly** typographic voice. Softly rounded shapes (610px). Light-first with full dark tokens. Comfortable density.
## Sources
None provided. Fonts are Google Fonts substitutes (see Caveats). No logo was provided — the brand renders as plain type ("Leeworks") wherever a mark would go.
## CONTENT FUNDAMENTALS
- **Voice:** plain, confident, specific. Short sentences. Verbs first in CTAs ("Create a workspace", "Invite your team", "View report").
- **Casing:** Sentence case everywhere — headings, buttons, labels, nav. Never Title Case, never ALL CAPS except tiny overline labels (letter-spaced, e.g. "OVERVIEW").
- **Person:** "you/your" to the user; "we" only when Leeworks itself acts ("We'll email you a receipt"). Never "I".
- **Numbers & data:** tabular figures in mono (`--font-mono`) for metrics, IDs, timestamps. Units spelled tight: "12.4k", "3m ago".
- **Emoji:** never in product UI. None.
- **Microcopy vibe:** helpful, unhurried, no exclamation marks. Empty states state the fact + one action: "No reports yet. Create your first report."
- Examples: "Everything your team ships, in one place." / "Save changes" / "This can't be undone." / "Last synced 2m ago".
## VISUAL FOUNDATIONS
- **Color:** bright white pages (`--surface-page: #fff`), a warm-neutral ink ramp (`--ink-0…9`) doing nearly all the work. Primary interactive color is **ink black** (`--interactive: #141413`) — buttons, links, focus. One restrained non-neutral: a deep leaf green `--signal` (#1d6b45), used sparingly for positive signals and small moments of life. Status colors are muted (green/ochre/brick), always paired with a tint surface. No gradients anywhere.
- **Type:** Outfit for display/headings (geometric, friendly terminals), Figtree for body/UI, Spline Sans Mono for data & code. Headings tight (-0.02em, 1.1 leading); body 15px/1.55. Overlines are 12px Outfit semibold, +0.08em, uppercase.
- **Spacing:** 4px base scale (`--space-1…11`), comfortable density (~4/10). Controls: 32/40/48px heights. Page max 1200px, prose max 720px, 24px gutters.
- **Backgrounds:** flat bright white; sections separated by hairline borders (`--border-subtle`) or a faint sunken panel (`--surface-sunken: #f6f6f4`) — never gradients, textures, or imagery washes. Marketing hero = white with big ink type.
- **Borders:** 1px hairlines, `--border-subtle` (#ececea) inside cards/dividers, `--border-default` (#dcdcd8) on inputs. `--border-strong` (ink) marks selected states.
- **Shadows:** whisper-quiet. Cards: `--shadow-card` (1px). Popovers/menus: `--shadow-raised`. Dialogs: `--shadow-overlay`. No inner shadows, no colored shadows.
- **Radii:** 6px small (badges, inputs' inner bits), 8px controls, 10px cards, 16px dialogs/large panels, pill for tags & switches. Never fully-square, never blob-round on containers.
- **Cards:** white, 1px `--border-subtle`, 10px radius, `--shadow-card`, 2024px padding. Sunken variant swaps white for `--ink-1` and drops the shadow.
- **Animation:** brief and functional — 120180ms, `--ease-out`, opacity + small translate (48px). No bounces, no spring theatrics. Dialogs fade+scale from .98.
- **Hover:** ink surfaces lighten (`--interactive-hover`), quiet elements gain `--ink-1` wash. **Press:** darken to true black + no shrink transforms. **Focus:** 3px soft ring (`--shadow-focus`), visible on keyboard focus.
- **Transparency/blur:** essentially none. Scrims are flat `rgba(20,20,19,.4)`. No backdrop blur.
- **Imagery:** when photography appears (marketing), it is neutral-warm, high-key, plenty of white in frame; slight desaturation OK; no duotones or heavy grade.
- **Dark mode:** `[data-theme="dark"]` scope; warm near-black (#161615) pages, same geometry, inverted ink roles.
## ICONOGRAPHY
- No proprietary icon set was provided. The system uses **Lucide** (CDN, `lucide.dev`) as its icon language — 1.75px stroke weight at 1620px sizes matches the geometric-friendly type. Load via `https://unpkg.com/lucide@latest` and `lucide.createIcons()`, or inline copied SVGs.
- Icons are always monochrome `currentColor`, never multicolor, never filled variants.
- No emoji, no unicode-glyph icons. **Substitution flag:** if Leeworks adopts a bespoke icon set later, drop the SVGs into `assets/icons/` and update this section.
## Index
- `styles.css` — global entry (imports everything under `tokens/`).
- `tokens/``fonts.css`, `colors.css`, `typography.css`, `spacing.css`, `effects.css`.
- `guidelines/` — foundation specimen cards (Design System tab).
- `components/forms/` — Button, IconButton, Input, Select, Checkbox, Radio, Switch.
- `components/display/` — Card, Badge, Tag, Icon.
- `components/navigation/` — Tabs.
- `components/feedback/` — Dialog, Toast, Tooltip.
- `ui_kits/dashboard/` — Leeworks web app (analytics/workspace dashboard).
- `ui_kits/website/` — marketing site (homepage).
- `ui_kits/mobile/` — mobile app screens.
- `SKILL.md` — agent-skill entry point.
## Intentional additions
Standard component set authored from scratch (no source inventory existed). Lucide adopted as icon set (flagged above). `Icon` component wraps an inlined Lucide subset so kits never hand-roll SVG.
## Caveats
- **Fonts are CDN-served Google Fonts** (Outfit, Figtree, Spline Sans Mono) — no binaries in-project. Provide licensed font files to ship offline.
- No logo exists; wordmark is plain Outfit type. Provide a real mark if one exists.
@@ -0,0 +1,5 @@
@import "tokens/fonts.css";
@import "tokens/colors.css";
@import "tokens/typography.css";
@import "tokens/spacing.css";
@import "tokens/effects.css";
@@ -0,0 +1,74 @@
/* Leeworks color system — bright white base, ink-led near-monochrome. */
:root{
/* Base scale (warm-neutral ink ramp) */
--white:#ffffff;
--ink-0:#fcfcfb;
--ink-1:#f6f6f4;
--ink-2:#ececea;
--ink-3:#dcdcd8;
--ink-4:#b8b8b3;
--ink-5:#8b8b86;
--ink-6:#62625e;
--ink-7:#3d3d3a;
--ink-8:#232321;
--ink-9:#141413;
/* Signal (the one non-neutral: a deep leaf green used sparingly) */
--signal:#1d6b45;
--signal-strong:#155236;
--signal-tint:#e8f2ec;
/* Semantic status (muted, low-saturation) */
--positive:#1d6b45;
--positive-tint:#e8f2ec;
--warning:#8a6116;
--warning-tint:#f7efdd;
--danger:#a33a2f;
--danger-tint:#f9e9e6;
/* Semantic aliases — light */
--surface-page:var(--white);
--surface-raised:var(--white);
--surface-sunken:var(--ink-1);
--surface-card:var(--white);
--surface-inverse:var(--ink-9);
--text-heading:var(--ink-9);
--text-body:var(--ink-7);
--text-muted:var(--ink-5);
--text-inverse:var(--ink-0);
--text-link:var(--ink-9);
--border-subtle:var(--ink-2);
--border-default:var(--ink-3);
--border-strong:var(--ink-9);
--interactive:var(--ink-9);
--interactive-hover:var(--ink-7);
--interactive-press:#000000;
--focus-ring:rgba(20,20,19,.28);
}
[data-theme="dark"]{
--surface-page:#161615;
--surface-raised:#1e1e1d;
--surface-sunken:#111110;
--surface-card:#1e1e1d;
--surface-inverse:var(--ink-0);
--text-heading:#f4f4f2;
--text-body:#c9c9c4;
--text-muted:#8b8b86;
--text-inverse:var(--ink-9);
--text-link:#f4f4f2;
--border-subtle:#2b2b29;
--border-default:#3a3a37;
--border-strong:#f4f4f2;
--interactive:#f4f4f2;
--interactive-hover:#d8d8d4;
--interactive-press:#ffffff;
--focus-ring:rgba(244,244,242,.35);
--signal:#4fa377;
--signal-tint:#1d2f26;
--positive:#4fa377;
--positive-tint:#1d2f26;
--warning:#c99a3f;
--warning-tint:#2f2818;
--danger:#d0685c;
--danger-tint:#331e1b;
}
@@ -0,0 +1,19 @@
/* Leeworks effects — soft rounding, hairline borders, restrained shadows. */
:root{
--radius-sm:6px; --radius-md:8px; --radius-lg:10px; --radius-xl:16px; --radius-pill:999px;
--shadow-card:0 1px 2px rgba(20,20,19,.05);
--shadow-raised:0 2px 8px rgba(20,20,19,.07),0 1px 2px rgba(20,20,19,.05);
--shadow-overlay:0 12px 40px rgba(20,20,19,.14),0 2px 8px rgba(20,20,19,.06);
--shadow-focus:0 0 0 3px var(--focus-ring);
--ease-out:cubic-bezier(.2,.7,.3,1); /* @kind other */
--dur-fast:120ms; /* @kind other */
--dur-base:180ms; /* @kind other */
--dur-slow:280ms; /* @kind other */
}
[data-theme="dark"]{
--shadow-card:0 1px 2px rgba(0,0,0,.4);
--shadow-raised:0 2px 8px rgba(0,0,0,.5);
--shadow-overlay:0 12px 40px rgba(0,0,0,.6);
}
@@ -0,0 +1,2 @@
/* Leeworks webfonts — served from Google Fonts (no local binaries provided). */
@import url("https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800&family=Figtree:ital,wght@0,400;0,500;0,600;0,700;1,400&family=Spline+Sans+Mono:wght@400;500;600&display=swap");
@@ -0,0 +1,11 @@
/* Leeworks spacing — 4px base, comfortable (not dense) rhythm. */
:root{
--space-1:4px; --space-2:8px; --space-3:12px; --space-4:16px;
--space-5:20px; --space-6:24px; --space-7:32px; --space-8:40px;
--space-9:56px; --space-10:80px; --space-11:120px;
--control-h-sm:32px; --control-h-md:40px; --control-h-lg:48px;
--control-pad-x-sm:12px; --control-pad-x-md:16px; --control-pad-x-lg:20px;
--page-max:1200px; --content-max:720px; --gutter:24px;
}
@@ -0,0 +1,27 @@
/* Leeworks typography — Outfit (display), Figtree (body), Spline Sans Mono (data/code). */
:root{
--font-display:"Outfit",system-ui,sans-serif;
--font-body:"Figtree",system-ui,sans-serif;
--font-mono:"Spline Sans Mono",ui-monospace,monospace;
--text-xs:12px; --text-sm:13px; --text-base:15px; --text-md:17px;
--text-lg:20px; --text-xl:24px; --text-2xl:32px; --text-3xl:44px; --text-4xl:60px;
--leading-tight:1.1; --leading-snug:1.3; --leading-normal:1.55;
--tracking-tight:-0.02em; --tracking-normal:0; --tracking-wide:0.08em;
--weight-regular:400; --weight-medium:500; --weight-semibold:600; --weight-bold:700; --weight-heavy:800;
/* Composite styles */
--type-display:var(--weight-bold) var(--text-4xl)/var(--leading-tight) var(--font-display);
--type-h1:var(--weight-bold) var(--text-3xl)/var(--leading-tight) var(--font-display);
--type-h2:var(--weight-semibold) var(--text-2xl)/1.2 var(--font-display);
--type-h3:var(--weight-semibold) var(--text-xl)/var(--leading-snug) var(--font-display);
--type-h4:var(--weight-semibold) var(--text-md)/var(--leading-snug) var(--font-display);
--type-body:var(--weight-regular) var(--text-base)/var(--leading-normal) var(--font-body);
--type-body-sm:var(--weight-regular) var(--text-sm)/var(--leading-normal) var(--font-body);
--type-label:var(--weight-semibold) var(--text-sm)/1.2 var(--font-body);
--type-caption:var(--weight-medium) var(--text-xs)/1.4 var(--font-body);
--type-overline:var(--weight-semibold) var(--text-xs)/1.2 var(--font-display);
--type-mono:var(--weight-regular) var(--text-sm)/1.5 var(--font-mono);
}