# Why roundel

> roundel against chalk, one capability per row, every cell linked to the test, grade or source that proves it.

Source: https://roundel.interlace.tools/docs/why-roundel

chalk styles a string, and does it well: `roundel/chalk` passes all 58 cases of chalk 6.0.0's
own suite, and roundel's colour-level rule agrees with chalk's across a differential sweep
except where this page says it does not. What chalk leaves to every package that uses it is the
decision around the colour — whether this output is going to a terminal, a CI log, a screen
reader or an agent, and what the colours mean. roundel makes that decision once, as a pure
function, and gives the colours names.

The table below is the whole comparison. Every mark links to its evidence: a test in this
repository for ours, and for chalk the source file of chalk 6.0.0 — the version compat-oracle
grades — or chalk's own test suite. `scripts/capabilities-lock.test.ts` fails the build when a
cited test no longer contains the title it is cited for.

✓ yes · ◐ partial (what is missing is said) · ✗ no · — does not apply. Every cell links to its evidence: our test or grade, or the incumbent’s source at the version compat-oracle grades.

### One output policy

| Capability | **roundel** | chalk |
| :-- | :-- | :-- |
| **Five output modes from one rule** — `outputMode()` answers `json`, `accessible`, `ci`, `pipe` or `tty` from the runtime it is given, first match wins, so a spinner, a prompt and the help ask one question and get one answer. | [✓](https://github.com/ofri-peretz/burgee/blob/main/packages/roundel/src/policy.test.ts) | [✗ decides a colour level and nothing else; whether to redraw is left to each package](https://cdn.jsdelivr.net/npm/chalk@6.0.0/source/vendor/supports-color/index.js) |
| **`NO_COLOR` turns colour off, over `FORCE_COLOR` and `--color`** — A user who sets `NO_COLOR` gets no colour, whatever else the environment or the command line asks for. | [✓](https://github.com/ofri-peretz/burgee/blob/main/packages/roundel/src/policy.test.ts) | [✗ its colour detection never reads NO_COLOR](https://cdn.jsdelivr.net/npm/chalk@6.0.0/source/vendor/supports-color/index.js) |
| **`FORCE_COLOR=0` is never overridden by a `--color` flag** — An explicit "colour off" in the environment wins over `--color=256` or `--color=16m` on the command line, so off never turns into on. | [✓](https://github.com/ofri-peretz/burgee/blob/main/packages/roundel/src/policy.test.ts) | [✓ an environment FORCE_COLOR replaces the flag's answer before any --color level is read](https://cdn.jsdelivr.net/npm/chalk@6.0.0/source/vendor/supports-color/index.js) |
| **`--color`, `--color=256` and `--no-color` flags** — The flags chalk reads are read under the same spellings, from the argv the program hands the policy. | [✓](https://github.com/ofri-peretz/burgee/blob/main/packages/roundel/src/policy.test.ts) | [✓ read from process.argv at import](https://cdn.jsdelivr.net/npm/chalk@6.0.0/source/vendor/supports-color/index.js) |
| **`--no-color` beats an ambient `FORCE_COLOR`** — A flag typed for this run outranks a `FORCE_COLOR` the machine exports, so `--no-color` under `FORCE_COLOR=3` is no colour. | [✓](https://github.com/ofri-peretz/burgee/blob/main/packages/roundel/src/policy.test.ts) | [✗ a set FORCE_COLOR replaces the flag's answer, so --no-color under FORCE_COLOR=3 is level 3](https://cdn.jsdelivr.net/npm/chalk@6.0.0/source/vendor/supports-color/index.js) |
| **An agent on a terminal is not someone to prompt** — `interactive()` answers no under `CLAUDECODE`, `CURSOR_AGENT` and the other agent variables even with a terminal on stdin, so a prompt asks the one question that tells an agent from a person. | [✓](https://github.com/ofri-peretz/burgee/blob/main/packages/roundel/src/terminal.test.ts) | [— decides a colour level, not whether a person can answer](https://cdn.jsdelivr.net/npm/chalk@6.0.0/source/vendor/supports-color/index.js) |
| **A screen-reader mode with no colour** — With `CLI_ACCESSIBLE=1` the level is 0 even on a truecolor terminal, because escape codes are noise to a screen reader; an explicit ask still colours it. | [✓](https://github.com/ofri-peretz/burgee/blob/main/packages/roundel/src/policy.test.ts) | [✗ reads no accessibility switch](https://cdn.jsdelivr.net/npm/chalk@6.0.0/source/vendor/supports-color/index.js) |
| **`--json` output is never coloured** — Under `--json` the level is 0 even when `FORCE_COLOR` asks for colour, so structured output never carries an escape. | [✓](https://github.com/ofri-peretz/burgee/blob/main/packages/roundel/src/policy.test.ts) | [✗ has no notion of structured output; the program sets chalk.level = 0 itself](https://cdn.jsdelivr.net/npm/chalk@6.0.0/source/index.js) |
| **The decision is a pure function of a runtime** — The policy reads only the environment, arguments and terminal flag it is handed, so a test or an agent harness asks the program's question and gets the program's answer. | [✓](https://github.com/ofri-peretz/burgee/blob/main/packages/roundel/src/policy.test.ts) | [✗ reads process.env, process.argv and the file descriptors' TTY state once, at import](https://cdn.jsdelivr.net/npm/chalk@6.0.0/source/vendor/supports-color/index.js) |

### Colour levels

| Capability | **roundel** | chalk |
| :-- | :-- | :-- |
| **Truecolor, 256 and 16 colours, downsampled** — A hex colour is sent as truecolor at level 3 and as the nearest of 256 or 16 colours below it. | [✓](https://github.com/ofri-peretz/burgee/blob/main/packages/roundel/src/chalk.test.ts) | [✓ through its vendored ansi-styles](https://cdn.jsdelivr.net/npm/chalk@6.0.0/source/index.js) |
| **A CI vendor's colour level, once colour is asked for** — GitHub Actions, Gitea Actions and CircleCI get truecolor and the other known vendors 16 colours, while a CI pipe nobody asked to colour stays plain. | [✓](https://github.com/ofri-peretz/burgee/blob/main/packages/roundel/src/policy.test.ts) | [✓ the same vendor table](https://cdn.jsdelivr.net/npm/chalk@6.0.0/source/vendor/supports-color/index.js) |

### Tokens and themes

| Capability | **roundel** | chalk |
| :-- | :-- | :-- |
| **Semantic tokens instead of colour names** — A program writes `error()`, `hint()` and `command()`, and the theme decides what each looks like, so the colours mean something and change together. | [✓](https://github.com/ofri-peretz/burgee/blob/main/packages/roundel/src/tokens.test.ts) | [✗ every colour is named at the call site](https://cdn.jsdelivr.net/npm/chalk@6.0.0/source/index.js) |
| **A theme checked for WCAG contrast** — `fly()` refuses a hex token below 4.5:1 against the declared background, at every colour level, and names the token and its ratio. | [✓](https://github.com/ofri-peretz/burgee/blob/main/packages/roundel/src/theme.test.ts) | [✗ draws any colour it is given](https://cdn.jsdelivr.net/npm/chalk@6.0.0/source/index.js) |
| **Themes through a validated plugin registry** — A plugin's tokens are validated at `register()`, a misspelt token is refused rather than dropped, and the contrast gate applies to them as to a hand-written theme. | [✓](https://github.com/ofri-peretz/burgee/blob/main/packages/roundel/src/plugin.test.ts) | [✗ has no themes to register](https://cdn.jsdelivr.net/npm/chalk@6.0.0/source/index.js) |

### Weight

| Capability | **roundel** | chalk |
| :-- | :-- | :-- |
| **No runtime dependencies** — Installing it adds one package and nothing else. | [✓](https://github.com/ofri-peretz/burgee/blob/main/packages/roundel/src/shape.test.ts) | [✓ ansi-styles and supports-color are copied inside it](https://cdn.jsdelivr.net/npm/chalk@6.0.0/package.json) |

### Compatibility

| Capability | **roundel** | chalk |
| :-- | :-- | :-- |
| **Passes chalk's own test suite** — `roundel/chalk` is graded by chalk 6.0.0's own tests, unedited, so changing the import keeps chalk's output. | [✓ 58 / 58 of its own tests](https://github.com/ofri-peretz/burgee/blob/main/packages/compat-oracle/baseline/chalk.json) | [✓ its own suite, the control run](https://github.com/ofri-peretz/burgee/blob/main/packages/compat-oracle/vendor/chalk/test/chalk.js) |

## Reading it

- **Parity rows are here on purpose.** chalk reads the `--color` flags, keeps an explicit
  `FORCE_COLOR=0` off, downsamples to 256 and 16 colours, knows the CI vendors and has no
  dependencies. A reader would otherwise have to go and check; the cells say they match.
- **chalk's cells link to chalk 6.0.0 on jsDelivr**, not to the copy installed in this
  repository, which is 6.0.1: the lock only accepts an installed file at the graded version.
  A URL cell is not re-read by the lock, so each was checked against that file by hand; the
  compatibility row, whose source is chalk's vendored suite, is checked on every run.

## What is not in the table

A row goes in only when every cell of it can be proved. These were left out:

- **`npx roundel check`.** It validates a theme plugin before it ships; chalk has no plugins,
  so there is nothing to compare it with. It is on [Theme plugins](/docs/guides/plugins).
- **The per-façade level.** Setting `chalk.level` on `roundel/chalk` changes that façade and not
  the tokens. chalk also has separate instances (`new Chalk({ level })`), so the difference is
  in what the default instance is shared with, not a yes-or-no capability.
- **Terminal allow-list detection.** chalk recognises kitty, ghostty, wezterm, iTerm, Apple
  Terminal, TeamCity and the Windows build number; roundel deliberately does not, and reads only
  `TERM`, `COLORTERM` and explicit instructions ([Colour levels](/docs/guides/colour-levels)).
  That is a difference in approach, and the row would read as a loss or a win depending on the
  terminal.
- **Weight in bytes.** The per-subpath figures are asserted by
  [`weight.test.ts`](https://github.com/ofri-peretz/burgee/blob/main/packages/roundel/src/weight.test.ts)
  and published on [Benchmarks](https://burgee.interlace.tools/docs/benchmarks).
