@qijenchen/design-system 0.1.0-beta.75 → 0.1.0-beta.76
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/CLAUDE.md +1 -1
- package/dist/components/AppShell/app-shell.d.ts +2 -2
- package/dist/components/AppShell/app-shell.js.map +1 -1
- package/dist/components/Avatar/avatar.js.map +1 -1
- package/dist/components/Button/button.d.ts.map +1 -1
- package/dist/components/Button/button.js.map +1 -1
- package/dist/components/Chart/chart.d.ts +1 -1
- package/dist/components/Chart/chart.js.map +1 -1
- package/dist/components/Checkbox/checkbox.d.ts +1 -1
- package/dist/components/Checkbox/checkbox.js +1 -1
- package/dist/components/Checkbox/checkbox.js.map +1 -1
- package/dist/components/Combobox/combobox.d.ts +1 -1
- package/dist/components/Combobox/combobox.d.ts.map +1 -1
- package/dist/components/Combobox/combobox.js +13 -10
- package/dist/components/Combobox/combobox.js.map +1 -1
- package/dist/components/Command/command.d.ts +1 -1
- package/dist/components/Command/command.js +3 -3
- package/dist/components/Command/command.js.map +1 -1
- package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
- package/dist/components/DataTable/cell-registry.js +2 -2
- package/dist/components/DataTable/cell-registry.js.map +1 -1
- package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
- package/dist/components/DatePicker/date-picker.js +2 -2
- package/dist/components/DatePicker/date-picker.js.map +1 -1
- package/dist/components/DescriptionList/description-list.d.ts +1 -1
- package/dist/components/DescriptionList/description-list.js +2 -2
- package/dist/components/DescriptionList/description-list.js.map +1 -1
- package/dist/components/Empty/empty.d.ts +2 -0
- package/dist/components/Empty/empty.d.ts.map +1 -1
- package/dist/components/Empty/empty.js.map +1 -1
- package/dist/components/Field/field-wrapper.js +4 -4
- package/dist/components/Field/field-wrapper.js.map +1 -1
- package/dist/components/OverflowIndicator/overflow-indicator.d.ts +1 -1
- package/dist/components/OverflowIndicator/overflow-indicator.js +2 -2
- package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
- package/dist/components/PeoplePicker/people-picker.js +2 -2
- package/dist/components/PeoplePicker/people-picker.js.map +1 -1
- package/dist/components/ProfileCard/profile-card.d.ts +1 -1
- package/dist/components/ProfileCard/profile-card.js +2 -1
- package/dist/components/ProfileCard/profile-card.js.map +1 -1
- package/dist/components/Rating/rating.js.map +1 -1
- package/dist/components/Select/select.js +4 -4
- package/dist/components/Select/select.js.map +1 -1
- package/dist/components/Textarea/textarea.d.ts +1 -1
- package/dist/components/Textarea/textarea.js +2 -2
- package/dist/components/Textarea/textarea.js.map +1 -1
- package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
- package/dist/components/TimePicker/time-picker.js +14 -23
- package/dist/components/TimePicker/time-picker.js.map +1 -1
- package/dist/components/TreeView/tree-view.d.ts +1 -1
- package/dist/components/TreeView/tree-view.js +1 -1
- package/dist/components/TreeView/tree-view.js.map +1 -1
- package/ds-canonical/fork/governance.lock +1 -1
- package/ds-canonical/fork/preamble.md +2 -2
- package/ds-canonical/hooks/check_field_controls_contracts.sh +30 -3
- package/ds-canonical/references/props-naming.md +15 -1
- package/ds-canonical/rules/ui-development.md +2 -2
- package/llms-full.txt +7 -2
- package/llms.txt +3 -3
- package/package.json +1 -1
- package/src/components/Alert/alert.anatomy.stories.tsx +4 -4
- package/src/components/AppShell/app-shell.spec.md +4 -4
- package/src/components/AppShell/app-shell.tsx +2 -2
- package/src/components/Avatar/avatar.tsx +1 -1
- package/src/components/Breadcrumb/breadcrumb.spec.md +11 -1
- package/src/components/Button/button.tsx +0 -10
- package/src/components/Calendar/calendar.anatomy.stories.tsx +1 -1
- package/src/components/Chart/chart.tsx +1 -1
- package/src/components/Checkbox/checkbox.tsx +1 -1
- package/src/components/Coachmark/coachmark.anatomy.stories.tsx +1 -1
- package/src/components/Coachmark/coachmark.spec.md +2 -2
- package/src/components/Combobox/combobox.anatomy.stories.tsx +12 -12
- package/src/components/Combobox/combobox.principles.stories.tsx +1 -1
- package/src/components/Combobox/combobox.spec.md +1 -1
- package/src/components/Combobox/combobox.tsx +25 -14
- package/src/components/Command/command.anatomy.stories.tsx +2 -0
- package/src/components/Command/command.tsx +2 -2
- package/src/components/DataTable/cell-registry.tsx +6 -2
- package/src/components/DataTable/data-table-filter-panel.tsx +3 -3
- package/src/components/DataTable/data-table.anatomy.stories.tsx +1 -1
- package/src/components/DataTable/data-table.css +1 -1
- package/src/components/DataTable/data-table.spec.md +2 -2
- package/src/components/DateGrid/date-grid.anatomy.stories.tsx +1 -1
- package/src/components/DateGrid/date-grid.spec.md +1 -1
- package/src/components/DatePicker/date-picker.anatomy.stories.tsx +15 -11
- package/src/components/DatePicker/date-picker.spec.md +1 -1
- package/src/components/DatePicker/date-picker.tsx +9 -6
- package/src/components/DescriptionList/description-list.tsx +1 -1
- package/src/components/Dialog/dialog.anatomy.stories.tsx +1 -1
- package/src/components/DropdownMenu/dropdown-menu.spec.md +1 -1
- package/src/components/Empty/empty.tsx +2 -0
- package/src/components/Field/field-controls.spec.md +9 -6
- package/src/components/Field/field-wrapper.tsx +4 -4
- package/src/components/FileItem/file-item.principles.stories.tsx +1 -0
- package/src/components/FileUpload/file-upload.principles.stories.tsx +2 -2
- package/src/components/FileUpload/file-upload.spec.md +1 -1
- package/src/components/HoverCard/hover-card.principles.stories.tsx +1 -1
- package/src/components/Input/input.anatomy.stories.tsx +3 -3
- package/src/components/LinkInput/link-input.anatomy.stories.tsx +3 -3
- package/src/components/Notice/notice.anatomy.stories.tsx +1 -1
- package/src/components/NumberInput/number-input.anatomy.stories.tsx +8 -7
- package/src/components/NumberInput/number-input.spec.md +1 -1
- package/src/components/OverflowIndicator/overflow-indicator.tsx +1 -1
- package/src/components/PeoplePicker/people-picker.spec.md +3 -3
- package/src/components/PeoplePicker/people-picker.tsx +6 -6
- package/src/components/Popover/popover.principles.stories.tsx +4 -4
- package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +1 -1
- package/src/components/ProfileCard/profile-card.tsx +1 -1
- package/src/components/ProgressBar/progress-bar.spec.md +1 -1
- package/src/components/Rating/rating.anatomy.stories.tsx +2 -2
- package/src/components/Rating/rating.spec.md +1 -1
- package/src/components/Rating/rating.tsx +1 -1
- package/src/components/Select/select.anatomy.stories.tsx +18 -18
- package/src/components/Select/select.spec.md +1 -1
- package/src/components/Select/select.tsx +7 -7
- package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +1 -1
- package/src/components/Sidebar/sidebar.spec.md +2 -2
- package/src/components/Slider/slider.anatomy.stories.tsx +1 -1
- package/src/components/Steps/steps.spec.md +2 -2
- package/src/components/Tabs/tabs.principles.stories.tsx +1 -1
- package/src/components/Tabs/tabs.spec.md +1 -1
- package/src/components/Textarea/textarea.tsx +3 -3
- package/src/components/TimePicker/time-picker.spec.md +1 -1
- package/src/components/TimePicker/time-picker.tsx +11 -12
- package/src/components/TreeView/tree-view.tsx +1 -1
- package/src/patterns/element-anatomy/item-anatomy.spec.md +1 -1
- package/src/patterns/element-anatomy/item-anatomy.stories.tsx +1 -1
- package/src/patterns/overlay-surface/overlay-surface.spec.md +1 -0
- package/src/patterns/resize-handle/resize-handle.spec.md +1 -1
- package/src/tokens/color/semantic.css +1 -1
- package/src/tokens/uiSize/uiSize.css +5 -0
- package/src/tokens/uiSize/uiSize.spec.md +17 -3
|
@@ -47,11 +47,11 @@ const TOKEN_MAP: Record<ModeKey, Record<StateKey, ColorSpec>> = {
|
|
|
47
47
|
},
|
|
48
48
|
// readonly wrapper 為靜態 bg-readonly(field-wrapper.tsx 無 hover/focus 樣式;mode prop 勝 disabled)
|
|
49
49
|
readonly: {
|
|
50
|
-
default: { bg: '--bg-readonly', text: '--foreground', border: 'transparent', icon: '
|
|
51
|
-
hover: { bg: '--bg-readonly', text: '--foreground', border: 'transparent', icon: '
|
|
52
|
-
focus: { bg: '--bg-readonly', text: '--foreground', border: 'transparent', icon: '
|
|
53
|
-
error: { bg: '--bg-readonly', text: '--foreground', border: 'transparent', icon: '
|
|
54
|
-
disabled: { bg: '--bg-readonly', text: '--foreground', border: 'transparent', icon: '
|
|
50
|
+
default: { bg: '--bg-readonly', text: '--foreground', border: 'transparent', icon: '—' },
|
|
51
|
+
hover: { bg: '--bg-readonly', text: '--foreground', border: 'transparent', icon: '—' },
|
|
52
|
+
focus: { bg: '--bg-readonly', text: '--foreground', border: 'transparent', icon: '—' },
|
|
53
|
+
error: { bg: '--bg-readonly', text: '--foreground', border: 'transparent', icon: '—' },
|
|
54
|
+
disabled: { bg: '--bg-readonly', text: '--foreground', border: 'transparent', icon: '—' },
|
|
55
55
|
},
|
|
56
56
|
disabled: {
|
|
57
57
|
default: { bg: '--bg-disabled', text: '--fg-disabled', border: 'transparent', icon: '--fg-disabled' },
|
|
@@ -86,7 +86,7 @@ interface SizeSpec {
|
|
|
86
86
|
const SIZE_SPECS: Record<SizeKey, SizeSpec> = {
|
|
87
87
|
sm: {
|
|
88
88
|
heightToken: 'h-field-sm', height: '28px',
|
|
89
|
-
pxToken: 'px-
|
|
89
|
+
pxToken: 'px-[var(--field-px)]', px: '12px',
|
|
90
90
|
gapToken: 'gap-2', gap: '8px',
|
|
91
91
|
fontToken: 'text-body', font: '14px',
|
|
92
92
|
icon: 16,
|
|
@@ -95,7 +95,7 @@ const SIZE_SPECS: Record<SizeKey, SizeSpec> = {
|
|
|
95
95
|
},
|
|
96
96
|
md: {
|
|
97
97
|
heightToken: 'h-field-md', height: '32px',
|
|
98
|
-
pxToken: 'px-
|
|
98
|
+
pxToken: 'px-[var(--field-px)]', px: '12px',
|
|
99
99
|
gapToken: 'gap-2', gap: '8px',
|
|
100
100
|
fontToken: 'text-body', font: '14px',
|
|
101
101
|
icon: 16,
|
|
@@ -104,7 +104,7 @@ const SIZE_SPECS: Record<SizeKey, SizeSpec> = {
|
|
|
104
104
|
},
|
|
105
105
|
lg: {
|
|
106
106
|
heightToken: 'h-field-lg', height: '36px',
|
|
107
|
-
pxToken: 'px-
|
|
107
|
+
pxToken: 'px-[var(--field-px)]', px: '12px',
|
|
108
108
|
gapToken: 'gap-2', gap: '8px',
|
|
109
109
|
fontToken: 'text-body-lg', font: '16px',
|
|
110
110
|
icon: 20,
|
|
@@ -271,7 +271,7 @@ export const Overview = {
|
|
|
271
271
|
<span className="rounded px-2 py-1 text-[11px] font-mono border border-dashed"
|
|
272
272
|
style={{ borderColor: 'var(--color-turquoise-6)', backgroundColor: 'var(--color-turquoise-1)', color: 'var(--color-turquoise-6)' }}>Tag</span>
|
|
273
273
|
</div>
|
|
274
|
-
<span className="text-[10px] text-fg-muted font-mono">chevron
|
|
274
|
+
<span className="text-[10px] text-fg-muted font-mono">chevron:readonly 不顯示 / disabled 保留(類型身份 indicator)· 不可開選單 · tagPadding 置中</span>
|
|
275
275
|
</div>
|
|
276
276
|
</div>
|
|
277
277
|
</div>
|
|
@@ -437,7 +437,7 @@ const InspectorInner = () => {
|
|
|
437
437
|
{ c: Z.gap, l: '元素間距' },
|
|
438
438
|
{ c: Z.select, l: 'Select 文字' },
|
|
439
439
|
...(clearable && value ? [{ c: Z.action, l: 'Clear' }] : []),
|
|
440
|
-
{ c: Z.icon, l: 'Chevron' },
|
|
440
|
+
...(mode !== 'readonly' ? [{ c: Z.icon, l: 'Chevron' }] : []),
|
|
441
441
|
].map(({ c, l }) => (
|
|
442
442
|
<span key={l} className="inline-flex items-center gap-1">
|
|
443
443
|
<span className="w-2.5 h-2.5 rounded-md" style={{ background: c.bg, border: `1px dashed ${c.border}` }} />
|
|
@@ -452,7 +452,7 @@ const InspectorInner = () => {
|
|
|
452
452
|
{hasStartIcon && <BpZone w={32} color={Z.gap} label={s.gapToken} sub={s.gap} />}
|
|
453
453
|
<BpZone w={80} color={Z.select} label="flex-1" sub="select 文字" />
|
|
454
454
|
{clearable && value && <BpZone w={36} color={Z.action} label={`${s.icon}px`} sub="clear" />}
|
|
455
|
-
<BpZone w={36} color={Z.icon} label={`${s.icon}px`} sub="chevron" />
|
|
455
|
+
{mode !== 'readonly' && <BpZone w={36} color={Z.icon} label={`${s.icon}px`} sub="chevron" />}
|
|
456
456
|
<BpZone w={44} color={Z.pad} label={s.pxToken} sub={s.px} />
|
|
457
457
|
</div>
|
|
458
458
|
<div className="ml-3 flex items-center" style={{ height: 52 }}>
|
|
@@ -475,7 +475,7 @@ const InspectorInner = () => {
|
|
|
475
475
|
{ c: Z.select, l: 'Select (hidden)' },
|
|
476
476
|
{ c: Z.gap, l: 'Spacer' },
|
|
477
477
|
...(clearable && value ? [{ c: Z.action, l: 'Clear' }] : []),
|
|
478
|
-
{ c: Z.icon, l: 'Chevron' },
|
|
478
|
+
...(mode !== 'readonly' ? [{ c: Z.icon, l: 'Chevron' }] : []),
|
|
479
479
|
].map(({ c, l }) => (
|
|
480
480
|
<span key={l} className="inline-flex items-center gap-1">
|
|
481
481
|
<span className="w-2.5 h-2.5 rounded-md" style={{ background: c.bg, border: `1px dashed ${c.border}` }} />
|
|
@@ -490,7 +490,7 @@ const InspectorInner = () => {
|
|
|
490
490
|
<BpZone w={40} color={Z.select} label="select" sub="hidden" />
|
|
491
491
|
<BpZone w={36} color={Z.gap} label="flex-1" sub="spacer" />
|
|
492
492
|
{clearable && value && <BpZone w={36} color={Z.action} label={`${s.icon}px`} sub="clear" />}
|
|
493
|
-
<BpZone w={36} color={Z.icon} label={`${s.icon}px`} sub="chevron" />
|
|
493
|
+
{mode !== 'readonly' && <BpZone w={36} color={Z.icon} label={`${s.icon}px`} sub="chevron" />}
|
|
494
494
|
<BpZone w={44} color={Z.pad} label="12px" sub="pR fixed" />
|
|
495
495
|
</div>
|
|
496
496
|
<div className="ml-3 flex items-center" style={{ height: 52 }}>
|
|
@@ -520,7 +520,7 @@ const InspectorInner = () => {
|
|
|
520
520
|
<PropRow label="Fill"><TokenValue value={colors.bg} /></PropRow>
|
|
521
521
|
<PropRow label="Text"><TokenValue value={colors.text} /></PropRow>
|
|
522
522
|
<PropRow label="Stroke"><TokenValue value={colors.border} /></PropRow>
|
|
523
|
-
<PropRow label="Icon"><TokenValue value={colors.icon} /></PropRow>
|
|
523
|
+
{mode !== 'readonly' && <PropRow label="Icon"><TokenValue value={colors.icon} /></PropRow>}
|
|
524
524
|
</div>
|
|
525
525
|
|
|
526
526
|
{/* LAYOUT */}
|
|
@@ -530,14 +530,14 @@ const InspectorInner = () => {
|
|
|
530
530
|
{isTag ? (
|
|
531
531
|
<>
|
|
532
532
|
<PropRow label="tagPadding" dot={Z.pad.text}><TkVal token="calc()" value={s.tagPaddingFormula} /></PropRow>
|
|
533
|
-
<PropRow label="右側 pR"><TkVal token="
|
|
533
|
+
<PropRow label="右側 pR"><TkVal token="var(--field-px)" value="12px" /></PropRow>
|
|
534
534
|
<PropRow label="Tag 高度" dot={Z.tag.text}>{s.tagHeight}</PropRow>
|
|
535
535
|
</>
|
|
536
536
|
) : (
|
|
537
537
|
<PropRow label="左右內距" dot={Z.pad.text}><TkVal token={s.pxToken} value={s.px} /></PropRow>
|
|
538
538
|
)}
|
|
539
539
|
<PropRow label="元素間距" dot={Z.gap.text}><TkVal token={s.gapToken} value={s.gap} /></PropRow>
|
|
540
|
-
<PropRow label="Icon 尺寸" dot={Z.icon.text}>{s.icon}px</PropRow>
|
|
540
|
+
{mode !== 'readonly' && <PropRow label="Icon 尺寸" dot={Z.icon.text}>{s.icon}px</PropRow>}
|
|
541
541
|
</div>
|
|
542
542
|
|
|
543
543
|
{/* TYPOGRAPHY */}
|
|
@@ -722,7 +722,7 @@ export const SizeMatrix = {
|
|
|
722
722
|
{/* ── tag mode additional tokens ── */}
|
|
723
723
|
<div className="flex flex-col gap-2">
|
|
724
724
|
<span className="text-caption font-medium text-fg-secondary">tag 模式額外 token</span>
|
|
725
|
-
<Desc>tag 模式的 padding 用 calc() 公式置中 Tag:px = (field-height - tag-height) / 2。右側 paddingRight 固定 12px
|
|
725
|
+
<Desc>tag 模式的 padding 用 calc() 公式置中 Tag:px = (field-height - tag-height) / 2。右側 paddingRight 固定 12px(--field-px token)。</Desc>
|
|
726
726
|
<div className="overflow-x-auto">
|
|
727
727
|
<table className="border-collapse text-caption">
|
|
728
728
|
<thead><tr>
|
|
@@ -752,7 +752,7 @@ export const SizeMatrix = {
|
|
|
752
752
|
<Td>paddingRight</Td>
|
|
753
753
|
{SIZES.map((sz) => (
|
|
754
754
|
<Td key={sz} mono>
|
|
755
|
-
<div className="text-fg-secondary">
|
|
755
|
+
<div className="text-fg-secondary">var(--field-px)</div>
|
|
756
756
|
<div className="text-fg-muted text-[10px]">12px(固定)</div>
|
|
757
757
|
</Td>
|
|
758
758
|
))}
|
|
@@ -240,7 +240,7 @@ Select 的值套用時機是**由 onChange handler 的副作用決定**,不是
|
|
|
240
240
|
- Tag 元件呈現選中值 + 隱藏的原生 select overlay
|
|
241
241
|
- Tag 設為 `pointer-events-none`,點擊穿透到底層 select
|
|
242
242
|
- edit 模式:Tag + ChevronDown + 可選 clear
|
|
243
|
-
- readonly
|
|
243
|
+
- readonly:Tag 顯示,**ChevronDown 不顯示**(純值、不可開選單);disabled:Tag 顯示 + **ChevronDown 保留**(類型身份 indicator,fg-disabled),不可開選單
|
|
244
244
|
|
|
245
245
|
---
|
|
246
246
|
|
|
@@ -327,12 +327,12 @@ function ReadonlyDisplay({
|
|
|
327
327
|
)
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
-
// 2026-06-
|
|
331
|
-
//
|
|
332
|
-
//
|
|
330
|
+
// 2026-06-26 類型身份 indicator 規則:edit 顯示 / readonly 不顯示(純值、不可開下拉,箭頭會誤導) /
|
|
331
|
+
// disabled 保留(fg-disabled,對齊原生 <select disabled> 灰示箭頭 + Accordion M24 precedent)。
|
|
332
|
+
// naked cell 情境依 showDisplayEndIcon = isEditable,維持 2026-05-10 cell canonical「非可編欄不顯」。
|
|
333
333
|
// aria-disabled:styled-disabled(非 native disabled 元素)需明告 AT「inactive」,同時讓 axe 正確
|
|
334
334
|
// 豁免 disabled 文字的 color-contrast(WCAG 1.4.3 inactive UI 例外)。
|
|
335
|
-
const showIndicator = variant === 'naked' ? !!showDisplayEndIcon :
|
|
335
|
+
const showIndicator = variant === 'naked' ? !!showDisplayEndIcon : resolvedMode === 'disabled'
|
|
336
336
|
const ariaDisabled = resolvedMode === 'disabled' ? true : undefined
|
|
337
337
|
|
|
338
338
|
if (isTextDisplay) {
|
|
@@ -351,7 +351,7 @@ function ReadonlyDisplay({
|
|
|
351
351
|
const tagVariant = selectedOpt?.tagVariant as 'blue' | 'green' | 'red' | 'yellow' | 'neutral' | undefined
|
|
352
352
|
|
|
353
353
|
return (
|
|
354
|
-
<div className={cn(fieldWrapperStyles({ mode: resolvedMode, variant, size: sz }), value && tagPadding[sz], className)} data-field-mode={resolvedMode} aria-disabled={ariaDisabled}>
|
|
354
|
+
<div className={cn(fieldWrapperStyles({ mode: resolvedMode, variant, size: sz }), value && tagPadding[sz], className)} style={{ paddingRight: 'var(--field-px)' }} data-field-mode={resolvedMode} aria-disabled={ariaDisabled}>
|
|
355
355
|
{value ? <Tag size={sz} color={tagVariant}>{label}</Tag> : <span className={emptyColorCls}>{emptyText}</span>}
|
|
356
356
|
{showIndicator && <ItemSuffix className="pointer-events-none"><ChevronDown size={iconSize} className={cn('shrink-0', iconColor)} aria-hidden /></ItemSuffix>}
|
|
357
357
|
</div>
|
|
@@ -429,7 +429,7 @@ const NativeSelect = React.forwardRef<HTMLSelectElement, SelectProps>(
|
|
|
429
429
|
return (
|
|
430
430
|
<div className={cn(fieldWrapperStyles({ mode: 'edit', variant: variant, size }), value && tagPadding[size], 'relative',
|
|
431
431
|
error && ['border-error hover:border-error-hover', 'focus-within:border-error focus-within:hover:border-error'], className)}
|
|
432
|
-
style={{ paddingRight: '
|
|
432
|
+
style={{ paddingRight: 'var(--field-px)' }} data-field-mode="edit" data-error={error ? '' : undefined}>
|
|
433
433
|
{value ? <Tag size={size} color={nativeTagVariant} className="shrink-0 relative z-10 pointer-events-none">{label}</Tag> : <span className="text-fg-muted">{placeholder ?? '選擇...'}</span>}
|
|
434
434
|
{selectEl}
|
|
435
435
|
<span className="flex-1" />
|
|
@@ -599,7 +599,7 @@ const CustomSelect = React.forwardRef<HTMLDivElement, SelectProps>(
|
|
|
599
599
|
'cursor-pointer',
|
|
600
600
|
className,
|
|
601
601
|
)}
|
|
602
|
-
style={!isTextDisplay ? { paddingRight: '
|
|
602
|
+
style={!isTextDisplay ? { paddingRight: 'var(--field-px)' } : undefined}
|
|
603
603
|
data-field-mode="edit"
|
|
604
604
|
data-error={error ? '' : undefined}
|
|
605
605
|
onKeyDown={(e) => {
|
|
@@ -806,7 +806,7 @@ export const Accessibility = {
|
|
|
806
806
|
render: () => (
|
|
807
807
|
<div className="max-w-3xl text-body text-fg-secondary">
|
|
808
808
|
<h3 className="text-h5 text-foreground mb-2">無障礙設計</h3>
|
|
809
|
-
<p className="whitespace-pre-line">{"詳 `select-menu.spec.md` 「A11y 預設」段。摘要:\n\n ARIA / Pattern :基於 cmdk library a11y(combobox / listbox / option role + aria-activedescendant)。詳 [cmdk a11y](https://cmdk.paco.me/#accessibility)。\n\n Keyboard 行為 :\n\n- Tab — focus trigger\n- Enter / Space — 開啟 menu(trigger 由 consumer 經 PopoverTrigger asChild 提供:Select / Combobox 的 trigger 是 role=\"combobox\" 容器自綁 Enter / Space handler;若 consumer 用 DS Button 則由 native click 觸發)\n- ↑/↓ — 導覽 options(menu 開啟後)\n- Enter — 選擇\n- 字母鍵 — type-ahead 過濾(search 模式)\n- Esc — 關閉\n\n Focus :menu 開啟時 active-descendant 虛擬焦點落在第一個 / 已選 option(aria-activedescendant 高亮,非 DOM focus);searchable 時 DOM focus 給搜尋 input
|
|
809
|
+
<p className="whitespace-pre-line">{"詳 `select-menu.spec.md` 「A11y 預設」段。摘要:\n\n ARIA / Pattern :基於 cmdk library a11y(combobox / listbox / option role + aria-activedescendant)。詳 [cmdk a11y](https://cmdk.paco.me/#accessibility)。\n\n Keyboard 行為 :\n\n- Tab — focus trigger\n- Enter / Space — 開啟 menu(trigger 由 consumer 經 PopoverTrigger asChild 提供:Select / Combobox 的 trigger 是 role=\"combobox\" 容器自綁 Enter / Space handler;若 consumer 用 DS Button 則由 native click 觸發)\n- ↑/↓ — 導覽 options(menu 開啟後)\n- Enter — 選擇\n- 字母鍵 — type-ahead 過濾(search 模式)\n- Esc — 關閉\n\n Focus :menu 開啟時 active-descendant 虛擬焦點落在第一個 / 已選 option(aria-activedescendant 高亮,非 DOM focus);searchable 時 DOM focus 給搜尋 input;非 searchable 時 DOM focus 移到 cmdk 的 [cmdk-root](Command 元素,見 handleNonSearchableAutoFocus),讓 cmdk 內建方向鍵 / Enter / Home / End 導覽生效;option 無 tabIndex,DOM focus 不落在 option 上。關閉時 focus 回 trigger。\n\n 驗證 :Storybook a11y addon panel 應 0 critical violation;鍵盤完整可操作(無需滑鼠)。WCAG AA contrast ≥ 4.5:1(text)/ 3:1(UI)。"}</p>
|
|
810
810
|
</div>
|
|
811
811
|
),
|
|
812
812
|
}
|
|
@@ -74,7 +74,7 @@ benchmark:
|
|
|
74
74
|
|
|
75
75
|
```
|
|
76
76
|
SidebarProvider ← 全域 context(open 狀態、cookie、快捷鍵)
|
|
77
|
-
Sidebar ← 主容器(
|
|
77
|
+
Sidebar ← 主容器(collapsible + side + viewportInsetTop)
|
|
78
78
|
SidebarHeader ← 頂部:logo、workspace switcher
|
|
79
79
|
SidebarContent ← 中間可捲動區
|
|
80
80
|
SidebarGroup ← 分組容器(可多個,純視覺分段)
|
|
@@ -83,7 +83,7 @@ SidebarProvider ← 全域 context(open 狀態、cookie、快捷
|
|
|
83
83
|
│
|
|
84
84
|
├─ SidebarMenu 扁平選單(1 層、有限可列舉)
|
|
85
85
|
│ SidebarMenuItem
|
|
86
|
-
│ SidebarMenuButton ←
|
|
86
|
+
│ SidebarMenuButton ← icon 模式下 consumer 應確保每個 item 有 icon(否則 collapsed rail 出現空槽;非元件強制)
|
|
87
87
|
│ SidebarMenuBadge ← 右側 badge
|
|
88
88
|
│ SidebarMenuAction ← hover 浮出的 action
|
|
89
89
|
│
|
|
@@ -43,7 +43,7 @@ export const Overview: Story = {
|
|
|
43
43
|
['min / max', 'number', '0 / 100', '值範圍'],
|
|
44
44
|
['step', 'number', '1', '步進'],
|
|
45
45
|
['size', "'sm' | 'md' | 'lg'", "'md'", '**只影響容器外高**,不影響 track/thumb 尺寸'],
|
|
46
|
-
['disabled', 'boolean', 'false', '灰階降級(range/thumb border→border,thumb bg
|
|
46
|
+
['disabled', 'boolean', 'false', '灰階降級(range/thumb border→border,thumb bg 沉回 bg-canvas(不透明背景色))'],
|
|
47
47
|
['minStepsBetweenThumbs', 'number', '—', 'range mode 兩 thumb 最小距離'],
|
|
48
48
|
].map(([p, t, d, desc]) => (
|
|
49
49
|
<tr key={p}><Td mono>{p}</Td><Td mono>{t}</Td><Td mono>{d}</Td><Td>{desc}</Td></tr>
|
|
@@ -80,7 +80,7 @@ Steps 是 `patterns/element-anatomy/item-anatomy.spec.md` 的 row primitive **co
|
|
|
80
80
|
- **字體 tier**:label `text-body` (sm/md) / `text-body-lg` (lg);description `text-caption` (sm/md) / `text-body` (lg)。跟 MenuItem / TreeItem 同一套。
|
|
81
81
|
- **字重**:`font-medium`(含 label,不隨 focus 變)
|
|
82
82
|
- **預設文字色**:`text-fg-secondary`;`value` 指向的 step(focused)為 `text-foreground`
|
|
83
|
-
- **Icon tier**:`
|
|
83
|
+
- **Icon tier**:`INDICATOR_ICON_SIZE = { sm: 0, md: 16, lg: 20 }`(sm 為純圓點無內部 icon,見 size table)
|
|
84
84
|
- **Description 永遠可選**,任何 size 都不強制
|
|
85
85
|
- **Hit area 地板**:可點擊的 indicator 至少 `field-height-xs`(24px),不足者用透明 padding 撐開
|
|
86
86
|
|
|
@@ -172,7 +172,7 @@ indicator 圓形 flex items-center 居中
|
|
|
172
172
|
|
|
173
173
|
| State | 視覺 | 觸發 |
|
|
174
174
|
|---|---|---|
|
|
175
|
-
| `upcoming` | 灰底(`
|
|
175
|
+
| `upcoming` | 灰底(inline `background: var(--muted)`)+ 灰字(`text-fg-disabled`)| 未走到 |
|
|
176
176
|
| `current` | 藍底(`bg-info`)+ 白字數字 | step === `value` 且不在 completedValues / errorValues |
|
|
177
177
|
| `completed` | 藍底(`bg-info`)+ 白色 ✓ | step 在 `completedValues` 內 |
|
|
178
178
|
| `reachable` | 藍底(`bg-info`)+ 白字數字(僅 linear;sm 為空心環)| linear 下「下一個未完成」step(非 current / completed / error),可點 |
|
|
@@ -57,7 +57,7 @@ export const UsageGuidance: Story = {
|
|
|
57
57
|
<LinkTo kind="Design System/Components/Tabs/展示" name="溢出處理 — 水平捲動"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">溢出處理 — 水平捲動</span></LinkTo>
|
|
58
58
|
</li>
|
|
59
59
|
<li>
|
|
60
|
-
<LinkTo kind="Design System/Components/Tabs/展示" name="溢出處理 — ⌄ 導覽選單"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">溢出處理 —
|
|
60
|
+
<LinkTo kind="Design System/Components/Tabs/展示" name="溢出處理 — ⌄ 導覽選單"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">溢出處理 — ⌄ 導覽選單</span></LinkTo>
|
|
61
61
|
</li>
|
|
62
62
|
</ul>
|
|
63
63
|
<p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見下方「vs 近親」)。</p>
|
|
@@ -207,7 +207,7 @@ TabsList 底部有 1px gray border(`border-divider`,neutral-4),selected
|
|
|
207
207
|
|
|
208
208
|
Tabs 常與容器 header 的底邊 border 合併——**視覺上只有一條線**,不是 header border + tabs border 疊兩條。
|
|
209
209
|
|
|
210
|
-
**做法**:Tabs 的 `TabsList` 底部 border 與 header 的 `border-b` 實際上是**同一條線**(設計上重疊、實作上不重複渲染)。**Header 退讓**(移除自己 `border-b`),**Tabs 接管**(自身 `border-b border-divider` per `TABS_LIST_BASE`,2026-05-18 fd843c25 統一 chrome separator 色)。三種 overflow mode(none
|
|
210
|
+
**做法**:Tabs 的 `TabsList` 底部 border 與 header 的 `border-b` 實際上是**同一條線**(設計上重疊、實作上不重複渲染)。**Header 退讓**(移除自己 `border-b`),**Tabs 接管**(自身 `border-b border-divider` per `TABS_LIST_BASE`,2026-05-18 fd843c25 統一 chrome separator 色)。三種 overflow mode 的 TabsList 一律跨滿父容器(none 模式 `w-full`;scroll / menu 模式 inner list `min-w-full` 保留溢出成長,見 L184;`TABS_LIST_BASE` + `overflow-y-hidden` 阻 y auto-promote,2026-05-19 c359c711 border owner 升 list 內部);chrome header 內的全寬 paint 由 `ChromeHeader` tabsSlot wrapper 以 `[&>[role=tablist]]:w-full` 強制(見 `header-canonical.spec.md`)。
|
|
211
211
|
|
|
212
212
|
**世界級對照(verbatim cite)**:
|
|
213
213
|
- **GitHub Primer PageHeader**:「`hasBorder` defaults true,**but border NOT rendered if Navigation child contains UnderlineNav**;UnderlineNav itself provides bottom border」(`primer.style/components/page-header/react`)
|
|
@@ -19,7 +19,7 @@ import { EMPTY_DISPLAY } from '@/design-system/components/Field/field-wrapper'
|
|
|
19
19
|
*
|
|
20
20
|
* ── Padding 規則 ───────────────────────────────────────────────────────
|
|
21
21
|
* 多行內容必須有上下內距才能閱讀舒適。不沿用 Input 的 items-center,
|
|
22
|
-
* 改用 py-2(8px)固定上下內距 + px-
|
|
22
|
+
* 改用 py-2(8px)固定上下內距 + px-[var(--field-px)](12px token,左右內距 SSOT,與 Input/Field family 一致)。
|
|
23
23
|
*
|
|
24
24
|
* ── Size ────────────────────────────────────────────────────────────────
|
|
25
25
|
* sm / md → text-body(14px)
|
|
@@ -40,7 +40,7 @@ const textareaVariants = cva(
|
|
|
40
40
|
// K10 fix(2026-05-04):disabled 時 placeholder + text 切 fg-disabled(parallel 到 bareInputStyles)
|
|
41
41
|
// Textarea 自身 `<textarea disabled>` 帶 disabled HTML attribute,用 `disabled:` variant 直接命中
|
|
42
42
|
'disabled:placeholder:text-fg-disabled disabled:text-fg-disabled',
|
|
43
|
-
'px-
|
|
43
|
+
'px-[var(--field-px)] py-2',
|
|
44
44
|
'transition-colors duration-150',
|
|
45
45
|
],
|
|
46
46
|
{
|
|
@@ -77,7 +77,7 @@ const textareaVariants = cva(
|
|
|
77
77
|
{
|
|
78
78
|
mode: 'display',
|
|
79
79
|
variant: 'default',
|
|
80
|
-
// 2026-05-13 Q3 Path Ⅰ:Textarea default display zero chrome,!px-0 !py-0 override base `px-
|
|
80
|
+
// 2026-05-13 Q3 Path Ⅰ:Textarea default display zero chrome,!px-0 !py-0 override base `px-[var(--field-px)] py-2`
|
|
81
81
|
// (跟 Input 同 SSOT,per field-controls.spec.md (d))
|
|
82
82
|
className: 'bg-transparent border border-transparent !px-0 !py-0',
|
|
83
83
|
},
|
|
@@ -152,7 +152,7 @@ Panel 展開後的 column picker 結構:
|
|
|
152
152
|
|
|
153
153
|
### Spacing + 結構(2026-04-21 canonical,2026-04-21 window width 修正)
|
|
154
154
|
|
|
155
|
-
- Footer 內 padding = `--layout-space-tight`(12px @ md density)
|
|
155
|
+
- Footer 內 padding = `--layout-space-tight`(12px @ md density);**消費 `<SurfaceFooter>` SSOT**(border-t + py + gap + justify + shrink-0),px 經 `className` override 回 tight——因滿欄 column 面板無 chrome-padded body 內縮邊可對齊(見 `overlay-surface.spec.md` SurfaceFooter「footer px = body 內縮原則」例外);此刻按鈕 `mr-auto` 推右,視覺等同原 justify-between(零視覺變化)
|
|
156
156
|
- **Panel 容器固定寬:2 欄(時 / 分)`w-40`(160px)/ 3 欄(時 / 分 / 秒)`w-60`(240px)**;**每欄 `flex-1 h-full` 等分**容器寬(非固定 `w-12`)。**分隔「:」移除**(AR8 canonical — Ant TimePicker / Google Calendar 同樣不加 `:`,靠 column 間距自明 `@benchmark-unverified` visual)
|
|
157
157
|
- Scrollable list 用 **`<ScrollArea>`**(對齊 DS 跨 OS 一致 overlay 捲軸 canonical);不 raw `overflow-y-auto`
|
|
158
158
|
- **Scroll-into-view**:mount = `behavior:'auto'`(避閃爍),後續 `value` 變更 = `behavior:'smooth'`(對齊 iOS / Material / Ant timepicker idiom)。SSOT 在 `time-columns.tsx` `TimeColumn` useEffect
|
|
@@ -14,6 +14,7 @@ import { ItemInlineAction, ItemSuffix } from '@/design-system/patterns/element-a
|
|
|
14
14
|
import { Popover, PopoverTrigger, PopoverContent } from '@/design-system/components/Popover/popover'
|
|
15
15
|
import { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid } from '@/design-system/components/Field/field-context'
|
|
16
16
|
import { Button } from '@/design-system/components/Button/button'
|
|
17
|
+
import { SurfaceFooter } from '@/design-system/patterns/overlay-surface/overlay-surface'
|
|
17
18
|
import {
|
|
18
19
|
TimeColumns,
|
|
19
20
|
isoToTimeParts,
|
|
@@ -279,8 +280,8 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
|
|
|
279
280
|
: <span className="text-fg-muted">{EMPTY_DISPLAY}</span>
|
|
280
281
|
}
|
|
281
282
|
</span>
|
|
282
|
-
{/* 2026-06-
|
|
283
|
-
{EndIconCmp && (variant === 'naked' ? showDisplayEndIcon :
|
|
283
|
+
{/* 2026-06-26 類型身份 indicator:edit 顯示 / readonly 不顯示 / disabled 保留(fg-disabled);naked cell 依 showDisplayEndIcon=isEditable(修 disabled cell 漏顯 bug)*/}
|
|
284
|
+
{EndIconCmp && (variant === 'naked' ? showDisplayEndIcon : resolvedMode === 'disabled') && (
|
|
284
285
|
<ItemSuffix className="pointer-events-none">
|
|
285
286
|
<EndIconCmp
|
|
286
287
|
size={iconSize}
|
|
@@ -389,15 +390,13 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
|
|
|
389
390
|
// 不會走到 document body 把 popover 內容推出畫面(user 報「hours 欄空白」根因)。
|
|
390
391
|
className="flex-1 min-h-0"
|
|
391
392
|
/>
|
|
392
|
-
{/* Footer
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
>
|
|
400
|
-
<Button variant="text" size="sm" onClick={handleNow}>
|
|
393
|
+
{/* Footer:消費 SurfaceFooter SSOT(border-t + py-tight + gap-2 + shrink-0 + justify-end)。
|
|
394
|
+
px override 回 layout-space-tight,因 TimePicker 滿欄 column 面板無 chrome-padded body
|
|
395
|
+
內縮邊可對齊(footer px = body 內縮 原則;column-selector 數字置中、零內距,
|
|
396
|
+
見 time-picker.spec.md)。此刻 mr-auto 把確定推右,視覺等同原 justify-between
|
|
397
|
+
—— 純結構消費 overlay-surface canonical(overlay-surface.spec.md:17「不自寫 padding token」),零視覺變化。 */}
|
|
398
|
+
<SurfaceFooter className="px-[var(--layout-space-tight)]">
|
|
399
|
+
<Button variant="text" size="sm" onClick={handleNow} className="mr-auto">
|
|
401
400
|
此刻
|
|
402
401
|
</Button>
|
|
403
402
|
<Button
|
|
@@ -407,7 +406,7 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
|
|
|
407
406
|
>
|
|
408
407
|
確定
|
|
409
408
|
</Button>
|
|
410
|
-
</
|
|
409
|
+
</SurfaceFooter>
|
|
411
410
|
</div>
|
|
412
411
|
</PopoverContent>
|
|
413
412
|
</Popover>
|
|
@@ -1057,7 +1057,7 @@ export const treeViewMeta = {
|
|
|
1057
1057
|
},
|
|
1058
1058
|
states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],
|
|
1059
1059
|
tokens: {
|
|
1060
|
-
bg: ['bg-neutral-hover', 'bg-
|
|
1060
|
+
bg: ['bg-neutral-hover', 'bg-neutral-selected', 'bg-surface'],
|
|
1061
1061
|
fg: ['text-fg-disabled', 'text-fg-muted', 'text-fg-secondary', 'text-foreground'],
|
|
1062
1062
|
ring: ['ring-ring'],
|
|
1063
1063
|
},
|
|
@@ -474,7 +474,7 @@ MenuItem 承載 Switch 時,**整列 row 的 click = toggle 這個 control**(不
|
|
|
474
474
|
| 內容物高度 | 對齊容器 | 對齊目標 |
|
|
475
475
|
|---|---|---|
|
|
476
476
|
| ≤ 24px | `h-[1lh]` | 第一行 label 的垂直中心 |
|
|
477
|
-
| > 24px(+ 有 description) | `h-[calc(1lh +
|
|
477
|
+
| > 24px(+ 有 description) | `h-[calc(1lh + var(--item-gap-label-desc-<mode>[-lg]) + desc-font-size*1.3)]` | label + gap + description 文字塊的垂直中心 |
|
|
478
478
|
| > 24px(無 description) | `h-[1lh]` | 強制 inline(沒有文字塊可對齊) |
|
|
479
479
|
|
|
480
480
|
### 為什麼 prefix 和 suffix 各自獨立(不互相同步)
|
|
@@ -161,7 +161,7 @@ const SCANNING_SPECS: Record<SizeKey, TypoSpec> = {
|
|
|
161
161
|
const READING_SPECS: Record<SizeKey, TypoSpec> = {
|
|
162
162
|
sm: { labelFont: 'text-body', labelSize: '14px', labelLh: '1.5', descFont: 'text-body', descSize: '14px', descLh: '1.5', iconPx: 16 },
|
|
163
163
|
md: { labelFont: 'text-body', labelSize: '14px', labelLh: '1.5', descFont: 'text-body', descSize: '14px', descLh: '1.5', iconPx: 16 },
|
|
164
|
-
lg: { labelFont: 'text-body-lg', labelSize: '16px', labelLh: '1.5', descFont: 'text-body
|
|
164
|
+
lg: { labelFont: 'text-body-lg', labelSize: '16px', labelLh: '1.5', descFont: 'text-body', descSize: '14px', descLh: '1.5', iconPx: 20 },
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
/* ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -177,6 +177,7 @@ list 不再是 body 唯一 region → **不該撤 body chrome padding**(撤了
|
|
|
177
177
|
- `border-t border-divider`
|
|
178
178
|
- `px-[var(--layout-space-loose)] py-[var(--layout-space-tight)]`
|
|
179
179
|
- `flex items-center justify-end gap-2 shrink-0`(右對齊按鈕列,不被壓縮)
|
|
180
|
+
- **Footer px = body 內容內縮原則**:預設 `layout-space-loose` 讓 footer 按鈕左緣對齊 SurfaceHeader title / SurfaceBody 內容左緣(整面板內容垂直對齊一條線,同 Body alignment rationale)。**例外**:body 為 full-bleed(滿欄 column selector / 無 chrome padding 的 unbounded list)、無內容左緣可對齊時,可經 `className` override 較緊 px(eg. TimePicker footer override `px-[var(--layout-space-tight)]` 對齊滿欄置中 columns、保窄面板平衡)。**注**:`--field-px` 是 form-field gutter 概念(uiSize),**不**用於 overlay footer——footer 屬 overlay chrome,padding 走 layoutSpace 家族。
|
|
180
181
|
|
|
181
182
|
---
|
|
182
183
|
|
|
@@ -59,7 +59,7 @@ benchmark:
|
|
|
59
59
|
取值依據:7px 命中區 / 1px line 非自創——來自下方「世界級對照細節」5 家共識(hit zone 7-8px fingertip-friendly / 1px line non-intrusive)+ DataTable v11 已 ship 的既有 canonical(本 primitive 抽取自它,M17)。
|
|
60
60
|
|
|
61
61
|
- **命中區**:7px 寬(horizontal)/ 高(vertical),`-3px` outward offset 跨 boundary 抓得到
|
|
62
|
-
- **Visual line**:1px,
|
|
62
|
+
- **Visual line**:1px,line offset 3px from the position edge(inline style `right: 3` / `bottom: 3`,非 Tailwind class — 見 tsx JIT-quirk note),default full-extent
|
|
63
63
|
- **idle**:`bg-divider`
|
|
64
64
|
- **disabled**:`bg-divider`(無 hover affordance)
|
|
65
65
|
- **hover**:`bg-[var(--border-hover)]`(via `group/resize` selector)
|
|
@@ -197,7 +197,7 @@
|
|
|
197
197
|
|
|
198
198
|
/* Brand / Action — 品牌主操作色
|
|
199
199
|
--primary-text 為 SOP 規定的 5 件套之一,目前無消費者
|
|
200
|
-
(Button 直接用 text-
|
|
200
|
+
(Button 直接用 text-on-emphasis, Tag 用 primitive --color-blue-7) */
|
|
201
201
|
--primary: var(--color-blue-6);
|
|
202
202
|
--primary-hover: var(--color-blue-5);
|
|
203
203
|
--primary-active: var(--color-blue-7);
|
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
--field-height-md: 2rem; /* 32px */
|
|
25
25
|
--field-height-lg: 2.25rem; /* 36px */
|
|
26
26
|
|
|
27
|
+
/* Field horizontal padding — 固定 12px(不隨 size/density);form-context field 左右內距 SSOT。
|
|
28
|
+
跟 --table-cell-px(cell-context)同 -px 慣例;Input/Select/Combobox/DatePicker/TimePicker/Textarea
|
|
29
|
+
+ tag 模式右緣 re-assert 全消費此 token(取代散落的 px-3 / inline 0.75rem,M17 SSOT 可傳播)。 */
|
|
30
|
+
--field-px: 0.75rem; /* 12px */
|
|
31
|
+
|
|
27
32
|
/* Table Row Height — DataTable */
|
|
28
33
|
--table-row-sm: 2rem; /* 32px */
|
|
29
34
|
--table-row-md: 2.5rem; /* 40px */
|
|
@@ -94,6 +94,20 @@ Consumer 寫 Form 或 Toolbar 時並排多個 field-height 元件:
|
|
|
94
94
|
|
|
95
95
|
本專案曾發生 SegmentedControl 的 code defaults 是 `md`、spec + docblock 寫 `sm ★default` 的三方不一致(2026-04-18 修正)。避免方式:改 cva `defaultVariants` 前先讀本表,確認新值仍符合 family 約束。
|
|
96
96
|
|
|
97
|
+
## Field Horizontal Padding
|
|
98
|
+
|
|
99
|
+
Form-context field 控件的左右水平內距。**固定 12px,不隨 size / density 變化**——縱向才是 density 節奏;橫向是內容溝槽常數,對齊 field-height family 全控件。
|
|
100
|
+
|
|
101
|
+
| Token | 值 | 說明 |
|
|
102
|
+
|-------|-----|------|
|
|
103
|
+
| `--field-px` | 0.75rem (12px) | form-context field 左右內距 SSOT;固定不隨 size / density |
|
|
104
|
+
|
|
105
|
+
**消費者**:`Input` / `NumberInput` / `Select` / `Combobox` / `DatePicker` / `TimePicker` / `LinkInput` / `Textarea`(經 `fieldWrapperStyles` cva `px-[var(--field-px)]`)+ `PeoplePicker`(form-context inject `!px-[var(--field-px)]`)+ tag 模式右緣 re-assert(`paddingRight: var(--field-px)`,Select / Combobox readonly + edit)。
|
|
106
|
+
|
|
107
|
+
**與 `--table-cell-px` 的關係**:同 `-px` 命名慣例。`--table-cell-px`(DataTable-scoped)預設 `var(--field-px)`(form / cell 同 12px content gutter SSOT),但仍是獨立 named token、可被 DataTable 單獨 override(per `components/Field/field-controls.spec.md` contract (c) scoped 決策)。
|
|
108
|
+
|
|
109
|
+
**Why 固定不隨 density**:density 本質是縱向密度(一屏幾列);橫向管可讀性(字離格線距離),目的不同不綁。對齊 Ant Table(橫向 padding 不隨 density 變、縱橫分離)+ 全 DS field 控件 12px 常數。M17「SSOT 必可傳播」:散落的 `px-3` / inline `0.75rem` 全收斂進此 token。
|
|
110
|
+
|
|
97
111
|
## Table Row
|
|
98
112
|
|
|
99
113
|
DataTable 行高。density 切換統一 +0.5rem (+8px)。
|
|
@@ -152,9 +166,9 @@ DataTable 行高。density 切換統一 +0.5rem (+8px)。
|
|
|
152
166
|
| Avatar 內 icon | `components/Avatar/avatar.spec.md:165` | `round_even(size × 0.6)` formula | Material / Apple HIG |
|
|
153
167
|
| Empty illustration | `components/Empty/empty.tsx:48` | Avatar 48 wrap → icon 28(Avatar formula derived)| Empty-state canonical |
|
|
154
168
|
| FileViewer thumb | `components/FileViewer/file-viewer.tsx:621` | thumb 64 → icon 20(file-type indicator hardcode 無公式)| Thumbnail UI 慣例 |
|
|
155
|
-
| CircularProgress | `components/CircularProgress/circular-progress.tsx:
|
|
156
|
-
| Steps indicator icon | `components/Steps/steps.tsx:
|
|
157
|
-
| Checkbox/Switch check | `components/Checkbox/checkbox.tsx:
|
|
169
|
+
| CircularProgress | `components/CircularProgress/circular-progress.tsx:86` | `strokeWidth = max(2, round(size/10))` stroke ring 厚度非 icon | Geometric scaling |
|
|
170
|
+
| Steps indicator icon | `components/Steps/steps.tsx:25` | `INDICATOR_ICON_SIZE {sm:0, md:16, lg:20}`(sm 因圓圈 8px 太小)| Indicator-internal |
|
|
171
|
+
| Checkbox/Switch check | `components/Checkbox/checkbox.tsx:52` + `components/Switch/switch.tsx:83` | `{sm:12, md:12, lg:16}` form-control internal + stroke 下限 12 | iOS HIG / Material 3 / Polaris | <!-- @benchmark-unverified -->
|
|
158
172
|
|
|
159
173
|
**程式化 SSOT**:`patterns/element-anatomy/item-anatomy.tsx:66` `ICON_SIZE = {sm:16, md:16, lg:20}` 是本 tier 的 type-safe const。**Form control 透過 `tokens/uiSize/icon-size.ts` re-export entry import**(避 components→patterns 反向 dependency)。
|
|
160
174
|
|