Repository navigation
feat(registry)!: adopt the new Remix APIs in the Vanilla preset - #342
Draft
leoafarias wants to merge 17 commits into
Draft
leoafarias wants to merge 17 commits into
leoafarias wants to merge 17 commits into
Conversation
This was referenced Oct 27, 2026
Tab, toggle, toggle group option, menu trigger, accordion, and disclosure drew their ring with a foreground decoration, which needed a second helper with its own radius and inset arguments and, on the accordion, ringed the whole section so the ring grew with the revealed panel. They now use vanillaFocusRing in containerEffects, or triggerEffects on the accordion and disclosure, so the ring follows the part's own corners, takes no layout space, and wraps only the trigger. A toggle group option rings inside itself with the new inset argument because the group clips. The vanillaFocusRingDecoration helper is removed.
RemixRadio and RemixSwitch take a label drawn inside the control's own tap target, focus ring, and accessible name. The Vanilla recipes now set it as a form label like the checkbox does, textSm at medium weight in foreground, 12px from a radio and 8px from a switch, so callers no longer compose a Row of the control and a Text. The fixture gallery and the Vanilla widgetbook radio and switch cases pass label instead.
…button The order table kebab was a Semantics-wrapped Box passed as a visual RemixMenuTrigger.builder, so it had no hover, focus ring, or disabled state of its own. It is now a ghost VanillaIconButton given to RemixMenuTrigger.control with the menu handle's focusNode and toggle, keeping the muted glyph. The menu still owns the overlay, Escape, and focus return, and merges its expanded state onto the button, which stays the one focus stop.
… trigger The using-remix Vanilla reference lists the single focus ring helper and the slots it goes in, the radio and switch label, and how to open a menu from a real button, and both it and the open-code guide tell an older install to reinstall the ring recipes with the theme now that the decoration helper is gone.
…debar The ring probe matched a drawPath call, so most cases failed against the old foreground-decoration recipes for how the ring was painted rather than for what a reader sees. It now compares the frame before and after focus: the ring's color laid over what was there at each point it must cover, and no change at the points it must not, keeping the size and neighbor checks. A sidebar destination case is added, and the state distinguishability case for the sidebar destination reads the spec as before instead of its effects layer. RemixSidebar draws destinations with RowBox, which does not paint containerEffects, so both fail until the sidebar paints them.
… trigger shadcn's default item-aligned select never draws the panel narrower than its trigger. The panel keeps its 128px minimum and adds remixAnchorWidth as a second minimum through a box modifier, so the panel is max(128, trigger) and a caller who replaces the floor through style still keeps the trigger's width.
…is open shadcn's dashboard block fills the kebab with muted while its menu is open. The button reads the menu handle's isOpen and merges that fill in, and the dashboard test checks the fill follows the menu open and closed.
…bel note The Vanilla spec and the open-code guide said the focus ring is always drawn outside with no offset. A toggle group option draws it inside because the group clips its options, where the reference lifts the focused item with focus-visible:z-10, so the spec gains a deviation row and the guide notes it. The using-remix Vanilla reference no longer says the radio and switch label sits inside the focus ring: the label is part of the tap and focus target, and the ring wraps the circle or track only. The toggle's "No shadow" comment is dropped, since a decoration shadow is no longer the reason the outline toggle has none, and the spec row says so.
The dashboard's account row is a RemixMenuTrigger.builder, and a builder replaces the styled trigger and its effects, so the row was a keyboard focus stop that showed no focus. Its content is now a RemixCard that draws vanillaFocusRing() in containerEffects on focus-visible, resolved under the menu's widget states, so the ring paints outside the row and moves nothing. It is the only builder trigger in the Vanilla recipes. The ring test focuses the real trigger in the dashboard and checks the ring pixels and that the trigger and its content keep their boxes. The menu docs' avatar builder example draws its own ring and says why, and the Vanilla reference notes it.
…Tap false The fixture and the Vanilla popover recipe docs said a button trigger needs a controller because the button consumes the tap that openOnTap waits for. NakedPopover with openOnTap false no longer wraps its child in a focus stop of its own, so the button can be the only tab stop and closing can return focus to it when the popover and the button share one FocusNode. The fixture gallery now passes openOnTap false, the shared node as triggerFocusNode, and the same node to the button, keeping the controller toggle. The fixture test asserts one focus stop inside the popover, that focus leaves the button when the panel opens, and that Escape closes the panel and returns focus to the button. The popover recipe doc comment shows the same pattern, with its template and installed copies regenerated.
The notifications and theme settings popovers in the dashboard header held a VanillaIconButton with no onPressed inside a popover that kept the default openOnTap. The popover's own wrapper was the one focus stop and the one semantics node, so the styled button inside could never take focus: keyboard focus showed no ring, and the button's own label was hidden behind the popover's. A small stateful trigger now owns the button: openOnTap is false, the button toggles a MenuController, and one FocusNode, owned and disposed by that State, is both the popover's triggerFocusNode and the button's focusNode. The button is the only focus stop, rings on keyboard focus, names itself, reports the expanded state the popover's anchor puts on it, and gets focus back when the popover closes. The popover's semanticLabel, ignored with openOnTap false, is dropped. The dashboard test checks one focus stop, the button's label and expanded state, opening and closing from the button, and focus returning on Escape for both triggers; the ring test checks the ring pixels for both.
The kebab fills with muted while its menu is open, but Mix applies the active hover and press variants after the base, so with the pointer still on the button the icon turned accentForeground and, in the dark theme, the fill became accent at half strength. The open style now repeats its fill and muted glyph in its hover and press variants, so it stays in force under the pointer. The dashboard test opens the menu with a mouse that is already hovering the button and holds a press, in both themes.
The account row is bordered, and a bordered control turns its own border ring while it has focus, as the buttons, the text field, the select trigger, and the outline toggle do. The row's focus fragment drew only the ring band, so the border stayed its resting color. The focus fragment now also sets vanillaFocusBorder(). Only the color changes, so the row keeps its box. The ring test checks the border pixel turns ring over the row's card fill.
The reference's outline toggle carries shadow-xs, and the toggle spec now has an effects layer that cuts the box out of its shadow the way CSS does, as the outline icon button, radio, and switch already use. The outline variant draws the xs shadow through containerEffects behindContent, and keeps it while the ring is drawn on focus. The spec targets read the shadow for the outline variant, no shadow for the ghost variant, and both the shadow and the ring when focused. The deviation row now covers only the toggle group strip, which has no effects layer.
…tests The popover recipe's doc example now says the controller and focus node live in a State and the node is disposed there, as the Naked UI example does, with the installed copies regenerated. The Vanilla reference gains a Popover bullet for the openOnTap false pattern. The fixture popover drops a semanticLabel that openOnTap false ignores. The header popover ring test checks the adjacent appearance toggle stays put rather than a button across the header, and a probe comment now says the captured frame has one pixel per logical pixel.
leoafarias
force-pushed
the
stack/follow-ups-integration
branch
from
October 29, 2026 21:44
c931a3a to
d5990f4
Compare
leoafarias
force-pushed
the
feat/registry-vanilla-adopt-remix-apis
branch
from
October 29, 2026 21:44
1444ab2 to
615d647
Compare
This branch has not been deployed
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 free
to 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.
Description
Draft, stacked on #339. Do not merge until remix 1.0.0-beta.14 and naked_ui 1.3.0 are on pub.dev.
Vanilla now uses the Remix APIs from the open follow-up PRs.
vanillaFocusRing()throughcontainerEffects(triggerEffectson accordion and disclosure) instead of a foreground decoration.inset: true), because the group clips.vanillaFocusRingDecorationis removed.labelas a form label (textSm, w500, foreground), with a 12px gap on a radio and 8px on a switch. The fixture and widgetbook demos passlabel.VanillaIconButtonpassed toRemixMenuTrigger.control. It has one focus stop, the menu's expanded state is merged onto the button, and the button keeps amutedfill and glyph while the menu is open, including under the pointer.max(128, trigger width). It keeps the 128px minimum and addsremixAnchorWidthas a second minimum through a box modifier.RemixCardwithcontainerEffects, and its border turnsringon focus, because aRemixMenuTrigger.builderreplaces the styled trigger and its effects. The menu docs' avatar example now does the same.openOnTap: falsewith oneFocusNodeshared by the popover and its button. The button is the only tab stop, keeps its own accessible name, shows its focus ring, and gets focus back on Escape. Before, the popover's wrapper took focus with no visible ring and announced the popover's label instead. The Vanilla popover recipe's doc comment shows the same pattern.specs/vanilla.md. The outline toggle now draws itsshadow-xsthrough the effects slot.Left as is:
RemixMenuTrigger.buildertrigger, because it is a profile row, not a button. Only its focus ring is new;semanticLabel.Restacked onto the refreshed integration hub #339 (
d5990f4d) on mainca9331be. Remains DRAFT and release-blocked until naked_ui 1.3.0 and Remix 1.0.0-beta.14 are available on pub.dev and their runtime/registry floor updates are approved. Registry dependency floors are unchanged.