@godxjp/ui 23.4.1 → 23.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -2
- package/dist/components/data-display/badge.d.ts +14 -1
- package/dist/components/data-display/badge.js +21 -1
- package/dist/components/data-display/data-table.d.ts +4 -2
- package/dist/components/data-display/data-table.js +14 -2
- package/dist/components/data-entry/date-picker.js +4 -16
- package/dist/components/layout/draggable-panel.d.ts +3 -2
- package/dist/components/layout/draggable-panel.js +11 -2
- package/dist/components/layout/index.d.ts +1 -1
- package/dist/components/navigation/filter-bar.js +11 -16
- package/dist/components/ui/segmented.d.ts +19 -1
- package/dist/components/ui/segmented.js +34 -2
- package/dist/props/components/data-display.prop.d.ts +5 -0
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/components/layout.prop.d.ts +13 -2
- package/dist/props/registry.d.ts +5 -1
- package/dist/props/registry.js +6 -1
- package/dist/props/vocabulary/data.prop.d.ts +11 -0
- package/dist/styles/badge-layout.css +25 -0
- package/dist/styles/control.css +37 -0
- package/dist/styles/core-with-fallbacks.css +3 -0
- package/dist/styles/focus-ring.css +2 -0
- package/dist/styles/font-fallbacks.css +83 -0
- package/dist/styles/fonts.css +1 -82
- package/dist/styles/layout.css +17 -5
- package/dist/tokens/components/badge.css +6 -0
- package/dist/tokens/components/draggable-panel.css +4 -0
- package/dist/tokens/components/segmented.css +8 -0
- package/docs/CONSUMER-RULES.md +1 -1
- package/docs/CUSTOMER-THEMING.md +16 -2
- package/docs/DESIGN-AUTHORITY.md +15 -0
- package/docs/data-entry/segmented-in-filter-row.tsx +2 -19
- package/docs/data-entry/segmented.tsx +2 -19
- package/package.json +4 -2
- package/scripts/_agent-setup.mjs +90 -17
- package/scripts/cli.mjs +6 -2
- package/scripts/consumer-rule.md +76 -18
- package/scripts/guinea-pig-skill.md +38 -9
- package/scripts/postinstall.mjs +1 -1
- package/scripts/ui-audit.mjs +1 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
|
|
2
|
+
@font-face {
|
|
3
|
+
font-family: "Noto Sans JP Fallback";
|
|
4
|
+
font-weight: 400;
|
|
5
|
+
src: local("Arial"), local("ArialMT"), local("Liberation Sans"), local("LiberationSans");
|
|
6
|
+
size-adjust: 103.02%;
|
|
7
|
+
ascent-override: 112.6%;
|
|
8
|
+
descent-override: 27.96%;
|
|
9
|
+
line-gap-override: 0%;
|
|
10
|
+
unicode-range:
|
|
11
|
+
U+0000-02CC, U+02CE-02D7, U+02DA, U+02DC-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
|
|
12
|
+
U+1D00-1DBF, U+1E00-1EFF, U+2000-206F, U+20A0-20C0, U+2113, U+2122, U+2191, U+2193, U+2212,
|
|
13
|
+
U+2215, U+2C60-2C7F, U+A720-A7FF, U+FEFF, U+FFFD;
|
|
14
|
+
}
|
|
15
|
+
@font-face {
|
|
16
|
+
font-family: "Noto Sans JP Fallback";
|
|
17
|
+
font-weight: 500;
|
|
18
|
+
src: local("Arial"), local("ArialMT"), local("Liberation Sans"), local("LiberationSans");
|
|
19
|
+
size-adjust: 105.5%;
|
|
20
|
+
ascent-override: 109.95%;
|
|
21
|
+
descent-override: 27.3%;
|
|
22
|
+
line-gap-override: 0%;
|
|
23
|
+
unicode-range:
|
|
24
|
+
U+0000-02CC, U+02CE-02D7, U+02DA, U+02DC-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
|
|
25
|
+
U+1D00-1DBF, U+1E00-1EFF, U+2000-206F, U+20A0-20C0, U+2113, U+2122, U+2191, U+2193, U+2212,
|
|
26
|
+
U+2215, U+2C60-2C7F, U+A720-A7FF, U+FEFF, U+FFFD;
|
|
27
|
+
}
|
|
28
|
+
@font-face {
|
|
29
|
+
font-family: "Noto Sans JP Fallback";
|
|
30
|
+
font-weight: 700;
|
|
31
|
+
src:
|
|
32
|
+
local("Arial Bold"), local("Arial-BoldMT"), local("Liberation Sans Bold"),
|
|
33
|
+
local("LiberationSans-Bold");
|
|
34
|
+
size-adjust: 101.5%;
|
|
35
|
+
ascent-override: 114.29%;
|
|
36
|
+
descent-override: 28.37%;
|
|
37
|
+
line-gap-override: 0%;
|
|
38
|
+
unicode-range:
|
|
39
|
+
U+0000-02CC, U+02CE-02D7, U+02DA, U+02DC-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
|
|
40
|
+
U+1D00-1DBF, U+1E00-1EFF, U+2000-206F, U+20A0-20C0, U+2113, U+2122, U+2191, U+2193, U+2212,
|
|
41
|
+
U+2215, U+2C60-2C7F, U+A720-A7FF, U+FEFF, U+FFFD;
|
|
42
|
+
}
|
|
43
|
+
@font-face {
|
|
44
|
+
font-family: "Noto Sans JP Fallback";
|
|
45
|
+
font-weight: 400;
|
|
46
|
+
src:
|
|
47
|
+
local("Hiragino Sans W3"), local("HiraginoSans-W3"), local("Hiragino Kaku Gothic ProN W3"),
|
|
48
|
+
local("HiraKakuProN-W3"), local("Yu Gothic Medium"), local("YuGothic-Medium"), local("Meiryo"),
|
|
49
|
+
local("Noto Sans CJK JP"), local("NotoSansCJKjp-Regular"), local("IPAGothic");
|
|
50
|
+
ascent-override: 116%;
|
|
51
|
+
descent-override: 28.8%;
|
|
52
|
+
line-gap-override: 0%;
|
|
53
|
+
unicode-range:
|
|
54
|
+
U+3000-30FF, U+31F0-31FF, U+3400-4DBF, U+4E00-9FFF, U+F900-FAFF, U+FF00-FFEF, U+20000-2FA1F;
|
|
55
|
+
}
|
|
56
|
+
@font-face {
|
|
57
|
+
font-family: "Noto Sans JP Fallback";
|
|
58
|
+
font-weight: 500;
|
|
59
|
+
src:
|
|
60
|
+
local("Hiragino Sans W5"), local("HiraginoSans-W5"), local("Hiragino Kaku Gothic ProN W3"),
|
|
61
|
+
local("HiraKakuProN-W3"), local("Yu Gothic Medium"), local("YuGothic-Medium"), local("Meiryo"),
|
|
62
|
+
local("Noto Sans CJK JP Medium"), local("NotoSansCJKjp-Medium"), local("Noto Sans CJK JP"),
|
|
63
|
+
local("NotoSansCJKjp-Regular"), local("IPAGothic");
|
|
64
|
+
ascent-override: 116%;
|
|
65
|
+
descent-override: 28.8%;
|
|
66
|
+
line-gap-override: 0%;
|
|
67
|
+
unicode-range:
|
|
68
|
+
U+3000-30FF, U+31F0-31FF, U+3400-4DBF, U+4E00-9FFF, U+F900-FAFF, U+FF00-FFEF, U+20000-2FA1F;
|
|
69
|
+
}
|
|
70
|
+
@font-face {
|
|
71
|
+
font-family: "Noto Sans JP Fallback";
|
|
72
|
+
font-weight: 700;
|
|
73
|
+
src:
|
|
74
|
+
local("Hiragino Sans W6"), local("HiraginoSans-W6"), local("Hiragino Kaku Gothic ProN W6"),
|
|
75
|
+
local("HiraKakuProN-W6"), local("Yu Gothic Bold"), local("YuGothic-Bold"), local("Meiryo Bold"),
|
|
76
|
+
local("Meiryo-Bold"), local("Noto Sans CJK JP Bold"), local("NotoSansCJKjp-Bold"),
|
|
77
|
+
local("IPAGothic");
|
|
78
|
+
ascent-override: 116%;
|
|
79
|
+
descent-override: 28.8%;
|
|
80
|
+
line-gap-override: 0%;
|
|
81
|
+
unicode-range:
|
|
82
|
+
U+3000-30FF, U+31F0-31FF, U+3400-4DBF, U+4E00-9FFF, U+F900-FAFF, U+FF00-FFEF, U+20000-2FA1F;
|
|
83
|
+
}
|
package/dist/styles/fonts.css
CHANGED
|
@@ -6,88 +6,7 @@
|
|
|
6
6
|
@import "@fontsource/noto-sans-jp/500.css";
|
|
7
7
|
@import "@fontsource/noto-sans-jp/700.css";
|
|
8
8
|
|
|
9
|
-
@font-
|
|
10
|
-
font-family: "Noto Sans JP Fallback";
|
|
11
|
-
font-weight: 400;
|
|
12
|
-
src: local("Arial"), local("ArialMT"), local("Liberation Sans"), local("LiberationSans");
|
|
13
|
-
size-adjust: 103.02%;
|
|
14
|
-
ascent-override: 112.6%;
|
|
15
|
-
descent-override: 27.96%;
|
|
16
|
-
line-gap-override: 0%;
|
|
17
|
-
unicode-range:
|
|
18
|
-
U+0000-02CC, U+02CE-02D7, U+02DA, U+02DC-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
|
|
19
|
-
U+1D00-1DBF, U+1E00-1EFF, U+2000-206F, U+20A0-20C0, U+2113, U+2122, U+2191, U+2193, U+2212,
|
|
20
|
-
U+2215, U+2C60-2C7F, U+A720-A7FF, U+FEFF, U+FFFD;
|
|
21
|
-
}
|
|
22
|
-
@font-face {
|
|
23
|
-
font-family: "Noto Sans JP Fallback";
|
|
24
|
-
font-weight: 500;
|
|
25
|
-
src: local("Arial"), local("ArialMT"), local("Liberation Sans"), local("LiberationSans");
|
|
26
|
-
size-adjust: 105.5%;
|
|
27
|
-
ascent-override: 109.95%;
|
|
28
|
-
descent-override: 27.3%;
|
|
29
|
-
line-gap-override: 0%;
|
|
30
|
-
unicode-range:
|
|
31
|
-
U+0000-02CC, U+02CE-02D7, U+02DA, U+02DC-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
|
|
32
|
-
U+1D00-1DBF, U+1E00-1EFF, U+2000-206F, U+20A0-20C0, U+2113, U+2122, U+2191, U+2193, U+2212,
|
|
33
|
-
U+2215, U+2C60-2C7F, U+A720-A7FF, U+FEFF, U+FFFD;
|
|
34
|
-
}
|
|
35
|
-
@font-face {
|
|
36
|
-
font-family: "Noto Sans JP Fallback";
|
|
37
|
-
font-weight: 700;
|
|
38
|
-
src:
|
|
39
|
-
local("Arial Bold"), local("Arial-BoldMT"), local("Liberation Sans Bold"),
|
|
40
|
-
local("LiberationSans-Bold");
|
|
41
|
-
size-adjust: 101.5%;
|
|
42
|
-
ascent-override: 114.29%;
|
|
43
|
-
descent-override: 28.37%;
|
|
44
|
-
line-gap-override: 0%;
|
|
45
|
-
unicode-range:
|
|
46
|
-
U+0000-02CC, U+02CE-02D7, U+02DA, U+02DC-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
|
|
47
|
-
U+1D00-1DBF, U+1E00-1EFF, U+2000-206F, U+20A0-20C0, U+2113, U+2122, U+2191, U+2193, U+2212,
|
|
48
|
-
U+2215, U+2C60-2C7F, U+A720-A7FF, U+FEFF, U+FFFD;
|
|
49
|
-
}
|
|
50
|
-
@font-face {
|
|
51
|
-
font-family: "Noto Sans JP Fallback";
|
|
52
|
-
font-weight: 400;
|
|
53
|
-
src:
|
|
54
|
-
local("Hiragino Sans W3"), local("HiraginoSans-W3"), local("Hiragino Kaku Gothic ProN W3"),
|
|
55
|
-
local("HiraKakuProN-W3"), local("Yu Gothic Medium"), local("YuGothic-Medium"), local("Meiryo"),
|
|
56
|
-
local("Noto Sans CJK JP"), local("NotoSansCJKjp-Regular"), local("IPAGothic");
|
|
57
|
-
ascent-override: 116%;
|
|
58
|
-
descent-override: 28.8%;
|
|
59
|
-
line-gap-override: 0%;
|
|
60
|
-
unicode-range:
|
|
61
|
-
U+3000-30FF, U+31F0-31FF, U+3400-4DBF, U+4E00-9FFF, U+F900-FAFF, U+FF00-FFEF, U+20000-2FA1F;
|
|
62
|
-
}
|
|
63
|
-
@font-face {
|
|
64
|
-
font-family: "Noto Sans JP Fallback";
|
|
65
|
-
font-weight: 500;
|
|
66
|
-
src:
|
|
67
|
-
local("Hiragino Sans W5"), local("HiraginoSans-W5"), local("Hiragino Kaku Gothic ProN W3"),
|
|
68
|
-
local("HiraKakuProN-W3"), local("Yu Gothic Medium"), local("YuGothic-Medium"), local("Meiryo"),
|
|
69
|
-
local("Noto Sans CJK JP Medium"), local("NotoSansCJKjp-Medium"), local("Noto Sans CJK JP"),
|
|
70
|
-
local("NotoSansCJKjp-Regular"), local("IPAGothic");
|
|
71
|
-
ascent-override: 116%;
|
|
72
|
-
descent-override: 28.8%;
|
|
73
|
-
line-gap-override: 0%;
|
|
74
|
-
unicode-range:
|
|
75
|
-
U+3000-30FF, U+31F0-31FF, U+3400-4DBF, U+4E00-9FFF, U+F900-FAFF, U+FF00-FFEF, U+20000-2FA1F;
|
|
76
|
-
}
|
|
77
|
-
@font-face {
|
|
78
|
-
font-family: "Noto Sans JP Fallback";
|
|
79
|
-
font-weight: 700;
|
|
80
|
-
src:
|
|
81
|
-
local("Hiragino Sans W6"), local("HiraginoSans-W6"), local("Hiragino Kaku Gothic ProN W6"),
|
|
82
|
-
local("HiraKakuProN-W6"), local("Yu Gothic Bold"), local("YuGothic-Bold"), local("Meiryo Bold"),
|
|
83
|
-
local("Meiryo-Bold"), local("Noto Sans CJK JP Bold"), local("NotoSansCJKjp-Bold"),
|
|
84
|
-
local("IPAGothic");
|
|
85
|
-
ascent-override: 116%;
|
|
86
|
-
descent-override: 28.8%;
|
|
87
|
-
line-gap-override: 0%;
|
|
88
|
-
unicode-range:
|
|
89
|
-
U+3000-30FF, U+31F0-31FF, U+3400-4DBF, U+4E00-9FFF, U+F900-FAFF, U+FF00-FFEF, U+20000-2FA1F;
|
|
90
|
-
}
|
|
9
|
+
@import "./font-fallbacks.css";
|
|
91
10
|
|
|
92
11
|
:root {
|
|
93
12
|
--font-sans-base:
|
package/dist/styles/layout.css
CHANGED
|
@@ -1238,7 +1238,6 @@
|
|
|
1238
1238
|
.ui-filter-bar-chip {
|
|
1239
1239
|
display: inline-flex;
|
|
1240
1240
|
align-items: center;
|
|
1241
|
-
gap: var(--space-1);
|
|
1242
1241
|
min-width: 0;
|
|
1243
1242
|
}
|
|
1244
1243
|
|
|
@@ -1417,28 +1416,36 @@
|
|
|
1417
1416
|
|
|
1418
1417
|
@media (width < 40rem) {
|
|
1419
1418
|
.ui-flex[data-hide-below="sm"],
|
|
1420
|
-
.ui-topbar-item[data-hide-below="sm"]
|
|
1419
|
+
.ui-topbar-item[data-hide-below="sm"],
|
|
1420
|
+
[data-slot="table-head"][data-hide-below="sm"],
|
|
1421
|
+
[data-slot="table-cell"][data-hide-below="sm"] {
|
|
1421
1422
|
display: none;
|
|
1422
1423
|
}
|
|
1423
1424
|
}
|
|
1424
1425
|
|
|
1425
1426
|
@media (width < 48rem) {
|
|
1426
1427
|
.ui-flex[data-hide-below="md"],
|
|
1427
|
-
.ui-topbar-item[data-hide-below="md"]
|
|
1428
|
+
.ui-topbar-item[data-hide-below="md"],
|
|
1429
|
+
[data-slot="table-head"][data-hide-below="md"],
|
|
1430
|
+
[data-slot="table-cell"][data-hide-below="md"] {
|
|
1428
1431
|
display: none;
|
|
1429
1432
|
}
|
|
1430
1433
|
}
|
|
1431
1434
|
|
|
1432
1435
|
@media (width < 64rem) {
|
|
1433
1436
|
.ui-flex[data-hide-below="lg"],
|
|
1434
|
-
.ui-topbar-item[data-hide-below="lg"]
|
|
1437
|
+
.ui-topbar-item[data-hide-below="lg"],
|
|
1438
|
+
[data-slot="table-head"][data-hide-below="lg"],
|
|
1439
|
+
[data-slot="table-cell"][data-hide-below="lg"] {
|
|
1435
1440
|
display: none;
|
|
1436
1441
|
}
|
|
1437
1442
|
}
|
|
1438
1443
|
|
|
1439
1444
|
@media (width < 80rem) {
|
|
1440
1445
|
.ui-flex[data-hide-below="xl"],
|
|
1441
|
-
.ui-topbar-item[data-hide-below="xl"]
|
|
1446
|
+
.ui-topbar-item[data-hide-below="xl"],
|
|
1447
|
+
[data-slot="table-head"][data-hide-below="xl"],
|
|
1448
|
+
[data-slot="table-cell"][data-hide-below="xl"] {
|
|
1442
1449
|
display: none;
|
|
1443
1450
|
}
|
|
1444
1451
|
}
|
|
@@ -1792,6 +1799,7 @@
|
|
|
1792
1799
|
flex-direction: column;
|
|
1793
1800
|
inline-size: var(--draggable-panel-width);
|
|
1794
1801
|
max-inline-size: calc(100% - var(--draggable-panel-inset) - var(--draggable-panel-inset));
|
|
1802
|
+
block-size: var(--draggable-panel-block-size);
|
|
1795
1803
|
max-block-size: var(--draggable-panel-max-block-size);
|
|
1796
1804
|
border: var(--draggable-panel-border-width) solid hsl(var(--border));
|
|
1797
1805
|
border-radius: var(--draggable-panel-radius);
|
|
@@ -1811,6 +1819,10 @@
|
|
|
1811
1819
|
inline-size: var(--draggable-panel-width-lg);
|
|
1812
1820
|
}
|
|
1813
1821
|
|
|
1822
|
+
.ui-draggable-panel[data-width="xl"] {
|
|
1823
|
+
inline-size: var(--draggable-panel-width-xl);
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1814
1826
|
.ui-draggable-panel[data-placement^="top"] {
|
|
1815
1827
|
inset-block-start: var(--draggable-panel-inset);
|
|
1816
1828
|
}
|
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
|
|
13
13
|
--badge-icon-size: var(--icon-size-xs);
|
|
14
14
|
|
|
15
|
+
--badge-space-x-removable: var(--space-1);
|
|
16
|
+
--badge-remove-offset-inline-end: calc(-1 * var(--space-1));
|
|
17
|
+
--badge-remove-icon-size: var(--badge-icon-size);
|
|
18
|
+
--badge-remove-rest-alpha: 0.7;
|
|
19
|
+
--badge-remove-hover-alpha: 1;
|
|
20
|
+
|
|
15
21
|
--badge-tint-fill: 18%;
|
|
16
22
|
--badge-tint-edge: 45%;
|
|
17
23
|
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
--draggable-panel-width-sm: 18rem;
|
|
13
13
|
--draggable-panel-width: 22rem;
|
|
14
14
|
--draggable-panel-width-lg: 28rem;
|
|
15
|
+
|
|
16
|
+
--draggable-panel-width-xl: var(--centered-shell-width-md);
|
|
17
|
+
|
|
18
|
+
--draggable-panel-block-size: auto;
|
|
15
19
|
--draggable-panel-max-block-size: 70vh;
|
|
16
20
|
|
|
17
21
|
--draggable-panel-radius: var(--radius-lg);
|
|
@@ -22,4 +22,12 @@
|
|
|
22
22
|
|
|
23
23
|
--segmented-item-selected-background: var(--background);
|
|
24
24
|
--segmented-item-selected-shadow: var(--shadow-sm);
|
|
25
|
+
|
|
26
|
+
--segmented-count-min-width: var(--button-count-min-width);
|
|
27
|
+
--segmented-count-space-inline: var(--button-count-space-inline);
|
|
28
|
+
--segmented-count-font-size: var(--button-count-font-size);
|
|
29
|
+
--segmented-count-radius: var(--radius-pill);
|
|
30
|
+
--segmented-count-gap: var(--space-1);
|
|
31
|
+
--segmented-count-background: initial;
|
|
32
|
+
--segmented-count-color: initial;
|
|
25
33
|
}
|
package/docs/CONSUMER-RULES.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Read this once; the audit enforces it. Everything else in `docs/` is for contributors.
|
|
4
4
|
|
|
5
|
-
1. Load styles with `@import "@godxjp/ui/styles"` (fonts bundled) or `@import "@godxjp/ui/styles/core"` (
|
|
5
|
+
1. Load styles with `@import "@godxjp/ui/styles"` (fonts bundled), `@import "@godxjp/ui/styles/core"` (no `@font-face` at all) or `@import "@godxjp/ui/styles/core-with-fallbacks"` (`core` + six `local()`-only metric-matched faces, still zero network bytes — for when you supply Noto Sans JP yourself). Never cherry-pick `*-layout.css`.
|
|
6
6
|
2. Every page is `<PageContainer title subtitle extra footer>`; its sections are spaced by the page. Group items inside a section with `<Flex direction="col" gap>` or `<ResponsiveGrid>`.
|
|
7
7
|
3. No Tailwind layout on your own elements: no `flex`, `grid`, `gap-*`, `p-*`, `m-*`, `space-*`. Rows are `<Flex>` (default row), stacks are `<Flex direction="col">`, grids are `<ResponsiveGrid>`.
|
|
8
8
|
4. No hand-rolled surfaces: no `rounded-* border bg-*` divs. A box is `Card`, a pill is `Badge`, a person is `Avatar`, a row is `ListRow`, a label/value pair is `Descriptions`, an empty area is `EmptyState`, a read-only sample of a colour the USER chose is `Swatch`.
|
package/docs/CUSTOMER-THEMING.md
CHANGED
|
@@ -16,14 +16,28 @@ That single import ships everything: colors, the bundled fonts (**Noto Sans JP**
|
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
-
## CSS entries — `styles`
|
|
19
|
+
## CSS entries — `styles`, `styles/core`, `styles/core-with-fallbacks`, nothing smaller
|
|
20
20
|
|
|
21
|
-
`@godxjp/ui/styles` bundles every component's CSS plus the fonts. When you manage fonts yourself (next/font, a system stack, a browser extension that must not ship font files), load the same layers without the faces:
|
|
21
|
+
`@godxjp/ui/styles` bundles every component's CSS plus the fonts: **729 woff2 subsets, ~11.7 MB on disk** at the current @fontsource versions (issue #535 measured 737 files / 13 MB in a real consumer build). When you manage fonts yourself (next/font, a system stack, a browser extension that must not ship font files), load the same layers without the faces:
|
|
22
22
|
|
|
23
23
|
```css
|
|
24
24
|
@import "@godxjp/ui/styles/core"; /* every component layer, no @font-face */
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
`core` carries **zero** `@font-face` — `grep -c '@font-face' node_modules/@godxjp/ui/dist/styles/core.css` → `0` — and that number is the point of it. If you supply Noto Sans JP yourself and also want the cold-visit swap to stop reflowing the page (issue #475), take the third entry: `core` plus the six metric-matched fallback faces, every one `local()`-only, so the extra cost over `core` is **zero network bytes**.
|
|
28
|
+
|
|
29
|
+
```css
|
|
30
|
+
@import "@godxjp/ui/styles/core-with-fallbacks"; /* core + 6 local()-only faces */
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
It declares the faces and nothing else — name the family yourself, directly after your own face:
|
|
34
|
+
|
|
35
|
+
```css
|
|
36
|
+
:root {
|
|
37
|
+
--font-sans-base: "Noto Sans JP", "Noto Sans JP Fallback", system-ui, sans-serif;
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
27
41
|
The per-layer files (`control`, `card-layout`, `navigation-layout`, …) are the package's internal structure, **not a public menu**. Layers share rules — a Select's rows and a menu's surface, a form's rhythm, a card's header type — so a page that loads a subset renders naked menus and unsized rows with no error. The runtime `visual-audit` reports it as `css-layers-missing`.
|
|
28
42
|
|
|
29
43
|
---
|
package/docs/DESIGN-AUTHORITY.md
CHANGED
|
@@ -242,6 +242,21 @@ in page CSS.
|
|
|
242
242
|
- **A capability this library already has keeps its own name.** antd's `size`
|
|
243
243
|
(`small | middle | large`) IS `density` (`compact | default | comfortable`); antd's `locale` IS
|
|
244
244
|
the `t()` layer. Adding the antd spelling as an alias would be duplication, not parity.
|
|
245
|
+
- **`SegmentedOption.count`.** antd's `SegmentedItemType` has no count field — consumers embed counts
|
|
246
|
+
in `label`. Nested `Badge` `secondary` is `--muted`, which matches the Segmented track
|
|
247
|
+
(gh#602, measured 1.00:1). This library adds `count` / `overflowCount` / `showZero` /
|
|
248
|
+
`countLabel` on `SegmentedOption` with a DS-owned pill (`--segmented-count-*` tokens), same
|
|
249
|
+
counter vocabulary as `Button` / `Toggle`.
|
|
250
|
+
- **`DraggablePanel.labels`.** antd's Draggable Modal demo has no i18n hook for the drag handle.
|
|
251
|
+
Embedded consumers cannot mount `AppProvider` without writing theme attributes on the host
|
|
252
|
+
`document.documentElement` (gh#606). `labels?: { close?: string; move?: string }` overrides `t()`
|
|
253
|
+
per control; a scoped `AppProvider` subtree remains the long-term fix.
|
|
254
|
+
- **`Badge.onRemove` instead of antd `Tag`'s `closable` + `onClose`.** Semantics match antd's
|
|
255
|
+
closable tag (a chip that draws its own × and fires one callback). The boolean is folded into
|
|
256
|
+
prop presence (`onRemove` omitted ⇒ no ×), and the callback is named `onRemove` rather than
|
|
257
|
+
`onClose`, because `onClose` already means overlay dismiss across Dialog/Drawer and would read
|
|
258
|
+
as closing a surface, not removing one applied filter chip. Implemented on `Badge` — the DS chip
|
|
259
|
+
primitive — rather than adding a separate `Tag` export beside `Badge`.
|
|
245
260
|
|
|
246
261
|
**A knob that only a fork could reach is not parity either.** antd's `components`,
|
|
247
262
|
`filterDropdown`, `classNames`/`styles` semantic maps and `prefixCls` all exist to let a consumer
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Badge,
|
|
3
|
-
Card,
|
|
4
|
-
CardContent,
|
|
5
|
-
CardDescription,
|
|
6
|
-
CardHeader,
|
|
7
|
-
CardTitle,
|
|
8
|
-
} from "@godxjp/ui/data-display";
|
|
1
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@godxjp/ui/data-display";
|
|
9
2
|
import { Segmented } from "@godxjp/ui/data-entry";
|
|
10
3
|
import { Button, Text } from "@godxjp/ui/general";
|
|
11
4
|
import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
@@ -24,17 +17,7 @@ const OPTIONS = [
|
|
|
24
17
|
{ value: "ginou", label: "技能実習", count: 82 },
|
|
25
18
|
{ value: "tokutei", label: "特定技能", count: 51 },
|
|
26
19
|
{ value: "ikusei", label: "育成就労", count: 0 },
|
|
27
|
-
]
|
|
28
|
-
value,
|
|
29
|
-
label: (
|
|
30
|
-
<>
|
|
31
|
-
{label}
|
|
32
|
-
<Badge as="span" variant="secondary">
|
|
33
|
-
{count}
|
|
34
|
-
</Badge>
|
|
35
|
-
</>
|
|
36
|
-
),
|
|
37
|
-
}));
|
|
20
|
+
];
|
|
38
21
|
|
|
39
22
|
export default function SegmentedInFilterRow() {
|
|
40
23
|
return (
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
Badge,
|
|
5
|
-
Card,
|
|
6
|
-
CardContent,
|
|
7
|
-
CardDescription,
|
|
8
|
-
CardHeader,
|
|
9
|
-
CardTitle,
|
|
10
|
-
} from "@godxjp/ui/data-display";
|
|
3
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@godxjp/ui/data-display";
|
|
11
4
|
import { FormField, Segmented } from "@godxjp/ui/data-entry";
|
|
12
5
|
import { Text, VisuallyHidden } from "@godxjp/ui/general";
|
|
13
6
|
import { Flex, PageContainer } from "@godxjp/ui/layout";
|
|
@@ -108,17 +101,7 @@ export default function Demo() {
|
|
|
108
101
|
{ value: "active", label: "実習中", count: 96 },
|
|
109
102
|
{ value: "pending", label: "申請中", count: 12 },
|
|
110
103
|
{ value: "gone", label: "失踪・帰国", count: 0 },
|
|
111
|
-
]
|
|
112
|
-
value,
|
|
113
|
-
label: (
|
|
114
|
-
<>
|
|
115
|
-
{label}
|
|
116
|
-
<Badge as="span" variant="secondary">
|
|
117
|
-
{count}
|
|
118
|
-
</Badge>
|
|
119
|
-
</>
|
|
120
|
-
),
|
|
121
|
-
}))}
|
|
104
|
+
]}
|
|
122
105
|
/>
|
|
123
106
|
</CardContent>
|
|
124
107
|
</Card>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@godxjp/ui",
|
|
3
|
-
"version": "23.4.
|
|
4
|
-
"godxUiMcp": "23.4.
|
|
3
|
+
"version": "23.4.3",
|
|
4
|
+
"godxUiMcp": "23.4.3",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -46,6 +46,8 @@
|
|
|
46
46
|
"import": "./dist/index.js"
|
|
47
47
|
},
|
|
48
48
|
"./styles": "./dist/styles/index.css",
|
|
49
|
+
"./styles/core": "./dist/styles/core.css",
|
|
50
|
+
"./styles/core-with-fallbacks": "./dist/styles/core-with-fallbacks.css",
|
|
49
51
|
"./styles/*": "./dist/styles/*.css",
|
|
50
52
|
"./theme/*": "./dist/theme/*",
|
|
51
53
|
"./tokens": "./dist/tokens/base.css",
|
package/scripts/_agent-setup.mjs
CHANGED
|
@@ -24,13 +24,37 @@ import {
|
|
|
24
24
|
import { basename, dirname, join } from "node:path";
|
|
25
25
|
import { fileURLToPath } from "node:url";
|
|
26
26
|
|
|
27
|
-
/** The godxjp-ui MCP server — pulled on demand via npx (no extra dependency to ship). */
|
|
28
27
|
/** This package's own root — the source of the version we stamp with. */
|
|
29
28
|
const SELF_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
30
29
|
|
|
31
|
-
export const MCP_SERVER = { command: "npx", args: ["@godxjp/ui-mcp"] };
|
|
32
30
|
export const MCP_KEY = "godx-ui";
|
|
33
31
|
|
|
32
|
+
/** @deprecated Prefer `mcpServerFor(root)` — bare npx resolves latest on the registry (gh#543). */
|
|
33
|
+
export const MCP_SERVER = { command: "npx", args: ["@godxjp/ui-mcp"] };
|
|
34
|
+
|
|
35
|
+
function mcpEntryMatches(a, b) {
|
|
36
|
+
if (a?.command !== b?.command) return false;
|
|
37
|
+
const argsA = a?.args ?? [];
|
|
38
|
+
const argsB = b?.args ?? [];
|
|
39
|
+
if (argsA.length !== argsB.length || argsA.some((x, i) => x !== argsB[i])) return false;
|
|
40
|
+
const envA = a?.env ?? {};
|
|
41
|
+
const envB = b?.env ?? {};
|
|
42
|
+
const keysA = Object.keys(envA).sort();
|
|
43
|
+
const keysB = Object.keys(envB).sort();
|
|
44
|
+
if (keysA.length !== keysB.length || keysA.some((k, i) => k !== keysB[i])) return false;
|
|
45
|
+
return keysA.every((k) => envA[k] === envB[k]);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function mcpConfigMismatchMessage(root, existing, expected) {
|
|
49
|
+
const ui = readConsumerUiMetadata(root);
|
|
50
|
+
const pin = expected.args?.[0] ?? "@godxjp/ui-mcp";
|
|
51
|
+
const ver = ui?.version ?? "(unknown)";
|
|
52
|
+
return (
|
|
53
|
+
`present (custom godx-ui MCP entry — not overwritten; expected ${pin} with ` +
|
|
54
|
+
`env.GODX_UI_VERSION=${ver} from node_modules/@godxjp/ui)`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
34
58
|
/** Commands wired into the consumer's .claude/settings.json. */
|
|
35
59
|
export const AUDIT_HOOK_CMD = "node node_modules/@godxjp/ui/scripts/audit-hook.mjs";
|
|
36
60
|
export const PRIMER_CMD = "cat .claude/godxjp-ui-workflow.md";
|
|
@@ -46,6 +70,31 @@ const SUGGESTED_HOOKS = {
|
|
|
46
70
|
/** The per-session workflow mandate the SessionStart hook injects into the agent. */
|
|
47
71
|
export const KIT_VERSION = readJson(join(SELF_ROOT, "package.json"))?.version ?? "0.0.0";
|
|
48
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Read the consumer's installed @godxjp/ui metadata (postinstall runs with UI already in
|
|
75
|
+
* node_modules). Returns null when absent — caller falls back to this package's release train.
|
|
76
|
+
*/
|
|
77
|
+
export function readConsumerUiMetadata(root) {
|
|
78
|
+
const uiPkg = readJson(join(root, "node_modules/@godxjp/ui/package.json"));
|
|
79
|
+
if (!uiPkg?.version) return null;
|
|
80
|
+
return {
|
|
81
|
+
version: String(uiPkg.version),
|
|
82
|
+
godxUiMcp: uiPkg.godxUiMcp ? String(uiPkg.godxUiMcp) : String(uiPkg.version),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* MCP launch config for a consumer root: pin @godxjp/ui-mcp to godxUiMcp from the installed UI
|
|
88
|
+
* package and pass the real UI version via env (gh#543 — MCP must not read the repo to learn it).
|
|
89
|
+
*/
|
|
90
|
+
export function mcpServerFor(root) {
|
|
91
|
+
const meta = readConsumerUiMetadata(root);
|
|
92
|
+
const mcpPin = meta?.godxUiMcp ?? KIT_VERSION;
|
|
93
|
+
const server = { command: "npx", args: [`@godxjp/ui-mcp@${mcpPin}`] };
|
|
94
|
+
if (meta?.version) server.env = { GODX_UI_VERSION: meta.version };
|
|
95
|
+
return server;
|
|
96
|
+
}
|
|
97
|
+
|
|
49
98
|
const STAMP = (v) => `<!-- godxjp-ui:version ${v} -->`;
|
|
50
99
|
const STAMP_RE = /<!-- godxjp-ui:version ([^\s]+) -->/;
|
|
51
100
|
|
|
@@ -76,6 +125,18 @@ export function stampedDigest(text) {
|
|
|
76
125
|
return text?.match(DIGEST_RE)?.[1] ?? null;
|
|
77
126
|
}
|
|
78
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Refresh only the human-readable release marker when the managed body is already current.
|
|
130
|
+
*
|
|
131
|
+
* The writer keys off digest; `ui-audit` keys off version (godx-jp/id#513). When rule text is
|
|
132
|
+
* unchanged across releases both must still agree after sync — without rewriting the body or
|
|
133
|
+
* breaking idempotency when digest and version already match.
|
|
134
|
+
*/
|
|
135
|
+
function resyncVersionStamp(text) {
|
|
136
|
+
if (stampedVersion(text) === KIT_VERSION) return null;
|
|
137
|
+
return text.replace(STAMP_RE, `<!-- godxjp-ui:version ${KIT_VERSION} -->`);
|
|
138
|
+
}
|
|
139
|
+
|
|
79
140
|
/**
|
|
80
141
|
* Replace the MANAGED region of a file and leave everything else alone.
|
|
81
142
|
*
|
|
@@ -291,13 +352,11 @@ export function refreshBlock(current, next, startMarker, endMarker) {
|
|
|
291
352
|
|
|
292
353
|
export function ensureMcpJson(root) {
|
|
293
354
|
const path = join(root, ".mcp.json");
|
|
355
|
+
const expected = mcpServerFor(root);
|
|
356
|
+
const suggested = JSON.stringify({ mcpServers: { [MCP_KEY]: expected } }, null, 2) + "\n";
|
|
294
357
|
const read = readJsonFile(path);
|
|
295
358
|
if (read.state !== "ok" && read.state !== "missing") {
|
|
296
|
-
return refuseAndSuggest(
|
|
297
|
-
path,
|
|
298
|
-
JSON.stringify({ mcpServers: { [MCP_KEY]: MCP_SERVER } }, null, 2) + "\n",
|
|
299
|
-
READ_FAILURE[read.state],
|
|
300
|
-
);
|
|
359
|
+
return refuseAndSuggest(path, suggested, READ_FAILURE[read.state]);
|
|
301
360
|
}
|
|
302
361
|
const json = read.state === "ok" ? read.json : {};
|
|
303
362
|
if (
|
|
@@ -306,16 +365,17 @@ export function ensureMcpJson(root) {
|
|
|
306
365
|
typeof json.mcpServers !== "object" ||
|
|
307
366
|
Array.isArray(json.mcpServers))
|
|
308
367
|
) {
|
|
309
|
-
return refuseAndSuggest(
|
|
310
|
-
path,
|
|
311
|
-
JSON.stringify({ mcpServers: { [MCP_KEY]: MCP_SERVER } }, null, 2) + "\n",
|
|
312
|
-
"`mcpServers` is not an object",
|
|
313
|
-
);
|
|
368
|
+
return refuseAndSuggest(path, suggested, "`mcpServers` is not an object");
|
|
314
369
|
}
|
|
315
370
|
json.mcpServers = json.mcpServers ?? {};
|
|
316
|
-
if (json.mcpServers[MCP_KEY])
|
|
371
|
+
if (json.mcpServers[MCP_KEY]) {
|
|
372
|
+
if (!mcpEntryMatches(json.mcpServers[MCP_KEY], expected)) {
|
|
373
|
+
return mcpConfigMismatchMessage(root, json.mcpServers[MCP_KEY], expected);
|
|
374
|
+
}
|
|
375
|
+
return "present";
|
|
376
|
+
}
|
|
317
377
|
const created = read.state === "missing";
|
|
318
|
-
json.mcpServers[MCP_KEY] =
|
|
378
|
+
json.mcpServers[MCP_KEY] = expected;
|
|
319
379
|
writeFileAtomic(path, JSON.stringify(json, null, 2) + "\n");
|
|
320
380
|
return created ? "created" : "added";
|
|
321
381
|
}
|
|
@@ -485,7 +545,14 @@ export function refreshGuineaPigSkill(root) {
|
|
|
485
545
|
* to anyone reading it. Only the digest half is compared.
|
|
486
546
|
*/
|
|
487
547
|
const stamp = `${KIT_VERSION}:${digestOf(base)}`;
|
|
488
|
-
|
|
548
|
+
const optinLine = readFileSync(optin, "utf8").trim();
|
|
549
|
+
const optinDigest = optinLine.split(":").pop();
|
|
550
|
+
if (optinDigest === digestOf(base)) {
|
|
551
|
+
if (optinLine.split(":")[0] === KIT_VERSION) return false;
|
|
552
|
+
// Base unchanged — only the release half of `<version>:<digest>` drifted (same split as above).
|
|
553
|
+
writeFileAtomic(optin, `${stamp}\n`);
|
|
554
|
+
return true;
|
|
555
|
+
}
|
|
489
556
|
|
|
490
557
|
const current = readFileSync(target, "utf8");
|
|
491
558
|
const marker = "\n---\n\n# 8. ";
|
|
@@ -533,8 +600,14 @@ export function ensureConsumerRules(root) {
|
|
|
533
600
|
const managed = `${front}${body}`;
|
|
534
601
|
const next = `${OWNED_STAMP(managed)}\n${managed}`;
|
|
535
602
|
|
|
536
|
-
if (existsSync(target)
|
|
537
|
-
|
|
603
|
+
if (existsSync(target)) {
|
|
604
|
+
const current = readFileSync(target, "utf8");
|
|
605
|
+
if (stampedDigest(current) === digestOf(managed)) {
|
|
606
|
+
const resynced = resyncVersionStamp(current);
|
|
607
|
+
if (resynced == null) return false;
|
|
608
|
+
writeFileAtomic(target, resynced);
|
|
609
|
+
return uiDir;
|
|
610
|
+
}
|
|
538
611
|
}
|
|
539
612
|
mkdirSync(dir, { recursive: true });
|
|
540
613
|
writeFileAtomic(target, next);
|
package/scripts/cli.mjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @godxjp/ui CLI — `npx @godxjp/ui <command>`.
|
|
4
4
|
* init-agent scaffold the agent forcing-kit (MCP + auto-audit hook + mandate)
|
|
5
|
+
* sync-rules refresh package-owned agent rules (same path as postinstall)
|
|
5
6
|
* audit static UI audit (regex over source)
|
|
6
7
|
* visual-audit runtime audit (Playwright + axe-core) against a running app
|
|
7
8
|
*/
|
|
@@ -12,6 +13,7 @@ import { fileURLToPath } from "node:url";
|
|
|
12
13
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
13
14
|
const MAP = {
|
|
14
15
|
"init-agent": "init-agent-kit.mjs",
|
|
16
|
+
"sync-rules": "postinstall.mjs",
|
|
15
17
|
audit: "ui-audit.mjs",
|
|
16
18
|
"visual-audit": "visual-audit.mjs",
|
|
17
19
|
};
|
|
@@ -19,8 +21,10 @@ const MAP = {
|
|
|
19
21
|
const [cmd, ...rest] = process.argv.slice(2);
|
|
20
22
|
const script = MAP[cmd];
|
|
21
23
|
if (!script) {
|
|
22
|
-
console.error("usage: godxjp-ui <init-agent | audit | visual-audit> [args]");
|
|
24
|
+
console.error("usage: godxjp-ui <init-agent | sync-rules | audit | visual-audit> [args]");
|
|
23
25
|
process.exit(1);
|
|
24
26
|
}
|
|
25
|
-
const
|
|
27
|
+
const env =
|
|
28
|
+
cmd === "sync-rules" ? { ...process.env, INIT_CWD: process.env.INIT_CWD ?? process.cwd() } : process.env;
|
|
29
|
+
const r = spawnSync("node", [join(HERE, script), ...rest], { stdio: "inherit", env });
|
|
26
30
|
process.exit(r.status ?? 0);
|