Components
Separator
A horizontal or vertical line that visually divides content. Two flavours: decorative (purely visual, ignored by assistive tech) and semantic (renders <hr> or role="separator" so AT announces a thematic break).
Installation
1. Install via the shadcn CLI
npx shadcn@latest add http://localhost/r/separator.json2. Use it
import { Separator } from "@/components/ui/separator"
// Decorative horizontal (default)
<Separator />
// Vertical between flex items
<div class="flex h-5 items-center gap-3">
<span>Profile</span>
<Separator orientation="vertical" />
<span>Settings</span>
</div>
// Semantic <hr>
<Separator decorative={false} />Or copy the source manually
/** @jsxImportSource hono/jsx */
import { cn, type ClassValue } from "@/registry/lib/cn"
// Separator — shadcn-htmx, htmx v4 + Tailwind v4.
//
// Source of truth (visual styles):
// repos/shadcn-ui/apps/v4/registry/new-york-v4/ui/separator.tsx
//
// Semantics (spec-first):
// - decorative=true (default): the line is purely visual. We render a
// <div> with no role so assistive tech skips it.
// - decorative=false (semantic): the line marks a thematic break between
// content groups. We render <hr> (implicit
// role="separator") for horizontal, and a div with
// role="separator" + aria-orientation for vertical
// (there's no native vertical hr).
//
// MDN:
// repos/mdn/files/en-us/web/html/reference/elements/hr/index.md
// repos/mdn/files/en-us/web/accessibility/aria/reference/roles/separator_role/
// APG:
// repos/aria-practices/content/patterns/none/ (separator role)
export type SeparatorOrientation = "horizontal" | "vertical"
const base =
"shrink-0 bg-border " +
"data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full " +
"data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px"
export function separatorClasses(opts?: {
orientation?: SeparatorOrientation
class?: ClassValue
}): string {
return cn(base, opts?.class)
}
type SeparatorProps = {
orientation?: SeparatorOrientation
decorative?: boolean
class?: ClassValue
id?: string
}
export function Separator(props: SeparatorProps) {
const { orientation = "horizontal", decorative = true, class: className, id } = props
const classes = separatorClasses({ orientation, class: className })
// Horizontal + semantic → native <hr> (has implicit role="separator" so
// AT announces; we strip the default margin via Tailwind classes).
if (!decorative && orientation === "horizontal") {
return (
<hr
id={id}
data-slot="separator"
data-orientation="horizontal"
// <hr> already carries role=separator. We set aria-orientation
// explicitly to be defensive against older AT that read the
// implicit role but want explicit attribute.
aria-orientation="horizontal"
class={cn(classes, "border-0")}
/>
)
}
return (
<div
id={id}
data-slot="separator"
data-orientation={orientation}
// decorative: drop the implicit role. Browser default for <div> is
// generic. Setting role="none" is redundant but signals intent.
role={decorative ? undefined : "separator"}
aria-orientation={!decorative ? orientation : undefined}
class={classes}
/>
)
}
1. Save the file
Copy separator.html into templates/components/.
2. Use it
{% from "components/separator.html" import separator %}
{{ separator() }} {# decorative horizontal #}
{{ separator(orientation="vertical") }} {# decorative vertical #}
{{ separator(decorative=false) }} {# semantic <hr> #}View source
{# Separator macro — shadcn-htmx, htmx v4 + Tailwind v4.
Mirrors registry/ui/separator.tsx.
Usage:
{% from "components/separator.html" import separator %}
{{ separator() }} {# decorative horizontal #}
{{ separator(orientation="vertical") }} {# decorative vertical #}
{{ separator(decorative=false) }} {# semantic <hr> #} #}
{% macro separator(
orientation="horizontal",
decorative=true,
id=none,
extra_class=""
) %}
{%- set base -%}
shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px
{%- endset -%}
{%- if not decorative and orientation == "horizontal" -%}
<hr
{%- if id %} id="{{ id }}"{% endif %}
data-slot="separator"
data-orientation="horizontal"
aria-orientation="horizontal"
class="{{ base }} border-0 {{ extra_class }}">
{%- else -%}
<div
{%- if id %} id="{{ id }}"{% endif %}
data-slot="separator"
data-orientation="{{ orientation }}"
{%- if not decorative %} role="separator" aria-orientation="{{ orientation }}"{% endif %}
class="{{ base }} {{ extra_class }}"></div>
{%- endif -%}
{% endmacro %}
1. Save the file
Add separator.tmpl alongside button.tmpl.
2. Use it
{{template "separator" (dict)}} // decorative horizontal
{{template "separator" (dict "Orientation" "vertical")}} // decorative vertical
{{template "separator" (dict "Decorative" (ptr false))}} // semantic <hr>View source
{{/*
Separator template — shadcn-htmx, htmx v4 + Tailwind v4.
Mirrors registry/ui/separator.tsx.
type SeparatorArgs struct {
Orientation string // "horizontal" (default) | "vertical"
Decorative *bool // nil/true = visual only; false = semantic <hr> / role=separator
ID string
}
*/}}
{{define "separator"}}
{{- $orientation := or .Orientation "horizontal" -}}
{{- $decorative := true -}}{{- if .Decorative}}{{- $decorative = deref .Decorative}}{{end -}}
{{- $base := "shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px" -}}
{{- if and (not $decorative) (eq $orientation "horizontal") -}}
<hr {{if .ID}}id="{{.ID}}" {{end}}data-slot="separator" data-orientation="horizontal" aria-orientation="horizontal" class="{{$base}} border-0">
{{- else -}}
<div {{if .ID}}id="{{.ID}}" {{end}}data-slot="separator" data-orientation="{{$orientation}}"{{if not $decorative}} role="separator" aria-orientation="{{$orientation}}"{{end}} class="{{$base}}"></div>
{{- end -}}
{{end}}
1. Save the file
Drop separator.ex into lib/my_app_web/components/.
2. Use it
<.separator />
<.separator orientation="vertical" />
<.separator decorative={false} />View source
defmodule ShadcnHtmx.Components.Separator do
@moduledoc """
Separator — shadcn-htmx, htmx v4 + Tailwind v4 for Phoenix.
Mirrors registry/ui/separator.tsx.
Semantics:
- `decorative: true` (default) → purely visual; renders as a styled <div>
with no role so AT skips it.
- `decorative: false` (semantic) → marks a thematic break. Horizontal
uses <hr> (implicit role="separator"); vertical uses
<div role="separator" aria-orientation="vertical"> (no native equivalent).
## Examples
<.separator /> # decorative horizontal
<.separator orientation="vertical" /> # decorative vertical
<.separator decorative={false} /> # semantic <hr>
"""
use Phoenix.Component
@base "shrink-0 bg-border " <>
"data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full " <>
"data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px"
attr :orientation, :string, default: "horizontal", values: ~w(horizontal vertical)
attr :decorative, :boolean, default: true
attr :class, :string, default: nil
attr :rest, :global
def separator(assigns) do
assigns = assign(assigns, :base, @base)
cond do
not assigns.decorative and assigns.orientation == "horizontal" ->
~H"""
<hr
data-slot="separator"
data-orientation="horizontal"
aria-orientation="horizontal"
class={[@base, "border-0", @class]}
{@rest}
/>
"""
true ->
~H"""
<div
data-slot="separator"
data-orientation={@orientation}
role={if !@decorative, do: "separator"}
aria-orientation={if !@decorative, do: @orientation}
class={[@base, @class]}
{@rest}
/>
"""
end
end
end
1. Save the file
Tailwind utilities only; no script.
2. Use it
<!-- Decorative horizontal -->
<div data-slot="separator" data-orientation="horizontal"
class="shrink-0 bg-border h-px w-full"></div>
<!-- Semantic horizontal -->
<hr class="shrink-0 border-0 bg-border h-px w-full">View source
<!--
shadcn-htmx — raw HTML separator snippets.
Two semantic modes:
1. Decorative (default) — purely visual line. Use <div> with no role
so assistive tech skips it.
2. Semantic — marks a thematic break. <hr> for horizontal, role-only
div for vertical.
BASE:
shrink-0 bg-border
data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full
data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px
-->
<!-- Decorative horizontal -->
<div data-slot="separator" data-orientation="horizontal"
class="shrink-0 bg-border h-px w-full"></div>
<!-- Decorative vertical (works inside a flex/grid container with height) -->
<div data-slot="separator" data-orientation="vertical"
class="shrink-0 bg-border h-full w-px"></div>
<!-- Semantic horizontal — <hr> has implicit role="separator" -->
<hr data-slot="separator" data-orientation="horizontal" aria-orientation="horizontal"
class="shrink-0 border-0 bg-border h-px w-full">
<!-- Semantic vertical — no native vertical hr; explicit role + orientation -->
<div data-slot="separator" data-orientation="vertical"
role="separator" aria-orientation="vertical"
class="shrink-0 bg-border h-full w-px"></div>
Examples
Decorative — visual divider that AT skips
Default mode. Renders as a styled <div> with no role so screen readers don't announce it.
Use decorative for pure layout — the line between two paragraphs in a card, the row separators in a sidebar. The visual carries the meaning; the DOM stays semantically silent so AT users aren't interrupted with "separator, separator, separator" as they scan.
Above the separator.
Below the separator.
<p>Above the separator.</p>
<Separator />
<p>Below the separator.</p><p>Above the separator.</p>
{{ separator() }}
<p>Below the separator.</p><p>Above the separator.</p>
{{template "separator" (dict)}}
<p>Below the separator.</p><p>Above the separator.</p>
<.separator />
<p>Below the separator.</p><div class="w-full max-w-md space-y-3 text-sm">
<p>Above the separator.</p>
<div data-slot="separator" data-orientation="horizontal" class="shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px">
</div>
<p>Below the separator.</p>
</div>Further reading
Semantic — thematic break
Use when the line marks a genuine section change (end of a chapter, new topic). Renders as <hr>, which AT announces.
HTML's <hr> is "a paragraph-level thematic break"; it has implicit role="separator" and screen readers announce "separator" or "horizontal rule". Reach for decorative={false} when the line carries meaning (between two chapters, between the body and footer of a long article).
Chapter 1 concludes.
Chapter 2 begins.
<p>Chapter 1 concludes.</p>
<Separator decorative={false} />
<p>Chapter 2 begins.</p><p>Chapter 1 concludes.</p>
{{ separator(decorative=false) }}
<p>Chapter 2 begins.</p><p>Chapter 1 concludes.</p>
{{template "separator" (dict "Decorative" (ptr false))}}
<p>Chapter 2 begins.</p><p>Chapter 1 concludes.</p>
<.separator decorative={false} />
<p>Chapter 2 begins.</p><div class="w-full max-w-md space-y-3 text-sm">
<p>Chapter 1 concludes.</p>
<hr data-slot="separator" data-orientation="horizontal" aria-orientation="horizontal" class="shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px border-0"/>
<p>Chapter 2 begins.</p>
</div>Further reading
Vertical — inside a flex row
Set orientation="vertical". The parent needs a defined height (flex with items-center, or explicit h-*).
Vertical separators don't have a native HTML equivalent. When decorative we render a div; when semantic we add role="separator" + aria-orientation="vertical". APG notes that vertical orientation must be set explicitly — assistive tech can't infer it from CSS.
<div class="flex h-6 items-center gap-3 text-sm">
<span>Profile</span>
<Separator orientation="vertical" />
<span>Settings</span>
<Separator orientation="vertical" />
<span>Log out</span>
</div><div class="flex h-6 items-center gap-3 text-sm">
<span>Profile</span> {{ separator(orientation="vertical") }}
<span>Settings</span> {{ separator(orientation="vertical") }}
<span>Log out</span>
</div><div class="flex h-6 items-center gap-3 text-sm">
<span>Profile</span> {{template "separator" (dict "Orientation" "vertical")}}
<span>Settings</span> {{template "separator" (dict "Orientation" "vertical")}}
<span>Log out</span>
</div><div class="flex h-6 items-center gap-3 text-sm">
<span>Profile</span> <.separator orientation="vertical" />
<span>Settings</span> <.separator orientation="vertical" />
<span>Log out</span>
</div><div class="flex h-6 items-center gap-3 text-sm">
<span>Profile</span>
<div data-slot="separator" data-orientation="vertical" class="shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px">
</div>
<span>Settings</span>
<div data-slot="separator" data-orientation="vertical" class="shrink-0 bg-border data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px">
</div>
<span>Log out</span>
</div>Further reading
API Reference
<Separator>
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | "horizontal"|"vertical" | "horizontal" | Direction. Vertical needs a parent with defined height. |
decorative | boolean | true | Decorative — div with no role (AT skips). Semantic — <hr> for horizontal, div+role for vertical.MDNrole="separator" |
class | string | — | Extra Tailwind classes appended to the root element. |