# Separator

> <hr>, or role="separator" between inline items.

Page: https://svelte-lean.vercel.app/primitives/separator · Markdown: https://svelte-lean.vercel.app/primitives/separator.md · Index: https://svelte-lean.vercel.app/llms.txt

A line between blocks or between items in a row: a native \<hr\>, or role="separator" with a vertical orientation.

```svelte
<script lang="ts">
	import '@svelte-lean/styles/separator.css';
</script>

<div class="profile">
	<p class="name">Svelte Lean</p>
	<p>Browser-native UI primitives for Svelte.</p>
	<hr data-slean="separator" />
	<nav aria-label="Project links" class="links">
		<a href="/docs">Docs</a>
		<div role="separator" aria-orientation="vertical" data-slean="separator"></div>
		<a href="/primitives">Primitives</a>
		<div role="separator" aria-orientation="vertical" data-slean="separator"></div>
		<a href="/table">Table</a>
	</nav>
</div>

<style>
	.profile {
		max-inline-size: 22rem;
		font-size: var(--slean-text-sm);
	}

	.profile p {
		margin: 0;
	}

	.name {
		font-weight: var(--slean-font-weight-medium);
	}

	.links {
		display: flex;
		align-items: center;
		block-size: 1.25rem;
	}
</style>
```

The rule is an \<hr\>; the lines between the links are vertical separators.

## Examples

### Between sections

```svelte
<script lang="ts">
	import '@svelte-lean/styles/separator.css';
</script>

<!-- <hr> between blocks that change topic: announced as a separator. -->
<article class="sections">
	<h4>Notifications</h4>
	<p>Email and push notifications for mentions and replies.</p>
	<hr data-slean="separator" />
	<h4>Privacy</h4>
	<p>Who can see your profile and your activity.</p>
	<hr data-slean="separator" />
	<h4>Danger zone</h4>
	<p>Delete the account and every project in it.</p>
</article>

<style>
	.sections {
		max-inline-size: 26rem;
		font-size: var(--slean-text-sm);
	}

	.sections :is(h4, p) {
		margin: 0;
	}

	.sections h4 {
		font-size: var(--slean-text-sm);
	}

	.sections p {
		color: var(--slean-fg-muted);
	}
</style>
```

### Between buttons

```svelte
<script lang="ts">
	import '@svelte-lean/styles/button.css';
	import '@svelte-lean/styles/separator.css';
</script>

<div class="actions">
	<button type="button" data-slean="button" data-variant="text" data-color="neutral" data-size="small">Undo</button>
	<button type="button" data-slean="button" data-variant="text" data-color="neutral" data-size="small">Redo</button>
	<div role="separator" aria-orientation="vertical" data-slean="separator"></div>
	<button type="button" data-slean="button" data-variant="text" data-color="neutral" data-size="small">Cut</button>
	<button type="button" data-slean="button" data-variant="text" data-color="neutral" data-size="small">Copy</button>
	<button type="button" data-slean="button" data-variant="text" data-color="neutral" data-size="small">Paste</button>
</div>

<style>
	.actions {
		display: flex;
		align-items: center;
		gap: var(--slean-space-1);
	}
</style>
```

In a flex row the vertical separator takes the row’s height.

### Decorative

```svelte
<script lang="ts">
	import '@svelte-lean/styles/separator.css';
</script>

<!-- The names already read as separate items; aria-hidden keeps the line silent. -->
<p class="byline">
	<span>Amara Okafor</span>
	<span role="separator" aria-orientation="vertical" data-slean="separator" aria-hidden="true"></span>
	<span>Design team</span>
	<span role="separator" aria-orientation="vertical" data-slean="separator" aria-hidden="true"></span>
	<span>Istanbul</span>
</p>

<style>
	.byline {
		display: flex;
		align-items: center;
		margin: 0;
		font-size: var(--slean-text-sm);
	}
</style>
```

A line that only repeats a boundary the content already has is hidden from assistive technology.

## Usage

Plain markup with a stylesheet: install `@svelte-lean/styles`. No JavaScript runs.

```bash
npm install @svelte-lean/styles
```

`stylesheets`

```ts
import '@svelte-lean/styles/separator.css';
```

Use `<hr data-slean="separator">` between blocks that change topic, and `<div role="separator" aria-orientation="vertical" data-slean="separator">` between items in a flex row, where the line takes the row’s height; in running text it is one line of text tall.

For a decorative line add `aria-hidden="true"`. On an `<hr>`, `role="none"` has the same effect, but it cannot carry `aria-orientation`, so the vertical form uses `aria-hidden`.

## Anatomy

| Part | Element | data-slean-part | Required | Notes |
| --- | --- | --- | --- | --- |
| root | `<hr data-slean="separator">` | – | yes | Horizontal, between blocks. aria-hidden="true" when decorative. |
| vertical | `<div role="separator" aria-orientation="vertical">` | – | no | Between inline items. The styles read aria-orientation. |

## Keyboard

| Key | When | Result |
| --- | --- | --- |
| `Tab` | anywhere | Passes the separator by: a static separator is not focusable (native) |

## Styling

`separator.css` removes the inset border of `<hr>`, draws a one-pixel border in the border token, sets the spacing, and turns an element with `aria-orientation="vertical"` into a line that fills its flex row. A decorative line can also be a border in the application’s CSS, with no element. The tokens it reads:

| Token | Default (light) | Applies to |
| --- | --- | --- |
| `--slean-space-4` | `calc(var(--slean-spacing) * 4)` | margin-block |
| `--slean-border` | `var(--slean-neutral-6)` | border-block-start, border-inline-start |
| `--slean-space-2` | `calc(var(--slean-spacing) * 2)` | margin-inline |

State selectors the stylesheet targets, all from the platform or ARIA: `[aria-orientation="vertical"]`.

## Under the hood

Runtime and size

- **Tier**: 0 · Native
- **Behavior JS**: 0 B brotli · 0 B gzip · `fixtures/results.json (native-only)` · [method](https://svelte-lean.vercel.app/performance/methodology.md)
- **Platform features**: `<hr>`, `role="separator"`, `aria-orientation`
- **Shared listeners**: none
- **Per-instance listeners**: none
- **Lazy state**: none
- **Native base**: `<hr>, role="separator"`

Tier 0: the separator has no behavior module, the Vite plugin maps `separator` to no module, and the native-only fixture proves the path ships no Svelte Lean JavaScript.

Why this implementation

`<hr>` is a separator for assistive technology and a thematic break in the document; the browser draws it with an inset border that the stylesheet replaces with a one-pixel line. Between inline items there is no element for a vertical line, so the ARIA role takes its place, with the orientation set because the role defaults to horizontal.

The decision that matters is whether a separator is announced at all. Most lines in an interface only draw a boundary that the structure already makes, and hearing “separator” between every pair of links adds nothing. The contract names the two forms and how to silence one.

### The browser owns

- the separator role of \<hr\> and its horizontal orientation
- the vertical orientation announced from aria-orientation
- leaving a decorative separator out of the accessibility tree (aria-hidden)

### Svelte Lean owns

- separator.css: the one-pixel border, the spacing, the vertical line that fills its row
- the contract: when a separator is announced and when it is decorative
- documentation

Accessibility

- `<hr>` is exposed as a horizontal separator; the vertical form as a vertical separator from `aria-orientation`.
- A static separator is not focusable and has no name.
- A decorative separator with `aria-hidden="true"` is not in the accessibility tree.
- A focusable separator with a value is a window splitter, which this primitive is not.

Platform features

| Feature | Baseline | Outside the target |
| --- | --- | --- |
| `<hr>, role="separator", aria-orientation` | Widely available | Not applicable within the support policy |

Without JavaScript

Fully functional.

Server rendering and hydration

Static HTML.

Nothing is attached.

Compatibility

Everything here is widely available.

Tests and source

- `apps/playground/tests/primitives/display.spec.ts` Playwright: open state, exclusive groups, stretched links, roles and names, carousel scrolling, axe
- `packages/styles/tests` static checks of the stylesheet (layers, tokens, specificity, dark parity)

- `packages/primitives/src/separator/contract.md` the contract in prose: semantics, keyboard, focus, ARIA, SSR, JS-disabled behavior
- `packages/primitives/src/separator/contract.ts` typed constants: name, tier, base, parts, options, events
- `packages/styles/css/separator.css` the optional stylesheet
