Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ on:
jobs:
checks:
runs-on: ubuntu-latest
env:
ASTRO_TELEMETRY_DISABLED: "1"
steps:
- uses: actions/checkout@v7
# The pnpm version comes from package.json#packageManager, the same one
Expand All @@ -18,10 +20,10 @@ jobs:
- run: pnpm install --frozen-lockfile
# The same sequence `pnpm verify` runs locally, one step each so a failure
# names its stage: format → lint + typecheck → behaviour tests → build →
# catalog → pack + consumer smoke.
# docs site → pack + consumer smoke.
- run: pnpm format:check
- run: pnpm lint
- run: pnpm test
- run: pnpm build
- run: pnpm catalog:check
- run: pnpm docs:check
- run: pnpm test:package
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
node-version-file: .nvmrc
- run: pnpm install --frozen-lockfile
# Nothing publishes that has only been compiled: format, lint, typecheck,
# behaviour tests, build, catalog, and the packed-consumer smoke test all
# behaviour tests, build, the docs site, and the packed-consumer smoke test all
# have to pass first.
- run: pnpm verify
- name: tag matches package.json
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
node_modules/
dist/
examples/catalog/dist/
examples/catalog/.astro/
examples/catalog/src/generated/
*.tmp.md

# The Claude Design sync: its inputs, its staged converter and the bundle it
Expand Down
5 changes: 5 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Build output and generated files.
dist/
examples/catalog/.astro/
examples/catalog/src/generated/
pnpm-lock.yaml

# Markdown is formatted by hand here, and Prettier's markdown printer fights
Expand All @@ -17,6 +19,9 @@ pnpm-lock.yaml
# 100-column guide in `.editorconfig`.
*.md

# MDX pages are prose too, hand-formatted for the same two reasons.
*.mdx

# Process records, kept as written at the time.
docs/superpowers/
.superpowers/
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ src/theme/ styles.css, the single visual contract; shadcn.css, shadcn's l
src/index.ts the public surface, exported by name
components.json how the shadcn CLI installs a new component here
tests/ behaviour tests, the token and shadcn-layer contracts, the packed-consumer test
examples/catalog manual inspection of every component and state, light and dark
examples/catalog the documentation site (Astro): a page, live demos and API tables per component
docs/ DESIGN.md, CONTRIBUTING.md, components/, MIGRATION-*.md
```

Expand All @@ -71,7 +71,7 @@ docs/ DESIGN.md, CONTRIBUTING.md, components/, MIGRATION-*.md

## Verification

`pnpm verify` runs format, lint and typecheck, the behaviour tests, the build, the catalog, and a
`pnpm verify` runs format, lint and typecheck, the behaviour tests, the build, the docs site, and a
packed-consumer test that installs the tarball into a throwaway project and compiles it with a real
Tailwind. The release workflow runs the same before it publishes.

Expand Down
25 changes: 18 additions & 7 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ Node 24 (`.nvmrc`) and the pnpm named in `package.json#packageManager`.
pnpm install
```

The repository is a pnpm workspace: the package at the root, and `examples/catalog` as a second
member that depends on it as `workspace:*`.
The repository is a pnpm workspace: the package at the root, and the documentation site (Astro) at
`examples/catalog` as a second member that depends on it as `workspace:*`.

## Scripts

Expand All @@ -20,9 +20,9 @@ member that depends on it as `workspace:*`.
| `pnpm lint` | ESLint (including the colour-literal rule) and `tsc --noEmit` over `src` and `tests`. |
| `pnpm format` / `pnpm format:check` | Prettier. Markdown is hand-formatted at 100 columns and excluded. |
| `pnpm build` | Cleans `dist/`, compiles `src/` with declarations, and rewrites the `@/` alias to relative paths (`tsc-alias`). |
| `pnpm catalog` | Builds the package and serves the catalog with Vite. |
| `pnpm catalog:check` | Typechecks and builds the catalog, as CI does. |
| `pnpm verify` | Everything above in CI order: format, lint, test, build, catalog, package test. |
| `pnpm docs:dev` | Stops any running dev server, drops Vite's dependency cache, builds the package and serves the docs site on <http://localhost:4321>. Named with the colon because plain `docs` is a pnpm built-in. |
| `pnpm docs:check` | Generates the API tables, runs `astro check` and builds the docs site, as CI does. |
| `pnpm verify` | Everything above in CI order: format, lint, test, build, docs site, package test. |

## Adding or changing a component

Expand All @@ -37,7 +37,7 @@ imports, which the build resolves. Read the diff, then adapt: the checklist is i
[DESIGN.md](DESIGN.md), *Adding a component*. In short: colour only through the roles, geometry in
the component, `type="button"`, no exit animation on a surface whose trigger can be pressed again on
the next frame, export by name from `src/index.ts`, a behaviour test in `tests/components/`, a
specimen in the catalog, a row in `docs/components/README.md`. Never reinstall an existing
page with demos in the docs site, a row in `docs/components/README.md`. Never reinstall an existing
component.

The CLI pulls in an item's registry dependencies, which for most of the newer components include
Expand All @@ -57,6 +57,17 @@ happens to emit (`data-[state=open]:`, `data-[orientation=vertical]:`). A `data-
the values the layer declares no variant for, and for attributes that are not states; see
DESIGN.md, *State attributes*.

## The docs site

`examples/catalog` is an Astro app that imports the package through the workspace link, so
`pnpm build` runs first. A component page is `src/content/components/<name>.mdx`; its demos are
`src/demos/<name>/<story>.tsx`, one story per file, and the file is the code sample the page shows.
The API tables are generated into `src/generated/api/` (gitignored) by `scripts/extract-api.ts`
from the component sources: `astro dev` and `pnpm docs:check` run it, and
`tests/docs/api-extractor.test.ts` holds its shape and fails once a component has no page.
`.astro` files are formatted by Prettier but not linted; the colour-literal rule covers the `.tsx`
demos and islands.

## Updating the shadcn layer

`src/theme/shadcn.css` is a byte-for-byte copy of `shadcn/dist/tailwind.css`, and
Expand All @@ -76,7 +87,7 @@ that file; a utility we need lives in `styles.css` after the import.
A token is a role. Declare it in `:root` and `.dark` in `src/theme/styles.css`, expose it in
`@theme inline` as `--color-<role>: var(--<role>)`, add it to `ROLE_NAMES` in
`tests/theme/tokens.test.ts` and to both maps in `src/theme/tokens.ts`, and give it a swatch in
`examples/catalog/src/sections/Foundations.tsx`. If it is not a role — if the name describes a
`examples/catalog/src/components/Swatches.astro`. If it is not a role — if the name describes a
pigment rather than a purpose — it does not belong here.

## Tests
Expand Down
10 changes: 6 additions & 4 deletions docs/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ src/theme/shadcn.css shadcn's utility and variant layer, vendored byte for byt
src/theme/tokens.ts a runtime mirror of the token values, for callers that cannot read CSS
components.json the shadcn CLI's configuration: style, stylesheet, aliases
tests/ behaviour tests (jsdom), token and shadcn-layer contracts, consumer test
examples/catalog/ manual inspection, importing the real package
examples/catalog/ the documentation site (Astro), importing the real package
```

Internal imports are written against the `@/` alias `components.json` declares (`@/components/…`,
Expand Down Expand Up @@ -287,8 +287,10 @@ Then the component is adapted, whichever road it came by:
to bottom; `export *` is not used.
8. **Test the behaviour a screen would silently lose**, in `tests/components/`: roles, focus,
`aria-*`, the form or pointer outcome. Not the class string.
9. **Show it in `examples/catalog/`**, in every state it has, and give it a row in
`docs/components/README.md`.
9. **Give it a page in the docs site**: `examples/catalog/src/content/components/<name>.mdx` with
a demo per state under `examples/catalog/src/demos/<name>/`, and a row in
`docs/components/README.md`. The API table is generated from the source; the extractor test
fails until the page exists.

If a caller wants something the component does not offer, the answer is a prop or a variant *in
the component*, never a class string spread onto it from the call site.
Expand All @@ -313,6 +315,6 @@ justification written into this file.
| The packed tarball installs, its stylesheet compiles under a real Tailwind with the components' utilities, shadcn's layer and no physical palette, its entry imports and renders (Sidebar included, through the rewritten alias), `dist/` carries no `@/` import, `shadcn` is not a runtime dependency, a Button-only bundle stays small | `tests/package/consumer.test.ts` |
| The harness itself | `tests/harness.test.tsx` |

`pnpm verify` runs format, lint, typecheck, the behaviour tests, the build, the catalog, and the
`pnpm verify` runs format, lint, typecheck, the behaviour tests, the build, the docs site, and the
packed-consumer test, in that order; the release workflow refuses to publish anything that has not
passed it.
4 changes: 2 additions & 2 deletions docs/components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ The package is built on top of shadcn/ui: a component enters from the shadcn reg
`components.json` (`pnpm dlx shadcn@latest add <name>`), is adapted to the rules in
[DESIGN.md](../DESIGN.md), and is owned here from then on.
The file is the reference for anatomy, variants and class strings; this table says what each one
is for and where its behaviour comes from. Every one of them is shown in every state it has in
`examples/catalog/` (`pnpm catalog`).
is for and where its behaviour comes from. Every one of them has a page, with live demos and a
generated API table, in the docs site under `examples/catalog/` (`pnpm docs:dev`).

| Component | Anatomy exported | Behaviour | Notes |
| --- | --- | --- | --- |
Expand Down
Loading
Loading