@slotted/react 0.1.0-beta.3 → 0.1.0-beta.5

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 (245) hide show
  1. package/README.md +16 -1
  2. package/dist/{accordion-D-YuMJzk.js → accordion-lCqrmQ3w.js} +43 -43
  3. package/dist/accordion.js +1 -1
  4. package/dist/{action-bar-OACchRZw.js → action-bar-ti3e6FK1.js} +1 -1
  5. package/dist/action-bar.js +1 -1
  6. package/dist/{app-shell-CmYvnu_7.js → app-shell-nnSP44OO.js} +271 -267
  7. package/dist/app-shell.js +1 -1
  8. package/dist/bin/slotted-agent-docs.mjs +297 -0
  9. package/dist/calendar-BZ6PP7Ke.js +436 -0
  10. package/dist/calendar.js +1 -1
  11. package/dist/{cascader-D1AhTyBL.js → cascader-Cn2Acpcv.js} +227 -210
  12. package/dist/cascader.js +1 -1
  13. package/dist/chart-04i6NZQA.js +968 -0
  14. package/dist/chart.js +1 -1
  15. package/dist/{checkbox-DfxFHZaU.js → checkbox-B4Sb1eXj.js} +3 -3
  16. package/dist/{checkbox-group-9sDgrV5Z.js → checkbox-group-LpGMuq9w.js} +2 -2
  17. package/dist/checkbox-group.js +1 -1
  18. package/dist/checkbox.js +1 -1
  19. package/dist/{code-block-CFHf8uTm.js → code-block-CTNiyt0U.js} +1 -1
  20. package/dist/code-block.js +1 -1
  21. package/dist/{collapsible-CGo49NNW.js → collapsible-F-K36XbF.js} +17 -17
  22. package/dist/collapsible.js +1 -1
  23. package/dist/{color-picker-CwbWzCKg.js → color-picker-BcRCeISx.js} +4 -4
  24. package/dist/color-picker.js +1 -1
  25. package/dist/{column-manager-CTtizVhm.js → column-manager-BtkYLs5G.js} +4 -4
  26. package/dist/column-manager.js +1 -1
  27. package/dist/{combobox-BVMI9lg8.js → combobox-C1D5doWM.js} +227 -198
  28. package/dist/combobox.js +1 -1
  29. package/dist/{context-menu-BuST6W94.js → context-menu-B00UIBVC.js} +1 -1
  30. package/dist/context-menu.js +1 -1
  31. package/dist/{currency-field-DD9A5U3D.js → currency-field-Dti9JFu0.js} +1 -1
  32. package/dist/currency-field.js +1 -1
  33. package/dist/{dashboard-grid-CBMP_-JV.js → dashboard-grid-CoLKLvYL.js} +1 -1
  34. package/dist/dashboard-grid.js +1 -1
  35. package/dist/{data-grid-BKaGMqIm.js → data-grid-DX95Y7mY.js} +18 -18
  36. package/dist/data-grid.js +1 -1
  37. package/dist/{data-list-oICmxGkA.js → data-list-CWYTPNm-.js} +2 -2
  38. package/dist/data-list.js +1 -1
  39. package/dist/date-field-BxEpqAPg.js +290 -0
  40. package/dist/date-field.js +1 -1
  41. package/dist/date-picker-DRk9SZp1.js +308 -0
  42. package/dist/date-picker.js +1 -1
  43. package/dist/date-range-picker-6Yz7avTS.js +483 -0
  44. package/dist/date-range-picker.js +1 -1
  45. package/dist/{datetime-field-B8TrmsDK.js → datetime-field-D2f9u6im.js} +1 -1
  46. package/dist/datetime-field.js +1 -1
  47. package/dist/datetime-picker-STqSWMkH.js +645 -0
  48. package/dist/datetime-picker.js +1 -1
  49. package/dist/datetime-range-picker-DQNQwr3K.js +1000 -0
  50. package/dist/datetime-range-picker.js +1 -1
  51. package/dist/{detail-panel-qOJiNuzr.js → detail-panel-z6WGleP4.js} +3 -3
  52. package/dist/detail-panel.js +1 -1
  53. package/dist/docs/AGENTS.snippet.md +2 -0
  54. package/dist/docs/app-shell.examples.md +1 -1
  55. package/dist/docs/app-shell.json +4 -4
  56. package/dist/docs/app-shell.md +2 -2
  57. package/dist/docs/calendar.json +7 -0
  58. package/dist/docs/calendar.md +1 -0
  59. package/dist/docs/collapsible.json +1 -1
  60. package/dist/docs/collapsible.md +1 -1
  61. package/dist/docs/date-field.json +8 -1
  62. package/dist/docs/date-field.md +2 -1
  63. package/dist/docs/date-picker.examples.md +29 -0
  64. package/dist/docs/date-picker.json +16 -1
  65. package/dist/docs/date-picker.md +2 -1
  66. package/dist/docs/guidelines/README.md +2 -2
  67. package/dist/docs/guidelines/composition.md +10 -0
  68. package/dist/docs/guidelines/index.md +3 -1
  69. package/dist/docs/guidelines/list-pages.md +12 -1
  70. package/dist/docs/guidelines/own-elements.md +1 -1
  71. package/dist/docs/page-header.json +1 -0
  72. package/dist/docs/page-header.md +1 -1
  73. package/dist/docs/page.json +1 -0
  74. package/dist/docs/page.md +1 -0
  75. package/dist/docs/select.json +1 -1
  76. package/dist/docs/select.md +1 -1
  77. package/dist/docs/skills/build-own-element/SKILL.md +6 -6
  78. package/dist/docs/skills/compose-screen/SKILL.md +13 -6
  79. package/dist/docs/skills/review-screen/SKILL.md +6 -6
  80. package/dist/docs/skills/theme-app/SKILL.md +1 -1
  81. package/dist/docs/skills/waiting-states/SKILL.md +2 -2
  82. package/dist/docs/table.json +13 -0
  83. package/dist/docs/table.md +3 -1
  84. package/dist/docs/tabs.json +22 -4
  85. package/dist/docs/tabs.md +6 -3
  86. package/dist/docs/text-field.json +1 -1
  87. package/dist/docs/text-field.md +1 -1
  88. package/dist/{entity-picker-CPDd73JR.js → entity-picker-BdySwW8F.js} +274 -256
  89. package/dist/entity-picker.js +1 -1
  90. package/dist/{event-calendar-CnF3iIZD.js → event-calendar-CiphPXdV.js} +4 -4
  91. package/dist/event-calendar.js +1 -1
  92. package/dist/{field-NJaCxl2h.js → field-BxQFpOXS.js} +1 -1
  93. package/dist/{field-group-hAzVa2O9.js → field-group-DcN_hiZN.js} +1 -1
  94. package/dist/field-group.js +1 -1
  95. package/dist/field.js +1 -1
  96. package/dist/{filter-bar-D_xDcAp2.js → filter-bar-C_dXljI_.js} +10 -10
  97. package/dist/filter-bar.js +1 -1
  98. package/dist/{hover-card-BJE5dtaQ.js → hover-card-DpVzwvDJ.js} +1 -1
  99. package/dist/hover-card.js +1 -1
  100. package/dist/{import-export-center-DpY2DGBZ.js → import-export-center-BQnMJz4g.js} +3 -3
  101. package/dist/import-export-center.js +1 -1
  102. package/dist/index.js +68 -68
  103. package/dist/{listbox-Dv856z9I.js → listbox-D0MJcAMm.js} +1 -1
  104. package/dist/listbox.js +1 -1
  105. package/dist/{markdown-CeG8mUbI.js → markdown-IiVoh6yi.js} +2 -2
  106. package/dist/markdown.js +1 -1
  107. package/dist/menu-parts-D6v_mz6O.js +809 -0
  108. package/dist/menu.js +1 -1
  109. package/dist/{menubar-b2CruimJ.js → menubar-DApIg1eR.js} +1 -1
  110. package/dist/menubar.js +1 -1
  111. package/dist/{multi-select-DQ5ds0fX.js → multi-select-B8z6ApgW.js} +286 -257
  112. package/dist/multi-select.js +1 -1
  113. package/dist/{notification-center-Sz54IziY.js → notification-center-CGEEHcRA.js} +2 -2
  114. package/dist/notification-center.js +1 -1
  115. package/dist/{number-field-BFIAmv-z.js → number-field-DXk1TAXA.js} +1 -1
  116. package/dist/number-field.js +1 -1
  117. package/dist/{otp-input-BNQhuKaT.js → otp-input-CI-kdNgx.js} +1 -1
  118. package/dist/otp-input.js +1 -1
  119. package/dist/{page-kQgLctsA.js → page-IiYXk1sz.js} +32 -32
  120. package/dist/{page-header-CWEBfMGq.js → page-header-Bn4nIGfF.js} +1 -1
  121. package/dist/page-header.js +1 -1
  122. package/dist/page.js +1 -1
  123. package/dist/{popover-Be7JWbGJ.js → popover-DEp8Bf3e.js} +186 -166
  124. package/dist/popover.js +1 -1
  125. package/dist/{prompt-composer-BxpdkTpZ.js → prompt-composer-DdcU3S3L.js} +1 -1
  126. package/dist/prompt-composer.js +1 -1
  127. package/dist/{radio-group-BaVIP9UH.js → radio-group-D8csBZ8Z.js} +2 -2
  128. package/dist/radio-group.js +1 -1
  129. package/dist/{rating-DYBQBzwB.js → rating-rI3b5zII.js} +1 -1
  130. package/dist/rating.js +1 -1
  131. package/dist/{saved-views-DlE_--Z9.js → saved-views-B3brBTD9.js} +4 -4
  132. package/dist/saved-views.js +1 -1
  133. package/dist/scroll-area-DQ_ansLu.js +112 -0
  134. package/dist/scroll-area.js +1 -1
  135. package/dist/{segmented-control-DmuOGiu6.js → segmented-control-C-PzkTtg.js} +2 -2
  136. package/dist/segmented-control.js +1 -1
  137. package/dist/{select-DOFqPy26.js → select-BUylPwpb.js} +309 -258
  138. package/dist/select.js +1 -1
  139. package/dist/{side-navigation-CBLtCzuJ.js → side-navigation-Bm-BZma1.js} +156 -140
  140. package/dist/side-navigation.js +1 -1
  141. package/dist/{slider-DTes4HLF.js → slider-fp7kyg-6.js} +1 -1
  142. package/dist/slider.js +1 -1
  143. package/dist/{split-button-CPgHiy9G.js → split-button-DKRm0j5c.js} +1 -1
  144. package/dist/split-button.js +1 -1
  145. package/dist/styles.css +1 -1
  146. package/dist/{switch-DQzuIhPX.js → switch-aVoc4C2F.js} +1 -1
  147. package/dist/switch.js +1 -1
  148. package/dist/{table-Cipi2c5X.js → table-BYEvFr4P.js} +206 -203
  149. package/dist/table.js +1 -1
  150. package/dist/tabs-HWmvOKM4.js +355 -0
  151. package/dist/tabs.js +1 -1
  152. package/dist/{tags-input-CcOkm0AW.js → tags-input-pbv6x1Pi.js} +1 -1
  153. package/dist/tags-input.js +1 -1
  154. package/dist/{text-field-DLfIZ0fk.js → text-field-CbQ6UCAP.js} +1 -1
  155. package/dist/text-field.js +1 -1
  156. package/dist/{textarea-CBJBJBi6.js → textarea-DJSfhrXH.js} +1 -1
  157. package/dist/textarea.js +1 -1
  158. package/dist/{time-field-DrhiCtUl.js → time-field-BI9n607q.js} +1 -1
  159. package/dist/time-field.js +1 -1
  160. package/dist/time-picker-D6SlQWNF.js +402 -0
  161. package/dist/time-picker.js +1 -1
  162. package/dist/time-range-picker-CcV3_45h.js +751 -0
  163. package/dist/time-range-picker.js +1 -1
  164. package/dist/{toggle-group-7iBR9Rn1.js → toggle-group-i7JPO8BM.js} +1 -1
  165. package/dist/toggle-group.js +1 -1
  166. package/dist/{toolbar-DDZpEftQ.js → toolbar-B9knmgHA.js} +1 -1
  167. package/dist/toolbar.js +1 -1
  168. package/dist/tooltip-CgjIROUy.js +410 -0
  169. package/dist/tooltip.js +1 -1
  170. package/dist/{top-navigation-e-tWMGNr.js → top-navigation-DiG2PF5W.js} +2 -2
  171. package/dist/top-navigation.js +1 -1
  172. package/dist/{transfer-CqZOTLMP.js → transfer--UOQVeKE.js} +5 -5
  173. package/dist/transfer.js +1 -1
  174. package/dist/types/accordion/accordion.d.ts.map +1 -1
  175. package/dist/types/app-shell/app-shell-parts.d.ts +6 -4
  176. package/dist/types/app-shell/app-shell-parts.d.ts.map +1 -1
  177. package/dist/types/app-shell/app-shell.d.ts.map +1 -1
  178. package/dist/types/calendar/calendar.d.ts +1 -1
  179. package/dist/types/calendar/calendar.d.ts.map +1 -1
  180. package/dist/types/calendar/calendar.types.d.ts +9 -0
  181. package/dist/types/calendar/calendar.types.d.ts.map +1 -1
  182. package/dist/types/calendar/index.d.ts +1 -1
  183. package/dist/types/calendar/index.d.ts.map +1 -1
  184. package/dist/types/cascader/cascader.d.ts.map +1 -1
  185. package/dist/types/chart/chart.d.ts.map +1 -1
  186. package/dist/types/collapsible/collapsible-parts.d.ts.map +1 -1
  187. package/dist/types/combobox/combobox-parts.d.ts.map +1 -1
  188. package/dist/types/date-field/date-field.d.ts +1 -1
  189. package/dist/types/date-field/date-field.d.ts.map +1 -1
  190. package/dist/types/date-field/date-field.types.d.ts +7 -0
  191. package/dist/types/date-field/date-field.types.d.ts.map +1 -1
  192. package/dist/types/date-picker/date-picker.d.ts +5 -1
  193. package/dist/types/date-picker/date-picker.d.ts.map +1 -1
  194. package/dist/types/date-picker/date-picker.types.d.ts +20 -2
  195. package/dist/types/date-picker/date-picker.types.d.ts.map +1 -1
  196. package/dist/types/date-picker/index.d.ts +1 -1
  197. package/dist/types/date-picker/index.d.ts.map +1 -1
  198. package/dist/types/date-range-picker/date-range-picker.d.ts.map +1 -1
  199. package/dist/types/datetime-picker/datetime-picker.d.ts.map +1 -1
  200. package/dist/types/datetime-range-picker/datetime-range-picker.d.ts.map +1 -1
  201. package/dist/types/entity-picker/entity-picker-popup.d.ts.map +1 -1
  202. package/dist/types/menu/menu-parts.d.ts.map +1 -1
  203. package/dist/types/multi-select/multi-select-parts.d.ts.map +1 -1
  204. package/dist/types/page/page.d.ts.map +1 -1
  205. package/dist/types/popover/popover-parts.d.ts.map +1 -1
  206. package/dist/types/scroll-area/scroll-area.d.ts.map +1 -1
  207. package/dist/types/select/select-items.d.ts.map +1 -1
  208. package/dist/types/select/select-parts.d.ts.map +1 -1
  209. package/dist/types/select/select.d.ts.map +1 -1
  210. package/dist/types/side-navigation/side-navigation-parts.d.ts.map +1 -1
  211. package/dist/types/stat-card/stat-card.types.d.ts +2 -2
  212. package/dist/types/stat-card/stat-card.types.d.ts.map +1 -1
  213. package/dist/types/table/index.d.ts +1 -1
  214. package/dist/types/table/index.d.ts.map +1 -1
  215. package/dist/types/table/table-content.d.ts.map +1 -1
  216. package/dist/types/table/table-defaults.d.ts.map +1 -1
  217. package/dist/types/table/table.d.ts +1 -1
  218. package/dist/types/table/table.d.ts.map +1 -1
  219. package/dist/types/table/table.types.d.ts +15 -0
  220. package/dist/types/table/table.types.d.ts.map +1 -1
  221. package/dist/types/tabs/tabs-parts.d.ts +2 -4
  222. package/dist/types/tabs/tabs-parts.d.ts.map +1 -1
  223. package/dist/types/tabs/tabs.d.ts +1 -1
  224. package/dist/types/tabs/tabs.d.ts.map +1 -1
  225. package/dist/types/tabs/tabs.types.d.ts +18 -0
  226. package/dist/types/tabs/tabs.types.d.ts.map +1 -1
  227. package/dist/types/time-picker/time-picker.d.ts.map +1 -1
  228. package/dist/types/time-range-picker/time-range-picker.d.ts.map +1 -1
  229. package/dist/types/tooltip/tooltip-parts.d.ts.map +1 -1
  230. package/dist/{upload-DjnlVR8y.js → upload-CLX5cURk.js} +1 -1
  231. package/dist/upload.js +1 -1
  232. package/package.json +10 -7
  233. package/dist/calendar-B3mio8iQ.js +0 -387
  234. package/dist/chart-O0ZhkzJe.js +0 -966
  235. package/dist/date-field-CYcN6diW.js +0 -279
  236. package/dist/date-picker-4X-_2ltN.js +0 -264
  237. package/dist/date-range-picker-CNOAtHwU.js +0 -471
  238. package/dist/datetime-picker-HDVlUhzy.js +0 -629
  239. package/dist/datetime-range-picker-IqOFJtdW.js +0 -994
  240. package/dist/menu-parts-W58pVNUt.js +0 -786
  241. package/dist/scroll-area-BriwmvYY.js +0 -111
  242. package/dist/tabs-DQ36APY-.js +0 -343
  243. package/dist/time-picker-CRQsMJbQ.js +0 -396
  244. package/dist/time-range-picker-DlZJsj1e.js +0 -745
  245. package/dist/tooltip-DbKTPe_i.js +0 -392
@@ -1,2 +1,2 @@
1
- import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c } from "./datetime-range-picker-IqOFJtdW.js";
1
+ import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c } from "./datetime-range-picker-DQNQwr3K.js";
2
2
  export { n as DATETIME_RANGE_PICKER_MAX_OPTIONS, e as DATETIME_RANGE_PICKER_MONTHS, a as DATETIME_RANGE_PICKER_STEP, c as DateTimeRangePicker, s as DateTimeRangePickerDefaultsProvider, i as DateTimeRangePickerPreset, t as dateTimeRangePickerLengthLabel, o as isSameDateTimeRange, r as useDateTimeRangePickerDefaults };
@@ -2,10 +2,10 @@ import { r as e } from "./i18n-DpW8lqJ1.js";
2
2
  import { n as t } from "./badge-C6rbwVpu.js";
3
3
  import { t as n } from "./skeleton-2LSpg84Z.js";
4
4
  import { n as r } from "./container-CjxY6rYv.js";
5
- import { a as i } from "./page-kQgLctsA.js";
6
- import { w as a } from "./page-header-CWEBfMGq.js";
5
+ import { a as i } from "./page-IiYXk1sz.js";
6
+ import { w as a } from "./page-header-Bn4nIGfF.js";
7
7
  import { a as o, l as s, n as c, o as l, s as u, t as d } from "./section-Be7dX9MC.js";
8
- import { a as f, n as p, o as m, r as h, t as g } from "./tabs-DQ36APY-.js";
8
+ import { a as f, n as p, o as m, r as h, t as g } from "./tabs-HWmvOKM4.js";
9
9
  import { createControllable as _ } from "@slotted/core/state";
10
10
  import { createContext as v, useCallback as y, useContext as b, useEffect as x, useImperativeHandle as S, useMemo as C, useRef as w, useState as T } from "react";
11
11
  import { Fragment as E, jsx as D, jsxs as O } from "react/jsx-runtime";
@@ -1,2 +1,2 @@
1
- import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "./detail-panel-qOJiNuzr.js";
1
+ import { a as e, i as t, n, o as r, r as i, s as a, t as o } from "./detail-panel-z6WGleP4.js";
2
2
  export { t as DetailPanel, o as DetailPanelBody, r as DetailPanelDefaultsProvider, n as DetailPanelSection, i as DetailPanelTabs, e as useDetailPanel, a as useDetailPanelDefaults };
@@ -1,5 +1,7 @@
1
1
  ## Slotted UI (@slotted/react)
2
2
 
3
+ Paths below are relative to the directory that holds node_modules.
4
+
3
5
  Before writing or changing UI, read `node_modules/@slotted/react/dist/docs/index.md`, then the file of each family you use. These files match the installed version: prefer them over prior knowledge of the library.
4
6
 
5
7
  - Import components from `@slotted/react` or the family subpath that each file's Import section shows.
@@ -304,7 +304,7 @@ const navigation = useNavigation();
304
304
 
305
305
  ### The first thing a keyboard meets
306
306
 
307
- Press Tab from the top of the frame: a link to the content appears, then one to the navigation, then one to the pane — one per region that rendered and is open, out of the flow until focused. The content region carries tabindex="-1", so the link moves the focus as well as the scroll.
307
+ Press Tab from the top of the frame: a link to the content appears, then one to the navigation, then one to the pane — one per region that rendered and is open, out of the flow until focused. The link makes the content region focusable for that one focus, so it moves the focus as well as the scroll.
308
308
 
309
309
  Excerpt — not a complete module
310
310
 
@@ -228,9 +228,9 @@
228
228
  },
229
229
  {
230
230
  "part": "main",
231
- "element": "main with tabindex=\"-1\", or the element render returns",
231
+ "element": "main, or the element render returns",
232
232
  "required": true,
233
- "purpose": "The main landmark and the skip link’s destination. Without the tabindex the link moves the scroll and leaves the focus behind, in Safari and in Firefox."
233
+ "purpose": "The main landmark and the skip link’s destination. The skip link and focusMain() write tabindex=\"-1\" for the focus they move, and blur or the next pointer press inside removes it: a main focusable all the time takes the focus of every click on its text, and the next key draws the ring around the page."
234
234
  },
235
235
  {
236
236
  "part": "progress",
@@ -571,7 +571,7 @@
571
571
  "The structure is the WAI-ARIA APG’s Landmark Regions (w3.org/WAI/ARIA/apg/practices/landmark-regions/), and every one of the five is earned from the element rather than written as a role: header in the context of the body is banner, nav is navigation, main is main, aside is complementary, footer is contentinfo. The root is a div for exactly that reason — a main inside a sectioning element stops being a landmark, and the shell has to be placeable anywhere.",
572
572
  "Both controls are the APG’s Disclosure pattern (w3.org/WAI/ARIA/apg/patterns/disclosure/): real buttons, so Enter and Space activate them natively, carrying aria-expanded and aria-controls pointing at their region. One departure is written and costed: under collapsedTo=\"rail\" the navigation does not disappear, it narrows — aria-expanded is still true or false about the region’s full form, and the region stays in the tab order because a rail whose destinations cannot be reached is not a rail. The pane has no such departure: closed, it leaves the layout and the tab order.",
573
573
  "A document with more than one navigation or complementary landmark needs a unique label on each, so both regions carry the family’s strings and a consumer may replace either per instance. Each control’s own name changes with the state — “Open navigation” and “Close navigation”, “Open side panel” and “Close side panel” — because a control whose name never changes leaves the reader to infer the state from aria-expanded alone.",
574
- "The skip links are written from the regions that rendered and are open, so no link ever points at a target nobody built or the reader closed, and main carries tabindex=\"-1\": without it the browser moves the scroll and leaves the focus behind in Safari and Firefox, which is a link that appears to work and moves nothing a screen reader follows.",
574
+ "The skip links are written from the regions that rendered and are open, so no link ever points at a target nobody built or the reader closed, and the main link writes tabindex=\"-1\" on main before it moves the focus: without it the browser moves the scroll and leaves the focus behind in Safari and Firefox. Blur or the next pointer press inside removes the attribute, so a click on the content never focuses main.",
575
575
  "While an overlaid region is modal, @slotted/core/focus contains the keyboard inside it and returns it to the control that opened it on close, and every region beside it — the banner included — takes inert, so Tab cannot reach them and a screen reader does not read them. The ways out are Escape, the veil, a destination, and whatever control the product put in the panel. Two layers can be open together only with both modes forced, and @slotted/core/dismissable-layer keeps one stack, so Escape reaches the topmost.",
576
576
  "The pane’s edge is the APG’s Window Splitter (w3.org/WAI/ARIA/apg/patterns/windowsplitter/): a focusable separator with aria-orientation=\"vertical\", aria-controls pointing at the pane and aria-valuemin, aria-valuemax and aria-valuenow as whole percentages of the shell, named “Resize panel”. ArrowLeft and ArrowRight move it sixteen pixels, sixty-four with Shift, by the direction on the screen — so ArrowLeft grows an end pane and shrinks a start one, mirrored in right-to-left — and Home and End go to the limits.",
577
577
  "Nothing important depends on hover, the frame answers its own inline size rather than the window’s — which is what makes a 400% zoom behave like a narrow window — and every transition comes from the motion scale, so prefers-reduced-motion collapses all of them at once."
@@ -798,7 +798,7 @@
798
798
  {
799
799
  "id": "skipLinks",
800
800
  "title": "The first thing a keyboard meets",
801
- "note": "Press Tab from the top of the frame: a link to the content appears, then one to the navigation, then one to the pane — one per region that rendered and is open, out of the flow until focused. The content region carries tabindex=\"-1\", so the link moves the focus as well as the scroll.",
801
+ "note": "Press Tab from the top of the frame: a link to the content appears, then one to the navigation, then one to the pane — one per region that rendered and is open, out of the flow until focused. The link makes the content region focusable for that one focus, so it moves the focus as well as the scroll.",
802
802
  "language": "tsx",
803
803
  "source": "<AppShell messages={{ skipToMain: 'Skip to the order' }}>\n <AppShellMain>…</AppShellMain>\n</AppShell>;\n",
804
804
  "verdict": "excerpt"
@@ -100,7 +100,7 @@ export default function AppShellBasic() {
100
100
  | `nav-body` | div | no |
101
101
  | `nav-footer` | div | no |
102
102
  | `scrim` | div | no |
103
- | `main` | main with tabindex="-1", or the element render returns | yes |
103
+ | `main` | main, or the element render returns | yes |
104
104
  | `progress` | div, holding one ProgressBar at placement="edge-start" | no |
105
105
  | `aside` | aside, or the element render returns | no |
106
106
  | `aside-resizer` | div with role="separator", or the element render returns | no |
@@ -186,7 +186,7 @@ export default function AppShellBasic() {
186
186
  - The structure is the WAI-ARIA APG’s Landmark Regions (w3.org/WAI/ARIA/apg/practices/landmark-regions/), and every one of the five is earned from the element rather than written as a role: header in the context of the body is banner, nav is navigation, main is main, aside is complementary, footer is contentinfo. The root is a div for exactly that reason — a main inside a sectioning element stops being a landmark, and the shell has to be placeable anywhere.
187
187
  - Both controls are the APG’s Disclosure pattern (w3.org/WAI/ARIA/apg/patterns/disclosure/): real buttons, so Enter and Space activate them natively, carrying aria-expanded and aria-controls pointing at their region. One departure is written and costed: under collapsedTo="rail" the navigation does not disappear, it narrows — aria-expanded is still true or false about the region’s full form, and the region stays in the tab order because a rail whose destinations cannot be reached is not a rail. The pane has no such departure: closed, it leaves the layout and the tab order.
188
188
  - A document with more than one navigation or complementary landmark needs a unique label on each, so both regions carry the family’s strings and a consumer may replace either per instance. Each control’s own name changes with the state — “Open navigation” and “Close navigation”, “Open side panel” and “Close side panel” — because a control whose name never changes leaves the reader to infer the state from aria-expanded alone.
189
- - The skip links are written from the regions that rendered and are open, so no link ever points at a target nobody built or the reader closed, and main carries tabindex="-1": without it the browser moves the scroll and leaves the focus behind in Safari and Firefox, which is a link that appears to work and moves nothing a screen reader follows.
189
+ - The skip links are written from the regions that rendered and are open, so no link ever points at a target nobody built or the reader closed, and the main link writes tabindex="-1" on main before it moves the focus: without it the browser moves the scroll and leaves the focus behind in Safari and Firefox. Blur or the next pointer press inside removes the attribute, so a click on the content never focuses main.
190
190
  - While an overlaid region is modal, @slotted/core/focus contains the keyboard inside it and returns it to the control that opened it on close, and every region beside it — the banner included — takes inert, so Tab cannot reach them and a screen reader does not read them. The ways out are Escape, the veil, a destination, and whatever control the product put in the panel. Two layers can be open together only with both modes forced, and @slotted/core/dismissable-layer keeps one stack, so Escape reaches the topmost.
191
191
  - The pane’s edge is the APG’s Window Splitter (w3.org/WAI/ARIA/apg/patterns/windowsplitter/): a focusable separator with aria-orientation="vertical", aria-controls pointing at the pane and aria-valuemin, aria-valuemax and aria-valuenow as whole percentages of the shell, named “Resize panel”. ArrowLeft and ArrowRight move it sixteen pixels, sixty-four with Shift, by the direction on the screen — so ArrowLeft grows an end pane and shrinks a start one, mirrored in right-to-left — and Home and End go to the limits.
192
192
  - Nothing important depends on hover, the frame answers its own inline size rather than the window’s — which is what makes a 400% zoom behave like a narrow window — and every transition comes from the motion scale, so prefers-reduced-motion collapses all of them at once.
@@ -258,6 +258,13 @@
258
258
  "appliesTo": "Calendar",
259
259
  "description": "The view it opens on."
260
260
  },
261
+ {
262
+ "name": "granularity",
263
+ "type": "'day' | 'month'",
264
+ "defaultValue": "day",
265
+ "appliesTo": "Calendar",
266
+ "description": "What a choice is. month opens on the months of a year and never shows the day grid: a month cell is the value, written as the first day of that month, the arrow keys move one month or one row of four, Home and End reach January and December, Page Up and Page Down move a year, and Enter or Space chooses. A month the bounds leave no day of carries aria-disabled."
267
+ },
261
268
  {
262
269
  "name": "min",
263
270
  "type": "PlainDate",
@@ -51,6 +51,7 @@ Excerpt — not a complete module
51
51
  | `months` | `number` | 1 | Calendar | How many months are drawn side by side. They wrap when they do not fit; nothing is withdrawn. |
52
52
  | `view` | `'day' \| 'month' \| 'year'` | day | Calendar | Which grid is showing. Present means the consumer owns it; defaultView sets the starting one. |
53
53
  | `defaultView` | `'day' \| 'month' \| 'year'` | day | Calendar | The view it opens on. |
54
+ | `granularity` | `'day' \| 'month'` | day | Calendar | What a choice is. month opens on the months of a year and never shows the day grid: a month cell is the value, written as the first day of that month, the arrow keys move one month or one row of four, Home and End reach January and December, Page Up and Page Down move a year, and Enter or Space chooses. A month the bounds leave no day of carries aria-disabled. |
54
55
  | `min` | `PlainDate` | — | Calendar | The earliest date the reader may reach. An arrow that would leave the range stops at the edge. |
55
56
  | `max` | `PlainDate` | — | Calendar | The latest date the reader may reach. |
56
57
  | `isDateUnavailable` | `(date: PlainDate) => boolean` | — | Calendar | Refuses particular dates. A refused date stays focusable and cannot be chosen. |
@@ -416,7 +416,7 @@
416
416
  {
417
417
  "id": "mount",
418
418
  "title": "When the content exists",
419
- "note": "eager builds it up front so the panel can transition closed, lazy builds it on the first open, active drops it each time, and until-found lets the browser’s own page search open the panel.",
419
+ "note": "eager builds it up front so the panel can transition closed, lazy builds it on the first open, active drops it each time, and until-found lets the browser’s own page search open the panel. A closed item under active commits once and reads no layout and no computed style; it creates no observer and no portal. Fifty closed items inside an Accordion, measured in Chrome: 7 ms of script and 23 ms to layout with a production build of React, 46 ms of script and 83 ms to layout with its development build.",
420
420
  "language": "tsx",
421
421
  "source": "<Collapsible mount=\"until-found\">\n <CollapsibleHeader>\n <CollapsibleTrigger>Histórico de entregas</CollapsibleTrigger>\n </CollapsibleHeader>\n <CollapsiblePanel>Entregue em 2 de setembro por Ana Ribeiro.</CollapsiblePanel>\n</Collapsible>;\n",
422
422
  "verdict": "excerpt"
@@ -187,7 +187,7 @@ Excerpt — not a complete module
187
187
 
188
188
  ### When the content exists
189
189
 
190
- eager builds it up front so the panel can transition closed, lazy builds it on the first open, active drops it each time, and until-found lets the browser’s own page search open the panel.
190
+ eager builds it up front so the panel can transition closed, lazy builds it on the first open, active drops it each time, and until-found lets the browser’s own page search open the panel. A closed item under active commits once and reads no layout and no computed style; it creates no observer and no portal. Fifty closed items inside an Accordion, measured in Chrome: 7 ms of script and 23 ms to layout with a production build of React, 46 ms of script and 83 ms to layout with its development build.
191
191
 
192
192
  Excerpt — not a complete module
193
193
 
@@ -125,7 +125,7 @@
125
125
  "type": "PlainDate | null",
126
126
  "defaultValue": "—",
127
127
  "appliesTo": "DateField",
128
- "description": "The date the control shows. Present means the consumer owns it: the control asks and waits rather than moving it."
128
+ "description": "The date the control shows. Present means the consumer owns it: the control asks and waits rather than moving it. PlainDate is exported by @slotted/core/date. parseISODate(text) returns a PlainDate or null, and toISODate(date) returns 'YYYY-MM-DD': value={parseISODate(iso)} onValueChange={(v) => setIso(v === null ? '' : toISODate(v))}."
129
129
  },
130
130
  {
131
131
  "name": "defaultValue",
@@ -134,6 +134,13 @@
134
134
  "appliesTo": "DateField",
135
135
  "description": "The date the control starts from when nobody else writes one. Read once; a later change to it is not new state."
136
136
  },
137
+ {
138
+ "name": "granularity",
139
+ "type": "'day' | 'month'",
140
+ "defaultValue": "day",
141
+ "appliesTo": "DateField",
142
+ "description": "What the control holds. month shows the month and the year segments in the locale’s order, takes the first day of the month as its value, and submits YYYY-MM through name."
143
+ },
137
144
  {
138
145
  "name": "min",
139
146
  "type": "PlainDate",
@@ -38,8 +38,9 @@ Excerpt — not a complete module
38
38
 
39
39
  | Name | Type | Default | Applies to | Description |
40
40
  | --- | --- | --- | --- | --- |
41
- | `value` | `PlainDate \| null` | — | DateField | The date the control shows. Present means the consumer owns it: the control asks and waits rather than moving it. |
41
+ | `value` | `PlainDate \| null` | — | DateField | The date the control shows. Present means the consumer owns it: the control asks and waits rather than moving it. PlainDate is exported by @slotted/core/date. parseISODate(text) returns a PlainDate or null, and toISODate(date) returns 'YYYY-MM-DD': value={parseISODate(iso)} onValueChange={(v) => setIso(v === null ? '' : toISODate(v))}. |
42
42
  | `defaultValue` | `PlainDate \| null` | — | DateField | The date the control starts from when nobody else writes one. Read once; a later change to it is not new state. |
43
+ | `granularity` | `'day' \| 'month'` | day | DateField | What the control holds. month shows the month and the year segments in the locale’s order, takes the first day of the month as its value, and submits YYYY-MM through name. |
43
44
  | `min` | `PlainDate` | — | DateField | The earliest date the control accepts. Reported through each segment’s aria-valuemin, and a value below it makes the control invalid. |
44
45
  | `max` | `PlainDate` | — | DateField | The latest date the control accepts, on the same terms as min. |
45
46
  | `isDateUnavailable` | `(date: PlainDate) => boolean` | — | DateField | Refuses particular dates — a weekend, a holiday, a day already booked. A refused date makes the control invalid. |
@@ -740,6 +740,35 @@ export default function DatePickerInvoiceDue() {
740
740
  }
741
741
  ```
742
742
 
743
+ ### iso-date.tsx
744
+
745
+ Example
746
+
747
+ ```tsx
748
+ import { useState } from 'react';
749
+ import { DatePicker } from '@slotted/react/date-picker';
750
+ import { Field, FieldDescription, FieldLabel } from '@slotted/react/field';
751
+ import { isPlainDate, parseISODate, toISODate } from '@slotted/core/date';
752
+
753
+ export default function DatePickerIsoDate() {
754
+ /* The API carries the date as text; the picker holds a PlainDate. */
755
+ const [iso, setIso] = useState('2026-10-19');
756
+
757
+ return (
758
+ <Field>
759
+ <FieldLabel>Statement date</FieldLabel>
760
+ <DatePicker
761
+ onValueChange={(value) => setIso(isPlainDate(value) ? toISODate(value) : '')}
762
+ value={parseISODate(iso)}
763
+ />
764
+ <FieldDescription>
765
+ {iso === '' ? 'No date sent.' : `The API receives ${iso}.`}
766
+ </FieldDescription>
767
+ </Field>
768
+ );
769
+ }
770
+ ```
771
+
743
772
  ### locale.tsx
744
773
 
745
774
  Example
@@ -135,7 +135,7 @@
135
135
  "type": "PlainDate | DateRange | PlainDate[] | null",
136
136
  "defaultValue": "—",
137
137
  "appliesTo": "DatePicker",
138
- "description": "The selected value, whose shape follows selectionMode. Setting it makes the value controlled."
138
+ "description": "The selected value, whose shape follows selectionMode. Setting it makes the value controlled. PlainDate is exported by @slotted/core/date. parseISODate(text) returns a PlainDate or null, toISODate(date) returns 'YYYY-MM-DD', and isPlainDate(value) narrows the CalendarValue onValueChange reports: value={parseISODate(iso)} onValueChange={(v) => setIso(isPlainDate(v) ? toISODate(v) : '')}."
139
139
  },
140
140
  {
141
141
  "name": "defaultValue",
@@ -179,6 +179,13 @@
179
179
  "appliesTo": "DatePicker",
180
180
  "description": "Returns true for a date the reader cannot choose. The calendar disables it, and a typed unavailable date marks the date picker invalid."
181
181
  },
182
+ {
183
+ "name": "granularity",
184
+ "type": "'day' | 'month'",
185
+ "defaultValue": "day",
186
+ "appliesTo": "DatePicker",
187
+ "description": "What a choice is. month opens the calendar on the months of a year and chooses a month cell; the field shows the month and the year segments; value, defaultValue, min and max are PlainYearMonth from @slotted/core/date ({ year, month }), onValueChange reports one, and name submits YYYY-MM. parseISOYearMonth and toISOYearMonth convert to and from that text. selectionMode, months, isDateUnavailable and the week props do not apply."
188
+ },
182
189
  {
183
190
  "name": "selectionMode",
184
191
  "type": "'single' | 'range' | 'multiple'",
@@ -627,6 +634,14 @@
627
634
  "source": "import { useState } from 'react';\nimport { DatePicker } from '@slotted/react/date-picker';\nimport { Field, FieldDescription, FieldLabel } from '@slotted/react/field';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@slotted/react/select';\nimport { Stack } from '@slotted/react/stack';\nimport {\n addDays,\n createDateFormat,\n isPlainDate,\n plainDate,\n type PlainDate,\n} from '@slotted/core/date';\n\nconst TERMS = [\n { value: 'receipt', label: 'Due on receipt', days: 0 },\n { value: 'net-15', label: 'Net 15', days: 15 },\n { value: 'net-30', label: 'Net 30', days: 30 },\n { value: 'net-60', label: 'Net 60', days: 60 },\n];\nconst words = createDateFormat({ locale: 'en-US' });\n\nexport default function DatePickerInvoiceDue() {\n const [issued, setIssued] = useState<PlainDate | null>(plainDate(2026, 9, 30));\n const [terms, setTerms] = useState('net-30');\n const days = TERMS.find((option) => option.value === terms)?.days ?? 0;\n const due = issued === null ? null : addDays(issued, days);\n\n return (\n <Stack gap={5}>\n <Field required>\n <FieldLabel>Invoice date</FieldLabel>\n <DatePicker\n onValueChange={(value) => setIssued(isPlainDate(value) ? value : null)}\n value={issued}\n />\n </Field>\n <Field>\n <FieldLabel>Payment terms</FieldLabel>\n <Select\n items={TERMS}\n onValueChange={(next) => {\n if (typeof next === 'string') setTerms(next);\n }}\n value={terms}\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {TERMS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </Field>\n <Field readOnly>\n <FieldLabel>Due date</FieldLabel>\n <DatePicker value={due} />\n <FieldDescription>\n {due === null ? 'Set the invoice date first.' : `Payment is due ${words.describe(due)}.`}\n </FieldDescription>\n </Field>\n </Stack>\n );\n}\n",
628
635
  "verdict": "example"
629
636
  },
637
+ {
638
+ "id": "iso-date",
639
+ "title": "iso-date.tsx",
640
+ "note": null,
641
+ "language": "tsx",
642
+ "source": "import { useState } from 'react';\nimport { DatePicker } from '@slotted/react/date-picker';\nimport { Field, FieldDescription, FieldLabel } from '@slotted/react/field';\nimport { isPlainDate, parseISODate, toISODate } from '@slotted/core/date';\n\nexport default function DatePickerIsoDate() {\n /* The API carries the date as text; the picker holds a PlainDate. */\n const [iso, setIso] = useState('2026-10-19');\n\n return (\n <Field>\n <FieldLabel>Statement date</FieldLabel>\n <DatePicker\n onValueChange={(value) => setIso(isPlainDate(value) ? toISODate(value) : '')}\n value={parseISODate(iso)}\n />\n <FieldDescription>\n {iso === '' ? 'No date sent.' : `The API receives ${iso}.`}\n </FieldDescription>\n </Field>\n );\n}\n",
643
+ "verdict": "example"
644
+ },
630
645
  {
631
646
  "id": "locale",
632
647
  "title": "locale.tsx",
@@ -44,13 +44,14 @@ export default function DatePickerBasic() {
44
44
 
45
45
  | Name | Type | Default | Applies to | Description |
46
46
  | --- | --- | --- | --- | --- |
47
- | `value` | `PlainDate \| DateRange \| PlainDate[] \| null` | — | DatePicker | The selected value, whose shape follows selectionMode. Setting it makes the value controlled. |
47
+ | `value` | `PlainDate \| DateRange \| PlainDate[] \| null` | — | DatePicker | The selected value, whose shape follows selectionMode. Setting it makes the value controlled. PlainDate is exported by @slotted/core/date. parseISODate(text) returns a PlainDate or null, toISODate(date) returns 'YYYY-MM-DD', and isPlainDate(value) narrows the CalendarValue onValueChange reports: value={parseISODate(iso)} onValueChange={(v) => setIso(isPlainDate(v) ? toISODate(v) : '')}. |
48
48
  | `defaultValue` | `PlainDate \| DateRange \| PlainDate[] \| null` | — | DatePicker | The starting value of an uncontrolled date picker. Read once, on the first render. |
49
49
  | `open` | `boolean` | — | DatePicker | Whether the calendar is showing. Setting it makes the open state controlled. |
50
50
  | `defaultOpen` | `boolean` | false | DatePicker | Whether the calendar shows on the first render. Focus does not move into it. |
51
51
  | `min` | `PlainDate` | — | DatePicker | The earliest date the field and the calendar accept. Earlier days are disabled in the calendar, and an earlier typed date marks the date picker invalid. |
52
52
  | `max` | `PlainDate` | — | DatePicker | The latest date the field and the calendar accept. Later days are disabled in the calendar, and a later typed date marks the date picker invalid. |
53
53
  | `isDateUnavailable` | `(date: PlainDate) => boolean` | — | DatePicker | Returns true for a date the reader cannot choose. The calendar disables it, and a typed unavailable date marks the date picker invalid. |
54
+ | `granularity` | `'day' \| 'month'` | day | DatePicker | What a choice is. month opens the calendar on the months of a year and chooses a month cell; the field shows the month and the year segments; value, defaultValue, min and max are PlainYearMonth from @slotted/core/date ({ year, month }), onValueChange reports one, and name submits YYYY-MM. parseISOYearMonth and toISOYearMonth convert to and from that text. selectionMode, months, isDateUnavailable and the week props do not apply. |
54
55
  | `selectionMode` | `'single' \| 'range' \| 'multiple'` | single | DatePicker | How many dates the calendar selects, and so the shape of the value. The field shows a single date only; for a start and an end, use DateRangePicker. |
55
56
  | `months` | `number` | 1 | DatePicker | How many months the calendar draws side by side. |
56
57
  | `defaultView` | `'day' \| 'month' \| 'year'` | day | DatePicker | The grid the calendar opens on. |
@@ -14,8 +14,8 @@ screen:
14
14
  2. Match the task against the `triggers` in each topic file's front matter.
15
15
  Read the topic files that match, and `composition.md` always.
16
16
  3. Before writing an element beside a family, read that family's page in
17
- `node_modules/@slotted/react/dist/docs`. Before writing an element no family draws, read
18
- `own-elements.md` and `node_modules/@slotted/react/dist/docs/tokens.md`.
17
+ `..`. Before writing an element no family draws, read
18
+ `own-elements.md` and `../tokens.md`.
19
19
  4. Apply the rules by level: a `MUST` broken is a defect, a `SHOULD` broken is
20
20
  stated with its reason, a `MAY` is a permission.
21
21
  5. A screen is done when each `Check` of the rules read passes.
@@ -127,3 +127,13 @@ Why: The line beside Reply and Internal note was longer in one mode and wrapped
127
127
  Do: Give the varying text `flex: 1 1 0`, `min-inline-size: 0` and a one-line cut, write the two texts to similar lengths, and keep a control's label set fixed across modes.
128
128
  Don't: Let a mode add or remove a line, a hint or a control that changes the region's height, or let a row wrap differently in one mode than in the other.
129
129
  Check: At 1280, 570 and 390 pixels, switch the mode and read the bounding box of each box in the region: every top and height is the same in both modes.
130
+
131
+ ### CP-09 · A control that switches a region does not render the region in its own frame
132
+
133
+ Level: MUST
134
+ Applies to: tabs, a segmented control, a route link or a filter whose change mounts a region holding tables, grids, charts or forms
135
+ Rule: The control's own state — the selected tab, the pressed segment — updates in the interaction's frame; the region renders from a deferred value (in React, useDeferredValue or startTransition) and states aria-busy while the shown value lags the selected one.
136
+ Why: Mounting a region of families in the click's task blocks the next paint, and Interaction to Next Paint above 200 ms is "needs improvement" and above 500 ms "poor": a table of a hundred rows of twenty cells measured 160 ms in one task in a development build, and a 4× CPU throttle takes its 93 ms in a production build past the first threshold.
137
+ Do: Keep the selection state urgent and hand the region the deferred value; mount only the selected panel, with mount active.
138
+ Don't: Set the selection and the region from one state update inside the click handler, or mount every panel to make switching look instant.
139
+ Check: Switch the control with the CPU throttled 4× in Chrome: the selection marker moves in the next frame, and the Performance panel shows no long task between the click and that paint.
@@ -29,6 +29,7 @@ Every active rule, one line each. Load this first; load a topic file when the ta
29
29
  | CP-06 | SHOULD | A screen states a state once. Where the family has a state for it, the screen sets that state and adds nothing louder beside it. | `composition.md` |
30
30
  | CP-07 | SHOULD | A list page is `bleed` with `fill`; a record, a dashboard and an overview keep the default `wide`; a create or edit form is `content`; a page of preferences read as prose is `measure`. | `composition.md` |
31
31
  | CP-08 | MUST | Switching the mode changes the words and the values in the region, never the size, the line count or the position of any box in it or around it; text whose length differs between modes stands on one line, cut with an ellipsis and read whole from its title. | `composition.md` |
32
+ | CP-09 | MUST | The control's own state — the selected tab, the pressed segment — updates in the interaction's frame; the region renders from a deferred value (in React, useDeferredValue or startTransition) and states aria-busy while the shown value lags the selected one. | `composition.md` |
32
33
  | CV-01 | MUST | From 600 pixels the conversation fills its region and scrolls on its own with the composer under it, and the record's context scrolls on its own beside it or behind a tab of the header; below 600 pixels the page scrolls, the composer is stuck to the bottom of the screen, and the record opens on its last message. | `conversations.md` |
33
34
  | CV-02 | SHOULD | A running clock says the business time left and the hour it falls due in the organization's zone, a passed one by how much and when it was due, a paused one that it is stopped and why; a queue's row says the span alone, and no clock is ever said in raw minutes. | `conversations.md` |
34
35
  | CV-03 | SHOULD | Each queue is a view of the list's `filter-bar`, a tree of the bar's own fields, labelled with the count of the rows it opens on; choosing it writes its pills, and editing a pill leaves a view the bar marks as changed. | `conversations.md` |
@@ -72,12 +73,13 @@ Every active rule, one line each. Load this first; load a topic file when the ta
72
73
  | LP-11 | SHOULD | Where the primary input is a touch screen, the list tools leave out the row density: the theme raises every density's rows to the touch target, and the tools keep the sort, the views and the columns on one line. | `list-pages.md` |
73
74
  | LP-12 | MUST | From 600 pixels of the page's width the listing takes the height the bar and the messages leave and scrolls its own rows while the page does not scroll; below 600 the page's `fill` does not apply, the listing is as tall as its rows and the page scrolls. | `list-pages.md` |
74
75
  | LP-13 | SHOULD | The mark takes the `sm` `size`; the default `md` belongs to a record's header and to a region with room around it. | `list-pages.md` |
76
+ | LP-14 | MUST | A list that can hold more than a hundred rows, or more than twenty columns, is a `data-grid`, which windows rows and middle columns under its default `virtualize` of `auto`; `table` is for sets the page bounds, such as a statement's lines or a record's related rows. | `list-pages.md` |
75
77
  | OV-01 | SHOULD | The figure leads to the list page of the records it counts, and that list opens with the figure's condition applied as the bar's own pills, which the reader removes like any other. | `overviews.md` |
76
78
  | OV-02 | SHOULD | Below 600 pixels the figures are one `list` of rows that are destinations — label, comparison, number — followed by the records to act on, the work in flight, the charts, and the history last. | `overviews.md` |
77
79
  | OV-03 | SHOULD | Where the reader's scope holds one category, the chart is not drawn and one sentence states the numbers it would have compared. | `overviews.md` |
78
80
  | OV-04 | SHOULD | The thresholds are stated once in words beside the figures, and a figure past one says so in its own comparison line, on the line the placeholder reserves. | `overviews.md` |
79
81
  | OV-05 | SHOULD | A chart takes the `card` `variant` when it is one of two or more peer tiles in one grid with no section heading of its own, and stays `plain` when it is the body of a titled section or the only content of a tab. | `overviews.md` |
80
- | OE-01 | MUST | An element of the application's own reads only tokens of the groups `node_modules/@slotted/react/dist/docs/tokens.md` lists for the application. | `own-elements.md` |
82
+ | OE-01 | MUST | An element of the application's own reads only tokens of the groups `../tokens.md` lists for the application. | `own-elements.md` |
81
83
  | OE-02 | MUST | Where two states hold at once, the element paints the ground, the edge and the text colour of the stronger, in the order `disabled`, `loading`, `readonly`, `invalid`, `checked`, `selected`, `pressed`, `current`, `complete`, `active`, `hover`; the focus ring of OE-03 is drawn over every state. | `own-elements.md` |
82
84
  | OE-03 | MUST | In its `focus-visible` state the element draws an outline of `--slotted-focus-ring-width` in `--slotted-focus-ring-color`, offset by `--slotted-focus-ring-offset`. | `own-elements.md` |
83
85
  | OE-04 | MUST | The element's hit area measures at least `--slotted-target-min` in both axes, and at least `--slotted-target-comfortable` under `pointer: coarse`. | `own-elements.md` |
@@ -27,7 +27,8 @@ triggers:
27
27
  badge,
28
28
  tag,
29
29
  ]
30
- families: [data-grid, filter-bar, action-bar, pagination, page, page-header, link, badge, tag]
30
+ families:
31
+ [data-grid, table, filter-bar, action-bar, pagination, page, page-header, link, badge, tag]
31
32
  ---
32
33
 
33
34
  # List pages
@@ -163,3 +164,13 @@ Why: A row sets its text at the small type step, 13 pixels in a 34-pixel row; an
163
164
  Do: Give every mark a cell holds — a state, a flag, a warning — the `sm` `size`: 12-pixel words in a 19-pixel box.
164
165
  Don't: Leave a cell's mark at the family's default, or size it up to make a state stand out; a tone says that.
165
166
  Check: On every list, every `badge` and `tag` inside a cell of a `data-grid` or a `table` carries the `sm` `size`.
167
+
168
+ ### LP-14 · A list that can pass a hundred rows is a data-grid
169
+
170
+ Level: MUST
171
+ Applies to: a list of records whose count the reader does not bound — transactions, postings, imports
172
+ Rule: A list that can hold more than a hundred rows, or more than twenty columns, is a `data-grid`, which windows rows and middle columns under its default `virtualize` of `auto`; `table` is for sets the page bounds, such as a statement's lines or a record's related rows.
173
+ Why: `table` mounts every cell: a hundred rows of twenty columns measured 93 ms in one task with a production build of React in Chrome and 160 ms with a development build, and the count of rows the store returns sets that cost.
174
+ Do: Give the store's list a `data-grid` with its query, and keep `table` for the bounded sets inside a record or a document.
175
+ Don't: Render an unbounded store into a `table`, or page a `table` to keep it under a hundred rows where the grid's window would have kept the whole set reachable.
176
+ Check: Every list over an unbounded store is a `data-grid`; every `table` on a page renders a set the page bounds.
@@ -18,7 +18,7 @@ the problem, the family is used instead (CP-04).
18
18
 
19
19
  Level: MUST
20
20
  Applies to: every element of the application's own that paints, spaces or moves
21
- Rule: An element of the application's own reads only tokens of the groups `node_modules/@slotted/react/dist/docs/tokens.md` lists for the application.
21
+ Rule: An element of the application's own reads only tokens of the groups `../tokens.md` lists for the application.
22
22
  Why: A token of a family-only group is that family's adjustment and changes with it; an application group is the theme's promise.
23
23
  Do: A strip of day cells spaces with `--slotted-space-2`, paints a busy day with `--slotted-tone-warning-surface` and rounds with `--slotted-radius-sm`.
24
24
  Don't: A cell takes its height from `--slotted-avatar-size-sm` because the number fit.
@@ -412,6 +412,7 @@
412
412
  "--slotted-line-height-md",
413
413
  "--slotted-line-height-sm",
414
414
  "--slotted-line-height-xl",
415
+ "--slotted-page-header-backdrop-filter",
415
416
  "--slotted-page-header-background",
416
417
  "--slotted-page-header-border",
417
418
  "--slotted-page-header-control-size",
@@ -105,7 +105,7 @@ Excerpt — not a complete module
105
105
 
106
106
  ## Tokens
107
107
 
108
- `--slotted-border-subtle`, `--slotted-border-width-thin`, `--slotted-control-gap-md`, `--slotted-control-gap-sm`, `--slotted-control-height-md`, `--slotted-control-height-sm`, `--slotted-control-icon-size-md`, `--slotted-control-icon-size-sm`, `--slotted-control-radius`, `--slotted-disabled-foreground`, `--slotted-duration-medium`, `--slotted-duration-short`, `--slotted-easing-standard`, `--slotted-focus-ring-color`, `--slotted-focus-ring-offset`, `--slotted-focus-ring-width`, `--slotted-font-family-heading`, `--slotted-font-family-sans`, `--slotted-font-size-2xl`, `--slotted-font-size-3xl`, `--slotted-font-size-lg`, `--slotted-font-size-md`, `--slotted-font-size-sm`, `--slotted-font-size-xl`, `--slotted-font-weight-medium`, `--slotted-font-weight-semibold`, `--slotted-line-height-2xl`, `--slotted-line-height-3xl`, `--slotted-line-height-lg`, `--slotted-line-height-md`, `--slotted-line-height-sm`, `--slotted-line-height-xl`, `--slotted-page-header-background`, `--slotted-page-header-border`, `--slotted-page-header-control-size`, `--slotted-page-header-description-measure`, `--slotted-page-header-gap`, `--slotted-page-header-heading-size-lg`, `--slotted-page-header-heading-size-md`, `--slotted-page-header-heading-size-sm`, `--slotted-page-header-inset`, `--slotted-page-header-padding-block`, `--slotted-page-header-radius`, `--slotted-page-header-shadow`, `--slotted-page-header-snapped-heading-size-lg`, `--slotted-page-header-snapped-heading-size-md`, `--slotted-page-header-snapped-heading-size-sm`, `--slotted-radius-full`, `--slotted-radius-lg`, `--slotted-radius-sm`, `--slotted-shadow-sm`, `--slotted-space-1`, `--slotted-space-2`, `--slotted-space-3`, `--slotted-space-4`, `--slotted-space-5`, `--slotted-space-6`, `--slotted-surface`, `--slotted-target-comfortable`, `--slotted-target-min`, `--slotted-text`, `--slotted-text-muted`, `--slotted-tone-accent-subtle-hover`, `--slotted-tone-accent-surface`, `--slotted-tone-accent-text`, `--slotted-tone-secondary-subtle-active`, `--slotted-tone-secondary-subtle-hover`, `--slotted-toolbar-inset`, `--slotted-z-index-sticky`
108
+ `--slotted-border-subtle`, `--slotted-border-width-thin`, `--slotted-control-gap-md`, `--slotted-control-gap-sm`, `--slotted-control-height-md`, `--slotted-control-height-sm`, `--slotted-control-icon-size-md`, `--slotted-control-icon-size-sm`, `--slotted-control-radius`, `--slotted-disabled-foreground`, `--slotted-duration-medium`, `--slotted-duration-short`, `--slotted-easing-standard`, `--slotted-focus-ring-color`, `--slotted-focus-ring-offset`, `--slotted-focus-ring-width`, `--slotted-font-family-heading`, `--slotted-font-family-sans`, `--slotted-font-size-2xl`, `--slotted-font-size-3xl`, `--slotted-font-size-lg`, `--slotted-font-size-md`, `--slotted-font-size-sm`, `--slotted-font-size-xl`, `--slotted-font-weight-medium`, `--slotted-font-weight-semibold`, `--slotted-line-height-2xl`, `--slotted-line-height-3xl`, `--slotted-line-height-lg`, `--slotted-line-height-md`, `--slotted-line-height-sm`, `--slotted-line-height-xl`, `--slotted-page-header-backdrop-filter`, `--slotted-page-header-background`, `--slotted-page-header-border`, `--slotted-page-header-control-size`, `--slotted-page-header-description-measure`, `--slotted-page-header-gap`, `--slotted-page-header-heading-size-lg`, `--slotted-page-header-heading-size-md`, `--slotted-page-header-heading-size-sm`, `--slotted-page-header-inset`, `--slotted-page-header-padding-block`, `--slotted-page-header-radius`, `--slotted-page-header-shadow`, `--slotted-page-header-snapped-heading-size-lg`, `--slotted-page-header-snapped-heading-size-md`, `--slotted-page-header-snapped-heading-size-sm`, `--slotted-radius-full`, `--slotted-radius-lg`, `--slotted-radius-sm`, `--slotted-shadow-sm`, `--slotted-space-1`, `--slotted-space-2`, `--slotted-space-3`, `--slotted-space-4`, `--slotted-space-5`, `--slotted-space-6`, `--slotted-surface`, `--slotted-target-comfortable`, `--slotted-target-min`, `--slotted-text`, `--slotted-text-muted`, `--slotted-tone-accent-subtle-hover`, `--slotted-tone-accent-surface`, `--slotted-tone-accent-text`, `--slotted-tone-secondary-subtle-active`, `--slotted-tone-secondary-subtle-hover`, `--slotted-toolbar-inset`, `--slotted-z-index-sticky`
109
109
 
110
110
  ## Examples
111
111
 
@@ -219,6 +219,7 @@
219
219
  "accessibility": [
220
220
  "The practice is the WAI-ARIA APG’s Landmark Regions (w3.org/WAI/ARIA/apg/practices/landmark-regions/): each page should have one main landmark, and the page is it. landmark=\"none\" is for the page that is not the document’s — inside a drawer, a dialog or the detail pane of a master–detail layout — and it renders a div so the document keeps one main.",
221
221
  "The footer is a footer element inside main, which is not a contentinfo landmark, so it is named: the catalog’s footer message by default, an aria-label the consumer writes otherwise. A reader walking the page’s controls finds “Page actions” before the buttons in it.",
222
+ "Content outside PageBody receives no inset: the inset lives on the body, so content written straight into Page is drawn against the viewport edges. In development, Page warns once when it holds content and no PageBody.",
222
223
  "The outline is typography’s. The body is one HeadingLevel deeper than the header, so a Section written directly in it takes h2 under the header’s h1, and a section inside that takes h3 — without either family reading the other, and without a skip the HTML Standard forbids.",
223
224
  "The body carries aria-busy while loading, which is what tells a screen reader the region is not ready. The page draws no placeholder: the header stands skeleton text of the title’s own width, and each family inside stands its own.",
224
225
  "Scrolling is the platform’s. The page never writes a scroll position, and under scroll=\"self\" the scroll part is an ordinary scrolling box a keyboard reaches with the arrow keys once something inside it has focus."
package/dist/docs/page.md CHANGED
@@ -64,6 +64,7 @@ Excerpt — not a complete module
64
64
 
65
65
  - The practice is the WAI-ARIA APG’s Landmark Regions (w3.org/WAI/ARIA/apg/practices/landmark-regions/): each page should have one main landmark, and the page is it. landmark="none" is for the page that is not the document’s — inside a drawer, a dialog or the detail pane of a master–detail layout — and it renders a div so the document keeps one main.
66
66
  - The footer is a footer element inside main, which is not a contentinfo landmark, so it is named: the catalog’s footer message by default, an aria-label the consumer writes otherwise. A reader walking the page’s controls finds “Page actions” before the buttons in it.
67
+ - Content outside PageBody receives no inset: the inset lives on the body, so content written straight into Page is drawn against the viewport edges. In development, Page warns once when it holds content and no PageBody.
67
68
  - The outline is typography’s. The body is one HeadingLevel deeper than the header, so a Section written directly in it takes h2 under the header’s h1, and a section inside that takes h3 — without either family reading the other, and without a skip the HTML Standard forbids.
68
69
  - The body carries aria-busy while loading, which is what tells a screen reader the region is not ready. The page draws no placeholder: the header stands skeleton text of the title’s own width, and each family inside stands its own.
69
70
  - Scrolling is the platform’s. The page never writes a scroll position, and under scroll="self" the scroll part is an ordinary scrolling box a keyboard reaches with the arrow keys once something inside it has focus.
@@ -483,7 +483,7 @@
483
483
  "type": "SelectItems",
484
484
  "defaultValue": "—",
485
485
  "appliesTo": "Select",
486
- "description": "Values with their labels, so the closed control can show the label of a value whose option has not been rendered. Options are rendered only while the list is open."
486
+ "description": "Values with their labels, read before any option renders. Without it, a held value takes its label from its SelectItem: while the list is closed and a held value has no label, the options render once outside the document and register theirs. Pass items when the options are not rendered until the list opens, such as options fetched on open."
487
487
  },
488
488
  {
489
489
  "name": "name",
@@ -100,7 +100,7 @@ export default function SelectBasic() {
100
100
  | `defaultOpen` | `boolean` | false | Select | Whether an uncontrolled select starts open. |
101
101
  | `highlightedValue` | `string \| null` | — | Select | The highlighted option. Passing it makes the highlight controlled. The highlight is where the keyboard is, not what is chosen. |
102
102
  | `defaultHighlightedValue` | `string \| null` | — | Select | The option an uncontrolled highlight starts on. |
103
- | `items` | `SelectItems` | — | Select | Values with their labels, so the closed control can show the label of a value whose option has not been rendered. Options are rendered only while the list is open. |
103
+ | `items` | `SelectItems` | — | Select | Values with their labels, read before any option renders. Without it, a held value takes its label from its SelectItem: while the list is closed and a held value has no label, the options render once outside the document and register theirs. Pass items when the options are not rendered until the list opens, such as options fetched on open. |
104
104
  | `name` | `string` | — | Select | The name the value is submitted under. It renders a hidden native select, which FormData reads and a form reset restores. |
105
105
  | `form` | `string` | — | Select | The id of the form the control belongs to, when it is not the enclosing form. |
106
106
  | `total` | `number` | — | Select | The size of the whole collection when only part of it is rendered. Options with position then carry aria-setsize and aria-posinset. |
@@ -11,9 +11,9 @@ built from the same theme as the families beside it.
11
11
 
12
12
  ## 1. Prove that no family answers
13
13
 
14
- 1. Read `node_modules/@slotted/react/dist/docs/index.md` and list the families whose line is near the
14
+ 1. Read `../../index.md` and list the families whose line is near the
15
15
  element.
16
- 2. Read `node_modules/@slotted/react/dist/docs/<family>.md` for each: its anatomy, its slots, its axes.
16
+ 2. Read `../../<family>.md` for each: its anatomy, its slots, its axes.
17
17
  When a family draws the element, or names a slot that holds it, the family
18
18
  is used (CP-01).
19
19
  3. When a family draws a part of the element — a toggle, a popup, a list, a
@@ -33,7 +33,7 @@ Free, decided by the problem:
33
33
  Fixed, read from the theme:
34
34
 
35
35
  - every value it paints comes from a token of the group
36
- `node_modules/@slotted/react/dist/docs/tokens.md` lists for that decision (OE-01, CP-05);
36
+ `../../tokens.md` lists for that decision (OE-01, CP-05);
37
37
  - a tone carries the meaning the `tone` group gives it, and a data series takes
38
38
  the `data` slots in order;
39
39
  - how its states yield, how focus is drawn, how large a target is, what forced
@@ -43,7 +43,7 @@ A new appearance is a new combination of tokens, never a new value.
43
43
 
44
44
  ## 3. Map each decision to a group
45
45
 
46
- | Decision | Group in `node_modules/@slotted/react/dist/docs/tokens.md` |
46
+ | Decision | Group in `../../tokens.md` |
47
47
  | ---------------------------------------------- | --------------------------------------------------- |
48
48
  | Distance between parts, inner padding | `space` |
49
49
  | Corners | `radius` |
@@ -84,7 +84,7 @@ why no token covers it (CP-05).
84
84
 
85
85
  ## 5. Done
86
86
 
87
- 1. Each `Check` of `node_modules/@slotted/react/dist/docs/guidelines/own-elements.md` passes.
87
+ 1. Each `Check` of `../../guidelines/own-elements.md` passes.
88
88
  2. The element's CSS holds no colour or length literal without its comment.
89
89
  3. Every custom property the element reads is listed in
90
- `node_modules/@slotted/react/dist/docs/tokens.md` above "Read by their family only".
90
+ `../../tokens.md` above "Read by their family only".
@@ -28,18 +28,18 @@ topics.
28
28
 
29
29
  ## 2. Read
30
30
 
31
- 1. `node_modules/@slotted/react/dist/docs/guidelines/index.md`.
32
- 2. `node_modules/@slotted/react/dist/docs/guidelines/composition.md`, always.
31
+ 1. `../../guidelines/index.md`.
32
+ 2. `../../guidelines/composition.md`, always.
33
33
  3. The topic of step 1. Add `layout-alignment.md` and `shell-composition.md`
34
34
  when the screen places controls in a row or in the application frame.
35
35
  4. Add `errors-and-conflicts.md` and `reading-permissions.md` when the screen
36
36
  writes data or reads under a role.
37
37
  5. When the screen writes an element of its own, follow
38
- `node_modules/@slotted/react/dist/docs/skills/build-own-element/SKILL.md`.
38
+ `../../skills/build-own-element/SKILL.md`.
39
39
 
40
40
  ## 3. Read each family before writing beside it
41
41
 
42
- For each family the screen uses, read `node_modules/@slotted/react/dist/docs/<family>.md` — its anatomy,
42
+ For each family the screen uses, read `../../<family>.md` — its anatomy,
43
43
  its slots, its axes (CP-01). When the family names a slot for an element, the
44
44
  element stands in that slot.
45
45
 
@@ -59,14 +59,18 @@ element stands in that slot.
59
59
  nothing louder beside it (CP-06).
60
60
  - A control that switches what a region holds changes words and values, never
61
61
  the size or the position of a box (CP-08).
62
+ - A control that switches a region — tabs, a route, a filter — updates its
63
+ own state at once and renders the region from a deferred value (in React,
64
+ useDeferredValue or startTransition) (CP-09). A list longer than a hundred
65
+ rows is a `data-grid`, which windows its rows (LP-14).
62
66
  - Where no anatomy answers the problem, the screen writes an element of its
63
67
  own instead of bending a family into a shape it does not have (CP-04). That
64
- element is built with `node_modules/@slotted/react/dist/docs/skills/build-own-element/SKILL.md`.
68
+ element is built with `../../skills/build-own-element/SKILL.md`.
65
69
 
66
70
  ## 5. Waits
67
71
 
68
72
  Every region filled by a request and every control that starts one follow
69
- `node_modules/@slotted/react/dist/docs/skills/waiting-states/SKILL.md`.
73
+ `../../skills/waiting-states/SKILL.md`.
70
74
 
71
75
  ## 6. Done
72
76
 
@@ -74,3 +78,6 @@ Every region filled by a request and every control that starts one follow
74
78
  2. Each `SHOULD` rule not followed is listed with its reason.
75
79
  3. No CSS of the screen's own targets a family's parts; a family's appearance
76
80
  changes through its axes.
81
+ 4. In a browser with the CPU throttled 4×, each control that switches a region
82
+ paints its new state in the next frame, with no long task between the input
83
+ and that paint.
@@ -12,20 +12,20 @@ A review answers one question per rule read: does the screen pass the rule's
12
12
 
13
13
  1. List the screen's files: its component, its template, its stylesheet and
14
14
  the requests it starts.
15
- 2. List the families it imports, and read `node_modules/@slotted/react/dist/docs/<family>.md` for each.
15
+ 2. List the families it imports, and read `../../<family>.md` for each.
16
16
  3. List the elements of the screen's own: every element that is not a family,
17
17
  with the CSS that paints it.
18
18
  4. Classify the screen with the table in step 1 of
19
- `node_modules/@slotted/react/dist/docs/skills/compose-screen/SKILL.md`.
19
+ `../../skills/compose-screen/SKILL.md`.
20
20
 
21
21
  ## 2. Read
22
22
 
23
- 1. `node_modules/@slotted/react/dist/docs/guidelines/index.md` and
24
- `node_modules/@slotted/react/dist/docs/guidelines/composition.md`.
23
+ 1. `../../guidelines/index.md` and
24
+ `../../guidelines/composition.md`.
25
25
  2. The topics of the classification, and each topic whose `triggers` the
26
26
  screen matches.
27
27
  3. `own-elements.md` when step 1 listed an element of the screen's own.
28
- 4. `node_modules/@slotted/react/dist/docs/skills/waiting-states/SKILL.md` when the screen starts a
28
+ 4. `../../skills/waiting-states/SKILL.md` when the screen starts a
29
29
  request.
30
30
 
31
31
  ## 3. Sweep the code
@@ -65,6 +65,6 @@ deviation. The screen passes when no `MUST` finding remains.
65
65
  1. Fix every `MUST` finding, then each `SHOULD` finding the request covers.
66
66
  2. A fix uses what the family offers — its state, its slot, its axis — and
67
67
  never CSS on its parts.
68
- 3. A literal becomes a token of the group `node_modules/@slotted/react/dist/docs/tokens.md` lists for
68
+ 3. A literal becomes a token of the group `../../tokens.md` lists for
69
69
  that decision.
70
70
  4. Run the searches of step 3 again on the changed files: none finds anything.
@@ -17,7 +17,7 @@ Take the first row that reaches the change.
17
17
  | The brand colour, or the colour of one intent | a seed: `--slotted-seed-accent` or another name of `SEED_NAMES` | the theme's `seeds`, or a scope |
18
18
  | Light, dark, high contrast, or the system's preference | the scheme: `light`, `dark`, `high-contrast`, `system` | `data-slotted-scheme`, or the scheme of the `ThemeScope` component from `@slotted/react/theme` |
19
19
  | How tight controls and rows are | the density: `comfortable`, `compact`, `dense` | `data-slotted-density`, or the density of the `ThemeScope` component from `@slotted/react/theme` |
20
- | Spacing, corners, type, lines, shadows or motion | a token of a group in `node_modules/@slotted/react/dist/docs/tokens.md` | the theme's `base`; `schemes` for a value per scheme; `densities` per density |
20
+ | Spacing, corners, type, lines, shadows or motion | a token of a group in `../../tokens.md` | the theme's `base`; `schemes` for a value per scheme; `densities` per density |
21
21
  | One family's look | a token the family's page lists under Tokens | the theme's `components` |
22
22
  | What no token reaches | a theme rule | the theme's `rules` |
23
23