Skip to content

UN-4299 [FEAT] v1 design tokens, themes and style isolation (P0-T04) - #2399

Open
praveen-formido wants to merge 39 commits into
mainfrom
UN-4299-design-tokens-themes
Open

praveen-formido wants to merge 39 commits into
mainfrom
UN-4299-design-tokens-themes

Conversation

@praveen-formido

@praveen-formido praveen-formido commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

What

  • Gives v1 its own design tokens in three modes (Light, Dark, Unstract): the values live in src/v1/styles/tokens.json (one array per family, board order), styles/tokens.ts types them and derives each CSS variable from the token name, and bun run tokens:v1 generates src/v1/styles/tokens.css, scoped to .v1[data-theme="…"]. Values come from the Figma "Midnight Bloom" variable collection and the dev-spec board (node 683:135); the table the P1-T01 gallery will render is renderTokenTable(TOKENS) and its snapshot is committed.
  • Gives v1 its own Tailwind 4 root, src/v1/styles/index.css, compiled with prefix(ui) and source(none) + @source "../": every v1 utility is ui:…, every theme variable --ui-…, only v1 files are scanned, and preflight is not imported a second time. Theme keys are bound to the runtime tokens through @theme inline; the stock colour, radius, shadow, text and font scales are reset so only token-backed utilities exist. dark: and unstract: variants key on data-theme; one focus treatment, ui:focus-ring, implements the board's rule.
  • Adds the theme model and store (lib/theme.ts, stores/theme.ts, preference key unstract.v1.theme, follow-system; each mode block also sets the browser's color-scheme, so native controls and scrollbars follow data-theme), a portal container #v1-portal.v1 for dialogs, menus and toasts (lib/portal.ts), cn() configured for the prefix (lib/cn.ts), and ThemeProvider (app/theme/ThemeProvider.tsx), which renders the v1 root (data-ui-version="v1", the marker the E2E harness in UN-4345 [FEAT] Frontend v1 E2E harness and the frontend-v1-e2e group #2355 expects), hydrates the stored mode before first paint and keeps the portal in step.
  • Adds scripts/check-v1-styles.mjs to lint:v1: raw colours (hex, functions, palette and named, in code and CSS), raw lengths (anywhere inside an arbitrary value, and px/rem/em/pt/vh/vw in CSS), inline style, !important, CSS imports outside app/ and styles/, selectors outside .v1 (a sibling combinator right after the root included, at top level or nested directly in the root; .v1 .menu { & + x } stays inside), prefers-color-scheme in v1 CSS, and utilities without ui: first (hover:ui:flex compiles to nothing) all fail lint. className values and the class positions of cn()/cva() are always checked (in cva() the base string, variant values and a compound class; a variant name or choice such as defaultVariants: { variant: "outline" } is not a class); other strings when they look like a class list. Twenty-nine guardrail fixtures in test-fixtures/v1-boundary/ pin each rule, three passing fixtures pin what the rules must allow. README gains a Styling section.
  • Self-hosted fonts through the @fontsource packages v0 already pins: Inter 400/500/600 (the only weights the type roles use) and Geist Mono 400.

Why

  • P0-T04 (UN-4299, spec): v1 needs its token set in three modes, self-hosted fonts, and styles that cannot reach v0 or be reached by v0, with lint that enforces the styling policy. It gates all of P1 and P2-T01.
  • Two Tailwind roots in one document would otherwise emit the same unprefixed class (.text-sm) with different theme values and overwrite each other in the shared utilities layer. A prefix makes the two sets disjoint by construction; v1 also survives v0's deletion in P9.

How

Decision Why
Prefix is ui, not v1 Tailwind 4.3 accepts lowercase letters only as a prefix; v1 is rejected at compile time. Raised in #engg-unstract-frontend-v1-migration on 28 Oct.
No second preflight A second copy later in the base layer would re-run border: 0 solid and turn v0's borders black. v1 ships scoped base rules in base.css; P9 adds preflight when src/index.css goes.
Runtime tokens keep shadcn names (--background, --radius-md), scoped under .v1[data-theme] "Bound to the Midnight Bloom variables"; shadcn components expect these names; every token is redefined in every mode so nothing inherits from v0's :root/.dark. Tailwind's own variables live in the separate --ui- namespace.
Three @source not lines in v0's src/index.css v0's root auto-scans the whole frontend. Without them the v1 tree, the lint script and the fixtures added 12 unprefixed utilities and 8 theme variables to v0's stylesheet. With them the v0 build is byte-identical to the base (239/239 files). This is the one edit to a v0 file in the PR; it is a build-scope directive, not a token or style change. Please confirm this is acceptable under the seam rule.
skipLibCheck: true in src/v1/tsconfig.json vitest 3.2 and @types/chai ^5.2 both declare containSubset; with library checks on, the first v1 test file fails TS2300, and no @types/chai version in vitest's accepted range avoids it. Turn back off once vitest no longer clashes. Scaffold change; Jaseem and Harini, please note.
Biome formatter disabled for the generated tokens.css The file is written by the generator and checked byte-for-byte by a test; biome ci src/ would otherwise rewrap it.
Lint string rules apply to class-shaped strings only Scanning every literal flagged variant="outline", type="hidden" and UI copy; shape detection still catches a constant holding unprefixed classes.
S1 allows a style attribute that sets only CSS custom properties (style={{ "--page-aspect": w / h }}) Runtime geometry (page size, highlight position in P1-T21) cannot be a token or a class. The numbers travel through variables that ui: classes read (ui:aspect-(--page-aspect)); the classes keep every visual decision. A string, a variable, a spread or a plain key still fails, so the keys stay visible at the call site. lib/css-custom-properties.d.ts augments React's CSSProperties with --* keys, which strict TS otherwise rejects (TS2353); the passing fixture is typechecked to exercise it. Raised by Ali on 29 Oct.
ThemeProvider uses layout effects With plain effects a saved dark or Unstract preference painted one light frame on every load.
Spacing utilities bound to the tokens: --spacing is --space-1, named steps 1 to 8 are their tokens Without it ui:p-5 used Tailwind's stock scale and --space-5 drove nothing (Greptile). The bare multiplier stays so ui:w-64 and ui:size-4 keep working; ui:p-5 is now var(--space-5).
Token values in tokens.json, not in the TypeScript source SonarCloud's duplication check ignores literal values, so 75 same-shaped entries in tokens.ts counted as copies (16.8% of new lines against a 3% gate). JSON is not checked for duplication, and the file is the data anyway; cssVar was -- + name for every entry, so it is derived. Ritwik's preferred option.
tokens:v1 runs under bun The generator imports the TypeScript sources; Node needs a registerHooks resolver for that, which exists only from 22.15 while the engines field allows 20.19 (Greptile). bun imports them directly.

Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)

  • No. Nothing in v0 imports @v1 yet, so none of the new code or CSS is in the production bundle: the v0 build output is byte-identical to the base commit (239 files, SHA-256 compared; see claudedocs/frontend-v1-lane-c/p0-t04/no-regression.md in the lane folder). The v0 vitest count is unchanged (629).
  • The only v0 file touched is src/index.css, and only to stop scanning three non-v0 folders; its output is identical.
  • When P0-T03 later mounts v1 through @v1, the v1 stylesheet joins the bundle. Measured delta on a scratch build: 363 added rules, 0 removed; every added rule is .v1- or ui\:-scoped, a --ui-/--tw- variable, an @font-face identical to one v0 already declares, or a @layer statement, plus one duplicate block of Tailwind's --tw-* defaults with values identical to v0's. No v0 rule changes.

Database Migrations

  • None

Env Config

  • None

Relevant Docs

Related Issues or PRs

Dependencies Versions

  • @tailwindcss/node 4.3.3 added as a dev dependency (same version as @tailwindcss/vite), used only by the root compile test.

Notes on Testing

  • .sonarcloud.properties excludes frontend/test-fixtures/** from analysis: the boundary fixtures break the v1 rules on purpose.
  • v1: 39 vitest tests across 8 files (theme model, store, portal, tokens, renderer, cn, root compile, provider) plus 89 guardrail fixture rows (bun run test:v1, 29 of them for the styling policy). typecheck:v1, lint:v1 and biome ci src/ clean (Biome's 798 warnings are v0's, unchanged).
  • The root compile test compiles index.css with @tailwindcss/node and asserts: only ui:-prefixed utilities, every emitted selector scoped (checked per comma-separated part, with an in-test negative proof), variants keyed on .v1[data-theme], the focus ring's forced-colors outline, no !important, no prefers-color-scheme, no stock palette or scales, and every custom property either a --ui-/--tw- variable or a declared token.
  • Token values: all 75 tokens checked against the captured board values in all three modes.
  • cn() extends tailwind-merge's text scale with the eight type roles, so ui:text-body ui:text-foreground keeps both (tailwind-merge would otherwise read the role as a colour and drop it); a test ties that list to the type family in TOKENS.
  • Known v0 rules that still reach v1 (unlayered in src/index.css, so no layered v1 rule can beat them, and v1 lint bans !important): the global scrollbar colours, the Radix popper z-index: 1500 !important, the sonner toaster rule, and the svg.lucide sizing rule (its :not(.flex, .inline-flex, .grid) does not exempt ui:inline-flex). All harmless today; listed in the v1 README and in the evidence file so nobody is surprised.
  • Two spec acceptance items are carried over, because they need code this task does not contain: screenshots of three v0 pages before and after v1 loads (needs P0-T03 to mount v1 and a running stack; owner: Lane C at P0-T03's go-live) and "a v1 dialog over a v1 page styled by v1 tokens" (needs the first P1 dialog; P1-T01's gallery will render it against #v1-portal). The byte-identical build and the classified CSS delta stand in for the first today.

🤖 Generated with Claude Code

jaseemjaskp and others added 30 commits October 26, 2026 17:51
…N-4270)

Adds frontend/src/v1/ as an isolated tree for the v1 frontend (P0-T01):

- Folder taxonomy from the spec, a public entry (src/v1/index.ts, imported
  by v0 as `@v1`) and a cloud overlay placeholder.
- src/v1/tsconfig.json: strict, no JS, unused locals/params are errors,
  `@v1` alias only. v0's tsconfig/jsconfig exclude src/v1.
- Biome overrides for the two-way import boundary and v1 bans (no `../`,
  `@/`, prop-types, antd, component CSS; noExplicitAny).
- scripts/check-v1.mjs for what Biome cannot express: TypeScript-only
  files, the folder taxonomy and import contract, PropTypes, `ant-`.
- typecheck:v1, lint:v1 and test:v1 scripts; v0 scripts are unchanged.
- test-fixtures/v1-boundary/ proves each rule fails and passes as
  specified, run with node --test so the v0 vitest suite never sees it.
- .gitignore re-includes src/v1/lib/ (the Python `lib/` rule matched it).

v0 build output, test counts, typecheck and Biome results are unchanged.
…270)

check-v1.mjs:
- Scan comments with a string/template-aware tokenizer; a `/*` or `//`
  inside a string or line comment no longer hides the imports after it.
- Read template-literal specifiers, import.meta.glob and `import(..., opts)`.
- Flag specifiers that leave src/v1 (`/`, `..`, `@/`, `src/`) itself, not
  only via Biome, which skips template literals.
- Check `@import`/`url()` and `ant-` in styles/ CSS; report symlinks;
  catch built `ant-` names; treat .test/.spec .mts/.cts as test files;
  ignore .DS_Store; drop the unused --root option.

biome.json: also ban `..`, `./../**`, `/**`, `antd/**`, `prop-types/**`.
tsconfig: skipLibCheck off so v1's own .d.ts files are checked.
.gitignore: re-include lib/ at any depth under src/v1.

Fixtures: a generated matrix over every folder-contract pair, every import
form, every outside spelling, a symlink case, and cases for each new rule;
tsc cases assert error codes; output matching excludes the command text and
spawn errors fail loudly. Comment and README corrections.
… (UN-4270)

- ci-frontend-lint: the typecheck job also runs `typecheck:v1` and
  `lint:v1`, so the strict v1 tsconfig and the scripts/check-v1.mjs checks
  (folder contract, TypeScript-only files, PropTypes, `ant-`) gate PRs now
  rather than from P0-T10.
- Pages may no longer import app/, test/ or the v1 entry, only the shared
  folders (components, shell, services, stores, hooks, lib, styles,
  plugins). Test files in a page may still import test/. Contract table,
  README and the fixture matrix updated together.
…(UN-4270)

check-v1.mjs: split the comment scanner into small helpers (cognitive
complexity 40 -> under 15), drop the nested ternary and nested template
literal, use String.raw for the import regexes, keep CLI flags in a Set.

Fixture runner:
- Cases and the expected folder contract are parsed text tables instead of
  repeated object literals (the 10% duplication).
- Sorts use an explicit localeCompare comparator.
- The contract matrix is built by a helper.
- Spawns /bin/sh by absolute path and no longer needs `git init` (Biome only
  needs the ignore file). The git check-ignore call keeps a PATH lookup, with
  the reason and a NOSONAR marker.

Fixture cases: re-exports rewritten as small functions and read-only props,
without changing what each case exercises.
…nnot see (UN-4270)

Addresses Greptile review on #2380:
- import.meta.glob([...]): every pattern in the array is checked, not only
  the first, so an allowed first pattern no longer hides a forbidden one.
- check-v1 --code also scans v0 (src/ outside v1, including the cloud
  src/plugins overlay) for template-literal imports, globs, require(),
  @/v1, src/v1, relative paths and CSS @import that reach into v1 other
  than through `@v1`. Biome still covers plain imports.

Fixtures: Greptile's example as a case, a glob array with a forbidden later
pattern, and a generated test of every v0 -> v1 form (denied and allowed).
The v0 scan reports nothing on the current tree.
…i 5.2 both declare containSubset

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… per-browser preference

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tore

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in three modes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… match the v1 Tailwind root

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ercase letters only

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…heme mapping and the focus utility

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…so biome ci passes

src/v1/styles/tokens.css is written by scripts/build-v1-tokens.mjs and checked byte-for-byte by styles/tokens.test.ts; the formatter would rewrap it and break the check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sts and on missing variants

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and portal sync

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tEffect

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…line styles, CSS imports and !important fail lint

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… arbitrary values

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… and the fixtures

v0's root auto-detected sources across the whole frontend, so the v1 tree, scripts/check-v1-styles.mjs and test-fixtures/ added 12 unprefixed utilities and 8 theme variables to v0's stylesheet. With these three @source not lines the v0 build is byte-identical to the base again (239/239 files).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…longer cancel each other

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…our arbitrary values and !important after a variant

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cope variants, shadows and the root fill to v1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the theme test exercises the mode effect

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ge, the pixel escape and the current unprefixed-class rationale

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Runtime geometry (a page's aspect ratio, a highlight's position) cannot be
a token or a class. Rule S1 now accepts a style attribute whose value is an
object literal with only "--name" keys, so the numbers travel through custom
properties that ui: classes read (ui:aspect-(--page-aspect)); a string, a
variable, a spread or a plain key still fails. Three fixtures pin it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread frontend/scripts/check-v1-styles.mjs Resolved
praveen-formido and others added 2 commits October 29, 2026 13:54
pre-commit's check-shebang-scripts-are-executable fails on branches stacked
on this one; check-v1.mjs already carries the bit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
React's CSSProperties has no index signature for "--*" keys, so the object
literal rule S1 allows fails TS2353 under strict mode. Augment it under
lib/, and typecheck the passing fixture so the augmentation is exercised.
Raised by Ali on the viewer stack.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@athul-rs

Copy link
Copy Markdown
Contributor

Token gaps found while porting the shell menus (#2402) and the product switcher (cloud #1917) onto this PR. Before the port, these values matched the Figma frames as arbitrary values. No token in this PR holds them, so each one either needs a token or the frames need to move onto the existing scale.

Need Frames Closest token today
Body-small line height 13/20 (every shell frame: menu items, org rows, dialog text) --type-body-small 13/18
Larger headings 30/36, 24/32 and 18/28, weight 600 or 400 (product switcher P01, NEW02, LW02, F00) text-display 34/46, text-page-title 20/28, text-section-title 16/24
Radius 12 (org popover, switch dialog) and 4 (Current chip) 10 and 6
Shadows menu 0 8 24 0 #0000001f, org popover 0 8 24 -4 #0000001a, switch dialog 0 12 32 0 #0000002e, product switcher 0 24 60 -10 #00000047 shadow-lg and shadow-xl
Scrim 32% black (switch dialog O02), #18181b at 40% (product switcher) bg-scrim, black at 50%
Status dot colours #f59e0a, #21c45e, #f04545 (product switcher plan row) --warning #b45309, --success #15803d, --destructive #b41b30
Overline tracking 0.08em (org list eyebrow); the avatar initials have none text-overline 0.05em

The body-small line height is the one that shows up most: it changes the height of every single-line row and of the dialog text. The design questions (sidebar vs popover surface, dark-mode secondary and border, dot colours) are with design separately.

@muhammad-ali-e muhammad-ali-e left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed with the latest commits (8028a73). Thanks for taking the custom-property typing in. The viewer stack (#2404–#2409) is now on it and has dropped its copy.

I left 6 comments that don't repeat Greptile's open threads. The first three are gaps in check-v1-styles that let styling through which the rules mean to block: variant-before-prefix classes that compile to nothing, named colours, and uneven unit checks. I reproduced each one on this branch with a probe file and got check-v1-styles: ok. The other three are color-scheme for dark mode, one v0 icon rule missing from the README list, and a small duplication in the theme store.

Greptile's five earlier threads (Node 20 for tokens:v1, spacing tokens, single-class prefix, sibling selectors, inline-style parsing) are still open and unanswered. A reply on each would help close them out.


Generated by Claude Code

Comment thread frontend/scripts/check-v1-styles.mjs Resolved
Comment thread frontend/scripts/check-v1-styles.mjs Resolved
Comment thread frontend/scripts/check-v1-styles.mjs Resolved
Comment thread frontend/src/v1/styles/base.css Resolved
Comment thread frontend/src/v1/README.md Resolved
praveen-formido and others added 3 commits November 2, 2026 12:51
check-v1-styles.mjs let banned styling through in six ways, each
reproduced on this branch before the fix:

- S8 accepted `hover:ui:bg-primary` and `md:ui:flex`; Tailwind 4 needs
  the prefix first and compiles those to nothing. A prefix after a
  variant is now reported with its own message.
- S2 missed named colours as whole JSX values (`fill="red"`) and in CSS
  declarations (`color: white`); both now reuse the named-colour list,
  with transparent and currentcolor still allowed.
- S3 matched a unit only right before `]`, so `grid-cols-[200px_1fr]`
  and `w-[calc(100%-13px)]` passed; it now matches anywhere inside the
  brackets. The CSS pattern gains vh and vw, which the TSX side already
  rejected.
- S8 skipped a single-word class (`className="flex"`) because the
  class-string heuristic needed two utilities; the value of className
  and the arguments of cn()/cva()/clsx()/cx() are now always checked.
- S6 accepted `.v1 + div` and `.v1 ~ div`, and never looked at nested
  rules, so `& + div` escaped the root too. A sibling combinator right
  after the root now fails, at top level and nested.
- S1 missed `style = {{ … }}` with spaces around the `=`.

Nine fixtures pin the gaps; the passing fixture gains the forms the
tightened rules must still allow (prefix-first variants, `.v1 > a + b`,
nested `& > a`, a non-utility single word, transparent in CSS).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e tokens, share the store's storage

- Each generated `.v1[data-theme=…]` block now starts with the mode's
  `color-scheme` (dark for dark, light otherwise), declared beside the
  modes in lib/theme.ts. Without it native controls, select popups,
  autofill and scrollbars kept the browser's light palette on a dark
  v1 page, and S7 rules out the prefers-color-scheme fallback.
- The spacing utilities read the spacing tokens: `--spacing` is bound to
  `--space-1` so every bare step (`ui:w-64`, `ui:size-4`) multiplies the
  token, and the named steps 1 to 8 read their own token, so `ui:p-5`
  is `var(--space-5)` as the token table says. The compile test asserts
  both forms.
- `hydrate()` defaults to the storage the store writes through, and the
  provider's duplicate localStorage lookup is gone, so the preference
  is read from where it was written.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`tokens:v1` used node with a `registerHooks` resolver so the generator
could import the TypeScript sources; that API needs Node 22.15 while
the engines field allows 20.19, so the script failed on the oldest
supported Node. bun, which the frontend toolchain already uses, imports
the sources and resolves extensionless imports directly, so the hook is
removed. The generated tokens.css is unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread frontend/scripts/check-v1-styles.mjs Resolved
Comment thread frontend/scripts/check-v1-styles.mjs Resolved
Base automatically changed from UN-4270-v1-scaffold to main November 2, 2026 09:09

Copy link
Copy Markdown
Contributor

Re-checked on 622695faa. Most of it is fixed, and I verified each item with a probe file: prefix order, named colours, vh/vw in CSS, color-scheme per mode, the single storage handle and the five earlier Greptile threads. Three things remain before approval:

  1. S3 still misses [200px_1fr]. Details are in the S3 thread: the lookahead rejects a unit followed by _.
  2. Greptile's two new comments. The cva() one is a real regression: every string inside cva() is now linted as a class, so defaultVariants: { variant: "outline" } fails lint (reproduced). That will hit every shadcn-style component with variants. Only class-supplying positions should be checked (the base string and variant values), not variant names or choices. The nested sibling case (.v1 .menu { & + .demo {} }) is a smaller false positive, and the new fixture locks it in.
  3. The branch now conflicts with main, since the scaffold UN-4270 [FEAT] v1 scaffold, strict TypeScript and import boundary #2380 merged.

Please let me know once it's ready for re-review.


Generated by Claude Code

praveen-formido and others added 2 commits November 2, 2026 16:07
…fold has landed

The scaffold PR #2380 was squash-merged, so every scaffold file both sides
touched conflicted. Main's version is taken for the import checker, the
tsconfigs, the CI workflow and the Biome boundary groups; this branch keeps
the styling policy script and its lint:v1 and tokens:v1 entries, the
tokens.css formatter override, the Styling section of the v1 README and the
27 styling fixture rows. Two fixtures main re-added or changed carried an
unscoped :root selector with a raw hex, which the styling policy rejects, so
they reference a token under .v1 instead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… from review

S3 let a unit followed by `_` through (`ui:grid-cols-[200px_1fr]`,
`ui:m-[2rem_auto]`): the lookahead after the unit rejected `\w`, which
includes Tailwind's in-bracket separator. It now rejects letters and `-`
only, and fixture v1-raw-pixel-arbitrary-separator pins both forms.

S8 read every literal inside cva() as a class list, so a normal
`defaultVariants: { variant: "outline" }` failed because outline is also a
utility name. classSiteLiterals now tracks the property chain inside cva()
and treats as classes only the base string, the values under variants and
a compound `class`/`className`; a property name, a defaultVariants value or
a compoundVariants matcher is a choice, not a class. The passing fixture
carries a full cva config; v1-cva-unprefixed-variant proves a variant
value is still checked.

S6 reported every nested `& + x` as leaving the root, but under `.v1 .menu`
it resolves to `.v1 .menu + x`, inside v1. selectorPreludes now records
whether every enclosing rule is one compound on the root, and only then
does a `&` sibling leave. The nested fixture is rewritten to the leaving
forms (directly under `.v1`, and under `&[data-theme="dark"]`), and the
passing fixture gains the safe ones.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@praveen-formido

Copy link
Copy Markdown
Contributor Author

All three are on the branch at c6c0b21: main merged in (e565ae5, a merge rather than a rebase so the five P1-T21 PRs stacked on this branch keep clean diffs), then the S3 separator gap, the cva() regression and the nested sibling false positive (c6c0b21), each with a fixture. Gates on that head: typecheck:v1, lint:v1, 39 vitest tests, 89 fixture rows, biome ci src/ at v0's baseline. Ready for re-review.

@muhammad-ali-e muhammad-ali-e left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-checked on c6c0b21c7: all six review items and the three follow-ups are fixed, each confirmed with a probe file (S3 [200px_1fr]/[2rem_auto], cva() variant names, nested & + inside v1, prefix order, named colours, vh/vw), and typecheck:v1, lint:v1 and all 89 fixture rows pass. Approving. Thanks, Praveen!


Generated by Claude Code

Comment thread frontend/src/v1/styles/tokens.ts Resolved
…narCloud findings

SonarCloud's duplication check ignores literal values, so the 75 same-shaped
entries in styles/tokens.ts read as copies of each other (91% of the file,
16.8% of the PR's new lines, gate threshold 3%). The values now live in
styles/tokens.json, one array per family in board order; tokens.ts keeps the
Token type and builds TOKENS from it, deriving cssVar from name (true for
every entry) and expanding a single string to all three modes. tokens.css is
regenerated (header only) and the lint no longer needs a raw-values exemption
for tokens.ts.

Also, from Ritwik's pass over the remaining findings:
- CSS_UNIT_RE: `\d+(?:\.\d*)?|\.\d+` matches the same strings without the
  overlapping alternation (javascript:S8786).
- The stem escape `s.replace("-", "\\-")` was a no-op: `-` is literal outside
  a character class.
- The file-type dispatch uses `endsWith(".css")` (javascript:S6557).
- `.sonarcloud.properties` excludes frontend/test-fixtures/**: the boundary
  fixtures break the v1 rules on purpose.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Frontend Lint Report (Biome)

✅ All checks passed! No linting or formatting issues found.

@sonarqubecloud

sonarqubecloud Bot commented Nov 3, 2026

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Unstract test results

Per-group results

Status Group Tier Passed Failed Errors Skipped Duration (s)
⚪ e2e-agent-kv e2e 0 0 0 1 0.1
✅ e2e-api-deployment e2e 3 0 0 0 12.8
✅ e2e-coowners e2e 1 0 0 0 1.4
✅ e2e-etl e2e 1 0 0 0 14.4
✅ e2e-login e2e 2 0 0 0 1.2
✅ e2e-prompt-studio e2e 1 0 0 0 15.4
✅ e2e-smoke e2e 2 0 0 0 2.5
✅ e2e-workflow e2e 1 0 0 0 22.3
✅ frontend unit 690 0 0 0 14.0
✅ integration-backend integration 1459 0 0 26 66.7
✅ integration-connectors integration 1 0 0 7 7.1
✅ integration-workers integration 181 0 0 1 45.9
❌ ui e2e 0 1 0 0 0.0
✅ unit-agent-kv-schema unit 143 0 0 0 1.1
✅ unit-backend unit 2107 0 0 1 39.7
✅ unit-connectors unit 82 0 0 0 7.2
✅ unit-core unit 281 0 0 0 2.1
✅ unit-filesystem unit 1 0 0 0 2.7
✅ unit-platform-service unit 18 0 0 0 1.7
✅ unit-rig unit 120 0 0 0 3.0
✅ unit-runner unit 12 0 0 0 1.9
✅ unit-sdk1 unit 878 0 0 0 29.9
✅ unit-tool-sidecar unit 8 0 0 0 1.2
✅ unit-workers unit 1589 0 0 2 108.1
TOTAL 7581 1 0 38 402.4

Critical paths

⚠️ Critical paths not yet covered

  • workflow-execution-fan-out — Multi-file workflow execution fans out to file-processing workers and rejoins. (declared coverage: no groups declared)
✅ Covered critical paths
  • auth-login — covered by e2e-login
  • adapter-register-llm — covered by integration-backend
  • workflow-author — covered by integration-backend
  • co-owner-manage — covered by integration-backend, e2e-coowners
  • workflow-create-execute — covered by e2e-workflow
  • api-deployment-provision — covered by integration-backend
  • api-deployment-auth — covered by integration-backend
  • api-deployment-run — covered by e2e-api-deployment
  • mcp-server-auth — covered by integration-backend
  • mcp-platform-auth — covered by integration-backend
  • platform-key-whoami — covered by integration-backend
  • prompt-studio-author — covered by integration-backend
  • prompt-studio-fetch-response — covered by e2e-prompt-studio
  • connector-register-test — covered by integration-backend
  • pipeline-etl-execute — covered by e2e-etl
  • usage-aggregate-read — covered by integration-backend
  • usage-token-tracking — covered by e2e-api-deployment
  • callback-result-delivery — covered by e2e-api-deployment

athul-rs added a commit that referenced this pull request Nov 3, 2026
… and the scaffold

README: the tokens branch's newer wording throughout. package.json: its bun
runner for tokens:v1, keeping this stack's plugin fixtures in test:v1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants