Skip to content

feat(ui): add Alert, DropdownMenu, Tooltip primitives and ThemeProvider with dark mode toggle - #141

Merged
codebestia merged 3 commits into
ShadeProtocol:mainfrom
0xDeon:feat/ui-primitives-theme
Jul 31, 2026
Merged

feat(ui): add Alert, DropdownMenu, Tooltip primitives and ThemeProvider with dark mode toggle#141
codebestia merged 3 commits into
ShadeProtocol:mainfrom
0xDeon:feat/ui-primitives-theme

Conversation

@0xDeon

@0xDeon 0xDeon commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the three missing shared UI primitives (Alert, DropdownMenu, Tooltip) and the user-facing dark mode toggle.

Issues

Closes #90
Closes #92
Closes #96
Closes #104


Alert#90

src/components/ui/alert.tsx exports Alert, AlertTitle, and AlertDescription, with default and destructive variants via cva, following the existing button.tsx conventions (forwardRef, data-slot, cn).

No visual regression. The destructive variant reproduces the hand-written classes exactly:

rounded-lg border border-destructive/30 bg-destructive/10 p-3 text-sm text-destructive

Two details worth flagging for review:

  • AlertDescription deliberately sets no line-height. An earlier draft applied leading-6, which would have silently changed the error text from 20px to 24px leading at text-sm — a real regression on multi-line errors.
  • The per-call spacing utilities (mt-5 in register, mb-4 in sign-in) are passed through className rather than baked into the variant, so both screens keep their original layout.

Refactored:

  • src/app/register/register-client.tsx
  • src/app/sign-in/sign-in-client.tsx

DropdownMenu#92

src/components/ui/dropdown-menu.tsx built on @radix-ui/react-dropdown-menu (already a dependency, no install needed).

Exports DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, and DropdownMenuLabel. Content is portalled, themed with the popover tokens, and animated with the tw-animate-css classes already imported in globals.css.

Tooltip#96

src/components/ui/tooltip.tsx built on @radix-ui/react-tooltipthe one new dependency in this PR.

TooltipProvider wraps a 200ms default delay and is mounted once in src/app/layout.tsx, inside ThemeProvider, so any subtree can use tooltips without its own provider.

Theme toggle — #104

The repo already had a working ThemeProvider at src/components/ThemeProvider.tsx, mounted in the root layout — it toggles .dark and persists to localStorage under shade:theme. What was missing was the toggle button.

  • src/components/theme-provider.tsx — re-exports the existing provider at the path the issue specifies. Defining a second provider here would fork theme state into two independent copies, so this re-exports instead. The root layout now imports from this path.
  • src/components/ui/theme-toggle.tsxThemeToggle button using lucide-react's Sun/Moon, with an aria-label that tracks the current mode.

Note: lucide-react is path-mapped in tsconfig.json to the local shim at src/lib/lucide-react.tsx; both Sun and Moon already exist there, so no shim additions were needed.

Tests

16 new cases across 4 files, written against the acceptance criteria:

File Covers
alert.test.tsx alert role, variant classes match the original error box, caller className preserved
dropdown-menu.test.tsx opens anchored to trigger (aria-controls/aria-expanded), closes on Escape, closes on outside click, onSelect fires
tooltip.test.tsx shows full value on hover, shows on keyboard focus
theme-toggle.test.tsx toggles .dark instantly, persists to localStorage, restores across remount (reload), a11y labels

The outside-click test dispatches pointerDown/mouseDown directly rather than using user.click. Radix sets pointer-events: none on the body while an open menu traps interaction, which user-event refuses to click through; the dispatched sequence is what Radix actually listens for. This is commented inline.

Verification

Check Result
tsc --noEmit Clean
next build ✓ Compiled successfully
New tests 16/16 passing
Lint on changed files Clean
Prettier Formatted

Two pre-existing failures, confirmed not from this PR

Both were reproduced on a clean upstream/main with this branch's changes stashed:

  1. next build fails at its lint gate on 4 errors in files this PR does not touch — src/app/payment/[id]/page.tsx, src/components/invoices/InvoiceRowActions.test.tsx, src/components/profile/ProfileDetailsForm.test.tsx, src/components/ui/api-key-reveal.tsx. Compilation itself succeeds. Baseline produces the identical 4 errors.
  2. billing-plan-form.test.tsx times out at 5000ms under full-suite parallel load. It passes when run alone, both with and without this branch's changes, and fails on baseline under the full suite. Looks like a timeout that needs raising, unrelated to this work.

Left both alone as out of scope — happy to fix in a follow-up if you'd like them bundled.

0xDeon added 2 commits July 30, 2026 02:00
…oggle

Adds the shared UI primitives that table row actions, topbar menus, and
truncated-address displays need, plus the user-facing theme switch.

Alert (ShadeProtocol#90)
- src/components/ui/alert.tsx with Alert, AlertTitle, AlertDescription
- default and destructive variants via cva, matching button.tsx conventions
- destructive variant reproduces the hand-written error box classes exactly,
  so refactoring both auth screens carries no visual change
- AlertDescription adds no line-height of its own to preserve text-sm leading
- register-client.tsx and sign-in-client.tsx now use Alert, keeping their
  mt-5 / mb-4 spacing via className

DropdownMenu (ShadeProtocol#92)
- src/components/ui/dropdown-menu.tsx on @radix-ui/react-dropdown-menu
- DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem,
  DropdownMenuSeparator, DropdownMenuLabel
- portalled content with popover tokens and tw-animate-css transitions

Tooltip (ShadeProtocol#96)
- src/components/ui/tooltip.tsx on @radix-ui/react-tooltip (new dependency)
- TooltipProvider defaults to a 200ms delay and is mounted once in the
  root layout so any subtree can use tooltips

Theme toggle (ShadeProtocol#104)
- src/components/theme-provider.tsx re-exports the existing ThemeProvider at
  the path the issue specifies; a second provider would fork theme state
- src/components/ui/theme-toggle.tsx toggles via useTheme with Sun/Moon icons
  and an aria-label that tracks the current mode

Tests: 16 new cases covering the acceptance criteria - menu anchoring,
Escape and outside-click dismissal, onSelect firing, tooltip on hover and
keyboard focus, dark class toggling, and localStorage persistence across
remount.
@drips-wave

drips-wave Bot commented Jul 30, 2026

Copy link
Copy Markdown

@0xDeon Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@codebestia
codebestia merged commit adf60d5 into ShadeProtocol:main Jul 31, 2026
1 check failed
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.

Implement ThemeProvider and Dark Mode Toggle Create Tooltip Component Create DropdownMenu Component Create Alert Component

2 participants