From 767b64cdd4c1d561f4e37886ee476f5e367ac3bd Mon Sep 17 00:00:00 2001 From: Till JS Date: Wed, 15 Apr 2026 01:31:30 +0200 Subject: [PATCH] refactor(shared-ui): migrate remaining PillNav triggers to Pill Sync status, user menu (bar-mode + overlay fallback) and logout now use the shared Pill component like the rest of PillNavigation. All pill styling now lives in a single place. - Pill gains an escape-hatch `data?: Record` prop for arbitrary data-* attributes (used by the user-menu trigger which is click()-ed via querySelector from the consuming layout) and a bindable `element` binding for imperative focus/positioning (replaces the old bind:this={userMenuTrigger}). - Remove the now-dead inline .pill / .glass-pill / .logout-pill / .pill-label / .pill.active / .pill.icon-only CSS from PillNavigation (all living in Pill.svelte now). ~110 lines of CSS gone. - The mobile override that forced 44px min-height on .pill is also gone; Pill sizes are controlled via the size prop. Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/shared-ui/src/navigation/Pill.svelte | 10 ++ .../src/navigation/PillNavigation.svelte | 155 +++--------------- 2 files changed, 36 insertions(+), 129 deletions(-) diff --git a/packages/shared-ui/src/navigation/Pill.svelte b/packages/shared-ui/src/navigation/Pill.svelte index e26d4d2ca..bf3b8d118 100644 --- a/packages/shared-ui/src/navigation/Pill.svelte +++ b/packages/shared-ui/src/navigation/Pill.svelte @@ -29,6 +29,10 @@ title?: string; /** Extra class names (e.g. drag-source marker) */ class?: string; + /** Bind the rendered diff --git a/packages/shared-ui/src/navigation/PillNavigation.svelte b/packages/shared-ui/src/navigation/PillNavigation.svelte index 23678294c..3f0e4894a 100644 --- a/packages/shared-ui/src/navigation/PillNavigation.svelte +++ b/packages/shared-ui/src/navigation/PillNavigation.svelte @@ -456,7 +456,7 @@ // User menu panel state let userMenuOpen = $state(false); - let userMenuTrigger = $state(undefined); + let userMenuTrigger = $state(null); // Close user menu on navigation $effect(() => { @@ -738,16 +738,14 @@ icon: 'cloud', items: syncStatusItems, }} - + /> {:else if showSyncStatus && syncStatusItems.length > 0} toggleBar(userBarConfig)} - class="pill glass-pill icon-only" - class:active={activeBarId === 'user'} - aria-label={userLabel} - title={userLabel} - data-user-menu-trigger - > - - + data={{ 'data-user-menu-trigger': '' }} + /> {:else if userEmail || loginHref} {@const userLabel = userEmail ? truncateEmail(userEmail) : guestMenuLabel} - + bind:element={userMenuTrigger} + data={{ 'data-user-menu-trigger': '' }} + /> {:else if onLogout && showLogout} - + {/if} @@ -822,7 +813,7 @@ showLanguageSwitcher={showLanguageSwitcher && languageItems.length > 0} {languageItems} onClose={() => (userMenuOpen = false)} - triggerElement={userMenuTrigger} + triggerElement={userMenuTrigger ?? undefined} /> {/if} @@ -894,76 +885,6 @@ padding: 0.375rem 0.75rem; gap: 0.5rem; } - - .pill-label { - display: none; - } - - .pill { - padding: 0.625rem; - min-width: 44px; - min-height: 44px; - justify-content: center; - } - } - - /* Base pill styles */ - .pill { - display: flex; - align-items: center; - gap: 0.375rem; - padding: 0 0.875rem; - height: 36px; - border-radius: 9999px; - font-size: 0.875rem; - font-weight: 500; - white-space: nowrap; - text-decoration: none; - transition: all 0.2s; - border: none; - cursor: pointer; - } - - /* Solid theme-tokened pill (formerly the "glass" frosted pill). - The class name is kept for backwards compatibility. */ - .glass-pill { - background: hsl(var(--color-card)); - border: 1px solid hsl(var(--color-border)); - box-shadow: - 0 1px 2px hsl(0 0% 0% / 0.05), - 0 2px 6px hsl(0 0% 0% / 0.04); - color: hsl(var(--color-foreground)); - } - - .glass-pill:hover { - background: hsl(var(--color-surface-hover)); - border-color: hsl(var(--color-border-strong, var(--color-border))); - transform: translateY(-2px); - box-shadow: - 0 6px 12px hsl(0 0% 0% / 0.08), - 0 2px 4px hsl(0 0% 0% / 0.05); - } - - /* Active state - uses CSS custom property for theming */ - .pill.active { - background: var(--pill-primary-color, var(--color-primary-500, rgba(248, 214, 43, 0.9))); - background: color-mix( - in srgb, - var(--pill-primary-color, var(--color-primary-500, #f8d62b)) 20%, - white 80% - ); - border-color: var(--pill-primary-color, var(--color-primary-500, rgba(248, 214, 43, 0.5))); - color: #1a1a1a; - } - - :global(.dark) .pill.active { - background: color-mix( - in srgb, - var(--pill-primary-color, var(--color-primary-500, #f8d62b)) 30%, - transparent 70% - ); - border-color: var(--pill-primary-color, var(--color-primary-500, rgba(248, 214, 43, 0.4))); - color: var(--pill-primary-color, var(--color-primary-500, #f8d62b)); } /* Divider */ @@ -979,30 +900,6 @@ background: rgba(255, 255, 255, 0.2); } - /* Logout pill */ - .logout-pill { - color: #dc2626; - } - - :global(.dark) .logout-pill { - color: #ef4444; - } - - .logout-pill:hover { - background: rgba(220, 38, 38, 0.15); - border-color: rgba(220, 38, 38, 0.3); - } - - .pill-label { - display: inline; - } - - /* Icon-only pill: wider than tall so it reads as a pill, not a chip. */ - .pill.icon-only { - gap: 0; - padding: 0 1.125rem; - } - /* Progress ring on pill (used for download indicator). Uses a conic-gradient border trick so it follows the pill's own border-radius regardless of shape. */