genetics-mcp 2.9.1 → 2.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/aarch64-apple-darwin/gcell +0 -0
- package/bin/aarch64-unknown-linux-musl/gcell +0 -0
- package/bin/manifest.json +4 -4
- package/bin/x86_64-apple-darwin/gcell +0 -0
- package/bin/x86_64-unknown-linux-musl/gcell +0 -0
- package/genetics.mjs +658 -26
- package/package.json +1 -1
- package/skills/genetics-cell-id/SKILL.md +204 -0
- package/skills/genetics-report/SKILL.md +217 -93
- package/skills/genetics-report/assets/example/cases.json +19 -22
- package/skills/genetics-report/assets/example/report.json +198 -53
- package/skills/genetics-report/assets/render.py +21 -10
- package/skills/genetics-report/assets/subset_font.py +24 -8
- package/skills/genetics-report/references/content-pipeline.md +316 -733
- package/skills/genetics-report/references/design-system.md +20 -723
- package/skills/genetics-report/references/font-embedding.md +132 -108
- package/skills/genetics-report/references/illustrations.md +20 -462
- package/skills/genetics-report/references/lim-portal-format.md +756 -182
- package/skills/genetics-report/references/process.md +414 -403
- package/skills/genetics-report/references/qa.md +28 -712
- package/skills/genetics-report/assets/generate-illustrations.py +0 -159
- package/skills/genetics-report/assets/qa.py +0 -198
- package/skills/genetics-report/assets/visual-baseline.py +0 -459
- package/skills/genetics-report/references/textbook-mode.md +0 -111
- package/skills/genetics-report/references/wysiwyg.md +0 -751
|
@@ -1,733 +1,30 @@
|
|
|
1
|
-
# 디자인 시스템
|
|
1
|
+
# 디자인 시스템 — 폐기됨 (2026-08-24 관보 전환)
|
|
2
2
|
|
|
3
|
-
이 문서는
|
|
3
|
+
> 이 문서는 「팔레트 6색 → 21토큰 → 대비 감사 → report.css」 체계를 설명했다.
|
|
4
|
+
> 관보 서식에는 그 층이 존재하지 않는다.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
## 왜 폐기인가
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
| 폰트 임베드·서브셋·라이선스 | `references/font-embedding.md` |
|
|
10
|
-
| 데이터 모델·절 번호·고객 언어 치환 | `references/content-pipeline.md` |
|
|
11
|
-
| 편집기 마크업 계약 | `references/wysiwyg.md` |
|
|
12
|
-
| 검수 항목 전체 | `references/qa.md` |
|
|
13
|
-
|
|
14
|
-
본문에 나오는 고객사·수행사 이름·"18건"·`korean-navy`는 이 시스템이 처음 적용된 프로젝트의 **예시**다. 실제 동작 예시는 그 프로젝트의 `report/` 안 `report.json` · `cases.json` · `figure.html` 이다. 규칙 자체는 팔레트·도메인과 무관하다.
|
|
15
|
-
|
|
16
|
-
색·타이포·아키타입 체계는 `moai-officer:doc-pptx`(발표 슬라이드)에서 계승했고, 매체를 "스크롤되는 단일 HTML 문서"로 바꾸면서 **색은 유도 방식으로, 치수는 전부 재산정**했다.
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## 0. 어느 파일이 무엇을 정하는가
|
|
21
|
-
|
|
22
|
-
| 파일 | 정하는 것 | 손대는 빈도 |
|
|
23
|
-
|---|---|---|
|
|
24
|
-
| `assets/palette.py` | **색 값 전부.** 팔레트 6색 → 문서용 토큰 21개 유도 + `audit()` | 새 팔레트를 추가할 때만 |
|
|
25
|
-
| `assets/build.py` | `:root` 주입, 마크업 조립, 폰트 임베드, 유출 점검 | 거의 없음 |
|
|
26
|
-
| `assets/report.css` | **구조·치수·조판·인쇄.** 색 토큰은 선언하지 않는다 | 블록을 새로 만들 때 |
|
|
27
|
-
| `assets/editor.css` / `editor.js` | 편집 레이어. 문서 토큰을 **빌려 쓰기만** 한다 | `wysiwyg.md` 참고 |
|
|
28
|
-
| `assets/qa.py` | 규칙의 실행 게이트. 산출물에서 **되잰다** | 검사를 추가할 때 |
|
|
29
|
-
| `report.json` | 팔레트 선택 · 판형 변수 · 뼈대 · 치환표 · 금지어 | **매 프로젝트** |
|
|
30
|
-
| `cases.json` | 건별 내용 | **매 프로젝트** |
|
|
31
|
-
|
|
32
|
-
### 규칙 0 — `report.css` 에 색 리터럴을 쓰지 않는다
|
|
33
|
-
|
|
34
|
-
`report.css` 첫 15줄이 이유를 못 박고 있다.
|
|
35
|
-
|
|
36
|
-
```css
|
|
37
|
-
/* 색 토큰은 여기서 선언하지 않는다. build.py 가 palette.py 로 유도해 :root 를
|
|
38
|
-
앞에 붙인다. 팔레트를 갈아끼워도 대비 기준이 자동으로 지켜지게 하기 위함이다. */
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
`grep ':root' report.css editor.css` → 주석 한 줄 외에 선언 없음. 색은 전부 `var(--토큰)`이다.
|
|
42
|
-
|
|
43
|
-
**어기면**: 팔레트를 바꿨는데 그 한 색만 안 따라온다. 그리고 그 색이 대비 미달이어도 `audit()` 대상이 아니므로 검사가 못 잡는다. 실제로 아직 두 곳이 남아 있다(§10).
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## 1. 색을 손으로 고르지 않는다
|
|
48
|
-
|
|
49
|
-
### 1.1 왜 — 상위 팔레트의 6색은 슬라이드 기준이다
|
|
50
|
-
|
|
51
|
-
`palette.py` 도크스트링 원문:
|
|
52
|
-
|
|
53
|
-
> doc-pptx 스킬의 10개 큐레이션 팔레트는 **발표 슬라이드용**이라 6개 값(Primary·Secondary·Background·Surface·Ink·Mid)만 준다. 문서는 슬라이드보다 글자가 훨씬 작고 많아서 그 6개만으로는 대비 기준을 못 맞춘다. 실제로 Korean Navy 의 Gold `#C9A961` 는 흰 바탕에서 2.25:1 이라 **글자로 쓰면 WCAG 를 통과하지 못한다**.
|
|
54
|
-
|
|
55
|
-
슬라이드는 24pt 이상 큰 활자를 3–5m 거리에서 본다. 문서는 10.5–16px 활자를 팔 길이에서 읽는다. **같은 색이 한쪽에서는 통과하고 한쪽에서는 떨어진다.**
|
|
56
|
-
|
|
57
|
-
손으로 고르면 실패하는 방식이 세 가지다.
|
|
58
|
-
|
|
59
|
-
| 실패 | 증상 |
|
|
60
|
-
|---|---|
|
|
61
|
-
| 원색을 그대로 글자로 씀 | 흰 바탕 2.25:1. 화면으로는 "좀 연하네" 정도로만 보여 **눈으로 못 잡는다** |
|
|
62
|
-
| 흰 바탕 기준으로만 잰 색을 다른 배경에 얹음 | 강조 블록(`--accent-bg`) 위, 옅은 Primary 배경(`--primary-bg`) 위에서 미달 |
|
|
63
|
-
| 팔레트를 바꿀 때 색을 다시 고르지 않음 | 이름은 그대로인데 대비만 무너진다 |
|
|
64
|
-
|
|
65
|
-
### 1.2 유도 파이프라인
|
|
66
|
-
|
|
67
|
-
```
|
|
68
|
-
report.json "palette": "korean-navy" (또는 6키 인라인 딕셔너리)
|
|
69
|
-
│
|
|
70
|
-
▼
|
|
71
|
-
palette.PALETTES[name] ─ 6색 ─▶ palette.derive() ─ 21토큰 ─▶ palette.audit() 17쌍
|
|
72
|
-
│ │
|
|
73
|
-
│ build.py: assert not bad ← 미달이면 빌드 실패
|
|
74
|
-
▼
|
|
75
|
-
build.py 가 :root { --primary:…; … --nav-w; --pad-x; } 를 report.css 앞에 붙임
|
|
76
|
-
▼
|
|
77
|
-
qa.py 가 **산출물에 박힌 :root 를 다시 파싱해** audit() 을 재실행
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
`qa.py` 주석이 마지막 단계의 요점을 적어 두었다.
|
|
81
|
-
|
|
82
|
-
```python
|
|
83
|
-
# 3. 색 대비 — 문서에 박힌 :root 를 그대로 읽어 잰다.
|
|
84
|
-
# 빌드가 유도한 값이 아니라 **실제로 나간 값**을 재는 것이 요점이다.
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
빌드 시점의 값이 아니라 **파일에 실제로 들어간 값**을 재므로, 누가 손으로 `:root` 를 고쳐도 잡힌다.
|
|
88
|
-
|
|
89
|
-
### 1.3 유도 알고리즘
|
|
90
|
-
|
|
91
|
-
원자 연산 다섯 개로 전부 만든다. 전부 **색상(hue)과 채도는 지키고 명도만 움직인다** — 그래야 팔레트의 인상이 유지된다.
|
|
92
|
-
|
|
93
|
-
| 함수 | 하는 일 | 쓰이는 곳 |
|
|
94
|
-
|---|---|---|
|
|
95
|
-
| `darken_to(h, target, bg)` | `bg` 대비 `target` 이상이 될 때까지 HLS 명도를 0.01씩 낮춘다. 검정에 닿으면 멈춘다 | `primary`, `primary-l`, `accent-mid`, `accent-ink`, `muted` |
|
|
96
|
-
| `lighten_to(h, target, fg)` | `fg` 가 그 위에서 `target` 대비가 날 때까지 밝힌다(채도는 0.004씩 뺀다) | `accent-on-primary`, `on_color` 의 마지막 폴백 |
|
|
97
|
-
| `mix(a, b, t)` | 선형 보간 | `primary-bg`(92% 흰쪽), `primary-hair`(72%), `accent-bg`(90%) |
|
|
98
|
-
| `on_color(bg, ink)` | 그 배경 위에 얹을 글자색을 **고른다** — ink → 흰색 → (둘 다 안 되면) 배경을 밝힘 | `accent-chip` / `accent-on` 쌍 |
|
|
99
|
-
| `soft_on(bg, need)` | 흰색에서 `bg` 쪽으로 **이진 탐색**해 `need` 를 지키는 가장 부드러운 값 | `on-primary-soft` |
|
|
100
|
-
|
|
101
|
-
`on_color` 의 주석이 왜 글자색을 고정할 수 없는지 설명한다.
|
|
102
|
-
|
|
103
|
-
> 칩(색 채운 라벨)은 팔레트마다 Secondary 밝기가 제각각이라 글자색을 고정할 수 없다. Mono 의 `#666666` 은 흰 글자라야 하고, Navy 의 Gold 는 검은 글자라야 한다.
|
|
104
|
-
|
|
105
|
-
`soft_on` 도 마찬가지다.
|
|
106
|
-
|
|
107
|
-
> 흰색을 그냥 반투명으로 깔면 Primary 가 밝은 팔레트(예: Claude Classic 의 주황)에서 3:1 대로 떨어진다. 그래서 섞는 비율을 팔레트마다 **계산**한다.
|
|
108
|
-
|
|
109
|
-
#### 순서가 중요한 세 지점
|
|
110
|
-
|
|
111
|
-
`derive()` 는 단순 나열이 아니다. 세 곳에서 **의존 순서**가 있고, 순서를 바꾸면 조용히 미달이 난다.
|
|
112
|
-
|
|
113
|
-
1. **Primary 수렴 루프.** Primary 는 글자색이자 칩 배경이고, 자기 자신의 옅은 배경(`primary-bg`) 위에도 글자로 얹힌다. 흰 바탕 기준으로만 잡으면 그 옅은 배경 위에서 4.1:1 로 미달한다. 서로가 서로에 의존하므로 6회 되풀이해 수렴시킨다.
|
|
114
|
-
|
|
115
|
-
```python
|
|
116
|
-
pri = darken_to(primary, 4.5, W)
|
|
117
|
-
for _ in range(6):
|
|
118
|
-
pri = darken_to(pri, 4.5, mix(pri, W, 0.92))
|
|
119
|
-
pri_bg = mix(pri, W, 0.92)
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
2. **강조 블록 배경을 먼저 정한다.** `accent-ink` 는 흰 바탕이 아니라 `accent-bg` 기준으로 유도한다 — 흰 바탕 기준으로 잡으면 블록 위에서 미달한다.
|
|
123
|
-
|
|
124
|
-
3. **`muted` 는 `surface` 기준.** 더 어두운 배경을 기준으로 6.0:1 을 잡아야 `paper`·`surface` 두 지면 모두에서 산다. 그리고 중성 회색이 아니라 **Primary 의 색상을 그대로 쓴 회색**이다(`_hue_of(pri)` → HLS(h, 0.42, 0.10)). 도크스트링: "아무 회색이나 쓰면 '고르지 않은' 인상이 난다."
|
|
125
|
-
|
|
126
|
-
### 1.4 토큰 역할표 (전수 21개)
|
|
127
|
-
|
|
128
|
-
값은 `korean-navy` 기준 실측. `css`/`ed`/`svg` = `report.css` / `editor.css` / 예시 `figure.html` 의 `var()` 참조 횟수(직접 셈).
|
|
129
|
-
|
|
130
|
-
#### Primary 계열 — 구조 · 완료 · 신뢰
|
|
131
|
-
|
|
132
|
-
| 토큰 | 값 | 지켜야 하는 대비 | 허용 용도 | 주요 사용처 | css/ed/svg |
|
|
133
|
-
|---|---|---|---|---|---|
|
|
134
|
-
| `--primary` | `#1D3557` | paper 4.5:1 (실측 12.36) · `--primary-bg` 위 4.5:1 (10.73) | 글자·괘선·칩 배경 전부 | `.cover` 배경, `h2.sec` 하단 2px, `table.verdict th`, `.case .num`, `.m-live`, `.cta`, `footer` 상단선, `.ref a`, `.xref`, `ol.enum .n` | 27/1/4 |
|
|
135
|
-
| `--primary-d` | `#152740` | — | **예비. 현재 참조 0회** | 없음 | 0/0/0 |
|
|
136
|
-
| `--primary-l` | `#2C5185` | paper 4.5:1 (8.01) | 부속 숫자 글자 | `.stat .v small`, `.toc .i` | 2/0/0 |
|
|
137
|
-
| `--primary-bg` | `#EDEFF2` | 그 위 `--ink` 4.5:1 (16.0) · `--primary` 4.5:1 (10.73) | **배경 전용** | `.m-data` 칩, `.r-check` 블록, 다이어그램 노드 | 2/0/3 |
|
|
138
|
-
| `--primary-hair` | `#C0C6D0` | — (글자 금지) | **괘선 전용** | `.m-data` inset 1px, `.ref a` 밑줄, `.xref` 밑줄, 편집 hover 아웃라인, 다이어그램 연결선 | 3/1/9 |
|
|
139
|
-
|
|
140
|
-
#### Accent 계열 — 고객 액션 · 미결
|
|
141
|
-
|
|
142
|
-
| 토큰 | 값 | 지켜야 하는 대비 | 허용 용도 | 주요 사용처 | css/ed/svg |
|
|
143
|
-
|---|---|---|---|---|---|
|
|
144
|
-
| `--accent` | `#C9A961` | **검사하지 않는다** (글자로 안 쓰므로) | **배경·괘선 전용** | `.cover::before` 5px 바, `.toc a:hover` 좌측선, `.r-open`·`.open-item` inset 3px | 5/0/0 |
|
|
145
|
-
| `--accent-chip` | `#C9A961` | `--accent-on` 과 4.5:1 (8.19) | **칩 배경.** `--accent-on` 과 **쌍으로만** | `.m-test` 배경 | 1/0/3 |
|
|
146
|
-
| `--accent-on` | `#141413` | 위와 같은 쌍 | 그 칩 위 글자 | `.m-test` 글자 | 1/0/2 |
|
|
147
|
-
| `--accent-on-primary` | `#C9A961` | `--primary` 위 4.5:1 (5.49) | **어두운 표지 위 강조 글자 전용** | `.cover .kicker`, `.cover dt` | 2/0/0 |
|
|
148
|
-
| `--accent-mid` | `#B38E3B` | paper 3.0:1 (3.07) | **24px+ Bold 글자만** | `h2.sec .no`(clamp 26–40px/700), `:focus-visible` 아웃라인 | 2/0/0 |
|
|
149
|
-
| `--accent-ink` | `#876D30` | paper 4.5:1 (4.93) · `--accent-bg` 위 4.5:1 (4.58) | **모든 크기의 글자** | `.toc-g .no`(12px), `.stat.is-open .v`, `.c-o[data-open="1"]`, `.r-open>.tag`, `.open-item h3`, `.orefs`, `.r-open ol.enum .n` 배경 | 10/8/0 |
|
|
150
|
-
| `--accent-bg` | `#FAF6EF` | 그 위 `--ink` 4.5:1 (17.11) | **배경 전용** | `.r-open`, `.open-item`, `.ev` 형광 | 3/1/1 |
|
|
151
|
-
|
|
152
|
-
#### 어두운 바탕 위 글자
|
|
153
|
-
|
|
154
|
-
| 토큰 | 값 | 대비 | 용도 | css/ed/svg |
|
|
155
|
-
|---|---|---|---|---|
|
|
156
|
-
| `--on-primary` | `#FFFFFF` | `--primary` 위 12.36 | Primary 배경 위 본문·칩 글자 | 7/0/2 |
|
|
157
|
-
| `--on-primary-soft` | `#929DAE` | `--primary` 위 4.51 | Primary 배경 위 **부제·메타** — 본문보다 한 톤 죽여 위계를 만든다 | 3/0/0 |
|
|
158
|
-
|
|
159
|
-
#### 중립 — 지면 · 활자 · 괘선
|
|
160
|
-
|
|
161
|
-
| 토큰 | 값 | 대비 | 역할 | css/ed/svg |
|
|
162
|
-
|---|---|---|---|---|
|
|
163
|
-
| `--paper` | `#FFFFFF` | — | 본문 지면. 헤어라인 그리드의 셀 배경 | 6/0/0 |
|
|
164
|
-
| `--surface` | `#FAF9F5` | — | 한 단계 낮은 지면 (`.row`, `.fig`, 표 짝수행) | 3/0/0 |
|
|
165
|
-
| `--ink` | `#141413` | paper 18.43 | 본문 글자 | 4/0/0 |
|
|
166
|
-
| `--muted` | `#585F6A` | paper 6.44 · surface 6.11 | **본문 보조 글자** (캡션·리드·설명·푸터) | 13/0/3 |
|
|
167
|
-
| `--mid` | `#B0AEA5` | paper 2.2 ❌ | 팔레트 Mid. **선언만 하고 쓰지 않는다** | **0/0/0** |
|
|
168
|
-
| `--rule` | `#EFECE1` | — | 1px 괘선 / 헤어라인 그리드 바탕 | 9/0/0 |
|
|
169
|
-
| `--hair` | `#F5F3EC` | — | 더 약한 구분선 (표 행선, `.toc-g` 상단) | 3/0/0 |
|
|
170
|
-
|
|
171
|
-
#### 판형 변수 (색이 아니라 `report.json` 에서 온다)
|
|
172
|
-
|
|
173
|
-
| 토큰 | 기본값 | `report.json` 키 | 역할 |
|
|
174
|
-
|---|---|---|---|
|
|
175
|
-
| `--nav-w` | `224px` | `nav_width` | 사이드 목차 폭 |
|
|
176
|
-
| `--pad-x` | `clamp(20px,4vw,68px)` | `pad_x` | 지면 좌우 여백. `.cover` 와 `.page` 가 **같은 값을 공유** |
|
|
177
|
-
|
|
178
|
-
`--pad-x` 공유의 효과: 표지가 전면 색 블록이라 배경은 끊기지만, **표지 문자열과 본문 문자열의 좌측 정렬선은 스크롤 내내 일치한다.**
|
|
179
|
-
|
|
180
|
-
### 1.5 `audit()` — 17쌍을 되잰다
|
|
181
|
-
|
|
182
|
-
`derive()` 를 고칠 때의 안전망이다. 17쌍의 구성:
|
|
183
|
-
|
|
184
|
-
| 묶음 | 쌍 | 기준 |
|
|
185
|
-
|---|---|---|
|
|
186
|
-
| 본문 (4) | `ink` × {`paper`, `surface`, `primary-bg`, `accent-bg`} | 4.5 |
|
|
187
|
-
| 보조 (2) | `muted` × {`paper`, `surface`} | 4.5 |
|
|
188
|
-
| 링크 (2) | {`primary`, `primary-l`} on `paper` | 4.5 |
|
|
189
|
-
| 칩 (3) | 흰글자/`primary`, `accent-on`/`accent-chip`, `primary`/`primary-bg` | 4.5 |
|
|
190
|
-
| 강조 (2) | `accent-ink` × {`accent-bg`, `paper`} | 4.5 |
|
|
191
|
-
| 큰활자 (1) | `accent-mid`/`paper` | **3.0** |
|
|
192
|
-
| 표지 (3) | {`accent-on-primary`, `on-primary`, `on-primary-soft`} on `primary` | 4.5 |
|
|
193
|
-
|
|
194
|
-
**기준값이 3.0인 쌍은 하나뿐이고, 그 하나는 26–40px Bold 에만 쓰인다**(`h2.sec .no`). 기준을 통일하고 싶은 유혹이 오면 이 대응 관계를 먼저 확인하라. 3.0으로 통일하면 12px 라벨이 무너지고, 4.5로 통일하면 밝은 Secondary 팔레트가 전부 실패한다.
|
|
195
|
-
|
|
196
|
-
`PALETTES` 에 등록된 **9개 팔레트 전부가 17쌍을 통과한다**. `python3 assets/palette.py` 로 확인할 수 있다.
|
|
197
|
-
|
|
198
|
-
### 1.6 3중 게이트
|
|
199
|
-
|
|
200
|
-
| 시점 | 무엇이 막는가 | 실패하면 |
|
|
201
|
-
|---|---|---|
|
|
202
|
-
| 팔레트 추가 | `python3 palette.py <name>` | 미달 쌍을 이름·수치와 함께 출력 |
|
|
203
|
-
| 빌드 | `build.py` 의 `assert not bad, f'팔레트 대비 미달: …'` | **HTML 이 생성되지 않는다** |
|
|
204
|
-
| 검수 | `qa.py` 가 산출물의 `:root` 를 파싱해 `audit()` 재실행 | `err` → `sys.exit(1)` |
|
|
205
|
-
|
|
206
|
-
> **R1.** 색은 유도한다. 손으로 고른 hex 를 CSS 에 넣지 마라. 넣으면 (a) 팔레트 교체 시 그 색만 안 따라오고, (b) `audit()` 대상이 아니라 **검사가 통과하는데 대비는 무너진 상태**가 된다. 예시 프로젝트에서 실제로 잡아낸 것도 이 유형이다.
|
|
207
|
-
|
|
208
|
-
---
|
|
209
|
-
|
|
210
|
-
## 2. 색을 쓰는 규칙
|
|
211
|
-
|
|
212
|
-
### 2.1 배경 전용 / 글자 전용을 섞지 않는다
|
|
213
|
-
|
|
214
|
-
| 분류 | 토큰 | 어기면 |
|
|
215
|
-
|---|---|---|
|
|
216
|
-
| **배경·괘선 전용** | `--accent`, `--primary-bg`, `--accent-bg`, `--primary-hair`, `--rule`, `--hair`, `--mid` | `--accent` 를 글자로 쓰면 2.25:1. 화면으로는 "좀 연하네"로만 보인다 |
|
|
217
|
-
| **글자 전용 등급** | `--accent-mid`(24px+ Bold) / `--accent-ink`(모든 크기) | 등급을 무시하면 12px 라벨이 3:1 로 떨어진다 |
|
|
218
|
-
| **쌍으로만** | `--accent-chip` + `--accent-on` | 칩 배경만 바꾸고 글자색을 고정하면 팔레트에 따라 흰 글자가 흰 배경에 얹힌다 |
|
|
219
|
-
| **어두운 바탕 전용** | `--on-primary`, `--on-primary-soft`, `--accent-on-primary` | 흰 바탕에 얹으면 각각 1.0:1 / **2.74:1** / 2.25:1 — 셋 다 흰 바탕 본문으로는 못 쓴다. Primary 위에서는 12.36 / 4.51 / 5.49 (korean-navy 실측) |
|
|
220
|
-
|
|
221
|
-
두 규칙은 코드로 강제된다. `qa.py`:
|
|
222
|
-
|
|
223
|
-
```python
|
|
224
|
-
leak = re.findall(r'(?<![a-z-])color:\s*var\(--mid\)', doc)
|
|
225
|
-
leak2 = re.findall(r'(?<![-a-z])color:\s*var\(--accent\)(?![-a-z])', doc)
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
lookbehind/lookahead 가 핵심이다. `border-left-color:var(--accent)`(괘선 — 정상)와 `color:var(--accent-mid)`(정상)를 오탐하지 않으면서 `color:var(--accent)` 만 잡는다. 정규식을 "정리"하다가 이 경계를 지우면 검사가 전부 오탐으로 뒤덮여 결국 검사를 끈다.
|
|
229
|
-
|
|
230
|
-
### 2.2 `--mid` 는 선언하되 절대 쓰지 않는다
|
|
231
|
-
|
|
232
|
-
참조 횟수 **0**. 흰 바탕 2.2:1 이라 본문은 물론 캡션에도 못 쓴다.
|
|
233
|
-
|
|
234
|
-
**이것은 상위 스킬과의 정면 충돌이고, 이쪽을 택했다.** doc-pptx 는 Mid 를 캡션·부제 색으로 안내한다(발표 거리라는 다른 전제가 있다). 문서에서는 통과하지 못한다. 대체재가 `--muted` 이고, 그래서 `--muted` 가 `--primary` 다음으로 많이 쓰인다(13회).
|
|
235
|
-
|
|
236
|
-
**선언만 하고 안 쓰는 토큰이 있어도 된다.** "이 색은 여기 있지만 쓰면 안 된다"는 것 자체가 명세다. 그리고 그 금지는 주석이 아니라 검사로 적는다.
|
|
237
|
-
|
|
238
|
-
### 2.3 확인 등급 칩 — 4단계를 4색으로 만들지 않는다
|
|
239
|
-
|
|
240
|
-
```css
|
|
241
|
-
.m-live { background:var(--primary); color:var(--on-primary); }
|
|
242
|
-
.m-data { background:var(--primary-bg); color:var(--primary); box-shadow:inset 0 0 0 1px var(--primary-hair); }
|
|
243
|
-
.m-test { background:var(--accent-chip); color:var(--accent-on); }
|
|
244
|
-
.m-code { background:var(--paper); color:var(--muted); box-shadow:inset 0 0 0 1px var(--rule); }
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
`report.css` 의 절 제목이 문법을 요약한다 — "확인 등급 칩 — 팔레트 안에서 농도로 강약". 근거가 강한 순서대로 **색이 진하다.** 신호등(빨강/노랑/초록)을 도입하면 "한 문서 한 팔레트"가 무너지고, 독자는 색에서 등급이 아니라 위험도를 읽는다.
|
|
248
|
-
|
|
249
|
-
칩 라벨(`grades` 키)과 클래스의 대응은 `report.json` 이 정한다. 4단계가 아니어도 되지만 **클래스는 이 넷 중에서 고른다** — 새 색을 만들지 않는다.
|
|
250
|
-
|
|
251
|
-
### 2.4 Accent 의 의미를 하나에 독점 배정한다
|
|
252
|
-
|
|
253
|
-
이 문서 체계에서 Accent 는 **"상대가 해야 할 일 / 아직 안 끝난 것"** 하나만 뜻한다. `.r-open`, `.open-item`, `.stat.is-open`, `.c-o[data-open="1"]`, `.toc-g .no` 가 전부 이 배정에서 파생된다.
|
|
254
|
-
|
|
255
|
-
다른 문서라면 "위험", "변경됨", "신규" 중 하나를 고르되 **하나만** 고른다. Accent 가 "예쁜 포인트 색"이 되는 순간 독자는 그 색을 봐도 아무 정보를 얻지 못한다.
|
|
256
|
-
|
|
257
|
-
---
|
|
258
|
-
|
|
259
|
-
## 3. 슬라이드 아키타입 → 스크롤 HTML 매핑
|
|
260
|
-
|
|
261
|
-
`build.py` 는 `report.json` 의 `sections[].kind` 로 블록을 고른다. `kind` 는 5종(`summary` / `status` / `cases` / `asks` / `note`)이고, 그 앞뒤에 표지·지표 밴드·꼬리말이 고정으로 붙는다.
|
|
262
|
-
|
|
263
|
-
| 아키타입 | `kind` / 위치 | CSS 블록 | 마크업 | 슬라이드와 달라진 점 |
|
|
264
|
-
|---|---|---|---|---|
|
|
265
|
-
| **Title** | 고정 (문서 최상단) | `.cover` | `<header class="cover">` | 좌측 강조선을 **Accent 5px** 으로. 표지 전체가 Primary 라 Primary 바가 안 보인다. 하단 메타는 `<dl>` 4열. 제목 `clamp(32→62px)/700`, `max-width:22ch` |
|
|
266
|
-
| **Agenda** | 고정 (`.page` 좌열) | `nav.toc` | `<nav class="toc" aria-label>` | **한 장이 아니라 스크롤 내내 따라오는 좌측 고정 내비.** 2계층(대분류 `.toc-g` + 건별 `<ol>`) |
|
|
267
|
-
| **Stats** | 고정 (`<main>` 최상단) | `.stats` / `.stat` | `<div class="stats">` 4열 | `clamp(34→58px)`. 단위는 `<small>`(0.4em, `--primary-l`). `report.json` 의 `stats[].accent:true` 하나만 `--accent-ink` |
|
|
268
|
-
| **Problem** | `summary` | `.ex-grid` + `.fig` | `<div class="ex-grid">`, `<figure class="fig">` | 요약 4칸을 2×2 헤어라인 그리드로. 공통 원인은 **인라인 SVG** 1장(`role="img"` + 문장형 `aria-label`) |
|
|
269
|
-
| **Features** | `cases` | `.case` | `<article class="case">` × N | **그리드가 아니라 세로로 쌓인 카드 목록.** 각 카드가 3단 내부 구조(§4.3) |
|
|
270
|
-
| **CTA** | `asks` | `.cta` + `.opens` | `<div class="cta">` + `<article class="open-item">` | **버튼 없음.** Primary 박스 = 요청의 총론, 그 아래 2열 카드 = 요청 각론 |
|
|
271
|
-
| **Closing** | `note` + 고정 | `.appendix` + `footer` | `<div class="appendix">` + `<footer data-rte-standalone>` | **"감사합니다" 80pt 없음.** `--muted` 13.8px 후속 안내 + Primary 2px 상단선 위 12px 푸터 |
|
|
272
|
-
| **Solution** | — | — | — | **미채택.** `.ex-p` 안에서 문제와 해법을 한 단락으로 합침 |
|
|
273
|
-
| **Team** | — | — | — | **미채택** |
|
|
274
|
-
|
|
275
|
-
> **R2.** 아키타입은 **의미 단위의 목록**으로 상속하고 치수는 전부 재산정한다. 해당 없는 아키타입은 버린다. "9개를 다 써야 한다"는 규정은 없다.
|
|
276
|
-
|
|
277
|
-
### 3.1 문서 매체가 새로 요구한 블록
|
|
278
|
-
|
|
279
|
-
아키타입에는 없지만 **없으면 문서가 성립하지 않는다.**
|
|
280
|
-
|
|
281
|
-
| 블록 | 존재 이유 |
|
|
282
|
-
|---|---|
|
|
283
|
-
| `h2.sec` (절 표제 `01` + 제목 + Primary 2px 하단선 + `.sec-lede`) | 슬라이드는 장 전환이 물리적이지만 스크롤은 **연속면**이라 절 경계를 활자로 세워야 한다 |
|
|
284
|
-
| `table.verdict` (전건 요약표) | 카드 N장을 다 읽지 않고도 전모를 보게 하는 색인 |
|
|
285
|
-
| `.legend` (칩 + 설명 + 건수) | **칩을 쓰면 범례가 의무가 된다.** 등급 이름만으로는 "어디까지 확인했다"가 전달되지 않는다 |
|
|
286
|
-
| `.xref` (절 참조 인라인 링크) | `build.py` 의 `link()` 주석 — "검수자는 건과 건을 오가며 대조하므로 손으로 찾게 두면 안 된다" |
|
|
287
|
-
| `ol.enum` (번호 칩 열거) | `chunk()` 주석 — "산문 한 덩어리로 두면 **색을 아무리 칠해도 읽히지 않는다**" |
|
|
288
|
-
|
|
289
|
-
`.xref` 는 원고의 `§3.1` 마커를 링크로 바꾸고 `§` 기호는 버린다. `§` 가 산출물에 남으면 `build.py` 가 `assert` 로 빌드를 세운다 — 링크 변환을 거치지 않은 텍스트가 있다는 뜻이기 때문이다.
|
|
290
|
-
|
|
291
|
-
### 3.2 재방문 장치
|
|
292
|
-
|
|
293
|
-
문서는 **비선형·반복 소비**다. 독자는 3.7 을 읽다 4.2 로 뛰고 다시 돌아온다. 그래서 `.xref` · `table.verdict` · `.legend` · `.case{scroll-margin-top:16px}` 가 전부 필요하다. 슬라이드에는 이런 장치가 하나도 없다(선형 1회 소비이므로).
|
|
294
|
-
|
|
295
|
-
---
|
|
296
|
-
|
|
297
|
-
## 4. 레이아웃 골격
|
|
298
|
-
|
|
299
|
-
### 4.1 `.page` 그리드
|
|
300
|
-
|
|
301
|
-
```css
|
|
302
|
-
.page {
|
|
303
|
-
max-width:1560px; margin:0 auto; padding:0 var(--pad-x) 120px;
|
|
304
|
-
display:grid; grid-template-columns:var(--nav-w) minmax(0,1fr);
|
|
305
|
-
column-gap:clamp(34px,4.2vw,76px); align-items:start;
|
|
306
|
-
}
|
|
307
|
-
@media (max-width:1080px) { .page { grid-template-columns:1fr; } }
|
|
308
|
-
main { min-width:0; }
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
| 선언 | 근거 | 빼면 |
|
|
312
|
-
|---|---|---|
|
|
313
|
-
| `minmax(0,1fr)` | `1fr` 의 기본 min 은 `auto`(=min-content). 긴 URL·`min-width:640px` 표·`nowrap` 셀이 들어오면 열이 콘텐츠 폭까지 부푼다 | **페이지 전체가 가로로 밀린다.** 한 건만 URL 이 길어도 문서 전체가 깨진다 |
|
|
314
|
-
| `main{min-width:0}` | 같은 blowout 을 막는 이중 안전장치 | 위와 같음 |
|
|
315
|
-
| `align-items:start` | 없으면 기본값 `stretch` 가 되어 sticky 목차가 트랙 높이만큼 늘어난다 | **`position:sticky` 가 조용히 무력화된다** — 요소가 이미 전체 높이라 붙을 여지가 없다 |
|
|
316
|
-
| `max-width:1560px` | `.cover-in` 과 **같은 값** | 표지와 본문의 좌우 정렬선이 어긋난다 |
|
|
317
|
-
| `padding-bottom:120px` | 마지막 절이 화면 바닥에 붙지 않게 | 마지막 앵커로 점프해도 스크롤 여유가 없어 제목이 상단에 안 온다 |
|
|
318
|
-
|
|
319
|
-
**`minmax(0,1fr)` 는 예외 없이 반복된다 — `report.css` 에 14곳.** `.cover dl`(4열/2열), `.page`, `.stats`(4열/2열), `h2.sec`, `.ex-grid`, `.legend`(4열/2열), `.dx`, `.ck`, `.ck.one`, `ol.enum li`, `.opens`.
|
|
320
|
-
|
|
321
|
-
> **되돌리면 안 되는 결정.** 모든 다열 그리드는 `minmax(0,1fr)`. `1fr` 로 "정리"하면 표나 URL 이 들어온 순간 페이지가 가로로 밀린다. `qa.py` 의 `(?<![a-z-])width:\s*\d{3,}px` 경고 검사가 같은 문제의 다른 얼굴을 잡는다(lookbehind 가 없으면 `max-width:1080px` 같은 정상 반응형 코드가 전부 걸린다 — 그래서 붙어 있다).
|
|
322
|
-
|
|
323
|
-
### 4.2 고정 사이드 목차
|
|
324
|
-
|
|
325
|
-
```css
|
|
326
|
-
.toc { position:sticky; top:0; max-height:100dvh; overflow-y:auto;
|
|
327
|
-
padding:clamp(30px,3.4vw,46px) 14px 40px 0; scrollbar-width:thin; }
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
| 결정 | 근거 |
|
|
331
|
-
|---|---|
|
|
332
|
-
| 상단 바가 아니라 **좌측 컬럼** | 본문 상단을 먹지 않고, "5절 + 18건" 같은 **긴 목록을 세로로 펼칠 수 있다.** 상단 고정 바에는 18건이 안 들어간다 |
|
|
333
|
-
| `top:0` | 위에 고정 헤더가 없다. 표지는 스크롤과 함께 지나간다 |
|
|
334
|
-
| `max-height:100dvh` + `overflow-y:auto` | 목차가 뷰포트보다 길면 **목차만 따로 스크롤**된다. `dvh`(≠`vh`)는 모바일 주소창 높이 변동에 대응 |
|
|
335
|
-
| 2계층 — `.toc-g .nm`(12.5px/650/`--primary`) + `.toc ol a`(12.4px/`--muted`) | **크기 차이 0.1px.** 사실상 명도 차 하나로만 계층을 표현한다. 들여쓰기·불릿 없음 |
|
|
336
|
-
| 좌측 2px hover 바 | `border-left:2px solid transparent` + `margin-left:-9px` 로 **레이아웃 시프트 없이** hover 시 `--accent` 점등 |
|
|
337
|
-
| 번호는 `--accent-ink`, 이름은 `--primary` | 12px 작은 활자 → Accent 중 4.5:1 등급 강제 |
|
|
338
|
-
| 1080px↓ **static 전환 + `columns:2`** | 좁은 화면에서 sticky 사이드는 본문을 압살한다. `position:static` + Primary 2px 하단선으로 상단 블록화, `columns` 로 세로 길이 절반. 620px↓ `columns:1` |
|
|
339
|
-
| `.toc li{break-inside:avoid}` | `columns` 안에서 한 항목이 단 경계에 걸쳐 갈라지는 것 방지 |
|
|
340
|
-
| `.toc .t{overflow-wrap:anywhere}` | 224px 좁은 열에 대한 **방어적 재선언**(body 에 이미 있는데도 한 번 더) |
|
|
341
|
-
|
|
342
|
-
### 4.3 건 카드의 3단 구성 — 정보설계의 핵심
|
|
343
|
-
|
|
344
|
-
`report.css` 가 구조를 한 줄로 선언한다: `/* 진단 2열 → 조치 전폭 → 확인/요청 2열 */`
|
|
345
|
-
|
|
346
|
-
```
|
|
347
|
-
<article class="case">
|
|
348
|
-
<div class="case-top"> [절번호 칩] [제목 h3] [확인등급 칩] </div>
|
|
349
|
-
<p class="ref"> 제보 원본 보기 ↗ </p> ← source_url 이 있을 때만
|
|
350
|
-
<div class="rows">
|
|
351
|
-
<div class="dx"> [증상 r-sym] [원인 r-cause] </div> ← 1단: 대조 2열
|
|
352
|
-
[조치 r-fix] ← 2단: 결론 전폭
|
|
353
|
-
<div class="ck"> [확인 방법 r-check] [확인 요청 r-open] </div> ← 3단: 독자 액션 2열
|
|
354
|
-
</div>
|
|
355
|
-
</article>
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
**단별 강조 문법 — 색이 아니라 "지면 층위"로 위계를 만든다:**
|
|
359
|
-
|
|
360
|
-
| 단 | 블록 | 배경 | 좌측 바 | 태그 색 | 뜻 |
|
|
361
|
-
|---|---|---|---|---|---|
|
|
362
|
-
| 1 진단 | `.r-sym` `.r-cause` | `--surface` (기본 `.row`) | 없음 | `--primary` | **전용 스타일이 없다** = 배경 정보 |
|
|
363
|
-
| 2 조치 | `.r-fix` | `--paper` (한 단 **밝게**) | inset 3px `--primary` | `--primary` | 우리가 한 일 |
|
|
364
|
-
| 3-a 확인 | `.r-check` | `--primary-bg` | 없음 | `--primary` | 고객이 확인할 방법 |
|
|
365
|
-
| 3-b 요청 | `.r-open` | `--accent-bg` | inset 3px `--accent` | `--accent-ink` | **고객 액션 = Accent 독점** |
|
|
366
|
-
|
|
367
|
-
**`.r-sym` / `.r-cause` 는 CSS 에 선언 자체가 없다.** `build.py` 의 `case_html()` 에만 존재하고 기본 `.row` 로 떨어진다. **강조를 뺀 것이 곧 설계다. CSS 에 없는 건 누락이 아니다.**
|
|
8
|
+
`build_gazette()` 가 `styles(..., tokens=False)` 로 부른다(`private/builder/build.py:441`).
|
|
9
|
+
팔레트에서 유도한 토큰이 문서에 아예 안 실린다.
|
|
368
10
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
| 왜 진단은 2열인가 | 증상과 원인은 **대구(對句)**다. 나란히 놓으면 "이렇게 보였고 → 실은 이래서였다"가 한눈에 대조된다. 세로로 쌓으면 인과가 아니라 나열로 읽힌다 |
|
|
372
|
-
| 왜 조치만 전폭인가 | 카드의 **결론이자 가장 긴 서술**이다. 배경을 `--surface` 에서 `--paper` 로 한 단 **올리고**(밝게) 3px 바를 세워 이 블록만 지면에서 떠 있게 한다. **색을 더 칠한 게 아니라 한 단 더 밝게 만든 것이 요령** |
|
|
373
|
-
| 왜 확인/요청이 2열인가 | 둘 다 **독자의 할 일**이다. "이대로 확인하시면 되고 / 이건 판단이 필요합니다"가 대비된다 |
|
|
374
|
-
| 요청이 없는 건은? | `build.py` 가 개수를 세어 `.ck.one` 을 붙여 1열이 된다. 빈칸이 생기지 않는다 |
|
|
375
|
-
|
|
376
|
-
**구조 고정은 코드로 검사한다.** `qa.py` 가 `<article class="case">` 개수 `n` 을 세고, `class="num"` · `r-sym` · `r-cause` · `r-fix` · `r-check` · 등급 칩이 각각 `n` 개인지 확인한다(예시 프로젝트: 전부 18/18). 등급 칩은 `<div class="case-top">` 구간 **안에서만** 센다 — 범례·판정표의 칩이 애초에 집계에 안 들어오므로 뺄셈이 필요 없다. `qa.py` 는 `cfg['grades']` 를 읽지 않는다.
|
|
377
|
-
|
|
378
|
-
**`.r-open` 만 검사 대상이 아니다** — 확인 요청이 없는 건이 있을 수 있으므로 개수가 카드 수와 같을 필요가 없다.
|
|
379
|
-
|
|
380
|
-
> **R3.** 반복 카드는 **[대조 2열] → [결론 전폭] → [독자 액션 2열]** 3단으로 짠다. 위계는 색 추가가 아니라 **지면 밝기 한 단 + 3px inset 바**로 만든다. 강조하지 않을 블록은 전용 클래스를 **선언하지 않는 것**으로 표현한다. 그리고 모든 카드가 같은 블록 수를 갖는지 빌드 후 센다.
|
|
381
|
-
|
|
382
|
-
### 4.4 헤어라인 그리드 관용구 (3곳 반복)
|
|
383
|
-
|
|
384
|
-
```css
|
|
385
|
-
gap:1px; background:var(--rule); /* 부모 */
|
|
386
|
-
background:var(--paper); /* 자식 */
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
`.stats` · `.ex-grid` · `.legend`. 바깥 테두리는 `.stats` 가 `border-block`(위아래만), 나머지는 `border`(사방).
|
|
11
|
+
서버도 팔레트 유도기를 일부러 뺐다 — `report_publish.php:867-869` 이 `palette.py` 를 지문 목록에서 제외한다.
|
|
12
|
+
관보는 자기 색 체계를 통째로 갖는다(`gazette.css` 에 `--krds-primary` 41개, `--accent` 0개).
|
|
390
13
|
|
|
391
|
-
|
|
392
|
-
|---|---|---|
|
|
393
|
-
| 셀 border 대신 **부모 `gap:1px` + 부모 배경색** | 셀마다 border 를 주면 (a) 인접 경계가 2px 로 겹치고, (b) 반응형으로 열 수가 바뀔 때(4→2→1) 어느 변에 border 를 줄지 매번 다시 계산해야 한다 | 열 수가 바뀔 때마다 `:nth-child` 규칙을 새로 쓰게 되고, 그때마다 버그가 난다 |
|
|
394
|
-
| **패딩 비대칭** — 오른쪽만 두고 왼쪽은 0 | `.stat{padding:… 4px … 0}`, `table.verdict th/td{padding:… 14px … 0}` + `:first-child{padding-left:14px}` | 숫자·글자가 각 열의 좌측 경계선에서 밀려 세로 정렬선이 죽는다 |
|
|
14
|
+
**포털 발행에 팔레트 인자는 없다.** 고를 자리가 없다.
|
|
395
15
|
|
|
396
|
-
|
|
16
|
+
## 대신 어디를 보는가
|
|
397
17
|
|
|
398
|
-
|
|
|
399
|
-
|---|---|---|
|
|
400
|
-
| 1080 | `.page` / `.toc` / `.toc ol` | 사이드 목차 해제 → 상단 블록 + 2단 컬럼 |
|
|
401
|
-
| 900 | `.ex-grid` `.legend` `.opens` | 2열→1열 / 4열→2열 |
|
|
402
|
-
| 860 | `.dx` `.ck` | 카드 내부 2열→1열 |
|
|
403
|
-
| 820 | `.cover dl` | 4열→2열 |
|
|
404
|
-
| 760 | `.stats` | 4열→2열 |
|
|
405
|
-
| 620 | `.toc ol` | 2단→1단 |
|
|
406
|
-
| 560 | `.legend` | 2열→1열 |
|
|
407
|
-
|
|
408
|
-
전역 3단계(sm/md/lg)가 아니라 **각 블록이 자기 콘텐츠가 못 견디는 폭**에서 무너진다. 4열 지표(760)와 4열 범례(900)가 다른 이유는 범례 셀에는 설명 문장이 들어가고 지표 셀에는 숫자만 들어가기 때문이다.
|
|
409
|
-
|
|
410
|
-
> **일반화**: 전역 브레이크포인트 상수를 만들지 마라. 블록마다 "이 콘텐츠가 이 폭에서 읽히는가"를 따로 판단한다.
|
|
411
|
-
|
|
412
|
-
### 4.6 활자 폭 사다리 — `ch` 는 한국어 글자 수가 아니다
|
|
413
|
-
|
|
414
|
-
| 요소 | `max-width` |
|
|
18
|
+
| 찾던 것 | 지금 있는 곳 |
|
|
415
19
|
|---|---|
|
|
416
|
-
|
|
|
417
|
-
|
|
|
418
|
-
|
|
|
419
|
-
|
|
|
420
|
-
| `.cta p` | `78ch` |
|
|
421
|
-
|
|
422
|
-
**`1ch` = 숫자 `0` 의 폭이다.** Pretendard 는 숫자보다 한글이 훨씬 넓어서 `Nch` 는 한글로 대략 `0.7N` 자다. **영문 감각으로 `65ch` 를 잡으면 한국어로는 45자가 되어 지나치게 좁아진다.**
|
|
423
|
-
|
|
424
|
-
폰트를 바꾸면 이 비율도 바뀐다. 브라우저에서 다시 재라(`font-embedding.md` 의 확인 스니펫과 같은 방식):
|
|
425
|
-
|
|
426
|
-
```js
|
|
427
|
-
const w = (s) => { const c=document.createElement('canvas').getContext('2d');
|
|
428
|
-
c.font='64px Pretendard'; return c.measureText(s).width; };
|
|
429
|
-
w('0') / 64 // 1ch 의 em 값
|
|
430
|
-
w('가') / w('0') // 한글 1자 = 몇 ch 인가
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
---
|
|
434
|
-
|
|
435
|
-
## 5. 한국어 조판
|
|
436
|
-
|
|
437
|
-
### 5.1 `word-break:keep-all` + `overflow-wrap:anywhere` 는 항상 짝
|
|
438
|
-
|
|
439
|
-
```css
|
|
440
|
-
/* 한국어는 어절 단위로 끊어야 읽힌다. keep-all 이 없으면 "마무리되 / 는" 처럼
|
|
441
|
-
낱말 한가운데가 갈라진다. 긴 영문·URL 만 예외로 강제 개행한다. */
|
|
442
|
-
word-break:keep-all; overflow-wrap:anywhere;
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
| 선언 | 하는 일 | 단독으로 쓰면 |
|
|
446
|
-
|---|---|---|
|
|
447
|
-
| `word-break:keep-all` | CJK 를 **음절 단위로 못 끊게** 막는다 → 줄바꿈이 어절(띄어쓰기) 경계에서만 | 공백 없는 긴 문자열(URL, 긴 식별자)이 **절대 안 끊겨 컨테이너를 뚫는다** → 가로 스크롤 |
|
|
448
|
-
| `overflow-wrap:anywhere` | 한 줄에 안 들어가는 **끊을 수 없는** 문자열만 예외적으로 강제 개행 | keep-all 이 없어 한국어가 음절 단위로 갈라진다 |
|
|
449
|
-
|
|
450
|
-
**keep-all 이 "원칙", anywhere 가 "탈출구"다.** 원칙만 있으면 레이아웃이 깨지고, 탈출구만 있으면 한국어가 깨진다.
|
|
451
|
-
|
|
452
|
-
**`anywhere` 를 `break-word` 로 바꾸면 안 된다.** `overflow-wrap:anywhere` 는 **min-content 크기 계산에 반영**되지만 `break-word` 는 반영되지 않는다. 즉 `anywhere` 여야 §4.1 의 `minmax(0,1fr)` 와 함께 작동해 그리드 열이 실제로 줄어든다. `break-word` 로 "정리"하면 그리드가 다시 부푼다.
|
|
453
|
-
|
|
454
|
-
관련 선언:
|
|
455
|
-
|
|
456
|
-
| 선언 | 이유 |
|
|
457
|
-
|---|---|
|
|
458
|
-
| `h1,h2,h3,h4{text-wrap:balance}` | 제목 줄 길이 균등화. 한국어 제목은 어절이 길어 마지막 줄에 한 어절만 남는 고아줄이 잘 생긴다 |
|
|
459
|
-
| 제목 공통 `letter-spacing:-.022em` | 큰 헤딩은 자간을 좁힌다 |
|
|
460
|
-
| 큰 표제는 `-.03em ~ -.04em` | `.cover h1`(-.035) `.stat .v`(-.04) `h2.sec .no`(-.03) — **클수록 더 좁힌다** |
|
|
461
|
-
| 본문 `line-height:1.7` / `.val p`·`ol.enum` `1.78` | 슬라이드 권고(1.4–1.6)보다 넓다. 한국어 산문을 화면에서 길게 읽는 전제 |
|
|
462
|
-
| `white-space:nowrap` (칩·표 좁은 열) | 칩 라벨과 표의 좁은 열이 어절 단위로도 갈라지지 않게. keep-all 보다 강한 국소 규칙 |
|
|
463
|
-
|
|
464
|
-
### 5.2 라벨 활자 문법 — 한글에는 대문자가 없으므로 자간으로 위계를 만든다
|
|
465
|
-
|
|
466
|
-
동일한 문법이 6곳에서 반복된다.
|
|
467
|
-
|
|
468
|
-
| 요소 | 크기 | weight | letter-spacing | 색 |
|
|
469
|
-
|---|---|---|---|---|
|
|
470
|
-
| `.cover .kicker` | 11.5px | 600 | `.24em` | `--accent-on-primary` |
|
|
471
|
-
| `.toc-h` | 10.5px | 700 | `.22em` | `--primary` |
|
|
472
|
-
| `.fig-cap` | 10.5px | 700 | `.20em` | `--primary` |
|
|
473
|
-
| `.cover dt` | 10.5px | 600 | `.18em` | `--accent-on-primary` |
|
|
474
|
-
| `.row > .tag` | 10.5px | 700 | `.17em` | `--primary` |
|
|
475
|
-
| `table.verdict th` | 11px | 650 | `.13em` | `--on-primary` |
|
|
476
|
-
|
|
477
|
-
여섯 곳 전부 `text-transform:uppercase` 가 붙어 있지만 **한글에는 대문자가 없어 아무 효과가 없다**(라틴 라벨에서만 동작한다). 실제로 위계를 만드는 것은 **작은 크기 + 굵은 weight + 넓은 자간(.13–.24em)** 세 가지다.
|
|
478
|
-
|
|
479
|
-
**본문 제목과 정확히 반대 방향이다:**
|
|
480
|
-
|
|
481
|
-
```
|
|
482
|
-
큰 활자(26–62px) → 자간을 좁힌다 (-.022em ~ -.04em)
|
|
483
|
-
작은 라벨(10.5–11.5px) → 자간을 넓힌다 (+.13em ~ +.24em)
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
> **R4.** 한글 라벨의 위계를 `text-transform` 에 의존하지 마라. 자간 부호는 크기에 따라 뒤집힌다.
|
|
487
|
-
|
|
488
|
-
### 5.3 `tabular-nums` — 끄고 시작해서 필요한 곳만 켠다
|
|
489
|
-
|
|
490
|
-
```css
|
|
491
|
-
body { font-feature-settings:"tnum" 0; } /* 전역 OFF */
|
|
492
|
-
.tnum { font-variant-numeric:tabular-nums; } /* 유틸리티 */
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
| 규칙 | 근거 |
|
|
496
|
-
|---|---|
|
|
497
|
-
| **전역 OFF 가 출발점** | 고정폭 숫자는 폭이 균일한 대신 자간이 어색해 산문 속에서 튄다. 한국어 본문에 섞인 "18건", "8월 9일"은 **비례 숫자**가 자연스럽다 |
|
|
498
|
-
| **켜는 곳 = 숫자가 세로로 정렬되어야 하는 곳만** | `report.css` 에 11곳 |
|
|
499
|
-
|
|
500
|
-
켜는 10곳: `.cover dd` · `.toc-g .no` · `.toc .i` · `.stat .v` · `h2.sec .no` · `.lg .ct` · `table.verdict .c-sec` · `.case .num` · `ol.enum .n` · `.xref`. (`.stat .v small` 은 단위 글자라 숫자가 없어 켜지 않는다. 별도로 유틸 클래스 `.tnum` 이 있다.)
|
|
501
|
-
|
|
502
|
-
`.tnum` 유틸리티는 선언돼 있지만 컴포넌트 CSS 가 직접 켜므로 실사용은 없다. 유틸리티에 의존하면 새 컴포넌트에서 빠뜨린다.
|
|
503
|
-
|
|
504
|
-
**이 11곳이 폰트 서브셋에서 `tnum` 피처를 반드시 남기는 이유다** — 피처를 버리면 **CSS 는 그대로인데 정렬만 조용히 죽는다.** 상세는 `font-embedding.md`.
|
|
505
|
-
|
|
506
|
-
---
|
|
507
|
-
|
|
508
|
-
## 6. 다이어그램 (인라인 SVG)
|
|
509
|
-
|
|
510
|
-
`report.json` 의 `figure` 가 가리키는 HTML 조각을 `build.py` 가 그대로 삽입한다. 예시: `figure.html`.
|
|
511
|
-
|
|
512
|
-
| 규칙 | 근거 | 어기면 |
|
|
513
|
-
|---|---|---|
|
|
514
|
-
| **색은 전부 `var(--토큰)`** | 인라인 SVG 는 문서의 CSS 사용자 정의 속성을 상속한다. `fill="var(--primary)"` 가 그대로 동작한다 | 팔레트 교체가 "JSON 한 줄"에서 "JSON + SVG 전면 수정"으로 늘어난다 |
|
|
515
|
-
| `role="img"` + **문장형** `aria-label` | 도형은 스크린리더에 아무것도 아니다. 라벨은 "무엇이 그려졌나"가 아니라 **"무엇을 말하는 그림인가"** 를 한 문장으로 | 스크린리더 사용자에게 이 절이 통째로 비어 보인다 |
|
|
516
|
-
| `viewBox` + `.fig svg{width:100%;height:auto}` | 좁은 화면에서 비례 축소 | 고정 px 폭이면 §4.1 의 blowout 을 유발한다 |
|
|
517
|
-
| **배경/글자 짝을 §1.4 표에서 고른다** | `audit()` 은 **토큰 쌍 17개만** 잰다. SVG 안에서 어떤 토큰을 어떤 토큰 위에 얹었는지는 검사하지 않는다 | 대비 미달이 검사를 통과한 채 나간다 |
|
|
518
|
-
|
|
519
|
-
**마지막 항목은 실제 사례가 있다.** 예시 `figure.html` 의 "75%" 텍스트는 `fill="var(--accent-chip)"` 를 `fill="var(--accent-bg)"` 사각형 위에 얹는다. `korean-navy` 에서 `#C9A961` on `#FAF6EF` = **2.09:1** 이다. `--accent-bg` 위의 글자는 `--accent-ink`(4.58:1) 또는 `--ink`(17.11:1) 여야 한다. 새 다이어그램을 그릴 때 이 짝을 복사하지 마라.
|
|
520
|
-
|
|
521
|
-
권장 짝:
|
|
522
|
-
|
|
523
|
-
| 배경 | 그 위 글자 |
|
|
524
|
-
|---|---|
|
|
525
|
-
| `--primary` | `--on-primary` (부제는 `--on-primary-soft`) |
|
|
526
|
-
| `--primary-bg` | `--primary` 또는 `--ink` (보조는 `--muted`) |
|
|
527
|
-
| `--accent-chip` | `--accent-on` (**반드시 이 쌍**) |
|
|
528
|
-
| `--accent-bg` | `--accent-ink` 또는 `--ink` |
|
|
529
|
-
| `--paper` / `--surface` | `--ink` / `--muted` / `--primary` |
|
|
530
|
-
| 연결선 | `--primary-hair` |
|
|
531
|
-
|
|
532
|
-
---
|
|
533
|
-
|
|
534
|
-
## 7. 편집 레이어와의 경계
|
|
535
|
-
|
|
536
|
-
편집기 자체는 `wysiwyg.md` 가 다룬다. **디자인 쪽 계약만** 여기 적는다.
|
|
537
|
-
|
|
538
|
-
### 7.1 도구는 자기 중립 팔레트를 쓴다
|
|
539
|
-
|
|
540
|
-
`editor.css` 주석: `/* 런타임 UI — 문서 활자와 섞이지 않게 계열을 분리 */`
|
|
541
|
-
|
|
542
|
-
편집기 크롬은 `#1b1f22` `#c8ccd0` `#dfe3e6` `#f2f4f5` `#8b9095` 같은 **팔레트 밖 중립색**을 쓴다. 문서 토큰은 딱 네 개만 빌려 간다 — `--primary`(포커스 아웃라인), `--primary-hair`(hover 아웃라인), `--accent-ink`(미결·경고 UI, 8회), `--accent-bg`(내부 전용 블록 빗금).
|
|
543
|
-
|
|
544
|
-
**어기면**: 인쇄·스크린샷에서 툴바 색이 콘텐츠처럼 보인다. 독자가 도구를 문서 내용으로 오독한다.
|
|
545
|
-
|
|
546
|
-
### 7.2 색 검사는 편집기 CSS 구간만 도려낸다 — `split` 이 아니라 **구간**으로
|
|
547
|
-
|
|
548
|
-
```python
|
|
549
|
-
EDITOR_CSS_MARK = '/* ── 편집 레이어'
|
|
550
|
-
i, j = doc.find(EDITOR_CSS_MARK), doc.find('</style>', doc.find(EDITOR_CSS_MARK))
|
|
551
|
-
scan = doc[:i] + doc[j:]
|
|
552
|
-
scan = re.sub(r'base64,[A-Za-z0-9+/=]+', 'base64,', scan) # 폰트 페이로드만 지운다
|
|
553
|
-
# ↑ split 으로 자르면 문서 대부분이 함께 날아가 검사가 무의미해진다. 실제로 그랬다.
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
`split(mark)[0]` 를 쓰면 마커 **뒤 전부**가 버려진다. 편집기 CSS 는 `<head>` 안에 있으므로 `<body>` 마크업·인라인 SVG·스크립트가 통째로 검사에서 빠진다. **검사가 통과했는데 실제로는 아무것도 검사하지 않은 상태** — 가장 나쁜 실패다. 지금은 시작·끝 두 지점으로 잘라 붙이고, 폰트 base64 페이로드만 따로 지운다(400KB 의 무작위 문자열에서 `#[0-9a-f]{6}` 이 무수히 오탐되기 때문).
|
|
557
|
-
|
|
558
|
-
마커를 못 찾으면 `warn` 을 내고 전체를 검사한다. **조용히 통과하지 않는다.**
|
|
559
|
-
|
|
560
|
-
### 7.3 편집기가 삽입하는 클래스는 문서 스타일시트에 전역 정의가 있어야 한다
|
|
561
|
-
|
|
562
|
-
```css
|
|
563
|
-
/* 편집기가 삽입할 수 있는 의미 클래스는 **문서 스타일시트에 전역 정의**가 있어야 한다.
|
|
564
|
-
.ev 는 편집기 "강조" 버튼이 만드는 클래스인데 전역 정의가 없으면 버튼이 먹통으로 보인다. */
|
|
565
|
-
.ev { background:var(--accent-bg); padding:.06em .22em; }
|
|
566
|
-
.ref .ev { background:none; color:var(--muted); padding:0; }
|
|
567
|
-
```
|
|
568
|
-
|
|
569
|
-
`editor.js` 의 `wrapSelection()` 이 `el.className = 'ev'` 로 만든다. 정의가 `editor.css` 에만 있으면 **고객본에서 그 강조가 통째로 사라진다**(고객본은 `data-rte-src` 스타일을 들어내므로). 편집기가 만드는 의미 클래스는 항상 `report.css` 쪽에 둔다.
|
|
570
|
-
|
|
571
|
-
### 7.4 자동 재생성되는 표에는 행 편집이 붙지 않는다
|
|
572
|
-
|
|
573
|
-
`build.py` 는 판정표에 `data-rte-derived` 를 붙인다. `editor.js` 의 `isTable` 판정이 이 속성을 제외한다.
|
|
574
|
-
|
|
575
|
-
```js
|
|
576
|
-
// 자동 재생성되는 표(data-rte-derived)에는 행 편집을 붙이지 않는다.
|
|
577
|
-
var isTable = !!tbl && !tbl.hasAttribute('data-rte-derived');
|
|
578
|
-
```
|
|
579
|
-
|
|
580
|
-
판정표는 본문 카드에서 매번 다시 만들어지므로, 손으로 행을 넣어봐야 다음 재생성에서 사라진다. 행 추가 폴백도 `tbody tr:last-child` 를 먼저 집는다 — `tr:last-of-type` 만 쓰면 `thead` 의 헤더 행을 복제한다.
|
|
581
|
-
|
|
582
|
-
### 7.5 고객본 내보내기 표식
|
|
583
|
-
|
|
584
|
-
`build.py` 는 편집 레이어를 `<!-- REPORT-EDITOR:BEGIN/END -->` 로 감싸고 `<style data-rte-src>` / `<script data-rte-src>` 를 붙인다. 주석 원문:
|
|
585
|
-
|
|
586
|
-
> editor.js 의 고객본 내보내기가 **정확히 이 표식을 찾아 들어낸다.** 표식을 안 붙이면 내보내기 코드는 조용히 아무것도 못 지우고, 고객본에 편집기 30KB 가 그대로 실린다.
|
|
587
|
-
|
|
588
|
-
`qa.py` 가 같은 규칙으로 **내보내기를 시뮬레이션**해 잔존 문자열(`data-rte-ui`, `showSaveFilePicker`, `syncVerdict`, `data-report-edit`, `data-internal="true"`)을 검사하고, 고객본 크기가 원본의 98% 미만인지도 본다. 예시 프로젝트: 523,464B → 고객본 491,765B(94%).
|
|
589
|
-
|
|
590
|
-
---
|
|
591
|
-
|
|
592
|
-
## 8. 인쇄
|
|
593
|
-
|
|
594
|
-
`report.css` 끝의 `@media print` 블록. 규칙과 이유:
|
|
595
|
-
|
|
596
|
-
| # | 규칙 | 화면 | 인쇄 | 왜 |
|
|
597
|
-
|---|---|---|---|---|
|
|
598
|
-
| 1 | `body{font-size:10.4pt}` | 16px | 10.4pt | 물리 크기 기준 재산정 |
|
|
599
|
-
| 2 | `.cover{padding:24pt 0 0; background:#fff; color:var(--ink)}` | Primary 전면 | **흰 바탕** | 전면 색 블록은 잉크를 너무 먹는다. 유일하게 하드코딩된 `#fff` 인 이유는 팔레트와 무관하게 **종이는 흰색**이기 때문 |
|
|
600
|
-
| 3 | `.cover::before{background:var(--primary); width:4pt}` | Accent 5px | **Primary 4pt** | 바탕이 흰색이 되었으니 강조선은 Primary 가 맞다. 아키타입 원문(좌측 4pt Primary 강조선)으로 복귀 |
|
|
601
|
-
| 4 | `.cover h1{font-size:26pt}` | clamp(32→62px) | 26pt | 지면 폭 기준 재산정 |
|
|
602
|
-
| 5 | `.cover .sub{color:var(--muted)}` · `.cover dd{color:var(--ink)}` | `--on-primary-soft` | muted / ink | `#929DAE` 는 흰 바탕에서 2.4:1. **필수 교체** |
|
|
603
|
-
| 6 | **`.cover .kicker` · `.cover dt` → `var(--accent-ink)`** | `--accent-on-primary` | **`--accent-ink`** | `--accent-on-primary` 는 *어두운 바탕 위* 4.5:1 로 유도된 값이다. 흰 바탕에서는 2.25:1 |
|
|
604
|
-
| 7 | `.cover dl{border-top-color:var(--rule)}` | `rgba(255,255,255,.22)` | `--rule` | 흰 바탕에서 흰 알파 선은 사라진다 |
|
|
605
|
-
| 8 | `.page{max-width:none; padding:0; grid-template-columns:1fr}` | 2열 1560px | 1열 전폭 | 용지 폭이 곧 판형. 좌우 여백은 프린터 마진이 담당 |
|
|
606
|
-
| 9 | `.toc{position:static; break-after:page}` + `.toc ol{columns:2}` | sticky 사이드 | **독립 목차 페이지 1장** | 종이는 스크롤이 없다 → Agenda 아키타입이 원래 형태로 복귀 |
|
|
607
|
-
| 10 | `.case,.open-item,.fig,.ex-p,.stat,.cta{break-inside:avoid}` | — | 분할 금지 | 카드가 페이지 경계에서 잘리면 §4.3 의 3단 대조가 무너진다 |
|
|
608
|
-
| 11 | `h2.sec{break-after:avoid}` | — | 고아 표제 방지 | 절 표제만 페이지 끝에 남는 것 방지 |
|
|
609
|
-
| 12 | `a{color:inherit}` | Primary 링크 | 본문색 | 종이에서 색 링크는 정보가 아니라 노이즈 |
|
|
610
|
-
| 13 | `.ref a::after{content:" (" attr(href) ")"; font-size:7.5pt; color:var(--muted); word-break:break-all}` | — | **URL 을 괄호로 펼침** | 종이에서 하이퍼링크는 죽는다. `break-all` 로 **§5.1 의 keep-all 을 이 요소에서만 뒤집는다** — URL 은 어절이 없어 keep-all 이면 지면을 뚫는다 |
|
|
611
|
-
| 14 | `editor.css`: `@media print{[data-rte-ui]{display:none !important}}` | 편집 툴바 | 제거 | 도구 UI 는 문서가 아니다 |
|
|
612
|
-
|
|
613
|
-
### 8.1 무엇을 뒤집지 **않았는가**도 규칙이다
|
|
614
|
-
|
|
615
|
-
인쇄에서 흰 바탕으로 뒤집히는 것은 **`.cover` 뿐이다.** `.cta`(Primary 박스), `table.verdict th`, `.m-live` 칩, `.case .num` 칩은 그대로 어두운 채로 인쇄된다. 기준은 **전면 배경(한 페이지 전체를 덮는 것)만 뒤집고 국소 블록은 둔다** 이다. 국소 블록까지 뒤집으면 칩의 농도 위계(§2.3)가 종이에서 통째로 사라진다.
|
|
616
|
-
|
|
617
|
-
> **R5.** 인쇄 규칙은 최소 5종을 포함한다 — (1) **전면 배경 반전 + 그에 따른 글자색 토큰 재지정**, (2) 다열 레이아웃 1열화, (3) 목차 `break-after:page`, (4) 반복 카드·도표 `break-inside:avoid` + 표제 `break-after:avoid`, (5) 링크 URL `::after` 펼침 + **해당 요소만** `break-all`. 특히 (1)의 "글자색 토큰 재지정"을 빠뜨리면 **대비 붕괴가 화면에서는 안 보인 채 종이에서만 터진다.**
|
|
618
|
-
|
|
619
|
-
---
|
|
620
|
-
|
|
621
|
-
## 9. 새 팔레트 추가 절차
|
|
622
|
-
|
|
623
|
-
### 9.1 이미 있는 팔레트를 고를 때
|
|
624
|
-
|
|
625
|
-
`report.json` 한 줄만 바꾼다. 그게 전부다.
|
|
626
|
-
|
|
627
|
-
```json
|
|
628
|
-
"palette": "claude-mono"
|
|
629
|
-
```
|
|
630
|
-
|
|
631
|
-
등록된 9종: `claude-classic` · `claude-coral` · `claude-mono` · `claude-blue-calm` · `claude-green` · `korean-brick` · `korean-navy` · `korean-sage` · `high-contrast`. 독자별 선택 가이드는 `SKILL.md` 3단계 표.
|
|
632
|
-
|
|
633
|
-
### 9.2 고객 브랜드 색으로 새로 만들 때
|
|
634
|
-
|
|
635
|
-
`build.py` 는 `palette` 값이 **문자열이면 등록 팔레트, 딕셔너리면 그대로** 쓴다. 이름을 등록하지 않고 `report.json` 안에서 끝낼 수 있다.
|
|
636
|
-
|
|
637
|
-
```json
|
|
638
|
-
"palette": { "primary":"#B7E400", "secondary":"#FF00A8",
|
|
639
|
-
"bg":"#FFFFFF", "surface":"#F7F7F4", "ink":"#111111", "mid":"#B0AEA5" }
|
|
640
|
-
```
|
|
641
|
-
|
|
642
|
-
`bg` `surface` `ink` `mid` 는 생략 가능하다(기본값 `#FFFFFF` / `#FAF9F5` / `#141413` / `#B0AEA5`).
|
|
643
|
-
|
|
644
|
-
**여섯 값을 정하는 법:**
|
|
645
|
-
|
|
646
|
-
| 키 | 무엇을 넣는가 | 주의 |
|
|
647
|
-
|---|---|---|
|
|
648
|
-
| `primary` | 브랜드 주색 **원본 그대로** | 밝아도 된다. `darken_to` 가 글자용으로 내린다 |
|
|
649
|
-
| `secondary` | 강조/포인트 색 **원본 그대로** | 배경·괘선으로 쓰이므로 채도가 높아도 된다 |
|
|
650
|
-
| `bg` | **본문 지면.** 보통 흰색 | 팔레트 표의 "Background" 를 이름만 보고 넣지 마라 — **역할로 매핑**한다 |
|
|
651
|
-
| `surface` | 한 단계 낮은 지면 | `bg` 보다 어두워야 헤어라인 그리드가 보인다 |
|
|
652
|
-
| `ink` | 본문 글자 | |
|
|
653
|
-
| `mid` | 팔레트의 회색 | 어차피 쓰이지 않는다. 계보 기록용 |
|
|
654
|
-
|
|
655
|
-
`bg`/`surface` 는 **이름이 아니라 역할로** 매핑한다. 상위 팔레트 표에는 Background=Beige, Surface=White 인 것이 있는데, 이 문서 체계는 `paper`(본문 지면)=White, `surface`(낮은 층)=Cream 이다. 이름대로 넣으면 헤어라인 그리드의 층위가 뒤집힌다.
|
|
656
|
-
|
|
657
|
-
### 9.3 절차 6단계
|
|
658
|
-
|
|
659
|
-
```
|
|
660
|
-
1. palette.py 의 PALETTES 에 이름을 추가하거나, report.json 에 인라인 딕셔너리를 쓴다
|
|
661
|
-
2. python3 assets/palette.py <name> → "OK" 여야 한다
|
|
662
|
-
3. 실패하면 **색을 손으로 고치지 말고 derive() 를 고친다**
|
|
663
|
-
4. python3 assets/palette.py → 9개 팔레트 **전부** 다시 OK 인지 확인
|
|
664
|
-
5. python3 assets/build.py report.json → assert 가 한 번 더 막는다
|
|
665
|
-
6. python3 assets/qa.py report.json → 실패 0. 그리고 **브라우저로 열어 본다**
|
|
666
|
-
```
|
|
667
|
-
|
|
668
|
-
**3번이 이 절차의 핵심이다.** 한 팔레트만 손으로 맞추면 유도기가 그 팔레트에 대해서만 틀린 상태로 남고, 다음 팔레트에서 같은 실패가 반복된다. 유도 규칙을 고쳤으면 **4번으로 회귀를 확인한다** — `darken_to` 의 목표값 하나만 건드려도 다른 팔레트가 무너진다.
|
|
669
|
-
|
|
670
|
-
6번의 "브라우저로 열어 본다"는 생략 가능한 항목이 아니다. `audit()` 은 **17쌍만** 잰다. 색상 자체가 서로 가까워 구분이 안 되는 문제(예: Primary 와 Secondary 의 명도가 비슷한 팔레트에서 `.r-fix` 와 `.r-open` 이 구분되지 않는 것)는 대비 수치로는 잡히지 않는다. 색상 대비가 부족하면 **명도 대비와 형태**로 보완한다 — inset 바를 3px→5px, `--accent-bg` 를 더 진하게, `.stat.is-open` 에 상단 괘선 추가 등.
|
|
671
|
-
|
|
672
|
-
### 9.4 다크 팔레트는 쓸 수 없다
|
|
673
|
-
|
|
674
|
-
`derive()` 는 **밝은 배경을 전제**한다. `darken_to` 는 흰 바탕 기준으로 내리고, `mix(·, '#FFFFFF', t)` 는 흰쪽으로 올린다. 어두운 `bg` 를 넣으면 `audit()` 이 무더기로 실패한다. 실측(Dark Editorial 형태의 6색을 넣었을 때):
|
|
675
|
-
|
|
676
|
-
```
|
|
677
|
-
■ dark-editorial 실패 8
|
|
678
|
-
❌ 본문 ink/primary-bg 1.14 < 4.5 ❌ 보조 muted/paper 1.14 < 4.5
|
|
679
|
-
❌ 본문 ink/accent-bg 1.13 < 4.5 ❌ 링크 primary/paper 3.69 < 4.5
|
|
680
|
-
…
|
|
681
|
-
```
|
|
682
|
-
|
|
683
|
-
**빌드가 `assert` 로 막으므로 잘못된 문서가 나가지는 않는다.** 다크가 필요하면 `derive()` 를 배경 밝기에 따라 분기시키는 작업이 선행되어야 한다 — 지금은 없다. 그리고 이 산출물은 인쇄·PDF 전달을 전제하므로 다크 배경은 애초에 맞지 않는다.
|
|
684
|
-
|
|
685
|
-
> **R6.** 팔레트 교체는 "hex 두 개 갈아끼우기"가 아니지만, **이 시스템에서는 진짜로 한 줄이다** — 유도기가 나머지를 한다. 대신 유도기를 고칠 때는 9개 전부를 회귀 확인한다.
|
|
686
|
-
|
|
687
|
-
---
|
|
688
|
-
|
|
689
|
-
## 10. 남은 부채
|
|
690
|
-
|
|
691
|
-
| # | 부채 | 위치 | 영향 |
|
|
692
|
-
|---|---|---|---|
|
|
693
|
-
| a | `border-bottom-color:rgba(111,87,33,.35)` — `korean-navy` 의 옛 Gold 를 35% 로 하드코딩 | `report.css` 의 `.r-open .xref, .orefs .xref` | 다른 팔레트에서 강조 블록 안 링크 밑줄만 **갈색으로 남는다.** `qa.py` 가 "rgb/rgba 리터럴" 경고로 잡는다 |
|
|
694
|
-
| b | `--primary-d` 참조 0회 | `palette.py derive()` | 용도를 정하거나 지운다 |
|
|
695
|
-
| c | 예시 `figure.html` 의 `--accent-chip` on `--accent-bg` (2.09:1) | 프로젝트 파일 | 새 다이어그램이 이 짝을 복사하면 대비 미달이 검사를 통과한다 (§6) |
|
|
696
|
-
| d | `@page` 규칙(용지 크기·마진) 없음 | `report.css` `@media print` | 프린터 기본값에 의존 |
|
|
697
|
-
| e | `thead{display:table-header-group}` 없음 | 같은 곳 | 긴 판정표가 페이지를 넘어가면 **둘째 장부터 헤더가 없다** |
|
|
698
|
-
| f | `table.verdict{min-width:640px}` 의 인쇄 대응 없음 | 같은 곳 | 종이에는 가로 스크롤이 없다 → 잘릴 수 있다 |
|
|
699
|
-
|
|
700
|
-
`rgba(255,255,255,.22)`(표지 `<dl>` 상단선)는 **부채가 아니다** — 흰색 알파는 어느 팔레트에서도 "배경보다 살짝 밝은 선"으로 동작한다. 인쇄에서는 `--rule` 로 교체된다.
|
|
701
|
-
|
|
702
|
-
---
|
|
703
|
-
|
|
704
|
-
## 11. 미확정 — 정하고 나면 소스 주석에 적어라
|
|
705
|
-
|
|
706
|
-
1. **브레이크포인트 값의 산출 근거**(1080 / 900 / 860 / 820 / 760 / 620 / 560). 각 블록의 콘텐츠에서 나온 값이지만 계산 과정이 남아 있지 않다.
|
|
707
|
-
2. **본문 `line-height` 1.7 / 1.78 이 상위 스킬 권고(1.4–1.6)보다 넓은 이유.** 화면 장문 읽기 전제로 보이나 근거가 적혀 있지 않다.
|
|
708
|
-
3. **`.stats` 를 요약(01)보다 앞, `<main>` 최상단에 둔 근거.** 슬라이드 시퀀스(Stats 는 중후반)와 다르다. 스크롤 문서는 독자가 아무 데나 뛰므로 첫 화면에 총계를 두는 것으로 보인다.
|
|
709
|
-
4. **`--primary-d` 의 원래 용도**(§10 b).
|
|
710
|
-
5. **다이어그램 SVG 의 반응형 하한.** `viewBox` + `width:100%` 라 좁은 화면에서 내부 활자(12–15px 고정)가 비례 축소되는데, 최소 가독 폭을 검증한 기록이 없다.
|
|
711
|
-
6. **Primary/Secondary 명도가 가까운 팔레트의 보완 기준**(§9.3). 방향은 적었지만 수치 기준이 없다.
|
|
712
|
-
|
|
713
|
-
---
|
|
714
|
-
|
|
715
|
-
## 12. 재현 체크리스트
|
|
20
|
+
| 조판 규칙 — 활자 스케일 · 괘선 · 간격 격자 · 인쇄 페이지 문법 | `gazette-report` 스킬 (원전) |
|
|
21
|
+
| 색을 뜻으로 고르는 법 — 절 `tone` 여섯 | `references/lim-portal-format.md` |
|
|
22
|
+
| 대비 기준 · 한글 글줄 폭 근거 | `gazette-report` 스킬 |
|
|
23
|
+
| 검사기 | `~/.claude/skills/gazette-report/scripts/` 8종 |
|
|
716
24
|
|
|
717
|
-
|
|
25
|
+
## 여기서 건져 간 문장
|
|
718
26
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
-
|
|
723
|
-
- [ ] 칩은 `--accent-chip` + `--accent-on` **쌍으로** 썼는가 (§2.1)
|
|
724
|
-
- [ ] 어두운 바탕 위 글자는 `--on-primary` / `--on-primary-soft` / `--accent-on-primary` 를 썼는가 (§1.4)
|
|
725
|
-
- [ ] 모든 다열 그리드에 `minmax(0,1fr)` + sticky 컬럼이 있으면 `align-items:start` + `main{min-width:0}` (§4.1)
|
|
726
|
-
- [ ] 반복 카드가 **[대조 2열] → [결론 전폭] → [독자 액션 2열]** 3단인가 (§4.3)
|
|
727
|
-
- [ ] 문서 매체용 블록이 있는가 — 절 표제 / 색인표 / 범례 / 상호참조 (§3.1)
|
|
728
|
-
- [ ] body 에 `word-break:keep-all; overflow-wrap:anywhere;` (§5.1)
|
|
729
|
-
- [ ] `font-feature-settings:"tnum" 0` 전역 OFF + 정렬이 필요한 컴포넌트만 ON (§5.3)
|
|
730
|
-
- [ ] 다이어그램 색이 전부 `var()` 이고 **배경/글자 짝이 §6 표에 있는가**
|
|
731
|
-
- [ ] 인쇄에서 전면 배경 반전 + **글자색 토큰 재지정**을 했는가 (§8)
|
|
732
|
-
- [ ] `python3 assets/build.py report.json` → `python3 assets/qa.py report.json` **실패 0**
|
|
733
|
-
- [ ] **브라우저로 열어 본다.** 자동 검수는 안 보이는 결함만 잡는다 (§9.3, `references/qa.md`)
|
|
27
|
+
「같은 색이 한쪽에서는 통과하고 한쪽에서는 떨어진다」 — 활자 하한 14px 논거로 `gazette-report` 에.
|
|
28
|
+
「선언만 하고 안 쓰는 토큰이 있어도 된다. 금지는 주석이 아니라 검사로 적는다」 — 같은 곳.
|
|
29
|
+
「`1ch` 는 숫자 `0` 의 폭이다. 영문 감각 `65ch` 가 한국어로는 45자」 — `--measure: 37em ≈ 43자` 의 근거.
|
|
30
|
+
「`keep-all` 이 원칙, `anywhere` 가 탈출구다」 — 단 두 속성을 갈라 적을 것. `gazette.css:231-232` 는 `word-break: keep-all` + `overflow-wrap: break-word` 다.
|