Documentation
Start
Changelog
Every release of dsgn, newest first.
One version for the npm package and the Figma library. Semantic versioning: patch = fix, minor = addition, major = rename or removal (with migration notes).
0.15.0 — 2026-11-11
Added
--dsgn-space-6(Figmaspace/6): the space scale is now 0 · 2 · 4 · 6 · 8 · 12 · 16 · 20 · 24 · 32 · 40 · 48 · 64, so it covers the dsgn.systems logo's row heights (2 · 4 · 6 · 8 · 12 · 16 · 24 · 32). Places that wrote 6 px asspace-4 + space-2now use it: menu and select padding (menu/pad), the icon side of a button (control/pad-x-icon) and the menu item radius (radius/item); the values are unchanged.- Toast, layout stack:
dsgn.toast({ …, actions: [{ label, onClick }, …], layout: 'stack' })puts the buttons under the text (the first subtle, the others ghost neutral). It is the default with a title or several actions and always applies on phones (< 30rem), so the text is never squeezed beside a button.actionstakes several buttons;actionstill works. Figma: Toast hasLayout = Inline | StackandShow second action. - Figma plugin "dsgn sync check" (
figma-check-plugin/, generated and committed): runsfigma-sync.js→figma-parity.js→figma-handoff.js→figma-lint.jsunchanged in the library file and shows all results as one JSON;node build/save-reports.mjssaves the three reports. A fallback for when the scripts cannot run through the Figma MCP.
Changed
- Toast is 448 px wide (was 360) and never wider than the screen minus its margins (it ran 2 px off a 390 px phone). The dismiss button is its own grid cell (
.dsgn-toast-close), next to the actions instead of inside them.
Fixed
- A wide table (
.dsgn-table-wrap) inside a.dsgn-clusterwidened the page instead of scrolling inside: the item holding it now takes a whole row and may shrink. In Split, Grid and Card it already scrolled inside; the a11y test now guards all of these. .dsgn-footer-titleon an<h2>got the browser's heading size and margins; it now keeps the footer's text size, so the column titles can be headings.
0.14.0 — 2026-10-19
Added
- Minified files
dist/dsgn.min.cssanddist/dsgn.min.js(exportsdsgn.systems/min.css,min.js): about 19 kB CSS and 5 kB JS gzipped instead of 41 kB and 7 kB. The a11y test checks that every element of the demo renders the same with the minified CSS.npx dsgn copycopies the minified files by default (--readablefor the commented ones); the skill's CDN links point to them. - Motion where things move, all on the motion tokens and off with
prefers-reduced-motion: smooth scrolling to#sectionlinks on a.dsgn-page; dialogs fade and drawers slide out as well as in; popover menus and combobox lists fade and grow in and out; tooltips fade out too; accordion content slides open and closed (browsers with::details-content); the newly shown tab panel fades in and the tab indicator changes colour smoothly; card links, links, table rows and the file drop zone ease their hover. - dsgn.js closes a dialog when a link to a section of the page (
href="#…") inside it is picked, so a mobile menu drawer needs no script of its own. - Skill: what goes to the hosting — the website lives in
public/and only that folder is deployed; theme source, Figma theme files, source images andnode_modulesstay out;.gitignoreis for what is not in git. Definition of done has a deploy item and minified files.
Changed
npx dsgn theme --out <dir>writes onlydsgn.theme.cssthere;dsgn.theme.figma.js/.jsonstay next todsgn.theme.mjs, so they are never deployed with the site.npx dsgn lint <folder>skips copies of dsgn itself (dsgn.css,dsgn.min.css,dsgn.theme.css), sodsgn lint publicchecks only the project's CSS.
Fixed
- Drawer (mobile menu) closing glitch: while closing, the panel lost its column layout, so the close button jumped next to the links and the panel widened, and it disappeared without sliding out. The column layout no longer depends on
[open], and the closed state is defined, so the drawer slides back to its edge (a dialog fades out).
0.13.0 — 2026-10-19
Added
npx dsgn copy [dir]: copiesdsgn.cssanddsgn.js(plus aVERSIONfile) into a static site,vendor/dsgnby default. Run it as"postinstall"so an update never leaves a stale copy behind.npx dsgn serve [dir] [--port n]: a local preview server for static sites (Node only, port 4173, serves only files under the folder). The skill now tells agents to use it instead of Python or a hand-written server.- The build fails when the skill or the README pins the CDN to an older minor version (the skill still said
@0.11).
Fixed
data-radius="pill"or"sharp"on<html>was ignored in browsers that supportround(): buttons got 4 px corners instead of pill or square. The snapped copy of each mode block left out the values that are not rounded (0, 9999px), so the default block, same specificity and set on:root, won on the root element. Every mode block is now complete; sections with the attribute were never affected. Guarded by a new check in the a11y test (modes on<html>).
0.12.0 — 2026-10-19
Added
- Figma: Grid has a new variant Columns = Auto (=
data-cols="auto"): one Items slot that wraps by width, so the columns change on their own when the frame width changes, e.g. when switching the Layout mode (Mobile 1, Tablet 2, Desktop 4 columns). Columns 1–4 stay for a fixed count per breakpoint frame; Figma cannot bind the column count to a variable. - Figma: responsive frames driven by the Layout mode. New Layout variables:
breakpoint(string Mobile / Tablet / Desktop) and the booleansshow/tablet-up,show/desktop-up,show/mobile-only,show/below-desktop(=data-hide-below/data-hide-abovein code, for layer visibility). Header, Hero, CTA band, Footer and App shell got a Tablet variant; bind their Breakpoint property tobreakpointand one page frame re-flows when you switch its Layout mode: width, variant, margins, gaps and Grid Auto columns. Example: Templates › Landing / Responsive. - Header variants (all combinable):
data-layout="start|middle|center"; navigation stylesdata-variant="text|underline"on.dsgn-nav;data-surface="glass|transparent"(frosted glass with a solid fallback; transparent at the top of the page, glass or solid once scrolled,data-top-theme="dark"over dark heroes);data-floating(a rounded box on tablet and desktop that widens a little on scroll);data-hide-on-scroll(mobile). All changes animate with the motion tokens and respect reduced motion. New markup hooks.dsgn-header-actionsand.dsgn-header-menu. dsgn.js setsdata-at-top/data-scrolled/data-hiddenand--dsgn-header-height(also used forscroll-padding, so anchors are not hidden under a sticky header).data-sticky="false"now usesposition: relative. - New colour token
--dsgn-surface-glass(surface-raised at 78 %) for frosted surfaces. Figma:Color › surface/glassaliases the translucent primitivescolor/glass/light|dark, which themes regenerate from their neutral palette (Figma ignores the paint opacity of a variable-bound fill inside instances, so the fade has to live in the variable). - Material mode
data-material="solid|glass"(Figma: collection Material, modes Solid / Glass): one switch for everything that floats over content — the site header, the app top bar and toasts become frosted glass (--dsgn-surface-chrome,--dsgn-blur-chrome) and the dim layer behind dialogs and drawers gets a light blur (--dsgn-blur-backdrop). Works on<html>or any section, in light and dark; solid where blur is unsupported, with reduced transparency and in forced colours. A component's owndata-surface="solid|glass"still wins. Figma: Header surfaceDefaultfollows the mode; Toast, the App shell top bar and the dialog scrim are bound to it. - Glass card:
data-surface="glass"on.dsgn-card(Figma: Card › Surface = Glass) for a card over a photo or a colourful band. - Lines mode
data-lines="on|minimal|none"(Figma: collection Lines, modes On / Minimal / None): switches the decorative lines.minimaldrops the outlines of cards, table and list frames, stat tiles, media, alerts, banners, the header / app top bar / sidebar edges and the footer edge (--dsgn-line-container);nonealso drops the separators — table and list rows, description lists, accordions, the tab baseline, menu and footer separators (--dsgn-line-separator). Never switched: outlines of fields and selection controls (WCAG 1.4.11), focus rings, overlays (menu, dialog, toast, tooltip), Divider, hover outlines. Combine with Shadows = Soft if cards should stay distinct without a line. - Figma ↔ code sync check: the Figma scripts (
dist/figma-parity.js,figma-handoff.js,figma-lint.js) return fingerprints of the code they were built from and of the Figma file they ran on;npm run build(and CI) fails unless all three reports are fresh and share one Figma state. Any change in code or Figma therefore requires running all three again.CLAUDE.mdlists the full sync checklist. Removed the outdatedfigma/lint.figma.jsandfigma/parity.figma.js. - Figma → code handoff (a Code Connect substitute that works on every Figma plan): every component (70) carries its code in the description — the snippet, every property value → attribute (
Variant: Ghost → data-variant="ghost",State: Hover → :hover) and the docs link; each variant lists its own attributes; each component has its documentation link set. Source:src/docs/figma-map.mjs, checked against the docs on every build;dist/figma-handoff.jswrites it into the library. The skill has a Figma property → code table for agents building from a design. - Standalone icons:
.dsgn-icontakesdata-size="s|m|l|xl|2xl"(16 / 20 / 24 / 32 / 48 px, optical size follows),data-intentfor its colour, outline ordata-fill, anddata-variant="subtle|solid"for a background — the same icon on a tinted square, padding added around the glyph (24 / 36 / 48 / 64 / 80 px), accent unless an intent is set, corners follow Radius (Pill = circle). Figma: one component Icon (Size × Style × Background None / Subtle / Solid × Intent) and text stylesicon/{s…2xl}/{outline|fill}. - Feature
.dsgn-feature: icon + title + text,data-layoutinline (default) / stack / center; title and text optional. Figma: Feature (Layout, Show title, Show text, nested Icon — switch its Background on the instance; min width 256 like Card, so it wraps in Grid › Auto at Width = Fill; Grid slots offer it). - Testimonial
.dsgn-testimonialon a Card (<figure class="dsgn-card dsgn-testimonial">): author (avatar beside name and a smaller role), quote, stars — each optional; the stars sit at the bottom so testimonials of different lengths line up in a Grid;data-align="center"puts a 48 px avatar on the top edge, half outside the card, with name, role, quote and stars centred below; glass as on Card. Figma: Testimonial (Align × Surface, Show author (the whole block) / rating / text / avatar / name / role, Text / Name / Role, nested Rating and Avatar; min width 256, offered in Grid slots). - Rating
.dsgn-rating: five stars, filled for the score (data-fill, accent colour, so it follows the brand) and outline for the rest,role="img"+aria-labelwith the score,data-size="s". Figma: Rating (Value 1–5 × Size M / S). - Media
data-ratio="circle"(1:1, fully round). Figma: Media › Ratio = Circle. - Badge with close (
.dsgn-badge-remove, a button after the label): for active filters and picked values. The badge stays 20 px; the close glyph is 16 px with a 24 × 24 hit area (WCAG 2.5.8), the end padding tightens to space-4, keyboard focus ring included. Figma: Badge › Show close. - Figma: Description row has a Value slot, so the description can hold components (badge, link, avatar …), like
<dd>in code.
Changed
- Figma: the 22 role slots (
accent/fill-light,control/tint-dark…) moved out of Primitives, where they were raw hex values, into the Accent collection as aliases to Primitives palette steps. Primitives now hold only raw palettes, space, radius and stroke, so a theme mode (e.g. a brand palette) recolours buttons and controls too. The dsgn theme plugin re-points the Accent Default mode for a theme's accent fill / monochrome setting and restores it when the theme is removed. Code output is unchanged. - Figma: containers and slots no longer clip their content, so shadows and focus rings of nested components are not cut off. Clipping stays only where it is needed: a frame's own focus ring (Figma draws shadow spread on frames only with clipping on), images and fills on rounded edges (Media, Card image, Avatar image, Progress, Checkbox). The Figma lint (
figma/figma-lint.js) now reports clipping without a reason. - Touch screens get density L by default (
@media (pointer: coarse)): controls are 44 px tall instead of 36 px (WCAG 2.5.5 AAA, Apple HIG 44 pt). Only when the page sets nodata-densityand the project theme keeps the default;data-density="m"on<html>keeps M everywhere. In Figma pick Density L for touch designs. - Card icon on top is now the Icon itself:
<span class="dsgn-icon" data-variant="subtle">as the card's first child, so it can be bare, subtle or solid and any size..dsgn-card-iconkeeps working (deprecated, removal in 1.0). Figma: Card › Top = Icon holds a nested Icon instance (Background, Intent, Size and Style switchable on the instance); the separate Card icon component is removed — swap any detached use to Icon. - Figma: Radius Pill ›
radius/m(controls, fully round) is now an alias ofradius/fullinstead of a raw 9999. The values are unchanged and deliberate: controls become pills, small bits 6, containers capped at 24 (a pill-shaped card or dialog would look broken). - Footer reflows by its own width, in code and in Figma, like Grid Auto: new hooks
.dsgn-footer-top,.dsgn-footer-brand(16rem) and.dsgn-footer-columns(columns at least 8rem, as many as fit). The brand sits beside the columns, or above them when both do not fit: 4 columns on desktop, 2 beside the brand on a tablet, 2 below it on a phone. Figma: the Footer's Top slot and columns wrap (brand 256 px, Footer column fills, min 128 px), so a resized instance re-arranges in every Breakpoint variant; Breakpoint now only picks the Layout mode. The previous markup (adsgn-gridwith breakpoint columns) still works.
Fixed
- Hero on mobile and tablet: the gap between content and media (grid gap, 16 / 24 px) was not larger than the gaps inside the content (24 px), so the two read as one block. Split now takes
data-gaplike Stack and Grid; the hero usesdata-gap="xl"(48 px) at every breakpoint. Figma: Hero › container gap = space/48. - Figma: a Badge with an icon was 24 px tall instead of 20 (the icon used the 20 px
icon/filltext style). The icon is now 16 px (icon/s/fill), and the start padding tightens to space-4 with an icon, as in code. - Figma: switching a variant no longer resets what was typed or added. 32 Card variants (image / icon on top, centred) had lost the links to Title, Text, Show actions and the Content slot (now one shared slot again); Input, Textarea and Date input variants were missing links in some states; Section header's Actions is now a real slot; the unused Alert › Icon name property was removed. The Figma lint now reports any variant layer that is not linked to the property its siblings use.
- Figma: slots no longer ship placeholder boxes (Grid, Section, Split) — they start empty and offer preferred components (e.g. Grid → Card, Stat tile, Media) with a hint. Card, Stat tile and Media have a minimum width of 256, so in Grid › Columns = Auto an item set to Width = Fill wraps like
data-cols="auto". Layout page: Grid / examples shows both grids filled with cards.
0.11.0 — 2026-10-02
Added
- Skip link (
components/skip-link.css):<a class="dsgn-button dsgn-skip-link" href="#main">as the first element of the page; hidden until the first Tab, then a solid button in the top corner that jumps to the content (WCAG 2.4.1). The documentation, the demo and the preview now have one. Figma: component Skip link. - File upload (
components/file.css): a drop zone around a native<input type="file">(.dsgn-file,.dsgn-file-title,.dsgn-file-text), with hover, keyboard focus, drag over, invalid and disabled states. dsgn.js adds drag and drop (dropped files go into the input and firechange). Chosen files are listed with List rows. Figma: set File upload (State). - Combobox (
components/combobox.css+ dsgn.js): WAI-ARIA combobox with a filtered list of suggestions, reusing the Menu panel (role="listbox", options as.dsgn-menu-item[role="option"]). Case- and diacritics-insensitive filtering, arrow keys, Enter, Escape, a polite result count, hidden input for the value,changeanddsgn-selectevents,data-filter="none"for remote data. Figma: component Combobox (field + list). - Date and time inputs: native
<input class="dsgn-input" type="date|time|datetime-local|month">at the control height; the browser's picker button sits at the end of the field like a Select chevron, drawn as the Material Symbols glyph (calendar_month / schedule) in the muted icon colour. The browser calendar follows light / dark. Figma: set Date input (Type = Date / Time, State). - Honeypot (
.dsgn-honeypot, ininput.css): an off-screen,inertspam-trap field for forms. skills/dsgn-systems/SKILL.md: a skill for AI agents building with dsgn (setup, tokens, components, accessibility, SEO, form security and injection, performance, privacy, definition of done). Shipped in the npm package.
Fixed
- Menu items had no focus ring colour after the 0.10 focus change (the ring colour variable was only defined for danger items).
0.10.0 — 2026-10-02
Added
-
Project themes:
dsgn theme --initanddsgn themeturn a smalldsgn.theme.mjs(brand hex or hue + chroma per palette, font, default corners and density) intodsgn.theme.css, with the contrast check. Also writes a Figma script that adds the theme as a mode in Primitives. -
Theme customizer page in the documentation: the same engine in the browser, with a live light / dark preview, a theme code to keep and reopen, and downloads for the website file.
-
Figma plugin dsgn theme (
figma-plugin/): the same controls inside Figma; adds, updates or removes the theme as a mode in the library, or takes a theme code pasted from the web customizer. -
dsgn.systems/themeexport (resolveTheme,themeCss,checkContrast,hexToOklch, …) for build tools. -
--dsgn-brand: the exact brand colour for logos and illustrations (outside the contrast contract). -
Cascade layer
dsgn.themebetweendsgn.tokensanddsgn.components. -
Elevation mode
data-elevation="flat|soft"(Figma: collection Elevation, effect stylesshadow/controlandshadow/raised): small shadows on buttons, fields, chips, selected segment, switch thumb, and on cards, stat tiles, option cards, lists and tables. Flat (no shadow) is the default. -
Theme options:
accentFill(default / strong / stronger / auto = closest to the brand colour) andmonochrome(no accent colour: greys everywhere, near-black / near-white fills). -
Figma: collection Accent with modes Default / Strong / Stronger / Monochrome, switched on any frame like Density or Radius. Default follows the file's theme; the others show the button fill strengths and the monochrome look. Semantic accent colours and the role-based tokens go through it.
-
Media (
components/media.css): a frame for an image or video,data-ratio16:9 (default) · 4:3 · 3:2 · 1:1 · 21:9 · auto, the picture fills it, corners follow radius-l, neutral placeholder when empty.dsgn-figureadds a caption. Figma: component set Media (Ratio, Show image). -
Card: image on top (
dsgn-card-media, flush with the card edges) or an icon on top (dsgn-card-icon, intent tint, size = control height), centred layout (data-align="center"), and a stretched title link (dsgn-card-link) so a card can be a link and still hold buttons. Figma: Card is now a set — Top = None / Image / Icon, Type = Static / Link, State = Default / Hover / Focus, Align = Left / Center — with a Content slot between text and actions (Show content), plus a Card icon set (Intent, Icon name). Existing card instances keep their text. -
Avatar: Figma variant Content = Image (photo with a 1 px inner border, corners follow radius-m) replaces the Show image switch; code already took
<img>. -
Tokens:
border-control,focus-danger,control-*(controls and selection),control-track,control-track-border,control-selected, shadow tokens; role slots in Primitives (accent/fill-*,control/*) that a theme re-points.
Changed
- Fields and selectable containers (input, select, textarea, search, number, chip, option card) share one outline,
border-control: lighter than before in light mode, 3 : 1 on base and raised surfaces in both modes. - Buttons with an icon: 6 px less padding on the icon side and a 2 px tighter gap (the glyph has its own white space). A trailing icon is marked
data-end(Next<span class="dsgn-icon" data-end>arrow_forward</span>); a leading icon needs nothing. Fixes a bug where a button with one icon got the tight padding on both sides, so the text side looked cramped. Badge and chip follow the same rule. - Segmented control: selected segment without an outline; in dark mode an outlined track and a lighter selected segment.
- Focus ring: one colour everywhere,
focus(accent; neutral in monochrome), so keyboard focus always looks the same. Danger buttons, danger menu items and invalid fields usefocus-danger. Links in a solid banner use the banner's text colour. The ring also holds 3 : 1 on the tinted fills it is drawn inside (menu item, nav link, list row, segment, tab). - Checkbox, radio, switch, slider, progress, tabs, steps and selected chip / option card / page / row use the
control-*tokens (accent by default). - Table: ghost buttons, chips and badges inside a hovered or selected row are shifted one step, so they never melt into the row colour; in a selected row they take the selection tint.
- Textarea: without a
rowsattribute it starts three lines tall in every density (browsers default to two);rows="…"always wins. Cornersmin(radius-m, radius-l), so in Pill it stays a rounded rectangle (Figma had it as a pill). - Menu and select picker: 6 px padding around the items (was 4), and item corners concentric with the panel (panel radius − padding), so a highlighted item fits the panel in every radius mode (Figma:
menu/pad,radius/panel,radius/item). - Slider: track and knob follow the radius mode (square knob in Sharp, round in Rounded and Pill).
- Switch follows the radius mode: square in Sharp, softly rounded in Default and Rounded, round in Pill; the thumb is concentric with the track.
- Layout examples (Stack, Cluster, Grid, Split, Section placeholders): fixed 4 px corners, no longer following the radius mode.
- Disabled controls look the same everywhere: grey fill, light outline (
border), no elevation shadow (fields, checkbox, radio, switch thumb, option card). - Menu divider stays inside the panel padding, like the items (Figma already had it so).
- Number input: 2 px more side padding than an icon button, so the steps clear the curve in Pill (Figma:
number/pad-x). - Figma: Input (text, icon, select) and Textarea gain the variant
State=Invalid focus(red outline and red focus ring), matching code. - Table and List: the outer frame is a real border on the wrapper (all four sides); rows and the header have only a bottom line and the last row none. Content is clipped inside the border, so the sticky header, a hovered, selected or current row can never cover it. In Figma the frame is a top layer "frame" over the rows (Figma draws children over a frame's stroke). List row separators sit on the row, so a hovered row keeps its line. Figma: List item has a Divider switch (a bottom line layer instead of a stroke); the last item in List and the last table row have no line.
- Hint text flows inline (code, links); the icon layout applies only when the hint starts with an icon.
0.9.0 — 2026-09-29
First packaged release, ahead of 1.0.
Added
- Package
dsgn.systemson npm (MIT) withexportsfor the full bundle, tokens only, single components, JS, DTCG tokens and the Figma payload. - Cascade layers: all CSS ships in
@layer dsgn.tokensand@layer dsgn.components; unlayered project CSS always wins. - One CSS file per component in
dist/components/. dsgn lintCLI for project CSS (tokens only, no px, no literal colours, no internal properties).- Documentation site generated from the source (
dist/docs.html): foundations, every component with live examples, code and API, Figma handoff, quality reports; checked by the accessibility audit. - Components: Segmented control, Search field, Number input, Kbd, Slider, Chip, Option card, Stepper, Stat tile, List, Description list, Divider, Banner.
- Layout primitives (Container, Section, Stack, Cluster, Grid, Split), patterns (Header, Footer, Page header, App shell, Accordion, Nav, Drawer) and text style classes.
- Checks: Figma ↔ code parity script, Figma component lint, accessibility audit (axe WCAG 2.2 AA, keyboard, reflow, text spacing, reduced motion, forced colours).
Changed
- App shell topbar is a
<header>in the examples (landmark). - Segmented control scrolls sideways when the options do not fit; focus ring is drawn inside the segment.
Fixed
- Forced colours: slider track, selected table row, table outline, brand mark, disabled pagination.
0.1.0 – 0.8.x (unreleased)
Foundations (OKLCH palettes with the contrast guarantee, density, radius, central unit with 2 px snapping, light / dark) and the base components: Button, Icon button, Input, Select, Textarea, Field, Checkbox, Radio, Switch, Badge, Alert, Card, Link, Tabs, Tooltip, Menu, Dialog, Table, Avatar, Breadcrumbs, Pagination, Toast, Progress, Spinner, Skeleton, Empty state.