css-calipers 1.0.0 → 1.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +1 -1
- package/README.md +70 -424
- package/dist/cjs/color/formats/display-p3/display-p3.js +25 -0
- package/dist/cjs/color/formats/escalate.js +41 -0
- package/dist/cjs/color/formats/hex/hex.js +28 -0
- package/dist/cjs/color/formats/hexAlpha/hexAlpha.js +25 -0
- package/dist/cjs/color/formats/hsl/hsl.js +23 -0
- package/dist/cjs/color/formats/hwb/hwb.js +24 -0
- package/dist/cjs/color/formats/index.js +36 -0
- package/dist/cjs/color/formats/internals.js +93 -0
- package/dist/cjs/color/formats/lab/lab.js +24 -0
- package/dist/cjs/color/formats/lch/lch.js +23 -0
- package/dist/cjs/color/formats/oklab/oklab.js +24 -0
- package/dist/cjs/color/formats/oklch/oklch.js +28 -0
- package/dist/cjs/color/formats/resolve.js +16 -0
- package/dist/cjs/color/formats/rgb/rgb.js +29 -0
- package/dist/cjs/color/formats/rgba/rgba.js +27 -0
- package/dist/cjs/color/formats/types.js +11 -0
- package/dist/cjs/color/index.js +619 -0
- package/dist/cjs/color/types.js +2 -0
- package/dist/cjs/core.js +11 -8
- package/dist/cjs/corpus.js +53 -0
- package/dist/cjs/css-values/cssValues.js +78 -0
- package/dist/cjs/css-values/index.js +36 -0
- package/dist/cjs/css-values/multi.js +290 -0
- package/dist/cjs/css-values/propertyMap.js +2 -0
- package/dist/cjs/css-values/spec.js +197 -0
- package/dist/cjs/default.js +17 -0
- package/dist/cjs/factory.js +1 -1
- package/dist/cjs/float.js +110 -0
- package/dist/cjs/index.js +29 -9
- package/dist/cjs/integer.js +114 -0
- package/dist/cjs/internal/createCoreApi.js +103 -38
- package/dist/cjs/internal/errors.js +13 -14
- package/dist/cjs/internal/toPlainDecimal.js +31 -0
- package/dist/cjs/measurements.js +41 -0
- package/dist/cjs/ratio.js +57 -22
- package/dist/cjs/scalar.js +9 -0
- package/dist/cjs/units/absolute.js +8 -8
- package/dist/cjs/units/angle.js +5 -5
- package/dist/cjs/units/container.js +7 -7
- package/dist/cjs/units/font-relative.js +13 -13
- package/dist/cjs/units/frequency.js +3 -3
- package/dist/cjs/units/grid.js +2 -2
- package/dist/cjs/units/index.js +9 -9
- package/dist/cjs/units/percent.js +4 -4
- package/dist/cjs/units/resolution.js +4 -4
- package/dist/cjs/units/time.js +3 -3
- package/dist/cjs/units/viewport-dynamic.js +7 -7
- package/dist/cjs/units/viewport-large.js +7 -7
- package/dist/cjs/units/viewport-small.js +7 -7
- package/dist/cjs/units/viewport.js +7 -7
- package/dist/esm/color/formats/display-p3/display-p3.d.ts +10 -0
- package/dist/esm/color/formats/display-p3/display-p3.d.ts.map +1 -0
- package/dist/esm/color/formats/display-p3/display-p3.js +22 -0
- package/dist/esm/color/formats/escalate.d.ts +10 -0
- package/dist/esm/color/formats/escalate.d.ts.map +1 -0
- package/dist/esm/color/formats/escalate.js +37 -0
- package/dist/esm/color/formats/hex/hex.d.ts +9 -0
- package/dist/esm/color/formats/hex/hex.d.ts.map +1 -0
- package/dist/esm/color/formats/hex/hex.js +25 -0
- package/dist/esm/color/formats/hexAlpha/hexAlpha.d.ts +9 -0
- package/dist/esm/color/formats/hexAlpha/hexAlpha.d.ts.map +1 -0
- package/dist/esm/color/formats/hexAlpha/hexAlpha.js +22 -0
- package/dist/esm/color/formats/hsl/hsl.d.ts +8 -0
- package/dist/esm/color/formats/hsl/hsl.d.ts.map +1 -0
- package/dist/esm/color/formats/hsl/hsl.js +20 -0
- package/dist/esm/color/formats/hwb/hwb.d.ts +9 -0
- package/dist/esm/color/formats/hwb/hwb.d.ts.map +1 -0
- package/dist/esm/color/formats/hwb/hwb.js +21 -0
- package/dist/esm/color/formats/index.d.ts +24 -0
- package/dist/esm/color/formats/index.d.ts.map +1 -0
- package/dist/esm/color/formats/index.js +32 -0
- package/dist/esm/color/formats/internals.d.ts +35 -0
- package/dist/esm/color/formats/internals.d.ts.map +1 -0
- package/dist/esm/color/formats/internals.js +78 -0
- package/dist/esm/color/formats/lab/lab.d.ts +9 -0
- package/dist/esm/color/formats/lab/lab.d.ts.map +1 -0
- package/dist/esm/color/formats/lab/lab.js +21 -0
- package/dist/esm/color/formats/lch/lch.d.ts +8 -0
- package/dist/esm/color/formats/lch/lch.d.ts.map +1 -0
- package/dist/esm/color/formats/lch/lch.js +20 -0
- package/dist/esm/color/formats/oklab/oklab.d.ts +9 -0
- package/dist/esm/color/formats/oklab/oklab.d.ts.map +1 -0
- package/dist/esm/color/formats/oklab/oklab.js +21 -0
- package/dist/esm/color/formats/oklch/oklch.d.ts +13 -0
- package/dist/esm/color/formats/oklch/oklch.d.ts.map +1 -0
- package/dist/esm/color/formats/oklch/oklch.js +25 -0
- package/dist/esm/color/formats/resolve.d.ts +15 -0
- package/dist/esm/color/formats/resolve.d.ts.map +1 -0
- package/dist/esm/color/formats/resolve.js +12 -0
- package/dist/esm/color/formats/rgb/rgb.d.ts +11 -0
- package/dist/esm/color/formats/rgb/rgb.d.ts.map +1 -0
- package/dist/esm/color/formats/rgb/rgb.js +26 -0
- package/dist/esm/color/formats/rgba/rgba.d.ts +9 -0
- package/dist/esm/color/formats/rgba/rgba.d.ts.map +1 -0
- package/dist/esm/color/formats/rgba/rgba.js +24 -0
- package/dist/esm/color/formats/types.d.ts +48 -0
- package/dist/esm/color/formats/types.d.ts.map +1 -0
- package/dist/esm/color/formats/types.js +7 -0
- package/dist/esm/color/index.d.ts +81 -0
- package/dist/esm/color/index.d.ts.map +1 -0
- package/dist/esm/color/index.js +609 -0
- package/dist/esm/color/types.d.ts +294 -0
- package/dist/esm/color/types.d.ts.map +1 -0
- package/dist/esm/color/types.js +1 -0
- package/dist/esm/core.d.ts +76 -270
- package/dist/esm/core.d.ts.map +1 -1
- package/dist/esm/core.js +5 -7
- package/dist/esm/corpus.d.ts +26 -0
- package/dist/esm/corpus.d.ts.map +1 -0
- package/dist/esm/corpus.js +37 -0
- package/dist/esm/css-values/cssValues.d.ts +60 -0
- package/dist/esm/css-values/cssValues.d.ts.map +1 -0
- package/dist/esm/css-values/cssValues.js +74 -0
- package/dist/esm/css-values/index.d.ts +6 -0
- package/dist/esm/css-values/index.d.ts.map +1 -0
- package/dist/esm/css-values/index.js +12 -0
- package/dist/esm/css-values/multi.d.ts +188 -0
- package/dist/esm/css-values/multi.d.ts.map +1 -0
- package/dist/esm/css-values/multi.js +281 -0
- package/dist/esm/css-values/propertyMap.d.ts +39 -0
- package/dist/esm/css-values/propertyMap.d.ts.map +1 -0
- package/dist/esm/css-values/propertyMap.js +1 -0
- package/dist/esm/css-values/spec.d.ts +36 -0
- package/dist/esm/css-values/spec.d.ts.map +1 -0
- package/dist/esm/css-values/spec.js +194 -0
- package/dist/esm/default.d.ts +267 -0
- package/dist/esm/default.d.ts.map +1 -0
- package/dist/esm/default.js +14 -0
- package/dist/esm/factory.d.ts +1 -1
- package/dist/esm/factory.d.ts.map +1 -1
- package/dist/esm/factory.js +3 -3
- package/dist/esm/float.d.ts +35 -0
- package/dist/esm/float.d.ts.map +1 -0
- package/dist/esm/float.js +104 -0
- package/dist/esm/index.d.ts +11 -9
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +19 -9
- package/dist/esm/integer.d.ts +36 -0
- package/dist/esm/integer.d.ts.map +1 -0
- package/dist/esm/integer.js +108 -0
- package/dist/esm/internal/buildMeasurementCreationError.tests.d.ts +1 -1
- package/dist/esm/internal/buildMeasurementCreationError.tests.d.ts.map +1 -1
- package/dist/esm/internal/createCoreApi.d.ts +9 -1
- package/dist/esm/internal/createCoreApi.d.ts.map +1 -1
- package/dist/esm/internal/createCoreApi.js +103 -38
- package/dist/esm/internal/createUnitsApi.d.ts +1 -1
- package/dist/esm/internal/createUnitsApi.d.ts.map +1 -1
- package/dist/esm/internal/errors.d.ts +3 -3
- package/dist/esm/internal/errors.d.ts.map +1 -1
- package/dist/esm/internal/errors.js +13 -14
- package/dist/esm/internal/toPlainDecimal.d.ts +2 -0
- package/dist/esm/internal/toPlainDecimal.d.ts.map +1 -0
- package/dist/esm/internal/toPlainDecimal.js +27 -0
- package/dist/esm/measurements.d.ts +16 -0
- package/dist/esm/measurements.d.ts.map +1 -0
- package/dist/esm/measurements.js +25 -0
- package/dist/esm/ratio.d.ts +14 -5
- package/dist/esm/ratio.d.ts.map +1 -1
- package/dist/esm/ratio.js +57 -22
- package/dist/esm/scalar.d.ts +14 -0
- package/dist/esm/scalar.d.ts.map +1 -0
- package/dist/esm/scalar.js +5 -0
- package/dist/esm/units/absolute.d.ts +1 -1
- package/dist/esm/units/absolute.d.ts.map +1 -1
- package/dist/esm/units/absolute.js +1 -1
- package/dist/esm/units/angle.d.ts +1 -1
- package/dist/esm/units/angle.d.ts.map +1 -1
- package/dist/esm/units/angle.js +1 -1
- package/dist/esm/units/container.d.ts +1 -1
- package/dist/esm/units/container.d.ts.map +1 -1
- package/dist/esm/units/container.js +1 -1
- package/dist/esm/units/font-relative.d.ts +1 -1
- package/dist/esm/units/font-relative.d.ts.map +1 -1
- package/dist/esm/units/font-relative.js +1 -1
- package/dist/esm/units/frequency.d.ts +1 -1
- package/dist/esm/units/frequency.d.ts.map +1 -1
- package/dist/esm/units/frequency.js +1 -1
- package/dist/esm/units/grid.d.ts +1 -1
- package/dist/esm/units/grid.d.ts.map +1 -1
- package/dist/esm/units/grid.js +1 -1
- package/dist/esm/units/index.d.ts +9 -9
- package/dist/esm/units/index.d.ts.map +1 -1
- package/dist/esm/units/index.js +9 -9
- package/dist/esm/units/percent.d.ts +1 -1
- package/dist/esm/units/percent.d.ts.map +1 -1
- package/dist/esm/units/percent.js +1 -1
- package/dist/esm/units/resolution.d.ts +1 -1
- package/dist/esm/units/resolution.d.ts.map +1 -1
- package/dist/esm/units/resolution.js +1 -1
- package/dist/esm/units/time.d.ts +1 -1
- package/dist/esm/units/time.d.ts.map +1 -1
- package/dist/esm/units/time.js +1 -1
- package/dist/esm/units/viewport-dynamic.d.ts +1 -1
- package/dist/esm/units/viewport-dynamic.d.ts.map +1 -1
- package/dist/esm/units/viewport-dynamic.js +1 -1
- package/dist/esm/units/viewport-large.d.ts +1 -1
- package/dist/esm/units/viewport-large.d.ts.map +1 -1
- package/dist/esm/units/viewport-large.js +1 -1
- package/dist/esm/units/viewport-small.d.ts +1 -1
- package/dist/esm/units/viewport-small.d.ts.map +1 -1
- package/dist/esm/units/viewport-small.js +1 -1
- package/dist/esm/units/viewport.d.ts +1 -1
- package/dist/esm/units/viewport.d.ts.map +1 -1
- package/dist/esm/units/viewport.js +1 -1
- package/package.json +54 -12
package/LICENSE.txt
CHANGED
package/README.md
CHANGED
|
@@ -1,483 +1,129 @@
|
|
|
1
|
-
#
|
|
1
|
+
# css-calipers
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[](https://www.npmjs.com/package/css-calipers)
|
|
5
|
-
[](./LICENSE.txt)
|
|
3
|
+
The missing pieces of typed CSS input, build-time-validated: colour, measurements, integers, floats, and ratios.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
Compile-time unit safety for numeric, unit-bearing CSS values, with no surprises at runtime.
|
|
5
|
+
## The problem
|
|
9
6
|
|
|
10
|
-
|
|
11
|
-
// Before: pull the number AND the unit apart, do the math, glue them back
|
|
12
|
-
const match = base.match(/^(-?\d*\.?\d+)([a-z%]+)$/i);
|
|
13
|
-
if (!match) throw new Error(`Bad measurement: ${base}`);
|
|
14
|
-
const [, numStr, unit] = match;
|
|
15
|
-
const num = parseFloat(numStr);
|
|
16
|
-
const pad = `${num + 4}${unit}`; // and nobody checked that `unit` matches what the caller expects
|
|
17
|
-
|
|
18
|
-
// After: typed math, units enforced by the compiler
|
|
19
|
-
const base = m(8); // or m(8, "rem"), m(1.5, "em"), etc.
|
|
20
|
-
const pad = base.add(4).css(); // type error if units don't match
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
A small TypeScript library for **type-safe CSS measurements**. Do arithmetic on real numbers with the unit attached, let the compiler catch `px`-vs-`rem` mistakes, and emit a CSS string only at the edge.
|
|
24
|
-
|
|
25
|
-
At a glance:
|
|
26
|
-
|
|
27
|
-
- Create measurements with `m` from a number and a unit; if you omit the unit, it defaults to `px` and is typed as the px measurement type.
|
|
28
|
-
- Do unit-safe math with methods like `add` and `multiply`, then call `.css()`
|
|
29
|
-
at the edge to get a CSS string (for example "10px").
|
|
7
|
+
CSS input values are untyped. A measurement is a string (`'8px'`), an opacity is a bare number, a colour is whatever string you typed. Nothing catches `opacity: 1.5`, a `px` value added to an `em`, or a `z-index` that is silently a float. The mistake surfaces in the browser, not the compiler.
|
|
30
8
|
|
|
31
|
-
##
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
npm install css-calipers
|
|
35
|
-
```
|
|
9
|
+
## Highlights
|
|
36
10
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
## Quick start
|
|
11
|
+
`.css()` on a colour emits the simplest format that holds it without losing information:
|
|
40
12
|
|
|
41
13
|
```ts
|
|
42
|
-
import {
|
|
43
|
-
|
|
44
|
-
// Declare vars
|
|
45
|
-
const paddingBase = m(4); // defaults to px (and is typed as a px measurement) when no unit is specified
|
|
46
|
-
const rotation = m(45, "deg"); // equivalent to a dedicated helper: mDeg(45)
|
|
14
|
+
import { color } from 'css-calipers';
|
|
47
15
|
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// Emit only at the end in CSS (at runtime or in a build step)
|
|
53
|
-
const style = {
|
|
54
|
-
padding: paddingBase.css(),
|
|
55
|
-
transform: `rotate(${rotation.double().css()})`, // 90deg
|
|
56
|
-
};
|
|
16
|
+
color('#3366cc').css(); // '#3366cc' (opaque -> hex)
|
|
17
|
+
color('#3366cc').alpha(0.5).css(); // 'rgba(51, 102, 204, 0.5)' (translucent -> rgba)
|
|
18
|
+
color('oklch(0.7 0.37 150)').css(); // 'oklch(0.7 0.37 150 / 1)' (wide gamut -> oklch)
|
|
57
19
|
```
|
|
58
20
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## Status & support
|
|
64
|
-
|
|
65
|
-
- Status: stable `1.0` release of the measurement layer, part of the [CSS-Bookends](https://github.com/slafleche/css-bookends) umbrella.
|
|
66
|
-
- Questions or bugs: open an issue on GitHub (see the repository link at the top of this page or in `package.json`).
|
|
67
|
-
- Tooling: tested primarily with TypeScript 5.6+ on Node 18+.
|
|
68
|
-
- Support: this is a solo, early-stage project. If it saves you time, you can [buy me a coffee](https://buymeacoffee.com/slafleche) to support continued work.
|
|
69
|
-
|
|
70
|
-
---
|
|
71
|
-
|
|
72
|
-
## Media queries
|
|
21
|
+
That ladder is the default and is reconfigurable: set your own output format or priority list, per call or per instance.
|
|
73
22
|
|
|
74
|
-
|
|
75
|
-
layer. They live in
|
|
76
|
-
[`@css-bookends/media-queries`](https://www.npmjs.com/package/@css-bookends/media-queries),
|
|
77
|
-
a book in the [CSS-Bookends](https://github.com/slafleche/css-bookends) umbrella
|
|
78
|
-
that builds on this lexicon.
|
|
79
|
-
|
|
80
|
-
**Upgrading to v1?** The only change is media queries. If you never used the media
|
|
81
|
-
query helper, v1 is a drop-in, nothing to change, no functional difference. If you
|
|
82
|
-
did use it, switch to [CSS-Bookends](https://github.com/slafleche/css-bookends),
|
|
83
|
-
which has the same measurement layer plus the media query helper and the rest of
|
|
84
|
-
the umbrella.
|
|
85
|
-
|
|
86
|
-
---
|
|
87
|
-
|
|
88
|
-
## Features
|
|
89
|
-
|
|
90
|
-
- **Compile-time unit validation.** Prevents mixing incompatible units.
|
|
91
|
-
- **Arithmetic safety.** Operate only within matching units; explicit when
|
|
92
|
-
converting.
|
|
93
|
-
- **Explicit emission.** `.css()` outputs a typed string literal only when
|
|
94
|
-
needed.
|
|
95
|
-
- **Light runtime footprint.** Near-zero cost when emitted at build time.
|
|
96
|
-
- **Framework-agnostic.** Works anywhere TypeScript does.
|
|
97
|
-
|
|
98
|
-
Any numeric, unit-bearing CSS value is supported: `m` accepts any unit string you’d use in CSS
|
|
99
|
-
(`'px'`, `'rem'`, `'%'`, `'vw'`, `'deg'`, `'ms'`, …), and you can model new
|
|
100
|
-
measurements without waiting for a dedicated helper. For convenience and better
|
|
101
|
-
types, every standard CSS unit also has a named helper (for example
|
|
102
|
-
`mPx`, `mPercent`, `mVw`, `mEm`, `mMs`, `mFr`), which are equivalent to calling
|
|
103
|
-
`m(value, 'unit')` directly.
|
|
104
|
-
|
|
105
|
-
CSS-Calipers focuses exclusively on numeric, unit-bearing CSS values. Keywords
|
|
106
|
-
like `auto`, `fit-content`, or `max-content`, full shorthand strings,
|
|
107
|
-
`var(--token)`, or `calc(...)` expressions should remain explicit strings or
|
|
108
|
-
dedicated keyword types in your app or styling layer. Everything else stays as
|
|
109
|
-
plain CSS (see "Philosophy & Boundaries" below for more detail). For a concrete
|
|
110
|
-
example of this separation in a mixed-input helper, see
|
|
111
|
-
[examples/lineHeight-normalizer.example.ts](examples/lineHeight-normalizer.example.ts),
|
|
112
|
-
which keeps keywords and CSS variables as plain strings while using measurements
|
|
113
|
-
for numeric values.
|
|
114
|
-
|
|
115
|
-
---
|
|
116
|
-
|
|
117
|
-
## Should I use this?
|
|
118
|
-
|
|
119
|
-
CSS-Calipers is a good fit if:
|
|
120
|
-
|
|
121
|
-
- You already use TypeScript (or plan to) and want compile-time guarantees around CSS units.
|
|
122
|
-
- You have a design system or token layer where layout math and unit conversions matter.
|
|
123
|
-
- You care about catching unit mismatches and layout invariants early, in dev or tests.
|
|
124
|
-
This library is opinionated about types and emitting valid CSS at the edges,
|
|
125
|
-
but intentionally loose about how you structure or apply styles in between.
|
|
126
|
-
|
|
127
|
-
It’s probably overkill if:
|
|
128
|
-
|
|
129
|
-
- Your project has minimal custom layout math or relies mostly on utility classes/framework CSS.
|
|
130
|
-
- You don’t use TypeScript and aren’t looking for stronger typing around CSS values.
|
|
131
|
-
- You’re comfortable relying on manual discipline instead of typed measurements for units.
|
|
132
|
-
|
|
133
|
-
---
|
|
134
|
-
|
|
135
|
-
### Layout tokens example
|
|
23
|
+
Measurements are branded by unit, so a `px` cannot be added to an `em`, and arithmetic re-validates:
|
|
136
24
|
|
|
137
25
|
```ts
|
|
138
|
-
import { m
|
|
139
|
-
|
|
140
|
-
// Token-style measurements (px by default)
|
|
141
|
-
const spacing = m(8); // Defaults to px and is typed as a PxMeasurement
|
|
142
|
-
const cardPadding = spacing.multiply(2); // 16px
|
|
143
|
-
const gutter = spacing.multiply(1.5); // 12px
|
|
144
|
-
|
|
145
|
-
// Responsive bounds expressed in other units
|
|
146
|
-
const minWidthPercent = mPercent(75); // 75%; same as m(75, "%")
|
|
147
|
-
const maxWidthViewport = mVw(100); // 100vw; same as m(100, "vw")
|
|
148
|
-
|
|
149
|
-
// Derived content width in px
|
|
150
|
-
const contentBase = m(320);
|
|
151
|
-
const minCardWidth = m(260);
|
|
152
|
-
const maxCardWidth = m(360);
|
|
153
|
-
|
|
154
|
-
// In real code, these bounds typically come from tokens or configuration,
|
|
155
|
-
// so keeping the clamp in measurement space ensures units stay consistent.
|
|
156
|
-
const cardWidth = contentBase.clamp(minCardWidth, maxCardWidth);
|
|
157
|
-
|
|
158
|
-
// Unitless ratio you can reuse elsewhere
|
|
159
|
-
const ratio = contentBase.getValue() / spacing.getValue(); // returns a number, no unit
|
|
160
|
-
|
|
161
|
-
// Apply ratio to a different unit family
|
|
162
|
-
const heroHeight = mVh(40).multiply(ratio);
|
|
163
|
-
|
|
164
|
-
// Invalid arithmetic (different units) fails at compile time
|
|
165
|
-
const exampleError = cardWidth.add(heroHeight); // ❌ Type error (px vs vh) see error handling below
|
|
166
|
-
|
|
167
|
-
// Use plain numbers where they are just counts
|
|
168
|
-
const columns = 3;
|
|
169
|
-
|
|
170
|
-
// In development, enforce simple invariants so layout mistakes fail fast.
|
|
171
|
-
// In production, you can either rely on earlier validation or add a separate
|
|
172
|
-
// fallback path if this invariant is ever broken.
|
|
173
|
-
if (process.env.NODE_ENV !== "production") {
|
|
174
|
-
assertCondition(
|
|
175
|
-
() => columns > 0,
|
|
176
|
-
"cardGridStyles: columns must be greater than zero",
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Compose a simple grid layout
|
|
181
|
-
const cardGridStyles = {
|
|
182
|
-
display: "grid",
|
|
183
|
-
gap: gutter.css(),
|
|
184
|
-
// Keep fraction units as plain CSS alongside measurement-derived values
|
|
185
|
-
gridTemplateColumns: `repeat(${columns}, 1fr)`,
|
|
186
|
-
// width driven by card width + gutters
|
|
187
|
-
width: cardWidth
|
|
188
|
-
.multiply(columns)
|
|
189
|
-
.add(gutter.multiply(columns - 1))
|
|
190
|
-
.css(),
|
|
191
|
-
// container bounds in percent/viewport units
|
|
192
|
-
minWidth: minWidthPercent.css(),
|
|
193
|
-
maxWidth: maxWidthViewport.css(),
|
|
194
|
-
// derived hero height based on px ratio, expressed in vh and used inside a calc() string
|
|
195
|
-
// calc() stays plain CSS; CSS-Calipers only provides the numeric pieces
|
|
196
|
-
minHeight: `calc(${heroHeight.css()} + 10vh)`,
|
|
197
|
-
};
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
---
|
|
201
|
-
|
|
202
|
-
## Do custom checks your way
|
|
26
|
+
import { m } from 'css-calipers';
|
|
203
27
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
a theme override, hardcode a debug routine, or wire a global invariant; the
|
|
207
|
-
structure is up to you:
|
|
208
|
-
|
|
209
|
-
```ts
|
|
210
|
-
import { assertMatchingUnits } from "css-calipers";
|
|
211
|
-
import { formTokens } from "@/tokens/forms.tokens";
|
|
212
|
-
|
|
213
|
-
if (process.env.NODE_ENV !== "production") {
|
|
214
|
-
assertMatchingUnits(
|
|
215
|
-
formTokens.field.paddingBlock,
|
|
216
|
-
formTokens.field.paddingInline,
|
|
217
|
-
"Form control padding mismatch",
|
|
218
|
-
);
|
|
219
|
-
}
|
|
28
|
+
m(8).add(m(4)).css(); // '12px' (m() defaults to px)
|
|
29
|
+
m(2, 'rem').multiply(2).css(); // '4rem'
|
|
220
30
|
```
|
|
221
31
|
|
|
222
|
-
|
|
223
|
-
inside the components that need them, or in dedicated test helpers. For more
|
|
224
|
-
complete patterns, see the examples folder: the validation unit-tests example
|
|
225
|
-
([examples/validation-unit-tests.example.ts](examples/validation-unit-tests.example.ts)) shows how to
|
|
226
|
-
enforce spacing token invariants in a test suite, and the validation and runtime
|
|
227
|
-
checks example ([examples/validation-and-runtime-checks.example.ts](examples/validation-and-runtime-checks.example.ts))
|
|
228
|
-
shows how to apply dev-only guards around shared tokens in two different
|
|
229
|
-
consumers (an HTML snippet and a style object) using the same line-height
|
|
230
|
-
measurement.
|
|
231
|
-
|
|
232
|
-
---
|
|
233
|
-
|
|
234
|
-
## Error behavior
|
|
235
|
-
|
|
236
|
-
- Operations are fail-fast: when you call helpers like `add`, `divide`, `clamp`,
|
|
237
|
-
`measurementMin` / `measurementMax`, or the assertion helpers with invalid
|
|
238
|
-
input (for example, mismatched units or non-finite values), CSS-Calipers
|
|
239
|
-
throws a normal `Error`.
|
|
240
|
-
- Error messages include the operation name (for example,
|
|
241
|
-
`css-calipers.Measurement.divide` or `css-calipers.assertMatchingUnits`), the
|
|
242
|
-
relevant values/units, and any context string you pass in.
|
|
243
|
-
- The library does not catch these errors for you. You choose where to place
|
|
244
|
-
assertions and where (if anywhere) to catch and handle exceptions.
|
|
245
|
-
- In production, a common pattern is to wrap assertions in dev-only guards
|
|
246
|
-
(such as `if (process.env.NODE_ENV !== 'production')`) or to enforce
|
|
247
|
-
invariants in tests, so checks stay cheap and predictable at runtime.
|
|
248
|
-
|
|
249
|
-
For concrete uses of these errors in tests and dev-only guards, see
|
|
250
|
-
`TESTING.md` and the validation examples in
|
|
251
|
-
[examples/validation-unit-tests.example.ts](examples/validation-unit-tests.example.ts) and
|
|
252
|
-
[examples/validation-and-runtime-checks.example.ts](examples/validation-and-runtime-checks.example.ts).
|
|
253
|
-
|
|
254
|
-
---
|
|
255
|
-
|
|
256
|
-
## Common errors
|
|
257
|
-
|
|
258
|
-
### Non-finite measurement value
|
|
259
|
-
|
|
260
|
-
Example
|
|
261
|
-
|
|
262
|
-
```
|
|
263
|
-
css-calipers.m: Non-finite measurement value: undefined [code=CALIPERS_E_NONFINITE | helper=m | inputs=value=undefined, unit=px | stack=...]
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
What it means
|
|
267
|
-
|
|
268
|
-
- A measurement was constructed with undefined, NaN, or Infinity.
|
|
269
|
-
|
|
270
|
-
How to fix
|
|
271
|
-
|
|
272
|
-
- Provide a real numeric value and a unit (m(12), m(12, "px")).
|
|
273
|
-
- Add a context label so the error points to the calling helper or token (m(12, { context: "tokens.cardWidth" })).
|
|
32
|
+
Ratios accept typed integers and floats on either side:
|
|
274
33
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
Example
|
|
34
|
+
```ts
|
|
35
|
+
import { r, i, f } from 'css-calipers';
|
|
278
36
|
|
|
37
|
+
r(i(16), i(9)).css(); // '16/9'
|
|
38
|
+
r(f(1.5), i(2)).css(); // '1.5/2' (a float and an integer)
|
|
279
39
|
```
|
|
280
|
-
css-calipers.assertMatchingUnits: measurement unit mismatch: px vs em [code=CALIPERS_E_UNIT_MISMATCH]
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
What it means
|
|
284
40
|
|
|
285
|
-
-
|
|
41
|
+
Refinements turn a runtime check into a compile-time guarantee: the checked value is branded, so a function that demands a non-negative measurement cannot be handed an unchecked one.
|
|
286
42
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
- Normalize units at the source (convert em to px or vice versa).
|
|
290
|
-
- Add an assertMatchingUnits call where the values enter your system.
|
|
43
|
+
```ts
|
|
44
|
+
import { m, nonNegative, inRange } from 'css-calipers';
|
|
291
45
|
|
|
292
|
-
|
|
46
|
+
// `ensure` runs the check, then hands the value back with a HARDENED type. The brand
|
|
47
|
+
// proves it passed, so anything typed NonNegativeMeasurement is known to be checked.
|
|
48
|
+
const width = nonNegative.ensure(m(8)); // type: NonNegativeMeasurement<'px'> (throws if negative)
|
|
293
49
|
|
|
294
|
-
|
|
50
|
+
// `inRange` bakes its literal bounds into the type, so it is distinct from any other range.
|
|
51
|
+
const pct = inRange(0, 100).ensure(m(50, '%')); // type: InRangeMeasurement<'%', 0, 100>
|
|
295
52
|
|
|
296
|
-
|
|
297
|
-
|
|
53
|
+
// `hardenWith` always returns a valid, hardened value: if the input fails the check it
|
|
54
|
+
// falls back to a known-good one instead of throwing.
|
|
55
|
+
nonNegative.hardenWith(m(-4, 'px')).css(); // '0px' (-4 fails the check, so it falls back to 0)
|
|
298
56
|
```
|
|
299
57
|
|
|
300
|
-
|
|
58
|
+
Integers (`i`), floats (`f`), and custom colour formats round out the set.
|
|
301
59
|
|
|
302
|
-
|
|
60
|
+
## Why csstype does not cover this
|
|
303
61
|
|
|
304
|
-
|
|
62
|
+
`csstype` is excellent and ships here as a dependency. It types CSS property names and their keywords well. What it leaves loose is open INPUT values: where a property accepts an open number or string, csstype falls back to `(number & {})`, so a bare `1.5`, a `px` added to an `em`, or a float `z-index` all pass. You cannot construct a validated value from that. calipers fills exactly that gap, and `.css()` renders to a string that still satisfies csstype on output. It complements csstype; it never replaces it. csstype types the property and keyword side, calipers types the value side, and the aim is a complete typed surface for CSS input, built from both.
|
|
305
63
|
|
|
306
|
-
-
|
|
64
|
+
calipers is standalone and complete on its own. It is also Layer 1 of the larger CSS-Bookends project (helpers, then an opinionated framework, built on these primitives), and design-token (DTCG) documents convert to these primitives via the Bookends typesetter. Both are there if you want them, never required.
|
|
307
65
|
|
|
308
|
-
|
|
66
|
+
A note on the names. The book metaphor (calipers, bookends, gilding, compendium) is deliberate, not just whimsy. Each name marks a ROLE, and intentionally hides the library currently filling it, because those internals are meant to be swappable. You import `color()`, not `culori`; the project's browser-compat finisher is `gilding`, not Lightning CSS. Swap the engine underneath and your call sites do not move.
|
|
309
67
|
|
|
310
|
-
|
|
68
|
+
## Install
|
|
311
69
|
|
|
312
|
-
```
|
|
313
|
-
css-calipers
|
|
70
|
+
```sh
|
|
71
|
+
npm install css-calipers
|
|
314
72
|
```
|
|
315
73
|
|
|
316
|
-
|
|
74
|
+
Dependencies: `csstype` and `culori` (the colour engine). ESM and CommonJS are both published; the colour surface is reachable from the root import.
|
|
317
75
|
|
|
318
|
-
|
|
76
|
+
## Colour
|
|
319
77
|
|
|
320
|
-
|
|
78
|
+
`color(input, config?)` parses a CSS string, a structured `ColorObject` (one shape per colour space: `{ space: 'rgb', r, g, b, alpha? }`, hsl, hwb, lab, lch, oklab, oklch), a symbolic keyword (`currentColor`, a system colour, a cascade keyword), or an existing resolved colour. It normalizes to OKLCH internally and returns an immutable result.
|
|
321
79
|
|
|
322
|
-
|
|
80
|
+
Modifications return a new colour (the original is untouched) and thread the configured output format through: `darken`, `lighten`, `brighten`, `saturate`, `desaturate`, `setLightness`, `setChroma`, `setHue`, `hueShift`, `complement`, `mix`, `mixSolid`, `mixWithAlpha`, `alpha`, `solid`, `clone`, `contrast`. See `examples/color-modify.example.ts`.
|
|
323
81
|
|
|
324
|
-
|
|
82
|
+
**Output.** `.css()` takes no argument and walks the default ladder `[hex, rgba, oklch]` (the order is backed by usage data, see `docs/color-format-popularity.md`). Force a format with a named selector (`.hex()`, `.rgb()`, `.rgba()`, `.hexAlpha()`, `.hsl()`, `.hwb()`, `.lab()`, `.lch()`, `.oklab()`, `.oklch()`, `.displayP3()`) or `.formatAs(...)`. `omitOpaqueAlpha` drops the alpha slot for an opaque colour where it is optional (lossless, off by default).
|
|
325
83
|
|
|
326
|
-
|
|
327
|
-
You can disable or force stack hints globally:
|
|
84
|
+
**Strictness.** When a render cannot faithfully hold the colour (dropping a real alpha, out of sRGB gamut, modifying a symbolic colour), `strictness` decides: `auto` (default, throw in dev, warn in prod), `throw`, `warn`, or `silent`.
|
|
328
85
|
|
|
86
|
+
```ts
|
|
87
|
+
color('#3366cc80').rgb().css(); // throws in dev (rgb carries no alpha)
|
|
88
|
+
color('#3366cc80', { strictness: 'silent' }).rgb().css(); // 'rgb(51, 102, 204)' (alpha dropped)
|
|
329
89
|
```
|
|
330
|
-
import { setErrorConfig } from "css-calipers";
|
|
331
90
|
|
|
332
|
-
|
|
333
|
-
setErrorConfig({ stackHints: "off" });
|
|
91
|
+
**Transparency.** A fully transparent colour renders as the `transparent` keyword by default; configurable with `{ transparent: 'keyword' | 'white' | 'black' | 'preserve' }` or per render with `.transparentAs(mode)`. See `examples/transparency.example.ts`.
|
|
334
92
|
|
|
335
|
-
|
|
336
|
-
setErrorConfig({ stackHints: "on" });
|
|
337
|
-
```
|
|
93
|
+
**Custom formats.** `createColor({ formats })` binds custom format plugins. A plugin bridges the input and output edges (storage stays canonical OKLCH) and gets a typed named selector; author one with `defineColorSpace`, and an optional `fallback` hook rewrites its output into browser-safe CSS. See `examples/custom-format.example.ts` (its "zoo" format is a deliberately silly extensibility demo, not a real format), `examples/plugin-fallback.example.ts`, and `docs/adding-a-color-format.md`.
|
|
338
94
|
|
|
339
|
-
|
|
95
|
+
## Measurements
|
|
340
96
|
|
|
341
|
-
|
|
97
|
+
`m(value, unit?)` builds a measurement (unit defaults to `px`, lower-cased). Arithmetic, `round`, and `clamp` return new measurements in the same unit. Per-unit helpers bind the unit (`mPx`, `mEm`, `mRem`, `mPercent`, `mCqw`, ...) across every unit family: absolute, font-relative, viewport, container, angle, time, frequency, resolution, and grid.
|
|
342
98
|
|
|
343
|
-
|
|
344
|
-
the factory entrypoint. The instance includes core helpers and unit helpers.
|
|
99
|
+
## Integers and floats
|
|
345
100
|
|
|
346
|
-
|
|
347
|
-
import { createCalipers } from "css-calipers/factory";
|
|
101
|
+
`i()` (a whole number) and `f()` (a real number) are constrained scalars for the unitless number space CSS leaves untyped. Both validate at construction and re-validate on every operation, so a constrained value stays valid (or throws) through arithmetic. `clamp(min, max)` snaps into range instead of throwing; `hardenInteger({ min, max })` / `hardenFloat({ min, max })` bind a constraint once into a reusable factory. See `examples/integers-floats.example.ts`.
|
|
348
102
|
|
|
349
|
-
|
|
350
|
-
errorConfig: { stackHints: "on" },
|
|
351
|
-
});
|
|
103
|
+
## Ratios
|
|
352
104
|
|
|
353
|
-
|
|
354
|
-
```
|
|
105
|
+
`r(numerator, denominator?)` (denominator `1` when omitted) accepts typed integers and floats on either side, with helpers `simplifyRatio`, `reduceRatio`, `normalizeRatio`, and `parseRatio`. See `examples/ratio.example.ts`.
|
|
355
106
|
|
|
356
|
-
|
|
357
|
-
for a wrapper module you can re-export across your project.
|
|
107
|
+
## Hardening and constraints
|
|
358
108
|
|
|
359
|
-
|
|
109
|
+
Refinements run a runtime check and return the same value branded with the constraint, so a function can demand "a non-negative measurement" and the compiler rejects anything unchecked (brands are keyed by a private symbol). The built-ins are `nonNegative`, `nonPositive`, and `inRange(min, max)`, each exposing `.is` / `.ensure` / `.check` / `.hardenWith`. `inRange` carries its literal bounds in the type. Build your own with `makeMeasurementRefinement`. The full model is in `docs/hardening.md`, with runnable examples in `examples/refinements.example.ts`.
|
|
360
110
|
|
|
361
|
-
##
|
|
111
|
+
## Per-property value helpers live in the books layer
|
|
362
112
|
|
|
363
|
-
|
|
364
|
-
you can write small adapters that accept existing CSS strings, CSS-Calipers
|
|
365
|
-
measurements, or plain numbers and turn them into CSS values. CSS-Calipers can
|
|
366
|
-
be dropped into an existing styling system or used from the ground up; it
|
|
367
|
-
focuses narrowly on numeric, unit-bearing values and leaves the rest of your
|
|
368
|
-
styling approach up to you. For a more realistic adapter pattern that
|
|
369
|
-
normalizes mixed inputs (including CSS variables) into a single css-like value,
|
|
370
|
-
see the line-height normalizer example referenced below.
|
|
113
|
+
calipers is the value-type primitives only (colour, measurements, integers, floats, ratios). The per-property value helpers (`opacity`, `zIndex`, `fontWeight`, ...) are NOT a calipers feature: they live one layer up, in the books layer. Each is a book that binds a calipers primitive to one CSS property, applies that property's bound and keyword companions, and types its `.css()` output against the matching csstype `Property.X`. The shared engine behind them is `@css-bookends/css-value-core`. For the full picture, the value-type side is mapped in `lexicons/calipers/surface.md` and the per-property side in `packages/css-value-core/surface.md`.
|
|
371
114
|
|
|
372
|
-
##
|
|
115
|
+
## Factories
|
|
373
116
|
|
|
374
|
-
|
|
117
|
+
The bare exports (`m`, `color`, the refinements, ...) are each a factory already called at its defaults, so the default instance and a custom instance share one construction path. The factory itself is the real configurable path and the override seam: `createCalipers({ errorConfig? })` returns a measurement instance and `createColor({ formats })` a colour instance with custom format plugins registered, and routing every consumer through a factory means you can rewrite or wrap any step (input, storage, output) with zero call-site changes. `corpus` is the lazy-defaults convenience entry: it default-exports a master factory, `createCalipersBundle({ measurements?, color? })`, that combines `createCalipers` + `createColor` under one keyed config and binds the whole calipers surface in one object (mirroring the compendium's `publishCompendium`), and it named-exports that same primitive set already bound at defaults, so you get everything wired without touching a factory yourself. (`corpus` and the bookends `@css-bookends/compendium/defaults` subpath are the only two lazy-defaults entries in the project.) Binding a factory once in one of your own modules and re-exporting the bound helper from there also keeps the blast radius of a library change small: your code imports that helper from a single seam, so a major restructuring of the library's internal paths lands in that one file, not across the hundreds or thousands of call sites that use it. And because each factory call returns its own independent instance, several configurations coexist side by side with no shared global state to collide: a `createColor` that emits hex and another that emits oklch, or a strict instance next to a clamping one, all live at once, with no cascade or global state to fight (each instance is just a value in scope, not a stylesheet competing in the cascade). See `examples/factory-wrapper.example.ts`.
|
|
375
118
|
|
|
376
|
-
|
|
377
|
-
use the exported `MeasurementString` type together with your existing CSS
|
|
378
|
-
property typings (for example, the `Property` types from the `csstype`
|
|
379
|
-
package):
|
|
119
|
+
All of that power is opt-in. If you just want sensible defaults and none of the above, you never touch a factory: the bare `m` / `color` / refinement exports are ready as-is, and `corpus` hands you the whole primitive set bound at defaults in one import.
|
|
380
120
|
|
|
381
|
-
|
|
382
|
-
import type { MeasurementString } from "css-calipers";
|
|
383
|
-
import type { Property } from "csstype";
|
|
121
|
+
## Documentation
|
|
384
122
|
|
|
385
|
-
|
|
386
|
-
Extract<Property.Margin, string>,
|
|
387
|
-
MeasurementString
|
|
388
|
-
>;
|
|
389
|
-
```
|
|
123
|
+
Deeper dives live in `docs/`:
|
|
390
124
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
`
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
- **Typed helpers:** Accept either `IMeasurement` or a constrained keyword type,
|
|
398
|
-
never a generic string.
|
|
399
|
-
- **Pre-emission transforms:** Compose all math in CSS-Calipers, emit once at
|
|
400
|
-
the style boundary.
|
|
401
|
-
- **Build-time pipelines:** Run measurement math in Node or a build step
|
|
402
|
-
(scripts, codegen, or bundler plugins) and emit plain CSS or tokens for your
|
|
403
|
-
existing styling system so runtime only sees static values.
|
|
404
|
-
- **Unit guards in debug:** Use `assertUnit()` in dev-only blocks to confirm
|
|
405
|
-
consistency between related measurements.
|
|
406
|
-
- **CSS variables:** Pass CSS-Calipers `.css()` output into style layers that
|
|
407
|
-
interpolate them, but don’t try to store `var(--token)` inside the library.
|
|
408
|
-
|
|
409
|
-
---
|
|
410
|
-
|
|
411
|
-
## Philosophy & Boundaries
|
|
412
|
-
|
|
413
|
-
- **Measurement math lives here; string composition lives elsewhere.**
|
|
414
|
-
Use CSS-Calipers for unit-aware calculations, then hand results to
|
|
415
|
-
helpers/adapters that emit CSS literals. Keep `calc()`/`linear-gradient()` logic outside
|
|
416
|
-
the library so measurement objects remain pure.
|
|
417
|
-
|
|
418
|
-
- **`.css()` at runtime is an edge, not a habit.**
|
|
419
|
-
You can call `.css()` at runtime, but prefer emitting once to avoid hot-path
|
|
420
|
-
string churn.
|
|
421
|
-
|
|
422
|
-
- **Numbers are operands, not CSS-Calipers values.**
|
|
423
|
-
You cannot create a CSS-Calipers value without a unit. Pass plain numbers as
|
|
424
|
-
operands (`add`, `subtract`, `multiply`, `divide`) or combine with another
|
|
425
|
-
`IMeasurement`, but never store bare numbers inside library state. If a value
|
|
426
|
-
lacks both number and unit, CSS-Calipers won’t track it; you own whatever
|
|
427
|
-
logic wraps it.
|
|
428
|
-
|
|
429
|
-
- **Model keywords explicitly (not “escape hatches”).**
|
|
430
|
-
If a helper needs symbolic CSS (e.g., `'auto'`, `'fit-content'`), define a
|
|
431
|
-
precise keyword type and purposely exclude the emitted string type from
|
|
432
|
-
CSS-Calipers so numeric, unit-bearing CSS values remain the default path.
|
|
433
|
-
|
|
434
|
-
- **CSS custom properties coexist; they don’t mix.**
|
|
435
|
-
Third-party primitives exposing `var(--token)` should keep those values as raw
|
|
436
|
-
CSS strings. CSS-Calipers is intentionally narrow: it works with numeric
|
|
437
|
-
measurements and unit-safe conversions, not tokens or CSS variables. You can
|
|
438
|
-
still use `var(...)` and token strings anywhere in your styling system; they
|
|
439
|
-
just sit outside the library. If you want those values to flow through
|
|
440
|
-
CSS-Calipers, first extract the numeric value and unit in your own code and
|
|
441
|
-
then pass that measurement into the library.
|
|
442
|
-
|
|
443
|
-
## Using CSS-Calipers in a larger styling system
|
|
444
|
-
|
|
445
|
-
CSS is inherently flexible: the same property can accept numbers, unit-bearing
|
|
446
|
-
strings, keywords, and CSS variables. CSS-Calipers is one focused piece of that
|
|
447
|
-
ecosystem. It keeps the numeric, unit-bearing parts typed and predictable, and
|
|
448
|
-
lets the rest of your styling system own tokens, variables, and higher-level
|
|
449
|
-
APIs.
|
|
450
|
-
|
|
451
|
-
For a worked example of this pattern, see
|
|
452
|
-
[examples/lineHeight-normalizer.example.ts](examples/lineHeight-normalizer.example.ts). It shows a helper that accepts a
|
|
453
|
-
`lineHeight` value from a CMS or configuration (numbers, numeric strings with
|
|
454
|
-
units, keywords like `"normal"`, or CSS variables such as
|
|
455
|
-
`"var(--body-line-height)"`) and normalizes them into a value with a `.css()`
|
|
456
|
-
method. CSS-Calipers only participates when there is a concrete measurement
|
|
457
|
-
(numbers and units); keywords and CSS variables remain plain CSS strings owned
|
|
458
|
-
by your styling layer. That’s the intended scope: CSS will always be a mix of
|
|
459
|
-
values, but the library gives you a tight, unit-safe boundary for the numeric
|
|
460
|
-
parts inside a broader styling solution.
|
|
461
|
-
|
|
462
|
-
### Module guides
|
|
463
|
-
|
|
464
|
-
Deeper guides live alongside this README:
|
|
465
|
-
|
|
466
|
-
- Measurements core: [README_MEASUREMENT.md](README_MEASUREMENT.md)
|
|
467
|
-
- Media queries: now in [`@css-bookends/media-queries`](https://github.com/slafleche/css-bookends)
|
|
468
|
-
|
|
469
|
-
### Further examples in this repo
|
|
470
|
-
|
|
471
|
-
The `examples/` folder contains a few non-published usage sketches:
|
|
472
|
-
|
|
473
|
-
- [examples/lineHeight-normalizer.example.ts](examples/lineHeight-normalizer.example.ts) —
|
|
474
|
-
mixed input normalization for `lineHeight` (numbers, strings, CSS variables)
|
|
475
|
-
into a single value with a `.css()` method.
|
|
476
|
-
- [examples/validation-unit-tests.example.ts](examples/validation-unit-tests.example.ts) —
|
|
477
|
-
simple unit tests that enforce spacing token invariants (shared units and
|
|
478
|
-
small ≤ large).
|
|
479
|
-
- [examples/validation-and-runtime-checks.example.ts](examples/validation-and-runtime-checks.example.ts) —
|
|
480
|
-
dev-only validation around shared tokens in two different consumers (HTML
|
|
481
|
-
string and style object) using the same line-height measurement.
|
|
482
|
-
- [examples/factory-wrapper.example.ts](examples/factory-wrapper.example.ts) —
|
|
483
|
-
instance-scoped factory wrapper that re-exports the helpers.
|
|
125
|
+
- `docs/number-space.md` - which scalar CSS values are worth typing, and why.
|
|
126
|
+
- `docs/hardening.md` - the refinement model, branded types, and custom refinements.
|
|
127
|
+
- `docs/custom-format-registration.md` and `docs/adding-a-color-format.md` - registering custom colour formats end to end.
|
|
128
|
+
- `docs/color-format-popularity.md` - the usage data behind the default output ladder.
|
|
129
|
+
- `docs/integration.md` - using calipers with a styling pipeline.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.displayP3 = void 0;
|
|
4
|
+
const internals_1 = require("../internals");
|
|
5
|
+
const types_1 = require("../types");
|
|
6
|
+
/**
|
|
7
|
+
* display-p3 — wider-than-sRGB device RGB (the P3 gamut), with alpha. This is the
|
|
8
|
+
* one gamut-DEPENDENT format: a fallback should also gate on
|
|
9
|
+
* `@media (color-gamut: p3)`, since the wider color only shows on a P3 display. Out
|
|
10
|
+
* of P3 is clamped (strictness-governed). Not a floor.
|
|
11
|
+
*
|
|
12
|
+
* See `display-p3.md`.
|
|
13
|
+
*/
|
|
14
|
+
exports.displayP3 = (0, types_1.defineColorSpace)({
|
|
15
|
+
format: 'displayP3',
|
|
16
|
+
hasAlpha: true,
|
|
17
|
+
gamut: 'p3',
|
|
18
|
+
supportsProbe: '(color: color(display-p3 0 0 0))',
|
|
19
|
+
gamutDependent: true,
|
|
20
|
+
srgbFloor: false,
|
|
21
|
+
render: (color, cfg) => {
|
|
22
|
+
const c = (0, internals_1.toP3)((0, internals_1.fitGamut)(color, internals_1.inP3, 'p3', cfg.strictness));
|
|
23
|
+
return `color(display-p3 ${(0, internals_1.round)(c.r, 5)} ${(0, internals_1.round)(c.g, 5)} ${(0, internals_1.round)(c.b, 5)}${(0, internals_1.alphaSlot)(color, cfg)})`;
|
|
24
|
+
},
|
|
25
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.chooseFormat = void 0;
|
|
4
|
+
const index_1 = require("./index");
|
|
5
|
+
const internals_1 = require("./internals");
|
|
6
|
+
const resolve_1 = require("./resolve");
|
|
7
|
+
const descriptorFor = (format) => (0, resolve_1.asDescriptor)(format, index_1.colorFormats);
|
|
8
|
+
/**
|
|
9
|
+
* Whether a format can FAITHFULLY hold the color: its gamut contains the color, and
|
|
10
|
+
* it carries alpha if the color is non-opaque. 8-bit sRGB precision is accepted as
|
|
11
|
+
* faithful, so gamut + alpha are the only fit criteria.
|
|
12
|
+
*/
|
|
13
|
+
const fits = (color, format) => {
|
|
14
|
+
const d = descriptorFor(format);
|
|
15
|
+
const gamutOk = d.gamut === 'unbounded'
|
|
16
|
+
? true
|
|
17
|
+
: d.gamut === 'p3'
|
|
18
|
+
? (0, internals_1.inP3)(color)
|
|
19
|
+
: (0, internals_1.inSrgb)(color);
|
|
20
|
+
const alphaOk = d.hasAlpha || !(0, internals_1.hasRealAlpha)(color);
|
|
21
|
+
return gamutOk && alphaOk;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Resolve an output config to a single format. A single format passes through
|
|
25
|
+
* unchanged (an explicit choice, no escalation). A priority list escalates to the
|
|
26
|
+
* first format that faithfully holds the color, or the last entry if none do (which
|
|
27
|
+
* then clamps via the render step's strictness).
|
|
28
|
+
*/
|
|
29
|
+
const chooseFormat = (color, output) => {
|
|
30
|
+
if (!Array.isArray(output)) {
|
|
31
|
+
return output;
|
|
32
|
+
}
|
|
33
|
+
const list = output;
|
|
34
|
+
for (const format of list) {
|
|
35
|
+
if (fits(color, format)) {
|
|
36
|
+
return format;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return list[list.length - 1];
|
|
40
|
+
};
|
|
41
|
+
exports.chooseFormat = chooseFormat;
|