The "edit one thing" page: a sticky publish bar (back, title, save status, actions), a wide content canvas, and a side panel of folding meta boxes — status, permalink, categories, SEO — that remember whether they're open. Below 44rem (704px) of its own width the panel opens as a non-modal sheet; focus mode hides the panel and fades the bar.
Installation
The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";
Usage
import { EditorLayout, EditorBar, SaveStatus, EditorCanvas, EditorPanel, EditorPanelSection } from "@vamos-ui/react";
<EditorLayout>
<EditorBar
back={{ href: "/posts", label: "Posts" }}
title={title}
status={<SaveStatus state="saved" at={savedAt} />}
actions={<Button size="sm" variant="primary">Publish</Button>}
/>
<EditorCanvas width="prose">{editor}</EditorCanvas>
<EditorPanel>
<EditorPanelSection id="status" title="Status & visibility" summary="Draft · Public" defaultOpen>…</EditorPanelSection>
<EditorPanelSection id="permalink" title="Permalink" summary="/blog/spring-menu">…</EditorPanelSection>
</EditorPanel>
</EditorLayout>Autosave scheduling (debounce, conflicts) is your app's job — feed the result into SaveStatus.
Examples
Save status
Every SaveState. A polite status; the error is an alert with Retry. "Saved 2 min. ago" uses Intl.RelativeTimeFormat. While dirty or saving it guards beforeunload (guard={false} to opt out).
- dirty
- Unsaved changes
- saving
- Saving…
- saved
- Saved 2 min. ago
- saved (hours)
- Saved 3 hr. ago
- error
- Couldn't save
- offline
- Offline · changes kept locally
Panel tabs
tabs splits the panel into Document / Block; put each tab's sections in an EditorPanelContent. tone="scene" drops the canvas surface.
Hi {{first_name}},
Your Northwind plan renews on {{renewal_date}}. Nothing changes unless you want it to — here's a summary of what you're using today.
Reply to this email or book a call with {{owner_name}} if you'd like to review your plan.
Focus mode
Hides the panel and fades the bar until the pointer moves; ⌘/Ctrl+Shift+F toggles, Esc leaves. Hide the App shell sidebar from onFocusModeChange.
Senior product designer
You'll own the design of our scheduling product end to end — from the first sketch with a customer to the last pixel in production. We're a team of nine designers across Istanbul, Lisbon and remote, and we ship every week.
You have five or more years of product design experience, a portfolio that shows how you think, and you're comfortable presenting to customers.
Narrow and read-only
On narrow layouts the panel toggle opens a non-modal sheet, secondary actions hide (mirror them in menu). readOnly hides actions and disables every panel control.
Reseller agreement
1. Territory. Atlas Distribution is appointed as a non-exclusive reseller of the Products in the EMEA region for an initial term of 24 months.
1. Territory. Atlas Distribution is appointed as a non-exclusive reseller…
API
EditorLayout
| Prop | Type | Default |
|---|---|---|
panelOpen · defaultPanelOpen · onPanelOpenChangePanel visibility; drives the sheet on narrow layouts. | boolean | true |
panelWidth · defaultPanelWidth · onPanelWidthChangePanel width in px. | number | 320 |
resizableDrag or arrow-key the panel edge. | boolean | false |
focusMode · defaultFocusMode · onFocusModeChangeDistraction-free mode. | boolean | false |
readOnlyLocked version: bar actions hidden, panel disabled. | boolean | false |
scrollPage scroll with sticky bar/panel, or fill the parent with independently scrolling panes. | "page" | "panes" | "page" |
stickyTopSticky offset in page mode. | number | string | "var(--topbar-h)" |
shortcuts⌘/Ctrl+Shift+, toggles the panel; ⌘/Ctrl+Shift+F focus mode. | boolean | true |
labelsOverrides for the editor namespace. | EditorLayoutLabels | — |
EditorBar
| Prop | Type | Default |
|---|---|---|
backBack link; the label hides on narrow layouts. | { href: string; label: string } | — |
title · statusDocument title and a SaveStatus. | ReactNode | — |
actionsPrimary actions, always shown (hidden when readOnly). | ReactNode | — |
secondaryActionsHidden below 640px — mirror them in menu. | ReactNode | — |
menu"⋯" menu at the end. | MenuEntry[] | — |
panelTogglePanel toggle (aria-expanded, aria-controls). | boolean | true |
focusToggleA visible focus-mode button. | boolean | false |
SaveStatus
| Prop | Type | Default |
|---|---|---|
stateWhere the changes are. | "idle" | "dirty" | "saving" | "saved" | "error" | "offline" | — |
atLast save, for "Saved 2 min. ago". | Date | number | — |
onRetryRetry button in the error state. | () => void | — |
guardbeforeunload warning while dirty or saving. | boolean | true |
nowFixed reference time (docs, tests). | Date | number | — |
EditorCanvas
| Prop | Type | Default |
|---|---|---|
width720 / 1080 px / full. | "prose" | "wide" | "full" | "prose" |
toneCanvas background. | "surface" | "scene" | "surface" |
noticeBanner above the content (read-only Alert). | ReactNode | — |
loadingSkeleton title and paragraphs. | boolean | — |
EditorPanel · EditorPanelContent
| Prop | Type | Default |
|---|---|---|
tabsPanel tabs; EditorPanelContent value matches an id. | { id: string; label: string }[] | — |
value · defaultValue · onValueChangeSelected tab. | string | — |
footerSticky footer inside the panel. | ReactNode | — |
aria-labelName of the complementary landmark. | string | "Settings" |
EditorPanelSection
| Prop | Type | Default |
|---|---|---|
idOpen state is remembered per id (localStorage). | string | — |
title · summaryHeader and the one-line summary shown while folded. | ReactNode | — |
defaultOpen · open · onOpenChangeOpen state. | boolean | false |
actionsControls next to the toggle. | ReactNode | — |
headingLevelHeading level. | 2 | 3 | 2 |