# Compared with plain HTML

> For native primitives plain HTML is sometimes enough; what the package adds at Tier 0 and at Tier 1, with the measured cost of each.

Page: https://svelte-lean.vercel.app/docs/why-not-html · Markdown: https://svelte-lean.vercel.app/docs/why-not-html.md · Index: https://svelte-lean.vercel.app/llms.txt

For the 27 native primitives plain HTML is sometimes the right choice, because the behavior is the browser's either way.

## Comparison

| Concern | Plain HTML | Svelte Lean |
| --- | --- | --- |
| Tier 0 behavior (dialog, popover, details, form controls) | The browser's | The browser's; behavior JavaScript 0 B |
| Written contract and tests | Yours | Semantics, keyboard, focus, RTL, SSR, no-JS (dialog) |
| Styles | Yours | A token-based [stylesheet](https://svelte-lean.vercel.app/styling.md) per primitive: dark, forced colors, reduced motion, coarse pointers |
| Browser support | Yours to research | Feature, Baseline status and fallback per primitive ([Browser support](https://svelte-lean.vercel.app/docs/browser-support.md)) |
| Markup validation | None | Development warnings in the console |
| Tabs, menu keyboard model | Not in the platform | Shared behavior: tabs 1839 B brotli, menu 2773 B brotli, kernel included |

## When plain HTML is enough

With your own design system and no need for a written contract, write the markup and stop there:

`settings.svelte`

```html
<!-- Plain HTML. No package, no attribute from Svelte Lean. -->
<button type="button" commandfor="settings" command="show-modal">Settings</button>

<dialog id="settings" aria-labelledby="settings-title">
	<h2 id="settings-title">Settings</h2>
	<button type="button" commandfor="settings" command="close">Close</button>
</dialog>
```

## What the package adds for Tier 0

`settings.svelte`

```html
<!-- The same dialog with the protocol attributes: hooks for the stylesheet and for
     development validation. The behavior is still the browser's. -->
<button type="button" data-slean="button" commandfor="settings" command="show-modal">Settings</button>

<dialog id="settings" data-slean="dialog" aria-labelledby="settings-title">
	<h2 id="settings-title">Settings</h2>
	<button type="button" data-slean="button" commandfor="settings" command="close">Close</button>
</dialog>
```

- No production JavaScript: `data-slean` attributes are hooks for the stylesheet and validation, and the markup is valid without them.
- Shared tokens, protocol and option types across every primitive and the table.

## What the package adds for Tier 1

- Keyboard: arrows, Home and End, activation modes, disabled skipping, RTL, typeahead, Escape and focus return, tested in a browser.
- ARIA written to the DOM: `aria-selected`, roving `tabindex`, `hidden`, `aria-checked`.
- One listener per event type for every instance; build-time discovery.
- Cancelable events, so the application can veto a change.

## The rule

If the browser owns the behavior, Svelte Lean owns nothing, and the documentation shows the plain markup first.
