@comitor/ui 1.7.0 → 1.7.1
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 +23 -14
- package/dist/components/ui/radio-group.js +1 -1
- package/dist/components/ui/radio-group.js.map +1 -1
- package/package.json +1 -1
- package/styles.css +38 -7
package/README.md
CHANGED
|
@@ -8,8 +8,8 @@ component ghép sẵn và khung ứng dụng (shell) kiểu workspace × app.
|
|
|
8
8
|
- Token brand nằm ở **`styles.css`** — đổi token, bump version, mọi app nhận cùng một thay đổi.
|
|
9
9
|
- Server-component-friendly: `"use client"` chỉ nằm ở file thật sự cần.
|
|
10
10
|
|
|
11
|
-
> Trạng thái: `1.
|
|
12
|
-
> (Tier 3) · 4 file chart.
|
|
11
|
+
> Trạng thái: `1.7.1` — 52 file primitive (Tier 1) · 31 file composite (Tier 2) · 25 file shell
|
|
12
|
+
> (Tier 3) · 4 file chart · 5 file uploader. API chốt từ `1.0.0`.
|
|
13
13
|
>
|
|
14
14
|
> Tài liệu đầy đủ: **https://design-system.comitor.ai** —
|
|
15
15
|
> [Get started](https://design-system.comitor.ai/get-started) ·
|
|
@@ -49,6 +49,8 @@ pnpm add @uppy/core @uppy/xhr-upload # cho @comitor/ui/uploader
|
|
|
49
49
|
| `next-themes` | `>=0.4` | — | `/shell` (context singleton — app và package phải dùng **cùng một bản**) |
|
|
50
50
|
| `recharts` | `>=2.15.0` | — | `/chart` (kiểu đã dựng cho **cả 2.x lẫn 3.x**, xem ghi chú dưới) |
|
|
51
51
|
| `react-hook-form` | `^7.54.0` | — | `/form` (context singleton) |
|
|
52
|
+
| `@uppy/core` | `^6.0.0` | — | `/uploader` |
|
|
53
|
+
| `@uppy/xhr-upload` | `^6.0.0` | — | `/uploader` |
|
|
52
54
|
|
|
53
55
|
> **Vì sao `recharts` và `lucide-react` là `>=` chứ không phải `^`.** `^2.15.0` loại thẳng recharts
|
|
54
56
|
> 3 — mà 3.x chạy tốt với gói này: kiểu của tầng `/chart` không dẫn xuất `payload`/`label` từ
|
|
@@ -103,7 +105,7 @@ buộc ở thẻ `<html>`: https://design-system.comitor.ai/shell
|
|
|
103
105
|
|
|
104
106
|
## 3. Entry point
|
|
105
107
|
|
|
106
|
-
Package có **
|
|
108
|
+
Package có **6 entry**, tách theo peer optional mà mỗi tầng kéo theo:
|
|
107
109
|
|
|
108
110
|
| Entry | Nội dung | Peer optional kéo theo |
|
|
109
111
|
|---|---|---|
|
|
@@ -114,8 +116,10 @@ Package có **5 entry**, tách theo peer optional mà mỗi tầng kéo theo:
|
|
|
114
116
|
| `@comitor/ui/form` | Binding shadcn ⇄ react-hook-form | `react-hook-form` |
|
|
115
117
|
| `@comitor/ui/tokens` | Token dạng object TS — cho nơi không có Tailwind: canvas, email HTML, PDF | không có |
|
|
116
118
|
|
|
117
|
-
⚠ **
|
|
118
|
-
entry — trùng xuyên entry thì im lặng hoàn toàn.
|
|
119
|
+
⚠ **Một số tên xuất hiện ở nhiều hơn một entry**, và `export *` chỉ báo đỏ khi trùng trong **cùng
|
|
120
|
+
một** entry — trùng xuyên entry thì im lặng hoàn toàn. (Đếm chính xác bao nhiêu tên thì tuỳ cách
|
|
121
|
+
đếm — "cùng ký tự" hay "cùng binding sau khi giải alias" cho hai kết quả khác nhau — nên đừng ghim
|
|
122
|
+
một con số ở đây. Bất biến cần giữ: cùng tên ⇒ **cùng một binding**.) Đúng **2** trong số đó là hai cài đặt khác nhau
|
|
119
123
|
(`FormField` / `FormFieldProps`: ở Tier 2 là bố cục form 12 cột, ở `/form` là wrapper của
|
|
120
124
|
`Controller`); 18 tên còn lại trỏ về cùng một binding nên lấy ở đâu cũng như nhau. Bảng đầy đủ, kèm
|
|
121
125
|
hai ca **không** trùng tên hay bị tưởng nhầm (`Toaster` ⇄ `SonnerToaster`, và `useIsMac` chỉ xuất ở
|
|
@@ -155,7 +159,7 @@ https://design-system.comitor.ai/foundations/server-client
|
|
|
155
159
|
|
|
156
160
|
## 5. Bảng component
|
|
157
161
|
|
|
158
|
-
Số file nguồn hiện tại: **52 primitive · 31 composite · 25 shell · 4 chart**. Mỗi component có trang
|
|
162
|
+
Số file nguồn hiện tại: **52 primitive · 31 composite · 25 shell · 4 chart · 5 uploader**. Mỗi component có trang
|
|
159
163
|
riêng kèm demo, bảng prop và cạm bẫy của nó:
|
|
160
164
|
|
|
161
165
|
| Tầng | Là gì | Trang |
|
|
@@ -219,21 +223,26 @@ https://design-system.comitor.ai/foundations/colors
|
|
|
219
223
|
## 7. Kiểm định trước mỗi lần phát hành
|
|
220
224
|
|
|
221
225
|
Mỗi bản publish đi qua cùng một chuỗi cổng (`prepublishOnly` nối chúng lại): typecheck → build →
|
|
222
|
-
`verify:directives` → `smoke:entries` → `check:exports` → `test:nav-active` → `test:
|
|
223
|
-
`lint:a11y`. Những cổng đáng để người dùng gói biết là chúng tồn tại:
|
|
226
|
+
`verify:directives` → `smoke:entries` → `check:exports` → `test:nav-active` → `test:grid-contract`
|
|
227
|
+
→ `test:a11y-gate` → `lint:a11y`. Những cổng đáng để người dùng gói biết là chúng tồn tại:
|
|
224
228
|
|
|
225
229
|
- **`lint:a11y` — hợp đồng màu.** Quét **toàn bộ** `src/**/*.{ts,tsx}` theo QUY TẮC (không theo danh
|
|
226
230
|
sách phát hiện) và đối chiếu với `styles.css`: token TÔ NỀN đem làm màu chữ/icon/viền là sai
|
|
227
231
|
*vai trò*, và mọi cặp chữ/nền phải đạt 4,5:1 (chữ) hoặc 3:1 (thành phần phi văn bản) ở **cả**
|
|
228
|
-
light lẫn dark.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
232
|
+
light lẫn dark. **Bảng màu là một TRỤC** (từ `1.6.0`): cổng đo cả bốn tổ hợp bảng × theme, nhưng
|
|
233
|
+
chỉ **CHẶN** ở bảng tương phản cao. Bảng mặc định vào một sổ riêng, IN RA mỗi lần chạy nhưng
|
|
234
|
+
không làm đỏ — nó là bản duyệt và đã được chấp nhận với đúng những con số dưới ngưỡng ở §6; bắt
|
|
235
|
+
nó là bắt một quyết định, và một cổng lúc nào cũng đỏ thì lần sau không ai chạy nữa.
|
|
236
|
+
⚠ **Cổng xanh vì thế KHÔNG có nghĩa bảng mặc định đạt** — đọc khối "BẢNG MÀU MẶC ĐỊNH: N cặp dưới
|
|
237
|
+
ngưỡng" mà nó in ra (hôm nay N = 384, trong đó 78% nằm ở ba token: `text-muted-foreground`,
|
|
238
|
+
`text-destructive-ink`, `border-input`). Trước `1.6.0` cổng còn không ĐO bảng mặc định lần nào,
|
|
239
|
+
tức nó in ✓ cho một bảng nó chưa từng nhìn.
|
|
240
|
+
Ở bảng tương phản cao thì mọi loại phát hiện đều `exit 1`, kể cả lớp dùng bảng màu mặc định của
|
|
241
|
+
Tailwind và miễn trừ đã thừa; miễn trừ mỗi mục bắt buộc kèm lý do bằng chữ.
|
|
233
242
|
- **`verify:directives` — ranh giới server/client.** File nguồn nào mở đầu bằng `"use client"` thì
|
|
234
243
|
file `dist/` tương ứng phải mở đầu y hệt (thiếu file build cũng tính là hỏng), và không hằng dữ
|
|
235
244
|
liệu thuần nào được kẹt trong file client (§4.2).
|
|
236
|
-
- **`smoke:entries`** nạp cả
|
|
245
|
+
- **`smoke:entries`** nạp cả 6 entry bằng Node ESM thuần, qua đúng `exports` map (§3).
|
|
237
246
|
- **`test:a11y-gate`** tự kiểm chính cổng màu (cổng còn bắt đủ không); **`test:nav-active`** khoá
|
|
238
247
|
toàn bộ luật active matching của sidebar dưới dạng hàm thuần (§4.1).
|
|
239
248
|
|
|
@@ -21,7 +21,7 @@ function RadioGroupItem({ className, ...props }) {
|
|
|
21
21
|
{
|
|
22
22
|
"data-slot": "radio-group-indicator",
|
|
23
23
|
className: "relative flex items-center justify-center",
|
|
24
|
-
children: /* @__PURE__ */ jsx(CircleIcon, { className: "fill-control-on absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" })
|
|
24
|
+
children: /* @__PURE__ */ jsx(CircleIcon, { className: "fill-control-on stroke-control-on absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" })
|
|
25
25
|
}
|
|
26
26
|
)
|
|
27
27
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/ui/radio-group.tsx"],"sourcesContent":["\"use client\";\n\nimport * as RadioGroupPrimitive from \"@radix-ui/react-radio-group\";\nimport { CircleIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/*\n * ─ CHẤM ĐÃ CHỌN: `--control-on`, KHÔNG PHẢI `--primary` ─\n *\n * Ô radio để nền trong suốt, nên \"đã chọn hay chưa\" chỉ còn được kể bằng cái CHẤM ở giữa —\n * thông tin thị giác đúng nghĩa của WCAG 1.4.11, ngưỡng 3:1. `fill-primary` (gold #E8B824)\n * trên nền trắng chỉ **1,86:1** ✗: ở light người dùng nhìn ô đã chọn gần như y hệt ô chưa chọn.\n * (Ở dark thì lọt lưới nên lỗi này chỉ lộ ở một nửa số theme.)\n * `--control-on` là token của vai trò \"control ở trạng thái BẬT/ĐÃ CHỌN\"; giá trị của nó đổi theo\n * bảng màu (styles.css khối 5b), nên ĐỪNG viết thẳng `fill-primary` / `bg-gold-300` ở đây.\n * Đo với nền TRANG, lấy ca tệ nhất trong 5 nền (`--muted`) và ca thường gặp (`--background`):\n *\n * bảng MẶC ĐỊNH chấm ⇄ nền trang : 1,73–1,86:1 ✗ (bản duyệt, đã chấp nhận)\n * bảng TƯƠNG PHẢN CAO chấm ⇄ nền trang : 6,09–6,51:1 (light) · 10,80–12,67:1 (dark) ✓\n *\n * ─ LỚP TÔ Ở DARK: `dark:bg-input-fill` ─\n * comitor-ds viết `dark:bg-input/30`, và ở bảng mặc định lớp tô đó là thứ DUY NHẤT còn khoanh\n * vùng ô radio: viền `--input` (#1E2128) trên `--card` (#16181D) chỉ 1,10:1. Nhưng viết thẳng\n * `bg-input/30` thì ở bảng tương phản cao (`--input` = #7E8290) lớp tô sáng hơn mọi nền trang và\n * kéo `--muted-foreground` xuống 3,81 · 3,54 · 3,22:1 ✗ — nên nó đi qua `--input-fill`, biến này\n * trong suốt ở bảng tương phản cao (bên đó viền tự đủ đậm: 4,94:1).\n *\n *
|
|
1
|
+
{"version":3,"sources":["../../../src/components/ui/radio-group.tsx"],"sourcesContent":["\"use client\";\n\nimport * as RadioGroupPrimitive from \"@radix-ui/react-radio-group\";\nimport { CircleIcon } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"../../lib/cn.js\";\n\n/*\n * ─ CHẤM ĐÃ CHỌN: `--control-on`, KHÔNG PHẢI `--primary` ─\n *\n * Ô radio để nền trong suốt, nên \"đã chọn hay chưa\" chỉ còn được kể bằng cái CHẤM ở giữa —\n * thông tin thị giác đúng nghĩa của WCAG 1.4.11, ngưỡng 3:1. `fill-primary` (gold #E8B824)\n * trên nền trắng chỉ **1,86:1** ✗: ở light người dùng nhìn ô đã chọn gần như y hệt ô chưa chọn.\n * (Ở dark thì lọt lưới nên lỗi này chỉ lộ ở một nửa số theme.)\n * `--control-on` là token của vai trò \"control ở trạng thái BẬT/ĐÃ CHỌN\"; giá trị của nó đổi theo\n * bảng màu (styles.css khối 5b), nên ĐỪNG viết thẳng `fill-primary` / `bg-gold-300` ở đây.\n * Đo với nền TRANG, lấy ca tệ nhất trong 5 nền (`--muted`) và ca thường gặp (`--background`):\n *\n * bảng MẶC ĐỊNH chấm ⇄ nền trang : 1,73–1,86:1 ✗ (bản duyệt, đã chấp nhận)\n * bảng TƯƠNG PHẢN CAO chấm ⇄ nền trang : 6,09–6,51:1 (light) · 10,80–12,67:1 (dark) ✓\n *\n * ─ LỚP TÔ Ở DARK: `dark:bg-input-fill` ─\n * comitor-ds viết `dark:bg-input/30`, và ở bảng mặc định lớp tô đó là thứ DUY NHẤT còn khoanh\n * vùng ô radio: viền `--input` (#1E2128) trên `--card` (#16181D) chỉ 1,10:1. Nhưng viết thẳng\n * `bg-input/30` thì ở bảng tương phản cao (`--input` = #7E8290) lớp tô sáng hơn mọi nền trang và\n * kéo `--muted-foreground` xuống 3,81 · 3,54 · 3,22:1 ✗ — nên nó đi qua `--input-fill`, biến này\n * trong suốt ở bảng tương phản cao (bên đó viền tự đủ đậm: 4,94:1).\n *\n * ─ CHẤM PHẢI KHAI CẢ `fill-` LẪN `stroke-`, KHÔNG BAO GIỜ CHỈ MỘT ─\n *\n * `<CircleIcon>` là icon lucide, và lucide mặc định `stroke=\"currentColor\"` + `strokeWidth={2}`.\n * Khai mỗi `fill-control-on` là chấm được TÔ gold rồi VIỀN bằng màu chữ kế thừa — hai màu trên\n * một hình 8px.\n *\n * ⚠ Đây là một hồi quy đã xảy ra thật, và nó chui lọt vì lập luận gỡ class chỉ xét MỘT tác dụng.\n * `0.5.0` (765f6e8) gỡ `text-control-on` khỏi Item với lý do \"một class không ai dùng\": Item\n * render Indicator cứng nên không có icon nào của app đi vào, và `text-*` làm cổng tương phản đọc\n * chấm là CHỮ (4,5:1) trong khi nó là HÌNH (3:1). Cả hai vế đều ĐÚNG. Nhưng `text-control-on`\n * còn một tác dụng thứ hai không ai nêu: nó đặt `currentColor` = gold, tức nó chính là thứ đang\n * tô `stroke` mặc định của lucide. Gỡ nó đi thì `currentColor` rơi về `--foreground`.\n *\n * Đo trên app thật (`/tasks/new`, radio \"Loại yêu cầu\", trước khi sửa):\n * fill rgb(232,184,36) #E8B824 ✓ đúng token\n * stroke rgb(14,17,22) #0E1116 ✗ = --foreground, dày 0,67px quanh chấm 8px\n *\n * Nay `stroke-control-on` khai THẲNG, không mượn `currentColor` — vừa trả lại hình đã duyệt, vừa\n * bỏ được sợi dây vô hình giữa một class `text-*` ở Item và màu viền của một icon ở Indicator.\n * ⚠ ĐỪNG \"gọn hơn\" bằng `stroke-none`: nét vẽ nằm giữa đường tròn `r=10` trong viewBox 24, nên\n * bỏ nó là chấm co từ bán kính 11 xuống 10 đơn vị — nhỏ đi 10% đường kính (7,33px → 6,67px) so\n * với bản đã duyệt. Hai class cùng token là đúng ý đồ, không phải thừa.\n * Viền lỗi dùng `--destructive-ink`: ở bảng mặc định nó bằng `--destructive` (đúng `border-red`\n * của comitor-ds), ở bảng tương phản cao nó là bậc tối riêng (7,15:1 / 6,84:1) — xem hợp đồng\n * màu §1: bậc đi lên nền TRANG luôn là `-ink`.\n */\nexport type RadioGroupProps = React.ComponentProps<typeof RadioGroupPrimitive.Root>;\nexport type RadioGroupItemProps = React.ComponentProps<typeof RadioGroupPrimitive.Item>;\n\nfunction RadioGroup({ className, ...props }: RadioGroupProps) {\n return <RadioGroupPrimitive.Root data-slot=\"radio-group\" className={cn(\"grid gap-3\", className)} {...props} />;\n}\n\nfunction RadioGroupItem({ className, ...props }: RadioGroupItemProps) {\n return (\n <RadioGroupPrimitive.Item\n data-slot=\"radio-group-item\"\n className={cn(\n \"border-choice-edge focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive-ink dark:bg-input-fill aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-colors duration-150 ease-out outline-none disabled:cursor-not-allowed disabled:opacity-50\",\n className\n )}\n {...props}\n >\n <RadioGroupPrimitive.Indicator\n data-slot=\"radio-group-indicator\"\n className=\"relative flex items-center justify-center\"\n >\n <CircleIcon className=\"fill-control-on stroke-control-on absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2\" />\n </RadioGroupPrimitive.Indicator>\n </RadioGroupPrimitive.Item>\n );\n}\n\nexport { RadioGroup, RadioGroupItem };\n"],"mappings":";AA0DS;AAxDT,YAAY,yBAAyB;AACrC,SAAS,kBAAkB;AAE3B,SAAS,UAAU;AAoDnB,SAAS,WAAW,EAAE,WAAW,GAAG,MAAM,GAAoB;AAC5D,SAAO,oBAAC,oBAAoB,MAApB,EAAyB,aAAU,eAAc,WAAW,GAAG,cAAc,SAAS,GAAI,GAAG,OAAO;AAC9G;AAEA,SAAS,eAAe,EAAE,WAAW,GAAG,MAAM,GAAwB;AACpE,SACE;AAAA,IAAC,oBAAoB;AAAA,IAApB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,QAAC,oBAAoB;AAAA,QAApB;AAAA,UACC,aAAU;AAAA,UACV,WAAU;AAAA,UAEV,8BAAC,cAAW,WAAU,wGAAuG;AAAA;AAAA,MAC/H;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
package/package.json
CHANGED
package/styles.css
CHANGED
|
@@ -935,15 +935,46 @@
|
|
|
935
935
|
*
|
|
936
936
|
* ── VÌ SAO Ở ĐÂY, KHÔNG PHẢI RẢI VÀO TỪNG COMPONENT ─────────────────────────────────────
|
|
937
937
|
* Vì đây là hành vi của TẦNG NỀN, không phải của một component. Đã đếm: 18 file trong `src/`
|
|
938
|
-
* render
|
|
939
|
-
*
|
|
938
|
+
* render 36 `<button>` thô ngoài `<Button>`, và chỉ 4 file khai `cursor-pointer` — ba trong số
|
|
939
|
+
* đó nằm trên `label[for]` nên nay là thừa, cái thứ tư (`data-table.tsx`, dòng bấm được) thì
|
|
940
|
+
* luật này KHÔNG phủ vì `<tr>` không phải button. Rải tay là bỏ sót ngay ở component tiếp theo
|
|
941
|
+
* có người viết, và không cổng nào bắt được cái bỏ sót đó.
|
|
940
942
|
*
|
|
941
|
-
*
|
|
942
|
-
*
|
|
943
|
-
*
|
|
943
|
+
* ── HAI ĐIỀU KIỆN, VÀ CẢ HAI ÁP CHO CẢ HAI SELECTOR ─────────────────────────────────────
|
|
944
|
+
* Control bị vô hiệu KHÔNG được mời bấm. Cần `:not(:disabled)` VÀ `:not([aria-disabled])`:
|
|
945
|
+
* Radix (và react-day-picker) vô hiệu bằng `aria-disabled` ở những chỗ vẫn cần giữ focus, và
|
|
946
|
+
* ở đó thuộc tính `disabled` không có mặt.
|
|
947
|
+
*
|
|
948
|
+
* ⚠ `1.7.0` ghi đúng câu trên trong comment nhưng viết SAI xuống selector: mỗi selector chỉ
|
|
949
|
+
* mang MỘT guard (`button:not(:disabled)` + `[role="button"]:not([aria-disabled="true"])`).
|
|
950
|
+
* Nên `<button aria-disabled="true">` — đúng khuôn Radix mà hai guard sinh ra để xử lý — vẫn
|
|
951
|
+
* nhận bàn tay; chiều ngược lại `<button role="button" disabled>` cũng lọt qua nhánh hai.
|
|
952
|
+
* Ca thật đã đọc trong `react-day-picker@9` (`dist/esm/DayPicker.js`):
|
|
953
|
+
* · dòng 255 và 282 — mũi tên "tháng trước / tháng sau" khi KHÔNG còn tháng để đi:
|
|
954
|
+
* `aria-disabled: true` kèm `tabIndex: -1`, KHÔNG có thuộc tính `disabled`. Đây là ca xảy
|
|
955
|
+
* ra thường xuyên — mọi lịch có `startMonth`/`endMonth` hoặc khoảng giới hạn đều chạm nó.
|
|
956
|
+
* · dòng 320 — ô ngày: ngày bị khoá mà KHÔNG được focus dùng thuộc tính `disabled` thật,
|
|
957
|
+
* chỉ ngày bị khoá ĐANG focus mới dùng `aria-disabled`. Tức ô ngày dính hẹp hơn mũi tên.
|
|
958
|
+
* Bài học: một comment nói "hai điều kiện" không kiểm chứng được cái selector bên dưới nó.
|
|
959
|
+
*
|
|
960
|
+
* ── TÁM CA, ĐO TRÊN TRÌNH DUYỆT ─────────────────────────────────────────────────────────
|
|
961
|
+
* Đo lại bằng `getComputedStyle(el).cursor` sau khi sửa luật này. Hai ca đánh dấu ✗→✓ là hai
|
|
962
|
+
* ca `1.7.0` trả sai; ai đổi luật này phải chạy lại CẢ TÁM, đừng chọn vài ca dễ.
|
|
963
|
+
*
|
|
964
|
+
* <button> pointer
|
|
965
|
+
* <button disabled> default
|
|
966
|
+
* <button aria-disabled="true"> default ✗→✓
|
|
967
|
+
* <button role="button" disabled> default ✗→✓
|
|
968
|
+
* <div role="button"> pointer
|
|
969
|
+
* <div role="button" aria-disabled="true"> auto
|
|
970
|
+
* <label for="…"> pointer
|
|
971
|
+
* <label> (không for) default
|
|
972
|
+
*
|
|
973
|
+
* `<fieldset disabled>` cũng đúng theo đặc tả: control hậu duệ khớp `:disabled`, nên màn hình
|
|
974
|
+
* chỉ-đọc dựng bằng khuôn đó không mời bấm gì. (comitor-starter dùng đúng khuôn này.)
|
|
944
975
|
*/
|
|
945
|
-
button:not(:disabled),
|
|
946
|
-
[role="button"]:not([aria-disabled="true"]),
|
|
976
|
+
button:not(:disabled):not([aria-disabled="true"]),
|
|
977
|
+
[role="button"]:not(:disabled):not([aria-disabled="true"]),
|
|
947
978
|
label[for] {
|
|
948
979
|
cursor: pointer;
|
|
949
980
|
}
|