fix(ui): chrome convergence — restore section title + surface backgrounds + homogeneous gutter#4188
Conversation
…view outside container
… (homogeneous chrome)
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (17)
WalkthroughThis PR implements a unified page layout convention across admin and user modules. The admin layout template is refactored to move CoreSurfaceTabBar outside PageHeader as a sibling and remove the wrapper div around router-view. All routed views are wrapped in consistent v-container/v-row/v-col structure. Unit tests are updated to assert the new chrome pattern instead of prior negation expectations. ChangesChrome Convergence Implementation
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #4188 +/- ##
=======================================
Coverage 99.56% 99.56%
=======================================
Files 31 31
Lines 1140 1140
Branches 329 329
=======================================
Hits 1135 1135
Misses 5 5 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR fixes post-#4187 UI chrome regressions by restoring consistent “section header + tab bar + surfaced content” layout across Admin and Account child routes, and documents the convention for downstream projects.
Changes:
- Adjust
admin.layout.vueto renderPageHeaderandCoreSurfaceTabBaras siblings (avoidingPageHeader’s#tabsmode), and move<router-view />outside the layout container so children own their gutter/surface. - Reintroduce consistent child-view chrome wrappers (
v-container fluid+v-row pa-2 mt-0+v-col cols=12and surface cards where appropriate) for Admin sub-views and/users/*child views. - Update unit tests to assert the restored chrome structure, and add a new MIGRATIONS section describing the convention and downstream opt-in patterns.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/modules/admin/views/admin.layout.vue | Restores section title visibility and consistent tab placement by making the tab bar a sibling (not #tabs) and moving router-view outside the container. |
| src/modules/admin/views/admin.users.view.vue | Adds standardized container/row/col guttering around the data table view. |
| src/modules/admin/views/admin.user.view.vue | Wraps the detail view in standardized guttering and keeps the surfaced card layout. |
| src/modules/admin/views/admin.readiness.view.vue | Adds standardized container/row/col chrome around the readiness surface. |
| src/modules/admin/views/admin.organizations.view.vue | Adds standardized container/row/col chrome around the organizations data table view. |
| src/modules/admin/views/admin.activity.view.vue | Adds standardized container/row/col chrome around the activity surface. |
| src/modules/users/views/user.profile.view.vue | Restores surface background and consistent guttering; restructures danger zone layout within the surfaced content area. |
| src/modules/users/views/user.organizations.view.vue | Restores surface background and consistent guttering around the organizations list content. |
| src/modules/admin/tests/admin.layout.unit.tests.js | Updates/extends tests to verify sibling tab-bar pattern, router-view placement, and header props across modes. |
| src/modules/admin/tests/admin.users.view.unit.tests.js | Updates chrome assertions to expect the new container/row/col structure. |
| src/modules/admin/tests/admin.user.view.unit.tests.js | Updates chrome assertions to expect the new container/row/col structure. |
| src/modules/admin/tests/admin.readiness.view.unit.tests.js | Updates chrome assertions to expect the new container/row/col structure. |
| src/modules/admin/tests/admin.organizations.view.unit.tests.js | Updates chrome assertions to expect the new container/row/col structure. |
| src/modules/admin/tests/admin.activity.view.unit.tests.js | Updates chrome assertions to expect the new container/row/col structure. |
| src/modules/users/tests/user.profile.view.unit.tests.js | Adds chrome structure assertions for the restored surfaced/guttered layout. |
| src/modules/users/tests/user.organizations.view.unit.tests.js | Adds chrome structure assertions for the restored surfaced/guttered layout. |
| MIGRATIONS.md | Documents the converged chrome convention and downstream opt-in migration pattern. |
Splits the chrome primitive into two specialised components so each page gets the spacing it actually needs: - `PageHeader` (existing, simplified): content-sized header for list views and simple pages (Tasks, Scraps, etc.). Drops the 56px min/max-height enforcement and removes the dead `#tabs` slot path (unused since the CoreSurfaceTabBar sibling refactor in #4187/#4188). - `CorePageHeaderTabs` (new): bundles a PageHeader + CoreSurfaceTabBar and enforces the 56px height externally via a scoped `:deep()` rule. Used on tabbed surfaces (Account, Organization detail, Admin) where the title↔ breadcrumb rhythm matters across route transitions. The new primitive accepts `tabs`, `can`, `basePath`, and `hideTabs` for the tab bar, plus passthrough of icon/title/subtitle/avatar/actions/breadcrumb slots and props to the inner PageHeader. Migrates 3 callsites: - user.view.vue (Account) - admin.layout.vue (with hideTabs in breadcrumb mode) - organization.detail.component.vue Visual impact: pages without tabs (e.g. /tasks) no longer pay the 56px chrome tax; tabbed surfaces preserve the route-transition rhythm.
…erTabs) (#4190) * refactor(ui): tabs align with default sidenav offset + tighten gap below tabs - CoreSurfaceTabBar: add `mx-4` to `<v-tabs>` so the tab strip aligns horizontally with PageHeader's title icon (both now use the same 16px inset inside `v-container fluid`). - user.view.vue + admin.layout.vue: add `pb-0` on the parent `<v-container fluid>` to collapse ~16px of dead space between the tab underline and the routed child's top edge. Child views own their own top padding via their own `<v-container fluid>` + `pa-2` row. Visual impact (Account, Org detail, Admin surfaces): - Tabs now align with the section title icon instead of being 16px to the left - Vertical gap below tabs drops from ~40px to ~24px * refactor(ui): apply pb-0 to organization detail container Extends the tabs-spacing-alignment fix to organization.detail.component.vue which had the same `<v-container fluid>` parent pattern as user.view.vue and admin.layout.vue. Brings /users/organizations/:id/* into visual parity with /users/profile and /admin/* surfaces. * refactor(core): split PageHeader + CorePageHeaderTabs Splits the chrome primitive into two specialised components so each page gets the spacing it actually needs: - `PageHeader` (existing, simplified): content-sized header for list views and simple pages (Tasks, Scraps, etc.). Drops the 56px min/max-height enforcement and removes the dead `#tabs` slot path (unused since the CoreSurfaceTabBar sibling refactor in #4187/#4188). - `CorePageHeaderTabs` (new): bundles a PageHeader + CoreSurfaceTabBar and enforces the 56px height externally via a scoped `:deep()` rule. Used on tabbed surfaces (Account, Organization detail, Admin) where the title↔ breadcrumb rhythm matters across route transitions. The new primitive accepts `tabs`, `can`, `basePath`, and `hideTabs` for the tab bar, plus passthrough of icon/title/subtitle/avatar/actions/breadcrumb slots and props to the inner PageHeader. Migrates 3 callsites: - user.view.vue (Account) - admin.layout.vue (with hideTabs in breadcrumb mode) - organization.detail.component.vue Visual impact: pages without tabs (e.g. /tasks) no longer pay the 56px chrome tax; tabbed surfaces preserve the route-transition rhythm. * fix(tasks): align empty state row spacing with populated state The empty-state v-row was missing `pa-2 mt-0` (so Vuetify's default v-row margin applied) and the card carried `ma-6` (24px all-sides margin), which together added ~36px of dead space above the "No tasks yet" card compared to the populated state. Drop `ma-6` and apply the same `pa-2 mt-0` row classes as the populated branch — empty state now sits flush with where task cards would render. * fix(tasks): harmonize empty state card with /task layout Drop `align="start" justify="center"` on the v-row and align card padding (pa-8 → pa-6) so the empty-state card sits at the exact same top/left as the form card on /task. Keeps `text-center` for icon + label centering inside the card. * fix(tasks): hide populated v-row when task list is empty The populated state's v-row had no v-if guard, so when the task list was empty it still rendered (just without children) — taking ~16-24px of vertical space (pa-2 + default v-row margins) on top of the empty-state row below. Empty-state card now sits at the same top position as the populated cards (and /task form card). * docs(migrations): document PageHeader split + tabs spacing/alignment refactor Adds a new migration entry above the previous chrome convergence entry covering the changes in this batch: - PageHeader is content-sized (56px enforcement removed) - #tabs slot on PageHeader dropped (dead since #4187) - mx-4 on v-tabs inside CoreSurfaceTabBar (horizontal alignment with title) - New CorePageHeaderTabs primitive that bundles PageHeader + SurfaceTabBar and re-enforces the 56px rhythm for tabbed surfaces - pb-0 on the tabbed-layout container to collapse the gap below tabs - Empty-state v-row regression pattern (guard the populated row with v-if) Downstream impact: minimal — only projects with custom tabbed layouts need to migrate to CorePageHeaderTabs; default consumers get the new convention via /update-stack automatically.
# [2.1.0](v2.0.0...v2.1.0) (2026-06-01) ### Bug Fixes * **billing:** 4 pricing page bugs (signed-in features, hero bg, truncation, guest CTA) ([#4123](#4123)) ([bd399bd](bd399bd)) * **billing:** add aria-expanded + touch support to BillingComputeGauge components ([#4181](#4181)) ([7e7fa97](7e7fa97)) * **billing:** billing UX hardening — 5 reliability fixes ([#4079](#4079)) ([dd62b3d](dd62b3d)), closes [#4078](#4078) * **billing:** canonical FA6 icons + meterError lifecycle + reject URL credentials ([#4082](#4082)) ([be16045](be16045)), closes [#4081](#4081) * **billing:** clear intentId on Stripe cancel-redirect ([#4085](#4085)) ([11be9ab](11be9ab)) * **billing:** drop empty overage aria-live div + add fr overDetail key ([#4142](#4142)) ([10e5d81](10e5d81)) * **billing:** i18n migration + Intl.NumberFormat USD (audit Codex P1) ([#4069](#4069)) ([3d3a4f6](3d3a4f6)) * **billing:** mega vue hardening v3 — UX + design + a11y + V4 ([#4063](#4063)) ([802cbc4](802cbc4)) * **billing:** send {} body on portal POST to satisfy Zod PortalRequest schema ([#4137](#4137)) ([bf6b9c8](bf6b9c8)) * **billing:** send intentId UUID to close extras double-charge window ([#4084](#4084)) ([3e27c4c](3e27c4c)) * **billing:** subscription state safety + webhook lag polling (audit Codex P1) ([#4068](#4068)) ([328700b](328700b)) * **billing:** surface meterError + remove dead 409 dialog from subscriptions ([#4081](#4081)) ([61f5d53](61f5d53)) * **billing:** V5 polish — F5 polling recovery + visibility refetch + i18n residual + locale null-guard ([#4070](#4070)) ([4908adb](4908adb)) * **billing:** V6 polish — sessionStorage guard + locale BCP47 + i18n plural + NaN guard ([#4076](#4076)) ([2ad018c](2ad018c)) * **build:** drop ARG defaults for analytics_* + filter empty env vars ([#4112](#4112)) ([afd336e](afd336e)), closes [Vue#4110](https://github.com/Vue/issues/4110) [#4110](#4110) * **build:** restore Layer 5 empty-string env override (revert [#4112](#4112) part 2) ([c9aa9e6](c9aa9e6)), closes [Vue#4110](https://github.com/Vue/issues/4110) [comes-io/trawl_vue#880](https://github.com/comes-io/trawl_vue/issues/880) [Vue#4110](https://github.com/Vue/issues/4110) * **core:** pin CorePageHeader to content size in flex-column contexts ([#4210](#4210)) ([e182512](e182512)), closes [#4202](#4202) * **layout:** route /pricing outside app shell — no drawer offset on signed-in ([#4124](#4124)) ([d862fbb](d862fbb)) * **legal:** cookie banner UA-detection + appName fallback to app.title ([#4113](#4113)) ([04cc70a](04cc70a)), closes [trawl_vue#876](https://github.com/trawl_vue/issues/876) [#4109](#4109) * **legal:** cookie consent + footer polish (5 QA bugs) ([#4098](#4098)) ([a9bf0a3](a9bf0a3)) * **legal:** gate cookie banner on isMounted to prevent prerender hydration double-render ([#4109](#4109)) ([3198e48](3198e48)), closes [#app](https://github.com/pierreb-devkit/Vue/issues/app) * **router:** redirect authenticated users to config.sign.route instead of hardcoded '/' ([#4088](#4088)) ([ca9094e](ca9094e)), closes [#4083](#4083) * **tasks:** propagate store errors so views gate navigation on success ([#4221](#4221)) ([b029d39](b029d39)), closes [#4218](#4218) * **ui:** chrome convergence — restore section title + surface backgrounds + homogeneous gutter ([#4188](#4188)) ([1e5fdf1](1e5fdf1)) * **users:** re-apply route tab when serverConfig.billing arrives async ([#4138](#4138)) ([87cc7b2](87cc7b2)) * **users:** refetch billing subscription on auth state change ([#4125](#4125)) ([5a3425a](5a3425a)) ### Features * **admin:** invitations management tab ([#4217](#4217)) ([61ad86c](61ad86c)), closes [invitedBy/#actions](https://github.com/pierreb-devkit/Vue/issues/actions) * **analytics:** add identify and reset helpers, wire auth store ([#4104](#4104)) ([c4d8b87](c4d8b87)) * **auth:** invite-gated signup UI ([#4212](#4212)) ([d17a841](d17a841)) * **billing:** add 'Manage subscription' footer link in meterDrawer ([#4056](#4056)) ([#4057](#4057)) ([7d494af](7d494af)) * **billing:** align packs.component on BillingCardComponent (V4 unified schema) ([#4147](#4147)) ([7f5dd41](7f5dd41)) * **billing:** combined pool gauge + linear breakdown bars + alerts cleanup ([f5034b1](f5034b1)) * **billing:** config-driven static-content resolver (no file replace) ([e95d944](e95d944)) * **billing:** expose netRemainingRaw + overage in useMeter for negative quota display ([#4061](#4061)) ([111e64b](111e64b)), closes [#4060](#4060) * **billing:** meter gauges display % primary, compute units in overflow tooltip ([#4139](#4139)) ([50498ba](50498ba)) * **billing:** meter UX refonte — drop drawer + subscriptions tab ([#4059](#4059)) ([891b5ae](891b5ae)), closes [#subscriptions](https://github.com/pierreb-devkit/Vue/issues/subscriptions) [#1](#1) [#2](#2) [#3](#3) [#4](#4) * **billing:** post-grant upgrade prompt variant for depleted signupGrant ([#4128](#4128)) ([0ae8558](0ae8558)) * **billing:** pricing page redesign — multi-mode + auto-savings + sectioned features + FAQ ([#4102](#4102)) ([2c83ab6](2c83ab6)) * **billing:** pricingCard Free CTA — 'Sign up' for guests, route to /signup ([#4106](#4106)) ([cc92ac8](cc92ac8)) * **billing:** redesign Subscriptions view to match user-tabs aesthetic ([#4127](#4127)) ([15fd466](15fd466)) * **billing:** relocate billing under Organization settings ([#4175](#4175)) ([1f36137](1f36137)) * **billing:** sidenav compute gauge above sign-out row (meter mode) ([#4126](#4126)) ([fe1497f](fe1497f)) * **billing:** sidenav compute gauge redesign — button-shape above sign-out row ([#4140](#4140)) ([9d3a090](9d3a090)) * **billing:** sidenav compute gauge revamp — v-progress-circular + v-tooltip ([#4144](#4144)) ([5d60f1b](5d60f1b)), closes [#prepend](https://github.com/pierreb-devkit/Vue/issues/prepend) * **billing:** subscriptions view 2-col layout — drop dup bar, CTA to /pricing#units ([#4141](#4141)) ([b7ef516](b7ef516)), closes [pricing#units](https://github.com/pricing/issues/units) * **billing:** unified BillingCardComponent + annual toggle disabled state ([#4146](#4146)) ([474bb11](474bb11)) * **billing:** unified BillingCardComponent + annual toggle disabled state ([#4149](#4149)) ([d1403ff](d1403ff)) * **billing:** v4 hardening + Phase 3 polish — equivalences chips + UX gaps + a11y ([#4066](#4066)) ([dd40b2a](dd40b2a)) * **billing:** wire netRemainingRaw + overage into devkit components ([#4062](#4062)) ([61d6897](61d6897)), closes [#4061](#4061) * **core:** reusable PageTabs component + Account view refactor ([#4183](#4183)) ([dc2504a](dc2504a)) * **core:** unified logo+title lockup in header and sidenav ([#4086](#4086)) ([ec457db](ec457db)), closes [#4083](#4083) * **feature:** read ERRORS.md in Phase 0 before coding ([#4089](#4089)) ([ddf8f60](ddf8f60)) * **legal:** Add legal module + cookie consent (RGPD) ([#4097](#4097)) ([595e6c4](595e6c4)), closes [#3204116570](https://github.com/pierreb-devkit/Vue/issues/3204116570) [#3204116650](https://github.com/pierreb-devkit/Vue/issues/3204116650) [#1](#1) [#19](#19) [#18](#18) [#13](#13) [#11](#11) [#18](#18) [#3](#3) [#10](#10) [#12](#12) [#14](#14) [#15](#15) [#17](#17) [#20](#20) [#4](#4) [#5](#5) [#6](#6) [#7](#7) [#8](#8) [#9](#9) [#16](#16) [#3](#3) [#16](#16) [#22](#22) * **legal:** liquid glass cookie banner — Vuetify-only with friendlier copy ([#4115](#4115)) ([3d74789](3d74789)), closes [#4114](#4114) * **monitoring:** single-source PostHog Error Tracking (drop Sentry) ([#4118](#4118)) ([82dfca6](82dfca6)) * **organizations:** add Organization tab + rename General route to tab-addressable ([#4184](#4184)) ([fdfbf0b](fdfbf0b)) * **organizations:** soft suggestedJoin onboarding banner + recovery-screen copy ([#4176](#4176)) ([25a3ceb](25a3ceb)) * **seo:** enrich seoInjectPlugin — multi-schemas + rich SoftwareApplication + themeColor ([#4092](#4092)) ([#4111](#4111)) ([7f8bd09](7f8bd09)) * **skill/feature:** add Phase 0.0 issue claim-on-start ([#4117](#4117)) ([a582430](a582430)), closes [pierreb-projects/infra#28](https://github.com/pierreb-projects/infra/issues/28) * **skills/update-stack:** block on undeclared drift vs upstream ([#4228](#4228)) ([ab8ee68](ab8ee68)), closes [#4227](#4227) * **users:** subs full-width + delete account danger zone + halo full-bleed fix ([#4143](#4143)) ([981f073](981f073))
Summary
Fixes three regressions reported by the user after #4187 (homogenization) shipped to production:
/users/profile+/users/organizationshad transparent content with no surface card, tabs flush against the sidenav (no gutter)./admin/userslost the "Admin" section title with icon above the tabs./developers+/tasks.Plus a documentation gap raised at the same time:
MIGRATIONS.mdneeded to explain the new chrome convention to downstream projects (admin extras + tabs-under-title modules).Root cause
#4187 misread
corePageHeader's#tabsslot — that slot is mutually exclusive with the icon+title row, so puttingCoreSurfaceTabBarinside it hid the section title. The established pattern (used byuser.view.vuesince #4185 andorganization.detail.component.vuesince #4184) is PageHeader and CoreSurfaceTabBar as siblings inside<v-container fluid>, with<router-view />outside the container. The upstream PR #4185 also moved chrome from the monolithicuser.view.vueto the layout, but the routed children (user.profile.view,user.organizations.view) never got their<v-card color="surface">wrap back — leaving transparent panes.The fix
admin.layout.vue(commit8dc68af3)<template v-if="!currentBreadcrumb" #tabs>slot wrapping. PageHeader + CoreSurfaceTabBar are siblings.<div class="admin-content pa-4">wrapper.<router-view />outside<v-container fluid>— children own their gutter.icon="fa-solid fa-user-tie"is unconditional (shows in both list + breadcrumb mode).5 admin sub-views (commits
37bb4bc5,dad8bd3d,703c9637,7cb8f6e0,9efc4239)<v-container fluid>+<v-row class="pa-2 mt-0">+<v-col cols="12">chrome.coreDataTableComponentconsumers (users, organizations) skip the wrap-card — data table already provides a surface.v-card-based views (readiness, activity, user form) keep their card INSIDE the<v-col>.2 account child views (commits
7591fb2b,1efec145)<v-row class="pa-2 mt-0">+<v-col cols="12">+<v-card color="surface">inside<v-container fluid>.user.profile.view: danger zone restructured to flex row (heading + description + button inline) for visual coherence. All 3 info pieces preserved.MIGRATIONS.md (commit
469994d6)/developersbefore/after example).Tests
All 8 affected unit-test files updated in lockstep:
5 admin sub-view tests had their
not.toMatch(/<v-container/)source-grep assertion REVERSED totoMatch(/<v-container fluid/)+ require v-row pa-2 mt-0 + v-col cols=12.2 account-view tests added the same source-grep assertion +
<v-card color="surface">check.admin.layout test: 6 new tests asserting sibling pattern (NOT inside
#tabsslot), router-view outside container, title="Admin" / "" by breadcrumb mode, icon stays both modes.Devkit Vue full suite: 119 test files / 1910 tests green.
Coverage: 98.98% / 94.11% / 98.95% / 99.59% — exact match with refactor(ui): homogenize Account, Organization detail, and Admin pages #4187 baseline (no logic paths changed, only templates).
Audit: 0 vulnerabilities. No new deps.
Visual smoke (Chrome DevTools MCP)
GREEN — all 3 user-reported surfaces visually confirmed clean:
/admin/users— icon + "Admin" title + tab bar; data table onbg-surfacewith matching gutter./users/profile— Account chrome + profile form onbg-surface(rgb(255,255,255) confirmed via DOM), danger zone below./users/organizations— Account chrome + org list onbg-surface, gutter matches/tasksreference.Downstream propagation
After merge, propagate to all Vue downstream projects via
/update-project. Order:pierreb_vue,comes_vue,trawl_vue,montaine_vue,ism_vue. Perfeedback_shim_defer_downstream— on-demand. The trawl_vue propagation will be triggered first (user-priority) since the user reported the issue ontrawl.me.Summary by CodeRabbit
Release Notes
Documentation
Refactor