@godxjp/ui 28.7.0 → 28.8.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.
- package/dist/components/data-display/index.d.ts +2 -0
- package/dist/components/data-display/index.js +2 -0
- package/dist/components/data-display/marquee.d.ts +16 -0
- package/dist/components/data-display/marquee.js +155 -0
- package/dist/components/general/reveal.d.ts +23 -2
- package/dist/components/general/reveal.js +37 -7
- package/dist/components/general/typography.d.ts +4 -1
- package/dist/components/general/typography.js +14 -1
- package/dist/components/layout/affix.d.ts +86 -0
- package/dist/components/layout/affix.js +187 -0
- package/dist/components/layout/index.d.ts +4 -0
- package/dist/components/layout/index.js +4 -0
- package/dist/components/layout/legal-document-shell.js +4 -3
- package/dist/components/layout/masonry.d.ts +74 -0
- package/dist/components/layout/masonry.js +214 -0
- package/dist/components/layout/page-container.js +5 -20
- package/dist/components/navigation/anchor.d.ts +64 -0
- package/dist/components/navigation/anchor.js +284 -0
- package/dist/components/navigation/index.d.ts +4 -0
- package/dist/components/navigation/index.js +4 -0
- package/dist/components/navigation/mega-menu.d.ts +21 -0
- package/dist/components/navigation/mega-menu.js +526 -0
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +517 -0
- package/dist/i18n/messages/ja.json +513 -0
- package/dist/i18n/messages/vi.json +513 -0
- package/dist/lib/hooks.d.ts +68 -0
- package/dist/lib/hooks.js +52 -0
- package/dist/lib/platform.d.ts +14 -0
- package/dist/lib/platform.js +10 -1
- package/dist/lib/utils.d.ts +1 -1
- package/dist/lib/utils.js +3 -2
- package/dist/props/components/data-display.prop.d.ts +95 -1
- package/dist/props/components/general.prop.d.ts +47 -3
- package/dist/props/components/layout.prop.d.ts +194 -0
- package/dist/props/components/navigation.prop.d.ts +263 -0
- package/dist/props/registry.d.ts +359 -4
- package/dist/props/registry.js +472 -3
- package/dist/props/vocabulary/index.d.ts +1 -1
- package/dist/props/vocabulary/interaction.prop.d.ts +39 -2
- package/dist/styles/control.css +5 -6
- package/dist/styles/data-display-layout.css +2 -1
- package/dist/styles/density.css +4 -0
- package/dist/styles/layout.css +79 -0
- package/dist/styles/motion.css +121 -1
- package/dist/styles/navigation-layout.css +397 -1
- package/dist/styles/shell-layout.css +3 -0
- package/dist/styles/text-layout.css +52 -4
- package/dist/tokens/base.css +5 -0
- package/dist/tokens/components/affix.css +7 -0
- package/dist/tokens/components/anchor.css +17 -0
- package/dist/tokens/components/control.css +3 -3
- package/dist/tokens/components/form.css +1 -1
- package/dist/tokens/components/marquee.css +7 -0
- package/dist/tokens/components/masonry.css +6 -0
- package/dist/tokens/components/mega-menu.css +62 -0
- package/dist/tokens/components/shell.css +3 -0
- package/dist/tokens/foundation.css +11 -0
- package/dist/tokens/semantic/layout.css +7 -0
- package/docs/COMPOSITION-VS-COMPONENT.md +19 -1
- package/docs/DESIGN-AUTHORITY.md +99 -18
- package/docs/FRAME-COVERAGE-REPORT.md +7 -2
- package/docs/data-display/marquee.tsx +254 -0
- package/docs/foundation/_theme-editor-scope.ts +222 -0
- package/docs/foundation/density.tsx +12 -2
- package/docs/foundation/spacing.tsx +5 -0
- package/docs/foundation/theme-editor.tsx +645 -0
- package/docs/general/activity.tsx +65 -0
- package/docs/general/reveal.tsx +290 -22
- package/docs/general/typography.tsx +91 -1
- package/docs/layout/affix.tsx +209 -0
- package/docs/layout/masonry.tsx +291 -0
- package/docs/navigation/anchor.tsx +285 -0
- package/docs/navigation/mega-menu-panel.tsx +86 -0
- package/docs/navigation/mega-menu.tsx +254 -0
- package/docs/roadmap/website-components.md +779 -0
- package/docs/showcase/acme-website.tsx +75 -39
- package/docs/showcase/futurelastic-web.tsx +91 -49
- package/docs/showcase/marketing-page.tsx +885 -0
- package/docs/showcase/table-footer-totals.tsx +12 -2
- package/docs/showcase/theme-customization.tsx +1259 -0
- package/package.json +5 -3
- package/scripts/brand-accent.generated.mjs +27 -0
- package/scripts/ui-audit.mjs +66 -0
- package/scripts/visual-audit-rules.mjs +46 -2
|
@@ -195,6 +195,71 @@ export default function Demo() {
|
|
|
195
195
|
</CardContent>
|
|
196
196
|
</Card>
|
|
197
197
|
|
|
198
|
+
{/*
|
|
199
|
+
* gh#830 — "there is no standalone spinner". There is; this is it. What was missing was
|
|
200
|
+
* the signpost, so it lives here, on the page someone lands on.
|
|
201
|
+
*/}
|
|
202
|
+
<Card>
|
|
203
|
+
<CardHeader>
|
|
204
|
+
<CardTitle level={2}>スピナーを探して来た場合(antd Spin との対応)</CardTitle>
|
|
205
|
+
</CardHeader>
|
|
206
|
+
<CardContent>
|
|
207
|
+
<Flex direction="col" gap="md">
|
|
208
|
+
<Text tone="muted">
|
|
209
|
+
antd の Spin は spinning という真偽値ひとつで 4
|
|
210
|
+
つの異なる状況を兼ねている。ここではそれが 4
|
|
211
|
+
つの別コンポーネントになっている。見た目ではなく、支援技術に対する意味が
|
|
212
|
+
それぞれ違うからで、形(「丸いやつが欲しい」)で選ぶと、常駐インジケータが
|
|
213
|
+
スクリーンリーダーに「このページはずっと処理中です」と言い続けることになる。
|
|
214
|
+
</Text>
|
|
215
|
+
|
|
216
|
+
<Flex direction="col" gap="sm">
|
|
217
|
+
<Flex direction="col" gap="xs">
|
|
218
|
+
<Text weight="medium">1 · どこか別の場所で、無期限に、何かが起きている</Text>
|
|
219
|
+
<Text size="xs" tone="muted">
|
|
220
|
+
→ この Activity。既定ではライブリージョンを一切出さない。
|
|
221
|
+
</Text>
|
|
222
|
+
<Activity variant="bar" tone="info" label="同期中…" />
|
|
223
|
+
</Flex>
|
|
224
|
+
|
|
225
|
+
<Separator />
|
|
226
|
+
|
|
227
|
+
<Flex direction="col" gap="xs">
|
|
228
|
+
<Text weight="medium">2 · この領域の「中身」が読み込み中</Text>
|
|
229
|
+
<Text size="xs" tone="muted">
|
|
230
|
+
→ Skeleton(aria-busy + aria-live と、形のあるプレースホルダ)。これが antd Spin
|
|
231
|
+
の ラッパー形態(<Spin spinning>{"{children}"}</Spin>)に当たる。
|
|
232
|
+
クエリのライフサイクル全体(skeleton → 前提未充足 → 空 →
|
|
233
|
+
エラー、原因別の再試行つき)なら DataState。
|
|
234
|
+
</Text>
|
|
235
|
+
</Flex>
|
|
236
|
+
|
|
237
|
+
<Separator />
|
|
238
|
+
|
|
239
|
+
<Flex direction="col" gap="xs">
|
|
240
|
+
<Text weight="medium">3 · 「この操作」が実行中</Text>
|
|
241
|
+
<Text size="xs" tone="muted">
|
|
242
|
+
→ Button の loading。aria-busy はコントロール自身に付き、起動もブロックされる。
|
|
243
|
+
</Text>
|
|
244
|
+
</Flex>
|
|
245
|
+
|
|
246
|
+
<Separator />
|
|
247
|
+
|
|
248
|
+
<Flex direction="col" gap="xs">
|
|
249
|
+
<Text weight="medium">4 · 進捗の数値が分かっている</Text>
|
|
250
|
+
<Text size="xs" tone="muted">
|
|
251
|
+
→ Progress。数値が分からないときが Activity の bar
|
|
252
|
+
で、それがこの体系の不確定インジケータ。円が回る形ではなく走るバーなのは、
|
|
253
|
+
システムとして決めたことであって、欠落ではない。antd の percent="auto"
|
|
254
|
+
(100% に決して到達しない合成の進捗率)は意図的に移植していない。
|
|
255
|
+
分からない待ち時間に作った数字を被せるのは、確定に見える嘘だから。
|
|
256
|
+
</Text>
|
|
257
|
+
</Flex>
|
|
258
|
+
</Flex>
|
|
259
|
+
</Flex>
|
|
260
|
+
</CardContent>
|
|
261
|
+
</Card>
|
|
262
|
+
|
|
198
263
|
<Card>
|
|
199
264
|
<CardHeader>
|
|
200
265
|
<CardTitle level={2}>prefers-reduced-motion</CardTitle>
|
package/docs/general/reveal.tsx
CHANGED
|
@@ -1,40 +1,308 @@
|
|
|
1
|
-
import { Card, CardContent, StatCard } from "@godxjp/ui/data-display";
|
|
2
|
-
import { Reveal } from "@godxjp/ui/general";
|
|
3
|
-
import { Flex, PageContainer, ResponsiveGrid } from "@godxjp/ui/layout";
|
|
1
|
+
import { Badge, Card, CardContent, CardHeader, CardTitle, StatCard } from "@godxjp/ui/data-display";
|
|
2
|
+
import { Heading, Reveal, Text } from "@godxjp/ui/general";
|
|
3
|
+
import { Flex, PageContainer, ResponsiveGrid, Separator } from "@godxjp/ui/layout";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Reveal — the official entrance-motion primitive (staggered fade-up). Reads the DS motion tokens
|
|
7
7
|
* (`--duration-slow`, `--ease-emphasized`, `--reveal-distance`, `--reveal-stagger-step`) and honours
|
|
8
8
|
* `prefers-reduced-motion`, replacing hand-rolled `@keyframes` + `.app-reveal`/`.d1..d6` classes.
|
|
9
|
+
*
|
|
10
|
+
* `on` moves the TRIGGER and nothing else (gh#829) — `mount` (default) or `view`. There is no
|
|
11
|
+
* second `ScrollReveal` component: same keyframes, same tokens, same reduced-motion contract.
|
|
12
|
+
*
|
|
13
|
+
* This page is deliberately TALL. `on="view"` observes against the document viewport, so a demo
|
|
14
|
+
* that fits on one screen would reveal everything at once and prove nothing — scroll it.
|
|
15
|
+
*
|
|
9
16
|
* Composed only from real @godxjp/ui components.
|
|
10
17
|
*/
|
|
11
|
-
export default function Demo() {
|
|
12
|
-
const stats = [
|
|
13
|
-
{ label: "本日の売上", value: "¥1,240,000" },
|
|
14
|
-
{ label: "新規顧客", value: "38" },
|
|
15
|
-
{ label: "未処理", value: "12" },
|
|
16
|
-
{ label: "承認待ち", value: "5" },
|
|
17
|
-
];
|
|
18
18
|
|
|
19
|
+
const STATS = [
|
|
20
|
+
{ label: "本日の売上", value: "¥1,240,000" },
|
|
21
|
+
{ label: "新規顧客", value: "38" },
|
|
22
|
+
{ label: "未処理", value: "12" },
|
|
23
|
+
{ label: "承認待ち", value: "5" },
|
|
24
|
+
] as const;
|
|
25
|
+
|
|
26
|
+
const DELAY_LADDER = [0, 1, 2, 3, 4, 5, 6] as const;
|
|
27
|
+
|
|
28
|
+
const AMOUNTS = [
|
|
29
|
+
{
|
|
30
|
+
amount: "some",
|
|
31
|
+
title: 'amount="some"(既定)',
|
|
32
|
+
body: "1 ピクセルでも入った瞬間に入場する。IntersectionObserver の threshold: 0。最も早く、遅れて見える失敗をしない既定値。",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
amount: 0.5,
|
|
36
|
+
title: "amount={0.5}",
|
|
37
|
+
body: "0..1 の明示的な比率。半分見えてから入場する。読み始める前に動きが終わっていてほしい場面で使う。",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
amount: "all",
|
|
41
|
+
title: 'amount="all"',
|
|
42
|
+
body: "箱全体が見えてから。比率は要素自身の箱に対して測られるため、ビューポートより背の高い要素は 1 に到達できない。その場合は到達可能な最大値まで自動でクランプされ、決して隠れたままにならない。",
|
|
43
|
+
},
|
|
44
|
+
] as const;
|
|
45
|
+
|
|
46
|
+
/** Long-form body copy. The scroll depth here is the demo, not filler around it. */
|
|
47
|
+
const SECTIONS = [
|
|
48
|
+
{
|
|
49
|
+
id: "s1",
|
|
50
|
+
eyebrow: "01",
|
|
51
|
+
title: "在庫の締め",
|
|
52
|
+
body: "倉庫 B の棚卸しは 18 時に締め、差異は翌営業日の朝会までに共有する。締め処理の間は在庫の移動伝票を受け付けない。",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "s2",
|
|
56
|
+
eyebrow: "02",
|
|
57
|
+
title: "出荷の波",
|
|
58
|
+
body: "午前便と午後便で積載率が大きく変わる。午後便は返品の再出荷が混ざるため、検品の工数を 1.4 倍で見積もる。",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: "s3",
|
|
62
|
+
eyebrow: "03",
|
|
63
|
+
title: "差異の扱い",
|
|
64
|
+
body: "実地と帳簿の差異は金額ではなく件数で追う。金額で追うと単価の高い一点が全体の傾向を隠してしまう。",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "s4",
|
|
68
|
+
eyebrow: "04",
|
|
69
|
+
title: "承認の流れ",
|
|
70
|
+
body: "1 万円を超える廃棄は倉庫長の承認が要る。承認待ちのまま翌月に跨いだ伝票は、月次の締めから自動で外れる。",
|
|
71
|
+
},
|
|
72
|
+
] as const;
|
|
73
|
+
|
|
74
|
+
export default function Demo() {
|
|
19
75
|
return (
|
|
20
76
|
<PageContainer
|
|
21
77
|
title="Reveal"
|
|
22
|
-
subtitle="
|
|
78
|
+
subtitle="入場モーション · on=mount | view · モーショントークン準拠 · prefers-reduced-motion で静止"
|
|
23
79
|
>
|
|
24
80
|
<Flex direction="col" gap="lg">
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
81
|
+
{/* ── on × both union values, side by side ── */}
|
|
82
|
+
<Card>
|
|
83
|
+
<CardHeader>
|
|
84
|
+
<CardTitle level={2}>on · 引き金はこの 2 つだけ</CardTitle>
|
|
85
|
+
</CardHeader>
|
|
86
|
+
<CardContent>
|
|
87
|
+
<Flex direction="col" gap="md">
|
|
88
|
+
<Flex direction="col" gap="xs">
|
|
89
|
+
<Text size="xs" tone="muted">
|
|
90
|
+
on="mount"(既定)· 描画された瞬間に入場する。従来の挙動そのままなので、
|
|
91
|
+
既存の呼び出し側は一切動かない。
|
|
92
|
+
</Text>
|
|
93
|
+
<Reveal on="mount">
|
|
94
|
+
<Card>
|
|
95
|
+
<CardContent>読み込み時にフェードアップします。</CardContent>
|
|
96
|
+
</Card>
|
|
97
|
+
</Reveal>
|
|
98
|
+
</Flex>
|
|
99
|
+
|
|
100
|
+
<Flex direction="col" gap="xs">
|
|
101
|
+
<Text size="xs" tone="muted">
|
|
102
|
+
on="view" ·
|
|
103
|
+
ビューポートに入るまで入場を待つ。このカードは画面内にあるので、
|
|
104
|
+
読み込み直後に一度だけ入場する。下までスクロールすると本来の挙動が見られる。
|
|
105
|
+
</Text>
|
|
106
|
+
<Reveal on="view">
|
|
107
|
+
<Card>
|
|
108
|
+
<CardContent>ビューポートに入ってからフェードアップします。</CardContent>
|
|
109
|
+
</Card>
|
|
110
|
+
</Reveal>
|
|
111
|
+
</Flex>
|
|
112
|
+
</Flex>
|
|
113
|
+
</CardContent>
|
|
114
|
+
</Card>
|
|
115
|
+
|
|
116
|
+
{/* ── delay × the whole 0..6 ladder ── */}
|
|
117
|
+
<Card>
|
|
118
|
+
<CardHeader>
|
|
119
|
+
<CardTitle level={2}>delay · 0..6 の段差(ms ではなく序数)</CardTitle>
|
|
120
|
+
</CardHeader>
|
|
121
|
+
<CardContent>
|
|
122
|
+
<Flex direction="col" gap="sm">
|
|
123
|
+
<Text size="xs" tone="muted">
|
|
124
|
+
各段が --reveal-stagger-step 一つ分の遅延を足す。引き金が view
|
|
125
|
+
でも同じ梯子がそのまま効く。
|
|
126
|
+
</Text>
|
|
127
|
+
{DELAY_LADDER.map((step) => (
|
|
128
|
+
<Reveal key={step} on="view" delay={step}>
|
|
129
|
+
<Card>
|
|
130
|
+
<CardContent>
|
|
131
|
+
<Flex direction="row" gap="sm" align="center">
|
|
132
|
+
<Badge>{`delay=${step}`}</Badge>
|
|
133
|
+
<Text tone="muted">
|
|
134
|
+
{step === 0 ? "遅延なし · 即時" : `--reveal-stagger-step × ${step}`}
|
|
135
|
+
</Text>
|
|
136
|
+
</Flex>
|
|
137
|
+
</CardContent>
|
|
138
|
+
</Card>
|
|
139
|
+
</Reveal>
|
|
140
|
+
))}
|
|
141
|
+
</Flex>
|
|
142
|
+
</CardContent>
|
|
143
|
+
</Card>
|
|
144
|
+
|
|
145
|
+
{/* ── the canonical cascade: a stat row ── */}
|
|
146
|
+
<Card>
|
|
147
|
+
<CardHeader>
|
|
148
|
+
<CardTitle level={2}>段送り · 行がカスケードする</CardTitle>
|
|
149
|
+
</CardHeader>
|
|
150
|
+
<CardContent>
|
|
151
|
+
<ResponsiveGrid columns={{ sm: 2, lg: 4 }}>
|
|
152
|
+
{STATS.map((stat, index) => (
|
|
153
|
+
<Reveal key={stat.label} on="view" delay={(index + 1) as 1 | 2 | 3 | 4}>
|
|
154
|
+
<StatCard label={stat.label} value={stat.value} />
|
|
155
|
+
</Reveal>
|
|
156
|
+
))}
|
|
157
|
+
</ResponsiveGrid>
|
|
158
|
+
</CardContent>
|
|
159
|
+
</Card>
|
|
160
|
+
|
|
161
|
+
{/* ── amount × every union shape ── */}
|
|
162
|
+
<Card>
|
|
163
|
+
<CardHeader>
|
|
164
|
+
<CardTitle level={2}>amount · どれだけ見えたら入場するか</CardTitle>
|
|
165
|
+
</CardHeader>
|
|
166
|
+
<CardContent>
|
|
167
|
+
<Flex direction="col" gap="md">
|
|
168
|
+
<Text size="xs" tone="muted">
|
|
169
|
+
Motion の useInView から名前と型をそのまま借りている(some | all | 0..1
|
|
170
|
+
の数値)。借りなかったのは margin(rootMargin は px/%
|
|
171
|
+
しか受け付けず、デザイントークンを読めない)と
|
|
172
|
+
initial(この実装の静止状態は常に「見えている」方なので意味を持たない)。
|
|
173
|
+
</Text>
|
|
174
|
+
{AMOUNTS.map((entry) => (
|
|
175
|
+
<Flex key={entry.title} direction="col" gap="xs">
|
|
176
|
+
<Text size="xs" tone="muted">
|
|
177
|
+
{entry.title}
|
|
178
|
+
</Text>
|
|
179
|
+
<Reveal on="view" amount={entry.amount}>
|
|
180
|
+
<Card>
|
|
181
|
+
<CardContent>
|
|
182
|
+
<Text tone="muted">{entry.body}</Text>
|
|
183
|
+
</CardContent>
|
|
184
|
+
</Card>
|
|
185
|
+
</Reveal>
|
|
186
|
+
</Flex>
|
|
187
|
+
))}
|
|
188
|
+
</Flex>
|
|
189
|
+
</CardContent>
|
|
190
|
+
</Card>
|
|
191
|
+
|
|
192
|
+
{/* ── once × both values ── */}
|
|
193
|
+
<Card>
|
|
194
|
+
<CardHeader>
|
|
195
|
+
<CardTitle level={2}>once · 一度きりか、毎回か</CardTitle>
|
|
196
|
+
</CardHeader>
|
|
197
|
+
<CardContent>
|
|
198
|
+
<Flex direction="col" gap="md">
|
|
199
|
+
<Flex direction="col" gap="xs">
|
|
200
|
+
<Text size="xs" tone="muted">
|
|
201
|
+
once(既定 true)· 一度入場したら二度と隠れない。Reveal
|
|
202
|
+
はどちらの引き金でも「一度きりの入場」なので、mount と view
|
|
203
|
+
の意味を揃える既定値。Motion の useInView は false
|
|
204
|
+
が既定だが、あれは汎用の観測フックで、入場プリミティブではない。
|
|
205
|
+
</Text>
|
|
206
|
+
<Reveal on="view">
|
|
207
|
+
<Card>
|
|
208
|
+
<CardContent>上下にスクロールしても再入場しません。</CardContent>
|
|
209
|
+
</Card>
|
|
210
|
+
</Reveal>
|
|
211
|
+
</Flex>
|
|
212
|
+
<Flex direction="col" gap="xs">
|
|
213
|
+
<Text size="xs" tone="muted">
|
|
214
|
+
once={false} · ビューポートを出るたびに隠れ、戻るたびに入場し直す。
|
|
215
|
+
</Text>
|
|
216
|
+
<Reveal on="view" once={false}>
|
|
217
|
+
<Card>
|
|
218
|
+
<CardContent>画面外に出して戻すと、もう一度フェードアップします。</CardContent>
|
|
219
|
+
</Card>
|
|
220
|
+
</Reveal>
|
|
221
|
+
</Flex>
|
|
222
|
+
</Flex>
|
|
223
|
+
</CardContent>
|
|
224
|
+
</Card>
|
|
225
|
+
|
|
226
|
+
{/* ── asChild, under the view trigger: the observed node is the child itself ── */}
|
|
227
|
+
<Card>
|
|
228
|
+
<CardHeader>
|
|
229
|
+
<CardTitle level={2}>asChild · 観測されるのは子要素そのもの</CardTitle>
|
|
230
|
+
</CardHeader>
|
|
231
|
+
<CardContent>
|
|
232
|
+
<Flex direction="col" gap="xs">
|
|
233
|
+
<Text size="xs" tone="muted">
|
|
234
|
+
ラッパーの div を挟まず、子要素に直接マージする。grid / flex
|
|
235
|
+
の直下で余計な箱が組版を壊す場面用。観測対象もその子要素の箱になる。
|
|
236
|
+
</Text>
|
|
237
|
+
<ResponsiveGrid columns={{ sm: 2 }}>
|
|
238
|
+
<Reveal on="view" asChild delay={1}>
|
|
239
|
+
<StatCard label="粗利率" value="31.4%" />
|
|
240
|
+
</Reveal>
|
|
241
|
+
<Reveal on="view" asChild delay={2}>
|
|
242
|
+
<StatCard label="返品率" value="1.8%" />
|
|
243
|
+
</Reveal>
|
|
244
|
+
</ResponsiveGrid>
|
|
245
|
+
</Flex>
|
|
246
|
+
</CardContent>
|
|
247
|
+
</Card>
|
|
248
|
+
|
|
249
|
+
<Separator />
|
|
250
|
+
|
|
251
|
+
{/* ── The real reason the page is long: scroll-triggered entrances in prose ── */}
|
|
252
|
+
<Flex direction="col" gap="md">
|
|
253
|
+
<Heading level={2}>スクロールして確かめる区間</Heading>
|
|
254
|
+
<Text tone="muted">
|
|
255
|
+
ここから下の各節は on="view"
|
|
256
|
+
で、画面に入ったときに初めて入場する。スクロールを止めずに一気に流すと、入場が「遅れて」見えないことも確認できる。
|
|
257
|
+
既定の amount="some" は threshold 0 で、最初の 1 ピクセルで発火する。
|
|
258
|
+
</Text>
|
|
259
|
+
|
|
260
|
+
{SECTIONS.map((section, index) => (
|
|
261
|
+
<Reveal key={section.id} on="view" delay={((index % 3) + 1) as 1 | 2 | 3}>
|
|
262
|
+
<Card>
|
|
263
|
+
<CardHeader>
|
|
264
|
+
<CardTitle level={3}>
|
|
265
|
+
{section.eyebrow} · {section.title}
|
|
266
|
+
</CardTitle>
|
|
267
|
+
</CardHeader>
|
|
268
|
+
<CardContent>
|
|
269
|
+
<Text tone="muted">{section.body}</Text>
|
|
270
|
+
</CardContent>
|
|
271
|
+
</Card>
|
|
35
272
|
</Reveal>
|
|
36
273
|
))}
|
|
37
|
-
</
|
|
274
|
+
</Flex>
|
|
275
|
+
|
|
276
|
+
{/* ── The contract that matters most ── */}
|
|
277
|
+
<Card>
|
|
278
|
+
<CardHeader>
|
|
279
|
+
<CardTitle level={2}>prefers-reduced-motion と、観測できない環境</CardTitle>
|
|
280
|
+
</CardHeader>
|
|
281
|
+
<CardContent>
|
|
282
|
+
<Flex direction="col" gap="sm">
|
|
283
|
+
<Heading level={3}>
|
|
284
|
+
観測器が支配するのはアニメーションであって、可視性ではない
|
|
285
|
+
</Heading>
|
|
286
|
+
<Text tone="muted">
|
|
287
|
+
スタイルシート側の静止状態は「完成後の、完全に見えている」状態で、opacity: 0
|
|
288
|
+
が静止状態になることは一度もない。隠れた状態(data-reveal-state="out")を書くのは、
|
|
289
|
+
ブラウザに IntersectionObserver があり、要素がまだ画面外だと実測できた
|
|
290
|
+
マウント済みコンポーネントだけ。したがってサーバーレンダリング、jsdom、
|
|
291
|
+
IntersectionObserver を持たないブラウザ、そして OS
|
|
292
|
+
のモーション低減設定のいずれでも、内容は最初から見えている。
|
|
293
|
+
</Text>
|
|
294
|
+
<Text tone="muted">
|
|
295
|
+
モーション低減時は観測器そのものを一切生成しない(animation を止めるだけではない)。
|
|
296
|
+
「入場しないまま永久に隠れている」が、この種のコンポーネントで最も多く、最も重い壊れ方だから
|
|
297
|
+
(WCAG 2.2 SC 2.3.3 / SC 2.2.2)。
|
|
298
|
+
</Text>
|
|
299
|
+
<Text tone="muted">
|
|
300
|
+
テーマ側のつまみ: --duration-slow · --ease-emphasized · --reveal-distance ·
|
|
301
|
+
--reveal-stagger-step。on="view" で新しいトークンは一つも増えていない。
|
|
302
|
+
</Text>
|
|
303
|
+
</Flex>
|
|
304
|
+
</CardContent>
|
|
305
|
+
</Card>
|
|
38
306
|
</Flex>
|
|
39
307
|
</PageContainer>
|
|
40
308
|
);
|
|
@@ -30,6 +30,8 @@ import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
|
30
30
|
* golden-ratio type scale (never an arbitrary px); tone/weight are semantic tokens.
|
|
31
31
|
*/
|
|
32
32
|
const sizes = ["2xs", "xs", "sm", "md", "lg", "xl", "2xl"] as const;
|
|
33
|
+
/** The display ramp — its own card below, because it is a different ramp, not three more steps. */
|
|
34
|
+
const displaySizes = ["3xl", "4xl", "5xl"] as const;
|
|
33
35
|
const tones = ["default", "muted", "primary", "success", "warning", "destructive", "info"] as const;
|
|
34
36
|
const levels = [1, 2, 3, 4] as const;
|
|
35
37
|
|
|
@@ -56,7 +58,9 @@ export default function Demo() {
|
|
|
56
58
|
<CardHeader>
|
|
57
59
|
<CardTitle level={2}>Heading levels</CardTitle>
|
|
58
60
|
<CardDescription>
|
|
59
|
-
level sets BOTH the --heading-h* size token and the semantic <h1..h4> element
|
|
61
|
+
level sets BOTH the --heading-h* size token and the semantic <h1..h4> element —
|
|
62
|
+
unless `size` overrides the size alone (see the display ramp below), which leaves the
|
|
63
|
+
document outline where the author put it.
|
|
60
64
|
</CardDescription>
|
|
61
65
|
</CardHeader>
|
|
62
66
|
<CardContent>
|
|
@@ -143,6 +147,92 @@ export default function Demo() {
|
|
|
143
147
|
</CardContent>
|
|
144
148
|
</Card>
|
|
145
149
|
|
|
150
|
+
<Card>
|
|
151
|
+
<CardHeader>
|
|
152
|
+
<CardTitle level={2}>size · 10 段の共通ラダー(Text と Heading が共有)</CardTitle>
|
|
153
|
+
<CardDescription>
|
|
154
|
+
size は Text と Heading が共有する一本のラダーです。2xs…2xl は UI
|
|
155
|
+
ランプ(--font-size-base × φ^¼、管理画面の密度)、3xl…5xl はマーケティング用の
|
|
156
|
+
ディスプレイランプ(--font-size-display と --font-size-display-ratio
|
|
157
|
+
から導出)。別ランプなので、サービスは管理画面に一切触れずにヒーローだけを調整できます。
|
|
158
|
+
ここに至る公開 API が無かったため、どのマーケティングページも自前の font-size
|
|
159
|
+
クラスを書いていました(gh#826)。
|
|
160
|
+
</CardDescription>
|
|
161
|
+
</CardHeader>
|
|
162
|
+
<CardContent>
|
|
163
|
+
<Flex direction="col" gap="lg">
|
|
164
|
+
{/* Heading `size` over the WHOLE ladder. `level` keeps the document outline — every
|
|
165
|
+
row below is the same <h2> — and `size` moves the ramp alone. Ten drawn branches,
|
|
166
|
+
which is the evidence check:frame-coverage wants for a new public prop. */}
|
|
167
|
+
<Flex direction="col" gap="sm">
|
|
168
|
+
{[...sizes, ...displaySizes].map((size) => (
|
|
169
|
+
<Flex key={size} direction="row" gap="sm" align="baseline">
|
|
170
|
+
<Heading level={2} size={size}>
|
|
171
|
+
国境を越える物流を、ひとつに
|
|
172
|
+
</Heading>
|
|
173
|
+
<Text size="2xs" mono tone="muted">
|
|
174
|
+
{`size="${size}"`}
|
|
175
|
+
</Text>
|
|
176
|
+
</Flex>
|
|
177
|
+
))}
|
|
178
|
+
</Flex>
|
|
179
|
+
|
|
180
|
+
{/* The hero shape the two website showcases used to hand-write. `level` and `size`
|
|
181
|
+
are separate axes — antd is the one system that conflates them, and it is also
|
|
182
|
+
the only one with no display range at all (38px ceiling), which is the same fact
|
|
183
|
+
twice. On a marketing page this headline IS the page title and takes `level={1}`:
|
|
184
|
+
a real <h1> at 54px, with no admin screen's <h1> moving. It is `level={2}` here
|
|
185
|
+
only because PageContainer already renders this page's one <h1> (WCAG 1.3.1). */}
|
|
186
|
+
<Flex direction="col" gap="xs">
|
|
187
|
+
<Heading level={2} size="5xl">
|
|
188
|
+
Ship it everywhere
|
|
189
|
+
</Heading>
|
|
190
|
+
<Text size="xl" tone="muted">
|
|
191
|
+
リード文は UI ランプの xl / 2xl のままで十分です — 大きくすべきは見出しだけ。
|
|
192
|
+
</Text>
|
|
193
|
+
<Text size="2xs" mono tone="muted">
|
|
194
|
+
{`<Heading level={1} size="5xl"> + <Text size="xl" tone="muted"> — zero bespoke classes`}
|
|
195
|
+
</Text>
|
|
196
|
+
</Flex>
|
|
197
|
+
|
|
198
|
+
{/* The same ladder on Text — a stat figure is display-sized but is NOT a heading, so
|
|
199
|
+
it must be reachable without inventing an <h*> for it. This is exactly why the
|
|
200
|
+
size axis has to be separate from the semantic one. */}
|
|
201
|
+
<Flex direction="col" gap="xs">
|
|
202
|
+
{displaySizes.map((size) => (
|
|
203
|
+
<Flex key={size} direction="row" gap="sm" align="baseline">
|
|
204
|
+
<Text size={size} weight="bold" tone="primary" tabular>
|
|
205
|
+
98.4%
|
|
206
|
+
</Text>
|
|
207
|
+
<Text size="2xs" mono tone="muted">
|
|
208
|
+
{`<Text size="${size}" weight="bold" tabular>`}
|
|
209
|
+
</Text>
|
|
210
|
+
</Flex>
|
|
211
|
+
))}
|
|
212
|
+
</Flex>
|
|
213
|
+
|
|
214
|
+
{/* Paragraph and Link read the SAME TextSizeProp, so widening the ladder widened
|
|
215
|
+
their public surface too. Drawn over the whole ladder rather than left as an
|
|
216
|
+
undrawn branch — an undrawn branch is what refused the 28.6.0 release. */}
|
|
217
|
+
<Flex direction="col" gap="xs">
|
|
218
|
+
{[...sizes, ...displaySizes].map((size) => (
|
|
219
|
+
<Flex key={size} direction="row" gap="sm" align="baseline">
|
|
220
|
+
<Paragraph size={size} tone="muted">
|
|
221
|
+
「速い」
|
|
222
|
+
</Paragraph>
|
|
223
|
+
<Link href="#typography" size={size}>
|
|
224
|
+
詳しく
|
|
225
|
+
</Link>
|
|
226
|
+
<Text size="2xs" mono tone="muted">
|
|
227
|
+
{`<Paragraph size="${size}"> · <Link size="${size}">`}
|
|
228
|
+
</Text>
|
|
229
|
+
</Flex>
|
|
230
|
+
))}
|
|
231
|
+
</Flex>
|
|
232
|
+
</Flex>
|
|
233
|
+
</CardContent>
|
|
234
|
+
</Card>
|
|
235
|
+
|
|
146
236
|
<Card>
|
|
147
237
|
<CardHeader>
|
|
148
238
|
<CardTitle level={2}>Tone + weight</CardTitle>
|