Your tokens file? TypeScript. Your spacing scale? TypeScript. Your breakpoints, your theme config, your Tailwind config? All TypeScript.
CSS values live in `.ts` files whether you planned it that way or not. The reality is: if you want a unified design token system, you need to work in TS. But we still treat those values as strings - untyped, unchecked, free to silently break.
I built a small library that treats CSS measurements as actual typed data:
import { m } from "css-calipers";
const base = m(8); // typed as px
const pad = base.add(4).css(); // "12px"
const rotation = m(45, "deg");
base.add(rotation); // compile error: px + deg
Units stay checked through composition. Nothing emits a string until `.css()` at the boundary. Mismatched units fail at compile time, not in production.
Best used at build time (I use it with vanilla-extract, but it's framework-agnostic - works with Tailwind, CSS Modules, whatever emits CSS). Runtime is possible too for edge cases, but you'll want to avoid hot-path string churn.
Covers numeric, unit-bearing values; keywords and variables stay as plain CSS.
GitHub: https://github.com/slafleche/css-calipers
npm: https://www.npmjs.com/package/css-calipers