The Editable Design System

A shared starting point for Editable. Simple styles, ready to use and adapt.

Foundations

Colors, fonts, and corner radii come from the shared tokens in src/app.css. These specimens follow the current token values.

Colors and surfaces

Keep each surface with its foreground color. Neutral controls use the muted surface on hover and a 10% foreground tint on press. Primary actions fill with the foreground color; accented actions and editing actions use their respective accent treatments.

Page content

--background / --foreground

Secondary surface

Supporting text

--muted / --muted-foreground

Accented action or highlight

--accent / --accent-foreground

Active editing

--editing / --editing-foreground

Passive editing highlight

--editing-muted / --editing

Borders and dividers


--stroke

Color sets

Each color set assigns every color token, so everything inside it stays readable without changes of its own. Color set 1 is the page; sections switch to set 2 or 3. Editable text on a solid fill, such as a primary button or a highlight, adds ew-native-selection, so its caret and text selection stay visible.

Color set 1

The page.

Muted surface
Primary Accent Secondary

Color set 2

Color set 1 with background and muted swapped, so muted boxes turn white.

Muted surface
Primary Accent Secondary

Color set 3

Color set 1 inverted, for sections that stand out.

Muted surface
Primary Accent Secondary

Font families

Clear words.

font-sans · Inter

Clear words.

font-serif · Libertinos Serif Display

Clear words.

font-mono · IBM Plex Mono

Corner radii

Buttons and pill shells use the button radius. Popovers cap it at 1rem; their inner rows and pill controls subtract 4px padding and a 1px border, clamped at zero. Images use their own radius. Set either token to zero for square corners.

Button

--button-border-radius

Pill interior

Button radius with an inset interior.

Popover row

Button radius capped at 1rem, with an inset row.

Image corners

--image-border-radius

Page limiter

Center each block with mx-auto max-w-7xl, then add px-5 sm:px-7 inside it. The width limit is 80rem (1280px), including side padding: 1.25rem (20px) on small screens and 1.75rem (28px) from the 40rem (640px) breakpoint, at the default root font size.

Page content aligns here.

The shaded edges show the shared page padding. Resize the window to compare.

<div class="mx-auto max-w-7xl">
	<div class="px-5 sm:px-7">
		Page content
	</div>
</div>

Keep full-width backgrounds outside the limiter and choose vertical spacing per block. Narrower reading columns can sit inside this shared page width. Apply the side padding once so adjacent blocks line up.

Content tracks

A track controls how much of the page limiter a block occupies and where it sits. Prose and descriptive listings share narrow left, center, and right tracks, plus a wide layout. Alternating their positions adds variation while keeping shared alignment edges.

Narrow tracks use max-w-4xl (56rem / 896px) inside the max-w-7xl page limiter (80rem / 1280px). These are maximum widths, not fixed proportions. Both include the shared side padding.

Narrow left · max-w-4xl
Content starts at the left page edge.
Narrow center · mx-auto max-w-4xl
Content sits in the center of the page.
Narrow right · ml-auto max-w-4xl
Content ends at the right page edge.
Wide · use the full available content width
Content spans the limiter, inside the shared side padding.
Two columns · descriptive listings · 50:50 after the gap
First column
Second column

Vertical block spacing

Generous spacing separates sections. Compact spacing pulls related blocks together. Each block has its own top and bottom padding, chosen independently.

  • A standalone block has generous padding above and below.
  • In a section, the first block has generous top padding and compact bottom padding.
  • Middle blocks have compact padding above and below.
  • The last block has compact top padding and generous bottom padding.
  • A section containing just one block has generous padding above and below.

Padding per edge

Responsive generous and compact padding values
Viewport widthGenerousCompact
Below 640px4rem · 64px1.5rem · 24px
640px to 1023px6rem · 96px1.5rem · 24px
1024px and above8rem · 128px2rem · 32px

Pixel equivalents assume the default 16px root font size. Adjacent padding adds up: two compact edges create a 48px gap, 64px from 1024px, always wider than the gap between elements inside a block. Two generous edges create a gap of 128px on phones, 192px from 640px, and 256px from 1024px. Only the rows of a plain prose grid sit further apart than two compact edges: with nothing but whitespace between their items, they are 96px apart, 128px from 1024px.

Standalone block

Generous

Independent content

Generous

Three blocks in one section

Generous

First block

Compact
Compact

Middle block

Compact
Compact

Last block

Generous

Shaded bands show the padding at its actual size. Resize the window to compare the scale. Spacing within a block, such as the gap between a heading and a paragraph, is a separate choice.

Sample block implementation

A top-level block receives its section context through mark, aliased to section. As in Prose.svelte, the block derives its top and bottom padding from that context. No extra padding props are needed.

Sample prose block

<script lang="ts">
	import { Node, NodeArrayProperty } from 'svedit';
	import type { DocumentPath, NodeArrayAttachmentContext } from 'svedit';

	let {
		path,
		mark: section = null
	}: {
		path: DocumentPath;
		mark?: NodeArrayAttachmentContext | null;
	} = $props();

	let padding_top_generous = $derived(!section || section.is_start);
	let padding_bottom_generous = $derived(!section || section.is_end);
</script>

<Node {path} class="bg-(--background) text-(--foreground)">
	<div class="mx-auto max-w-7xl">
		<div class={[
			'px-5 sm:px-7',
			padding_top_generous ? 'pt-block-generous' : 'pt-block-compact',
			padding_bottom_generous ? 'pb-block-generous' : 'pb-block-compact'
		]}>
			<NodeArrayProperty
				path={[...path, 'body']}
				class="flex flex-col gap-5 sm:gap-7"
			/>
		</div>
	</div>
</Node>
  • No section mark: both edges are generous.
  • section.is_start: the top edge is generous.
  • section.is_end: the bottom edge is generous.
  • section.is_middle: both edges are compact; the two boundary checks already cover this case.
  • A one-block section is both the start and the end, so both edges are generous.

The app uses shared utilities in src/app.css for the default rhythm: pt-block-generous, pt-block-compact, and their pb- equivalents. They apply the values above to existing elements without adding a wrapper component.

For a custom rhythm, replace the default utility on that edge with explicit responsive classes, such as pt-8 lg:pt-16. Avoid combining a default utility with overrides on the same edge. Keep deliberate exceptions local: flush and full-bleed figures have no vertical padding.

Typography

A shared scale for headings and body text.

display-1

Make room for what matters.

display-2

Make room for what matters.

display-3

Make room for what matters.

display-4

Make room for what matters.

display-5

Make room for what matters.

body-xl

Good design makes everyday things easier to understand. Clear words, comfortable spacing, and a thoughtful hierarchy help people find what they need and decide what to do next.

body-lg

Good design makes everyday things easier to understand. Clear words, comfortable spacing, and a thoughtful hierarchy help people find what they need and decide what to do next.

body-base

Good design makes everyday things easier to understand. Clear words, comfortable spacing, and a thoughtful hierarchy help people find what they need and decide what to do next.

body-sm

Good design makes everyday things easier to understand. Clear words, comfortable spacing, and a thoughtful hierarchy help people find what they need and decide what to do next.

eyebrow

Visual builder

Inline formatting

Emphasis and detail within the flow of a sentence.

Emphasis · em

Start with what people actually need.

Bold · strong

Keep the most important information easy to find.

Code · code

Use font-semibold for item titles.

Highlight · mark

Highlight the part worth remembering, not the whole paragraph.

Card

A 4:3 media area, a display-5 title like heading 4, and an optional description. Shared by gallery cards; width and arrangement belong to the surrounding layout. Linked cards underline the title and make the whole card clickable. Use a div and omit the underline for an unlinked card. Keyboard focus uses a 2px editing-color outline with a 2px offset; scroll containers use an inset outline to prevent clipping.

Buttons

Standard size. Hover, press, or tab to explore the states.

Primary

Primary accent

Secondary

Small buttons

For navigation and compact layouts. Small buttons keep a 36px minimum height on all devices.

Primary

Primary accent

Secondary

Disabled buttons

Buttons and pill controls use 40% opacity with the default cursor. Native disabled buttons skip keyboard focus and suppress hover and press feedback. Menu rows instead use muted text to keep unavailable actions legible.

Primary

Secondary

Small primary

Small secondary

Editing pill

Descriptive listing

A linked row with a semibold title, description, and optional metadata. Hover or press anywhere on the row to show the title underline in full; keyboard focus outlines the whole row. A plain listing uses the same recipe with the description omitted.

Dialog buttons

For the editing dialogs outside the toolbar: the login dialog, the save conflict dialog, and the OAuth consent page. The primary action uses the editing colors, since these dialogs belong to editing rather than to the site.

Primary

Secondary

Icon

Boxed items

Boxed items use ew-muted-surface: they fill with the muted color, and their children swap background and muted, so an eyebrow, inline code, or a secondary button stands out from the box. Accent boxes use ew-accent-surface: they fill with the accent of their color set, and their children get matching tokens, so every recipe inside stays readable.

Personal

Make it your own

Start with the shared styles and adapt them to your site.

Explore buttons

Make it stand out

The accent of the color set, with every recipe inside still readable.

Get started

Editor pills

Compact surfaces for tools, actions, and previews.

Toolbar

Tooltips and shortcut keys

Show on hover and keyboard focus; Escape dismisses. Use separate keycaps for each shortcut key. Mac shortcuts are shown for now.

Edit Command + E

Inline call to action

Variant selector and select parent

Editor forms

Link and media prompts use an opaque surface with 4px padding, no shadow, and the button radius capped at 1rem. Fields and actions subtract the padding and border from that radius. Text inputs and textareas use 16px text and an editing-color border on focus.

Page browser

A muted editing fill marks the result Enter will open. Only keyboard-focused rows get a ring, drawn inside the row to keep tree guides and adjacent rows clear. Search focus uses a thin editing-color border. Action menus reuse the list popover recipe.

List popovers

Compact lists with a shared surface and aligned rows.