Contributing
This page covers working on Hololink UI itself. To use it in your app, see Installation.
Local setup
Section titled “Local setup”You need Node.js 22.12 or later (the repo pins Node 24, the current LTS, in .nvmrc) and pnpm 12.9.1 (set by packageManager in the root package.json).
git clone git@github.com:Philbob-SideProjects/holocron.gitcd holocronnvm use # or any other way of getting Node 24corepack enable # provides the pinned pnpmpnpm install --frozen-lockfilepnpm build:ui # the docs and Storybook need the built componentsThe workspace has two packages. packages/ui holds the components, and apps/docs holds this site.
| Script | What it does |
|---|---|
pnpm dev:docs |
Starts the docs site with hot reload |
pnpm storybook |
Starts Storybook on port 6006 |
pnpm build:ui |
Builds the components and regenerates custom-elements.json |
pnpm build:docs |
Builds the docs site |
pnpm build:site |
Builds the components, docs and Storybook into apps/docs/dist |
pnpm test |
Runs the unit and browser component tests (Vitest) |
pnpm typecheck |
Type-checks every package that has a typecheck script |
pnpm format |
Formats everything with Prettier (pnpm format:check only checks) |
pnpm lint:design |
Lints DESIGN.md |
pnpm changeset |
Writes a changeset |
The docs site imports the built output in packages/ui/dist. Re-run pnpm build:ui after you change a component, then reload the docs.
The browser tests need Playwright’s Chromium. If pnpm test reports a missing browser, run pnpm --filter @philbob-sideprojects/hololink-ui exec playwright install chromium.
Add a component
Section titled “Add a component”Read DESIGN.md first. It is the source of truth for tokens, accessibility and per-component styling.
-
Generate the files from
packages/ui:Terminal window cd packages/uipnpm generate holo-thingChoose the stylesheet and spec/E2E options you want. Rename or delete any test file the generator creates so that it matches the conventions in step 2.
-
Create these files in
packages/ui/src/components/holo-thing/:File Purpose holo-thing.tsxThe component. Tag prefix holo-,shadow: true.holo-thing.cssStyles, scoped by Shadow DOM. holo-thing.stories.tsStorybook stories covering every variant and state. holo-thing.cmp.test.tsxComponent tests (Vitest, @stencil/vitest).holo-thing.unit.test.tsPlain unit tests, if you have logic worth testing without the DOM. Look at
holo-badgefor a small, complete example. -
Follow the token rules in the CSS:
- Consume
--s-*semantic tokens only. Never use--p-*primitives or literal colours, pixel sizes, durations or shadows. - If no semantic token fits, add one to
packages/ui/src/styles/settings/tokens.cssand document it inDESIGN.md. - Expose styling hooks named
--holo-<component>-<prop>, each with a semantic fallback:var(--holo-thing-bg, var(--s-color-bg-surface)). - Do not style by theme. Remap tokens instead of using
[data-theme]orprefers-color-scheme.
- Consume
-
Document the API with JSDoc. Stencil reads the comments on
@Prop(),@Event(),@Method()and the class, and writes them tocustom-elements.json. Write each comment as a short sentence about what the member does and its allowed values./** The visual style of the badge. */@Prop() variant: 'neutral' | 'success' = 'neutral'; -
Add the package export in
packages/ui/package.json, matching the tag name:"./holo-thing": {"types": "./dist/components/holo-thing.d.ts","import": "./dist/components/holo-thing.js"} -
Add a docs page at
apps/docs/src/content/docs/components/thing.mdxwith live examples, a code block for each, and an API reference. Copy the structure of an existing page. Add the slug to the matching group inapps/docs/astro.config.mjsso it appears in the sidebar. The component status table picks up the new component automatically. -
Run
pnpm build:ui,pnpm test,pnpm typecheckandpnpm build:docs.
Accessibility checks
Section titled “Accessibility checks”Hololink UI targets WCAG 2.2 AA. Before you open a pull request:
- Use native elements (
<button>,<input>,<dialog>) and the Popover API where they fit, instead of rebuilding them fromdivand ARIA. - Forward labels and states through Shadow DOM:
aria-invalid,aria-describedby,aria-disabled. - Keep focus visible with the focus ring from
DESIGN.md. Never remove an outline without an equivalent replacement. - Open the component’s Storybook story and check the Accessibility panel (axe, WCAG 2.x A and AA rules) in both light and dark themes.
- Do a keyboard-only pass: Tab, Shift+Tab, Enter, Space, Esc and arrow keys where relevant.
- Respect
prefers-reduced-motion. - If you add a colour pairing, add it to
tokens.unit.test.tsso the contrast ratio is checked.
Write a changeset
Section titled “Write a changeset”Any change that affects the published package needs a changeset. Docs-only changes do not.
pnpm changesetChoose the bump type for @philbob-sideprojects/hololink-ui, then write the summary for a consumer: what changed, and what they must do. Commit the generated file in .changeset/ with your pull request. The package is below 1.0, so breaking changes go in a minor bump, and the summary must include migration steps.
The @hololink/docs app is private and is never versioned.
What CI runs
Section titled “What CI runs”Pull requests to main run the checks workflow (.github/workflows/_checks.yml):
| Job | Checks |
|---|---|
lint |
pnpm format:check (Prettier), pnpm lint:design, actionlint on the workflows, and changeset status against main (pull requests only) |
typecheck |
pnpm build:ui, then pnpm typecheck |
test |
pnpm test, with Playwright Chromium for the browser tests |
build |
pnpm build:site, then confirms the package tarball contains the manifest, loader, bundles, types and styles |
A pull request that touches the package without a changeset fails lint. Run pnpm format before you push to keep Prettier happy.
On pull requests, a preview job uploads a non-production Cloudflare Worker version and comments the preview URL. It is skipped for forks and Dependabot, and until the first production deploy exists.
How a release happens
Section titled “How a release happens”- A pull request with a changeset merges to
main. - The
on_mergeworkflow runs the samechecks, deploys the docs and Storybook to Cloudflare Workers, and runs a smoke test againsthttps://www.hololink-ui.co.uk. - The
releasejob opens or updates a pull request titledchore: version packages. It bumps the version and writes the entry inpackages/ui/CHANGELOG.md, which feeds the changelog. - Merging that pull request publishes
@philbob-sideprojects/hololink-uito GitHub Packages, tags the release and creates a GitHub release.
If a docs deploy breaks production, run the rollback workflow from the Actions tab. It returns the Worker to the previous version, or to a version ID you give it. It does not unpublish packages.