@beehexa/design-rules 0.0.0-stage → 2611.3.3

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 (290) hide show
  1. package/README.md +56 -2
  2. package/dist/AGENTS.md +24 -0
  3. package/dist/catalog.json +1814 -0
  4. package/dist/docs/public/_redirects +7 -0
  5. package/dist/docs/public/apple-touch-icon.png +0 -0
  6. package/dist/docs/public/brand/logos/app/apple-touch-icon.png +0 -0
  7. package/dist/docs/public/brand/logos/app/avatar-800.png +0 -0
  8. package/dist/docs/public/brand/logos/app/icon-192.png +0 -0
  9. package/dist/docs/public/brand/logos/app/icon-512.png +0 -0
  10. package/dist/docs/public/brand/logos/app/icon-maskable-512.png +0 -0
  11. package/dist/docs/public/brand/logos/archive/d-550/beehexa-horizontal-1-light.svg +1 -0
  12. package/dist/docs/public/brand/logos/archive/d-550/beehexa-horizontal-2-dark.svg +1 -0
  13. package/dist/docs/public/brand/logos/archive/d-550/beehexa-horizontal-3-white.svg +1 -0
  14. package/dist/docs/public/brand/logos/archive/d-550/beehexa-horizontal-4-graphite.svg +1 -0
  15. package/dist/docs/public/brand/logos/archive/d-550/hexasync-horizontal-1-light.svg +1 -0
  16. package/dist/docs/public/brand/logos/archive/d-550/hexasync-horizontal-2-dark.svg +1 -0
  17. package/dist/docs/public/brand/logos/archive/d-550/hexasync-horizontal-3-white.svg +1 -0
  18. package/dist/docs/public/brand/logos/archive/d-550/hexasync-horizontal-4-graphite.svg +1 -0
  19. package/dist/docs/public/brand/logos/archive/d-550/ui/beehexa-d-550-dark.svg +1 -0
  20. package/dist/docs/public/brand/logos/archive/d-550/ui/beehexa-d-550-light.svg +1 -0
  21. package/dist/docs/public/brand/logos/archive/d-550/ui/beehexa-design-d-550-dark.svg +1 -0
  22. package/dist/docs/public/brand/logos/archive/d-550/ui/beehexa-design-d-550-light.svg +1 -0
  23. package/dist/docs/public/brand/logos/archive/d-550/ui/hexasync-d-550-dark.svg +1 -0
  24. package/dist/docs/public/brand/logos/archive/d-550/ui/hexasync-d-550-light.svg +1 -0
  25. package/dist/docs/public/brand/logos/archive/d-550/ui/png/beehexa-design-d-550-dark@3x.png +0 -0
  26. package/dist/docs/public/brand/logos/archive/d-550/ui/png/beehexa-design-d-550-light@3x.png +0 -0
  27. package/dist/docs/public/brand/logos/beehexa-logo.zip +0 -0
  28. package/dist/docs/public/brand/logos/fig/anatomy-en.png +0 -0
  29. package/dist/docs/public/brand/logos/fig/anatomy-vi.png +0 -0
  30. package/dist/docs/public/brand/logos/fig/beehexa-dark-12.png +0 -0
  31. package/dist/docs/public/brand/logos/fig/beehexa-dark-14.png +0 -0
  32. package/dist/docs/public/brand/logos/fig/beehexa-dark-28.png +0 -0
  33. package/dist/docs/public/brand/logos/fig/beehexa-dark-32.png +0 -0
  34. package/dist/docs/public/brand/logos/fig/beehexa-dark-56.png +0 -0
  35. package/dist/docs/public/brand/logos/fig/beehexa-graphite-28.png +0 -0
  36. package/dist/docs/public/brand/logos/fig/beehexa-graphite-32.png +0 -0
  37. package/dist/docs/public/brand/logos/fig/beehexa-light-14.png +0 -0
  38. package/dist/docs/public/brand/logos/fig/beehexa-light-21.png +0 -0
  39. package/dist/docs/public/brand/logos/fig/beehexa-light-22.png +0 -0
  40. package/dist/docs/public/brand/logos/fig/beehexa-light-24.png +0 -0
  41. package/dist/docs/public/brand/logos/fig/beehexa-light-28.png +0 -0
  42. package/dist/docs/public/brand/logos/fig/beehexa-light-32.png +0 -0
  43. package/dist/docs/public/brand/logos/fig/beehexa-light-36-7c3aed7c3aed.png +0 -0
  44. package/dist/docs/public/brand/logos/fig/beehexa-light-36-swap.png +0 -0
  45. package/dist/docs/public/brand/logos/fig/beehexa-light-36.png +0 -0
  46. package/dist/docs/public/brand/logos/fig/beehexa-light-56.png +0 -0
  47. package/dist/docs/public/brand/logos/fig/beehexa-white-20.png +0 -0
  48. package/dist/docs/public/brand/logos/fig/beehexa-white-32.png +0 -0
  49. package/dist/docs/public/brand/logos/fig/clearspace.png +0 -0
  50. package/dist/docs/public/brand/logos/fig/construction-en.png +0 -0
  51. package/dist/docs/public/brand/logos/fig/construction-vi.png +0 -0
  52. package/dist/docs/public/brand/logos/fig/container-en.png +0 -0
  53. package/dist/docs/public/brand/logos/fig/container-vi.png +0 -0
  54. package/dist/docs/public/brand/logos/fig/hexasync-dark-20.png +0 -0
  55. package/dist/docs/public/brand/logos/fig/hexasync-dark-28.png +0 -0
  56. package/dist/docs/public/brand/logos/fig/hexasync-dark-48.png +0 -0
  57. package/dist/docs/public/brand/logos/fig/hexasync-light-19.png +0 -0
  58. package/dist/docs/public/brand/logos/fig/hexasync-light-28.png +0 -0
  59. package/dist/docs/public/brand/logos/fig/hexasync-light-48.png +0 -0
  60. package/dist/docs/public/brand/logos/fig/icon-graphite-14.png +0 -0
  61. package/dist/docs/public/brand/logos/fig/icon-graphite-16.png +0 -0
  62. package/dist/docs/public/brand/logos/fig/icon-light-16.png +0 -0
  63. package/dist/docs/public/brand/logos/fig/icon-light-24.png +0 -0
  64. package/dist/docs/public/brand/logos/fig/icon-light-48.png +0 -0
  65. package/dist/docs/public/brand/logos/fig/icon-light-92.png +0 -0
  66. package/dist/docs/public/brand/logos/fig/meaning-bee-en.png +0 -0
  67. package/dist/docs/public/brand/logos/fig/meaning-bee-vi.png +0 -0
  68. package/dist/docs/public/brand/logos/fig/meaning-outline-en.png +0 -0
  69. package/dist/docs/public/brand/logos/fig/meaning-outline-vi.png +0 -0
  70. package/dist/docs/public/brand/logos/fig/meaning-stack-en.png +0 -0
  71. package/dist/docs/public/brand/logos/fig/meaning-stack-vi.png +0 -0
  72. package/dist/docs/public/brand/logos/fig/misuse-serif.png +0 -0
  73. package/dist/docs/public/brand/logos/icon/beehexa-icon-graphite.svg +1 -0
  74. package/dist/docs/public/brand/logos/icon/beehexa-icon-white.svg +1 -0
  75. package/dist/docs/public/brand/logos/icon/beehexa-icon-yellow.svg +1 -0
  76. package/dist/docs/public/brand/logos/lockups/beehexa-horizontal-1-light.svg +1 -0
  77. package/dist/docs/public/brand/logos/lockups/beehexa-horizontal-2-dark.svg +1 -0
  78. package/dist/docs/public/brand/logos/lockups/beehexa-horizontal-3-white.svg +1 -0
  79. package/dist/docs/public/brand/logos/lockups/beehexa-horizontal-4-graphite.svg +1 -0
  80. package/dist/docs/public/brand/logos/lockups/hexasync-horizontal-1-light.svg +1 -0
  81. package/dist/docs/public/brand/logos/lockups/hexasync-horizontal-2-dark.svg +1 -0
  82. package/dist/docs/public/brand/logos/lockups/hexasync-horizontal-3-white.svg +1 -0
  83. package/dist/docs/public/brand/logos/lockups/hexasync-horizontal-4-graphite.svg +1 -0
  84. package/dist/docs/public/brand/logos/ui/beehexa-dark.svg +1 -0
  85. package/dist/docs/public/brand/logos/ui/beehexa-design-dark.svg +1 -0
  86. package/dist/docs/public/brand/logos/ui/beehexa-design-light.svg +1 -0
  87. package/dist/docs/public/brand/logos/ui/beehexa-icon-yellow.svg +1 -0
  88. package/dist/docs/public/brand/logos/ui/beehexa-light.svg +1 -0
  89. package/dist/docs/public/brand/logos/ui/hexasync-dark.svg +1 -0
  90. package/dist/docs/public/brand/logos/ui/hexasync-light.svg +1 -0
  91. package/dist/docs/public/brand/logos/ui/png/beehexa-design-dark@3x.png +0 -0
  92. package/dist/docs/public/brand/logos/ui/png/beehexa-design-light@3x.png +0 -0
  93. package/dist/docs/public/favicon.ico +0 -0
  94. package/dist/docs/public/favicon.svg +1 -0
  95. package/dist/docs/public/fonts/LICENSE-Inter.txt +93 -0
  96. package/dist/docs/public/fonts/LICENSE-JetBrainsMono.txt +93 -0
  97. package/dist/docs/public/fonts/inter-latin-ext-opsz-wght-italic.woff2 +0 -0
  98. package/dist/docs/public/fonts/inter-latin-ext-opsz-wght-normal.woff2 +0 -0
  99. package/dist/docs/public/fonts/inter-latin-opsz-wght-italic.woff2 +0 -0
  100. package/dist/docs/public/fonts/inter-latin-opsz-wght-normal.woff2 +0 -0
  101. package/dist/docs/public/fonts/inter-vietnamese-opsz-wght-italic.woff2 +0 -0
  102. package/dist/docs/public/fonts/inter-vietnamese-opsz-wght-normal.woff2 +0 -0
  103. package/dist/docs/public/fonts/jetbrains-mono-latin-ext-wght-normal.woff2 +0 -0
  104. package/dist/docs/public/fonts/jetbrains-mono-latin-wght-normal.woff2 +0 -0
  105. package/dist/docs/public/fonts/jetbrains-mono-vietnamese-wght-normal.woff2 +0 -0
  106. package/dist/docs/public/icon-192.png +0 -0
  107. package/dist/docs/public/icon-512.png +0 -0
  108. package/dist/docs/public/icon-maskable-512.png +0 -0
  109. package/dist/docs/public/robots.txt +4 -0
  110. package/dist/docs/public/site.webmanifest +21 -0
  111. package/dist/docs/scripts/color-pairs.mjs +46 -0
  112. package/dist/docs/scripts/contrast-exceptions.mjs +6 -0
  113. package/dist/docs/scripts/token-utils.d.mts +26 -0
  114. package/dist/docs/scripts/token-utils.mjs +114 -0
  115. package/dist/docs/src/brand/Beehexa-brandguide-full-mar12.pdf +0 -0
  116. package/dist/docs/src/brand/color+typoDirections.pdf +0 -0
  117. package/dist/docs/src/brand/structure-proposal.md +227 -0
  118. package/dist/docs/src/components/ChildPages.astro +25 -0
  119. package/dist/docs/src/components/CodeExample.astro +18 -0
  120. package/dist/docs/src/components/ColorDemo.astro +794 -0
  121. package/dist/docs/src/components/ColorExample.astro +32 -0
  122. package/dist/docs/src/components/ColorSwatch.astro +20 -0
  123. package/dist/docs/src/components/ComponentTabs.astro +21 -0
  124. package/dist/docs/src/components/DocsFooter.astro +25 -0
  125. package/dist/docs/src/components/HelpOptions.astro +20 -0
  126. package/dist/docs/src/components/IconBrowser.astro +257 -0
  127. package/dist/docs/src/components/IconDemo.astro +185 -0
  128. package/dist/docs/src/components/InDevelopment.astro +29 -0
  129. package/dist/docs/src/components/LayoutDemo.astro +384 -0
  130. package/dist/docs/src/components/Logo.astro +25 -0
  131. package/dist/docs/src/components/PhaseTable.astro +22 -0
  132. package/dist/docs/src/components/RoleGuides.astro +48 -0
  133. package/dist/docs/src/components/ShapeDemo.astro +239 -0
  134. package/dist/docs/src/components/ShapePin.astro +22 -0
  135. package/dist/docs/src/components/Sidebar.astro +206 -0
  136. package/dist/docs/src/components/SiteHeader.astro +211 -0
  137. package/dist/docs/src/components/SpacingDemo.astro +172 -0
  138. package/dist/docs/src/components/StatusBadge.astro +22 -0
  139. package/dist/docs/src/components/SystemAreas.astro +29 -0
  140. package/dist/docs/src/components/TableOfContents.astro +47 -0
  141. package/dist/docs/src/components/TokenTable.astro +40 -0
  142. package/dist/docs/src/components/TypeDemo.astro +249 -0
  143. package/dist/docs/src/components/logos/Fig.astro +28 -0
  144. package/dist/docs/src/components/logos/LogoAnatomy.astro +101 -0
  145. package/dist/docs/src/components/logos/LogoClearSpace.astro +81 -0
  146. package/dist/docs/src/components/logos/LogoColourVersions.astro +45 -0
  147. package/dist/docs/src/components/logos/LogoConstruction.astro +84 -0
  148. package/dist/docs/src/components/logos/LogoContainer.astro +84 -0
  149. package/dist/docs/src/components/logos/LogoHero.astro +42 -0
  150. package/dist/docs/src/components/logos/LogoInUse.astro +197 -0
  151. package/dist/docs/src/components/logos/LogoMeanings.astro +75 -0
  152. package/dist/docs/src/components/logos/LogoMinSize.astro +55 -0
  153. package/dist/docs/src/components/logos/LogoMistakes.astro +109 -0
  154. package/dist/docs/src/components/logos/LogoMisuse.astro +96 -0
  155. package/dist/docs/src/components/logos/LogoPair.astro +26 -0
  156. package/dist/docs/src/components/logos/LogoSvg.astro +68 -0
  157. package/dist/docs/src/content/components/button/accessibility.mdx +18 -0
  158. package/dist/docs/src/content/components/button/changelog.mdx +6 -0
  159. package/dist/docs/src/content/components/button/code.mdx +27 -0
  160. package/dist/docs/src/content/components/button/examples.mdx +21 -0
  161. package/dist/docs/src/content/components/button/meta.json +5 -0
  162. package/dist/docs/src/content/components/button/usage.mdx +18 -0
  163. package/dist/docs/src/content/foundations/guidelines/accessibility.mdx +19 -0
  164. package/dist/docs/src/content/foundations/guidelines/content.mdx +25 -0
  165. package/dist/docs/src/content/foundations/index.mdx +9 -0
  166. package/dist/docs/src/content/foundations/styles/border.mdx +89 -0
  167. package/dist/docs/src/content/foundations/styles/color/accessibility.mdx +32 -0
  168. package/dist/docs/src/content/foundations/styles/color/dark-mode.mdx +22 -0
  169. package/dist/docs/src/content/foundations/styles/color/index.mdx +65 -0
  170. package/dist/docs/src/content/foundations/styles/color/palette.mdx +57 -0
  171. package/dist/docs/src/content/foundations/styles/color/tokens.mdx +55 -0
  172. package/dist/docs/src/content/foundations/styles/color/usage.mdx +111 -0
  173. package/dist/docs/src/content/foundations/styles/data-visualization.mdx +11 -0
  174. package/dist/docs/src/content/foundations/styles/elevation.mdx +12 -0
  175. package/dist/docs/src/content/foundations/styles/icons/accessibility.mdx +72 -0
  176. package/dist/docs/src/content/foundations/styles/icons/code.mdx +171 -0
  177. package/dist/docs/src/content/foundations/styles/icons/index.mdx +62 -0
  178. package/dist/docs/src/content/foundations/styles/icons/library.mdx +19 -0
  179. package/dist/docs/src/content/foundations/styles/icons/usage.mdx +100 -0
  180. package/dist/docs/src/content/foundations/styles/illustration.mdx +9 -0
  181. package/dist/docs/src/content/foundations/styles/interaction-states.mdx +49 -0
  182. package/dist/docs/src/content/foundations/styles/layout/app-frame.mdx +81 -0
  183. package/dist/docs/src/content/foundations/styles/layout/breakpoints.mdx +56 -0
  184. package/dist/docs/src/content/foundations/styles/layout/grid.mdx +87 -0
  185. package/dist/docs/src/content/foundations/styles/layout/index.mdx +24 -0
  186. package/dist/docs/src/content/foundations/styles/layout/overlays.mdx +45 -0
  187. package/dist/docs/src/content/foundations/styles/layout/page-widths.mdx +38 -0
  188. package/dist/docs/src/content/foundations/styles/logos/downloads-and-terms.mdx +65 -0
  189. package/dist/docs/src/content/foundations/styles/logos/index.mdx +64 -0
  190. package/dist/docs/src/content/foundations/styles/logos/placement-and-containers.mdx +96 -0
  191. package/dist/docs/src/content/foundations/styles/logos/product-logos.mdx +56 -0
  192. package/dist/docs/src/content/foundations/styles/logos/using-the-logo.mdx +70 -0
  193. package/dist/docs/src/content/foundations/styles/motion.mdx +11 -0
  194. package/dist/docs/src/content/foundations/styles/opacity.mdx +9 -0
  195. package/dist/docs/src/content/foundations/styles/radius.mdx +98 -0
  196. package/dist/docs/src/content/foundations/styles/size-and-density.mdx +41 -0
  197. package/dist/docs/src/content/foundations/styles/spacing.mdx +70 -0
  198. package/dist/docs/src/content/foundations/styles/typography/code.mdx +53 -0
  199. package/dist/docs/src/content/foundations/styles/typography/index.mdx +58 -0
  200. package/dist/docs/src/content/foundations/styles/typography/usage.mdx +144 -0
  201. package/dist/docs/src/content/foundations/styles/typography/vietnamese.mdx +46 -0
  202. package/dist/docs/src/content/foundations/styles/z-index.mdx +9 -0
  203. package/dist/docs/src/content/foundations/tokens/all-tokens.mdx +36 -0
  204. package/dist/docs/src/content/foundations/tokens/index.mdx +27 -0
  205. package/dist/docs/src/content/get-started/content.mdx +14 -0
  206. package/dist/docs/src/content/get-started/design.mdx +30 -0
  207. package/dist/docs/src/content/get-started/develop.mdx +59 -0
  208. package/dist/docs/src/content/get-started/get-help.mdx +19 -0
  209. package/dist/docs/src/content/get-started/index.mdx +50 -0
  210. package/dist/docs/src/content/news/2026-10-16-site-structure.md +15 -0
  211. package/dist/docs/src/content/news/2026-10-20-docs-site.md +13 -0
  212. package/dist/docs/src/content/release-phases/index.mdx +12 -0
  213. package/dist/docs/src/content/tools/ai-agents.mdx +49 -0
  214. package/dist/docs/src/content/tools/ant-design-vue-theme.mdx +138 -0
  215. package/dist/docs/src/content/tools/eslint.mdx +15 -0
  216. package/dist/docs/src/content/tools/index.mdx +7 -0
  217. package/dist/docs/src/content/tools/stylelint.mdx +19 -0
  218. package/dist/docs/src/content-vi/foundations/styles/border.mdx +89 -0
  219. package/dist/docs/src/content-vi/foundations/styles/color/accessibility.mdx +32 -0
  220. package/dist/docs/src/content-vi/foundations/styles/color/dark-mode.mdx +22 -0
  221. package/dist/docs/src/content-vi/foundations/styles/color/index.mdx +65 -0
  222. package/dist/docs/src/content-vi/foundations/styles/color/palette.mdx +57 -0
  223. package/dist/docs/src/content-vi/foundations/styles/color/tokens.mdx +55 -0
  224. package/dist/docs/src/content-vi/foundations/styles/color/usage.mdx +111 -0
  225. package/dist/docs/src/content-vi/foundations/styles/data-visualization.mdx +11 -0
  226. package/dist/docs/src/content-vi/foundations/styles/icons/accessibility.mdx +72 -0
  227. package/dist/docs/src/content-vi/foundations/styles/icons/code.mdx +170 -0
  228. package/dist/docs/src/content-vi/foundations/styles/icons/index.mdx +62 -0
  229. package/dist/docs/src/content-vi/foundations/styles/icons/library.mdx +19 -0
  230. package/dist/docs/src/content-vi/foundations/styles/icons/usage.mdx +100 -0
  231. package/dist/docs/src/content-vi/foundations/styles/illustration.mdx +9 -0
  232. package/dist/docs/src/content-vi/foundations/styles/interaction-states.mdx +49 -0
  233. package/dist/docs/src/content-vi/foundations/styles/layout/app-frame.mdx +77 -0
  234. package/dist/docs/src/content-vi/foundations/styles/layout/breakpoints.mdx +56 -0
  235. package/dist/docs/src/content-vi/foundations/styles/layout/grid.mdx +87 -0
  236. package/dist/docs/src/content-vi/foundations/styles/layout/index.mdx +24 -0
  237. package/dist/docs/src/content-vi/foundations/styles/layout/page-widths.mdx +34 -0
  238. package/dist/docs/src/content-vi/foundations/styles/logos/downloads-and-terms.mdx +65 -0
  239. package/dist/docs/src/content-vi/foundations/styles/logos/index.mdx +64 -0
  240. package/dist/docs/src/content-vi/foundations/styles/logos/placement-and-containers.mdx +96 -0
  241. package/dist/docs/src/content-vi/foundations/styles/logos/product-logos.mdx +56 -0
  242. package/dist/docs/src/content-vi/foundations/styles/logos/using-the-logo.mdx +70 -0
  243. package/dist/docs/src/content-vi/foundations/styles/opacity.mdx +9 -0
  244. package/dist/docs/src/content-vi/foundations/styles/radius.mdx +98 -0
  245. package/dist/docs/src/content-vi/foundations/styles/size-and-density.mdx +41 -0
  246. package/dist/docs/src/content-vi/foundations/styles/spacing.mdx +70 -0
  247. package/dist/docs/src/content-vi/foundations/styles/typography/code.mdx +53 -0
  248. package/dist/docs/src/content-vi/foundations/styles/typography/index.mdx +58 -0
  249. package/dist/docs/src/content-vi/foundations/styles/typography/usage.mdx +144 -0
  250. package/dist/docs/src/content-vi/foundations/styles/typography/vietnamese.mdx +46 -0
  251. package/dist/docs/src/content-vi/foundations/styles/z-index.mdx +9 -0
  252. package/dist/docs/src/content.config.ts +68 -0
  253. package/dist/docs/src/lib/brand.ts +57 -0
  254. package/dist/docs/src/lib/contrast.ts +34 -0
  255. package/dist/docs/src/lib/i18n.ts +152 -0
  256. package/dist/docs/src/lib/icons.ts +46 -0
  257. package/dist/docs/src/lib/logo-geometry.ts +30 -0
  258. package/dist/docs/src/lib/lucide-categories.json +1 -0
  259. package/dist/docs/src/lib/lucide-data.ts +40 -0
  260. package/dist/docs/src/lib/navigation.ts +222 -0
  261. package/dist/docs/src/lib/release-phases.ts +10 -0
  262. package/dist/docs/src/lib/routes.ts +40 -0
  263. package/dist/docs/src/lib/token-descriptions-vi.ts +40 -0
  264. package/dist/docs/src/lib/tokens.ts +67 -0
  265. package/dist/docs/src/lib/tri-grid.ts +28 -0
  266. package/dist/docs/src/styles/chrome.css +122 -0
  267. package/dist/docs/src/styles/color.css +270 -0
  268. package/dist/docs/src/styles/docs.css +323 -0
  269. package/dist/docs/src/styles/global.css +66 -0
  270. package/dist/docs/src/styles/icons.css +135 -0
  271. package/dist/docs/src/styles/layout.css +165 -0
  272. package/dist/docs/src/styles/logos.css +236 -0
  273. package/dist/docs/src/styles/shapes.css +139 -0
  274. package/dist/docs/src/styles/tokens.css +461 -0
  275. package/dist/docs/src/styles/tokens.js +425 -0
  276. package/dist/docs/src/styles/typography.css +91 -0
  277. package/dist/docs/tokens/border.json +26 -0
  278. package/dist/docs/tokens/breakpoints.json +8 -0
  279. package/dist/docs/tokens/color.json +1350 -0
  280. package/dist/docs/tokens/elevation.json +26 -0
  281. package/dist/docs/tokens/layout.json +29 -0
  282. package/dist/docs/tokens/motion.json +12 -0
  283. package/dist/docs/tokens/radius.json +44 -0
  284. package/dist/docs/tokens/size.json +13 -0
  285. package/dist/docs/tokens/spacing.json +18 -0
  286. package/dist/docs/tokens/states.json +12 -0
  287. package/dist/docs/tokens/typography.json +220 -0
  288. package/dist/index.d.ts +19 -0
  289. package/dist/index.js +1823 -0
  290. package/package.json +36 -4
@@ -0,0 +1,32 @@
1
+ ---
2
+ // A small HexaSync screen drawn with role tokens only, used to show colour in context.
3
+ // `markers` adds the numbered callouts used by the "Using color" page.
4
+ import { langOf } from '../lib/i18n';
5
+ interface Props {
6
+ markers?: boolean;
7
+ style?: string;
8
+ }
9
+ const { markers = false, style } = Astro.props;
10
+ const vi = langOf(Astro.url.pathname) === 'vi';
11
+ const L = (en: string, viText: string) => (vi ? viText : en);
12
+ const n = (i: number) => (markers ? `<span class="c-ex-n">${i}</span>` : '');
13
+ ---
14
+
15
+ <div class="c-ex-card" style={style}>
16
+ <div class="c-ex-head">
17
+ <span class="c-ex-title">{L('Connections', 'Kết nối')} <Fragment set:html={n(1)} /></span>
18
+ <span><span class="bh-button bh-button-primary">{L('Add connection', 'Thêm kết nối')}</span><Fragment set:html={n(2)} /></span>
19
+ </div>
20
+ <div class="c-ex-row">
21
+ <span><b class="c-ex-name">{L('Online store', 'Cửa hàng online')}</b><span class="c-ex-meta">{L('Synced 2 min ago', 'Đồng bộ 2 phút trước')} <Fragment set:html={n(3)} /></span></span>
22
+ <span><span class="bh-label is-success">{L('Synced', 'Đã đồng bộ')}</span><Fragment set:html={n(4)} /></span>
23
+ </div>
24
+ <div class="c-ex-row is-selected">
25
+ <span><b class="c-ex-name">ERP</b><span class="c-ex-meta">{L('Token expires in 3 days', 'Token hết hạn sau 3 ngày')}</span></span>
26
+ <span><span class="bh-label is-warning">{L('Expiring', 'Sắp hết hạn')}</span><Fragment set:html={n(5)} /></span>
27
+ </div>
28
+ <div class="c-ex-row">
29
+ <span><b class="c-ex-name">{L('Accounting', 'Kế toán')}</b><span class="c-ex-meta"><a class="c-ex-link" href="#">{L('View error log', 'Xem nhật ký lỗi')}</a> <Fragment set:html={n(6)} /></span></span>
30
+ <span class="bh-label is-danger">{L('Failed', 'Lỗi')}</span>
31
+ </div>
32
+ </div>
@@ -0,0 +1,20 @@
1
+ ---
2
+ import { tokensByPrefix } from '../lib/tokens';
3
+
4
+ // Grid of color swatches for every color token under `prefix`.
5
+ interface Props {
6
+ prefix: string;
7
+ }
8
+
9
+ const tokens = tokensByPrefix(Astro.props.prefix).filter((token) => token.type === 'color');
10
+ ---
11
+
12
+ <ul class="swatch-grid">
13
+ {tokens.map((token) => (
14
+ <li>
15
+ <span class="swatch-large" style={`background: var(${token.cssVar})`} aria-hidden="true"></span>
16
+ <code>{token.name}</code>
17
+ <span>{token.resolved}</span>
18
+ </li>
19
+ ))}
20
+ </ul>
@@ -0,0 +1,21 @@
1
+ ---
2
+ import { tabLabels } from '../lib/navigation';
3
+ import { langOf, localize } from '../lib/i18n';
4
+
5
+ const viTabs: Record<string, string> = { examples: 'Ví dụ', code: 'Code', usage: 'Cách dùng', accessibility: 'Khả năng tiếp cận', changelog: 'Nhật ký thay đổi' };
6
+
7
+ interface Props {
8
+ name: string;
9
+ tabs: (keyof typeof tabLabels)[];
10
+ current: keyof typeof tabLabels;
11
+ }
12
+
13
+ const { name, tabs, current } = Astro.props;
14
+ const lang = langOf(Astro.url.pathname);
15
+ ---
16
+
17
+ <nav class="component-tabs" aria-label={lang === 'vi' ? 'Tài liệu component' : 'Component documentation'}>
18
+ {tabs.map((tab) => (
19
+ <a href={localize(`/components/${name}/${tab}/`, lang)} aria-current={tab === current ? 'page' : undefined}>{lang === 'vi' ? viTabs[tab] : tabLabels[tab]}</a>
20
+ ))}
21
+ </nav>
@@ -0,0 +1,25 @@
1
+ ---
2
+ // Site footer: one line. Everything a reader needs to move around is already on screen
3
+ // (header: Design system, News; sidebar: every page; under each page: feedback and changelog),
4
+ // so the footer only carries what lives nowhere else: the copyright and the legal
5
+ // links. The owner is Beehexa Corp, the company named in the Terms of Service. Same shape as fluent2.microsoft.design (checked 23/10/2026).
6
+ // llms.txt stays at /llms.txt for AI tools. A trademark link joins the legal links once
7
+ // Logos › Downloads and terms is written.
8
+ import { langOf, t } from '../lib/i18n';
9
+
10
+ const year = new Date().getFullYear();
11
+ const T = t(langOf(Astro.url.pathname));
12
+
13
+ const links = [
14
+ { label: T.terms, href: 'https://beehexa.com/legal/beehexa-term-of-service' },
15
+ { label: T.privacy, href: 'https://beehexa.com/legal/privacy-policy' },
16
+ { label: T.cookies, href: 'https://beehexa.com/legal/beehexa-cookie-policy' },
17
+ ];
18
+ ---
19
+
20
+ <footer class="site-footer">
21
+ <span>© {year} Beehexa Corp</span>
22
+ <nav aria-label={T.legal}>
23
+ {links.map((link) => <a href={link.href}>{link.label}</a>)}
24
+ </nav>
25
+ </footer>
@@ -0,0 +1,20 @@
1
+ ---
2
+ import { feedbackEmail, feedbackHref } from '../lib/brand';
3
+
4
+ const proposal = `mailto:${feedbackEmail}?subject=${encodeURIComponent('beehexa.design proposal')}`;
5
+ ---
6
+
7
+ <ul class="help-list">
8
+ <li>
9
+ <b>Ask a question</b>
10
+ <span>A shared channel is being set up. Until then, email <a href={`mailto:${feedbackEmail}`}>{feedbackEmail}</a>.</span>
11
+ </li>
12
+ <li>
13
+ <b>Report a problem</b>
14
+ <span>Something wrong, missing or hard to follow? <a href={feedbackHref(Astro.url.pathname)}>Report it</a>. Every page also has this link at the bottom.</span>
15
+ </li>
16
+ <li>
17
+ <b>Propose a change</b>
18
+ <span>Need a new token, component or rule? <a href={proposal}>Send a proposal</a> with the problem it solves and where you need it.</span>
19
+ </li>
20
+ </ul>
@@ -0,0 +1,257 @@
1
+ ---
2
+ // Icon library: every Lucide icon, with search by name and tags, a category filter, a size
3
+ // switch and click to copy the name. The page holds only the controls; the icons come from
4
+ // /foundations/styles/icons/lucide.json (built from lucide-static) when the library comes into
5
+ // view, about 540 kB, 140 kB compressed. Without JavaScript the page links to lucide.dev.
6
+ import Search from '@lucide/astro/icons/search';
7
+ import ChevronDown from '@lucide/astro/icons/chevron-down';
8
+ import { lucide } from '../lib/lucide-data';
9
+ import { iconActions } from '../lib/icons';
10
+ import '../styles/icons.css';
11
+ import { langOf } from '../lib/i18n';
12
+
13
+ const { version, names, categories } = lucide();
14
+ const vi = langOf(Astro.url.pathname) === 'vi';
15
+ const L = (en: string, viText: string) => (vi ? viText : en);
16
+ const n = (value: number) => value.toLocaleString(vi ? 'vi-VN' : 'en-US');
17
+ const total = n(names.length);
18
+ const picks = iconActions.map((action) => action.icon);
19
+ // Search also matches the meaning, in English and Vietnamese, so “sync” and “đồng bộ” both find refresh-cw.
20
+ const meanings = Object.fromEntries(iconActions.map((a) => [a.icon, `${a.meaning} ${a.meaningVi}`.toLowerCase().split(/[\s,]+/).filter(Boolean)]));
21
+ // Words the script writes at run time.
22
+ const words = vi
23
+ ? { locale: 'vi-VN', loading: 'Đang tải icon…', failed: 'Không tải được icon. Tải lại trang để thử lại.', one: 'icon', many: 'icon', in: ' trong nhóm ', for: ' cho ', copyFailed: 'Không sao chép được. Tên icon là ', copied: 'Đã sao chép ', copiedLabel: 'Đã sao chép' }
24
+ : { locale: 'en-US', loading: 'Loading icons…', failed: 'The icons did not load. Reload the page to try again.', one: 'icon', many: 'icons', in: ' in ', for: ' for ', copyFailed: 'Could not copy. The name is ', copied: 'Copied ', copiedLabel: 'Copied' };
25
+ ---
26
+
27
+ <section class="ib" aria-label={L('Icon library', 'Thư viện icon')} data-words={JSON.stringify(words)} data-icon-browser data-src="/foundations/styles/icons/lucide.json" data-picks={JSON.stringify(picks)} data-meanings={JSON.stringify(meanings)} data-total={names.length}>
28
+ <div class="ib-controls">
29
+ <div class="ib-search">
30
+ <Search size={16} class="ib-search-icon" />
31
+ <input id="ib-query" type="search" placeholder={L(`Search ${total} icons`, `Tìm trong ${total} icon`)} aria-label={L(`Search ${total} icons by name or tag`, `Tìm trong ${total} icon theo tên hoặc thẻ`)} autocomplete="off" spellcheck="false" aria-controls="ib-grid" />
32
+ </div>
33
+ <label class="ib-field">
34
+ <span>{L('Category', 'Nhóm')}</span>
35
+ <span class="ib-select">
36
+ <select id="ib-category" aria-controls="ib-grid">
37
+ <option value="all">{L('All', 'Tất cả')} ({total})</option>
38
+ <option value="common" selected>{L('Common actions', 'Thao tác thường gặp')} ({picks.length})</option>
39
+ {categories.map((c, i) => <option value={String(i)}>{c.title} ({n(c.count)})</option>)}
40
+ </select>
41
+ <ChevronDown size={16} class="ib-select-icon" aria-hidden="true" />
42
+ </span>
43
+ </label>
44
+ <div class="ib-field">
45
+ <span id="ib-size-label">{L('Size', 'Cỡ')}</span>
46
+ <div class="ib-sizes" role="radiogroup" aria-labelledby="ib-size-label">
47
+ {[16, 20, 24].map((size) => (
48
+ <label class="ib-size">
49
+ <input type="radio" name="ib-size" value={size} checked={size === 24} aria-label={`${size} px`} />
50
+ <span aria-hidden="true">{size}</span>
51
+ </label>
52
+ ))}
53
+ </div>
54
+ </div>
55
+ </div>
56
+
57
+ <p class="ib-status" id="ib-status" aria-live="polite">{picks.length} {L('icons in Common actions', 'icon trong nhóm Thao tác thường gặp')}</p>
58
+ <p class="sr-only" id="ib-hint">{L('Enter copies the icon name. Arrow keys move between icons.', 'Nhấn Enter để sao chép tên icon. Dùng phím mũi tên để di chuyển giữa các icon.')}</p>
59
+ <ul class="ib-grid" id="ib-grid" aria-label="Icon" style="--ib-size: 24px"></ul>
60
+ <p class="ib-empty" hidden>{L('No icon matches. Try another word, such as “sync” or “order”, or choose All.', 'Không có icon nào khớp. Thử từ khác, như "đồng bộ" hoặc "đơn hàng", hoặc chọn Tất cả.')}</p>
61
+ <noscript><p class="ib-empty">{L('The library needs JavaScript. Browse the same icons on', 'Thư viện cần JavaScript. Xem các icon này trên')} <a href="https://lucide.dev/icons/">lucide.dev/icons</a>.</p></noscript>
62
+ <div class="ib-toast" role="status" aria-live="polite"></div>
63
+
64
+ <p class="ib-credit">
65
+ {L('Icons from', 'Icon từ')} <a href="https://lucide.dev">Lucide</a> {version}, © Lucide Contributors, <a href="https://lucide.dev/license">{L('ISC licence', 'giấy phép ISC')}</a>. {L('Categories and tags come from the Lucide project.', 'Tên nhóm và thẻ lấy từ dự án Lucide, giữ nguyên tiếng Anh.')}
66
+ </p>
67
+ </section>
68
+
69
+ <script>
70
+ type Row = { name: string; tags: string[]; cats: number[]; li: HTMLLIElement; button: HTMLButtonElement };
71
+
72
+ const root = document.querySelector<HTMLElement>('[data-icon-browser]');
73
+ if (root) init(root);
74
+
75
+ function init(root: HTMLElement) {
76
+ const query = root.querySelector<HTMLInputElement>('#ib-query')!;
77
+ const category = root.querySelector<HTMLSelectElement>('#ib-category')!;
78
+ const grid = root.querySelector<HTMLUListElement>('#ib-grid')!;
79
+ const status = root.querySelector<HTMLElement>('#ib-status')!;
80
+ const empty = root.querySelector<HTMLElement>('.ib-empty')!;
81
+ const toast = root.querySelector<HTMLElement>('.ib-toast')!;
82
+ const picks: string[] = JSON.parse(root.dataset.picks ?? '[]');
83
+ const meanings: Record<string, string[]> = JSON.parse(root.dataset.meanings ?? '{}');
84
+ const W: Record<string, string> = JSON.parse(root.dataset.words ?? '{}');
85
+ const fmt = (value: number) => value.toLocaleString(W.locale);
86
+ let rows: Row[] = [];
87
+ let shown: Row[] = [];
88
+ let active = 0;
89
+ let loading: Promise<void> | undefined;
90
+
91
+ const load = () =>
92
+ (loading ??= (async () => {
93
+ status.textContent = W.loading;
94
+ try {
95
+ const response = await fetch(root.dataset.src!);
96
+ const data = (await response.json()) as { icons: [string, string, number[], string][] };
97
+ grid.innerHTML = data.icons
98
+ .map(
99
+ ([name, , , svg]) =>
100
+ `<li><button type="button" class="ib-icon" tabindex="-1" data-name="${name}" title="${name}" aria-describedby="ib-hint">` +
101
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${svg}</svg>` +
102
+ `<span class="ib-name">${name}</span></button></li>`,
103
+ )
104
+ .join('');
105
+ const items = [...grid.children] as HTMLLIElement[];
106
+ rows = data.icons.map(([name, tags, cats], i) => ({ name, tags: tags ? tags.split(',') : [], cats, li: items[i], button: items[i].firstElementChild as HTMLButtonElement }));
107
+ // The Common actions icons also match the words of their meaning.
108
+ for (const [name, words] of Object.entries(meanings)) rows.find((row) => row.name === name)?.tags.unshift(...words);
109
+ apply(true);
110
+ } catch {
111
+ loading = undefined;
112
+ status.textContent = W.failed;
113
+ }
114
+ })());
115
+
116
+ // Every word must match the name or a tag. Name matches rank first.
117
+ const score = (row: Row, words: string[]) => {
118
+ let total = 0;
119
+ for (const word of words) {
120
+ const s = row.name === word ? 12 : row.name.startsWith(word) ? 8 : row.name.includes(word) ? 5
121
+ : row.tags.some((tag) => tag.startsWith(word)) ? 3 : row.tags.some((tag) => tag.includes(word)) ? 1 : 0;
122
+ if (!s) return 0;
123
+ total += s;
124
+ }
125
+ // Common actions come first among equal matches.
126
+ return total + (picks.includes(row.name) ? 6 : 0);
127
+ };
128
+
129
+ let statusTimer = 0;
130
+ function apply(now = false) {
131
+ if (!rows.length) return;
132
+ const words = query.value.toLowerCase().split(/\s+/).filter(Boolean);
133
+ const value = category.value;
134
+ // Common actions is the default view, so a search from it looks through every icon
135
+ // (common actions still rank first); other categories narrow the search.
136
+ const everything = value === 'all' || (value === 'common' && words.length > 0);
137
+ let list = everything ? rows
138
+ : value === 'common' ? picks.map((name) => rows.find((row) => row.name === name)!).filter(Boolean)
139
+ : rows.filter((row) => row.cats.includes(Number(value)));
140
+ if (words.length) {
141
+ list = list
142
+ .map((row) => ({ row, s: score(row, words) }))
143
+ .filter((x) => x.s > 0)
144
+ .sort((a, b) => b.s - a.s || a.row.name.localeCompare(b.row.name))
145
+ .map((x) => x.row);
146
+ }
147
+ shown = list;
148
+ grid.replaceChildren(...list.map((row) => row.li));
149
+ empty.hidden = list.length > 0;
150
+ setActive(0, false);
151
+ const label = everything ? '' : `${W.in}${category.selectedOptions[0].textContent!.replace(/\s*\(.*\)$/, '')}`;
152
+ const text = `${fmt(list.length)} ${list.length === 1 ? W.one : W.many}${label}${words.length ? `${W.for}“${query.value.trim()}”` : ''}`;
153
+ window.clearTimeout(statusTimer);
154
+ if (now) status.textContent = text;
155
+ else statusTimer = window.setTimeout(() => (status.textContent = text), 400);
156
+ }
157
+
158
+ function setActive(index: number, focus = true) {
159
+ if (!shown.length) return;
160
+ shown[active]?.button.setAttribute('tabindex', '-1');
161
+ active = Math.max(0, Math.min(index, shown.length - 1));
162
+ const button = shown[active].button;
163
+ button.setAttribute('tabindex', '0');
164
+ if (focus) button.focus();
165
+ }
166
+
167
+ // Columns in the current layout: the items on the first row.
168
+ const columns = () => {
169
+ const top = shown[0]?.li.offsetTop;
170
+ let count = 0;
171
+ while (count < shown.length && shown[count].li.offsetTop === top) count++;
172
+ return Math.max(1, count);
173
+ };
174
+
175
+ grid.addEventListener('keydown', (event) => {
176
+ const step: Record<string, number> = { ArrowRight: 1, ArrowLeft: -1, ArrowDown: columns(), ArrowUp: -columns() };
177
+ if (event.key in step) setActive(active + step[event.key]);
178
+ else if (event.key === 'Home') setActive(0);
179
+ else if (event.key === 'End') setActive(shown.length - 1);
180
+ else return;
181
+ event.preventDefault();
182
+ });
183
+
184
+ let toastTimer = 0;
185
+ const say = (text: string) => {
186
+ toast.textContent = '';
187
+ window.setTimeout(() => {
188
+ toast.textContent = text;
189
+ toast.dataset.show = '';
190
+ window.clearTimeout(toastTimer);
191
+ toastTimer = window.setTimeout(() => delete toast.dataset.show, 2400);
192
+ }, 50);
193
+ };
194
+
195
+ const fallbackCopy = (text: string) => {
196
+ const area = document.createElement('textarea');
197
+ area.value = text;
198
+ area.setAttribute('readonly', '');
199
+ area.className = 'sr-only';
200
+ document.body.append(area);
201
+ area.select();
202
+ let ok = false;
203
+ try {
204
+ ok = document.execCommand('copy');
205
+ } catch {}
206
+ area.remove();
207
+ return ok;
208
+ };
209
+
210
+ grid.addEventListener('click', async (event) => {
211
+ const button = (event.target as HTMLElement).closest<HTMLButtonElement>('.ib-icon');
212
+ if (!button) return;
213
+ const name = button.dataset.name!;
214
+ const index = shown.findIndex((row) => row.button === button);
215
+ if (index >= 0) setActive(index, false);
216
+ let ok = true;
217
+ try {
218
+ await navigator.clipboard.writeText(name);
219
+ } catch {
220
+ ok = fallbackCopy(name);
221
+ }
222
+ if (!ok) return say(`${W.copyFailed}${name}`);
223
+ say(`${W.copied}“${name}”`);
224
+ const label = button.querySelector('.ib-name')!;
225
+ button.dataset.copied = '';
226
+ label.textContent = W.copiedLabel;
227
+ window.setTimeout(() => {
228
+ delete button.dataset.copied;
229
+ label.textContent = name;
230
+ }, 1600);
231
+ });
232
+
233
+ query.addEventListener('input', () => {
234
+ void load();
235
+ apply();
236
+ });
237
+ query.addEventListener('focus', () => void load());
238
+ category.addEventListener('change', () => {
239
+ void load();
240
+ apply(true);
241
+ });
242
+ root.querySelectorAll<HTMLInputElement>('input[name="ib-size"]').forEach((radio) =>
243
+ radio.addEventListener('change', () => grid.style.setProperty('--ib-size', `${radio.value}px`)),
244
+ );
245
+
246
+ // Load the icons when the library is close to the screen.
247
+ if ('IntersectionObserver' in window) {
248
+ const observer = new IntersectionObserver((entries) => {
249
+ if (entries.some((entry) => entry.isIntersecting)) {
250
+ observer.disconnect();
251
+ void load();
252
+ }
253
+ }, { rootMargin: '400px' });
254
+ observer.observe(root);
255
+ } else void load();
256
+ }
257
+ </script>
@@ -0,0 +1,185 @@
1
+ ---
2
+ // Illustrations for the Icons pages. Icons are Lucide 1.62.0, read from lucide-static at build
3
+ // time, so the pictures show the exact shapes products get. Colours are role tokens.
4
+ import '../styles/icons.css';
5
+ import { lucide } from '../lib/lucide-data';
6
+ import { iconActions, iconSizes } from '../lib/icons';
7
+ import { langOf } from '../lib/i18n';
8
+
9
+ interface Props {
10
+ kind: 'sizes' | 'align' | 'spacing' | 'actions' | 'dodont' | 'target';
11
+ /** For kind="dodont": which pair to show. */
12
+ item?: 'label' | 'meaning' | 'selected' | 'size' | 'stroke' | 'align' | 'colour' | 'target' | 'tooltip' | 'decorative';
13
+ }
14
+ const { kind, item } = Astro.props;
15
+ const vi = langOf(Astro.url.pathname) === 'vi';
16
+ const L = (en: string, viText: string) => (vi ? viText : en);
17
+
18
+ const { icons } = lucide();
19
+ const byName = new Map(icons.map((i) => [i.name, i.svg]));
20
+ /** Inline SVG of a Lucide icon, hidden from assistive technology (the text says what it is). */
21
+ const icon = (name: string, size = 16, opts: { stroke?: number; cls?: string; fill?: boolean } = {}) => {
22
+ const inner = byName.get(name);
23
+ if (!inner) throw new Error(`IconDemo: no Lucide icon "${name}"`);
24
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="${opts.fill ? 'currentColor' : 'none'}" stroke="currentColor" stroke-width="${opts.stroke ?? 2}" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-${name}${opts.cls ? ` ${opts.cls}` : ''}" aria-hidden="true">${inner}</svg>`;
25
+ };
26
+
27
+ const pairs: Record<string, { do: string; dont: string }> = vi ? {
28
+ label: { do: 'Icon có nhãn. Nhãn nói điều gì sẽ xảy ra.', dont: 'Icon đứng riêng cho một thao tác người dùng có thể không biết.' },
29
+ meaning: { do: 'Một nghĩa một icon, ở mọi nơi: refresh-cw cho mọi thao tác đồng bộ.', dont: 'Ba icon khác nhau cho cùng một thao tác trên một màn hình.' },
30
+ selected: { do: 'Đã chọn: nền "đã chọn" và thanh chỉ báo. Icon vẫn là dạng nét.', dont: 'Icon tô đặc là dấu hiệu duy nhất của mục đã chọn.' },
31
+ size: { do: 'Icon 16 px cạnh chữ 14 px.', dont: 'Icon 24 px cạnh chữ 14 px, hoặc cỡ lưng chừng như 18 px.' },
32
+ stroke: { do: 'Nét gốc của Lucide, co giãn theo icon (ở đây cỡ 24 px).', dont: 'Đổi độ dày nét, hoặc trộn nhiều độ dày.' },
33
+ align: { do: 'Icon căn giữa dòng chữ.', dont: 'Icon đặt trên đường chân chữ: bị lệch lên trên.' },
34
+ colour: { do: 'Icon cùng màu chữ, hoặc icon trạng thái đi kèm trạng thái viết bằng chữ.', dont: 'Tô màu để trang trí, hoặc icon có màu dưới 3:1.' },
35
+ target: { do: 'Nút 32 × 32 bao quanh icon 16 px.', dont: 'Icon 16 px là thứ duy nhất để bấm.' },
36
+ tooltip: { do: 'Nút chỉ có icon, có tên và tooltip cùng nội dung.', dont: 'Nút chỉ có icon, không tên và không tooltip.' },
37
+ decorative: { do: 'Icon trang trí bị ẩn; nhãn chỉ được đọc một lần.', dont: 'Icon cũng bị đọc: "refresh-cw, Đồng bộ ngay".' },
38
+ } : {
39
+ label: { do: 'Icon with a label. The label says what happens.', dont: 'An icon on its own for an action people may not know.' },
40
+ meaning: { do: 'One icon for one meaning, everywhere: refresh-cw for every sync.', dont: 'Three icons for the same action on one screen.' },
41
+ selected: { do: 'Selected: the selected background and an indicator bar. The icon stays outline.', dont: 'A filled icon as the only sign of the selected item.' },
42
+ size: { do: '16 px icon next to 14 px text.', dont: 'A 24 px icon next to 14 px text, or an in-between size such as 18 px.' },
43
+ stroke: { do: 'Lucide’s own stroke, scaled with the icon (here at 24 px).', dont: 'Changing the stroke width, or mixing widths.' },
44
+ align: { do: 'Icon centred on the line of text.', dont: 'Icon sitting on the text baseline: it rides high.' },
45
+ colour: { do: 'Icon in the text colour, or a status icon next to the status in words.', dont: 'Colour to decorate, or an icon in a colour below 3:1.' },
46
+ target: { do: 'A 32 × 32 button around a 16 px icon.', dont: 'The 16 px icon is the only thing to click.' },
47
+ tooltip: { do: 'Icon-only button with a name and a matching tooltip.', dont: 'Icon-only button with no name and no tooltip.' },
48
+ decorative: { do: 'Decorative icon hidden; the label is read once.', dont: 'The icon is read too: “refresh-cw, Sync now”.' },
49
+ };
50
+ const syncNow = L('Sync now', 'Đồng bộ ngay');
51
+ ---
52
+
53
+ {kind === 'sizes' && (
54
+ <div class="ic-sizes">
55
+ {iconSizes.map((s) => (
56
+ <figure class="ic-size">
57
+ <div class="ic-size-stage">
58
+ <Fragment set:html={icon('refresh-cw', s.size)} />
59
+ </div>
60
+ <figcaption>
61
+ <b>{s.size} px</b>
62
+ <span class="ic-meta">{L('Stroke', 'Nét')} {vi ? s.stroke.replace('.', ',') : s.stroke} px</span>
63
+ <span>{vi ? s.useVi : s.use}</span>
64
+ </figcaption>
65
+ </figure>
66
+ ))}
67
+ </div>
68
+ )}
69
+
70
+ {kind === 'align' && (
71
+ <div class="ic-panels">
72
+ <figure class="ic-panel">
73
+ <div class="ic-panel-stage is-zoom">
74
+ <span class="ic-line is-guided">
75
+ <Fragment set:html={icon('refresh-cw', 16)} /><span>{syncNow}</span>
76
+ </span>
77
+ </div>
78
+ <figcaption>{vi ? <Fragment><b>Một dòng.</b> Icon căn giữa line height của chữ (<code>align-items: center</code>).</Fragment> : <Fragment><b>One line.</b> The icon is centred on the line height of the text (<code>align-items: center</code>).</Fragment>}</figcaption>
79
+ </figure>
80
+ <figure class="ic-panel">
81
+ <div class="ic-panel-stage">
82
+ <span class="ic-multi">
83
+ <span class="ic-multi-icon"><Fragment set:html={icon('triangle-alert', 16, { cls: 'is-warning' })} /></span>
84
+ <span>{L('3 products were skipped because their SKU is missing in the ERP. Add the SKU, then sync again.', '3 sản phẩm bị bỏ qua vì thiếu SKU trong ERP. Thêm SKU rồi đồng bộ lại.')}</span>
85
+ </span>
86
+ </div>
87
+ <figcaption>{vi ? <Fragment><b>Nhiều dòng.</b> Icon căn giữa dòng đầu tiên, không căn theo cả khối.</Fragment> : <Fragment><b>Several lines.</b> The icon is centred on the first line, not on the whole block.</Fragment>}</figcaption>
88
+ </figure>
89
+ </div>
90
+ )}
91
+
92
+ {kind === 'spacing' && (
93
+ <div class="ic-panels">
94
+ <figure class="ic-panel">
95
+ <div class="ic-panel-stage is-zoom">
96
+ <span class="ic-spaced is-16">
97
+ <Fragment set:html={icon('plus', 16)} /><i class="ic-gap" aria-hidden="true"><small>4</small></i><span>{L('Add store', 'Thêm cửa hàng')}</span>
98
+ </span>
99
+ </div>
100
+ <figcaption>{vi ? <Fragment><b>4 px</b> với icon 16 px, trong control cỡ nhỏ và mặc định (<code>space.050</code>).</Fragment> : <Fragment><b>4 px</b> with a 16 px icon, in small and default controls (<code>space.050</code>).</Fragment>}</figcaption>
101
+ </figure>
102
+ <figure class="ic-panel">
103
+ <div class="ic-panel-stage is-zoom">
104
+ <span class="ic-spaced is-20">
105
+ <Fragment set:html={icon('plus', 20)} /><i class="ic-gap" aria-hidden="true"><small>8</small></i><span>{L('Add store', 'Thêm cửa hàng')}</span>
106
+ </span>
107
+ </div>
108
+ <figcaption>{vi ? <Fragment><b>8 px</b> với icon 20 hoặc 24 px, trong control cỡ lớn và bên ngoài control (<code>space.100</code>).</Fragment> : <Fragment><b>8 px</b> with a 20 or 24 px icon, in large controls and outside controls (<code>space.100</code>).</Fragment>}</figcaption>
109
+ </figure>
110
+ </div>
111
+ )}
112
+
113
+ {kind === 'target' && (
114
+ <div class="ic-panels">
115
+ <figure class="ic-panel">
116
+ <div class="ic-panel-stage is-zoom">
117
+ <span class="ic-target is-24"><Fragment set:html={icon('x', 16)} /><small>24</small></span>
118
+ </div>
119
+ <figcaption>{vi ? <Fragment><b>Tối thiểu 24 × 24</b> cho mọi thứ bấm được (WCAG 2.5.8). Chỉ dùng ở chỗ hẹp, như nút gỡ của một tag.</Fragment> : <Fragment><b>24 × 24 minimum</b> for anything you can click (WCAG 2.5.8). Use it only where space is tight, such as a tag’s remove button.</Fragment>}</figcaption>
120
+ </figure>
121
+ <figure class="ic-panel">
122
+ <div class="ic-panel-stage is-zoom">
123
+ <span class="ic-target is-32"><Fragment set:html={icon('settings', 16)} /><small>32</small></span>
124
+ </div>
125
+ <figcaption>{vi ? <Fragment><b>32 × 32 trong sản phẩm.</b> Mọi nút icon đều 32 × 32 với icon 16 px, hoặc 20 px trong thanh công cụ.</Fragment> : <Fragment><b>32 × 32 in products.</b> Every icon button is 32 × 32 with a 16 px icon, or 20 px in a toolbar.</Fragment>}</figcaption>
126
+ </figure>
127
+ </div>
128
+ )}
129
+
130
+ {kind === 'actions' && (
131
+ <table class="ic-actions">
132
+ <thead><tr><th scope="col">{L('Meaning', 'Nghĩa')}</th><th scope="col">Icon</th><th scope="col">{L('Name', 'Tên')}</th><th scope="col">{L('Note', 'Ghi chú')}</th></tr></thead>
133
+ <tbody>
134
+ {iconActions.map((a) => (
135
+ <tr>
136
+ <td>{vi ? a.meaningVi : a.meaning}</td>
137
+ <td><span class="ic-action-icon"><Fragment set:html={icon(a.icon, 16)} /><Fragment set:html={icon(a.icon, 24)} /></span></td>
138
+ <td><code>{a.icon}</code></td>
139
+ <td>{'note' in a ? (vi && 'noteVi' in a ? a.noteVi : a.note) : ''}</td>
140
+ </tr>
141
+ ))}
142
+ </tbody>
143
+ </table>
144
+ )}
145
+
146
+ {kind === 'dodont' && item && (
147
+ <div class="ic-dd">
148
+ <figure class="ic-dd-panel is-do">
149
+ <figcaption><span aria-hidden="true">✓</span> {L('Do', 'Nên')}</figcaption>
150
+ <div class:list={['ic-dd-stage', { 'is-zoom': item === 'align' || item === 'target' }]}>
151
+ {item === 'label' && (<span class="bh-button"><Fragment set:html={icon('refresh-cw', 16)} />{syncNow}</span>)}
152
+ {item === 'meaning' && (<span class="ic-row"><span class="bh-button"><Fragment set:html={icon('refresh-cw', 16)} />{syncNow}</span><span class="ic-ibtn" aria-hidden="true"><Fragment set:html={icon('refresh-cw', 16)} /></span></span>)}
153
+ {item === 'selected' && (
154
+ <span class="ic-nav"><span class="ic-nav-item is-selected"><Fragment set:html={icon('store', 16)} />{L('Stores', 'Cửa hàng')}</span><span class="ic-nav-item"><Fragment set:html={icon('receipt', 16)} />{L('Orders', 'Đơn hàng')}</span></span>
155
+ )}
156
+ {item === 'size' && (<span class="ic-text14"><Fragment set:html={icon('clock', 16)} />{L('Last sync 5 minutes ago', 'Đồng bộ lần cuối 5 phút trước')}</span>)}
157
+ {item === 'stroke' && (<span class="ic-row is-icons"><Fragment set:html={icon('store', 24)} /><Fragment set:html={icon('receipt', 24)} /><Fragment set:html={icon('package', 24)} /></span>)}
158
+ {item === 'align' && (<span class="ic-text14"><Fragment set:html={icon('calendar', 16)} />{L('Runs every day', 'Chạy hằng ngày')}</span>)}
159
+ {item === 'colour' && (<span class="ic-text14 is-danger"><Fragment set:html={icon('circle-x', 16)} />{L('Sync failed', 'Đồng bộ thất bại')}</span>)}
160
+ {item === 'target' && (<span class="ic-ibtn is-outlined" aria-hidden="true"><Fragment set:html={icon('pencil', 16)} /></span>)}
161
+ {item === 'tooltip' && (<span class="ic-tip-wrap"><span class="ic-tip">{L('Settings', 'Cài đặt')}</span><span class="ic-ibtn is-outlined" aria-hidden="true"><Fragment set:html={icon('settings', 16)} /></span></span>)}
162
+ {item === 'decorative' && (<code class="ic-dd-code">&lt;button&gt;&lt;RefreshCw /&gt; {syncNow}&lt;/button&gt;<br />{vi ? `Được đọc là: "${syncNow}, nút"` : `Read as: “${syncNow}, button”`}</code>)}
163
+ </div>
164
+ <p>{pairs[item].do}</p>
165
+ </figure>
166
+ <figure class="ic-dd-panel is-dont">
167
+ <figcaption><span aria-hidden="true">✕</span> {L('Don’t', 'Không nên')}</figcaption>
168
+ <div class:list={['ic-dd-stage', { 'is-zoom': item === 'align' || item === 'target' }]}>
169
+ {item === 'label' && (<span class="ic-ibtn is-outlined" aria-hidden="true"><Fragment set:html={icon('webhook', 16)} /></span>)}
170
+ {item === 'meaning' && (<span class="ic-row"><span class="bh-button"><Fragment set:html={icon('rotate-cw', 16)} />{syncNow}</span><span class="ic-ibtn" aria-hidden="true"><Fragment set:html={icon('repeat', 16)} /></span><span class="ic-ibtn" aria-hidden="true"><Fragment set:html={icon('refresh-ccw', 16)} /></span></span>)}
171
+ {item === 'selected' && (
172
+ <span class="ic-nav"><span class="ic-nav-item"><Fragment set:html={icon('store', 16, { fill: true, cls: 'is-filled' })} />{L('Stores', 'Cửa hàng')}</span><span class="ic-nav-item"><Fragment set:html={icon('receipt', 16)} />{L('Orders', 'Đơn hàng')}</span></span>
173
+ )}
174
+ {item === 'size' && (<span class="ic-text14 is-big"><Fragment set:html={icon('clock', 24)} />{L('Last sync 5 minutes ago', 'Đồng bộ lần cuối 5 phút trước')}</span>)}
175
+ {item === 'stroke' && (<span class="ic-row is-icons"><Fragment set:html={icon('store', 24, { stroke: 1 })} /><Fragment set:html={icon('receipt', 24, { stroke: 3 })} /><Fragment set:html={icon('package', 24, { stroke: 1.5 })} /></span>)}
176
+ {item === 'align' && (<span class="ic-text14 is-baseline"><Fragment set:html={icon('calendar', 16)} />{L('Runs every day', 'Chạy hằng ngày')}</span>)}
177
+ {item === 'colour' && (<span class="ic-text14"><Fragment set:html={icon('store', 16, { cls: 'is-decor' })} />{L('Online store', 'Cửa hàng online')}</span>)}
178
+ {item === 'target' && (<span class="ic-bare" aria-hidden="true"><Fragment set:html={icon('pencil', 16)} /></span>)}
179
+ {item === 'tooltip' && (<span class="ic-ibtn is-outlined" aria-hidden="true"><Fragment set:html={icon('settings', 16)} /></span>)}
180
+ {item === 'decorative' && (<code class="ic-dd-code">&lt;button&gt;&lt;svg role="img" aria-label="refresh-cw"&gt; {syncNow}&lt;/button&gt;<br />{vi ? `Được đọc là: "refresh-cw, ${syncNow}, nút"` : `Read as: “refresh-cw, ${syncNow}, button”`}</code>)}
181
+ </div>
182
+ <p>{pairs[item].dont}</p>
183
+ </figure>
184
+ </div>
185
+ )}
@@ -0,0 +1,29 @@
1
+ ---
2
+ import { feedbackHref } from '../lib/brand';
3
+ import { langOf, t } from '../lib/i18n';
4
+
5
+ // Shown in place of content on pages that are listed but not written yet.
6
+ interface Props {
7
+ planned?: string[];
8
+ }
9
+
10
+ const { planned = [] } = Astro.props;
11
+ const T = t(langOf(Astro.url.pathname));
12
+ ---
13
+
14
+ <section class="in-development" aria-labelledby="in-development-title">
15
+ <div class="in-development-icon" aria-hidden="true">
16
+ <svg viewBox="0 0 24 24" width="24" height="24"><path d="M12 2.5l8.2 4.75v9.5L12 21.5l-8.2-4.75v-9.5z" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round" /><path d="M12 8v4.5l2.5 1.5" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" /></svg>
17
+ </div>
18
+ <div>
19
+ <p class="in-development-title" id="in-development-title">{T.inDevTitle}</p>
20
+ <p>{T.inDevText}</p>
21
+ {planned.length > 0 && (
22
+ <>
23
+ <p class="in-development-label">{T.inDevPlanned}</p>
24
+ <ul>{planned.map((item) => <li>{item}</li>)}</ul>
25
+ </>
26
+ )}
27
+ <p>{T.inDevSooner} <a href={feedbackHref(Astro.url.pathname)}>{T.inDevAsk}</a>.</p>
28
+ </div>
29
+ </section>