Skip to main content

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

orwhite.DarkPhase
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 transparencyFees visible before every transaction. No hidden costs. PSD2 disclosures shown explicitly.
Mobile-firstThe 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
TODO:mode Addis principle {{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)

PairText ColorBackground 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:1PassPass
TextSecondary secondarytext on page bg primary {{X:1}}#6B7280 {{Pass/Fail}}#FAFCF8 {{Pass/Fail}}~4.7:1PassFail
InteractivePrimary onbutton bg primarytext {{X:1}}#FFFFFF {{Pass/Fail}}#0B6E35 {{Pass/Fail}}~5.0:1PassFail
WhiteSuccess on brand-500text {{X:1}}#10B981 {{Pass/Fail}}#FFFFFF {{Pass/Fail}}~3.0:1Fail (large text only)Fail
Error text#EF4444#FFFFFF~3.9:1Fail (large text only)Fail
Muted text#9CA3AF#FFFFFF~2.9:1FailFail

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

meta,
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.01emSection headings
--text-h322px600(Fraunces) 1.3 0-0.005em SubsectionsSection headers
--text-h4H3 18px24px500 (Fraunces)1.3normalSub-sections
H420px 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
--text-code14px4001.60Inline codehints

TODO:

3.3 VerifyIn-App scaleTailwind againstTypography FigmaPatterns

updatevaluesifmismatched.

Context Tailwind ClassExample
Logo wordmarkfont-[family-name:var(--font-fraunces)] text-3xl font-bold"drop" on login
Page headingtext-xl font-semibold text-[#1A1A1A]Dashboard greeting
Section headingtext-lg font-semibold text-[#1A1A1A]"Siste transaksjoner"
Form labeltext-sm font-medium text-[#1A1A1A]"E-post"
Body textInherited DM SansGeneral content
Muted texttext-sm text-[#6B7280]Descriptions, timestamps
Small texttext-xs text-[#9CA3AF]Footers, hints
Balance (large)text-2xl font-bold text-[#1A1A1A]Balance display
Amount (list)text-sm font-semibold + colorTransaction amounts

4. Spacing & Layout

4.1 Spacing Scale (4px Base Unit)Unit — Tailwind defaults)

(BottomNav
TokenTailwind Value Usage
--space-00px
--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-520pxCard 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-1664pxPage-levelBottom padding
--space-2080pxHero sectionsclearance)

4.2 GridPage SystemStructure Patterns

/* 
Standard authenticated page */ min-h-screen bg-[#FAFCF8] /* Off-white */ px-6 pb-24 pt-6 Padding: */ min-h-screen bg-[#EEEEEE] w-sm mx-auto /* 384px centered */ rounded-2xl bg-white
PropertyValue
Columnbackground count12
Column/* gutterhorizontal 24px, bottom 96px (nav), top 24px */ /* Login / onboarding (mobile:centered) 16px)
Container/* Slightly darker bg for auth pages */ max-width1280px
Containermax sidewidth, padding24px*/ /* Card pattern (mobile:standard) 16px)
p-6 shadow-sm /* Card pattern (compact) */ rounded-xl bg-white p-4 shadow-sm

4.3 Responsive Breakpoints (Tailwind defaults)

landingpage3-column
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
xl 1280pxDesktops
2xl1536pxLarge 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

ComponentFileStatusDescription
BottomNavcomponents/bottom-nav.tsxDoneFixed 5-tab navigation
DropLogocomponents/drop-logo.tsxDoneForward-D SVG mark
DropWordmarkcomponents/drop-logo.tsxDone"drop" in Fraunces
DropLogoFullcomponents/drop-logo.tsxDoneLogo mark + wordmark
DropAppIconcomponents/drop-logo.tsxDoneApp icon — rounded green square
CookieConsentcomponents/cookie-consent.tsxDoneGDPR consent banner + modal
PrePaymentDisclosurecomponents/pre-payment-disclosure.tsxDonePSD2 pre-payment modal
PWARegistercomponents/pwa-register.tsxDoneService Worker registration
drop-iconscomponents/drop-icons.tsxDone9 custom fintech icons

5.12 shadcn/ui Primitive Components (Atoms)

Component StatusFile VariantsRadix Primitive StorybookStatus
Alertui/alert.tsx— (div-based)Done
Avatarui/avatar.tsx@radix-ui/react-avatarDone
Badgeui/badge.tsx— (cva variants)Done
Button `{{ui/button.tsx@radix-ui/react-slotDone
WIPCard Planned}}`ui/card.tsx— (div-based)Done
Dialogui/dialog.tsx@radix-ui/react-dialogDone
Input `{{ui/input.tsx— (input element)Done
WIPScrollArea Planned}}`ui/scroll-area.tsx@radix-ui/react-scroll-areaDone
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`{{DoneWIPPlanned}}`
Badge`{{DoneWIPPlanned}}`
Avatar`{{DoneWIPPlanned}}`
Tooltip`{{DoneWIPPlanned}}`
Spinner`{{DoneWIPPlanned}}`
Divider`{{DoneWIPPlanned}}`

5.2 Composite Components (Molecules)

sonnertoast
ComponentStatusStorybook
CardSonner {{Status}}ui/sonner.tsx {{URL}}
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

fortransaction
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
PageLayoutFull-page layout with sidebar/header/main/footer slots
SectionContent section with standard vertical rhythmlists

6. Iconography Guidelines

fromparenttext
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 currentColorneverinherits hardcoded
Interactive iconsMust have visible focus ring + 44×44px touch targetcolor
Custom icons components/drop-icons.tsx — 9 domain-specific icons
Social authInline SVG optimized viaBankID SVGO,(green placedrounded rect, "ID"), Vipps (orange circle, "V")

Custom Drop Icons:

ExportDescription
IconSendMoneyArrow going up-right from horizontal line
IconQrScanQR code frame with scan corners
IconVirtualCardCredit card outline
IconShieldShield with checkmark
IconFastTransferLightning bolt
IconCorridorsGlobe with meridians
IconWalletWallet outline (unused — no wallet in pass-through model)
src/components/icons/IconHistoryClock with arrow
IconTopUpPlus 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

transition-colorsTextanimate-pulseNone
TokenUsage ValueUsage
--duration-instantButton hover 50msMicro-feedbacktransition-colors (checkboxCSS check)default ~150ms)
--duration-fastFocus states 100ms Buttonon states, hoverborder/ring
--duration-normalLoading states 200ms Modals,replacement dropdowns("Logger inn...", "Sender...") or Skeleton
--duration-slowScanner frame 300ms Pageon transitions,scan largepage panelscorners
--duration-slowerComplex animations 500ms Onboarding,in loadingPhase states1 — simplicity-first

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-springcubic-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

Requirement Buttons Inputs Modals TablesBottomNav NavigationTransaction List
Keyboard accessible Required Required Required Required Required
Focus visible ring-[#0B6E35] focus:border-[#0B6E35] Focus trap Required Required
ARIA role button textbox/combobox dialog table/gridnav navlist/listitem
Screen reader label aria-labelVisible text or visible textaria-label <label> associated aria-labelledby Caption + headersaria-label="Navigasjon" aria-label
Error state aria-describedbyText error msgmessage aria-invalidtext-[#EF4444]
Touch target 44×44pxh-12 min(48px) 44pxh-11 height(44px) Row:h-16 44px min(64px) 44×44pxmin-h-[44px]

9. Design Token Format

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 variablesCSS/JS/update Tailwind filesconfig → PR review.


10. Component DocumentationCode StandardPatterns

Primary

Each component story must include:

  1. Default story — basic render with minimal props
  2. All Variants story — every visual variant displayed
  3. All States story — hover, focus, disabled, error, loading
  4. Responsive story — behavior at each breakpoint
  5. 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>
*

Outline 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>

Props

Text 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