إنتقل إلى المحتوى الرئيسي

07 — Theming & Design Tokens for Rasd Forms (React + React Native)

Research date: 2026-08-15. Scope: how a cross-platform (web + React Native) form/survey component library should let humanitarian-sector customers (WFP, UNRWA, UNHCR, UNICEF, IOM, NGOs) ship fully custom themes as data, while staying accessible (WCAG 2.2), RTL-safe (Arabic), and usable on low-end Android in the field.

Summary

  1. The W3C DTCG design-token format reached its first stable release, 2025.10, on 28 Oct 2025 (Format + Color + Resolver modules). File extension .tokens.json, MIME application/design-tokens+json, $value/$type/$description/$extensions/$deprecated, {group.token} aliases; the Resolver module (.resolver.json) expresses light/dark/brand/density variants via sets → modifiers → contexts → resolutionOrder [1][2][3][4]. Rasd should read/write this format rather than invent a token schema.
  2. Style Dictionary is at v5.5.1 (Apache-2.0, 7 Aug 2026); DTCG object values for dimension landed in v5.4.0 (Mar 2026), but its own docs still say 2025.10 is "not fully supported yet" (no Resolver support). Terrazzo (@terrazzo/cli 2.7.1) is the other reference implementation [5][6][7]. Use SD/Terrazzo for the build-time default theme; do not make customers run a build to theme.
  3. React Native has no CSS; the 2026 field is: react-native-unistyles 3.3.0 (C++/Fabric, New Architecture only, adaptiveThemes, UnistylesRuntime.setTheme/updateTheme, hashed CSS classes on web), Tamagui 2.7.7 (Tamagui 2 shipped 29 Dec 2025; tokens→themes→sub-themes, CSS variables + .tui_dark classes on web), Shopify Restyle 2.4.5 (last release Mar 2025), NativeWind 4.2.6 stable / 5.0.0-preview.4 (Tailwind v4 @theme, VariableContextProvider; v5 explicitly "not intended for production use") [8][9][10][11][12].
  4. A library that must run inside customers' apps should not force a styling engine. Every RN engine above needs its own Babel/Metro plugin or provider; the safest core is plain StyleSheet + our own ThemeProvider, with optional adapters.
  5. On web, the winning pattern is CSS custom properties scoped to the form root (--rasd-* on the container, not :root), data-theme/data-color-scheme attributes for explicit mode, prefers-color-scheme only as the default, light-dark() (Baseline "newly available" since May 2024, "widely available" ~Nov 2026) as an optimisation, and @layer so unlayered customer CSS always wins without !important [13][14].
  6. Headless libraries converged on the same override contract: className/style accept functions of state, state is exposed as data-* attributes, parts are named (Root/Trigger/Popup, Ark's data-scope/data-part), and element replacement is asChild (Radix) or render (Base UI). shadcn/ui made Base UI the default over Radix on 3 Jul 2026 (Base UI then 1.6.0, 6M+ weekly downloads; now @base-ui/react 1.7.0) [15][16][17][18].
  7. Form engines expose theming at three levels: (a) CSS/token themes as JSON — SurveyJS { themeName, colorPalette, isPanelless, cssVariables: {"--sjs-…"} } applied with survey.applyTheme(); (b) template/renderer registries — RJSF withTheme({widgets, fields, templates}) (priority User > Theme > Defaults), JSON Forms {tester: rankWith(3, …), renderer}, SurveyJS RendererFactory.registerRenderer(questionType, renderAs, rendererName, useAsDefault), Form.io Templates.current['input-textfield'].form = ctx => …; (c) per-field class hooks (uiSchema ui:classNames, Form.io customClass) [19][20][21][22][23][24].
  8. WCAG 2.2 (Rec. 5 Oct 2023, edited 12 Dec 2024) adds form-relevant AA criteria: 2.5.8 Target Size (Minimum) 24×24 CSS px, 2.4.11 Focus Not Obscured, 3.3.7 Redundant Entry (A), 3.3.8 Accessible Authentication; plus the classics 1.4.3 (4.5:1 text), 1.4.11 (3:1 UI components/focus rings), 1.4.4 (200% text), 1.4.10 (reflow at 320 px), 3.3.1/3.3.2/3.3.3 labels & errors, 4.1.3 status messages [25]. Default tokens must pass these out of the box, and the theme validator should refuse themes that break contrast.
  9. RN accessibility signals exist but are platform-split: AccessibilityInfo.isReduceMotionEnabled() (both), isHighTextContrastEnabled() (Android), isDarkerSystemColorsEnabled()/isBoldTextEnabled() (iOS); font scale via PixelRatio.getFontScale() and Text maxFontSizeMultiplier; RTL via I18nManager.forceRTL/allowRTL which requires an app reload and only flips start/end, not left/right [26][27].
  10. UN branding is concrete and diverse: UNHCR blue #0072BC, fonts Proxima Nova/Arial/Lato; WFP UI Kit primary #2A93FC (accent #1f6ebc, @wfp/ui last published Apr 2024); UNICEF cyan #1CABE2 with Univers; generic UN blue #5B92E5. Fonts must be bundled offline (Expo config-plugin embedding; static TTF/OTF only on Android — variable fonts are not cross-platform) [28][29][30][31].

1. Design tokens: DTCG 2025.10 and the tool pipeline

1.1 The spec

The Design Tokens Community Group (W3C CG) declared version 2025.10 the first stable release on 28 October 2025, with reference implementations in Style Dictionary, Tokens Studio and Terrazzo, and adopters including Figma, Penpot, Sketch, Framer, Supernova, Knapsack and zeroheight [1]. The release bundles three modules: Format, Color and Resolver [2].

Format module essentials [3]:

  • Files: .tokens or .tokens.json; MIME application/design-tokens+json.
  • Reserved keys: $value (required), $type, $description, $extensions (reverse-DNS namespaced vendor data), $deprecated (bool or string).
  • Atomic types: color, dimension, fontFamily, fontWeight, duration, cubicBezier, number. Composite: strokeStyle, border, transition, shadow, gradient, typography.
  • Color is an object { colorSpace, components[], alpha, hex } (Display P3, OKLCH and other CSS Color 4 spaces are legal); dimension is { value, unit: "px" | "rem" } — no bare strings.
  • Aliases: "{color.brand.primary}". Names may not contain $ { } ..
  • Tools must preserve unknown $extensions. $type is never inferred.
  • Caveat: the current editor's draft (dated 30 Jul 2026) additionally describes $ref JSON-pointer references and $extends group inheritance; treat those as post-2025.10 and not yet stable [3].

Resolver module [4]: a .resolver.json document with sets (merged in order), modifiers (each with named contexts, e.g. theme: {light, dark}, density: {compact, comfortable}, contrast: {normal, high}) and a resolutionOrder; a consumer supplies {theme: "dark", contrast: "high"} and receives one flat token set. This is exactly the light/dark/brand/high-contrast matrix a form library needs, and it avoids the combinatorial-file explosion.

1.2 Tooling status (verified on npm, 2026-08-15)

ToolVersionLicenseDTCG 2025.10
style-dictionary5.5.1 (7 Aug 2026)Apache-2.0$-keys since v4; dimension objects since 5.4.0 (22 Mar 2026); docs: full 2025.10 support "work in progress" [5][6]
@terrazzo/cli2.7.1 (11 Aug 2026)MITreference implementation per DTCG announcement [1]
@tokens-studio/sd-transforms2.0.3 (Dec 2025)MITTokens Studio → SD bridge

Style Dictionary's built-in formats cover both of Rasd's targets: css/variables (with selector and outputReferences options), javascript/es6 / javascript/esm, typescript/es6-declarations, json/flat, plus android/resources, ios-swift/class.swift, compose/object if agencies want native parity [7]. There is no dedicated "react-native" format — RN consumes the JS/JSON output.

Implication: DTCG is a source format for designers and pipelines. A runtime library still needs a resolved, flat, typed theme object; the DTCG file should compile to that object (build-time via SD/Terrazzo, or at runtime via a small resolver Rasd ships).


2. Web: CSS custom properties, scoping, dark mode

  • Scope, don't globalise. Set variables on the form root (.rasd-root { --rasd-color-primary: … }) so two forms with different agency themes can coexist on one page (SurveyJS also attaches its --sjs-* variables at the survey element level) [19].
  • Explicit mode beats media query. Read prefers-color-scheme only to choose the default; expose data-color-scheme="light|dark" and data-contrast="high" attributes on the root so hosts can force a mode. Modern shorthand: color-scheme: light dark + light-dark(a, b) (Baseline "newly available" 13 May 2024, "widely available" only around Nov 2026, colors only) — fine as progressive enhancement, not as the only mechanism, given the older Android WebViews field devices run [13].
  • Cascade layers. Ship component CSS in @layer rasd.reset, rasd.base, rasd.components;. Unlayered author styles always beat layered ones, so a customer's plain .rasd-input { … } overrides Rasd without specificity fights; supported in all evergreen browsers since 2022 [14].
  • High contrast / forced colors. Respect @media (forced-colors: active) (Windows High Contrast) by using system colors for borders/focus rings, and provide a contrast: "high" theme mode for platforms without forced-colors.
  • Reduced motion. @media (prefers-reduced-motion: reduce) → set --rasd-motion-duration: 0ms.

3. React Native: theming without CSS

LibraryVersion (npm, Aug 2026)ModelRuntime theme switchWeb outputCost to embed in a library
react-native-unistyles3.3.0 (10 Jul 2026), MITAny JS object as theme; StyleSheet.configure({themes, settings}); adaptiveThemes (needs light/dark names, mutually exclusive with initialTheme); ScopedTheme [8]UnistylesRuntime.setTheme(), updateTheme() — no React re-render (C++ ShadowTree updates)Hashed CSS classes + native media queries; styles object is empty on web [32]Requires New Architecture, Babel plugin, and the host to call StyleSheet.configure — intrusive for a drop-in lib
Tamagui2.7.7 (15 Aug 2026), MIT; Tamagui 2 released 29 Dec 2025 [9][33]Tokens → themes → sub-themes (dark_green_subtle), component themes (_Button), createV5Theme<Theme name> providerCSS variables + .tui_dark classes, optimising compilerWhole framework + compiler config; heavy for a form widget
@shopify/restyle2.4.5 (19 Mar 2025), MITcreateTheme (typed), ThemeProvider, createBox/createText/createVariant [34]Swap theme prop → re-renderRN-web onlyLight, but slow release cadence; single-theme provider
NativeWind4.2.6 stable (22 Jun 2026); 5.0.0-preview.4v5: Tailwind v4 @theme + :root CSS vars, VariableContextProvider (v4 vars() deprecated); dark via Appearance/prefers-color-scheme [10][11]Provider value or Appearance.setColorScheme()Real Tailwind CSSv5 "not intended for production use"; requires Tailwind + Metro CSS pipeline in host
Plain StyleSheet + ContextRN 0.87.0Theme object in React context; memoised StyleSheet.create per themeContext change → re-rendervia react-native-web 0.21.2 (inline styles/atomic CSS)Zero host configuration; portable into any host regardless of styling stack

Take-away: for a library that must be installed into agencies' existing Expo/RN apps (which may already use NativeWind, Tamagui, or nothing), the core must be engine-agnostic: plain StyleSheet + RasdThemeProvider, with the theme object shaped so it can also be fed into Unistyles/Tamagui adapters. Unistyles' "theme is any JS object" and Restyle's typed createTheme are the two patterns worth copying [8][34].


4. Headless / unstyled architecture vs styled defaults

LibraryVersionElement replacementState exposurePart addressingStyling entry points
Radix Primitives@radix-ui/react-slot 1.3.3, @radix-ui/react-primitive 2.1.10asChild (Slot)data-state="open" etc.Compound componentsclassName, CSS
Base UI (@base-ui/react)1.7.0 (4 Aug 2026), MITrender prop[data-checked], [data-highlighted], [data-pressed], [data-starting-style]Root/Trigger/Portal/Positioner/Popup/ItemclassName/style as functions of state; CSS vars like --anchor-width, --available-height [15]
React Aria Components1.20.0 (31 Jul 2026), Apache-2.0render props (children fn)ARIA attrs first, data-* otherwiseslot prop for repeated childrenclassName/style functions with defaultClassName; default class react-aria-ComponentName [17]
Ark UI@ark-ui/react 5.38.1 (7 Aug 2026), MITasChilddata-statedata-scope="slider" data-part="root"CSS on data attributes; Panda defineSlotRecipe [18]
Headless UI2.2.10 (Apr 2026), MITas propdata-open, data-checkedcompound componentsTailwind-oriented
shadcn/uicopy-into-repo; Base UI default since 3 Jul 2026 [16]inherits Radix/BaseinheritsinheritsTailwind + CSS vars (--primary, --background)

Common denominator worth adopting: named parts + state as data attributes + className/style-as-function. Ark's data-scope/data-part pair is the most CSS-friendly for third parties who cannot touch JSX; Base UI's render/function-className is the most ergonomic for React devs [15][18].

Styled defaults still matter: RJSF ships a Bootstrap-3-based default plus @rjsf/mui, @rjsf/antd, @rjsf/chakra-ui, @rjsf/fluentui-rc, @rjsf/mantine, @rjsf/primereact, @rjsf/react-bootstrap, @rjsf/semantic-ui, @rjsf/shadcn (v6.8.0, 14 Aug 2026) — an existence proof that customers want both a good-looking default and a way to inject their design system [22].


5. Component registry replacement in form engines

EngineVersionRegistry APIGranularityActivation
RJSF@rjsf/core 6.8.0, Apache-2.0withTheme({ widgets, fields, templates }); templates/widgets props; v6 requires GridTemplate in custom template sets [22][35]widget (input), field (schema type), template (layout, error list, array item)uiSchema ui:widget, ui:field, ui:classNames; merge order User > Theme > Defaults
JSON Forms@jsonforms/core 3.8.0, MITrenderers: [{ tester: rankWith(3, scopeEndsWith('rating')), renderer }]; NOT_APPLICABLE = -1; highest rank wins [21]renderer per UI-schema elementtester over (uischema, schema)
SurveyJSsurvey-core/survey-react-ui 3.0.0 (11 Aug 2026), MITReactElementFactory.Instance.registerElement(name, props => <C/>) + RendererFactory.Instance.registerRenderer(questionType, renderAs, rendererName, useAsDefault); custom item templates [20][24]question type, or renderAs variant"renderAs": "…" in form JSON, or useAsDefault=true
Form.io@formio/js 5.5.1, MITTemplates.current.input.form = ctx => html; per-type ['input-textfield']; per-instance ['input-textfield-firstName']; `renderMode: 'html'custom` [23]template string per component/type/instance

Design lessons: (1) offer both a map by type (simple, RJSF/SurveyJS style) and a tester + rank escape hatch (JSON Forms style) for conditional overrides (e.g., "all select with > 20 options render as searchable sheet on Android"); (2) let form JSON select an alternate renderer (renderAs) so a theme or form author can pick a variant without code; (3) keep the layout templates (field wrapper, error list, page/section, array item, grid) overridable separately from input widgets.


6. Theme as data (JSON), so orgs ship themes via config

  • SurveyJS: a theme is a JSON object { themeName, colorPalette: "light"|"dark", isPanelless, backgroundImage, backgroundOpacity, header, cssVariables: { "--sjs-primary-backcolor", "--sjs-general-backcolor", "--sjs-font-family", … } }, applied with survey.applyTheme(theme); the Creator's Theme Editor exports it; predefined themes ship as survey-core/themes exports such as FlatDarkPanelless (theme × light/dark × paneled/panelless) [19]. Themes are decoupled from forms and can be stored per tenant.
  • Form.io: theming is markup templates + CSS framework choice (Bootstrap 5 default, USWDS, Semantic, community GOV.UK/Bulma) — code, not data — plus per-field customClass from the builder [23].
  • shadcn/ui: theme is a set of CSS variables in :root/.dark — data-ish but CSS.
  • DTCG + Resolver: theme = tokens files + resolver inputs — pure data, tool-agnostic [3][4].

For UN agencies (procurement often forbids code changes to vendor libraries; brand teams own colors, IT owns deployment) the SurveyJS model — a JSON theme that a non-developer can produce in an editor and that a developer can load from config or fetch from a tenant API and cache offline — is the target. Rasd should extend it with typography, spacing, radius, motion, assets and per-component parts, and make it DTCG-importable.


7. RTL, font scaling, high contrast, motion, WCAG 2.2

RTL (Arabic).

  • Web: dir="rtl" on the form root and only logical properties (margin-inline-start, padding-inline, inset-inline-end, text-align: start); icons that imply direction (back/next chevrons) get transform: scaleX(-1) under [dir=rtl].
  • RN: I18nManager.forceRTL(true) + allowRTL(true) flips flexDirection: 'row', marginStart/End, paddingStart/End, textAlign defaults — but it is global, requires an app reload (Updates.reloadAsync() in Expo), and does not swap left/right [26]. A form embedded in an LTR host that must render an Arabic questionnaire therefore needs a per-form direction: Rasd's useDirection() should resolve theme.direction ?? form.locale.dir ?? I18nManager.isRTL and, when it disagrees with I18nManager.isRTL, emit explicit left/right and row-reverse instead of start/end.
  • Arabic typography needs larger line-height (≈1.6–1.8) and Arabic-capable fonts (see §8).

Font scaling / dynamic type. RN Text/TextInput scale with allowFontScaling (default true); cap with maxFontSizeMultiplier (values under 1.2 harm accessibility) rather than disabling; read PixelRatio.getFontScale() for layout decisions [27]. Web: size in rem, no fixed heights on inputs, verify at 200% zoom (WCAG 1.4.4) and 320 px reflow (1.4.10) [25].

High contrast / system a11y signals. AccessibilityInfo.isHighTextContrastEnabled() + highTextContrastChanged (Android), isDarkerSystemColorsEnabled(), isBoldTextEnabled(), isReduceTransparencyEnabled() (iOS), isReduceMotionEnabled() + reduceMotionChanged (both), isScreenReaderEnabled() [27]. Web: forced-colors, prefers-contrast: more, prefers-reduced-motion.

WCAG 2.2 for forms (Rec. 5 Oct 2023; edited 12 Dec 2024) [25]:

  • 1.4.3 text contrast 4.5:1 (3:1 large); 1.4.11 3:1 for input borders, checkbox marks, focus rings against adjacent colours.
  • 2.4.7 focus visible; 2.4.11 Focus Not Obscured (AA) — sticky headers/footers or a "Next" bar must not fully hide the focused input; 2.4.13 (AAA) focus ring ≥ 2 px perimeter, 3:1.
  • 2.5.8 Target Size (Minimum) (AA) — 24×24 CSS px, or 24 px spacing, exceptions for inline text links; humanitarian field use argues for 44–48 px defaults.
  • 3.3.1/3.3.2/3.3.3 — errors identified in text, labels/instructions present, suggestions offered; 3.3.7 Redundant Entry (A) — prefill/auto-populate; 3.3.8 Accessible Authentication (AA); 4.1.3 status messages via live regions; 1.3.5 autocomplete purposes.
  • 2.3.3 Animation from Interactions (AAA) — honour reduced motion.

8. Branding needs of UN agencies (colors, logos, offline fonts)

AgencyPrimaryFontsSource
UNHCR#0072BC (RGB 0,114,188); categorical #FFC740 #32C189 #6CD8FD #D25A45 #A097E3 #7C3C36 #BFBFBF, each with AAA text pairingsProxima Nova, Arial, Lato (Lato for dataviz)[28]
WFP (UI Kit v0.10.1)#2A93FC ("enhanced corporate WFP Blue for RGB"); accent #1f6ebc; text #404040; status #ff5252 #ffc759 #fcdc5d #00a878; @wfp/ui 1.22.5, Apache-2, last published Apr 2024not stated in fetched page[29]
UNICEF#1CABE2 (Pantone Process Cyan)Univers Light[30] (secondary source)
UN generic#5B92E5[31] (secondary source)

Notes: agency palettes routinely fail 4.5:1 as text on white (e.g. #2A93FC ≈ 3.2:1, #6CD8FD far lower); a theme system must therefore separate brand accent (buttons, headers, progress) from semantic text/border tokens and validate contrast per role rather than reject brand colours. Logos are usually SVG with clear-space rules; a theme should carry logo.light, logo.dark, logo.compact and a minClearSpace hint.

Offline-embedded fonts. Expo supports OTF/TTF on all platforms, WOFF/WOFF2 additionally on web and iOS but not Android; variable fonts are not supported across all platforms — ship static weights. Fonts can be embedded at build time with the expo-font config plugin (available at startup, no loading code; requires a dev build) or loaded at runtime with useFonts (works in Expo Go; async) [32]. Web PWAs must precache self-hosted .woff2 in the service worker with @font-face { font-display: swap } and never depend on Google Fonts CDN. Recommended OFL Arabic-capable candidates: Noto Sans Arabic / Noto Naskh Arabic (Google Noto), IBM Plex Sans Arabic; UNHCR's Lato and Proxima Nova lack Arabic glyphs, so per-script fallback (fontFamily: { latin, arabic }) is required.


9. Comparison table — theming models

DimensionSurveyJSRJSFJSON FormsForm.ioBase UI / Ark (headless)Rasd target
Theme is data (JSON)Yes (cssVariables)No (React theme object)NoNo (templates)NoYes, DTCG-compatible
Runtime switch w/o rebuildYesYesYesYesYesYes
Component/part overridesclass overrides + rendererswidgets/fields/templatesrenderer registrytemplate stringsdata-part + className fnparts API + registry
RN supportNo (web)community onlyNoNoNoYes, first-class
Visual theme editorYes (Creator)NoNoNoNoYes (builder plugin)
Dark/high-contrast modeslight/darkvia host DSvia host DSvia CSSvia host CSSlight/dark/high-contrast/density
RTLCSS-basedvia hostvia hostvia Bootstrap RTLvia hostlogical props + per-form dir

10.1 Layers

  1. Tokens (data)RasdTheme JSON (below). Also accepts DTCG 2025.10 token files + an optional resolver document through @rasd/tokens (fromDtcg(tokens, {theme:'dark', contrast:'high'}) → RasdTheme).
  2. Runtime adapters — web: variables --rasd-* on the form root + data-color-scheme, data-contrast, data-density, dir; components styled with CSS in @layer rasd.* and Ark-style data-scope="rasd" data-part="…" attributes. Native: RasdThemeProvider (context) + memoised StyleSheet.create(theme => …); optional @rasd/adapter-unistyles/-tamagui that map the same JSON into those engines.
  3. Parts/slots API — every component: classNames?: Partial<Record<Part, string | (state) => string>>, styles?: Partial<Record<Part, StyleProp | (state) => StyleProp>>, render?: Partial<Record<Part, (props, state) => ReactNode>>, plus a theme.components[Component].parts default in JSON.
  4. Renderer registryrenderers: { byType: Record<QuestionType, Renderer>, testers: Array<{ rank, tester, renderer }> } and "renderAs" in the form definition; layout templates (FieldWrapper, Page, Section, ErrorSummary, RepeatItem, Grid) are separately overridable.
  5. Validation — JSON Schema for RasdTheme, contrast linting per semantic role (fail < 4.5:1 text, < 3:1 UI), warning if minTargetSize < 24, and locale/font checks (Arabic script without an Arabic-capable family).
  6. Builder theme editor — WYSIWYG that edits and exports the same JSON (SurveyJS precedent) and can import brand tokens from DTCG/Figma exports.

10.2 Concrete RasdTheme shape (v1)

{
"$schema": "https://rasd.dev/schemas/theme/1.json",
"name": "unhcr-field",
"version": "1.0.0",
"extends": "rasd/default", // optional base
"colorScheme": "auto", // "light" | "dark" | "auto"
"direction": "auto", // "ltr" | "rtl" | "auto" (from form locale)
"density": "comfortable", // "compact" | "comfortable" | "spacious"
"tokens": {
"color": { // primitives (brand palette)
"brand": { "500": "#0072BC", "600": "#005A96", "700": "#00426E" },
"neutral": { "0": "#FFFFFF", "50": "#F5F7FA", "900": "#111827" },
"danger": { "600": "#B42318" }, "success": { "600": "#067647" }, "warning": { "600": "#B54708" }
},
"semantic": { // roles — components ONLY use these
"bg.canvas": "{color.neutral.0}", "bg.surface": "{color.neutral.50}",
"fg.default": "{color.neutral.900}", "fg.muted": "#475467",
"accent.default": "{color.brand.500}", "accent.pressed": "{color.brand.700}",
"accent.onAccent": "{color.neutral.0}",
"border.default": "#D0D5DD", "border.focus": "{color.brand.600}",
"state.error": "{color.danger.600}", "state.success": "{color.success.600}"
},
"typography": {
"fontFamily": { "latin": "Lato", "arabic": "NotoSansArabic", "mono": "RobotoMono" },
"size": { "xs": 12, "sm": 14, "md": 16, "lg": 18, "xl": 22 }, // px; web emits rem
"lineHeight": { "latin": 1.5, "arabic": 1.7 },
"weight": { "regular": "400", "medium": "500", "bold": "700" },
"maxFontScale": 2.0 // RN maxFontSizeMultiplier
},
"space": { "1": 4, "2": 8, "3": 12, "4": 16, "6": 24, "8": 32 },
"radius": { "sm": 4, "md": 8, "lg": 12, "full": 9999 },
"border": { "width": { "thin": 1, "focus": 2 } },
"shadow": { "sm": { "color": "#00000014", "offset": [0, 1], "blur": 2 } },
"motion": { "duration": { "fast": 120, "normal": 200 }, "easing": "cubic-bezier(.2,.8,.2,1)" },
"a11y": { "minTargetSize": 44, "focusRingWidth": 2, "focusRingOffset": 2 }
},
"modes": { // partial overrides merged over tokens.semantic etc.
"dark": { "semantic": { "bg.canvas": "#0B1220", "fg.default": "#F2F4F7", "accent.default": "#4DA3E0" } },
"highContrast": { "semantic": { "border.default": "#000000", "fg.muted": "#000000" }, "border": { "width": { "thin": 2 } } },
"reducedMotion": { "motion": { "duration": { "fast": 0, "normal": 0 } } }
},
"components": { // per-part defaults; keys = data-part names
"TextField": {
"parts": { "root": { "gap": "{space.1}" }, "input": { "height": 48, "radius": "{radius.md}" },
"label": { "weight": "{typography.weight.medium}" } },
"variants": { "size": { "sm": { "input": { "height": 40 } } } }
},
"Button": { "parts": { "root": { "minHeight": 48, "radius": "{radius.md}" } } },
"ProgressBar": { "parts": { "fill": { "bg": "{semantic.accent.default}" } } }
},
"assets": {
"logo": { "light": "asset://brand/logo.svg", "dark": "asset://brand/logo-white.svg", "height": 28 },
"fonts": [
{ "family": "Lato", "weight": "400", "src": { "native": "asset://fonts/Lato-Regular.ttf", "web": "/fonts/Lato-Regular.woff2" } },
{ "family": "NotoSansArabic", "weight": "400", "src": { "native": "asset://fonts/NotoSansArabic-Regular.ttf", "web": "/fonts/NotoSansArabic-Regular.woff2" } }
]
},
"renderers": { "rating": "stars", "select.large": "searchable-sheet" }, // renderAs defaults
"$extensions": { "org.rasd.builder": { "editorPreset": "unhcr" } }
}

Rules: components read only tokens.semantic, typography, space, radius, motion, a11y (never primitives); every leaf accepts a literal or a {path} alias (DTCG-style); modes are resolved at runtime from colorScheme + OS/AccessibilityInfo/media queries; on web the resolved set is emitted as --rasd-<path-with-dashes> on the root; on native it is a frozen object in context and cached per (theme, mode, fontScale, direction) key.


Implications & recommendations for Rasd Forms

  1. Adopt DTCG 2025.10 as the interchange format, RasdTheme JSON as the runtime format. Provide fromDtcg() and toDtcg() so agencies with Figma/Tokens Studio/Style Dictionary pipelines plug in, while everyone else edits one JSON [1][3][4].
  2. Do not depend on any RN styling engine in core. Ship plain StyleSheet + RasdThemeProvider; offer optional Unistyles 3 / Tamagui 2 adapters later. NativeWind v5 is still preview and Restyle is quiet — neither is a safe foundation for a 2026 library [8][9][10][34].
  3. Web = scoped CSS variables + data attributes + @layer. Never write to :root; expose data-color-scheme, data-contrast, data-density, dir on the form root; publish the full --rasd-* variable list as public API [13][14].
  4. Standardise a parts API across every component (classNames, styles, render per part; state via data-*), plus data-scope="rasd" data-part="…" on web for pure-CSS customers [15][17][18].
  5. Two-tier renderer registry: map-by-type + renderAs variants (SurveyJS/RJSF ergonomics) and rank/tester overrides (JSON Forms power); keep layout templates separate from input widgets; a v1 rule: overrides must implement the same accessible props contract (label id, aria-describedby/accessibilityLabelledBy, error state) [20][21][22][23].
  6. Ship modes as data: light, dark, highContrast, reducedMotion, density, resolved from OS signals (prefers-color-scheme, forced-colors, prefers-reduced-motion; Appearance, AccessibilityInfo.isReduceMotionEnabled/isHighTextContrastEnabled/isDarkerSystemColorsEnabled) unless the host forces them [26][27].
  7. RTL by construction: logical properties on web; on native, useDirection() that can render RTL per form even when I18nManager.isRTL is false (explicit row-reverse/left|right fallback), mirrored directional icons, Arabic line-height token, per-script font families [26].
  8. Accessible defaults that pass WCAG 2.2 AA: 4.5:1/3:1 tokens, 2 px 3:1 focus ring, 44–48 px targets (≥ 24 hard floor), visible labels (never placeholder-only), text + icon errors bound via aria-describedby, error summary as live region, autocomplete on identity fields, sticky nav that never covers the focused field, no re-entry across pages (3.3.7) [25].
  9. Theme validator + contrast linter in @rasd/theme-tools, run in the builder editor and CI; fail on text < 4.5:1, warn on brand accents used as text; suggest darkened variants (e.g. WFP #2A93FC#1f6ebc for text/borders) [29].
  10. Offline fonts as first-class theme assets: static TTF/OTF for Android, WOFF2 for web, precached by the PWA service worker; embed via expo-font config plugin for production builds; refuse variable fonts in the validator; bundle a default OFL Arabic family [32].
  11. Font scaling budget: honour allowFontScaling, default maxFontSizeMultiplier 2.0 for body/inputs, and no fixed-height rows; test snapshot matrix at font scale 1.0/1.3/2.0 × ltr/rtl × light/dark/highContrast [27].
  12. Builder theme editor exports the same JSON and stores it per tenant; forms reference a theme by name/version so a form definition, a theme and a data submission can be synced and cached offline independently (mirrors SurveyJS's theme/form decoupling) [19].
  13. Publish a "brand pack" recipe for UNHCR/WFP/UNICEF/IOM/UNRWA (colors, logo slots, font stacks) as sample themes — a low-effort adoption accelerator with high signalling value to the target market [28][29][30].

Sources (accessed 2026-08-15)

  1. W3C DTCG — "Design Tokens specification reaches first stable version" (28 Oct 2025): https://www.w3.org/community/design-tokens/2025/10/28/design-tokens-specification-reaches-first-stable-version/
  2. Design Tokens 2025.10 report index (Format, Color, Resolver): https://www.designtokens.org/tr/2025.10/
  3. Design Tokens Format Module (2025.10 stable; editor's draft dated 30 Jul 2026 also consulted): https://www.designtokens.org/tr/drafts/format/
  4. Design Tokens Resolver Module 2025.10: https://www.designtokens.org/tr/2025.10/resolver/
  5. Style Dictionary — DTCG support page: https://styledictionary.com/info/dtcg/
  6. Style Dictionary releases (v5.5.1, v5.4.0): https://github.com/style-dictionary/style-dictionary/releases
  7. Style Dictionary — predefined formats: https://styledictionary.com/reference/hooks/formats/predefined/
  8. react-native-unistyles v3 — Theming guide: https://www.unistyl.es/v3/guides/theming/
  9. Tamagui — Themes docs: https://tamagui.dev/docs/intro/themes
  10. NativeWind v5 — Dynamic Themes guide: https://www.nativewind.dev/v5/guides/themes
  11. NativeWind v5 — Dark Mode: https://www.nativewind.dev/v5/core-concepts/dark-mode
  12. npm registry metadata (versions/licenses/publish dates for all packages cited), queried via https://registry.npmjs.org/ on 2026-08-15
  13. MDN — light-dark() CSS function (Baseline status): https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark
  14. MDN — Cascade layers: https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Cascade_layers
  15. Base UI — Styling handbook: https://base-ui.com/react/handbook/styling
  16. shadcn/ui changelog — "July 2026: Base UI as the Default": https://ui.shadcn.com/docs/changelog/2026-07-base-ui-default
  17. React Aria Components — Styling: https://react-spectrum.adobe.com/react-aria/styling.html
  18. Ark UI — Styling guide (data-scope/data-part): https://ark-ui.com/docs/guides/styling
  19. SurveyJS — Theming system guide (theme JSON, applyTheme, survey-core/themes): https://surveyjs.io/form-library/documentation/manage-default-themes-and-styles
  20. SurveyJS — Custom question renderer (RendererFactory.registerRenderer, renderAs): https://surveyjs.io/form-library/examples/create-custom-question-renderer/documentation
  21. JSON Forms — Custom renderers tutorial (rankWith, testers): https://jsonforms.io/docs/tutorial/custom-renderers
  22. RJSF — Themes: https://rjsf-team.github.io/react-jsonschema-form/docs/usage/themes/
  23. Form.io — Form Templates developer guide: https://help.form.io/dev/form-templates
  24. SurveyJS — Architecture guide (ReactElementFactory / ElementFactory): https://surveyjs.io/documentation/surveyjs-architecture
  25. W3C — WCAG 2.2 Recommendation: https://www.w3.org/TR/WCAG22/
  26. React Native — I18nManager: https://reactnative.dev/docs/i18nmanager
  27. React Native — AccessibilityInfo: https://reactnative.dev/docs/accessibilityinfo (and Text maxFontSizeMultiplier: https://reactnative.dev/docs/text)
  28. UNHCR Dataviz Platform — brand guidance: https://dataviz.unhcr.org/guidance/
  29. WFP UI Guidelines v0.10.1 — Branding: https://cdn.wfp.org/guides/ui/v0.10.1/basics/branding/
  30. UNICEF brand colour (secondary): https://www.brandcolorcode.com/unicef
  31. UN blue #5B92E5 (secondary): https://www.colorhexa.com/5b92e5
  32. Expo — Fonts guide (config plugin vs useFonts, formats, variable-font caveat): https://docs.expo.dev/develop/user-interface/fonts/ ; Unistyles web styles reference: https://www.unistyl.es/v3/references/web-styles/
  33. Tamagui 2 announcement (29 Dec 2025): https://tamagui.dev/blog/version-two
  34. Shopify Restyle — Defining your theme: https://shopify.github.io/restyle/fundamentals/defining-your-theme/
  35. RJSF — 6.x upgrade guide (GridTemplate): https://rjsf-team.github.io/react-jsonschema-form/docs/migration-guides/v6.x%20upgrade%20guide/
  36. React Native Web / RN Text docs on allowFontScaling, PixelRatio.getFontScale: https://reactnative.dev/docs/pixelratio

Unverified / conflicting notes: WCAG 2.2 fetched page shows "12 December 2024" (edited Recommendation); original Recommendation date 5 Oct 2023 is from prior knowledge. UNICEF/UN-blue hex values come from secondary colour sites, not official brand manuals. Style Dictionary's DTCG page states 2025.10 support is incomplete even though v5.4.0+ handles dimension objects — treat Resolver support as absent until confirmed. Unistyles' web theme mechanism (CSS variables vs class swap) is not explicit in the docs fetched.