Skip to content

🐛 fix: let consumers retheme Sidebar options through className - #685

Merged
futjesus merged 2 commits into
mainfrom
fix/sidebar-option-theming-precedence
Jul 27, 2026
Merged

🐛 fix: let consumers retheme Sidebar options through className#685
futjesus merged 2 commits into
mainfrom
fix/sidebar-option-theming-precedence

Conversation

@futjesus

Copy link
Copy Markdown
Member

Problem

NavigationOption's hover and active colors lived in Sidebar.css, keyed on li[role='option']:

.konstruct-sidebar li[role='option'][data-active='true'] {
  background-color: var(--konstruct-sidebar-active-bg, #252a41);
  color: var(--konstruct-sidebar-active-color, #fff);
}

#677 removed that role="option" from the <li> (correctly — axe flagged it as an invalid role for an li outside a listbox). The selector stopped matching, so every consumer theming through --konstruct-sidebar-hover-bg / --konstruct-sidebar-active-bg silently fell back to the library default. In the Civo billing MFE the active item went from metal-800 to #252a41 with no code change on their side.

Why not just re-point the selector

Because the rule should never have won in the first place. .konstruct-sidebar li[role='option']:hover has specificity (0,2,2); a Tailwind utility has (0,1,0). Even while the selector matched, a consumer could not override the color through className — the library rule always beat it. The custom properties were the only escape hatch, and they were undiscoverable.

Change

Move option styling entirely into the cva variants, so cva provides the default and className can take over:

  • Drop the dead li[role='option'] rules from Sidebar.css. The variants already mirrored their layout, padding and anchor styling ([&>a]:p-2.5, [&>a]:flex, group-data-[mode=collapsed]/sidebar:justify-center, …)
  • Add the one rule that was not mirrored: shrink-0
  • Prefix the active classes with data-[active=true]: so tailwind-merge sees the same modifier as a consumer override and drops the default instead of shipping both and leaving it to stylesheet source order
  • Remove the dead text-black (already dropped by tailwind-merge in favour of text-kubefirst-dark-blue-300) and a duplicated font-normal

Consumer migration

 <Sidebar
-  style={{
-    '--konstruct-sidebar-hover-bg': 'var(--color-metal-800)',
-    '--konstruct-sidebar-active-bg': 'var(--color-metal-800)',
-  }}
 >
   <Sidebar.NavigationOption
     isActive={isActive(route)}
-    className="dark:text-metal-400"
+    className={cn(
+      'dark:text-metal-400',
+      'hover:bg-metal-800 hover:text-metal-50',
+      'data-[active=true]:bg-metal-800 data-[active=true]:text-metal-50',
+    )}
   >

--konstruct-sidebar-logo-padding-* is unaffected — those rules key off [data-konstruct-sidebar-logo], not role.

Breaking change

--konstruct-sidebar-hover-bg, --konstruct-sidebar-hover-color, --konstruct-sidebar-active-bg and --konstruct-sidebar-active-color no longer do anything. They have been broken since #677, so this documents reality rather than removing working behavior.

Tests

Two new cases pin both directions — a className override wins, and the default still applies when nothing is passed. Full suite green (566 tests, 47 files), plus lint, types and prettier.

Verified against the real consumer: built locally, copied into the Civo billing MFE and confirmed the active item renders metal-800 again.

Also included

A 📝 docs commit tightening the CLAUDE.md comments rule — change rationale belongs in the commit message, not in the source, and the rule now says so explicitly for CSS too.

futjesus added 2 commits July 27, 2026 12:45
The NavigationOption hover/active colors lived in Sidebar.css keyed on
`li[role='option']`. #677 dropped that invalid role from the `<li>`, so the
rules stopped matching and every consumer theming through the
`--konstruct-sidebar-hover-bg` / `--konstruct-sidebar-active-bg` custom
properties silently fell back to the library default.

Rather than re-point the selector, move the styling into the cva variants:
a descriptor selector like `.konstruct-sidebar li:hover` (0,2,2) outranks a
single utility class (0,1,0), so a consumer could never win through
`className` even when the selector did match.

- Drop the dead `li[role='option']` rules from Sidebar.css; the variants
  already cover the layout, padding and anchor styling they mirrored
- Add the one rule that was not mirrored (`shrink-0`) to the variants
- Prefix the active classes with `data-[active=true]:` so tailwind-merge
  detects the conflict against a consumer override and drops the default
- Remove the now-unused `text-black` and duplicated `font-normal`
- Cover both directions with tests: an override wins, no override keeps the
  default

BREAKING CHANGE: `--konstruct-sidebar-hover-bg`, `--konstruct-sidebar-hover-color`,
`--konstruct-sidebar-active-bg` and `--konstruct-sidebar-active-color` no longer
do anything. Pass the colors to `NavigationOption` via `className` instead:
`hover:bg-metal-800 data-[active=true]:bg-metal-800`.
@futjesus
futjesus merged commit 78abade into main Jul 27, 2026
2 checks passed
@futjesus
futjesus deleted the fix/sidebar-option-theming-precedence branch July 27, 2026 16:50
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