{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "toggle",
  "title": "Toggle",
  "type": "registry:ui",
  "description": "On/off button (different from Switch — this is shaped like a button and lives in toolbars). Three sizes, two variants. Use inside Toggle Group or standalone for \"press to enable\" buttons like bold / italic.",
  "categories": [
    "action"
  ],
  "files": [
    {
      "path": "packages/elements-lit/src/components/toggle/toggle.ts",
      "content": "import { LitElement, css, html, nothing } from 'lit'\nimport { cn } from '../../lib/utils'\nimport { tailwind } from '../../lib/styles'\nimport { ThemeController } from '../../lib/theme'\nimport { toggleVariants, type ToggleVariants } from './toggle.variants'\n\ntype Variant = NonNullable<ToggleVariants['variant']>\ntype Size = NonNullable<ToggleVariants['size']>\n\n/**\n * <uip-toggle> — the registry Toggle as a web component.\n *\n * Class strings are React's `toggleVariants` verbatim; the inner <button>\n * carries `aria-pressed` and `data-state=\"on|off\"` like Radix's Toggle.\n * Slotted icons get React's `[&_svg]:…` sizing via `::slotted(svg)` on the\n * <slot> (a light-DOM <svg> isn't a descendant of the inner button).\n *\n * `pressed` property = live state; the `pressed` attribute is the initial\n * state (and drives it when changed). Form-associated: submits `name=value`\n * (value defaults to \"on\") while pressed, resets with its form.\n *\n * Events: `pressed-change` (React's `onPressedChange`, `detail` = `{ pressed }`,\n * the requested new state) fires first and is cancelable; then, unless it was\n * cancelled, `.pressed` flips and `input` + `change` fire (read `.pressed`).\n *\n * Controlled mode (React's `pressed` + `onPressedChange`): call\n * `preventDefault()` in the `pressed-change` listener and set `.pressed`\n * yourself — the toggle then only changes when you say so:\n *   toggle.addEventListener('pressed-change', (e) => {\n *     e.preventDefault()\n *     if (allowed) toggle.pressed = e.detail.pressed\n *   })\n */\nexport class UipToggle extends LitElement {\n  static formAssociated = true\n  static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true }\n  // :host display can't be a utility class (the host has no template of its own).\n  static styles = [tailwind, css`:host { display: inline-flex; }`]\n\n  static properties = {\n    pressed: { type: Boolean, attribute: false },\n    defaultPressed: { type: Boolean, attribute: 'pressed' },\n    variant: { reflect: true },\n    size: { reflect: true },\n    disabled: { type: Boolean, reflect: true },\n    name: { reflect: true },\n    value: {},\n    accessibleLabel: { attribute: 'aria-label' },\n  }\n\n  pressed = false\n  defaultPressed = false\n  variant: Variant = 'default'\n  size: Size = 'default'\n  disabled = false\n  name?: string\n  value = 'on'\n  accessibleLabel?: string\n\n  private internals = this.attachInternals()\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'toggle')\n  }\n\n  protected willUpdate(changed: Map<string, unknown>) {\n    if (changed.has('defaultPressed') && (this.hasUpdated || this.defaultPressed)) this.pressed = this.defaultPressed\n    this.setAttribute('data-state', this.pressed ? 'on' : 'off')\n    if (changed.has('pressed') || changed.has('value')) this.internals.setFormValue(this.pressed ? this.value : null)\n  }\n\n  formResetCallback() {\n    this.pressed = this.defaultPressed\n  }\n  formDisabledCallback(disabled: boolean) {\n    this.disabled = disabled\n  }\n\n  private onClick() {\n    if (this.disabled) return\n    const pressed = !this.pressed\n    const ok = this.dispatchEvent(\n      new CustomEvent('pressed-change', { detail: { pressed }, bubbles: true, composed: true, cancelable: true }),\n    )\n    if (!ok) return\n    this.pressed = pressed\n    this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n    this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n  }\n\n  render() {\n    return html`<button\n      part=\"base\"\n      type=\"button\"\n      data-uipkge=\"\"\n      data-slot=\"toggle\"\n      class=${cn(toggleVariants({ variant: this.variant, size: this.size }))}\n      aria-pressed=${this.pressed ? 'true' : 'false'}\n      aria-label=${this.accessibleLabel ?? nothing}\n      data-state=${this.pressed ? 'on' : 'off'}\n      ?data-disabled=${this.disabled}\n      ?disabled=${this.disabled}\n      @click=${this.onClick}\n    >\n      <slot\n        class=\"[&::slotted(svg)]:pointer-events-none [&::slotted(svg)]:shrink-0 [&::slotted(svg:not([class*='size-']))]:size-4\"\n      ></slot>\n    </button>`\n  }\n}\n\ncustomElements.get('uip-toggle') || customElements.define('uip-toggle', UipToggle)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-toggle': UipToggle\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/components/toggle/toggle.ts"
    },
    {
      "path": "packages/elements-lit/src/components/toggle/toggle.variants.ts",
      "content": "import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\n/**\n * Variant definitions live in their own file (rather than the package\n * `index.ts`) so consuming Vue SFCs can import without creating a circular\n * dependency through the index. See card.variants.ts for the canonical\n * example + the SSR symptom that motivated the split.\n */\n\nexport const toggleVariants = cva(\n  // Focus ring intentionally subtle: 1px ring on :focus-visible only, no\n  // border swap. Keyboard users still get a clear focused state; mouse\n  // users don't see a loud 3px halo after click (which lingers because\n  // the button retains focus). Matches the segmented-control density.\n  \"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:ring-ring/50 focus-visible:ring-1 outline-none transition-colors duration-200 whitespace-nowrap\",\n  {\n    variants: {\n      variant: {\n        default: 'bg-transparent',\n        outline: 'border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground',\n      },\n      size: {\n        default: 'h-9 px-2 min-w-9',\n        sm: 'h-8 px-1.5 min-w-8',\n        lg: 'h-10 px-2.5 min-w-10',\n      },\n    },\n    defaultVariants: {\n      variant: 'default',\n      size: 'default',\n    },\n  },\n)\n\nexport type ToggleVariants = VariantProps<typeof toggleVariants>\n",
      "type": "registry:file",
      "target": "src/uip/components/toggle/toggle.variants.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority",
    "lit"
  ],
  "registryDependencies": [
    "https://uipkge.dev/r/lit/lit-core.json"
  ]
}
