Repository navigation
[Feature] ToggleGroup: match shadcn (Base UI + vega) - #650
Merged
Merged
Conversation
- Single mode renders role=group with <button aria-pressed>, like Base UI; pressing the pressed item clears the value. - Roving tabindex and arrow keys in both modes; arrows follow orientation; new loop_focus: (default true). - Emit data-slot/variant/size/spacing/pressed/disabled (+ data-multiple). - Items keep the size min-width; joined groups use px-2 and the icon padding, pressed items bg-muted (vega cn-toggle-group-item). - spacing defaults to 2, as in shadcn. - Audit map, fixtures, divergences and baseline; MCP registry rebuilt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex review: removing or disabling the tab-stop item left the group unreachable by Tab, and with loop_focus: false the arrow at an end scrolled the page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
All reported issues were addressed across 11 files
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
Cubic review: render a single tabindex=0 before Stimulus connects, normalize tab stops and rebuild hidden inputs when items change, raise on a non-boolean loop_focus, tighten the disabled assertion and document the value shape in the audit map. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ToggleGroupItem now exposes aria-pressed in both modes, so aria-pressed:bg-muted covers it (follow-up from #649). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 13 files
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.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 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.
Brings ToggleGroup in line with shadcn's Base UI build and the vega style, following a shadcn audit of the component (first audit for it, so this PR also adds its component map, fixtures and divergences).
Before and after
Columns: RubyUI on
main, RubyUI on this PR, shadcn (Base UI + vega). The fixture has a joined single group (Alignment, focus on Left), a multiple group with spacing 1, a vertical joined group in size sm, and a disabled group.What changes
Single groups now render
role="group"with<button aria-pressed>items, as Base UI does. They used to be aradiogroupofrole="radio"buttons whose arrow keys moved focus without checking anything. Pressing the pressed item of a single group now clears the value (the hidden input is sent empty).Both modes use a roving tabindex: one tab stop per group, arrow keys move between items and wrap. Multiple groups used to make every item a tab stop with no arrow keys. Arrows follow
orientation(Left/Right when horizontal, flipped in RTL; Up/Down when vertical), and the newloop_focus: falsestops them at the ends. Enter and Space are left to the native button. The controller keeps a tab stop when the current one is removed or disabled.Root and items emit shadcn's
data-slot,data-variant,data-size,data-spacing,data-pressedanddata-disabled(plusdata-multipleanddata-orientationon the root). Classes are still chosen in Ruby.data-stateanddata-valuestay because the controller and the Toggle classes read them.ToggleGroupItemno longer setspx-3 w-auto min-w-0, so items keep the size's min-width (36px default, 32px sm). Joined groups (spacing: 0) get vega'spx-2, the inline icon padding anddata-[state=on]:bg-muted.spacingnow defaults to 2, as in shadcn. The docs' Spacing example showsspacing: 0next to the default.Breaking changes
ToggleGroupwithoutspacing:renders separated items (gap-2). Passspacing: 0to keep the joined look.aria-pressedinstead ofrole="radio"/aria-checked, and clicking the pressed item clears the selection. Code or tests that query[role=radio]oraria-checkedneed updating.Toggledropsdata-[state=on]:bg-muted. An app that copies the new Toggle but keeps an older ToggleGroup (radio items withdata-state, noaria-pressed) loses the pressed background, so update Toggle and ToggleGroup together. Installing or updating ToggleGroup already pulls Toggle in throughdependencies.yml.Toggle (#649)
Part of what the audit found lived in
RubyUI::Toggle, which ToggleGroupItem inherits (gap, padding, pressed and hover colors, the dark invalid ring, inline icon padding). #649 fixed those and is merged into this branch. With it, the items match vega element by element in light and dark.#649 kept
data-[state=on]:bg-mutedinToggleonly so the old radio-mode ToggleGroup items kept their pressed style. Items now always carryaria-pressed, so this PR drops that class andaria-pressed:bg-mutedcovers them.Deliberate differences
Registered in
audit/divergences/toggle_group.yml:valueis the initial value and the state reaches forms through hidden inputs (name). There is noonValueChangeorpressed/onPressedChange, since Phlex has no controlled state.One more difference follows the APG instead of shadcn: shadcn passes
orientationto the DOM asdata-orientationbut not to the Base UI primitive, so its vertical groups still navigate with Left/Right. RubyUI uses Up/Down there (APG toolbar keyboard interaction).Follow-ups
spacingnumber through--gap; RubyUI still accepts 0..4. Opening it up would mean switching togap-[--spacing(var(--gap))]and an inline style.loop_focus: false, tab stop repair). The gem has no JS test runner anddocs/test/systemisn't wired into CI; this PR checked those paths with Playwright against the audit fixture.Review
Codex reviewed the diff against
mainand found two issues, both fixed here: removing or disabling the tab-stop item left the group unreachable by Tab, and withloop_focus: falsean arrow key at the end scrolled the page.Cubic's first round (six findings) is fixed too: the server HTML has a single tab stop, the controller normalizes tab stops and rebuilds hidden inputs when items change, and
loop_focusonly accepts true or false.How to test
cd gem && bundle exec rake;cd docs && bin/rails test;cd audit && bundle exec rake;cd mcp && bundle exec rake.bin/devindocs/, open/docs/toggle_group.Audit checklist on
mainAudit checklist on this PR
🤖 Generated with Claude Code