Skip to content

chore: add portalling and make ui updates#1726

Merged
kotAPI merged 8 commits intomainfrom
kotapi/ui-improvements-apr-3
Apr 4, 2026
Merged

chore: add portalling and make ui updates#1726
kotAPI merged 8 commits intomainfrom
kotapi/ui-improvements-apr-3

Conversation

@kotAPI
Copy link
Copy Markdown
Collaborator

@kotAPI kotAPI commented Apr 3, 2026

Summary by CodeRabbit

  • New Features

    • Card gains compound subcomponents (Header, Title, Content, Footer, Action, Description).
    • Dialog and AlertDialog now provide a Footer subcomponent.
    • Theme context introduced to manage portal roots and theming.
  • Bug Fixes

    • Improved portal/root resolution for popups (Tooltip, HoverCard, Dialog, Select/Combobox).
  • Style Updates

    • Large shift to semantic theme tokens and refreshed component styles; numerous visual refinements and dark-mode improvements.
  • Documentation

    • Added a clarity design system guide for tokens and intent mapping.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 3, 2026

🦋 Changeset detected

Latest commit: 13c1e2a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@radui/ui Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 3, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds ThemeContext-based portal resolution and many UI token/styling refactors; introduces Card/Dialog/AlertDialog footer subcomponents and multiple Card fragments; refactors SandboxEditor color picker; updates numerous Storybook stories; and makes accessibility/focus and portal-related behavior changes across primitives and components.

Changes

Cohort / File(s) Summary
Card / Dialog / AlertDialog fragments
src/components/ui/Card/fragments/*, src/components/ui/Card/Card.tsx, src/components/ui/Card/contexts/CardContext.tsx, src/components/ui/Dialog/fragments/DialogFooter.tsx, src/components/ui/Dialog/Dialog.tsx, src/components/ui/AlertDialog/fragments/AlertDialogFooter.tsx, src/components/ui/AlertDialog/AlertDialog.tsx
Added Card subcomponents (Header/Title/Content/Description/Action/Footer) with CardContext; added Dialog.Footer and AlertDialog.Footer fragments and exported types/static properties.
Theme & Portal infra
src/components/ui/Theme/Theme.tsx, src/components/ui/Theme/ThemeContext.tsx, src/components/ui/Theme/tests/Theme.test.tsx, src/components/ui/HoverCard/fragments/HoverCardPortal.tsx, src/core/primitives/Dialog/fragments/DialogPrimitivePortal.tsx, src/components/ui/Tooltip/fragments/TooltipContent.tsx
Introduced ThemeContext (containerRef, portalRootRef) and updated portal selection logic across HoverCard/Dialog/Tooltip to prefer ThemeContext refs and multiple fallback DOM queries; added Theme tests.
SCSS tokenization & theme overhaul
styles/themes/components/... (accordion, alert-dialog, avatar*, badge, blockquote, button, callout, card, checkbox*, code, collapsible, combobox, context-menu, dialog, dropdown-menu, hover-card, kbd, link, menubar, navigation-menu, progress, radio*, select, slider, splitter, steps, switch, tabs, textarea, toggle*, toolbar, tooltip, tree, etc.)
Replaced many hardcoded colors/sizing with semantic CSS variables (--rad-ui-*); moved pixel values to rem/min-height, adjusted hover/focus/active states, removed many accent-dependent overrides and transitions; added/renamed some public CSS classes and keyframes.
Gray token standardization
styles/cssTokens/base.tokens.css, src/design-systems/clarity/tokens/colors/gray.ts, styles/themes/default.scss
Converted gray tokens from oklch to hex values; added root font/radius variables and [data-rad-ui-theme]/[data-rad-ui-portal-root] scopes with semantic token definitions.
Storybook simplifications & updates
many story files under src/components/ui/*/stories/* and src/core/primitives/*/stories/* (Progress, Switch, Toggle, ToggleGroup, Slider, TextArea, Tooltip, Card, Checkbox, CheckboxCards, Collapsible, HoverCard, RadioCards, RadioGroup, ContextMenu, etc.)
Collapsed complex/parametrized story suites into smaller focused examples, changed many story titles to WIP/..., removed or simplified arg-based renderers, and updated demo content/layout and utility classes (e.g., text-whitetext-gray-50).
SandboxEditor / Color select
src/components/tools/SandboxEditor/SandboxEditor.tsx
Replaced swatch RovingFocusGroup with a Select dropdown, turned color indicator into non-interactive visual, changed default color from 'plum' to 'gray', and improved aria labeling for dark-mode toggle.
Focus/navigation / primitives
src/components/ui/Tree/fragments/TreeItem.tsx, src/core/utils/RovingFocusGroup/fragments/RovingFocusGroup.tsx, src/core/primitives/CheckboxGroup/fragments/CheckboxGroupPrimitiveTrigger.tsx, src/components/ui/Accordion/fragments/AccordionRoot.tsx, src/core/primitives/Collapsible/fragments/CollapsiblePrimitiveContent.tsx
Improved tree keyboard focus handling and role-aware focusing; RovingFocusGroup ID collection now includes nested treeitems; checkbox triggers gain data-state; Accordion height-lock logic removed; Collapsible observer/animation moved to layout effect and ref-driven comparisons.
Menu / Floating UI positioning
src/core/primitives/Menu/fragments/MenuPrimitiveRoot.tsx
Adjusted Floater middleware: made offsets optional with computed defaults, set strategy to 'fixed', added flip crossAxis avoidCollision and a shift middleware for padding.
Radio / Checkbox cards orientation prop
src/components/ui/CheckboxCards/fragments/CheckboxCardsRoot.tsx, src/components/ui/RadioCards/fragments/RadioCardsRoot.tsx
Exposed/forwarded orientation prop (default 'both') into underlying group primitives and reformatted JSX.
Slider/thumb positioning tweaks
src/components/ui/Slider/fragments/SliderRangeSlider.tsx, src/components/ui/Slider/fragments/SliderThumb.tsx
Introduced THUMB_HALF_SIZE and adjusted inline positioning offsets (12px → 8–10px) for more accurate thumb placement.
Splitter handle state
src/components/ui/Splitter/fragments/SplitterHandle.tsx
Added active modifier class when handle is being dragged (isDragging + activeHandleIndex match).
Tests & small fixes
src/components/ui/Card/tests/Card.test.tsx, src/core/primitives/Menu/tests/MenuPrimitive.test.tsx, src/components/ui/Collapsible/tests/Collapsible.control.test.tsx, src/components/ui/Theme/tests/Theme.test.tsx
Added Card fragment rendering tests, refined Floater.flip assertions, hardened Collapsible test error filtering and cleanup, added Theme portal-root test.
Docs & tooling
knowledge/design_system/clarity_design_system.md, styles/themes/components/fix_tokens.cjs, docs/next.config.js
Added design-system documentation for grayscale tokens; added a fix_tokens script to mass-remap SCSS token usages; simplified MDX remark-gfm plugin reference.
Misc
package.json, styles/themes/components/table.scss, styles/themes/components/separator.scss, various small story/demo JSX
Trivial whitespace/newline edits in package.json and some styles; minor utility class adjustments across demos/stories (e.g., bg-blackbg-gray-1000).

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Theme as Theme (Provider)
participant Component as UI Component (Tooltip/Dialog/HoverCard)
participant Floating as FloatingPortal/Floater
participant DOM as Document / portal root element

Component->>Theme: read ThemeContext (portalRootRef/containerRef)
alt portalRootRef available
Component->>Floating: render into root={portalRootRef.current}
else data attribute found
Component->>DOM: querySelector('[data-rad-ui-portal-root]')
Component->>Floating: render into found element
else containerRef available
Component->>Floating: render into root={containerRef.current}
else fallback
Component->>Floating: render into legacy DOM target
Floating->>DOM: append portal nodes to chosen root

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

Suggested labels

codex

Poem

🐰 A Hop Through Tokens
From hardcoded grays to variables bright,
portals now find home by Theme's guiding light,
cards gained footers, stories grew lean—
a rabbit applauds this tidy design clean! 🥕

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch kotapi/ui-improvements-apr-3

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/components/ui/Badge/stories/Badge.stories.tsx (1)

89-100: ⚠️ Potential issue | 🟡 Minor

Don't advertise color on the destructive variant.

badge.scss hard-codes destructive to red, so these examples either ignore color or render identical badges with different labels. Keep destructive in the variant demo, but filter it out of the color showcase.

Suggested story adjustment
-                {Variants.map((variant, vIndex) => (
+                {Variants.filter((variant) => variant !== 'destructive').map((variant, vIndex) => (
                     <div key={vIndex} className='flex flex-col gap-2'>
                         <p className='text-xs text-gray-600'>{variant}</p>
                         <div className='flex flex-wrap gap-2'>
                             {Colors.slice(0, 3).map((color, cIndex) => (
                                 <Badge key={cIndex} color={color} variant={variant}>
                                     {color}
                                 </Badge>
                             ))}
                         </div>
                     </div>
                 ))}
@@
-                <Badge color="blue" variant="destructive">Destructive</Badge>
+                <Badge variant="destructive">Destructive</Badge>

Also applies to: 140-145

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/Badge/stories/Badge.stories.tsx` around lines 89 - 100, The
story currently advertises different colors for every variant, but the
destructive variant is hard-coded to red in badge.scss so color props are
ignored; in the Badge.stories.tsx change the inner render logic inside the
Variants.map: if variant === 'destructive' render a single Badge (e.g., <Badge
variant="destructive">destructive</Badge>) instead of mapping Colors, otherwise
continue mapping Colors.slice(0,3). Update the block that renders Colors
(referencing Variants, Colors and Badge) to conditionally skip the color
showcase for the 'destructive' variant.
src/components/tools/SandboxEditor/SandboxEditor.tsx (1)

45-47: ⚠️ Potential issue | 🟡 Minor

Avoid rendering a literal undefined class.

When className is omitted, the wrapper on Line 130 becomes pt-3 undefined. Coalesce the prop before interpolating so bare <SandboxEditor> usages don't leak a bogus class.

💡 Minimal fix
-type SandboxProps = {className?: string | ''} & PropsWithChildren
+type SandboxProps = {className?: string} & PropsWithChildren
...
-            <div className={`pt-3 ${className}`} >
+            <div className={className ? `pt-3 ${className}` : 'pt-3'}>

Also applies to: 130-130

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/tools/SandboxEditor/SandboxEditor.tsx` around lines 45 - 47,
The SandboxEditor currently types className as "string | ''" and interpolates it
directly causing a literal "undefined" class when omitted; change SandboxProps
to className?: string and update the SandboxEditor to coalesce the prop (e.g.,
default param or use className ?? '' or conditional concat) when building the
wrapper class so that bare <SandboxEditor> does not inject "undefined" into the
class list; locate the SandboxProps type and the SandboxEditor function to apply
these changes.
src/core/primitives/Dialog/fragments/DialogPrimitivePortal.tsx (1)

46-76: 🛠️ Refactor suggestion | 🟠 Major

Redundant conditional branches render identical JSX.

The forceMount, keepMounted, and default branches (lines 46-76) all render the exact same <Floater.Portal> markup. This makes the conditions meaningless and adds unnecessary complexity.

Either differentiate the behavior between these branches or consolidate to a single return:

-    // If forceMount is true, always render
-    if (forceMount) {
-        return (
-            <Floater.Portal
-                root={rootElementRef.current}
-                {...props}
-            >
-                {children}
-            </Floater.Portal>
-        );
-    }
-
-    // If keepMounted is true, keep the portal container but children follow their own mounting logic
-    if (keepMounted) {
-        return (
-            <Floater.Portal
-                root={rootElementRef.current}
-                {...props}
-            >
-                {children}
-            </Floater.Portal>
-        );
-    }
-
     return (
         <Floater.Portal
             root={rootElementRef.current}
             {...props}
         >
             {children}
         </Floater.Portal>
     );

If forceMount and keepMounted are meant to control rendering differently, implement the actual differentiation logic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/primitives/Dialog/fragments/DialogPrimitivePortal.tsx` around lines
46 - 76, The three branches checking forceMount and keepMounted in
DialogPrimitivePortal that all return the same Floater.Portal are redundant;
consolidate them by removing the conditional branches and returning a single
Floater.Portal using rootElementRef.current, props, and children, or if
forceMount/keepMounted should alter behavior, implement the intended differences
inside the component (e.g., conditionally render children, change portal props,
or apply mounting logic) within the DialogPrimitivePortal function so the
symbols forceMount, keepMounted, Floater.Portal, rootElementRef, children, and
props are used meaningfully.
🟡 Minor comments (19)
styles/themes/components/steps.scss-50-55 (1)

50-55: ⚠️ Potential issue | 🟡 Minor

Replace hardcoded color: white with the semantic token --rad-ui-text-inverse.

Line 54 uses a hardcoded white value, which is inconsistent with the semantic token approach used throughout the codebase. The design system provides --rad-ui-text-inverse (defined as var(--rad-ui-color-gray-50)) for text on dark/accent backgrounds, as used in button.scss and tooltip.scss. Update to maintain consistency with the design system.

♻️ Suggested refactor
         &[data-state="completed"] {
             .rad-ui-steps-bubble {
                 border-color: var(--rad-ui-color-accent-900);
                 background-color: var(--rad-ui-color-accent-900);
-                color: white;
+                color: var(--rad-ui-text-inverse);
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/steps.scss` around lines 50 - 55, In the
&[data-state="completed"] rule for .rad-ui-steps-bubble replace the hardcoded
"color: white" with the semantic token --rad-ui-text-inverse; update the
declaration to use var(--rad-ui-text-inverse) so text on accent backgrounds uses
the design system token (match the pattern used in button.scss and
tooltip.scss).
src/components/ui/Progress/stories/Progress.stories.tsx-40-47 (1)

40-47: ⚠️ Potential issue | 🟡 Minor

Add an accessible name to the range control.

Lines 40-47 render a range input without an accessible name. Add aria-label (or a <label htmlFor> pair) so the control is discoverable by assistive tech.

💡 Suggested fix
                     <input
                         type="range"
+                        aria-label="Progress value"
                         min="0"
                         max="100"
                         value={value}
Based on learnings: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/Progress/stories/Progress.stories.tsx` around lines 40 -
47, The range input rendered (the <input> with type="range" using value,
setValue, onChange and className="w-full max-w-[25.25rem]") needs an accessible
name; add either an aria-label (e.g., aria-label="Progress" or similar
descriptive text) to the input or wire it to a visible <label htmlFor="..."> by
giving the input an id and matching label text so assistive tech can discover
the control; update the input element where value and onChange are defined (and
ensure the label text describes the control's purpose).
styles/themes/components/table.scss-2-2 (1)

2-2: ⚠️ Potential issue | 🟡 Minor

Use an existing border token key on the table wrapper.

Line 2 references --rad-ui-border, but the semantic border tokens in this PR are --rad-ui-border-soft/default/strong. If --rad-ui-border is undefined, the border declaration is dropped.

💡 Suggested fix
-.rad-ui-table-wrapper{
-    border: 1px solid var(--rad-ui-border);
+.rad-ui-table-wrapper{
+    border: 1px solid var(--rad-ui-border-default);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/table.scss` at line 2, The table wrapper's border
uses a nonexistent token (--rad-ui-border); replace it with an existing semantic
border token such as --rad-ui-border-soft (or more specific variant
--rad-ui-border-soft-default or --rad-ui-border-soft-strong as appropriate for
visual weight) in the rule that sets "border: 1px solid var(--rad-ui-border)" so
the declaration never drops when the token is undefined and matches the
project's semantic token naming.
styles/themes/components/textarea.scss-21-24 (1)

21-24: ⚠️ Potential issue | 🟡 Minor

Let the placeholder inherit the current size variant.

Hard-coding ::placeholder to 1rem overrides the small and large font-size variants, so the placeholder and entered text no longer match. Use inherit or remove the explicit size.

Suggested fix
     &::placeholder {
       color: var(--rad-ui-text-muted);
-      font-size: 1rem;
+      font-size: inherit;
     }

Also applies to: 62-76

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/textarea.scss` around lines 21 - 24, The
::placeholder rule in the textarea component currently hard-codes font-size:
1rem which breaks the small/large size variants; update the &::placeholder
selector in styles/themes/components/textarea.scss to use font-size: inherit (or
remove the font-size declaration) so the placeholder inherits the component's
current size variant, and apply the same change to the duplicate placeholder
block around lines 62-76 to keep behavior consistent.
src/components/ui/Checkbox/stories/Checkbox.stories.tsx-85-92 (1)

85-92: ⚠️ Potential issue | 🟡 Minor

The new copy mapping assigns the wrong labels to two rows.

notifications now renders the same label/description as terms, and the disabled row falls through to the notification label. That leaves Storybook showing duplicated content instead of three distinct checkbox states.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/Checkbox/stories/Checkbox.stories.tsx` around lines 85 -
92, The label ternary and the conditional description in Checkbox.stories.tsx
are mis-mapped: the expression rendering the label text (inside the <label> for
variable key) currently gives 'Accept terms and conditions' for both 'terms' and
'notifications', and the description block beneath only targets 'notifications'
but uses the terms copy. Fix the ternary so key === 'terms' => 'Accept terms and
conditions', key === 'notifications' => 'Enable notifications' (and for
'disabled' use an appropriate fallback like 'Disabled' or the intended label),
and update the conditional paragraph rendering so that 'terms' shows "By
clicking this checkbox, you agree to the terms." and 'notifications' shows a
notifications-specific line (e.g., "Enable notifications to receive updates."),
leaving 'disabled' without the description if intended; adjust the label
rendering and the conditional (key === 'terms' / key === 'notifications') to
match these mappings.
styles/themes/components/select.scss-130-154 (1)

130-154: ⚠️ Potential issue | 🟡 Minor

Selected options lose hover/highlight feedback.

[aria-selected="true"] is declared after [data-highlighted] and [data-active="true"], so the selected option keeps surface-muted even while hovered or keyboard-highlighted. That makes the current focus target harder to track.

Suggested fix
-        &[data-highlighted] {
-            background-color: var(--rad-ui-surface-subtle);
-            color: var(--rad-ui-text-primary);
-        }
+        &[data-highlighted] {
+            background-color: var(--rad-ui-surface-subtle);
+            color: var(--rad-ui-text-primary);
+        }
@@
-        &[data-active="true"] {
+        &[data-active="true"] {
             outline: none;
             background-color: var(--rad-ui-surface-subtle);
         }
@@
-        &[aria-selected="true"] {
+        &[aria-selected="true"]:not([data-highlighted]):not([data-active="true"]) {
             background-color: var(--rad-ui-surface-muted);
             color: var(--rad-ui-text-primary);
             font-weight: 500;
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/select.scss` around lines 130 - 154, The selected
option's styles ([aria-selected="true"]) are overriding hover/keyboard feedback
([data-highlighted], [data-active="true"]); update the stylesheet so
highlight/active states take precedence by adding more specific selectors or
reordering: create combined selectors like
&[aria-selected="true"][data-highlighted] and
&[aria-selected="true"][data-active="true"] (and also adjust
.rad-ui-select-item-indicator if needed) to set background-color:
var(--rad-ui-surface-subtle), color: var(--rad-ui-text-primary) and opacity for
the indicator, ensuring hovered/active selected items show the highlight instead
of surface-muted.
src/components/ui/Table/stories/Table.stories.tsx-74-75 (1)

74-75: ⚠️ Potential issue | 🟡 Minor

Label the actions column, not just the icon.

The last header cell is empty and each row action is icon-only, so screen readers get no useful name for that column/action.

💡 Minimal fix
-                        <Table.ColumnCellHeader style={{ width: '3rem' }} />
+                        <Table.ColumnCellHeader style={{ width: '3rem' }}>
+                            <span className="sr-only">Actions</span>
+                        </Table.ColumnCellHeader>
...
-                                <button className="inline-flex h-8 w-8 items-center justify-center rounded-md text-gray-700" type="button">
+                                <button
+                                    aria-label={`Open actions for ${row.email}`}
+                                    className="inline-flex h-8 w-8 items-center justify-center rounded-md text-gray-700"
+                                    type="button"
+                                >

Also applies to: 89-92

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/Table/stories/Table.stories.tsx` around lines 74 - 75, The
actions column header is empty so screen readers can't identify the icon-only
buttons; update the two Table.ColumnCellHeader instances (the one currently with
style width: '3rem' and the other occurrence around rows 89-92) to include an
accessible label such as the visible text "Actions" or a visually-hidden span
"Actions" inside the Table.ColumnCellHeader, and ensure each row action
icon/button (the icon-only controls referenced in the row rendering) includes an
appropriate aria-label or title attribute describing the action.
styles/themes/components/splitter.scss-59-70 (1)

59-70: ⚠️ Potential issue | 🟡 Minor

Reduced-motion still animates the handle indicator.

The visible motion now lives on ::after, but the media query only disables transitions on the handle itself. Users with prefers-reduced-motion will still get the fade/background animation.

💡 Minimal fix
   `@media` (prefers-reduced-motion: reduce) {
     .rad-ui-splitter-panel {
       transition: none;
     }
     
     .rad-ui-splitter-handle {
       transition: none;
     }
+
+    .rad-ui-splitter-handle::after {
+      transition: none;
+    }
   }

Also applies to: 111-123, 144-151

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/splitter.scss` around lines 59 - 70, The ::after
pseudo-element still has transitions so users with prefers-reduced-motion still
see the fade; update the prefers-reduced-motion media query that currently
targets the handle to also target the handle::after (and any other
pseudo-elements used, e.g., the visible indicator) and disable its
animations/transitions (set transition: none or remove opacity/background-color
transitions) for the selectors that include ::after (references: the rule
containing &::after and the other blocks around lines 111-123 and 144-151).
src/components/ui/Table/stories/Table.stories.tsx-47-52 (1)

47-52: ⚠️ Potential issue | 🟡 Minor

Give the filter field an accessible name.

The placeholder disappears on input and doesn’t reliably serve as a label, so this control is currently unnamed to assistive tech.

💡 Minimal fix
                 <input
+                    aria-label="Filter emails"
                     className="h-10 w-full max-w-[29rem] rounded-xl border border-gray-300 bg-gray-50 px-3 text-sm text-gray-950 outline-none"
                     placeholder="Filter emails..."
                     type="text"
                 />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/Table/stories/Table.stories.tsx` around lines 47 - 52, The
input in the Table story is missing an accessible name; add one by giving the
input an id and a visible or visually-hidden label or by adding an aria-label
(e.g., aria-label="Filter emails") so assistive tech can identify it when the
placeholder disappears; update the input element in Table.stories.tsx (the
<input ... placeholder="Filter emails..." ... />) to include either a matching
<label htmlFor="filter-emails">Filter emails</label> or aria-label="Filter
emails".
styles/themes/components/toolbar.scss-100-107 (1)

100-107: ⚠️ Potential issue | 🟡 Minor

Preserve the pressed state on hover.

[data-state='on'] and :hover have the same specificity here, so the later hover rule replaces the accent background and makes selected items look unselected under the pointer.

💡 Minimal fix
         &:hover {
             background-color: var(--rad-ui-surface-subtle);
         }
+
+        &[data-state='on']:hover {
+            background-color: var(--rad-ui-color-accent-100);
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/toolbar.scss` around lines 100 - 107, The hover rule
currently overrides the selected rule because both selectors (&[data-state='on']
and &:hover) have equal specificity; update the CSS so hover styling does not
apply when the item is selected — e.g. change the hover selector to only target
non-selected items (use &:hover:not([data-state='on']) or add a specific rule
for &[data-state='on']:hover to preserve its background) so the
&[data-state='on'] accent background and color remain visible while hovering.
src/components/ui/Collapsible/stories/Collapsible.stories.tsx-27-33 (1)

27-33: ⚠️ Potential issue | 🟡 Minor

Hide the chevron from assistive tech.

This icon is purely decorative in both triggers. Without aria-hidden, some screen readers will announce an extra graphic inside the button.

💡 Suggested fix
-        <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
+        <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" {...props} aria-hidden="true" focusable="false">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/Collapsible/stories/Collapsible.stories.tsx` around lines
27 - 33, The DoubleChevronIcon SVG is decorative and being announced by screen
readers; update the DoubleChevronIcon component to hide it from assistive tech
by adding accessibility attributes to the <svg> element (e.g.,
aria-hidden="true" and focusable="false") so the graphic is removed from the
accessibility tree and not focusable; keep spreading {...props} but ensure these
attributes are present on the svg in the DoubleChevronIcon function.
styles/themes/components/slider.scss-133-136 (1)

133-136: ⚠️ Potential issue | 🟡 Minor

Fix the vertical mark label layout.

In the vertical slider variant (lines 133–136), the .rad-ui-slider-mark-label has no position property, so top and right do nothing. However, transform: translateY(-50%) still applies and shifts the label upward instead of keeping it flex-centered. Replace with margin-based spacing for the flex row layout.

💡 Suggested fix
      .rad-ui-slider-mark-label {
-       right: 1rem;
-       top: 50%;
-       transform: translateY(-50%);
+       margin-left: 0.5rem;
+       transform: none;
      }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/slider.scss` around lines 133 - 136, The
.rad-ui-slider-mark-label in the vertical slider variant lacks a position, so
its top/right rules are ineffective while transform: translateY(-50%)
incorrectly shifts it; update the .rad-ui-slider-mark-label styling used by the
vertical slider to remove top/right/transform and instead rely on the flex row
layout by using margin (e.g., margin-right or margin-left as appropriate) and
align-items/justify-content to vertically center the label; adjust any parent
.rad-ui-slider (or vertical variant selector) to ensure the label is placed in
the flex row and uses margin-based spacing rather than absolute positioning.
styles/themes/components/switch.scss-1-12 (1)

1-12: ⚠️ Potential issue | 🟡 Minor

Add box-sizing: border-box after all: unset.

The all: unset declaration resets box-sizing to content-box, which expands the root's total width to 42px (40px content + 2px borders). This misaligns the checked thumb position—at translateX(1.125rem) (18px), it falls 2px short of the right edge when border-box sizing is intended.

Suggested fix
 .rad-ui-switch {
   all: unset;
+  box-sizing: border-box;
   position: relative;

Please verify the thumb alignment in the Checked story after applying this fix.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/switch.scss` around lines 1 - 12, The .rad-ui-switch
rule uses all: unset which resets box-sizing to content-box and causes the
component width to grow by borders; add box-sizing: border-box immediately after
all: unset in the .rad-ui-switch selector to ensure borders are included in the
width so the checked thumb (the switch's thumb translateX value used in the
Checked story) aligns correctly with the right edge; after adding it, verify the
Checked story thumb position and adjust translateX only if needed.
styles/themes/components/context-menu.scss-91-94 (1)

91-94: ⚠️ Potential issue | 🟡 Minor

Keep destructive items red when they’re highlighted.

[data-highlighted] and [aria-selected="true"] set the text color back to --rad-ui-text-primary, so .rad-ui-context-menu-danger only works at rest. Add matching state selectors for the danger variant.

🎯 Minimal state-preserving variant
-.rad-ui-context-menu-danger {
-    color: var(--rad-ui-color-red-900);
-}
+.rad-ui-context-menu-danger,
+.rad-ui-context-menu-sub-trigger.rad-ui-context-menu-danger[data-highlighted],
+.rad-ui-context-menu-item.rad-ui-context-menu-danger[data-highlighted],
+.rad-ui-context-menu-item.rad-ui-context-menu-danger[aria-selected="true"] {
+    color: var(--rad-ui-color-red-900);
+}

Also applies to: 147-150, 182-184

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/context-menu.scss` around lines 91 - 94, The danger
variant loses its red text when highlighted/selected because the generic
highlighted selectors override it; update the `.rad-ui-context-menu-danger`
rules to include matching state selectors so the danger color is preserved (add
`[data-highlighted]` and `[aria-selected="true"]` variants for
`.rad-ui-context-menu-danger` and the inner elements that currently set color in
the `[data-highlighted]`/`[aria-selected="true"]` blocks) — target the
`.rad-ui-context-menu-danger` class and the existing `[data-highlighted]` and
`[aria-selected="true"]` selectors referenced in the diff so
highlighted/selected danger items keep the danger color.
styles/themes/components/alert-dialog.scss-94-95 (1)

94-95: ⚠️ Potential issue | 🟡 Minor

Make the mobile and desktop breakpoints mutually exclusive.

At exactly 640px, both (width >= 640px) and (width <= 640px) match, causing both the desktop footer layout and mobile padding/typography overrides to apply together. Change one comparison to exclude the boundary (e.g., (width > 640px) or (width < 640px)).

Also applies to: 127-146

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/alert-dialog.scss` around lines 94 - 95, The media
query at `@media` (width >= 640px) currently overlaps the mobile rule `@media`
(width <= 640px) at exactly 640px; update the desktop breakpoint to be exclusive
(e.g., change `@media` (width >= 640px) to `@media` (width > 640px)) so the mobile
and desktop rules do not both apply; apply the same boundary fix to the other
overlapping queries in the file (the block around lines 127-146) so each
breakpoint is mutually exclusive.
src/core/primitives/Dialog/fragments/DialogPrimitivePortal.tsx-29-32 (1)

29-32: ⚠️ Potential issue | 🟡 Minor

Missing null in type cast for querySelector.

For consistency with HoverCardPortal.tsx (line 16) and type safety, the cast should include null:

-            const themeContainer = themeContext?.portalRootRef.current
-                || document.querySelector('[data-rad-ui-portal-root]') as HTMLElement
+            const themeContainer = themeContext?.portalRootRef.current
+                || (document.querySelector('[data-rad-ui-portal-root]') as HTMLElement | null)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/primitives/Dialog/fragments/DialogPrimitivePortal.tsx` around lines
29 - 32, The querySelector cast for themeContainer in DialogPrimitivePortal (the
themeContainer assignment using themeContext?.portalRootRef.current ||
document.querySelector('[data-rad-ui-portal-root]') as HTMLElement || ...)
should include null for type-safety and to match HoverCardPortal.tsx; change the
HTMLElement cast to HTMLElement | null (e.g., cast the
document.querySelector('[data-rad-ui-portal-root]') to HTMLElement | null) so
themeContainer's union resolution is correct and TypeScript won't assume a
non-null HTMLElement.
src/components/ui/HoverCard/stories/HoverCard.stories.tsx-63-65 (1)

63-65: ⚠️ Potential issue | 🟡 Minor

Use a semantic button for the interactive trigger in the controlled example.

Line 64 uses a <span onClick={() => setOpen(true)}>, which lacks keyboard accessibility. Replace it with a <button type="button"> to ensure the controlled example demonstrates accessible patterns.

Suggested fix
                <HoverCard.Trigger>
-                    <span onClick={() => setOpen(true)}>{open ? 'Hover Here' : 'Show Card'}</span>
+                    <button type="button" onClick={() => setOpen(true)}>
+                        {open ? 'Hover Here' : 'Show Card'}
+                    </button>
                </HoverCard.Trigger>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/HoverCard/stories/HoverCard.stories.tsx` around lines 63 -
65, The trigger uses a non-semantic <span> which breaks keyboard accessibility;
replace the <span onClick={() => setOpen(true)}> inside HoverCard.Trigger with a
<button type="button"> that calls setOpen(true) (and preserves any
className/aria props) so the controlled HoverCard example demonstrates an
accessible, keyboard-focusable trigger; ensure any existing styling or props are
forwarded to the button and keep the open ? 'Hover Here' : 'Show Card' label
logic.
styles/themes/components/radiocards.scss-80-85 (1)

80-85: ⚠️ Potential issue | 🟡 Minor

cursor: not-allowed is ineffective when pointer-events: none is set.

When pointer-events: none is applied, the element doesn't receive pointer events, so the cursor style won't be visible. Consider removing pointer-events: none if you want the not-allowed cursor to show, or remove cursor: not-allowed since it has no effect.

🔧 Suggested fix (option: show cursor)
         &[data-disabled],
         &:disabled {
             opacity: 0.5;
             cursor: not-allowed;
-            pointer-events: none;
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/radiocards.scss` around lines 80 - 85, The disabled
state block for the radio card selectors (&[data-disabled], &:disabled) mixes
cursor: not-allowed with pointer-events: none which makes the cursor
ineffective; update the rule to remove pointer-events: none (leave cursor:
not-allowed and opacity) so the not-allowed cursor is visible for disabled
items, and if needed move pointer-events: none to an inner interactive element
or toggle it only when you truly want to block pointer interactions.
styles/themes/components/radio-group.scss-32-35 (1)

32-35: ⚠️ Potential issue | 🟡 Minor

Border-width change on checked state may cause minor layout shift.

Increasing border-width from 1px to 2px shifts the inner content by 1px. Consider using box-shadow for the thicker border effect to avoid layout shift, or adjust the checked state sizing to compensate.

🔧 Alternative using inset box-shadow
     &[data-state="checked"] {
-      border-width: 2px;
-      border-color: var(--rad-ui-text-primary);
+      border-color: var(--rad-ui-text-primary);
+      box-shadow: inset 0 0 0 1px var(--rad-ui-text-primary);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/radio-group.scss` around lines 32 - 35, The
checked-state rule for the radio-group (&[data-state="checked"]) increases
border-width from 1px to 2px causing a 1px layout shift; instead, keep the
border-width constant and emulate a thicker border using an inset box-shadow or
outline so inner content doesn't move. Update the &[data-state="checked"] rule
in radio-group.scss to remove the border-width change and add an inset
box-shadow (or outline) using var(--rad-ui-text-primary), or alternatively
adjust padding/margin to compensate while keeping box-sizing consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@styles/themes/components/blockquote.scss`:
- Around line 5-11: The base blockquote rule currently defines neutral defaults
(border-inline-start: 4px solid var(--rad-ui-border-default); color:
var(--rad-ui-text-secondary);) but then immediately overrides them with accent
values (border-inline-start-color: var(--rad-ui-color-accent-600); color:
var(--rad-ui-color-accent-1000);), making the neutral variant unreachable;
remove the accent-specific declarations from this base rule and instead place
them in a dedicated variant selector (e.g., .blockquote--accent or a
.variant-accent rule) so the base styles (border-inline-start, color, font-size,
line-height) remain the default and the accent colors are applied only when the
accent variant class is present.

In `@styles/themes/components/context-menu.scss`:
- Around line 14-16: The shared context-menu theme currently hardcodes
demo-sized dimensions (padding: 6rem 1.5rem; width: 24rem; min-height: 13.5rem)
which forces every .rad-ui-context-menu-trigger to be oversized; remove the
fixed width and min-height from the theme (leave sensible padding or use
relative sizes like auto/max-content) and instead apply the 24rem×13.5rem sizing
in the story/docs via a demo-specific modifier class (e.g.,
.rad-ui-context-menu--demo) or a CSS variable so normal inline/custom triggers
remain unmodified; update any references to .rad-ui-context-menu-trigger in
styles/themes/components/context-menu.scss and the story CSS to use the demo
modifier.

---

Outside diff comments:
In `@src/components/tools/SandboxEditor/SandboxEditor.tsx`:
- Around line 45-47: The SandboxEditor currently types className as "string |
''" and interpolates it directly causing a literal "undefined" class when
omitted; change SandboxProps to className?: string and update the SandboxEditor
to coalesce the prop (e.g., default param or use className ?? '' or conditional
concat) when building the wrapper class so that bare <SandboxEditor> does not
inject "undefined" into the class list; locate the SandboxProps type and the
SandboxEditor function to apply these changes.

In `@src/components/ui/Badge/stories/Badge.stories.tsx`:
- Around line 89-100: The story currently advertises different colors for every
variant, but the destructive variant is hard-coded to red in badge.scss so color
props are ignored; in the Badge.stories.tsx change the inner render logic inside
the Variants.map: if variant === 'destructive' render a single Badge (e.g.,
<Badge variant="destructive">destructive</Badge>) instead of mapping Colors,
otherwise continue mapping Colors.slice(0,3). Update the block that renders
Colors (referencing Variants, Colors and Badge) to conditionally skip the color
showcase for the 'destructive' variant.

In `@src/core/primitives/Dialog/fragments/DialogPrimitivePortal.tsx`:
- Around line 46-76: The three branches checking forceMount and keepMounted in
DialogPrimitivePortal that all return the same Floater.Portal are redundant;
consolidate them by removing the conditional branches and returning a single
Floater.Portal using rootElementRef.current, props, and children, or if
forceMount/keepMounted should alter behavior, implement the intended differences
inside the component (e.g., conditionally render children, change portal props,
or apply mounting logic) within the DialogPrimitivePortal function so the
symbols forceMount, keepMounted, Floater.Portal, rootElementRef, children, and
props are used meaningfully.

---

Minor comments:
In `@src/components/ui/Checkbox/stories/Checkbox.stories.tsx`:
- Around line 85-92: The label ternary and the conditional description in
Checkbox.stories.tsx are mis-mapped: the expression rendering the label text
(inside the <label> for variable key) currently gives 'Accept terms and
conditions' for both 'terms' and 'notifications', and the description block
beneath only targets 'notifications' but uses the terms copy. Fix the ternary so
key === 'terms' => 'Accept terms and conditions', key === 'notifications' =>
'Enable notifications' (and for 'disabled' use an appropriate fallback like
'Disabled' or the intended label), and update the conditional paragraph
rendering so that 'terms' shows "By clicking this checkbox, you agree to the
terms." and 'notifications' shows a notifications-specific line (e.g., "Enable
notifications to receive updates."), leaving 'disabled' without the description
if intended; adjust the label rendering and the conditional (key === 'terms' /
key === 'notifications') to match these mappings.

In `@src/components/ui/Collapsible/stories/Collapsible.stories.tsx`:
- Around line 27-33: The DoubleChevronIcon SVG is decorative and being announced
by screen readers; update the DoubleChevronIcon component to hide it from
assistive tech by adding accessibility attributes to the <svg> element (e.g.,
aria-hidden="true" and focusable="false") so the graphic is removed from the
accessibility tree and not focusable; keep spreading {...props} but ensure these
attributes are present on the svg in the DoubleChevronIcon function.

In `@src/components/ui/HoverCard/stories/HoverCard.stories.tsx`:
- Around line 63-65: The trigger uses a non-semantic <span> which breaks
keyboard accessibility; replace the <span onClick={() => setOpen(true)}> inside
HoverCard.Trigger with a <button type="button"> that calls setOpen(true) (and
preserves any className/aria props) so the controlled HoverCard example
demonstrates an accessible, keyboard-focusable trigger; ensure any existing
styling or props are forwarded to the button and keep the open ? 'Hover Here' :
'Show Card' label logic.

In `@src/components/ui/Progress/stories/Progress.stories.tsx`:
- Around line 40-47: The range input rendered (the <input> with type="range"
using value, setValue, onChange and className="w-full max-w-[25.25rem]") needs
an accessible name; add either an aria-label (e.g., aria-label="Progress" or
similar descriptive text) to the input or wire it to a visible <label
htmlFor="..."> by giving the input an id and matching label text so assistive
tech can discover the control; update the input element where value and onChange
are defined (and ensure the label text describes the control's purpose).

In `@src/components/ui/Table/stories/Table.stories.tsx`:
- Around line 74-75: The actions column header is empty so screen readers can't
identify the icon-only buttons; update the two Table.ColumnCellHeader instances
(the one currently with style width: '3rem' and the other occurrence around rows
89-92) to include an accessible label such as the visible text "Actions" or a
visually-hidden span "Actions" inside the Table.ColumnCellHeader, and ensure
each row action icon/button (the icon-only controls referenced in the row
rendering) includes an appropriate aria-label or title attribute describing the
action.
- Around line 47-52: The input in the Table story is missing an accessible name;
add one by giving the input an id and a visible or visually-hidden label or by
adding an aria-label (e.g., aria-label="Filter emails") so assistive tech can
identify it when the placeholder disappears; update the input element in
Table.stories.tsx (the <input ... placeholder="Filter emails..." ... />) to
include either a matching <label htmlFor="filter-emails">Filter emails</label>
or aria-label="Filter emails".

In `@src/core/primitives/Dialog/fragments/DialogPrimitivePortal.tsx`:
- Around line 29-32: The querySelector cast for themeContainer in
DialogPrimitivePortal (the themeContainer assignment using
themeContext?.portalRootRef.current ||
document.querySelector('[data-rad-ui-portal-root]') as HTMLElement || ...)
should include null for type-safety and to match HoverCardPortal.tsx; change the
HTMLElement cast to HTMLElement | null (e.g., cast the
document.querySelector('[data-rad-ui-portal-root]') to HTMLElement | null) so
themeContainer's union resolution is correct and TypeScript won't assume a
non-null HTMLElement.

In `@styles/themes/components/alert-dialog.scss`:
- Around line 94-95: The media query at `@media` (width >= 640px) currently
overlaps the mobile rule `@media` (width <= 640px) at exactly 640px; update the
desktop breakpoint to be exclusive (e.g., change `@media` (width >= 640px) to
`@media` (width > 640px)) so the mobile and desktop rules do not both apply; apply
the same boundary fix to the other overlapping queries in the file (the block
around lines 127-146) so each breakpoint is mutually exclusive.

In `@styles/themes/components/context-menu.scss`:
- Around line 91-94: The danger variant loses its red text when
highlighted/selected because the generic highlighted selectors override it;
update the `.rad-ui-context-menu-danger` rules to include matching state
selectors so the danger color is preserved (add `[data-highlighted]` and
`[aria-selected="true"]` variants for `.rad-ui-context-menu-danger` and the
inner elements that currently set color in the
`[data-highlighted]`/`[aria-selected="true"]` blocks) — target the
`.rad-ui-context-menu-danger` class and the existing `[data-highlighted]` and
`[aria-selected="true"]` selectors referenced in the diff so
highlighted/selected danger items keep the danger color.

In `@styles/themes/components/radio-group.scss`:
- Around line 32-35: The checked-state rule for the radio-group
(&[data-state="checked"]) increases border-width from 1px to 2px causing a 1px
layout shift; instead, keep the border-width constant and emulate a thicker
border using an inset box-shadow or outline so inner content doesn't move.
Update the &[data-state="checked"] rule in radio-group.scss to remove the
border-width change and add an inset box-shadow (or outline) using
var(--rad-ui-text-primary), or alternatively adjust padding/margin to compensate
while keeping box-sizing consistent.

In `@styles/themes/components/radiocards.scss`:
- Around line 80-85: The disabled state block for the radio card selectors
(&[data-disabled], &:disabled) mixes cursor: not-allowed with pointer-events:
none which makes the cursor ineffective; update the rule to remove
pointer-events: none (leave cursor: not-allowed and opacity) so the not-allowed
cursor is visible for disabled items, and if needed move pointer-events: none to
an inner interactive element or toggle it only when you truly want to block
pointer interactions.

In `@styles/themes/components/select.scss`:
- Around line 130-154: The selected option's styles ([aria-selected="true"]) are
overriding hover/keyboard feedback ([data-highlighted], [data-active="true"]);
update the stylesheet so highlight/active states take precedence by adding more
specific selectors or reordering: create combined selectors like
&[aria-selected="true"][data-highlighted] and
&[aria-selected="true"][data-active="true"] (and also adjust
.rad-ui-select-item-indicator if needed) to set background-color:
var(--rad-ui-surface-subtle), color: var(--rad-ui-text-primary) and opacity for
the indicator, ensuring hovered/active selected items show the highlight instead
of surface-muted.

In `@styles/themes/components/slider.scss`:
- Around line 133-136: The .rad-ui-slider-mark-label in the vertical slider
variant lacks a position, so its top/right rules are ineffective while
transform: translateY(-50%) incorrectly shifts it; update the
.rad-ui-slider-mark-label styling used by the vertical slider to remove
top/right/transform and instead rely on the flex row layout by using margin
(e.g., margin-right or margin-left as appropriate) and
align-items/justify-content to vertically center the label; adjust any parent
.rad-ui-slider (or vertical variant selector) to ensure the label is placed in
the flex row and uses margin-based spacing rather than absolute positioning.

In `@styles/themes/components/splitter.scss`:
- Around line 59-70: The ::after pseudo-element still has transitions so users
with prefers-reduced-motion still see the fade; update the
prefers-reduced-motion media query that currently targets the handle to also
target the handle::after (and any other pseudo-elements used, e.g., the visible
indicator) and disable its animations/transitions (set transition: none or
remove opacity/background-color transitions) for the selectors that include
::after (references: the rule containing &::after and the other blocks around
lines 111-123 and 144-151).

In `@styles/themes/components/steps.scss`:
- Around line 50-55: In the &[data-state="completed"] rule for
.rad-ui-steps-bubble replace the hardcoded "color: white" with the semantic
token --rad-ui-text-inverse; update the declaration to use
var(--rad-ui-text-inverse) so text on accent backgrounds uses the design system
token (match the pattern used in button.scss and tooltip.scss).

In `@styles/themes/components/switch.scss`:
- Around line 1-12: The .rad-ui-switch rule uses all: unset which resets
box-sizing to content-box and causes the component width to grow by borders; add
box-sizing: border-box immediately after all: unset in the .rad-ui-switch
selector to ensure borders are included in the width so the checked thumb (the
switch's thumb translateX value used in the Checked story) aligns correctly with
the right edge; after adding it, verify the Checked story thumb position and
adjust translateX only if needed.

In `@styles/themes/components/table.scss`:
- Line 2: The table wrapper's border uses a nonexistent token (--rad-ui-border);
replace it with an existing semantic border token such as --rad-ui-border-soft
(or more specific variant --rad-ui-border-soft-default or
--rad-ui-border-soft-strong as appropriate for visual weight) in the rule that
sets "border: 1px solid var(--rad-ui-border)" so the declaration never drops
when the token is undefined and matches the project's semantic token naming.

In `@styles/themes/components/textarea.scss`:
- Around line 21-24: The ::placeholder rule in the textarea component currently
hard-codes font-size: 1rem which breaks the small/large size variants; update
the &::placeholder selector in styles/themes/components/textarea.scss to use
font-size: inherit (or remove the font-size declaration) so the placeholder
inherits the component's current size variant, and apply the same change to the
duplicate placeholder block around lines 62-76 to keep behavior consistent.

In `@styles/themes/components/toolbar.scss`:
- Around line 100-107: The hover rule currently overrides the selected rule
because both selectors (&[data-state='on'] and &:hover) have equal specificity;
update the CSS so hover styling does not apply when the item is selected — e.g.
change the hover selector to only target non-selected items (use
&:hover:not([data-state='on']) or add a specific rule for
&[data-state='on']:hover to preserve its background) so the &[data-state='on']
accent background and color remain visible while hovering.

---

Nitpick comments:
In `@src/components/ui/Card/fragments/CardRoot.tsx`:
- Around line 17-23: The CardContext.Provider is being passed an inline object
value {{ rootClass }} which creates a new reference each render and can cause
unnecessary re-renders in consumers; fix this in CardRoot (the component
rendering CardContext.Provider) by memoizing the context value using
React.useMemo (e.g., const value = useMemo(() => ({ rootClass }), [rootClass]))
and pass that memoized value to CardContext.Provider instead of the inline
object; ensure you import useMemo from React if not already.

In `@src/components/ui/Card/stories/Card.stories.tsx`:
- Around line 29-47: Replace hardcoded Tailwind color classes in the
Card.stories form inputs (e.g., text-gray-950, border-gray-300, bg-gray-50 used
on the input and label elements) with the project’s semantic design tokens or
the shared Input component so colors adapt to theme changes; either swap the
class names for token-based classes (e.g., token-text, token-border, token-bg
equivalents) or refactor the repeated input markup into the existing Input
component and use that in Card.stories.tsx to ensure consistent theming.

In `@src/components/ui/Slider/fragments/SliderRangeSlider.tsx`:
- Around line 204-206: Replace the hard-coded "8px" thumb offset in
SliderRangeSlider.tsx with a single shared value: define a constant or CSS
custom property (e.g., THUMB_HALF_SIZE or --thumb-half-size) used wherever the
thumb is centered, and update the two places that compute positions (the style
objects using percent1 and percent2 with orientation checks) to use that shared
value instead of 8px so future thumb-size changes only require one edit.

In `@src/components/ui/Slider/stories/Slider.stories.tsx`:
- Around line 10-15: Add a new story named VerticalWithMarks to this file that
renders the Slider in the vertical+marks branch so regressions are caught;
locate the stories block (where Basic.render and SandboxEditor are used) and
create VerticalWithMarks that returns a SandboxEditor wrapping a container with
a fixed height (so vertical layout is visible) and a Slider configured for
vertical orientation and marks (e.g., set the Slider's vertical/orientation prop
and a marks array and a defaultValue) so the story exercises the vertical and
marks code paths.

In `@src/components/ui/TextArea/stories/TextArea.stories.tsx`:
- Around line 12-24: The story currently uses aria-label on <TextArea> while the
visible "Message" label and helper text aren't programmatically attached; update
the story to demonstrate a real label/description pair by rendering the visible
label text as the form label (use the TextArea's label prop or wrap with a
<label> tied to the textarea id) and give the helper copy an id (e.g.,
message-desc) then pass that id into TextArea via aria-describedby so the label
and helper are correctly associated with the textarea; update the
placeholders/props accordingly to remove the redundant aria-label.

In `@src/components/ui/Theme/Theme.tsx`:
- Around line 40-43: The current useCallback for setRefs includes the
parent-provided ref in its dependency array which can recreate the callback when
a new ref object/function is passed; fix this by storing the latest ref in a
stable ref and using that inside a deps-free callback: create a ref holder
(e.g., refRef = useRef(ref)), update refRef.current via useEffect whenever prop
ref changes, then define setRefs with useCallback((node) => {
containerRef.current = node; assignRef(refRef.current, node); }, []) so setRefs
identity stays stable while still using the latest parent ref; reference
symbols: setRefs, containerRef, assignRef, ref, refRef.

In `@src/components/ui/Tooltip/fragments/TooltipContent.tsx`:
- Line 35: TooltipContent uses a simpler portal root fallback (FloatingPortal
root={themeContext?.portalRootRef.current || themeContext?.containerRef.current
|| undefined}) that is inconsistent with HoverCardPortal and
DialogPrimitivePortal; update TooltipContent to use the same fallback lookup
those components use (check for portalRootRef, containerRef, then query the DOM
for [data-rad-ui-portal-root] and any other selectors used by
HoverCardPortal/DialogPrimitivePortal) or extract and call the shared utility
resolver (e.g., getPortalRoot or resolvePortalRoot) so FloatingPortal receives
the identical resolved root as other portal components.

In `@styles/themes/components/alert-dialog.scss`:
- Around line 100-123: Extract the duplicated styles for
.rad-ui-alert-dialog-action and .rad-ui-alert-dialog-cancel into a single shared
placeholder/utility and have both classes extend it: create a placeholder (e.g.,
%rad-ui-alert-dialog-button-skin) that contains the common base rules and the
shared &:hover rules, then replace the duplicated rule sets by `@extend-ing` that
placeholder in both .rad-ui-alert-dialog-action and .rad-ui-alert-dialog-cancel
(apply same change to the other duplicated pair mentioned). Preserve any unique
properties by keeping them in the specific classes and only move truly identical
declarations into the shared placeholder so future token changes stay in one
place.

In `@styles/themes/components/avatar-group.scss`:
- Around line 74-79: The duplicated box-shadow on the more specific selector
&[data-slot="avatar"] .rad-ui-avatar-group-item appears redundant because
.rad-ui-avatar-group-item already sets the same box-shadow; either remove the
entire &[data-slot="avatar"] block or, if it exists to re-apply the shadow after
other overrides, leave it and add a concise comment above the block explaining
that it intentionally restores the 0 0 0 2px var(--rad-ui-surface-panel)
box-shadow for grouped avatar items (reference selector
.rad-ui-avatar-group-item and attribute selector [data-slot="avatar"]).

In `@styles/themes/components/card.scss`:
- Around line 12-16: The outline variant selector &[data-card-variant="outline"]
repeats background-color and border-color that are already defined on the base
.rad-ui-card; remove the redundant background-color and border-color
declarations from the &[data-card-variant="outline"] block and keep only
box-shadow: none so the variant inherits the base .rad-ui-card colors while
overriding the shadow.

In `@styles/themes/components/code.scss`:
- Around line 18-22: The soft variant block &[data-code-variant="soft"] in
styles/themes/components/code.scss is redundant because it duplicates the base
.rad-ui-code styles; either remove the entire &[data-code-variant="soft"] rule
so consumers inherit the base styles, or modify it to be visually distinct (for
example change background-color to var(--rad-ui-color-accent-50) and/or
remove/alter border like no border or a lighter border color) — update the
selector block for &[data-code-variant="soft"] accordingly to implement one of
these two options.

In `@styles/themes/components/dialog.scss`:
- Line 44: Remove the redundant "gap: 0" declaration in
styles/themes/components/dialog.scss (the "gap: 0" property shown in the dialog
styles) since flex containers default to gap: 0; only keep it if you are
intentionally overriding an earlier non-zero value—otherwise delete the line to
simplify the stylesheet.

In `@styles/themes/components/navigation-menu.scss`:
- Around line 32-44: The open-state box-shadow is snapping because the
transition declaration on navigation-menu.scss (currently on the selector with
&:hover) omits box-shadow; update the transition to include box-shadow (e.g.,
add "box-shadow 0.2s" to the transition property) so the &[data-state="open"]
change animates smoothly alongside background and color.
- Around line 71-83: Replace the hardcoded border-radius value in the
.rad-ui-navigation-menu-link rule with the theme radius token; locate the
.rad-ui-navigation-menu-link selector and change border-radius: 4px to use the
project radius CSS variable (e.g. var(--rad-ui-radius-1) or the established
radius token name) so the component follows the theming tokens.
- Around line 90-93: The rule targeting &[aria-current="page"],
&[data-active="true"] uses the accent token --rad-ui-color-accent-100; replace
that token with the semantic token --rad-ui-surface-subtle to match select.scss
and the design system. Update the background value in the navigation-menu.scss
selector (the &[aria-current="page"], &[data-active="true"] block) to use
var(--rad-ui-surface-subtle) so active/selected styling is consistent across
themes.

In `@styles/themes/components/number-field.scss`:
- Line 63: The .rad-ui-number-field-decrement nested selector is missing the
leading space used by other nested selectors, making the nesting unclear; update
the indentation of the .rad-ui-number-field-decrement block to match the nesting
level of .rad-ui-number-field-input and .rad-ui-number-field-increment so it
aligns with the other nested selectors and maintains consistent SCSS structure.
- Around line 36-87: Both .rad-ui-number-field-increment and
.rad-ui-number-field-decrement contain identical rules; consolidate them by
replacing the two duplicate blocks with a single combined selector
".rad-ui-number-field-increment, .rad-ui-number-field-decrement { ... }" that
contains the shared properties and nested &:hover and &:focus-visible states,
then remove the redundant separate block so maintenance is easier and
duplication is eliminated.

In `@styles/themes/components/radio-group.scss`:
- Around line 26-30: The rule currently applies both &:focus and &:focus-visible
so the focus ring appears on mouse clicks as well as keyboard navigation; change
the selector to only use &:focus-visible in the radio-group styles block
(replace the combined &:focus, &:focus-visible rule with a single
&:focus-visible rule that keeps outline: none and box-shadow:
var(--rad-ui-ring-shadow-sm)) so focus rings are shown only for keyboard users.

In `@styles/themes/components/radiocards.scss`:
- Around line 1-6: The .rad-ui-radio-cards selector currently hardcodes
max-width: 29rem which limits reuse; make this configurable by replacing the
hardcoded value with a CSS custom property (e.g., use
var(--rad-ui-radio-cards-max-width, 29rem)) so callers can override it, or
alternatively add documentation/comments near .rad-ui-radio-cards describing the
intentional constraint and default value; update the SCSS for the rule and any
relevant component docs/readme to reflect the new CSS variable name and default.

In `@styles/themes/components/tabs.scss`:
- Around line 44-48: Replace the hardcoded focus-visible box-shadow rgba values
in the tabs component with the theme tokens used elsewhere so the ring adapts to
theme/mode changes: locate the box-shadow definition in
styles/themes/components/tabs.scss (the block setting box-shadow for focus/
focus-visible) and swap the rgba entries for the corresponding design tokens
(e.g., use var(--rad-ui-ring-shadow-sm) and/or var(--rad-ui-ring-shadow) in
place of the rgba(...) layers) so it matches radio-group.scss / radiocards.scss
token usage and inherits theme colors.
- Around line 60-66: The active+focus-visible rule (&.active:focus-visible)
currently uses a hardcoded box-shadow; replace it with the same token-based
focus-ring values used elsewhere in the theme (use the shared focus/ring tokens
used for regular :focus-visible rules) so the ring color, offset and blur come
from theme variables rather than literals, updating the &.active:focus-visible
declaration in styles/themes/components/tabs.scss to reference those tokens for
consistency.

In `@styles/themes/components/toggle.scss`:
- Around line 18-28: The hover selector (&:hover) and the pressed selectors
(&[data-state="on"], &[aria-pressed="true"]) currently use the same
background-color and border-color, so update the pressed state styling in
components/toggle.scss to use a distinct token (e.g., a stronger surface or
border token) or adjust color intensity so pressed has a clearer visual
difference from hover; locate the selectors "&:hover" and the group
"&[data-state=\"on\"], &[aria-pressed=\"true\"]" and change the background-color
and/or border-color values for the pressed group to a stronger/different CSS
variable (and ensure color contrast via color: var(--rad-ui-text-primary)
remains appropriate).

In `@styles/themes/default.scss`:
- Around line 91-95: The portal root CSS hardcodes z-index: 1000 on the
[data-rad-ui-portal-root] selector; replace this hardcoded value with a
theme/token variable so host apps can override overlay stacking. Update the
styles to reference your design token (e.g., $z-index-portal or
var(--z-index-portal)) and ensure the token exists in the theme tokens file and
is exported where themes are composed; adjust any places that import or use
[data-rad-ui-portal-root] to rely on the token rather than the numeric literal.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
styles/themes/components/minimap.scss (1)

56-66: ⚠️ Potential issue | 🟡 Minor

Duplicate padding-bottom declaration.

Line 61 sets padding-bottom: 0px; but line 65 sets padding-bottom: 20px;, making line 61 dead code.

🧹 Suggested fix to remove redundant property
             padding-left: 10px;
             display: flex;
             flex-direction: column;
             gap: 4px;
-            padding-bottom: 0px;
             color: var(--rad-ui-text-secondary);
             font-size: 0.875rem;
             padding-top: 5px;
             padding-bottom: 20px;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/minimap.scss` around lines 56 - 66, The
.rad-ui-minimap-content block contains a duplicated padding-bottom (0px at the
earlier line and 20px later); remove the redundant padding-bottom: 0px
declaration so only the intended padding-bottom: 20px remains in the
.rad-ui-minimap-content rule to avoid dead code and ensure consistent styling.
♻️ Duplicate comments (1)
styles/themes/components/context-menu.scss (1)

14-16: ⚠️ Potential issue | 🟠 Major

Move demo-sized trigger dimensions out of shared theme styles.

Line 14–16 still force every .rad-ui-context-menu-trigger into a large canvas, which breaks normal inline/custom trigger composition.

Suggested fix
 .rad-ui-context-menu-trigger {
@@
-    padding: 6rem 1.5rem;
-    width: 24rem;
-    min-height: 13.5rem;
+    padding: 1rem 1.25rem;

Use a story/docs-only modifier class (or CSS variable override) for the 24rem × 13.5rem demo sizing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/context-menu.scss` around lines 14 - 16, The shared
theme file currently forces demo dimensions on the component by applying
padding/width/min-height in context-menu.scss to the default selector (affecting
.rad-ui-context-menu-trigger); remove those demo sizes from the shared rule and
instead add a story/docs-only modifier or CSS-variable override (e.g.,
.rad-ui-context-menu-trigger--demo or --rad-context-menu-demo-width/height) and
update the demo/story styles to apply that modifier/variables so inline/custom
triggers keep normal sizing while the demo uses 24rem × 13.5rem.
🧹 Nitpick comments (15)
styles/themes/components/tabs.scss (1)

38-65: Hardcoded focus ring colors won't adapt to theme changes.

The rgba(17, 24, 39, ...) values are hardcoded dark gray (#111827) which will look inconsistent in different themes, particularly light vs dark mode. Consider using a CSS custom property for the focus ring color to maintain theming consistency with the rest of the file.

♻️ Suggested refactor using theme tokens
             &:focus-visible{
                 z-index: 2;
                 color: var(--rad-ui-text-primary);
                 background-color: var(--rad-ui-surface-panel);
                 outline: none;
                 box-shadow:
-                    0 0 0 3px rgba(17, 24, 39, 0.3),
-                    0 0 0 4px rgba(17, 24, 39, 0.1),
+                    0 0 0 3px var(--rad-ui-focus-ring, rgba(17, 24, 39, 0.3)),
+                    0 0 0 4px var(--rad-ui-focus-ring-outer, rgba(17, 24, 39, 0.1)),
                     0 1px 2px rgba(0, 0, 0, 0.05),
                     0 2px 6px rgba(0, 0, 0, 0.04);
             }

Apply similar changes to &.active:focus-visible block (lines 59-65).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/tabs.scss` around lines 38 - 65, The focus ring
color is hardcoded as rgba(17,24,39,...) in the &:focus-visible, &.active and
&.active:focus-visible blocks which breaks theming; replace those rgba(...)
color literals with a CSS custom property (e.g. var(--rad-ui-focus-ring-color,
rgba(17,24,39,0.3)) and similar fallbacks for the other alpha stops) so the ring
adapts to theme tokens, and ensure a sensible default is declared in your theme
tokens file or a root selector so existing visuals remain unchanged if the token
is missing.
knowledge/design_system/clarity_design_system.md (2)

25-38: Fix markdown table formatting to satisfy lint and avoid rendering issues.

The table has missing trailing pipes on some rows and should be separated by blank lines. This will keep markdownlint clean and improve consistency. (e.g., Lines 34, 35, and spacing around the table near Line 37).

Proposed doc-format fix
 ### 2. Intent-Based Design (VERY IMPORTANT)
 
-
 | Step | Token | Category                  | Usage Description |
 |------|------|---------------------------|------------------|
 | 1    | 50   | Backgrounds               | App/page background, lowest visual weight |
 | 2    | 100  | Backgrounds               | Subtle surfaces, section backgrounds |
 | 3    | 200  | Interactive components    | Hover backgrounds, subtle interaction states |
 | 4    | 300  | Interactive components    | Active/pressed states, stronger surface contrast |
 | 5    | 400  | Interactive components    | Selected states, emphasized surfaces |
 | 6    | 500  | Borders & separators      | Light borders, dividers |
 | 7    | 600  | Borders & separators      | Default borders, stronger separators |
-| 8    | 700  | Solid colors              | Disabled text/icons, muted UI elements
-| 9    | 800  | Solid colors              | supporting content
+| 8    | 700  | Solid colors              | Disabled text/icons, muted UI elements |
+| 9    | 800  | Solid colors              | Supporting content |
 | 10   | 900  | Accessible text           | Secondary text, high readability |
 | 11   | 1000 | Accessible text           | Headings,Primary text, max contrast text (near black) |
+
 ---
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@knowledge/design_system/clarity_design_system.md` around lines 25 - 38, The
markdown table in clarity_design_system.md has malformed rows (missing trailing
"|" on some rows) and lacks blank lines before and after the table which breaks
markdownlint; fix it by adding the missing trailing pipe characters to every
table row (ensure each row, e.g., the rows for steps 8, 9, 10, 11, etc., ends
with "|"), verify the header separator row is intact, and add a blank line both
immediately before the table and immediately after the trailing "---" separator
so the table renders cleanly and passes linting.

63-68: Make accessibility guidance testable with explicit contrast criteria.

“Must meet WCAG AA” is good, but this section should specify concrete ratio targets for intended token pairs to prevent inconsistent usage.

Proposed clarity addition
 ### 5. Accessibility First
 
 - Text (900–1000) must meet **WCAG AA**
 - Borders (400–500) must be visible but not distracting
-- Avoid low contrast adjacent steps
+- Avoid low-contrast adjacent steps
+- Define and validate target pairs (example):
+  - Body text: 900 on 50/100 ≥ 4.5:1
+  - Large text/headings: 1000 on 50/100 ≥ 3:1
+  - Borders: 500 on 50/100 should remain visually distinct in both light and dark themes
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@knowledge/design_system/clarity_design_system.md` around lines 63 - 68, The
"Accessibility First" section (### 5. Accessibility First) needs explicit,
testable contrast criteria instead of just “must meet WCAG AA”: add concrete
ratio targets for the referenced tokens (Text (900–1000), Borders (400–500)) and
common token pairs — e.g., require text (normal) to be >=4.5:1 on its intended
background, large text >=3:1, UI components and primary controls >=3:1, and
borders (400–500) to be >=3:1 against adjacent surfaces; also call out minimum
contrast deltas for adjacent steps (suggest >=3:1) and state that all ratios
must be measurable with standard tools (e.g., WCAG contrast checkers) so
designers can validate token usage.
styles/themes/components/minimap.scss (1)

30-38: Incomplete transition properties for in-view state.

The transition animates color and box-shadow, but the [data-in-view="true"] state also changes border-color and background-color. These will snap instantly while other properties animate smoothly, creating a jarring visual effect.

♻️ Suggested fix to add missing transition properties
                 transition:
                     color 0.3s ease-in-out,
+                    background-color 0.3s ease-in-out,
+                    border-color 0.3s ease-in-out,
                     box-shadow 0.3s ease-in-out;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/minimap.scss` around lines 30 - 38, The transition
currently only lists color and box-shadow, but the &[data-in-view="true"] rule
also changes border-color and background-color; update the transition
declaration (the block containing "transition: color 0.3s ease-in-out,
box-shadow 0.3s ease-in-out;") to include border-color and background-color so
those properties animate as well when the selector &[data-in-view="true"]
toggles.
src/core/primitives/Collapsible/fragments/CollapsiblePrimitiveContent.tsx (1)

134-134: Including shouldRender in the dependency array may cause redundant effect executions.

When the closing animation completes, setShouldRender(false) at line 121 triggers a re-run of this effect. At that point, open is still false, so the closing branch (lines 107-124) executes again on content that's already collapsed. While the cleanup on lines 126-132 prevents the old timeout from firing, the effect still performs unnecessary DOM reads (scrollHeight) and schedules new animation frames.

Consider removing shouldRender from the dependency array or adding an early return guard:

Option 1: Remove shouldRender from dependencies
-    }, [open, transitionDuration, forceMount, shouldRender]);
+    }, [open, transitionDuration, forceMount]);
Option 2: Add early return guard
         if (!ref.current) return;
+
+        // Skip if already unmounted (closing animation completed)
+        if (!open && !shouldRender && !forceMount) return;
 
         if (transitionDuration === 0) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/primitives/Collapsible/fragments/CollapsiblePrimitiveContent.tsx` at
line 134, The effect is re-running unnecessarily because shouldRender is
included in the dependency array; either remove shouldRender from the array or
add an early-return guard at the top of the useEffect so it bails when both open
is false and shouldRender is false. Specifically, in the effect that references
open, transitionDuration, forceMount, shouldRender (around the block that calls
setShouldRender(false) and uses scrollHeight/animation frames), remove
shouldRender from the dependency list or add: if (!open && !shouldRender)
return; to avoid extra DOM reads/RAF scheduling; keep other dependencies (open,
transitionDuration, forceMount) intact and ensure any timers/RAFs still get
cleaned up in the existing cleanup block.
styles/themes/components/toggle.scss (1)

17-24: Consider transitioning the properties you mutate on hover.

Hover changes background-color and border-color, but transition currently excludes both, so state change is abrupt.

Optional tweak
     transition:
         color 150ms cubic-bezier(0.4, 0, 0.2, 1),
-        box-shadow 150ms cubic-bezier(0.4, 0, 0.2, 1);
+        background-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
+        border-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
+        box-shadow 150ms cubic-bezier(0.4, 0, 0.2, 1);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/toggle.scss` around lines 17 - 24, The transition
declaration currently only lists color and box-shadow but the :hover block
mutates background-color and border-color; update the transition on the relevant
selector (the rule surrounding transition and &:hover) to include
background-color and border-color (e.g., add "background-color 150ms ..." and
"border-color 150ms ...") so hover state changes animate smoothly rather than
abruptly.
styles/themes/components/slider.scss (3)

78-79: Redundant state selectors with no visual differentiation.

Both [data-state="dragging"] and [data-state="active"] apply the same transform: translateY(-50%) that's already set on the base selector (line 63). If these are placeholders for future styling, consider adding a comment. Otherwise, they can be removed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/slider.scss` around lines 78 - 79, The two redundant
rules [&data-state="dragging"] and [&data-state="active"] in slider.scss
duplicate the base selector's transform (translateY(-50%)); either remove these
duplicate selectors to clean up the CSS or, if kept as placeholders for future
state-specific styling, replace their bodies with a brief comment noting they
are intentional placeholders (e.g., "TODO: state-specific styles") so reviewers
know they are deliberate—locate the rules by the selectors
[data-state="dragging"] and [data-state="active"] and update accordingly.

50-52: Consider using attribute selector instead of listing class variants.

The current approach lists all thumb class variants explicitly. If new variants are added, this selector needs updating. Using an attribute selector or a more flexible pattern would be more maintainable.

♻️ Alternative: Use attribute selector
-  .rad-ui-slider-thumb,
-  .rad-ui-slider-thumb-lower,
-  .rad-ui-slider-thumb-upper {
+  [class^="rad-ui-slider-thumb"],
+  [class*=" rad-ui-slider-thumb"] {

Or keep the explicit listing if you want strict control over which elements receive these styles.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/slider.scss` around lines 50 - 52, Replace the
explicit multi-class selector (.rad-ui-slider-thumb, .rad-ui-slider-thumb-lower,
.rad-ui-slider-thumb-upper) with a single, more maintainable selector that
matches all thumb variants (e.g., an attribute or prefix-based selector such as
[class^="rad-ui-slider-thumb"] or a custom data-* attribute like
[data-rad-ui-thumb]) so new variants inherit the styles without editing this
rule; update any markup to include the chosen attribute if using data-* and
ensure the rule still targets only slider thumbs (adjust specificity if needed).

161-175: Duplicate thumb selectors in vertical orientation.

Lines 161-175 repeat the thumb selector list and include the same redundant state selectors (lines 168-169) as the horizontal variant. Consider extracting shared thumb properties to reduce duplication, or use a mixin if the SCSS setup supports it.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/slider.scss` around lines 161 - 175, The vertical
thumb block duplicates the same selectors and state rules as the horizontal one
(.rad-ui-slider-thumb, .rad-ui-slider-thumb-lower, .rad-ui-slider-thumb-upper
with &[data-state="dragging"], &[data-state="active"], and &:focus-visible);
extract the shared properties into a reusable SCSS construct (a mixin or
placeholder selector) and include it from both the horizontal and vertical
orientation blocks, leaving only orientation-specific rules (e.g.,
left/top/transform differences) in each block and removing the redundant state
selectors from the vertical section.
styles/themes/components/table.scss (1)

36-42: Hover state has no visual transition.

The row hover changes background-color but lacks a transition property, causing an abrupt color change. Header cells at line 30 also use --rad-ui-surface-panel, which means both header and row share the same background—the hover won't visually distinguish rows from headers.

🎨 Add transition for smoother hover effect
         .row{
             vertical-align: inherit;
             border-bottom: 1px solid var(--rad-ui-border-soft);
             background-color: var(--rad-ui-surface-panel);
+            transition: background-color 150ms ease;


             &:hover{
                 background-color: var(--rad-ui-surface-subtle);
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/table.scss` around lines 36 - 42, Add a
background-color transition to the row hover rule to smooth the change (e.g.,
transition: background-color 150ms ease) by updating the block containing
&:hover; also ensure header cells do not share the same background variable as
rows—either change the header cell background from --rad-ui-surface-panel to a
distinct token (e.g., --rad-ui-surface-header) or pick a hover color variable
different from the header so the &:hover background-color:
var(--rad-ui-surface-subtle) is visually distinct.
styles/cssTokens/base.tokens.css (2)

813-825: Mixed color format between light and dark mode gray tokens.

Light mode gray tokens now use hex values (lines 3-14), while dark mode continues to use oklch() (lines 814-825). This creates a maintenance inconsistency—if you need to adjust a gray shade, you'll need to work with different color spaces.

Consider whether both modes should use the same format for consistency, or document the rationale for the mixed approach.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/cssTokens/base.tokens.css` around lines 813 - 825, The dark theme gray
tokens (--rad-ui-color-gray-50 through --rad-ui-color-gray-1000) use oklch()
while the light tokens use hex, causing inconsistent color formats; pick a
single color format (either hex or oklch) and convert the dark-theme token
values to that format (or convert light tokens to oklch) so all
--rad-ui-color-gray-* variables use the same notation, then update any
documentation or a short inline comment explaining the chosen convention; locate
and edit the block containing [data-rad-ui-theme="dark"] and the specific
variables --rad-ui-color-gray-50 ... --rad-ui-color-gray-1000 to apply the
converted values.

3-14: Inconsistent indentation in gray token declarations.

Line 3 has proper indentation inside the :root block, but lines 4-14 are left-aligned. This breaks the formatting consistency with the rest of the file.

🔧 Proposed fix for indentation
     :root {
       --rad-ui-color-gray-50: `#fafafa`;
---rad-ui-color-gray-100: `#f4f4f5`;
---rad-ui-color-gray-200: `#e4e4e7`;
---rad-ui-color-gray-300: `#d4d4d8`;
---rad-ui-color-gray-400: `#a1a1aa`;
---rad-ui-color-gray-500: `#71717a`;
---rad-ui-color-gray-600: `#52525b`;
---rad-ui-color-gray-700: `#3f3f46`;
---rad-ui-color-gray-800: `#27272a`;
---rad-ui-color-gray-900: `#18181b`;
---rad-ui-color-gray-950: `#141416`;
---rad-ui-color-gray-1000: `#0f0f11`;
+      --rad-ui-color-gray-100: `#f4f4f5`;
+      --rad-ui-color-gray-200: `#e4e4e7`;
+      --rad-ui-color-gray-300: `#d4d4d8`;
+      --rad-ui-color-gray-400: `#a1a1aa`;
+      --rad-ui-color-gray-500: `#71717a`;
+      --rad-ui-color-gray-600: `#52525b`;
+      --rad-ui-color-gray-700: `#3f3f46`;
+      --rad-ui-color-gray-800: `#27272a`;
+      --rad-ui-color-gray-900: `#18181b`;
+      --rad-ui-color-gray-950: `#141416`;
+      --rad-ui-color-gray-1000: `#0f0f11`;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/cssTokens/base.tokens.css` around lines 3 - 14, The gray color CSS
custom properties inside the :root block are misaligned; please align the
declarations (--rad-ui-color-gray-100 through --rad-ui-color-gray-1000) to match
the indentation of the first token (--rad-ui-color-gray-50) so all lines share
the same indentation level and spacing style, e.g., indent each line to the same
column within the :root block and keep one property per line.
styles/themes/components/alert-dialog.scss (2)

100-124: Consider consolidating duplicate action/cancel button styles.

The .rad-ui-alert-dialog-action and .rad-ui-alert-dialog-cancel styles are duplicated between the nested context (lines 100-124) and standalone context (lines 149-173). This duplication increases maintenance burden.

You could use SCSS placeholder selectors or mixins to DRY this up:

♻️ Example using placeholder selectors
// Define once at top of file
%alert-dialog-action-base {
  `@extend` .rad-ui-button;
  background-color: var(--rad-ui-text-primary);
  color: var(--rad-ui-surface-panel);
  border: 1px solid var(--rad-ui-text-primary);
  
  &:hover {
    background-color: color-mix(in oklab, var(--rad-ui-text-primary) 92%, var(--rad-ui-color-gray-1000));
    border-color: color-mix(in oklab, var(--rad-ui-text-primary) 92%, var(--rad-ui-color-gray-1000));
  }
}

%alert-dialog-cancel-base {
  `@extend` .rad-ui-button;
  background-color: var(--rad-ui-surface-panel);
  color: var(--rad-ui-text-primary);
  border: 1px solid var(--rad-ui-border-soft);
  box-shadow: var(--rad-ui-shadow-xs);
  
  &:hover {
    background-color: var(--rad-ui-surface-subtle);
    border-color: var(--rad-ui-border);
    color: var(--rad-ui-text-primary);
  }
}

// Then extend in both locations
.rad-ui-alert-dialog-footer .rad-ui-alert-dialog-action {
  `@extend` %alert-dialog-action-base;
}

.rad-ui-alert-dialog-action {
  `@extend` %alert-dialog-action-base;
}

Also applies to: 149-173

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/alert-dialog.scss` around lines 100 - 124, The
.rad-ui-alert-dialog-action and .rad-ui-alert-dialog-cancel rules are duplicated
in both the nested and standalone contexts; define shared SCSS placeholders or
mixins (e.g., %alert-dialog-action-base and %alert-dialog-cancel-base) that
contain the common properties (including &:hover blocks) and replace the
duplicated blocks by extending/including these placeholders wherever
.rad-ui-alert-dialog-action and .rad-ui-alert-dialog-cancel appear (both the
nested footer variant and the top-level selectors) so the styles are centralized
and maintained in one place.

106-109: Be aware that color-mix() lacks support in older browsers.

color-mix(in oklab, ...) is a CSS Color Level 5 feature supported in Chrome/Edge 111+ (March 2023), Firefox 113+ (May 2023), and Safari 16.2+ (October 2022). As of 2026, this covers over 93% of global browser usage. Older browsers will skip this declaration, resulting in no hover color change on the action button.

If your project targets older browsers, consider adding a fallback color or using a CSS custom property instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/alert-dialog.scss` around lines 106 - 109, The hover
rule using color-mix(in oklab, ...) may be ignored in older browsers; update the
&:hover block in styles/themes/components/alert-dialog.scss (the selector using
&:hover for the action button) to provide a compatible fallback before the
color-mix declaration — e.g., set a static background-color and border-color (or
use an existing CSS custom property like --rad-ui-color-gray-1000 or a derived
var(--rad-ui-hover)) and then keep the color-mix rule after it so modern
browsers use the advanced mix while legacy browsers pick the fallback.
styles/themes/components/radio-group.scss (1)

10-13: Reconsider width: max-content on the label.

Line 13 forces the label to its unwrapped width, so longer or localized option text will overflow instead of wrapping, and the hit area shrinks to the content width. If this row should stay responsive, leaving the width auto is safer.

Please verify this with a long label in a narrow container or Storybook viewport.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/radio-group.scss` around lines 10 - 13, The rule in
radio-group.scss that sets width: max-content forces labels to their unwrapped
width causing overflow and small hit areas; locate the CSS block containing
display: inline-flex; align-items: center; gap: 0.75rem; width: max-content and
replace/remove width: max-content (use width: auto or remove it) and optionally
add max-width: 100% to allow wrapping and keep responsiveness; then verify the
change by testing a long radio label in a narrow container or Storybook viewport
to ensure it wraps and the hit area expands correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@knowledge/design_system/clarity_design_system.md`:
- Around line 49-57: The phrase "slightly tinted white" conflicts with the "###
4. Neutral Only" rule (which requires true grayscale); update the wording so
both places are consistent—either remove "slightly tinted white" and replace it
with "neutral white (no tint)" or change the "Neutral Only" bullets to allow the
specific tint; locate the string "slightly tinted white" and the "### 4. Neutral
Only" section and make them use the same explicit wording (e.g., "neutral white
— no hue tint") to eliminate the semantic conflict.

In `@styles/themes/components/alert-dialog.scss`:
- Around line 94-98: The media queries for `(width >= 640px)` and `(width <=
640px)` overlap at exactly 640px; update them to create exclusive ranges—e.g.,
change the mobile query `(width <= 640px)` to `(width < 640px)` (or change the
desktop query to `(width >= 641px)`) so the rules in the alert-dialog styles no
longer conflict at 640px; adjust both occurrences (the block starting with
`@media (width >= 640px)` and the block starting with `@media (width <= 640px)`)
accordingly.

In `@styles/themes/components/card.scss`:
- Around line 77-115: Nested cards are picking up outer size rules because
selectors like &[data-card-size="small"] .rad-ui-card-content and
&[data-card-size="x-large"] .rad-ui-card-footer use descendant combinators;
change them to target only the current card’s direct children (use the child
combinator, e.g. &> .rad-ui-card-content and &> .rad-ui-card-footer) for all
size overrides (small, large, x-large) so outer card sizes cannot bleed into
nested cards.

In `@styles/themes/components/fix_tokens.cjs`:
- Line 4: The file hardcodes a machine-local path in the const dir variable and
immediately uses it; replace the hardcoded string for dir with a resolved path
built from __dirname or process.cwd() (using path.resolve/path.join) and allow
an override via an environment variable (e.g., process.env.COMPONENTS_DIR) so
the script is portable, and stop executing side-effects at module load — instead
compute or export a getter (e.g., getComponentsDir) or defer using dir until a
function is called so consumers can set overrides before use; update references
to dir accordingly.
- Around line 10-11: The current raw replacements (content.replace(/-950/g,
'-1000') and the similar 'gray-200' replacement) are too broad and mutate
unintended text; update these to use more specific regexes with
lookbehind/lookahead that target CSS variable declarations like the existing
patterns in this file (e.g., the surrounding variable-matching logic between
lines 14–23) so they only match values inside --variable: ... or token keys, not
arbitrary occurrences (use the same style of (?<=--[\w-]+:\s*)... or lookarounds
that assert variable context); keep the replacements applied to the same content
variable and preserve global flag semantics where appropriate.
- Around line 14-18: The two color-rewrite replace calls that currently use
(?<=color:\s*var\(--rad-ui-color-...) should be tightened so they only match the
standalone "color:" property and not compound properties like
"background-color:" or "outline-color:"; update the lookbehind in the two
replace patterns (the content.replace calls matching 600→700 and 800→900) to
require a boundary/anchor before "color:" (e.g. ensure "color:" is preceded by a
non-word/non-dash or a word boundary) so those replaces only run on actual
"color:" declarations and the later background-color replace (500|600|900→800)
can apply as intended.

In `@styles/themes/components/radio-group.scss`:
- Around line 47-49: The hover rule currently applies to disabled radio items;
update the selector that currently uses &:hover to exclude disabled items by
changing it to &:not([data-disabled]):hover (so the border-color change only
applies to interactive radios). Locate the rule using &:hover in
radio-group.scss and replace it with the :not([data-disabled]) guard around the
hover selector.
- Around line 51-60: The checked-state rule &[data-state="checked"] currently
comes after :focus-visible and hides the keyboard focus ring; add a combined
selector &[data-state="checked"]:focus-visible that merges the checked
box-shadow (the inset 1px color-mix shadow and subtle drop shadow) with the
focus ring (var(--rad-ui-ring-shadow-sm)) so both shadows are preserved (place
the combined selector after the existing rules or include it alongside the
checked rule) — reference the selectors &:focus-visible,
&[data-state="checked"], and add &[data-state="checked"]:focus-visible to apply
both shadows together.

In `@styles/themes/components/radiocards.scss`:
- Around line 22-24: The hardcoded rgba(255, 255, 255, 0.52) used in the
box-shadow in radiocards.scss causes light-only highlights that break dark mode;
replace each hardcoded white value (the box-shadow occurrences around the shown
block and the other instances) with a theme-aware token or dynamic color
expression such as var(--rad-ui-highlight-inset) or a semi-transparent
surface-based color (e.g., using color-mix() or rgba(var(--rad-ui-surface-rgb),
0.52)) so the inset highlight adapts to both light and dark themes; update all
occurrences in the radiocards.scss file to use the chosen token/expression
instead of the literal rgba(255,255,255,0.52).

In `@styles/themes/components/table.scss`:
- Around line 40-42: The hover rule under the table row selector (&:hover) uses
--rad-ui-surface-subtle which resolves to the same color as
--rad-ui-surface-panel, making the hover invisible; update the rule to use
--rad-ui-surface-hover instead (replace the background-color in the &:hover
block to var(--rad-ui-surface-hover)) so the row hover becomes visually
distinct.

In `@styles/themes/components/textarea.scss`:
- Around line 20-22: The placeholder rule in textarea.scss currently forces
font-size: 1rem causing size jumps for small/large textarea variants; change the
::placeholder selector to inherit the textarea's font-size (remove the fixed
1rem and use font-size: inherit or no font-size so it inherits) so placeholder
text matches the component variants (affects the &::placeholder block and the
equivalent rules at lines referenced for variants).
- Around line 25-41: The shared state selectors (&:disabled, &:read-only,
&:focus) are being overridden by same-specificity per-variant rules (.soft,
.outline, .ghost), so either move the shared state blocks to appear after the
variant selectors in textarea.scss so they win by source order, or lower the
variants' specificity (e.g. wrap variant selectors in :where(...) or otherwise
avoid repeating the base selector) so the shared state rules keep the
focus/disabled styling; update the &:disabled / &:read-only / &:focus blocks
(and the duplicate block at 116-142) accordingly to ensure shared state wins
while keeping only intentional per-variant overrides.

In `@styles/themes/components/toggle-group.scss`:
- Around line 10-12: The vertical orientation rule lives inside the component
scope (using &), so it never matches the default wrapper that gets
data-orientation (RovingFocusGroup.Group); update the CSS so the
[data-orientation="vertical"] selector targets the wrapper element used at
runtime (e.g., the RovingFocusGroup.Group wrapper) instead of being nested,
ensuring the flex-direction: column rule applies for vertical groups by either
moving the rule out of the nested scope or adding an explicit selector that
matches RovingFocusGroup.Group[data-orientation="vertical"] (and keep the same
flex-direction change).

---

Outside diff comments:
In `@styles/themes/components/minimap.scss`:
- Around line 56-66: The .rad-ui-minimap-content block contains a duplicated
padding-bottom (0px at the earlier line and 20px later); remove the redundant
padding-bottom: 0px declaration so only the intended padding-bottom: 20px
remains in the .rad-ui-minimap-content rule to avoid dead code and ensure
consistent styling.

---

Duplicate comments:
In `@styles/themes/components/context-menu.scss`:
- Around line 14-16: The shared theme file currently forces demo dimensions on
the component by applying padding/width/min-height in context-menu.scss to the
default selector (affecting .rad-ui-context-menu-trigger); remove those demo
sizes from the shared rule and instead add a story/docs-only modifier or
CSS-variable override (e.g., .rad-ui-context-menu-trigger--demo or
--rad-context-menu-demo-width/height) and update the demo/story styles to apply
that modifier/variables so inline/custom triggers keep normal sizing while the
demo uses 24rem × 13.5rem.

---

Nitpick comments:
In `@knowledge/design_system/clarity_design_system.md`:
- Around line 25-38: The markdown table in clarity_design_system.md has
malformed rows (missing trailing "|" on some rows) and lacks blank lines before
and after the table which breaks markdownlint; fix it by adding the missing
trailing pipe characters to every table row (ensure each row, e.g., the rows for
steps 8, 9, 10, 11, etc., ends with "|"), verify the header separator row is
intact, and add a blank line both immediately before the table and immediately
after the trailing "---" separator so the table renders cleanly and passes
linting.
- Around line 63-68: The "Accessibility First" section (### 5. Accessibility
First) needs explicit, testable contrast criteria instead of just “must meet
WCAG AA”: add concrete ratio targets for the referenced tokens (Text (900–1000),
Borders (400–500)) and common token pairs — e.g., require text (normal) to be
>=4.5:1 on its intended background, large text >=3:1, UI components and primary
controls >=3:1, and borders (400–500) to be >=3:1 against adjacent surfaces;
also call out minimum contrast deltas for adjacent steps (suggest >=3:1) and
state that all ratios must be measurable with standard tools (e.g., WCAG
contrast checkers) so designers can validate token usage.

In `@src/core/primitives/Collapsible/fragments/CollapsiblePrimitiveContent.tsx`:
- Line 134: The effect is re-running unnecessarily because shouldRender is
included in the dependency array; either remove shouldRender from the array or
add an early-return guard at the top of the useEffect so it bails when both open
is false and shouldRender is false. Specifically, in the effect that references
open, transitionDuration, forceMount, shouldRender (around the block that calls
setShouldRender(false) and uses scrollHeight/animation frames), remove
shouldRender from the dependency list or add: if (!open && !shouldRender)
return; to avoid extra DOM reads/RAF scheduling; keep other dependencies (open,
transitionDuration, forceMount) intact and ensure any timers/RAFs still get
cleaned up in the existing cleanup block.

In `@styles/cssTokens/base.tokens.css`:
- Around line 813-825: The dark theme gray tokens (--rad-ui-color-gray-50
through --rad-ui-color-gray-1000) use oklch() while the light tokens use hex,
causing inconsistent color formats; pick a single color format (either hex or
oklch) and convert the dark-theme token values to that format (or convert light
tokens to oklch) so all --rad-ui-color-gray-* variables use the same notation,
then update any documentation or a short inline comment explaining the chosen
convention; locate and edit the block containing [data-rad-ui-theme="dark"] and
the specific variables --rad-ui-color-gray-50 ... --rad-ui-color-gray-1000 to
apply the converted values.
- Around line 3-14: The gray color CSS custom properties inside the :root block
are misaligned; please align the declarations (--rad-ui-color-gray-100 through
--rad-ui-color-gray-1000) to match the indentation of the first token
(--rad-ui-color-gray-50) so all lines share the same indentation level and
spacing style, e.g., indent each line to the same column within the :root block
and keep one property per line.

In `@styles/themes/components/alert-dialog.scss`:
- Around line 100-124: The .rad-ui-alert-dialog-action and
.rad-ui-alert-dialog-cancel rules are duplicated in both the nested and
standalone contexts; define shared SCSS placeholders or mixins (e.g.,
%alert-dialog-action-base and %alert-dialog-cancel-base) that contain the common
properties (including &:hover blocks) and replace the duplicated blocks by
extending/including these placeholders wherever .rad-ui-alert-dialog-action and
.rad-ui-alert-dialog-cancel appear (both the nested footer variant and the
top-level selectors) so the styles are centralized and maintained in one place.
- Around line 106-109: The hover rule using color-mix(in oklab, ...) may be
ignored in older browsers; update the &:hover block in
styles/themes/components/alert-dialog.scss (the selector using &:hover for the
action button) to provide a compatible fallback before the color-mix declaration
— e.g., set a static background-color and border-color (or use an existing CSS
custom property like --rad-ui-color-gray-1000 or a derived var(--rad-ui-hover))
and then keep the color-mix rule after it so modern browsers use the advanced
mix while legacy browsers pick the fallback.

In `@styles/themes/components/minimap.scss`:
- Around line 30-38: The transition currently only lists color and box-shadow,
but the &[data-in-view="true"] rule also changes border-color and
background-color; update the transition declaration (the block containing
"transition: color 0.3s ease-in-out, box-shadow 0.3s ease-in-out;") to include
border-color and background-color so those properties animate as well when the
selector &[data-in-view="true"] toggles.

In `@styles/themes/components/radio-group.scss`:
- Around line 10-13: The rule in radio-group.scss that sets width: max-content
forces labels to their unwrapped width causing overflow and small hit areas;
locate the CSS block containing display: inline-flex; align-items: center; gap:
0.75rem; width: max-content and replace/remove width: max-content (use width:
auto or remove it) and optionally add max-width: 100% to allow wrapping and keep
responsiveness; then verify the change by testing a long radio label in a narrow
container or Storybook viewport to ensure it wraps and the hit area expands
correctly.

In `@styles/themes/components/slider.scss`:
- Around line 78-79: The two redundant rules [&data-state="dragging"] and
[&data-state="active"] in slider.scss duplicate the base selector's transform
(translateY(-50%)); either remove these duplicate selectors to clean up the CSS
or, if kept as placeholders for future state-specific styling, replace their
bodies with a brief comment noting they are intentional placeholders (e.g.,
"TODO: state-specific styles") so reviewers know they are deliberate—locate the
rules by the selectors [data-state="dragging"] and [data-state="active"] and
update accordingly.
- Around line 50-52: Replace the explicit multi-class selector
(.rad-ui-slider-thumb, .rad-ui-slider-thumb-lower, .rad-ui-slider-thumb-upper)
with a single, more maintainable selector that matches all thumb variants (e.g.,
an attribute or prefix-based selector such as [class^="rad-ui-slider-thumb"] or
a custom data-* attribute like [data-rad-ui-thumb]) so new variants inherit the
styles without editing this rule; update any markup to include the chosen
attribute if using data-* and ensure the rule still targets only slider thumbs
(adjust specificity if needed).
- Around line 161-175: The vertical thumb block duplicates the same selectors
and state rules as the horizontal one (.rad-ui-slider-thumb,
.rad-ui-slider-thumb-lower, .rad-ui-slider-thumb-upper with
&[data-state="dragging"], &[data-state="active"], and &:focus-visible); extract
the shared properties into a reusable SCSS construct (a mixin or placeholder
selector) and include it from both the horizontal and vertical orientation
blocks, leaving only orientation-specific rules (e.g., left/top/transform
differences) in each block and removing the redundant state selectors from the
vertical section.

In `@styles/themes/components/table.scss`:
- Around line 36-42: Add a background-color transition to the row hover rule to
smooth the change (e.g., transition: background-color 150ms ease) by updating
the block containing &:hover; also ensure header cells do not share the same
background variable as rows—either change the header cell background from
--rad-ui-surface-panel to a distinct token (e.g., --rad-ui-surface-header) or
pick a hover color variable different from the header so the &:hover
background-color: var(--rad-ui-surface-subtle) is visually distinct.

In `@styles/themes/components/tabs.scss`:
- Around line 38-65: The focus ring color is hardcoded as rgba(17,24,39,...) in
the &:focus-visible, &.active and &.active:focus-visible blocks which breaks
theming; replace those rgba(...) color literals with a CSS custom property (e.g.
var(--rad-ui-focus-ring-color, rgba(17,24,39,0.3)) and similar fallbacks for the
other alpha stops) so the ring adapts to theme tokens, and ensure a sensible
default is declared in your theme tokens file or a root selector so existing
visuals remain unchanged if the token is missing.

In `@styles/themes/components/toggle.scss`:
- Around line 17-24: The transition declaration currently only lists color and
box-shadow but the :hover block mutates background-color and border-color;
update the transition on the relevant selector (the rule surrounding transition
and &:hover) to include background-color and border-color (e.g., add
"background-color 150ms ..." and "border-color 150ms ...") so hover state
changes animate smoothly rather than abruptly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fb3dad09-d835-4160-8ce8-18252d8bf721

📥 Commits

Reviewing files that changed from the base of the PR and between fddc3f2 and 787bda1.

📒 Files selected for processing (108)
  • .changeset/green-ducks-rest.md
  • docs/app/docs/components/blockquote/examples/BlockQuoteColor.tsx
  • docs/app/docs/components/blockquote/examples/BlockQuoteExample.tsx
  • docs/app/docs/components/blockquote/examples/BlockQuoteSizes.tsx
  • docs/app/docs/components/button/docs/codeUsage.js
  • docs/app/docs/components/card/content.mdx
  • docs/app/docs/components/em/docs/EmExample.tsx
  • docs/app/docs/components/heading/content.mdx
  • docs/app/docs/components/separator/content.mdx
  • docs/app/docs/components/strong/content.mdx
  • docs/app/docs/components/strong/docs/codeUsage.js
  • docs/app/docs/components/text/content.mdx
  • docs/app/docs/components/tooltip/docs/examples/tooltip_example1.tsx
  • docs/app/docs/layout.tsx
  • docs/app/landingComponents/HeroSection.js
  • docs/app/landingComponents/MusicAppPlayerDemo.js
  • docs/app/landingComponents/ToolbarDemo.js
  • docs/app/landingComponents/TrafficAnalyticsDemo.js
  • docs/app/landingComponents/YourTeamDemo.js
  • docs/app/layout.js
  • docs/app/not-found.tsx
  • docs/app/og/route.tsx
  • docs/app/page.tsx
  • docs/app/playground/components/BlockquotePlayground.js
  • docs/app/playground/components/EmPlayground.js
  • docs/app/playground/components/HeadingPlayground.js
  • docs/app/playground/components/QuotePlayground.js
  • docs/app/playground/components/SeparatorPlayground.js
  • docs/app/playground/components/TextPlayground.js
  • docs/app/playground/helpers/ColorLooper.js
  • docs/app/showcase/layout.js
  • docs/app/showcase/music-app/helpers/MusicPlayer.tsx
  • docs/app/showcase/music-app/helpers/MusicSidebar.js
  • docs/app/showcase/music-app/helpers/sections/PlaylistHero.tsx
  • docs/app/sponsors/components/BecomeSponsorCTA.tsx
  • docs/app/sponsors/components/SponsorCard.tsx
  • docs/app/sponsors/components/SponsorsFooter.tsx
  • docs/app/sponsors/components/SponsorsHeader.tsx
  • docs/app/testing/full_page_scroll/page.tsx
  • docs/components/Main/Main.js
  • docs/components/Main/NavBar/helpers/NavRoot/index.tsx
  • docs/components/layout/Documentation/Documentation.js
  • docs/components/layout/Documentation/helpers/ComponentFeatures/ComponentFeatures.js
  • docs/components/layout/Documentation/helpers/ComponentHero/ComponentHero.js
  • docs/components/layout/Documentation/helpers/DocsTable.js
  • docs/components/navigation/Category.tsx
  • docs/components/navigation/NavItem.js
  • docs/icons/logos/SoundWaveSampleLogo.js
  • docs/mdx-components.tsx
  • docs/utils/seo/generateSeoMetadata.ts
  • knowledge/design_system/clarity_design_system.md
  • src/components/tools/SandboxEditor/SandboxEditor.tsx
  • src/components/ui/Accordion/fragments/AccordionRoot.tsx
  • src/components/ui/Button/stories/Button.stories.tsx
  • src/components/ui/Collapsible/tests/Collapsible.control.test.tsx
  • src/components/ui/Splitter/demo.tsx
  • src/components/ui/Tabs/stories/Tabs.stories.tsx
  • src/components/ui/VisuallyHidden/stories/VisuallyHidden.stories.tsx
  • src/core/primitives/Collapsible/fragments/CollapsiblePrimitiveContent.tsx
  • src/core/primitives/Combobox/stories/Combobox.stories.tsx
  • src/core/primitives/Menu/stories/MenuPrimitive.stories.tsx
  • src/core/primitives/Menu/tests/MenuPrimitive.test.tsx
  • src/design-systems/clarity/tokens/colors/gray.ts
  • src/examples/Colors/Colors.stories.js
  • src/examples/FloatingUI/useInteractions/UseInteractionsExample.js
  • styles/cssTokens/base.tokens.css
  • styles/themes/components/_avatar-base.scss
  • styles/themes/components/alert-dialog.scss
  • styles/themes/components/badge.scss
  • styles/themes/components/blockquote.scss
  • styles/themes/components/button.scss
  • styles/themes/components/callout.scss
  • styles/themes/components/card.scss
  • styles/themes/components/checkbox-cards.scss
  • styles/themes/components/checkbox-group.scss
  • styles/themes/components/checkbox.scss
  • styles/themes/components/code.scss
  • styles/themes/components/combobox.scss
  • styles/themes/components/context-menu.scss
  • styles/themes/components/dialog.scss
  • styles/themes/components/dropdown-menu.scss
  • styles/themes/components/fix_tokens.cjs
  • styles/themes/components/heading.scss
  • styles/themes/components/kbd.scss
  • styles/themes/components/menubar.scss
  • styles/themes/components/minimap.scss
  • styles/themes/components/navigation-menu.scss
  • styles/themes/components/number-field.scss
  • styles/themes/components/progress.scss
  • styles/themes/components/radio-group.scss
  • styles/themes/components/radio.scss
  • styles/themes/components/radiocards.scss
  • styles/themes/components/select.scss
  • styles/themes/components/separator.scss
  • styles/themes/components/slider.scss
  • styles/themes/components/spinner.scss
  • styles/themes/components/splitter.scss
  • styles/themes/components/steps.scss
  • styles/themes/components/switch.scss
  • styles/themes/components/tab-nav.scss
  • styles/themes/components/table.scss
  • styles/themes/components/tabs.scss
  • styles/themes/components/textarea.scss
  • styles/themes/components/toggle-group.scss
  • styles/themes/components/toggle.scss
  • styles/themes/components/toolbar.scss
  • styles/themes/components/tree.scss
  • styles/themes/default.scss
✅ Files skipped from review due to trivial changes (66)
  • .changeset/green-ducks-rest.md
  • src/core/primitives/Combobox/stories/Combobox.stories.tsx
  • docs/app/landingComponents/TrafficAnalyticsDemo.js
  • src/examples/Colors/Colors.stories.js
  • src/core/primitives/Menu/stories/MenuPrimitive.stories.tsx
  • docs/app/docs/components/heading/content.mdx
  • docs/app/docs/components/card/content.mdx
  • docs/app/showcase/layout.js
  • docs/app/og/route.tsx
  • docs/app/sponsors/components/SponsorsFooter.tsx
  • docs/components/layout/Documentation/helpers/ComponentFeatures/ComponentFeatures.js
  • src/components/ui/VisuallyHidden/stories/VisuallyHidden.stories.tsx
  • src/examples/FloatingUI/useInteractions/UseInteractionsExample.js
  • docs/app/testing/full_page_scroll/page.tsx
  • docs/app/playground/components/TextPlayground.js
  • docs/app/docs/components/separator/content.mdx
  • docs/app/playground/components/QuotePlayground.js
  • docs/components/layout/Documentation/helpers/ComponentHero/ComponentHero.js
  • docs/icons/logos/SoundWaveSampleLogo.js
  • docs/app/sponsors/components/SponsorsHeader.tsx
  • docs/app/docs/components/strong/content.mdx
  • docs/app/docs/components/blockquote/examples/BlockQuoteColor.tsx
  • docs/app/sponsors/components/BecomeSponsorCTA.tsx
  • docs/components/Main/Main.js
  • src/components/ui/Splitter/demo.tsx
  • docs/components/navigation/Category.tsx
  • docs/app/sponsors/components/SponsorCard.tsx
  • docs/utils/seo/generateSeoMetadata.ts
  • docs/app/playground/components/HeadingPlayground.js
  • src/components/ui/Button/stories/Button.stories.tsx
  • docs/app/docs/components/blockquote/examples/BlockQuoteSizes.tsx
  • docs/app/docs/components/tooltip/docs/examples/tooltip_example1.tsx
  • docs/app/not-found.tsx
  • docs/app/docs/components/text/content.mdx
  • docs/app/landingComponents/ToolbarDemo.js
  • src/core/primitives/Menu/tests/MenuPrimitive.test.tsx
  • docs/app/showcase/music-app/helpers/sections/PlaylistHero.tsx
  • src/components/ui/Tabs/stories/Tabs.stories.tsx
  • docs/app/docs/layout.tsx
  • docs/app/playground/helpers/ColorLooper.js
  • docs/app/docs/components/strong/docs/codeUsage.js
  • docs/app/showcase/music-app/helpers/MusicSidebar.js
  • docs/app/docs/components/em/docs/EmExample.tsx
  • docs/app/playground/components/EmPlayground.js
  • docs/components/navigation/NavItem.js
  • docs/app/landingComponents/YourTeamDemo.js
  • docs/components/layout/Documentation/helpers/DocsTable.js
  • docs/app/showcase/music-app/helpers/MusicPlayer.tsx
  • docs/app/docs/components/blockquote/examples/BlockQuoteExample.tsx
  • styles/themes/components/spinner.scss
  • docs/app/page.tsx
  • docs/mdx-components.tsx
  • docs/components/Main/NavBar/helpers/NavRoot/index.tsx
  • docs/app/landingComponents/HeroSection.js
  • styles/themes/components/progress.scss
  • styles/themes/components/heading.scss
  • styles/themes/components/tab-nav.scss
  • docs/app/docs/components/button/docs/codeUsage.js
  • docs/app/layout.js
  • styles/themes/components/steps.scss
  • styles/themes/components/tree.scss
  • docs/app/playground/components/SeparatorPlayground.js
  • docs/components/layout/Documentation/Documentation.js
  • docs/app/playground/components/BlockquotePlayground.js
  • styles/themes/components/select.scss
  • docs/app/landingComponents/MusicAppPlayerDemo.js
🚧 Files skipped from review as they are similar to previous changes (22)
  • styles/themes/components/kbd.scss
  • styles/themes/components/toolbar.scss
  • styles/themes/components/blockquote.scss
  • styles/themes/components/code.scss
  • styles/themes/components/checkbox-group.scss
  • styles/themes/components/_avatar-base.scss
  • src/components/tools/SandboxEditor/SandboxEditor.tsx
  • styles/themes/components/radio.scss
  • styles/themes/components/callout.scss
  • styles/themes/components/dialog.scss
  • styles/themes/components/separator.scss
  • styles/themes/components/checkbox-cards.scss
  • styles/themes/components/checkbox.scss
  • styles/themes/components/combobox.scss
  • styles/themes/components/splitter.scss
  • styles/themes/components/switch.scss
  • styles/themes/components/badge.scss
  • styles/themes/components/navigation-menu.scss
  • styles/themes/components/button.scss
  • styles/themes/components/dropdown-menu.scss
  • styles/themes/components/number-field.scss
  • styles/themes/default.scss

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
styles/themes/components/alert-dialog.scss (1)

27-44: ⚠️ Potential issue | 🟠 Major

Cap the dialog height and allow internal scrolling.

This panel is fixed and vertically centered, but it never gets a max-height or overflow rule. Longer content will clip off-screen on short viewports, and the footer can become unreachable.

🔧 Suggested fix
 .rad-ui-alert-dialog-content {
   position: fixed;
   left: 50%;
   top: 50%;
   z-index: 60;
   width: 100%;
   max-width: 36rem;
   transform: translate(-50%, -50%);
+  max-height: calc(100vh - 2rem);
+  overflow-y: auto;
   padding: 1.2rem 1.2rem 0;
   border-radius: 1.25rem;
   background-color: var(--rad-ui-surface-panel);
   color: var(--rad-ui-text-primary);

Also applies to: 125-144

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/alert-dialog.scss` around lines 27 - 44, The dialog
panel (.rad-ui-alert-dialog-content) lacks a max-height and overflow, causing
content/footer to be clipped on short viewports; update
.rad-ui-alert-dialog-content to set a sensible max-height (e.g., relative to
viewport like max-height: 80vh) and enable internal scrolling via overflow: auto
(or overflow-y: auto) so long content can scroll while the panel remains
centered; apply the same change to the other dialog content selector defined
later (the second dialog content block around lines 125-144) to keep consistent
behavior across dialogs.
♻️ Duplicate comments (5)
styles/themes/components/textarea.scss (2)

18-21: ⚠️ Potential issue | 🟡 Minor

Let placeholder typography follow the active size variant.

Line 20 hard-codes font-size: 1rem, so small/large variants still show placeholder/content size mismatch. Use inheritance instead.

Suggested fix
     &::placeholder {
       color: var(--rad-ui-text-muted);
-      font-size: 1rem;
+      font-size: inherit;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/textarea.scss` around lines 18 - 21, The placeholder
rule in textarea.scss currently hard-codes font-size: 1rem causing mismatch with
size variants; update the &::placeholder block (for the textarea component's
placeholder styling) to stop forcing 1rem and instead inherit the font size from
the host (e.g., use font-size: inherit or remove the font-size line) so the
placeholder follows the active small/large size variant while keeping the color
var(--rad-ui-text-muted).

114-140: ⚠️ Potential issue | 🟠 Major

Variant base rules are still overriding shared focus/disabled/read-only states.

Because these selectors come later with matching specificity, variant declarations can cancel shared state styling (focus ring/border, muted disabled/read-only colors). Scope variant base rules to non-state cases (or move shared state rules after variants).

Suggested fix (state-safe selector scoping)
-  &[data-textarea-variant="soft"] textarea {
+  &[data-textarea-variant="soft"] textarea:not(:disabled):not(:read-only):not(:focus) {
     border-color: transparent;
     background-color: var(--rad-ui-surface-subtle);
     color: var(--rad-ui-text-primary);
     box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--rad-ui-border-soft) 55%, transparent);
   }

-  &[data-textarea-variant="outline"] textarea {
+  &[data-textarea-variant="outline"] textarea:not(:disabled):not(:read-only):not(:focus) {
     background-color: transparent;
     border: 1px solid var(--rad-ui-border-default);
     box-shadow: none;
   }

-  &[data-textarea-variant="solid"] textarea {
+  &[data-textarea-variant="solid"] textarea:not(:disabled):not(:read-only):not(:focus) {
     background-color: var(--rad-ui-surface-panel);
     border: 1px solid var(--rad-ui-border-soft);
   }

-  &[data-textarea-variant="ghost"] textarea {
+  &[data-textarea-variant="ghost"] textarea:not(:disabled):not(:read-only):not(:focus) {
     background-color: transparent;
     border: 1px solid transparent;
     box-shadow: none;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/textarea.scss` around lines 114 - 140, The variant
selectors (e.g. &[data-textarea-variant="soft"] textarea,
&[data-textarea-variant="outline"] textarea, &[data-textarea-variant="solid"]
textarea, &[data-textarea-variant="ghost"] textarea) are declared after the
shared focus/disabled/read-only rules and thus override those state styles; fix
by scoping these variant base rules to only apply when no state pseudo-classes
or state attributes are present (e.g. restrict to non-:focus,
non-[data-disabled], non[readonly] contexts) or alternatively move the shared
state rules so they appear after the variant blocks so focus/disabled/read-only
styling wins; update the selectors for the variant blocks (and the ghost &:focus
rule) accordingly so they no longer cancel the shared state styles.
styles/themes/components/toggle-group.scss (1)

10-12: ⚠️ Potential issue | 🟠 Major

Make the vertical selector match the default render path.

When rovingFocus is enabled, data-orientation lives on RovingFocusGroup.Group, not on .rad-ui-toggle-group, so this selector still misses the common case and vertical groups stay horizontal.

Proposed fix
-    &[data-orientation="vertical"] {
+    &[data-orientation="vertical"],
+    [data-orientation="vertical"] & {
         flex-direction: column;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/toggle-group.scss` around lines 10 - 12, The
vertical selector only targets .rad-ui-toggle-group[data-orientation="vertical"]
via the current nested &[data-orientation="vertical"], but when rovingFocus is
enabled the attribute is placed on RovingFocusGroup.Group instead; update the
stylesheet so the same rule (flex-direction: column) also applies to
RovingFocusGroup.Group[data-orientation="vertical"]—i.e., add an additional
selector for RovingFocusGroup.Group[data-orientation="vertical"] alongside the
existing &[data-orientation="vertical"] to ensure vertical groups render
correctly in both cases.
styles/themes/components/context-menu.scss (1)

14-16: ⚠️ Potential issue | 🟠 Major

Keep the demo-sized canvas out of the shared trigger.

These fixed values make every .rad-ui-context-menu-trigger render like the story/demo surface. Normal inline or custom triggers should size from their content or an opt-in demo modifier instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/context-menu.scss` around lines 14 - 16, The shared
.rad-ui-context-menu-trigger currently hardcodes padding, width and min-height
(padding: 6rem 1.5rem; width: 24rem; min-height: 13.5rem;) which forces every
trigger to render like the demo; remove those fixed properties from the
.rad-ui-context-menu-trigger rule in styles/themes/components/context-menu.scss
and instead place them behind an opt-in demo modifier class (e.g.
.rad-ui-context-menu-trigger--demo or a story wrapper) so normal inline/custom
triggers size from their content while the demo surface retains the fixed demo
dimensions.
styles/themes/components/alert-dialog.scss (1)

92-96: ⚠️ Potential issue | 🟡 Minor

Make the 640px breakpoint exclusive.

At exactly 640px, both media queries match, so the later mobile block overrides part of the desktop layout. Pick one side to be exclusive.

🔧 Suggested fix
-  `@media` (width <= 640px) {
+  `@media` (width < 640px) {

Also applies to: 125-144

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/alert-dialog.scss` around lines 92 - 96, The
breakpoint at 640px is inclusive on both sides causing overlapping rules; make
the desktop media query (`@media` (width >= 640px)) or the mobile counterpart
exclusive so they don't both match at exactly 640px—for example change one side
to use a strict upper bound (e.g., max-width just below 640px) or change the
desktop to min-width:640px and the mobile to max-width:639.98px; update the
`@media` block(s) around the alert-dialog styles (the `@media` (width >= 640px)
block and its mobile counterpart) accordingly so only one applies at 640px.
🧹 Nitpick comments (7)
styles/themes/components/radiocards.scss (1)

89-94: Minor: pointer-events: none may block tooltip interactions.

While the disabled styling is comprehensive, pointer-events: none prevents any mouse interaction, including hover tooltips that might explain why the item is disabled. If tooltips on disabled items are needed, consider removing this line or wrapping the item in a container that can receive pointer events.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/radiocards.scss` around lines 89 - 94, The disabled
rule in radiocards.scss (selectors &[data-disabled] and &:disabled) uses
pointer-events: none which blocks hover/tooltips; to fix, remove the
pointer-events: none declaration from that rule (or alternatively move
pointer-events: none to a specific interactive child selector inside the card so
the outer element still receives hover events), keeping opacity: 0.5 and cursor:
not-allowed so the visual and accessibility cues remain while allowing tooltip
interactions.
styles/themes/components/splitter.scss (2)

147-153: Remove empty reduced-motion media query.

The @media (prefers-reduced-motion: reduce) block contains empty rule sets that have no effect. Since the base styles no longer include transitions, this media query is dead code.

🧹 Suggested removal
    }

-  `@media` (prefers-reduced-motion: reduce) {
-    .rad-ui-splitter-panel {
-    }
-    
-    .rad-ui-splitter-handle {
-    }
-  }
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/splitter.scss` around lines 147 - 153, Remove the
dead `@media` (prefers-reduced-motion: reduce) block that contains empty rule sets
for .rad-ui-splitter-panel and .rad-ui-splitter-handle; delete the entire media
query since base styles no longer use transitions and these selectors have no
rules inside, leaving no behavioral change.

93-96: Remove dead selector—handle is not a child of panel.

The .rad-ui-splitter-panel & selector targets the handle when it's nested inside a panel, but structurally the handle is a sibling to panels, not a child. This selector will never match. Additionally, background: transparent is already set on line 33, making this redundant even if it could match.

🧹 Suggested removal
      &.active::after,
      &:active::after {
        background: var(--rad-ui-border-strong);
      }
-
-      // expand hit target without changing visuals
-      .rad-ui-splitter-panel & {
-        background: transparent;
-      }
    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/splitter.scss` around lines 93 - 96, Remove the dead
selector `.rad-ui-splitter-panel & { background: transparent; }` from the
splitter SCSS: this rule never matches because the handle is a sibling of
`.rad-ui-splitter-panel` (not a child) and the same `background: transparent` is
already declared earlier (line with the handle's base rule). Delete the entire
selector block referencing `.rad-ui-splitter-panel &` to avoid redundancy and
dead CSS.
styles/themes/components/slider.scss (2)

74-75: Redundant transform rules provide no visual feedback.

The data-state="dragging" and data-state="active" transforms are identical to the base transform (translateY(-50%)). If these states should provide visual feedback (e.g., slight scale), consider adding distinct transforms. Otherwise, these rules can be removed.

💡 Option: Add visual feedback or remove redundant rules
-    &[data-state="dragging"] { transform: translateY(-50%); }
-    &[data-state="active"] { transform: translateY(-50%); }
+    &[data-state="dragging"] { transform: translateY(-50%) scale(1.1); }
+    &[data-state="active"] { transform: translateY(-50%) scale(1.05); }

Or simply remove if no feedback is intended:

-    &[data-state="dragging"] { transform: translateY(-50%); }
-    &[data-state="active"] { transform: translateY(-50%); }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/slider.scss` around lines 74 - 75, The two rules for
&[data-state="dragging"] and &[data-state="active"] duplicate the base transform
translateY(-50%) and thus provide no feedback; either remove these redundant
selectors from slider.scss or change them to a distinct transform (for example
add a subtle scale/translate difference) so the .slider thumb shows visual
feedback during active/dragging states; update the selectors
&[data-state="dragging"] and &[data-state="active"] accordingly.

162-163: Same redundant transform issue for vertical orientation.

As with the horizontal thumbs, these state transforms are identical to the base and provide no visual feedback.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/slider.scss` around lines 162 - 163, The state rules
&[data-state="dragging"] and &[data-state="active"] in slider.scss are redundant
for the vertical thumb because they repeat the base transform; remove these
duplicate rules or, if you intended vertical positioning feedback, change them
to use translateY(-50%) under the vertical orientation selector (e.g.
[data-orientation="vertical"] &[data-state="dragging"] and
[data-orientation="vertical"] &[data-state="active"]) so only vertical thumbs
get the correct transform.
styles/themes/components/badge.scss (1)

22-25: Consider consolidating the shared neutral fill rules for secondary and soft.

Lines 23-25 and Lines 51-53 are identical now. Pulling them into a shared selector would make the next token tweak less likely to drift between the two variants.

Also applies to: 50-53

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/badge.scss` around lines 22 - 25, The SCSS contains
duplicated neutral-fill rules for the badge variants
[data-badge-variant="secondary"] and [data-badge-variant="soft"]; consolidate
them by creating a combined selector (e.g., &[data-badge-variant="secondary"],
&[data-badge-variant="soft"]) and move the shared properties (background-color:
var(--rad-ui-surface-subtle); border-color: transparent; color:
var(--rad-ui-text-primary);) into that combined rule so both variants reuse the
same declarations, leaving any unique overrides in their individual selectors.
styles/themes/components/tabs.scss (1)

34-60: Use the theme ring tokens here instead of hard-coded grays.

These focus states are the outlier in this PR: they won't track accent color or theme changes, so tabs end up with a different focus treatment than the rest of the system.

Proposed refactor
             &:focus-visible{
                 z-index: 2;
                 color: var(--rad-ui-text-primary);
                 background-color: var(--rad-ui-surface-panel);
                 outline: none;
                 box-shadow:
-                    0 0 0 3px rgba(17, 24, 39, 0.3),
-                    0 0 0 4px rgba(17, 24, 39, 0.1),
+                    var(--rad-ui-ring-shadow),
                     0 1px 2px rgba(0, 0, 0, 0.05),
                     0 2px 6px rgba(0, 0, 0, 0.04);
             }
@@
             &.active:focus-visible{
                 box-shadow:
-                    0 0 0 3px rgba(17, 24, 39, 0.3),
-                    0 0 0 4px rgba(17, 24, 39, 0.1),
+                    var(--rad-ui-ring-shadow),
                     0 1px 2px rgba(0, 0, 0, 0.05),
                     0 2px 6px rgba(0, 0, 0, 0.04);
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/tabs.scss` around lines 34 - 60, The focus
box-shadow in styles/themes/components/tabs.scss uses hard-coded gray RGBA
values in the &:focus-visible and &.active:focus-visible selectors; replace
those literal colors with the project's theme ring tokens (the ring/accent token
variables used elsewhere for focus rings) so the tab focus treatment tracks
accent color and theme changes, keeping the existing box-shadow structure but
substituting the rgba(17,24,39,...) and rgba(17,24,39,0.1) values with the
corresponding token variables.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@styles/themes/components/checkbox-cards.scss`:
- Around line 29-34: The checked-state CSS currently hardcodes semantic tokens
and ignores per-instance accent color from the public color prop (composed into
data-rad-ui-accent-color in CheckboxCardsRoot.tsx); update the checked-state
rules in checkbox-cards.scss (and the other matching blocks at lines 95-113) to
use the accent override variable (e.g., var(--rad-ui-accent-color) with a
fallback to var(--rad-ui-text-primary)) for border-color, inset border and any
relevant background or shadow so per-instance color customization works, or
alternatively remove/deprecate the color prop in CheckboxCardsRoot.tsx if you
intend to drop instance-level customization.
- Around line 29-39: The current SCSS uses [data-state="checked"/"unchecked"]
but the checkbox primitive exposes state via aria-checked, so update the
selectors in styles/themes/components/checkbox-cards.scss (including the block
around .rad-ui-checkbox-cards-content and the repeated block at lines ~95-113)
to target aria-checked values instead (e.g., use &[aria-checked="true"] and
&[aria-checked="false"] or the equivalent on the trigger/card element referenced
by CheckboxGroupPrimitiveTrigger.tsx) so the checked/unchecked visual styles
actually apply when the component toggles.

In `@styles/themes/components/slider.scss`:
- Around line 106-116: Update the .rad-ui-slider-mark-label rule to use an
accessible font size: replace the hardcoded 10px with a token or a rem-based
value (suggest 0.75rem/12px) so labels are more readable across devices; modify
the font-size declaration in .rad-ui-slider-mark-label to reference an existing
design token (e.g., --rad-ui-font-size-sm) or set font-size: 0.75rem and keep
the rest of the rule unchanged.
- Around line 178-185: The `.rad-ui-slider-mark-label` positioning properties
are ineffective because the label is in normal document flow; change its
positioning to be absolute (and ensure the mark container has `position:
relative`) so `right`, `top`, and `transform` take effect, and remove the
redundant duplicate rule for `.rad-ui-slider-mark-line` under
`&.rad-ui-slider-mark-custom` since it repeats the same width/height as the base
`.rad-ui-slider-mark-line`; update the styles around
`.rad-ui-slider-mark-label`, `.rad-ui-slider-mark-line`, and
`&.rad-ui-slider-mark-custom .rad-ui-slider-mark-line` accordingly.
- Around line 50-65: The slider thumb is misaligned because .rad-ui-slider-thumb
in styles sets width: 1.25rem (20px) but SliderThumb.tsx positions thumbs using
calc(${percent}% - 8px) (half of 16px), causing a 2px offset; fix by either
changing the JS offset in SliderThumb.tsx from 8px to 10px or change
.rad-ui-slider-thumb width to 1rem (16px) so the existing 8px offset is correct.
Also remove or update the duplicate transform: translateY(-50%) rules under
[data-state="dragging"] and [data-state="active"] in slider.scss—either delete
them if no additional feedback is needed or add distinct active/dragging styles
(e.g., scale, box-shadow, or color change) to provide visible interactivity
feedback.

In `@styles/themes/components/splitter.scss`:
- Around line 15-29: .rad-ui-splitter-panel has overflow: hidden in SCSS but
SplitterPanel.tsx sets overflow: 'auto' inline, creating a fragile dependency;
either remove the overflow property from .rad-ui-splitter-panel so the component
(SplitterPanel.tsx) is the single source of truth for scrolling, or add a clear
comment in the SCSS above .rad-ui-splitter-panel stating that overflow is
intentionally omitted/overridden by SplitterPanel.tsx and must remain in the
component; update whichever file you choose (.scss or SplitterPanel.tsx) so the
intent is explicit and add a brief note referencing SplitterPanel.tsx if you
keep the SCSS entry.

In `@styles/themes/components/tabs.scss`:
- Around line 7-18: The .rad-ui-tabs-list CSS never changes flex-direction based
on TabList's aria-orientation, so vertical tabs still render horizontally;
update the styling for .rad-ui-tabs-list to switch flex-direction to column when
the container/tablist indicates vertical orientation (i.e., when TabList sets
aria-orientation="vertical") so the layout matches the semantic orientation and
stacked/column layout is applied for vertical tabs.

In `@styles/themes/components/toggle-group.scss`:
- Around line 16-17: The fixed width/height on the ToggleGroup item forces
icon-only squares and will clip labeled children; update the CSS rule targeting
the toggle item (e.g., the ToggleGroup.Item selector) to remove the hard
width/height and instead use min-width/min-height and appropriate padding (or
rely on line-height/inline-flex sizing) so labeled content can expand naturally;
keep any square styling for purely-icon variants by applying a specific
modifier/class for icon-only buttons rather than the base ToggleGroup.Item.
- Around line 29-39: The pressed state selector &[aria-pressed="true"] overrides
the :focus-visible box-shadow, removing the focus ring; update the CSS so the
focus-visible ring is preserved for pressed items by adding a more specific rule
(e.g. &:focus-visible[aria-pressed="true"] or splitting the selectors) that
merges the focus ring with the pressed state's shadows, or by composing the
box-shadow in the &[aria-pressed="true"] rule to include
var(--rad-ui-ring-shadow-inset) in addition to the pressed shadows; modify the
rules around :focus-visible and &[aria-pressed="true"] to ensure the focus ring
is retained during roving-focus navigation.

---

Outside diff comments:
In `@styles/themes/components/alert-dialog.scss`:
- Around line 27-44: The dialog panel (.rad-ui-alert-dialog-content) lacks a
max-height and overflow, causing content/footer to be clipped on short
viewports; update .rad-ui-alert-dialog-content to set a sensible max-height
(e.g., relative to viewport like max-height: 80vh) and enable internal scrolling
via overflow: auto (or overflow-y: auto) so long content can scroll while the
panel remains centered; apply the same change to the other dialog content
selector defined later (the second dialog content block around lines 125-144) to
keep consistent behavior across dialogs.

---

Duplicate comments:
In `@styles/themes/components/alert-dialog.scss`:
- Around line 92-96: The breakpoint at 640px is inclusive on both sides causing
overlapping rules; make the desktop media query (`@media` (width >= 640px)) or the
mobile counterpart exclusive so they don't both match at exactly 640px—for
example change one side to use a strict upper bound (e.g., max-width just below
640px) or change the desktop to min-width:640px and the mobile to
max-width:639.98px; update the `@media` block(s) around the alert-dialog styles
(the `@media` (width >= 640px) block and its mobile counterpart) accordingly so
only one applies at 640px.

In `@styles/themes/components/context-menu.scss`:
- Around line 14-16: The shared .rad-ui-context-menu-trigger currently hardcodes
padding, width and min-height (padding: 6rem 1.5rem; width: 24rem; min-height:
13.5rem;) which forces every trigger to render like the demo; remove those fixed
properties from the .rad-ui-context-menu-trigger rule in
styles/themes/components/context-menu.scss and instead place them behind an
opt-in demo modifier class (e.g. .rad-ui-context-menu-trigger--demo or a story
wrapper) so normal inline/custom triggers size from their content while the demo
surface retains the fixed demo dimensions.

In `@styles/themes/components/textarea.scss`:
- Around line 18-21: The placeholder rule in textarea.scss currently hard-codes
font-size: 1rem causing mismatch with size variants; update the &::placeholder
block (for the textarea component's placeholder styling) to stop forcing 1rem
and instead inherit the font size from the host (e.g., use font-size: inherit or
remove the font-size line) so the placeholder follows the active small/large
size variant while keeping the color var(--rad-ui-text-muted).
- Around line 114-140: The variant selectors (e.g.
&[data-textarea-variant="soft"] textarea, &[data-textarea-variant="outline"]
textarea, &[data-textarea-variant="solid"] textarea,
&[data-textarea-variant="ghost"] textarea) are declared after the shared
focus/disabled/read-only rules and thus override those state styles; fix by
scoping these variant base rules to only apply when no state pseudo-classes or
state attributes are present (e.g. restrict to non-:focus, non-[data-disabled],
non[readonly] contexts) or alternatively move the shared state rules so they
appear after the variant blocks so focus/disabled/read-only styling wins; update
the selectors for the variant blocks (and the ghost &:focus rule) accordingly so
they no longer cancel the shared state styles.

In `@styles/themes/components/toggle-group.scss`:
- Around line 10-12: The vertical selector only targets
.rad-ui-toggle-group[data-orientation="vertical"] via the current nested
&[data-orientation="vertical"], but when rovingFocus is enabled the attribute is
placed on RovingFocusGroup.Group instead; update the stylesheet so the same rule
(flex-direction: column) also applies to
RovingFocusGroup.Group[data-orientation="vertical"]—i.e., add an additional
selector for RovingFocusGroup.Group[data-orientation="vertical"] alongside the
existing &[data-orientation="vertical"] to ensure vertical groups render
correctly in both cases.

---

Nitpick comments:
In `@styles/themes/components/badge.scss`:
- Around line 22-25: The SCSS contains duplicated neutral-fill rules for the
badge variants [data-badge-variant="secondary"] and [data-badge-variant="soft"];
consolidate them by creating a combined selector (e.g.,
&[data-badge-variant="secondary"], &[data-badge-variant="soft"]) and move the
shared properties (background-color: var(--rad-ui-surface-subtle); border-color:
transparent; color: var(--rad-ui-text-primary);) into that combined rule so both
variants reuse the same declarations, leaving any unique overrides in their
individual selectors.

In `@styles/themes/components/radiocards.scss`:
- Around line 89-94: The disabled rule in radiocards.scss (selectors
&[data-disabled] and &:disabled) uses pointer-events: none which blocks
hover/tooltips; to fix, remove the pointer-events: none declaration from that
rule (or alternatively move pointer-events: none to a specific interactive child
selector inside the card so the outer element still receives hover events),
keeping opacity: 0.5 and cursor: not-allowed so the visual and accessibility
cues remain while allowing tooltip interactions.

In `@styles/themes/components/slider.scss`:
- Around line 74-75: The two rules for &[data-state="dragging"] and
&[data-state="active"] duplicate the base transform translateY(-50%) and thus
provide no feedback; either remove these redundant selectors from slider.scss or
change them to a distinct transform (for example add a subtle scale/translate
difference) so the .slider thumb shows visual feedback during active/dragging
states; update the selectors &[data-state="dragging"] and &[data-state="active"]
accordingly.
- Around line 162-163: The state rules &[data-state="dragging"] and
&[data-state="active"] in slider.scss are redundant for the vertical thumb
because they repeat the base transform; remove these duplicate rules or, if you
intended vertical positioning feedback, change them to use translateY(-50%)
under the vertical orientation selector (e.g. [data-orientation="vertical"]
&[data-state="dragging"] and [data-orientation="vertical"]
&[data-state="active"]) so only vertical thumbs get the correct transform.

In `@styles/themes/components/splitter.scss`:
- Around line 147-153: Remove the dead `@media` (prefers-reduced-motion: reduce)
block that contains empty rule sets for .rad-ui-splitter-panel and
.rad-ui-splitter-handle; delete the entire media query since base styles no
longer use transitions and these selectors have no rules inside, leaving no
behavioral change.
- Around line 93-96: Remove the dead selector `.rad-ui-splitter-panel & {
background: transparent; }` from the splitter SCSS: this rule never matches
because the handle is a sibling of `.rad-ui-splitter-panel` (not a child) and
the same `background: transparent` is already declared earlier (line with the
handle's base rule). Delete the entire selector block referencing
`.rad-ui-splitter-panel &` to avoid redundancy and dead CSS.

In `@styles/themes/components/tabs.scss`:
- Around line 34-60: The focus box-shadow in styles/themes/components/tabs.scss
uses hard-coded gray RGBA values in the &:focus-visible and
&.active:focus-visible selectors; replace those literal colors with the
project's theme ring tokens (the ring/accent token variables used elsewhere for
focus rings) so the tab focus treatment tracks accent color and theme changes,
keeping the existing box-shadow structure but substituting the
rgba(17,24,39,...) and rgba(17,24,39,0.1) values with the corresponding token
variables.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dfcd1cc3-2e7b-414a-994a-a4167bcaf939

📥 Commits

Reviewing files that changed from the base of the PR and between 787bda1 and ed375a2.

📒 Files selected for processing (36)
  • docs/next.config.js
  • styles/themes/components/accordion.scss
  • styles/themes/components/alert-dialog.scss
  • styles/themes/components/badge.scss
  • styles/themes/components/button.scss
  • styles/themes/components/checkbox-cards.scss
  • styles/themes/components/checkbox-group.scss
  • styles/themes/components/checkbox.scss
  • styles/themes/components/collapsible.scss
  • styles/themes/components/combobox.scss
  • styles/themes/components/context-menu.scss
  • styles/themes/components/dialog.scss
  • styles/themes/components/dropdown-menu.scss
  • styles/themes/components/kbd.scss
  • styles/themes/components/link.scss
  • styles/themes/components/menubar.scss
  • styles/themes/components/minimap.scss
  • styles/themes/components/navigation-menu.scss
  • styles/themes/components/number-field.scss
  • styles/themes/components/progress.scss
  • styles/themes/components/radio-group.scss
  • styles/themes/components/radio.scss
  • styles/themes/components/radiocards.scss
  • styles/themes/components/scroll-area.scss
  • styles/themes/components/select.scss
  • styles/themes/components/slider.scss
  • styles/themes/components/splitter.scss
  • styles/themes/components/steps.scss
  • styles/themes/components/switch.scss
  • styles/themes/components/tab-nav.scss
  • styles/themes/components/tabs.scss
  • styles/themes/components/textarea.scss
  • styles/themes/components/toggle-group.scss
  • styles/themes/components/toggle.scss
  • styles/themes/components/toolbar.scss
  • styles/themes/components/tree.scss
✅ Files skipped from review due to trivial changes (2)
  • styles/themes/components/combobox.scss
  • styles/themes/components/menubar.scss
🚧 Files skipped from review as they are similar to previous changes (19)
  • styles/themes/components/scroll-area.scss
  • styles/themes/components/kbd.scss
  • styles/themes/components/link.scss
  • styles/themes/components/tree.scss
  • styles/themes/components/minimap.scss
  • styles/themes/components/progress.scss
  • styles/themes/components/tab-nav.scss
  • styles/themes/components/steps.scss
  • styles/themes/components/switch.scss
  • styles/themes/components/radio-group.scss
  • styles/themes/components/dialog.scss
  • styles/themes/components/toggle.scss
  • styles/themes/components/collapsible.scss
  • styles/themes/components/toolbar.scss
  • styles/themes/components/accordion.scss
  • styles/themes/components/navigation-menu.scss
  • styles/themes/components/number-field.scss
  • styles/themes/components/button.scss
  • styles/themes/components/radio.scss

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/ui/Tree/fragments/TreeItem.tsx (1)

141-141: ⚠️ Potential issue | 🟡 Minor

Using label as React key may cause issues with duplicate labels.

If two sibling items share the same label, React will produce duplicate key warnings and may incorrectly reuse/unmount components. Consider using a unique identifier from the item data.

-                            key={subItem.label}
+                            key={subItem.id ?? subItem.label}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/Tree/fragments/TreeItem.tsx` at line 141, The current JSX
uses key={subItem.label} which can create duplicate React keys; update the key
for the mapped child in TreeItem (where subItem is rendered) to use a unique
identifier from the data (e.g., subItem.id) or a stable composite (e.g.,
`${subItem.id}-${subItem.label}` or include the index as a fallback) instead of
subItem.label so sibling items always have unique keys.
♻️ Duplicate comments (2)
styles/themes/components/tabs.scss (1)

7-17: ⚠️ Potential issue | 🟠 Major

Vertical orientation is still only semantic here.

TabList already exposes aria-orientation, but .rad-ui-tabs-list never switches direction, so vertical tabs still render as a horizontal strip.

Proposed fix
     .rad-ui-tabs-list{
         display: inline-flex;
         align-items: center;
         gap: 0.16rem;
         width: fit-content;
         padding: 0.14rem;
         border-radius: 0.95rem;
         background-color: var(--rad-ui-surface-muted);
         box-shadow:
             inset 0 0 0 1px rgba(0, 0, 0, 0.06),
             0 2px 8px rgba(0, 0, 0, 0.025);
+
+        &[aria-orientation="vertical"] {
+            flex-direction: column;
+            align-items: stretch;
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/tabs.scss` around lines 7 - 17, The
.rad-ui-tabs-list class currently always lays out tabs horizontally; update the
stylesheet so it responds to TabList's aria-orientation by adding a rule
targeting .rad-ui-tabs-list[aria-orientation="vertical"] to switch layout to a
vertical column (set flex-direction: column and adjust alignment/gap/width as
needed) so vertical tabs render stacked instead of in a horizontal strip; ensure
the rule references the existing .rad-ui-tabs-list selector so it overrides the
default display properties.
styles/themes/components/toggle-group.scss (1)

10-12: ⚠️ Potential issue | 🟠 Major

Vertical groups can still render horizontally.

This selector only matches when data-orientation is on .rad-ui-toggle-group itself. If the primitive still places that attribute on the roving-focus wrapper, the vertical variant never flips to a column layout.

💡 Proposed fix
-    &[data-orientation="vertical"] {
+    &[data-orientation="vertical"],
+    [data-orientation="vertical"] & {
         flex-direction: column;
     }

Run this to confirm where data-orientation is applied at runtime. If it lands on a wrapper rather than the .rad-ui-toggle-group element, the current selector is still too narrow.

#!/bin/bash
rg -n -C3 'data-orientation|orientation' src --iglob '*ToggleGroup*'
rg -n -C3 'RovingFocusGroup' src --iglob '*ToggleGroup*'
rg -n -C3 'rad-ui-toggle-group' src --iglob '*ToggleGroup*'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/toggle-group.scss` around lines 10 - 12, The CSS
only flips layout when data-orientation="vertical" is on .rad-ui-toggle-group
itself, so when the primitive places data-orientation on the roving-focus
wrapper (RovingFocusGroup) the vertical layout never applies; update the
selector in toggle-group.scss to also match the case where the attribute lives
on the wrapper/ancestor (e.g. target .rad-ui-toggle-group when a parent or
wrapper has [data-orientation="vertical"] or when the attribute is placed on the
roving-focus element), then verify at runtime that data-orientation is applied
to which element (search for data-orientation, RovingFocusGroup, and
rad-ui-toggle-group) and adjust the selector accordingly so vertical groups
render in a column.
🧹 Nitpick comments (8)
styles/themes/components/blockquote.scss (1)

52-55: Consider de-duplicating shared accent color declarations.

soft and accent both set the same border/text accent colors. You could group shared declarations to reduce drift risk.

♻️ Optional DRY refactor
+  &[data-block-quote-variant="soft"],
+  &[data-block-quote-variant="accent"] {
+    border-inline-start-color: var(--rad-ui-color-accent-600);
+    color: var(--rad-ui-color-accent-1000);
+  }
+
   &[data-block-quote-variant="soft"] {
     background-color: var(--rad-ui-color-accent-50);
     border-radius: 0.375rem; // rounded-md
-    border-inline-start-color: var(--rad-ui-color-accent-600);
-    color: var(--rad-ui-color-accent-1000);

Also applies to: 76-79

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/blockquote.scss` around lines 52 - 55, The soft and
accent blockquote variants duplicate the same border/text accent declarations;
factor those shared declarations into a single reusable SCSS unit (e.g., a
placeholder like %blockquote-accent or a mixin named blockquote-accent) that
contains border-inline-start-color and color, then `@extend` or include that
placeholder/mixin inside both the soft and accent variant blocks while keeping
their variant-specific background-color lines in place; apply the same refactor
for the other duplicated block (the one currently at lines 76-79).
styles/themes/components/navigation-menu.scss (2)

84-84: Consider using a z-index token for dropdown layering.

The hardcoded z-index: 100 could lead to layering conflicts with other components. If the design system has a z-index scale token (e.g., --rad-ui-z-dropdown), using it here would improve consistency across components.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/navigation-menu.scss` at line 84, Replace the
hardcoded z-index: 100 in the navigation menu styles with the design-system
z-index token to avoid layering conflicts; locate the rule in
styles/themes/components/navigation-menu.scss where z-index is set and change it
to use the shared token (e.g., var(--rad-ui-z-dropdown) or the project’s
equivalent SCSS variable like $z-index-dropdown) so dropdown stacking follows
the global z-index scale.

8-8: Consider using design tokens consistently for border-radius.

The file mixes hardcoded border-radius values with token usage:

  • Line 8: border-radius: 1rem; (hardcoded)
  • Line 27: border-radius: 0.8rem; (hardcoded)
  • Line 81: border-radius: 1rem; (hardcoded)
  • Line 90: border-radius: var(--rad-ui-radius-md); (token)

Given the PR's objective of introducing semantic design tokens, consider using radius tokens (e.g., --rad-ui-radius-lg, --rad-ui-radius-md) consistently for maintainability.

Also applies to: 27-27, 81-81, 90-90

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/navigation-menu.scss` at line 8, Replace hardcoded
border-radius values with the design tokens used elsewhere: find the rules that
currently set border-radius: 1rem; (two occurrences) and border-radius: 0.8rem;
and change them to the appropriate semantic tokens (e.g., --rad-ui-radius-lg for
1rem and --rad-ui-radius-md for 0.8rem) so all radius values use the
--rad-ui-radius-* tokens (note there is already a line using --rad-ui-radius-md
to mirror). Update the three hardcoded occurrences to use the token names
instead of literal rem values.
src/components/ui/Checkbox/stories/Checkbox.stories.tsx (2)

20-20: Prefer semantic text tokens over hardcoded gray utilities in stories.

These updated story lines still use raw grayscale utilities (text-gray-*). If semantic tokens are available in this repo, use those classes/variables so stories stay aligned with the new design-token direction.

Also applies to: 35-37, 49-50, 55-56, 87-87, 96-100

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/Checkbox/stories/Checkbox.stories.tsx` at line 20, Replace
hardcoded grayscale utility classes on story labels with the project's semantic
text token classes/variables; locate the label elements in Checkbox.stories.tsx
(e.g., the label with htmlFor="accept-terms" and the other label instances
referenced in the comment) and swap className values like "text-gray-950" /
"text-gray-700" / "text-gray-500" for the corresponding semantic token class
names or CSS variable (e.g., text-primary, text-muted, text-subtle or
--text-strong/--text-muted) used across the codebase so stories use design
tokens consistently.

89-100: Replace nested conditional label/description rendering with a config map.

The chained ternary + duplicated conditional blocks are harder to extend. A keyed config object will simplify this story and reduce branching noise.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/Checkbox/stories/Checkbox.stories.tsx` around lines 89 -
100, Replace the chained ternary and duplicated conditional blocks that render
the label and description for keys 'terms' and 'notifications' with a single
keyed config map (e.g., an object mapping each key to { label, description }).
In the component/story where the nested conditional currently produces the label
and the two separate {key === 'terms'} / {key === 'notifications'} paragraphs,
look up the entry for the current key (e.g., config[key]) and render its label
and description if present (use optional chaining/fallbacks), removing the
chained ternary and the two separate conditional <p> blocks; this centralizes
text for 'terms' and 'notifications' and makes it easy to add more keys later.
src/components/ui/Tree/fragments/TreeItem.tsx (1)

108-108: Use explicit boolean value for aria-hidden.

While JSX treats aria-hidden (without a value) as truthy, ARIA spec expects "true" or "false". Use aria-hidden="true" for explicit semantics.

-                    <span className={`${rootClass}-item-chevron`} aria-hidden>
+                    <span className={`${rootClass}-item-chevron`} aria-hidden="true">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/Tree/fragments/TreeItem.tsx` at line 108, The span element
in TreeItem.tsx using class `${rootClass}-item-chevron` sets aria-hidden without
a value; update that attribute to use an explicit boolean string
(aria-hidden="true") to comply with ARIA semantics so assistive tech reads it
correctly.
styles/themes/components/context-menu.scss (1)

170-182: Keep the danger color semantic here too.

This selector goes back to a palette-specific --rad-ui-color-red-900 while the rest of the file resolves through semantic theme tokens. That makes the danger state harder to retheme and easier to drift in dark/high-contrast variants. Please route it through a semantic danger foreground token and reuse that token for the shortcut/sub-icon overrides.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/context-menu.scss` around lines 170 - 182, The
selectors under .rad-ui-context-menu-danger are using the palette token
--rad-ui-color-red-900; change all occurrences in that block (including the
nested .rad-ui-context-menu-shortcut and .rad-ui-context-menu-sub-icon
overrides) to use the semantic danger foreground token used elsewhere in the
theme (e.g. the project’s --*-danger-foreground or equivalent semantic token) so
the danger state is themed consistently and reused for the shortcut/sub-icon
styles; update .rad-ui-context-menu-danger, &[data-highlighted],
&[aria-selected="true"], and the nested shortcut/sub-icon rules to reference
that single semantic token.
styles/themes/components/select.scss (1)

152-168: Collapse the overlapping selected-item state rules.

&[aria-selected="true"][data-active="true"] is declared in both blocks, so the later selector immediately overrides the earlier background-color. Merging these into a single rule would make the final active-selected state explicit and easier to maintain.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/select.scss` around lines 152 - 168, The two
overlapping rules for &[aria-selected="true"][data-active="true"] and
&[aria-selected="true"][data-highlighted] should be collapsed into a single
explicit rule so the active-selected state isn't unintentionally overridden;
combine the styles (background-color, color, box-shadow) and the nested
.rad-ui-select-item-indicator { opacity: 1 } into one selector group that
includes both &[aria-selected="true"][data-active="true"] and
&[aria-selected="true"][data-highlighted] (or a combined selector like
&[aria-selected="true"][data-active="true"],
&[aria-selected="true"][data-highlighted]) and remove the earlier duplicate
block so the final active-selected styling is explicit and maintainable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/ui/Badge/stories/Badge.stories.tsx`:
- Line 146: The story shows Badge with both color="blue" and
variant="destructive", which conflicts with the WithColors pattern that avoids
using color with destructive; update this Badge instance (component Badge in the
stories file) to follow the same pattern as WithColors by removing the color
prop when variant="destructive" (i.e., use <Badge
variant="destructive">Destructive</Badge>) so examples are consistent for
consumers.

In `@src/components/ui/Checkbox/stories/Checkbox.stories.tsx`:
- Line 17: The onCheckedChange handlers on Checkbox.Root (and the other
instances around lines 78–80) currently use unsafe casts like "val as boolean";
replace those with explicit boolean normalization (e.g., use val === true) and
update state via the functional setter to avoid closure issues—call
setChecked(prev => val === true) (or equivalent) inside the onCheckedChange for
Checkbox.Root to safely handle boolean | 'indeterminate' | null without type
assertions.

In `@src/components/ui/RadioCards/fragments/RadioCardsRoot.tsx`:
- Around line 45-51: RadioCardsRoot spreads {...props} after the explicit
orientation prop, allowing props.orientation to override the explicit value and
making behavior inconsistent with CheckboxCardsRoot; fix this in the
RadioGroupPrimitive.Root element inside RadioCardsRoot by moving the {...props}
spread to come before the explicit orientation prop (mirroring
CheckboxCardsRoot) so orientation={orientation} takes precedence; update the JSX
in RadioCardsRoot (RadioGroupPrimitive.Root) accordingly.

In `@src/components/ui/ToggleGroup/stories/ToggleGroup.stories.tsx`:
- Around line 17-25: ToggleGroup.Root is missing an accessible name, so add an
aria-label (for example "Text formatting") to the ToggleGroup.Root element to
give the group/toolbar context for screen readers; update the ToggleGroup.Root
declaration (the component named ToggleGroup.Root in this story) to include
aria-label="Text formatting".

In `@styles/themes/components/_avatar-base.scss`:
- Around line 64-70: The accent border is being applied to the avatar wrapper so
the default soft inset shadow from the fallback still shows; update the selector
&[data-rad-ui-accent-color]:not([data-rad-ui-has-image]) to either move the
accent box-shadow onto .rad-ui-avatar-fallback or explicitly unset the inherited
shadow on .rad-ui-avatar-fallback before applying the accent shadow. Concretely,
locate the block with &[data-rad-ui-accent-color]:not([data-rad-ui-has-image])
and adjust .rad-ui-avatar-fallback to remove any existing box-shadow (e.g.,
box-shadow: none) and then apply the accent box-shadow there so the fallback
fully replaces the default inset border.

In `@styles/themes/components/context-menu.scss`:
- Around line 35-36: The rule setting width: max-content (paired with min-width:
14.5rem) allows unbounded growth; change it to include a viewport-aware
max-width (e.g. max-width: min(90vw, <some-desired-absolute>)) on the same
selector and add truncation rules on the menu row text cell (overflow: hidden;
text-overflow: ellipsis; white-space: nowrap) so long labels/shortcuts don’t
expand the panel; apply the same max-width/truncation changes to the other
occurrences noted (the blocks at the lines referenced: 69 and 112) and ensure
the min-width stays for small menus.
- Around line 61-66: The rule using justify-content: space-between is causing
labels to separate from their leading icons when items gain extra children;
change the layout to keep items left-packed (use justify-content: flex-start or
remove space-between) and instead push only trailing affordances by adding
margin-inline-start: auto to .rad-ui-context-menu-shortcut and
.rad-ui-context-menu-sub-icon (apply the same change to the other blocks
mentioned around the other occurrences). Locate the blocks where
justify-content: space-between is set (e.g., the rule containing gap: 0.75rem)
and replace the justification, then add margin-inline-start: auto to the two
selector rules so trailing icons/shortcuts are right-aligned without
distributing free space across the whole row.

In `@styles/themes/components/dialog.scss`:
- Around line 79-94: The CSS uses .rad-ui-dialog-footer with flex-direction:
column-reverse which flips visual order but not DOM/tab order; change it to
maintain DOM order by replacing column-reverse with column (i.e., use
flex-direction: column for the mobile/small-screen rule) and keep the existing
media query that sets flex-direction: row for larger screens so visual and
keyboard/tab order remain aligned; ensure no other rules rely on the reverse
ordering and adjust spacing/gap if needed.

In `@styles/themes/components/tabs.scss`:
- Around line 8-11: The tab strip is shrink-wrapped (width: fit-content in the
tab strip block) so toggling the active label weight (500→600 at the active
label rule) changes trigger and strip width causing layout jump; fix by
preventing width reflow—either remove/replace width: fit-content on the tab
strip with a non-shrinking layout (e.g., width: auto or full-width), or ensure
each .tab-trigger has a fixed/minimum width (or reserve space by using the
heavier font-weight on all triggers and use opacity/color for active emphasis)
so the font-weight change on the active label (the active label rule) no longer
alters element width.

In `@styles/themes/components/toggle-group.scss`:
- Around line 32-35: Hover styles for the toggle group are being applied even
when items are disabled because the &:hover block isn't restricted; update the
hover selectors in the toggle-group.scss hover rule(s) (the existing &:hover
blocks) to only apply when the element is not disabled by using a negation such
as &:not([data-disabled]):not(:disabled):hover (and update the second hover
block similarly), so disabled items (marked with [data-disabled] or :disabled)
do not receive the hover background or text color.

In `@styles/themes/components/toggle.scss`:
- Around line 18-21: The hover styles currently apply even when the toggle is
disabled; update the hover selector so it only matches enabled toggles by gating
&:hover with enabled state checks (e.g., use :not([data-disabled]) and
:not(:disabled) combined before :hover) for the rule that contains
background-color and border-color, and make the same change for the other hover
block referenced (the block around lines with similar hover styles). Ensure you
modify the selectors that contain &:hover so disabled controls no longer receive
hover visuals.

---

Outside diff comments:
In `@src/components/ui/Tree/fragments/TreeItem.tsx`:
- Line 141: The current JSX uses key={subItem.label} which can create duplicate
React keys; update the key for the mapped child in TreeItem (where subItem is
rendered) to use a unique identifier from the data (e.g., subItem.id) or a
stable composite (e.g., `${subItem.id}-${subItem.label}` or include the index as
a fallback) instead of subItem.label so sibling items always have unique keys.

---

Duplicate comments:
In `@styles/themes/components/tabs.scss`:
- Around line 7-17: The .rad-ui-tabs-list class currently always lays out tabs
horizontally; update the stylesheet so it responds to TabList's aria-orientation
by adding a rule targeting .rad-ui-tabs-list[aria-orientation="vertical"] to
switch layout to a vertical column (set flex-direction: column and adjust
alignment/gap/width as needed) so vertical tabs render stacked instead of in a
horizontal strip; ensure the rule references the existing .rad-ui-tabs-list
selector so it overrides the default display properties.

In `@styles/themes/components/toggle-group.scss`:
- Around line 10-12: The CSS only flips layout when data-orientation="vertical"
is on .rad-ui-toggle-group itself, so when the primitive places data-orientation
on the roving-focus wrapper (RovingFocusGroup) the vertical layout never
applies; update the selector in toggle-group.scss to also match the case where
the attribute lives on the wrapper/ancestor (e.g. target .rad-ui-toggle-group
when a parent or wrapper has [data-orientation="vertical"] or when the attribute
is placed on the roving-focus element), then verify at runtime that
data-orientation is applied to which element (search for data-orientation,
RovingFocusGroup, and rad-ui-toggle-group) and adjust the selector accordingly
so vertical groups render in a column.

---

Nitpick comments:
In `@src/components/ui/Checkbox/stories/Checkbox.stories.tsx`:
- Line 20: Replace hardcoded grayscale utility classes on story labels with the
project's semantic text token classes/variables; locate the label elements in
Checkbox.stories.tsx (e.g., the label with htmlFor="accept-terms" and the other
label instances referenced in the comment) and swap className values like
"text-gray-950" / "text-gray-700" / "text-gray-500" for the corresponding
semantic token class names or CSS variable (e.g., text-primary, text-muted,
text-subtle or --text-strong/--text-muted) used across the codebase so stories
use design tokens consistently.
- Around line 89-100: Replace the chained ternary and duplicated conditional
blocks that render the label and description for keys 'terms' and
'notifications' with a single keyed config map (e.g., an object mapping each key
to { label, description }). In the component/story where the nested conditional
currently produces the label and the two separate {key === 'terms'} / {key ===
'notifications'} paragraphs, look up the entry for the current key (e.g.,
config[key]) and render its label and description if present (use optional
chaining/fallbacks), removing the chained ternary and the two separate
conditional <p> blocks; this centralizes text for 'terms' and 'notifications'
and makes it easy to add more keys later.

In `@src/components/ui/Tree/fragments/TreeItem.tsx`:
- Line 108: The span element in TreeItem.tsx using class
`${rootClass}-item-chevron` sets aria-hidden without a value; update that
attribute to use an explicit boolean string (aria-hidden="true") to comply with
ARIA semantics so assistive tech reads it correctly.

In `@styles/themes/components/blockquote.scss`:
- Around line 52-55: The soft and accent blockquote variants duplicate the same
border/text accent declarations; factor those shared declarations into a single
reusable SCSS unit (e.g., a placeholder like %blockquote-accent or a mixin named
blockquote-accent) that contains border-inline-start-color and color, then
`@extend` or include that placeholder/mixin inside both the soft and accent
variant blocks while keeping their variant-specific background-color lines in
place; apply the same refactor for the other duplicated block (the one currently
at lines 76-79).

In `@styles/themes/components/context-menu.scss`:
- Around line 170-182: The selectors under .rad-ui-context-menu-danger are using
the palette token --rad-ui-color-red-900; change all occurrences in that block
(including the nested .rad-ui-context-menu-shortcut and
.rad-ui-context-menu-sub-icon overrides) to use the semantic danger foreground
token used elsewhere in the theme (e.g. the project’s --*-danger-foreground or
equivalent semantic token) so the danger state is themed consistently and reused
for the shortcut/sub-icon styles; update .rad-ui-context-menu-danger,
&[data-highlighted], &[aria-selected="true"], and the nested shortcut/sub-icon
rules to reference that single semantic token.

In `@styles/themes/components/navigation-menu.scss`:
- Line 84: Replace the hardcoded z-index: 100 in the navigation menu styles with
the design-system z-index token to avoid layering conflicts; locate the rule in
styles/themes/components/navigation-menu.scss where z-index is set and change it
to use the shared token (e.g., var(--rad-ui-z-dropdown) or the project’s
equivalent SCSS variable like $z-index-dropdown) so dropdown stacking follows
the global z-index scale.
- Line 8: Replace hardcoded border-radius values with the design tokens used
elsewhere: find the rules that currently set border-radius: 1rem; (two
occurrences) and border-radius: 0.8rem; and change them to the appropriate
semantic tokens (e.g., --rad-ui-radius-lg for 1rem and --rad-ui-radius-md for
0.8rem) so all radius values use the --rad-ui-radius-* tokens (note there is
already a line using --rad-ui-radius-md to mirror). Update the three hardcoded
occurrences to use the token names instead of literal rem values.

In `@styles/themes/components/select.scss`:
- Around line 152-168: The two overlapping rules for
&[aria-selected="true"][data-active="true"] and
&[aria-selected="true"][data-highlighted] should be collapsed into a single
explicit rule so the active-selected state isn't unintentionally overridden;
combine the styles (background-color, color, box-shadow) and the nested
.rad-ui-select-item-indicator { opacity: 1 } into one selector group that
includes both &[aria-selected="true"][data-active="true"] and
&[aria-selected="true"][data-highlighted] (or a combined selector like
&[aria-selected="true"][data-active="true"],
&[aria-selected="true"][data-highlighted]) and remove the earlier duplicate
block so the final active-selected styling is explicit and maintainable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7ff5ec4e-1ed8-4066-9b44-30ff1d0ab545

📥 Commits

Reviewing files that changed from the base of the PR and between ed375a2 and 7c4c040.

📒 Files selected for processing (52)
  • package.json
  • src/components/tools/SandboxEditor/SandboxEditor.tsx
  • src/components/ui/Badge/stories/Badge.stories.tsx
  • src/components/ui/Card/fragments/CardRoot.tsx
  • src/components/ui/Card/stories/Card.stories.tsx
  • src/components/ui/Checkbox/stories/Checkbox.stories.tsx
  • src/components/ui/CheckboxCards/fragments/CheckboxCardsRoot.tsx
  • src/components/ui/CheckboxCards/stories/CheckboxCards.stories.tsx
  • src/components/ui/Collapsible/stories/Collapsible.stories.tsx
  • src/components/ui/ContextMenu/stories/ContextMenu.stories.tsx
  • src/components/ui/HoverCard/stories/HoverCard.stories.tsx
  • src/components/ui/Progress/stories/Progress.stories.tsx
  • src/components/ui/RadioCards/RadioCards.stories.tsx
  • src/components/ui/RadioCards/fragments/RadioCardsRoot.tsx
  • src/components/ui/Slider/fragments/SliderRangeSlider.tsx
  • src/components/ui/Slider/stories/Slider.stories.tsx
  • src/components/ui/Switch/stories/Switch.stories.tsx
  • src/components/ui/TextArea/stories/TextArea.stories.tsx
  • src/components/ui/Theme/Theme.tsx
  • src/components/ui/ToggleGroup/stories/ToggleGroup.stories.tsx
  • src/components/ui/Tooltip/fragments/TooltipContent.tsx
  • src/components/ui/Tree/fragments/TreeItem.tsx
  • src/core/primitives/CheckboxGroup/fragments/CheckboxGroupPrimitiveTrigger.tsx
  • src/core/primitives/Dialog/fragments/DialogPrimitivePortal.tsx
  • src/core/primitives/Menu/fragments/MenuPrimitiveRoot.tsx
  • src/core/primitives/Menu/tests/MenuPrimitive.test.tsx
  • src/core/utils/RovingFocusGroup/fragments/RovingFocusGroup.tsx
  • styles/themes/components/_avatar-base.scss
  • styles/themes/components/alert-dialog.scss
  • styles/themes/components/avatar-group.scss
  • styles/themes/components/blockquote.scss
  • styles/themes/components/card.scss
  • styles/themes/components/code.scss
  • styles/themes/components/context-menu.scss
  • styles/themes/components/dialog.scss
  • styles/themes/components/menubar.scss
  • styles/themes/components/minimap.scss
  • styles/themes/components/navigation-menu.scss
  • styles/themes/components/number-field.scss
  • styles/themes/components/radiocards.scss
  • styles/themes/components/select.scss
  • styles/themes/components/slider.scss
  • styles/themes/components/splitter.scss
  • styles/themes/components/switch.scss
  • styles/themes/components/table.scss
  • styles/themes/components/tabs.scss
  • styles/themes/components/textarea.scss
  • styles/themes/components/toggle-group.scss
  • styles/themes/components/toggle.scss
  • styles/themes/components/toolbar.scss
  • styles/themes/components/tree.scss
  • styles/themes/default.scss
💤 Files with no reviewable changes (1)
  • styles/themes/components/table.scss
✅ Files skipped from review due to trivial changes (8)
  • package.json
  • src/components/ui/Card/fragments/CardRoot.tsx
  • src/components/ui/Slider/fragments/SliderRangeSlider.tsx
  • styles/themes/components/toolbar.scss
  • styles/themes/components/alert-dialog.scss
  • styles/themes/components/textarea.scss
  • src/components/ui/Card/stories/Card.stories.tsx
  • src/components/ui/ContextMenu/stories/ContextMenu.stories.tsx
🚧 Files skipped from review as they are similar to previous changes (19)
  • src/core/primitives/Menu/tests/MenuPrimitive.test.tsx
  • src/components/ui/RadioCards/RadioCards.stories.tsx
  • src/components/ui/Tooltip/fragments/TooltipContent.tsx
  • src/components/ui/Theme/Theme.tsx
  • src/components/ui/HoverCard/stories/HoverCard.stories.tsx
  • styles/themes/components/code.scss
  • src/core/primitives/Dialog/fragments/DialogPrimitivePortal.tsx
  • styles/themes/components/switch.scss
  • styles/themes/components/number-field.scss
  • styles/themes/components/avatar-group.scss
  • src/components/ui/Collapsible/stories/Collapsible.stories.tsx
  • styles/themes/components/minimap.scss
  • src/components/ui/TextArea/stories/TextArea.stories.tsx
  • src/components/ui/CheckboxCards/stories/CheckboxCards.stories.tsx
  • src/components/ui/Switch/stories/Switch.stories.tsx
  • styles/themes/default.scss
  • styles/themes/components/slider.scss
  • styles/themes/components/menubar.scss
  • styles/themes/components/splitter.scss

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 3, 2026

Coverage

This report compares the PR with the base branch. "Δ" shows how the PR affects each metric.

Metric PR Δ
Statements 81.22% +0.27%
Branches 64.14% -0.24%
Functions 69.57% +0.52%
Lines 82.54% +0.27%

Coverage decreased for at least one metric. Please add or update tests to improve coverage.

Run npm run coverage locally for detailed reports and target untested areas to raise these numbers.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (4)
styles/themes/components/toggle-group.scss (2)

42-45: ⚠️ Potential issue | 🟡 Minor

Disabled items still receive hover styles.

The :hover rule applies unconditionally, so disabled items will show the hover background and text color despite being visually marked as disabled.

Proposed fix
-        &:hover {
+        &:not([data-disabled]):not(:disabled):hover {
             background-color: color-mix(in oklab, var(--rad-ui-surface-panel) 88%, transparent);
             color: var(--rad-ui-text-primary);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/toggle-group.scss` around lines 42 - 45, The hover
rule currently applies to all items via the &:hover selector, causing disabled
items to receive hover styles; update the selector to exclude disabled elements
(e.g., use &:not([aria-disabled="true"]):hover and/or &:not(:disabled):hover) so
the background-color and color rules only apply to interactive items and not to
elements marked disabled.

10-12: ⚠️ Potential issue | 🟠 Major

Vertical orientation selector won't match at runtime.

Per ToggleGroupRoot.tsx, data-orientation is spread onto RovingFocusGroup.Group, which wraps the div.rad-ui-toggle-group. The current &[data-orientation="vertical"] selector expects the attribute on the .rad-ui-toggle-group element itself.

Proposed fix

Either move the data-orientation attribute to the inner div in ToggleGroupRoot.tsx, or update the selector:

-    &[data-orientation="vertical"] {
+    [data-orientation="vertical"] & {
         flex-direction: column;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/toggle-group.scss` around lines 10 - 12, The SCSS
rule in styles/themes/components/toggle-group.scss currently expects
data-orientation on .rad-ui-toggle-group but ToggleGroupRoot.tsx spreads
data-orientation onto the outer RovingFocusGroup.Group wrapper, so the vertical
selector never matches; either move the data-orientation attribute from the
wrapper to the inner div.rad-ui-toggle-group in ToggleGroupRoot.tsx (so the
existing &[data-orientation="vertical"] selector works), or update the SCSS
selector to target the inner .rad-ui-toggle-group when the attribute is present
on its parent wrapper (i.e., match the wrapper's data-orientation to apply the
same flex-direction: column to .rad-ui-toggle-group).
src/components/ui/ToggleGroup/stories/ToggleGroup.stories.tsx (1)

17-27: ⚠️ Potential issue | 🟡 Minor

Add an accessible name to ToggleGroup.Root.

While the individual items have proper aria-label attributes, the group itself lacks an accessible name. Screen reader users won't have context about what this toolbar controls.

Proposed fix
-                    <ToggleGroup.Root type="multiple" defaultValue={['bold']}>
+                    <ToggleGroup.Root type="multiple" defaultValue={['bold']} aria-label="Text formatting">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/ToggleGroup/stories/ToggleGroup.stories.tsx` around lines
17 - 27, The ToggleGroup.Root is missing an accessible name; add an accessible
name (e.g., aria-label="Text formatting toolbar" or aria-labelledby referencing
a visible label) and ensure the root has an appropriate role such as
role="toolbar" if needed; update the ToggleGroup.Root element (the
ToggleGroup.Root component that wraps ToggleGroup.Item elements) to include the
chosen aria-label or aria-labelledby so screen readers have context for the
bold/italic/underline controls.
styles/themes/components/tabs.scss (1)

8-12: ⚠️ Potential issue | 🟡 Minor

Active-state weight still causes tab-strip reflow.

Line 12 keeps the list shrink-wrapped, so the 500 → 600 jump on Line 55 can still resize both the active trigger and the overall control when selection moves.

Minimal fix
         &.active{
             z-index: 1;
             color: var(--rad-ui-text-primary);
-            font-weight: 600;
             background-color: var(--rad-ui-surface-canvas);
             box-shadow:

If you want to keep the heavier active label, reserve width per trigger instead.

Also applies to: 52-55

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/tabs.scss` around lines 8 - 12, The tab container
uses width: fit-content which shrink-wraps and lets the active-state font-weight
change (500→600) reflow the control; remove or replace width: fit-content on the
container in styles/themes/components/tabs.scss with width: auto (or an explicit
inline-size) and instead reserve space per trigger by adding a min-width to the
trigger selector (e.g., .tab-trigger) or introduce a CSS variable like
--tab-reserve-width applied as min-width on .tab-trigger so the heavier active
label won’t resize the trigger or the overall tab-strip when selection changes.
🧹 Nitpick comments (2)
knowledge/design_system/clarity_design_system.md (1)

67-67: Hyphenate compound adjective for clarity.

Line 67 should use “low-contrast adjacent steps”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@knowledge/design_system/clarity_design_system.md` at line 67, The phrase
"Avoid low contrast adjacent steps" should hyphenate the compound adjective;
update the copy so the sentence reads "Avoid low-contrast adjacent steps"
(replace the existing "Avoid low contrast adjacent steps" text to include the
hyphen between "low" and "contrast").
styles/themes/components/_avatar-base.scss (1)

54-59: Remove duplicated fallback declarations in image-state block.

Line 56-Line 58 repeat the base fallback styles from Line 30-Line 32, so this block can be dropped without behavior change and with less maintenance overhead.

♻️ Proposed no-op cleanup
-  &[data-rad-ui-has-image] {
-    .rad-ui-avatar-fallback {
-      color: var(--rad-ui-text-primary);
-      background-color: var(--rad-ui-surface-muted);
-      box-shadow: inset 0 0 0 1px var(--rad-ui-border-soft);
-    }
-  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/_avatar-base.scss` around lines 54 - 59, The
&[data-rad-ui-has-image] block duplicates the .rad-ui-avatar-fallback styles
already declared earlier, so remove the entire &[data-rad-ui-has-image] {
.rad-ui-avatar-fallback { ... } } block to eliminate duplicate declarations;
locate the selector &[data-rad-ui-has-image] and delete the nested
.rad-ui-avatar-fallback rules (color, background-color, box-shadow) so the
component relies on the single base .rad-ui-avatar-fallback definition.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@knowledge/design_system/clarity_design_system.md`:
- Around line 34-35: The two markdown table rows starting with "| 8    | 700  |
Solid colors              | Disabled text/icons, muted UI elements" and "| 9   
| 800  | Solid colors              | supporting content" are missing the
trailing pipe; update these rows in clarity_design_system.md by appending a
closing "|" to each line so the table cells are properly terminated (i.e.,
ensure both rows end with " |").
- Around line 37-38: The Markdown table ending with the row "| 11   | 1000 |
Accessible text           | Headings,Primary text, max contrast text (near
black) |" needs a blank line added immediately after it to satisfy Markdown
table boundary rules; edit the document to insert one empty line following that
table block so the subsequent content (line 38) is separated from the table.

In `@styles/themes/components/card.scss`:
- Around line 75-114: The footer divider and content top spacing should only
apply when those sections follow another section; update the selectors so
.rad-ui-card-footer’s border/margin/padding apply via an adjacent/sibling rule
(e.g. use .rad-ui-card-content + .rad-ui-card-footer or .rad-ui-card-header +
.rad-ui-card-footer or a combined :is(...) + .rad-ui-card-footer) instead of
unconditionally on .rad-ui-card-footer, and likewise only add
.rad-ui-card-content’s extra padding when it is not the first child (e.g. use
:is(.rad-ui-card-header, .rad-ui-card-actions) + .rad-ui-card-content or
.rad-ui-card-content:not(:first-child)); mirror these conditional selector
changes in the small/large/x-large overrides ([data-card-size="small"] >
.rad-ui-card-footer, [data-card-size="large"] > .rad-ui-card-footer,
[data-card-size="x-large"] > .rad-ui-card-footer and the corresponding content
rules) so separators and inset adjustments only appear when a preceding section
exists.

---

Duplicate comments:
In `@src/components/ui/ToggleGroup/stories/ToggleGroup.stories.tsx`:
- Around line 17-27: The ToggleGroup.Root is missing an accessible name; add an
accessible name (e.g., aria-label="Text formatting toolbar" or aria-labelledby
referencing a visible label) and ensure the root has an appropriate role such as
role="toolbar" if needed; update the ToggleGroup.Root element (the
ToggleGroup.Root component that wraps ToggleGroup.Item elements) to include the
chosen aria-label or aria-labelledby so screen readers have context for the
bold/italic/underline controls.

In `@styles/themes/components/tabs.scss`:
- Around line 8-12: The tab container uses width: fit-content which shrink-wraps
and lets the active-state font-weight change (500→600) reflow the control;
remove or replace width: fit-content on the container in
styles/themes/components/tabs.scss with width: auto (or an explicit inline-size)
and instead reserve space per trigger by adding a min-width to the trigger
selector (e.g., .tab-trigger) or introduce a CSS variable like
--tab-reserve-width applied as min-width on .tab-trigger so the heavier active
label won’t resize the trigger or the overall tab-strip when selection changes.

In `@styles/themes/components/toggle-group.scss`:
- Around line 42-45: The hover rule currently applies to all items via the
&:hover selector, causing disabled items to receive hover styles; update the
selector to exclude disabled elements (e.g., use
&:not([aria-disabled="true"]):hover and/or &:not(:disabled):hover) so the
background-color and color rules only apply to interactive items and not to
elements marked disabled.
- Around line 10-12: The SCSS rule in styles/themes/components/toggle-group.scss
currently expects data-orientation on .rad-ui-toggle-group but
ToggleGroupRoot.tsx spreads data-orientation onto the outer
RovingFocusGroup.Group wrapper, so the vertical selector never matches; either
move the data-orientation attribute from the wrapper to the inner
div.rad-ui-toggle-group in ToggleGroupRoot.tsx (so the existing
&[data-orientation="vertical"] selector works), or update the SCSS selector to
target the inner .rad-ui-toggle-group when the attribute is present on its
parent wrapper (i.e., match the wrapper's data-orientation to apply the same
flex-direction: column to .rad-ui-toggle-group).

---

Nitpick comments:
In `@knowledge/design_system/clarity_design_system.md`:
- Line 67: The phrase "Avoid low contrast adjacent steps" should hyphenate the
compound adjective; update the copy so the sentence reads "Avoid low-contrast
adjacent steps" (replace the existing "Avoid low contrast adjacent steps" text
to include the hyphen between "low" and "contrast").

In `@styles/themes/components/_avatar-base.scss`:
- Around line 54-59: The &[data-rad-ui-has-image] block duplicates the
.rad-ui-avatar-fallback styles already declared earlier, so remove the entire
&[data-rad-ui-has-image] { .rad-ui-avatar-fallback { ... } } block to eliminate
duplicate declarations; locate the selector &[data-rad-ui-has-image] and delete
the nested .rad-ui-avatar-fallback rules (color, background-color, box-shadow)
so the component relies on the single base .rad-ui-avatar-fallback definition.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 70b2d809-6226-4a8c-ae53-0036e237aaf6

📥 Commits

Reviewing files that changed from the base of the PR and between 7c4c040 and 13c1e2a.

⛔ Files ignored due to path filters (1)
  • src/components/ui/Slider/tests/__snapshots__/Slider.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (20)
  • docs/app/docs/components/toggle-group/docs/component_api/item.tsx
  • docs/app/docs/components/toggle-group/docs/examples/ToggleGroupExample.tsx
  • knowledge/design_system/clarity_design_system.md
  • src/components/ui/Badge/stories/Badge.stories.tsx
  • src/components/ui/RadioCards/fragments/RadioCardsRoot.tsx
  • src/components/ui/Slider/fragments/SliderThumb.tsx
  • src/components/ui/ToggleGroup/fragments/ToggleItem.tsx
  • src/components/ui/ToggleGroup/stories/ToggleGroup.stories.tsx
  • styles/themes/components/_avatar-base.scss
  • styles/themes/components/badge.scss
  • styles/themes/components/card.scss
  • styles/themes/components/checkbox-cards.scss
  • styles/themes/components/context-menu.scss
  • styles/themes/components/fix_tokens.cjs
  • styles/themes/components/radio-group.scss
  • styles/themes/components/slider.scss
  • styles/themes/components/splitter.scss
  • styles/themes/components/tabs.scss
  • styles/themes/components/textarea.scss
  • styles/themes/components/toggle-group.scss
✅ Files skipped from review due to trivial changes (4)
  • docs/app/docs/components/toggle-group/docs/examples/ToggleGroupExample.tsx
  • docs/app/docs/components/toggle-group/docs/component_api/item.tsx
  • src/components/ui/Slider/fragments/SliderThumb.tsx
  • styles/themes/components/splitter.scss
🚧 Files skipped from review as they are similar to previous changes (5)
  • styles/themes/components/badge.scss
  • styles/themes/components/fix_tokens.cjs
  • styles/themes/components/textarea.scss
  • styles/themes/components/context-menu.scss
  • styles/themes/components/slider.scss

Comment on lines +34 to +35
| 8 | 700 | Solid colors | Disabled text/icons, muted UI elements
| 9 | 800 | Solid colors | supporting content
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix malformed table rows (missing trailing pipe).

Line 34 and Line 35 are missing the closing |, so markdown table rendering/linting can break.

Proposed fix
-| 8    | 700  | Solid colors              | Disabled text/icons, muted UI elements
-| 9    | 800  | Solid colors              | supporting content
+| 8    | 700  | Solid colors              | Disabled text/icons, muted UI elements |
+| 9    | 800  | Solid colors              | Supporting content |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| 8 | 700 | Solid colors | Disabled text/icons, muted UI elements
| 9 | 800 | Solid colors | supporting content
| 8 | 700 | Solid colors | Disabled text/icons, muted UI elements |
| 9 | 800 | Solid colors | Supporting content |
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 34-34: Table pipe style
Expected: leading_and_trailing; Actual: leading_only; Missing trailing pipe

(MD055, table-pipe-style)


[warning] 35-35: Table pipe style
Expected: leading_and_trailing; Actual: leading_only; Missing trailing pipe

(MD055, table-pipe-style)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@knowledge/design_system/clarity_design_system.md` around lines 34 - 35, The
two markdown table rows starting with "| 8    | 700  | Solid colors             
| Disabled text/icons, muted UI elements" and "| 9    | 800  | Solid colors     
| supporting content" are missing the trailing pipe; update these rows in
clarity_design_system.md by appending a closing "|" to each line so the table
cells are properly terminated (i.e., ensure both rows end with " |").

Comment on lines +37 to +38
| 11 | 1000 | Accessible text | Headings,Primary text, max contrast text (near black) |
---
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add a blank line after the table block.

Line 38 should be separated from the table by a blank line to satisfy markdown table boundary rules.

Proposed fix
-| 11   | 1000 | Accessible text           | Headings,Primary text, max contrast text (near black) |
----
+| 11   | 1000 | Accessible text           | Headings, Primary text, max contrast text (near black) |
+
+---
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| 11 | 1000 | Accessible text | Headings,Primary text, max contrast text (near black) |
---
| 11 | 1000 | Accessible text | Headings, Primary text, max contrast text (near black) |
---
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 37-37: Tables should be surrounded by blank lines

(MD058, blanks-around-tables)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@knowledge/design_system/clarity_design_system.md` around lines 37 - 38, The
Markdown table ending with the row "| 11   | 1000 | Accessible text           |
Headings,Primary text, max contrast text (near black) |" needs a blank line
added immediately after it to satisfy Markdown table boundary rules; edit the
document to insert one empty line following that table block so the subsequent
content (line 38) is separated from the table.

Comment on lines +75 to +114
.rad-ui-card-content {
display: flex;
flex-direction: column;
gap: 1rem;
padding-top: 1.5rem;
}

.rad-ui-card-footer {
display: flex;
flex-direction: column;
gap: 0.75rem;
margin: 1.5rem -1.5rem -1.5rem;
padding: 1.25rem 1.5rem 1.5rem;
border-top: 1px solid var(--rad-ui-border);
background-color: color-mix(in oklab, var(--rad-ui-surface) 94%, var(--rad-ui-surface-subtle));
}

&[data-card-size="small"] > .rad-ui-card-content {
padding-top: 1rem;
}

&[data-card-size="small"] > .rad-ui-card-footer {
margin: 1rem -1rem -1rem;
padding: 1rem;
}

&[data-card-size="large"] > .rad-ui-card-content,
&[data-card-size="x-large"] > .rad-ui-card-content {
padding-top: 1.75rem;
}

&[data-card-size="large"] > .rad-ui-card-footer {
margin: 1.75rem -1.75rem -1.75rem;
padding: 1.25rem 1.75rem 1.75rem;
}

&[data-card-size="x-large"] > .rad-ui-card-footer {
margin: 2rem -2rem -2rem;
padding: 1.5rem 2rem 2rem;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Only add content/footer separators when they follow another section.

These rules assume Card.Content and Card.Footer are never the first child. A content-only card gets double top inset from the root padding plus padding-top, and a first footer renders with an orphan divider/gap. Make that spacing conditional, and mirror the same rule in the small/large/x-large overrides.

💡 Possible fix
   .rad-ui-card-content {
     display: flex;
     flex-direction: column;
     gap: 1rem;
-    padding-top: 1.5rem;
+    padding-top: 0;
   }

   .rad-ui-card-footer {
     display: flex;
     flex-direction: column;
     gap: 0.75rem;
-    margin: 1.5rem -1.5rem -1.5rem;
+    margin: 0 -1.5rem -1.5rem;
     padding: 1.25rem 1.5rem 1.5rem;
-    border-top: 1px solid var(--rad-ui-border);
+    border-top: 0;
     background-color: color-mix(in oklab, var(--rad-ui-surface) 94%, var(--rad-ui-surface-subtle));
   }

+  > .rad-ui-card-content:not(:first-child) {
+    padding-top: 1.5rem;
+  }
+
+  > .rad-ui-card-footer:not(:first-child) {
+    margin-top: 1.5rem;
+    border-top: 1px solid var(--rad-ui-border);
+  }
+
-  &[data-card-size="small"] > .rad-ui-card-content {
+  &[data-card-size="small"] > .rad-ui-card-content:not(:first-child) {
     padding-top: 1rem;
   }

   &[data-card-size="small"] > .rad-ui-card-footer {
-    margin: 1rem -1rem -1rem;
+    margin: 0 -1rem -1rem;
     padding: 1rem;
   }
+
+  &[data-card-size="small"] > .rad-ui-card-footer:not(:first-child) {
+    margin-top: 1rem;
+  }

-  &[data-card-size="large"] > .rad-ui-card-content,
-  &[data-card-size="x-large"] > .rad-ui-card-content {
+  &[data-card-size="large"] > .rad-ui-card-content:not(:first-child),
+  &[data-card-size="x-large"] > .rad-ui-card-content:not(:first-child) {
     padding-top: 1.75rem;
   }

   &[data-card-size="large"] > .rad-ui-card-footer {
-    margin: 1.75rem -1.75rem -1.75rem;
+    margin: 0 -1.75rem -1.75rem;
     padding: 1.25rem 1.75rem 1.75rem;
   }
+
+  &[data-card-size="large"] > .rad-ui-card-footer:not(:first-child) {
+    margin-top: 1.75rem;
+  }

   &[data-card-size="x-large"] > .rad-ui-card-footer {
-    margin: 2rem -2rem -2rem;
+    margin: 0 -2rem -2rem;
     padding: 1.5rem 2rem 2rem;
   }
+
+  &[data-card-size="x-large"] > .rad-ui-card-footer:not(:first-child) {
+    margin-top: 2rem;
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@styles/themes/components/card.scss` around lines 75 - 114, The footer divider
and content top spacing should only apply when those sections follow another
section; update the selectors so .rad-ui-card-footer’s border/margin/padding
apply via an adjacent/sibling rule (e.g. use .rad-ui-card-content +
.rad-ui-card-footer or .rad-ui-card-header + .rad-ui-card-footer or a combined
:is(...) + .rad-ui-card-footer) instead of unconditionally on
.rad-ui-card-footer, and likewise only add .rad-ui-card-content’s extra padding
when it is not the first child (e.g. use :is(.rad-ui-card-header,
.rad-ui-card-actions) + .rad-ui-card-content or
.rad-ui-card-content:not(:first-child)); mirror these conditional selector
changes in the small/large/x-large overrides ([data-card-size="small"] >
.rad-ui-card-footer, [data-card-size="large"] > .rad-ui-card-footer,
[data-card-size="x-large"] > .rad-ui-card-footer and the corresponding content
rules) so separators and inset adjustments only appear when a preceding section
exists.

@kotAPI kotAPI merged commit a42620d into main Apr 4, 2026
10 checks passed
@kotAPI kotAPI deleted the kotapi/ui-improvements-apr-3 branch April 4, 2026 03:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant