feat(e10): migrate the notification, message, auth and Plex action buttons

Second sweep, 34 more sites: NotificationsPanel (14 -- eight identical
accent text links, three ghost icon buttons, two ghost text actions),
Welcome's five auth links, KeyGate, Messages, NavSidebar, ChatThread,
ShareDialog, ChannelPage, PlexPlaylistView, PlexPlaylistAdd,
PlexCollectionEditor and plexViews.

Moves the font-weight out of Button's BASE and into each variant, so the
`link` variant can be semibold (which is what every text link in this app
looks like) while the action variants stay medium, and no element can end
up with two font-* utilities.

Welcome is the one signed-out surface this session cannot see, so only
its byte-identical link swaps were taken; its primary submit and the
bg-card button family are left for a pass that can UAT the screen.

3x tsc, eslint, knip, prettier, 689 vitest green; UAT on Notifications,
Messages and the nav rail, clean console in a fresh tab.
This commit is contained in:
2026-09-05 18:39:22 +02:00
parent 29bb914a95
commit 47e2293de3
13 changed files with 165 additions and 153 deletions
+16 -11
View File
@@ -36,26 +36,28 @@ type ButtonSize =
const VARIANTS: Record<ButtonVariant, string> = {
// .btn-primary / .btn-danger own the fill + one hover-dim (index.css @layer components).
primary: "btn-primary",
danger: "btn-danger",
primary: "font-medium btn-primary",
danger: "font-medium btn-danger",
// E10 S1b: the neutral variants were RE-AIMED at the paint the app actually uses. Counting the
// un-migrated call sites showed the majority hovering to `accent` (outline) and filling with `card`
// (ghost), while this primitive hovered to `fg` and filled with `surface` — so the 37 migrated sites
// and the ~100 hand-rolled ones had drifted into two different looks. That split IS the
// inconsistency this epic exists to remove; the primitive follows the majority, not the reverse.
outline: "border border-border text-muted hover:text-accent hover:border-accent",
"outline-accent": "border border-accent/40 text-accent hover:bg-accent/10",
outline: "font-medium border border-border text-muted hover:text-accent hover:border-accent",
"outline-accent": "font-medium border border-accent/40 text-accent hover:bg-accent/10",
// Neutral until you reach for it. NOT `danger-soft`, which is already red at rest — the difference
// is whether the button should advertise the destruction before the pointer arrives.
"outline-danger": "border border-border text-muted hover:text-danger hover:border-danger/40",
"outline-warn": "border border-warn/40 text-warn hover:bg-warn/10",
ghost: "text-muted hover:text-fg hover:bg-card",
glass: "glass-card glass-hover hover:text-accent",
"danger-soft": "border border-danger/60 text-danger hover:bg-danger/10 hover:border-danger",
"outline-danger":
"font-medium border border-border text-muted hover:text-danger hover:border-danger/40",
"outline-warn": "font-medium border border-warn/40 text-warn hover:bg-warn/10",
ghost: "font-medium text-muted hover:text-fg hover:bg-card",
glass: "font-medium glass-card glass-hover hover:text-accent",
"danger-soft":
"font-medium border border-danger/60 text-danger hover:bg-danger/10 hover:border-danger",
// No font-weight here on purpose: BASE already sets font-medium, and a second font-* utility would
// be a coin-flip (both are single-class utilities of equal specificity, and without tailwind-merge
// the winner is decided by the generated stylesheet order, not by the class list order).
link: "text-accent hover:underline",
link: "font-semibold text-accent hover:underline",
};
const SIZES: Record<ButtonSize, string> = {
@@ -68,8 +70,11 @@ const SIZES: Record<ButtonSize, string> = {
none: "gap-1.5",
};
// The font-weight is owned by each VARIANT, not by BASE: `link` wants semibold (that is what a text
// link looks like everywhere in this app) and the action buttons want medium. Two font-* utilities on
// one element is a coin flip without tailwind-merge, so exactly one has to emit it — see the test.
const BASE =
"inline-flex items-center justify-center font-medium transition disabled:opacity-50 disabled:cursor-not-allowed";
"inline-flex items-center justify-center transition disabled:opacity-50 disabled:cursor-not-allowed";
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant;