@starci/skills 1.1.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.
Files changed (321) hide show
  1. package/INDEX.md +74 -0
  2. package/INDEX.vi.md +75 -0
  3. package/README.md +44 -0
  4. package/README.vi.md +43 -0
  5. package/SKILL.md +135 -0
  6. package/SKILL.vi.md +128 -0
  7. package/alias/INDEX.md +104 -0
  8. package/alias/INDEX.vi.md +104 -0
  9. package/alias/alias.json +297 -0
  10. package/bin/starci-skills.mjs +219 -0
  11. package/knowledge/INDEX.md +22 -0
  12. package/knowledge/INDEX.vi.md +22 -0
  13. package/knowledge/grammars/starci/DNA.md +146 -0
  14. package/knowledge/grammars/starci/DNA.vi.md +146 -0
  15. package/knowledge/grammars/starci/INDEX.md +25 -0
  16. package/knowledge/grammars/starci/INDEX.vi.md +25 -0
  17. package/knowledge/grammars/starci/family.md +50 -0
  18. package/knowledge/grammars/starci/family.vi.md +50 -0
  19. package/knowledge/grammars/starci/idioms.md +120 -0
  20. package/knowledge/grammars/starci/idioms.vi.md +121 -0
  21. package/knowledge/grammars/starci/playbook.md +36 -0
  22. package/knowledge/grammars/starci/playbook.vi.md +35 -0
  23. package/knowledge/patterns/be/INDEX.md +29 -0
  24. package/knowledge/patterns/be/INDEX.vi.md +29 -0
  25. package/knowledge/patterns/be/comment.md +80 -0
  26. package/knowledge/patterns/be/comment.vi.md +80 -0
  27. package/knowledge/patterns/be/error.md +86 -0
  28. package/knowledge/patterns/be/error.vi.md +86 -0
  29. package/knowledge/patterns/be/folder.md +87 -0
  30. package/knowledge/patterns/be/folder.vi.md +86 -0
  31. package/knowledge/patterns/be/function.md +80 -0
  32. package/knowledge/patterns/be/function.vi.md +80 -0
  33. package/knowledge/patterns/be/imports.md +79 -0
  34. package/knowledge/patterns/be/imports.vi.md +79 -0
  35. package/knowledge/patterns/be/naming.md +87 -0
  36. package/knowledge/patterns/be/naming.vi.md +87 -0
  37. package/knowledge/patterns/be/test.md +79 -0
  38. package/knowledge/patterns/be/test.vi.md +79 -0
  39. package/knowledge/patterns/be/typing.md +73 -0
  40. package/knowledge/patterns/be/typing.vi.md +73 -0
  41. package/knowledge/patterns/fe/INDEX.md +29 -0
  42. package/knowledge/patterns/fe/INDEX.vi.md +29 -0
  43. package/knowledge/patterns/fe/comment.md +70 -0
  44. package/knowledge/patterns/fe/comment.vi.md +70 -0
  45. package/knowledge/patterns/fe/error.md +61 -0
  46. package/knowledge/patterns/fe/error.vi.md +61 -0
  47. package/knowledge/patterns/fe/folder.md +98 -0
  48. package/knowledge/patterns/fe/folder.vi.md +95 -0
  49. package/knowledge/patterns/fe/function.md +70 -0
  50. package/knowledge/patterns/fe/function.vi.md +70 -0
  51. package/knowledge/patterns/fe/imports.md +87 -0
  52. package/knowledge/patterns/fe/imports.vi.md +87 -0
  53. package/knowledge/patterns/fe/naming.md +77 -0
  54. package/knowledge/patterns/fe/naming.vi.md +77 -0
  55. package/knowledge/patterns/fe/test.md +73 -0
  56. package/knowledge/patterns/fe/test.vi.md +73 -0
  57. package/knowledge/patterns/fe/typing.md +67 -0
  58. package/knowledge/patterns/fe/typing.vi.md +67 -0
  59. package/knowledge/ui/INDEX.md +101 -0
  60. package/knowledge/ui/INDEX.vi.md +101 -0
  61. package/knowledge/ui/composition/INDEX.md +60 -0
  62. package/knowledge/ui/composition/INDEX.vi.md +63 -0
  63. package/knowledge/ui/composition/accent.md +73 -0
  64. package/knowledge/ui/composition/accent.vi.md +74 -0
  65. package/knowledge/ui/composition/action.md +55 -0
  66. package/knowledge/ui/composition/action.vi.md +56 -0
  67. package/knowledge/ui/composition/coverage.md +37 -0
  68. package/knowledge/ui/composition/coverage.vi.md +37 -0
  69. package/knowledge/ui/composition/cta.md +79 -0
  70. package/knowledge/ui/composition/cta.vi.md +79 -0
  71. package/knowledge/ui/composition/feedback.md +55 -0
  72. package/knowledge/ui/composition/feedback.vi.md +56 -0
  73. package/knowledge/ui/composition/hierarchy.md +83 -0
  74. package/knowledge/ui/composition/hierarchy.vi.md +82 -0
  75. package/knowledge/ui/composition/layout.md +91 -0
  76. package/knowledge/ui/composition/layout.vi.md +93 -0
  77. package/knowledge/ui/composition/responsive.md +67 -0
  78. package/knowledge/ui/composition/responsive.vi.md +67 -0
  79. package/knowledge/ui/composition/state.md +105 -0
  80. package/knowledge/ui/composition/state.vi.md +106 -0
  81. package/knowledge/ui/presentation/INDEX.md +94 -0
  82. package/knowledge/ui/presentation/INDEX.vi.md +69 -0
  83. package/knowledge/ui/presentation/boundary.md +170 -0
  84. package/knowledge/ui/presentation/boundary.vi.md +169 -0
  85. package/knowledge/ui/presentation/font.md +155 -0
  86. package/knowledge/ui/presentation/font.vi.md +156 -0
  87. package/knowledge/ui/presentation/gap.md +181 -0
  88. package/knowledge/ui/presentation/gap.vi.md +181 -0
  89. package/knowledge/ui/presentation/margin.md +168 -0
  90. package/knowledge/ui/presentation/margin.vi.md +171 -0
  91. package/knowledge/ui/presentation/measure.md +174 -0
  92. package/knowledge/ui/presentation/measure.vi.md +178 -0
  93. package/knowledge/ui/presentation/overflow.md +142 -0
  94. package/knowledge/ui/presentation/overflow.vi.md +142 -0
  95. package/knowledge/ui/presentation/padding.md +293 -0
  96. package/knowledge/ui/presentation/padding.vi.md +292 -0
  97. package/knowledge/ui/presentation/surface.md +166 -0
  98. package/knowledge/ui/presentation/surface.vi.md +166 -0
  99. package/knowledge/ui/presentation/text-flow.md +123 -0
  100. package/knowledge/ui/presentation/text-flow.vi.md +123 -0
  101. package/knowledge/ui/presentation/tone.md +114 -0
  102. package/knowledge/ui/presentation/tone.vi.md +113 -0
  103. package/knowledge/ui/proof/INDEX.md +55 -0
  104. package/knowledge/ui/proof/INDEX.vi.md +55 -0
  105. package/knowledge/ui/proof/accessibility.md +67 -0
  106. package/knowledge/ui/proof/accessibility.vi.md +68 -0
  107. package/knowledge/ui/proof/contrast.md +58 -0
  108. package/knowledge/ui/proof/contrast.vi.md +57 -0
  109. package/knowledge/ui/proof/focus.md +76 -0
  110. package/knowledge/ui/proof/focus.vi.md +76 -0
  111. package/knowledge/ui/proof/motion.md +63 -0
  112. package/knowledge/ui/proof/motion.vi.md +64 -0
  113. package/knowledge/ui/proof/render-truth.md +63 -0
  114. package/knowledge/ui/proof/render-truth.vi.md +63 -0
  115. package/operators/INDEX.md +199 -0
  116. package/operators/INDEX.vi.md +199 -0
  117. package/operators/architecture-decide/errors.json +115 -0
  118. package/operators/architecture-decide/operator.json +20 -0
  119. package/operators/architecture-decide/operator.md +133 -0
  120. package/operators/architecture-decide/operator.vi.md +130 -0
  121. package/operators/architecture-decide/self-test.mjs +221 -0
  122. package/operators/architecture-decide/validate.mjs +112 -0
  123. package/operators/backend-source-apply/errors.json +66 -0
  124. package/operators/backend-source-apply/operator.json +20 -0
  125. package/operators/backend-source-apply/operator.md +189 -0
  126. package/operators/backend-source-apply/operator.vi.md +185 -0
  127. package/operators/backend-source-apply/self-test.mjs +257 -0
  128. package/operators/backend-source-apply/validate.mjs +240 -0
  129. package/operators/business-decide/errors.json +78 -0
  130. package/operators/business-decide/operator.json +20 -0
  131. package/operators/business-decide/operator.md +176 -0
  132. package/operators/business-decide/operator.vi.md +174 -0
  133. package/operators/business-decide/self-test.mjs +225 -0
  134. package/operators/business-decide/validate.mjs +277 -0
  135. package/operators/content-generate/errors.json +106 -0
  136. package/operators/content-generate/operator.json +21 -0
  137. package/operators/content-generate/operator.md +155 -0
  138. package/operators/content-generate/operator.vi.md +155 -0
  139. package/operators/content-generate/self-test.mjs +288 -0
  140. package/operators/content-generate/validate.mjs +196 -0
  141. package/operators/errors.json +178 -0
  142. package/operators/frontend-direction-decide/errors.json +135 -0
  143. package/operators/frontend-direction-decide/operator.json +21 -0
  144. package/operators/frontend-direction-decide/operator.md +167 -0
  145. package/operators/frontend-direction-decide/operator.vi.md +167 -0
  146. package/operators/frontend-direction-decide/self-test.mjs +226 -0
  147. package/operators/frontend-direction-decide/validate.mjs +146 -0
  148. package/operators/frontend-presentation-resolve/errors.json +42 -0
  149. package/operators/frontend-presentation-resolve/operator.json +19 -0
  150. package/operators/frontend-presentation-resolve/operator.md +142 -0
  151. package/operators/frontend-presentation-resolve/operator.vi.md +140 -0
  152. package/operators/frontend-presentation-resolve/self-test.mjs +163 -0
  153. package/operators/frontend-presentation-resolve/validate.mjs +143 -0
  154. package/operators/frontend-source-apply/errors.json +30 -0
  155. package/operators/frontend-source-apply/operator.json +20 -0
  156. package/operators/frontend-source-apply/operator.md +127 -0
  157. package/operators/frontend-source-apply/operator.vi.md +126 -0
  158. package/operators/frontend-source-apply/self-test.mjs +214 -0
  159. package/operators/frontend-source-apply/validate.mjs +133 -0
  160. package/operators/frontend-surface-audit/errors.json +5 -0
  161. package/operators/frontend-surface-audit/operator.json +22 -0
  162. package/operators/frontend-surface-audit/operator.md +121 -0
  163. package/operators/frontend-surface-audit/operator.vi.md +121 -0
  164. package/operators/frontend-surface-audit/self-test.mjs +185 -0
  165. package/operators/frontend-surface-audit/validate.mjs +127 -0
  166. package/operators/git-publish/errors.json +54 -0
  167. package/operators/git-publish/operator.json +20 -0
  168. package/operators/git-publish/operator.md +180 -0
  169. package/operators/git-publish/operator.vi.md +178 -0
  170. package/operators/git-publish/self-test.mjs +177 -0
  171. package/operators/git-publish/validate.mjs +106 -0
  172. package/operators/platform-operate/errors.json +90 -0
  173. package/operators/platform-operate/operator.json +22 -0
  174. package/operators/platform-operate/operator.md +162 -0
  175. package/operators/platform-operate/operator.vi.md +160 -0
  176. package/operators/platform-operate/self-test.mjs +202 -0
  177. package/operators/platform-operate/validate.mjs +193 -0
  178. package/operators/quality-verify/errors.json +54 -0
  179. package/operators/quality-verify/operator.json +20 -0
  180. package/operators/quality-verify/operator.md +185 -0
  181. package/operators/quality-verify/operator.vi.md +177 -0
  182. package/operators/quality-verify/self-test.mjs +210 -0
  183. package/operators/quality-verify/validate.mjs +197 -0
  184. package/operators/release-deploy/errors.json +158 -0
  185. package/operators/release-deploy/operator.json +23 -0
  186. package/operators/release-deploy/operator.md +183 -0
  187. package/operators/release-deploy/operator.vi.md +181 -0
  188. package/operators/release-deploy/self-test.mjs +235 -0
  189. package/operators/release-deploy/validate.mjs +129 -0
  190. package/operators/uat-verify/errors.json +78 -0
  191. package/operators/uat-verify/operator.json +25 -0
  192. package/operators/uat-verify/operator.md +162 -0
  193. package/operators/uat-verify/operator.vi.md +161 -0
  194. package/operators/uat-verify/self-test.mjs +270 -0
  195. package/operators/uat-verify/validate.mjs +202 -0
  196. package/operators/workspace-bind/errors.json +90 -0
  197. package/operators/workspace-bind/operator.json +21 -0
  198. package/operators/workspace-bind/operator.md +148 -0
  199. package/operators/workspace-bind/operator.vi.md +148 -0
  200. package/operators/workspace-bind/self-test.mjs +205 -0
  201. package/operators/workspace-bind/validate.mjs +152 -0
  202. package/package.json +50 -0
  203. package/readiness/initialization/workspaces/commit-policy.json +91 -0
  204. package/readiness/initialization/workspaces/config.schema.json +35 -0
  205. package/readiness/initialization/workspaces/device-state.schema.json +54 -0
  206. package/readiness/initialization/workspaces/local-route.schema.json +206 -0
  207. package/readiness/initialization/workspaces/portable-route.schema.json +200 -0
  208. package/resources/INDEX.md +96 -0
  209. package/resources/INDEX.vi.md +99 -0
  210. package/resources/agents/profiles/claude.json +131 -0
  211. package/resources/agents/profiles/openai.json +131 -0
  212. package/resources/orchestrator.json +71 -0
  213. package/resources/tools.json +85 -0
  214. package/routing.json +272 -0
  215. package/scripts/alias-registry.mjs +31 -0
  216. package/scripts/device-state.mjs +497 -0
  217. package/scripts/device-state.spec.mjs +18 -0
  218. package/scripts/errors-registry.mjs +68 -0
  219. package/scripts/generate-alias-doc.mjs +66 -0
  220. package/scripts/generate-grammar-dna.mjs +387 -0
  221. package/scripts/generate-operators-index.mjs +95 -0
  222. package/scripts/generate-presentation-owned.mjs +681 -0
  223. package/scripts/install-cli.spec.mjs +74 -0
  224. package/scripts/json-schema.mjs +94 -0
  225. package/scripts/operator-md.mjs +96 -0
  226. package/scripts/run-operator-self-tests.mjs +36 -0
  227. package/scripts/validate-alias.mjs +165 -0
  228. package/scripts/validate-defaults.mjs +72 -0
  229. package/scripts/validate-knowledge-citations.mjs +90 -0
  230. package/scripts/validate-operator.mjs +125 -0
  231. package/scripts/validate-request.mjs +80 -0
  232. package/scripts/validate-resources.mjs +117 -0
  233. package/scripts/validate-response.mjs +141 -0
  234. package/scripts/validate-routing.mjs +91 -0
  235. package/scripts/validate-step.mjs +50 -0
  236. package/scripts/validate-templates.mjs +226 -0
  237. package/scripts/validate-templates.spec.mjs +144 -0
  238. package/scripts/validate-workflows.mjs +106 -0
  239. package/scripts/workspace-portable.mjs +389 -0
  240. package/scripts/workspace-portable.spec.mjs +246 -0
  241. package/templates/README.md +47 -0
  242. package/templates/README.vi.md +45 -0
  243. package/templates/changes.example.md +27 -0
  244. package/templates/grammars.template.md +34 -0
  245. package/templates/kinds/architecture-decision.contract.json +14 -0
  246. package/templates/kinds/architecture-decision.skeleton.md +52 -0
  247. package/templates/kinds/backend-source-application.contract.json +10 -0
  248. package/templates/kinds/backend-source-application.skeleton.md +34 -0
  249. package/templates/kinds/business-promise-authority.contract.json +13 -0
  250. package/templates/kinds/business-promise-authority.skeleton.md +67 -0
  251. package/templates/kinds/capture.schema.json +33 -0
  252. package/templates/kinds/changes.contract.json +10 -0
  253. package/templates/kinds/changes.skeleton.md +26 -0
  254. package/templates/kinds/checks.schema.json +44 -0
  255. package/templates/kinds/claims.schema.json +165 -0
  256. package/templates/kinds/conformance.schema.json +15 -0
  257. package/templates/kinds/content-brief.contract.json +12 -0
  258. package/templates/kinds/content-brief.skeleton.md +45 -0
  259. package/templates/kinds/content-generation-receipt.contract.json +11 -0
  260. package/templates/kinds/content-generation-receipt.skeleton.md +40 -0
  261. package/templates/kinds/content-review.contract.json +11 -0
  262. package/templates/kinds/content-review.skeleton.md +45 -0
  263. package/templates/kinds/contract.schema.json +28 -0
  264. package/templates/kinds/coverage-matrix.schema.json +86 -0
  265. package/templates/kinds/coverage.schema.json +62 -0
  266. package/templates/kinds/current-state.schema.json +40 -0
  267. package/templates/kinds/delta.schema.json +95 -0
  268. package/templates/kinds/e2e.schema.json +63 -0
  269. package/templates/kinds/frontend-direction-decision.contract.json +93 -0
  270. package/templates/kinds/frontend-direction-decision.skeleton.md +68 -0
  271. package/templates/kinds/frontend-presentation-resolution.contract.json +11 -0
  272. package/templates/kinds/frontend-presentation-resolution.skeleton.md +32 -0
  273. package/templates/kinds/frontend-source-application.contract.json +10 -0
  274. package/templates/kinds/frontend-source-application.skeleton.md +30 -0
  275. package/templates/kinds/frontend-surface-audit.contract.json +52 -0
  276. package/templates/kinds/frontend-surface-audit.skeleton.md +32 -0
  277. package/templates/kinds/gate-result.schema.json +64 -0
  278. package/templates/kinds/git-publication.contract.json +82 -0
  279. package/templates/kinds/git-publication.skeleton.md +61 -0
  280. package/templates/kinds/independent-critique.contract.json +9 -0
  281. package/templates/kinds/independent-critique.skeleton.md +28 -0
  282. package/templates/kinds/inventory.schema.json +35 -0
  283. package/templates/kinds/model.schema.json +99 -0
  284. package/templates/kinds/mutations.schema.json +65 -0
  285. package/templates/kinds/platform-operation-receipt.contract.json +76 -0
  286. package/templates/kinds/platform-operation-receipt.skeleton.md +54 -0
  287. package/templates/kinds/probes.schema.json +130 -0
  288. package/templates/kinds/proof.schema.json +17 -0
  289. package/templates/kinds/quality-verification.contract.json +87 -0
  290. package/templates/kinds/quality-verification.skeleton.md +58 -0
  291. package/templates/kinds/release-deployment.contract.json +85 -0
  292. package/templates/kinds/release-deployment.skeleton.md +67 -0
  293. package/templates/kinds/route.schema.json +293 -0
  294. package/templates/kinds/stack-model.schema.json +90 -0
  295. package/templates/kinds/uat-capture.schema.json +35 -0
  296. package/templates/kinds/uat-flow-verification.contract.json +11 -0
  297. package/templates/kinds/uat-flow-verification.skeleton.md +47 -0
  298. package/templates/kinds/uat-snapshot.schema.json +96 -0
  299. package/templates/kinds/uat-verdicts.schema.json +41 -0
  300. package/templates/kinds/ui-coverage.schema.json +77 -0
  301. package/templates/kinds/verdicts.schema.json +39 -0
  302. package/templates/kinds/workspace-route-binding.contract.json +12 -0
  303. package/templates/kinds/workspace-route-binding.skeleton.md +60 -0
  304. package/templates/kinds/writes.schema.json +37 -0
  305. package/templates/operator.template.md +73 -0
  306. package/templates/patterns.template.md +31 -0
  307. package/templates/step/request.schema.json +42 -0
  308. package/templates/step/response.schema.json +144 -0
  309. package/templates/ui-composition.template.md +36 -0
  310. package/templates/ui-presentation.template.md +57 -0
  311. package/templates/ui-proof.template.md +34 -0
  312. package/workflows/README.md +37 -0
  313. package/workflows/README.vi.md +37 -0
  314. package/workflows/backend-feature.json +59 -0
  315. package/workflows/content-unit.json +19 -0
  316. package/workflows/frontend-new-surface.json +81 -0
  317. package/workflows/frontend-reconstruct.json +67 -0
  318. package/workflows/frontend-refine.json +67 -0
  319. package/workflows/frontend-with-uat.json +78 -0
  320. package/workflows/full-feature.json +104 -0
  321. package/workflows/release.json +29 -0
@@ -0,0 +1,292 @@
1
+ # Padding presentation
2
+
3
+ File này trả lời đúng một câu hỏi: một ranh giới do ứng dụng sở hữu thì cách nội dung của nó bao xa.
4
+
5
+ Bước composition đã chọn xong cây DOM và các Grammar object. Padding presentation chỉ giải quyết
6
+ phần inset của page, section và container do ứng dụng sở hữu. Inset bên trong một card, input,
7
+ button hay bất kỳ Grammar object nào khác đều thuộc về Grammar.
8
+
9
+ ## Thang giá trị
10
+
11
+ `COMMON_SPACING_SCALE` là thang đóng. Số của rule là thứ tự trên thang đó. Nó không phải số bậc
12
+ Tailwind, và hai số này lệch nhau từ PADDING-5 trở đi.
13
+
14
+ | Rule | Class | Giá trị | Token Common |
15
+ | --- | --- | --- | --- |
16
+ | PADDING-0 | `p-0` | `0` | không có |
17
+ | PADDING-1 | `p-1` | `.25rem` | không có |
18
+ | PADDING-2 | `p-2` | `.5rem` | không có |
19
+ | PADDING-3 | `p-3` | `.75rem` | không có |
20
+ | PADDING-4 | `p-4` | `1rem` | không có |
21
+ | PADDING-5 | `p-6` | `1.5rem` | không có |
22
+ | PADDING-6 | `p-8` | `2rem` | không có |
23
+
24
+ Common không publish token padding nào. Các inset bên dưới chỉ đến được qua chính component sở hữu
25
+ chúng, chứ không qua một biến mà ứng dụng được phép đặt.
26
+
27
+ Page inset là ngoại lệ duy nhất và không nằm trên thang này: `--grammar-page-inset` cho ra
28
+ `clamp(1rem, 3vw, 2rem)`, một giá trị responsive do `PageContainer` sở hữu. Ứng dụng không bao giờ
29
+ dựng lại nó bằng một class cố định.
30
+
31
+ Các giá trị rem chỉ quy ra `0 / 4 / 8 / 12 / 16 / 24 / 32` pixel CSS khi root font size tính được là
32
+ `16px`. Khi kiểm tra lúc chạy thì dùng `expectedPx = remFactor * observedRootFontPx`.
33
+
34
+ ## Owner
35
+
36
+ Mỗi case gọi tên ai sở hữu inset. Owner quyết định ứng dụng có được viết class hay không.
37
+
38
+ | Owner | Nghĩa | Ứng dụng viết |
39
+ | --- | --- | --- |
40
+ | `App` | Ranh giới thuộc về ứng dụng | Viết class |
41
+ | Tên component | Common đã áp inset bên trong component đó | Không viết gì, chỉ ghép component |
42
+ | `—` | Common chưa có đường dùng công khai cho ranh giới này | Viết class, ghi nhận là workaround |
43
+
44
+ Viết class ở chỗ owner là một component chính là `APP_REIMPLEMENTATION`. Thò vào ruột một Grammar
45
+ component bằng selector hoặc bằng class truyền vào để đổi inset của nó là `APP_OVERRIDE`.
46
+
47
+ ## Padding mà Common đã sở hữu
48
+
49
+ Sinh từ claim của `@grammar/core` bằng `scripts/generate-presentation-owned.mjs`; muốn đổi thì sửa component, đừng sửa bảng này.
50
+
51
+ | Component | Phần tử hoặc điều kiện | Rule |
52
+ | --- | --- | --- |
53
+ | `ChatWorkspace` | drawer body, hasRail, isCompactRail | PADDING-0 |
54
+ | `ChatWorkspace` | overlay rail, hasRail, isCompactRail | PADDING-4 |
55
+ | `ChatWorkspace` | overlay rail, hasRail, not isCompactRail | PADDING-4 |
56
+ | `ChatWorkspace` | rail trigger boundary, hasRail, isCompactRail | PADDING-2 |
57
+ | `ChatWorkspace` | rail trigger boundary, hasRail, isCompactRail | PADDING-4 |
58
+ | `ChatWorkspace` | rail trigger, hasRail, isCompactRail | PADDING-3 |
59
+ | `EmptyNotice` | root | PADDING-4 |
60
+ | `HorizontalScrollRegion` | root | PADDING-1 |
61
+ | `NavigationFeatureNav` | primary | PADDING-3 |
62
+ | `OtpInput` | root | PADDING-1 |
63
+ | `Rail` | body, inset="content" | PADDING-3 |
64
+ | `Rail` | body, inset="content" | PADDING-5 |
65
+ | `Sidebar` | footer, not (collapsed || footer == null) | PADDING-0 |
66
+ | `Sidebar` | footer, not (collapsed || footer == null) | PADDING-3 |
67
+ | `Sidebar` | group label, group.label!=undefined, not collapsed | PADDING-1 |
68
+ | `Sidebar` | group label, group.label!=undefined, not collapsed | PADDING-2 |
69
+ | `Sidebar` | group label, group.label!=undefined, not collapsed | PADDING-3 |
70
+ | `Sidebar` | header, not (collapsed || header == null) | PADDING-3 |
71
+ | `Sidebar` | icon button wrapper, canToggle | PADDING-2 |
72
+ | `Sidebar` | list box item, not collapsed | PADDING-2 |
73
+ | `Sidebar` | list box item, not collapsed | PADDING-3 |
74
+ | `Sidebar` | list box, collapsed | PADDING-2 |
75
+ | `Sidebar` | list box, not collapsed | PADDING-3 |
76
+ | `StaticStateRow` | root | PADDING-4 |
77
+ | `Subnav` | root | PADDING-3 |
78
+ | `SurfaceAccordionCard` | accordion body | PADDING-8 |
79
+ | `SurfaceAccordionCard` | accordion body wrapper | PADDING-0 |
80
+ | `SurfaceAccordionCard` | accordion trigger | PADDING-4 |
81
+ | `SurfaceCard` | card content, composition!="joined" | PADDING-4 |
82
+ | `SurfaceCard` | card content, composition="joined" | PADDING-0 |
83
+ | `SurfaceListCard` | root | PADDING-0 |
84
+ | `Tabs` | hero tabs tab | PADDING-3 |
85
+ | `Tabs` | root, inset="page" | PADDING-5 |
86
+ | `TextAction` | root, appearance="choice" | PADDING-1 |
87
+ | `TextAction` | root, appearance="choice" | PADDING-2 |
88
+ | `TextAction` | root, appearance="route" | PADDING-2 |
89
+ | `TextAction` | root, appearance="route" | PADDING-3 |
90
+ | `TextAction` | root, appearance="section" | PADDING-2 |
91
+ | `TextAction` | root, appearance="section" | PADDING-3 |
92
+ | `TextAction` | root, appearance="tab" | PADDING-3 |
93
+ | `Tooltip` | content | PADDING-1 |
94
+ | `Tooltip` | content | PADDING-2 |
95
+
96
+ ## Tiếp xúc theo cạnh
97
+
98
+ Một ranh giới không phải lúc nào cũng lấy một giá trị cho cả bốn cạnh. Khi một surface để phẳng và
99
+ các dải con tự sở hữu inset của mình, mỗi cạnh lấy giá trị theo thứ mà nó chạm vào.
100
+
101
+ | Cạnh chạm vào | Giá trị | Rule |
102
+ | --- | --- | --- |
103
+ | Mép ngoài của surface | `1rem` | PADDING-4 |
104
+ | Separator giữa hai dải | `.75rem` | PADDING-3 |
105
+ | Không chạm gì, vì con đã sở hữu mép | `0` | PADDING-0 |
106
+
107
+ Separator đã tự vẽ ra đường ranh, nên cạnh chạm vào nó cần ít khoảng hở hơn cạnh chạm mép trần. Hai
108
+ cạnh ngang của một dải luôn chạm mép ngoài, nên luôn giữ `1rem`.
109
+
110
+ Với một chồng dải thì quy ra `px-4` xuyên suốt, `pt-4` ở dải đầu, `pb-4` ở dải cuối, và `.75rem` cho
111
+ mọi cạnh chạm separator. Một dải nằm giữa hai separator là `px-4 py-3`.
112
+
113
+ Common sở hữu bản thân cái surface phẳng qua `composition="joined"`, thứ đặt inset nội dung về `0`.
114
+ Nó chưa có đường nào cho inset của các dải, nên phần đó vẫn là workaround của ứng dụng.
115
+
116
+ ## PADDING-0 — `p-0` / `0`
117
+
118
+ Ranh giới không đóng góp khoảng cách nào, vì nội dung hoặc phần tử con đã tự mang đường bao của nó.
119
+
120
+ | Case | Dùng khi | Owner | Render |
121
+ | --- | --- | --- | --- |
122
+ | Case 1 | Container chỉ gom nhóm, mà mỗi con đã tự mang ranh giới riêng | `App` | `<section className="p-0">` |
123
+ | Case 2 | Các dải trong surface phải chạy sát mép card | `SurfaceCard` | `composition="joined"` đã đặt sẵn `0` |
124
+ | Case 3 | Danh sách mà mỗi hàng tự sở hữu inset của nó | `SurfaceListCard` | Ghép card, không viết padding |
125
+
126
+ Không phải rule này: ranh giới duy nhất bảo vệ nội dung đọc được thì phải giữ inset. Dùng PADDING-4.
127
+
128
+ ## PADDING-1 — `p-1` / `.25rem`
129
+
130
+ Inset đều nhỏ nhất, dành cho một container cố ý làm nhỏ mà nội dung gần như lấp đầy.
131
+
132
+ | Case | Dùng khi | Owner | Render |
133
+ | --- | --- | --- | --- |
134
+ | Case 1 | Một nhãn hoặc chú giải nhỏ nổi trên canvas do app sở hữu | `App` | `<aside className="p-1">` với `<Text size="xs">` |
135
+ | Case 2 | Vùng cuộn cần chừa chỗ để focus ring không bị cắt | `HorizontalScrollRegion` | Ghép region, không viết padding |
136
+ | Case 3 | Một gợi ý ngắn gắn vào một control | `Tooltip` | Ghép tooltip, không viết padding |
137
+
138
+ ## PADDING-2 — `p-2` / `.5rem`
139
+
140
+ Container nhỏ gọn do app sở hữu, cần khoảng mép rõ ràng nhưng tiết kiệm.
141
+
142
+ | Case | Dùng khi | Owner | Render |
143
+ | --- | --- | --- | --- |
144
+ | Case 1 | Một dải phụ nhỏ mà cả bốn cạnh dùng chung một inset | `App` | `<aside className="p-2">` với `<Text size="sm">` |
145
+ | Case 2 | Một thanh control gọn nằm trên nội dung do app sở hữu | `App` | `<div className="flex items-center gap-2 p-2">` |
146
+
147
+ Không phải rule này: khoảng cách giữa các phần tử con bên trong container thuộc về gap của cha, không
148
+ thuộc padding. Dùng rule GAP tương ứng.
149
+
150
+ ## PADDING-3 — `p-3` / `.75rem`
151
+
152
+ Inset dày dặn nhưng vẫn đọc được, cho một section hoặc container lồng do app sở hữu.
153
+
154
+ | Case | Dùng khi | Owner | Render |
155
+ | --- | --- | --- | --- |
156
+ | Case 1 | Một container lồng bên trong, sở hữu ranh giới khác với ranh giới của cha | `App` | `<aside className="p-3">` nằm trong section `p-4` |
157
+ | Case 2 | Inset ngang của một thanh tab hoặc navigation phụ | `Tabs`, `Subnav` | Ghép component, không viết padding |
158
+ | Case 3 | Cạnh của một dải chạm separator bên trong surface phẳng | `—` | `<div className="px-4 py-3">` nằm giữa hai separator |
159
+ | Case 4 | Cạnh hướng về separator của một ô metric hoặc một hàng danh sách | `—` | `<div className="p-4 pb-3">`, hoặc `pt-3` khi separator nằm trên |
160
+
161
+ Không phải rule này: lồng cùng một inset cho cùng một ranh giới và cùng mục đích là nhân đôi nó.
162
+ Padding của cha và của con cộng dồn trên đường từ mép ngoài tới nội dung cuối.
163
+
164
+ ## PADDING-4 — `p-4` / `1rem`
165
+
166
+ Inset đều chuẩn cho nội dung đọc được bên trong một ranh giới do app sở hữu.
167
+
168
+ | Case | Dùng khi | Owner | Render |
169
+ | --- | --- | --- | --- |
170
+ | Case 1 | Một section của app sở hữu trọn khoảng cách từ đường viền tới nội dung | `App` | `<section className="p-4">` với `<Heading level={2}>` |
171
+ | Case 2 | Nội dung bên trong một card | `SurfaceCard` | Ghép card, không viết padding |
172
+ | Case 3 | Trigger của disclosure và panel xổ ra của nó | `SurfaceAccordionCard` | Ghép card, không viết padding |
173
+ | Case 4 | Một thông báo trạng thái rỗng | `EmptyNotice` | Ghép notice, không viết padding |
174
+ | Case 5 | Một khối code có khung | `FencedCodeBlock` | Ghép khối, không viết padding |
175
+ | Case 6 | Cạnh của một dải chạm mép ngoài của surface phẳng | `—` | `pt-4` ở dải đầu, `pb-4` ở dải cuối |
176
+ | Case 7 | Hai cạnh ngang của mọi dải bên trong surface phẳng | `—` | `px-4` cho mọi dải, không đổi theo vị trí |
177
+
178
+ ## PADDING-5 — `p-6` / `1.5rem`
179
+
180
+ Một section lớn do app sở hữu, cố ý thoáng hơn mức inset chuẩn.
181
+
182
+ | Case | Dùng khi | Owner | Render |
183
+ | --- | --- | --- | --- |
184
+ | Case 1 | Section mà cả bốn cạnh dùng chung một quyết định ranh giới rộng rãi | `App` | `<section className="p-6">` |
185
+ | Case 2 | Khung tab mang inset ngang ở cấp trang | `Tabs` | `inset="page"` đã đặt sẵn `1.5rem` |
186
+
187
+ Không phải rule này: khoảng cách giữa các section con là gap chứ không phải inset. Dùng GAP-5.
188
+
189
+ ## PADDING-6 — `p-8` / `2rem`
190
+
191
+ Một khung rộng rãi có chủ ý bao quanh vùng lớn, mật độ thấp.
192
+
193
+ | Case | Dùng khi | Owner | Render |
194
+ | --- | --- | --- | --- |
195
+ | Case 1 | Vùng trang cần khoảng trống đều và mạnh ở mọi cạnh | `App` | `<section className="p-8">` |
196
+ | Case 2 | Vùng rỗng hoặc đang chờ, chiếm gần hết viewport | `App` | `<section className="p-8">` với `<EmptyNotice>` |
197
+
198
+ Không phải rule này: lấy làm mặc định cho container thường là phí viewport. Dùng PADDING-4.
199
+
200
+ ## Inset ghép
201
+
202
+ `PADDING-0` tới `PADDING-6` là thang: một rule, một giá trị. Từ `PADDING-7` trở đi, rule là những
203
+ công thức có tên mà các cạnh cố ý lấy giá trị khác nhau. Số vẫn đếm tiếp, nhưng thôi mang nghĩa vị
204
+ trí trên thang, nên một rule ghép không bao giờ xuất hiện trong bảng thang và không tự đẻ ra giá trị
205
+ mới. Mọi cạnh của một công thức ghép đều quy về một rule gốc.
206
+
207
+ Thêm số tiếp theo khi xuất hiện một pattern lệch cạnh thật sự mới. Phải nêu lý do vì sao từng cạnh
208
+ khác nhau; một công thức không có lý do thì là quyết định riêng của sản phẩm chứ không phải rule.
209
+
210
+ ## PADDING-7 — Nhích inset lên ở breakpoint rộng
211
+
212
+ Cùng một ranh giới giữ nguyên ý nghĩa qua các khổ màn hình nhưng lấy rộng hơn khi có chỗ. Giá trị ở
213
+ khổ hẹp và khổ rộng là hai rule liền kề, không bao giờ nhảy hai bậc, và trục block chỉ nhích theo ở
214
+ những cạnh mà chính ranh giới đó làm chủ.
215
+
216
+ Ghép từ: hai cạnh ngang của surface `1rem` → `1.5rem` (PADDING-4 → PADDING-5); cạnh trục block chạm
217
+ mép ngoài `1rem` → `1.5rem` (PADDING-4 → PADDING-5); cạnh trục block chạm separator giữ `.75rem` ở cả
218
+ hai khổ (PADDING-3).
219
+
220
+ | Case | Dùng khi | Owner | Render |
221
+ | --- | --- | --- | --- |
222
+ | Case 1 | Một surface quyết định, inset ngang mở rộng ra ở viewport lớn hơn | `—` | `<div className="px-4 sm:px-6">` |
223
+ | Case 2 | Cạnh trục block chạm mép ngoài của chính surface đó, nhích theo cùng | `—` | `<div className="px-4 pt-4 pb-4 sm:px-6 sm:pt-6 sm:pb-6">` |
224
+ | Case 3 | Cạnh hướng về separator bên trong surface đó | `—` | `pt-3` giữ `.75rem` ở cả hai khổ |
225
+
226
+ Không phải rule này: cạnh chạm separator thì không nhích, vì separator vẫn vẽ đúng một đường ở mọi
227
+ khổ màn hình.
228
+
229
+ ## PADDING-8 — Nội dung con thụt vào ở đầu trục inline
230
+
231
+ Nội dung thuộc về hàng nằm trên nó, chứ không phải đứng ngang với nó, thì thụt vào một bậc ở đầu
232
+ trục inline. Chỉ cạnh đầu mang nghĩa phân cấp. Cạnh cuối không mang nghĩa đó nên giữ thẳng hàng với
233
+ cha, và khổ đọc không bị bóp từ cả hai phía.
234
+
235
+ Ghép từ: đầu trục inline cao hơn inset của surface một bậc, `1.5rem` khi surface là `1rem`
236
+ (PADDING-5); cuối trục inline đúng inset của surface, giữ nguyên (PADDING-4); hai cạnh trục block theo
237
+ thứ mà cạnh đó chạm, dùng luật tiếp xúc (PADDING-3, PADDING-4).
238
+
239
+ | Case | Dùng khi | Owner | Render |
240
+ | --- | --- | --- | --- |
241
+ | Case 1 | Phần thân disclosure, đọc lên là câu trả lời thuộc về trigger phía trên | `—` | `<div className="ps-6 pe-4 py-3">` |
242
+ | Case 2 | Một nhóm lồng mà các hàng thuộc về một hàng cha có tên | `—` | Cùng công thức, mỗi cấp lồng thêm một bậc |
243
+
244
+ Không phải rule này: thụt vào cả hai bên, vì như vậy là canh giữa nội dung và đọc lên thành một khối
245
+ inset riêng chứ không phải nội dung con.
246
+
247
+ Disclosure của Common hiện áp `1rem` cho cả hai cạnh ngang, nên dạng thụt vào chưa có đường công
248
+ khai. Việc sửa thuộc về chính component disclosure, không phải một override ở tầng ứng dụng.
249
+
250
+ ## PADDING-9 — Inset trục dọc của một block được route ở breakpoint rộng
251
+
252
+ Một block được route tự cho `main` của nó một inset dọc, nhích lên khi chiều rộng cho phép, còn
253
+ inset trục ngang vẫn thuộc `PageContainer` (`--grammar-page-inset`). Hai giá trị là hai rule kề nhau
254
+ trên thang, không bao giờ nhảy hai bậc, và ở đây chỉ viết trục dọc.
255
+
256
+ Ghép từ: hai cạnh trục dọc của block được route `1.5rem` → `2rem` (PADDING-5 → PADDING-6); trục ngang
257
+ không có, vì `PageContainer` sở hữu (MEASURE-1).
258
+
259
+ | Case | Dùng khi | Owner | Render |
260
+ | --- | --- | --- | --- |
261
+ | Case 1 | `main` của một block được route nằm ngay dưới shell của trang | `App` | `<main className="py-6 sm:py-8">` |
262
+
263
+ Bằng chứng, bốn block viết cùng một cặp: `blocks/commerce/ProSubscriptionBlock/classNames.ts`
264
+ (`proPageClassName`), `blocks/learn/CoursePlaygroundCatalog/classNames.ts`,
265
+ `blocks/learn/PlaygroundSession/classNames.ts`, `blocks/learn/PlaygroundSetup/classNames.ts`.
266
+
267
+ Không phải rule này: inset trục ngang trên cùng phần tử, vì nó lặp lại inset trang mà container đã
268
+ vẽ. Dùng MEASURE-1 và để yên trục ngang.
269
+
270
+ ## Biến thể theo trục
271
+
272
+ `px-*` và `py-*` không phải rule riêng. Chúng áp một rule đã có lên một trục, khi hai trục mang hai
273
+ quyết định ranh giới khác nhau.
274
+
275
+ | Trục | Class | Nghĩa |
276
+ | --- | --- | --- |
277
+ | Inline | `px-*` | Rule đã chọn chỉ áp cho cạnh đầu và cuối theo chiều ngang |
278
+ | Block | `py-*` | Rule đã chọn chỉ áp cho cạnh trên và dưới |
279
+ | Một cạnh | `pt-*`, `pb-*`, `ps-*`, `pe-*` | Rule đã chọn chỉ áp cho đúng cạnh đó |
280
+
281
+ Class theo từng cạnh chính là thứ luật tiếp xúc cần đến, vì hai cạnh trục block của cùng một dải
282
+ thường lấy hai giá trị khác nhau. `px-4 pt-4 pb-3` là PADDING-4 ở ba cạnh và PADDING-3 ở cạnh chạm
283
+ separator, và mỗi cạnh vẫn phải gọi tên case của riêng nó.
284
+
285
+ Trên cùng một phần tử, class hẹp hơn thắng ở những cạnh nó gọi tên chứ không cộng vào nhau, nên
286
+ `p-4 pb-3` cho ra `1rem` ở ba cạnh và `.75rem` ở cạnh dưới. Giữa hai phần tử lồng nhau thì padding
287
+ cộng dồn, và đó là lý do một wrapper thêm vào chỉ để sửa một cạnh lại tạo ra inset không ai muốn.
288
+
289
+ ## File này không quyết định
290
+
291
+ Khoảng cách giữa các phần tử ngang hàng thuộc về [Gap](gap.md). Khoảng lệch ra ngoài thuộc về
292
+ [Margin](margin.md). Page inset thuộc về `PageContainer` và không phải quyết định của ứng dụng.
@@ -0,0 +1,166 @@
1
+ # Surface presentation
2
+
3
+ This file answers one question: given a region the application already decided to render, which
4
+ semantic surface token does that region take, and which foreground token pairs with it?
5
+
6
+ A surface and its foreground always travel together. The application never picks a background alone,
7
+ because a background chosen without its paired foreground leaves the copy on top of it unmeasured,
8
+ which is a contrast defect rather than a preference. Paint inside a Grammar object belongs to
9
+ Grammar; this file resolves only the faces the application owns.
10
+
11
+ ## Catalog
12
+
13
+ Surfaces have no value ramp, so the rule ID is an address over semantic faces rather than a position
14
+ on a scale. Prefer the earliest rule that works: no surface before the card face, the card face
15
+ before a secondary band, a neutral band before a meaningful one.
16
+
17
+ | Rule | Surface | Paired foreground | Carries |
18
+ | --- | --- | --- | --- |
19
+ | SURFACE-1 | none, transparent | inherited | Content that already sits on someone else's face |
20
+ | SURFACE-2 | `--surface` | `--surface-foreground` | The face of one bounded object |
21
+ | SURFACE-3 | `--surface-secondary` | `--foreground` | A neutral band inside a joined surface |
22
+ | SURFACE-4 | `--accent-soft` | `--accent-soft-foreground` | A band the product deliberately raises |
23
+ | SURFACE-5 | `--success-soft` | the foreground of the band it replaces | A proven outcome |
24
+ | SURFACE-6 | `--accent` | none, because it carries no copy | A decorative slab |
25
+
26
+ `--surface`, `--surface-secondary` and `--accent` are base theme tokens; `--accent-soft` and
27
+ `--success-soft` and their `-foreground` partners are derived from `--accent` and `--success` on the
28
+ same element, so overriding a base moves the whole family. Nothing on this list may be reproduced as
29
+ a hex value, an `oklch()` literal, or a palette step, even when the literal matches the token.
30
+
31
+ ## Owner
32
+
33
+ Each case names who owns the face. The owner decides whether the application writes a class at all.
34
+
35
+ | Owner | Meaning | Application writes |
36
+ | --- | --- | --- |
37
+ | `App` | The region belongs to the application | The class pair |
38
+ | A component name | Grammar already paints this face | Nothing. Pass the prop |
39
+ | `—` | Grammar exposes no public path for this face | The class pair, recorded as a workaround |
40
+
41
+ Writing a class where a component is the owner is `APP_REIMPLEMENTATION`. Writing one where the owner
42
+ is `—` stays linked to `COMMON_CAPABILITY_MISSING`. Reaching into a Grammar component with a selector
43
+ or a passed class to repaint it is `APP_OVERRIDE`.
44
+
45
+ ## Surfaces Common already owns
46
+
47
+ Generated from `@grammar/core` claims by `scripts/generate-presentation-owned.mjs`; edit the component, not this table.
48
+
49
+ | Component | Element or condition | Rule |
50
+ | --- | --- | --- |
51
+ | `ChatWorkspace` | drawer close, hasRail, isCompactRail | SURFACE-2 |
52
+ | `ChatWorkspace` | rail trigger, hasRail, isCompactRail | SURFACE-2 |
53
+ | `IconTile` | root, not (isSkeleton || showsArtwork), tone="accent" | SURFACE-4 |
54
+ | `IconTile` | root, not (isSkeleton || showsArtwork), tone="success" | SURFACE-5 |
55
+ | `MediaFrame` | root, treatment!="plain" | SURFACE-3 |
56
+ | `MediaFrame` | root, treatment="plain" | SURFACE-1 |
57
+ | `Sidebar` | list box item | SURFACE-4 |
58
+ | `SurfaceAccordionCard` | accordion shell, bounded | SURFACE-2 |
59
+ | `SurfaceAccordionCard` | accordion shell, not bounded | SURFACE-1 |
60
+ | `SurfaceCard` | card content, frame!="frameless" | SURFACE-2 |
61
+ | `SurfaceCard` | card content, frame="frameless" | SURFACE-1 |
62
+ | `SurfaceCard` | root, wholeAction!=undefined | SURFACE-4 |
63
+ | `SurfaceListCard` | root | SURFACE-2 |
64
+ | `TextAction` | root, isCurrent, appearance="choice"|"route"|"section" | SURFACE-4 |
65
+
66
+ ## SURFACE-1 — no surface / inherited foreground
67
+
68
+ The region paints nothing, because the face underneath it is already the right one and the copy
69
+ inherits the foreground that was measured against it.
70
+
71
+ | Case | When | Owner | Render |
72
+ | --- | --- | --- | --- |
73
+ | Case 1 | Content whose visible boundaries are already drawn by its children | `SurfaceCard` | `frame="frameless"` already sets transparent and `color: inherit` |
74
+ | Case 2 | Media that must sit on the page canvas with no inset frame | `MediaFrame` | `treatment="plain"` already clears the fill and the border |
75
+ | Case 3 | An app-owned grouping container that only stacks children which each own a face | `App` | `<div className="flex min-w-0 flex-col">` with no background class |
76
+
77
+ Not this rule: a region that needs its own readable face uses SURFACE-2.
78
+
79
+ ## SURFACE-2 — `bg-surface` / `text-surface-foreground`
80
+
81
+ The face of one bounded object. This is the surface a card presents to the page, and Grammar paints
82
+ it as a pair so the copy inside never has to state a colour.
83
+
84
+ | Case | When | Owner | Render |
85
+ | --- | --- | --- | --- |
86
+ | Case 1 | One bounded content object on the page canvas | `SurfaceCard` | Compose the card; no background class |
87
+ | Case 2 | A list or disclosure presenting the same bounded face | `SurfaceListCard`, `SurfaceAccordionCard` | Compose the card; no background class |
88
+ | Case 3 | Rows that must show the card face through a joined list at reduced alpha | `—` | `<li className="bg-surface/90">` inside a joined card body |
89
+
90
+ Not this rule: a band that must read as recessed against the card face uses SURFACE-3.
91
+
92
+ Case 3 composes two layers, so contrast is measured against the resulting pixels rather than against
93
+ `--surface` alone.
94
+
95
+ ## SURFACE-3 — `bg-surface-secondary` / `text-foreground`
96
+
97
+ A neutral band inside a joined surface. It reads as recessed against the card face without claiming
98
+ any meaning of its own, so the copy on it stays at ordinary reading attention.
99
+
100
+ | Case | When | Owner | Render |
101
+ | --- | --- | --- | --- |
102
+ | Case 1 | A summary or explanatory band running edge to edge inside a joined card | `—` | `<div className="bg-surface-secondary px-4 py-3 text-foreground">` |
103
+ | Case 2 | A full-bleed illustration band sharing that neutral face | `—` | `<div className="min-w-0 bg-surface-secondary">` with the image inside |
104
+ | Case 3 | The recessed backdrop behind framed media | `MediaFrame` | Compose the frame; no background class |
105
+
106
+ Not this rule: a band the product means to raise uses SURFACE-4, and a band reporting a proven
107
+ outcome uses SURFACE-5.
108
+
109
+ The theme also publishes `--surface-secondary-foreground`, but the bands above pair the secondary
110
+ face with `--foreground`. The two resolve to the same value in both themes, so the pair is recorded
111
+ as it is written rather than corrected here.
112
+
113
+ ## SURFACE-4 — `bg-accent-soft` / `text-accent-soft-foreground`
114
+
115
+ A band the product deliberately raises. Accent is scarce: repeated across every peer band it stops
116
+ marking anything, and the raised band still has to say in words why it is raised.
117
+
118
+ | Case | When | Owner | Render |
119
+ | --- | --- | --- | --- |
120
+ | Case 1 | One summary band inside a joined card that the product ranked above its peers | `—` | `<div className="bg-accent-soft px-4 py-3 text-accent-soft-foreground">` |
121
+ | Case 2 | A leading marker inside a raised region, taking the paired foreground alone | `—` | `<Icon className="text-accent-soft-foreground" />` on the raised band |
122
+ | Case 3 | A card whose whole face responds to hover or keyboard focus | `SurfaceCard` | `interaction="whole-action"` already swaps the face |
123
+
124
+ Not this rule: a full-strength accent slab under copy. Use SURFACE-6 only where no copy sits on it.
125
+
126
+ A raised band that omits `text-accent-soft-foreground` leaves its copy on the inherited foreground of
127
+ whatever face it replaced, which is the defect this rule exists to prevent.
128
+
129
+ `--accent-soft` is a `color-mix` of `--accent` toward transparent, so the band is translucent and the
130
+ face beneath it shows through. Contrast on this rule is therefore measured against the composed
131
+ pixels, never against the token.
132
+
133
+ ## SURFACE-5 — `bg-success-soft` / the foreground of the band it replaces
134
+
135
+ A band reports a proven outcome by exchanging its neutral face for the status face, and keeps the
136
+ foreground the neutral band already established.
137
+
138
+ | Case | When | Owner | Render |
139
+ | --- | --- | --- | --- |
140
+ | Case 1 | A band whose outcome is settled and evidenced, not merely promised | `—` | The neutral band class followed by `bg-success-soft` |
141
+ | Case 2 | The same band before the outcome is proven | `—` | Stay on SURFACE-3; do not pre-paint the result |
142
+
143
+ Not this rule: colour as the only carrier of the outcome. The band still states the outcome in words,
144
+ and status colour is never chosen as a text tone.
145
+
146
+ The status face is exchanged without a matching foreground swap, so the pair is `--success-soft` under
147
+ `--foreground`. That composition has to be measured, not assumed, in both themes.
148
+
149
+ ## SURFACE-6 — `bg-accent` / no paired foreground
150
+
151
+ A decorative slab at full accent strength. It is admissible only because nothing readable sits on it,
152
+ which is also why it names no foreground.
153
+
154
+ | Case | When | Owner | Render |
155
+ | --- | --- | --- | --- |
156
+ | Case 1 | A hero band carrying one decorative image and no copy | `—` | `<div className="relative isolate min-h-32 overflow-hidden bg-accent">` with an `aria-hidden` image |
157
+
158
+ Not this rule: any slab carrying text, a label, or a control. Put copy on SURFACE-4 instead, where a
159
+ paired foreground exists.
160
+
161
+ ## What this file does not decide
162
+
163
+ Which foreground a single line of copy takes, once its surface is settled, is [Tone](tone.md). The
164
+ edges that separate two faces are [Boundary](boundary.md). The space between a face and its content
165
+ is [Padding](padding.md), and the clipping that keeps a band inside its card is
166
+ [Overflow](overflow.md).
@@ -0,0 +1,166 @@
1
+ # Surface presentation
2
+
3
+ File này trả lời đúng một câu hỏi: một vùng mà ứng dụng đã quyết định render thì nhận token surface
4
+ ngữ nghĩa nào, và token foreground nào đi kèm với nó.
5
+
6
+ Surface và foreground luôn đi thành cặp. Ứng dụng không bao giờ chọn riêng phần nền, vì một nền chọn
7
+ mà thiếu foreground đi kèm sẽ để phần copy nằm trên đó không được đo, và đó là lỗi tương phản chứ
8
+ không phải chuyện sở thích. Phần sơn bên trong một Grammar object thuộc về Grammar; file này chỉ giải
9
+ quyết những mặt do ứng dụng sở hữu.
10
+
11
+ ## Danh mục
12
+
13
+ Surface không có thang giá trị, nên số của rule là địa chỉ trên tập các mặt ngữ nghĩa chứ không phải
14
+ vị trí trên một dải. Ưu tiên rule đứng trước nếu nó đủ dùng: không surface trước mặt card, mặt card
15
+ trước dải phụ, dải trung tính trước dải mang nghĩa.
16
+
17
+ | Rule | Surface | Foreground đi kèm | Mang nghĩa |
18
+ | --- | --- | --- | --- |
19
+ | SURFACE-1 | không có, trong suốt | kế thừa | Nội dung đã nằm trên mặt của người khác |
20
+ | SURFACE-2 | `--surface` | `--surface-foreground` | Mặt của một đối tượng có ranh giới |
21
+ | SURFACE-3 | `--surface-secondary` | `--foreground` | Dải trung tính bên trong một surface nối liền |
22
+ | SURFACE-4 | `--accent-soft` | `--accent-soft-foreground` | Dải mà sản phẩm cố ý nâng lên |
23
+ | SURFACE-5 | `--success-soft` | foreground của dải mà nó thay thế | Một kết quả đã được chứng minh |
24
+ | SURFACE-6 | `--accent` | không có, vì không có copy nằm trên | Một mảng trang trí |
25
+
26
+ `--surface`, `--surface-secondary` và `--accent` là token nền của theme; `--accent-soft`,
27
+ `--success-soft` cùng các bản `-foreground` của chúng được dẫn xuất từ `--accent` và `--success` trên
28
+ cùng một element, nên ghi đè token gốc là dịch cả họ. Không token nào trong danh sách này được viết
29
+ lại bằng giá trị hex, literal `oklch()`, hay một bậc palette, kể cả khi literal đó trùng giá trị.
30
+
31
+ ## Owner
32
+
33
+ Mỗi case gọi tên ai sở hữu cái mặt đó. Owner quyết định ứng dụng có được viết class hay không.
34
+
35
+ | Owner | Nghĩa | Ứng dụng viết |
36
+ | --- | --- | --- |
37
+ | `App` | Vùng thuộc về ứng dụng | Viết cặp class |
38
+ | Tên component | Grammar đã sơn mặt này | Không viết gì, chỉ truyền prop |
39
+ | `—` | Grammar chưa có đường dùng công khai cho mặt này | Viết cặp class, ghi nhận là workaround |
40
+
41
+ Viết class ở chỗ owner là một component chính là `APP_REIMPLEMENTATION`. Viết class ở chỗ owner là `—`
42
+ thì gắn liền với `COMMON_CAPABILITY_MISSING`. Thò vào một Grammar component bằng selector hay class
43
+ truyền xuống để sơn lại nó là `APP_OVERRIDE`.
44
+
45
+ ## Surface mà Common đã sở hữu
46
+
47
+ Sinh từ claim của `@grammar/core` bằng `scripts/generate-presentation-owned.mjs`; muốn đổi thì sửa component, đừng sửa bảng này.
48
+
49
+ | Component | Phần tử hoặc điều kiện | Rule |
50
+ | --- | --- | --- |
51
+ | `ChatWorkspace` | drawer close, hasRail, isCompactRail | SURFACE-2 |
52
+ | `ChatWorkspace` | rail trigger, hasRail, isCompactRail | SURFACE-2 |
53
+ | `IconTile` | root, not (isSkeleton || showsArtwork), tone="accent" | SURFACE-4 |
54
+ | `IconTile` | root, not (isSkeleton || showsArtwork), tone="success" | SURFACE-5 |
55
+ | `MediaFrame` | root, treatment!="plain" | SURFACE-3 |
56
+ | `MediaFrame` | root, treatment="plain" | SURFACE-1 |
57
+ | `Sidebar` | list box item | SURFACE-4 |
58
+ | `SurfaceAccordionCard` | accordion shell, bounded | SURFACE-2 |
59
+ | `SurfaceAccordionCard` | accordion shell, not bounded | SURFACE-1 |
60
+ | `SurfaceCard` | card content, frame!="frameless" | SURFACE-2 |
61
+ | `SurfaceCard` | card content, frame="frameless" | SURFACE-1 |
62
+ | `SurfaceCard` | root, wholeAction!=undefined | SURFACE-4 |
63
+ | `SurfaceListCard` | root | SURFACE-2 |
64
+ | `TextAction` | root, isCurrent, appearance="choice"|"route"|"section" | SURFACE-4 |
65
+
66
+ ## SURFACE-1 — không có surface / foreground kế thừa
67
+
68
+ Vùng không sơn gì cả, vì cái mặt bên dưới nó đã đúng rồi và phần copy kế thừa đúng foreground đã được
69
+ đo trên mặt đó.
70
+
71
+ | Case | Dùng khi | Owner | Render |
72
+ | --- | --- | --- | --- |
73
+ | Case 1 | Nội dung mà các ranh giới nhìn thấy được đã do chính con của nó vẽ | `SurfaceCard` | `frame="frameless"` đã đặt sẵn trong suốt và `color: inherit` |
74
+ | Case 2 | Media phải nằm thẳng trên canvas trang, không khung lót | `MediaFrame` | `treatment="plain"` đã bỏ sẵn phần nền và border |
75
+ | Case 3 | Container gom nhóm do app sở hữu, chỉ xếp chồng các con mà mỗi con tự có mặt riêng | `App` | `<div className="flex min-w-0 flex-col">` không có class nền |
76
+
77
+ Không phải rule này: một vùng cần mặt đọc được của riêng nó thì dùng SURFACE-2.
78
+
79
+ ## SURFACE-2 — `bg-surface` / `text-surface-foreground`
80
+
81
+ Mặt của một đối tượng có ranh giới. Đây là mặt mà một card trình ra với trang, và Grammar sơn nó theo
82
+ cặp để phần copy bên trong không bao giờ phải tự khai màu.
83
+
84
+ | Case | Dùng khi | Owner | Render |
85
+ | --- | --- | --- | --- |
86
+ | Case 1 | Một đối tượng nội dung có ranh giới nằm trên canvas trang | `SurfaceCard` | Ghép card, không viết class nền |
87
+ | Case 2 | Một list hay disclosure trình ra cùng cái mặt có ranh giới đó | `SurfaceListCard`, `SurfaceAccordionCard` | Ghép card, không viết class nền |
88
+ | Case 3 | Các hàng phải để lộ mặt card qua một list nối liền ở độ trong nhất định | `—` | `<li className="bg-surface/90">` bên trong thân card nối liền |
89
+
90
+ Không phải rule này: một dải cần đọc thành lõm xuống so với mặt card thì dùng SURFACE-3.
91
+
92
+ Case 3 chồng hai lớp lên nhau, nên tương phản được đo trên pixel kết quả chứ không phải trên riêng
93
+ `--surface`.
94
+
95
+ ## SURFACE-3 — `bg-surface-secondary` / `text-foreground`
96
+
97
+ Dải trung tính bên trong một surface nối liền. Nó đọc thành lõm xuống so với mặt card mà không tự
98
+ nhận nghĩa nào, nên phần copy trên đó vẫn ở mức chú ý đọc bình thường.
99
+
100
+ | Case | Dùng khi | Owner | Render |
101
+ | --- | --- | --- | --- |
102
+ | Case 1 | Dải tóm tắt hoặc diễn giải chạy sát hai mép trong một card nối liền | `—` | `<div className="bg-surface-secondary px-4 py-3 text-foreground">` |
103
+ | Case 2 | Dải minh hoạ tràn viền dùng chung cái mặt trung tính đó | `—` | `<div className="min-w-0 bg-surface-secondary">` với ảnh bên trong |
104
+ | Case 3 | Nền lõm phía sau media có khung | `MediaFrame` | Ghép frame, không viết class nền |
105
+
106
+ Không phải rule này: dải mà sản phẩm cố ý nâng lên thì dùng SURFACE-4, còn dải báo một kết quả đã
107
+ chứng minh thì dùng SURFACE-5.
108
+
109
+ Theme cũng công bố `--surface-secondary-foreground`, nhưng các dải ở trên ghép mặt phụ với
110
+ `--foreground`. Hai token này quy về cùng một giá trị ở cả hai theme, nên cặp này được ghi đúng như nó
111
+ đang được viết chứ không sửa lại ở đây.
112
+
113
+ ## SURFACE-4 — `bg-accent-soft` / `text-accent-soft-foreground`
114
+
115
+ Một dải mà sản phẩm cố ý nâng lên. Accent là thứ khan hiếm: lặp lại trên mọi dải ngang hàng thì nó
116
+ thôi đánh dấu điều gì, và dải được nâng vẫn phải nói bằng chữ vì sao nó được nâng.
117
+
118
+ | Case | Dùng khi | Owner | Render |
119
+ | --- | --- | --- | --- |
120
+ | Case 1 | Một dải tóm tắt trong card nối liền mà sản phẩm đã xếp trên các dải ngang hàng | `—` | `<div className="bg-accent-soft px-4 py-3 text-accent-soft-foreground">` |
121
+ | Case 2 | Một dấu dẫn đầu bên trong vùng được nâng, chỉ lấy riêng foreground đi kèm | `—` | `<Icon className="text-accent-soft-foreground" />` trên dải được nâng |
122
+ | Case 3 | Một card mà cả mặt của nó phản hồi khi hover hoặc focus bàn phím | `SurfaceCard` | `interaction="whole-action"` đã đổi sẵn cái mặt |
123
+
124
+ Không phải rule này: một mảng accent nguyên độ nằm dưới copy. Chỉ dùng SURFACE-6 ở chỗ không có copy
125
+ nào nằm trên.
126
+
127
+ Một dải được nâng mà bỏ `text-accent-soft-foreground` sẽ để copy của nó nằm trên foreground kế thừa
128
+ của cái mặt mà nó vừa thay, và đó chính là lỗi mà rule này sinh ra để chặn.
129
+
130
+ `--accent-soft` là một `color-mix` của `--accent` pha về phía trong suốt, nên dải này không đục và mặt
131
+ bên dưới vẫn lộ qua. Vì vậy tương phản ở rule này được đo trên pixel đã chồng, không bao giờ đo trên
132
+ token.
133
+
134
+ ## SURFACE-5 — `bg-success-soft` / foreground của dải mà nó thay thế
135
+
136
+ Một dải báo kết quả đã được chứng minh bằng cách đổi mặt trung tính của nó lấy mặt trạng thái, và giữ
137
+ nguyên foreground mà dải trung tính đã thiết lập.
138
+
139
+ | Case | Dùng khi | Owner | Render |
140
+ | --- | --- | --- | --- |
141
+ | Case 1 | Dải mà kết quả đã ngã ngũ và có bằng chứng, không phải mới chỉ hứa | `—` | Class dải trung tính rồi tới `bg-success-soft` |
142
+ | Case 2 | Chính dải đó khi kết quả chưa được chứng minh | `—` | Ở lại SURFACE-3, không sơn trước kết quả |
143
+
144
+ Không phải rule này: lấy màu làm phương tiện duy nhất chở kết quả. Dải vẫn phải nói kết quả bằng chữ,
145
+ và màu trạng thái không bao giờ được chọn như một tone chữ.
146
+
147
+ Mặt trạng thái được đổi mà không đổi foreground kèm theo, nên cặp thực tế là `--success-soft` dưới
148
+ `--foreground`. Tổ hợp đó phải được đo ở cả hai theme, không được suy đoán.
149
+
150
+ ## SURFACE-6 — `bg-accent` / không có foreground đi kèm
151
+
152
+ Một mảng trang trí ở độ accent nguyên. Nó chỉ được chấp nhận vì không có gì đọc được nằm trên đó, và
153
+ cũng chính vì thế nó không gọi tên foreground nào.
154
+
155
+ | Case | Dùng khi | Owner | Render |
156
+ | --- | --- | --- | --- |
157
+ | Case 1 | Dải hero chỉ mang một ảnh trang trí và không mang copy | `—` | `<div className="relative isolate min-h-32 overflow-hidden bg-accent">` với một ảnh `aria-hidden` |
158
+
159
+ Không phải rule này: bất kỳ mảng nào mang chữ, nhãn hay control. Đặt copy lên SURFACE-4, nơi có sẵn
160
+ một foreground đi kèm.
161
+
162
+ ## File này không quyết định
163
+
164
+ Một dòng copy lấy foreground nào sau khi surface của nó đã chốt là việc của [Tone](tone.md). Các cạnh
165
+ tách hai mặt là [Boundary](boundary.md). Khoảng cách giữa một mặt và nội dung của nó là
166
+ [Padding](padding.md), còn việc cắt để giữ một dải nằm gọn trong card là [Overflow](overflow.md).