{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "output",
  "type": "registry:ui",
  "title": "Output",
  "description": "Live result region for the outcome of a calculation or server action. Renders the native <output> element (implicit role=\"status\", an aria-live=\"polite\" + aria-atomic=\"true\" region), tied to its inputs via the for attribute. htmx innerHTML swaps of its content are announced by assistive tech automatically — zero JS.",
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/output.tsx",
      "type": "registry:ui",
      "target": "components/ui/output.tsx",
      "content": "/** @jsxImportSource hono/jsx */\nimport type { Child } from \"hono/jsx\"\nimport { cn, type ClassValue } from \"@/registry/lib/cn\"\n\n// Output — shadcn-htmx, htmx v4 + Tailwind v4.\n//\n// A live result region: the outcome of a calculation or a server action,\n// tied to the inputs that produced it via the `for` attribute.\n//\n// Source of truth — we render the real native <output> element:\n//   repos/mdn/files/en-us/web/html/reference/elements/output/index.md\n//     - `for`  — space-separated list of the ids of elements that\n//       contributed input values to the calculation (lines 16-17).\n//     - `form` — associate the output with a <form> elsewhere in the\n//       document by its id; overrides an ancestor <form> (lines 18-21).\n//     - `name` — the element's name in the form.elements API (lines 23-24).\n//     - \"Implicit ARIA role: status\" (lines 119-120). shadcn/ui ships no\n//       Output component — this is a native-platform addition.\n//\n// Why no JS: <output>'s implicit role=\"status\" IS a live region with an\n// implicit aria-live=\"polite\" and aria-atomic=\"true\":\n//   repos/mdn/files/en-us/web/accessibility/aria/reference/roles/status_role/index.md:18\n//   repos/mdn/files/en-us/web/accessibility/aria/guides/live_regions/index.md:12\n//     \"Including ... role='status' ... works as long as you add the\n//      attribute before the changes occur ... Start with an empty live\n//      region, then change the content inside the region.\"\n// So an htmx swap of the output's CONTENT (hx-swap=\"innerHTML\", with the\n// <output> itself as the persistent target) is announced automatically —\n// no aria-live wiring, no site.js. Native semantics do the work.\n//\n// htmx note: target the <output> and swap innerHTML so the live region\n// element persists across requests (a fresh outerHTML swap would replace\n// the region each time and defeat the announcement contract above).\n//   repos/htmx/www/reference.md — hx-target, hx-swap (innerHTML).\n\nexport type OutputTone = \"default\" | \"muted\" | \"primary\" | \"destructive\"\n\nconst base =\n  \"inline-flex min-h-9 w-fit items-center gap-2 rounded-md border px-3 py-1.5 text-sm font-medium tabular-nums transition-colors \" +\n  // htmx v4 adds .htmx-request to the swap target while a request that\n  // targets it is in flight — dim the stale value so the update reads as\n  // a refresh. See repos/htmx/www/reference.md (htmx-request class).\n  \"[&.htmx-request]:opacity-60\"\n\nconst tones: Record<OutputTone, string> = {\n  default: \"border-border bg-card text-card-foreground\",\n  muted: \"border-transparent bg-muted text-muted-foreground\",\n  primary: \"border-transparent bg-primary text-primary-foreground\",\n  destructive:\n    \"border-destructive/30 bg-destructive/5 text-destructive dark:bg-destructive/10\",\n}\n\nexport function outputClasses(opts?: {\n  tone?: OutputTone\n  class?: ClassValue\n}): string {\n  const tone = opts?.tone ?? \"default\"\n  return cn(base, tones[tone], opts?.class)\n}\n\ntype OutputProps = {\n  // Space-separated list of the ids of the inputs that feed this result.\n  // Ties the output to its sources (MDN <output for>). React calls this\n  // `htmlFor`; on the native element it's the `for` attribute.\n  htmlFor?: string\n  // Associate with a <form> elsewhere in the document by its id. Without\n  // it, the output belongs to its nearest ancestor <form>, if any.\n  form?: string\n  // The element's name in the form.elements API.\n  name?: string\n  id?: string\n  tone?: OutputTone\n  class?: ClassValue\n  // The result value / content. With an htmx swap, this is the initial\n  // (often placeholder) content that gets replaced in place.\n  children?: Child\n\n  // Accessible name. <output> already announces via its implicit\n  // role=\"status\"; a name lets AT preface the value (e.g. \"Total: 42\").\n  ariaLabel?: string\n  ariaLabelledby?: string\n  ariaDescribedby?: string\n  // Override the implicit aria-atomic=\"true\" if you only want the changed\n  // bits announced (rare — leave the implicit value in most cases).\n  ariaAtomic?: boolean\n\n  // htmx — point at this <output> as the persistent target and swap its\n  // innerHTML so the live region stays put and announcements fire.\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-include\"?: string\n  \"hx-vals\"?: string\n  \"hx-indicator\"?: string\n}\n\nexport function Output(props: OutputProps) {\n  const {\n    htmlFor,\n    form,\n    name,\n    id,\n    tone,\n    class: className,\n    children,\n    ariaLabel,\n    ariaLabelledby,\n    ariaDescribedby,\n    ariaAtomic,\n    ...rest\n  } = props\n  return (\n    <output\n      id={id}\n      for={htmlFor}\n      form={form}\n      name={name}\n      data-slot=\"output\"\n      data-tone={tone ?? \"default\"}\n      aria-label={ariaLabel}\n      aria-labelledby={ariaLabelledby}\n      aria-describedby={ariaDescribedby}\n      aria-atomic={ariaAtomic === undefined ? undefined : ariaAtomic}\n      class={outputClasses({ tone, class: className })}\n      {...rest}\n    >\n      {children}\n    </output>\n  )\n}\n"
    },
    {
      "path": "registry/jinja2/output.html",
      "type": "registry:file",
      "target": "templates/components/output.html",
      "content": "{# Output macro — shadcn-htmx, htmx v4 + Tailwind v4.\n   Mirrors registry/ui/output.tsx. Renders the native <output> element\n   (implicit role=\"status\" — an aria-live=\"polite\", aria-atomic=\"true\" live\n   region), so an htmx innerHTML swap of its content is announced with no JS.\n\n   Source: repos/mdn/.../elements/output/index.md (for / form / name; implicit\n   role=\"status\"). Target THIS <output> with hx-swap=\"innerHTML\" so the live\n   region persists across requests.\n\n   Usage:\n       {% from \"components/output.html\" import output %}\n       <form hx-post=\"/cart/total\" hx-trigger=\"change, input delay:300ms\"\n             hx-target=\"#total\" hx-swap=\"innerHTML\">\n         <input name=\"qty\" value=\"1\" />\n         {% call output(id=\"total\", for=\"qty price\", tone=\"primary\") %}$0.00{% endcall %}\n       </form>\n#}\n\n{% macro output(\n    for=none,\n    form=none,\n    name=none,\n    id=none,\n    tone=\"default\",\n    aria_label=none,\n    aria_labelledby=none,\n    aria_describedby=none,\n    aria_atomic=none,\n    extra_class=\"\",\n    **attrs\n) %}\n{%- set base -%}\ninline-flex min-h-9 w-fit items-center gap-2 rounded-md border px-3 py-1.5 text-sm font-medium tabular-nums transition-colors [&.htmx-request]:opacity-60\n{%- endset -%}\n{%- set tones = {\n    \"default\": \"border-border bg-card text-card-foreground\",\n    \"muted\": \"border-transparent bg-muted text-muted-foreground\",\n    \"primary\": \"border-transparent bg-primary text-primary-foreground\",\n    \"destructive\": \"border-destructive/30 bg-destructive/5 text-destructive dark:bg-destructive/10\"\n} -%}\n<output class=\"{{ base }} {{ tones[tone] }} {{ extra_class }}\"\n       {%- if id %} id=\"{{ id }}\"{% endif %}\n       {%- if for %} for=\"{{ for }}\"{% endif %}\n       {%- if form %} form=\"{{ form }}\"{% endif %}\n       {%- if name %} name=\"{{ name }}\"{% endif %}\n       data-slot=\"output\"\n       data-tone=\"{{ tone }}\"\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       {%- if aria_atomic is not none %} aria-atomic=\"{{ aria_atomic|lower }}\"{% endif %}\n       {%- for k, v in attrs.items() %} {{ k|replace('_', '-') }}=\"{{ v }}\"{% endfor -%}\n  >{{ caller() }}</output>\n{% endmacro %}\n"
    },
    {
      "path": "registry/go-templates/output.tmpl",
      "type": "registry:file",
      "target": "components/output.tmpl",
      "content": "{{/*\n  Output template — shadcn-htmx, htmx v4 + Tailwind v4.\n  Mirrors registry/ui/output.tsx. Renders the native <output> element\n  (implicit role=\"status\" — an aria-live=\"polite\", aria-atomic=\"true\" live\n  region), so an htmx innerHTML swap of its content is announced with no JS.\n\n  Source: repos/mdn/.../elements/output/index.md (for / form / name; implicit\n  role=\"status\"). Target THIS <output> with hx-swap=\"innerHTML\" so the live\n  region persists across requests.\n\n  Usage:\n\n      {{template \"output\" (dict\n          \"ID\" \"total\" \"For\" \"qty price\" \"Tone\" \"primary\"\n          \"Attrs\" (dict \"hx-post\" \"/cart/total\" \"hx-trigger\" \"change, input delay:300ms\"\n                        \"hx-target\" \"#total\" \"hx-swap\" \"innerHTML\")\n          \"Body\" \"$0.00\")}}\n\n  Tone is one of default | muted | primary | destructive.\n*/}}\n\n{{define \"output\"}}\n{{- $base := \"inline-flex min-h-9 w-fit items-center gap-2 rounded-md border px-3 py-1.5 text-sm font-medium tabular-nums transition-colors [&.htmx-request]:opacity-60\" -}}\n{{- $tone := or .Tone \"default\" -}}\n{{- $tones := dict\n    \"default\" \"border-border bg-card text-card-foreground\"\n    \"muted\" \"border-transparent bg-muted text-muted-foreground\"\n    \"primary\" \"border-transparent bg-primary text-primary-foreground\"\n    \"destructive\" \"border-destructive/30 bg-destructive/5 text-destructive dark:bg-destructive/10\" -}}\n<output class=\"{{$base}} {{index $tones $tone}}{{if .Class}} {{.Class}}{{end}}\"\n       {{- if .ID}} id=\"{{.ID}}\"{{end}}\n       {{- if .For}} for=\"{{.For}}\"{{end}}\n       {{- if .Form}} form=\"{{.Form}}\"{{end}}\n       {{- if .Name}} name=\"{{.Name}}\"{{end}}\n       data-slot=\"output\"\n       data-tone=\"{{$tone}}\"\n       {{- if .AriaLabel}} aria-label=\"{{.AriaLabel}}\"{{end}}\n       {{- if .AriaLabelledby}} aria-labelledby=\"{{.AriaLabelledby}}\"{{end}}\n       {{- if .AriaDescribedby}} aria-describedby=\"{{.AriaDescribedby}}\"{{end}}\n       {{- if .AriaAtomic}} aria-atomic=\"{{.AriaAtomic}}\"{{end}}\n       {{- range $k, $v := .Attrs}} {{$k}}=\"{{$v}}\"{{end -}}\n  >{{htmlSafe .Body}}</output>\n{{end}}\n"
    },
    {
      "path": "registry/phoenix/output.ex",
      "type": "registry:file",
      "target": "lib/my_app_web/components/output.ex",
      "content": "defmodule ShadcnHtmx.Components.Output do\n  @moduledoc \"\"\"\n  Output — shadcn-htmx, htmx v4 + Tailwind v4 for Phoenix.\n\n  Renders the native `<output>` element (implicit `role=\"status\"`) — a live\n  result region for the outcome of a calculation or a server action, tied to\n  its inputs via the `for` attribute.\n\n  Because `role=\"status\"` is an implicit `aria-live=\"polite\"`,\n  `aria-atomic=\"true\"` live region, an htmx `innerHTML` swap of the output's\n  content is announced by assistive tech automatically — no JS needed. Target\n  THIS `<output>` and swap `innerHTML` so the live region persists across\n  requests.\n\n  Source: repos/mdn/.../elements/output/index.md (for / form / name; implicit\n  role=\"status\").\n\n  ## Examples\n\n      <form hx-post=\"/cart/total\" hx-trigger=\"change, input delay:300ms\"\n        hx-target=\"#total\" hx-swap=\"innerHTML\">\n        <input id=\"qty\" name=\"qty\" value=\"1\" />\n        <.output id=\"total\" for=\"qty price\" tone=\"primary\">$0.00</.output>\n      </form>\n\n      <.output for=\"a b\" name=\"result\">60</.output>\n  \"\"\"\n\n  use Phoenix.Component\n\n  @base \"inline-flex min-h-9 w-fit items-center gap-2 rounded-md border px-3 py-1.5 text-sm font-medium tabular-nums transition-colors [&.htmx-request]:opacity-60\"\n\n  @tones %{\n    \"default\" => \"border-border bg-card text-card-foreground\",\n    \"muted\" => \"border-transparent bg-muted text-muted-foreground\",\n    \"primary\" => \"border-transparent bg-primary text-primary-foreground\",\n    \"destructive\" => \"border-destructive/30 bg-destructive/5 text-destructive dark:bg-destructive/10\"\n  }\n\n  attr :for, :string, default: nil\n  attr :form, :string, default: nil\n  attr :name, :string, default: nil\n  attr :tone, :string, default: \"default\", values: ~w(default muted primary destructive)\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\n         hx-include hx-vals hx-indicator id aria-label aria-labelledby\n         aria-describedby aria-atomic)\n\n  slot :inner_block, required: false\n\n  def output(assigns) do\n    assigns =\n      assigns\n      |> assign(:base, @base)\n      |> assign(:tone_class, Map.fetch!(@tones, assigns.tone))\n\n    ~H\"\"\"\n    <output\n      class={[@base, @tone_class, @class]}\n      for={@for}\n      form={@form}\n      name={@name}\n      data-slot=\"output\"\n      data-tone={@tone}\n      {@rest}\n    >{render_slot(@inner_block)}</output>\n    \"\"\"\n  end\nend\n"
    },
    {
      "path": "registry/html/output.html",
      "type": "registry:file",
      "target": "snippets/output.html",
      "content": "<!--\n  shadcn-htmx — raw HTML output snippets.\n\n  Mirrors registry/ui/output.tsx. The native <output> element carries the\n  implicit role=\"status\" — an aria-live=\"polite\", aria-atomic=\"true\" live\n  region. Do NOT add role/aria-live: the browser supplies them. Start with\n  initial content, then let htmx swap the output's innerHTML in place and the\n  update is announced automatically. No JS.\n\n  Target THIS <output> with hx-swap=\"innerHTML\" so the live region element\n  persists across requests (an outerHTML swap would replace the region and\n  break the announcement contract).\n\n  Source: repos/mdn/.../elements/output/index.md (for / form / name; implicit\n  role=\"status\").\n\n  BASE: inline-flex min-h-9 w-fit items-center gap-2 rounded-md border px-3 py-1.5\n        text-sm font-medium tabular-nums transition-colors [&.htmx-request]:opacity-60\n  TONES:\n    default     border-border bg-card text-card-foreground\n    muted       border-transparent bg-muted text-muted-foreground\n    primary     border-transparent bg-primary text-primary-foreground\n    destructive border-destructive/30 bg-destructive/5 text-destructive dark:bg-destructive/10\n-->\n\n<!-- Native — output tied to its inputs via for, updated by the browser -->\n<form>\n  <input type=\"range\" id=\"o-b\" name=\"b\" value=\"50\" /> +\n  <input type=\"number\" id=\"o-a\" name=\"a\" value=\"10\" /> =\n  <output for=\"o-a o-b\" name=\"result\" data-slot=\"output\" data-tone=\"default\"\n          class=\"inline-flex min-h-9 w-fit items-center gap-2 rounded-md border border-border bg-card px-3 py-1.5 text-sm font-medium tabular-nums text-card-foreground transition-colors [&.htmx-request]:opacity-60\">60</output>\n</form>\n\n<!-- htmx — server-computed result swapped into the same live region -->\n<form hx-post=\"/cart/total\" hx-trigger=\"change, input delay:300ms\"\n      hx-target=\"#cart-total\" hx-swap=\"innerHTML\">\n  <label for=\"o-qty\" class=\"text-sm font-medium\">Quantity</label>\n  <input type=\"number\" id=\"o-qty\" name=\"qty\" value=\"1\" min=\"1\"\n         class=\"h-9 w-20 rounded-md border bg-transparent px-3 text-sm\" />\n  <output id=\"cart-total\" for=\"o-qty\" data-slot=\"output\" data-tone=\"primary\"\n          class=\"inline-flex min-h-9 w-fit items-center gap-2 rounded-md border border-transparent bg-primary px-3 py-1.5 text-sm font-medium tabular-nums text-primary-foreground transition-colors [&.htmx-request]:opacity-60\">$0.00</output>\n</form>\n"
    }
  ]
}
