Skip to content

fix(theme): bordered focus styles take precedence over hover#5893

Merged
wingkwong merged 4 commits into
heroui-inc:canaryfrom
hasegawa-101:fix/bordered-components-focus-visibility
Nov 16, 2025
Merged

fix(theme): bordered focus styles take precedence over hover#5893
wingkwong merged 4 commits into
heroui-inc:canaryfrom
hasegawa-101:fix/bordered-components-focus-visibility

Conversation

@hasegawa-101
Copy link
Copy Markdown

@hasegawa-101 hasegawa-101 commented Nov 11, 2025

Closes #5585

📝 Description

Fixed bordered Input, NumberInput, and Select components where hover styles were taking precedence over focus styles, making it difficult to see the focus state when hovering and clicking on the input.

⛳️ Current behavior (updates)

When clicking on a bordered input/select component while hovering:

  • The focus border style was not visible immediately
  • Focus border only became visible after moving the mouse away
  • Hover styles overrode focus styles due to CSS specificity

This caused accessibility issues as users couldn't see the focus state clearly when interacting with the component using a mouse.

🚀 New behavior

  • Focus styles now take precedence over hover styles on bordered components
  • When clicking on a hovered input/select, the focus border is immediately visible
  • Added group-data-[focus=true]:data-[hover=true]:border-* classes to ensure focus styles win in the specificity battle
  • Affected components: Input, NumberInput, and Select with bordered variant

The fix ensures proper visual feedback for all interaction states, improving accessibility.

2025-11-11.13.55.49.mov

💣 Is this a breaking change (Yes/No):

No

📝 Additional Information

This fix applies to all color variants (default, primary, secondary, success, warning, danger) of the bordered style. Keyboard navigation (tab key focus) was already working correctly in v2.8.2.

Summary by CodeRabbit

  • Style
    • Improved visual feedback for Input, Number Input, and Select so borders correctly reflect simultaneous focus+hover across default, primary, secondary, success, warning, and danger variants.
  • Chores
    • Added a changeset to publish patch updates addressing bordered focus/hover styling.
    • Updated peer dependency requirement for the theme package in affected component packages.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Nov 11, 2025

🦋 Changeset detected

Latest commit: 0d704eb

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

This PR includes changesets to release 19 packages
Name Type
@heroui/button Patch
@heroui/select Patch
@heroui/input Patch
@heroui/theme Patch
@heroui/alert Patch
@heroui/autocomplete Patch
@heroui/calendar Patch
@heroui/date-picker Patch
@heroui/dropdown Patch
@heroui/form Patch
@heroui/input-otp Patch
@heroui/number-input Patch
@heroui/popover Patch
@heroui/snippet Patch
@heroui/table Patch
@heroui/react Patch
@heroui/checkbox Patch
@heroui/date-input Patch
@heroui/radio 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

@vercel
Copy link
Copy Markdown

vercel Bot commented Nov 11, 2025

@hasegawa-101 is attempting to deploy a commit to the HeroUI Inc Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 11, 2025

Walkthrough

Adds compound Tailwind selectors to bordered variants in Input, NumberInput, and Select theme components so focus border styles apply when both focus and hover are active. Converts some inputWrapper class definitions from strings to arrays to support the compound classes. Adds a changeset bumping related packages.

Changes

Cohort / File(s) Change Summary
Input theme
packages/core/theme/src/components/input.ts
Adds compound selector group-data-[focus=true]:data-[hover=true]:border-<color> and converts bordered inputWrapper classes from a string to an array to enable simultaneous focus+hover border rules for default and color-specific variants.
NumberInput theme
packages/core/theme/src/components/number-input.ts
Adds data-[focus=true]:data-[hover=true]:border-<color> compound classes and converts bordered inputWrapper definitions to arrays for default and color-specific variants to apply hover border color during focus.
Select theme
packages/core/theme/src/components/select.ts
Adds data-[focus=true]:data-[hover=true]:border-<color> compound selector entries to bordered variant trigger arrays (default and color-specific variants) so focus+hover border rules apply consistently.
Changeset
.changeset/popular-fishes-camp.md
Adds a changeset bumping @heroui/button, @heroui/select, @heroui/input, and @heroui/theme with description: "Fix bordered focus styles overridden by hover styles (#5585)".
Peer dependency bumps
packages/components/input/package.json, packages/components/number-input/package.json, packages/components/select/package.json
Updates peer dependency constraint for @heroui/theme to >=2.4.23 in these packages' package.json files.

Sequence Diagram(s)

(omitted — changes are styling/class composition only; no runtime/control-flow changes to diagram)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Review focus+hover selector syntax correctness across files (use of group- vs non-grouped selectors).
  • Verify all color variants covered: default, primary, secondary, success, warning, danger.
  • Check array/string conversions for trailing commas and class merging.
  • Confirm changeset and package.json peer dependency updates are consistent.

Possibly related PRs

  • fix(theme): focus ring styles #5531 — Modifies the same theme component files (input, number-input, select) and Tailwind class strings; likely related to styling/focus-outline changes.

Suggested reviewers

  • jrgarciadev

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: fixing bordered focus styles to take precedence over hover styles.
Description check ✅ Passed The PR description is comprehensive and follows the template with all required sections: closed issue, description, current behavior, new behavior, breaking change status, and additional information.
Linked Issues check ✅ Passed The PR successfully addresses all requirements from #5585: focus styles now take precedence over hover styles through compound selectors applied to bordered Input, NumberInput, and Select components across all color variants.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing the bordered focus/hover precedence issue; peer dependency updates align with the theme changes, and no unrelated modifications are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f307183 and 0d704eb.

📒 Files selected for processing (3)
  • packages/components/input/package.json (1 hunks)
  • packages/components/number-input/package.json (1 hunks)
  • packages/components/select/package.json (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: TypeScript
  • GitHub Check: Build
  • GitHub Check: Prettier
  • GitHub Check: Continuous Release
  • GitHub Check: ESLint
🔇 Additional comments (3)
packages/components/input/package.json (1)

39-39: Peer dependency update aligns with theme styling fixes.

The @heroui/theme peer dependency is correctly bumped to >=2.4.23 to enforce the minimum version that includes the hover-on-focus compound selector fixes referenced in the PR objectives.

Please confirm that @heroui/theme version 2.4.23 (or later) includes the corresponding compound Tailwind selectors (e.g., group-data-[focus=true]:data-[hover=true]:border-*) for the Input component as described in the PR objectives.

packages/components/number-input/package.json (1)

42-42: Peer dependency update is consistent with input and select packages.

The @heroui/theme peer dependency is correctly updated to >=2.4.23, matching the version constraint in the input and select packages, ensuring consistent access to the hover-on-focus styling fixes.

packages/components/select/package.json (1)

38-38: Peer dependency update enforces minimum theme version for Select fixes.

The @heroui/theme peer dependency is correctly updated to >=2.4.23. Note that this represents a 6-patch-level bump from >=2.4.17, aligning Select with the Input and NumberInput components to ensure all three have access to the hover-on-focus styling fixes.

Tip

📝 Customizable high-level summaries are now available!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide custom instructions to shape the summary (bullet lists, tables, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example:

"Create a concise high-level summary as a bullet-point list. Then include a Markdown table showing lines added and removed by each contributing author."


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 and usage tips.

Copy link
Copy Markdown
Member

@wingkwong wingkwong left a comment

Choose a reason for hiding this comment

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

could you please also paste the storybook code of your demonstration here.

@hasegawa-101
Copy link
Copy Markdown
Author

const Issue5585Template = (args) => {
  return (
    <div className="w-full max-w-6xl space-y-8">
      <div>
        <div className="space-y-8">
          <div>
            <h4 className="text-md font-semibold mb-4">Before vs After Comparison</h4>
            <div className="grid grid-cols-2 gap-8">
              {/* Before Fix - Simulating the bug */}
              <div className="space-y-4">
                <h5 className="text-sm font-semibold text-danger mb-2"> Before Fix (Bug)</h5>
                <p className="text-xs text-default-400 mb-4">
                  Hover styles override focus styles - border stays light
                </p>

                <Input
                  {...args}
                  color="default"
                  label="Default (Before)"
                  placeholder="Hover then focus"
                  variant="bordered"
                />

                <Input
                  {...args}
                  color="primary"
                  label="Primary (Before)"
                  placeholder="Hover then focus"
                  variant="bordered"
                />

                <Input
                  {...args}
                  color="danger"
                  label="Danger (Before)"
                  placeholder="Hover then focus"
                  variant="bordered"
                />
              </div>

              {/* After Fix - Default behavior */}
              <div className="space-y-4">
                <h5 className="text-sm font-semibold text-success mb-2"> After Fix (Correct)</h5>
                <p className="text-xs text-default-400 mb-4">
                  Focus styles take precedence - border becomes dark
                </p>

                <Input
                  {...args}
                  classNames={{
                    inputWrapper:
                      "group-data-[focus=true]:data-[hover=true]:border-default-foreground",
                  }}
                  color="default"
                  label="Default (After)"
                  placeholder="Hover then focus"
                  variant="bordered"
                />

                <Input
                  {...args}
                  classNames={{
                    inputWrapper: "group-data-[focus=true]:data-[hover=true]:border-primary",
                  }}
                  color="primary"
                  label="Primary (After)"
                  placeholder="Hover then focus"
                  variant="bordered"
                />

                <Input
                  {...args}
                  classNames={{
                    inputWrapper: "group-data-[focus=true]:data-[hover=true]:border-danger",
                  }}
                  color="danger"
                  label="Danger (After)"
                  placeholder="Hover then focus"
                  variant="bordered"
                />
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export const Issue5585HoverFocusPrecedence = {
  render: Issue5585Template,

  args: {
    ...defaultProps,
  },
};`

Here is the code to reproduce this in Storybook. Please check it on the canary branch.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Nov 11, 2025

Open in StackBlitz

@heroui/accordion

npm i https://pkg.pr.new/@heroui/accordion@5893

@heroui/alert

npm i https://pkg.pr.new/@heroui/alert@5893

@heroui/autocomplete

npm i https://pkg.pr.new/@heroui/autocomplete@5893

@heroui/avatar

npm i https://pkg.pr.new/@heroui/avatar@5893

@heroui/badge

npm i https://pkg.pr.new/@heroui/badge@5893

@heroui/breadcrumbs

npm i https://pkg.pr.new/@heroui/breadcrumbs@5893

@heroui/button

npm i https://pkg.pr.new/@heroui/button@5893

@heroui/calendar

npm i https://pkg.pr.new/@heroui/calendar@5893

@heroui/card

npm i https://pkg.pr.new/@heroui/card@5893

@heroui/checkbox

npm i https://pkg.pr.new/@heroui/checkbox@5893

@heroui/chip

npm i https://pkg.pr.new/@heroui/chip@5893

@heroui/code

npm i https://pkg.pr.new/@heroui/code@5893

@heroui/date-input

npm i https://pkg.pr.new/@heroui/date-input@5893

@heroui/date-picker

npm i https://pkg.pr.new/@heroui/date-picker@5893

@heroui/divider

npm i https://pkg.pr.new/@heroui/divider@5893

@heroui/drawer

npm i https://pkg.pr.new/@heroui/drawer@5893

@heroui/dropdown

npm i https://pkg.pr.new/@heroui/dropdown@5893

@heroui/form

npm i https://pkg.pr.new/@heroui/form@5893

@heroui/image

npm i https://pkg.pr.new/@heroui/image@5893

@heroui/input

npm i https://pkg.pr.new/@heroui/input@5893

@heroui/input-otp

npm i https://pkg.pr.new/@heroui/input-otp@5893

@heroui/kbd

npm i https://pkg.pr.new/@heroui/kbd@5893

@heroui/link

npm i https://pkg.pr.new/@heroui/link@5893

@heroui/listbox

npm i https://pkg.pr.new/@heroui/listbox@5893

@heroui/menu

npm i https://pkg.pr.new/@heroui/menu@5893

@heroui/modal

npm i https://pkg.pr.new/@heroui/modal@5893

@heroui/navbar

npm i https://pkg.pr.new/@heroui/navbar@5893

@heroui/number-input

npm i https://pkg.pr.new/@heroui/number-input@5893

@heroui/pagination

npm i https://pkg.pr.new/@heroui/pagination@5893

@heroui/popover

npm i https://pkg.pr.new/@heroui/popover@5893

@heroui/progress

npm i https://pkg.pr.new/@heroui/progress@5893

@heroui/radio

npm i https://pkg.pr.new/@heroui/radio@5893

@heroui/ripple

npm i https://pkg.pr.new/@heroui/ripple@5893

@heroui/scroll-shadow

npm i https://pkg.pr.new/@heroui/scroll-shadow@5893

@heroui/select

npm i https://pkg.pr.new/@heroui/select@5893

@heroui/skeleton

npm i https://pkg.pr.new/@heroui/skeleton@5893

@heroui/slider

npm i https://pkg.pr.new/@heroui/slider@5893

@heroui/snippet

npm i https://pkg.pr.new/@heroui/snippet@5893

@heroui/spacer

npm i https://pkg.pr.new/@heroui/spacer@5893

@heroui/spinner

npm i https://pkg.pr.new/@heroui/spinner@5893

@heroui/switch

npm i https://pkg.pr.new/@heroui/switch@5893

@heroui/table

npm i https://pkg.pr.new/@heroui/table@5893

@heroui/tabs

npm i https://pkg.pr.new/@heroui/tabs@5893

@heroui/toast

npm i https://pkg.pr.new/@heroui/toast@5893

@heroui/tooltip

npm i https://pkg.pr.new/@heroui/tooltip@5893

@heroui/user

npm i https://pkg.pr.new/@heroui/user@5893

@heroui/react

npm i https://pkg.pr.new/@heroui/react@5893

@heroui/system

npm i https://pkg.pr.new/@heroui/system@5893

@heroui/system-rsc

npm i https://pkg.pr.new/@heroui/system-rsc@5893

@heroui/theme

npm i https://pkg.pr.new/@heroui/theme@5893

@heroui/use-aria-accordion

npm i https://pkg.pr.new/@heroui/use-aria-accordion@5893

@heroui/use-aria-accordion-item

npm i https://pkg.pr.new/@heroui/use-aria-accordion-item@5893

@heroui/use-aria-button

npm i https://pkg.pr.new/@heroui/use-aria-button@5893

@heroui/use-aria-link

npm i https://pkg.pr.new/@heroui/use-aria-link@5893

@heroui/use-aria-modal-overlay

npm i https://pkg.pr.new/@heroui/use-aria-modal-overlay@5893

@heroui/use-aria-multiselect

npm i https://pkg.pr.new/@heroui/use-aria-multiselect@5893

@heroui/use-aria-overlay

npm i https://pkg.pr.new/@heroui/use-aria-overlay@5893

@heroui/use-callback-ref

npm i https://pkg.pr.new/@heroui/use-callback-ref@5893

@heroui/use-clipboard

npm i https://pkg.pr.new/@heroui/use-clipboard@5893

@heroui/use-data-scroll-overflow

npm i https://pkg.pr.new/@heroui/use-data-scroll-overflow@5893

@heroui/use-disclosure

npm i https://pkg.pr.new/@heroui/use-disclosure@5893

@heroui/use-draggable

npm i https://pkg.pr.new/@heroui/use-draggable@5893

@heroui/use-form-reset

npm i https://pkg.pr.new/@heroui/use-form-reset@5893

@heroui/use-image

npm i https://pkg.pr.new/@heroui/use-image@5893

@heroui/use-infinite-scroll

npm i https://pkg.pr.new/@heroui/use-infinite-scroll@5893

@heroui/use-intersection-observer

npm i https://pkg.pr.new/@heroui/use-intersection-observer@5893

@heroui/use-is-mobile

npm i https://pkg.pr.new/@heroui/use-is-mobile@5893

@heroui/use-is-mounted

npm i https://pkg.pr.new/@heroui/use-is-mounted@5893

@heroui/use-measure

npm i https://pkg.pr.new/@heroui/use-measure@5893

@heroui/use-pagination

npm i https://pkg.pr.new/@heroui/use-pagination@5893

@heroui/use-real-shape

npm i https://pkg.pr.new/@heroui/use-real-shape@5893

@heroui/use-ref-state

npm i https://pkg.pr.new/@heroui/use-ref-state@5893

@heroui/use-resize

npm i https://pkg.pr.new/@heroui/use-resize@5893

@heroui/use-safe-layout-effect

npm i https://pkg.pr.new/@heroui/use-safe-layout-effect@5893

@heroui/use-scroll-position

npm i https://pkg.pr.new/@heroui/use-scroll-position@5893

@heroui/use-ssr

npm i https://pkg.pr.new/@heroui/use-ssr@5893

@heroui/use-theme

npm i https://pkg.pr.new/@heroui/use-theme@5893

@heroui/use-update-effect

npm i https://pkg.pr.new/@heroui/use-update-effect@5893

@heroui/use-viewport-size

npm i https://pkg.pr.new/@heroui/use-viewport-size@5893

@heroui/aria-utils

npm i https://pkg.pr.new/@heroui/aria-utils@5893

@heroui/dom-animation

npm i https://pkg.pr.new/@heroui/dom-animation@5893

@heroui/framer-utils

npm i https://pkg.pr.new/@heroui/framer-utils@5893

@heroui/react-rsc-utils

npm i https://pkg.pr.new/@heroui/react-rsc-utils@5893

@heroui/react-utils

npm i https://pkg.pr.new/@heroui/react-utils@5893

@heroui/shared-icons

npm i https://pkg.pr.new/@heroui/shared-icons@5893

@heroui/shared-utils

npm i https://pkg.pr.new/@heroui/shared-utils@5893

@heroui/stories-utils

npm i https://pkg.pr.new/@heroui/stories-utils@5893

@heroui/test-utils

npm i https://pkg.pr.new/@heroui/test-utils@5893

commit: 0d704eb

@wingkwong wingkwong self-assigned this Nov 15, 2025
@wingkwong wingkwong added this to the v2.8.6 milestone Nov 15, 2025
@vercel
Copy link
Copy Markdown

vercel Bot commented Nov 15, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
heroui Ready Ready Preview Comment Nov 16, 2025 2:16pm
heroui-sb Ready Ready Preview Comment Nov 16, 2025 2:16pm

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.

[BUG] - Bordered components - hover styles take precedence over focus ones

2 participants