Polarize
GitHub Get in touch

Work · Repository

polarize-ui

Polarize's design system for scientific interfaces: tokens, zero-build CSS, evidence badges, shadcn/ui + visx React components. Live Storybook ↓

  • TypeScript
  • MIT licence
  • 1 post
  • 5 merged PRs

View on GitHub → Changelog ↓ Homepage →

Posts about it

README

Live Storybook → · Releases

The design system behind Polarize’s research tools. It is built for interfaces that show scientific data honestly: every measured, modelled or speculative value carries a label, and the label can never be hidden.

A label may never be hidden. Its explanation must be.

It comes in two forms that share one set of tokens:

  • Zero-build CSS + custom elements — link a stylesheet, no npm, no bundler.
  • React — shadcn/ui components on the same theme, plus publication layouts, evidence badges and visx charts.

How we choose and judge a view for scientific data (the question first, five things every view states, forms for common data, simulation views, demo-data rules): METHOD.md.

What is in it

Science: the charts and analysis views (the point of the library)

   
Charts LineChart, BarChart, ScatterPlot (least-squares fit with a labelled 95% confidence or prediction band, fitted only over the measured range, with n, slope interval, r, R² and p printed; linearFit() gives the same numbers without the chart). A threshold is drawn on the axes it judges, a log axis says so in its label, thinning a trace for display is printed on the panel, and a shared or independent y-scale across panels is declared.
Signals Comodulogram + computeComodulogram(): phase–amplitude coupling (Tort MI) with a family-wise surrogate threshold, significant cells outlined, and unresolvable cells hatched instead of shown as zero. Heatmap (spectrograms, time–frequency maps) on a one-hue magnitude ramp with a colour bar, hover readout, and counted clipping. Waveform with marked regions.
Statistics NullDistribution and nullTest(): an observed statistic against its surrogate or permutation null, with the permutation p printed with its count, z beside it, and a warning when p is at its floor.
Evidence Tier (a MEASURED / PREDICTED / REFUTED badge that can’t be hidden), Value (measured and predicted never styled alike), Readout (figures with their source), and Note + DocsProvider (explanations in a drawer).

Source: src/science/. Every view is in the live Storybook under Science.

General UI

   
tokens.json The single source of truth: colour, type, spacing, the tier ladder, a colour-blind-validated categorical palette and a one-hue sequential ramp.
design.css + design.js The zero-build form, generated from the tokens: link it and set <body class="ui">. Custom elements: <ui-tier>, <ui-note>, <ui-docs>.
publication.css Layouts for a research blog: sidebar shell, post list, article, prose, source cards, citations, claims, references, pager, footer. React versions render the same classes.
landing.css A lab’s front door: top bar, hero, section heads, process steps, project features, repository cards, a changelog feed, article and story cards, archival plates, the principle block and the site footer. React: src/components/landing.tsx.
trajectory3d.js A path through three dimensions with a marker at the current time, on a plain canvas: mountTrajectory3D(host, options). Drag to turn, pinch to zoom. No dependencies; colour from the tokens. See INTEGRATION.md § 5.
network3d.js An interactive 3-D node-link network on a plain canvas: mountNetwork3D(host, options). The caller supplies geometry and visual state; the component supplies projection, orbit, zoom and depth sorting.
docs.css + docs.js A documentation site for a library or tool: top bar with ⌘K search, left nav and “on this page” column, an always-dark code window, the example panel (one real call with its code, input and output plots), an API reference entry, package and dataset cards, callouts and chips. docs.js wires it through data-ui-* attributes and exports renderPlot / highlight. React: src/components/docs.tsx. How to build one for an existing repository: DOCS-SITES.md.
specimens.css Field-guide style illustrations drawn in theme colours (SPECIMEN-ICONS.md).
src/components/ shadcn/ui components restyled to the type rules, typography (Display, Eyebrow, Standfirst), the publication layouts and Specimen.
fonts/, icons/ Self-hosted Instrument Serif, Inter, IBM Plex Mono, Literata (OFL) and a Phosphor icon sprite (MIT).

Using it

It is not published to npm. If there is enough interest we will happily turn it into a proper npm package. Until then, use it straight from this repository, always pinned to a release tag, or fork or clone it and make it your own.

In a React app (Vite + Tailwind v4)

npm install github:polarizetech/polarize-ui#v0.5.2     # or: yarn add github:polarizetech/polarize-ui#v0.5.2
/* your app's main stylesheet */
@import "tailwindcss";
@import "tw-animate-css";
@import "@polarizetech/polarize-ui/theme.css";
@source "../node_modules/@polarizetech/polarize-ui/src";
import { Heatmap, NullDistribution, LineChart, Tier, Button } from "@polarizetech/polarize-ui/react"
  • The package ships TypeScript source and your bundler compiles it; there is no build step here. It is type-checked with noUnusedLocals/noUnusedParameters on, so it compiles under strict app settings.
  • Dark mode is a .dark class on <html>.
  • Importing from /react also loads the self-hosted fonts.

In a plain HTML page

Add it as a git submodule pinned to a tag, and link the files:

git submodule add https://github.com/polarizetech/polarize-ui.git design
git -C design checkout v0.5.2
<link rel="stylesheet" href="https://github.com/polarizetech/polarize-ui/blob/main/design/design.css">
<link rel="stylesheet" href="https://github.com/polarizetech/polarize-ui/blob/main/design/publication.css">   <!-- optional: blog layouts -->
<script type="module" src="https://raw.githubusercontent.com/polarizetech/polarize-ui/main/design/design.js"></script>
<body class="ui">

The full wiring guide, including how to serve the fonts, is INTEGRATION.md.

Staying up to date

  • Watch → Custom → Releases on this repository to be told when a new version is out. Each release has notes listing what changed.
  • To update, change the tag you pin: the #v… in your dependency, or git -C design checkout v… for a submodule.
  • Read the release notes before crossing a minor version while we are below 1.0 (see below).

Versioning

Releases follow Semantic Versioning. While the version is below 1.0.0, the rule is the one npm applies to 0.x versions:

bump below 1.0.0 from 1.0.0 on
patch (0.5.2 → 0.5.3) never breaking never breaking
minor (0.5.x → 0.6.0) may be breaking: read the release notes new features, never breaking
major — breaking

Covered by that promise (the public API):

  • everything exported from @polarizetech/polarize-ui/react, and its props;
  • the stylesheet entry points (design.css, publication.css, specimens.css, theme.css) and the ui-* class names and modifiers they define;
  • the CSS custom properties (--background, --space-4, --tier-measured, …) and the keys in tokens.json;
  • the custom elements in design.js and their attributes.

Not covered: exact colour values and spacing (a tweak is a patch release, not a breaking change), the stories, the build scripts, and anything under .storybook/ or .github/.

Removing or renaming anything in the public API always produces a breaking bump: scripts/api_surface.py lists that surface, and the release workflow compares it with the previous release. What was removed is listed at the top of the release notes. Changes it cannot see, such as a prop’s type or a component’s behaviour, have to be marked by whoever makes them, and the contributor guidance (AGENTS.md) requires it.

Every push to main is released automatically by .github/workflows/release.yml, which applies these rules: a commit whose subject has !: (for example refactor!: rename Label to Eyebrow) or a BREAKING CHANGE: line produces a breaking bump, and anything else produces a patch. Setting the version in package.json by hand always wins.

Working on it

npm install
npm run storybook        # http://localhost:6006
python3 dev_check.py     # tokens, contrast, the type rule, generated-file drift

Edit tokens.json, never design.css; regenerate with python3 build_css.py and python3 shadcn/build_shadcn.py. src/ uses relative imports only. An @/ import would resolve against the consuming app’s alias, and dev_check.py rejects it. The shadcn CLI writes @/ imports, so rewrite them after npx shadcn add.

The Storybook’s own interface follows the viewer’s light or dark setting, and so does its Theme toolbar button. The MCP add-on (@storybook/addon-mcp) lets coding agents find the components and their stories.

Where it is used

These Polarize repos track new releases automatically. A daily workflow in each one moves its pin to the newest tag, runs that repo’s own checks, and commits and deploys only if they pass.

repo pin gate before it commits
polarizetech/polarize.tech design/ submodule check_design.py, validate_posts.py
joshuaanderton/joshuaanderton.ca package.json git dependency tsc -b && vite build, then the Pages deploy
polarizetech/audio-projects tools/design submodule tools/design/dev_check.py

Background and design decisions: CLAUDE.md. React specifics: shadcn/SHADCN.md.

Licence

Code: MIT (see LICENSE). Fonts: SIL Open Font License, texts in fonts/. Icons: Phosphor, MIT.

Changelog

2026-10-18 #5

Add reusable 3D node-link network view

Summary

  • add a dependency-free mountNetwork3D renderer with orbit, zoom, depth sorting, token colours, and accessible keyboard controls
  • ship public TypeScript declarations and integration documentation
  • add a synthetic Storybook example and API/design checks

Verification

  • python3 dev_check.py: 507/507 passed
  • TypeScript: passed
  • API surface removal check against v0.6.0: passed

This is an additive public API. It contains only a standard node-link visualization and synthetic story data; Neural Field Lab’s model remains in world-seeding.

2026-10-15 #4

feat!: remove the cell field; the homepage story is the shipped page

Removes the animated cell field (<ui-cellfield>, CellField, ./cellfield.js, .ui-cellfield*) at the operator’s request, and rebuilds the Landing homepage story as the page polarize.tech now ships (fieldguide). Adds an optional media prop to ProjectFeature.

Breaking. api_surface.py --removed v0.5.19 lists exactly the cell field’s surface. No consumer uses it: polarize.tech dropped it in v0.5.19; joshuaanderton.ca and the monorepo never used it.

Checks: dev_check 491/491, build_css/build_shadcn –check, tsc; Storybook homepage story renders with no errors.

🤖 Generated with Claude Code

2026-10-15 #3

feat: fieldguide theme — white, magenta, Geist

Opt-in fieldguide theme (white page, magenta accent, Geist sans) and additive landing components for polarize.tech’s restyle: .ui-action, .ui-stats, .ui-steps--rows, .ui-topics, .ui-specimen-tile, .ui-plate--ink, and the crab/neuron/magnet specimens.

Non-breaking: existing themes render unchanged (cards read --landing-radius/--landing-fill with the old values as fallbacks); api_surface.py --removed v0.5.18 is empty. dev_check 497/497, build_css/build_shadcn –check, tsc pass.

🤖 Generated with Claude Code

2026-10-07 #2

feat: trajectory3d.js, a 3-D path with a time marker (zero-build)

What

mountTrajectory3D(host, options) draws a path through three dimensions on a plain 2-D canvas, with a marker at the current time and the last trail seconds drawn heavier. It returns {update, setTime, show, resetView, destroy}. Zero-build, no dependencies, same shape as cellfield.js (a mount* function plus a .d.ts).

  • trajectory3d.js + trajectory3d.d.ts at the repo root
  • package.json: shipped in files, new entry point ./trajectory3d.js
  • dev_check.py: check_trajectory3d(); scripts/api_surface.py tracks the export and the handle methods, so a later rename forces a breaking bump
  • Story: Science / 3-D views / Path with a time marker (zero-build, synthetic)
  • Docs: INTEGRATION.md § 5 (and the allow-list line in § 1), a row in README.md

Versioning

Additive, so a patch. A new entry point and nothing existing changes. The React Trajectory3D (the hodogram on Scene3D) is a separate view and is untouched; no React wrapper was added for this module, to avoid a name collision.

Public-repo rule

  • Method: a 3-D state-space path / phase portrait with a playhead. Standard.
  • Data: the story integrates the Lorenz system (Lorenz 1963) in the story itself. Synthetic, and labelled so.
  • Findings: none shown or stated.

Differences from the private copy it was ported from

  • Axes are right-handed and positive pitch looks down, as in src/science/space (the original was left-handed, seen from below). Paths appear mirrored relative to the original.
  • On a drag the near side follows the pointer (direction reversed).
  • A two-finger touch pinch zooms. Before, only Ctrl/Cmd-scroll did.
  • Arrow keys turn and +/- zoom when the canvas has focus.
  • Redraws on a theme change; falls back to --chart-1 / --chart-4 on the React theme.

Checked

  • python3 dev_check.py: 429/429
  • npx tsc -p .: clean
  • python3 scripts/api_surface.py --removed v0.5.16: nothing removed
  • In a browser: a zero-build page on design.css and the Storybook story, light and dark, and at 375 px (no sideways scroll). Turn, pinch, Ctrl-scroll zoom, keyboard, reset, destroy and live theme switch work; a plain scroll is left to the page. Pinch was tested with synthetic pointer events, not on a real phone.

Note for consumers

The canvas takes every touch that starts on it (touch-action: none), so a page needs scroll room beside or below it on a phone. Documented in INTEGRATION.md.

🤖 Generated with Claude Code

2026-09-28 #1

feat: landing layer and cell field

A lab’s front door, in both renderers, drawn from the polarize.tech homepage redesign.

What’s added

  • landing.css (zero-build) and src/components/landing.tsx (React), rendering the same classes: TopNav, Hero, Cta, Section, ProcessSteps, Promises, ProjectFeature, RepoCard, ChangeFeed, FeaturedArticle, ArticleCard, MetaLine, StoryCard / StoryRow / StoryGrid, Plate, Principle, TierLegend, SiteFooter. Responsive to phone width; the top bar folds into a <details> menu with no script.
  • cellfield.js: <ui-cellfield>, a WebGL speck field (clusters at different depths, per-cell polarisation, occasional coordinated waves, an optional hero cell). Decoration only: aria-hidden, no fetch, one still frame under prefers-reduced-motion. React: <CellField>, same engine, typed by cellfield.d.ts.
  • Token: radius.xs (4px).
  • Checks: dev_check.py holds landing.css to the publication.css rules (generalised as check_layer) and checks cellfield.js; scripts/api_surface.py lists both files.
  • Storybook: General UI/Landing: the full homepage at desktop and phone, plus each part.

Versioning

Additive only: nothing renamed or removed (api_surface.py --removed v0.5.5 is empty), so this is a patch release.

Checks

  • python3 dev_check.py: 309/309
  • build_css.py --check, build_shadcn.py --check: up to date
  • tsc -p .: clean

polarize.tech adopts it in polarizetech/polarize.tech (branch home-redesign) once this is released.

🤖 Generated with Claude Code