{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "button",
  "type": "registry:ui",
  "title": "Button",
  "description": "A native <button> element with shadcn variants, sized for htmx v4. Ships in five flavours: Hono JSX (TypeScript), Jinja2 macro, Go html/template, Phoenix function component, and a raw HTML snippet. Follows the WAI-ARIA APG button pattern.",
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/button.tsx",
      "type": "registry:ui",
      "target": "components/ui/button.tsx",
      "content": "/** @jsxImportSource hono/jsx */\nimport type { PropsWithChildren } from \"hono/jsx\"\nimport { cloneElement, isValidElement } from \"hono/jsx\"\nimport { cn, type ClassValue } from \"@/registry/lib/cn\"\n\n// Variants mirror shadcn/ui's Button (new-york-v4), translated to htmx-friendly\n// server-rendered JSX. Source of truth:\n//   repos/shadcn-ui/apps/v4/registry/new-york-v4/ui/button.tsx\n//\n// Accessibility contract follows the WAI-ARIA APG button pattern:\n//   repos/aria-practices/content/patterns/button/button-pattern.html\n// Because we render a real <button>, role and Space/Enter activation come for\n// free from the platform — we only add aria-* where the pattern demands it.\n//\n// Polymorphic rendering: shadcn uses Radix Slot.Root for `asChild`. Hono JSX\n// has cloneElement, so we implement the same idea — pass a single JSX child\n// (e.g. <a href=\"...\">), and the button classes are merged onto it.\n\nexport type ButtonVariant =\n  | \"default\"\n  | \"destructive\"\n  | \"outline\"\n  | \"secondary\"\n  | \"ghost\"\n  | \"link\"\n\nexport type ButtonSize =\n  | \"default\"\n  | \"xs\"\n  | \"sm\"\n  | \"lg\"\n  | \"icon\"\n  | \"icon-xs\"\n  | \"icon-sm\"\n  | \"icon-lg\"\n\nconst base =\n  \"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none \" +\n  \"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 \" +\n  \"disabled:pointer-events-none disabled:opacity-50 \" +\n  // aria-disabled mirrors the disabled affordance for cases where the element\n  // must stay focusable (so screen readers can land on it and announce why\n  // it's unavailable). See repos/mdn/files/en-us/web/accessibility/aria/attributes/aria-disabled/.\n  \"aria-disabled:pointer-events-none aria-disabled:opacity-50 \" +\n  \"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 \" +\n  \"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 \" +\n  // htmx v4: while a request triggered by/targeting this button is in flight,\n  // htmx adds the .htmx-request class. We mirror disabled affordance.\n  \"[&.htmx-request]:pointer-events-none [&.htmx-request]:opacity-70\"\n\nconst variants: Record<ButtonVariant, string> = {\n  default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n  destructive:\n    \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40\",\n  outline:\n    \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50\",\n  secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n  ghost: \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n  link: \"text-primary underline-offset-4 hover:underline\",\n}\n\nconst sizes: Record<ButtonSize, string> = {\n  default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n  xs: \"h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3\",\n  sm: \"h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5\",\n  lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n  icon: \"size-9\",\n  \"icon-xs\": \"size-6 rounded-md [&_svg:not([class*='size-'])]:size-3\",\n  \"icon-sm\": \"size-8\",\n  \"icon-lg\": \"size-10\",\n}\n\nexport function buttonClasses(opts?: {\n  variant?: ButtonVariant\n  size?: ButtonSize\n  class?: ClassValue\n}): string {\n  const variant = opts?.variant ?? \"default\"\n  const size = opts?.size ?? \"default\"\n  return cn(base, variants[variant], sizes[size], opts?.class)\n}\n\n// Props beyond visual variants. We intentionally type the standard <button>\n// attributes we actually want IDE support for. Hono's JSX accepts unknown\n// attribute names on intrinsic elements, but typing the common ones keeps\n// call sites honest.\ntype ButtonProps = PropsWithChildren<{\n  variant?: ButtonVariant\n  size?: ButtonSize\n  class?: ClassValue\n  type?: \"button\" | \"submit\" | \"reset\"\n  disabled?: boolean\n  // aria-disabled keeps the element focusable while its action is unavailable\n  // (so a screen reader can land on it and announce it), unlike native\n  // `disabled` which removes it from the a11y tree / tab order. Independent of\n  // `disabled`. See repos/aria-practices/content/patterns/button/button-pattern.html\n  // and repos/mdn/files/en-us/web/accessibility/aria/reference/attributes/aria-disabled/.\n  ariaDisabled?: boolean\n  // APG: ARIA toggle button. When set, aria-pressed reflects the state and\n  // the label must stay constant across states. aria-pressed is tri-state:\n  // \"mixed\" means the items the toggle controls don't all share one value.\n  // See repos/mdn/files/en-us/web/accessibility/aria/reference/attributes/aria-pressed/.\n  pressed?: boolean | \"mixed\"\n  ariaLabel?: string\n  ariaLabelledby?: string\n  ariaDescribedby?: string\n\n  // Disclosure / menu / popover trigger contract. Lets a styled Button act as\n  // an expandable trigger (accordion/collapsible) or menu/listbox/dialog\n  // opener without hand-rolling a bare <button>.\n  // See repos/aria-practices/content/patterns/button/button-pattern.html\n  // and repos/mdn/files/en-us/web/accessibility/aria/reference/attributes/aria-expanded/\n  // and repos/mdn/files/en-us/web/accessibility/aria/reference/attributes/aria-haspopup/.\n  ariaExpanded?: boolean\n  ariaHaspopup?: boolean | \"menu\" | \"listbox\" | \"tree\" | \"grid\" | \"dialog\"\n  ariaControls?: string\n\n  // Standard form attributes (MDN <button>). Useful for multi-submit-button\n  // forms where one button posts to a different URL or method.\n  id?: string\n  name?: string\n  value?: string\n  form?: string\n  formaction?: string\n  formenctype?: \"application/x-www-form-urlencoded\" | \"multipart/form-data\" | \"text/plain\"\n  formmethod?: \"get\" | \"post\" | \"dialog\"\n  formnovalidate?: boolean\n  formtarget?: string\n  popovertarget?: string\n  popovertargetaction?: \"show\" | \"hide\" | \"toggle\"\n\n  // Focus this button on initial page load (one per document).\n  autofocus?: boolean\n\n  // Invoker API (newer than popovertarget — declarative dialog/popover\n  // control). `command` is one of: show-modal | close | request-close |\n  // show-popover | hide-popover | toggle-popover | --custom; `commandfor`\n  // is the target element id.\n  // See repos/mdn/files/en-us/web/html/reference/elements/button/index.md:60-85\n  command?:\n    | \"show-modal\"\n    | \"close\"\n    | \"request-close\"\n    | \"show-popover\"\n    | \"hide-popover\"\n    | \"toggle-popover\"\n    | (string & {}) // `--custom-command` is allowed too\n  commandfor?: string\n\n  // htmx v4 attributes (subset). See repos/htmx/www/src/content/reference/01-attributes/.\n  \"hx-get\"?: string\n  \"hx-post\"?: string\n  \"hx-put\"?: string\n  \"hx-patch\"?: string\n  \"hx-delete\"?: string\n  \"hx-target\"?: string\n  \"hx-swap\"?: string\n  \"hx-trigger\"?: string\n  \"hx-indicator\"?: string\n  \"hx-confirm\"?: string\n  \"hx-vals\"?: string\n  // v4: \"disable form elements during requests\" (renamed from v3's hx-disabled-elt).\n  // See repos/htmx/www/src/content/docs/01-get-started/02-migration.md.\n  \"hx-disable\"?: string\n\n  // Render as the single JSX child element (anchor, label, etc.) with the\n  // button classes merged onto it. SSR-friendly equivalent of shadcn's\n  // Radix-Slot-based `asChild` pattern.\n  asChild?: boolean\n}>\n\nexport function Button(props: ButtonProps) {\n  const {\n    children,\n    variant,\n    size,\n    class: className,\n    type = \"button\",\n    disabled,\n    ariaDisabled,\n    pressed,\n    ariaLabel,\n    ariaLabelledby,\n    ariaDescribedby,\n    ariaExpanded,\n    ariaHaspopup,\n    ariaControls,\n    asChild,\n    ...rest\n  } = props\n\n  const classes = buttonClasses({ variant, size, class: className })\n\n  // asChild path: clone the single child and merge classes/data-* onto it so\n  // the call site can render as <a>, <label>, etc. while keeping the visual\n  // contract. Throws softly (returns the children unchanged) if the child\n  // isn't a valid element.\n  if (asChild && isValidElement(children)) {\n    const child = children as any\n    const merged = cn(classes, child?.props?.class)\n    return cloneElement(child, {\n      ...rest,\n      class: merged,\n      \"data-slot\": \"button\",\n      \"data-variant\": variant ?? \"default\",\n      \"data-size\": size ?? \"default\",\n      \"aria-disabled\": ariaDisabled ? \"true\" : undefined,\n      \"aria-pressed\": pressed === undefined ? undefined : pressed,\n      \"aria-label\": ariaLabel,\n      \"aria-labelledby\": ariaLabelledby,\n      \"aria-describedby\": ariaDescribedby,\n      \"aria-expanded\": ariaExpanded === undefined ? undefined : ariaExpanded,\n      \"aria-haspopup\": ariaHaspopup === undefined ? undefined : ariaHaspopup,\n      \"aria-controls\": ariaControls,\n    })\n  }\n\n  return (\n    <button\n      type={type}\n      class={classes}\n      disabled={disabled}\n      aria-disabled={ariaDisabled ? \"true\" : undefined}\n      aria-pressed={pressed === undefined ? undefined : pressed}\n      aria-label={ariaLabel}\n      aria-labelledby={ariaLabelledby}\n      aria-describedby={ariaDescribedby}\n      aria-expanded={ariaExpanded === undefined ? undefined : ariaExpanded}\n      aria-haspopup={ariaHaspopup === undefined ? undefined : ariaHaspopup}\n      aria-controls={ariaControls}\n      data-slot=\"button\"\n      data-variant={variant ?? \"default\"}\n      data-size={size ?? \"default\"}\n      {...rest}\n    >\n      {children}\n    </button>\n  )\n}\n"
    },
    {
      "path": "registry/jinja2/button.html",
      "type": "registry:file",
      "target": "templates/components/button.html",
      "content": "{# Button macro — shadcn-htmx, htmx v4 + Tailwind v4.\n   Mirrors registry/ui/button.tsx so a user on a Python/Flask/FastAPI/Django\n   project can render the same markup our docs site renders.\n\n   Usage:\n       {% from \"components/button.html\" import button %}\n       {{ button(\"Save\", hx_post=\"/save\") }}\n       {{ button(\"Delete\", variant=\"destructive\", size=\"sm\") }}\n\n   Multi-submit forms (button posts to a per-button URL):\n       {{ button(\"Save and continue\",\n                  type=\"submit\", name=\"action\", value=\"continue\",\n                  formaction=\"/orders/save\", formmethod=\"post\") }}\n\n   All hx-* attributes are passed through via **attrs (underscores become\n   dashes, so `hx_post=\"/save\"` emits `hx-post=\"/save\"`).\n\n   The macro emits a native <button>, so role and Space/Enter activation come\n   for free. See repos/aria-practices/content/patterns/button/. #}\n\n{% macro button(\n    label,\n    variant=\"default\",\n    size=\"default\",\n    type=\"button\",\n    disabled=false,\n    aria_disabled=false,\n    pressed=none,\n    aria_label=none,\n    aria_labelledby=none,\n    aria_describedby=none,\n    aria_expanded=none,\n    aria_haspopup=none,\n    aria_controls=none,\n    id=none,\n    name=none,\n    value=none,\n    form=none,\n    formaction=none,\n    formenctype=none,\n    formmethod=none,\n    formnovalidate=false,\n    formtarget=none,\n    popovertarget=none,\n    popovertargetaction=none,\n    extra_class=\"\",\n    **attrs\n) %}\n{%- set base -%}\ninline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&.htmx-request]:pointer-events-none [&.htmx-request]:opacity-70\n{%- endset -%}\n\n{%- set variants = {\n    \"default\": \"bg-primary text-primary-foreground hover:bg-primary/90\",\n    \"destructive\": \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40\",\n    \"outline\": \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50\",\n    \"secondary\": \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n    \"ghost\": \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n    \"link\": \"text-primary underline-offset-4 hover:underline\"\n} -%}\n\n{%- set sizes = {\n    \"default\": \"h-9 px-4 py-2 has-[>svg]:px-3\",\n    \"xs\": \"h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3\",\n    \"sm\": \"h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5\",\n    \"lg\": \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n    \"icon\": \"size-9\",\n    \"icon-xs\": \"size-6 rounded-md [&_svg:not([class*='size-'])]:size-3\",\n    \"icon-sm\": \"size-8\",\n    \"icon-lg\": \"size-10\"\n} -%}\n\n<button type=\"{{ type }}\"\n        class=\"{{ base }} {{ variants[variant] }} {{ sizes[size] }} {{ extra_class }}\"\n        {%- if id %} id=\"{{ id }}\"{% endif %}\n        {%- if name %} name=\"{{ name }}\"{% endif %}\n        {%- if value is not none %} value=\"{{ value }}\"{% endif %}\n        {%- if form %} form=\"{{ form }}\"{% endif %}\n        {%- if formaction %} formaction=\"{{ formaction }}\"{% endif %}\n        {%- if formenctype %} formenctype=\"{{ formenctype }}\"{% endif %}\n        {%- if formmethod %} formmethod=\"{{ formmethod }}\"{% endif %}\n        {%- if formnovalidate %} formnovalidate{% endif %}\n        {%- if formtarget %} formtarget=\"{{ formtarget }}\"{% endif %}\n        {%- if popovertarget %} popovertarget=\"{{ popovertarget }}\"{% endif %}\n        {%- if popovertargetaction %} popovertargetaction=\"{{ popovertargetaction }}\"{% endif %}\n        {%- if disabled %} disabled{% endif %}\n        {# aria-disabled stays focusable while unavailable, unlike `disabled`.\n           See repos/aria-practices/content/patterns/button/button-pattern.html #}\n        {%- if aria_disabled %} aria-disabled=\"true\"{% endif %}\n        {# aria-pressed is tri-state: true | false | \"mixed\". See\n           repos/mdn/files/en-us/web/accessibility/aria/reference/attributes/aria-pressed/ #}\n        {%- if pressed is not none %} aria-pressed=\"{{ pressed if pressed is string else ('true' if pressed else 'false') }}\"{% endif %}\n        {%- if aria_label %} aria-label=\"{{ aria_label }}\"{% endif %}\n        {%- if aria_labelledby %} aria-labelledby=\"{{ aria_labelledby }}\"{% endif %}\n        {%- if aria_describedby %} aria-describedby=\"{{ aria_describedby }}\"{% endif %}\n        {# Disclosure / menu trigger contract. See\n           repos/mdn/files/en-us/web/accessibility/aria/reference/attributes/aria-expanded/\n           and .../aria-haspopup/ #}\n        {%- if aria_expanded is not none %} aria-expanded=\"{{ aria_expanded if aria_expanded is string else ('true' if aria_expanded else 'false') }}\"{% endif %}\n        {%- if aria_haspopup is not none %} aria-haspopup=\"{{ aria_haspopup if aria_haspopup is string else ('true' if aria_haspopup else 'false') }}\"{% endif %}\n        {%- if aria_controls %} aria-controls=\"{{ aria_controls }}\"{% endif %}\n        data-slot=\"button\" data-variant=\"{{ variant }}\" data-size=\"{{ size }}\"\n        {%- for k, v in attrs.items() %} {{ k|replace('_', '-') }}=\"{{ v }}\"{% endfor -%}\n>{{ label }}</button>\n{% endmacro %}\n"
    },
    {
      "path": "registry/go-templates/button.tmpl",
      "type": "registry:file",
      "target": "components/button.tmpl",
      "content": "{{/*\n  Button template — shadcn-htmx, htmx v4 + Tailwind v4.\n  Mirrors registry/ui/button.tsx for Go projects using html/template.\n\n  Usage in your code:\n\n      type ButtonArgs struct {\n          Label    string\n          Variant  string // default | destructive | outline | secondary | ghost | link\n          Size     string // default | xs | sm | lg | icon | icon-xs | icon-sm | icon-lg\n          Type     string // button | submit | reset\n          Disabled bool\n          // AriaDisabled keeps the button focusable while its action is\n          // unavailable (unlike Disabled, which drops it from the a11y tree).\n          AriaDisabled bool\n          // Pressed is the aria-pressed toggle state. Use a *bool for true/false,\n          // or set PressedMixed=true for the tri-state \"mixed\" value.\n          // aria-pressed is tri-state: true | false | \"mixed\".\n          Pressed      *bool\n          PressedMixed bool\n\n          // ARIA\n          AriaLabel       string\n          AriaLabelledby  string\n          AriaDescribedby string\n          // Disclosure / menu trigger contract.\n          AriaExpanded     *bool\n          AriaHaspopup     string // true | menu | listbox | tree | grid | dialog\n          AriaControls     string\n\n          // Standard <button> form attributes — useful for multi-submit forms.\n          ID             string\n          Name           string\n          Value          string\n          Form           string\n          FormAction     string\n          FormEnctype    string // application/x-www-form-urlencoded | multipart/form-data | text/plain\n          FormMethod     string // get | post | dialog\n          FormNoValidate bool\n          FormTarget     string\n          PopoverTarget  string\n          PopoverTargetAction string // show | hide | toggle\n\n          // Everything else (hx-post, hx-target, hx-swap, …) goes here.\n          Attrs map[string]string\n      }\n\n      // Once at startup:\n      tpl := template.Must(template.New(\"\").ParseFiles(\"components/button.tmpl\"))\n\n      // Then per-render:\n      tpl.ExecuteTemplate(w, \"button\", ButtonArgs{\n          Label: \"Save\", Variant: \"default\",\n          Attrs: map[string]string{\"hx-post\": \"/save\"},\n      })\n\n  The template emits a native <button>, so role and Space/Enter activation\n  come for free. See repos/aria-practices/content/patterns/button/.\n*/}}\n\n{{define \"button\"}}\n{{- $variants := dict\n    \"default\" \"bg-primary text-primary-foreground hover:bg-primary/90\"\n    \"destructive\" \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40\"\n    \"outline\" \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50\"\n    \"secondary\" \"bg-secondary text-secondary-foreground hover:bg-secondary/80\"\n    \"ghost\" \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\"\n    \"link\" \"text-primary underline-offset-4 hover:underline\" -}}\n{{- $sizes := dict\n    \"default\" \"h-9 px-4 py-2 has-[>svg]:px-3\"\n    \"xs\" \"h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3\"\n    \"sm\" \"h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5\"\n    \"lg\" \"h-10 rounded-md px-6 has-[>svg]:px-4\"\n    \"icon\" \"size-9\"\n    \"icon-xs\" \"size-6 rounded-md [&_svg:not([class*='size-'])]:size-3\"\n    \"icon-sm\" \"size-8\"\n    \"icon-lg\" \"size-10\" -}}\n{{- $variant := or .Variant \"default\" -}}\n{{- $size := or .Size \"default\" -}}\n{{- $type := or .Type \"button\" -}}\n{{- $base := \"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&.htmx-request]:pointer-events-none [&.htmx-request]:opacity-70\" -}}\n<button type=\"{{$type}}\"\n        class=\"{{$base}} {{index $variants $variant}} {{index $sizes $size}}\"\n        {{- if .ID}} id=\"{{.ID}}\"{{end}}\n        {{- if .Name}} name=\"{{.Name}}\"{{end}}\n        {{- if .Value}} value=\"{{.Value}}\"{{end}}\n        {{- if .Form}} form=\"{{.Form}}\"{{end}}\n        {{- if .FormAction}} formaction=\"{{.FormAction}}\"{{end}}\n        {{- if .FormEnctype}} formenctype=\"{{.FormEnctype}}\"{{end}}\n        {{- if .FormMethod}} formmethod=\"{{.FormMethod}}\"{{end}}\n        {{- if .FormNoValidate}} formnovalidate{{end}}\n        {{- if .FormTarget}} formtarget=\"{{.FormTarget}}\"{{end}}\n        {{- if .PopoverTarget}} popovertarget=\"{{.PopoverTarget}}\"{{end}}\n        {{- if .PopoverTargetAction}} popovertargetaction=\"{{.PopoverTargetAction}}\"{{end}}\n        {{- if .Disabled}} disabled{{end}}\n        {{/* aria-disabled stays focusable while unavailable, unlike disabled.\n             See repos/aria-practices/content/patterns/button/button-pattern.html */}}\n        {{- if .AriaDisabled}} aria-disabled=\"true\"{{end}}\n        {{/* aria-pressed is tri-state: true | false | \"mixed\". See\n             repos/mdn/files/en-us/web/accessibility/aria/reference/attributes/aria-pressed/ */}}\n        {{- if .PressedMixed}} aria-pressed=\"mixed\"\n        {{- else if .Pressed}} aria-pressed=\"{{if deref .Pressed}}true{{else}}false{{end}}\"{{end}}\n        {{- if .AriaLabel}} aria-label=\"{{.AriaLabel}}\"{{end}}\n        {{- if .AriaLabelledby}} aria-labelledby=\"{{.AriaLabelledby}}\"{{end}}\n        {{- if .AriaDescribedby}} aria-describedby=\"{{.AriaDescribedby}}\"{{end}}\n        {{/* Disclosure / menu trigger contract. See\n             repos/mdn/files/en-us/web/accessibility/aria/reference/attributes/aria-expanded/\n             and .../aria-haspopup/ */}}\n        {{- if .AriaExpanded}} aria-expanded=\"{{if deref .AriaExpanded}}true{{else}}false{{end}}\"{{end}}\n        {{- if .AriaHaspopup}} aria-haspopup=\"{{.AriaHaspopup}}\"{{end}}\n        {{- if .AriaControls}} aria-controls=\"{{.AriaControls}}\"{{end}}\n        data-slot=\"button\" data-variant=\"{{$variant}}\" data-size=\"{{$size}}\"\n        {{- range $k, $v := .Attrs}} {{$k}}=\"{{$v}}\"{{end -}}\n>{{.Label}}</button>\n{{end}}\n\n{{/*\n  Note: this template uses sprig's `dict` and `deref` helpers. If you don't\n  use sprig, hard-code the lookup or pass the class string from Go code:\n\n      args.Class = computeButtonClass(args.Variant, args.Size)\n\n  and reference {{.Class}} directly in the template.\n*/}}\n"
    },
    {
      "path": "registry/phoenix/button.ex",
      "type": "registry:file",
      "target": "lib/my_app_web/components/button.ex",
      "content": "defmodule ShadcnHtmx.Components.Button do\n  @moduledoc \"\"\"\n  Button — shadcn-htmx, htmx v4 + Tailwind v4 for Phoenix.\n\n  Mirrors registry/ui/button.tsx so a Phoenix LiveView project can render\n  the same markup our docs site renders. Works with plain HEEx templates\n  too — htmx attributes pass straight through via `:rest`.\n\n  ## Examples\n\n      <.button hx-post=\"/save\">Save</.button>\n      <.button variant=\"destructive\" size=\"sm\">Delete</.button>\n      <.button pressed={true} aria-label=\"Mute\">Mute</.button>\n\n      # Multi-submit form (one button posts to a different URL)\n      <.button type=\"submit\" name=\"action\" value=\"continue\"\n               formaction=\"/orders/save\" formmethod=\"post\">\n        Save and continue\n      </.button>\n\n  The button is a native `<button>` so role and Space/Enter activation come\n  for free. See repos/aria-practices/content/patterns/button/.\n  \"\"\"\n\n  use Phoenix.Component\n\n  @variants %{\n    \"default\" => \"bg-primary text-primary-foreground hover:bg-primary/90\",\n    \"destructive\" =>\n      \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40\",\n    \"outline\" =>\n      \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50\",\n    \"secondary\" => \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n    \"ghost\" => \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n    \"link\" => \"text-primary underline-offset-4 hover:underline\"\n  }\n\n  @sizes %{\n    \"default\" => \"h-9 px-4 py-2 has-[>svg]:px-3\",\n    \"xs\" =>\n      \"h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3\",\n    \"sm\" => \"h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5\",\n    \"lg\" => \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n    \"icon\" => \"size-9\",\n    \"icon-xs\" => \"size-6 rounded-md [&_svg:not([class*='size-'])]:size-3\",\n    \"icon-sm\" => \"size-8\",\n    \"icon-lg\" => \"size-10\"\n  }\n\n  @base \"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium \" <>\n          \"whitespace-nowrap transition-all outline-none \" <>\n          \"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 \" <>\n          \"disabled:pointer-events-none disabled:opacity-50 \" <>\n          \"aria-disabled:pointer-events-none aria-disabled:opacity-50 \" <>\n          \"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 \" <>\n          \"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 \" <>\n          \"[&.htmx-request]:pointer-events-none [&.htmx-request]:opacity-70\"\n\n  attr :variant, :string,\n    default: \"default\",\n    values: ~w(default destructive outline secondary ghost link)\n\n  attr :size, :string,\n    default: \"default\",\n    values: ~w(default xs sm lg icon icon-xs icon-sm icon-lg)\n\n  attr :type, :string, default: \"button\"\n  attr :disabled, :boolean, default: false\n  # aria-disabled keeps the button focusable while unavailable, unlike `disabled`\n  # which drops it from the a11y tree / tab order. Independent of `disabled`.\n  # See repos/aria-practices/content/patterns/button/button-pattern.html\n  attr :aria_disabled, :boolean, default: false\n  # aria-pressed is tri-state: true | false | \"mixed\". `:any` accepts a \"mixed\"\n  # string for toggles whose controlled items don't all share one value.\n  # See repos/mdn/files/en-us/web/accessibility/aria/reference/attributes/aria-pressed/\n  attr :pressed, :any, default: nil\n  # Disclosure / menu trigger contract. See\n  # repos/mdn/files/en-us/web/accessibility/aria/reference/attributes/aria-expanded/\n  # and .../aria-haspopup/\n  attr :aria_expanded, :any, default: nil\n  attr :aria_haspopup, :any, default: nil\n  attr :class, :string, default: nil\n\n  attr :rest, :global,\n    include:\n      ~w(hx-get hx-post hx-put hx-patch hx-delete hx-target hx-swap hx-trigger hx-indicator hx-confirm hx-vals hx-disable\n         id name value form formaction formenctype formmethod formnovalidate formtarget popovertarget popovertargetaction\n         command commandfor\n         aria-label aria-labelledby aria-describedby aria-controls)\n\n  slot :inner_block, required: true\n\n  def button(assigns) do\n    assigns =\n      assigns\n      |> assign(:variant_class, Map.fetch!(@variants, assigns.variant))\n      |> assign(:size_class, Map.fetch!(@sizes, assigns.size))\n      |> assign(:base_class, @base)\n\n    ~H\"\"\"\n    <button\n      type={@type}\n      class={[@base_class, @variant_class, @size_class, @class]}\n      disabled={@disabled}\n      aria-disabled={if @aria_disabled, do: \"true\", else: nil}\n      aria-pressed={if is_nil(@pressed), do: nil, else: to_string(@pressed)}\n      aria-expanded={if is_nil(@aria_expanded), do: nil, else: to_string(@aria_expanded)}\n      aria-haspopup={if is_nil(@aria_haspopup), do: nil, else: to_string(@aria_haspopup)}\n      data-slot=\"button\"\n      data-variant={@variant}\n      data-size={@size}\n      {@rest}\n    >\n      {render_slot(@inner_block)}\n    </button>\n    \"\"\"\n  end\nend\n"
    },
    {
      "path": "registry/html/button.html",
      "type": "registry:file",
      "target": "snippets/button.html",
      "content": "<!--\n  shadcn-htmx — raw HTML button snippets.\n\n  No template engine, no JavaScript framework. Just the class strings you need\n  on a real <button> element, ready to drop into any HTML file that loads\n  Tailwind CSS v4 and (optionally) htmx v4.\n\n  Requirements:\n    1. Tailwind CSS v4 set up in your project, OR include the Play CDN for\n       quick experiments:\n         <script src=\"https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4\"></script>\n    2. htmx v4 if you want the hx-* attributes to do anything:\n         <script src=\"https://unpkg.com/htmx.org@4.0.0/dist/htmx.min.js\" defer></script>\n    3. The CSS variables shadcn relies on (--background, --foreground, --primary,\n       --primary-foreground, --border, --ring, --destructive, etc.). Copy the\n       :root and .dark blocks from app/styles/input.css into your stylesheet —\n       these are framework-agnostic.\n\n  The base class string is the same for every snippet — only the variant-\n  specific colour utilities and the size-specific dimensions change. We keep\n  the full string in each snippet so you can copy a single block and paste it\n  into a working button immediately.\n\n  BASE (shared by every variant):\n    inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm\n    font-medium whitespace-nowrap transition-all outline-none\n    focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50\n    disabled:pointer-events-none disabled:opacity-50\n    aria-disabled:pointer-events-none aria-disabled:opacity-50\n    aria-invalid:border-destructive aria-invalid:ring-destructive/20\n    dark:aria-invalid:ring-destructive/40\n    [&_svg]:pointer-events-none [&_svg]:shrink-0\n    [&_svg:not([class*='size-'])]:size-4\n    [&.htmx-request]:pointer-events-none [&.htmx-request]:opacity-70\n-->\n\n<!-- ─── Variants ────────────────────────────────────────────────────── -->\n\n<!-- Default -->\n<button type=\"button\" data-slot=\"button\" data-variant=\"default\" data-size=\"default\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-9 px-4 py-2\">\n  Save\n</button>\n\n<!-- Secondary -->\n<button type=\"button\" data-slot=\"button\" data-variant=\"secondary\" data-size=\"default\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 bg-secondary text-secondary-foreground hover:bg-secondary/80 h-9 px-4 py-2\">\n  Cancel\n</button>\n\n<!-- Destructive -->\n<button type=\"button\" data-slot=\"button\" data-variant=\"destructive\" data-size=\"default\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 h-9 px-4 py-2\">\n  Delete\n</button>\n\n<!-- Outline -->\n<button type=\"button\" data-slot=\"button\" data-variant=\"outline\" data-size=\"default\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50 h-9 px-4 py-2\">\n  Outline\n</button>\n\n<!-- Ghost -->\n<button type=\"button\" data-slot=\"button\" data-variant=\"ghost\" data-size=\"default\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50 h-9 px-4 py-2\">\n  Ghost\n</button>\n\n<!-- Link -->\n<button type=\"button\" data-slot=\"button\" data-variant=\"link\" data-size=\"default\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 text-primary underline-offset-4 hover:underline h-9 px-4 py-2\">\n  Learn more\n</button>\n\n<!-- ─── Sizes (with the default variant) ────────────────────────────── -->\n\n<!-- xs — 24px tall, dense rows -->\n<button type=\"button\" data-slot=\"button\" data-size=\"xs\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-1 rounded-md font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-6 px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3\">\n  Tiny\n</button>\n\n<!-- Small -->\n<button type=\"button\" data-slot=\"button\" data-size=\"sm\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-1.5 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-8 px-3 has-[>svg]:px-2.5\">\n  Small\n</button>\n\n<!-- Large -->\n<button type=\"button\" data-slot=\"button\" data-size=\"lg\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-6 has-[>svg]:px-4\">\n  Large\n</button>\n\n<!-- ─── Icon-only sizes (always pair with aria-label) ───────────────── -->\n\n<!-- icon-xs — 24×24 -->\n<button type=\"button\" data-slot=\"button\" data-size=\"icon-xs\" aria-label=\"Add\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3 bg-primary text-primary-foreground hover:bg-primary/90 size-6 rounded-md\">\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n    <path d=\"M5 12h14\" /><path d=\"M12 5v14\" />\n  </svg>\n</button>\n\n<!-- icon-sm — 32×32 -->\n<button type=\"button\" data-slot=\"button\" data-size=\"icon-sm\" aria-label=\"Add\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 bg-primary text-primary-foreground hover:bg-primary/90 size-8\">\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n    <path d=\"M5 12h14\" /><path d=\"M12 5v14\" />\n  </svg>\n</button>\n\n<!-- icon — 36×36 -->\n<button type=\"button\" data-slot=\"button\" data-size=\"icon\" aria-label=\"Add\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 bg-primary text-primary-foreground hover:bg-primary/90 size-9\">\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n    <path d=\"M5 12h14\" /><path d=\"M12 5v14\" />\n  </svg>\n</button>\n\n<!-- icon-lg — 40×40 -->\n<button type=\"button\" data-slot=\"button\" data-size=\"icon-lg\" aria-label=\"Add\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 bg-primary text-primary-foreground hover:bg-primary/90 size-10\">\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n    <path d=\"M5 12h14\" /><path d=\"M12 5v14\" />\n  </svg>\n</button>\n\n<!-- ─── States ──────────────────────────────────────────────────────── -->\n\n<!-- Disabled (native attribute — also removes from tab order) -->\n<button type=\"button\" disabled\n  class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground h-9 px-4 py-2\">\n  Disabled\n</button>\n\n<!-- Aria-disabled (looks disabled but remains focusable; pair with click handler that no-ops) -->\n<button type=\"button\" aria-disabled=\"true\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-disabled:pointer-events-none aria-disabled:opacity-50 bg-primary text-primary-foreground h-9 px-4 py-2\">\n  Aria-disabled\n</button>\n\n<!-- Toggle (aria-pressed) — keep the label constant across states -->\n<button type=\"button\" aria-pressed=\"false\" aria-label=\"Mute\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground h-9 px-4 py-2\">\n  Mute\n</button>\n\n<!-- Toggle (tri-state) — aria-pressed=\"mixed\" when the controlled items don't\n     all share one value (e.g. a Bold toggle over a mixed text selection).\n     See repos/mdn/files/en-us/web/accessibility/aria/reference/attributes/aria-pressed/ -->\n<button type=\"button\" aria-pressed=\"mixed\" aria-label=\"Bold\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground h-9 px-4 py-2\">\n  Bold\n</button>\n\n<!-- Menu / disclosure trigger — aria-haspopup announces the popup kind;\n     aria-expanded + aria-controls wire the trigger to the controlled element.\n     See repos/mdn/files/en-us/web/accessibility/aria/reference/attributes/aria-haspopup/\n     and .../aria-expanded/ -->\n<button type=\"button\" aria-haspopup=\"menu\" aria-expanded=\"false\" aria-controls=\"actions-menu\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground h-9 px-4 py-2\">\n  Actions\n</button>\n\n<!-- Aria-invalid (pairs the button to a failing form field via aria-describedby) -->\n<button type=\"submit\" aria-invalid=\"true\" aria-describedby=\"email-error\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 bg-primary text-primary-foreground hover:bg-primary/90 h-9 px-4 py-2\">\n  Save\n</button>\n\n<!-- ─── Multi-submit form (per-button formaction/formmethod) ────────── -->\n<form action=\"/orders\" method=\"post\">\n  <!-- normal submit goes to /orders POST -->\n  <button type=\"submit\"\n    class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 bg-primary text-primary-foreground hover:bg-primary/90 h-9 px-4 py-2\">\n    Save\n  </button>\n\n  <!-- this submit overrides the form's action + method -->\n  <button type=\"submit\" name=\"action\" value=\"continue\"\n          formaction=\"/orders/save\" formmethod=\"post\"\n    class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground h-9 px-4 py-2\">\n    Save and continue\n  </button>\n</form>\n\n<!-- ─── htmx — fragment swap ────────────────────────────────────────── -->\n<!--\n  htmx adds .htmx-request to this button while the request is in flight.\n  The matching utility (.[&.htmx-request]:opacity-70) provides the visual cue.\n  hx-disable=\"this\" (v4; was hx-disabled-elt in v3) blocks repeat submits.\n-->\n<button type=\"button\"\n  hx-post=\"/save\" hx-target=\"#result\" hx-swap=\"innerHTML\" hx-disable=\"this\"\n  class=\"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&.htmx-request]:pointer-events-none [&.htmx-request]:opacity-70 bg-primary text-primary-foreground hover:bg-primary/90 h-9 px-4 py-2\">\n  Save\n</button>\n<span id=\"result\" aria-live=\"polite\"></span>\n"
    }
  ]
}
