--- @fox/styles/_mixins.scss
+++ @fox/styles/_mixins.scss
... | ... | @@ -1,3 +1,4 @@ |
| 1 |
+@use "sass:map"; |
|
| 1 | 2 |
@use "tokens"; |
| 2 | 3 |
|
| 3 | 4 |
/// PC 이상(>= 768px). 미디어 쿼리 조건부는 `var()`를 해석하지 못해 브레이크포인트만 |
... | ... | @@ -14,3 +15,15 @@ |
| 14 | 15 |
@content; |
| 15 | 16 |
} |
| 16 | 17 |
} |
| 18 |
+ |
|
| 19 |
+/// 이름 붙은 브레이크포인트 이상에서 적용 — `xsm`(768) · `sm`(1024) · `default`(1440). |
|
| 20 |
+/// 같은 이름의 `grid-wrap-*`이 그 폭의 컨테이너다. |
|
| 21 |
+@mixin from($name) {
|
|
| 22 |
+ @if not map.has-key(tokens.$breakpoints, $name) {
|
|
| 23 |
+ @error "[@fox] 알 수 없는 브레이크포인트: `#{$name}` — 사용 가능한 값: #{map.keys(tokens.$breakpoints)}";
|
|
| 24 |
+ } |
|
| 25 |
+ |
|
| 26 |
+ @media (min-width: map.get(tokens.$breakpoints, $name)) {
|
|
| 27 |
+ @content; |
|
| 28 |
+ } |
|
| 29 |
+} |
--- @fox/styles/tokens/_responsive.scss
+++ @fox/styles/tokens/_responsive.scss
... | ... | @@ -6,6 +6,13 @@ |
| 6 | 6 |
// 모바일 우선 — `$responsive`가 기본값이고 `$responsive-pc`가 브레이크포인트 이상에서 덮는다. |
| 7 | 7 |
$breakpoint-pc: 768px; |
| 8 | 8 |
|
| 9 |
+// 컨테이너 폭(`grid-wrap-*`)과 같은 수다 — 미디어 쿼리와 폭이 어긋나지 않게 한 곳에서 낸다. |
|
| 10 |
+$breakpoints: ( |
|
| 11 |
+ xsm: 768px, |
|
| 12 |
+ sm: 1024px, |
|
| 13 |
+ default: 1440px, |
|
| 14 |
+); |
|
| 15 |
+ |
|
| 9 | 16 |
$responsive: ( |
| 10 | 17 |
font-size-display-lg: 4.4rem, |
| 11 | 18 |
font-size-display-md: 3.2rem, |
... | ... | @@ -41,13 +48,13 @@ |
| 41 | 48 |
form-height-md: 4rem, |
| 42 | 49 |
form-height-lg: 4.8rem, |
| 43 | 50 |
grid-wrap-full: 100%, |
| 44 |
- grid-wrap-default: 34.8rem, |
|
| 51 |
+ grid-wrap-default: 36rem, |
|
| 45 | 52 |
grid-wrap-sm: 32rem, |
| 46 | 53 |
grid-wrap-xsm: 28rem, |
| 47 | 54 |
grid-margin-full: 0, |
| 48 | 55 |
grid-margin-default: 1.6rem, |
| 49 |
- grid-margin-sm: 3rem, |
|
| 50 |
- grid-margin-xsm: 5rem, |
|
| 56 |
+ grid-margin-sm: 1.6rem, |
|
| 57 |
+ grid-margin-xsm: 1.6rem, |
|
| 51 | 58 |
grid-gutter-sm: 0.8rem, |
| 52 | 59 |
grid-gutter-md: 1.6rem, |
| 53 | 60 |
grid-gutter-lg: 2rem, |
... | ... | @@ -119,13 +126,13 @@ |
| 119 | 126 |
form-height-md: 4rem, |
| 120 | 127 |
form-height-lg: 5.6rem, |
| 121 | 128 |
grid-wrap-full: 100%, |
| 122 |
- grid-wrap-default: 155.2rem, |
|
| 123 |
- grid-wrap-sm: 108rem, |
|
| 124 |
- grid-wrap-xsm: 80rem, |
|
| 129 |
+ grid-wrap-default: 144rem, |
|
| 130 |
+ grid-wrap-sm: 102.4rem, |
|
| 131 |
+ grid-wrap-xsm: 76.8rem, |
|
| 125 | 132 |
grid-margin-full: 0, |
| 126 | 133 |
grid-margin-default: 4.8rem, |
| 127 |
- grid-margin-sm: 42rem, |
|
| 128 |
- grid-margin-xsm: 56rem, |
|
| 134 |
+ grid-margin-sm: 4.8rem, |
|
| 135 |
+ grid-margin-xsm: 4.8rem, |
|
| 129 | 136 |
grid-gutter-sm: 0.8rem, |
| 130 | 137 |
grid-gutter-md: 1.6rem, |
| 131 | 138 |
grid-gutter-lg: 2.4rem, |
--- @fox/tools/build-tokens.py
+++ @fox/tools/build-tokens.py
... | ... | @@ -206,16 +206,45 @@ |
| 206 | 206 |
resp_pc = OrderedDict((name_responsive(p), css_len(n["$value"])) for p, n in raw["pc"].items()) |
| 207 | 207 |
assert list(resp_mobile) == list(resp_pc) |
| 208 | 208 |
|
| 209 |
-# Figma 값을 그대로 쓸 수 없는 토큰. 시안의 값은 **아트보드 폭**이라 CSS에서는 뜻이 달라진다. |
|
| 210 |
-# 재export해도 이 치환이 유지되도록 변환기가 들고 있는다(생성 파일을 손으로 고치면 지워진다). |
|
| 211 |
-DEV_OVERRIDES = {
|
|
| 212 |
- # `full`은 "화면을 채운다"는 뜻인데 Figma에서는 아트보드 폭(모바일 380px / PC 1920px)으로 |
|
| 213 |
- # 잡혀 있다. 그대로 쓰면 그보다 좁은 창에서 넘치고 넓은 창에서는 덜 찬다. |
|
| 209 |
+# ── 개발용 치환 ─────────────────────────────────────────────────────────────── |
|
| 210 |
+# Figma의 grid 값은 **아트보드 좌표**다. 폭은 아트보드 폭이고 여백은 그 안에서 가운데로 맞추기 |
|
| 211 |
+# 위한 offset이라(예: PC sm = (1920-1080)/2 = 420px) CSS에서는 뜻이 달라진다. |
|
| 212 |
+# 재export해도 유지되도록 변환기가 들고 있는다 — 생성 파일을 손으로 고치면 지워진다. |
|
| 213 |
+# |
|
| 214 |
+# 폭은 브레이크포인트와 같은 수를 쓴다. 그래야 "이 폭에서 레이아웃이 갈린다"와 "컨테이너가 |
|
| 215 |
+# 여기까지 넓어진다"가 어긋나지 않는다(사용자 확정 사항). |
|
| 216 |
+DEV_OVERRIDES_MOBILE = {
|
|
| 214 | 217 |
"grid-wrap-full": "100%", |
| 218 |
+ "grid-wrap-default": "36rem", |
|
| 219 |
+ "grid-wrap-sm": "32rem", |
|
| 220 |
+ "grid-wrap-xsm": "28rem", |
|
| 221 |
+ # 페이지 좌우 여백. 컨테이너가 좁아진다고 여백이 넓어지지는 않으므로 한 값을 공유한다. |
|
| 222 |
+ "grid-margin-full": "0", |
|
| 223 |
+ "grid-margin-default": "1.6rem", |
|
| 224 |
+ "grid-margin-sm": "1.6rem", |
|
| 225 |
+ "grid-margin-xsm": "1.6rem", |
|
| 215 | 226 |
} |
| 216 |
-for _key, _value in DEV_OVERRIDES.items(): |
|
| 217 |
- resp_mobile[_key] = _value |
|
| 218 |
- resp_pc[_key] = _value |
|
| 227 |
+DEV_OVERRIDES_PC = {
|
|
| 228 |
+ "grid-wrap-full": "100%", |
|
| 229 |
+ "grid-wrap-default": "144rem", |
|
| 230 |
+ "grid-wrap-sm": "102.4rem", |
|
| 231 |
+ "grid-wrap-xsm": "76.8rem", |
|
| 232 |
+ "grid-margin-full": "0", |
|
| 233 |
+ "grid-margin-default": "4.8rem", |
|
| 234 |
+ "grid-margin-sm": "4.8rem", |
|
| 235 |
+ "grid-margin-xsm": "4.8rem", |
|
| 236 |
+} |
|
| 237 |
+resp_mobile.update(DEV_OVERRIDES_MOBILE) |
|
| 238 |
+resp_pc.update(DEV_OVERRIDES_PC) |
|
| 239 |
+ |
|
| 240 |
+# 미디어 쿼리는 `var()`를 해석하지 못해 컴파일타임 값이어야 한다. 위 PC 폭과 같은 수다. |
|
| 241 |
+BREAKPOINTS = OrderedDict( |
|
| 242 |
+ ( |
|
| 243 |
+ ("xsm", "768px"),
|
|
| 244 |
+ ("sm", "1024px"),
|
|
| 245 |
+ ("default", "1440px"),
|
|
| 246 |
+ ) |
|
| 247 |
+) |
|
| 219 | 248 |
|
| 220 | 249 |
BREAKPOINT_PC = "768px" |
| 221 | 250 |
|
... | ... | @@ -282,6 +311,9 @@ |
| 282 | 311 |
// 뷰포트에 따라 달라지는 크기 토큰. 키는 두 모드가 완전히 동일하다. |
| 283 | 312 |
// 모바일 우선 — `$responsive`가 기본값이고 `$responsive-pc`가 브레이크포인트 이상에서 덮는다. |
| 284 | 313 |
$breakpoint-pc: {BREAKPOINT_PC};
|
| 314 |
+ |
|
| 315 |
+// 컨테이너 폭(`grid-wrap-*`)과 같은 수다 — 미디어 쿼리와 폭이 어긋나지 않게 한 곳에서 낸다. |
|
| 316 |
+{scss_map("breakpoints", BREAKPOINTS)}
|
|
| 285 | 317 |
|
| 286 | 318 |
{scss_map("responsive", resp_mobile)}
|
| 287 | 319 |
|
... | ... | @@ -433,6 +465,7 @@ |
| 433 | 465 |
|
| 434 | 466 |
written.append(write("_mixins.scss", f"""// 믹스인 — 여러 선언이 항상 함께 가야 하는 패턴을 묶는다.
|
| 435 | 467 |
|
| 468 |
+@use "sass:map"; |
|
| 436 | 469 |
@use "tokens"; |
| 437 | 470 |
|
| 438 | 471 |
/// PC 이상(>= {BREAKPOINT_PC})에서 적용. 모바일 우선이므로 기본 스타일은 밖에 쓴다.
|
... | ... | @@ -451,6 +484,18 @@ |
| 451 | 484 |
@content; |
| 452 | 485 |
}} |
| 453 | 486 |
}} |
| 487 |
+ |
|
| 488 |
+/// 이름 붙은 브레이크포인트 이상에서 적용 — `xsm`(768) · `sm`(1024) · `default`(1440). |
|
| 489 |
+/// 같은 이름의 `grid-wrap-*`이 그 폭의 컨테이너다. |
|
| 490 |
+@mixin from($name) {{
|
|
| 491 |
+ @if not map.has-key(tokens.$breakpoints, $name) {{
|
|
| 492 |
+ @error "[@fox] 알 수 없는 브레이크포인트: `#{{$name}}` — 사용 가능한 값: #{{map.keys(tokens.$breakpoints)}}";
|
|
| 493 |
+ }} |
|
| 494 |
+ |
|
| 495 |
+ @media (min-width: map.get(tokens.$breakpoints, $name)) {{
|
|
| 496 |
+ @content; |
|
| 497 |
+ }} |
|
| 498 |
+}} |
|
| 454 | 499 |
""")) |
| 455 | 500 |
|
| 456 | 501 |
written.append(write("abstracts.scss", """// 저작(authoring) 진입점 — 컴포넌트의 `.module.scss`가 `@use`하는 파일.
|
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?