# DESIGN SYSTEM — GOVERNMENT SERVICE PORTAL
# Satu Mimpi City Administration
Version 2.0

---

## Design Direction

Style: Modern Government Dashboard
Tone: Professional · Trustworthy · Clean · Citizen-Friendly
Inspirasi: Gov.uk · Singapore Government Portal · Jakarta Smart City · Dukcapil Modern Portal

---

## Color Palette

### Primary (Green Government)

| Token | Hex | Tailwind Custom |
|---|---|---|
| green-500 | #4CAF50 | gov-primary |
| green-600 | #43A047 | gov-primary-dark |
| green-700 | #388E3C | gov-primary-darker |
| green-50 | #F1F8F1 | gov-primary-light |
| green-100 | #DCEDC8 | gov-primary-lighter |

### Neutral (Light Mode)

| Token | Hex | Usage |
|---|---|---|
| white | #FFFFFF | Card background |
| bg-page | #F7F9F7 | Page background |
| border | #E5E7EB | Dividers, input borders |
| text-primary | #1F2937 | Headings, body text |
| text-secondary | #6B7280 | Subtext, metadata |
| text-muted | #9CA3AF | Placeholder, disabled |

### Semantic Colors

| Token | Hex | Usage |
|---|---|---|
| danger | #EF4444 | Error, reject, delete |
| danger-light | #FEE2E2 | Error background |
| warning | #F59E0B | Pending, in-review |
| warning-light | #FEF3C7 | Warning background |
| info | #3B82F6 | Info, links |
| info-light | #DBEAFE | Info background |
| success | #22C55E | Approved, completed |
| success-light | #DCFCE7 | Success background |

### Dark Mode Tokens

| Token | Light | Dark |
|---|---|---|
| bg-page | #F7F9F7 | #0F1714 |
| bg-card | #FFFFFF | #1A2420 |
| bg-sidebar | #FFFFFF | #111F18 |
| bg-navbar | #FFFFFF | #131D18 |
| border | #E5E7EB | #2D3B35 |
| text-primary | #1F2937 | #F0F4F1 |
| text-secondary | #6B7280 | #8FA898 |
| gov-primary | #4CAF50 | #4CAF50 |
| gov-primary-dark | #43A047 | #5BB55F |

---

## Typography

Font Family: Poppins (Primary) → Inter → sans-serif
Load via: Google Fonts CDN (weights: 300, 400, 500, 600, 700)

### Scale

| Element | Size | Weight | Line Height |
|---|---|---|---|
| H1 | 48px / 3rem | 700 | 1.15 |
| H2 | 36px / 2.25rem | 700 | 1.2 |
| H3 | 28px / 1.75rem | 600 | 1.25 |
| H4 | 24px / 1.5rem | 600 | 1.3 |
| H5 | 20px / 1.25rem | 600 | 1.35 |
| H6 | 18px / 1.125rem | 500 | 1.4 |
| Body | 16px / 1rem | 400 | 1.6 |
| Small | 14px / 0.875rem | 400 | 1.5 |
| Caption | 12px / 0.75rem | 400 | 1.4 |
| Label | 12px / 0.75rem | 500 | 1.4 |

---

## Spacing Scale

Base unit: 4px (0.25rem)
Uses Tailwind default spacing scale.

| Usage | Value |
|---|---|
| Card padding | 24px (p-6) |
| Section gap | 32px (gap-8) |
| Page padding | 32px horizontal |
| Sidebar item padding | 12px 16px |
| Button padding | 10px 20px (px-5 py-2.5) |
| Input padding | 10px 14px |

---

## Border Radius

| Token | Value | Usage |
|---|---|---|
| radius-sm | 8px / rounded-lg | Buttons, badges, inputs |
| radius-md | 12px / rounded-xl | Cards, dropdowns |
| radius-lg | 16px / rounded-2xl | Modals, panels |
| radius-xl | 20px / rounded-3xl | Feature cards |
| radius-full | 9999px / rounded-full | Avatars, pills, toggles |

---

## Shadows

| Token | Value | Usage |
|---|---|---|
| shadow-card | 0 4px 20px rgba(0,0,0,.06) | Default card |
| shadow-card-hover | 0 8px 30px rgba(0,0,0,.10) | Card on hover |
| shadow-dropdown | 0 8px 30px rgba(0,0,0,.12) | Dropdowns, popovers |
| shadow-modal | 0 12px 40px rgba(0,0,0,.18) | Modals |
| shadow-navbar | 0 2px 12px rgba(0,0,0,.06) | Top navbar |
| shadow-sidebar | 4px 0 20px rgba(0,0,0,.05) | Sidebar right edge |

---

## Animation & Transitions

### Durations

| Token | Value | Usage |
|---|---|---|
| duration-fast | 150ms | Hover states, toggles |
| duration-normal | 250ms | Page transitions, modals |
| duration-slow | 400ms | Sidebar collapse, accordion |

### Easing

| Token | Value |
|---|---|
| ease-standard | cubic-bezier(0.4, 0, 0.2, 1) |
| ease-decelerate | cubic-bezier(0, 0, 0.2, 1) |
| ease-accelerate | cubic-bezier(0.4, 0, 1, 1) |

### Animation Definitions (Tailwind @keyframes)

```
fadeIn: opacity 0→1, translateY 8px→0
slideInLeft: translateX -16px→0, opacity 0→1
slideInRight: translateX 16px→0, opacity 0→1
slideDown: translateY -8px→0, opacity 0→1
scaleIn: scale 0.95→1, opacity 0→1
pulseGreen: box-shadow 0→0 0 0 4px rgba(76,175,80,0.3)→0
shimmer: background-position -200%→200% (skeleton loading)
countUp: custom JS (not CSS keyframe)
```

### Interactive States

```
Buttons:
  hover: bg-gov-primary-dark, shadow-md, scale-[1.01] translateY(-1px)
  active: scale-[0.99]
  focus: ring-2 ring-gov-primary ring-offset-2
  disabled: opacity-50 cursor-not-allowed

Cards:
  hover: shadow-card-hover translateY(-2px)
  transition: all 250ms ease-standard

Sidebar items:
  hover: bg-green-50 text-gov-primary dark:bg-green-950
  active: bg-gov-primary text-white font-medium
  transition: all 150ms ease-standard

Table rows:
  hover: bg-gray-50 dark:bg-gray-800/50
  transition: background 150ms

Inputs:
  focus: border-gov-primary ring-2 ring-gov-primary/20
  transition: border 150ms, box-shadow 150ms
```

---

## Layout

### Admin Panel

```
┌─────────────────────────────────────────────────────┐
│ NAVBAR (height: 72px, sticky top)                   │
├────────────────┬────────────────────────────────────┤
│ SIDEBAR        │ MAIN CONTENT AREA                  │
│ expanded: 280px│ padding: 32px                      │
│ collapsed: 80px│ max-width: calc(100% - 280px)      │
│ sticky, h-full │ overflow-y: auto                   │
│                │                                    │
└────────────────┴────────────────────────────────────┘
```

Sidebar collapse animation: width 280px→80px, 400ms ease-decelerate
Content area: margin-left transitions with sidebar
On collapse: hide text labels, show only icons centered

### Public Website

```
Container max-width: 1440px
Content max-width: 1280px
Section padding: py-16 lg:py-24
Section gap: 64px
```

---

## Breakpoints

| Name | Width | Behavior |
|---|---|---|
| Mobile | 375px | Single column, hamburger menu |
| Tablet | 768px | 2-column, sidebar hidden |
| Laptop | 1280px | Sidebar visible, full layout |
| Desktop | 1440px | Full content width |
| Wide | 1920px | Max content width capped |

Admin panel: sidebar collapses to drawer overlay on tablet/mobile.

---

## Icon Library

Library: Heroicons v2 (via CDN or Blade component)
Style: Outline (default) · Solid (active state, badges)
Size tokens:
  icon-xs: 14px (w-3.5 h-3.5)
  icon-sm: 16px (w-4 h-4)
  icon-md: 20px (w-5 h-5) ← default
  icon-lg: 24px (w-6 h-6)
  icon-xl: 32px (w-8 h-8)

---

## Tailwind Config Extensions

```js
// tailwind.config.js additions
theme: {
  extend: {
    fontFamily: {
      sans: ['Poppins', 'Inter', 'sans-serif'],
    },
    colors: {
      'gov': {
        50:  '#F1F8F1',
        100: '#DCEDC8',
        500: '#4CAF50',
        600: '#43A047',
        700: '#388E3C',
      },
    },
    borderRadius: {
      'xl': '12px',
      '2xl': '16px',
      '3xl': '20px',
    },
    boxShadow: {
      'card': '0 4px 20px rgba(0,0,0,.06)',
      'card-hover': '0 8px 30px rgba(0,0,0,.10)',
      'dropdown': '0 8px 30px rgba(0,0,0,.12)',
      'modal': '0 12px 40px rgba(0,0,0,.18)',
    },
    animation: {
      'fade-in': 'fadeIn 250ms ease-out',
      'slide-in-left': 'slideInLeft 300ms ease-out',
      'slide-down': 'slideDown 200ms ease-out',
      'scale-in': 'scaleIn 200ms ease-out',
      'shimmer': 'shimmer 1.5s infinite',
      'pulse-green': 'pulseGreen 1s ease-out',
    },
    keyframes: {
      fadeIn: {
        '0%': { opacity: '0', transform: 'translateY(8px)' },
        '100%': { opacity: '1', transform: 'translateY(0)' },
      },
      slideInLeft: {
        '0%': { opacity: '0', transform: 'translateX(-16px)' },
        '100%': { opacity: '1', transform: 'translateX(0)' },
      },
      slideDown: {
        '0%': { opacity: '0', transform: 'translateY(-8px)' },
        '100%': { opacity: '1', transform: 'translateY(0)' },
      },
      scaleIn: {
        '0%': { opacity: '0', transform: 'scale(0.95)' },
        '100%': { opacity: '1', transform: 'scale(1)' },
      },
      shimmer: {
        '0%': { backgroundPosition: '-200% 0' },
        '100%': { backgroundPosition: '200% 0' },
      },
      pulseGreen: {
        '0%': { boxShadow: '0 0 0 0 rgba(76,175,80,0.4)' },
        '70%': { boxShadow: '0 0 0 8px rgba(76,175,80,0)' },
        '100%': { boxShadow: '0 0 0 0 rgba(76,175,80,0)' },
      },
    },
    transitionDuration: {
      '250': '250ms',
      '400': '400ms',
    },
  },
},
plugins: [
  require('@tailwindcss/forms'),
  require('@tailwindcss/typography'),  // for CMS content rendering
],
```

---

## Dark Mode Setup

Strategy: class-based (`dark` class on `<html>`)
Toggle: Alpine.js + localStorage persistence
Default: system preference detection on first load

```js
// Alpine.js dark mode init (in app.js)
document.documentElement.classList.toggle(
  'dark',
  localStorage.theme === 'dark' ||
  (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
)
```

---

## Status Badge Design

| Status | BG (Light) | Text | BG (Dark) | Icon |
|---|---|---|---|---|
| Draft | gray-100 | gray-600 | gray-800 | PencilSquare |
| Submitted | blue-100 | blue-700 | blue-900 | ArrowUpTray |
| Pending | yellow-100 | yellow-700 | yellow-900 | Clock |
| Processing | purple-100 | purple-700 | purple-900 | ArrowPath |
| Approved | green-100 | green-700 | green-900 | CheckCircle |
| Rejected | red-100 | red-700 | red-900 | XCircle |
| Completed | teal-100 | teal-700 | teal-900 | CheckBadge |
| Printed | gray-100 | gray-600 | gray-800 | Printer |

All badges: rounded-full px-3 py-1 text-xs font-medium inline-flex items-center gap-1.5

---

## Chart.js Theme Config

```js
// Default Chart.js colors aligned with design system
const chartColors = {
  primary: '#4CAF50',
  primaryLight: 'rgba(76,175,80,0.15)',
  secondary: '#3B82F6',
  warning: '#F59E0B',
  danger: '#EF4444',
  success: '#22C55E',
  gridColor: 'rgba(107,114,128,0.15)',
  textColor: '#6B7280',
}

// Global Chart.js defaults
Chart.defaults.font.family = 'Poppins, sans-serif'
Chart.defaults.font.size = 12
Chart.defaults.color = chartColors.textColor
Chart.defaults.plugins.legend.display = true
Chart.defaults.plugins.tooltip.cornerRadius = 8
Chart.defaults.plugins.tooltip.padding = 10
```
