Desktop window
The window of a Mac app (Tauri 2 on the system WebView): a sidebar, the content and an inspector in a three-pane split — resizable by pointer and keyboard, collapsible — under a toolbar that moves the window and clears the traffic lights, at the desktop's density with a native-feeling focus ring. Its commands are written once for the menu bar, the page's shortcuts and the palette.
import { DesktopWindow, useDesktopCommands, desktopMenus } from "@krizaka/ui/desktop-window";Web only — React, from @krizaka/ui/desktop-window. Beta: its API may still change in a minor version.
- Mobile · n/a. A phone has no windows: its screens are the list and detail, the tabs and the sheets. → List and detail
Why it is here. The Mac apps' window (Orazaka, Yomizaka, Akinaza; Tauri, ADR-002): three panes, the toolbar and the menu bar's commands (krizaka-ui#753).
When to use
- The main window of a Tauri Mac app with a source list, a main view and the details of the selection (Finder, Mail, Xcode).
- A two-pane window: omit
inspector.
When not to use
- A console with grouped sections and a collapsing sidebar of words.Use instead: App shell
- The signed-in web app in a browser.Use instead: App frame
- A phone or a tablet.Use instead: List and detail
Installation
Install
npm install @krizaka/ui @krizaka/tailwind @krizaka/tokens tailwindcssStyles
@import "tailwindcss";
@import "@krizaka/tailwind";
@import "@krizaka/ui/tailwind.css";Import
import { DesktopWindow, useDesktopCommands, desktopMenus } from "@krizaka/ui/desktop-window";Examples
A library window
A source list, a list of files and the details of the selected one; ⌘B and ⌘I fold the panes, the menu bar gets the same commands.
Props
The window of a Mac app: sidebar · content · inspector, a toolbar that moves the window, the desktop's density.
| Prop | Type | Default | Description |
|---|---|---|---|
childrenrequired | ReactNode | — | The main content under the toolbar. |
labelsrequired | DesktopWindowLabels | — | The words of the panes, the dividers and the toggles — passed translated. |
sidebarrequired | ReactNode | — | The sidebar's content: a list of places, a Tree. It scrolls by itself. |
className | string | — | Classes for the window ([--kz-window-height:34rem] to size it inside a page). |
contentMin | number | 360 | The least the content keeps when the window is narrow (default 360). |
defaultInspectorOpen | boolean | true | The inspector starts open (default true) when uncontrolled. |
defaultSidebarOpen | boolean | true | The sidebar starts open (default true) when uncontrolled. |
defaultSizes | { sidebar?: number; inspector?: number; } | undefined | — | The sizes in px: the sidebar's, the inspector's. Persist them with onSizesChange. |
inspector | ReactNode | — | The inspector's content: the selected item's details. Without it, a two-pane window. |
inspectorOpen | boolean | — | The inspector is open (controlled). |
onInspectorOpenChange | ((open: boolean) => void) | — | Called when the inspector folds or unfolds. |
onSidebarOpenChange | ((open: boolean) => void) | — | Called when the sidebar folds or unfolds (the toolbar, a divider, the shortcut). |
onSizesChange | ((sizes: { sidebar: number; inspector: number; }) => void) | — | Called with the sizes in px after a drag or a key: persist them. |
shortcuts | { sidebar?: string | false; inspector?: string | false; } | undefined | — | The keys that fold the panes (a chord, or false for none): ⌘B and ⌘I by default. Pass false when the menu bar's commands (useDesktopCommands) own them. |
sidebarOpen | boolean | — | The sidebar is open (controlled). Pair with onSidebarOpenChange: the menu bar's command toggles it too. |
title | ReactNode | — | The toolbar's title, centred in the drag region. |
toolbarEnd | ReactNode | — | Before the inspector's toggle, at the end of the toolbar (actions, search). |
toolbarStart | ReactNode | — | After the sidebar's toggle, at the start of the toolbar (back, forward). |
Accessibility
- The panes are
asidelandmarks named bylabels; the toolbar is the window'sheader, the content itsmain. - The dividers are the only draggable things; the toolbar's drag region is the bar, its title and the gaps — never a control, so every button keeps its click and its focus.
- The focus ring is the macOS one (a 3 px halo of the accent) and shows for the keyboard only.
Keyboard
| Keys | Action |
|---|---|
| Tab to a divider | Focuses it; it is a separator (WAI-ARIA window splitter) with its size as aria-valuenow. |
| ← → | Resizes the pane by 16 px (64 with Shift); the arrow pointing away from the pane's edge grows it, mirrored in right-to-left. |
| Home / End | The smallest or the largest size. |
| Enter on a divider | Folds its pane; the focus goes to the toolbar's button that brings it back. |
| ⌘B / ⌘I | Fold the sidebar and the inspector. |
Best practices
- Use it at a window width of 900 pt or more (Tauri's minimum width): the content keeps 360 pt, and the panes give way to it when the window is narrower.
- Write the commands once with
useDesktopCommands: it registers the chords in the page and returns themenusandhandlersforsetNativeMenuof@krizaka/app-desktop. Then passshortcuts={{ sidebar: false, inspector: false }}to the window so ⌘B and ⌘I are not answered twice. - Keep the sizes: persist
onSizesChange(a preference of the window) and give them back asdefaultSizes. - Fold with the controlled
sidebarOpen/inspectorOpen, so the menu's « Hide sidebar » and the toolbar's button are one state. - Put only what moves the window's content in the toolbar (back, search, the main action); never a form.
- Controls inside use the desktop's density (32 px rows, 13 px text); do not enlarge them for touch.
Related components
Generated from the code of @krizaka/ui 3.65.0: its meta.ts, its examples and its types.Edit this documentation on GitHub