@handamade/psi-react 0.12.0 → 0.13.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.
@@ -1214,6 +1214,179 @@
1214
1214
  "description": "The subtree that may call `useToast()`. The region is rendered alongside it."
1215
1215
  }
1216
1216
  ]
1217
+ },
1218
+ {
1219
+ "name": "Tabs",
1220
+ "description": "Tab set root (D67) — holds no selection state of its own, following D50, D53 and D62: `value` and `onValueChange` are required and there is no `defaultValue`. Values are strings rather than indices because an index breaks the moment a tab is inserted, and real tab sets map to ids. `Tab` and `TabPanel` pair by value, so their source order need not match. Activation is automatic: arrow keys move focus and selection together. That is the APG default where panel content is already available, and manual activation is deliberately not offered as a mode — a consumer for whom activating a panel is expensive already controls what the panel renders.",
1221
+ "slots": [
1222
+ {
1223
+ "name": "body",
1224
+ "accepts": {
1225
+ "components": [
1226
+ "TabList",
1227
+ "TabPanel"
1228
+ ]
1229
+ },
1230
+ "cardinality": "1..*",
1231
+ "order": 1
1232
+ }
1233
+ ],
1234
+ "props": [
1235
+ {
1236
+ "name": "value",
1237
+ "type": "string",
1238
+ "required": true,
1239
+ "default": null,
1240
+ "description": "Controlled selected tab, matched against each `Tab`/`TabPanel` value."
1241
+ },
1242
+ {
1243
+ "name": "onValueChange",
1244
+ "type": "(value: string) => void",
1245
+ "required": true,
1246
+ "default": null,
1247
+ "description": "Fires with the newly selected value; the consumer flips `value`."
1248
+ },
1249
+ {
1250
+ "name": "orientation",
1251
+ "type": "\"horizontal\" | \"vertical\"",
1252
+ "required": false,
1253
+ "default": "horizontal",
1254
+ "description": "Axis of the tab list, which also picks the arrow keys."
1255
+ },
1256
+ {
1257
+ "name": "children",
1258
+ "type": "ReactNode",
1259
+ "required": true,
1260
+ "default": null,
1261
+ "description": "A `TabList` and one `TabPanel` per tab."
1262
+ },
1263
+ {
1264
+ "name": "className",
1265
+ "type": "string",
1266
+ "required": false,
1267
+ "default": null,
1268
+ "description": "Additional CSS class name(s) merged onto the component's root element."
1269
+ },
1270
+ {
1271
+ "name": "ref",
1272
+ "type": "Ref<HTMLDivElement>",
1273
+ "required": false,
1274
+ "default": null,
1275
+ "description": "Forwarded ref to the wrapper `<div>`."
1276
+ }
1277
+ ]
1278
+ },
1279
+ {
1280
+ "name": "TabList",
1281
+ "description": "The `role=\"tablist\"` container (D67). Owns the roving tabindex: the whole list is one Tab stop, and the arrow keys move between tabs inside it.",
1282
+ "slots": [],
1283
+ "props": [
1284
+ {
1285
+ "name": "aria-label",
1286
+ "type": "string",
1287
+ "required": false,
1288
+ "default": null,
1289
+ "description": "Accessible name for the tab set. Declared here rather than inherited so\ndocgen keeps it in the manifest (D60) — a tablist without a name is\nannounced as an unlabelled group, and the prop would otherwise be\ninvisible to an agent reading the manifest."
1290
+ },
1291
+ {
1292
+ "name": "children",
1293
+ "type": "ReactNode",
1294
+ "required": true,
1295
+ "default": null,
1296
+ "description": "One `Tab` per view."
1297
+ },
1298
+ {
1299
+ "name": "className",
1300
+ "type": "string",
1301
+ "required": false,
1302
+ "default": null,
1303
+ "description": "Additional CSS class name(s) merged onto the component's root element."
1304
+ },
1305
+ {
1306
+ "name": "ref",
1307
+ "type": "Ref<HTMLDivElement>",
1308
+ "required": false,
1309
+ "default": null,
1310
+ "description": "Forwarded ref to the tablist element."
1311
+ }
1312
+ ]
1313
+ },
1314
+ {
1315
+ "name": "Tab",
1316
+ "description": "One `role=\"tab\"` (D67). Renders a real `<button>`. `disabled` sets `aria-disabled` rather than the `disabled` attribute, so the tab stays discoverable to assistive tech while being skipped by roving navigation — the same choice MenuItem made in D53.",
1317
+ "slots": [],
1318
+ "props": [
1319
+ {
1320
+ "name": "value",
1321
+ "type": "string",
1322
+ "required": true,
1323
+ "default": null,
1324
+ "description": "Pairs this tab with the `TabPanel` of the same value."
1325
+ },
1326
+ {
1327
+ "name": "children",
1328
+ "type": "ReactNode",
1329
+ "required": true,
1330
+ "default": null,
1331
+ "description": "Label — keep it to one or two words."
1332
+ },
1333
+ {
1334
+ "name": "disabled",
1335
+ "type": "boolean",
1336
+ "required": false,
1337
+ "default": false,
1338
+ "description": "Skipped by arrow navigation and not selectable, but still announced."
1339
+ },
1340
+ {
1341
+ "name": "className",
1342
+ "type": "string",
1343
+ "required": false,
1344
+ "default": null,
1345
+ "description": "Additional CSS class name(s) merged onto the component's root element."
1346
+ },
1347
+ {
1348
+ "name": "ref",
1349
+ "type": "Ref<HTMLButtonElement>",
1350
+ "required": false,
1351
+ "default": null,
1352
+ "description": "Forwarded ref to the underlying `<button>`."
1353
+ }
1354
+ ]
1355
+ },
1356
+ {
1357
+ "name": "TabPanel",
1358
+ "description": "One `role=\"tabpanel\"` (D67). Every panel renders; unselected ones carry `hidden`. Returning null instead would leave `aria-controls` on every unselected tab pointing at an element that does not exist, and would throw away DOM state — a half-filled form in a panel would lose its values on each tab switch. The cost is a heavier DOM when panels are large; the answer to that is rendering less inside the panel, not unmounting it behind the consumer's back. `tabIndex={0}` because a panel whose content has no focusable element would otherwise be unreachable by keyboard.",
1359
+ "slots": [],
1360
+ "props": [
1361
+ {
1362
+ "name": "value",
1363
+ "type": "string",
1364
+ "required": true,
1365
+ "default": null,
1366
+ "description": "Pairs this panel with the `Tab` of the same value."
1367
+ },
1368
+ {
1369
+ "name": "children",
1370
+ "type": "ReactNode",
1371
+ "required": true,
1372
+ "default": null,
1373
+ "description": "The view for this tab. Rendered even when unselected, then `hidden`."
1374
+ },
1375
+ {
1376
+ "name": "className",
1377
+ "type": "string",
1378
+ "required": false,
1379
+ "default": null,
1380
+ "description": "Additional CSS class name(s) merged onto the component's root element."
1381
+ },
1382
+ {
1383
+ "name": "ref",
1384
+ "type": "Ref<HTMLDivElement>",
1385
+ "required": false,
1386
+ "default": null,
1387
+ "description": "Forwarded ref to the panel element."
1388
+ }
1389
+ ]
1217
1390
  }
1218
1391
  ]
1219
1392
  }
@@ -637,11 +637,45 @@
637
637
  "orientation": "{param:orientation}"
638
638
  },
639
639
  "slots": {
640
- "list": [
641
- "{content:tabs-note}"
642
- ],
643
- "panels": [
644
- "{content:panels-note}"
640
+ "body": [
641
+ {
642
+ "component": "TabList",
643
+ "props": {
644
+ "aria-label": "Views"
645
+ },
646
+ "slots": {
647
+ "body": [
648
+ {
649
+ "component": "Tab",
650
+ "props": {
651
+ "value": "all"
652
+ },
653
+ "content": "first-label"
654
+ },
655
+ {
656
+ "component": "Tab",
657
+ "props": {
658
+ "value": "flagged"
659
+ },
660
+ "content": "second-label"
661
+ }
662
+ ]
663
+ }
664
+ },
665
+ {
666
+ "component": "TabPanel",
667
+ "props": {
668
+ "value": "all"
669
+ },
670
+ "content": "first-panel"
671
+ },
672
+ {
673
+ "component": "TabPanel",
674
+ "props": {
675
+ "value": "flagged"
676
+ },
677
+ "content": "second-panel"
678
+ }
645
679
  ]
646
680
  }
647
681
  },
@@ -657,14 +691,14 @@
657
691
  }
658
692
  ],
659
693
  "content": {
660
- "tabs-note": "[one tab per view; keep labels to one or two words]",
661
- "panels-note": "[one panel per tab, in the same order]"
694
+ "first-label": "All",
695
+ "second-label": "Flagged",
696
+ "first-panel": "[the default view]",
697
+ "second-panel": "[the second view; one panel per tab, paired by value]"
662
698
  },
663
- "gaps": [
664
- "Tabs"
665
- ],
666
- "blocked": true,
667
- "preset": null
699
+ "gaps": [],
700
+ "blocked": false,
701
+ "preset": "<Tabs\n orientation=\"horizontal\"\n>\n <TabList\n aria-label=\"Views\"\n >\n <Tab value=\"all\">All</Tab>\n <Tab value=\"flagged\">Flagged</Tab>\n </TabList>\n <TabPanel value=\"all\">[the default view]</TabPanel>\n <TabPanel value=\"flagged\">[the second view; one panel per tab, paired by value]</TabPanel>\n</Tabs>\n"
668
702
  },
669
703
  {
670
704
  "id": "table-pagination",
package/dist/styles.css CHANGED
@@ -1 +1 @@
1
- ._button_c94w9_3{display:inline-flex;align-items:center;justify-content:center;border:none;border-radius:var(--psi-button-radius);cursor:pointer;font-family:var(--psi-font-sans);white-space:nowrap;text-decoration:none;-webkit-user-select:none;user-select:none;transition:background-color var(--psi-duration-150) var(--psi-ease-standard),color var(--psi-duration-150) var(--psi-ease-standard)}._button_c94w9_3:focus-visible{outline:2px solid var(--psi-button-focus-ring);outline-offset:2px}._button_c94w9_3:disabled{opacity:.4;cursor:not-allowed}._button_c94w9_3._disabled_c94w9_30{opacity:.4;cursor:not-allowed;pointer-events:none}._size24_c94w9_44{height:var(--psi-button-24-height);padding-inline:var(--psi-button-24-padding-inline);gap:var(--psi-button-24-gap);font:var(--psi-button-font, var(--psi-button-24-font))}._size24_c94w9_44:has(>svg:first-child){padding-inline-start:var(--psi-button-24-padding-inline-icon)}._size32_c94w9_54{height:var(--psi-button-32-height);padding-inline:var(--psi-button-32-padding-inline);gap:var(--psi-button-32-gap);font:var(--psi-button-font, var(--psi-button-32-font))}._size32_c94w9_54:has(>svg:first-child){padding-inline-start:var(--psi-button-32-padding-inline-icon)}._size40_c94w9_64{height:var(--psi-button-40-height);padding-inline:var(--psi-button-40-padding-inline);gap:var(--psi-button-40-gap);font:var(--psi-button-font, var(--psi-button-40-font))}._size40_c94w9_64:has(>svg:first-child){padding-inline-start:var(--psi-button-40-padding-inline-icon)}._size48_c94w9_74{height:var(--psi-button-48-height);padding-inline:var(--psi-button-48-padding-inline);gap:var(--psi-button-48-gap);font:var(--psi-button-font, var(--psi-button-48-font))}._size48_c94w9_74:has(>svg:first-child){padding-inline-start:var(--psi-button-48-padding-inline-icon)}._accent_c94w9_87{background:var(--psi-button-accent-bg);color:var(--psi-button-accent-fg)}._accent_c94w9_87:hover:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-accent-bg-hover)}._accent_c94w9_87:active:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-accent-bg-active)}._accentSubtle_c94w9_99{background:var(--psi-button-accent-subtle-bg);color:var(--psi-button-accent-subtle-fg)}._accentSubtle_c94w9_99:hover:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-accent-subtle-bg-hover)}._accentSubtle_c94w9_99:active:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-accent-subtle-bg-active)}._neutral_c94w9_111{background:var(--psi-button-neutral-bg);color:var(--psi-button-neutral-fg)}._neutral_c94w9_111:hover:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-neutral-bg-hover)}._neutral_c94w9_111:active:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-neutral-bg-active)}._neutralSubtle_c94w9_123{background:var(--psi-button-neutral-subtle-bg);color:var(--psi-button-neutral-subtle-fg)}._neutralSubtle_c94w9_123:hover:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-neutral-subtle-bg-hover)}._neutralSubtle_c94w9_123:active:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-neutral-subtle-bg-active)}._ghost_c94w9_135{background:var(--psi-button-ghost-bg);color:var(--psi-button-ghost-fg)}._ghost_c94w9_135:hover:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-ghost-bg-hover)}._ghost_c94w9_135:active:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-ghost-bg-active)}._danger_c94w9_147{background:var(--psi-button-danger-bg);color:var(--psi-button-danger-fg)}._danger_c94w9_147:hover:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-danger-bg-hover)}._danger_c94w9_147:active:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-danger-bg-active)}._dangerSubtle_c94w9_159{background:var(--psi-button-danger-subtle-bg);color:var(--psi-button-danger-subtle-fg)}._dangerSubtle_c94w9_159:hover:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-danger-subtle-bg-hover)}._dangerSubtle_c94w9_159:active:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-danger-subtle-bg-active)}._outline_c94w9_171{background:var(--psi-button-outline-bg);color:var(--psi-button-outline-fg);border:1px solid var(--psi-button-outline-border)}._outline_c94w9_171:hover:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-outline-bg-hover);color:var(--psi-button-outline-fg-hover);border-color:transparent}._outline_c94w9_171:active:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-outline-bg-active)}._iconButton_129ca_3{display:inline-flex;align-items:center;justify-content:center;border:none;border-radius:var(--psi-button-radius);cursor:pointer;padding:0;text-decoration:none;-webkit-user-select:none;user-select:none;transition:background-color var(--psi-duration-150) var(--psi-ease-standard),color var(--psi-duration-150) var(--psi-ease-standard)}._iconButton_129ca_3:focus-visible{outline:2px solid var(--psi-button-focus-ring);outline-offset:2px}._iconButton_129ca_3:disabled{opacity:.4;cursor:not-allowed}._iconButton_129ca_3._disabled_129ca_29{opacity:.4;cursor:not-allowed;pointer-events:none}._size24_129ca_37{width:var(--psi-button-24-height);height:var(--psi-button-24-height)}._size32_129ca_42{width:var(--psi-button-32-height);height:var(--psi-button-32-height)}._size40_129ca_47{width:var(--psi-button-40-height);height:var(--psi-button-40-height)}._size48_129ca_52{width:var(--psi-button-48-height);height:var(--psi-button-48-height)}._accent_129ca_60{background:var(--psi-button-accent-bg);color:var(--psi-button-accent-fg)}._accent_129ca_60:hover:not(:disabled):not(._disabled_129ca_29){background:var(--psi-button-accent-bg-hover)}._accent_129ca_60:active:not(:disabled):not(._disabled_129ca_29){background:var(--psi-button-accent-bg-active)}._neutral_129ca_72{background:var(--psi-button-neutral-bg);color:var(--psi-button-neutral-fg)}._neutral_129ca_72:hover:not(:disabled):not(._disabled_129ca_29){background:var(--psi-button-neutral-bg-hover)}._neutral_129ca_72:active:not(:disabled):not(._disabled_129ca_29){background:var(--psi-button-neutral-bg-active)}._ghost_129ca_84{background:var(--psi-button-ghost-bg);color:var(--psi-button-ghost-fg)}._ghost_129ca_84:hover:not(:disabled):not(._disabled_129ca_29){background:var(--psi-button-ghost-bg-hover)}._ghost_129ca_84:active:not(:disabled):not(._disabled_129ca_29){background:var(--psi-button-ghost-bg-active)}._danger_129ca_96{background:var(--psi-button-danger-bg);color:var(--psi-button-danger-fg)}._danger_129ca_96:hover:not(:disabled):not(._disabled_129ca_29){background:var(--psi-button-danger-bg-hover)}._danger_129ca_96:active:not(:disabled):not(._disabled_129ca_29){background:var(--psi-button-danger-bg-active)}._card_1btb3_1{background:var(--psi-card-bg);border:1px solid var(--psi-card-border);border-radius:var(--psi-card-radius);overflow:hidden}._stacked_1btb3_8{display:flex;flex-direction:column;gap:var(--psi-card-gap)}._featured_1btb3_10{display:grid;grid-template-columns:1.6fr 1fr;gap:var(--psi-card-gap);align-items:start}@media(max-width:960px){._featured_1btb3_10{grid-template-columns:1fr}}._media_1btb3_22{min-width:0}._body_1btb3_23{min-width:0;display:flex;flex-direction:column;gap:var(--psi-card-gap);padding:var(--psi-card-gap)}._hoverLift_1btb3_31{transition:transform var(--psi-duration-350) var(--psi-ease-standard)}._hoverLift_1btb3_31:hover{transform:translateY(-6px)}._panel_jn8w4_1{background:var(--psi-panel-bg);border:1px solid var(--psi-panel-border);border-radius:var(--psi-panel-radius);padding:var(--psi-panel-padding)}._padding16_jn8w4_8{padding:var(--psi-space-16)}._navbar_1gyow_1{font:var(--psi-text-14-20-medium);position:sticky;top:0;z-index:var(--psi-z-nav);background:var(--psi-navbar-bg);backdrop-filter:blur(var(--psi-navbar-blur));-webkit-backdrop-filter:blur(var(--psi-navbar-blur));border-bottom:1px solid var(--psi-navbar-border)}._inner_1gyow_12{display:flex;align-items:center;gap:var(--psi-space-24);height:var(--psi-navbar-height)}._brand_1gyow_19{display:flex;align-items:center}._links_1gyow_20{display:flex;align-items:center;gap:var(--psi-space-20);margin-inline-start:auto}._links_1gyow_20 a,._brand_1gyow_19 a{text-decoration:none;transition:color var(--psi-duration-150) var(--psi-ease-soft)}._links_1gyow_20 a{color:var(--psi-navbar-link-fg)}._links_1gyow_20 a:hover,._brand_1gyow_19 a{color:var(--psi-navbar-link-fg-hover)}._links_1gyow_20 a:focus-visible,._brand_1gyow_19 a:focus-visible{outline:2px solid var(--psi-navbar-focus-ring);outline-offset:2px}._actions_1gyow_50{display:flex;align-items:center;gap:var(--psi-space-8)}._frame_et4fc_1{overflow:hidden}._frame_et4fc_1>img,._frame_et4fc_1>video{width:100%;height:100%;object-fit:cover;display:block}._field_lplye_1{display:grid;gap:var(--psi-field-gap);border:0;margin:0;padding:0;min-width:0}._label_lplye_10{font:var(--psi-text-14-20-medium);color:var(--psi-field-label-fg);padding:0}._marker_lplye_16{color:var(--psi-field-marker-fg)}._message_lplye_20{margin:0;font:var(--psi-text-12-16-regular);color:var(--psi-field-message-fg)}._invalid_lplye_26 ._message_lplye_20{color:var(--psi-field-error-fg)}._dialog_h5f0e_2{padding:0;border:1px solid var(--psi-dialog-border);border-radius:var(--psi-dialog-radius);background:var(--psi-dialog-bg);color:var(--psi-dialog-fg);max-width:calc(100vw - var(--psi-space-32))}._dialog_h5f0e_2::backdrop{background:var(--psi-dialog-backdrop)}._dialog_h5f0e_2[open]::backdrop{animation:_backdrop-enter_h5f0e_1 var(--psi-duration-200) var(--psi-ease-soft)}@keyframes _backdrop-enter_h5f0e_1{0%{opacity:0}}._center_h5f0e_32[open]{animation:_dialog-enter_h5f0e_1 var(--psi-duration-200) var(--psi-ease-soft)}@keyframes _dialog-enter_h5f0e_1{0%{opacity:0;transform:scale(.97)}}._inlineStart_h5f0e_40,._inlineEnd_h5f0e_41{box-sizing:border-box;margin-block:0;height:100dvh;max-height:100dvh;max-width:100vw;border-radius:0}._inlineEnd_h5f0e_41{margin-inline-start:auto;margin-inline-end:0;border-start-start-radius:var(--psi-dialog-radius);border-end-start-radius:var(--psi-dialog-radius)}._inlineStart_h5f0e_40{margin-inline-start:0;margin-inline-end:auto;border-start-end-radius:var(--psi-dialog-radius);border-end-end-radius:var(--psi-dialog-radius)}._inlineEnd_h5f0e_41[open]{animation:_drawer-enter-end_h5f0e_1 var(--psi-duration-200) var(--psi-ease-soft)}._inlineStart_h5f0e_40[open]{animation:_drawer-enter-start_h5f0e_1 var(--psi-duration-200) var(--psi-ease-soft)}@keyframes _drawer-enter-end_h5f0e_1{0%{transform:translate(100%)}}@keyframes _drawer-enter-start_h5f0e_1{0%{transform:translate(-100%)}}._w400_h5f0e_90{width:400px}._w560_h5f0e_91{width:560px}._w720_h5f0e_92{width:720px}._panel_h5f0e_94{display:grid;gap:var(--psi-space-16);padding:var(--psi-space-24)}._inlineStart_h5f0e_40 ._panel_h5f0e_94,._inlineEnd_h5f0e_41 ._panel_h5f0e_94{height:100%;overflow-y:auto;align-content:start}._header_h5f0e_114{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--psi-space-12)}._title_h5f0e_121{margin:0;font:var(--psi-text-18-28-medium);color:var(--psi-dialog-title-fg)}._footer_h5f0e_127{display:flex;justify-content:flex-end;gap:var(--psi-space-8)}._input_hpei8_3{display:block;width:100%;border:1px solid var(--psi-input-border);border-radius:var(--psi-input-radius);background:var(--psi-input-bg);color:var(--psi-input-fg);font-family:var(--psi-font-sans);transition:border-color var(--psi-duration-150) var(--psi-ease-standard),outline var(--psi-duration-150) var(--psi-ease-standard)}._input_hpei8_3::placeholder{color:var(--psi-input-placeholder)}._input_hpei8_3:hover:not(:disabled):not(:focus){border-color:var(--psi-input-border-hover)}._input_hpei8_3:focus{outline:2px solid var(--psi-input-focus-ring);outline-offset:-1px;border-color:transparent}._input_hpei8_3:disabled{opacity:.4;cursor:not-allowed}._size24_hpei8_37{height:var(--psi-input-24-height);padding-inline:var(--psi-input-24-padding-inline);font:var(--psi-input-24-font)}._size32_hpei8_43{height:var(--psi-input-32-height);padding-inline:var(--psi-input-32-padding-inline);font:var(--psi-input-32-font)}._size40_hpei8_49{height:var(--psi-input-40-height);padding-inline:var(--psi-input-40-padding-inline);font:var(--psi-input-40-font)}._size48_hpei8_55{height:var(--psi-input-48-height);padding-inline:var(--psi-input-48-padding-inline);font:var(--psi-input-48-font)}._error_hpei8_63{border-color:var(--psi-input-border-error)}._error_hpei8_63:hover:not(:disabled):not(:focus){border-color:var(--psi-input-border-error)}._select_4x9ck_3{display:block;width:100%;-webkit-appearance:none;-moz-appearance:none;appearance:none;border:1px solid var(--psi-select-border);border-radius:var(--psi-select-radius);background-color:var(--psi-select-bg);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23666' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");background-repeat:no-repeat;color:var(--psi-select-fg);font-family:var(--psi-font-sans);cursor:pointer;transition:border-color var(--psi-duration-150) var(--psi-ease-standard),outline var(--psi-duration-150) var(--psi-ease-standard)}._select_4x9ck_3:hover:not(:disabled):not(:focus){border-color:var(--psi-select-border-hover)}._select_4x9ck_3:focus{outline:2px solid var(--psi-select-focus-ring);outline-offset:-1px;border-color:transparent}._select_4x9ck_3:disabled{opacity:.4;cursor:not-allowed}._size24_4x9ck_37{height:var(--psi-select-24-height);padding-inline-start:var(--psi-select-24-padding-inline);padding-inline-end:var(--psi-select-24-padding-inline-end);background-position:right var(--psi-select-24-chevron-offset) center;font:var(--psi-select-24-font)}._size32_4x9ck_45{height:var(--psi-select-32-height);padding-inline-start:var(--psi-select-32-padding-inline);padding-inline-end:var(--psi-select-32-padding-inline-end);background-position:right var(--psi-select-32-chevron-offset) center;font:var(--psi-select-32-font)}._size40_4x9ck_53{height:var(--psi-select-40-height);padding-inline-start:var(--psi-select-40-padding-inline);padding-inline-end:var(--psi-select-40-padding-inline-end);background-position:right var(--psi-select-40-chevron-offset) center;font:var(--psi-select-40-font)}._size48_4x9ck_61{height:var(--psi-select-48-height);padding-inline-start:var(--psi-select-48-padding-inline);padding-inline-end:var(--psi-select-48-padding-inline-end);background-position:right var(--psi-select-48-chevron-offset) center;font:var(--psi-select-48-font)}._error_4x9ck_71{border-color:var(--psi-select-border-error)}._error_4x9ck_71:hover:not(:disabled):not(:focus){border-color:var(--psi-select-border-error)}._label_1v4vv_3{display:inline-flex;align-items:center;gap:var(--psi-space-8);cursor:pointer;-webkit-user-select:none;user-select:none;font:var(--psi-text-14-20-regular);color:var(--psi-checkbox-fg)}._disabled_1v4vv_13{opacity:.4;cursor:not-allowed}._input_1v4vv_20{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}._indicator_1v4vv_34{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;flex-shrink:0;border:1px solid var(--psi-checkbox-box-border);border-radius:var(--psi-checkbox-box-radius);background:var(--psi-checkbox-box-bg);transition:background-color var(--psi-duration-150) var(--psi-ease-standard),border-color var(--psi-duration-150) var(--psi-ease-standard)}._input_1v4vv_20:checked+._indicator_1v4vv_34{background:var(--psi-checkbox-box-bg-checked);border-color:var(--psi-checkbox-box-border-checked)}._input_1v4vv_20:checked+._indicator_1v4vv_34:after{content:"";display:block;width:4px;height:8px;color:var(--psi-checkbox-check-fg);border:solid currentColor;border-width:0 2px 2px 0;transform:rotate(45deg) translate(-.5px,-.5px)}._input_1v4vv_20:focus-visible+._indicator_1v4vv_34{outline:2px solid var(--psi-checkbox-focus-ring);outline-offset:2px}._text_1v4vv_75{color:var(--psi-checkbox-fg)}._label_jru2c_3{display:inline-flex;align-items:center;gap:var(--psi-space-8);cursor:pointer;-webkit-user-select:none;user-select:none;font:var(--psi-text-14-20-regular);color:var(--psi-switch-fg)}._disabled_jru2c_13{opacity:.4;cursor:not-allowed}._input_jru2c_20{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}._track_jru2c_34{position:relative;display:inline-flex;align-items:center;width:36px;height:20px;flex-shrink:0;border-radius:var(--psi-radius-full);background:var(--psi-switch-track-bg);transition:background-color var(--psi-duration-150) var(--psi-ease-standard)}._input_jru2c_20:checked+._track_jru2c_34{background:var(--psi-switch-track-bg-checked)}._input_jru2c_20:focus-visible+._track_jru2c_34{outline:2px solid var(--psi-switch-focus-ring);outline-offset:2px}._thumb_jru2c_58{position:absolute;width:16px;height:16px;border-radius:var(--psi-radius-full);background:var(--psi-switch-thumb-bg);left:2px;transition:left var(--psi-duration-150) var(--psi-ease-standard);box-shadow:0 1px 2px #00000029}._input_jru2c_20:checked+._track_jru2c_34 ._thumb_jru2c_58{left:18px}._text_jru2c_75{color:var(--psi-switch-fg)}._tag_3oysa_3{display:inline-flex;align-items:center;gap:var(--psi-space-4);height:24px;padding-inline:var(--psi-space-8);border-radius:var(--psi-radius-full);font:var(--psi-text-12-16-medium);white-space:nowrap;-webkit-user-select:none;user-select:none}._dismiss_3oysa_17{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;background:none;color:inherit;cursor:pointer;opacity:.7;margin-inline-start:var(--psi-space-2);margin-inline-end:calc(-1 * var(--psi-space-4));border-radius:var(--psi-radius-full)}._dismiss_3oysa_17:hover{opacity:1}._dismiss_3oysa_17:focus-visible{outline:2px solid var(--psi-tag-focus-ring);outline-offset:2px}._neutral_3oysa_43{background:var(--psi-tag-neutral-bg);color:var(--psi-tag-neutral-fg)}._accent_3oysa_48{background:var(--psi-tag-accent-bg);color:var(--psi-tag-accent-fg)}._success_3oysa_53{background:var(--psi-tag-success-bg);color:var(--psi-tag-success-fg)}._warning_3oysa_58{background:var(--psi-tag-warning-bg);color:var(--psi-tag-warning-fg)}._danger_3oysa_63{background:var(--psi-tag-danger-bg);color:var(--psi-tag-danger-fg)}._neutralSubtle_3oysa_70{background:var(--psi-tag-neutral-subtle-bg);color:var(--psi-tag-neutral-subtle-fg)}._accentSubtle_3oysa_75{background:var(--psi-tag-accent-subtle-bg);color:var(--psi-tag-accent-subtle-fg)}._successSubtle_3oysa_80{background:var(--psi-tag-success-subtle-bg);color:var(--psi-tag-success-subtle-fg)}._warningSubtle_3oysa_85{background:var(--psi-tag-warning-subtle-bg);color:var(--psi-tag-warning-subtle-fg)}._dangerSubtle_3oysa_90{background:var(--psi-tag-danger-subtle-bg);color:var(--psi-tag-danger-subtle-fg)}._wrapper_1eu6z_3{position:relative;display:inline-flex}._tooltip_1eu6z_10{position:absolute;z-index:var(--psi-z-tooltip);padding:var(--psi-space-4) var(--psi-space-8);border-radius:var(--psi-tooltip-radius);background:var(--psi-tooltip-bg);color:var(--psi-tooltip-fg);font:var(--psi-text-12-16-regular);white-space:nowrap;pointer-events:none}._top_1eu6z_24{bottom:100%;left:50%;transform:translate(-50%);margin-bottom:var(--psi-space-4)}._bottom_1eu6z_31{top:100%;left:50%;transform:translate(-50%);margin-top:var(--psi-space-4)}._left_1eu6z_38{right:100%;top:50%;transform:translateY(-50%);margin-right:var(--psi-space-4)}._right_1eu6z_45{left:100%;top:50%;transform:translateY(-50%);margin-left:var(--psi-space-4)}._toolbar_1i848_1{display:flex;flex-wrap:wrap;align-items:center}._gap8_1i848_7{gap:var(--psi-space-8)}._gap12_1i848_8{gap:var(--psi-space-12)}._gap16_1i848_9{gap:var(--psi-space-16)}._trigger_vpa0u_4{display:inline-flex;width:fit-content}._menu_vpa0u_16{min-width:180px;margin:var(--psi-space-4);padding:var(--psi-space-4);border:1px solid var(--psi-menu-border);border-radius:var(--psi-menu-radius);background:var(--psi-menu-bg);color:var(--psi-menu-fg);font:var(--psi-text-14-20-regular)}._menu_vpa0u_16:not([data-open]):not(:popover-open){display:none}._item_vpa0u_37{display:flex;width:100%;align-items:center;padding:var(--psi-space-8) var(--psi-space-12);border:0;border-radius:var(--psi-radius-6);background:var(--psi-menu-item-bg);color:var(--psi-menu-item-fg);font:inherit;text-align:start;cursor:pointer}._item_vpa0u_37:hover{background:var(--psi-menu-item-bg-hover)}._item_vpa0u_37:active{background:var(--psi-menu-item-bg-active)}._item_vpa0u_37[data-variant=danger]{color:var(--psi-menu-item-fg-danger)}._item_vpa0u_37[aria-disabled=true]{color:var(--psi-menu-item-fg-disabled);cursor:default}._item_vpa0u_37[aria-disabled=true]:hover{background:var(--psi-menu-item-bg)}._separator_vpa0u_74{height:0;margin:var(--psi-space-4) 0;border-top:1px solid var(--psi-menu-separator-border)}@supports (anchor-name: --x){._menu_vpa0u_16{position:fixed;margin:var(--psi-space-4);position-try-fallbacks:flip-block,flip-inline,flip-block flip-inline}._menu_vpa0u_16[data-placement=bottom-start]{position-area:block-end span-inline-end}._menu_vpa0u_16[data-placement=bottom-end]{position-area:block-end span-inline-start}._menu_vpa0u_16[data-placement=top-start]{position-area:block-start span-inline-end}._menu_vpa0u_16[data-placement=top-end]{position-area:block-start span-inline-start}}._table_146hc_3{width:100%;border-collapse:collapse;background:var(--psi-table-bg);color:var(--psi-table-fg);font:var(--psi-text-14-20-regular);border:1px solid var(--psi-table-border);border-radius:var(--psi-table-radius)}._headerCell_146hc_13{color:var(--psi-table-header-fg);font:var(--psi-text-14-20-medium);text-align:start}._cell_146hc_19,._headerCell_146hc_13{border-bottom:1px solid var(--psi-table-cell-border)}._cell_146hc_19[data-numeric],._headerCell_146hc_13[data-numeric]{text-align:end;font-variant-numeric:var(--psi-font-variant-numeric)}._sortButton_146hc_30{display:inline-flex;align-items:center;gap:var(--psi-space-4);padding:0;border:0;background:none;color:inherit;font:inherit;cursor:pointer}._sortButton_146hc_30:focus-visible{outline:2px solid var(--psi-table-focus-ring);outline-offset:2px}._sortIndicator_146hc_47{color:var(--psi-table-sort-indicator-fg)}._table_146hc_3[data-size="32"] ._cell_146hc_19,._table_146hc_3[data-size="32"] ._headerCell_146hc_13{height:var(--psi-table-32-row-height);padding-inline:var(--psi-table-32-cell-padding-x)}._table_146hc_3[data-size="40"] ._cell_146hc_19,._table_146hc_3[data-size="40"] ._headerCell_146hc_13{height:var(--psi-table-40-row-height);padding-inline:var(--psi-table-40-cell-padding-x)}._table_146hc_3[data-size="48"] ._cell_146hc_19,._table_146hc_3[data-size="48"] ._headerCell_146hc_13{height:var(--psi-table-48-row-height);padding-inline:var(--psi-table-48-cell-padding-x)}._table_146hc_3[data-size] ._selectCell_146hc_79{width:1%;padding-inline-end:0}._body_146hc_86 ._row_146hc_89:hover{background:var(--psi-table-row-bg-hover)}._body_146hc_86 ._row_146hc_89[data-selected]:hover,._row_146hc_89[data-selected]{background:var(--psi-table-row-bg-selected)}._row_146hc_89{background:var(--psi-table-row-bg)}._sticky_146hc_111 ._headerCell_146hc_13{position:sticky;inset-block-start:0;z-index:var(--psi-z-sticky);background:var(--psi-table-bg);box-shadow:inset 0 -1px 0 var(--psi-table-cell-border)}._nav_166vs_6{display:flex;align-items:center;gap:var(--psi-space-4)}._ellipsis_166vs_12{padding-inline:var(--psi-space-4)}._toast_7toru_10{pointer-events:auto;display:grid;grid-template-columns:auto 1fr auto auto;align-items:center;gap:var(--psi-space-12);min-width:280px;max-width:420px;padding:var(--psi-space-12) var(--psi-space-16);border:1px solid var(--psi-toast-border);border-radius:var(--psi-toast-radius);background:var(--psi-toast-bg);color:var(--psi-toast-fg);font:var(--psi-text-14-20-regular);animation:_toast-enter_7toru_1 var(--psi-duration-200) var(--psi-ease-soft)}@keyframes _toast-enter_7toru_1{0%{opacity:0;transform:translateY(8px)}}._icon_7toru_39{display:inline-flex;align-items:center}._iconNeutral_7toru_44{color:var(--psi-toast-icon-fg-neutral)}._iconSuccess_7toru_45{color:var(--psi-toast-icon-fg-success)}._iconWarning_7toru_46{color:var(--psi-toast-icon-fg-warning)}._iconDanger_7toru_47{color:var(--psi-toast-icon-fg-danger)}._body_7toru_51{min-width:0}._action_7toru_55{display:inline-flex}._srOnly_7toru_60{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}._region_7toru_74{position:fixed;inset:auto;margin:0;padding:var(--psi-space-16);border:0;background:none;overflow:visible;pointer-events:none;display:flex;flex-direction:column;gap:var(--psi-space-8);width:max-content;max-width:100%}._region_7toru_74[data-placement=top-start]{top:0;left:0;align-items:flex-start}._region_7toru_74[data-placement=top-end]{top:0;right:0;align-items:flex-end}._region_7toru_74[data-placement=bottom-start]{bottom:0;left:0;align-items:flex-start}._region_7toru_74[data-placement=bottom-end]{bottom:0;right:0;align-items:flex-end}._region_7toru_74[data-placement^=bottom] ._live_7toru_104{flex-direction:column-reverse}._live_7toru_104{display:flex;flex-direction:column;gap:var(--psi-space-8)}
1
+ ._button_c94w9_3{display:inline-flex;align-items:center;justify-content:center;border:none;border-radius:var(--psi-button-radius);cursor:pointer;font-family:var(--psi-font-sans);white-space:nowrap;text-decoration:none;-webkit-user-select:none;user-select:none;transition:background-color var(--psi-duration-150) var(--psi-ease-standard),color var(--psi-duration-150) var(--psi-ease-standard)}._button_c94w9_3:focus-visible{outline:2px solid var(--psi-button-focus-ring);outline-offset:2px}._button_c94w9_3:disabled{opacity:.4;cursor:not-allowed}._button_c94w9_3._disabled_c94w9_30{opacity:.4;cursor:not-allowed;pointer-events:none}._size24_c94w9_44{height:var(--psi-button-24-height);padding-inline:var(--psi-button-24-padding-inline);gap:var(--psi-button-24-gap);font:var(--psi-button-font, var(--psi-button-24-font))}._size24_c94w9_44:has(>svg:first-child){padding-inline-start:var(--psi-button-24-padding-inline-icon)}._size32_c94w9_54{height:var(--psi-button-32-height);padding-inline:var(--psi-button-32-padding-inline);gap:var(--psi-button-32-gap);font:var(--psi-button-font, var(--psi-button-32-font))}._size32_c94w9_54:has(>svg:first-child){padding-inline-start:var(--psi-button-32-padding-inline-icon)}._size40_c94w9_64{height:var(--psi-button-40-height);padding-inline:var(--psi-button-40-padding-inline);gap:var(--psi-button-40-gap);font:var(--psi-button-font, var(--psi-button-40-font))}._size40_c94w9_64:has(>svg:first-child){padding-inline-start:var(--psi-button-40-padding-inline-icon)}._size48_c94w9_74{height:var(--psi-button-48-height);padding-inline:var(--psi-button-48-padding-inline);gap:var(--psi-button-48-gap);font:var(--psi-button-font, var(--psi-button-48-font))}._size48_c94w9_74:has(>svg:first-child){padding-inline-start:var(--psi-button-48-padding-inline-icon)}._accent_c94w9_87{background:var(--psi-button-accent-bg);color:var(--psi-button-accent-fg)}._accent_c94w9_87:hover:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-accent-bg-hover)}._accent_c94w9_87:active:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-accent-bg-active)}._accentSubtle_c94w9_99{background:var(--psi-button-accent-subtle-bg);color:var(--psi-button-accent-subtle-fg)}._accentSubtle_c94w9_99:hover:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-accent-subtle-bg-hover)}._accentSubtle_c94w9_99:active:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-accent-subtle-bg-active)}._neutral_c94w9_111{background:var(--psi-button-neutral-bg);color:var(--psi-button-neutral-fg)}._neutral_c94w9_111:hover:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-neutral-bg-hover)}._neutral_c94w9_111:active:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-neutral-bg-active)}._neutralSubtle_c94w9_123{background:var(--psi-button-neutral-subtle-bg);color:var(--psi-button-neutral-subtle-fg)}._neutralSubtle_c94w9_123:hover:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-neutral-subtle-bg-hover)}._neutralSubtle_c94w9_123:active:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-neutral-subtle-bg-active)}._ghost_c94w9_135{background:var(--psi-button-ghost-bg);color:var(--psi-button-ghost-fg)}._ghost_c94w9_135:hover:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-ghost-bg-hover)}._ghost_c94w9_135:active:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-ghost-bg-active)}._danger_c94w9_147{background:var(--psi-button-danger-bg);color:var(--psi-button-danger-fg)}._danger_c94w9_147:hover:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-danger-bg-hover)}._danger_c94w9_147:active:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-danger-bg-active)}._dangerSubtle_c94w9_159{background:var(--psi-button-danger-subtle-bg);color:var(--psi-button-danger-subtle-fg)}._dangerSubtle_c94w9_159:hover:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-danger-subtle-bg-hover)}._dangerSubtle_c94w9_159:active:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-danger-subtle-bg-active)}._outline_c94w9_171{background:var(--psi-button-outline-bg);color:var(--psi-button-outline-fg);border:1px solid var(--psi-button-outline-border)}._outline_c94w9_171:hover:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-outline-bg-hover);color:var(--psi-button-outline-fg-hover);border-color:transparent}._outline_c94w9_171:active:not(:disabled):not(._disabled_c94w9_30){background:var(--psi-button-outline-bg-active)}._iconButton_129ca_3{display:inline-flex;align-items:center;justify-content:center;border:none;border-radius:var(--psi-button-radius);cursor:pointer;padding:0;text-decoration:none;-webkit-user-select:none;user-select:none;transition:background-color var(--psi-duration-150) var(--psi-ease-standard),color var(--psi-duration-150) var(--psi-ease-standard)}._iconButton_129ca_3:focus-visible{outline:2px solid var(--psi-button-focus-ring);outline-offset:2px}._iconButton_129ca_3:disabled{opacity:.4;cursor:not-allowed}._iconButton_129ca_3._disabled_129ca_29{opacity:.4;cursor:not-allowed;pointer-events:none}._size24_129ca_37{width:var(--psi-button-24-height);height:var(--psi-button-24-height)}._size32_129ca_42{width:var(--psi-button-32-height);height:var(--psi-button-32-height)}._size40_129ca_47{width:var(--psi-button-40-height);height:var(--psi-button-40-height)}._size48_129ca_52{width:var(--psi-button-48-height);height:var(--psi-button-48-height)}._accent_129ca_60{background:var(--psi-button-accent-bg);color:var(--psi-button-accent-fg)}._accent_129ca_60:hover:not(:disabled):not(._disabled_129ca_29){background:var(--psi-button-accent-bg-hover)}._accent_129ca_60:active:not(:disabled):not(._disabled_129ca_29){background:var(--psi-button-accent-bg-active)}._neutral_129ca_72{background:var(--psi-button-neutral-bg);color:var(--psi-button-neutral-fg)}._neutral_129ca_72:hover:not(:disabled):not(._disabled_129ca_29){background:var(--psi-button-neutral-bg-hover)}._neutral_129ca_72:active:not(:disabled):not(._disabled_129ca_29){background:var(--psi-button-neutral-bg-active)}._ghost_129ca_84{background:var(--psi-button-ghost-bg);color:var(--psi-button-ghost-fg)}._ghost_129ca_84:hover:not(:disabled):not(._disabled_129ca_29){background:var(--psi-button-ghost-bg-hover)}._ghost_129ca_84:active:not(:disabled):not(._disabled_129ca_29){background:var(--psi-button-ghost-bg-active)}._danger_129ca_96{background:var(--psi-button-danger-bg);color:var(--psi-button-danger-fg)}._danger_129ca_96:hover:not(:disabled):not(._disabled_129ca_29){background:var(--psi-button-danger-bg-hover)}._danger_129ca_96:active:not(:disabled):not(._disabled_129ca_29){background:var(--psi-button-danger-bg-active)}._card_1btb3_1{background:var(--psi-card-bg);border:1px solid var(--psi-card-border);border-radius:var(--psi-card-radius);overflow:hidden}._stacked_1btb3_8{display:flex;flex-direction:column;gap:var(--psi-card-gap)}._featured_1btb3_10{display:grid;grid-template-columns:1.6fr 1fr;gap:var(--psi-card-gap);align-items:start}@media(max-width:960px){._featured_1btb3_10{grid-template-columns:1fr}}._media_1btb3_22{min-width:0}._body_1btb3_23{min-width:0;display:flex;flex-direction:column;gap:var(--psi-card-gap);padding:var(--psi-card-gap)}._hoverLift_1btb3_31{transition:transform var(--psi-duration-350) var(--psi-ease-standard)}._hoverLift_1btb3_31:hover{transform:translateY(-6px)}._panel_jn8w4_1{background:var(--psi-panel-bg);border:1px solid var(--psi-panel-border);border-radius:var(--psi-panel-radius);padding:var(--psi-panel-padding)}._padding16_jn8w4_8{padding:var(--psi-space-16)}._navbar_1gyow_1{font:var(--psi-text-14-20-medium);position:sticky;top:0;z-index:var(--psi-z-nav);background:var(--psi-navbar-bg);backdrop-filter:blur(var(--psi-navbar-blur));-webkit-backdrop-filter:blur(var(--psi-navbar-blur));border-bottom:1px solid var(--psi-navbar-border)}._inner_1gyow_12{display:flex;align-items:center;gap:var(--psi-space-24);height:var(--psi-navbar-height)}._brand_1gyow_19{display:flex;align-items:center}._links_1gyow_20{display:flex;align-items:center;gap:var(--psi-space-20);margin-inline-start:auto}._links_1gyow_20 a,._brand_1gyow_19 a{text-decoration:none;transition:color var(--psi-duration-150) var(--psi-ease-soft)}._links_1gyow_20 a{color:var(--psi-navbar-link-fg)}._links_1gyow_20 a:hover,._brand_1gyow_19 a{color:var(--psi-navbar-link-fg-hover)}._links_1gyow_20 a:focus-visible,._brand_1gyow_19 a:focus-visible{outline:2px solid var(--psi-navbar-focus-ring);outline-offset:2px}._actions_1gyow_50{display:flex;align-items:center;gap:var(--psi-space-8)}._frame_et4fc_1{overflow:hidden}._frame_et4fc_1>img,._frame_et4fc_1>video{width:100%;height:100%;object-fit:cover;display:block}._field_lplye_1{display:grid;gap:var(--psi-field-gap);border:0;margin:0;padding:0;min-width:0}._label_lplye_10{font:var(--psi-text-14-20-medium);color:var(--psi-field-label-fg);padding:0}._marker_lplye_16{color:var(--psi-field-marker-fg)}._message_lplye_20{margin:0;font:var(--psi-text-12-16-regular);color:var(--psi-field-message-fg)}._invalid_lplye_26 ._message_lplye_20{color:var(--psi-field-error-fg)}._dialog_h5f0e_2{padding:0;border:1px solid var(--psi-dialog-border);border-radius:var(--psi-dialog-radius);background:var(--psi-dialog-bg);color:var(--psi-dialog-fg);max-width:calc(100vw - var(--psi-space-32))}._dialog_h5f0e_2::backdrop{background:var(--psi-dialog-backdrop)}._dialog_h5f0e_2[open]::backdrop{animation:_backdrop-enter_h5f0e_1 var(--psi-duration-200) var(--psi-ease-soft)}@keyframes _backdrop-enter_h5f0e_1{0%{opacity:0}}._center_h5f0e_32[open]{animation:_dialog-enter_h5f0e_1 var(--psi-duration-200) var(--psi-ease-soft)}@keyframes _dialog-enter_h5f0e_1{0%{opacity:0;transform:scale(.97)}}._inlineStart_h5f0e_40,._inlineEnd_h5f0e_41{box-sizing:border-box;margin-block:0;height:100dvh;max-height:100dvh;max-width:100vw;border-radius:0}._inlineEnd_h5f0e_41{margin-inline-start:auto;margin-inline-end:0;border-start-start-radius:var(--psi-dialog-radius);border-end-start-radius:var(--psi-dialog-radius)}._inlineStart_h5f0e_40{margin-inline-start:0;margin-inline-end:auto;border-start-end-radius:var(--psi-dialog-radius);border-end-end-radius:var(--psi-dialog-radius)}._inlineEnd_h5f0e_41[open]{animation:_drawer-enter-end_h5f0e_1 var(--psi-duration-200) var(--psi-ease-soft)}._inlineStart_h5f0e_40[open]{animation:_drawer-enter-start_h5f0e_1 var(--psi-duration-200) var(--psi-ease-soft)}@keyframes _drawer-enter-end_h5f0e_1{0%{transform:translate(100%)}}@keyframes _drawer-enter-start_h5f0e_1{0%{transform:translate(-100%)}}._w400_h5f0e_90{width:400px}._w560_h5f0e_91{width:560px}._w720_h5f0e_92{width:720px}._panel_h5f0e_94{display:grid;gap:var(--psi-space-16);padding:var(--psi-space-24)}._inlineStart_h5f0e_40 ._panel_h5f0e_94,._inlineEnd_h5f0e_41 ._panel_h5f0e_94{height:100%;overflow-y:auto;align-content:start}._header_h5f0e_114{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--psi-space-12)}._title_h5f0e_121{margin:0;font:var(--psi-text-18-28-medium);color:var(--psi-dialog-title-fg)}._footer_h5f0e_127{display:flex;justify-content:flex-end;gap:var(--psi-space-8)}._input_hpei8_3{display:block;width:100%;border:1px solid var(--psi-input-border);border-radius:var(--psi-input-radius);background:var(--psi-input-bg);color:var(--psi-input-fg);font-family:var(--psi-font-sans);transition:border-color var(--psi-duration-150) var(--psi-ease-standard),outline var(--psi-duration-150) var(--psi-ease-standard)}._input_hpei8_3::placeholder{color:var(--psi-input-placeholder)}._input_hpei8_3:hover:not(:disabled):not(:focus){border-color:var(--psi-input-border-hover)}._input_hpei8_3:focus{outline:2px solid var(--psi-input-focus-ring);outline-offset:-1px;border-color:transparent}._input_hpei8_3:disabled{opacity:.4;cursor:not-allowed}._size24_hpei8_37{height:var(--psi-input-24-height);padding-inline:var(--psi-input-24-padding-inline);font:var(--psi-input-24-font)}._size32_hpei8_43{height:var(--psi-input-32-height);padding-inline:var(--psi-input-32-padding-inline);font:var(--psi-input-32-font)}._size40_hpei8_49{height:var(--psi-input-40-height);padding-inline:var(--psi-input-40-padding-inline);font:var(--psi-input-40-font)}._size48_hpei8_55{height:var(--psi-input-48-height);padding-inline:var(--psi-input-48-padding-inline);font:var(--psi-input-48-font)}._error_hpei8_63{border-color:var(--psi-input-border-error)}._error_hpei8_63:hover:not(:disabled):not(:focus){border-color:var(--psi-input-border-error)}._select_4x9ck_3{display:block;width:100%;-webkit-appearance:none;-moz-appearance:none;appearance:none;border:1px solid var(--psi-select-border);border-radius:var(--psi-select-radius);background-color:var(--psi-select-bg);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23666' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");background-repeat:no-repeat;color:var(--psi-select-fg);font-family:var(--psi-font-sans);cursor:pointer;transition:border-color var(--psi-duration-150) var(--psi-ease-standard),outline var(--psi-duration-150) var(--psi-ease-standard)}._select_4x9ck_3:hover:not(:disabled):not(:focus){border-color:var(--psi-select-border-hover)}._select_4x9ck_3:focus{outline:2px solid var(--psi-select-focus-ring);outline-offset:-1px;border-color:transparent}._select_4x9ck_3:disabled{opacity:.4;cursor:not-allowed}._size24_4x9ck_37{height:var(--psi-select-24-height);padding-inline-start:var(--psi-select-24-padding-inline);padding-inline-end:var(--psi-select-24-padding-inline-end);background-position:right var(--psi-select-24-chevron-offset) center;font:var(--psi-select-24-font)}._size32_4x9ck_45{height:var(--psi-select-32-height);padding-inline-start:var(--psi-select-32-padding-inline);padding-inline-end:var(--psi-select-32-padding-inline-end);background-position:right var(--psi-select-32-chevron-offset) center;font:var(--psi-select-32-font)}._size40_4x9ck_53{height:var(--psi-select-40-height);padding-inline-start:var(--psi-select-40-padding-inline);padding-inline-end:var(--psi-select-40-padding-inline-end);background-position:right var(--psi-select-40-chevron-offset) center;font:var(--psi-select-40-font)}._size48_4x9ck_61{height:var(--psi-select-48-height);padding-inline-start:var(--psi-select-48-padding-inline);padding-inline-end:var(--psi-select-48-padding-inline-end);background-position:right var(--psi-select-48-chevron-offset) center;font:var(--psi-select-48-font)}._error_4x9ck_71{border-color:var(--psi-select-border-error)}._error_4x9ck_71:hover:not(:disabled):not(:focus){border-color:var(--psi-select-border-error)}._label_1v4vv_3{display:inline-flex;align-items:center;gap:var(--psi-space-8);cursor:pointer;-webkit-user-select:none;user-select:none;font:var(--psi-text-14-20-regular);color:var(--psi-checkbox-fg)}._disabled_1v4vv_13{opacity:.4;cursor:not-allowed}._input_1v4vv_20{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}._indicator_1v4vv_34{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;flex-shrink:0;border:1px solid var(--psi-checkbox-box-border);border-radius:var(--psi-checkbox-box-radius);background:var(--psi-checkbox-box-bg);transition:background-color var(--psi-duration-150) var(--psi-ease-standard),border-color var(--psi-duration-150) var(--psi-ease-standard)}._input_1v4vv_20:checked+._indicator_1v4vv_34{background:var(--psi-checkbox-box-bg-checked);border-color:var(--psi-checkbox-box-border-checked)}._input_1v4vv_20:checked+._indicator_1v4vv_34:after{content:"";display:block;width:4px;height:8px;color:var(--psi-checkbox-check-fg);border:solid currentColor;border-width:0 2px 2px 0;transform:rotate(45deg) translate(-.5px,-.5px)}._input_1v4vv_20:focus-visible+._indicator_1v4vv_34{outline:2px solid var(--psi-checkbox-focus-ring);outline-offset:2px}._text_1v4vv_75{color:var(--psi-checkbox-fg)}._label_jru2c_3{display:inline-flex;align-items:center;gap:var(--psi-space-8);cursor:pointer;-webkit-user-select:none;user-select:none;font:var(--psi-text-14-20-regular);color:var(--psi-switch-fg)}._disabled_jru2c_13{opacity:.4;cursor:not-allowed}._input_jru2c_20{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}._track_jru2c_34{position:relative;display:inline-flex;align-items:center;width:36px;height:20px;flex-shrink:0;border-radius:var(--psi-radius-full);background:var(--psi-switch-track-bg);transition:background-color var(--psi-duration-150) var(--psi-ease-standard)}._input_jru2c_20:checked+._track_jru2c_34{background:var(--psi-switch-track-bg-checked)}._input_jru2c_20:focus-visible+._track_jru2c_34{outline:2px solid var(--psi-switch-focus-ring);outline-offset:2px}._thumb_jru2c_58{position:absolute;width:16px;height:16px;border-radius:var(--psi-radius-full);background:var(--psi-switch-thumb-bg);left:2px;transition:left var(--psi-duration-150) var(--psi-ease-standard);box-shadow:0 1px 2px #00000029}._input_jru2c_20:checked+._track_jru2c_34 ._thumb_jru2c_58{left:18px}._text_jru2c_75{color:var(--psi-switch-fg)}._tag_3oysa_3{display:inline-flex;align-items:center;gap:var(--psi-space-4);height:24px;padding-inline:var(--psi-space-8);border-radius:var(--psi-radius-full);font:var(--psi-text-12-16-medium);white-space:nowrap;-webkit-user-select:none;user-select:none}._dismiss_3oysa_17{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;background:none;color:inherit;cursor:pointer;opacity:.7;margin-inline-start:var(--psi-space-2);margin-inline-end:calc(-1 * var(--psi-space-4));border-radius:var(--psi-radius-full)}._dismiss_3oysa_17:hover{opacity:1}._dismiss_3oysa_17:focus-visible{outline:2px solid var(--psi-tag-focus-ring);outline-offset:2px}._neutral_3oysa_43{background:var(--psi-tag-neutral-bg);color:var(--psi-tag-neutral-fg)}._accent_3oysa_48{background:var(--psi-tag-accent-bg);color:var(--psi-tag-accent-fg)}._success_3oysa_53{background:var(--psi-tag-success-bg);color:var(--psi-tag-success-fg)}._warning_3oysa_58{background:var(--psi-tag-warning-bg);color:var(--psi-tag-warning-fg)}._danger_3oysa_63{background:var(--psi-tag-danger-bg);color:var(--psi-tag-danger-fg)}._neutralSubtle_3oysa_70{background:var(--psi-tag-neutral-subtle-bg);color:var(--psi-tag-neutral-subtle-fg)}._accentSubtle_3oysa_75{background:var(--psi-tag-accent-subtle-bg);color:var(--psi-tag-accent-subtle-fg)}._successSubtle_3oysa_80{background:var(--psi-tag-success-subtle-bg);color:var(--psi-tag-success-subtle-fg)}._warningSubtle_3oysa_85{background:var(--psi-tag-warning-subtle-bg);color:var(--psi-tag-warning-subtle-fg)}._dangerSubtle_3oysa_90{background:var(--psi-tag-danger-subtle-bg);color:var(--psi-tag-danger-subtle-fg)}._wrapper_1eu6z_3{position:relative;display:inline-flex}._tooltip_1eu6z_10{position:absolute;z-index:var(--psi-z-tooltip);padding:var(--psi-space-4) var(--psi-space-8);border-radius:var(--psi-tooltip-radius);background:var(--psi-tooltip-bg);color:var(--psi-tooltip-fg);font:var(--psi-text-12-16-regular);white-space:nowrap;pointer-events:none}._top_1eu6z_24{bottom:100%;left:50%;transform:translate(-50%);margin-bottom:var(--psi-space-4)}._bottom_1eu6z_31{top:100%;left:50%;transform:translate(-50%);margin-top:var(--psi-space-4)}._left_1eu6z_38{right:100%;top:50%;transform:translateY(-50%);margin-right:var(--psi-space-4)}._right_1eu6z_45{left:100%;top:50%;transform:translateY(-50%);margin-left:var(--psi-space-4)}._toolbar_1i848_1{display:flex;flex-wrap:wrap;align-items:center}._gap8_1i848_7{gap:var(--psi-space-8)}._gap12_1i848_8{gap:var(--psi-space-12)}._gap16_1i848_9{gap:var(--psi-space-16)}._trigger_vpa0u_4{display:inline-flex;width:fit-content}._menu_vpa0u_16{min-width:180px;margin:var(--psi-space-4);padding:var(--psi-space-4);border:1px solid var(--psi-menu-border);border-radius:var(--psi-menu-radius);background:var(--psi-menu-bg);color:var(--psi-menu-fg);font:var(--psi-text-14-20-regular)}._menu_vpa0u_16:not([data-open]):not(:popover-open){display:none}._item_vpa0u_37{display:flex;width:100%;align-items:center;padding:var(--psi-space-8) var(--psi-space-12);border:0;border-radius:var(--psi-radius-6);background:var(--psi-menu-item-bg);color:var(--psi-menu-item-fg);font:inherit;text-align:start;cursor:pointer}._item_vpa0u_37:hover{background:var(--psi-menu-item-bg-hover)}._item_vpa0u_37:active{background:var(--psi-menu-item-bg-active)}._item_vpa0u_37[data-variant=danger]{color:var(--psi-menu-item-fg-danger)}._item_vpa0u_37[aria-disabled=true]{color:var(--psi-menu-item-fg-disabled);cursor:default}._item_vpa0u_37[aria-disabled=true]:hover{background:var(--psi-menu-item-bg)}._separator_vpa0u_74{height:0;margin:var(--psi-space-4) 0;border-top:1px solid var(--psi-menu-separator-border)}@supports (anchor-name: --x){._menu_vpa0u_16{position:fixed;margin:var(--psi-space-4);position-try-fallbacks:flip-block,flip-inline,flip-block flip-inline}._menu_vpa0u_16[data-placement=bottom-start]{position-area:block-end span-inline-end}._menu_vpa0u_16[data-placement=bottom-end]{position-area:block-end span-inline-start}._menu_vpa0u_16[data-placement=top-start]{position-area:block-start span-inline-end}._menu_vpa0u_16[data-placement=top-end]{position-area:block-start span-inline-start}}._table_146hc_3{width:100%;border-collapse:collapse;background:var(--psi-table-bg);color:var(--psi-table-fg);font:var(--psi-text-14-20-regular);border:1px solid var(--psi-table-border);border-radius:var(--psi-table-radius)}._headerCell_146hc_13{color:var(--psi-table-header-fg);font:var(--psi-text-14-20-medium);text-align:start}._cell_146hc_19,._headerCell_146hc_13{border-bottom:1px solid var(--psi-table-cell-border)}._cell_146hc_19[data-numeric],._headerCell_146hc_13[data-numeric]{text-align:end;font-variant-numeric:var(--psi-font-variant-numeric)}._sortButton_146hc_30{display:inline-flex;align-items:center;gap:var(--psi-space-4);padding:0;border:0;background:none;color:inherit;font:inherit;cursor:pointer}._sortButton_146hc_30:focus-visible{outline:2px solid var(--psi-table-focus-ring);outline-offset:2px}._sortIndicator_146hc_47{color:var(--psi-table-sort-indicator-fg)}._table_146hc_3[data-size="32"] ._cell_146hc_19,._table_146hc_3[data-size="32"] ._headerCell_146hc_13{height:var(--psi-table-32-row-height);padding-inline:var(--psi-table-32-cell-padding-x)}._table_146hc_3[data-size="40"] ._cell_146hc_19,._table_146hc_3[data-size="40"] ._headerCell_146hc_13{height:var(--psi-table-40-row-height);padding-inline:var(--psi-table-40-cell-padding-x)}._table_146hc_3[data-size="48"] ._cell_146hc_19,._table_146hc_3[data-size="48"] ._headerCell_146hc_13{height:var(--psi-table-48-row-height);padding-inline:var(--psi-table-48-cell-padding-x)}._table_146hc_3[data-size] ._selectCell_146hc_79{width:1%;padding-inline-end:0}._body_146hc_86 ._row_146hc_89:hover{background:var(--psi-table-row-bg-hover)}._body_146hc_86 ._row_146hc_89[data-selected]:hover,._row_146hc_89[data-selected]{background:var(--psi-table-row-bg-selected)}._row_146hc_89{background:var(--psi-table-row-bg)}._sticky_146hc_111 ._headerCell_146hc_13{position:sticky;inset-block-start:0;z-index:var(--psi-z-sticky);background:var(--psi-table-bg);box-shadow:inset 0 -1px 0 var(--psi-table-cell-border)}._nav_166vs_6{display:flex;align-items:center;gap:var(--psi-space-4)}._ellipsis_166vs_12{padding-inline:var(--psi-space-4)}._toast_7toru_10{pointer-events:auto;display:grid;grid-template-columns:auto 1fr auto auto;align-items:center;gap:var(--psi-space-12);min-width:280px;max-width:420px;padding:var(--psi-space-12) var(--psi-space-16);border:1px solid var(--psi-toast-border);border-radius:var(--psi-toast-radius);background:var(--psi-toast-bg);color:var(--psi-toast-fg);font:var(--psi-text-14-20-regular);animation:_toast-enter_7toru_1 var(--psi-duration-200) var(--psi-ease-soft)}@keyframes _toast-enter_7toru_1{0%{opacity:0;transform:translateY(8px)}}._icon_7toru_39{display:inline-flex;align-items:center}._iconNeutral_7toru_44{color:var(--psi-toast-icon-fg-neutral)}._iconSuccess_7toru_45{color:var(--psi-toast-icon-fg-success)}._iconWarning_7toru_46{color:var(--psi-toast-icon-fg-warning)}._iconDanger_7toru_47{color:var(--psi-toast-icon-fg-danger)}._body_7toru_51{min-width:0}._action_7toru_55{display:inline-flex}._srOnly_7toru_60{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}._region_7toru_74{position:fixed;inset:auto;margin:0;padding:var(--psi-space-16);border:0;background:none;overflow:visible;pointer-events:none;display:flex;flex-direction:column;gap:var(--psi-space-8);width:max-content;max-width:100%}._region_7toru_74[data-placement=top-start]{top:0;left:0;align-items:flex-start}._region_7toru_74[data-placement=top-end]{top:0;right:0;align-items:flex-end}._region_7toru_74[data-placement=bottom-start]{bottom:0;left:0;align-items:flex-start}._region_7toru_74[data-placement=bottom-end]{bottom:0;right:0;align-items:flex-end}._region_7toru_74[data-placement^=bottom] ._live_7toru_104{flex-direction:column-reverse}._live_7toru_104{display:flex;flex-direction:column;gap:var(--psi-space-8)}._tabs_17vw6_8{display:flex;flex-direction:column;gap:var(--psi-space-16)}._tabs_17vw6_8[data-orientation=vertical]{flex-direction:row;gap:var(--psi-space-24)}._list_17vw6_21{display:flex;flex-wrap:wrap;gap:var(--psi-space-4);border-bottom:1px solid var(--psi-tabs-list-border)}._tabs_17vw6_8[data-orientation=vertical] ._list_17vw6_21{flex-direction:column;flex-wrap:nowrap;border-bottom:none;border-inline-start:1px solid var(--psi-tabs-list-border)}._tab_17vw6_8{position:relative;-webkit-appearance:none;-moz-appearance:none;appearance:none;border:0;background:none;cursor:pointer;height:var(--psi-tabs-40-height);padding-inline:var(--psi-tabs-40-padding-x);font:var(--psi-text-14-20-medium);color:var(--psi-tabs-fg);transition:color var(--psi-duration-150) var(--psi-ease-standard)}._tab_17vw6_8:hover:not([aria-disabled=true]){background:var(--psi-tabs-bg-hover)}._tab_17vw6_8:focus-visible{outline:2px solid var(--psi-tabs-focus-ring);outline-offset:-2px}._tab_17vw6_8[aria-disabled=true]{color:var(--psi-tabs-fg-disabled);cursor:default}._tab_17vw6_8[aria-selected=true]{color:var(--psi-tabs-fg-selected)}._tab_17vw6_8[aria-selected=true]:after{content:"";position:absolute;inset-inline:0;bottom:-1px;height:2px;background:var(--psi-tabs-indicator)}._tabs_17vw6_8[data-orientation=vertical] ._tab_17vw6_8{text-align:start}._tabs_17vw6_8[data-orientation=vertical] ._tab_17vw6_8[aria-selected=true]:after{inset-inline:auto;inset-block:0;inset-inline-start:-1px;width:2px;height:auto}._panel_17vw6_97{min-width:0}._panel_17vw6_97:focus-visible{outline:2px solid var(--psi-tabs-focus-ring);outline-offset:2px}
package/docs/Tab.md ADDED
@@ -0,0 +1,39 @@
1
+ <!-- Generated by emit-docs.ts — do not edit. -->
2
+ # Tab
3
+
4
+ One `role="tab"` (D67). Renders a real `<button>`. `disabled` sets `aria-disabled` rather than the `disabled` attribute, so the tab stays discoverable to assistive tech while being skipped by roving navigation — the same choice MenuItem made in D53.
5
+
6
+ ## Props
7
+
8
+ | Prop | Type | Default | Required | Description |
9
+ |---|---|---|---|---|
10
+ | `value` | `string` | — | yes | Pairs this tab with the `TabPanel` of the same value. |
11
+ | `children` | `ReactNode` | — | yes | Label — keep it to one or two words. |
12
+ | `disabled` | `boolean` | false | no | Skipped by arrow navigation and not selectable, but still announced. |
13
+ | `className` | `string` | — | no | Additional CSS class name(s) merged onto the component's root element. |
14
+ | `ref` | `Ref<HTMLButtonElement>` | — | no | Forwarded ref to the underlying `<button>`. |
15
+
16
+ ## Keyboard & assistive tech
17
+
18
+ | Keys | Behavior |
19
+ |---|---|
20
+ | Enter / Space | Activates (native button behavior); arrow keys already select on focus. |
21
+
22
+ Renders a real <button> with role="tab", aria-selected and aria-controls. disabled sets aria-disabled (not the disabled attribute) so the tab stays discoverable to assistive tech while being skipped by roving navigation and refused for selection — the same choice MenuItem made in D53.
23
+
24
+ ## Theming
25
+
26
+ This component has no `--psi-tab-*` tokens — its styling binds scale tokens only, so there is nothing component-scoped to override. Theme changes reach it through the semantic tokens of its children and surroundings.
27
+
28
+
29
+
30
+ ## Rules
31
+
32
+ - One accent per visual group; everything else neutral or ghost.
33
+ - danger only for actions with real consequences.
34
+ - Sizes are px numbers (24|32|40|48), never S/M/L.
35
+ - Typography tokens are --psi-text-{size}-{lineHeight}-{weight}.
36
+ - Override component tokens (--psi-{component}-*), not semantic tokens, for one-off theming.
37
+ - --psi-button-font overrides button typography across all sizes (documented D34 override; ember → mono).
38
+ - Wrap labeled form controls in Field — label association, description/error line, aria-describedby and aria-invalid come wired; don't hand-roll label+message rows.
39
+ - Use Dialog for blocking modal flows — title/footer slots, dismissible gate; danger stays on the footer Buttons, one accent per group.
@@ -0,0 +1,40 @@
1
+ <!-- Generated by emit-docs.ts — do not edit. -->
2
+ # TabList
3
+
4
+ The `role="tablist"` container (D67). Owns the roving tabindex: the whole list is one Tab stop, and the arrow keys move between tabs inside it.
5
+
6
+ ## Props
7
+
8
+ | Prop | Type | Default | Required | Description |
9
+ |---|---|---|---|---|
10
+ | `aria-label` | `string` | — | no | Accessible name for the tab set. Declared here rather than inherited so docgen keeps it in the manifest (D60) — a tablist without a name is announced as an unlabelled group, and the prop would otherwise be invisible to an agent reading the manifest. |
11
+ | `children` | `ReactNode` | — | yes | One `Tab` per view. |
12
+ | `className` | `string` | — | no | Additional CSS class name(s) merged onto the component's root element. |
13
+ | `ref` | `Ref<HTMLDivElement>` | — | no | Forwarded ref to the tablist element. |
14
+
15
+ ## Keyboard & assistive tech
16
+
17
+ | Keys | Behavior |
18
+ |---|---|
19
+ | Arrow Left / Arrow Right | Horizontal orientation: moves to the previous/next enabled tab, wrapping at both ends. Selection follows focus (automatic activation). |
20
+ | Arrow Up / Arrow Down | Vertical orientation: the same, on the block axis. The cross-axis arrows are ignored rather than swallowed. |
21
+ | Home / End | Jumps to the first or last enabled tab. |
22
+
23
+ Renders role="tablist" with aria-orientation. Requires an accessible name — pass aria-label, which is promoted onto its own props interface (D60) so the manifest shows it. Owns the roving tabindex: exactly one tab is in the page tab order at a time.
24
+
25
+ ## Theming
26
+
27
+ This component has no `--psi-tab-list-*` tokens — its styling binds scale tokens only, so there is nothing component-scoped to override. Theme changes reach it through the semantic tokens of its children and surroundings.
28
+
29
+
30
+
31
+ ## Rules
32
+
33
+ - One accent per visual group; everything else neutral or ghost.
34
+ - danger only for actions with real consequences.
35
+ - Sizes are px numbers (24|32|40|48), never S/M/L.
36
+ - Typography tokens are --psi-text-{size}-{lineHeight}-{weight}.
37
+ - Override component tokens (--psi-{component}-*), not semantic tokens, for one-off theming.
38
+ - --psi-button-font overrides button typography across all sizes (documented D34 override; ember → mono).
39
+ - Wrap labeled form controls in Field — label association, description/error line, aria-describedby and aria-invalid come wired; don't hand-roll label+message rows.
40
+ - Use Dialog for blocking modal flows — title/footer slots, dismissible gate; danger stays on the footer Buttons, one accent per group.
@@ -0,0 +1,38 @@
1
+ <!-- Generated by emit-docs.ts — do not edit. -->
2
+ # TabPanel
3
+
4
+ One `role="tabpanel"` (D67). Every panel renders; unselected ones carry `hidden`. Returning null instead would leave `aria-controls` on every unselected tab pointing at an element that does not exist, and would throw away DOM state — a half-filled form in a panel would lose its values on each tab switch. The cost is a heavier DOM when panels are large; the answer to that is rendering less inside the panel, not unmounting it behind the consumer's back. `tabIndex={0}` because a panel whose content has no focusable element would otherwise be unreachable by keyboard.
5
+
6
+ ## Props
7
+
8
+ | Prop | Type | Default | Required | Description |
9
+ |---|---|---|---|---|
10
+ | `value` | `string` | — | yes | Pairs this panel with the `Tab` of the same value. |
11
+ | `children` | `ReactNode` | — | yes | The view for this tab. Rendered even when unselected, then `hidden`. |
12
+ | `className` | `string` | — | no | Additional CSS class name(s) merged onto the component's root element. |
13
+ | `ref` | `Ref<HTMLDivElement>` | — | no | Forwarded ref to the panel element. |
14
+
15
+ ## Keyboard & assistive tech
16
+
17
+ | Keys | Behavior |
18
+ |---|---|
19
+ | Tab | The panel itself is a tab stop (tabIndex=0), so a panel whose content has no focusable element is still reachable. |
20
+
21
+ Renders role="tabpanel" with aria-labelledby pointing back at its tab. Unselected panels stay in the DOM with `hidden`.
22
+
23
+ ## Theming
24
+
25
+ This component has no `--psi-tab-panel-*` tokens — its styling binds scale tokens only, so there is nothing component-scoped to override. Theme changes reach it through the semantic tokens of its children and surroundings.
26
+
27
+
28
+
29
+ ## Rules
30
+
31
+ - One accent per visual group; everything else neutral or ghost.
32
+ - danger only for actions with real consequences.
33
+ - Sizes are px numbers (24|32|40|48), never S/M/L.
34
+ - Typography tokens are --psi-text-{size}-{lineHeight}-{weight}.
35
+ - Override component tokens (--psi-{component}-*), not semantic tokens, for one-off theming.
36
+ - --psi-button-font overrides button typography across all sizes (documented D34 override; ember → mono).
37
+ - Wrap labeled form controls in Field — label association, description/error line, aria-describedby and aria-invalid come wired; don't hand-roll label+message rows.
38
+ - Use Dialog for blocking modal flows — title/footer slots, dismissible gate; danger stays on the footer Buttons, one accent per group.
package/docs/Tabs.md ADDED
@@ -0,0 +1,40 @@
1
+ <!-- Generated by emit-docs.ts — do not edit. -->
2
+ # Tabs
3
+
4
+ Tab set root (D67) — holds no selection state of its own, following D50, D53 and D62: `value` and `onValueChange` are required and there is no `defaultValue`. Values are strings rather than indices because an index breaks the moment a tab is inserted, and real tab sets map to ids. `Tab` and `TabPanel` pair by value, so their source order need not match. Activation is automatic: arrow keys move focus and selection together. That is the APG default where panel content is already available, and manual activation is deliberately not offered as a mode — a consumer for whom activating a panel is expensive already controls what the panel renders.
5
+
6
+ ## Props
7
+
8
+ | Prop | Type | Default | Required | Description |
9
+ |---|---|---|---|---|
10
+ | `value` | `string` | — | yes | Controlled selected tab, matched against each `Tab`/`TabPanel` value. |
11
+ | `onValueChange` | `(value: string) => void` | — | yes | Fires with the newly selected value; the consumer flips `value`. |
12
+ | `orientation` | `"horizontal" \| "vertical"` | horizontal | no | Axis of the tab list, which also picks the arrow keys. |
13
+ | `children` | `ReactNode` | — | yes | A `TabList` and one `TabPanel` per tab. |
14
+ | `className` | `string` | — | no | Additional CSS class name(s) merged onto the component's root element. |
15
+ | `ref` | `Ref<HTMLDivElement>` | — | no | Forwarded ref to the wrapper `<div>`. |
16
+
17
+ ## Keyboard & assistive tech
18
+
19
+ | Keys | Behavior |
20
+ |---|---|
21
+ | Tab | Enters the tab list at its selected tab (one stop for the whole list), then moves on to the active panel. |
22
+
23
+ Controlled-only (D67): value and onValueChange are required and Tabs never selects itself. Tab and TabPanel pair by string value, not index, so their source order need not match. Every panel renders and unselected ones carry `hidden`, so aria-controls always resolves and panel DOM state survives a switch.
24
+
25
+ ## Theming
26
+
27
+ Override `--psi-tabs-*` custom properties at any scope; interactive states derive automatically (L - 0.04 hover, L - 0.08 active).
28
+
29
+
30
+
31
+ ## Rules
32
+
33
+ - One accent per visual group; everything else neutral or ghost.
34
+ - danger only for actions with real consequences.
35
+ - Sizes are px numbers (24|32|40|48), never S/M/L.
36
+ - Typography tokens are --psi-text-{size}-{lineHeight}-{weight}.
37
+ - Override component tokens (--psi-{component}-*), not semantic tokens, for one-off theming.
38
+ - --psi-button-font overrides button typography across all sizes (documented D34 override; ember → mono).
39
+ - Wrap labeled form controls in Field — label association, description/error line, aria-describedby and aria-invalid come wired; don't hand-roll label+message rows.
40
+ - Use Dialog for blocking modal flows — title/footer slots, dismissible gate; danger stays on the footer Buttons, one accent per group.
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # @handamade/psi-react
2
2
 
3
- 28 React 19 components consuming @handamade/psi-tokens. Zero runtime deps, CSS Modules, ref-as-prop.
3
+ 32 React 19 components consuming @handamade/psi-tokens. Zero runtime deps, CSS Modules, ref-as-prop.
4
4
 
5
5
  ## Machine-readable artifacts
6
6
  - dist/manifest.json: full component/prop inventory with types and defaults
@@ -59,6 +59,13 @@
59
59
  - `<ToastProvider limit={3} duration={5000} actionDuration={10000}>` owns the queue, the timers and the single region — the library's **one stateful container** (D65), and opt-in. Inside it, `const toast = useToast()` gives `show({variant, message, action}) => id`, `dismiss(id)` and `clear()`. Timers pause while the pointer or focus is in the region and resume with the time remaining (WCAG 2.2.1); toasts carrying an action get the longer lifetime.
60
60
  - **Do not hand-roll a queue or a `setTimeout`** — that is what `ToastProvider` is for. Do not render a bare `<Toast>` outside a `ToastRegion`: it would announce nothing. `useToast()` throws outside a provider rather than silently doing nothing.
61
61
 
62
+ ## Tabs family (4)
63
+ - `<Tabs value={v} onValueChange={setV} orientation="horizontal|vertical">` is **controlled-only** (D67): `value` and `onValueChange` are required, there is no `defaultValue`, and Tabs never selects itself. Compose `<TabList aria-label="…">` with one `<Tab value>` each, then one `<TabPanel value>` per tab.
64
+ - **Values are strings, not indices**, and `Tab`/`TabPanel` pair by value — their source order need not match, though keeping it matching reads better.
65
+ - **Activation is automatic**: arrow keys move focus and selection together. There is no manual-activation mode. Arrows follow the orientation's axis (Left/Right horizontal, Up/Down vertical) with wrap; Home/End jump to the first/last enabled tab; disabled tabs are skipped and use `aria-disabled`, not the `disabled` attribute.
66
+ - **Every panel renders; unselected ones get `hidden`.** So `aria-controls` always resolves and panel DOM state survives a switch. If a panel is expensive, render less *inside* it (`{selected ? <Heavy/> : null}`) rather than omitting the `TabPanel` — omitting it leaves the tab's `aria-controls` pointing at nothing.
67
+ - `TabList` requires `aria-label`. The active panel is itself a tab stop, so a panel with no focusable content is still reachable.
68
+
62
69
  ## Compositions (recipes for what has no component yet)
63
70
 
64
71
  - Form field: wrap the label, control, and message with `<Field label description error required>{control}</Field>` — label association (htmlFor/id), the description/error message line (swaps to error, aria-live="polite"), and aria-describedby/aria-invalid wiring onto a nested Input/Select are automatic (D49). `group` mode renders fieldset/legend for self-labeled controls (Checkbox/Switch). Standalone Input/Select used outside Field still need `aria-invalid` and `aria-describedby` paired manually. Spacing: `psi-gap-24` between fields, `psi-gap-12` for toggle groups, `psi-gap-8` for button rows and label+Tag rows.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@handamade/psi-react",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -40,7 +40,7 @@
40
40
  "tsx": "^4.22.4",
41
41
  "vite": "^6.0.0",
42
42
  "vitest": "^3.2.6",
43
- "@handamade/psi-tokens": "0.12.0"
43
+ "@handamade/psi-tokens": "0.13.0"
44
44
  },
45
45
  "scripts": {
46
46
  "build": "vite build && tsc -p tsconfig.build.json && tsx scripts/emit-manifest.ts && tsx scripts/emit-patterns.ts && tsx scripts/emit-docs.ts",