feat(portal-shell): align theme switcher trigger with locale switcher shape
The theme switcher used to render as a round 44×44 icon button while the locale switcher next to it (in the footer's device-prefs cluster since #162) renders as a small chip: icon + label + chevron. Two controls sitting side-by-side with two different visual shapes read as two unrelated widgets instead of one family of preferences. This PR aligns the theme trigger on the locale chip shape: Before: [☀] (round icon button, 44×44) After: [☀ System ▾] (chip — icon + label + chevron) The leading icon stays driven by `currentIcon()` so it still flips between sun / moon / monitor as the user selects light / dark / system — that visual feedback was the one reason the original icon- only design existed and it's preserved. Implementation: * `theme-switcher.html` swaps the Tailwind utility-class icon button for a `.theme-switcher__trigger` chip with three children — the current-mode icon (size 14), the localised mode label, and a `chevron-down` (size 12). Same children layout as `.locale-switcher__trigger`. * `theme-switcher.scss` adds `.theme-switcher__trigger` mirroring `.locale-switcher__trigger` rule-for-rule: same chip metrics (min-height 2.75rem for the AAA 44×44 tap target via vertical- padding overflow inside the thin footer), same hover/focus tokens, same dark-mode swap. Two copies rather than a shared `_chip- trigger.scss` partial — two switchers in one app is below the "three similar things" threshold from CLAUDE.md where extraction starts paying off. Promotes when a third switcher lands. No spec changes: the existing assertions check `button[aria-haspopup="menu"]` + the aria-label content, both of which are preserved. `triggerAriaLabel()` is unchanged. Verification: * 43 portal-shell specs pass. * `pnpm nx run portal-shell:build:production` (i18n-strict) green — confirms the `theme.mode.{light,dark,system}` labels are already translated (they were used in the menu items before; the trigger just reuses the same `currentLabel()` computed).
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex h-11 w-11 items-center justify-center rounded-full text-gray-600 transition-colors hover:bg-gray-100 hover:text-brand-primary-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-primary-500 dark:text-gray-300 dark:hover:bg-gray-800 dark:hover:text-brand-primary-300"
|
||||
class="theme-switcher__trigger"
|
||||
[cdkMenuTriggerFor]="themeMenu"
|
||||
[attr.aria-label]="triggerAriaLabel()"
|
||||
>
|
||||
<lib-icon [name]="currentIcon()" />
|
||||
<lib-icon [name]="currentIcon()" [size]="14" />
|
||||
<span>{{ currentLabel() }}</span>
|
||||
<lib-icon name="chevron-down" [size]="12" />
|
||||
</button>
|
||||
|
||||
<ng-template #themeMenu>
|
||||
|
||||
@@ -3,6 +3,47 @@
|
||||
// when the labels grow under translation. dark-mode surface mirrors
|
||||
// the rest of the shell (gray-900 background, gray-800 border).
|
||||
|
||||
// Trigger mirrors `.locale-switcher__trigger` — same chip shape,
|
||||
// same hover / focus tokens, same AAA-friendly 44×44 tap target
|
||||
// via vertical-padding overflow inside the thin footer. The two
|
||||
// switchers sit side-by-side in the footer's device-prefs cluster
|
||||
// (#162), so a single visual family across the pair reads as one
|
||||
// component. The leading icon stays driven by `currentIcon()` so a
|
||||
// sun / moon / monitor glyph still signals which mode is selected.
|
||||
.theme-switcher__trigger {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
min-height: 2.75rem;
|
||||
margin: -0.5rem 0;
|
||||
padding: 0 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: inherit;
|
||||
font-size: inherit;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background-color 0.12s ease-out,
|
||||
color 0.12s ease-out;
|
||||
|
||||
&:hover {
|
||||
background-color: #f3f4f6;
|
||||
color: var(--color-brand-primary-500);
|
||||
|
||||
:where(.dark) & {
|
||||
background-color: #1f2937;
|
||||
color: var(--color-brand-primary-300);
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--color-brand-primary-500);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.theme-switcher__menu {
|
||||
min-width: 11rem;
|
||||
margin-top: 0.25rem;
|
||||
|
||||
Reference in New Issue
Block a user