Scope
The org ▸ project scope contract shared by the scoped-shell blocks. Every screen renders inside an organisation (the auth boundary) and optionally one construction project; blocks receive the scope as display snapshots and report changes through three callbacks. See docs/scoped-shell.md for the full architecture note.
bunx shadcn@latest add @nx-ui/scopeThe display snapshots
OrgRef and ProjectRef are the two shapes every scoped block agrees on — id, name, and just enough display metadata for menus (org logo/role, project reference/client).
With no logoUrl, avatars fall back to scopeInitials(name) — first letters of the first two words.
These are display snapshots, not domain records — consumers map their own models down to them.
The two scope states
Scope is always an organisation plus either a project or null. null means workspace level — the projects picker and cross-project overviews.
project: null — workspace level. Nav, breadcrumbs and data cover the whole organisation (projects picker, cross-project dashboards).
project set — project scope. Every nav entry and breadcrumb tail is scoped to Riverside Quarter.
The callback contract
Scope arrives via props and leaves via onOrgChange, onProjectChange and onProjectClear — the consumer owns the source of truth and its obligations (an org switch is an auth-scope change; a project switch lands on the overview; clearing returns to the picker). Try it:
The current scope, derived only from the callbacks above — the blocks themselves hold no state, persistence or auth.
scopeInitials
The one helper in the module: initials for an org/project avatar fallback — the first letters of the first two words, upper-cased and whitespace-tolerant.