Design System
Design System Documentation
Project: {{PROJECT_NAME}}Drop — Fintech Payment App
Version: {{VERSION}}0.1.0
Date: {{DATE}}2026-02-23
Author: {{AUTHOR}}John (AI Director, ALAI)
Status: Draft | In Review | Approved
Reviewers: {{REVIEWERS}}Alem Bašić (CEO)
Document History
| Version |
Date |
Author |
Changes |
| 0.1 |
{{DATE}}2026-02-23 |
{{AUTHOR}}John |
Initial draft from source code + brand guide analysis |
1. Design Principles
| Principle |
Description |
| Clarity first |
Every UI element must communicate its purpose without explanationexplanation. Amounts are large and legible. Actions are labeled. |
ConsistentScandinavian over cleverminimal |
PreferGenerous familiarwhitespace, patternsno overvisual novelnoise. interactionsForest green + white — nothing extra. |
| Trust through transparency |
Fees visible before every transaction. No hidden costs. PSD2 disclosures shown explicitly. |
| Mobile-first |
The app is a mobile PWA. max-w-sm containers, pb-24 for BottomNav clearance, touch targets ≥ 44×44px. |
| Accessible by default |
WCAG AA compliance is a baseline,baseline requirement, not aan featureoptional enhancement. |
Density-awareLight mode only (Phase 1) |
SupportBackground comfortableis andalways compactoff-white density(#FAFCF8) modes | or
white. Dark TODO:mode Addis principle | Phase {{DESCRIPTION}}2. |
2. Color System
2.1 Primitive Palette (Raw Values)
/* Brand primitives — do NOT use directlydefined in componentsbrand/colors.css and globals.css */
/* Green scale */
--color-brand-50:green-primary: {{#F0F9FF}};#0B6E35; /* Forest Green — primary brand */
--color-brand-100:green-dark: {{#E0F2FE}};#095C2C; /* Hover/pressed state */
--color-brand-500:green-light: {{#0EA5E9}};#E8F5E9; /* Light green tint, badges */
/* Gold scale */
--color-brand-600:gold-primary: {{#0284C7}};#D4A017; /* Gold accent, logo arrow, premium elements */
--color-brand-900:gold-light: {{#0C4A6E}};#FFF8E1; /* Gold tint */
/* Neutral scale */
--color-neutral-0: #FFFFFF; /* White — cards, elevated surfaces */
--color-neutral-50: {{#F8FAFC}};#FAFCF8; /* Off-white — page background */
--color-neutral-100: {{#F1F5F9}};#F9FAFB; /* Input field backgrounds */
--color-neutral-200: #F3F4F6; /* Section backgrounds, shadcn secondary */
--color-neutral-300: #E5E7EB; /* Input borders, card borders, dividers */
--color-neutral-400: {{#94A3B8}};#D1D5DB; /* Horizontal rule dividers */
--color-neutral-500: #9CA3AF; /* Muted text, inactive nav items */
--color-neutral-600: #6B7280; /* Secondary text, placeholders */
--color-neutral-700: {{#334155}};#374151; /* Dark text on light backgrounds */
--color-neutral-900: {{#0F172A}};#1A1A1A; /* Near-black — headings, primary text */
--color-neutral-1000: #000000;
/* Semantic */
--color-success: #10B981; /* Emerald — success, positive amounts */
--color-error: #EF4444; /* Red — error states, negative amounts */
--color-warning: #D97706; /* Warning — pending transactions */
--color-info: #2563EB; /* Info — PSD2 banners */
2.2 Semantic Tokens (Light Mode)Mode — from globals.css :root)
:root {
/* Backgroundshadcn/ui CSS variable tokens */
--background: #FAFCF8;
--foreground: #1A1A1A;
--primary: #0B6E35;
--primary-foreground: #FFFFFF;
--secondary: #F3F4F6;
--secondary-foreground: #1A1A1A;
--accent: #F3F4F6;
--accent-foreground: #1A1A1A;
--muted: #F3F4F6;
--muted-foreground: #6B7280;
--destructive: #EF4444;
--border: #E5E7EB;
--input: #E5E7EB;
--ring: #0B6E35;
--radius: 0.75rem;
/* Drop custom semantic tokens */
--color-bg-drop-primary: var(--color-neutral-0);#0B6E35;
--color-bg-drop-secondary: var(--color-neutral-50);#D4A017;
--color-bg-elevated:drop-accent: var(--color-neutral-0);
/* Text */#10B981;
--color-text-primary:drop-dark: var(--color-neutral-900);#1A1A1A;
--color-text-secondary:drop-light: var(--color-neutral-700);#FAFCF8;
--color-text-disabled: var(--color-neutral-400);
--color-text-inverse: var(--color-neutral-0);
/* Interactive */
--color-interactive-primary: var(--color-brand-500);
--color-interactive-primary-hover: var(--color-brand-600);
--color-interactive-primary-active: var(--color-brand-700);
/* Semantic */
--color-success: {{#10B981}};
--color-warning: {{#F59E0B}};
--color-drop-error: {{#EF4444}#EF4444;
};
--color-info: {{#3B82F6}};
2.3 Semantic Tokens (Dark Mode)
/* Phase 2 — Dark mode TBD */
[data-theme="dark"] {
--color-bg-primary:/* var(--color-neutral-900);TBD --color-bg-secondary:— var(--color-neutral-800);requires --color-text-primary:design var(--color-neutral-50);review */
/* TODO:Drop Completeis darklight-mode-first. Dark mode tokenplanned mappingfor Phase 2. */
}
2.4 Brand Gradient
.bg-gradient-brand {
background: linear-gradient(135deg, #0B6E35 0%, #D4A017 100%);
}
2.5 Contrast Ratios (WCAG)
| Pair |
Text Color |
Background |
Ratio |
WCAG AA (4.5:1) |
WCAG AAA (7:1) |
TextPrimary primarytext on page bg primary |
{{X:1}}#1A1A1A |
{{Pass/Fail}}#FAFCF8 |
{{Pass/Fail}}~18.6:1 |
Pass |
Pass |
TextSecondary secondarytext on page bg primary |
{{X:1}}#6B7280 |
{{Pass/Fail}}#FAFCF8 |
{{Pass/Fail}}~4.7:1 |
Pass |
Fail |
InteractivePrimary onbutton bg primarytext |
{{X:1}}#FFFFFF |
{{Pass/Fail}}#0B6E35 |
{{Pass/Fail}}~5.0:1 |
Pass |
Fail |
WhiteSuccess on brand-500text |
{{X:1}}#10B981 |
{{Pass/Fail}}#FFFFFF |
{{Pass/Fail}}~3.0:1 |
Fail (large text only) |
Fail |
| Error text |
#EF4444 |
#FFFFFF |
~3.9:1 |
Fail (large text only) |
Fail |
| Muted text |
#9CA3AF |
#FFFFFF |
~2.9:1 |
Fail |
Fail |
TODO:Action items: RunSuccess contrast(#10B981), checkserror (#EF4444), and muted (#9CA3AF) colors fail WCAG AA for small text. These are used for semantic indicators (amount colors, hints) — always paired with a non-color indicator. Full audit in {{axe | Colour Contrast Analyser}}accessibility-audit.md and fill table..
3. Typography
3.1 Font Families
| Token |
Value |
Usage |
--font-headingfraunces |
{{Inter,Fraunces, sans-serif}}Georgia, "Times New Roman", serif |
H1–H4,Display/headings, displaylogo wordmark, brand text |
--font-bodydm-sans |
{{Inter,"DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif}}serif |
Body copy,text, UI labels, UIinputs (default body font) |
--font-geist-mono |
{{"Geist Mono", "JetBrains Mono,Mono", monospace}}"Fira Code", "Courier New", monospace |
Code, technicalmonospace dataelements |
Why these fonts:
- Fraunces — Variable serif with "wonky" optical size. Gives character and warmth. Differentiates Drop from every other fintech app (which all use sans-serif). Signals: "we are different, we are human."
- DM Sans — Clean geometric sans-serif. Readable at all screen sizes. Friendly without being playful. Good number legibility (critical for fintech).
3.2 Type Scale
| Token |
Size |
Weight |
Line Height |
Letter Spacing |
Usage |
--text-displayDisplay |
48px56px |
700 (Fraunces) |
1.1 |
-0.02em |
Hero headingsheadlines (landing page) |
--text-h1H1 |
36px40px |
700600 (Fraunces) |
1.2 |
-0.01em |
Page titles |
--text-h2H2 |
28px32px |
600 | 1.25 |
-0.01em |
Section headings |
--text-h3 |
22px |
600(Fraunces) |
1.3 |
0-0.005em |
SubsectionsSection headers |
--text-h4H3 |
18px24px |
500 (Fraunces) |
1.3 |
normal |
Sub-sections |
| H4 |
20px |
600 (DM Sans) |
1.4 |
0normal |
Card headingstitles |
--text-body-lgBody Large |
18px |
400 (DM Sans) |
1.67 |
0normal |
Lead paragraphs |
--text-bodyBody |
16px |
400 (DM Sans) |
1.6 |
0normal |
Default body copytext |
--text-body-smBody Small |
14px |
400 (DM Sans) |
1.5 |
0normal |
Secondary text,content, captions |
--text-labelLabel |
14px |
500 (DM Sans) |
1.4 |
0.01em |
Form labels, UI labels |
--text-captionCaption |
12px |
400500 (DM Sans) |
1.4 |
0.02em |
Timestamps, meta | meta,
--text-code |
14px |
400 |
1.6 |
0 |
Inline codehints |
TODO:
3.3 VerifyIn-App scaleTailwind againstTypography FigmaPatterns
—
update
values
ifContext |
mismatched.Tailwind Class |
Example |
| Logo wordmark |
font-[family-name:var(--font-fraunces)] text-3xl font-bold |
"drop" on login |
| Page heading |
text-xl font-semibold text-[#1A1A1A] |
Dashboard greeting |
| Section heading |
text-lg font-semibold text-[#1A1A1A] |
"Siste transaksjoner" |
| Form label |
text-sm font-medium text-[#1A1A1A] |
"E-post" |
| Body text |
Inherited DM Sans |
General content |
| Muted text |
text-sm text-[#6B7280] |
Descriptions, timestamps |
| Small text |
text-xs text-[#9CA3AF] |
Footers, hints |
| Balance (large) |
text-2xl font-bold text-[#1A1A1A] |
Balance display |
| Amount (list) |
text-sm font-semibold + color |
Transaction amounts |
4. Spacing & Layout
4.1 Spacing Scale (4px Base Unit)Unit — Tailwind defaults)
TokenTailwind |
Value |
Usage |
--space-0 |
0px |
|
--space-p-1 |
4px |
Micro gaps, icon paddinggaps |
--space-p-2 |
8px |
Tight inline spacing |
--space-p-3 |
12px |
Compact form elements |
--space-p-4 |
16px |
Default content spacing |
--space-5 |
20px |
Card padding (compact) |
--space-p-6 |
24px |
Card padding, section padding |
--space-p-8 |
32px |
Large section gaps |
--space-10px-6 |
40px24px |
FeaturePage sectionhorizontal padding (standard) |
--space-12pb-24 |
48px96px |
Section separation |
--space-16 |
64px |
Page-levelBottom padding |
(BottomNav
--space-20 |
80px |
Hero sectionsclearance) |
4.2 GridPage SystemStructure Patterns
/* Standard authenticated Property | page Value | */
min-h-screen bg-[#FAFCF8] /* Off-white Columnbackground count | */
12 | px-6
pb-24 pt-6 Column/* gutter | Padding: horizontal 24px, bottom 96px (nav), top 24px */
/* Login / onboarding (mobile:centered) 16px) | */
min-h-screen bg-[#EEEEEE] Container/* Slightly darker bg for auth pages */
max-width | w-sm 1280px | mx-auto
/* 384px Containermax sidewidth, padding | centered 24px*/
/* Card pattern (mobile:standard) 16px) | */
rounded-2xl bg-white
p-6 shadow-sm
/* Card pattern (compact) */
rounded-xl bg-white p-4 shadow-sm
4.3 Responsive Breakpoints (Tailwind defaults)
| Name |
Min Width |
Target DevicesUsage |
xsDefault |
0px |
SmallMobile phones— primary design target |
sm |
480px640px |
LargeLarger phones |
md |
768px |
Tablets — landing page 2-column grid |
lg |
1024px |
SmallDesktop laptops | —
landing page xl | 3-column 1280px |
Desktops |
2xl |
1536px |
Large displaysgrid |
Note: The app is mobile-first. max-w-sm containers keep content readable on wider screens.
4.4 Bottom Nav
The BottomNav is fixed bottom-0 left-0 right-0 with h-16 border-t bg-white. All pages that include BottomNav must add pb-24 to their content container.
5. Component Library
5.1 Custom Drop Components
| Component |
File |
Status |
Description |
| BottomNav |
components/bottom-nav.tsx |
Done |
Fixed 5-tab navigation |
| DropLogo |
components/drop-logo.tsx |
Done |
Forward-D SVG mark |
| DropWordmark |
components/drop-logo.tsx |
Done |
"drop" in Fraunces |
| DropLogoFull |
components/drop-logo.tsx |
Done |
Logo mark + wordmark |
| DropAppIcon |
components/drop-logo.tsx |
Done |
App icon — rounded green square |
| CookieConsent |
components/cookie-consent.tsx |
Done |
GDPR consent banner + modal |
| PrePaymentDisclosure |
components/pre-payment-disclosure.tsx |
Done |
PSD2 pre-payment modal |
| PWARegister |
components/pwa-register.tsx |
Done |
Service Worker registration |
| drop-icons |
components/drop-icons.tsx |
Done |
9 custom fintech icons |
5.12 shadcn/ui Primitive Components (Atoms)
| Component |
StatusFile |
VariantsRadix Primitive |
StorybookStatus |
| Alert |
ui/alert.tsx |
— (div-based) |
Done |
| Avatar |
ui/avatar.tsx |
@radix-ui/react-avatar |
Done |
| Badge |
ui/badge.tsx |
— (cva variants) |
Done |
| Button |
`{{ui/button.tsx |
@radix-ui/react-slot |
Done |
WIPCard |
Planned}}`ui/card.tsx |
— (div-based) |
Done |
| Dialog |
ui/dialog.tsx |
@radix-ui/react-dialog |
Done |
| Input |
`{{ui/input.tsx |
— (input element) |
Done |
WIPScrollArea |
Planned}}`ui/scroll-area.tsx |
@radix-ui/react-scroll-area |
Done |
| Select |
`{{Doneui/select.tsx |
WIP@radix-ui/react-select |
Planned}}`Done |
CheckboxSeparator |
`{{Doneui/separator.tsx |
WIP@radix-ui/react-separator |
Planned}}`Done |
RadioSheet |
`{{Doneui/sheet.tsx |
WIP@radix-ui/react-dialog |
Planned}}`Done |
Toggle/SwitchSkeleton |
`{{Doneui/skeleton.tsx |
WIP— (pulse animation) |
Planned}}`Done |
Textarea |
`{{Done |
WIP |
Planned}}` |
Badge |
`{{Done |
WIP |
Planned}}` |
Avatar |
`{{Done |
WIP |
Planned}}` |
Tooltip |
`{{Done |
WIP |
Planned}}` |
Spinner |
`{{Done |
WIP |
Planned}}` |
Divider |
`{{Done |
WIP |
Planned}}` |
5.2 Composite Components (Molecules)
Component |
Status |
Storybook |
|---|
CardSonner |
{{Status}}ui/sonner.tsx |
{{URL}} | sonner
toast
Modal / Dialoglibrary |
{{Status}} |
{{URL}} |
Dropdown Menu |
{{Status}} |
{{URL}} |
Table |
{{Status}} |
{{URL}} |
Pagination |
{{Status}} |
{{URL}} |
Toast / Notification |
{{Status}} |
{{URL}} |
Form Field (label + input + error) |
{{Status}} |
{{URL}} |
Search Bar |
{{Status}} |
{{URL}} |
Breadcrumb |
{{Status}} |
{{URL}}Done |
| Tabs |
{{Status}}ui/tabs.tsx |
{{URL}} |
Accordion@radix-ui/react-tabs |
{{Status}} |
{{URL}} |
Date Picker |
{{Status}} |
{{URL}}Done |
5.3 Layout Components
| Component |
Description |
ContainerPage wrapper |
Max-widthmin-h-screen wrapperbg-[#FAFCF8] withpx-6 responsivepb-24 paddingpt-6 |
StackAuth wrapper |
Verticalmin-h-screen flex stackitems-center withjustify-center configurable gapbg-[#EEEEEE] |
InlineCard |
Horizontalrounded-2xl flexbg-white rowp-6 with configurable gap/alignmentshadow-sm |
GridScroll area |
CSSScrollArea gridfrom layoutshadcn/ui wrapper | for
transaction
PageLayout |
Full-page layout with sidebar/header/main/footer slots |
Section |
Content section with standard vertical rhythmlists |
6. Iconography Guidelines
| Item |
Standard |
LibraryPrimary library |
`{{Lucide Reactlucide-react |
| Delivery |
Inline SVG via React component (no sprite, no icon font) |
| Sizes |
16pxh-4 w-4 (sm)inline/small), 20pxh-5 w-5 (md, default)nav/buttons), 24pxh-6 w-6 (lg),feature 32px (xl)icons) |
| Stroke width |
1.5px at 24px,24px scaled(Lucide proportionallydefault) |
| Color |
Inherits currentColor — neverinherits hardcoded | from
parent text Interactive icons |
Must have visible focus ring + 44×44px touch targetcolor |
| Custom icons |
components/drop-icons.tsx — 9 domain-specific icons |
| Social auth |
Inline SVG optimized— viaBankID SVGO,(green placedrounded rect, "ID"), Vipps (orange circle, "V") |
Custom Drop Icons:
| Export |
Description |
IconSendMoney |
Arrow going up-right from horizontal line |
IconQrScan |
QR code frame with scan corners |
IconVirtualCard |
Credit card outline |
IconShield |
Shield with checkmark |
IconFastTransfer |
Lightning bolt |
IconCorridors |
Globe with meridians |
IconWallet |
Wallet outline (unused — no wallet in pass-through model) |
src/components/icons/IconHistory |
Clock with arrow |
IconTopUp |
Plus inside circle (unused — no top-up in pass-through model) |
Accessibility rule: Icons conveying meaning must have aria-label. Decorative icons: aria-hidden="true".
7. Motion & Animation Standards
7.1 Duration Tokens
7.2 Easing Tokens
| Token |
Value |
Usage |
--ease-default |
cubic-bezier(0.4, 0, 0.2, 1) |
General UI transitions |
--ease-enter |
cubic-bezier(0, 0, 0.2, 1) |
Elements entering |
--ease-exit |
cubic-bezier(0.4, 0, 1, 1) |
Elements leaving |
--ease-spring |
cubic-bezier(0.34, 1.56, 0.64, 1) |
Playful, emphasis |
7.3 Reduced Motion
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
Rule: Every animated component MUST respect prefers-reduced-motion.
8. Accessibility Requirements Per Component
9.1 CSS Custom Properties (Source of Truth)
/* tokens/colors.src/app/globals.css */
:root {
--color-brand-500:drop-primary: #0EA5E9;#0B6E35;
--color-drop-secondary: #D4A017;
--color-drop-accent: #10B981;
--color-drop-dark: #1A1A1A;
--color-drop-light: #FAFCF8;
--color-drop-error: #EF4444;
/* .shadcn/ui tokens... */
}
9.2 Tailwind Config Extension
// tailwind.config.ts
export default {
theme: {
extend: {
colors: {
brand:drop: {
500:primary: '#0B6E35',
secondary: '#D4A017',
accent: '#10B981',
dark: '#1A1A1A',
light: '#FAFCF8',
error: '#EF4444',
}
},
fontFamily: {
fraunces: ['var(--color-brand-500)font-fraunces)', //'Georgia', ...'serif'],
}sans: ['var(--font-dm-sans)', 'system-ui', 'sans-serif'],
mono: ['var(--font-geist-mono)', 'monospace'],
}
}
}
}
9.3 JavaScript/TypeScript Constants (for charting/canvas)
// tokens/colors.ts — forFor use in chartingnon-CSS libraries,contexts canvas,(future etc.charts, canvas)
export const colorsdropColors = {
brand500:primary: '#0EA5E9'#0B6E35',
//secondary: ...'#D4A017',
accent: '#10B981',
dark: '#1A1A1A',
light: '#FAFCF8',
error: '#EF4444',
} as const;
Token update process: Figma → update Style Dictionaryglobals.css exportCSS variables → CSS/JS/update Tailwind filesconfig → PR review.
10. Component DocumentationCode StandardPatterns
Each component story must include:
Default story — basic render with minimal props
All Variants story — every visual variant displayed
All States story — hover, focus, disabled, error, loading
Responsive story — behavior at each breakpoint
Accessibility story — keyboard navigation, screen reader notes
Component JSDoc minimum:
Button
/**
* Button component for primary user actions.
*
* @example
* <Button variant=className="primary"h-12 onClick={handleSubmit}w-full rounded-xl bg-[#0B6E35] text-sm font-semibold text-white shadow-sm hover:bg-[#095C2C]">Save
ChangesSend penger
</Button>
*
<button className="flex h-12 flex-1 items-center justify-center gap-2 rounded-xl border border-[#E5E7EB] bg-white text-sm font-medium text-[#1A1A1A] hover:bg-[#F9FAFB]">
Avbryt
</button>
PropsText table:
Input Everywith propLeft mustIcon
have:
<div type,className="relative">
default,<Mail requiredclassName="absolute flag,left-3 description.top-1/2 h-4 w-4 -translate-y-1/2 text-[#6B7280]" />
<input
className="h-11 w-full rounded-lg border border-[#E5E7EB] bg-[#F9FAFB] pl-10 pr-3 text-sm outline-none focus:border-[#0B6E35] focus:ring-1 focus:ring-[#0B6E35]"
type="email"
placeholder="[email protected]"
/>
</div>
Error Message
<p className="rounded-md bg-[#EF4444]/10 p-2 text-sm text-[#EF4444]">
{error}
</p>
Primary Badge
<span className="rounded-full bg-[#0B6E35]/10 px-2 py-0.5 text-xs font-medium text-[#0B6E35]">
Primær konto
</span>
Avatar (Initials)
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-[#0B6E35] text-sm font-bold text-white">
{initials}
</div>
Transaction Amount (Positive / Negative)
// Positive (received)
<span className="text-sm font-semibold text-[#10B981]">+1 250,00 NOK</span>
// Negative (sent)
<span className="text-sm font-semibold text-[#EF4444]">-500,00 NOK</span>
Approval
| Role |
Name |
Date |
Signature |
| Author |
John (AI Director) |
2026-02-23 |
|
| Lead Designer |
|
|
|
| Frontend Lead |
|
|
|
| Accessibility Reviewer |
|
|
|