uxskill
Star on GitHub
Blog · Launch · 2026-11-02

ux-skill 4.0, Foundations: one brand color in, a design system out.

Give it your brand color and a short brief. ux-skill 4.0 works out a whole design system from them, measures every color pairing against WCAG in light, dark and high contrast, and writes the files only when every pairing passes. If you already have a system, it reads that one in its own names and extends it instead.

9foundations, primitives to semantic roles
5mode axes in every role
171lint rules that read the page's own system
25MCP tools on one command layer

What 4.0 is

Earlier releases handed an AI coding tool a recommendation: a style, a palette, a type pair, the rules to hold. 4.0 hands it a system: the tokens, the roles that bind them, the contracts components and sections follow, and a report that says in plain words what was built and why. One command does it:

# one brand color in, a checked design system out
pip install --upgrade uxskill
uxskill system build --brand '#0E7490' --out design-system

On that exact command the build prints WCAG gate passed: 1308 checks, 0 failing, and the report notes that the button keeps the brand color exactly, with white text at 5.35:1. Add --brief with the file /ux-discover writes and the look follows the brief as well as the color. The same inputs give the same bytes; no model is called.

A build writes six things: tokens.css to link from your pages, tokens.json in the W3C design tokens format (DTCG), fonts.css with metric-matched fallbacks, fonts-self-host.css for faces served from your own files, decorative brand art in art/, and system-report.md.

Nine foundations

Each foundation runs from primitives (a scale, a ramp) to semantic roles your markup uses, such as --color-action-primary, --space-control-gap or --motion-state-duration:

  1. Color. Scales and roles from your brand color, including six category colors for nominal data, each with a soft fill, a strong tone, text, and text on the strong tone.
  2. Type. Display, text and mono faces plus an Arabic face with its own sizes and line height.
  3. Space. One scale for gaps and padding, in comfortable and compact density.
  4. Layout. Container, margins, reading width and the gap between sections.
  5. Radius. One value per kind of part, from sharp to round.
  6. Border. Line widths, control edges and a focus ring.
  7. Elevation. Shadows and surfaces that keep depth readable in dark mode.
  8. Motion. Timing and curves for state changes, presses and arrivals, still under reduced motion.
  9. Imagery. A photo direction every picture on the page shares: light, temperature, grain and framing, locked to one grade.

Seven axes shape the look (warmth, contrast, density, geometry, formality, motion and type personality), each a value from 0 to 1. Every look is a continuous function of the axes, the brand and the brief; no industry or keyword picks one.

Five mode axes, one set of roles

Every role carries a value for each mode, so a page switches with one attribute on <html> instead of a second stylesheet:

Under dir="rtl" text switches to the Arabic face and its type scale, spacing follows logical properties and motion mirrors. There is no second system to keep in step. --latin-only leaves the Arabic face out.

Checked before it is written

The gate measures every text, control and focus color pairing in light and dark, at standard and high contrast. At standard contrast it holds text to 4.5:1, the level WCAG 1.4.3 (Contrast, Minimum) sets for text that is not large-scale, and control edges, focus rings and other parts you need to see to 3:1, the level WCAG 1.4.11 (Non-text Contrast) sets for user interface components and graphical objects. In high contrast it raises text to 7:1, the level WCAG 1.4.6 (Contrast, Enhanced) sets for text that is not large-scale. WCAG sets no enhanced level for non-text parts, so the 4.5:1 floor the gate holds them to in high contrast is ux-skill's own.

If one pairing falls short in any mode, nothing is written. The message names the pairing, the mode and what to change.

Reading and extending the system you have

Most teams are not starting from nothing. 4.0 reads an existing system in its own names: DTCG tokens, CSS custom properties, a Tailwind 4 theme or a resolved Tailwind 3 config, Markdown rule files, and Figma variables exports. An importer never guesses. It reads a value, or lists the entry under Not read with how to write it.

# what is there, and what it lacks
uxskill system detect
uxskill system import --from tokens.css
uxskill system enhance --from DESIGN.md --out design-system
# add what is missing, beside the source, never over it
uxskill system extend --from tokens.css --add motion

enhance measures the system through the same gate, and with --scan the code that uses it; it changes nothing. extend adds foundations, roles or contracts in an extension file next to the source and never overwrites a token you have.

Exports

uxskill system export writes a system as tokens.css, a Tailwind 4 theme, Figma variables (with the script that applies them in a Figma file) or tokens.json, into the folder you name and never over its source. Every write goes through an intake step that backs up your files byte for byte.

uxskill system export --from design-system/tokens.json --to tailwind --out exports

Contracts for components and sections

23 component contracts say which role each part of a control binds in every state and how each state moves: a change of state transitions on motion.state, a press scales on motion.press.scale and holds still under reduced motion, and tabs, menus and segmented controls slide one indicator. 14 section contracts (hero, pricing, FAQ, footer and the rest) name each section's job, the components its slots take, the proof it needs and how it recomposes on a phone. Pages built from them use photographs; interface fragments are extra imagery, never a replacement.

The linter reads the page

The linter ships 171 rules, and in 4.0 it reads the page's own system: a weight, size or duration your tokens define passes, motion is timed from its declared curve, and a display line height is held to the engine's floor. uxskill lint --render opens each page in headless Chromium at desktop and phone width and drives it: focus rings that do not show or are clipped, hover and press that answer late, focus lost after Escape, a press that still moves under reduced motion, the color budget and the photo grade lock.

uxskill lint index.html --render

Fewer commands

25 slash commands become 18, and each merged command keeps every step and flag of the ones it absorbed: /ux-discover takes --frame and --recommend, /ux-design takes --component, --dashboard and --from-image, /ux-polish loops lint, fix and re-lint until the score reaches 90 or three rounds pass, and /ux-init takes --stats. The seven old names still run as aliases that say where they moved; they are removed in 4.1.

What it took, and what came back

None of this was tracked as it happened, so these are estimates, and the method behind each one is written down.

  • Time: about 600 hours, somewhere between 400 and 800, from the first release on 22 May to 4.0 on 1 November. Git shows only the part after June, because the early history was squashed; the rest is counted from the release log, 44 changelog entries in five months, which cover about a hundred builds in the last week of May.
  • Money: about $1,400 over six months: two AI coding subscriptions, a self-hosted model router, a small server and the domain. The site, the package registries and the CI run free.
  • What it is now: 17 tagged releases, 13,101 passing tests, 171 lint rules and 25 MCP tools, MIT licensed and free.
  • What came back so far: 537 installs from npm between 27 May and 29 October, and 230 from PyPI in the last month alone. And a working design engine that builds the pages it was made to judge, this site included.

How to upgrade

4.0 needs Python 3.10 or newer. Then:

pip install --upgrade uxskill
# for the MCP server
pip install --upgrade 'uxskill[mcp]'
# or, with Node
npx uxskill@latest

With pipx over an installed 3.x, run pipx upgrade uxskill. To stay on 3.x on purpose, pin pip install 'uxskill<4'; on Python 3.9, pip keeps installing 3.2.x, which still works.

The 3.x outputs (the tokens.css that ux generate wrote, and the DESIGN.md and tokens.css of a system pack) are replaced by the files above. Nothing is rewritten for you. The guide to moving from 3.x to 4.0 gives two paths:

  1. Build a new system and move the page onto its roles. Link fonts.css and then tokens.css where the 3.x tokens.css was linked, and replace each 3.x custom property with its role: --color-canvas is --color-surface-page, --color-primary is --color-action-primary, --space-md is --space-4, --motion-base is --motion-state-duration. The guide has the full table. Then run uxskill lint with --render and fix what it reports.
  2. Keep your 3.x system and extend it. uxskill system import --from DESIGN.md reads it as it is, system enhance reports what it lacks and how to add it, and system extend adds the missing foundations, roles or contracts beside it.
What it does not do

The engine sets the rails; your AI tool still writes the markup.

A vague brief still gives a neutral system: with no brief or axes every axis sits at 0.5, and the report says so. The gate measures contrast, sizes, widths and durations; it does not judge whether a layout is good. That is what the render checks, the contracts and your own eye are for.

The full list of what changed is in the changelog.

Get 4.0

One brand color in. A design system out.

Free, MIT, offline. No telemetry, no account, no model calls in the engine.

$ pip install --upgrade uxskill
$ uxskill system build --brand '#0E7490' --out design-system
or with Node
$ npx uxskill@latest