Design system
Tokens and components extracted from the Figma file "Inwallsept2025" (canvas "MVP Flows TO BE USED FOR MVP ONLY"), then renamed onto one consistent scale and re-typeset in Manrope. Source code: css/styles.css.
Token architecture
A raw value (hex, px) never appears directly in a component. It goes through a semantic token, itself built on a primitive token.
| Layer | Role | Example |
|---|---|---|
| Primitive | Figma value, renamed onto a standard scale | --brand-700: #551BCD; |
| Semantic | Alias with meaning | --color-accent: var(--brand-700); |
| Component | Specific use in one component | --button-primary-bg: var(--gradient-cta); |
Naming contract
Figma's own keys were sparse and inconsistent per color family — brand went 300 / 400 / 500 then an unnumbered "enabled," then "800"; warning only had 200 and 600; success only 200 and 600. Every family below has been renumbered onto one shared scale, so a token's role is predictable from its number alone without opening the color to check — the same contract Tailwind and Material use.
| Step | Role | Rule |
|---|---|---|
-100 | Lightest tint | Backgrounds and tint fills only, never text |
-300 / -400 | Light / mid | Decorative, large fills, never small text |
-500 | Base | Default interactive state — links, mid-emphasis icons |
-700 | Strong | Always safe as text/icon on white (≥4.5:1, verified per color below) |
-900 | Darkest | Inverted surfaces (dark panels, tab bar), max-emphasis text |
| Figma name | Renamed to |
|---|---|
brand InWall/300 | --brand-300 (unchanged) |
brand InWall/400 | --brand-400 (unchanged) |
brand InWall/500 / Link-Active | --brand-500 (unchanged) |
interactive-primary-brand/enabled | --brand-700 |
brand InWall/800 / Surface-Inverted | --brand-900 |
success/200 | --success-100 |
success/600 | --success-700 |
warning/200 | --warning-100 |
Text-Thirdary (Figma's own typo) | --text-tertiary |
Colors
One brand hue — a violet-purple — carries the entire app, advertiser and Inwaller screens alike. Depth is what varies (a light tint for secondary surfaces, the same brand purple for every primary action), not a second accent — the earlier provisional lime/indigo split didn't match the real file.
Brand InWall
Gradients
The app icon's diagonal sweep is Inwall's most distinctive visual asset — the first pass of this page under-used it, applying it only to the logo mark. Two variants now exist, because one gradient isn't safe in every context text can sit on it:
| Token | Stops | Use |
|---|---|---|
--gradient-hero | #9F68F8 → #530BC9, Figma's exact logo gradient | Icon-only contexts: app icon, FAB, avatars — never under text (light end alone is 3.6:1, fails AA text) |
--gradient-cta | --brand-500 → --brand-700 | Primary button fill — both stops independently clear 4.5:1 with a white label, safe wherever it sits |
Text
See accessibility for why text-tertiary was darkened from the raw Figma export (also renamed: Figma's own key was misspelled "Thirdary").
Surface
Feedback & data
Success, warning, critical, plus the three network colors used in the shares donut chart on the advertiser dashboard.
Typography
One family across the app: Manrope, replacing the SF Pro Display pass this page shipped with earlier. Variable weight 200–800, one license, one look on iOS, Android, and the web — the cross-platform gap SF Pro had (Apple-only, no native Android equivalent) doesn't exist with Manrope, so it's used directly rather than paired with a fallback face.
Letter-spacing is expressed in em below, not the fixed px values Figma exports — an em value scales with font-size automatically, a px value has to be hand-picked at every step. Values are the corrected scale; see the type fixes table for what changed and why.
Spacing & radius
Radius reads very rounded throughout the file: list rows and input pills go almost fully round, not just soft corners.
| Token | Value | Usage |
|---|---|---|
--radius-sm | 10px | Small UI elements |
--radius-md | 20px | Cards, dashboard tiles |
--radius-lg | 28px | Large surfaces, sheet corners |
--radius-pill | 999px | Buttons, list rows, badges, tab bar, FAB |
Loop steps
The four-step core loop component used on the specs page, colored by which side owns the step.
Fund & split
Set budget, price per network.
Scroll & share
Browse feed, share an ad.
Side cards
Campaign console
- Set budget and price per share
Vertical feed
- Share, get paid instantly
App chrome
Bottom tab bar: dark brand-900 fill, floating pill, rounded top corners on the screen itself. The FAB on the advertiser dashboard now carries --gradient-hero (icon-only, safe); the feed's own bottom-right button reuses the logomark instead of a generic icon.
| Screen | Right chrome element |
|---|---|
Advertiser dashboard (Full ad) | Gradient circular "+" FAB — create a new ad |
Feed (explore / infinite scroll) | Circular button using the Inwall logomark — share this ad |
Accessibility
Target: WCAG 2.1/2.2 level AA. Checked against the real Figma export values (computed contrast ratios, not visual guesses). Four issues found and fixed in the tokens above; three more verified as already passing.
Contrast fixes
| Token | Before | After | Why |
|---|---|---|---|
--text-tertiary | Figma export #6B6F8C — 4.56:1 on --surface-secondary | #5B5F7A — 5.81:1 on --surface-secondary, 6.25:1 on white | Used for secondary/placeholder text down to 13px ("Text X-Small"). At 4.56:1 it clears the 4.5:1 AA floor by less than a rounding error. Same exact color and same fix HugeOne's design system needed. |
--color-warning (text/icon) | #D1870A — 2.9:1 on white | #9A6208 — 5.1:1 on white | Used as text color on .tag.risk and .note.open. 2.9:1 fails AA outright for text (needs 4.5:1). The original is kept as --color-warning-border for fills/borders, where the lower 3:1 non-text threshold applies and it already passes. |
--brand-700 #551BCD reaches 8.7:1 with white button labels — well past AA and close to AAA. --brand-500 #803CF2 (Figma's "Link-Active") reaches 5.5:1 as running text on white. --gradient-cta's two stops (500 and 700) both independently clear 4.5:1, so the gradient stays safe under a button label at every point along it — unlike --gradient-hero, whose light end (#9F68F8) drops to 3.6:1 and is restricted to icon-only use for exactly that reason.Type scale fixes
Figma's line-heights and tracking were copied from the file as-is for the first pass of this page; on review, three headings ran looser than any mainstream mobile guideline (iOS HIG, Material) recommends, and the largest display size used positive tracking where large numerals read better tight. Tracking is now in em, not px, for the reason noted above the type scale.
| Style | Before | After | Why |
|---|---|---|---|
| Display Huge (money amounts) | 40/44, +0.5px tracking | 40/44, −0.02em tracking | Positive tracking on a 40px numeral loosens exactly the figure the app most wants to read as solid and precise (a budget, a payout). Large display sizes read better with neutral-to-negative tracking; small text is where positive tracking earns its keep (see Link Small, X-Small below). |
| Display Small Bold | 20/32 (1.6 ratio) | 20/26 (1.3 ratio) | 1.6 is loose even for a multi-line paragraph, let alone a one-line screen title ("Create an ad"). 1.2–1.3 is standard for headings at this size. |
| Text Semibold | 18/28 (1.56 ratio) | 18/24 (1.33 ratio) | Same over-loose pattern, used for short confirmation copy ("Your ad is now live") that doesn't need paragraph-level breathing room. |
| Text Large | 20/32 (1.6 ratio) | 20/28 (1.4 ratio) | Used for a one-line ad title ("Ôde à la vie"); 1.4 is the standard upper bound for body-adjacent text, 1.6 is paragraph territory. |
Typeface (cross-platform gap)
Figma specified SF Pro Display with no fallback — an Apple-only font that, on Android (half the target platforms per the specs), silently drops to the OS default and the two builds stop matching. Now replaced app-wide with Manrope: one variable face, licensed for iOS, Android (via Flutter's google_fonts package or a bundled asset), and the web, so the cross-platform gap doesn't need a fallback face to paper over it.
Touch targets
The budget screen's price-per-share steppers render at 32×32px in Figma — under the 44×44px minimum (WCAG 2.5.8 AA / Apple HIG), and the WCAG 2.2 floor of 24×24px doesn't leave much margin either for a control that adjusts a money amount. Fixed: .stepper-btn is now 44×44px (token --touch-target-min).
Open, not yet resolved
- The feed's share button sits over a variable video/photo background — contrast needs checking against real creative, not a static mock, since the button has no solid background of its own in that context.
- Spacing scale had a 32px → 56px jump (skipping 40/48) inconsistent with the otherwise steady 4px progression below it — closed to 32 → 48 in
--space-9, but the scale hasn't been re-audited past that single fix. - Donut-chart legend colors (Facebook blue, Instagram pink, Snapchat yellow) haven't been checked for color-blind distinguishability — a shares-by-network chart that only differs by hue is a real risk for deuteranopia/protanopia; needs a pattern or label fallback, not color alone.
Usage notes
- Extraction covers the 11 top-level frames on the "MVP Flows TO BE USED FOR MVP ONLY" canvas — inputs, modals, and empty states beyond what's shown on those screens still need documenting.
- Manrope isn't embedded as a variable-font file on this web page (loaded from Google Fonts instead); the Flutter app should bundle it directly via
google_fontsor a local asset rather than a network fetch. - Only one brand accent exists in the file (violet-purple) — resist introducing a second "advertiser accent," a second hue doesn't match the actual design.
--warning-600and--warning-700are not interchangeable: 600 for fills/borders/large icons (3:1 threshold), 700 for text and small icons (4.5:1 threshold). Reaching for 600 as a text color is exactly the bug this page fixed.--gradient-heroand--gradient-ctaare not interchangeable either: hero for icon-only surfaces, cta for anything with a text label on top. Reaching for hero under a button label reintroduces the 3.6:1 contrast failure cta was built to avoid.