@kestack/web-platform
v0.0.6
Published
The way to build a kestack app on Kabuki Start in one go: its UI, its PWA, Recipes for whole features, and the app's Document and deploy.
Readme
@kestack/web-platform
The way to build a kestack app on Kabuki Start in one go: its UI, its PWA, Recipes for whole features, and the app's Document and deploy.
Big picture
Every kestack web app used to wire the same things by hand: the Document, the Theme, the PWA, the screens and the deploy. This Package holds them for apps on Kabuki Start, so a new app writes only its pages. Kabuki Start serves the app and knows none of this; the Web Platform is where the choices are made (ADR 0010). It replaces @kestack/ui-toolkit and @kestack/pwa-toolkit.
It has four modules, each depending only on the ones before it:
ui: what the user sees and touches:ui/theme,ui/components/*,ui/viewers/*,ui/formandui/input(device, gestures, keys, sounds).pwa: the app installable, working offline and updated in place.pwa/vitebuilds the worker and manifest,pwa/workerruns in it,pwa/rpc/*talks to it, andpwais the page's side:PwaHead,Pwa, the Update and Install Prompts and the extras.recipes/*: whole features built fromuiandpwa: the Frame, the Account Switcher, the Vim status line, Local sign-in, Kabuki's Devtools, the Thumb Picker, swipe rows, and the touch layers that keep a Drawer and its field above the on-screen keyboard.app: putting it together:app/viteis the one Vite plugin an app lists,app/documentthe Document it passes toStart.app, andapp/alchemyhow it is deployed on its Stages.
Why the Web Platform may break while the Toolkits keep what persists is ADR 0006. It ships built dist/ (from vp pack). Terms are in CONTEXT.md; the PWA's decisions are in docs/adr/.
Install
pnpm add @kestack/web-platform @kestack/auth-toolkit react react-dom @kestack/use-gesture
pnpm add -D @tailwindcss/vite @vitejs/plugin-reactAn app's stylesheet imports @kestack/web-platform/ui/theme/global.css, which also points Tailwind at the Package's own classes. app/vite bundles the Package for the server too; an app that lists start() and pwa() itself adds @kestack/web-platform to ssr.noExternal.
@kestack/auth-toolkit:./recipes/account-switcherfollows itsAccounts, by the sign-in service's cookie or as Fake Accounts.react,react-dom: every component, Recipe and the Document render with React 19.@kestack/use-gesture: the platform-free gesture core that./ui/input's web gestures and the Thumb Picker run on.@kestack/use-keys(optional): the Binding and Shortcut types./ui/inputshows, and thecreateKeyskeyboard./recipes/keyboardshows../ui/theme/global.cssdraws everykeys.Surfacefrom its data attributes, so akeys.Providerneeds noappearance(also alone as./ui/theme/keys.css).effect(optional): the PWA subpaths and the diff and source viewers are built on it.vite(optional): needed by./pwa/viteand./app/vite.@tailwindcss/vite,@vitejs/plugin-react(optional): the Tailwind and React plugins./app/viteadds.alchemyand@alchemy.run/cloudflare(optional): the Cloudflare Website and state./app/alchemydeploys with.laymos(optional): the file diff and change types the diff, git-changes and source-explorer viewers read.use-effect-ts(optional): runs./ui/viewers/source-explorer's loaders inside React.@kestack/kabuki(optional): the Start parts./app/document's Document draws, the Start app./pwa/vitebuilds beside, and the Actor and View./recipes/account-switcheris.
Exports
@kestack/web-platform/ui/theme
| Export | What it does |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| createTheme | Makes the app's Theme: useTheme, the head Script that sets it before paint, and the StatusBar strip. |
| FontPreload | Preloads Inter's Latin file from the head, the same file font-inter.css uses, so the first paint is already in Inter. |
| themeFromCookies | Reads the Theme from a Cookie header string; dark when it is missing or unknown. |
| THEME_COOKIE | The cookie's name, kui-theme. |
@kestack/web-platform/ui/theme/*.css
| Export | What it does |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------- |
| global.css | The one stylesheet to import: Tailwind, the tokens, the dark, keyboard: and touch: variants, and the rest. |
| theme.css | The design tokens as Tailwind theme variables; included by global.css. |
| sources.css | Points Tailwind at the Package's built components, form, Recipes, client and PWA; included by global.css. |
| typography.css | The Tailwind typography plugin; included by global.css. |
| shiki.css | Dark-theme colors and diff line marks for highlighted code; included by global.css. |
| font-inter.css | Inter and JetBrains Mono as variable fonts, and Inter Fallback sized to Inter's metrics; import it beside global.css. |
@kestack/web-platform/ui/components/*
Owned shadcn copies on Base UI, one subpath per file, such as @kestack/web-platform/ui/components/button.
| Export | What it does |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| accordion: Accordion, AccordionItem, AccordionTrigger, AccordionContent | Sections that expand and collapse. |
| alert-dialog: AlertDialog and its Trigger, Content, Header, Footer, Title, Description, Media, Action, Cancel, Overlay, Portal | A modal that asks to confirm or cancel. |
| alert: Alert, AlertTitle, AlertDescription, AlertAction | An inline message box. |
| aspect-ratio: AspectRatio | Keeps its content at one width-to-height ratio. |
| attachment: Attachment, AttachmentGroup, AttachmentMedia, AttachmentContent, AttachmentTitle, AttachmentDescription, AttachmentActions, AttachmentAction, AttachmentTrigger | A file attachment card with its state and actions. |
| avatar: Avatar, AvatarImage, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarBadge | A user's picture or initials, alone or stacked. |
| badge: Badge, badgeVariants | A small label, and its classes for other elements. |
| breadcrumb: Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis | The path to the current page. |
| bubble: BubbleGroup, Bubble, BubbleContent, BubbleReactions | Chat bubbles with reactions. |
| button-group: ButtonGroup, ButtonGroupSeparator, ButtonGroupText, buttonGroupVariants | Buttons joined into one control. |
| button: Button, buttonVariants | A button, and its classes for links that look like one. |
| calendar: Calendar, CalendarDayButton | A month grid for picking dates, on react-day-picker. |
| card: Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent | A bordered surface with header, body and footer. |
| carousel: Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext, useCarousel | Slides that scroll sideways, on Embla. |
| chart: ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, ChartStyle | Recharts wrapped with the Theme's colors. |
| checkbox: Checkbox | A checkbox. |
| collapsible: Collapsible, CollapsibleTrigger, CollapsibleContent | One region that opens and closes. |
| combobox: Combobox, ComboboxInput, ComboboxContent, ComboboxList, ComboboxItem, ComboboxGroup, ComboboxLabel, ComboboxCollection, ComboboxEmpty, ComboboxSeparator, ComboboxChips, ComboboxChip, ComboboxChipsInput, ComboboxTrigger, ComboboxValue, useComboboxAnchor | A text input that filters a list, with single or chip selection. |
| command: Command, CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandShortcut, CommandSeparator | A searchable command list, inline or in a dialog, on cmdk. |
| context-menu: ContextMenu and its Trigger, Content, Item, CheckboxItem, RadioGroup, RadioItem, Label, Separator, Shortcut, Group, Portal, Sub, SubTrigger, SubContent | A menu opened by right-click or long press. |
| dialog: Dialog, DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, DialogClose, DialogOverlay, DialogPortal | A modal window. |
| direction: DirectionProvider, useDirection | Sets and reads left-to-right or right-to-left for the components inside. |
| drawer: Drawer and its Trigger, Content, Header, Footer, Title, Description, Close, Overlay, Portal, SwipeHandle | A panel that slides in from an edge and swipes away. |
| dropdown-menu: DropdownMenu and its Trigger, Content, Item, CheckboxItem, RadioGroup, RadioItem, Label, Separator, Shortcut, Group, Portal, Sub, SubTrigger, SubContent | A menu opened from a button. |
| empty: Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent, EmptyMedia | What a screen shows when it has nothing yet. |
| field: Field, FieldLabel, FieldDescription, FieldError, FieldGroup, FieldLegend, FieldSeparator, FieldSet, FieldContent, FieldTitle | A form control with its label, hint and error. |
| file-tree: Tree, Folder, File, CollapseButton | A tree of folders and files that expand and collapse. |
| google-button: GoogleButton | The "Sign in with Google" button. |
| hover-card: HoverCard, HoverCardTrigger, HoverCardContent | A card shown while pointing at a link. |
| input-group: InputGroup, InputGroupAddon, InputGroupButton, InputGroupText, InputGroupInput, InputGroupTextarea | An input or textarea with text, icons or buttons attached. |
| input-otp: InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator | A one-time code entered one character per box. |
| input: Input | A text input. |
| item: Item, ItemMedia, ItemContent, ItemActions, ItemGroup, ItemSeparator, ItemTitle, ItemDescription, ItemHeader, ItemFooter | A list row with media, text and actions. |
| kbd: Kbd, KbdGroup | Keyboard keys as they read on the device. |
| label: Label | A form label. |
| marker: Marker, MarkerIcon, MarkerContent, markerVariants | A short line of status text with an icon. |
| menubar: Menubar and its Menu, Trigger, Content, Item, CheckboxItem, RadioGroup, RadioItem, Label, Separator, Shortcut, Group, Portal, Sub, SubTrigger, SubContent | A desktop-style menu bar. |
| message-scroller: MessageScrollerProvider, MessageScroller, MessageScrollerViewport, MessageScrollerContent, MessageScrollerItem, MessageScrollerButton, useMessageScroller, useMessageScrollerScrollable, useMessageScrollerVisibility | A chat list that stays at the newest message and offers a jump back to it. |
| message: MessageGroup, Message, MessageAvatar, MessageContent, MessageHeader, MessageFooter | One chat message with its sender. |
| native-select: NativeSelect, NativeSelectOption, NativeSelectOptGroup | The browser's own select, styled. |
| navigation-menu: NavigationMenu and its List, Item, Trigger, Content, Link, Indicator, Positioner, navigationMenuTriggerStyle | Site navigation with dropdown panels. |
| pagination: Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis | Page number links. |
| popover: Popover, PopoverTrigger, PopoverContent, PopoverHeader, PopoverTitle, PopoverDescription | Content floating next to its trigger. |
| progress: Progress, ProgressTrack, ProgressIndicator, ProgressLabel, ProgressValue | A progress bar. |
| questionnaire: Questionnaire and its Item, Title, Description, Choices, Choice, ChoiceDescription, Input, Error, Progress, Actions, Previous, Next, Skip, Submit | A form asked one question at a time. |
| radio-group: RadioGroup, RadioGroupItem | One choice out of several. |
| resizable: ResizablePanelGroup, ResizablePanel, ResizableHandle | Panels the user resizes by dragging. |
| scroll-area: ScrollArea, ScrollBar | A scrolling region with styled scrollbars. |
| select: Select, SelectTrigger, SelectValue, SelectContent, SelectGroup, SelectLabel, SelectItem, SelectSeparator, SelectScrollUpButton, SelectScrollDownButton | A styled select. |
| separator: Separator | A line between content. |
| sheet: Sheet, SheetTrigger, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription, SheetClose | A dialog that slides in from a side. |
| skeleton: Skeleton | A placeholder while content loads. |
| slider: Slider | A value picked by dragging. |
| sonner: Toaster, toast | Toasts on sonner, and the function that shows one. |
| spinner: Spinner | A loading spinner. |
| switch: Switch | An on/off switch. |
| table: Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption | A data table. |
| tabs: Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants | Tabbed panels. |
| textarea: Textarea | A multi-line text input. |
| toast: Toaster, Toast, ToastProvider, ToastViewport, ToastPortal, ToastContent, ToastTitle, ToastDescription, ToastAction, ToastClose, toast, createToastManager, useToastManager | Toasts on Base UI, with a manager to show them from anywhere. |
| toggle-group: ToggleGroup, ToggleGroupItem | Toggle buttons where one or several are on. |
| toggle: Toggle, toggleVariants | A button that stays pressed. |
| tooltip: Tooltip, TooltipTrigger, TooltipContent, TooltipProvider | A short hint shown on hover or focus. |
@kestack/web-platform/ui/viewers/diff-viewer
| Export | What it does |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| DiffViewer | Shows one file's diff, side by side or unified; its options can be held by the caller to keep. |
| defaultDiffOptions | The options a diff opens with: split, unwrapped, both sides, folded. |
@kestack/web-platform/ui/viewers/file-tree
| Export | What it does |
| --------------- | --------------------------------------------------------------- |
| FileTree | Shows a list of file paths as a tree of folders. |
| expandAll | Every folder path in a list of files, to open the whole tree. |
| expandToDepth | The folder paths no deeper than a given depth. |
| expandTo | The folder paths that lead to one file, to reveal it. |
| toggleSubtree | Opens or closes one folder together with every folder under it. |
@kestack/web-platform/ui/viewers/git-changes
| Export | What it does |
| -------------------- | ------------------------------------------------------------------------------------ |
| ChangeBadge | The new or mod mark for an added or modified item. |
| ChangesMenu | The menu that shows or hides changes and picks the branch they are measured against. |
| changeSurfaceClass | Border classes that mark an added or modified surface. |
| defaultGitOptions | Changes shown, unchanged items kept. |
| uncommittedBaseRef | The base ref, HEAD, that means the working tree's uncommitted changes. |
| rollUpChanges | Each owner's change status from the files it owns. |
| changedPathsUnder | The changed files under one or more path prefixes. |
@kestack/web-platform/ui/viewers/json
| Export | What it does |
| ------------ | ----------------------------------------- |
| JsonViewer | Shows a JSON value as a collapsible view. |
| JsonTree | Shows a JSON value as an expandable tree. |
| JsonEditor | Edits JSON text in CodeMirror. |
@kestack/web-platform/ui/viewers/markdown-viewer
| Export | What it does |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| MarkdownViewer | Renders Markdown, with GitHub tables and lists, as typography, one top-level block at a time; it can wrap each block, and leave the first ones out. |
| blocksOf | Cuts Markdown into the top-level blocks MarkdownViewer draws, only where a block surely starts. |
@kestack/web-platform/ui/viewers/source-explorer
| Export | What it does |
| ---------------- | -------------------------------------------------------------------------- |
| SourceExplorer | A dialog that opens one scope's documentation and files, with their diffs. |
@kestack/web-platform/ui/viewers/source-viewer
| Export | What it does |
| -------------- | ------------------------------------------------------------------ |
| SourceViewer | Shows highlighted source with line numbers, ranges and diff lines. |
@kestack/web-platform/ui/components/hooks/use-element-width
| Export | What it does |
| ----------------- | -------------------------------------- |
| useElementWidth | An element's content width, kept live. |
@kestack/web-platform/ui/components/hooks/use-mobile
| Export | What it does |
| -------------- | --------------------------------------------------------------------------- |
| useIsMobile | Whether the screen is phone-sized; wide on the server and during hydration. |
| MOBILE_QUERY | The media query useIsMobile reads. |
@kestack/web-platform/ui/components/hooks/use-scroll
| Export | What it does |
| ----------- | ----------------------------------------------------------------------------------------------------------- |
| useScroll | Keeps a scroller pinned to its bottom as content grows, until the user scrolls up; scrolls back on request. |
@kestack/web-platform/ui/components/utils
| Export | What it does |
| ------ | ---------------------------------------------------------- |
| cn | Joins class names and merges conflicting Tailwind classes. |
@kestack/web-platform/ui/components/scroll-styles
| Export | What it does |
| ----------------- | ----------------------------------------------------------------------------------- |
| scrollbarStyles | Classes for thin scrollbars on devices with a fine pointer. |
| scrollBar | The same classes for a default, small or hidden scrollbar, in one or both axes. |
@kestack/web-platform/ui/components/shadow-dom
| Export | What it does |
| ---------------- | -------------------------------------------------------------------------------- |
| ShadowScope | Renders its children in a shadow root with the given CSS and a portal container. |
| useShadowScope | The shadow root, portal container and Theme of the nearest ShadowScope. |
@kestack/web-platform/ui/form
| Export | What it does |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| useAppForm | A TanStack Form hook whose fields come bound to the components: TextField, TextareaField, SelectField, SwitchField, DatePickerField, and SubmitButton. |
| withForm | Builds a piece of form that takes the form as a prop. |
| formOptions | TanStack Form's formOptions, to share options between forms. |
@kestack/web-platform/ui/input
How a person drives the app: the device, touch gestures, keys and sounds.
| Export | What it does |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
| play | Plays one short sound made by the browser, such as tick, arm, confirm or wrong; the web's twin of haptics. |
| DEVICE_SCRIPT | Inline script that marks <html> with data-keyboard and data-touch before the first paint; ./app/document's Document already adds it. |
| useDevice | Whether this device has a keyboard, a touch screen or both, live; a key pressed outside a text field turns keyboard on. |
| GestureProvider | Follows every touch and pen finger in the Gesture Zones inside it; put one at the app's root. |
| GestureZone | An area where the app may own touch; zones nest, and the browser scrolls when no listener takes the touch. |
| useGesture | Every finger its nearest Gesture Zone hears, with motion values that follow it; never says what the Gesture means. |
| useSwipe | The Swipe Recognizer: fingers moving one way, Tracking with motion values, then a Commit or a Cancel. |
| useSidebar | A sidebar that follows a Swipe open from anywhere or an edge, and back, then settles open or closed. |
| usePullToRefresh | Pull to refresh: a Swipe down at a list's top, followed with resistance, refreshing past a distance. |
| BindingKeys | A Binding's keys step by step, such as ⌘ K, shown only where there is a keyboard unless always. |
| recorded | Turns one recorded key press into a Shortcut, with Cmd on a Mac and Ctrl elsewhere as mod. |
@kestack/web-platform/pwa
The PWA on the page.
| Export | What it does |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| PwaHead | The PWA's head tags for a Kabuki Start Document's head: the manifest link, the Apple icon and title, the Splash and the Build ID. |
| Pwa | Around the app in the Document: registers the worker, shows the Update Prompt while a new version waits, and offers install once. |
| PwaProvider | Registers the worker after mount and gives usePwa its Status; Pwa renders it. |
| pwaHead | Head tags: manifest link, Apple icon, and meta tags for the Build ID, build time and commit. |
| usePwa | The Status with checkForUpdate and applyUpdate (Unsupported before mount), and the page's version. |
| updateToNewest | Once per page load, from anywhere: checks for a newer build and applies it once downloaded, so every page reloads into it. |
| clearRuntimeCache | Deletes every Runtime Cache; works outside PwaProvider, for example in sign-out. |
| UpdatePrompt | Persistent toast while an update is ready; accepting it reloads every page into it. |
| splashHead | The iOS startup image tags, one per screen iOS matches. |
| splashScreens | Each splash image's size, its /splash/<w>x<h>.png path, and the screen it is for, for an app to draw them. |
| useInstall | Install state with prompt and dismiss (remembered 30 days); call it in the root to catch the browser's offer. |
| InstallPrompt | Bottom sheet on small screens, corner card on larger ones, manual steps on iOS Safari. |
| Install | The Effect service behind useInstall, with its layer. |
| InstallState | Tagged enum Unsupported, Available, ManualIos, Installed, Dismissed. |
| IosSteps | The Share, then Add to Home Screen steps InstallPrompt shows on iOS Safari, for an app's own install screen. |
| isIosSafari | Whether a navigator is Safari on iOS or iPadOS, where install has no API and only those steps work. |
| useOnline | Whether the browser is online; true before mount. |
| OfflineIndicator | "You're offline" pill in a live region, stacked above sheets. |
| Online | The Effect service behind useOnline, with its layer. |
| useDisplayMode | The display mode, such as browser or standalone; browser before mount. |
| DisplayMode | The Effect service behind useDisplayMode, with its layer. |
| useStoragePersistence | Persisted flag with persist and estimate; null while unknown. |
| StoragePersistence | The Effect service behind useStoragePersistence, with its layer. |
@kestack/web-platform/pwa/splash
splashHead and splashScreens alone, without the page's React and worker, for a script that draws the Splash images.
@kestack/web-platform/pwa/vite
| Export | What it does |
| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| pwa | Vite plugins that emit the manifest from the app's name, pick the Precache, compute the Build ID, build the worker (or the Kill Switch) and add no-cache rules to _headers. |
| noPwa | The Vite plugin for an app that is no PWA: PwaHead and Pwa draw nothing of their own, and there is no worker or manifest. |
@kestack/web-platform/pwa/worker
| Export | What it does |
| ------------------ | ----------------------------------------------------------------------------------------------------------------- |
| runServiceWorker | Starts the worker; call it once at the top of the entry, optionally with a layer such as a Worker Server. |
| WorkerHost | Service given to that layer: the worker's Build ID and a stream of every message event that is not a Command. |
@kestack/web-platform/pwa/rpc/worker
| Export | What it does |
| -------------------- | ------------------------------------------------------------------------------------------------------ |
| WorkerServer.layer | Serves an Effect RpcGroup inside the worker; provide its handlers and pass it to runServiceWorker. |
@kestack/web-platform/pwa/rpc/client
| Export | What it does |
| -------------------- | ----------------------------------------------------------------------------------------- |
| WorkerClient.make | Scoped Effect that opens this page's Worker Client to the Worker Server. |
| WorkerClient.layer | The same client as a Layer under your own service tag. |
| VersionSkew | Tagged error a call fails with when the worker belongs to another Build ID than the page. |
@kestack/web-platform/recipes/frame
| Export | What it does |
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| AppShell | The Frame: an optional Sidebar, an optional header with title and actions, the page, with vim as a floating window, the same in Vim Mode or out of it, with a status line while it is on, and column to wrap the header and page in one element; on a narrow screen the Sidebar shows in its sidebarMode, slide beside the page or fullscreen in its place, under a finger. |
| useAppShell | open, setOpen, toggle and isMobile, inside an AppShell. |
| SidebarGroup, SidebarGroupContent, SidebarGroupLabel | Labeled groups for custom Sidebar content. |
| SidebarMenu, SidebarMenuItem, SidebarMenuButton | Menu rows; a button shuts the Sidebar when tapped on a phone. |
| SidebarMenuSub, SidebarMenuSubItem, SidebarMenuSubButton | Nested menu rows. |
| SidebarInput, SidebarMenuSkeleton | A search input and a loading row for the Sidebar. |
@kestack/web-platform/recipes/account-switcher
The Account Switcher of a Kabuki app: a Child of the root page, its View in the header, its list for a sheet, and CurrentAccount for every page.
| Export | What it does |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| AccountSwitcherLive | The Child to place on the root page, given maxAccounts: follows auth-toolkit's Accounts, shows a switch once under way 150 ms and for at least 300 ms, and runs switch, add and sign-out one at a time. |
| AccountSwitcherView | Its slot in the header, always the width of "Sign in": the button signed out, the Account menu signed in, the remembered avatar or nothing while checking. |
| AccountSwitcherList | The Account menu as a list for a sheet, such as a Drawer, from the same Instance: the Active Account, the others to switch to, adding one and signing out. Nothing unless signed in. |
| CurrentAccount | The Capability the root page Provides with CurrentAccount.layer: who is signed in and the switch on screen as a stream, and Requests to sign in, switch and sign out. |
@kestack/web-platform/recipes/keyboard
| Export | What it does |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| KeysStatusBar | The Vim window's status line for an app's keys: the Mode, the path of Surfaces the user can be in, the Sequence under way, help, and a button to turn Vim Mode off. |
| KeysHelp | A help sheet of every Action that works where the user is, drawn while its Isolated Surface is Active; Escape closes it, and finalFocus={false} leaves focus to use-keys. |
| SurfaceScrim | A scrim behind an Isolated Surface the app draws itself. |
@kestack/web-platform/recipes/local-sign-in
| Export | What it does |
| ------------- | --------------------------------------------------------------------------------------------- |
| LocalSignIn | Asks who to sign in as on the Local Backend, with one-tap presets and an email and name form. |
@kestack/web-platform/recipes/devtools
The Dock devtools sit in. Kabuki's Inspector is in @kestack/devtools-kabuki/inspector.
| Export | What it does |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| DevtoolsDock | Wraps a page with devtools opened from a corner button of one fixed size and place, or Ctrl+/ unless shortcut is off: full screen, a resizable bar or side panel, or a floating window, kept across reloads; phones get only full screen and the bar, or nothing with onPhones off. With scrolls="window", a page that scrolls the window stays in the document's flow, the bar or panel fixed over it. |
@kestack/web-platform/recipes/thumb-picker
| Export | What it does |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| ThumbPicker | Picks from a menu by a thumb's swipe while another finger holds still: up and down Step, right opens, left goes back, lifting chooses. |
@kestack/web-platform/recipes/swipe-row
| Export | What it does |
| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SwipeRow | A row one finger swipes aside, left or right, showing what it does behind it; past 40% of its width or on a flick it slides out and closes its gap. A mouse never swipes it. |
@kestack/web-platform/recipes/touch-layers
| Export | What it does |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Drawer | A layer rising from the bottom, closed by a swipe down or a tap outside, kept above the on-screen keyboard with its head in sight and its focused field scrolled into view. |
| TopDialog | A layer under the status bar with a list to search, in what the keyboard leaves seen, never in motion. |
| LayerHeader | The head of a Drawer or Top Dialog: its title, a way back when given one, and in a Top Dialog a way to close it. |
| raiseKeyboard | Raises the on-screen keyboard in the tap that opens a layer whose field takes focus once it is drawn. |
| useVisibleViewport | The CSS variables a layer fixed to the page sits by: --visible-top, --visible-height, --visible-bottom (what the keyboard hides of the page's foot) and --visible-safe-bottom, read on the browser's own viewport and focus events; keeps a focused field in the layer in sight as it takes focus and as the keyboard takes room. |
| useKeyboardOver | What the keyboard hides of a box in the page's flow, how tall a layer at its foot may be, and whether the keyboard is up; keeps a focused field in the layer in sight. |
| visibleOf | What can be seen and how much the keyboard hides, from the layout viewport's height and visualViewport: the measure useVisibleViewport uses. |
| overBox | How much of a box the keyboard hides, and the room above it: the measure useKeyboardOver uses. |
| SWIPE_DISMISS | How far, 80px, or how fast, 500px/s, a swipe down closes a Drawer: for a sheet of an app's own to dismiss alike. |
@kestack/web-platform/app/vite
| Export | What it does |
| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| app | Every Vite plugin a kestack app needs: Tailwind, React, Kabuki Start and the PWA (pwa: false for none), with the Web Platform bundled for the server. Under vite dev it signs in with auth-toolkit's Fake, serves every backend at /rpc-<name> on the site's origin and pipes a Kabuki Session's socket at /__devtools/kabuki/connect to the DevTools Server (devtools, http://127.0.0.1:14400 by default, false for none), nothing else of DevTools, and it fails any build that carries the Fake. |
@kestack/web-platform/app/document
| Export | What it does |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| document | The Document for Start.app({ document }): Start's head and scripts, Inter preloaded, the Theme and the device marked before the first paint, the PWA when the build has it, and the app's own head. |
@kestack/web-platform/app/alchemy
| Export | What it does |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| Site | A Kabuki Start app as a Cloudflare Website: on its domain on a deployed Stage, on Portless's port under alchemy dev. |
| State | Where alchemy keeps a Stack's state: Cloudflare's state store for a deployed Stage, .alchemy/state on disk for anyone's own. |
| where | Where the Stage being run serves the site and its API Workers, once it is checked it may be run. |
| isDeployedStage | Whether a Stage reaches the world: prod, demo or a pull request's prN. |
| assertStageIsSafe | Refuses a deployed Stage unless ALLOW_DEPLOY=true. |
| portlessPort | The PORT portless run gives pnpm dev; throws when there is none. |
| backend | A backend Worker's build: auth-toolkit's Fake under alchemy dev (unless KESTACK_AUTH=real), and failing elsewhere if it carries it. |
Usage
A kestack app
An app writes its Vite config, its src/app.tsx and its alchemy.run.ts; the Web Platform gives the rest, and the app is a PWA. Lifted from apps/code.
// vite.config.ts
plugins: [app({ name: 'Code', platform: cloudflare(), app: 'src/app' })],
// src/app.tsx
export default Start.app({
router,
view: Shell.view,
document: document({ theme: Theme, head: <link rel="icon" href="/favicon.svg" /> }),
layer,
});
// alchemy.run.ts
export default Stack('Code', { providers, state: State }, Site({ host: 'code', zone: 'kishore.app' }));- Start builds the client and the Worker;
pwa()then writessw.js,manifest.webmanifestand a_headersblock withCache-Control: no-cachefor both into the client's folder. The worker entry is the app'ssrc/sw.tsif it has one, or a built-in one. nameis all the manifest needs: its colors are the default Theme's, and its icons areicon-192.png,icon-512.png,icon-maskable-512.pngandapple-touch-icon.pnginpublic/; the build warns about any that is missing.pwa: { manifest }overrides any field. The Install Prompt is titled with the name, and iOS labels the home-screen icon withshort_name, elsename.pwa: { splash: true }lists the iOS startup images,/splash/<w>x<h>.pngfor each ofsplashScreens, which the app draws intopublic/. It turns onapple-mobile-web-app-capable, which iOS shows them only with; the Status Bar Surface keeps the status bar on the Theme.- How each page works offline follows from its Render Mode, as Kabuki Start's Pages (
/_kabuki/pages) say. On install the worker saves the Pages, the App Shell and every static page that is one URL ([ADR 0010](../../docs/adr/0010-
