Design System

Accessibility

WCAG 2.2 AA baseline — AAA where the palette allows.

Every corporate and product surface ships against WCAG 2.2 AA. The palette is tuned so AAA is cheap on navy-900 surfaces; critical-alert contexts (destructive actions, mission-blocking errors, hostile-contact indicators in running text) are expected to clear AAA.

A surface fails review if any of these eight pillars is wrong:

Contrast - the real numbers

All values computed per the WCAG 2.x relative-luminance formula against FDT’s actual token values.

Thresholds.

Threshold Minimum ratio Applies to
AA body 4.5:1 text under 18pt regular or 14pt bold
AA large 3:1 text at or above 18pt regular or 14pt bold · UI components
AAA body 7:1 preferred for running text on navy-900

Text pairings.

Foreground Background Ratio AA body AA large Verdict
fog-100 (#E8EEF7) navy-900 16.0:1 AAA
fog-100 navy-800 14.7:1 AAA
fog-200 (#C5D1E2) navy-900 12.1:1 AAA
fog-200 navy-800 11.1:1 AAA
steel-300 (#8A9BB4) navy-900 6.5:1 AA
steel-300 navy-800 6.0:1 AA
steel-400 (#5A6D8A) navy-900 3.5:1 UI only
steel-400 navy-800 3.3:1 UI only
electric-500 (#1D6EF5) navy-900 4.1:1 large/UI only
electric-500 navy-800 3.8:1 large/UI only
electric-400 (#4A8DFF) navy-900 5.8:1 AA
electric-400 navy-800 5.3:1 AA
electric-300 (#7BA9FF) navy-900 7.9:1 AAA
hostile (#E0322C) navy-900 4.2:1 large/UI only
hostile-400 (#EE5050) navy-900 5.3:1 AA
neutral (#2FA85A) navy-900 6.3:1 AA
caution (#F2B535) navy-900 10.4:1 AAA

Three rules the table implies:

  • electric-500 and hostile are fills, not text. They pass only the large-text and UI thresholds. Inline emphasis, links inside paragraphs, and error messages use electric-400 and hostile-400 - the body-safe variants.
  • steel-400 is metadata only. Labels, axis ticks, timestamps - fine. Paragraphs and captions - use steel-300 or lighter.
  • Button label colors are locked per variant. See the button table below.

Button labels - locked per variant. The label color is determined by the fill; this is the only way buttons clear WCAG with the system’s fill values.

Variant Fill Label Contrast Rule
brand / primary electric-500 white 4.6:1 weight ≥600, size ≥14px
danger hostile white 4.5:1 weight ≥600, size ≥14px
success neutral navy-900 6.3:1 dark label always · white on green fails at 3.0:1
warning caution navy-900 10.4:1 dark label always · white on amber fails at 1.8:1
neutral (secondary) navy-700 fog-100 11.2:1 standard dark-button treatment

WebAwesome ships white labels as the default on all semantic variants. fdt-theme.css must override success and warning - the default ships inaccessible buttons.

Focus

Every interactive element has a visible focus state. The treatment is two-layer:

  • 2px solid electric-500 border - bound to --wa-focus-ring so native and component focus match.
  • --fdt-glow-sm halo - additive atmosphere, not a replacement for the solid border.
  • Uses :focus-visible, not :focus - keyboard users get the treatment, mouse users don’t see flashing.
  • Never removed, only restyled.

The 2px border alone clears the 3:1 UI-contrast threshold against every navy surface. The glow is decorative reinforcement; the border is load-bearing.

Keyboard navigation

Every component is keyboard-operable. WebAwesome covers its own primitives (tab order, arrow-key navigation within groups, escape-to-dismiss on overlays). FDT-specific components must match:

  • Program cards, sector cards, feature tiles - wrap in <a> or <button> for native focus behavior.
  • Decision prompt (QRF) - fixed tab order, left button first.
  • Map HUD corner panels - tab-reachable; inner content follows DOM order.
  • Radar scope tracks - up/down cycles through active tracks, enter acquires.

Reduced motion

All motion respects prefers-reduced-motion: reduce. When active:

  • Radar sweep - freezes at current position, no rotation.
  • Live pulse - stops; opacity holds at 100%.
  • Panel slides - become instant crossfades (150ms opacity only).
  • Acquire brackets - snap to position without bloom or settle.
  • Glow scale - remains. Glows are atmosphere, not motion.

Color-blind differentiation

Red/green is the most commonly conflated pair (deuteranopia ≈ 6% of males, protanopia ≈ 2%). FDT’s hostile and neutral sit on exactly that axis. The system defends with shape + text + color, never color alone.

  • APP-6D symbology carries shape as the primary signal. Friendly = rounded rectangle (blue). Hostile = diamond (red). Neutral = square (green). Unknown = quatrefoil (yellow). A deuteranope can’t distinguish red from green by hue but distinguishes a diamond from a square instantly. Load-bearing for tactical contexts.
  • Status pills always carry text. CONTESTED (hostile) and SECURED (neutral) are spelled out - removing the label is a bug.
  • Resource meter icons. Each resource pairs color with glyph: INTEL (eye, blue), MORALE (heart, amber), LOGISTICS (box, green), ESCALATION (triangle-exclaim, red). Color reinforces; it never stands alone.
  • Classification and program-status badges already carry text.
  • Data visualizations distinguish series by position, pattern (solid / dashed / dotted), and color - in that order. A line chart with only color differentiation is incomplete.

Testing requirement. Every screen that uses hostile and neutral together is validated through a deuteranopia simulator (Chrome DevTools → Rendering → Emulate vision deficiency) before ship. If the screen becomes ambiguous, the shape or label layer has failed.

Forced colors (Windows High Contrast Mode)

Government and military workstations commonly enforce forced-colors themes. The system respects forced-colors: active and falls back to system colors:

@media (forced-colors: active) {
  /* Surfaces become CanvasText/Canvas */
  .wa-button, .fdt-program-card {
    border: 1px solid ButtonText;
    background: ButtonFace;
    color: ButtonText;
  }
  /* Focus uses the system Highlight color */
  :focus-visible {
    outline: 2px solid Highlight;
    outline-offset: 2px;
  }
  /* Accent fills become LinkText or Highlight */
  .fdt-accent-fill {
    background: Highlight;
    color: HighlightText;
  }
  /* APP-6D symbols retain shape; fill becomes CanvasText */
  .fdt-symbol {
    forced-color-adjust: auto;
  }
  /* Glow is decorative - drop entirely */
  [class*="fdt-glow"] {
    box-shadow: none;
  }
  /* Semantic colors replaced by text labels + shape; affiliation color is stripped */
}

Rules.

  • Never background-color alone - pair with border, icon, or text.
  • Never border-color alone for focus - use outline (forced-colors respects it).
  • forced-color-adjust: none is reserved for elements where brand identity is essential and the four major Windows schemes (Black, White, Aquatic, Dusk) have all been verified.
  • APP-6D symbols keep forced-color-adjust: auto - shape survives, color replaces with system text color.

Screen readers

  • APP-6D symbols carry role="img" with aria-label describing type and affiliation: "friendly infantry battalion", "hostile armored company".
  • Mono track IDs (UNK-003) use aria-label that spells out the reading: "unknown zero zero three".
  • Classification badges include the classification name in accessible text.
  • Radar tracks announce state via aria-live="polite" (acquisitions, new contacts) or aria-live="assertive" (incoming threats).
  • Sector cards announce status transitions (CONTESTEDSECURED) via aria-live.
  • Decision-prompt buttons use aria-label that includes the consequence, not just the verb - e.g. aria-label="Conserve - hold current ammunition, accept reduced engagement capability".

Touch targets

  • Default: minimum 44×44px for any standalone tap target on mobile or tablet.
  • Dense operator UI may tighten to 36×36px, but only inside clustered controls (resource meters, sub-unit badges) where the user is explicitly in operator mode with a known small pointer or stylus.
  • Spacing between targets: at least 8px regardless of size.

Internationalization - NATO language support

Three locales aligned with NATO’s official working languages and the alliance’s largest continental member. All Latin-script LTR.

Locale Code Constituency
English en NATO official · authoring language · voice tuned to this register
French fr NATO official · French MoD and partner commands
German de Bundeswehr and allied Central European customers

Practical consequences.

  • Text expansion. German can run ~35% longer than English; French sits between. All text-bearing components flex on content - no fixed widths on buttons, labels, badges, or tags. Strings are externalized to the i18n catalog; no text is baked into images.
  • Number and date formats. EN uses 1,234.56 and 2026-04-23. DE and FR use 1.234,56 with DD.MM.YYYY or DD/MM/YYYY. Formatters use wa-format-number and wa-format-date, driven by the document lang. Tactical contexts stay on ISO 8601 regardless of locale.
  • Military conventions stay locale-independent. 24-hour Zulu, MGRS, metric, NATO phonetic, APP-6D - alliance-wide, not linguistic.
  • Fonts. Inter, Barlow Condensed, JetBrains Mono cover the extended Latin set - umlauts, ß, French diacritics. No extra subsets needed.
  • Directional iconography. LTR across all three locales.

Not in scope: other Romance locales (Spanish, Italian, Portuguese), Nordic locales (Danish, Swedish, Norwegian, Finnish, Icelandic), Dutch, Cyrillic, Greek, RTL (Arabic, Hebrew), CJK. Each triggers a V2 cycle - font-stack changes, layout review, copy re-authoring.