@acronis-platform/ui-react 2.0.0 → 4.0.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/dist/components/ui/accordion-container/accordion-container.js +144 -0
- package/dist/components/ui/accordion-container/accordion-container.js.map +1 -0
- package/dist/components/ui/app-shell-chat/app-shell-chat.js.map +1 -1
- package/dist/components/ui/area-chart/area-chart.js +141 -139
- package/dist/components/ui/area-chart/area-chart.js.map +1 -1
- package/dist/components/ui/avatar/avatar.js +33 -22
- package/dist/components/ui/avatar/avatar.js.map +1 -1
- package/dist/components/ui/bar-chart/bar-chart.js +295 -293
- package/dist/components/ui/bar-chart/bar-chart.js.map +1 -1
- package/dist/components/ui/card/card.js +150 -74
- package/dist/components/ui/card/card.js.map +1 -1
- package/dist/components/ui/card-section/card-section.js +111 -0
- package/dist/components/ui/card-section/card-section.js.map +1 -0
- package/dist/components/ui/category-bar/category-bar.js +109 -93
- package/dist/components/ui/category-bar/category-bar.js.map +1 -1
- package/dist/components/ui/chart/chart-palette.js +113 -0
- package/dist/components/ui/chart/chart-palette.js.map +1 -0
- package/dist/components/ui/chart/chart.js +176 -129
- package/dist/components/ui/chart/chart.js.map +1 -1
- package/dist/components/ui/chart-state/chart-state-illustrations.js +310 -0
- package/dist/components/ui/chart-state/chart-state-illustrations.js.map +1 -0
- package/dist/components/ui/chart-state/chart-state.js +48 -34
- package/dist/components/ui/chart-state/chart-state.js.map +1 -1
- package/dist/components/ui/chart-widget/chart-widget.js +87 -0
- package/dist/components/ui/chart-widget/chart-widget.js.map +1 -0
- package/dist/components/ui/composed-chart/composed-chart.js +117 -115
- package/dist/components/ui/composed-chart/composed-chart.js.map +1 -1
- package/dist/components/ui/confidence-cone/confidence-cone.js +166 -158
- package/dist/components/ui/confidence-cone/confidence-cone.js.map +1 -1
- package/dist/components/ui/data-table/data-table.js +4 -2
- package/dist/components/ui/data-table/data-table.js.map +1 -1
- package/dist/components/ui/empty-overlay/empty-overlay.js +49 -0
- package/dist/components/ui/empty-overlay/empty-overlay.js.map +1 -0
- package/dist/components/ui/funnel-chart/funnel-chart.js +162 -161
- package/dist/components/ui/funnel-chart/funnel-chart.js.map +1 -1
- package/dist/components/ui/histogram/histogram.js +94 -92
- package/dist/components/ui/histogram/histogram.js.map +1 -1
- package/dist/components/ui/input/input.js.map +1 -1
- package/dist/components/ui/label/label.js +2 -9
- package/dist/components/ui/label/label.js.map +1 -1
- package/dist/components/ui/line-chart/line-chart.js +143 -141
- package/dist/components/ui/line-chart/line-chart.js.map +1 -1
- package/dist/components/ui/metric/metric.js +74 -185
- package/dist/components/ui/metric/metric.js.map +1 -1
- package/dist/components/ui/page-content/page-content.js.map +1 -1
- package/dist/components/ui/pie-chart/pie-chart.js +201 -195
- package/dist/components/ui/pie-chart/pie-chart.js.map +1 -1
- package/dist/components/ui/radar-chart/radar-chart.js +133 -131
- package/dist/components/ui/radar-chart/radar-chart.js.map +1 -1
- package/dist/components/ui/radial-bar-chart/radial-bar-chart.js +297 -265
- package/dist/components/ui/radial-bar-chart/radial-bar-chart.js.map +1 -1
- package/dist/components/ui/sankey-chart/sankey-chart.js +123 -101
- package/dist/components/ui/sankey-chart/sankey-chart.js.map +1 -1
- package/dist/components/ui/scatter-chart/scatter-chart.js +76 -74
- package/dist/components/ui/scatter-chart/scatter-chart.js.map +1 -1
- package/dist/components/ui/section/section.js +157 -31
- package/dist/components/ui/section/section.js.map +1 -1
- package/dist/components/ui/stepper/stepper.js +24 -24
- package/dist/components/ui/stepper/stepper.js.map +1 -1
- package/dist/components/ui/stepper-item/stepper-item.js +43 -24
- package/dist/components/ui/stepper-item/stepper-item.js.map +1 -1
- package/dist/components/ui/table/table.js +27 -27
- package/dist/components/ui/table/table.js.map +1 -1
- package/dist/components/ui/treemap/treemap.js +133 -124
- package/dist/components/ui/treemap/treemap.js.map +1 -1
- package/dist/components/ui/trend-indicator/trend-indicator.js +34 -69
- package/dist/components/ui/trend-indicator/trend-indicator.js.map +1 -1
- package/dist/components/ui/wizard/wizard.js +59 -0
- package/dist/components/ui/wizard/wizard.js.map +1 -0
- package/dist/hooks/use-wizard.js +75 -0
- package/dist/hooks/use-wizard.js.map +1 -0
- package/dist/index.js +540 -525
- package/dist/index.js.map +1 -1
- package/dist/llms/accordion-container.md +181 -0
- package/dist/llms/accordion.md +124 -0
- package/dist/llms/alert.md +225 -0
- package/dist/llms/app-shell-chat.md +415 -0
- package/dist/llms/area-chart.md +355 -0
- package/dist/llms/avatar.md +208 -0
- package/dist/llms/bar-chart.md +406 -0
- package/dist/llms/breadcrumb.md +326 -0
- package/dist/llms/button-group.md +298 -0
- package/dist/llms/button-icon-input.md +142 -0
- package/dist/llms/button-icon-menu.md +209 -0
- package/dist/llms/button-icon.md +128 -0
- package/dist/llms/button-menu.md +172 -0
- package/dist/llms/button.md +185 -0
- package/dist/llms/calendar-panel.md +733 -0
- package/dist/llms/calendar.md +188 -0
- package/dist/llms/card-filter.md +214 -0
- package/dist/llms/card-section.md +293 -0
- package/dist/llms/card.md +447 -0
- package/dist/llms/carousel-dialog.md +156 -0
- package/dist/llms/category-bar.md +193 -0
- package/dist/llms/chart-state.md +190 -0
- package/dist/llms/chart-widget.md +209 -0
- package/dist/llms/chart.md +182 -0
- package/dist/llms/checkbox.md +257 -0
- package/dist/llms/chip.md +195 -0
- package/dist/llms/combobox.md +164 -0
- package/dist/llms/composed-chart.md +508 -0
- package/dist/llms/confidence-cone.md +277 -0
- package/dist/llms/data-table.md +474 -0
- package/dist/llms/date-range-picker.md +272 -0
- package/dist/llms/description-list.md +131 -0
- package/dist/llms/dialog-footer-carousel.md +147 -0
- package/dist/llms/dialog-footer-default.md +142 -0
- package/dist/llms/dialog-root.md +246 -0
- package/dist/llms/dialog-welcome.md +223 -0
- package/dist/llms/dialog.md +403 -0
- package/dist/llms/dropdown-menu.md +193 -0
- package/dist/llms/empty-overlay.md +146 -0
- package/dist/llms/empty.md +156 -0
- package/dist/llms/field.md +185 -0
- package/dist/llms/filter-cards.md +157 -0
- package/dist/llms/filter-chips.md +323 -0
- package/dist/llms/filter-search.md +512 -0
- package/dist/llms/form.md +154 -0
- package/dist/llms/funnel-chart.md +333 -0
- package/dist/llms/grid.md +80 -0
- package/dist/llms/histogram.md +190 -0
- package/dist/llms/input-date-picker.md +203 -0
- package/dist/llms/input-otp.md +173 -0
- package/dist/llms/input-password.md +197 -0
- package/dist/llms/input-search.md +170 -0
- package/dist/llms/input-select.md +278 -0
- package/dist/llms/input-text-area.md +271 -0
- package/dist/llms/input-text.md +192 -0
- package/dist/llms/line-chart.md +369 -0
- package/dist/llms/link.md +209 -0
- package/dist/llms/loading.md +130 -0
- package/dist/llms/meter.md +177 -0
- package/dist/llms/metric.md +204 -0
- package/dist/llms/number-field.md +122 -0
- package/dist/llms/page-content.md +69 -0
- package/dist/llms/page-header.md +182 -0
- package/dist/llms/pagination.md +133 -0
- package/dist/llms/pie-chart.md +307 -0
- package/dist/llms/popover.md +252 -0
- package/dist/llms/progress-circle.md +126 -0
- package/dist/llms/progress.md +111 -0
- package/dist/llms/radar-chart.md +316 -0
- package/dist/llms/radial-bar-chart.md +319 -0
- package/dist/llms/radio.md +255 -0
- package/dist/llms/resizable.md +195 -0
- package/dist/llms/sankey-chart.md +208 -0
- package/dist/llms/scatter-chart.md +239 -0
- package/dist/llms/scroll-area.md +183 -0
- package/dist/llms/section.md +401 -0
- package/dist/llms/select.md +210 -0
- package/dist/llms/separator.md +87 -0
- package/dist/llms/sheet.md +202 -0
- package/dist/llms/sidebar-primary.md +360 -0
- package/dist/llms/sidebar-secondary.md +417 -0
- package/dist/llms/skeleton.md +81 -0
- package/dist/llms/slider.md +97 -0
- package/dist/llms/spinner.md +100 -0
- package/dist/llms/stack.md +80 -0
- package/dist/llms/stepper-item.md +309 -0
- package/dist/llms/stepper.md +251 -0
- package/dist/llms/switch.md +140 -0
- package/dist/llms/table.md +436 -0
- package/dist/llms/tabs.md +197 -0
- package/dist/llms/tag.md +139 -0
- package/dist/llms/timeline.md +361 -0
- package/dist/llms/timer.md +259 -0
- package/dist/llms/toast.md +241 -0
- package/dist/llms/toggle-group.md +121 -0
- package/dist/llms/toolbar.md +453 -0
- package/dist/llms/tooltip.md +164 -0
- package/dist/llms/treemap.md +254 -0
- package/dist/llms/trend-indicator.md +132 -0
- package/dist/llms/widget-placeholder.md +169 -0
- package/dist/llms/wizard.md +412 -0
- package/dist/llms.txt +142 -0
- package/dist/react.js +540 -525
- package/dist/react.js.map +1 -1
- package/dist/src/components/ui/accordion-container/accordion-container.d.ts +44 -0
- package/dist/src/components/ui/accordion-container/index.d.ts +1 -0
- package/dist/src/components/ui/area-chart/area-chart.d.ts +9 -4
- package/dist/src/components/ui/avatar/avatar.d.ts +27 -8
- package/dist/src/components/ui/bar-chart/bar-chart.d.ts +9 -4
- package/dist/src/components/ui/button/button.d.ts +1 -1
- package/dist/src/components/ui/card/card.d.ts +69 -8
- package/dist/src/components/ui/card/index.d.ts +1 -1
- package/dist/src/components/ui/card-section/card-section.d.ts +50 -0
- package/dist/src/components/ui/card-section/index.d.ts +1 -0
- package/dist/src/components/ui/category-bar/category-bar.d.ts +10 -4
- package/dist/src/components/ui/chart/__stories__/palette-control.d.ts +48 -0
- package/dist/src/components/ui/chart/__tests__/chart-palette.test.d.ts +1 -0
- package/dist/src/components/ui/chart/chart-palette.d.ts +128 -0
- package/dist/src/components/ui/chart/chart.d.ts +66 -15
- package/dist/src/components/ui/chart/index.d.ts +4 -2
- package/dist/src/components/ui/chart-state/chart-state-illustrations.d.ts +6 -0
- package/dist/src/components/ui/chart-state/chart-state.d.ts +13 -2
- package/dist/src/components/ui/chart-state/index.d.ts +1 -0
- package/dist/src/components/ui/chart-widget/chart-widget.d.ts +64 -0
- package/dist/src/components/ui/chart-widget/index.d.ts +1 -0
- package/dist/src/components/ui/composed-chart/composed-chart.d.ts +12 -7
- package/dist/src/components/ui/confidence-cone/confidence-cone.d.ts +10 -5
- package/dist/src/components/ui/dialog/dialog.d.ts +1 -1
- package/dist/src/components/ui/empty-overlay/empty-overlay.d.ts +20 -0
- package/dist/src/components/ui/empty-overlay/index.d.ts +2 -0
- package/dist/src/components/ui/funnel-chart/funnel-chart.d.ts +11 -5
- package/dist/src/components/ui/grid/grid.d.ts +1 -1
- package/dist/src/components/ui/histogram/histogram.d.ts +10 -4
- package/dist/src/components/ui/label/index.d.ts +1 -1
- package/dist/src/components/ui/label/label.d.ts +1 -5
- package/dist/src/components/ui/line-chart/line-chart.d.ts +9 -4
- package/dist/src/components/ui/metric/metric.d.ts +13 -14
- package/dist/src/components/ui/pie-chart/pie-chart.d.ts +17 -8
- package/dist/src/components/ui/radar-chart/radar-chart.d.ts +10 -5
- package/dist/src/components/ui/radial-bar-chart/radial-bar-chart.d.ts +17 -6
- package/dist/src/components/ui/sankey-chart/sankey-chart.d.ts +10 -5
- package/dist/src/components/ui/scatter-chart/scatter-chart.d.ts +10 -5
- package/dist/src/components/ui/section/index.d.ts +1 -1
- package/dist/src/components/ui/section/section.d.ts +75 -6
- package/dist/src/components/ui/sheet/sheet.d.ts +1 -1
- package/dist/src/components/ui/stack/stack.d.ts +1 -1
- package/dist/src/components/ui/stepper-item/stepper-item.d.ts +6 -5
- package/dist/src/components/ui/tag/tag.d.ts +1 -1
- package/dist/src/components/ui/treemap/treemap.d.ts +11 -5
- package/dist/src/components/ui/trend-indicator/trend-indicator.d.ts +3 -7
- package/dist/src/components/ui/wizard/index.d.ts +1 -0
- package/dist/src/components/ui/wizard/wizard.d.ts +6 -0
- package/dist/src/hooks/index.d.ts +1 -0
- package/dist/src/hooks/use-wizard.d.ts +80 -0
- package/dist/src/index.d.ts +5 -3
- package/dist/ui-react.css +1 -1
- package/package.json +12 -6
- package/dist/components/ui/app-shell/app-shell.js +0 -75
- package/dist/components/ui/app-shell/app-shell.js.map +0 -1
- package/dist/components/ui/auth-layout/auth-layout.js +0 -56
- package/dist/components/ui/auth-layout/auth-layout.js.map +0 -1
- package/dist/components/ui/collapsible/collapsible.js +0 -22
- package/dist/components/ui/collapsible/collapsible.js.map +0 -1
- package/dist/src/components/ui/app-shell/app-shell.d.ts +0 -8
- package/dist/src/components/ui/app-shell/index.d.ts +0 -1
- package/dist/src/components/ui/auth-layout/auth-layout.d.ts +0 -6
- package/dist/src/components/ui/auth-layout/index.d.ts +0 -1
- package/dist/src/components/ui/collapsible/collapsible.d.ts +0 -5
- package/dist/src/components/ui/collapsible/index.d.ts +0 -1
- package/dist/src/components/ui/label/label.docs.d.ts +0 -10
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
# LineChart
|
|
2
|
+
|
|
3
|
+
> A typed line-chart composition over the shared `Chart` primitives. Takes `data` + `config` + a set of series `dataKeys` and renders a themed recharts `LineChart` inside a `ChartContainer`, with tooltip and legend chrome. Two variant axes: `curve` (linear / monotone / step interpolation) and `lineStyle` (solid / dashed stroke); single vs multi line is driven by the number of `dataKeys`. Ported from the apps/demo `ChartPlayground`.
|
|
4
|
+
|
|
5
|
+
**Status:** draft · **Category:** data-display · **Since:** v0.58.0
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { LineChart } from '@acronis-platform/ui-react'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick example
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<LineChart
|
|
17
|
+
config={config}
|
|
18
|
+
data={data}
|
|
19
|
+
dataKeys={['desktop', 'mobile']}
|
|
20
|
+
xKey="month"
|
|
21
|
+
curve="monotone"
|
|
22
|
+
lineStyle="solid"
|
|
23
|
+
className="h-[320px] w-[560px]"
|
|
24
|
+
/>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
A typed line-chart built on the shared `Chart` primitives. Give it `data`, a
|
|
28
|
+
per-series `config`, the series to plot (`dataKeys`), and the category key
|
|
29
|
+
(`xKey`); it renders a themed recharts `LineChart` — tooltip, legend, axes, and
|
|
30
|
+
grid included — so you don't hand-compose recharts children.
|
|
31
|
+
|
|
32
|
+
Series colors come from the `palette` prop — a dataviz palette resolving to the
|
|
33
|
+
`--ui-dataviz-*` tokens, `categorical` by default — not from `config`.
|
|
34
|
+
|
|
35
|
+
> **Design-pending v1.** Ported from the apps/demo `ChartPlayground`. The chrome
|
|
36
|
+
> is reconciled with Figma later; Code Connect is deferred.
|
|
37
|
+
|
|
38
|
+
## When to use
|
|
39
|
+
|
|
40
|
+
- Showing a trend over a continuous or ordered dimension (revenue by month,
|
|
41
|
+
latency over time).
|
|
42
|
+
- Comparing the trends of a few series on shared axes.
|
|
43
|
+
|
|
44
|
+
## When not to use
|
|
45
|
+
|
|
46
|
+
- Comparing a quantity across discrete categories — use a bar chart.
|
|
47
|
+
- Part-to-whole of a single total — consider a pie/donut chart.
|
|
48
|
+
- A single metric or KPI — use a `Tag`, `Badge`, or plain text.
|
|
49
|
+
- Tabular detail — use `Table` / `DataTable`.
|
|
50
|
+
|
|
51
|
+
## Variants
|
|
52
|
+
|
|
53
|
+
| Axis | Values | Effect |
|
|
54
|
+
| ----------- | ------------------------------ | ------------------------------------------------------------ |
|
|
55
|
+
| `curve` | `linear` · `monotone` · `step` | Straight, smoothed, or stepped interpolation between points. |
|
|
56
|
+
| `lineStyle` | `solid` · `dashed` | Solid or dashed stroke. |
|
|
57
|
+
|
|
58
|
+
Single vs multi line is not a variant — it follows from how many `dataKeys` you
|
|
59
|
+
plot.
|
|
60
|
+
|
|
61
|
+
## Example
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { LineChart } from '@acronis-platform/ui-react';
|
|
65
|
+
import type { ChartConfig } from '@acronis-platform/ui-react';
|
|
66
|
+
|
|
67
|
+
const data = [
|
|
68
|
+
{ month: 'Jan', desktop: 186, mobile: 80 },
|
|
69
|
+
{ month: 'Feb', desktop: 305, mobile: 200 },
|
|
70
|
+
{ month: 'Mar', desktop: 237, mobile: 120 },
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
const config = {
|
|
74
|
+
desktop: { label: 'Desktop', color: 'var(--ui-background-brand-secondary)' },
|
|
75
|
+
mobile: {
|
|
76
|
+
label: 'Mobile',
|
|
77
|
+
color: 'var(--ui-background-status-strong-danger)',
|
|
78
|
+
},
|
|
79
|
+
} satisfies ChartConfig;
|
|
80
|
+
|
|
81
|
+
<LineChart
|
|
82
|
+
config={config}
|
|
83
|
+
data={data}
|
|
84
|
+
dataKeys={['desktop', 'mobile']}
|
|
85
|
+
xKey="month"
|
|
86
|
+
className="h-[320px] w-[560px]"
|
|
87
|
+
/>;
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Series colors reference existing semantic `--ui-*` tokens. `--ui-background-status-strong-*`
|
|
91
|
+
is chromatic in every brand; `--ui-background-brand-secondary` is brand-dependent
|
|
92
|
+
(blue in `default`, neutral in some white-label brands), so it is not color-stable
|
|
93
|
+
across brands until the real data-viz palette lands.
|
|
94
|
+
|
|
95
|
+
## Props
|
|
96
|
+
|
|
97
|
+
| Prop | Type | Default | Required | Description |
|
|
98
|
+
|------|------|---------|----------|-------------|
|
|
99
|
+
| `curve` | `'linear' | 'monotone' | 'natural' | 'basis' | 'step' | 'stepBefore' | 'stepAfter'` | `'monotone'` | no | How each line interpolates between points. `linear` = straight segments; `monotone` = a smoothed curve that never overshoots a point; `natural` = a natural cubic spline (smoother, may overshoot); `basis` = the smoothest option, and not required to pass through the points at all; `step` = right-angle segments changing at the midpoint, with `stepBefore` / `stepAfter` changing at the leading or trailing point instead. |
|
|
100
|
+
| `lineStyle` | `'solid' | 'dashed'` | `'solid'` | no | Stroke style of every line — solid or dashed. |
|
|
101
|
+
| `data` | `ReadonlyArray<Record<string, string | number | null>>` | — | yes | Row-per-point data — each row holds `xKey` plus one numeric field per series. A `null` breaks the line at that point unless `connectNulls`. |
|
|
102
|
+
| `config` | `ChartConfig` | — | yes | Series take their colour from the container's `palette`; each entry maps a data key to a `label` and an optional `tone`. Resolved into the `--color-<key>` custom properties the lines stroke from. |
|
|
103
|
+
| `dataKeys` | `string[]` | — | yes | Series to plot — one line per key. Each must exist in `config` and every data row. |
|
|
104
|
+
| `xKey` | `string` | — | yes | The category dimension key shared across rows. |
|
|
105
|
+
| `comparisonKeys` | `string[]` | — | no | Subset of `dataKeys` rendered as comparison/trend overlays (e.g. a previous quarter or year) — dashed, dimmed, and dot-less, keeping each series' own `config` color. |
|
|
106
|
+
| `deltaBands` | `[string, string][]` | — | no | Pairs `[current, comparison]` to shade a delta band between — a dimmed area filling the gap between the two series at each point (the QoQ/YoY difference), tinted with the first key's `config` color. |
|
|
107
|
+
| `xAxisLabel` | `string` | — | no | Title rendered beneath the horizontal (X) axis. |
|
|
108
|
+
| `yAxisLabel` | `string` | — | no | Title rendered beside the vertical (Y) axis (rotated). |
|
|
109
|
+
| `yUnit` | `string` | — | no | Unit suffix appended to Y-axis tick values (recharts `unit`); the X axis is categorical. |
|
|
110
|
+
| `strokeWidth` | `number` | `2` | no | Stroke width of each line. |
|
|
111
|
+
| `showDots` | `boolean` | `true` | no | Render a dot at each data point. |
|
|
112
|
+
| `dotSize` | `number` | `3` | no | Radius of each point's dot, in px. Its hover dot is 2px larger. |
|
|
113
|
+
| `showActiveDot` | `boolean` | — | no | Enlarge the hovered point's dot. Defaults to following `showDots`, so set it explicitly for a hover-only line (dots off, active dot on) or a static-only line (dots on, active dot off). |
|
|
114
|
+
| `lineSettings` | `Record<string, { color?: string; strokeWidth?: number; dashed?: boolean; curveType?: CurveType; showDots?: boolean; dotSize?: number; showLabel?: boolean; labelPosition?: LabelPosition }>` | — | no | Per-series style override keyed by `dataKeys` entry — anything unset falls back to the chart-wide prop, so an entry states only what differs. A series listed in `comparisonKeys` keeps its dashed, dimmed, dot-less treatment: `color` / `strokeWidth` / `curveType` still apply to it, but `showDots` / `dotSize` do not. |
|
|
115
|
+
| `referenceLine` | `Line | Line[] where Line = { value?: number; average?: boolean | string; label?: string; labelPosition?: 'top' | 'insideTopLeft' | 'insideTopRight' | 'insideBottomLeft' | 'insideBottomRight' }` | — | no | One or more dashed reference/average lines on the value axis. Pass a single object or an array. Per line, a fixed `value` wins; otherwise the mean of one series (`average: '<key>'`) or of every plotted series (`average: true`, which includes any `comparisonKeys` overlay). An optional `label` captions each, at the rule's top right unless `labelPosition` moves it away from a series it would sit on. The same config shape as `BarChart` / `AreaChart`. |
|
|
116
|
+
| `connectNulls` | `boolean` | `false` | no | Bridge `null` gaps in the data instead of breaking the line. |
|
|
117
|
+
| `showGrid` | `boolean` | `true` | no | Render the CartesianGrid. |
|
|
118
|
+
| `showTooltip` | `boolean` | `true` | no | Render the hover tooltip. |
|
|
119
|
+
| `showLegend` | `boolean` | `true` | no | Render the legend. |
|
|
120
|
+
| `tooltipContent` | `ReactElement | ((props) => ReactNode)` | — | no | Replace the default tooltip. Pass a configured ChartTooltipContent (from this library) — e.g. with a formatter / labelFormatter — to customize the tooltip content without composing recharts. Ignored when showTooltip is false. |
|
|
121
|
+
| `showXAxis` | `boolean` | `true` | no | Render the X axis (its ticks and title). Set false to hide it. |
|
|
122
|
+
| `showYAxis` | `boolean` | `true` | no | Render the Y axis (its ticks and title). Set false to hide it. |
|
|
123
|
+
| `xTickFormatter` | `(value: number | string, index?: number) => string` | — | no | Format each X-axis tick value. Unlike the `unit` suffix (append-only), a formatter transforms the value — abbreviate, prefix a symbol, or map to a label. Use formatCompactNumber / formatPercent / createTickFormatter from the shared Chart primitives, or any function. |
|
|
124
|
+
| `yTickFormatter` | `(value: number | string, index?: number) => string` | — | no | Format each Y-axis tick value (see xTickFormatter). |
|
|
125
|
+
| `xAxisAngle` | `number` | — | no | Rotate the X-axis tick labels by this many degrees (a negative angle tilts them up toward the right); the tick anchor + axis height adjust. |
|
|
126
|
+
| `xAxisInterval` | `number | 'preserveStart' | 'preserveEnd' | 'preserveStartEnd' | 'equidistantPreserveStart'` | — | no | X-axis tick density — a recharts placement strategy, or a fixed number of ticks to skip between two rendered ones (recharts interval: 0 shows every tick, 1 every other one, 2 every third). |
|
|
127
|
+
| `yAxisTickCount` | `number` | — | no | Desired number of ticks on the value axis (recharts tickCount; a hint, not exact). Applies to whichever axis holds the values — Y for most charts, X for BarChart with orientation="horizontal". |
|
|
128
|
+
| `yAxisDomain` | `'auto' | 'dataMin-dataMax' | 'zero'` | — | no | Value-axis domain preset — auto (fit the data at both ends; the axis need not include 0), zero (anchor the axis at 0, which is also recharts' behaviour when the prop is omitted), or dataMin-dataMax (tight to the data, no padding). Applies to whichever axis holds the values — Y for most charts, X for BarChart with orientation="horizontal". |
|
|
129
|
+
| `gridDashed` | `boolean` | — | no | Draw grid lines dashed instead of solid. |
|
|
130
|
+
| `gridHorizontal` | `boolean` | — | no | Show horizontal grid lines (defaults to the chart's own default when unset). |
|
|
131
|
+
| `gridVertical` | `boolean` | — | no | Show vertical grid lines (defaults to the chart's own default when unset). |
|
|
132
|
+
| `animate` | `boolean` | `false` | no | Enable entrance animation. Off by default so output/baselines are unchanged. |
|
|
133
|
+
| `animationDuration` | `number` | — | no | Animation duration in ms. |
|
|
134
|
+
| `animationBegin` | `number` | — | no | Delay before the animation starts, in ms. |
|
|
135
|
+
| `animationEasing` | `'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'linear'` | — | no | Easing curve. |
|
|
136
|
+
| `showLabels` | `boolean` | `false` | no | Render a value label on each data point/segment. |
|
|
137
|
+
| `labelPosition` | `'top' | 'bottom' | 'left' | 'right' | 'center' | 'insideTop' | 'insideBottom' | 'insideLeft' | 'insideRight' | 'insideStart' | 'insideEnd'` | — | no | Where the value labels sit. |
|
|
138
|
+
| `labelFormatter` | `(value) => string` | — | no | Format each label value (reuses the tick formatters). |
|
|
139
|
+
| `showBrush` | `boolean` | `false` | no | Render a range brush beneath the chart — drag a handle (or the selected window) to zoom the series into a slice of the data. |
|
|
140
|
+
| `brushHeight` | `number` | `28` | no | Height of the brush strip in px. A non-positive value falls back to the default rather than erasing the brush. |
|
|
141
|
+
| `brushAriaLabel` | `string` | `'Chart range selector'` | no | Accessible name for the brush's two range handles, which are focusable `role="slider"` elements. Consumers localize by passing their own. |
|
|
142
|
+
|
|
143
|
+
## Behavior
|
|
144
|
+
|
|
145
|
+
`LineChart` is a typed [recharts](https://recharts.org) composition over the
|
|
146
|
+
shared `Chart` primitives. It takes `data`, a per-series `config`, the series to
|
|
147
|
+
plot (`dataKeys`), and the category key (`xKey`), and renders a themed recharts
|
|
148
|
+
`LineChart` inside a `ChartContainer`.
|
|
149
|
+
|
|
150
|
+
```gherkin
|
|
151
|
+
Scenario: Render lines from data and config
|
|
152
|
+
Given data rows and a config mapping each series key to a label and color
|
|
153
|
+
And dataKeys ["desktop", "mobile"]
|
|
154
|
+
Then one <Line> renders per dataKey
|
|
155
|
+
And each line strokes from its injected --color-<key> custom property
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
```gherkin
|
|
159
|
+
Scenario: Single line
|
|
160
|
+
Given dataKeys has one entry
|
|
161
|
+
Then a single line renders against the category and value axes
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
```gherkin
|
|
165
|
+
Scenario: Multiple lines
|
|
166
|
+
Given dataKeys has two or more entries
|
|
167
|
+
Then one line renders per key on shared axes
|
|
168
|
+
And the legend distinguishes them by color + label
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```gherkin
|
|
172
|
+
Scenario: Curve interpolation (default monotone)
|
|
173
|
+
Given curve is one of linear, monotone, natural, basis, step, stepBefore, stepAfter
|
|
174
|
+
Then every line uses that recharts `type`
|
|
175
|
+
And "linear" draws straight segments, "monotone" smooths them without overshooting a point
|
|
176
|
+
And "natural" smooths further and may overshoot, while "basis" need not pass through the points
|
|
177
|
+
And "step" draws right angles at the midpoint, "stepBefore" at the leading point, "stepAfter" at the trailing one
|
|
178
|
+
And each type draws distinct geometry — an unrecognized value would silently fall back to straight segments
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
```gherkin
|
|
182
|
+
Scenario: Dashed line style
|
|
183
|
+
Given lineStyle is "dashed"
|
|
184
|
+
Then every line strokes with a dashed pattern
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
```gherkin
|
|
188
|
+
Scenario: Dots
|
|
189
|
+
Given showDots is true
|
|
190
|
+
Then a dot renders at each data point with radius dotSize
|
|
191
|
+
And hovering a point enlarges its active dot by 2px
|
|
192
|
+
But when showDots is false neither the static dots nor the hover active dot render
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
```gherkin
|
|
196
|
+
Scenario: Hover dot independent of the static dots
|
|
197
|
+
Given showActiveDot is set
|
|
198
|
+
Then it decides the hover dot on its own instead of following showDots
|
|
199
|
+
And showDots false with showActiveDot true gives a bare line that still emphasizes the hovered point
|
|
200
|
+
And showDots true with showActiveDot false gives static dots with no hover emphasis
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
```gherkin
|
|
204
|
+
Scenario: Restyle one series
|
|
205
|
+
Given lineSettings maps "mobile" to { dashed: true, strokeWidth: 3, showDots: false }
|
|
206
|
+
Then only the "mobile" line is dashed, thicker, and dot-less
|
|
207
|
+
And every other series renders from the chart-wide props
|
|
208
|
+
And an unset field in the entry falls back to the chart-wide prop
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
```gherkin
|
|
212
|
+
Scenario: A comparison overlay keeps its treatment
|
|
213
|
+
Given a series is listed in comparisonKeys
|
|
214
|
+
And its lineSettings entry sets showDots true
|
|
215
|
+
Then the series still renders dot-less — the overlay treatment wins
|
|
216
|
+
But its color, strokeWidth and curveType overrides still apply
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
```gherkin
|
|
220
|
+
Scenario: Per-series value labels
|
|
221
|
+
Given lineSettings maps a series to { showLabel: false }
|
|
222
|
+
And showLabels is true
|
|
223
|
+
Then that series renders no value labels while the others do
|
|
224
|
+
And a { showLabel: true } entry labels only that series with showLabels unset
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
```gherkin
|
|
228
|
+
Scenario: Reference line
|
|
229
|
+
Given referenceLine is { value: 250, label: "Target" }
|
|
230
|
+
Then a dashed rule draws across the value axis at 250, captioned "Target"
|
|
231
|
+
And it is drawn in the muted text token, over the series
|
|
232
|
+
And the axis domain extends to include it, so a target above the data maximum stays visible
|
|
233
|
+
And the caption sits at the rule's top right unless labelPosition moves it
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
```gherkin
|
|
237
|
+
Scenario: Averaged reference line
|
|
238
|
+
Given referenceLine is { average: true } (or a single series key)
|
|
239
|
+
Then the rule draws at the mean of every plotted series' values (or that one series)
|
|
240
|
+
And a comparisonKeys overlay counts as a plotted series in that mean
|
|
241
|
+
And null values are skipped rather than counted as zero
|
|
242
|
+
And when there is nothing numeric to average, no rule is drawn
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
```gherkin
|
|
246
|
+
Scenario: Multiple reference lines
|
|
247
|
+
Given referenceLine is an array of configs (e.g. a fixed target and an average)
|
|
248
|
+
Then one dashed rule draws per config
|
|
249
|
+
And each is resolved independently (a config with nothing to draw is skipped)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
```gherkin
|
|
253
|
+
Scenario: Null gaps
|
|
254
|
+
Given a data row has a null value for a series
|
|
255
|
+
And connectNulls is false
|
|
256
|
+
Then that line breaks at the gap
|
|
257
|
+
But when connectNulls is true the line bridges the gap
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
```gherkin
|
|
261
|
+
Scenario: Tooltip on hover
|
|
262
|
+
Given showTooltip is true
|
|
263
|
+
When the user hovers a point
|
|
264
|
+
Then a card shows the category label and one row per series (indicator + value)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
```gherkin
|
|
268
|
+
Scenario: Legend
|
|
269
|
+
Given showLegend is true
|
|
270
|
+
Then a swatch + label renders for each series in dataKeys
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
```gherkin
|
|
274
|
+
Scenario: Comparison / trend overlay
|
|
275
|
+
Given comparisonKeys lists a subset of dataKeys (e.g. ["lastYear"])
|
|
276
|
+
Then those series render dashed, dimmed, and without dots
|
|
277
|
+
And the remaining series render normally (current lineStyle / showDots)
|
|
278
|
+
And each series keeps its own config color
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
```gherkin
|
|
282
|
+
Scenario: Delta band between two series
|
|
283
|
+
Given deltaBands lists a pair [current, comparison] (e.g. [["thisYear","lastYear"]])
|
|
284
|
+
Then a dimmed area shades the gap between the two series at each point
|
|
285
|
+
And the band is tinted with the current key's config color and sits behind the lines
|
|
286
|
+
And it follows the current key's curveType, so its edges match the line it shades
|
|
287
|
+
And points where either series is non-numeric are left un-banded
|
|
288
|
+
And the band is not surfaced as its own tooltip row or legend entry
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
```gherkin
|
|
292
|
+
Scenario: Empty data
|
|
293
|
+
Given data is an empty array
|
|
294
|
+
Then the chart renders its axes and grid with no lines and does not throw
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
```gherkin
|
|
298
|
+
Scenario: Entrance animation is opt-in
|
|
299
|
+
Given animate is unset
|
|
300
|
+
Then the series render at their final geometry with no entrance animation
|
|
301
|
+
And the committed visual baselines are unaffected
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
```gherkin
|
|
305
|
+
Scenario: Reduced motion
|
|
306
|
+
Given animate is true
|
|
307
|
+
And a user with prefers-reduced-motion
|
|
308
|
+
Then the entrance animation does not play and the series render at their final geometry
|
|
309
|
+
And the same applies when rendering on the server
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
```gherkin
|
|
313
|
+
Scenario: Range brush
|
|
314
|
+
Given showBrush is true
|
|
315
|
+
Then a brush strip renders beneath the plot with a handle at each end
|
|
316
|
+
And dragging a handle (or the selected window) zooms the series to that slice
|
|
317
|
+
And the category axis and tooltip follow the selected range
|
|
318
|
+
And the brush renders whether or not the category axis is shown
|
|
319
|
+
And unset renders no brush
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
```gherkin
|
|
323
|
+
Scenario: Range brush keyboard and accessible name
|
|
324
|
+
Given showBrush is true
|
|
325
|
+
Then each of the two handles is reachable by Tab
|
|
326
|
+
And the focused handle moves one row per left/right arrow key press
|
|
327
|
+
And each handle exposes brushAriaLabel as its accessible name
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Accessibility
|
|
331
|
+
|
|
332
|
+
- recharts' `accessibilityLayer` is **on by default** (recharts v3), giving the
|
|
333
|
+
chart keyboard focus and an accessible description of the plotted points.
|
|
334
|
+
- The **range brush** (`showBrush`) is the only interactive control these charts
|
|
335
|
+
render. It adds two focusable `role="slider"` handles: Tab reaches each one and
|
|
336
|
+
the left/right arrow keys move the selection a row at a time, so the whole
|
|
337
|
+
feature is operable without a pointer.
|
|
338
|
+
- Both handles take their accessible name from `brushAriaLabel` (default
|
|
339
|
+
`'Chart range selector'`); pass your own to localize it. Never leave it to
|
|
340
|
+
recharts' fallback, which names the handles from a `name` field on the data
|
|
341
|
+
row and otherwise announces "Min value: undefined, Max value: undefined".
|
|
342
|
+
- The handles keep a visible focus indicator: the shared `ChartContainer`
|
|
343
|
+
suppresses outlines on recharts layers, so it re-enables one for
|
|
344
|
+
`.recharts-brush-traveller` specifically (an `outline`, since `box-shadow`
|
|
345
|
+
does not paint on SVG children).
|
|
346
|
+
- The two range captions only appear while a handle is hovered, dragged, or
|
|
347
|
+
focused — they are a supplement to the category axis, not a replacement for
|
|
348
|
+
it. Zooming changes which rows are plotted, so **pair the brush with a text
|
|
349
|
+
alternative that reflects the current range** rather than the full series.
|
|
350
|
+
- A line chart is inherently visual. **Pair it with a text alternative** — a
|
|
351
|
+
caption, a summary sentence, or an adjacent data table carrying the same
|
|
352
|
+
numbers — and give the chart an accessible name (`aria-label` /
|
|
353
|
+
`aria-labelledby` referencing a visible heading) when it is meaningful on its
|
|
354
|
+
own. The wrapper forwards native `div` attributes, so `aria-*` pass through.
|
|
355
|
+
- Do **not** rely on color alone to distinguish series. Keep `showLegend` (or the
|
|
356
|
+
tooltip) visible so each color is paired with a text label; the `lineStyle`
|
|
357
|
+
(solid / dashed) and `curve` variants add a non-color channel between lines.
|
|
358
|
+
- The chrome (tooltip, legend, axis ticks, grid) resolves to semantic `--ui-*`
|
|
359
|
+
tokens that meet contrast in light and dark. **Series colors are
|
|
360
|
+
caller-supplied** via `config` — pick values that meet 3:1 against the surface
|
|
361
|
+
and are distinguishable for color-vision deficiencies. The borrowed semantic
|
|
362
|
+
tokens are a design-pending stopgap until the `--ui-chart-*` palette lands.
|
|
363
|
+
- Watch recharts issue [#4809](https://github.com/recharts/recharts/issues/4809)
|
|
364
|
+
on the a11y layer for heavily-customized charts.
|
|
365
|
+
|
|
366
|
+
## Contrast
|
|
367
|
+
|
|
368
|
+
Chart chrome meets contrast in both themes via the semantic tokens. Line strokes
|
|
369
|
+
come from `config` and are the caller's responsibility.
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# Link
|
|
2
|
+
|
|
3
|
+
> An inline text link (semibold) that underlines on hover, with an optional trailing external-link icon. Navigates to an href or composes a router link. Themed by the `--ui-link-*` token tier.
|
|
4
|
+
|
|
5
|
+
**Status:** ready · **Category:** navigation · **Since:** v0.31.0
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { Link } from '@acronis-platform/ui-react'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick example
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<Link href="/docs">Documentation</Link>
|
|
17
|
+
|
|
18
|
+
{/* External */}
|
|
19
|
+
<Link href="https://acronis.com" external target="_blank" rel="noreferrer">
|
|
20
|
+
Acronis
|
|
21
|
+
</Link>
|
|
22
|
+
|
|
23
|
+
{/* On a backdrop / dark brand surface */}
|
|
24
|
+
<Link href="/docs" variant="inverse">Documentation</Link>
|
|
25
|
+
|
|
26
|
+
{/* Polymorphic — render a router link */}
|
|
27
|
+
<Link render={<RouterLink to="/settings" />}>Settings</Link>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
An inline text link (semibold) that underlines on hover, with an optional trailing
|
|
31
|
+
external-link icon. Navigates via `href` or composes a router link through `render`.
|
|
32
|
+
Themed by the `--ui-link-*` token tier, which carries one token set per surface —
|
|
33
|
+
`variant="normal"` (default) and `variant="inverse"` for links over a backdrop.
|
|
34
|
+
|
|
35
|
+
## When to use
|
|
36
|
+
|
|
37
|
+
- Inline navigation within prose or UI — to another page, a section, or an external
|
|
38
|
+
resource (`external`).
|
|
39
|
+
- Over a scrim, dark brand banner, or other dark surface — with `variant="inverse"`.
|
|
40
|
+
|
|
41
|
+
## When not to use
|
|
42
|
+
|
|
43
|
+
- For a primary/secondary **action** (submit, open dialog) — use `Button` (its
|
|
44
|
+
borderless `ghost`/link-style variant reads like a link but is a real button).
|
|
45
|
+
- For breadcrumb trails — use `Breadcrumb`.
|
|
46
|
+
|
|
47
|
+
## Examples
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
import { Link } from '@acronis-platform/ui-react';
|
|
51
|
+
|
|
52
|
+
// Internal
|
|
53
|
+
<Link href="/docs">Documentation</Link>;
|
|
54
|
+
|
|
55
|
+
// External (trailing icon)
|
|
56
|
+
<Link
|
|
57
|
+
href="https://acronis.com"
|
|
58
|
+
external
|
|
59
|
+
target="_blank"
|
|
60
|
+
rel="noopener noreferrer"
|
|
61
|
+
>
|
|
62
|
+
Acronis
|
|
63
|
+
</Link>;
|
|
64
|
+
|
|
65
|
+
// Polymorphic — render a router link, keep the Link styling
|
|
66
|
+
<Link render={<RouterLink to="/settings" />}>Settings</Link>;
|
|
67
|
+
|
|
68
|
+
// On a backdrop / dark brand surface. Text-only and always enabled: both `external`
|
|
69
|
+
// and `disabled` are ignored here.
|
|
70
|
+
<Link href="/docs" variant="inverse">
|
|
71
|
+
Documentation
|
|
72
|
+
</Link>;
|
|
73
|
+
|
|
74
|
+
// Disabled (inert) — `variant="normal"` only; the prop is ignored on "inverse".
|
|
75
|
+
<Link href="/docs" disabled>
|
|
76
|
+
Documentation
|
|
77
|
+
</Link>;
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Parts
|
|
81
|
+
|
|
82
|
+
| Part | Element | Description |
|
|
83
|
+
| ------- | ------- | ------------------------------------------------------------------------------ |
|
|
84
|
+
| `label` | text | The link text (children). |
|
|
85
|
+
| `icon` | `<svg>` | Optional trailing external-link icon (`external`), on `variant="normal"` only. |
|
|
86
|
+
|
|
87
|
+
## Props
|
|
88
|
+
|
|
89
|
+
| Prop | Type | Default | Required | Description |
|
|
90
|
+
|------|------|---------|----------|-------------|
|
|
91
|
+
| `variant` | `'normal' | 'inverse'` | `'normal'` | no | Which surface the link sits on (Figma's `background` property). `normal` targets ordinary surfaces via the `--ui-link-normal-*` tokens; `inverse` targets a backdrop/scrim or other dark brand surface via `--ui-link-inverse-*`. `inverse` is text-only and always enabled: it supports neither the external icon (see `external`) nor the disabled state (see `disabled`), and both props are ignored there. |
|
|
92
|
+
| `href` | `string` | — | no | Navigation target. Omitted when `disabled`. |
|
|
93
|
+
| `external` | `boolean` | `false` | no | Append a trailing external-link icon (for links that leave the app). Ignored on `variant="inverse"`: the design carries the icon layer only on the `normal` surface, so no icon is rendered there. |
|
|
94
|
+
| `disabled` | `boolean` | `false` | no | Render the link inert — disabled color, removed from the tab order (`tabindex=-1`), `aria-disabled`, and no navigation (href dropped). Ignored entirely on `variant="inverse"`: the design defines no disabled state on a backdrop, so the link renders exactly as an enabled one — still navigable, focusable and hoverable, with no `aria-disabled`. Omit the link when it must be inert on that surface. |
|
|
95
|
+
|
|
96
|
+
## Events
|
|
97
|
+
|
|
98
|
+
| Event | Payload | Description |
|
|
99
|
+
|-------|---------|-------------|
|
|
100
|
+
| `click` | `MouseEvent` | Native activation (click / Enter), forwarded to the anchor. |
|
|
101
|
+
|
|
102
|
+
## Content slots
|
|
103
|
+
|
|
104
|
+
| Slot | Description |
|
|
105
|
+
|------|-------------|
|
|
106
|
+
| `default` | The link label. |
|
|
107
|
+
|
|
108
|
+
## Behavior
|
|
109
|
+
|
|
110
|
+
### Rendering
|
|
111
|
+
|
|
112
|
+
**Given** an `href` and a label
|
|
113
|
+
**When** the link renders
|
|
114
|
+
**Then** it is an `<a role="link">` with the label in `--ui-link-normal-text-color-idle` (semibold,
|
|
115
|
+
no underline).
|
|
116
|
+
|
|
117
|
+
**Given** `external`
|
|
118
|
+
**Then** a trailing external-link icon is appended (`--ui-link-normal-external-icon-color-*`).
|
|
119
|
+
|
|
120
|
+
### Surface (`variant`)
|
|
121
|
+
|
|
122
|
+
**Given** no `variant`
|
|
123
|
+
**Then** the link uses the `normal` surface — the `--ui-link-normal-*` token set.
|
|
124
|
+
|
|
125
|
+
**Given** `variant="inverse"`
|
|
126
|
+
**When** the link renders on a backdrop/scrim or dark brand surface
|
|
127
|
+
**Then** every state resolves its text color against
|
|
128
|
+
`--ui-link-inverse-text-color-{idle,hover,active}` instead. Text decoration and the
|
|
129
|
+
focus ring are shared with the `normal` surface (`--ui-link-global-text-decoration-*`,
|
|
130
|
+
`--ui-focus-primary`).
|
|
131
|
+
|
|
132
|
+
**Given** `variant="inverse"` **and** `external`
|
|
133
|
+
**When** the link renders
|
|
134
|
+
**Then** no external icon is appended — the design defines the icon layer only on the
|
|
135
|
+
`normal` surface, so `external` is a no-op here (as it is on an inverse Figma
|
|
136
|
+
instance). The link is text-only.
|
|
137
|
+
|
|
138
|
+
### Interaction
|
|
139
|
+
|
|
140
|
+
**Given** the link
|
|
141
|
+
**When** the pointer hovers
|
|
142
|
+
**Then** the text shifts to `--ui-link-normal-text-color-hover` and the
|
|
143
|
+
`--ui-link-global-text-decoration-hover` underline appears (the icon shifts to its hover
|
|
144
|
+
color).
|
|
145
|
+
|
|
146
|
+
**Given** the link
|
|
147
|
+
**When** it is pressed (`:active`)
|
|
148
|
+
**Then** the text/icon shift to their `-active` color.
|
|
149
|
+
|
|
150
|
+
**Given** the link
|
|
151
|
+
**When** it receives keyboard focus
|
|
152
|
+
**Then** a 3px `--ui-focus-primary` ring is shown.
|
|
153
|
+
|
|
154
|
+
**Given** the link
|
|
155
|
+
**When** activated (click / Enter)
|
|
156
|
+
**Then** the native `click` fires and the browser navigates to `href`.
|
|
157
|
+
|
|
158
|
+
### Disabled
|
|
159
|
+
|
|
160
|
+
**Given** `disabled`
|
|
161
|
+
**When** the link renders
|
|
162
|
+
**Then** it uses `--ui-link-normal-text-color-disabled`, drops its `href`, sets
|
|
163
|
+
`aria-disabled="true"` and `tabindex="-1"`, and does not navigate or underline.
|
|
164
|
+
|
|
165
|
+
**Given** `disabled` **and** `variant="inverse"`
|
|
166
|
+
**When** the link renders
|
|
167
|
+
**Then** `disabled` has no effect whatsoever: the link keeps its `href`, sets no
|
|
168
|
+
`aria-disabled`, stays in the tab order, and hovers, underlines and navigates exactly
|
|
169
|
+
like an enabled one. The Figma set has only four enabled inverse variants and marks the
|
|
170
|
+
fifth unsupported ("disable state not supported onBackdrop"), so the prop is discarded
|
|
171
|
+
rather than applied without its color. A link that must be inert on a backdrop should be
|
|
172
|
+
omitted from the UI instead.
|
|
173
|
+
|
|
174
|
+
### Composition
|
|
175
|
+
|
|
176
|
+
**Given** a `render` prop (e.g. a router link component)
|
|
177
|
+
**When** the link renders
|
|
178
|
+
**Then** it renders as that element with the Link's classes/props merged on (Base UI
|
|
179
|
+
`useRender`), so client-side routing works while keeping the Link styling.
|
|
180
|
+
|
|
181
|
+
## Accessibility
|
|
182
|
+
|
|
183
|
+
- **Role:** a native `<a>` with an `href` exposes `role="link"` and is keyboard
|
|
184
|
+
focusable / activatable by default.
|
|
185
|
+
- **Accessible name:** comes from the link text (the children). For icon-only or
|
|
186
|
+
ambiguous links, provide `aria-label`. The external icon is decorative and inherits
|
|
187
|
+
the text color.
|
|
188
|
+
- **External links:** when `external`, also set `target="_blank"` +
|
|
189
|
+
`rel="noopener noreferrer"` as appropriate; consider announcing "opens in a new tab"
|
|
190
|
+
in the label or an `aria-label` for screen-reader users.
|
|
191
|
+
- **Keyboard:** Tab focuses the link; Enter activates it (native anchor behavior).
|
|
192
|
+
- **Focus visible:** keyboard focus paints a 3px `--ui-focus-primary` ring; `:focus`
|
|
193
|
+
is not suppressed without a visible alternative.
|
|
194
|
+
- **Disabled:** `disabled` removes the link from the tab order (`tabindex="-1"`), sets
|
|
195
|
+
`aria-disabled="true"`, and drops the `href` so it is not navigable — state is not
|
|
196
|
+
conveyed by color alone (the control is inert). This applies to `variant="normal"`
|
|
197
|
+
only. On `variant="inverse"` the design defines no disabled state, so the prop is
|
|
198
|
+
ignored outright and the link stays fully interactive and exposed as an enabled link
|
|
199
|
+
to assistive tech. **Do not rely on `disabled` to make a link on a backdrop inert** —
|
|
200
|
+
omit the link, or render it on the `normal` surface, instead.
|
|
201
|
+
- **Decoration:** underline is reserved for hover/affordance; the semibold weight +
|
|
202
|
+
link color distinguish the link at rest. Within body copy, prefer a persistent
|
|
203
|
+
underline for the clearest affordance.
|
|
204
|
+
- **Contrast:** link / hover / active / disabled colors come from the design tokens,
|
|
205
|
+
authored to meet WCAG contrast against the surface. Each surface has its own set —
|
|
206
|
+
use `variant="inverse"` over a backdrop/scrim rather than leaving the `normal`
|
|
207
|
+
tokens to fail contrast there.
|
|
208
|
+
- **WCAG:** 1.4.3 / 1.4.11 (contrast), 2.1.1 (keyboard), 2.4.4 (link purpose), 2.4.7
|
|
209
|
+
(focus visible), 4.1.2.
|