Repository navigation
UI Consistency: 1 issue found
apps/web/src/components/settings/KeybindingsSettings.tsx — KeybindingRowMenu returns null when a row has no reset/remove action. The removed table reserved a fixed 60px status column, so the keybinding control column stayed aligned regardless; inside the new SettingsRow control cluster (flex … gap-2 sm:justify-end) the menu is the trailing item, so Default rows render their shortcut pill/Input ~36px further right than Custom rows, producing a ragged right edge down the list. SettingsRow itself reserves a fixed h-5 w-5 slot for resetAction for exactly this reason; suggested keeping the slot occupied with a size-7 shrink-0 placeholder.
Inline comment posted on the affected line.
Details
Note
Your check run agent prompt is: .macroscope/check-run-agents/ui-consistency.md
More information about how Check Run Agents work can be found in our Help Center.
Reviewed at head b7a129d.
Items checked and found acceptable:
- Shared primitives:
Input size="compact"now used for both key inputs (className lands on the wrapper span, which owns border/background, so the recordingborder-primary/70 bg-primary/5still renders);Badge variant="outline" size="sm"andButton size="compact"/size="icon-sm"usages match existing contracts. - Prior-run findings appear resolved: the pill's
aria-labelnow includesformatShortcutLabel(row.binding.shortcut)(replacing the droppedsr-onlyshortcut text); the add row uses a plain-text"New keybinding"title with the commandSelectmoved into the control cluster;WhenClauseControlcollapsed to a single inline trigger; the browser-notice tooltip trigger now carriesaria-labeland matches the sibling warning-span pattern, plus always-visible summary text. ScrollArearemoval is consistent with dropping the fixedmin-w-[680px]grid; vertical scrolling remains owned bySettingsPageContainer.- Interactive
PopoverTriggerinside theSettingsRowdescription<p>is valid phrasing content;TooltipTrigger render={<span/>}with icon children matches theMenuTriggerpattern already used in this file.
Not reported (pre-existing and not worsened): the hand-rolled ghost treatment on the shortcut pill and menu buttons (size-7 overrides on icon-sm), the truncate span inside a min-width:auto flex trigger, and the unused autoFocus pass-through on NewKeybindingKeyInput.
🔍 Agent activity — 13 tool calls
💭 I should check the file's current state at HEAD, since earlier commits were flagged with issues.
1. git_tools — a159f51..b7a129d
2. git_tools
💭 The git log doesn't show the PR commit, so I'll just check the current file contents at HEAD directly.
3. browse_code — apps/web/src/components/settings/KeybindingsSettings.tsx
💭 I'm now checking whether previously flagged accessibility issues were fixed—the tooltip trigger span missing an accessible name now has an aria-label added, and the sr-only shortcut label removal is resolved since the aria-label now includes the formatted shortcut text. I'm also noting the keybinding title has been updated to "New keybinding" with select mode. With those prior findings addressed, I want to check for any new issues in the current code, so I'll look at lines 1120-1300 and the panel.
4. browse_code — apps/web/src/components/settings/KeybindingsSettings.tsx
5. browse_code — apps/web/src/components/settings/settingsLayout.tsx
💭 I'm checking whether nesting a button-based popover trigger inside the paragraph used for descriptions is valid HTML, and confirming that buttons are allowed as phrasing content inside a
<p>. Looking at the styling onWhenClauseControl's trigger, I'm weighing whether it should reuse a namedButtonvariant rather than a bespoke class string, though this pattern already existed before and was previously deemed acceptable. I'm also checking that the focus-visible ring styling includes the necessary outline reset for proper accessibility. Then I'm verifying a handful of smaller details: whethersize-4.5is a valid Tailwind v4 spacing class (it is), whetherBadgeandInputsupport thesize="sm"andsize="compact"props respectively, and whether repeated overrides likesize-7on icon buttons acrossKeybindingRowMenuandNewKeybindingCancelIconshould instead map to an existing named variant such asicon-microor aghost-mutedstyle already defined elsewhere. I also confirm the removal of the horizontally-scrollingScrollArea/grid table in favor of responsiveSettingsRowlayout is an intentional redesign rather than a regression, thatdivide-ystyling on the list combined withrounded-nonerows is fine, and that the unusedautoFocusprop onNewKeybindingKeyInputwas already flagged before and isn't a new issue. I'm also noting thatallRowsgets passed redundantly alongsiderowsin the panel'slistProps, which is harmless duplication rather than a bug, and checking accessibility of howSettingsRowrenders its description text. I'm noting that the trigger's text color now inherits muted-foreground/80 instead of the previous explicit foreground color, which is a minor visual difference. Also, the "When" control has lost its bordered, select-like appearance and become a quieter inline text element—which looks intentional rather than a bug, so I move on to checking the settings row's grid column sizing. I'm now tracing whether className passed toKeybindingKeyControl's Input reaches the actual input element or just the wrapper span—since font styles inherit down through the DOM,font-monoshould still apply to the inner input even when set on the wrapper, andw-44constrains the wrapper's width while the inner input presumably fills it. The recording-state styling (border and background tint) would land on that same wrapper, which seems fine. I also noticeNewKeybindingTableRowalready uses this exact pattern withsize="compact", so the row editor is just being brought in line with an existing convention—consistent with what was suggested earlier. I want to double check the Input component's implementation to be sure the border actually renders on the wrapper rather than being overridden by the inner input's own styling.
6. browse_code — apps/web/src/components/ui/input.tsx
7. browse_code — apps/web/src/components/ui/badge.tsx
💭 I'm checking that the Badge component works fine wrapped in the input and title elements, then moving on to review the KeybindingSourceBadge styling. Every keybinding row now renders as an
<h3>heading via SettingsRow, which is a lot of headings but seems acceptable given the shared component contract. My bigger concern is the removed ScrollArea — the old table had horizontal overflow handling that's now gone since rows are responsive, but I want to verify the page still scrolls properly and that removing it didn't break the scroll fade or panel layout, so I'm going to look at the surrounding container code around lines 1400-1520.
8. browse_code — apps/web/src/components/settings/KeybindingsSettings.tsx
💭 I'm checking the BrowserKeybindingNotice, which uses a focusable span with an aria-label as its tooltip trigger, matching the pattern used by the other two warning triggers in the file, so consistency seems fine there. But I notice its full guidance is now only shown via tooltip, which makes it inaccessible on touch or coarse-pointer devices, an issue flagged in a prior run. The author added visible text about browser shortcuts taking precedence, which partially addresses that concern, and since the missing accessible name from the prior comment has been fixed, I shouldn't re-flag it. Now I want to compare the span patterns in KeybindingConflictWarning and UnknownWhenVariableWarning, checking for consistent styling classes.
9. git_tools — tabIndex={0}
💭 I'm noticing the notice trigger structures its render prop differently, with the icon as TooltipTrigger children rather than nested inside the render span like the other two. I need to check how Base UI's useRender merges children when a self-closing render element also receives children props, since the render element's own props typically take precedence during merging. I confirm that both patterns actually appear elsewhere in this codebase already, so this works fine. Now I'm checking whether embedding a button (from PopoverTrigger's default render) inside a paragraph tag causes any HTML validity issues, and it doesn't since buttons are valid phrasing content. I'm also examining whether the flex layout of the control cluster—conflict warning, key control, and menu—holds up correctly when switching into edit mode with an Input plus Save button. I'm noting the layout shift when isDirty triggers a Save button is pre-existing, not a new regression. The shortcut pill button and the WhenClauseControl's focus ring styling both look like existing, unchanged patterns worth leaving alone rather than flagging, though I'm weighing whether it should instead use a shared ghost button primitive per repo conventions. Turning to what's actually new here, I'm noting NewKeybindingKeyInput's autoFocus prop is never actually passed anywhere, and NewKeybindingWhen is just a trivial wrapper — minor dead code that a prior review comment already asked to clean up but apparently didn't fully address, including the unused className pass-through. The className issue got resolved but autoFocus is still dangling; flagging just that alone feels petty and isn't really a UI consistency issue, so I'll skip it. The aria-label improvement on the pill button looks fine, and now I'm trying to trace whether the "Source" info existed anywhere in the previous version's row layout before this PR, since the old header only had Command, Keybinding, When, and Status columns. I don't see source displayed in the old command cell either, just a tooltip on the truncated label — so the Badge is genuinely new, not a regression. But I do notice the old tooltip revealed the full command id on hover with an aria-label matching it, and that discoverability seems to have disappeared in the new row component, which just renders the command label directly. That's a deliberate simplification rather than a bug, but worth noting as a minor regression in discoverability. I also want to check whether the row styling stays consistent with how other settings sections wrap their rows, since this one overrides the default rounded/padding classes.
10. git_tools — <SettingsRow
11. git_tools — SettingsRow
💭 I want to check how similar list patterns are implemented elsewhere, like IntegrationsSettings, to see if there's an established convention for using SettingsRow with a divide-y wrapper and rounded-none styling.
12. git_tools — rounded-none
💭 I'm confirming the per-row keybinding editor hook behaves the same as before, and checking the search target and header labels are unchanged. Now I'm scanning for any real regression, starting with row hover behavior. Then I'm examining a possible truncation bug: the
WhenClauseControltrigger uses a truncate span nested inside a flex trigger with max-w-full, but since flex items default to min-width:auto, that inner truncate span won't actually shrink — meaning the text could overflow the trigger instead of truncating properly. That said, it turns out the old code had the exact same pattern, so it's a pre-existing issue rather than something introduced by this change. Checking a few other candidates — the conflict warning placement, autoFocus on the recording input, missing type="button" on the popover trigger — I conclude these are all either fine or unchanged from before. Now I'm looking at whether wrapping a Badge inside an<h3>heading causes any accessibility issues with the heading's accessible name. Since Default rows lack the menu and Custom rows have it, this is a real inconsistency worth flagging—the fix would be reserving fixed space for the menu trigger even when absent, similar to how other settings rows in the codebase reserve space for optional reset actions to preserve alignment. Right, previously the collapse existed but was contained within the fixed-width column, so it never affected the alignment of other columns. Now the menu's disappearance shifts the whole trailing control cluster, since justify-end means a missing trailing element moves the right edge of the pill/input itself. That confirms Finding 1: the row menu returning null causes right-edge misalignment, and reserving the slot would fix it. I'm now also looking at howNewKeybindingSettingsRownests its flex-wrap control container inside the already-flex control container from SettingsRow. I check the redundant flex classes are harmless since flex-wrap still allows wrapping at narrow widths, and the background stripe change from alternating rows to a staticbg-muted/15seems fine too. I notice the command Select width changed slightly but that's inconsequential. What stands out more is that the old table had a tooltip on the command label showing the raw command ID plus an aria-label with the command, and that's now gone entirely — meaning the raw command identifier is no longer discoverable anywhere in the UI, which could be a real regression in an accessibility or debugging sense rather than just a cosmetic redesign choice. Screen readers now only get the humanized label since the aria-label lost the raw command reference, though I'll treat this as minor and intentional. The pill's aria-label already covers the shortcut glyphs adequately. I also confirm the reducer initialization for the row editor doesn't reset on row prop changes, but since rows are keyed by id this just causes remounts, which is pre-existing behavior rather than a new bug. Now I'm tracing howrowActionsgets spread throughKeybindingsListinto each row component, checking thatallRows,variables,onSave,onReset, andonRemovedon't collide with the explicitly passedrowandisSavingprops — the spread order looks safe there. I'm also checking styling consistency on the row hover states against the previous version, and comparing howTooltipTriggeruses therenderprop with aspanacross the different notice components — specifically whether passing children alongside arenderelement that also has its own children could cause conflicts in Base UI's rendering behavior. Since it's a span, the button type attribute doesn't apply here. I'm also noticing the notice icon is now a smaller focusable target (size-4.5) compared to the sibling warning icons (size-5), which is a minor inconsistency but not a real accessibility failure given neither meets coarse-pointer minimums. I'll treat this as secondary to the tooltip-only content issue I already flagged. My main finding remains the alignment issue: the control container in the settings row uses flexible layout, so a Default row shows just the pill while a Custom row shows the pill plus a menu trigger, shifting the pill's right edge by roughly 36px. In a long list where rows alternate between these states, this creates a ragged, inconsistent right edge for the shortcut pills — a genuine layout regression from removing the fixed-width column. The fix I'm considering: rather than having the row menu return null when there's nothing to reset or remove, render an invisible placeholder span of the same size to reserve the slot. This change is self-contained and lands within the diff's added lines, so it's a clean inline suggestion. I'm also double-checking that the transient Save button shown during editing doesn't introduce a similar problem, but that shift feels acceptable since it's tied to an active editing state rather than a static list appearance. Now I'm also weighing whether the WhenClauseControl's inline trigger has a real hit-target problem — it shrank from a 28px bordered field to a bare 24px inline text trigger with no visible affordance until hover, sitting inside muted description text. That's below the usual touch-target guidance, and unlike the Button primitive, this local trigger has no coarse-pointer minimum-height fallback, though I note the older version had the same gap so it may not be a regression introduced by this change. I'm also second-guessing whether the hand-rolled button lackscursor-pointer, trying to recall whether Tailwind's preflight (v3 vs v4) sets pointer cursor on native buttons by default. Actually, let me check the repo instead of relying on memory — does the Button base or other custom triggers here include cursor-pointer? The similar pill button in KeybindingKeyControl also lacks it and predates this change, suggesting the repo has some global rule instead of relying on per-component classes. Since the trigger class already existed without cursor-pointer before this PR, I'll treat that as pre-existing and not a new issue, leaving one real finding — which is enough to mark the check as a failure with an inline comment rather than "all clear." I'm confirming the alignment claim: since canRemove is false for Default rows, only non-Default rows get the menu trigger, meaning most rows (Default) lack it and align together while the few Custom rows stick out to the left by 36px, creating a ragged edge. The conflict warning being leading rather than trailing means it doesn't affect this right-edge alignment issue, so I'll write an inline suggestion comment on the relevant line, making sure to map it correctly to the diff's line numbering for the review comment. I'm drafting the suggested fix: replacing the early return null with a hidden placeholder span sized to match the menu button (size-7) so that rows without the menu still occupy the same width, keeping the right edges aligned since the row's gap spacing applies uniformly. I also double-checked KeybindingSettingsRow's className overrides and confirmed they merge cleanly with SettingsRow's base classes without conflict. This spacing inconsistency feels like a real layout misalignment rather than just an aesthetic class-ordering nitpick, so I'll flag it and mark the review as a failure.
13. modify_pr — One finding: the trailing row-actions slot now collapses, so shortcut controls no longer share a right edge across rows. Earlier findings from previous runs (missing shortcut in the pill's accessible name, the interactive heading on the add row, the duplicated when-clause trigger variant, and the unlabeled notice trigger) look addressed at this head.
Agent Credits: 124 credits