Uh oh!
There was an error while loading. Please reload this page.
feat: webkit documentation - #919
Merged
Merged
Conversation
…dy registers (#905) * chore: [ENG-47606] add the documentation component category * feat(theme): [ENG-47561] add the docs measure and the mono body register * feat(theme): [ENG-47606] add the prose body register
The documentation column was capped at 752px, narrower than every other measure in the system. Docs pages carry code blocks, card grids and tables, none of which fit a prose measure, so the column now takes the same 1192px the form measure uses. The token is defined here, so it is set here: the doc component stack carries the same value, and an add/add disagreement between the two made every PR in that stack unmergeable.
This was referenced Aug 25, 2026
…il (#918) * chore: [ENG-47606] add the documentation component category * feat(theme): [ENG-47561] add the docs measure and the mono body register * feat(theme): [ENG-47606] add the prose body register * feat(webkit): [ENG-47606] add DocProse, the documentation typography contract * test(storybook): add the linux baselines for the DocProse stories * fix(theme): [ENG-47606] widen the docs measure to the form column The documentation column was capped at 752px, narrower than every other measure in the system. Docs pages carry code blocks, card grids and tables, none of which fit a prose measure, so the column now takes the same 1192px the form measure uses. * fix(webkit): [ENG-47606] pin the prose section step and separate the heading registers The section rung was a descending pair that read 32 on a phone and on a laptop and only reached 48 at xl, so the same page broke its sections at different gaps by window width. It is now a flat 56 to open and 48 to close, taken from the theme's primitive spacing steps. Each heading level now names two registers instead of one. On one register per level the base column collapsed: h2 was 16 (the body's own size), h3 and h4 were both 14, and between 640 and 768 the page title was overtaken by its own h2. Weight and leading are no longer restated here — every element takes its type token verbatim, so the ladder is size and space only. * feat(webkit): [ENG-47607] add DocCallout, the documentation admonition * test(storybook): add the linux baselines for the DocProse and DocCallout stories * fix(webkit): [ENG-47607] step an inline callout link to the only other weight the system defines font-medium is 500, a weight no text token in the theme uses — the set is light (300) and normal (400). An inline link inside a callout now steps to 400. * feat(webkit): [ENG-47611] add DocCard, the documentation grid cell * test(storybook): add the linux baselines for the documentation stories * feat(webkit): [ENG-47612] add DocCardGroup, the documentation card grid * test(storybook): add the linux baselines for the doccardgroup stories * feat(webkit): [ENG-47609] add DocItem, the documentation list row * test(storybook): add the linux baselines for the docitem stories * feat(webkit): [ENG-47613] add DocSteps and DocStep, the documentation procedure list * test(storybook): add the linux baselines for the docsteps stories * feat(webkit): [ENG-47608] add DocFrame, the documentation figure with a full-screen view * test(storybook): add the linux baselines for the docframe stories * test(storybook): stop snapshotting the autoplaying DocFrame clip * feat(webkit): [ENG-47615] add DocUpdate and the heading-nav composable * test(storybook): add the linux baselines for the docupdate stories * feat(webkit): [ENG-47561] add DocPrompt, the copyable agent prompt * test(storybook): add the linux baselines for the docprompt stories * feat(webkit): [ENG-47610] add DocTooltip, the inline gloss * test(storybook): add the linux baselines for the doctooltip stories * fix(webkit): [ENG-47610] let the tooltip headline take its type token's weight The headline restated font-medium (500), a weight no text token in the theme defines. It now takes text-body-sm verbatim, and the step to the tip below it is carried by ink colour. * feat(webkit): [ENG-47618] add DocPagination, the documentation prev/next pair * test(storybook): add the linux baselines for the docpagination stories * feat(webkit): [ENG-47617] add DocPageHeader, the documentation masthead * test(storybook): add the linux baselines for the docpageheader stories * fix(webkit): [ENG-47617] stop the page title being overtaken by its own h2 text-heading-lg runs 18/18/30, so between 640 and 768 the masthead title rendered smaller than the prose h2 under it (20). The title now takes the same descending pair the prose h1 uses — text-heading-2xl with text-heading-xl from sm — which reads 30/30/36 and stays above every heading below it. Closes the theme gap the spec recorded. The deck stops restating font-light; text-body-md already resolves to 300. * feat(webkit): [ENG-47616] add DocOnThisPage, the documentation TOC rail * test(storybook): add the linux baselines for the doconthispage stories * fix(webkit): [ENG-47616] mark the rail's opening line so a shell can align to it A docs shell aligns the outline rail against the page bar beside it, and needed a stable thing to measure. data-toc-title marks the rail's first line so the shell reaches it without depending on this component's internal structure.
…tion # Conflicts: # packages/theme/dist/v4/globals.css # packages/theme/dist/v4/globals.scss # packages/webkit/.size-limit.json
robson-junior-azion
approved these changes
Aug 25, 2026
guilherme-santana-azion
approved these changes
Aug 26, 2026
Uh oh!
There was an error while loading. Please reload this page.
gabriel-lisboa-azion added a commit
that referenced
this pull request
Aug 26, 2026
Brings in the 17 commits main has since 2026-08-17 — the webkit documentation category (#919), the shared leading column for Select / MultiSelect / Dropdown (#935), resizable-panel (#902), the copy-button tooltip and card-box header inset (#936), --container-site (#923), the page-enter / content-enter arrivals (#896), the colored-icon currentColor fix (#930) and the skills that landed with them. Conflict resolution, all fifteen: Demo wins where it already carries work that is still an OPEN PR on main and main's side is the older shape that PR rewrote — toast-item (#900's `h-[1lh]` line box over #892's `self-center`), use-placement and popover.test (#932's translate anchoring over #891's prose), and the theme animation tokens, where the entry-count canary confirms every main entry survived alongside demo's. Main wins where its side is a superset or the corrected form — DESIGN.md (gains `text-body-prose-md` and `.layout-column-docs`), package.json (keeping demo's footer block would have duplicated the four keys main already merged in above it), and resizable-panel.test, whose named imports are the form that keeps CodeQL from reading a compiler-generated `.vue` default export as undefined. Both sides where each added something: .size-limit.json keeps demo's ratcheted 1.1 KB footer-root budget and takes main's fourteen doc-* entries; the ui-craft skill and the CLI plan keep demo's microcopy routing without duplicating main's entry. Generated artifacts were rebuilt rather than hand-resolved — theme dist via build:tokens, catalog.json via catalog:build — and both carry each side's tokens and all 258 exports.
isaque-bock-azion added a commit
that referenced
this pull request
Aug 28, 2026
#939) * feat(theme): add --container-site, the marketing site's shared measure (#923) * feat(theme): add --container-site, the marketing site's shared measure The azion.com pages are a single vertical frame: the nav bar, the hero's inner column, every section below it and the footer all sit on the same centred column, and the border-x that runs down the page is only continuous because those four agree to the pixel. A width re-typed per band cannot hold that — the frame is one decision, so it gets one place to live. `5xl` (1192px) is a rung of the shared container ladder, not a number chosen for this page. It is its own token rather than a reference to one of the console measures because the two answer different questions: a console measure caps a payload (a table, a form), while this one is the width of a frame whose four bands have to line up, so retuning either must not move the other. * refactor(webkit): point the footer column at --container-site (#928) The footer's column named `--container-5xl` directly. On the marketing site the nav bar, the hero band, the framed sections and this footer are ONE vertical frame, and its side rules only meet because all four resolve to the same width — so the measure has to be a token they share, not a rung each of them names. Retuning it now moves them together, which a hardcoded rung here could not do. No visual change: `--container-site` resolves to `--container-5xl`, which is what the class already said. * feat(webkit): add the resizable-panel layout primitive (#902) * feat(webkit): add the resizable-panel layout primitive A group of adjacent regions whose shared edges the reader can drag — the layout behind a workspace, where an editor, a terminal under it and a preview beside it are all content and the split between them is the reader's to set. It exists because `Sidebar` was being used for both jobs. `Sidebar` is the app's NAVIGATION rail: it collapses to icons, it hides, it is the thing you leave to go somewhere. A terminal, a preview and a code editor are not places you navigate to — they are the work — and dressing two of them as navigation rails produced a screen with three "sidebars" and one document, on an axis `Sidebar` does not even have: it has no horizontal edge, so a terminal under an editor had to hand-roll its own splitter. This component owns the EDGE; `Sidebar` goes back to owning navigation. Composition: `ResizablePanel` with `Pane` and `Handle`, attached for dot-notation and exported standalone. The compound is built by naming the static surface and casting once rather than with `Object.assign`, whose inferred type walks each part's local `interface Props` into the declaration and trips TS4082. Ships the spec, a browser-mode test suite, the story, the `-root` and per-sub-component exports, and a size budget. * test(storybook): regenerate linux baselines for resizable-panel Adds the 24 snapshots for the four new ResizablePanel stories across the 6-mode matrix. Generated by the Storybook Regenerate Baseline workflow on this branch's HEAD — baselines are ubuntu-only, so a local run would not match the folder the visual gate compares against. No existing snapshot is modified or deleted; the component is additive. * test(webkit): compose resizable-panel through its named sub-component exports Every other composition suite in the package renders through the named exports (`AccordionItem`, `DropdownTrigger`, …) and checks dot-notation once, as an identity assertion against those same exports. This file was the only one that rendered through the root's statics — `h(ResizablePanel.Pane, …)` — which made static analysis flag eight unguarded member accesses: a `.vue` default export is generated by the SFC compiler, so the analyzer sees no `export default`, reads the imported binding as undefined, and reports every `.Pane` / `.Handle` read on it. Compose through `ResizablePanelPane` / `ResizablePanelHandle` instead, and keep the compound contract in the one place it is the subject rather than the vehicle, asserting identity against those exports through a guarded read. Same nine tests, same behaviour covered — the dot-notation contract is now verified more tightly than `toBeTruthy` did. --------- Co-authored-by: robson-junior-azion <robson.junior@azion.com> * feat(webkit): give Select, MultiSelect and Dropdown one shared leading column (#935) A list that mixes iconed and icon-less options used to run two label edges: an option with a glyph indented its text, one without it did not, so the eye had no column to read down. Fixing it per-option is not possible — no option knows whether any sibling has an icon. So the reservation is made by the LIST, with a self-triggering `:has()` rule. Every option renders a fixed leading box and marks itself `data-leading` only when it actually has a glyph; the box is `hidden` and turns back on under `group-has-[[data-leading]]/options:flex`. `:has()` matches the marker even while the box is hidden, so one iconed option is enough to open the column for the whole list — and a list with no icons at all reserves nothing and pays no indent. The same `group/options` marker is on all three panels, so Select, MultiSelect and Dropdown behave identically. The group label is now OPTIONAL, and sentence-case `text-label-sm` rather than an uppercase overline: an unlabelled group is a grouping the reader does not need named, and an overline read as a second heading level inside a panel that has none. Group rhythm is `--spacing-sm` above every group but the first — the same step CommandMenu already puts between two bare groups. Dropdown keeps its hairline and Select does not: the Figma frame for the Select panel separates groups by space alone. Dropdown's divider moves to the group root, which spans the panel's full inner width, so it reads edge-to-edge like the panel's own border; the horizontal inset the rows need moves onto the content wrapper. Panels also gain top/bottom slots for content that is not an option (a search row, a footer action), and the panel's vertical padding steps up one so the first and last row do not crowd its border. Includes the 10 dropdown visual baselines this moves. Co-authored-by: robson-junior-azion <robson.junior@azion.com> * fix(icons): resolve currentColor per theme in the colored-icon CSS (#930) Astro's "A" shipped invisible on every light-mode surface. Some brand logos are only partly colored: Astro is a fixed red-to-magenta gradient swoosh under an "A" that is the brand's INK — black on a light background, white on a dark one. That part is authored as `fill="currentColor"`, which is what `validate-svg.mjs` asks for, and it resolves correctly wherever the SVG is INLINED (dist/color-catalog.json → the gallery, Storybook). A `background-image` is a separate document, so it inherits nothing from the element painting it. `currentColor` there resolves against the SVG's own root, not the consumer's text color, and no amount of `color` on the `<i>` reaches it. Left alone it lands on the UA default and the icon disappears in one theme — which is what happened: `ai-astro.svg` had the "A" hardcoded `#FAFAFA`, so it was white-on-white everywhere in light mode. So the generator resolves it. An icon containing `currentColor` is emitted twice: the base rule with the light ink, and an override with the dark ink under the same selectors @aziontech/theme uses for its dark tokens (`[data-theme=dark]`, `.dark`, `.azion.azion-dark`). The override's extra selector out-specifies the base, so source order does not decide the winner. The inks are literals (`#141414` / `#FAFAFA`) rather than `var(--text-default)` by necessity — same document isolation — and are the theme's own values for that token in each mode. A fully-colored logo with no ink parts still emits one rule, exactly as before: 17 colored icons, 1 theme-aware. Co-authored-by: robson-junior-azion <robson.junior@azion.com> * docs(webkit): ship the webkit-create-surface and webkit-errors skills (#895) * docs(webkit): ship the webkit-create-surface and webkit-errors skills webkit-create-surface fixes where a create or settings form lives — a first-level resource creates on a page, anything created inside a resource opens a drawer, nothing creates in a dialog — plus the anatomy all of them share, what belongs behind the collapsed Advanced band, and the commit model each surface uses. webkit-errors is the one decision table for where a failure goes: client-side validation on the field, a server rejection scoped to one field as a Message in that section, an unscoped request failure as a toast with Retry, the signed-out screens where auth never toasts, and the async failure that outlives its screen. Both join the CLI bundle and the ui-craft routing table; webkit-form and webkit-ui-states now hand their error rows off to webkit-errors. * docs(webkit): ship the webkit-microcopy consumer skill (#931) The writing rules for Azion product interfaces, as a skill the adoption toolkit ships — so a consuming project's agent writes the same strings the console does instead of inventing a house style per screen. It fixes the things that were being decided per-PR: punctuation and capitalization (no em dash, no ampersand, no parentheses in labels, no bold in running text, sentence case with Azion product names capitalized), which surface each kind of copy belongs on (a label names, a description constrains, a Message explains impact, a Popover defines, a Tag states), one word per concept (Select not Pick, Application not Edge Application, and no `edge` as a prefix or a location), navigation and IA naming, and copy that survives every breakpoint. Registered in the three places a consumer skill has to be registered or it never reaches a project: the CLI's file list (`plan.js`), the UI-craft pack in `plan.test.mjs` (now 18 skills), and the routing index in `CLAUDE.fragment.md`, where it gets its own **Words** line — it is the only skill about strings rather than structure, and it applies during every other one. Distinct from the generic `content-microcopy` the plan drops: that one is house-agnostic, this one carries Azion's own vocabulary. --------- Co-authored-by: Isaque Böck <isaque.santos@azion.com> Co-authored-by: robson-junior-azion <robson.junior@azion.com> * feat(theme): add the page-enter and content-enter arrival animations (#896) * feat(theme): add the page-enter and content-enter arrival animations The two entrances an app shell needs, as a pair. page-enter is the route transition: it starts one --layout-boundary-inline to the LEFT of where it lands and travels right while fading up, so a page reads as arriving FROM the nav row that was clicked — a plain fade has no origin and a right-side arrival reads as going back. content-enter is content settling inside a page already on screen, so it rises a hair instead of travelling, and takes a --content-enter-delay stagger (with backwards fill, or a delayed follower would paint its landed state first). Neither fills forwards: page-enter's box is usually the scroll container, and a lingering translate would make it a containing block for any fixed descendant. * docs(webkit): teach the two arrival animations in webkit-motion-polish Documents both utilities and their knobs, and the failure they exist to prevent: a page arrives once, so a mount-time entrance inside a page whose route transition is still running lockesteps with it into one diagonal move (identical opacities frame for frame is the tell). Adds that shape and the two-shells-per- screen variant to the silent-failure table. catalog.json rebuilt so the two animations reach the MCP and validate-spec-compliance. --------- Co-authored-by: robson-junior-azion <robson.junior@azion.com> * docs: rewrite add-animation for the CSS-first theme (#897) The steps still described the deleted v3 plugin (semantic/animations.js, addUtilities/addComponents, the camelCase keyframe object form). An animation is now two edits under primitives/animations/ — the shorthand in animate.js, which is what makes animate-<name> a utility at all, and the CSS-string keyframes in keyframes.js, which Tailwind v4 does NOT generate — compiled by build:tokens, which must run before catalog:build or nothing renders. Also records the three things the two new arrivals taught: a per-instance value rides a var() with a token default inside the keyframe, a stagger rides a var() delay with backwards fill, and fill is a decision — forwards leaves a translate that makes the element a containing block for any fixed descendant. Co-authored-by: robson-junior-azion <robson.junior@azion.com> * fix(webkit): give table cells block padding so tall content can breathe (#899) A cell was `min-h-12` with horizontal padding only. For a single-line cell that is invisible — the 48px minimum decides the height on its own against ~20px of text — so every existing row renders exactly as before. It only shows on a cell whose content is TALLER than the minimum: a wrapped list of tags, a two-line summary. There the height came from the content alone, which put the first and last lines flush against the row's borders with no breathing room at all. Co-authored-by: robson-junior-azion <robson.junior@azion.com> * docs(webkit): bring webkit-lists onto the current list-page shape (#929) The shipped consumer skill still described the list page as it was before three decisions landed, so a consumer following it built a page the product no longer looks like: - A first-level module index opens with a `PageHeading` again, and the module's create action lives in it. The shell draws the breadcrumb only from the second crumb up, so at first level the heading is the ONLY place the module's name appears in the content. - The size ladder is the hierarchy: the heading's action is the page's one `large` (40px) control and the whole controls row is `medium` (32px). At 40px both rows competed and the create button stopped being the page's answer. - The controls row is narrowing only — `#actions` there is for an action on the LISTING, not the page's own. `last_updated` moves to 2026-08-24. Co-authored-by: robson-junior-azion <robson.junior@azion.com> * feat: webkit documentation (#919) * chore: [ENG-47606] add the documentation component category (#904) * feat(theme): [ENG-47561] add the docs measure and the prose + mono body registers (#905) * chore: [ENG-47606] add the documentation component category * feat(theme): [ENG-47561] add the docs measure and the mono body register * feat(theme): [ENG-47606] add the prose body register * fix(theme): [ENG-47606] widen the docs measure to the form column The documentation column was capped at 752px, narrower than every other measure in the system. Docs pages carry code blocks, card grids and tables, none of which fit a prose measure, so the column now takes the same 1192px the form measure uses. The token is defined here, so it is set here: the doc component stack carries the same value, and an add/add disagreement between the two made every PR in that stack unmergeable. * feat(webkit): [ENG-47616] add DocOnThisPage, the documentation TOC rail (#918) * chore: [ENG-47606] add the documentation component category * feat(theme): [ENG-47561] add the docs measure and the mono body register * feat(theme): [ENG-47606] add the prose body register * feat(webkit): [ENG-47606] add DocProse, the documentation typography contract * test(storybook): add the linux baselines for the DocProse stories * fix(theme): [ENG-47606] widen the docs measure to the form column The documentation column was capped at 752px, narrower than every other measure in the system. Docs pages carry code blocks, card grids and tables, none of which fit a prose measure, so the column now takes the same 1192px the form measure uses. * fix(webkit): [ENG-47606] pin the prose section step and separate the heading registers The section rung was a descending pair that read 32 on a phone and on a laptop and only reached 48 at xl, so the same page broke its sections at different gaps by window width. It is now a flat 56 to open and 48 to close, taken from the theme's primitive spacing steps. Each heading level now names two registers instead of one. On one register per level the base column collapsed: h2 was 16 (the body's own size), h3 and h4 were both 14, and between 640 and 768 the page title was overtaken by its own h2. Weight and leading are no longer restated here — every element takes its type token verbatim, so the ladder is size and space only. * feat(webkit): [ENG-47607] add DocCallout, the documentation admonition * test(storybook): add the linux baselines for the DocProse and DocCallout stories * fix(webkit): [ENG-47607] step an inline callout link to the only other weight the system defines font-medium is 500, a weight no text token in the theme uses — the set is light (300) and normal (400). An inline link inside a callout now steps to 400. * feat(webkit): [ENG-47611] add DocCard, the documentation grid cell * test(storybook): add the linux baselines for the documentation stories * feat(webkit): [ENG-47612] add DocCardGroup, the documentation card grid * test(storybook): add the linux baselines for the doccardgroup stories * feat(webkit): [ENG-47609] add DocItem, the documentation list row * test(storybook): add the linux baselines for the docitem stories * feat(webkit): [ENG-47613] add DocSteps and DocStep, the documentation procedure list * test(storybook): add the linux baselines for the docsteps stories * feat(webkit): [ENG-47608] add DocFrame, the documentation figure with a full-screen view * test(storybook): add the linux baselines for the docframe stories * test(storybook): stop snapshotting the autoplaying DocFrame clip * feat(webkit): [ENG-47615] add DocUpdate and the heading-nav composable * test(storybook): add the linux baselines for the docupdate stories * feat(webkit): [ENG-47561] add DocPrompt, the copyable agent prompt * test(storybook): add the linux baselines for the docprompt stories * feat(webkit): [ENG-47610] add DocTooltip, the inline gloss * test(storybook): add the linux baselines for the doctooltip stories * fix(webkit): [ENG-47610] let the tooltip headline take its type token's weight The headline restated font-medium (500), a weight no text token in the theme defines. It now takes text-body-sm verbatim, and the step to the tip below it is carried by ink colour. * feat(webkit): [ENG-47618] add DocPagination, the documentation prev/next pair * test(storybook): add the linux baselines for the docpagination stories * feat(webkit): [ENG-47617] add DocPageHeader, the documentation masthead * test(storybook): add the linux baselines for the docpageheader stories * fix(webkit): [ENG-47617] stop the page title being overtaken by its own h2 text-heading-lg runs 18/18/30, so between 640 and 768 the masthead title rendered smaller than the prose h2 under it (20). The title now takes the same descending pair the prose h1 uses — text-heading-2xl with text-heading-xl from sm — which reads 30/30/36 and stays above every heading below it. Closes the theme gap the spec recorded. The deck stops restating font-light; text-body-md already resolves to 300. * feat(webkit): [ENG-47616] add DocOnThisPage, the documentation TOC rail * test(storybook): add the linux baselines for the doconthispage stories * fix(webkit): [ENG-47616] mark the rail's opening line so a shell can align to it A docs shell aligns the outline rail against the page bar beside it, and needed a stable thing to measure. data-toc-title marks the rail's first line so the shell reaches it without depending on this component's internal structure. * style(webkit): sort the use-heading-nav imports * test(storybook): regenerate the documentation baselines after the typography fixes --------- Co-authored-by: Gab <gabriel.mendonca@azion.com> * fix(webkit): copy-button tooltip, card-box header inset, flow attrs (#936) * fix(webkit): copy-button tooltip, card-box header inset, flow attrs Three unrelated small fixes. COPY-BUTTON COMPOSES TOOLTIP. An icon-only button says nothing to a pointer user until it is hovered, so the glyph was unlabelled in practice — and the "Copied" confirmation existed only as an accessible-name swap, announced to a screen reader and invisible to everyone else. One string now names the control AND labels its tooltip, so the two can never disagree: `ariaLabel` while idle, `copiedLabel` for the two seconds after a write. The confirmation is visible because it rides the same state. CARD-BOX HEADER INSET `sm` → `md`. The header's horizontal padding now matches the content's `p-(--spacing-md)` and the footer's `px-(--spacing-md)`, so the title sits on the same vertical line as the first thing under it — and, in a flush card, as the rows of whatever fills it (ItemList, Accordion and LogView are all inset `--spacing-md`). At `--spacing-sm` every card header sat 4px left of its own body. FLOW FORWARDS `$attrs`. It declared `inheritAttrs: false` and then never spread `$attrs` on its root, merging only `attrs.class`. Everything else a consumer passed — `id`, `aria-*`, `data-*`, listeners — was silently dropped, which `.claude/rules/root-element.md` exists to prevent. Same shape as its siblings now: `v-bind="$attrs"` on the real root, with `cn` still merging the class. * test(storybook): regenerate the card-box baselines for the header inset * chore(webkit): raise the doc-prompt budget for the copy-button tooltip DocPrompt composes CopyButton, which now wraps its IconButton in the Tooltip overlay so the icon-only control is legible on hover. The overlay machinery raises the measured entry from 6.20 KB to 8.61 KB; the budget moves to 9 KB to carry that composition with headroom. --------- Co-authored-by: Isaque Böck <isaque.santos@azion.com> * fix(webkit): emit navigate when a collapsed breadcrumb crumb is picked `select` is the Dropdown's event, not the option's: `Dropdown.Option` forwards its activation to the root through the injected context, and the root is what emits `select(event, value)`. The listener sat on the option, where it never fired — so picking a collapsed crumb below `md` did nothing at all. The menu closed and the reader stayed on the page. That is the whole trail on a phone, since the collapsed shape is the only one a narrow bar renders. The test that covered this was skipped as a harness limitation ("@select does not fire under test interaction"). It was not a harness limitation — it was this bug, reproducing exactly as a real browser did. The listener moved to the root and the test is un-skipped. --------- Co-authored-by: robson-junior-azion <robson.junior@azion.com> Co-authored-by: Isaque Böck <isaque.santos@azion.com>
isaque-bock-azion added a commit
that referenced
this pull request
Aug 28, 2026
* feat(webkit): let a table column declare a minWidth floor A bounded column — a chip list, a date, a count, an id — could only be sized by a fixed `width`, and a fixed width is a bet in both directions: guess high and the column reserves space the rest of the row needs, guess low and the content inside it truncates (a chip list ends up as `SQL In…`). `minWidth` only has to be right about the minimum: the column resolves to the widest of that floor and what it actually holds — its header, with the sort affordance, and every rendered body cell. `width` still wins when both are set, so an explicit width remains the consumer overriding the measurement outright. ONE width is resolved per column and applied to the header and to every body cell, which keeps the invariant fixed widths exist for: the column resolves identically in every row. A per-cell `min-width` would let each row size to its own content and drift the header away from the body. It has to be measured, because a flex row cannot express "as wide as the widest cell in this column" — every row is its own flex container and knows nothing about its siblings. The measurement is the same clone-based one the resize floor already uses, so a column's auto width and its drag floor can never disagree. Three things the measurement has to respect: - Debounced (32ms). The rendered content changes in bursts — a sort, a page turn, a column toggled back on — and measuring reads layout, so an un-coalesced version would reflow per keystroke of a filter. - Skipped while `loading`. Skeleton rows are not the content; measuring against them would size every column to a placeholder and never grow it back. - Re-run on `document.fonts.ready`. Text width is font-dependent and the first paint can happen on the fallback face. `autoWidths` joins the row `v-memo` so a resolved width actually reaches the body, and feeds `frozenWidthOf` so a frozen `minWidth` column contributes its real width to a neighbour's sticky offset. * test(storybook): regenerate the table baselines for the column min-width * fix(webkit): emit navigate when a collapsed breadcrumb crumb is picked (#939) * feat(theme): add --container-site, the marketing site's shared measure (#923) * feat(theme): add --container-site, the marketing site's shared measure The azion.com pages are a single vertical frame: the nav bar, the hero's inner column, every section below it and the footer all sit on the same centred column, and the border-x that runs down the page is only continuous because those four agree to the pixel. A width re-typed per band cannot hold that — the frame is one decision, so it gets one place to live. `5xl` (1192px) is a rung of the shared container ladder, not a number chosen for this page. It is its own token rather than a reference to one of the console measures because the two answer different questions: a console measure caps a payload (a table, a form), while this one is the width of a frame whose four bands have to line up, so retuning either must not move the other. * refactor(webkit): point the footer column at --container-site (#928) The footer's column named `--container-5xl` directly. On the marketing site the nav bar, the hero band, the framed sections and this footer are ONE vertical frame, and its side rules only meet because all four resolve to the same width — so the measure has to be a token they share, not a rung each of them names. Retuning it now moves them together, which a hardcoded rung here could not do. No visual change: `--container-site` resolves to `--container-5xl`, which is what the class already said. * feat(webkit): add the resizable-panel layout primitive (#902) * feat(webkit): add the resizable-panel layout primitive A group of adjacent regions whose shared edges the reader can drag — the layout behind a workspace, where an editor, a terminal under it and a preview beside it are all content and the split between them is the reader's to set. It exists because `Sidebar` was being used for both jobs. `Sidebar` is the app's NAVIGATION rail: it collapses to icons, it hides, it is the thing you leave to go somewhere. A terminal, a preview and a code editor are not places you navigate to — they are the work — and dressing two of them as navigation rails produced a screen with three "sidebars" and one document, on an axis `Sidebar` does not even have: it has no horizontal edge, so a terminal under an editor had to hand-roll its own splitter. This component owns the EDGE; `Sidebar` goes back to owning navigation. Composition: `ResizablePanel` with `Pane` and `Handle`, attached for dot-notation and exported standalone. The compound is built by naming the static surface and casting once rather than with `Object.assign`, whose inferred type walks each part's local `interface Props` into the declaration and trips TS4082. Ships the spec, a browser-mode test suite, the story, the `-root` and per-sub-component exports, and a size budget. * test(storybook): regenerate linux baselines for resizable-panel Adds the 24 snapshots for the four new ResizablePanel stories across the 6-mode matrix. Generated by the Storybook Regenerate Baseline workflow on this branch's HEAD — baselines are ubuntu-only, so a local run would not match the folder the visual gate compares against. No existing snapshot is modified or deleted; the component is additive. * test(webkit): compose resizable-panel through its named sub-component exports Every other composition suite in the package renders through the named exports (`AccordionItem`, `DropdownTrigger`, …) and checks dot-notation once, as an identity assertion against those same exports. This file was the only one that rendered through the root's statics — `h(ResizablePanel.Pane, …)` — which made static analysis flag eight unguarded member accesses: a `.vue` default export is generated by the SFC compiler, so the analyzer sees no `export default`, reads the imported binding as undefined, and reports every `.Pane` / `.Handle` read on it. Compose through `ResizablePanelPane` / `ResizablePanelHandle` instead, and keep the compound contract in the one place it is the subject rather than the vehicle, asserting identity against those exports through a guarded read. Same nine tests, same behaviour covered — the dot-notation contract is now verified more tightly than `toBeTruthy` did. --------- Co-authored-by: robson-junior-azion <robson.junior@azion.com> * feat(webkit): give Select, MultiSelect and Dropdown one shared leading column (#935) A list that mixes iconed and icon-less options used to run two label edges: an option with a glyph indented its text, one without it did not, so the eye had no column to read down. Fixing it per-option is not possible — no option knows whether any sibling has an icon. So the reservation is made by the LIST, with a self-triggering `:has()` rule. Every option renders a fixed leading box and marks itself `data-leading` only when it actually has a glyph; the box is `hidden` and turns back on under `group-has-[[data-leading]]/options:flex`. `:has()` matches the marker even while the box is hidden, so one iconed option is enough to open the column for the whole list — and a list with no icons at all reserves nothing and pays no indent. The same `group/options` marker is on all three panels, so Select, MultiSelect and Dropdown behave identically. The group label is now OPTIONAL, and sentence-case `text-label-sm` rather than an uppercase overline: an unlabelled group is a grouping the reader does not need named, and an overline read as a second heading level inside a panel that has none. Group rhythm is `--spacing-sm` above every group but the first — the same step CommandMenu already puts between two bare groups. Dropdown keeps its hairline and Select does not: the Figma frame for the Select panel separates groups by space alone. Dropdown's divider moves to the group root, which spans the panel's full inner width, so it reads edge-to-edge like the panel's own border; the horizontal inset the rows need moves onto the content wrapper. Panels also gain top/bottom slots for content that is not an option (a search row, a footer action), and the panel's vertical padding steps up one so the first and last row do not crowd its border. Includes the 10 dropdown visual baselines this moves. Co-authored-by: robson-junior-azion <robson.junior@azion.com> * fix(icons): resolve currentColor per theme in the colored-icon CSS (#930) Astro's "A" shipped invisible on every light-mode surface. Some brand logos are only partly colored: Astro is a fixed red-to-magenta gradient swoosh under an "A" that is the brand's INK — black on a light background, white on a dark one. That part is authored as `fill="currentColor"`, which is what `validate-svg.mjs` asks for, and it resolves correctly wherever the SVG is INLINED (dist/color-catalog.json → the gallery, Storybook). A `background-image` is a separate document, so it inherits nothing from the element painting it. `currentColor` there resolves against the SVG's own root, not the consumer's text color, and no amount of `color` on the `<i>` reaches it. Left alone it lands on the UA default and the icon disappears in one theme — which is what happened: `ai-astro.svg` had the "A" hardcoded `#FAFAFA`, so it was white-on-white everywhere in light mode. So the generator resolves it. An icon containing `currentColor` is emitted twice: the base rule with the light ink, and an override with the dark ink under the same selectors @aziontech/theme uses for its dark tokens (`[data-theme=dark]`, `.dark`, `.azion.azion-dark`). The override's extra selector out-specifies the base, so source order does not decide the winner. The inks are literals (`#141414` / `#FAFAFA`) rather than `var(--text-default)` by necessity — same document isolation — and are the theme's own values for that token in each mode. A fully-colored logo with no ink parts still emits one rule, exactly as before: 17 colored icons, 1 theme-aware. Co-authored-by: robson-junior-azion <robson.junior@azion.com> * docs(webkit): ship the webkit-create-surface and webkit-errors skills (#895) * docs(webkit): ship the webkit-create-surface and webkit-errors skills webkit-create-surface fixes where a create or settings form lives — a first-level resource creates on a page, anything created inside a resource opens a drawer, nothing creates in a dialog — plus the anatomy all of them share, what belongs behind the collapsed Advanced band, and the commit model each surface uses. webkit-errors is the one decision table for where a failure goes: client-side validation on the field, a server rejection scoped to one field as a Message in that section, an unscoped request failure as a toast with Retry, the signed-out screens where auth never toasts, and the async failure that outlives its screen. Both join the CLI bundle and the ui-craft routing table; webkit-form and webkit-ui-states now hand their error rows off to webkit-errors. * docs(webkit): ship the webkit-microcopy consumer skill (#931) The writing rules for Azion product interfaces, as a skill the adoption toolkit ships — so a consuming project's agent writes the same strings the console does instead of inventing a house style per screen. It fixes the things that were being decided per-PR: punctuation and capitalization (no em dash, no ampersand, no parentheses in labels, no bold in running text, sentence case with Azion product names capitalized), which surface each kind of copy belongs on (a label names, a description constrains, a Message explains impact, a Popover defines, a Tag states), one word per concept (Select not Pick, Application not Edge Application, and no `edge` as a prefix or a location), navigation and IA naming, and copy that survives every breakpoint. Registered in the three places a consumer skill has to be registered or it never reaches a project: the CLI's file list (`plan.js`), the UI-craft pack in `plan.test.mjs` (now 18 skills), and the routing index in `CLAUDE.fragment.md`, where it gets its own **Words** line — it is the only skill about strings rather than structure, and it applies during every other one. Distinct from the generic `content-microcopy` the plan drops: that one is house-agnostic, this one carries Azion's own vocabulary. --------- Co-authored-by: Isaque Böck <isaque.santos@azion.com> Co-authored-by: robson-junior-azion <robson.junior@azion.com> * feat(theme): add the page-enter and content-enter arrival animations (#896) * feat(theme): add the page-enter and content-enter arrival animations The two entrances an app shell needs, as a pair. page-enter is the route transition: it starts one --layout-boundary-inline to the LEFT of where it lands and travels right while fading up, so a page reads as arriving FROM the nav row that was clicked — a plain fade has no origin and a right-side arrival reads as going back. content-enter is content settling inside a page already on screen, so it rises a hair instead of travelling, and takes a --content-enter-delay stagger (with backwards fill, or a delayed follower would paint its landed state first). Neither fills forwards: page-enter's box is usually the scroll container, and a lingering translate would make it a containing block for any fixed descendant. * docs(webkit): teach the two arrival animations in webkit-motion-polish Documents both utilities and their knobs, and the failure they exist to prevent: a page arrives once, so a mount-time entrance inside a page whose route transition is still running lockesteps with it into one diagonal move (identical opacities frame for frame is the tell). Adds that shape and the two-shells-per- screen variant to the silent-failure table. catalog.json rebuilt so the two animations reach the MCP and validate-spec-compliance. --------- Co-authored-by: robson-junior-azion <robson.junior@azion.com> * docs: rewrite add-animation for the CSS-first theme (#897) The steps still described the deleted v3 plugin (semantic/animations.js, addUtilities/addComponents, the camelCase keyframe object form). An animation is now two edits under primitives/animations/ — the shorthand in animate.js, which is what makes animate-<name> a utility at all, and the CSS-string keyframes in keyframes.js, which Tailwind v4 does NOT generate — compiled by build:tokens, which must run before catalog:build or nothing renders. Also records the three things the two new arrivals taught: a per-instance value rides a var() with a token default inside the keyframe, a stagger rides a var() delay with backwards fill, and fill is a decision — forwards leaves a translate that makes the element a containing block for any fixed descendant. Co-authored-by: robson-junior-azion <robson.junior@azion.com> * fix(webkit): give table cells block padding so tall content can breathe (#899) A cell was `min-h-12` with horizontal padding only. For a single-line cell that is invisible — the 48px minimum decides the height on its own against ~20px of text — so every existing row renders exactly as before. It only shows on a cell whose content is TALLER than the minimum: a wrapped list of tags, a two-line summary. There the height came from the content alone, which put the first and last lines flush against the row's borders with no breathing room at all. Co-authored-by: robson-junior-azion <robson.junior@azion.com> * docs(webkit): bring webkit-lists onto the current list-page shape (#929) The shipped consumer skill still described the list page as it was before three decisions landed, so a consumer following it built a page the product no longer looks like: - A first-level module index opens with a `PageHeading` again, and the module's create action lives in it. The shell draws the breadcrumb only from the second crumb up, so at first level the heading is the ONLY place the module's name appears in the content. - The size ladder is the hierarchy: the heading's action is the page's one `large` (40px) control and the whole controls row is `medium` (32px). At 40px both rows competed and the create button stopped being the page's answer. - The controls row is narrowing only — `#actions` there is for an action on the LISTING, not the page's own. `last_updated` moves to 2026-08-24. Co-authored-by: robson-junior-azion <robson.junior@azion.com> * feat: webkit documentation (#919) * chore: [ENG-47606] add the documentation component category (#904) * feat(theme): [ENG-47561] add the docs measure and the prose + mono body registers (#905) * chore: [ENG-47606] add the documentation component category * feat(theme): [ENG-47561] add the docs measure and the mono body register * feat(theme): [ENG-47606] add the prose body register * fix(theme): [ENG-47606] widen the docs measure to the form column The documentation column was capped at 752px, narrower than every other measure in the system. Docs pages carry code blocks, card grids and tables, none of which fit a prose measure, so the column now takes the same 1192px the form measure uses. The token is defined here, so it is set here: the doc component stack carries the same value, and an add/add disagreement between the two made every PR in that stack unmergeable. * feat(webkit): [ENG-47616] add DocOnThisPage, the documentation TOC rail (#918) * chore: [ENG-47606] add the documentation component category * feat(theme): [ENG-47561] add the docs measure and the mono body register * feat(theme): [ENG-47606] add the prose body register * feat(webkit): [ENG-47606] add DocProse, the documentation typography contract * test(storybook): add the linux baselines for the DocProse stories * fix(theme): [ENG-47606] widen the docs measure to the form column The documentation column was capped at 752px, narrower than every other measure in the system. Docs pages carry code blocks, card grids and tables, none of which fit a prose measure, so the column now takes the same 1192px the form measure uses. * fix(webkit): [ENG-47606] pin the prose section step and separate the heading registers The section rung was a descending pair that read 32 on a phone and on a laptop and only reached 48 at xl, so the same page broke its sections at different gaps by window width. It is now a flat 56 to open and 48 to close, taken from the theme's primitive spacing steps. Each heading level now names two registers instead of one. On one register per level the base column collapsed: h2 was 16 (the body's own size), h3 and h4 were both 14, and between 640 and 768 the page title was overtaken by its own h2. Weight and leading are no longer restated here — every element takes its type token verbatim, so the ladder is size and space only. * feat(webkit): [ENG-47607] add DocCallout, the documentation admonition * test(storybook): add the linux baselines for the DocProse and DocCallout stories * fix(webkit): [ENG-47607] step an inline callout link to the only other weight the system defines font-medium is 500, a weight no text token in the theme uses — the set is light (300) and normal (400). An inline link inside a callout now steps to 400. * feat(webkit): [ENG-47611] add DocCard, the documentation grid cell * test(storybook): add the linux baselines for the documentation stories * feat(webkit): [ENG-47612] add DocCardGroup, the documentation card grid * test(storybook): add the linux baselines for the doccardgroup stories * feat(webkit): [ENG-47609] add DocItem, the documentation list row * test(storybook): add the linux baselines for the docitem stories * feat(webkit): [ENG-47613] add DocSteps and DocStep, the documentation procedure list * test(storybook): add the linux baselines for the docsteps stories * feat(webkit): [ENG-47608] add DocFrame, the documentation figure with a full-screen view * test(storybook): add the linux baselines for the docframe stories * test(storybook): stop snapshotting the autoplaying DocFrame clip * feat(webkit): [ENG-47615] add DocUpdate and the heading-nav composable * test(storybook): add the linux baselines for the docupdate stories * feat(webkit): [ENG-47561] add DocPrompt, the copyable agent prompt * test(storybook): add the linux baselines for the docprompt stories * feat(webkit): [ENG-47610] add DocTooltip, the inline gloss * test(storybook): add the linux baselines for the doctooltip stories * fix(webkit): [ENG-47610] let the tooltip headline take its type token's weight The headline restated font-medium (500), a weight no text token in the theme defines. It now takes text-body-sm verbatim, and the step to the tip below it is carried by ink colour. * feat(webkit): [ENG-47618] add DocPagination, the documentation prev/next pair * test(storybook): add the linux baselines for the docpagination stories * feat(webkit): [ENG-47617] add DocPageHeader, the documentation masthead * test(storybook): add the linux baselines for the docpageheader stories * fix(webkit): [ENG-47617] stop the page title being overtaken by its own h2 text-heading-lg runs 18/18/30, so between 640 and 768 the masthead title rendered smaller than the prose h2 under it (20). The title now takes the same descending pair the prose h1 uses — text-heading-2xl with text-heading-xl from sm — which reads 30/30/36 and stays above every heading below it. Closes the theme gap the spec recorded. The deck stops restating font-light; text-body-md already resolves to 300. * feat(webkit): [ENG-47616] add DocOnThisPage, the documentation TOC rail * test(storybook): add the linux baselines for the doconthispage stories * fix(webkit): [ENG-47616] mark the rail's opening line so a shell can align to it A docs shell aligns the outline rail against the page bar beside it, and needed a stable thing to measure. data-toc-title marks the rail's first line so the shell reaches it without depending on this component's internal structure. * style(webkit): sort the use-heading-nav imports * test(storybook): regenerate the documentation baselines after the typography fixes --------- Co-authored-by: Gab <gabriel.mendonca@azion.com> * fix(webkit): copy-button tooltip, card-box header inset, flow attrs (#936) * fix(webkit): copy-button tooltip, card-box header inset, flow attrs Three unrelated small fixes. COPY-BUTTON COMPOSES TOOLTIP. An icon-only button says nothing to a pointer user until it is hovered, so the glyph was unlabelled in practice — and the "Copied" confirmation existed only as an accessible-name swap, announced to a screen reader and invisible to everyone else. One string now names the control AND labels its tooltip, so the two can never disagree: `ariaLabel` while idle, `copiedLabel` for the two seconds after a write. The confirmation is visible because it rides the same state. CARD-BOX HEADER INSET `sm` → `md`. The header's horizontal padding now matches the content's `p-(--spacing-md)` and the footer's `px-(--spacing-md)`, so the title sits on the same vertical line as the first thing under it — and, in a flush card, as the rows of whatever fills it (ItemList, Accordion and LogView are all inset `--spacing-md`). At `--spacing-sm` every card header sat 4px left of its own body. FLOW FORWARDS `$attrs`. It declared `inheritAttrs: false` and then never spread `$attrs` on its root, merging only `attrs.class`. Everything else a consumer passed — `id`, `aria-*`, `data-*`, listeners — was silently dropped, which `.claude/rules/root-element.md` exists to prevent. Same shape as its siblings now: `v-bind="$attrs"` on the real root, with `cn` still merging the class. * test(storybook): regenerate the card-box baselines for the header inset * chore(webkit): raise the doc-prompt budget for the copy-button tooltip DocPrompt composes CopyButton, which now wraps its IconButton in the Tooltip overlay so the icon-only control is legible on hover. The overlay machinery raises the measured entry from 6.20 KB to 8.61 KB; the budget moves to 9 KB to carry that composition with headroom. --------- Co-authored-by: Isaque Böck <isaque.santos@azion.com> * fix(webkit): emit navigate when a collapsed breadcrumb crumb is picked `select` is the Dropdown's event, not the option's: `Dropdown.Option` forwards its activation to the root through the injected context, and the root is what emits `select(event, value)`. The listener sat on the option, where it never fired — so picking a collapsed crumb below `md` did nothing at all. The menu closed and the reader stayed on the page. That is the whole trail on a phone, since the collapsed shape is the only one a narrow bar renders. The test that covered this was skipped as a harness limitation ("@select does not fire under test interaction"). It was not a harness limitation — it was this bug, reproducing exactly as a real browser did. The listener moved to the root and the test is un-skipped. --------- Co-authored-by: robson-junior-azion <robson.junior@azion.com> Co-authored-by: Isaque Böck <isaque.santos@azion.com> --------- Co-authored-by: Isaque dos Santos <isaque.santos@azion.com> Co-authored-by: robson-junior-azion <robson.junior@azion.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
How to test
Notes