Contributing
UniAria is built by AI agents. They write the component specs, the conformance scenarios, the code, the tests and this site. The owner, @tenphi, approves every spec before work on a component starts and reviews the APIs at each phase gate.
The binding rules live in AGENTS.md files. The
root AGENTS.md
summarizes the design, the architecture rules, the component workflow and
every command; read it in full before you change anything. Each package adds
a short file with its local rules:
@uniaria/core,
@uniaria/react,
@uniaria/vue,
@uniaria/elements,
@uniaria/testing
and conformance.
The owner’s full design doc stands behind them; when it and AGENTS.md
disagree, ask the owner.
This section is the readable overview. It explains how the pieces fit and
links to the rules rather than repeating each one. If a page here and an
AGENTS.md disagree, the AGENTS.md wins.
- Architecture: the layers, the runtime and its store, the DOM binder, the adapters, owners, overlays and custom elements.
- Component workflow: spec, conformance scenarios, definition, adapters and docs, in that order.
- Testing: Node and browser tests, conformance, render counts and screen readers.
- Documentation: how this site is built, and how to write a component page with a live demo.
| Path | Contents | Published |
|---|---|---|
packages/core |
@uniaria/core: the runtime, dom/ (the binder, interactions, overlays, positioning), collections, the virtualizer and one subpath per component |
Yes |
packages/react |
@uniaria/react: useBehavior, useBehaviorState and OwnerProvider |
Yes |
packages/vue |
@uniaria/vue: useBehavior and useBehaviorState; its spec in spec.md |
Yes |
packages/elements |
@uniaria/elements: defineElement and one registering entry per component element |
Yes |
packages/testing |
@uniaria/testing: the mount contract, the conformance runner, real input and screen-reader helpers |
Yes, for development |
conformance |
@uniaria/conformance: the scenarios every adapter must pass, one folder per component with a suite |
No |
apps/docs |
This site, built with Cookbook | No |
docs |
The site’s pages. docs/_internal/ holds the component spec template and interaction-layer.md, the catalogue of what dom/ must do; it is never published |
No |
scripts |
Repository checks CI runs, and the script that fetches the reference repos | No |
.github |
The CI workflow, the nightly screen-reader workflow and the Dependabot configuration | No |
references |
Read-only clones of related projects, for reading their source and tests (gitignored) | No |
specs |
The owner’s local design notes (gitignored; never commit anything here) | No |
All published packages share one version, a Changesets fixed group. Inside
packages/core/src, runtime/ and collections/ are DOM-free, dom/ holds
what every adapter shares, and components/<name>/ holds each component’s
spec, definition and tests. See Architecture.
You need Node 22 or later (22.19 or later for the documentation site) and
pnpm 11; the root package.json pins the exact pnpm version in
packageManager.
pnpm install
pnpm test:setup
pnpm testpnpm installrefuses any package version published less than 14 days ago. The policy isminimumReleaseAgeinpnpm-workspace.yaml, a guard against compromised releases. Its exceptions are listed there too: a pinned@tanstack/virtual-corerelease until it turns 14 days old, and the owner’s own@tenphi/*packages (Cookbook, Tasty and Glaze, which build this site). Install scripts run only for packagesallowBuildsenables, and it enables none.pnpm test:setupinstalls the Playwright builds of Chromium, Firefox and WebKit that the browser tests run in.pnpm referencesis optional. It clones the related projects listed in the rootAGENTS.md(Reference repos) intoreferences/, or updates them; pass names to fetch only some (pnpm references zag virtual). Read them, never edit or import them.
Workspace packages resolve each other’s src/ through the uniaria-source
export condition, so tests and type checks never need a build first.
| Command | What it does |
|---|---|
pnpm install |
Installs the workspace. Versions younger than 14 days are refused on purpose. |
pnpm test:setup |
Installs the Playwright browsers. |
pnpm test |
The node project, then the browser project (Chromium, Firefox, WebKit). |
pnpm test:node |
Only the DOM-free tests. |
pnpm test:browser |
Only the browser tests, in all three engines. |
pnpm vitest run --project "browser (chromium)" |
One browser engine. |
pnpm test:watch |
Vitest in watch mode. |
pnpm test:screen-readers |
The real screen-reader project. It exists only in the nightly workflow; see Testing. |
pnpm build |
Builds every published package with tsdown: ESM, .d.ts files and sourcemaps. |
pnpm typecheck |
Type-checks every workspace package, including the DOM-free core configuration. |
pnpm lint / pnpm lint:fix |
ESLint, including the architecture boundaries and the Tasty rules for the site. |
pnpm format / pnpm format:check |
Prettier: single quotes, semicolons, trailing commas, 80 columns. |
pnpm hygiene / pnpm hygiene:fix |
Lint, format check and type check in one go; the :fix variant fixes lint and formats first. |
pnpm check:conformance |
Fails when an unpushed commit changes both conformance scenarios and implementation. |
pnpm bench |
Times UniAria’s Button, React Aria’s and a plain one in a production build; rewrites packages/react/bench/results.md. |
pnpm size |
Builds, then reports the brotlied size of each entry point with each adapter, and of the prebuilt CSS (.size-limit.js). There are no limits yet. |
pnpm references [name …] |
Clones or updates the reference repos. |
pnpm changeset |
Creates a changeset for a user-visible package change (see How changes land). |
The site has its own commands; see Documentation.
Commits follow Conventional Commits:
type(scope): short description.
- Types:
feat,fix,refactor,test,docs,chore,perf,ci. - Scope: the component or the layer:
feat(checkbox): …,fix(runtime): …,test(listbox): …,docs(select): …. - Small and in order. A component’s spec, its conformance scenarios and its implementation land in separate commits (see Component workflow).
- Conformance alone. A commit that changes
conformance/changes no implementation underpackages/*/src/, and the reverse. CI checks it. - Never commit anything under
specs/orreferences/. - Commit
packages/react/bench/results.mdonly after a run on a quiet machine.
Since the first release, work lands through pull requests, and big ones: a branch carries a whole piece of work (the implementation, its tests, docs and changeset), not one commit. The owner merges and releases.
- Branch from an up-to-date
main. pnpm hygiene, and fix what it reports.pnpm test: every project passes.pnpm format, then commit.pnpm changesetfor every user-visible change to a published package:minorfor a feature or a breaking change while versions are 0.x,patchfor the rest. Tests, docs and tooling need none.pnpm check:conformance: no commit on the branch touches both conformance and implementation. Split a commit that does; scenarios and implementation also go in separate pull requests.- Push the branch and open a pull request against
main(gh pr create); a draft while it waits for a spec’s approval. - Watch CI (
gh pr checks --watch) and fix a failure in a follow-up commit.
The same steps are in .cursor/commands/submit-changes.md.
.github/workflows/ci.yml runs on every push to main and on every pull
request, on Ubuntu with Node 22. Its jobs run side by side, and a new push
to a pull request cancels the run for the previous one:
checks:pnpm install --frozen-lockfile, thenpnpm lint,pnpm format:check,pnpm typecheck,pnpm buildandpnpm test:node.browser: thebrowserproject, one job per engine, each in Playwright’s Docker image for the Playwright the lockfile installs, which has the browsers and their system libraries. That runs the conformance scenarios in three engines and every adapter, spoken-output checks included; only the scenarios that need a real screen reader wait for the nightly run. Firefox runs its files one at a time, so it is split in four jobs: each adapter’s conformance file, and the rest. A pull request that changes onlydocs/, Markdown or.changeset/skips these jobs, since no test reads those files.docs:pnpm -C apps/docs validate(see Documentation).conformance-separation:scripts/check-conformance-separation.mjschecks each pushed commit, or a pull request’s whole diff, for changes to bothconformance/and non-test files underpackages/*/src/. On a pull request only the owner’sconformance-approvedlabel overrides it; on a push nothing does.
Real screen readers run nightly in a separate workflow; see Testing.
Dependabot opens grouped update pull requests weekly, for releases at least
14 days old. They wait for now (owner, 2026-10-21); when they resume, agents
review them: an update lands on main once CI passes, with fixes for
whatever it breaks. Dependabot skips major versions
of the packages that set what UniAria supports: @types/node, React and its
types, TypeScript and Vitest. Those are raised by hand, only for a concrete
need. Adding a dependency is a different matter; see
Component workflow.