Phase 1.10 · admin
Bright brand. Calm interface.
Every token and component, read from the live theme rather than copied into this page. Switch the theme to check both — light and dark are equal citizens, not one inverted from the other.
- sections
- 20sections
- primitives
- 19primitives
- terms
- 66terms
- gradients
- 4gradients
§2 · Brand assets
Supplied artwork. Never redrawn.
Horizontal for headers, sign-in and email; vertical for splash and onboarding. Never stretched, recoloured, or rebuilt as HTML text.
Horizontal
Headers, sign-in, email
Vertical
Splash, onboarding, compact brand moments
⚠️ In dark mode the mark sits on a light neutral container rather than being recoloured. Its wordmark ink #0037AC measures 1.93:1 on the dark canvas, and §2 forbids programmatic recolouring. Switch the theme and watch the container appear behind it.
§3–§5 · Colour
Five brand colours. Everything else does the structural work.
Values and contrast are measured in the browser, in the theme you are viewing. Ratios are against bg-surface.
Brand
The logo palette. Identical in both themes — never redefined per theme (§3).
brand-blue…brand-deep…brand-teal…brand-lime…brand-coral…Brand as text
Used when a brand colour must carry type. The fills above are 1.95:1 to 3.21:1 on white and cannot (D-072).
brand-blue-ink…brand-teal-ink…brand-lime-ink…brand-coral-ink…Surface
utilities: bg-canvas · bg-surface · bg-subtle · bg-raised
bg-canvas…bg-surface…bg-subtle…bg-raised…Text
utilities: text-ink · text-ink-2 · text-ink-3 — never text-primary, which is shadcn’s and means the brand blue.
text-primary…text-secondary…text-muted…text-on-brand…Ink on a brand fill
text-on-brand is white and belongs only on blue or deep. text-on-warm is the dark ink coral and lime need, and is fixed in both themes because the fills under it are (§3, D-075).
text-on-brand…text-on-warm…Border
utilities: border-line · border-line-strong · border-field. Only border-field clears WCAG 1.4.11’s 3:1 for a control boundary.
border-default…border-strong…border-input…State
utilities: text-success · text-warning · text-error · text-info. Error is semantic, never brand coral (§13).
state-success…state-warning…state-error…state-info…Card tints
utilities: bg-progress · bg-completed · bg-upcoming · bg-information (§15). ⚠️ text-ink-3 is never legible on these — see the note under Card families.
accent-progress…accent-completed…accent-upcoming…accent-information…Button depth
The slab a press-shaped button sinks into (D-075). Behind the fill, so it carries no contrast floor. Blue and coral are theme-fixed; only the neutral one follows the surface.
depth-blue…depth-coral…depth-neutral…§11 · Gradients
Four ramps. Three inks, and one that carries none.
Selected brand moments only — never the default primary button, and never a rainbow of all five brand colours.
Primary brand
--gradient-brandOnboarding accents, splash artwork, decorative fills
No text. White is 2.38:1 at the teal end, dark ink 2.71:1 at the blue.
Trust
--gradient-deep§40 subscription and premium surfaces, sign-in, splash
White, 5.86:1 → 9.80:1. The only ramp white is safe on.
Celebration
--gradient-celebrateMoments Engine and completion. Used sparingly
Dark ink, 6.66:1 → 8.13:1.
Warm attention
--gradient-warmReminders, streaks, a nudge that is not an error
Dark ink, 4.95:1 → 8.13:1.
⚠️ A gradient is not a colour, so “it passes contrast” has to be true at both ends and in the middle. White on --gradient-brand is 5.86:1 where it starts and 2.38:1 by the teal — this page shipped exactly that mistake before the gate existed. contrast-check.mjs now measures every stop and the midpoint of each ramp against the ink named above.
§6 · Typography
Fredoka for joy. Nunito Sans for clarity.
Fredoka carries emotion; it is banned from body copy, forms, tables, money and Vault UI. Nunito Sans does everything else.
Display — Fredoka
Greetings, onboarding, Moments, empty states
Functional — Nunito Sans
Body, forms, tables, money, settings
Tabular numerals
₹18,000.00 · ₹1,05,250.50 · ₹99.99
Money uses data-numeric, so digits hold a column and a value ticking from 9 to 10 does not shift the row.
§7 · Shape
Rounded and soft, not one corner everywhere.
A deliberate scale. The button step is a squircle (D-075).
8pxcompact controls12pxfields, chips16pxsmall cards20pxgrouped panels22pxbuttons (squircle)24pxfeature cards28pxhero / Today cards999pxpills§8 · Spacing
Everything lands on a 4px grid.
Card padding 16–24, major groups 24–32, page sections 40–64.
§27 · Motion
Motion explains. It never decorates.
All of it is disabled under prefers-reduced-motion — with one deliberate exemption.
--duration-controlbutton, hover--duration-dropdowndropdown--duration-cardcard reveal--duration-sheetsheet, drawer--duration-pagepage transition--duration-celebratecelebration — the ceiling, not the target
⚠️ The spinner is exempt, keyed on data-slot="spinner". The blanket rule does not reduce a spinner, it freezes it — one iteration at 0.01ms leaves a still icon where the only sign of work used to be.
§10 · Elevation
Light lifts with shadow. Dark lifts with edges.
Switch the theme — two of these three go flat on purpose.
--shadow-card--shadow-raised--shadow-floating§12 · Iconography
One family, in a pale container.
Rounded line icons, ~1.75–2px stroke, filled only for selected or celebratory states.
⚠️ The icon takes the -ink variant, not the brand fill. Brand teal on pale teal is 2.1:1 and disappears. A destructive icon uses the semantic error, never coral.
§7 · Organic accents
Personality at the edges, never over the data.
Soft blobs, curved underlines, radial marks from the logo, pill chips. Used selectively.
⚠️ §7 also says where these may not go: never distorting a form control, never behind dense data, and never on every card. The radial mark is drawn from the logo’s capsule rays — it is not the logo, and must never be used as one.
§15 · Cards
Vary the composition, not just the colour.
Do NOT build Today as six identical KPI cards. Vary by content: list, progress, reminder, money snapshot, moment.
Standard
Neutral surface, minimal border
Active
Blue tint and border — the selected one
Progress
Pale teal — active life areas
Completed
Pale lime — achievements
Upcoming
Pale coral — warm attention
Informational
Pale blue — context
Good morning, Ayesha!
Three things deserve your attention today.
Feature / Today hero — 28px radius, richer composition⚠️ The hero uses --gradient-deep, not --gradient-brand. White is legible end to end on the first and fails at the teal end of the second.
⚠️ text-ink-3 is never legible on a card tint — 4.33:1 on bg-information in light, 4.04:1 on the tints in dark. Secondary text on a tint is text-ink-2, and the design-system check enforces it.
§14 · Form controls
A place to type, not a thing to press.
Minimum 44px. Labels stay visible, required is explicit, and an error pairs the semantic colour with a message in words.
Money is stored in paise and shown in rupees. Digits are tabular so columns line up.
That does not look like a 16-digit card number. Check the last four digits.
Optional. One or two lines is plenty.
⚠️ “Shared with Meera” is a relationship, not a financial treatment. §3.5 — relationship never decides how money is split.
A disabled control still has to be readable — §13 and §37.
A switch applies the moment it moves. Anything needing a Save button is a checkbox.
§5.4, §20 · Help
Four levels, all of them reachable.
Tooltip, popover and sheet. Every trigger works on click, tap and keyboard.
⚠️ A tooltip may only ever add to a focusable control, never be the sole route to the information — a touchscreen has no hover at all, which is most of India.
§31, §32 · Loading
Say what is happening, and how far along.
Skeleton for a content area, spinner for a short action, progress where the share is known.
Progress — the share is known
The second one is indeterminate — value={null}. It says work is happening without inventing a percentage nobody measured.
Spinner — a short action
Skeleton — a content area (§31)
⚠️ A skeleton must be roughly the shape of what replaces it. One that is the wrong size makes the page jump when the content lands, which is worse than an empty box.
Toast — a result needing no action (§32)
§32 — a toast is for something the person does not have to act on. Anything they must acknowledge is inline or a dialog, and destructive confirmation is never a toast.
Inline result (§32)
Saved to August. Your reports have been updated.
Colour is never the only signal — the tick and the sentence say it too (§22, §33).
§22 · Notifications
Semantic colour means system. Brand colour means category.
Never colour alone: every one of these carries an icon and says the same thing in words.
⚠️ security is deliberately colourless. §39 wants trust surfaces calm and undecorated — a red border around a factual sign-in notice reads as an accusation. Celebration is the only one allowed a Fredoka title, and only a short one.
§21 · Dense data
Money stays calm, and lines up.
Nunito Sans only, neutral styling, subtle row hover, numbers aligned, status chips used sparingly.
| Date | Who | Kind | Amount | Status |
|---|---|---|---|---|
| 24 Aug | Amazon | Shopping | ₹18,000.00 | Confirmed |
| 23 Aug | Meera — rent share | Settlement | ₹12,500.00 | Confirmed |
| 22 Aug | HDFC statement row 41 | Uncategorised | ₹1,05,250.50 | Draft |
| 21 Aug | LIC premium | Policy | ₹9,412.00 | Due |
The header is sticky and the amount column is data-numeric and right aligned, so ₹1,05,250.50 and ₹99.99 line up on the decimal. ⚠️ Scroll it — a sticky header inside a short card is worse than none, so it is opt-in per table, not a default.
§19 · Empty states
Say what goes here. Offer one way in.
Fredoka for the short title only. Never fake data, and never more than one action.
Nothing here yet
Money you record will show up here, grouped by month.
Nothing overdue. Nice.
You have no reminders due today.
⚠️ No button on purpose. This is good news, and an action here invents work.
§21 · Chips
Small pills, used sparingly.
A status chip earns its place by being rare. The composer's chips are real form controls, not these.
A separator above. §21 asks for restraint with rules in tables — prefer spacing to lines.
terminology.md §7 · Registry
Every word the product says, all 66 of them.
Rendered from the registry so drift is visible. The UI label is what a person reads; the internal name is never shown to them.
| termId | UI label | Internal | Level 4? |
|---|---|---|---|
allocation | Set aside for someoneMoney handed over for household, personal or savings use, with nothing expected back. | household_allocation | popover only |
base_currency | Your main currencyThe currency your totals are shown in. | base_currency | popover only |
borrower | Who did you lend the money to?The person who took the money and will return it. | borrower_person_id | popover only |
capital_contribution | Money you put into your businessIt moved from you to your business. It is not spending. | capital_contribution | popover only |
category | What was it for?Groceries, education, fuel. Separate from what the money *meant*. | category_id | popover only |
circle | A group of peopleA named set of people you can greet, invite or split with. | circle | popover only |
claim | Sent to be repaidThings you paid for at work, sent together so they come back in one payment. | claim | popover only |
classification | How protected is this?Controls what can be shared, exported, searched or read by AI. | classification | popover only |
closeness | How close are you?Private to you. It decides who to show first, and nothing about money. | closeness | popover only |
coverage_episode | One period of coverOne provider, one continuous period. A new provider starts a new one. | policy_coverage_episode | popover only |
deceased | No longer with usOccasions become remembrances. Celebratory reminders stop. | deceased_on | popover only |
dependent | Someone you look afterA child or family member whose records you manage. | dependent_of | popover only |
dish | Something they cook wellA dish, who makes it, and what it looked like the last time. | dish | popover only |
draft | Not confirmed yetImported or suggested. It affects nothing until you confirm it. | status = draft | popover only |
entitlement | What your plan includesThe features and limits available to you. | entitlement | popover only |
family_support | Given, not expected backMoney given to family with no repayment expected. Not a loan. | family_support | popover only |
financial_context | Whose money is this?Your own, a job, or your business. Kept apart so one total never hides inside another. | context_id | popover only |
funding_person | Who puts money in?The person paying into the investment, who may not own it. | funding_person_id | popover only |
fx_rate | Exchange rate usedThe rate on the day. Saved so history never changes. | rate | popover only |
gift_given | Something you gaveWhat you gave, to whom, and when. The amount comes from the money you recorded, if you recorded any. | gift | popover only |
gift_received | Something you were givenA gift someone gave you. It is never counted as income. | gift | popover only |
guardian | Who manages this?The person responsible for a dependent's records or accounts. | guardian_of | popover only |
household | Your spaceEverything in SabSahi belongs to your household. | household_id | popover only |
how_it_felt | How did it feel?One word for the day. It is not a score out of five. | felt | popover only |
income_episode | A change in what you earnEvery revision is kept with its dates. The earlier one is never written over. | income_episode | popover only |
income_source | Where the money comes fromA job, a business, rent, or anything else that pays you regularly. | income_source | popover only |
insured | Who is covered?Everyone the policy protects. May differ from the holder. | insured_person_ids | popover only |
intent | What does this money mean?Whether this was spending, a loan, a transfer, savings or a gift. | intent | popover only |
lapsed | Cover stoppedThe policy ended without renewal. The gap is kept on record. | status | popover only |
lender | Who did you borrow the money from?The person who gave you the money and expects it back. | lender_person_id | written |
memory | Something worth keepingA day that happened, with the people, the place and your own words. | memory | popover only |
memory_kind | What kind of memory?A celebration, a milestone, an achievement, a first time, or an ordinary good day. | kind | popover only |
moment | Worth your attentionSomething SabSahi thinks you want to know about today. | moment | popover only |
mute_memories | Stop showing theseHide a person, a place or a set of dates from memories. No reason needed, and you are never asked again. | resurface_mute | popover only |
nominee | Who receives the benefit?The person nominated to receive the payout. | nominee_person_ids | popover only |
organisation_person | A company or organisationAn employer, a client or your society. It can owe you money like anyone else. | kind | popover only |
outstanding | Still to be returnedWhat is left after repayments so far. | outstanding_minor | popover only |
own_expense | Everyone pays for themselvesNobody owes anybody anything, and no balances are created. | cost_mode | popover only |
owner_drawing | Money you took out of your businessIt moved from your business to you. It is not income. | owner_drawing | popover only |
payables | Money you need to payAmounts you owe, across everything. | obligation (debtor = self) | popover only |
person | PersonAnyone in your life. They do not need a SabSahi account. | person | popover only |
personal_allowance | Spending money givenGiven for personal use. SabSahi does not assume it was all spent. | personal_allowance | popover only |
plan | Something you are planningAn idea for doing something together, before it is certain. | plan | popover only |
plan_link | A link you can shareAnyone with the link sees the plan and nothing else. Never your money, never anyone's phone number. | share_grant | popover only |
plan_reply | Are they coming?Going, not going, maybe, or no reply yet. | status | popover only |
policyholder | Whose policy is it?The person the policy belongs to. | policyholder_person_id | popover only |
ported | Moved to another companyCover continued with a new provider, keeping the old history. | status | popover only |
premium_payer | Who pays for this policy?The person actually paying. Often not the policyholder. | payer_person_id | popover only |
principal | Original amount borrowedThe starting amount, before any repayments. | principal_minor | popover only |
receivables | Money others need to pay youAmounts people owe you, across everything. | obligation (creditor = self) | popover only |
reimbursable | You paid, they will return itMoney you paid for someone else and expect back. Not counted as your spending. | reimbursable_expense | popover only |
relationship | How are they related?A social relationship only. It never decides how money is treated. | relationship | popover only |
resurfaced | From around this daySabSahi noticed a memory from this time in an earlier year. | moment | popover only |
savings_allocation | Money set asideKept, not spent. Shows under "set aside", never under spending. | savings_allocation | popover only |
self_person | YouYour own record. Used when you are part of a split. | is_self | popover only |
settlement | Clearing what was owedPaying off a balance. It is not a new expense or income. | settlement_paid / _received | popover only |
share_grant | Sharing thisWho can see it, for how long, and whether they can download it. | share_grant | popover only |
sip | Monthly investmentA fixed amount invested on a schedule. | investment + schedule | popover only |
sponsored | You are paying, no return expectedTheir share is on you, and no balance is created. | split type | popover only |
stored_value | Money loaded in advanceA metro card or a wallet. Counted when you load it, unless you choose to record each journey. | account | popover only |
story | A slideshow of your memoriesPhotos put in order with words, ready to watch. You can change it whenever you like. | story | popover only |
tracking_boundary | Keep tracking this money?Choose whether SabSahi follows what happens to this money afterwards, or stops here. | tracking_boundary | popover only |
treatment | What does this money mean for this person?Personal or shared for your own portion. Reimbursable, gift or family support for someone else's. Who the person is never decides it. | movement_line.intent | popover only |
treatment_gift | Given for them, nothing expected backCounted as your spending, as a gift. It never creates a balance. | gift | popover only |
treatment_personal | Your own portionCounted as your spending. Nobody owes anything for it. | personal_expense | popover only |
treatment_shared | Your share of something splitOnly your share counts as your spending. The other shares belong to the people who owe them or were given them. | shared_expense | popover only |
Fredoka for joy. Nunito Sans for clarity.
Bright brand colours, calm foundations, varied card composition, helpful explanations and small delightful moments — a consumer-friendly personality without compromising trust. Everything here is read from the live theme, so when it drifts, this page is where you see it first.