@beehexa/design-rules 2611.3.2 → 2611.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +1 -1
  2. package/dist/AGENTS.md +1 -7
  3. package/dist/catalog.json +153 -125
  4. package/dist/docs/scripts/color-pairs.mjs +5 -1
  5. package/dist/docs/scripts/token-utils.mjs +16 -1
  6. package/dist/docs/src/components/ColorDemo.astro +5 -5
  7. package/dist/docs/src/components/ElevationDemo.astro +196 -0
  8. package/dist/docs/src/components/LayoutDemo.astro +1 -1
  9. package/dist/docs/src/components/OpacityDemo.astro +95 -0
  10. package/dist/docs/src/components/ShapeDemo.astro +11 -10
  11. package/dist/docs/src/components/ShapePin.astro +5 -3
  12. package/dist/docs/src/components/ZIndexDemo.astro +102 -0
  13. package/dist/docs/src/content/foundations/styles/border.mdx +3 -3
  14. package/dist/docs/src/content/foundations/styles/color/usage.mdx +11 -4
  15. package/dist/docs/src/content/foundations/styles/data-visualization.mdx +74 -6
  16. package/dist/docs/src/content/foundations/styles/elevation.mdx +66 -3
  17. package/dist/docs/src/content/foundations/styles/icons/usage.mdx +9 -6
  18. package/dist/docs/src/content/foundations/styles/layout/app-frame.mdx +10 -0
  19. package/dist/docs/src/content/foundations/styles/opacity.mdx +43 -5
  20. package/dist/docs/src/content/foundations/styles/size-and-density.mdx +7 -0
  21. package/dist/docs/src/content/foundations/styles/typography/code.mdx +1 -1
  22. package/dist/docs/src/content/foundations/styles/z-index.mdx +63 -5
  23. package/dist/docs/src/content/tools/ant-design-vue-theme.mdx +125 -18
  24. package/dist/docs/src/content/tools/application-content.mdx +42 -0
  25. package/dist/docs/src/content/tools/editors.mdx +27 -0
  26. package/dist/docs/src/content/tools/mermaid.mdx +95 -0
  27. package/dist/docs/src/content/tools/vueflow.mdx +121 -0
  28. package/dist/docs/src/content-vi/foundations/styles/border.mdx +3 -3
  29. package/dist/docs/src/content-vi/foundations/styles/color/usage.mdx +11 -4
  30. package/dist/docs/src/content-vi/foundations/styles/data-visualization.mdx +75 -6
  31. package/dist/docs/src/content-vi/foundations/styles/elevation.mdx +75 -0
  32. package/dist/docs/src/content-vi/foundations/styles/icons/usage.mdx +9 -6
  33. package/dist/docs/src/content-vi/foundations/styles/layout/app-frame.mdx +10 -0
  34. package/dist/docs/src/content-vi/foundations/styles/opacity.mdx +43 -5
  35. package/dist/docs/src/content-vi/foundations/styles/size-and-density.mdx +7 -0
  36. package/dist/docs/src/content-vi/foundations/styles/typography/code.mdx +1 -1
  37. package/dist/docs/src/content-vi/foundations/styles/z-index.mdx +63 -5
  38. package/dist/docs/src/lib/token-descriptions-vi.ts +21 -5
  39. package/dist/docs/src/styles/chrome.css +14 -14
  40. package/dist/docs/src/styles/color.css +19 -32
  41. package/dist/docs/src/styles/docs.css +18 -34
  42. package/dist/docs/src/styles/elevation.css +83 -0
  43. package/dist/docs/src/styles/global.css +1 -10
  44. package/dist/docs/src/styles/icons.css +11 -15
  45. package/dist/docs/src/styles/kit.css +112 -0
  46. package/dist/docs/src/styles/layout.css +1 -6
  47. package/dist/docs/src/styles/opacity.css +43 -0
  48. package/dist/docs/src/styles/shapes.css +15 -19
  49. package/dist/docs/src/styles/tokens.css +1282 -3
  50. package/dist/docs/src/styles/tokens.js +358 -3
  51. package/dist/docs/src/styles/typography.css +4 -7
  52. package/dist/docs/src/styles/zindex.css +56 -0
  53. package/dist/docs/tokens/border.json +1 -1
  54. package/dist/docs/tokens/color.json +57 -3
  55. package/dist/docs/tokens/content.json +19 -0
  56. package/dist/docs/tokens/diagram.json +48 -0
  57. package/dist/docs/tokens/editor.json +129 -0
  58. package/dist/docs/tokens/elevation.json +15 -5
  59. package/dist/docs/tokens/flow.json +497 -0
  60. package/dist/docs/tokens/opacity.json +9 -0
  61. package/dist/docs/tokens/resize.json +67 -0
  62. package/dist/docs/tokens/size.json +6 -0
  63. package/dist/docs/tokens/spacing.json +4 -4
  64. package/dist/docs/tokens/typography.json +5 -0
  65. package/dist/docs/tokens/visualization.json +123 -0
  66. package/dist/docs/tokens/z-index.json +49 -0
  67. package/dist/index.js +153 -125
  68. package/package.json +6 -1
@@ -0,0 +1,102 @@
1
+ ---
2
+ // Illustrations for the Z-index page. Each layer in the figures uses its own z-index token, and its
3
+ // surface and shadow come from the Elevation page, so the drawings stack the way the product does.
4
+ import '../styles/layout.css';
5
+ import '../styles/shapes.css';
6
+ import '../styles/elevation.css';
7
+ import '../styles/zindex.css';
8
+ import { langOf } from '../lib/i18n';
9
+ import ChevronDown from '@lucide/astro/icons/chevron-down';
10
+ import CircleHelp from '@lucide/astro/icons/circle-question-mark';
11
+ import Info from '@lucide/astro/icons/info';
12
+ import CircleAlert from '@lucide/astro/icons/circle-alert';
13
+
14
+ interface Props {
15
+ kind: 'stack' | 'screen' | 'dodont';
16
+ /** dodont: 'toast' | 'inside' | 'scrim'. */
17
+ item?: string;
18
+ }
19
+ const { kind, item } = Astro.props;
20
+ const vi = langOf(Astro.url.pathname) === 'vi';
21
+ const L = (en: string, viText: string) => (vi ? viText : en);
22
+ const layers = [
23
+ { id: 'sticky', v: 100, use: L('Sticky table header', 'Header bảng dính') },
24
+ { id: 'navigation', v: 200, use: L('App header, side navigation', 'Header app, menu bên trái') },
25
+ { id: 'overlay', v: 300, use: L('Menu, select list, popover', 'Menu, danh sách select, popover') },
26
+ { id: 'modal', v: 400, use: L('Dialog, drawer, scrim', 'Dialog, drawer, lớp phủ mờ') },
27
+ { id: 'spotlight', v: 500, use: L('Product tour', 'Tour hướng dẫn') },
28
+ { id: 'tooltip', v: 600, use: 'Tooltip' },
29
+ { id: 'toast', v: 700, use: L('Toast, skip link', 'Toast, skip link') },
30
+ ];
31
+ const rows = [['#10293', 'ERP', L('Synced', 'Đã đồng bộ')], ['#10294', 'ERP', L('Synced', 'Đã đồng bộ')], ['#10295', L('Accounting', 'Kế toán'), L('Failed', 'Lỗi')]];
32
+ const legend = [
33
+ ['1', 'z-index.navigation', L('App header', 'Header của app')],
34
+ ['2', 'z-index.sticky', L('Table header while the table scrolls', 'Header của bảng khi bảng cuộn')],
35
+ ['3', 'z-index.overlay', L('Menu opened from a button', 'Menu mở từ một nút')],
36
+ ['4', 'z-index.tooltip', L('Tooltip on an icon button', 'Tooltip trên nút icon')],
37
+ ['5', 'z-index.toast', L('Toast', 'Toast')],
38
+ ];
39
+ ---
40
+
41
+ {kind === 'stack' && (
42
+ <figure class="zi-stack-fig sh-root">
43
+ <div class="zi-stack" role="img" aria-label={L('Seven layers stacked from sticky at the bottom to toast at the top', 'Bảy lớp xếp chồng, từ sticky ở dưới cùng tới toast ở trên cùng')}>
44
+ <span class="zi-plate is-page"><b>Page</b><code>auto</code></span>
45
+ {layers.map((l, i) => (
46
+ <span class={`zi-plate is-${l.id}`} style={`--i: ${i + 1}; z-index: var(--bh-z-index-${l.id})`}><b>{l.id}</b><code>{l.v}</code><em>{l.use}</em></span>
47
+ ))}
48
+ </div>
49
+ <figcaption>{L('Each layer covers the ones below it. The step is 100, so a new layer can go between two others.', 'Mỗi lớp che các lớp bên dưới. Các lớp cách nhau 100, nên có thể chèn lớp mới vào giữa hai lớp.')}</figcaption>
50
+ </figure>
51
+ )}
52
+
53
+ {kind === 'screen' && (
54
+ <div class="zi-screen-wrap sh-root">
55
+ <div class="zi-screen" role="img" aria-label={L('A sync log screen with an app header, a sticky table header, an open menu, a tooltip and a toast, each numbered', 'Màn hình nhật ký đồng bộ có header app, header bảng dính, menu đang mở, tooltip và toast, mỗi phần được đánh số')}>
56
+ <div class="zi-head"><span class="zi-n">1</span><b>HexaSync</b><span class="zi-head-r"><span class="zi-icon" aria-label="Help"><CircleHelp size={16} aria-hidden="true" /></span></span></div>
57
+ <div class="zi-body">
58
+ <div class="zi-toolbar"><span class="sp-btn is-small el-btn-icon">{L('Actions', 'Thao tác')}<ChevronDown size={16} aria-hidden="true" /></span></div>
59
+ <div class="zi-table">
60
+ <div class="zi-th"><span class="zi-n">2</span><span>{L('Order', 'Đơn')}</span><span>{L('Destination', 'Đích')}</span><span>{L('Status', 'Trạng thái')}</span></div>
61
+ {rows.map((r) => <div class="zi-tr"><span>{r[0]}</span><span>{r[1]}</span><span>{r[2]}</span></div>)}
62
+ </div>
63
+ <span class="el-menu zi-menu"><span class="zi-n">3</span><span class="is-on">{L('Retry failed', 'Chạy lại đơn lỗi')}</span><span>{L('Export CSV', 'Xuất CSV')}</span></span>
64
+ <span class="zi-tip"><span class="zi-n">4</span>{L('Help', 'Trợ giúp')}</span>
65
+ <span class="el-toast is-info zi-toast"><span class="zi-n">5</span><Info size={16} aria-hidden="true" />{L('Retry started', 'Đã bắt đầu chạy lại')}</span>
66
+ </div>
67
+ </div>
68
+ <ol class="zi-legend">
69
+ {legend.map(([n, token, what]) => <li><span class="zi-n">{n}</span><code>{token}</code><span>{what}</span></li>)}
70
+ </ol>
71
+ </div>
72
+ )}
73
+
74
+ {kind === 'dodont' && (
75
+ <div class="sp-dd sh-root">
76
+ {['do', 'dont'].map((side) => (
77
+ <figure class={`sp-dd-panel is-${side}`}>
78
+ <figcaption><span aria-hidden="true">{side === 'do' ? '✓' : '✕'}</span> {side === 'do' ? L('Do', 'Nên') : L('Don’t', 'Không nên')}</figcaption>
79
+ <div class="sp-dd-stage zi-dd">
80
+ <div class={`zi-mini is-${item} is-${side}`} aria-hidden="true">
81
+ <span class="el-lines"><i></i><i></i><i></i></span>
82
+ <div class="el-scrim zi-mini-scrim"></div>
83
+ <div class="zi-mini-dialog">
84
+ <b>{L('New sync job', 'Tạo sync job')}</b>
85
+ {item === 'inside' && <span class="zi-field">{L('Destination', 'Đích')}<span class="zi-input">ERP<ChevronDown size={16} aria-hidden="true" /></span><span class="el-menu zi-mini-list"><span class="is-on">ERP</span><span>{L('Accounting', 'Kế toán')}</span><span>CRM</span><span>{L('Warehouse', 'Kho')}</span></span></span>}
86
+ {item !== 'inside' && <span class="zi-mini-text">{L('Choose a source and a destination.', 'Chọn nguồn và đích.')}</span>}
87
+ </div>
88
+ {item === 'toast' && <span class="el-toast is-danger zi-mini-toast"><CircleAlert size={16} aria-hidden="true" />{L('Connection lost', 'Mất kết nối')}</span>}
89
+ </div>
90
+ </div>
91
+ <p>
92
+ {item === 'toast' && side === 'do' && L('Give toasts z-index.toast. A toast stays readable while a dialog is open.', 'Cho toast dùng z-index.toast. Toast vẫn đọc được khi đang mở dialog.')}
93
+ {item === 'toast' && side === 'dont' && L('Don’t give a toast a lower value than the dialog. It ends up under the scrim.', 'Không cho toast giá trị thấp hơn dialog. Nó sẽ nằm dưới lớp phủ mờ.')}
94
+ {item === 'inside' && side === 'do' && L('Render a list that opens inside a dialog inside the dialog, so it can run past the dialog’s edge.', 'Danh sách mở ra bên trong dialog thì đặt luôn trong dialog, để nó có thể tràn qua mép dialog.')}
95
+ {item === 'inside' && side === 'dont' && L('Don’t let the dialog clip the list. Raising its z-index does not fix that.', 'Không để dialog cắt mất danh sách. Tăng z-index cũng không sửa được lỗi này.')}
96
+ {item === 'scrim' && side === 'do' && L('Put the scrim before the dialog. Both use z-index.modal, so the dialog draws on top.', 'Đặt lớp phủ mờ trước dialog. Cả hai dùng z-index.modal, nên dialog được vẽ lên trên.')}
97
+ {item === 'scrim' && side === 'dont' && L('Don’t put the scrim after the dialog. It dims the dialog too.', 'Không đặt lớp phủ mờ sau dialog. Nó sẽ làm mờ cả dialog.')}
98
+ </p>
99
+ </figure>
100
+ ))}
101
+ </div>
102
+ )}
@@ -36,7 +36,7 @@ The focus ring, in `color.border.focus`, drawn 2 px outside the element (`state.
36
36
 
37
37
  ### `border.width.accent` · 4 px
38
38
 
39
- A status or emphasis bar on one edge: alerts and banners, a table row with an error, callouts and quotes. The box keeps its radius on all four corners and the bar follows the curve.
39
+ A status or emphasis bar on one edge: a table row with an error, callouts and quotes. Banners don't use it; they use a tinted background (see [Color usage](/foundations/styles/color/usage/)). The box keeps its radius on all four corners and the bar follows the curve.
40
40
 
41
41
  <ShapeDemo kind="border-use" item="accent" />
42
42
 
@@ -60,13 +60,13 @@ Don't show a state with colour alone. Selected and focused also change the width
60
60
  ## Code
61
61
 
62
62
  ```css
63
- .alert {
63
+ .callout {
64
64
  position: relative;
65
65
  overflow: hidden;
66
66
  border: var(--bh-border-width-default) solid var(--bh-color-border);
67
67
  border-radius: var(--bh-radius-100);
68
68
  }
69
- .alert::before {
69
+ .callout::before {
70
70
  content: "";
71
71
  position: absolute;
72
72
  inset: 0 auto 0 0;
@@ -14,8 +14,9 @@ import ColorDemo from '../../../../components/ColorDemo.astro';
14
14
  | I need | Use |
15
15
  |---|---|
16
16
  | Page background | `surface.page` |
17
- | Card, panel, dialog | `surface`, edge `border` |
18
- | Sidebar, table header, quiet area | `surface.subtle` |
17
+ | Card, panel | `surface`, edge `border` |
18
+ | Menu, popover, dialog, drawer | `surface.overlay`; items inside use `surface.overlay.hover`, `.pressed`, `.selected` |
19
+ | Global header and sidebar (same shell surface), mobile navigation drawer, table header, quiet area | `surface.subtle` |
19
20
  | Body text, headings, labels | `text` |
20
21
  | Descriptions, secondary table columns, navigation | `text.subtle` |
21
22
  | Metadata, timestamps, helper text, breadcrumbs | `text.subtlest` |
@@ -34,7 +35,11 @@ import ColorDemo from '../../../../components/ColorDemo.astro';
34
35
  | Error under a field | `text.danger`, `icon.danger` |
35
36
  | Banner for a status | `background.{role}.subtle`, `border.{role}.subtle`, `text`, `icon.{role}` |
36
37
  | Status in a table row | A [label](#labels) |
37
- | Tooltip, toast | `surface.inverse`, `text.inverse` |
38
+ | Tooltip | `surface.inverse`, `text.inverse` |
39
+ | Toast | `surface.overlay`, `text`, `icon.{role}`, shadow `elevation.shadow.overlay`; no border |
40
+ | Switch | On: `background.neutral.bold`; off: `background.neutral`, edge `border.bold`; knob `surface` |
41
+ | Avatar with initials | `background.neutral`, `text` |
42
+ | Step number, count | `background.neutral.bold`, `text.inverse` |
38
43
  | Layer behind a dialog or drawer | `surface.scrim` |
39
44
  | Loading placeholder | `skeleton`, sheen `skeleton.subtle` |
40
45
  | Code block | `code.background`, `code.text` |
@@ -87,7 +92,9 @@ Warning is orange, not yellow: yellow is the brand. There is no sixth level; say
87
92
  |---|---|---|
88
93
  | Looks | Outline in the role's `border`, text in its `text` | Fill in the role's `bold`, text and icon in `text.on-bold` |
89
94
  | Size | 20 px, padding 6 px; large 24 px, 8 px | 32 px, padding 12 px |
90
- | Use | Many per screen: table rows, tags | One per page: the status the page is about |
95
+ | Use | Many per screen: status in table rows and lists | One per page: the status the page is about |
96
+
97
+ A tag is not a label. A tag is something people add or remove, such as a filter or a keyword: 24 px high, radius 4 px, `border` and `text`, with a remove button. Labels show a status; people can't remove them.
91
98
 
92
99
  ## Interaction states
93
100
 
@@ -2,10 +2,78 @@
2
2
  title: "Data visualization"
3
3
  description: "Colours, type and rules for charts, KPI tiles and other data displays."
4
4
  order: 13
5
- planned:
6
- - "A chart palette for series, with contrast in light and dark"
7
- - "Status colours in charts"
8
- - "Chart types and when to use each"
9
- - "Axis, legend, tooltip and number formats"
10
- - "Accessible charts: not by colour alone, text alternatives"
11
5
  ---
6
+
7
+ ## Choose by the question
8
+
9
+ Use a line chart for a value over time, a compact sparkline only beside its current
10
+ value, a proportional bar for parts of a stated total, and a timeline for when
11
+ executions happened. A health ring must also show its numeric score and a named
12
+ assessment. Do not use decorative area fills, gradients, glass or pulsing failures.
13
+
14
+ ## Metrics, categories and status are different
15
+
16
+ `visualization.metric.*` defines named operational series: total jobs, completed,
17
+ failed, success rate, active jobs, average execution time and queue depth. Volume
18
+ and duration alone are not warnings. Keep units, thresholds and metric names visible.
19
+
20
+ `visualization.job-type.*` defines queue-status, sync-history, polling-jobs and
21
+ generic-jobs as categorical identities, never as success/warning/failure. Their
22
+ foundation colour aliases adapt in light and dark. Always pair colour with the
23
+ job-type name or distinct glyph. In combined timeline mode, the outer mark encodes
24
+ job type and the inner mark encodes status; explain both in the legend. In status
25
+ mode, use `color.text.success`, `danger`, `info`, `warning` or neutral text by the
26
+ actual state. Preserve visible status labels and outcome icons.
27
+
28
+ `visualization.priority.1` through `.5` encode ranked queue urgency, always labelled
29
+ P1–P5. Priority is not an execution outcome. P1 is highest; P5 is lowest. Do not
30
+ reuse a priority colour to claim success or failure.
31
+
32
+ ## Geometry and typography
33
+
34
+ Use `visualization.line-width`, `sparkline-width`, `grid-width`, `point-radius`
35
+ and `hover-point-radius` for SVG series and markers. Strokes are CSS pixel
36
+ dimensions; use a measured viewBox or non-scaling strokes to avoid distortion.
37
+ `timeline-track-height` and `activity-track-height` set compact proportional tracks.
38
+ `ring-size` is a CSS dimension; `ring-width` is the Ant Design percent-based stroke
39
+ API, and `ring-gap` is degrees. Convert dimensions only at numeric API boundaries.
40
+ Data coordinates and measured container sizes remain data, not design tokens.
41
+
42
+ `timeline-node-size` and `timeline-node-inner-size` define the outer categorical and
43
+ inner status marks. Their `touch` and `tablet` variants preserve legibility without
44
+ changing the encoding. `timeline-cluster-size` sets an aggregate count mark; paint
45
+ its boundary with the category/status role and keep its count in normal text on a
46
+ normal surface, never white on a theme-adapted bright mark. Use `size.target.touch`
47
+ for the interactive footprint, independent of the visible mark.
48
+ `chart-height`, `chart-initial-width`, `sparkline-height`,
49
+ `sparkline-compact-height` and `sparkline-initial-width` define plotting viewports;
50
+ initial widths are replaced by measured widths. `sparkline-point-radius` is the
51
+ compact endpoint radius. These are not data scales or axis values.
52
+
53
+ Use `font.heading.large` with tabular figures for KPI values, `font.body.small` for
54
+ metadata, axes and tooltip copy, and `font.label.small` for legend labels.
55
+ Use `color.border` for decorative grids, `color.text.subtle` for labels and the
56
+ surface/inverse text pair for tooltips. A meaningful data mark needs at least 3:1
57
+ against its actual surface; normal text needs 4.5:1. Check actual theme pairs rather
58
+ than assuming an alias proves rendered contrast.
59
+
60
+ ## Labels, formats and interaction
61
+
62
+ Keep locale-aware number, timestamp and duration formatting consistent with the
63
+ adjacent table. Include units and time range; preserve timezone context. Never
64
+ silently turn a count into a rate or truncate a duration. Legends must name every
65
+ shown series; a selected series needs an indicator other than colour. Tooltips
66
+ must stay inside the chart container and must not be the only way to read values.
67
+ Provide an adjacent numeric summary or table for the same data and an accessible
68
+ chart name. Keyboard-operable legend controls retain visible focus.
69
+
70
+ ## Traffic assessment bands
71
+
72
+ `visualization.traffic.{healthy,attention,degraded,critical,unavailable,fault}.{fill,outline}`
73
+ provides paired band paints. Healthy means accepted traffic; attention, degraded and
74
+ critical describe increasing rejected-request ratio thresholds. State the thresholds
75
+ in labels or tooltips. Attention and degraded share warning colours: use a distinct
76
+ dash pattern and explicit labels, not colour alone. Unavailable means no requests
77
+ to assess. Fault means internal/backend request-processing errors, not a ratio band;
78
+ show a separate fault count, explicit label/icon and cross-hatching. Fetch failures remain errors, not a band. Never turn missing
79
+ data into zero or a healthy result. Use normal `color.text` for text on subtle fills.
@@ -1,12 +1,75 @@
1
1
  ---
2
2
  title: Elevation
3
- description: Shadows that show which surfaces sit above others.
3
+ description: "How far a surface seems to sit above the page. It sets items apart, marks what is temporary and points attention at what matters now."
4
4
  order: 8
5
5
  ---
6
+ import ElevationDemo from '../../../components/ElevationDemo.astro';
6
7
  import TokenTable from '../../../components/TokenTable.astro';
7
8
 
8
- - `elevation.shadow.raised`: cards that sit on the page.
9
- - `elevation.shadow.overlay`: dropdowns, popovers and dialogs that float above content.
9
+ Elevation is the distance a surface seems to have from the page, shown with a shadow and, in dark mode, a lighter background. It tells people what stands apart from the page, what is temporary, and what needs attention first.
10
+
11
+ <ElevationDemo kind="hero" />
12
+
13
+ ## How it works
14
+
15
+ - **Each level is a pair: a background and a shadow.** Use the two tokens of a level together.
16
+ - **The further from the page, the larger and softer the shadow.** A card has a faint shadow; a dialog a deep one.
17
+ - **Three levels only.** If everything floats, nothing stands out.
18
+
19
+ Elevation shows how high a surface looks. [Z-index](/foundations/styles/z-index/) decides which surface is drawn on top.
20
+
21
+ | Level | Background | Shadow | Use for |
22
+ |---|---|---|---|
23
+ | Page | `color.surface.page`, `color.surface.subtle` | none | The page, panels, tables, side navigation, headers |
24
+ | Raised | `color.surface` | `elevation.shadow.raised` | Cards |
25
+ | Overlay | `color.surface.overlay` | `elevation.shadow.overlay` | Menus, select lists, popovers, date pickers, toasts |
26
+ | Modal | `color.surface.overlay` | `elevation.shadow.modal` | Dialogs, drawers, full sheets |
27
+
28
+ ## Page
29
+
30
+ Most of the screen sits on the page with no shadow. Separate panels and tables with a `color.border` border. Side navigation and table headers use `color.surface.subtle`.
31
+
32
+ <ElevationDemo kind="level" item="page" />
33
+
34
+ ## Raised
35
+
36
+ For one item people treat as a unit: a sync job, a connection, a dashboard widget. Cards keep their border, because a faint shadow can disappear on a bright screen or in high-contrast mode.
37
+
38
+ <ElevationDemo kind="level" item="raised" />
39
+
40
+ ## Overlay
41
+
42
+ For things that open from a control and close again: menus, select lists, popovers. Toasts use the same surface and shadow.
43
+
44
+ <ElevationDemo kind="level" item="overlay" />
45
+
46
+ ## Modal
47
+
48
+ For dialogs and drawers that block the page until people finish or cancel. A modal always sits on `color.surface.scrim`, which dims the page and shows it is unavailable.
49
+
50
+ <ElevationDemo kind="level" item="modal" />
51
+
52
+ ## Dark mode
53
+
54
+ **The higher the surface, the lighter it is.** A dark shadow barely shows on a dark page, so each level also gets a lighter background: page graphite 1, cards graphite 2, menus and dialogs graphite 3. The token names are the same in both themes.
55
+
56
+ <ElevationDemo kind="dark" />
57
+
58
+ ## Inside menus and dialogs
59
+
60
+ Rows inside a floating surface use the overlay state colours, such as `color.surface.overlay.hover`. The page's hover colour would match the menu background in dark mode and disappear. Mark a selected row with a check, not colour alone.
61
+
62
+ <ElevationDemo kind="states" />
63
+
64
+ ## Do and don’t
65
+
66
+ <ElevationDemo kind="dodont" item="pair" />
67
+ <ElevationDemo kind="dodont" item="flat" />
68
+ <ElevationDemo kind="dodont" item="own" />
69
+ <ElevationDemo kind="dodont" item="border" />
10
70
 
11
71
  ## Tokens
72
+
12
73
  <TokenTable prefix="elevation" />
74
+
75
+ <TokenTable prefix="color.surface.overlay" />
@@ -12,15 +12,18 @@ import ColorDemo from '../../../../components/ColorDemo.astro';
12
12
 
13
13
  **Pick the size from the text next to the icon.**
14
14
 
15
- | Size | Use it for |
16
- |---|---|
17
- | 16 px | Default. Buttons, menus, tables, form fields, next to 14 px text. |
18
- | 20 px | Next to 16 px text, in toolbars and in the page header. |
19
- | 24 px | An icon on its own, such as a status in a card or a small empty state. |
20
- | 32 px | Empty states only. |
15
+ | Size | Token | Use it for |
16
+ |---|---|---|
17
+ | 16 px | `size.icon.default` | Default. Buttons, menus, tables, form fields, next to 14 px text. |
18
+ | 20 px | `size.icon.medium` | Next to 16 px text, in toolbars and in the page header. |
19
+ | 24 px | `size.icon.large` | An icon on its own, such as a status in a card or a small empty state. |
20
+ | 32 px | `size.icon.empty` | Empty states only. |
21
21
 
22
22
  Don’t use other sizes. Don’t scale an icon to fit a box: change the box.
23
23
 
24
+ Bind these roles through CSS variables or convert them at numeric icon APIs;
25
+ the documented pixel sizes are not separate consumer constants.
26
+
24
27
  <IconDemo kind="dodont" item="size" />
25
28
 
26
29
  ## Stroke
@@ -7,6 +7,16 @@ import LayoutDemo from '../../../../components/LayoutDemo.astro';
7
7
 
8
8
  **The header, the side navigation and the page header repeat on every page, in the same place and at the same size.**
9
9
 
10
+ The global header and side navigation are one continuous shell surface: both use
11
+ `surface.subtle` (`--bh-color-surface-subtle`) in light and dark themes. The mobile
12
+ navigation drawer, including its logo row, uses that same surface. Do not give the
13
+ header `surface` while the sidebar uses `surface.subtle`. Page headers belong to
14
+ the content surface, not this global shell.
15
+
16
+ In a mobile navigation drawer, the logo row does not shrink. The menu occupies the
17
+ remaining height and scrolls only when its contents do not fit. Do not give a menu
18
+ below a logo row `min-height: 100%`: that adds the logo height to a full-height menu.
19
+
10
20
  ## Header
11
21
 
12
22
  **48 px high, across the full width. It holds what belongs to the whole product.**
@@ -1,9 +1,47 @@
1
1
  ---
2
2
  title: "Opacity"
3
- description: "When transparency is allowed, and why states and disabled controls use colour instead."
3
+ description: "How see-through something is. Used only for images inside something turned off and for the scrim behind dialogs. Controls show their state with colour."
4
4
  order: 10.5
5
- planned:
6
- - "Where transparency is allowed: the scrim behind dialogs, image overlays"
7
- - "Why hover, pressed and disabled use colour steps, not opacity"
8
- - "Opacity tokens, if any are needed"
9
5
  ---
6
+ import OpacityDemo from '../../../components/OpacityDemo.astro';
7
+ import TokenTable from '../../../components/TokenTable.astro';
8
+
9
+ Opacity sets how see-through an element is. A faded element lets the background show through, so its colour changes from screen to screen. Beehexa uses opacity in two places only: images inside something turned off, and the scrim behind dialogs.
10
+
11
+ ## Why controls use colour
12
+
13
+ - **The background shows through.** A faded button looks different on a white card, a yellow banner and a selected row.
14
+ - **Contrast is no longer checked.** A faded colour is a new colour, and text on it may become unreadable.
15
+ - **Opacity multiplies.** A 40% item inside a 40% card ends up at 16%.
16
+
17
+ The disabled colour tokens, such as `color.text.disabled`, look the same on every background. See [Interaction states](/foundations/styles/interaction-states/).
18
+
19
+ | I need to | Use |
20
+ |---|---|
21
+ | Fade a logo, avatar or illustration of something turned off | `opacity.disabled` (0.4) on the image |
22
+ | Turn off a button, input, text or icon | The disabled colour tokens |
23
+ | Dim the page behind a dialog or drawer | `color.surface.scrim` |
24
+ | Fade something in or out | Opacity 0 and 1 in an animation, see [Motion](/foundations/styles/motion/) |
25
+
26
+ ## Images that are turned off
27
+
28
+ Images have their own colours, so no colour token can change them. Fade the image; give the text `color.text.disabled`; keep the card's border. Always say "Turned off" in words as well.
29
+
30
+ <OpacityDemo kind="tile" />
31
+
32
+ <OpacityDemo kind="screen" />
33
+
34
+ ## The scrim
35
+
36
+ The scrim dims the page behind a dialog or drawer, so the dialog is the focus and the page reads as unavailable. It is a colour with transparency built in: graphite at 40% in light mode, black at 60% in dark mode, where a dark page needs more to dim. Use it as it is.
37
+
38
+ <OpacityDemo kind="scrim" />
39
+
40
+ ## Do and don’t
41
+
42
+ <OpacityDemo kind="dodont" item="container" />
43
+ <OpacityDemo kind="dodont" item="controls" />
44
+
45
+ ## Tokens
46
+
47
+ <TokenTable prefix="opacity" />
@@ -21,6 +21,13 @@ Controls are the things people click or type in: buttons, text fields, selects,
21
21
  - Keep controls of one height in one row. A 24 px button next to a 32 px field looks misaligned.
22
22
  - Set heights with the size tokens, not with padding. The text sits in the middle of the height.
23
23
 
24
+ Two other parts have a fixed size:
25
+
26
+ | Part | Size |
27
+ |---|---|
28
+ | Switch | 20 px high, 36 px wide; knob 16 px |
29
+ | Avatar | 32 px; 24 px in dense tables and lists |
30
+
24
31
  ## Target size
25
32
 
26
33
  **Every control can be clicked in an area of at least 24 × 24 px, and tapped in 44 × 44 px on touch screens.**
@@ -37,7 +37,7 @@ Noto Sans is the fallback because it covers Vietnamese and ₫ and is close to I
37
37
 
38
38
  <TypeDemo kind="features" />
39
39
 
40
- Every text style is applied with the `font` shorthand, and the shorthand resets `font-feature-settings`. So the features are set on every element from one variable, `--bh-font-features`; to add a feature, change the variable, not the property.
40
+ Every text style uses the `font` shorthand, which resets `font-feature-settings`. The source token `font.features` owns `"cv05", "cv08"`; generated token CSS applies `--bh-font-features` universally with `!important`. Consumers import that CSS rather than copy the base rule below. Add features through the variable, not a competing property. Keep a native 16px root and the 14px `font.body.default` role on the body.
41
41
 
42
42
  ```css
43
43
  /* Base: on every element */
@@ -1,9 +1,67 @@
1
1
  ---
2
2
  title: "Z-index"
3
- description: "The stacking order of layers: what sits above what, from sticky headers to dialogs and toasts."
3
+ description: "Which element is drawn on top when two overlap. Seven base layers and explicit fullscreen/readiness roles keep floating things in the same order everywhere."
4
4
  order: 8.5
5
- planned:
6
- - "Layer names and their order"
7
- - "Which layer each overlay uses: dropdown, popover, tooltip, dialog, drawer, toast"
8
- - "How layering pairs with elevation"
9
5
  ---
6
+ import ZIndexDemo from '../../../components/ZIndexDemo.astro';
7
+ import TokenTable from '../../../components/TokenTable.astro';
8
+
9
+ Z-index decides which element is drawn on top when two overlap. Beehexa uses seven named base layers instead of free numbers, so a menu never opens behind a dialog and a toast never hides under the scrim.
10
+
11
+ <ZIndexDemo kind="stack" />
12
+
13
+ ## How it works
14
+
15
+ - **A higher layer covers a lower one.** Base values run from 100 to 700 in steps of 100, leaving room for explicit fullscreen and readiness roles.
16
+ - **Pick the layer by what the element does**, not by how high a number you need.
17
+ - **A layer only competes with its neighbours.** An element with its own z-index, transform, filter or opacity below 1 starts a new stacking context; nothing inside can rise above it. That is why a list inside a dialog is rendered inside the dialog.
18
+
19
+ [Elevation](/foundations/styles/elevation/) shows how high a surface looks; z-index makes sure it is drawn on top. A menu needs both.
20
+
21
+ | Layer | Token | Value | Elevation | Use for |
22
+ |---|---|---|---|---|
23
+ | Sticky | `z-index.sticky` | 100 | Page | Sticky table headers and toolbars |
24
+ | Navigation | `z-index.navigation` | 200 | Page | App header, side navigation |
25
+ | Overlay | `z-index.overlay` | 300 | Overlay | Menus, select lists, popovers |
26
+ | Modal | `z-index.modal` | 400 | Modal | Dialogs, drawers and their scrim |
27
+ | Spotlight | `z-index.spotlight` | 500 | Modal | Product tour, onboarding spotlight |
28
+ | Tooltip | `z-index.tooltip` | 600 | none | Tooltips |
29
+ | Toast | `z-index.toast` | 700 | Overlay | Toasts, the skip link |
30
+
31
+ ## Why this order
32
+
33
+ - Menus cover the navigation they open from.
34
+ - A modal blocks the whole page, menus included.
35
+ - A tour can point at something inside a dialog.
36
+ - A tooltip explains a control, wherever that control is.
37
+ - A toast reports on the whole app, so nothing covers it.
38
+
39
+ ## On a screen
40
+
41
+ <ZIndexDemo kind="screen" />
42
+
43
+ Inside one component, values from 1 to 9 need no token, for example to lift the focused button in a button group.
44
+
45
+ ## Do and don’t
46
+
47
+ <ZIndexDemo kind="dodont" item="toast" />
48
+ <ZIndexDemo kind="dodont" item="inside" />
49
+ <ZIndexDemo kind="dodont" item="scrim" />
50
+
51
+ ## Tokens
52
+
53
+ <TokenTable prefix="z-index" />
54
+
55
+ ## Fullscreen and framework integration
56
+
57
+ The extended roles `z-index.fullscreen` (450) and `z-index.loading` (650) cover editor/code/image takeovers and blocking application readiness respectively. Fullscreen stays below spotlight, tooltip and toast feedback; readiness covers interactive surfaces and tooltips while toast feedback and the skip link remain on top at 700.
58
+
59
+ A fullscreen operational graph uses `z-index.modal` in an isolated local stacking context, not the editor takeover role. Child graph chrome uses private local ordering, so a global inspector can appear above it.
60
+
61
+ The Ant Design Vue adapter owns native component stacking: menus and select lists use the overlay band, dialogs and drawers use modal, tooltips use tooltip, and feedback uses toast. Bind its contextual `getPopupContainer` at the root ConfigProvider so a popup inside a dialog, drawer or fullscreen surface is rendered inside its owner, above the owner's scrim. Do not raise every popup globally to compensate for an incorrect container. Mark custom fullscreen hosts with `data-bh-overlay-owner`.
62
+
63
+ Root native Modal/Drawer surfaces remain in the document body for viewport geometry. Set root modal defaults from the adapter's invocation-time `getAntDesignVueModalZIndex()` (preserving an explicit caller value), and pass the live parent layer to `getAntDesignVueOverlayZIndex(fullscreen, ownerZIndex)` for nested drawers. A child uses the highest owner bound plus one, not a sum of ancestor z-index values; this keeps native confirmations above a fullscreen editor without crossing into the spotlight/feedback tiers.
64
+
65
+ Monaco's detached hover, completion and parameter-hint widgets use `z-index.tooltip`. Keep the editor takeover isolated so its child chrome cannot cover global feedback. Use one `color.surface.scrim` behind a modal or image inspector, not multiple translucent layers.
66
+
67
+ Preserve focus containment, focus return, Escape handling and scroll locking. Exercise the actual graph, editor hints, nested menus, inspector and loading stack; numeric ordering alone is not visual or accessibility proof.