From 012dcafbab263e1f80b474072ec1d83b6f9e5966 Mon Sep 17 00:00:00 2001 From: Lukas Chylik Date: Thu, 8 Oct 2026 18:05:28 +0200 Subject: [PATCH 01/75] docs(ui): log Carousel/Steps Figma facelift and contrast backlog 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 --- libs/ui/docs/design-system-facelift-todo.md | 121 ++++++++++++++++ libs/ui/docs/figma-facelift-log-2026-10.md | 146 ++++++++++++++++++++ 2 files changed, 267 insertions(+) create mode 100644 libs/ui/docs/figma-facelift-log-2026-10.md diff --git a/libs/ui/docs/design-system-facelift-todo.md b/libs/ui/docs/design-system-facelift-todo.md index bb171fda03..b494f10178 100644 --- a/libs/ui/docs/design-system-facelift-todo.md +++ b/libs/ui/docs/design-system-facelift-todo.md @@ -388,6 +388,125 @@ Built by hand while making the 41 `Pages/*` stories, all of them standard: --- +## F ยท Carousel and Steps facelift (contrast + control sizing) + +**Date added:** 2026-10-08. Evidence is measured from the Figma file `New-Design-System` +(pages `๐ŸŸข Carousel` 1188:2 and `๐ŸŸข Steps` 1153:2, light mode) and from +`molecules/carousel.tsx`, `molecules/steps.tsx` and `tokens/figma/variables.css`. +Both pages are marked ๐ŸŸข in Figma, but neither follows the sizing and contrast rules the +rest of the kit uses. + +**Reference sizes already in Figma (what Carousel and Steps should match):** + +| Control | sm | md | lg | +| --- | --- | --- | --- | +| Button | 34 | 44 | 57.5 | +| Form control (Input, Select, โ€ฆ) | 32 | 44 | 70 | +| ActionIcon (icon-only sub-button) | 24 | 32 | 40 | + +Note the three scales do not agree with each other either (Button `lg` 57.5 vs form-control +`lg` 70). That mismatch is the root of A4, and it gets fixed there, not by patching Carousel. + +### ๐Ÿ”ด F1 ยท Carousel โ€” contrast +Measured in light mode, using the Figma fills. + +| Pair | Ratio | Needed | +| --- | --- | --- | +| Inactive indicator dot on the control bar (both `fill-highlight`) | **1.0 : 1**, so the dot is invisible | 3 : 1 | +| Active dot (`#8ec5ff`) on the control bar (`#d1d5db`) | **โ‰ˆ 1.2 : 1** | 3 : 1 | +| Prev/next arrow on its pill | passes, but the pill (`#d1d5db`) against a white page is โ‰ˆ 1.5 : 1 | 3 : 1 | + +- [ ] Inactive dot: a visible neutral fill or a 1 px border at โ‰ฅ 3 : 1 against the bar. + `--color-carousel-indicator-border-base` is `transparent` today, so the border is unused. +- [ ] Active dot: use a stronger primary step. This is the same fix as C6, since the root + cause is the accent tint. +- [ ] Re-point `--color-carousel-control-bg` and `--color-carousel-trigger-bg-base` away from + `fill-highlight`. Today the bar, the buttons and the inactive dots all share one fill, + so nothing separates from anything else. +- [ ] Check dark mode as well. Only light was measured. + +### ๐Ÿ”ด F2 ยท Carousel โ€” control sizing is not on the system +- **Prev/next are `Button` with `icon`.** This is the B9 call-site list (5 sites in this file). + They render as 40 ร— 40 circles with a `<` text glyph in Figma. That matches neither + ActionIcon (24 / 32 / 40, `radius/icon-control` = `sm`) nor Button (34 / 44 / 57.5). +- **The control bar is 144 ร— 48**, with 32 ร— 32 inner frames for prev/next. So the bar + contains 32 px targets inside a 40 px sub-component, and the two disagree. +- **Indicators are 16 ร— 16** (`--size-carousel-indicator` = `dimension-16`) in the code and in + the Sub-components frame. The composite `CarouselControl` frame draws them at **8 ร— 8**. + So Figma shows two different indicator sizes. The 16 px one is also under the 24 ร— 24 + WCAG 2.2 minimum (C5). +- **Trigger size is not tied to the carousel `size`.** `sm`, `md`, `lg` and `full` change only + the slide area (320 / 480 / 768 / 1000 px). The controls stay 40 px in every size. + +- [ ] Migrate prev/next/autoplay to `ActionIcon` (B9), size derived from the carousel `size` + context: `sm` โ†’ 24, `md` โ†’ 32, `lg`/`full` โ†’ 40. +- [ ] Indicators: a โ‰ฅ 24 px hit area around a visible 8 px dot, decoupled from the visual size. + Do not grow the dot. +- [ ] Use one icon-control token set for all three triggers (`--size-icon-control-*`). +- [ ] Rename `Carousel.Control` โ†’ `Carousel.Toolbar` (B5), and use the same chevron glyph as + Pagination (B2). Today Figma draws `<` and `>` as text. +- [ ] Figma: rebuild `CarouselPrevious`, `CarouselNext`, `CarouselIndicator`, + `CarouselIndicators` and `CarouselControl` as ActionIcon instances with real chevron + glyphs, and fix the 8 px vs 16 px indicator mismatch. + +### ๐Ÿ”ด F3 ยท Steps โ€” contrast +Measured in light mode. The same fills are used for `sm`, `md` and `lg`. + +| Element | Colours | Ratio | Needed | +| --- | --- | --- | --- | +| `current` indicator, white number on `#8ec5ff` | | **1.81 : 1** | 4.5 : 1 (3 : 1 if the number is โ‰ฅ 18.66 px bold) | +| `complete` indicator, white check on `#00bc7d` | | **2.46 : 1** | 3 : 1 for the glyph | +| `complete` title, `#00bc7d` on white | | **2.46 : 1** | 4.5 : 1 | +| `current` title, `#4d6789` on white | | 5.81 : 1 | passes, but it reads as lighter than `default` (20.17 : 1), which is backwards for the active step | +| `default` separator, `#020617` | 20.17 : 1 | | the line is solid black in Figma, while code uses `border-primary`; check which is intended | +| `default` indicator number | 4.98 : 1 | | passes, but only just | + +- [ ] Fix `--color-steps-indicator-bg-current` / `-fg-current`: dark fg on the light tint, or + a stronger bg with white fg. Same root cause as C6. +- [ ] Fix `--color-steps-indicator-bg-complete` and `--color-steps-title-fg-complete`. The + success green fails here for the same reason as C1 (`--color-fg-success`). +- [ ] The `current` step should be the highest-emphasis one, so make the title weight and + colour at least as strong as `default`. +- [ ] Reconcile the Figma separator colour with the code token. +- [ ] Re-check the `solid` variant and dark mode. Only the `subtle` row was measured. + +### ๐ŸŸ  F4 ยท Steps โ€” size scale does not match Figma or the other controls +- Figma variables say indicator `sm / md / lg` = **24 / 30 / 36** (`size/steps/indicator/*`). + The drawn components are **24 / 28 / 32**. One of the two is wrong, and the code reads the + variable. +- Indicator numbers are 12 / 14 / 16 px, and `--text-steps-icon` is a fixed `text-md` (20 px). + So the check glyph is too large for the `sm` indicator. +- The Steps row heights are 46 / 58 / 72, which is not a multiple of the 4 px base. That + follows from the sub-pixel dimension tokens in A2. +- Steps `Prev` / `Next` navigation uses `Button`, so those sizes at least follow the + Button scale. The indicator circles are the part that is off. Decide whether the + indicator should map to the ActionIcon scale (24 / 32 / 40) so that a `md` step sits on the + same grid as a `md` button. +- [ ] Pick one size source, update Figma and `--size-steps-indicator-*` together, and scale the + check and number text with the indicator. +- [ ] `Steps` `role="tablist"` with `div[aria-current]` children is still the C5 critical item. + Fix it in the same PR, since the markup changes either way. +- [ ] Figma: the Orientation frame says "orientation axis not yet encoded on component set". + Add `orientation` to the component set. + +> **Status 2026-10-08:** Figma side of F1โ€“F4 done. See +> [`figma-facelift-log-2026-10.md`](./figma-facelift-log-2026-10.md) for every variable and +> node change, the measured contrast in all six modes, and the code check-list to run after +> the token export. Still open: an `orientation` axis on the Steps set, and the Theme-level +> contrast misses (Akros placeholder, neo-dark accent, Dark `fg/secondary`). + +### Figma action list for F (do in this order) +1. Fix variables first: steps `current` / `complete` colours, carousel control and indicator + colours, steps indicator sizes. Edit in Figma, then the user re-exports (see the + "never edit exported tokens" rule). +2. Rebuild Carousel prev/next/indicators/control from `Action Icon` instances (2774:39583). +3. Rebuild Steps indicators on the agreed size scale. +4. Re-screenshot both pages in light and dark and re-measure the ratios above. +5. Publish Code Connect once the component sets exist (`carousel.figma.ts`, `steps.figma.ts` + are already in `src/molecules/`). + +--- + ## Suggested order 1. **A2** (export precision) โ€” one script change, removes sub-pixel noise everywhere and must @@ -403,4 +522,6 @@ Built by hand while making the 41 `Pages/*` stories, all of them standard: size inheritance) โ€” mechanical, wide, safe behind visual regression. 8. **A1, A3, A4** (scale unification + rhythm) โ€” the big one; do it component group by component group, behind the same visual-regression suite. + **F1 โ€“ F4** (Carousel / Steps contrast and control sizing) ride along with C1/C6 and B9, + because the colour fixes are the same tokens and the button migration is the same work. 9. **B12 โ€“ B14, D, E** โ€” API convergence and the missing primitives, as capacity allows. diff --git a/libs/ui/docs/figma-facelift-log-2026-10.md b/libs/ui/docs/figma-facelift-log-2026-10.md new file mode 100644 index 0000000000..61c7a0b175 --- /dev/null +++ b/libs/ui/docs/figma-facelift-log-2026-10.md @@ -0,0 +1,146 @@ +# Figma facelift log โ€” October 2026 + +Work log for the design-system facelift done **in Figma first**. After each block the user +re-exports the tokens (`tokens/figma//variables.css`, never hand-edited) and the +codebase is checked against this log. + +File: [New-Design-System](https://www.figma.com/design/gi5GUSWwAeXknaKEeLqK5w/New-Design-System) +Backlog this implements: [`design-system-facelift-todo.md`](./design-system-facelift-todo.md) ยง F. + +--- + +## 2026-10-08 ยท Carousel, Steps, Table consolidation + +### Root cause found: pages were bound to a remote library, not to the local variables + +Every node on the Steps and Carousel pages was bound to **remote** variables. These are the +library copies (`VariableID:/โ€ฆ`) of the local collections, with the same names. +The token export reads the **local** variables. So before this change, editing a local +variable changed the code but not what the component drew in Figma. The two had silently +drifted apart. For example, `size/steps/indicator/md` was 30 locally but drawn at 28. + +Fixed for these two pages by rebinding each remote binding to the same-named local variable +in the same-named collection: **Steps 533 bindings, Carousel 382**, with 0 unmatched. +**Other pages were not checked.** The same rebind script should be run page by page as each +component gets its facelift. + +### Variables โ€” re-aliased (local collections) + +| Variable | Before | After | Why | +| --- | --- | --- | --- | +| `color/carousel/control/bg` | `color/fill/highlight` | `color/fill/surface` | bar, buttons and dots all shared one fill | +| `color/carousel/trigger/bg/base` | `color/fill/highlight` | `color/bg/transparent` | triggers are Action Icons, so the pill only shows on hover | +| `color/carousel/indicator/bg/base` | `color/fill/highlight` | `color/fg/placeholder` | inactive dot was 1.0 : 1 (invisible) | +| `color/carousel/indicator/bg/active` | `color/bg/primary/base` | `color/fg/primary` | active dot was โ‰ˆ 1.2 : 1 | +| `size/carousel/indicator` | `dimension/16` | `dimension/24` | now the **hit area** (WCAG 2.2 target size) | +| `spacing/carousel/indicator` | `dimension/6` | `dimension/0` | 24 px hit areas already space the 8 px dots 16 px apart | +| `spacing/carousel/control` | `dimension/16` | `dimension/8` | tighter bar | +| `color/steps/indicator/fg` | `color/fg/secondary` | `color/fg/primary` | default number was 2.4 : 1 in Dark | +| `color/steps/indicator/fg/current` | `color/white` | `color/fg/on-primary-solid-neo` | was 1.81 : 1 in Light | +| `color/steps/indicator/bg/complete` | `color/bg/success` | `color/success/700` | white check was 2.46 : 1 | +| `color/steps/indicator/border/complete` | `color/bg/success` | `color/success/700` | match bg | +| `color/steps/separator/bg/complete` | `color/bg/success` | `color/success/700` | match indicator | +| `color/steps/content/border/complete` | `color/bg/success` | `color/success/700` | match indicator | +| `color/steps/title/fg/complete` | `color/fg-accent/success` | `color/fg/status-success` | was 2.46 : 1 (Figma) / 3.3 : 1 | +| `color/steps/indicator/bg/solid/complete` | `color/bg/success` | `color/success/700` | solid variant, same fix | +| `color/steps/indicator/fg/solid/complete` | `color/fg/reverse` | `color/white` | dark fg on `success/700` failed in Dark | +| `color/steps/indicator/fg/solid/current` | `color/fg/reverse` | `color/fg/on-primary-solid-neo` | same fix as subtle | +| `color/steps/indicator/fg/solid` | `color/fg/on-primary-indicator` | `color/fg/primary` | white on 15 % overlay failed in neo-light | +| `color/steps/trigger/bg/complete` | `color/bg/success` | `color/fill/overlay` | green title on green bg failed; state is carried by the indicator | +| `color/steps/progress/range/bg` | `color/bg/primary/base` | `color/fg-accent/primary` | range vs track was โ‰ˆ 1.4 : 1 | +| `size/steps/indicator/md` | `dimension/30` | `dimension/32` | one scale with Action Icon (24 / 32 / 40) | +| `size/steps/indicator/lg` | `dimension/36` | `dimension/40` | ใ€ƒ | +| `text/steps/number/sm \| md \| lg` | `text/sm \| md \| lg` | `text/xs \| sm \| md` | number was too big for the circle | +| `text/steps/icon/sm \| md \| lg` | `text/sm \| md \| lg` | `dimension/16 \| text/icon/md \| text/icon/lg` | same glyph scale as Action Icon | + +`color/fg/on-primary-solid-neo` is the only existing semantic token that passes on +`color/bg/primary/base` in **all six** modes. The name is a misnomer; a rename to +`color/fg/on-primary-base` belongs in the Theme collection clean-up. + +### Variables โ€” new (collection `carousel`, code syntax set) + +| Variable | Alias | CSS | +| --- | --- | --- | +| `size/carousel/trigger/sm` | `dimension/24` | `--size-carousel-trigger-sm` | +| `size/carousel/trigger/md` | `dimension/32` | `--size-carousel-trigger-md` | +| `size/carousel/trigger/lg` | `dimension/40` | `--size-carousel-trigger-lg` | +| `size/carousel/indicator/dot` | `dimension/8` | `--size-carousel-indicator-dot` | + +### Variables โ€” scope fixes +`spacing/steps/text`: `ALL_SCOPES` โ†’ `GAP`. `radius/steps/separator`: `ALL_SCOPES` โ†’ `CORNER_RADIUS`. + +### Contrast after the change (measured from the variables, all six Theme modes) + +| Pair | need | Light | Dark | neo-light | neo-dark | Business | Akros | +| --- | --- | --- | --- | --- | --- | --- | --- | +| Carousel inactive dot / bar | 3 | 4.39 | 3.04 | 4.39 | 3.04 | 4.39 | **2.56** | +| Carousel active dot / bar | 3 | 16.22 | 14.68 | 16.22 | 14.68 | 16.22 | 18.90 | +| Carousel arrow / bar | 3 | 16.22 | 14.68 | 16.22 | 14.68 | 16.22 | 18.90 | +| Steps current number | 4.5 | 9.84 | 10.71 | 4.81 | 4.81 | 5.48 | 13.21 | +| Steps complete check | 3 | 5.02 | 5.02 | 5.02 | 5.02 | 5.02 | 11.01 | +| Steps default number | 4.5 | 14.42 | 14.68 | 14.42 | 14.68 | 14.42 | 14.84 | +| Steps complete title / page | 4.5 | 5.02 | 14.34 | 5.02 | 14.34 | 5.02 | 6.96 | +| Steps current title / page | 4.5 | 5.81 | 12.09 | 4.81 | **4.19** | 7.41 | 21.00 | +| Steps description / page | 4.5 | 6.16 | **3.27** | 6.16 | **3.27** | 6.16 | 7.57 | +| Steps progress range / track | 3 | 4.14 | 8.36 | 3.43 | **2.89** | 5.29 | 13.21 | +| Steps solid current / complete / default | 4.5 / 3 / 4.5 | 9.84 / 5.02 / 12.73 | 10.71 / 5.02 / 13.93 | 4.81 / 5.02 / 12.73 | 4.81 / 5.02 / 13.93 | 5.48 / 5.02 / 12.73 | 13.21 / 11.01 / 14.84 | + +The remaining failures, in **bold**, are all **Theme-level** semantics. Fixing them in a +component alias would only move the problem: +- Akros `color/fg/placeholder` = `#999999`. +- neo-dark `color/fg-accent/primary` = `#e60000` on near-black. +- Dark `color/fg/secondary` (facelift **C2**). + +### Components โ€” Carousel page (`๐ŸŸข Carousel`, 1188:2) +- New component sets: + - `CarouselIndicator` (`state=default|active`): a 24 ร— 24 frame (`size/carousel/indicator`) around an 8 px ellipse (`size/carousel/indicator/dot`). + - `CarouselIndicators`: three indicators, gap `spacing/carousel/indicator`. + - `CarouselControl` (`size=sm|md|lg`): Prev and Next are **Action Icon** instances (neutral, the same size as the variant) with `token-icon-carousel-prev` / `-next`, plus `CarouselIndicators`. Fill `color/carousel/control/bg`, padding `padding/carousel/control`, gap `spacing/carousel/control`, radius `radius/carousel`. +- All 8 `Carousel` variants now contain a `CarouselControl` instance. `sm` maps to `sm`, `md` to `md`, and `lg` and `full` map to `lg`. Center variants centre the control. +- Variant `size=ful, Alignment=Center` was renamed to `size=full, Alignment=Center`. +- The 3 instances of the old `CarouselControl` (in the Control Position demos) were swapped to the new `md`. +- Removed: the orphan `CarouselPrevious` / `CarouselNext` instances (their main components no longer existed), the old `CarouselIndicator` set, the `CarouselIndicators` instance, and the old `CarouselControl` component. +- The "Properties & Tokens" section now carries the token table and this log. + +### Components โ€” Steps page (`๐ŸŸข Steps`, 1153:2) +- Main set `Steps` (`variant ร— size`): indicators are now 24 / 32 / 40, with number and check sizes 12 / 14 / 20 and 16 / 20 / 24. +- The demo grids (Indicator, Number, Orientation) were bound to the tokens instead of raw 24 / 28 / 32 sizes. +- Vertical orientation demo. These were the broken gaps the user reported: + - The separator used to start 4 px under the circle and run into the next circle. It now has a symmetric `spacing/steps/text` gap above and below. Its length is `spacing/steps/separator-vertical` and its width is `size/steps/separator/width`. + - All row, column and text gaps are bound to `spacing/steps/*`. + - The demo shows only the **active** panel, as Zag renders it. +- New "Properties & Tokens" section at the bottom, with the props, the token table and this log. +- Not done: an `orientation` variant axis on the component set. It is still demo-only. + +### Table consolidation +- Deleted the old `๐ŸŸง Table` page (1390:4). None of its 18 components had instances on any other page. +- `๐ŸŸง Table2` was renamed to `๐ŸŸง Table`. All 188 `Table2*` node names became `Table*` (for example `Table2.Cell` โ†’ `Table.Cell`), along with the page title texts. +- Node IDs are unchanged, so the 10 `data-table-*.figma.ts` Code Connect templates still resolve. +- The Table page now holds **both** tables, built from the same `Table.*` parts: + - **`Table`** (3938:1793) is new. It is the static primitive (`organisms/table.tsx`), a component set with `variant` line|outline|striped ร— `size` sm|md|lg. Each variant has a caption, a header (4 ร— `Table.ColumnHeader`), a body (3 ร— `Table.Row`) and a footer (4 ร— `Table.Cell`). Sizes are bound to `padding/table/cell/{size}/x|y`, `text/table/{size}` and `padding/table/caption/{size}`. Outline uses `border/table/width` + `radius/table`. + - **`DataTable`** (3399:330) is the former `Table2` assembly. The component, frame and title were renamed from `Table` to `DataTable` so the two don't collide. +- Rebound the Table page from remote library variables to local ones: 197 bindings. The remaining remote bindings are inside instances of remote library components (Token Icons, Icon) and follow those main components. +- Code Connect republished on 2026-10-08: + - `table.figma.ts` now points at `3938-1793`. + - The `data-table.figma.ts` comment changed from `Table2` to `DataTable`. + - `carousel.figma.ts` gained the `full` size mapping, after the variant typo fix. + - All templates uploaded without errors. +- Not done: the shadow `shadow/table/outline` on the outline variant (an effect, not bound). + +### Page status +Carousel and Steps were already ๐ŸŸข. They now meet the bar the emoji claims. Table stays ๐ŸŸง. + +--- + +## Check after the token export (code side) + +Run after the user re-exports. Expected diff in `tokens/figma/*/variables.css`: +- [ ] All re-aliases from the table above, in every mode file. +- [ ] 4 new `--size-carousel-*` variables. + +Code changes these Figma changes require: +- [ ] `carousel.tsx`: `Carousel.Previous` / `Next` / `Autoplay` become `ActionIcon`, with size from the carousel `size` context (`sm` โ†’ `sm`, `md` โ†’ `md`, `lg` / `full` โ†’ `lg`). This is B9. +- [ ] `carousel.tsx`: `Carousel.Indicator` becomes a 24 px button (`w-carousel-indicator`) that renders an 8 px dot (`size-carousel-indicator-dot`) inside it. Today the whole button is the dot. +- [ ] `_carousel.css`: `--width-carousel-indicator` alias still valid (now 24 px); add the dot alias. +- [ ] `_steps.css`: drop the hardcoded `--text-steps-icon: var(--text-md)` and use the per-size `--text-steps-icon-{sm,md,lg}`. +- [ ] Re-run Storybook a11y on Carousel and Steps and compare against the contrast table. From ade6ffc103f005357a9e0914dfa6eb8f9d217f7e Mon Sep 17 00:00:00 2001 From: Lukas Chylik Date: Thu, 8 Oct 2026 18:05:32 +0200 Subject: [PATCH 02/75] chore(ui): reconnect Table and Carousel Code Connect after Figma rework - 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 --- libs/ui/src/molecules/carousel.figma.ts | 1 + libs/ui/src/organisms/data-table.figma.ts | 2 +- libs/ui/src/organisms/table.figma.ts | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libs/ui/src/molecules/carousel.figma.ts b/libs/ui/src/molecules/carousel.figma.ts index 8241c878bb..4ef3d71e3a 100644 --- a/libs/ui/src/molecules/carousel.figma.ts +++ b/libs/ui/src/molecules/carousel.figma.ts @@ -8,6 +8,7 @@ const size = figma.selectedInstance.getEnum("size", { sm: "sm", md: "md", lg: "lg", + full: "full", }) const align = figma.selectedInstance.getEnum("Alignment", { Left: "start", diff --git a/libs/ui/src/organisms/data-table.figma.ts b/libs/ui/src/organisms/data-table.figma.ts index 9fd0c10760..d94f38f21a 100644 --- a/libs/ui/src/organisms/data-table.figma.ts +++ b/libs/ui/src/organisms/data-table.figma.ts @@ -5,7 +5,7 @@ import figma from "figma" /* - * `Table2` is a static assembly โ€” it exposes no component properties, so the + * `DataTable` is a static assembly โ€” it exposes no component properties, so the * snippet is the canonical composition rather than a mapped one. The feature * flags below are the ones the assembly actually shows: a toolbar, a sortable * header, a filter row and a pagination bar. diff --git a/libs/ui/src/organisms/table.figma.ts b/libs/ui/src/organisms/table.figma.ts index 5230f39568..7b7e4c4d73 100644 --- a/libs/ui/src/organisms/table.figma.ts +++ b/libs/ui/src/organisms/table.figma.ts @@ -1,4 +1,4 @@ -// url=https://www.figma.com/design/gi5GUSWwAeXknaKEeLqK5w/New-Design-System?node-id=2774-39180 +// url=https://www.figma.com/design/gi5GUSWwAeXknaKEeLqK5w/New-Design-System?node-id=3938-1793 // source=https://github.com/TechsioCZ/new-engine/blob/master/libs/ui/src/organisms/table.tsx // component=Table @@ -20,7 +20,7 @@ export default { imports: ['import { Table } from "@techsio/ui-kit/organisms/table"'], example: figma.code` Recent orders From 409a62869d37356171c00419b5dbca1be50122d8 Mon Sep 17 00:00:00 2001 From: Lukas Chylik Date: Thu, 8 Oct 2026 18:10:18 +0200 Subject: [PATCH 03/75] fix(ui): Skeleton no longer puts aria-label on a role-less div axe flagged aria-prohibited-attr on every skeleton shape: aria-label is not allowed on a generic
. The root and standalone shapes are now role="status" (named live region); shapes inside 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 --- .../skills/skeleton-usage/SKILL.md | 2 +- libs/ui/skills/skeleton-usage/SKILL.md | 2 +- libs/ui/src/atoms/skeleton.tsx | 41 +++++++++++++++---- .../stories/changelog/changelog.stories.tsx | 3 ++ 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/libs/ui/agent-plugin/skills/skeleton-usage/SKILL.md b/libs/ui/agent-plugin/skills/skeleton-usage/SKILL.md index 08167908d4..59cca23db6 100644 --- a/libs/ui/agent-plugin/skills/skeleton-usage/SKILL.md +++ b/libs/ui/agent-plugin/skills/skeleton-usage/SKILL.md @@ -5,7 +5,7 @@ description: > loading placeholders using Root, Circle, Text, and Rectangle compound parts with token-backed variants, sizes, and animation speeds. metadata: - component_version: "1.0.1" + component_version: "1.0.2" type: "core" library: "@techsio/ui-kit" library_version: "0.3.2" diff --git a/libs/ui/skills/skeleton-usage/SKILL.md b/libs/ui/skills/skeleton-usage/SKILL.md index fbe9aa3550..f2d582feb4 100644 --- a/libs/ui/skills/skeleton-usage/SKILL.md +++ b/libs/ui/skills/skeleton-usage/SKILL.md @@ -5,7 +5,7 @@ description: > loading placeholders using Root, Circle, Text, and Rectangle compound parts with token-backed variants, sizes, and animation speeds. metadata: - component_version: "1.0.1" + component_version: "1.0.2" type: "core" library: "@techsio/ui-kit" library_version: "0.3.2" diff --git a/libs/ui/src/atoms/skeleton.tsx b/libs/ui/src/atoms/skeleton.tsx index 814fde03d2..bc6e0879c1 100644 --- a/libs/ui/src/atoms/skeleton.tsx +++ b/libs/ui/src/atoms/skeleton.tsx @@ -2,7 +2,7 @@ * Skeleton โ€” @techsio/ui-kit atom. * * @component Skeleton - * @componentVersion v1.0.1 + * @componentVersion v1.0.2 * @skill skeleton-usage * @changelog libs/ui/stories/changelog/changelog.stories.tsx * @@ -105,6 +105,30 @@ function useResolvedSkeletonProps(props: { } } +const LOADING_STATUS_PROPS = { + role: "status", + "aria-busy": true, + "aria-label": "Loading content", +} as const + +/** + * Accessible semantics for a skeleton shape. A standalone shape is its own + * loading status; a shape inside `` is decorative because the root + * already announces the loading state once. `aria-label` is only allowed on a + * role that supports naming, hence `role="status"` instead of a bare div. + * A consumer-hidden shape (`aria-hidden`) gets no status semantics at all. + */ +function useSkeletonA11yProps(ariaHidden?: boolean | "true" | "false") { + const context = useSkeletonContext() + if (context) { + return { "aria-hidden": true } as const + } + if (ariaHidden === true || ariaHidden === "true") { + return { "aria-busy": true, "aria-hidden": true } as const + } + return LOADING_STATUS_PROPS +} + interface SkeletonRootProps extends Omit, "children"> { isLoaded?: boolean @@ -131,8 +155,7 @@ export function Skeleton({ <>{children} ) : (
\` are \`aria-hidden\` so a group announces once, and a consumer-hidden shape (\`aria-hidden\`) gets no status semantics. + ### Table v1.2.1 - Switched the border between header and cell columns from physical border-r to logical border-e for RTL. From 8b011575d5cb29f3c18861a9ba24406f85e2d15f Mon Sep 17 00:00:00 2001 From: Lukas Chylik Date: Thu, 8 Oct 2026 18:10:34 +0200 Subject: [PATCH 04/75] fix(ui): Pagination disabled prev/next render as a button Without a target page the prev/next trigger rendered as an 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 - +
) } diff --git a/libs/ui/src/templates/accordion.tsx b/libs/ui/src/templates/accordion.tsx index 1d7437779b..5548a6feb5 100644 --- a/libs/ui/src/templates/accordion.tsx +++ b/libs/ui/src/templates/accordion.tsx @@ -2,7 +2,7 @@ * Accordion โ€” @techsio/ui-kit template. * * @component Accordion - * @componentVersion v1.0.1 + * @componentVersion v1.0.2 * @skill accordion-usage * @changelog libs/ui/stories/changelog/changelog.stories.tsx * diff --git a/libs/ui/stories/changelog/changelog.stories.tsx b/libs/ui/stories/changelog/changelog.stories.tsx index 0821d3eaea..d073721b2e 100644 --- a/libs/ui/stories/changelog/changelog.stories.tsx +++ b/libs/ui/stories/changelog/changelog.stories.tsx @@ -12,6 +12,9 @@ import type { Meta, StoryObj } from "@storybook/react" const CHANGELOG = ` ## @techsio/ui-kit โ€” Unreleased +### Accordion v1.0.2 +- \`Accordion.Header\` renders the APG heading (\`role="heading"\`, \`aria-level\` 3 by default, overridable) instead of a bare \`
\`, which created one \`banner\` landmark per item (axe \`landmark-no-duplicate-banner\`, \`landmark-unique\`, \`landmark-banner-is-top-level\`). The ref is now \`Ref\`. + ### PhoneInput v1.0.3 - The country trigger (a \`role="combobox"\`, which takes no name from its content) gets a default accessible name, \`Country: \` โ€” axe \`button-name\` (critical) on every PhoneInput story (facelift C5). Pass \`aria-label\` or \`aria-labelledby\` on \`PhoneInput.CountryTrigger\` (or \`triggerProps\`) to localise it. From bf37fa649271562c45365aef4981c607cd6bbd66 Mon Sep 17 00:00:00 2001 From: Lukas Chylik Date: Thu, 8 Oct 2026 23:11:53 +0200 Subject: [PATCH 23/75] docs(ui): name unlabelled controls in Checkbox, Textarea, Tooltip, Breadcrumb stories The full non-contrast axe sweep (623 stories) flagged story-only gaps: standalone Checkbox args stories and value-only Textareas had no label (label, critical), icon-only Tooltip triggers had no name (button-name, critical), and the Breadcrumb Sizes/Variants stories rendered several navs with the same name (landmark-unique). Stories only; no component changes. axe: 0 non-contrast violations across these 43 stories. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_011Mx4mNZrqt5ZWysuyyKcE1 --- libs/ui/stories/atoms/checkbox.stories.tsx | 2 ++ libs/ui/stories/atoms/textarea.stories.tsx | 6 ++++-- libs/ui/stories/atoms/tooltip.stories.tsx | 10 ++++++++-- libs/ui/stories/molecules/breadcrumb.stories.tsx | 10 +++++----- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/libs/ui/stories/atoms/checkbox.stories.tsx b/libs/ui/stories/atoms/checkbox.stories.tsx index fd10ed850d..046827d473 100644 --- a/libs/ui/stories/atoms/checkbox.stories.tsx +++ b/libs/ui/stories/atoms/checkbox.stories.tsx @@ -37,6 +37,8 @@ const meta = { onChange: { action: 'changed' }, }, args: { + // Standalone checkboxes have no visible label; give the control a name. + 'aria-label': 'Accept terms', invalid: false, required: false, }, diff --git a/libs/ui/stories/atoms/textarea.stories.tsx b/libs/ui/stories/atoms/textarea.stories.tsx index b4a05ebd7d..b4a9ccbd78 100644 --- a/libs/ui/stories/atoms/textarea.stories.tsx +++ b/libs/ui/stories/atoms/textarea.stories.tsx @@ -107,8 +107,8 @@ export const AllVariants: Story = {