Kampa in this repository

What Kampa writes here

Every Kampa push is one commit on a new branch, opened as a pull request:

Path What it is
tokens/<mode>.json Your design tokens, one DTCG file per mode. The files to review.
tokens/tokens.css, tokens/tokens.ts, tokens/manifest.json Generated from the token files. Marked as generated in .gitattributes so GitHub collapses them in the diff.
tokens/components/ Component documents, if the project has components.
schemas/component/<version>/ The component format those documents follow, with a plain explanation.
.kampa/check.mjs The Kampa check. Plain Node.js, no dependencies.
.kampa/README.md This file.

Kampa never writes under .github/. It deletes a file only when its own last push listed it in tokens/manifest.json and the project no longer has it: a mode's tokens/<mode>.json after the mode is removed, or a component's two documents after the component is removed. The deletion is part of the pull request, so you review it like any other change. A file Kampa did not list is never deleted; if one sits in tokens/, the pull request names it, because .kampa/check.mjs checks it.

If your project stores tokens somewhere other than tokens/, read that path wherever this file says tokens/.

1. Connect

In Kampa, open the project's Settings, then GitHub, and click Connect GitHub. GitHub asks you to install the Kampa GitHub App and choose repositories. Pick this one.

To start a new project from the tokens already in this repository, use New project, then Connect, on Kampa's start screen. Pick this repository, its branch and the token folder, then click Read repository. Kampa shows what it found, or the one reason it can't use the folder, before it creates anything. It reads the folder a Kampa push writes, or a flat folder with one DTCG <mode>.json file per mode. The project starts from the commit Kampa read. If your files spell something differently from the way Kampa writes DTCG, for example a $type set on a group instead of on each token, the first pull request says what Kampa rewrote. A project started this way is set to Review first unless you pick otherwise, because the branch may be someone else's.

The app asks for:

  • Contents: Read and write, to commit the files above to a new branch.
  • Pull requests: Read and write, to open the pull request and, on Auto-merge, merge it.

It does not ask for the Workflows permission, so it cannot add or change anything under .github/workflows/.

To disconnect, remove the app in GitHub: your profile or organisation Settings, then Applications, then Installed GitHub Apps.

2. Pick a rail

The rail is chosen when the project is created and cannot change. To use the other one, create a new project.

  • Auto-merge (Rail 1). Kampa opens the pull request and merges it straight away, if Kampa's own check passed. Nobody reviews it first.
  • Review first (Rail 2). Kampa opens the pull request and stops. A person, usually your developer, reviews and merges it.

On both rails Kampa runs its check before it pushes. With Block push on check failure on (the default, under Settings, then Deploy), a failing check stops the push before any pull request exists. See section 5.

3. Add the workflow

The check runs in your CI only if this repository has the workflow file .github/workflows/kampa-check.yml. Kampa cannot add it for you.

After each push, the pull request description says whether the file is there. If it is missing, the description has an Add the workflow link that opens GitHub with the file already filled in: commit it to your default branch.

If your file is out of date, the description says so and gives two links, one commit each:

  1. Delete the old file. GitHub cannot fill in an edit to a file that already exists, so the old one goes first.
  2. Add the updated workflow. The same pre-filled link as above.

The status check name stays the same, so a rule that requires the check keeps working once the new file is in. If the check is required, the pull request that deletes the old file gets no check, because no file provides it; merge it as an admin or with a bypass. An older workflow that only ran when a token file changed is flagged the same way: as a required check, it made every other pull request wait forever.

The workflow runs on every pull request. When a pull request changes no token or component file, it passes in seconds and says there was nothing to check.

An older setup. If your workflow is .github/workflows/alloy-check.yml and runs .alloy/check.mjs, Kampa's two files are in .alloy/. Kampa keeps writing them there while any workflow needs .alloy/check.mjs, so your CI keeps working, and every pull request opens with the offer to replace the workflow with kampa-check.yml. Do it in two pull requests, in this order, merging each before the next Kampa push:

  1. Add kampa-check.yml. The new file runs .kampa/check.mjs when the repository has it and .alloy/check.mjs until then, so this pull request is green.
  2. Delete alloy-check.yml. kampa-check.yml still reports the check, so this pull request is green too.

GitHub can't do both in one commit from a link, so it takes two, and the order matters: deleted first, the old file takes the only check with it, and a rule that requires the check blocks that pull request. The first Kampa push after both are merged moves the two files to .kampa/ and deletes .alloy/ in the same commit; the new file runs .kampa/check.mjs from then on. A current workflow still named alloy-check.yml works indefinitely; rename it the same way, adding first, whenever you like.

The workflow adds one status check, WCAG 2.2 contrast + token lint, and leaves your other CI alone.

4. Protect main and require the Kampa check

Do this after the workflow has run at least once. GitHub only lets you require a check that has completed in this repository in the last seven days. Any pull request run counts, including the next Kampa push.

You need admin access to the repository. Use a ruleset (recommended) or a classic branch protection rule. Pick one.

Option A: ruleset (recommended)

  1. On the repository page, click Settings.
  2. In the left sidebar, open Rules and click Rulesets.
  3. Click New ruleset, then New branch ruleset.
  4. Under Ruleset name, type Protect main.
  5. Set Enforcement status to Active.
  6. Under Target branches, click Add target, then Include default branch.
  7. In the list of rules, check Restrict deletions and Block force pushes.
  8. Check Require a pull request before merging. On Review first, set Required approvals to at least 1.
  9. Check Require status checks to pass before merging. Under it, click Add checks, search for WCAG 2.2 contrast + token lint, and add it.
  10. Click Create.

On Auto-merge, also do this, or Kampa's own merges will stop working (see below): under Bypass list, click Add bypass, choose the Kampa GitHub App, and click Add selected.

Option B: classic branch protection rule

  1. On the repository page, click Settings.
  2. In the left sidebar, click Branches.
  3. Click Add classic branch protection rule.
  4. Under Branch name pattern, type main (or your default branch).
  5. Check Require a pull request before merging. On Review first, set the required approvals to at least 1.
  6. Check Require status checks to pass before merging. In the search field, type WCAG 2.2 contrast + token lint and select it.
  7. Check Do not allow bypassing the above settings.
  8. Click Create.

A classic rule cannot let the Kampa app skip the required check. On Auto-merge, use Option A.

On Review first, also turn on Automatically delete head branches in Settings, General, under Pull Requests, so each Kampa branch goes as soon as its pull request merges.

GitHub moves these screens around from time to time. If a label here does not match what you see, the GitHub docs on rulesets and protected branches have the current wording.

Why Auto-merge needs the bypass

On Auto-merge, Kampa merges its pull request a few seconds after opening it, before CI has reported. If main requires the Kampa check and the app is not on the bypass list, GitHub refuses that merge. Nothing is lost: Kampa shows merge blocked, the pull request stays open, and anyone can merge it once the check is green.

With the app on the bypass list, Kampa's pushes merge straight away. That is safe because Kampa ran the same check before it pushed. The required check then protects every other change to these files: hand edits, other tools, and AI agents.

On Review first, no bypass is needed. The check reports on the pull request and the reviewer merges when it is green.

5. What a blocked push means

A blocked push means Kampa ran its check, the check failed, and Kampa did not push. No branch, no commit, no pull request. Your repository is untouched.

The check fails when:

  • a colour pair fails contrast in any mode, measured against WCAG 2.2 at the project's level (AA: 4.5:1, AAA: 7:1);
  • a pair cannot be measured, for example an alias that points at nothing. It fails rather than being skipped;
  • a component document breaks its format, for example a slot that is not an alias. schemas/component/<version>/SEMANTICS.md lists every error.
  • Kampa can't write a value or a name into tokens.css or tokens.ts: text that would break out of its CSS declaration, a type Kampa can't write yet, or two tokens that become one CSS name. The check names each one (E009, E010), and these stop the push on both rails, even with Block push on check failure off.

For components, every combination of variant, state and theme is checked, not only the ones on screen.

6. Where to fix things

In Kampa. A blocked push shows a banner above the editor that counts the failing pairs. Show details lists every one: the mode, the measured ratio and the required one. Show in matrix jumps to a failing token pair. Show in components jumps to the failing component tile. Fix it, then push again. Kampa checks everything again before it pushes.

In CI. The WCAG 2.2 contrast + token lint check prints a summary in the job log, and a full JSON report. The pull request description has Kampa's own result at the time of the push.

Units. A dimension is px or rem, the two units DTCG defines. Letter spacing is written in the unit of its font size: 0.02em at 16px is stored as 0.32px. A dimension in any other unit, em included, fails the check as E006, with the token and mode named.

Fix the problem in Kampa and push again, rather than editing the files here. Kampa writes the token and component files from the project.

When the files here change. Kampa remembers the commit it last agreed with this repository on. When you open the project, and before every push, it checks whether the token folder changed since then. A change Kampa already has, such as its own pull request merged as it was, needs nothing from you. A change Kampa doesn't have, such as a hand edit, an amended pull request or another tool's commit, pauses pushing: Kampa counts the changed tokens, lists each one under Show details with a link to the change on GitHub, and offers Take the repo's version. That copies the repository's changes into the project in one step you can undo, after a confirm that says how many values in Kampa it replaces. When the same token changed in Kampa too, Resolve lists each one with Kampa's value and this repository's, and you pick which to keep, one token in one mode at a time. A token removed on one side and changed on the other is one pick for every mode: taking this repository's side brings it back in every mode, with this repository's value in each. A change only one side made applies on its own, nothing applies until every conflict is picked, and the result is one step you can undo. Keeping Kampa's value means the next push writes it here on purpose. Once Kampa has a commit it agreed with, it never pushes over a change it hasn't read. A project that has none yet, one last pushed before Kampa started recording it, is only told: Kampa names the differences, lets you push, and the pull request lists every value in this repository it replaced. That push records the commit, and every push after it is protected. If the folder can't be read as a project, for example a token that is in one mode file and not the others, Kampa names the problem and waits until it is fixed here. A push that would change nothing opens no pull request.

Running the check yourself. From the repository root:

node .kampa/check.mjs tokens/

It exits 0 on pass and 1 on fail. Add --level AAA to check at AAA. The workflow Kampa offers already runs at your project's level and on its token folder.