werkstatt brand and design system version 1.2, october 2026

components

the parts, built once, with their states and rules.

the parts

every component on this page is live html styled by assets/styles.css. copy the markup, link the stylesheet, and it works. class names start with ws-. overlays, menus and states are on the controls page; whole sections are on the patterns page.

buttons

four fills, one shape. the primary button is red and there is one per screen. hover turns any button yellow, pressing turns it ink.

<button class="ws-btn ws-btn--primary" type="button">save design</button>
<button class="ws-btn" type="button">cancel</button>
<button class="ws-btn ws-btn--emphasis" type="button">new exercise</button>
<button class="ws-btn ws-btn--dark" type="button">view</button>
<button class="ws-btn" type="button" disabled>locked</button>
  • states.hover: yellow fill. focus: 3 px blue outline. pressed: ink fill, yellow text. disabled: paper fill, grey border, no hover.
  • size.48 px tall, 24 px side padding. the small variant is 40 px for dense toolbars only.
  • label.a verb and a noun in sentence case: save design, not Submit. no icons inside buttons except a leading mark for actions like play.

text fields and selects

a 3 px ink box with a bold label above. focus is a 5 px blue underline inside the box. errors turn the border and underline red and add a message that says what to fix.

use at least 8 characters.
<div class="ws-field">
  <label for="email">email</label>
  <input class="ws-input" id="email" type="email" placeholder="you@example.com">
</div>

<div class="ws-field ws-field--error">
  <label for="pw">password</label>
  <input class="ws-input" id="pw" type="password" aria-invalid="true" aria-describedby="pw-hint">
  <span class="ws-hint ws-hint--error" id="pw-hint">use at least 8 characters.</span>
</div>
  • labels always.a visible label above the field, bound with for and id. placeholders are examples, never labels.
  • focus.blue underline inside the box, 5 px. the outline is removed because the underline replaces it.
  • errors.red border, red underline, and a message that starts with a red square. aria-invalid and aria-describedby connect them.

checkboxes, radios and toggles

square for checkboxes, circle for radios, a sliding dot for toggles. the checked state is a filled shape, never a tick.

<label class="ws-choice"><input class="ws-check" type="checkbox" checked> daily reminder</label>
<label class="ws-choice"><input class="ws-radio" type="radio" name="level" checked> beginner</label>
<label class="ws-choice"><input class="ws-toggle" type="checkbox" role="switch"> sound</label>
  • real inputs.these are native inputs with appearance none, so keyboards and screen readers work without extra code.
  • toggle.86 by 44 px. on is a yellow track with the dot on the right. the label sits beside it and says what is on.
  • touch.every row is at least 44 px tall and the whole label is the hit area.

tags

a bordered chip with a flat fill. use them for categories, not for status; status is a sentence.

furnituretypographyarchitecturetextile1925a link tag
<span class="ws-tag ws-tag--yellow">furniture</span>
<span class="ws-tag ws-tag--blue">typography</span>
<span class="ws-tag">textile</span>
  • height.36 px, 12 px side padding, weight 600. no close buttons inside a tag; removable filters are buttons.
  • color.color means category, so keep one color per category across the product.

cards

a surface with a 3 px border and an optional 16 px color band at the top. media sits flush above the body with a rule between.

wassily chair

tubular steel and canvas bands. designed by marcel breuer, 1925.

view

color studio

mix, match and name colors on a living wheel.

14days in a row

your longest streak so far.

<div class="ws-card">
  <div class="ws-card__band"></div>
  <div class="ws-card__body">
    <h3>wassily chair</h3>
    <p>tubular steel and canvas bands.</p>
    <a class="ws-btn ws-btn--dark" href="/objects/wassily">view</a>
  </div>
</div>
  • band color.red for featured, blue for information, yellow for a selected or current card. omit it for plain cards.
  • whole card as link.when the card has one destination, make the card an anchor and keep no other buttons inside it.
  • no shadow, no radius.a card is a box. hover on a linked card is a 3 px outline outside the border.

alerts and progress

four alert fills: yellow for warning, blue for information, red for errors, white for success. progress is a blue bar in an ink box.

unsaved changes

leave this page and you will lose today's exercise.

new room open

the type room is ready. try the first lesson.

couldn't save

check your connection and try again.

saved to notebook

find it under color studio.

progress 64%
<div class="ws-alert ws-alert--error" role="alert">
  <svg aria-hidden="true">…</svg>
  <div><b>couldn't save</b><p>check your connection and try again.</p></div>
</div>

<div class="ws-progress" role="progressbar" aria-valuenow="64" aria-valuemin="0" aria-valuemax="100" aria-label="exercise progress">
  <i style="width:64%"></i>
</div>
  • icon per kind.triangle for warning, circle for information, square for error, a check for success. the shape repeats the meaning so color is never the only signal.
  • errors use role alert.they announce themselves. other alerts use role status or nothing.
  • progress.24 px tall. always pair it with a text value for people who cannot see the bar.

tables

ink header, 2 px rules between rows, left aligned text. wide tables scroll inside their box instead of breaking the page.

objectdesigneryearmaterial
club chairmarcel breuer1925steel, canvas
teapotmarianne brandt1924silver, ebony
table lampwilhelm wagenfeld1924glass, steel
nesting tablesjosef albers1927oak, glass
<div class="tbl">
  <table class="ws-table">
    <thead><tr><th>object</th><th>designer</th><th>year</th></tr></thead>
    <tbody><tr><td>club chair</td><td>marcel breuer</td><td>1925</td></tr></tbody>
  </table>
</div>
  • numbers.right align a column only when it is added up. years are labels, so they stay left.
  • no zebra stripes.the rules separate rows. a selected row is yellow.

inverse panels

any block can flip to ink. text goes white, headings and links go yellow, the primary button stays red.

less is more

every lesson ends the same way: take one thing away and see if the design still works.

.inverse { background: var(--inverse-bg); color: var(--inverse-text); }
.inverse h2, .inverse h3, .inverse a { color: var(--inverse-accent); }
  • one inverse block per screen.it is the strongest thing on the page, so it marks the one section that matters most.
  • buttons keep their fills.red and yellow read well on ink. the white secondary button is not used on ink; use the yellow one.
© 2026 Intelligent Operations. Directed, prompted and published by Tommy Saunders, Intelligent Operations.Generated with Claude Fable 5.1 by Anthropic in the Claude app, 10 and 11 October 2026. Bauhaus is referenced as history; all artwork is original. credits and how to cite · sitemap