@haposoft/cafekit 0.16.7 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -22
- package/bin/__tests__/codex-hooks-ownership.test.js +2 -2
- package/bin/__tests__/codex-hooks.test.js +0 -36
- package/bin/__tests__/codex-native.test.js +46 -88
- package/bin/__tests__/develop-contract.test.js +26 -57
- package/bin/__tests__/docs-sync-on-demand.test.js +93 -0
- package/bin/__tests__/grok-envelope.test.js +0 -56
- package/bin/__tests__/hook-payload.test.js +4 -6
- package/bin/__tests__/legacy-authority-decoupled.test.js +159 -0
- package/bin/__tests__/legacy-hooks-retired.test.js +221 -0
- package/bin/__tests__/omp-bridge.test.js +20 -17
- package/bin/__tests__/omp-hooks.test.js +0 -19
- package/bin/__tests__/optional-skill-inventory.test.js +112 -4
- package/bin/__tests__/orca-session.test.js +0 -2
- package/bin/__tests__/package-inventory.test.js +100 -541
- package/bin/__tests__/runtime-dir.test.js +0 -3
- package/bin/__tests__/runtime-schema.test.js +3 -2
- package/bin/__tests__/skill-routing-source.test.js +27 -15
- package/bin/__tests__/spec-narrowing.test.js +0 -27
- package/bin/__tests__/specs-v2-execution-closeout.test.js +4 -69
- package/bin/__tests__/ui-ux-skill.test.js +101 -0
- package/bin/__tests__/usage-hook-retired.test.js +114 -0
- package/bin/__tests__/validator-grounding.test.js +9 -28
- package/bin/phases/codex-runtime.js +24 -0
- package/bin/phases/omp-runtime.js +22 -0
- package/package.json +1 -1
- package/src/claude/CLAUDE.md +3 -10
- package/src/claude/agents/brainstormer.md +7 -3
- package/src/claude/agents/code-auditor.md +35 -50
- package/src/claude/agents/debugger.md +5 -79
- package/src/claude/agents/deployer.md +3 -5
- package/src/claude/agents/docs-keeper.md +2 -7
- package/src/claude/agents/implementer.md +6 -9
- package/src/claude/agents/inspector.md +2 -3
- package/src/claude/agents/project-manager.md +7 -15
- package/src/claude/agents/researcher.md +9 -3
- package/src/claude/agents/spec-maker.md +11 -11
- package/src/claude/hooks/__tests__/fixtures/statusline-default.golden +1 -1
- package/src/claude/hooks/__tests__/spec-gate.test.js +74 -3
- package/src/claude/hooks/__tests__/spec-state-touched.test.js +134 -0
- package/src/claude/hooks/__tests__/state.test.js +68 -0
- package/src/claude/hooks/__tests__/statusline.test.js +140 -11
- package/src/claude/hooks/agent.cjs +2 -1
- package/src/claude/hooks/lib/context.cjs +0 -89
- package/src/claude/hooks/lib/hook-payload.cjs +2 -2
- package/src/claude/hooks/rules.cjs +2 -1
- package/src/claude/hooks/session.cjs +0 -1
- package/src/claude/hooks/spec-gate.cjs +17 -28
- package/src/claude/hooks/spec-state.cjs +95 -1
- package/src/claude/hooks/state.cjs +21 -2
- package/src/claude/migration-manifest.json +30 -15
- package/src/claude/rules/ai-dev-rules.md +1 -1
- package/src/claude/rules/hook-protocols.md +8 -15
- package/src/claude/rules/manage-docs.md +9 -23
- package/src/claude/rules/orchestrator.md +3 -4
- package/src/claude/rules/review-audit-self-decision.md +3 -3
- package/src/claude/rules/skill-domain-routing.md +1 -3
- package/src/claude/rules/skill-workflow-routing.md +4 -5
- package/src/claude/rules/state-sync.md +3 -12
- package/src/claude/rules/workflow.md +10 -18
- package/src/claude/runtime.schema.json +5 -3
- package/src/claude/scripts/generate-skill-catalog.cjs +7 -6
- package/src/claude/scripts/provenance.cjs +23 -5
- package/src/claude/scripts/spec-receipt.cjs +24 -7
- package/src/claude/scripts/spec-resolver.cjs +1 -2
- package/src/claude/scripts/validate-spec-output.cjs +5 -32
- package/src/claude/settings/settings.json +1 -32
- package/src/claude/skills/{question → ask}/SKILL.md +9 -75
- package/src/claude/skills/brainstorm/SKILL.md +43 -131
- package/src/claude/skills/code-review/SKILL.md +48 -17
- package/src/claude/skills/code-review/references/spec-compliance-review.md +2 -3
- package/src/claude/skills/code-review/references/verification-gate.md +22 -27
- package/src/claude/skills/debug/SKILL.md +29 -149
- package/src/claude/skills/develop/SKILL.md +27 -36
- package/src/claude/skills/develop/references/parallel-waves.md +0 -8
- package/src/claude/skills/develop/references/quality-gate.md +15 -13
- package/src/claude/skills/docs/SKILL.md +2 -2
- package/src/claude/skills/docs/references/update-workflow.md +1 -1
- package/src/claude/skills/fix/SKILL.md +153 -0
- package/src/claude/skills/{hotfix → fix}/references/diagnosis-protocol.md +2 -2
- package/src/claude/skills/{hotfix → fix}/references/parallel-patterns.md +2 -2
- package/src/claude/skills/{hotfix → fix}/references/prevention-gate.md +3 -3
- package/src/claude/skills/{hotfix → fix}/references/review-cycle.md +1 -1
- package/src/claude/skills/git/SKILL.md +48 -9
- package/src/claude/skills/git/references/commit-protocols.md +35 -16
- package/src/claude/skills/git/references/worktree-blueprint.md +42 -6
- package/src/claude/skills/orca/SKILL.md +9 -8
- package/src/claude/skills/research/SKILL.md +2 -0
- package/src/claude/skills/scout/SKILL.md +61 -0
- package/src/claude/skills/{inspect → scout}/references/internal-inspection.md +26 -10
- package/src/claude/skills/specs/SKILL.md +22 -29
- package/src/claude/skills/specs/references/review.md +8 -6
- package/src/claude/skills/specs/references/templates.md +34 -41
- package/src/claude/skills/sync/SKILL.md +8 -15
- package/src/claude/skills/sync/references/rebind-and-audit.md +59 -0
- package/src/claude/skills/sync/references/sync-protocols.md +4 -13
- package/src/claude/skills/test/SKILL.md +22 -3
- package/src/claude/skills/test/references/execution-strategy.md +83 -0
- package/src/claude/skills/test/references/failure-triage.md +1 -1
- package/src/claude/skills/ui-ux/LICENSE +21 -0
- package/src/claude/skills/ui-ux/SKILL.md +497 -0
- package/src/claude/skills/ui-ux/references/brand-tokens.md +90 -0
- package/src/claude/skills/ui-ux/references/budgets.md +113 -0
- package/src/claude/skills/ui-ux/references/checklist.md +271 -0
- package/src/claude/skills/ui-ux/references/components/accordion.md +246 -0
- package/src/claude/skills/ui-ux/references/components/avatar.md +153 -0
- package/src/claude/skills/ui-ux/references/components/banner.md +43 -0
- package/src/claude/skills/ui-ux/references/components/breadcrumb.md +201 -0
- package/src/claude/skills/ui-ux/references/components/button.md +142 -0
- package/src/claude/skills/ui-ux/references/components/card.md +113 -0
- package/src/claude/skills/ui-ux/references/components/charts.md +376 -0
- package/src/claude/skills/ui-ux/references/components/chat.md +132 -0
- package/src/claude/skills/ui-ux/references/components/choice-controls.md +369 -0
- package/src/claude/skills/ui-ux/references/components/comment-thread.md +17 -0
- package/src/claude/skills/ui-ux/references/components/description-list.md +116 -0
- package/src/claude/skills/ui-ux/references/components/empty-state.md +98 -0
- package/src/claude/skills/ui-ux/references/components/file-upload.md +107 -0
- package/src/claude/skills/ui-ux/references/components/inline-edit.md +128 -0
- package/src/claude/skills/ui-ux/references/components/input.md +161 -0
- package/src/claude/skills/ui-ux/references/components/list-row.md +56 -0
- package/src/claude/skills/ui-ux/references/components/loading.md +198 -0
- package/src/claude/skills/ui-ux/references/components/logo.md +147 -0
- package/src/claude/skills/ui-ux/references/components/otp-input.md +43 -0
- package/src/claude/skills/ui-ux/references/components/quantity-input.md +100 -0
- package/src/claude/skills/ui-ux/references/components/range-slider.md +16 -0
- package/src/claude/skills/ui-ux/references/components/small-controls.md +340 -0
- package/src/claude/skills/ui-ux/references/components/sortable-header.md +86 -0
- package/src/claude/skills/ui-ux/references/components/tag-input.md +14 -0
- package/src/claude/skills/ui-ux/references/components/timeline.md +38 -0
- package/src/claude/skills/ui-ux/references/components/tree.md +30 -0
- package/src/claude/skills/ui-ux/references/design-process.md +650 -0
- package/src/claude/skills/ui-ux/references/layouts/app-kanban.html +303 -0
- package/src/claude/skills/ui-ux/references/layouts/app.md +1369 -0
- package/src/claude/skills/ui-ux/references/layouts/form.md +430 -0
- package/src/claude/skills/ui-ux/references/layouts/overlay.md +552 -0
- package/src/claude/skills/ui-ux/references/layouts/pricing.md +204 -0
- package/src/claude/skills/ui-ux/references/locked-rules.md +35 -0
- package/src/claude/skills/ui-ux/references/principles.md +408 -0
- package/src/claude/skills/ui-ux/references/refactor.md +245 -0
- package/src/claude/skills/ui-ux/references/responsive.md +195 -0
- package/src/claude/skills/ui-ux/references/review.md +528 -0
- package/src/claude/skills/ui-ux/references/rules-color.md +623 -0
- package/src/claude/skills/ui-ux/references/rules-form.md +235 -0
- package/src/claude/skills/ui-ux/references/rules-state.md +680 -0
- package/src/claude/skills/ui-ux/references/rules-type.md +369 -0
- package/src/claude/skills/ui-ux/references/styles.md +311 -0
- package/src/claude/skills/ui-ux/references/system.md +219 -0
- package/src/claude/skills/ui-ux/references/tailwind-v4-traps.md +237 -0
- package/src/claude/skills/ui-ux/references/tokens.css +362 -0
- package/src/claude/skills/ui-ux/scripts/lint-skill.mjs +216 -0
- package/src/claude/skills/ui-ux/scripts/package.json +9 -0
- package/src/claude/skills/ui-ux/scripts/probe.mjs +4447 -0
- package/src/claude/skills/ui-ux-pro-max/SKILL.md +1 -1
- package/src/claude/status.cjs +149 -144
- package/src/codex/AGENTS.md +8 -13
- package/src/codex/hooks/agent.cjs +2 -1
- package/src/codex/hooks/lib/spec-receipt.cjs +2 -2
- package/src/codex/hooks/lib/spec-utils.cjs +0 -7
- package/src/codex/hooks/rules.cjs +2 -1
- package/src/codex/hooks/session.cjs +0 -2
- package/src/codex/hooks/spec-gate.cjs +7 -18
- package/src/codex/hooks.json +0 -20
- package/src/codex/rules/state-sync.md +3 -12
- package/src/common/AGENTS.md +2 -2
- package/src/omp/extensions/cafekit-bridge.mjs +6 -6
- package/src/claude/hooks/__tests__/completion-authority.test.js +0 -990
- package/src/claude/hooks/__tests__/semantic-review-authority.test.js +0 -259
- package/src/claude/hooks/completion-authority-check.cjs +0 -56
- package/src/claude/hooks/completion-authority-state.cjs +0 -527
- package/src/claude/hooks/completion-authority.cjs +0 -163
- package/src/claude/hooks/semantic-review-authority.cjs +0 -154
- package/src/claude/hooks/task-scaffold-guard.cjs +0 -114
- package/src/claude/hooks/usage.cjs +0 -197
- package/src/claude/skills/delegate/SKILL.md +0 -124
- package/src/claude/skills/delegate/references/codex-delegation.md +0 -71
- package/src/claude/skills/delegate/references/grok-delegation.md +0 -84
- package/src/claude/skills/hotfix/SKILL.md +0 -289
- package/src/claude/skills/inspect/SKILL.md +0 -142
- package/src/claude/skills/loop/SKILL.md +0 -114
- package/src/claude/skills/loop/references/bounded-loop-protocol.md +0 -104
- package/src/claude/skills/loop/references/metric-and-guard-contract.md +0 -76
- package/src/claude/skills/route/SKILL.md +0 -94
- package/src/claude/skills/route/references/agent-timing.md +0 -46
- package/src/claude/skills/route/references/chaining-patterns.md +0 -53
- package/src/claude/skills/route/references/task-taxonomy.md +0 -48
- package/src/codex/hooks/completion-authority-check.cjs +0 -67
- package/src/codex/hooks/completion-authority-state.cjs +0 -527
- package/src/codex/hooks/completion-authority.cjs +0 -109
- package/src/codex/hooks/semantic-review-authority.cjs +0 -130
- package/src/codex/hooks/task-scaffold-guard.cjs +0 -63
- /package/src/claude/skills/{question → ask}/templates/question.md +0 -0
- /package/src/claude/skills/{hotfix → fix}/references/escalation-tactics.md +0 -0
- /package/src/claude/skills/{hotfix → fix}/references/workflow-specialized.md +0 -0
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
# Màu — luật M
|
|
2
|
+
|
|
3
|
+
Đây là **nguồn duy nhất** cho mọi luật về màu, viền và bóng. `SKILL.md` chỉ chép
|
|
4
|
+
một dòng tóm tắt kèm số hiệu; giải thích, ngoại lệ và bằng chứng nằm ở đây.
|
|
5
|
+
|
|
6
|
+
Luật có dấu ⚑ là chưa qua vòng test nào.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Nền và thứ bậc bề mặt
|
|
11
|
+
|
|
12
|
+
**M1. Nền trang xám nhạt, không trắng tinh. Card mới trắng.**
|
|
13
|
+
|
|
14
|
+
*Gu flat — phong cách khác đã chọn theo `P1` thì xem `P2` trong `references/styles.md`.*
|
|
15
|
+
|
|
16
|
+
**M2. 95% trung tính, 5% điểm nhấn.**
|
|
17
|
+
|
|
18
|
+
*Gu flat — phong cách khác đã chọn theo `P1` thì xem `P2` trong `references/styles.md`.*
|
|
19
|
+
|
|
20
|
+
Đây là luật quan trọng nhất trong nhóm, và là thứ quyết định một màn hình trông
|
|
21
|
+
có chủ ý hay trông như chưa ai quyết định gì.
|
|
22
|
+
|
|
23
|
+
Bằng chứng thật (một dự án): một trang tích dần tới **năm màu
|
|
24
|
+
nền** cho năm loại khối — vàng be cho dặn dò, đỏ hồng cho nội quy, xanh nhạt cho
|
|
25
|
+
quà và nhật ký, tím indigo cho chip quà, cộng xanh/vàng của trạng thái. Khối nào
|
|
26
|
+
cũng đòi được chú ý nên **không khối nào nổi**, và trang đọc ra như "rainbow UI".
|
|
27
|
+
|
|
28
|
+
Hệ quả ngược cũng đã dính: gỡ hết màu đi thì thẻ "dặn dò" trắng nằm
|
|
29
|
+
giữa các thẻ task trắng, bị đọc lẫn thành một task. Nên trung tính **không phải
|
|
30
|
+
là không có điểm neo**:
|
|
31
|
+
|
|
32
|
+
> Nổi bằng **một điểm màu nhỏ**, không tô cả khối. Giữ thẻ trắng, đặt icon trong
|
|
33
|
+
> một **ô vuông nhạt 28px** kèm nhãn cùng màu.
|
|
34
|
+
|
|
35
|
+
**M3. Đúng một màu nhấn cho cả app.** Nút chính, link, control đang bật (checkbox,
|
|
36
|
+
switch, chip lọc đã chọn) dùng chung nó. Mục **điều hướng** đang chọn (sidebar, tab,
|
|
37
|
+
trang hiện tại) thì dùng nền xám hoặc vạch `--foreground`, không màu nhấn (bảng
|
|
38
|
+
công thức phần tử bên dưới, `small-controls.md`). Màu thứ hai phải xin phép.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Màu nói gì
|
|
43
|
+
|
|
44
|
+
**M4. Màu để báo trạng thái, không để phân loại.**
|
|
45
|
+
|
|
46
|
+
*Có màu công thức B — ô icon card số liệu, ô icon đầu dòng theo loại và chuỗi biểu đồ dashboard được tô màu phân loại, xem `P12` trong `references/styles.md`.*
|
|
47
|
+
|
|
48
|
+
> `M4` và `M5` là **gu mặc định cho dự án chưa có ngôn ngữ màu**. Dự án đã tô màu
|
|
49
|
+
> theo cách riêng (đếm ở `P4`) thì theo dự án; chỉ giữ các luật về nghĩa và đọc được
|
|
50
|
+
> liệt kê ở đầu `principles.md` (đỏ cho lỗi và phá huỷ, một bảng trạng thái, màu không
|
|
51
|
+
> đứng một mình, tương phản 4.5:1).
|
|
52
|
+
|
|
53
|
+
Bảng màu của MỘT màn hình, không thêm:
|
|
54
|
+
|
|
55
|
+
| Màu | Chỉ dùng cho |
|
|
56
|
+
| --- | --- |
|
|
57
|
+
| Xám trung tính | Mọi thứ còn lại: khung, chữ phụ, badge, icon, viền |
|
|
58
|
+
| Màu nhấn | Nút hành động chính khi thật cần nổi, mục đang chọn, link. **Không** cho badge số đếm, **không** cho trạng thái |
|
|
59
|
+
| Xanh lá | "Đang ổn", "đã xong": badge, thanh tiến độ xong (`M7`). Cố định, không lấy màu nhấn |
|
|
60
|
+
| Hổ phách | "Cần chú ý": quá hạn, nộp trễ, bỏ lỡ |
|
|
61
|
+
| Đỏ lỗi — `red` | Lỗi thật mà người dùng phải xử lý: bài bị từ chối, lỗi form |
|
|
62
|
+
| Đỏ nguy hiểm — `rose` | Hành động trả lời "có" ở một trong ba câu của `I4` (mất dữ liệu, kết thúc thứ đang chạy, cắt quyền): xoá, huỷ gói, thu hồi khoá, rời nhóm, đăng xuất. Lấy lại được không làm việc đó hết đỏ. Nút đứng riêng thì nền mờ + chữ đỏ luôn hiện; mục menu thì chỉ đỏ khi rê vào (`I4`) |
|
|
63
|
+
|
|
64
|
+
Hai sắc đỏ là cố ý, không phải gõ nhầm — xem `M30`.
|
|
65
|
+
|
|
66
|
+
Cách áp: định thêm một màu nền cho một loại khối thì **dừng lại và hỏi — màu đó
|
|
67
|
+
báo trạng thái gì?** Không trả lời được thì nó là trang trí, dùng xám.
|
|
68
|
+
|
|
69
|
+
Nội quy không phải lỗi → không đỏ. Quà không phải trạng thái → không màu riêng.
|
|
70
|
+
|
|
71
|
+
**M5. Phân loại khối bằng icon + chữ + viền, không bằng nền màu.**
|
|
72
|
+
|
|
73
|
+
Dặn dò, nội quy, quà, nhật ký đều là thẻ trắng viền mảnh; cái gì là gì do **icon
|
|
74
|
+
lucide + nhãn** nói. Thứ bậc đến từ cỡ chữ, độ đậm và khoảng cách: chữ chính đen,
|
|
75
|
+
chữ phụ xám; giữa các khu vực thoáng, trong từng thẻ gọn.
|
|
76
|
+
|
|
77
|
+
**M6. Một tín hiệu cho một ý.** Nhãn cộng ô màu đã nói "lưu ý" thì không thêm
|
|
78
|
+
badge "Lưu ý" nữa. Cùng tinh thần với `F6`: phần tử nổi bật chỉ cần một dấu hiệu.
|
|
79
|
+
|
|
80
|
+
**M7. Trạng thái đứng riêng một ô thì là badge màu. Nằm lẫn trong câu thì chữ màu.**
|
|
81
|
+
|
|
82
|
+
Cột trạng thái trong bảng, góc thẻ kanban, đầu trang chi tiết: **badge pill nền
|
|
83
|
+
nhạt, chấm tròn + chữ cùng tông**. Liếc dọc một cột 20 dòng thì mắt bắt màu nhanh
|
|
84
|
+
hơn bắt chữ; chấm xám + chữ đen thì cả cột trông như nhau ("Đang giao dịch"
|
|
85
|
+
với "Ngừng giao dịch" chỉ khác nhau ở độ mờ của một chấm 4px).
|
|
86
|
+
|
|
87
|
+
```html
|
|
88
|
+
<span class="inline-flex items-center gap-1.5 whitespace-nowrap rounded-full bg-emerald-50 px-2.5 py-1 text-xs font-medium text-emerald-700">
|
|
89
|
+
<span class="size-1.5 rounded-full bg-current"></span>Đang giao dịch
|
|
90
|
+
</span>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Bốn tông, không thêm. Trạng thái nào vào tông nào theo **nghĩa**, không theo sở thích:
|
|
94
|
+
|
|
95
|
+
| Tông | Nền / chữ | Token (không Tailwind) | Nghĩa | Ví dụ |
|
|
96
|
+
| --- | --- | --- | --- | --- |
|
|
97
|
+
| Xám | `bg-zinc-100 text-zinc-600` | `--neutral-bg` / `--neutral` | Chờ, nháp, chưa bắt đầu, trung lập | Tiềm năng, Nháp, Cần làm |
|
|
98
|
+
| Xanh lá | `bg-emerald-50 text-emerald-700` | `--success-bg` / `--success` | Đang ổn, đang chạy, đã xong | Đang giao dịch, Hoạt động, Đã thanh toán, Đã giao |
|
|
99
|
+
| Hổ phách | `bg-amber-50 text-amber-700` | `--warning-bg` / `--warning` | Cần chú ý | Quá hạn, Sắp hết hạn, Tạm dừng |
|
|
100
|
+
| Đỏ | `bg-red-50 text-red-700` | `--error-bg` / `--error-strong` | Đã dừng, thất bại, bị từ chối | Ngừng giao dịch, Đã huỷ, Lỗi |
|
|
101
|
+
|
|
102
|
+
**Dự án có dark mode thì mọi class `-700` của bảng trên đi kèm bản `dark:` `-400`**
|
|
103
|
+
(`text-red-700 dark:text-red-400`; **riêng hổ phách là `text-amber-700 dark:text-orange-400`**, xem
|
|
104
|
+
dưới; không Tailwind thì token, đã lật sẵn). `-700` không tự
|
|
105
|
+
đổi ở nền tối: chữ trạng thái còn 2,9–3,7:1 (`red-700` 2,9, `emerald-700` 3,5, `amber-700`
|
|
106
|
+
3,7). Sót một chỗ là cùng một nghĩa ra hai sắc trên một dòng ("Khẩn cấp" `text-amber-700` cam sẫm 3,74:1 cạnh "Quá hạn 3 ngày"
|
|
107
|
+
`dark:text-amber-400` vàng sáng). Gom mỗi tông vào một helper để
|
|
108
|
+
không phải nhớ từng chỗ.
|
|
109
|
+
|
|
110
|
+
**Bản tối chọn theo sắc, không theo số bậc.** Lên bậc sáng hơn thì phần lớn thang Tailwind giữ sắc
|
|
111
|
+
(đỏ, xanh lá, xanh dương, tím lệch 0–6° OKLCH), riêng **amber trôi sang vàng**: `amber-700` 49°,
|
|
112
|
+
`amber-400` 84°. "Quá hạn" cam ở bản sáng thành vàng ở bản tối, đọc ra hai màu, hai nghĩa. Hổ phách tối dùng **`orange-400`** (56°, lệch 7°, 7,9:1 trên card); nền
|
|
113
|
+
badge giữ `amber-500/15` (nền mờ không đọc ra sắc như chữ). Màu khác ngoài bảng này (avatar, chart, nhãn
|
|
114
|
+
tự chọn) cũng vậy: lệch quá ~20° OKLCH với bản sáng thì đổi họ màu gần sắc hơn. Probe `--dark` đo cặp này.
|
|
115
|
+
|
|
116
|
+
**Mọi badge có thêm `ring-1 ring-inset ring-black/5`** (nền tối `ring-white/10`). Nền badge
|
|
117
|
+
nhạt tới mức chỉ chênh vài phần trăm với nền dưới nó: `zinc-100` (#f4f4f5) đặt lên dòng
|
|
118
|
+
bảng đã chọn hay nền trang (#f4f4f6) là mất hẳn khung, "Tiềm năng" chỉ còn chấm với chữ.
|
|
119
|
+
Vòng trong 5% giữ khung ở mọi nền mà không nặng thêm.
|
|
120
|
+
|
|
121
|
+
**Một luồng có cả bước "đang" lẫn bước "xong" thì xanh dành cho "xong".** Đơn hàng:
|
|
122
|
+
Chờ xử lý → **hổ phách** (người bán phải làm gì đó), Đang giao → **xám** (đang chạy nhưng
|
|
123
|
+
không ai phải làm gì), Đã giao → **xanh**, Đã huỷ → **đỏ**. Để "Đang giao" cũng xanh thì
|
|
124
|
+
hai trạng thái khác nghĩa trùng màu (`N2`). "Đang giao dịch" của khách hàng vẫn xanh vì
|
|
125
|
+
luồng đó không có bước "xong".
|
|
126
|
+
|
|
127
|
+
**Luồng có từ hai trạng thái cùng tông thì chấm đổi thành icon, mỗi trạng thái một hình.**
|
|
128
|
+
Bốn tông không đủ cho luồng công việc: theo đoạn trên thì Cần làm và Đang làm cùng xám, và
|
|
129
|
+
bảng nhóm theo trạng thái ra ba nhóm xám một nhóm xanh, liếc không tách được nhóm nào với
|
|
130
|
+
nhóm nào. Các app quản lý công việc lớn tách bằng **hình**, không thêm màu:
|
|
131
|
+
|
|
132
|
+
| Trạng thái | Tông | Icon lucide | Vì sao |
|
|
133
|
+
| --- | --- | --- | --- |
|
|
134
|
+
| Cần làm | xám | `circle` | vòng rỗng: chưa bắt đầu |
|
|
135
|
+
| Đang làm | xám | `circle-dot` | có lõi: đang có người làm, không ai khác phải làm gì |
|
|
136
|
+
| Chờ duyệt | hổ phách | `circle-ellipsis` | người duyệt phải làm gì đó, cùng lý với "Chờ xử lý" |
|
|
137
|
+
| Xong | xanh lá | `circle-check` | |
|
|
138
|
+
| Đã huỷ | đỏ | `circle-x` | |
|
|
139
|
+
|
|
140
|
+
Icon `size-3.5` trong badge (thay chấm), `size-4` ở đầu nhóm và đầu cột kanban, màu
|
|
141
|
+
`-600` cùng tông (`text-zinc-500` cho xám). Một bảng này dùng cho mọi bề mặt (`D2`).
|
|
142
|
+
|
|
143
|
+
Nền tối: class Tailwind thì thêm `dark:` (nền `-500/15`, chữ `-400`), token thì
|
|
144
|
+
khối `.dark` trong `tokens.css` đã đổi sẵn.
|
|
145
|
+
|
|
146
|
+
- Xanh lá là màu **cố định**, không lấy màu nhấn. Màu nhấn mặc định gần đen, badge đen đặc giữa bảng trông như nút bấm.
|
|
147
|
+
- Chữ `-700`, không `-500`: chữ nhỏ trên nền nhạt cần đậm để đạt tương phản (`P3`).
|
|
148
|
+
- Không `border`. Vòng trong `ring-black/5` ở trên là khung giữ badge không tan vào nền, không tính là tín hiệu thứ hai (`M6`).
|
|
149
|
+
- Cả app một bảng ánh xạ, xem `D2` trong `system.md`.
|
|
150
|
+
|
|
151
|
+
Nhãn **nằm trong dòng chữ phụ** ("Hằng tuần · quá hạn 2 ngày") thì vẫn là chữ màu,
|
|
152
|
+
không nền. Pill chen giữa câu làm dòng chữ gồ lên.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Ngoại lệ đã duyệt
|
|
157
|
+
|
|
158
|
+
**M8. Màu mã hoá dữ liệu không tính vào ngân sách một màu nhấn.**
|
|
159
|
+
|
|
160
|
+
Tag phân loại, nhãn ngành, nhãn mô hình được phép nhiều màu, vì màu ở đó **mang
|
|
161
|
+
thông tin**. Bốn điều kiện, thiếu một là bỏ:
|
|
162
|
+
|
|
163
|
+
- Chỉ cho phân loại thật, thứ mà người ta cần liếc là phân biệt được.
|
|
164
|
+
- Luôn **pastel nhạt**: nền khoảng 10%, chữ đậm cùng tông, viền một bậc đậm hơn nền.
|
|
165
|
+
- **Một nhãn một màu cố định** trong cả app. "Technology" xanh dương thì ở đâu cũng xanh dương.
|
|
166
|
+
- Không lan sang nút, nền khối, hay đường kẻ.
|
|
167
|
+
|
|
168
|
+
Đặt tên thang màu phân loại **khác tên trạng thái**. Dự án thật tách riêng
|
|
169
|
+
`iris` / `magenta` / `coral` thay vì dùng lại `accent` / `danger`, để badge đỏ
|
|
170
|
+
"B2C" không bị đọc nhầm thành lỗi.
|
|
171
|
+
|
|
172
|
+
**M9. Biểu tượng quen thuộc được giữ màu của nó, dù đó không phải trạng thái.**
|
|
173
|
+
|
|
174
|
+
Chủ dự án duyệt, sau khi trung tính hoá làm mất nghĩa:
|
|
175
|
+
|
|
176
|
+
- Huy chương top 3 tô đặc vàng / bạc / đồng — màu **trên icon**, không tô thẻ.
|
|
177
|
+
- Thẻ hạng nhất viền vàng, **nền vẫn trắng**. Nền vàng nhạt ra màu be, đọc như thẻ cũ hoặc thẻ đã khoá. Đã thử và đã bỏ.
|
|
178
|
+
- Ngọn lửa chuỗi ngày tô đặc: ruột vàng, viền cam. Lửa xám nét mảnh chìm hẳn.
|
|
179
|
+
- Dấu `*` của trường bắt buộc tô đỏ. Không phải lỗi, nhưng "`*` đỏ = bắt buộc" là quy ước phổ biến tới mức xám lại khó đọc.
|
|
180
|
+
|
|
181
|
+
Nguyên tắc chung: khi màu trung tính làm **một biểu tượng mất nghĩa**, chọn nghĩa,
|
|
182
|
+
nói một câu lý do, rồi ghi ngoại lệ vào đây.
|
|
183
|
+
|
|
184
|
+
**M10. Nội dung người dùng tự viết thì không kiểm soát màu.** SOP, ghi chú có
|
|
185
|
+
emoji, chữ đỏ trong markdown — chỉ làm KHUNG bao quanh trung tính, đừng đi sửa
|
|
186
|
+
ruột.
|
|
187
|
+
|
|
188
|
+
**M34. Thứ người dùng tạo ra thì có màu nhận diện.** Dự án, board, workspace, danh
|
|
189
|
+
mục, cột kanban, kênh: thứ người dùng tự đặt tên, hiện ở nhiều chỗ, và cần nhận ra
|
|
190
|
+
bằng một cái liếc trong danh sách dài. App một màu nhấn nhìn gọn nhưng dễ đơn điệu;
|
|
191
|
+
điểm màu nên nằm ở **dữ liệu**, còn **khung** (nút, sidebar, header, ô nhập) vẫn một
|
|
192
|
+
màu nhấn như `M2`. Khác `M8`: `M8` là nhãn phân loại gắn lên một mục, `M34` là màu
|
|
193
|
+
của chính thực thể. Một card có thể mang cả hai.
|
|
194
|
+
|
|
195
|
+
- **Khi nào dùng:** app có thực thể như trên, và nó xuất hiện từ hai chỗ trở lên (sidebar, bảng, breadcrumb, ô chọn) hoặc có từ năm cái trở lên. Không có thì thôi, đừng bịa ra thực thể để có chỗ tô. Có thì làm luôn, không hỏi.
|
|
196
|
+
- **Sáu sắc của avatar**, cùng thứ tự (`components/avatar.md`): emerald, sky, indigo, pink, amber, violet. Không thêm sắc, không `red` / `rose` (`M30`).
|
|
197
|
+
- **Màu nằm trên dấu nhỏ**, không trên mảng lớn: chấm tròn `size-2` sắc `-500` (nền tối `-400`) cạnh tên; ô vuông bo góc chữ cái đầu theo khuôn avatar; dải `h-1` ở đỉnh card hay đầu cột; ảnh bìa người dùng tải lên. **Không** tô nền cả card, nền dòng, nền cột, không tô chữ tên.
|
|
198
|
+
- **Màu là dữ liệu, không tính lúc render.** Tạo mới thì có ô chọn sáu màu, mặc định lấy theo `id` như avatar; lưu lại cùng thực thể. Ở sidebar, bảng, breadcrumb, ô chọn đều cùng một màu.
|
|
199
|
+
- **Tên luôn hiện cạnh màu.** Màu giúp liếc, không thay chữ; chấm và ô vuông để `aria-hidden`.
|
|
200
|
+
- **Trạng thái của khung không đổi:** mục sidebar đang chọn vẫn nền `bg-secondary` trung tính (`checklist.md`, mục Sidebar), chấm giữ nguyên màu, không đổi sang màu thực thể.
|
|
201
|
+
- Không tính vào ngân sách một màu nhấn (`budgets.md`). Dự án đã có cách tô riêng cho thực thể thì theo họ.
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
DỰ ÁN ┌─────────────────────┐
|
|
205
|
+
● Website khách A │▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔│ ← dải h-1 màu dự án
|
|
206
|
+
● App nội bộ │ Sửa form đăng ký │
|
|
207
|
+
● Marketing Q4 │ [Bug] [Gấp] │ ← nhãn M8
|
|
208
|
+
└─────────────────────┘
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**M11. Chữ chỉ ba sắc độ.** Chữ chính, chữ phụ, và màu nằm trên nền nhấn.
|
|
212
|
+
|
|
213
|
+
Ngoại lệ duy nhất: **mục điều hướng lúc chưa rê/chưa chọn** dùng
|
|
214
|
+
`foreground/70`, để hover có chỗ "sáng lên" mà lúc thường vẫn đọc rõ. Dùng
|
|
215
|
+
`--muted` ở đó thì tên mục mờ quá trên nền trắng.
|
|
216
|
+
|
|
217
|
+
⚠️ Bẫy đã dính ở dự án thật: token tên `--text-muted` bị alias về `--text-normal`,
|
|
218
|
+
tức "chữ phụ" và "chữ chính" cùng một màu đen. Đừng tin tên token — mở giá trị
|
|
219
|
+
thật ra xem. Xem `refactor.md` luật L3.
|
|
220
|
+
|
|
221
|
+
**M12. Không gradient.** Ngoại lệ duy nhất: **ảnh đại diện và dấu hiệu nhận diện**
|
|
222
|
+
— avatar người dùng, icon workspace, logo tổ chức. Chúng là hình tròn hoặc vuông
|
|
223
|
+
nhỏ dưới 40px, và gradient ở đó đóng vai ảnh chứ không đóng vai nền.
|
|
224
|
+
|
|
225
|
+
*Gu flat — phong cách khác đã chọn theo `P1` thì xem `P2` trong `references/styles.md`.*
|
|
226
|
+
|
|
227
|
+
Không bao giờ cho nút, card, nền trang, hay chữ (`bg-clip-text text-transparent`).
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Viền và bóng
|
|
232
|
+
|
|
233
|
+
> **⚠️ Đảo luật.** Bản cũ của skill này cấm viền card và bắt tách khối bằng chênh
|
|
234
|
+
> lệch nền. **Luật đó đã bỏ.** Chủ dự án chốt phong cách "đường tóc 1px + bo góc,
|
|
235
|
+
> không bóng". Đừng hồi sinh luật cũ.
|
|
236
|
+
|
|
237
|
+
**M13. Tách khối bằng đường tóc 1px + bo góc, không bằng bóng.**
|
|
238
|
+
|
|
239
|
+
*Gu flat — phong cách khác đã chọn theo `P1` thì xem `P2` trong `references/styles.md`.*
|
|
240
|
+
|
|
241
|
+
Trang phẳng, sạch; thứ bậc đến từ cỡ chữ, độ đậm và màu chữ.
|
|
242
|
+
|
|
243
|
+
*Ngoại lệ: card đứng một mình giữa trang trống — xem `M29`.*
|
|
244
|
+
|
|
245
|
+
| Phần tử | Công thức |
|
|
246
|
+
| --- | --- |
|
|
247
|
+
| Thẻ / khung | trắng, viền 1px xám rất nhạt, bo 16px `rounded-2xl` (`F1`), **không bóng** |
|
|
248
|
+
| Danh sách nhiều mục | MỘT khung, các dòng chia bằng `divide-y`. Dòng tiêu đề và dòng hành động cuối nằm TRONG khung |
|
|
249
|
+
| Khối tóm tắt phụ | nền xám nhạt + viền, bo như thẻ |
|
|
250
|
+
| Mục sidebar, cây thư mục đang chọn | nền `--secondary` + `font-medium`, **không viền**, không màu nhấn; rê vào `--background`, mục chưa chọn không nền. Đang chọn đậm hơn nền rê một bậc (`I10`, `I15`) |
|
|
251
|
+
| Tab ngang trên bảng / danh sách | tab đang chọn **nền `--secondary`, viền trong suốt** (không nền trắng, không `--surface-hover`: trên card trắng liếc không thấy), mọi tab có `border`, mục chưa chọn `border-transparent`. Xem "Thanh tab" trong `components/small-controls.md` |
|
|
252
|
+
| Ô nhập | viền — đây là chỗ viền đúng vai nhất, người ta phải nhìn ra ranh giới vùng gõ được |
|
|
253
|
+
|
|
254
|
+
**M14. Hai token viền, chia theo vai trò. Không có cái thứ ba.**
|
|
255
|
+
|
|
256
|
+
| Token | Cho | Vì sao |
|
|
257
|
+
| --- | --- | --- |
|
|
258
|
+
| `--border` | Viền card, khung dropdown, đường chia trong danh sách và menu, **đường kẻ dưới đầu sidebar và dưới header** (cùng màu vạch trong menu, chủ dự án chốt, `layouts/app.md`) | **Trang trí**: chỉ vạch ranh giới, nhạt được bao nhiêu thì nhạt |
|
|
259
|
+
| `--border-strong` | **Viền ô nhập**, **viền nút outline**, select, viền card khi hover, đường kẻ nằm trên nền trang xám (dưới hàng tab khu cài đặt), kẻ dọc sidebar **chỉ khi vùng nội dung cũng trắng** (`layouts/app.md`) | **Chức năng**: ô nhập và nút outline cùng nền trắng với card, viền là thứ duy nhất báo "đây là chỗ gõ", "đây là chỗ bấm" (`I8`) |
|
|
260
|
+
|
|
261
|
+
Trong mỗi nhóm thì mọi chỗ dùng chung đúng một token, để đường tóc không chỗ
|
|
262
|
+
đậm chỗ nhạt. Muốn viền card nhạt đi thì hạ `--border`, ô nhập không nhạt theo.
|
|
263
|
+
|
|
264
|
+
Checkbox, radio chưa chọn cũng dùng `--border-strong`, cùng độ đậm với ô nhập.
|
|
265
|
+
|
|
266
|
+
Nền tối giữ đúng hai vai đó, cả hai là `rgba` mờ: `--border` 1.23:1 trên card, `--border-strong`
|
|
267
|
+
1.33:1 (đừng dùng lại `0.2`: 1.47:1, dòng bảng thành lưới kẻ ô). Ô nhập ở nền tối
|
|
268
|
+
**giữ viền** `--border-strong` (`M32`).
|
|
269
|
+
|
|
270
|
+
**Dự án đã có token viền riêng** (dựng mới trong dự án có sẵn, hai chế độ dựng lại): xám
|
|
271
|
+
viền là **dáng, không phải vai màu** (`review.md`), nên xếp token của họ vào hai vai trên
|
|
272
|
+
chứ không giữ cách bản cũ dùng. Bậc nhạt nhất của dự án cho vai trang trí (viền card, khung
|
|
273
|
+
dropdown, đường chia). Đường kẻ khung app: **đoạn dưới đầu sidebar và đoạn dưới header
|
|
274
|
+
cùng một token**, vì hai đoạn nối thành một đường (probe: "đường ngăn thẳng hàng mà khác
|
|
275
|
+
màu"). Bậc nào đậm cỡ `#e4e4e7` trở lên (~1.25:1 trên trắng, mức đã bị chê "đường kẻ sidebar
|
|
276
|
+
đậm") thì không dùng cho đường kẻ khung và viền card, chỉ cho ô nhập, nút
|
|
277
|
+
viền. Đã dính: vạch dưới logo `border-light` `#f1f5f9` nối vào
|
|
278
|
+
vạch dưới header `border` `#e2e8f0`, card lọc, card danh sách, card chi tiết cũng
|
|
279
|
+
`#e2e8f0`: đường ở sidebar ổn, ngoài thì đậm, người xem tưởng brand bắt vậy.
|
|
280
|
+
|
|
281
|
+
> Đã thử làm đậm viền điều khiển cho đạt 3:1 (WCAG 1.4.11): ô nhập,
|
|
282
|
+
> select, nút outline lên `#8a8a91`; checkbox, radio lên `--muted`; track công
|
|
283
|
+
> tắc lên `muted/75`. Chủ dự án xem thật thấy **đậm và xấu**, trả về hết.
|
|
284
|
+
> Đừng đề xuất lại cho dự án thường; chỉ dự án bắt buộc AA (xem `P3`).
|
|
285
|
+
|
|
286
|
+
⚠️ **Đừng lấy `--border` cho ô nhập hay nút outline để "cho đồng bộ".** Hạ
|
|
287
|
+
`--border` cho card và dropdown nhẹ đi là ô nhập và nút tan luôn vào nền. Đã xảy
|
|
288
|
+
ra thật: `--border` hạ từ `#f3f3f4` xuống `#f7f7f8` cho khung dropdown,
|
|
289
|
+
nút outline đang dùng chung token nên trông như đã bị khoá.
|
|
290
|
+
|
|
291
|
+
⚠️ Thiếu class màu viền thì Tailwind v4 để `border-color: currentColor` — nút chữ
|
|
292
|
+
đen sẽ ra **viền gần đen**. Thấy viền đậm bất thường thì kiểm chỗ này trước khi
|
|
293
|
+
nghi mã màu.
|
|
294
|
+
|
|
295
|
+
**M15. Bóng CHỈ cho lớp nổi.**
|
|
296
|
+
|
|
297
|
+
*Gu flat — phong cách khác đã chọn theo `P1` thì xem `P2` trong `references/styles.md`.*
|
|
298
|
+
|
|
299
|
+
Modal, command palette, dropdown, popover, toast được đổ bóng vì chúng nằm **trên**
|
|
300
|
+
trang. Mọi thứ nằm **trong** trang thì không.
|
|
301
|
+
|
|
302
|
+
Bóng lớp nổi đi qua hai token, không viết `shadow-lg`, `shadow-xl` trần: `shadow-popover`
|
|
303
|
+
(dropdown, menu, popover, toast) và `shadow-modal` (modal, panel trượt, command palette).
|
|
304
|
+
Nền sáng chúng bằng đúng `shadow-lg` / `shadow-xl`; nền tối đậm hơn (`M23`). Khung nào
|
|
305
|
+
cũng `border border-border`. Bảng nền của từng khối ở đầu `layouts/overlay.md`.
|
|
306
|
+
|
|
307
|
+
Định thêm `shadow-*` cho khối nằm trong trang → thử viền trước, xem có đủ tách
|
|
308
|
+
khối không. Gần như luôn là đủ.
|
|
309
|
+
|
|
310
|
+
*Ngoại lệ: card đứng một mình giữa trang trống — xem `M29`. Ô đang chọn của tab
|
|
311
|
+
`segmented` (phím nổi trên rãnh chìm), dùng đúng hai token `--shadow-segment-*` —
|
|
312
|
+
xem "Thanh tab" trong `components/small-controls.md`.*
|
|
313
|
+
|
|
314
|
+
**M16. Không đẻ token viền mới từ màu nhấn.**
|
|
315
|
+
|
|
316
|
+
Viền tĩnh chỉ có token viền thường (và tối đa một bậc đậm hơn). Viền trạng thái
|
|
317
|
+
chỉ có `--border-focus`, và nó chỉ hiện lúc focus.
|
|
318
|
+
|
|
319
|
+
Thấy mình sắp viết `--primary-ring`, `--accent-border` là dấu hiệu đang muốn nhấn
|
|
320
|
+
một khối bằng viền — mà nhấn bằng viền là cách rẻ nhất. Đã xảy ra thật: một bản
|
|
321
|
+
dựng tự chế `--primary-ring: rgba(233,237,245,0.22)` rồi viền card nổi bật, trên
|
|
322
|
+
nền tối trông sáng chói.
|
|
323
|
+
|
|
324
|
+
**M17. `ring` khi không được đụng bố cục, `border` cho phần còn lại.**
|
|
325
|
+
|
|
326
|
+
`border` ăn vào hộp theo `box-sizing: border-box` nên phần tử cỡ cố định sẽ co
|
|
327
|
+
lại. Cần đường bao quanh avatar, quanh ô vuông cỡ chuẩn thì dùng `ring-1`.
|
|
328
|
+
|
|
329
|
+
**M18. Phần tử con trong hàng có hover không được trùng token với nền hover của hàng.**
|
|
330
|
+
|
|
331
|
+
Hàng hover đổi nền (luật `I10`: `--item-hover` hoặc `--surface-hover`). Nếu ô vuông trạng thái, checkbox hay
|
|
332
|
+
avatar bên trong cũng dùng đúng token đó làm nền, hoặc chỉ có viền nhạt, thì rê
|
|
333
|
+
chuột vào là chúng **biến mất**.
|
|
334
|
+
|
|
335
|
+
*Cách kiểm:* rê chuột lên hàng, đếm xem còn nhìn thấy đủ mọi thứ không.
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Bo góc lồng nhau
|
|
340
|
+
|
|
341
|
+
**M19. Bo lồng nhau: ngoài = trong + khoảng cách giữa hai mép.**
|
|
342
|
+
|
|
343
|
+
```
|
|
344
|
+
R_ngoài = r_trong + d d = padding của khung ngoài + độ dày viền (nếu có)
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**Vì sao:** hai góc chỉ trông song song, khe hở đều nhau suốt đường cong, khi
|
|
348
|
+
chúng có **chung một tâm**. Công thức trên chính là điều kiện để hai tâm trùng
|
|
349
|
+
nhau. Lệch khỏi nó thì khe hở ở góc khác khe hở ở cạnh:
|
|
350
|
+
|
|
351
|
+
| Bán kính trong | Khe hở ở góc | Trông |
|
|
352
|
+
| --- | --- | --- |
|
|
353
|
+
| **Bằng** bán kính ngoài (12 trong 12) | **≈ 1.4 × d**, rộng hơn ở cạnh | Góc phình ra, như hai hình không khớp |
|
|
354
|
+
| **= R − d** | **= d**, bằng đúng ở cạnh | Song song, gọn |
|
|
355
|
+
| Nhỏ hơn nhiều, hoặc vuông | Hẹp hơn d, có thể về 0 | Góc trong bị ép sát, chọc vào đường cong ngoài |
|
|
356
|
+
|
|
357
|
+
**Các cặp hay dùng, đều nằm trên thang Tailwind:**
|
|
358
|
+
|
|
359
|
+
| Khung ngoài | Padding | Phần tử trong |
|
|
360
|
+
| --- | --- | --- |
|
|
361
|
+
| `rounded-xl` 12px | `p-1` 4px | `rounded-lg` 8px |
|
|
362
|
+
| `rounded-2xl` 16px | `p-1` 4px | `rounded-xl` 12px |
|
|
363
|
+
| `rounded-2xl` 16px | `p-2` 8px | `rounded-lg` 8px |
|
|
364
|
+
|
|
365
|
+
Cả ba cặp chỉ dùng bậc có trong thang bốn bậc của `F1`. Công thức ra một số
|
|
366
|
+
ngoài thang (ví dụ `p-1.5` ra 6px) thì **đổi padding cho khớp thang**, đừng đẻ
|
|
367
|
+
thêm bậc bo góc.
|
|
368
|
+
|
|
369
|
+
**Chỉ áp khi hai mép ở gần nhau** — khi `d` không lớn hơn `R`. Dropdown, menu,
|
|
370
|
+
thanh tab dạng viên thuốc, ô nhập có nút bên trong: đều là ca này, và mắt so hai
|
|
371
|
+
góc với nhau ngay.
|
|
372
|
+
|
|
373
|
+
**Khoảng cách lớn thì bỏ công thức.** Card `rounded-2xl` 16px với `p-5` 20px chứa
|
|
374
|
+
một nút: công thức ra `16 − 20 = −4`. Hai góc cách nhau quá xa để mắt so, nên dùng
|
|
375
|
+
bo theo vai trò (`F1`): card 16px, nút và ô nhập 12px, control nhỏ 8px. Đừng ép
|
|
376
|
+
công thức ra số âm hay 0.
|
|
377
|
+
|
|
378
|
+
Có viền 1px thì `d` cộng thêm 1. Lệch 1px không ai thấy, nên cứ lấy bậc gần nhất
|
|
379
|
+
trên thang.
|
|
380
|
+
|
|
381
|
+
Không trộn nút bo tròn hẳn với nút bo vuông trong cùng một nhóm; badge trạng thái
|
|
382
|
+
là ngoại lệ.
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
## Dark mode
|
|
387
|
+
|
|
388
|
+
**M20. Mặc định chỉ làm light mode.** Dark mode là việc gấp đôi và gấp đôi chỗ
|
|
389
|
+
phải kiểm tương phản. Chỉ làm khi người dùng nói cần, và hỏi một câu lúc giao.
|
|
390
|
+
Làm thì theo đủ `M21`–`M23`, `M31`–`M33`, và khối `.dark` của `tokens.css`.
|
|
391
|
+
|
|
392
|
+
*Gu flat — phong cách khác đã chọn theo `P1` thì xem `P2` trong `references/styles.md`.*
|
|
393
|
+
|
|
394
|
+
**M21. Tầng nổi sáng dần ở nền tối; vùng tô là lớp phủ, giữ độ chênh với nền phía sau
|
|
395
|
+
chứ không giữ chiều.**
|
|
396
|
+
|
|
397
|
+
Hai loại bề mặt, hai cách đổi theme:
|
|
398
|
+
|
|
399
|
+
| Loại | Gồm | Nền sáng | Nền tối |
|
|
400
|
+
| --- | --- | --- | --- |
|
|
401
|
+
| **Tầng** | nền trang → card → lớp nổi nhỏ (`--surface-overlay`) | trang xám, card và lớp nổi trắng | **sáng dần**: `#05060f` → `#0f111a` → `#171a26`, mỗi bậc ~1.07–1.09:1 |
|
|
402
|
+
| **Vùng tô** | nút phụ, mục / tab đang chọn (`--secondary`), nền rê (`--item-hover`, `--button-hover`), rãnh, chip | phủ **tối** hơn nền phía sau | phủ **trắng mờ**, **sáng** hơn nền phía sau |
|
|
403
|
+
|
|
404
|
+
Vùng tô ở nền sáng tối hơn card, ở nền tối sáng hơn card: đúng ở cả hai. Thứ phải giữ
|
|
405
|
+
là **độ chênh** với nền ngay sau nó, ngang nhau giữa hai theme (đang chọn `--secondary`
|
|
406
|
+
1.22:1 cả hai bản; nền rê 1.10 / 1.12:1). Đây là cách gần như mọi bộ thiết kế lớn làm
|
|
407
|
+
(7/7 bộ có số liệu).
|
|
408
|
+
|
|
409
|
+
- **Vùng tô nền tối viết bằng trắng phủ mờ** (`white/4`–`white/10`), không bằng xám đặc.
|
|
410
|
+
Phủ mờ thì đặt trên nền trang, card hay lớp nổi đều đúng; xám đặc chỉ đúng trên một nền.
|
|
411
|
+
Trong code: lớp phủ `bg-foreground/5` (tự đảo theo theme) hoặc token, không `dark:bg-zinc-800`.
|
|
412
|
+
- **Không dùng `--background` làm nền rê hay nền chọn.** Ở nền tối nó tối hơn card, rê vào
|
|
413
|
+
là chìm xuống 1.07:1, gần như không thấy. Nền rê mục thụt vào là `--item-hover` (`I10`).
|
|
414
|
+
- **Đang chọn đậm hơn rê đúng một bậc ở cả hai theme** (`I10`, `I15`).
|
|
415
|
+
|
|
416
|
+
*Cách kiểm:* liệt kê nền trang, card, lớp nổi của mỗi theme: nền tối phải sáng dần. Rồi đo
|
|
417
|
+
nền rê và nền đang chọn trên card ở cả hai theme: độ chênh hai bản xấp xỉ nhau, đang chọn
|
|
418
|
+
đậm hơn rê.
|
|
419
|
+
|
|
420
|
+
> Đừng bắt nút phụ **chìm hơn card** ở nền tối: `--secondary` tối `#010207`, dưới cả nền
|
|
421
|
+
> trang, thì nút và tab đang chọn gần như không tách khỏi nền. Nút phụ `#1c2030` trên card
|
|
422
|
+
> `#0f111a` trông "nổi lên" là đúng chiều; thứ cần canh là độ chênh, không phải chiều.
|
|
423
|
+
|
|
424
|
+
**M22. Ở nền tối, màu nhấn chỉ dùng làm nền, không dùng làm đường mảnh.**
|
|
425
|
+
|
|
426
|
+
Màu nhấn trong dark mode thường là gần trắng. Tô nền nút thì đẹp; đem làm viền ô
|
|
427
|
+
nhập lúc focus, gạch chân, hay chỉ báo đang chọn thì thành sợi trắng đặc một
|
|
428
|
+
pixel, gắt và rẻ. Đường mảnh dùng chính màu đó **hạ độ đục xuống khoảng 42%**, vừa đủ 3:1 với nền
|
|
429
|
+
(WCAG 1.4.11). Xuống 35% là còn 2.9:1, trượt.
|
|
430
|
+
|
|
431
|
+
**M23. Dark mode mặc định của skill là navy rất tối. Đây là gu, không phải quy ước:
|
|
432
|
+
dự án đã có bảng tối riêng thì theo dự án.**
|
|
433
|
+
|
|
434
|
+
Phần lớn các bộ thiết kế lớn để nền tối trung tính hoặc gần trung tính; navy là một
|
|
435
|
+
trong nhiều gu. Dự án có xám kẽm, xám trung tính, hay nền tối ám màu thương hiệu thì giữ
|
|
436
|
+
nguyên (`P10`, `V4`). Dự án chưa có thì lấy khối `.dark` của `tokens.css`.
|
|
437
|
+
|
|
438
|
+
Dù gu nào:
|
|
439
|
+
|
|
440
|
+
- **Không đen tuyệt đối, không trắng tuyệt đối**, kể cả nền rê của nút chính. Nền gần đen,
|
|
441
|
+
chữ gần trắng. `--primary-hover` lệch **về phía nền** một bậc ở cả hai theme: nền sáng nhạt
|
|
442
|
+
đi, nền tối tối đi (`#e9edf5` → `#cfd5e0`), không sáng lên `#ffffff`.
|
|
443
|
+
- **Viền là `rgba` mờ**, không phải màu đặc, không thì thành lưới kẻ ô. Ở nền tối viền
|
|
444
|
+
**đảo vai**: nền trang và card chênh nhau quá ít (1.07:1) nên viền là thứ chính để tách khối.
|
|
445
|
+
- **Bóng giữ, đậm hơn, đi cặp viền 1px.** Bóng 10% trên nền gần đen gần như không thấy, nên
|
|
446
|
+
lớp nổi tối có ba thứ cùng lúc: nền sáng hơn một bậc (`M21`), viền `border-border`, bóng
|
|
447
|
+
đậm (`--elevation-*` khối `.dark`). Không bỏ bóng, không thay bằng vầng sáng.
|
|
448
|
+
- **Không bóng màu ở nền tối** (bóng cam, bóng hổ phách dưới nút): trên nền tối nó thành
|
|
449
|
+
vầng sáng bẩn. `dark:shadow-none` cho mọi bóng có sắc.
|
|
450
|
+
|
|
451
|
+
*Gu flat — phong cách khác đã chọn theo `P1` thì xem `P2` trong `references/styles.md`.*
|
|
452
|
+
|
|
453
|
+
**M31. Nút đổi theme: Sáng / Tối / Hệ thống, mặc định Hệ thống.**
|
|
454
|
+
|
|
455
|
+
- **Ba lựa chọn thấy hết một lần**, không một nút bấm xoay vòng (người dùng phải bấm thử mới
|
|
456
|
+
biết lần sau ra gì). Mặc định **Hệ thống** (`prefers-color-scheme`), không mặc định Sáng.
|
|
457
|
+
- **Chỗ đặt:** trong app là **Cài đặt → Giao diện** hoặc mục Giao diện trong **menu tài khoản**;
|
|
458
|
+
đề bảo đặt trên header thì là **icon button** (`Sun` / `Moon`) mở menu ba mục, mục đang chọn
|
|
459
|
+
có dấu ✓. Trang công khai, docs: nhóm ba icon (radiogroup) ở footer hoặc nút icon trên header.
|
|
460
|
+
- **Màn xác thực, trang lỗi không cần nút đổi theme**: script đầu trang áp lựa chọn đã lưu hoặc theo
|
|
461
|
+
hệ thống cho mọi route, người dùng đổi trong app.
|
|
462
|
+
- **Nhớ lựa chọn, không nháy trắng khi tải.** Script đặt class trên `<html>` trước khi vẽ;
|
|
463
|
+
`<html suppressHydrationWarning>` vì script sửa `<html>` trước React.
|
|
464
|
+
- **Lật theme thì tắt transition một nhịp**, không thì nền trang đổi tức thì còn nút, card
|
|
465
|
+
chuyển màu 200ms lệch nhịp.
|
|
466
|
+
- **Tầng trình duyệt:** `color-scheme` đi cùng theme (khối `:root` / `.dark` của `tokens.css`
|
|
467
|
+
đã có), để thanh cuộn gốc, ô chọn ngày, autofill, `<select>` vẽ đúng bản. `theme-color` hai thẻ
|
|
468
|
+
theo `media` là tuỳ chọn.
|
|
469
|
+
- **`dark:` phải theo class, không theo máy.** Tailwind v4 mặc định `dark:` chạy theo
|
|
470
|
+
`prefers-color-scheme`; thiếu dòng `@custom-variant dark` trong `tokens.css` thì bấm Tối trên
|
|
471
|
+
máy đang sáng ra token tối, class `dark:` sáng.
|
|
472
|
+
|
|
473
|
+
Next.js: `next-themes`.
|
|
474
|
+
|
|
475
|
+
```tsx
|
|
476
|
+
// app/layout.tsx
|
|
477
|
+
<html lang="vi" suppressHydrationWarning>
|
|
478
|
+
<body>
|
|
479
|
+
<ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
|
|
480
|
+
{children}
|
|
481
|
+
</ThemeProvider>
|
|
482
|
+
</body>
|
|
483
|
+
</html>
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
Icon button trên header đọc `resolvedTheme` sau khi mount (trước đó render icon cố định), không
|
|
487
|
+
thì lệch HTML lúc hydrate. Không Next thì script nội tuyến đầu `<head>` đọc `localStorage` +
|
|
488
|
+
`matchMedia("(prefers-color-scheme: dark)")` rồi gắn `.dark`, và nghe đổi của máy khi đang Hệ thống.
|
|
489
|
+
|
|
490
|
+
**M32. Ở nền tối, vài thứ đổi cách vẽ chứ không chỉ đổi màu.**
|
|
491
|
+
|
|
492
|
+
| Thứ | Nền tối | Vì sao |
|
|
493
|
+
| --- | --- | --- |
|
|
494
|
+
| Ô nhập, select, textarea | **giữ viền** `--border-strong`, nền phủ mờ `dark:bg-white/4` | bỏ viền chỉ còn nền mờ là mất ranh giới vùng gõ; mọi bộ lớn đều giữ viền |
|
|
495
|
+
| Nút đảo màu (nền `--foreground`, không phải `--primary`) | về variant `secondary`, hoặc nền trắng mờ + viền | đảo thẳng ra khối trắng chói, nặng hơn cả nút chính. Chỉ màu nhấn được thành khối sáng |
|
|
496
|
+
| Tooltip | **đảo màu**: `bg-foreground text-background` | nền tối thì tooltip sáng, nổi khỏi mọi tầng |
|
|
497
|
+
| Lớp phủ sau modal, panel, sidebar trượt | **`bg-black/…`** ở cả hai theme, không `bg-foreground/…` | `foreground` đảo sang gần trắng, lớp phủ thành màn sương sáng che trang |
|
|
498
|
+
| Mục đang trỏ, đang chọn viết bằng điều kiện JS (`isHighlighted && …`, `isActive && …`) | `bg-item-hover` (trỏ), `bg-secondary` (chọn), như class `hover:` | `bg-background` trong điều kiện JS lọt khỏi mọi phép grep `hover:`; ở nền tối thành lỗ khoét trong dropdown (ô chọn, bảng lệnh) |
|
|
499
|
+
| Chữ, icon màu trạng thái không nền (`text-amber-700`, `text-red-700`, `text-emerald-700`) | kèm `dark:text-*-400` cùng sắc (token `--warning`, `--error-strong`, `--success` đã lật sẵn) | `-700` trên nền tối chỉ 2,9–3,7:1, dưới ngưỡng chữ (`M7`) |
|
|
500
|
+
| Nền nhạt `-50`, `-100` (badge, banner, chip, ô đáp án) | màu `-500` phủ 10–20%, chữ `-300`/`-400` | nền `-50` sang tối là khối trắng hồng giữa màn đen. Màu mang nghĩa đã có sẵn trong `tokens.css` |
|
|
501
|
+
| Gradient, vệt màu loang trang trí | tắt (`dark:hidden`) hoặc thay bản tối | loang màu sáng trên nền tối là chói |
|
|
502
|
+
| Biểu đồ | cột, vùng tô dùng `--chart-fill` (màu nhấn 70% ở nền tối); thang phân loại có bản tối (`components/charts.md`) | cột màu nhấn gần trắng 100% là khối chói nhất màn; xám "Khác" `slate-300` thành nổi nhất |
|
|
503
|
+
| Ảnh nền trắng, logo | ảnh cần bản tối thì `<picture><source media="(prefers-color-scheme: dark)">` hoặc hai ảnh theo class; logo chuyển bản sáng | |
|
|
504
|
+
| Avatar, ảnh nhỏ | vòng `ring-1 ring-border` | ảnh tối tan vào nền |
|
|
505
|
+
|
|
506
|
+
**M33. Vùng khoá theme khai lại token tại chỗ; thư viện bên ngoài đọc token.**
|
|
507
|
+
|
|
508
|
+
- **Vùng luôn sáng / luôn tối** (trang chia sẻ nền kem, hero luôn tối): class `.force-light` /
|
|
509
|
+
`.force-dark` trên khung ngoài cùng, `tokens.css` đã cho hai class này đọc lại đúng bộ token,
|
|
510
|
+
mọi component con đổi theo mà không truyền gì xuống. `@custom-variant dark` của `tokens.css`
|
|
511
|
+
đã loại vùng `.force-light`, nên `dark:` không rò vào. Cả trang khoá theme thì `next-themes`
|
|
512
|
+
`forcedTheme`, và ẩn nút đổi theme ở trang đó.
|
|
513
|
+
- **Thư viện bên ngoài đọc token**, không để màu mặc định của nó: toast, hộp thoại, thanh tiến
|
|
514
|
+
trình chuyển trang, trình soạn thảo, lịch. Đè style bằng `var(--surface-overlay)`,
|
|
515
|
+
`var(--border)`, `var(--foreground)`, `var(--primary)` là tự lật theo theme. Để màu mặc định
|
|
516
|
+
của thư viện thì nền tối còn một hộp trắng giữa màn (chỉ người bật tối mới thấy, nên rất lâu
|
|
517
|
+
mới có người báo).
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## Token
|
|
522
|
+
|
|
523
|
+
**M24. Mọi màu đi qua token đặt tên theo vai trò.** Không rải mã hex trong markup.
|
|
524
|
+
|
|
525
|
+
**M25. Một khái niệm một token.** Mọi đường kẻ và viền dùng chung một tên. Đừng
|
|
526
|
+
chỗ thì `divide-border` chỗ thì `border-muted/25`.
|
|
527
|
+
|
|
528
|
+
**M26. Mọi màu và font gom vào khối đánh dấu ở đầu file.** Ngoài khối đó không
|
|
529
|
+
được xuất hiện mã màu. Có dark mode thì màu nhấn có **hai chỗ**: `:root` và
|
|
530
|
+
`.dark`. Thiếu chỗ thứ hai là màu nhấn tàng hình trên nền tối.
|
|
531
|
+
|
|
532
|
+
**M27. Khối đổi thương hiệu phải chép nguyên văn từ `tokens.css`.** Mở file ra
|
|
533
|
+
copy, không gõ lại từ trí nhớ, không tự nghĩ mã hex. Đã có lần AI tự chế ra
|
|
534
|
+
`#a99cff` tím và `#fa99cff0d` sai cú pháp. Cần màu khác thì thay đúng một dòng.
|
|
535
|
+
|
|
536
|
+
**M28. Lúc giao phải chỉ rõ chỗ đổi thương hiệu.** Một dòng: "đổi màu nhấn ở dòng
|
|
537
|
+
14, font ở dòng 8". Có dark mode thì nói rõ là hai chỗ.
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
|
|
541
|
+
## Card đứng một mình
|
|
542
|
+
|
|
543
|
+
**M29. Màn chỉ có đúng MỘT card giữa trang trống thì bỏ viền. Chìm quá thì dùng
|
|
544
|
+
bóng rất mờ, không phải viền đậm hơn.**
|
|
545
|
+
|
|
546
|
+
*Gu flat — phong cách khác đã chọn theo `P1` thì xem `P2` trong `references/styles.md`.*
|
|
547
|
+
|
|
548
|
+
Đăng nhập, đăng ký, quên mật khẩu, màn onboarding một khối. (Trang lỗi không dùng card, xem
|
|
549
|
+
"Trang lỗi" ở `layouts/app.md`.) Đặc điểm
|
|
550
|
+
chung: **không có khối thứ hai nào để mà tách khỏi.**
|
|
551
|
+
|
|
552
|
+
`M13` bắt viền vì viền là thứ phân định ranh giới giữa các khối nằm cạnh nhau.
|
|
553
|
+
Trên màn chỉ có một card, không còn việc đó để làm — đường viền lúc này chỉ là
|
|
554
|
+
một nét vẽ quanh hộp, và nó làm card trông như một cái khung chờ nội dung.
|
|
555
|
+
|
|
556
|
+
Thứ tự thử, dừng ngay khi đủ:
|
|
557
|
+
|
|
558
|
+
1. **Không viền, không bóng.** Nền trang xám (`--background`) + card trắng (`--surface`) đã đủ chênh để đọc ra ranh giới. Đây là mặc định.
|
|
559
|
+
2. **Chìm quá thì thêm bóng rất mờ.** Cỡ `shadow-sm` của Tailwind — mờ đến mức chỉ cảm thấy chứ không nhìn ra. Card lúc này đang **nổi trên** một trang trống, nên nó hợp tinh thần "lớp nổi" của `M15` hơn là khối nằm trong trang.
|
|
560
|
+
3. **Không bao giờ dùng cả viền lẫn bóng.** Hai thứ cùng làm một việc. Có bóng rồi mà vẫn thấy cần viền thì bóng đang đặt sai, không phải thiếu viền.
|
|
561
|
+
|
|
562
|
+
**Đừng chữa cháy bằng viền đậm hơn.** Thấy card chìm mà tăng độ đậm của viền là
|
|
563
|
+
đi ngược `M14` — viền đậm lên thì cái hộp hiện ra rõ hơn nội dung bên trong nó.
|
|
564
|
+
|
|
565
|
+
Có từ hai card trở lên trên màn thì quay về `M13` như thường.
|
|
566
|
+
|
|
567
|
+
**Nền tối giữ y như vậy, không thêm viền dù `M23` nói viền gánh việc tách khối.** Card tối chênh nền
|
|
568
|
+
1.07:1, ngang bản sáng 1.10:1, đủ đọc ra ranh giới. Đã thử trên màn đăng nhập: viền
|
|
569
|
+
`--border` biến card thành khung, bóng `--elevation-popover` gần như không thấy. `M23` nói về các khối
|
|
570
|
+
nằm cạnh nhau trong trang, không về một card một mình.
|
|
571
|
+
|
|
572
|
+
---
|
|
573
|
+
|
|
574
|
+
## Hai sắc đỏ
|
|
575
|
+
|
|
576
|
+
**M30. Đỏ có hai sắc cho hai việc. Không dùng lẫn, không thêm sắc thứ ba.**
|
|
577
|
+
|
|
578
|
+
| | Sắc | Việc | Khi nào hiện | Ở đâu |
|
|
579
|
+
| --- | --- | --- | --- | --- |
|
|
580
|
+
| **Lỗi** | `red` | *Đã có gì đó sai*, phải sửa mới đi tiếp được | Sau khi người dùng làm sai | Ô nhập, câu lỗi, banner lỗi máy chủ |
|
|
581
|
+
| **Nguy hiểm** | `rose` | *Bấm vào thì không lấy lại được* | Nút: luôn hiện, nền mờ. Mục menu: chỉ lúc rê vào (`I4`) | Xoá, huỷ tài khoản, rời nhóm, đăng xuất (mục menu) |
|
|
582
|
+
|
|
583
|
+
**Vì sao tách.** Hai việc khác nhau về thời điểm và mức nặng:
|
|
584
|
+
|
|
585
|
+
- **Lỗi** là chuyện **đã xảy ra**. Nó phải nhận ra ngay, không lẫn với gì — nên dùng `red`, sắc đỏ chuẩn mà ai nhìn cũng đọc ra "sai".
|
|
586
|
+
- **Nguy hiểm** là **lời nhắc trước khi bấm**, hiện lên chỉ vì chuột đi ngang qua. Chưa có gì sai cả. Nếu nó đỏ y như lỗi thì mỗi lần rê chuột qua menu, người dùng thấy như vừa làm hỏng gì — nên dùng `rose`, ngả hồng hơn, mềm hơn một bậc. Cùng tinh thần `I4`: nút xoá không hét vào mặt người dùng.
|
|
587
|
+
|
|
588
|
+
**Phép thử khi phân vân:** *người dùng đã làm sai gì chưa?* Rồi → `red`. Chưa,
|
|
589
|
+
chỉ đang sắp bấm → `rose`.
|
|
590
|
+
|
|
591
|
+
### Bậc dùng — đừng tự chế
|
|
592
|
+
|
|
593
|
+
Mỗi ô ghi class Tailwind, token CSS trong ngoặc. Hai cách ra cùng một màu
|
|
594
|
+
(`tokens.css`), dự án không có Tailwind thì dùng token.
|
|
595
|
+
|
|
596
|
+
| Việc | Lỗi (`red`) | Nguy hiểm (`rose`) |
|
|
597
|
+
| --- | --- | --- |
|
|
598
|
+
| Chữ, icon | câu lỗi dưới ô: `text-red-600` (`--error-text`) — `red-500` trên nền trắng chỉ 3.8:1, trượt 4.5:1. Dấu `*` trường bắt buộc cũng sắc này (ngoại lệ `M9`) | `text-rose-700` (`--danger`) (nút luôn hiện, mục menu lúc rê vào) — `rose-500` trên nền mờ chỉ 3.2:1 |
|
|
599
|
+
| Viền | `border-red-500` (`--error`) | — *(không có viền đỏ)* |
|
|
600
|
+
| Nền mờ | `ring-red-500/10` (`--error-ring`) quanh ô nhập lỗi **đang focus** | nút: `bg-rose-500/10` (`--danger-bg`), rê vào `/15` (`--danger-bg-hover`) · mục menu: `hover:bg-rose-500/10` |
|
|
601
|
+
| Banner | `bg-red-50` (`--error-bg`) · `border-red-200` (`--error-border`) · tiêu đề `red-700` (`--error-strong`), mô tả `text-foreground/80` (`components/banner.md`) | — *(không có banner)* |
|
|
602
|
+
|
|
603
|
+
Ô "—" là **cố ý trống**: hành động nguy hiểm không bao giờ có viền đỏ hay banner
|
|
604
|
+
đỏ. Thấy mình định viết `border-rose-*` là đang biến lời nhắc thành cảnh báo.
|
|
605
|
+
|
|
606
|
+
**Không có sắc thứ ba.** Không `pink`, không `orange-red`, không đỏ tuỳ chế
|
|
607
|
+
`#e53e3e` để **báo trạng thái**. Cần một kiểu "nhẹ hơn lỗi nhưng vẫn cần chú ý"
|
|
608
|
+
thì đó là **hổ phách** (`M4`), không phải một sắc đỏ mới.
|
|
609
|
+
|
|
610
|
+
**Phạm vi: `M30` chỉ áp cho màu mang NGHĨA.** Màu nhận diện — nền avatar chữ cái
|
|
611
|
+
đầu, icon workspace — không báo gì cả, nên không thuộc luật này. Cùng lý do với
|
|
612
|
+
ngoại lệ của `M12`: ở đó màu đóng vai ảnh, không đóng vai trạng thái. Nhưng để
|
|
613
|
+
không ai phải phân vân, bộ màu avatar **không dùng `red` hay `rose`** — xem
|
|
614
|
+
`references/components/avatar.md`.
|
|
615
|
+
|
|
616
|
+
### Đổi thương hiệu
|
|
617
|
+
|
|
618
|
+
Thương hiệu có đỏ riêng thì sửa khối `--danger*` / `--error*` trong `tokens.css`,
|
|
619
|
+
và đổi **cả hai nhóm cùng lúc**, giữ khoảng cách giữa chúng: lỗi đậm và chuẩn
|
|
620
|
+
hơn, nguy hiểm mềm hơn. Đổi một nhóm mà quên nhóm kia thì hai việc lại trông như
|
|
621
|
+
một. Dự án dùng class Tailwind thì phải map lại trong `@theme` hoặc thay class,
|
|
622
|
+
sửa token thôi không đổi được `text-rose-700` (xem `tailwind-v4-traps.md`).
|
|
623
|
+
Đổi xong đo lại tương phản: chữ trên nền phải từ 4.5:1.
|