plass-ui 1.5.0 → 1.7.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 (183) hide show
  1. package/CHANGELOG.md +336 -0
  2. package/README.md +29 -7
  3. package/dist/components/accordion/PlAccordion.d.ts +13 -0
  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/animate-counter/PlAnimateCounter.js +1 -1
  8. package/dist/components/animate-float/PlAnimateFloat.js +1 -1
  9. package/dist/components/animate-headline/PlAnimateHeadline.d.ts +10 -1
  10. package/dist/components/animate-marquee/PlAnimateMarquee.d.ts +11 -1
  11. package/dist/components/animate-marquee/PlAnimateMarquee.js +1 -1
  12. package/dist/components/animate-scramble/PlAnimateScramble.js +1 -1
  13. package/dist/components/animate-split/PlAnimateSplit.js +1 -1
  14. package/dist/components/animate-typing/PlAnimateTyping.js +1 -1
  15. package/dist/components/app-logo/PlAppLogo.js +1 -1
  16. package/dist/components/area-chart/PlAreaChart.d.ts +35 -6
  17. package/dist/components/area-chart/PlAreaChart.js +1 -1
  18. package/dist/components/aspect-ratio/PlAspectRatio.js +1 -1
  19. package/dist/components/avatar/PlAvatar.js +1 -1
  20. package/dist/components/back-top/PlBackTop.d.ts +6 -0
  21. package/dist/components/back-top/PlBackTop.js +1 -1
  22. package/dist/components/badge/PlBadge.js +1 -1
  23. package/dist/components/bar-chart/PlBarChart.d.ts +54 -2
  24. package/dist/components/bar-chart/PlBarChart.js +1 -1
  25. package/dist/components/blockquote/PlBlockquote.d.ts +2 -2
  26. package/dist/components/blockquote/PlBlockquote.js +1 -1
  27. package/dist/components/bottom-navigation/PlBottomNavigation.js +1 -1
  28. package/dist/components/breadcrumb/PlBreadcrumb.js +1 -1
  29. package/dist/components/button/PlButton.d.ts +7 -0
  30. package/dist/components/button/PlButton.js +1 -1
  31. package/dist/components/calendar/PlCalendar.js +1 -1
  32. package/dist/components/card/PlCard.d.ts +7 -4
  33. package/dist/components/carousel/PlCarousel.d.ts +14 -4
  34. package/dist/components/carousel/PlCarousel.js +1 -1
  35. package/dist/components/chat-bubble/PlChatBubble.js +1 -1
  36. package/dist/components/checkbox/PlCheckbox.js +1 -1
  37. package/dist/components/chip/PlChip.d.ts +2 -1
  38. package/dist/components/chip/PlChip.js +1 -1
  39. package/dist/components/collapsible/PlCollapsible.d.ts +11 -0
  40. package/dist/components/collapsible/PlCollapsible.js +1 -1
  41. package/dist/components/color-picker/PlColorPicker.d.ts +21 -2
  42. package/dist/components/color-picker/PlColorPicker.js +1 -1
  43. package/dist/components/combobox/PlCombobox.d.ts +18 -6
  44. package/dist/components/combobox/PlCombobox.js +1 -1
  45. package/dist/components/command-palette/PlCommandPalette.js +1 -1
  46. package/dist/components/confirm/PlConfirmProvider.js +1 -1
  47. package/dist/components/data-table/PlDataTable.d.ts +13 -2
  48. package/dist/components/data-table/PlDataTable.js +1 -1
  49. package/dist/components/date-picker/PlDatePicker.js +1 -1
  50. package/dist/components/date-range-picker/PlDateRangePicker.js +1 -1
  51. package/dist/components/drawer/PlDrawer.js +1 -1
  52. package/dist/components/empty/PlEmpty.js +1 -1
  53. package/dist/components/fieldset/PlFieldset.js +1 -1
  54. package/dist/components/file-picker/PlFilePicker.d.ts +20 -2
  55. package/dist/components/file-picker/PlFilePicker.js +1 -1
  56. package/dist/components/floating-action-button/PlFloatingActionButton.d.ts +5 -0
  57. package/dist/components/floating-action-button/PlFloatingActionButton.js +1 -1
  58. package/dist/components/floating-bottom-navigation/PlFloatingBottomNavigation.js +1 -1
  59. package/dist/components/footer/PlFooter.d.ts +3 -1
  60. package/dist/components/gallery/PlGallery.js +1 -1
  61. package/dist/components/gauge-chart/PlGaugeChart.js +1 -1
  62. package/dist/components/header/PlHeader.d.ts +4 -2
  63. package/dist/components/header/PlHeader.js +1 -1
  64. package/dist/components/heatmap-chart/PlHeatmapChart.js +1 -1
  65. package/dist/components/highlight/PlHighlight.d.ts +10 -3
  66. package/dist/components/hover-card/PlHoverCard.js +1 -1
  67. package/dist/components/how-to-steps/PlHowToSteps.js +1 -1
  68. package/dist/components/image/PlImage.js +1 -1
  69. package/dist/components/line-chart/PlLineChart.d.ts +32 -6
  70. package/dist/components/line-chart/PlLineChart.js +1 -1
  71. package/dist/components/list/PlList.js +1 -1
  72. package/dist/components/menu/PlMenu.js +1 -1
  73. package/dist/components/mockup/PlMockup.d.ts +5 -0
  74. package/dist/components/mockup/PlMockup.js +1 -1
  75. package/dist/components/modal/PlModal.js +1 -1
  76. package/dist/components/navigation-menu/PlNavigationMenu.d.ts +4 -3
  77. package/dist/components/navigation-menu/PlNavigationMenu.js +1 -1
  78. package/dist/components/number-field/PlNumberField.d.ts +14 -4
  79. package/dist/components/number-field/PlNumberField.js +1 -1
  80. package/dist/components/otp-field/PlOtpField.d.ts +9 -1
  81. package/dist/components/otp-field/PlOtpField.js +1 -1
  82. package/dist/components/overlay/PlOverlay.js +1 -1
  83. package/dist/components/page-layout/PlPageLayout.d.ts +14 -3
  84. package/dist/components/page-layout/PlPageLayout.js +1 -1
  85. package/dist/components/pagination/PlPagination.js +1 -1
  86. package/dist/components/panes/PlPanes.js +1 -1
  87. package/dist/components/pie-chart/PlPieChart.d.ts +26 -1
  88. package/dist/components/pie-chart/PlPieChart.js +1 -1
  89. package/dist/components/pill/PlPill.js +1 -1
  90. package/dist/components/popconfirm/PlPopconfirm.d.ts +8 -1
  91. package/dist/components/popconfirm/PlPopconfirm.js +1 -1
  92. package/dist/components/popover/PlPopover.js +1 -1
  93. package/dist/components/progress-box/PlProgressBox.js +1 -1
  94. package/dist/components/progress-circular/PlProgressCircular.js +1 -1
  95. package/dist/components/progress-linear/PlProgressLinear.js +1 -1
  96. package/dist/components/radio-group/PlRadioGroup.js +1 -1
  97. package/dist/components/rating/PlRating.js +1 -1
  98. package/dist/components/scatter-chart/PlScatterChart.js +1 -1
  99. package/dist/components/scroll-area/PlScrollArea.js +1 -1
  100. package/dist/components/segmented-button/PlSegmentedButton.js +1 -1
  101. package/dist/components/select/PlSelect.d.ts +11 -2
  102. package/dist/components/select/PlSelect.js +1 -1
  103. package/dist/components/sidebar/PlSidebar.js +1 -1
  104. package/dist/components/slider/PlSlider.js +1 -1
  105. package/dist/components/sparkline/PlSparkline.js +1 -1
  106. package/dist/components/stepper/PlStepper.js +1 -1
  107. package/dist/components/switch/PlSwitch.js +1 -1
  108. package/dist/components/table/PlTable.d.ts +11 -1
  109. package/dist/components/table/PlTable.js +1 -1
  110. package/dist/components/tabs/PlTabs.d.ts +18 -1
  111. package/dist/components/tabs/PlTabs.js +1 -1
  112. package/dist/components/text-field/PlTextField.d.ts +13 -5
  113. package/dist/components/text-field/PlTextField.js +1 -1
  114. package/dist/components/text-link/PlTextLink.js +1 -1
  115. package/dist/components/timeline/PlTimeline.js +1 -1
  116. package/dist/components/timeline-chart/PlTimelineChart.js +1 -1
  117. package/dist/components/toast/PlToast.js +1 -1
  118. package/dist/components/toggle/PlToggle.js +1 -1
  119. package/dist/components/tooltip/PlTooltip.d.ts +4 -0
  120. package/dist/components/tooltip/PlTooltip.js +1 -1
  121. package/dist/components/tour/PlTour.js +1 -1
  122. package/dist/components/transfer/PlTransfer.d.ts +5 -2
  123. package/dist/components/transfer/PlTransfer.js +1 -1
  124. package/dist/components/tree/PlTree.js +1 -1
  125. package/dist/components/tree-select/PlTreeSelect.js +1 -1
  126. package/dist/components/typography/PlTypography.js +1 -1
  127. package/dist/components/window-pane/PlWindowPane.d.ts +16 -6
  128. package/dist/components/window-pane/PlWindowPane.js +1 -1
  129. package/dist/internal/animate.d.ts +0 -25
  130. package/dist/internal/animate.js +1 -1
  131. package/dist/internal/bottom-bar.d.ts +29 -0
  132. package/dist/internal/bottom-bar.js +1 -0
  133. package/dist/internal/calendar.js +1 -1
  134. package/dist/internal/chart-frame.d.ts +90 -27
  135. package/dist/internal/chart-frame.js +1 -1
  136. package/dist/internal/chart-line.d.ts +11 -6
  137. package/dist/internal/chart-line.js +1 -1
  138. package/dist/internal/chart.d.ts +158 -1
  139. package/dist/internal/chart.js +1 -1
  140. package/dist/internal/defaults.d.ts +14 -1
  141. package/dist/internal/focusable.d.ts +10 -0
  142. package/dist/internal/focusable.js +1 -1
  143. package/dist/internal/form.d.ts +29 -0
  144. package/dist/internal/form.js +1 -1
  145. package/dist/internal/glow.d.ts +47 -1
  146. package/dist/internal/glow.js +1 -1
  147. package/dist/internal/icons.d.ts +24 -0
  148. package/dist/internal/icons.js +1 -1
  149. package/dist/internal/labels.d.ts +35 -0
  150. package/dist/internal/labels.js +1 -1
  151. package/dist/internal/loading.d.ts +13 -0
  152. package/dist/internal/loading.js +1 -0
  153. package/dist/internal/mockup.js +1 -1
  154. package/dist/internal/notch.d.ts +137 -0
  155. package/dist/internal/notch.js +1 -0
  156. package/dist/internal/picker.d.ts +15 -3
  157. package/dist/internal/picker.js +1 -1
  158. package/dist/internal/progress.d.ts +17 -6
  159. package/dist/internal/progress.js +1 -1
  160. package/dist/internal/sizer.d.ts +12 -11
  161. package/dist/internal/sizer.js +1 -1
  162. package/dist/internal/steps.d.ts +0 -15
  163. package/dist/internal/steps.js +1 -1
  164. package/dist/internal/styles.d.ts +135 -8
  165. package/dist/internal/styles.js +1 -1
  166. package/dist/internal/table-scroll.d.ts +42 -0
  167. package/dist/internal/table-scroll.js +1 -0
  168. package/dist/internal/table.js +1 -1
  169. package/dist/internal/text.d.ts +8 -5
  170. package/dist/internal/window.js +1 -1
  171. package/dist/locales/de.js +1 -1
  172. package/dist/locales/es.js +1 -1
  173. package/dist/locales/fr.js +1 -1
  174. package/dist/locales/ja.js +1 -1
  175. package/dist/locales/ko.js +1 -1
  176. package/dist/locales/zh-Hans.js +1 -1
  177. package/dist/provider/PlassProvider.d.ts +4 -3
  178. package/dist/provider/PlassProvider.js +1 -1
  179. package/dist/styles.css +1 -1
  180. package/dist/tailwind.css +461 -46
  181. package/dist/tokens.css +461 -46
  182. package/dist/types.d.ts +270 -11
  183. package/package.json +1 -1
package/dist/tailwind.css CHANGED
@@ -53,8 +53,11 @@
53
53
  * surface rather than looked at.
54
54
  *
55
55
  * Three values are hand-picked per family and everything else is derived with
56
- * `color-mix()`, so **adding a colour family is two edits**: one entry in
57
- * `PlassColor` and three lines here (plus its `accent` in each theme).
56
+ * `color-mix()`. Adding a family is still five places, though: one entry in
57
+ * `PlassColor`, those three lines here, an `accent` in each of the three theme
58
+ * blocks below, the eight derived declarations, which are written out per
59
+ * family rather than generated, and the Flutter package's own enum and
60
+ * families.
58
61
  *
59
62
  * `solid` and `solid-to` are the two ends of the gradient, at 135° — the
60
63
  * top-left corner and the bottom-right one. They are a **hue sweep at one
@@ -169,6 +172,29 @@
169
172
  --plass-breakpoint-xl: var(--breakpoint-xl, 80rem);
170
173
  }
171
174
 
175
+ /* ---------------------------------------------------------------------------
176
+ * The room a fixed bottom bar takes
177
+ *
178
+ * A `PlBottomNavigation` or a `PlFloatingBottomNavigation` is `fixed` by
179
+ * default: held against the bottom of the window and out of the flow, so the
180
+ * end of the page is always under it, and so is a link a reader tabs to down
181
+ * there. Only the page can make room, and only if it knows how much, so a fixed
182
+ * bar measures itself — size, density, names and the home indicator included —
183
+ * and writes the answer here, on the root element, for as long as it is
184
+ * mounted. With no bar it is `0px`, so a page can reserve it unconditionally:
185
+ *
186
+ * main { padding-bottom: var(--plass-bottom-navigation-height); }
187
+ * html { scroll-padding-bottom: var(--plass-bottom-navigation-height); }
188
+ *
189
+ * On a bare `:root`, for the reason `--plass-z-portal` is. It is a reading
190
+ * rather than a setting: a bar replaces whatever is written here the moment it
191
+ * has measured itself.
192
+ * ------------------------------------------------------------------------- */
193
+
194
+ :root {
195
+ --plass-bottom-navigation-height: 0px;
196
+ }
197
+
172
198
  /* ---------------------------------------------------------------------------
173
199
  * Light theme (default)
174
200
  *
@@ -269,6 +295,20 @@
269
295
  --plass-glow-on-fill: rgb(255 255 255 / 0.18);
270
296
  --plass-flash-on-fill: rgb(255 255 255 / 0.26);
271
297
 
298
+ /* How much of that light a **field** carries, as a share of what a key gets.
299
+
300
+ A key is pressed and is finished with; a field is pressed once and then
301
+ written in for a minute, with the bloom sitting under the words being typed
302
+ and following the hand that is typing them. The same strength that reads as
303
+ a control answering a pointer reads as a stain on a paragraph. So a field
304
+ takes the family's tint at a little over half, which is enough to say the
305
+ surface answered and not enough to compete with the ink on it.
306
+
307
+ One value for both layers and for every family, because the thing being
308
+ dialled down is the effect rather than a colour. It is per-theme neither:
309
+ the tints it scales are already per-theme. */
310
+ --plass-glow-field-strength: 55%;
311
+
272
312
  /* Elevation, and how strongly a key's own colour bleeds into the shadow it
273
313
  casts. The ambient is a blue-grey rather than black: a neutral shadow over
274
314
  a blue-tinted page reads as dirt — and it is faint, because the tint below
@@ -564,11 +604,21 @@
564
604
  * resolves its `var()`s on the element that declares it — declaring these only
565
605
  * on `:root` would freeze `--plass-*-tint` to the light theme's
566
606
  * `--plass-tint-strength` inside a `.dark` subtree.
607
+ *
608
+ * `.plass-theme` is the same reasoning applied to a *colour* override. Setting
609
+ * `--plass-primary-solid` on a `<div>` changes the base and nothing else,
610
+ * because every value derived from it was computed further up, where the old
611
+ * colour still is. The class re-runs the whole block on that element, so the
612
+ * gradient, the tint, the hairline and the ring follow the base that was just
613
+ * written beside them. It is the one hook here that does not force a theme:
614
+ * `--plass-tint-strength` and the rest inherit from whichever theme root is
615
+ * above, so a scoped family works the same in light and in dark.
567
616
  * ------------------------------------------------------------------------- */
568
617
 
569
618
  :root,
570
619
  .dark,
571
620
  .light,
621
+ .plass-theme,
572
622
  [data-theme='dark'],
573
623
  [data-theme='light'] {
574
624
  /* The elevation ladder. Neutral, wide and faint — it climbs by *blur* far
@@ -793,6 +843,17 @@
793
843
  * never re-renders — the component writes the two custom properties directly.
794
844
  * ------------------------------------------------------------------------- */
795
845
 
846
+ /*
847
+ * A stacking context of the surface's own, which is what lets the bloom below
848
+ * sit at a negative depth without falling through the surface it belongs to.
849
+ * `isolation` rather than a `z-index`, because a z-index is a position in a
850
+ * stack somebody else owns and this is only a statement that the two layers are
851
+ * the surface's business.
852
+ */
853
+ .plass-glow {
854
+ isolation: isolate;
855
+ }
856
+
796
857
  .plass-glow::before,
797
858
  .plass-glow::after {
798
859
  content: '';
@@ -803,8 +864,20 @@
803
864
  opacity: 0;
804
865
  }
805
866
 
806
- /* The bloom trailing the pointer across the surface. */
867
+ /*
868
+ * The bloom trailing the pointer across the surface.
869
+ *
870
+ * **Under the label, and over the fill.** A positioned pseudo-element otherwise
871
+ * paints above every in-flow thing in the box, so the bloom was laid over the
872
+ * words on a key and over the value in a field — faint enough to miss on a
873
+ * white label and not on dark ink. `z-index: -1` puts it where a stacking
874
+ * context paints a negative layer, which is after the element's own background
875
+ * and before its content: over the gradient, under what is written on it. The
876
+ * flash below stays where it is, over everything, which is the pair the Flutter
877
+ * build draws as two separate widgets on either side of the label.
878
+ */
807
879
  .plass-glow::before {
880
+ z-index: -1;
808
881
  background: radial-gradient(
809
882
  6rem circle at var(--p-mx, 50%) var(--p-my, 50%),
810
883
  var(--p-glow, transparent),
@@ -844,6 +917,86 @@
844
917
  transition-duration: 0ms;
845
918
  }
846
919
 
920
+ /*
921
+ * The bloom steps out of the way while the surface is being typed into.
922
+ *
923
+ * A pointer resting on a field is a pointer that has been parked: the hand is
924
+ * on the keyboard, the light is no longer following anything, and what it is
925
+ * doing is sitting under the sentence being written. Fading it out on the first
926
+ * keystroke and back in on the next real pointer move costs the effect nothing —
927
+ * it only ever *meant* anything while the pointer was moving — and gives the
928
+ * text a clean sheet for as long as the reader is on the keys.
929
+ *
930
+ * Only the bloom. The flash is drawn by `:active`, which cannot be held down
931
+ * and typed through.
932
+ *
933
+ * After the `:hover` rule, and at the same specificity: that order is what makes
934
+ * this the one that wins. The component writes and clears the attribute — see
935
+ * `useFieldLight`.
936
+ */
937
+ .plass-glow[data-quiet]::before {
938
+ opacity: 0;
939
+ }
940
+
941
+ /* ---------------------------------------------------------------------------
942
+ * A fill that comes and goes with a state
943
+ *
944
+ * A `solid` toggle, a tick, a radio's ring, a switch's track, a chosen day, a
945
+ * step's bullet and the current page are glass, a groove or nothing while they
946
+ * are off and the family's gradient while they are on, and no browser eases a
947
+ * gradient to or from nothing. Set on the element itself, the gradient arrived
948
+ * in one frame and left in one. So it is a layer of its own, there in both
949
+ * states and faded by its opacity, which is the one thing about it a browser
950
+ * can ease — the way a `PlProgressBox` plate breathes.
951
+ *
952
+ * The component says when it is on with `--p-fill-on`, 0 or 1, from the state
953
+ * variants that used to set the gradient, and a control with an edge says how
954
+ * wide the edge is with `--p-fill-edge`, so the fill runs under the edge it
955
+ * turns transparent, as a background does. Both are only read here, never
956
+ * declared: this is a real class, which a consumer's own Tailwind build keeps
957
+ * outside the layer it puts utilities in, so a value declared here would beat
958
+ * the component's. Left unset, the fill is off and has no edge.
959
+ *
960
+ * Under what is drawn on the control and over its own background, which is what
961
+ * the negative depth in a box of its own is for, as the bloom's is above. It is
962
+ * in CSS rather than in Tailwind for the bloom's reason too: it is a gradient
963
+ * on a pseudo-element.
964
+ * ------------------------------------------------------------------------- */
965
+
966
+ .plass-fill {
967
+ isolation: isolate;
968
+ }
969
+
970
+ .plass-fill::before,
971
+ .plass-fill-layer {
972
+ position: absolute;
973
+ inset: calc(-1 * var(--p-fill-edge, 0px));
974
+ z-index: -1;
975
+ /* An edge as wide as the control's, and as clear, so the gradient is laid out
976
+ inside it and drawn under it exactly as the control's own background was. */
977
+ border: var(--p-fill-edge, 0px) solid transparent;
978
+ border-radius: inherit;
979
+ background-image: var(--p-fill);
980
+ opacity: var(--p-fill-on, 0);
981
+ pointer-events: none;
982
+ transition: opacity var(--plass-duration) var(--plass-ease);
983
+ }
984
+
985
+ .plass-fill::before {
986
+ content: '';
987
+ }
988
+
989
+ /*
990
+ * The same layer as an element, for a control whose two pseudo-elements are
991
+ * already the light: a `solid` `PlToggle` and a `PlPagination` page. The bloom
992
+ * is the `::before` of the same surface, which comes first, so the fill goes
993
+ * one step further down to stay under it. The surface's `.plass-glow` gives
994
+ * both depths a box of their own.
995
+ */
996
+ .plass-fill-layer {
997
+ z-index: -2;
998
+ }
999
+
847
1000
  /* ---------------------------------------------------------------------------
848
1001
  * The three dots in a bubble somebody is typing into
849
1002
  *
@@ -1040,18 +1193,25 @@
1040
1193
  * a writing direction, so the measurement is turned round once, here, rather
1041
1194
  * than in four places that could disagree.
1042
1195
  *
1196
+ * A tab brought into view stops the fade's length short of the edge, through
1197
+ * `scroll-padding-inline`. The reveal is not this sheet's — the browser does it
1198
+ * for a focused tab and Base UI for the chosen one and the next one along — but
1199
+ * both honour the padding, and without it the tab they brought into view would
1200
+ * end flush with the edge, which is exactly where the fade is strongest.
1201
+ *
1043
1202
  * The mask is dropped entirely while something inside holds a visible focus
1044
- * ring. Focusing a tab scrolls it flush against the edge it came from, which is
1045
- * exactly where the fade is strongest — so the one control whose outline has to
1046
- * be legible would be the one drawn half transparent. `:focus-visible` and not
1047
- * `:focus-within`: a tab that was clicked has the focus and no ring, and a bar
1048
- * that stopped fading for the rest of the session because somebody pressed a
1049
- * tab would have lost the signal at the first interaction.
1203
+ * ring, as well: a reader walking the strip with the arrow keys is looking at
1204
+ * the ring, and the one outline on the bar that has to be legible whole should
1205
+ * not be one a fade can reach. `:focus-visible` and not `:focus-within`: a tab
1206
+ * that was clicked has the focus and no ring, and a bar that stopped fading for
1207
+ * the rest of the session because somebody pressed a tab would have lost the
1208
+ * signal at the first interaction.
1050
1209
  * ------------------------------------------------------------------------- */
1051
1210
 
1052
1211
  .plass-fade-x {
1053
1212
  --p-fade: 1.5rem;
1054
1213
 
1214
+ scroll-padding-inline: var(--p-fade);
1055
1215
  scrollbar-width: none;
1056
1216
  mask-image: linear-gradient(
1057
1217
  to right,
@@ -1866,13 +2026,28 @@
1866
2026
  animation-name: plass-anim-lighting;
1867
2027
  animation-duration: var(--p-anim-duration, 3s);
1868
2028
  animation-delay: var(--p-anim-delay, 0ms);
1869
- animation-timing-function: linear;
2029
+ /* Linear unless a caller says otherwise, which is what `curve` does in the
2030
+ Flutter build: an arc that eases at both ends of a turn that has no ends
2031
+ reads as a stutter, and that is a default rather than a rule. */
2032
+ animation-timing-function: var(--p-anim-ease, linear);
1870
2033
  animation-iteration-count: var(--p-anim-repeat, infinite);
1871
2034
  animation-direction: var(--p-anim-direction, normal);
1872
2035
  animation-fill-mode: both;
1873
2036
  animation-play-state: var(--p-anim-state, running);
1874
2037
  }
1875
2038
 
2039
+ /*
2040
+ * The rewind, for the one effect that runs on a pseudo-element.
2041
+ *
2042
+ * Restarting a CSS animation means clearing its name, forcing the style to
2043
+ * settle and putting the name back, and an inline style cannot reach a
2044
+ * `::before`. So the root carries `data-plass-rewind` for the length of that
2045
+ * read, and this is what answers it. Nothing is painted in between.
2046
+ */
2047
+ .plass-anim-lighting[data-plass-rewind]::before {
2048
+ animation-name: none;
2049
+ }
2050
+
1876
2051
  /*
1877
2052
  * Marquee: two identical copies, both moving by exactly one copy plus the gap.
1878
2053
  *
@@ -2056,10 +2231,11 @@
2056
2231
  * theme below declares its `fg` and `bg` on that same element — so the derived
2057
2232
  * five follow whichever theme won without being restated fourteen times.
2058
2233
  *
2059
- * The two dark palettes at the top are written twice on purpose — once for
2060
- * `dark`, once for `auto` inside a dark page — because a media query cannot be
2061
- * folded into a selector list. It is the same repetition the theme roots at the
2062
- * top of this file make, for the same reason.
2234
+ * The two house palettes are written more than once on purpose: once on the
2235
+ * block for `light` and `dark`, and again on the theme roots for `auto`, where
2236
+ * the dark one is there twice because a media query cannot be folded into a
2237
+ * selector list. It is the same repetition the theme roots at the top of this
2238
+ * file make, for the same reason.
2063
2239
  *
2064
2240
  * Every hue in the house themes is solved against its own background rather
2065
2241
  * than flipped: the light set runs dark and saturated on near-white, the dark
@@ -2071,7 +2247,8 @@
2071
2247
  * ------------------------------------------------------------------------- */
2072
2248
 
2073
2249
  .plass-code {
2074
- /* The light set, which is also what `auto` and `light` start from. */
2250
+ /* The light set, which is what `light` is and what a theme that fills only
2251
+ some of the slots starts from. */
2075
2252
  --p-code-bg: oklch(98.4% 0.003 262);
2076
2253
  --p-code-fg: oklch(30% 0.016 262);
2077
2254
 
@@ -2102,8 +2279,7 @@
2102
2279
  --p-code-mark-edge: color-mix(in oklab, var(--p-code-fg) 45%, transparent);
2103
2280
  }
2104
2281
 
2105
- .plass-code[data-code-theme='dark'],
2106
- :where(.dark, [data-theme='dark']) .plass-code[data-code-theme='auto'] {
2282
+ .plass-code[data-code-theme='dark'] {
2107
2283
  --p-code-bg: oklch(19.5% 0.014 262);
2108
2284
  --p-code-fg: oklch(90% 0.012 262);
2109
2285
 
@@ -2121,31 +2297,92 @@
2121
2297
  --p-code-del: oklch(75% 0.15 25);
2122
2298
  }
2123
2299
 
2124
- /* Both ways of forcing light are named, the same pair the token block above
2125
- answers to. `auto` means "whatever the page is", so a page that has said it
2126
- is light with `.light` and one that has said it with `[data-theme='light']`
2127
- have to leave the block light alike — otherwise the page is white and the
2128
- code on it is the one dark rectangle nobody asked for. */
2300
+ /* `auto` is the palette of the nearest element that says which theme it is in,
2301
+ however deep the nesting goes: a light preview box inside a dark page, or a
2302
+ dark panel inside that. A selector can name a dark ancestor or a light one
2303
+ but not which of the two is nearer, so the palette `auto` reads is declared
2304
+ on every theme root instead, the way the tokens at the top of this file are,
2305
+ and inheritance is what picks the nearest. The system preference is the
2306
+ root's answer when the page has not given one, and both ways of forcing
2307
+ light are named, so a page that has said it is light leaves the block light
2308
+ either way. */
2309
+ :root,
2310
+ .light,
2311
+ [data-theme='light'] {
2312
+ --p-code-auto-bg: oklch(98.4% 0.003 262);
2313
+ --p-code-auto-fg: oklch(30% 0.016 262);
2314
+
2315
+ --p-code-auto-comment: oklch(58% 0.022 262);
2316
+ --p-code-auto-keyword: oklch(48% 0.19 322);
2317
+ --p-code-auto-string: oklch(46% 0.12 148);
2318
+ --p-code-auto-number: oklch(50% 0.14 48);
2319
+ --p-code-auto-function: oklch(48% 0.17 264);
2320
+ --p-code-auto-type: oklch(45% 0.1 214);
2321
+ --p-code-auto-variable: oklch(45% 0.12 28);
2322
+ --p-code-auto-tag: oklch(48% 0.18 22);
2323
+ --p-code-auto-attr: oklch(47% 0.13 82);
2324
+ --p-code-auto-meta: oklch(52% 0.05 262);
2325
+ --p-code-auto-add: oklch(45% 0.14 152);
2326
+ --p-code-auto-del: oklch(48% 0.18 25);
2327
+ }
2328
+
2129
2329
  @media (prefers-color-scheme: dark) {
2130
- :root:not(.light):not([data-theme='light']) .plass-code[data-code-theme='auto'] {
2131
- --p-code-bg: oklch(19.5% 0.014 262);
2132
- --p-code-fg: oklch(90% 0.012 262);
2330
+ :root:not(.light):not([data-theme='light']) {
2331
+ --p-code-auto-bg: oklch(19.5% 0.014 262);
2332
+ --p-code-auto-fg: oklch(90% 0.012 262);
2133
2333
 
2134
- --p-code-comment: oklch(60% 0.024 262);
2135
- --p-code-keyword: oklch(76% 0.13 324);
2136
- --p-code-string: oklch(80% 0.13 148);
2137
- --p-code-number: oklch(81% 0.12 62);
2138
- --p-code-function: oklch(78% 0.12 258);
2139
- --p-code-type: oklch(82% 0.1 210);
2140
- --p-code-variable: oklch(82% 0.09 32);
2141
- --p-code-tag: oklch(77% 0.13 22);
2142
- --p-code-attr: oklch(85% 0.11 92);
2143
- --p-code-meta: oklch(70% 0.05 262);
2144
- --p-code-add: oklch(80% 0.14 152);
2145
- --p-code-del: oklch(75% 0.15 25);
2334
+ --p-code-auto-comment: oklch(60% 0.024 262);
2335
+ --p-code-auto-keyword: oklch(76% 0.13 324);
2336
+ --p-code-auto-string: oklch(80% 0.13 148);
2337
+ --p-code-auto-number: oklch(81% 0.12 62);
2338
+ --p-code-auto-function: oklch(78% 0.12 258);
2339
+ --p-code-auto-type: oklch(82% 0.1 210);
2340
+ --p-code-auto-variable: oklch(82% 0.09 32);
2341
+ --p-code-auto-tag: oklch(77% 0.13 22);
2342
+ --p-code-auto-attr: oklch(85% 0.11 92);
2343
+ --p-code-auto-meta: oklch(70% 0.05 262);
2344
+ --p-code-auto-add: oklch(80% 0.14 152);
2345
+ --p-code-auto-del: oklch(75% 0.15 25);
2146
2346
  }
2147
2347
  }
2148
2348
 
2349
+ .dark,
2350
+ [data-theme='dark'] {
2351
+ --p-code-auto-bg: oklch(19.5% 0.014 262);
2352
+ --p-code-auto-fg: oklch(90% 0.012 262);
2353
+
2354
+ --p-code-auto-comment: oklch(60% 0.024 262);
2355
+ --p-code-auto-keyword: oklch(76% 0.13 324);
2356
+ --p-code-auto-string: oklch(80% 0.13 148);
2357
+ --p-code-auto-number: oklch(81% 0.12 62);
2358
+ --p-code-auto-function: oklch(78% 0.12 258);
2359
+ --p-code-auto-type: oklch(82% 0.1 210);
2360
+ --p-code-auto-variable: oklch(82% 0.09 32);
2361
+ --p-code-auto-tag: oklch(77% 0.13 22);
2362
+ --p-code-auto-attr: oklch(85% 0.11 92);
2363
+ --p-code-auto-meta: oklch(70% 0.05 262);
2364
+ --p-code-auto-add: oklch(80% 0.14 152);
2365
+ --p-code-auto-del: oklch(75% 0.15 25);
2366
+ }
2367
+
2368
+ .plass-code[data-code-theme='auto'] {
2369
+ --p-code-bg: var(--p-code-auto-bg);
2370
+ --p-code-fg: var(--p-code-auto-fg);
2371
+
2372
+ --p-code-comment: var(--p-code-auto-comment);
2373
+ --p-code-keyword: var(--p-code-auto-keyword);
2374
+ --p-code-string: var(--p-code-auto-string);
2375
+ --p-code-number: var(--p-code-auto-number);
2376
+ --p-code-function: var(--p-code-auto-function);
2377
+ --p-code-type: var(--p-code-auto-type);
2378
+ --p-code-variable: var(--p-code-auto-variable);
2379
+ --p-code-tag: var(--p-code-auto-tag);
2380
+ --p-code-attr: var(--p-code-auto-attr);
2381
+ --p-code-meta: var(--p-code-auto-meta);
2382
+ --p-code-add: var(--p-code-auto-add);
2383
+ --p-code-del: var(--p-code-auto-del);
2384
+ }
2385
+
2149
2386
  /*
2150
2387
  * `mono` is the one theme that follows the page, and the only one with no hue
2151
2388
  * in it at all: the structure is carried by weight and by how far a run is
@@ -2557,6 +2794,138 @@
2557
2794
  flex-basis: 0;
2558
2795
  }
2559
2796
 
2797
+ /* ---------------------------------------------------------------------------
2798
+ * The float label
2799
+ *
2800
+ * `labelPlacement="float"`: a notched field whose label rests inside the
2801
+ * control, where the value would be written, while the control is empty,
2802
+ * nothing in the field has the focus and no popup of its is open. At rest it is
2803
+ * set the way that placeholder is — the value's size, the regular weight and
2804
+ * the muted ink — and the moment any of the three stops being true, the label
2805
+ * rises into the notch at the notch's own weight and the placeholder comes
2806
+ * back.
2807
+ *
2808
+ * `internal/notch.tsx` draws the same fieldset and legend a `notch` does and
2809
+ * hands this block its numbers as custom properties on the frame. It is CSS
2810
+ * rather than Tailwind because the condition is a relation between three
2811
+ * elements — the frame's focus, the control's emptiness and a trigger's popup
2812
+ * — and because an input can say it is empty through `:placeholder-shown`
2813
+ * before any script has run, so a server-rendered field is right on its first
2814
+ * frame.
2815
+ *
2816
+ * **The label moves on `top`, `inset-inline-start`, `font-size` and
2817
+ * `font-weight`,** never on a transform: it is set again at every size on the
2818
+ * way rather than scaled, so no frame of it is a resampled picture of the word.
2819
+ * How smoothly the weight goes is the font's to say: a variable font passes
2820
+ * through every weight between, and a font with a few fixed faces steps
2821
+ * between them. Its line box is held at the notch's height throughout, which
2822
+ * is what keeps the legend, and the edge drawn across its middle, where they
2823
+ * are while the word grows and shrinks.
2824
+ *
2825
+ * **The gap folds rather than fades.** A legend cannot fill its own gap, so the
2826
+ * legend is folded to nothing under a resting label and the fieldset draws its
2827
+ * whole top edge again. It opens at once as the label starts to rise, so the
2828
+ * word never crosses a line on its way up, and closes only once the label has
2829
+ * come all the way down, so the line never runs through it on the way down.
2830
+ * ------------------------------------------------------------------------- */
2831
+
2832
+ .plass-notch-float > fieldset > legend {
2833
+ max-width: 100%;
2834
+ white-space: nowrap;
2835
+ transition: max-width 0s;
2836
+ }
2837
+
2838
+ .plass-notch-float > fieldset > legend > * {
2839
+ display: block;
2840
+ position: relative;
2841
+ top: 0;
2842
+ inset-inline-start: 0;
2843
+ margin-inline: var(--p-notch-pad);
2844
+ line-height: var(--p-notch-leading);
2845
+ transition-property: top, inset-inline-start, font-size, font-weight, color;
2846
+ transition-duration: var(--plass-duration);
2847
+ transition-timing-function: var(--plass-ease);
2848
+ }
2849
+
2850
+ /*
2851
+ * At rest. Three ways to know the control is empty, one for each kind of
2852
+ * control: an input's `:placeholder-shown`, a select trigger's
2853
+ * `data-placeholder`, and `data-empty` on the frame from a component that knows
2854
+ * and has no control that can say so, a picker's trigger. Autofill counts as a
2855
+ * value before the reader has accepted it, since its text is already drawn where
2856
+ * the label would rest.
2857
+ *
2858
+ * The same condition is written out in front of each of the three rules below,
2859
+ * since the stylesheet ships to projects whose own build may not lower nesting.
2860
+ */
2861
+ .plass-notch-float:not(:focus-within):not(:has([data-popup-open])):is(
2862
+ [data-empty],
2863
+ :has(.plass-float-control:placeholder-shown:not(:autofill)),
2864
+ :has(.plass-float-control[data-placeholder])
2865
+ )
2866
+ > fieldset
2867
+ > legend {
2868
+ max-width: 0.01px;
2869
+ transition-delay: var(--p-float-fold, var(--plass-duration));
2870
+ /* A press on the words lands on the control under them, as it would on the
2871
+ placeholder they stand in for. A select opens on the pointer going down on
2872
+ its trigger, which a label in front of it would swallow. */
2873
+ pointer-events: none;
2874
+ }
2875
+
2876
+ .plass-notch-float:not(:focus-within):not(:has([data-popup-open])):is(
2877
+ [data-empty],
2878
+ :has(.plass-float-control:placeholder-shown:not(:autofill)),
2879
+ :has(.plass-float-control[data-placeholder])
2880
+ )
2881
+ > fieldset
2882
+ > legend
2883
+ > * {
2884
+ top: var(--p-float-y);
2885
+ inset-inline-start: var(--p-float-x, 0px);
2886
+ font-size: var(--p-float-text);
2887
+ font-weight: 400;
2888
+ color: var(--plass-muted-fg);
2889
+ }
2890
+
2891
+ .plass-notch-float:not(:focus-within):not(:has([data-popup-open])):is(
2892
+ [data-empty],
2893
+ :has(.plass-float-control:placeholder-shown:not(:autofill)),
2894
+ :has(.plass-float-control[data-placeholder])
2895
+ )
2896
+ .plass-float-control::placeholder,
2897
+ .plass-notch-float:not(:focus-within):not(:has([data-popup-open])):is(
2898
+ [data-empty],
2899
+ :has(.plass-float-control:placeholder-shown:not(:autofill)),
2900
+ :has(.plass-float-control[data-placeholder])
2901
+ )
2902
+ .plass-float-placeholder {
2903
+ color: transparent;
2904
+ }
2905
+
2906
+ /* The placeholder comes back as the label leaves, over the same time. */
2907
+ .plass-notch-float .plass-float-control::placeholder,
2908
+ .plass-notch-float .plass-float-placeholder {
2909
+ transition: color var(--plass-duration) var(--plass-ease);
2910
+ }
2911
+
2912
+ /*
2913
+ * A press that went down on the control comes up on it. The focus the press
2914
+ * hands the control ends the rest before the button is let go, so the label is
2915
+ * back in reach of the pointer while it is still lying over the words, and a
2916
+ * quick click on them came up on the label. The browser then sent the click to
2917
+ * the frame round both, and a picker that opens on a click, such as
2918
+ * `PlTreeSelect`, stayed shut. The label lets the pointer through until that
2919
+ * press is over.
2920
+ *
2921
+ * Only a press on the control: one on the label itself leaves the control out
2922
+ * of the chain `:active` marks, so clicking a risen label still focuses the
2923
+ * field.
2924
+ */
2925
+ .plass-notch-float:has(> :not(fieldset):active) > fieldset > legend {
2926
+ pointer-events: none;
2927
+ }
2928
+
2560
2929
  /* ---------------------------------------------------------------------------
2561
2930
  * Reduced motion
2562
2931
  *
@@ -2564,7 +2933,9 @@
2564
2933
  * transitions: someone who has asked for less movement is asking for state
2565
2934
  * changes to *arrive*, not to be taken away. The colours still change; they
2566
2935
  * simply stop easing, and the light stops trailing the pointer across the
2567
- * surface — it is either on the control or it is not.
2936
+ * surface — it is either on the control or it is not. The colours are the house
2937
+ * transition's, which is a set of utilities rather than a class, so it says so
2938
+ * beside itself in `internal/styles.ts`.
2568
2939
  * ------------------------------------------------------------------------- */
2569
2940
 
2570
2941
  @media (prefers-reduced-motion: reduce) {
@@ -2573,6 +2944,25 @@
2573
2944
  transition-duration: 1ms;
2574
2945
  }
2575
2946
 
2947
+ /* A fill that comes with a state arrives with it, as the colours round it
2948
+ do. */
2949
+ .plass-fill::before,
2950
+ .plass-fill-layer {
2951
+ transition-duration: 0ms;
2952
+ }
2953
+
2954
+ /* A float label arrives where it is going, and the gap under it opens and
2955
+ closes with it rather than a moment after. */
2956
+ .plass-notch-float {
2957
+ --p-float-fold: 0ms;
2958
+ }
2959
+
2960
+ .plass-notch-float > fieldset > legend > *,
2961
+ .plass-notch-float .plass-float-control::placeholder,
2962
+ .plass-notch-float .plass-float-placeholder {
2963
+ transition-duration: 0ms;
2964
+ }
2965
+
2576
2966
  /* The highlight stops travelling and the placeholder pulses in place. It is
2577
2967
  kept running rather than stopped, because a skeleton that holds still is
2578
2968
  indistinguishable from an empty box that finished loading with nothing in
@@ -2612,16 +3002,41 @@
2612
3002
  /*
2613
3003
  * The `PlAnimate*` components go the other way from the indicators above, and
2614
3004
  * the difference is what each of them is saying. A spinner that stops is
2615
- * lying about whether anything is happening; an entrance that does not play
2616
- * has still delivered its content, which is the only thing it was ever
2617
- * carrying.
3005
+ * lying about whether anything is happening; an effect that does not move has
3006
+ * still delivered what it was carrying — which for an entrance is the content
3007
+ * and for an exit is its absence.
3008
+ *
3009
+ * So an effect is not switched off but run in no time at all. Nothing changes
3010
+ * until the moment it would have started, its `delay` included, and then it
3011
+ * is on its last frame: an exit has gone, a turn has turned, and
3012
+ * `animationend` arrives for a caller waiting on it. Until that moment the
3013
+ * element is as it is, because the fill runs forwards only — an effect
3014
+ * waiting for its trigger is held at the start of its delay, which with no
3015
+ * backwards fill draws nothing, rather than on a first frame at `opacity: 0`
3016
+ * that would blank the page. The delay is a millisecond at the least, which
3017
+ * is what keeps a waiting effect there: with no delay and no duration, a run
3018
+ * held at its very start has already finished.
2618
3019
  *
2619
- * `animation: none` rather than a zero duration, because the shorthand also
2620
- * clears `fill-mode` — and the fill is what was holding an untriggered
2621
- * element on its own first frame at `opacity: 0`. Left in place, respecting a
2622
- * reader's preference would blank the page.
3020
+ * A count of `infinite` is one pass here, which `--p-anim-still-repeat`
3021
+ * says: an endless run has no last frame of its own. And a scroll-linked
3022
+ * effect goes back to the clock, because a zero duration spread over a scroll
3023
+ * range is an effect that snaps at one point of the scroll.
2623
3024
  */
2624
- .plass-anim,
3025
+ .plass-anim {
3026
+ animation-duration: 0s;
3027
+ animation-delay: max(1ms, var(--p-anim-delay, 0ms));
3028
+ animation-iteration-count: var(--p-anim-still-repeat, var(--p-anim-repeat, 1));
3029
+ animation-fill-mode: forwards;
3030
+ animation-timeline: auto;
3031
+ }
3032
+
3033
+ /* The one effect whose last frame is not the element's own: before it has
3034
+ run, a turn stands at the angle it turns to, which is what it is waiting to
3035
+ deliver. The keyframe outranks this once the run has begun. */
3036
+ .plass-anim-rotate {
3037
+ rotate: var(--p-anim-angle-to, 0deg);
3038
+ }
3039
+
2625
3040
  .plass-marquee-track,
2626
3041
  .plass-headline-item,
2627
3042
  .plass-caret {