@rojaostudio/ds 2.0.0-next.1 → 2.0.0-next.11

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.
@@ -0,0 +1,66 @@
1
+ # Avisos de terceiros
2
+
3
+ O Rojão DS é distribuído sob a [licença MIT](../../LICENSE). Alguns trechos vêm de projetos de terceiros, ou se apoiam neles, sob as licenças abaixo.
4
+
5
+ ## Incluído no pacote
6
+
7
+ ### Lucide (ícones)
8
+
9
+ Os SVGs em `packages/ds/icons/svg/` e os traçados de `packages/ds/components/internal/icons.tsx` vêm do [Lucide](https://lucide.dev), sob a licença ISC. Parte do Lucide vem do Feather, sob a licença MIT.
10
+
11
+ ```
12
+ ISC License
13
+
14
+ Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as part of Feather (MIT).
15
+ All other copyright (c) for Lucide are held by Lucide Contributors 2022.
16
+
17
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is
18
+ hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
21
+ INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
22
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
23
+ DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
24
+ CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25
+ ```
26
+
27
+ ```
28
+ The MIT License (MIT) — Feather
29
+
30
+ Copyright (c) 2013-2022 Cole Bemis
31
+
32
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
33
+ documentation files (the "Software"), to deal in the Software without restriction, including without
34
+ limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
35
+ Software, and to permit persons to whom the Software is furnished to do so, subject to the following
36
+ conditions:
37
+
38
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions
39
+ of the Software.
40
+
41
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
42
+ TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
43
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
44
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
45
+ DEALINGS IN THE SOFTWARE.
46
+ ```
47
+
48
+ ### Pix
49
+
50
+ O símbolo do Pix (`packages/ds/icons/pix.tsx`) é marca do Banco Central do Brasil. Ele está aqui só para indicar o Pix como meio de pagamento, conforme o manual de uso da marca. A licença MIT deste projeto não se estende a ele.
51
+
52
+ ## Dependências (instaladas pelo gerenciador de pacotes, não redistribuídas)
53
+
54
+ | Pacote | Licença | Uso |
55
+ |---|---|---|
56
+ | `@radix-ui/react-*` | MIT | comportamento acessível de overlays, menus, abas e afins |
57
+ | `lucide-react` (peer opcional) | ISC | re-exportação de ícones em `@rojaostudio/ds/icons` |
58
+ | `react-image-crop` (peer opcional) | ISC | recorte do `ImageCropModal` |
59
+ | `react-international-phone` (peer opcional) | MIT | campo e bandeiras do `PhoneInput` |
60
+
61
+ ## Fontes (referenciadas pelo nome, não distribuídas)
62
+
63
+ | Fonte | Licença |
64
+ |---|---|
65
+ | Inter | SIL Open Font License 1.1 |
66
+ | Roboto Mono | Apache License 2.0 |
@@ -10,7 +10,7 @@ interface ChartSeries {
10
10
  /** line only: which axis it reads on. right only when the two series have different units (volume and rate). */
11
11
  axis?: 'left' | 'right';
12
12
  }
13
- interface ChartProps extends Omit<HTMLAttributes<HTMLElement>, 'children'> {
13
+ interface ChartProps extends Omit<HTMLAttributes<HTMLElement>, 'children' | 'onSelect'> {
14
14
  /** What the chart shows, with the period. It names the chart and captions its data table. */
15
15
  label: string;
16
16
  /** line: lines over time; bar: a ranking in horizontal bars; column: columns by category. */
@@ -32,8 +32,22 @@ interface ChartProps extends Omit<HTMLAttributes<HTMLElement>, 'children'> {
32
32
  showLegend?: boolean;
33
33
  /** The values of the point under the pointer or the keyboard (Figma: `showTooltip`). */
34
34
  showTooltip?: boolean;
35
- /** Off: only the first, middle and last label (Figma: `showAllDates`). Use it under about 360 px. */
35
+ /**
36
+ * line and column: which x labels show (Figma: `dates`). edges, the default: the first, middle and last; all: every
37
+ * label, for short series only.
38
+ */
39
+ dates?: 'edges' | 'all';
40
+ /** line and column: one label every N points, with a tick on every point (Figma: `dates=every`). Wins over `dates`. */
41
+ dateEvery?: number;
42
+ /** @deprecated Use `dates="all"` (true) or `dates="edges"` (false). */
36
43
  showAllDates?: boolean;
44
+ /**
45
+ * bar and column: where each item leads, in the order of `labels`. In bar each bar becomes a link named
46
+ * "<label>: <value>"; in column a click or Enter on the plot follows the item's link.
47
+ */
48
+ hrefs?: Array<string | undefined>;
49
+ /** bar and column: called with the item's index when it is chosen (click, Enter). In bar each bar becomes a button. */
50
+ onSelect?: (index: number) => void;
37
51
  /** Show the data table under the chart. By default it is there only for screen readers. */
38
52
  showTable?: boolean;
39
53
  /** Height of the plot area, in px (Figma: 200). */
@@ -44,6 +58,6 @@ interface ChartProps extends Omit<HTMLAttributes<HTMLElement>, 'children'> {
44
58
  * data (visually hidden unless `showTable`). In line and column the plot is a slider for screen readers and the
45
59
  * keyboard: the arrows move from point to point, and each point is read with its label and values. Styles: chart.css.
46
60
  */
47
- declare function Chart({ label, type, labels, series, formatLeft, formatRight, showRightAxis, showLegend, showTooltip, showAllDates, showTable, height, className, ...rest }: ChartProps): react.JSX.Element;
61
+ declare function Chart({ label, type, labels, series, formatLeft, formatRight, showRightAxis, showLegend, showTooltip, dates, dateEvery, showAllDates, hrefs, onSelect, showTable, height, className, ...rest }: ChartProps): react.JSX.Element;
48
62
 
49
63
  export { Chart, type ChartProps, type ChartSeries, type ChartType };
@@ -36,7 +36,11 @@ function Chart(_a) {
36
36
  showRightAxis = true,
37
37
  showLegend = true,
38
38
  showTooltip = true,
39
- showAllDates = true,
39
+ dates,
40
+ dateEvery,
41
+ showAllDates,
42
+ hrefs,
43
+ onSelect,
40
44
  showTable = false,
41
45
  height = 200,
42
46
  className
@@ -50,7 +54,11 @@ function Chart(_a) {
50
54
  "showRightAxis",
51
55
  "showLegend",
52
56
  "showTooltip",
57
+ "dates",
58
+ "dateEvery",
53
59
  "showAllDates",
60
+ "hrefs",
61
+ "onSelect",
54
62
  "showTable",
55
63
  "height",
56
64
  "className"
@@ -78,7 +86,14 @@ function Chart(_a) {
78
86
  return `${s.name} ${formatFor(s)((_a3 = s.data[index]) != null ? _a3 : 0)}`;
79
87
  }).join(", ");
80
88
  const legendName = (s) => dual ? `${s.name} (${onRight(s) ? "direita" : "esquerda"})` : s.name;
81
- const labelIndexes = showAllDates || count <= 3 ? labels.map((_, i) => i) : [0, Math.floor((count - 1) / 2), count - 1];
89
+ const every = dateEvery && dateEvery > 1 ? Math.floor(dateEvery) : null;
90
+ const allDates = (dates != null ? dates : showAllDates === void 0 ? "edges" : showAllDates ? "all" : "edges") === "all";
91
+ const labelIndexes = every ? labels.map((_, i) => i).filter((i) => i % every === 0) : allDates || count <= 3 ? labels.map((_, i) => i) : [0, Math.floor((count - 1) / 2), count - 1];
92
+ const linked = type !== "line" && Boolean(onSelect || (hrefs == null ? void 0 : hrefs.some(Boolean)));
93
+ const choose = (index) => {
94
+ if (onSelect) onSelect(index);
95
+ else if (hrefs == null ? void 0 : hrefs[index]) window.location.assign(hrefs[index]);
96
+ };
82
97
  const onPointerMove = (event) => {
83
98
  const box = event.currentTarget.getBoundingClientRect();
84
99
  const ratio = Math.min(1, Math.max(0, (event.clientX - box.left) / box.width));
@@ -94,6 +109,11 @@ function Chart(_a) {
94
109
  Home: 0,
95
110
  End: count - 1
96
111
  };
112
+ if (linked && event.key === "Enter" && active !== null) {
113
+ event.preventDefault();
114
+ choose(active);
115
+ return;
116
+ }
97
117
  if (!(event.key in keys)) return;
98
118
  event.preventDefault();
99
119
  setActive(keys[event.key]);
@@ -133,6 +153,7 @@ function Chart(_a) {
133
153
  onPointerLeave: (event) => {
134
154
  if (document.activeElement !== event.currentTarget) setActive(null);
135
155
  },
156
+ onClick: linked ? () => active !== null && choose(active) : void 0,
136
157
  onFocus: () => setActive((current) => current != null ? current : 0),
137
158
  onBlur: () => setActive(null),
138
159
  onKeyDown
@@ -142,19 +163,37 @@ function Chart(_a) {
142
163
  if (type === "bar") {
143
164
  const s = shown[0];
144
165
  const top = Math.max(0, ...(_a2 = s == null ? void 0 : s.data) != null ? _a2 : [0]);
145
- body = /* @__PURE__ */ jsx("div", { className: "rds-chart__bars", "aria-hidden": "true", children: labels.map((name, i) => {
166
+ body = /* @__PURE__ */ jsx("div", { className: "rds-chart__bars", "aria-hidden": linked ? void 0 : true, children: labels.map((name, i) => {
146
167
  var _a3, _b2;
147
- return /* @__PURE__ */ jsxs("div", { className: "rds-chart__bar-row", children: [
168
+ const value = s ? formatLeft((_a3 = s.data[i]) != null ? _a3 : 0) : "";
169
+ const parts = /* @__PURE__ */ jsxs(Fragment, { children: [
148
170
  /* @__PURE__ */ jsx("span", { className: "rds-chart__bar-label", children: name }),
149
171
  /* @__PURE__ */ jsx("span", { className: "rds-chart__track", children: /* @__PURE__ */ jsx(
150
172
  "span",
151
173
  {
152
174
  className: "rds-chart__bar",
153
- style: __spreadProps(__spreadValues({}, seriesStyle(i + 1)), { width: `${top ? ((_a3 = s == null ? void 0 : s.data[i]) != null ? _a3 : 0) / top * 100 : 0}%` })
175
+ style: __spreadProps(__spreadValues({}, seriesStyle(i + 1)), { width: `${top ? ((_b2 = s == null ? void 0 : s.data[i]) != null ? _b2 : 0) / top * 100 : 0}%` })
154
176
  }
155
177
  ) }),
156
- /* @__PURE__ */ jsx("span", { className: "rds-chart__bar-value", children: s ? formatLeft((_b2 = s.data[i]) != null ? _b2 : 0) : "" })
157
- ] }, name);
178
+ /* @__PURE__ */ jsx("span", { className: "rds-chart__bar-value", children: value })
179
+ ] });
180
+ const href = hrefs == null ? void 0 : hrefs[i];
181
+ const accessibleName = `${name}: ${value}`;
182
+ if (linked && href && !onSelect)
183
+ return /* @__PURE__ */ jsx("a", { href, className: "rds-chart__bar-row rds-chart__bar-row--link", "aria-label": accessibleName, children: parts }, name);
184
+ if (linked && onSelect)
185
+ return /* @__PURE__ */ jsx(
186
+ "button",
187
+ {
188
+ type: "button",
189
+ className: "rds-chart__bar-row rds-chart__bar-row--link",
190
+ "aria-label": accessibleName,
191
+ onClick: () => onSelect(i),
192
+ children: parts
193
+ },
194
+ name
195
+ );
196
+ return /* @__PURE__ */ jsx("div", { className: "rds-chart__bar-row", children: parts }, name);
158
197
  }) });
159
198
  } else {
160
199
  body = /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -208,7 +247,10 @@ function Chart(_a) {
208
247
  ] })),
209
248
  rightScale && /* @__PURE__ */ jsx(Axis, { ticks: rightScale.ticks, format: formatRight, side: "right" })
210
249
  ] }),
211
- /* @__PURE__ */ jsx("div", { className: "rds-chart__labels", "aria-hidden": "true", children: /* @__PURE__ */ jsx("div", { className: `rds-chart__labels-track rds-chart__labels-track--${type}`, children: labelIndexes.map((i) => /* @__PURE__ */ jsx("span", { className: "rds-chart__label", style: { left: `${x(i)}%` }, children: labels[i] }, i)) }) })
250
+ /* @__PURE__ */ jsx("div", { className: "rds-chart__labels", "aria-hidden": "true", children: /* @__PURE__ */ jsxs("div", { className: `rds-chart__labels-track rds-chart__labels-track--${type}${every ? " rds-chart__labels-track--ticks" : ""}`, children: [
251
+ every && labels.map((_, i) => /* @__PURE__ */ jsx("i", { className: "rds-chart__tick", style: { left: `${x(i)}%` } }, `t${i}`)),
252
+ labelIndexes.map((i) => /* @__PURE__ */ jsx("span", { className: "rds-chart__label", style: { left: `${x(i)}%` }, children: labels[i] }, i))
253
+ ] }) })
212
254
  ] });
213
255
  }
214
256
  const firstColumn = type === "line" ? "Data" : "Categoria";
@@ -216,7 +258,7 @@ function Chart(_a) {
216
258
  "figure",
217
259
  __spreadProps(__spreadValues({}, rest), {
218
260
  "aria-label": label,
219
- className: ["rds-chart", `rds-chart--${type}`, dual && "rds-chart--dual", className].filter(Boolean).join(" "),
261
+ className: ["rds-chart", `rds-chart--${type}`, dual && "rds-chart--dual", linked && "rds-chart--linked", className].filter(Boolean).join(" "),
220
262
  children: [
221
263
  body,
222
264
  showLegend && type !== "bar" && /* @__PURE__ */ jsx("ul", { className: "rds-chart__legend", "aria-hidden": "true", children: shown.map((s, n) => /* @__PURE__ */ jsxs("li", { children: [
@@ -229,26 +271,30 @@ function Chart(_a) {
229
271
  shown.map((s) => /* @__PURE__ */ jsx(TableHead, { align: "end", children: legendName(s) }, s.name))
230
272
  ] }) }),
231
273
  /* @__PURE__ */ jsx(TableBody, { children: labels.map((name, i) => /* @__PURE__ */ jsxs(TableRow, { children: [
232
- /* @__PURE__ */ jsx(TableCell, { children: name }),
274
+ /* @__PURE__ */ jsx(TableCell, { children: linked && onSelect ? /* @__PURE__ */ jsx("button", { type: "button", className: "rds-chart__table-link", onClick: () => onSelect(i), children: name }) : linked && (hrefs == null ? void 0 : hrefs[i]) ? /* @__PURE__ */ jsx("a", { className: "rds-chart__table-link", href: hrefs[i], children: name }) : name }),
233
275
  shown.map((s) => {
234
276
  var _a3;
235
277
  return /* @__PURE__ */ jsx(TableCell, { align: "end", children: formatFor(s)((_a3 = s.data[i]) != null ? _a3 : 0) }, s.name);
236
278
  })
237
279
  ] }, name)) })
238
- ] }) : /* @__PURE__ */ jsxs("table", { className: "rds-visually-hidden", children: [
239
- /* @__PURE__ */ jsx("caption", { children: label }),
240
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
241
- /* @__PURE__ */ jsx("th", { scope: "col", children: firstColumn }),
242
- shown.map((s) => /* @__PURE__ */ jsx("th", { scope: "col", children: legendName(s) }, s.name))
243
- ] }) }),
244
- /* @__PURE__ */ jsx("tbody", { children: labels.map((name, i) => /* @__PURE__ */ jsxs("tr", { children: [
245
- /* @__PURE__ */ jsx("th", { scope: "row", children: name }),
246
- shown.map((s) => {
247
- var _a3;
248
- return /* @__PURE__ */ jsx("td", { children: formatFor(s)((_a3 = s.data[i]) != null ? _a3 : 0) }, s.name);
249
- })
250
- ] }, name)) })
251
- ] })
280
+ ] }) : (
281
+ // The clip goes on a wrapper: a <table> ignores the 1px box and overflow of .rds-visually-hidden, so it
282
+ // kept its full size, anchored to the page and stretched the page scroll.
283
+ /* @__PURE__ */ jsx("div", { className: "rds-visually-hidden", children: /* @__PURE__ */ jsxs("table", { children: [
284
+ /* @__PURE__ */ jsx("caption", { children: label }),
285
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
286
+ /* @__PURE__ */ jsx("th", { scope: "col", children: firstColumn }),
287
+ shown.map((s) => /* @__PURE__ */ jsx("th", { scope: "col", children: legendName(s) }, s.name))
288
+ ] }) }),
289
+ /* @__PURE__ */ jsx("tbody", { children: labels.map((name, i) => /* @__PURE__ */ jsxs("tr", { children: [
290
+ /* @__PURE__ */ jsx("th", { scope: "row", children: name }),
291
+ shown.map((s) => {
292
+ var _a3;
293
+ return /* @__PURE__ */ jsx("td", { children: formatFor(s)((_a3 = s.data[i]) != null ? _a3 : 0) }, s.name);
294
+ })
295
+ ] }, name)) })
296
+ ] }) })
297
+ )
252
298
  ]
253
299
  })
254
300
  );
@@ -0,0 +1,40 @@
1
+ import * as react from 'react';
2
+ import { ComponentPropsWithRef, ReactNode } from 'react';
3
+
4
+ interface ChoiceListProps extends Omit<ComponentPropsWithRef<'ul'>, 'children' | 'defaultValue' | 'onChange'> {
5
+ /** The ChoiceListItems (Figma: the slot `items`). */
6
+ children: ReactNode;
7
+ /** Controlled: the chosen row's value. */
8
+ value?: string;
9
+ /** Uncontrolled: the row chosen at first. */
10
+ defaultValue?: string;
11
+ onValueChange?: (value: string) => void;
12
+ }
13
+ /**
14
+ * ChoiceList — Figma [RDS] Content/ChoiceList. A dense list to choose one thing from: a client, a product, an
15
+ * order. Each row is media, a title, a muted description and a trailing value; a line under each row, dozens on
16
+ * screen. One chosen at a time, marked by the tint and a bar on the left (role listbox, aria-selected). The list
17
+ * takes the focus: the arrows move the active row past disabled ones, Home and End go to the ends, Enter or Space
18
+ * choose, a click chooses. Name it with aria-label or aria-labelledby. Styles: choice-list.css.
19
+ *
20
+ * Not for 2 to 4 options that need an explanation (ChoiceCard), nor for a list that only shows (Item).
21
+ */
22
+ declare function ChoiceList({ children, value, defaultValue, onValueChange, className, onKeyDown, onFocus, ref, ...rest }: ChoiceListProps): react.JSX.Element;
23
+ interface ChoiceListItemProps {
24
+ /** The value it stands for. */
25
+ value: string;
26
+ /** The name, one line (Figma: `title`). It is the row's accessible name. */
27
+ title: ReactNode;
28
+ /** One muted line under the title (Figma: `showDescription` + `description`). */
29
+ description?: ReactNode;
30
+ /** An Avatar for a person, a Tile for a thing, 32 (Figma: `showMedia` + `media`). Decorative: hidden from screen readers. */
31
+ media?: ReactNode;
32
+ /** A value or a date on the right (Figma: `showTrailing` + `trailing`). */
33
+ trailing?: ReactNode;
34
+ /** Cannot be chosen now: skipped by the arrows, read as unavailable. */
35
+ disabled?: boolean;
36
+ }
37
+ /** One row of the ChoiceList (Figma: .choice-list/item). The whole row is the target. */
38
+ declare function ChoiceListItem({ value, title, description, media, trailing, disabled }: ChoiceListItemProps): react.JSX.Element;
39
+
40
+ export { ChoiceList, ChoiceListItem, type ChoiceListItemProps, type ChoiceListProps };
@@ -0,0 +1,122 @@
1
+ "use client";
2
+ import {
3
+ __objRest,
4
+ __spreadProps,
5
+ __spreadValues
6
+ } from "../chunk-ORMEWXMH.js";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ import {
9
+ createContext,
10
+ useContext,
11
+ useId,
12
+ useRef,
13
+ useState
14
+ } from "react";
15
+ import { warnIfUnlabelled } from "./internal/field.js";
16
+ const ChoiceListContext = createContext(null);
17
+ function ChoiceList(_a) {
18
+ var _b = _a, { children, value, defaultValue, onValueChange, className, onKeyDown, onFocus, ref } = _b, rest = __objRest(_b, ["children", "value", "defaultValue", "onValueChange", "className", "onKeyDown", "onFocus", "ref"]);
19
+ warnIfUnlabelled("ChoiceList", void 0, rest["aria-label"], rest["aria-labelledby"]);
20
+ const [own, setOwn] = useState(defaultValue);
21
+ const selected = value !== void 0 ? value : own;
22
+ const [activeId, setActiveId] = useState(null);
23
+ const list = useRef(null);
24
+ const rows = () => {
25
+ var _a2, _b2;
26
+ return Array.from((_b2 = (_a2 = list.current) == null ? void 0 : _a2.querySelectorAll('[role="option"]')) != null ? _b2 : []).filter(
27
+ (o) => o.getAttribute("aria-disabled") !== "true"
28
+ );
29
+ };
30
+ function choose(next) {
31
+ if (value === void 0) setOwn(next);
32
+ onValueChange == null ? void 0 : onValueChange(next);
33
+ }
34
+ function activate(row) {
35
+ if (!row) return;
36
+ setActiveId(row.id);
37
+ row.scrollIntoView({ block: "nearest" });
38
+ }
39
+ function keyDown(event) {
40
+ var _a2;
41
+ onKeyDown == null ? void 0 : onKeyDown(event);
42
+ if (event.defaultPrevented) return;
43
+ const all = rows();
44
+ const index = all.findIndex((o) => o.id === activeId);
45
+ const moves = {
46
+ ArrowDown: () => all[Math.min(index + 1, all.length - 1)],
47
+ ArrowUp: () => all[Math.max(index - 1, 0)],
48
+ Home: () => all[0],
49
+ End: () => all[all.length - 1]
50
+ };
51
+ if (moves[event.key]) {
52
+ event.preventDefault();
53
+ activate(moves[event.key]());
54
+ } else if ((event.key === "Enter" || event.key === " ") && index >= 0) {
55
+ event.preventDefault();
56
+ choose((_a2 = all[index].dataset.value) != null ? _a2 : "");
57
+ }
58
+ }
59
+ function setRef(node) {
60
+ list.current = node;
61
+ if (typeof ref === "function") ref(node);
62
+ else if (ref) ref.current = node;
63
+ }
64
+ return /* @__PURE__ */ jsx(ChoiceListContext.Provider, { value: { selected, activeId, setActiveId, choose }, children: /* @__PURE__ */ jsx(
65
+ "ul",
66
+ __spreadProps(__spreadValues({
67
+ tabIndex: 0
68
+ }, rest), {
69
+ ref: setRef,
70
+ role: "listbox",
71
+ "aria-activedescendant": activeId != null ? activeId : void 0,
72
+ className: ["rds-choice-list", className].filter(Boolean).join(" "),
73
+ onKeyDown: keyDown,
74
+ onFocus: (event) => {
75
+ var _a2;
76
+ onFocus == null ? void 0 : onFocus(event);
77
+ if (activeId && document.getElementById(activeId)) return;
78
+ const all = rows();
79
+ activate((_a2 = all.find((o) => o.dataset.value === selected)) != null ? _a2 : all[0]);
80
+ },
81
+ children
82
+ })
83
+ ) });
84
+ }
85
+ function ChoiceListItem({ value, title, description, media, trailing, disabled }) {
86
+ const context = useContext(ChoiceListContext);
87
+ const id = useId();
88
+ if (!context) throw new Error("[@rojaostudio/ds] ChoiceListItem goes inside a ChoiceList.");
89
+ const selected = context.selected === value;
90
+ const describedBy = [description != null && `${id}-description`, trailing != null && `${id}-trailing`].filter(Boolean).join(" ");
91
+ return /* @__PURE__ */ jsxs(
92
+ "li",
93
+ {
94
+ id,
95
+ role: "option",
96
+ "data-value": value,
97
+ "aria-selected": selected,
98
+ "aria-disabled": disabled || void 0,
99
+ "aria-labelledby": `${id}-title`,
100
+ "aria-describedby": describedBy || void 0,
101
+ "data-active": context.activeId === id ? "" : void 0,
102
+ className: "rds-choice-list__item",
103
+ onClick: () => {
104
+ if (disabled) return;
105
+ context.setActiveId(id);
106
+ context.choose(value);
107
+ },
108
+ children: [
109
+ media != null && /* @__PURE__ */ jsx("span", { className: "rds-choice-list__media", "aria-hidden": "true", children: media }),
110
+ /* @__PURE__ */ jsxs("span", { className: "rds-choice-list__content", children: [
111
+ /* @__PURE__ */ jsx("span", { id: `${id}-title`, className: "rds-choice-list__title", children: title }),
112
+ description != null && /* @__PURE__ */ jsx("span", { id: `${id}-description`, className: "rds-choice-list__description", children: description })
113
+ ] }),
114
+ trailing != null && /* @__PURE__ */ jsx("span", { id: `${id}-trailing`, className: "rds-choice-list__trailing", children: trailing })
115
+ ]
116
+ }
117
+ );
118
+ }
119
+ export {
120
+ ChoiceList,
121
+ ChoiceListItem
122
+ };
@@ -0,0 +1,39 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode } from 'react';
3
+
4
+ /** The [RDS] type scale for titles: band 36/48, heading 24/30, value 20/30. */
5
+ type HeadingLevel = 'band' | 'heading' | 'value';
6
+ /** default: text/heading (navy on light in Rojão, white on dark and on the brand plate). accent: the logo orange. */
7
+ type HeadingTone = 'default' | 'accent';
8
+ /** The element in the page outline. The visual level never decides it: the order of the page does. */
9
+ type HeadingElement = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
10
+ interface HeadingProps extends HTMLAttributes<HTMLHeadingElement> {
11
+ /** The size, from the type scale (Figma `level`). */
12
+ level?: HeadingLevel;
13
+ /** The colour of the brand pair (Figma `tone`). The Mark takes the other one. */
14
+ tone?: HeadingTone;
15
+ /** The heading element. Default h2. */
16
+ as?: HeadingElement;
17
+ /** The text (Figma `text`), with an optional <Heading.Mark> stretch (Figma `showMark` + `mark`). */
18
+ children: ReactNode;
19
+ }
20
+ interface HeadingMarkProps extends HTMLAttributes<HTMLSpanElement> {
21
+ children: ReactNode;
22
+ }
23
+ /**
24
+ * Heading.Mark — Figma [RDS] .heading/mark. A stretch of the title in the other colour of the pair: accent inside a
25
+ * default Heading, default inside an accent one. A plain span: screen readers read the whole title, unannounced.
26
+ */
27
+ declare function HeadingMark({ className, children, ...rest }: HeadingMarkProps): react.JSX.Element;
28
+ /**
29
+ * Heading — Figma [RDS] Content/Heading. A title in the brand pair: one colour, or both in the same title with a
30
+ * Heading.Mark. The colour is chosen per screen; tone=default is the rule. Orange on white fails contrast even for
31
+ * a large title (2.9:1 in Rojão): tone=accent belongs on dark or on the brand plate. Styles: heading.css.
32
+ */
33
+ declare function HeadingRoot({ level, tone, as: Tag, className, children, ...rest }: HeadingProps): react.JSX.Element;
34
+ /** `<Heading>` with `<Heading.Mark>` for the stretch in the other colour. */
35
+ declare const Heading: typeof HeadingRoot & {
36
+ Mark: typeof HeadingMark;
37
+ };
38
+
39
+ export { Heading, type HeadingElement, type HeadingLevel, HeadingMark, type HeadingMarkProps, type HeadingProps, type HeadingTone };
@@ -0,0 +1,25 @@
1
+ import {
2
+ __objRest,
3
+ __spreadProps,
4
+ __spreadValues
5
+ } from "../chunk-ORMEWXMH.js";
6
+ import { jsx } from "react/jsx-runtime";
7
+ function HeadingMark(_a) {
8
+ var _b = _a, { className, children } = _b, rest = __objRest(_b, ["className", "children"]);
9
+ return /* @__PURE__ */ jsx("span", __spreadProps(__spreadValues({}, rest), { className: ["rds-heading__mark", className].filter(Boolean).join(" "), children }));
10
+ }
11
+ function HeadingRoot(_c) {
12
+ var _d = _c, { level = "heading", tone = "default", as: Tag = "h2", className, children } = _d, rest = __objRest(_d, ["level", "tone", "as", "className", "children"]);
13
+ return /* @__PURE__ */ jsx(
14
+ Tag,
15
+ __spreadProps(__spreadValues({}, rest), {
16
+ className: ["rds-heading", `rds-heading--${level}`, tone === "accent" && "rds-heading--accent", className].filter(Boolean).join(" "),
17
+ children
18
+ })
19
+ );
20
+ }
21
+ const Heading = Object.assign(HeadingRoot, { Mark: HeadingMark });
22
+ export {
23
+ Heading,
24
+ HeadingMark
25
+ };
@@ -47,6 +47,7 @@ export { ChipInput, ChipInputProps } from './chip-input.js';
47
47
  export { CopyField, CopyFieldProps } from './copy-field.js';
48
48
  export { QRDisplay, QRDisplayProps } from './qr-display.js';
49
49
  export { ChoiceCard } from './choice-card.js';
50
+ export { ChoiceList, ChoiceListItem, ChoiceListItemProps, ChoiceListProps } from './choice-list.js';
50
51
  export { OptionTile, OptionTileGrid, OptionTileGridProps, OptionTileItem, OptionTileProps } from './option-tile.js';
51
52
  export { SelectableCard, SelectableCardProps } from './selectable-card.js';
52
53
  export { ChoicePreviewCard, ChoicePreviewCardProps } from './choice-preview-card.js';
@@ -76,6 +77,7 @@ export { Switch, SwitchProps } from './switch.js';
76
77
  export { Tooltip, TooltipProps, TooltipSide } from './tooltip.js';
77
78
  export { ToastFn, ToastOptions, ToastTone, ToastVariant, ToastView, ToastViewProps, Toaster, ToasterProps, toast, toastClassName, useToast } from './toast.js';
78
79
  export { Separator, SeparatorOrientation, SeparatorProps } from './separator.js';
80
+ export { Heading, HeadingElement, HeadingLevel, HeadingMark, HeadingMarkProps, HeadingProps, HeadingTone } from './heading.js';
79
81
  export { Avatar, AvatarGroup, AvatarGroupProps, AvatarGroupSize, AvatarProps, AvatarSize, AvatarType, AvatarVariant, initials } from './avatar.js';
80
82
  export { IconButton, IconButtonProps } from './icon-button.js';
81
83
  export { ButtonGroup, ButtonGroupOrientation, ButtonGroupProps } from './button-group.js';
@@ -54,6 +54,7 @@ import { ChipInput } from "./chip-input.js";
54
54
  import { CopyField } from "./copy-field.js";
55
55
  import { QRDisplay } from "./qr-display.js";
56
56
  import { ChoiceCard, ChoiceCardGroup } from "./choice-card.js";
57
+ import { ChoiceList, ChoiceListItem } from "./choice-list.js";
57
58
  import { OptionTile, OptionTileGrid } from "./option-tile.js";
58
59
  import { SelectableCard } from "./selectable-card.js";
59
60
  import { ChoicePreviewCard } from "./choice-preview-card.js";
@@ -83,6 +84,7 @@ import { Switch } from "./switch.js";
83
84
  import { Tooltip } from "./tooltip.js";
84
85
  import { Toaster, ToastView, toast, useToast, toastClassName } from "./toast.js";
85
86
  import { Separator } from "./separator.js";
87
+ import { Heading, HeadingMark } from "./heading.js";
86
88
  import { Avatar, AvatarGroup, initials } from "./avatar.js";
87
89
  import { IconButton } from "./icon-button.js";
88
90
  import { ButtonGroup } from "./button-group.js";
@@ -161,6 +163,8 @@ export {
161
163
  ChoiceCard,
162
164
  ChoiceCardGroup,
163
165
  ChoiceCarousel,
166
+ ChoiceList,
167
+ ChoiceListItem,
164
168
  ChoicePreviewCard,
165
169
  ColorInput,
166
170
  Combobox,
@@ -205,6 +209,8 @@ export {
205
209
  GoogleButton,
206
210
  GoogleConsent,
207
211
  GoogleSignIn,
212
+ Heading,
213
+ HeadingMark,
208
214
  HoverCard,
209
215
  IconButton,
210
216
  Input,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rojaostudio/ds",
3
- "version": "2.0.0-next.1",
3
+ "version": "2.0.0-next.11",
4
4
  "type": "module",
5
5
  "description": "Rojão Design System — componentes React, estilos e alvo React Native. O motor vive em @rojaostudio/ds-core.",
6
6
  "license": "MIT",
@@ -100,6 +100,7 @@
100
100
  },
101
101
  "files": [
102
102
  "LICENSE",
103
+ "THIRD_PARTY_NOTICES.md",
103
104
  "README.md",
104
105
  "dist",
105
106
  "styles"
@@ -153,7 +154,7 @@
153
154
  "publishConfig": {
154
155
  "registry": "https://registry.npmjs.org",
155
156
  "access": "public",
156
- "provenance": false
157
+ "provenance": true
157
158
  },
158
159
  "devDependencies": {
159
160
  "@types/node": "^20.19.43",
@@ -190,7 +191,7 @@
190
191
  "@radix-ui/react-toggle": "^1.1.18",
191
192
  "@radix-ui/react-toggle-group": "^1.1.19",
192
193
  "@radix-ui/react-tooltip": "^1.2.16",
193
- "@rojaostudio/ds-core": "1.1.0-next.0"
194
+ "@rojaostudio/ds-core": "1.1.0-next.6"
194
195
  },
195
196
  "scripts": {
196
197
  "build": "pnpm run build:tokens && pnpm run build:themes && pnpm run build:rds && pnpm run build:native && tsup",
@@ -1992,6 +1992,8 @@ button.rds-breadcrumb__link:focus-visible {
1992
1992
  * in --_series: chart.tsx picks one of the --_series-N locals below, each one a chart/series-color/N token.
1993
1993
  */
1994
1994
  .rds-chart {
1995
+ /* Anchors the hidden alternative table (and any absolute part) to the chart, not to the page. */
1996
+ position: relative;
1995
1997
  --_series-1: var(--chart-series-color-1);
1996
1998
  --_series-2: var(--chart-series-color-2);
1997
1999
  --_series-3: var(--chart-series-color-3);
@@ -2180,14 +2182,31 @@ button.rds-breadcrumb__link:focus-visible {
2180
2182
  white-space: nowrap;
2181
2183
  }
2182
2184
 
2183
- .rds-chart__labels-track--line .rds-chart__label:first-child {
2185
+ .rds-chart__labels-track--line .rds-chart__label:first-of-type {
2184
2186
  transform: none;
2185
2187
  }
2186
2188
 
2187
- .rds-chart__labels-track--line .rds-chart__label:last-child {
2189
+ .rds-chart__labels-track--line .rds-chart__label:last-of-type {
2188
2190
  transform: translateX(-100%);
2189
2191
  }
2190
2192
 
2193
+ /* dateEvery: a 1x4 tick on every point, the labels under them. */
2194
+ .rds-chart__labels-track--ticks {
2195
+ height: calc(var(--type-caption-line) + var(--space-4));
2196
+ }
2197
+
2198
+ .rds-chart__labels-track--ticks .rds-chart__label {
2199
+ top: var(--space-4);
2200
+ }
2201
+
2202
+ .rds-chart__tick {
2203
+ position: absolute;
2204
+ top: 0;
2205
+ width: 1px;
2206
+ height: var(--space-4);
2207
+ background: var(--chart-tick);
2208
+ }
2209
+
2191
2210
  /* Bars: label, track, value. */
2192
2211
  .rds-chart__bars {
2193
2212
  display: flex;
@@ -2218,6 +2237,50 @@ button.rds-breadcrumb__link:focus-visible {
2218
2237
  background: var(--_series);
2219
2238
  }
2220
2239
 
2240
+ /* A bar with a destination (hrefs or onSelect): the row is the link or button. */
2241
+ .rds-chart__bar-row--link {
2242
+ box-sizing: border-box;
2243
+ width: 100%;
2244
+ margin: 0;
2245
+ padding: 0;
2246
+ border: 0;
2247
+ border-radius: var(--radius-focus);
2248
+ background: none;
2249
+ color: inherit;
2250
+ font: inherit;
2251
+ text-align: start;
2252
+ text-decoration: none;
2253
+ cursor: pointer;
2254
+ }
2255
+
2256
+ .rds-chart__bar-row--link:hover .rds-chart__track {
2257
+ background: var(--chart-bar-hover);
2258
+ }
2259
+
2260
+ .rds-chart__bar-row--link:hover .rds-chart__bar-label {
2261
+ text-decoration: underline;
2262
+ }
2263
+
2264
+ .rds-chart__bar-row--link:focus-visible,
2265
+ .rds-chart__table-link:focus-visible {
2266
+ outline: var(--focus-ring-width) solid var(--chart-focus-ring);
2267
+ outline-offset: var(--focus-ring-width);
2268
+ }
2269
+
2270
+ .rds-chart--linked.rds-chart--column .rds-chart__plot {
2271
+ cursor: pointer;
2272
+ }
2273
+
2274
+ .rds-chart__table-link {
2275
+ padding: 0;
2276
+ border: 0;
2277
+ background: none;
2278
+ color: inherit;
2279
+ font: inherit;
2280
+ text-decoration: underline;
2281
+ cursor: pointer;
2282
+ }
2283
+
2221
2284
  .rds-chart__bar-value {
2222
2285
  color: var(--chart-axis);
2223
2286
  font-variant-numeric: tabular-nums;
@@ -2251,7 +2314,8 @@ button.rds-breadcrumb__link:focus-visible {
2251
2314
  background: CanvasText;
2252
2315
  }
2253
2316
 
2254
- .rds-chart__plot:focus-visible {
2317
+ .rds-chart__plot:focus-visible,
2318
+ .rds-chart__bar-row--link:focus-visible {
2255
2319
  outline-color: Highlight;
2256
2320
  }
2257
2321
  }
@@ -2993,6 +3057,132 @@ input:focus-visible + .rds-checkbox__box {
2993
3057
  }
2994
3058
  }
2995
3059
 
3060
+ /* components/choice-list.css */
3061
+ /* @tokens choice-list */
3062
+ /*
3063
+ * ChoiceList (Content collection, choice-list/*): the dense list to choose one from. Each row pads 12 16 with 12
3064
+ * between media, text and trailing; a line of border-width only at the bottom (padding-bottom gives it back, so the
3065
+ * row is 60 with a description, 44 without). Title small medium, description caption, trailing small.
3066
+ * Hover: choice-list/item/background/hover. Chosen: the tint and a bar of border-width-strong on the left, no hover.
3067
+ * Focus: the list holds it (aria-activedescendant); the active row draws the ring inside its box, as the rows touch.
3068
+ * Disabled: text in the disabled tokens, media at half; it stays chosen if it was.
3069
+ */
3070
+ .rds-choice-list {
3071
+ box-sizing: border-box;
3072
+ margin: 0;
3073
+ padding: 0;
3074
+ overflow-y: auto;
3075
+ font-family: var(--type-font-stack);
3076
+ list-style: none;
3077
+ outline: none;
3078
+ }
3079
+
3080
+ .rds-choice-list__item {
3081
+ --_background: transparent;
3082
+ --_title: var(--choice-list-item-title-default);
3083
+ --_description: var(--choice-list-item-description-default);
3084
+ --_trailing: var(--choice-list-item-trailing-default);
3085
+
3086
+ box-sizing: border-box;
3087
+ display: flex;
3088
+ align-items: center;
3089
+ gap: var(--space-12);
3090
+ min-height: calc(var(--space-12) * 2 + var(--type-button-line));
3091
+ padding: var(--space-12) var(--space-16) calc(var(--space-12) - var(--border-width));
3092
+ border-bottom: var(--border-width) solid var(--choice-list-item-border);
3093
+ background: var(--_background);
3094
+ cursor: pointer;
3095
+ user-select: none;
3096
+ }
3097
+
3098
+ @media (hover: hover) {
3099
+ .rds-choice-list__item:not([aria-selected='true'], [aria-disabled='true']):hover {
3100
+ --_background: var(--choice-list-item-background-hover);
3101
+ }
3102
+ }
3103
+
3104
+ .rds-choice-list__item[aria-selected='true'] {
3105
+ --_background: var(--choice-list-item-background-selected);
3106
+ /* On the tint the muted line does not hold contrast in dark: it takes text/on/tint. */
3107
+ --_description: var(--choice-list-item-description-selected);
3108
+
3109
+ box-shadow: inset var(--border-width-strong) 0 0 var(--choice-list-item-indicator);
3110
+ }
3111
+
3112
+ .rds-choice-list:focus-visible .rds-choice-list__item[data-active] {
3113
+ outline: var(--focus-ring-width) solid var(--choice-list-focus-ring);
3114
+ outline-offset: calc(-1 * var(--focus-ring-width));
3115
+ }
3116
+
3117
+ .rds-choice-list__item[aria-disabled='true'] {
3118
+ --_title: var(--choice-list-item-title-disabled);
3119
+ --_description: var(--choice-list-item-description-disabled);
3120
+ --_trailing: var(--choice-list-item-trailing-disabled);
3121
+
3122
+ cursor: not-allowed;
3123
+ }
3124
+
3125
+ .rds-choice-list__media {
3126
+ display: inline-flex;
3127
+ flex: none;
3128
+ }
3129
+
3130
+ .rds-choice-list__item[aria-disabled='true'] .rds-choice-list__media {
3131
+ opacity: 0.5;
3132
+ }
3133
+
3134
+ .rds-choice-list__content {
3135
+ display: flex;
3136
+ flex: 1;
3137
+ flex-direction: column;
3138
+ min-width: 0;
3139
+ }
3140
+
3141
+ .rds-choice-list__title,
3142
+ .rds-choice-list__description {
3143
+ overflow: hidden;
3144
+ text-overflow: ellipsis;
3145
+ white-space: nowrap;
3146
+ }
3147
+
3148
+ .rds-choice-list__title {
3149
+ color: var(--_title);
3150
+ font-size: var(--type-small-size);
3151
+ line-height: var(--type-small-line);
3152
+ font-weight: 500;
3153
+ }
3154
+
3155
+ .rds-choice-list__description {
3156
+ color: var(--_description);
3157
+ font-size: var(--type-caption-size);
3158
+ line-height: var(--type-caption-line);
3159
+ }
3160
+
3161
+ .rds-choice-list__trailing {
3162
+ flex: none;
3163
+ color: var(--_trailing);
3164
+ font-size: var(--type-small-size);
3165
+ line-height: var(--type-small-line);
3166
+ font-variant-numeric: tabular-nums;
3167
+ text-align: right;
3168
+ }
3169
+
3170
+ @media (forced-colors: active) {
3171
+ .rds-choice-list__item[aria-selected='true'] {
3172
+ forced-color-adjust: none;
3173
+ background: Highlight;
3174
+ color: HighlightText;
3175
+ }
3176
+
3177
+ .rds-choice-list__item[aria-selected='true'] :is(.rds-choice-list__title, .rds-choice-list__description, .rds-choice-list__trailing) {
3178
+ color: HighlightText;
3179
+ }
3180
+
3181
+ .rds-choice-list:focus-visible .rds-choice-list__item[data-active] {
3182
+ outline-color: CanvasText;
3183
+ }
3184
+ }
3185
+
2996
3186
  /* components/color-input.css */
2997
3187
  /* @tokens color-input */
2998
3188
  /*
@@ -4595,6 +4785,48 @@ input:focus-visible + .rds-checkbox__box {
4595
4785
  }
4596
4786
  }
4597
4787
 
4788
+ /* components/heading.css */
4789
+ /* @tokens heading */
4790
+ /*
4791
+ * Heading (Content collection, heading/*): a title in the brand pair. heading/default is text/heading, heading/accent
4792
+ * the logo orange; .heading/mark paints a stretch in the other one. level follows the type scale (band, heading,
4793
+ * value), all bold.
4794
+ */
4795
+ .rds-heading {
4796
+ margin: 0;
4797
+ font-family: var(--type-font-stack);
4798
+ font-weight: var(--type-heading-weight);
4799
+ color: var(--heading-default);
4800
+ text-wrap: balance;
4801
+ }
4802
+
4803
+ .rds-heading--band {
4804
+ font-size: var(--type-band-size);
4805
+ line-height: var(--type-band-line);
4806
+ }
4807
+
4808
+ .rds-heading--heading {
4809
+ font-size: var(--type-heading-size);
4810
+ line-height: var(--type-heading-line);
4811
+ }
4812
+
4813
+ .rds-heading--value {
4814
+ font-size: var(--type-value-size);
4815
+ line-height: var(--type-value-line);
4816
+ }
4817
+
4818
+ .rds-heading--accent {
4819
+ color: var(--heading-accent);
4820
+ }
4821
+
4822
+ .rds-heading__mark {
4823
+ color: var(--heading-accent);
4824
+ }
4825
+
4826
+ .rds-heading--accent .rds-heading__mark {
4827
+ color: var(--heading-default);
4828
+ }
4829
+
4598
4830
  /* components/hover-card.css */
4599
4831
  /* @tokens hovercard */
4600
4832
  /* HoverCard (Feedback collection, hovercard/*): 320 wide, 16 inside, 12 between avatar and text, elevation/overlay. */
@@ -5298,6 +5530,23 @@ input:focus-visible + .rds-checkbox__box {
5298
5530
  font: inherit;
5299
5531
  }
5300
5532
 
5533
+ /*
5534
+ * Browser autofill paints the control's own background (blue in light, yellow in dark) and the control is smaller
5535
+ * than the box that draws the border: it showed as a patch in the middle of the field. Cover it with the box
5536
+ * colour and keep the value colour. The background-color transition is the fallback for browsers that ignore the
5537
+ * inset shadow on autofill.
5538
+ */
5539
+ .rds-field__control:-webkit-autofill,
5540
+ .rds-field__control:-webkit-autofill:hover,
5541
+ .rds-field__control:-webkit-autofill:focus,
5542
+ .rds-field__control:autofill {
5543
+ -webkit-box-shadow: 0 0 0 1000px var(--_background) inset;
5544
+ box-shadow: 0 0 0 1000px var(--_background) inset;
5545
+ -webkit-text-fill-color: var(--_value);
5546
+ caret-color: var(--_value);
5547
+ transition: background-color 600000s 0s;
5548
+ }
5549
+
5301
5550
  .rds-field__control::placeholder {
5302
5551
  color: var(--_placeholder);
5303
5552
  opacity: 1;
@@ -8772,9 +9021,12 @@ input:focus-visible + .rds-radio__circle {
8772
9021
  cursor: pointer;
8773
9022
  }
8774
9023
 
9024
+ /* Figma .sidebar/section: label in caps with 6% tracking; the count keeps its own case and no tracking. */
8775
9025
  .rds-sidebar__section-label {
8776
9026
  flex: 1;
8777
9027
  min-width: 0;
9028
+ text-transform: uppercase;
9029
+ letter-spacing: 0.06em;
8778
9030
  }
8779
9031
 
8780
9032
  .rds-sidebar__section-count {
@@ -292,6 +292,9 @@
292
292
  --chart-tooltip-border: var(--border-default);
293
293
  --chart-label: var(--text-body);
294
294
  --chart-tooltip-title: var(--text-heading);
295
+ --chart-bar-hover: var(--surface-muted);
296
+ --chart-focus-ring: var(--focus-ring);
297
+ --chart-tick: var(--border-strong);
295
298
  --carousel-slide-background: var(--surface-muted);
296
299
  --carousel-dot: var(--border-strong);
297
300
  --carousel-dot-active: var(--colors-primary-default);
@@ -371,6 +374,20 @@
371
374
  --choice-card-preview-background: var(--surface-muted);
372
375
  --choice-card-preview-border: var(--border-default);
373
376
  --choice-card-preview-icon: var(--text-subtle);
377
+ --choice-list-item-background-hover: var(--surface-muted);
378
+ --choice-list-item-background-selected: var(--surface-tint-default);
379
+ --choice-list-item-indicator: var(--colors-primary-default);
380
+ --choice-list-item-border: var(--border-default);
381
+ --choice-list-item-title-default: var(--text-body);
382
+ --choice-list-item-title-disabled: var(--text-disabled);
383
+ --choice-list-item-description-default: var(--text-muted);
384
+ --choice-list-item-description-disabled: var(--text-disabled);
385
+ --choice-list-item-trailing-default: var(--text-body);
386
+ --choice-list-item-trailing-disabled: var(--text-disabled);
387
+ --choice-list-focus-ring: var(--focus-ring);
388
+ --choice-list-item-description-selected: var(--text-on-tint);
389
+ --heading-default: var(--text-heading);
390
+ --heading-accent: var(--colors-accent-default);
374
391
 
375
392
  /* external */
376
393
  --google-button-light-background: #ffffff;
@@ -11,7 +11,7 @@
11
11
  --colors-accent-default: #ff6a00;
12
12
  --colors-accent-hover: #ff9640;
13
13
  --colors-accent-invert: #ffffff;
14
- --text-heading: #ff6a00;
14
+ --text-heading: #1b2a4a;
15
15
  --text-body: #27272a;
16
16
  --text-link: #006bb5;
17
17
  --surface-page: #f4f4f5;