{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "link",
  "title": "Link",
  "description": "Styled anchor with animated underline; external links get rel/target and a screen-reader suffix.",
  "dependencies": [
    "@radix-ui/react-slot",
    "class-variance-authority"
  ],
  "registryDependencies": [
    "@hjconsult/lib",
    "@hjconsult/a11y"
  ],
  "files": [
    {
      "path": "src/components/link/index.tsx",
      "content": "import { Slot, Slottable } from '@radix-ui/react-slot'\nimport type { ComponentPropsWithoutRef, Ref } from 'react'\nimport { VisuallyHidden } from '../../a11y/visually-hidden'\nimport { cn, cva, focusRing, type VariantProps } from '../../lib'\n\nexport const linkVariants = cva(\n  [\n    'rounded-sm font-medium',\n    'bg-[linear-gradient(currentColor,currentColor)] bg-no-repeat bg-position-[0_100%]',\n    'motion-safe:transition-[background-size,color] motion-safe:duration-[var(--hj-motion-duration-base)] motion-safe:ease-out-expo',\n    focusRing,\n  ],\n  {\n    variants: {\n      variant: {\n        /** Links inside running text: always underlined, thickens on hover. */\n        inline: 'text-fg bg-size-[100%_1px] hover:bg-size-[100%_2px] hover:text-accent',\n        /** Navigation: quiet until hovered, the underline draws in; current page stays underlined. */\n        nav: [\n          'text-fg-muted bg-size-[0%_1px] hover:bg-size-[100%_1px] hover:text-fg focus-visible:bg-size-[100%_1px]',\n          'aria-[current=page]:bg-size-[100%_1px] aria-[current=page]:text-fg',\n        ],\n        /** No underline at all; colour shift only. */\n        quiet: 'text-fg-muted bg-none hover:text-fg',\n      },\n    },\n    defaultVariants: { variant: 'inline' },\n  },\n)\n\nconst EXTERNAL_HREF = /^(?:[a-z][a-z0-9+.-]*:)?\\/\\//i\n\nexport interface LinkProps\n  extends ComponentPropsWithoutRef<'a'>,\n    VariantProps<typeof linkVariants> {\n  /** Render the child element (e.g. Next.js `Link`) with link styling. */\n  asChild?: boolean\n  /** Force external treatment. Auto-detected for absolute `http(s)://` and `//` hrefs. */\n  external?: boolean\n  /** Screen-reader suffix announced for external links. Localise per locale. */\n  externalLabel?: string\n  ref?: Ref<HTMLAnchorElement>\n}\n\nexport function Link({\n  asChild = false,\n  variant,\n  external,\n  externalLabel = 'opens in a new tab',\n  href,\n  target,\n  rel,\n  className,\n  children,\n  ref,\n  ...props\n}: LinkProps) {\n  const Comp = (asChild ? Slot : 'a') as 'a'\n  const isExternal = external ?? (typeof href === 'string' && EXTERNAL_HREF.test(href))\n  const relTokens = new Set((rel ?? '').split(/\\s+/).filter(Boolean))\n  if (isExternal) {\n    relTokens.add('noopener')\n    relTokens.add('noreferrer')\n  }\n  const resolvedRel = relTokens.size ? [...relTokens].join(' ') : undefined\n  return (\n    <Comp\n      ref={ref}\n      href={href}\n      target={isExternal ? (target ?? '_blank') : target}\n      rel={resolvedRel}\n      className={cn(linkVariants({ variant }), className)}\n      {...props}\n    >\n      <Slottable>{children}</Slottable>\n      {isExternal ? <VisuallyHidden>, {externalLabel}</VisuallyHidden> : null}\n    </Comp>\n  )\n}\n",
      "type": "registry:ui",
      "target": "hj/components/link/index.tsx"
    }
  ],
  "cssVars": {},
  "type": "registry:ui"
}