Adding a component
The shortest path from “I want a new component” to “merged and documented”:
1. Scaffold under packages/core/src/components/<name>/
packages/core/src/components/banner/ Banner.tsx # the component banner.css # styles (CSS variables, no hard-coded values) spec.md # the contract (see below)Follow naming conventions:
- Component file: PascalCase (
Banner.tsx) - Directory: kebab-case (
banner/) - CSS file: kebab-case (
banner.css) - Spec file: always
spec.md
2. Write the component
Two rules that keep helixui coherent:
- CSS variables, not inline values. Every color, spacing, radius, font
size — all of them come from
--helixui-*custom properties. - No global state. Components are pure; if you need state, lift it.
import './banner.css';import type { ReactNode } from 'react';
export interface BannerProps { tone?: 'info' | 'success' | 'warning' | 'danger'; icon?: ReactNode; children: ReactNode;}
export function Banner({ tone = 'info', icon, children }: BannerProps) { return ( <div className={`helixui-banner helixui-banner--${tone}`} role="status"> {icon ? <span className="helixui-banner__icon">{icon}</span> : null} <div className="helixui-banner__body">{children}</div> </div> );}3. Write the spec
spec.md is read by three different consumers: the docs site, the LLM
manifest, and the MCP server. Keep it accurate.
---title: Bannerimport: "import { Banner } from '@helixui/core'"description: "A horizontal callout for inline messages."props: - { name: tone, type: '"info" | "success" | "warning" | "danger"', default: '"info"' } - { name: icon, type: 'ReactNode', default: 'undefined' }tokens: - color.bg.banner.info - color.text.banner.info - radius.md - spacing.3a11y: - role="status" on the root - icon should be aria-hidden (decorative)---
## Anatomy
A `<div role="status">` with an optional icon slot and a body slot.
## Examples
```tsx<Banner tone="success">Saved.</Banner><Banner tone="warning" icon={<Warning />}>Rate limit approaching.</Banner>## 4. Export it
Add the re-export to `packages/core/src/index.ts` in alphabetical order:
```tsexport { Banner, type BannerProps } from './components/banner/Banner.js';5. Build a showcase (if it warrants one)
For components that show their value in context (forms, layouts, data
displays), add a usage in apps/site/src/pages/showcase/. For atomic
primitives (Text, Icon, etc.) the docs page from spec.md is enough.
Verify
pnpm typecheck # types are validpnpm build:llms # spec.md is picked uppnpm dev:docs # spec renders correctly on the docs siteIf build:llms fails complaining about a missing spec, you forgot step 3.