⌂ Kelas Sekejap Knowledge BaseBrandDesign SystemDocsLogo
Docs · Type, spacing, shape, states, contrast, motion, voice

Foundations

Kelas Sekejap — Foundations

The designer-facing guidance behind Say it with confidence v7: the rules for type, space, shape, states, accessibility, motion, iconography, and voice. Tokens named here live in src/styles/tokens.css (and typed in src/tokens/tokens.ts).

Everything carries the four brand rules: named colour blocks · type-first · keep the product calm · organic shapes.


02 · Typography

Three roles do all the work: a heavy Display for loud moments, a friendly Body for reading, a Mono for labels/tokens/eyebrows. One Display element per view — the same discipline as "one loud primary."

Role Family Usage
Display Archivo Black UPPERCASE, stacked. Tracking −0.065 to −0.085em, leading 0.80–0.86
Body Plus Jakarta Sans Sentence case. Runs heavy — 700 floor, 900 for buttons
Mono / Label JetBrains Mono UPPERCASE eyebrows, tags, tokens. Tracking +6–10%

Type scale (token · desktop / mobile · line-height):

Token Size Line-height
--ks-text-display-xl 64 / 40px .95
--ks-text-display-l 40 / 32px 1.0
--ks-text-heading-l 28px 1.1
--ks-text-heading-m 20px 1.25
--ks-text-body-l 18px 1.5
--ks-text-body-m 16px 1.5
--ks-text-body-s 14px 1.45
--ks-text-label 12px — (UPPER, mono)

Rules — One Display element per screen. Never stretch, condense, or outline the Display face. Body line length ≤ 66 characters. Uppercase is Display & Mono only — never uppercase body copy. Minimum body size on mobile is 16px.


03 · Spacing & Layout

A 4px base keeps rhythm predictable. Everything — padding, gaps, insets — snaps to --ks-space-1…10 = 4 · 8 · 12 · 16 · 20 · 24 · 32 · 40 · 48 · 64.

Breakpoints (mobile-first): --ks-bp-sm 480 · --ks-bp-md 768 · --ks-bp-lg 1024 · --ks-bp-xl 1280.

Containers & grid

Use Value
Reading width (--ks-container-reading) 720px max
App / page max (--ks-container-app) 1120px
Grid — mobile 4 col · 16px gutter
Grid — desktop 12 col · 24px gutter
Screen edge margin (mobile) 16–20px

The primary surface is a phone — design at 375–430px first, respect safe-area insets (notch, home indicator), and keep the primary CTA within thumb reach at the bottom.


04 · Shape & Elevation

The hard, blur-free offset shadow paired with a 2px Ink outline is the single most recognisable thing about the system.

Radii: --ks-radius-sm 14 (inputs) · md 20 (chips) · lg 28 (cards) · xl 36 (hero) · pill 999.

Elevation (always Ink, never grey, never blurred):

Token Value Use
--ks-shadow-sm 4px 4px 0 small chips
--ks-shadow-button 5px 5px 0 resting buttons, pills, tags
--ks-shadow-card 8px 8px 0 cards, modals
--ks-shadow-hover 7px 7px 0 hover lift
--ks-shadow-soft 0 22px 60px rgba(16,16,20,.14) marketing / hero docs only — never interactive UI

The press rule is mandatory: every shadowed element carries the 2px Ink border. On press the element "sits down" — translate(5px, 5px) and drop the shadow to 0. That physical click is the brand's core interaction feel.

Organic shapes — the system explicitly calls for fluid shape language: blobs, swashes, sticker cut-outs (see DoodleAccent). Radii are the floor, not the whole story — reach for organic forms in marketing and hero moments.


06 · Component States

Define the full set for every interactive component — not just rest and disabled.

State Signal Notes
rest Fill + 2px Ink + shadow-button Default
hover Lift −2/−2, shadow-hover Pointer only
focus-visible Blue ring offset by Haze (--ks-focus-ring) Keyboard — never remove
active / pressed Sink +5/+5, shadow 0 The signature press feel
loading Label → spinner, size held Block re-tap; announce via aria-live
selected Ink fill or check badge Toggles, nav, choices
error / invalid Flame border + message + icon Never colour alone
disabled #DEDBD3 fill, 45% Ink text, 18% border, no shadow not-allowed cursor

Keep the product calm — the loud, high-contrast treatment belongs to marketing and hero moments. Inside the learning UI, dial states down: readable and quiet so practice stays low-pressure. Same tokens, gentler application.


07 · Accessibility

Measured WCAG 2.1 contrast for the real palette. Default to Ink text; only Blue is safe for white body text. (AA body needs 4.5; large/bold ≥24px needs 3.0.)

Ink text on… (recommended default)

Surface Ratio
Paper 19.0 AAA
Haze 15.9 AAA
Sunflower 11.8 AAA
Pulse 9.4 AAA
Spark 7.8 AAA
Moss 5.9 AA
Flame 4.9 AA
Blue 3.5 Large text only

White text on…

Surface Ratio
Blue 5.4 AA
Flame 3.9 Large only
Moss 3.2 Large only
Spark 2.4 ✗ Fail
Pulse 2.0 ✗ Fail
Sunflower 1.6 ✗ Fail
Haze 1.2 ✗ Fail

Do — Ink on any accent. White only on Blue (body), or Flame/Moss at large sizes. Give every state a non-colour signal (icon, label, shape). Focus ring always visible. Touch targets ≥ 44×44px. Support 200% zoom & dynamic type.

Decision A1 (2026-07): Flame and Moss surfaces carry Ink text system-wide (4.88 / 5.94 AA). White icons on them are fine (non-text, ≥3:1), and display-size white (≥24px, e.g. PosterCard headlines) passes AA-large for marketing surfaces.

Decision B2 (2026-07): the v7 board's "Soft" layer — Lavender #CAA7FF, Mint #B8DDCE, Cream #FFF7ED — is formally retired. In-product calm is expressed with Haze + Paper only; the board should no longer be read as spec for those three colours.

Don't — White text on Sunflower/Pulse/Spark/Haze. Colour as the only meaning (a streak or progress state needs a label). Removing focus outlines. Sub-44px tap areas. Body text below 16px on mobile.

Audio-specific (this app is voice-first) — transcripts/captions for every listening task; announce recording start/stop and results with aria-live; a visible non-audio state for "tap to speak"; never make audio the only way to complete a task; respect prefers-reduced-motion on the mic pulse.


08 · Motion

Physical (matching the tactile shadow) and quick. Purposeful, never decorative. Every looping effect has a reduced-motion fallback.

Token Value Use
--ks-dur-press 80ms Press / snap — no ease
--ks-dur-fast 150ms Hover
--ks-dur-base 200ms Most transitions
--ks-dur-slow 300ms Entrances, toasts
--ks-dur-reward 500ms Celebrations

Easing --ks-ease = cubic-bezier(0.2, 0.8, 0.2, 1). Signatures: press snaps down/up (no ease); toast slides up from bottom and settles; reward pops (overshoot) then settles; card hover lifts −2/−2 into shadow-hover. Recording pulse = ksPulse on the Flame mic while listening.

Reduced motion — under prefers-reduced-motion: reduce, replace movement and loops with a simple opacity change and hold the mic on a static "listening" state.


10 · Iconography

Lucide React only — not custom icons. Lucide's rounded caps and even weight already match the brand.

Rule Value
Library lucide-react
Default stroke 2 (reserve 2.4–3 for rare emphasis)
Sizes 16 · 20 · 24 (match text scale)
Colour Ink single-tone; accent only for reward moments
Alignment Optical-centre with adjacent text baseline

Live set includes mic, house, chart-column, user, check, sparkles, arrow-right, circle-alert. Don't swap in another icon set, mix stroke weights in one view, recolour with low-contrast accents, or draw custom one-offs when a Lucide glyph exists.

The only sanctioned non-Lucide expressive graphic is the branded Expression set — see the emoji-rule exception in the README.


11 · Voice & Tone

The brand speaks in a distinct voice — "No scores — only progress," the Say it / Say more / Say better ladder.

Core terminology

Use Not Why
Practice / session Test / exam Lowers pressure
Streak Score / rank Momentum, not judgement
Say it · Say more · Say better Level 1/2/3 The brand's speaking ladder
Getting clearer / Building Fail / Wrong Growth framing (see BandBadge)
SPM · Form 5 · Paper 3 (localise, keep proper nouns) Malaysian exam context

Do — "We couldn't hear you — try again." · "Your streak is safe for today." · "Practice today to start your streak." Encourage after every attempt.

Don't — "Error: invalid input." · "You scored 3/10." · "Incorrect." Cold, clinical, or score-first language.

Bilingual — natural Malay-English code-switching is welcome in warmth ("Sekejap," playful lines), but keep instructions in clear English.


Sections numbered to match the source foundations pack (02–04, 06–08, 10–11). Colour (01), the component set (05), and the app kit (09) live in the README and src/.