[Proposal] Design Tokens for Open edX Mobile (Branding, Theming & Dark Mode)
- 1 Overview
- 2 Problem
- 3 Use Cases
- 4 Proposed Solution
- 5 Key Principles
- 6 In Scope (v1)
- 7 Out of Scope (v1)
- 8 Implementation Plan
- 9 Long-Term Ownership & Maintenance
- 10 Security & Privacy
- 11 Error Handling & Edge Cases
- 12 Telemetry & Success Criteria
- 13 Color Math (Deterministic Formulas)
- 14 Feature Toggle & Deployment
- 15 Open Questions
- 16 Implementation Plan
Overview
This proposal describes a tokens-first theming pipeline for Open edX Mobile, where Paragon design tokens are the single design source of truth, and mobile apps adjust their theme at runtime without requiring new builds.
Design tokens are defined in Paragon as JSON and then compiled via Style Dictionary into:
CSS artifacts for the web (as today, unchanged), and
a single mobile artifact colors.json, consumed by both Android and iOS.
colors.json is deployed as a static file (for example, in the LMS or on a CDN) and exposed via a stable URL.
Live demo: Open edX Mobile Tokens Demo
View the Github ticket for proposal status update
Mobile apps:
do not parse “raw” Paragon JSON directly;
do not rely on platform-specific artifacts such as colors.xml or Swift/ObjC theme files;
instead, fetch a single colors.json from a stable URL;
build the light theme directly from colors.json;
locally derive a dark theme using deterministic color math (no dedicated dark palette required in tokens);
keep neutral gray surfaces under app control so the UI remains clean and legible even if operators choose extreme brand colors.
If tokens are unavailable or the feature is disabled, the app falls back to its built-in color configuration.
The feature is controlled by:
DESIGN_TOKENS_ENABLED: truein config.yaml.
This approach is also a step toward a future multi-tenant mobile application: a tokens-first pipeline and colors.json make it possible to swap themes for different tenants without changing the binary.
Problem
Operators occasionally update their branding and color palettes. Even if this doesn’t happen frequently, any such change on mobile today typically means:
colors are hard-coded into the app;
changing the palette requires a new release to the app stores;
color behavior is often out of sync with the web and behaves unpredictably in dark mode.
This causes:
drift between web and mobile (different colors for the same brand),
inconsistent or broken dark mode,
accessibility regressions (insufficient contrast),
unnecessary operational overhead (a new build even for a once-a-year rebrand).
Additionally, the Open edX ecosystem is moving toward multi-tenant scenarios: one mobile client, multiple installations/tenants. Without a unified token pipeline, this is difficult and expensive to support, because each themed variant tends to turn into a separate branch or build.
We need a single, robust token system that:
drives both web and mobile from the same token set;
lets operators change brand colors without rebuilding the app;
keeps dark mode and contrast under control on mobile;
does not require operators to maintain a separate dark palette in tokens;
lays the foundation for a future multi-tenant mobile app, where different operators can receive their own themes on top of a single codebase.
Use Cases
Learner Stories
Consistent brand & readability
As a learner, I want colors to look consistent and readable across light and dark modes, so the app feels trustworthy and comfortable to use.
Operator Stories
Rebrand without a new build
As an operator, I want my mobile app to automatically follow the same design as my website, without manually adjusting colors on every platform. I want a single source of truth for the brand.
Compliance & Governance
Store-safe remote configuration
As a compliance owner, I need theming to be driven purely by static presentation data (tokens) that:
are fetched securely over HTTPS,
are applied in a deterministic way,
do not contain arbitrary code or business logic.
Proposed Solution
1. Single Source of Truth: Paragon Design Tokens
Design tokens (colors and related metadata) are defined in Paragon as JSON.
These tokens are the single design source of truth for:
the web (CSS), and
mobile apps (via a single colors.json artifact).
Paragon tokens remain “raw” design data. For use in products, they are transformed into consumption-friendly artifacts.
2. Style Dictionary as the Build Pipeline
We use Style Dictionary to compile raw Paragon tokens into:
Web: existing CSS outputs (no change to how the web works today).
Mobile: a normalized mobile artifact colors.json that contains:
brand accent ramps (primary / secondary),
semantic roles for accent usage (buttons, toggles, links, borders),
on-colors (text and icons on accent surfaces).
Base pipeline:
Paragon JSON Tokens (design source of truth)
→ Style Dictionary (extended config: web + mobile)
→ Artifacts:
Web: CSS variables
Mobile: colors.json (the contract for Android/iOS)
→ Deployed as static assets (LMS / CDN)
→ Mobile apps fetch colors.json and map tokens into their native theme systems (Compose, SwiftUI, etc.).
3. Distribution & Hosting
The mobile artifact colors.json is deployed as a static file, for example:
https://{lms-address}/static/tokens/mobile/colors.jsonKey properties:
a fixed, documented URL for colors.json;
can be hosted on:
LMS static assets,
an external CDN,
any operator-controlled static hosting with HTTPS;
no server-side logic required — just static file hosting.
This matches the expectation that mobile clients can consume themes from “any HTTPS URL,” while keeping the path predictable and documented.
4. Mobile Consumption & Caching
For each mobile app (Android/iOS):
The app knows the canonical URL for colors.json.
On cold start, when DESIGN_TOKENS_ENABLED: true:
the app loads the “last known good theme” from local cache or uses the built-in theme;
it renders the first frame using this theme (no flash of unthemed UI);
in the background, it fetches the latest colors.json using standard HTTP caching (ETag / Last-Modified).
The app validates the received colors.json:
checks structure and types,
checks presence of required keys,
optionally checks signatures/integrity.
If the payload is valid and different from the cached version:
it is stored as the new “last known good” theme;
it will take effect on the next cold start.
If the payload is unavailable or invalid:
the app keeps the current theme (cached or built-in),
and logs the failure for later analysis.
This ensures:
deterministic theming behavior;
no mid-session “color popping”;
any issues with colors.json result in a safe fallback to a stable theme.
5. Dark Mode: Local Derivation from Light Tokens
The design tokens do not provide a dedicated dark palette.
Operators define only a light palette:
base brand color,
accent ramps,
semantic roles (primary/secondary, on-colors, borders).
The mobile app uses deterministic color math to derive the dark theme from these light tokens.
When computing the dark theme, we consider:
target contrast levels (WCAG),
brightness caps (to avoid glare in dark mode),
saturation limits (to avoid “neon” colors).
Behavior:
Light theme: colors from tokens are applied as-is, with no automatic adjustment.
Dark theme: is computed on-device using the formulas below, with the light tokens as input.
As a result:
dark mode logic is controlled by the mobile apps;
readability and visual quality are preserved even with suboptimal brand colors;
operators do not need to maintain two independent palettes (light/dark).
6. Neutral Surfaces & Gray Backgrounds
To keep the UI coherent and readable, neutral gray surfaces are not controlled by tokens in v1:
The app defines and owns its own set of neutral surfaces:
background colors,
cards,
list surfaces,
base layout layers.
These surfaces:
are neutral in color,
are tested against a wide range of brand accents,
are optimized for accessibility and readability.
The role of tokens in v1 is to:
control accent colors (primary/secondary),
control text/icon colors on accent elements,
control accent borders on top of neutral surfaces.
Consequences:
even with very bright, dark, or unusual brand colors, the UI remains “designed”:
core surfaces stay neutral,
accents are automatically adjusted as needed.
operators get strong brand presence through buttons, toggles, and highlights, without risking unreadable backgrounds.
In future versions (v2+), we may allow tokens to drive neutral surfaces as well, but in v1 this is a deliberate app-owned safety layer.
Key Principles
Single design source of truth: Paragon JSON tokens.
Dedicated mobile artifact: Style Dictionary generates colors.json as the contract for Android and iOS.
Runtime theming: mobile apps do not depend on build-time theme generation; all color configuration comes from colors.json at runtime.
Local dark mode: the dark theme is computed on-device from light tokens using predictable, testable formulas.
Stable neutral surfaces: gray backgrounds and base surfaces are owned by the app and are not overridden by tokens in v1.
Apply theme before first frame: users do not see a flash of the default theme.
Deterministic color math: color transformations are predictable and debuggable.
No explicit versioning contracts: if colors.json no longer matches what the client expects, the app simply falls back to its built-in theme.
Foundation for multi-tenant: a runtime token pipeline enables future per-tenant themes without changing the binary.
In Scope (v1)
In scope for v1:
Colors only, including:
brand accent ramps (primary),
semantic roles for accent usage (buttons, toggles, links, borders),
on-colors for text and icons on accent surfaces.
Dark theme, computed locally on-device from light tokens.
Fixed neutral surfaces, defined by the app itself (light and dark).
Style Dictionary integration:
extend existing Paragon configuration to generate colors.json for mobile.
Fetch + cache + validation:
loading colors.json over HTTPS,
using HTTP caching (ETag/Last-Modified),
structural validation,
persisting the “last known good theme.”
Feature flag:
DESIGN_TOKENS_ENABLED: truewhen true, the app attempts to load colors.json;
when false, it uses the built-in theme.
Out of Scope (v1)
Explicitly out of scope for v1:
typography tokens (fonts, sizes, weights);
spacing, radius, elevation tokens;
per-user theming;
full multi-tenant theming (separate tokens per operator/tenant — that is a future step);
controlling neutral gray surfaces via tokens.
Implementation Plan
Milestone 1 — Core Token Pipeline & Mobile Integration
Paragon / Style Dictionary
Define and lock the set of color tokens in Paragon required for mobile.
Extend Style Dictionary configuration to generate:
CSS outputs for the web (as today),
colors.json for mobile.
Static Hosting
Define the canonical URL for colors.json, for example:
/static/tokens/mobile/colors.jsonDocument hosting requirements for operators (LMS static assets or CDN).
Mobile Apps (Light Theme)
Implement:
colors.json fetch,
caching and HTTP header usage,
structural validation,
fallback to built-in theme.
Wire token-driven accents into:
buttons,
toggles,
primary CTAs,
links,
accent borders.
Milestone 2 — Dark Mode & Accessibility
Local Dark Theme Generation
Implement deterministic dark theme generation from light tokens according to the color math described below.
Ensure:
target contrast levels (especially for text),
brightness caps (anti-glare),
saturation control (anti-neon).
Neutral Surfaces
Lock in neutral gray palettes for light and dark modes.
Test behavior of the UI with a wide range of brand colors (including “difficult” cases).
Accessibility & QA
Add automated checks for:
contrast of tokenized surfaces,
visual regressions when brand colors change,
correct fallback behavior.
Milestone 3 — Operational Polish & Extensions (Optional)
Operator Tools
CLI or CI validator for colors.json.
Documentation with examples:
colors.json structure,
typical brand configurations.
Telemetry
Track:
how often colors.json is used vs the built-in theme,
frequency of fetch/validation errors,
how often automatic contrast/saturation adjustments are applied.
Future Extensions
OKLCH-based ramps,
typography and spacing tokens,
full multi-tenant theming, where colors.json can differ per tenant.
Long-Term Ownership & Maintenance
Engineering / Tech: AXIM Mobile WG + Open edX Mobile maintainers own the implementation and maintenance of the mobile side and its integration with colors.json.
Design System WG: owns Paragon tokens, ramp structure, and alignment between web and mobile.
Accessibility WG: formalizes contrast requirements and color math constraints, audits dark-mode behavior.
Operators: own hosting of colors.json and the choice of brand colors within recommended ranges.
Security & Privacy
All token artifacts, including colors.json, are fetched over HTTPS.
colors.json contains no PII — only presentation data (colors and metadata).
Optionally, we may use:
response signing,
domain or certificate pinning for token URLs.
Cache integrity is ensured through:
structural validation,
type checking,
robust error handling.
On any failure, the app falls back to the last known good theme or its built-in theme.
Error Handling & Edge Cases
DESIGN_TOKENS_ENABLED = false
→ The app uses its built-in color configuration (light + dark) and app-owned neutral surfaces.
Network failure / unreachable URL
→ The app uses the cached “last known good” theme or the built-in theme, and logs the error.
Invalid colors.json (schema mismatch, missing keys, invalid values)
→ The payload is rejected, the event is logged, and the previous working or built-in theme remains in use.
Extreme brand colors (too bright, too dark, too saturated)
→ Local color math:
clamps brightness and saturation,
enforces minimum contrast,
maintains visual balance against neutral surfaces.
Telemetry & Success Criteria
Functional
≥ 99.9% of cold starts apply a theme before the first frame.
< 0.1% of sessions hit a token-related fallback.
≥ 99% of tokenized surfaces meet target contrast thresholds.
Business
≥ 80% reduction in time-to-rebrand for mobile apps.
≥ 60% fewer support tickets related to theming and dark-mode issues.
Technical / Reliability
colors.json payload size < 10 KB.
High HTTP cache hit rate.
p95 fetch time low enough for a smooth UX.
Logging includes:
validation results,
fallback reasons,
automatic color adjustment counts.
Color Math (Deterministic Formulas)
Note: the implementation may be encapsulated behind utility functions, but behavior must remain deterministic and testable.
Notation (sRGB)
Channels in [0…1].