@bitforgehq/ui 0.3.0 → 0.3.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 (144) hide show
  1. package/CHANGELOG.md +482 -0
  2. package/README.md +17 -11
  3. package/ai/MANIFEST.index.json +288 -0
  4. package/ai/MANIFEST.json +8716 -3926
  5. package/ai/REFERENCE.index.json +413 -0
  6. package/ai/REFERENCE.json +4710 -2047
  7. package/ai/UI_GUIDE.md +185 -57
  8. package/ai/components/back-button.md +42 -2
  9. package/ai/components/badge.md +17 -7
  10. package/ai/components/bar-chart.md +188 -0
  11. package/ai/components/breadcrumb-item.md +121 -0
  12. package/ai/components/breadcrumb.md +277 -0
  13. package/ai/components/button.md +82 -15
  14. package/ai/components/calendar.md +300 -0
  15. package/ai/components/card.md +63 -2
  16. package/ai/components/chart-card.md +283 -0
  17. package/ai/components/combobox.md +411 -0
  18. package/ai/components/date-input.md +396 -0
  19. package/ai/components/datetime-input.md +215 -0
  20. package/ai/components/description-item.md +111 -0
  21. package/ai/components/description-list.md +160 -0
  22. package/ai/components/donut-chart.md +169 -0
  23. package/ai/components/header.md +67 -2
  24. package/ai/components/icon-button.md +74 -16
  25. package/ai/components/icon.md +63 -7
  26. package/ai/components/line-chart.md +234 -0
  27. package/ai/components/list.md +70 -6
  28. package/ai/components/menu.md +5 -0
  29. package/ai/components/multi-combobox.md +257 -0
  30. package/ai/components/nav-group.md +178 -0
  31. package/ai/components/nav-item.md +210 -0
  32. package/ai/components/page-header.md +237 -0
  33. package/ai/components/pagination.md +173 -0
  34. package/ai/components/popover.md +250 -0
  35. package/ai/components/side-panel.md +391 -0
  36. package/ai/components/sidebar.md +363 -0
  37. package/ai/components/skeleton.md +215 -0
  38. package/ai/components/sparkline.md +188 -0
  39. package/ai/components/stat.md +233 -0
  40. package/ai/components/tab-group.md +226 -0
  41. package/ai/components/tab-panel.md +163 -0
  42. package/ai/components/table.md +568 -0
  43. package/ai/components/tabs.md +42 -2
  44. package/ai/components/theme-menu-button.md +182 -0
  45. package/ai/components/theme-switch.md +170 -0
  46. package/ai/components/time-input.md +240 -0
  47. package/ai/components/toolbar.md +289 -0
  48. package/ai/components/tooltip.md +230 -0
  49. package/ai/components/top-nav.md +159 -0
  50. package/ai/reference/bf-badge-appearance.md +36 -0
  51. package/ai/reference/bf-badge-variant.md +1 -1
  52. package/ai/reference/bf-bar-chart-orientation.md +27 -0
  53. package/ai/reference/bf-button-appearance.md +36 -0
  54. package/ai/reference/bf-button-variant.md +1 -1
  55. package/ai/reference/bf-calendar-disabled-date.md +23 -0
  56. package/ai/reference/bf-calendar-harness.md +51 -0
  57. package/ai/reference/bf-calendar-weekday.md +31 -0
  58. package/ai/reference/bf-chart-card-heading-level.md +26 -0
  59. package/ai/reference/bf-chart-curve.md +28 -0
  60. package/ai/reference/bf-chart-harness.md +51 -0
  61. package/ai/reference/bf-chart-legend-position.md +30 -0
  62. package/ai/reference/bf-chart-series.md +23 -0
  63. package/ai/reference/bf-chart-value-format.md +23 -0
  64. package/ai/reference/bf-color-scheme-service.md +30 -0
  65. package/ai/reference/bf-color-scheme.md +31 -0
  66. package/ai/reference/bf-combobox-filter.md +27 -0
  67. package/ai/reference/bf-combobox-harness.md +89 -0
  68. package/ai/reference/bf-combobox-option.md +33 -0
  69. package/ai/reference/bf-compare-with.md +2 -0
  70. package/ai/reference/bf-date-input-harness.md +67 -0
  71. package/ai/reference/bf-date-input-picker.md +27 -0
  72. package/ai/reference/bf-date-time-input-harness.md +76 -0
  73. package/ai/reference/bf-date-time-input-picker.md +27 -0
  74. package/ai/reference/bf-description-list-layout.md +26 -0
  75. package/ai/reference/bf-donut-slice.md +22 -0
  76. package/ai/reference/bf-labels.md +67 -20
  77. package/ai/reference/bf-menu-button-variant.md +1 -0
  78. package/ai/reference/bf-menu-placement.md +1 -0
  79. package/ai/reference/bf-overlay-options.md +28 -27
  80. package/ai/reference/bf-overlay-placement.md +34 -0
  81. package/ai/reference/bf-overlay-position.md +1 -1
  82. package/ai/reference/bf-overlay-service.md +5 -3
  83. package/ai/reference/bf-page-header-heading-level.md +26 -0
  84. package/ai/reference/bf-pagination-harness.md +61 -0
  85. package/ai/reference/bf-platform-service.md +17 -15
  86. package/ai/reference/bf-popover-harness.md +42 -0
  87. package/ai/reference/bf-popover-placement.md +28 -0
  88. package/ai/reference/bf-popover-trigger-directive.md +56 -0
  89. package/ai/reference/bf-side-panel-config.md +35 -0
  90. package/ai/reference/bf-side-panel-harness.md +34 -0
  91. package/ai/reference/bf-side-panel-ref.md +44 -0
  92. package/ai/reference/bf-side-panel-service.md +43 -0
  93. package/ai/reference/bf-side-panel-template-context.md +32 -0
  94. package/ai/reference/bf-side-panel-width.md +28 -0
  95. package/ai/reference/bf-size.md +12 -0
  96. package/ai/reference/bf-skeleton-shape.md +28 -0
  97. package/ai/reference/bf-stat-trend-intent.md +27 -0
  98. package/ai/reference/bf-stat-trend.md +27 -0
  99. package/ai/reference/bf-stat-variant.md +26 -0
  100. package/ai/reference/bf-tab-group-harness.md +44 -0
  101. package/ai/reference/bf-table-align.md +26 -0
  102. package/ai/reference/bf-table-cell-context.md +24 -0
  103. package/ai/reference/bf-table-cell-directive.md +22 -0
  104. package/ai/reference/bf-table-column.md +28 -0
  105. package/ai/reference/bf-table-harness.md +77 -0
  106. package/ai/reference/bf-table-row-key.md +21 -0
  107. package/ai/reference/bf-table-sort-direction.md +26 -0
  108. package/ai/reference/bf-table-sort.md +27 -0
  109. package/ai/reference/bf-theme.md +5 -4
  110. package/ai/reference/bf-time-input-harness.md +67 -0
  111. package/ai/reference/bf-time-input-hour-cycle.md +27 -0
  112. package/ai/reference/bf-time-input-picker.md +27 -0
  113. package/ai/reference/bf-tooltip-directive.md +43 -0
  114. package/ai/reference/bf-tooltip-harness.md +43 -0
  115. package/ai/reference/bf-tooltip-placement.md +28 -0
  116. package/ai/reference/bf_color_scheme_storage.md +20 -0
  117. package/ai/reference/bf_side_panel_data.md +17 -0
  118. package/ai/reference/bf_theme_storage.md +3 -1
  119. package/ai/reference/provide-bf-color-scheme.md +20 -0
  120. package/ai/reference/provide-bf-labels.md +12 -4
  121. package/ai/reference/slide-in-from-end.md +24 -0
  122. package/ai/reference/slide-out-to-end.md +24 -0
  123. package/fesm2022/bitforgehq-ui-charts.mjs +3045 -0
  124. package/fesm2022/bitforgehq-ui-charts.mjs.map +1 -0
  125. package/fesm2022/bitforgehq-ui-icons.mjs +107 -3
  126. package/fesm2022/bitforgehq-ui-icons.mjs.map +1 -1
  127. package/fesm2022/bitforgehq-ui-mobile.mjs +14 -59
  128. package/fesm2022/bitforgehq-ui-mobile.mjs.map +1 -1
  129. package/fesm2022/bitforgehq-ui-overlay.mjs +6436 -33
  130. package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -1
  131. package/fesm2022/bitforgehq-ui-testing.mjs +618 -1
  132. package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -1
  133. package/fesm2022/bitforgehq-ui.mjs +5092 -153
  134. package/fesm2022/bitforgehq-ui.mjs.map +1 -1
  135. package/llms-full.txt +25177 -14398
  136. package/llms.txt +95 -1
  137. package/package.json +7 -3
  138. package/theme.css +101 -0
  139. package/types/bitforgehq-ui-charts.d.ts +967 -0
  140. package/types/bitforgehq-ui-icons.d.ts +13 -1
  141. package/types/bitforgehq-ui-mobile.d.ts +25 -18
  142. package/types/bitforgehq-ui-overlay.d.ts +1844 -7
  143. package/types/bitforgehq-ui-testing.d.ts +748 -1
  144. package/types/bitforgehq-ui.d.ts +2136 -44
@@ -0,0 +1,188 @@
1
+ # bf-bar-chart
2
+
3
+ Bar chart of series over categories, grouped or stacked, vertical or horizontal
4
+
5
+ `BfBarChartComponent` from `@bitforgehq/ui/charts` · Data display · since 0.3.1
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component } from '@angular/core';
11
+ import { BfBarChartComponent, type BfChartSeries } from '@bitforgehq/ui/charts';
12
+
13
+ /**
14
+ * The bf-bar-chart overview.
15
+ *
16
+ * Live demo of `bf-bar-chart`: support tickets opened and closed per weekday, side by side.
17
+ * Hover a day, or focus the plot and use the arrow keys; hide a series in the legend; open Show
18
+ * data for the table.
19
+ */
20
+ @Component({
21
+ selector: 'app-example-bar-chart-overview',
22
+ imports: [BfBarChartComponent],
23
+ template: `
24
+ <bf-bar-chart label="Support tickets this week" [series]="series" [categories]="days" />
25
+ `,
26
+ })
27
+ export class BarChartOverviewExampleComponent {
28
+ readonly days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'];
29
+ readonly series: readonly BfChartSeries[] = [
30
+ { name: 'Opened', values: [14, 9, 12, 17, 8] },
31
+ { name: 'Closed', values: [11, 12, 8, 15, 13] },
32
+ ];
33
+ }
34
+ ```
35
+
36
+ ## Overview
37
+
38
+ A bar chart of one or more series over shared categories, drawn in SVG with the chart palette:
39
+ bars side by side per category (or `stacked`), up from the baseline (or `horizontal`, along
40
+ from it), with a value axis of nice ticks, a legend that shows and hides series, a tooltip on
41
+ hover and on keyboard focus (Arrow keys move between the categories and a polite live region
42
+ reads each one) and a Show data table. Bars grow in on the first render and on every data
43
+ change, still under reduced motion; under `dir="rtl"` the horizontal axis runs from the right.
44
+
45
+ A bar chart compares amounts between categories: tickets opened and closed per weekday, revenue per region, hours per project. `bf-bar-chart` draws it in SVG from `series` (each a `name` and one value per category) and `categories`, side by side or `stacked`, as vertical columns or `horizontal` bars, with a value axis of nice ticks from zero, a legend that shows and hides series, a tooltip on hover and on keyboard focus, and a Show data table. It lives in `@bitforgehq/ui/charts` (`import { BfBarChartComponent } from '@bitforgehq/ui/charts'`) and works the same in a desktop web app and in the Capacitor shell.
46
+
47
+ ## When to use
48
+
49
+ Use `bf-bar-chart` to compare amounts between categories, or the parts of each category with `stacked`. Our charts are themed with the `--color-chart-*` tokens, follow dark mode and `dir="rtl"`, and bring their own legend, tooltip, keyboard reading and data table; bring a chart library (themed with the same tokens) only for a form they do not draw. Use [bf-line-chart](https://bitforge-ui.web.app/components/line-chart) instead for a trend over time, [bf-donut-chart](https://bitforge-ui.web.app/components/donut-chart) for the shares of one whole, and [bf-table](https://bitforge-ui.web.app/components/table) when readers look up exact values more than they compare them.
50
+
51
+ ## Stacked
52
+
53
+ `stacked` puts the series of each category into one bar, one segment per series with a 2 px gap between them, so the bar's length is the category's total. Positive values stack up from zero and negative ones down. Use it when the total matters as much as its parts; compare the parts across categories with grouped bars instead (the default), which share one baseline.
54
+
55
+ ```ts
56
+ import { Component } from '@angular/core';
57
+ import { BfBarChartComponent, type BfChartSeries } from '@bitforgehq/ui/charts';
58
+
59
+ /**
60
+ * A stacked bar chart.
61
+ *
62
+ * `stacked` puts the series of each month into one bar, so its length is the month's total:
63
+ * hours logged per month, split by kind of work.
64
+ */
65
+ @Component({
66
+ selector: 'app-example-bar-chart-stacked',
67
+ imports: [BfBarChartComponent],
68
+ template: `
69
+ <bf-bar-chart
70
+ label="Hours logged by kind of work"
71
+ stacked
72
+ legend="top"
73
+ [series]="series"
74
+ [categories]="months"
75
+ [valueFormat]="hours"
76
+ />
77
+ `,
78
+ })
79
+ export class BarChartStackedExampleComponent {
80
+ readonly months = ['Jun', 'Jul', 'Aug', 'Sep', 'Oct'];
81
+ readonly series: readonly BfChartSeries[] = [
82
+ { name: 'Design', values: [42, 38, 24, 51, 47] },
83
+ { name: 'Build', values: [88, 96, 61, 102, 110] },
84
+ { name: 'Review', values: [16, 20, 12, 24, 22] },
85
+ ];
86
+ readonly hours = (value: number): string => `${value} h`;
87
+ }
88
+ ```
89
+
90
+ ## Horizontal
91
+
92
+ `orientation="horizontal"` runs the bars along from a vertical baseline, the category names down the start side: for long names (projects, countries) or many categories. The categories run top to bottom, and <kbd>ArrowUp</kbd> and <kbd>ArrowDown</kbd> move between them too.
93
+
94
+ ```ts
95
+ import { Component } from '@angular/core';
96
+ import { BfBarChartComponent, type BfChartSeries } from '@bitforgehq/ui/charts';
97
+
98
+ /**
99
+ * A horizontal bar chart.
100
+ *
101
+ * `orientation="horizontal"` runs the bars along from the start side, with room for long
102
+ * category names: open tasks per project, sorted from most to fewest.
103
+ */
104
+ @Component({
105
+ selector: 'app-example-bar-chart-horizontal',
106
+ imports: [BfBarChartComponent],
107
+ template: `
108
+ <bf-bar-chart
109
+ label="Open tasks per project"
110
+ orientation="horizontal"
111
+ [height]="200"
112
+ [series]="series"
113
+ [categories]="projects"
114
+ />
115
+ `,
116
+ })
117
+ export class BarChartHorizontalExampleComponent {
118
+ readonly projects = ['Atlas relaunch', 'Billing', 'Mobile app', 'Docs site'];
119
+ readonly series: readonly BfChartSeries[] = [{ name: 'Open tasks', values: [23, 17, 12, 6] }];
120
+ }
121
+ ```
122
+
123
+ ## Data and formatting
124
+
125
+ The value axis always includes zero, so every bar's length is honest; values below zero draw below (or before) the baseline. A `null` value draws no bar. `valueFormat` formats the values on the axis, in the tooltip, the announcement and the table; unset, values use `Intl.NumberFormat` in the document's language. Each series keeps its colour (`--color-chart-1` for the first) when another is hidden. Bars grow from the baseline on the first render and whenever the data change. `height` sets the plot's height in px (240 by default); the width follows the container.
126
+
127
+ ## Desktop
128
+
129
+ Under a mouse, hovering a category highlights its band in `surface-muted` and shows its tooltip; leaving the plot hides them. The legend's buttons take a `surface-muted` fill on hover. The plot is one stop in the tab order, with the focus ring on keyboard focus: focusing it shows the first category. Under a coarse pointer (touch) a tap shows a category's tooltip, and the legend buttons and the Show data button are at least 44 px tall. Inside `provideBfShell()` it renders and behaves the same.
130
+
131
+ ## Theming
132
+
133
+ - Bars: `--color-chart-1` … `--color-chart-8` by series (`fill-chart-n`), 2 px corner radius, at most 40 px thick.
134
+ - Grid lines: `chart-grid`; the zero baseline: `chart-axis`; axis text: `text-micro` in `text-muted`.
135
+ - Active category band: `surface-muted`.
136
+ - Tooltip, legend and data table: as in [bf-line-chart](https://bitforge-ui.web.app/components/line-chart).
137
+ - Motion: `--animate-chart-grow-y` (columns) or `--animate-chart-grow-x` (horizontal bars); none under `prefers-reduced-motion`.
138
+
139
+ ## Accessibility
140
+
141
+ The chart is a native `figure` named by `label` (else the `bf-chart-card` title, else the `chart` label); the SVG is hidden from assistive technology. The plot is a focusable `role="group"` named by the `chartValues` label: the arrow keys move between the categories and a polite live region reads each one ("Tue: Opened 9, Closed 12"). The legend is a group of toggle buttons (`aria-pressed`); the Show data button (`aria-expanded`) shows the data as a native `table` with a caption and row headers. Every series is ≥ 3:1 against `surface` and `bg` in both modes, and the legend names each colour.
142
+
143
+ ### Keyboard
144
+
145
+ | Key | Action |
146
+ | ------------------------------------------------ | ------------------------------------------------------------------------------------- |
147
+ | <kbd>Tab</kbd> / <kbd>Shift</kbd>+<kbd>Tab</kbd> | Moves through the plot, the legend buttons and the Show data button |
148
+ | <kbd>ArrowRight</kbd> | Moves to the next category and reads it (the previous one under `dir="rtl"`) |
149
+ | <kbd>ArrowLeft</kbd> | Moves to the previous category and reads it (the next one under `dir="rtl"`) |
150
+ | <kbd>ArrowDown</kbd> / <kbd>ArrowUp</kbd> | With `orientation="horizontal"`, moves to the next / previous category |
151
+ | <kbd>Home</kbd> / <kbd>End</kbd> | Moves to the first / last category |
152
+ | <kbd>Escape</kbd> | Hides the tooltip |
153
+ | <kbd>Enter</kbd> / <kbd>Space</kbd> | On a legend button, shows or hides its series; on Show data, shows or hides the table |
154
+
155
+ ## Inputs
156
+
157
+ | Name | Type | Default | Description |
158
+ | ------------- | --------------------------------------------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
159
+ | `series` | `readonly BfChartSeries[]` | required | The series to draw, each with one value per category. |
160
+ | `categories` | `readonly string[]` | required | The category names along the category axis, in order. |
161
+ | `label` | `string` | `''` | The chart's accessible name; empty falls back to the `bf-chart-card` title, then to `provideBfLabels()`'s `chart` ("Chart"). |
162
+ | `height` | `number` | `240` | The plot's height in px; the width follows the container. |
163
+ | `orientation` | `BfBarChartOrientation` (`'vertical' \| 'horizontal'`) | `'vertical'` | `vertical` (default) bars rise from a horizontal baseline; `horizontal` bars run along from a vertical one, the categories down the start side. |
164
+ | `stacked` | `boolean` | `false` | Stacks the series of a category into one bar instead of side by side. |
165
+ | `valueFormat` | `BfChartValueFormat \| undefined` | `undefined` | Formats the values on the axis, in the tooltip and in the table; unset uses `Intl.NumberFormat` in the document's language. |
166
+ | `legend` | `BfChartLegendPosition \| undefined` (`'top' \| 'bottom' \| 'none' \| undefined`) | `undefined` | Where the legend goes; unset follows the `bf-chart-card`, else `bottom`. A single series draws no legend. |
167
+
168
+ ## Notes
169
+
170
+ ### Do
171
+
172
+ - Sort the categories by value when they have no order of their own, so the bars read as a ranking.
173
+ - Use `horizontal` when the category names are long, rather than abbreviating them.
174
+
175
+ ### Don't
176
+
177
+ - Don't stack series that do not add up to a meaningful total.
178
+ - Don't draw more than eight series; fold the smallest into "Other".
179
+
180
+ ## Migration notes
181
+
182
+ ### 0.3.1
183
+
184
+ New `bf-bar-chart` in `@bitforgehq/ui/charts`: series over categories as bars from a zero
185
+ baseline, side by side or `stacked`, vertical or `orientation="horizontal"`
186
+ (`BfBarChartOrientation`), with the line chart's value axis, legend, tooltip, keyboard reading and
187
+ data table. Bars grow from the baseline on the first render and on every data change, not under
188
+ reduced motion; `dir="rtl"` mirrors the horizontal axis.
@@ -0,0 +1,121 @@
1
+ # li[bfBreadcrumbItem]
2
+
3
+ One level of a breadcrumb trail: a link, or the current page as text
4
+
5
+ `BfBreadcrumbItemComponent` from `@bitforgehq/ui` · Navigation · since 0.3.1
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component } from '@angular/core';
11
+ import { RouterLink } from '@angular/router';
12
+ import { BfBreadcrumbComponent, BfBreadcrumbItemComponent } from '@bitforgehq/ui';
13
+
14
+ /**
15
+ * The li[bfBreadcrumbItem] overview.
16
+ *
17
+ * Three levels of a `bf-breadcrumb`: two ancestor pages as router links, each followed by a
18
+ * chevron, and the current page as plain text, marked `aria-current="page"`.
19
+ */
20
+ @Component({
21
+ selector: 'app-example-breadcrumb-item-overview',
22
+ imports: [RouterLink, BfBreadcrumbComponent, BfBreadcrumbItemComponent],
23
+ template: `
24
+ <bf-breadcrumb label="Component breadcrumb">
25
+ <li bfBreadcrumbItem><a routerLink="/components">Components</a></li>
26
+ <li bfBreadcrumbItem><a routerLink="/components/breadcrumb">Breadcrumb</a></li>
27
+ <li bfBreadcrumbItem>Breadcrumb item</li>
28
+ </bf-breadcrumb>
29
+ `,
30
+ })
31
+ export class BreadcrumbItemOverviewExampleComponent {}
32
+ ```
33
+
34
+ ## Overview
35
+
36
+ One level of a `bf-breadcrumb` trail, on the consumer's own `<li>` so the list stays `ol > li`.
37
+ Project an `<a routerLink>` for an ancestor page, or plain text for the current page; the last
38
+ item is the current page (`aria-current="page"`, no link look, no separator), every other one is
39
+ followed by a chevron.
40
+
41
+ One level of a [`bf-breadcrumb`](https://bitforge-ui.web.app/components/breadcrumb) trail, as an attribute on the app's own `<li>`. It styles the link projected into it, adds the chevron after it and, on the last level, marks the current page. An item lives only inside a `bf-breadcrumb`, which decides which level is current and which ones a collapsed trail hides.
42
+
43
+ ## When to use
44
+
45
+ Use `li[bfBreadcrumbItem]` for each level of a [bf-breadcrumb](https://bitforge-ui.web.app/components/breadcrumb), and only there. On mobile, in the Capacitor shell, use [bf-back-button](https://bitforge-ui.web.app/components/back-button) in [bf-header](https://bitforge-ui.web.app/components/header) instead.
46
+
47
+ ## Links and the current page
48
+
49
+ Put an `<a routerLink>` (or `href`) straight inside the `li` for a page above the current one: as a direct child it takes the link look, the hover underline and the focus ring. Put plain text in the last item, the current page: it is wrapped in a span with `aria-current="page"` and shown semibold, and no chevron follows it.
50
+
51
+ ```ts
52
+ import { Component } from '@angular/core';
53
+ import { RouterLink } from '@angular/router';
54
+ import { BfBreadcrumbComponent, BfBreadcrumbItemComponent } from '@bitforgehq/ui';
55
+
56
+ /**
57
+ * A breadcrumb trail.
58
+ *
59
+ * Each level is the app's own `<li bfBreadcrumbItem>` holding a router link; the last one is the
60
+ * current page as plain text. `label` names the `nav`; this one is not the page's only breadcrumb
61
+ * (the docs site has its own), so it gets a name of its own.
62
+ */
63
+ @Component({
64
+ selector: 'app-example-breadcrumb-basic',
65
+ imports: [RouterLink, BfBreadcrumbComponent, BfBreadcrumbItemComponent],
66
+ template: `
67
+ <bf-breadcrumb label="Docs breadcrumb">
68
+ <li bfBreadcrumbItem><a routerLink="/">Docs</a></li>
69
+ <li bfBreadcrumbItem><a routerLink="/components">Components</a></li>
70
+ <li bfBreadcrumbItem>Breadcrumb</li>
71
+ </bf-breadcrumb>
72
+ `,
73
+ })
74
+ export class BreadcrumbBasicExampleComponent {}
75
+ ```
76
+
77
+ ## Desktop
78
+
79
+ A linked level turns from `text-muted` to `text` and is underlined on hover, with the pointer cursor; the ring shows on keyboard focus only. A link is at least 24 px tall under a fine pointer and 44 px under a coarse one (`pointer-coarse:min-h-hit`), through a min height rather than an `::after`, because it is your content. See the [bf-breadcrumb](https://bitforge-ui.web.app/components/breadcrumb) page for collapsing.
80
+
81
+ ## Theming
82
+
83
+ - Ancestor level: `text-muted`, `text` on hover with an underline (`underline-offset-4`); `rounded-tile` with the `ring` focus ring.
84
+ - Current page: `text`, semibold.
85
+ - Separator: a 1.5 px CSS chevron in `text-subtle`, pointing to the inline end and mirrored under `dir="rtl"`.
86
+
87
+ ## Accessibility
88
+
89
+ The host is your native `<li>`, so the trail stays an ordered list. Only the last item carries `aria-current="page"`. The chevron is an empty `aria-hidden` element, never announced. A level a collapsed trail hides carries `hidden`; when the ellipsis shows it and it has no link, the item takes focus itself (`tabindex="-1"`).
90
+
91
+ ### Keyboard
92
+
93
+ | Key | Action |
94
+ | ---------------- | ------------------------- |
95
+ | <kbd>Tab</kbd> | Moves to the level's link |
96
+ | <kbd>Enter</kbd> | Follows the focused link |
97
+
98
+ ## Slots
99
+
100
+ - `default`: The level: an `<a>` link to the page, or plain text for the current page.
101
+
102
+ ## Notes
103
+
104
+ ### Do
105
+
106
+ - Project the link as the direct child of the `li`, so it takes the link look.
107
+ - Keep the current page plain text.
108
+
109
+ ### Don't
110
+
111
+ - Don't use `li[bfBreadcrumbItem]` outside a `bf-breadcrumb`.
112
+ - Don't put more than one link, or a control, in one level.
113
+
114
+ ## Migration notes
115
+
116
+ ### 0.3.1
117
+
118
+ New `li[bfBreadcrumbItem]` in `@bitforgehq/ui`: one level of a `bf-breadcrumb` on the app's own
119
+ `<li>`. A projected link takes the muted link look, the hover underline and the focus ring (at
120
+ least 24 px tall, 44 px under a coarse pointer); the last level is the current page, wrapped in
121
+ `aria-current="page"` with no chevron.
@@ -0,0 +1,277 @@
1
+ # bf-breadcrumb
2
+
3
+ Desktop breadcrumb trail of links to the current page, with optional collapsing
4
+
5
+ `BfBreadcrumbComponent` from `@bitforgehq/ui` · Navigation · since 0.3.1
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component, signal } from '@angular/core';
11
+ import { RouterLink } from '@angular/router';
12
+ import { BfBreadcrumbComponent, BfBreadcrumbItemComponent } from '@bitforgehq/ui';
13
+
14
+ /**
15
+ * The bf-breadcrumb overview.
16
+ *
17
+ * Live demo of `bf-breadcrumb`: a five-level trail collapsed to three with `maxItems`; the ellipsis
18
+ * shows the hidden levels in place and moves focus to the first one, which the result line
19
+ * reports. The demo's links stay on this page (a fragment per level); an app links each level to
20
+ * its route.
21
+ */
22
+ @Component({
23
+ selector: 'app-example-breadcrumb-overview',
24
+ imports: [RouterLink, BfBreadcrumbComponent, BfBreadcrumbItemComponent],
25
+ template: `
26
+ <div class="flex flex-col gap-3" (focusin)="onFocus($event)">
27
+ <bf-breadcrumb label="Release breadcrumb" [maxItems]="3">
28
+ @for (level of trail; track level.id) {
29
+ <li bfBreadcrumbItem>
30
+ <a routerLink="." [fragment]="level.id">{{ level.label }}</a>
31
+ </li>
32
+ }
33
+ <li bfBreadcrumbItem>Release 2.4</li>
34
+ </bf-breadcrumb>
35
+ <p data-status aria-live="polite" class="text-caption text-text-muted">{{ status() }}</p>
36
+ </div>
37
+ `,
38
+ })
39
+ export class BreadcrumbOverviewExampleComponent {
40
+ readonly status = signal('Two levels are hidden.');
41
+ readonly trail: readonly { id: string; label: string }[] = [
42
+ { id: 'workspace', label: 'Workspace' },
43
+ { id: 'projects', label: 'Projects' },
44
+ { id: 'atlas', label: 'Atlas' },
45
+ { id: 'releases', label: 'Releases' },
46
+ ];
47
+
48
+ onFocus(event: FocusEvent): void {
49
+ if (event.target instanceof HTMLAnchorElement) {
50
+ this.status.set(`Focus on ${event.target.text.trim()}.`);
51
+ }
52
+ }
53
+ }
54
+ ```
55
+
56
+ ## Overview
57
+
58
+ Desktop breadcrumb trail: a `nav` named "Breadcrumb" around an ordered list of
59
+ `li[bfBreadcrumbItem]` levels, the last one the current page, a CSS chevron between them. With
60
+ `maxItems` set and a longer trail it keeps the first level and the last `maxItems - 1`, and hides
61
+ the middle ones behind an ellipsis button that shows them in place, or behind the consumer's own
62
+ `bfSlot="overflow"` content (a menu button of the hidden levels).
63
+
64
+ A breadcrumb trail for desktop web apps: a `nav` named "Breadcrumb" around an ordered list of the pages above the current one, each a link, with the current page last. Every level is the app's own `<li bfBreadcrumbItem>`, so the list stays `ol > li` and the links stay the app's `routerLink`s; the widget draws the chevrons between them, marks the current page and, with `maxItems`, collapses a long trail behind an ellipsis.
65
+
66
+ ## When to use
67
+
68
+ Use `bf-breadcrumb` on a desktop page that sits deep in a hierarchy (a client's billing settings, a file in a folder), so the user sees where they are and can go up any number of levels. Don't use it for a flat app with one level, where the sidebar or the top nav already says where the user is. On mobile, in the Capacitor shell, use [bf-back-button](https://bitforge-ui.web.app/components/back-button) in [bf-header](https://bitforge-ui.web.app/components/header) instead.
69
+
70
+ ## Basic usage
71
+
72
+ Put one `li[bfBreadcrumbItem]` per level inside `bf-breadcrumb`, from the root down: an `<a routerLink>` for each page above, plain text for the current page. The last item is always the current page; it gets `aria-current="page"` and no chevron. Import `BfBreadcrumbComponent` and `BfBreadcrumbItemComponent` from `@bitforgehq/ui`. The `nav` is named "Breadcrumb"; when a page shows more than one breadcrumb (as this page does, beside the site's own), give each a distinct `label`.
73
+
74
+ ```ts
75
+ import { Component } from '@angular/core';
76
+ import { RouterLink } from '@angular/router';
77
+ import { BfBreadcrumbComponent, BfBreadcrumbItemComponent } from '@bitforgehq/ui';
78
+
79
+ /**
80
+ * A breadcrumb trail.
81
+ *
82
+ * Each level is the app's own `<li bfBreadcrumbItem>` holding a router link; the last one is the
83
+ * current page as plain text. `label` names the `nav`; this one is not the page's only breadcrumb
84
+ * (the docs site has its own), so it gets a name of its own.
85
+ */
86
+ @Component({
87
+ selector: 'app-example-breadcrumb-basic',
88
+ imports: [RouterLink, BfBreadcrumbComponent, BfBreadcrumbItemComponent],
89
+ template: `
90
+ <bf-breadcrumb label="Docs breadcrumb">
91
+ <li bfBreadcrumbItem><a routerLink="/">Docs</a></li>
92
+ <li bfBreadcrumbItem><a routerLink="/components">Components</a></li>
93
+ <li bfBreadcrumbItem>Breadcrumb</li>
94
+ </bf-breadcrumb>
95
+ `,
96
+ })
97
+ export class BreadcrumbBasicExampleComponent {}
98
+ ```
99
+
100
+ ## Collapsing a long trail
101
+
102
+ Set `maxItems` to collapse a trail with more levels than that: the first level and the last `maxItems - 1` stay, and the middle ones are hidden behind an ellipsis button ("Show 2 more levels", `provideBfLabels()`'s `breadcrumbExpand`). Pressing it shows every level in place and moves focus to the first one it revealed, so a keyboard user carries on from there. The trail collapses again when its levels change (on the next page). `0`, the default, never collapses; a value below 2 acts as 2, so the first level and the current page always stay.
103
+
104
+ ```ts
105
+ import { Component } from '@angular/core';
106
+ import { RouterLink } from '@angular/router';
107
+ import { BfBreadcrumbComponent, BfBreadcrumbItemComponent } from '@bitforgehq/ui';
108
+
109
+ /**
110
+ * A long trail collapsed in place.
111
+ *
112
+ * `maxItems="4"` keeps the first level and the last three; the ellipsis button shows the two
113
+ * hidden ones in place and moves focus to the first of them. The demo's links stay on this page.
114
+ */
115
+ @Component({
116
+ selector: 'app-example-breadcrumb-collapsed',
117
+ imports: [RouterLink, BfBreadcrumbComponent, BfBreadcrumbItemComponent],
118
+ template: `
119
+ <bf-breadcrumb label="Invoice breadcrumb" maxItems="4">
120
+ @for (level of trail; track level.id) {
121
+ <li bfBreadcrumbItem>
122
+ <a routerLink="." [fragment]="level.id">{{ level.label }}</a>
123
+ </li>
124
+ }
125
+ <li bfBreadcrumbItem>Invoice INV-204</li>
126
+ </bf-breadcrumb>
127
+ `,
128
+ })
129
+ export class BreadcrumbCollapsedExampleComponent {
130
+ readonly trail: readonly { id: string; label: string }[] = [
131
+ { id: 'home', label: 'Home' },
132
+ { id: 'clients', label: 'Clients' },
133
+ { id: 'meadow', label: 'Meadow Studio' },
134
+ { id: 'billing', label: 'Billing' },
135
+ { id: 'invoices', label: 'Invoices' },
136
+ ];
137
+ }
138
+ ```
139
+
140
+ ## A menu of the hidden levels
141
+
142
+ To keep the trail on one line, project your own control in `bfSlot="overflow"`: it takes the ellipsis button's place, after the first level. A `bf-menu-button` from `@bitforgehq/ui/overlay` with an `a[bfMenuItem]` link per hidden level is the usual one (a root widget cannot open a menu itself). The hidden levels are a slice of the array you already render: `trail.slice(1, count - maxItems + 1)`, where `count` includes the current page. Name the menu button with its `label`.
143
+
144
+ ```ts
145
+ import { Component, computed } from '@angular/core';
146
+ import { RouterLink } from '@angular/router';
147
+ import { BfBreadcrumbComponent, BfBreadcrumbItemComponent } from '@bitforgehq/ui';
148
+ import { BfIconComponent } from '@bitforgehq/ui/icons';
149
+ import {
150
+ BfMenuButtonComponent,
151
+ BfMenuComponent,
152
+ BfMenuItemDirective,
153
+ } from '@bitforgehq/ui/overlay';
154
+ import { Ellipsis } from 'lucide';
155
+
156
+ /**
157
+ * A long trail collapsed behind a menu.
158
+ *
159
+ * A `bf-menu-button` in `bfSlot="overflow"` replaces the ellipsis button: its menu links to the
160
+ * levels the trail hides, the same slice of the app's own array (`maxItems` 3 keeps the first
161
+ * level and the last two). The demo's links stay on this page.
162
+ */
163
+ @Component({
164
+ selector: 'app-example-breadcrumb-collapsed-menu',
165
+ imports: [
166
+ RouterLink,
167
+ BfBreadcrumbComponent,
168
+ BfBreadcrumbItemComponent,
169
+ BfIconComponent,
170
+ BfMenuButtonComponent,
171
+ BfMenuComponent,
172
+ BfMenuItemDirective,
173
+ ],
174
+ template: `
175
+ <bf-breadcrumb label="Client breadcrumb" [maxItems]="maxItems">
176
+ <bf-menu-button
177
+ bfSlot="overflow"
178
+ [menu]="hiddenLevels"
179
+ iconOnly
180
+ label="Hidden levels"
181
+ variant="text"
182
+ size="sm"
183
+ >
184
+ <bf-icon [icon]="ellipsis" />
185
+ </bf-menu-button>
186
+ @for (level of trail; track level.id) {
187
+ <li bfBreadcrumbItem>
188
+ <a routerLink="." [fragment]="level.id">{{ level.label }}</a>
189
+ </li>
190
+ }
191
+ <li bfBreadcrumbItem>Invoice INV-204</li>
192
+ </bf-breadcrumb>
193
+
194
+ <ng-template #hiddenLevels>
195
+ <bf-menu>
196
+ @for (level of hidden(); track level.id) {
197
+ <a bfMenuItem routerLink="." [fragment]="level.id">{{ level.label }}</a>
198
+ }
199
+ </bf-menu>
200
+ </ng-template>
201
+ `,
202
+ })
203
+ export class BreadcrumbCollapsedMenuExampleComponent {
204
+ readonly ellipsis = Ellipsis;
205
+ readonly maxItems = 3;
206
+ readonly trail: readonly { id: string; label: string }[] = [
207
+ { id: 'home', label: 'Home' },
208
+ { id: 'clients', label: 'Clients' },
209
+ { id: 'meadow', label: 'Meadow Studio' },
210
+ { id: 'billing', label: 'Billing' },
211
+ { id: 'invoices', label: 'Invoices' },
212
+ ];
213
+ /** The levels between the first and the last `maxItems - 1` (the current page counts as one). */
214
+ readonly hidden = computed(() =>
215
+ this.trail.slice(1, this.trail.length + 1 - (this.maxItems - 1)),
216
+ );
217
+ }
218
+ ```
219
+
220
+ ## Desktop
221
+
222
+ `bf-breadcrumb` is a desktop pattern. A link turns from `text-muted` to `text` and is underlined on hover, with the pointer cursor; the ellipsis button takes a `surface-muted` fill on hover. The focus ring shows on keyboard focus only. Links keep the browser's behaviour: Ctrl/Cmd-click and a middle click open a level in a new tab. Under a coarse pointer (a touch laptop) every link is at least 44 px tall (`pointer-coarse:min-h-hit`) and the ellipsis button's hit area extends to 44 px. The trail wraps onto more lines when it does not fit; collapse it with `maxItems` to keep it short.
223
+
224
+ ## Theming
225
+
226
+ - Text: `text-label-sm`; ancestor levels `text-muted`, `text` on hover; the current page `text`, semibold.
227
+ - Separators: a 1.5 px CSS chevron in `text-subtle`, mirrored under `dir="rtl"`.
228
+ - Links and the ellipsis button: `rounded-tile` and the `ring` focus ring; the ellipsis button's hover fill is `surface-muted`, its pressed fill `surface-pressed`.
229
+
230
+ ## Accessibility
231
+
232
+ The host renders a `nav` landmark named by `label` (default "Breadcrumb", `provideBfLabels()`'s `breadcrumb`) around an `ol`, so a screen reader announces the number of levels; give a second breadcrumb on the same page another `label`. The current page is wrapped in a span with `aria-current="page"`. The chevrons are empty, `aria-hidden` elements, so the trail reads as its level names only. Hidden levels carry `hidden`, so they leave the accessibility tree until the ellipsis shows them; the ellipsis is a native `<button>` named "Show N more levels", and pressing it moves focus to the first revealed level's link (a level without a link takes focus itself). Content in `bfSlot="overflow"` brings its own name and keyboard handling.
233
+
234
+ ### Keyboard
235
+
236
+ | Key | Action |
237
+ | ----------------------------------- | ----------------------------------------------------------------------------- |
238
+ | <kbd>Tab</kbd> | Moves through the links of the visible levels and the ellipsis button |
239
+ | <kbd>Enter</kbd> | Follows the focused link |
240
+ | <kbd>Enter</kbd> / <kbd>Space</kbd> | On the ellipsis button: shows the hidden levels and focuses the first of them |
241
+
242
+ ## Inputs
243
+
244
+ | Name | Type | Default | Description |
245
+ | ---------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
246
+ | `label` | `string` | `''` | Accessible name of the navigation; empty falls back to `provideBfLabels()`'s `breadcrumb` ("Breadcrumb"). |
247
+ | `maxItems` | `number` | `0` | Collapse a trail longer than this: keep the first level and the last `maxItems - 1` and hide the middle ones behind an ellipsis. `0` never collapses; values below 2 act as 2, so the current page always stays. |
248
+
249
+ ## Slots
250
+
251
+ - `overflow`: Replaces the ellipsis button of a collapsed trail, e.g. a menu of the hidden levels.
252
+ - `default`: The levels, each a `li[bfBreadcrumbItem]`, the current page last.
253
+
254
+ ## Notes
255
+
256
+ ### Do
257
+
258
+ - Keep the labels short, the page titles users know, and the current page last as plain text.
259
+ - Collapse a trail deeper than four or five levels with `maxItems`.
260
+
261
+ ### Don't
262
+
263
+ - Don't link the current page, and don't put anything but `li[bfBreadcrumbItem]` levels (and the `overflow` slot) inside `bf-breadcrumb`.
264
+ - Don't use a breadcrumb as the app's main navigation, and don't show it in the Capacitor shell.
265
+
266
+ ## Migration notes
267
+
268
+ ### 0.3.1
269
+
270
+ New `bf-breadcrumb` in `@bitforgehq/ui`, the desktop breadcrumb trail: a `nav` (named by
271
+ `label`, default `provideBfLabels()`'s new `breadcrumb` key, "Breadcrumb") around an `ol` of
272
+ `li[bfBreadcrumbItem]` levels, with CSS chevrons between them that are never announced.
273
+ `maxItems` collapses a longer trail to the first level and the last `maxItems - 1`, behind an
274
+ ellipsis button (the new `breadcrumbExpand` label, "Show 2 more levels") that shows the hidden
275
+ levels in place and focuses the first one; `bfSlot="overflow"` replaces that button with the app's
276
+ own control, such as a `bf-menu-button` of the hidden levels. It replaces the hand-built
277
+ breadcrumb the back-button docs showed.