@godxjp/ui 17.0.0 → 18.0.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/card.d.ts +37 -1
- package/dist/components/data-display/card.js +7 -1
- package/dist/components/data-display/data-table.js +18 -3
- package/dist/components/data-display/empty-state.d.ts +1 -1
- package/dist/components/data-display/empty-state.js +5 -1
- package/dist/components/data-display/scroll-area.js +1 -1
- package/dist/components/data-display/table.js +14 -9
- package/dist/components/data-entry/calendar.js +4 -0
- package/dist/components/data-entry/cascader.d.ts +1 -1
- package/dist/components/data-entry/cascader.js +10 -1
- package/dist/components/data-entry/checkbox-group.d.ts +1 -1
- package/dist/components/data-entry/checkbox-group.js +13 -4
- package/dist/components/data-entry/color-picker.d.ts +1 -1
- package/dist/components/data-entry/color-picker.js +5 -2
- package/dist/components/data-entry/command.d.ts +2 -2
- package/dist/components/data-entry/date-picker.d.ts +1 -1
- package/dist/components/data-entry/date-picker.js +11 -1
- package/dist/components/data-entry/date-range-picker.d.ts +1 -1
- package/dist/components/data-entry/date-range-picker.js +11 -3
- package/dist/components/data-entry/form-field.js +7 -3
- package/dist/components/data-entry/month-picker.d.ts +1 -1
- package/dist/components/data-entry/month-picker.js +13 -1
- package/dist/components/data-entry/month-range-picker.d.ts +1 -1
- package/dist/components/data-entry/month-range-picker.js +11 -3
- package/dist/components/data-entry/number-input.d.ts +19 -2
- package/dist/components/data-entry/number-input.js +5 -7
- package/dist/components/data-entry/radio.d.ts +1 -1
- package/dist/components/data-entry/radio.js +11 -3
- package/dist/components/data-entry/search-input.d.ts +3 -2
- package/dist/components/data-entry/search-input.js +5 -2
- package/dist/components/data-entry/search-select.d.ts +1 -1
- package/dist/components/data-entry/search-select.js +51 -10
- package/dist/components/data-entry/select.d.ts +7 -0
- package/dist/components/data-entry/select.js +21 -3
- package/dist/components/data-entry/time-picker.d.ts +1 -1
- package/dist/components/data-entry/time-picker.js +11 -1
- package/dist/components/data-entry/transfer.d.ts +1 -1
- package/dist/components/data-entry/transfer.js +73 -59
- package/dist/components/data-entry/tree-select.d.ts +1 -1
- package/dist/components/data-entry/tree-select.js +10 -1
- package/dist/components/data-entry/upload.d.ts +1 -1
- package/dist/components/data-entry/upload.js +7 -2
- package/dist/components/layout/app-shell.d.ts +1 -1
- package/dist/components/layout/app-shell.js +42 -2
- package/dist/components/layout/breadcrumb.d.ts +7 -1
- package/dist/components/layout/breadcrumb.js +6 -2
- package/dist/components/layout/page-container.d.ts +1 -1
- package/dist/components/layout/page-container.js +32 -22
- package/dist/components/layout/responsive-grid.js +1 -1
- package/dist/components/layout/sidebar.d.ts +1 -1
- package/dist/components/layout/sidebar.js +44 -11
- package/dist/components/layout/split-pane.js +2 -2
- package/dist/components/navigation/app-setting-picker.js +8 -1
- package/dist/components/navigation/pagination.d.ts +1 -1
- package/dist/components/navigation/pagination.js +84 -95
- package/dist/components/navigation/tabs.js +21 -4
- package/dist/i18n/messages/en.json +3 -1
- package/dist/i18n/messages/ja.json +3 -1
- package/dist/i18n/messages/vi.json +3 -1
- package/dist/lib/field-a11y.d.ts +66 -0
- package/dist/lib/field-a11y.js +44 -0
- package/dist/props/components/data-display.prop.d.ts +16 -1
- package/dist/props/components/data-entry.prop.d.ts +69 -19
- package/dist/props/components/layout.prop.d.ts +42 -0
- package/dist/props/components/navigation.prop.d.ts +16 -0
- package/dist/props/registry.d.ts +8 -3
- package/dist/props/registry.js +21 -3
- package/dist/props/vocabulary/data.prop.d.ts +9 -0
- package/dist/props/vocabulary/interaction.prop.d.ts +1 -1
- package/dist/styles/base.css +1 -1
- package/dist/styles/layout.css +46 -9
- package/dist/styles/shell-layout.css +19 -0
- package/dist/styles/text-layout.css +1 -1
- package/dist/tokens/components/card.css +1 -1
- package/dist/tokens/foundation.css +3 -3
- package/package.json +8 -4
- package/scripts/ui-audit.mjs +2 -2
- package/scripts/visual-audit-rules.mjs +2 -2
|
@@ -13,6 +13,15 @@ export type OnRowClickProp<T> = (row: T) => void;
|
|
|
13
13
|
export type ColumnDefProp<T> = {
|
|
14
14
|
key: string;
|
|
15
15
|
header: React.ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* Accessible header text for a column whose `header` is visually empty — the
|
|
18
|
+
* standard case for a row-actions or selection column. Rendered as an
|
|
19
|
+
* `sr-only` label inside the `<th>` so the column keeps a screen-reader name
|
|
20
|
+
* (e.g. "Actions" / "Select") while showing nothing on screen. Required
|
|
21
|
+
* (dev-warned) when `header` is empty; ignored when `header` has visible text.
|
|
22
|
+
* Route the string through `t()` for i18n.
|
|
23
|
+
*/
|
|
24
|
+
ariaLabel?: string;
|
|
16
25
|
render?: (row: T) => React.ReactNode;
|
|
17
26
|
sortable?: boolean;
|
|
18
27
|
width?: string;
|
|
@@ -11,7 +11,7 @@ export type ShapeProp = "default" | "pill" | "sharp";
|
|
|
11
11
|
export type TextSizeProp = "2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
|
|
12
12
|
/** Text colour intent — maps to semantic foreground tokens (no raw palette). */
|
|
13
13
|
export type TextToneProp = "default" | "muted" | "primary" | "success" | "warning" | "destructive" | "info";
|
|
14
|
-
/** Font weight — the
|
|
14
|
+
/** Font weight — the reference-design canon is THREE weights only: `regular` (400 body), `medium` (500
|
|
15
15
|
* heading/label), `bold` (700 emphasis). 600/`semibold` is forbidden. */
|
|
16
16
|
export type FontWeightProp = "regular" | "medium" | "bold";
|
|
17
17
|
/** Heading level — drives both the `--heading-h*` size token and the semantic `<h1..h4>` element. */
|
package/dist/styles/base.css
CHANGED
|
@@ -218,7 +218,7 @@
|
|
|
218
218
|
|
|
219
219
|
/* Semantic heading scale — SEO + accessibility: real <h1>–<h6> elements carry
|
|
220
220
|
* the document outline (one <h1> per page, levels never skipped). Visual size
|
|
221
|
-
* follows the
|
|
221
|
+
* follows the reference-design scale; component titles override via their own class.
|
|
222
222
|
* Headings read --font-family-display (defaults to --font-family-sans → unchanged) for dual-font
|
|
223
223
|
* brands (display face on headings, body face on prose). */
|
|
224
224
|
:where(h1, h2, h3, h4, h5, h6) {
|
package/dist/styles/layout.css
CHANGED
|
@@ -175,6 +175,16 @@
|
|
|
175
175
|
gap: var(--space-inline-lg);
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
/* ResponsiveGrid OWNS its query container. The component wraps the grid in
|
|
179
|
+
* `.ui-responsive-grid-scope` (container-type: inline-size, named `responsive-grid`) so the
|
|
180
|
+
* column breakpoints resolve against the WIDTH AVAILABLE TO THE GRID — not the viewport and not
|
|
181
|
+
* an undeclared ancestor `container-type`. This makes it correct inside a narrow card, a
|
|
182
|
+
* SplitPane aside, or a bare `<div>`; it can never silently stay one column for want of an
|
|
183
|
+
* external container context (gh#165). Named so it queries its own scope, never a closer one. */
|
|
184
|
+
.ui-responsive-grid-scope {
|
|
185
|
+
container: responsive-grid / inline-size;
|
|
186
|
+
}
|
|
187
|
+
|
|
178
188
|
.ui-responsive-grid {
|
|
179
189
|
display: grid;
|
|
180
190
|
gap: var(--space-stack-md);
|
|
@@ -185,24 +195,33 @@
|
|
|
185
195
|
min-width: 0;
|
|
186
196
|
}
|
|
187
197
|
|
|
188
|
-
@container (min-width:
|
|
198
|
+
@container responsive-grid (min-width: 40rem) {
|
|
189
199
|
.ui-responsive-grid {
|
|
190
200
|
grid-template-columns: repeat(var(--responsive-grid-sm, 2), minmax(0, 1fr));
|
|
191
201
|
}
|
|
192
202
|
}
|
|
193
203
|
|
|
194
|
-
@container (min-width:
|
|
204
|
+
@container responsive-grid (min-width: 48rem) {
|
|
195
205
|
.ui-responsive-grid {
|
|
196
206
|
grid-template-columns: repeat(var(--responsive-grid-md, 3), minmax(0, 1fr));
|
|
197
207
|
}
|
|
198
208
|
}
|
|
199
209
|
|
|
200
|
-
@container (min-width:
|
|
210
|
+
@container responsive-grid (min-width: 64rem) {
|
|
201
211
|
.ui-responsive-grid {
|
|
202
212
|
grid-template-columns: repeat(var(--responsive-grid-lg, 4), minmax(0, 1fr));
|
|
203
213
|
}
|
|
204
214
|
}
|
|
205
215
|
|
|
216
|
+
/* SplitPane OWNS its query container too — `.ui-split-pane-scope`
|
|
217
|
+
* (container-type: inline-size, named `split-pane`). The two-column layout is chosen from the
|
|
218
|
+
* pane's OWN available width, so a narrow embedded pane on a large viewport correctly stays a
|
|
219
|
+
* single column, and a wide pane on a small viewport can split (gh#165 — replaces the old
|
|
220
|
+
* `@media (min-width: 1080px)` viewport query that ignored the container). */
|
|
221
|
+
.ui-split-pane-scope {
|
|
222
|
+
container: split-pane / inline-size;
|
|
223
|
+
}
|
|
224
|
+
|
|
206
225
|
.ui-split-pane {
|
|
207
226
|
display: grid;
|
|
208
227
|
gap: var(--space-stack-md);
|
|
@@ -259,7 +278,7 @@
|
|
|
259
278
|
opacity: 0.5;
|
|
260
279
|
}
|
|
261
280
|
|
|
262
|
-
@
|
|
281
|
+
@container split-pane (min-width: 48rem) {
|
|
263
282
|
.ui-split-pane[data-aside-width="sm"] {
|
|
264
283
|
grid-template-columns: minmax(0, 1fr) 20rem;
|
|
265
284
|
}
|
|
@@ -543,22 +562,30 @@
|
|
|
543
562
|
}
|
|
544
563
|
|
|
545
564
|
@layer components {
|
|
565
|
+
/* Pagination is ONE horizontal row and never wraps to a second desktop line (gh#153 — the old
|
|
566
|
+
* `flex-wrap: wrap` split the range / size-changer / page strip across rows). The number strip
|
|
567
|
+
* (`.ui-pagination-list`) absorbs any overflow by scrolling horizontally rather than wrapping; a
|
|
568
|
+
* genuinely narrow viewport uses the INTENTIONAL compact form via the `simple` prop (Prev · n/N ·
|
|
569
|
+
* Next, no page buttons) — not browser-driven wrapping. */
|
|
546
570
|
.ui-pagination {
|
|
547
571
|
display: flex;
|
|
548
|
-
flex-wrap:
|
|
572
|
+
flex-wrap: nowrap;
|
|
573
|
+
min-width: 0;
|
|
549
574
|
align-items: center;
|
|
550
575
|
justify-content: flex-end;
|
|
551
576
|
gap: var(--pagination-gap);
|
|
552
577
|
}
|
|
553
578
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
}
|
|
557
|
-
|
|
579
|
+
/* Total / range label sits at the start and truncates rather than pushing the controls onto a
|
|
580
|
+
* second line — the nav row stays one line at every width (gh#153). */
|
|
558
581
|
.ui-pagination-total {
|
|
582
|
+
min-width: 0;
|
|
559
583
|
margin-inline-end: auto;
|
|
584
|
+
overflow: hidden;
|
|
560
585
|
color: hsl(var(--muted-foreground));
|
|
561
586
|
font-size: var(--pagination-total-font-size);
|
|
587
|
+
text-overflow: ellipsis;
|
|
588
|
+
white-space: nowrap;
|
|
562
589
|
}
|
|
563
590
|
|
|
564
591
|
.ui-pagination-count {
|
|
@@ -576,12 +603,22 @@
|
|
|
576
603
|
|
|
577
604
|
.ui-pagination-list {
|
|
578
605
|
display: flex;
|
|
606
|
+
min-width: 0;
|
|
579
607
|
align-items: center;
|
|
580
608
|
gap: var(--pagination-item-gap);
|
|
609
|
+
/* Overflow scrolls horizontally instead of wrapping onto a second row. Hidden scrollbar keeps
|
|
610
|
+
* the desktop bar clean; the strip stays swipeable on touch. */
|
|
611
|
+
overflow-x: auto;
|
|
612
|
+
scrollbar-width: none;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
.ui-pagination-list::-webkit-scrollbar {
|
|
616
|
+
display: none;
|
|
581
617
|
}
|
|
582
618
|
|
|
583
619
|
.ui-pagination-item {
|
|
584
620
|
display: flex;
|
|
621
|
+
flex: 0 0 auto;
|
|
585
622
|
align-items: center;
|
|
586
623
|
justify-content: center;
|
|
587
624
|
}
|
|
@@ -143,6 +143,21 @@
|
|
|
143
143
|
display: none;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
/* AppShell-owned mobile drawer trigger — hidden on desktop, shown once the docked sidebar
|
|
147
|
+
* collapses out below `lg` (gh#165). The drawer nav itself is a focus-trapped Sheet. */
|
|
148
|
+
.app-mobile-nav-trigger {
|
|
149
|
+
display: none;
|
|
150
|
+
flex: 0 0 auto;
|
|
151
|
+
margin-inline-end: var(--space-1);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* The drawer nav reuses the docked Sidebar node; its `.sb-root { display: contents }` flattens
|
|
155
|
+
* into the scrollable SheetBody so sections/footer flow naturally in the panel. */
|
|
156
|
+
.app-mobile-nav-body {
|
|
157
|
+
display: flex;
|
|
158
|
+
flex-direction: column;
|
|
159
|
+
}
|
|
160
|
+
|
|
146
161
|
.app-topbar-spacer {
|
|
147
162
|
flex: 1;
|
|
148
163
|
}
|
|
@@ -171,6 +186,10 @@
|
|
|
171
186
|
.app-topbar-left:has(.app-mobile-sidebar-trigger) {
|
|
172
187
|
display: flex;
|
|
173
188
|
}
|
|
189
|
+
|
|
190
|
+
.app-mobile-nav-trigger {
|
|
191
|
+
display: inline-flex;
|
|
192
|
+
}
|
|
174
193
|
}
|
|
175
194
|
|
|
176
195
|
.sb-root {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
font-size: var(--font-size-2xl);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
/* Weight — the
|
|
36
|
+
/* Weight — the reference-design canon (400 body · 500 heading/label · 700 emphasis). No 600.
|
|
37
37
|
* Shared by text + heading; the two-attribute heading selectors outrank the base heading
|
|
38
38
|
* rule below, so `<Heading weight="bold">` renders a semantic, emphasised title. */
|
|
39
39
|
[data-slot="text"][data-weight="regular"],
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
--card-header-border-bottom: initial;
|
|
44
44
|
--card-radius: var(--radius);
|
|
45
45
|
/* Resting elevation — quiet by default (rule #44): cards are flat (1px border, no shadow) in the
|
|
46
|
-
*
|
|
46
|
+
* reference-design baseline. A service that wants lifted cards sets this to an elevation token once,
|
|
47
47
|
* e.g. --card-shadow: var(--shadow-sm), and every Card picks up the shadow with no markup change. */
|
|
48
48
|
--card-shadow: 0 0 0 0 transparent;
|
|
49
49
|
/* Brand glow layer — invisible no-op at rest (rule #44). Paired AFTER --card-shadow in the
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* Foundation tokens: color, typography, raw spacing, ratio, radius, shadow. */
|
|
2
2
|
|
|
3
3
|
:root {
|
|
4
|
-
/* Color system (HSL components) —
|
|
4
|
+
/* Color system (HSL components) — the reference design: warm hue-60 neutral spine,
|
|
5
5
|
* SmartHR blue primary, 和色/wa-iro semantics. */
|
|
6
6
|
--background: 60 33% 99%; /* #fdfdfb warm off-white */
|
|
7
7
|
--foreground: 48 8% 13%; /* #23221e SmartHR TEXT_BLACK */
|
|
@@ -220,7 +220,7 @@
|
|
|
220
220
|
|
|
221
221
|
--font-weight-normal: 400;
|
|
222
222
|
--font-weight-medium: 500;
|
|
223
|
-
/*
|
|
223
|
+
/* the reference design uses three weights only: 400 body · 500 heading/label · 700 emphasis. 600 is
|
|
224
224
|
* forbidden (ambiguous between 500 and 700) — the legacy "semibold" token collapses to medium
|
|
225
225
|
* so any remaining reference renders at 500. `bold` is the 700 emphasis weight. */
|
|
226
226
|
--font-weight-semibold: 500;
|
|
@@ -325,7 +325,7 @@
|
|
|
325
325
|
|
|
326
326
|
/* Motion — the primitive tier for animation, so enter/transition timing reads a token
|
|
327
327
|
* instead of a literal 0.5s / cubic-bezier(...) / translateY(10px) (cardinal rule #2).
|
|
328
|
-
*
|
|
328
|
+
* the reference design keeps motion LOW: short durations, a calm decelerate curve, a small reveal shift.
|
|
329
329
|
* Consumers honour prefers-reduced-motion at the call site. */
|
|
330
330
|
--duration-fast: 150ms;
|
|
331
331
|
--duration-base: 250ms;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@godxjp/ui",
|
|
3
|
-
"version": "
|
|
4
|
-
"godxUiMcp": "
|
|
3
|
+
"version": "18.0.0",
|
|
4
|
+
"godxUiMcp": "17.0.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@10.29.1",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -276,9 +276,10 @@
|
|
|
276
276
|
"test:watch": "vitest",
|
|
277
277
|
"test:coverage": "vitest run --coverage",
|
|
278
278
|
"check:example-imports": "node scripts/check-example-imports.mjs",
|
|
279
|
-
"verify": "pnpm typecheck && pnpm lint && pnpm format && pnpm build && pnpm preview:build && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-token-sync && pnpm check:mcp-lockstep && pnpm check:mcp-sync && pnpm check:mcp-orphans && pnpm check:audit-sync && pnpm test",
|
|
280
|
-
"verify:release": "pnpm build && pnpm typecheck && pnpm typecheck:docs && pnpm lint && pnpm preview:build && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:use-client && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-token-sync && pnpm check:mcp-lockstep && pnpm check:mcp-sync && pnpm check:mcp-orphans && pnpm check:audit-sync && pnpm check:mcp-prop-sync && pnpm check:contrast && pnpm check:visual-audit && pnpm test",
|
|
279
|
+
"verify": "pnpm typecheck && pnpm lint && pnpm format && pnpm build && pnpm preview:build && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-token-sync && pnpm check:mcp-lockstep && pnpm check:mcp-sync && pnpm check:mcp-orphans && pnpm check:mcp-pattern-imports && pnpm check:audit-sync && pnpm check:frame-coverage && pnpm test",
|
|
280
|
+
"verify:release": "pnpm build && pnpm typecheck && pnpm typecheck:docs && pnpm lint && pnpm preview:build && pnpm check:example-imports && pnpm check:core-isolation && pnpm check:no-consumer-coupling && pnpm check:use-client && pnpm check:prop-vocabulary && pnpm check:token-tiers && pnpm check:control-sizing && pnpm check:rtl && pnpm check:typography && pnpm check:mcp-token-sync && pnpm check:mcp-lockstep && pnpm check:mcp-sync && pnpm check:mcp-orphans && pnpm check:mcp-pattern-imports && pnpm check:audit-sync && pnpm check:mcp-prop-sync && pnpm check:contrast && pnpm check:visual-audit && pnpm check:frame-coverage && pnpm check:frame-axe && pnpm test",
|
|
281
281
|
"check:mcp-lockstep": "node scripts/check-release-lockstep.mjs",
|
|
282
|
+
"check:mcp-pattern-imports": "node scripts/check-mcp-pattern-imports.mjs",
|
|
282
283
|
"check:mcp-sync": "node scripts/check-mcp-sync.mjs",
|
|
283
284
|
"check:mcp-orphans": "node scripts/check-mcp-orphans.mjs",
|
|
284
285
|
"check:mcp-prop-sync": "node scripts/check-mcp-prop-sync.mjs",
|
|
@@ -304,6 +305,9 @@
|
|
|
304
305
|
"check:contrast": "node scripts/check-contrast.mjs",
|
|
305
306
|
"visual-audit": "node scripts/visual-audit.mjs",
|
|
306
307
|
"check:visual-audit": "node scripts/visual-audit-smoke.mjs",
|
|
308
|
+
"check:frame-axe": "node scripts/check-frame-axe.mjs",
|
|
309
|
+
"check:frame-geometry": "node scripts/frame-geometry.mjs",
|
|
310
|
+
"check:frame-coverage": "node scripts/frame-coverage.mjs",
|
|
307
311
|
"postinstall": "node scripts/postinstall.mjs",
|
|
308
312
|
"init-agent": "node scripts/init-agent-kit.mjs",
|
|
309
313
|
"check:use-client": "node scripts/check-use-client.mjs"
|
package/scripts/ui-audit.mjs
CHANGED
|
@@ -281,9 +281,9 @@ const RULES = [
|
|
|
281
281
|
{
|
|
282
282
|
id: "no-em-dash-in-copy",
|
|
283
283
|
severity: "warn",
|
|
284
|
-
// Em-dash (U+2014) in JSX text —
|
|
284
|
+
// Em-dash (U+2014) in JSX text — the reference design uses the middot `·` for JP/EN pairs and calm copy.
|
|
285
285
|
test: /[A-Za-z0-9-ヿ一-鿿]\s*—\s*[A-Za-z0-9-ヿ一-鿿]/,
|
|
286
|
-
standard: "@godxjp/ui
|
|
286
|
+
standard: "@godxjp/ui reference-design typography (best-ux) · Unicode punctuation",
|
|
287
287
|
message:
|
|
288
288
|
"No em-dash (—) in product copy. Use a middot `·` for JP/EN label pairs, or restructure into two calm sentences. Keep copy factual and quiet.",
|
|
289
289
|
},
|
|
@@ -29,7 +29,7 @@ export const VISUAL_RULES = [
|
|
|
29
29
|
id: "oversaturated-accent",
|
|
30
30
|
severity: "warn",
|
|
31
31
|
category: "color",
|
|
32
|
-
standard: "@godxjp/ui
|
|
32
|
+
standard: "@godxjp/ui reference-design 渋み (OKLCH chroma ≤ 0.18)",
|
|
33
33
|
fix: "Desaturate brand/primary surfaces — keep OKLCH chroma ≤ 0.18. Read --primary tokens; never paint a full-width bar in raw vivid blue.",
|
|
34
34
|
},
|
|
35
35
|
{
|
|
@@ -75,7 +75,7 @@ export function oklchChroma({ r, g, b }) {
|
|
|
75
75
|
return Math.hypot(oa, ob);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
/**
|
|
78
|
+
/** reference-design restraint bound. A signal/brand surface above this chroma "screams". */
|
|
79
79
|
export const CHROMA_LIMIT = 0.18;
|
|
80
80
|
|
|
81
81
|
/** @returns {boolean} true when an accent surface exceeds the 渋み chroma limit. */
|