{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "flag",
  "title": "Flag",
  "author": "Shadi Al Milhem",
  "description": "An accessible no-crop flag image primitive with 4:3, 1:1, and original frames in SVG, PNG, WebP, or JPEG.",
  "files": [
    {
      "path": "src/components/flags/flag.tsx",
      "content": "// SPDX-License-Identifier: MIT\n\nimport * as React from \"react\"\n\nimport {\n  getFlagSrcSet,\n  getFlagUrl,\n  type FlagFormat,\n  type FlagRatio,\n  type FlagWidth,\n} from \"./flag-utils\"\n\nexport interface FlagProps\n  extends Omit<React.ComponentProps<\"img\">, \"src\" | \"srcSet\" | \"width\" | \"height\"> {\n  code: string\n  format?: FlagFormat\n  width?: FlagWidth | number\n  ratio?: FlagRatio\n  decorative?: boolean\n}\n\nexport function Flag({\n  code,\n  format = \"svg\",\n  width = 80,\n  ratio = \"4x3\",\n  decorative = false,\n  alt,\n  loading = \"lazy\",\n  decoding = \"async\",\n  sizes,\n  className,\n  style,\n  ...props\n}: FlagProps) {\n  const src = getFlagUrl(code, { format, width, ratio })\n  const srcSet = getFlagSrcSet(code, { format, width, ratio })\n  const normalizedCode = code.trim().toLowerCase()\n  const height = ratio === \"original\" ? undefined : ratio === \"1x1\" ? width : Math.round(width * 0.75)\n  const aspectRatio = ratio === \"original\" ? undefined : ratio === \"1x1\" ? \"1 / 1\" : \"4 / 3\"\n\n  return (\n    <img\n      {...props}\n      data-slot=\"flag\"\n      data-code={normalizedCode}\n      data-format={format}\n      data-ratio={ratio}\n      src={src}\n      srcSet={srcSet}\n      sizes={sizes ?? (srcSet ? `${width}px` : undefined)}\n      width={width}\n      height={height}\n      alt={decorative ? \"\" : (alt ?? `${code.toUpperCase()} flag`)}\n      aria-hidden={decorative || undefined}\n      loading={loading}\n      decoding={decoding}\n      className={className}\n      style={{\n        display: \"block\",\n        maxWidth: \"100%\",\n        ...(aspectRatio ? {\n          aspectRatio,\n          objectFit: \"contain\" as const,\n          objectPosition: \"center\" as const,\n        } : {}),\n        ...style,\n      }}\n    />\n  )\n}\n",
      "type": "registry:ui",
      "target": "@components/flags/flag.tsx"
    },
    {
      "path": "src/components/flags/flag-utils.ts",
      "content": "// SPDX-License-Identifier: MIT\n\nexport const flagFormats = [\"svg\", \"png\", \"webp\", \"jpg\"] as const\nexport type FlagFormat = (typeof flagFormats)[number]\n\nexport const flagIconsVersion = \"7.5.0\" as const\nexport const flagIconsBaseUrl = `https://cdn.jsdelivr.net/npm/flag-icons@${flagIconsVersion}/flags`\n\nexport const flagWidths = [20, 40, 80, 160, 320, 640, 1280, 2560] as const\nexport type FlagWidth = (typeof flagWidths)[number]\nexport const flagRatios = [\"4x3\", \"1x1\", \"original\"] as const\nexport type FlagRatio = (typeof flagRatios)[number]\n\nexport interface FlagUrlOptions {\n  format?: FlagFormat\n  width?: FlagWidth | number\n  ratio?: FlagRatio\n}\n\nexport interface FlagAssetInfo {\n  provider: \"Flag Icons\" | \"FlagCDN\"\n  kind: \"vector\" | \"raster\"\n  sourceProportions: boolean\n  label: string\n}\n\nexport function getFlagAssetInfo(code: string, options: FlagUrlOptions = {}): FlagAssetInfo {\n  const normalizedCode = normalizeFlagCode(code)\n  const format = options.format ?? \"svg\"\n  const ratio = options.ratio ?? \"4x3\"\n  const usesFlagIcons = format === \"svg\" && ratio !== \"original\" && !normalizedCode.startsWith(\"us-\")\n  const provider = usesFlagIcons ? \"Flag Icons\" : \"FlagCDN\"\n  const kind = format === \"svg\" ? \"vector\" : \"raster\"\n  const sourceProportions = !usesFlagIcons\n\n  return {\n    provider,\n    kind,\n    sourceProportions,\n    label: `${provider} · ${format.toUpperCase()}${ratio === \"original\" ? \" · source proportions\" : \"\"}`,\n  }\n}\n\nexport function normalizeFlagCode(code: string) {\n  const normalizedCode = code.trim().toLowerCase()\n  if (!/^[a-z]{2}(?:-[a-z0-9]{2,3})?$/.test(normalizedCode)) {\n    throw new Error(`Invalid flag code: ${code}`)\n  }\n  return normalizedCode\n}\n\nexport function getFlagUrl(code: string, options: FlagUrlOptions = {}) {\n  const normalizedCode = normalizeFlagCode(code)\n  const format = options.format ?? \"svg\"\n  const width = options.width ?? 80\n  const ratio = options.ratio ?? \"4x3\"\n  validateFlagWidth(width)\n\n  if (format === \"svg\") {\n    if (ratio !== \"original\" && !normalizedCode.startsWith(\"us-\")) {\n      return `${flagIconsBaseUrl}/${ratio}/${normalizedCode}.svg`\n    }\n\n    return `https://flagcdn.com/${normalizedCode}.svg`\n  }\n\n  return `https://flagcdn.com/w${atLeastWidth(width, flagWidths)}/${normalizedCode}.${format}`\n}\n\nexport function getFlagSrcSet(code: string, options: FlagUrlOptions = {}) {\n  const format = options.format ?? \"svg\"\n  if (format === \"svg\") return undefined\n\n  const ratio = options.ratio ?? \"4x3\"\n  const requestedWidth = options.width ?? 80\n  validateFlagWidth(requestedWidth)\n  const candidates = [requestedWidth, requestedWidth * 2, requestedWidth * 3]\n    .map((candidate) => atLeastWidth(candidate, flagWidths))\n    .filter((candidate, index, values) => values.indexOf(candidate) === index)\n\n  return candidates\n    .map((candidate) => `${getFlagUrl(code, { ...options, width: candidate, ratio })} ${candidate}w`)\n    .join(\", \")\n}\n\nfunction atLeastWidth<T extends number>(width: number, widths: readonly T[]): T {\n  return widths.find((candidate) => candidate >= width) ?? widths.at(-1)!\n}\n\nfunction validateFlagWidth(width: number) {\n  if (!Number.isFinite(width) || width <= 0) {\n    throw new Error(`Flag width must be a positive finite number: ${width}`)\n  }\n}\n",
      "type": "registry:lib",
      "target": "@components/flags/flag-utils.ts"
    }
  ],
  "meta": {
    "formats": [
      "svg",
      "png",
      "webp",
      "jpg"
    ],
    "ratios": [
      "4x3",
      "1x1",
      "original"
    ],
    "license": "MIT",
    "artworkLicense": "Public Domain",
    "source": "https://flagpedia.net",
    "cdn": "https://flagcdn.com"
  },
  "docs": "Pass a supported flag code and meaningful alt text. Use ratio='1x1' for square UI or ratio='4x3' for consistent cards. Set decorative when nearby text already names the place.",
  "categories": [
    "flags",
    "images",
    "internationalization"
  ],
  "type": "registry:ui"
}