Skip to content

feat: added focus state BED-7215#2899

Merged
Holocraft merged 9 commits into
mainfrom
BED-7215
Jun 23, 2026
Merged

feat: added focus state BED-7215#2899
Holocraft merged 9 commits into
mainfrom
BED-7215

Conversation

@Holocraft

@Holocraft Holocraft commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Description

This changeset adds focus state styling to all general inputs: buttons, text fields, toggles, etc. to adhere to WCAG standards.

Motivation and Context

Resolves BED-7215

How Has This Been Tested?

Manually

Screenshots (optional):

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist:

Summary by CodeRabbit

  • New Features

    • Added .focus-ring and .focus-ring-inset Tailwind utilities for consistent keyboard focus indicators.
    • Improved keyboard support for interactive table rows (Enter/Space activation) and added clearer focus styling on several controls.
  • Style / Accessibility

    • Refined focus-visible/hover/focus states across inputs, buttons, dialogs, menus, links, dropdowns/selectors, carousels, toggles, tooltips, and form controls.
    • Updated styling for highlighted/focused dropdown and search results, and enhanced icon styling consistency (including tooltip/icon fallback paths).
  • Theme

    • Updated focus-ring variables and primary/secondary color token mappings to power the new focus styling system.

@Holocraft Holocraft self-assigned this Jun 17, 2026
@Holocraft Holocraft added enhancement New feature or request user interface A pull request containing changes affecting the UI code. labels Jun 17, 2026
@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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

Introduces shared .focus-ring and .focus-ring-inset Tailwind utilities backed by new CSS custom properties in the doodle-ui plugin and preset, then systematically replaces ad-hoc focus-visible ring classes across doodle-ui components, Radix wrapper exports, MUI theme overrides, and bh-shared-ui selector and navigation components. DataTable gains keyboard Enter/Space row activation with matching focus-visible row styling.

Changes

Unified focus and keyboard accessibility pass

Layer / File(s) Summary
Tailwind focus-ring token and utility foundation
packages/javascript/doodle-ui/src/tailwind/plugin.ts, packages/javascript/doodle-ui/src/tailwind/preset.ts
Plugin adds --common-dark/--common-white root variables and renames color tokens to --*-main scheme for both light and dark themes, then introduces --focus-ring* custom properties (--focus-ring, --focus-ring-offset, --focus-ring-width, --focus-ring-offset-width). Registers .focus-ring and .focus-ring-inset utilities that consume these variables. Preset retargets primary/secondary/tertiary to their --*-main values and adds -main suffix aliases; enables common-dark and common-white.
doodle-ui component class string updates
packages/javascript/doodle-ui/src/components/Button/Button.tsx, packages/javascript/doodle-ui/src/components/Checkbox/Checkbox.tsx, packages/javascript/doodle-ui/src/components/Input/Input.tsx, packages/javascript/doodle-ui/src/components/Select/Select.tsx, packages/javascript/doodle-ui/src/components/Tabs/Tabs.tsx, packages/javascript/doodle-ui/src/components/Toggle/Toggle.tsx, packages/javascript/doodle-ui/src/components/Textarea/Textarea.tsx, packages/javascript/doodle-ui/src/components/Switch/Switch.tsx, packages/javascript/doodle-ui/src/components/Carousel/Carousel.tsx, packages/javascript/doodle-ui/src/components/Accordion/Accordion.tsx, packages/javascript/doodle-ui/src/components/Alert/Alert.tsx, packages/javascript/doodle-ui/src/components/Breadcrumbs/Breadcrumbs.tsx, packages/javascript/doodle-ui/src/components/Link/Link.tsx, packages/javascript/doodle-ui/src/components/Pagination/Pagination.tsx, packages/javascript/doodle-ui/src/components/RadioGroup/RadioGroup.tsx, packages/javascript/doodle-ui/src/components/Table/Table.tsx, packages/javascript/doodle-ui/src/components/ToggleGroup/ToggleGroup.tsx
Replaces per-component ad-hoc focus-visible:ring-*/ring-offset-* class combinations with focus:outline-none focus-visible:focus-ring. ToggleGroup also switches background/shadow from hardcoded hex values to token-based neutral and shadow-outer-1 utilities. Updates CVA variants, base class strings, and inline className compositions. No exported component APIs changed.
Radix trigger/close wrappers inject default focus classes
packages/javascript/doodle-ui/src/components/Dialog/Dialog.tsx, packages/javascript/doodle-ui/src/components/Menu/Menu.tsx, packages/javascript/doodle-ui/src/components/Popover/Popover.tsx, packages/javascript/doodle-ui/src/components/Tooltip/Tooltip.tsx
Converts DialogTrigger, DialogClose, MenuTrigger, and PopoverTrigger from direct Radix re-exports to typed React.forwardRef wrappers that merge default focus:outline-none focus-visible:focus-ring classes with caller-supplied className. Extends MenuItem, MenuSubTrigger, and DialogContent class lists with inset focus-ring shadow; adds dark:text-white to PopoverContent.
DataTable keyboard row activation and focus styling
packages/javascript/doodle-ui/src/components/DataTable/DataTable.tsx, packages/javascript/doodle-ui/src/components/DataTable/StorybookExample/utils.tsx
Virtualized rows now destructure and forward onKeyDown/role/tabIndex from TableBodyRowProps, add keyboard handler for Enter/space to trigger handleRowClick, set tabIndex={0} when onRowClick is enabled, and apply focus-visible outline/background row classes. Storybook example adds tabIndex={0}, aria-label attributes, and onClick handlers to action-menu and sort header buttons.
bh-shared-ui MUI focus-visible theme overrides
packages/javascript/bh-shared-ui/src/constants.ts
Introduces focusRingStyles and inheritFocusedIconStyles helper objects and applies them to MuiButtonBase, MuiLink, MuiAutocomplete, MuiCheckbox, MuiRadio, MuiSwitch, MuiTab, MuiMenuItem, and MuiTableSortLabel component Mui-focusVisible / :focus-visible state overrides with consistent ring behavior and optional icon inheritance.
Dropdown selector style constants and consumer wiring
packages/javascript/bh-shared-ui/src/components/DropdownSelector/constants.ts, packages/javascript/bh-shared-ui/src/components/DropdownSelector/DropdownSelector.tsx, packages/javascript/bh-shared-ui/src/components/DropdownSelector/DropdownTriggerContents.tsx, packages/javascript/bh-shared-ui/src/components/SimpleEnvironmentSelector/SimpleEnvironmentSelector.tsx
Adds focusedControlStateStyles and dropdownIconStateStyles composable constants, then expands triggerStyles and optionStyles to include richer hover/focus/SVG-fill behavior. Introduces selectorIconStyles for icon text color based on group states. Wires these exports through DropdownSelector option icons, DropdownTriggerContents button/caret styling, and all SimpleEnvironmentSelector button and icon class compositions with layout flex utility composition.
Additional bh-shared-ui focus styling updates
packages/javascript/bh-shared-ui/src/components/NodeIcon/NodeIcon.tsx, packages/javascript/bh-shared-ui/src/components/SearchResultItem/SearchResultItem.tsx, packages/javascript/bh-shared-ui/src/components/ConfirmationDialog.tsx, packages/javascript/bh-shared-ui/src/components/Navigation/MainNav.tsx, packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/SaveQueryActionMenu.tsx, packages/javascript/bh-shared-ui/src/views/PrivilegeZones/ZoneIcon.tsx
Adds optional className prop to NodeIcon via cn composition for downstream style inheritance. Extends SearchResultItem list item with focus/focus-visible background/text states and wires NodeIcon className for group-based color inheritance. Updates ConfirmationDialog input rounding to rounded-sm; adds focus-visible text/ring classes to MainNav toggle. Refactors SaveQueryActionMenu to use PopoverTrigger with asChild pattern, rendering a styled Button, and updates list item with focus-visible background/ring. Adds className forwarding to ZoneIcon icon rendering paths.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: added focus state BED-7215' accurately describes the main change: adding focus state styling across input components to improve accessibility.
Description check ✅ Passed The description covers all required template sections: describes focus state styling addition, provides Jira ticket link (BED-7215), mentions manual testing, categorizes as new feature, and confirms all checklist items completed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch BED-7215

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added the javascript Pull requests that update javascript code label Jun 17, 2026

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/javascript/doodle-ui/src/components/DataTable/DataTable.tsx`:
- Around line 604-605: The role attribute assignment on the `<tr>` element in
the DataTable component is violating ARIA semantics by conditionally setting
role="button" when onRowClick is true. Table rows must maintain their implicit
role="row" and cannot have role="button" assigned to them. Remove the
`(onRowClick ? 'button' : undefined)` conditional logic from the role assignment
and instead only use the `tableBodyRowRole` value, allowing it to remain
undefined when not explicitly provided. The tabIndex handling on the following
line should remain unchanged as it correctly handles keyboard navigation.

In `@packages/javascript/doodle-ui/src/components/ToggleGroup/ToggleGroup.tsx`:
- Around line 30-35: The ToggleGroup component is setting rovingFocus to false
by default in its destructured parameters, which overrides Radix's default
behavior and disables keyboard navigation. Remove the `= false` default
assignment from the rovingFocus parameter in the arrow function signature so
that Radix's default value of true applies, allowing arrow-key navigation to
work unless explicitly disabled by a caller.
🪄 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: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: f728ee66-008e-4f5e-a7dc-ef494ad0e17b

📥 Commits

Reviewing files that changed from the base of the PR and between 102b0fb and b26ffd4.

📒 Files selected for processing (34)
  • packages/javascript/bh-shared-ui/src/components/ConfirmationDialog.tsx
  • packages/javascript/bh-shared-ui/src/components/DropdownSelector/DropdownSelector.tsx
  • packages/javascript/bh-shared-ui/src/components/DropdownSelector/DropdownTriggerContents.tsx
  • packages/javascript/bh-shared-ui/src/components/DropdownSelector/constants.ts
  • packages/javascript/bh-shared-ui/src/components/Navigation/MainNav.tsx
  • packages/javascript/bh-shared-ui/src/components/NodeIcon/NodeIcon.tsx
  • packages/javascript/bh-shared-ui/src/components/SearchResultItem/SearchResultItem.tsx
  • packages/javascript/bh-shared-ui/src/components/SimpleEnvironmentSelector/SimpleEnvironmentSelector.tsx
  • packages/javascript/bh-shared-ui/src/constants.ts
  • packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/SaveQueryActionMenu.tsx
  • packages/javascript/doodle-ui/src/components/Accordion/Accordion.tsx
  • packages/javascript/doodle-ui/src/components/Alert/Alert.tsx
  • packages/javascript/doodle-ui/src/components/Breadcrumbs/Breadcrumbs.tsx
  • packages/javascript/doodle-ui/src/components/Button/Button.tsx
  • packages/javascript/doodle-ui/src/components/Carousel/Carousel.tsx
  • packages/javascript/doodle-ui/src/components/Checkbox/Checkbox.tsx
  • packages/javascript/doodle-ui/src/components/DataTable/DataTable.tsx
  • packages/javascript/doodle-ui/src/components/DataTable/StorybookExample/utils.tsx
  • packages/javascript/doodle-ui/src/components/Dialog/Dialog.tsx
  • packages/javascript/doodle-ui/src/components/Input/Input.tsx
  • packages/javascript/doodle-ui/src/components/Link/Link.tsx
  • packages/javascript/doodle-ui/src/components/Menu/Menu.tsx
  • packages/javascript/doodle-ui/src/components/Pagination/Pagination.tsx
  • packages/javascript/doodle-ui/src/components/Popover/Popover.tsx
  • packages/javascript/doodle-ui/src/components/RadioGroup/RadioGroup.tsx
  • packages/javascript/doodle-ui/src/components/Select/Select.tsx
  • packages/javascript/doodle-ui/src/components/Switch/Switch.tsx
  • packages/javascript/doodle-ui/src/components/Table/Table.tsx
  • packages/javascript/doodle-ui/src/components/Tabs/Tabs.tsx
  • packages/javascript/doodle-ui/src/components/Textarea/Textarea.tsx
  • packages/javascript/doodle-ui/src/components/Toggle/Toggle.tsx
  • packages/javascript/doodle-ui/src/components/ToggleGroup/ToggleGroup.tsx
  • packages/javascript/doodle-ui/src/components/Tooltip/Tooltip.tsx
  • packages/javascript/doodle-ui/src/tailwind/plugin.ts

Comment thread packages/javascript/doodle-ui/src/components/DataTable/DataTable.tsx Outdated
Comment thread packages/javascript/doodle-ui/src/components/ToggleGroup/ToggleGroup.tsx Outdated
'active:ring-0 active:bg-[#C0C6CB] dark:active:bg-neutral-2',
'focus:text-[#121212] dark:focus:text-white',
'focus:ring-2 focus:ring-offset-2 focus:ring-secondary dark:focus:ring-offset-[#1F1F1F]',
'focus-visible:text-[#121212] dark:focus-visible:text-white',

@KillahDillah KillahDillah Jun 18, 2026

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.

Using the new color token of text-main will allow for dark:focus-visible:text-white to be removed.

<div
className='flex items-center justify-center border border-neutral-dark-1 rounded-full p-1 size-[22px] text-neutral-dark-1 bg-neutral-light-1 pointer-events-none'
className={cn(
'flex items-center justify-center border border-neutral-dark-1 rounded-full p-1 size-[22px] text-neutral-dark-1 bg-neutral-light-1 pointer-events-none',

@KillahDillah KillahDillah Jun 18, 2026

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.

same here with border-neutral-dark-1 and text-neutral-dark-1 - to common-dark.

common-dark will need to be uncommented from preset.ts and plugin.ts

same here with border-neutral-light-1 - to common-white.
common-white will need to be uncommented from preset.ts and plugin.ts

style={style}
className={cn(
'hover:bg-secondary hover:text-neutral-light-1 hover:dark:bg-secondary-variant-2 hover:dark:text-neutral-dark-1',
'group hover:bg-secondary hover:text-neutral-light-1 hover:dark:bg-secondary-variant-2 hover:dark:text-neutral-dark-1 focus:bg-secondary focus:text-neutral-light-1 focus:dark:bg-secondary-variant-2 focus:dark:text-neutral-dark-1 focus-visible:bg-secondary focus-visible:text-neutral-light-1 focus-visible:dark:bg-secondary-variant-2 focus-visible:dark:text-neutral-dark-1',

@KillahDillah KillahDillah Jun 18, 2026

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.

Same here - see above

'hover:bg-secondary hover:text-neutral-light-1 hover:dark:bg-secondary-variant-2 hover:dark:text-neutral-dark-1',
'group hover:bg-secondary hover:text-neutral-light-1 hover:dark:bg-secondary-variant-2 hover:dark:text-neutral-dark-1 focus:bg-secondary focus:text-neutral-light-1 focus:dark:bg-secondary-variant-2 focus:dark:text-neutral-dark-1 focus-visible:bg-secondary focus-visible:text-neutral-light-1 focus-visible:dark:bg-secondary-variant-2 focus-visible:dark:text-neutral-dark-1',
{
'bg-secondary text-neutral-light-1 dark:bg-secondary-variant-2 dark:text-neutral-dark-1':

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.

also here - use new color tokens

<PopoverTrigger
aria-label='Show save query options'
className='inline-flex items-center justify-center whitespace-nowrap rounded-3xl text-sm ring-offset-background transition-colors hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 active:no-underline bg-neutral-light-5 text-neutral-dark-0 shadow-outer-1 hover:bg-secondary hover:text-white h-9 px-4 py-1 text-xs rounded-l-none pl-2 -ml-1 dark:text-neutral-dark-1 dark:hover:text-white'
className='inline-flex items-center justify-center whitespace-nowrap rounded-3xl text-sm ring-offset-focus-offset transition-colors hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 active:no-underline bg-neutral-light-5 text-neutral-dark-0 shadow-outer-1 hover:bg-secondary hover:text-white focus-visible:bg-secondary focus-visible:text-white h-9 px-4 py-1 text-xs rounded-l-none pl-2 -ml-1 dark:text-neutral-dark-1 dark:hover:text-white'

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.

dark and light mode styles can be reduced if using the new tailwind presets


const CheckboxVariants = cva(
'peer shrink-0 rounded-sm border-2 border-neutral-dark-1 dark:border-neutral-light-1 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-neutral-dark-1 data-[state=checked]:text-neutral-light-1 dark:data-[state=checked]:bg-neutral-light-1 dark:data-[state=checked]:text-neutral-dark-1',
'peer shrink-0 rounded-sm border-2 border-neutral-dark-1 dark:border-neutral-light-1 focus:outline-none focus-visible:focus-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-neutral-dark-1 data-[state=checked]:text-neutral-light-1 dark:data-[state=checked]:bg-neutral-light-1 dark:data-[state=checked]:text-neutral-dark-1',

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.

same here with new color tokens

'--primary': dark.primary.main,
'--primary-variant': dark.primary.variant,
'--secondary': dark.secondary.main,
'--secondary-main': dark.secondary.main,

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.

this style is redundant to the style above it.

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.

After a meeting, we decided to keep both variable names as a failsafe - but this requires a change to preset.

'--primary': light.primary.main,
'--primary-variant': light.primary.variant,
'--secondary': light.secondary.main,
'--secondary-main': light.secondary.main,

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.

style is redundant

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.

I am noticing a discrepancy in the preset.ts file for secondary.

keep 'secondary', but remove 'secondary-main' and match the same for the below dark mode.

in preset - can you change secondary: 'var(--secondary-main)' to secondary: 'var(--secondary)' ?

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.

After a meeting, we decided to keep both variable names as a failsafe - but this requires a change to preset.

'.focus-ring-inset': {
outline: 'none',
'box-shadow': 'inset 0 0 0 var(--focus-ring-width) var(--focus-ring), var(--tw-shadow, 0 0 #0000)',
},

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.

Very nice 👍

// TODO: Replace hardcoded hex colors with design token CSS variables once the token system is ready.
const ToggleVariants = cva(
'inline-flex items-center justify-center rounded-lg text-sm transition-colors gap-2 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border border-[#C0C6CB] bg-neutral-1 hover:border-[#4A3BD7] hover:bg-[#4A3BD7] hover:text-neutral-1 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-[#4A3BD7] focus-visible:ring-offset-neutral-1 focus-visible:border-[#4A3BD7] focus-visible:bg-[#4A3BD7] focus-visible:text-neutral-1 active:border-[#0D0A30] active:bg-[#0D0A30] active:text-neutral-1 data-[state=on]:border-[#2C2677] data-[state=on]:bg-[#2C2677] data-[state=on]:text-neutral-1 disabled:cursor-not-allowed disabled:border-[#E3E7EA] disabled:bg-[#E3E7EA] disabled:text-[#616161] dark:border-[#121212] dark:bg-[#121212] dark:hover:border-[#66A3FF] dark:hover:bg-[#66A3FF] dark:hover:text-neutral-dark-1 dark:focus-visible:ring-[#66A3FF] dark:focus-visible:ring-offset-neutral-dark-1 dark:focus-visible:border-[#66A3FF] dark:focus-visible:bg-[#66A3FF] dark:focus-visible:text-neutral-dark-1 dark:active:border-[#8D8BF8] dark:active:bg-[#8D8BF8] dark:data-[state=on]:border-[#A1A0FF] dark:data-[state=on]:bg-[#A1A0FF] dark:data-[state=on]:text-neutral-dark-1 dark:disabled:border-[#2E2E2E] dark:disabled:bg-[#2E2E2E] dark:disabled:text-[#A6A6A6]',
'inline-flex items-center justify-center rounded text-sm transition-colors gap-2 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border border-[#C0C6CB] bg-neutral-1 text-neutral-dark-1 hover:border-[#4A3BD7] hover:bg-[#4A3BD7] hover:text-neutral-1 hover:underline focus:outline-none focus-visible:focus-ring focus-visible:border-[#4A3BD7] focus-visible:bg-[#4A3BD7] focus-visible:text-neutral-1 active:border-[#0D0A30] active:bg-[#0D0A30] active:text-neutral-1 data-[state=on]:border-[#2C2677] data-[state=on]:bg-[#2C2677] data-[state=on]:text-neutral-1 disabled:cursor-not-allowed disabled:border-[#E3E7EA] disabled:bg-[#E3E7EA] disabled:text-[#616161] dark:border-[#121212] dark:bg-[#121212] dark:text-neutral-light-1 dark:hover:border-[#66A3FF] dark:hover:bg-[#66A3FF] dark:hover:text-neutral-dark-1 dark:focus-visible:border-[#66A3FF] dark:focus-visible:bg-[#66A3FF] dark:focus-visible:text-neutral-dark-1 dark:active:border-[#8D8BF8] dark:active:bg-[#8D8BF8] dark:active:text-neutral-dark-1 dark:data-[state=on]:border-[#A1A0FF] dark:data-[state=on]:bg-[#A1A0FF] dark:data-[state=on]:text-neutral-dark-1 dark:disabled:border-[#2E2E2E] dark:disabled:bg-[#2E2E2E] dark:disabled:text-[#A6A6A6]',

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.

Many of these HEX codes and dark mode classing can be replaced with the new tokens

@KillahDillah

KillahDillah commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

There's a lot of styling that isn't using the new color tokens - I commented on most of them.

First thing I noticed visually was the color contrast and that the focus indicator loses it's border-radius when open:
Screenshot 2026-06-18 at 9 22 48 AM

Screenshot 2026-06-18 at 3 14 17 PM

variant={variant ?? 'transparent'}
className={cn(
'uppercase',
'uppercase group hover:[&_svg]:text-white hover:[&_svg]:fill-current hover:[&_svg_*]:text-white hover:[&_svg_*]:fill-current focus:[&_svg]:text-white focus:[&_svg]:fill-current focus:[&_svg_*]:text-white focus:[&_svg_*]:fill-current focus-visible:[&_svg]:text-white focus-visible:[&_svg]:fill-current focus-visible:[&_svg_*]:text-white focus-visible:[&_svg_*]:fill-current',

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.

a lot of these styles are included in the triggerStyles - thinking this could be DRYed up.

'--primary': light.primary.main,
'--primary-variant': light.primary.variant,
'--secondary': light.secondary.main,
'--secondary-main': light.secondary.main,

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.

After a meeting, we decided to keep both variable names as a failsafe - but this requires a change to preset.

'--primary': dark.primary.main,
'--primary-variant': dark.primary.variant,
'--secondary': dark.secondary.main,
'--secondary-main': dark.secondary.main,

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.

After a meeting, we decided to keep both variable names as a failsafe - but this requires a change to preset.

export const optionStyles =
'px-4 py-1 rounded-none w-full justify-normal hover:no-underline hover:bg-neutral-4 disabled:bg-neutral-4';
'px-4 py-1 rounded-none w-full justify-normal hover:no-underline hover:bg-neutral-4 hover:[&_svg]:text-white hover:[&_svg]:fill-current hover:[&_svg_*]:text-white hover:[&_svg_*]:fill-current focus:bg-primary focus:text-white focus:[&_svg]:text-white focus:[&_svg]:fill-current focus:[&_svg_*]:text-white focus:[&_svg_*]:fill-current focus-visible:bg-primary focus-visible:text-white focus-visible:[&_svg]:text-white focus-visible:[&_svg]:fill-current focus-visible:[&_svg_*]:text-white focus-visible:[&_svg_*]:fill-current disabled:bg-neutral-4 group';

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.

Many repeating class name here from above - can this be DRYed up?

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
packages/javascript/doodle-ui/src/tailwind/plugin.ts (1)

404-423: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

The outline property doesn't respect border-radius, causing visual inconsistency.

The .focus-ring utility applies both outline and box-shadow, but CSS outline does not respect border-radius. This matches the concern raised in the PR objectives about "border-radius loss" — when applied to rounded elements (buttons, inputs), the outline will remain square while the box-shadow ring follows the border-radius, creating a mismatched appearance.

Consider one of these alternatives:

  1. Remove outline, use only box-shadow (simplest, but may be clipped by overflow: hidden containers):

    '.focus-ring': {
        '--tw-ring-offset-width': 'var(--focus-ring-offset-width)',
        '--tw-ring-offset-color': 'var(--focus-ring-offset)',
        '--tw-ring-color': 'var(--focus-ring)',
        '--tw-ring-width': 'var(--focus-ring-width)',
        '--tw-ring-offset-shadow': '0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)',
        '--tw-ring-shadow': '0 0 0 calc(var(--focus-ring-width) + var(--tw-ring-offset-width)) var(--tw-ring-color)',
        'box-shadow': 'var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 `#0000`)',
    }
  2. Increase outline-offset to visually separate it from the element (outline remains square but is pushed out further)

  3. Use a pseudo-element with border instead of outline (more complex but respects border-radius)

[accessibility_and_usability]

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/javascript/doodle-ui/src/tailwind/plugin.ts` around lines 404 - 423,
The `.focus-ring` utility combines both outline and box-shadow for focus
styling, but CSS outline does not respect border-radius, causing a square
outline on rounded elements while the box-shadow follows the curve. Remove the
outline and outline-offset properties from the `.focus-ring` utility definition
in the addUtilities block, keeping only the box-shadow implementation along with
the CSS custom properties that support it. This ensures the focus ring adapts
consistently to the element's border-radius for a visually cohesive appearance.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/javascript/doodle-ui/src/tailwind/preset.ts`:
- Around line 33-46: The tertiary color mapping in the colors object is pointing
to an undefined CSS variable `--tertiary-main`. The correct CSS variable defined
in plugin.ts is `--tertiary`. Update the tertiary property in the colors object
to map to 'var(--tertiary)' instead of 'var(--tertiary-main)' to reference the
correct variable definition.

---

Outside diff comments:
In `@packages/javascript/doodle-ui/src/tailwind/plugin.ts`:
- Around line 404-423: The `.focus-ring` utility combines both outline and
box-shadow for focus styling, but CSS outline does not respect border-radius,
causing a square outline on rounded elements while the box-shadow follows the
curve. Remove the outline and outline-offset properties from the `.focus-ring`
utility definition in the addUtilities block, keeping only the box-shadow
implementation along with the CSS custom properties that support it. This
ensures the focus ring adapts consistently to the element's border-radius for a
visually cohesive appearance.
🪄 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: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: ebc07c5a-ec61-4782-955c-c883bc2f1612

📥 Commits

Reviewing files that changed from the base of the PR and between fce6f91 and 2949c9c.

📒 Files selected for processing (2)
  • packages/javascript/doodle-ui/src/tailwind/plugin.ts
  • packages/javascript/doodle-ui/src/tailwind/preset.ts

Comment on lines +33 to +46
primary: 'var(--primary-main)',
'primary-main':
'var(--primary-main)' /* failsafe for any classes possibly using the optional '-main' suffix*/,
'primary-variant': 'var(--primary-variant)',

secondary: 'var(--secondary-main)',
'secondary-main':
'var(--secondary-main)' /* failsafe for any classes possibly using the optional '-main' suffix*/,
'secondary-variant': 'var(--secondary-variant)',
'secondary-variant-2': 'var(--secondary-variant-2)',

tertiary: 'var(--tertiary)',
tertiary: 'var(--tertiary-main)',
'tertiary-main':
'var(--tertiary-main)' /* failsafe for any classes possibly using the optional '-main' suffix*/,

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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Search for usage of tertiary color classes

# Search for Tailwind classes using tertiary color
rg -n '\b(bg|text|border|ring)-tertiary\b' packages/javascript/doodle-ui/src --type tsx --type ts -C2

# Check if tertiary is referenced in component styles
ast-grep --pattern 'tertiary'

Repository: SpecterOps/BloodHound

Length of output: 252


🏁 Script executed:

#!/bin/bash
# Check the plugin.ts file to see if --tertiary-main is commented out
fd -type f -name "plugin.ts" packages/javascript/doodle-ui/

# Get the specific lines in plugin.ts
sed -n '40,60p' packages/javascript/doodle-ui/src/tailwind/plugin.ts
sed -n '240,250p' packages/javascript/doodle-ui/src/tailwind/plugin.ts

Repository: SpecterOps/BloodHound

Length of output: 1775


🏁 Script executed:

#!/bin/bash
# Search for all tertiary color usage across the codebase
rg -n 'tertiary' packages/javascript --type ts --type tsx | head -30

Repository: SpecterOps/BloodHound

Length of output: 93


🏁 Script executed:

#!/bin/bash
# Check the ThemeCSSVars definition to understand how tertiary is exposed
rg -n 'ThemeCSSVars' packages/javascript --type ts --type tsx -A 3 -B 1 | head -50

Repository: SpecterOps/BloodHound

Length of output: 93


🏁 Script executed:

#!/bin/bash
# Find ThemeCSSVars definition
fd -type f -name "*.ts" -o -name "*.tsx" | xargs grep -l "ThemeCSSVars" | head -5

Repository: SpecterOps/BloodHound

Length of output: 236


🏁 Script executed:

#!/bin/bash
# Look at useTheme.tsx to see how tertiary is handled
cat packages/javascript/bh-shared-ui/src/hooks/useTheme.tsx | grep -A 5 -B 5 "tertiary"

Repository: SpecterOps/BloodHound

Length of output: 2761


🏁 Script executed:

#!/bin/bash
# Check if there are any other theme color definitions
fd -type f -name "*.ts" packages/javascript/doodle-ui/src/tailwind -exec cat {} \; | grep -n "tertiary" | head -20

Repository: SpecterOps/BloodHound

Length of output: 236


🏁 Script executed:

#!/bin/bash
# Look for the ThemeCSSVars enum/object definition
rg -n "const ThemeCSSVars\|enum ThemeCSSVars\|export.*ThemeCSSVars" packages/javascript -A 10 | head -40

Repository: SpecterOps/BloodHound

Length of output: 47


🏁 Script executed:

#!/bin/bash
# Search for '--tertiary' variable definition (without -main suffix)
rg -n '\-\-tertiary[^-]' packages/javascript/doodle-ui/

Repository: SpecterOps/BloodHound

Length of output: 263


🏁 Script executed:

#!/bin/bash
# Look at the complete preset.ts to understand color mappings
cat packages/javascript/doodle-ui/src/tailwind/preset.ts | head -60

Repository: SpecterOps/BloodHound

Length of output: 2369


🏁 Script executed:

#!/bin/bash
# Check how primary color is defined in plugin.ts and preset.ts to compare
rg -n "primary" packages/javascript/doodle-ui/src/tailwind/preset.ts | head -10

Repository: SpecterOps/BloodHound

Length of output: 861


🏁 Script executed:

#!/bin/bash
# Check if preset.ts extends or inherits from a base config
head -100 packages/javascript/doodle-ui/src/tailwind/preset.ts

Repository: SpecterOps/BloodHound

Length of output: 4068


Critical: tertiary color in preset.ts maps to undefined CSS variable.

Line 44 maps tertiary: 'var(--tertiary-main)', but --tertiary-main is commented out in plugin.ts (lines 51, 244). However, plugin.ts line 205 defines --tertiary: '#02c577', which is the correct variable to use.

Change preset.ts line 44 from tertiary: 'var(--tertiary-main)' to tertiary: 'var(--tertiary)'. Alternatively, uncomment the correct mapping at preset.ts line 85.

Current incorrect code (preset.ts line 44)
tertiary: 'var(--tertiary-main)',
'tertiary-main':
    'var(--tertiary-main)' /* failsafe for any classes possibly using the optional '-main' suffix*/,
'tertiary-variant': 'var(--tertiary-variant)',
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/javascript/doodle-ui/src/tailwind/preset.ts` around lines 33 - 46,
The tertiary color mapping in the colors object is pointing to an undefined CSS
variable `--tertiary-main`. The correct CSS variable defined in plugin.ts is
`--tertiary`. Update the tertiary property in the colors object to map to
'var(--tertiary)' instead of 'var(--tertiary-main)' to reference the correct
variable definition.

@Holocraft Holocraft merged commit e62f862 into main Jun 23, 2026
12 checks passed
@Holocraft Holocraft deleted the BED-7215 branch June 23, 2026 22:49
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 23, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

enhancement New feature or request javascript Pull requests that update javascript code user interface A pull request containing changes affecting the UI code.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants