@gradeui/ui 4.0.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,52 @@
1
+ ---
2
+ title: Grade Design System — read this first
3
+ audience: any AI agent or developer consuming @gradeui/ui
4
+ ---
5
+
6
+ # Grade Design System (@gradeui/ui)
7
+
8
+ This package is **self-describing**. Everything an agent needs to generate correct
9
+ Grade UI ships inside the installed npm package — you do not need the source repo,
10
+ the website, or any external docs. Treat this document as the single comprehensive
11
+ design spec: read it before generating markup.
12
+
13
+ ## The non-negotiable page scaffold
14
+
15
+ **A page is an ordered stack of `Section` bands, and every `Section` wraps a `Container`.**
16
+
17
+ ```jsx
18
+ <Section scope="inverse" pad="xl">
19
+ <Container maxW="lg">
20
+ {/* content */}
21
+ </Container>
22
+ </Section>
23
+ ```
24
+
25
+ - `Section` = the full-width themeable band (colour `scope` + vertical `pad`). It is
26
+ ALWAYS full width and never sets a max width.
27
+ - `Container` = the measure (centred `maxW` + gutters). For an edge-to-edge band use
28
+ `<Container maxW="full">` — that is how you go full-bleed. **You never omit the Container.**
29
+ - This holds for app content regions too, not just marketing pages. `AppShell` is only
30
+ the outer chrome; the regions inside it are still `Section` → `Container`.
31
+
32
+ **Never hand-roll** `<section className="py-20">` or `<div className="max-w-7xl mx-auto px-6">`.
33
+ That raw markup is exactly what `Section` + `Container` replace. Reaching for div-soup
34
+ instead of the primitives is the single most common mistake — don't make it.
35
+
36
+ ## How the system is organised
37
+
38
+ 1. **Foundations** (this folder / the sections below) — the *rules* that aren't components:
39
+ themes, colour scopes, expressive accents, typography, spacing & layout.
40
+ 2. **Components** — every component ships a sidecar (`when_to_use`, `composes_with`,
41
+ `props`, worked examples + anti-patterns). The component reference follows the foundations.
42
+ 3. **Machine-readable contracts** — `import { COMPONENT_CONTRACTS } from "@gradeui/ui/contracts"`
43
+ gives programmatic prop schemas (zod), descriptions, aliases, and composition data.
44
+ 4. **Theme engine** — `import { generateTheme, GradeThemeProvider } from "@gradeui/ui"`.
45
+ 5. **Styles** — `import "@gradeui/ui/styles.css"` (precompiled) or wire the source
46
+ `@gradeui/ui/styles/globals.css` into your own Tailwind v4 build.
47
+
48
+ ## Token namespace
49
+
50
+ Runtime tokens live under `--gds-*` (CSS custom properties), `gds-*` (class prefix),
51
+ `--ramp-*` (per-step OKLCH colour ramps), and the active theme is set via the
52
+ `data-grade-theme` attribute on `<html>`.
@@ -0,0 +1,52 @@
1
+ ---
2
+ foundation: color-scopes
3
+ classes: [scope-default, scope-inverse, scope-brand, scope-accent, scope-muted, scope-card]
4
+ applies_via: "Section scope=... | className=\"scope-*\""
5
+ ---
6
+
7
+ # Colour scopes
8
+
9
+ A **scope** is a Figma-style *variable mode* scoped to a subtree. It is the primary
10
+ way a band changes colour. Putting a `scope-*` class on an element (or `scope` on a
11
+ `Section`) re-points the **surface family** — `--background`, `--foreground`, `--card`,
12
+ `--popover`, `--muted`, `--muted-foreground`, `--border` — for everything inside it,
13
+ while leaving the **action colours** (`--primary` / `--accent` / `--secondary` /
14
+ `--destructive`) vivid so a CTA still pops.
15
+
16
+ Descendants keep using the ordinary tokens (`bg-background`, `text-foreground`,
17
+ `bg-card`); only what those tokens *resolve to* changes. This is why you re-tone a
18
+ whole band by setting one scope, never by hand-colouring children.
19
+
20
+ ## The scopes
21
+
22
+ | scope | what it is |
23
+ |-----------|------------------------------------------------------------------|
24
+ | `default` | the page surface (omit `scope` to get this) |
25
+ | `inverse` | dark band / light text — the marketing flip |
26
+ | `brand` | brand-toned surface (remaps from the theme's existing tokens) |
27
+ | `accent` | accent-toned surface |
28
+ | `muted` | a quiet, low-contrast band |
29
+ | `card` | a raised card-toned band with a hairline top/bottom border |
30
+
31
+ `brand` / `accent` / `muted` / `card` remap straight from existing theme tokens — no
32
+ new tokens. `inverse` reads two stable mirrors (`--bg-base` / `--fg-base`) so the
33
+ fg/bg swap can't form a custom-property cycle.
34
+
35
+ ## Usage
36
+
37
+ ```jsx
38
+ // Each distinct band gets its own Section + scope so it's independently themeable.
39
+ <Section scope="inverse" pad="xl"><Container maxW="lg">{/* hero */}</Container></Section>
40
+ <Section pad="lg"><Container maxW="xl">{/* default-surface features */}</Container></Section>
41
+ <Section scope="muted" pad="lg"><Container maxW="xl">{/* quiet logos strip */}</Container></Section>
42
+
43
+ // Or drop the class on any element:
44
+ <div className="scope-brand">{/* re-toned island */}</div>
45
+ ```
46
+
47
+ ## Rules
48
+
49
+ - One band, one scope. Don't mix ad-hoc background/text colours inside a scoped band —
50
+ let the scope do the work so the band re-themes as a unit.
51
+ - Scopes are for **structural surfaces**. For a loud on-brand splash use the
52
+ **expressive** layer (see that foundation), not a scope.
@@ -0,0 +1,57 @@
1
+ ---
2
+ foundation: expressive
3
+ attributes: [data-expressive, data-expressive-tier]
4
+ tokens: [--gds-expressive-bg, --gds-expressive-fg, "--gds-expressive-accent{1..5}-{100,300,700,900}"]
5
+ ---
6
+
7
+ # Expressive colours
8
+
9
+ Expressive colours are a **highlight layer**, not the base UI. They paint *sections* —
10
+ marketing banners (including banners inside an app), feature cards, promo strips,
11
+ editorial blocks — anywhere you want an on-brand splash that is deliberately louder
12
+ than the neutral product chrome.
13
+
14
+ They are **NOT** for base surfaces, body text, form controls, or anything structural.
15
+ The semantic layer (surfaces, actions, borders) and colour **scopes** own the product
16
+ UI. Expressive sits on top, scoped to a region. So: "make a promo banner" → reach for
17
+ expressive; "build a settings form" → do not.
18
+
19
+ ## The model — 5 accent slots × 4 tiers
20
+
21
+ Five **positional** accent slots (`accent1` … `accent5`) — names are positions, not
22
+ hues, so a slot's colour can be retuned without renaming anything. Each slot resolves
23
+ to four bg+fg tiers, each pair legible by construction:
24
+
25
+ | tier | background | foreground |
26
+ |--------------|-------------------|-------------------|
27
+ | `superlight` | `{accent}/100` | `{accent}/900` |
28
+ | `light` | `{accent}/300` | `{accent}/900` |
29
+ | `dark` | `{accent}/700` | `{accent}/100` |
30
+ | `superdark` | `{accent}/900` | `{accent}/100` |
31
+
32
+ ## Usage
33
+
34
+ Set the slot + tier on the region; paint with the expressive tokens:
35
+
36
+ ```jsx
37
+ <Section pad="xl">
38
+ <Container maxW="lg">
39
+ <div data-expressive="accent3" data-expressive-tier="superdark"
40
+ className="rounded-2xl p-10"
41
+ style={{ background: "var(--gds-expressive-bg)", color: "var(--gds-expressive-fg)" }}>
42
+ {/* promo content — bg + fg come as a legible pair */}
43
+ </div>
44
+ </Container>
45
+ </Section>
46
+ ```
47
+
48
+ `data-expressive="accentN"` selects the slot; `data-expressive-tier` selects the tier;
49
+ `--gds-expressive-bg` / `--gds-expressive-fg` then resolve to that pair. Switch the slot
50
+ or tier → the whole region reskins, on-brand, contrast intact.
51
+
52
+ ## Rules
53
+
54
+ - Expressive = louder-than-chrome highlight regions only. Never base surfaces or controls.
55
+ - Always use the **pair** (`bg` + `fg`) so contrast holds; don't pick a background without
56
+ its paired foreground.
57
+ - The 5 accent ramps are rebrandable via `--gds-expressive-accent{N}-{100,300,700,900}`.
@@ -0,0 +1,51 @@
1
+ ---
2
+ foundation: spacing-layout
3
+ section_pad: [none, sm, md, lg, xl]
4
+ container_maxw: [sm, md, lg, xl, prose, full]
5
+ tokens: ["--spacing"]
6
+ ---
7
+
8
+ # Spacing & layout
9
+
10
+ Layout rhythm comes from two primitives, not from ad-hoc padding/margins on bands.
11
+
12
+ ## Section — vertical rhythm
13
+
14
+ `Section` owns the band's vertical padding via `pad` (responsive `py`):
15
+
16
+ | pad | use |
17
+ |--------|---------------------------------------|
18
+ | `none` | flush band (e.g. full-bleed media) |
19
+ | `sm` | tight |
20
+ | `md` | compact |
21
+ | `lg` | **default** — standard band rhythm |
22
+ | `xl` | hero / statement band |
23
+
24
+ `Section` is always full width and never sets a max width — that is the Container's job.
25
+
26
+ ## Container — the measure (horizontal)
27
+
28
+ `Container` centres content and sets gutters via `maxW`:
29
+
30
+ | maxW | use |
31
+ |---------|------------------------------------------------|
32
+ | `sm` | narrow (focused CTA / form) |
33
+ | `md` | medium |
34
+ | `lg` | **default** — standard content measure |
35
+ | `xl` | wide (feature grids, dense dashboards) |
36
+ | `prose` | long-form reading measure (markdown / article) |
37
+ | `full` | edge-to-edge — full-bleed bands STILL use this |
38
+
39
+ `Container grid` snaps children to a 12-column grid (`col-span-*` on children).
40
+
41
+ ## Density
42
+
43
+ Base spacing scales from the theme's `--spacing` density token, so the whole system
44
+ re-pitches its rhythm from one knob. Spacing utilities derive from it — don't hardcode
45
+ absolute spacing that can't follow the density.
46
+
47
+ ## Rules
48
+
49
+ - Bands get their vertical rhythm from `Section pad`, their measure from `Container maxW`.
50
+ - Full-bleed = `<Container maxW="full">`, **never** omitting the Container.
51
+ - Don't hand-roll `py-*` / `max-w-* mx-auto px-*` page wrappers — that's what these replace.
@@ -0,0 +1,52 @@
1
+ ---
2
+ foundation: themes
3
+ import: "@gradeui/ui"
4
+ apis: [generateTheme, themeToCSSVars, applyThemeToRoot, GradeThemeProvider, useGradeTheme, builtInThemes, GRADE_PRE_HYDRATION_SCRIPT]
5
+ attribute: data-grade-theme
6
+ ---
7
+
8
+ # Themes
9
+
10
+ A Grade theme is a **deterministic `ThemeInput`** — a small, portable description
11
+ that the generator expands into the full CSS-variable token set. The same input
12
+ always produces the same tokens, so a theme is shareable and reproducible.
13
+
14
+ ## The ThemeInput shape (what you set)
15
+
16
+ - **hues** — OKLCH hue anchors for the `neutral`, `primary`, and `accent` ramp families.
17
+ - **chroma / vibrancy** — colour intensity, backed by the real OKLCH `C` (not a multiplier).
18
+ - **intensity** — overall vibrancy of the expressive/accent layer.
19
+ - **typography** — font roles (`display` / `body` / `mono` / `accent`) + a modular `scale`.
20
+ See the typography foundation.
21
+ - **spacing / density** — base spacing rhythm. See the spacing & layout foundation.
22
+
23
+ Everything is optional and sparse: an empty `ThemeInput` generates the default
24
+ Grade theme. Each ramp family expands to a 50–950 OKLCH ramp (`--ramp-*`), and the
25
+ semantic surface/action tokens reference those ramps.
26
+
27
+ ## Applying a theme
28
+
29
+ ```tsx
30
+ import { GradeThemeProvider } from "@gradeui/ui";
31
+ import "@gradeui/ui/styles.css";
32
+
33
+ export default function App({ children }) {
34
+ return <GradeThemeProvider>{children}</GradeThemeProvider>;
35
+ }
36
+ ```
37
+
38
+ - `GradeThemeProvider` / `useGradeTheme` — React provider + hook for the active theme + mode.
39
+ - `generateTheme(input)` → tokens; `themeToCSSVars(theme)` / `applyThemeToRoot(theme)` to
40
+ apply them outside React.
41
+ - `builtInThemes` — the shipped starter themes.
42
+ - `GRADE_PRE_HYDRATION_SCRIPT` — inline in `<head>` to set `data-grade-theme` before paint
43
+ (no flash of the wrong theme).
44
+
45
+ ## Rules
46
+
47
+ - **Token-bound, never raw.** Generated UI references tokens (`bg-background`,
48
+ `text-foreground`, `text-primary`, the `--gds-*` / `--ramp-*` vars), never literal hex.
49
+ A value that can't be reached through a token can't be re-themed — so don't emit it.
50
+ - **Minimum extra tokens, maximum impact.** A handful of named roles re-skin every
51
+ surface. Don't add a token per component per state; assign a role and let surfaces wear it.
52
+ - **Determinism is load-bearing.** The theme must stay a pure function of its input.
@@ -0,0 +1,60 @@
1
+ ---
2
+ foundation: typography
3
+ font_roles: [display, body, mono, accent]
4
+ steps: [display, h1, h2, h3, h4, h5, h6, body, small, caption]
5
+ tokens: ["--text-display", "--text-h1..--text-h6", "--text-body", "--text-small", "--text-caption", "--text-label", "--text-overline", "--font-display", "--font-body", "--font-mono", "--font-accent"]
6
+ ---
7
+
8
+ # Typography
9
+
10
+ Type is theme-owned and token-bound. A style never names a raw font family or a
11
+ `tracking-*` utility — it picks a **role** and rides the theme's scale, so it stays
12
+ portable and re-themeable.
13
+
14
+ ## Font roles
15
+
16
+ - **display** — headings / large type (`--font-display`, `font-display` utility).
17
+ - **body** — the workhorse (`--font-body`).
18
+ - **mono** — code / tabular (`--font-mono`).
19
+ - **accent** — supplementary display face for eyebrows, pull quotes, stylised bits
20
+ (`--font-accent`, `font-accent` utility); defaults to Instrument Serif, overridable.
21
+
22
+ ## The step ladder
23
+
24
+ Named steps that screens actually use, each emitted as a `--text-*` token with its
25
+ companion line-height / letter-spacing / weight:
26
+
27
+ ```
28
+ display · h1 · h2 · h3 · h4 · h5 · h6 · body · small · caption
29
+ ```
30
+
31
+ Plus the supporting tokens `--text-label`, `--text-overline`, and the raw size ladder
32
+ (`--text-2xs … --text-7xl`). Size always comes from the modular scale; weight,
33
+ leading, and tracking cascade **base default → base style → step override**.
34
+
35
+ Base styles (the mixers each step inherits from): **Body / Header / Mono / Prose**.
36
+ `h*` steps inherit Header; the rest inherit Body. Prose is the typography of a
37
+ markdown/rich-text tree (the Tailwind `prose` surface) and reuses the base styles, so
38
+ restyling the Header base restyles both app headings and prose headings.
39
+
40
+ ## Usage
41
+
42
+ Prefer the Section heading parts and semantic elements over hand-sized text:
43
+
44
+ ```jsx
45
+ <Section pad="xl">
46
+ <Container maxW="lg">
47
+ <SectionEyebrow>New</SectionEyebrow> {/* overline / accent */}
48
+ <SectionTitle>Own the components.</SectionTitle> {/* display / h1 */}
49
+ <SectionSubtitle>Ship on your subscription.</SectionSubtitle>
50
+ </Container>
51
+ </Section>
52
+ ```
53
+
54
+ ## Rules
55
+
56
+ - **Token-bound, never raw.** Reference roles and `--text-*` steps; don't emit literal
57
+ `font-family`, px sizes, or `tracking-[...]` values.
58
+ - **Weight is per style**, not a single global heading knob.
59
+ - The modular scale can differ per breakpoint (mobile drops a step); only sizes change,
60
+ leading/tracking/weight ride along.
package/llms.txt ADDED
@@ -0,0 +1,32 @@
1
+ # @gradeui/ui
2
+
3
+ > Grade Design System — React components, a deterministic theme engine, and design
4
+ > tokens. This package is self-describing: everything an AI agent needs to generate
5
+ > correct Grade UI ships inside the installed package.
6
+
7
+ The one rule: a page is an ordered stack of `Section` bands, and every `Section`
8
+ wraps a `Container`. Full-bleed is `<Container maxW="full">`, never omitting it.
9
+ Never hand-roll `<section className="py-20">` or `<div className="max-w-7xl mx-auto px-6">`.
10
+
11
+ ## Read first
12
+
13
+ - [AGENTS.md](./AGENTS.md): the entry point — the scaffold rule + where everything is.
14
+ - [DESIGN.md](./DESIGN.md): the whole design system in one file — foundations
15
+ (themes, colour scopes, expressive accents, typography, spacing) + every component
16
+ sidecar (when_to_use, props, examples).
17
+ - [DESIGN.index.md](./DESIGN.index.md): a cheap one-line-per-component scan; pull the
18
+ specific `components/ui/<name>.md` sidecar you need from it.
19
+
20
+ ## Programmatic
21
+
22
+ - `import { COMPONENT_CONTRACTS } from "@gradeui/ui/contracts"`: machine-readable prop
23
+ schemas (zod), descriptions, aliases, composition data.
24
+ - `import { generateTheme, GradeThemeProvider } from "@gradeui/ui"`: the theme engine.
25
+ - `import "@gradeui/ui/styles.css"`: precompiled styles (or wire
26
+ `@gradeui/ui/styles/globals.css` into a Tailwind v4 build).
27
+
28
+ ## Optional per-project layer
29
+
30
+ A consumer may add their own `Product.md` (what the product is + voice/do-don'ts) in
31
+ their repo. The generating harness concatenates it on top of this design-system layer:
32
+ [system: foundations + scaffold] + [product: Product.md] + [retrieved sidecars] + [prompt].
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradeui/ui",
3
- "version": "4.0.1",
3
+ "version": "4.1.0",
4
4
  "description": "Grade Design System — React components, theme engine, and design tokens",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -62,6 +62,11 @@
62
62
  "./package.json": "./package.json",
63
63
  "./styles.css": "./dist/styles.css",
64
64
  "./styles/globals.css": "./styles/globals.css",
65
+ "./DESIGN.md": "./DESIGN.md",
66
+ "./DESIGN.index.md": "./DESIGN.index.md",
67
+ "./AGENTS.md": "./AGENTS.md",
68
+ "./llms.txt": "./llms.txt",
69
+ "./foundations/*": "./foundations/*",
65
70
  "./contracts": {
66
71
  "types": "./dist/contracts.d.ts",
67
72
  "import": "./dist/contracts.mjs",
@@ -91,7 +96,12 @@
91
96
  "files": [
92
97
  "dist",
93
98
  "styles",
94
- "components/ui/*.md"
99
+ "components/ui/*.md",
100
+ "foundations/*.md",
101
+ "DESIGN.md",
102
+ "DESIGN.index.md",
103
+ "AGENTS.md",
104
+ "llms.txt"
95
105
  ],
96
106
  "sideEffects": [
97
107
  "**/*.css"
@@ -199,7 +209,9 @@
199
209
  },
200
210
  "scripts": {
201
211
  "generate:contracts": "node scripts/generate-contracts.mjs",
202
- "prebuild": "npm run generate:contracts",
212
+ "generate:design": "node scripts/generate-design.mjs",
213
+ "verify:portability": "node scripts/verify-portability.mjs",
214
+ "prebuild": "npm run generate:contracts && npm run generate:design",
203
215
  "build": "npm run clean && tsup && npm run build:css",
204
216
  "build:css": "tailwindcss -i ./styles/globals.css -o ./dist/styles.css --minify",
205
217
  "build:css:watch": "tailwindcss -i ./styles/globals.css -o ./dist/styles.css --watch",