DESIGN.md — SHIELDIDLegacy format

Project: ShieldID — Privacy-Preserving Humanitarian Identity System. Tagline: 'Prove you qualify. Reveal nothing.' User A: UNHCR/NGO staff — Issuers of credentials User B: Refugees / displaced persons — Holders of credentials User C: Aid distribution workers — Verifiers of eligibility proofs Primary job: Verify humanitarian aid eligibility without exposing any PII Tone: Calm authority. Serious but not intimidating. Trustworthy, not cold. Aesthetic: 'Secure Clarity' — deep slate backgrounds, precision teal accents, clean geometric type, zero decorative noise.

Typography

text-displaySpace Grotesk · 2.25rem · Bold

Every letter tells a story worth reading, and every typeface gives that story a new voice waiting to be heard.

text-h1Space Grotesk · 1.75rem · Bold

Every letter tells a story worth reading, and every typeface gives that story a new voice waiting to be heard.

text-h2Space Grotesk · 1.25rem · SemiBold

Every letter tells a story worth reading, and every typeface gives that story a new voice waiting to be heard.

text-h3Space Grotesk · 1.0rem · SemiBold

Every letter tells a story worth reading, and every typeface gives that story a new voice waiting to be heard.

text-bodyInter · 0.9375rem · Regular

Every letter tells a story worth reading, and every typeface gives that story a new voice waiting to be heard.

text-body-smInter · 0.8125rem · Regular

Every letter tells a story worth reading, and every typeface gives that story a new voice waiting to be heard.

text-labelInter · 0.75rem · Medium

Every letter tells a story worth reading, and every typeface gives that story a new voice waiting to be heard.

text-captionInter · 0.6875rem · Regular

Every letter tells a story worth reading, and every typeface gives that story a new voice waiting to be heard.

text-monoJetBrains Mono · 0.8125rem · Regular

Every letter tells a story worth reading, and every typeface gives that story a new voice waiting to be heard.

text-mono-lgJetBrains Mono · 0.9375rem · SemiBold

Every letter tells a story worth reading, and every typeface gives that story a new voice waiting to be heard.

Project: ShieldID — Privacy-Preserving Humanitarian Identity System. Tagline: 'Prove you qualify. Reveal nothing.' User A: UNHCR/NGO staff — Issuers of credentials User B: Refugees / displaced persons — Holders of credentials User C: Aid distribution workers — Verifiers of eligibility proofs Primary job: Verify humanitarian aid eligibility without exposing any PII Tone: Calm authority. Serious but not intimidating. Trustworthy, not cold. Aesthetic: 'Secure Clarity' — deep slate backgrounds, precision teal accents, clean geometric type, zero decorative noise.

Components

Buttons

Inputs

Please enter a valid email

Elevation & Depth

shadow-card
shadow-elevated
shadow-modal
shadow-glow-success
shadow-glow-danger

Do's & Don'ts

Do

Use exact color tokens from the palette
Follow the spacing scale with 4px increments
Maintain 36px minimum for icon buttons
Always apply 0.5rem radius to buttons
Use semantic state colors for alerts

Don't

Use arbitrary spacing values outside the scale
Display holder PII in the UI
Use light mode in MVP
0. PROJECT DESIGN BRIEF

| Field | Value |

|--------------|-----------------------------------------------------------------------|

| Product | ShieldID |

| Tagline | "Prove you qualify. Reveal nothing." |

| User A | UNHCR/NGO staff — Issuers of credentials |

| User B | Refugees / displaced persons — Holders of credentials |

| User C | Aid distribution workers — Verifiers of eligibility proofs |

| Primary job | Verify humanitarian aid eligibility without exposing any PII |

| Tone | Calm authority. Serious but not intimidating. Trustworthy, not cold. |

| Aesthetic | "Secure Clarity" — deep slate backgrounds, precision teal accents, clean geometric type, zero decorative noise. |

| Signature | The ShieldMark — an animated shield glyph that pulses on successful proof verification. The single moment of visual celebration in an otherwise restrained interface. |

| Color mode | Dark only. No light mode in MVP. Target users operate in low-light field environments. |

---

1. DESIGN TOKENS

### 1.1 Color Palette

All color values are fixed. Never use arbitrary hex values in components — always reference token names.

#### Brand Core

| Token name | Hex | Role |

|----------------------|-----------|----------------------------------------------|

| brand-primary | #0F4C81 | Primary action color — buttons, active links, page headers |

| brand-secondary | #0D7A6B | Secondary accent — supporting highlights |

| brand-accent | #00C9A7 | Bright teal — hover states, proof-verified glow, focus rings |

#### Surface Scale (Dark-first)

| Token name | Hex | Role |

|--------------------|-----------|---------------------------------------------------|

| surface-base | #0B0F1A | Primary page background |

| surface-panel | #131929 | Card and panel backgrounds |

| surface-elevated | #1C2540 | Modal, dropdown, tooltip backgrounds |

| surface-subtle | #242E4C | Input fields, code blocks, inset areas |

#### Text

| Token name | Hex | Role |

|-------------------|-----------|----------------------------------------------------|

| content-primary | #EDF0F7 | Main readable text on dark backgrounds |

| content-secondary| #8C95B0| Labels, captions, metadata |

| content-muted | #4F5A78 | Placeholder text, disabled text |

#### Border

| Token name | Hex | Role |

|------------------|-----------|-----------------------------------------------------|

| border-default | #2A3555 | Standard card and input borders |

| border-focus | #00C9A7 | Focus ring color — always teal for accessibility |

| border-subtle | #1C2540 | Dividers and separators |

#### Semantic States

| Token name | Foreground | Background | Role |

|---------------|-------------|-------------|-----------------------------------------------|

| state-success | #00C9A7 | #003D30 | Valid credential, verified proof |

| state-warning | #F5A623 | #3D2A00 | Expiring credential, pending action |

| state-danger | #E74C3C | #3D0D09 | Invalid proof, expired credential, errors |

| state-info | #3498DB | #0D2A3D | Informational notices, help text |

#### Role Identity Colors

Each user role has a distinct color used in badges and role indicators. Never swap role colors.

| Role | Color | Usage context |

|------------|-----------|----------------------------------------------|

| Issuer | #6C63FF | UNHCR/NGO staff — purple, authoritative |

| Holder | #0D7A6B | Refugee / displaced person — teal, protected |

| Verifier | #F5A623 | Aid distribution worker — amber, operational |

---

### 1.2 Typography

Three typefaces form the typographic system. Each has an exclusive role — never substitute one for another.

| Role | Family | Weights used | Purpose |

|----------|-----------------|--------------|----------------------------------------------------|

| Display | Space Grotesk | 600, 700 | Hero headlines, page titles, section headers. Geometric and precise — conveys technical authority. |

| Body | Inter | 400, 500, 600| All readable prose, labels, UI copy. Neutral, high-legibility at small sizes on dark backgrounds. |

| Mono | JetBrains Mono | 400, 600 | Credential IDs, proof tokens, cryptographic hashes. Never use for general text. |

Source: Google Fonts CDN (Space+Grotesk, Inter, JetBrains+Mono).

#### Type Scale

| Scale token | Family | Size | Weight | Line height | Use case |

|----------------|---------------|-----------|--------|-------------|---------------------------------------|

| text-display | Space Grotesk | 2.25rem | 700 | 1.15 | Hero headlines, verification result title |

| text-h1 | Space Grotesk | 1.75rem | 700 | 1.2 | Page-level section headers |

| text-h2 | Space Grotesk | 1.25rem | 600 | 1.3 | Card titles, panel headers |

| text-h3 | Space Grotesk | 1.0rem | 600 | 1.4 | Sub-section labels, group headings |

| text-body | Inter | 0.9375rem | 400 | 1.65 | Default readable content |

| text-body-sm | Inter | 0.8125rem | 400 | 1.60 | Helper text, descriptions, secondary info |

| text-label | Inter | 0.75rem | 500 | 1.4 | Form labels, table column headers, badges — uppercase, tracked |

| text-caption | Inter | 0.6875rem | 400 | 1.4 | Timestamps, metadata, footnotes |

| text-mono | JetBrains Mono| 0.8125rem | 400 | 1.6 | Credential IDs, proof tokens |

| text-mono-lg | JetBrains Mono| 0.9375rem | 600 | 1.5 | Proof output display, key results |

---

### 1.3 Spacing System

Base unit: 4px. All spacing values are multiples of 4. Never use arbitrary values.

| Token | px | rem | Primary use case |

|------------|------|----------|-----------------------------------------------|

| space-1 | 4px | 0.25rem | Icon internal padding, micro gaps |

| space-2 | 8px | 0.5rem | Badge padding, tight list gaps |

| space-3 | 12px | 0.75rem | Button vertical padding, input vertical padding |

| space-4 | 16px | 1rem | Default card padding, form field gap — use this when in doubt for internals |

| space-5 | 20px | 1.25rem | Section inner padding |

| space-6 | 24px | 1.5rem | Card padding on desktop, gap between cards — use this when in doubt for inter-component gaps |

| space-8 | 32px | 2rem | Section-to-section gap, modal padding |

| space-10 | 40px | 2.5rem | Hero vertical padding |

| space-12 | 48px | 3rem | Page section top padding |

| space-16 | 64px | 4rem | Major section separation |

---

### 1.4 Border Radius

| Token | Value | Use case |

|--------------|--------|-------------------------------------------------------|

| radius-sm | 4px | Badges, chips, small status tags |

| radius-md | 8px | Buttons, inputs, small cards |

| radius-lg | 12px | Standard cards, panels, modals — default card radius |

| radius-xl | 16px | Dashboard widgets, feature cards |

| radius-full| 9999px | Avatar circles, status indicator dots only |

Design intent: corners are gently rounded — modern and trustworthy, but never pill-shaped or boxy.

---

### 1.5 Elevation & Shadow

Shadows are dark and deep. No soft white glows — this is a dark-mode product.

| Token | Role |

|--------------------|--------------------------------------------------|

| shadow-card | Default card elevation — subtle depth |

| shadow-elevated | Modals, dropdowns — clearly above base layer |

| shadow-modal | Full-screen overlays |

| shadow-glow-success | Teal outer glow — ShieldMark verified state (rgba(0,201,167,0.35)) |

| shadow-glow-danger | Red outer glow — invalid proof state (rgba(231,76,60,0.35)) |

---

### 1.6 Animation

Animations are purposeful and minimal. The only complex animation is the ShieldMark.

| Token | Duration | Easing | Use case |

|--------------------|----------|-------------------------------------------|----------------------------|

| transition-fast | 150ms | ease-in-out | Hover state changes |

| transition-base | 250ms | ease-in-out | General transitions |

| transition-slow | 400ms | ease-in-out | Modals, page-level changes |

| ease-spring | — | cubic-bezier(0.175, 0.885, 0.32, 1.275) | ShieldMark entry animation |

ShieldMark animation behavior:

On verification success: shield icon scales from 85% → 108% → 97% → 100% over 500ms using ease-spring.

After entry: shield pulses with a teal glow (opacity oscillates) — continuous, slow, 2s loop.

On verification failure: no animation. Danger state appears immediately, static.

Respect prefers-reduced-motion: disable all animations if user has this set.

---

2. COMPONENT GUIDELINES

### 2.1 Button System

All buttons share these baseline rules:

Font: Inter, weight 500, 15px

Border radius: radius-md (8px)

Minimum height: 44px (mobile touch target compliance)

Focus ring: 2px solid border-focus (#00C9A7), offset 2px — required on all variants

Disabled state: muted background (surface-subtle), muted text (content-muted), not-allowed cursor

#### Button Variants

| Variant | Background | Text | Border | Hover background | Use case |

|----------------|----------------|------------------|-----------------|------------------|-------------------------------------|

| btn-primary | brand-primary (#0F4C81) | content-primary | #1A6AAF (lighter blue) | #1A6AAF | Main CTA: Issue, Generate Proof, Verify |

| btn-secondary| transparent | content-primary | border-default | surface-elevated | Secondary: Export, Back, View Details |

| btn-success | #003D30 | state-success | state-success / 40% opacity | #004D3C | Verify action on Verifier portal only |

| btn-danger | #3D0D09 | state-danger | state-danger | #5A1410 | Revoke credential, destructive actions |

| btn-ghost | transparent | content-secondary | none | surface-subtle / content-primary text | Cancel, tertiary navigation |

| btn-icon | surface-subtle | content-secondary | border-subtle | surface-elevated / state-success text | Copy hash, download QR — square, 36×36px |

---

### 2.2 Form Inputs

Baseline rules for all form inputs:

Minimum height: 44px

Border radius: radius-md (8px)

Background: surface-subtle (#242E4C)

Default border: border-default (#2A3555)

Focus border: border-focus (#00C9A7) with a soft teal ring (10–20% opacity teal fill)

Text: content-primary, placeholder text: content-muted

Font: Inter, 15px, weight 400

Label: always above the input, never inside as placeholder — use text-label style (Inter, 12px, weight 500, uppercase, tracked)

Helper text: always rendered below input in text-body-sm + content-muted, even if empty (to preserve layout)

Error text: state-danger color, with a small alert icon prefix

#### Input Types and Special Behaviors

| Input type | Special rule |

|----------------------|--------------------------------------------------------------------------|

| Text, date, select | Standard rules above apply |

| Proof token textarea | Use text-mono (JetBrains Mono, 13px) — tokens are cryptographic strings, mono is required |

| Proof token textarea | 4 rows minimum, no resize, surface-subtle background |

| Select/dropdown | Hide default OS arrow, use a custom caret icon in content-secondary |

| Error state | Border becomes state-danger. Helper text becomes error message in state-danger with alert icon |

---

### 2.3 Navbar

Structure: Single fixed horizontal bar, 56px height, z-50, surface-panel background at 95% opacity with backdrop-blur.

Bottom border: 1px, border-subtle.

Page content offset: All pages must have padding-top: 56px to clear the fixed navbar.

#### Navbar Anatomy

```

[ ◈ ShieldID ] [ Dashboard ] [ Issue ] [ Verify ] [ Audit Log ] [ ROLE BADGE ] [ Avatar ▼ ]

logo + name ← nav links filtered by user role → identity area

```

Logo: Shield icon (teal, 24×24px) + wordmark "ShieldID" where "ID" is brand-accent (#00C9A7).

Nav links behavior:

Default: content-secondary text, transparent background

Hover: content-primary text, surface-subtle background

Active/current page: content-primary text, surface-elevated background

Never use underline — use background highlight only

Links are role-filtered: Issuers see Issue link; Verifiers see Verify link; all roles see Dashboard and Audit Log

Role badge: Small chip displayed next to avatar. Colors from role identity tokens (section 1.1). Style: radius-sm (4px), text-label size, 20% opacity background of role color, full-opacity text and border.

Avatar button: 32×32px circle, surface-elevated background, border-default border. On hover: border becomes border-focus. Shows user initial or avatar image.

Mobile: Hamburger icon at right edge. Tap opens full-screen overlay menu on surface-elevated background. Nav links stack vertically, larger touch targets.

---

### 2.4 Card System

All cards share these base rules:

Background: surface-panel (#131929)

Border: 1px solid border-default (#2A3555)

Border radius: radius-lg (12px)

Padding: space-6 (24px) on desktop, space-4 (16px) on mobile

No box shadow by default — border provides the definition on dark backgrounds

#### Card Variants

Standard card — Generic container for grouped content. No hover state unless it is interactive.

Credential card (Holder wallet) — Interactive, tappable. Rules:

On hover: border transitions to border-focus (#00C9A7)

Left edge: a 4px vertical status stripe in the credential's state color (success/warning/danger)

Status badge: top-right corner chip using semantic state colors

Credential ID displayed in text-mono, truncated to 32 characters + "..."

Actions row at bottom: primary button + icon buttons for QR and export

Never display holder name, photo, national ID, or any other PII anywhere in the card

Verification result card (ShieldMark — the signature element):

SUCCESS state: background #003D30, border state-success at 40% opacity. Contains ShieldMark icon (64×64px, teal, with glow), "VERIFIED" in text-display using state-success, eligibility summary below (aid category + validity). No holder identity shown.

FAILURE state: background #3D0D09, border state-danger at 40% opacity. Contains X-shield icon (64×64px, red), "INVALID" in text-display using state-danger, brief error description.

This is the most visually prominent element in the entire product. Do not simplify it.

Dashboard stats card — Single metric. Contains: label in text-label + content-muted, value in text-display + content-primary, trend indicator in text-body-sm using state color.

Alert / notice card — Inline informational strip. Always uses the semantic background + foreground pair (state-info, state-warning, etc.). Contains an icon (18×18px) + text-body-sm message. Uses role="alert" or role="status" attribute.

---

3. TECH STACK & LAYOUT RULES

### 3.1 Technology Stack

| Layer | Choice | Notes |

|----------------|-----------------------|--------------------------------------------------------|

| Framework | Next.js 14 (App Router) | TypeScript, strict mode — no any types |

| Styling | Tailwind CSS v3 | Custom config extends tokens from section 1 |

| Icon library | lucide-react | Consistent, tree-shakeable — no mixing icon sets |

| UI primitives | shadcn/ui (Radix UI) | For Dialog, Dropdown, Tooltip — do not modify base styles |

| Crypto layer | SubtleCrypto Web API | Built-in browser API, zero dependencies |

| QR generation | qrcode | — |

| QR scanning | jsQR | — |

| State | React Context + useReducer | No external state library for MVP |

| Forms | react-hook-form + zod | All form inputs validated client-side before submission |

| HTTP client | Native fetch | No axios for MVP |

| Backend | FastAPI (Python 3.11+)| Type hints required on all functions |

| Auth | JWT (python-jose) + bcrypt (passlib) | — |

| ORM | SQLAlchemy async + Alembic | — |

| Validation | Pydantic v2 | — |

| Database | Supabase (PostgreSQL) | Pre-configured before Day H (see competition rules) |

| Frontend deploy| Vercel | Pre-configured before Day H |

| Backend deploy | Railway or Render | Pre-configured before Day H |

#### Database Schema Summary

Three tables only. No holder PII is stored at any level.

| Table | Key columns |

|-----------------------|------------------------------------------------------------------------|

| issuers | id, name, organization, role, public_key_pem |

| credential_anchors | id, hash (not raw credential), issuer_id, issued_at, expires_at, revoked |

| verification_events | id, proof_hash (not identity), verifier_id, result, timestamp |

---

### 3.2 Page Layout Architecture

Global layout wrapper: Fixed navbar (56px) + scrollable content area with min-height: 100vh, background surface-base. Content inside a centered container, max-width: 1152px, horizontal padding space-4 mobile / space-6 tablet / space-8 desktop, vertical padding space-8 mobile / space-10 desktop.

ASCII layout wireframe:

```

┌──────────────────────────────────────────────────────────────────────┐

│ NAVBAR — fixed, 56px tall, full width, z-index 50 │

├──────────────────────────────────────────────────────────────────────┤

│ PAGE (background: surface-base, padding-top: 56px) │

│ ┌────────────────────────────────────────────────────────────────┐ │

│ │ CONTENT CONTAINER — max-w-1152px, centered, horizontal padding │ │

│ │ │ │

│ │ [ PAGE CONTENT GOES HERE ] │ │

│ └────────────────────────────────────────────────────────────────┘ │

└──────────────────────────────────────────────────────────────────────┘

```

---

### 3.3 Grid System

Mobile-first. All breakpoints use Tailwind defaults — no custom breakpoints.

| Layout context | Mobile (default) | Tablet md: 768px | Desktop lg: 1024px |

|-------------------------|------------------|--------------------|------------------------|

| Dashboard stats row | 2 columns | 2 columns | 4 columns |

| Credential card grid | 1 column | 2 columns | 3 columns |

| Main content + sidebar | 1 column | 1 column | 3 cols (main: 2 cols) |

| Issue form + preview | 1 column | 1 column | 2 columns |

| Full-width single col | full width | full width | full width |

Column gap: space-4 (16px) on mobile, space-6 (24px) on desktop.

AI Agent rule: Always write mobile styles first (no breakpoint prefix), then override with md: and lg:.

---

### 3.4 Responsive Breakpoints

| Breakpoint | Min width | Behavior |

|------------|-----------|----------------------------------------------|

| (default) | 0px | Single column, full-width, stacked layout |

| sm: | 640px | Minor tweaks — some elements shown/hidden |

| md: | 768px | 2-column grids, expanded navbar |

| lg: | 1024px | Full multi-column layout, max-width container active |

| xl: | 1280px | Container padding widens slightly |

---

### 3.5 Color Mode

Dark mode only. The dark class is applied to the `` element unconditionally. No toggle. No light mode in MVP.

Rationale: target users operate in low-light field environments; dark backgrounds reduce eye strain and battery consumption on mobile devices in the field.

---

### 3.6 Page-Specific Layout Rules

#### Dashboard (/dashboard)

Structure: stats row (4 cards) → recent credentials table → quick-issue CTA button.

Table shows maximum 10 rows. Pagination required — no infinite scroll.

No sidebar.

#### Credential Issuance Form (/issue)

Structure: single-column centered form, max-width 576px.

Three logical sections: [Holder Info] → [Aid Category + Validity] → [Review & Sign].

Progress shown as a simple step counter ("Step 2 of 3") — not a wizard sidebar, not a progress bar.

On desktop, optionally show a credential preview card to the right of the form.

#### Holder Wallet (/wallet)

Structure: filter tabs (All / Valid / Expiring / Expired) → credential card grid.

Empty state: centered ghost icon (48px, content-muted) + heading "No credentials yet" + description pointing to their UNHCR officer.

#### Proof Generation (/wallet/[id]/prove)

Structure: single full-width panel. No distractions, no sidebar, no unrelated navigation.

Step 1: Show credential summary (type + validity only — no PII).

Step 2: Generating state — spinner + copy "Preparing your proof in your browser…" + info notice "No data leaves your device."

Step 3: Proof ready — large QR code centered, copy-token button below, optional "Show full token" toggle (truncated by default).

Absolute rule: never display raw credential data at any step.

#### Verifier Portal (/verify)

Structure: centered container, max-width 672px.

Input: proof token paste area OR QR scan button.

Verification result: occupies the full panel, replaces the input. Contains the ShieldMark (signature element). Do not minimize or simplify this.

After result: audit log entry auto-saved silently (proof hash only).

#### Audit Log (/audit)

Structure: filterable, sortable table.

Columns: Timestamp, Verifier ID, Result (VALID/INVALID badge), Proof Hash (truncated mono).

No holder identity data in any column — ever.

CSV export button available in table header.

---

### 3.7 Loading & Empty States

#### Loading

Content areas (tables, card grids): use skeleton shimmer — 2–4 placeholder rows/cards with animate-pulse on surface-subtle rounded rects. Never use a spinner for content placeholders.

Action buttons (submit, generate proof, verify): replace button label with spinner icon + present-tense verb (e.g., "Verifying…"). Disable the button during loading.

Proof generation specifically: show a dedicated generating state with a larger animated indicator and the "no data leaves your device" reassurance copy. This is security-critical messaging.

#### Empty States

Every list or grid that can be empty must define an explicit empty state. Do not show blank space.

| Screen | Empty state copy |

|-------------------|------------------------------------------------------------------------|

| Holder wallet | "No credentials yet. Ask your UNHCR officer to issue your first credential." |

| Audit log | "No verification events recorded yet." |

| Issued credentials | "No credentials issued yet. Use the Issue button to get started." |

Empty state anatomy: centered layout, ghost/outline icon (48px, content-muted), text-h3 heading in content-secondary, text-body-sm description in content-muted.

---

### 3.8 Critical Security UX Rules

These are design constraints, not just security notes. They are non-negotiable.

1. Never display holder PII in the UI. Name, national ID, biometrics, ethnicity, country of origin must never appear on screen at any point. Show only: credential type, validity period, eligible aid category.

2. Proof generation requires a visible "in-browser" notice. Every proof generation screen must display the copy "Proof computed locally. No data sent to servers." in a state-info alert card above the generating animation.

3. Proof tokens are always truncated in display. Show first 12 characters + "…" + last 8 characters. A "Show full token" toggle may reveal the complete token — it must be opt-in.

4. Audit trail logs proof hashes only. The verification_events table stores proof_hash, verifier_id, timestamp, result. No credential ID, no holder reference. This constraint must be reflected in both UI and backend.

5. Session timeout on holder screens. After 10 minutes of inactivity on any holder page (/wallet, /wallet/[id]/prove), show a modal warning: "For your security, your session will expire in 60 seconds." with a "Stay logged in" button.

---

4. ACCESSIBILITY BASELINE

Minimum standard: WCAG 2.1 AA. The following rules are enforced, not aspirational.

| Requirement | Rule |

|---------------------|-----------------------------------------------------------------------------|

| Color contrast | All body text ≥ 4.5:1 contrast ratio against background. Large text ≥ 3:1. Design tokens are verified to meet this. |

| Focus ring | 2px solid border-focus (#00C9A7), 2px offset — required on every interactive element without exception. |

| Reduced motion | All animations (including ShieldMark) must be disabled when prefers-reduced-motion: reduce is set. |

| Semantic HTML | Use `, , , for landmark regions. Never use ` for structural roles. |

| Form labels | Every visible input has an explicit ` with for attribute. aria-label` is not a substitute for visible inputs. |

| Alert roles | All dynamic state feedback uses role="alert" (for errors) or role="status" (for success). |

| Keyboard navigation | Every interactive element must be reachable and fully operable via keyboard alone. Tab order must follow visual reading order. |

| QR code alt text | Format: "Credential QR code for [credential type] — scan to verify" |

---

5. COPYWRITING STANDARDS

Voice: Precise, calm, human. Not bureaucratic, not "techy", not emotional.

Principle: Every string must answer "what does this mean for the person right now?"

| Context | ❌ Do not write | ✅ Write instead |

|----------------------|------------------------------------|-----------------------------------------------|

| Proof verified | "ZKP verification successful" | "Verified — eligible for Food Ration B" |

| Proof failed | "Cryptographic assertion failed" | "Proof is invalid or expired" |

| Generating proof | "Executing HMAC computation" | "Preparing your proof…" |

| No PII stored | "Data minimization policy applied" | "No personal data stored on our servers" |

| Credential expired | "VC validity period elapsed" | "This credential expired on 31 Dec 2025" |

| Issue action | "Instantiate new VC record" | "Issue new credential" |

| Session expiring | "Authentication token invalidation imminent" | "Your session will expire in 60 seconds." |

| Revoke action | "Nullify credential assertions" | "Revoke credential" |

---

6. PROJECT FILE STRUCTURE

AI Coding Agents must follow this structure exactly. Do not deviate.

```

shieldid/

├── app/ # Next.js App Router

│ ├── (auth)/

│ │ ├── login/page.tsx

│ │ └── layout.tsx

│ ├── (app)/

│ │ ├── dashboard/page.tsx

│ │ ├── issue/page.tsx

│ │ ├── wallet/

│ │ │ ├── page.tsx

│ │ │ └── [id]/prove/page.tsx

│ │ ├── verify/page.tsx

│ │ ├── audit/page.tsx

│ │ └── layout.tsx # Renders Navbar

│ ├── globals.css # Tailwind directives + CSS token variables

│ └── layout.tsx # Root: applies dark class, loads fonts

├── components/

│ ├── ui/ # shadcn/ui primitives — DO NOT MODIFY

│ ├── credential/

│ │ ├── CredentialCard.tsx # Credential card variant (section 2.4)

│ │ ├── CredentialForm.tsx # Issue form (section 3.6)

│ │ └── ProofGenerator.tsx # Proof generation flow (section 3.6)

│ ├── verify/

│ │ ├── ProofInput.tsx # Token paste + QR scan

│ │ └── VerificationResult.tsx # ShieldMark — signature element

│ └── shared/

│ ├── Navbar.tsx

│ ├── RoleBadge.tsx

│ ├── StatusBadge.tsx

│ ├── EmptyState.tsx

│ └── LoadingSkeleton.tsx

├── lib/

│ ├── crypto.ts # ALL SubtleCrypto logic lives here — nowhere else

│ ├── credential.ts # VC creation and parsing utilities

│ ├── qr.ts # QR encode/decode wrappers

│ └── api.ts # Typed fetch wrapper for backend

├── types/

│ └── index.ts # All shared TypeScript interfaces

├── tailwind.config.js # Extends tokens from section 1

├── DESIGN.md # ← THIS FILE

└── .env.local.example

```

Architecture rule: All cryptographic operations live exclusively in lib/crypto.ts. Components must never call SubtleCrypto directly — they call functions exported from crypto.ts. This makes the crypto layer auditable in one place.

---

7. QUICK REFERENCE

For AI Coding Agents: consult this table first before any implementation decision.

| Decision | Answer |

|------------------------|---------------------------------------------------------------------|

| Page background | surface-base (#0B0F1A) |

| Card background | surface-panel (#131929) |

| Input background | surface-subtle (#242E4C) |

| Modal background | surface-elevated (#1C2540) |

| Primary text | content-primary (#EDF0F7) |

| Label / caption text | content-secondary (#8C95B0) |

| Placeholder text | content-muted (#4F5A78) |

| Default border | border-default (#2A3555) |

| Focus border | border-focus (#00C9A7) with teal ring |

| Primary action color | brand-primary (#0F4C81) |

| Success / verified | state-success (#00C9A7) on #003D30 background |

| Warning / expiring | state-warning (#F5A623) on #3D2A00 background |

| Error / invalid | state-danger (#E74C3C) on #3D0D09 background |

| Display font | Space Grotesk |

| Body font | Inter |

| Mono font (hashes) | JetBrains Mono |

| Button border radius | 8px (radius-md) |

| Card border radius | 12px (radius-lg) |

| Badge border radius | 4px (radius-sm) |

| Spacing base unit | 4px — always multiples of 4 |

| Internal component gap | space-4 (16px) — default |

| Inter-component gap | space-6 (24px) — default |

| Color mode | Dark only — class="dark" on ``, no toggle |

| PII in UI | Never. No name, ID, biometrics, origin. Only type + validity. |

| Signature element | ShieldMark on /verify result — animated, full panel, never skip |

| Crypto location | lib/crypto.ts only — never in components |

---

End of DESIGN.md — ShieldID v2.0.0

Download .md

License MIT
Uploaded today
Version v1
File size 35.6 KB
Downloads 1
Copies 0

Use with MCP

Using designmd mcp, download the design system https://designmd.ai/Hihanghohengg/design-md-shieldid and implement it in my code

Don't have the MCP? Install it here