Skip to content

feat(ui): button + icon-button 4-tier rewrite (slice 04, issue #440)#459

Merged
Astro-Han merged 3 commits into
devfrom
worktree-claude+ui-slice-04-buttons
May 5, 2026
Merged

feat(ui): button + icon-button 4-tier rewrite (slice 04, issue #440)#459
Astro-Han merged 3 commits into
devfrom
worktree-claude+ui-slice-04-buttons

Conversation

@Astro-Han

@Astro-Han Astro-Han commented May 5, 2026

Copy link
Copy Markdown
Owner

Summary

Slice 04 of eleven in issue #440 (PawWork UI carve-out). Scope: Button and IconButton component family only.

Token additions (packages/ui/src/styles/theme.css, packages/ui/src/theme/themes/pawwork.json):

  • --brand-danger: #d24a3a and --brand-danger-hover: #b53c2e added to the STANDARDS token set (light-only; same value in dark, registered in SAME_IN_DARK).

Button rewrite (packages/ui/src/components/button.{tsx,css}):

  • Removes size prop. Single canonical height 28px, radius-md.
  • Adds danger variant (fourth tier alongside primary/secondary/ghost).
  • Active state unified across all variants to --surface-interactive-base.
  • Focus-visible ring inlined per DESIGN.md §137: box-shadow: 0 0 0 1px #FF5910, 0 0 0 3px rgba(255,89,16,0.20).
  • Disabled via opacity: 0.45 + cursor: not-allowed.
  • Secondary hover via background-image: linear-gradient(var(--hover-overlay), var(--hover-overlay)) — preserves --ring-base box-shadow while applying the overlay.
  • Ghost hover via background-color: var(--hover-overlay).

IconButton rewrite (packages/ui/src/components/icon-button.{tsx,css}):

  • Removes size, iconSize, and variant props. Ghost-only, fixed 24×24, radius-sm, inner icon 16px.
  • Makes aria-label required in the TypeScript interface (was implicit).
  • Preserves .titlebar-icon 32×24 exception (DESIGN.md §172, limited to 4 uses in titlebar.tsx).

State matrix tests (packages/ui/test/button-states.test.ts, icon-button-states.test.ts):

  • CSS-source parsing tests covering API contract, canonical size, all 4 Button variants, active/focus-visible/disabled states, and IconButton ghost behaviour + titlebar exception.

Consumer migration (packages/app/src/, packages/ui/src/components/line-comment.tsx):

  • Removes Button size= from 39 call sites; removes IconButton variant=/size=/iconSize= from 9 call sites.
  • Adds missing aria-label to two IconButton usages in settings-keybinds and settings-models.

Why

Slices 01–03 settled the token layer, typography wiring, and motion primitives. Slice 04 brings the button primitive — the most widely consumed component family (54 import sites) — into alignment with DESIGN.md §157-172 and §305-313. Buttons are a dependency of inputs (slice 05), popover/tooltip (slice 06), and dialog (slice 07), so the four-tier API and correct state tokens must be stable before those slices begin.

Related Issue

Refs #440 (umbrella). This PR is slice 04 of eleven.

Human Review Status

Pending. A human should make the final merge decision after reviewing the final diff and verification evidence, and after running dev:desktop for the visual smoke check (screenshots pending manual run — see How To Verify).

Review Focus

  • Danger variant: confirm --brand-danger / --brand-danger-hover are visually readable on both light and dark surfaces (warm dark, #221f1c background).
  • Secondary hover: uses background-image: linear-gradient(var(--hover-overlay), var(--hover-overlay)) as an overlay on --surface-base. This keeps box-shadow: var(--ring-base) unaffected. Verify that the hover state is visible and the ring does not disappear on hover.
  • Focus-visible ring: inlined 0 0 0 1px #FF5910, 0 0 0 3px rgba(255,89,16,0.20) per DESIGN.md mandate. Confirm ring is visible on Tab navigation for all 4 variants.
  • Disabled opacity: switched from per-variant background-color to opacity: 0.45. Verify disabled buttons look consistent and are clearly inert.
  • IconButton aria-label required: this is a TypeScript-level enforcement. Existing callers that had aria-label are unaffected; two that lacked it received appropriate labels ("Clear filter").
  • .titlebar-icon exception: titlebar.tsx uses <Button class="titlebar-icon w-8 h-6 ..."> (Tailwind-sized, not <IconButton>). The icon-button.css .titlebar-icon rule targets [data-component="icon-button"], so no conflict — verify the four titlebar buttons still render at 32×24.
  • Consumer migration scope: 20 files changed in app/pages and app/components. No logic changes — only size=/variant=/iconSize= prop removal. Spot-check dialog-delete-session.tsx, session-permission-dock.tsx, and line-comment.tsx.

Risk Notes

  • size prop removal is a source-level breaking API change absorbed entirely within this PR (39 call sites migrated). Any consumer outside packages/app and packages/ui that imports Button/IconButton from @opencode-ai/ui and passes size would have a TypeScript error — but this package is internal-only and there are no external consumers.
  • IconButton no longer accepts variant prop. The only variants previously used were ghost (now the sole implicit tier) and secondary (removed). No primary/danger IconButton was used anywhere.
  • --hover-overlay / --hover-overlay-warm tokens (from slice 03) are now first consumed here. Secondary and ghost button hover on cream/sidebar surfaces will use --hover-overlay; warm-surface icon buttons will consume --hover-overlay-warm in later slices.
  • The aria-label enforcement does not break runtime — it is TypeScript-only. Components without aria-label still render; the requirement is a code-quality gate.

Changed-file boundary check

All changes are inside packages/ui/src/components/, packages/ui/src/styles/, packages/ui/src/theme/, packages/ui/test/, and packages/app/src/. No files in packages/opencode/, packages/desktop-electron/, or packages/ui/src/theme/themes/ other than pawwork.json are touched.

packages/ui/src/styles/theme.css          — new tokens
packages/ui/src/theme/themes/pawwork.json — token mirror
packages/ui/test/theme-parity.test.ts     — SAME_IN_DARK update
packages/ui/src/components/button.{tsx,css}
packages/ui/src/components/icon-button.{tsx,css}
packages/ui/test/button-states.test.ts    — new
packages/ui/test/icon-button-states.test.ts — new
packages/ui/src/components/line-comment.tsx — consumer migration
packages/app/src/components/*.tsx (12 files) — consumer migration
packages/app/src/pages/**/*.tsx (8 files)    — consumer migration

How To Verify

token parity:
  bun --cwd packages/ui test test/theme-parity.test.ts
  133 pass, 0 fail (up from 131; 2 new brand-danger assertions)

button state matrix:
  bun --cwd packages/ui test test/button-states.test.ts
  20 pass, 0 fail

icon-button state matrix:
  bun --cwd packages/ui test test/icon-button-states.test.ts
  16 pass, 0 fail

ui typecheck:
  bun --cwd packages/ui run typecheck (tsgo --noEmit)
  0 errors

app typecheck:
  bun --cwd packages/app run typecheck (tsgo -b)
  0 errors

app unit tests:
  bun --cwd packages/app run test:unit
  907 pass, 0 fail (137 files)

visual smoke (manual — dev:desktop):
  Pending. Reviewer should run bun run dev:desktop and check:
  1. Dialog footer: secondary (cancel) + primary/danger (confirm) contrast
  2. Sidebar IconButton rows: hover state visible
  3. Titlebar 4 IconButtons: still 32×24, no shape change
  4. Ghost button hover: rgba overlay visible on white and cream surfaces
  5. Secondary button: hover overlay visible, ring-base border preserved
  6. Keyboard Tab: focus ring visible on all 4 variants
  7. Disabled: opacity-dimmed, not clickable

Screenshots or Recordings

Visual smoke check completed manually via bun run dev:desktop. All 7 items passed (dialog footer button tiers, sidebar IconButton hover, titlebar 32×24 shape, ghost/secondary hover overlay, focus ring on Tab, disabled opacity). No screenshots — results recorded in PR comment.

Checklist

  • Human review status is stated above as pending, approved, or not required
  • I linked the related issue, or stated why there is no issue
  • This PR has type, scope, and priority labels, or I requested maintainer labeling
  • I described the review focus and any meaningful risks
  • I listed the relevant verification steps and the key result for each
  • I did not introduce unrelated refactors, dependencies, generated files, or file changes beyond the stated scope
  • I manually checked visible UI or copy changes when needed, with screenshots or recordings
  • I considered macOS and Windows impact for desktop, packaging, updater, signing, paths, shell, or permissions changes
  • I called out docs, release notes, dependencies, permissions, credentials, deletion behavior, generated content, or local file changes when relevant
  • I reviewed the final diff for unrelated changes and suspicious dependency changes
  • I am targeting dev, and my PR title and commit messages use Conventional Commits in English

@coderabbitai

coderabbitai Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@Astro-Han has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 26 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e42a23fd-4b92-4578-a53b-8cd46fecfa34

📥 Commits

Reviewing files that changed from the base of the PR and between 36e2f24 and 0f8b73d.

📒 Files selected for processing (28)
  • packages/app/src/components/dialog-connect-provider.tsx
  • packages/app/src/components/dialog-connect-websearch.tsx
  • packages/app/src/components/dialog-custom-provider.tsx
  • packages/app/src/components/dialog-delete-session.tsx
  • packages/app/src/components/dialog-edit-project.tsx
  • packages/app/src/components/dialog-release-notes.tsx
  • packages/app/src/components/dialog-select-server.tsx
  • packages/app/src/components/settings-general.tsx
  • packages/app/src/components/settings-keybinds.tsx
  • packages/app/src/components/settings-models.tsx
  • packages/app/src/components/settings-providers.tsx
  • packages/app/src/components/settings-worktree-row.tsx
  • packages/app/src/pages/error.tsx
  • packages/app/src/pages/home.tsx
  • packages/app/src/pages/layout.tsx
  • packages/app/src/pages/layout/pawwork-sidebar.tsx
  • packages/app/src/pages/session/composer/session-permission-dock.tsx
  • packages/app/src/pages/session/composer/session-question-dock.tsx
  • packages/ui/src/components/button.css
  • packages/ui/src/components/button.tsx
  • packages/ui/src/components/icon-button.css
  • packages/ui/src/components/icon-button.tsx
  • packages/ui/src/components/line-comment.tsx
  • packages/ui/src/styles/theme.css
  • packages/ui/src/theme/themes/pawwork.json
  • packages/ui/test/button-states.test.ts
  • packages/ui/test/icon-button-states.test.ts
  • packages/ui/test/theme-parity.test.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-claude+ui-slice-04-buttons

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.

@Astro-Han Astro-Han added enhancement New feature or request ui Design system and user interface P2 Medium priority labels May 5, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request standardizes the Button and IconButton components by enforcing a single canonical size and removing deprecated size, iconSize, and variant props across the codebase. It introduces a new danger variant for the Button component, updates the theme with corresponding danger color tokens, and makes aria-label mandatory for IconButton. Additionally, new test suites are added to verify the component state matrices and API contracts. I have no feedback to provide as there were no review comments to assess.

@Astro-Han

Copy link
Copy Markdown
Owner Author

Visual smoke check passed (dev:desktop, 2026-05-05). All 7 golden-path items verified. No regressions introduced by this slice.

Astro-Han added 3 commits May 5, 2026 23:45
… issue #440)

Adds the danger button colour pair to the STANDARDS token set.
--brand-danger/#d24a3a and --brand-danger-hover/#b53c2e are light-only
(same value in dark, added to SAME_IN_DARK in theme-parity.test.ts).
Button: removes size prop, adds danger variant. Single height 28px,
radius-md. Active state unified to --surface-interactive-base.
Focus-visible ring inlined per DESIGN.md (0 0 0 1px #FF5910, outer glow).
Disabled via opacity:0.45. Secondary hover via background-image overlay.

IconButton: removes size/iconSize/variant props. Ghost-only, fixed 24×24,
radius-sm. Preserves .titlebar-icon 32×24 exception (DESIGN.md §172).

Adds button-states.test.ts and icon-button-states.test.ts with full state
matrix coverage (API contract, size, 4 variants, active, focus, disabled).
…Button consumers

Removes Button size= prop from 39 call sites across packages/app and
packages/ui. Removes IconButton variant=/size=/iconSize= props from 9
call sites. Adds missing aria-label to two IconButton usages that lacked
it (settings-keybinds, settings-models). All changes are mechanical prop
removal with no behaviour change.
@Astro-Han Astro-Han force-pushed the worktree-claude+ui-slice-04-buttons branch from b4c764f to 0f8b73d Compare May 5, 2026 15:46
@Astro-Han Astro-Han merged commit f8a9213 into dev May 5, 2026
22 checks passed
@Astro-Han Astro-Han deleted the worktree-claude+ui-slice-04-buttons branch May 5, 2026 15:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request P2 Medium priority ui Design system and user interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant