|
|
---
|
|
|
title: Recipe Contract — the transversal contract of every eidos recipe
|
|
|
type: canon
|
|
|
audience: human + agent
|
|
|
authority: canonical — which theming systems every recipe MUST consume, and under which names
|
|
|
status: current
|
|
|
enforcement: scripts/component-audit.ts (rules R-4.x, all `error`)
|
|
|
source: migrated from src/uix/eidos/RECIPE_CONTRACT.md (2026-07-02, docs-book F7.3)
|
|
|
related:
|
|
|
tsc: docs/canon/tsc.md (where each token is emitted)
|
|
|
theming: docs/theming/reference.md (the system; §5 sizes · §6 naming)
|
|
|
guide: docs/theming/guide.md (how to add a component, step by step)
|
|
|
---
|
|
|
|
|
|
# Recipe Contract
|
|
|
|
|
|
> The TSC decides **where** each token is emitted. This contract decides
|
|
|
> **what** every recipe consumes: the theming's transversal systems
|
|
|
> (state-layer, focus, elevation, typography, opacity, motion, spacing,
|
|
|
> touch, lists, z) and the vocabulary of its dimensional tokens. A recipe
|
|
|
> deviating without an annotated exception is out of contract — the
|
|
|
> `component-audit` (R-4.x) flags it.
|
|
|
>
|
|
|
> **Why it exists**: the 2026-07-01 audit (`fable-eidos-audit.md`) measured
|
|
|
> that canons defended by a mechanical guard drift ~0% while canons defended
|
|
|
> by prose drift 30–85% (state-layer 21/135, partial depth-channel adoption,
|
|
|
> 49 keyframes outside the channel). This document is the canon; the R-4.x
|
|
|
> rules are its executable form.
|
|
|
|
|
|
---
|
|
|
|
|
|
## 1. A recipe's skeleton — dimensional tokens
|
|
|
|
|
|
Every recipe declares its knobs in `lib/recipes/base.ts` with this
|
|
|
vocabulary. **The names are canon**, just like the color slots are in
|
|
|
THEMING §6:
|
|
|
|
|
|
| Concept | Canonical name | Forbidden |
|
|
|
| ----------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
|
| Control height per size | `control-height-{size}` (or `{part}-height-{size}` when the part is not the control) | inventing a third name per component |
|
|
|
| Padding per axis | `padding-inline[-{size}]` / `padding-block[-{size}]` | **`padding-x` / `padding-y`** AND the abbreviated segments **`px` / `py`** (physical axes) |
|
|
|
| Margin per axis | `margin-inline[-{size}]` / `margin-block[-{size}]` | `margin-x/-y`, `mx` / `my` |
|
|
|
| Internal separation | `gap[-{size}]` | — |
|
|
|
| Radius | `radius[-{size}]` → `var(--radius-*)` | px that equal a step of the scale |
|
|
|
| Control font | `font-size-{size}` = `var(--font-size-{size})` (**1:1**, THEMING §5) | literal px/rem (guarded in `recipe-css-contract`) |
|
|
|
| Icon | `icon-size-{size}` = `var(--icon-size-{size})` | hand px; size from `--control-height-*` unless the element IS a control |
|
|
|
| Field label | one typographic step below the input (THEMING §5) | — |
|
|
|
| Ink (foreground) | `[{part}-]fg` | **`color` as a slot** — `trigger-color`, `-color-hover` (D-TH.6, 2026-08-20) |
|
|
|
| Interactive state | `{modifier}-{part-}{slot}` — the modifier IN FRONT (`hover-bg`, `disabled-trigger-fg`) | the modifier behind (`bg-hover`, `trigger-color-active`) |
|
|
|
|
|
|
**Where the modifier goes — one sentence: _in front what is interactive,
|
|
|
behind what is dimensional or contextual_** (D-TH.6, signed 2026-08-20).
|
|
|
`hover-bg` and `disabled-trigger-fg`, but `control-height-md`,
|
|
|
`gap-vertical`, `indicator-bg-below` and `width-icon`: a size, an
|
|
|
orientation, a semantic range or a collapsed mode are not states of the
|
|
|
control, and promoting them rewrites what the name MEANS (`--sidebar-width-icon`,
|
|
|
the width of the icon-collapsed rail, is not `--sidebar-icon-width`, the width
|
|
|
of an icon). The interactive vocabulary is closed: `hover · active · selected ·
|
|
|
disabled · checked · open · focus · invalid · current`.
|
|
|
|
|
|
Two families keep a grammar of their own and R-5.3 knows it:
|
|
|
|
|
|
- **role / palette** — `{part-}?{role|palette}-{slot}` where the slot comes from
|
|
|
`COLOR_ROLE_SLOTS`, which carries the modifier BEHIND by construction
|
|
|
(`primary-solid-hover`, `palette-hover`). 447 keys; they are canon, not drift.
|
|
|
- **system** — `--state-*`, `--opacity-*`, `--focus-ring-*` are consumed, never
|
|
|
minted by a recipe (§2), so this grammar does not reach them. A recipe key
|
|
|
that MIRRORS the system family keeps its spelling: `trigger-focus-ring-color`
|
|
|
is the signed exception, along with colour as a NOUN (`orb-color-*`) and
|
|
|
`stop-color-*`, where `stop-color` is a part name.
|
|
|
|
|
|
Inherited naming rules (THEMING §6): public `--{c}-{slot}`, private
|
|
|
`--_{c}-{slot}`, no `color-` segment, no kebab abbreviation, no
|
|
|
`--eidos-/-soma-/-air-`.
|
|
|
|
|
|
> **Normalización ejecutada (2026-08-20, D-TH.6 — «un idioma, todo el
|
|
|
> ecosistema», segunda parte).** El catálogo hablaba dos idiomas para la tinta:
|
|
|
> 196 claves `-color` contra 55 `fg`, y el modificador caía a los dos lados
|
|
|
> (`hover-bg` ×13 contra `bg-hover` ×22). El origen era una CONTRADICCIÓN entre
|
|
|
> dos doctrinas firmadas: THEMING §6.7 r7 dice `fg`; el principio de plataforma
|
|
|
> de la normalización px/py («el token se llama como la propiedad») dice
|
|
|
> `color`. El autor adjudicó `fg` acotando el principio: gobierna los ejes
|
|
|
> DIMENSIONALES, no la pareja `bg`/`fg` — si la gobernara, `bg` tendría que
|
|
|
> llamarse `background-color`. Codemod value-preserving: **269 claves + 793
|
|
|
> referencias en 101 ficheros**, diff de `generated/` = renombres 1:1 con cero
|
|
|
> cambios de valor, censo de alcance idéntico (162 · 5.203 · 1.841 · 37 %),
|
|
|
> computed idéntico en navegador (6.467 valores, 23 estados, 3 componentes).
|
|
|
> **No entraron**: las 447 role-slot (canónicas), las 13 exentas firmadas, y
|
|
|
> **47 hovers neutros que MIGRAN a la capa de estado** (§38 + R-4.3) en vez de
|
|
|
> renombrarse — renombrar lo condenado es churn. El muro de tipos de
|
|
|
> `defineRecipes` cierra la gramática cuando esa migración termine.
|
|
|
|
|
|
**Value-reference rules (2026-07-07, theming audit B — both guarded in
|
|
|
`recipe-css-contract.test.ts`):**
|
|
|
|
|
|
- A recipe token VALUE may reference only vocabulary that **exists** in the
|
|
|
emitted contract — a no-fallback `var(--x)` pointing at a name the
|
|
|
generators never emit is a _phantom_ (it freezes the token
|
|
|
guaranteed-invalid; `--color-content-tertiary` shipped a month that way).
|
|
|
`var(--x, fallback)` is runtime-optional by construction.
|
|
|
- A value referencing a component **private** (`--_{c}-*`, declared in the
|
|
|
component's CSS) must declare a **scope that covers it** (`host` / leaf) —
|
|
|
never `root`: a `:root`-emitted token computes where the private does not
|
|
|
exist and freezes invalid down the whole tree (the calendar holiday/event
|
|
|
marks and the field segment heights shipped broken exactly this way).
|
|
|
|
|
|
> **Normalización ejecutada (2026-07-06, decisión usuario: "un idioma, todo
|
|
|
> el ecosistema").** El catálogo hablaba dos idiomas: 32 claves canónicas
|
|
|
> conviviendo con **198 claves abreviadas** `p[xy]` (+3 `my`) que evadían el
|
|
|
> regex de R-4.4 — hábito Tailwind/Chakra sin decisión que lo respaldara. El
|
|
|
> nombre canónico es el de la PLATAFORMA (CSS Logical Properties): el token
|
|
|
> se llama como la propiedad que alimenta (nombre = contrato, sin capa de
|
|
|
> traducción — a diferencia de Panda, donde `px` es azúcar que un compilador
|
|
|
> resuelve), es direccional-agnóstico, y en un contrato de tokens `px` además
|
|
|
> es un pun con la unidad. Codemod atómico value-preserving: 198+3 claves +
|
|
|
> ~570 nombres en **57 ficheros** (tracks excluidos words/palabras/chronos
|
|
|
> INCLUIDOS por decisión explícita) + los **20 shorthands físicos**
|
|
|
> (`padding: var(--py) var(--px)`) reescritos a longhands lógicos (el único
|
|
|
> subconjunto con semántica física real — modos de escritura verticales).
|
|
|
> Triple muro: R-4.4 con el regex ampliado a segmentos abreviados (error,
|
|
|
> sin allowlist — la deuda murió en el mismo pass) · **muro de tipos**
|
|
|
> `defineRecipes` (`lib/recipes/define.ts`): una clave de eje físico no
|
|
|
> compila · la suite/regen verifican (diff de generated = 230 renombres 1:1;
|
|
|
> computed padding idéntico en navegador).
|
|
|
|
|
|
**Typographic primitives are measured against the STYLE LAYER, not against
|
|
|
their own tokens** (D-TH.2, signed 2026-08-20). `heading`, `text`, `s-text`,
|
|
|
`code`, `display` and `label` resolve every typographic axis through
|
|
|
`--style-{name}-*`, selected by `data-style` or bound wholesale to one style:
|
|
|
that layer IS their theming surface, and it is already public and live. Minting
|
|
|
`--heading-*` to mirror it would be one alias per axis × level — the class of
|
|
|
alias the changelog §39 purge killed. The census therefore counts `--style-*`
|
|
|
as a transversal `system` for those six (their `--_{c}-*` privates are the
|
|
|
per-INSTANCE escape hatch the wrapper writes from a prop, not a theming knob).
|
|
|
A component that merely READS a named style for one axis — a menu label
|
|
|
reading `--style-label-font-family` — is not a primitive and is not in the set.
|
|
|
|
|
|
## 2. Mandatory transversal systems
|
|
|
|
|
|
The table IS the contract: **concept → canonical mechanism → the rule that
|
|
|
guards it**. A recipe reimplements none of these concepts on its own.
|
|
|
|
|
|
| Concept | Canonical mechanism | How it is consumed | Rule |
|
|
|
| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
|
|
|
| **Neutral hover / press / selected** | the state-layer (rules in `archetypes.css`; magnitudes are config data `primitives.state` — changelog §40) | transparent background → `background: var(--state-hover)`; filled background → the overlay `background-image: linear-gradient(var(--state-hover), var(--state-hover))` | R-4.3 |
|
|
|
| **Valenced hover** (solid / soft per color) | the recipe's palette swap | `background: var(--{c}-solid-hover)` etc. — tokens, never hand-rolled `color-mix(currentColor …)` | R-4.3 |
|
|
|
| **Elevation** | the scale + the depth channel | `box-shadow: var(--shadow-*)` or compose `var(--depth-{plane}-shadow), var(--depth-{plane}-halo)`; never literal shadows | R-4.1 |
|
|
|
| **Crisp inner ring** | the inset-ring (THEMING §29) | `box-shadow: inset 0 0 0 var(--ring-inset-width) var(--ring-inset-color, …)` — the expression lives at the point of use | R-4.1 (passes: it carries `var()`) |
|
|
|
| **Focus** | per archetype (THEMING §32) | fields → the two-ring `--focus-ring-*` (box-shadow); surfaces/controls → their own `outline` (HCM-safe); declaring nothing falls back to `archetypes.css` | R-1.5 |
|
|
|
| **Disabled** | the opacity token | `opacity: var(--opacity-disabled)` + `cursor: not-allowed` — never hand `0.4`/`0.5`/`0.6` | R-4.2 |
|
|
|
| **Typography** | 1:1 with the scale | see §1; canonical exceptions: avatar/marker (glyph ∝ diameter), accordion (prose scale) | R-2.7 + guard |
|
|
|
| **Motion — state transitions** | tokens | `transition: X var(--duration-*) var(--ease-*)` | THEMING §35 block C |
|
|
|
| **Motion — event signatures** | `EidosConfig.motion.{keyframes,signatures}` (generated, themeable) | NO local `@keyframes` for perceptual reactions; a local `@keyframes` only when **functional** (spin/shimmer/continuous period) and annotated | R-4.5 |
|
|
|
| **Spacing** | `var(--space-*)` | density × scaling arrive composed from the foundation — **never** multiply by `--density-*`/`--scaling` in the recipe | R-2.3 |
|
|
|
| **Touch target** | archetype (`trigger`/`close`/`action`) + list-surface | free via `@media (pointer: coarse)` in `archetypes.css` — don't declare your own 44px minimums | — |
|
|
|
| **List/menu rows** | the list-surface (`--list-item-*`) | list surfaces bridge `--list-item-height`/`-py`, not their own row heights | — |
|
|
|
| **Floating z-index** | the `--z-index-overlay-*` band | each PORTALED overlay consumes its rung; raw integers `0..5` only for intra-component order | guard in contracts.test |
|
|
|
| **Viewport-fixed chrome** (not portaled) | the `--z-index-*` ladder, `affix` rung | a consent strip / FAB / floating bar is page chrome, not an overlay: it rides `affix` (150) — above `sticky`, below every menu. Two elements in the SAME band tie, and DOM order breaks the tie (measured: a notice correctly first in the source lost to a sticky header, 30.7px of overlap) | THEME-SYS-1 + `layer:check` |
|
|
|
| **Color** | role slots / recipe tokens | `var(--color-{role}-{slot})` or `var(--{c}-*)`; never direct `--scale-*`/`--primitive-*` nor hex/rgb | R-2.1/2.6, R-4.6 |
|
|
|
|
|
|
## 3. Exceptions — how they are declared
|
|
|
|
|
|
A deviation is valid **only** when annotated on the same line. The audit
|
|
|
honors annotations; an unannotated deviation is drift.
|
|
|
|
|
|
| Annotation | When | Example |
|
|
|
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
|
| `/* literal: <reason> */` | a physically-fixed value or a justified optical tuning | `font-size: 13px; /* literal: tight icon affordance */` |
|
|
|
| `/* functional: <reason> */` | a local `@keyframes` that is NOT a perceptual signature (continuous period, machinery) | `/* functional: continuous spin period, not an event signature */` `@keyframes spin { … }` |
|
|
|
| physically-fixed | colors that must not follow the theme (the QR's white, the natural clock's skies) — hex allowed WITH a comment | `--_qr-bg: #ffffff; /* literal: QR quiet zone must be true white */` |
|
|
|
| `/* important: <reason> */` | an `!important` that must win EVERY cascade fight — the three legitimate shapes: bypassing a soma inline style, cross-recipe fusion (the group's radius flattening), reduced-motion kills | `cursor: nwse-resize !important; /* important: wins over the header's inherited grab cursor */` |
|
|
|
|
|
|
What does **not** pass as an exception: "it's faster this way", "the token
|
|
|
didn't exist" (create it), "it's just a hover" (that is exactly the
|
|
|
contract's case).
|
|
|
|
|
|
## 4. Enforcement
|
|
|
|
|
|
| Rule | Guards | Severity |
|
|
|
| ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- |
|
|
|
| R-4.1 | `box-shadow` without `var(` (elevation/ring outside tokens) | error |
|
|
|
| R-4.2 | literal `opacity: 0<N<1` outside `@keyframes` | error |
|
|
|
| R-4.3 | `background*` in `:hover` rules without a token (`var(`) or with manual `color-mix(… currentColor …)` | error |
|
|
|
| R-4.4 | recipe tokens with physical axes `padding-x/-y`, `margin-x/-y` | error |
|
|
|
| R-4.5 | a local `@keyframes` without a `/* functional: … */` annotation | error |
|
|
|
| R-4.6 | direct `var(--scale-*)` / `var(--primitive-*)` in component CSS | error |
|
|
|
| R-4.7 | `!important` without a same-line `/* important: <reason> */` annotation (THM-5, 2026-07-11) | error |
|
|
|
| R-5.3 | recipe token keys outside the naming grammar of §1 — `color` as a slot, or an interactive modifier behind (D-TH.6, 2026-08-20) | error |
|
|
|
| R-5.4 | a public token that moves no computed value in the live demo and carries no written adjudication (`npm run theming:sentinel -- <c> <url>`, 2026-08-21) | error |
|
|
|
|
|
|
**R-5.3 is a grammar guard, not a list guard** — unlike
|
|
|
`eidos-event-vocabulary`, which checks membership of a closed vocabulary, this
|
|
|
one checks a SHAPE. It consumes the classifier of
|
|
|
`theming-census --names`, the same source the codemod ran on: two
|
|
|
implementations of one grammar are two grammars that end up disagreeing. It
|
|
|
went to `error` directly, with no `warn` ramp, because the codemod emptied its
|
|
|
debt in the same pass (the R-4.4 precedent). Keys queued for the state-layer
|
|
|
migration are reported apart — they are not naming debt, they are knobs about
|
|
|
to disappear. Muta-prueba de tres caras (it is in `scripts/__names-mutatest.ts`
|
|
|
and it earned its keep: it caught the classifier promoting ANY morfo-declared
|
|
|
value to the front, which turned `--sidebar-width-icon` into
|
|
|
`--sidebar-icon-width`): `-bg-hover` with a valenced value → red ·
|
|
|
`trigger-color` → red · `primary-solid-hover` → **green**.
|
|
|
|
|
|
**R-5.4 is a REACH guard, and it needs a browser** — like `morfo-check`, it
|
|
|
runs against a live dev server (`scripts/theming-sentinel.ts`), because what
|
|
|
it checks no static analysis can see: for every public token it writes an
|
|
|
unmistakable value and demands that some node's computed style follow it. It
|
|
|
was born from two findings of the F2-A adversarial review (2026-08-21) that
|
|
|
only this instrument caught — `gradient-picker` re-declared its trigger's
|
|
|
entire chrome under a `popover.css` rule that always wins (36/45 tokens dead),
|
|
|
and `carousel` minted an `item-gap` trio against a soma INLINE style that no
|
|
|
cascade can beat. A silent token is either retired or ADJUDICATED: the ledger
|
|
|
(`scripts/theming-sentinel-exceptions.ts`) carries one written reason per
|
|
|
token — part not mounted by the demo, state that needs forcing, pseudo-element
|
|
|
or composed-component limit — and the guard fails on any dead token without an
|
|
|
entry. A ledger entry whose token moves again is reported as STALE (prune it;
|
|
|
same debt as a stale allowlist row — warning, not failure, because the
|
|
|
instrument keeps a residual nondeterminism the probe's history documents).
|
|
|
|
|
|
**The guard grew six capabilities during F2-B (2026-08-22), each born from a
|
|
|
measurement and each re-verified against every component that already had
|
|
|
ledger entries — zero regressions after any of them.** They are worth knowing
|
|
|
because each names a way the instrument used to LIE:
|
|
|
|
|
|
- **The pointer is parked after the blur.** `reopen()` runs before EVERY token
|
|
|
and clicks the component open, so `blur()` alone left the cursor on the node:
|
|
|
`:hover` matched for the whole run and every hover rule out-ranked its
|
|
|
resting neighbours. Cost three false negatives on `textarea` alone.
|
|
|
- **`::placeholder` IS readable** through `getComputedStyle`. The note claiming
|
|
|
otherwise was never checked; it had already cost `command.input-placeholder-fg`
|
|
|
a hand-verified ledger entry, now pruned as STALE.
|
|
|
- **`COMPONENT_OVERRIDES`** — per-component attribute prefix and opening
|
|
|
selector, for components whose DOM does not follow `data-{component}-{part}`.
|
|
|
`picker-shell` names its parts generically ON PURPOSE (`data-picker-header`)
|
|
|
so every picker shares one visual contract, and that convention blinded every
|
|
|
filter: the guard reported 0/31.
|
|
|
- **`openBy: 'hover'`** for hover cards, whose trigger is an `<a>`: clicking it
|
|
|
navigates instead of revealing the portaled panel.
|
|
|
- **`text-decoration-thickness` / `-underline-offset`** joined the property
|
|
|
list, which had `text-decoration-color` but not its siblings.
|
|
|
|
|
|
The lesson that generalizes: **a probe or guard that inspects few nodes passes
|
|
|
in false**. Count what the instrument actually sees before trusting a green
|
|
|
gate — `picker-shell` measured ZERO nodes and reported success.
|
|
|
|
|
|
**All R-4.x are `error`** (graduated 2026-07-02: the five mechanical ones
|
|
|
after the backfill; R-4.5 after the motion migration emptied its 15
|
|
|
components). The doctrine the migration left — **how a recipe consumes the
|
|
|
motion channel**:
|
|
|
|
|
|
1. **Preset stamp** (`data-animation-style` via the `motion` prop) when the
|
|
|
element has no semantic `data-state` of its own and no conditional
|
|
|
trigger: overlays (popover, menus, select, combobox, tooltip via the
|
|
|
`delayed-open` alias), content mounts (`motionAttrs`).
|
|
|
2. **Event signature** (`EidosConfig.motion.signatures`) when the animation
|
|
|
reacts to sema's `data-event-*` and is generic per verb/family
|
|
|
(expand/collapse, present/dismiss, announce per intent, the `shift`
|
|
|
crossing per `data-event-direction`).
|
|
|
3. **Materials pattern** (the rule lives in the recipe but consumes ONLY
|
|
|
registered keyframes + `--motion-*` hooks) when the trigger is
|
|
|
irreducible to the generic: a semantic `data-state` of its own (card),
|
|
|
container-flag gating (timeline `data-live`), per-component axes (tabs
|
|
|
`data-motion` × orientation), soma's directional machinery
|
|
|
(navigation-menu from/to), or the per-component materialization of a
|
|
|
signal (metrics `value-flash` per intent). The reason is documented in
|
|
|
the rule.
|
|
|
|
|
|
Sibling guards already active: R-2.1/2.5/2.6 (color), R-2.7 (literal
|
|
|
typography), `recipe-css-contract.test.ts` (consumed tokens + TSC +
|
|
|
non-literal font/icon-size + variants vs type unions + **no phantom public
|
|
|
refs** — every no-fallback `var()` in a recipe value must exist in the
|
|
|
derived contract + **privates need a covering scope** — `--_*` refs never
|
|
|
from `root`, both 2026-07-07), the raw-z guard in `contracts.test.ts`.
|
|
|
|
|
|
The WIP tracks `words` / `palabras` / `chronos` are excluded from R-4.x
|
|
|
(same as the typographic guard) until they leave their parallel track.
|
|
|
|
|
|
## 5. Authoring checklist (before calling a recipe done)
|
|
|
|
|
|
1. Dimensional tokens with §1's names (logical axes, canonical heights).
|
|
|
2. Neutral hover = state-layer; valenced hover = palette tokens.
|
|
|
3. Zero literal `box-shadow` — everything through
|
|
|
`--shadow-*`/`--depth-*`/inset-ring.
|
|
|
4. Focus per archetype; not reinvented.
|
|
|
5. `opacity: var(--opacity-disabled)` on disabled.
|
|
|
6. `font-size`/`icon-size` 1:1 with the scale (or a documented canonical
|
|
|
exception).
|
|
|
7. Transitions with `--duration-*`/`--ease-*`; no perceptual-signature
|
|
|
`@keyframes`.
|
|
|
8. Spacing with `--space-*`, no hand-multiplied density.
|
|
|
9. If it's a list/menu → list-surface; if it PORTALS → a `--z-index-overlay-*`
|
|
|
rung; if it is viewport-fixed page chrome → the `affix` rung, via the
|
|
|
shared layer rather than a private copy of the geometry.
|
|
|
10. Every deviation carries its annotation (§3).
|
|
|
11. `node --import tsx/esm scripts/component-audit.ts --only {kebab}` with no
|
|
|
new R-4.x.
|