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