Shopify Polaris
잉크 블랙 액션과 상태 중심 컬러의 커머스 어드민
Brand DNA
브랜드 DNA
- 브랜드명: Shopify Polaris
- 한 줄 정체성: 머천트(상점주)의 일을 자신감 있게 해내도록 돕는 커머스 어드민 시스템
- 공식 디자인 철학: "Build for merchants — empower them to focus on what matters"
- 시그니처 요소 1개: 잉크 블랙(#303030) 핵심 액션과 의미가 있을 때만 쓰는 상태 컬러가 만드는 커머스 어드민 위계
Tone & Mood
톤 & 무드
- 핵심 키워드 3개: 신뢰, 효율, 따뜻함
- 무드 설명: 밝은 중성 캔버스와 선명한 잉크 블랙 액션으로 업무 우선순위를 즉시 드러낸다. 그린·옐로·레드는 브랜드 장식이 아니라 완료·주의·오류처럼 뜻이 분명한 상태에만 사용해 머천트의 판단을 돕는다.
- 비주얼 스타일: 모던 미니멀 + 휴머니즘 (부드러운 모서리, 친근한 안내 문장)
- 밀도(Density): Comfortable — 다양한 숙련도의 머천트를 위해 여유 있는 라인
- 모서리 성향: Soft (8px 기본 컴포넌트, 12px 카드)
- 평면성: Subtle — 옅은 중성 배경 위 surface, 1px border, 최소 그림자의 조합
Color System
컬러 시스템 (CSS 변수)
:root {
/* Primary - Polaris Ink: 핵심 액션과 선택 위계 */
--color-primary-50: #F7F7F7;
--color-primary-100: #F1F1F1;
--color-primary-200: #E3E3E3;
--color-primary-300: #CCCCCC;
--color-primary-400: #8A8A8A;
--color-primary-500: #303030; /* primary action */
--color-primary-600: #252525; /* hover */
--color-primary-700: #1A1A1A; /* active / admin top bar */
--color-primary-800: #111111;
--color-primary-900: #0A0A0A;
/* Secondary - Shopify Green은 완료·성공에 제한 */
--color-secondary-500: #29845A;
/* Neutral - current Polaris admin */
--color-neutral-0: #FFFFFF;
--color-neutral-50: #F7F7F7; /* surface hover */
--color-neutral-100: #F1F1F1; /* page / subdued */
--color-neutral-200: #E3E3E3; /* divider */
--color-neutral-300: #CCCCCC;
--color-neutral-500: #8A8A8A;
--color-neutral-700: #616161; /* subdued text */
--color-neutral-800: #4A4A4A;
--color-neutral-900: #303030; /* primary text */
--color-neutral-1000: #000000;
/* Semantic - 컬러와 레이블/아이콘을 함께 사용 */
--color-success-bg: #CDFEE1;
--color-success-fg: #0C5132;
--color-warning-bg: #FFF1E3;
--color-warning-fg: #5E4200;
--color-error-bg: #FEE9E8;
--color-error-fg: #8E1F0B;
--color-info-bg: #EAF4FF;
--color-info-fg: #005BD3;
/* Surface */
--bg-base: #F1F1F1; /* Shopify Admin page */
--bg-subtle: #F7F7F7;
--bg-elevated: #FFFFFF; /* card */
--bg-overlay: #FFFFFF;
/* Text */
--text-primary: #303030;
--text-secondary: #616161;
--text-tertiary: #8A8A8A;
--text-on-primary: #FFFFFF;
--text-disabled: #B5B5B5;
/* Border */
--border-default: #CCCCCC;
--border-subtle: #E3E3E3;
--border-strong: #8A8A8A;
--border-focus: #005BD3;
}
Typography
타이포그래피
- 폰트 페어링:
- 영문: system-ui / San Francisco / Segoe UI / Roboto (플랫폼 기본 라이선스)
- 한글: Pretendard (OFL) 또는 플랫폼 시스템 고딕 폴백
- 위계 (관리 화면의 높은 정보 밀도 기준):
- Display: 32px / 700 / 1.20 / -0.02em
- H1: 20px / 650 / 1.40 / -0.01em
- H2: 16px / 650 / 1.50 / 0
- H3: 14px / 650 / 1.43 / 0
- Body Large: 15px / 400 / 1.53 / 0
- Body: 13px / 400 / 1.54 / 0
- Body Small: 12px / 400 / 1.50 / 0
- Caption: 11px / 400 / 1.45 / 0.01em
Spacing
스페이싱
- Base unit: 4px
- 토큰: ``
css --space-xs: 4px; --space-sm: 8px; --space-md: 16px; --space-lg: 20px; /* Polaris는 20px도 자주 사용 */ --space-xl: 32px; --space-2xl: 48px; --space-3xl: 64px;`` - Container: max-width 1140px (admin 기본), 좌우 패딩 16px (mobile) / 32px (desktop)
Border Radius
Border Radius
--radius-none: 0;
--radius-sm: 4px;
--radius-md: 8px; /* 버튼, 입력 */
--radius-lg: 12px; /* 카드 */
--radius-xl: 16px;
--radius-full: 9999px;
Shadow / Elevation
Shadow / Elevation
--shadow-none: none;
--shadow-sm: 0 1px 0 rgba(0,0,0,0.05); /* resting card */
--shadow-md: 0 2px 4px rgba(0,0,0,0.10), 0 1px 0 rgba(0,0,0,0.05); /* floating action */
--shadow-lg: 0 8px 20px rgba(0,0,0,0.14); /* popover */
--shadow-xl: 0 16px 40px rgba(0,0,0,0.18); /* modal */
Iconography
Iconography
- 스타일: Outline 중심 + 선택·상태에서만 Filled
- Stroke 굵기: 1.5px (16px 기본) / 2px (20px 강조)
- 모서리 처리: Round
- 추천 라이브러리: Shopify Polaris Icons / Lucide (대체 시 의미와 16px 그리드 유지)
Component Guide
컴포넌트 가이드
Button
.btn {
font: 650 13px/1 -apple-system, BlinkMacSystemFont, "Segoe UI", "Pretendard", sans-serif;
border-radius: var(--radius-md);
padding: 7px 12px;
min-height: 32px;
display: inline-flex; align-items: center; gap: 6px;
border: 1px solid transparent;
justify-content: center;
transition: background var(--duration-fast) var(--ease-out), box-shadow var(--duration-fast) var(--ease-out);
}
.btn-primary { background: var(--color-primary-500); color: var(--text-on-primary); box-shadow: inset 0 1px 0 rgba(255,255,255,0.18), 0 1px 0 rgba(0,0,0,0.20); }
.btn-primary:hover { background: var(--color-primary-600); }
.btn-primary:active { background: var(--color-primary-700); box-shadow: inset 0 1px 2px rgba(0,0,0,0.32); }
.btn:disabled { background: var(--color-neutral-100); color: var(--text-disabled); border-color: var(--border-subtle); box-shadow: none; cursor: not-allowed; }
.btn.is-loading { color: transparent; position: relative; pointer-events: none; }
.btn.is-loading::after { content: ""; position: absolute; width: 12px; height: 12px; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; color: var(--text-on-primary); }
.btn-secondary { background: var(--bg-elevated); color: var(--text-primary); border-color: var(--border-default); box-shadow: var(--shadow-sm); }
.btn-secondary:hover { background: var(--bg-subtle); }
.btn-secondary:active { background: var(--color-neutral-100); box-shadow: inset 0 1px 1px rgba(0,0,0,0.08); }
.btn-ghost { background: transparent; color: var(--text-primary); }
.btn-ghost:hover { background: var(--color-neutral-100); }
.btn-danger { background: var(--color-error-fg); color: #fff; box-shadow: inset 0 1px 0 rgba(255,255,255,0.18); }
Input
.input {
background: var(--bg-elevated);
border: 1px solid var(--border-default);
border-radius: var(--radius-md);
padding: 6px 10px;
height: 32px;
color: var(--text-primary);
font: 400 13px/1.4 inherit;
box-shadow: 0 0 0 1px transparent inset;
}
.input:focus {
outline: none;
border-color: var(--border-focus);
box-shadow: 0 0 0 1px var(--border-focus) inset;
}
.input[aria-invalid="true"] { border-color: var(--color-error-fg); box-shadow: 0 0 0 1px var(--color-error-fg) inset; }
.input:disabled { background: var(--color-neutral-100); color: var(--text-disabled); border-color: var(--border-subtle); }
Card
.card { background: var(--bg-elevated); border-radius: var(--radius-lg); padding: 20px; box-shadow: var(--shadow-sm); border: 1px solid var(--border-subtle); }
.card-emphasized { border-color: var(--border-strong); box-shadow: var(--shadow-md); }
.card-outlined { background: transparent; box-shadow: none; }
Badge
.badge { padding: 2px 8px; border-radius: var(--radius-sm); font-size: 12px; font-weight: 500; display: inline-flex; align-items: center; gap: 4px; }
.badge-solid { background: var(--color-primary-500); color: #fff; }
.badge-subtle { background: var(--color-neutral-100); color: var(--text-primary); }
.badge-outline { border: 1px solid var(--border-default); color: var(--text-primary); }
.badge-success { background: var(--color-success-bg); color: var(--color-success-fg); }
.badge-warning { background: var(--color-warning-bg); color: var(--color-warning-fg); }
Navigation (Admin Shell)
.topbar { height: 44px; background: var(--color-primary-700); color: #F7F7F7; display: flex; align-items: center; padding: 0 12px; gap: 12px; box-shadow: 0 1px 0 rgba(0,0,0,0.28); }
.sidebar { width: 224px; background: #EBEBEB; border-right: 1px solid var(--border-subtle); padding: 12px; }
.nav-item { min-height: 32px; border-radius: var(--radius-md); padding: 6px 8px; color: var(--text-secondary); display: flex; align-items: center; gap: 8px; }
.nav-item:hover, .nav-item[aria-current="page"] { background: #D4D4D4; color: var(--text-primary); }
Motion
Motion
--duration-fast: 100ms;
--duration-base: 200ms;
--duration-slow: 300ms;
--ease-out: cubic-bezier(0.4, 0.22, 0.28, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
Anti-patterns
Anti-patterns
1. 초록색을 기본 CTA나 장식에 사용 금지 — 현재 Polaris에서 초록은 성공·완료 의미에 집중 2. 어드민 화면에 채도 높은 그라데이션 배경 금지 — 데이터와 작업 흐름을 방해 3. 상태를 색만으로 전달 금지 — 배지 문구와 아이콘을 함께 제공 4. 같은 작업 영역에 primary 버튼을 둘 이상 배치 금지 — 행동 위계가 흐려짐 5. 카드마다 강한 그림자를 반복 사용 금지 — 1px border와 surface 위계를 우선