Skip to content
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 (Figma space/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 as space-4 + space-2 now 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. actions takes several buttons; action still works. Figma: Toast has Layout = Inline | Stack and Show second action.
  • Figma plugin "dsgn sync check" (figma-check-plugin/, generated and committed): runs figma-sync.js → figma-parity.js → figma-handoff.js → figma-lint.js unchanged in the library file and shows all results as one JSON; node build/save-reports.mjs saves 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-cluster widened 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-title on 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.css and dist/dsgn.min.js (exports dsgn.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 copy copies the minified files by default (--readable for 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 #section links 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 and node_modules stay out; .gitignore is for what is not in git. Definition of done has a deploy item and minified files.

Changed

  • npx dsgn theme --out <dir> writes only dsgn.theme.css there; dsgn.theme.figma.js / .json stay next to dsgn.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), so dsgn lint public checks 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]: copies dsgn.css and dsgn.js (plus a VERSION file) into a static site, vendor/dsgn by 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 support round(): 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 booleans show/tablet-up, show/desktop-up, show/mobile-only, show/below-desktop (= data-hide-below / data-hide-above in code, for layer visibility). Header, Hero, CTA band, Footer and App shell got a Tablet variant; bind their Breakpoint property to breakpoint and 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 styles data-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-actions and .dsgn-header-menu. dsgn.js sets data-at-top / data-scrolled / data-hidden and --dsgn-header-height (also used for scroll-padding, so anchors are not hidden under a sticky header). data-sticky="false" now uses position: relative.
  • New colour token --dsgn-surface-glass (surface-raised at 78 %) for frosted surfaces. Figma: Color › surface/glass aliases the translucent primitives color/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 own data-surface="solid|glass" still wins. Figma: Header surface Default follows 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. minimal drops 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); none also 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.md lists the full sync checklist. Removed the outdated figma/lint.figma.js and figma/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.js writes it into the library. The skill has a Figma property → code table for agents building from a design.
  • Standalone icons: .dsgn-icon takes data-size="s|m|l|xl|2xl" (16 / 20 / 24 / 32 / 48 px, optical size follows), data-intent for its colour, outline or data-fill, and data-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 styles icon/{s…2xl}/{outline|fill}.
  • Feature .dsgn-feature: icon + title + text, data-layout inline (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-testimonial on 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-label with 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 no data-density and 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-icon keeps 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 of radius/full instead 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 (a dsgn-grid with 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-gap like Stack and Grid; the hero uses data-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/fill text 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 fire change). 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, change and dsgn-select events, 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, in input.css): an off-screen, inert spam-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 --init and dsgn theme turn a small dsgn.theme.mjs (brand hex or hue + chroma per palette, font, default corners and density) into dsgn.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/theme export (resolveTheme, themeCss, checkContrast, hexToOklch, …) for build tools.

  • --dsgn-brand: the exact brand colour for logos and illustrations (outside the contrast contract).

  • Cascade layer dsgn.theme between dsgn.tokens and dsgn.components.

  • Elevation mode data-elevation="flat|soft" (Figma: collection Elevation, effect styles shadow/control and shadow/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) and monochrome (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-ratio 16:9 (default) · 4:3 · 3:2 · 1:1 · 21:9 · auto, the picture fills it, corners follow radius-l, neutral placeholder when empty. dsgn-figure adds 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 use focus-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 rows attribute it starts three lines tall in every density (browsers default to two); rows="…" always wins. Corners min(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.systems on npm (MIT) with exports for the full bundle, tokens only, single components, JS, DTCG tokens and the Figma payload.
  • Cascade layers: all CSS ships in @layer dsgn.tokens and @layer dsgn.components; unlayered project CSS always wins.
  • One CSS file per component in dist/components/.
  • dsgn lint CLI 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.