{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"credit","type":"registry:component","title":"Credit","description":"\"Designed with Beamline\" for a site's footer: the Beamline mark and a quiet line, or a small badge. With your partner code, each new customer's first purchase through it earns you 30% in cash.","categories":["display"],"dependencies":["@radix-ui/react-direction@1.1.4","clsx@^2.1.1","tailwind-merge@^3.7.0"],"registryDependencies":["https://beamline.io/registry/look-prism.json"],"files":[{"path":"beamline/components/credit/credit.module.css","type":"registry:file","target":"src/ui/beamline/components/credit/credit.module.css","content":"@layer properties, theme, base, components, utilities;\n@layer components {\n  /* A credit line: quiet at rest, the name brightening and one pulse of light running up the mark's slash on hover\n     or keyboard focus. Nothing moves at rest. */\n  .credit {\n    display: inline-flex;\n    align-items: center;\n    gap: var(--pui-space-1-5);\n    color: var(--pui-ink-3);\n    font-size: var(--pui-text-xs);\n    line-height: 1;\n    text-decoration: none;\n    white-space: nowrap;\n    transition:\n      color var(--pui-motion-out) var(--pui-ease-default),\n      background-color var(--pui-motion-out) var(--pui-ease-default),\n      scale var(--pui-motion-out) var(--pui-ease-default);\n  }\n  /* hover arrives at once and leaves over the leave duration; a press is acknowledged at once */\n  .credit:hover,\n  .credit:focus-visible {\n    color: var(--pui-ink-2);\n    transition-duration: 0ms;\n  }\n  .credit:active {\n    scale: 0.97;\n    transition-duration: 0ms;\n  }\n  .name {\n    color: var(--pui-ink-2);\n    font-weight: var(--pui-weight-medium);\n    transition: color var(--pui-motion-out) var(--pui-ease-default);\n  }\n  .credit:hover .name,\n  .credit:focus-visible .name {\n    color: var(--pui-ink);\n    transition-duration: 0ms;\n  }\n  .mark {\n    display: inline-block;\n    flex: none;\n    width: 14px;\n    height: 14px;\n  }\n  .mark > :global(svg) {\n    display: block;\n    width: 100%;\n    height: 100%;\n  }\n  .md {\n    font-size: var(--pui-text-sm);\n  }\n  .md .mark {\n    width: 16px;\n    height: 16px;\n  }\n\n  /* the badge: a small tag a footer or a \"made with\" strip can hold */\n  .badge {\n    height: 24px;\n    padding-inline: var(--pui-space-2) var(--pui-space-2-5);\n    border-radius: var(--pui-radius-pill);\n  }\n  .badge:hover,\n  .badge:focus-visible {\n    background-color: var(--pui-hover);\n  }\n  .badge.md {\n    height: 28px;\n    padding-inline: var(--pui-space-2-5) var(--pui-space-3);\n  }\n\n  /* one run of light up the slash per hover or focus (the brand mark's own pulse, marketing/brand/src/motion.css), on the\n     system's clock: the motion scale stretches it, reduced or no motion leaves the mark still */\n  .credit:hover .mark :global(.bm) line:last-child,\n  .credit:focus-visible .mark :global(.bm) line:last-child {\n    stroke-dasharray: 0.22 1.2;\n    animation: credit-run calc(var(--pui-duration-considered) * 3) var(--pui-ease-in-out) 1;\n  }\n  @keyframes credit-run {\n    from {\n      stroke-dashoffset: 0.22;\n    }\n    to {\n      stroke-dashoffset: -1;\n    }\n  }\n}\n/* CSS-module composition only; visual rules stay in the component layer. */\n.badge {\n  composes: m-tag from global;\n}\n"},{"path":"beamline/components/credit/credit.tsx","type":"registry:file","target":"src/ui/beamline/components/credit/credit.tsx","content":"\"use client\";\n\nimport { useMessages } from \"../../runtime/localization\";\nimport { english as englishMessages } from \"./messages\";\nimport { useDeprecatedProp } from \"../../hooks/use-deprecated-prop\";\n\nimport { useComponentDefaults } from \"../../runtime/appearance\";\nimport type { CreditDefaults } from \"./defaults\";\nexport type { CreditDefaults } from \"./defaults\";\n\nimport { useId, useMemo } from \"react\";\nimport type { AnchorHTMLAttributes, Ref } from \"react\";\nimport { cn } from \"../../lib/utils\";\nimport { CREDIT_MARK } from \"./mark\";\nimport styles from \"./credit.module.css\";\n\n/** A partner code from beamline.io/account/referrals: a name or a random code, letters, digits, - and _. */\nconst CODE = /^[A-Za-z0-9][A-Za-z0-9_-]{1,47}$/;\nconst SITE = \"https://beamline.io\";\n\nexport interface CreditClassNames {\n  /** Classes for the root part; no override by default. */\n  root?: string;\n  /** Classes for the mark part; no override by default. */\n  mark?: string;\n  /** Classes for the label part; no override by default. */\n  label?: string;\n}\n\nexport interface CreditProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, \"href\" | \"rel\" | \"children\"> {\n  /** Your partner code (beamline.io/account/referrals). With it the link is yours: each new customer's first purchase\n   *  through it earns you 30% in cash. Without it the credit links to beamline.io. */\n  code?: string;\n  /** \"Designed with Beamline\" (default) or \"Built with Beamline\". */\n  wording?: \"designed\" | \"built\";\n  /** Footer text or a compact credit badge; defaults to text. */\n  presentation?: \"text\" | \"badge\";\n  /** Legacy presentation setting; omitted by default. @deprecated Use presentation instead. */\n  variant?: \"text\" | \"badge\";\n  /** Control size within the supported size steps. Defaults to \"sm\". */\n  size?: \"sm\" | \"md\";\n  /** Opens beamline.io in a new tab (default), so the visitor keeps your page. */\n  newTab?: boolean;\n  /** Receives the link root; no ref by default. */\n  ref?: Ref<HTMLAnchorElement>;\n  /** Classes for the named visual parts; no part overrides by default. */\n  classNames?: CreditClassNames;\n}\n\n/**\n * \"Designed with Beamline\": a credit line for a site's footer, with the Beamline mark. A plain link: no script, no\n * request of its own, nothing stored. With a code it is marked rel=\"sponsored\" (Google's rule for links that earn), and\n * without one rel=\"nofollow\" (Google's advice for footer credits). Hovering it runs one pulse of light up the mark.\n */\nexport function Credit({\n  code,\n  wording: wordingProp,\n  presentation: presentationProp,\n  variant: variantProp,\n  size: sizeProp,\n  newTab = true,\n  className,\n  classNames,\n  ref,\n  ...props\n}: CreditProps) {\n  const i18n = useMessages(\"credit\", englishMessages);\n\n  const scopeDefaults = useComponentDefaults<CreditDefaults>(\"credit\", [\"variant\", \"size\"]);\n  useDeprecatedProp(\n    \"credit\",\n    \"variant\",\n    \"presentation\",\n    variantProp !== undefined || (presentationProp === undefined && scopeDefaults.variant !== undefined),\n  );\n  const variant = presentationProp ?? variantProp ?? scopeDefaults.variant ?? \"text\";\n  const size = sizeProp ?? scopeDefaults.size ?? \"sm\";\n  const wording = wordingProp ?? \"designed\";\n  const valid = !!code && CODE.test(code);\n  // every instance gets its own gradient and mask ids, so two credits on a page never borrow each other's\n  const uid = useId().replace(/[^a-zA-Z0-9]/g, \"\");\n  const mark = useMemo(() => CREDIT_MARK.replace(/crm-/g, `${uid}-`), [uid]);\n  return (\n    <a\n      ref={ref}\n      data-slot=\"credit\"\n      data-variant={variant}\n      data-size={size}\n      href={valid ? `${SITE}/r/${code}` : SITE}\n      rel={valid ? \"sponsored noopener\" : \"nofollow noopener\"}\n      target={newTab ? \"_blank\" : undefined}\n      className={cn(styles.credit, styles[variant], styles[size], classNames?.root, className)}\n      {...props}\n    >\n      <span\n        data-slot=\"credit-mark\"\n        aria-hidden\n        className={cn(styles.mark, classNames?.mark)}\n        dangerouslySetInnerHTML={{ __html: mark }}\n      />\n      <span data-slot=\"credit-label\" className={cn(styles.label, classNames?.label)}>\n        {wording === \"built\" ? i18n(\"builtWith\") : i18n(\"designedWith\")}{\" \"}\n        <b className={styles.name}>{i18n(\"beamline\")}</b>\n        {newTab ? <span className=\"sr-only\"> {i18n(\"opensInANewTab\")}</span> : null}\n      </span>\n    </a>\n  );\n}\n\nexport default Credit;\n"},{"path":"beamline/components/credit/defaults.ts","type":"registry:file","target":"src/ui/beamline/components/credit/defaults.ts","content":"/** Pure allowed presentation defaults for Credit; no component or runtime imports. */\nexport interface CreditDefaults {\n  /** Shared presentation setting; omit to inherit the component default. Scoped defaults retain the legacy key variant. */\n  variant?: \"text\" | \"badge\";\n  /** Shared size setting; omit to inherit the surrounding scope or the component default. */\n  size?: \"sm\" | \"md\";\n}\n"},{"path":"beamline/components/credit/mark.ts","type":"registry:file","target":"src/ui/beamline/components/credit/mark.ts","content":"// generated by node marketing/brand/build.mjs — the Beamline mark (small cut) the credit carries; every instance renames its ids\nexport const CREDIT_MARK =\n  '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\" aria-hidden=\"true\"><defs><linearGradient id=\"crm-g1\" x1=\"42.5\" y1=\"84\" x2=\"57.5\" y2=\"16\" gradientUnits=\"userSpaceOnUse\"><stop offset=\"0\" stop-color=\"#fff\" stop-opacity=\"0\"/><stop offset=\"0.09\" stop-color=\"#fff\"/><stop offset=\"0.91\" stop-color=\"#fff\"/><stop offset=\"1\" stop-color=\"#fff\" stop-opacity=\"0\"/></linearGradient><mask id=\"crm-m2\" maskUnits=\"userSpaceOnUse\" x=\"-50\" y=\"-50\" width=\"200\" height=\"200\"><line x1=\"42.5\" y1=\"84\" x2=\"57.5\" y2=\"16\" stroke=\"url(#crm-g1)\" stroke-width=\"26.32\"/></mask><linearGradient id=\"crm-g3\" x1=\"42.5\" y1=\"84\" x2=\"57.5\" y2=\"16\" gradientUnits=\"userSpaceOnUse\"><stop offset=\"0\" style=\"stop-color:light-dark(transparent,rgb(255 255 255 / .35))\"/><stop offset=\"0.72\" style=\"stop-color:light-dark(transparent,#fff)\"/><stop offset=\"1\" style=\"stop-color:light-dark(transparent,rgb(255 255 255 / .55))\"/></linearGradient><linearGradient id=\"crm-g4\" x1=\"42.5\" y1=\"84\" x2=\"57.5\" y2=\"16\" gradientUnits=\"userSpaceOnUse\"><stop offset=\"0\" style=\"stop-color:light-dark(oklch(0.46 0.15 262 / 0),oklch(0.62 0.15 262 / 0.2))\"/><stop offset=\"0.42\" style=\"stop-color:light-dark(oklch(0.56 0.17 250 / 0),oklch(0.72 0.17 250 / 0.2))\"/><stop offset=\"0.55\" style=\"stop-color:light-dark(oklch(0.66 0.12 215 / 0),oklch(0.82 0.12 215 / 0.2))\"/><stop offset=\"0.63\" style=\"stop-color:light-dark(oklch(0.26 0.03 240 / 0),oklch(0.42 0.03 240 / 0.2))\"/><stop offset=\"0.69\" style=\"stop-color:light-dark(oklch(0.31 0.08 22 / 0),oklch(0.47 0.08 22 / 0.2))\"/><stop offset=\"0.8\" style=\"stop-color:light-dark(oklch(0.6 0.2 30 / 0),oklch(0.76 0.2 30 / 0.2))\"/><stop offset=\"1\" style=\"stop-color:light-dark(oklch(0.8 0.16 64 / 0),oklch(0.92 0.16 64 / 0.2))\"/></linearGradient><linearGradient id=\"crm-g5\" x1=\"42.5\" y1=\"84\" x2=\"57.5\" y2=\"16\" gradientUnits=\"userSpaceOnUse\"><stop offset=\"0\" style=\"stop-color:light-dark(oklch(0.46 0.15 262 / 1),oklch(0.62 0.15 262 / 0.92))\"/><stop offset=\"0.42\" style=\"stop-color:light-dark(oklch(0.56 0.17 250 / 1),oklch(0.72 0.17 250 / 0.92))\"/><stop offset=\"0.55\" style=\"stop-color:light-dark(oklch(0.66 0.12 215 / 1),oklch(0.82 0.12 215 / 0.92))\"/><stop offset=\"0.63\" style=\"stop-color:light-dark(oklch(0.26 0.03 240 / 1),oklch(0.42 0.03 240 / 0.92))\"/><stop offset=\"0.69\" style=\"stop-color:light-dark(oklch(0.31 0.08 22 / 1),oklch(0.47 0.08 22 / 0.92))\"/><stop offset=\"0.8\" style=\"stop-color:light-dark(oklch(0.6 0.2 30 / 1),oklch(0.76 0.2 30 / 0.92))\"/><stop offset=\"1\" style=\"stop-color:light-dark(oklch(0.8 0.16 64 / 1),oklch(0.92 0.16 64 / 0.92))\"/></linearGradient><linearGradient id=\"crm-g6\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" style=\"stop-color:light-dark(#56585d,#ffffff)\"/><stop offset=\"0.55\" style=\"stop-color:light-dark(#14161a,#d1d4da)\"/><stop offset=\"1\" style=\"stop-color:light-dark(#050608,#8c939f)\"/></linearGradient><linearGradient id=\"crm-g7\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" style=\"stop-color:light-dark(rgb(255 255 255 / 0.75),transparent)\"/><stop offset=\"0.5\" style=\"stop-color:light-dark(rgb(255 255 255 / 0.28),transparent)\"/><stop offset=\"1\" style=\"stop-color:light-dark(rgb(255 255 255 / 0.4),transparent)\"/></linearGradient><linearGradient id=\"crm-g8\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" style=\"stop-color:light-dark(#56585d,#ffffff)\"/><stop offset=\"0.55\" style=\"stop-color:light-dark(#14161a,#d1d4da)\"/><stop offset=\"1\" style=\"stop-color:light-dark(#050608,#8c939f)\"/></linearGradient><linearGradient id=\"crm-g9\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" style=\"stop-color:light-dark(rgb(255 255 255 / 0.75),transparent)\"/><stop offset=\"0.5\" style=\"stop-color:light-dark(rgb(255 255 255 / 0.28),transparent)\"/><stop offset=\"1\" style=\"stop-color:light-dark(rgb(255 255 255 / 0.4),transparent)\"/></linearGradient></defs><g class=\"lb\"><polygon points=\"25.41,19.76 -2.51,50 0.76,50 25.54,23.15\" style=\"fill:light-dark(#e8eaed,#f2f6ff);stroke:light-dark(#e8eaed,#f2f6ff)\" stroke-width=\".2\"/><polygon points=\"-2.51,50 25.41,80.24 25.54,76.85 0.76,50\" style=\"fill:light-dark(#313438,#7e8289);stroke:light-dark(#313438,#7e8289)\" stroke-width=\".2\"/><polygon points=\"25.41,80.24 34.59,71.76 31.11,71.71 25.54,76.85\" style=\"fill:light-dark(#14161b,#5a5e65);stroke:light-dark(#14161b,#5a5e65)\" stroke-width=\".2\"/><polygon points=\"34.59,71.76 12.31,50 8.87,50 31.11,71.71\" style=\"fill:light-dark(#484a4e,#91959c);stroke:light-dark(#484a4e,#91959c)\" stroke-width=\".2\"/><polygon points=\"12.31,50 34.59,28.24 31.11,28.29 8.87,50\" style=\"fill:light-dark(#14161b,#5a5e65);stroke:light-dark(#14161b,#5a5e65)\" stroke-width=\".2\"/><polygon points=\"34.59,28.24 25.41,19.76 25.54,23.15 31.11,28.29\" style=\"fill:light-dark(#4b4e52,#93979f);stroke:light-dark(#4b4e52,#93979f)\" stroke-width=\".2\"/><polygon points=\"25.54,23.15 0.76,50 25.54,76.85 31.11,71.71 8.87,50 31.11,28.29\" fill=\"url(#crm-g6)\"/><polygon points=\"25.41,19.76 -2.51,50 25.41,80.24 34.59,71.76 12.31,50 34.59,28.24\" fill=\"none\" stroke=\"url(#crm-g7)\" stroke-width=\"0.7\" stroke-linejoin=\"miter\"/></g><g class=\"rb\"><polygon points=\"65.41,28.24 87.69,50 91.13,50 68.89,28.29\" style=\"fill:light-dark(#2d2f33,#797e85);stroke:light-dark(#2d2f33,#797e85)\" stroke-width=\".2\"/><polygon points=\"87.69,50 65.41,71.76 68.89,71.71 91.13,50\" style=\"fill:light-dark(#eaecef,#f3f7ff);stroke:light-dark(#eaecef,#f3f7ff)\" stroke-width=\".2\"/><polygon points=\"65.41,71.76 74.59,80.24 74.46,76.85 68.89,71.71\" style=\"fill:light-dark(#2a2d31,#777b82);stroke:light-dark(#2a2d31,#777b82)\" stroke-width=\".2\"/><polygon points=\"74.59,80.24 102.51,50 99.24,50 74.46,76.85\" style=\"fill:light-dark(#14161b,#5a5e65);stroke:light-dark(#14161b,#5a5e65)\" stroke-width=\".2\"/><polygon points=\"102.51,50 74.59,19.76 74.46,23.15 99.24,50\" style=\"fill:light-dark(#424448,#8c9097);stroke:light-dark(#424448,#8c9097)\" stroke-width=\".2\"/><polygon points=\"74.59,19.76 65.41,28.24 68.89,28.29 74.46,23.15\" style=\"fill:light-dark(#ebedef,#f3f8ff);stroke:light-dark(#ebedef,#f3f8ff)\" stroke-width=\".2\"/><polygon points=\"68.89,28.29 91.13,50 68.89,71.71 74.46,76.85 99.24,50 74.46,23.15\" fill=\"url(#crm-g8)\"/><polygon points=\"65.41,28.24 87.69,50 65.41,71.76 74.59,80.24 102.51,50 74.59,19.76\" fill=\"none\" stroke=\"url(#crm-g9)\" stroke-width=\"0.7\" stroke-linejoin=\"miter\"/></g><g class=\"bm\" mask=\"url(#crm-m2)\"><line x1=\"42.5\" y1=\"84\" x2=\"57.5\" y2=\"16\" stroke=\"url(#crm-g4)\" stroke-width=\"15.79\" pathLength=\"1\"/><line x1=\"42.5\" y1=\"84\" x2=\"57.5\" y2=\"16\" stroke=\"url(#crm-g5)\" stroke-width=\"6.91\" pathLength=\"1\"/><line x1=\"42.5\" y1=\"84\" x2=\"57.5\" y2=\"16\" stroke=\"url(#crm-g3)\" stroke-width=\"1.6\" pathLength=\"1\"/></g></svg>';\n"},{"path":"beamline/components/credit/messages.ts","type":"registry:file","target":"src/ui/beamline/components/credit/messages.ts","content":"import type { MessageDefinitions } from \"../../runtime/messages\";\n/** Built-in English copy; stable keys are the public translation API. */\nexport const english = {\n  builtWith: \"Built with\",\n  designedWith: \"Designed with\",\n  beamline: \"Beamline\",\n  opensInANewTab: \" (opens in a new tab)\",\n} satisfies MessageDefinitions;\n"},{"path":"beamline/hooks/use-deprecated-prop.ts","type":"registry:file","target":"src/ui/beamline/hooks/use-deprecated-prop.ts","content":"\"use client\";\n\nimport { useEffect } from \"react\";\n\n// Bundlers replace NODE_ENV for browser builds; this declaration emits no Node dependency.\ndeclare const process: { env: { NODE_ENV?: string } };\n\nconst warned = new Set<string>();\n\n/** Report a legacy prop once after mount in development; production and server renders stay silent. */\nexport function useDeprecatedProp(component: string, prop: string, replacement: string, supplied: boolean) {\n  useEffect(() => {\n    if (process.env.NODE_ENV !== \"development\" || !supplied) return;\n    const key = `${component}.${prop}`;\n    if (warned.has(key)) return;\n    warned.add(key);\n    console.warn(`[Beamline] ${key} is deprecated. Use ${replacement} instead; the legacy prop still works.`);\n  }, [component, prop, replacement, supplied]);\n}\n"},{"path":"beamline/lib/utils.ts","type":"registry:file","target":"src/ui/beamline/lib/utils.ts","content":"import { clsx, type ClassValue } from \"clsx\";\nimport { extendTailwindMerge } from \"tailwind-merge\";\n\n/* The type roles (utilities.css) and the 2xs step are sizes, not text colours: without this, tailwind-merge reads\n   `text-overline` as a colour and drops it next to `text-ink-3`. A role and a size step still replace each other.\n   Likewise `font-title` is the look's title weight, not a family: read as a family it dropped `font-display` beside it,\n   and every merged page title lost the look's display face. */\nconst twMerge = extendTailwindMerge({\n  extend: {\n    classGroups: {\n      \"font-weight\": [{ font: [\"title\"] }],\n      \"font-family\": [{ font: [\"display\", \"numeric\", \"figure\"] }],\n      \"font-size\": [\n        {\n          text: [\n            \"2xs\",\n            \"display\",\n            \"title\",\n            \"heading\",\n            \"subheading\",\n            \"body\",\n            \"body-sm\",\n            \"reading\",\n            \"label\",\n            \"caption\",\n            \"overline\",\n            \"numeric\",\n            \"code\",\n          ],\n        },\n      ],\n    },\n  },\n});\n\n/** Merge class names, letting later Tailwind utilities win over earlier ones. */\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n\n/** A small seeded random generator (LCG): the same seed gives the same sequence, for stable sample data and jitter. */\nexport function rng(seed = 1) {\n  let s = seed >>> 0 || 1;\n  return () => (s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32;\n}\n\nexport const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));\n"}],"docs":"Credit is in src/ui/beamline/components/credit/credit.tsx. The source is in src/ui/beamline (ui/beamline in a project without src/). After Tailwind, import its styles/beamline.css once, then styles/look-prism.css in your app stylesheet (use relative paths). Keep the shared base before all chosen looks. Wrap your app in <UIRoot preset={prism}> with UIRoot from its runtime/appearance and prism from its looks/prism.  shadcn replaces files when asked; use the Beamline managed-source CLI from the start for three-way upgrades that preserve edits.","meta":{"free":true,"version":"0.2.0-rc.42","library":"https://beamline.io/components/credit"}}