@pearpages/pulp-react 0.2.2 → 0.3.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.
Files changed (156) hide show
  1. package/README.md +14 -0
  2. package/dist/accordion.js +1 -1
  3. package/dist/alert.css +18 -0
  4. package/dist/alert.css.map +1 -1
  5. package/dist/alert.js +4 -4
  6. package/dist/avatar.css +46 -0
  7. package/dist/avatar.css.map +1 -0
  8. package/dist/avatar.d.ts +41 -0
  9. package/dist/avatar.js +5 -0
  10. package/dist/avatar.js.map +1 -0
  11. package/dist/badge.css +37 -0
  12. package/dist/badge.css.map +1 -1
  13. package/dist/badge.d.ts +22 -5
  14. package/dist/badge.js +2 -1
  15. package/dist/button.css +18 -0
  16. package/dist/button.css.map +1 -1
  17. package/dist/button.d.ts +11 -2
  18. package/dist/button.js +2 -2
  19. package/dist/calendar.js +1 -1
  20. package/dist/card.js +2 -2
  21. package/dist/checkbox.js +4 -4
  22. package/dist/chip.css +95 -0
  23. package/dist/chip.css.map +1 -0
  24. package/dist/chip.d.ts +43 -0
  25. package/dist/chip.js +4 -0
  26. package/dist/chip.js.map +1 -0
  27. package/dist/{chunk-N6E2E2OY.js → chunk-2NVC3C7K.js} +3 -3
  28. package/dist/{chunk-N6E2E2OY.js.map → chunk-2NVC3C7K.js.map} +1 -1
  29. package/dist/{chunk-MF66ICX6.js → chunk-56RSH7GQ.js} +3 -3
  30. package/dist/{chunk-MF66ICX6.js.map → chunk-56RSH7GQ.js.map} +1 -1
  31. package/dist/chunk-5TYCIBLW.js +27 -0
  32. package/dist/chunk-5TYCIBLW.js.map +1 -0
  33. package/dist/chunk-7O6FTCZL.js +101 -0
  34. package/dist/chunk-7O6FTCZL.js.map +1 -0
  35. package/dist/{chunk-N4C3NQIN.js → chunk-A7TMFEQ2.js} +3 -3
  36. package/dist/{chunk-N4C3NQIN.js.map → chunk-A7TMFEQ2.js.map} +1 -1
  37. package/dist/{chunk-2NSQPN7S.js → chunk-AZAI4OVR.js} +3 -3
  38. package/dist/{chunk-2NSQPN7S.js.map → chunk-AZAI4OVR.js.map} +1 -1
  39. package/dist/{chunk-DVZ46GQN.js → chunk-CLE2QGCU.js} +3 -3
  40. package/dist/{chunk-DVZ46GQN.js.map → chunk-CLE2QGCU.js.map} +1 -1
  41. package/dist/{chunk-5TTYVQ6Q.js → chunk-DE67ECD7.js} +5 -3
  42. package/dist/chunk-DE67ECD7.js.map +1 -0
  43. package/dist/{chunk-BAPNIZDV.js → chunk-F2YI5N4L.js} +3 -3
  44. package/dist/chunk-F2YI5N4L.js.map +1 -0
  45. package/dist/{chunk-U3N3G2UA.js → chunk-FFCEY76B.js} +10 -5
  46. package/dist/chunk-FFCEY76B.js.map +1 -0
  47. package/dist/{chunk-XWPXW46D.js → chunk-GWTHITZO.js} +3 -3
  48. package/dist/{chunk-XWPXW46D.js.map → chunk-GWTHITZO.js.map} +1 -1
  49. package/dist/chunk-HDC2IQV2.js +34 -0
  50. package/dist/chunk-HDC2IQV2.js.map +1 -0
  51. package/dist/{chunk-VQN5DQP6.js → chunk-HKNIEQ2V.js} +4 -4
  52. package/dist/{chunk-VQN5DQP6.js.map → chunk-HKNIEQ2V.js.map} +1 -1
  53. package/dist/{chunk-LA3YBXCR.js → chunk-HPQ462R5.js} +3 -3
  54. package/dist/{chunk-LA3YBXCR.js.map → chunk-HPQ462R5.js.map} +1 -1
  55. package/dist/chunk-KSLKNWWM.js +28 -0
  56. package/dist/chunk-KSLKNWWM.js.map +1 -0
  57. package/dist/chunk-OKA2PLDV.js +85 -0
  58. package/dist/chunk-OKA2PLDV.js.map +1 -0
  59. package/dist/{chunk-6BIEW4WJ.js → chunk-Q6SYKNZ5.js} +3 -3
  60. package/dist/{chunk-6BIEW4WJ.js.map → chunk-Q6SYKNZ5.js.map} +1 -1
  61. package/dist/chunk-QID6YYFF.js +33 -0
  62. package/dist/chunk-QID6YYFF.js.map +1 -0
  63. package/dist/{chunk-ZVDN3GRI.js → chunk-QJG5ILK5.js} +3 -3
  64. package/dist/{chunk-ZVDN3GRI.js.map → chunk-QJG5ILK5.js.map} +1 -1
  65. package/dist/chunk-QXHSA4QD.js +43 -0
  66. package/dist/chunk-QXHSA4QD.js.map +1 -0
  67. package/dist/chunk-S632WN4W.js +106 -0
  68. package/dist/chunk-S632WN4W.js.map +1 -0
  69. package/dist/{chunk-OFFFRIJG.js → chunk-SDIK6VSC.js} +3 -3
  70. package/dist/{chunk-OFFFRIJG.js.map → chunk-SDIK6VSC.js.map} +1 -1
  71. package/dist/{chunk-M5VL22MY.js → chunk-SXMORGSG.js} +3 -3
  72. package/dist/{chunk-M5VL22MY.js.map → chunk-SXMORGSG.js.map} +1 -1
  73. package/dist/{chunk-Z6SS4G4V.js → chunk-UMK4D234.js} +3 -3
  74. package/dist/{chunk-Z6SS4G4V.js.map → chunk-UMK4D234.js.map} +1 -1
  75. package/dist/{chunk-5PYLJQJX.js → chunk-VS2HPJIX.js} +3 -3
  76. package/dist/{chunk-5PYLJQJX.js.map → chunk-VS2HPJIX.js.map} +1 -1
  77. package/dist/{chunk-NOUUOH4R.js → chunk-WDIFWMKE.js} +3 -3
  78. package/dist/chunk-WDIFWMKE.js.map +1 -0
  79. package/dist/chunk-Y7KC2FDU.js +88 -0
  80. package/dist/chunk-Y7KC2FDU.js.map +1 -0
  81. package/dist/{chunk-OFKSU77Y.js → chunk-ZEVQQQOA.js} +3 -3
  82. package/dist/{chunk-OFKSU77Y.js.map → chunk-ZEVQQQOA.js.map} +1 -1
  83. package/dist/combobox.css +3 -0
  84. package/dist/combobox.css.map +1 -1
  85. package/dist/combobox.js +2 -2
  86. package/dist/component-manifest.json +679 -8
  87. package/dist/date-picker.js +1 -1
  88. package/dist/dialog.js +1 -1
  89. package/dist/divider.css +37 -0
  90. package/dist/divider.css.map +1 -0
  91. package/dist/divider.d.ts +35 -0
  92. package/dist/divider.js +4 -0
  93. package/dist/divider.js.map +1 -0
  94. package/dist/empty-state.css +175 -0
  95. package/dist/empty-state.css.map +1 -0
  96. package/dist/empty-state.d.ts +40 -0
  97. package/dist/empty-state.js +7 -0
  98. package/dist/empty-state.js.map +1 -0
  99. package/dist/field.js +2 -2
  100. package/dist/icon-button.css +18 -0
  101. package/dist/icon-button.css.map +1 -1
  102. package/dist/icon-button.js +3 -3
  103. package/dist/index.css +489 -0
  104. package/dist/index.css.map +1 -1
  105. package/dist/index.d.ts +8 -2
  106. package/dist/index.js +38 -32
  107. package/dist/link.css +35 -0
  108. package/dist/link.css.map +1 -0
  109. package/dist/link.d.ts +37 -0
  110. package/dist/link.js +5 -0
  111. package/dist/link.js.map +1 -0
  112. package/dist/listbox.js +1 -1
  113. package/dist/menu.js +2 -2
  114. package/dist/pagination.css +18 -0
  115. package/dist/pagination.css.map +1 -1
  116. package/dist/pagination.js +4 -4
  117. package/dist/picker.css +3 -0
  118. package/dist/picker.css.map +1 -1
  119. package/dist/picker.js +2 -2
  120. package/dist/popover.js +2 -2
  121. package/dist/radio.js +3 -3
  122. package/dist/segmented-control.css +111 -0
  123. package/dist/segmented-control.css.map +1 -0
  124. package/dist/segmented-control.d.ts +90 -0
  125. package/dist/segmented-control.js +5 -0
  126. package/dist/segmented-control.js.map +1 -0
  127. package/dist/select.js +3 -3
  128. package/dist/sheet.css +28 -0
  129. package/dist/sheet.css.map +1 -1
  130. package/dist/sheet.d.ts +9 -2
  131. package/dist/sheet.js +1 -1
  132. package/dist/slider.js +1 -1
  133. package/dist/switch.js +4 -4
  134. package/dist/table.js +1 -1
  135. package/dist/tabs.js +1 -1
  136. package/dist/text-field.css +63 -0
  137. package/dist/text-field.css.map +1 -1
  138. package/dist/text-field.d.ts +15 -2
  139. package/dist/text-field.js +3 -3
  140. package/dist/textarea.js +3 -3
  141. package/dist/toast.css +18 -0
  142. package/dist/toast.css.map +1 -1
  143. package/dist/toast.d.ts +21 -3
  144. package/dist/toast.js +5 -5
  145. package/dist/tooltip.js +2 -2
  146. package/package.json +36 -6
  147. package/dist/chunk-5TTYVQ6Q.js.map +0 -1
  148. package/dist/chunk-737GQSOB.js +0 -19
  149. package/dist/chunk-737GQSOB.js.map +0 -1
  150. package/dist/chunk-BAPNIZDV.js.map +0 -1
  151. package/dist/chunk-IFIQLIA3.js +0 -48
  152. package/dist/chunk-IFIQLIA3.js.map +0 -1
  153. package/dist/chunk-NOUUOH4R.js.map +0 -1
  154. package/dist/chunk-TJJBCS3N.js +0 -28
  155. package/dist/chunk-TJJBCS3N.js.map +0 -1
  156. package/dist/chunk-U3N3G2UA.js.map +0 -1
@@ -36,6 +36,7 @@
36
36
  ],
37
37
  "import": "import { Accordion } from '@pearpages/pulp-react/accordion';",
38
38
  "css": "@pearpages/pulp-react/accordion.css",
39
+ "client": true,
39
40
  "tokens": "--accordion-*",
40
41
  "props": [
41
42
  {
@@ -171,6 +172,7 @@
171
172
  ],
172
173
  "import": "import { Alert } from '@pearpages/pulp-react/alert';",
173
174
  "css": "@pearpages/pulp-react/alert.css",
175
+ "client": false,
174
176
  "tokens": "--alert-*",
175
177
  "props": [
176
178
  {
@@ -243,14 +245,85 @@
243
245
  ],
244
246
  "parts": []
245
247
  },
248
+ {
249
+ "name": "Avatar",
250
+ "description": "A person, as a picture or as initials. The initials take over when no image\nis given and when the image fails to load, so a broken URL never shows a\nbroken-image glyph. Every value comes from `--avatar-*` tokens; the\nbackground is one token for everyone, not a colour computed from the name.",
251
+ "status": "experimental",
252
+ "category": "Utilities",
253
+ "accessibility": "With an image, the `img` carries the name as its `alt`. With initials, the root is `role=\"img\"` named by `alt` or `name`, and the letters are hidden from assistive technology (\"AR\" read aloud means nothing). `alt=\"\"` makes the avatar decorative (`aria-hidden`): use it when the name is written next to it. Not focusable and not interactive; wrap it in a Link or a Button to make it one.",
254
+ "do": [
255
+ "Pass `name` always, even with an image: it is the fallback when the image fails.",
256
+ "Pass `alt=\"\"` when the person's name is visible beside the avatar."
257
+ ],
258
+ "dont": [
259
+ "Colour avatars per person from a hash of the name; the colours will not be tokens and will not pass contrast in both schemes.",
260
+ "Use it for a logo or an illustration; it crops to a circle and falls back to initials."
261
+ ],
262
+ "import": "import { Avatar } from '@pearpages/pulp-react/avatar';",
263
+ "css": "@pearpages/pulp-react/avatar.css",
264
+ "client": true,
265
+ "tokens": "--avatar-*",
266
+ "props": [
267
+ {
268
+ "name": "alt",
269
+ "type": "string",
270
+ "required": false,
271
+ "default": null,
272
+ "description": "Accessible name. Defaults to `name`. Pass `\"\"` next to the visible name, so it is not read twice."
273
+ },
274
+ {
275
+ "name": "children",
276
+ "type": "ReactElement<unknown, string | JSXElementConstructor<any>>",
277
+ "required": false,
278
+ "default": null,
279
+ "description": "An image element to use instead of `img`: a framework's image component\n(`<Avatar name=\"Ana\"><Image src={url} alt=\"\" width={40} height={40} /></Avatar>`).\nIt receives the styles and the error handling; its own `alt` is replaced by the avatar's."
280
+ },
281
+ {
282
+ "name": "name",
283
+ "type": "string",
284
+ "required": false,
285
+ "default": null,
286
+ "description": "The person's name. Its initials show when there is no image or the image fails, and it names the avatar unless `alt` does."
287
+ },
288
+ {
289
+ "name": "ref",
290
+ "type": "Ref<HTMLSpanElement>",
291
+ "required": false,
292
+ "default": null,
293
+ "description": ""
294
+ },
295
+ {
296
+ "name": "size",
297
+ "type": "enum",
298
+ "values": [
299
+ "\"sm\"",
300
+ "\"md\"",
301
+ "\"lg\"",
302
+ "\"xl\""
303
+ ],
304
+ "required": false,
305
+ "default": "md",
306
+ "description": "`sm` and `md` match the control heights, so an avatar lines up with a button."
307
+ },
308
+ {
309
+ "name": "src",
310
+ "type": "string",
311
+ "required": false,
312
+ "default": null,
313
+ "description": "Image URL. Ignored when an image element is passed as the child."
314
+ }
315
+ ],
316
+ "parts": []
317
+ },
246
318
  {
247
319
  "name": "Badge",
248
320
  "description": "A small label for status, counts and tags. Tone and variant resolve\nthrough `--badge-*` onto the status tokens, whose contrast is proven by\nthe token tests. Not interactive: for a removable chip, compose with a\nButton.",
249
321
  "status": "stable",
250
322
  "category": "Feedback",
251
- "accessibility": "Plain text: no role, no interaction. Every tone × variant pair meets WCAG AA, proven by the token tests. Tone is reinforced by the text, not only by colour.",
323
+ "accessibility": "Plain text: no role, no interaction, and deliberately no live region: a badge that is there when the page renders must not be announced as news (put `role=\"status\"` on a wrapper yourself if a count changes while the user is on the page). A `dot` has no visible text, so its `label` is rendered visually hidden; a `count` above `max` reads as \"99+\", followed by the `label` if there is one. Every tone × variant pair meets WCAG AA, proven by the token tests. Tone is reinforced by the text, not only by colour.",
252
324
  "do": [
253
325
  "Keep the text to one or two words.",
326
+ "Give a `dot` a `label` that says what it marks, and a `count` a `label` that says what is counted.",
254
327
  "Pair a leading icon with text, never alone."
255
328
  ],
256
329
  "dont": [
@@ -259,8 +332,16 @@
259
332
  ],
260
333
  "import": "import { Badge } from '@pearpages/pulp-react/badge';",
261
334
  "css": "@pearpages/pulp-react/badge.css",
335
+ "client": false,
262
336
  "tokens": "--badge-*",
263
337
  "props": [
338
+ {
339
+ "name": "count",
340
+ "type": "number",
341
+ "required": false,
342
+ "default": null,
343
+ "description": "A number to show instead of children, clamped to `max` (\"99+\"). Zero or less\nrenders nothing: an empty inbox has no badge."
344
+ },
264
345
  {
265
346
  "name": "icon",
266
347
  "type": "ReactNode",
@@ -268,6 +349,20 @@
268
349
  "default": null,
269
350
  "description": "Leading glyph, decorative."
270
351
  },
352
+ {
353
+ "name": "label",
354
+ "type": "string",
355
+ "required": false,
356
+ "default": null,
357
+ "description": "What the badge means, for assistive technology. Required for `variant=\"dot\"`\n(\"Unread messages\"), where it is the only content. With a `count` it is read\nafter the number: `count={3} label=\"unread\"` is announced as \"3 unread\"."
358
+ },
359
+ {
360
+ "name": "max",
361
+ "type": "number",
362
+ "required": false,
363
+ "default": "99",
364
+ "description": "The highest count shown as is."
365
+ },
271
366
  {
272
367
  "name": "ref",
273
368
  "type": "Ref<HTMLSpanElement>",
@@ -305,11 +400,12 @@
305
400
  "type": "enum",
306
401
  "values": [
307
402
  "\"solid\"",
308
- "\"subtle\""
403
+ "\"subtle\"",
404
+ "\"dot\""
309
405
  ],
310
406
  "required": false,
311
407
  "default": "subtle",
312
- "description": "`solid` fills with the tone; `subtle` tints and uses the tone as text."
408
+ "description": "`solid` fills with the tone; `subtle` tints and uses the tone as text; `dot` is a\nsmall filled circle with no visible text, which needs a `label`."
313
409
  }
314
410
  ],
315
411
  "parts": []
@@ -322,7 +418,8 @@
322
418
  "accessibility": "A native `button` (or the child element with `asChild`). `loading` sets `aria-disabled` and `aria-busy` and keeps focus; only `disabled` removes it from the tab order. Icon-only use warns in development: use IconButton, which requires a name.",
323
419
  "do": [
324
420
  "One `primary` per view.",
325
- "Use `asChild` for links that look like buttons, keeping the `a` semantics."
421
+ "Use `asChild` for links that look like buttons, keeping the `a` semantics.",
422
+ "Use `tone=\"danger\"` for the action that destroys, and say what it destroys in the label (\"Delete place\"), since colour alone must not carry it."
326
423
  ],
327
424
  "dont": [
328
425
  "Use `loading` on a button that is not the one that started the work.",
@@ -330,6 +427,7 @@
330
427
  ],
331
428
  "import": "import { Button } from '@pearpages/pulp-react/button';",
332
429
  "css": "@pearpages/pulp-react/button.css",
430
+ "client": false,
333
431
  "tokens": "--button-*",
334
432
  "props": [
335
433
  {
@@ -379,6 +477,17 @@
379
477
  "default": "md",
380
478
  "description": ""
381
479
  },
480
+ {
481
+ "name": "tone",
482
+ "type": "enum",
483
+ "values": [
484
+ "\"default\"",
485
+ "\"danger\""
486
+ ],
487
+ "required": false,
488
+ "default": "default",
489
+ "description": "`danger` for an action that destroys something (delete, remove, leave). It\nrecolours whichever `variant` is in use, so emphasis and danger stay two\nseparate decisions: `variant=\"ghost\" tone=\"danger\"` is a quiet delete."
490
+ },
382
491
  {
383
492
  "name": "variant",
384
493
  "type": "enum",
@@ -410,6 +519,7 @@
410
519
  ],
411
520
  "import": "import { Calendar } from '@pearpages/pulp-react/calendar';",
412
521
  "css": "@pearpages/pulp-react/calendar.css",
522
+ "client": true,
413
523
  "tokens": "--calendar-*",
414
524
  "props": [
415
525
  {
@@ -522,6 +632,7 @@
522
632
  ],
523
633
  "import": "import { Card } from '@pearpages/pulp-react/card';",
524
634
  "css": "@pearpages/pulp-react/card.css",
635
+ "client": false,
525
636
  "tokens": "--card-*",
526
637
  "props": [
527
638
  {
@@ -628,6 +739,7 @@
628
739
  ],
629
740
  "import": "import { Checkbox } from '@pearpages/pulp-react/checkbox';",
630
741
  "css": "@pearpages/pulp-react/checkbox.css",
742
+ "client": true,
631
743
  "tokens": "--checkbox-*",
632
744
  "props": [
633
745
  {
@@ -687,6 +799,93 @@
687
799
  ],
688
800
  "parts": []
689
801
  },
802
+ {
803
+ "name": "Chip",
804
+ "description": "A small pill for a choice or a value the user controls: a filter that\ntoggles (`selected`), a tag that can be removed (`onRemove`), or both. The\nlabel is a button only when pressing it does something.",
805
+ "status": "experimental",
806
+ "category": "Actions",
807
+ "accessibility": "A toggle chip is a native `button` with `aria-pressed`, so Space and Enter work and the state is announced. The remove control is a second `button`, a sibling of the first (a button inside a button is invalid HTML and unreachable by keyboard), named by `removeLabel` plus the chip's own label through `aria-labelledby`: \"Remove Vegan\", not \"Remove\" six times. The pair is wrapped in a `role=\"group\"` named by the label only when there are two controls. `disabled` disables both. After removing a chip, move focus yourself (to the next chip or the input): the button that had it is gone.",
808
+ "do": [
809
+ "Use a group of toggle chips for filters where several can be on at once.",
810
+ "Keep the label to a word or two; a chip does not wrap."
811
+ ],
812
+ "dont": [
813
+ "Use a chip that does nothing as a label or a status: that is a Badge.",
814
+ "Use chips for one choice out of several; that is a SegmentedControl or a RadioGroup."
815
+ ],
816
+ "import": "import { Chip } from '@pearpages/pulp-react/chip';",
817
+ "css": "@pearpages/pulp-react/chip.css",
818
+ "client": true,
819
+ "tokens": "--chip-*",
820
+ "props": [
821
+ {
822
+ "name": "defaultSelected",
823
+ "type": "boolean",
824
+ "required": false,
825
+ "default": null,
826
+ "description": "Uncontrolled initial pressed state."
827
+ },
828
+ {
829
+ "name": "onRemove",
830
+ "type": "(() => void)",
831
+ "required": false,
832
+ "default": null,
833
+ "description": "Adds a remove button after the label: a second button beside the chip, never inside it."
834
+ },
835
+ {
836
+ "name": "onSelectedChange",
837
+ "type": "((selected: boolean) => void)",
838
+ "required": false,
839
+ "default": null,
840
+ "description": "Called with the next state when the chip is pressed."
841
+ },
842
+ {
843
+ "name": "ref",
844
+ "type": "Ref<HTMLButtonElement>",
845
+ "required": false,
846
+ "default": null,
847
+ "description": "The `button` that carries the label, or the `span` when the chip neither toggles nor has an `onClick`."
848
+ },
849
+ {
850
+ "name": "removeLabel",
851
+ "type": "string",
852
+ "required": false,
853
+ "default": "Remove",
854
+ "description": "The remove button is named \"`removeLabel` + the chip's label\" (\"Remove Vegan\")."
855
+ },
856
+ {
857
+ "name": "selected",
858
+ "type": "boolean",
859
+ "required": false,
860
+ "default": null,
861
+ "description": "Controlled pressed state. Passing it (or `defaultSelected`, or `onSelectedChange`) makes the chip a toggle."
862
+ },
863
+ {
864
+ "name": "size",
865
+ "type": "enum",
866
+ "values": [
867
+ "\"sm\"",
868
+ "\"md\"",
869
+ "\"lg\""
870
+ ],
871
+ "required": false,
872
+ "default": "md",
873
+ "description": "`sm` is 24px high, the minimum target size."
874
+ },
875
+ {
876
+ "name": "tone",
877
+ "type": "enum",
878
+ "values": [
879
+ "\"neutral\"",
880
+ "\"action\""
881
+ ],
882
+ "required": false,
883
+ "default": "neutral",
884
+ "description": "`action` takes the brand's colour; a selected chip is filled in either tone."
885
+ }
886
+ ],
887
+ "parts": []
888
+ },
690
889
  {
691
890
  "name": "Combobox",
692
891
  "description": "A text input that suggests options as the user types, on React Aria's\ncombobox (decision record 001): the list is announced through\n`aria-activedescendant`, arrows move, Enter selects, Escape closes and\nthen clears. Static lists filter themselves; for async options, control\n`inputValue`, fetch, and pass the results with `filter=\"none\"` and\n`loading` while they arrive. Options look the same as Listbox's.",
@@ -703,6 +902,7 @@
703
902
  ],
704
903
  "import": "import { Combobox } from '@pearpages/pulp-react/combobox';",
705
904
  "css": "@pearpages/pulp-react/combobox.css",
905
+ "client": true,
706
906
  "tokens": "--combobox-*",
707
907
  "props": [
708
908
  {
@@ -914,6 +1114,7 @@
914
1114
  ],
915
1115
  "import": "import { DatePicker } from '@pearpages/pulp-react/date-picker';",
916
1116
  "css": "@pearpages/pulp-react/date-picker.css",
1117
+ "client": true,
917
1118
  "tokens": "--date-picker-*",
918
1119
  "props": [
919
1120
  {
@@ -1064,6 +1265,7 @@
1064
1265
  ],
1065
1266
  "import": "import { DialogSystem } from '@pearpages/pulp-react/dialog';",
1066
1267
  "css": "@pearpages/pulp-react/dialog.css",
1268
+ "client": true,
1067
1269
  "tokens": "--dialog-*",
1068
1270
  "props": [
1069
1271
  {
@@ -1099,6 +1301,7 @@
1099
1301
  ],
1100
1302
  "import": "import { Dialog } from '@pearpages/pulp-react/dialog';",
1101
1303
  "css": "@pearpages/pulp-react/dialog.css",
1304
+ "client": true,
1102
1305
  "tokens": "--dialog-*",
1103
1306
  "props": [
1104
1307
  {
@@ -1132,6 +1335,149 @@
1132
1335
  ],
1133
1336
  "parts": []
1134
1337
  },
1338
+ {
1339
+ "name": "Divider",
1340
+ "description": "A hairline between two groups of content, horizontal or vertical. A vertical\ndivider stretches to its flex or grid row, so it needs a parent that lays it out.",
1341
+ "status": "experimental",
1342
+ "category": "Layout",
1343
+ "accessibility": "Decorative by default: `role=\"none\"`, nothing is announced, because headings and landmarks should already carry the structure. With `decorative={false}` it is a `separator`, and a vertical one says so through `aria-orientation`. It is never focusable: a separator that moves is a splitter, which this is not.",
1344
+ "do": [
1345
+ "Put it between groups inside a Card, a Menu-like list or a toolbar.",
1346
+ "Let `spacing` set the distance, so it stays on the spacing scale."
1347
+ ],
1348
+ "dont": [
1349
+ "Use it to draw a border around something; that is the container's border.",
1350
+ "Rely on it alone to tell a screen reader that a new section starts; use a heading."
1351
+ ],
1352
+ "import": "import { Divider } from '@pearpages/pulp-react/divider';",
1353
+ "css": "@pearpages/pulp-react/divider.css",
1354
+ "client": false,
1355
+ "tokens": "--divider-*",
1356
+ "props": [
1357
+ {
1358
+ "name": "decorative",
1359
+ "type": "boolean",
1360
+ "required": false,
1361
+ "default": "true",
1362
+ "description": "A decorative line is hidden from assistive technology. Pass `false` when the\nline is the only thing that says two groups are separate."
1363
+ },
1364
+ {
1365
+ "name": "orientation",
1366
+ "type": "enum",
1367
+ "values": [
1368
+ "\"horizontal\"",
1369
+ "\"vertical\""
1370
+ ],
1371
+ "required": false,
1372
+ "default": "horizontal",
1373
+ "description": ""
1374
+ },
1375
+ {
1376
+ "name": "ref",
1377
+ "type": "Ref<HTMLDivElement>",
1378
+ "required": false,
1379
+ "default": null,
1380
+ "description": ""
1381
+ },
1382
+ {
1383
+ "name": "spacing",
1384
+ "type": "enum",
1385
+ "values": [
1386
+ "\"none\"",
1387
+ "\"sm\"",
1388
+ "\"md\"",
1389
+ "\"lg\""
1390
+ ],
1391
+ "required": false,
1392
+ "default": "none",
1393
+ "description": "Space on both sides, along the axis the line separates."
1394
+ }
1395
+ ],
1396
+ "parts": []
1397
+ },
1398
+ {
1399
+ "name": "EmptyState",
1400
+ "description": "What a list, a search or a page shows instead of its content: nothing yet,\nnothing found, or it failed to load. An icon, a title, a sentence and one\naction, centred. Composes Heading, Text and Stack, so the type follows the\nbrand without tokens of its own.",
1401
+ "status": "experimental",
1402
+ "category": "Feedback",
1403
+ "accessibility": "The title is a real heading at `headingLevel`, so the empty region shows up in the outline; pick the level from the page, as with Heading. The icon is `aria-hidden`. Nothing is announced on its own: an empty state that is there when the page loads is just content. When it **replaces** content after the user did something (a search with no results, a failed reload), pass `role=\"status\"` (or `role=\"alert\"` for an error that needs attention) so it is read out; do not put `role=\"alert\"` on one that renders with the page. The action is whatever you pass, with its own semantics.",
1404
+ "do": [
1405
+ "Say what to do next in the description, and make the action that next step.",
1406
+ "Use `tone=\"error\"` with a retry action when loading failed."
1407
+ ],
1408
+ "dont": [
1409
+ "Use it for a validation error or a transient message; that is a Field error, an Alert or a Toast.",
1410
+ "Offer several actions; an empty state has one way forward."
1411
+ ],
1412
+ "import": "import { EmptyState } from '@pearpages/pulp-react/empty-state';",
1413
+ "css": "@pearpages/pulp-react/empty-state.css",
1414
+ "client": false,
1415
+ "tokens": "--empty-state-*",
1416
+ "props": [
1417
+ {
1418
+ "name": "title",
1419
+ "type": "ReactNode",
1420
+ "required": true,
1421
+ "default": null,
1422
+ "description": "What is missing, or what went wrong, in a few words. Rendered as a heading."
1423
+ },
1424
+ {
1425
+ "name": "action",
1426
+ "type": "ReactNode",
1427
+ "required": false,
1428
+ "default": null,
1429
+ "description": "One control: a Button, or a Link. For an error, the retry."
1430
+ },
1431
+ {
1432
+ "name": "description",
1433
+ "type": "ReactNode",
1434
+ "required": false,
1435
+ "default": null,
1436
+ "description": "One or two sentences: why it is empty and what to do about it."
1437
+ },
1438
+ {
1439
+ "name": "headingLevel",
1440
+ "type": "enum",
1441
+ "values": [
1442
+ "1",
1443
+ "2",
1444
+ "3",
1445
+ "4",
1446
+ "5",
1447
+ "6"
1448
+ ],
1449
+ "required": false,
1450
+ "default": "3",
1451
+ "description": "The heading's outline level, from where the empty state sits in the page."
1452
+ },
1453
+ {
1454
+ "name": "icon",
1455
+ "type": "ReactNode",
1456
+ "required": false,
1457
+ "default": null,
1458
+ "description": "A glyph, shown in a tinted disc. Decorative: the title carries the meaning."
1459
+ },
1460
+ {
1461
+ "name": "ref",
1462
+ "type": "Ref<HTMLDivElement>",
1463
+ "required": false,
1464
+ "default": null,
1465
+ "description": ""
1466
+ },
1467
+ {
1468
+ "name": "tone",
1469
+ "type": "enum",
1470
+ "values": [
1471
+ "\"neutral\"",
1472
+ "\"error\""
1473
+ ],
1474
+ "required": false,
1475
+ "default": "neutral",
1476
+ "description": "`error` tints the icon with the error colours. It does not announce anything: see the accessibility notes."
1477
+ }
1478
+ ],
1479
+ "parts": []
1480
+ },
1135
1481
  {
1136
1482
  "name": "Field",
1137
1483
  "description": "The wiring every form control shares: a label pointing at the control, a\ndescription and an error exposed through `aria-describedby`, and invalid,\ndisabled and required state in one place. Only parts that are mounted end\nup in `aria-describedby`, so no id ever dangles. Compose with\n`Field.Label`, `Field.Control`, `Field.Description` and `Field.Error`\n(one of each); state on the DOM as `data-invalid`, `data-disabled`,\n`data-required`.\n\nKnown limitation: parts register in a layout effect, so server-rendered\nHTML carries no `aria-describedby` until hydration. The relationship is\ncomplete for every interactive use; static HTML consumers should not rely\non it yet.",
@@ -1146,6 +1492,7 @@
1146
1492
  ],
1147
1493
  "import": "import { Field } from '@pearpages/pulp-react/field';",
1148
1494
  "css": "@pearpages/pulp-react/field.css",
1495
+ "client": true,
1149
1496
  "tokens": "--field-*",
1150
1497
  "props": [
1151
1498
  {
@@ -1271,6 +1618,7 @@
1271
1618
  ],
1272
1619
  "import": "import { Heading } from '@pearpages/pulp-react/heading';",
1273
1620
  "css": "@pearpages/pulp-react/heading.css",
1621
+ "client": false,
1274
1622
  "tokens": "--heading-*",
1275
1623
  "props": [
1276
1624
  {
@@ -1337,6 +1685,7 @@
1337
1685
  ],
1338
1686
  "import": "import { Icon } from '@pearpages/pulp-react/icon';",
1339
1687
  "css": "@pearpages/pulp-react/icon.css",
1688
+ "client": false,
1340
1689
  "tokens": "--icon-*",
1341
1690
  "props": [
1342
1691
  {
@@ -1390,6 +1739,7 @@
1390
1739
  ],
1391
1740
  "import": "import { IconButton } from '@pearpages/pulp-react/icon-button';",
1392
1741
  "css": "@pearpages/pulp-react/icon-button.css",
1742
+ "client": false,
1393
1743
  "tokens": "--icon-button-*",
1394
1744
  "props": [
1395
1745
  {
@@ -1446,6 +1796,17 @@
1446
1796
  "default": "'md'",
1447
1797
  "description": ""
1448
1798
  },
1799
+ {
1800
+ "name": "tone",
1801
+ "type": "enum",
1802
+ "values": [
1803
+ "\"default\"",
1804
+ "\"danger\""
1805
+ ],
1806
+ "required": false,
1807
+ "default": "'default'",
1808
+ "description": "`danger` for an action that destroys something (delete, remove, leave). It\nrecolours whichever `variant` is in use, so emphasis and danger stay two\nseparate decisions: `variant=\"ghost\" tone=\"danger\"` is a quiet delete."
1809
+ },
1449
1810
  {
1450
1811
  "name": "variant",
1451
1812
  "type": "enum",
@@ -1475,6 +1836,7 @@
1475
1836
  ],
1476
1837
  "import": "import { Inline } from '@pearpages/pulp-react/inline';",
1477
1838
  "css": "@pearpages/pulp-react/inline.css",
1839
+ "client": false,
1478
1840
  "tokens": "--inline-*",
1479
1841
  "props": [
1480
1842
  {
@@ -1552,6 +1914,65 @@
1552
1914
  ],
1553
1915
  "parts": []
1554
1916
  },
1917
+ {
1918
+ "name": "Link",
1919
+ "description": "A text link. Underlined by default, because colour alone must not be what\ntells a link from the words around it. With `asChild` the styles and props\nland on a router's own link, so client-side navigation keeps working.",
1920
+ "status": "experimental",
1921
+ "category": "Typography",
1922
+ "accessibility": "A native `a`: focusable and activated with Enter only when it has an `href` (or the `asChild` element provides one). The focus ring comes from the semantic focus tokens. `underline=\"hover\"` removes the only non-colour cue, so it is for lists of links, never for a link inside a sentence (WCAG 1.4.1).",
1923
+ "do": [
1924
+ "Use `asChild` with the router's link: `<Link asChild><NextLink href=\"/feed\">Feed</NextLink></Link>`.",
1925
+ "Write link text that makes sense out of context; screen readers list links on their own."
1926
+ ],
1927
+ "dont": [
1928
+ "Use it for an action that does not navigate; that is a Button (`variant=\"ghost\"`).",
1929
+ "Use `underline=\"hover\"` for a link inside running text."
1930
+ ],
1931
+ "import": "import { Link } from '@pearpages/pulp-react/link';",
1932
+ "css": "@pearpages/pulp-react/link.css",
1933
+ "client": false,
1934
+ "tokens": "--link-*",
1935
+ "props": [
1936
+ {
1937
+ "name": "asChild",
1938
+ "type": "boolean",
1939
+ "required": false,
1940
+ "default": "false",
1941
+ "description": "Render the single child instead of an `a`: a router's link component."
1942
+ },
1943
+ {
1944
+ "name": "ref",
1945
+ "type": "Ref<E>",
1946
+ "required": false,
1947
+ "default": null,
1948
+ "description": ""
1949
+ },
1950
+ {
1951
+ "name": "tone",
1952
+ "type": "enum",
1953
+ "values": [
1954
+ "\"action\"",
1955
+ "\"default\"",
1956
+ "\"muted\""
1957
+ ],
1958
+ "required": false,
1959
+ "default": "action",
1960
+ "description": "`action` is the brand's link colour; `default` and `muted` take the text colours."
1961
+ },
1962
+ {
1963
+ "name": "underline",
1964
+ "type": "enum",
1965
+ "values": [
1966
+ "\"always\"",
1967
+ "\"hover\""
1968
+ ],
1969
+ "required": false,
1970
+ "default": "always",
1971
+ "description": "`hover` drops the underline until the pointer is over it. Only for links that\nare obviously links from where they sit (a nav, a footer list)."
1972
+ }
1973
+ ],
1974
+ "parts": []
1975
+ },
1555
1976
  {
1556
1977
  "name": "Listbox",
1557
1978
  "description": "A visible list of options with single or multiple selection, on React\nAria's listbox (decision record 001): arrow keys move, Space toggles,\ntyping jumps, Shift and Ctrl/Cmd extend a multiple selection, Ctrl/Cmd+A\nselects all. For a list that opens from a control, use Picker; for one\nthat filters as you type, use Combobox. Both render these options.",
@@ -1567,6 +1988,7 @@
1567
1988
  ],
1568
1989
  "import": "import { Listbox } from '@pearpages/pulp-react/listbox';",
1569
1990
  "css": "@pearpages/pulp-react/listbox.css",
1991
+ "client": true,
1570
1992
  "tokens": "--listbox-*",
1571
1993
  "props": [
1572
1994
  {
@@ -1675,6 +2097,7 @@
1675
2097
  ],
1676
2098
  "import": "import { Menu } from '@pearpages/pulp-react/menu';",
1677
2099
  "css": "@pearpages/pulp-react/menu.css",
2100
+ "client": true,
1678
2101
  "tokens": "--menu-*",
1679
2102
  "props": [
1680
2103
  {
@@ -1810,6 +2233,7 @@
1810
2233
  ],
1811
2234
  "import": "import { Pagination } from '@pearpages/pulp-react/pagination';",
1812
2235
  "css": "@pearpages/pulp-react/pagination.css",
2236
+ "client": false,
1813
2237
  "tokens": "--pagination-*",
1814
2238
  "props": [
1815
2239
  {
@@ -1918,6 +2342,7 @@
1918
2342
  ],
1919
2343
  "import": "import { Picker } from '@pearpages/pulp-react/picker';",
1920
2344
  "css": "@pearpages/pulp-react/picker.css",
2345
+ "client": true,
1921
2346
  "tokens": "--picker-*",
1922
2347
  "props": [
1923
2348
  {
@@ -2056,6 +2481,7 @@
2056
2481
  ],
2057
2482
  "import": "import { Popover } from '@pearpages/pulp-react/popover';",
2058
2483
  "css": "@pearpages/pulp-react/popover.css",
2484
+ "client": true,
2059
2485
  "tokens": "--popover-*",
2060
2486
  "props": [
2061
2487
  {
@@ -2167,6 +2593,7 @@
2167
2593
  ],
2168
2594
  "import": "import { Progress } from '@pearpages/pulp-react/progress';",
2169
2595
  "css": "@pearpages/pulp-react/progress.css",
2596
+ "client": false,
2170
2597
  "tokens": "--progress-*",
2171
2598
  "props": [
2172
2599
  {
@@ -2252,6 +2679,7 @@
2252
2679
  ],
2253
2680
  "import": "import { RadioGroup } from '@pearpages/pulp-react/radio';",
2254
2681
  "css": "@pearpages/pulp-react/radio.css",
2682
+ "client": true,
2255
2683
  "tokens": "--radio-*",
2256
2684
  "props": [
2257
2685
  {
@@ -2364,6 +2792,7 @@
2364
2792
  ],
2365
2793
  "import": "import { Radio } from '@pearpages/pulp-react/radio';",
2366
2794
  "css": "@pearpages/pulp-react/radio.css",
2795
+ "client": true,
2367
2796
  "tokens": "--radio-*",
2368
2797
  "props": [
2369
2798
  {
@@ -2397,6 +2826,197 @@
2397
2826
  ],
2398
2827
  "parts": []
2399
2828
  },
2829
+ {
2830
+ "name": "SegmentedControl",
2831
+ "description": "One choice out of a few, all visible, switching what is shown in place: a\nview mode, a unit, a sort order. Generic over a string union, so `value`\nand `onValueChange` are typed by the options. For links that navigate to\nanother page use `SegmentedControl.Nav`, which looks the same and is a `nav`.",
2832
+ "status": "experimental",
2833
+ "category": "Forms",
2834
+ "accessibility": "A `radiogroup` of native radio inputs, exactly as RadioGroup: the keyboard is the browser's own (arrows move and select, Tab enters on the checked radio and leaves the group), nothing is re-implemented. Name the group with `label` or `aria-labelledby`. The inputs are visually hidden, not removed, so the focus ring is drawn on the visible segment and the control takes part in a form (`name`). Icons are decorative.",
2835
+ "do": [
2836
+ "Use it for two to five short options that change the view in place.",
2837
+ "Use `SegmentedControl.Nav` when each option is a URL."
2838
+ ],
2839
+ "dont": [
2840
+ "Use it for on/off; that is a Switch. For several at once, toggle Chips.",
2841
+ "Use it for a form question with longer labels or a description; that is a RadioGroup."
2842
+ ],
2843
+ "import": "import { SegmentedControl } from '@pearpages/pulp-react/segmented-control';",
2844
+ "css": "@pearpages/pulp-react/segmented-control.css",
2845
+ "client": true,
2846
+ "tokens": "--segmented-control-*",
2847
+ "props": [
2848
+ {
2849
+ "name": "options",
2850
+ "type": "readonly SegmentedControlOption<T>[]",
2851
+ "required": true,
2852
+ "default": null,
2853
+ "description": ""
2854
+ },
2855
+ {
2856
+ "name": "defaultValue",
2857
+ "type": "string",
2858
+ "required": false,
2859
+ "default": null,
2860
+ "description": "Initially selected value when uncontrolled."
2861
+ },
2862
+ {
2863
+ "name": "disabled",
2864
+ "type": "boolean",
2865
+ "required": false,
2866
+ "default": "false",
2867
+ "description": ""
2868
+ },
2869
+ {
2870
+ "name": "fullWidth",
2871
+ "type": "boolean",
2872
+ "required": false,
2873
+ "default": "false",
2874
+ "description": "Segments share the container's width equally. `segmented` look only."
2875
+ },
2876
+ {
2877
+ "name": "label",
2878
+ "type": "string",
2879
+ "required": false,
2880
+ "default": null,
2881
+ "description": "Names the group for assistive technology. Not rendered; label it visibly with `aria-labelledby` instead when there is a heading."
2882
+ },
2883
+ {
2884
+ "name": "look",
2885
+ "type": "enum",
2886
+ "values": [
2887
+ "\"segmented\"",
2888
+ "\"chips\""
2889
+ ],
2890
+ "required": false,
2891
+ "default": "segmented",
2892
+ "description": "`segmented` is one track with a raised thumb; `chips` is separate pills that wrap."
2893
+ },
2894
+ {
2895
+ "name": "name",
2896
+ "type": "string",
2897
+ "required": false,
2898
+ "default": null,
2899
+ "description": "Form field name shared by the radios. Generated when omitted."
2900
+ },
2901
+ {
2902
+ "name": "onValueChange",
2903
+ "type": "((value: T) => void)",
2904
+ "required": false,
2905
+ "default": null,
2906
+ "description": ""
2907
+ },
2908
+ {
2909
+ "name": "ref",
2910
+ "type": "Ref<HTMLDivElement>",
2911
+ "required": false,
2912
+ "default": null,
2913
+ "description": ""
2914
+ },
2915
+ {
2916
+ "name": "size",
2917
+ "type": "enum",
2918
+ "values": [
2919
+ "\"sm\"",
2920
+ "\"md\""
2921
+ ],
2922
+ "required": false,
2923
+ "default": "md",
2924
+ "description": ""
2925
+ },
2926
+ {
2927
+ "name": "value",
2928
+ "type": "string",
2929
+ "required": false,
2930
+ "default": null,
2931
+ "description": "Controlled selected value."
2932
+ }
2933
+ ],
2934
+ "parts": [
2935
+ {
2936
+ "name": "SegmentedControl.Nav",
2937
+ "description": "The same look for links that navigate: a labelled `nav` with a list of\nlinks, the current one marked `aria-current=\"page\"`. Not a radiogroup and\nnot tabs: following a link loads another page, and a screen reader user\nshould be told so.",
2938
+ "props": [
2939
+ {
2940
+ "name": "children",
2941
+ "type": "ReactNode",
2942
+ "required": true,
2943
+ "default": null,
2944
+ "description": "`SegmentedControl.NavItem`s."
2945
+ },
2946
+ {
2947
+ "name": "label",
2948
+ "type": "string",
2949
+ "required": true,
2950
+ "default": null,
2951
+ "description": "Names the landmark (\"Feed view\"). Required: a page with several `nav`s needs them told apart."
2952
+ },
2953
+ {
2954
+ "name": "fullWidth",
2955
+ "type": "boolean",
2956
+ "required": false,
2957
+ "default": "false",
2958
+ "description": ""
2959
+ },
2960
+ {
2961
+ "name": "look",
2962
+ "type": "enum",
2963
+ "values": [
2964
+ "\"segmented\"",
2965
+ "\"chips\""
2966
+ ],
2967
+ "required": false,
2968
+ "default": "segmented",
2969
+ "description": ""
2970
+ },
2971
+ {
2972
+ "name": "ref",
2973
+ "type": "Ref<HTMLElement>",
2974
+ "required": false,
2975
+ "default": null,
2976
+ "description": ""
2977
+ },
2978
+ {
2979
+ "name": "size",
2980
+ "type": "enum",
2981
+ "values": [
2982
+ "\"sm\"",
2983
+ "\"md\""
2984
+ ],
2985
+ "required": false,
2986
+ "default": "md",
2987
+ "description": ""
2988
+ }
2989
+ ]
2990
+ },
2991
+ {
2992
+ "name": "SegmentedControl.NavItem",
2993
+ "description": "One link of a `SegmentedControl.Nav`. With `asChild`, the router's own link takes the styles.",
2994
+ "props": [
2995
+ {
2996
+ "name": "asChild",
2997
+ "type": "boolean",
2998
+ "required": false,
2999
+ "default": "false",
3000
+ "description": "Render the single child instead of an `a`: a router's link component."
3001
+ },
3002
+ {
3003
+ "name": "current",
3004
+ "type": "boolean",
3005
+ "required": false,
3006
+ "default": "false",
3007
+ "description": "This link is the page being shown: `aria-current=\"page\"` and the selected look."
3008
+ },
3009
+ {
3010
+ "name": "ref",
3011
+ "type": "Ref<E>",
3012
+ "required": false,
3013
+ "default": null,
3014
+ "description": ""
3015
+ }
3016
+ ]
3017
+ }
3018
+ ]
3019
+ },
2400
3020
  {
2401
3021
  "name": "Select",
2402
3022
  "description": "A native `<select>` on top of Field: the browser's own popup, keyboard and\nassistive-technology support, with the closed control drawn from\n`--select-*` tokens. For rich options or multi-select, a listbox component\ncomes later on a headless layer; this one stays native on purpose.",
@@ -2411,6 +3031,7 @@
2411
3031
  ],
2412
3032
  "import": "import { Select } from '@pearpages/pulp-react/select';",
2413
3033
  "css": "@pearpages/pulp-react/select.css",
3034
+ "client": true,
2414
3035
  "tokens": "--select-*",
2415
3036
  "props": [
2416
3037
  {
@@ -2482,7 +3103,7 @@
2482
3103
  "description": "A dialog docked to an edge of the viewport: a side panel for detail or settings that keeps the\npage in view, or a bottom sheet on a phone. It is Dialog with a `placement`: the same parts\n(`Sheet.Trigger`, `Sheet.Content`, `Sheet.Header`, `Sheet.Title`, `Sheet.Description`,\n`Sheet.Close`, `Sheet.Body`, `Sheet.Footer`), the same focus trap, stacking and dismissal, and\nthe same colours and type, from Dialog's tokens. Only the size is Sheet's: `--sheet-width`,\n`--sheet-height` and `--sheet-motion-translate`, the distance it slides in from its edge.\n\nIt needs what Dialog needs: `DialogSystem` mounted once near the app root, and\n`@pearpages/modals/styles.css` imported into the `vendor` layer.",
2483
3104
  "status": "experimental",
2484
3105
  "category": "Overlays",
2485
- "accessibility": "Dialog's: `role=\"dialog\"` with `aria-modal`, named by `Sheet.Title` and described by `Sheet.Description`; focus moves in on open, is trapped, and returns to the trigger on close; Escape and the backdrop dismiss it; the page behind is inert. Docking changes the layout only, never the semantics.",
3106
+ "accessibility": "The drag handle of a bottom sheet is a pointer and touch shortcut, hidden from assistive technology and not focusable: it adds no keyboard stop, and closing by keyboard stays Escape or `Sheet.Close`, so always render a `Sheet.Close` in a sheet that can be dragged. Otherwise Dialog's: `role=\"dialog\"` with `aria-modal`, named by `Sheet.Title` and described by `Sheet.Description`; focus moves in on open, is trapped, and returns to the trigger on close; Escape and the backdrop dismiss it; the page behind is inert. Docking changes the layout only, never the semantics.",
2486
3107
  "do": [
2487
3108
  "Always render a `Sheet.Title`.",
2488
3109
  "Dock to `end` for detail and settings beside the page, and to `bottom` for choices on a narrow screen."
@@ -2493,6 +3114,7 @@
2493
3114
  ],
2494
3115
  "import": "import { Sheet } from '@pearpages/pulp-react/sheet';",
2495
3116
  "css": "@pearpages/pulp-react/sheet.css",
3117
+ "client": true,
2496
3118
  "tokens": "--sheet-*",
2497
3119
  "props": [
2498
3120
  {
@@ -2564,6 +3186,13 @@
2564
3186
  "default": null,
2565
3187
  "description": "Whether pressing Esc closes topmost modal (default: true)"
2566
3188
  },
3189
+ {
3190
+ "name": "dragToDismiss",
3191
+ "type": "boolean",
3192
+ "required": false,
3193
+ "default": "true",
3194
+ "description": "`placement=\"bottom\"` only: a grab handle at the top of the sheet that can be dragged down to\nclose it (past a quarter of the sheet's height, or with a flick). Pointer and touch only, and\noff under `prefers-reduced-motion`; Escape, the backdrop and `Sheet.Close` are unchanged."
3195
+ },
2567
3196
  {
2568
3197
  "name": "onInteractOutside",
2569
3198
  "type": "((e: { target: EventTarget; preventDefault(): void; }) => void)",
@@ -2616,6 +3245,7 @@
2616
3245
  ],
2617
3246
  "import": "import { Skeleton } from '@pearpages/pulp-react/skeleton';",
2618
3247
  "css": "@pearpages/pulp-react/skeleton.css",
3248
+ "client": false,
2619
3249
  "tokens": "--skeleton-*",
2620
3250
  "props": [
2621
3251
  {
@@ -2675,6 +3305,7 @@
2675
3305
  ],
2676
3306
  "import": "import { Slider } from '@pearpages/pulp-react/slider';",
2677
3307
  "css": "@pearpages/pulp-react/slider.css",
3308
+ "client": true,
2678
3309
  "tokens": "--slider-*",
2679
3310
  "props": [
2680
3311
  {
@@ -2824,6 +3455,7 @@
2824
3455
  ],
2825
3456
  "import": "import { Spinner } from '@pearpages/pulp-react/spinner';",
2826
3457
  "css": "@pearpages/pulp-react/spinner.css",
3458
+ "client": false,
2827
3459
  "tokens": "--spinner-*",
2828
3460
  "props": [
2829
3461
  {
@@ -2888,6 +3520,7 @@
2888
3520
  ],
2889
3521
  "import": "import { Stack } from '@pearpages/pulp-react/stack';",
2890
3522
  "css": "@pearpages/pulp-react/stack.css",
3523
+ "client": false,
2891
3524
  "tokens": "--stack-*",
2892
3525
  "props": [
2893
3526
  {
@@ -2972,6 +3605,7 @@
2972
3605
  ],
2973
3606
  "import": "import { Switch } from '@pearpages/pulp-react/switch';",
2974
3607
  "css": "@pearpages/pulp-react/switch.css",
3608
+ "client": true,
2975
3609
  "tokens": "--switch-*",
2976
3610
  "props": [
2977
3611
  {
@@ -3040,6 +3674,7 @@
3040
3674
  ],
3041
3675
  "import": "import { Table } from '@pearpages/pulp-react/table';",
3042
3676
  "css": "@pearpages/pulp-react/table.css",
3677
+ "client": true,
3043
3678
  "tokens": "--table-*",
3044
3679
  "props": [
3045
3680
  {
@@ -3333,6 +3968,7 @@
3333
3968
  ],
3334
3969
  "import": "import { Tabs } from '@pearpages/pulp-react/tabs';",
3335
3970
  "css": "@pearpages/pulp-react/tabs.css",
3971
+ "client": true,
3336
3972
  "tokens": "--tabs-*",
3337
3973
  "props": [
3338
3974
  {
@@ -3456,6 +4092,7 @@
3456
4092
  ],
3457
4093
  "import": "import { Text } from '@pearpages/pulp-react/text';",
3458
4094
  "css": "@pearpages/pulp-react/text.css",
4095
+ "client": false,
3459
4096
  "tokens": "--text-*",
3460
4097
  "props": [
3461
4098
  {
@@ -3556,10 +4193,11 @@
3556
4193
  "description": "A single-line text input on top of `Field`: label, description and error\nwired for assistive technology. Controlled and uncontrolled behave exactly\nlike the native input (`value`/`onChange` or `defaultValue`). Every value\ncomes from `--text-field-*` and `--field-*` tokens; state is exposed as\n`data-size`, `data-invalid`, `data-disabled` and `data-readonly` on the\nwrapper.",
3557
4194
  "status": "stable",
3558
4195
  "category": "Forms",
3559
- "accessibility": "A native text `input` wired through Field: `label[for]`, `aria-describedby` for description and error, `aria-invalid` and `aria-required`. Everything the browser gives a native input (autofill, IME, forms) applies.",
4196
+ "accessibility": "A native text `input` wired through Field: `label[for]`, `aria-describedby` for description and error, `aria-invalid` and `aria-required`. Everything the browser gives a native input (autofill, IME, forms) applies. `hideLabel` hides the label visually, never from assistive technology. The leading icon is decorative. The clear button is a real `button` named by `clearLabel`, after the input in the tab order, present only while there is a value; pressing it (or Escape in the field) empties the field and leaves focus in the input. `type=\"search\"` gives the input the `searchbox` role; wrap the form in `<search>` or `role=\"search\"` yourself when it is the page's search.",
3560
4197
  "do": [
3561
4198
  "Always pass `label`; use a visible one.",
3562
- "Prefer `error` text that says how to fix the value."
4199
+ "Prefer `error` text that says how to fix the value.",
4200
+ "For a search box: `type=\"search\"`, `hideLabel` if a heading names it, and `onClear`."
3563
4201
  ],
3564
4202
  "dont": [
3565
4203
  "Use `placeholder` as the label.",
@@ -3567,6 +4205,7 @@
3567
4205
  ],
3568
4206
  "import": "import { TextField } from '@pearpages/pulp-react/text-field';",
3569
4207
  "css": "@pearpages/pulp-react/text-field.css",
4208
+ "client": true,
3570
4209
  "tokens": "--text-field-*",
3571
4210
  "props": [
3572
4211
  {
@@ -3583,6 +4222,13 @@
3583
4222
  "default": null,
3584
4223
  "description": "Applied to the wrapper, not the input."
3585
4224
  },
4225
+ {
4226
+ "name": "clearLabel",
4227
+ "type": "string",
4228
+ "required": false,
4229
+ "default": "Clear",
4230
+ "description": "The clear button's name, for other languages."
4231
+ },
3586
4232
  {
3587
4233
  "name": "description",
3588
4234
  "type": "ReactNode",
@@ -3597,6 +4243,27 @@
3597
4243
  "default": null,
3598
4244
  "description": "Error message. Its presence marks the input invalid (`aria-invalid`,\n`data-invalid`) and the message is announced as an alert."
3599
4245
  },
4246
+ {
4247
+ "name": "hideLabel",
4248
+ "type": "boolean",
4249
+ "required": false,
4250
+ "default": "false",
4251
+ "description": "Keeps the label for assistive technology only. For a search box that a heading or its place in the page already names."
4252
+ },
4253
+ {
4254
+ "name": "iconStart",
4255
+ "type": "ReactNode",
4256
+ "required": false,
4257
+ "default": null,
4258
+ "description": "A decorative glyph inside the field, before the text. `type=\"search\"` gets the Search glyph unless you pass another (or `null`)."
4259
+ },
4260
+ {
4261
+ "name": "onClear",
4262
+ "type": "(() => void)",
4263
+ "required": false,
4264
+ "default": null,
4265
+ "description": "Adds a clear button that appears once the field has a value. Called when it\nis pressed and when Escape is pressed with a value. Controlled: set your\nvalue to `''` here. Uncontrolled: the input is emptied for you."
4266
+ },
3600
4267
  {
3601
4268
  "name": "ref",
3602
4269
  "type": "Ref<HTMLInputElement>",
@@ -3633,6 +4300,7 @@
3633
4300
  ],
3634
4301
  "import": "import { Textarea } from '@pearpages/pulp-react/textarea';",
3635
4302
  "css": "@pearpages/pulp-react/textarea.css",
4303
+ "client": true,
3636
4304
  "tokens": "--textarea-*",
3637
4305
  "props": [
3638
4306
  {
@@ -3706,7 +4374,7 @@
3706
4374
  },
3707
4375
  {
3708
4376
  "name": "ToastProvider",
3709
- "description": "Mount once near the app root. Owns a portal element with a polite live\nregion; `useToast()` adds notifications to it. Timers pause while the\nregion is hovered or holds focus, Escape dismisses the focused toast, and\nerror toasts stay until dismissed. Each toast is an Alert.",
4377
+ "description": "Mount once near the app root. Owns a portal element with a polite live\nregion; `useToast()` adds notifications to it. Timers pause while the\nregion is hovered or holds focus, Escape dismisses the focused toast, and\nerror toasts stay until dismissed. Each toast is an Alert.\n`toast.undo(message, onUndo)` is the shorthand for a reversible action.",
3710
4378
  "status": "stable",
3711
4379
  "category": "Feedback",
3712
4380
  "accessibility": "Owns a `region` named \"Notifications\" with `aria-live=\"polite\"`; each toast is an Alert with its live role off (the region announces). Timers pause while the region is hovered or holds focus; Escape dismisses the focused toast; error toasts stay until dismissed.",
@@ -3720,6 +4388,7 @@
3720
4388
  ],
3721
4389
  "import": "import { ToastProvider } from '@pearpages/pulp-react/toast';",
3722
4390
  "css": "@pearpages/pulp-react/toast.css",
4391
+ "client": true,
3723
4392
  "tokens": "--toast-*",
3724
4393
  "props": [
3725
4394
  {
@@ -3773,6 +4442,7 @@
3773
4442
  ],
3774
4443
  "import": "import { Tooltip } from '@pearpages/pulp-react/tooltip';",
3775
4444
  "css": "@pearpages/pulp-react/tooltip.css",
4445
+ "client": true,
3776
4446
  "tokens": "--tooltip-*",
3777
4447
  "props": [
3778
4448
  {
@@ -3830,6 +4500,7 @@
3830
4500
  ],
3831
4501
  "import": "import { VisuallyHidden } from '@pearpages/pulp-react/visually-hidden';",
3832
4502
  "css": "@pearpages/pulp-react/visually-hidden.css",
4503
+ "client": false,
3833
4504
  "tokens": "--visually-hidden-*",
3834
4505
  "props": [
3835
4506
  {