cssints 0.34.0 → 0.35.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/dist/vite.mjs +14 -0
- package/docs/API.md +68 -0
- package/package.json +2 -2
- package/skills/cssints-plugin/references/table.md +1 -1
package/dist/vite.mjs
CHANGED
|
@@ -672,6 +672,7 @@ function cssints({ inner = false, exclude, devtools = false, opencode = false, e
|
|
|
672
672
|
const siteOf = /* @__PURE__ */ new Map();
|
|
673
673
|
let buildMap = false;
|
|
674
674
|
const callsOf = /* @__PURE__ */ new Map();
|
|
675
|
+
const sheetEdits = {};
|
|
675
676
|
const sheet = Effect.fnUntraced(function* (ids = [...usedBy.keys()]) {
|
|
676
677
|
return yield* renderSheet(ids.flatMap((id) => usedBy.get(id)?.split("\n") ?? []));
|
|
677
678
|
});
|
|
@@ -912,6 +913,7 @@ function cssints({ inner = false, exclude, devtools = false, opencode = false, e
|
|
|
912
913
|
yield* drop(file);
|
|
913
914
|
}
|
|
914
915
|
yield* Effect.sync(() => forgetWarnings([file]));
|
|
916
|
+
yield* Effect.sync(() => sheetEdits.last = file);
|
|
915
917
|
const graph = env.moduleGraph;
|
|
916
918
|
const affected = [.../* @__PURE__ */ new Set([...depsOf.get(file) ?? [], ...inflight])].flatMap((id) => graph.getModuleById(id) ?? []);
|
|
917
919
|
yield* Effect.sync(() => affected.forEach((m) => {
|
|
@@ -959,6 +961,9 @@ function cssints({ inner = false, exclude, devtools = false, opencode = false, e
|
|
|
959
961
|
server = s;
|
|
960
962
|
if (inner) return;
|
|
961
963
|
evaluator.server = evaluator.needed ? void 0 : s;
|
|
964
|
+
s.environments.client.hot.on("cssints:edits:get", (_, client) => {
|
|
965
|
+
if (sheetEdits.measured) client.send("cssints:edits", sheetEdits.measured);
|
|
966
|
+
});
|
|
962
967
|
s.environments.client.hot.on("cssints:sheet", (v) => {
|
|
963
968
|
if (!s.environments.client.moduleGraph.getModuleById(SHEET)?.transformResult?.code.includes(v)) Effect.runSync(reloadSheet());
|
|
964
969
|
});
|
|
@@ -983,6 +988,15 @@ function cssints({ inner = false, exclude, devtools = false, opencode = false, e
|
|
|
983
988
|
if (id === SHEET) {
|
|
984
989
|
if (build) return LAYERS;
|
|
985
990
|
const code = runSync(hook("vite.load", id, sheet()));
|
|
991
|
+
if (this.environment.name === "client" && server) {
|
|
992
|
+
const classes = new Set([...usedBy.values()].flatMap((v) => v.split("\n"))).size;
|
|
993
|
+
sheetEdits.measured = {
|
|
994
|
+
file: sheetEdits.last ? relative(root, sheetEdits.last) : "",
|
|
995
|
+
bytes: code.length,
|
|
996
|
+
classes
|
|
997
|
+
};
|
|
998
|
+
server.environments.client.hot.send("cssints:edits", sheetEdits.measured);
|
|
999
|
+
}
|
|
986
1000
|
return {
|
|
987
1001
|
code,
|
|
988
1002
|
map: sheetMap(code, siteOf)
|
package/docs/API.md
CHANGED
|
@@ -4397,6 +4397,74 @@ TOKENS
|
|
|
4397
4397
|
- **Its own styles do not touch yours.** The overlay is a custom element with a shadow root and its own cssints sheet, built with the package: the page's rules do not reach it, and its classes are not in your sheet or in the table.
|
|
4398
4398
|
- **Limits.** The proximity of `@scope` is not ranked (no two looks set one property on one element), and a logical and a physical property (`margin-inline-start`, `margin-left`) are two properties, as `cn()` treats them; an element inside another shadow root is picked as its host; a page that Vite does not serve as HTML (server rendering) gets no entry.
|
|
4399
4399
|
|
|
4400
|
+
#### Vitals in the console (`@cssints/devtools/vitals`, 2026-11-07, cssints-0p4d)
|
|
4401
|
+
|
|
4402
|
+
The panel inspects an element; this entry inspects the page, and prints to the console rather than drawing a DOM panel —
|
|
4403
|
+
the only surface a page that draws to a canvas has. No dev server, no overlay chunk and no cssints dependency of its own: a
|
|
4404
|
+
plain ES module (about 30 kB, 9.5 kB gzip) any page imports in dev.
|
|
4405
|
+
|
|
4406
|
+
```ts
|
|
4407
|
+
// the app's entry, dev only
|
|
4408
|
+
import { observe, vitals } from "@cssints/devtools/vitals";
|
|
4409
|
+
observe(); // long tasks and long animation frames are not buffered: observe before the work
|
|
4410
|
+
|
|
4411
|
+
// later, from the console:
|
|
4412
|
+
vitals(); // or window.vitals(): prints the report and returns it
|
|
4413
|
+
```
|
|
4414
|
+
|
|
4415
|
+
```text skip
|
|
4416
|
+
cssints vitals
|
|
4417
|
+
265 ms · 214 nodes · 1 sheet · 380 classes
|
|
4418
|
+
vitals FCP 412 ms · LCP 1.20 s (h1.hero) · CLS 0.002
|
|
4419
|
+
INP 96 ms (button.primary) · 8+40+48 · TTFB 38 ms
|
|
4420
|
+
frames p50 16.7 ms · p95 33.4 ms · max 51.2 ms · 3 dropped ▁▂▃▅▇▆▃▂▁▂▃▅
|
|
4421
|
+
main 7 long tasks · 412 ms · max 120 ms · style+layout 41 ms · 0 errors · heap 42.1 MB
|
|
4422
|
+
sheet 64.4 kB · +29 B on src/registry/button/button.tsx · 3 loads · 81 B a class ▁▁█
|
|
4423
|
+
load 2 sheets · 1 render-blocking · 66 ms to last · slow.css 65 ms (327 B)
|
|
4424
|
+
history 12 loads · LCP 1.20 s (−180 ms) (−312 B) ▁▂▃▅▆▇▆▅
|
|
4425
|
+
CSS 41.2 kB · 380 rules · 1 902 decls · 0 blocked · specificity 0,3,2 (._1t8gqy5:hover)
|
|
4426
|
+
layer _.k ████████████████████ 22.4 kB
|
|
4427
|
+
tokens 14 colours · 2 fonts · 1 !important · custom props 12 declared / 9 used / 3 orphaned · keyframes 2 / 1 unused
|
|
4428
|
+
coverage 41 / 380 classes unused (11%) cs-Landing-hero …
|
|
4429
|
+
```
|
|
4430
|
+
|
|
4431
|
+
- **Web vitals** come from [web-vitals](https://github.com/GoogleChrome/web-vitals) (MIT), the reference implementation:
|
|
4432
|
+
CLS is the largest session window of shifts (not their sum), INP is the ~98th percentile of interactions (not the worst),
|
|
4433
|
+
and each metric names its culprit — the LCP element and render delay, the interaction's target with input / processing /
|
|
4434
|
+
presentation, the frames it covered. `!` is needs-improvement and `!!` poor, the library's thresholds. It reads the same
|
|
4435
|
+
APIs, so CLS is Chromium only and the other four come from Chromium, Firefox and Safari. Long animation frames are ours,
|
|
4436
|
+
and `render` sums the style and layout time — the part CSS pays for.
|
|
4437
|
+
- **The sheet's numbers** come from the CSSOM, not a parser: bytes, rules, declarations, layer bytes, the worst
|
|
4438
|
+
specificity, colours, fonts, `!important`, custom properties declared / read / orphaned / undeclared, keyframes declared
|
|
4439
|
+
/ used / unused, and coverage — the classes of the sheet that no element carries now. On a cssints sheet this should be
|
|
4440
|
+
clean by construction, so a drift names what bypassed the engine; it is Project Wallace's x-ray pointed at a generated
|
|
4441
|
+
sheet.
|
|
4442
|
+
- **The sheet's growth** is the dev server's measurement, over the HMR channel (`cssints:edits`; a late subscriber asks
|
|
4443
|
+
with `cssints:edits:get`): each load reports its size, its class count and the file that changed, so an edit reads
|
|
4444
|
+
`+29 B on src/registry/button/button.tsx`, with a sparkline of the last thirty loads and the bytes a class costs. Off a
|
|
4445
|
+
dev server the row is absent. These bytes are `renderSheet`'s, not the CSSOM's, so they differ a little from the `CSS`
|
|
4446
|
+
row.
|
|
4447
|
+
- **Long animation frames** also name their scripts: the top sources by script time (a source is the last two path
|
|
4448
|
+
segments), the invokers (`timer`, `message handler`, an event listener), and the _forced_ style and layout time — the
|
|
4449
|
+
read-after-write that makes a script pay for layout.
|
|
4450
|
+
- **The history** of the last twenty loads of a path lives in `sessionStorage` (`cssints-vitals`): LCP and the sheet's
|
|
4451
|
+
bytes as deltas against the previous load of the same page; `reset()` clears it, and a store that throws (a private
|
|
4452
|
+
window) is simply absent.
|
|
4453
|
+
- **The cost by call** needs the devtools table (`cssints({ devtools: true })`): bytes of each class at the call that made
|
|
4454
|
+
it, the total and the number of calls, and how many of the call's classes the page carries; without the table the row is
|
|
4455
|
+
absent.
|
|
4456
|
+
- **The CSS files' arrival** is the resource timeline: each `.css`, its duration, Chrome's `renderBlockingStatus`, the
|
|
4457
|
+
slowest and the last.
|
|
4458
|
+
- **Frames** are sampled by a rAF loop: p50 / p95 / max, the dropped frames, a sparkline and a heatmap of the timeline
|
|
4459
|
+
(green under 16.7 ms, amber to 33.4, orange to 50, red above).
|
|
4460
|
+
- **Limits.** Long animation frames are not buffered entries, so a report from a cold console sees only the work after
|
|
4461
|
+
`observe()`; the five vitals are buffered but arrive a task after the module loads, so the first report can be early.
|
|
4462
|
+
A cross-origin sheet throws on `cssRules` and is counted as `blocked`. Coverage asks the DOM now: a class used only in a
|
|
4463
|
+
state is used if an element carries it.
|
|
4464
|
+
|
|
4465
|
+
`report()` returns the numbers, `print(report)` prints them, `reset()` forgets the history and the sheet's measurements,
|
|
4466
|
+
`stop()` ends the frame loop.
|
|
4467
|
+
|
|
4400
4468
|
### Debugging: traces of the engine (`CSSINTS_TRACE`, cssints-r2qq)
|
|
4401
4469
|
|
|
4402
4470
|
Opt-in. Set `CSSINTS_TRACE=otlp` (or `otlp:http://host:port`; the default is `http://localhost:4318`) before `vite dev` or `vite build`, and the plugin sends one trace per Vite hook to an OpenTelemetry backend over OTLP/HTTP (JSON). Unset, nothing is loaded and nothing runs: the hooks use Effect's own `runSync` and `runPromiseExit`, no tracer, no exporter, no HTTP client. The variable is read once, when the plugin loads.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cssints",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"description": "CSS in TypeScript: typed css.* calls compiled to atomic CSS at build time (a Vite plugin)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
}
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@cssints/css-grammar": "^0.
|
|
75
|
+
"@cssints/css-grammar": "^0.35.0",
|
|
76
76
|
"culori": "^4.0.2",
|
|
77
77
|
"effect": "4.0.6",
|
|
78
78
|
"happy-dom": "^20.14.6",
|
|
@@ -114,7 +114,7 @@ shade: fn({
|
|
|
114
114
|
- `value` runs at build time, once per call; the call is replaced by its CSS text. It throws for values the grammar
|
|
115
115
|
cannot reject (units it cannot compute, a failed accessibility rule): `fluid` rejects `em` and fails WCAG 1.4.4.
|
|
116
116
|
- What `value` returns must be a string or a Typed OM value (`cssints: tint() value must return a string or a Typed OM
|
|
117
|
-
value`). A result that does not fit the property is that property's error at the same site.
|
|
117
|
+
value`). A result that does not fit the property is that property's error at the same site.
|
|
118
118
|
|
|
119
119
|
## Identity and HMR
|
|
120
120
|
|