Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
127 commits
Select commit Hold shift + click to select a range
d520f5e
Design System: New Fluent theme (dxdsfluent) powered by design tokens
EugeniyKiyashko Jul 27, 2026
5e717d4
Design system: fix inconsistencies after design review and add etalon…
EugeniyKiyashko Jul 31, 2026
6454b26
Design System: use current screenshots as etalons of dxdsfluent (#34585)
pharret31 Jul 31, 2026
6f10947
Design System: fix tests
pharret31 Jul 31, 2026
41ab0ba
Design System: naming convention standart
EugeniyKiyashko Aug 5, 2026
a7303e8
Design system: get rid of use ds tokens of component's level
EugeniyKiyashko Aug 5, 2026
f1bd006
Design system: update etalons after package bump to 262.6.0
EugeniyKiyashko Aug 6, 2026
1940a8f
Design System: Rename theme from dxdsfluent to fluent-next (#34677)
dmlvr Aug 6, 2026
c675c16
Design System: finish the fluent-next naming migration and rename bas…
EugeniyKiyashko Aug 12, 2026
eda86d9
Fluent design tokens pipeline (#34701)
Raushen Aug 12, 2026
ab76fa2
Design system: change highlight color after designer review (#34744)
EugeniyKiyashko Aug 12, 2026
31edfb2
Design System: New Fluent theme (dxdsfluent) powered by design tokens…
dmlvr Aug 18, 2026
b7c6916
Design System: apply the clear-styles cleanup to fluent-next
EugeniyKiyashko Aug 18, 2026
de957d7
Design System: drop the release-branch CI push triggers
EugeniyKiyashko Aug 18, 2026
bd74476
Fluent-next: delivery artifacts to distribution and support accents (…
Raushen Aug 24, 2026
ef35da3
Fluent-next: sync the theme with the fluent fixes that landed after t…
EugeniyKiyashko Aug 25, 2026
58589e5
Design System: move fluent-next onto design tokens 262.10.1 (#34916)
EugeniyKiyashko Aug 26, 2026
f97ea9d
Fluent-next: extract css variables of components level (#34888)
EugeniyKiyashko Aug 26, 2026
3e783be
Fluent-next: reduce runtime calc() with direct token steps and tier-p…
EugeniyKiyashko Aug 27, 2026
1e62bc8
Fluent-next: unify focus rings on one role and restore their contrast…
EugeniyKiyashko Aug 27, 2026
395434f
Fluent-next: gate the compiled bundles against runtime calc() growth …
EugeniyKiyashko Aug 27, 2026
0bc2afa
Fluent-next: fix demo and remove unused variable (#34947)
EugeniyKiyashko Aug 27, 2026
c5620bf
Fluent next: add px markers (#34950)
EugeniyKiyashko Aug 27, 2026
bc7f41c
Fluent-next: symplify aliases (#34976)
EugeniyKiyashko Aug 28, 2026
eda40a8
Fluent-next: extend the component tier to every component that owns v…
EugeniyKiyashko Aug 31, 2026
a400e2d
Fluent-next: publish the type scale into the system tier (#34983)
EugeniyKiyashko Aug 31, 2026
4c93a48
Fluent-next: move onto design tokens 262.15.0
pharret31 Aug 31, 2026
d3e8a48
Fluent-next: change colors in Viz palettes (#34981)
Raushen Sep 1, 2026
edf3750
Update demo with new palette (#34984)
Raushen Sep 1, 2026
cef6ef4
Point the Scheduler demo at a colour role that still exists
EugeniyKiyashko Sep 1, 2026
9fbf165
Design System: open up the fixed sizes in the shared base layer (#34996)
pharret31 Sep 3, 2026
6d84e74
Fluent-next: mirror the other-month hovered cell color from fluent
EugeniyKiyashko Sep 3, 2026
6e70cc2
Demos: move the Scheduler/ContextMenu appointment title color out of …
EugeniyKiyashko Sep 3, 2026
c7d2014
Fluent-next: paint the disabled state from the roles instead of opaci…
EugeniyKiyashko Sep 4, 2026
b3bfd73
Fluent-next: re-shoot the cardView column chooser drag etalons (#35057)
EugeniyKiyashko Sep 5, 2026
344f461
Fluent-next: run naming:check in CI
EugeniyKiyashko Sep 5, 2026
38ee436
Fluent-next: use the token font and the dark surface color in viz the…
EugeniyKiyashko Sep 7, 2026
877e89c
Fluent-next: paint the single-colour icons with a mask instead of a b…
EugeniyKiyashko Sep 7, 2026
c013a56
Fluent-next: bring the component tier generator back (#35068)
EugeniyKiyashko Sep 7, 2026
f243b47
Fluent-next: sync with main and bring the theme into line with the ne…
EugeniyKiyashko Sep 7, 2026
e917a89
Fluent-next: gate the fallback policy of the tier (runtime-contract.j…
EugeniyKiyashko Sep 8, 2026
d82a0d7
Fluent-next: move onto design tokens 262.16.0 (#35085)
EugeniyKiyashko Sep 8, 2026
ebfbcad
Fluent-next: re-shoot the two T1152498 scrolling etalons that were ca…
EugeniyKiyashko Sep 8, 2026
dfde100
Fluent-next: ship both colour modes in every bundle, selectable by cl…
EugeniyKiyashko Sep 11, 2026
9acec32
Custom Accent support (#35096)
Raushen Sep 14, 2026
215591a
Fluent-next: refactor scales exceptions (#35102)
pharret31 Sep 14, 2026
3b49584
Fluent-next: bump design-tokens package to 262.23.0 (#35205)
pharret31 Sep 15, 2026
d32641e
Fluent-next: guards for the public tier and the token bump (#35218)
pharret31 Sep 16, 2026
c911995
Fluent-next: CardView sizes stop repeating themselves between default…
EugeniyKiyashko Sep 17, 2026
83acac6
Fluent-next: validate role assignment across all components (#35107)
EugeniyKiyashko Sep 17, 2026
1d67490
Fluent-next: get shadows from the design tokens shadow scale (#35245)
pharret31 Sep 18, 2026
4ecad03
Fluent-next: update pnpm-lock.yaml
pharret31 Sep 18, 2026
a7a0f69
Fluent-next: add linter checks and fix its issues (#35276)
pharret31 Sep 21, 2026
370c8f0
Fluent-next: bump design tokens package 262.23.0 -> 262.25.0
pharret31 Sep 21, 2026
a81c818
Fluent-next: update changed etalons
pharret31 Sep 21, 2026
b32aae0
Fluent-Next: customAccentColor method (#35230)
Raushen Sep 22, 2026
64dc54d
Fluent-next: sync with main and extend the dead-declaration cleanup t…
EugeniyKiyashko Sep 23, 2026
d60d80a
Fluent-next: sync with main up to the module-variable cleanup (#35315)
EugeniyKiyashko Sep 23, 2026
fa46e89
Fluent-next: paint the data-uri icons from the mode scope instead of …
EugeniyKiyashko Sep 23, 2026
d3af1ff
Fluent-next: cover the drag and resize area for a popup in a theme mo…
EugeniyKiyashko Sep 23, 2026
c2edcd6
Fluent-next: remove unnecessary comments (#35316)
pharret31 Sep 23, 2026
11f0036
Fluent-next: a gate for parameters a theme sets and then paints over …
EugeniyKiyashko Sep 23, 2026
33a1160
Fluent-next: viz tokens (#35275)
Raushen Sep 24, 2026
2e8ad80
Fluent-next: the runtime takes its sizes from the theme (#35253)
EugeniyKiyashko Sep 24, 2026
a89df4c
Fluent-next: mark and role-map the typography rungs the px gate can't…
pharret31 Sep 24, 2026
d67b703
Bump design tokens package (#35346)
Raushen Sep 25, 2026
e576797
Fluent-next: viz tokens leftovers (#35341)
Raushen Sep 25, 2026
989113e
Fluent-next: merge main up to dc81680270 into the feature branch
EugeniyKiyashko Sep 25, 2026
667dd97
Fluent-next: re-shoot the etalons Chrome 153 changes and add the AI-P…
EugeniyKiyashko Sep 25, 2026
6a1aeb6
Fluent-next: re-shoot three sticky-column etalons from a run where th…
EugeniyKiyashko Sep 25, 2026
35be4ef
Fluent-next: drop the demo-scope jobs main removed in #35020 and run …
EugeniyKiyashko Sep 26, 2026
39d41c2
Fluent-next: merge main up to 294986486a into the feature branch
EugeniyKiyashko Sep 27, 2026
0ad4520
Fluent-next: finalize the public --dx-* surface before release (#35343)
dmlvr Sep 28, 2026
8e70b23
Fluent-next: merge main up to dab3f3a0 into the feature branch
pharret31 Sep 28, 2026
a4cdb97
Fluent-next: update etalon for new DataGrid-AIPoweredDetailView demo
pharret31 Sep 28, 2026
04c4248
Fluent-next: fix base-theme regressions and the !default lint rule (#…
pharret31 Sep 30, 2026
6e0a7ef
Fluent-next: merge main up to 52ad6817 into the feature branch
pharret31 Sep 30, 2026
424b132
Fluent-next: fix pnpm-lock.yaml file and lint issues
pharret31 Sep 30, 2026
03d8e82
Fluent-next: design leftovers (#35410)
Raushen Sep 30, 2026
0c6250c
Fluent-next: merge feature branch into the PR branch
pharret31 Sep 30, 2026
055896a
Fluent-next: design leftovers - update text's color styles for custom…
dmlvr Sep 30, 2026
5d5b29e
Fluent-next: ButtonGroup border from border-subtler (#35518)
Raushen Oct 7, 2026
c5900d2
Fluent-next: merge main up to 05e9aabd9c into the feature branch
EugeniyKiyashko Oct 8, 2026
e6ed4c8
Fluent-next: type the merge seams in m_svg, the viz tooltip and the v…
EugeniyKiyashko Oct 8, 2026
a2d5a05
Fluent-next: wrap the lines of the icon-font metadata test to the scs…
EugeniyKiyashko Oct 8, 2026
f801c99
Fluent-next: sort the palette imports
EugeniyKiyashko Oct 8, 2026
9d314e6
Fluent-next: declare the web header offsets as an interface
EugeniyKiyashko Oct 8, 2026
3699023
Fluent-next: final infrastructure+code review (#35460)
dmlvr Oct 8, 2026
f5b1aa5
Fluent-next: merge main up to f73407085f into the feature branch (#35…
EugeniyKiyashko Oct 9, 2026
10e3fc4
Fluent-next: record main f73407085f as the second parent of the merge…
EugeniyKiyashko Oct 9, 2026
090da06
Themes: replace the deprecated Sass if() function (no CSS change for …
EugeniyKiyashko Oct 9, 2026
3f3e2a6
Fluent-next: record main 029645927c as the second parent of the merge…
EugeniyKiyashko Oct 10, 2026
4aa7079
Themes: drop the type checks left after the Sass if() replacement (#3…
EugeniyKiyashko Oct 12, 2026
f7d08d3
Themes: write the zero lengths as 0px and replace calc-sum / calc-dif…
EugeniyKiyashko Oct 12, 2026
cb90cf9
Fluent-next: bring back the ResolveTimeConflicts test, drop the stale…
EugeniyKiyashko Oct 12, 2026
99f6b68
Fluent-next: re-shoot the ResolveTimeConflicts recurrence-group etalo…
EugeniyKiyashko Oct 12, 2026
dbcc8e1
Themes: replace the false switches with null defaults and pass the le…
EugeniyKiyashko Oct 12, 2026
eebd23a
Fluent-next: merge main up to 6195241843 into the feature branch (#35…
EugeniyKiyashko Oct 12, 2026
fa6f07a
Fluent-next: record main 6195241843 as the second parent of the merge…
EugeniyKiyashko Oct 12, 2026
9c168b1
Fluent-next: merge main up to 99f76c17b5 into the feature branch (#35…
EugeniyKiyashko Oct 12, 2026
04a5197
Fluent-next: record main 99f76c17b5 as the second parent of the merge…
EugeniyKiyashko Oct 12, 2026
5eef6b7
Themes: drop the base type checks and paint the grid text stub with a…
EugeniyKiyashko Oct 12, 2026
54f932b
Fluent-next: answer the AI chat from the openai mock's case table (#3…
EugeniyKiyashko Oct 12, 2026
00f04a2
Fluent-next demos: replace color-mix with transparent by the relative…
EugeniyKiyashko Oct 12, 2026
d8eddc3
Fluent-next: align the scss gates and tooling with main (#35638)
EugeniyKiyashko Oct 12, 2026
8ef3635
Fluent-next: write the theme list in TypeScript and trim the scss ESL…
EugeniyKiyashko Oct 12, 2026
31206b5
Fluent-next: merge main up to 928dbe8d92 into the feature branch
EugeniyKiyashko Oct 12, 2026
7bee4e5
Fluent-next: merge the feature tip 8ef36352e9 into the main sync
EugeniyKiyashko Oct 12, 2026
1e7e551
Fluent-next: group the public surface baseline by theme, as in main (…
EugeniyKiyashko Oct 12, 2026
c78a753
Fluent-next: merge main up to 149efaab4c into the feature branch
EugeniyKiyashko Oct 13, 2026
b421bea
Fluent-next: drop the a11y exclusions that also weakened the legacy t…
EugeniyKiyashko Oct 13, 2026
6e2f7c8
Fluent-next: remove px-audit.mjs (#35656)
pharret31 Oct 13, 2026
d51a4be
Fluent-next: merge main up to 92b27d8288 into the feature branch
EugeniyKiyashko Oct 13, 2026
3da4b9b
Fluent-next: remove roles checks (#35666)
pharret31 Oct 13, 2026
2722e69
Fluent-next: remove name-truth.mjs (#35665)
pharret31 Oct 13, 2026
a5c1a3e
Fluent-next: align the stylelint config with main, shorten the overri…
EugeniyKiyashko Oct 13, 2026
a0e458c
Fluent-next: pass the theme values to the base parameters instead of …
EugeniyKiyashko Oct 13, 2026
a10053b
Fluent-next: bring over the main @use, lint and colorView cleanups, k…
EugeniyKiyashko Oct 13, 2026
9be3111
Fluent-next: merge main up to c8175e1501 into the feature branch
EugeniyKiyashko Oct 13, 2026
e39a83d
Fluent-next: drop the base comments main does not have
EugeniyKiyashko Oct 13, 2026
406f09e
Fluent-next: remove comments and unused tests (#35677)
pharret31 Oct 13, 2026
029bac8
Fluent-next: drop the two cardView star imports from the naming basel…
EugeniyKiyashko Oct 13, 2026
40bea5f
Fluent-next: merge main up to 17ef400b77 into the feature branch
EugeniyKiyashko Oct 14, 2026
d1be1da
Fluent-next: type the scss tooling with the package tsconfig the test…
EugeniyKiyashko Oct 14, 2026
4f961dc
Fluent-next: move mjs to ts, speed up tests (#35698)
pharret31 Oct 15, 2026
7abbe25
Fluent-next: merge main up to d8aed98a into the feature branch
pharret31 Oct 15, 2026
2ee5c46
Fluent-next: merge main up to 0546c05e into the feature branch
pharret31 Oct 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
77 changes: 77 additions & 0 deletions .claude/skills/fluent-next-tier/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
name: fluent-next-tier
description: How the fluent-next public `--dx-*` component tier is generated and guarded in packages/devextreme-scss — derive-registries, publish, reachability and tokens:update. Use whenever work touches `registries.json`, `OVERRIDES`/`rootSelectors`, a generated `_public.scss` / `_public-tier.scss`, `_public-links.scss`, `nested-scopes.json`, `unread-tier.json`, a renamed or new fluent-next SCSS variable, a retired `--dx-*` name or `public-renames.json`, a design-token package bump (`@devexpress/design-tokens-internal`, Renovate), or when CI fails in "Check naming registries" / `naming:check` with "stale", "has no rootSelectors", "scope outside the roots", "portal", "declared and never read", "cross-scope duplicate" or "read and never declared" — even if the user names only the symptom.
---

# fluent-next component tier

fluent-next publishes its SCSS variables as public custom properties (`--dx-button-bg`, …). They are a
contract from the first release: a published name cannot be withdrawn or renamed silently. Four
scripts in `packages/devextreme-scss` produce and guard that layer. Run them from that package.

## The pipeline

```
token package + theme folders + OVERRIDES
│ tools/naming/derive-registries.ts
▼
tools/naming/registries.json (generated, committed — never edit by hand)
│ tools/naming/publish.ts (rules in tools/naming/tier.ts)
▼
fluent-next/<folder>/_public.scss (generated, committed, header "// GENERATED by …")
fluent-next/_public-tier.scss (generated collector: which root selectors carry which tier)
fluent-next/<folder>/_public-links.scss (hand-written relations, included by the generated file)
│ nx build:themes
▼
devextreme/artifacts/css/dx.fluent-next.blue.light.css
│ tools/review/reachability.ts
▼
does every --dx-* actually reach the elements that read it?
```

`tokens:update` sits beside this: it bumps the token package and reports what moved.

## Commands

| Command | What it does | Details |
|---|---|---|
| `pnpm run naming:registries` | regenerate `registries.json` (needs built themes) | [derive-registries.md](derive-registries.md) |
| `pnpm run naming:publish` | regenerate every `_public.scss` and `_public-tier.scss` | [publish.md](publish.md) |
| `node tools/review/reachability.ts [--report] [--update-scopes] [--update-unread]` | check the built bundle | [reachability.md](reachability.md) |
| `pnpm run tokens:update [<version>] [--report]` | bump the token package and report | [tokens-update.md](tokens-update.md) |
| `pnpm run naming:check` | what CI runs: `derive-registries --check` → `publish --check` → `reachability` | — |
| `pnpm run naming:baseline` | rewrite `tests/fluent-next-naming.baseline.json` | — |

## In CI

`.github/workflows/styles.yml` runs, in one job, `nx test devextreme-scss` and then
`nx naming:check devextreme-scss` (step "Check naming registries"). `naming:check` depends on
`build:themes`, so reachability always sees a fresh bundle there. Locally, rebuild the themes first
(`pnpm nx build:themes devextreme-scss`) — reachability silently skips when no bundle exists.

## A failure, and where to look

| Message | Read |
|---|---|
| `registries.json is stale` | [derive-registries.md](derive-registries.md) |
| `STATES and PARTS overlap…` / `sub-elements of X collide with a state name` | [derive-registries.md](derive-registries.md) |
| `rootSelectors[x]: "…" does not occur in the built bundle` | [derive-registries.md](derive-registries.md) |
| `… is stale … run pnpm naming:publish` | [publish.md](publish.md) |
| `X publishes from folder/ but has no rootSelectors` | [publish.md](publish.md) |
| `read and never declared`, `cross-scope duplicate`, `scope outside the roots`, `portal:`, `declared and never read`, `pinned name(s) are read now` | [reachability.md](reachability.md) |
| a token bump turned something red | [tokens-update.md](tokens-update.md) |

## Rules that hold everywhere

- Generated files (`registries.json`, `_public.scss`, `_public-tier.scss`) are only ever changed by
re-running their script. A hand edit is caught by `--check` and lost on the next run.
- Judgment calls live in `OVERRIDES` inside `derive-registries.ts` and are reviewed as code;
everything else in `registries.json` is derived.
- Renaming a fluent-next SCSS variable retires its public `--dx-*` name. Record the old name in
`tools/naming/public-renames.json` in the same PR; `tests/public-renames.test.ts` stops it from
coming back. Only names that shipped in a release are journaled — see the journal section of
[tokens-update.md](tokens-update.md) for when an entry is due and how to write it.
- `tests/fluent-next-naming.test.ts` reads the same rules as `publish.ts` (both import `tier.ts`):
the tier composition, links, the collector and the name grammar for new variables. Its baseline
sections fail on any change; after a deliberate one run `pnpm run naming:baseline` and review the
diff.
68 changes: 68 additions & 0 deletions .claude/skills/fluent-next-tier/derive-registries.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# derive-registries.ts

`tools/naming/derive-registries.ts` writes `tools/naming/registries.json`, the single source of truth
for the fluent-next naming standard.

```
node tools/naming/derive-registries.ts # writes registries.json (pnpm run naming:registries)
node tools/naming/derive-registries.ts --check # fails if the committed file is stale
```

## Where the data comes from

- **The token package** (`@devexpress/design-tokens-internal/tokens.flat.json`, the
`components/core/theme/fluent` tokens). From it come the colour `parts` (the last word of every
colour token: `bg`, `border`, `content`, `icon`…). The vocabulary is read from the package's flat
index, not from generated output, so the script needs no build for this part and cannot drift
from the package.
- **The theme folders** in `scss/widgets/fluent-next/`: each folder becomes a component, its name in
kebab case (`dateBox` → `date-box`) unless `OVERRIDES.components` says otherwise.
- **`OVERRIDES`** at the top of the script — every judgment call, reviewed as code, with the reason
next to each entry: folder ↔ component exceptions (`gridBase` → `grid`), `systemFolders` /
`systemTier`, `declarationHome`, `chassis` (who renders the grid / menu chassis), `embeds`,
`rootSelectors`, `migrated`, `sassOnly`, and the grammar vocabularies (`states`, `modifiers`,
`sizeSlots`, `subElements`, `systemConcerns`, `themeIdentity`, `partExclude` / `partsAdd` /
`partsRemove`).

`rootSelectors` defaults to `.dx-<component without dashes>` for every migrated and system-tier
component; an override replaces it, for example when a widget paints outside its own element (a
dialog rendered into `body`, a toolbar menu inside a popup). Add the reason beside the entry.

## The name grammar it describes

```
$<component>(-<sub-element>)*(-<modifier>)*-<slot>(-<state>)
```

Parsed right to left with longest match: the trailing state, then the slot (a colour `part` in
`_colors.scss`, a `sizeSlot` in `_sizes.scss`), then the middle words. Overlaps between vocabularies
that compete for different positions are fine (`text` is both a part and a modifier); two are fatal
and the script refuses them:

- a state that is also a part;
- a sub-element of a component that is also a state.

## Who reads which field

| Fields | Read by |
|---|---|
| `components`, `declarationHome`, `systemTier`, `rootSelectors`, `exemptFolders`, `migrated`, `sassOnly` | `tools/naming/tier.ts` → `publish.ts` (the generator) |
| `rootSelectors` | `tools/review/reachability.ts` |
| `systemFolders` | `tests/disabled-paint.test.ts` |
| the grammar vocabularies, `chassis`, `embeds`, `systemConcerns`, `themeIdentity` | `tests/fluent-next-naming.test.ts` |

## Failures

- **`registries.json is stale — run node tools/naming/derive-registries.ts`** — the committed file
differs from what the script builds now: `OVERRIDES` changed, a theme folder was added or removed,
or a token package bump changed the colour `parts`. Regenerate with `pnpm run naming:registries` (build the themes
first, see below) and review the JSON diff.
- **`STATES and PARTS overlap, the grammar would be undecidable`** / **`sub-elements of X collide with
a state name`** — an `OVERRIDES` edit (or a new part from the package) made a word ambiguous. Pick
another word or remove it from `partsAdd` / add it to `partExclude`.
- **`rootSelectors for non-migrated components: …`** — a `rootSelectors` override for a component that
is neither in `migrated` nor in `systemTier`.
- **`rootSelectors[x]: "…" does not occur in the built bundle`** / **`is not a single .dx-* class or
:root`** — a typo in a root selector, or the themes were not rebuilt. Regenerating needs the built
bundle at `packages/devextreme/artifacts/css/dx.fluent-next.blue.light.css`; `--check` skips this
gate when the bundle is absent (it only compares bytes).
50 changes: 50 additions & 0 deletions .claude/skills/fluent-next-tier/publish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# publish.ts

`tools/naming/publish.ts` regenerates the fluent-next component tier:

- every `fluent-next/<folder>/_public.scss` — the `--dx-*` projections of the component's eligible
variables, in alphabetical order;
- the collector `fluent-next/_public-tier.scss` — which root selectors (`registries.rootSelectors`)
carry which component's tier.

```
node tools/naming/publish.ts # regenerates the tier files (pnpm run naming:publish)
node tools/naming/publish.ts --check # exit 1 when a committed file is stale or a rule is broken
```

It needs no built bundle. The rules live in `tools/naming/tier.ts`, which
`tests/fluent-next-naming.test.ts` reads too, so the emitter and the test can never disagree.

## What is generated and what is not

- `_public.scss` and `_public-tier.scss` carry `// GENERATED by tools/naming/publish.ts`. Never edit
them; change the SCSS variable (or `registries.json` via `OVERRIDES`) and re-run.
- `<folder>/_public-links.scss` is hand-written and never touched by the script; the generated file
includes it. A relation between two names goes there as a link, the whole value being
`--dx-a: var(--dx-b);`, so it stays live when `--dx-b` is overridden. The naming test checks that a
link points at a name declared by the same component or published on `:root`, keeps the state
(`-hover` → `-hover`), and that an alias reaching another component's root is published as a value,
not a link.
- Not every variable is eligible: `sassOnly` entries, values a custom property cannot carry (CSS-wide
keywords, `null`, data-uri images and what they feed), and base-layer wiring are excluded.

## Renames retire public names

The published name is derived from the SCSS variable, so renaming a variable retires a public
`--dx-*` name, and the diff looks like plain regeneration. Record the retirement in
`tools/naming/public-renames.json` in the same pull request; `tests/public-renames.test.ts` keeps a
retired name from reappearing, as an alias or otherwise. Whether an entry is due (only names that
shipped) and how to write it: the journal section of [tokens-update.md](tokens-update.md).

## Failures

- **`fluent-next/<folder>/_public.scss is stale` … `run pnpm naming:publish`** — a declaration file
changed and the tier was not regenerated, or a generated file was edited by hand. Run
`pnpm run naming:publish` and commit the result.
- **`X publishes from folder/ but has no rootSelectors — add it to derive-registries.ts
OVERRIDES`** — only the components in `migrated` and `systemTier` publish, and the generator gives
each of them a root, so this means `registries.json` is stale or was edited by hand. Make the change
in `OVERRIDES` (add `rootSelectors` there if the default `.dx-<component>` is wrong), then
`pnpm run naming:registries` and `pnpm run naming:publish`.
- The naming test reports `not published — run pnpm naming:publish` or `the variable is excluded from
the tier` for the same causes.
62 changes: 62 additions & 0 deletions .claude/skills/fluent-next-tier/reachability.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# reachability.ts

`tools/review/reachability.ts` checks, on the **built** fluent-next bundle, that every `--dx-*` name
reaches the elements that read it. A custom property exists only on the element that declares it
and its descendants; a `var()` that finds nothing makes the browser drop the whole declaration, with
no compile error, no lint error and nothing in the console.

```
node tools/review/reachability.ts # the gate (last step of pnpm run naming:check)
node tools/review/reachability.ts --report # also list every read outside the root text
node tools/review/reachability.ts --update-scopes # rewrite nested-scopes.json from what it sees now
node tools/review/reachability.ts --update-unread # rewrite the names list in unread-tier.json
```

It reads `packages/devextreme/artifacts/css/dx.fluent-next.blue.light.css`; without that file it
prints a note and exits 0, so rebuild the themes (`pnpm nx build:themes devextreme-scss`) before
trusting a local green. Inputs besides the bundle: `registries.rootSelectors`, its two lists
`tools/review/nested-scopes.json` and `tools/review/unread-tier.json`, and the runtime contracts
`tools/naming/runtime-reads.json` (lengths JS reads back with `getComputedStyle`) and
`tools/naming/viz-contract.json` (names the charts write into SVG).

## The five checks — each one fails the run

1. **`read and never declared`** — a `var(--dx-foo)` with no `--dx-foo` declared anywhere: a typo or
a removed name. Fix the name or publish it on the scope that reads it. Names set at runtime by
another layer (CardView card sizes, the scheduler animation, the accent colour) are listed in
`SET_ELSEWHERE` in the script; add one there only when another layer really sets it.
2. **`cross-scope duplicate`** — the same selector and property are declared more than once and the
copies read variables of different components. The last copy wins everywhere, and inside the other
widget its variable is empty, so the property vanishes. In the legacy themes such duplicates were
harmless (equal literals); the tier splits them across roots. Real case: base/dataGrid and
base/treeList drew the same unscoped `.dx-command-ai-header-button .dx-button.dx-state-focused` and
the AI column button lost its focus ring in DataGrid. Fix: read a shared-layer name
(`gridBase`/`menuBase`, e.g. `$grid-outline-focused`), not one widget's.
3. **`scope outside the roots of X`** — a rule reads `--dx-X-…` but its selector text contains no root
of X. Usually harmless (the element is nested in the widget and the selector is just short), but
if JS mounts that element outside the widget — a portal: popup, drag preview, sortable clone,
dialog in `body` — the variable is missing there. The text cannot tell the two apart, so this is a
prompt to check: open the widget in that state and confirm in the DOM that the element sits inside
the root. Then either add the scope to `nested-scopes.json["X"]`, or, if it is a portal, add its
class to `OVERRIDES.rootSelectors` in `derive-registries.ts` (the tier is then declared on it too)
or read a Sass twin / `:root` value in that branch. `--update-scopes` writes every current scope in
one go — use it only after the checks, never as a way to turn CI green.
4. **`portal: .<scope> is another widget's overlay`** — a scope named after another widget's overlay
(`-popup`, `-popup-wrapper`, `-overlay`, `-overlay-wrapper`, `-overlay-content`, `-popover`,
`-popover-wrapper`, `-tooltip`, `-tooltip-wrapper`) is a portal by construction and cannot be
nested. Fires whether or not the scope is listed, and `--update-scopes` refuses to list it. This
is how the diagram toolbar's overflow menu was found: its scope had been listed by a bulk update and
the icons had lost their margins.
5. **`declared and never read`** / **`pinned name(s) are read now`** — liveness: a name is live when an
ordinary declaration reads it, a `@container style(--dx-…)` query names it, it is in the runtime or
viz contract, or a live name reads it (computed to a fixpoint). The dead set is pinned in
`unread-tier.json` by exact equality. A **new** dead name is public API that moves nothing — usually
a rule switched from `var(--dx-x)` to `$x` or a literal, or the generator published a Sass helper.
Read it where it belongs (or stop publishing it before release); after release, a published knob
must keep working. A name that **revived** just needs `--update-unread`. The list counts only reads
inside the bundle: about half of it is read by demos and application CSS by design.

## Known gap

A name that is read by a declaration that never wins the cascade (overwritten in the same rule, or
beaten by a more specific selector) passes. Deciding that needs the cascade, not the text.
Loading
Loading