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.
--background / --foreground
Secondary surface
Supporting text
--muted / --muted-foreground
--accent / --accent-foreground
--editing / --editing-foreground
--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.
Color set 2
Color set 1 with background and muted swapped, so muted boxes turn white.
Color set 3
Color set 1 inverted, for sections that stand out.
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-border-radius
Button radius with an inset interior.
Button radius capped at 1rem, with an inset row.
--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.
max-w-4xlmx-auto max-w-4xlml-auto max-w-4xlVertical 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
| Viewport width | Generous | Compact |
|---|---|---|
| Below 640px | 4rem · 64px | 1.5rem · 24px |
| 640px to 1023px | 6rem · 96px | 1.5rem · 24px |
| 1024px and above | 8rem · 128px | 2rem · 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
Independent content
Three blocks in one section
First block
Middle block
Last block
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.
Link · a
Explore the typography styles to see how the scale works.
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.
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.
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.
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.
Inline call to action
Variant selector and select parent
Link preview
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.