Files
agent-frontend-web/docs/superpowers/specs/2026-08-04-hide-chrome-for-common-role-design.md
2026-08-07 13:56:29 +08:00

4.7 KiB

Hide Layout Chrome for common Role Users

Date: 2026-08-04 Status: Approved (design phase)

Goal

When the logged-in user's role list contains "common", hide all layout chrome — left sidebar, top navbar, tags view, and the floating Settings button — leaving only <app-main /> filling the viewport. Non-common users see the existing full layout unchanged.

Trigger Condition

useUserStore().roles (array) includes the string "common".

The check is roles.includes('common'). A user with ['admin', 'common'] is treated as common because the array includes it; only roles where common is present trigger the new behavior.

Scope

  • Applies to all routes, unconditionally.
  • No per-route opt-out, no per-route opt-in.
  • Only the layout chrome is affected. <app-main /> keeps rendering any page contents exactly as before.

Files Touched

Only one file: src/layout/index.vue.

No stores, no router, no permission utilities, no new files.

Design

1. Add a isCommonUser computed in src/layout/index.vue

import useUserStore from '@/store/modules/user'

const isCommonUser = computed(() =>
  useUserStore().roles?.includes('common') ?? false
)

Defensive ?.includes so an undefined roles collapses to false rather than throwing.

2. Gate the four chrome elements with v-if

<sidebar v-if="!sidebar.hide && !isCommonUser" class="sidebar-container" />
<div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide, fullscreen: isCommonUser }" class="main-container">
  <div :class="{ 'fixed-header': fixedHeader }">
    <navbar v-if="!isCommonUser" @setLayout="setLayout" />
    <tags-view v-if="needTagsView && !isCommonUser" />
  </div>
  <app-main />
  <settings v-if="!isCommonUser" ref="settingRef" />
</div>

Notes:

  • Settings is hidden even though the original requirement listed only sidebar / navbar / tagsview. The user explicitly confirmed in brainstorming that Settings should also be hidden for cleanliness.
  • The outer <div :class="classObj"> (with hideSidebar / openSidebar / mobile) is left untouched; it still affects the <sidebar> slot even when sidebar is hidden, which is harmless.

3. Fullscreen styling for <app-main />

Add a scoped SCSS rule:

.main-container.fullscreen {
  position: absolute;
  inset: 0;
  width: 100% !important;
  height: 100% !important;
  margin-left: 0 !important;
}

This overrides any inherited width / margin-left from the sidebar-open state so <app-main /> truly fills the viewport.

<app-main /> internals (page padding, scoped styles) are not touched. If a future page wants a chrome-less padding experience, it owns that decision.

Out of Scope

  • Not changing user store APIs.
  • Not adding a getter to useUserStore (single use site — YAGNI).
  • Not introducing a second layout component or per-route layout switching.
  • Not modifying router meta or permission middleware.
  • Not changing <app-main /> internals or any page component.

Error / Edge Cases

  • roles is undefined or null?.includes('common') returns undefined, ?? false collapses to false. Safe.
  • roles is a non-array → Array.prototype.includes would throw. The user store initializes roles: [], so this is only a risk if a future change sets it to a non-array. No mitigation needed; treat as a contract violation.
  • User role changes mid-session (does not happen in this app) → computed reacts automatically, layout re-renders. Reactive by design.
  • Page refresh → useUserStore is repopulated via getInfo() during router guard; the computed is reactive and picks up the new value on next render. No flicker handling needed because the layout renders after auth is resolved.

Testing

The repo has no frontend unit / e2e tests configured (package.json scripts only cover dev / build). Verification is manual:

  1. Admin role: log in as a user without common in roles. All four chrome areas render as before.
  2. Common role: log in as a user with common in roles. Only <app-main /> is visible, filling the viewport edge-to-edge.
  3. Negative variant: log in as a user with ['common', 'other-role'] — chrome still hidden (array .includes semantics).
  4. Page interaction: inside <app-main />, the page's own links / buttons / forms work normally.

Future Considerations (not implemented)

  • If a second role-driven layout variant appears (e.g. compact admin view), lift isCommonUser into a useUserStore getter and consider per-route layout meta.
  • If pages ever need to opt out of fullscreen (e.g. a "preview" page that wants the chrome visible for a common user), introduce per-route meta and gate on it. Not a current requirement.