{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "action-pill",
  "type": "registry:ui",
  "title": "ActionPill",
  "description": "Toolbar pill of icon actions that collapse and grow as the page's context changes.",
  "categories": [
    "navigation",
    "actions"
  ],
  "dependencies": [
    "class-variance-authority"
  ],
  "registryDependencies": [
    "https://whiskeyjack.net/r/utils.json"
  ],
  "files": [
    {
      "path": "components/ui/action-pill.tsx",
      "type": "registry:ui",
      "target": "components/ui/action-pill.tsx",
      "content": "import * as React from 'react'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from '@/lib/utils'\n\n// ------------------------------------------------------------------\n// ActionPill container\n// ------------------------------------------------------------------\n\nconst actionPillVariants = cva(\n  [\n    'flex items-center rounded-full',\n    'shadow-sm overflow-hidden transition-all duration-300',\n  ],\n  {\n    variants: {\n      size: {\n        // Desktop rides the shared control scale so a pill sits level with a\n        // Button beside it; mobile stays a thumb-sized bar of its own.\n        desktop: 'h-control-md',\n        mobile: 'h-12',\n      },\n      variant: {\n        accent:\n          'bg-[var(--color-accent-500)] text-[var(--color-accent-foreground-icon)] shadow-sm-bevel-accent',\n        surface: [\n          'wj-frost',\n          'border border-[var(--color-border-light)] dark:border-[var(--color-border-dark)]',\n          'text-[var(--color-accent-500)]',\n        ].join(' '),\n      },\n    },\n    defaultVariants: {\n      size: 'desktop',\n      variant: 'accent',\n    },\n  }\n)\n\nexport type ActionPillVariant = 'accent' | 'surface'\n\nexport interface ActionPillProps\n  extends React.HTMLAttributes<HTMLDivElement>,\n    VariantProps<typeof actionPillVariants> {}\n\n/**\n * Pill container for icon-only toolbar action buttons. Place one or more\n * `ActionPillButton` children inside.\n *\n * Size variants: `desktop` (the shared 42px control height) and `mobile`\n * (h-12 / 48px, buttons w-12).\n *\n * Visual variants: `accent` (the primary pill -- accent bg, foreground-icon\n * color) and `surface` (secondary pill -- frosted surface bg + border with\n * accent-colored icons, same chrome as BottomNav / elevated toolbar panes).\n * Use `accent` for THE primary action group and `surface` for secondary\n * groups sitting next to it, so adjacent pills read as distinct groups\n * rather than one blob. Pass the same `variant` to the buttons inside.\n */\nconst ActionPill = React.forwardRef<HTMLDivElement, ActionPillProps>(\n  ({ className, size, variant, ...props }, ref) => (\n    <div\n      ref={ref}\n      className={cn(actionPillVariants({ size, variant }), className)}\n      {...props}\n    />\n  )\n)\nActionPill.displayName = 'ActionPill'\n\n// ------------------------------------------------------------------\n// Shared button hover/active treatment per pill variant\n// ------------------------------------------------------------------\n\nconst buttonVariantClasses: Record<ActionPillVariant, string> = {\n  accent:\n    'hover:bg-[var(--color-accent-foreground-icon-10)] active:bg-[var(--color-accent-foreground-icon-20)]',\n  surface:\n    'hover:bg-[var(--color-warm-100)] dark:hover:bg-[var(--color-neutral-800)] active:bg-[var(--color-warm-200)] dark:active:bg-[var(--color-neutral-700)] disabled:opacity-50 disabled:pointer-events-none',\n}\n\n// ------------------------------------------------------------------\n// ActionPillButton\n// ------------------------------------------------------------------\n\nexport interface ActionPillButtonProps\n  extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n  /** When false the button collapses to w-0 and becomes inert. */\n  visible?: boolean\n  /**\n   * ReactNode icon to render. The DS avoids bundling an icon library so the\n   * caller passes the icon element directly (e.g. `<Plus size={20} weight=\"bold\" />`).\n   */\n  icon: React.ReactNode\n  /** aria-label and title for the button -- required for accessibility. */\n  label: string\n  /** Size variant inherited from the parent ActionPill (pass through explicitly). */\n  size?: 'desktop' | 'mobile'\n  /** Visual variant inherited from the parent ActionPill (pass through explicitly). */\n  variant?: ActionPillVariant\n}\n\n/**\n * Icon-only button for use inside `ActionPill`.\n *\n * When `visible` is `false` the button collapses to zero width, opacity 0, and\n * `pointer-events-none` with `tabIndex=-1` so it is completely inert. This is\n * the morphing-pill pattern: the pill container shrinks/grows as context-\n * dependent buttons are shown or hidden.\n *\n * `label` is applied as both `aria-label` and `title` (tooltip on hover).\n */\nconst ActionPillButton = React.forwardRef<HTMLButtonElement, ActionPillButtonProps>(\n  ({ visible = true, icon, label, size = 'desktop', variant = 'accent', className, ...props }, ref) => {\n    const dim = size === 'mobile' ? 'w-12 h-12' : 'w-control-md h-control-md'\n    return (\n      <button\n        ref={ref}\n        aria-label={label}\n        title={label}\n        tabIndex={visible ? 0 : -1}\n        className={cn(\n          'wj-focus-ring flex items-center justify-center',\n          buttonVariantClasses[variant],\n          'transition-all',\n          // Asymmetric on purpose. A button that is LEAVING is never the one\n          // being aimed at, so the collapse keeps the full 300ms and reads as\n          // the pill closing up. A button ARRIVING is exactly what gets\n          // clicked, and while its width animates its hit box is narrower\n          // than where it will land -- a click aimed at the final position\n          // falls through to the page and is lost. Expanding in half the time\n          // puts the target under the pointer before a hand can reach it.\n          visible\n            ? `${dim} opacity-100 duration-150`\n            : 'w-0 opacity-0 pointer-events-none duration-300',\n          className\n        )}\n        {...props}\n      >\n        {icon}\n      </button>\n    )\n  }\n)\nActionPillButton.displayName = 'ActionPillButton'\n\n// ------------------------------------------------------------------\n// ActionPillStaticButton -- always-visible button (no collapse)\n// ------------------------------------------------------------------\n\nexport interface ActionPillStaticButtonProps\n  extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n  icon: React.ReactNode\n  label: string\n  size?: 'desktop' | 'mobile'\n  variant?: ActionPillVariant\n}\n\n/**\n * Always-visible button for use inside `ActionPill`. Use for actions that are\n * never context-dependent (e.g., the \"+\" create button). Contrast with\n * `ActionPillButton` which can be hidden.\n */\nconst ActionPillStaticButton = React.forwardRef<HTMLButtonElement, ActionPillStaticButtonProps>(\n  ({ icon, label, size = 'desktop', variant = 'accent', className, ...props }, ref) => {\n    const dim = size === 'mobile' ? 'w-12 h-12' : 'w-control-md h-control-md'\n    return (\n      <button\n        ref={ref}\n        aria-label={label}\n        title={label}\n        className={cn(\n          'wj-focus-ring flex items-center justify-center',\n          dim,\n          buttonVariantClasses[variant],\n          'transition-colors',\n          className\n        )}\n        {...props}\n      >\n        {icon}\n      </button>\n    )\n  }\n)\nActionPillStaticButton.displayName = 'ActionPillStaticButton'\n\nexport { ActionPill, ActionPillButton, ActionPillStaticButton, actionPillVariants }\n"
    }
  ],
  "docs": "Contextual actions (ActionPillButton) collapse away when they do not apply; the primary action (ActionPillStaticButton) stays put. Use size=\"mobile\" in the bottom bar and size=\"desktop\" in the header, with the same buttons in the same order. Pass the same `variant` to the pill and the buttons inside it.",
  "meta": {
    "group": "navigation",
    "related": [
      "bottom-nav",
      "mobile-bottom-nav"
    ],
    "exports": [
      "ActionPill",
      "ActionPillButton",
      "ActionPillStaticButton"
    ],
    "siteSlug": "action-pill"
  }
}
