Guy Nagar Contact
Case-study artifact

One language,
two themes.

The design system behind a CySEC-regulated margin trading platform. Nine colour pairs resolved per theme, one green in each, Outfit everywhere — an app, an internal admin and a marketing site that read as one product.

System snapshot · selected production foundations and components from the 50K product. Some screens show earlier product states and are labelled accordingly.

Product
Margin trading platform · iOS, Android, Web
Surfaces
Trading app, internal admin, marketing site
Themes
Dark first; light mapped after the system stabilised
Implemented in
Flutter and Dart · theme-resolved colour tokens
Responsibility
Foundations, components, motion, iconography, illustration
Constraint
Logo and brand green settled early, not open to change
01

What the system had to survive.

Six rules that decided the rest. Each one exists because something in a trading product punishes you for getting it wrong.

01Money makes mistakes expensive

An ambiguous control in a to-do app costs a minute. Here it costs a position. Every component is designed against the worst reading of it, not the intended one.

02Colour is already spoken for

Green means up and red means down before anyone opens the app. The system works around that rather than against it.

03Regulation is content

Risk disclosure appears in specific places at specific moments. It is designed into the screen instead of being appended to it.

04Dark first, light mapped after

Traders sit with charts for long stretches, so dark was built first. Once the system stabilised, the Light theme was mapped from the same foundations to preserve hierarchy, contrast and behaviour.

05Both directions cost the same

Short and Buy are equally reachable and equally weighted. Weighting one is a recommendation, which sat outside the role and regulatory boundary of the product’s information layer.

06A component means one thing

Switches apply immediately; choices confirmed later use a checkbox. Controls are chosen by behaviour, not by which looks better in a layout.

02

Nine colour pairs. Eighteen values. One theme map.

A colour is stored as a theme pair, not a single hex: one value for light, one for dark, resolved by the active theme. Switching themes never means redesigning a screen.

This is the product’s own colour documentation, row for row, with one correction applied: the original doc listed disabled buttons under Gray2, and the current component files resolve them to Gray3, so the map reflects the components. Read any line across: the usage stays, the value swaps.

Light
Dark
White#FFFFFF
DarkGray#191B1C
Background. In light it also carries clickable containers.
LightGray1#F8F8F8
DarkGray1#262629
Background and bubbles — the card, the row, the sheet.
LightGray2#E2E9EC
DarkGray2#393942
Borders and dividers.
LightGray3#ABBAB8
DarkGray3#5D5D64
Scroll bars, arrows — and the disabled button fill in the current component files.
LightGray4#879696
DarkGray4#8C9B98
Menu icons. The one pair that barely moves — an icon grey that works on both.
MidGray#505C59
DarkMidGray#BCD1CC
Subtext and small headlines.
DarkGray#191B1C
White#FFFFFF
Main text. The same hex that is dark’s background is light’s text — the palette mirrors instead of multiplying.
Primary#269969
DarkPrimary#1DDE8D
Calls for action, and the rising price. One green per theme — anything else green is a mistake, not a token.
Red#C74A54
DarkRed#ED5562
Alerts, cancel, delete, and the falling price.

The green

The product uses one primary colour role per theme. Dark resolves to #1DDE8D; light resolves to #269969. These are theme counterparts, not separate semantic greens. The light primary keeps the white label used in the shipped product, and no additional UI greens are part of the system.

Direction is never communicated by colour alone — signed values, arrows and labels carry the meaning; colour reinforces it. Section 08 holds the rules.

03

The same screen, answering to two themes.

Not two designs — one layout, one hierarchy, one set of roles, resolved twice. Dark is the default; light is chosen by the user in settings.

The Explore screen in dark: profit header, search, category chips, instrument rows with short and buy prices, live Tesla chart, and the bottom navigation with Trade, Ideas and Portfolio
DarkDarkGray ground, white text, #1DDE8D for actions and the rising price.
The same Explore screen in light: identical layout and hierarchy with white ground, dark text and the deeper green
LightThe same rows, chips, chart and nav — every role resolved to its light value, #269969 for actions.

The map in section 02 is the whole mechanism: the whole mechanism: colours are stored as theme pairs and resolved per theme. That is why light could be added without rebuilding a single screen.

04

Outfit, at seven weights.

One family for the app, the web, the brand and the marketing assets. Geometric enough to keep numerals unambiguous, neutral enough that a price never looks like an opinion.

Aglit · w200
Agreg · w300
Agmed · w400
AgsemiBold · w500
Agbold · w600
AgextraBold · w700
Agblack · w800

The names are the code’s, and they run half a step heavier than the convention — semiBold is w500, bold is w600. Inside one product that costs nothing; every screen uses the same dictionary.

The scale, in the styles that carry it

Sizes run from 8sp to 100sp; these are the steps a screen is actually built from. Line height is 1.0 — text sits in components, and the component owns its spacing.

captionreg1 · 12sp · w300
Available funds · Est. fees · Terms apply
body smallreg2 · 14sp · w300
Orders placed while the market is closed fill at the open.
bodyreg3 · 16sp · w300
Leverage splits one decision into three numbers.
subtitlesemiBold5 · 20sp · w500
Required margin
titlebold7 · 26sp · w600
Order entry
displayextraBold11 · 48sp · w700
€44,401
megamega · 100sp · w700
€888

As implemented

Simplified implementation example.

static TextStyle bold3 = TextStyle(fontFamily: font, fontSize: 16.sp,
    fontWeight: FontWeight.w600, height: 1.0, color: investBlack);

static Color get investLightGray1Color {
  switch (selectedColorScheme) {
    case D1ColorScheme.light: return investLightGray1;   // #F8F8F8
    case D1ColorScheme.dark:  return investDarkGray1;    // dark counterpart
  }
}

The palette is theme-resolved rather than semantically aliased — values are named by colour and resolved per theme through a switch like this. Light was added without rebuilding screens because every colour already went through that gate.

05

Eight, with a four for optical work.

Padding, gaps and rhythm come off an 8pt base with a 4px micro step, so nobody chooses between 13, 17 and 22.

4
8
16
24
32
Annotated pop-up spec: 32px top padding, 24px above and below the title block, 16px side margins, 8px between the two buttons, 15px button radius and 23px sheet radius
The spec as it is handed over — every distance named, nothing left to taste. 32 above, 24 around the title, 16 at the sides, 8 between the buttons.

Radius

15 · buttons
23 · sheets & pop-ups
pill · chips

The primary button contract

Primary actions placed at the bottom of mobile screens and sheets use the full available width, with a 16px side inset, a 48px height and an 8px gap between stacked actions — the component file is drawn at 358×48, which is exactly a 390 frame minus twice 16.

Primary button, dark theme: brand green fill with near-black label Disabled primary button, dark theme: DarkGray3 fill
Dark — enabled on #1DDE8D, disabled on DarkGray3.
Primary button, light theme: deep green fill with white label Disabled primary button, light theme: LightGray3 fill
Light — enabled on #269969, disabled on LightGray3.

These four are the component files themselves, not a recreation. Disabled resolves to DarkGray3 and LightGray3 — the correction already noted in the colour map.

06

The brand layer.

The logo and the green were settled early and were not open to change — the system’s job was to give them rules to live by.

50K logo lockups in the four approved variants: green on dark, dark on green, green on white and white on green
The four approved lockups. Nothing else ships.
The 50K icon library: a grid of line icons covering trading, account, education and messaging
The icon library — one drawing per icon, re-inked per theme through the same colour pairs.
07

Components, in every state that matters.

Rebuilt live from the token values, so each state is the real recipe rather than a picture of one — and the shipped pop-ups beside them, as vectors.

Buttons

One primary per screen; at the bottom of a mobile screen it takes the full available width.

Fields

The error carries a number, not an adjective. “Invalid amount” tells someone they are wrong; the available figure tells them what to do next.

Selection

Popular Watchlist Risers & fallers Indices
Open Pending Closed

We used switches only for changes that took effect immediately. Choices that were confirmed later used checkboxes or another selection pattern — the decision was based on behaviour, not on which control looked better in the layout.

Direction

Short320.29 Buy320.48

Not a segmented control, deliberately. Both prices are live before a direction is chosen, and the selected side is an underline rather than a filled background — a filled green Buy next to an empty Short would weight the decision.

Rows

TeslaTSLA · Stock
320.48+1.01%
Nasdaq 100Index
20,314−0.42%
GoldCommodity
2,655.100.00%

Unchanged is grey. Spending a colour on “nothing happened” makes the two that matter harder to spot.

Callouts

Market closedOrders can be placed now and fill when the market opens, in 6h 14m.
Deposit failedYour card was declined. Nothing was charged. Try another payment method.

Each one names the consequence, not the status. “Market closed” is a fact; “orders fill when it opens” is what the reader actually needed.

Pop-ups, as shipped

Deposit failed pop-up: wallet illustration, plain statement that the deposit was unsuccessful due to insufficient funds, a support link, and a full-width OK button
Failure says what happened and what to do next — another method or support — and the illustration keeps its warmth.
Tutorial completed pop-up: trophy illustration and a full-width continue button
Every sheet is the same anatomy: 23 radius, centred illustration, one full-width primary at 16 from the edges.
08

Up, down, and the third case nobody designs.

Market direction is the one place where colour carries meaning on its own — which is exactly why it is never allowed to.

+€373

Sign first, then colour. Someone who cannot distinguish the hues still reads the direction.

−€373

Loss at the same size and weight as gain. Shrinking it would be a design opinion about risk.

  • Never colour alone. Every coloured value carries a sign, an arrow or a word.
  • Never a container. Market state is text. Filled shapes belong to actions.
  • Symmetry. Profit and loss share type, size and position. The projection on the order ticket shows both, equally weighted.
  • Flat is grey. No change gets no colour.
Why the pairing rule is not optionalOn the dark background the green computes to roughly 9.8:1 and the red to about 5:1 — passes, but the two sit close enough in luminance that a red-green deficiency flattens the difference. The sign carries the meaning; the colour only confirms it.
09

Turning a dataset into a sentence.

Most financial data arrives as a table. The insight components exist to lead with the finding and keep the evidence underneath it, so the shortcut never hides what produced it.

Radar card: Apple is at a one month peak, with the price chart, projected up and down paths, and short and buy actions Analyst consensus card: average rating 4.2 on a sell-to-buy scale with a target price, and the data source named Margin alert card: your account margin level is running low, positions are about to be closed, with an add funds action Portfolio position row: an Apple buy position of 200 euros at 1 to 100 leverage, in profit by 54 euros Portfolio position row: a Google short position of 100 euros, down 75 euros, shown in red Get real-time signals card asking to enable push notifications for major market events

Each card states its conclusion in words, shows the data behind it, and makes both actions equally easy to take. A card offering only Buy would be a recommendation — outside the role and regulatory boundary of the original Radar layer.

Instrument detail: candlestick chart with timeframe controls, key statistics and buy and sell actions
Instrument detailChart, statistics and both actions, always reachable.
Commissions screen listing fees, interest rates, currency conversion and spreads
Dense dataWhere a table is genuinely the right answer, it stays one.
An earlier Market Radar screen with movers, indices and a market news card
Earlier Market RadarAn earlier product state — findings surfaced without being asked for. The current layer is the Ideas tab shown in the theme pair above.
10

Motion, and the second before the screen arrives.

Micro-interactions are animated in After Effects and ship as Lottie — deposits, rewards, confirmations, empty states — so the product moves the same way everywhere at a few kilobytes each.

Deposit
Funds arrive
Reward
Boost
Personal details

The files themselves, playing. With reduced motion they park on a readable frame instead.

Skeletons

Some screens took about a second to fill. A second is not long; a blank screen for a second makes a product feel unreliable. The skeleton holds the real structure in advance — the same sizes, spacing and radii the content will use — so when data lands it appears exactly where the placeholder was.

Designed and implemented in Flutter. It did not make anything load faster — it made the wait legible and stopped the layout rearranging itself under someone’s eyes.

11

The patterns the system exists for.

Components are the vocabulary. These are the sentences — the places where getting the language right decides whether someone trusts the product.

Order ticket: direction toggle with both prices, required margin as the headline value, amount slider, likely profit and loss, funds, and the confirm button
Order ticketRequired margin is the headline; position value and share count follow it. All three are editable, and all three move together.
An earlier order ticket variant using fixed amount buttons instead of a slider
The variant that lostFixed amounts, borrowed from deposit. Both were tested; the slider won and the buttons stayed where they started.
Identity verification screen listing document, selfie and proof of address steps with their states
VerificationEvery step visible with its own state, so progress is legible in a flow people abandon.
Deposit screen with preset amounts, minimum and maximum, a security reassurance and the saved card
DepositPreset amounts belong here — choosing fast without thinking is correct for this decision.
Ideas feed: market content and related instruments surfaced together
Ideas feedMarket content and relevant instruments are surfaced together so information can lead naturally into exploration.
Deposit failed dialog explaining the failure and offering to try another payment method or contact support
FailureStates what happened and offers the next move — try again, another method, or support.
Price alerts screen in its empty state, with a set new alert action
Empty alert stateThe screen explains what is missing and gives one clear action that resolves it.
Feedback Hub screen with an available survey, suggest a feature and report a bug
Feedback HubSurveys, feature requests and bug reporting give users a direct route to the product team.
12

Rules, and the ones worth breaking.

A system is easier to build than to keep. Most of the pressure on it arrives as small, reasonable-looking requests.

Always

  • Pair colour with a sign or a word. Colour alone never carries meaning.
  • Name the consequence. A closed market is a status; orders filling at the open is the consequence.
  • Keep both directions equal. Same size, same weight, same reachability.
  • Put the indicator where the decision happens. Text being on the page is not the same as text being read.

Never

  • Borrow a control for its looks. A button shaped like a tab teaches the wrong thing everywhere else.
  • Use a switch for a choice that is confirmed later. Immediate effect takes a switch; confirmed choices take a checkbox — behaviour decides, not layout.
  • Invent a second green. One per theme. A shade that is almost the brand green is a bug, not a nuance.
  • Style a component outside the system for one screen. It looks like one screen and it costs every other one.

And when to break them

The system was never untouchable. Where a feature genuinely needed behaviour the system did not have, it was extended — a new component, and sometimes an existing rule broken on purpose. The test was whether the exception served something real in the product, or was a local fix for one screen. Hard disagreements went to an A/B test rather than to whoever argued longest.

On provenance. The colour map is the product’s own colour documentation, read at the file level — names, values and usage as written, with the light and dark columns paired the way the source pairs them. Type is the Flutter implementation: Outfit, the seven named weights, the sp scale, line-height 1.0. Spacing, radii and the button contract come from the annotated spec; the primary buttons and pop-ups shown are the component files themselves. Screens are the shipped product. Live components on this page are rebuilt from those values, so every state shown is real rather than a picture of one.

Design and design system: Guy Nagar. No customer data appears on this page.

← 50K case study Portfolio guynagar21@gmail.com