Skip to content

feat(item-themes): merge neutral/secondary into clear/outline + isSel…#1179

Open
tenphi wants to merge 17 commits into
mainfrom
feat-item-themes-migration
Open

feat(item-themes): merge neutral/secondary into clear/outline + isSel…#1179
tenphi wants to merge 17 commits into
mainfrom
feat-item-themes-migration

Conversation

@tenphi
Copy link
Copy Markdown
Member

@tenphi tenphi commented May 19, 2026

…ected

  • Drop neutral and secondary from Button, ButtonSplit, Item, ItemAction, ItemBadge, ItemButton, and RadioGroup.buttonType. Migrate neutralclear, clearclear + isSelected, secondaryoutline + isSelected.
  • Default type for ItemAction / ItemBadge / ItemButton flips from neutral to clear. ItemBadge widens to 'primary' | 'outline' | 'clear' | 'link' and supports isSelected.
  • Rename Item.icon / ItemAction.icon / ItemBadge.icon special value 'checkbox''checkmark'; matching style modifier renamed too.
  • Realign DEFAULT_CLEAR_STYLES with DEFAULT_ITEM_STYLES so the non-selected pressed state and selected fill ramp behave consistently between the two.
  • Restructure state matrices in Button / ItemAction / Item / ItemButton / ButtonSplit stories to render selected variants in dedicated rows with consistent labels, switch row headers to Title, and fix MenuSelectableCheckboxes to pass icon="checkmark" explicitly.

Note

Medium Risk
Medium risk due to breaking API changes (removing neutral/secondary, renaming icon="checkbox" to checkmark, and special-theme token renames) plus broad updates to shared item-themes styles that affect many components’ visuals and interaction states.

Overview
Introduces a new outline-2 type across Button/Item-based components (and Select/Picker/FilterPicker/Menu triggers), using a #surface-3 base so outlined controls remain distinct on #surface-2; special theme explicitly falls back to outline.

Breaking: removes neutral and secondary from multiple type APIs (and RadioGroup.buttonType), expressing those looks via clear/outline combined with isSelected, and flips default types for ItemAction/ItemBadge/ItemButton to clear. Also renames the special icon value and modifier from checkboxcheckmark and updates docs/stories/tests accordingly.

Refactors src/data/item-themes.ts to use palette tokens directly, smooths fill transitions by standardizing fill layer shapes, fixes special theme fill-map value collisions, and unifies the primary fill ramp across themes (including special palette/token renames). Updates RadioGroup button/tabs UX with ButtonSplit-like grouping, improved selected-border rendering, keyboard-only group focus ring, and correct disabled-selected tabs styling.

Reviewed by Cursor Bugbot for commit 71167b3. Bugbot is set up for automated code reviews on this repo. Configure here.

…ected

- Drop `neutral` and `secondary` from `Button`, `ButtonSplit`, `Item`,
  `ItemAction`, `ItemBadge`, `ItemButton`, and `RadioGroup.buttonType`.
  Migrate `neutral` → `clear`, `clear` → `clear` + `isSelected`,
  `secondary` → `outline` + `isSelected`.
- Default `type` for `ItemAction` / `ItemBadge` / `ItemButton` flips
  from `neutral` to `clear`. `ItemBadge` widens to
  `'primary' | 'outline' | 'clear' | 'link'` and supports `isSelected`.
- Rename `Item.icon` / `ItemAction.icon` / `ItemBadge.icon` special
  value `'checkbox'` → `'checkmark'`; matching style modifier renamed
  too.
- Realign `DEFAULT_CLEAR_STYLES` with `DEFAULT_ITEM_STYLES` so the
  non-selected pressed state and selected fill ramp behave consistently
  between the two.
- Restructure state matrices in Button / ItemAction / Item / ItemButton
  / ButtonSplit stories to render selected variants in dedicated rows
  with consistent labels, switch row headers to `Title`, and fix
  `MenuSelectableCheckboxes` to pass `icon="checkmark"` explicitly.

Co-authored-by: Cursor <cursoragent@cursor.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 19, 2026

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

Project Deployment Actions Updated (UTC)
cube-ui-kit Ready Ready Preview, Comment May 21, 2026 4:35pm

Request Review

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 19, 2026

🦋 Changeset detected

Latest commit: 71167b3

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

This PR includes changesets to release 1 package
Name Type
@cube-dev/ui-kit Minor

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 19, 2026

📦 NPM canary release

Deployed canary version 0.0.0-canary-fca14eb.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 19, 2026

🏋️ Size limit report

Name Size Passed?
All 394.27 KB (+0.08% 🔺) Yes 🎉
Tree shaking (just a Button) 112.58 KB (+0.1% 🔺) Yes 🎉

Click here if you want to find out what is changed in this build

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 19, 2026

🧪 Storybook is successfully deployed!

Comment thread src/components/content/Layout/Layout.stories.tsx
Comment thread src/components/fields/FilterPicker/FilterPicker.stories.tsx Outdated
`outline-2` mirrors `outline` but paints over `#surface-3` instead of
`#surface-2`, so it stays visually distinct when placed inside a
`#surface-2` container. Wired into Button (props, ButtonVariant, Tasty
variants map, button-split margin rule) with theme constants for
default/danger/success/warning/note. Special theme has no `outline-2`
counterpart and `theme="special" + type="outline-2"` falls back to
`outline`. Button states stories now render the `outline-2` rows
inside a `#surface-2` card and the special-theme matrix filters it
out.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread src/components/overlays/Toast/Toast.stories.tsx
Comment thread src/components/fields/FilterPicker/FilterPicker.stories.tsx Outdated
Missed during the initial migration sweep:
- Layout.stories: type="neutral" → type="clear"
- RadioGroup.stories: buttonType="neutral" → "clear", argTypes updated
- FilterPicker.stories: Badge type="neutral" → "disabled", argTypes
  updated
- Select.stories: argTypes options updated
- Toast.stories: type="secondary" → type="outline"

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread src/components/actions/ButtonSplit/ButtonSplit.stories.tsx
Comment thread src/components/fields/RadioGroup/RadioGroup.stories.tsx Outdated
Group `RadioGroup type="button"` like `ButtonSplit`: zero gap, shared corner
radius, overlapping borders, and the selected button bumped above siblings via
z-index so its brand border reads from all four sides. Hover and focus-visible
bump higher still so they always sit on top of the selected highlight.

Replace the alpha-blended `#<theme>-text.15` outline border (which double-stacked
at every overlap into a darker stripe) with a new opaque `#<theme>-border` token.
The token re-resolves the existing neutral `border` ramp per colored theme at
`saturation: 0.5` via `TINTED_SURFACE_OVERRIDE`, so each theme gets a subtly
hue-tinted border without any extra palette bookkeeping.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread src/data/item-themes.ts Outdated
Each `fill` state map now uses the same 2-layer shape (opaque base + tint
overlay) across non-selected, selected, and disabled states. tasty renders
1-color and 2-color fills via different CSS properties (`background-color`
alone vs. `background-color` + `--tasty-second-fill-color` +
`background-image: linear-gradient(...)`); transitioning between shapes
caused the gradient overlay to snap on/off while `background-color`
interpolated, briefly exposing the base layer and producing a visible flash
(noticeable in `RadioGroup type="button"` selection toggles and in primary
buttons toggling `isDisabled` on form submit).

The brand-tinted overlays now composite over each variant's own base
(`#surface` / `#surface-2` / `#surface-3` / `#special-surface`) instead of
the body surface; the resulting visual shift is sub-1 OKHSL point and
imperceptible side-by-side. Also folds `RadioGroup`'s tab-selected element
into the base `RadioButtonElement` via mod-driven `selected & tabs` state
rules so the button-mode grouping uses the simpler `!tabs &` selectors
instead of the `@parent(radio-button-group, >)` chain.

Co-authored-by: Cursor <cursoragent@cursor.com>
Comment thread src/components/fields/RadioGroup/Radio.tsx
… to Item

- Migrated every color in `src/data/item-themes.ts` to Glaze palette tokens,
  removing the legacy alias indirection from `src/tokens/colors.ts`
  (`#dark` → `#surface-text`, `#dark-02` → `#surface-text-soft`,
  `#primary-text` → `#primary-accent-text`, `#primary-hover` →
  `#primary-accent-surface-hover`, `#primary` brand fill →
  `#primary-accent-surface`, `#light` → `#surface-3`, `#clear` →
  `transparent`, plus matching ramps for danger / success / warning / note).
  Resolved values are unchanged.
- Wired `outline-2` into `Item` (previously it only existed on `Button`),
  with the same `theme="special"` → `outline` fallback as `Button`. Every
  component that flows through `Item` (`ItemButton`, `Select`,
  `FilterPicker`, `Picker`, `Menu`, `RadioGroup`, etc.) now renders
  `type="outline-2"` correctly. Updated the corresponding `*.docs.mdx`
  files and `Select` / `FilterPicker` story argTypes.
- Removed migration duplicates left over after `secondary → outline` /
  `neutral → clear` (`Button.Split` Variants story + docs example,
  `RadioGroup` `CustomButtonTypes` story).
- `Radio.tsx`: gated the item-shadow on `tabs & selected`, gave the
  wrapper a focus-visible outline ring + radius so keyboard focus is
  visible on the radio label.

Co-authored-by: Cursor <cursoragent@cursor.com>
`ItemAction` and `ItemBadge` only ship variants for `primary` / `outline` /
`clear`. Without the mapping, an `Item` / `ItemButton` with `type="outline-2"`
would pass `'outline-2'` through to its children, which then resolved to a
non-existent `<theme>.outline-2` variant and rendered without any fill,
border or color styles.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 200962d. Configure here.

Comment thread src/components/actions/Button/Button.stories.tsx Outdated
Co-authored-by: Cursor <cursoragent@cursor.com>
…undant classic-wrapper ring

- Classic `RadioGroup` (`type="radio"`): the per-radio wrapper outline
  was driven by `:focus-within`, so it also fired on mouse-click on the
  label and duplicated the inner radio-circle focus ring. Drop the
  wrapper outline entirely; the inner circle's `focused`-mod ring
  remains the single, keyboard-gated indicator.
- Button / Tabs `RadioGroup` (`type="button"` / `type="tabs"`): there
  was no group-level focus ring at all — the per-item `Item` themes
  (`DEFAULT_OUTLINE_STYLES`, etc.) only swap the `border` color on
  focus, which is easy to miss when the item is also selected. Add a
  group-container outline gated on `focused & (button | tabs)`, driven
  by React Aria's `useFocusRing({ within: true })` reading
  `isFocusVisible` (not `isFocused`) so mouse clicks don't trigger it.

Co-authored-by: Cursor <cursoragent@cursor.com>
The `selected & tabs` override in `RadioButtonElement` forced
`fill: '#surface'`, `color: '#dark'`, and `shadow: '$item-shadow'`
regardless of the disabled state, so a disabled selected tab still
rendered as a fully-opaque white pill with dark text and a live
shadow — visually identical to an interactive one.

Add `tabs & selected & disabled` branches that drop the shadow
(`false`), bleed the surface (`#surface.6`) so the group's
`#surface-4` chip shows through, and mute the label (`#dark.3`) to
match the existing disabled unselected tabs.

Co-authored-by: Cursor <cursoragent@cursor.com>
tenphi and others added 2 commits May 21, 2026 12:54
Move every non-item, non-card variant (primary, outline, outline-2,
clear, link) across all themes from `border`-based focus to `outline`-
based focus, using a single color per ring family:

- Default / danger / success / warning / note: `#primary-accent-text`
- Special: `#special-accent-text` (fixed-mode, non-inverting in dark)

Item types keep their existing fill-based soft focus, and card types
have no focus styling. Validation borders and selected/disabled border
colors are unchanged. Theme-tinted *pressed* borders are preserved on
primary variants.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add `outline-2` to argTypes / story variants and surrounding docs for
Button, ButtonSplit, ItemButton, Item, Select, and RadioGroup, with a
`#surface-2`-tinted wrapper where the contrast against the default
surface ladder needs to be demonstrated. Refines existing comments to
describe outline-2 as "fill base = #surface-3, designed to sit on
#surface-2 containers" rather than the older "paints over"
phrasing.

Co-authored-by: Cursor <cursoragent@cursor.com>
`Block` lives at `src/components/Block.tsx`, not under `content/`. The wrong
relative import broke the Storybook build with `Could not resolve
'../../content/Block'`.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…t-fill→accent-surface

Migrate DANGER/SUCCESS/WARNING/NOTE/SPECIAL primary fills to the same
monotonic `accent-surface` → `-2` → `-3` ramp used by the default theme,
giving press feedback and consistent contrast steps in both schemes. Add
`accent-surface-2`/`-3` to the special theme palette and rename its
`accent-fill*` tokens to `accent-surface*` to match the colored-theme
naming convention.

Co-authored-by: Cursor <cursoragent@cursor.com>
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