@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.
- package/CHANGELOG.md +482 -0
- package/README.md +17 -11
- package/ai/MANIFEST.index.json +288 -0
- package/ai/MANIFEST.json +8716 -3926
- package/ai/REFERENCE.index.json +413 -0
- package/ai/REFERENCE.json +4710 -2047
- package/ai/UI_GUIDE.md +185 -57
- package/ai/components/back-button.md +42 -2
- package/ai/components/badge.md +17 -7
- package/ai/components/bar-chart.md +188 -0
- package/ai/components/breadcrumb-item.md +121 -0
- package/ai/components/breadcrumb.md +277 -0
- package/ai/components/button.md +82 -15
- package/ai/components/calendar.md +300 -0
- package/ai/components/card.md +63 -2
- package/ai/components/chart-card.md +283 -0
- package/ai/components/combobox.md +411 -0
- package/ai/components/date-input.md +396 -0
- package/ai/components/datetime-input.md +215 -0
- package/ai/components/description-item.md +111 -0
- package/ai/components/description-list.md +160 -0
- package/ai/components/donut-chart.md +169 -0
- package/ai/components/header.md +67 -2
- package/ai/components/icon-button.md +74 -16
- package/ai/components/icon.md +63 -7
- package/ai/components/line-chart.md +234 -0
- package/ai/components/list.md +70 -6
- package/ai/components/menu.md +5 -0
- package/ai/components/multi-combobox.md +257 -0
- package/ai/components/nav-group.md +178 -0
- package/ai/components/nav-item.md +210 -0
- package/ai/components/page-header.md +237 -0
- package/ai/components/pagination.md +173 -0
- package/ai/components/popover.md +250 -0
- package/ai/components/side-panel.md +391 -0
- package/ai/components/sidebar.md +363 -0
- package/ai/components/skeleton.md +215 -0
- package/ai/components/sparkline.md +188 -0
- package/ai/components/stat.md +233 -0
- package/ai/components/tab-group.md +226 -0
- package/ai/components/tab-panel.md +163 -0
- package/ai/components/table.md +568 -0
- package/ai/components/tabs.md +42 -2
- package/ai/components/theme-menu-button.md +182 -0
- package/ai/components/theme-switch.md +170 -0
- package/ai/components/time-input.md +240 -0
- package/ai/components/toolbar.md +289 -0
- package/ai/components/tooltip.md +230 -0
- package/ai/components/top-nav.md +159 -0
- package/ai/reference/bf-badge-appearance.md +36 -0
- package/ai/reference/bf-badge-variant.md +1 -1
- package/ai/reference/bf-bar-chart-orientation.md +27 -0
- package/ai/reference/bf-button-appearance.md +36 -0
- package/ai/reference/bf-button-variant.md +1 -1
- package/ai/reference/bf-calendar-disabled-date.md +23 -0
- package/ai/reference/bf-calendar-harness.md +51 -0
- package/ai/reference/bf-calendar-weekday.md +31 -0
- package/ai/reference/bf-chart-card-heading-level.md +26 -0
- package/ai/reference/bf-chart-curve.md +28 -0
- package/ai/reference/bf-chart-harness.md +51 -0
- package/ai/reference/bf-chart-legend-position.md +30 -0
- package/ai/reference/bf-chart-series.md +23 -0
- package/ai/reference/bf-chart-value-format.md +23 -0
- package/ai/reference/bf-color-scheme-service.md +30 -0
- package/ai/reference/bf-color-scheme.md +31 -0
- package/ai/reference/bf-combobox-filter.md +27 -0
- package/ai/reference/bf-combobox-harness.md +89 -0
- package/ai/reference/bf-combobox-option.md +33 -0
- package/ai/reference/bf-compare-with.md +2 -0
- package/ai/reference/bf-date-input-harness.md +67 -0
- package/ai/reference/bf-date-input-picker.md +27 -0
- package/ai/reference/bf-date-time-input-harness.md +76 -0
- package/ai/reference/bf-date-time-input-picker.md +27 -0
- package/ai/reference/bf-description-list-layout.md +26 -0
- package/ai/reference/bf-donut-slice.md +22 -0
- package/ai/reference/bf-labels.md +67 -20
- package/ai/reference/bf-menu-button-variant.md +1 -0
- package/ai/reference/bf-menu-placement.md +1 -0
- package/ai/reference/bf-overlay-options.md +28 -27
- package/ai/reference/bf-overlay-placement.md +34 -0
- package/ai/reference/bf-overlay-position.md +1 -1
- package/ai/reference/bf-overlay-service.md +5 -3
- package/ai/reference/bf-page-header-heading-level.md +26 -0
- package/ai/reference/bf-pagination-harness.md +61 -0
- package/ai/reference/bf-platform-service.md +17 -15
- package/ai/reference/bf-popover-harness.md +42 -0
- package/ai/reference/bf-popover-placement.md +28 -0
- package/ai/reference/bf-popover-trigger-directive.md +56 -0
- package/ai/reference/bf-side-panel-config.md +35 -0
- package/ai/reference/bf-side-panel-harness.md +34 -0
- package/ai/reference/bf-side-panel-ref.md +44 -0
- package/ai/reference/bf-side-panel-service.md +43 -0
- package/ai/reference/bf-side-panel-template-context.md +32 -0
- package/ai/reference/bf-side-panel-width.md +28 -0
- package/ai/reference/bf-size.md +12 -0
- package/ai/reference/bf-skeleton-shape.md +28 -0
- package/ai/reference/bf-stat-trend-intent.md +27 -0
- package/ai/reference/bf-stat-trend.md +27 -0
- package/ai/reference/bf-stat-variant.md +26 -0
- package/ai/reference/bf-tab-group-harness.md +44 -0
- package/ai/reference/bf-table-align.md +26 -0
- package/ai/reference/bf-table-cell-context.md +24 -0
- package/ai/reference/bf-table-cell-directive.md +22 -0
- package/ai/reference/bf-table-column.md +28 -0
- package/ai/reference/bf-table-harness.md +77 -0
- package/ai/reference/bf-table-row-key.md +21 -0
- package/ai/reference/bf-table-sort-direction.md +26 -0
- package/ai/reference/bf-table-sort.md +27 -0
- package/ai/reference/bf-theme.md +5 -4
- package/ai/reference/bf-time-input-harness.md +67 -0
- package/ai/reference/bf-time-input-hour-cycle.md +27 -0
- package/ai/reference/bf-time-input-picker.md +27 -0
- package/ai/reference/bf-tooltip-directive.md +43 -0
- package/ai/reference/bf-tooltip-harness.md +43 -0
- package/ai/reference/bf-tooltip-placement.md +28 -0
- package/ai/reference/bf_color_scheme_storage.md +20 -0
- package/ai/reference/bf_side_panel_data.md +17 -0
- package/ai/reference/bf_theme_storage.md +3 -1
- package/ai/reference/provide-bf-color-scheme.md +20 -0
- package/ai/reference/provide-bf-labels.md +12 -4
- package/ai/reference/slide-in-from-end.md +24 -0
- package/ai/reference/slide-out-to-end.md +24 -0
- package/fesm2022/bitforgehq-ui-charts.mjs +3045 -0
- package/fesm2022/bitforgehq-ui-charts.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-icons.mjs +107 -3
- package/fesm2022/bitforgehq-ui-icons.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui-mobile.mjs +14 -59
- package/fesm2022/bitforgehq-ui-mobile.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui-overlay.mjs +6436 -33
- package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui-testing.mjs +618 -1
- package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui.mjs +5092 -153
- package/fesm2022/bitforgehq-ui.mjs.map +1 -1
- package/llms-full.txt +25177 -14398
- package/llms.txt +95 -1
- package/package.json +7 -3
- package/theme.css +101 -0
- package/types/bitforgehq-ui-charts.d.ts +967 -0
- package/types/bitforgehq-ui-icons.d.ts +13 -1
- package/types/bitforgehq-ui-mobile.d.ts +25 -18
- package/types/bitforgehq-ui-overlay.d.ts +1844 -7
- package/types/bitforgehq-ui-testing.d.ts +748 -1
- package/types/bitforgehq-ui.d.ts +2136 -44
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# bf-toolbar
|
|
2
|
+
|
|
3
|
+
Dense row of search, filters, view switch, result count and actions
|
|
4
|
+
|
|
5
|
+
`BfToolbarComponent` from `@bitforgehq/ui` · Actions · since 0.3.1
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Component, computed, signal } from '@angular/core';
|
|
11
|
+
import {
|
|
12
|
+
BfButtonComponent,
|
|
13
|
+
BfInputComponent,
|
|
14
|
+
BfSegmentedComponent,
|
|
15
|
+
BfSelectComponent,
|
|
16
|
+
BfToolbarComponent,
|
|
17
|
+
type BfSegmentOption,
|
|
18
|
+
type BfSelectOption,
|
|
19
|
+
} from '@bitforgehq/ui';
|
|
20
|
+
|
|
21
|
+
type ProjectStatus = 'active' | 'paused' | 'done';
|
|
22
|
+
type StatusFilter = ProjectStatus | 'all';
|
|
23
|
+
type View = 'list' | 'grid';
|
|
24
|
+
|
|
25
|
+
interface Project {
|
|
26
|
+
readonly name: string;
|
|
27
|
+
readonly status: ProjectStatus;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The bf-toolbar overview.
|
|
32
|
+
*
|
|
33
|
+
* Live demo of `bf-toolbar`: a search field and a status filter narrow a list of projects, a
|
|
34
|
+
* view switch lays it out as a list or a grid, the toolbar's status announces the count, and a
|
|
35
|
+
* primary action adds a project. Every control in the row is `sm` from the toolbar's density.
|
|
36
|
+
*/
|
|
37
|
+
@Component({
|
|
38
|
+
selector: 'app-example-toolbar-overview',
|
|
39
|
+
imports: [
|
|
40
|
+
BfButtonComponent,
|
|
41
|
+
BfInputComponent,
|
|
42
|
+
BfSegmentedComponent,
|
|
43
|
+
BfSelectComponent,
|
|
44
|
+
BfToolbarComponent,
|
|
45
|
+
],
|
|
46
|
+
template: `
|
|
47
|
+
<div class="flex flex-col gap-3">
|
|
48
|
+
<bf-toolbar label="Project filters" [status]="count()">
|
|
49
|
+
<bf-input
|
|
50
|
+
type="search"
|
|
51
|
+
ariaLabel="Search projects"
|
|
52
|
+
placeholder="Search projects"
|
|
53
|
+
class="w-full sm:w-56"
|
|
54
|
+
[(value)]="query"
|
|
55
|
+
/>
|
|
56
|
+
<bf-select
|
|
57
|
+
ariaLabel="Status"
|
|
58
|
+
class="w-full sm:w-40"
|
|
59
|
+
[options]="statuses"
|
|
60
|
+
[(value)]="filter"
|
|
61
|
+
/>
|
|
62
|
+
<bf-segmented bfSlot="view" label="View" [options]="views" [(value)]="view" />
|
|
63
|
+
<bf-button bfSlot="actions" (clicked)="add()">New project</bf-button>
|
|
64
|
+
</bf-toolbar>
|
|
65
|
+
<ul [class]="view() === 'grid' ? 'grid grid-cols-2 gap-2' : 'flex flex-col gap-1'">
|
|
66
|
+
@for (project of visible(); track project.name) {
|
|
67
|
+
<li class="bg-surface rounded-field text-body px-3 py-2">{{ project.name }}</li>
|
|
68
|
+
}
|
|
69
|
+
</ul>
|
|
70
|
+
<p data-status aria-live="polite" class="text-caption text-text-muted">{{ status() }}</p>
|
|
71
|
+
</div>
|
|
72
|
+
`,
|
|
73
|
+
})
|
|
74
|
+
export class ToolbarOverviewExampleComponent {
|
|
75
|
+
protected readonly query = signal('');
|
|
76
|
+
protected readonly filter = signal<StatusFilter | null>('all');
|
|
77
|
+
protected readonly view = signal<View | null>('list');
|
|
78
|
+
protected readonly projects = signal<readonly Project[]>([
|
|
79
|
+
{ name: 'Atlas rebrand', status: 'active' },
|
|
80
|
+
{ name: 'Billing revamp', status: 'active' },
|
|
81
|
+
{ name: 'Carbon audit', status: 'paused' },
|
|
82
|
+
{ name: 'Docs refresh', status: 'done' },
|
|
83
|
+
{ name: 'Onboarding flow', status: 'active' },
|
|
84
|
+
]);
|
|
85
|
+
|
|
86
|
+
protected readonly statuses: BfSelectOption<StatusFilter>[] = [
|
|
87
|
+
{ value: 'all', label: 'All statuses' },
|
|
88
|
+
{ value: 'active', label: 'Active' },
|
|
89
|
+
{ value: 'paused', label: 'Paused' },
|
|
90
|
+
{ value: 'done', label: 'Done' },
|
|
91
|
+
];
|
|
92
|
+
protected readonly views: BfSegmentOption<View>[] = [
|
|
93
|
+
{ value: 'list', label: 'List' },
|
|
94
|
+
{ value: 'grid', label: 'Grid' },
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
protected readonly visible = computed(() => {
|
|
98
|
+
const query = this.query().trim().toLowerCase();
|
|
99
|
+
const filter = this.filter() ?? 'all';
|
|
100
|
+
return this.projects().filter(
|
|
101
|
+
(p) =>
|
|
102
|
+
(filter === 'all' || p.status === filter) &&
|
|
103
|
+
(!query || p.name.toLowerCase().includes(query)),
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
protected readonly count = computed(() => {
|
|
108
|
+
const n = this.visible().length;
|
|
109
|
+
return n === 1 ? '1 project' : `${n} projects`;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
protected readonly status = computed(
|
|
113
|
+
() => `Showing ${this.visible().length} of ${this.projects().length} · ${this.view()} view`,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
protected add(): void {
|
|
117
|
+
const next = this.projects().length + 1;
|
|
118
|
+
this.projects.update((list) => [...list, { name: `Project ${next}`, status: 'active' }]);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Overview
|
|
124
|
+
|
|
125
|
+
Row of search, filters, a view switch and trailing actions above a list or table. Every
|
|
126
|
+
control projected into it is `sm` unless it sets `size` itself.
|
|
127
|
+
|
|
128
|
+
A dense row above a list or table: the search field and filters, a view switch, a live result count and the trailing actions. `bf-toolbar` lays the row out, wraps it in a narrow column and makes every control in it `sm`; the controls themselves are ordinary `bf-input`, `bf-select`, `bf-chip-group`, `bf-segmented` and `bf-button`s.
|
|
129
|
+
|
|
130
|
+
## When to use
|
|
131
|
+
|
|
132
|
+
Use `bf-toolbar` on a desktop page for the controls that narrow or act on the collection below it: search, filters, list or grid, "New". Use [bf-action-bar](https://bitforge-ui.web.app/components/action-bar) instead for actions on a selection while it lasts. In the Capacitor shell, put the main action in [bf-header](https://bitforge-ui.web.app/components/header) actions and move filters into a [bf-sheet](https://bitforge-ui.web.app/components/sheet) instead.
|
|
133
|
+
|
|
134
|
+
## Layout
|
|
135
|
+
|
|
136
|
+
Project the search field and the filters as content, in the order they read. `bfSlot="view"` takes a view switch after them, and `bfSlot="actions"` the trailing actions, pushed to the inline end. Each field needs its own `ariaLabel` (or `label`), since the row shows no field labels. Bind `status` to the result count.
|
|
137
|
+
|
|
138
|
+
## Status
|
|
139
|
+
|
|
140
|
+
`status` renders a polite live region (`role="status"`) after the view switch, in muted caption text. Keep it short and countable ("24 results", "No matches"); it is announced each time it changes, without moving focus. The region is always in the DOM, so the first count after an empty one is announced too; while it is empty it takes no space.
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
import { Component, computed, signal } from '@angular/core';
|
|
144
|
+
import { BfChipComponent, BfChipGroupComponent, BfToolbarComponent } from '@bitforgehq/ui';
|
|
145
|
+
|
|
146
|
+
type Filter = 'all' | 'open' | 'done';
|
|
147
|
+
|
|
148
|
+
interface Task {
|
|
149
|
+
readonly title: string;
|
|
150
|
+
readonly done: boolean;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* A result count in the status line.
|
|
155
|
+
*
|
|
156
|
+
* The toolbar's `status` is a polite live region: picking a filter changes the count, and a
|
|
157
|
+
* screen reader announces it without moving focus.
|
|
158
|
+
*/
|
|
159
|
+
@Component({
|
|
160
|
+
selector: 'app-example-toolbar-status',
|
|
161
|
+
imports: [BfChipComponent, BfChipGroupComponent, BfToolbarComponent],
|
|
162
|
+
template: `
|
|
163
|
+
<bf-toolbar label="Task filters" [status]="count()">
|
|
164
|
+
<bf-chip-group label="Status" [(value)]="filter">
|
|
165
|
+
<bf-chip variant="ink" value="all">All</bf-chip>
|
|
166
|
+
<bf-chip variant="ink" value="open">Open</bf-chip>
|
|
167
|
+
<bf-chip variant="ink" value="done">Done</bf-chip>
|
|
168
|
+
</bf-chip-group>
|
|
169
|
+
</bf-toolbar>
|
|
170
|
+
`,
|
|
171
|
+
})
|
|
172
|
+
export class ToolbarStatusExampleComponent {
|
|
173
|
+
protected readonly filter = signal<Filter | null>('all');
|
|
174
|
+
private readonly tasks: readonly Task[] = [
|
|
175
|
+
{ title: 'Draft the brief', done: true },
|
|
176
|
+
{ title: 'Review the logo', done: false },
|
|
177
|
+
{ title: 'Book the venue', done: false },
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
protected readonly count = computed(() => {
|
|
181
|
+
const filter = this.filter() ?? 'all';
|
|
182
|
+
const n = this.tasks.filter((t) => filter === 'all' || t.done === (filter === 'done')).length;
|
|
183
|
+
return n === 1 ? '1 task' : `${n} tasks`;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Wrapping
|
|
189
|
+
|
|
190
|
+
The row is a wrapping flex row with an 8 px gap between items and between rows. In a narrow column the filters wrap onto new rows, and the actions wrap as one group, kept together at the inline end. Give a search field a width (`class="w-full sm:w-56"`) so it fills a phone-wide row and stays compact on a wide one.
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
import { Component } from '@angular/core';
|
|
194
|
+
import {
|
|
195
|
+
BfButtonComponent,
|
|
196
|
+
BfInputComponent,
|
|
197
|
+
BfSelectComponent,
|
|
198
|
+
BfToolbarComponent,
|
|
199
|
+
type BfSelectOption,
|
|
200
|
+
} from '@bitforgehq/ui';
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* A toolbar that wraps.
|
|
204
|
+
*
|
|
205
|
+
* In a narrow column the filters wrap onto new rows 8 px apart, and the actions move down
|
|
206
|
+
* together, still at the inline end.
|
|
207
|
+
*/
|
|
208
|
+
@Component({
|
|
209
|
+
selector: 'app-example-toolbar-wrapping',
|
|
210
|
+
imports: [BfButtonComponent, BfInputComponent, BfSelectComponent, BfToolbarComponent],
|
|
211
|
+
template: `
|
|
212
|
+
<div class="max-w-sm">
|
|
213
|
+
<bf-toolbar label="Invoice filters" status="18 invoices">
|
|
214
|
+
<bf-input type="search" ariaLabel="Search invoices" placeholder="Search invoices" />
|
|
215
|
+
<bf-select ariaLabel="Customer" [options]="customers" value="all" />
|
|
216
|
+
<bf-select ariaLabel="Period" [options]="periods" value="month" />
|
|
217
|
+
<bf-button bfSlot="actions" variant="outline">Export</bf-button>
|
|
218
|
+
<bf-button bfSlot="actions">New invoice</bf-button>
|
|
219
|
+
</bf-toolbar>
|
|
220
|
+
</div>
|
|
221
|
+
`,
|
|
222
|
+
})
|
|
223
|
+
export class ToolbarWrappingExampleComponent {
|
|
224
|
+
protected readonly customers: BfSelectOption[] = [
|
|
225
|
+
{ value: 'all', label: 'All customers' },
|
|
226
|
+
{ value: 'acme', label: 'Acme' },
|
|
227
|
+
];
|
|
228
|
+
protected readonly periods: BfSelectOption[] = [
|
|
229
|
+
{ value: 'month', label: 'This month' },
|
|
230
|
+
{ value: 'year', label: 'This year' },
|
|
231
|
+
];
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Desktop
|
|
236
|
+
|
|
237
|
+
`bf-toolbar` is a desktop pattern. Every control and field projected into it resolves its unset `size` to `sm` (32 px tall), because its `providers` set `provideBfDefaults({ size: 'sm' })` for its content; a control that sets `size` itself keeps it, and nothing outside the toolbar changes ([density guide](https://bitforge-ui.web.app/density)). Hover, the pointer cursor and the `focus-visible` ring come from each control; the row adds none of its own. Under a fine pointer each control's visual box is its hit area; under a coarse pointer the `sm` touch extensions reach up to 7.5 px past the visual edge, which the 8 px gap keeps off the neighbouring row.
|
|
238
|
+
|
|
239
|
+
Place it directly under `bf-page-header` and above the `bf-table` or `bf-list` it filters, with the page's own vertical gap (`gap-4`) between them: the page header names the page and holds its page-level actions, the toolbar holds what narrows the collection, and the count in `status` describes the rows below.
|
|
240
|
+
|
|
241
|
+
## Theming
|
|
242
|
+
|
|
243
|
+
The row has no surface, border or radius of its own; it sits on the page background, and each projected control themes itself.
|
|
244
|
+
|
|
245
|
+
- Status text: `text-caption` in `text-muted`.
|
|
246
|
+
- Layout: `gap-2` (8 px) between items and rows, in logical properties, so under `dir="rtl"` the row mirrors and the actions sit at the left end.
|
|
247
|
+
|
|
248
|
+
## Accessibility
|
|
249
|
+
|
|
250
|
+
The host is `role="group"`, named by `aria-label` from `label` ("Project filters"). It is a group, not `role="toolbar"`: that role promises arrow-key movement between its controls with one tab stop, and the search field needs the arrow keys to move its caret, so each control keeps its own tab stop and its own keys. Without `label` the group is left unnamed rather than given a generic default: a page has one toolbar under a heading that already names the context, and a stock name such as "Toolbar" would add noise to every focus entry without telling the user anything. Pass a `label` when the page has more than one toolbar.
|
|
251
|
+
|
|
252
|
+
The `status` element is `role="status"` (an `aria-live="polite"` region), always rendered, so a changed count is read after the user's current input finishes. The toolbar sets no focus and handles no keys; <kbd>Tab</kbd> moves through the projected controls in DOM order (filters, view switch, actions).
|
|
253
|
+
|
|
254
|
+
## Inputs
|
|
255
|
+
|
|
256
|
+
| Name | Type | Default | Description |
|
|
257
|
+
| -------- | -------- | ------- | -------------------------------------------------------------------------------------- |
|
|
258
|
+
| `label` | `string` | `''` | The group's accessible name, such as "Project filters"; unnamed when empty. |
|
|
259
|
+
| `status` | `string` | `''` | The result count or a short state, such as "24 results", announced politely on change. |
|
|
260
|
+
|
|
261
|
+
## Slots
|
|
262
|
+
|
|
263
|
+
- `default`: The search field and the filters (bf-input, bf-select, bf-chip-group), in order.
|
|
264
|
+
- `view`: A view switch after the filters, such as a bf-segmented of list and grid.
|
|
265
|
+
- `actions`: The trailing actions, kept together at the inline end of the row.
|
|
266
|
+
|
|
267
|
+
## Notes
|
|
268
|
+
|
|
269
|
+
### Do
|
|
270
|
+
|
|
271
|
+
- Name each field with `ariaLabel` (or a `label`), and keep the order: search, filters, view, actions.
|
|
272
|
+
- Bind `status` to the number of rows the filters leave, and update it as they change.
|
|
273
|
+
|
|
274
|
+
### Don't
|
|
275
|
+
|
|
276
|
+
- Don't put the page's title or page-level actions in it; they belong in `bf-page-header`.
|
|
277
|
+
- Don't use it in the Capacitor shell; use `bf-header` actions and a `bf-sheet` of filters.
|
|
278
|
+
|
|
279
|
+
## Migration notes
|
|
280
|
+
|
|
281
|
+
### 0.3.1
|
|
282
|
+
|
|
283
|
+
New `bf-toolbar` in `@bitforgehq/ui`: a wrapping `role="group"` row for the search field and
|
|
284
|
+
filters (default slot), a view switch (`bfSlot="view"`) and the trailing actions
|
|
285
|
+
(`bfSlot="actions"`, kept together at the inline end). `label` names the group; `status` renders
|
|
286
|
+
a polite `role="status"` live region for the result count. Its providers set
|
|
287
|
+
`provideBfDefaults({ size: 'sm' })`, so every projected control and field is `sm` unless it sets
|
|
288
|
+
`size` itself. Desktop only; in the Capacitor shell use `bf-header` actions and a `bf-sheet` of
|
|
289
|
+
filters.
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# bf-tooltip
|
|
2
|
+
|
|
3
|
+
Short hint beside a control on hover or keyboard focus, for desktop
|
|
4
|
+
|
|
5
|
+
`BfTooltipComponent` from `@bitforgehq/ui/overlay` · Overlays · since 0.3.1
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Component, signal } from '@angular/core';
|
|
11
|
+
import { BfIconButtonComponent } from '@bitforgehq/ui';
|
|
12
|
+
import { BfIconComponent } from '@bitforgehq/ui/icons';
|
|
13
|
+
import { BfTooltipDirective } from '@bitforgehq/ui/overlay';
|
|
14
|
+
import { Bold, Italic, Link, Underline } from 'lucide';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The bf-tooltip overview.
|
|
18
|
+
*
|
|
19
|
+
* Live demo of `bfTooltip`: a formatting toolbar of icon buttons, each named by its `label` and
|
|
20
|
+
* showing the same name, with its keyboard shortcut, in a tooltip on hover or keyboard focus. The
|
|
21
|
+
* result line reports each press.
|
|
22
|
+
*/
|
|
23
|
+
@Component({
|
|
24
|
+
selector: 'app-example-tooltip-overview',
|
|
25
|
+
imports: [BfIconButtonComponent, BfIconComponent, BfTooltipDirective],
|
|
26
|
+
template: `
|
|
27
|
+
<div class="flex flex-col gap-3">
|
|
28
|
+
<div role="group" aria-label="Formatting" class="flex flex-wrap items-center gap-2">
|
|
29
|
+
<bf-icon-button
|
|
30
|
+
label="Bold"
|
|
31
|
+
variant="text"
|
|
32
|
+
bfTooltip="Bold (Ctrl+B)"
|
|
33
|
+
(clicked)="press('Bold')"
|
|
34
|
+
>
|
|
35
|
+
<bf-icon [icon]="bold" />
|
|
36
|
+
</bf-icon-button>
|
|
37
|
+
<bf-icon-button
|
|
38
|
+
label="Italic"
|
|
39
|
+
variant="text"
|
|
40
|
+
bfTooltip="Italic (Ctrl+I)"
|
|
41
|
+
(clicked)="press('Italic')"
|
|
42
|
+
>
|
|
43
|
+
<bf-icon [icon]="italic" />
|
|
44
|
+
</bf-icon-button>
|
|
45
|
+
<bf-icon-button
|
|
46
|
+
label="Underline"
|
|
47
|
+
variant="text"
|
|
48
|
+
bfTooltip="Underline (Ctrl+U)"
|
|
49
|
+
(clicked)="press('Underline')"
|
|
50
|
+
>
|
|
51
|
+
<bf-icon [icon]="underline" />
|
|
52
|
+
</bf-icon-button>
|
|
53
|
+
<bf-icon-button
|
|
54
|
+
label="Insert link"
|
|
55
|
+
variant="text"
|
|
56
|
+
bfTooltip="Insert link (Ctrl+K)"
|
|
57
|
+
(clicked)="press('Insert link')"
|
|
58
|
+
>
|
|
59
|
+
<bf-icon [icon]="link" />
|
|
60
|
+
</bf-icon-button>
|
|
61
|
+
</div>
|
|
62
|
+
<p data-status aria-live="polite" class="text-caption text-text-muted">{{ status() }}</p>
|
|
63
|
+
</div>
|
|
64
|
+
`,
|
|
65
|
+
})
|
|
66
|
+
export class TooltipOverviewExampleComponent {
|
|
67
|
+
readonly bold = Bold;
|
|
68
|
+
readonly italic = Italic;
|
|
69
|
+
readonly underline = Underline;
|
|
70
|
+
readonly link = Link;
|
|
71
|
+
readonly status = signal('Hover or tab to a button for its tooltip.');
|
|
72
|
+
|
|
73
|
+
press(label: string): void {
|
|
74
|
+
this.status.set(`Pressed ${label}.`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Overview
|
|
80
|
+
|
|
81
|
+
The small dark pill of a tooltip (role `tooltip`). A `[bfTooltip]` directive renders it in the
|
|
82
|
+
CDK overlay beside its element while the pointer rests on it or it has keyboard focus, so you
|
|
83
|
+
rarely place it yourself; the directive is how you add a tooltip.
|
|
84
|
+
|
|
85
|
+
Short hints for desktop and web apps, from `@bitforgehq/ui/overlay` (needs `@angular/cdk`, no
|
|
86
|
+
Capacitor). Put `bfTooltip="Text"` on a control and a small dark `bf-tooltip` pill shows beside it
|
|
87
|
+
when a mouse rests on it for half a second, or at once when it gets keyboard focus. It hides when
|
|
88
|
+
the pointer leaves, on blur and on `Escape`. While it shows, the control is described by it
|
|
89
|
+
(`aria-describedby`), so a screen reader reads the hint after the control's name. The pill
|
|
90
|
+
renders in the CDK overlay container, so it works inside a `BfDialogService` dialog.
|
|
91
|
+
|
|
92
|
+
Import `BfTooltipDirective` from `@bitforgehq/ui/overlay`. You rarely place `bf-tooltip`
|
|
93
|
+
(`BfTooltipComponent`) yourself: the directive renders it.
|
|
94
|
+
|
|
95
|
+
## When to use
|
|
96
|
+
|
|
97
|
+
Use a tooltip to repeat the name of an icon-only control (`bf-icon-button`, a collapsed sidebar
|
|
98
|
+
link) for mouse users, or to add a keyboard shortcut to it. A tooltip never holds information
|
|
99
|
+
found nowhere else: touch screens never show it, so the control's own name and the page must say
|
|
100
|
+
everything that matters. Use [bf-popover](https://bitforge-ui.web.app/components/popover) instead for content a user reads at
|
|
101
|
+
length or interacts with (links, fields, buttons), [bf-field](https://bitforge-ui.web.app/components/field) help text for
|
|
102
|
+
guidance on a form field, and in a Capacitor app the control's visible label.
|
|
103
|
+
|
|
104
|
+
## Placement
|
|
105
|
+
|
|
106
|
+
`bfTooltipPlacement` puts the tooltip on the `top` (default), `bottom`, `start` or `end` side of
|
|
107
|
+
its element, centred along it, 8 px away. `start` and `end` follow the text direction, so they
|
|
108
|
+
swap sides in right-to-left pages. A tooltip without room on its side flips to the opposite side
|
|
109
|
+
first, then to the other two. The placement is read each time the tooltip shows.
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { Component } from '@angular/core';
|
|
113
|
+
import { BfButtonComponent } from '@bitforgehq/ui';
|
|
114
|
+
import { BfTooltipDirective, type BfTooltipPlacement } from '@bitforgehq/ui/overlay';
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Tooltips on each side of their button.
|
|
118
|
+
*
|
|
119
|
+
* `bfTooltipPlacement` puts the tooltip on the `top` (default), `bottom`, `start` or `end` side;
|
|
120
|
+
* `start` and `end` follow the text direction, and a tooltip without room flips to the other side.
|
|
121
|
+
*/
|
|
122
|
+
@Component({
|
|
123
|
+
selector: 'app-example-tooltip-placement',
|
|
124
|
+
imports: [BfButtonComponent, BfTooltipDirective],
|
|
125
|
+
template: `
|
|
126
|
+
<div class="flex flex-wrap items-center justify-center gap-3 py-8">
|
|
127
|
+
@for (side of sides; track side) {
|
|
128
|
+
<bf-button
|
|
129
|
+
variant="outline"
|
|
130
|
+
size="sm"
|
|
131
|
+
[bfTooltip]="'Shown on the ' + side + ' side'"
|
|
132
|
+
[bfTooltipPlacement]="side"
|
|
133
|
+
>
|
|
134
|
+
{{ side }}
|
|
135
|
+
</bf-button>
|
|
136
|
+
}
|
|
137
|
+
</div>
|
|
138
|
+
`,
|
|
139
|
+
})
|
|
140
|
+
export class TooltipPlacementExampleComponent {
|
|
141
|
+
protected readonly sides: readonly BfTooltipPlacement[] = ['top', 'bottom', 'start', 'end'];
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Text
|
|
146
|
+
|
|
147
|
+
Keep the text to a few words on one line: the name of the control, and maybe its shortcut
|
|
148
|
+
("Bold (Ctrl+B)"). Longer text wraps at 20 rem. A bound text that changes while the tooltip shows
|
|
149
|
+
("Copy link", then "Copied") updates it in place; an empty text shows no tooltip.
|
|
150
|
+
|
|
151
|
+
On an icon-only `bf-icon-button` the tooltip repeats its `label`: the `label` stays the accessible
|
|
152
|
+
name, and the tooltip describes the button. A collapsed `bf-sidebar` link (`a[bfNavItem]`) shows
|
|
153
|
+
its name in the browser's `title` tooltip; add `bfTooltip` with the same text and
|
|
154
|
+
`bfTooltipPlacement="end"` for a styled one that also shows on keyboard focus. Bind it to an empty
|
|
155
|
+
text while the sidebar is expanded, where the label is visible.
|
|
156
|
+
|
|
157
|
+
## Desktop
|
|
158
|
+
|
|
159
|
+
Built for a mouse and a keyboard. Hovering shows the tooltip after a 500 ms rest, so a pointer
|
|
160
|
+
that only passes over a toolbar shows nothing; moving the pointer from the control onto the
|
|
161
|
+
tooltip keeps it open (so it can be read under a magnifier). Keyboard focus (`focus-visible`)
|
|
162
|
+
shows it at once; a mouse click that focuses the control does not. The tooltip fades in over
|
|
163
|
+
120 ms, without the fade when the system asks for reduced motion.
|
|
164
|
+
|
|
165
|
+
Under a coarse pointer (`(pointer: coarse)`, touch screens and the Capacitor shell) no tooltip
|
|
166
|
+
ever shows: there is no hover there, and a tooltip after a tap would sit under the finger. That
|
|
167
|
+
is why a tooltip never carries information found nowhere else.
|
|
168
|
+
|
|
169
|
+
## Theming
|
|
170
|
+
|
|
171
|
+
- Pill: `bg-tooltip` with `text-on-tooltip` (14.9:1 in light, 13.8:1 in dark; the pill is dark
|
|
172
|
+
on a light page and light on a dark one), `rounded-field`, `shadow-raised`, `text-label-xs`,
|
|
173
|
+
10 px by 4 px padding, at most 20 rem wide.
|
|
174
|
+
- Under forced colours (Windows High Contrast) the pill drops its fill and draws a 1 px border in
|
|
175
|
+
the text colour.
|
|
176
|
+
|
|
177
|
+
## Accessibility
|
|
178
|
+
|
|
179
|
+
- The pill is `role="tooltip"` with an id. While it shows, the control (the host, or the first
|
|
180
|
+
control inside it, such as the button of a `bf-icon-button`) gets that id added to its
|
|
181
|
+
`aria-describedby`, next to any ids it already has; the id is removed when the tooltip hides.
|
|
182
|
+
- A tooltip is a description, never the name: an icon-only button keeps its `label` or
|
|
183
|
+
`aria-label`.
|
|
184
|
+
- WCAG 1.4.13 (content on hover or focus): the tooltip can be dismissed with `Escape` without
|
|
185
|
+
moving the pointer or focus, stays while the pointer is over it, and stays until the pointer
|
|
186
|
+
leaves, focus leaves or `Escape` is pressed.
|
|
187
|
+
- `Escape` goes to the tooltip first: inside a dialog or a popover, the first `Escape` hides the
|
|
188
|
+
tooltip and the next one closes the dialog.
|
|
189
|
+
- A disabled native button fires no pointer events, so its tooltip never shows; explain why a
|
|
190
|
+
control is disabled in the page instead.
|
|
191
|
+
|
|
192
|
+
### Keyboard
|
|
193
|
+
|
|
194
|
+
| Key | Action |
|
|
195
|
+
| ------------------------------------------------ | -------------------------------------------------------- |
|
|
196
|
+
| <kbd>Tab</kbd> / <kbd>Shift</kbd>+<kbd>Tab</kbd> | Focusing the control shows its tooltip; leaving hides it |
|
|
197
|
+
| <kbd>Escape</kbd> | Hides the tooltip, focus stays where it is |
|
|
198
|
+
|
|
199
|
+
## Inputs
|
|
200
|
+
|
|
201
|
+
| Name | Type | Default | Description |
|
|
202
|
+
| ------ | -------- | ------- | -------------------------------------------------------------------------- |
|
|
203
|
+
| `text` | `string` | `''` | The tooltip's text: a few words that repeat or explain the control's name. |
|
|
204
|
+
|
|
205
|
+
## Notes
|
|
206
|
+
|
|
207
|
+
### Do
|
|
208
|
+
|
|
209
|
+
- Repeat the name of icon-only controls, and add their keyboard shortcut.
|
|
210
|
+
- Keep the text to a few words.
|
|
211
|
+
- Put tooltips on controls that take focus, so keyboard users get them too.
|
|
212
|
+
|
|
213
|
+
### Don't
|
|
214
|
+
|
|
215
|
+
- Don't put information only in a tooltip: touch screens never show it.
|
|
216
|
+
- Don't put links, buttons or other controls in a tooltip; use a popover.
|
|
217
|
+
- Don't use a tooltip as the accessible name of a control.
|
|
218
|
+
- Don't add a tooltip that repeats a visible text label word for word.
|
|
219
|
+
|
|
220
|
+
## Migration notes
|
|
221
|
+
|
|
222
|
+
### 0.3.1
|
|
223
|
+
|
|
224
|
+
New `[bfTooltip]` (`BfTooltipDirective`) and its `bf-tooltip` pill (`BfTooltipComponent`) in
|
|
225
|
+
`@bitforgehq/ui/overlay`: a short hint beside a control after a 500 ms hover or at once on keyboard
|
|
226
|
+
focus, hidden on leave, blur and `Escape`, and kept while the pointer moves onto it (WCAG 1.4.13).
|
|
227
|
+
`bfTooltipPlacement` (`BfTooltipPlacement`: `top` default, `bottom`, `start`, `end`, logical)
|
|
228
|
+
flips when it would overflow. While it shows, the control is described by it
|
|
229
|
+
(`aria-describedby`). Nothing shows under a coarse pointer. Fades in over 120 ms, not under
|
|
230
|
+
reduced motion. New `BfTooltipHarness` in `@bitforgehq/ui/testing`.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# bf-top-nav
|
|
2
|
+
|
|
3
|
+
Horizontal app navigation, a wrapping row of links for apps with few sections
|
|
4
|
+
|
|
5
|
+
`BfTopNavComponent` from `@bitforgehq/ui` · Navigation · since 0.3.1
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Component } from '@angular/core';
|
|
11
|
+
import { RouterLink, RouterLinkActive } from '@angular/router';
|
|
12
|
+
import { BfNavItemComponent, BfTopNavComponent } from '@bitforgehq/ui';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The bf-top-nav overview.
|
|
16
|
+
*
|
|
17
|
+
* Live demo of `bf-top-nav`: a row of `a[bfNavItem]` router links for an app with few sections.
|
|
18
|
+
* This page is the current one; in a narrow container the row wraps onto more lines.
|
|
19
|
+
*/
|
|
20
|
+
@Component({
|
|
21
|
+
selector: 'app-example-top-nav-overview',
|
|
22
|
+
imports: [RouterLink, RouterLinkActive, BfNavItemComponent, BfTopNavComponent],
|
|
23
|
+
template: `
|
|
24
|
+
<bf-top-nav label="Desktop navigation">
|
|
25
|
+
@for (link of links; track link.path) {
|
|
26
|
+
<a bfNavItem [routerLink]="link.path" routerLinkActive ariaCurrentWhenActive="page">{{
|
|
27
|
+
link.label
|
|
28
|
+
}}</a>
|
|
29
|
+
}
|
|
30
|
+
</bf-top-nav>
|
|
31
|
+
`,
|
|
32
|
+
})
|
|
33
|
+
export class TopNavOverviewExampleComponent {
|
|
34
|
+
protected readonly links: readonly { path: string; label: string }[] = [
|
|
35
|
+
{ path: '/components/sidebar', label: 'Sidebar' },
|
|
36
|
+
{ path: '/components/top-nav', label: 'Top nav' },
|
|
37
|
+
{ path: '/components/nav-item', label: 'Nav item' },
|
|
38
|
+
{ path: '/components/nav-group', label: 'Nav group' },
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Overview
|
|
44
|
+
|
|
45
|
+
The horizontal navigation of a desktop app with few sections: a `nav` landmark holding a row
|
|
46
|
+
of `a[bfNavItem]` links that wraps onto more lines in a narrow container. Each link marks the
|
|
47
|
+
current page from `aria-current="page"`, as in `bf-sidebar`.
|
|
48
|
+
|
|
49
|
+
The horizontal navigation of a desktop web app with few sections: a `nav` landmark holding a row of [`a[bfNavItem]`](https://bitforge-ui.web.app/components/nav-item) links, usually in the app's header bar. In a narrow container the row wraps onto more lines. It shares its item with [`bf-sidebar`](https://bitforge-ui.web.app/components/sidebar), so the current page, the count badge and the focus ring look the same in both.
|
|
50
|
+
|
|
51
|
+
## When to use
|
|
52
|
+
|
|
53
|
+
Use `bf-top-nav` for the two to five top-level destinations of a desktop web app. Use [bf-sidebar](https://bitforge-ui.web.app/components/sidebar) instead for more sections or sections in groups, and [bf-segmented](https://bitforge-ui.web.app/components/segmented) to switch a view in place. In the Capacitor shell use [bf-tabs](https://bitforge-ui.web.app/components/tabs) with [bf-tab-bar](https://bitforge-ui.web.app/components/tab-bar) instead.
|
|
54
|
+
|
|
55
|
+
## Basic usage
|
|
56
|
+
|
|
57
|
+
Put `bfNavItem` on your own `<a routerLink routerLinkActive ariaCurrentWhenActive="page">`: the router sets `aria-current="page"` on the current page's link and the item shows it in the tonal fill. Items lay out inline at their own width; an icon in `bfSlot="icon"` and a `badge` work as in the sidebar. Name the landmark with `label` (it falls back to `provideBfLabels()`'s `navigation`, "Main").
|
|
58
|
+
|
|
59
|
+
## Icons and badges
|
|
60
|
+
|
|
61
|
+
An icon in `bfSlot="icon"` sits before the label and `badge` adds a count pill, announced as "4 new", as in the sidebar.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { Component } from '@angular/core';
|
|
65
|
+
import { RouterLink, RouterLinkActive } from '@angular/router';
|
|
66
|
+
import { BfNavItemComponent, BfTopNavComponent } from '@bitforgehq/ui';
|
|
67
|
+
import { BfIconComponent } from '@bitforgehq/ui/icons';
|
|
68
|
+
import { ChartColumn, Inbox, LayoutDashboard } from 'lucide';
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Icons and a count badge in a top nav.
|
|
72
|
+
*
|
|
73
|
+
* Each `a[bfNavItem]` takes an icon in `bfSlot="icon"` and a `badge`, as in the sidebar.
|
|
74
|
+
*/
|
|
75
|
+
@Component({
|
|
76
|
+
selector: 'app-example-top-nav-icons',
|
|
77
|
+
imports: [RouterLink, RouterLinkActive, BfIconComponent, BfNavItemComponent, BfTopNavComponent],
|
|
78
|
+
template: `
|
|
79
|
+
<bf-top-nav label="Sections with icons">
|
|
80
|
+
<a bfNavItem routerLink="/components/top-nav" routerLinkActive ariaCurrentWhenActive="page">
|
|
81
|
+
<bf-icon bfSlot="icon" [icon]="dashboard" />
|
|
82
|
+
Overview
|
|
83
|
+
</a>
|
|
84
|
+
<a
|
|
85
|
+
bfNavItem
|
|
86
|
+
routerLink="/components/badge"
|
|
87
|
+
routerLinkActive
|
|
88
|
+
ariaCurrentWhenActive="page"
|
|
89
|
+
[badge]="4"
|
|
90
|
+
>
|
|
91
|
+
<bf-icon bfSlot="icon" [icon]="inbox" />
|
|
92
|
+
Inbox
|
|
93
|
+
</a>
|
|
94
|
+
<a bfNavItem routerLink="/density" routerLinkActive ariaCurrentWhenActive="page">
|
|
95
|
+
<bf-icon bfSlot="icon" [icon]="chart" />
|
|
96
|
+
Reports
|
|
97
|
+
</a>
|
|
98
|
+
</bf-top-nav>
|
|
99
|
+
`,
|
|
100
|
+
})
|
|
101
|
+
export class TopNavIconsExampleComponent {
|
|
102
|
+
protected readonly dashboard = LayoutDashboard;
|
|
103
|
+
protected readonly inbox = Inbox;
|
|
104
|
+
protected readonly chart = ChartColumn;
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Small screens
|
|
109
|
+
|
|
110
|
+
The row wraps rather than scrolling sideways. When it would wrap onto too many lines, drive a [`bf-menu-button`](https://bitforge-ui.web.app/components/menu-button) of `a[bfMenuItem]` links below a breakpoint from the same array (`class="hidden md:block"` on `bf-top-nav`, `class="md:hidden"` on the menu button), as the [sidebar's responsive layout](https://bitforge-ui.web.app/components/sidebar) shows.
|
|
111
|
+
|
|
112
|
+
## Desktop
|
|
113
|
+
|
|
114
|
+
The top nav is a desktop pattern. Its items take a `surface-muted` fill and the pointer on hover, a `surface-pressed` fill while pressed and the focus ring on keyboard focus only; the current page keeps its tonal fill with its own hover and pressed steps. Item height follows `provideBfDefaults()` (`controlSize`); under a coarse pointer every item is at least 44 px tall. Ctrl/Cmd-click and a middle click open a link in a new tab.
|
|
115
|
+
|
|
116
|
+
## Theming
|
|
117
|
+
|
|
118
|
+
- Layout: `flex flex-wrap` with a 4 px gap; no surface of its own, so it sits on the header bar or page behind it.
|
|
119
|
+
- The items' tokens are on the [nav item](https://bitforge-ui.web.app/components/nav-item#theming) page.
|
|
120
|
+
|
|
121
|
+
## Accessibility
|
|
122
|
+
|
|
123
|
+
`bf-top-nav` renders a native `<nav>` landmark named by `label`, else `provideBfLabels()`'s `navigation` ("Main"); give each landmark on a page a different name. Its links are plain `<a>` elements in DOM order; the current page is the one with `aria-current="page"`, which screen readers announce ("current page") and which an outline marks under forced colours.
|
|
124
|
+
|
|
125
|
+
### Keyboard
|
|
126
|
+
|
|
127
|
+
| Key | Action |
|
|
128
|
+
| ------------------------------------------------ | ------------------------ |
|
|
129
|
+
| <kbd>Tab</kbd> / <kbd>Shift</kbd>+<kbd>Tab</kbd> | Moves between the links |
|
|
130
|
+
| <kbd>Enter</kbd> | Follows the focused link |
|
|
131
|
+
|
|
132
|
+
## Inputs
|
|
133
|
+
|
|
134
|
+
| Name | Type | Default | Description |
|
|
135
|
+
| ------- | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
136
|
+
| `label` | `string` | `''` | Accessible name of the navigation; empty falls back to `provideBfLabels()`'s `navigation` ("Main"). Name each landmark differently when a page has more than one. |
|
|
137
|
+
|
|
138
|
+
## Slots
|
|
139
|
+
|
|
140
|
+
- `default`: The destinations, `a[bfNavItem]` links.
|
|
141
|
+
|
|
142
|
+
## Notes
|
|
143
|
+
|
|
144
|
+
### Do
|
|
145
|
+
|
|
146
|
+
- Keep it to a few short labels that fit one line at the usual window width.
|
|
147
|
+
- Mark the current page with `routerLinkActive` and `ariaCurrentWhenActive="page"`.
|
|
148
|
+
|
|
149
|
+
### Don't
|
|
150
|
+
|
|
151
|
+
- Don't put more than five or six destinations in it; use a `bf-sidebar`.
|
|
152
|
+
- Don't use it to switch a view inside a page; use a `bf-segmented`.
|
|
153
|
+
|
|
154
|
+
## Migration notes
|
|
155
|
+
|
|
156
|
+
### 0.3.1
|
|
157
|
+
|
|
158
|
+
New `bf-top-nav`: the horizontal navigation of a desktop app with few sections, a `nav` row of
|
|
159
|
+
`a[bfNavItem]` links that wraps in a narrow container, named by `label` (else `navigation`).
|