@cyberskill/design 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (266) hide show
  1. package/DESIGN.md +10 -9
  2. package/README.md +31 -28
  3. package/SKILL.md +13 -9
  4. package/VERSION +1 -1
  5. package/_ds_bundle.js +660 -202
  6. package/_ds_manifest.json +1 -1
  7. package/_esm/cs.mjs +14 -8
  8. package/_esm/react.d.ts +216 -0
  9. package/_esm/react.mjs +114 -0
  10. package/base/collections.css +31 -0
  11. package/base/components.css +3 -0
  12. package/base/data.css +33 -0
  13. package/base/forms.css +8 -0
  14. package/base/interaction.css +1 -0
  15. package/base/typography.css +6 -0
  16. package/components/_i18n/strings.js +8 -4
  17. package/components/ai/ai-conversation.card.html +2 -2
  18. package/components/ai/ai-lumi.card.html +2 -2
  19. package/components/ai/ai.card.html +2 -2
  20. package/components/brand/brand.card.html +2 -2
  21. package/components/button/buttons.card.html +2 -2
  22. package/components/data/AspectRatio.d.ts +16 -0
  23. package/components/data/AspectRatio.figma.tsx +12 -0
  24. package/components/data/AspectRatio.jsx +16 -0
  25. package/components/data/AspectRatio.prompt.md +7 -0
  26. package/components/data/Collapsible.d.ts +21 -0
  27. package/components/data/Collapsible.figma.tsx +12 -0
  28. package/components/data/Collapsible.jsx +49 -0
  29. package/components/data/Collapsible.prompt.md +7 -0
  30. package/components/data/Image.d.ts +4 -0
  31. package/components/data/Image.prompt.md +2 -2
  32. package/components/data/Item.d.ts +26 -0
  33. package/components/data/Item.figma.tsx +12 -0
  34. package/components/data/Item.jsx +63 -0
  35. package/components/data/Item.prompt.md +10 -0
  36. package/components/data/ScrollArea.d.ts +16 -0
  37. package/components/data/ScrollArea.figma.tsx +12 -0
  38. package/components/data/ScrollArea.jsx +25 -0
  39. package/components/data/ScrollArea.prompt.md +7 -0
  40. package/components/data/collections.card.html +15 -4
  41. package/components/data/data.card.html +15 -4
  42. package/components/datatable/datatable.card.html +2 -2
  43. package/components/dialog/dialog.card.html +2 -2
  44. package/components/feedback/feedback-2.card.html +2 -2
  45. package/components/feedback/feedback.card.html +2 -2
  46. package/components/forms/Cascader.jsx +5 -3
  47. package/components/forms/Editor.prompt.md +1 -1
  48. package/components/forms/InputGroup.d.ts +4 -0
  49. package/components/forms/InputGroup.jsx +38 -8
  50. package/components/forms/InputGroup.prompt.md +3 -3
  51. package/components/forms/NativeSelect.d.ts +22 -0
  52. package/components/forms/NativeSelect.figma.tsx +12 -0
  53. package/components/forms/NativeSelect.jsx +56 -0
  54. package/components/forms/NativeSelect.prompt.md +10 -0
  55. package/components/forms/NumberField.d.ts +5 -0
  56. package/components/forms/NumberField.jsx +51 -7
  57. package/components/forms/NumberField.prompt.md +2 -2
  58. package/components/forms/SegmentedControl.jsx +1 -1
  59. package/components/forms/TagInput.d.ts +3 -0
  60. package/components/forms/TagInput.jsx +62 -20
  61. package/components/forms/TagInput.prompt.md +2 -2
  62. package/components/forms/forms-2.card.html +2 -2
  63. package/components/forms/forms.card.html +5 -4
  64. package/components/icon/Icon.prompt.md +1 -1
  65. package/components/icon/icon.card.html +2 -2
  66. package/components/logo/Logo.jsx +2 -1
  67. package/components/logo/logo.card.html +2 -2
  68. package/components/navigation/Menu.jsx +14 -1
  69. package/components/navigation/navigation.card.html +2 -2
  70. package/components/navigation/shell.card.html +2 -2
  71. package/components/overlays/AlertDialog.d.ts +29 -0
  72. package/components/overlays/AlertDialog.figma.tsx +12 -0
  73. package/components/overlays/AlertDialog.jsx +142 -0
  74. package/components/overlays/AlertDialog.prompt.md +12 -0
  75. package/components/overlays/Drawer.jsx +1 -1
  76. package/components/overlays/Popconfirm.jsx +14 -1
  77. package/components/overlays/Popover.jsx +14 -1
  78. package/components/overlays/alertdialog.card.html +42 -0
  79. package/components/overlays/overlays.card.html +2 -2
  80. package/components/textfield/textfield.card.html +2 -2
  81. package/docs/benchmark-rubric.md +46 -0
  82. package/docs/ci-cd.md +17 -11
  83. package/docs/consumer-grant.md +37 -0
  84. package/docs/consuming.md +45 -16
  85. package/docs/contrast-report.md +91 -103
  86. package/docs/conventions.md +12 -9
  87. package/docs/decisions.md +33 -10
  88. package/docs/deploy.md +1 -1
  89. package/docs/design-styles.md +26 -8
  90. package/docs/figma.md +3 -3
  91. package/docs/live-hub.md +5 -3
  92. package/docs/products.md +13 -11
  93. package/docs/quality-gates.md +35 -21
  94. package/docs/release-notes.md +21 -3
  95. package/docs/status/assets/favicon.svg +5 -0
  96. package/docs/status/assets/status-legacy.css +399 -0
  97. package/docs/status/assets/status-legacy.js +505 -0
  98. package/docs/status/assets/status.css +644 -0
  99. package/docs/status/assets/status.js +1308 -0
  100. package/docs/status/data/status-feed.json +1 -0
  101. package/docs/status/data/task/TASK-REL-001.js +1 -0
  102. package/docs/status/data/task/TASK-REL-002.js +1 -0
  103. package/docs/status/index.html +42 -0
  104. package/docs/status/roadmap.html +4 -0
  105. package/docs/status/status-legacy.html +117 -0
  106. package/docs/storybook.md +5 -4
  107. package/docs/sync.md +3 -2
  108. package/docs/tasks/BACKLOG.md +26 -0
  109. package/docs/tasks/release/TASK-REL-001-launch-1-1-0/spec.md +55 -0
  110. package/docs/tasks/release/TASK-REL-002-republish-1-1-1/spec.md +54 -0
  111. package/docs/template-schema-v2.md +15 -3
  112. package/docs/vi/benchmark-rubric.md +46 -0
  113. package/docs/vi/ci-cd.md +17 -11
  114. package/docs/vi/consumer-grant.md +37 -0
  115. package/docs/vi/consuming.md +45 -16
  116. package/docs/vi/contrast-report.md +94 -106
  117. package/docs/vi/conventions.md +12 -9
  118. package/docs/vi/decisions.md +33 -10
  119. package/docs/vi/deploy.md +1 -1
  120. package/docs/vi/design-styles.md +26 -8
  121. package/docs/vi/figma.md +3 -3
  122. package/docs/vi/live-hub.md +5 -3
  123. package/docs/vi/products.md +13 -11
  124. package/docs/vi/quality-gates.md +35 -21
  125. package/docs/vi/release-notes.md +26 -8
  126. package/docs/vi/storybook.md +5 -4
  127. package/docs/vi/sync.md +3 -2
  128. package/docs/vi/template-schema-v2.md +15 -3
  129. package/docs/viewer.html +2 -2
  130. package/fonts/spacegrotesk-var-latin-ext.woff2 +0 -0
  131. package/fonts/spacegrotesk-var-latin.woff2 +0 -0
  132. package/fonts/spacegrotesk-var-vietnamese.woff2 +0 -0
  133. package/guidelines/atomic-view.html +40 -27
  134. package/guidelines/elements-geometry.html +69 -0
  135. package/guidelines/elements-variants.html +17 -17
  136. package/guidelines/type-families.html +16 -10
  137. package/llms.txt +17 -11
  138. package/package.json +38 -16
  139. package/styles.css +1 -0
  140. package/templates/ai-cluster-demo.html +29 -14
  141. package/templates/app-shell/support.js +4 -4
  142. package/templates/article/content-schema.json +6 -0
  143. package/templates/article/support.js +4 -4
  144. package/templates/auth/support.js +4 -4
  145. package/templates/bod-deck/support.js +4 -4
  146. package/templates/bod-investor-update/support.js +4 -4
  147. package/templates/bod-memo/support.js +4 -4
  148. package/templates/bod-minutes/content-schema.json +12 -0
  149. package/templates/bod-minutes/support.js +4 -4
  150. package/templates/bod-report/support.js +4 -4
  151. package/templates/bod-resolution/support.js +4 -4
  152. package/templates/culture-event-invite/support.js +4 -4
  153. package/templates/dashboard/support.js +4 -4
  154. package/templates/delivery-change-note/support.js +4 -4
  155. package/templates/delivery-kickoff/support.js +4 -4
  156. package/templates/delivery-qbr-deck/support.js +4 -4
  157. package/templates/delivery-runbook/content-schema.json +6 -0
  158. package/templates/delivery-runbook/support.js +4 -4
  159. package/templates/delivery-status-email/support.js +4 -4
  160. package/templates/doc-style-guide/DocStyleGuide.dc.html +2 -2
  161. package/templates/doc-style-guide/support.js +4 -4
  162. package/templates/doc-suite-index/support.js +4 -4
  163. package/templates/doc-templates/DocTemplates.dc.html +1 -1
  164. package/templates/doc-templates/support.js +4 -4
  165. package/templates/email/support.js +4 -4
  166. package/templates/email-safe/README.md +17 -4
  167. package/templates/email-safe/email-launch.html +3 -3
  168. package/templates/email-safe/email-newsletter.html +1 -1
  169. package/templates/finance-dunning-email/support.js +4 -4
  170. package/templates/finance-expense-report/support.js +4 -4
  171. package/templates/finance-invoice/support.js +4 -4
  172. package/templates/finance-quote/support.js +4 -4
  173. package/templates/hr-announcement/support.js +4 -4
  174. package/templates/hr-interview-kit/content-schema.json +12 -0
  175. package/templates/hr-interview-kit/support.js +4 -4
  176. package/templates/hr-offboarding/content-schema.json +12 -0
  177. package/templates/hr-offboarding/support.js +4 -4
  178. package/templates/hr-performance-review/content-schema.json +12 -0
  179. package/templates/hr-performance-review/support.js +4 -4
  180. package/templates/hr-pip/content-schema.json +12 -0
  181. package/templates/hr-pip/support.js +4 -4
  182. package/templates/legal-msa/content-schema.json +16 -0
  183. package/templates/legal-msa/support.js +4 -4
  184. package/templates/legal-sow/support.js +4 -4
  185. package/templates/marketing-campaign-brief/support.js +4 -4
  186. package/templates/marketing-case-study/support.js +4 -4
  187. package/templates/marketing-launch/support.js +4 -4
  188. package/templates/marketing-newsletter/support.js +4 -4
  189. package/templates/marketing-page/content-schema.json +0 -1
  190. package/templates/marketing-page/support.js +4 -4
  191. package/templates/marketing-press-release/support.js +4 -4
  192. package/templates/marketing-social-kit/support.js +4 -4
  193. package/templates/playground.html +8 -4
  194. package/templates/sales-one-pager/support.js +4 -4
  195. package/templates/sales-proposal-deck/support.js +4 -4
  196. package/templates/settings/Settings.dc.html +26 -14
  197. package/templates/settings/content-schema.json +36 -0
  198. package/templates/settings/support.js +4 -4
  199. package/templates/slide-deck/support.js +4 -4
  200. package/templates/team-meeting-agenda/content-schema.json +16 -0
  201. package/templates/team-meeting-agenda/support.js +4 -4
  202. package/templates/tech-incident-report/support.js +4 -4
  203. package/templates/tech-release-notes/support.js +4 -4
  204. package/templates/tech-retro/content-schema.json +12 -0
  205. package/templates/tech-retro/support.js +4 -4
  206. package/templates/tech-rfc/content-schema.json +6 -0
  207. package/templates/tech-rfc/support.js +4 -4
  208. package/templates/vn-appointment-transfer/support.js +4 -4
  209. package/templates/vn-certificate-of-employment/support.js +4 -4
  210. package/templates/vn-code-of-conduct/support.js +4 -4
  211. package/templates/vn-compensation-regulation/support.js +4 -4
  212. package/templates/vn-contract-amendment/support.js +4 -4
  213. package/templates/vn-contractor-agreement/support.js +4 -4
  214. package/templates/vn-data-protection-policy/support.js +4 -4
  215. package/templates/vn-disciplinary-case-file/support.js +4 -4
  216. package/templates/vn-disciplinary-schedule/content-schema.json +15 -0
  217. package/templates/vn-disciplinary-schedule/support.js +4 -4
  218. package/templates/vn-employee-request/support.js +4 -4
  219. package/templates/vn-exit-handover/support.js +4 -4
  220. package/templates/vn-foreign-employee-pack/support.js +4 -4
  221. package/templates/vn-grassroots-democracy/support.js +4 -4
  222. package/templates/vn-infosec-policy/support.js +4 -4
  223. package/templates/vn-internal-labor-regulations/support.js +4 -4
  224. package/templates/vn-internship-agreement/support.js +4 -4
  225. package/templates/vn-job-description/support.js +4 -4
  226. package/templates/vn-labor-contract/support.js +4 -4
  227. package/templates/vn-labour-management-book/content-schema.json +15 -0
  228. package/templates/vn-labour-management-book/support.js +4 -4
  229. package/templates/vn-mutual-nda/support.js +4 -4
  230. package/templates/vn-ndnca-ip/support.js +4 -4
  231. package/templates/vn-offer-letter/support.js +4 -4
  232. package/templates/vn-onboarding-checklist/support.js +4 -4
  233. package/templates/vn-pdpl-consent/support.js +4 -4
  234. package/templates/vn-performance-evaluation-regulation/support.js +4 -4
  235. package/templates/vn-performance-review-form/support.js +4 -4
  236. package/templates/vn-phantom-stock/support.js +4 -4
  237. package/templates/vn-probation-contract/support.js +4 -4
  238. package/templates/vn-receipt-acknowledgement/content-schema.json +15 -0
  239. package/templates/vn-receipt-acknowledgement/support.js +4 -4
  240. package/templates/vn-remote-work-policy/support.js +4 -4
  241. package/templates/vn-resignation-letter/support.js +4 -4
  242. package/templates/vn-salary-scale/content-schema.json +15 -0
  243. package/templates/vn-salary-scale/support.js +4 -4
  244. package/templates/vn-sow-appendix/support.js +4 -4
  245. package/templates/vn-termination-decision/support.js +4 -4
  246. package/templates/vn-total-rewards-appendix/support.js +4 -4
  247. package/templates/vn-training-commitment/support.js +4 -4
  248. package/templates/vn-travel-expense-policy/support.js +4 -4
  249. package/tokens/element-seeds.json +112 -0
  250. package/tokens/elements.css +274 -187
  251. package/tokens/fonts.css +12 -3
  252. package/tokens/native/CSTokens.kt +84 -68
  253. package/tokens/native/CSTokens.swift +84 -68
  254. package/tokens/native/cs_tokens.dart +84 -68
  255. package/tokens/provenance.json +9 -9
  256. package/tokens/styles.css +15 -0
  257. package/tokens/tokens.dtcg.json +258 -233
  258. package/tokens/tokens.js +168 -167
  259. package/tokens/tokens.json +167 -166
  260. package/tokens/typography.css +9 -1
  261. package/ui_kits/status-hub/README.md +1 -2
  262. package/ui_kits/status-hub/index.html +2 -2
  263. package/ui_kits/status-hub/settings.html +6 -3
  264. package/ui_kits/website/chat.html +2 -2
  265. package/ui_kits/website/index.html +2 -2
  266. package/ui_kits/status-hub/identity-lab.html +0 -134
@@ -0,0 +1,142 @@
1
+ import React from "react";
2
+ import { makeT, useLang } from "../_i18n/i18n.js";
3
+ import { cx } from "../_utils/cx.js";
4
+
5
+ const FOCUSABLE = [
6
+ 'a[href]',
7
+ 'area[href]',
8
+ 'button:not([disabled])',
9
+ 'input:not([disabled]):not([type="hidden"])',
10
+ 'select:not([disabled])',
11
+ 'textarea:not([disabled])',
12
+ 'audio[controls]',
13
+ 'video[controls]',
14
+ 'summary',
15
+ 'iframe',
16
+ '[contenteditable]:not([contenteditable="false"])',
17
+ '[tabindex]:not([tabindex="-1"])',
18
+ ].join(",");
19
+
20
+ /**
21
+ * CyberSkill AlertDialog — first-class destructive/confirm modal (role=alertdialog).
22
+ * Distinct from Popconfirm (inline bubble) and Dialog (general modal). Focus trap +
23
+ * restore; bilingual confirm/cancel via the registry.
24
+ */
25
+ export function AlertDialog({
26
+ open,
27
+ defaultOpen = false,
28
+ onOpenChange,
29
+ title,
30
+ description,
31
+ children,
32
+ confirmLabel,
33
+ cancelLabel,
34
+ tone,
35
+ variant,
36
+ onConfirm,
37
+ onCancel,
38
+ lang,
39
+ className,
40
+ ...props
41
+ }) {
42
+ const baseId = React.useId();
43
+ const titleId = baseId + "-title";
44
+ const descId = baseId + "-desc";
45
+ const [ref, L] = useLang(lang);
46
+ const t = makeT("AlertDialog", L);
47
+ const panel = React.useRef(null);
48
+ const uncontrolled = open === undefined;
49
+ const [internal, setInternal] = React.useState(!!defaultOpen);
50
+ const isOpen = uncontrolled ? internal : !!open;
51
+ const setOpen = (next) => {
52
+ if (uncontrolled) setInternal(next);
53
+ onOpenChange?.(next);
54
+ };
55
+ const destructive = (tone ?? variant) === "destructive";
56
+ const confirmRef = React.useRef(onConfirm); confirmRef.current = onConfirm;
57
+ const cancelRef = React.useRef(onCancel); cancelRef.current = onCancel;
58
+
59
+ React.useEffect(() => {
60
+ if (!isOpen) return;
61
+ const prev = document.activeElement;
62
+ const prevOverflow = document.body.style.overflow;
63
+ document.body.style.overflow = "hidden";
64
+ const focusables = () => (panel.current ? [...panel.current.querySelectorAll(FOCUSABLE)] : []);
65
+ // Prefer the confirm action for destructive prompts (WAI-ARIA alertdialog).
66
+ const preferred = panel.current?.querySelector(".cs-alert-dialog__confirm") || focusables()[0];
67
+ (preferred || panel.current)?.focus?.();
68
+ const k = (e) => {
69
+ if (e.key === "Escape") {
70
+ setOpen(false);
71
+ cancelRef.current?.();
72
+ return;
73
+ }
74
+ if (e.key !== "Tab" || !panel.current) return;
75
+ const f = focusables();
76
+ if (!f.length) { e.preventDefault(); panel.current.focus(); return; }
77
+ const a = f[0], z = f[f.length - 1];
78
+ const active = document.activeElement;
79
+ const inside = panel.current.contains(active);
80
+ if (e.shiftKey && (!inside || active === a)) { e.preventDefault(); z.focus(); }
81
+ else if (!e.shiftKey && (!inside || active === z)) { e.preventDefault(); a.focus(); }
82
+ };
83
+ document.addEventListener("keydown", k);
84
+ return () => {
85
+ document.removeEventListener("keydown", k);
86
+ document.body.style.overflow = prevOverflow;
87
+ if (prev && prev.focus) prev.focus();
88
+ };
89
+ }, [isOpen]);
90
+
91
+ const cl = cancelLabel != null ? cancelLabel : t("cancel");
92
+ const cf = confirmLabel != null ? confirmLabel : t("confirm");
93
+ const body = description ?? children;
94
+ const hasDesc = body != null && body !== false && body !== "";
95
+
96
+ if (!isOpen) return null;
97
+ return (
98
+ <div ref={ref} className="cs-dialog-layer cs-alert-dialog-layer">
99
+ <div
100
+ className="cs-dialog__overlay"
101
+ onClick={() => { setOpen(false); cancelRef.current?.(); }}
102
+ aria-hidden="true"
103
+ />
104
+ <section
105
+ {...props}
106
+ ref={panel}
107
+ tabIndex={-1}
108
+ role="alertdialog"
109
+ aria-modal="true"
110
+ aria-labelledby={titleId}
111
+ aria-describedby={hasDesc ? descId : undefined}
112
+ className={cx("cs-dialog", "cs-alert-dialog", destructive && "cs-alert-dialog--destructive", className)}
113
+ >
114
+ <header className="cs-dialog__header">
115
+ <h2 id={titleId} className="cs-dialog__title">{title}</h2>
116
+ </header>
117
+ {hasDesc ? <div id={descId} className="cs-dialog__body">{body}</div> : null}
118
+ <footer className="cs-dialog__actions">
119
+ <button
120
+ type="button"
121
+ className="cs-button cs-button--ghost cs-button--md"
122
+ onClick={() => { setOpen(false); cancelRef.current?.(); }}
123
+ >
124
+ {cl}
125
+ </button>
126
+ <button
127
+ type="button"
128
+ className={cx(
129
+ "cs-button",
130
+ "cs-button--md",
131
+ "cs-alert-dialog__confirm",
132
+ destructive ? "cs-button--danger" : "cs-button--primary"
133
+ )}
134
+ onClick={() => { setOpen(false); confirmRef.current?.(); }}
135
+ >
136
+ {cf}
137
+ </button>
138
+ </footer>
139
+ </section>
140
+ </div>
141
+ );
142
+ }
@@ -0,0 +1,12 @@
1
+ **AlertDialog** — first-class confirm / destructive modal (`role="alertdialog"`). Distinct from Popconfirm (inline bubble) and Dialog (general modal). Bilingual Confirm/Cancel via the registry.
2
+
3
+ ```jsx
4
+ <AlertDialog
5
+ open={open}
6
+ onOpenChange={setOpen}
7
+ title="Delete workspace?"
8
+ description="Removes every project. This cannot be undone."
9
+ tone="destructive"
10
+ onConfirm={wipe}
11
+ />
12
+ ```
@@ -36,7 +36,7 @@ export function Drawer({ open, onClose, title, side = "right", children, actions
36
36
  <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M6 6l12 12M18 6L6 18" /></svg>
37
37
  </button>
38
38
  </div>
39
- <div className="cs-drawer__body">{children}</div>
39
+ <div className="cs-drawer__body" tabIndex={0}>{children}</div>
40
40
  {actions ? <div className="cs-drawer__footer">{actions}</div> : null}
41
41
  </aside>
42
42
  </>
@@ -16,9 +16,22 @@ export function Popconfirm({ trigger, title, onConfirm, onCancel, okLabel, cance
16
16
  document.addEventListener("keydown", k);
17
17
  return () => { document.removeEventListener("mousedown", d); document.removeEventListener("keydown", k); };
18
18
  }, [open]);
19
+ const toggle = () => setOpen((o) => !o);
20
+ // ARIA attrs must live on the interactive trigger (button), not a wrapping <span>
21
+ // — axe aria-allowed-attr flags aria-haspopup/aria-expanded on generic elements.
22
+ const triggerNode = React.isValidElement(trigger)
23
+ ? React.cloneElement(trigger, {
24
+ "aria-haspopup": "dialog",
25
+ "aria-expanded": open,
26
+ onClick: (e) => {
27
+ if (typeof trigger.props.onClick === "function") trigger.props.onClick(e);
28
+ if (!e.defaultPrevented) toggle();
29
+ },
30
+ })
31
+ : <button type="button" aria-haspopup="dialog" aria-expanded={open} onClick={toggle}>{trigger}</button>;
19
32
  return (
20
33
  <span ref={(el) => { wrap.current = el; ref.current = el; }} className={cx("cs-popconfirm", className)}>
21
- <span onClick={() => setOpen((o) => !o)} aria-haspopup="dialog" aria-expanded={open}>{trigger}</span>
34
+ {triggerNode}
22
35
  {open ? (
23
36
  <span className="cs-popconfirm__panel" role="alertdialog" aria-label={typeof title === "string" ? title : undefined}>
24
37
  <span className="cs-popconfirm__title">{title}</span>
@@ -15,9 +15,22 @@ export function Popover({ trigger, children, align = "start", open: controlled,
15
15
  document.addEventListener("keydown", k);
16
16
  return () => { document.removeEventListener("mousedown", d); document.removeEventListener("keydown", k); };
17
17
  }, [open]);
18
+ const toggle = () => set(!open);
19
+ // ARIA attrs must live on the interactive trigger (button), not a wrapping <span>
20
+ // — axe aria-allowed-attr flags aria-haspopup/aria-expanded on generic elements.
21
+ const triggerNode = React.isValidElement(trigger)
22
+ ? React.cloneElement(trigger, {
23
+ "aria-haspopup": "dialog",
24
+ "aria-expanded": open,
25
+ onClick: (e) => {
26
+ if (typeof trigger.props.onClick === "function") trigger.props.onClick(e);
27
+ if (!e.defaultPrevented) toggle();
28
+ },
29
+ })
30
+ : <button type="button" aria-haspopup="dialog" aria-expanded={open} onClick={toggle}>{trigger}</button>;
18
31
  return (
19
32
  <span className={cx("cs-popover", className)} ref={ref}>
20
- <span onClick={() => set(!open)} aria-haspopup="dialog" aria-expanded={open}>{trigger}</span>
33
+ {triggerNode}
21
34
  {open ? <div className={cx("cs-popover__panel", align === "end" && "cs-popover__panel--end")} role="dialog">{children}</div> : null}
22
35
  </span>
23
36
  );
@@ -0,0 +1,42 @@
1
+ <!-- @dsCard group="Components" viewport="700x320" name="AlertDialog" subtitle="Confirm / destructive modal (alertdialog)" -->
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <link rel="stylesheet" href="../../styles.css" />
8
+ <style>
9
+ body { margin: 0; background: var(--cs-color-surface-page); padding: 22px 24px; }
10
+ .frame { position: relative; height: 256px; border: 1px solid var(--cs-color-border-default); border-radius: var(--cs-radius-lg); overflow: hidden; background: var(--cs-color-surface-raised); }
11
+ .frame .cs-dialog-layer { position: absolute; }
12
+ .frame .cs-dialog { top: 28px; }
13
+ </style>
14
+ <script src="https://unpkg.com/umd-react@19.2.8/dist/react.development.js" integrity="sha384-r22tQd8qRIHDPIkmdXZcVtvPi1Tj2x1QaW2tmFQHEvHscATUnHQ5siTyxWw5PMUs" crossorigin="anonymous"></script>
15
+ <script src="https://unpkg.com/umd-react@19.2.8/dist/react-dom.development.js" integrity="sha384-gJZYmumEmwqVmX3JVsLZq+4QrxoPGh/BW47Rk7SqNPCPFpTGnRtvmeRPJ7//ZDPk" crossorigin="anonymous"></script>
16
+ <script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
17
+ <script src="../../_ds_bundle.js"></script>
18
+ </head>
19
+ <body>
20
+ <div id="root"></div>
21
+ <script type="text/babel">
22
+ const { AlertDialog, Button } = (window[Object.keys(window).find(k=>/^CyberSkillDesignSystem_[0-9a-f]{6}$/.test(k))]||{});
23
+ function Demo() {
24
+ const [open, setOpen] = React.useState(true);
25
+ return (
26
+ <div className="frame">
27
+ {!open && <div style={{padding:24}}><Button variant="danger-ghost" onClick={() => setOpen(true)}>Delete workspace</Button></div>}
28
+ <AlertDialog
29
+ open={open}
30
+ onOpenChange={setOpen}
31
+ title="Delete workspace?"
32
+ description="Removes every project and release. This cannot be undone."
33
+ tone="destructive"
34
+ onConfirm={() => setOpen(false)}
35
+ />
36
+ </div>
37
+ );
38
+ }
39
+ ReactDOM.createRoot(document.getElementById("root")).render(<Demo />);
40
+ </script>
41
+ </body>
42
+ </html>
@@ -11,8 +11,8 @@
11
11
  .frame { position: relative; overflow: hidden; height: 260px; border: 1px solid var(--cs-color-border-default); border-radius: var(--cs-radius-lg); background: var(--cs-color-surface-raised); }
12
12
  .frame .cs-drawer-scrim, .frame .cs-drawer { position: absolute; animation: none; }
13
13
  </style>
14
- <script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
15
- <script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
14
+ <script src="https://unpkg.com/umd-react@19.2.8/dist/react.development.js" integrity="sha384-r22tQd8qRIHDPIkmdXZcVtvPi1Tj2x1QaW2tmFQHEvHscATUnHQ5siTyxWw5PMUs" crossorigin="anonymous"></script>
15
+ <script src="https://unpkg.com/umd-react@19.2.8/dist/react-dom.development.js" integrity="sha384-gJZYmumEmwqVmX3JVsLZq+4QrxoPGh/BW47Rk7SqNPCPFpTGnRtvmeRPJ7//ZDPk" crossorigin="anonymous"></script>
16
16
  <script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
17
17
  <script src="../../_ds_bundle.js"></script>
18
18
  </head>
@@ -9,8 +9,8 @@
9
9
  body { margin: 0; background: var(--cs-color-surface-page); padding: 22px 24px; }
10
10
  .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px 24px; max-width: 640px; }
11
11
  </style>
12
- <script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
13
- <script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
12
+ <script src="https://unpkg.com/umd-react@19.2.8/dist/react.development.js" integrity="sha384-r22tQd8qRIHDPIkmdXZcVtvPi1Tj2x1QaW2tmFQHEvHscATUnHQ5siTyxWw5PMUs" crossorigin="anonymous"></script>
13
+ <script src="https://unpkg.com/umd-react@19.2.8/dist/react-dom.development.js" integrity="sha384-gJZYmumEmwqVmX3JVsLZq+4QrxoPGh/BW47Rk7SqNPCPFpTGnRtvmeRPJ7//ZDPk" crossorigin="anonymous"></script>
14
14
  <script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
15
15
  <script src="../../_ds_bundle.js"></script>
16
16
  </head>
@@ -0,0 +1,46 @@
1
+ # Design System Benchmark Rubric
2
+
3
+ Self-audit checklist for evolving CyberSkill Design System — external standards plus CDS-owned rules. Pair with [`docs/quality-gates.md`](quality-gates.md) (deterministic gates) and [`docs/design-styles.md`](design-styles.md) (Element colour contract).
4
+
5
+ ## External standards
6
+
7
+ | Domain | Standard / practice | How we measure |
8
+ |---|---|---|
9
+ | Legal a11y floor | WCAG 2.2 AA contrast (4.5:1 normal / 3:1 large / UI non-text ≥ 3:1) | `contrast-guard`, theme-overflow, axe-smoke |
10
+ | Perceptual readability | APCA Bronze-style Lc bands (body ≥ 75, UI labels ≥ 60, large ≥ 45, non-text ≥ 15; dark max ~90) | `apca-dark-preview`, [`docs/contrast-report.md`](contrast-report.md) |
11
+ | Color space | OKLCH luminance-first ramps; hue preserved across modes | `element-geometry` + `npm run tokens:elements` |
12
+ | Token architecture | W3C DTCG 2025.10 — primitive → semantic → component; typed leaves; multi-dimensional modifiers (theme × element) | `dtcg-typing`, token-format-parity, token-pipeline |
13
+ | Dual-mode theming | Same hue family light↔dark; role remap (not invert-everything) | `element-geometry` hue-lock |
14
+ | Dual-standard audit | Classify pairs: pass-both / fail-both / WCAG-only / APCA-only | Document in contrast-report workflow after token changes |
15
+ | Gamut safety | Prefer sRGB-safe chroma; clamp before emit | Generator + geometry gate |
16
+
17
+ ## CDS-owned rules
18
+
19
+ | Rule | Enforcement |
20
+ |---|---|
21
+ | Axes = Theme × Element × Language × Style until a Style Expansion Rule pass | `axis-guard` (retired Expression/Density); doctrine in design-styles |
22
+ | Text never on mid `-accent`; Ochre focus ring never remapped; status colours never remapped | `elements.css` contract · contrast-report · conventions |
23
+ | One element per surface; Tương sinh gradients only | products registry · conventions |
24
+ | Product→element registry locked; UI kits Thổ-faithful | [`docs/products.md`](products.md) |
25
+ | Expansion Rule completeness + `_audit/run.html` green + docs EN·VI parity | CLAUDE.md · docs-consistency · docs-lang-parity |
26
+ | VERSION **1.1.1** post-LAUNCH | version-stamp · docs-consistency |
27
+ | Element packs from seeds only (30 sets) | `element-geometry` · `tokens:elements` |
28
+
29
+ ## Style expansion checklist (`data-cs-style`)
30
+
31
+ Sole pack today: **liquid-glass** (`tokens/styles.css`, gate `_audit/style-contract.html`). When adding a style pack beyond liquid-glass:
32
+
33
+ 1. Token overlay that **consumes** the existing 9 `--cs-accent-*` roles (do not invent parallel Element packs).
34
+ 2. Specimen card + Storybook / Atomic View wiring if the style is product-selectable.
35
+ 3. Template / kit adoption only where the style is intentional — UI kits stay Thổ-faithful unless a decision says otherwise.
36
+ 4. Regenerate contrast surfaces if the style remaps backgrounds that host text.
37
+ 5. Expansion Rule grep for the new enum across templates, docs EN+VI, gates.
38
+ 6. Extend the style-contract allowlist; keep `_audit/run.html` green.
39
+ 7. Record the decision in [`docs/decisions.md`](decisions.md) (+ VI).
40
+
41
+ ## How to run a benchmark pass
42
+
43
+ 1. `npm run tokens:elements` (if seeds changed) → native regen → `node scripts/generate-contrast-report.mjs`
44
+ 2. Fast board: open `_audit/run.html` (or CI `fast-gates`) — all hard gates green, including **Element geometry**
45
+ 3. Whole-set Atomic View / Elements Geometry: all **30** sets (soft/middle/deep × light/dark × five elements) — soft reads as a washed pastel, middle intensity syncs across elements, deep is clearly darker, roles feel harmonious, and light↔dark keeps hue identity. Geometry gate requires **both** `minDeltaE` and `minDeltaL` (plus monotonic L) from `tokens/element-seeds.json`
46
+ 4. Update this rubric only when standards or CDS doctrine change — keep EN·VI parity
package/docs/ci-cd.md CHANGED
@@ -12,15 +12,16 @@
12
12
 
13
13
  1. **`fast-gates`** — `npm ci` + cached Playwright Chromium, serves the repo, opens `_audit/run.html` headlessly (`_audit/ci/run-gates.mjs`), fails on any hard-gate failure. Uploads import-report on failure. New rows added to the board (the Jul 2026 hardening added 8: token-format-parity, version-stamp, support-runtime-identity, package-exports-integrity, template-lang-parity, dtcg-typing, design-md-parity, bundle-freshness) are picked up automatically — the runner reads `window.__run`, not a job-side gate list.
14
14
  2. **`token-provenance`** — browser-free Node check that natives + `provenance.json` match DTCG source sha-256.
15
- 3. **`unit-tests`** — `npm run test:unit` (8 plain-Node contract tests; wired into CI by the Jul 2026 hardening — previously local-only).
15
+ 3. **`unit-tests`** — `npm run test:unit` (11 plain-Node contract tests; wired into CI by the Jul 2026 hardening — previously local-only). Includes `test-element-packs` (`node scripts/generate-element-packs.mjs --check` freshness).
16
16
  4. **`node-prechecks`** — browser-free Node authorities: `_audit/ci/check-bundle-freshness.mjs` (the bundle-freshness source of truth — full source discovery incl. new/deleted files; the board row only re-hashes header-recorded files) and `scripts/generate-design-md.mjs --check` (root `DESIGN.md` byte-equals regeneration).
17
17
  5. **`docs-consistency-blocker`** — `docs-consistency` + `bilingual-parity` merge blockers.
18
18
  6. **`whole-set-audits`** — owner decision B: every push/PR, plus nightly `0 3 * * *` and `workflow_dispatch` (responsive + language + theme overflow, ~15–20 min).
19
- 7. **`figma-variables-push`** — on `main` push + manual. Owner decision A (non-Enterprise): Variables REST soft-skips; secrets still prove file open. See `docs/figma.md`.
20
- 8. **`code-connect`** — on PR + `main` + manual. Decision 1C: dry-run always (config + 99 mappings); publish soft-skips when `FIGMA_TOKEN` / `FIGMA_FILE_KEY` missing or API 403/404/429. See `docs/figma.md`.
21
- 9. **`regenerate-tokens`** — path-filtered on push/PR (`tokens.dtcg.json`, natives, generator, `VERSION`); always available on schedule/manual. Deterministic native output; pushes with `contents: write` (or `DS_PUSH_TOKEN`).
19
+ 7. **`figma-variables-push`** — on `main` push + manual. **Empty `FIGMA_TOKEN` / `FIGMA_FILE_KEY` soft-skip** (exit 0 + report — same honesty as Code Connect). Owner decision A (non-Enterprise): Variables REST also **soft-skips on API 403** (and related plan/scope failures) after secrets prove file open. Soft-skip ≠ live Variables sync. See `docs/figma.md`.
20
+ 8. **`code-connect`** — on PR + `main` + manual. Decision 1C: dry-run always (config + 105 mappings); publish soft-skips when `FIGMA_TOKEN` / `FIGMA_FILE_KEY` missing or API 403/404/429. See `docs/figma.md`.
21
+ 9. **`regenerate-tokens`** (pull requests) + **`regenerate-tokens-push`** (push to `main` / schedule / manual) — path filter covers element seeds/packs (`element-seeds.json`, `elements.css`, JSON/JS/DTCG mirrors, `generate-element-packs.mjs`) plus natives / `VERSION`. Regeneration runs `npm run tokens:elements` then `node _audit/ci/generate-native-tokens.mjs`. On a **pull request** the job runs with `contents: read`, never pushes, and **exits 1** on drift: run both locally and commit. Only the push/schedule/manual twin holds `contents: write` and auto-commits with `DS_PUSH_TOKEN` (or `github.token`).
22
+ 10. **`npm-hello-smoke`** — registry consumer proof: `cd examples/npm-hello && npm ci && npm run smoke` (**hard fail**; package is public). Path-filtered on push/PR when `examples/npm-hello/**`, package publish surface, or this workflow changes; always on schedule / `workflow_dispatch`.
22
23
 
23
- Separate workflow **`npm-publish`** (`.github/workflows/npm-publish.yml`): `workflow_dispatch` + `v*` tags; pack dry-run always; publish soft-skips without `NPM_TOKEN`. Version stays **1.0.0**; license **UNLICENSED**.
24
+ Separate workflow **`npm-publish`** (`.github/workflows/npm-publish.yml`): `workflow_dispatch` + `v*` tags; pack dry-run always; publish via **npm Trusted Publishing (OIDC)** (`id-token: write`, no `NPM_TOKEN` on the publish step). Soft-skips on auth / 403 / 404 / EOTP / version conflict. version is **1.1.1**; license **UNLICENSED**. Trusted Publisher on npmjs must match workflow filename `npm-publish.yml`. Package Publishing access is **Require 2FA and disallow tokens** (OIDC still works; classic tokens rejected).
24
25
 
25
26
  Separate workflow **`native-store`** (`.github/workflows/native-store.yml`): PR + `main` (path-filtered) + `workflow_dispatch`; scaffold dry-run always; signed-release check soft-skips without `ASC_KEY_ID` / `ASC_ISSUER_ID` / `ASC_KEY_P8` / `PLAY_SERVICE_ACCOUNT_JSON`. **Never submits** to App Store / Play — samples remain samples. See `examples/native/README.md`.
26
27
 
@@ -39,7 +40,7 @@ Node **22** on runners (avoids Node 20 action deprecation). Playwright browser c
39
40
 
40
41
  ## Token auto-commit permissions
41
42
 
42
- Job requests `permissions: contents: write`. If org locks the default token to read-only, set repository secret `DS_PUSH_TOKEN` (fine-grained PAT, Contents read/write) or unlock write at org Actions settings. See earlier notes in git history / org settings.
43
+ Only `regenerate-tokens-push` requests `permissions: contents: write`; the pull-request twin `regenerate-tokens` stays on `contents: read` and checks out without persisted credentials, so a PR can never trigger a bot push. If org locks the default token to read-only, set repository secret `DS_PUSH_TOKEN` (fine-grained PAT, Contents read/write) or unlock write at org Actions settings. See earlier notes in git history / org settings.
43
44
 
44
45
  ## Branch protection (recommended)
45
46
 
@@ -61,24 +62,29 @@ node _audit/ci/check-bundle-freshness.mjs
61
62
  node scripts/generate-design-md.mjs --check
62
63
  npm run test:unit
63
64
  node _audit/ci/run-single-gate.mjs http://127.0.0.1:8080/_audit/docs-consistency.html __docs
65
+ npm run tokens:elements
66
+ node scripts/generate-element-packs.mjs --check
64
67
  node _audit/ci/generate-native-tokens.mjs
65
68
  ```
66
69
 
67
70
  ## What this does NOT auto-fail (by design)
68
71
 
69
72
  - **Side-by-side visual / component baseline rows** — advisory only (drift judged by eye). Playwright `%` pixel compare is a **hard** gate (`pixel-diff` job + board Pixel CI row).
70
- - **Figma Variables write on non-Enterprise** — soft-skip with report artifact.
73
+ - **Figma Variables** — soft-skip when secrets missing (same honesty as Code Connect) or Variables write hits non-Enterprise / API **403**; report artifact records the skip. Soft-skip ≠ live sync.
71
74
  - **Code Connect publish** — soft-skip when secrets missing or API 403/404/429 (Decision 1C).
72
- - **npm publish** — soft-skip when `NPM_TOKEN` absent (Decision 1C).
75
+ - **npm publish** — Trusted Publishing (OIDC) on `npm-publish.yml`; soft-skip on auth / conflict (Decision 1C).
73
76
  - **Native store signed release** — soft-skip when `ASC_*` / `PLAY_SERVICE_ACCOUNT_JSON` absent (Decision 1C); store submit stays disabled.
77
+ - **npm-hello registry smoke** — **hard fail** (`examples/npm-hello` → `npm ci` + `npm run smoke`); proves the public `@cyberskill/design` install path (not soft-skip).
78
+
79
+ A green `figma-variables-push` or `code-connect` job — and therefore a green badge or Status row — only means the job exited 0; it does **not** prove a live Figma sync, because a soft-skip and a real publish look identical from outside. Open the run's report artifact (`figma-push-report.json` / `code-connect-report.json`) to see which one happened.
74
80
 
75
81
  ## Soft-skip dry-runs (local)
76
82
 
77
83
  ```bash
78
- npm run code-connect:dry-run # config + ≥99 mappings; no secrets
84
+ npm run code-connect:dry-run # config + ≥105 mappings; no secrets
79
85
  node _audit/ci/code-connect-publish.mjs # without secrets → SOFT SKIP missing_secrets
80
- npm run npm:pack-dry-run # tarball inventory; no NPM_TOKEN
81
- node _audit/ci/npm-publish.mjs # without NPM_TOKEN → SOFT SKIP missing_secrets
86
+ npm run npm:pack-dry-run # tarball inventory; no auth
87
+ node _audit/ci/npm-publish.mjs # GHA OIDC; else SOFT SKIP (tokens disallowed on package)
82
88
  npm run native:store-dry-run # Fastlane scaffolds + metadata; no ASC_*/Play JSON
83
89
  node _audit/ci/native-store-dry-run.mjs # without secrets → SOFT SKIP missing_secrets
84
90
  ```
@@ -0,0 +1,37 @@
1
+ # Consumer grant — `@cyberskill/design`
2
+
3
+ **Status: in force** (Jul 2026). Policy text — not a secret. License on the package remains **UNLICENSED**; this grant is the approval layer for use in named products.
4
+
5
+ ## Grant
6
+
7
+ CyberSkill grants **CyberSkill** (the company and its product engineering teams under `cyberskill-official`) a **non-exclusive** right to install and use **`@cyberskill/design`** in the products listed in the locked registry `docs/products.md`:
8
+
9
+ | Product |
10
+ |---|
11
+ | Lumi — wish assistant |
12
+ | Status Hub — client portfolio portal |
13
+ | CyberOS Agent Spine — agent infra, memory, gates |
14
+ | CyberSkill Design System — this library |
15
+ | cyberskill.world — the studio site |
16
+ | Client delivery suite — kickoff · QBR · runbooks |
17
+ | Board / investor collateral |
18
+ | HR / employment instruments |
19
+
20
+ The package remains **UNLICENSED**. Installing from the npm registry does **not** by itself grant redistribution rights. Redistribution outside the products above — including publishing a fork, re-licensing, or shipping the package as a dependency of an unrelated third-party product — needs a **further written grant** recorded in this file (or a superseding instrument).
21
+
22
+ ## Scope notes
23
+
24
+ - **Clone / subtree / static link** of this repo for the same products is covered the same way as an npm install.
25
+ - **Host-only tooling** (Storybook, `_audit/`) is not part of the npm tarball and is not redistributable under this grant.
26
+ - Adding a new CyberSkill product: update `docs/products.md` (+ VI) via a maintainer decision, then add the product row here in the same change.
27
+ - Approving an **external** team or client: append a dated grant stanza below (team/org, named products, contact). Do not invent product → element mappings here — that stays in `docs/products.md`.
28
+
29
+ ## Additional grantees
30
+
31
+ *(None yet. Append dated entries here.)*
32
+
33
+ ## Related
34
+
35
+ - Adopt / install: `docs/consuming.md`
36
+ - Decision record: `docs/decisions.md` §7
37
+ - Product → element registry: `docs/products.md`
package/docs/consuming.md CHANGED
@@ -8,18 +8,18 @@ How any project — human-driven or agent-driven — adopts this HTML-first desi
8
8
 
9
9
  | Consumer | Start here | Works today | Do not |
10
10
  |---|---|---|---|
11
- | **Claude Code** | `SKILL.md` → `README.md` → `styles.css` + `_esm/cs.mjs` / `_ds_bundle.js` (prefix resolve) | Strong — rules, components, prompts; gates via full clone | Hardcode the bundle suffix; treat Storybook host as the portable contract |
11
+ | **Claude Code** | `SKILL.md` → `README.md` → `styles.css` + bundler `@cyberskill/design` / legacy `_esm/cs.mjs` / `_ds_bundle.js` (prefix resolve) | Strong — rules, components, prompts; gates via full clone | Hardcode the bundle suffix; treat Storybook host as the portable contract; import `_esm/cs.mjs` or `@cyberskill/design/legacy` into Next/SSR bundlers |
12
12
  | **Google Stitch** | `DESIGN.md` → `llms.txt` → `tokens/tokens.dtcg.json` | Strong for doctrine + tokens + static `.cs-*` HTML | Treat `templates/**/*.dc.html` as SoT — no tweaks / `__dcSetProps` / DC compiler |
13
13
  | **Claude Design** | Full repo + DC compiler | Full fidelity (tweaks, `x-import`, bilingual templates) | Skip the sync loop in `docs/sync.md` |
14
- | **npm** | `@cyberskill/design` | Org + `NPM_TOKEN` ready; dry-run always | Assume registry install is live before first **npm-publish** run + written consumer grant |
14
+ | **npm** | `@cyberskill/design` | **1.1.1** on registry; CI Trusted Publishing; grant in `docs/consumer-grant.md` | Treat registry install as a public license (still UNLICENSED) |
15
15
 
16
16
  **Stitch DC rule:** Stitch (and any non-DC tool) must **not** consume `*.dc.html` as source of truth. Use static export patterns, `templates/kitchen-sink.html`, `examples/static-hello/`, and `.cs-*` classes from `styles.css`.
17
17
 
18
- **Release signal:** VERSION stays pinned at **1.0.0** (no CHANGELOG). Treat the **git tip SHA** as technical truth; read curated product highlights in `docs/release-notes.md`.
18
+ **Release signal:** version is LAUNCHED at **1.1.0**; current pin **1.1.1** (no CHANGELOG). Treat the **git tip SHA** as technical truth; read curated product highlights in `docs/release-notes.md`.
19
19
 
20
20
  ## Quick path for AI agents (Claude Code, or driving one)
21
21
 
22
- **What you get:** `styles.css` (400+ tokens + `.cs-*` classes + Liquid Glass surfaces, `@import`s `tokens/` + `base/`) · `_ds_bundle.js` (compiled React components, no build step) · `_esm/cs.mjs` (ESM entry re-exporting every component) · `_ds_manifest.json` (machine-readable inventory) · per-component `Name.d.ts` (API) + `Name.prompt.md` (usage brief) · `tokens/tokens.dtcg.json` (W3C DTCG) + `tokens.json`/`tokens.js`.
22
+ **What you get:** `styles.css` (400+ tokens + `.cs-*` classes + Liquid Glass surfaces, `@import`s `tokens/` + `base/`) · `_ds_bundle.js` (compiled React components, no build step) · `_esm/react.mjs` (bundler-native React entry — React as peerDependency; default `exports["."]`) · `_esm/cs.mjs` (browser / no-build legacy via `exports["./legacy"]`) · `_ds_manifest.json` (machine-readable inventory) · per-component `Name.d.ts` (API) + `Name.prompt.md` (usage brief) · `tokens/tokens.dtcg.json` (W3C DTCG) + `tokens.json`/`tokens.js`.
23
23
 
24
24
  **Repo checkout** — clone or copy the whole tree; everything is relative-path static. Entry points: Storybook on the host site (`/` / `npm run storybook`) · `guidelines/atomic-view.html` (every component live, portable) · `templates/<slug>/` (copyable starting points — DC for Claude Design; kitchen-sink / `.cs-*` for Stitch). Read `SKILL.md` before authoring anything on-brand; Stitch readers start at `DESIGN.md`. Deeper maps live in `llms.txt` (inventory) and this file (full adoption + upgrade guide below).
25
25
 
@@ -38,18 +38,29 @@ How any project — human-driven or agent-driven — adopts this HTML-first desi
38
38
 
39
39
  ## Adopt via npm (optional)
40
40
 
41
- The package is publishable (`private: false`, version pinned **1.0.0**). License remains **UNLICENSED** — installing from the registry (or a packed tarball) does **not** grant redistribution rights by itself. Until the owner chooses an open license, **consumers need an explicit grant** from CyberSkill to use the package in a product. The `@cyberskill` org and repo `NPM_TOKEN` are operator-ready; run **Actions → npm-publish** once so `@cyberskill/design` is on the registry. Without `NPM_TOKEN` the workflow **soft-skips** (exit 0 + report) — that is honesty, not a successful registry release. See `docs/decisions.md` and `docs/quality-gates.md`.
41
+ The package is publishable (`private: false`, version **1.1.1**). License remains **UNLICENSED** — installing from the registry does **not** grant redistribution rights by itself. **LAUNCH was `@cyberskill/design@1.1.0`; current pin is `@cyberskill/design@1.1.1` (publish via tag `v1.1.1`).** CI republishes via **npm Trusted Publishing (OIDC)** on workflow `npm-publish.yml` (no long-lived publish token; package Publishing access **disallows tokens**). Approved use is recorded in **`docs/consumer-grant.md`** (CyberSkill portfolio products from `docs/products.md`). See `docs/decisions.md` and `docs/ci-cd.md`.
42
42
 
43
- **Consumer grant (owner policy — not a secret).** For each approved team, record something like: *CyberSkill grants [Team/Org] a non-exclusive right to use `@cyberskill/design` in [named products]. The package remains UNLICENSED; redistribution outside those products needs a further written grant.* Keep grant text internal unless you want it public.
43
+ **Consumer grant (owner policy — not a secret).** Written and in force at `docs/consumer-grant.md` (+ `docs/vi/consumer-grant.md`). External teams need a dated stanza appended there; redistribution outside granted products needs a further written grant.
44
44
 
45
45
  ```bash
46
- # after a successful npm-publish workflow run (or from a packed tarball)
47
- npm install @cyberskill/design@1.0.0
46
+ npm install @cyberskill/design@1.1.1
48
47
  ```
49
48
 
50
- Then link styles and import from the package entry (`_esm/cs.mjs` via `exports["."]`), or continue using the static tree paths below. The published tarball is the **full portable tree** (styles, tokens, components, templates, guidelines, docs, UI kits) — not a minimal “lib-only” subset. Host-only tooling (Storybook, `_audit/`) is not in `files[]`.
49
+ Then link styles and import components. **Next.js / Vite / SSR apps** use the default entry (bundler-native):
51
50
 
52
- **Publish path (maintainers):** `prepublishOnly` runs `build:bundle` + `build:design-md --check`. Workflow `.github/workflows/npm-publish.yml` on `workflow_dispatch` / `v*` tags; publish requires `NPM_TOKEN` (`node _audit/ci/npm-publish.mjs --dry-run` always lists the tarball). See `docs/ci-cd.md` and `docs/decisions.md`.
51
+ ```ts
52
+ import { Button, TextField } from "@cyberskill/design";
53
+ // equivalent: import { Button } from "@cyberskill/design/react";
54
+ import "@cyberskill/design/styles.css";
55
+ ```
56
+
57
+ Add `transpilePackages: ["@cyberskill/design"]` in Next.js (JSX ships as source). React and react-dom are **peerDependencies** — your app provides them. Published types use `export type *` (**TypeScript 5.0+**). Do **not** import `_esm/cs.mjs` (or `@cyberskill/design/legacy`) into an SSR bundler; that path self-ensures React via CDN and side-loads `_ds_bundle.js` for browsers only.
58
+
59
+ **Browser / no-build:** import `@cyberskill/design/legacy` (`_esm/cs.mjs`) or continue with the static tree paths below. The published tarball is the **full portable tree** (styles, tokens, components, templates, guidelines, docs, UI kits) — not a minimal “lib-only” subset. Host-only tooling (Storybook, `_audit/`) is not in `files[]`.
60
+
61
+ **First real install (Lumi).** Copy-paste working **browser** consumer: `examples/npm-hello/` — installs `@cyberskill/design@1.1.1` from the registry, links `styles.css`, mounts `Button` via the **legacy** browser entry (`_esm/cs.mjs`), scopes **Lumi** with `data-cs-element="hoa" data-cs-variant="plasma"` (locked row in `docs/products.md`). From that folder: `npm install && npm run smoke && npm start` → open `http://127.0.0.1:8766/`. Status Hub teams use the same install and swap to `data-cs-element="thuy"` — do not invent mappings. For Next/SSR product apps (e.g. Lumi landing), use `import { Button } from "@cyberskill/design"` as above — not the npm-hello import map.
62
+
63
+ **Publish path (maintainers):** `prepublishOnly` runs `build:bundle` + `build:design-md --check`. Workflow `.github/workflows/npm-publish.yml` on `workflow_dispatch` / `v*` tags uses **Trusted Publishing** (`permissions.id-token: write`; do not set `NODE_AUTH_TOKEN` on the publish step). `node _audit/ci/npm-publish.mjs --dry-run` always lists the tarball. See `docs/ci-cd.md` and `docs/decisions.md`.
53
64
 
54
65
  ## Adopt (two paths, plus a module shortcut)
55
66
 
@@ -65,7 +76,9 @@ Then link styles and import from the package entry (`_esm/cs.mjs` via `exports["
65
76
  </script>
66
77
  ```
67
78
  This is exactly what `_audit/consumer-smoke-test.html` exercises (and asserts green) — and the templates' `ds-base.js` does the same, publishing a stable `window.CyberSkillDS` alias.
68
- **2b. ESM (module) path — one import, no build.** `import { Button, TextField } from "<path>/_esm/cs.mjs"` — the module self-ensures React (pinned; skipped when `window.React` exists), side-loads `_ds_bundle.js` once, resolves the namespace by prefix, and re-exports all components (`_audit/esm-smoke-test.html` keeps the export list in lockstep with the manifest). Still link `styles.css` yourself.
79
+ **2b. Bundler-native React (Next / Vite / SSR) — default package entry.** `import { Button, TextField } from "@cyberskill/design"` resolves to `_esm/react.mjs` (also exported as `@cyberskill/design/react`). Re-exports every component from source JSX with **React as an external peer** — no CDN, no `_ds_bundle.js` side-load. Your bundler must transpile the package (Next: `transpilePackages: ["@cyberskill/design"]`). Still link `styles.css` yourself. Regenerated with `npm run build:react-entry`; parity is gated by `package-exports-integrity` + `test:react-entry`.
80
+
81
+ **2c. Browser ESM legacy — one import, no build.** `import { Button, TextField } from "@cyberskill/design/legacy"` (or `"<path>/_esm/cs.mjs"`) — the module self-ensures React via **umd-react@19.2.8** (SRI-pinned CDN; official React 19 has no UMD; skipped when `window.React` exists), side-loads `_ds_bundle.js` once, resolves the namespace by prefix, and re-exports all components (`_audit/esm-smoke-test.html` keeps the export list in lockstep with the manifest). Still link `styles.css` yourself. **Not for Next/SSR.** Bundler apps should keep providing their own `react` / `react-dom` peers (`^18 || ^19`).
69
82
 
70
83
  **Templates.** Each `templates/<slug>/` is a Design Component seeded from `ds-base.js` (one `base` line to rebind the path to wherever this system lives relative to the consuming page). Copy the folder and edit copy/tweaks.
71
84
 
@@ -73,13 +86,17 @@ This is exactly what `_audit/consumer-smoke-test.html` exercises (and asserts gr
73
86
 
74
87
  **Static HTML / no React / no build tooling.** Link `styles.css` and compose with `.cs-*` classes — full catalog demonstrated in `templates/kitchen-sink.html`.
75
88
 
76
- ## The three axes
89
+ **Fonts, including a display face.** All three brand families are self-hosted in `fonts/` and declared by `tokens/fonts.css` (which `styles.css` `@import`s): **Be Vietnam Pro** (`--cs-font-family-ui`), **JetBrains Mono** (`--cs-font-family-mono`), and **Space Grotesk** (`--cs-font-family-display`, variable 300–700, Vietnamese subset included). The display face is **opt-in** — nothing in the DS points at it. A product that wants a headline face adds `class="cs-display-face"` (or sets `--cs-heading-family: var(--cs-font-family-display)`) on the scope; heading utilities follow, body copy stays on the UI face.
90
+
91
+ Consumers that skip `styles.css` to control font loading (`cyberskill.world` / Lumi imports the token + base sheets individually and keeps its own `font-display: optional` strategy) can switch from their ad-hoc `brand-fonts.css` to the package: either `@import "@cyberskill/design/tokens/fonts.css"` for the packaged faces, or keep the local `@font-face` block and point `--cs-heading-family` at `var(--cs-font-family-display)` so the **role** comes from the DS even when the **bytes** are served locally. Either way the display face stops being a per-product exception. Space Grotesk has no 800 weight, so `--cs-heading-weight-strong` (800) under `.cs-display-face` clamps to 700 within Space Grotesk — CSS does not hop to Be Vietnam Pro for a missing weight.
77
92
 
78
- State Theme (`data-theme`), Element (`data-cs-element` + `data-cs-variant`), and Language (`lang` / template Language tweak) on a container; everything inside re-skins with no code change (see `templates/playground.html`). Defaults: `light · tho · en`. Surface treatment is liquid-glass (fixed). Bilingual: components resolve strings from `lang` (`lang="vi"` on any container → full Vietnamese).
93
+ ## The four axes
94
+
95
+ State Theme (`data-theme` — light / dark / system), Element (`data-cs-element` + `data-cs-variant`), Language (`lang` / template Language tweak), and Style (`data-cs-style`) on a container; everything inside re-skins with no code change (see `templates/playground.html`). Defaults: `light · tho · en · liquid-glass` (absent theme / style ≡ those defaults). Bilingual: components resolve strings from `lang` (`lang="vi"` on any container → full Vietnamese).
79
96
 
80
97
  ## Upgrading
81
98
 
82
- - **Version is pinned.** `VERSION` and `package.json` stay at **1.0.0**. There is no design-system changelog file — treat the **git tip SHA** as the technical truth, and read curated **Release Notes** (Storybook + `docs/release-notes.md`) for product-facing highlights.
99
+ - **Version is pinned.** `VERSION` and `package.json` stay at **1.1.1**. There is no design-system changelog file — treat the **git tip SHA** as the technical truth, and read curated **Release Notes** (Storybook + `docs/release-notes.md`) for product-facing highlights.
83
100
 
84
101
  - Anchors (Umber/Ochre), the `.cs-*` class names, and the `--cs-*` token names are stable contracts — safe to depend on. Breaking renames of those contracts should be rare and called out in the PR/docs when they happen.
85
102
 
@@ -87,10 +104,22 @@ State Theme (`data-theme`), Element (`data-cs-element` + `data-cs-variant`), and
87
104
 
88
105
  ## Host Storybook (optional)
89
106
 
90
- The live site serves Storybook at `/` as the **product surface** for operators (Theme × Element × Language + control matrices). That is **host-only tooling** — do not depend on Storybook in product apps. Portable Atomic View remains at `guidelines/atomic-view.html`. See `docs/storybook.md` and `docs/live-hub.md`.
107
+ The live site serves Storybook at `/` as the **product surface** for operators (Theme × Element × Language × Style + control matrices). That is **host-only tooling** — do not depend on Storybook in product apps. Portable Atomic View remains at `guidelines/atomic-view.html`. See `docs/storybook.md` and `docs/live-hub.md`.
91
108
 
92
109
  ## Five-minute consumer spike
93
110
 
111
+ **Registry path (preferred for product apps):**
112
+
113
+ ```bash
114
+ cd examples/npm-hello
115
+ npm install
116
+ npm run smoke
117
+ npm start
118
+ # open http://127.0.0.1:8766/
119
+ ```
120
+
121
+ **Clone / static path (no npm):**
122
+
94
123
  ```bash
95
124
  # from monorepo root
96
125
  python3 -m http.server 8765 --bind 127.0.0.1
@@ -102,7 +131,7 @@ Edit `examples/static-hello/index.html`: flip `data-theme="dark"` or `data-cs-el
102
131
 
103
132
  ## Native sample hosts (optional)
104
133
 
105
- Multi-screen samples (Sign in · Home · Settings) live under `examples/native/swiftui`, `compose`, and `flutter`. They sync generated tokens via `node examples/native/sync-tokens.mjs`. See `examples/native/README.md`. Not required for web consumers.
134
+ Multi-screen samples (Sign in · Home · Settings) live under `examples/native/swiftui`, `compose`, and `flutter`. They sync generated tokens via `node examples/native/sync-tokens.mjs`. Compose opens in **Android Studio** (no committed `gradlew` — CLI needs JDK 17+ + SDK; see `examples/native/README.md`). Not required for web consumers.
106
135
 
107
136
  ## Extending
108
137