Localization & Theming
Strings
l10n.yamlgenerateslib/l10n/app_localizations.dartfromlib/l10n/app_en.arb+app_cy.arb(English/Welsh). Every user-facing string is an ARB entry;design_systemholds none (purity test enforces).titleKey/bodyKey/altKeyfrom backend remain localizable keys — inspector prose (notes,contestReason,conditionNote) is never translated and never appears in ARB.- Dates: booking selector uses literal
MM/DD/YYYY; other dates useintllocale-sensitiveDateFormat(GB/Cymraeg diverge — screenshot note). - Grammar fix: "will be automatically accepted" (corrected) enforced via ARB value, not screenshot typo.
Locale switching
LocaleCubit persists en|cy via PreferencesService and rebuilds MaterialApp.locale. Widget sweep tests render every matrix at en and cy to catch overflow from longer Cymraeg strings.
Theming
ThemeCubit → ThemeMode.system|light|dark persisted vs system Brightness. KxTheme.light() / dark() maps KxColors + KxTypography to ThemeData including ColorScheme AA-checked tokens and textSelectionTheme. KxTone maps product states (outstanding→warning, accepted→success) away from raw color.
Tokens → WCAG
test/design_system/contrast_test.dart asserts every foreground/on background pair ≥4.5:1 and non-text ≥3:1 both themes; tealAccentDecorative never appears as foreground (explicit failure would block build if a new token mistakenly aliases it).
Adding a string
- Add key to
app_en.arb(andapp_cy.arbsame key). - Run
flutter gen-l10n. - Consume via
context.l10n.newKey. - Never hardcode literal —
design_system_purity_testwill fail.
RTL & motion
KxBreakpointsthresholds pinned at 599/600/839/840/1439/1440;responsive_sweep_testrenders 360/600/840/1440 × 1.0/1.3/2.0 text scale × LTR/RTL × light/dark without overflow.MediaQuery.disableAnimationscollapses allKxMotionto 0;KxSkeletonshimmer stops andpumpAndSettlereturns.