Docs Start
Philosophy
Native when possible. Tiny behavior when necessary. Svelte Lean lets the browser own the behavior it already understands and adds the smallest practical runtime only where the platform is incomplete.
On this page
The thesis
The browser changed. It now provides a native dialog with a top layer and page inertness, the
Popover API with light dismiss, declarative invoker commands, <details>,
native form controls, CSS state selectors and CSS anchor positioning. Many UI libraries still
start from an older assumption: every interaction becomes a component, the component gets state,
handlers, context and a runtime.
Svelte Lean starts from a different question: what does the browser already know how to do? Only after answering it does the package add JavaScript. Where the platform has no answer yet, as for tabs or the keyboard behavior of a menu, one shared behavior is registered per page and driven by attributes in the markup. Where the application genuinely owns state, the state stays in the application.
This is not a zero-JavaScript position. A correct, accessible combobox needs meaningful behavior code, and that is acceptable. The goal is the minimum necessary runtime, not zero runtime at any cost.
Three messages
Architecture
Use the browser first; do not rebuild platform behavior without a reason. The decision is recorded in ADR 0001 and explained on Native-first.
Runtime
A dialog needs no behavior JavaScript; tabs need a small shared behavior; a combobox needs a lazy controller. The runtime grows with the actual behavioral complexity rather than with a uniform component abstraction. Every primitive declares one of four runtime tiers and documents it.
Distribution
Installing the ecosystem is not the same as shipping it. One package holds every primitive; a page that uses only native dialog and popover markup ships 0 B of Svelte Lean behavior JavaScript in the production fixture, and a page with tabs ships 1521 B brotli including the shared kernel. The model and its invariants are on Distribution.
The Lean Rules
Seven rules, in order of precedence, from the architecture contract every change in the repository follows.
Native before custom
If HTML, CSS or the browser provides a behavior, the package uses it and owns nothing. A modal
is a <dialog> opened by command="show-modal", not an open-state
controller with a focus trap and a portal. A primitive must justify why it is not in a lower
tier.
Static before reactive
Configuration is a static attribute on the root, one per option, readable by CSS, by the build and by development validation. Nothing is computed at hydration for a root that has not been touched; the first interaction is the first work done for it.
DOM before duplicated state
If a state already has a natural DOM representation, the DOM is the source of truth: open, checked, aria-selected, the roving tabindex, hidden. Mirroring it into a JavaScript object or a data-state attribute creates synchronization work and a second thing that can be wrong.
Shared before per-instance
A Tier 1 behavior installs one listener per event type on the document runtime, for every
instance on the page. A thousand tabs roots keep one click and one keydown listener, which is asserted by a release-gate test with 1 and 1000 roots.
Lazy before eager
Roots are discovered through interaction, not by scanning the document on load or by observing
mutations. The combobox, the Tier 2 behavior, creates its per-instance state on the first
interaction with that instance and releases it through a scoped AbortController.
Compile before runtime
The build reads the markup and decides which registration modules the bundle needs. Svelte asks what the compiler can remove from the framework's runtime; Svelte Lean asks the same question about UI behavior.
Measure before optimize
Every number is produced by a script in the repository with its method and environment recorded, and every public claim has a registry entry with its evidence. A number that cannot be reproduced is not published; a missing measurement is shown as "not measured".
The playbook adds three rules for tests and copy: correctness before cleverness, accessibility before byte shaving, measure before claiming.
The mantra
Native before JavaScript. DOM before component state. Compile behavior; do not import components. And a fourth line used inside the repository: if the browser owns the behavior, Svelte Lean owns nothing.
"Compile behavior; do not import components" is the part that differs most from a conventional library. The author writes markup with a static marker; the build discovers it and adds the registration module; the runtime finds the root on its first event. No component is imported for the interaction, no instance object is created for a root nobody has touched, and the source markup, the build report and the manual import escape hatch keep the mechanism inspectable.
What is not claimed
- That Svelte is slow or that Svelte's event delegation needs fixing. Svelte compiles components to efficient code already; the advantage here is the absence of interaction abstractions the platform makes unnecessary, not a faster framework.
- That the browser can solve every widget. The tier model exists because it cannot; the point is to let the browser solve the parts it already solves well.
- That tiny bundles are the goal. Bytes are one cost among listeners, allocations, layout reads, hydration and main-thread time; accessibility correctness is never traded for any of them.
- Anything about another library. No comparative benchmark exists, and none will be published unless it meets every rule in the methodology.
- Anything that is not verified. The claim registry records the test, the measurement file and the status of each public statement; what is planned stays off the site.
The deliberate omissions are listed on What we do not do, the costs on Trade-offs.