MOBILE-SHELL-PARITY. Adds 7 Vitest assertions for the three defects:
- AppSidebar: header pt is '!hidden' (default close-X suppressed), content pt
is a full-height flex column (flex-col/h-full/min-h-0), showCloseIcon is not
forced false, and WorkspaceSwitcher renders inside the drawer.
- SidebarHeader: the Icon stub now exposes data-icon; mobile brand-row control
is an explicit close (aria-label 'Sluit menu', tabler-x), desktop stays the
collapse chevron, and the header renders expanded on mobile even when
sidebarCollapsed is true (logo parity).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Refines the prior sidebar styling commit (8e166512) after manual smoke:
- Brand logo + workspace avatar: rounded-xl -> rounded-lg (crisper
square per design review; both stay unified at the same radius).
- Logo no longer jumps on collapse. The previous code toggled
`justify-between` ⇄ `justify-center` on the header row, which
re-centred the logo against the parent's width — and the parent
width animates from 256px to 64px over 200ms, so the logo slid
from x≈112px (centred in the expanded rail) to x=16px (centred in
the collapsed rail). Visible jump.
Fix: the brand row is now ALWAYS `px-4` and left-aligned. The
logo's horizontal offset (16px from the rail's left edge) is
identical in expanded and collapsed states. Why this still looks
centred when the rail collapses:
rail_collapsed (64px) = logo (32px) + 2 × px-4 (2 × 16px)
With those numbers aligned, a stationary left-aligned logo IS
visually centred in the 64px-wide collapsed rail. The width
transition then "slides the rail closed around" the anchored
logo. Wordmark + Beta badge sit to the RIGHT of the logo and
v-if-disappear on collapse; their absence doesn't shift the logo
because they were never to its left.
- Toggle chevron placement:
- Expanded: collapse chevron (◀) inline at the right of the brand
row, pushed by `ms-auto` (NOT by justify-between forcing the
layout to recentre the logo).
- Collapsed: a SECOND row below the brand row holds a centred
expand chevron (▶) button. Replaces the prior tucked-chip that
overlapped the logo. No overlap, no overhang needed against the
aside's `overflow-hidden`.
- WorkspaceSwitcher trigger: same anchor-on-the-left treatment
applied. The `justify-center` switch on collapse is gone (it
caused an identical avatar slide). Wrapper padding `p-[10px]` ->
`p-2` so the avatar's left offset (wrapper 8 + trigger 8 = 16px)
matches the SidebarHeader logo (px-4 = 16px) — the brand square
and the workspace square are now vertically aligned in the
collapsed rail.
Desktop only. Mobile drawer chrome tracked separately as
MOBILE-SHELL-PARITY.
Tests adapted:
- WorkspaceSwitcher.spec.ts: trigger-rounded assertion bumped
rounded-xl -> rounded-lg; +1 spec locks "trigger never carries
justify-center" (avatar-anchored invariant).
- SidebarHeader.spec.ts: collapsed-behaviour spec rewritten — was
asserting `justify-center`, now asserts the row carries `px-4`
WITHOUT either justify-center OR justify-between (the actual
anchor contract); +1 spec confirms the expand chevron lives in a
SIBLING row of the brand mark (no overlap).
Suite delta: 561 -> 563 (+2). vue-tsc clean. Scoped ESLint clean
(0 errors, pre-existing warnings only).
Manual smoke pending Bert: collapse/expand slowly and watch the
logo — must NOT move horizontally. Confirm rounded-lg looks crisp
(not pebble-soft). Confirm expand chevron sits in its own row
below the logo, no overlap.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Plan 2.5 P6 follow-up. Closes two desktop shell-parity gaps from P6
manual smoke against crewli-starter SoT:
- Sidebar logo decoupled from the collapse toggle. Expanded layout is
now justify-between (brand group left, collapse chevron right) and
collapsed layout is justify-center with the logo alone. The expand
affordance becomes a small absolute-positioned circular button at
the rail's right edge — solid background + border so the slight
overlap with the centred logo reads as a tucked-in chip rather than
a collision. Toggling collapsed no longer shifts the logo.
- Brand square (SidebarHeader logo) and workspace avatar
(WorkspaceSwitcher trigger) unified to the same rounded square
(h-8 w-8 rounded-xl). Existing sizes were already consistent at
32px — radius bumped from rounded-lg (8px) / var(--p-border-radius)
(~6px) to rounded-xl (12px) per the design direction. Collapsed
rail now reads as a vertical mirror: brand square at the top,
avatar square at the bottom, bracketing the nav icons.
- WorkspaceSwitcher trigger restyled: rounded-xl (was the sharper
var-radius), p-2 (was px-[10px] py-[8px]), hover background. The
collapsed-variant gating of name + chevron is unchanged from P5.
Edge-mounted overhang past the rail edge was not possible: the aside
carries `overflow-hidden` (intentional, for the w-64 ⇄ w-16 width
transition) which clips anything past the rail edge. The tucked-chip
pattern (24px circle at end-0, solid bg) is the visual compromise —
the affordance stays inside the rail, discoverable, and visually
decoupled from the logo.
Desktop only. Mobile drawer chrome (logo placement, drawer X button,
missing switcher) tracked separately as MOBILE-SHELL-PARITY.
Tests:
- +2 WorkspaceSwitcher.spec.ts: trigger uses rounded-xl; collapsed
trigger renders avatar only (hides .meta).
- +1 SidebarHeader.spec.ts: collapsed row hides the .brand-name
wordmark and toggles to justify-center (logo-stays-centred lock).
Suite delta: 558 → 561 (+3). vue-tsc clean. Scoped ESLint clean
(0 errors, pre-existing warnings only).
Manual smoke pending Bert: collapse the rail, verify logo stays put
and the expand chip appears at the right edge; verify the trigger
shows rounded corners + hover bg; verify the collapsed avatar
mirrors the brand square size/radius.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Vitest hoists vi.mock()/vi.hoisted() above all imports, so the
component import can sit with the other imports (import/first satisfied)
without the eslint-disable-next-line directives — the mock factories
only deref their refs at mount time. Honors the no-eslint-disable rule.
28/28 affected specs green.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The project's no-unused-vars only ignores all-underscore names (/^_+$/u);
`_bp` in the @vueuse/core useBreakpoints mock failed it. Latent since
Task 3 — masked because the whole-codebase `pnpm lint` stylish formatter
OOMs (RangeError on the legacy-code message volume) and emitted no
results. Scoped errors-only lint surfaced it. 21/21 specs still green.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CRITICAL: replace `lg:hidden` on PrimeVue Drawer with `v-if="isMobile"` so the
teleported portal/overlay is never created on desktop viewports regardless of
mobileOpen state. Replace useMediaQuery raw string in SidebarHeader with
useBreakpoints(breakpointsTailwind).smaller('lg') shared by both components.
Add desktop/mobile comments; adapt tests to useBreakpoints mock; add
Drawer-absent-on-desktop and aside w-16/w-64 width-class assertions (21 tests).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Ports crewli-starter's monolithic AppSidebar.vue into two typed production
components: SidebarHeader (the .brand block) and AppSidebar (composing
SidebarHeader + SidebarNav + WorkspaceSwitcher). AppSidebar renders a
permanent <aside> on desktop (lg+) and a PrimeVue Drawer on mobile, both
wired to useShellUiStore for collapse/mobile state.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>