for-humanity 0.1.0

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.
Files changed (177) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/LICENSE +21 -0
  3. package/README.md +118 -0
  4. package/dist/cli.css +962 -0
  5. package/dist/cli.js +1241 -0
  6. package/dist/client.js +5 -0
  7. package/package.json +92 -0
  8. package/src/app.tsx +57 -0
  9. package/src/asset/favicon.svg +5 -0
  10. package/src/asset/font/architects-daughter/LICENSE.txt +93 -0
  11. package/src/asset/font/architects-daughter/architects-daughter-latin-400-normal.woff2 +0 -0
  12. package/src/asset/font/architects-daughter/architects-daughter.css +10 -0
  13. package/src/asset/font/jetbrains-mono/LICENSE +93 -0
  14. package/src/asset/font/jetbrains-mono/jetbrains-mono-latin-wght-normal.woff2 +0 -0
  15. package/src/asset/font/jetbrains-mono/jetbrains-mono.css +11 -0
  16. package/src/asset/font/pretendard/LICENSE.txt +94 -0
  17. package/src/asset/font/pretendard/pretendardvariable-dynamic-subset.css +1008 -0
  18. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.0.woff2 +0 -0
  19. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.1.woff2 +0 -0
  20. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.10.woff2 +0 -0
  21. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.11.woff2 +0 -0
  22. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.12.woff2 +0 -0
  23. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.13.woff2 +0 -0
  24. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.14.woff2 +0 -0
  25. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.15.woff2 +0 -0
  26. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.16.woff2 +0 -0
  27. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.17.woff2 +0 -0
  28. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.18.woff2 +0 -0
  29. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.19.woff2 +0 -0
  30. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.2.woff2 +0 -0
  31. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.20.woff2 +0 -0
  32. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.21.woff2 +0 -0
  33. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.22.woff2 +0 -0
  34. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.23.woff2 +0 -0
  35. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.24.woff2 +0 -0
  36. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.25.woff2 +0 -0
  37. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.26.woff2 +0 -0
  38. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.27.woff2 +0 -0
  39. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.28.woff2 +0 -0
  40. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.29.woff2 +0 -0
  41. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.3.woff2 +0 -0
  42. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.30.woff2 +0 -0
  43. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.31.woff2 +0 -0
  44. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.32.woff2 +0 -0
  45. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.33.woff2 +0 -0
  46. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.34.woff2 +0 -0
  47. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.35.woff2 +0 -0
  48. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.36.woff2 +0 -0
  49. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.37.woff2 +0 -0
  50. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.38.woff2 +0 -0
  51. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.39.woff2 +0 -0
  52. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.4.woff2 +0 -0
  53. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.40.woff2 +0 -0
  54. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.41.woff2 +0 -0
  55. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.42.woff2 +0 -0
  56. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.43.woff2 +0 -0
  57. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.44.woff2 +0 -0
  58. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.45.woff2 +0 -0
  59. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.46.woff2 +0 -0
  60. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.47.woff2 +0 -0
  61. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.48.woff2 +0 -0
  62. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.49.woff2 +0 -0
  63. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.5.woff2 +0 -0
  64. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.50.woff2 +0 -0
  65. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.51.woff2 +0 -0
  66. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.52.woff2 +0 -0
  67. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.53.woff2 +0 -0
  68. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.54.woff2 +0 -0
  69. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.55.woff2 +0 -0
  70. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.56.woff2 +0 -0
  71. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.57.woff2 +0 -0
  72. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.58.woff2 +0 -0
  73. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.59.woff2 +0 -0
  74. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.6.woff2 +0 -0
  75. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.60.woff2 +0 -0
  76. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.61.woff2 +0 -0
  77. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.62.woff2 +0 -0
  78. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.63.woff2 +0 -0
  79. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.64.woff2 +0 -0
  80. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.65.woff2 +0 -0
  81. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.66.woff2 +0 -0
  82. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.67.woff2 +0 -0
  83. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.68.woff2 +0 -0
  84. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.69.woff2 +0 -0
  85. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.7.woff2 +0 -0
  86. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.70.woff2 +0 -0
  87. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.71.woff2 +0 -0
  88. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.72.woff2 +0 -0
  89. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.73.woff2 +0 -0
  90. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.74.woff2 +0 -0
  91. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.75.woff2 +0 -0
  92. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.76.woff2 +0 -0
  93. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.77.woff2 +0 -0
  94. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.78.woff2 +0 -0
  95. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.79.woff2 +0 -0
  96. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.8.woff2 +0 -0
  97. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.80.woff2 +0 -0
  98. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.81.woff2 +0 -0
  99. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.82.woff2 +0 -0
  100. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.83.woff2 +0 -0
  101. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.84.woff2 +0 -0
  102. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.85.woff2 +0 -0
  103. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.86.woff2 +0 -0
  104. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.87.woff2 +0 -0
  105. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.88.woff2 +0 -0
  106. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.89.woff2 +0 -0
  107. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.9.woff2 +0 -0
  108. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.90.woff2 +0 -0
  109. package/src/asset/font/pretendard/woff2-dynamic-subset/PretendardVariable.subset.91.woff2 +0 -0
  110. package/src/cli.ts +162 -0
  111. package/src/client.ts +322 -0
  112. package/src/component/widget/prose/_constant/heading-highlight.ts +4 -0
  113. package/src/component/widget/prose/_constant/section.ts +7 -0
  114. package/src/component/widget/prose/_constant/status.ts +9 -0
  115. package/src/component/widget/prose/_function/has-block-content.ts +30 -0
  116. package/src/component/widget/prose/_function/rehype-head.ts +31 -0
  117. package/src/component/widget/prose/_function/rehype-sections/_to-section-headings.ts +20 -0
  118. package/src/component/widget/prose/_function/rehype-sections/rehype-sections.ts +21 -0
  119. package/src/component/widget/prose/_function/rehype-tables.ts +22 -0
  120. package/src/component/widget/prose/_function/remark-blocks.ts +112 -0
  121. package/src/component/widget/prose/_function/remark-flow.ts +26 -0
  122. package/src/component/widget/prose/_function/remark-links.ts +33 -0
  123. package/src/component/widget/prose/_function/remark-parts.ts +26 -0
  124. package/src/component/widget/prose/_function/remark-status.ts +59 -0
  125. package/src/component/widget/prose/_function/remark-swatch.ts +16 -0
  126. package/src/component/widget/prose/_function/remark-unknown-directives.ts +25 -0
  127. package/src/component/widget/prose/_type/doc-section.ts +9 -0
  128. package/src/component/widget/prose/wg-prose.css +381 -0
  129. package/src/component/widget/prose/wg-prose.tsx +43 -0
  130. package/src/component/widget/shell/_constant/cursor-face.ts +9 -0
  131. package/src/component/widget/shell/_constant/navigation.ts +4 -0
  132. package/src/component/widget/shell/_constant/reading-line.ts +4 -0
  133. package/src/component/widget/shell/_function/bind-mobile-navigation.ts +140 -0
  134. package/src/component/widget/shell/_function/to-toc-groups.ts +38 -0
  135. package/src/component/widget/shell/_type/doc-outline.ts +16 -0
  136. package/src/component/widget/shell/_type/toc-group.ts +39 -0
  137. package/src/component/widget/shell/_wg-shell-nav.css +134 -0
  138. package/src/component/widget/shell/_wg-shell-nav.tsx +75 -0
  139. package/src/component/widget/shell/_wg-shell-toc.css +268 -0
  140. package/src/component/widget/shell/_wg-shell-toc.tsx +57 -0
  141. package/src/component/widget/shell/wg-shell.css +261 -0
  142. package/src/component/widget/shell/wg-shell.tsx +147 -0
  143. package/src/constant/asset.ts +48 -0
  144. package/src/constant/cli.ts +19 -0
  145. package/src/constant/copy.ts +108 -0
  146. package/src/constant/doc.ts +13 -0
  147. package/src/constant/font.ts +39 -0
  148. package/src/constant/locale.ts +9 -0
  149. package/src/constant/site.ts +9 -0
  150. package/src/constant/status.ts +15 -0
  151. package/src/content/create-processor.ts +59 -0
  152. package/src/content/read-docs.test.ts +242 -0
  153. package/src/content/read-docs.ts +75 -0
  154. package/src/content/read-home.ts +32 -0
  155. package/src/content/rehype-heading-ids.ts +34 -0
  156. package/src/content/remark-report.ts +15 -0
  157. package/src/content/to-doc-groups.ts +18 -0
  158. package/src/dev.ts +79 -0
  159. package/src/env.d.ts +6 -0
  160. package/src/page/doc/pg-doc.tsx +35 -0
  161. package/src/page/home/pg-home.tsx +40 -0
  162. package/src/style/base.css +28 -0
  163. package/src/style/token.css +162 -0
  164. package/src/type/doc-content.ts +15 -0
  165. package/src/type/doc-data.ts +33 -0
  166. package/src/type/doc-file-data.ts +21 -0
  167. package/src/type/doc-heading.ts +17 -0
  168. package/src/type/doc.ts +16 -0
  169. package/src/type/site-assets.ts +17 -0
  170. package/src/type/site-config.ts +55 -0
  171. package/src/type/vfile-data.d.ts +10 -0
  172. package/src/util/dom/find-hash-target.ts +18 -0
  173. package/src/util/dom/reveal-hash-target.ts +15 -0
  174. package/src/util/error/to-error-message.ts +10 -0
  175. package/src/util/font/to-font-css.test.ts +41 -0
  176. package/src/util/font/to-font-css.ts +28 -0
  177. package/src/util/mermaid/render-flow.js +321 -0
@@ -0,0 +1,38 @@
1
+ import {takeWhile} from "es-toolkit/array";
2
+ import {section_heading_depth} from "@/component/widget/prose/_constant/section";
3
+ import type {DocOutline} from "@/component/widget/shell/_type/doc-outline";
4
+ import type {TocGroup, TocSection} from "@/component/widget/shell/_type/toc-group";
5
+
6
+ /**
7
+ * h2별 h3 수집 · 가름 시작 절에서 목차 묶음 분리
8
+ * 첫 h2 앞의 h3은 목차 제외
9
+ */
10
+ export const toTocGroups = (outline: DocOutline): TocGroup[] => {
11
+ const entries = outline.headings
12
+ .filter((heading) => heading.depth === section_heading_depth.section || heading.depth === section_heading_depth.sub)
13
+ .map((heading, index) => ({heading, section: outline.sections[index]}));
14
+ const sections = entries.flatMap((entry, index): TocSection[] =>
15
+ entry.heading.depth === section_heading_depth.section
16
+ ? [
17
+ {
18
+ heading: entry.heading,
19
+ part: entry.section.part,
20
+ subs: takeWhile(entries.slice(index + 1), (sub) => sub.heading.depth !== section_heading_depth.section).map((sub) => ({
21
+ heading: sub.heading,
22
+ })),
23
+ },
24
+ ]
25
+ : [],
26
+ );
27
+
28
+ return sections.flatMap((section, index) =>
29
+ index === 0 || section.part !== undefined
30
+ ? [
31
+ {
32
+ part: section.part,
33
+ sections: [section, ...takeWhile(sections.slice(index + 1), (next) => next.part === undefined)],
34
+ },
35
+ ]
36
+ : [],
37
+ );
38
+ };
@@ -0,0 +1,16 @@
1
+ import type {DocSection} from "@/component/widget/prose/_type/doc-section";
2
+ import type {DocHeading} from "@/type/doc-heading";
3
+
4
+ /**
5
+ * 문서 렌더링 결과의 목차 입력
6
+ */
7
+ export interface DocOutline {
8
+ /**
9
+ * 작성한 제목의 id와 텍스트 · 문서 순서
10
+ */
11
+ headings: DocHeading[];
12
+ /**
13
+ * 제목별 가름 · headings의 h2·h3 순서와 일치
14
+ */
15
+ sections: DocSection[];
16
+ }
@@ -0,0 +1,39 @@
1
+ import type {DocHeading} from "@/type/doc-heading";
2
+
3
+ /**
4
+ * 목차 소제목 항목
5
+ */
6
+ export interface TocSub {
7
+ /**
8
+ * 제목 id와 텍스트
9
+ */
10
+ heading: DocHeading;
11
+ }
12
+
13
+ /**
14
+ * 목차 절과 소제목 목록
15
+ */
16
+ export interface TocSection extends TocSub {
17
+ /**
18
+ * 이 절에서 시작하는 가름 이름
19
+ */
20
+ part?: string;
21
+ /**
22
+ * 해당 절의 소제목 · 현재 위치와 무관하게 전체 표시
23
+ */
24
+ subs: TocSub[];
25
+ }
26
+
27
+ /**
28
+ * 가름별 목차 묶음 · 첫 묶음 이름 생략 가능
29
+ */
30
+ export interface TocGroup {
31
+ /**
32
+ * 묶음 위의 가름 이름
33
+ */
34
+ part?: string;
35
+ /**
36
+ * 묶음의 절 목록
37
+ */
38
+ sections: TocSection[];
39
+ }
@@ -0,0 +1,134 @@
1
+ /*
2
+ * 읽는 순서의 문서 목록 · 중립색 1px 트리
3
+ */
4
+ .wg_shellNav__root {
5
+ font-size: var(--app-font-size-dense);
6
+ line-height: var(--app-line-height-tight);
7
+ }
8
+
9
+ .wg_shellNav__docs {
10
+ display: flex;
11
+ flex-direction: column;
12
+ gap: var(--app-space-stack);
13
+ }
14
+
15
+ .wg_shellNav__list {
16
+ margin: 0 0 0 var(--app-space-tree-inset);
17
+ padding: 0;
18
+ list-style: none;
19
+ }
20
+
21
+ .wg_shellNav__item {
22
+ /* 세로 줄기는 각 항목 기준 · 마지막 항목의 첫 줄 가운데에서 종료 */
23
+ position: relative;
24
+
25
+ &::before {
26
+ position: absolute;
27
+ top: 0;
28
+ bottom: 0;
29
+ left: 0;
30
+ border-left: 1px solid var(--app-color-border-soft);
31
+ content: "";
32
+ pointer-events: none;
33
+ }
34
+
35
+ &:where(:last-child)::before {
36
+ bottom: auto;
37
+ height: calc(0.5lh + var(--app-space-tree-row));
38
+ }
39
+
40
+ /* hover·키보드 포커스의 도착점 · 첫 줄 아래는 기본 줄기 유지 */
41
+ &::after {
42
+ position: absolute;
43
+ top: 0;
44
+ left: 0;
45
+ height: calc(0.5lh + var(--app-space-tree-row));
46
+ border-left: 1px solid transparent;
47
+ content: "";
48
+ pointer-events: none;
49
+ }
50
+
51
+ &:where(:hover)::after {
52
+ border-left-color: var(--app-color-text-strong);
53
+ }
54
+
55
+ &:where(:has(:focus-visible))::after {
56
+ border-left-color: var(--app-color-text-strong);
57
+ }
58
+
59
+ /* 뒤 항목까지의 세로 경로 · 같은 목록의 형제 안에서만 연결 */
60
+ &:where(:has(~ .wg_shellNav__item:hover))::before {
61
+ border-left-color: var(--app-color-text-strong);
62
+ }
63
+
64
+ &:where(:has(~ .wg_shellNav__item :focus-visible))::before {
65
+ border-left-color: var(--app-color-text-strong);
66
+ }
67
+ }
68
+
69
+ .wg_shellNav__item--trail {
70
+ &::before {
71
+ border-left-color: var(--app-color-text-strong);
72
+ }
73
+ }
74
+
75
+ .wg_shellNav__item--current {
76
+ &::after {
77
+ border-left-color: var(--app-color-text-strong);
78
+ }
79
+ }
80
+
81
+ .wg_shellNav__link {
82
+ /* 가로 가지는 링크 첫 줄 기준 · 여러 줄 제목에서도 연결 위치 유지 */
83
+ position: relative;
84
+ display: block;
85
+ padding: var(--app-space-tree-row) 0 var(--app-space-tree-row) calc(var(--app-space-tree-indent) + var(--app-space-inline));
86
+ color: var(--app-color-text);
87
+ font-size: var(--app-font-size-dense);
88
+ line-height: var(--app-line-height-tight);
89
+ text-decoration: none;
90
+
91
+ &::before {
92
+ position: absolute;
93
+ top: calc(0.5lh + var(--app-space-tree-row));
94
+ left: 0;
95
+ width: var(--app-space-tree-indent);
96
+ border-top: 1px solid var(--app-color-border-soft);
97
+ content: "";
98
+ pointer-events: none;
99
+ }
100
+
101
+ &:where(:hover) {
102
+ text-decoration: underline;
103
+ }
104
+
105
+ &:where(:hover)::before {
106
+ border-top-color: currentcolor;
107
+ }
108
+
109
+ /* hover와 같은 밑줄·가지선으로 키보드 위치 표시 */
110
+ &:where(:focus-visible) {
111
+ outline: none;
112
+ text-decoration: underline;
113
+ }
114
+
115
+ &:where(:focus-visible)::before {
116
+ border-top-color: currentcolor;
117
+ }
118
+ }
119
+
120
+ .wg_shellNav__link--active {
121
+ color: var(--app-color-text-strong);
122
+ font-weight: var(--app-font-weight-active);
123
+
124
+ &::before {
125
+ border-top-color: currentcolor;
126
+ }
127
+ }
128
+
129
+ .wg_shellNav__group {
130
+ margin: 0 0 4px;
131
+ color: var(--app-color-text-muted);
132
+ font-size: var(--app-font-size-label);
133
+ text-box: trim-start cap alphabetic;
134
+ }
@@ -0,0 +1,75 @@
1
+ import clsx from "clsx";
2
+ import {copy_nav_aria_label} from "@/constant/copy";
3
+ import {toDocGroups} from "@/content/to-doc-groups";
4
+ import type {Doc} from "@/type/doc";
5
+ import type {SiteConfig} from "@/type/site-config";
6
+ import "./_wg-shell-nav.css";
7
+
8
+ /**
9
+ * 서버 사이드바 입력 · 읽는 순서의 문서 묶음
10
+ */
11
+ export interface WgShellNavProps {
12
+ /**
13
+ * 문서 묶음 순서
14
+ */
15
+ site: SiteConfig;
16
+ /**
17
+ * 전체 문서 목록
18
+ */
19
+ docs: Doc[];
20
+ /**
21
+ * 현재 문서 id · 첫 화면 생략
22
+ */
23
+ current?: string;
24
+ }
25
+
26
+ export const WgShellNav = (props: WgShellNavProps) => {
27
+ const docGroups = toDocGroups({docs: props.docs, navigation: props.site.navigation});
28
+
29
+ return (
30
+ <nav className={clsx("wg_shellNav__root")} aria-label={copy_nav_aria_label}>
31
+ {/**
32
+ * 문서 이동 · 독자 목적별 묶음
33
+ */}
34
+ <div className={clsx("wg_shellNav__docs")}>
35
+ {/**
36
+ * 묶음 이름과 독립 문서 · 제목이 링크의 접근 가능한 이름
37
+ */}
38
+ {docGroups.map((group) => {
39
+ const currentIndex = group.docs.findIndex((doc) => doc.id === props.current);
40
+
41
+ return (
42
+ <section className={clsx("wg_shellNav__docGroup")} key={group.name} aria-label={group.name}>
43
+ <div className={clsx("wg_shellNav__group")}>{group.name}</div>
44
+ {/**
45
+ * 현재 문서와 묶음 시작부터 이어지는 세로 경로 표시
46
+ */}
47
+ <ul className={clsx("wg_shellNav__list")}>
48
+ {group.docs.map((doc, index) => (
49
+ <li
50
+ className={clsx("wg_shellNav__item", {
51
+ "wg_shellNav__item--trail": index < currentIndex,
52
+ "wg_shellNav__item--current": index === currentIndex,
53
+ })}
54
+ key={doc.id}
55
+ >
56
+ <a
57
+ className={clsx("wg_shellNav__link", {
58
+ "wg_shellNav__link--active": index === currentIndex,
59
+ })}
60
+ href={`/${doc.id}/`}
61
+ title={doc.data.label}
62
+ aria-current={index === currentIndex ? "page" : undefined}
63
+ >
64
+ {doc.data.name}
65
+ </a>
66
+ </li>
67
+ ))}
68
+ </ul>
69
+ </section>
70
+ );
71
+ })}
72
+ </div>
73
+ </nav>
74
+ );
75
+ };
@@ -0,0 +1,268 @@
1
+ /*
2
+ * 문서 탐색 아래 목차 · 작성한 제목과 계층을 유지하는 중립색 트리
3
+ */
4
+ .wg_shellToc__root {
5
+ margin-top: var(--app-space-group);
6
+ padding-top: var(--app-space-group);
7
+ border-top: 1px solid var(--app-color-border-soft);
8
+ font-size: var(--app-font-size-dense);
9
+ line-height: var(--app-line-height-tight);
10
+ }
11
+
12
+ .wg_shellToc__label {
13
+ margin-bottom: var(--app-space-inline);
14
+ color: var(--app-color-text-muted);
15
+ font-size: var(--app-font-size-label);
16
+ font-weight: var(--app-font-weight-mark);
17
+ letter-spacing: var(--app-letter-spacing-label);
18
+ }
19
+
20
+ .wg_shellToc__group {
21
+ margin: var(--app-space-stack) 0 4px;
22
+ color: var(--app-color-text-muted);
23
+ font-size: var(--app-font-size-label);
24
+ }
25
+
26
+ .wg_shellToc__list {
27
+ margin: 0 0 0 var(--app-space-tree-inset);
28
+ padding: 0;
29
+ list-style: none;
30
+ }
31
+
32
+ .wg_shellToc__link {
33
+ /* 가로 가지는 링크 첫 줄 기준 · 여러 줄 제목에서도 연결 위치 유지 */
34
+ position: relative;
35
+ display: block;
36
+ padding: var(--app-space-tree-row) 0 var(--app-space-tree-row) calc(var(--app-space-tree-indent) + var(--app-space-inline));
37
+ color: var(--app-color-text-muted);
38
+ font-size: var(--app-font-size-dense);
39
+ line-height: var(--app-line-height-tight);
40
+ text-decoration: none;
41
+
42
+ &::before {
43
+ position: absolute;
44
+ top: calc(0.5lh + var(--app-space-tree-row));
45
+ left: 0;
46
+ width: var(--app-space-tree-indent);
47
+ border-top: 1px solid var(--app-color-border-soft);
48
+ content: "";
49
+ pointer-events: none;
50
+ }
51
+
52
+ &:where(:hover) {
53
+ color: var(--app-color-text-strong);
54
+ text-decoration: underline;
55
+ }
56
+
57
+ &:where(:hover)::before {
58
+ border-top-color: currentcolor;
59
+ }
60
+
61
+ /* 박스 대신 hover와 같은 진한 글·밑줄·가지선으로 키보드 위치 표시 */
62
+ &:where(:focus-visible) {
63
+ outline: none;
64
+ color: var(--app-color-text-strong);
65
+ text-decoration: underline;
66
+ }
67
+
68
+ &:where(:focus-visible)::before {
69
+ border-top-color: currentcolor;
70
+ }
71
+ }
72
+
73
+ .wg_shellToc__link--active {
74
+ color: var(--app-color-text-strong);
75
+ font-weight: var(--app-font-weight-active);
76
+
77
+ &::before {
78
+ border-top-color: currentcolor;
79
+ }
80
+ }
81
+
82
+ .wg_shellToc__item {
83
+ /* 하위 목록의 margin을 포함해 다음 항목까지 줄기 연결 */
84
+ display: flow-root;
85
+
86
+ /* 세로 줄기는 항목 전체 기준 · 중간 항목의 소제목 옆에서도 연결 */
87
+ position: relative;
88
+
89
+ &::before {
90
+ position: absolute;
91
+ top: 0;
92
+ bottom: 0;
93
+ left: 0;
94
+ border-left: 1px solid var(--app-color-border-soft);
95
+ content: "";
96
+ pointer-events: none;
97
+ }
98
+
99
+ &:where(:last-child)::before {
100
+ bottom: auto;
101
+ height: calc(0.5lh + var(--app-space-tree-row));
102
+ }
103
+
104
+ /* hover·키보드 포커스의 도착점 · 첫 줄 아래는 기본 줄기 유지 */
105
+ &::after {
106
+ position: absolute;
107
+ top: 0;
108
+ left: 0;
109
+ height: calc(0.5lh + var(--app-space-tree-row));
110
+ border-left: 1px solid transparent;
111
+ content: "";
112
+ pointer-events: none;
113
+ }
114
+
115
+ &:where(:hover)::after {
116
+ border-left-color: var(--app-color-text-strong);
117
+ }
118
+
119
+ &:where(:has(:focus-visible))::after {
120
+ border-left-color: var(--app-color-text-strong);
121
+ }
122
+
123
+ /* 뒤 항목까지의 세로 경로 · 같은 목록의 형제 안에서만 연결 */
124
+ &:where(:has(~ .wg_shellToc__item:hover))::before {
125
+ border-left-color: var(--app-color-text-strong);
126
+ }
127
+
128
+ &:where(:has(~ .wg_shellToc__item :focus-visible))::before {
129
+ border-left-color: var(--app-color-text-strong);
130
+ }
131
+
132
+ /* 하위 제목을 탐색할 때 상위 절의 가지도 연결 */
133
+ &:where(:hover) > .wg_shellToc__link::before {
134
+ border-top-color: var(--app-color-text-strong);
135
+ }
136
+
137
+ &:where(:has(:focus-visible)) > .wg_shellToc__link::before {
138
+ border-top-color: var(--app-color-text-strong);
139
+ }
140
+ }
141
+
142
+ .wg_shellToc__item--trail {
143
+ &::before {
144
+ border-left-color: var(--app-color-text-strong);
145
+ }
146
+ }
147
+
148
+ .wg_shellToc__item--current {
149
+ &::after {
150
+ border-left-color: var(--app-color-text-strong);
151
+ }
152
+ }
153
+
154
+ .wg_shellToc__sub {
155
+ margin: var(--app-space-tree-row) 0 4px var(--app-space-tree-indent);
156
+ padding: 0;
157
+ list-style: none;
158
+ }
159
+
160
+ .wg_shellToc__subItem {
161
+ /* 하위 줄기는 소제목 항목 기준 · 마지막 소제목 첫 줄에서 종료 */
162
+ position: relative;
163
+
164
+ &::before {
165
+ position: absolute;
166
+ top: 0;
167
+ bottom: 0;
168
+ left: 0;
169
+ border-left: 1px solid var(--app-color-border-soft);
170
+ content: "";
171
+ pointer-events: none;
172
+ }
173
+
174
+ &:where(:last-child)::before {
175
+ bottom: auto;
176
+ height: calc(0.5lh + var(--app-space-tree-row));
177
+ }
178
+
179
+ /* hover·키보드 포커스의 도착점 · 첫 줄 아래는 기본 줄기 유지 */
180
+ &::after {
181
+ position: absolute;
182
+ top: 0;
183
+ left: 0;
184
+ height: calc(0.5lh + var(--app-space-tree-row));
185
+ border-left: 1px solid transparent;
186
+ content: "";
187
+ pointer-events: none;
188
+ }
189
+
190
+ &:where(:hover)::after {
191
+ border-left-color: var(--app-color-text-strong);
192
+ }
193
+
194
+ &:where(:has(:focus-visible))::after {
195
+ border-left-color: var(--app-color-text-strong);
196
+ }
197
+
198
+ /* 뒤 항목까지의 세로 경로 · 같은 목록의 형제 안에서만 연결 */
199
+ &:where(:has(~ .wg_shellToc__subItem:hover))::before {
200
+ border-left-color: var(--app-color-text-strong);
201
+ }
202
+
203
+ &:where(:has(~ .wg_shellToc__subItem :focus-visible))::before {
204
+ border-left-color: var(--app-color-text-strong);
205
+ }
206
+ }
207
+
208
+ .wg_shellToc__subItem--trail {
209
+ &::before {
210
+ border-left-color: var(--app-color-text-strong);
211
+ }
212
+ }
213
+
214
+ .wg_shellToc__subItem--current {
215
+ &::after {
216
+ border-left-color: var(--app-color-text-strong);
217
+ }
218
+ }
219
+
220
+ .wg_shellToc__subLink {
221
+ /* 하위 가지도 링크 첫 줄 기준 · 줄바꿈된 제목 옆으로 선이 늘어나지 않음 */
222
+ position: relative;
223
+ display: block;
224
+ padding: var(--app-space-tree-row) 0 var(--app-space-tree-row) calc(var(--app-space-tree-indent) + var(--app-space-inline));
225
+ color: var(--app-color-text-muted);
226
+ font-size: var(--app-font-size-detail);
227
+ line-height: var(--app-line-height-tight);
228
+ text-decoration: none;
229
+
230
+ &::before {
231
+ position: absolute;
232
+ top: calc(0.5lh + var(--app-space-tree-row));
233
+ left: 0;
234
+ width: var(--app-space-tree-indent);
235
+ border-top: 1px solid var(--app-color-border-soft);
236
+ content: "";
237
+ pointer-events: none;
238
+ }
239
+
240
+ &:where(:hover) {
241
+ color: var(--app-color-text-strong);
242
+ text-decoration: underline;
243
+ }
244
+
245
+ &:where(:hover)::before {
246
+ border-top-color: currentcolor;
247
+ }
248
+
249
+ /* 상위 목차와 같은 키보드 표현 */
250
+ &:where(:focus-visible) {
251
+ outline: none;
252
+ color: var(--app-color-text-strong);
253
+ text-decoration: underline;
254
+ }
255
+
256
+ &:where(:focus-visible)::before {
257
+ border-top-color: currentcolor;
258
+ }
259
+ }
260
+
261
+ .wg_shellToc__subLink--active {
262
+ color: var(--app-color-text-strong);
263
+ font-weight: var(--app-font-weight-active);
264
+
265
+ &::before {
266
+ border-top-color: currentcolor;
267
+ }
268
+ }
@@ -0,0 +1,57 @@
1
+ import clsx from "clsx";
2
+ import {Fragment} from "react";
3
+ import type {TocGroup} from "@/component/widget/shell/_type/toc-group";
4
+ import {copy_nav_toc_label} from "@/constant/copy";
5
+ import "./_wg-shell-toc.css";
6
+
7
+ /**
8
+ * 문서 탐색 아래의 현재 문서 목차 · client.ts에서 읽는 위치 표시
9
+ */
10
+ export interface WgShellTocProps {
11
+ /**
12
+ * 본문 순서의 가름·절·소제목 · 전체 펼침
13
+ */
14
+ groups: TocGroup[];
15
+ }
16
+
17
+ export const WgShellToc = (props: WgShellTocProps) => {
18
+ return (
19
+ <nav className={clsx("wg_shellToc__root")} aria-label={copy_nav_toc_label}>
20
+ <div className={clsx("wg_shellToc__label")}>{copy_nav_toc_label}</div>
21
+ {/**
22
+ * 가름별 제목 이동 · 작성한 제목을 본문과 동일하게 표시
23
+ */}
24
+ {props.groups.map((group) => (
25
+ <Fragment key={group.sections[0].heading.slug}>
26
+ {group.part !== undefined && <div className={clsx("wg_shellToc__group")}>{group.part}</div>}
27
+ {/**
28
+ * 현재 위치와 무관하게 모든 소제목 표시
29
+ */}
30
+ <ul className={clsx("wg_shellToc__list")}>
31
+ {group.sections.map((section) => (
32
+ <li className={clsx("wg_shellToc__item")} key={section.heading.slug}>
33
+ <a className={clsx("wg_shellToc__link")} href={`#${section.heading.slug}`} data-toc-link="">
34
+ {section.heading.text}
35
+ </a>
36
+ {/**
37
+ * 소제목의 들여쓰기 · JavaScript 없이도 제목 이동 가능
38
+ */}
39
+ {section.subs.length > 0 && (
40
+ <ul className={clsx("wg_shellToc__sub")}>
41
+ {section.subs.map((sub) => (
42
+ <li className={clsx("wg_shellToc__subItem")} key={sub.heading.slug}>
43
+ <a className={clsx("wg_shellToc__subLink")} href={`#${sub.heading.slug}`} data-toc-sub-link="">
44
+ {sub.heading.text}
45
+ </a>
46
+ </li>
47
+ ))}
48
+ </ul>
49
+ )}
50
+ </li>
51
+ ))}
52
+ </ul>
53
+ </Fragment>
54
+ ))}
55
+ </nav>
56
+ );
57
+ };