@usefidel/design-system 0.17.0 → 0.18.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.
@@ -10,6 +10,6 @@
10
10
  "webapp.css"
11
11
  ],
12
12
  "storybookUrl": null,
13
- "packageVersion": "0.17.0",
14
- "resolvedSha": "eed47e39603e5683418c97c6d6dfbcaf68c284c0"
13
+ "packageVersion": "0.18.0",
14
+ "resolvedSha": "957525c79b7a52876dfbcc01b77e55168d4e3316"
15
15
  }
package/dist/index.cjs CHANGED
@@ -20,12 +20,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ Accordion: () => Accordion,
23
24
  AppNav: () => AppNav,
24
25
  Avatar: () => Avatar,
25
26
  Badge: () => Badge,
26
27
  Button: () => Button,
27
28
  ButtonLink: () => ButtonLink,
28
29
  Callout: () => Callout,
30
+ Card: () => Card,
31
+ Checkbox: () => Checkbox,
29
32
  DiffPropertyList: () => DiffPropertyList,
30
33
  DiffSection: () => DiffSection,
31
34
  EmptyState: () => EmptyState,
@@ -33,6 +36,7 @@ __export(index_exports, {
33
36
  Modal: () => Modal,
34
37
  MultiSelect: () => MultiSelect,
35
38
  Pagination: () => Pagination,
39
+ Radio: () => Radio,
36
40
  SearchInput: () => SearchInput,
37
41
  SectionHeader: () => SectionHeader,
38
42
  SegmentedControl: () => SegmentedControl,
@@ -42,6 +46,8 @@ __export(index_exports, {
42
46
  SkeletonGroup: () => SkeletonGroup,
43
47
  Split: () => Split,
44
48
  StackedCardsIllustration: () => StackedCardsIllustration,
49
+ Stat: () => Stat,
50
+ StepList: () => StepList,
45
51
  TabNav: () => TabNav,
46
52
  Tag: () => Tag,
47
53
  Toggle: () => Toggle,
@@ -983,9 +989,10 @@ function Badge({ tone = "neutral", icon, className, children, ...rest }) {
983
989
 
984
990
  // src/components/Tag/Tag.tsx
985
991
  var import_jsx_runtime16 = require("react/jsx-runtime");
986
- function Tag({ dot, className, children, ...rest }) {
992
+ function Tag({ dot, icon, className, children, ...rest }) {
987
993
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { className: ["fds-tag", className].filter(Boolean).join(" "), ...rest, children: [
988
994
  dot && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: `fds-dot fds-dot--${dot}`, "aria-hidden": "true" }),
995
+ icon != null && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "fds-tag__icon", "aria-hidden": "true", children: icon }),
989
996
  children
990
997
  ] });
991
998
  }
@@ -1304,14 +1311,190 @@ function Skeleton({ shape = "rect", width, height, lines = 1, className, style,
1304
1311
  function SkeletonGroup({ label = "Loading", className, children, ...rest }) {
1305
1312
  return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { role: "status", "aria-busy": "true", "aria-label": label, className, ...rest, children });
1306
1313
  }
1314
+
1315
+ // src/components/Card/Card.tsx
1316
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1317
+ function Card({ tone = "default", padding = "md", className, children, ...rest }) {
1318
+ const classes = ["fds-card", `fds-card--${tone}`, `fds-card--pad-${padding}`, className].filter(Boolean).join(" ");
1319
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: classes, ...rest, children });
1320
+ }
1321
+
1322
+ // src/components/Stat/Stat.tsx
1323
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1324
+ function Stat({ label, value, children, className, ...rest }) {
1325
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: ["fds-stat", className].filter(Boolean).join(" "), ...rest, children: [
1326
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "fds-stat__label", children: label }),
1327
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "fds-stat__value", children: value }),
1328
+ children != null && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "fds-stat__detail", children })
1329
+ ] });
1330
+ }
1331
+
1332
+ // src/components/Checkbox/Checkbox.tsx
1333
+ var import_react12 = require("react");
1334
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1335
+ var Checkbox = (0, import_react12.forwardRef)(function Checkbox2({ label, description, indeterminate = false, className, id, disabled, ...rest }, ref) {
1336
+ const generatedId = (0, import_react12.useId)();
1337
+ const inputId = id ?? generatedId;
1338
+ const inputRef = (0, import_react12.useRef)(null);
1339
+ (0, import_react12.useImperativeHandle)(ref, () => inputRef.current);
1340
+ (0, import_react12.useEffect)(() => {
1341
+ if (inputRef.current) inputRef.current.indeterminate = indeterminate;
1342
+ }, [indeterminate]);
1343
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
1344
+ "div",
1345
+ {
1346
+ className: ["fds-checkbox", className].filter(Boolean).join(" "),
1347
+ "data-disabled": disabled ? "" : void 0,
1348
+ children: [
1349
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1350
+ "input",
1351
+ {
1352
+ ...rest,
1353
+ id: inputId,
1354
+ ref: inputRef,
1355
+ type: "checkbox",
1356
+ disabled,
1357
+ className: "fds-checkbox__input"
1358
+ }
1359
+ ),
1360
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("label", { htmlFor: inputId, className: "fds-checkbox__control", children: [
1361
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "fds-checkbox__box", "aria-hidden": "true", children: [
1362
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("svg", { className: "fds-checkbox__tick", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("path", { d: "M10 3L4.5 8.5 2 6", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) }),
1363
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("svg", { className: "fds-checkbox__dash", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("path", { d: "M3 6h6", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" }) })
1364
+ ] }),
1365
+ (label != null || description != null) && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "fds-checkbox__text", children: [
1366
+ label != null && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "fds-checkbox__label", children: label }),
1367
+ description != null && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "fds-checkbox__description", children: description })
1368
+ ] })
1369
+ ] })
1370
+ ]
1371
+ }
1372
+ );
1373
+ });
1374
+
1375
+ // src/components/Radio/Radio.tsx
1376
+ var import_react13 = require("react");
1377
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1378
+ var Radio = (0, import_react13.forwardRef)(function Radio2({ label, description, className, id, disabled, ...rest }, ref) {
1379
+ const generatedId = (0, import_react13.useId)();
1380
+ const inputId = id ?? generatedId;
1381
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: ["fds-radio", className].filter(Boolean).join(" "), "data-disabled": disabled ? "" : void 0, children: [
1382
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("input", { ...rest, id: inputId, ref, type: "radio", disabled, className: "fds-radio__input" }),
1383
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("label", { htmlFor: inputId, className: "fds-radio__control", children: [
1384
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "fds-radio__circle", "aria-hidden": "true" }),
1385
+ (label != null || description != null) && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("span", { className: "fds-radio__text", children: [
1386
+ label != null && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "fds-radio__label", children: label }),
1387
+ description != null && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "fds-radio__description", children: description })
1388
+ ] })
1389
+ ] })
1390
+ ] });
1391
+ });
1392
+
1393
+ // src/components/Accordion/Accordion.tsx
1394
+ var import_react14 = require("react");
1395
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1396
+ function Accordion({
1397
+ summary,
1398
+ actions,
1399
+ open: openProp,
1400
+ defaultOpen = false,
1401
+ onOpenChange,
1402
+ children,
1403
+ className,
1404
+ ...rest
1405
+ }) {
1406
+ const [openState, setOpenState] = (0, import_react14.useState)(defaultOpen);
1407
+ const open = openProp ?? openState;
1408
+ const bodyId = (0, import_react14.useId)();
1409
+ const toggle = () => {
1410
+ const next = !open;
1411
+ if (openProp === void 0) setOpenState(next);
1412
+ onOpenChange?.(next);
1413
+ };
1414
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1415
+ "div",
1416
+ {
1417
+ className: ["fds-accordion", className].filter(Boolean).join(" "),
1418
+ "data-open": open ? "" : void 0,
1419
+ ...rest,
1420
+ children: [
1421
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "fds-accordion__header", children: [
1422
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
1423
+ "button",
1424
+ {
1425
+ type: "button",
1426
+ className: "fds-accordion__trigger",
1427
+ "aria-expanded": open,
1428
+ "aria-controls": bodyId,
1429
+ onClick: toggle,
1430
+ children: summary
1431
+ }
1432
+ ),
1433
+ actions != null && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "fds-accordion__actions", children: actions }),
1434
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "fds-accordion__chevron", "aria-hidden": "true", onClick: toggle, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("svg", { viewBox: "0 0 24 24", width: "16", height: "16", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("path", { d: "M6 9l6 6 6-6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) })
1435
+ ] }),
1436
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { id: bodyId, className: "fds-accordion__body", hidden: !open, children })
1437
+ ]
1438
+ }
1439
+ );
1440
+ }
1441
+
1442
+ // src/components/StepList/StepList.tsx
1443
+ var import_jsx_runtime28 = require("react/jsx-runtime");
1444
+ var STATUS_TEXT = {
1445
+ done: "Done",
1446
+ active: "In progress",
1447
+ pending: "Not started",
1448
+ failed: "Failed"
1449
+ };
1450
+ function StepIcon({ status }) {
1451
+ const common = { viewBox: "0 0 24 24", width: 18, height: 18, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" };
1452
+ if (status === "done") {
1453
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("svg", { ...common, children: [
1454
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("circle", { cx: "12", cy: "12", r: "9" }),
1455
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M8 12.5l2.5 2.5 5-5.5" })
1456
+ ] });
1457
+ }
1458
+ if (status === "active") {
1459
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("svg", { ...common, className: "fds-steps__spinner", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M12 3v3M12 18v3M3 12h3M18 12h3M5.6 5.6l2.1 2.1M16.3 16.3l2.1 2.1M5.6 18.4l2.1-2.1M16.3 7.7l2.1-2.1" }) });
1460
+ }
1461
+ if (status === "failed") {
1462
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("svg", { ...common, children: [
1463
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("circle", { cx: "12", cy: "12", r: "9" }),
1464
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("path", { d: "M12 7.5v5.5M12 16.5v.01" })
1465
+ ] });
1466
+ }
1467
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("svg", { ...common, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("circle", { cx: "12", cy: "12", r: "9" }) });
1468
+ }
1469
+ function StepList({ steps, className, ...rest }) {
1470
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("ol", { className: ["fds-steps", className].filter(Boolean).join(" "), ...rest, children: steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1471
+ "li",
1472
+ {
1473
+ className: `fds-steps__step fds-steps__step--${step.status}`,
1474
+ "aria-current": step.status === "active" ? "step" : void 0,
1475
+ children: [
1476
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "fds-steps__icon", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(StepIcon, { status: step.status }) }),
1477
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "fds-steps__label", children: step.label }),
1478
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "fds-steps__sr", children: [
1479
+ ", ",
1480
+ STATUS_TEXT[step.status]
1481
+ ] })
1482
+ ]
1483
+ },
1484
+ step.id
1485
+ )) });
1486
+ }
1307
1487
  // Annotate the CommonJS export names for ESM import in node:
1308
1488
  0 && (module.exports = {
1489
+ Accordion,
1309
1490
  AppNav,
1310
1491
  Avatar,
1311
1492
  Badge,
1312
1493
  Button,
1313
1494
  ButtonLink,
1314
1495
  Callout,
1496
+ Card,
1497
+ Checkbox,
1315
1498
  DiffPropertyList,
1316
1499
  DiffSection,
1317
1500
  EmptyState,
@@ -1319,6 +1502,7 @@ function SkeletonGroup({ label = "Loading", className, children, ...rest }) {
1319
1502
  Modal,
1320
1503
  MultiSelect,
1321
1504
  Pagination,
1505
+ Radio,
1322
1506
  SearchInput,
1323
1507
  SectionHeader,
1324
1508
  SegmentedControl,
@@ -1328,6 +1512,8 @@ function SkeletonGroup({ label = "Loading", className, children, ...rest }) {
1328
1512
  SkeletonGroup,
1329
1513
  Split,
1330
1514
  StackedCardsIllustration,
1515
+ Stat,
1516
+ StepList,
1331
1517
  TabNav,
1332
1518
  Tag,
1333
1519
  Toggle,
package/dist/index.css CHANGED
@@ -1519,6 +1519,15 @@
1519
1519
  .fds-dot--danger {
1520
1520
  background: var(--color-destructive, #eb2d2d);
1521
1521
  }
1522
+ .fds-tag__icon {
1523
+ display: inline-flex;
1524
+ flex-shrink: 0;
1525
+ color: var(--color-text-secondary, #525252);
1526
+ }
1527
+ .fds-tag__icon > svg {
1528
+ width: 14px;
1529
+ height: 14px;
1530
+ }
1522
1531
 
1523
1532
  /* src/components/MultiSelect/multi-select.css */
1524
1533
  .fds-multiselect {
@@ -2049,3 +2058,374 @@
2049
2058
  animation: none;
2050
2059
  }
2051
2060
  }
2061
+
2062
+ /* src/components/Card/card.css */
2063
+ .fds-card {
2064
+ box-sizing: border-box;
2065
+ border-radius: var(--radius-2xl, 16px);
2066
+ color: var(--color-text, #171717);
2067
+ }
2068
+ .fds-card--subtle {
2069
+ background: var(--color-surface-subtle, #fafafa);
2070
+ border: 1px solid var(--color-border-light, #f5f5f5);
2071
+ }
2072
+ .fds-card--default {
2073
+ background: var(--color-bg, #ffffff);
2074
+ border: 1px solid var(--color-border, #e5e5e5);
2075
+ border-radius: var(--radius-xl, 12px);
2076
+ }
2077
+ .fds-card--plain {
2078
+ background: var(--color-bg, #ffffff);
2079
+ border: 1px solid transparent;
2080
+ border-radius: var(--radius-xl, 12px);
2081
+ }
2082
+ .fds-card--pad-none {
2083
+ padding: 0;
2084
+ }
2085
+ .fds-card--pad-md {
2086
+ padding: var(--spacing-6, 24px);
2087
+ }
2088
+ .fds-card--pad-lg {
2089
+ padding: var(--spacing-10, 40px) var(--spacing-12, 48px);
2090
+ }
2091
+ @media (max-width: 600px) {
2092
+ .fds-card--pad-lg {
2093
+ padding: var(--spacing-6, 24px);
2094
+ }
2095
+ }
2096
+
2097
+ /* src/components/Stat/stat.css */
2098
+ .fds-stat {
2099
+ display: flex;
2100
+ flex-direction: column;
2101
+ gap: var(--spacing-1, 4px);
2102
+ font-family: var(--font-body, "Rethink Sans", system-ui, sans-serif);
2103
+ }
2104
+ .fds-stat__label {
2105
+ margin: 0;
2106
+ font-size: var(--font-size-text-sm, 14px);
2107
+ line-height: 20px;
2108
+ font-weight: var(--font-weight-medium, 500);
2109
+ color: var(--color-text-secondary, #525252);
2110
+ }
2111
+ .fds-stat__value {
2112
+ margin: 0;
2113
+ font-size: var(--font-size-display-md, 36px);
2114
+ line-height: 44px;
2115
+ font-weight: var(--font-weight-semibold, 600);
2116
+ letter-spacing: -0.02em;
2117
+ color: var(--color-text, #171717);
2118
+ font-variant-numeric: tabular-nums;
2119
+ }
2120
+ .fds-stat__detail {
2121
+ display: flex;
2122
+ flex-wrap: wrap;
2123
+ gap: var(--spacing-2, 8px);
2124
+ margin-top: var(--spacing-3, 12px);
2125
+ }
2126
+
2127
+ /* src/components/Checkbox/checkbox.css */
2128
+ .fds-checkbox {
2129
+ display: inline-flex;
2130
+ position: relative;
2131
+ }
2132
+ .fds-checkbox__input {
2133
+ position: absolute;
2134
+ inset: 0;
2135
+ width: 100%;
2136
+ height: 100%;
2137
+ margin: 0;
2138
+ opacity: 0;
2139
+ cursor: pointer;
2140
+ z-index: 1;
2141
+ }
2142
+ .fds-checkbox__input:disabled {
2143
+ cursor: not-allowed;
2144
+ }
2145
+ .fds-checkbox__control {
2146
+ display: inline-flex;
2147
+ align-items: flex-start;
2148
+ gap: var(--spacing-2, 8px);
2149
+ cursor: pointer;
2150
+ }
2151
+ .fds-checkbox__box {
2152
+ box-sizing: border-box;
2153
+ display: inline-grid;
2154
+ place-items: center;
2155
+ flex-shrink: 0;
2156
+ width: 16px;
2157
+ height: 16px;
2158
+ margin-top: 3px;
2159
+ border: 1px solid var(--color-border-strong, #d4d4d4);
2160
+ border-radius: var(--radius-sm, 4px);
2161
+ background: var(--color-bg, #ffffff);
2162
+ color: var(--color-bg, #ffffff);
2163
+ transition: background-color var(--duration-quick, 150ms) ease, border-color var(--duration-quick, 150ms) ease;
2164
+ }
2165
+ .fds-checkbox__control:not(:has(.fds-checkbox__text)) .fds-checkbox__box {
2166
+ margin-top: 0;
2167
+ }
2168
+ .fds-checkbox__tick,
2169
+ .fds-checkbox__dash {
2170
+ grid-area: 1 / 1;
2171
+ width: 10px;
2172
+ height: 10px;
2173
+ visibility: hidden;
2174
+ }
2175
+ .fds-checkbox__input:checked ~ .fds-checkbox__control .fds-checkbox__box,
2176
+ .fds-checkbox__input:indeterminate ~ .fds-checkbox__control .fds-checkbox__box {
2177
+ background: var(--color-text, #171717);
2178
+ border-color: var(--color-text, #171717);
2179
+ }
2180
+ .fds-checkbox__input:checked:not(:indeterminate) ~ .fds-checkbox__control .fds-checkbox__tick {
2181
+ visibility: visible;
2182
+ }
2183
+ .fds-checkbox__input:indeterminate ~ .fds-checkbox__control .fds-checkbox__dash {
2184
+ visibility: visible;
2185
+ }
2186
+ .fds-checkbox__input:hover:not(:disabled):not(:checked) ~ .fds-checkbox__control .fds-checkbox__box {
2187
+ border-color: var(--color-text-secondary, #525252);
2188
+ }
2189
+ .fds-checkbox__input:focus-visible ~ .fds-checkbox__control .fds-checkbox__box {
2190
+ outline: 2px solid var(--color-focus, #ea580c);
2191
+ outline-offset: 2px;
2192
+ }
2193
+ .fds-checkbox[data-disabled] .fds-checkbox__box {
2194
+ opacity: 0.5;
2195
+ }
2196
+ .fds-checkbox[data-disabled] .fds-checkbox__label,
2197
+ .fds-checkbox[data-disabled] .fds-checkbox__description {
2198
+ color: var(--color-text-muted, #a3a3a3);
2199
+ }
2200
+ .fds-checkbox__text {
2201
+ display: inline-flex;
2202
+ flex-direction: column;
2203
+ gap: 2px;
2204
+ }
2205
+ .fds-checkbox__label {
2206
+ font-family: var(--font-body, "Rethink Sans", system-ui, sans-serif);
2207
+ font-size: var(--font-size-text-sm, 14px);
2208
+ line-height: 22px;
2209
+ font-weight: var(--font-weight-medium, 500);
2210
+ color: var(--color-text, #171717);
2211
+ }
2212
+ .fds-checkbox__description {
2213
+ font-family: var(--font-body, "Rethink Sans", system-ui, sans-serif);
2214
+ font-size: var(--font-size-text-sm, 14px);
2215
+ line-height: 22px;
2216
+ color: var(--color-text-secondary, #525252);
2217
+ }
2218
+ @media (prefers-reduced-motion: reduce) {
2219
+ .fds-checkbox__box {
2220
+ transition: none;
2221
+ }
2222
+ }
2223
+
2224
+ /* src/components/Radio/radio.css */
2225
+ .fds-radio {
2226
+ display: inline-flex;
2227
+ position: relative;
2228
+ }
2229
+ .fds-radio__input {
2230
+ position: absolute;
2231
+ inset: 0;
2232
+ width: 100%;
2233
+ height: 100%;
2234
+ margin: 0;
2235
+ opacity: 0;
2236
+ cursor: pointer;
2237
+ z-index: 1;
2238
+ }
2239
+ .fds-radio__input:disabled {
2240
+ cursor: not-allowed;
2241
+ }
2242
+ .fds-radio__control {
2243
+ display: inline-flex;
2244
+ align-items: flex-start;
2245
+ gap: var(--spacing-2, 8px);
2246
+ cursor: pointer;
2247
+ }
2248
+ .fds-radio__circle {
2249
+ box-sizing: border-box;
2250
+ flex-shrink: 0;
2251
+ width: 16px;
2252
+ height: 16px;
2253
+ margin-top: 3px;
2254
+ border: 1px solid var(--color-border-strong, #d4d4d4);
2255
+ border-radius: var(--radius-full, 9999px);
2256
+ background: var(--color-bg, #ffffff);
2257
+ transition: border-color var(--duration-quick, 150ms) ease, border-width var(--duration-quick, 150ms) ease;
2258
+ }
2259
+ .fds-radio__control:not(:has(.fds-radio__text)) .fds-radio__circle {
2260
+ margin-top: 0;
2261
+ }
2262
+ .fds-radio__input:checked ~ .fds-radio__control .fds-radio__circle {
2263
+ border: 5px solid var(--color-text, #171717);
2264
+ }
2265
+ .fds-radio__input:hover:not(:disabled):not(:checked) ~ .fds-radio__control .fds-radio__circle {
2266
+ border-color: var(--color-text-secondary, #525252);
2267
+ }
2268
+ .fds-radio__input:focus-visible ~ .fds-radio__control .fds-radio__circle {
2269
+ outline: 2px solid var(--color-focus, #ea580c);
2270
+ outline-offset: 2px;
2271
+ }
2272
+ .fds-radio[data-disabled] .fds-radio__circle {
2273
+ opacity: 0.5;
2274
+ }
2275
+ .fds-radio[data-disabled] .fds-radio__label,
2276
+ .fds-radio[data-disabled] .fds-radio__description {
2277
+ color: var(--color-text-muted, #a3a3a3);
2278
+ }
2279
+ .fds-radio__text {
2280
+ display: inline-flex;
2281
+ flex-direction: column;
2282
+ gap: 2px;
2283
+ }
2284
+ .fds-radio__label {
2285
+ font-family: var(--font-body, "Rethink Sans", system-ui, sans-serif);
2286
+ font-size: var(--font-size-text-sm, 14px);
2287
+ line-height: 22px;
2288
+ font-weight: var(--font-weight-medium, 500);
2289
+ color: var(--color-text, #171717);
2290
+ }
2291
+ .fds-radio__description {
2292
+ font-family: var(--font-body, "Rethink Sans", system-ui, sans-serif);
2293
+ font-size: var(--font-size-text-sm, 14px);
2294
+ line-height: 22px;
2295
+ color: var(--color-text-secondary, #525252);
2296
+ }
2297
+ @media (prefers-reduced-motion: reduce) {
2298
+ .fds-radio__circle {
2299
+ transition: none;
2300
+ }
2301
+ }
2302
+
2303
+ /* src/components/Accordion/accordion.css */
2304
+ .fds-accordion {
2305
+ box-sizing: border-box;
2306
+ background: var(--color-bg, #ffffff);
2307
+ border: 1px solid var(--color-border, #e5e5e5);
2308
+ border-radius: var(--radius-xl, 12px);
2309
+ font-family: var(--font-body, "Rethink Sans", system-ui, sans-serif);
2310
+ color: var(--color-text, #171717);
2311
+ }
2312
+ .fds-accordion + .fds-accordion {
2313
+ margin-top: var(--spacing-3, 12px);
2314
+ }
2315
+ .fds-accordion__header {
2316
+ display: flex;
2317
+ align-items: center;
2318
+ gap: var(--spacing-3, 12px);
2319
+ padding: 0 var(--spacing-4, 16px) 0 0;
2320
+ }
2321
+ .fds-accordion__trigger {
2322
+ flex: 1 1 auto;
2323
+ min-width: 0;
2324
+ display: flex;
2325
+ align-items: center;
2326
+ gap: var(--spacing-3, 12px);
2327
+ padding: var(--spacing-4, 16px);
2328
+ margin: 0;
2329
+ border: 0;
2330
+ background: none;
2331
+ font: inherit;
2332
+ color: inherit;
2333
+ text-align: left;
2334
+ cursor: pointer;
2335
+ border-radius: var(--radius-xl, 12px);
2336
+ }
2337
+ .fds-accordion__trigger:focus-visible {
2338
+ outline: 2px solid var(--color-focus, #ea580c);
2339
+ outline-offset: -2px;
2340
+ }
2341
+ .fds-accordion__actions {
2342
+ display: flex;
2343
+ align-items: center;
2344
+ gap: var(--spacing-2, 8px);
2345
+ flex-shrink: 0;
2346
+ }
2347
+ .fds-accordion__chevron {
2348
+ display: inline-flex;
2349
+ flex-shrink: 0;
2350
+ color: var(--color-text-secondary, #525252);
2351
+ cursor: pointer;
2352
+ transition: transform var(--duration-quick, 150ms) ease;
2353
+ }
2354
+ .fds-accordion[data-open] .fds-accordion__chevron {
2355
+ transform: rotate(180deg);
2356
+ }
2357
+ .fds-accordion__body {
2358
+ padding: 0 var(--spacing-4, 16px) var(--spacing-4, 16px);
2359
+ }
2360
+ @media (prefers-reduced-motion: reduce) {
2361
+ .fds-accordion__chevron {
2362
+ transition: none;
2363
+ }
2364
+ }
2365
+
2366
+ /* src/components/StepList/step-list.css */
2367
+ .fds-steps {
2368
+ list-style: none;
2369
+ margin: 0;
2370
+ padding: 0;
2371
+ display: flex;
2372
+ flex-direction: column;
2373
+ gap: var(--spacing-3, 12px);
2374
+ font-family: var(--font-body, "Rethink Sans", system-ui, sans-serif);
2375
+ }
2376
+ .fds-steps__step {
2377
+ box-sizing: border-box;
2378
+ display: flex;
2379
+ align-items: center;
2380
+ gap: var(--spacing-3, 12px);
2381
+ min-height: 54px;
2382
+ padding: var(--spacing-3, 12px) var(--spacing-4, 16px);
2383
+ border: 1px solid var(--color-border, #e5e5e5);
2384
+ border-radius: var(--radius-lg, 8px);
2385
+ background: var(--color-surface-subtle, #fafafa);
2386
+ font-size: var(--font-size-text-sm, 14px);
2387
+ line-height: 20px;
2388
+ color: var(--color-text-muted, #a3a3a3);
2389
+ }
2390
+ .fds-steps__icon {
2391
+ display: inline-flex;
2392
+ flex-shrink: 0;
2393
+ }
2394
+ .fds-steps__step--done {
2395
+ background: var(--color-text, #171717);
2396
+ border-color: var(--color-text, #171717);
2397
+ color: var(--color-bg, #ffffff);
2398
+ font-weight: var(--font-weight-medium, 500);
2399
+ }
2400
+ .fds-steps__step--active {
2401
+ background: var(--color-surface, #f5f5f5);
2402
+ color: var(--color-text, #171717);
2403
+ font-weight: var(--font-weight-medium, 500);
2404
+ }
2405
+ .fds-steps__step--failed {
2406
+ background: var(--color-error-bg, #fef2f2);
2407
+ border-color: var(--color-error-border, #fecaca);
2408
+ color: var(--color-error-text, #991b1b);
2409
+ font-weight: var(--font-weight-medium, 500);
2410
+ }
2411
+ .fds-steps__spinner {
2412
+ animation: fds-steps-spin 1.2s linear infinite;
2413
+ }
2414
+ @keyframes fds-steps-spin {
2415
+ to {
2416
+ transform: rotate(360deg);
2417
+ }
2418
+ }
2419
+ .fds-steps__sr {
2420
+ position: absolute;
2421
+ width: 1px;
2422
+ height: 1px;
2423
+ overflow: hidden;
2424
+ clip: rect(0 0 0 0);
2425
+ white-space: nowrap;
2426
+ }
2427
+ @media (prefers-reduced-motion: reduce) {
2428
+ .fds-steps__spinner {
2429
+ animation: none;
2430
+ }
2431
+ }
package/dist/index.d.cts CHANGED
@@ -462,6 +462,8 @@ declare function Badge({ tone, icon, className, children, ...rest }: BadgeProps)
462
462
  interface TagProps extends HTMLAttributes<HTMLSpanElement> {
463
463
  /** Colour of the leading dot. Leave out for no dot. */
464
464
  dot?: Tone;
465
+ /** Leading icon, drawn at 14px, e.g. a repository or branch mark. */
466
+ icon?: ReactNode;
465
467
  children: ReactNode;
466
468
  }
467
469
  /**
@@ -469,7 +471,7 @@ interface TagProps extends HTMLAttributes<HTMLSpanElement> {
469
471
  * optional coloured dot. Measured from Figma "✅Project" comparison-type chip
470
472
  * (node 231:59808).
471
473
  */
472
- declare function Tag({ dot, className, children, ...rest }: TagProps): react.JSX.Element;
474
+ declare function Tag({ dot, icon, className, children, ...rest }: TagProps): react.JSX.Element;
473
475
 
474
476
  interface MultiSelectOption {
475
477
  value: string;
@@ -603,4 +605,105 @@ interface SkeletonGroupProps extends HTMLAttributes<HTMLDivElement> {
603
605
  /** Wraps a skeleton layout and announces it once as loading (role="status"). */
604
606
  declare function SkeletonGroup({ label, className, children, ...rest }: SkeletonGroupProps): react.JSX.Element;
605
607
 
606
- export { AppNav, type AppNavProps, Avatar, type AvatarProps, type AvatarSize, Badge, type BadgeProps, type Breakpoint, Button, ButtonLink, type ButtonLinkProps, type ButtonProps, type ButtonSize, type ButtonVariant, Callout, type CalloutProps, type CalloutTone, type DiffElement, type DiffPropertyLine, DiffPropertyList, type DiffPropertyListProps, DiffSection, type DiffSectionProps, EmptyState, type EmptyStateProps, MismatchCard, type MismatchCardProps, type MismatchProperty, Modal, type ModalProps, type ModalSize, type ModalTone, MultiSelect, type MultiSelectOption, type MultiSelectProps, Pagination, type PaginationProps, SearchInput, type SearchInputProps, SectionHeader, type SectionHeaderProps, SegmentedControl, type SegmentedControlProps, type SegmentedOption, Select, type SelectOption, type SelectProps, SettingsSection, type SettingsSectionProps, Skeleton, SkeletonGroup, type SkeletonGroupProps, type SkeletonProps, type SkeletonShape, Split, type SplitProps, StackedCardsIllustration, TabNav, type TabNavProps, Tag, type TagProps, Toggle, type ToggleProps, type ToggleSize, type Tone, breakpoints };
608
+ /** Surface colour. `subtle` is the grey panel, `default` white with a border, `plain` white without one. */
609
+ type CardTone = 'subtle' | 'default' | 'plain';
610
+ type CardPadding = 'none' | 'md' | 'lg';
611
+ interface CardProps extends HTMLAttributes<HTMLDivElement> {
612
+ /** @default 'default' */
613
+ tone?: CardTone;
614
+ /** Inner spacing: 24px (`md`) or 40px (`lg`, 24px on phones). @default 'md' */
615
+ padding?: CardPadding;
616
+ children?: ReactNode;
617
+ }
618
+ /**
619
+ * Fidel design-system Card: a rounded surface that groups related content.
620
+ * It has no layout of its own; put anything inside. Measured from Figma
621
+ * "Integrations" frames (nodes 231:66631, 231:67267, 231:67335).
622
+ */
623
+ declare function Card({ tone, padding, className, children, ...rest }: CardProps): react.JSX.Element;
624
+
625
+ interface StatProps extends HTMLAttributes<HTMLDivElement> {
626
+ /** What the number counts, e.g. "Tokens found". */
627
+ label: ReactNode;
628
+ /** The figure itself. */
629
+ value: ReactNode;
630
+ /** Detail under the figure, e.g. a row of Tags. */
631
+ children?: ReactNode;
632
+ }
633
+ /**
634
+ * Fidel design-system Stat: a caption, a large figure and optional detail.
635
+ * Has no surface; wrap it in a Card. Measured from Figma "Design System
636
+ * connected" (node 231:67335).
637
+ */
638
+ declare function Stat({ label, value, children, className, ...rest }: StatProps): react.JSX.Element;
639
+
640
+ interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'size'> {
641
+ /** Text beside the box. Without one, pass `aria-label`. */
642
+ label?: ReactNode;
643
+ /** Secondary line under the label. */
644
+ description?: ReactNode;
645
+ /** Shows a dash for "some of these"; screen readers announce it as mixed. */
646
+ indeterminate?: boolean;
647
+ }
648
+ /**
649
+ * Fidel design-system Checkbox. Renders a native checkbox, visually hidden
650
+ * over a drawn box, so checked/indeterminate/disabled/focus come from the
651
+ * input itself and every standard input attribute passes through. Built like
652
+ * Toggle; checked is ink-filled to match the Integrations frames.
653
+ */
654
+ declare const Checkbox: react.ForwardRefExoticComponent<CheckboxProps & react.RefAttributes<HTMLInputElement>>;
655
+
656
+ interface RadioProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'size'> {
657
+ /** Text beside the circle. Without one, pass `aria-label`. */
658
+ label?: ReactNode;
659
+ /** Secondary line under the label. */
660
+ description?: ReactNode;
661
+ }
662
+ /**
663
+ * Fidel design-system Radio: one choice of several. Give every Radio in a
664
+ * set the same `name`, and wrap the set in a `role="radiogroup"` element (or
665
+ * a fieldset) with a label. Built like Checkbox: a native input over a drawn
666
+ * circle, ink-filled when chosen.
667
+ */
668
+ declare const Radio: react.ForwardRefExoticComponent<RadioProps & react.RefAttributes<HTMLInputElement>>;
669
+
670
+ interface AccordionProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
671
+ /** Header content, always visible. Clicking it opens and closes the body. */
672
+ summary: ReactNode;
673
+ /**
674
+ * Controls in the header that do their own thing, e.g. a Retry button.
675
+ * Kept outside the toggle so they stay separate keyboard stops.
676
+ */
677
+ actions?: ReactNode;
678
+ /** Controlled open state. */
679
+ open?: boolean;
680
+ /** Initial open state when uncontrolled. @default false */
681
+ defaultOpen?: boolean;
682
+ onOpenChange?: (open: boolean) => void;
683
+ /** Body, shown while open. */
684
+ children?: ReactNode;
685
+ }
686
+ /**
687
+ * Fidel design-system Accordion: one row that expands to show more. Stack
688
+ * several for a list. The header is a button with `aria-expanded`; the
689
+ * chevron on the right points down when closed and up when open.
690
+ */
691
+ declare function Accordion({ summary, actions, open: openProp, defaultOpen, onOpenChange, children, className, ...rest }: AccordionProps): react.JSX.Element;
692
+
693
+ type StepStatus = 'done' | 'active' | 'pending' | 'failed';
694
+ interface Step {
695
+ id: string;
696
+ label: ReactNode;
697
+ status: StepStatus;
698
+ }
699
+ interface StepListProps extends Omit<HTMLAttributes<HTMLOListElement>, 'children'> {
700
+ steps: Step[];
701
+ }
702
+ /**
703
+ * Fidel design-system StepList: an ordered list of steps, each done, in
704
+ * progress, not started or failed. The in-progress step is marked
705
+ * `aria-current="step"`. Measured from Figma "Scanning" (node 231:67267).
706
+ */
707
+ declare function StepList({ steps, className, ...rest }: StepListProps): react.JSX.Element;
708
+
709
+ export { Accordion, type AccordionProps, AppNav, type AppNavProps, Avatar, type AvatarProps, type AvatarSize, Badge, type BadgeProps, type Breakpoint, Button, ButtonLink, type ButtonLinkProps, type ButtonProps, type ButtonSize, type ButtonVariant, Callout, type CalloutProps, type CalloutTone, Card, type CardPadding, type CardProps, type CardTone, Checkbox, type CheckboxProps, type DiffElement, type DiffPropertyLine, DiffPropertyList, type DiffPropertyListProps, DiffSection, type DiffSectionProps, EmptyState, type EmptyStateProps, MismatchCard, type MismatchCardProps, type MismatchProperty, Modal, type ModalProps, type ModalSize, type ModalTone, MultiSelect, type MultiSelectOption, type MultiSelectProps, Pagination, type PaginationProps, Radio, type RadioProps, SearchInput, type SearchInputProps, SectionHeader, type SectionHeaderProps, SegmentedControl, type SegmentedControlProps, type SegmentedOption, Select, type SelectOption, type SelectProps, SettingsSection, type SettingsSectionProps, Skeleton, SkeletonGroup, type SkeletonGroupProps, type SkeletonProps, type SkeletonShape, Split, type SplitProps, StackedCardsIllustration, Stat, type StatProps, type Step, StepList, type StepListProps, type StepStatus, TabNav, type TabNavProps, Tag, type TagProps, Toggle, type ToggleProps, type ToggleSize, type Tone, breakpoints };
package/dist/index.d.ts CHANGED
@@ -462,6 +462,8 @@ declare function Badge({ tone, icon, className, children, ...rest }: BadgeProps)
462
462
  interface TagProps extends HTMLAttributes<HTMLSpanElement> {
463
463
  /** Colour of the leading dot. Leave out for no dot. */
464
464
  dot?: Tone;
465
+ /** Leading icon, drawn at 14px, e.g. a repository or branch mark. */
466
+ icon?: ReactNode;
465
467
  children: ReactNode;
466
468
  }
467
469
  /**
@@ -469,7 +471,7 @@ interface TagProps extends HTMLAttributes<HTMLSpanElement> {
469
471
  * optional coloured dot. Measured from Figma "✅Project" comparison-type chip
470
472
  * (node 231:59808).
471
473
  */
472
- declare function Tag({ dot, className, children, ...rest }: TagProps): react.JSX.Element;
474
+ declare function Tag({ dot, icon, className, children, ...rest }: TagProps): react.JSX.Element;
473
475
 
474
476
  interface MultiSelectOption {
475
477
  value: string;
@@ -603,4 +605,105 @@ interface SkeletonGroupProps extends HTMLAttributes<HTMLDivElement> {
603
605
  /** Wraps a skeleton layout and announces it once as loading (role="status"). */
604
606
  declare function SkeletonGroup({ label, className, children, ...rest }: SkeletonGroupProps): react.JSX.Element;
605
607
 
606
- export { AppNav, type AppNavProps, Avatar, type AvatarProps, type AvatarSize, Badge, type BadgeProps, type Breakpoint, Button, ButtonLink, type ButtonLinkProps, type ButtonProps, type ButtonSize, type ButtonVariant, Callout, type CalloutProps, type CalloutTone, type DiffElement, type DiffPropertyLine, DiffPropertyList, type DiffPropertyListProps, DiffSection, type DiffSectionProps, EmptyState, type EmptyStateProps, MismatchCard, type MismatchCardProps, type MismatchProperty, Modal, type ModalProps, type ModalSize, type ModalTone, MultiSelect, type MultiSelectOption, type MultiSelectProps, Pagination, type PaginationProps, SearchInput, type SearchInputProps, SectionHeader, type SectionHeaderProps, SegmentedControl, type SegmentedControlProps, type SegmentedOption, Select, type SelectOption, type SelectProps, SettingsSection, type SettingsSectionProps, Skeleton, SkeletonGroup, type SkeletonGroupProps, type SkeletonProps, type SkeletonShape, Split, type SplitProps, StackedCardsIllustration, TabNav, type TabNavProps, Tag, type TagProps, Toggle, type ToggleProps, type ToggleSize, type Tone, breakpoints };
608
+ /** Surface colour. `subtle` is the grey panel, `default` white with a border, `plain` white without one. */
609
+ type CardTone = 'subtle' | 'default' | 'plain';
610
+ type CardPadding = 'none' | 'md' | 'lg';
611
+ interface CardProps extends HTMLAttributes<HTMLDivElement> {
612
+ /** @default 'default' */
613
+ tone?: CardTone;
614
+ /** Inner spacing: 24px (`md`) or 40px (`lg`, 24px on phones). @default 'md' */
615
+ padding?: CardPadding;
616
+ children?: ReactNode;
617
+ }
618
+ /**
619
+ * Fidel design-system Card: a rounded surface that groups related content.
620
+ * It has no layout of its own; put anything inside. Measured from Figma
621
+ * "Integrations" frames (nodes 231:66631, 231:67267, 231:67335).
622
+ */
623
+ declare function Card({ tone, padding, className, children, ...rest }: CardProps): react.JSX.Element;
624
+
625
+ interface StatProps extends HTMLAttributes<HTMLDivElement> {
626
+ /** What the number counts, e.g. "Tokens found". */
627
+ label: ReactNode;
628
+ /** The figure itself. */
629
+ value: ReactNode;
630
+ /** Detail under the figure, e.g. a row of Tags. */
631
+ children?: ReactNode;
632
+ }
633
+ /**
634
+ * Fidel design-system Stat: a caption, a large figure and optional detail.
635
+ * Has no surface; wrap it in a Card. Measured from Figma "Design System
636
+ * connected" (node 231:67335).
637
+ */
638
+ declare function Stat({ label, value, children, className, ...rest }: StatProps): react.JSX.Element;
639
+
640
+ interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'size'> {
641
+ /** Text beside the box. Without one, pass `aria-label`. */
642
+ label?: ReactNode;
643
+ /** Secondary line under the label. */
644
+ description?: ReactNode;
645
+ /** Shows a dash for "some of these"; screen readers announce it as mixed. */
646
+ indeterminate?: boolean;
647
+ }
648
+ /**
649
+ * Fidel design-system Checkbox. Renders a native checkbox, visually hidden
650
+ * over a drawn box, so checked/indeterminate/disabled/focus come from the
651
+ * input itself and every standard input attribute passes through. Built like
652
+ * Toggle; checked is ink-filled to match the Integrations frames.
653
+ */
654
+ declare const Checkbox: react.ForwardRefExoticComponent<CheckboxProps & react.RefAttributes<HTMLInputElement>>;
655
+
656
+ interface RadioProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'size'> {
657
+ /** Text beside the circle. Without one, pass `aria-label`. */
658
+ label?: ReactNode;
659
+ /** Secondary line under the label. */
660
+ description?: ReactNode;
661
+ }
662
+ /**
663
+ * Fidel design-system Radio: one choice of several. Give every Radio in a
664
+ * set the same `name`, and wrap the set in a `role="radiogroup"` element (or
665
+ * a fieldset) with a label. Built like Checkbox: a native input over a drawn
666
+ * circle, ink-filled when chosen.
667
+ */
668
+ declare const Radio: react.ForwardRefExoticComponent<RadioProps & react.RefAttributes<HTMLInputElement>>;
669
+
670
+ interface AccordionProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
671
+ /** Header content, always visible. Clicking it opens and closes the body. */
672
+ summary: ReactNode;
673
+ /**
674
+ * Controls in the header that do their own thing, e.g. a Retry button.
675
+ * Kept outside the toggle so they stay separate keyboard stops.
676
+ */
677
+ actions?: ReactNode;
678
+ /** Controlled open state. */
679
+ open?: boolean;
680
+ /** Initial open state when uncontrolled. @default false */
681
+ defaultOpen?: boolean;
682
+ onOpenChange?: (open: boolean) => void;
683
+ /** Body, shown while open. */
684
+ children?: ReactNode;
685
+ }
686
+ /**
687
+ * Fidel design-system Accordion: one row that expands to show more. Stack
688
+ * several for a list. The header is a button with `aria-expanded`; the
689
+ * chevron on the right points down when closed and up when open.
690
+ */
691
+ declare function Accordion({ summary, actions, open: openProp, defaultOpen, onOpenChange, children, className, ...rest }: AccordionProps): react.JSX.Element;
692
+
693
+ type StepStatus = 'done' | 'active' | 'pending' | 'failed';
694
+ interface Step {
695
+ id: string;
696
+ label: ReactNode;
697
+ status: StepStatus;
698
+ }
699
+ interface StepListProps extends Omit<HTMLAttributes<HTMLOListElement>, 'children'> {
700
+ steps: Step[];
701
+ }
702
+ /**
703
+ * Fidel design-system StepList: an ordered list of steps, each done, in
704
+ * progress, not started or failed. The in-progress step is marked
705
+ * `aria-current="step"`. Measured from Figma "Scanning" (node 231:67267).
706
+ */
707
+ declare function StepList({ steps, className, ...rest }: StepListProps): react.JSX.Element;
708
+
709
+ export { Accordion, type AccordionProps, AppNav, type AppNavProps, Avatar, type AvatarProps, type AvatarSize, Badge, type BadgeProps, type Breakpoint, Button, ButtonLink, type ButtonLinkProps, type ButtonProps, type ButtonSize, type ButtonVariant, Callout, type CalloutProps, type CalloutTone, Card, type CardPadding, type CardProps, type CardTone, Checkbox, type CheckboxProps, type DiffElement, type DiffPropertyLine, DiffPropertyList, type DiffPropertyListProps, DiffSection, type DiffSectionProps, EmptyState, type EmptyStateProps, MismatchCard, type MismatchCardProps, type MismatchProperty, Modal, type ModalProps, type ModalSize, type ModalTone, MultiSelect, type MultiSelectOption, type MultiSelectProps, Pagination, type PaginationProps, Radio, type RadioProps, SearchInput, type SearchInputProps, SectionHeader, type SectionHeaderProps, SegmentedControl, type SegmentedControlProps, type SegmentedOption, Select, type SelectOption, type SelectProps, SettingsSection, type SettingsSectionProps, Skeleton, SkeletonGroup, type SkeletonGroupProps, type SkeletonProps, type SkeletonShape, Split, type SplitProps, StackedCardsIllustration, Stat, type StatProps, type Step, StepList, type StepListProps, type StepStatus, TabNav, type TabNavProps, Tag, type TagProps, Toggle, type ToggleProps, type ToggleSize, type Tone, breakpoints };
package/dist/index.js CHANGED
@@ -940,9 +940,10 @@ function Badge({ tone = "neutral", icon, className, children, ...rest }) {
940
940
 
941
941
  // src/components/Tag/Tag.tsx
942
942
  import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
943
- function Tag({ dot, className, children, ...rest }) {
943
+ function Tag({ dot, icon, className, children, ...rest }) {
944
944
  return /* @__PURE__ */ jsxs14("span", { className: ["fds-tag", className].filter(Boolean).join(" "), ...rest, children: [
945
945
  dot && /* @__PURE__ */ jsx16("span", { className: `fds-dot fds-dot--${dot}`, "aria-hidden": "true" }),
946
+ icon != null && /* @__PURE__ */ jsx16("span", { className: "fds-tag__icon", "aria-hidden": "true", children: icon }),
946
947
  children
947
948
  ] });
948
949
  }
@@ -1261,13 +1262,195 @@ function Skeleton({ shape = "rect", width, height, lines = 1, className, style,
1261
1262
  function SkeletonGroup({ label = "Loading", className, children, ...rest }) {
1262
1263
  return /* @__PURE__ */ jsx22("div", { role: "status", "aria-busy": "true", "aria-label": label, className, ...rest, children });
1263
1264
  }
1265
+
1266
+ // src/components/Card/Card.tsx
1267
+ import { jsx as jsx23 } from "react/jsx-runtime";
1268
+ function Card({ tone = "default", padding = "md", className, children, ...rest }) {
1269
+ const classes = ["fds-card", `fds-card--${tone}`, `fds-card--pad-${padding}`, className].filter(Boolean).join(" ");
1270
+ return /* @__PURE__ */ jsx23("div", { className: classes, ...rest, children });
1271
+ }
1272
+
1273
+ // src/components/Stat/Stat.tsx
1274
+ import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
1275
+ function Stat({ label, value, children, className, ...rest }) {
1276
+ return /* @__PURE__ */ jsxs20("div", { className: ["fds-stat", className].filter(Boolean).join(" "), ...rest, children: [
1277
+ /* @__PURE__ */ jsx24("p", { className: "fds-stat__label", children: label }),
1278
+ /* @__PURE__ */ jsx24("p", { className: "fds-stat__value", children: value }),
1279
+ children != null && /* @__PURE__ */ jsx24("div", { className: "fds-stat__detail", children })
1280
+ ] });
1281
+ }
1282
+
1283
+ // src/components/Checkbox/Checkbox.tsx
1284
+ import {
1285
+ forwardRef as forwardRef4,
1286
+ useEffect as useEffect5,
1287
+ useId as useId7,
1288
+ useImperativeHandle as useImperativeHandle2,
1289
+ useRef as useRef6
1290
+ } from "react";
1291
+ import { jsx as jsx25, jsxs as jsxs21 } from "react/jsx-runtime";
1292
+ var Checkbox = forwardRef4(function Checkbox2({ label, description, indeterminate = false, className, id, disabled, ...rest }, ref) {
1293
+ const generatedId = useId7();
1294
+ const inputId = id ?? generatedId;
1295
+ const inputRef = useRef6(null);
1296
+ useImperativeHandle2(ref, () => inputRef.current);
1297
+ useEffect5(() => {
1298
+ if (inputRef.current) inputRef.current.indeterminate = indeterminate;
1299
+ }, [indeterminate]);
1300
+ return /* @__PURE__ */ jsxs21(
1301
+ "div",
1302
+ {
1303
+ className: ["fds-checkbox", className].filter(Boolean).join(" "),
1304
+ "data-disabled": disabled ? "" : void 0,
1305
+ children: [
1306
+ /* @__PURE__ */ jsx25(
1307
+ "input",
1308
+ {
1309
+ ...rest,
1310
+ id: inputId,
1311
+ ref: inputRef,
1312
+ type: "checkbox",
1313
+ disabled,
1314
+ className: "fds-checkbox__input"
1315
+ }
1316
+ ),
1317
+ /* @__PURE__ */ jsxs21("label", { htmlFor: inputId, className: "fds-checkbox__control", children: [
1318
+ /* @__PURE__ */ jsxs21("span", { className: "fds-checkbox__box", "aria-hidden": "true", children: [
1319
+ /* @__PURE__ */ jsx25("svg", { className: "fds-checkbox__tick", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsx25("path", { d: "M10 3L4.5 8.5 2 6", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) }),
1320
+ /* @__PURE__ */ jsx25("svg", { className: "fds-checkbox__dash", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsx25("path", { d: "M3 6h6", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" }) })
1321
+ ] }),
1322
+ (label != null || description != null) && /* @__PURE__ */ jsxs21("span", { className: "fds-checkbox__text", children: [
1323
+ label != null && /* @__PURE__ */ jsx25("span", { className: "fds-checkbox__label", children: label }),
1324
+ description != null && /* @__PURE__ */ jsx25("span", { className: "fds-checkbox__description", children: description })
1325
+ ] })
1326
+ ] })
1327
+ ]
1328
+ }
1329
+ );
1330
+ });
1331
+
1332
+ // src/components/Radio/Radio.tsx
1333
+ import { forwardRef as forwardRef5, useId as useId8 } from "react";
1334
+ import { jsx as jsx26, jsxs as jsxs22 } from "react/jsx-runtime";
1335
+ var Radio = forwardRef5(function Radio2({ label, description, className, id, disabled, ...rest }, ref) {
1336
+ const generatedId = useId8();
1337
+ const inputId = id ?? generatedId;
1338
+ return /* @__PURE__ */ jsxs22("div", { className: ["fds-radio", className].filter(Boolean).join(" "), "data-disabled": disabled ? "" : void 0, children: [
1339
+ /* @__PURE__ */ jsx26("input", { ...rest, id: inputId, ref, type: "radio", disabled, className: "fds-radio__input" }),
1340
+ /* @__PURE__ */ jsxs22("label", { htmlFor: inputId, className: "fds-radio__control", children: [
1341
+ /* @__PURE__ */ jsx26("span", { className: "fds-radio__circle", "aria-hidden": "true" }),
1342
+ (label != null || description != null) && /* @__PURE__ */ jsxs22("span", { className: "fds-radio__text", children: [
1343
+ label != null && /* @__PURE__ */ jsx26("span", { className: "fds-radio__label", children: label }),
1344
+ description != null && /* @__PURE__ */ jsx26("span", { className: "fds-radio__description", children: description })
1345
+ ] })
1346
+ ] })
1347
+ ] });
1348
+ });
1349
+
1350
+ // src/components/Accordion/Accordion.tsx
1351
+ import { useId as useId9, useState as useState7 } from "react";
1352
+ import { jsx as jsx27, jsxs as jsxs23 } from "react/jsx-runtime";
1353
+ function Accordion({
1354
+ summary,
1355
+ actions,
1356
+ open: openProp,
1357
+ defaultOpen = false,
1358
+ onOpenChange,
1359
+ children,
1360
+ className,
1361
+ ...rest
1362
+ }) {
1363
+ const [openState, setOpenState] = useState7(defaultOpen);
1364
+ const open = openProp ?? openState;
1365
+ const bodyId = useId9();
1366
+ const toggle = () => {
1367
+ const next = !open;
1368
+ if (openProp === void 0) setOpenState(next);
1369
+ onOpenChange?.(next);
1370
+ };
1371
+ return /* @__PURE__ */ jsxs23(
1372
+ "div",
1373
+ {
1374
+ className: ["fds-accordion", className].filter(Boolean).join(" "),
1375
+ "data-open": open ? "" : void 0,
1376
+ ...rest,
1377
+ children: [
1378
+ /* @__PURE__ */ jsxs23("div", { className: "fds-accordion__header", children: [
1379
+ /* @__PURE__ */ jsx27(
1380
+ "button",
1381
+ {
1382
+ type: "button",
1383
+ className: "fds-accordion__trigger",
1384
+ "aria-expanded": open,
1385
+ "aria-controls": bodyId,
1386
+ onClick: toggle,
1387
+ children: summary
1388
+ }
1389
+ ),
1390
+ actions != null && /* @__PURE__ */ jsx27("div", { className: "fds-accordion__actions", children: actions }),
1391
+ /* @__PURE__ */ jsx27("span", { className: "fds-accordion__chevron", "aria-hidden": "true", onClick: toggle, children: /* @__PURE__ */ jsx27("svg", { viewBox: "0 0 24 24", width: "16", height: "16", fill: "none", children: /* @__PURE__ */ jsx27("path", { d: "M6 9l6 6 6-6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) })
1392
+ ] }),
1393
+ /* @__PURE__ */ jsx27("div", { id: bodyId, className: "fds-accordion__body", hidden: !open, children })
1394
+ ]
1395
+ }
1396
+ );
1397
+ }
1398
+
1399
+ // src/components/StepList/StepList.tsx
1400
+ import { jsx as jsx28, jsxs as jsxs24 } from "react/jsx-runtime";
1401
+ var STATUS_TEXT = {
1402
+ done: "Done",
1403
+ active: "In progress",
1404
+ pending: "Not started",
1405
+ failed: "Failed"
1406
+ };
1407
+ function StepIcon({ status }) {
1408
+ const common = { viewBox: "0 0 24 24", width: 18, height: 18, fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" };
1409
+ if (status === "done") {
1410
+ return /* @__PURE__ */ jsxs24("svg", { ...common, children: [
1411
+ /* @__PURE__ */ jsx28("circle", { cx: "12", cy: "12", r: "9" }),
1412
+ /* @__PURE__ */ jsx28("path", { d: "M8 12.5l2.5 2.5 5-5.5" })
1413
+ ] });
1414
+ }
1415
+ if (status === "active") {
1416
+ return /* @__PURE__ */ jsx28("svg", { ...common, className: "fds-steps__spinner", children: /* @__PURE__ */ jsx28("path", { d: "M12 3v3M12 18v3M3 12h3M18 12h3M5.6 5.6l2.1 2.1M16.3 16.3l2.1 2.1M5.6 18.4l2.1-2.1M16.3 7.7l2.1-2.1" }) });
1417
+ }
1418
+ if (status === "failed") {
1419
+ return /* @__PURE__ */ jsxs24("svg", { ...common, children: [
1420
+ /* @__PURE__ */ jsx28("circle", { cx: "12", cy: "12", r: "9" }),
1421
+ /* @__PURE__ */ jsx28("path", { d: "M12 7.5v5.5M12 16.5v.01" })
1422
+ ] });
1423
+ }
1424
+ return /* @__PURE__ */ jsx28("svg", { ...common, children: /* @__PURE__ */ jsx28("circle", { cx: "12", cy: "12", r: "9" }) });
1425
+ }
1426
+ function StepList({ steps, className, ...rest }) {
1427
+ return /* @__PURE__ */ jsx28("ol", { className: ["fds-steps", className].filter(Boolean).join(" "), ...rest, children: steps.map((step) => /* @__PURE__ */ jsxs24(
1428
+ "li",
1429
+ {
1430
+ className: `fds-steps__step fds-steps__step--${step.status}`,
1431
+ "aria-current": step.status === "active" ? "step" : void 0,
1432
+ children: [
1433
+ /* @__PURE__ */ jsx28("span", { className: "fds-steps__icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx28(StepIcon, { status: step.status }) }),
1434
+ /* @__PURE__ */ jsx28("span", { className: "fds-steps__label", children: step.label }),
1435
+ /* @__PURE__ */ jsxs24("span", { className: "fds-steps__sr", children: [
1436
+ ", ",
1437
+ STATUS_TEXT[step.status]
1438
+ ] })
1439
+ ]
1440
+ },
1441
+ step.id
1442
+ )) });
1443
+ }
1264
1444
  export {
1445
+ Accordion,
1265
1446
  AppNav,
1266
1447
  Avatar,
1267
1448
  Badge,
1268
1449
  Button,
1269
1450
  ButtonLink,
1270
1451
  Callout,
1452
+ Card,
1453
+ Checkbox,
1271
1454
  DiffPropertyList,
1272
1455
  DiffSection,
1273
1456
  EmptyState,
@@ -1275,6 +1458,7 @@ export {
1275
1458
  Modal,
1276
1459
  MultiSelect,
1277
1460
  Pagination,
1461
+ Radio,
1278
1462
  SearchInput,
1279
1463
  SectionHeader,
1280
1464
  SegmentedControl,
@@ -1284,6 +1468,8 @@ export {
1284
1468
  SkeletonGroup,
1285
1469
  Split,
1286
1470
  StackedCardsIllustration,
1471
+ Stat,
1472
+ StepList,
1287
1473
  TabNav,
1288
1474
  Tag,
1289
1475
  Toggle,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usefidel/design-system",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "description": "Fidel brand design tokens + React component library — DTCG token source of truth, generated per-surface CSS + Tailwind-v4 parser-compatibility output, and on-brand React components (Button, MismatchCard, DiffSection).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",