<!-- 04_VISUAL/xwui/xwui-lib/xwui-lib-super/src/schedule/XWUIIntervalBars/XWUIIntervalBars.md -->
# XWUIIntervalBars

Rows of bars placed on a linear or log axis by start and end: the light drawing of mini-Gantts, roadmap strips,
plan-versus-actual rows, shift lists, packed lanes, seat ranges and decorative template thumbnails. Bars carry
ghosts (baseline or previous position), float tails, targets, progress and trailing text; the chart carries
finish-to-start, start-to-start, finish-to-finish and start-to-finish links, markers (a data date), bands
(non-working days, Ramadan hours) and ticks. Read-only by default; `interactive` turns the bars into buttons that
report a pick. `XWUIGanttChart` stays the one editing Gantt (plnx decision V-1): this component never moves or
resizes a bar, and never computes a schedule (the page's CPM pass does).

## Basic Usage

```example
file: examples/BasicUsage.ts
html: examples/BasicUsage.html
title: Basic Usage
description: Three activities on a 10-day axis: a bar, a bar with progress and a milestone.
```

## Data

```ts
data: {
  rows: Array<{
    id: string; label?: string; cells?: string[];                 // cells follow config.columns (minus 'label')
    variance?: { value: number; text: string };                    // diverging cell: + late (end side), - early
    bars: Array<{
      id: string; start: number; end: number;                      // domain units; not finite = "not recorded"
      label?: string; hue?: string; tone?: string;                 // hue is a token NAME; tone: neutral|info|success|warning|danger
      variant?: 'solid' | 'ghost' | 'critical' | 'milestone' | 'hatch' | 'moved';
      progress?: number;                                           // 0..1 inner fill
      ghost?: { start: number; end: number; kind: 'baseline' | 'previous' };
      floatTail?: number; target?: number; trailing?: string;
      lift?: boolean; selected?: boolean; dimmed?: boolean;
      startText?: string; endText?: string;                        // dates for the twin and the accessible name
    }>;
  }>;
  domain?: [number, number];                                       // unset or invalid: the bars' extent (from 0)
  ticks?: Array<{ at: number; label: string }>;                    // labels in the host's calendar and units
  links?: Array<{ from: string; to: string; type: 'FS' | 'SS' | 'FF' | 'SF'; lagLabel?: string; critical?: boolean; driving?: boolean }>;
  markers?: Array<{ at: number; label?: string; tone?: string; rowId?: string; style?: 'solid' | 'dashed' | 'dotted' | 'beam' }>;
  bands?: Array<{ from: number; to: number; kind: string; label?: string }>;   // 'nonworking' hatches
  status?: 'ready' | 'loading' | 'error';
  preset?: string;                                                 // 'release-plan', 'template-thumb'
}
```

Rules (pinned by `XWUIIntervalBars.test.ts`):

- A bar sits at `pct(start)` and is `pct(end) - pct(start)` wide, in percent of the plot, clamped to the plot. A
  bar narrower than the minimum token stays visible and is pulled back inside. An end before the start is the
  reverse range; a milestone has `end = start`.
- A row whose bars all have a missing or non-finite start or end draws none and says "Not recorded".
- Repeated row or bar ids are suffixed (`id`, `id~1`), so every event names one bar.
- `trailing` text sits on the roomier side of the bar (after its float tail; after on a tie).
- Log scale: `domain[0]` must be above 0, else the chart degrades to linear.
- Links: one orthogonal elbow per link (out of the source edge, along the row boundary, into the target edge;
  a straight run on one lane). Links to unknown bars are skipped.
- Selection is the host's: `barSelect` reports `{ rowId, barId }` and the page answers with data that marks the
  bar `selected`.

## Options

| Option | Default | What it does |
| --- | --- | --- |
| `variant` | `'full'` | `'thumb'`: decorative picture, `aria-hidden`, no labels, axis, twin or interaction. |
| `layout` | `'rows'` | `'lanes'`: every bar greedy-packed onto the fewest lanes (`laneCount` is the minimum). |
| `scale` | `'linear'` | `'log'` for ranges over orders of magnitude (seat counts). |
| `rowHeight`, `barInset`, `labelWidth` | CSS defaults | CSS lengths (rem, em, ch, px, %, vw, vh); anything else is ignored. |
| `labelColumn` | `'start'` | `'end'` puts the label column after the plot; `'none'` hides it. |
| `columns` | none | Leading table columns level with the bars; the one with id `label` places the row label. |
| `labelHeading` | none | Heading of the label column when `columns` has no `label` entry. |
| `dayGrid` | none | `{ nonWorking?, weekLabels?, start?, workWeek?, holidays?, calendar? }`: hatched non-working days (from offsets or the shared work calendar) and week ticks (dated in `calendar`, 'islamic-umalqura' for Hijri). |
| `fit` | `true` | `false`: a fixed width per domain unit and an inner, keyboard-reachable scroll. |
| `mirrorInRtl` | `true` | `false`: the axis stays a left-to-right island under `dir="rtl"`. |
| `tableTwin` | `true` | The visually hidden table of every bar. |
| `interactive` | `false` | Bars become buttons: arrow keys, Home, End, Enter, Space. |
| `transitionMs` | `300` | Position and width transition; 0 under `prefers-reduced-motion`. |
| `title`, `ariaLabel` | none, `xwui.interval_bars.label` | Visible heading and accessible name. |
| `emptyText`, `errorText` | `xwui.interval_bars.empty`, `.error` | State copy. |
| `locale`, `numberingSystem` | page language, `'latn'` | Number and date formatting; Western digits by default. |
| `className` | none | Extra root class. |

## Events and methods

`onBarSelect({ rowId, barId })`, `onBarHover({ rowId, barId } | null)`; the same go out on the host element as
DOM `CustomEvent`s `barselect` and `barhover`. Methods: `select(barId)`, `setData(data)`, `updateData(patch)`,
`updateConfig(patch)`, `getElement()`, `destroy()` (idempotent; releases the view mount, the reduced-motion
listener and the logic). `XWUIIntervalBars.projectView(data, config)` composes a still chart into a parent tree.
The geometry helpers (`layoutIntervalBars`, `packLanes`, `makeScale`, `linkGeometry`, `nonWorkingRanges`,
`weekTicks`, `stepBar`) are exported for hosts and for `XWUILayoutMorph`.

## Accessibility, keyboard and RTL

- Static chart: the drawing is `aria-hidden` and the visually hidden table twin (row, bar, start, end, float,
  state with "critical", "milestone", "baseline 11 to 19", note) carries the content. With `tableTwin: false` each
  bar is a named `role="img"`.
- Interactive chart: the bars are `button`s in a named group with one roving tab stop (the selected bar, else the
  first), `aria-pressed` for selection and an accessible name made of the row, the bar, the range, "critical", the
  float and the trailing text. Arrow Left and Right walk the reading order (swapped when the axis mirrors), Up and
  Down jump to the nearest bar on the next line, Home and End jump to the ends, Enter and Space pick.
- Direction comes from the PARENT `dir`. Every position is a logical inset, so the axis mirrors; the SVG link
  layer and the arrowheads are flipped by `--mirror`. With `mirrorInRtl: false` the chart is a `dir="ltr"` island
  and every label keeps `dir="auto"`.
- Colour is never the only signal: critical bars also say "critical" in the name and twin, ghosts are outlined or
  thin, hatched bars carry a pattern, variance cells carry signed text.
- `prefers-reduced-motion`: the adapter sets the transition knob to 0 (and listens for changes); the stylesheet
  also stops motion under the media query. Forced colours draw bars in `CanvasText`.
- Narrow containers (container queries, never the viewport): under 48rem the extra leading columns step aside and
  the label column yields to the plot; under 40rem and 26rem dense tick labels thin out (the grid lines stay).

## Styling

Class names are `.xwui-interval-bars` and `.xwui-interval-bars__<part>`. Colours are theme tokens (accent and
status tokens, text and surface tokens) or hue NAMES resolved to `var(--xwui-hue-<name>, var(--xwui-hue-slate))`;
the non-working hatch is `--pattern-nonworking` from the style tier's viz tokens. Knobs: `--xwui-interval-bars-row-h`,
`-inset`, `-label-w`, `-cell-w`, `-head-h`, `-min`, `-arrow`, `-unit`, `-var-w`.

## Provenance

Promoted from the app-owned `03_BASE/xwflow/xwflow-web/src/components/XWUIIntervalBars` (17-Oct-2026; run waterfall
in milliseconds: `startMs`/`durMs` rows with a tone, hue and note, a `sm`/`md` size, a table twin). There it
extends the runtime base classes from the CDN bundle; here it imports the library base classes like its siblings.
Carried over: the percent maths and clamping, the minimum-width rule, "not recorded" rows, roomier-side notes,
id de-duplication, CSS-length validation, the table twin, the roving tab stop. Its tests are the "app regression
floor" block of `XWUIIntervalBars.test.ts`, rewritten onto the unitless contract. Changed on purpose: `startMs`/
`durMs` became `start`/`end` on a unitless domain with several bars per row; `note` became `trailing`; the axis
mirrors by default (the app drew an LTR island, now `mirrorInRtl: false`); rows stopped being options (bars are
the interactive unit); the per-row `XWUIOverlayTooltip` was dropped (the accessible name and `barHover` cover it);
the twin is a plain table instead of a composed `XWUISpecTable`. `xwflow-web` keeps its copy until it adopts the
library version (no re-export shim either way). Spec: plnx `REF_06_GAPS.md` section 2.2, C07.

```api
```
