diff --git a/app/assets/fonts/42dotSans[wght].ttf b/app/assets/fonts/42dotSans[wght].ttf new file mode 100644 index 0000000..33a49a8 Binary files /dev/null and b/app/assets/fonts/42dotSans[wght].ttf differ diff --git a/app/assets/fonts/ABeeZee-Regular.ttf b/app/assets/fonts/ABeeZee-Regular.ttf new file mode 100644 index 0000000..2ecf01b Binary files /dev/null and b/app/assets/fonts/ABeeZee-Regular.ttf differ diff --git a/app/lib/core/theme/tokens.dart b/app/lib/core/theme/tokens.dart index 2aed64a..97abf8f 100644 --- a/app/lib/core/theme/tokens.dart +++ b/app/lib/core/theme/tokens.dart @@ -6,70 +6,74 @@ import 'dart:ui' show Color; /// 컬러 토큰. app 은 cosmic* 계열이 기본 서피스다(결정 D-19). abstract final class AppColors { - static const primary = Color(0xFFAC5992); // color-primary #ac5992 + static const primary = Color(0xFFB93074); // color-primary #b93074 static const onPrimary = Color(0xFFFFFFFF); // color-on-primary #ffffff - static const primaryContainer = Color(0xFFF0E1EB); // color-primary-container #f0e1eb - static const onPrimaryContainer = Color(0xFF924C7C); // color-on-primary-container #924c7c - static const surface = Color(0xFFF8F5FA); // color-surface #f8f5fa - static const surfaceContainer = Color(0xFFEBE7ED); // color-surface-container #ebe7ed - static const onSurface = Color(0xFF1A1C1E); // color-on-surface #1a1c1e - static const onSurfaceVariant = Color(0xFF4B4753); // color-on-surface-variant #4b4753 - static const outline = Color(0xFF7A7481); // color-outline #7a7481 + static const primaryContainer = Color(0xFFFFE3F3); // color-primary-container #ffe3f3 + static const onPrimaryContainer = Color(0xFF8A2E62); // color-on-primary-container #8a2e62 + static const surface = Color(0xFFFAF7F9); // color-surface #faf7f9 + static const surfaceContainer = Color(0xFFF0EAEE); // color-surface-container #f0eaee + static const onSurface = Color(0xFF201826); // color-on-surface #201826 + static const onSurfaceVariant = Color(0xFF55495A); // color-on-surface-variant #55495a + static const outline = Color(0xFF7D7383); // color-outline #7d7383 static const error = Color(0xFFC53030); // color-error #c53030 static const onError = Color(0xFFFFFFFF); // color-on-error #ffffff static const webBackground = Color(0xFFFFFFFF); // web-background #ffffff - static const webForeground = Color(0xFF1A1C1E); // web-foreground #1a1c1e - static const webPrimary = Color(0xFFAC5992); // web-primary #ac5992 + static const webForeground = Color(0xFF201826); // web-foreground #201826 + static const webPrimary = Color(0xFFB93074); // web-primary #b93074 static const webPrimaryForeground = Color(0xFFFFFFFF); // web-primary-foreground #ffffff - static const webMuted = Color(0xFFF1EEF4); // web-muted #f1eef4 - static const webMutedForeground = Color(0xFF4B4753); // web-muted-foreground #4b4753 - static const webBorder = Color(0xFFDED7DF); // web-border #ded7df - static const cosmicVoid = Color(0xFF000000); // color-cosmic-void #000000 - static const cosmicSurface = Color(0xFF05070F); // color-cosmic-surface #05070F - static const cosmicSurfaceContainer = Color(0xFF0D1220); // color-cosmic-surface-container #0D1220 - static const cosmicSurfaceRaised = Color(0xFF141A2B); // color-cosmic-surface-raised #141A2B - static const onCosmic = Color(0xFFF4F0F7); // color-on-cosmic #F4F0F7 - static const onCosmicVariant = Color(0xFFB4AEC0); // color-on-cosmic-variant #B4AEC0 - static const cosmicAccent = Color(0xFFFF85D9); // color-cosmic-accent #FF85D9 - static const onCosmicAccent = Color(0xFF05070F); // color-on-cosmic-accent #05070F - static const neonCyan = Color(0xFF00F2FF); // color-neon-cyan #00F2FF - static const onNeonCyan = Color(0xFF05070F); // color-on-neon-cyan #05070F - static const neonEmerald = Color(0xFF34D399); // color-neon-emerald #34D399 - static const onNeonEmerald = Color(0xFF05070F); // color-on-neon-emerald #05070F - static const neonMagenta = Color(0xFFFF007A); // color-neon-magenta #FF007A - static const onNeonMagenta = Color(0xFF05070F); // color-on-neon-magenta #05070F - static const neonViolet = Color(0xFFA855F7); // color-neon-violet #A855F7 - static const onNeonViolet = Color(0xFF05070F); // color-on-neon-violet #05070F - static const cosmicMint = Color(0xFFC1FFED); // color-cosmic-mint #C1FFED - static const onCosmicMint = Color(0xFF05070F); // color-on-cosmic-mint #05070F - static const cosmicOutline = Color(0xFF2A3348); // color-cosmic-outline #2A3348 + static const webMuted = Color(0xFFF6F0F4); // web-muted #f6f0f4 + static const webMutedForeground = Color(0xFF6E6878); // web-muted-foreground #6e6878 + static const webBorder = Color(0xFFE9E2E8); // web-border #e9e2e8 + static const cosmicVoid = Color(0xFF141414); // color-cosmic-void #141414 + static const cosmicSurface = Color(0xFF101010); // color-cosmic-surface #101010 + static const cosmicSurfaceContainer = Color(0xFF1A1A1C); // color-cosmic-surface-container #1A1A1C + static const cosmicSurfaceRaised = Color(0xFF232324); // color-cosmic-surface-raised #232324 + static const onCosmic = Color(0xFFFFFFFF); // color-on-cosmic #FFFFFF + static const onCosmicVariant = Color(0xFFA3A2A0); // color-on-cosmic-variant #A3A2A0 + static const onCosmicMuted = Color(0xFF767676); // color-on-cosmic-muted #767676 + static const cosmicAccent = Color(0xFFFF7DDD); // color-cosmic-accent #FF7DDD + static const onCosmicAccent = Color(0xFF141414); // color-on-cosmic-accent #141414 + static const neonCyan = Color(0xFF64EFF9); // color-neon-cyan #64EFF9 + static const onNeonCyan = Color(0xFF141414); // color-on-neon-cyan #141414 + static const neonEmerald = Color(0xFF00C60C); // color-neon-emerald #00C60C + static const onNeonEmerald = Color(0xFF141414); // color-on-neon-emerald #141414 + static const neonMagenta = Color(0xFFFF4141); // color-neon-magenta #FF4141 + static const onNeonMagenta = Color(0xFFFFFFFF); // color-on-neon-magenta #FFFFFF + static const neonViolet = Color(0xFF6866F3); // color-neon-violet #6866F3 + static const onNeonViolet = Color(0xFFFFFFFF); // color-on-neon-violet #FFFFFF + static const cosmicMint = Color(0xFF56CD9E); // color-cosmic-mint #56CD9E + static const onCosmicMint = Color(0xFF141414); // color-on-cosmic-mint #141414 + static const cosmicBlue = Color(0xFF52C1F9); // color-cosmic-blue #52C1F9 + static const onCosmicBlue = Color(0xFF141414); // color-on-cosmic-blue #141414 + static const cosmicOrange = Color(0xFFE4975D); // color-cosmic-orange #E4975D + static const onCosmicOrange = Color(0xFF141414); // color-on-cosmic-orange #141414 + static const cosmicOutline = Color(0xFF2C2C2E); // color-cosmic-outline #2C2C2E + static const gold = Color(0xFFFFD34D); // color-gold #FFD34D + static const onGold = Color(0xFF241A02); // color-on-gold #241A02 } /// 소셜 제공자 브랜드 색. 각 사 가이드라인 고정값이라 테마와 무관하게 그대로 쓴다. abstract final class BrandColors { static const kakao = Color(0xFFFEE500); // brand-kakao #FEE500 static const kakaoLabel = Color(0xFF000000); // brand-kakao-label #000000 - // 네이버는 확정 가입 수단 5종에서 빠졌다(정책 §P-01 · 답변서 Q7). 토큰은 - // 남겨 둔다 — 발주사께서 다시 넣으시면 그대로 쓴다. static const naver = Color(0xFF03C75A); // brand-naver #03C75A static const naverLabel = Color(0xFFFFFFFF); // brand-naver-label #FFFFFF - static const line = Color(0xFF06C755); // brand-line #06C755 - static const lineLabel = Color(0xFFFFFFFF); // brand-line-label #FFFFFF - // 이메일은 브랜드가 없다. 앱 강조색을 그대로 쓴다. - static const email = AppColors.cosmicAccent; - static const emailLabel = AppColors.onCosmicAccent; static const apple = Color(0xFF000000); // brand-apple #000000 static const appleLabel = Color(0xFFFFFFFF); // brand-apple-label #FFFFFF static const appleOnlight = Color(0xFFFFFFFF); // brand-apple-onlight #FFFFFF static const googleFill = Color(0xFFFFFFFF); // brand-google-fill #FFFFFF static const googleStroke = Color(0xFF747775); // brand-google-stroke #747775 static const googleLabel = Color(0xFF1F1F1F); // brand-google-label #1F1F1F + static const line = Color(0xFF06C755); // brand-line #06C755 + static const lineLabel = Color(0xFFFFFFFF); // brand-line-label #FFFFFF + static const email = Color(0xFFFF7DDD); // brand-email #FF7DDD + static const emailLabel = Color(0xFF141414); // brand-email-label #141414 } /// 글래스 FX. CSS 전용 표현(glow-*/grad-*/nebula/starfield)은 생성하지 않는다 — gen_tokens.py 헤더 참고. abstract final class AppFx { - static const glassFill = Color(0x0DFFFFFF); // glass-fill rgba(255,255,255,.05) - static const glassFillStrong = Color(0x14FFFFFF); // glass-fill-strong rgba(255,255,255,.08) + static const glassFill = Color(0x0FFFFFFF); // glass-fill rgba(255,255,255,.06) + static const glassFillStrong = Color(0x17FFFFFF); // glass-fill-strong rgba(255,255,255,.09) static const glassStroke = Color(0x1AFFFFFF); // glass-stroke rgba(255,255,255,.10) static const glassBlur = 16.0; // glass-blur 16px static const orbitStroke = Color(0x1AFFFFFF); // orbit-stroke rgba(255,255,255,.10) @@ -98,10 +102,10 @@ abstract final class AppSpace { /// 코너 반경. abstract final class AppRadius { - static const xs = 8.0; // corner-xs - static const sm = 10.0; // corner-sm - static const md = 14.0; // corner-md - static const lg = 18.0; // corner-lg + static const xs = 4.0; // corner-xs + static const sm = 8.0; // corner-sm + static const md = 12.0; // corner-md + static const lg = 16.0; // corner-lg static const full = 9999.0; // corner-full } @@ -110,7 +114,11 @@ abstract final class AppSizing { static const touchMinApp = 48.0; // touch-min-app static const touchMinWeb = 36.0; // touch-min-web static const bodyMinApp = 16.0; // body-min-app - static const bodyMinWeb = 14.0; // body-min-web + static const bodyMinWeb = 16.0; // body-min-web + static const uiMinDesktop = 14.0; // ui-min-desktop + static const uiMinMobile = 16.0; // ui-min-mobile + static const metaMinDesktop = 12.0; // meta-min-desktop + static const metaMinMobile = 13.0; // meta-min-mobile } /// 오버레이 순서. @@ -135,23 +143,25 @@ abstract final class AppMotion { /// 폰트 패밀리명. 폰트 파일은 리포에 없다 — 미투입 시 시스템 폰트로 폴백된다(README 「미투입 항목」). abstract final class AppFonts { - static const display = 'Gmarket Sans'; // font-display = 'Gmarket Sans', Pretendard Variable, sans-serif - static const body = 'Pretendard Variable'; // font-body = Pretendard Variable, system-ui, sans-serif - static const mono = 'JetBrains Mono'; // font-mono = JetBrains Mono, IBM Plex Mono, ui-monospace, monospace - // font-source: Gmarket Sans = webfontworld/jsdelivr · 본문 Pretendard = jsdelivr + static const display = 'ABeeZee'; // font-display = 'ABeeZee', '42dot Sans', sans-serif + static const body = '42dot Sans'; // font-body = '42dot Sans', 'Pretendard Variable', system-ui, sans-serif + static const mono = 'ABeeZee'; // font-mono = 'ABeeZee', ui-monospace, monospace + // font-source: 42dot Sans = Google Fonts OFL 가변 ttf · ABeeZee = Google Fonts OFL (app/assets/fonts/, 2026-09-02) } /// 타입 스케일. 값은 design-tokens.md 의 type 블록 그대로다. abstract final class AppType { static const displayL = TypeToken(family: AppFonts.display, size: 44.0, weight: 700, height: 1.06, letterSpacing: -0.44); // type-display-l - static const displayM = TypeToken(family: AppFonts.display, size: 32.0, weight: 700, height: 1.11, letterSpacing: -0.32); // type-display-m - static const titleL = TypeToken(family: AppFonts.display, size: 24.0, weight: 700, height: 1.2, letterSpacing: -0.24); // type-title-l + static const displayM = TypeToken(family: AppFonts.display, size: 32.0, weight: 700, height: 1.2, letterSpacing: -0.224); // type-display-m + static const titleL = TypeToken(family: AppFonts.display, size: 24.0, weight: 700, height: 1.3, letterSpacing: 0.0); // type-title-l static const titleM = TypeToken(family: AppFonts.body, size: 18.0, weight: 600, height: 1.3); // type-title-m static const bodyL = TypeToken(family: AppFonts.body, size: 18.0, weight: 400, height: 1.6); // type-body-l static const bodyM = TypeToken(family: AppFonts.body, size: 16.0, weight: 400, height: 1.6); // type-body-m static const bodyS = TypeToken(family: AppFonts.body, size: 14.0, weight: 400, height: 1.55); // type-body-s static const labelL = TypeToken(family: AppFonts.body, size: 14.0, weight: 500, height: 1.2); // type-label-l - static const eyebrow = TypeToken(family: AppFonts.body, size: 12.0, weight: 600, letterSpacing: 0.72); // type-eyebrow + static const labelMobile = TypeToken(family: AppFonts.body, size: 16.0, weight: 700, height: 1.4); // type-label-mobile + static const metaMobile = TypeToken(family: AppFonts.body, size: 13.0, weight: 600, height: 1.4); // type-meta-mobile + static const eyebrow = TypeToken(family: AppFonts.body, size: 12.0, weight: 600, letterSpacing: 0.48); // type-eyebrow static const caption = TypeToken(family: AppFonts.body, size: 12.0, weight: 500, height: 1.4); // type-caption // type-mono-num: {'family': 'font-mono', 'feature': 'tnum', 'note': '가격·지표·표는 tabular'} (크기 없는 지시자 — TextStyle 로 못 옮긴다) } @@ -173,4 +183,4 @@ class TypeToken { final double? letterSpacing; } -// 생성 제외된 cosmic_fx: glow-sm, glow-md, glow-lg, glow-accent, glow-cyan, glow-emerald, glow-magenta, grad-action, grad-brand, grad-eclipse, grad-veil, nebula, starfield +// 생성 제외된 cosmic_fx: glow-live, grad-action, grad-brand, grad-gold, grad-eclipse, grad-veil, nebula, starfield, foil diff --git a/app/pubspec.yaml b/app/pubspec.yaml index b421042..57511ed 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -64,22 +64,12 @@ flutter_launcher_icons: flutter: uses-material-design: true - # 폰트 파일은 리포에 없다(라이선스 원본 미투입). 투입 위치만 표시해 둔다. - # 투입하면 아래 주석을 풀고 `flutter pub get` 만 하면 된다 — 코드는 이미 - # AppFonts.display / AppFonts.body 를 참조하고 있고, 미투입 상태에서는 시스템 폰트로 폴백된다. - # - # fonts: - # - family: Gmarket Sans # 제목 · 출처 webfontworld/gmarket - # fonts: - # - asset: assets/fonts/GmarketSansTTFLight.ttf - # weight: 300 - # - asset: assets/fonts/GmarketSansTTFMedium.ttf - # weight: 500 - # - asset: assets/fonts/GmarketSansTTFBold.ttf - # weight: 700 - # - family: Pretendard Variable # 본문 · 출처 orioncactus/pretendard - # fonts: - # - asset: assets/fonts/PretendardVariable.ttf - # - family: JetBrains Mono # 수치(POP·FPI) tabular - # fonts: - # - asset: assets/fonts/JetBrainsMono-Regular.ttf + # 발주사 Figma 실측 폰트 (2026-09-02). 42dot Sans 가 본문·라벨 전량, ABeeZee 가 숫자·영문 표시. + # 둘 다 Google Fonts OFL 이라 동봉한다. design/design-tokens.md 의 font-* 와 짝이다. + fonts: + - family: 42dot Sans + fonts: + - asset: assets/fonts/42dotSans[wght].ttf + - family: ABeeZee + fonts: + - asset: assets/fonts/ABeeZee-Regular.ttf diff --git a/app/test/theme_coverage_test.dart b/app/test/theme_coverage_test.dart index 1c3b46c..a862589 100644 --- a/app/test/theme_coverage_test.dart +++ b/app/test/theme_coverage_test.dart @@ -14,7 +14,8 @@ void main() { /// 코스믹 팔레트에 속한 색인가. 토큰 밖의 색이면 Material 이 만들어 낸 것이다. bool isCosmic(Color? c) { if (c == null) return false; - final palette = { + // 값이 같은 토큰이 있어도 되도록 리스트에서 집합을 만든다. + final palette = [ AppColors.cosmicVoid, AppColors.cosmicSurface, AppColors.cosmicSurfaceContainer, @@ -27,7 +28,7 @@ void main() { AppColors.neonCyan, AppColors.neonViolet, AppColors.error, - }; + ].toSet(); return palette.contains(c) || c == Colors.transparent; } diff --git a/app/tool/gen_tokens.py b/app/tool/gen_tokens.py index eb39546..529747b 100644 --- a/app/tool/gen_tokens.py +++ b/app/tool/gen_tokens.py @@ -25,7 +25,7 @@ ROOT = Path(__file__).resolve().parent.parent # app/ TOKENS_MD = ROOT.parent / "design" / "design-tokens.md" OUT = ROOT / "lib" / "core" / "theme" / "tokens.dart" -SKIP_FX = re.compile(r"^(glow-|grad-|nebula$|starfield$)") +SKIP_FX = re.compile(r"^(glow-|grad-|nebula$|starfield$|foil)") def load_tokens() -> dict: diff --git a/design/design-tokens.md b/design/design-tokens.md index 1a567a3..c3391b3 100644 --- a/design/design-tokens.md +++ b/design/design-tokens.md @@ -92,29 +92,35 @@ colors: web-muted: "#f6f0f4" web-muted-foreground: "#6e6878" web-border: "#e9e2e8" - # --- LED 스테이지 (전 surface 기본 · 2026-08-17 「포카 온 스테이지」 재설계) --- - # 보랏빛 웜 블랙을 폐기하고 꺼진 LED 패널의 중성 콜드 블랙으로 통일. 흰 포토카드가 서는 바닥. - color-cosmic-void: "#050508" - color-cosmic-surface: "#0A0A0E" - color-cosmic-surface-container: "#101016" - color-cosmic-surface-raised: "#16161D" - color-on-cosmic: "#F4F4F6" - color-on-cosmic-variant: "#A6A4B0" - # 악센트 — 공식 로고 실측 핑크 하나. 시안·에메랄드·마젠타는 상태 의미(실시간·온에어·이클립스) 전용. - # 바이올렛은 홀로 라일락(포일 스펙트럼 추출색)으로 재정의, 골드는 전광판 앰버(신호색)로 전환. - color-cosmic-accent: "#FF98D7" - color-on-cosmic-accent: "#2B0E1F" - color-neon-cyan: "#6FE8FF" - color-on-neon-cyan: "#05070F" - color-neon-emerald: "#3DDC97" - color-on-neon-emerald: "#05070F" - color-neon-magenta: "#FF2E7E" - color-on-neon-magenta: "#05070F" - color-neon-violet: "#C9A7FF" - color-on-neon-violet: "#05070F" - color-cosmic-mint: "#A9FFE0" - color-on-cosmic-mint: "#05070F" - color-cosmic-outline: "#26262E" + # --- 코스믹 서피스 · 발주사 Figma 실측 (2026-09-02, figma-cli 로 03_Wireframe 1차 6화면 집계) --- + # 공통 기준 2항: Figma 가 화면·UI·디자인의 기준이다. 종전 「포카 온 스테이지」 자체 재설계값은 + # 기준 제외 산출물에서 온 것이라 폐기하고 시안이 실제로 쓰는 값으로 바꾼다. + # 시안은 남색 기운이 없는 중립 회색 계열이다 — 화면 #141414 · 시트 #101010 · 카드 #1a1a1c. + color-cosmic-void: "#141414" + color-cosmic-surface: "#101010" + color-cosmic-surface-container: "#1A1A1C" + color-cosmic-surface-raised: "#232324" + color-on-cosmic: "#FFFFFF" + color-on-cosmic-variant: "#A3A2A0" + color-on-cosmic-muted: "#767676" + # 악센트 — 시안 실측. 핑크가 선택·CTA, 초록이 POP·성공, 시안이 FPI·탭 활성, 블루가 정보 칩. + color-cosmic-accent: "#FF7DDD" + color-on-cosmic-accent: "#141414" + color-neon-cyan: "#64EFF9" + color-on-neon-cyan: "#141414" + color-neon-emerald: "#00C60C" + color-on-neon-emerald: "#141414" + color-neon-magenta: "#FF4141" + color-on-neon-magenta: "#FFFFFF" + color-neon-violet: "#6866F3" + color-on-neon-violet: "#FFFFFF" + color-cosmic-mint: "#56CD9E" + color-on-cosmic-mint: "#141414" + color-cosmic-blue: "#52C1F9" + color-on-cosmic-blue: "#141414" + color-cosmic-orange: "#E4975D" + color-on-cosmic-orange: "#141414" + color-cosmic-outline: "#2C2C2E" color-gold: "#FFD34D" color-on-gold: "#241A02" cosmic_fx: @@ -148,29 +154,41 @@ brand: brand-google-fill: "#FFFFFF" brand-google-stroke: "#747775" brand-google-label: "#1F1F1F" + # LINE 은 답변서 Q7 확정 5종에 들어왔다(네이버 대체). 이메일은 자체 제공자라 코스믹 악센트를 쓴다. + brand-line: "#06C755" + brand-line-label: "#FFFFFF" + brand-email: "#FF7DDD" + brand-email-label: "#141414" font: - font-display: "'Gmarket Sans', 'Pretendard Variable', sans-serif" - font-body: "'Pretendard Variable', system-ui, sans-serif" - font-mono: "'Pretendard Variable', ui-monospace, monospace" + # 발주사 Figma 실측 (2026-09-02). 42dot Sans Medium/SemiBold 가 본문·라벨 전량, ABeeZee 가 숫자·영문 표시. + font-display: "'ABeeZee', '42dot Sans', sans-serif" + font-body: "'42dot Sans', 'Pretendard Variable', system-ui, sans-serif" + font-mono: "'ABeeZee', ui-monospace, monospace" font-direction: "geometric" - font-source: "Gmarket Sans = projectnoonnu woff 동봉 · Pretendard Variable = woff2 동봉 (시안/assets/ · file:// 오프라인 렌더용, 2026-08-14)" + font-source: "42dot Sans = Google Fonts OFL 가변 ttf · ABeeZee = Google Fonts OFL (app/assets/fonts/, 2026-09-02)" type: type-display-l: {family: font-display, size: 44, weight: 700, tracking: -0.01em, leading: 1.06} - type-display-m: {family: font-display, size: 32, weight: 700, tracking: -0.01em, leading: 1.11} - type-title-l: {family: font-display, size: 24, weight: 700, tracking: -0.01em, leading: 1.2} + type-display-m: {family: font-display, size: 32, weight: 700, tracking: -0.007em, leading: 1.2} + type-title-l: {family: font-display, size: 24, weight: 700, tracking: 0, leading: 1.3} type-title-m: {family: font-body, size: 18, weight: 600, leading: 1.3} type-body-l: {family: font-body, size: 18, weight: 400, leading: 1.6} type-body-m: {family: font-body, size: 16, weight: 400, leading: 1.6} type-body-s: {family: font-body, size: 14, weight: 400, leading: 1.55} type-label-l: {family: font-body, size: 14, weight: 500, leading: 1.2} - type-eyebrow: {family: font-body, size: 12, weight: 600, tracking: 0.06em, transform: uppercase} + type-label-mobile: {family: font-body, size: 16, weight: 700, leading: 1.4} + type-meta-mobile: {family: font-body, size: 13, weight: 600, leading: 1.4} + type-eyebrow: {family: font-body, size: 12, weight: 600, tracking: 0.04em} type-caption: {family: font-body, size: 12, weight: 500, leading: 1.4} type-mono-num: {family: font-mono, feature: 'tnum', note: '가격·지표·표는 tabular'} sizing: touch-min-app: 48 touch-min-web: 36 body-min-app: 16 - body-min-web: 14 + body-min-web: 16 + ui-min-desktop: 14 + ui-min-mobile: 16 + meta-min-desktop: 12 + meta-min-mobile: 13 space: space-0: 0 space-1: 4 @@ -190,10 +208,11 @@ space: gutter-mobile: 16 measure-ch: 70 radius: - corner-xs: 8 - corner-sm: 10 - corner-md: 14 - corner-lg: 18 + # 발주사 Figma 실측 (2026-09-02). 카드 12 · 칩·배지 8 · 큰 카드 16 · 알약 9999. + corner-xs: 4 + corner-sm: 8 + corner-md: 12 + corner-lg: 16 corner-full: 9999 elevation: elev-border: "0 0 0 1px rgba(16,18,29,.08)"