App window
StableThe RChursin_ app frame: a sidebar with the tools, a title row with a toolbar, and your tool in the body.
Every tool in the product kit runs inside this window, so the tools read as one product. From 768 pixels wide a sidebar shows the workspace, links to the five tools, a static workspace list and the signed-in sample person; below that only the title row stays, with the logo mark. The tool links go to the kit’s own pages (#tasks, #calendar …) until you point them at yours with hrefFor. rc-scroll gives scrolling areas inside it a thin scrollbar in the line colour.
Updated View source
Around a tool
import { Icon, type IconName } from '@rchursin/product/icons/Icon'import { SampleBadge } from '@rchursin/product/shell/layout'import { AppWindow } from '@rchursin/product/shell/AppWindow'import type { KitSlug } from '@rchursin/product/shell/pages'import { Button } from '@rchursin/product/ui'
const tasks: { id: string; title: string; status: IconName; state: string }[] = [ { id: 'AGT-112', title: 'Merge api-docs into openapi-writer', status: 'status-progress', state: 'In progress' }, { id: 'AGT-108', title: 'Rewrite the ui-snapshot description', status: 'status-review', state: 'In review' }, { id: 'AGT-104', title: 'Find out why test-fixer retries twice', status: 'status-todo', state: 'Todo' }, { id: 'AGT-101', title: 'Remove db-migration-helper', status: 'status-done', state: 'Done' }, { id: 'AGT-097', title: 'Turn on tool details in telemetry', status: 'status-done', state: 'Done' }, { id: 'AGT-093', title: 'List skills nobody has run in 60 days', status: 'status-done', state: 'Done' }, { id: 'AGT-090', title: 'Archive the legacy-refactor agent', status: 'status-canceled', state: 'Canceled' }, { id: 'AGT-086', title: 'Write owners for every skill', status: 'status-done', state: 'Done' }, { id: 'AGT-081', title: 'Count skills across all repos', status: 'status-done', state: 'Done' }, { id: 'AGT-077', title: 'Agree on a naming rule for agents', status: 'status-backlog', state: 'Backlog' },]
/** Here each tool links to its page in this Library; in your app, return your own routes */Usage
import { AppWindow } from '@rchursin/product/shell/AppWindow'<AppWindow active="tasks" label="Tasks (sample workspace)" hrefFor={(slug) => (slug === 'overview' ? '/' : `/${slug}`)} title="Tasks" toolbar={<Button size="sm">New task</Button>} className="h-[36rem]"> <TaskList /></AppWindow>API
App window props
| Prop | Type | Default | Description |
|---|---|---|---|
activerequired | KitSlug | null | – | The tool marked as current in the sidebar (aria-current="page"): overview, tasks, calendar, timer or notes. null for screens that aren’t a tool, such as settings. |
titlerequired | ReactNode | – | The start of the title row, e.g. an icon and the tool’s name. |
toolbar | ReactNode | – | Controls at the end of the title row. It wraps under the title on narrow screens. |
childrenrequired | ReactNode | – | The tool, in the body under the title row. |
className | string | – | Extra classes on the window. Set its height here, e.g. "h-[36rem]". |
bodyClassName | string | – | Extra classes on the body (already min-h-0 flex-1), e.g. "flex flex-col" so a child can scroll. |
label | string | 'RChursin_ app (sample workspace)' | The accessible name of the window’s region. Give each window on a page its own. |
navLabel | string | `${label}: tools` | The accessible name of the sidebar’s tool navigation. The default follows label, so it is unique whenever the label is. |
hrefFor | (slug: KitSlug) => string | undefined | (slug) => `#${slug}` | Where each tool link goes. Return your own routes, or undefined to show that tool as plain text instead of a link. |
CSS
| Class or variable | What it does |
|---|---|
rc-scroll | A thin scrollbar in the line-strong colour on a clear track (scrollbar-width and scrollbar-color), for scrolling areas inside the app. |
Accessibility
Keyboard
| Key | What it does |
|---|---|
| Tab | Moves through the sidebar’s tool links (when shown), then the toolbar, then the tool. |
| Up arrowDown arrowPage UpPage Down | Scroll the body, once a focusable scroll region inside it has focus (as in the example). |
- The window is a <section> with aria-label (the label prop), so it is a named region.
- Tool links sit in a <nav> named “<label>: tools” (or navLabel), so several windows on one page never repeat a landmark name. The active tool has aria-current="page".
- By default the links point at #overview, #tasks and so on, the product kit’s own page addresses. Pass hrefFor to point them at your routes; a tool it returns undefined for is plain text, not a link that goes nowhere.
- The Agents, Skills, Sessions and Reports list is plain text, labelled “Workspace (not part of this kit)”, not links.
- The logo mark and the switcher chevron are decorative and hidden from screen readers.
- If the body scrolls, make it a focusable, named region (tabIndex={0}, role="region", aria-label), as the example does, so keyboard users can scroll it.
Guidelines
Do
- Set the window’s height with className and let the body scroll inside it.
- Keep the title short: an icon and a name. Put view switches and the main action in toolbar.
- Label sample content as sample, e.g. a SampleBadge in the toolbar.
Don’t
- Don’t put a <main> inside: the window is a region of the page, not the page.
- Don’t put the only way to do something in the sidebar; below 768 pixels it is hidden.
In use
Used on this site
Related
- TasksThe whole task tracker: a keyboard-first list and board, a detail view, bulk edits, and undo for every change.
- SpecimenOne part on a quiet raised card. Specimen captions it underneath; Figure titles it on top.
- Sample badgeA small “Sample data” marker for anything that shows invented records or numbers.
- LogoThe 09 Frame mark and the RChursin_ wordmark in four lockups, drawn as one SVG that takes the text colour.
Source
The component’s own files, exactly as they run on this site.
/** * The RChursin_ app, framed: sidebar on the left, a toolbar and the tool on the right. * Every kit shows its tool inside this window so the pieces read as one product. * Sidebar items for the tools link to their kit pages (#tasks, #calendar …) unless `hrefFor` says * otherwise; the workspace items are shown for context only. */import type { ReactNode } from "react"import { Logo } from "../../brand/Logo"import { cx } from "../../components/cx"import { Icon, type IconName } from "../icons/Icon"import { ME } from "../data/sample"import { Avatar } from "../ui/misc"import type { KitSlug } from "./pages"
const toolNav: { slug: KitSlug; label: string; icon: IconName }[] = [ { slug: "overview", label: "Today", icon: "home" }, { slug: "tasks", label: "Tasks", icon: "board" }, { slug: "calendar", label: "Calendar", icon: "calendar" }, { slug: "timer", label: "Focus", icon: "timer" }, { slug: "notes", label: "Notes", icon: "note" },