Generator (scripts/generate-htmx-css.ts): track `viaVariants` per slot so
slots that compose another component's variant system (e.g. ToggleGroupItem
via toggleVariants) inherit the referenced CVA's base + variant rules under
their own selector. Previously toggle-group-item's CSS contained only its
override classes, shipping with no padding/height/hover/active state.
Toggle (components/ui/toggle.tsx):
- data-[state=on] now uses bg-primary (orange) instead of bg-accent (grey),
matching every other "commit" affordance in the palette.
- Horizontal padding aligned with Button: px-4/px-3/px-6 per size, plus
has-[>svg]:px-* for icon-only toggles.
ToggleGroup (components/ui/toggle-group.tsx): drop min-w-0 flex-1 shrink-0
from the item override. Items now size to content instead of being clamped
into equal narrow columns where longer labels overflowed the bg box.
Showcase: add ToggleGroup section to the React page (component-matrix.tsx)
and 1:1 HTMX mirror (public/htmx.html) with a new JS bridge branch for
single/multi-select. compare-all.sh extended with the new section; 22/22
pass at ≥99.97%.
Docs: GAPS.md captures the generator gap, overflow root cause, color
rationale, and padding parity with before/after numbers.
Greyhaven Design System
A framework-agnostic React component library built on Radix UI, Tailwind CSS v4, and shadcn/ui patterns. Designed for LLM consumption with a Claude Skill, MCP server, and Storybook documentation.
Quick Start
pnpm install # Install dependencies
pnpm dev # Start showcase dev server (Next.js)
pnpm build # Tokens + SKILL.md + production build
pnpm storybook # Component catalog on http://localhost:6006
Project Structure
greyhaven-design-system/
├── components/ui/ # 37+ framework-agnostic React components
├── tokens/ # W3C DTCG design tokens (source of truth)
│ ├── color.json
│ ├── typography.json
│ ├── spacing.json
│ ├── radii.json
│ ├── shadows.json
│ └── motion.json
├── skill/ # AI skills
│ ├── SKILL.md # Design system reference (auto-generated)
│ ├── AGENTS.md # Project instructions (auto-generated)
│ ├── BRAND.md # Voice/tone/messaging (hand-curated, opt-in)
│ └── install.sh # Installer (supports --brand-skill flag)
├── mcp/ # MCP server for AI agents
│ └── server.ts
├── stories/ # Storybook stories (23 files, 8 categories)
├── lib/
│ ├── utils.ts # cn() utility
│ └── catalog.ts # Shared component catalog (used by MCP + SKILL.md)
├── scripts/
│ ├── generate-skill.ts # SKILL.md generator
│ └── generate-htmx-css.ts # HTMX / framework-agnostic CSS generator
├── dist/
│ └── greyhaven.htmx.css # Auto-generated CSS for HTMX/server-rendered projects
├── app/ # Next.js showcase app (demo only)
└── style-dictionary.config.mjs
Tech Stack
- Components: React 19, Radix UI, Tailwind CSS 4, CVA, tailwind-merge, clsx
- Icons: Lucide React
- Forms: React Hook Form + Zod
- Tokens: Style Dictionary v4 (W3C DTCG format)
- Docs: Storybook 10, auto-generated SKILL.md
- AI Integration: MCP server, Claude Skill
Framework-agnostic: Components have zero Next.js imports. They work with Vite, Remix, Astro, CRA, or any React framework.
Also works without React:
dist/greyhaven.htmx.cssexposes every component viadata-slot/data-variant/data-sizeattribute selectors. HTMX, Django, Rails, Go template, Astro SSR — any project that emits HTML can consume the visual layer. See HTMX / server-rendered usage.
Using the Design System with AI
The design system provides four things for AI agents:
| File | What it is | Where it goes |
|---|---|---|
| SKILL.md | Full design system reference (tokens, all components, composition rules, extension protocol) | .claude/skills/ — works in Claude Code, Cursor, OpenCode, Anigravity, etc. |
| AGENTS.md | Short project instructions telling the AI how to use the design system (follows the agents.md convention) | Project root — copy as AGENTS.md, CLAUDE.md, .cursorrules, or .github/copilot-instructions.md |
| BRAND.md (opt-in) | Voice/tone/messaging rules for generating marketing copy, CTAs, product explanations. Hand-curated from the brand guidelines. | .claude/skills/greyhaven-brand.md — opt in via --brand-skill flag |
| MCP Server | Runtime tools for looking up components, validating colors, suggesting components, fetching brand rules, validating copy | Configured in .mcp.json |
SKILL.md and AGENTS.md are auto-generated from tokens/*.json and lib/catalog.ts. BRAND.md is hand-curated from vibedocs/greyhaven-brand-system.md.
Quick Install (all at once)
# Default: SKILL.md + AGENTS.md + fonts
./skill/install.sh /path/to/your/project
# With brand skill: also install BRAND.md + logo SVGs
./skill/install.sh /path/to/your/project --brand-skill
This copies (not symlinks) the following into your project:
SKILL.md→.claude/skills/greyhaven-design-system.md(full reference)AGENTS.md→ project root (project-level instructions)- Aspekta font files →
public/fonts/
With --brand-skill, additionally:
BRAND.md→.claude/skills/greyhaven-brand.md(voice/tone/messaging)- Logo SVGs →
public/logos/(file names normalized:gh-logo-positive-full-black.svg,gh-symbol-full-black.svg, etc.)
The script also prints the CSS @font-face block and MCP server config to add next.
Re-run the script after design system updates to refresh your copies.
SKILL.md (full reference)
The skill file gives any AI agent full design system context — every token, every component with props/variants/examples, composition rules, font setup, and the extension protocol.
It's a global standard — works with Claude Code, Cursor, OpenCode, Anigravity, and any tool that reads skill files.
# Via install script (recommended — also handles fonts + AGENTS.md)
./skill/install.sh /path/to/your/project
# Or copy manually
mkdir -p /path/to/your/project/.claude/skills
cp /path/to/greyhaven-design-system/skill/SKILL.md \
/path/to/your/project/.claude/skills/greyhaven-design-system.md
AGENTS.md (project instructions)
Short, directive instructions that tell the AI agent how to work in the project — use TypeScript, use semantic tokens, reference the MCP tools, etc. Follows the agents.md convention so it works with most AI coding tools out of the box.
Copy it to your project root under whichever name your tool reads:
# Standard (agents.md convention — Cursor, OpenCode, Windsurf, Aider, etc.)
cp /path/to/greyhaven-design-system/skill/AGENTS.md /path/to/your/project/AGENTS.md
# Claude Code
cp /path/to/greyhaven-design-system/skill/AGENTS.md /path/to/your/project/CLAUDE.md
# Cursor (legacy)
cp /path/to/greyhaven-design-system/skill/AGENTS.md /path/to/your/project/.cursorrules
# GitHub Copilot
mkdir -p /path/to/your/project/.github
cp /path/to/greyhaven-design-system/skill/AGENTS.md /path/to/your/project/.github/copilot-instructions.md
Or use the install script, which copies AGENTS.md to the project root automatically.
BRAND.md (voice, tone, messaging)
BRAND.md is an opt-in skill for projects that generate user-facing content — marketing copy, landing pages, CTAs, product explanations, emails. It codifies the Greyhaven brand voice: direct, plain-spoken, engineering-flavored, no hype, no sales language.
It's not installed by default because most projects only need the design system components, not brand voice rules.
Install via the --brand-skill flag:
./skill/install.sh /path/to/your/project --brand-skill
This adds:
skill/BRAND.md→.claude/skills/greyhaven-brand.md(brand skill)- Greyhaven logo SVGs →
public/logos/(full logos + symbol-only + product lockups, in black and white variants, file names normalized)
Once installed, AI agents in your project can reference the brand skill when generating copy. The skill covers:
- Core positioning and the three brand axes (containment, human-centered, engineered)
- Tone of voice rules
- Writing patterns (plain-language engineering, no hype)
- Reasoning patterns (cause→effect, constraint→outcome, observation→explanation, finite scope→concrete result)
- CTA guidance (good vs. bad patterns)
- Logo usage rules
- A self-check list to run before shipping any copy
Option C: MCP Server
The MCP server provides 7 tools for programmatic access:
| Tool | Description |
|---|---|
get_tokens(category?) |
Returns token values (all, or filtered by: color, typography, spacing, radii, shadows, motion) |
get_component(name) |
Returns full component spec + source code |
list_components(category?) |
Lists components (all, or by: primitives, layout, overlay, navigation, data, feedback, form, composition) |
validate_colors(code) |
Checks code for raw hex values that should use design tokens |
suggest_component(description) |
Suggests components for a described UI need |
get_brand_rules(section?) |
Returns brand voice/tone/messaging rules. Section can be: positioning, axes, tone, writing-rules, reasoning-patterns, cta, logo, self-check, or all |
validate_copy(text) |
Lints marketing copy for hype words, sales language, vague superlatives, urgency framing, and exclamation marks |
Plus resources: tokens://all, component://{name} for each component, and brand://guidelines for the full brand skill.
Run directly:
pnpm mcp:start
Install in Claude Code (.mcp.json in your project root):
{
"mcpServers": {
"greyhaven": {
"command": "npx",
"args": ["tsx", "/absolute/path/to/greyhaven-design-system/mcp/server.ts"]
}
}
}
Install in Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"greyhaven": {
"command": "npx",
"args": ["tsx", "/absolute/path/to/greyhaven-design-system/mcp/server.ts"]
}
}
}
After adding, restart Claude Code / Claude Desktop. The tools will appear automatically.
Test it works:
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | pnpm mcp:start
You should see a JSON response with "serverInfo":{"name":"greyhaven-design-system"}.
Design Tokens
Tokens are defined in tokens/*.json using the W3C Design Token Community Group format. Style Dictionary v4 generates:
| Output | Path | Purpose |
|---|---|---|
| CSS (light) | app/tokens/tokens-light.css |
:root CSS custom properties |
| CSS (dark) | app/tokens/tokens-dark.css |
.dark CSS custom properties |
| TypeScript | app/tokens/tokens.ts |
Type-safe token constants |
| Markdown | app/tokens/TOKENS.md |
Reference doc |
pnpm tokens:build # Regenerate all outputs from tokens/*.json
Storybook
23 story files across 8 categories with autodocs, theme switching (light/dark via toolbar), and all component variants.
pnpm storybook # Dev server on http://localhost:6006
pnpm build-storybook # Static build
HTMX / server-rendered usage
The React components assume a React runtime. For HTMX, Django templates, Rails ERB, Go html/template, Astro SSR, or any other server-rendered stack, consume the design system via the auto-generated CSS layer.
What you get
dist/greyhaven.htmx.css is generated from components/ui/*.tsx (AST walk over cva() configs + static className strings on data-slot elements). It contains ~300 @layer components rules, one per data-slot, with attribute selectors for variants and sizes.
[data-slot="card"] { @apply bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm; }
[data-slot="card-header"] { @apply grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6; }
[data-slot="card-title"] { @apply leading-none font-semibold; }
[data-slot="button"] { @apply inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium …; }
[data-slot="button"]:not([data-variant]),
[data-slot="button"][data-variant="default"] { @apply bg-primary text-primary-foreground hover:bg-primary/90; }
[data-slot="button"][data-variant="outline"] { @apply border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground; }
[data-slot="button"][data-size="sm"] { @apply h-8 rounded-md gap-1.5 px-3; }
Install
./skill/install.sh /path/to/your/project --htmx-css
This copies:
dist/greyhaven.htmx.css→public/css/greyhaven.htmx.css- Aspekta fonts →
public/fonts/
Add to your Tailwind v4 input CSS:
@import "tailwindcss";
@import "./tokens-light.css";
@import "./tokens-dark.css";
@import "./greyhaven.htmx.css";
Consume
<div data-slot="card">
<div data-slot="card-header">
<div data-slot="card-title">Requests Over Time</div>
<div data-slot="card-description">Last 24 hours</div>
</div>
<div data-slot="card-content">…</div>
</div>
<button data-slot="button" data-variant="default">Save</button>
<button data-slot="button" data-variant="outline" data-size="sm">Cancel</button>
<span data-slot="badge" data-variant="success">Active</span>
Scope
- Static visual components (Card, Button, Badge, Input, Label, Textarea, Table, Separator, Code, Kbd, Progress, Avatar, Skeleton, Alert, Pagination, Breadcrumb, Navbar, etc.) → fully driven by CSS, no JS needed.
- Interactive components (Dialog, Dropdown, Popover, Select, Combobox, Accordion, Tabs, Tooltip, etc.) → CSS emits their static styles, but open/close / positioning / focus management is the consumer's responsibility. Alpine.js pairs naturally with HTMX for these.
- Native HTML alternatives:
<details>covers Accordion/Collapsible,<dialog>covers Dialog. The CSS rules apply to those too.
Regenerate
pnpm htmx-css:build # Regenerate dist/greyhaven.htmx.css from components/ui/*.tsx
Re-runs of ./skill/install.sh --htmx-css in consumer projects refresh their copy.
Adding a New Component
- Create
components/ui/my-component.tsxfollowing the CVA pattern (seebutton.tsx) - Add it to the catalog in
lib/catalog.ts - Create a story in
stories/<Category>/MyComponent.stories.tsx - Run
pnpm skill:buildto regenerate SKILL.md (or justpnpm build) - The MCP server picks it up automatically (reads
lib/catalog.tsat runtime)
Scripts Reference
| Script | Description |
|---|---|
pnpm dev |
Start Next.js showcase dev server |
pnpm build |
Full build: tokens + SKILL.md + Next.js |
pnpm storybook |
Storybook dev server on :6006 |
pnpm build-storybook |
Static Storybook build |
pnpm tokens:build |
Regenerate CSS/TS/MD from token JSON files |
pnpm skill:build |
Regenerate skill/SKILL.md and skill/AGENTS.md from tokens + catalog |
pnpm htmx-css:build |
Regenerate dist/greyhaven.htmx.css from components/ui/*.tsx |
pnpm mcp:start |
Start the MCP server (stdio transport) |
pnpm mcp:build |
Type-check MCP server |
pnpm lint |
Run ESLint |
