@kazamitte/design-token 0.0.0-stage → 0.3.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kazamitte
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,272 @@
1
- # Temporary Holding Version
1
+ # @kazamitte/design-token
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A design token layer for Tailwind CSS v4 — color, typography, and style themes. Requires Tailwind v4.
4
+
5
+ Every color token clears WCAG AA for normal text. No single scale can do that across every hue, so the steps differ from color to color.
6
+
7
+ ## Setup
8
+
9
+ Install the package alongside Tailwind CSS v4, which it expects as a peer dependency.
10
+
11
+ ```sh
12
+ npm install @kazamitte/design-token tailwindcss
13
+ ```
14
+
15
+ ### CSS
16
+
17
+ Import Tailwind first, then the package. The package does not import Tailwind itself, so it is loaded once and you keep control over how it is imported.
18
+
19
+ ```css
20
+ @import "tailwindcss";
21
+
22
+ @import "@kazamitte/design-token/index.css";
23
+ @import "@kazamitte/design-token/theme/color/default.css";
24
+ @import "@kazamitte/design-token/theme/style/soft.css";
25
+
26
+ @source '../node_modules/@kazamitte/design-token';
27
+
28
+ body {
29
+ background-color: var(--r-base-bg);
30
+ color: var(--r-base-fg);
31
+ }
32
+ ```
33
+
34
+ The `@source` directive is required because Tailwind v4 excludes `node_modules` from scanning by default. See [Detecting classes in source files](https://tailwindcss.com/docs/detecting-classes-in-source-files) in the Tailwind docs.
35
+
36
+ ### JavaScript
37
+
38
+ The JavaScript API exposes one function, for generating a color theme.
39
+
40
+ ```ts
41
+ import { generateColorTheme } from "@kazamitte/design-token";
42
+ ```
43
+
44
+ Everything the package does is delivered as CSS, so the JavaScript API is optional. Reach for it to generate several themes at once, or to swap themes at runtime from the app. See [Color theme generation](./docs/generate-color-theme.md) for usage.
45
+
46
+ ### Further reading
47
+
48
+ - [Color theme generation](./docs/generate-color-theme.md) — writing a theme file with `generateColorTheme`
49
+ - [Color pairings](./docs/color-pairings.md) — which secondary, base and link colors suit each primary (Japanese)
50
+ - [Fixing class conflicts with tailwind-merge](./docs/tailwind-merge.md) — registering the package's utilities with tailwind-merge
51
+
52
+ ---
53
+
54
+ ## Layout
55
+
56
+ ```text
57
+ tailwind/
58
+ ├─ base/
59
+ │ ├─ initialize.css clears the Tailwind color / font / text namespaces
60
+ │ ├─ color/
61
+ │ │ ├─ palette.css the raw palette (every --color-*)
62
+ │ │ ├─ semantic.css --c-<hue>-*, 26 hues x 15 tokens, light/dark
63
+ │ │ ├─ status.css error / warning / success / info
64
+ │ │ └─ utility.css role utilities (primary-bg, base-fg, …)
65
+ │ ├─ typography/
66
+ │ │ ├─ font.css
67
+ │ │ └─ utility.css
68
+ │ └─ style/
69
+ │ ├─ animation.css the keyframes and the --animate-* steps on them
70
+ │ ├─ radius.css the radius scale
71
+ │ ├─ shadow.css the shadow scale and the colors it is built from
72
+ │ └─ z-index.css the layering scale
73
+ ├─ theme/
74
+ │ ├─ color/ base / primary / secondary / link
75
+ │ └─ style/ neutral / editorial / soft / dense
76
+ └─ index.css loads the base, the dark variant, duration utilities
77
+ ```
78
+
79
+ ## Color
80
+
81
+ ### Roles
82
+
83
+ - Theme colors
84
+ - primary
85
+ - secondary
86
+ - base
87
+ - link
88
+ - Status colors
89
+ - error — red
90
+ - warning — amber
91
+ - success — green
92
+ - info — blue
93
+
94
+ Each role carries 15 tokens.
95
+
96
+ | Group | Tokens |
97
+ | ---------- | ------------------------------------------------------------------ |
98
+ | Text | `fg-strong`, `fg`, `fg-muted`, `fg-subtle` |
99
+ | Background | `bg`, `bg-subtle`, `bg-muted`, `bg-selected`, `bg-solid` |
100
+ | Border | `border-subtle`, `border-muted`, `border-selected`, `border-solid` |
101
+ | Contrast | `fg-contrast` |
102
+ | Focus ring | `focus-ring` |
103
+
104
+ ### Pairs that clear the contrast requirement
105
+
106
+ These four clear the WCAG AA threshold for normal text (4.5:1) across all 26 hues, in both light and dark.
107
+
108
+ - `fg-strong` on `bg-selected` — emphasized text on a hover surface
109
+ - `fg` on `bg-muted` — body text on a selected surface
110
+ - `fg-muted` on `bg` — secondary text on a tinted surface
111
+ - `fg-contrast` on `bg-solid` — the label on a filled control
112
+
113
+ `fg-subtle` is exempt from the requirement: it is meant for decoration and disabled states.
114
+
115
+ Roles can take different hues, so text from one role often sits on a surface from another — base text on a primary surface, or a link on a base surface. These clear 4.5:1 for text from any hue on a surface from any other hue, in both light and dark.
116
+
117
+ - `fg-strong` on `bg`, `bg-subtle` or `bg-muted`
118
+ - `fg` on `bg` or `bg-subtle`
119
+ - `fg-muted` on `bg`
120
+ - `fg-contrast` on `bg-solid`
121
+
122
+ For links, `link-fg` is the `fg-muted` of the link hue and `link-fg-strong` is its `fg`, so a link on any `bg` clears, and `link-fg-strong` also clears on `bg-subtle`.
123
+
124
+ One exception: in dark, indigo `fg` on a yellow or lime `bg-subtle` falls to about 4.3:1.
125
+
126
+ For non-text contrast (WCAG 1.4.11, 3:1), `border-solid`, `bg-solid` and `focus-ring` share a step and stand out from `bg` and `bg-subtle` of any hue, and from `bg-muted` of the same hue or of a neutral. Use them for input borders, checkboxes, progress bars, focus indicators and the like. A focus ring drawn against a solid control matches its fill, so separate the two with an offset in a `bg` color.
127
+
128
+ In dark, `bg-solid` is a light fill and `fg-contrast` a dark label. No fill dark enough for a white label also stands out 3:1 from a dark surface.
129
+
130
+ `fg-subtle` is not held to 3:1.
131
+
132
+ Contrast is measured the way axe-core measures it: out-of-gamut colors are clipped to sRGB, nothing is rounded, and a pair passes only when its ratio is strictly greater than the threshold.
133
+
134
+ ### Semantic tokens
135
+
136
+ All 26 hues expose the same 15 tokens under `--c-*`. Use them where a specific color is wanted directly — tags, categories, charts.
137
+
138
+ ```html
139
+ <span class="bg-(--c-lime-bg-subtle) text-(--c-lime-fg)">Draft</span>
140
+ ```
141
+
142
+ ### Palette
143
+
144
+ Adjusted from the Tailwind 4.3.0 default theme. All 26 hues are defined, including mauve, olive, mist and taupe, which Tailwind added in 4.2.
145
+
146
+ Every hue also carries two steps Tailwind does not have — 350 and 750 — each the OKLCH midpoint of its neighbours. They let a semantic token sit between two Tailwind steps where neither one holds the contrast requirement across hues.
147
+
148
+ ## Typography
149
+
150
+ Modeled on [digital-go-jp/tailwind-theme-plugin](https://github.com/digital-go-jp/tailwind-theme-plugin).
151
+
152
+ Each utility sets font-size, line-height and letter-spacing together.
153
+
154
+ ```html
155
+ <h1 class="text-display-48">…</h1>
156
+ <p class="text-body-16">…</p>
157
+ ```
158
+
159
+ | Utility | Variants | Use |
160
+ | ------------------ | ------------------------- | ---------------- |
161
+ | `text-display-*` | 64, 56, 48, 44 | Hero, page title |
162
+ | `text-highlight-*` | 36, 32, 28, 26, 24, 22 | Headings |
163
+ | `text-body-*` | 20, 18, 16 | Body copy |
164
+ | `text-dense-*` | 18, 16, 14 (+ `-compact`) | Tables, dense UI |
165
+ | `text-oneline-*` | 18, 16, 14 | Labels, buttons |
166
+ | `text-mono-*` | 18, 16, 14 | Code |
167
+
168
+ ## Style
169
+
170
+ A style preset sets spacing, radius, shadow, duration and easing together, so that the whole interface reads as one.
171
+
172
+ | Preset | Character |
173
+ | ----------- | ---------------------------------------------------------------------- |
174
+ | `neutral` | A balanced default |
175
+ | `editorial` | Sharp corners and the shallowest shadows; hierarchy comes from borders |
176
+ | `soft` | Generous rounding and pronounced elevation |
177
+ | `dense` | Minimal rounding and elevation, for information-dense UI |
178
+
179
+ ```html
180
+ <div class="rounded-surface shadow-raised hover:shadow-raised-hover"></div>
181
+ ```
182
+
183
+ Radius and easing do not name their utilities after the `@theme` namespace they live in: `--radius-*` generates `rounded-*`, and `--ease-*` generates `ease-*`.
184
+
185
+ `--spacing` is the base step of the spacing scale and has no utility of its own. `p-4` is emitted as `calc(var(--spacing) * 4)`, so switching preset moves `p-*`, `m-*`, `gap-*`, `size-*` and the rest together — note that the sizing utilities read the same variable.
186
+
187
+ | Utility | Variants | Sets |
188
+ | --------------- | ---------------------------------------------- | ---------------------------- |
189
+ | `p-*` `gap-*` … | every utility reads `--spacing`, the base step | spacing and sizing |
190
+ | `rounded-*` | tight, control, surface, overlay, pill | `border-radius` |
191
+ | `shadow-*` | flat, raised, floating (+ `-hover`), overlay | `box-shadow` |
192
+ | `duration-*` | instant, transition, enter | `transition-duration` |
193
+ | `ease-*` | standard, emphasized | `transition-timing-function` |
194
+ | `animate-*` | fade / scale / slide, in and out | `animation` |
195
+
196
+ Every duration collapses to 1ms under `prefers-reduced-motion: reduce`.
197
+
198
+ ### Motion
199
+
200
+ `animate-*` covers the enter and exit of the things that appear over the page.
201
+
202
+ | Utility | Use |
203
+ | --------------------------- | ------------------------ |
204
+ | `animate-fade-in` / `-out` | Scrims, tab panels |
205
+ | `animate-scale-in` / `-out` | Dialogs, menus, popovers |
206
+ | `animate-slide-in-from-*` | Drawers, sheets, toasts |
207
+ | `animate-slide-out-to-*` | The same, leaving |
208
+
209
+ The slide steps take `top`, `right`, `bottom` and `left`, and travel the full
210
+ size of the element unless it says otherwise.
211
+
212
+ ```html
213
+ <div class="animate-slide-in-from-bottom [--slide-distance:1rem]"></div>
214
+ ```
215
+
216
+ Every step is composed from the preset's duration and easing, so it follows the
217
+ preset and collapses to 1ms under reduced motion. Tailwind's own `animate-spin`,
218
+ `-ping`, `-pulse` and `-bounce` are untouched and keep animating under reduced
219
+ motion.
220
+
221
+ Exit animations only play while the element is still in the tree; unmount it on
222
+ `animationend`.
223
+
224
+ Collapse and expand are not included. They need a measured height, which no
225
+ keyframe can supply.
226
+
227
+ ### Layering
228
+
229
+ `z-*` follows [Chakra UI's z-index scale](https://chakra-ui.com/docs/theming/z-index), so the order matches what component libraries assume — notably `popover` above `modal`, since a select opened inside a dialog has to clear it.
230
+
231
+ | Step | Value | Step | Value |
232
+ | ---------- | ----- | ---------- | ---------- |
233
+ | `hide` | -1 | `overlay` | 1300 |
234
+ | `base` | 0 | `modal` | 1400 |
235
+ | `docked` | 10 | `popover` | 1500 |
236
+ | `dropdown` | 1000 | `skip-nav` | 1600 |
237
+ | `sticky` | 1100 | `toast` | 1700 |
238
+ | `banner` | 1200 | `tooltip` | 1800 |
239
+ | | | `max` | 2147483647 |
240
+
241
+ Layering does not vary by preset, so these are used directly rather than through
242
+ one. `z-10` and `z-auto` still work alongside them. `<dialog>` and the Popover
243
+ API render in the top layer, where z-index has no effect.
244
+
245
+ ### Shadow
246
+
247
+ `--shadow-2xs` through `--shadow-2xl` are the base scale, seven steps. A preset picks one step per role and does nothing more; light and dark are resolved inside the scale, so no preset carries a dark override.
248
+
249
+ Each step reads its colors through the custom properties below. These four are all that `[data-mode='dark']` swaps.
250
+
251
+ | Custom property | Light | Dark |
252
+ | ----------------------- | --------- | --------- |
253
+ | `--shadow-color-subtle` | black 5% | black 35% |
254
+ | `--shadow-color` | black 10% | black 50% |
255
+ | `--shadow-color-strong` | black 25% | black 70% |
256
+ | `--shadow-rim-color` | black 7% | white 12% |
257
+
258
+ A cast shadow barely reads on a dark surface, so every step opens with a `0 0 0 1px var(--shadow-rim-color)` rim. In dark it is that white rim that separates a surface from its background, while the heavier cast carries the elevation. `--shadow-2xs` is the rim alone — an edge with no elevation.
259
+
260
+ To recolor the shadows, redefine those four under `:root` and `[data-mode='dark']`; the whole scale, and every preset built on it, follows.
261
+
262
+ ---
263
+
264
+ ## AI assistance
265
+
266
+ The documentation and code in this package were written with AI agents — Claude and ChatGPT. Every output was reviewed by a human before it was committed.
267
+
268
+ ## License
269
+
270
+ MIT. See [LICENSE](./LICENSE).
271
+
272
+ The color palette comes from Tailwind CSS and the typography scale from digital-go-jp/tailwind-theme-plugin, both MIT. Their notices are reproduced in [THIRD-PARTY-NOTICES.md](./THIRD-PARTY-NOTICES.md).
@@ -0,0 +1,69 @@
1
+ # Third-party notices
2
+
3
+ This package contains material derived from the projects below. Both are
4
+ distributed under the MIT License, which requires its copyright notice to
5
+ travel with any substantial portion of the work. The notices are reproduced
6
+ here in full.
7
+
8
+ ## Tailwind CSS
9
+
10
+ `tailwind/base/color/palette.css` holds the 286 color values of the Tailwind
11
+ CSS v4.3 default theme. Those colors are unchanged; only the hue component of
12
+ the achromatic steps is written as `0` rather than `none`.
13
+
14
+ The same file adds 52 colors that are not in Tailwind: a 350 and a 750 step
15
+ for each of the 26 hues, each the OKLCH midpoint of the two Tailwind steps
16
+ around it. They are derived from the Tailwind values above.
17
+
18
+ Tailwind CSS is also the framework this package extends.
19
+
20
+ > MIT License
21
+ >
22
+ > Copyright (c) Tailwind Labs, Inc.
23
+ >
24
+ > Permission is hereby granted, free of charge, to any person obtaining a copy
25
+ > of this software and associated documentation files (the "Software"), to deal
26
+ > in the Software without restriction, including without limitation the rights
27
+ > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28
+ > copies of the Software, and to permit persons to whom the Software is
29
+ > furnished to do so, subject to the following conditions:
30
+ >
31
+ > The above copyright notice and this permission notice shall be included in
32
+ > all copies or substantial portions of the Software.
33
+ >
34
+ > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35
+ > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36
+ > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37
+ > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38
+ > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39
+ > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40
+ > SOFTWARE.
41
+
42
+ ## digital-go-jp/tailwind-theme-plugin
43
+
44
+ The typography scale in `tailwind/base/typography/utility.css` follows the
45
+ Digital Agency Design System as published through that plugin. The utilities
46
+ are written from scratch for Tailwind v4; the font sizes, line heights and
47
+ letter spacings come from the plugin's scale.
48
+
49
+ > MIT License
50
+ >
51
+ > Copyright (c) 2023 デジタル庁
52
+ >
53
+ > Permission is hereby granted, free of charge, to any person obtaining a copy
54
+ > of this software and associated documentation files (the "Software"), to deal
55
+ > in the Software without restriction, including without limitation the rights
56
+ > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
57
+ > copies of the Software, and to permit persons to whom the Software is
58
+ > furnished to do so, subject to the following conditions:
59
+ >
60
+ > The above copyright notice and this permission notice shall be included in
61
+ > all copies or substantial portions of the Software.
62
+ >
63
+ > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
64
+ > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
65
+ > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
66
+ > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
67
+ > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
68
+ > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
69
+ > SOFTWARE.
@@ -0,0 +1,25 @@
1
+ export type ChromaticColor =
2
+ | 'red' | 'orange' | 'amber' | 'yellow' | 'lime'
3
+ | 'green' | 'emerald' | 'teal' | 'cyan' | 'sky'
4
+ | 'blue' | 'indigo' | 'violet' | 'purple' | 'fuchsia'
5
+ | 'pink' | 'rose';
6
+
7
+ export type BaseColor =
8
+ | 'slate' | 'gray' | 'zinc' | 'neutral' | 'stone'
9
+ | 'mauve' | 'olive' | 'mist' | 'taupe';
10
+
11
+ export type Palette = ChromaticColor | BaseColor;
12
+
13
+ export interface ColorTheme {
14
+ base: Palette;
15
+ primary: Palette;
16
+ secondary: Palette;
17
+ link: Palette;
18
+ }
19
+
20
+ /**
21
+ * Generates the contents of a color theme stylesheet.
22
+ *
23
+ * Returns CSS as a string.
24
+ */
25
+ export declare const generateColorTheme: (theme: ColorTheme) => string;
@@ -0,0 +1,60 @@
1
+ // indent width: 2 half-width spaces
2
+ const INDENT = " ".repeat(2);
3
+
4
+ /**
5
+ * Role token names.
6
+ *
7
+ * These mirror the `--r-<role>-*` custom properties that
8
+ * `theme/color/default.css` defines and `base/color/utility.css`
9
+ * consumes, so generated themes are drop-in replacements for it.
10
+ */
11
+ const ROLE_TOKENS = [
12
+ "bg",
13
+ "bg-subtle",
14
+ "bg-muted",
15
+ "bg-selected",
16
+ "bg-solid",
17
+ "border-subtle",
18
+ "border-muted",
19
+ "border-selected",
20
+ "border-solid",
21
+ "fg-strong",
22
+ "fg",
23
+ "fg-muted",
24
+ "fg-subtle",
25
+ "fg-contrast",
26
+ "focus-ring",
27
+ ];
28
+
29
+ const roleBlock = (role, color) => {
30
+ const lines = ROLE_TOKENS.map(
31
+ (t) => `${INDENT}--r-${role}-${t}: var(--c-${color}-${t});`,
32
+ );
33
+ return lines.join("\n");
34
+ };
35
+
36
+ const linkBlock = (color) =>
37
+ [
38
+ `${INDENT}--r-link-fg: var(--c-${color}-fg-muted);`,
39
+ `${INDENT}--r-link-fg-strong: var(--c-${color}-fg);`,
40
+ ].join("\n");
41
+
42
+ /** Generates the contents of a color theme stylesheet. */
43
+ export const generateColorTheme = (theme) => {
44
+ const { base, primary, secondary, link } = theme;
45
+
46
+ return `:root {
47
+ ${INDENT}/* base - ${base} */
48
+ ${roleBlock("base", base)}
49
+
50
+ ${INDENT}/* primary - ${primary} */
51
+ ${roleBlock("primary", primary)}
52
+
53
+ ${INDENT}/* secondary - ${secondary} */
54
+ ${roleBlock("secondary", secondary)}
55
+
56
+ ${INDENT}/* link - ${link} */
57
+ ${linkBlock(link)}
58
+ }
59
+ `;
60
+ };
@@ -0,0 +1,220 @@
1
+ # 配色の組み合わせ
2
+
3
+ primaryを決めたあとに、secondary・base・linkをどこから選ぶかの一覧である。出発点として使い、最終判断はデザインに委ねる。
4
+
5
+ ## 推奨の組み合わせ表
6
+
7
+ SecondaryとBase (harmonized)は推奨順に並べている。Base (achromatic)とLinkはどの候補を選んでもよく、既定値は列の1番目である。
8
+
9
+ | Primary | Secondary | Base (harmonized) | Base (achromatic) | Link |
10
+ | --------- | ------------------------------- | ------------------- | ------------------- | ---------------------------- |
11
+ | `red` | indigo, fuchsia, purple, violet | taupe, stone | neutral, zinc, gray | indigo, violet, purple, blue |
12
+ | `orange` | indigo, purple, violet, fuchsia | taupe, stone | neutral, zinc, gray | indigo, violet, purple, blue |
13
+ | `amber` | indigo, violet, purple, blue | stone, taupe, olive | neutral, zinc, gray | indigo, violet, purple, blue |
14
+ | `yellow` | indigo, violet, purple, blue | olive, stone | neutral, zinc, gray | indigo, violet, purple, blue |
15
+ | `lime` | indigo, violet, purple, blue | olive, stone | neutral, gray, zinc | indigo, violet, purple, blue |
16
+ | `green` | indigo, blue, violet, purple | olive, mist | neutral, gray, zinc | indigo, violet, purple, blue |
17
+ | `emerald` | indigo, blue, violet, purple | mist, olive | neutral, gray, zinc | indigo, violet, purple, blue |
18
+ | `teal` | lime, orange, amber, yellow | mist, slate | neutral, gray, zinc | indigo, violet, purple, blue |
19
+ | `cyan` | lime, yellow, amber, orange | mist, slate | neutral, gray, zinc | indigo, violet, purple, blue |
20
+ | `sky` | lime, yellow, amber, orange | slate, mist | neutral, gray, zinc | indigo, violet, purple, blue |
21
+ | `blue` | lime, yellow, amber, orange | slate, mist | neutral, gray, zinc | indigo, violet, purple, blue |
22
+ | `indigo` | lime, yellow, amber, orange | slate, mauve | neutral, zinc, gray | indigo, violet, purple, blue |
23
+ | `violet` | yellow, lime, amber, orange | mauve, slate | neutral, zinc, gray | indigo, violet, purple, blue |
24
+ | `purple` | yellow, amber, lime, orange | mauve, slate | neutral, zinc, gray | indigo, violet, purple, blue |
25
+ | `fuchsia` | amber, yellow, orange, lime | mauve, slate | neutral, zinc, gray | indigo, violet, purple, blue |
26
+ | `pink` | orange, amber, yellow, red | mauve, taupe | neutral, zinc, gray | indigo, violet, purple, blue |
27
+ | `rose` | indigo, fuchsia, purple, violet | taupe, stone | neutral, zinc, gray | indigo, violet, purple, blue |
28
+
29
+ ## Secondary の選び方
30
+
31
+ Secondaryは、P型・D型の色覚シミュレーション後でもprimaryとできるだけ離れて見える色を選ぶ。
32
+
33
+ - P型(1型色覚/protan): 赤系の光に対する感度が低い、または欠けるタイプ
34
+ - D型(2型色覚/deutan): 緑系の光に対する感度が低い、または欠けるタイプ
35
+
36
+ primary以外の16色すべてについて、P型・D型に変換した後の色差を、ΔE2000(CIEDE2000)指標を用いて、2色の知覚的な違いを数値化する。0に近いほど似ており、値が大きいほど違って見える。
37
+
38
+ なお、T型(3型色覚/tritan)は、青系の色に対応するが、まれな色覚特性であるため今回の評価からは除外している。
39
+
40
+ 評価には次の4つの表示条件を使う。
41
+
42
+ - ライトモードの`bg-solid`(step 700)
43
+ - ダークモードの`bg-solid`(step 350)
44
+ - ライトモードの`fg-subtle`
45
+ - ダークモードの`fg-subtle`
46
+
47
+ P型・D型それぞれで測るため、合計8条件になる。そのうち最も小さいΔE2000値を、そのprimaryとsecondaryの組み合わせのスコアとする。
48
+
49
+ つまり、ある条件だけで大きく離れて見える色ではなく、P型・D型やライト・ダークをまたいでも安定して見分けやすい色を優先している。
50
+
51
+ そのスコアが大きい順に上位4色をSecondary候補とした。
52
+
53
+ ### P型・D型での見え方
54
+
55
+ 赤と緑を見分けにくい色覚(P型・D型)では、このパレットの多くの色が「黄っぽく見える色」と「青っぽく見える色」に大きく分かれる。
56
+
57
+ - 黄グループ: `rose`, `red`, `orange`, `amber`, `yellow`, `lime`, `green`, `emerald`
58
+ - 青グループ: `cyan`, `sky`, `blue`, `indigo`, `violet`, `purple`, `fuchsia`
59
+ - 境界にある色: `teal`, `pink`
60
+
61
+ `teal`と`pink`はP型とD型で見え方の方向が異なるため、どちらか一方のグループには固定していない。
62
+
63
+ 同じ側にある色どうしはP型・D型への変換後に色相差が小さくなり、主に明るさや鮮やかさの違いとして知覚されやすい。たとえば`red`と`green`は、通常色覚では大きく異なるが、P型・D型ではどちらも黄寄りに見える。
64
+
65
+ ### 数値での評価
66
+
67
+ P型・D型の見え方の変換にはMachado et al. (2009) の行列を使う。severityは1.0で、Chromium DevToolsの色覚エミュレーションで使われている行列と同じものである。
68
+
69
+ 変換手順は次のとおり。
70
+
71
+ ```text
72
+ OKLCH
73
+ → sRGB 8bit
74
+ → 線形RGB
75
+ → Machado 2009 行列
76
+ → CIELAB
77
+ → ΔE2000
78
+ ```
79
+
80
+ 2色の違いはΔE2000で測る。0に近いほど似ており、値が大きいほど離れている。
81
+
82
+ この評価では、十分な余裕を持たせるため20以上をひとつの目安としている。これはP型・D型における一般的な知覚閾値を意味するものではなく、この配色評価のための基準である。
83
+
84
+ 評価には、実際にsemantic tokenが参照する次の条件を使う。
85
+
86
+ - ライトモードの`bg-solid`: step 700
87
+ - ダークモードの`bg-solid`: step 350
88
+ - ライトモードの`fg-subtle`: 色ごとにstep 500または600
89
+ - ダークモードの`fg-subtle`: 色ごとにstep 500または600
90
+
91
+ P型・D型それぞれについて4つの表示条件を測り、8条件のうち最も小さいΔE2000をその組み合わせの値とする。
92
+
93
+ 代表的な結果は次のとおり。
94
+
95
+ - `teal`と`pink`を除く15色では、表の上位4候補はすべて26以上(`rose`が26〜27、`red`と`emerald`が35前後、ほかは40以上)
96
+ - `teal`では23〜24程度
97
+ - `pink`では最大でも16程度
98
+ - `red` × `green`: 6
99
+ - `orange` × `green`: 7
100
+ - `rose` × `green`: 3
101
+
102
+ `pink`は、この評価で採用した20の目安を満たすSecondary候補がない。PrimaryとSecondaryを色だけで明確に区別する必要がある用途では注意が必要である。
103
+
104
+ ## Base の選び方
105
+
106
+ Baseは、primaryに色みを寄せるharmonizedと、色みを抑えるachromaticの2系統から選ぶ。
107
+
108
+ ### harmonized — primaryに寄せた灰色
109
+
110
+ primaryと色相の近い灰色を選ぶ。画面全体に統一感を持たせながら、地の色としては灰色に近い見た目を保つ。
111
+
112
+ 候補は次の6色である。
113
+
114
+ - `taupe`
115
+ - `stone`
116
+ - `olive`
117
+ - `mist`
118
+ - `slate`
119
+ - `mauve`
120
+
121
+ 順位にはstep 500のOKLCH hueを使い、primaryとの色相角の差が小さい順に並べる。
122
+
123
+ 候補には常に上位2色を載せる。3番目の色もprimaryとの色相差が40°以内であれば候補に加える。
124
+
125
+ ### achromatic — 色みを抑えた灰色
126
+
127
+ primaryを灰色の上で目立たせたい場合や、画面内ですでに多くの色を使っている場合に使う。
128
+
129
+ 候補は次の3色で固定する。
130
+
131
+ - `neutral`
132
+ - `zinc`
133
+ - `gray`
134
+
135
+ 既定値は色みを持たない`neutral`である。
136
+
137
+ 残りの`zinc`と`gray`は、step 500のOKLCH hueがprimaryから近い順に並べる。順位は選択時の参考であり、どちらを使ってもよい。
138
+
139
+ ## Link の選び方
140
+
141
+ Linkはprimaryとは独立して選ぶ。
142
+
143
+ `link-fg`と`link-fg-strong`の2トークンを使い、それぞれ選んだ色の`fg-muted`と`fg`を参照する。
144
+
145
+ 表に載せた4色で、参照するstepは次のとおり。
146
+
147
+ | Mode | `link-fg` | `link-fg-strong` |
148
+ | ----- | --------- | ---------------- |
149
+ | Light | 600 | 700 |
150
+ | Dark | 400 (indigoのみ350) | 300 |
151
+
152
+ ### Link の順位
153
+
154
+ 各色について、P型・D型変換後のリンク色と本文色との差を測る。
155
+
156
+ 評価対象は次の4条件である。
157
+
158
+ - Lightの`link-fg`
159
+ - Lightの`link-fg-strong`
160
+ - Darkの`link-fg`
161
+ - Darkの`link-fg-strong`
162
+
163
+ さらにP型・D型の両方を評価し、その中で最も小さいΔE2000をその色の値とする。
164
+
165
+ 青グループの結果は次のとおり。
166
+
167
+ - `indigo`: 23
168
+ - `violet`: 22
169
+ - `purple`: 20
170
+ - `blue`: 20
171
+ - `sky`: 17
172
+ - `cyan`: 12
173
+ - `teal`: 5
174
+
175
+ このうち上位4色を候補として表に載せる。
176
+
177
+ - `indigo`, `violet`, `purple`, `blue`
178
+
179
+ 既定値は`indigo`である。
180
+
181
+ どの候補でも、最小値はダークモードのhover時に使うstep 300で発生する。
182
+
183
+ 黄グループでは`lime`、`yellow`、`amber`もダークモードで本文との色差を取りやすい。ただし、一般的なリンク色としての慣習を優先し、標準候補には含めていない。ダークモード主体のUIでは選択肢になり得る。
184
+
185
+ 一方、`red`、`rose`、`teal`、`pink`はP型・D型変換後に本文との差が小さくなりやすいため、リンク色としては避ける。特に`teal`はダークモードで無彩色に近づき、本文との差が小さくなる。
186
+
187
+ ### 色以外でもリンクを示す
188
+
189
+ リンクは下線など、色以外の視覚的な目印を付ける前提とする。
190
+
191
+ WCAG 2.2 Success Criterion 1.4.1 Use of Colorに従い、色だけでリンクであることを伝えない。
192
+
193
+ ## ステータスカラーとの競合
194
+
195
+ ステータスカラーは慣例に沿って次の色を使う。
196
+
197
+ - Error: `red`
198
+ - Warning: `amber`
199
+ - Success: `green`
200
+ - Info: `blue`
201
+
202
+ PrimaryやSecondaryがこれらと同色または近い色になる場合、ブランド表現と状態表現を色だけでは区別しにくくなる。
203
+
204
+ 特に`red`、`amber`、`green`、`blue`はステータスカラーと直接競合する。`rose`や`orange`などの近似色も、用途によっては同じ意味の色に見えることがある。
205
+
206
+ ### 回避策
207
+
208
+ 1. ステータス色とブランド色を近接して配置しない。
209
+ 2. アイコン、形状、ラベルなど色以外の視覚要素を併用する。
210
+ 3. 必要に応じてステータスカラーの変数を個別に上書きする。
211
+ 4. Warningでは、黄と黒の高コントラストな意匠を使う方法もある。
212
+
213
+ 色だけで状態を伝えず、意味の違いを他の視覚表現でも示すことを基本とする。
214
+
215
+ ## 参考文献
216
+
217
+ - カラーユニバーサルデザイン機構(CUDO)『カラーユニバーサルデザイン推奨配色セット ガイドブック』ver.4、2018年。[配布ページ](https://jfly.uni-koeln.de/colorset/)
218
+ - Machado, G. M., Oliveira, M. M., Fernandes, L. A. F. (2009). A Physiologically-based Model for Simulation of Color Vision Deficiency. _IEEE TVCG_ 15(6)
219
+ - Chromium, [Simulating color vision deficiencies in the Blink Renderer](https://developer.chrome.com/docs/chromium/cvd)
220
+ - W3C, [WCAG 2.2 Success Criterion 1.4.1 Use of Color](https://www.w3.org/TR/WCAG22/#use-of-color)