photo.webp
1920×1080 · 214 KB
The shared baseline for every app we build: design tokens, components, forms and small JavaScript behaviours, with light and dark themes. Load two files from this site and your app looks and works like the rest of the family.
Add the stylesheet and script to your page. Both are served from this site with CORS enabled, so any domain can use them. Everything under /v1/ stays backwards compatible; breaking changes ship as /v2/.
Prefer a classic script (no modules)? Use ds.iife.js instead. Both expose the same API on window.DS.
Keep the URL in an env variable so local development can point at a local copy of the design system:
Colors are defined with light-dark(), so the whole system follows the OS theme by default. Set data-theme="light" or data-theme="dark" on <html> to force one. Any button with data-theme-value switches and remembers the theme.
To rebrand an app, override tokens after loading ds.css:
Use tokens, never raw hex values, so components adapt to both themes. Values below update when you switch theme.
System UI font first (SF Pro on Apple devices), Inter everywhere else. Base size is 14px with a 1.45 line height.
Body text at 14px. Links use the accent color, bold, inline code and ⌘ K.
Hint text: 12px muted, for help under fields.
| Token | Size | Use |
|---|---|---|
--text-xs | 11px | Range labels, menu labels |
--text-sm | 12px | Hints, badges, meta |
--text-md | 13px | Buttons, labels, controls |
--text-base | 14px | Body |
--text-lg | 17px | Card and dialog titles |
--text-xl | 22px | Page titles |
--text-2xl / --text-3xl | 28px / 40px | Marketing headings |
A 4px grid. Use var(--space-*) for padding, margins and gaps.
Pill-shaped buttons. The default is a tinted secondary button; add a modifier for other styles.
Square buttons for a single icon. Always give them an aria-label. Use aria-pressed for toggles.
Small pill buttons for presets and filters. Mark the active one with aria-pressed="true".
Inline messages. Add a button with data-ds-dismiss to let people close it.
Heads up
Your images are processed on this device and never uploaded.
12 images converted, 4.2 MB saved.
Storage is almost full. Older history items will be removed.
Conversion failed
This file appears to be damaged.
Elevated containers. Use .surface alone for a plain panel with your own layout.
1920×1080 · 214 KB
Saved on this device
No shadow, for nesting inside a surface.
Add data-ds-tabs to a role="tablist". Each tab points at its panel with aria-controls. Arrow keys move between tabs.
A .dropdown with a toggle (data-ds-toggle="dropdown") and a .menu. Closes on outside click, Esc or choosing an item. Use .dropdown-end to align right.
Built on the native <dialog> element: focus trapping and Esc come for free. Open with data-ds-open="#id", close with data-ds-close. data-ds-dismissible also closes it on a backdrop click.
The returnValue of the dialog is the value of the data-ds-close button that closed it, so you can listen for close:
Short, non-blocking notifications, created from JavaScript.
CSS only: add data-tooltip. Keep the text short, and don't put essential information only in a tooltip.
Native <details>, no JavaScript. Give items the same name to allow only one open at a time.
No. Everything runs in your browser.
JPEG, PNG, WebP, AVIF, HEIC, TIFF, GIF, BMP, SVG and ICO.
Only your device's memory.
Wrap in .table-wrap for rounded corners and horizontal scrolling on small screens. Use .num for right-aligned numbers.
| File | Format | Before | After | Saved |
|---|---|---|---|---|
| beach.heic | WebP | 3.4 MB | 412 KB | −88% |
| logo.png | AVIF | 96 KB | 18 KB | −81% |
| icon.svg | PNG | 4 KB | 11 KB | +175% |
No history yet
Converted images will appear here.
Wrap a label and control in .field. Add .input to text, number, email, password, search, select and textarea elements.
Set aria-invalid="true" on the control (or .is-invalid on the field) and add a .field-error message linked with aria-describedby.
An iOS-style toggle: a checkbox with .switch, usually inside a .switch-row label.
A radio group styled as a single control. Use .segmented-grid (with --cols) to wrap many options.
Native range input. ds.js fills the track as you drag; if you change value from code, call DS.syncRange(input).
Styling for a file drop area. Toggle .is-dragover from your drag events; wire the click to a hidden file input.
Click or drag files here
PNG, JPEG or WebP, up to 50 MB
A small set of helpers. Layout helpers read --gap (and --min for the grid) so you can tune them inline.
| Class | Does |
|---|---|
.container | Centered page width (--container) with gutters |
.stack | Vertical flex with gap |
.cluster | Wrapping horizontal flex with gap |
.spread | Horizontal flex, items pushed to both ends |
.grid-auto | Responsive grid of columns at least --min wide |
.grow | Take remaining space in a flex row |
.surface | Card background, large radius and shadow |
.text-muted, .text-accent, .text-success, .text-warning, .text-danger | Text colors |
.text-xs … .text-xl, .fw-medium, .fw-semibold | Text size and weight |
.tabular, .mono, .truncate | Tabular numbers, monospace, single-line ellipsis |
.m-0, .mt-2, .mt-4, .mb-2, .mb-4, .p-4, .p-5 | Common spacing |
.checkerboard | Transparency backdrop for image previews |
.visually-hidden | Hide visually, keep for screen readers |
ds.js wires up behaviour through data attributes as soon as it loads; content added later works too. For anything else, use window.DS or import from the module:
| Function | Description |
|---|---|
DS.setTheme(theme, persist = true) | 'light', 'dark' or 'system'. Saved in localStorage['theme']. |
DS.getTheme() | The current theme setting. |
DS.toast(message, options) | Show a toast. Options: variant, duration (ms, 0 = sticky), action. Returns { dismiss }. |
DS.openDialog(target) / DS.closeDialog(target, value) | Open or close a <dialog> by selector or element. |
DS.selectTab(tab) | Select a tab and show its panel. |
DS.syncRange(input?) | Refresh the filled track of one range input, or all of them. |
DS.version | Library version. |
| Attribute | Effect |
|---|---|
data-theme-value="light|dark|system" | Click switches theme; gets aria-pressed. |
data-ds-open="#dialog" | Click opens that dialog. |
data-ds-close="value" | Click closes the surrounding dialog with that return value. |
data-ds-dismissible | On a dialog: close on backdrop click. |
data-ds-dismiss | Click removes the surrounding .alert. |
data-ds-toggle="dropdown" | Toggle of a .dropdown. |
data-ds-tabs | On a role="tablist": click and keyboard tab switching. |