WING-OPS 디자인 시스템
개요
WING-OPS UI 디자인 시스템의 비주얼 레퍼런스 카탈로그.
시맨틱 토큰 기반 다크/라이트 테마 전환을 지원한다.
테마 (Theme)
테마 전환 메커니즘
다크(기본)/라이트 2벌 테마를 CSS 변수 오버라이드 방식으로 지원한다.
[플래시 방지] index.html 인라인 스크립트
↓ localStorage → <html data-theme="dark|light">
[상태 관리] themeStore.ts (Zustand)
↓ toggleTheme() / setTheme()
[CSS 적용] base.css :root (dark) / [data-theme="light"] (light)
↓ CSS 변수 오버라이드
[UI 반영] 모든 컴포넌트가 var(--*) 참조 → 즉시 전환
1단계 — FOUC 방지 (index.html)
<script>
document.documentElement.setAttribute(
'data-theme',
localStorage.getItem('wing-theme') || 'dark'
);
</script>
HTML 파싱 즉시 data-theme 속성을 설정하여 테마 깜빡임을 방지한다.
2단계 — Zustand 스토어 (themeStore.ts)
type ThemeMode = 'dark' | 'light';
interface ThemeState {
theme: ThemeMode;
toggleTheme: () => void; // dark ↔ light 토글
setTheme: (mode: ThemeMode) => void; // 직접 지정
}
- 초기값:
localStorage.getItem('wing-theme') || 'dark'
toggleTheme(): localStorage 갱신 → DOM 속성 변경 → Zustand 상태 갱신
3단계 — CSS 변수 오버라이드 (base.css)
:root — 다크 테마 (기본값)
[data-theme="light"] — 라이트 테마 오버라이드
시맨틱 토큰(--bg-*, --fg-*, --stroke-*)만 테마별 오버라이드하고, 프리미티브 토큰(--gray-*, --blue-* 등)과 액센트 컬러(--color-*)는 테마 간 동일 값을 유지한다.
UI 진입점
TopBar 퀵메뉴에서 toggleTheme() 호출로 전환.
Foundations
색상 (Color Palette)
토큰 아키텍처
Primitive Tokens (정적) Semantic Tokens (테마 반응형)
────────────────────── ────────────────────────────
--gray-100 ~ --gray-1000 --bg-base, --bg-surface, ...
--blue-100 ~ --blue-1000 --fg-default, --fg-sub, ...
--red-100 ~ --red-1000 --stroke-default, --stroke-light
--green-100 ~ --green-1000 --hover-overlay, --dropdown-bg
--yellow-100 ~ --yellow-1000
--purple-100 ~ --purple-1000
Accent Tokens (테마 불변)
─────────────────────────
--color-accent, --color-info, ...
Semantic Colors — Background
| CSS 변수 |
Tailwind 클래스 |
Dark |
Light |
용도 |
--bg-base |
bg-bg-base |
#0a0e1a |
#f8fafc |
페이지 배경 |
--bg-surface |
bg-bg-surface |
#0f1524 |
#ffffff |
사이드바, 패널 |
--bg-elevated |
bg-bg-elevated |
#121929 |
#f1f5f9 |
테이블 헤더, 상위 요소 |
--bg-card |
bg-bg-card |
#1a2236 |
#ffffff |
카드 배경 |
--bg-surface-hover |
bg-bg-surface-hover |
#1e2844 |
#e2e8f0 |
호버 상태 |
Semantic Colors — Foreground (Text)
| CSS 변수 |
Tailwind 클래스 |
Dark |
Light |
용도 |
--fg-default |
text-fg |
#edf0f7 |
#0f172a |
기본 텍스트, 아이콘 |
--fg-sub |
text-fg-sub |
#c0c8dc |
#475569 |
보조 텍스트 |
--fg-disabled |
text-fg-disabled |
#9ba3b8 |
#94a3b8 |
비활성, 플레이스홀더 |
Semantic Colors — Border (Stroke)
| CSS 변수 |
Tailwind 클래스 |
Dark |
Light |
용도 |
--stroke-default |
border-stroke |
#1e2a42 |
#cbd5e1 |
기본 구분선 |
--stroke-light |
border-stroke-light |
#2a3a5c |
#e2e8f0 |
연한 구분선 |
Semantic Colors — Overlay
| CSS 변수 |
Dark |
Light |
용도 |
--hover-overlay |
rgba(255,255,255,0.06) |
rgba(0,0,0,0.04) |
호버 오버레이 |
--dropdown-bg |
rgba(18,25,41,0.97) |
rgba(255,255,255,0.97) |
드롭다운 배경 |
Accent Colors (테마 불변)
| CSS 변수 |
Tailwind 클래스 |
Hex |
용도 |
--color-accent |
text-color-accent |
#06b6d4 |
주요 강조 (Cyan) |
--color-info |
text-color-info |
#3b82f6 |
정보, 링크 (Blue) |
--color-tertiary |
text-color-tertiary |
#a855f7 |
3차 강조 (Purple) |
--color-danger |
text-color-danger |
#ef4444 |
위험, 삭제 (Red) |
--color-warning |
text-color-warning |
#f97316 |
주의 (Orange) |
--color-caution |
text-color-caution |
#eab308 |
경고 (Yellow) |
--color-success |
text-color-success |
#22c55e |
성공, 정상 (Green) |
--color-boom |
text-color-boom |
#f59e0b |
오일붐 전용 (Amber) |
--color-boom-hover |
— |
#fbbf24 |
오일붐 호버 |
Static Colors
| CSS 변수 |
Hex |
용도 |
--static-black |
#131415 |
테마 무관 고정 검정 |
--static-white |
#ffffff |
테마 무관 고정 흰색 |
Primitive Colors
UI 전반에서 직접 참조하거나 시맨틱 토큰의 원천으로 사용하는 기본 팔레트. 100~1000 (10단계).
Gray
| 100 |
200 |
300 |
400 |
500 |
600 |
700 |
800 |
900 |
1000 |
#f1f5f9 |
#e2e8f0 |
#cbd5e1 |
#94a3b8 |
#64748b |
#475569 |
#334155 |
#1e293b |
#0f172a |
#020617 |
Blue
| 100 |
200 |
300 |
400 |
500 |
600 |
700 |
800 |
900 |
1000 |
#dbeafe |
#bfdbfe |
#93c5fd |
#60a5fa |
#3b82f6 |
#2563eb |
#1d4ed8 |
#1e40af |
#1e3a8a |
#172554 |
Green
| 100 |
200 |
300 |
400 |
500 |
600 |
700 |
800 |
900 |
1000 |
#dcfce7 |
#bbf7d0 |
#86efac |
#4ade80 |
#22c55e |
#16a34a |
#15803d |
#166534 |
#14532d |
#052e16 |
Yellow
| 100 |
200 |
300 |
400 |
500 |
600 |
700 |
800 |
900 |
1000 |
#fef9c3 |
#fef08a |
#fde047 |
#facc15 |
#eab308 |
#ca8a04 |
#a16207 |
#854d0e |
#713f12 |
#422006 |
Red
| 100 |
200 |
300 |
400 |
500 |
600 |
700 |
800 |
900 |
1000 |
#fee2e2 |
#fecaca |
#fca5a5 |
#f87171 |
#ef4444 |
#dc2626 |
#b91c1c |
#991b1b |
#7f1d1d |
#450a0a |
Purple
| 100 |
200 |
300 |
400 |
500 |
600 |
700 |
800 |
900 |
1000 |
#f3e8ff |
#e9d5ff |
#d8b4fe |
#c084fc |
#a855f7 |
#9333ea |
#7e22ce |
#6b21a8 |
#581c87 |
#3b0764 |
타이포그래피 (Typography)
Font Family
| CSS 변수 |
Tailwind 클래스 |
용도 |
--font-korean |
font-korean |
기본 UI 텍스트, 한국어/영문 콘텐츠 |
--font-mono |
font-mono |
좌표, 수치, 데이터 값 |
| — |
font-sans |
body 기본 (PretendardGOV) |
모든 폰트 패밀리가 PretendardGOV 우선 스택으로 통일.
@font-face: Regular(400), Medium(500), SemiBold(600), Bold(700) — /fonts/PretendardGOV-*.otf
Typography Categories
5가지 용도 카테고리로 타이포그래피를 구성합니다.
| 카테고리 |
토큰 |
설명 |
| Display |
Display 1, Display 2, Display 3 |
배너, 마케팅 등 최대 크기 텍스트 |
| Heading |
Heading 1, Heading 2, Heading 3 |
페이지/모듈 단위 제목, 계층 설정 |
| Body |
Body 1, Body 2, Caption |
본문/콘텐츠 텍스트 |
| Navigation |
Title 1~6 |
사이트 내 이정표 역할 (패널 제목, 탭 버튼, 메뉴 항목 등) |
| Label |
Label 1, Label 2 |
컴포넌트 label, placeholder, 버튼 텍스트 |
Navigation 카테고리의 토큰은 CSS 변수명 --font-size-title-* / Tailwind text-title-*을 유지합니다.
Font Size Tokens
CSS 변수와 Tailwind 유틸리티가 1:1 매핑된 타이포그래피 스케일. text-* 클래스 사용 시 font-size, line-height, letter-spacing이 함께 적용됩니다.
Display — 배너, 마케팅, 랜딩 영역
| CSS 변수 |
Tailwind |
px |
Weight |
Line-H |
Spacing |
--font-size-display-1 |
text-display-1 |
60px |
700 |
1.3 |
0.06em |
--font-size-display-2 |
text-display-2 |
40px |
700 |
1.3 |
0.06em |
--font-size-display-3 |
text-display-3 |
36px |
500 |
1.4 |
0.06em |
Heading — 페이지/모듈 제목
| CSS 변수 |
Tailwind |
px |
Weight |
Line-H |
Spacing |
--font-size-heading-1 |
text-heading-1 |
32px |
700 |
1.4 |
0.02em |
--font-size-heading-2 |
text-heading-2 |
24px |
700 |
1.4 |
0.02em |
--font-size-heading-3 |
text-heading-3 |
22px |
500 |
1.4 |
0.02em |
Body — 본문/콘텐츠
| CSS 변수 |
Tailwind |
px |
Weight |
Line-H |
Spacing |
--font-size-body-1 |
text-body-1 |
14px |
400 |
1.6 |
0em |
--font-size-body-2 |
text-body-2 |
13px |
400 |
1.6 |
0em |
--font-size-caption |
text-caption |
11px |
400 |
1.5 |
0em |
Navigation — 패널 제목, 탭 버튼, 메뉴 항목, 소형 네비게이션
| CSS 변수 |
Tailwind |
px |
Weight |
Line-H |
Spacing |
--font-size-title-1 |
text-title-1 |
18px |
700 |
1.5 |
0.02em |
--font-size-title-2 |
text-title-2 |
16px |
500 |
1.5 |
0.02em |
--font-size-title-3 |
text-title-3 |
14px |
500 |
1.5 |
0.02em |
--font-size-title-4 |
text-title-4 |
13px |
500 |
1.5 |
0.02em |
--font-size-title-5 |
text-title-5 |
12px |
500 |
1.5 |
0.02em |
--font-size-title-6 |
text-title-6 |
11px |
500 |
1.5 |
0.02em |
Label — 레이블, 플레이스홀더, 버튼
| CSS 변수 |
Tailwind |
px |
Weight |
Line-H |
Spacing |
--font-size-label-1 |
text-label-1 |
12px |
500 |
1.5 |
0.04em |
--font-size-label-2 |
text-label-2 |
11px |
500 |
1.5 |
0.04em |
Font Weight Tokens
| CSS 변수 |
값 |
용도 |
--font-weight-thin |
300 |
얇은 텍스트 |
--font-weight-regular |
400 |
본문 기본 |
--font-weight-medium |
500 |
중간 강조 |
--font-weight-bold |
700 |
제목, 강조 |
Line Height Tokens
| CSS 변수 |
값 |
용도 |
--line-height-tight |
1.3 |
Display |
--line-height-snug |
1.4 |
Heading |
--line-height-normal |
1.5 |
Navigation, Label, Caption |
--line-height-relaxed |
1.6 |
Body |
Letter Spacing Tokens
카테고리별 자간 토큰. text-* 클래스에 자동 포함되며, tracking-* 클래스로 개별 사용도 가능합니다.
| CSS 변수 |
Tailwind |
값 |
카테고리 |
--letter-spacing-display |
tracking-display |
0.06em |
Display |
--letter-spacing-heading |
tracking-heading |
0.02em |
Heading |
--letter-spacing-body |
tracking-body |
0em |
Body |
--letter-spacing-navigation |
tracking-navigation |
0.02em |
Navigation |
--letter-spacing-label |
tracking-label |
0.04em |
Label |
Typography Tokens (.wing-* 클래스)
| 클래스 |
Size |
Font |
Weight |
용도 |
샘플 |
.wing-title |
15px |
font-korean |
Bold (700) |
패널 제목 |
확산 예측 시뮬레이션 |
.wing-section-header |
13px |
font-korean |
Bold (700) |
섹션 헤더 |
기본 정보 입력 |
.wing-label |
11px |
font-korean |
Semibold (600) |
필드 레이블 |
유출량 (kL) |
.wing-btn |
11px |
font-korean |
Semibold (600) |
버튼 텍스트 |
시뮬레이션 실행 |
.wing-value |
11px |
font-mono |
Semibold (600) |
수치 / 데이터 값 |
35.1284° N, 129.0598° E |
.wing-input |
11px |
font-korean |
Normal (400) |
입력 필드 |
서해 대산항 인근 해역 |
.wing-section-desc |
10px |
font-korean |
Normal (400) |
섹션 설명 |
예측 결과는 기상 조건에 따라... |
.wing-subtitle |
10px |
font-korean |
Normal (400) |
보조 설명 |
최근 업데이트: 2026-03-24 09:00 KST |
.wing-meta |
9px |
font-korean |
Normal (400) |
메타 정보 |
v2.1 | 해양환경공단 |
.wing-badge |
9px |
font-korean |
Bold (700) |
뱃지 / 태그 |
진행중 |
Border Radius
Radius Tokens
| Tailwind 클래스 |
값 |
비고 |
rounded-sm |
6px |
Custom (Tailwind 기본값 오버라이드) |
rounded |
4px (0.25rem) |
Tailwind 기본 |
rounded-md |
10px |
Custom (Tailwind 기본값 오버라이드) |
rounded-lg |
8px (0.5rem) |
Tailwind 기본 |
rounded-xl |
12px (0.75rem) |
Tailwind 기본 |
rounded-2xl |
16px (1rem) |
Tailwind 기본 |
rounded-full |
9999px |
Tailwind 기본 |
컴포넌트 매핑
| Radius |
값 |
적용 컴포넌트 |
rounded-sm |
6px |
.wing-btn, .wing-input, .wing-card-sm |
rounded |
4px |
.wing-badge |
rounded-md |
10px |
.wing-card, .wing-section, .wing-tab |
rounded-lg |
8px |
.wing-tab-bar |
rounded-xl |
12px |
.wing-modal |
레이아웃 (Layout)
Breakpoints
| Name |
Prefix |
Min Width |
사용 |
비고 |
| sm |
sm: |
640px |
- |
|
| md |
md: |
768px |
- |
|
| lg |
lg: |
1024px |
- |
|
| xl |
xl: |
1280px |
사용 중 |
TopBar 탭 레이블/아이콘 토글 |
| 2xl |
2xl: |
1536px |
- |
|
Desktop(≥ 1280px)만 지원. Tablet/Mobile 미지원.
| Device |
Width |
Columns |
Gutter |
Margin |
| Desktop |
≥ 1280px |
flex 기반 가변 |
gap-2 ~ gap-6 |
px-5 ~ px-8 |
| Tablet |
768px ~ 1279px |
- |
- |
- |
| Mobile |
< 768px |
- |
- |
- |
Spacing Scale
| Scale |
rem |
px |
용도 |
| 0.5 |
0.125rem |
2px |
미세 간격 |
| 1 |
0.25rem |
4px |
최소 간격 (gap-1) |
| 1.5 |
0.375rem |
6px |
컴팩트 간격 (gap-1.5) |
| 2 |
0.5rem |
8px |
기본 간격 (gap-2, p-2) |
| 2.5 |
0.625rem |
10px |
중간 간격 |
| 3 |
0.75rem |
12px |
표준 간격 (gap-3, p-3) |
| 4 |
1rem |
16px |
넓은 간격 (p-4, gap-4) |
| 5 |
1.25rem |
20px |
패널 패딩 (px-5, py-5) |
| 6 |
1.5rem |
24px |
섹션 간격 (gap-6, p-6) |
| 8 |
2rem |
32px |
큰 간격 (px-8, gap-8) |
| 16 |
4rem |
64px |
최대 간격 |
Z-Index Layers
| Layer |
z-index |
Color |
설명 |
| Tooltip |
60 |
#a855f7 |
툴팁, 드롭다운 메뉴 |
| Popup |
50 |
#f97316 |
팝업, 지도 오버레이 |
| Modal |
40 |
#ef4444 |
모달 다이얼로그, 백드롭 |
| TopBar |
30 |
#3b82f6 |
상단 네비게이션 바 |
| Sidebar |
20 |
#06b6d4 |
사이드바, 패널 |
| Content |
10 |
#22c55e |
메인 콘텐츠 영역 |
| Base |
0 |
#8690a6 |
기본 레이어, 배경 |
App Shell Classes
| 클래스 |
역할 |
Tailwind 스타일 |
.wing-panel |
탭 콘텐츠 패널 |
flex flex-col h-full overflow-hidden |
.wing-panel-scroll |
패널 내 스크롤 영역 |
flex-1 overflow-y-auto |
.wing-header-bar |
패널 헤더 |
flex items-center justify-between shrink-0 px-5 border-b |
.wing-sidebar |
사이드바 |
flex flex-col border-r border-border |
CSS 레이어 아키텍처
index.css
├── @import base.css → @layer base (CSS 변수, reset, body, @font-face)
├── @import components.css → @layer components (MapLibre, scrollbar, prd-*, combo-*)
├── @import wing.css → @layer components (wing-* 디자인 시스템 클래스)
├── @tailwind base
├── @tailwind components
└── @tailwind utilities
Tailwind 시맨틱 토큰 매핑 요약
| 카테고리 |
CSS 변수 |
Tailwind 클래스 예시 |
| Background |
--bg-base ~ --bg-surface-hover |
bg-bg-base, bg-bg-surface, ... |
| Foreground |
--fg-default, --fg-sub, --fg-disabled |
text-fg, text-fg-sub, text-fg-disabled |
| Border |
--stroke-default, --stroke-light |
border-stroke, border-stroke-light |
| Accent |
--color-accent ~ --color-success |
text-color-accent, bg-color-info, ... |
| Font Size |
--font-size-display-1 ~ --font-size-caption |
text-display-1, text-body-1, ... |
| Font Family |
--font-korean, --font-mono |
font-korean, font-mono, font-sans |