Skip to content
v0.1.7 GitHub

Design system

Uniform Design

Brand tokens, CSS components, self-hosted fonts and the Atmosphere wave motif. One small, versioned package for websites, applications and documents.

Get started All components on one page

  • HTML first. Plain u-* classes, no framework.
  • Two themes. Dark by default, light anywhere.
  • Strict-CSP ready. No inline styles, no third-party requests.

Install

Install a pinned release. dist/ is committed, so nothing builds on install.

sh
npm install --save-exact github:UniformChicago/design#v0.1.7

Copy node_modules/@uniform/design/dist/ to your static directory, keeping its structure (CSS references fonts by relative path), and link the stylesheet:

html
<link rel="stylesheet" href="/design/design.css" />

Not using npm? Download dist/ from a tag and check each file against dist/manifest.json.

Usage

Put u-root on the body and compose with native elements. Dark is the default; set data-theme="light" on any element to switch it and everything inside.

html
<body class="u-root" data-theme="light">
  <main class="u-panel">
    <h1 class="u-title">Your workspace<span class="u-dot" aria-hidden="true"></span></h1>
    <p class="u-lede">A clear place to begin.</p>
    <a class="u-button" href="/records">View records</a>
  </main>
</body>

For your own components, use the theme variables and the spacing and radius tokens:

css
.record-card {
  background: var(--u-surface);
  color: var(--u-fg);
  padding: var(--u-space-5);
  border-radius: var(--u-radius-lg);
}

Color

The brand palette, from tokens/tokens.json. It's also available as JavaScript (@uniform/design/tokens) and Python. Consumers never hard-code hex values.

  • --u-ink#14212bPrimary text on light; letters in the color lockup
  • --u-paper#f3f5f6Text on dark; light surfaces
  • --u-slate#5e6e79Secondary text on light; lockup descriptor
  • --u-signal#c8402aThe brand dot and accents on light
  • --u-signal-on-dark#e3654dSignal tuned for AA on Night
  • --u-lake#1f5f7aLinks and labels on light
  • --u-lake-light#8fc3d9Links and highlights on dark
  • --u-night#0e171eDark background
  • --u-mist#9dacb6Secondary text on dark (Slate tuned for AA on Night)
  • --u-rule#d5dce0Hairlines on light
  • --u-rule-dark#22313bHairlines on dark

Theme variables

Build with these, not the palette. They switch with the theme, so each swatch below changes when you switch between dark and light.

  • --u-bgPage background
  • --u-surfacePanels and cards
  • --u-fgBody text
  • --u-mutedSecondary text
  • --u-linkLinks, primary buttons
  • --u-accentBrand dot, accents
  • --u-lineHairlines
  • --u-hoverHover fill

Type

Two self-hosted families under the SIL Open Font License. No font CDN.

  • Public Sans

    Regular · body · --u-font-sans

  • Public Sans

    Semibold · headings · --u-font-sans

  • Public Sans

    Bold · titles · --u-font-sans

  • IBM Plex Mono

    Medium · labels, code · --u-font-mono

Spacing and radius

  • 4px--u-space-1
  • 8px--u-space-2
  • 12px--u-space-3
  • 16px--u-space-4
  • 24px--u-space-5
  • 32px--u-space-6
  • 48px--u-space-7
  • --u-radius-sm4px
  • --u-radius-md6px
  • --u-radius-lg10px

Typography

The brand dot is always u-dot, never a typed period.

Eyebrow label

Page title

Section heading

A lede introduces the page in one or two sentences.

Meta text · 2.4 MB · Updated today

Inline code: sha256:9f3c72b1

html
<p class="u-eyebrow">Eyebrow label</p>
<h3 class="u-title">Page title<span class="u-dot" aria-hidden="true"></span></h3>
<p class="u-heading">Section heading</p>
<p class="u-lede">A lede introduces the page in one or two sentences.</p>
<p class="u-meta">Meta text · 2.4 MB · Updated today</p>
<p>Inline code: <code class="u-code">sha256:9f3c72b1</code></p>

Buttons

Anchors for navigation, buttons for actions; the class only sets the look.

Primary action
html
<a class="u-button" href="#buttons">Primary action</a>
<button class="u-button u-button--quiet" type="button">Quiet action</button>

Tags

Short status labels. Keep the words meaningful without the color.

Neutral Final Inbox
html
<span class="u-tag">Neutral</span>
<span class="u-tag u-tag--ok">Final</span>
<span class="u-tag u-tag--warn">Inbox</span>

Panels

Panel

A surface for grouped content.

Accent panel

The accent edge marks the primary panel on a page.

html
<div class="u-panel">
  <p class="u-heading">Panel</p>
  <p>A surface for grouped content.</p>
</div>
<div class="u-panel u-panel--accent">
  <p class="u-heading">Accent panel</p>
  <p>The accent edge marks the primary panel on a page.</p>
</div>

Callouts

Audit chain verified: 42 entries.

Audit chain broken at entry 17.

html
<p class="u-callout">Audit chain verified: 42 entries.</p>
<p class="u-callout u-callout--danger">Audit chain broken at entry 17.</p>

Facts

Label and value pairs on a dl, inline or stacked.

Category
Agreement
Size
2.4 MB
Name
Jordan Rivera
Service area
Example City
html
<dl class="u-facts">
  <dt>Category</dt>
  <dd>Agreement</dd>
  <dt>Size</dt>
  <dd>2.4 MB</dd>
</dl>
<dl class="u-facts u-facts--stacked">
  <dt>Name</dt>
  <dd>Jordan Rivera</dd>
  <dt>Service area</dt>
  <dd>Example City</dd>
</dl>

Tables

The wrapper scrolls on narrow screens. Give it tabindex="0" role="region" aria-label="…" so keyboard users can reach it.

Name Role Status
Jordan Rivera Example role Active
Sam Lee Example role Pending
html
<div class="u-table" tabindex="0" role="region" aria-label="Example registry">
  <table>
    <thead>
      <tr>
        <th scope="col">Name</th>
        <th scope="col">Role</th>
        <th scope="col">Status</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Jordan Rivera</td>
        <td>Example role</td>
        <td><span class="u-tag u-tag--ok">Active</span></td>
      </tr>
      <tr>
        <td>Sam Lee</td>
        <td>Example role</td>
        <td><span class="u-tag">Pending</span></td>
      </tr>
    </tbody>
  </table>
</div>

Forms

Wrap or associate every control with its label. Validation and error messages belong to your app.

Uploads stay in your inbox until finalized.
html
<div class="u-field">
  <label for="ex-deal">Transaction</label>
  <input id="ex-deal" type="text" placeholder="e.g. 100 Example St" />
</div>
<div class="u-field">
  <label for="ex-category">Category</label>
  <span class="u-select">
    <select id="ex-category" aria-describedby="ex-category-hint">
      <option>agreement</option>
      <option>disclosure</option>
    </select>
  </span>
  <span class="u-hint" id="ex-category-hint">Uploads stay in your inbox until finalized.</span>
</div>
<div class="u-field">
  <label for="ex-file">File</label>
  <input id="ex-file" type="file" />
</div>

Empty state

No records yet

Upload a document to start.

html
<div class="u-empty">
  <p class="u-heading">No records yet</p>
  <p>Upload a document to start.</p>
</div>

Progress and checks

Example document Done
Example step In progress
html
<div class="u-progress" role="progressbar" aria-label="Example progress" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100">
  <span class="s-w40"></span>
</div>
<div class="u-check u-check--done">
  <span class="u-check__status" aria-hidden="true"></span>
  <span>
    <span class="u-check__title">Example document</span>
    <span class="u-check__detail">Done</span>
  </span>
</div>
<div class="u-check u-check--progress">
  <span class="u-check__status" aria-hidden="true"></span>
  <span>
    <span class="u-check__title">Example step</span>
    <span class="u-check__detail">In progress</span>
  </span>
</div>

Atmosphere

The wave motif behind this page. One implementation draws the animated canvas and the static SVG. It pauses for reduced motion and in hidden tabs.

html
<canvas id="atmosphere" class="u-atmosphere" aria-hidden="true"></canvas>
<script type="module" src="/design/atmosphere.js"></script>

Or mount it yourself, and dispose of it when the view unmounts:

js
import { mountAtmosphere } from "@uniform/design/waves";

const dispose = mountAtmosphere(document.querySelector("#atmosphere"));

For SVG output, pass only trusted values; the helpers reject out-of-range sizes and non-hex colors.

Assets

Brand marks ship in dist/svg/, fonts in dist/fonts/. Use the on-dark variants on dark surfaces.

Releases

Every vX.Y.Z tag becomes a GitHub Release once the full checks pass, with notes, the npm tarball, SHA256SUMS and a signed build-provenance attestation. Upgrade on purpose: change the pinned tag, read the changelog, then test.

sh
gh attestation verify uniform-design-0.1.7.tgz -R UniformChicago/design

Licensing

Code is Apache-2.0. Uniform names, logos, wordmarks and icons are all rights reserved (LICENSE-BRAND). Fonts are under the SIL Open Font License.