@tenphi/tasty 0.0.0-snapshot.fafcbfe → 0.0.0-snapshot.fdb16ea
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 +397 -185
- package/dist/async-storage-DKK-wTD4.js +44 -0
- package/dist/async-storage-DKK-wTD4.js.map +1 -0
- package/dist/babel-CMYf4JZ2.d.ts +83 -0
- package/dist/collector-Ctn5s74s.d.ts +145 -0
- package/dist/collector-DMSR6ANK.js +304 -0
- package/dist/collector-DMSR6ANK.js.map +1 -0
- package/dist/config-B23gZrfP.js +12317 -0
- package/dist/config-B23gZrfP.js.map +1 -0
- package/dist/config-CZx8_DCu.d.ts +1296 -0
- package/dist/context-CA8YKeMn.js +24 -0
- package/dist/context-CA8YKeMn.js.map +1 -0
- package/dist/core/index.d.ts +5 -33
- package/dist/core/index.js +6 -26
- package/dist/core-B1tyn69d.js +1573 -0
- package/dist/core-B1tyn69d.js.map +1 -0
- package/dist/css-writer-CvAxkR6S.js +390 -0
- package/dist/css-writer-CvAxkR6S.js.map +1 -0
- package/dist/format-global-rules-DklyaXv-.js +22 -0
- package/dist/format-global-rules-DklyaXv-.js.map +1 -0
- package/dist/format-rules-BCTWVvcS.js +130 -0
- package/dist/format-rules-BCTWVvcS.js.map +1 -0
- package/dist/hydrate-BCPvkJj3.js +37 -0
- package/dist/hydrate-BCPvkJj3.js.map +1 -0
- package/dist/index-B3o0aCYV.d.ts +1538 -0
- package/dist/index-CsvB0EXZ.d.ts +1909 -0
- package/dist/index.d.ts +5 -40
- package/dist/index.js +921 -32
- package/dist/index.js.map +1 -0
- package/dist/keyframes-Dxdx3KOo.js +493 -0
- package/dist/keyframes-Dxdx3KOo.js.map +1 -0
- package/dist/{utils/merge-styles.js → merge-styles-C-uwpoNW.js} +4 -6
- package/dist/merge-styles-C-uwpoNW.js.map +1 -0
- package/dist/{utils/merge-styles.d.ts → merge-styles-DJTSfz_M.d.ts} +3 -3
- package/dist/{utils/resolve-recipes.js → resolve-recipes-BuOvSEa2.js} +5 -8
- package/dist/resolve-recipes-BuOvSEa2.js.map +1 -0
- package/dist/ssr/astro-client.d.ts +1 -0
- package/dist/ssr/astro-client.js +19 -0
- package/dist/ssr/astro-client.js.map +1 -0
- package/dist/ssr/astro-middleware-static.d.ts +16 -0
- package/dist/ssr/astro-middleware-static.js +18 -0
- package/dist/ssr/astro-middleware-static.js.map +1 -0
- package/dist/ssr/astro-middleware.d.ts +17 -0
- package/dist/ssr/astro-middleware.js +19 -0
- package/dist/ssr/astro-middleware.js.map +1 -0
- package/dist/ssr/astro.d.ts +93 -0
- package/dist/ssr/astro.js +157 -0
- package/dist/ssr/astro.js.map +1 -0
- package/dist/ssr/index.d.ts +37 -0
- package/dist/ssr/index.js +10 -0
- package/dist/ssr/index.js.map +1 -0
- package/dist/ssr/next.d.ts +45 -0
- package/dist/ssr/next.js +74 -0
- package/dist/ssr/next.js.map +1 -0
- package/dist/static/index.d.ts +91 -5
- package/dist/static/index.js +49 -4
- package/dist/static/index.js.map +1 -0
- package/dist/static/inject.d.ts +5 -0
- package/dist/static/inject.js +17 -0
- package/dist/static/inject.js.map +1 -0
- package/dist/zero/babel.d.ts +2 -108
- package/dist/zero/babel.js +235 -39
- package/dist/zero/babel.js.map +1 -1
- package/dist/zero/index.d.ts +81 -3
- package/dist/zero/index.js +2 -4
- package/dist/zero/next.d.ts +56 -30
- package/dist/zero/next.js +106 -41
- package/dist/zero/next.js.map +1 -1
- package/docs/README.md +39 -0
- package/docs/adoption.md +323 -0
- package/docs/ai-agents.md +224 -0
- package/docs/comparison.md +448 -0
- package/docs/configuration.md +823 -0
- package/docs/debug.md +322 -0
- package/docs/design-system.md +455 -0
- package/docs/dsl.md +1021 -0
- package/docs/getting-started.md +238 -0
- package/docs/injector.md +614 -0
- package/docs/methodology.md +648 -0
- package/docs/migration-v3.md +285 -0
- package/docs/pipeline.md +741 -0
- package/docs/plugins.md +353 -0
- package/docs/react-api.md +681 -0
- package/docs/runtime-benchmarks.md +216 -0
- package/docs/ssr.md +451 -0
- package/docs/styles.md +645 -0
- package/docs/tasty-static.md +582 -0
- package/package.json +149 -40
- package/tasty.config.ts +6 -0
- package/dist/_virtual/_rolldown/runtime.js +0 -8
- package/dist/chunks/cacheKey.js +0 -70
- package/dist/chunks/cacheKey.js.map +0 -1
- package/dist/chunks/definitions.d.ts +0 -37
- package/dist/chunks/definitions.js +0 -260
- package/dist/chunks/definitions.js.map +0 -1
- package/dist/chunks/renderChunk.js +0 -61
- package/dist/chunks/renderChunk.js.map +0 -1
- package/dist/config.d.ts +0 -280
- package/dist/config.js +0 -403
- package/dist/config.js.map +0 -1
- package/dist/debug.d.ts +0 -204
- package/dist/debug.js +0 -733
- package/dist/debug.js.map +0 -1
- package/dist/hooks/useGlobalStyles.d.ts +0 -27
- package/dist/hooks/useGlobalStyles.js +0 -56
- package/dist/hooks/useGlobalStyles.js.map +0 -1
- package/dist/hooks/useKeyframes.d.ts +0 -56
- package/dist/hooks/useKeyframes.js +0 -54
- package/dist/hooks/useKeyframes.js.map +0 -1
- package/dist/hooks/useProperty.d.ts +0 -79
- package/dist/hooks/useProperty.js +0 -91
- package/dist/hooks/useProperty.js.map +0 -1
- package/dist/hooks/useRawCSS.d.ts +0 -53
- package/dist/hooks/useRawCSS.js +0 -28
- package/dist/hooks/useRawCSS.js.map +0 -1
- package/dist/hooks/useStyles.d.ts +0 -40
- package/dist/hooks/useStyles.js +0 -169
- package/dist/hooks/useStyles.js.map +0 -1
- package/dist/injector/index.d.ts +0 -157
- package/dist/injector/index.js +0 -154
- package/dist/injector/index.js.map +0 -1
- package/dist/injector/injector.d.ts +0 -139
- package/dist/injector/injector.js +0 -404
- package/dist/injector/injector.js.map +0 -1
- package/dist/injector/sheet-manager.d.ts +0 -127
- package/dist/injector/sheet-manager.js +0 -714
- package/dist/injector/sheet-manager.js.map +0 -1
- package/dist/injector/types.d.ts +0 -135
- package/dist/keyframes/index.js +0 -206
- package/dist/keyframes/index.js.map +0 -1
- package/dist/parser/classify.js +0 -319
- package/dist/parser/classify.js.map +0 -1
- package/dist/parser/const.js +0 -33
- package/dist/parser/const.js.map +0 -1
- package/dist/parser/lru.js +0 -109
- package/dist/parser/lru.js.map +0 -1
- package/dist/parser/parser.d.ts +0 -25
- package/dist/parser/parser.js +0 -116
- package/dist/parser/parser.js.map +0 -1
- package/dist/parser/tokenizer.js +0 -69
- package/dist/parser/tokenizer.js.map +0 -1
- package/dist/parser/types.d.ts +0 -51
- package/dist/parser/types.js +0 -46
- package/dist/parser/types.js.map +0 -1
- package/dist/pipeline/conditions.d.ts +0 -134
- package/dist/pipeline/conditions.js +0 -400
- package/dist/pipeline/conditions.js.map +0 -1
- package/dist/pipeline/exclusive.js +0 -231
- package/dist/pipeline/exclusive.js.map +0 -1
- package/dist/pipeline/index.d.ts +0 -53
- package/dist/pipeline/index.js +0 -641
- package/dist/pipeline/index.js.map +0 -1
- package/dist/pipeline/materialize.js +0 -908
- package/dist/pipeline/materialize.js.map +0 -1
- package/dist/pipeline/parseStateKey.d.ts +0 -15
- package/dist/pipeline/parseStateKey.js +0 -430
- package/dist/pipeline/parseStateKey.js.map +0 -1
- package/dist/pipeline/simplify.js +0 -557
- package/dist/pipeline/simplify.js.map +0 -1
- package/dist/plugins/okhsl-plugin.d.ts +0 -35
- package/dist/plugins/okhsl-plugin.js +0 -371
- package/dist/plugins/okhsl-plugin.js.map +0 -1
- package/dist/plugins/types.d.ts +0 -69
- package/dist/properties/index.js +0 -158
- package/dist/properties/index.js.map +0 -1
- package/dist/states/index.d.ts +0 -49
- package/dist/states/index.js +0 -416
- package/dist/states/index.js.map +0 -1
- package/dist/static/tastyStatic.d.ts +0 -46
- package/dist/static/tastyStatic.js +0 -31
- package/dist/static/tastyStatic.js.map +0 -1
- package/dist/static/types.d.ts +0 -49
- package/dist/static/types.js +0 -24
- package/dist/static/types.js.map +0 -1
- package/dist/styles/align.d.ts +0 -15
- package/dist/styles/align.js +0 -14
- package/dist/styles/align.js.map +0 -1
- package/dist/styles/border.d.ts +0 -25
- package/dist/styles/border.js +0 -114
- package/dist/styles/border.js.map +0 -1
- package/dist/styles/color.d.ts +0 -14
- package/dist/styles/color.js +0 -23
- package/dist/styles/color.js.map +0 -1
- package/dist/styles/createStyle.js +0 -77
- package/dist/styles/createStyle.js.map +0 -1
- package/dist/styles/dimension.js +0 -97
- package/dist/styles/dimension.js.map +0 -1
- package/dist/styles/display.d.ts +0 -37
- package/dist/styles/display.js +0 -67
- package/dist/styles/display.js.map +0 -1
- package/dist/styles/fade.d.ts +0 -15
- package/dist/styles/fade.js +0 -58
- package/dist/styles/fade.js.map +0 -1
- package/dist/styles/fill.d.ts +0 -44
- package/dist/styles/fill.js +0 -51
- package/dist/styles/fill.js.map +0 -1
- package/dist/styles/flow.d.ts +0 -16
- package/dist/styles/flow.js +0 -12
- package/dist/styles/flow.js.map +0 -1
- package/dist/styles/gap.d.ts +0 -31
- package/dist/styles/gap.js +0 -37
- package/dist/styles/gap.js.map +0 -1
- package/dist/styles/height.d.ts +0 -17
- package/dist/styles/height.js +0 -20
- package/dist/styles/height.js.map +0 -1
- package/dist/styles/index.d.ts +0 -2
- package/dist/styles/index.js +0 -9
- package/dist/styles/index.js.map +0 -1
- package/dist/styles/inset.d.ts +0 -52
- package/dist/styles/inset.js +0 -150
- package/dist/styles/inset.js.map +0 -1
- package/dist/styles/justify.d.ts +0 -15
- package/dist/styles/justify.js +0 -14
- package/dist/styles/justify.js.map +0 -1
- package/dist/styles/list.d.ts +0 -16
- package/dist/styles/list.js +0 -98
- package/dist/styles/list.js.map +0 -1
- package/dist/styles/margin.d.ts +0 -24
- package/dist/styles/margin.js +0 -104
- package/dist/styles/margin.js.map +0 -1
- package/dist/styles/outline.d.ts +0 -29
- package/dist/styles/outline.js +0 -65
- package/dist/styles/outline.js.map +0 -1
- package/dist/styles/padding.d.ts +0 -24
- package/dist/styles/padding.js +0 -104
- package/dist/styles/padding.js.map +0 -1
- package/dist/styles/predefined.d.ts +0 -73
- package/dist/styles/predefined.js +0 -241
- package/dist/styles/predefined.js.map +0 -1
- package/dist/styles/preset.d.ts +0 -47
- package/dist/styles/preset.js +0 -126
- package/dist/styles/preset.js.map +0 -1
- package/dist/styles/radius.d.ts +0 -14
- package/dist/styles/radius.js +0 -51
- package/dist/styles/radius.js.map +0 -1
- package/dist/styles/scrollbar.d.ts +0 -21
- package/dist/styles/scrollbar.js +0 -105
- package/dist/styles/scrollbar.js.map +0 -1
- package/dist/styles/shadow.d.ts +0 -14
- package/dist/styles/shadow.js +0 -24
- package/dist/styles/shadow.js.map +0 -1
- package/dist/styles/styledScrollbar.d.ts +0 -47
- package/dist/styles/styledScrollbar.js +0 -38
- package/dist/styles/styledScrollbar.js.map +0 -1
- package/dist/styles/transition.d.ts +0 -14
- package/dist/styles/transition.js +0 -158
- package/dist/styles/transition.js.map +0 -1
- package/dist/styles/types.d.ts +0 -498
- package/dist/styles/width.d.ts +0 -17
- package/dist/styles/width.js +0 -20
- package/dist/styles/width.js.map +0 -1
- package/dist/tasty.d.ts +0 -982
- package/dist/tasty.js +0 -191
- package/dist/tasty.js.map +0 -1
- package/dist/tokens/typography.d.ts +0 -19
- package/dist/tokens/typography.js +0 -237
- package/dist/tokens/typography.js.map +0 -1
- package/dist/types.d.ts +0 -184
- package/dist/utils/cache-wrapper.js +0 -26
- package/dist/utils/cache-wrapper.js.map +0 -1
- package/dist/utils/case-converter.js +0 -8
- package/dist/utils/case-converter.js.map +0 -1
- package/dist/utils/colors.d.ts +0 -5
- package/dist/utils/colors.js +0 -9
- package/dist/utils/colors.js.map +0 -1
- package/dist/utils/css-types.d.ts +0 -7
- package/dist/utils/dotize.d.ts +0 -26
- package/dist/utils/dotize.js +0 -122
- package/dist/utils/dotize.js.map +0 -1
- package/dist/utils/filter-base-props.d.ts +0 -15
- package/dist/utils/filter-base-props.js +0 -45
- package/dist/utils/filter-base-props.js.map +0 -1
- package/dist/utils/get-display-name.d.ts +0 -7
- package/dist/utils/get-display-name.js +0 -10
- package/dist/utils/get-display-name.js.map +0 -1
- package/dist/utils/hsl-to-rgb.js +0 -38
- package/dist/utils/hsl-to-rgb.js.map +0 -1
- package/dist/utils/is-dev-env.js +0 -19
- package/dist/utils/is-dev-env.js.map +0 -1
- package/dist/utils/is-valid-element-type.js +0 -15
- package/dist/utils/is-valid-element-type.js.map +0 -1
- package/dist/utils/merge-styles.js.map +0 -1
- package/dist/utils/mod-attrs.d.ts +0 -8
- package/dist/utils/mod-attrs.js +0 -21
- package/dist/utils/mod-attrs.js.map +0 -1
- package/dist/utils/okhsl-to-rgb.js +0 -296
- package/dist/utils/okhsl-to-rgb.js.map +0 -1
- package/dist/utils/process-tokens.d.ts +0 -31
- package/dist/utils/process-tokens.js +0 -171
- package/dist/utils/process-tokens.js.map +0 -1
- package/dist/utils/resolve-recipes.d.ts +0 -17
- package/dist/utils/resolve-recipes.js.map +0 -1
- package/dist/utils/string.js +0 -8
- package/dist/utils/string.js.map +0 -1
- package/dist/utils/styles.d.ts +0 -183
- package/dist/utils/styles.js +0 -585
- package/dist/utils/styles.js.map +0 -1
- package/dist/utils/typography.d.ts +0 -36
- package/dist/utils/typography.js +0 -53
- package/dist/utils/typography.js.map +0 -1
- package/dist/utils/warnings.d.ts +0 -16
- package/dist/utils/warnings.js +0 -16
- package/dist/utils/warnings.js.map +0 -1
- package/dist/zero/css-writer.d.ts +0 -45
- package/dist/zero/css-writer.js +0 -74
- package/dist/zero/css-writer.js.map +0 -1
- package/dist/zero/extractor.d.ts +0 -24
- package/dist/zero/extractor.js +0 -150
- package/dist/zero/extractor.js.map +0 -1
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Runtime Benchmarks
|
|
2
|
+
|
|
3
|
+
Tasty keeps its performance claims in reproducible benchmarks rather than
|
|
4
|
+
combining unlike measurements into one score. The repository measures four
|
|
5
|
+
different costs:
|
|
6
|
+
|
|
7
|
+
1. Style parsing and generation in Node.
|
|
8
|
+
2. The React overhead of an empty `tasty({})` wrapper.
|
|
9
|
+
3. Cold browser generation and injection compared with equivalent CSS that is
|
|
10
|
+
already on the page.
|
|
11
|
+
4. A representative React update that combines wrapper, cache, generation,
|
|
12
|
+
injection, and style-resolution work.
|
|
13
|
+
|
|
14
|
+
These are focused microbenchmarks, not page-load or interaction scores. Run
|
|
15
|
+
them several times on an otherwise idle machine and use a production profile
|
|
16
|
+
to decide whether any cost matters in an application.
|
|
17
|
+
|
|
18
|
+
## Reproducing the Results
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm bench
|
|
22
|
+
pnpm bench:overhead
|
|
23
|
+
pnpm bench:injection
|
|
24
|
+
pnpm bench:tree
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`pnpm bench` runs the core pipeline benchmarks in Node. The other three commands
|
|
28
|
+
use production code paths in headless Chromium. The first checkout may require
|
|
29
|
+
`pnpm test:setup` to download Chromium.
|
|
30
|
+
|
|
31
|
+
Run the Node and browser suites separately so they do not compete for CPU. The
|
|
32
|
+
browser timer has 0.1 ms resolution, so both browser benchmarks perform many
|
|
33
|
+
matched operations per sample and divide the absolute difference by the number
|
|
34
|
+
of elements or rules. Machine load, browser versions, and CPU power will move
|
|
35
|
+
the results.
|
|
36
|
+
|
|
37
|
+
## Core Style Pipeline
|
|
38
|
+
|
|
39
|
+
The following numbers are single-call throughput measured on an Apple M1 Max
|
|
40
|
+
with Node 22:
|
|
41
|
+
|
|
42
|
+
| Operation | ops/sec | Latency (mean) |
|
|
43
|
+
| ----------------------------------------------------------- | -------------------: | -------------: |
|
|
44
|
+
| `renderStyles` — 5 flat properties (cold) | ~60,000 | ~17 us |
|
|
45
|
+
| `renderStyles` — state map with media/hover/modifier (cold) | ~18,500 | ~54 us |
|
|
46
|
+
| `renderStyles` — same styles (cached) | ~5,800,000 | ~0.17 us |
|
|
47
|
+
| `parseStateKey` — simple key like `:hover` (cold) | ~790,000 | ~1.3 us |
|
|
48
|
+
| `parseStateKey` — complex OR/AND/NOT key (cold) | ~140,000 | ~7 us |
|
|
49
|
+
| `parseStateKey` — any key (cached) | ~3,400,000–8,300,000 | ~0.1–0.3 us |
|
|
50
|
+
| `parseStyle` — value tokens like `2x 4x` (cold) | ~344,000 | ~2.9 us |
|
|
51
|
+
| `parseStyle` — color tokens (cold) | ~567,000 | ~1.8 us |
|
|
52
|
+
| `parseStyle` — any value (cached) | ~15,250,000 | ~0.07 us |
|
|
53
|
+
|
|
54
|
+
“Cold” cases use unique inputs to bypass the relevant caches. Cached cases
|
|
55
|
+
reuse one input and measure the LRU hot path. Expect roughly ±10% between runs.
|
|
56
|
+
These benchmarks do not include React, DOM work, stylesheet injection, style
|
|
57
|
+
resolution, layout, or paint.
|
|
58
|
+
|
|
59
|
+
The benchmark sources are colocated with the code they exercise:
|
|
60
|
+
[`pipeline.bench.ts`](../src/pipeline/pipeline.bench.ts),
|
|
61
|
+
[`parseStateKey.bench.ts`](../src/pipeline/parseStateKey.bench.ts), and the
|
|
62
|
+
parser benchmark files under [`src/parser`](../src/parser).
|
|
63
|
+
|
|
64
|
+
## Empty Wrapper Overhead
|
|
65
|
+
|
|
66
|
+
Skipping the style pipeline does not make a `tasty()` component free. Even
|
|
67
|
+
`tasty({})` is a React component between its parent and the host element. React
|
|
68
|
+
tracks another fiber, and Tasty still processes and forwards the element's
|
|
69
|
+
props.
|
|
70
|
+
|
|
71
|
+
[`tasty-overhead.bench.tsx`](../src/tasty-overhead.bench.tsx) compares 10,000
|
|
72
|
+
raw `<div className>` siblings with 10,000 instances of one module-scoped
|
|
73
|
+
`tasty({})` component. Both receive the same props, and the benchmark fails if
|
|
74
|
+
they do not produce equivalent DOM.
|
|
75
|
+
|
|
76
|
+
The benchmark uses production React in headless Chromium. The factory is
|
|
77
|
+
created and its empty class-name cache is warmed before timing, so factory
|
|
78
|
+
creation, style generation, and injection are excluded. A detached container
|
|
79
|
+
excludes layout, paint, and stylesheet matching. Every commit is wrapped in
|
|
80
|
+
`flushSync`, keeping its synchronous reconciliation and commit inside the
|
|
81
|
+
sample. This does not estimate React's concurrent scheduling latency.
|
|
82
|
+
|
|
83
|
+
On an Apple M3 Pro with React 19.2.4 and Chromium 151, three consecutive runs
|
|
84
|
+
produced these ranges:
|
|
85
|
+
|
|
86
|
+
| Work on 10,000 siblings | Raw elements | `tasty({})` | Extra per wrapped element |
|
|
87
|
+
| ----------------------------------- | -----------: | -----------: | ------------------------: |
|
|
88
|
+
| Mount + remove | 4.5–4.9 ms | 14.0–14.9 ms | 0.95–1.00 us |
|
|
89
|
+
| Rerender, same host props | 1.3–1.4 ms | 10.9–11.4 ms | 0.96–1.00 us |
|
|
90
|
+
| Rerender, change one host attribute | 2.8–3.4 ms | 15.0–15.7 ms | 1.21–1.27 us |
|
|
91
|
+
|
|
92
|
+
The useful result is the raw/Tasty time difference divided by 10,000, not the
|
|
93
|
+
ratio between the two times. The ratio becomes large because the raw baseline
|
|
94
|
+
is tiny. In this synthetic workload, an empty wrapper adds roughly 1 us per
|
|
95
|
+
participating element, or 1.2–1.3 us when React also changes a DOM attribute.
|
|
96
|
+
|
|
97
|
+
This is the floor Tasty consumes when it has no styling job. It is not a
|
|
98
|
+
page-level score. Real trees include application components, effects, layout,
|
|
99
|
+
paint, and usually far fewer simultaneous styled-element updates. The benchmark
|
|
100
|
+
also does not measure retained memory; that requires a matched-tree heap
|
|
101
|
+
snapshot experiment with controlled garbage collection.
|
|
102
|
+
|
|
103
|
+
## Cold Generation and Injection
|
|
104
|
+
|
|
105
|
+
[`tasty-injection.bench.ts`](../src/tasty-injection.bench.ts) measures the extra
|
|
106
|
+
work when Tasty must generate and inject CSS that an otherwise equivalent page
|
|
107
|
+
already has. It does not compare different stylesheet insertion techniques.
|
|
108
|
+
|
|
109
|
+
The benchmark covers four group sizes with one style-resolution boundary per
|
|
110
|
+
group:
|
|
111
|
+
|
|
112
|
+
- **One new rule:** add one rule, append its element, and immediately read its
|
|
113
|
+
computed style. Each sample performs 50 independent groups for timer
|
|
114
|
+
stability.
|
|
115
|
+
- **10 new rules together:** generate and insert 10 rules, append their 10
|
|
116
|
+
elements, then read every computed color without another write. Each sample
|
|
117
|
+
performs 10 independent groups.
|
|
118
|
+
- **100 or 1,000 new rules together:** perform the same sequence once per
|
|
119
|
+
sample with 100 or 1,000 rules before the resolution boundary.
|
|
120
|
+
|
|
121
|
+
Repeating the smaller groups inside one sample only raises the measurement
|
|
122
|
+
above Chromium's timer resolution. Results are divided by that repetition
|
|
123
|
+
count, so every row below describes one group with one resolution boundary.
|
|
124
|
+
|
|
125
|
+
For every transaction, the existing-CSS control has the equivalent stylesheet
|
|
126
|
+
parsed, adopted, and attached before timing. The runtime root has a Tasty
|
|
127
|
+
stylesheet pre-created with an unrelated sentinel rule, but not the measured
|
|
128
|
+
rules. Both paths perform the same class assignment, DOM commit, and
|
|
129
|
+
computed-style reads. Only the runtime path calls `computeStyles()` and inserts
|
|
130
|
+
the new rules.
|
|
131
|
+
|
|
132
|
+
Preparation and cleanup happen outside the sample timer. Every runtime style
|
|
133
|
+
value is unique within a cycle, the relevant caches are cleared between cycles,
|
|
134
|
+
and a guard verifies that both paths resolve to the same color. React and the
|
|
135
|
+
`tasty()` wrapper are absent so their independently measured costs do not enter
|
|
136
|
+
the result. Pre-creating both stylesheets also excludes one-time sheet creation
|
|
137
|
+
and adoption from the subtraction.
|
|
138
|
+
|
|
139
|
+
On an Apple M3 Pro with Chromium 151, three consecutive runs produced these
|
|
140
|
+
normalized ranges:
|
|
141
|
+
|
|
142
|
+
| New rules before one resolution | Groups per sample | Incremental cost per group | Incremental cost per rule |
|
|
143
|
+
| ------------------------------: | ----------------: | -------------------------: | ------------------------: |
|
|
144
|
+
| 1 | 50 | 0.111–0.114 ms | 111–114 us |
|
|
145
|
+
| 10 | 10 | 0.142–0.159 ms | 14.2–15.9 us |
|
|
146
|
+
| 100 | 1 | 0.90–0.94 ms | 9.0–9.4 us |
|
|
147
|
+
| 1,000 | 1 | 7.58–8.10 ms | 7.6–8.1 us |
|
|
148
|
+
|
|
149
|
+
The curve makes the fixed work visible. Delivering 1,000 rules before one
|
|
150
|
+
resolution boundary cost roughly 67–73 times as much in total as delivering
|
|
151
|
+
one rule and resolving it, not 1,000 times as much. The average incremental
|
|
152
|
+
cost per rule was roughly 14–15 times lower because the group amortized fixed
|
|
153
|
+
transaction work and let the browser resolve all stylesheet writes together.
|
|
154
|
+
|
|
155
|
+
The subtraction is the meaningful result. It includes Tasty's cold style
|
|
156
|
+
generation, cache and injector bookkeeping, rule insertion, and any additional
|
|
157
|
+
style invalidation exposed by that workload's resolution boundary. It does not
|
|
158
|
+
pretend to isolate `insertRule()` from the system that calls it.
|
|
159
|
+
|
|
160
|
+
This is a deliberately cold workload. Reused styles resolve from cache and do
|
|
161
|
+
not inject another rule. Different rule complexity, DOM shape, stylesheet size,
|
|
162
|
+
browser, and hardware will change the number. Because the same group and
|
|
163
|
+
resolution pattern is present in each workload's control, the difference
|
|
164
|
+
answers the narrower delivery question: how much extra work did Tasty perform
|
|
165
|
+
when the same CSS was not already there?
|
|
166
|
+
|
|
167
|
+
## Representative React Tree
|
|
168
|
+
|
|
169
|
+
[`tasty-tree.bench.tsx`](../src/tasty-tree.bench.tsx) bridges the isolated
|
|
170
|
+
measurements with one production React update. Every path updates 1,000 host
|
|
171
|
+
elements sharing 20 combinations of `color` and `padding`, then reads computed
|
|
172
|
+
styles after the commit:
|
|
173
|
+
|
|
174
|
+
- **CSS already present:** raw elements switch between classes whose rules were
|
|
175
|
+
attached before timing.
|
|
176
|
+
- **Tasty, warm:** the elements switch between two sets of Tasty styles that
|
|
177
|
+
were generated before timing.
|
|
178
|
+
- **Tasty, cold:** every update introduces 20 new combinations shared across
|
|
179
|
+
the 1,000 elements.
|
|
180
|
+
|
|
181
|
+
The Tasty paths use the normal component API and `TastyBatchProvider`. On an
|
|
182
|
+
Apple M3 Pro with production React 19.2.4 and Chromium 151, three consecutive
|
|
183
|
+
runs produced these mean ranges:
|
|
184
|
+
|
|
185
|
+
| Update path | Total time for 1,000 elements |
|
|
186
|
+
| ----------------------------------- | ----------------------------: |
|
|
187
|
+
| CSS already present | 1.64–1.70 ms |
|
|
188
|
+
| Tasty, shared styles already cached | 4.17–4.43 ms |
|
|
189
|
+
| Tasty, 20 new shared styles | 4.62–4.80 ms |
|
|
190
|
+
|
|
191
|
+
In this workload, the warmed Tasty path added 2.52–2.73 ms over raw elements
|
|
192
|
+
with existing CSS. Introducing 20 new shared combinations added another
|
|
193
|
+
0.38–0.55 ms over the warm Tasty update. This is a synthetic update, not an
|
|
194
|
+
application score: it excludes effects, layout reads, paint, and concurrent
|
|
195
|
+
scheduling, while deliberately making all 1,000 elements participate in every
|
|
196
|
+
commit.
|
|
197
|
+
|
|
198
|
+
## Reading the Results Together
|
|
199
|
+
|
|
200
|
+
Do not add all three benchmark numbers to estimate an application blindly.
|
|
201
|
+
They describe different paths:
|
|
202
|
+
|
|
203
|
+
- A stable `tasty()` factory can skip the style pipeline on later renders, but
|
|
204
|
+
its React wrapper still participates in reconciliation.
|
|
205
|
+
- A cached style avoids cold parsing and generation and does not insert a new
|
|
206
|
+
rule.
|
|
207
|
+
- A genuinely new style pays generation and injection once, then becomes
|
|
208
|
+
reusable.
|
|
209
|
+
- A tree that shares a small style vocabulary pays the wrapper path per element
|
|
210
|
+
but cold generation only per new combination.
|
|
211
|
+
- Browser style resolution, layout, and paint depend on the actual document and
|
|
212
|
+
need application-level profiling.
|
|
213
|
+
|
|
214
|
+
The practical optimization target is therefore repeated work: keep style input
|
|
215
|
+
stable when possible, reuse generated chunks, and generate CSS at build or
|
|
216
|
+
server time when runtime flexibility is unnecessary.
|