KQ-2099 · WORKSPACE/LIFT
Workspace Lift
An app frame with a narrow strip of workspace icons at its far left, where the rest of the frame — sidebar, header and page — is one car: choosing another workspace runs it like an elevator on springs.glide, the current car and the next travelling together as one shaft so the seam between them passes through the frame, the new one arriving from below when it sits lower in the strip and from above when higher. The strip's indicator slides on springs.snap, the frame takes the new workspace's colour on a short tween, the header's floor number rolls one face the way the car went, and inside a workspace the nav tree's current pill travels on snap while pages cross-fade with a 4px rise. The strip is a vertical tablist whose Up and Down walk it with the car riding along, Cmd or Ctrl with 1 to 9 switches from anywhere in the frame, and in a narrow container the strip becomes a sheet dropping from the header.
ColdbrookQueuesInbox
Signed in as Ines CarvalhoColdbrook · Inbox · 15 unread
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/workspace-lift
Props
- workspaces
- LiftWorkspace[]
- default defaultLiftWorkspaces
- The workspaces in strip order: { id, name, initials, color, kind?, nav: { id, label, items: { id, label, unread?, icon?, page: { title, summary, rows } }[] }[], status?: ready | loading | locked }.
- workspace / defaultWorkspace
- string
- default the first workspace
- The open workspace, controlled or initial. A host change rides the lift too.
- onSwitchWorkspace
- (id: string, workspace: LiftWorkspace) => void
- default —
- Fires from the strip, the sheet, an arrow key or Cmd/Ctrl+number that chose a workspace.
- page / defaultPage
- string
- default each workspace's first page
- The page within the current workspace, controlled or initial; every workspace remembers its own page for the trip back.
- onNavigate
- (workspaceId: string, pageId: string) => void
- default —
- Fires when a nav item is chosen.
- onAddWorkspace
- () => void
- default —
- The strip's + button (or the sheet's Add a workspace) was pressed. Omit it and there is no + button.
- onRequestAccess
- (workspaceId: string) => void
- default —
- “Ask for access” was pressed on a locked workspace; the button then reads “Request sent”.
- user
- { name: string; initials: string }
- default { name: "Ines Carvalho", initials: "IC" }
- The person signed in, for the header avatar.
- title
- string
- default "Workspaces"
- The frame's accessible name.
- frameHeight
- number
- default 520
- The frame's height in px; pages scroll inside it.
- liftDirection
- "auto" | "up" | "down"
- default "auto"
- Where a new workspace arrives from: by its place in the strip, always rising from below, or always dropping from above.
- stripWidth
- number
- default 56
- The strip's width in px; its icons, badges and indicator scale with it.
- accent
- "cobalt" | "plum" | "mint" | "ink" | string
- default "cobalt"
- Selection colour for the nav pill and unread counts; each workspace keeps its own colour for the frame.
- shortcuts
- boolean
- default true
- Cmd or Ctrl with 1 to 9 switches workspace while focus is in the frame.
- disabled
- boolean
- default false
- Read only: nothing can be switched or opened.
- className
- string
- default —
- Classes for the frame.
| Prop | Type | Default | Description |
|---|---|---|---|
| workspaces | LiftWorkspace[] | defaultLiftWorkspaces | The workspaces in strip order: { id, name, initials, color, kind?, nav: { id, label, items: { id, label, unread?, icon?, page: { title, summary, rows } }[] }[], status?: ready | loading | locked }. |
| workspace / defaultWorkspace | string | the first workspace | The open workspace, controlled or initial. A host change rides the lift too. |
| onSwitchWorkspace | (id: string, workspace: LiftWorkspace) => void | — | Fires from the strip, the sheet, an arrow key or Cmd/Ctrl+number that chose a workspace. |
| page / defaultPage | string | each workspace's first page | The page within the current workspace, controlled or initial; every workspace remembers its own page for the trip back. |
| onNavigate | (workspaceId: string, pageId: string) => void | — | Fires when a nav item is chosen. |
| onAddWorkspace | () => void | — | The strip's + button (or the sheet's Add a workspace) was pressed. Omit it and there is no + button. |
| onRequestAccess | (workspaceId: string) => void | — | “Ask for access” was pressed on a locked workspace; the button then reads “Request sent”. |
| user | { name: string; initials: string } | { name: "Ines Carvalho", initials: "IC" } | The person signed in, for the header avatar. |
| title | string | "Workspaces" | The frame's accessible name. |
| frameHeight | number | 520 | The frame's height in px; pages scroll inside it. |
| liftDirection | "auto" | "up" | "down" | "auto" | Where a new workspace arrives from: by its place in the strip, always rising from below, or always dropping from above. |
| stripWidth | number | 56 | The strip's width in px; its icons, badges and indicator scale with it. |
| accent | "cobalt" | "plum" | "mint" | "ink" | string | "cobalt" | Selection colour for the nav pill and unread counts; each workspace keeps its own colour for the frame. |
| shortcuts | boolean | true | Cmd or Ctrl with 1 to 9 switches workspace while focus is in the frame. |
| disabled | boolean | false | Read only: nothing can be switched or opened. |
| className | string | — | Classes for the frame. |