# UI Design System — Midnight Green

> 어떤 스택에서든 동일한 톤을 이식하기 위한 단일 소스.
>
> **한 줄 정의**: **미드나잇 블랙(`#05141f`)** 을 축으로 한 쿨 그레이 뉴트럴 스케일 위에, **액티브 그린(`#5ea61e`)** 이 차량 상태·활성 순간을 표시하는 **오너십(차량 관리) 앱** 시스템. 색 역할은 `on-surface / on-primary × lowest→ultimate` 강도 축으로 정의되고, 라이트·다크 두 테마 값이 모든 토큰에 쌍으로 존재한다.

---

## 목차

1. [디자인 원칙](#1-디자인-원칙)
2. [색상 토큰](#2-색상-토큰)
3. [타이포그래피](#3-타이포그래피)
4. [레이아웃 & 라운드](#4-레이아웃--라운드)
5. [핵심 컴포넌트](#5-핵심-컴포넌트)
6. [인터랙션](#6-인터랙션)
7. [다크 모드](#7-다크-모드)
8. [접근성](#8-접근성)
9. [안티패턴](#9-안티패턴)
10. [프레임워크 어댑터](#10-프레임워크-어댑터)
11. [이식 가이드](#11-이식-가이드)

---

## 1. 디자인 원칙

| 원칙 | 정의 |
|---|---|
| **미드나잇 블랙 축** | 순검정 대신 푸른 기미의 `#05141f`. 텍스트·아이콘·강조 면이 모두 이 축의 강도 단계다. |
| **그린 = 활성** | `#5ea61e`는 차량 시동·충전·활성 상태의 색. 정적 장식에 쓰지 않는다. |
| **쿨 그레이 스케일** | 뉴트럴은 블루 틴트 그레이(#303e48 → #cdd0d2 → #e8eaeb). 웜그레이 혼입 금지. |
| **양 테마 동등** | 모든 토큰이 라이트/다크 쌍으로 정의된다. 한쪽만 있는 색은 토큰이 아니다. |
| **상태 뱃지 체계** | 정보 전달 뱃지는 container/on-container 짝 5색(green/blue/red/yellow/gray)으로 규격화. |
| **차분한 볼드** | 버튼·핵심 수치는 Bold(700)로 명확하게, 배경·면은 절제된 그레이로. |

---

## 2. 색상 토큰

### 2-1. 뉴트럴 스케일 (라이트)

```css
:root {
  --surface:            #ffffff;
  --surface-low:        #f5f5f5;   /* 섹션·입력 배경 */
  --surface-container:  #ffffff;   /* 카드 */

  --on-surface:         #05141f;   /* 기본 텍스트 — 미드나잇 블랙 */
  --on-surface-low:     #3b4850;
  --on-surface-mid:     #4a565e;   /* 보조 텍스트 */
  --on-surface-high:    #636d74;   /* 캡션·라벨 */
  --on-surface-highest: #8d959a;   /* 플레이스홀더 */
  --on-surface-disabled:#b3b8bc;

  --outline-low:        #e8eaeb;   /* 기본 구분선 */
  --outline-mid:        #cdd0d2;   /* 입력 테두리 */
}
```

### 2-2. 액센트 & 뱃지

```css
:root {
  --active-primary:    #05141f;   /* 주 버튼 면 */
  --on-active-primary: #ffffff;
  --active-green:      #5ea61e;   /* 활성·충전·성공 */

  /* 상태 뱃지 — container / on-container 짝 */
  --badge-green:     #ebf5df;  --badge-on-green:  #33691e;
  --badge-blue:      #e3edfa;  --badge-on-blue:   #2a6ead;
  --badge-red:       #ffedee;  --badge-on-red:    #d4000e;
  --badge-yellow:    #fbecda;  --badge-on-yellow: #a85700;
  --badge-gray:      #e8eaeb;  --badge-on-gray:   #4a565e;
}
```

### 2-3. 사용 규칙

| 역할 | 토큰 |
|---|---|
| 제목·수치·본문 | `var(--on-surface)` |
| 보조 텍스트 | `var(--on-surface-mid)` |
| 캡션·단위 | `var(--on-surface-high)` |
| 페이지 배경 | `var(--surface)` |
| 섹션·입력 배경 | `var(--surface-low)` |
| 주 버튼 | `var(--active-primary)` + 흰 텍스트 |
| 차량 활성 상태 | `var(--active-green)` |
| 상태 뱃지 | badge 5색 container/on 짝 |

> **핵심 규칙**: 주 버튼은 그린이 아니라 **미드나잇 블랙 채움**이다. 그린은 상태의 색이지 액션의 색이 아니다.

---

## 3. 타이포그래피

### 3-1. 폰트

```css
:root {
  --font-sans: 'Noto Sans KR',
               -apple-system, 'Apple SD Gothic Neo', sans-serif;
}

html { font-size: 62.5%; }   /* 1rem = 10px — 원 시스템의 rem 기준 */
body {
  font-family: var(--font-sans);
  font-size: 1.4rem;
  color: var(--on-surface);
  background: var(--surface);
}
```

- 서체는 Regular/Bold 두 웨이트 중심 — **강조는 Bold(700) 점프**로.
- 전용 브랜드 서체가 있다면 스택 맨 앞에 추가한다.

### 3-2. 타입 스케일

| 역할 | `font-size` | `font-weight` | 용도 |
|---|---|---|---|
| Display | 24px | 700 | 홈 인사·주행거리 수치 |
| Title | 20px | 700 | 화면 제목 |
| Section | 16px | 700 | 카드 제목 |
| Body | 14px | 400 | 본문 |
| Button Small | 12px | 700 | 소형 버튼 |
| Caption | 12px | 400 | 단위·시각 |
| Micro | 10px | 400 | 뱃지·탭 라벨 |

- 수치(주행거리·배터리%)는 크게+Bold, 단위는 작게+Regular로 짝 구성.

---

## 4. 레이아웃 & 라운드

```css
:root {
  --radius-small:  0.5rem;    /* 8px — 버튼·입력·카드 */
  --radius-medium: 0.625rem;  /* 10px — 큰 카드 */
  --radius-pill:   100px;     /* 칩·뱃지 */
}

.container { max-width: 40rem; margin-inline: auto; padding-inline: 1.25rem; }
```

- 모바일 단일 컬럼. 홈은 차량 히어로(이미지+상태) → 퀵 액션 → 정보 카드 순.
- 버튼 소형 규격: `min-height 34px, padding 7px 23px, 12px Bold`.

---

## 5. 핵심 컴포넌트

### 5-1. 차량 상태 카드 — 시그니처

```html
<section class="vehicle-card">
  <img class="vehicle-card__img" src="..." alt="내 차량" />
  <div class="vehicle-card__stats">
    <div class="stat">
      <span class="stat__value">324<em>km</em></span>
      <span class="stat__label">주행 가능 거리</span>
    </div>
    <div class="stat">
      <span class="stat__value stat__value--active">78<em>%</em></span>
      <span class="stat__label">배터리 · 충전 중</span>
    </div>
  </div>
</section>
```

```css
.vehicle-card {
  background: var(--surface-container);
  border: 1px solid var(--outline-low);
  border-radius: var(--radius-medium);
  padding: 1.25rem;
}
.stat__value {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--on-surface);
}
.stat__value em { font-size: 0.75rem; font-style: normal; font-weight: 400; margin-left: 2px; }
.stat__value--active { color: var(--active-green); }
.stat__label { display: block; font-size: 0.75rem; color: var(--on-surface-high); }
```

### 5-2. 버튼

```css
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 3rem;
  padding: 0.75rem 1.5rem;
  border-radius: var(--radius-small);
  font-size: 0.875rem;
  font-weight: 700;
  cursor: pointer;
  transition: background-color 0.2s ease, opacity 0.2s ease;
}
.btn--primary  { background: var(--active-primary); color: var(--on-active-primary); border: none; }
.btn--primary:active { opacity: 0.85; }
.btn--outline  { background: var(--surface); color: var(--on-surface);
                 border: 1px solid var(--outline-mid); }
.btn--small    { min-height: 2.125rem; padding: 0.4375rem 1.4375rem; font-size: 0.75rem; }
.btn:disabled  { background: var(--surface-low); color: var(--on-surface-disabled); border: none; }
```

### 5-3. 상태 뱃지

```css
.badge {
  display: inline-flex;
  align-items: center;
  padding: 0.1875rem 0.5rem;
  border-radius: var(--radius-pill);
  font-size: 0.6875rem;
  font-weight: 700;
}
.badge--green  { background: var(--badge-green);  color: var(--badge-on-green); }
.badge--blue   { background: var(--badge-blue);   color: var(--badge-on-blue); }
.badge--red    { background: var(--badge-red);    color: var(--badge-on-red); }
.badge--yellow { background: var(--badge-yellow); color: var(--badge-on-yellow); }
.badge--gray   { background: var(--badge-gray);   color: var(--badge-on-gray); }
```

### 5-4. 퀵 액션 (원격 제어)

```css
.quick-actions { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.5rem; }
.quick-action {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.375rem;
  padding: 0.875rem 0;
  background: var(--surface-low);
  border: none;
  border-radius: var(--radius-small);
  font-size: 0.75rem;
  color: var(--on-surface);
  cursor: pointer;
}
.quick-action[aria-pressed="true"] {
  background: var(--active-primary);
  color: var(--on-active-primary);
}
.quick-action[aria-pressed="true"] .icon { color: var(--active-green); }
```

### 5-5. 바텀시트 & 약관 동의

- 바텀시트: 상단 라운드 + 그래버, 약관 동의(전체 동의 → 개별 항목) 패턴이 공통 모듈로 규격화되어 있다.
- 전체 동의는 구분선 위, 개별 항목은 체크박스 + 보기 링크 구조 고정.

---

## 6. 인터랙션

- 전환 `0.2s ease`. 원격 제어(시동·공조)는 실행 중 로딩 스피너 + 완료 시 그린 상태 반영.
- 스켈레톤 로딩이 규격화되어 있다 — 카드·리스트 자리보전 후 페이드 인.
- 수치 갱신(배터리·거리)은 즉시 반영. 카운트 애니메이션 금지.
- `prefers-reduced-motion` 대응: 모든 전환 시간 0.

---

## 7. 다크 모드

모든 토큰의 다크 쌍이 정의되어 있다 — 차량 앱은 야간 사용이 많아 다크가 준-기본이다.

```css
.dark {
  color-scheme: dark;

  --surface:            #05141f;   /* 미드나잇 블랙이 배경으로 */
  --surface-low:        #172630;
  --surface-container:  #172630;

  --on-surface:         #ffffff;
  --on-surface-low:     #cdd0d2;
  --on-surface-mid:     #b3b8bc;
  --on-surface-high:    #8d959a;
  --on-surface-highest: #81898e;
  --on-surface-disabled:#4a565e;

  --outline-low:        #303e48;
  --outline-mid:        #3b4850;

  --active-primary:    #ffffff;    /* 주 버튼이 화이트로 반전 */
  --on-active-primary: #05141f;
  --active-green:      #8bc34a;    /* 그린 한 단계 밝게 */

  --badge-green:  #285417; --badge-on-green:  #dcedc8;
  --badge-blue:   #2a4a66; --badge-on-blue:   #a8cdf0;
  --badge-red:    #5c1a1e; --badge-on-red:    #ffb3b8;
  --badge-yellow: #5c3a00; --badge-on-yellow: #f3c98a;
  --badge-gray:   #303e48; --badge-on-gray:   #b3b8bc;
}
```

- 다크의 주 버튼은 **화이트 채움 + 블랙 텍스트** 반전 — 미드나잇 배경 위 최대 대비.

---

## 8. 접근성

- `--on-surface`(#05141f) 대비 18.7:1 — AAA. `--on-surface-mid`(#4a565e) 7.9:1 — AAA.
- `--on-surface-highest`(#8d959a)는 플레이스홀더 전용 (대비 2.9:1).
- 그린은 상태 보조 신호 — 상태는 항상 텍스트(“충전 중”)와 병기한다.
- 원격 제어 버튼은 실행 결과를 텍스트로 확인시킨다 (성공/실패 토스트).
- 터치 타깃 48px, 소형 버튼(34px)은 주변 여백으로 히트 영역 보전.

---

## 9. 안티패턴

| 금지 | 이유 |
|---|---|
| 그린 채움 주 버튼 | 주 버튼은 미드나잇 블랙 — 그린은 상태의 색 |
| 순검정(#000) 사용 | 미드나잇 블랙(#05141f) 축 유지 |
| 웜그레이 혼입 | 쿨 그레이 스케일 단일 |
| 라이트 전용 색 추가 | 모든 토큰은 라이트/다크 쌍 필수 |
| 뱃지 임의 색 | 5색 container/on 짝 규격만 |
| 수치 카운트업 애니메이션 | 차량 데이터는 즉시·정확 |
| 상태를 색만으로 전달 | 텍스트 병기 필수 |
| 다크에서 블랙 채움 버튼 | 화이트 반전 규칙 |

---

## 10. 프레임워크 어댑터

### 10-1. Tailwind CSS v4

```css
@import "tailwindcss";

@theme {
  --color-surface:      #ffffff;
  --color-surface-low:  #f5f5f5;
  --color-on-surface:   #05141f;
  --color-on-surface-mid:  #4a565e;
  --color-on-surface-high: #636d74;
  --color-outline-low:  #e8eaeb;
  --color-active-green: #5ea61e;
  --radius-small:       0.5rem;
}

@custom-variant dark (&:where(.dark, .dark *));
```

### 10-2. Tailwind CSS v3

```js
module.exports = {
  darkMode: 'class',
  theme: {
    extend: {
      colors: {
        surface:       'var(--surface)',
        'surface-low': 'var(--surface-low)',
        'on-surface':  'var(--on-surface)',
        'on-surface-mid': 'var(--on-surface-mid)',
        'outline-low': 'var(--outline-low)',
        active:        'var(--active-green)',
      },
      borderRadius: { sm: '0.5rem', md: '0.625rem' },
    },
  },
};
```

### 10-3. React / Vue / Svelte

CSS 변수가 원천. 라이트/다크 쌍이 완전하므로 `.dark` 토글만으로 전 화면 전환.

---

## 11. 이식 가이드

### Step 1 — 토큰 복사

§2 라이트 + §7 다크 블록을 전역 CSS에 붙인다.

### Step 2 — 폰트 연결

Noto Sans KR을 연결하고, 전용 브랜드 서체 보유 시 스택 맨 앞에 추가한다.

### Step 3 — 시그니처 3종 적용

| 패턴 | 핵심 CSS |
|---|---|
| **미드나잇 주 버튼** | `background: #05141f; color: #fff; border-radius: 8px; font-weight: 700` |
| **수치+단위 짝** | 큰 Bold 수치 + 작은 Regular 단위, 활성 시 그린 |
| **상태 뱃지** | container/on 짝 5색 pill |

### Step 4 — 양 테마 검증

새로 추가한 모든 색이 다크 쌍을 갖는지 검사한다.

### 신규 컴포넌트 결정 트리

```
새 컴포넌트가 필요하다
  → 차량 상태를 보여주는가? → 수치+단위 짝 + 그린 활성 규칙
  → 액션인가? → 미드나잇 채움 or 아웃라인 (그린 채움 금지)
  → 상태 라벨인가? → 뱃지 5색 규격
  → 다크 쌍을 정의했는가? 아니면 미완성
```
