css-calipers 0.3.0 → 0.5.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/README.md +76 -38
- package/RELEASING.md +1 -1
- package/dist/cjs/units/index.js +29 -0
- package/dist/esm/units/index.d.ts +14 -0
- package/dist/esm/units/index.d.ts.map +1 -0
- package/dist/esm/units/index.js +13 -0
- package/package.json +18 -6
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ import { m } from "css-calipers";
|
|
|
37
37
|
|
|
38
38
|
// Declare vars
|
|
39
39
|
const paddingBase = m(4); // defaults to "px" with no unit specified
|
|
40
|
-
const rotation = m(45, "deg"); // equivalent to a dedicated helper
|
|
40
|
+
const rotation = m(45, "deg"); // equivalent to a dedicated helper: mDeg(45)
|
|
41
41
|
|
|
42
42
|
// Do safe arithmetic
|
|
43
43
|
const margins = paddingBase.add(4);
|
|
@@ -50,6 +50,8 @@ const style = {
|
|
|
50
50
|
};
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
+
If you prefer, you can also import unit helpers from dedicated subpaths. For example, `mPercent` is available from the root entrypoint and from `css-calipers/units/percent`, and all unit helpers are aggregated under `css-calipers/units`.
|
|
54
|
+
|
|
53
55
|
---
|
|
54
56
|
|
|
55
57
|
## Features
|
|
@@ -73,7 +75,11 @@ CSS-Calipers focuses exclusively on numeric, unit-bearing CSS values. Keywords
|
|
|
73
75
|
like `auto`, `fit-content`, or `max-content`, full shorthand strings,
|
|
74
76
|
`var(--token)`, or `calc(...)` expressions should remain explicit strings or
|
|
75
77
|
dedicated keyword types in your app or styling layer. Everything else stays as
|
|
76
|
-
plain CSS (see "Philosophy & Boundaries" below for more detail).
|
|
78
|
+
plain CSS (see "Philosophy & Boundaries" below for more detail). For a concrete
|
|
79
|
+
example of this separation in a mixed-input helper, see
|
|
80
|
+
[examples/lineHeight-normalizer.example.ts](examples/lineHeight-normalizer.example.ts),
|
|
81
|
+
which keeps keywords and CSS variables as plain strings while using measurements
|
|
82
|
+
for numeric values.
|
|
77
83
|
|
|
78
84
|
---
|
|
79
85
|
|
|
@@ -96,7 +102,7 @@ It’s probably overkill if:
|
|
|
96
102
|
### Layout tokens example
|
|
97
103
|
|
|
98
104
|
```ts
|
|
99
|
-
import { m, mPercent, mVw, mVh,
|
|
105
|
+
import { m, mPercent, mVw, mVh, assertCondition } from "css-calipers";
|
|
100
106
|
|
|
101
107
|
// Token-style measurements (px by default)
|
|
102
108
|
const spacing = m(8); // Defaults to px; equivalent to mPx(8)
|
|
@@ -142,7 +148,8 @@ if (process.env.NODE_ENV !== "production") {
|
|
|
142
148
|
const cardGridStyles = {
|
|
143
149
|
display: "grid",
|
|
144
150
|
gap: gutter.css(),
|
|
145
|
-
|
|
151
|
+
// Keep fraction units as plain CSS alongside measurement-derived values
|
|
152
|
+
gridTemplateColumns: `repeat(${columns}, 1fr)`,
|
|
146
153
|
// width driven by card width + gutters
|
|
147
154
|
width: cardWidth
|
|
148
155
|
.multiply(columns)
|
|
@@ -152,7 +159,7 @@ const cardGridStyles = {
|
|
|
152
159
|
minWidth: minWidthPercent.css(),
|
|
153
160
|
maxWidth: maxWidthViewport.css(),
|
|
154
161
|
// derived hero height based on px ratio, expressed in vh and used inside a calc() string
|
|
155
|
-
// calc() stays plain CSS;
|
|
162
|
+
// calc() stays plain CSS; CSS-Calipers only provides the numeric pieces
|
|
156
163
|
minHeight: `calc(${heroHeight.css()} + 10vh)`,
|
|
157
164
|
};
|
|
158
165
|
```
|
|
@@ -163,7 +170,7 @@ const cardGridStyles = {
|
|
|
163
170
|
|
|
164
171
|
CSS-Calipers will happily enforce units anywhere you choose, but it stays
|
|
165
172
|
unopinionated about where those guards live. Drop assertions in a component, in
|
|
166
|
-
a theme
|
|
173
|
+
a theme override, hardcode a debug routine, or wire a global invariant; the
|
|
167
174
|
structure is up to you:
|
|
168
175
|
|
|
169
176
|
```ts
|
|
@@ -179,9 +186,15 @@ if (process.env.NODE_ENV !== "production") {
|
|
|
179
186
|
}
|
|
180
187
|
```
|
|
181
188
|
|
|
182
|
-
You can apply the same checks globally (e.g., during app bootstrap)
|
|
183
|
-
inside the components that need them
|
|
184
|
-
|
|
189
|
+
You can apply the same checks globally (e.g., during app bootstrap), only
|
|
190
|
+
inside the components that need them, or in dedicated test helpers. For more
|
|
191
|
+
complete patterns, see the examples folder: the validation unit-tests example
|
|
192
|
+
([examples/validation-unit-tests.example.ts](examples/validation-unit-tests.example.ts)) shows how to
|
|
193
|
+
enforce spacing token invariants in a test suite, and the validation and runtime
|
|
194
|
+
checks example ([examples/validation-and-runtime-checks.example.ts](examples/validation-and-runtime-checks.example.ts))
|
|
195
|
+
shows how to apply dev-only guards around shared tokens in two different
|
|
196
|
+
consumers (an HTML snippet and a style object) using the same line-height
|
|
197
|
+
measurement.
|
|
185
198
|
|
|
186
199
|
---
|
|
187
200
|
|
|
@@ -189,7 +202,7 @@ placement is a design decision.
|
|
|
189
202
|
|
|
190
203
|
- Operations are fail-fast: when you call helpers like `add`, `divide`, `clamp`,
|
|
191
204
|
`measurementMin` / `measurementMax`, or the assertion helpers with invalid
|
|
192
|
-
input (for example, mismatched units or non-finite values),
|
|
205
|
+
input (for example, mismatched units or non-finite values), CSS-Calipers
|
|
193
206
|
throws a normal `Error`.
|
|
194
207
|
- Error messages include the operation name (for example,
|
|
195
208
|
`css-calipers.Measurement.divide` or `css-calipers.assertMatchingUnits`), the
|
|
@@ -200,34 +213,23 @@ placement is a design decision.
|
|
|
200
213
|
(such as `if (process.env.NODE_ENV !== 'production')`) or to enforce
|
|
201
214
|
invariants in tests, so checks stay cheap and predictable at runtime.
|
|
202
215
|
|
|
216
|
+
For concrete uses of these errors in tests and dev-only guards, see
|
|
217
|
+
`TESTING.md` and the validation examples in
|
|
218
|
+
[examples/validation-unit-tests.example.ts](examples/validation-unit-tests.example.ts) and
|
|
219
|
+
[examples/validation-and-runtime-checks.example.ts](examples/validation-and-runtime-checks.example.ts).
|
|
220
|
+
|
|
203
221
|
---
|
|
204
222
|
|
|
205
223
|
## Co-existing with other systems
|
|
206
224
|
|
|
207
|
-
You don’t have to convert everything at once, or at all. If it fits your setup,
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if (typeof value === "string") {
|
|
216
|
-
// Already a CSS value (for example, "auto" or "var(--gap)")
|
|
217
|
-
return value;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const measurement = isMeasurement(value) ? value : m(value);
|
|
221
|
-
return measurement.css();
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
// Later, callers can pass tokens, raw CSS strings, or measurements:
|
|
225
|
-
toSpacingCss("var(--card-gap)");
|
|
226
|
-
toSpacingCss(8); // becomes "8px"
|
|
227
|
-
toSpacingCss(m(12, "px"));
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
---
|
|
225
|
+
You don’t have to convert everything at once, or at all. If it fits your setup,
|
|
226
|
+
you can write small adapters that accept existing CSS strings, CSS-Calipers
|
|
227
|
+
measurements, or plain numbers and turn them into CSS values. CSS-Calipers can
|
|
228
|
+
be dropped into an existing styling system or used from the ground up; it
|
|
229
|
+
focuses narrowly on numeric, unit-bearing values and leaves the rest of your
|
|
230
|
+
styling approach up to you. For a more realistic adapter pattern that
|
|
231
|
+
normalizes mixed inputs (including CSS variables) into a single css-like value,
|
|
232
|
+
see the line-height normalizer example referenced below.
|
|
231
233
|
|
|
232
234
|
## Advanced
|
|
233
235
|
|
|
@@ -272,7 +274,7 @@ keywords for symbolic CSS values, without reintroducing vague unions like
|
|
|
272
274
|
|
|
273
275
|
- **Measurement math lives here; string composition lives elsewhere.**
|
|
274
276
|
Use CSS-Calipers for unit-aware calculations, then hand results to
|
|
275
|
-
helpers/adapters that emit CSS literals. Keep `calc()`/`
|
|
277
|
+
helpers/adapters that emit CSS literals. Keep `calc()`/`linear-gradient()` logic outside
|
|
276
278
|
the library so measurement objects remain pure.
|
|
277
279
|
|
|
278
280
|
- **`.css()` at runtime is an edge, not a habit.**
|
|
@@ -293,6 +295,42 @@ keywords for symbolic CSS values, without reintroducing vague unions like
|
|
|
293
295
|
|
|
294
296
|
- **CSS custom properties coexist; they don’t mix.**
|
|
295
297
|
Third-party primitives exposing `var(--token)` should keep those values as raw
|
|
296
|
-
CSS strings.
|
|
297
|
-
|
|
298
|
-
|
|
298
|
+
CSS strings. CSS-Calipers is intentionally narrow: it works with numeric
|
|
299
|
+
measurements and unit-safe conversions, not tokens or CSS variables. You can
|
|
300
|
+
still use `var(...)` and token strings anywhere in your styling system; they
|
|
301
|
+
just sit outside the library. If you want those values to flow through
|
|
302
|
+
CSS-Calipers, first extract the numeric value and unit in your own code and
|
|
303
|
+
then pass that measurement into the library.
|
|
304
|
+
|
|
305
|
+
## Using CSS-Calipers in a larger styling system
|
|
306
|
+
|
|
307
|
+
CSS is inherently flexible: the same property can accept numbers, unit-bearing
|
|
308
|
+
strings, keywords, and CSS variables. CSS-Calipers is one focused piece of that
|
|
309
|
+
ecosystem. It keeps the numeric, unit-bearing parts typed and predictable, and
|
|
310
|
+
lets the rest of your styling system own tokens, variables, and higher-level
|
|
311
|
+
APIs.
|
|
312
|
+
|
|
313
|
+
For a worked example of this pattern, see
|
|
314
|
+
[examples/lineHeight-normalizer.example.ts](examples/lineHeight-normalizer.example.ts). It shows a helper that accepts a
|
|
315
|
+
`lineHeight` value from a CMS or configuration (numbers, numeric strings with
|
|
316
|
+
units, keywords like `"normal"`, or CSS variables such as
|
|
317
|
+
`"var(--body-line-height)"`) and normalizes them into a value with a `.css()`
|
|
318
|
+
method. CSS-Calipers only participates when there is a concrete measurement
|
|
319
|
+
(numbers and units); keywords and CSS variables remain plain CSS strings owned
|
|
320
|
+
by your styling layer. That’s the intended scope: CSS will always be a mix of
|
|
321
|
+
values, but the library gives you a tight, unit-safe boundary for the numeric
|
|
322
|
+
parts inside a broader styling solution.
|
|
323
|
+
|
|
324
|
+
### Further examples in this repo
|
|
325
|
+
|
|
326
|
+
The `examples/` folder contains a few non-published usage sketches:
|
|
327
|
+
|
|
328
|
+
- [examples/lineHeight-normalizer.example.ts](examples/lineHeight-normalizer.example.ts) —
|
|
329
|
+
mixed input normalization for `lineHeight` (numbers, strings, CSS variables)
|
|
330
|
+
into a single value with a `.css()` method.
|
|
331
|
+
- [examples/validation-unit-tests.example.ts](examples/validation-unit-tests.example.ts) —
|
|
332
|
+
simple unit tests that enforce spacing token invariants (shared units and
|
|
333
|
+
small ≤ large).
|
|
334
|
+
- [examples/validation-and-runtime-checks.example.ts](examples/validation-and-runtime-checks.example.ts) —
|
|
335
|
+
dev-only validation around shared tokens in two different consumers (HTML
|
|
336
|
+
string and style object) using the same line-height measurement.
|
package/RELEASING.md
CHANGED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./percent"), exports);
|
|
18
|
+
__exportStar(require("./absolute"), exports);
|
|
19
|
+
__exportStar(require("./font-relative"), exports);
|
|
20
|
+
__exportStar(require("./viewport"), exports);
|
|
21
|
+
__exportStar(require("./viewport-small"), exports);
|
|
22
|
+
__exportStar(require("./viewport-large"), exports);
|
|
23
|
+
__exportStar(require("./viewport-dynamic"), exports);
|
|
24
|
+
__exportStar(require("./container"), exports);
|
|
25
|
+
__exportStar(require("./angle"), exports);
|
|
26
|
+
__exportStar(require("./time"), exports);
|
|
27
|
+
__exportStar(require("./frequency"), exports);
|
|
28
|
+
__exportStar(require("./resolution"), exports);
|
|
29
|
+
__exportStar(require("./grid"), exports);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export * from './percent';
|
|
2
|
+
export * from './absolute';
|
|
3
|
+
export * from './font-relative';
|
|
4
|
+
export * from './viewport';
|
|
5
|
+
export * from './viewport-small';
|
|
6
|
+
export * from './viewport-large';
|
|
7
|
+
export * from './viewport-dynamic';
|
|
8
|
+
export * from './container';
|
|
9
|
+
export * from './angle';
|
|
10
|
+
export * from './time';
|
|
11
|
+
export * from './frequency';
|
|
12
|
+
export * from './resolution';
|
|
13
|
+
export * from './grid';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/units/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,QAAQ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from './percent';
|
|
2
|
+
export * from './absolute';
|
|
3
|
+
export * from './font-relative';
|
|
4
|
+
export * from './viewport';
|
|
5
|
+
export * from './viewport-small';
|
|
6
|
+
export * from './viewport-large';
|
|
7
|
+
export * from './viewport-dynamic';
|
|
8
|
+
export * from './container';
|
|
9
|
+
export * from './angle';
|
|
10
|
+
export * from './time';
|
|
11
|
+
export * from './frequency';
|
|
12
|
+
export * from './resolution';
|
|
13
|
+
export * from './grid';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "css-calipers",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Compile-time unit safety for numeric, unit-bearing CSS values via typed measurements.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -42,10 +42,22 @@
|
|
|
42
42
|
"import": "./dist/esm/index.js",
|
|
43
43
|
"require": "./dist/cjs/index.js"
|
|
44
44
|
},
|
|
45
|
+
"./units": {
|
|
46
|
+
"types": "./dist/esm/units/index.d.ts",
|
|
47
|
+
"import": "./dist/esm/units/index.js",
|
|
48
|
+
"require": "./dist/cjs/units/index.js"
|
|
49
|
+
},
|
|
50
|
+
"./units/*": {
|
|
51
|
+
"types": "./dist/esm/units/*.d.ts",
|
|
52
|
+
"import": "./dist/esm/units/*.js",
|
|
53
|
+
"require": "./dist/cjs/units/*.js"
|
|
54
|
+
},
|
|
45
55
|
"./package.json": "./package.json"
|
|
46
56
|
},
|
|
47
57
|
"devDependencies": {
|
|
58
|
+
"@types/node": "^24.10.1",
|
|
48
59
|
"@vitest/coverage-v8": "^4.0.14",
|
|
60
|
+
"csstype": "^3.1.3",
|
|
49
61
|
"tsd": "^0.31.0",
|
|
50
62
|
"typescript": "^5.6.3",
|
|
51
63
|
"vitest": "^4.0.14"
|
|
@@ -59,12 +71,12 @@
|
|
|
59
71
|
"prepublishOnly": "node -e \"if (!process.env.CSS_CALIPERS_RELEASE) { console.error('Direct npm publish is disabled; use \\\"npm run release\\\" instead.'); process.exit(1); }\"",
|
|
60
72
|
"release": "node scripts/release.mjs",
|
|
61
73
|
"release:dry": "node scripts/release.mjs --dry-run",
|
|
62
|
-
"test": "vitest tests/core.test.ts",
|
|
63
|
-
"test:core": "vitest run tests/core.test.ts",
|
|
64
|
-
"test:cjs": "vitest run tests/core.cjs.test.ts",
|
|
65
|
-
"test:esm": "vitest run tests/core.esm.test.ts",
|
|
74
|
+
"test": "vitest tests/runtime/core/core.src.test.ts",
|
|
75
|
+
"test:core": "vitest run tests/runtime/core/core.src.test.ts",
|
|
76
|
+
"test:cjs": "vitest run tests/runtime/core/core.cjs.test.ts tests/runtime/api-surface/api-surface.cjs.test.ts",
|
|
77
|
+
"test:esm": "vitest run tests/runtime/core/core.esm.test.ts tests/runtime/api-surface/api-surface.esm.test.ts",
|
|
66
78
|
"test:dist": "npm run test:cjs && npm run test:esm",
|
|
67
|
-
"test:all": "npm run test:core && npm run test:dist",
|
|
79
|
+
"test:all": "npm run test:core && npm run test:dist && npm run test:types",
|
|
68
80
|
"test:types": "echo \"\n 🧪 Starting tsd type checks...\n\" && tsd --files tests/types/**/*.test-d.ts && echo \"✅ tsd type checks passed!\n\""
|
|
69
81
|
}
|
|
70
82
|
}
|