# Flag

A country flag as a circular mark, drawn from vendored artwork so it is consistent across platforms and controllable in a flat interface.

Version: 0.2.0-preview.1 (preview)
Content digest: 70ac710f4d22dce040e7ac593379ba8aca9dcb59a0b9c0f839819cae0e2bb721
Contract kind: source-derived
Source: packages/design-system/src/components/primitives/Flag/Flag.tsx

## Guidance

{
  "summary": "A country flag as a circular mark, drawn from vendored artwork so it is consistent across platforms and controllable in a flat interface.",
  "guidance": [
    {
      "heading": "Overview",
      "body": "A country flag as a circular mark, keyed by ISO 3166-1 alpha-2 code."
    },
    {
      "heading": "Why not the flag emoji",
      "body": "Emoji flags are drawn by the platform, so they arrive glossy and waving in an otherwise flat\ninterface, at a size and baseline the design system cannot control. On iOS they at least render;\non Windows there are no flag glyphs at all and the browser falls back to showing the two letters.\n\nThe deciding reason here is internal consistency: the iOS app has always shipped circular flag\nartwork, so emoji in the webview made one product look like two."
    },
    {
      "heading": "Use it for places, not languages",
      "body": "A flag names a country or a server location. It does not name a language — the same language spans\nmany countries, and picking one country's flag for it is both inaccurate and easy to take badly.\n\nKeep the place named in text beside the mark. A flag on its own is ambiguous to plenty of readers,\nwhich is also why `label` is optional: when the adjacent text already says \"Amsterdam\", the flag is\ndecorative and should be hidden from assistive technology rather than announced twice."
    },
    {
      "heading": "The ring is load-bearing",
      "body": "Several flags are mostly white — Japan, Poland, Finland. Without a hairline ring they dissolve into\na light card, and Japan in particular reads as a red dot floating in space."
    },
    {
      "heading": "Codes come from payloads",
      "body": "`code` is a plain string rather than a union, because country codes arrive from backend responses\nand native bridge payloads. An unrecognized code renders nothing, so a country we have no artwork\nfor costs a mark instead of breaking the row. Narrow with `isFlagCode` when the caller wants to\nrender a fallback instead."
    },
    {
      "heading": "Artwork",
      "body": "Vendored from [circle-flags](https://github.com/HatScripts/circle-flags) (MIT), covering the 89\ncountries the VPN catalog can return. `scripts/generateFlagRegistry.ts` bakes them into shape data\nat build time, stripping each source file's circular mask — every file uses the same mask id, so\ninlining two would make the second resolve against the first. `Flag` restores the circle with\n`border-radius`, which is also what makes room for the ring. The whole set is 89 flags in 329\nshapes, about 10 KB gzipped."
    }
  ],
  "usage": [],
  "examples": [],
  "source": "packages/design-system/src/components/primitives/Flag/Flag.mdx",
  "status": "local-preview",
  "note": "Guidance is read from local documentation, originally imported from the frozen source. Platform assertions in guidance are not parity verification."
}

## Contract and public API

```json
{
  "props": [
    {
      "name": "code",
      "type": "string",
      "declared_type": "string",
      "required": true,
      "description": "ISO 3166-1 alpha-2 country code, any casing. Deliberately a plain string: codes arrive from\nbackend and bridge payloads, and an unknown one renders nothing rather than breaking a row.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Flag/Flag.tsx",
        "line": 23
      }
    },
    {
      "name": "size",
      "type": "FlagSize",
      "declared_type": "FlagSize",
      "required": true,
      "description": "Rendered scale.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Flag/Flag.tsx",
        "line": 25
      },
      "values": [
        "lg",
        "md",
        "sm"
      ],
      "values_complete": true
    },
    {
      "name": "label",
      "type": "string | undefined",
      "declared_type": "string",
      "required": false,
      "description": "Accessible label, e.g. the country name. Without one the flag is hidden from assistive tech —\ncorrect when the country is already named in adjacent text, which is the common case.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Flag/Flag.tsx",
        "line": 30
      }
    },
    {
      "name": "className",
      "type": "string | undefined",
      "declared_type": "string",
      "required": false,
      "description": "",
      "source": {
        "path": "packages/design-system/src/components/primitives/Flag/Flag.tsx",
        "line": 31
      }
    },
    {
      "name": "testId",
      "type": "string | undefined",
      "declared_type": "string",
      "required": false,
      "description": "Optional test selector.",
      "source": {
        "path": "packages/design-system/src/components/primitives/Flag/Flag.tsx",
        "line": 33
      }
    }
  ],
  "prop_shapes": {},
  "api": {
    "method": "typescript-type-checker",
    "typescript_version": "5.9.3",
    "public_export": "Flag",
    "public_path": "Flag",
    "import_path": "@cloakedinc/design-system",
    "root_kind": "component",
    "entrypoints": [
      {
        "name": "Flag",
        "signature": 1,
        "visibility": "public",
        "source": {
          "path": "packages/design-system/src/components/primitives/Flag/Flag.tsx",
          "line": 58
        },
        "props_type": "FlagProps",
        "props": [
          {
            "name": "code",
            "type": "string",
            "declared_type": "string",
            "required": true,
            "description": "ISO 3166-1 alpha-2 country code, any casing. Deliberately a plain string: codes arrive from\nbackend and bridge payloads, and an unknown one renders nothing rather than breaking a row.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Flag/Flag.tsx",
              "line": 23
            }
          },
          {
            "name": "size",
            "type": "FlagSize",
            "declared_type": "FlagSize",
            "required": true,
            "description": "Rendered scale.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Flag/Flag.tsx",
              "line": 25
            },
            "values": [
              "lg",
              "md",
              "sm"
            ],
            "values_complete": true
          },
          {
            "name": "label",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "Accessible label, e.g. the country name. Without one the flag is hidden from assistive tech —\ncorrect when the country is already named in adjacent text, which is the common case.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Flag/Flag.tsx",
              "line": 30
            }
          },
          {
            "name": "className",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "",
            "source": {
              "path": "packages/design-system/src/components/primitives/Flag/Flag.tsx",
              "line": 31
            }
          },
          {
            "name": "testId",
            "type": "string | undefined",
            "declared_type": "string",
            "required": false,
            "description": "Optional test selector.",
            "source": {
              "path": "packages/design-system/src/components/primitives/Flag/Flag.tsx",
              "line": 33
            }
          }
        ],
        "branches": [],
        "constraints": [],
        "extraction_status": "extracted"
      }
    ],
    "type_sources": [
      {
        "path": "packages/design-system/src/components/primitives/Flag/Flag.tsx",
        "line": 13,
        "name": "FlagSize",
        "code": "export type FlagSize = 'sm' | 'md' | 'lg';"
      },
      {
        "path": "packages/design-system/src/components/primitives/Flag/Flag.tsx",
        "line": 18,
        "name": "FlagProps",
        "code": "export interface FlagProps {\n    /**\n     * ISO 3166-1 alpha-2 country code, any casing. Deliberately a plain string: codes arrive from\n     * backend and bridge payloads, and an unknown one renders nothing rather than breaking a row.\n     */\n    code: string;\n    /** Rendered scale. */\n    size: FlagSize;\n    /**\n     * Accessible label, e.g. the country name. Without one the flag is hidden from assistive tech —\n     * correct when the country is already named in adjacent text, which is the common case.\n     */\n    label?: string;\n    className?: string;\n    /** Optional test selector. */\n    testId?: string;\n}"
      }
    ],
    "defaults_policy": "Only explicit destructured parameter initializers are extracted. Other runtime defaults remain in implementation source."
  },
  "spec": {
    "$schema": "../../../specs/component-spec.schema.json",
    "identity": {
      "name": "Flag",
      "status": "preview",
      "version": "1.0.0",
      "summary": "A country flag as a circular mark, drawn from vendored artwork so it is consistent across platforms and controllable in a flat interface."
    },
    "parts": [
      {
        "id": "artwork",
        "label": "Artwork",
        "description": "The flag geometry, clipped to a circle.",
        "required": true
      },
      {
        "id": "ring",
        "label": "Ring",
        "description": "Hairline inset ring that keeps white-field flags from dissolving into a light surface.",
        "required": true
      }
    ],
    "allowedDesignChoices": {
      "variants": {
        "base": {
          "label": "Base",
          "description": "One treatment; the country changes the artwork, not the styling."
        }
      },
      "sizes": {
        "sm": {
          "label": "Small",
          "description": "Inline with body and body-small text."
        },
        "md": {
          "label": "Medium",
          "description": "List rows and compact headers."
        },
        "lg": {
          "label": "Large",
          "description": "Leading mark on a row or card."
        }
      }
    },
    "variants": [
      {
        "id": "base",
        "label": "Base",
        "description": "A circular flag mark.",
        "platforms": [
          "ios",
          "android",
          "web"
        ]
      }
    ],
    "sizes": [
      {
        "id": "sm",
        "label": "Small",
        "description": "16px."
      },
      {
        "id": "md",
        "label": "Medium",
        "description": "20px."
      },
      {
        "id": "lg",
        "label": "Large",
        "description": "24px."
      }
    ],
    "measurements": [
      {
        "id": "size-sm",
        "label": "Small diameter",
        "kind": "exact",
        "token": "--cds-size-16",
        "value": 16,
        "unit": "px",
        "appliesTo": [
          "artwork"
        ]
      },
      {
        "id": "size-md",
        "label": "Medium diameter",
        "kind": "exact",
        "token": "--cds-size-20",
        "value": 20,
        "unit": "px",
        "appliesTo": [
          "artwork"
        ]
      },
      {
        "id": "size-lg",
        "label": "Large diameter",
        "kind": "exact",
        "token": "--cds-size-24",
        "value": 24,
        "unit": "px",
        "appliesTo": [
          "artwork"
        ]
      },
      {
        "id": "ring-width",
        "label": "Ring width",
        "kind": "exact",
        "token": "--cds-border-width-hairline",
        "value": 0.5,
        "unit": "px",
        "appliesTo": [
          "ring"
        ]
      }
    ],
    "layout": {
      "alignment": "Inline-block, nudged up slightly so it centers against adjacent text rather than sitting on the baseline.",
      "flow": "A fixed-size circular mark; it never stretches to its container.",
      "notes": [
        "The artwork is a full square clipped to a circle in CSS. The vendored source set masks itself with an id that is identical in every file, so inlining the mask would collide across instances."
      ]
    },
    "typographyAndColor": {
      "typography": [],
      "colors": [
        {
          "label": "Ring",
          "description": "Hairline ring around the artwork.",
          "token": "--cds-divider"
        }
      ]
    },
    "states": [
      {
        "id": "known",
        "label": "Known country",
        "description": "A country with vendored artwork renders its flag."
      },
      {
        "id": "unknown",
        "label": "Unknown country",
        "description": "An unrecognized code renders nothing, so a missing flag costs a mark rather than breaking the row."
      }
    ],
    "behavior": [
      {
        "label": "Case-insensitive codes",
        "description": "Codes are matched lowercased, so bridge and backend casing both resolve."
      },
      {
        "label": "Untrusted input",
        "description": "The code prop is a plain string because codes arrive from payloads; isFlagCode narrows it when a caller needs a fallback."
      }
    ],
    "accessibility": [
      {
        "label": "Named country",
        "description": "Pass a label when the flag is the only indication of the country, so it is announced as a meaningful image."
      },
      {
        "label": "Decorative flag",
        "description": "Omit the label when adjacent text already names the country; the flag is then hidden from assistive technology."
      }
    ],
    "contentAdaptation": [
      {
        "label": "Pair with the country name",
        "description": "A flag alone is ambiguous to many readers; keep the country or city in text beside it."
      },
      {
        "label": "Not for language",
        "description": "Use a flag for a country or a server location, never to indicate a language."
      }
    ],
    "motionHaptics": {
      "motion": [
        {
          "label": "Static",
          "description": "The mark does not animate."
        }
      ],
      "haptics": [
        {
          "label": "No feedback",
          "description": "The flag is a display and requests no haptic feedback."
        }
      ]
    },
    "platformDifferences": []
  },
  "platforms": {
    "implemented": [
      "web"
    ],
    "native": "Imported design guidance only; no native implementation or parity verification in this preview.",
    "react": {
      "implementation": "local",
      "status": "preview",
      "parity": "web-reference"
    },
    "vue": {
      "implementation": "absent",
      "parity": "not-verified",
      "status": "reference-only"
    },
    "swift": {
      "implementation": "absent",
      "parity": "not-verified",
      "status": "reference-only"
    },
    "kotlin": {
      "implementation": "absent",
      "parity": "not-verified",
      "status": "reference-only"
    }
  },
  "tokens": [
    {
      "name": "--cds-size-16",
      "light": "16px",
      "dark": "16px"
    },
    {
      "name": "--cds-size-20",
      "light": "20px",
      "dark": "20px"
    },
    {
      "name": "--cds-size-24",
      "light": "24px",
      "dark": "24px"
    },
    {
      "name": "--cds-border-width-hairline",
      "light": "0.5px",
      "dark": "0.5px"
    },
    {
      "name": "--cds-divider",
      "light": "#e0ddd6",
      "dark": "#ffffff1a"
    }
  ],
  "review": {
    "local_status": "not-reviewed",
    "source_approval": null,
    "imported_spec_status": "approved",
    "spec_origin": "imported-adapted",
    "local_spec_source_status": "approved",
    "imported_source_revision": "f96f416e1c3bfbfaffcd155a88b87b18b8bb25f1",
    "note": "Inherited specification status is provenance, not approval of the current local implementation or extracted contract."
  },
  "missing_facts": [
    {
      "code": "human-review-pending",
      "detail": "This local preview has no recorded human design or engineering sign-off."
    },
    {
      "code": "platform-parity-unverified",
      "detail": "Vue, Swift and Kotlin implementations are absent from this package; platform parity has not been verified."
    },
    {
      "code": "figma-mapping-unverified",
      "detail": "No current component-level Figma node and immutable revision have been verified for this contract."
    }
  ]
}
```

## Visual states

- [Default](/storybook/iframe.html?id=cloaked-design-system-primitives-flag--default&viewMode=story) | export: Default | source: packages/design-system/src/components/primitives/Flag/Flag.stories.tsx

- [Sizes](/storybook/iframe.html?id=cloaked-design-system-primitives-flag--sizes&viewMode=story) | export: Sizes | source: packages/design-system/src/components/primitives/Flag/Flag.stories.tsx

- [White Field Flags](/storybook/iframe.html?id=cloaked-design-system-primitives-flag--white-field-flags&viewMode=story) | export: WhiteFieldFlags | source: packages/design-system/src/components/primitives/Flag/Flag.stories.tsx

- [Inline](/storybook/iframe.html?id=cloaked-design-system-primitives-flag--inline&viewMode=story) | export: Inline | source: packages/design-system/src/components/primitives/Flag/Flag.stories.tsx

- [Unknown Code](/storybook/iframe.html?id=cloaked-design-system-primitives-flag--unknown-code&viewMode=story) | export: UnknownCode | source: packages/design-system/src/components/primitives/Flag/Flag.stories.tsx

- [Dark Theme](/storybook/iframe.html?id=cloaked-design-system-primitives-flag--dark-theme&viewMode=story) | export: DarkTheme | source: packages/design-system/src/components/primitives/Flag/Flag.stories.tsx

Preview pending human sign-off. Source-derived APIs and successful checks do not certify accessibility, native parity or approval.
