Source of truth for all visual decisions across the Flutter mobile app and the web. Every colour, spacing value, and component pattern is documented here.
The primary brand colour is a warm red-orange (#E63946), chosen for colorblind accessibility — distinguishable from greens and blues even with deuteranopia/protanopia.
Mobile: System default (Roboto on Android, SF Pro on iOS) via Material 3
Web: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif
--text-display: 3.5625rem /* 57px */
--text-headline: 1.5rem /* 24px — section titles */
--text-title-lg: 1.375rem /* 22px — card titles */
--text-title: 1rem /* 16px — subtitles */
--text-body: 1rem /* 16px — body text */
--text-body-sm: 0.875rem /* 14px — secondary text */
--text-label: 0.75rem /* 12px — labels, captions */
--text-label-sm: 0.6875rem /* 11px — smallest labels */
Based on a 4px base unit. Canonical values from design_tokens.dart.
--space-xs: 4px; /* 0.25rem — tight gaps, icon padding */
--space-sm: 8px; /* 0.5rem — compact spacing, inline gaps */
--space-md: 12px; /* 0.75rem — default content padding */
--space-lg: 16px; /* 1rem — section padding, card padding */
--space-xl: 24px; /* 1.5rem — large gaps, section margins */
--space-xxl: 32px; /* 2rem — page-level margins */
--radius-none: 0;
--radius-sm: 4px; /* chips, small elements */
--radius-md: 8px; /* buttons, inputs */
--radius-lg: 12px; /* cards, dialogs */
--radius-xl: 16px; /* large cards, bottom sheets */
--radius-circle: 999px; /* avatars, circular buttons */
Condis uses a flat design with minimal shadows. Cards are delineated by borders (1px solid) rather than drop shadows.
| Variant | Background | Text | Border | Radius |
|---|---|---|---|---|
| Primary | #E63946 | White | None | 12px |
| Secondary | gray-200 | Primary | None | 12px |
| Outlined | Transparent | On-surface | 1px outline | 12px |
| Text | Transparent | Primary | None | — |
| Disabled | gray-300 | gray-500 | None | 12px |
12px 24px (vertical × horizontal). Loading state: Replace text with circular progress indicator.Flat design, 1px border, 12px radius, 16px padding. Elevation is always 0 — no box shadow.
On hover: slight background tint and translateY(-2px). Border colour shifts to primary at 20% opacity.
Used for feature cards, program cards, exercise cards. Same border + radius pattern everywhere.
| Property | Value | Token |
|---|---|---|
| Background | White (light) / surface (dark) | --card-bg |
| Border | 1px solid outline-variant | --card-border |
| Radius | 12px | --radius-lg |
| Padding | 16px | --space-lg |
| Elevation | 0 (flat) | --shadow-none |
| Hover BG | White at 8% opacity | --card-hover |
| State | Background | Border | Radius |
|---|---|---|---|
| Default | Surface container | 1px solid outline | 12px |
| Focused | Surface container | 2px solid primary | 12px |
| Error | Surface container | 2px solid error | 12px |
| Token | Size | Usage |
|---|---|---|
xs | 16px | Inline indicators |
sm | 20px | Button icons, list icons |
md | 24px | Default, navigation |
lg | 32px | Feature icons |
xl | 48px | Hero / empty state icons |
xxl | 64px | Splash, onboarding |
| Token | Duration | Usage | CSS Variable |
|---|---|---|---|
| Fast | 150ms | Micro-interactions, toggles, hover states | --transition-fast |
| Normal | 300ms | Page transitions, reveals, modals | --transition-normal |
| Slow | 500ms | Elaborate animations, hero entrances | --transition-slow |
| Name | Curve | Usage |
|---|---|---|
| Standard | easeInOut | General purpose |
| Emphasized | easeInOutCubic | Important transitions |
| Decelerate | easeOut | Enter animations |
| Accelerate | easeIn | Exit animations |
Default brand theme — active in production
| Property | Value |
|---|---|
| Seed Colour | #E63946 (red-orange) |
| Theme Engine | Material 3 ColorScheme.fromSeed |
| Style | Flat, minimal shadows, border-delineated cards |
| Dark Mode | Supported via darkTheme Riverpod provider |
| Error Colour | Same as primary (brand red) |
The mobile app supports whitelabel theming via BrandConfig. Brands can override primary/secondary/accent colours, logos, splash screens, and welcome messages. Configuration is loaded from assets/config/brand.json.
Blue-based whitelabel brand
Green-based whitelabel brand
Purple-based whitelabel brand
{
"appName": "FitCoach Pro",
"appId": "com.fitcoachpro.app",
"colors": {
"primaryLight": "#2563EB",
"primaryDark": "#1D4ED8",
"secondaryLight": "#64748B",
"secondaryDark": "#475569",
"accentLight": "#3B82F6",
"accentDark": "#1E40AF"
},
"assets": {
"logo": "assets/brands/fitcoachpro/logo.png"
},
"messages": {
"welcomeMessage": "Welcome to FitCoach Pro",
"tagline": "Train smarter"
}
}
design-tokens.css via a brand-specific CSS file or inline <style> block.| Aspect | Mobile (Flutter) | Web (HTML/CSS/TS) |
|---|---|---|
| Font family | OS system font (Material 3) | system-ui stack |
| Theme engine | ThemeData + ColorScheme.fromSeed | CSS custom properties |
| Dark mode | darkTheme provider, auto-detects OS | prefers-color-scheme media query |
| Navigation | Bottom nav bar + GoRouter | Page links, topbar |
| Cards | Card widget, elevation 0 + border | div.card with border |
| Buttons | FilledButton, OutlinedButton | .btn-primary, .btn-secondary |
| Bottom sheets | Material bottom sheets | N/A (simple landing pages) |
| Animations | Flutter Curves + Duration | CSS transition |
| Icons | Material Icons | Inline SVG / emoji |
lib/core/
├── constants/
│ ├── design_tokens.dart ← Canonical spacing, radius, elevation, animation
│ └── app_colors.dart ← Canonical colour palette
├── theme/
│ ├── app_theme.dart ← ThemeData providers (light + dark)
│ └── typography.dart ← M3 type scale
└── config/
└── brand_config.dart ← Whitelabel configuration
src/
├── design-tokens.css ← CSS custom properties (mirrors design_tokens.dart)
├── condis-home.css ← Home/landing page styles
└── style.css ← Global base styles + status page
style-guide/
├── index.html ← This page
└── style-guide.css ← Style guide page styles
AppColors.* (mobile) or var(--color-*) (web).AppSpacing.* (mobile) or var(--space-*) (web).elevation: 0 + 1px border, not box shadows.#E63946), not purple or blue.design_tokens.dart is the single source of truth.