style(layout): align WorkspaceSwitcher avatar to the header logo recipe

Manual smoke after the logo-anchor fix (63300e5f) showed the footer
WorkspaceSwitcher used different spacing and a boxed avatar vs the
header logo. Applied the identical alignment recipe so the workspace
avatar mirrors the brand logo in both states (Option B from the
brief — bare square when collapsed, padded trigger only when
expanded).

Changes (WorkspaceSwitcher.vue only):
- Wrapper toggles `h-[56px] flex items-center px-4` when collapsed
  (mirrors the SidebarHeader brand row) and stays `p-2` when
  expanded (room for the padded trigger).
- Collapsed: a BARE rounded-lg avatar button at the same 16px left
  offset as the header logo. No `.trigger` container, no rounded
  hover-bg box wider than the avatar — the button IS the visible
  square (`.ws-logo .ws-logo-square w-8 h-8 rounded-lg`). True
  top/bottom mirror of the brand square.
- Expanded: unchanged padded `.trigger` button with avatar + name +
  chevron + hover bg. Avatar's left offset stays at 16px from the
  rail (wrapper p-2 + trigger p-2) so the expanded avatar also
  lines up vertically with the header logo.

Same alignment equation as the header recipe:

    rail_collapsed (64px) = square (32px) + 2 × px-4 (2 × 16px)

In both states the avatar's left edge sits at x=16px from the
rail's left — identical to the brand logo above. Vertical line
down the left side now reads as a single column of squares.

Desktop only. Mobile drawer chrome stays as MOBILE-SHELL-PARITY.

Tests adapted:
- `expanded trigger uses rounded-lg` (was tested in both states; the
  collapsed render no longer has a `.trigger` container).
- `expanded trigger has no justify-center` (split from the
  prior two-state assertion).
- New: `collapsed renders a bare avatar button (no .trigger
  container, just .ws-logo)` — locks the bare-square contract.
- New: `collapsed wrapper uses px-4` — locks the
  centring-equation invariant (rail=square+2×px-4) against
  accidental wrapper-padding regressions.

Suite delta: 563 → 564 (+1 net: +2 new collapsed-shape asserts,
−1 redundant two-state assert).

vue-tsc clean. Scoped ESLint clean (0 errors, pre-existing
warnings only). Manual smoke pending Bert — draw a vertical line
down the rail's left edge and verify the brand square and the
workspace square left edges sit on it in both states; in collapsed
mode verify the avatar is a bare square (no boxed button), same
visual treatment as the bare logo above.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-21 01:08:41 +02:00
parent 63300e5fc9
commit a9c5746e12
2 changed files with 71 additions and 29 deletions

View File

@@ -134,23 +134,52 @@ function inviteUser(): void {
</script>
<template>
<div class="relative flex-shrink-0 border-t border-[var(--p-content-border-color)] p-2">
<!--
Wrapper height + horizontal padding mirror the SidebarHeader brand
row (`h-[56px] px-4`) in collapsed mode so the workspace square
appears as a true bottom mirror of the brand square: same 32px
rounded-lg square at the same 16px left offset from the rail,
centred in the 64px collapsed rail by the same equation
(rail = square + 2 × px-4). In expanded mode the wrapper relaxes
to `p-2` so the full trigger (avatar + name + chevron + rounded
hover bg) fits naturally.
Plan 2.5 P6-styling-switcher-align Option B: collapsed renders
a BARE clickable avatar (no trigger box, no hover-bg container)
so it visually mirrors the bare brand logo above. Expanded keeps
the padded trigger box for the name/chevron/hover affordance.
-->
<div
class="relative flex-shrink-0 border-t border-[var(--p-content-border-color)]"
:class="collapsed ? 'h-[56px] flex items-center px-4' : 'p-2'"
>
<!--
Trigger button.
Plan 2.5 P6-styling-fix: avatar stays anchored on collapse the
`justify-center` switch is gone (it caused the same flex-recentre
slide as the SidebarHeader logo did). The trigger is ALWAYS
left-aligned; the name + chevron sit to the RIGHT of the avatar
and disappear via v-if on collapse without shifting the avatar.
Combined with the WorkspaceSwitcher wrapper's `p-2` (was
`p-[10px]`) the avatar's left offset is 8px (wrapper) + 8px
(trigger) = 16px identical to SidebarHeader's `px-4` logo
offset, so the brand square and the workspace avatar are
vertically aligned in the collapsed rail.
Radius is `rounded-lg` (8px) — matches the brand mark; crisper
than the prior `rounded-xl` per design review.
Collapsed: bare 32px rounded-lg button positioned at the same
`px-4` left offset as the brand logo. No trigger container, no
hover bg wider than the avatar true mirror of the logo above.
The `.ws-logo .ws-logo-square` classes carry the scoped
inset-shadow (RFC §7.4) so the visual treatment matches the
avatar in the expanded trigger.
-->
<button
v-if="collapsed && current"
class="ws-logo ws-logo-square w-8 h-8 flex-shrink-0 rounded-lg inline-flex items-center justify-center text-white font-bold text-[12px] border-0 cursor-pointer focus-visible:outline focus-visible:outline-2 focus-visible:outline-[var(--p-primary-color)] focus-visible:outline-offset-2"
:style="{ background: `linear-gradient(135deg, ${current.gradient[0]}, ${current.gradient[1]})` }"
:aria-label="`Workspace: ${current.name}`"
aria-haspopup="true"
@click="toggle"
>
{{ current.initials }}
</button>
<!--
Expanded trigger: full-width padded button with rounded hover bg,
avatar on the left (at wrapper p-2 + trigger p-2 = 16px from rail,
matching the collapsed avatar's offset and the header logo above),
then name and chevron.
-->
<button
v-else-if="!collapsed"
class="trigger flex w-full items-center gap-[10px] rounded-lg border border-transparent bg-transparent p-2 text-[var(--p-text-color)] transition-colors duration-150 hover:bg-[var(--p-content-hover-background)]"
aria-haspopup="true"
@click="toggle"
@@ -164,9 +193,9 @@ function inviteUser(): void {
{{ current.initials }}
</span>
<!-- Meta: name (hidden in collapsed mode). AD-2.5-W1 / P4: no sub. -->
<!-- Meta: name (AD-2.5-W1 / P4: no sub). -->
<span
v-if="!collapsed && current"
v-if="current"
class="meta flex flex-1 min-w-0 flex-col text-left leading-[1.2]"
>
<span class="name truncate text-[13.5px] font-semibold text-[var(--p-text-color)]">
@@ -175,7 +204,6 @@ function inviteUser(): void {
</span>
<Icon
v-if="!collapsed"
name="tabler-chevron-down"
:size="14"
class="flex-shrink-0 text-[var(--p-text-muted-color)]"