Budgets
A budget is the value a size script fails on. It sits ten to fifteen percent above the value measured when it was set (the behavior budgets take the measured value plus ten percent, rounded up to the next hundred bytes), so a regression fails CI before it reaches a release while a compressor update of a few bytes does not. Budget and measured value are read from the same artifact file; targets from the architecture records are marked as targets.
On this page
How budgets work
Each package's yarn size builds its entry points, compares the compressed size with
the budget written in the script, records both in artifacts/size.json and exits
with an error when the budget is exceeded. yarn verify runs it, so a pull request
cannot pass with a regression of that size. A budget is raised only with the reason in the
commit and a changeset (Methodology).
Behavior JavaScript
Each Tier 1 registration module built with Vite in production mode, including the shared kernel. The budget is brotli; the same script also requires the bundle to contain no diagnostic marker and to keep the registration side effect.
| Subject | Entry | Budget | Measured | Headroom | Status |
|---|---|---|---|---|---|
tabs | vite | 1700 B brotli | 1521 B brotli | 179 B | within budget |
menu | vite | 2100 B brotli | 1932 B brotli | 168 B | within budget |
listbox | vite | 2000 B brotli | 1838 B brotli | 162 B | within budget |
combobox | vite | 2900 B brotli | 2564 B brotli | 336 B | within budget |
select | vite | 3700 B brotli | 3362 B brotli | 338 B | within budget |
calendar | vite | 4300 B brotli | 3925 B brotli | 375 B | within budget |
date-picker | vite | 5400 B brotli | 4912 B brotli | 488 B | within budget |
tooltip | vite | 1400 B brotli | 1297 B brotli | 103 B | within budget |
toast | vite | 1600 B brotli | 1396 B brotli | 204 B | within budget |
tree | vite | 2100 B brotli | 1919 B brotli | 181 B | within budget |
range-slider | vite | 1400 B brotli | 1291 B brotli | 109 B | within budget |
number-field | vite | 1200 B brotli | 1082 B brotli | 118 B | within budget |
toggle | vite | 1000 B brotli | 888 B brotli | 112 B | within budget |
toggle-group | vite | 1800 B brotli | 1569 B brotli | 231 B | within budget |
toolbar | vite | 1700 B brotli | 1494 B brotli | 206 B | within budget |
splitter | vite | 1900 B brotli | 1683 B brotli | 217 B | within budget |
context-menu | vite | 1600 B brotli | 1409 B brotli | 191 B | within budget |
file-drop | vite | 1300 B brotli | 1107 B brotli | 193 B | within budget |
hover-card | vite | 1500 B brotli | 1353 B brotli | 147 B | within budget |
menubar | vite | 1700 B brotli | 1457 B brotli | 243 B | within budget |
Script: packages/primitives/scripts/size.mjs. Values from packages/primitives/artifacts/size.json;
method:
ESM, target es2022, gzip level 9, brotli quality 11 (node:zlib). Tier 1 and Tier 2 entries are dist/<name>/register.js with @svelte-lean/core from the workspace. "vite" builds the public specifier with Vite in production mode; the brotli budget applies to it and it must list no diagnostics and keep the registration. "esbuild" bundles the same file with esbuild under the production condition; it keeps development helpers core references from folded DEV gates and is recorded for comparison. "core-only" is a registration with an empty handler. "native-only" imports the root entry and a Tier 0 subpath and must contain no runtime.
Kernel
The root entry of @svelte-lean/core with every export, under the production condition and under the runtime-detected environment module, and what one Tier 1 behavior pulls in when built with Vite. The budget is gzip and applies to all three.
| Subject | Entry | Budget | Measured | Headroom | Status |
|---|---|---|---|---|---|
production | esbuild | 2500 B gzip | 2113 B gzip | 387 B | within budget |
default | esbuild | 2500 B gzip | 2197 B gzip | 303 B | within budget |
consumer | vite | 2500 B gzip | 994 B gzip | 1506 B | within budget |
Script: packages/core/scripts/size.mjs. Values from packages/core/artifacts/size.json;
method:
ESM, target es2022, gzip level 9, brotli quality 11 (node:zlib). "production" and "default" are dist/index.js with every export, esbuild bundle + minify, under the production export condition and the runtime-detected env module respectively. "consumer" is one Tier 1 behavior using parts and emit, built with Vite in production mode; it must list no diagnostics.
Table entry points
Every entry of @svelte-lean/table bundled on its own with esbuild, Svelte excluded; shared code is counted once per entry here and de-duplicated by a consumer bundler. Budgets are gzip.
| Subject | Entry | Budget | Measured | Headroom | Status |
|---|---|---|---|---|---|
renderer | dist/index.js | 22000 B gzip | 20031 B gzip | 1969 B | within budget |
headless | dist/core.js | 2700 B gzip | 2475 B gzip | 225 B | within budget |
engine | dist/table.svelte.js | 3600 B gzip | 3494 B gzip | 106 B | within budget |
grouping | dist/grouping.js | 2400 B gzip | 2148 B gzip | 252 B | within budget |
pivot | dist/pivot.js | 600 B gzip | 526 B gzip | 74 B | within budget |
editing | dist/editing.js | 800 B gzip | 707 B gzip | 93 B | within budget |
csv | dist/export.js | 850 B gzip | 741 B gzip | 109 B | within budget |
clipboard | dist/clipboard.js | 1000 B gzip | 895 B gzip | 105 B | within budget |
server | dist/server.js | 425 B gzip | 369 B gzip | 56 B | within budget |
state | dist/state.js | 900 B gzip | 796 B gzip | 104 B | within budget |
styles | dist/style.css | 7300 B gzip | 6665 B gzip | 635 B | within budget |
Script: packages/table/scripts/size.mjs. Values from packages/table/artifacts/size.json;
method:
Each entry bundled independently with esbuild (bundle + minify, ESM); shared code is counted per entry here and de-duplicated by consumer bundlers. Svelte peer runtime excluded. gzip level 9 / brotli quality 11 (node:zlib). Budgets are gzip.
Styles
The resolved index.css, every import inlined, minified. The budget is gzip and covers the whole package; single files are measured and listed on the Results page without a budget of their own.
| Subject | Entry | Budget | Measured | Headroom | Status |
|---|---|---|---|---|---|
index.css (resolved) | – | 21600 B gzip | 20765 B gzip | 835 B | within budget |
Script: packages/styles/scripts/size.mjs. Values from packages/styles/artifacts/size.json;
method:
bytes = shipped source; minified = comments and redundant whitespace removed by scripts/check.mjs minify (a bundler minifier may be slightly smaller); gzip level 9 and brotli quality 11 of the minified text. standalone = the file with the files it @imports inlined. "index.css (resolved)" inlines every @import once, depth first, and carries the budget.
Targets
ADR 0003 states initial targets for the behavior sizes and marks them as aspirational, to be published only once build output proves them. They are quoted here from the ADR text and compared with the measured values, whichever way the comparison goes.
Tabs < 1 kB brotli, basic Menu ~1–2 kB, entire common behavior set < 5 kB.
| Subject | Target (aspirational) | Measured | Enforced budget | Result |
|---|---|---|---|---|
| Tabs registration with the kernel | 1000 B brotli | 1521 B brotli | 1700 B brotli | above the target, within the enforced budget |
| Menu registration with the kernel | 1000–2000 B brotli | 1932 B brotli | 2100 B brotli | within the range |
| Entire common behavior set (tabs and menu on one page, one kernel) | 5000 B brotli | 2351 B brotli | none: measured by the tabs-and-menu fixture, not gated | within the target |
| Native primitives (button, dialog, popover, disclosure, checkbox, switch, radio group) | no behavior JavaScript | 0 B brotli | the native-only fixture forbids every runtime module | met |
Where a measured value sits above an aspirational target, the target stays in the ADR as a direction and the enforced budget is the gate. The two are different things: a target says where the architecture wants to go, a budget says what a release may not exceed.
Tier budgets
Every primitive declares one runtime tier in its contract, and each tier is a budget of a different kind: not bytes, but what a primitive may cost at runtime (ADR 0003).
| Tier | Name | Budget |
|---|---|---|
| 0 | Native | No runtime JavaScript, no instance listeners, no instance state objects |
| 1 | Delegated micro-behavior | One shared listener per required event type; no per-root listener; DOM-encoded state |
| 2 | Lazy scoped controller | WeakMap state created on first interaction; AbortController-scoped listeners; cleanup |
| 3 | Application-controlled | Svelte state owned by the application; never required by a default primitive |
Listener budget
A Tier 1 behavior installs one listener per event type it declares, on the document runtime,
regardless of how many roots are on the page. The gate is an exact equality in listeners.spec.ts:
the per-type counts for a thousand tabs roots equal the counts for one root, on the runtime's
account and on an independent count of every addEventListener call. No number in a
file holds this budget; the assertion is the budget. The Results page reads the live counts of this site and
the proof page mounts a thousand roots on demand.
Tier 2 has a budget of its own: lazy state created on first interaction and released with its listeners. The combobox is the Tier 2 behavior; its register module carries the brotli budget in the table above, and the lazy-state budget is an exact assertion, not a number: combobox.test.ts holds no state for a thousand untouched roots and lazy.spec.ts reads zero controllers after load for a hundred roots in Chrome, one per interacted root, released on close and on focus loss.