Design system · v1.0 · 14 November 2026

Daily Learning design system

A calm daily-learning ritual for practical social moments: what to do, what to say, and how to respond with care. Warm canvas, ink text, one orange accent, and a brighter sticker language for the illustration.

Reused from syafiqiskandar30/daily-learning — its DESIGN-SYSTEM.md direction and the token layer in src/styles.css — then extended with the components, the missing primitives, a dark scheme and the contrast fixes recorded at the end.

tokens.css · 235 lines components.css · 20 components system.js · guide behaviour Shell 430px → 600px 15/18 pairs pass · 3 documented fixes

Direction

Four rules decide every screen. They come from the app's own design system, not from taste.

One lesson at a timeA screen asks one question and offers one action. Progress is a ring, not a dashboard.
Mobile is the surface430px shell, 24px page inset, 44px controls, safe-area padding. Wide screens grow to 600px, never to a desktop grid.
Warm, not playfulCanvas #fffdfa, ink #172236, one orange. Illustrations carry the colour; the interface stays quiet.
Care over judgementFeedback is gentle and specific — what to try next, never what the learner got wrong about themselves.

Colour

Semantic names only; a component asks for a role, never a hue. Contrast figures are measured against the canvas #fffdfa unless stated.

canvas#fffdfa
ink#172236 · 15.69:1
muted#6b7482 · 4.65:1
muted-strong#5f6877 · 5.54:1
primary#f04b28 · shapes only
action-strong#c9340f · 5.27:1 with white
primary-soft#fde8df
secondary#fce8e1 · ink 5.43:1
primary-ink#a83920 · text on peach
lemon#fff0c3 · gold ink 4.59:1
border#f1e6de
surface#fff2ec
course desc AA#6d7684 · 4.52:1
lesson desc AA#6d7685 · 4.52:1
success#23633c
danger#b3261e

Sticker palette

Illustration only. These four never appear on text, borders or buttons — the rule that keeps the interface calm while the art stays bright.

sticker blue#55b8e8
sticker coral#f2735f
sticker yellow#ffd34e
green#bce9c9
pink#f5c1ce

Type roles

One family — the platform rounded stack — two weights, and a size per role. Never a raw size in a component: ask for the role.

RoleTokenSizeWeight / tracking
Main heading--type-page-heading27–30px600 · −0.025em
Simple page heading--type-simple-heading28px600
Section heading--type-section-heading21px600 · −0.02em
Lesson title--type-card-title22px600
Question--type-question21–23px600 · 0.01em
Body--type-body15px500 · 1.45
Supporting--type-supporting14px500 · 1.4
Action label--type-action18px600 · 0.01em
Metadata--type-metadata12px600 · tabular
Tab label--type-tab-label11px600 · 0.015em

In use

A calm daily-learning ritual

Your courses

A friend cancels plans last minute. What do you say?

Body copy sits at 15px with 500 weight — the app never uses 400, because the rounded face thins out.

3 scenarios · 5 minutes

Space, shape & motion

The 4/8 scale, a 12px surface radius and a 24px lesson-card radius, and three durations — one of them the app's own feedback entrance.

1
2
3
4
5
6
7
8
9
TokenValueUse
--radius-surface12pxCards, fields, stats — the nested radius is the outer 12 minus its padding
--radius-card24pxLesson cards and sheets
--radius-pill999pxEvery action, every answer choice
--motion-press120msscale(.96) on press
--motion-quick150msHover, focus, colour changes
--motion-feedback-enter-duration380msThe feedback footer entering, 52px travel
Motion ruleNothing animates on a keyboard-driven highlight, and everything dies to 1ms under prefers-reduced-motion. The feedback entrance is the only staged moment in the app.

Components

Twenty components, each with the rule that decides when to use it. Live markup sits beside every one — copy it, then change the words, not the tokens.

Actions

One filled action per view. Primary carries a word, so it uses action-strong (5.27:1) rather than the orange that measures 3.66:1 with white.

<button class="action-button action-button--primary">Start lesson</button>
  • Do put the primary action full-width at the bottom of the flow.
  • Do keep 44px minimum; these are 48px.
  • Don't use two primaries in one view.
  • Don't put an icon in the app's actions — the system has none.

Brand row + profile

Wordmark and a letter avatar, Learn screen only. The avatar is a 44px target even though it draws at 40px.

  • Do show it on Learn; omit it on the lesson and overview screens.
  • Don't add a menu to the avatar — it goes to Profile.

Course card

One card in both lists, whole card clickable, no repeated "View course". Description uses the AA token, not the 3.93:1 original.

  • Do keep illustration, title, one description, one meta line.
  • Do use the text-only variant when there is no art.
  • Don't add progress bars before a course starts.

Bottom navigation

Three tabs, icon above label, active item in primary-ink. Fixed to the shell with safe-area padding.

  • Do keep three tabs; the app never grows a fourth.
  • Don't mirror it on desktop — use the wordmark link and in-page actions.
  • Do set aria-current="page" on the active tab.

Lesson progress ring

One arc per scenario: complete in primary, current in mid-peach, upcoming in pale peach. The number is tabular so it does not shift.

<div class="lesson-progress-ring" data-value="40">…</div>
  • Do expose the value in text for screen readers.
  • Do keep the ring 44px in the lesson header.
  • Don't add a second counter beside it.

Question and answer choices

Pill choices, 52px tall. Selection uses fill and border; keyboard focus is a 3px ring. Feedback states are colour and a state attribute.

A friend cancels plans last minute. What do you say?

You were looking forward to it. They sound stressed.

  • Do reset retry feedback when the learner picks a different answer.
  • Do keep the hint behind a secondary action.
  • Don't judge — frame the answer as a useful default, not a right one.

Feedback footer

Full-width, same canvas, separated by a hairline. Icon plus two lines: what happened, what to do next.

That asked before assuming

Checking in first gives them room to explain. Continue when you are ready.

One more try

Short answers can read as cold. Add what you mean.

Unable to save your progress

Check your connection, then try again.

  • Do announce it with a polite live region.
  • Do keep it above the tab bar.
  • Don't use an error red for a gentle nudge — that is the notice variant.

Profile stats

Lemon surface, gold ink, tabular values. Two per row, never more.

7day streak
12lessons done
  • Do keep the label in sentence case.
  • Don't add social metrics — the system has none.

Badge

A single word of state. Streak uses the lemon pair; nothing else competes with the primary action.

🔥 7 day streak Common sense Complete Needs review
  • Do use one per screen.
  • Don't use a badge to say something the heading already says.

Field

Visible label, 16px input text (no iOS zoom), error beside the field and phrased as a next step.

Shown on your profile only.

Add the part after the @, like name@example.com.

  • Do keep the label visible; the placeholder is an example.
  • Don't validate on every keystroke.

Alert, empty, skeleton

The three states most products forget: something happened, nothing is here yet, and something is loading.

Streak saved

7 days in a row. Tomorrow's lesson is 5 minutes.

No courses yet Courses keep your lessons and scenarios together. The first one arrives Monday.
  • Do show nothing under 400ms, a skeleton at 400ms–1s, progress past 10s.
  • Do point an empty state at the next action.

Dialog and toast

Native <dialog> for anything that needs a decision; a toast for confirmation that needs no reply.

Saved · offline copy kept

Leave this lesson?

Your answers so far are kept on this device.

  • Do label the confirm with the consequence ("Leave"), never "Yes".
  • Don't stack toasts; one at a time.

Keyboard and code

Mono only where the characters matter: keys, identifiers, code. Never for body copy.

Press J then K to move --radius-surface: 12px
  • Do set numbers in tabular-nums wherever they change.

Patterns

The assembled lesson screen: header ring, question, choices, feedback footer, tab bar — the whole flow in one 430px shell.

Scenario 2 of 3

A friend cancels plans last minute. What do you say?

You were looking forward to it. They sound stressed.

That asked before assuming

Continue when you are ready.

Findings from the audit

Three contrast failures in the reused tokens, each measured, each with a fix that keeps the app's look. The originals are untouched so existing screens do not shift; the new tokens sit beside them.

1 · White on the primary orange — 3.66:1 #ffffff on #f04b28. Already recorded in the app's own DESIGN-SYSTEM.md. Fix: filled actions use --color-action-strong #c9340f → 5.27:1. The orange stays for rings, marks and illustration.
2 · Course description — 3.93:1 #77808f on the canvas, used for 13px card copy. Fix: --color-course-description-aa #6d7684 → 4.52:1.
3 · Lesson description — 3.39:1 #828b99 on the canvas, used for 14px scenario copy. Fix: --color-lesson-description-aa #6d7685 → 4.52:1.
Everything else passes 15 of 18 measured pairs clear 4.5:1, including both dark-scheme pairs. Ink on canvas is 15.69:1; muted is 4.65:1; gold on lemon 4.59:1.

Usage rules

What a reviewer checks before anything ships.

ColourOne accent, one meaning. Never tint text; ask for a semantic token. Illustration colours never touch UI chrome.
TypeRole tokens only. 500 for interface, 600 for emphasis. No 400 — it thins out on the rounded face. 16px inputs.
Space4/8 scale, 24px page inset, gap between groups at least twice the gap inside them.
Targets44px minimum. The visible shape may be smaller; the target may not.
ActionsOne primary per view, pill, 48px, no icon, sentence case, verb first.
FocusAlways visible: 3px ring in --color-focus, 3px offset. Never outline: none.
Motion120/150/380ms, eased out. None on keyboard-driven highlights. Reduced motion respected.
CopyPlain, concrete, non-judgemental. Name the next step. No "oops", no exclamation marks.
StatesFour, always: default, loading, empty, error. A component is not finished until all four exist.