AppShell

The signed-in app: okami's header across the top, Okami's chat on the left and beside it the app itself, framed, over the app's one composer across the bottom, with its nav inside as a rail of icons on a hairline of its own. Talk on the left, see it change on the right, as in setup. Under 1024px the chat and the app become panes in one swipe under okami's glass logo, Chat | App and the studio, with the composer pinned under both.

Example

Held in a box here; in the app it is the whole screen. Its breakpoints follow the window: narrow this window to see the rail give way to the menu under okami's mark, and the tabs.

Good morning. Katka is in from 9, and you have four bookings.
Katka is off Friday
Done — Friday is off for Katka.

Home

Katka

Mon–Fri 9:00–18:00

Mila

Mon–Fri 9:00–18:00

API reference

PropTypeDefaultDescription
chatReactNode—The chat's thread. It lives in the layout, so it stays put while pages change.
composerReactNode—The app's one composer, pinned to the foot: across the last row from its start at a measure side by side, the chat and the frame ending above it; under both panes on a phone, the chat fading out above it.
useHeaderBack(back)hook—A page's way back ({ label, onBack }) in place of okami's mark on a phone while it's open and the app pane is in view (the Inbox's open thread); the composer and tab bar step away with it.
useComposerAway(away)hook—For a page with a composer of its own (the Inbox's open thread): on a phone, with the app pane in view, the app's composer steps away and the tab bar stays.
composerEndReactNode—Beside the composer: the rest of its row side by side — the studio's channels next to it and Chat settings at the far end. Left out on a phone, where it would take a row from the chat for what Settings already holds.
sidebarReactNode—The nav, labelled: on a phone with no tab bar, okami's mark in the header opens it.
tabBarReactNode—The nav on a phone, a TabBar under the composer within the thumb's reach: a tap on a page slides to the app, and it steps away while the composer has focus. With it okami's mark is just the mark.
railReactNode—The nav as a NavRail of icons, in the frame from 1024px.
accountReactNode—The header's end: a StudioMenu with what's the owner's (settings, sign out).
appTabReactNodelabels.appWhat the App tab says on a phone: the page in view, “Inbox”, rising in as it changes.
labelsPartial<AppShellLabels>Englishchat, app and menu, in the owner's language.
classNamestring—The whole screen (h-dvh) by default; give a height to hold it in a box.
childrenReactNode—The page. It sits in an @container: size it with @-variants (@xl:grid-cols-2), not the window's.

Guidelines

  • As the whole screen (no className), it keeps to the part in view: a phone's keyboard shrinks it instead of panning the page up, and the composer sits 16px over the keyboard (useVisibleViewport, the keyboard: variant). Given a height to sit in a box, as here, it leaves the page alone.
  • One header over everything: okami's wordmark and the studio side by side; under 1024px the glass logo alone, Chat | App in the middle and the studio's mark. The app opens on the chat.
  • One composer for the whole app, always at the foot. The chat is always in view side by side; there's no putting it away.
  • Side by side from 1024px, the nav a slim rail of icons in the frame, each named by a tooltip. On a phone, a tab bar under the composer, each page in words under its icon.
  • The swipe is the browser's own snap scrolling: the tab thumb rides it, and a tap on a tab scrolls there (at once with reduced motion).
  • The frame's corner (squircle-4xl) is the rail's (squircle-2xl) plus the 8px between them, so the curves run parallel.
  • Pages size themselves by the frame, never the window: the same page is narrower beside the chat than alone in the App tab.
  • Below lg it's a native app's screen: the header, the composer and the tab bar hold still, and only the middle scrolls. The document never bounces, so a drag past a list's end moves the list, not the chrome.