mirror of
https://github.com/0xWheatyz/handler.git
synced 2026-09-05 19:46:24 +00:00
Claude Design handoff: Handler mobile app design
This commit is contained in:
+432
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
+2759
File diff suppressed because it is too large
Load Diff
+1
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 (6–10px). 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`, 20–24px padding. Sunken variant swaps white for `--ink-1` and drops the shadow.
|
||||
- **Animation:** brief and functional — 120–180ms, `--ease-out`, opacity + small translate (4–8px). 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 16–20px 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";
|
||||
+74
@@ -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;
|
||||
}
|
||||
+19
@@ -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);
|
||||
}
|
||||
+2
@@ -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");
|
||||
+11
@@ -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;
|
||||
}
|
||||
+27
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user