@aiquants/menu-bar 1.19.0 → 1.20.0
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 +23 -0
- package/README.md +94 -26
- package/dist/index.es.js +312 -310
- package/dist/index.umd.js +2 -2
- package/dist/styles/menu-bar.css +1 -1
- package/dist/styles/menu-bar.standalone.css +1 -1
- package/package.json +5 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`--aqmb-switch-target-size` sets the minimum side T of the switch's box (default `0px`).** The box you press is
|
|
8
|
+
B = max(S, T), where S is `--aqmb-switch-size`; the drawing keeps the side S and stands centred across the box (an
|
|
9
|
+
inline padding of (B − S) / 2), and the caption's column stands centred in the box when it fits. With the default the
|
|
10
|
+
box is the drawing, exactly as before, so a host that sets nothing sees no change, also under a coarse pointer: the
|
|
11
|
+
package never raises T on its own, since the band around the switch belongs to the host. To meet WCAG 2.5.5 (AAA) on
|
|
12
|
+
touch screens, set T to at least 44px under `@media (pointer: coarse)` where the band holds the box (the README shows
|
|
13
|
+
a 56px band holding a 48px box), and derive anything laid out beside the switch from the box
|
|
14
|
+
`max(var(--aqmb-switch-size), var(--aqmb-switch-target-size))` rather than from S.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **The drawer's controls are 44px targets under a coarse pointer.** Under `@media (pointer: coarse)` the footer's pin,
|
|
19
|
+
theme and sign-out buttons, every link of the menu list (which then fills its row), the group headings and their pin
|
|
20
|
+
buttons, and the links of `SessionInfo` are at least 2.75rem × 2.75rem (44 × 44px at the default root font size, WCAG
|
|
21
|
+
2.5.5). The rows lose their block padding there, so a row is exactly its target. With a fine pointer every size stays
|
|
22
|
+
as it was. Tailwind hosts generate these `pointer-coarse:` utilities from the package's `dist` like the others (they
|
|
23
|
+
need Tailwind CSS v4.1 or later); the standalone stylesheet carries them.
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ A React menu bar component with authentication support and theming for modern we
|
|
|
13
13
|
- **Configurable Switch**: Position, size, layering and caption of the open/close switch set through CSS custom properties
|
|
14
14
|
- **TypeScript Support**: Full TypeScript definitions included
|
|
15
15
|
- **React Router Integration**: Built for React Router with generic link component support
|
|
16
|
-
- **CSS
|
|
16
|
+
- **Scoped Component CSS**: The hand-written rules are scoped to `.aiquants-*` classes and package-prefixed variables; the standalone build also carries Tailwind utilities and theme variables, which are global (see "Styling (CSS)")
|
|
17
17
|
|
|
18
18
|
## Installation
|
|
19
19
|
|
|
@@ -21,6 +21,8 @@ A React menu bar component with authentication support and theming for modern we
|
|
|
21
21
|
npm install @aiquants/menu-bar
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
Peer dependencies: `react` and `react-dom` `^19.2.7`, and `react-router-dom` `^7.7.1`. The package's `engines` field asks for Node.js 18 or later.
|
|
25
|
+
|
|
24
26
|
## Exports
|
|
25
27
|
|
|
26
28
|
This package exports the following components, functions and types:
|
|
@@ -52,11 +54,11 @@ import '@aiquants/menu-bar/styles/menu-bar.standalone.css';
|
|
|
52
54
|
|
|
53
55
|
```tsx
|
|
54
56
|
import React, { useState } from 'react';
|
|
55
|
-
import { MenuBar } from '@aiquants/menu-bar';
|
|
57
|
+
import { MenuBar, type Theme } from '@aiquants/menu-bar';
|
|
56
58
|
import '@aiquants/menu-bar/styles/menu-bar.standalone.css';
|
|
57
59
|
|
|
58
60
|
function App() {
|
|
59
|
-
const [theme, setTheme] = useState('light');
|
|
61
|
+
const [theme, setTheme] = useState<Theme>('light');
|
|
60
62
|
|
|
61
63
|
const menuList = [
|
|
62
64
|
{
|
|
@@ -111,7 +113,7 @@ function App() {
|
|
|
111
113
|
| `toggleTheme` | `() => void` | Yes | Function to toggle between light and dark themes |
|
|
112
114
|
| `onClickSignout` | `() => void` | Yes | Callback function for sign out action |
|
|
113
115
|
| `menuHeader` | `React.ReactNode` | No | Optional header content for the menu |
|
|
114
|
-
| `isMenuHide` | `boolean` | No | Whether to hide the menu bar entirely (
|
|
116
|
+
| `isMenuHide` | `boolean` | No | Whether to hide the menu bar entirely. Renders nothing while `true`, but the pin state is still reported: while pinned, `--aiquants-menu-pinned-width` and the pin event keep the pinned width (see "Pin State") |
|
|
115
117
|
| `isMenuhide` | `boolean` | No | Legacy spelling of `isMenuHide`; read only when `isMenuHide` is not given |
|
|
116
118
|
| `onMenuNavigate` | `(payload: MenuNavigationPayload) => void` | No | Callback function triggered when a menu or submenu item is selected |
|
|
117
119
|
| `isPinned` | `boolean` | No | Controlled pin state. When omitted, the menu bar keeps the pin state itself and stores it in `localStorage` |
|
|
@@ -193,15 +195,20 @@ interface LinkProps {
|
|
|
193
195
|
|
|
194
196
|
The package ships **two CSS artifacts** — pick the one that matches your host.
|
|
195
197
|
|
|
196
|
-
- **Tailwind v4 host** — import the components-only build into the `components` layer, and let your own Tailwind build generate the JSX utility classes by scanning the package's built JavaScript (the published package ships `dist`, not `src`; the class names stay intact in the bundle):
|
|
198
|
+
- **Tailwind v4 host** (Tailwind CSS v4.1 or later) — import the components-only build into the `components` layer, and let your own Tailwind build generate the JSX utility classes by scanning the package's built JavaScript (the published package ships `dist`, not `src`; the class names stay intact in the bundle):
|
|
197
199
|
|
|
198
200
|
```css
|
|
199
201
|
/* app tailwind.css */
|
|
200
202
|
@import "@aiquants/menu-bar/styles/menu-bar.css" layer(components);
|
|
201
203
|
@source "../node_modules/@aiquants/menu-bar/dist/**/*.js";
|
|
202
|
-
/* the package
|
|
204
|
+
/* consuming the package's sources from a workspace: point @source at its src/**/*.{ts,tsx} instead */
|
|
205
|
+
@custom-variant dark (&:where(.dark, .dark *));
|
|
203
206
|
```
|
|
204
207
|
|
|
208
|
+
The `@custom-variant` line makes the menu bar's `dark:` utilities follow the `theme` prop: the menu bar's root carries `.dark` while `theme` is `'dark'`. With Tailwind's default `dark` variant (`prefers-color-scheme`), they follow the OS setting instead. The variant applies to your whole build, so your own `dark:` utilities then key off a `.dark` ancestor as well.
|
|
209
|
+
|
|
210
|
+
The drawer's sizes under a coarse pointer (see the end of "Menu Switch Custom Properties") are `pointer-coarse:` utilities, a variant Tailwind CSS v4.1 introduced. A v4.0 build drops those classes without an error, and the drawer's controls then keep their fine-pointer sizes on touch screens.
|
|
211
|
+
|
|
205
212
|
`menu-bar.css` (Artifact A) carries only the hand-written `.aiquants-*` component classes and the package's own `:root` variables — no Tailwind theme variables, no Tailwind utilities, no preflight — so it drops into `layer(components)` safely.
|
|
206
213
|
Its `:root` rule declares menu-bar's runtime variable `--aiquants-menu-pinned-width` (which the component sets inline at runtime) and the defaults of the menu switch custom properties (`--aqmb-switch-*`, see below).
|
|
207
214
|
Do **not** also `@import` the standalone build in a Tailwind host: it re-emits the same utilities your build produces, and two concatenated builds cannot reproduce the single-build canonical order (base → variants), so base/variant cascade winners flip.
|
|
@@ -212,13 +219,15 @@ The package ships **two CSS artifacts** — pick the one that matches your host.
|
|
|
212
219
|
@import "@aiquants/menu-bar/styles/menu-bar.standalone.css";
|
|
213
220
|
```
|
|
214
221
|
|
|
215
|
-
|
|
222
|
+
The utilities in this build are global selectors (`.flex`, `.fixed`, `.hidden`, ...), and it declares the Tailwind theme variables it uses on `:root, :host`, so a host element with one of those class names picks up the rule.
|
|
223
|
+
|
|
224
|
+
Dark mode follows the `theme` prop: the menu bar's root carries `.dark` while `theme` is `'dark'`, and this build's `dark:` utilities apply to an element with the `.dark` class or inside one, so `<html>` needs no class. A `SessionInfo` rendered outside `MenuBar` takes its dark styles from a `.dark` ancestor of its own. If you also put `.dark` on `<html>`, keep it in step with `theme`: with `theme="light"` it turns on the `dark:` utilities inside the light menu bar while the light colours of the hand-written rules stay, mixing the two palettes.
|
|
216
225
|
|
|
217
226
|
In both setups the component classes sit in the `components` layer and the utilities in the later `utilities` layer, so a utility wins over a component rule for the same property.
|
|
218
227
|
|
|
219
228
|
## Menu Switch Custom Properties
|
|
220
229
|
|
|
221
|
-
The open/close switch is a square box with four bars that turn into a cross while the drawer is open, over a "menu" / "close" caption. Its whole drawing lives in the package stylesheet, and it carries no utility classes, so every custom property below takes effect. The defaults are declared on `:root`; override them on `:root` (or any ancestor of the menu bar).
|
|
230
|
+
The open/close switch is a square box with four bars that turn into a cross while the drawer is open, over a "menu" / "close" caption. The drawing has the side S; the box you press has the side B = max(S, T), where T is a target size you may set, with the drawing centred across it. Its whole drawing lives in the package stylesheet, and it carries no utility classes, so every custom property below takes effect. The defaults are declared on `:root`; override them on `:root` (or any ancestor of the menu bar).
|
|
222
231
|
|
|
223
232
|
| Property | Default | Effect |
|
|
224
233
|
| --- | --- | --- |
|
|
@@ -227,16 +236,19 @@ The open/close switch is a square box with four bars that turn into a cross whil
|
|
|
227
236
|
| `--aqmb-switch-right` | `8px` | `right` of the switch |
|
|
228
237
|
| `--aqmb-switch-left` | `auto` | `left` of the switch |
|
|
229
238
|
| `--aqmb-switch-bottom` | `auto` | `bottom` of the switch |
|
|
230
|
-
| `--aqmb-switch-z-index` | `
|
|
231
|
-
| `--aqmb-switch-size` | `27px` | Side S of the switch's
|
|
239
|
+
| `--aqmb-switch-z-index` | `calc(infinity)` | `z-index` of the switch. The default computes to the largest integer the browser supports (2147483647 in Chromium), which keeps the switch above the drawer (`z-index` 2147483642) and the backdrop (2147483640) |
|
|
240
|
+
| `--aqmb-switch-size` | `27px` | Side S of the switch's drawing; every length of the drawing derives from it |
|
|
241
|
+
| `--aqmb-switch-target-size` | `0px` | Minimum side T of the box you press: the box is B = max(S, T), and the drawing keeps its size, centred across the box by an inline padding of (B − S) / 2. The default leaves B = S. The package never raises it on its own, under a coarse pointer either: only you know whether a larger box fits the band around the switch |
|
|
232
242
|
| `--aqmb-switch-caption-display` | `block` | `display` of the caption; `none` hides it |
|
|
233
243
|
|
|
244
|
+
The `z-index` default is `calc(infinity)` rather than a number because Lightning CSS, which Tailwind v4 builds pass stylesheets through, writes the numbers of custom-property values and `var()` fallbacks with 6 significant digits: `2147483647` would reach the browser as `2147480000`, below the drawer. An override in a stylesheet that also goes through Lightning CSS is rounded the same way when it has 7 or more significant digits. A small value such as `50` is kept as written, and stacks the switch below the drawer and the backdrop.
|
|
245
|
+
|
|
234
246
|
How the drawing follows S:
|
|
235
247
|
|
|
236
248
|
- The bars are S/9 thick, at 0, 2S/9, 4S/9 and 6S/9 of a stack 7S/9 high. While open, the outer bars become 32S/27 wide and 4S/27 thick and cross at ±45°; the inner bars fade out.
|
|
237
249
|
- The caption's top edge is S/2 + 0.5rem below the top of the stack.
|
|
238
|
-
- With the caption shown (the default) and S below 48px
|
|
239
|
-
- With `--aqmb-switch-caption-display: none`, the bars are centred in the box and nothing is drawn above or below it. The cross can still extend slightly past the box's right edge, as it does in the default drawing.
|
|
250
|
+
- The column of bars and caption is S/2 + 1.5rem tall. With the caption shown (the default) and a box B below that (B = S below 48px at a 16px root font size), the column is taller than the box: it starts at the box's top and the caption hangs below the box. In a box that holds it (the default S = 27px in a 44px box, for one), the column stands centred in the box.
|
|
251
|
+
- With `--aqmb-switch-caption-display: none`, the bars are centred in the box (their stack starts (B − 7S/9) / 2 below its top) and nothing is drawn above or below it. The cross can still extend slightly past the box's right edge, as it does in the default drawing.
|
|
240
252
|
- The default `27px` gives 3px bars spaced 6px apart, a 32px × 4px cross, and a caption 21.5px below the top of the box.
|
|
241
253
|
- The switch draws in black, and in white on the dark theme and while the drawer is open. Its keyboard focus ring is a 2px outline in the same colour at a 2px offset, so it occupies 4px around the box.
|
|
242
254
|
|
|
@@ -252,9 +264,31 @@ Example — fit the switch into a 32px header bar (4px + 24px line box + 4px):
|
|
|
252
264
|
|
|
253
265
|
The box sits on the bar's 24px line box, and the focus ring (0 to 32px from the top) stays inside the bar. Adjust `--aqmb-switch-right` to align it horizontally.
|
|
254
266
|
|
|
267
|
+
Example — a 44px target on touch screens (WCAG 2.5.5, AAA). Under a coarse pointer, set the target size to at least 44px where your band allows it, and grow the band to hold the box. A 32px bar built as 4px + line box + 4px on a 4px lattice keeps its height on that lattice when the line box becomes the smallest multiple of 8px that is at least 44px, 48px, so the band becomes 4px + 48px + 4px = 56px:
|
|
268
|
+
|
|
269
|
+
```css
|
|
270
|
+
:root {
|
|
271
|
+
--aqmb-switch-size: 24px;
|
|
272
|
+
--aqmb-switch-caption-display: none;
|
|
273
|
+
--aqmb-switch-top: 4px;
|
|
274
|
+
--aqmb-switch-target-size: 24px;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
@media (pointer: coarse) {
|
|
278
|
+
:root {
|
|
279
|
+
--aqmb-switch-target-size: 48px;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
The drawing stays 24px and the box becomes 48 × 48 with 12px of inline padding, at 4px from the top; its focus ring spans 0 to 56px, exactly the band. Anything you lay out beside the switch should reserve the box B rather than the drawing S (for example `max(var(--aqmb-switch-size), var(--aqmb-switch-target-size))`).
|
|
285
|
+
|
|
286
|
+
Under a coarse pointer the drawer's own controls are at least 2.75rem × 2.75rem (44 × 44 px at the default root font size): the footer's pin, theme and sign-out buttons, every link of the list (it then fills its row), the group headings and their pin buttons, and the links of `SessionInfo`.
|
|
287
|
+
With a fine pointer they are sized by their content: the footer's buttons are 1.75rem tall; a list link is its text, in a row with 0.25rem of block padding (0.125rem inside a group); a group heading is its text with 0.25rem of block padding, and its pin button a 13px icon with 0.15rem of padding; a `SessionInfo` link is one line of 11px text with 0.25rem of block padding.
|
|
288
|
+
|
|
255
289
|
## Theming
|
|
256
290
|
|
|
257
|
-
The menu bar supports both light and dark themes.
|
|
291
|
+
The menu bar supports both light and dark themes. The hand-written styles are scoped with the `.aiquants-menu-bar` class; the utility classes and Tailwind theme variables of the standalone build are global (see "Styling (CSS)"). The root element carries the theme as a class: `.aiquants-menu-bar.light` or `.aiquants-menu-bar.dark`.
|
|
258
292
|
|
|
259
293
|
### Custom Styling
|
|
260
294
|
|
|
@@ -278,10 +312,10 @@ You can override the default styles by targeting the scoped classes:
|
|
|
278
312
|
|
|
279
313
|
```tsx
|
|
280
314
|
import React, { useState } from 'react';
|
|
281
|
-
import { MenuBar } from '@aiquants/menu-bar';
|
|
315
|
+
import { MenuBar, type Theme } from '@aiquants/menu-bar';
|
|
282
316
|
|
|
283
317
|
function App() {
|
|
284
|
-
const [theme, setTheme] = useState('light');
|
|
318
|
+
const [theme, setTheme] = useState<Theme>('light');
|
|
285
319
|
|
|
286
320
|
const menuList = [
|
|
287
321
|
{
|
|
@@ -337,7 +371,34 @@ The rule:
|
|
|
337
371
|
- An `href` of `/` matches only the root, even with `'prefix'`. External URLs never match.
|
|
338
372
|
- When several entries match, the longest `href` wins, across groups too; an exact match therefore always beats a prefix match. On a tie, the first entry in menu order wins (a top-level item before its own submenu items).
|
|
339
373
|
|
|
340
|
-
The group of the matched entry opens once per route change, and only one group is open at a time (pinned groups always stay open). A route with no matching entry leaves the open group as it is, and reloading the menu on the same route does not reopen a group the user closed.
|
|
374
|
+
The group of the matched entry opens once per route change, and only one group is open at a time (pinned groups always stay open). When the matched entry is a top-level link, no group opens, and the group that was open closes. A route with no matching entry leaves the open group as it is, and reloading the menu on the same route does not reopen a group the user closed.
|
|
375
|
+
|
|
376
|
+
## Pin State
|
|
377
|
+
|
|
378
|
+
The pin button keeps the drawer open, without the backdrop, while the user navigates. When it mounts and whenever the pin state, `pinnedWidth` or the open state changes, the menu bar reports the state in two ways, and it resets both to "not pinned" when it unmounts:
|
|
379
|
+
|
|
380
|
+
- **CSS variable**: `--aiquants-menu-pinned-width` on `<html>` holds the pinned width (`pinnedWidth`, default `240`) in pixels while pinned, and `0px` otherwise.
|
|
381
|
+
- **Event**: a `CustomEvent` named by `MENU_PIN_CHANGE_EVENT` (`"aiquants-menu-pin-change"`) on `window`. Its `detail` is a `MenuPinChangeDetail`, `{ pinned, width, active? }`: `width` matches the CSS variable, and `active` tells whether the drawer is open or pinned.
|
|
382
|
+
|
|
383
|
+
These exports read the state:
|
|
384
|
+
|
|
385
|
+
| Export | Behaviour |
|
|
386
|
+
| --- | --- |
|
|
387
|
+
| `useMenuPinState(options?)` | Returns the current `MenuPinChangeDetail` and re-renders when it changes. Options: `initialPinned` (default `false`) and `defaultPinnedWidth` (default `240`). During server rendering it returns `{ pinned: initialPinned, width: initialPinned ? defaultPinnedWidth : 0 }`, without `active` |
|
|
388
|
+
| `subscribeMenuPinChange(listener, options?)` | Calls `listener` with the `detail` of every event and returns a function that unsubscribes. Options: `immediate` (default `true`: also calls `listener` once right away with `getInitialPinState(defaultPinnedWidth)`) and `defaultPinnedWidth` (default `240`) |
|
|
389
|
+
| `getInitialPinState(defaultPinnedWidth = 240)` | Reads the CSS variable: pinned when it is a positive number of pixels, which is then the width. When the variable is empty or not a number, it reads the `aiquants-menu-pinned` `localStorage` key instead (`"true"`: pinned at `defaultPinnedWidth`); the package stylesheet declares the variable as `0px`, so this happens only while neither the stylesheet nor a menu bar has set it. `active` is that of the last `dispatchMenuPinChange` call (`false` before the first) |
|
|
390
|
+
| `dispatchMenuPinChange(pinned, width, active = pinned)` | Dispatches the event on `window`. The menu bar calls it itself |
|
|
391
|
+
|
|
392
|
+
```tsx
|
|
393
|
+
import type { ReactNode } from 'react';
|
|
394
|
+
import { useMenuPinState } from '@aiquants/menu-bar';
|
|
395
|
+
|
|
396
|
+
function Page({ children }: { children: ReactNode }) {
|
|
397
|
+
const { width } = useMenuPinState();
|
|
398
|
+
// The pinned drawer covers the right edge of the viewport
|
|
399
|
+
return <main style={{ paddingRight: width }}>{children}</main>;
|
|
400
|
+
}
|
|
401
|
+
```
|
|
341
402
|
|
|
342
403
|
## Accessibility
|
|
343
404
|
|
|
@@ -349,6 +410,7 @@ The group of the matched entry opens once per route change, and only one group i
|
|
|
349
410
|
- **Groups**: group summaries toggle with Enter or Space and show a focus outline on keyboard focus. Every button has an accessible name.
|
|
350
411
|
- **Reduced motion**: the drawer's slide, the backdrop's fade and the switch's transitions run only under `prefers-reduced-motion: no-preference`.
|
|
351
412
|
- **Forced colors**: the switch's bars are drawn in `ButtonText`, so they stay visible when forced colors replace backgrounds.
|
|
413
|
+
- **Built-in labels**: the names and texts are fixed, and no prop localises them. The switch is named in English ("Open menu" / "Close menu", caption "menu" / "close"); the drawer's pin and theme buttons, the sign-out button and the group pin buttons are named in Japanese (for example "ピン留め", "ダークモードに切り替え", "サインアウト"), and `SessionInfo` shows Japanese texts such as "プロフィール" and "ログインしてください".
|
|
352
414
|
|
|
353
415
|
## DOM Hooks
|
|
354
416
|
|
|
@@ -364,6 +426,7 @@ Select the menu bar's elements in scripts and tests by these attributes (or by r
|
|
|
364
426
|
| `data-aqmb-theme-button` | The theme toggle button |
|
|
365
427
|
| `data-aqmb-signout-button` | The sign-out button |
|
|
366
428
|
| `data-aqmb-submenu-pin-button` | The pin button of each group |
|
|
429
|
+
| `data-aqmb-session-card` | The `SessionInfo` card, with or without a `user` |
|
|
367
430
|
|
|
368
431
|
## Advanced Usage
|
|
369
432
|
|
|
@@ -418,9 +481,11 @@ The main menu bar component that includes:
|
|
|
418
481
|
|
|
419
482
|
A standalone session information component that displays:
|
|
420
483
|
|
|
421
|
-
-
|
|
422
|
-
-
|
|
423
|
-
-
|
|
484
|
+
- The user's avatar (a default icon when `photo` is empty), display name and a "Google Account" label
|
|
485
|
+
- Two links rendered through the customizable `LinkComponent` (default: `DefaultLink`, a plain `<a>`): a profile link to `/` and a link to `https://myaccount.google.com/`
|
|
486
|
+
- A "please sign in" card ("ログインしてください") when `user` is `null`
|
|
487
|
+
|
|
488
|
+
The card carries `data-aqmb-session-card` in both states. Sign-out is not part of `SessionInfo`: it is the drawer's sign-out button, which calls `onClickSignout`.
|
|
424
489
|
|
|
425
490
|
```tsx
|
|
426
491
|
import { SessionInfo } from '@aiquants/menu-bar';
|
|
@@ -441,12 +506,12 @@ import { SessionInfo } from '@aiquants/menu-bar';
|
|
|
441
506
|
- **Hierarchical Menu Structure**: Support for main menu items with submenus
|
|
442
507
|
- **Dynamic Navigation**: Menu items can have direct links or submenu collections
|
|
443
508
|
- **Current Page**: Exact or prefix matching per entry, with `aria-current="page"`
|
|
444
|
-
- **Visual Indicators**:
|
|
445
|
-
- **External Links**:
|
|
509
|
+
- **Visual Indicators**: An icon per submenu `mark` (`report`, `slip`, `table`, `chart`, `download`, `lock`, `href`); any other value, or none, renders the text only
|
|
510
|
+
- **External Links**: `new: true` opens a submenu item in a new tab (`target="_blank"`, `rel="noopener noreferrer"`); it draws no badge
|
|
446
511
|
|
|
447
512
|
## CSS Classes
|
|
448
513
|
|
|
449
|
-
|
|
514
|
+
Hand-written component classes use the `.aiquants-` prefix (the modifiers are the theme class `light` / `dark` on the root and `pinned` on a pinned group's pin button), for example:
|
|
450
515
|
|
|
451
516
|
- `.aiquants-menu-bar` - Main container (with the theme class `light` or `dark`)
|
|
452
517
|
- `.aiquants-menu-switch` - Open/close switch (`.aiquants-menu-switch-bars`, `.aiquants-menu-switch-bar`, `.aiquants-menu-switch-caption`)
|
|
@@ -454,14 +519,17 @@ All hand-written CSS classes use the `.aiquants-` prefix, for example:
|
|
|
454
519
|
- `.aiquants-menu-list-item` - Menu item and group summary
|
|
455
520
|
- `.aiquants-session-card` - Session information card
|
|
456
521
|
|
|
522
|
+
The menu list's scroll container also carries `hidden-scrollbar`, a class the package does not define, so a host rule of that name applies to it.
|
|
523
|
+
|
|
457
524
|
These classes are a styling contract, not DOM hooks — use the `data-aqmb-*` attributes above to select elements.
|
|
458
525
|
|
|
459
526
|
## Browser Support
|
|
460
527
|
|
|
461
|
-
- Chrome/Edge
|
|
462
|
-
- Firefox
|
|
463
|
-
- Safari
|
|
464
|
-
|
|
528
|
+
- Chrome/Edge 111+
|
|
529
|
+
- Firefox 128+
|
|
530
|
+
- Safari 16.4+
|
|
531
|
+
|
|
532
|
+
These are the versions Tailwind CSS v4, which builds both stylesheets, targets. The stylesheets also sit in cascade layers, which an older browser drops whole, and the closed drawer and the backdrop rely on the HTML `inert` attribute.
|
|
465
533
|
|
|
466
534
|
## Development
|
|
467
535
|
|