@godxjp/ui 23.4.4 → 23.4.5

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.
@@ -166,6 +166,12 @@ function Tabs({
166
166
  const needsBar = Boolean(extraStart || extraEnd || showAdd || collapsible);
167
167
  const card = variant === "card" || variant === "editable-card";
168
168
  const sizeTriggerClassName = triggerSizeClassName(size);
169
+ const CARD_FACE = {
170
+ top: "rounded-[var(--tabs-card-radius)_var(--tabs-card-radius)_0_0] data-[state=active]:border-b-background",
171
+ bottom: "rounded-[0_0_var(--tabs-card-radius)_var(--tabs-card-radius)] data-[state=active]:border-t-background",
172
+ start: "rounded-s-[var(--tabs-card-radius)] rounded-e-none data-[state=active]:border-e-background",
173
+ end: "rounded-e-[var(--tabs-card-radius)] rounded-s-none data-[state=active]:border-s-background"
174
+ };
169
175
  const list = items ? /* @__PURE__ */ jsx(
170
176
  TabsList,
171
177
  {
@@ -239,11 +245,21 @@ function Tabs({
239
245
  // pixel and `justify-content` has nothing left to centre — measured, the leading and
240
246
  // trailing gaps were both 0px with `centered` on and off.
241
247
  centered && "flex-none",
242
- // CARD face: a real boundary on every side, rounded on the leading block edge only.
243
- // The ACTIVE face's block-end edge is the SURFACE colour, never `transparent`: the
244
- // rail is an inset shadow at the strip's padding-box edge, so a see-through border
245
- // would let that 1px of rail run straight across the tab it is joined to.
246
- card && "data-[state=active]:border-b-background rounded-[var(--tabs-card-radius)_var(--tabs-card-radius)_0_0] border-[color:hsl(var(--border))] bg-[hsl(var(--tabs-card-background,var(--muted)))]"
248
+ // The merged edge is the SURFACE colour, never `transparent`: the rail is an inset
249
+ // shadow at the strip's padding-box edge, so a see-through border would let that 1px
250
+ // of rail run straight across the tab it is joined to.
251
+ // CARD face: a real boundary on every side, opened toward the panel by
252
+ // `CARD_FACE`. `flex-none` is antd's geometry, not a preference — its
253
+ // `nav-list` is a plain `display: flex` with no `flex-grow` on the tab, so a
254
+ // card tab is CONTENT-WIDTH. The base trigger is `flex-1`, which made every
255
+ // card tab stretch to an equal share of the strip: measured, three tabs in a
256
+ // 942px strip came out 311px each for labels needing ~70px, and with
257
+ // `justify-center` the label and its × floated in the middle of that box —
258
+ // the × ended up 90px from the tab's own trailing edge (179px on a two-tab
259
+ // strip). antd's is 8px away. This is also why the face read as a free-standing
260
+ // bordered box rather than a tab.
261
+ card && "flex-none border-[color:hsl(var(--border))] bg-[hsl(var(--tabs-card-background,var(--muted)))]",
262
+ card && CARD_FACE[placement]
247
263
  ),
248
264
  children: [
249
265
  item.icon ? /* @__PURE__ */ jsx(
@@ -463,7 +479,7 @@ const TabsTrigger = React.forwardRef(
463
479
  // The line indicator lives in src/styles/navigation-layout.css so it reads --tabs-indicator-*.
464
480
  // Selected and focused stay visually distinct (WCAG 2.4.7): selected is a 1px hairline in the
465
481
  // border, focused is the 2px ring plus its halo outside it.
466
- "text-muted-foreground ring-offset-background hover:text-foreground ui-focus-ring data-[state=active]:bg-background data-[state=active]:text-foreground group-data-[variant=default]/tabs-list:data-[state=active]:border-primary/25 relative inline-flex flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-3 py-1 text-sm font-medium whitespace-nowrap transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:flex-none group-data-[orientation=vertical]/tabs:justify-start group-data-[variant=line]/tabs-list:border-e-0 group-data-[variant=line]/tabs-list:border-b-0 disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none",
482
+ "text-muted-foreground ring-offset-background hover:text-foreground ui-focus-ring data-[state=active]:bg-background data-[state=active]:text-foreground group-data-[variant=default]/tabs:group-data-[variant=default]/tabs-list:data-[state=active]:border-primary/25 relative inline-flex flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-3 py-1 text-sm font-medium whitespace-nowrap transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:flex-none group-data-[orientation=vertical]/tabs:justify-start group-data-[variant=line]/tabs-list:border-e-0 group-data-[variant=line]/tabs-list:border-b-0 disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs:group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none",
467
483
  // The tier the root was given. Without this the compound form ignored `size` outright.
468
484
  triggerSizeClassName(size),
469
485
  className
@@ -33,7 +33,7 @@ export declare const EMAIL_COLOR_SOURCE: {
33
33
  };
34
34
  readonly border: {
35
35
  readonly cssVar: "--border";
36
- readonly hsl: "30 7% 83%";
36
+ readonly hsl: "30 7% 93%";
37
37
  };
38
38
  readonly primary: {
39
39
  readonly cssVar: "--primary";
@@ -92,7 +92,7 @@ export declare const EMAIL_COLOR_SOURCE_DARK: {
92
92
  };
93
93
  readonly border: {
94
94
  readonly cssVar: "--border";
95
- readonly hsl: "45 6% 22%";
95
+ readonly hsl: "45 6% 19%";
96
96
  };
97
97
  readonly primary: {
98
98
  readonly cssVar: "--primary";
@@ -25,7 +25,7 @@ const EMAIL_COLOR_SOURCE = {
25
25
  },
26
26
  "border": {
27
27
  "cssVar": "--border",
28
- "hsl": "30 7% 83%"
28
+ "hsl": "30 7% 93%"
29
29
  },
30
30
  "primary": {
31
31
  "cssVar": "--primary",
@@ -83,7 +83,7 @@ const EMAIL_COLOR_SOURCE_DARK = {
83
83
  },
84
84
  "border": {
85
85
  "cssVar": "--border",
86
- "hsl": "45 6% 22%"
86
+ "hsl": "45 6% 19%"
87
87
  },
88
88
  "primary": {
89
89
  "cssVar": "--primary",
@@ -76,19 +76,19 @@
76
76
  }
77
77
 
78
78
  [data-slot="alert-icon"][data-tone="destructive"] {
79
- color: hsl(var(--destructive));
79
+ color: hsl(var(--text-error));
80
80
  }
81
81
 
82
82
  [data-slot="alert-icon"][data-tone="warning"] {
83
- color: hsl(var(--warning));
83
+ color: hsl(var(--text-warning));
84
84
  }
85
85
 
86
86
  [data-slot="alert-icon"][data-tone="success"] {
87
- color: hsl(var(--success));
87
+ color: hsl(var(--text-success));
88
88
  }
89
89
 
90
90
  [data-slot="alert-icon"][data-tone="info"] {
91
- color: hsl(var(--info));
91
+ color: hsl(var(--text-info));
92
92
  }
93
93
 
94
94
  [data-slot="alert-body"] {
@@ -1053,6 +1053,7 @@
1053
1053
  width: 1.25rem;
1054
1054
  height: 1.25rem;
1055
1055
  }
1056
+
1056
1057
  .ui-rating-star-filled {
1057
1058
  color: hsl(var(--warning));
1058
1059
  }
@@ -1572,7 +1573,7 @@
1572
1573
  }
1573
1574
 
1574
1575
  .ui-search-select-placeholder[data-tone="destructive"] {
1575
- color: hsl(var(--destructive));
1576
+ color: hsl(var(--text-error));
1576
1577
  }
1577
1578
 
1578
1579
  .ui-search-select-footer {
@@ -87,15 +87,15 @@
87
87
  }
88
88
 
89
89
  .ui-password-strength-segment[data-tone="destructive"] {
90
- background: hsl(var(--destructive));
90
+ background: hsl(var(--mark-destructive));
91
91
  }
92
92
 
93
93
  .ui-password-strength-segment[data-tone="warning"] {
94
- background: hsl(var(--warning));
94
+ background: hsl(var(--mark-warning));
95
95
  }
96
96
 
97
97
  .ui-password-strength-segment[data-tone="success"] {
98
- background: hsl(var(--success));
98
+ background: hsl(var(--mark-success));
99
99
  }
100
100
 
101
101
  .ui-password-strength-meta {
@@ -133,11 +133,11 @@
133
133
  }
134
134
 
135
135
  .ui-password-strength-checklist-item[data-state="passed"] svg {
136
- color: hsl(var(--success));
136
+ color: hsl(var(--text-success));
137
137
  }
138
138
 
139
139
  .ui-password-strength-checklist-item[data-state="failed"] svg {
140
- color: hsl(var(--destructive));
140
+ color: hsl(var(--text-error));
141
141
  }
142
142
  }
143
143
 
@@ -8,7 +8,7 @@
8
8
 
9
9
  .ui-form-field-required {
10
10
  margin-inline-start: var(--space-inline-xs);
11
- color: hsl(var(--destructive));
11
+ color: hsl(var(--text-error));
12
12
  }
13
13
 
14
14
  .ui-form {
@@ -1247,7 +1247,7 @@
1247
1247
  }
1248
1248
 
1249
1249
  .ui-filter-bar-error {
1250
- color: hsl(var(--destructive));
1250
+ color: hsl(var(--text-error));
1251
1251
  }
1252
1252
 
1253
1253
  .ui-legal-document-scope {
@@ -76,16 +76,16 @@
76
76
  color: hsl(var(--primary));
77
77
  }
78
78
  .ui-activity[data-tone="success"] .ui-activity-mark {
79
- color: hsl(var(--success));
79
+ color: hsl(var(--text-success));
80
80
  }
81
81
  .ui-activity[data-tone="warning"] .ui-activity-mark {
82
- color: hsl(var(--warning));
82
+ color: hsl(var(--text-warning));
83
83
  }
84
84
  .ui-activity[data-tone="destructive"] .ui-activity-mark {
85
- color: hsl(var(--destructive));
85
+ color: hsl(var(--text-error));
86
86
  }
87
87
  .ui-activity[data-tone="info"] .ui-activity-mark {
88
- color: hsl(var(--info));
88
+ color: hsl(var(--text-info));
89
89
  }
90
90
 
91
91
  .ui-activity-dot {
@@ -60,7 +60,16 @@
60
60
 
61
61
  [data-slot="tabs-list"][data-variant="line"][data-orientation="horizontal"]
62
62
  [data-slot="tabs-trigger"]::after {
63
- inset-block-end: calc(-1 * var(--tabs-indicator-offset));
63
+ inset-block-end: calc(
64
+ -1 *
65
+ (
66
+ var(--tabs-indicator-offset) + var(--tabs-list-line-space-inset) +
67
+ var(
68
+ --tabs-list-focus-ring-space-inset,
69
+ calc(var(--focus-ring-width) + var(--focus-ring-glow-width))
70
+ )
71
+ )
72
+ );
64
73
  block-size: var(--tabs-indicator-size);
65
74
  inline-size: calc(100% - 2 * var(--tabs-indicator-inset));
66
75
  inset-inline-start: var(--tabs-indicator-inset);
@@ -204,6 +213,12 @@
204
213
  box-shadow: inset 0 calc(-1 * var(--tabs-card-rail-border-width)) 0 hsl(var(--border));
205
214
  }
206
215
 
216
+ [data-slot="tabs"][data-variant="card"][data-placement="bottom"] [data-slot="tabs-list"],
217
+ [data-slot="tabs"][data-variant="editable-card"][data-placement="bottom"]
218
+ [data-slot="tabs-list"] {
219
+ box-shadow: inset 0 var(--tabs-card-rail-border-width) 0 hsl(var(--border));
220
+ }
221
+
207
222
  .ui-tabs-tab-remove {
208
223
  display: inline-flex;
209
224
  flex: 0 0 auto;
@@ -223,7 +238,8 @@
223
238
  color: hsl(var(--accent-foreground));
224
239
  }
225
240
 
226
- .ui-tabs-tab-remove-icon {
241
+ .ui-tabs-tab-remove-icon,
242
+ .ui-tabs-tab-remove > :is(svg, img) {
227
243
  inline-size: var(--tabs-tab-remove-icon-size);
228
244
  block-size: var(--tabs-tab-remove-icon-size);
229
245
  }
@@ -374,7 +390,7 @@
374
390
  }
375
391
 
376
392
  .ui-dropdown-menu-item[data-variant="destructive"] {
377
- color: hsl(var(--destructive));
393
+ color: hsl(var(--text-error));
378
394
  }
379
395
 
380
396
  .ui-dropdown-menu-separator,
@@ -461,7 +477,7 @@
461
477
  }
462
478
 
463
479
  .ui-steps-inline-item[data-status="error"] .ui-steps-inline-control {
464
- color: hsl(var(--destructive));
480
+ color: hsl(var(--text-error));
465
481
  }
466
482
 
467
483
  .ui-steps-inline-item[data-status="wait"] .ui-steps-inline-control {
@@ -590,7 +606,7 @@
590
606
 
591
607
  .ui-dropdown-menu-item[data-variant="destructive"][data-highlighted] {
592
608
  background: hsl(var(--destructive) / 0.1);
593
- color: hsl(var(--destructive));
609
+ color: hsl(var(--text-error));
594
610
  }
595
611
 
596
612
  .ui-dropdown-menu-item[data-disabled],
@@ -709,7 +725,7 @@
709
725
  }
710
726
 
711
727
  .ui-steps-title[data-status="error"] {
712
- color: hsl(var(--destructive));
728
+ color: hsl(var(--text-error));
713
729
  }
714
730
 
715
731
  .ui-steps-subtitle {
@@ -20,7 +20,8 @@
20
20
  --accent-foreground: 48 8% 13%;
21
21
  --destructive: 357 64% 44%;
22
22
  --destructive-foreground: 60 33% 99%;
23
- --border: 30 7% 83%;
23
+
24
+ --border: 30 7% 93%;
24
25
 
25
26
  --input: 30 7% 53%;
26
27
 
@@ -331,7 +332,8 @@
331
332
 
332
333
  --destructive: 357 55% 48%;
333
334
  --destructive-foreground: 0 0% 100%;
334
- --border: 45 6% 22%;
335
+
336
+ --border: 45 6% 19%;
335
337
 
336
338
  --input: 45 6% 47%;
337
339
 
@@ -11,7 +11,9 @@ import { Flex, PageContainer } from "@godxjp/ui/layout";
11
11
  */
12
12
  export default function Demo() {
13
13
  const [signupPassword, setSignupPassword] = useState("");
14
- const [resetPassword, setResetPassword] = useState("");
14
+ /* Seeded so the PASSED state is on screen without typing: the checklist's success icon is
15
+ * otherwise only reachable by interaction, which means no sweep can measure it (gh#612). */
16
+ const [resetPassword, setResetPassword] = useState("Passw0rdSeed");
15
17
 
16
18
  return (
17
19
  <PageContainer
@@ -300,6 +300,38 @@ export default function Demo() {
300
300
  </DropdownMenu>
301
301
  </CardContent>
302
302
  </Card>
303
+ {/* defaultOpen — OPEN ON MOUNT, on purpose, and this is the only example on the page that
304
+ is. A menu paints nothing until it is opened, so for as long as this page has existed
305
+ no sweep has ever measured a single menu pixel: that is how
306
+ `.ui-dropdown-menu-item[data-variant="destructive"]` — the label on a Delete row, i.e.
307
+ prose — kept the destructive FILL tier at 2.95:1 on dark (gh#612). Same reasoning as
308
+ `?toast=` in check-contrast's route list: a surface that needs an interaction is a
309
+ surface no gate sees. */}
310
+ <Card>
311
+ <CardHeader>
312
+ <CardTitle level={2}>defaultOpen · 計測できる状態で開いておく</CardTitle>
313
+ <CardDescription>
314
+ antd の open/defaultOpen。ここだけはマウント時から開いたままにしてある。
315
+ 閉じたメニューは 1 ピクセルも描かないので、どのスイープにも測れない。
316
+ destructive の行が読める色で描かれているかは、開いていて初めて測れる。
317
+ </CardDescription>
318
+ </CardHeader>
319
+ <CardContent>
320
+ <DropdownMenu defaultOpen>
321
+ <DropdownMenuTrigger asChild>
322
+ <Button variant="outline" size="sm">
323
+ 仕訳アクション
324
+ </Button>
325
+ </DropdownMenuTrigger>
326
+ <DropdownMenuContent align="start">
327
+ <DropdownMenuItem>編集</DropdownMenuItem>
328
+ <DropdownMenuItem>複製</DropdownMenuItem>
329
+ <DropdownMenuSeparator />
330
+ <DropdownMenuItem variant="destructive">削除</DropdownMenuItem>
331
+ </DropdownMenuContent>
332
+ </DropdownMenu>
333
+ </CardContent>
334
+ </Card>
303
335
  </Flex>
304
336
  </PageContainer>
305
337
  );
@@ -128,6 +128,26 @@ export default function Demo() {
128
128
  />
129
129
  </Flex>
130
130
 
131
+ {/* error — the ONE sentence that says why a filter set was refused, and the reason this
132
+ page has an example at all: `.ui-filter-bar-error` was painted with the destructive
133
+ FILL tier (2.95:1 on dark, gh#612) and NO route rendered it, so no sweep could ever
134
+ have caught that. Static, not behind a submit: a surface that needs an interaction is
135
+ a surface `check:contrast` does not measure. */}
136
+ <Flex direction="col" gap="xs">
137
+ <Text size="xs" tone="muted">
138
+ error · role=&quot;alert&quot; line under the bar, painted from the TEXT tier
139
+ </Text>
140
+ <FilterBar
141
+ search={{
142
+ value: modelQuery,
143
+ onValueChange: setModelQuery,
144
+ placeholder: "氏名・メールで検索",
145
+ ariaLabel: "メンバーを検索",
146
+ }}
147
+ error="期間の開始日が終了日より後です。条件を修正してください。"
148
+ />
149
+ </Flex>
150
+
131
151
  {/* overflow="wrap" (default) — stacked below 640px, wrapping rows above. */}
132
152
  <Flex direction="col" gap="xs">
133
153
  <Text size="xs" tone="muted">
@@ -27,7 +27,11 @@ const onboardingSteps = [
27
27
  export default function Demo() {
28
28
  const [approvalCurrent, setApprovalCurrent] = useState(1);
29
29
  const [wizardCurrent, setWizardCurrent] = useState(0);
30
- const [hasError, setHasError] = useState(false);
30
+ /* Seeded TRUE so `status="error"` paints on mount. The toggle below is still the point of the
31
+ * demo, but starting from `false` meant the error state was reachable only by clicking —
32
+ * and a surface that needs an interaction is a surface no sweep measures (gh#612), which is
33
+ * how `.ui-steps-title[data-status="error"]` kept the destructive FILL tier unnoticed. */
34
+ const [hasError, setHasError] = useState(true);
31
35
 
32
36
  return (
33
37
  <PageContainer
@@ -256,6 +260,15 @@ export default function Demo() {
256
260
  value={1}
257
261
  items={[{ title: "コード入力" }, { title: "確認" }, { title: "完了" }]}
258
262
  />
263
+ {/* status="error" on the INLINE form too. The vertical/horizontal error title had a
264
+ route; `.ui-steps-inline-item[data-status="error"] .ui-steps-inline-control` did
265
+ not, and it reads the same tone token (gh#612). */}
266
+ <Steps
267
+ type="inline"
268
+ status="error"
269
+ value={1}
270
+ items={[{ title: "コード入力" }, { title: "確認" }, { title: "完了" }]}
271
+ />
259
272
  </Flex>
260
273
  </CardContent>
261
274
  </Card>
@@ -1,6 +1,6 @@
1
1
  import { useState } from "react";
2
2
 
3
- import { Trash2 } from "lucide-react";
3
+ import { CircleX } from "lucide-react";
4
4
 
5
5
  import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@godxjp/ui/data-display";
6
6
  import { Button, Text } from "@godxjp/ui/general";
@@ -547,7 +547,7 @@ export default function Demo() {
547
547
  id="antd-remove-icon"
548
548
  variant="editable-card"
549
549
  defaultValue="je-0042"
550
- closeIcon={<Trash2 aria-hidden="true" />}
550
+ closeIcon={<CircleX aria-hidden="true" />}
551
551
  onEdit={() => undefined}
552
552
  items={[
553
553
  {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@godxjp/ui",
3
- "version": "23.4.4",
4
- "godxUiMcp": "23.4.4",
3
+ "version": "23.4.5",
4
+ "godxUiMcp": "23.4.5",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",