Repository navigation
feat(ui): design-system facelift — control scale, contrast in 6 themes, a11y fixes, Figma relink - #639
feat(ui): design-system facelift — control scale, contrast in 6 themes, a11y fixes, Figma relink#639depthark wants to merge 60 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedToo many files! This PR contains 146 files, which is 46 over the limit of 100. To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch. Upgrade to a paid plan to raise the limit. This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry. ⚙️ Run configuration
⛔ Files ignored due to path filters (1)
📒 Files selected for processing (146)
You can disable this status message by setting the
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
| --color-vertical-navigation-accent: var(--color-bg-primary-base); | ||
| --color-vertical-navigation-accent-light: var(--color-mix-vertical-navigation-accent-light); | ||
| --color-vertical-navigation-fg: var(--color-fg-primary); | ||
| --color-vertical-navigation-focus-border: var(--color-ring); | ||
| --color-vertical-navigation-foreground: var(--color-fg-primary); | ||
| --color-vertical-navigation-group-bg-accent: var(--color-mix-vertical-navigation-accent-light); |
There was a problem hiding this comment.
Navigation colors stop following overrides
The new VerticalNavigation tokens replace colors derived from its reference tokens with fixed exported colors. Changing --color-vertical-navigation-accent, --color-vertical-navigation-surface, or --color-vertical-navigation-foreground no longer updates the selected background and related colors. Apps must override several extra tokens or keep parts of the exported palette.
Preserve the existing formulas and reference links in the runtime token layer so app overrides still update the whole component.
Knowledge Base Used: UI design system and theming
There was a problem hiding this comment.
Agreed. Fixed in Figma, where these tokens come from (this file is export output, so we don't edit it by hand). group-fg-*, item-fg-current, label-fg and fg now alias vertical-navigation/foreground; group-bg-subtle aliases vertical-navigation/surface; group-bg-accent aliases vertical-navigation/accent-light. This line updates with the next token export.
Storybook A11y Report
Light
By group
New violations (23)
Resolved violations (351)
Dark
By group
New violations (23)
Resolved violations (351)
|
Adds section F (Carousel and Steps contrast + control sizing) to the facelift backlog and a work log of the Figma-side changes: re-aliased and new variables, remote-to-local variable rebinding, rebuilt Carousel controls on Action Icon, Steps 24/32/40 scale, measured contrast in all six theme modes, and the code checklist to run after the token export. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- table.figma.ts now targets the new static Table set (3938:1793) on the consolidated Figma Table page; the old Table page was removed. - data-table.figma.ts: the Figma assembly is now named DataTable. - carousel.figma.ts: map the size=full variant (typo fixed in Figma). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
axe flagged aria-prohibited-attr on every skeleton shape: aria-label is not allowed on a generic <div>. The root and standalone shapes are now role="status" (named live region); shapes inside <Skeleton> are aria-hidden so a group announces once; a consumer-hidden shape keeps aria-busy + aria-hidden without status semantics (PriceBlock and AvailabilityStatus contract unchanged). Skeleton v1.0.2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without a target page the prev/next trigger rendered as an <a> with no href, which has no role, so Zag's aria-label was prohibited (axe aria-prohibited-attr, also on DataTable pagination). It now renders an aria-disabled <button type="button">. Pagination v1.0.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without an id prop the Label rendered htmlFor={undefined} while Zag
fell back to its own number-input:<uid>:input id, leaving the field
without an accessible name (axe label, critical). Fall back to a
generated id so label and input always match. FormNumericInput v1.1.1.
The NumericInput Playground story also names its input when the
visible label is toggled off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Akros Orders demo squeezed the shipping / pickup-point column at 1440 px: under table-layout auto the enum filter selects reserve a min-width from their longest option label (264-304 px against declared 150-170 px), the table grew to 1836 px and the shipping column slid under the pinned actions. - FieldSelect skips the width reservation under tableLayout="fixed" (where content cannot reflow a column) and fills the cell instead. - Under fixed layout the built-in actions column is sized to its widest rendered actions cell instead of collapsing to 0 px. - The Orders demo opts into tableLayout="fixed". DataTable v1.2.2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
storybook, @storybook/react, addon-a11y, addon-themes 10.6.0 -> 10.6.1; @storybook/test-runner 0.24.4 -> 0.26.0; storybook-react-rsbuild and storybook-addon-rslib 3.4.2 -> 3.6.0. `storybook automigrate` reports no migrations; dev server and static build verified. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Records the Tabs, TreeView, Badge, placeholder, fg/secondary (Dark) and neo-dark bg-light changes made in Figma for the audit / Notion items, measured contrast in all six modes, the open input-hover decision, and the post-export check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Format: CSS variables Exported themes: 6 themes (light, dark, neo, neo-dark, business, akros) Base branch: master
Regenerated tokens/figma/variables.css and brand-overrides.css with merge-figma-themes.mjs from the cherry-picked export. Verified against figma-facelift-log-2026-10.md: all Carousel/Steps/Tabs/TreeView/Badge re-aliases, the 4 new carousel size tokens, fg/secondary + fg/placeholder per mode and the neo-dark bg-light/primary scale are present. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Code side of the Carousel facelift (Figma 2026-10-08, tokens exported): - Previous / Next / Autoplay render ActionIcon (facelift B9) sized from the carousel size: sm 24, md 32, lg/full 40 px. - Carousel.Indicator is a 24 px target (was 16 px, below WCAG 2.2 target size) with an 8 px dot child; children still replace the dot. - Autoplay keeps the opaque control surface over the slide image. Carousel v1.1.0 (molecule and the template that composes it). axe: 0 violations across the 13 Carousel stories. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
All logged re-aliases and per-mode values are present in the export. axe on real stories cleared Tabs/TreeView/Badge/Input/Carousel in all six brand/mode combinations; three remaining Steps solid-variant issues are fixed in Figma (new color/steps/title/fg/solid/current) and wait for the next export. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Format: CSS variables Exported themes: 6 themes (light, dark, neo, neo-dark, business, akros) Base branch: master
Adds color/steps/title/fg/solid/current, moves the solid complete trigger to fill/surface and lightens neo-dark fg-accent/primary (primary/400). Verified against figma-facelift-log-2026-10.md round 2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The solid variant puts the current trigger on the primary tint, where the accent title was accent-on-accent (neo-light 3.95, neo-dark 2.65). It now uses the new --color-steps-title-fg-solid-current (fg/primary, 6.42-18.54 across modes). Steps v1.0.2; axe color-contrast clean on all Steps stories' component markup in all six brand/mode combinations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Zag renders the step list as role="tablist" but the items as div[aria-current], which a tablist may not own (aria-required-children, critical — facelift C5). Items are now role="presentation" and the step state moves to the tab as aria-current="step"; separators are aria-hidden. Steps.Progress gets a default accessible name (aria-progressbar-name). The Validation story labels its inputs. Steps v1.0.3. axe: 0 non-contrast violations across 13 Steps stories. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Zag hides off-screen slides with aria-hidden only, so links or buttons on them stayed in the tab order (facelift C5, axe aria-hidden-focus). Hidden slides are now inert; verified the flag follows navigation. - New aria-label prop names the carousel region, so several carousels on a page are distinguishable (axe landmark-unique); multi-carousel stories are labelled. Carousel v1.2.0. axe: 0 non-contrast violations across 13 stories. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
StatusText rendered a bare <div>, so form errors appearing after validation were never announced (facelift C4). status="error" now renders role="alert"; warning/success render role="status"; plain helper text stays silent and is read through aria-describedby. A caller-supplied role still wins. Checked the stories the Playwright specs query by role: no duplicate alert/status roles appear. StatusText v1.0.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Validation text sat next to Select, Switch, Slider, RadioGroup and PhoneInput but was never associated with the focusable control, so screen readers did not read it on focus (facelift C4). - New internal useDescribedBy / useRegisterDescription: a compound StatusText part registers its id while mounted, and the control renders aria-describedby only then (never a dangling id). A caller's own aria-describedby is joined, not replaced. - Select: trigger. RadioGroup: radiogroup root. PhoneInput: input. - Switch: hidden input. Slider: each thumb (role="slider"). Select v1.1.2, Switch v1.0.1, Slider v1.0.2, RadioGroup v1.0.1, PhoneInput v1.0.2. Browser check: 17 links, 0 broken references; axe has no new violations across 54 stories. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Mx4mNZrqt5ZWysuyyKcE1
b2b72f2 to
44280de
Compare
The country trigger is Zag's role="combobox", which takes no name from its content, and its aria-labelledby points at a Select label that PhoneInput never renders. Every PhoneInput story failed axe button-name (critical, 18 nodes / 13 stories). It now defaults to aria-label "Country: <selected country>", skipped when the caller passes aria-label or aria-labelledby. PhoneInput v1.0.3. axe: 0 non-contrast violations across 13 stories (the warning StatusText contrast left is C1, a Figma status-color fix). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Mx4mNZrqt5ZWysuyyKcE1
Format: CSS variables Exported themes: 6 themes (light, dark, neo, neo-dark, business, akros) Base branch: master
Brings in the 2026-10-09 Figma pass: - control scale: height/form-control/lg 70 → 56, size/form-control/* aliases height/form-control/*, pagination lg follows it; - small-field text 16 px (text/base) incl. Select / CascadeSelect trigger and value xs/sm; labels pinned to text/sm; - contrast: fg-accent / status-warning re-aliases, new border/control, bg/control-checked, border/control-selected, border/danger|success|warning, fg/on-selected, neo raw colours aliased; radius/cascade-select/lg. pnpm validate:tokens passes (unused-token warnings only). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The visual comparison found three lg heights (Button 56, Select /
Combobox 60, form controls 70) and Tabs at 33 / 50 / 68. With the
2026-10-09 export (height/form-control/lg = 56) every control now uses
the shared height tokens:
- Button lg, Select lg, Combobox lg, CascadeSelect lg → h-form-control-lg
(+ the lg radius);
- Select xs and CascadeSelect xs → h-form-control-sm (32 px, same as
sm, as asked for in Component comparison);
- Tabs triggers → h-form-control-{sm,md,lg}.
Measured in Storybook: Button, Input, NumericInput, SearchForm, Select,
Combobox, CascadeSelect, PhoneInput, Pagination and Tabs all render
32 / 44 / 56 px with radius 8 / 12 / 16 (Pagination keeps r8);
small field text is 16 px everywhere.
Button v0.3.4, Select v1.1.3, Combobox v1.3.3, CascadeSelect v1.0.3,
Tabs v1.0.2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rified) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| --color-date-picker-day-border: var(--color-date-picker-transparent); | ||
| --color-date-picker-day-fg: var(--color-date-picker-text); | ||
| --color-date-picker-day-fg-selected: var(--color-date-picker-accent-foreground); | ||
| --color-date-picker-day-fg-selected: var(--color-fg-on-primary-solid-neo); |
There was a problem hiding this comment.
The new --color-date-picker-day-fg-selected skips --color-date-picker-accent-foreground. Apps that change the calendar’s reference text color no longer change selected dates. The generated declaration loads last and replaces the existing link.
This violates the repository requirement to keep tokens two-layered, with references feeding derived values. Update the reference color for the contrast fix, then keep the selected-date token linked to it, including the light export. The same bypass affects --color-select-item-fg-selected and the input validation focus colors. These links must be restored before merging.
Context Used: libs/ui/AGENTS.md (source)
Knowledge Base Used: UI design system and theming
There was a problem hiding this comment.
Agreed. Fixed in Figma: date-picker/day/fg/selected now aliases date-picker/accent/foreground, and that aliases fg/on-primary-solid-neo, which keeps the contrast fix. This file is export output, so the change lands with the next token export.
Format: CSS variables Exported themes: 6 themes (light, dark, neo, neo-dark, business, akros) Base branch: master
Button text on its own fills: new per-mode fg/on-secondary, on-tertiary, on-warning, on-danger and on-tint tokens keep the current colour where it passes and switch to white / base-dark only where it failed (Business, Akros solid fills; tints in Dark, neo-dark, Akros). The button *-light and solid variant fg tokens use them. pnpm validate:tokens passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011PeAJJVgQ7SbBZu4zGN2X3
White text on bg-primary (light blue) was 1.36:1 (axe color-contrast in every brand). The demo content now uses fill-surface / fg-primary. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011PeAJJVgQ7SbBZu4zGN2X3
| --color-fg-on-accent-solid: var(--color-fg-reverse); | ||
| --color-fg-on-danger: var(--color-base-dark); | ||
| --color-fg-on-primary-solid-neo: var(--color-fg-reverse); | ||
| --color-fg-on-secondary: var(--color-base-dark); |
There was a problem hiding this comment.
In Business and Akros, the new --color-fg-on-secondary makes solid secondary buttons use near-black text in every state. Their hover and pressed backgrounds still turn dark green, so labels that previously used white become hard to read.
Keep a readable foreground for those states, or update the backgrounds alongside this change.
Knowledge Base Used: UI design system and theming
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Agreed, and it went further than secondary. With state backgrounds included, 15 button state pairs were under 4.5 : 1, for example Business secondary pressed at 2.75 : 1 and Akros at 3.02 : 1. Fixed in Figma at the semantic layer, where these come from:
- Fills with dark text now lighten on hover and press:
bg/secondary/hover|active→ secondary/400 and /300 in Light, neo-light, Business and Akros;bg/tertiary/hover|active→ tertiary/200 and /100 in Light and neo-light. Dark mode already used this pattern. - Fills with white text darken on press:
bg-light/primary|secondary|warning/active→ /900 in Dark and neo-dark. - neo-dark outlined primary hover and press used pale pink under white text; they now use primary/900 and /800.
A recheck of every button bg state against its fg, composited over color/base, now passes 4.5 : 1 in all six modes. This file is export output, so the change lands with the next token export.
Review (Greptile): moving Previous / Next onto ActionIcon dropped the carousel trigger colour classes, so herbatika's overrides (--color-carousel-trigger-bg-base: white, primary arrows over banner images) no longer applied. The prev/next slots put the carousel trigger bg/fg tokens back on top of ActionIcon. tailwind-merge drops ActionIcon's neutral hover, and the defaults are unchanged (transparent / fill-hover / fg-primary). Verified in Storybook: overriding the tokens on :root now recolours the arrows. Carousel v1.2.3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011PeAJJVgQ7SbBZu4zGN2X3
Review (Greptile, P1): the v1.1.0 read-only display mode removed the hidden input, so a named read-only Rating disappeared from form submission / FormData. The hidden input is rendered in every mode again. It is not focusable and does not affect the named image. Rating v1.1.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011PeAJJVgQ7SbBZu4zGN2X3
Review (Greptile): AccordionHeaderProps, ProductCardNameProps and NumericInputTemplateProps were interfaces; libs/ui rules require `type`. Same shapes, no runtime change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011PeAJJVgQ7SbBZu4zGN2X3
readonly styled the text with the disabled foreground, 4.39:1 on the read-only fill (axe color-contrast in all six brand/mode sweeps). Read-only is not disabled: it keeps the muted surface and border as the cue and uses the normal textarea foreground. Textarea v1.0.2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011PeAJJVgQ7SbBZu4zGN2X3
The demo rendered the real content inside the pulsing skeleton, so axe caught it mid-animation (3.33:1). It now shows a Skeleton.Rectangle until loaded, then the content. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011PeAJJVgQ7SbBZu4zGN2X3
From the 6-mode Storybook color-contrast sweep: - FormCheckbox Controlled and Menu CustomTrigger used raw bg-blue-500 / text-white buttons (3.67:1); they now use Button. - ProductCard "Exclusive" badge was white text on a transparent badge over a light card (1.1:1); it is now an outlined violet badge. - RadioCard badges were white on #888 (3.54:1); now on #4b5563. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011PeAJJVgQ7SbBZu4zGN2X3
Footer.Bottom was painted bg-black but kept the default dark footer text (3.4:1) and link (1.17:1). The copyright line, credit and link now use text-fg-reverse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011PeAJJVgQ7SbBZu4zGN2X3
fg-reverse turns near-black in the dark modes while the bar stays black. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011PeAJJVgQ7SbBZu4zGN2X3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011PeAJJVgQ7SbBZu4zGN2X3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011PeAJJVgQ7SbBZu4zGN2X3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011PeAJJVgQ7SbBZu4zGN2X3
| * Accessible name for a meaningful standalone icon (rendered as `role="img"`). | ||
| * Omit it for decorative icons and icons next to text: they stay `aria-hidden`. | ||
| */ | ||
| label?: string |
There was a problem hiding this comment.
Changed props violate type rules
The updated IconProps still uses interface, but the repository requires type for type definitions. Convert it to a type alias before merging. The updated SliderProps and ProductCardTemplateProps need the same fix, and this pattern appears in other changed definitions.
Context Used: libs/ui/AGENTS.md (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Summary
Design-system facelift and accessibility pass for
@techsio/ui-kit, done in Figma and code together. There is one feature per commit; the full log is inlibs/ui/docs/figma-facelift-log-2026-10.md.Figma (source of truth) → 4 token exports merged
xsmatchessm, and small field text is 16 px everywhere.border/control,bg/control-checked,border/control-selectedandborder/danger|success|warningtokens for control boundaries and validation (≥ 3 : 1, C3).fg/on-selected, plusfg/on-secondary|tertiary|warning|danger|tintfor text on fills.Code
label).aria-controls.h-form-control-*on Button, Select, Combobox, CascadeSelect and Tabs.ProductCard.Name as,NumericInputTemplate aria-label,Slider aria-label[], DataTabletranslations.selectColumnLabel/reorderColumnLabel, DataTable fixed-layout widths.@componentVersion, the skillcomponent_versionand the changelog (enforced by the skill-sync gate).Testing
color-contrastsweep in six brand/mode combinations runs after each export.tscclean;pnpm validate:tokenspasses; Biome per file adds no new errors.🤖 Generated with Claude Code
https://claude.ai/code/session_011PeAJJVgQ7SbBZu4zGN2X3