@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.
- package/README.md +56 -2
- package/dist/AGENTS.md +24 -0
- package/dist/catalog.json +1814 -0
- package/dist/docs/public/_redirects +7 -0
- package/dist/docs/public/apple-touch-icon.png +0 -0
- package/dist/docs/public/brand/logos/app/apple-touch-icon.png +0 -0
- package/dist/docs/public/brand/logos/app/avatar-800.png +0 -0
- package/dist/docs/public/brand/logos/app/icon-192.png +0 -0
- package/dist/docs/public/brand/logos/app/icon-512.png +0 -0
- package/dist/docs/public/brand/logos/app/icon-maskable-512.png +0 -0
- package/dist/docs/public/brand/logos/archive/d-550/beehexa-horizontal-1-light.svg +1 -0
- package/dist/docs/public/brand/logos/archive/d-550/beehexa-horizontal-2-dark.svg +1 -0
- package/dist/docs/public/brand/logos/archive/d-550/beehexa-horizontal-3-white.svg +1 -0
- package/dist/docs/public/brand/logos/archive/d-550/beehexa-horizontal-4-graphite.svg +1 -0
- package/dist/docs/public/brand/logos/archive/d-550/hexasync-horizontal-1-light.svg +1 -0
- package/dist/docs/public/brand/logos/archive/d-550/hexasync-horizontal-2-dark.svg +1 -0
- package/dist/docs/public/brand/logos/archive/d-550/hexasync-horizontal-3-white.svg +1 -0
- package/dist/docs/public/brand/logos/archive/d-550/hexasync-horizontal-4-graphite.svg +1 -0
- package/dist/docs/public/brand/logos/archive/d-550/ui/beehexa-d-550-dark.svg +1 -0
- package/dist/docs/public/brand/logos/archive/d-550/ui/beehexa-d-550-light.svg +1 -0
- package/dist/docs/public/brand/logos/archive/d-550/ui/beehexa-design-d-550-dark.svg +1 -0
- package/dist/docs/public/brand/logos/archive/d-550/ui/beehexa-design-d-550-light.svg +1 -0
- package/dist/docs/public/brand/logos/archive/d-550/ui/hexasync-d-550-dark.svg +1 -0
- package/dist/docs/public/brand/logos/archive/d-550/ui/hexasync-d-550-light.svg +1 -0
- package/dist/docs/public/brand/logos/archive/d-550/ui/png/beehexa-design-d-550-dark@3x.png +0 -0
- package/dist/docs/public/brand/logos/archive/d-550/ui/png/beehexa-design-d-550-light@3x.png +0 -0
- package/dist/docs/public/brand/logos/beehexa-logo.zip +0 -0
- package/dist/docs/public/brand/logos/fig/anatomy-en.png +0 -0
- package/dist/docs/public/brand/logos/fig/anatomy-vi.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-dark-12.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-dark-14.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-dark-28.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-dark-32.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-dark-56.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-graphite-28.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-graphite-32.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-light-14.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-light-21.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-light-22.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-light-24.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-light-28.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-light-32.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-light-36-7c3aed7c3aed.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-light-36-swap.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-light-36.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-light-56.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-white-20.png +0 -0
- package/dist/docs/public/brand/logos/fig/beehexa-white-32.png +0 -0
- package/dist/docs/public/brand/logos/fig/clearspace.png +0 -0
- package/dist/docs/public/brand/logos/fig/construction-en.png +0 -0
- package/dist/docs/public/brand/logos/fig/construction-vi.png +0 -0
- package/dist/docs/public/brand/logos/fig/container-en.png +0 -0
- package/dist/docs/public/brand/logos/fig/container-vi.png +0 -0
- package/dist/docs/public/brand/logos/fig/hexasync-dark-20.png +0 -0
- package/dist/docs/public/brand/logos/fig/hexasync-dark-28.png +0 -0
- package/dist/docs/public/brand/logos/fig/hexasync-dark-48.png +0 -0
- package/dist/docs/public/brand/logos/fig/hexasync-light-19.png +0 -0
- package/dist/docs/public/brand/logos/fig/hexasync-light-28.png +0 -0
- package/dist/docs/public/brand/logos/fig/hexasync-light-48.png +0 -0
- package/dist/docs/public/brand/logos/fig/icon-graphite-14.png +0 -0
- package/dist/docs/public/brand/logos/fig/icon-graphite-16.png +0 -0
- package/dist/docs/public/brand/logos/fig/icon-light-16.png +0 -0
- package/dist/docs/public/brand/logos/fig/icon-light-24.png +0 -0
- package/dist/docs/public/brand/logos/fig/icon-light-48.png +0 -0
- package/dist/docs/public/brand/logos/fig/icon-light-92.png +0 -0
- package/dist/docs/public/brand/logos/fig/meaning-bee-en.png +0 -0
- package/dist/docs/public/brand/logos/fig/meaning-bee-vi.png +0 -0
- package/dist/docs/public/brand/logos/fig/meaning-outline-en.png +0 -0
- package/dist/docs/public/brand/logos/fig/meaning-outline-vi.png +0 -0
- package/dist/docs/public/brand/logos/fig/meaning-stack-en.png +0 -0
- package/dist/docs/public/brand/logos/fig/meaning-stack-vi.png +0 -0
- package/dist/docs/public/brand/logos/fig/misuse-serif.png +0 -0
- package/dist/docs/public/brand/logos/icon/beehexa-icon-graphite.svg +1 -0
- package/dist/docs/public/brand/logos/icon/beehexa-icon-white.svg +1 -0
- package/dist/docs/public/brand/logos/icon/beehexa-icon-yellow.svg +1 -0
- package/dist/docs/public/brand/logos/lockups/beehexa-horizontal-1-light.svg +1 -0
- package/dist/docs/public/brand/logos/lockups/beehexa-horizontal-2-dark.svg +1 -0
- package/dist/docs/public/brand/logos/lockups/beehexa-horizontal-3-white.svg +1 -0
- package/dist/docs/public/brand/logos/lockups/beehexa-horizontal-4-graphite.svg +1 -0
- package/dist/docs/public/brand/logos/lockups/hexasync-horizontal-1-light.svg +1 -0
- package/dist/docs/public/brand/logos/lockups/hexasync-horizontal-2-dark.svg +1 -0
- package/dist/docs/public/brand/logos/lockups/hexasync-horizontal-3-white.svg +1 -0
- package/dist/docs/public/brand/logos/lockups/hexasync-horizontal-4-graphite.svg +1 -0
- package/dist/docs/public/brand/logos/ui/beehexa-dark.svg +1 -0
- package/dist/docs/public/brand/logos/ui/beehexa-design-dark.svg +1 -0
- package/dist/docs/public/brand/logos/ui/beehexa-design-light.svg +1 -0
- package/dist/docs/public/brand/logos/ui/beehexa-icon-yellow.svg +1 -0
- package/dist/docs/public/brand/logos/ui/beehexa-light.svg +1 -0
- package/dist/docs/public/brand/logos/ui/hexasync-dark.svg +1 -0
- package/dist/docs/public/brand/logos/ui/hexasync-light.svg +1 -0
- package/dist/docs/public/brand/logos/ui/png/beehexa-design-dark@3x.png +0 -0
- package/dist/docs/public/brand/logos/ui/png/beehexa-design-light@3x.png +0 -0
- package/dist/docs/public/favicon.ico +0 -0
- package/dist/docs/public/favicon.svg +1 -0
- package/dist/docs/public/fonts/LICENSE-Inter.txt +93 -0
- package/dist/docs/public/fonts/LICENSE-JetBrainsMono.txt +93 -0
- package/dist/docs/public/fonts/inter-latin-ext-opsz-wght-italic.woff2 +0 -0
- package/dist/docs/public/fonts/inter-latin-ext-opsz-wght-normal.woff2 +0 -0
- package/dist/docs/public/fonts/inter-latin-opsz-wght-italic.woff2 +0 -0
- package/dist/docs/public/fonts/inter-latin-opsz-wght-normal.woff2 +0 -0
- package/dist/docs/public/fonts/inter-vietnamese-opsz-wght-italic.woff2 +0 -0
- package/dist/docs/public/fonts/inter-vietnamese-opsz-wght-normal.woff2 +0 -0
- package/dist/docs/public/fonts/jetbrains-mono-latin-ext-wght-normal.woff2 +0 -0
- package/dist/docs/public/fonts/jetbrains-mono-latin-wght-normal.woff2 +0 -0
- package/dist/docs/public/fonts/jetbrains-mono-vietnamese-wght-normal.woff2 +0 -0
- package/dist/docs/public/icon-192.png +0 -0
- package/dist/docs/public/icon-512.png +0 -0
- package/dist/docs/public/icon-maskable-512.png +0 -0
- package/dist/docs/public/robots.txt +4 -0
- package/dist/docs/public/site.webmanifest +21 -0
- package/dist/docs/scripts/color-pairs.mjs +46 -0
- package/dist/docs/scripts/contrast-exceptions.mjs +6 -0
- package/dist/docs/scripts/token-utils.d.mts +26 -0
- package/dist/docs/scripts/token-utils.mjs +114 -0
- package/dist/docs/src/brand/Beehexa-brandguide-full-mar12.pdf +0 -0
- package/dist/docs/src/brand/color+typoDirections.pdf +0 -0
- package/dist/docs/src/brand/structure-proposal.md +227 -0
- package/dist/docs/src/components/ChildPages.astro +25 -0
- package/dist/docs/src/components/CodeExample.astro +18 -0
- package/dist/docs/src/components/ColorDemo.astro +794 -0
- package/dist/docs/src/components/ColorExample.astro +32 -0
- package/dist/docs/src/components/ColorSwatch.astro +20 -0
- package/dist/docs/src/components/ComponentTabs.astro +21 -0
- package/dist/docs/src/components/DocsFooter.astro +25 -0
- package/dist/docs/src/components/HelpOptions.astro +20 -0
- package/dist/docs/src/components/IconBrowser.astro +257 -0
- package/dist/docs/src/components/IconDemo.astro +185 -0
- package/dist/docs/src/components/InDevelopment.astro +29 -0
- package/dist/docs/src/components/LayoutDemo.astro +384 -0
- package/dist/docs/src/components/Logo.astro +25 -0
- package/dist/docs/src/components/PhaseTable.astro +22 -0
- package/dist/docs/src/components/RoleGuides.astro +48 -0
- package/dist/docs/src/components/ShapeDemo.astro +239 -0
- package/dist/docs/src/components/ShapePin.astro +22 -0
- package/dist/docs/src/components/Sidebar.astro +206 -0
- package/dist/docs/src/components/SiteHeader.astro +211 -0
- package/dist/docs/src/components/SpacingDemo.astro +172 -0
- package/dist/docs/src/components/StatusBadge.astro +22 -0
- package/dist/docs/src/components/SystemAreas.astro +29 -0
- package/dist/docs/src/components/TableOfContents.astro +47 -0
- package/dist/docs/src/components/TokenTable.astro +40 -0
- package/dist/docs/src/components/TypeDemo.astro +249 -0
- package/dist/docs/src/components/logos/Fig.astro +28 -0
- package/dist/docs/src/components/logos/LogoAnatomy.astro +101 -0
- package/dist/docs/src/components/logos/LogoClearSpace.astro +81 -0
- package/dist/docs/src/components/logos/LogoColourVersions.astro +45 -0
- package/dist/docs/src/components/logos/LogoConstruction.astro +84 -0
- package/dist/docs/src/components/logos/LogoContainer.astro +84 -0
- package/dist/docs/src/components/logos/LogoHero.astro +42 -0
- package/dist/docs/src/components/logos/LogoInUse.astro +197 -0
- package/dist/docs/src/components/logos/LogoMeanings.astro +75 -0
- package/dist/docs/src/components/logos/LogoMinSize.astro +55 -0
- package/dist/docs/src/components/logos/LogoMistakes.astro +109 -0
- package/dist/docs/src/components/logos/LogoMisuse.astro +96 -0
- package/dist/docs/src/components/logos/LogoPair.astro +26 -0
- package/dist/docs/src/components/logos/LogoSvg.astro +68 -0
- package/dist/docs/src/content/components/button/accessibility.mdx +18 -0
- package/dist/docs/src/content/components/button/changelog.mdx +6 -0
- package/dist/docs/src/content/components/button/code.mdx +27 -0
- package/dist/docs/src/content/components/button/examples.mdx +21 -0
- package/dist/docs/src/content/components/button/meta.json +5 -0
- package/dist/docs/src/content/components/button/usage.mdx +18 -0
- package/dist/docs/src/content/foundations/guidelines/accessibility.mdx +19 -0
- package/dist/docs/src/content/foundations/guidelines/content.mdx +25 -0
- package/dist/docs/src/content/foundations/index.mdx +9 -0
- package/dist/docs/src/content/foundations/styles/border.mdx +89 -0
- package/dist/docs/src/content/foundations/styles/color/accessibility.mdx +32 -0
- package/dist/docs/src/content/foundations/styles/color/dark-mode.mdx +22 -0
- package/dist/docs/src/content/foundations/styles/color/index.mdx +65 -0
- package/dist/docs/src/content/foundations/styles/color/palette.mdx +57 -0
- package/dist/docs/src/content/foundations/styles/color/tokens.mdx +55 -0
- package/dist/docs/src/content/foundations/styles/color/usage.mdx +111 -0
- package/dist/docs/src/content/foundations/styles/data-visualization.mdx +11 -0
- package/dist/docs/src/content/foundations/styles/elevation.mdx +12 -0
- package/dist/docs/src/content/foundations/styles/icons/accessibility.mdx +72 -0
- package/dist/docs/src/content/foundations/styles/icons/code.mdx +171 -0
- package/dist/docs/src/content/foundations/styles/icons/index.mdx +62 -0
- package/dist/docs/src/content/foundations/styles/icons/library.mdx +19 -0
- package/dist/docs/src/content/foundations/styles/icons/usage.mdx +100 -0
- package/dist/docs/src/content/foundations/styles/illustration.mdx +9 -0
- package/dist/docs/src/content/foundations/styles/interaction-states.mdx +49 -0
- package/dist/docs/src/content/foundations/styles/layout/app-frame.mdx +81 -0
- package/dist/docs/src/content/foundations/styles/layout/breakpoints.mdx +56 -0
- package/dist/docs/src/content/foundations/styles/layout/grid.mdx +87 -0
- package/dist/docs/src/content/foundations/styles/layout/index.mdx +24 -0
- package/dist/docs/src/content/foundations/styles/layout/overlays.mdx +45 -0
- package/dist/docs/src/content/foundations/styles/layout/page-widths.mdx +38 -0
- package/dist/docs/src/content/foundations/styles/logos/downloads-and-terms.mdx +65 -0
- package/dist/docs/src/content/foundations/styles/logos/index.mdx +64 -0
- package/dist/docs/src/content/foundations/styles/logos/placement-and-containers.mdx +96 -0
- package/dist/docs/src/content/foundations/styles/logos/product-logos.mdx +56 -0
- package/dist/docs/src/content/foundations/styles/logos/using-the-logo.mdx +70 -0
- package/dist/docs/src/content/foundations/styles/motion.mdx +11 -0
- package/dist/docs/src/content/foundations/styles/opacity.mdx +9 -0
- package/dist/docs/src/content/foundations/styles/radius.mdx +98 -0
- package/dist/docs/src/content/foundations/styles/size-and-density.mdx +41 -0
- package/dist/docs/src/content/foundations/styles/spacing.mdx +70 -0
- package/dist/docs/src/content/foundations/styles/typography/code.mdx +53 -0
- package/dist/docs/src/content/foundations/styles/typography/index.mdx +58 -0
- package/dist/docs/src/content/foundations/styles/typography/usage.mdx +144 -0
- package/dist/docs/src/content/foundations/styles/typography/vietnamese.mdx +46 -0
- package/dist/docs/src/content/foundations/styles/z-index.mdx +9 -0
- package/dist/docs/src/content/foundations/tokens/all-tokens.mdx +36 -0
- package/dist/docs/src/content/foundations/tokens/index.mdx +27 -0
- package/dist/docs/src/content/get-started/content.mdx +14 -0
- package/dist/docs/src/content/get-started/design.mdx +30 -0
- package/dist/docs/src/content/get-started/develop.mdx +59 -0
- package/dist/docs/src/content/get-started/get-help.mdx +19 -0
- package/dist/docs/src/content/get-started/index.mdx +50 -0
- package/dist/docs/src/content/news/2026-10-16-site-structure.md +15 -0
- package/dist/docs/src/content/news/2026-10-20-docs-site.md +13 -0
- package/dist/docs/src/content/release-phases/index.mdx +12 -0
- package/dist/docs/src/content/tools/ai-agents.mdx +49 -0
- package/dist/docs/src/content/tools/ant-design-vue-theme.mdx +138 -0
- package/dist/docs/src/content/tools/eslint.mdx +15 -0
- package/dist/docs/src/content/tools/index.mdx +7 -0
- package/dist/docs/src/content/tools/stylelint.mdx +19 -0
- package/dist/docs/src/content-vi/foundations/styles/border.mdx +89 -0
- package/dist/docs/src/content-vi/foundations/styles/color/accessibility.mdx +32 -0
- package/dist/docs/src/content-vi/foundations/styles/color/dark-mode.mdx +22 -0
- package/dist/docs/src/content-vi/foundations/styles/color/index.mdx +65 -0
- package/dist/docs/src/content-vi/foundations/styles/color/palette.mdx +57 -0
- package/dist/docs/src/content-vi/foundations/styles/color/tokens.mdx +55 -0
- package/dist/docs/src/content-vi/foundations/styles/color/usage.mdx +111 -0
- package/dist/docs/src/content-vi/foundations/styles/data-visualization.mdx +11 -0
- package/dist/docs/src/content-vi/foundations/styles/icons/accessibility.mdx +72 -0
- package/dist/docs/src/content-vi/foundations/styles/icons/code.mdx +170 -0
- package/dist/docs/src/content-vi/foundations/styles/icons/index.mdx +62 -0
- package/dist/docs/src/content-vi/foundations/styles/icons/library.mdx +19 -0
- package/dist/docs/src/content-vi/foundations/styles/icons/usage.mdx +100 -0
- package/dist/docs/src/content-vi/foundations/styles/illustration.mdx +9 -0
- package/dist/docs/src/content-vi/foundations/styles/interaction-states.mdx +49 -0
- package/dist/docs/src/content-vi/foundations/styles/layout/app-frame.mdx +77 -0
- package/dist/docs/src/content-vi/foundations/styles/layout/breakpoints.mdx +56 -0
- package/dist/docs/src/content-vi/foundations/styles/layout/grid.mdx +87 -0
- package/dist/docs/src/content-vi/foundations/styles/layout/index.mdx +24 -0
- package/dist/docs/src/content-vi/foundations/styles/layout/page-widths.mdx +34 -0
- package/dist/docs/src/content-vi/foundations/styles/logos/downloads-and-terms.mdx +65 -0
- package/dist/docs/src/content-vi/foundations/styles/logos/index.mdx +64 -0
- package/dist/docs/src/content-vi/foundations/styles/logos/placement-and-containers.mdx +96 -0
- package/dist/docs/src/content-vi/foundations/styles/logos/product-logos.mdx +56 -0
- package/dist/docs/src/content-vi/foundations/styles/logos/using-the-logo.mdx +70 -0
- package/dist/docs/src/content-vi/foundations/styles/opacity.mdx +9 -0
- package/dist/docs/src/content-vi/foundations/styles/radius.mdx +98 -0
- package/dist/docs/src/content-vi/foundations/styles/size-and-density.mdx +41 -0
- package/dist/docs/src/content-vi/foundations/styles/spacing.mdx +70 -0
- package/dist/docs/src/content-vi/foundations/styles/typography/code.mdx +53 -0
- package/dist/docs/src/content-vi/foundations/styles/typography/index.mdx +58 -0
- package/dist/docs/src/content-vi/foundations/styles/typography/usage.mdx +144 -0
- package/dist/docs/src/content-vi/foundations/styles/typography/vietnamese.mdx +46 -0
- package/dist/docs/src/content-vi/foundations/styles/z-index.mdx +9 -0
- package/dist/docs/src/content.config.ts +68 -0
- package/dist/docs/src/lib/brand.ts +57 -0
- package/dist/docs/src/lib/contrast.ts +34 -0
- package/dist/docs/src/lib/i18n.ts +152 -0
- package/dist/docs/src/lib/icons.ts +46 -0
- package/dist/docs/src/lib/logo-geometry.ts +30 -0
- package/dist/docs/src/lib/lucide-categories.json +1 -0
- package/dist/docs/src/lib/lucide-data.ts +40 -0
- package/dist/docs/src/lib/navigation.ts +222 -0
- package/dist/docs/src/lib/release-phases.ts +10 -0
- package/dist/docs/src/lib/routes.ts +40 -0
- package/dist/docs/src/lib/token-descriptions-vi.ts +40 -0
- package/dist/docs/src/lib/tokens.ts +67 -0
- package/dist/docs/src/lib/tri-grid.ts +28 -0
- package/dist/docs/src/styles/chrome.css +122 -0
- package/dist/docs/src/styles/color.css +270 -0
- package/dist/docs/src/styles/docs.css +323 -0
- package/dist/docs/src/styles/global.css +66 -0
- package/dist/docs/src/styles/icons.css +135 -0
- package/dist/docs/src/styles/layout.css +165 -0
- package/dist/docs/src/styles/logos.css +236 -0
- package/dist/docs/src/styles/shapes.css +139 -0
- package/dist/docs/src/styles/tokens.css +461 -0
- package/dist/docs/src/styles/tokens.js +425 -0
- package/dist/docs/src/styles/typography.css +91 -0
- package/dist/docs/tokens/border.json +26 -0
- package/dist/docs/tokens/breakpoints.json +8 -0
- package/dist/docs/tokens/color.json +1350 -0
- package/dist/docs/tokens/elevation.json +26 -0
- package/dist/docs/tokens/layout.json +29 -0
- package/dist/docs/tokens/motion.json +12 -0
- package/dist/docs/tokens/radius.json +44 -0
- package/dist/docs/tokens/size.json +13 -0
- package/dist/docs/tokens/spacing.json +18 -0
- package/dist/docs/tokens/states.json +12 -0
- package/dist/docs/tokens/typography.json +220 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +1823 -0
- package/package.json +36 -4
|
@@ -0,0 +1,794 @@
|
|
|
1
|
+
---
|
|
2
|
+
// Live examples for the Color page. Everything reads tokens/color.json, so the page shows the
|
|
3
|
+
// values the products get. Swatches use the CSS variables, so they follow the page theme;
|
|
4
|
+
// tables list both themes, with contrast computed from the same values.
|
|
5
|
+
import '../styles/color.css';
|
|
6
|
+
import { tokens } from '../lib/tokens';
|
|
7
|
+
import { contrast, ratio } from '../lib/contrast';
|
|
8
|
+
import { pairs } from '../../scripts/color-pairs.mjs';
|
|
9
|
+
import { exceptions } from '../../scripts/contrast-exceptions.mjs';
|
|
10
|
+
import ColorExample from './ColorExample.astro';
|
|
11
|
+
import Info from '@lucide/astro/icons/info';
|
|
12
|
+
import CircleCheck from '@lucide/astro/icons/circle-check';
|
|
13
|
+
import TriangleAlert from '@lucide/astro/icons/triangle-alert';
|
|
14
|
+
import CircleX from '@lucide/astro/icons/circle-x';
|
|
15
|
+
import Circle from '@lucide/astro/icons/circle';
|
|
16
|
+
import { langOf } from '../lib/i18n';
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
kind: 'brand' | 'scales' | 'roles' | 'status' | 'labels' | 'actions' | 'tokens' | 'pairing' | 'dodont' | 'example'
|
|
20
|
+
| 'balance' | 'anatomy' | 'surfaces' | 'states' | 'banners' | 'steps' | 'themes' | 'contrast'
|
|
21
|
+
| 'palette' | 'ramps' | 'emphasis';
|
|
22
|
+
group?: 'text' | 'icon' | 'surface' | 'background' | 'border';
|
|
23
|
+
/** For kind="dodont": which pair to show. */
|
|
24
|
+
item?: 'yellow' | 'meaning' | 'alone' | 'tokens' | 'primary' | 'pairing' | 'warning';
|
|
25
|
+
}
|
|
26
|
+
const { kind, group, item } = Astro.props;
|
|
27
|
+
const vi = langOf(Astro.url.pathname) === 'vi';
|
|
28
|
+
const L = (en: string, viText: string) => (vi ? viText : en);
|
|
29
|
+
|
|
30
|
+
// Vietnamese text of the token descriptions shown on the page. The English text is the
|
|
31
|
+
// description in tokens/color.json; a token missing here shows its English description.
|
|
32
|
+
const descVi: Record<string, string> = {
|
|
33
|
+
'color.brand.yellow': 'Vàng thương hiệu. Logo, vạch chỉ trang hiện tại, phần tô nổi bật. Không bao giờ dùng cho chữ hay icon trên nền sáng (1.85:1 trên nền trắng).',
|
|
34
|
+
'color.brand.graphite': 'Xám ghi thương hiệu. Chữ và nút chính.',
|
|
35
|
+
'color.brand.white': 'Trắng thương hiệu.',
|
|
36
|
+
'color.static.white': 'Trắng ở cả hai giao diện: chữ trên nền đặc của trạng thái.',
|
|
37
|
+
'color.static.black': 'Đen ở cả hai giao diện.',
|
|
38
|
+
'color.text': 'Chữ mặc định.',
|
|
39
|
+
'color.text.subtle': 'Chữ phụ: mô tả, cột phụ trong bảng, điều hướng.',
|
|
40
|
+
'color.text.subtlest': 'Chữ mức ba: metadata, mốc thời gian, placeholder, chữ hướng dẫn, breadcrumb.',
|
|
41
|
+
'color.text.disabled': 'Chữ của control bị vô hiệu. Được miễn yêu cầu tương phản (WCAG 1.4.3).',
|
|
42
|
+
'color.text.inverse': 'Chữ trên background.neutral.bold và surface.inverse.',
|
|
43
|
+
'color.text.on-bold': 'Chữ trên các nền đặc của trạng thái (background.*.bold): trắng ở giao diện sáng, graphite 1 ở giao diện tối.',
|
|
44
|
+
'color.text.on-brand': 'Chữ trên màu vàng thương hiệu (7.76:1).',
|
|
45
|
+
'color.icon': 'Màu icon mặc định.',
|
|
46
|
+
'color.icon.subtle': 'Icon phụ, đặt cạnh text.subtle hoặc text.subtlest.',
|
|
47
|
+
'color.icon.disabled': 'Icon của control bị vô hiệu.',
|
|
48
|
+
'color.icon.inverse': 'Icon trên background.neutral.bold và surface.inverse.',
|
|
49
|
+
'color.icon.on-bold': 'Icon trên các nền đặc của trạng thái.',
|
|
50
|
+
'color.link': 'Chữ của link.',
|
|
51
|
+
'color.link.hover': 'Link khi hover.',
|
|
52
|
+
'color.background.neutral': 'Nền trung tính ở trạng thái nghỉ: nút mặc định, tag, ô nhập trên bề mặt nhạt.',
|
|
53
|
+
'color.background.neutral.hover': 'Nền trung tính khi hover.',
|
|
54
|
+
'color.background.neutral.pressed': 'Nền trung tính khi nhấn.',
|
|
55
|
+
'color.background.neutral.subtle': 'Trong suốt ở trạng thái nghỉ: dòng trong bảng, mục menu, nút ghost.',
|
|
56
|
+
'color.background.neutral.subtle.hover': 'Dòng, mục menu hoặc nút ghost khi hover. Nhạt hơn trạng thái được chọn, nên dòng đang hover không bao giờ trông như đang được chọn.',
|
|
57
|
+
'color.background.neutral.subtle.pressed': 'Dòng, mục menu hoặc nút ghost khi nhấn.',
|
|
58
|
+
'color.background.neutral.bold': 'Nút chính. Xám ghi ở giao diện sáng; chuyển sang màu sáng ở giao diện tối, để vẫn là thành phần nổi nhất.',
|
|
59
|
+
'color.background.neutral.bold.hover': 'Nút chính khi hover: lùi một bậc khỏi graphite 12, vì đó là bậc cuối.',
|
|
60
|
+
'color.background.neutral.bold.pressed': 'Nút chính khi nhấn: lùi hai bậc khỏi graphite 12.',
|
|
61
|
+
'color.background.selected': 'Dòng hoặc mục được chọn. Luôn thêm một dấu hiệu thứ hai: vạch chỉ báo màu vàng, dấu tích hoặc chữ đậm.',
|
|
62
|
+
'color.background.selected.hover': 'Dòng hoặc mục được chọn khi hover.',
|
|
63
|
+
'color.background.disabled': 'Nền của control bị vô hiệu.',
|
|
64
|
+
'color.background.brand.subtle': 'Bề mặt và ghi chú thương hiệu.',
|
|
65
|
+
'color.background.brand.bold': 'Nền vàng thương hiệu. Chữ xám ghi bên trên (text.on-brand). Không có nút màu vàng.',
|
|
66
|
+
'color.surface': 'Card, panel, dialog. Sáng hơn nền trang một bậc ở giao diện tối.',
|
|
67
|
+
'color.surface.page': 'Nền trang.',
|
|
68
|
+
'color.surface.subtle': 'Sidebar, header của bảng, vùng nền nhạt.',
|
|
69
|
+
'color.surface.inverse': 'Bề mặt đảo màu: tooltip, toast. Chữ bên trên: text.inverse.',
|
|
70
|
+
'color.surface.scrim': 'Lớp phủ sau dialog và drawer.',
|
|
71
|
+
'color.border': 'Viền mặc định: đường phân cách, card, bảng.',
|
|
72
|
+
'color.border.bold': 'Viền của ô nhập, checkbox và select. 3:1 (WCAG 1.4.11) trên mọi bề mặt và nền trung tính, kể cả dòng được chọn.',
|
|
73
|
+
'color.border.disabled': 'Viền của control bị vô hiệu.',
|
|
74
|
+
'color.border.focus': 'Viền focus: dày 2 px, cách control 2 px. 3:1 trên mọi bề mặt và dòng được chọn.',
|
|
75
|
+
'color.border.selected': 'Vạch chỉ báo trang hoặc tab hiện tại. Luôn đi kèm một dấu hiệu thứ hai.',
|
|
76
|
+
'color.border.brand': 'Viền thương hiệu, như nhãn early access.',
|
|
77
|
+
'color.border.brand.subtle': 'Viền của bề mặt và ghi chú thương hiệu.',
|
|
78
|
+
};
|
|
79
|
+
const desc = (t: { name: string; description: string }) => (vi ? descVi[t.name] ?? t.description : t.description);
|
|
80
|
+
|
|
81
|
+
const byName = new Map(tokens.map((t) => [t.name, t]));
|
|
82
|
+
const tok = (name: string) => {
|
|
83
|
+
const t = byName.get(`color.${name}`);
|
|
84
|
+
if (!t) throw new Error(`ColorDemo: no token color.${name}`);
|
|
85
|
+
return { name: t.name, cssVar: t.cssVar, light: t.resolved, dark: t.darkResolved ?? t.resolved, description: t.description };
|
|
86
|
+
};
|
|
87
|
+
const up = (hex: string) => (hex.startsWith('#') ? hex.toUpperCase() : hex);
|
|
88
|
+
const bgLight = tok('surface.page').light;
|
|
89
|
+
const bgDark = tok('surface.page').dark;
|
|
90
|
+
|
|
91
|
+
// Brand
|
|
92
|
+
const brand = [
|
|
93
|
+
{ id: 'yellow', name: L('Yellow', 'Vàng'), rgb: '252 176 23', use: L('Fills, indicators and highlights, always with graphite text on top. Never text or icons on a light background.', 'Nền, vạch chỉ báo và phần tô nổi bật, luôn có chữ xám ghi bên trên. Không bao giờ dùng cho chữ hay icon trên nền sáng.') },
|
|
94
|
+
{ id: 'graphite', name: L('Graphite', 'Xám ghi'), rgb: '43 42 40', use: L('Text, the primary button and dark brand surfaces. Step 12 of the graphite scale.', 'Chữ, nút chính và bề mặt thương hiệu màu tối. Bậc 12 của thang graphite.') },
|
|
95
|
+
{ id: 'white', name: L('White', 'Trắng'), rgb: '255 255 255', use: L('Page and card background in the light theme; text on dark fills.', 'Nền trang và card ở giao diện sáng; chữ trên nền tối.') },
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
// Scales: twelve steps, each with one job. Every step has a dark value under the same name.
|
|
99
|
+
const stepJobs = [
|
|
100
|
+
{ steps: '1–2', job: L('Backgrounds; graphite 2 is also the hover of a row or menu item in light', 'Nền; graphite 2 cũng là màu hover của dòng hoặc mục menu ở giao diện sáng') },
|
|
101
|
+
{ steps: '3–5', job: L('Component backgrounds: rest, hover, pressed or selected', 'Nền component: nghỉ, hover, nhấn hoặc được chọn') },
|
|
102
|
+
{ steps: '6–8', job: L('Borders and dividers', 'Viền và đường phân cách') },
|
|
103
|
+
{ steps: '9–10', job: L('Solid fills and their hover; in graphite, 9 is the control border and 10 the tertiary text', 'Nền đặc và màu hover của chúng; ở graphite, 9 là viền control và 10 là chữ mức ba') },
|
|
104
|
+
{ steps: '11–12', job: L('Text: 11 subtle, 12 default', 'Chữ: 11 là chữ phụ, 12 là chữ mặc định') },
|
|
105
|
+
];
|
|
106
|
+
const scaleNames: Record<string, string> = {
|
|
107
|
+
graphite: 'Graphite', yellow: 'Yellow', blue: 'Blue', green: 'Green', orange: 'Orange', red: 'Red',
|
|
108
|
+
};
|
|
109
|
+
const scales = Object.entries(scaleNames).map(([id, name]) => ({
|
|
110
|
+
id,
|
|
111
|
+
name,
|
|
112
|
+
steps: Array.from({ length: 12 }, (_, i) => tok(`${id}.${i + 1}`)),
|
|
113
|
+
}));
|
|
114
|
+
|
|
115
|
+
// Roles
|
|
116
|
+
const roleGroups: Record<string, string[]> = {
|
|
117
|
+
text: ['text', 'text.subtle', 'text.subtlest', 'text.disabled', 'text.inverse', 'text.on-bold', 'text.on-brand', 'link', 'link.hover'],
|
|
118
|
+
icon: ['icon', 'icon.subtle', 'icon.disabled', 'icon.inverse', 'icon.on-bold'],
|
|
119
|
+
surface: ['surface.page', 'surface', 'surface.subtle', 'surface.inverse', 'surface.scrim'],
|
|
120
|
+
background: [
|
|
121
|
+
'background.neutral', 'background.neutral.hover', 'background.neutral.pressed',
|
|
122
|
+
'background.neutral.subtle', 'background.neutral.subtle.hover', 'background.neutral.subtle.pressed',
|
|
123
|
+
'background.neutral.bold', 'background.neutral.bold.hover', 'background.neutral.bold.pressed',
|
|
124
|
+
'background.selected', 'background.selected.hover', 'background.disabled',
|
|
125
|
+
'background.brand.subtle', 'background.brand.bold',
|
|
126
|
+
],
|
|
127
|
+
border: ['border', 'border.bold', 'border.disabled', 'border.focus', 'border.selected', 'border.brand', 'border.brand.subtle'],
|
|
128
|
+
};
|
|
129
|
+
// What each role is checked against (its hardest background), and the minimum.
|
|
130
|
+
const checks: Record<string, { on: string; min: number } | undefined> = {
|
|
131
|
+
text: { on: 'background.selected.hover', min: 4.5 },
|
|
132
|
+
'text.subtle': { on: 'background.selected.hover', min: 4.5 },
|
|
133
|
+
'text.subtlest': { on: 'background.selected.hover', min: 4.5 },
|
|
134
|
+
'text.inverse': { on: 'background.neutral.bold', min: 4.5 },
|
|
135
|
+
'text.on-bold': { on: 'background.danger.bold', min: 4.5 },
|
|
136
|
+
'text.on-brand': { on: 'background.brand.bold', min: 4.5 },
|
|
137
|
+
link: { on: 'background.selected.hover', min: 4.5 },
|
|
138
|
+
'link.hover': { on: 'background.selected.hover', min: 4.5 },
|
|
139
|
+
icon: { on: 'background.selected', min: 3 },
|
|
140
|
+
'icon.subtle': { on: 'background.neutral', min: 3 },
|
|
141
|
+
'icon.inverse': { on: 'background.neutral.bold', min: 3 },
|
|
142
|
+
'icon.on-bold': { on: 'background.danger.bold', min: 3 },
|
|
143
|
+
'border.bold': { on: 'background.neutral', min: 3 },
|
|
144
|
+
'border.focus': { on: 'background.selected', min: 3 },
|
|
145
|
+
};
|
|
146
|
+
const roles = group
|
|
147
|
+
? roleGroups[group].map((n) => {
|
|
148
|
+
const t = tok(n);
|
|
149
|
+
const c = checks[n];
|
|
150
|
+
const on = c ? tok(c.on) : undefined;
|
|
151
|
+
return {
|
|
152
|
+
...t,
|
|
153
|
+
check: c && on ? { on: c.on, min: c.min, light: contrast(t.light, on.light, bgLight), dark: contrast(t.dark, on.dark, bgDark) } : undefined,
|
|
154
|
+
};
|
|
155
|
+
})
|
|
156
|
+
: [];
|
|
157
|
+
|
|
158
|
+
// Status roles. Each has text, a subtle and a bold background, and borders, from its own scale.
|
|
159
|
+
const statusRoles = [
|
|
160
|
+
{ id: 'info', name: L('Info', 'Thông tin'), scale: 'blue', meaning: L('Running and in-progress work, neutral information', 'Việc đang chạy, đang xử lý; thông tin trung lập'), example: L('Syncing', 'Đang đồng bộ') },
|
|
161
|
+
{ id: 'success', name: L('Success', 'Thành công'), scale: 'green', meaning: L('Synced, connected, completed, healthy', 'Đã đồng bộ, đã kết nối, đã xong, hoạt động tốt'), example: L('Synced', 'Đã đồng bộ') },
|
|
162
|
+
{ id: 'warning', name: L('Warning', 'Cảnh báo'), scale: 'orange', meaning: L('Needs attention soon: partial sync, expiring token, rate limit', 'Sắp cần xử lý: đồng bộ một phần, token sắp hết hạn, bị giới hạn tần suất'), example: L('Partial', 'Một phần') },
|
|
163
|
+
{ id: 'danger', name: L('Danger', 'Nguy hiểm'), scale: 'red', meaning: L('Failed, error, blocked; destructive actions', 'Thất bại, lỗi, bị chặn; hành động xoá hoặc huỷ'), example: L('Failed', 'Lỗi') },
|
|
164
|
+
{ id: 'neutral', name: L('Neutral', 'Trung tính'), scale: 'graphite', meaning: L('Idle, paused, draft, skipped, archived', 'Chờ, tạm dừng, nháp, bỏ qua, lưu trữ'), example: L('Paused', 'Tạm dừng') },
|
|
165
|
+
].map((s) => {
|
|
166
|
+
const neutral = s.id === 'neutral';
|
|
167
|
+
const fg = tok(neutral ? 'text.subtle' : `text.${s.id}`);
|
|
168
|
+
const bg = tok(neutral ? 'background.neutral' : `background.${s.id}.subtle`);
|
|
169
|
+
const border = tok(neutral ? 'border' : `border.${s.id}.subtle`);
|
|
170
|
+
const em = tok(neutral ? 'background.neutral.bold' : `background.${s.id}.bold`);
|
|
171
|
+
const on = tok(neutral ? 'text.inverse' : 'text.on-bold');
|
|
172
|
+
return {
|
|
173
|
+
...s,
|
|
174
|
+
fg, bg, border, em,
|
|
175
|
+
tokens: neutral
|
|
176
|
+
? ['text.subtle', 'background.neutral', 'background.neutral.bold']
|
|
177
|
+
: [`text.${s.id}`, `background.${s.id}.subtle`, `background.${s.id}.bold`],
|
|
178
|
+
c: {
|
|
179
|
+
fgLight: contrast(fg.light, bgLight), fgDark: contrast(fg.dark, bgDark, bgDark),
|
|
180
|
+
onBgLight: contrast(fg.light, bg.light, bgLight), onBgDark: contrast(fg.dark, bg.dark, bgDark),
|
|
181
|
+
emLight: contrast(on.light, em.light), emDark: contrast(on.dark, em.dark),
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Token reference: every role and component token split into the four parts of its name,
|
|
187
|
+
// color.{property}.{role}.{emphasis}.{state}. A part that is left out takes its default.
|
|
188
|
+
const PROPS = ['text', 'icon', 'link', 'surface', 'background', 'border', 'skeleton', 'code'];
|
|
189
|
+
const ROLES = ['neutral', 'brand', 'danger', 'warning', 'success', 'info', 'inverse', 'disabled', 'page', 'scrim', 'on-bold', 'on-brand', 'background', 'text'];
|
|
190
|
+
const EMPHASIS = ['subtle', 'subtlest', 'bold'];
|
|
191
|
+
const STATES = ['hover', 'pressed', 'selected', 'focus'];
|
|
192
|
+
const SCALE_NAMES = Object.keys(scaleNames);
|
|
193
|
+
const parts = (name: string) => {
|
|
194
|
+
const [, property, ...rest] = name.split('.');
|
|
195
|
+
const out = { property, role: [] as string[], emphasis: [] as string[], state: [] as string[] };
|
|
196
|
+
for (const seg of rest) {
|
|
197
|
+
if (EMPHASIS.includes(seg)) out.emphasis.push(seg);
|
|
198
|
+
else if (STATES.includes(seg)) out.state.push(seg);
|
|
199
|
+
else if (ROLES.includes(seg)) out.role.push(seg);
|
|
200
|
+
else throw new Error(`ColorDemo: unknown name part "${seg}" in ${name}`);
|
|
201
|
+
}
|
|
202
|
+
return out;
|
|
203
|
+
};
|
|
204
|
+
const refTokens = tokens
|
|
205
|
+
.filter((t) => t.name.startsWith('color.') && t.type === 'color')
|
|
206
|
+
.filter((t) => PROPS.includes(t.name.split('.')[1]))
|
|
207
|
+
.map((t) => ({ ...tok(t.name.replace('color.', '')), parts: parts(t.name), component: t.name.startsWith('color.code.') }));
|
|
208
|
+
const baseTokens = tokens
|
|
209
|
+
.filter((t) => t.name.startsWith('color.brand.') || t.name.startsWith('color.static.'))
|
|
210
|
+
.map((t) => tok(t.name.replace('color.', '')));
|
|
211
|
+
|
|
212
|
+
// Pairing: which text, icon and border tokens may sit on each background. Built from
|
|
213
|
+
// scripts/color-pairs.mjs, the same list the contrast check reads, so the page and the check agree.
|
|
214
|
+
const onBg = new Map<string, { text: string[]; other: string[] }>();
|
|
215
|
+
for (const { fg, on, min } of pairs) {
|
|
216
|
+
for (const bg of on) {
|
|
217
|
+
const entry = onBg.get(bg) ?? { text: [], other: [] };
|
|
218
|
+
(min >= 4.5 ? entry.text : entry.other).push(fg);
|
|
219
|
+
onBg.set(bg, entry);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const pairingRows: { bgs: string[]; text: string[]; other: string[] }[] = [];
|
|
223
|
+
for (const [bg, { text, other }] of onBg) {
|
|
224
|
+
const key = JSON.stringify([text, other]);
|
|
225
|
+
const row = pairingRows.find((r) => JSON.stringify([r.text, r.other]) === key);
|
|
226
|
+
if (row) row.bgs.push(bg);
|
|
227
|
+
else pairingRows.push({ bgs: [bg], text, other });
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Known exceptions: a token is flagged on a pairing card when it is below WCAG on any of the card's backgrounds.
|
|
231
|
+
const below = (fg: string, bgs: string[]) => exceptions.some((e) => e.fg === fg && bgs.includes(e.bg));
|
|
232
|
+
|
|
233
|
+
// Theme preview: every colour token pinned to its light or dark value, so one card can be
|
|
234
|
+
// drawn in both themes side by side, whatever theme the page is in.
|
|
235
|
+
const colorTokens = tokens.filter((t) => t.name.startsWith('color.') && t.type === 'color');
|
|
236
|
+
const themeStyle = (mode: 'light' | 'dark') =>
|
|
237
|
+
colorTokens.map((t) => `${t.cssVar}: ${mode === 'dark' ? (t.darkResolved ?? t.resolved) : t.resolved}`).join('; ');
|
|
238
|
+
|
|
239
|
+
// Contrast samples: pairs that pass and pairs that fail, with their real ratios.
|
|
240
|
+
const contrastSamples = [
|
|
241
|
+
{ fg: 'text', bg: 'surface.page', text: L('Graphite on white', 'Xám ghi trên nền trắng'), min: 4.5 },
|
|
242
|
+
{ fg: 'text.subtle', bg: 'background.selected.hover', text: L('Subtle text on a selected row', 'Chữ phụ trên dòng được chọn'), min: 4.5 },
|
|
243
|
+
{ fg: 'text.on-brand', bg: 'background.brand.bold', text: L('Graphite on yellow', 'Xám ghi trên nền vàng'), min: 4.5 },
|
|
244
|
+
{ fg: 'brand.yellow', bg: 'surface.page', text: L('Yellow on white', 'Vàng trên nền trắng'), min: 4.5 },
|
|
245
|
+
{ fg: 'static.white', bg: 'brand.yellow', text: L('White on yellow', 'Trắng trên nền vàng'), min: 4.5 },
|
|
246
|
+
{ fg: 'border.bold', bg: 'background.neutral', text: L('Control edge on a grey fill', 'Viền control trên nền xám'), min: 3 },
|
|
247
|
+
].map((c) => {
|
|
248
|
+
const fg = tok(c.fg); const bg = tok(c.bg);
|
|
249
|
+
const r = contrast(fg.light, bg.light);
|
|
250
|
+
return { ...c, fgVal: fg.light, bgVal: bg.light, r, pass: r >= c.min };
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// Step jobs, drawn: what each step of a scale looks like in use.
|
|
254
|
+
const stepUse = (s: string) => Array.from({ length: 12 }, (_, i) => ({ step: i + 1, v: `var(--bh-color-${s}-${i + 1})` }));
|
|
255
|
+
|
|
256
|
+
// Icons for the solid state labels (Lucide, 16 px, currentColor).
|
|
257
|
+
const icon: Record<string, typeof Info> = {
|
|
258
|
+
info: Info,
|
|
259
|
+
success: CircleCheck,
|
|
260
|
+
warning: TriangleAlert,
|
|
261
|
+
danger: CircleX,
|
|
262
|
+
neutral: Circle,
|
|
263
|
+
};
|
|
264
|
+
const SuccessIcon = icon.success;
|
|
265
|
+
const DangerIcon = icon.danger;
|
|
266
|
+
|
|
267
|
+
// Palette: the ink that reads best on each swatch, and where the brand colours sit.
|
|
268
|
+
const INK_DARK = '#2b2a28';
|
|
269
|
+
const INK_LIGHT = '#ffffff';
|
|
270
|
+
const ink = (bg: string) => (contrast(INK_LIGHT, bg) >= contrast(INK_DARK, bg) ? INK_LIGHT : INK_DARK);
|
|
271
|
+
const brandStep: Record<'light' | 'dark', Record<string, number>> = { light: { yellow: 7, graphite: 12 }, dark: { yellow: 11 } };
|
|
272
|
+
|
|
273
|
+
// Role specimens: each role token drawn as what it paints, on the background it is checked on.
|
|
274
|
+
const pageOf = (mode: 'light' | 'dark') => (mode === 'light' ? bgLight : bgDark);
|
|
275
|
+
const specimen = (r: (typeof roles)[number], mode: 'light' | 'dark') => {
|
|
276
|
+
const v = r[mode];
|
|
277
|
+
const on = r.check ? tok(r.check.on)[mode] : pageOf(mode);
|
|
278
|
+
const edge = tok('border')[mode];
|
|
279
|
+
if (group === 'text') return { style: `background: ${on}; color: ${v}`, body: 'Aa' };
|
|
280
|
+
if (group === 'icon') return { style: `background: ${on}; color: ${v}`, body: 'icon' };
|
|
281
|
+
if (group === 'border') return { style: `background: ${pageOf(mode)}; box-shadow: inset 0 0 0 2px ${v}`, body: '' };
|
|
282
|
+
return { style: `background: ${pageOf(mode)}; --fill: ${v}; --edge: ${edge}`, body: 'fill' };
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// Emphasis: subtle, default and bold fills of each role, with the text that goes on them.
|
|
286
|
+
const emphasisRows = [
|
|
287
|
+
{ name: L('Neutral', 'Trung tính'), fills: [['background.neutral.subtle', 'text'], ['background.neutral', 'text'], ['background.neutral.bold', 'text.inverse']] },
|
|
288
|
+
{ name: L('Brand', 'Thương hiệu'), fills: [['background.brand.subtle', 'text'], null, ['background.brand.bold', 'text.on-brand']] },
|
|
289
|
+
...['info', 'success', 'warning', 'danger'].map((r) => ({
|
|
290
|
+
name: vi ? ({ info: 'Thông tin', success: 'Thành công', warning: 'Cảnh báo', danger: 'Nguy hiểm' } as Record<string, string>)[r] : r[0].toUpperCase() + r.slice(1),
|
|
291
|
+
fills: [[`background.${r}.subtle`, `text.${r}`], null, [`background.${r}.bold`, 'text.on-bold']],
|
|
292
|
+
})),
|
|
293
|
+
].map((row) => ({ ...row, fills: row.fills.map((f) => (f ? { bg: tok(f[0]), fg: tok(f[1]) } : null)) }));
|
|
294
|
+
|
|
295
|
+
// Actions
|
|
296
|
+
const actions = [
|
|
297
|
+
{ id: 'primary', label: L('Save changes', 'Lưu thay đổi'), tokens: 'background.neutral.bold · text.inverse', bg: tok('background.neutral.bold'), text: tok('text.inverse'), use: L('One per view: the main action. Graphite; it turns light in dark.', 'Mỗi màn hình một nút: hành động chính. Xám ghi; chuyển sang màu sáng ở giao diện tối.') },
|
|
298
|
+
{ id: 'danger', label: L('Delete', 'Xoá'), tokens: 'background.danger.bold · text.on-bold', bg: tok('background.danger.bold'), text: tok('text.on-bold'), use: L('Destructive actions, after a confirmation.', 'Hành động xoá hoặc huỷ, sau bước xác nhận.') },
|
|
299
|
+
].map((a) => ({ ...a, cLight: contrast(a.text.light, a.bg.light), cDark: contrast(a.text.dark, a.bg.dark) }));
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
{kind === 'brand' && (
|
|
303
|
+
<div class="c-brand">
|
|
304
|
+
{brand.map((b) => {
|
|
305
|
+
const t = tok(`brand.${b.id}`);
|
|
306
|
+
return (
|
|
307
|
+
<figure class={`c-brand-card is-${b.id}`}>
|
|
308
|
+
<div class="c-brand-chip" style={`background: var(${t.cssVar})`}>
|
|
309
|
+
<span>{b.name}</span>
|
|
310
|
+
</div>
|
|
311
|
+
<figcaption>
|
|
312
|
+
<code>color.brand.{b.id}</code>
|
|
313
|
+
<span class="c-num">{up(t.light)} · RGB {b.rgb}</span>
|
|
314
|
+
<span>{b.use}</span>
|
|
315
|
+
</figcaption>
|
|
316
|
+
</figure>
|
|
317
|
+
);
|
|
318
|
+
})}
|
|
319
|
+
</div>
|
|
320
|
+
)}
|
|
321
|
+
|
|
322
|
+
{kind === 'scales' && (
|
|
323
|
+
<div class="c-scales">
|
|
324
|
+
{scales.map((s) => (
|
|
325
|
+
<figure class="c-scale">
|
|
326
|
+
<figcaption><b>{s.name}</b> <code>color.{s.id}.1–12</code> · {L('the dark theme uses the same names', 'giao diện tối dùng cùng tên')}</figcaption>
|
|
327
|
+
<div class="table-wrap">
|
|
328
|
+
<table class="c-scale-table">
|
|
329
|
+
<thead><tr><th scope="col"><span class="sr-only">{L('Theme', 'Giao diện')}</span></th>{s.steps.map((_, i) => <th scope="col">{i + 1}</th>)}</tr></thead>
|
|
330
|
+
<tbody>
|
|
331
|
+
{(['light', 'dark'] as const).map((mode) => (
|
|
332
|
+
<tr>
|
|
333
|
+
<th scope="row">{mode === 'light' ? L('Light', 'Sáng') : L('Dark', 'Tối')}</th>
|
|
334
|
+
{s.steps.map((t) => (
|
|
335
|
+
<td>
|
|
336
|
+
<span class="c-chip" style={`background: ${t[mode]}`} />
|
|
337
|
+
<span class="c-num">{up(t[mode]).slice(1)}</span>
|
|
338
|
+
</td>
|
|
339
|
+
))}
|
|
340
|
+
</tr>
|
|
341
|
+
))}
|
|
342
|
+
</tbody>
|
|
343
|
+
</table>
|
|
344
|
+
</div>
|
|
345
|
+
</figure>
|
|
346
|
+
))}
|
|
347
|
+
<ul class="c-steps">
|
|
348
|
+
{stepJobs.map((j) => <li><b>{j.steps}</b> {j.job}</li>)}
|
|
349
|
+
</ul>
|
|
350
|
+
</div>
|
|
351
|
+
)}
|
|
352
|
+
|
|
353
|
+
{kind === 'roles' && (
|
|
354
|
+
<div class="table-wrap">
|
|
355
|
+
<table class="c-roles stackable">
|
|
356
|
+
<colgroup><col class="c-col-token" /><col class="c-col-mode" /><col class="c-col-mode" /><col class="c-col-use" /></colgroup>
|
|
357
|
+
<thead><tr><th scope="col">Token</th><th scope="col">{L('Light', 'Sáng')}</th><th scope="col">{L('Dark', 'Tối')}</th><th scope="col">{L('Use', 'Dùng cho')}</th></tr></thead>
|
|
358
|
+
<tbody>
|
|
359
|
+
{roles.map((r) => (
|
|
360
|
+
<tr>
|
|
361
|
+
<td data-label="Token"><code set:html={r.name.replace('color.', '').split('.').join('.<wbr>')} /></td>
|
|
362
|
+
{(['light', 'dark'] as const).map((mode) => {
|
|
363
|
+
const sp = specimen(r, mode);
|
|
364
|
+
return (
|
|
365
|
+
<td data-label={mode === 'light' ? L('Light', 'Sáng') : L('Dark', 'Tối')}>
|
|
366
|
+
<span class={`c-spec is-${group}`} style={sp.style} aria-hidden="true">
|
|
367
|
+
{group === 'icon' ? <SuccessIcon size={20} /> : sp.body === 'fill' ? <span class="c-spec-fill" /> : sp.body}
|
|
368
|
+
</span>
|
|
369
|
+
<span class="c-num">{up(r[mode])}</span>
|
|
370
|
+
</td>
|
|
371
|
+
);
|
|
372
|
+
})}
|
|
373
|
+
<td data-label={L('Use', 'Dùng cho')}>
|
|
374
|
+
{desc(r)}
|
|
375
|
+
{r.check && <span class="c-check">{L('Contrast', 'Tương phản')} {ratio(r.check.light)} / {ratio(r.check.dark)} {L('on', 'trên')} {r.check.on}</span>}
|
|
376
|
+
</td>
|
|
377
|
+
</tr>
|
|
378
|
+
))}
|
|
379
|
+
</tbody>
|
|
380
|
+
</table>
|
|
381
|
+
</div>
|
|
382
|
+
)}
|
|
383
|
+
|
|
384
|
+
{kind === 'actions' && (
|
|
385
|
+
<div class="c-actions">
|
|
386
|
+
{actions.map((a) => (
|
|
387
|
+
<figure class="c-action">
|
|
388
|
+
<div class="c-action-stage">
|
|
389
|
+
<span class={`c-button is-${a.id}`}>{a.label}</span>
|
|
390
|
+
</div>
|
|
391
|
+
<figcaption>
|
|
392
|
+
<code>{a.tokens}</code>
|
|
393
|
+
<span class="c-num">{L('Light', 'Sáng')} {up(a.bg.light)}, {L('text', 'chữ')} {ratio(a.cLight)} · {L('Dark', 'Tối')} {up(a.bg.dark)}, {L('text', 'chữ')} {ratio(a.cDark)}</span>
|
|
394
|
+
<span>{a.use}</span>
|
|
395
|
+
</figcaption>
|
|
396
|
+
</figure>
|
|
397
|
+
))}
|
|
398
|
+
</div>
|
|
399
|
+
)}
|
|
400
|
+
|
|
401
|
+
{kind === 'status' && (
|
|
402
|
+
<div class="table-wrap">
|
|
403
|
+
<table class="c-status">
|
|
404
|
+
<thead>
|
|
405
|
+
<tr><th scope="col">{L('Role', 'Vai trò')}</th><th scope="col">{L('Means', 'Ý nghĩa')}</th><th scope="col">{L('Text', 'Chữ')}</th><th scope="col">{L('Subtle', 'Nhạt')}</th><th scope="col">{L('Bold', 'Đậm')}</th><th scope="col">{L('Contrast (light / dark)', 'Tương phản của chữ (sáng / tối)')}</th></tr>
|
|
406
|
+
</thead>
|
|
407
|
+
<tbody>
|
|
408
|
+
{statusRoles.map((s) => (
|
|
409
|
+
<tr>
|
|
410
|
+
<td><b>{s.name}</b><br /><span class="c-from">{vi ? `thang ${s.scale}` : `${s.scale} scale`}</span></td>
|
|
411
|
+
<td>{s.meaning}</td>
|
|
412
|
+
<td><span class="c-cell"><span class="c-dot" style={`background: var(${s.fg.cssVar})`} /><span class="c-num v-light">{up(s.fg.light)}</span><span class="c-num v-dark">{up(s.fg.dark)}</span></span></td>
|
|
413
|
+
<td><span class="c-cell"><span class="c-dot" style={`background: var(${s.bg.cssVar}); box-shadow: inset 0 0 0 1px var(${s.border.cssVar})`} /><span class="c-num v-light">{up(s.bg.light)}</span><span class="c-num v-dark">{up(s.bg.dark)}</span></span></td>
|
|
414
|
+
<td><span class="c-cell"><span class="c-dot" style={`background: var(${s.em.cssVar})`} /><span class="c-num v-light">{up(s.em.light)}</span><span class="c-num v-dark">{up(s.em.dark)}</span></span></td>
|
|
415
|
+
<td class="c-num">
|
|
416
|
+
{L('text on page', 'nền trang')} {ratio(s.c.fgLight)} / {ratio(s.c.fgDark)}<br />
|
|
417
|
+
{L('text on subtle', 'nền nhạt')} {ratio(s.c.onBgLight)} / {ratio(s.c.onBgDark)}<br />
|
|
418
|
+
{L('text on bold', 'nền đậm')} {ratio(s.c.emLight)} / {ratio(s.c.emDark)}
|
|
419
|
+
</td>
|
|
420
|
+
</tr>
|
|
421
|
+
))}
|
|
422
|
+
</tbody>
|
|
423
|
+
</table>
|
|
424
|
+
</div>
|
|
425
|
+
)}
|
|
426
|
+
|
|
427
|
+
{kind === 'labels' && (
|
|
428
|
+
<div class="c-labels">
|
|
429
|
+
<figure class="c-label-set">
|
|
430
|
+
<figcaption><b>{L('Label', 'Nhãn')}</b> · {L('outlined, for tags and statuses inside tables, lists and cards', 'có viền, cho tag và trạng thái trong bảng, danh sách và card')}</figcaption>
|
|
431
|
+
<div class="c-label-row">
|
|
432
|
+
<span class="bh-label">{L('Default', 'Mặc định')}</span>
|
|
433
|
+
<span class="bh-label is-brand">Early access</span>
|
|
434
|
+
{statusRoles.map((s) => <span class={`bh-label is-${s.id}`}>{s.example}</span>)}
|
|
435
|
+
</div>
|
|
436
|
+
<div class="c-label-row">
|
|
437
|
+
<span class="bh-label is-large">{L('Large', 'Cỡ lớn')}</span>
|
|
438
|
+
<span class="bh-label is-large is-brand">Early access</span>
|
|
439
|
+
{statusRoles.map((s) => <span class={`bh-label is-large is-${s.id}`}>{s.example}</span>)}
|
|
440
|
+
</div>
|
|
441
|
+
</figure>
|
|
442
|
+
<figure class="c-label-set">
|
|
443
|
+
<figcaption><b>{L('State label', 'Nhãn trạng thái')}</b> · {L('solid, for the one status that a page or record is about', 'nền đặc, cho trạng thái chính của một trang hoặc bản ghi')}</figcaption>
|
|
444
|
+
<div class="c-label-row">
|
|
445
|
+
{statusRoles.map((s) => { const StateIcon = icon[s.id]; return <span class={`bh-state is-${s.id}`}><StateIcon size={16} />{s.example}</span>; })}
|
|
446
|
+
</div>
|
|
447
|
+
</figure>
|
|
448
|
+
</div>
|
|
449
|
+
)}
|
|
450
|
+
|
|
451
|
+
{kind === 'tokens' && (
|
|
452
|
+
<div class="c-tokens">
|
|
453
|
+
{vi
|
|
454
|
+
? <p class="c-from">Role token. Dấu gạch nghĩa là phần đó được bỏ đi và lấy giá trị mặc định: neutral cho role, mức nhấn mặc định, trạng thái nghỉ. <code>on-bold</code> và <code>on-brand</code> gọi tên nền nằm dưới chữ thay vì một role. Component token có ghi chú riêng.</p>
|
|
455
|
+
: <p class="c-from">Role tokens. A dash means the part is left out and takes its default: neutral for the role, the default emphasis, the resting state. <code>on-bold</code> and <code>on-brand</code> name the fill the text sits on rather than a role. Component tokens are marked.</p>}
|
|
456
|
+
<div class="table-wrap">
|
|
457
|
+
<table class="c-token-ref">
|
|
458
|
+
<thead><tr><th scope="col">Token</th><th scope="col">{L('Property', 'Thứ được tô')}</th><th scope="col">{L('Role', 'Vai trò')}</th><th scope="col">{L('Emphasis', 'Mức nhấn')}</th><th scope="col">{L('State', 'Trạng thái')}</th><th scope="col">{L('Light / dark', 'Sáng / tối')}</th></tr></thead>
|
|
459
|
+
<tbody>
|
|
460
|
+
{refTokens.map((t) => (
|
|
461
|
+
<tr>
|
|
462
|
+
<td><code set:html={t.name.split('.').join('.<wbr>')} />{t.component && <span class="c-from"> {L('component', 'component token')}</span>}<span class="c-var" set:html={t.cssVar.split('-').join('-<wbr>')} /></td>
|
|
463
|
+
<td>{t.parts.property}</td>
|
|
464
|
+
<td>{t.parts.role.join(' ') || '–'}</td>
|
|
465
|
+
<td>{t.parts.emphasis.join(' ') || '–'}</td>
|
|
466
|
+
<td>{t.parts.state.join(' ') || '–'}</td>
|
|
467
|
+
<td>
|
|
468
|
+
<span class="c-cell"><span class="c-dot" style={`background: ${t.light}`} /><span class="c-num">{up(t.light)}</span></span>
|
|
469
|
+
<span class="c-cell"><span class="c-dot" style={`background: ${t.dark}`} /><span class="c-num">{up(t.dark)}</span></span>
|
|
470
|
+
</td>
|
|
471
|
+
</tr>
|
|
472
|
+
))}
|
|
473
|
+
</tbody>
|
|
474
|
+
</table>
|
|
475
|
+
</div>
|
|
476
|
+
{vi
|
|
477
|
+
? <p class="c-from">Token thang màu: <code>color.{'{'}scale{'}'}.{'{'}1–12{'}'}</code> cho {SCALE_NAMES.join(', ')}, ví dụ <code>color.graphite.12</code> (<code>--bh-color-graphite-12</code>). Giá trị của chúng có ở trang Bảng màu. Component không dùng trực tiếp các token này.</p>
|
|
478
|
+
: <p class="c-from">Scale tokens: <code>color.{'{'}scale{'}'}.{'{'}1–12{'}'}</code> for {SCALE_NAMES.join(', ')}, for example <code>color.graphite.12</code> (<code>--bh-color-graphite-12</code>). Their values are on the Palette page. Components don't use them directly.</p>}
|
|
479
|
+
<div class="table-wrap">
|
|
480
|
+
<table class="c-token-ref">
|
|
481
|
+
<thead><tr><th scope="col">Token</th><th scope="col">{L('Value', 'Giá trị')}</th><th scope="col">{L('Use', 'Dùng cho')}</th></tr></thead>
|
|
482
|
+
<tbody>
|
|
483
|
+
{baseTokens.map((t) => (
|
|
484
|
+
<tr>
|
|
485
|
+
<td><code>{t.name}</code><span class="c-var">{t.cssVar}</span></td>
|
|
486
|
+
<td><span class="c-cell"><span class="c-dot" style={`background: ${t.light}`} /><span class="c-num">{up(t.light)}</span></span></td>
|
|
487
|
+
<td>{desc(t)}</td>
|
|
488
|
+
</tr>
|
|
489
|
+
))}
|
|
490
|
+
</tbody>
|
|
491
|
+
</table>
|
|
492
|
+
</div>
|
|
493
|
+
</div>
|
|
494
|
+
)}
|
|
495
|
+
|
|
496
|
+
{kind === 'pairing' && (
|
|
497
|
+
<div class="c-pairs">
|
|
498
|
+
{pairingRows.map((r) => {
|
|
499
|
+
const bg = tok(r.bgs[0]);
|
|
500
|
+
return (
|
|
501
|
+
<figure class="c-pair">
|
|
502
|
+
<div class="c-pair-stage" style={`background: var(${bg.cssVar})`}>
|
|
503
|
+
{r.text.map((t) => <span class="c-pair-text" style={`color: var(${tok(t).cssVar})`}>{t}{below(t, r.bgs) && <span class="c-pair-warn" title={L('Below WCAG 2.2 on some of these backgrounds', 'Dưới mức WCAG 2.2 trên một số nền này')}>⚠</span>}</span>)}
|
|
504
|
+
{r.other.length > 0 && (
|
|
505
|
+
<span class="c-pair-marks">
|
|
506
|
+
{r.other.map((t) => <span class="c-pair-mark" title={t} style={t.startsWith('border') ? `box-shadow: inset 0 0 0 2px var(${tok(t).cssVar})` : `color: var(${tok(t).cssVar})`}>{t.startsWith('border') ? '' : <SuccessIcon size={16} />}</span>)}
|
|
507
|
+
</span>
|
|
508
|
+
)}
|
|
509
|
+
</div>
|
|
510
|
+
<figcaption>
|
|
511
|
+
<b>{L('On', 'Trên')}</b> {r.bgs.map((b) => <code class="c-pair-token">{b}</code>)}
|
|
512
|
+
{r.other.length > 0 && <span class="c-from">{L('Icons and borders', 'Icon và viền')}: {r.other.map((t) => (below(t, r.bgs) ? `${t} ⚠` : t)).join(', ')}</span>}
|
|
513
|
+
</figcaption>
|
|
514
|
+
</figure>
|
|
515
|
+
);
|
|
516
|
+
})}
|
|
517
|
+
</div>
|
|
518
|
+
)}
|
|
519
|
+
|
|
520
|
+
{kind === 'dodont' && (
|
|
521
|
+
<div class="c-dd">
|
|
522
|
+
<figure class="c-dd-panel is-do">
|
|
523
|
+
<figcaption><span aria-hidden="true">✓</span> {L('Do', 'Nên')}</figcaption>
|
|
524
|
+
<div class="c-dd-stage">
|
|
525
|
+
{item === 'yellow' && (
|
|
526
|
+
<div class="c-dd-tabs"><span class="c-dd-tab is-current">{L('Connections', 'Kết nối')}</span><span class="c-dd-tab">{L('Jobs', 'Tác vụ')}</span><span class="c-dd-tab">{L('Logs', 'Nhật ký')}</span></div>
|
|
527
|
+
)}
|
|
528
|
+
{item === 'meaning' && (<span class="bh-label is-danger">{L('Failed', 'Lỗi')}</span>)}
|
|
529
|
+
{item === 'alone' && (<span class="bh-state is-danger"><DangerIcon size={16} />{L('Failed', 'Lỗi')}</span>)}
|
|
530
|
+
{item === 'tokens' && (<code class="c-dd-code">color: var(--bh-color-text-subtle);</code>)}
|
|
531
|
+
{item === 'primary' && (<span class="c-dd-row"><span class="bh-button bh-button-primary">{L('Save changes', 'Lưu thay đổi')}</span><span class="bh-button">{L('Cancel', 'Huỷ')}</span></span>)}
|
|
532
|
+
{item === 'pairing' && (<span class="c-dd-fill">{L('Sync failed', 'Đồng bộ lỗi')}<small>{L('3 orders skipped', 'Bỏ qua 3 đơn hàng')}</small></span>)}
|
|
533
|
+
{item === 'warning' && (<span class="bh-label is-warning">{L('Partial', 'Một phần')}</span>)}
|
|
534
|
+
</div>
|
|
535
|
+
<p>
|
|
536
|
+
{item === 'yellow' && L('Graphite text; yellow only for the current-page bar.', 'Chữ xám ghi; màu vàng chỉ cho vạch chỉ trang hiện tại.')}
|
|
537
|
+
{item === 'meaning' && L('Red for something that failed.', 'Màu đỏ cho việc bị lỗi.')}
|
|
538
|
+
{item === 'alone' && L('Say the status in words, with an icon.', 'Ghi trạng thái bằng chữ, kèm icon.')}
|
|
539
|
+
{item === 'tokens' && L('Use the role token for what you are painting.', 'Dùng role token cho thứ bạn đang tô.')}
|
|
540
|
+
{item === 'primary' && L('One graphite primary button per view.', 'Mỗi màn hình một nút chính màu xám ghi.')}
|
|
541
|
+
{item === 'pairing' && L('text.on-bold on a solid fill: white in light, dark in dark.', 'text.on-bold trên nền đặc: trắng ở giao diện sáng, tối ở giao diện tối.')}
|
|
542
|
+
{item === 'warning' && L('Warnings are orange.', 'Cảnh báo dùng màu cam.')}
|
|
543
|
+
</p>
|
|
544
|
+
</figure>
|
|
545
|
+
<figure class="c-dd-panel is-dont">
|
|
546
|
+
<figcaption><span aria-hidden="true">✕</span> {L('Don’t', 'Không nên')}</figcaption>
|
|
547
|
+
<div class="c-dd-stage">
|
|
548
|
+
{item === 'yellow' && (<span class="c-dd-yellow-text">{L('Sync now', 'Đồng bộ ngay')}</span>)}
|
|
549
|
+
{item === 'meaning' && (<span class="bh-label is-danger">{L('New', 'Mới')}</span>)}
|
|
550
|
+
{item === 'alone' && (<span class="c-dd-dot" aria-label={L('Red dot only', 'Chỉ có chấm đỏ')} />)}
|
|
551
|
+
{item === 'tokens' && (<code class="c-dd-code">color: #5f5e5c;<br />color: var(--bh-color-graphite-11);</code>)}
|
|
552
|
+
{item === 'primary' && (<span class="c-dd-row"><span class="bh-button bh-button-primary">{L('Save', 'Lưu')}</span><span class="bh-button bh-button-primary">{L('Publish', 'Xuất bản')}</span><span class="c-dd-yellow-button">{L('Export', 'Xuất file')}</span></span>)}
|
|
553
|
+
{item === 'pairing' && (<span class="c-dd-fill is-wrong">{L('Sync failed', 'Đồng bộ lỗi')}<small>{L('3 orders skipped', 'Bỏ qua 3 đơn hàng')}</small></span>)}
|
|
554
|
+
{item === 'warning' && (<span class="c-dd-yellow-label">{L('Partial', 'Một phần')}</span>)}
|
|
555
|
+
</div>
|
|
556
|
+
<p>
|
|
557
|
+
{item === 'yellow' && L('Yellow text or links: 1.85:1 on white, unreadable.', 'Chữ hoặc link màu vàng: 1.85:1 trên nền trắng, không đọc được.')}
|
|
558
|
+
{item === 'meaning' && L('A status colour used for decoration or emphasis.', 'Dùng màu trạng thái để trang trí hoặc nhấn mạnh.')}
|
|
559
|
+
{item === 'alone' && L('A coloured dot with no words.', 'Một chấm màu, không có chữ.')}
|
|
560
|
+
{item === 'tokens' && L('A hex value or a scale step in product code.', 'Mã hex hoặc một bậc của thang màu trong code sản phẩm.')}
|
|
561
|
+
{item === 'primary' && L('Several primary buttons, or a yellow button.', 'Nhiều nút chính, hoặc một nút màu vàng.')}
|
|
562
|
+
{item === 'pairing' && L('Subtle or coloured text on a solid fill.', 'Chữ phụ hoặc chữ màu trên nền đặc.')}
|
|
563
|
+
{item === 'warning' && L('A yellow warning: it reads as the brand.', 'Cảnh báo màu vàng: trông như màu thương hiệu.')}
|
|
564
|
+
</p>
|
|
565
|
+
</figure>
|
|
566
|
+
</div>
|
|
567
|
+
)}
|
|
568
|
+
|
|
569
|
+
{kind === 'example' && (
|
|
570
|
+
<figure class="c-ex">
|
|
571
|
+
<ColorExample markers />
|
|
572
|
+
<figcaption>
|
|
573
|
+
<ol class="c-ex-legend">
|
|
574
|
+
<li>Card: <code>surface</code>, {L('edge', 'viền')} <code>border</code>; {L('title', 'tiêu đề')} <code>text</code></li>
|
|
575
|
+
<li>{L('Primary button', 'Nút chính')}: <code>background.neutral.bold</code>, <code>text.inverse</code></li>
|
|
576
|
+
<li>{L('Secondary text', 'Chữ phụ')}: <code>text.subtle</code></li>
|
|
577
|
+
<li>{L('Status label', 'Nhãn trạng thái')}: <code>border.success</code>, <code>text.success</code></li>
|
|
578
|
+
<li>{vi ? <>Dòng được chọn: <code>background.selected</code> và vạch <code>border.selected</code></> : <>Selected row: <code>background.selected</code> and the <code>border.selected</code> bar</>}</li>
|
|
579
|
+
<li>Link: <code>link</code></li>
|
|
580
|
+
</ol>
|
|
581
|
+
</figcaption>
|
|
582
|
+
</figure>
|
|
583
|
+
)}
|
|
584
|
+
|
|
585
|
+
{kind === 'balance' && (
|
|
586
|
+
<figure class="c-balance">
|
|
587
|
+
<div class="c-balance-bar" aria-hidden="true">
|
|
588
|
+
<span style="flex: 55; background: var(--bh-color-surface-page)" />
|
|
589
|
+
<span style="flex: 18; background: var(--bh-color-surface-subtle)" />
|
|
590
|
+
<span style="flex: 15; background: var(--bh-color-background-neutral-bold)" />
|
|
591
|
+
<span style="flex: 5; background: var(--bh-color-border)" />
|
|
592
|
+
<span style="flex: 3; background: var(--bh-color-background-brand-bold)" />
|
|
593
|
+
<span style="flex: 1; background: var(--bh-color-background-info-bold)" />
|
|
594
|
+
<span style="flex: 1; background: var(--bh-color-background-success-bold)" />
|
|
595
|
+
<span style="flex: 1; background: var(--bh-color-background-warning-bold)" />
|
|
596
|
+
<span style="flex: 1; background: var(--bh-color-background-danger-bold)" />
|
|
597
|
+
</div>
|
|
598
|
+
<figcaption>{L('A typical screen, roughly: mostly white and light grey, graphite for text and the primary button, a touch of yellow for the brand, and status colours only where there is a status.', 'Tỉ lệ màu ước chừng trên một màn hình thường gặp: phần lớn là trắng và xám nhạt, xám ghi cho chữ và nút chính, một chút vàng cho thương hiệu, và màu trạng thái chỉ ở nơi có trạng thái.')}</figcaption>
|
|
599
|
+
</figure>
|
|
600
|
+
)}
|
|
601
|
+
|
|
602
|
+
{kind === 'anatomy' && (
|
|
603
|
+
<figure class="c-anat">
|
|
604
|
+
<div class="c-anat-row">
|
|
605
|
+
<span class="c-anat-part"><code>color</code><small>{L('Foundation', 'Nền tảng')}</small></span><span class="c-anat-dot">.</span>
|
|
606
|
+
<span class="c-anat-part is-prop"><code>background</code><small>{L('Property', 'Thứ được tô')}</small></span><span class="c-anat-dot">.</span>
|
|
607
|
+
<span class="c-anat-part is-role"><code>danger</code><small>{L('Role', 'Vai trò')}</small></span><span class="c-anat-dot">.</span>
|
|
608
|
+
<span class="c-anat-part is-emph"><code>bold</code><small>{L('Emphasis', 'Mức nhấn')}</small></span><span class="c-anat-dot">.</span>
|
|
609
|
+
<span class="c-anat-part is-state"><code>hover</code><small>{L('State', 'Trạng thái')}</small></span>
|
|
610
|
+
</div>
|
|
611
|
+
<div class="c-anat-row">
|
|
612
|
+
<span class="c-anat-part"><code>color</code><small>{L('Foundation', 'Nền tảng')}</small></span><span class="c-anat-dot">.</span>
|
|
613
|
+
<span class="c-anat-part is-prop"><code>text</code><small>{L('Property', 'Thứ được tô')}</small></span><span class="c-anat-dot">.</span>
|
|
614
|
+
<span class="c-anat-part is-skip"><code>neutral</code><small>{L('left out', 'bỏ đi')}</small></span>
|
|
615
|
+
<span class="c-anat-part is-emph"><code>subtle</code><small>{L('Emphasis', 'Mức nhấn')}</small></span>
|
|
616
|
+
</div>
|
|
617
|
+
<figcaption>{L('The solid danger fill on hover, and secondary text. A part that is the default is left out.', 'Nền đặc màu nguy hiểm khi hover, và chữ phụ. Phần nào là mặc định thì bỏ đi.')}</figcaption>
|
|
618
|
+
</figure>
|
|
619
|
+
)}
|
|
620
|
+
|
|
621
|
+
{kind === 'surfaces' && (
|
|
622
|
+
<div class="c-surf">
|
|
623
|
+
<figure>
|
|
624
|
+
<div class="c-surf-page">
|
|
625
|
+
<span class="c-tag">surface.page</span>
|
|
626
|
+
<div class="c-surf-side"><span class="c-tag">surface.subtle</span></div>
|
|
627
|
+
<div class="c-surf-card"><span class="c-tag">surface</span><span class="c-tag">border</span>
|
|
628
|
+
<span class="c-surf-tip">surface.inverse</span>
|
|
629
|
+
</div>
|
|
630
|
+
</div>
|
|
631
|
+
<figcaption>{L('Page, sidebar, card and a tooltip.', 'Trang, sidebar, card và một tooltip.')}</figcaption>
|
|
632
|
+
</figure>
|
|
633
|
+
<figure>
|
|
634
|
+
<div class="c-surf-page is-dimmed">
|
|
635
|
+
<div class="c-surf-scrim"><span class="c-tag is-on-dark">surface.scrim</span></div>
|
|
636
|
+
<div class="c-surf-dialog"><b>{L('Disconnect this connection?', 'Ngắt kết nối này?')}</b><span>{L('Syncing stops until you connect again.', 'Việc đồng bộ dừng lại cho tới khi bạn kết nối lại.')}</span><span class="c-dd-row"><span class="bh-button">{L('Cancel', 'Huỷ')}</span><span class="bh-button bh-button-danger">{L('Disconnect', 'Ngắt kết nối')}</span></span></div>
|
|
637
|
+
</div>
|
|
638
|
+
<figcaption>{L('A dialog on the scrim: one layer, behind dialogs and drawers only.', 'Dialog trên lớp phủ scrim: một lớp, chỉ nằm sau dialog và drawer.')}</figcaption>
|
|
639
|
+
</figure>
|
|
640
|
+
</div>
|
|
641
|
+
)}
|
|
642
|
+
|
|
643
|
+
{kind === 'states' && (
|
|
644
|
+
<div class="table-wrap">
|
|
645
|
+
<table class="c-states">
|
|
646
|
+
<thead><tr><th scope="col"></th><th scope="col">{L('Rest', 'Nghỉ')}</th><th scope="col">Hover</th><th scope="col">{L('Pressed', 'Nhấn')}</th><th scope="col">Focus</th><th scope="col">{L('Disabled', 'Vô hiệu')}</th></tr></thead>
|
|
647
|
+
<tbody>
|
|
648
|
+
<tr>
|
|
649
|
+
<th scope="row">{L('Primary', 'Nút chính')}</th>
|
|
650
|
+
<td><span class="bh-button bh-button-primary">{L('Save', 'Lưu')}</span></td>
|
|
651
|
+
<td><span class="bh-button bh-button-primary" style="background: var(--bh-color-background-neutral-bold-hover); border-color: var(--bh-color-background-neutral-bold-hover)">{L('Save', 'Lưu')}</span></td>
|
|
652
|
+
<td><span class="bh-button bh-button-primary" style="background: var(--bh-color-background-neutral-bold-pressed); border-color: var(--bh-color-background-neutral-bold-pressed)">{L('Save', 'Lưu')}</span></td>
|
|
653
|
+
<td><span class="bh-button bh-button-primary c-focus">{L('Save', 'Lưu')}</span></td>
|
|
654
|
+
<td><span class="bh-button c-disabled">{L('Save', 'Lưu')}</span></td>
|
|
655
|
+
</tr>
|
|
656
|
+
<tr>
|
|
657
|
+
<th scope="row">{L('Default', 'Nút mặc định')}</th>
|
|
658
|
+
<td><span class="bh-button">{L('Cancel', 'Huỷ')}</span></td>
|
|
659
|
+
<td><span class="bh-button" style="background: var(--bh-color-background-neutral-subtle-hover)">{L('Cancel', 'Huỷ')}</span></td>
|
|
660
|
+
<td><span class="bh-button" style="background: var(--bh-color-background-neutral-subtle-pressed)">{L('Cancel', 'Huỷ')}</span></td>
|
|
661
|
+
<td><span class="bh-button c-focus">{L('Cancel', 'Huỷ')}</span></td>
|
|
662
|
+
<td><span class="bh-button c-disabled">{L('Cancel', 'Huỷ')}</span></td>
|
|
663
|
+
</tr>
|
|
664
|
+
<tr>
|
|
665
|
+
<th scope="row">{L('Row', 'Dòng')}</th>
|
|
666
|
+
<td><span class="c-state-row">{L('Store', 'Cửa hàng')}</span></td>
|
|
667
|
+
<td><span class="c-state-row" style="background: var(--bh-color-background-neutral-subtle-hover)">{L('Store', 'Cửa hàng')}</span></td>
|
|
668
|
+
<td><span class="c-state-row" style="background: var(--bh-color-background-neutral-subtle-pressed)">{L('Store', 'Cửa hàng')}</span></td>
|
|
669
|
+
<td><span class="c-state-row c-focus">{L('Store', 'Cửa hàng')}</span></td>
|
|
670
|
+
<td><span class="c-state-row is-selected">{L('Selected', 'Được chọn')}</span></td>
|
|
671
|
+
</tr>
|
|
672
|
+
</tbody>
|
|
673
|
+
</table>
|
|
674
|
+
<ul class="c-state-key">
|
|
675
|
+
<li><b>{L('Primary', 'Nút chính')}</b> <code>background.neutral.bold</code> → <code>.hover</code> → <code>.pressed</code></li>
|
|
676
|
+
<li><b>{L('Default, row', 'Nút mặc định, dòng')}</b> <code>surface</code> → <code>background.neutral.subtle.hover</code> → <code>.pressed</code></li>
|
|
677
|
+
{vi
|
|
678
|
+
? <li><b>Focus</b> <code>border.focus</code>, dày 2 px, cách 2 px · <b>Vô hiệu</b> <code>background.disabled</code>, <code>text.disabled</code> · <b>Được chọn</b> <code>background.selected</code>, vạch <code>border.selected</code></li>
|
|
679
|
+
: <li><b>Focus</b> <code>border.focus</code>, 2 px, 2 px away · <b>Disabled</b> <code>background.disabled</code>, <code>text.disabled</code> · <b>Selected</b> <code>background.selected</code>, <code>border.selected</code> bar</li>}
|
|
680
|
+
</ul>
|
|
681
|
+
</div>
|
|
682
|
+
)}
|
|
683
|
+
|
|
684
|
+
{kind === 'banners' && (
|
|
685
|
+
<div class="c-banners">
|
|
686
|
+
{(vi
|
|
687
|
+
? [['info', 'Đang đồng bộ 1.240 đơn hàng từ cửa hàng online.'], ['success', 'Đã đồng bộ mọi đơn hàng.'], ['warning', 'Token ERP hết hạn sau 3 ngày. Hãy kết nối lại để tiếp tục đồng bộ.'], ['danger', 'Đồng bộ lỗi: ứng dụng kế toán từ chối thông tin đăng nhập.']]
|
|
688
|
+
: [['info', 'Syncing 1,240 orders from the online store.'], ['success', 'All orders synced.'], ['warning', 'The ERP token expires in 3 days. Reconnect to keep syncing.'], ['danger', 'Sync failed: the accounting app rejected the credentials.']]).map(([r, msg]) => { const BannerIcon = icon[r]; return (
|
|
689
|
+
<div class={`c-banner is-${r}`}><span class="c-banner-icon"><BannerIcon size={16} /></span><span>{msg}</span></div>
|
|
690
|
+
); })}
|
|
691
|
+
<p class="c-from">Banner: <code>background.{'{'}role{'}'}.subtle</code>, {L('edge', 'viền')} <code>border.{'{'}role{'}'}.subtle</code>, icon <code>icon.{'{'}role{'}'}</code>, {L('text', 'chữ')} <code>text</code>.</p>
|
|
692
|
+
</div>
|
|
693
|
+
)}
|
|
694
|
+
|
|
695
|
+
{kind === 'steps' && (
|
|
696
|
+
<figure class="c-steps-ill">
|
|
697
|
+
{['graphite', 'blue'].map((sc) => (
|
|
698
|
+
<div class="c-steps-row">
|
|
699
|
+
<b>{sc === 'graphite' ? 'Graphite' : 'Blue'}</b>
|
|
700
|
+
{stepUse(sc).map(({ step, v }) => (
|
|
701
|
+
<div class="c-step-cell">
|
|
702
|
+
{step <= 2 && <span class="c-step-bg" style={`background: ${v}`} />}
|
|
703
|
+
{step >= 3 && step <= 5 && <span class="c-step-pill" style={`background: ${v}`}>Tag</span>}
|
|
704
|
+
{step >= 6 && step <= 8 && <span class="c-step-line" style={`border-color: ${v}`} />}
|
|
705
|
+
{step >= 9 && step <= 10 && <span class="c-step-fill" style={`background: ${v}`}>{L('Go', 'Đi')}</span>}
|
|
706
|
+
{step >= 11 && <span class="c-step-text" style={`color: ${v}`}>Aa</span>}
|
|
707
|
+
<small>{step}</small>
|
|
708
|
+
</div>
|
|
709
|
+
))}
|
|
710
|
+
</div>
|
|
711
|
+
))}
|
|
712
|
+
<figcaption>{L('1–2 backgrounds · 3–5 component fills at rest, hover, pressed · 6–8 borders · 9–10 solid fills · 11–12 text.', '1–2 nền · 3–5 nền component khi nghỉ, hover, nhấn · 6–8 viền · 9–10 nền đặc · 11–12 chữ.')}</figcaption>
|
|
713
|
+
</figure>
|
|
714
|
+
)}
|
|
715
|
+
|
|
716
|
+
{kind === 'themes' && (
|
|
717
|
+
<div class="c-themes">
|
|
718
|
+
<figure><div class="c-theme-stage" style={`${themeStyle('light')}; background: ${tok('surface.page').light}`}><ColorExample style={themeStyle('light')} /></div><figcaption>{L('Light', 'Sáng')}</figcaption></figure>
|
|
719
|
+
<figure><div class="c-theme-stage" style={`${themeStyle('dark')}; background: ${tok('surface.page').dark}`}><ColorExample style={themeStyle('dark')} /></div><figcaption>{L('Dark: the same tokens, no extra code', 'Tối: cùng token, không thêm code')}</figcaption></figure>
|
|
720
|
+
</div>
|
|
721
|
+
)}
|
|
722
|
+
|
|
723
|
+
{kind === 'contrast' && (
|
|
724
|
+
<div class="c-contrast">
|
|
725
|
+
{contrastSamples.map((c) => (
|
|
726
|
+
<figure class={c.pass ? 'is-pass' : 'is-fail'}>
|
|
727
|
+
<div class="c-contrast-chip" style={c.min === 3 ? `background: ${c.bgVal}` : `background: ${c.bgVal}; color: ${c.fgVal}`}>
|
|
728
|
+
{c.min === 3 ? <span class="c-contrast-box" style={`border-color: ${c.fgVal}`} /> : L('Synced 2 min ago', 'Đồng bộ 2 phút trước')}
|
|
729
|
+
</div>
|
|
730
|
+
<figcaption><b>{c.pass ? L('Pass', 'Đạt') : L('Fail', 'Không đạt')} {ratio(c.r)}</b> {c.text} ({L('needs', 'cần')} {c.min}:1)</figcaption>
|
|
731
|
+
</figure>
|
|
732
|
+
))}
|
|
733
|
+
</div>
|
|
734
|
+
)}
|
|
735
|
+
|
|
736
|
+
{kind === 'palette' && (
|
|
737
|
+
<div class="c-pal-set">
|
|
738
|
+
{(['light', 'dark'] as const).map((mode) => (
|
|
739
|
+
<figure class={`c-pal is-${mode}`} style={`background: ${pageOf(mode)}; color: ${mode === 'light' ? INK_DARK : '#eeeeed'}`}>
|
|
740
|
+
<figcaption>{mode === 'light' ? L('Light theme', 'Giao diện sáng') : L('Dark theme', 'Giao diện tối')}</figcaption>
|
|
741
|
+
<div class="c-pal-grid">
|
|
742
|
+
{scales.map((sc) => (
|
|
743
|
+
<div class="c-pal-col">
|
|
744
|
+
<b>{sc.name}</b>
|
|
745
|
+
{sc.steps.map((t, i) => {
|
|
746
|
+
const v = t[mode];
|
|
747
|
+
return (
|
|
748
|
+
<div class="c-pal-sw" style={`background: ${v}; color: ${ink(v)}`} title={`color.${sc.id}.${i + 1}`}>
|
|
749
|
+
<span>{i + 1}{brandStep[mode][sc.id] === i + 1 && <em>{L('brand', 'thương hiệu')}</em>}</span>
|
|
750
|
+
<span class="c-num">{up(v).slice(1)}</span>
|
|
751
|
+
</div>
|
|
752
|
+
);
|
|
753
|
+
})}
|
|
754
|
+
</div>
|
|
755
|
+
))}
|
|
756
|
+
</div>
|
|
757
|
+
</figure>
|
|
758
|
+
))}
|
|
759
|
+
</div>
|
|
760
|
+
)}
|
|
761
|
+
|
|
762
|
+
{kind === 'ramps' && (
|
|
763
|
+
<div class="c-ramps">
|
|
764
|
+
{(['light', 'dark'] as const).map((mode) => (
|
|
765
|
+
<figure class="c-ramp-card" style={`background: ${pageOf(mode)}`}>
|
|
766
|
+
{scales.map((sc) => (
|
|
767
|
+
<div class="c-ramp" title={sc.name}>
|
|
768
|
+
{sc.steps.map((t) => <span style={`background: ${t[mode]}`} />)}
|
|
769
|
+
</div>
|
|
770
|
+
))}
|
|
771
|
+
<figcaption style={`color: ${mode === 'light' ? INK_DARK : '#eeeeed'}`}>{mode === 'light' ? L('Light', 'Sáng') : L('Dark', 'Tối')}: {L('graphite, yellow, blue, green, orange, red', 'xám ghi, vàng, xanh dương, xanh lá, cam, đỏ')}</figcaption>
|
|
772
|
+
</figure>
|
|
773
|
+
))}
|
|
774
|
+
</div>
|
|
775
|
+
)}
|
|
776
|
+
|
|
777
|
+
{kind === 'emphasis' && (
|
|
778
|
+
<figure class="c-emph">
|
|
779
|
+
<div class="c-emph-grid">
|
|
780
|
+
<span /><b>{L('Subtle', 'Nhạt')}</b><b>{L('Default', 'Mặc định')}</b><b>{L('Bold', 'Đậm')}</b>
|
|
781
|
+
{emphasisRows.map((row) => (
|
|
782
|
+
<>
|
|
783
|
+
<b>{row.name}</b>
|
|
784
|
+
{row.fills.map((f) => f
|
|
785
|
+
? <span class="c-emph-fill" style={`background: var(${f.bg.cssVar}); color: var(${f.fg.cssVar})`} title={f.bg.name.replace('color.', '')}>{f.bg.name.replace('color.background.', '')}</span>
|
|
786
|
+
: <span class="c-emph-none">–</span>)}
|
|
787
|
+
</>
|
|
788
|
+
))}
|
|
789
|
+
</div>
|
|
790
|
+
{vi
|
|
791
|
+
? <figcaption>Nhạt cho vùng lớn và dòng, đậm cho một thứ nhỏ cần nổi bật. Chữ trên mỗi nền là cặp của nền đó: chữ theo vai trò trên nền nhạt, <code>text.on-bold</code> trên nền đậm.</figcaption>
|
|
792
|
+
: <figcaption>Subtle for large areas and rows, bold for one small thing that must stand out. Text on each fill is its pair: role text on subtle, <code>text.on-bold</code> on bold.</figcaption>}
|
|
793
|
+
</figure>
|
|
794
|
+
)}
|