plass-ui 1.6.0 → 1.8.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 (373) hide show
  1. package/CHANGELOG.md +494 -0
  2. package/README.md +5 -3
  3. package/dist/components/accordion/PlAccordion.d.ts +19 -1
  4. package/dist/components/accordion/PlAccordion.js +1 -1
  5. package/dist/components/accordion/index.d.ts +1 -1
  6. package/dist/components/alert/PlAlert.js +1 -1
  7. package/dist/components/anchor/PlAnchor.js +1 -1
  8. package/dist/components/animate-appear/PlAnimateAppear.js +1 -1
  9. package/dist/components/animate-counter/PlAnimateCounter.d.ts +5 -2
  10. package/dist/components/animate-counter/PlAnimateCounter.js +1 -1
  11. package/dist/components/animate-float/PlAnimateFloat.js +1 -1
  12. package/dist/components/animate-headline/PlAnimateHeadline.d.ts +10 -1
  13. package/dist/components/animate-headline/PlAnimateHeadline.js +1 -1
  14. package/dist/components/animate-lighting/PlAnimateLighting.js +1 -1
  15. package/dist/components/animate-marquee/PlAnimateMarquee.d.ts +8 -1
  16. package/dist/components/animate-marquee/PlAnimateMarquee.js +1 -1
  17. package/dist/components/animate-scramble/PlAnimateScramble.js +1 -1
  18. package/dist/components/animate-shake/PlAnimateShake.js +1 -1
  19. package/dist/components/animate-split/PlAnimateSplit.js +1 -1
  20. package/dist/components/animate-typing/PlAnimateTyping.js +1 -1
  21. package/dist/components/app-logo/PlAppLogo.d.ts +51 -15
  22. package/dist/components/app-logo/PlAppLogo.js +1 -1
  23. package/dist/components/area-chart/PlAreaChart.d.ts +35 -6
  24. package/dist/components/area-chart/PlAreaChart.js +1 -1
  25. package/dist/components/aspect-ratio/PlAspectRatio.js +1 -1
  26. package/dist/components/avatar/PlAvatar.js +1 -1
  27. package/dist/components/back-top/PlBackTop.d.ts +5 -5
  28. package/dist/components/back-top/PlBackTop.js +1 -1
  29. package/dist/components/badge/PlBadge.js +1 -1
  30. package/dist/components/bar-chart/PlBarChart.d.ts +54 -2
  31. package/dist/components/bar-chart/PlBarChart.js +1 -1
  32. package/dist/components/blockquote/PlBlockquote.d.ts +2 -2
  33. package/dist/components/blockquote/PlBlockquote.js +1 -1
  34. package/dist/components/bottom-navigation/PlBottomNavigation.d.ts +15 -5
  35. package/dist/components/bottom-navigation/PlBottomNavigation.js +1 -1
  36. package/dist/components/breadcrumb/PlBreadcrumb.d.ts +16 -3
  37. package/dist/components/breadcrumb/PlBreadcrumb.js +1 -1
  38. package/dist/components/button/PlButton.d.ts +7 -0
  39. package/dist/components/button/PlButton.js +1 -1
  40. package/dist/components/calendar/PlCalendar.js +1 -1
  41. package/dist/components/card/PlCard.d.ts +9 -5
  42. package/dist/components/card/PlCard.js +1 -1
  43. package/dist/components/carousel/PlCarousel.d.ts +14 -4
  44. package/dist/components/carousel/PlCarousel.js +1 -1
  45. package/dist/components/chat-bubble/PlChatBubble.js +1 -1
  46. package/dist/components/checkbox/PlCheckbox.js +1 -1
  47. package/dist/components/chip/PlChip.d.ts +2 -1
  48. package/dist/components/chip/PlChip.js +1 -1
  49. package/dist/components/code-block/PlCodeBlock.js +1 -1
  50. package/dist/components/collapsible/PlCollapsible.d.ts +11 -0
  51. package/dist/components/collapsible/PlCollapsible.js +1 -1
  52. package/dist/components/color-picker/PlColorPicker.d.ts +3 -1
  53. package/dist/components/color-picker/PlColorPicker.js +1 -1
  54. package/dist/components/combobox/PlCombobox.d.ts +11 -5
  55. package/dist/components/combobox/PlCombobox.js +1 -1
  56. package/dist/components/command-palette/PlCommandPalette.js +1 -1
  57. package/dist/components/confirm/PlConfirmProvider.js +1 -1
  58. package/dist/components/data-table/PlDataTable.d.ts +13 -2
  59. package/dist/components/data-table/PlDataTable.js +1 -1
  60. package/dist/components/date-picker/PlDatePicker.js +1 -1
  61. package/dist/components/date-range-picker/PlDateRangePicker.js +1 -1
  62. package/dist/components/date-time-picker/PlDateTimePicker.js +1 -1
  63. package/dist/components/drawer/PlDrawer.d.ts +27 -2
  64. package/dist/components/drawer/PlDrawer.js +1 -1
  65. package/dist/components/empty/PlEmpty.d.ts +4 -1
  66. package/dist/components/empty/PlEmpty.js +1 -1
  67. package/dist/components/fieldset/PlFieldset.js +1 -1
  68. package/dist/components/file-picker/PlFilePicker.d.ts +32 -1
  69. package/dist/components/file-picker/PlFilePicker.js +1 -1
  70. package/dist/components/file-picker/index.d.ts +1 -1
  71. package/dist/components/floating-action-button/PlFloatingActionButton.d.ts +5 -0
  72. package/dist/components/floating-action-button/PlFloatingActionButton.js +1 -1
  73. package/dist/components/floating-bottom-navigation/PlFloatingBottomNavigation.d.ts +10 -0
  74. package/dist/components/floating-bottom-navigation/PlFloatingBottomNavigation.js +1 -1
  75. package/dist/components/footer/PlFooter.d.ts +3 -1
  76. package/dist/components/footer/PlFooter.js +1 -1
  77. package/dist/components/gallery/PlGallery.js +1 -1
  78. package/dist/components/gauge-chart/PlGaugeChart.d.ts +11 -2
  79. package/dist/components/gauge-chart/PlGaugeChart.js +1 -1
  80. package/dist/components/header/PlHeader.d.ts +4 -2
  81. package/dist/components/header/PlHeader.js +1 -1
  82. package/dist/components/heatmap-chart/PlHeatmapChart.d.ts +1 -1
  83. package/dist/components/heatmap-chart/PlHeatmapChart.js +1 -1
  84. package/dist/components/hover-card/PlHoverCard.js +1 -1
  85. package/dist/components/how-to-steps/PlHowToSteps.js +1 -1
  86. package/dist/components/image/PlImage.js +1 -1
  87. package/dist/components/image/PlImagePreview.d.ts +3 -2
  88. package/dist/components/line-chart/PlLineChart.d.ts +32 -6
  89. package/dist/components/line-chart/PlLineChart.js +1 -1
  90. package/dist/components/list/PlList.d.ts +16 -5
  91. package/dist/components/list/PlList.js +1 -1
  92. package/dist/components/menu/PlMenu.d.ts +19 -7
  93. package/dist/components/menu/PlMenu.js +1 -1
  94. package/dist/components/menubar/PlMenubar.js +1 -1
  95. package/dist/components/meter/PlMeter.js +1 -1
  96. package/dist/components/mockup/PlMockup.d.ts +6 -0
  97. package/dist/components/mockup/PlMockup.js +1 -1
  98. package/dist/components/modal/PlModal.js +1 -1
  99. package/dist/components/navigation-menu/PlNavigationMenu.d.ts +56 -11
  100. package/dist/components/navigation-menu/PlNavigationMenu.js +1 -1
  101. package/dist/components/number-field/PlNumberField.d.ts +7 -5
  102. package/dist/components/number-field/PlNumberField.js +1 -1
  103. package/dist/components/otp-field/PlOtpField.js +1 -1
  104. package/dist/components/overlay/PlOverlay.js +1 -1
  105. package/dist/components/page-layout/PlPageLayout.d.ts +14 -3
  106. package/dist/components/page-layout/PlPageLayout.js +1 -1
  107. package/dist/components/pagination/PlPagination.js +1 -1
  108. package/dist/components/panes/PlPanes.d.ts +4 -1
  109. package/dist/components/panes/PlPanes.js +1 -1
  110. package/dist/components/pie-chart/PlPieChart.d.ts +26 -1
  111. package/dist/components/pie-chart/PlPieChart.js +1 -1
  112. package/dist/components/pill/PlPill.js +1 -1
  113. package/dist/components/popconfirm/PlPopconfirm.d.ts +8 -1
  114. package/dist/components/popconfirm/PlPopconfirm.js +1 -1
  115. package/dist/components/popover/PlPopover.js +1 -1
  116. package/dist/components/progress-box/PlProgressBox.js +1 -1
  117. package/dist/components/progress-circular/PlProgressCircular.d.ts +11 -1
  118. package/dist/components/progress-circular/PlProgressCircular.js +1 -1
  119. package/dist/components/progress-linear/PlProgressLinear.js +1 -1
  120. package/dist/components/radio-group/PlRadioGroup.js +1 -1
  121. package/dist/components/rating/PlRating.js +1 -1
  122. package/dist/components/scatter-chart/PlScatterChart.js +1 -1
  123. package/dist/components/scroll-area/PlScrollArea.js +1 -1
  124. package/dist/components/scroll-zone/PlScrollZone.js +1 -1
  125. package/dist/components/segmented-button/PlSegmentedButton.js +1 -1
  126. package/dist/components/select/PlSelect.d.ts +4 -1
  127. package/dist/components/select/PlSelect.js +1 -1
  128. package/dist/components/sidebar/PlSidebar.d.ts +12 -0
  129. package/dist/components/sidebar/PlSidebar.js +1 -1
  130. package/dist/components/slider/PlSlider.d.ts +2 -1
  131. package/dist/components/slider/PlSlider.js +1 -1
  132. package/dist/components/sparkline/PlSparkline.d.ts +15 -3
  133. package/dist/components/sparkline/PlSparkline.js +1 -1
  134. package/dist/components/stepper/PlStepper.d.ts +11 -0
  135. package/dist/components/stepper/PlStepper.js +1 -1
  136. package/dist/components/switch/PlSwitch.js +1 -1
  137. package/dist/components/table/PlTable.d.ts +11 -1
  138. package/dist/components/table/PlTable.js +1 -1
  139. package/dist/components/tabs/PlTabs.d.ts +18 -1
  140. package/dist/components/tabs/PlTabs.js +1 -1
  141. package/dist/components/text-field/PlTextField.d.ts +6 -8
  142. package/dist/components/text-field/PlTextField.js +1 -1
  143. package/dist/components/text-link/PlTextLink.js +1 -1
  144. package/dist/components/time-picker/PlTimePicker.js +1 -1
  145. package/dist/components/timeline-chart/PlTimelineChart.js +1 -1
  146. package/dist/components/toast/PlToast.js +1 -1
  147. package/dist/components/toggle/PlToggle.js +1 -1
  148. package/dist/components/tooltip/PlTooltip.d.ts +4 -0
  149. package/dist/components/tooltip/PlTooltip.js +1 -1
  150. package/dist/components/tour/PlTour.js +1 -1
  151. package/dist/components/transfer/PlTransfer.d.ts +5 -2
  152. package/dist/components/transfer/PlTransfer.js +1 -1
  153. package/dist/components/tree/PlTree.d.ts +12 -0
  154. package/dist/components/tree/PlTree.js +1 -1
  155. package/dist/components/tree-select/PlTreeSelect.js +1 -1
  156. package/dist/components/typography/PlTypography.d.ts +20 -7
  157. package/dist/components/typography/PlTypography.js +1 -1
  158. package/dist/components/visually-hidden/PlVisuallyHidden.d.ts +12 -3
  159. package/dist/components/visually-hidden/PlVisuallyHidden.js +1 -1
  160. package/dist/components/window-pane/PlWindowPane.d.ts +16 -3
  161. package/dist/components/window-pane/PlWindowPane.js +1 -1
  162. package/dist/css/accordion.css +4 -0
  163. package/dist/css/alert.css +5 -0
  164. package/dist/css/anchor.css +6 -0
  165. package/dist/css/animate-appear.css +3 -0
  166. package/dist/css/animate-blink.css +3 -0
  167. package/dist/css/animate-counter.css +4 -0
  168. package/dist/css/animate-fade.css +3 -0
  169. package/dist/css/animate-float.css +3 -0
  170. package/dist/css/animate-grow.css +3 -0
  171. package/dist/css/animate-headline.css +3 -0
  172. package/dist/css/animate-lighting.css +4 -0
  173. package/dist/css/animate-marquee.css +4 -0
  174. package/dist/css/animate-reveal.css +3 -0
  175. package/dist/css/animate-rotate.css +3 -0
  176. package/dist/css/animate-scramble.css +5 -0
  177. package/dist/css/animate-shake.css +3 -0
  178. package/dist/css/animate-slide.css +3 -0
  179. package/dist/css/animate-split.css +4 -0
  180. package/dist/css/animate-typing.css +4 -0
  181. package/dist/css/animate-zoom.css +3 -0
  182. package/dist/css/app-logo.css +2 -0
  183. package/dist/css/area-chart.css +12 -0
  184. package/dist/css/aspect-ratio.css +2 -0
  185. package/dist/css/avatar.css +2 -0
  186. package/dist/css/back-top.css +11 -0
  187. package/dist/css/badge.css +2 -0
  188. package/dist/css/bar-chart.css +10 -0
  189. package/dist/css/base.css +2 -3
  190. package/dist/css/blockquote.css +2 -0
  191. package/dist/css/bottom-navigation.css +4 -0
  192. package/dist/css/box.css +2 -0
  193. package/dist/css/breadcrumb.css +6 -0
  194. package/dist/css/button-group.css +2 -0
  195. package/dist/css/button.css +7 -0
  196. package/dist/css/calendar.css +17 -0
  197. package/dist/css/card.css +2 -0
  198. package/dist/css/carousel.css +10 -0
  199. package/dist/css/chat-bubble.css +6 -0
  200. package/dist/css/checkbox.css +3 -0
  201. package/dist/css/chip.css +8 -0
  202. package/dist/css/code-block.css +6 -0
  203. package/dist/css/collapsible.css +3 -0
  204. package/dist/css/color-picker.css +17 -0
  205. package/dist/css/combobox.css +10 -0
  206. package/dist/css/command-palette.css +7 -0
  207. package/dist/css/confirm.css +9 -0
  208. package/dist/css/container.css +5 -0
  209. package/dist/css/data-list.css +2 -0
  210. package/dist/css/data-table.css +15 -0
  211. package/dist/css/date-picker.css +16 -0
  212. package/dist/css/date-range-picker.css +16 -0
  213. package/dist/css/date-time-picker.css +16 -0
  214. package/dist/css/divider.css +2 -0
  215. package/dist/css/drawer.css +6 -0
  216. package/dist/css/empty.css +2 -0
  217. package/dist/css/fieldset.css +2 -0
  218. package/dist/css/file-picker.css +8 -0
  219. package/dist/css/flex.css +5 -0
  220. package/dist/css/floating-action-button.css +7 -0
  221. package/dist/css/floating-bottom-navigation.css +5 -0
  222. package/dist/css/footer.css +6 -0
  223. package/dist/css/form.css +2 -0
  224. package/dist/css/gallery.css +15 -0
  225. package/dist/css/gauge-chart.css +11 -0
  226. package/dist/css/grid.css +5 -0
  227. package/dist/css/header.css +6 -0
  228. package/dist/css/heatmap-chart.css +10 -0
  229. package/dist/css/highlight.css +2 -0
  230. package/dist/css/hot-keys.css +3 -0
  231. package/dist/css/hover-card.css +9 -0
  232. package/dist/css/how-to-steps.css +3 -0
  233. package/dist/css/icon-button.css +7 -0
  234. package/dist/css/icon.css +1 -0
  235. package/dist/css/image.css +7 -0
  236. package/dist/css/line-chart.css +12 -0
  237. package/dist/css/list.css +2 -0
  238. package/dist/css/menu.css +5 -0
  239. package/dist/css/menubar.css +5 -0
  240. package/dist/css/meter.css +6 -0
  241. package/dist/css/mockup.css +4 -0
  242. package/dist/css/modal.css +5 -0
  243. package/dist/css/navigation-menu.css +4 -0
  244. package/dist/css/number-field.css +10 -0
  245. package/dist/css/otp-field.css +6 -0
  246. package/dist/css/overlay.css +4 -0
  247. package/dist/css/page-layout.css +7 -0
  248. package/dist/css/pagination.css +8 -0
  249. package/dist/css/panes.css +7 -0
  250. package/dist/css/pie-chart.css +10 -0
  251. package/dist/css/pill.css +4 -0
  252. package/dist/css/popconfirm.css +12 -0
  253. package/dist/css/popover.css +11 -0
  254. package/dist/css/portal.css +1 -0
  255. package/dist/css/progress-box.css +5 -0
  256. package/dist/css/progress-circular.css +5 -0
  257. package/dist/css/progress-linear.css +5 -0
  258. package/dist/css/radio-group.css +3 -0
  259. package/dist/css/rating.css +6 -0
  260. package/dist/css/scatter-chart.css +10 -0
  261. package/dist/css/scroll-area.css +2 -0
  262. package/dist/css/scroll-zone.css +15 -0
  263. package/dist/css/segmented-button.css +5 -0
  264. package/dist/css/select.css +9 -0
  265. package/dist/css/show.css +1 -0
  266. package/dist/css/sidebar.css +16 -0
  267. package/dist/css/skeleton.css +2 -0
  268. package/dist/css/slider.css +3 -0
  269. package/dist/css/sparkline.css +10 -0
  270. package/dist/css/spoiler.css +10 -0
  271. package/dist/css/stack.css +5 -0
  272. package/dist/css/stat.css +2 -0
  273. package/dist/css/stepper.css +8 -0
  274. package/dist/css/switch.css +3 -0
  275. package/dist/css/table.css +3 -0
  276. package/dist/css/tabs.css +7 -0
  277. package/dist/css/text-field.css +8 -0
  278. package/dist/css/text-link.css +6 -0
  279. package/dist/css/time-picker.css +16 -0
  280. package/dist/css/timeline-chart.css +10 -0
  281. package/dist/css/timeline.css +6 -0
  282. package/dist/css/toast.css +5 -0
  283. package/dist/css/toggle.css +5 -0
  284. package/dist/css/toolbar.css +2 -0
  285. package/dist/css/tooltip.css +9 -0
  286. package/dist/css/tour.css +10 -0
  287. package/dist/css/transfer.css +13 -0
  288. package/dist/css/tree-select.css +14 -0
  289. package/dist/css/tree.css +3 -0
  290. package/dist/css/visually-hidden.css +2 -0
  291. package/dist/css/window-pane.css +9 -0
  292. package/dist/internal/animate.d.ts +59 -4
  293. package/dist/internal/animate.js +1 -1
  294. package/dist/internal/bottom-bar.d.ts +29 -0
  295. package/dist/internal/bottom-bar.js +1 -0
  296. package/dist/internal/calendar.js +1 -1
  297. package/dist/internal/chart-frame.d.ts +216 -19
  298. package/dist/internal/chart-frame.js +1 -1
  299. package/dist/internal/chart-line.d.ts +11 -6
  300. package/dist/internal/chart-line.js +1 -1
  301. package/dist/internal/chart.d.ts +149 -1
  302. package/dist/internal/chart.js +1 -1
  303. package/dist/internal/commit-change.d.ts +38 -0
  304. package/dist/internal/commit-change.js +1 -0
  305. package/dist/internal/data-table.d.ts +2 -2
  306. package/dist/internal/data-table.js +1 -1
  307. package/dist/internal/date.d.ts +9 -0
  308. package/dist/internal/date.js +1 -1
  309. package/dist/internal/defaults.d.ts +36 -3
  310. package/dist/internal/defaults.js +1 -1
  311. package/dist/internal/direction.d.ts +8 -2
  312. package/dist/internal/direction.js +1 -1
  313. package/dist/internal/focusable.d.ts +10 -0
  314. package/dist/internal/focusable.js +1 -1
  315. package/dist/internal/form.d.ts +72 -0
  316. package/dist/internal/form.js +1 -1
  317. package/dist/internal/format.d.ts +4 -1
  318. package/dist/internal/format.js +1 -1
  319. package/dist/internal/gallery.d.ts +28 -0
  320. package/dist/internal/gallery.js +1 -1
  321. package/dist/internal/glow.d.ts +39 -3
  322. package/dist/internal/glow.js +1 -1
  323. package/dist/internal/heading.d.ts +11 -0
  324. package/dist/internal/heading.js +1 -0
  325. package/dist/internal/highlight.d.ts +4 -2
  326. package/dist/internal/highlight.js +1 -1
  327. package/dist/internal/icons.d.ts +24 -0
  328. package/dist/internal/icons.js +1 -1
  329. package/dist/internal/labels.d.ts +27 -0
  330. package/dist/internal/labels.js +1 -1
  331. package/dist/internal/layout-box.d.ts +35 -0
  332. package/dist/internal/layout-box.js +1 -0
  333. package/dist/internal/lazy.d.ts +37 -0
  334. package/dist/internal/lazy.js +1 -0
  335. package/dist/internal/loading.d.ts +13 -0
  336. package/dist/internal/loading.js +1 -0
  337. package/dist/internal/media.d.ts +6 -0
  338. package/dist/internal/media.js +1 -1
  339. package/dist/internal/mockup.js +1 -1
  340. package/dist/internal/notch.d.ts +57 -3
  341. package/dist/internal/notch.js +1 -1
  342. package/dist/internal/page-layout.d.ts +24 -13
  343. package/dist/internal/page-layout.js +1 -1
  344. package/dist/internal/picker.d.ts +8 -2
  345. package/dist/internal/picker.js +1 -1
  346. package/dist/internal/progress.d.ts +41 -6
  347. package/dist/internal/progress.js +1 -1
  348. package/dist/internal/search.d.ts +12 -0
  349. package/dist/internal/search.js +1 -1
  350. package/dist/internal/sizer.d.ts +18 -10
  351. package/dist/internal/sizer.js +1 -1
  352. package/dist/internal/steps.d.ts +0 -15
  353. package/dist/internal/steps.js +1 -1
  354. package/dist/internal/styles.d.ts +136 -14
  355. package/dist/internal/styles.js +1 -1
  356. package/dist/internal/table-scroll.d.ts +42 -0
  357. package/dist/internal/table-scroll.js +1 -0
  358. package/dist/internal/table.js +1 -1
  359. package/dist/internal/text.d.ts +8 -5
  360. package/dist/internal/wheel.d.ts +13 -1
  361. package/dist/internal/wheel.js +1 -1
  362. package/dist/internal/window.js +1 -1
  363. package/dist/locales/de.js +1 -1
  364. package/dist/locales/es.js +1 -1
  365. package/dist/locales/fr.js +1 -1
  366. package/dist/locales/ja.js +1 -1
  367. package/dist/locales/ko.js +1 -1
  368. package/dist/locales/zh-Hans.js +1 -1
  369. package/dist/styles.css +1 -1
  370. package/dist/tailwind.css +1163 -81
  371. package/dist/tokens.css +1163 -81
  372. package/dist/types.d.ts +226 -13
  373. package/package.json +18 -17
package/dist/tailwind.css CHANGED
@@ -52,8 +52,11 @@
52
52
  * glass around it, and the `accent` — the one value that has to be *read* off a
53
53
  * surface rather than looked at.
54
54
  *
55
- * Three values are hand-picked per family and everything else is derived with
56
- * `color-mix()`. Adding a family is still five places, though: one entry in
55
+ * Three values are hand-picked per family here, and an `accent` in each theme
56
+ * block below. Everything else is derived from those: `-fill` is a
57
+ * `linear-gradient()` between the two ends, `-tint`, `-soft` and `-line` are a
58
+ * `color-mix()` of `solid` or `accent` with `transparent`, and `-ring` is
59
+ * `accent` itself. Adding a family is still five places, though: one entry in
57
60
  * `PlassColor`, those three lines here, an `accent` in each of the three theme
58
61
  * blocks below, the eight derived declarations, which are written out per
59
62
  * family rather than generated, and the Flutter package's own enum and
@@ -172,6 +175,262 @@
172
175
  --plass-breakpoint-xl: var(--breakpoint-xl, 80rem);
173
176
  }
174
177
 
178
+ /* ---------------------------------------------------------------------------
179
+ * The room a fixed bottom bar takes
180
+ *
181
+ * A `PlBottomNavigation` or a `PlFloatingBottomNavigation` is `fixed` by
182
+ * default: held against the bottom of the window and out of the flow, so the
183
+ * end of the page is always under it, and so is a link a reader tabs to down
184
+ * there. Only the page can make room, and only if it knows how much, so a fixed
185
+ * bar measures itself — size, density, names and the home indicator included —
186
+ * and writes the answer here, on the root element, for as long as it is
187
+ * mounted. With no bar it is `0px`, so a page can reserve it unconditionally:
188
+ *
189
+ * main { padding-bottom: var(--plass-bottom-navigation-height); }
190
+ * html { scroll-padding-bottom: var(--plass-bottom-navigation-height); }
191
+ *
192
+ * On a bare `:root`, for the reason `--plass-z-portal` is. It is a reading
193
+ * rather than a setting: a bar replaces whatever is written here the moment it
194
+ * has measured itself.
195
+ * ------------------------------------------------------------------------- */
196
+
197
+ :root {
198
+ --plass-bottom-navigation-height: 0px;
199
+ }
200
+
201
+ /* ---------------------------------------------------------------------------
202
+ * The room a pinned header takes in a page layout
203
+ *
204
+ * A `PlHeader` with `position="fixed"` is out of the flow, so a `PlPageLayout`
205
+ * pads its content down by the header's height and starts its sidebars there.
206
+ * A `sticky` one across the whole width is in the flow, but it stays across
207
+ * the top of the window, so the sidebars under it start below it and are that
208
+ * much shorter. The layout measures the height and writes it on its root — but
209
+ * only once JavaScript runs, so the HTML a server sends would be drawn with
210
+ * the content under a fixed header, or with the sidebars under a sticky one as
211
+ * tall as the whole window, which pushes a short page's footer a header's
212
+ * height down. Both would move a moment later, when the page hydrates.
213
+ *
214
+ * These rules give that first paint the same room out of what the stylesheet
215
+ * already knows: the header's floor for its `size`, which is the ladder of
216
+ * `barMinHeightClasses` in `PlHeader.tsx` spelled in the same `--spacing`, and
217
+ * the hairlines along its top and bottom edges. A header whose content is
218
+ * taller than its floor is still measured, and the measurement is an inline
219
+ * style on the same root, so it replaces all of this the moment it is written.
220
+ *
221
+ * Two paths for a fixed header, because it is the layout's own child or, with
222
+ * `headerSpan="content"`, its content column's. A sticky header counts only as
223
+ * the layout's own child: in the content column it sits beside the sidebars
224
+ * rather than over them. A `PlHeader` any deeper is one the page drew rather
225
+ * than the layout's.
226
+ *
227
+ * Every layout starts from zero rather than from what it inherits, so a layout
228
+ * inside another one's page does not take the outer header's room as its own.
229
+ * ------------------------------------------------------------------------- */
230
+
231
+ .plass-layout {
232
+ --p-layout-header: 0px;
233
+ --p-layout-header-inset: 0px;
234
+ --p-layout-header-floor: 0px;
235
+ --p-layout-header-rule: 0px;
236
+ }
237
+
238
+ .plass-layout:has(> .plass-header-fixed, > div > div > .plass-header-fixed) {
239
+ --p-layout-header: calc(var(--p-layout-header-floor) + var(--p-layout-header-rule));
240
+ --p-layout-header-inset: var(--p-layout-header);
241
+ }
242
+
243
+ .plass-layout:has(> .plass-header-sticky) {
244
+ --p-layout-header: calc(var(--p-layout-header-floor) + var(--p-layout-header-rule));
245
+ }
246
+
247
+ .plass-layout:has(
248
+ > .plass-header-fixed[data-size='xs'],
249
+ > div > div > .plass-header-fixed[data-size='xs'],
250
+ > .plass-header-sticky[data-size='xs']
251
+ ) {
252
+ --p-layout-header-floor: calc(var(--spacing, 0.25rem) * 10);
253
+ }
254
+
255
+ .plass-layout:has(
256
+ > .plass-header-fixed[data-size='sm'],
257
+ > div > div > .plass-header-fixed[data-size='sm'],
258
+ > .plass-header-sticky[data-size='sm']
259
+ ) {
260
+ --p-layout-header-floor: calc(var(--spacing, 0.25rem) * 12);
261
+ }
262
+
263
+ .plass-layout:has(
264
+ > .plass-header-fixed[data-size='md'],
265
+ > div > div > .plass-header-fixed[data-size='md'],
266
+ > .plass-header-sticky[data-size='md']
267
+ ) {
268
+ --p-layout-header-floor: calc(var(--spacing, 0.25rem) * 16);
269
+ }
270
+
271
+ .plass-layout:has(
272
+ > .plass-header-fixed[data-size='lg'],
273
+ > div > div > .plass-header-fixed[data-size='lg'],
274
+ > .plass-header-sticky[data-size='lg']
275
+ ) {
276
+ --p-layout-header-floor: calc(var(--spacing, 0.25rem) * 20);
277
+ }
278
+
279
+ .plass-layout:has(
280
+ > .plass-header-fixed[data-size='xl'],
281
+ > div > div > .plass-header-fixed[data-size='xl'],
282
+ > .plass-header-sticky[data-size='xl']
283
+ ) {
284
+ --p-layout-header-floor: calc(var(--spacing, 0.25rem) * 24);
285
+ }
286
+
287
+ /* The divider is a line along the bottom; a glass sheet has an edge all round,
288
+ which the divider only colours. Later, so the glass wins where both match. */
289
+ .plass-layout:has(
290
+ > .plass-header-fixed[data-divider],
291
+ > div > div > .plass-header-fixed[data-divider],
292
+ > .plass-header-sticky[data-divider]
293
+ ) {
294
+ --p-layout-header-rule: 1px;
295
+ }
296
+
297
+ .plass-layout:has(
298
+ > .plass-header-fixed[data-variant='glass'],
299
+ > div > div > .plass-header-fixed[data-variant='glass'],
300
+ > .plass-header-sticky[data-variant='glass']
301
+ ) {
302
+ --p-layout-header-rule: 2px;
303
+ }
304
+
305
+ /* ---------------------------------------------------------------------------
306
+ * The room a pinned footer takes in a page layout
307
+ *
308
+ * The same arrangement at the other end, with one difference: a `PlFooter`
309
+ * has no height floor. What the stylesheet knows of its height is the air
310
+ * above and below its content, which is `sheetPaddingYClasses` in
311
+ * `internal/styles.ts` for its `size` and `density` spelled in the same
312
+ * `--spacing`, and the hairlines along its edges. The content is the caller's,
313
+ * and only the layout's measurement knows how tall it is, so a server's first
314
+ * paint reserves the air and the edges and the measurement adds the rest.
315
+ *
316
+ * The two paths of a fixed footer, the one path of a sticky footer that spans
317
+ * the full width, and the start from zero are the header's, for its reasons.
318
+ * ------------------------------------------------------------------------- */
319
+
320
+ .plass-layout {
321
+ --p-layout-footer: 0px;
322
+ --p-layout-footer-inset: 0px;
323
+ --p-layout-footer-floor: 0px;
324
+ --p-layout-footer-rule: 0px;
325
+ }
326
+
327
+ .plass-layout:has(> .plass-footer-fixed, > div > div > .plass-footer-fixed) {
328
+ --p-layout-footer: calc(var(--p-layout-footer-floor) + var(--p-layout-footer-rule));
329
+ --p-layout-footer-inset: var(--p-layout-footer);
330
+ }
331
+
332
+ .plass-layout:has(> .plass-footer-sticky) {
333
+ --p-layout-footer: calc(var(--p-layout-footer-floor) + var(--p-layout-footer-rule));
334
+ }
335
+
336
+ .plass-layout:has(
337
+ > .plass-footer-fixed[data-size='xs'][data-density='default'],
338
+ > div > div > .plass-footer-fixed[data-size='xs'][data-density='default'],
339
+ > .plass-footer-sticky[data-size='xs'][data-density='default']
340
+ ) {
341
+ --p-layout-footer-floor: calc(var(--spacing, 0.25rem) * 5);
342
+ }
343
+
344
+ .plass-layout:has(
345
+ > .plass-footer-fixed[data-size='sm'][data-density='default'],
346
+ > div > div > .plass-footer-fixed[data-size='sm'][data-density='default'],
347
+ > .plass-footer-sticky[data-size='sm'][data-density='default']
348
+ ) {
349
+ --p-layout-footer-floor: calc(var(--spacing, 0.25rem) * 7);
350
+ }
351
+
352
+ .plass-layout:has(
353
+ > .plass-footer-fixed[data-size='md'][data-density='default'],
354
+ > div > div > .plass-footer-fixed[data-size='md'][data-density='default'],
355
+ > .plass-footer-sticky[data-size='md'][data-density='default']
356
+ ) {
357
+ --p-layout-footer-floor: calc(var(--spacing, 0.25rem) * 10);
358
+ }
359
+
360
+ .plass-layout:has(
361
+ > .plass-footer-fixed[data-size='lg'][data-density='default'],
362
+ > div > div > .plass-footer-fixed[data-size='lg'][data-density='default'],
363
+ > .plass-footer-sticky[data-size='lg'][data-density='default']
364
+ ) {
365
+ --p-layout-footer-floor: calc(var(--spacing, 0.25rem) * 12);
366
+ }
367
+
368
+ .plass-layout:has(
369
+ > .plass-footer-fixed[data-size='xl'][data-density='default'],
370
+ > div > div > .plass-footer-fixed[data-size='xl'][data-density='default'],
371
+ > .plass-footer-sticky[data-size='xl'][data-density='default']
372
+ ) {
373
+ --p-layout-footer-floor: calc(var(--spacing, 0.25rem) * 14);
374
+ }
375
+
376
+ .plass-layout:has(
377
+ > .plass-footer-fixed[data-size='xs'][data-density='compact'],
378
+ > div > div > .plass-footer-fixed[data-size='xs'][data-density='compact'],
379
+ > .plass-footer-sticky[data-size='xs'][data-density='compact']
380
+ ) {
381
+ --p-layout-footer-floor: calc(var(--spacing, 0.25rem) * 4);
382
+ }
383
+
384
+ .plass-layout:has(
385
+ > .plass-footer-fixed[data-size='sm'][data-density='compact'],
386
+ > div > div > .plass-footer-fixed[data-size='sm'][data-density='compact'],
387
+ > .plass-footer-sticky[data-size='sm'][data-density='compact']
388
+ ) {
389
+ --p-layout-footer-floor: calc(var(--spacing, 0.25rem) * 5);
390
+ }
391
+
392
+ .plass-layout:has(
393
+ > .plass-footer-fixed[data-size='md'][data-density='compact'],
394
+ > div > div > .plass-footer-fixed[data-size='md'][data-density='compact'],
395
+ > .plass-footer-sticky[data-size='md'][data-density='compact']
396
+ ) {
397
+ --p-layout-footer-floor: calc(var(--spacing, 0.25rem) * 7);
398
+ }
399
+
400
+ .plass-layout:has(
401
+ > .plass-footer-fixed[data-size='lg'][data-density='compact'],
402
+ > div > div > .plass-footer-fixed[data-size='lg'][data-density='compact'],
403
+ > .plass-footer-sticky[data-size='lg'][data-density='compact']
404
+ ) {
405
+ --p-layout-footer-floor: calc(var(--spacing, 0.25rem) * 8);
406
+ }
407
+
408
+ .plass-layout:has(
409
+ > .plass-footer-fixed[data-size='xl'][data-density='compact'],
410
+ > div > div > .plass-footer-fixed[data-size='xl'][data-density='compact'],
411
+ > .plass-footer-sticky[data-size='xl'][data-density='compact']
412
+ ) {
413
+ --p-layout-footer-floor: calc(var(--spacing, 0.25rem) * 10);
414
+ }
415
+
416
+ /* The divider is a line along the top; the glass wins where both match, as
417
+ on the header. */
418
+ .plass-layout:has(
419
+ > .plass-footer-fixed[data-divider],
420
+ > div > div > .plass-footer-fixed[data-divider],
421
+ > .plass-footer-sticky[data-divider]
422
+ ) {
423
+ --p-layout-footer-rule: 1px;
424
+ }
425
+
426
+ .plass-layout:has(
427
+ > .plass-footer-fixed[data-variant='glass'],
428
+ > div > div > .plass-footer-fixed[data-variant='glass'],
429
+ > .plass-footer-sticky[data-variant='glass']
430
+ ) {
431
+ --p-layout-footer-rule: 2px;
432
+ }
433
+
175
434
  /* ---------------------------------------------------------------------------
176
435
  * Light theme (default)
177
436
  *
@@ -272,6 +531,20 @@
272
531
  --plass-glow-on-fill: rgb(255 255 255 / 0.18);
273
532
  --plass-flash-on-fill: rgb(255 255 255 / 0.26);
274
533
 
534
+ /* How much of that light a **field** carries, as a share of what a key gets.
535
+
536
+ A key is pressed and is finished with; a field is pressed once and then
537
+ written in for a minute, with the bloom sitting under the words being typed
538
+ and following the hand that is typing them. The same strength that reads as
539
+ a control answering a pointer reads as a stain on a paragraph. So a field
540
+ takes the family's tint at a little over half, which is enough to say the
541
+ surface answered and not enough to compete with the ink on it.
542
+
543
+ One value for both layers and for every family, because the thing being
544
+ dialled down is the effect rather than a colour. It is per-theme neither:
545
+ the tints it scales are already per-theme. */
546
+ --plass-glow-field-strength: 55%;
547
+
275
548
  /* Elevation, and how strongly a key's own colour bleeds into the shadow it
276
549
  casts. The ambient is a blue-grey rather than black: a neutral shadow over
277
550
  a blue-tinted page reads as dirt — and it is faint, because the tint below
@@ -279,8 +552,10 @@
279
552
  --plass-shadow-ambient: rgb(20 40 90 / 0.1);
280
553
  --plass-tint-strength: 35%;
281
554
 
282
- /* The scrim behind a modal surface. */
555
+ /* The scrim behind a modal surface, and the blur it lays over the page. The
556
+ blur is the same in every theme, so it is written here only. */
283
557
  --plass-scrim: rgb(16 24 40 / 0.4);
558
+ --plass-scrim-blur: blur(2px);
284
559
 
285
560
  /* Corner radius: roughly 30% of the control height, and roughly constant in
286
561
  feel across the ladder — a Plass corner is a moulded fillet, not a pill and
@@ -408,7 +683,10 @@
408
683
 
409
684
  --plass-surface: #141b30;
410
685
  --plass-fg: #e7eaf3;
411
- --plass-muted-fg: #99a2ba;
686
+ /* Light enough to clear 4.5:1 on the densest step of the glass, which is
687
+ lighter than the sheet: a modal or a select's list over an opaque sheet
688
+ is the hardest place muted text is read. */
689
+ --plass-muted-fg: #a0a9bf;
412
690
  --plass-border: rgb(255 255 255 / 0.12);
413
691
 
414
692
  --plass-bg-from: #111731;
@@ -487,7 +765,7 @@
487
765
 
488
766
  --plass-surface: #141b30;
489
767
  --plass-fg: #e7eaf3;
490
- --plass-muted-fg: #99a2ba;
768
+ --plass-muted-fg: #a0a9bf;
491
769
  --plass-border: rgb(255 255 255 / 0.12);
492
770
 
493
771
  --plass-bg-from: #111731;
@@ -557,11 +835,79 @@
557
835
  --plass-chart-div-on-5: var(--plass-fg);
558
836
  }
559
837
 
838
+ /* ---------------------------------------------------------------------------
839
+ * Reduced transparency
840
+ *
841
+ * A reader who has asked their system for less transparency gets every sheet
842
+ * opaque. The blur goes, and each rung of the glass ladder is laid over the
843
+ * theme's own `--plass-surface` instead of over the page: the same white at the
844
+ * same strength, with the opaque sheet behind it where the page used to be.
845
+ * That is the pair of layers a pinned table header already stacks so that rows
846
+ * do not show through it, and it keeps the ladder: an engaged sheet still holds
847
+ * more light than one at rest, except on a white surface, which has no lighter
848
+ * step to take.
849
+ *
850
+ * Only the fill and the blur move. The hairline, the light along the top edge,
851
+ * the shadows and the focus ring are drawn exactly as before, so a sheet keeps
852
+ * its edge and its place above the page.
853
+ *
854
+ * The scrim behind a modal, a drawer, a command palette or a tour, and an
855
+ * overlay's `scrim` tone, loses its blur too and keeps its dim, which is what
856
+ * tells the reader the page behind has stopped being reachable. Its blur is the same in every theme, so the
857
+ * first block alone turns it off, and every element inherits that.
858
+ *
859
+ * It is done here, on the tokens, rather than on the components, because the
860
+ * sheets in the library paint with these four: a new component that draws glass
861
+ * the house way is opaque for this reader without having to know that this
862
+ * block exists. The two that blur a translucency of their own, `PlOverlay`'s
863
+ * `glass` tone and a `PlWindowPane`, answer the same reader in their own
864
+ * classes.
865
+ *
866
+ * Three blocks for the three theme blocks above, each with the selector of the
867
+ * one it overrides, because the strengths are per theme and a rule only takes
868
+ * over from one at least as specific as itself. The forced dark block stays
869
+ * last, for the reason it is last up there. The surface is the first colour in
870
+ * each mix because the compiler writes that one out as the fallback for a
871
+ * browser with no `color-mix()`, and the opaque surface is the right one.
872
+ * ------------------------------------------------------------------------- */
873
+
874
+ @media (prefers-reduced-transparency: reduce) {
875
+ :root,
876
+ .light,
877
+ [data-theme='light'] {
878
+ --plass-glass: color-mix(in srgb, var(--plass-surface), rgb(255 255 255) 62%);
879
+ --plass-glass-hover: color-mix(in srgb, var(--plass-surface), rgb(255 255 255) 76%);
880
+ --plass-glass-press: color-mix(in srgb, var(--plass-surface), rgb(255 255 255) 88%);
881
+ --plass-blur: none;
882
+ --plass-scrim-blur: none;
883
+ }
884
+ }
885
+
886
+ @media (prefers-reduced-transparency: reduce) and (prefers-color-scheme: dark) {
887
+ :root:not(.light):not([data-theme='light']) {
888
+ --plass-glass: color-mix(in srgb, var(--plass-surface), rgb(255 255 255) 7%);
889
+ --plass-glass-hover: color-mix(in srgb, var(--plass-surface), rgb(255 255 255) 11%);
890
+ --plass-glass-press: color-mix(in srgb, var(--plass-surface), rgb(255 255 255) 15%);
891
+ --plass-blur: none;
892
+ }
893
+ }
894
+
895
+ @media (prefers-reduced-transparency: reduce) {
896
+ .dark,
897
+ [data-theme='dark'] {
898
+ --plass-glass: color-mix(in srgb, var(--plass-surface), rgb(255 255 255) 7%);
899
+ --plass-glass-hover: color-mix(in srgb, var(--plass-surface), rgb(255 255 255) 11%);
900
+ --plass-glass-press: color-mix(in srgb, var(--plass-surface), rgb(255 255 255) 15%);
901
+ --plass-blur: none;
902
+ }
903
+ }
904
+
560
905
  /* ---------------------------------------------------------------------------
561
906
  * Derived tokens
562
907
  *
563
- * Gradients, glows, tints, hairlines and focus rings are all one base colour at
564
- * different strengths, so they are computed rather than hand-picked per theme.
908
+ * A family's gradient, tint, soft fills, hairlines and focus ring are computed
909
+ * from its hand-picked values rather than picked per theme: the gradient from
910
+ * the two ends, the tint from `solid`, and the rest from `accent`.
565
911
  *
566
912
  * **This block is repeated for every theme root**, because a custom property
567
913
  * resolves its `var()`s on the element that declares it — declaring these only
@@ -572,8 +918,9 @@
572
918
  * `--plass-primary-solid` on a `<div>` changes the base and nothing else,
573
919
  * because every value derived from it was computed further up, where the old
574
920
  * colour still is. The class re-runs the whole block on that element, so the
575
- * gradient, the tint, the hairline and the ring follow the base that was just
576
- * written beside them. It is the one hook here that does not force a theme:
921
+ * gradient and the tint follow the ends that were just written beside them,
922
+ * and the soft fills, the hairline and the ring follow an `accent` written
923
+ * there too. It is the one hook here that does not force a theme:
577
924
  * `--plass-tint-strength` and the rest inherit from whichever theme root is
578
925
  * above, so a scoped family works the same in light and in dark.
579
926
  * ------------------------------------------------------------------------- */
@@ -750,6 +1097,25 @@
750
1097
  margin: 0;
751
1098
  }
752
1099
 
1100
+ /* ---------------------------------------------------------------------------
1101
+ * Struts
1102
+ *
1103
+ * An empty line in the type of the element that carries the class, for lining
1104
+ * something up by a line of text it does not hold: a card's `headerAction` is
1105
+ * centred with one so that its middle meets the middle of the title's first
1106
+ * line. A zero-width space is the least a line box needs, and as generated
1107
+ * content it never reaches the element's `textContent`.
1108
+ *
1109
+ * In CSS rather than in Tailwind because the character is an escape: a
1110
+ * `content-[…]` utility holding it matches its rule only while every tool
1111
+ * between the source and the `dist/` a project's Tailwind scans keeps the
1112
+ * backslash as written.
1113
+ * ------------------------------------------------------------------------- */
1114
+
1115
+ .plass-strut::before {
1116
+ content: '\200b';
1117
+ }
1118
+
753
1119
  /* ---------------------------------------------------------------------------
754
1120
  * Links
755
1121
  *
@@ -880,6 +1246,86 @@
880
1246
  transition-duration: 0ms;
881
1247
  }
882
1248
 
1249
+ /*
1250
+ * The bloom steps out of the way while the surface is being typed into.
1251
+ *
1252
+ * A pointer resting on a field is a pointer that has been parked: the hand is
1253
+ * on the keyboard, the light is no longer following anything, and what it is
1254
+ * doing is sitting under the sentence being written. Fading it out on the first
1255
+ * keystroke and back in on the next real pointer move costs the effect nothing —
1256
+ * it only ever *meant* anything while the pointer was moving — and gives the
1257
+ * text a clean sheet for as long as the reader is on the keys.
1258
+ *
1259
+ * Only the bloom. The flash is drawn by `:active`, which cannot be held down
1260
+ * and typed through.
1261
+ *
1262
+ * After the `:hover` rule, and at the same specificity: that order is what makes
1263
+ * this the one that wins. The component writes and clears the attribute — see
1264
+ * `useFieldLight`.
1265
+ */
1266
+ .plass-glow[data-quiet]::before {
1267
+ opacity: 0;
1268
+ }
1269
+
1270
+ /* ---------------------------------------------------------------------------
1271
+ * A fill that comes and goes with a state
1272
+ *
1273
+ * A `solid` toggle, a tick, a radio's ring, a switch's track, a chosen day, a
1274
+ * step's bullet and the current page are glass, a groove or nothing while they
1275
+ * are off and the family's gradient while they are on, and no browser eases a
1276
+ * gradient to or from nothing. Set on the element itself, the gradient arrived
1277
+ * in one frame and left in one. So it is a layer of its own, there in both
1278
+ * states and faded by its opacity, which is the one thing about it a browser
1279
+ * can ease — the way a `PlProgressBox` plate breathes.
1280
+ *
1281
+ * The component says when it is on with `--p-fill-on`, 0 or 1, from the state
1282
+ * variants that used to set the gradient, and a control with an edge says how
1283
+ * wide the edge is with `--p-fill-edge`, so the fill runs under the edge it
1284
+ * turns transparent, as a background does. Both are only read here, never
1285
+ * declared: this is a real class, which a consumer's own Tailwind build keeps
1286
+ * outside the layer it puts utilities in, so a value declared here would beat
1287
+ * the component's. Left unset, the fill is off and has no edge.
1288
+ *
1289
+ * Under what is drawn on the control and over its own background, which is what
1290
+ * the negative depth in a box of its own is for, as the bloom's is above. It is
1291
+ * in CSS rather than in Tailwind for the bloom's reason too: it is a gradient
1292
+ * on a pseudo-element.
1293
+ * ------------------------------------------------------------------------- */
1294
+
1295
+ .plass-fill {
1296
+ isolation: isolate;
1297
+ }
1298
+
1299
+ .plass-fill::before,
1300
+ .plass-fill-layer {
1301
+ position: absolute;
1302
+ inset: calc(-1 * var(--p-fill-edge, 0px));
1303
+ z-index: -1;
1304
+ /* An edge as wide as the control's, and as clear, so the gradient is laid out
1305
+ inside it and drawn under it exactly as the control's own background was. */
1306
+ border: var(--p-fill-edge, 0px) solid transparent;
1307
+ border-radius: inherit;
1308
+ background-image: var(--p-fill);
1309
+ opacity: var(--p-fill-on, 0);
1310
+ pointer-events: none;
1311
+ transition: opacity var(--plass-duration) var(--plass-ease);
1312
+ }
1313
+
1314
+ .plass-fill::before {
1315
+ content: '';
1316
+ }
1317
+
1318
+ /*
1319
+ * The same layer as an element, for a control whose two pseudo-elements are
1320
+ * already the light: a `solid` `PlToggle` and a `PlPagination` page. The bloom
1321
+ * is the `::before` of the same surface, which comes first, so the fill goes
1322
+ * one step further down to stay under it. The surface's `.plass-glow` gives
1323
+ * both depths a box of their own.
1324
+ */
1325
+ .plass-fill-layer {
1326
+ z-index: -2;
1327
+ }
1328
+
883
1329
  /* ---------------------------------------------------------------------------
884
1330
  * The three dots in a bubble somebody is typing into
885
1331
  *
@@ -924,28 +1370,56 @@
924
1370
  * Two rules hold across all three. **Nothing changes the paint.** A Plass fill is a gradient and
925
1371
  * a gradient cannot be interpolated, so what moves is a length or an opacity and
926
1372
  * the fill itself is either there or not. And **the segment travels on
927
- * `inset-inline-start`**, not on a `transform` — the same trade the skeleton's
928
- * highlight makes, and for the same two reasons: it runs the other way under RTL
929
- * without being told, and the layout pass it costs is confined to a box with
930
- * nothing in it.
1373
+ * `translate`**, never on an inset: an inset lays the box out again on every
1374
+ * frame, and the browser records each of those frames as a layout shift, which
1375
+ * a page loading behind an indicator pays for in its CLS for as long as it
1376
+ * loads. The skeleton's highlight travels the same way.
1377
+ *
1378
+ * The segment rests at the inline start and a percentage `translate` resolves
1379
+ * against its own width, so the keyframes are the old path divided by 45%: one
1380
+ * whole width out before the start, to the end of the groove, which is 1 / 0.45
1381
+ * of its width away. There is no logical `translate`, so the sign is turned
1382
+ * round under RTL by hand, the way the marquee's is (see `.plass-marquee-track`
1383
+ * for why it is written twice).
931
1384
  * ------------------------------------------------------------------------- */
932
1385
 
933
1386
  @keyframes plass-progress-sweep {
934
1387
  from {
935
- inset-inline-start: -45%;
1388
+ translate: calc(-100% * var(--p-anim-along, 1)) 0;
936
1389
  }
937
1390
  to {
938
- inset-inline-start: 100%;
1391
+ translate: calc(100% / 0.45 * var(--p-anim-along, 1)) 0;
939
1392
  }
940
1393
  }
941
1394
 
942
1395
  .plass-progress-sweep {
943
1396
  position: absolute;
944
1397
  inset-block: 0;
1398
+ inset-inline-start: 0;
945
1399
  inline-size: 45%;
946
1400
  animation: plass-progress-sweep 1.15s var(--plass-ease) infinite;
947
1401
  }
948
1402
 
1403
+ .plass-progress-sweep:dir(rtl) {
1404
+ --p-anim-along: -1;
1405
+ }
1406
+
1407
+ .plass-progress-sweep:dir(ltr) {
1408
+ --p-anim-along: 1;
1409
+ }
1410
+
1411
+ /* The attribute, for a browser without `:dir()`; see `.plass-marquee-track` for
1412
+ why the selectors are written this way and how deep they reach. */
1413
+ @supports not selector(:dir(rtl)) {
1414
+ [dir='rtl'] .plass-progress-sweep {
1415
+ --p-anim-along: -1;
1416
+ }
1417
+
1418
+ [dir='rtl'] [dir='ltr'] .plass-progress-sweep {
1419
+ --p-anim-along: 1;
1420
+ }
1421
+ }
1422
+
949
1423
  /* The one place the library turns something, and the exception is the same one
950
1424
  the spinner already has: an indeterminate indicator that holds still says the
951
1425
  opposite of what it is for. `rotate` rather than `transform`, so nothing else
@@ -1002,10 +1476,17 @@
1002
1476
  /* ---------------------------------------------------------------------------
1003
1477
  * The travelling highlight on a placeholder
1004
1478
  *
1005
- * `inset-inline-start` rather than a `transform`, which is the same trade the
1006
- * rest of the library makes and for the same two reasons: it runs the other way
1007
- * under RTL without being told, and the layout pass it costs is confined to a
1008
- * box with nothing in it.
1479
+ * It travels on `translate`, as the progress segment does and for the same
1480
+ * reason: an inset would lay the highlight out again on every frame and record
1481
+ * a layout shift each time, for as long as the page is waiting. The highlight
1482
+ * rests at the left edge and is 60% of the box, so its path of -60% to 100% of
1483
+ * the box is -100% to 100% / 0.6 of its own width.
1484
+ *
1485
+ * The left edge, and not the inline start, on purpose: this is a light
1486
+ * crossing a surface rather than something read along a line, so it runs left
1487
+ * to right in every writing direction, as the Flutter build's does. Its
1488
+ * keyframes leave `--p-anim-along` out for the same reason, so a placeholder
1489
+ * inside a right-to-left marquee does not inherit the turn.
1009
1490
  *
1010
1491
  * A pseudo-element rather than a child, so a `PlSkeleton` is one element per
1011
1492
  * shape and a stack of ten lines is ten nodes rather than twenty. The gradient
@@ -1017,10 +1498,10 @@
1017
1498
 
1018
1499
  @keyframes plass-skeleton-sweep {
1019
1500
  from {
1020
- inset-inline-start: -60%;
1501
+ translate: -100% 0;
1021
1502
  }
1022
1503
  to {
1023
- inset-inline-start: 100%;
1504
+ translate: calc(100% / 0.6) 0;
1024
1505
  }
1025
1506
  }
1026
1507
 
@@ -1028,6 +1509,7 @@
1028
1509
  content: '';
1029
1510
  position: absolute;
1030
1511
  inset-block: 0;
1512
+ left: 0;
1031
1513
  inline-size: 60%;
1032
1514
  background-image: linear-gradient(90deg, transparent, var(--p-soft-press), transparent);
1033
1515
  animation: plass-skeleton-sweep 1.5s var(--plass-ease) infinite;
@@ -1076,18 +1558,25 @@
1076
1558
  * a writing direction, so the measurement is turned round once, here, rather
1077
1559
  * than in four places that could disagree.
1078
1560
  *
1561
+ * A tab brought into view stops the fade's length short of the edge, through
1562
+ * `scroll-padding-inline`. The reveal is not this sheet's — the browser does it
1563
+ * for a focused tab and Base UI for the chosen one and the next one along — but
1564
+ * both honour the padding, and without it the tab they brought into view would
1565
+ * end flush with the edge, which is exactly where the fade is strongest.
1566
+ *
1079
1567
  * The mask is dropped entirely while something inside holds a visible focus
1080
- * ring. Focusing a tab scrolls it flush against the edge it came from, which is
1081
- * exactly where the fade is strongest — so the one control whose outline has to
1082
- * be legible would be the one drawn half transparent. `:focus-visible` and not
1083
- * `:focus-within`: a tab that was clicked has the focus and no ring, and a bar
1084
- * that stopped fading for the rest of the session because somebody pressed a
1085
- * tab would have lost the signal at the first interaction.
1568
+ * ring, as well: a reader walking the strip with the arrow keys is looking at
1569
+ * the ring, and the one outline on the bar that has to be legible whole should
1570
+ * not be one a fade can reach. `:focus-visible` and not `:focus-within`: a tab
1571
+ * that was clicked has the focus and no ring, and a bar that stopped fading for
1572
+ * the rest of the session because somebody pressed a tab would have lost the
1573
+ * signal at the first interaction.
1086
1574
  * ------------------------------------------------------------------------- */
1087
1575
 
1088
1576
  .plass-fade-x {
1089
1577
  --p-fade: 1.5rem;
1090
1578
 
1579
+ scroll-padding-inline: var(--p-fade);
1091
1580
  scrollbar-width: none;
1092
1581
  mask-image: linear-gradient(
1093
1582
  to right,
@@ -1415,6 +1904,81 @@
1415
1904
  inherits: false;
1416
1905
  }
1417
1906
 
1907
+ @property --p-lane-rows-xs {
1908
+ syntax: '*';
1909
+ inherits: false;
1910
+ }
1911
+
1912
+ @property --p-lane-rows-sm {
1913
+ syntax: '*';
1914
+ inherits: false;
1915
+ }
1916
+
1917
+ @property --p-lane-rows-md {
1918
+ syntax: '*';
1919
+ inherits: false;
1920
+ }
1921
+
1922
+ @property --p-lane-rows-lg {
1923
+ syntax: '*';
1924
+ inherits: false;
1925
+ }
1926
+
1927
+ @property --p-lane-rows-xl {
1928
+ syntax: '*';
1929
+ inherits: false;
1930
+ }
1931
+
1932
+ @property --p-lane-area-xs {
1933
+ syntax: '*';
1934
+ inherits: false;
1935
+ }
1936
+
1937
+ @property --p-lane-area-sm {
1938
+ syntax: '*';
1939
+ inherits: false;
1940
+ }
1941
+
1942
+ @property --p-lane-area-md {
1943
+ syntax: '*';
1944
+ inherits: false;
1945
+ }
1946
+
1947
+ @property --p-lane-area-lg {
1948
+ syntax: '*';
1949
+ inherits: false;
1950
+ }
1951
+
1952
+ @property --p-lane-area-xl {
1953
+ syntax: '*';
1954
+ inherits: false;
1955
+ }
1956
+
1957
+ @property --p-lane-span-xs {
1958
+ syntax: '*';
1959
+ inherits: false;
1960
+ }
1961
+
1962
+ @property --p-lane-span-sm {
1963
+ syntax: '*';
1964
+ inherits: false;
1965
+ }
1966
+
1967
+ @property --p-lane-span-md {
1968
+ syntax: '*';
1969
+ inherits: false;
1970
+ }
1971
+
1972
+ @property --p-lane-span-lg {
1973
+ syntax: '*';
1974
+ inherits: false;
1975
+ }
1976
+
1977
+ @property --p-lane-span-xl {
1978
+ syntax: '*';
1979
+ inherits: false;
1980
+ }
1981
+
1418
1982
  .plass-grid,
1419
1983
  .plass-flex {
1420
1984
  --p-gap-x: var(--p-gap-x-xs, 0px);
@@ -1429,6 +1993,21 @@
1429
1993
  --p-cols: var(--p-cols-xs, 2);
1430
1994
  }
1431
1995
 
1996
+ /*
1997
+ * A masonry's deal at the width the window is: the list's rows, and each
1998
+ * tile's place in them and how many it spans. The span is read by the tile's
1999
+ * button, picture and caption as well, so it is the one of the three that
2000
+ * inherits.
2001
+ */
2002
+ .plass-gallery-masonry {
2003
+ --p-lane-rows: var(--p-lane-rows-xs, none);
2004
+ }
2005
+
2006
+ .plass-gallery-masonry > li {
2007
+ --p-lane-area: var(--p-lane-area-xs, auto);
2008
+ --p-lane-span: var(--p-lane-span-xs, 1);
2009
+ }
2010
+
1432
2011
  /*
1433
2012
  * `PlFlex`'s axis. One slot rather than two, because `reverse` is not
1434
2013
  * responsive and the direction is: the component folds the two into the four
@@ -1493,6 +2072,15 @@
1493
2072
  --p-cols: var(--p-cols-sm, var(--p-cols-xs, 2));
1494
2073
  }
1495
2074
 
2075
+ .plass-gallery-masonry {
2076
+ --p-lane-rows: var(--p-lane-rows-sm, var(--p-lane-rows-xs, none));
2077
+ }
2078
+
2079
+ .plass-gallery-masonry > li {
2080
+ --p-lane-area: var(--p-lane-area-sm, var(--p-lane-area-xs, auto));
2081
+ --p-lane-span: var(--p-lane-span-sm, var(--p-lane-span-xs, 1));
2082
+ }
2083
+
1496
2084
  .plass-flex {
1497
2085
  --p-dir: var(--p-dir-sm, var(--p-dir-xs, row));
1498
2086
  }
@@ -1522,6 +2110,15 @@
1522
2110
  --p-cols: var(--p-cols-md, var(--p-cols-sm, var(--p-cols-xs, 2)));
1523
2111
  }
1524
2112
 
2113
+ .plass-gallery-masonry {
2114
+ --p-lane-rows: var(--p-lane-rows-md, var(--p-lane-rows-sm, var(--p-lane-rows-xs, none)));
2115
+ }
2116
+
2117
+ .plass-gallery-masonry > li {
2118
+ --p-lane-area: var(--p-lane-area-md, var(--p-lane-area-sm, var(--p-lane-area-xs, auto)));
2119
+ --p-lane-span: var(--p-lane-span-md, var(--p-lane-span-sm, var(--p-lane-span-xs, 1)));
2120
+ }
2121
+
1525
2122
  .plass-flex {
1526
2123
  --p-dir: var(--p-dir-md, var(--p-dir-sm, var(--p-dir-xs, row)));
1527
2124
  }
@@ -1551,6 +2148,24 @@
1551
2148
  --p-cols: var(--p-cols-lg, var(--p-cols-md, var(--p-cols-sm, var(--p-cols-xs, 2))));
1552
2149
  }
1553
2150
 
2151
+ .plass-gallery-masonry {
2152
+ --p-lane-rows: var(
2153
+ --p-lane-rows-lg,
2154
+ var(--p-lane-rows-md, var(--p-lane-rows-sm, var(--p-lane-rows-xs, none)))
2155
+ );
2156
+ }
2157
+
2158
+ .plass-gallery-masonry > li {
2159
+ --p-lane-area: var(
2160
+ --p-lane-area-lg,
2161
+ var(--p-lane-area-md, var(--p-lane-area-sm, var(--p-lane-area-xs, auto)))
2162
+ );
2163
+ --p-lane-span: var(
2164
+ --p-lane-span-lg,
2165
+ var(--p-lane-span-md, var(--p-lane-span-sm, var(--p-lane-span-xs, 1)))
2166
+ );
2167
+ }
2168
+
1554
2169
  .plass-flex {
1555
2170
  --p-dir: var(--p-dir-lg, var(--p-dir-md, var(--p-dir-sm, var(--p-dir-xs, row))));
1556
2171
  }
@@ -1598,6 +2213,30 @@
1598
2213
  );
1599
2214
  }
1600
2215
 
2216
+ .plass-gallery-masonry {
2217
+ --p-lane-rows: var(
2218
+ --p-lane-rows-xl,
2219
+ var(
2220
+ --p-lane-rows-lg,
2221
+ var(--p-lane-rows-md, var(--p-lane-rows-sm, var(--p-lane-rows-xs, none)))
2222
+ )
2223
+ );
2224
+ }
2225
+
2226
+ .plass-gallery-masonry > li {
2227
+ --p-lane-area: var(
2228
+ --p-lane-area-xl,
2229
+ var(
2230
+ --p-lane-area-lg,
2231
+ var(--p-lane-area-md, var(--p-lane-area-sm, var(--p-lane-area-xs, auto)))
2232
+ )
2233
+ );
2234
+ --p-lane-span: var(
2235
+ --p-lane-span-xl,
2236
+ var(--p-lane-span-lg, var(--p-lane-span-md, var(--p-lane-span-sm, var(--p-lane-span-xs, 1))))
2237
+ );
2238
+ }
2239
+
1601
2240
  .plass-flex {
1602
2241
  --p-dir: var(
1603
2242
  --p-dir-xl,
@@ -1646,7 +2285,7 @@
1646
2285
 
1647
2286
  .plass-anim {
1648
2287
  animation-duration: var(--p-anim-duration, 300ms);
1649
- animation-delay: var(--p-anim-delay, 0ms);
2288
+ animation-delay: var(--p-anim-run-delay, var(--p-anim-delay, 0ms));
1650
2289
  animation-timing-function: var(--p-anim-ease, var(--plass-ease));
1651
2290
  animation-iteration-count: var(--p-anim-repeat, 1);
1652
2291
  animation-direction: var(--p-anim-direction, normal);
@@ -1654,6 +2293,57 @@
1654
2293
  animation-play-state: var(--p-anim-state, running);
1655
2294
  }
1656
2295
 
2296
+ /*
2297
+ * The delay a run started with, kept until the next run.
2298
+ *
2299
+ * A new `animation-delay` applies to an animation that is already running, so
2300
+ * a `delay` changed while a run was under way sent it back to waiting: a fade
2301
+ * with a delay of 300ms given 1000ms at 400ms went back to nothing and waited
2302
+ * again. `src/internal/animate.ts` writes the delay into `--p-anim-run-delay`
2303
+ * as the run starts and takes it off when the next one does, so a new delay is
2304
+ * waited out from the next run on. A run still waiting has nothing written, and
2305
+ * a new delay is measured from when its wait began.
2306
+ *
2307
+ * Every element that reads it is reset here, because a custom property is
2308
+ * inherited and an effect nested inside a running one has a delay of its own.
2309
+ * The value written inline on an element outranks the reset. The arc of a
2310
+ * `PlAnimateLighting` is a pseudo-element, and takes its root's.
2311
+ */
2312
+ .plass-anim,
2313
+ .plass-marquee-track,
2314
+ .plass-anim-lighting {
2315
+ --p-anim-run-delay: initial;
2316
+ }
2317
+
2318
+ /*
2319
+ * Every effect's slots are its own.
2320
+ *
2321
+ * An effect writes inline only the slots it has a value for, and a custom
2322
+ * property is inherited, so a slot it left out was whatever an effect around
2323
+ * it had written: a fade inside an endless linear turn eased linearly, took
2324
+ * the turn's one pass under reduced motion, and inside a `timeline="view"`
2325
+ * effect followed the scroll. Each element an effect writes its slots on
2326
+ * starts them over here, and what it writes inline outranks this. The strips
2327
+ * of a marquee and the lines of a headline read their root's, which is why
2328
+ * they are not on the list.
2329
+ */
2330
+ .plass-anim,
2331
+ .plass-anim-lighting,
2332
+ .plass-marquee,
2333
+ .plass-headline {
2334
+ --p-anim-still-repeat: initial;
2335
+ --p-anim-ease: initial;
2336
+ --p-anim-timeline: initial;
2337
+ --p-anim-range: initial;
2338
+ --p-anim-opacity: initial;
2339
+ --p-anim-scale: initial;
2340
+ --p-anim-x: initial;
2341
+ --p-anim-y: initial;
2342
+ --p-anim-angle: initial;
2343
+ --p-anim-angle-to: initial;
2344
+ --p-anim-clip: initial;
2345
+ }
2346
+
1657
2347
  /*
1658
2348
  * `timeline="view"`, and the whole of it.
1659
2349
  *
@@ -1901,7 +2591,7 @@
1901
2591
  filter: blur(var(--p-anim-glow-blur, 5px));
1902
2592
  animation-name: plass-anim-lighting;
1903
2593
  animation-duration: var(--p-anim-duration, 3s);
1904
- animation-delay: var(--p-anim-delay, 0ms);
2594
+ animation-delay: var(--p-anim-run-delay, var(--p-anim-delay, 0ms));
1905
2595
  /* Linear unless a caller says otherwise, which is what `curve` does in the
1906
2596
  Flutter build: an arc that eases at both ends of a turn that has no ends
1907
2597
  reads as a stutter, and that is a default rather than a rule. */
@@ -1974,7 +2664,7 @@
1974
2664
  gap: var(--p-anim-gap, 0px);
1975
2665
  animation-name: plass-anim-marquee-x;
1976
2666
  animation-duration: var(--p-anim-duration, 12s);
1977
- animation-delay: var(--p-anim-delay, 0ms);
2667
+ animation-delay: var(--p-anim-run-delay, var(--p-anim-delay, 0ms));
1978
2668
  /* Linear unless a caller insists otherwise. An eased marquee slows at both
1979
2669
  ends of a loop that has no ends, which reads as the page stuttering. */
1980
2670
  animation-timing-function: var(--p-anim-ease, linear);
@@ -1984,7 +2674,14 @@
1984
2674
  animation-play-state: var(--p-anim-state, running);
1985
2675
  }
1986
2676
 
1987
- .plass-marquee-vertical .plass-marquee-track {
2677
+ /* `:where()` keeps this as specific as `.plass-marquee-track` on its own, so
2678
+ the reduced-motion block below switches a vertical strip off as it does a
2679
+ horizontal one. Written more specifically, the name outranked that block's
2680
+ `animation: none`, so the first copy ran a pass in no time under the setting
2681
+ and went on from its own clock afterwards, while the second, not drawn until
2682
+ then, started afresh and ran behind it. The track is the strip's own child,
2683
+ so a strip running across inside one running down keeps its own way. */
2684
+ :where(.plass-marquee-vertical) > .plass-marquee-track {
1988
2685
  flex-direction: column;
1989
2686
  animation-name: plass-anim-marquee-y;
1990
2687
  }
@@ -1996,15 +2693,43 @@
1996
2693
  *
1997
2694
  * There is no logical `translate`, so the sign is flipped by hand. The flex
1998
2695
  * track has already reversed on its own, which is what puts the overflow on the
1999
- * other side for the translate to bring in. Written against the attribute as
2000
- * well as `:dir()` because `:dir()` is newer than the rest of this sheet's
2001
- * floor; the two agree on every page that set `dir`.
2696
+ * other side for the translate to bring in. `:dir()` asks for the direction the
2697
+ * track itself is in, which is the nearest `dir` above it however deeply one
2698
+ * region is nested in another.
2002
2699
  */
2003
- [dir='rtl'] .plass-marquee-track,
2004
2700
  .plass-marquee-track:dir(rtl) {
2005
2701
  --p-anim-along: -1;
2006
2702
  }
2007
2703
 
2704
+ .plass-marquee-track:dir(ltr) {
2705
+ --p-anim-along: 1;
2706
+ }
2707
+
2708
+ /*
2709
+ * `:dir()` is newer than the rest of this sheet's floor (Chrome 120), so a
2710
+ * browser without it reads the attribute instead, and only that browser:
2711
+ * written beside `:dir()` for every browser, the attribute rules would outrank
2712
+ * it where the two disagree.
2713
+ *
2714
+ * `[dir='rtl']` asks whether any ancestor is right to left rather than the
2715
+ * nearest, so the second rule turns a left-to-right region inside a
2716
+ * right-to-left page back again; it is the more specific, so the nearer `dir`
2717
+ * wins. It is `[dir='rtl'] [dir='ltr']` and not a bare `[dir='ltr']`: a page
2718
+ * that writes `dir="ltr"` on its root would otherwise turn every right-to-left
2719
+ * region inside it the English way. That answers one region inside another in
2720
+ * either order and goes no deeper: in such a browser, a right-to-left region
2721
+ * inside a left-to-right one inside a right-to-left page runs the English way.
2722
+ */
2723
+ @supports not selector(:dir(rtl)) {
2724
+ [dir='rtl'] .plass-marquee-track {
2725
+ --p-anim-along: -1;
2726
+ }
2727
+
2728
+ [dir='rtl'] [dir='ltr'] .plass-marquee-track {
2729
+ --p-anim-along: 1;
2730
+ }
2731
+ }
2732
+
2008
2733
  .plass-marquee[data-pause-on-hover]:hover .plass-marquee-track {
2009
2734
  animation-play-state: paused;
2010
2735
  }
@@ -2107,10 +2832,11 @@
2107
2832
  * theme below declares its `fg` and `bg` on that same element — so the derived
2108
2833
  * five follow whichever theme won without being restated fourteen times.
2109
2834
  *
2110
- * The two dark palettes at the top are written twice on purpose — once for
2111
- * `dark`, once for `auto` inside a dark page — because a media query cannot be
2112
- * folded into a selector list. It is the same repetition the theme roots at the
2113
- * top of this file make, for the same reason.
2835
+ * The two house palettes are written more than once on purpose: once on the
2836
+ * block for `light` and `dark`, and again on the theme roots for `auto`, where
2837
+ * the dark one is there twice because a media query cannot be folded into a
2838
+ * selector list. It is the same repetition the theme roots at the top of this
2839
+ * file make, for the same reason.
2114
2840
  *
2115
2841
  * Every hue in the house themes is solved against its own background rather
2116
2842
  * than flipped: the light set runs dark and saturated on near-white, the dark
@@ -2122,7 +2848,8 @@
2122
2848
  * ------------------------------------------------------------------------- */
2123
2849
 
2124
2850
  .plass-code {
2125
- /* The light set, which is also what `auto` and `light` start from. */
2851
+ /* The light set, which is what `light` is and what a theme that fills only
2852
+ some of the slots starts from. */
2126
2853
  --p-code-bg: oklch(98.4% 0.003 262);
2127
2854
  --p-code-fg: oklch(30% 0.016 262);
2128
2855
 
@@ -2153,8 +2880,7 @@
2153
2880
  --p-code-mark-edge: color-mix(in oklab, var(--p-code-fg) 45%, transparent);
2154
2881
  }
2155
2882
 
2156
- .plass-code[data-code-theme='dark'],
2157
- :where(.dark, [data-theme='dark']) .plass-code[data-code-theme='auto'] {
2883
+ .plass-code[data-code-theme='dark'] {
2158
2884
  --p-code-bg: oklch(19.5% 0.014 262);
2159
2885
  --p-code-fg: oklch(90% 0.012 262);
2160
2886
 
@@ -2172,31 +2898,92 @@
2172
2898
  --p-code-del: oklch(75% 0.15 25);
2173
2899
  }
2174
2900
 
2175
- /* Both ways of forcing light are named, the same pair the token block above
2176
- answers to. `auto` means "whatever the page is", so a page that has said it
2177
- is light with `.light` and one that has said it with `[data-theme='light']`
2178
- have to leave the block light alike — otherwise the page is white and the
2179
- code on it is the one dark rectangle nobody asked for. */
2901
+ /* `auto` is the palette of the nearest element that says which theme it is in,
2902
+ however deep the nesting goes: a light preview box inside a dark page, or a
2903
+ dark panel inside that. A selector can name a dark ancestor or a light one
2904
+ but not which of the two is nearer, so the palette `auto` reads is declared
2905
+ on every theme root instead, the way the tokens at the top of this file are,
2906
+ and inheritance is what picks the nearest. The system preference is the
2907
+ root's answer when the page has not given one, and both ways of forcing
2908
+ light are named, so a page that has said it is light leaves the block light
2909
+ either way. */
2910
+ :root,
2911
+ .light,
2912
+ [data-theme='light'] {
2913
+ --p-code-auto-bg: oklch(98.4% 0.003 262);
2914
+ --p-code-auto-fg: oklch(30% 0.016 262);
2915
+
2916
+ --p-code-auto-comment: oklch(58% 0.022 262);
2917
+ --p-code-auto-keyword: oklch(48% 0.19 322);
2918
+ --p-code-auto-string: oklch(46% 0.12 148);
2919
+ --p-code-auto-number: oklch(50% 0.14 48);
2920
+ --p-code-auto-function: oklch(48% 0.17 264);
2921
+ --p-code-auto-type: oklch(45% 0.1 214);
2922
+ --p-code-auto-variable: oklch(45% 0.12 28);
2923
+ --p-code-auto-tag: oklch(48% 0.18 22);
2924
+ --p-code-auto-attr: oklch(47% 0.13 82);
2925
+ --p-code-auto-meta: oklch(52% 0.05 262);
2926
+ --p-code-auto-add: oklch(45% 0.14 152);
2927
+ --p-code-auto-del: oklch(48% 0.18 25);
2928
+ }
2929
+
2180
2930
  @media (prefers-color-scheme: dark) {
2181
- :root:not(.light):not([data-theme='light']) .plass-code[data-code-theme='auto'] {
2182
- --p-code-bg: oklch(19.5% 0.014 262);
2183
- --p-code-fg: oklch(90% 0.012 262);
2931
+ :root:not(.light):not([data-theme='light']) {
2932
+ --p-code-auto-bg: oklch(19.5% 0.014 262);
2933
+ --p-code-auto-fg: oklch(90% 0.012 262);
2184
2934
 
2185
- --p-code-comment: oklch(60% 0.024 262);
2186
- --p-code-keyword: oklch(76% 0.13 324);
2187
- --p-code-string: oklch(80% 0.13 148);
2188
- --p-code-number: oklch(81% 0.12 62);
2189
- --p-code-function: oklch(78% 0.12 258);
2190
- --p-code-type: oklch(82% 0.1 210);
2191
- --p-code-variable: oklch(82% 0.09 32);
2192
- --p-code-tag: oklch(77% 0.13 22);
2193
- --p-code-attr: oklch(85% 0.11 92);
2194
- --p-code-meta: oklch(70% 0.05 262);
2195
- --p-code-add: oklch(80% 0.14 152);
2196
- --p-code-del: oklch(75% 0.15 25);
2935
+ --p-code-auto-comment: oklch(60% 0.024 262);
2936
+ --p-code-auto-keyword: oklch(76% 0.13 324);
2937
+ --p-code-auto-string: oklch(80% 0.13 148);
2938
+ --p-code-auto-number: oklch(81% 0.12 62);
2939
+ --p-code-auto-function: oklch(78% 0.12 258);
2940
+ --p-code-auto-type: oklch(82% 0.1 210);
2941
+ --p-code-auto-variable: oklch(82% 0.09 32);
2942
+ --p-code-auto-tag: oklch(77% 0.13 22);
2943
+ --p-code-auto-attr: oklch(85% 0.11 92);
2944
+ --p-code-auto-meta: oklch(70% 0.05 262);
2945
+ --p-code-auto-add: oklch(80% 0.14 152);
2946
+ --p-code-auto-del: oklch(75% 0.15 25);
2197
2947
  }
2198
2948
  }
2199
2949
 
2950
+ .dark,
2951
+ [data-theme='dark'] {
2952
+ --p-code-auto-bg: oklch(19.5% 0.014 262);
2953
+ --p-code-auto-fg: oklch(90% 0.012 262);
2954
+
2955
+ --p-code-auto-comment: oklch(60% 0.024 262);
2956
+ --p-code-auto-keyword: oklch(76% 0.13 324);
2957
+ --p-code-auto-string: oklch(80% 0.13 148);
2958
+ --p-code-auto-number: oklch(81% 0.12 62);
2959
+ --p-code-auto-function: oklch(78% 0.12 258);
2960
+ --p-code-auto-type: oklch(82% 0.1 210);
2961
+ --p-code-auto-variable: oklch(82% 0.09 32);
2962
+ --p-code-auto-tag: oklch(77% 0.13 22);
2963
+ --p-code-auto-attr: oklch(85% 0.11 92);
2964
+ --p-code-auto-meta: oklch(70% 0.05 262);
2965
+ --p-code-auto-add: oklch(80% 0.14 152);
2966
+ --p-code-auto-del: oklch(75% 0.15 25);
2967
+ }
2968
+
2969
+ .plass-code[data-code-theme='auto'] {
2970
+ --p-code-bg: var(--p-code-auto-bg);
2971
+ --p-code-fg: var(--p-code-auto-fg);
2972
+
2973
+ --p-code-comment: var(--p-code-auto-comment);
2974
+ --p-code-keyword: var(--p-code-auto-keyword);
2975
+ --p-code-string: var(--p-code-auto-string);
2976
+ --p-code-number: var(--p-code-auto-number);
2977
+ --p-code-function: var(--p-code-auto-function);
2978
+ --p-code-type: var(--p-code-auto-type);
2979
+ --p-code-variable: var(--p-code-auto-variable);
2980
+ --p-code-tag: var(--p-code-auto-tag);
2981
+ --p-code-attr: var(--p-code-auto-attr);
2982
+ --p-code-meta: var(--p-code-auto-meta);
2983
+ --p-code-add: var(--p-code-auto-add);
2984
+ --p-code-del: var(--p-code-auto-del);
2985
+ }
2986
+
2200
2987
  /*
2201
2988
  * `mono` is the one theme that follows the page, and the only one with no hue
2202
2989
  * in it at all: the structure is carried by weight and by how far a run is
@@ -2569,19 +3356,30 @@
2569
3356
  * tiles across and a layout grid is counted in tracks, and two is the narrowest
2570
3357
  * arrangement that still reads as a set.
2571
3358
  *
2572
- * The three lines below are what the slot is *for*. A grid and a quilt lay out
2573
- * in CSS Grid off `--p-cols`; a masonry deals its items into lanes in JavaScript
2574
- * and writes the count and the rows inline, because which lane an item lands in
2575
- * depends on the heights of the items before it and no stylesheet can work that
2576
- * out; and a justified row is flexbox, which needs no count at all — it fills
2577
- * to the edge with whatever fits.
3359
+ * The rules below are what the slot is *for*. A grid, a quilt and a masonry lay
3360
+ * out in CSS Grid off `--p-cols`. A masonry also deals its items into lanes in
3361
+ * JavaScript, because which lane an item lands in depends on the heights of the
3362
+ * items before it and no stylesheet can work that out; it deals once for every
3363
+ * lane count the columns name and writes each deal as `--p-lane-*` slots, which
3364
+ * the chains above switch between, so a server's markup has the right lanes at
3365
+ * every width. A justified row is flexbox, which needs no count at all — it
3366
+ * fills to the edge with whatever fits.
2578
3367
  * ------------------------------------------------------------------------- */
2579
3368
 
2580
3369
  .plass-gallery-grid,
2581
- .plass-gallery-quilted {
3370
+ .plass-gallery-quilted,
3371
+ .plass-gallery-masonry {
2582
3372
  grid-template-columns: repeat(var(--p-cols, 2), minmax(0, 1fr));
2583
3373
  }
2584
3374
 
3375
+ .plass-gallery-masonry {
3376
+ grid-template-rows: var(--p-lane-rows);
3377
+ }
3378
+
3379
+ .plass-gallery-masonry > li {
3380
+ grid-area: var(--p-lane-area);
3381
+ }
3382
+
2585
3383
  /*
2586
3384
  * What holds a justified gallery's last row down.
2587
3385
  *
@@ -2608,6 +3406,145 @@
2608
3406
  flex-basis: 0;
2609
3407
  }
2610
3408
 
3409
+ /* ---------------------------------------------------------------------------
3410
+ * The float label
3411
+ *
3412
+ * `labelPlacement="float"`: a notched field whose label rests inside the
3413
+ * control, where the value would be written, while the control is empty,
3414
+ * nothing in the field has the focus and no popup of its is open. At rest it is
3415
+ * set the way that placeholder is — the value's size, the regular weight and
3416
+ * the muted ink — and the moment any of the three stops being true, the label
3417
+ * rises into the notch at the notch's own weight and the placeholder comes
3418
+ * back.
3419
+ *
3420
+ * `internal/notch.tsx` draws the same fieldset and legend a `notch` does and
3421
+ * hands this block its numbers as custom properties on the frame. It is CSS
3422
+ * rather than Tailwind because the condition is a relation between three
3423
+ * elements — the frame's focus, the control's emptiness and a trigger's popup
3424
+ * — and because an input can say it is empty through `:placeholder-shown`
3425
+ * before any script has run, so a server-rendered field is right on its first
3426
+ * frame.
3427
+ *
3428
+ * **The label moves on `top`, `inset-inline-start`, `font-size` and
3429
+ * `font-weight`,** never on a transform: it is set again at every size on the
3430
+ * way rather than scaled, so no frame of it is a resampled picture of the word.
3431
+ * How smoothly the weight goes is the font's to say: a variable font passes
3432
+ * through every weight between, and a font with a few fixed faces steps
3433
+ * between them. Its line box is held at the notch's height throughout, which
3434
+ * is what keeps the legend, and the edge drawn across its middle, where they
3435
+ * are while the word grows and shrinks.
3436
+ *
3437
+ * **The gap folds rather than fades.** A legend cannot fill its own gap, so the
3438
+ * legend is folded to nothing under a resting label and the fieldset draws its
3439
+ * whole top edge again. It opens at once as the label starts to rise, so the
3440
+ * word never crosses a line on its way up, and closes only once the label has
3441
+ * come all the way down, so the line never runs through it on the way down.
3442
+ * ------------------------------------------------------------------------- */
3443
+
3444
+ .plass-notch-float > fieldset > legend {
3445
+ max-width: 100%;
3446
+ white-space: nowrap;
3447
+ transition: max-width 0s;
3448
+ }
3449
+
3450
+ .plass-notch-float > fieldset > legend > * {
3451
+ display: block;
3452
+ position: relative;
3453
+ top: 0;
3454
+ inset-inline-start: 0;
3455
+ margin-inline: var(--p-notch-pad);
3456
+ line-height: var(--p-notch-leading);
3457
+ transition-property: top, inset-inline-start, font-size, font-weight, color;
3458
+ transition-duration: var(--plass-duration);
3459
+ transition-timing-function: var(--plass-ease);
3460
+ }
3461
+
3462
+ /*
3463
+ * At rest. Three ways to know the control is empty, one for each kind of
3464
+ * control: an input's `:placeholder-shown`, a select trigger's
3465
+ * `data-placeholder`, and `data-empty` on the frame from a component that knows
3466
+ * and has no control that can say so, a picker's trigger. Autofill counts as a
3467
+ * value before the reader has accepted it, since its text is already drawn where
3468
+ * the label would rest.
3469
+ *
3470
+ * The same condition is written out in front of each of the three rules below,
3471
+ * since the stylesheet ships to projects whose own build may not lower nesting.
3472
+ */
3473
+ .plass-notch-float:not(:focus-within):not(:has([data-popup-open])):is(
3474
+ [data-empty],
3475
+ :has(.plass-float-control:placeholder-shown:not(:autofill)),
3476
+ :has(.plass-float-control[data-placeholder])
3477
+ )
3478
+ > fieldset
3479
+ > legend {
3480
+ max-width: 0.01px;
3481
+ transition-delay: var(--p-float-fold, var(--plass-duration));
3482
+ /* A press on the words lands on the control under them, as it would on the
3483
+ placeholder they stand in for. A select opens on the pointer going down on
3484
+ its trigger, which a label in front of it would swallow. */
3485
+ pointer-events: none;
3486
+ }
3487
+
3488
+ .plass-notch-float:not(:focus-within):not(:has([data-popup-open])):is(
3489
+ [data-empty],
3490
+ :has(.plass-float-control:placeholder-shown:not(:autofill)),
3491
+ :has(.plass-float-control[data-placeholder])
3492
+ )
3493
+ > fieldset
3494
+ > legend
3495
+ > * {
3496
+ top: var(--p-float-y);
3497
+ inset-inline-start: var(--p-float-x, 0px);
3498
+ font-size: var(--p-float-text);
3499
+ font-weight: 400;
3500
+ color: var(--plass-muted-fg);
3501
+ }
3502
+
3503
+ /*
3504
+ * The placeholder steps out of sight while the label rests where it is written,
3505
+ * and only out of sight: it stays in the accessibility tree. A reader who moves
3506
+ * to the control gives it the focus, which raises the label and brings the
3507
+ * placeholder back, so what a screen reader says there is what the page shows.
3508
+ * The Flutter build keeps it the same way.
3509
+ */
3510
+ .plass-notch-float:not(:focus-within):not(:has([data-popup-open])):is(
3511
+ [data-empty],
3512
+ :has(.plass-float-control:placeholder-shown:not(:autofill)),
3513
+ :has(.plass-float-control[data-placeholder])
3514
+ )
3515
+ .plass-float-control::placeholder,
3516
+ .plass-notch-float:not(:focus-within):not(:has([data-popup-open])):is(
3517
+ [data-empty],
3518
+ :has(.plass-float-control:placeholder-shown:not(:autofill)),
3519
+ :has(.plass-float-control[data-placeholder])
3520
+ )
3521
+ .plass-float-placeholder {
3522
+ color: transparent;
3523
+ }
3524
+
3525
+ /* The placeholder comes back as the label leaves, over the same time. */
3526
+ .plass-notch-float .plass-float-control::placeholder,
3527
+ .plass-notch-float .plass-float-placeholder {
3528
+ transition: color var(--plass-duration) var(--plass-ease);
3529
+ }
3530
+
3531
+ /*
3532
+ * A press that went down on the control comes up on it. The focus the press
3533
+ * hands the control ends the rest before the button is let go, so the label is
3534
+ * back in reach of the pointer while it is still lying over the words, and a
3535
+ * quick click on them came up on the label. The browser then sent the click to
3536
+ * the frame round both, and a picker that opens on a click, such as
3537
+ * `PlTreeSelect`, stayed shut. The label lets the pointer through until that
3538
+ * press is over.
3539
+ *
3540
+ * Only a press on the control: one on the label itself leaves the control out
3541
+ * of the chain `:active` marks, so clicking a risen label still focuses the
3542
+ * field.
3543
+ */
3544
+ .plass-notch-float:has(> :not(fieldset):active) > fieldset > legend {
3545
+ pointer-events: none;
3546
+ }
3547
+
2611
3548
  /* ---------------------------------------------------------------------------
2612
3549
  * Reduced motion
2613
3550
  *
@@ -2615,7 +3552,9 @@
2615
3552
  * transitions: someone who has asked for less movement is asking for state
2616
3553
  * changes to *arrive*, not to be taken away. The colours still change; they
2617
3554
  * simply stop easing, and the light stops trailing the pointer across the
2618
- * surface — it is either on the control or it is not.
3555
+ * surface — it is either on the control or it is not. The colours are the house
3556
+ * transition's, which is a set of utilities rather than a class, so it says so
3557
+ * beside itself in `internal/styles.ts`.
2619
3558
  * ------------------------------------------------------------------------- */
2620
3559
 
2621
3560
  @media (prefers-reduced-motion: reduce) {
@@ -2624,12 +3563,30 @@
2624
3563
  transition-duration: 1ms;
2625
3564
  }
2626
3565
 
3566
+ /* A fill that comes with a state arrives with it, as the colours round it
3567
+ do. */
3568
+ .plass-fill::before,
3569
+ .plass-fill-layer {
3570
+ transition-duration: 0ms;
3571
+ }
3572
+
3573
+ /* A float label arrives where it is going, and the gap under it opens and
3574
+ closes with it rather than a moment after. */
3575
+ .plass-notch-float {
3576
+ --p-float-fold: 0ms;
3577
+ }
3578
+
3579
+ .plass-notch-float > fieldset > legend > *,
3580
+ .plass-notch-float .plass-float-control::placeholder,
3581
+ .plass-notch-float .plass-float-placeholder {
3582
+ transition-duration: 0ms;
3583
+ }
3584
+
2627
3585
  /* The highlight stops travelling and the placeholder pulses in place. It is
2628
3586
  kept running rather than stopped, because a skeleton that holds still is
2629
3587
  indistinguishable from an empty box that finished loading with nothing in
2630
3588
  it. */
2631
3589
  .plass-skeleton::after {
2632
- inset-inline-start: 0;
2633
3590
  inline-size: 100%;
2634
3591
  background-image: none;
2635
3592
  animation: plass-skeleton-pulse 2.4s var(--plass-ease) infinite;
@@ -2647,7 +3604,6 @@
2647
3604
  two that genuinely have to keep turning or cycling are slowed to where they
2648
3605
  stop reading as motion. */
2649
3606
  .plass-progress-sweep {
2650
- inset-inline-start: 0;
2651
3607
  inline-size: 100%;
2652
3608
  animation: plass-progress-pulse 1.8s var(--plass-ease) infinite;
2653
3609
  }
@@ -2663,17 +3619,44 @@
2663
3619
  /*
2664
3620
  * The `PlAnimate*` components go the other way from the indicators above, and
2665
3621
  * the difference is what each of them is saying. A spinner that stops is
2666
- * lying about whether anything is happening; an entrance that does not play
2667
- * has still delivered its content, which is the only thing it was ever
2668
- * carrying.
3622
+ * lying about whether anything is happening; an effect that does not move has
3623
+ * still delivered what it was carrying — which for an entrance is the content
3624
+ * and for an exit is its absence.
3625
+ *
3626
+ * So an effect is not switched off but run in no time at all. Nothing changes
3627
+ * until the moment it would have started, its `delay` included, and then it
3628
+ * is on its last frame: an exit has gone, a turn has turned, and
3629
+ * `animationend` arrives for a caller waiting on it. Until that moment the
3630
+ * element is as it is, because the fill runs forwards only — an effect
3631
+ * waiting for its trigger is held at the start of its delay, which with no
3632
+ * backwards fill draws nothing, rather than on a first frame at `opacity: 0`
3633
+ * that would blank the page. The delay is a millisecond at the least, which
3634
+ * is what keeps a waiting effect there: with no delay and no duration, a run
3635
+ * held at its very start has already finished.
2669
3636
  *
2670
- * `animation: none` rather than a zero duration, because the shorthand also
2671
- * clears `fill-mode` — and the fill is what was holding an untriggered
2672
- * element on its own first frame at `opacity: 0`. Left in place, respecting a
2673
- * reader's preference would blank the page.
3637
+ * A count of `infinite` is one pass here, which `--p-anim-still-repeat`
3638
+ * says: an endless run has no last frame of its own. And a scroll-linked
3639
+ * effect goes back to the clock, because a zero duration spread over a scroll
3640
+ * range is an effect that snaps at one point of the scroll.
2674
3641
  */
2675
- .plass-anim,
2676
- .plass-marquee-track,
3642
+ .plass-anim {
3643
+ animation-duration: 0s;
3644
+ animation-delay: max(1ms, var(--p-anim-run-delay, var(--p-anim-delay, 0ms)));
3645
+ animation-iteration-count: var(--p-anim-still-repeat, var(--p-anim-repeat, 1));
3646
+ animation-fill-mode: forwards;
3647
+ animation-timeline: auto;
3648
+ }
3649
+
3650
+ /* The one effect whose last frame is not the element's own: before it has
3651
+ run, a turn stands at the angle it turns to, which is what it is waiting to
3652
+ deliver. The keyframe outranks this once the run has begun. */
3653
+ .plass-anim-rotate {
3654
+ rotate: var(--p-anim-angle-to, 0deg);
3655
+ }
3656
+
3657
+ /* A strip that ends is the exception, below. `:where()` keeps the selector
3658
+ as specific as `.plass-marquee-track` on its own. */
3659
+ :where(.plass-marquee:not([data-plass-finite])) > .plass-marquee-track,
2677
3660
  .plass-headline-item,
2678
3661
  .plass-caret {
2679
3662
  animation: none;
@@ -2704,6 +3687,105 @@
2704
3687
  skeleton makes: the decoration survives, the motion does not. */
2705
3688
  .plass-anim-lighting::before {
2706
3689
  background: var(--p-anim-glow-to);
3690
+ }
3691
+
3692
+ .plass-anim-lighting:not([data-plass-finite])::before {
2707
3693
  animation: none;
2708
3694
  }
3695
+
3696
+ /*
3697
+ * A strip or a light that ends is run in no time instead, as an effect
3698
+ * above is, so that it lands and `animationend` arrives. Neither fills
3699
+ * either side of its run, so nothing it draws moves: the strip stands where
3700
+ * it started and the arc is the even glow. `src/internal/animate.ts` marks
3701
+ * one that has landed, which is what keeps it there when the reader gives
3702
+ * movement back; an endless one is switched off and goes on from its start.
3703
+ */
3704
+ .plass-marquee[data-plass-finite] > .plass-marquee-track,
3705
+ .plass-anim-lighting[data-plass-finite]::before {
3706
+ animation-duration: 0s;
3707
+ animation-delay: max(1ms, var(--p-anim-run-delay, var(--p-anim-delay, 0ms)));
3708
+ animation-fill-mode: none;
3709
+ }
3710
+ }
3711
+
3712
+ /*
3713
+ * A run that landed under reduced motion, until it runs again.
3714
+ *
3715
+ * Its keyframe goes on counting its time from when it began, so a fade that
3716
+ * landed in no time and was handed its duration back while it would still have
3717
+ * been running stood that far into the run and played on from there. The
3718
+ * timing the block above lands it with holds it on its last frame whatever the
3719
+ * setting is now. `src/internal/animate.ts` marks a finite run on the clock
3720
+ * when it lands, and takes the mark off when the run starts again. An endless
3721
+ * run is never marked: it has no last frame to stay on, and goes on from where
3722
+ * its passes would have got to.
3723
+ *
3724
+ * Unless `paused` is holding it, finite or endless. A paused keyframe stands
3725
+ * where its clock stopped: the start of a pass for an endless run that landed,
3726
+ * so an endless fade held while the reader gave movement back went from fully
3727
+ * drawn to nearly gone, and ahead of its start for a run held before its delay
3728
+ * was over, so a fade held there went from fully drawn to nothing and a turn
3729
+ * from its `to` angle back to its `from`. The script marks such a run while
3730
+ * reduced motion shows it, and takes the mark off when it is let go, which
3731
+ * plays it on from where its clock stands. Held before its delay was over, it
3732
+ * stands where the block above stands a waiting effect, which is where a turn
3733
+ * stands too.
3734
+ */
3735
+ .plass-anim[data-plass-landed],
3736
+ .plass-anim[data-plass-held] {
3737
+ animation-duration: 0s;
3738
+ animation-delay: max(1ms, var(--p-anim-run-delay, var(--p-anim-delay, 0ms)));
3739
+ animation-iteration-count: var(--p-anim-still-repeat, var(--p-anim-repeat, 1));
3740
+ animation-fill-mode: forwards;
3741
+ }
3742
+
3743
+ .plass-anim-rotate[data-plass-held] {
3744
+ rotate: var(--p-anim-angle-to, 0deg);
3745
+ }
3746
+
3747
+ /*
3748
+ * A light held the same way keeps the even glow the setting draws. Its arc is
3749
+ * left as it is underneath, an endless one made paused at its start when the
3750
+ * setting goes and a finite one paused where its clock stopped, so once it is
3751
+ * let go it turns from there, as it did. Unmarked, the arc came back at the
3752
+ * angle its clock stood at, which is `0deg` for an endless light.
3753
+ */
3754
+ .plass-anim-lighting[data-plass-held]::before {
3755
+ background: var(--p-anim-glow-to);
3756
+ }
3757
+
3758
+ /*
3759
+ * A strip held the same way keeps the one copy the setting draws, in the box
3760
+ * that scrolls along it, so what the reader scrolled to stays where it is.
3761
+ * Unmarked, the copies came back and the box clipped them while the strip was
3762
+ * still paused. Its keyframe is left as it is underneath, which already stands
3763
+ * where the setting showed it, so once it is let go the strip goes on as it
3764
+ * would have without the hold. The child combinator leaves a strip nested
3765
+ * inside it alone.
3766
+ */
3767
+ .plass-marquee[data-plass-held] {
3768
+ overflow-x: auto;
3769
+ overflow-y: hidden;
3770
+ }
3771
+
3772
+ .plass-marquee-vertical[data-plass-held] {
3773
+ overflow-x: hidden;
3774
+ overflow-y: auto;
3775
+ }
3776
+
3777
+ .plass-marquee[data-plass-held] > .plass-marquee-track + .plass-marquee-track {
3778
+ display: none;
3779
+ }
3780
+
3781
+ /*
3782
+ * A strip or a light that landed under reduced motion, marked on its root, has
3783
+ * its keyframe taken off until it runs again: the strip, every copy of it,
3784
+ * stands where it started, where the setting showed it, and the arc where a
3785
+ * turn ends, which is where it begins. Played again from the start, a finite
3786
+ * marquee or light set off on its whole run once more.
3787
+ */
3788
+ .plass-marquee[data-plass-landed] > .plass-marquee-track,
3789
+ .plass-anim-lighting[data-plass-landed]::before {
3790
+ animation-name: none;
2709
3791
  }