# UI Design System — LINE LDSG

> 어떤 스택에서든 동일한 톤을 이식하기 위한 단일 소스. LINE Design System for Global(LDSG) 공개 문서의 톤을 웹 이식용으로 재해석한 문서다.
>
> **한 줄 정의**: 화이트 베이스 위 **브랜드 그린(`#06c755`)** 을 핵심 액션에만 사용하는 **글로벌 메신저** 시스템. 그린(긍정/브랜드)·레드(`#ff3a44`, 오류)·블루(`#4d73ff`, 링크)의 역할이 엄격히 분리되어 있고, 뉴트럴은 따뜻함 없는 순수 그레이로 유지한다.

---

## 목차

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

---

## 1. 디자인 원칙

| 원칙 | 정의 |
|---|---|
| **그린은 브랜드 액션 전용** | `#06c755`는 주 CTA·브랜드 식별에만. 장식·배경·아이콘 채색에 남용하지 않는다. |
| **시맨틱 3색 분리** | 긍정=그린(`#07b53b`), 오류=레드(`#ff3a44`), 링크=블루(`#4d73ff`). 서로의 역할을 침범하지 않는다. |
| **순수 그레이 뉴트럴** | 뉴트럴은 색 기미 없는 순수 그레이(#1f1f1f~#fcfcfc). 웜/쿨 틴트 금지. |
| **글로벌 우선** | 텍스트 길이가 언어별로 2~3배 달라진다 — 고정폭 라벨 금지, 줄바꿈·말줄임 규칙 필수. |
| **명확한 상태** | 모든 인터랙티브 요소는 default / hover / pressed / disabled 4상태를 가진다. |
| **중간 라운드** | 8px 기본. 과한 라운드(pill)는 칩·아바타에만, 직각은 사용하지 않는다. |

---

## 2. 색상 토큰

### 2-1. 브랜드 & 시맨틱

```css
:root {
  --ldsg-green:       #06c755;  /* LINE 브랜드 그린 — 주 CTA */
  --ldsg-green-hover: #05b34c;
  --ldsg-green-press: #049f44;

  --semantic-success: #07b53b;  /* 긍정·완료 */
  --semantic-error:   #ff3a44;  /* 오류·삭제·경고 */
  --semantic-link:    #4d73ff;  /* 링크·정보 */
}
```

### 2-2. 뉴트럴 스케일

```css
:root {
  --neutral-0:    #ffffff;  /* 페이지 배경 */
  --neutral-50:   #fcfcfc;  /* 옅은 표면 */
  --neutral-100:  #f7f8f9;  /* 섹션·입력 배경 */
  --neutral-150:  #f5f5f5;
  --neutral-200:  #efefef;  /* 옅은 구분선 */
  --neutral-300:  #dfdfdf;  /* 기본 구분선 */
  --neutral-350:  #d9d9d9;
  --neutral-400:  #b7b7b7;  /* 비활성 */
  --neutral-500:  #949494;  /* 플레이스홀더 */
  --neutral-600:  #616161;  /* 보조 텍스트 */
  --neutral-800:  #2a2a2a;
  --neutral-900:  #1f1f1f;  /* 기본 텍스트 */
}
```

### 2-3. 시맨틱 별칭 — 컴포넌트가 소비하는 층

```css
:root {
  --bg:            var(--neutral-0);
  --surface:       var(--neutral-100);
  --text:          var(--neutral-900);
  --text-sub:      var(--neutral-600);
  --text-hint:     var(--neutral-500);
  --text-disabled: var(--neutral-400);
  --divider:       var(--neutral-300);
  --divider-weak:  var(--neutral-200);
  --primary:       var(--ldsg-green);
}
```

### 2-4. 사용 규칙

| 역할 | 토큰 |
|---|---|
| 본문·제목 | `var(--text)` |
| 보조 설명 | `var(--text-sub)` |
| 플레이스홀더 | `var(--text-hint)` |
| 페이지 배경 | `var(--bg)` |
| 입력·섹션 배경 | `var(--surface)` |
| 구분선 | `var(--divider)` 1px |
| 주 CTA | `var(--primary)` + 흰 텍스트 |
| 링크 | `var(--semantic-link)` |
| 오류 | `var(--semantic-error)` |

> **핵심 규칙**: 그린 위 텍스트는 항상 흰색. 그린을 텍스트 색으로 쓰지 않는다(성공 메시지는 `--semantic-success` 아이콘 + 기본 텍스트).

---

## 3. 타이포그래피

### 3-1. 폰트

LDSG의 기준 서체는 **LINE Seed**. 한국어는 LINE Seed Sans KR, 미설치 환경 폴백은 Noto Sans KR.

```css
:root {
  --font-sans: 'LINE Seed Sans KR', 'LINESeedKR', 'Noto Sans KR',
               system-ui, -apple-system, Roboto, sans-serif;
}

body {
  font-family: var(--font-sans);
  color: var(--text);
  background: var(--bg);
}
```

```html
<!-- LINE Seed 미배포 환경 폴백 -->
<link rel="stylesheet"
  href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;700;800&display=swap" />
```

- LINE Seed는 Regular / Bold / ExtraBold 3개 웨이트 축을 기본으로 사용한다. Medium이 없으므로 **강조는 Bold로 점프**한다.

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

| 역할 | `font-size` | `font-weight` | `line-height` |
|---|---|---|---|
| Display | 28px | 800 | 1.35 |
| Heading 1 | 22px | 700 | 1.4 |
| Heading 2 | 18px | 700 | 1.4 |
| Subtitle | 16px | 700 | 1.5 |
| Body | 15px | 400 | 1.5 |
| Body Small | 13px | 400 | 1.5 |
| Caption | 11px | 400 | 1.4 |

```css
.display  { font-size: 1.75rem;   font-weight: 800; line-height: 1.35; }
.heading1 { font-size: 1.375rem;  font-weight: 700; line-height: 1.4; }
.body     { font-size: 0.9375rem; line-height: 1.5; }
.caption  { font-size: 0.6875rem; color: var(--text-hint); }
```

### 3-3. 글로벌 텍스트 규칙

- 라벨·버튼 텍스트는 고정폭 금지 — 콘텐츠에 맞춰 늘어나되 최대폭에서 말줄임.
- 한국어 `word-break: keep-all`, 그 외 언어 `overflow-wrap: break-word`.
- 숫자는 `font-variant-numeric: tabular-nums`로 정렬.

---

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

```css
:root {
  --radius:      0.5rem;   /* 8px — 버튼·카드·입력 */
  --radius-pill: 62.4375rem; /* 칩·아바타 */
  --spacing:     0.25rem;  /* 4px 배수 */
}

.container { max-width: 48rem; margin-inline: auto; padding-inline: 1rem; }
```

- 리스트 중심 레이아웃. 콘텐츠는 셀(cell) 단위로 쌓고 셀 사이는 `--divider-weak` 1px.

---

## 5. 핵심 컴포넌트

### 5-1. 버튼

```css
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.75rem 1.25rem;
  border-radius: var(--radius);
  font-size: 0.9375rem;
  font-weight: 700;
  border: none;
  cursor: pointer;
  transition: background-color 0.15s ease;
}

/* Primary — 브랜드 그린 */
.btn--primary { background: var(--primary); color: #ffffff; }
.btn--primary:hover  { background: var(--ldsg-green-hover); }
.btn--primary:active { background: var(--ldsg-green-press); }

/* Secondary — 뉴트럴 */
.btn--secondary {
  background: var(--surface);
  color: var(--text);
}
.btn--secondary:hover { background: var(--neutral-200); }

/* Destructive */
.btn--danger { background: var(--semantic-error); color: #ffffff; }

.btn:disabled {
  background: var(--neutral-200);
  color: var(--text-disabled);
  cursor: default;
}
```

### 5-2. 리스트 셀 (메신저의 기본 단위)

```html
<li class="cell">
  <img class="cell__avatar" src="..." alt="" />
  <div class="cell__body">
    <p class="cell__title">채팅방 이름</p>
    <p class="cell__sub">마지막 메시지 미리보기…</p>
  </div>
  <span class="cell__meta">오후 2:41</span>
</li>
```

```css
.cell {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--divider-weak);
}
.cell__avatar { width: 48px; height: 48px; border-radius: var(--radius-pill); }
.cell__title  { font-size: 1rem; font-weight: 700; color: var(--text); }
.cell__sub    { font-size: 0.8125rem; color: var(--text-sub);
                overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cell__meta   { font-size: 0.6875rem; color: var(--text-hint); }
```

### 5-3. 뱃지 & 토스트

```css
.badge {
  display: inline-flex;
  min-width: 18px;
  padding: 0 5px;
  border-radius: var(--radius-pill);
  background: var(--semantic-error);
  color: #ffffff;
  font-size: 0.6875rem;
  font-weight: 700;
  justify-content: center;
}

.toast {
  background: rgba(31, 31, 31, 0.92);
  color: #ffffff;
  padding: 0.75rem 1.25rem;
  border-radius: var(--radius);
  font-size: 0.875rem;
}
```

---

## 6. 인터랙션

- 호버는 배경 한 단계 어둡게, 프레스는 두 단계. `0.15s ease`.
- 셀 탭 피드백: `background: var(--neutral-150)` 즉시 적용.
- 오버레이(모달·시트) 배경은 `rgba(0,0,0,0.5)`, 등장 0.2s `ease-out`.

---

## 7. 다크 모드

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

  --bg:            #1f1f1f;
  --surface:       #2a2a2a;
  --text:          #efefef;
  --text-sub:      #b7b7b7;
  --text-hint:     #949494;
  --text-disabled: #616161;
  --divider:       #3d3d3d;
  --divider-weak:  #333333;

  /* 브랜드 그린은 유지 — 어두운 배경에서도 대비 충분 */
  --primary:       #06c755;
  --semantic-error: #ff6066;   /* 레드만 한 단계 밝게 */
  --semantic-link:  #7893ff;
}
```

- 그린은 다크에서 그대로 유지 (흰 텍스트 대비 유지됨). 레드·블루는 한 단계 밝게.

---

## 8. 접근성

- `--text`(#1f1f1f) 대비 16.1:1, `--text-sub`(#616161) 5.9:1 — AA 통과.
- `--text-hint`(#949494)는 플레이스홀더 전용 (대비 2.8:1) — 정보 텍스트 금지.
- 그린 버튼 흰 텍스트 대비 2.3:1로 낮으므로 **버튼은 최소 15px Bold**를 유지하고, 텍스트만으로 상태를 전달하지 않는다.
- 뱃지 숫자에는 `aria-label`("읽지 않은 메시지 3개")을 병기.
- 언어 전환을 고려해 방향·길이에 의존하는 레이아웃을 피한다.

---

## 9. 안티패턴

| 금지 | 이유 |
|---|---|
| 그린을 장식·배경·본문 텍스트에 사용 | 브랜드 액션 전용 원칙 |
| 성공 메시지를 그린 텍스트로 표기 | 아이콘 + 기본 텍스트로 — 그린 텍스트 금지 |
| 링크에 그린/레드 사용 | 링크는 `--semantic-link` 블루 고정 |
| 웜그레이·쿨그레이 도입 | 순수 그레이 원칙 |
| Medium 웨이트 사용 | LINE Seed는 Regular/Bold/ExtraBold 3축 |
| 고정폭 라벨·버튼 | 다국어 길이 변동 대응 불가 |
| 직각 모서리 | 8px 라운드가 기본 |
| 원시 뉴트럴 직접 참조 | 시맨틱 별칭 층을 거칠 것 |

---

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

### 10-1. Tailwind CSS v4

```css
@import "tailwindcss";

@theme {
  --color-bg:       #ffffff;
  --color-surface:  #f7f8f9;
  --color-text:     #1f1f1f;
  --color-text-sub: #616161;
  --color-divider:  #dfdfdf;
  --color-primary:  #06c755;
  --color-error:    #ff3a44;
  --color-link:     #4d73ff;
  --radius:         0.5rem;
}

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

### 10-2. Tailwind CSS v3

```js
module.exports = {
  darkMode: 'class',
  theme: {
    extend: {
      colors: {
        bg:        'var(--bg)',
        surface:   'var(--surface)',
        text:      'var(--text)',
        'text-sub':'var(--text-sub)',
        divider:   'var(--divider)',
        primary:   'var(--primary)',
        error:     'var(--semantic-error)',
        link:      'var(--semantic-link)',
      },
      borderRadius: { DEFAULT: '0.5rem' },
    },
  },
};
```

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

CSS 변수가 원천. 다크 전환은 루트에 `.dark` 토글 한 줄로 끝난다.

---

## 11. 이식 가이드

### Step 1 — 토큰 복사

§2의 브랜드/뉴트럴/별칭 3블록 + §7 다크 블록을 전역 CSS에 붙인다.

### Step 2 — 폰트 연결

LINE Seed 웹폰트를 보유했다면 우선 연결, 아니면 Noto Sans KR 폴백 링크만으로 시작한다.

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

| 패턴 | 핵심 CSS |
|---|---|
| **그린 CTA** | `background: #06c755; color: #fff; border-radius: 8px; font-weight: 700` |
| **리스트 셀** | 아바타(pill) + 제목 Bold + 말줄임 서브텍스트 + 우측 메타 |
| **시맨틱 3색** | 성공 `#07b53b` / 오류 `#ff3a44` / 링크 `#4d73ff` — 역할 혼용 금지 |

### Step 4 — 다국어 검증

가장 긴 언어(독일어 등) 기준으로 라벨 오버플로를 확인한다.

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

```
새 컴포넌트가 필요하다
  → 색이 필요한가?
    → 브랜드 액션인가? → 그린
    → 오류/성공/링크인가? → 시맨틱 3색
    → 그 외 → 뉴트럴만 사용
  → 다국어 라벨이 들어가는가?
    yes → 가변폭 + 말줄임 규칙 적용 후 추가
```
