@greese/ui 0.0.0-stage → 1.0.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 (274) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +55 -2
  3. package/bin/greese-utilities.mjs +56 -0
  4. package/dist/_layers.scss +2 -0
  5. package/dist/_maps.scss +1884 -0
  6. package/dist/_tokens-base.scss +174 -0
  7. package/dist/_tokens.scss +2140 -0
  8. package/dist/base.css +67 -0
  9. package/dist/colorScheme.d.ts +16 -0
  10. package/dist/colorScheme.js +13 -0
  11. package/dist/contrast.d.ts +15 -0
  12. package/dist/contrast.js +43 -0
  13. package/dist/css/Accordion.css +9 -0
  14. package/dist/css/ActionSelect.css +6 -0
  15. package/dist/css/Alert.css +84 -0
  16. package/dist/css/AppBar.css +35 -0
  17. package/dist/css/AspectRatio.css +40 -0
  18. package/dist/css/AutoLayout.css +508 -0
  19. package/dist/css/Avatar.css +88 -0
  20. package/dist/css/Badge.css +66 -0
  21. package/dist/css/Box.css +640 -0
  22. package/dist/css/Breadcrumbs.css +57 -0
  23. package/dist/css/Button.css +275 -0
  24. package/dist/css/ButtonGroup.css +40 -0
  25. package/dist/css/Card.css +101 -0
  26. package/dist/css/Carousel.css +170 -0
  27. package/dist/css/Center.css +11 -0
  28. package/dist/css/ChatBubble.css +128 -0
  29. package/dist/css/Checkbox.css +65 -0
  30. package/dist/css/Chip.css +124 -0
  31. package/dist/css/ChoiceGroup.css +57 -0
  32. package/dist/css/Code.css +29 -0
  33. package/dist/css/Collapsible.css +126 -0
  34. package/dist/css/ColorPicker.css +191 -0
  35. package/dist/css/Combobox.css +90 -0
  36. package/dist/css/Container.css +30 -0
  37. package/dist/css/Countdown.css +25 -0
  38. package/dist/css/DateField.css +15 -0
  39. package/dist/css/Dialog.css +640 -0
  40. package/dist/css/Diff.css +103 -0
  41. package/dist/css/Divider.css +19 -0
  42. package/dist/css/Dock.css +91 -0
  43. package/dist/css/Field.css +33 -0
  44. package/dist/css/Fieldset.css +54 -0
  45. package/dist/css/FileInput.css +65 -0
  46. package/dist/css/Footer.css +72 -0
  47. package/dist/css/Grid.css +998 -0
  48. package/dist/css/Header.css +89 -0
  49. package/dist/css/Heading.css +6 -0
  50. package/dist/css/Icon.css +12 -0
  51. package/dist/css/IconButton.css +7 -0
  52. package/dist/css/Image.css +69 -0
  53. package/dist/css/Indicator.css +41 -0
  54. package/dist/css/Input.css +115 -0
  55. package/dist/css/Key.css +42 -0
  56. package/dist/css/Link.css +72 -0
  57. package/dist/css/List.css +52 -0
  58. package/dist/css/Media.css +8 -0
  59. package/dist/css/Menu.css +161 -0
  60. package/dist/css/Motion.css +1356 -0
  61. package/dist/css/NumberField.css +49 -0
  62. package/dist/css/OtpField.css +90 -0
  63. package/dist/css/PageTransition.css +979 -0
  64. package/dist/css/Pagination.css +43 -0
  65. package/dist/css/Popover.css +93 -0
  66. package/dist/css/Progress.css +101 -0
  67. package/dist/css/RadialProgress.css +44 -0
  68. package/dist/css/Radio.css +41 -0
  69. package/dist/css/Rating.css +100 -0
  70. package/dist/css/ScrollSpy.css +6 -0
  71. package/dist/css/SegmentedControl.css +101 -0
  72. package/dist/css/Select.css +115 -0
  73. package/dist/css/Sidebar.css +207 -0
  74. package/dist/css/Skeleton.css +48 -0
  75. package/dist/css/Slider.css +75 -0
  76. package/dist/css/Spacer.css +34 -0
  77. package/dist/css/SpeedDial.css +165 -0
  78. package/dist/css/Spinner.css +44 -0
  79. package/dist/css/Stack.css +528 -0
  80. package/dist/css/Stat.css +107 -0
  81. package/dist/css/Status.css +86 -0
  82. package/dist/css/Steps.css +201 -0
  83. package/dist/css/Swap.css +49 -0
  84. package/dist/css/Switch.css +66 -0
  85. package/dist/css/Table.css +269 -0
  86. package/dist/css/Tabs.css +119 -0
  87. package/dist/css/Text.css +60 -0
  88. package/dist/css/TextRotate.css +147 -0
  89. package/dist/css/Textarea.css +107 -0
  90. package/dist/css/ThemeProvider.css +6 -0
  91. package/dist/css/Tile.css +18 -0
  92. package/dist/css/Toast.css +110 -0
  93. package/dist/css/ToggleButton.css +20 -0
  94. package/dist/css/Tooltip.css +94 -0
  95. package/dist/css/Type.css +211 -0
  96. package/dist/css/Visibility.css +78 -0
  97. package/dist/css/VisuallyHidden.css +15 -0
  98. package/dist/css/core.css +251 -0
  99. package/dist/css.d.ts +29 -0
  100. package/dist/css.js +28 -0
  101. package/dist/effect-classes.d.ts +8 -0
  102. package/dist/effect-classes.js +5 -0
  103. package/dist/effects/css.d.ts +71 -0
  104. package/dist/effects/css.js +184 -0
  105. package/dist/effects/definitions.d.ts +555 -0
  106. package/dist/effects/definitions.js +379 -0
  107. package/dist/effects/frames.d.ts +18 -0
  108. package/dist/effects/frames.js +68 -0
  109. package/dist/effects/sequence.d.ts +33 -0
  110. package/dist/effects/sequence.js +106 -0
  111. package/dist/effects.d.ts +30 -0
  112. package/dist/effects.js +30 -0
  113. package/dist/greese.css +11907 -0
  114. package/dist/index.d.ts +10 -0
  115. package/dist/index.js +10 -0
  116. package/dist/kebab.d.ts +2 -0
  117. package/dist/kebab.js +2 -0
  118. package/dist/palette.css +249 -0
  119. package/dist/palette.d.ts +599 -0
  120. package/dist/palette.js +334 -0
  121. package/dist/shapes.d.ts +193 -0
  122. package/dist/shapes.js +852 -0
  123. package/dist/storage.d.ts +47 -0
  124. package/dist/storage.js +118 -0
  125. package/dist/style-manifest.json +3010 -0
  126. package/dist/theme.d.ts +71 -0
  127. package/dist/theme.js +180 -0
  128. package/dist/tokens.css +253 -0
  129. package/dist/tokens.d.ts +343 -0
  130. package/dist/tokens.js +179 -0
  131. package/dist/typography.d.ts +55 -0
  132. package/dist/typography.js +144 -0
  133. package/dist/utilities.css +6975 -0
  134. package/dist/utilities.d.ts +82 -0
  135. package/dist/utilities.js +636 -0
  136. package/dist/utilitiesFiles.d.ts +18 -0
  137. package/dist/utilitiesFiles.js +26 -0
  138. package/dist/vite.d.ts +40 -0
  139. package/dist/vite.js +43 -0
  140. package/package.json +131 -4
  141. package/src/components/Accordion/Accordion.scss +9 -0
  142. package/src/components/ActionSelect/ActionSelect.scss +6 -0
  143. package/src/components/Alert/Alert.scss +71 -0
  144. package/src/components/AppBar/AppBar.scss +43 -0
  145. package/src/components/AspectRatio/AspectRatio.scss +22 -0
  146. package/src/components/AutoLayout/AutoLayout.scss +90 -0
  147. package/src/components/Avatar/Avatar.scss +91 -0
  148. package/src/components/Badge/Badge.scss +52 -0
  149. package/src/components/Box/Box.scss +63 -0
  150. package/src/components/Breadcrumbs/Breadcrumbs.scss +59 -0
  151. package/src/components/Button/Button.scss +231 -0
  152. package/src/components/ButtonGroup/ButtonGroup.scss +56 -0
  153. package/src/components/Card/Card.scss +99 -0
  154. package/src/components/Carousel/Carousel.scss +161 -0
  155. package/src/components/Center/Center.scss +11 -0
  156. package/src/components/ChatBubble/ChatBubble.scss +119 -0
  157. package/src/components/Checkbox/Checkbox.scss +66 -0
  158. package/src/components/Chip/Chip.scss +119 -0
  159. package/src/components/ChoiceGroup/ChoiceGroup.scss +69 -0
  160. package/src/components/Code/Code.scss +28 -0
  161. package/src/components/Collapsible/Collapsible.scss +136 -0
  162. package/src/components/ColorPicker/ColorPicker.scss +235 -0
  163. package/src/components/Combobox/Combobox.scss +83 -0
  164. package/src/components/Container/Container.scss +28 -0
  165. package/src/components/Countdown/Countdown.scss +28 -0
  166. package/src/components/DateField/DateField.scss +19 -0
  167. package/src/components/Dialog/Dialog.scss +268 -0
  168. package/src/components/Diff/Diff.scss +125 -0
  169. package/src/components/Divider/Divider.scss +22 -0
  170. package/src/components/Dock/Dock.scss +86 -0
  171. package/src/components/Field/Field.scss +38 -0
  172. package/src/components/Fieldset/Fieldset.scss +63 -0
  173. package/src/components/FileInput/FileInput.scss +66 -0
  174. package/src/components/Footer/Footer.scss +79 -0
  175. package/src/components/Grid/Grid.scss +86 -0
  176. package/src/components/Header/Header.scss +111 -0
  177. package/src/components/Heading/Heading.scss +7 -0
  178. package/src/components/Icon/Icon.scss +12 -0
  179. package/src/components/IconButton/IconButton.scss +6 -0
  180. package/src/components/Image/Image.scss +46 -0
  181. package/src/components/Indicator/Indicator.scss +48 -0
  182. package/src/components/Input/Input.scss +58 -0
  183. package/src/components/Key/Key.scss +48 -0
  184. package/src/components/Link/Link.scss +50 -0
  185. package/src/components/List/List.scss +62 -0
  186. package/src/components/Media/Media.scss +7 -0
  187. package/src/components/Menu/Menu.scss +91 -0
  188. package/src/components/Motion/Motion.scss +106 -0
  189. package/src/components/NumberField/NumberField.scss +44 -0
  190. package/src/components/OtpField/OtpField.scss +91 -0
  191. package/src/components/PageTransition/PageTransition.scss +168 -0
  192. package/src/components/Pagination/Pagination.scss +55 -0
  193. package/src/components/Popover/Popover.scss +13 -0
  194. package/src/components/Progress/Progress.scss +126 -0
  195. package/src/components/RadialProgress/RadialProgress.scss +65 -0
  196. package/src/components/Radio/Radio.scss +36 -0
  197. package/src/components/Rating/Rating.scss +121 -0
  198. package/src/components/ScrollSpy/ScrollSpy.scss +6 -0
  199. package/src/components/SegmentedControl/SegmentedControl.scss +107 -0
  200. package/src/components/Select/Select.scss +59 -0
  201. package/src/components/Sidebar/Sidebar.scss +88 -0
  202. package/src/components/Skeleton/Skeleton.scss +60 -0
  203. package/src/components/Slider/Slider.scss +96 -0
  204. package/src/components/Spacer/Spacer.scss +15 -0
  205. package/src/components/SpeedDial/SpeedDial.scss +109 -0
  206. package/src/components/Spinner/Spinner.scss +43 -0
  207. package/src/components/Stack/Stack.scss +57 -0
  208. package/src/components/Stat/Stat.scss +96 -0
  209. package/src/components/Status/Status.scss +60 -0
  210. package/src/components/Steps/Steps.scss +216 -0
  211. package/src/components/Swap/Swap.scss +62 -0
  212. package/src/components/Switch/Switch.scss +63 -0
  213. package/src/components/Table/Table.scss +183 -0
  214. package/src/components/Tabs/Tabs.scss +108 -0
  215. package/src/components/Text/Text.scss +34 -0
  216. package/src/components/TextRotate/TextRotate.scss +51 -0
  217. package/src/components/Textarea/Textarea.scss +56 -0
  218. package/src/components/ThemeProvider/ThemeProvider.scss +7 -0
  219. package/src/components/Tile/Tile.scss +23 -0
  220. package/src/components/Toast/Toast.scss +93 -0
  221. package/src/components/ToggleButton/ToggleButton.scss +23 -0
  222. package/src/components/Tooltip/Tooltip.scss +21 -0
  223. package/src/components/Type/Type.scss +35 -0
  224. package/src/components/Visibility/Visibility.scss +39 -0
  225. package/src/components/VisuallyHidden/VisuallyHidden.scss +17 -0
  226. package/src/styles/_breakpoint-vars.scss +16 -0
  227. package/src/styles/_config.scss +48 -0
  228. package/src/styles/_controls.scss +88 -0
  229. package/src/styles/_core.scss +4 -0
  230. package/src/styles/_effect.scss +51 -0
  231. package/src/styles/_floating.scss +141 -0
  232. package/src/styles/_kit.scss +1548 -0
  233. package/src/styles/_manifest.scss +670 -0
  234. package/src/styles/_mixins.scss +261 -0
  235. package/src/styles/_tones.scss +63 -0
  236. package/src/styles/_type-emit.scss +76 -0
  237. package/src/styles/_typography-vars.scss +8 -0
  238. package/src/styles/base.scss +80 -0
  239. package/src/styles/breakpoints.test.ts +73 -0
  240. package/src/styles/entryPoints.test.ts +68 -0
  241. package/src/styles/index.scss +93 -0
  242. package/src/styles/kit.test.ts +754 -0
  243. package/src/styles/public/_controls.scss +3 -0
  244. package/src/styles/public/_floating.scss +3 -0
  245. package/src/styles/public/_mixins.scss +5 -0
  246. package/src/styles/public/_tones.scss +4 -0
  247. package/themes/bevel.css +1077 -0
  248. package/themes/bevel.scss +1166 -0
  249. package/themes/depth.css +123 -0
  250. package/themes/depth.scss +46 -0
  251. package/themes/extrude.css +219 -0
  252. package/themes/extrude.scss +255 -0
  253. package/themes/folio.css +986 -0
  254. package/themes/folio.scss +1047 -0
  255. package/themes/gentle.css +66 -0
  256. package/themes/gentle.scss +39 -0
  257. package/themes/outline.css +127 -0
  258. package/themes/outline.scss +126 -0
  259. package/themes/pebble.css +65 -0
  260. package/themes/pebble.scss +69 -0
  261. package/themes/pocket.css +137 -0
  262. package/themes/pocket.scss +45 -0
  263. package/themes/ridge.css +927 -0
  264. package/themes/ridge.scss +998 -0
  265. package/themes/snappy.css +82 -0
  266. package/themes/snappy.scss +40 -0
  267. package/themes/soft.css +79 -0
  268. package/themes/soft.scss +84 -0
  269. package/themes/ticket.css +960 -0
  270. package/themes/ticket.scss +1031 -0
  271. package/themes/tilt.css +148 -0
  272. package/themes/tilt.scss +169 -0
  273. package/themes/wipe.css +186 -0
  274. package/themes/wipe.scss +46 -0
@@ -0,0 +1,71 @@
1
+ import { type BuiltInEffect, type EffectFrames } from './definitions.ts';
2
+ /** One step of CSS keyframes: `from`, `to` or a percentage, and its declarations. */
3
+ export type CssStep = [at: string, declarations: Record<string, string>];
4
+ export declare const length: (v: unknown) => string;
5
+ /**
6
+ * greese.js keyframes as CSS keyframes. `x` and `y` become `translate`,
7
+ * `scaleX`/`scaleY` `scale`, `rotate` degrees; other properties pass through,
8
+ * kebab-cased. An object is property → values spread evenly from 0 to 100% (a
9
+ * lone value is the end); a list is one object per frame, at its `offset` or
10
+ * evenly spaced.
11
+ */
12
+ export declare const toCssSteps: (frames: EffectFrames | Record<string, unknown> | ReadonlyArray<Record<string, unknown>>) => CssStep[];
13
+ /** Whether a CSS value is the property's resting state, where an element is when nothing animates it. */
14
+ /**
15
+ * A mask only exists while an entrance plays: the element has none of its
16
+ * own, so wherever the mask ends, the element is at rest once it's gone.
17
+ * Masks are how an entrance wipes or rises out of a pocket without touching
18
+ * `clip-path`, which belongs to the element's shape (ADR 0024).
19
+ */
20
+ export declare const transient: (property: string) => boolean;
21
+ /** A mask's size that shows the whole element: every percentage 100 or more, every length 0. */
22
+ export declare const whole: (size: string) => boolean;
23
+ export declare const atRest: (property: string, value: string) => boolean;
24
+ /** How an effect becomes CSS. */
25
+ export interface CssPlan {
26
+ /**
27
+ * Its `@keyframes`, when it ends at the element's resting state, so the
28
+ * animation can finish at the element's own styles: every step but the last
29
+ * (the 100% one, which is the resting state).
30
+ */
31
+ enter?: CssStep[];
32
+ /**
33
+ * When it ends away from rest, everything an overlay's exit sets: the
34
+ * `translate`, `scale` and `opacity` it ends at, and its mask (`mask-image`
35
+ * is `none` without one). A wipe, iris or pocket adds the mask it ends at
36
+ * (`mask-size`, `mask-position`, `mask-repeat`) and the size it starts at
37
+ * (`mask-from`): an overlay holds that mask while it closes
38
+ * (styles/_floating.scss). Complete, so nothing falls through to an exit a
39
+ * theme assigns around it.
40
+ */
41
+ exit: Record<string, string>;
42
+ /**
43
+ * When it ends away from rest, its whole `@keyframes`, from the element's
44
+ * resting state to where it ends: what a page transition's old snapshot
45
+ * plays to leave (ADR 0029). An overlay's exit doesn't use them; it
46
+ * transitions to `exit`'s values.
47
+ */
48
+ leave?: CssStep[];
49
+ }
50
+ /**
51
+ * Decides, from the keyframes alone, how CSS plays an effect. It is the only
52
+ * place that does, for the built-in effects at build time and for the ones
53
+ * Theme Studio defines.
54
+ */
55
+ export declare const cssPlan: (frames: Parameters<typeof toCssSteps>[0]) => CssPlan;
56
+ /**
57
+ * A plan as CSS text, for a page that can't load a compiled theme: the same
58
+ * rules the kit's `effect()` writes (styles/_effect.scss), a test keeps them equal.
59
+ */
60
+ export declare const effectCss: (name: string, plan: CssPlan) => string;
61
+ export declare const plans: Map<string, CssPlan>;
62
+ /** A built-in effect's CSS plan, in the build's reach (`--greese-motion-distance`). */
63
+ export declare const effectPlan: (name: BuiltInEffect) => CssPlan;
64
+ /** Whether a built-in effect ends invisible: it leaves, where others arrive or add emphasis. */
65
+ export declare const endsHidden: (name: BuiltInEffect) => boolean;
66
+ /** The built-in effects CSS plays alone: those that end at rest. */
67
+ export declare const entranceNames: () => BuiltInEffect[];
68
+ /** The built-in effects an overlay can leave with: those that end away from rest. */
69
+ export declare const exitNames: () => BuiltInEffect[];
70
+ /** The class-name form of an effect name: `fadeUp` → `fade-up`. */
71
+ export declare const effectClass: (name: string) => string;
@@ -0,0 +1,184 @@
1
+ /** Effects as CSS: keyframe steps, the plan of what's animated, and the rules the build writes. */
2
+ import { kebab } from "../kebab.js";
3
+ import { cssReach, effectNames, ZERO, } from "./definitions.js";
4
+ import { effectFrames, round } from "./frames.js";
5
+ export const length = (v) => typeof v === 'number' ? `${v}px` : String(v);
6
+ /**
7
+ * greese.js keyframes as CSS keyframes. `x` and `y` become `translate`,
8
+ * `scaleX`/`scaleY` `scale`, `rotate` degrees; other properties pass through,
9
+ * kebab-cased. An object is property → values spread evenly from 0 to 100% (a
10
+ * lone value is the end); a list is one object per frame, at its `offset` or
11
+ * evenly spaced.
12
+ */
13
+ export const toCssSteps = (frames) => {
14
+ const steps = new Map();
15
+ const at = (offset) => {
16
+ const key = round(offset);
17
+ if (!steps.has(key))
18
+ steps.set(key, {});
19
+ return steps.get(key);
20
+ };
21
+ if (Array.isArray(frames)) {
22
+ const list = frames;
23
+ list.forEach((frame, i) => {
24
+ const { offset, ...values } = frame;
25
+ delete values.ease;
26
+ Object.assign(at(typeof offset === 'number'
27
+ ? offset
28
+ : list.length > 1
29
+ ? i / (list.length - 1)
30
+ : 1), values);
31
+ });
32
+ }
33
+ else {
34
+ for (const [prop, value] of Object.entries(frames)) {
35
+ const list = Array.isArray(value) ? value : [value];
36
+ list.forEach((v, i) => {
37
+ at(list.length > 1 ? i / (list.length - 1) : 1)[prop] = v;
38
+ });
39
+ }
40
+ }
41
+ return [...steps]
42
+ .sort(([a], [b]) => a - b)
43
+ .map(([offset, values]) => {
44
+ const out = {};
45
+ const { x, y, scaleX, scaleY, rotate, ...rest } = values;
46
+ if (x !== undefined || y !== undefined)
47
+ out.translate = `${x === undefined ? ZERO : length(x)} ${y === undefined ? ZERO : length(y)}`;
48
+ if (scaleX !== undefined || scaleY !== undefined)
49
+ out.scale = `${scaleX ?? 1} ${scaleY ?? 1}`;
50
+ if (rotate !== undefined)
51
+ out.rotate = typeof rotate === 'number' ? `${rotate}deg` : String(rotate);
52
+ for (const [prop, v] of Object.entries(rest))
53
+ out[kebab(prop)] = String(v);
54
+ return [offset === 0 ? 'from' : offset === 1 ? 'to' : `${offset * 100}%`, out];
55
+ });
56
+ };
57
+ /** Whether a CSS value is the property's resting state, where an element is when nothing animates it. */
58
+ /**
59
+ * A mask only exists while an entrance plays: the element has none of its
60
+ * own, so wherever the mask ends, the element is at rest once it's gone.
61
+ * Masks are how an entrance wipes or rises out of a pocket without touching
62
+ * `clip-path`, which belongs to the element's shape (ADR 0024).
63
+ */
64
+ export const transient = (property) => property.includes('mask');
65
+ /** A mask's size that shows the whole element: every percentage 100 or more, every length 0. */
66
+ export const whole = (size) => (size.match(/-?[\d.]+[a-z%]*/gi) ?? []).every((n) => n.endsWith('%') ? parseFloat(n) >= 100 : parseFloat(n) === 0);
67
+ export const atRest = (property, value) => {
68
+ // A mask is at rest when it shows everything: an entrance that ends so
69
+ // arrives, one that ends with the element masked away leaves.
70
+ if (property === 'mask-size')
71
+ return whole(value);
72
+ if (transient(property))
73
+ return true;
74
+ // Where a turn pivots, and whether its back shows, move nothing by themselves.
75
+ if (property === 'transform-origin' || property === 'backface-visibility')
76
+ return true;
77
+ const numbers = (text) => text.match(/-?[\d.]+/g)?.map(Number) ?? [];
78
+ switch (property) {
79
+ case 'opacity':
80
+ return Number(value) === 1;
81
+ case 'translate':
82
+ case 'rotate':
83
+ return numbers(value).every((n) => n === 0);
84
+ // A turn or a depth with its perspective: at rest when it turns and moves
85
+ // nowhere (the perspective alone changes nothing).
86
+ case 'transform':
87
+ return (value.match(/(rotate|translate)\w*\([^)]*\)/g) ?? []).every((fn) => numbers(fn.replace(/^\w+\(/, '')).every((n) => n === 0));
88
+ case 'scale':
89
+ return numbers(value).every((n) => n === 1);
90
+ case 'filter':
91
+ return (value.match(/[\w-]+\([^)]*\)|none/g) ?? []).every((fn) => {
92
+ const one = /^(brightness|contrast|saturate|opacity)\(/.test(fn);
93
+ return fn === 'none' || numbers(fn).every((n) => n === (one ? 1 : 0));
94
+ });
95
+ default:
96
+ return false;
97
+ }
98
+ };
99
+ /**
100
+ * Decides, from the keyframes alone, how CSS plays an effect. It is the only
101
+ * place that does, for the built-in effects at build time and for the ones
102
+ * Theme Studio defines.
103
+ */
104
+ export const cssPlan = (frames) => {
105
+ const steps = toCssSteps(frames);
106
+ const end = {};
107
+ for (const [, declarations] of steps)
108
+ Object.assign(end, declarations);
109
+ if (Object.entries(end).every(([property, value]) => atRest(property, value))) {
110
+ // The last step is the element's own styles, so it goes, unless it holds
111
+ // a mask: that one has to finish the reveal before the mask disappears.
112
+ const [at, last] = steps.at(-1) ?? [];
113
+ return {
114
+ enter: at === 'to' && !Object.keys(last).some(transient) ? steps.slice(0, -1) : steps,
115
+ exit: {},
116
+ };
117
+ }
118
+ // Everything an overlay's exit sets, so nothing falls back to the theme's
119
+ // default exit (which moves toward the overlay's origin): what the effect
120
+ // doesn't move stays put. A masked exit hides with its mask, so it stays
121
+ // opaque; anything else fades, as overlays always have.
122
+ const masked = 'mask-size' in end;
123
+ const mask = Object.entries(end).filter(([property]) => property.startsWith('mask-'));
124
+ return {
125
+ exit: {
126
+ translate: end.translate ?? '0px 0px',
127
+ scale: end.scale ?? '1',
128
+ opacity: end.opacity ?? (masked ? '1' : '0'),
129
+ 'mask-image': 'none',
130
+ ...Object.fromEntries(mask),
131
+ // A three-dimensional exit turns or sinks (`transform`, with its own
132
+ // perspective), about its own pivot.
133
+ transform: end.transform ?? 'none',
134
+ ...(end['transform-origin'] && { 'transform-origin': end['transform-origin'] }),
135
+ // What size the mask starts at: the overlay holds it while open (an
136
+ // iris starts just past the far corner, at 142%).
137
+ ...(masked && { 'mask-from': steps[0]?.[1]['mask-size'] ?? '100% 100%' }),
138
+ },
139
+ leave: steps,
140
+ };
141
+ };
142
+ /**
143
+ * A plan as CSS text, for a page that can't load a compiled theme: the same
144
+ * rules the kit's `effect()` writes (styles/_effect.scss), a test keeps them equal.
145
+ */
146
+ export const effectCss = (name, plan) => {
147
+ const kebabName = kebab(name);
148
+ const block = (selector, lines) => `${selector} {\n${lines.map((line) => ` ${line}`).join('\n')}\n}`;
149
+ const rules = [];
150
+ if (plan.enter)
151
+ rules.push(block(`.greese-Motion--enter-${kebabName},\n.greese-Motion--view-${kebabName}`, [
152
+ `--greese-motion-name: greese-${kebabName};`,
153
+ ]));
154
+ const exit = Object.entries(plan.exit);
155
+ if (exit.length)
156
+ rules.push(block(`.greese-Motion--exit-${kebabName}`, exit.map(([property, value]) => `--greese-motion-exit-${property}: ${value};`)));
157
+ const keyframes = plan.enter ?? plan.leave;
158
+ if (keyframes)
159
+ rules.push(`@media (prefers-reduced-motion: no-preference) {\n${block(`@keyframes greese-${kebabName}`, keyframes.map(([at, declarations]) => block(at, Object.entries(declarations).map(([property, value]) => `${property}: ${value};`))))
160
+ .split('\n')
161
+ .map((line) => ` ${line}`)
162
+ .join('\n')}\n}`);
163
+ return rules.join('\n');
164
+ };
165
+ export const plans = new Map();
166
+ /** A built-in effect's CSS plan, in the build's reach (`--greese-motion-distance`). */
167
+ export const effectPlan = (name) => {
168
+ if (!plans.has(name))
169
+ plans.set(name, cssPlan(effectFrames(name, cssReach)));
170
+ return plans.get(name);
171
+ };
172
+ /** Whether a built-in effect ends invisible: it leaves, where others arrive or add emphasis. */
173
+ export const endsHidden = (name) => {
174
+ const end = {};
175
+ for (const [, declarations] of toCssSteps(effectFrames(name, cssReach)))
176
+ Object.assign(end, declarations);
177
+ return Number(end.opacity) === 0;
178
+ };
179
+ /** The built-in effects CSS plays alone: those that end at rest. */
180
+ export const entranceNames = () => effectNames.filter((name) => effectPlan(name).enter);
181
+ /** The built-in effects an overlay can leave with: those that end away from rest. */
182
+ export const exitNames = () => effectNames.filter((name) => !effectPlan(name).enter);
183
+ /** The class-name form of an effect name: `fadeUp` → `fade-up`. */
184
+ export const effectClass = (name) => kebab(name);