{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "landmarks",
  "type": "registry:ui",
  "title": "Landmarks",
  "description": "An accessible page-shell from the native HTML landmark elements (header, nav, search, main, aside, section, footer). Thin semantic wrappers that expose the matching ARIA landmark role, with aria-label/aria-labelledby where the APG Landmarks practice requires a unique name. Zero JS.",
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/landmarks.tsx",
      "type": "registry:ui",
      "target": "components/ui/landmarks.tsx",
      "content": "/** @jsxImportSource hono/jsx */\nimport type { PropsWithChildren, Child } from \"hono/jsx\"\nimport { cn, type ClassValue } from \"@/registry/lib/cn\"\n\n// Landmarks — shadcn-htmx, htmx v4 + Tailwind v4.\n//\n// An accessible page-shell built from the native HTML landmark elements.\n// Pure structure — no JS. Each subcomponent is a thin semantic wrapper\n// around the native element that implicitly exposes the matching ARIA\n// landmark role, so assistive-tech users can jump between the major\n// regions of the page.\n//\n// Native element  →  implicit landmark role\n//   <header>  (in body context)  →  banner\n//   <nav>                          →  navigation\n//   <search>                       →  search\n//   <main>                         →  main\n//   <aside>                        →  complementary\n//   <section>  (when labelled)     →  region\n//   <form>     (when labelled)      →  form\n//   <footer>  (in body context)    →  contentinfo\n//\n// Labelling rules (when to pass an aria-label / aria-labelledby) come\n// straight from the APG. Several landmark roles must (region) or should\n// (navigation, complementary, search) carry a unique label so multiple\n// landmarks of the same type are distinguishable.\n//\n// APG (read for the per-role design patterns + labelling rules):\n//   repos/aria-practices/content/patterns/landmarks/examples/banner.html\n//   repos/aria-practices/content/patterns/landmarks/examples/navigation.html\n//   repos/aria-practices/content/patterns/landmarks/examples/main.html\n//   repos/aria-practices/content/patterns/landmarks/examples/complementary.html\n//   repos/aria-practices/content/patterns/landmarks/examples/region.html\n//   repos/aria-practices/content/patterns/landmarks/examples/search.html\n//   repos/aria-practices/content/patterns/landmarks/examples/contentinfo.html\n// MDN (the native <search> element defines a search landmark — no role=search needed):\n//   repos/mdn/files/en-us/web/html/reference/elements/search/index.md:20-22\n\ntype LandmarkProps = PropsWithChildren<\n  {\n    class?: ClassValue\n    id?: string\n    // The accessible name for this landmark. Where the APG calls for one\n    // (navigation, complementary, search when there is more than one; and\n    // always for region) pass either ariaLabel or ariaLabelledby.\n    ariaLabel?: string\n    ariaLabelledby?: string\n  } & Record<string, any>\n>\n\n// <header> in body context = banner landmark. Top-level only: a <header>\n// nested inside article/aside/main/nav/section is just a sectioning header,\n// not a banner. There should be one banner per page.\n//   APG: repos/aria-practices/content/patterns/landmarks/examples/banner.html:52-57,67-76\nexport function Banner(props: LandmarkProps) {\n  const { class: className, children, ariaLabel, ariaLabelledby, ...rest } = props\n  return (\n    <header\n      data-slot=\"landmark-banner\"\n      class={cn(\"border-b bg-card px-4 py-3 text-card-foreground\", className)}\n      aria-label={ariaLabel}\n      aria-labelledby={ariaLabelledby}\n      {...rest}\n    >\n      {children}\n    </header>\n  )\n}\n\n// <nav> = navigation landmark. If a page has more than one navigation\n// landmark, each should have a unique label; with only one, a label is\n// optional (but recommended once there are multiple navs in a shell).\n//   APG: repos/aria-practices/content/patterns/landmarks/examples/navigation.html:51-52,61\nexport function NavLandmark(props: LandmarkProps) {\n  const { class: className, children, ariaLabel, ariaLabelledby, ...rest } = props\n  return (\n    <nav\n      data-slot=\"landmark-navigation\"\n      class={cn(\"text-sm\", className)}\n      aria-label={ariaLabel}\n      aria-labelledby={ariaLabelledby}\n      {...rest}\n    >\n      {children}\n    </nav>\n  )\n}\n\n// <search> = search landmark (native element). Removes the need for\n// role=\"search\" on the inner <form>. Use the search landmark instead of\n// the form landmark when the form performs a search/filter. If there is\n// more than one search landmark, each should have a unique label.\n//   MDN: repos/mdn/files/en-us/web/html/reference/elements/search/index.md:20-22,32-43\n//   APG: repos/aria-practices/content/patterns/landmarks/examples/search.html:54-55,64-66\nexport function SearchLandmark(props: LandmarkProps) {\n  const { class: className, children, ariaLabel, ariaLabelledby, ...rest } = props\n  return (\n    <search\n      data-slot=\"landmark-search\"\n      class={cn(\"\", className)}\n      aria-label={ariaLabel}\n      aria-labelledby={ariaLabelledby}\n      {...rest}\n    >\n      {children}\n    </search>\n  )\n}\n\n// <main> = main landmark. Exactly one per page, and it should be a\n// top-level landmark (not nested in another landmark).\n//   APG: repos/aria-practices/content/patterns/landmarks/examples/main.html:51-52\nexport function MainLandmark(props: LandmarkProps) {\n  const { class: className, children, ariaLabel, ariaLabelledby, ...rest } = props\n  return (\n    <main\n      data-slot=\"landmark-main\"\n      class={cn(\"min-w-0 flex-1\", className)}\n      aria-label={ariaLabel}\n      aria-labelledby={ariaLabelledby}\n      {...rest}\n    >\n      {children}\n    </main>\n  )\n}\n\n// <aside> = complementary landmark. Supporting content at a similar level\n// to the main content; should be a top-level landmark. If the content\n// isn't related to the main content, use a region instead. With more than\n// one complementary landmark, each should have a unique label.\n//   APG: repos/aria-practices/content/patterns/landmarks/examples/complementary.html:52-54\nexport function Complementary(props: LandmarkProps) {\n  const { class: className, children, ariaLabel, ariaLabelledby, ...rest } = props\n  return (\n    <aside\n      data-slot=\"landmark-complementary\"\n      class={cn(\n        \"rounded-lg border bg-card p-4 text-sm text-card-foreground\",\n        className,\n      )}\n      aria-label={ariaLabel}\n      aria-labelledby={ariaLabelledby}\n      {...rest}\n    >\n      {children}\n    </aside>\n  )\n}\n\n// <section> = region landmark, but ONLY when it has an accessible name.\n// A bare <section> exposes NO landmark role; a region landmark MUST have a\n// label — so always pass ariaLabel or ariaLabelledby here. Used to name\n// content that no other (named) landmark appropriately describes.\n//   APG: repos/aria-practices/content/patterns/landmarks/examples/region.html:52-54,64,101-121\nexport function RegionLandmark(props: LandmarkProps) {\n  const { class: className, children, ariaLabel, ariaLabelledby, ...rest } = props\n  return (\n    <section\n      data-slot=\"landmark-region\"\n      class={cn(\"rounded-lg border bg-card p-4 text-card-foreground\", className)}\n      aria-label={ariaLabel}\n      aria-labelledby={ariaLabelledby}\n      {...rest}\n    >\n      {children}\n    </section>\n  )\n}\n\n// <form> = form landmark, but ONLY when it has an accessible name. A bare\n// <form> exposes NO landmark role; the form landmark appears once you pass\n// ariaLabel or ariaLabelledby — exactly like region. Give each form\n// landmark a unique label. Use SearchLandmark (<search>) instead when the\n// form performs a search. The id/...rest passthrough means action/method/\n// hx-* forward straight through to the <form>.\n//   MDN: repos/mdn/files/en-us/web/accessibility/aria/reference/roles/form_role/index.md:12,31,33,101\n//   APG: repos/aria-practices/content/patterns/landmarks/examples/form.html:79,84,85,86\nexport function FormLandmark(props: LandmarkProps) {\n  const { class: className, children, ariaLabel, ariaLabelledby, ...rest } = props\n  return (\n    <form\n      data-slot=\"landmark-form\"\n      class={cn(\"rounded-lg border bg-card p-4 text-card-foreground\", className)}\n      aria-label={ariaLabel}\n      aria-labelledby={ariaLabelledby}\n      {...rest}\n    >\n      {children}\n    </form>\n  )\n}\n\n// <footer> in body context = contentinfo landmark. Top-level only: a\n// <footer> nested inside article/aside/main/nav/section is just a\n// sectioning footer. One contentinfo per page.\n//   APG: repos/aria-practices/content/patterns/landmarks/examples/contentinfo.html:51-56,66-74\nexport function ContentInfo(props: LandmarkProps) {\n  const { class: className, children, ariaLabel, ariaLabelledby, ...rest } = props\n  return (\n    <footer\n      data-slot=\"landmark-contentinfo\"\n      class={cn(\n        \"border-t bg-card px-4 py-3 text-sm text-muted-foreground\",\n        className,\n      )}\n      aria-label={ariaLabel}\n      aria-labelledby={ariaLabelledby}\n      {...rest}\n    >\n      {children}\n    </footer>\n  )\n}\n\n// Convenience shell — a tasteful demo skeleton wiring the landmarks into a\n// holy-grail layout (banner on top, a row of [navigation | main |\n// complementary], contentinfo at the bottom). Compose the subcomponents\n// directly when you need a different arrangement.\nexport function PageShell(\n  props: PropsWithChildren<{ class?: ClassValue; children?: Child }>,\n) {\n  return (\n    <div\n      data-slot=\"landmark-shell\"\n      class={cn(\"flex min-h-0 flex-col overflow-hidden rounded-lg border\", props.class)}\n    >\n      {props.children}\n    </div>\n  )\n}\n"
    },
    {
      "path": "registry/jinja2/landmarks.html",
      "type": "registry:file",
      "target": "templates/components/landmarks.html",
      "content": "{# Landmarks macros — shadcn-htmx, htmx v4 + Tailwind v4.\n   Mirrors registry/ui/landmarks.tsx.\n\n   An accessible page-shell built from the native HTML landmark elements.\n   Each native element exposes the matching ARIA landmark role implicitly:\n\n     <header>  →  banner          <aside>    →  complementary\n     <nav>     →  navigation      <section>  →  region (only when labelled)\n     <search>  →  search          <form>     →  form   (only when labelled)\n     <main>    →  main            <footer>   →  contentinfo\n\n   Labelling rules from the APG (pass aria_label / aria_labelledby where the\n   practice calls for one — always for region; once there is more than one\n   navigation/complementary/search landmark each needs a unique label).\n     repos/aria-practices/content/patterns/landmarks/examples/*.html\n   The native <search> element defines a search landmark (no role=search):\n     repos/mdn/files/en-us/web/html/reference/elements/search/index.md:20-22\n\n   Usage:\n     {% from \"components/landmarks.html\" import\n        banner_open, banner_close, nav_open, nav_close,\n        search_open, search_close, main_open, main_close,\n        complementary_open, complementary_close,\n        region_open, region_close, form_open, form_close,\n        contentinfo_open, contentinfo_close %}\n\n     {{ banner_open() }} … {{ banner_close() }}\n     {{ nav_open(aria_label=\"Primary\") }} … {{ nav_close() }}\n     {{ search_open(aria_label=\"Site\") }}<form …>…</form>{{ search_close() }}\n     {{ main_open() }} … {{ main_close() }}\n     {{ complementary_open(aria_label=\"Related\") }} … {{ complementary_close() }}\n     {{ region_open(aria_label=\"Stats\") }} … {{ region_close() }}\n     {{ form_open(aria_label=\"Contact\") }}<form-controls…>{{ form_close() }}\n     {{ contentinfo_open() }} … {{ contentinfo_close() }}\n#}\n\n{% macro _attrs(extra) -%}\n{%- for k, v in extra.items() %} {{ k|replace('_', '-') }}=\"{{ v }}\"{% endfor -%}\n{%- endmacro %}\n\n{% macro _label(aria_label, aria_labelledby) -%}\n{%- if aria_label %} aria-label=\"{{ aria_label }}\"{% endif -%}\n{%- if aria_labelledby %} aria-labelledby=\"{{ aria_labelledby }}\"{% endif -%}\n{%- endmacro %}\n\n{# banner — <header> in body context. Top-level only; one per page. #}\n{% macro banner_open(aria_label=none, aria_labelledby=none, extra_class=\"\", **attrs) -%}\n<header data-slot=\"landmark-banner\"{{ _label(aria_label, aria_labelledby) }} class=\"border-b bg-card px-4 py-3 text-card-foreground {{ extra_class }}\"{{ _attrs(attrs) }}>\n{%- endmacro %}\n{% macro banner_close() %}</header>{% endmacro %}\n\n{# navigation — <nav>. Each nav should have a unique label when >1 on a page. #}\n{% macro nav_open(aria_label=none, aria_labelledby=none, extra_class=\"\", **attrs) -%}\n<nav data-slot=\"landmark-navigation\"{{ _label(aria_label, aria_labelledby) }} class=\"text-sm {{ extra_class }}\"{{ _attrs(attrs) }}>\n{%- endmacro %}\n{% macro nav_close() %}</nav>{% endmacro %}\n\n{# search — native <search> element. Wrap a <form>. Defines a search landmark\n   so no role=search on the form is needed. #}\n{% macro search_open(aria_label=none, aria_labelledby=none, extra_class=\"\", **attrs) -%}\n<search data-slot=\"landmark-search\"{{ _label(aria_label, aria_labelledby) }} class=\"{{ extra_class }}\"{{ _attrs(attrs) }}>\n{%- endmacro %}\n{% macro search_close() %}</search>{% endmacro %}\n\n{# main — <main>. Exactly one per page; top-level. #}\n{% macro main_open(aria_label=none, aria_labelledby=none, extra_class=\"\", **attrs) -%}\n<main data-slot=\"landmark-main\"{{ _label(aria_label, aria_labelledby) }} class=\"min-w-0 flex-1 {{ extra_class }}\"{{ _attrs(attrs) }}>\n{%- endmacro %}\n{% macro main_close() %}</main>{% endmacro %}\n\n{# complementary — <aside>. Top-level; unique label when >1. #}\n{% macro complementary_open(aria_label=none, aria_labelledby=none, extra_class=\"\", **attrs) -%}\n<aside data-slot=\"landmark-complementary\"{{ _label(aria_label, aria_labelledby) }} class=\"rounded-lg border bg-card p-4 text-sm text-card-foreground {{ extra_class }}\"{{ _attrs(attrs) }}>\n{%- endmacro %}\n{% macro complementary_close() %}</aside>{% endmacro %}\n\n{# region — <section> WITH a label (a region landmark must be named). #}\n{% macro region_open(aria_label=none, aria_labelledby=none, extra_class=\"\", **attrs) -%}\n<section data-slot=\"landmark-region\"{{ _label(aria_label, aria_labelledby) }} class=\"rounded-lg border bg-card p-4 text-card-foreground {{ extra_class }}\"{{ _attrs(attrs) }}>\n{%- endmacro %}\n{% macro region_close() %}</section>{% endmacro %}\n\n{# form — <form> WITH a label (a form landmark must be named, like region).\n   Use search_open instead when the form performs a search. action/method/\n   hx-* pass straight through via **attrs.\n   MDN repos/mdn/files/en-us/web/accessibility/aria/reference/roles/form_role/index.md:12,31,33,101 #}\n{% macro form_open(aria_label=none, aria_labelledby=none, extra_class=\"\", **attrs) -%}\n<form data-slot=\"landmark-form\"{{ _label(aria_label, aria_labelledby) }} class=\"rounded-lg border bg-card p-4 text-card-foreground {{ extra_class }}\"{{ _attrs(attrs) }}>\n{%- endmacro %}\n{% macro form_close() %}</form>{% endmacro %}\n\n{# contentinfo — <footer> in body context. Top-level only; one per page. #}\n{% macro contentinfo_open(aria_label=none, aria_labelledby=none, extra_class=\"\", **attrs) -%}\n<footer data-slot=\"landmark-contentinfo\"{{ _label(aria_label, aria_labelledby) }} class=\"border-t bg-card px-4 py-3 text-sm text-muted-foreground {{ extra_class }}\"{{ _attrs(attrs) }}>\n{%- endmacro %}\n{% macro contentinfo_close() %}</footer>{% endmacro %}\n"
    },
    {
      "path": "registry/go-templates/landmarks.tmpl",
      "type": "registry:file",
      "target": "components/landmarks.tmpl",
      "content": "{{/*\n  Landmarks templates — shadcn-htmx, htmx v4 + Tailwind v4.\n  Mirrors registry/ui/landmarks.tsx.\n\n  An accessible page-shell built from the native HTML landmark elements.\n  Each native element exposes the matching ARIA landmark role implicitly:\n\n    <header>  -> banner          <aside>    -> complementary\n    <nav>     -> navigation       <section>  -> region (only when labelled)\n    <search>  -> search           <form>     -> form   (only when labelled)\n    <main>    -> main             <footer>   -> contentinfo\n\n  Labelling rules from the APG (pass AriaLabel / AriaLabelledby where the\n  practice calls for one — always for region; once there is more than one\n  navigation/complementary/search landmark, each needs a unique label).\n    repos/aria-practices/content/patterns/landmarks/examples/*.html\n  The native <search> element defines a search landmark (no role=search):\n    repos/mdn/files/en-us/web/html/reference/elements/search/index.md:20-22\n\n  Each named template takes a (dict ...) and renders one landmark. Pass the\n  inner markup as a pre-rendered template.HTML via (htmlSafe ...):\n\n      {{template \"landmark_main\" (dict\n        \"Body\" (htmlSafe `<h1>Dashboard</h1>…`)\n      )}}\n\n      {{template \"landmark_nav\" (dict\n        \"AriaLabel\" \"Primary\"\n        \"Body\" (htmlSafe `<ul>…</ul>`)\n      )}}\n*/}}\n\n{{/* banner — <header> in body context. Top-level only; one per page. */}}\n{{define \"landmark_banner\"}}\n<header data-slot=\"landmark-banner\"\n  {{- if .AriaLabel}} aria-label=\"{{.AriaLabel}}\"{{end}}\n  {{- if .AriaLabelledby}} aria-labelledby=\"{{.AriaLabelledby}}\"{{end}}\n  class=\"border-b bg-card px-4 py-3 text-card-foreground{{with .Class}} {{.}}{{end}}\">{{.Body}}</header>\n{{end}}\n\n{{/* navigation — <nav>. Each nav should have a unique label when >1 on a page. */}}\n{{define \"landmark_nav\"}}\n<nav data-slot=\"landmark-navigation\"\n  {{- if .AriaLabel}} aria-label=\"{{.AriaLabel}}\"{{end}}\n  {{- if .AriaLabelledby}} aria-labelledby=\"{{.AriaLabelledby}}\"{{end}}\n  class=\"text-sm{{with .Class}} {{.}}{{end}}\">{{.Body}}</nav>\n{{end}}\n\n{{/* search — native <search> element. Wrap a <form>; defines a search\n     landmark so no role=search on the form is needed. */}}\n{{define \"landmark_search\"}}\n<search data-slot=\"landmark-search\"\n  {{- if .AriaLabel}} aria-label=\"{{.AriaLabel}}\"{{end}}\n  {{- if .AriaLabelledby}} aria-labelledby=\"{{.AriaLabelledby}}\"{{end}}\n  class=\"{{with .Class}}{{.}}{{end}}\">{{.Body}}</search>\n{{end}}\n\n{{/* main — <main>. Exactly one per page; top-level. */}}\n{{define \"landmark_main\"}}\n<main data-slot=\"landmark-main\"\n  {{- if .AriaLabel}} aria-label=\"{{.AriaLabel}}\"{{end}}\n  {{- if .AriaLabelledby}} aria-labelledby=\"{{.AriaLabelledby}}\"{{end}}\n  class=\"min-w-0 flex-1{{with .Class}} {{.}}{{end}}\">{{.Body}}</main>\n{{end}}\n\n{{/* complementary — <aside>. Top-level; unique label when >1. */}}\n{{define \"landmark_complementary\"}}\n<aside data-slot=\"landmark-complementary\"\n  {{- if .AriaLabel}} aria-label=\"{{.AriaLabel}}\"{{end}}\n  {{- if .AriaLabelledby}} aria-labelledby=\"{{.AriaLabelledby}}\"{{end}}\n  class=\"rounded-lg border bg-card p-4 text-sm text-card-foreground{{with .Class}} {{.}}{{end}}\">{{.Body}}</aside>\n{{end}}\n\n{{/* region — <section> WITH a label (a region landmark must be named). */}}\n{{define \"landmark_region\"}}\n<section data-slot=\"landmark-region\"\n  {{- if .AriaLabel}} aria-label=\"{{.AriaLabel}}\"{{end}}\n  {{- if .AriaLabelledby}} aria-labelledby=\"{{.AriaLabelledby}}\"{{end}}\n  class=\"rounded-lg border bg-card p-4 text-card-foreground{{with .Class}} {{.}}{{end}}\">{{.Body}}</section>\n{{end}}\n\n{{/* form — <form> WITH a label (a form landmark must be named, like region).\n     Use landmark_search instead when the form performs a search.\n     MDN repos/mdn/files/en-us/web/accessibility/aria/reference/roles/form_role/index.md:12,31,33,101 */}}\n{{define \"landmark_form\"}}\n<form data-slot=\"landmark-form\"\n  {{- if .AriaLabel}} aria-label=\"{{.AriaLabel}}\"{{end}}\n  {{- if .AriaLabelledby}} aria-labelledby=\"{{.AriaLabelledby}}\"{{end}}\n  class=\"rounded-lg border bg-card p-4 text-card-foreground{{with .Class}} {{.}}{{end}}\">{{.Body}}</form>\n{{end}}\n\n{{/* contentinfo — <footer> in body context. Top-level only; one per page. */}}\n{{define \"landmark_contentinfo\"}}\n<footer data-slot=\"landmark-contentinfo\"\n  {{- if .AriaLabel}} aria-label=\"{{.AriaLabel}}\"{{end}}\n  {{- if .AriaLabelledby}} aria-labelledby=\"{{.AriaLabelledby}}\"{{end}}\n  class=\"border-t bg-card px-4 py-3 text-sm text-muted-foreground{{with .Class}} {{.}}{{end}}\">{{.Body}}</footer>\n{{end}}\n"
    },
    {
      "path": "registry/phoenix/landmarks.ex",
      "type": "registry:file",
      "target": "lib/my_app_web/components/landmarks.ex",
      "content": "defmodule ShadcnHtmx.Components.Landmarks do\n  @moduledoc \"\"\"\n  Landmarks — shadcn-htmx, htmx v4 + Tailwind v4 for Phoenix.\n\n  Mirrors registry/ui/landmarks.tsx. An accessible page-shell built from the\n  native HTML landmark elements. Each native element exposes the matching\n  ARIA landmark role implicitly:\n\n      <header>  -> banner          <aside>    -> complementary\n      <nav>     -> navigation       <section>  -> region (only when labelled)\n      <search>  -> search           <form>     -> form   (only when labelled)\n      <main>    -> main             <footer>   -> contentinfo\n\n  Labelling rules from the APG (pass `aria-label` / `aria-labelledby` where the\n  practice calls for one — always for region; once there is more than one\n  navigation/complementary/search landmark, each needs a unique label).\n    repos/aria-practices/content/patterns/landmarks/examples/*.html\n  The native `<search>` element defines a search landmark (no `role=search`):\n    repos/mdn/files/en-us/web/html/reference/elements/search/index.md:20-22\n\n  ## Examples\n\n      <.banner>\n        <h1 class=\"font-semibold\">Acme Console</h1>\n      </.banner>\n\n      <.nav_landmark aria-label=\"Primary\">\n        <ul>…</ul>\n      </.nav_landmark>\n\n      <.search_landmark aria-label=\"Site\">\n        <form action=\"/search\">\n          <label for=\"q\">Search</label>\n          <input id=\"q\" type=\"search\" name=\"q\" />\n        </form>\n      </.search_landmark>\n\n      <.main_landmark>\n        <h1>Dashboard</h1>\n      </.main_landmark>\n\n      <.complementary aria-label=\"Related\">…</.complementary>\n      <.region_landmark aria-label=\"Usage this month\">…</.region_landmark>\n\n      <.form_landmark aria-label=\"Contact\">\n        <form action=\"/contact\" method=\"post\">…</form>\n      </.form_landmark>\n\n      <.content_info>© 2026 Acme</.content_info>\n  \"\"\"\n\n  use Phoenix.Component\n\n  # banner — <header> in body context. Top-level only; one per page.\n  attr :class, :string, default: nil\n  attr :rest, :global, include: ~w(aria-label aria-labelledby)\n  slot :inner_block, required: true\n\n  def banner(assigns) do\n    ~H\"\"\"\n    <header\n      data-slot=\"landmark-banner\"\n      class={[\"border-b bg-card px-4 py-3 text-card-foreground\", @class]}\n      {@rest}\n    >\n      {render_slot(@inner_block)}\n    </header>\n    \"\"\"\n  end\n\n  # navigation — <nav>. Each nav should have a unique label when >1 on a page.\n  attr :class, :string, default: nil\n  attr :rest, :global, include: ~w(aria-label aria-labelledby)\n  slot :inner_block, required: true\n\n  def nav_landmark(assigns) do\n    ~H\"\"\"\n    <nav data-slot=\"landmark-navigation\" class={[\"text-sm\", @class]} {@rest}>\n      {render_slot(@inner_block)}\n    </nav>\n    \"\"\"\n  end\n\n  # search — native <search> element. Wrap a <form>; defines a search landmark\n  # so no role=search on the form is needed.\n  attr :class, :string, default: nil\n  attr :rest, :global, include: ~w(aria-label aria-labelledby)\n  slot :inner_block, required: true\n\n  def search_landmark(assigns) do\n    ~H\"\"\"\n    <search data-slot=\"landmark-search\" class={[@class]} {@rest}>\n      {render_slot(@inner_block)}\n    </search>\n    \"\"\"\n  end\n\n  # main — <main>. Exactly one per page; top-level.\n  attr :class, :string, default: nil\n  attr :rest, :global, include: ~w(aria-label aria-labelledby)\n  slot :inner_block, required: true\n\n  def main_landmark(assigns) do\n    ~H\"\"\"\n    <main data-slot=\"landmark-main\" class={[\"min-w-0 flex-1\", @class]} {@rest}>\n      {render_slot(@inner_block)}\n    </main>\n    \"\"\"\n  end\n\n  # complementary — <aside>. Top-level; unique label when >1.\n  attr :class, :string, default: nil\n  attr :rest, :global, include: ~w(aria-label aria-labelledby)\n  slot :inner_block, required: true\n\n  def complementary(assigns) do\n    ~H\"\"\"\n    <aside\n      data-slot=\"landmark-complementary\"\n      class={[\"rounded-lg border bg-card p-4 text-sm text-card-foreground\", @class]}\n      {@rest}\n    >\n      {render_slot(@inner_block)}\n    </aside>\n    \"\"\"\n  end\n\n  # region — <section> WITH a label (a region landmark must be named).\n  attr :class, :string, default: nil\n  attr :rest, :global, include: ~w(aria-label aria-labelledby)\n  slot :inner_block, required: true\n\n  def region_landmark(assigns) do\n    ~H\"\"\"\n    <section\n      data-slot=\"landmark-region\"\n      class={[\"rounded-lg border bg-card p-4 text-card-foreground\", @class]}\n      {@rest}\n    >\n      {render_slot(@inner_block)}\n    </section>\n    \"\"\"\n  end\n\n  # form — <form> WITH a label (a form landmark must be named, like region).\n  # Use search_landmark instead when the form performs a search. action/method\n  # and hx-* forward through :global onto the native <form>.\n  #   MDN repos/mdn/files/en-us/web/accessibility/aria/reference/roles/form_role/index.md:12,31,33,101\n  attr :class, :string, default: nil\n  attr :rest, :global, include: ~w(aria-label aria-labelledby action method)\n  slot :inner_block, required: true\n\n  def form_landmark(assigns) do\n    ~H\"\"\"\n    <form\n      data-slot=\"landmark-form\"\n      class={[\"rounded-lg border bg-card p-4 text-card-foreground\", @class]}\n      {@rest}\n    >\n      {render_slot(@inner_block)}\n    </form>\n    \"\"\"\n  end\n\n  # contentinfo — <footer> in body context. Top-level only; one per page.\n  attr :class, :string, default: nil\n  attr :rest, :global, include: ~w(aria-label aria-labelledby)\n  slot :inner_block, required: true\n\n  def content_info(assigns) do\n    ~H\"\"\"\n    <footer\n      data-slot=\"landmark-contentinfo\"\n      class={[\"border-t bg-card px-4 py-3 text-sm text-muted-foreground\", @class]}\n      {@rest}\n    >\n      {render_slot(@inner_block)}\n    </footer>\n    \"\"\"\n  end\nend\n"
    },
    {
      "path": "registry/html/landmarks.html",
      "type": "registry:file",
      "target": "snippets/landmarks.html",
      "content": "<!--\n  shadcn-htmx — raw HTML landmarks page-shell.\n\n  Mirrors registry/ui/landmarks.tsx. One self-contained, correctly\n  landmarked page shell built from native HTML elements — no JS, no special\n  CSS, just Tailwind utilities + theme tokens.\n\n  Native element -> implicit ARIA landmark role:\n    <header>  -> banner          <aside>    -> complementary\n    <nav>     -> navigation       <section>  -> region (only when labelled)\n    <search>  -> search           <form>     -> form   (only when labelled)\n    <main>    -> main             <footer>   -> contentinfo\n\n  Labelling rules (APG):\n    - <main> and the body-level <header>/<footer> need no label (one each).\n    - Each <nav> carries a unique aria-label so AT can tell them apart.\n    - <search> wraps the <form>; the element itself is the search landmark\n      (no role=search needed). See MDN <search>.\n    - <aside> (complementary) is labelled when there is more than one.\n    - <section> is ONLY a region landmark when it has an accessible name\n      (aria-label / aria-labelledby) — a bare <section> exposes no role.\n    - <form> is ONLY a form landmark when it has an accessible name\n      (aria-label / aria-labelledby / title); use <search> for search forms.\n      MDN: repos/mdn/files/en-us/web/accessibility/aria/reference/roles/form_role/index.md:12,31,33,101\n\n  data-slot=\"landmark-<role>\" hangs off each root element.\n-->\n\n<div data-slot=\"landmark-shell\" class=\"flex min-h-0 flex-col overflow-hidden rounded-lg border\">\n  <!-- banner: top-level <header> -->\n  <header data-slot=\"landmark-banner\" class=\"flex items-center justify-between gap-4 border-b bg-card px-4 py-3 text-card-foreground\">\n    <span class=\"font-semibold tracking-tight\">Acme Console</span>\n\n    <!-- search landmark: the native <search> element wraps the form -->\n    <search data-slot=\"landmark-search\" aria-label=\"Site\" class=\"hidden sm:block\">\n      <form action=\"/search\" role=\"none\" class=\"flex items-center gap-2\">\n        <label for=\"lm-q\" class=\"sr-only\">Search the site</label>\n        <input id=\"lm-q\" type=\"search\" name=\"q\" placeholder=\"Search…\"\n          class=\"h-8 w-48 rounded-md border bg-background px-3 text-sm placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none\" />\n        <button type=\"submit\" class=\"inline-flex h-8 items-center rounded-md bg-primary px-3 text-sm font-medium text-primary-foreground hover:bg-primary/90\">Go</button>\n      </form>\n    </search>\n  </header>\n\n  <div class=\"flex flex-1 flex-col gap-4 bg-background p-4 md:flex-row\">\n    <!-- navigation: labelled so it is distinguishable from other navs -->\n    <nav data-slot=\"landmark-navigation\" aria-label=\"Primary\" class=\"shrink-0 text-sm md:w-44\">\n      <ul class=\"space-y-1\">\n        <li><a href=\"#\" aria-current=\"page\" class=\"block rounded-md bg-accent px-2 py-1.5 font-medium text-accent-foreground\">Overview</a></li>\n        <li><a href=\"#\" class=\"block rounded-md px-2 py-1.5 text-muted-foreground hover:bg-accent hover:text-accent-foreground\">Reports</a></li>\n        <li><a href=\"#\" class=\"block rounded-md px-2 py-1.5 text-muted-foreground hover:bg-accent hover:text-accent-foreground\">Settings</a></li>\n      </ul>\n    </nav>\n\n    <!-- main: exactly one per page, top-level -->\n    <main data-slot=\"landmark-main\" class=\"min-w-0 flex-1 space-y-4\">\n      <h1 class=\"text-lg font-semibold tracking-tight\">Overview</h1>\n      <p class=\"text-sm text-muted-foreground\">\n        The primary content of the page. There is exactly one\n        <code>&lt;main&gt;</code> landmark per document.\n      </p>\n\n      <!-- region: a labelled <section> is a region landmark -->\n      <section data-slot=\"landmark-region\" aria-labelledby=\"lm-usage-title\" class=\"rounded-lg border bg-card p-4 text-card-foreground\">\n        <h2 id=\"lm-usage-title\" class=\"text-sm font-semibold\">Usage this month</h2>\n        <p class=\"mt-1 text-sm text-muted-foreground\">42,318 requests · 7.1 GB transferred</p>\n      </section>\n\n      <!-- form: a labelled <form> is a form landmark (only when named).\n           Use <search> instead when the form performs a search. -->\n      <form data-slot=\"landmark-form\" aria-labelledby=\"lm-contact-title\" action=\"/contact\" method=\"post\" class=\"rounded-lg border bg-card p-4 text-card-foreground\">\n        <h2 id=\"lm-contact-title\" class=\"text-sm font-semibold\">Contact support</h2>\n        <label for=\"lm-msg\" class=\"mt-2 block text-sm text-muted-foreground\">Message</label>\n        <textarea id=\"lm-msg\" name=\"message\" rows=\"3\"\n          class=\"mt-1 w-full rounded-md border bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none\"></textarea>\n        <button type=\"submit\" class=\"mt-2 inline-flex h-8 items-center rounded-md bg-primary px-3 text-sm font-medium text-primary-foreground hover:bg-primary/90\">Send</button>\n      </form>\n    </main>\n\n    <!-- complementary: top-level supporting content, labelled -->\n    <aside data-slot=\"landmark-complementary\" aria-label=\"Related\" class=\"shrink-0 rounded-lg border bg-card p-4 text-sm text-card-foreground md:w-56\">\n      <h2 class=\"font-semibold\">Related</h2>\n      <ul class=\"mt-2 space-y-1 text-muted-foreground\">\n        <li><a href=\"#\" class=\"underline-offset-4 hover:underline\">Billing history</a></li>\n        <li><a href=\"#\" class=\"underline-offset-4 hover:underline\">API keys</a></li>\n      </ul>\n    </aside>\n  </div>\n\n  <!-- contentinfo: top-level <footer> -->\n  <footer data-slot=\"landmark-contentinfo\" class=\"border-t bg-card px-4 py-3 text-sm text-muted-foreground\">\n    © 2026 Acme, Inc. · <a href=\"#\" class=\"underline-offset-4 hover:underline\">Privacy</a> · <a href=\"#\" class=\"underline-offset-4 hover:underline\">Accessibility</a>\n  </footer>\n</div>\n"
    }
  ]
}
