@moonarc/core 0.1.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 (232) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +39 -0
  3. package/package.json +228 -0
  4. package/src/components/Accordion.astro +110 -0
  5. package/src/components/Accordion.meta.ts +33 -0
  6. package/src/components/AnimatedBeam.astro +217 -0
  7. package/src/components/AnimatedBeam.meta.ts +39 -0
  8. package/src/components/AnimatedIcons.astro +193 -0
  9. package/src/components/AnimatedIcons.meta.ts +34 -0
  10. package/src/components/AnimatedList.astro +305 -0
  11. package/src/components/AnimatedList.meta.ts +36 -0
  12. package/src/components/AnimatedSelect.astro +227 -0
  13. package/src/components/AnimatedSelect.meta.ts +38 -0
  14. package/src/components/ArrowButton.astro +133 -0
  15. package/src/components/ArrowButton.meta.ts +32 -0
  16. package/src/components/Aurora.astro +188 -0
  17. package/src/components/Aurora.meta.ts +36 -0
  18. package/src/components/AvatarStack.astro +205 -0
  19. package/src/components/AvatarStack.meta.ts +37 -0
  20. package/src/components/BarGrow.astro +123 -0
  21. package/src/components/BarGrow.meta.ts +42 -0
  22. package/src/components/BorderBeam.astro +75 -0
  23. package/src/components/BorderBeam.meta.ts +33 -0
  24. package/src/components/BrowserFrame.astro +172 -0
  25. package/src/components/BrowserFrame.meta.ts +36 -0
  26. package/src/components/BubbleText.astro +90 -0
  27. package/src/components/BubbleText.meta.ts +38 -0
  28. package/src/components/CardStack.astro +79 -0
  29. package/src/components/CardStack.meta.ts +33 -0
  30. package/src/components/Carousel.astro +147 -0
  31. package/src/components/Carousel.meta.ts +35 -0
  32. package/src/components/CheckDraw.astro +159 -0
  33. package/src/components/CheckDraw.meta.ts +37 -0
  34. package/src/components/CircularText.astro +103 -0
  35. package/src/components/CircularText.meta.ts +38 -0
  36. package/src/components/CodeTabs.astro +213 -0
  37. package/src/components/CodeTabs.meta.ts +36 -0
  38. package/src/components/CommandPalette.astro +363 -0
  39. package/src/components/CommandPalette.meta.ts +40 -0
  40. package/src/components/Confetti.astro +143 -0
  41. package/src/components/Confetti.meta.ts +40 -0
  42. package/src/components/CopyButton.astro +189 -0
  43. package/src/components/CopyButton.meta.ts +41 -0
  44. package/src/components/CountUp.astro +134 -0
  45. package/src/components/CountUp.meta.ts +40 -0
  46. package/src/components/CountdownFlip.astro +244 -0
  47. package/src/components/CountdownFlip.meta.ts +39 -0
  48. package/src/components/CounterPreloader.astro +122 -0
  49. package/src/components/CounterPreloader.meta.ts +34 -0
  50. package/src/components/Cursor.astro +177 -0
  51. package/src/components/Cursor.meta.ts +37 -0
  52. package/src/components/CursorGrid.astro +113 -0
  53. package/src/components/CursorGrid.meta.ts +37 -0
  54. package/src/components/Curtain.astro +305 -0
  55. package/src/components/Curtain.meta.ts +38 -0
  56. package/src/components/Dialog.astro +156 -0
  57. package/src/components/Dialog.meta.ts +37 -0
  58. package/src/components/Dock.astro +193 -0
  59. package/src/components/Dock.meta.ts +35 -0
  60. package/src/components/DotPattern.astro +58 -0
  61. package/src/components/DotPattern.meta.ts +32 -0
  62. package/src/components/DottedMap.astro +153 -0
  63. package/src/components/DottedMap.meta.ts +38 -0
  64. package/src/components/Drawer.astro +196 -0
  65. package/src/components/Drawer.meta.ts +35 -0
  66. package/src/components/DynamicIsland.astro +122 -0
  67. package/src/components/DynamicIsland.meta.ts +35 -0
  68. package/src/components/FileTree.astro +207 -0
  69. package/src/components/FileTree.meta.ts +34 -0
  70. package/src/components/FilterGrid.astro +170 -0
  71. package/src/components/FilterGrid.meta.ts +38 -0
  72. package/src/components/FooterReveal.astro +54 -0
  73. package/src/components/FooterReveal.meta.ts +31 -0
  74. package/src/components/GlitchText.astro +145 -0
  75. package/src/components/GlitchText.meta.ts +39 -0
  76. package/src/components/Globe.astro +304 -0
  77. package/src/components/Globe.meta.ts +41 -0
  78. package/src/components/GlyphField.astro +363 -0
  79. package/src/components/GlyphField.meta.ts +53 -0
  80. package/src/components/GradientText.astro +63 -0
  81. package/src/components/GradientText.meta.ts +35 -0
  82. package/src/components/GridPattern.astro +55 -0
  83. package/src/components/GridPattern.meta.ts +32 -0
  84. package/src/components/HighlightMarker.astro +68 -0
  85. package/src/components/HighlightMarker.meta.ts +37 -0
  86. package/src/components/HorizontalScroll.astro +112 -0
  87. package/src/components/HorizontalScroll.meta.ts +34 -0
  88. package/src/components/HoverImageLinks.astro +219 -0
  89. package/src/components/HoverImageLinks.meta.ts +34 -0
  90. package/src/components/HoverVideo.astro +179 -0
  91. package/src/components/HoverVideo.meta.ts +37 -0
  92. package/src/components/ImageCompare.astro +212 -0
  93. package/src/components/ImageCompare.meta.ts +39 -0
  94. package/src/components/ImageTrail.astro +169 -0
  95. package/src/components/ImageTrail.meta.ts +36 -0
  96. package/src/components/Lightbox.astro +236 -0
  97. package/src/components/Lightbox.meta.ts +40 -0
  98. package/src/components/LinkPreview.astro +234 -0
  99. package/src/components/LinkPreview.meta.ts +39 -0
  100. package/src/components/LiquidGlass.astro +148 -0
  101. package/src/components/LiquidGlass.meta.ts +37 -0
  102. package/src/components/Magnetic.astro +92 -0
  103. package/src/components/Magnetic.meta.ts +31 -0
  104. package/src/components/Marquee.astro +165 -0
  105. package/src/components/Marquee.meta.ts +38 -0
  106. package/src/components/Meteors.astro +156 -0
  107. package/src/components/Meteors.meta.ts +36 -0
  108. package/src/components/Noise.astro +61 -0
  109. package/src/components/Noise.meta.ts +33 -0
  110. package/src/components/NumberFlow.astro +143 -0
  111. package/src/components/NumberFlow.meta.ts +42 -0
  112. package/src/components/Orb.astro +130 -0
  113. package/src/components/Orb.meta.ts +39 -0
  114. package/src/components/OrbitingCircles.astro +119 -0
  115. package/src/components/OrbitingCircles.meta.ts +36 -0
  116. package/src/components/OtpInput.astro +215 -0
  117. package/src/components/OtpInput.meta.ts +42 -0
  118. package/src/components/PageTransition.astro +121 -0
  119. package/src/components/PageTransition.meta.ts +33 -0
  120. package/src/components/Parallax.astro +69 -0
  121. package/src/components/Parallax.meta.ts +32 -0
  122. package/src/components/Particles.astro +210 -0
  123. package/src/components/Particles.meta.ts +37 -0
  124. package/src/components/PersistPlayer.astro +126 -0
  125. package/src/components/PersistPlayer.meta.ts +35 -0
  126. package/src/components/PhoneFrame.astro +161 -0
  127. package/src/components/PhoneFrame.meta.ts +35 -0
  128. package/src/components/PillNav.astro +170 -0
  129. package/src/components/PillNav.meta.ts +37 -0
  130. package/src/components/Popover.astro +95 -0
  131. package/src/components/Popover.meta.ts +34 -0
  132. package/src/components/PriceSwitch.astro +251 -0
  133. package/src/components/PriceSwitch.meta.ts +41 -0
  134. package/src/components/Probe.astro +84 -0
  135. package/src/components/Progress.astro +165 -0
  136. package/src/components/Progress.meta.ts +40 -0
  137. package/src/components/ProgressiveBlur.astro +95 -0
  138. package/src/components/ProgressiveBlur.meta.ts +36 -0
  139. package/src/components/PromptInput.astro +273 -0
  140. package/src/components/PromptInput.meta.ts +46 -0
  141. package/src/components/ReadingToc.astro +153 -0
  142. package/src/components/ReadingToc.meta.ts +34 -0
  143. package/src/components/RetroGrid.astro +84 -0
  144. package/src/components/RetroGrid.meta.ts +36 -0
  145. package/src/components/Reveal.astro +292 -0
  146. package/src/components/Reveal.meta.ts +48 -0
  147. package/src/components/Ripple.astro +94 -0
  148. package/src/components/Ripple.meta.ts +34 -0
  149. package/src/components/RotatingText.astro +170 -0
  150. package/src/components/RotatingText.meta.ts +35 -0
  151. package/src/components/ScrollExpand.astro +90 -0
  152. package/src/components/ScrollExpand.meta.ts +35 -0
  153. package/src/components/ScrollProgress.astro +73 -0
  154. package/src/components/ScrollProgress.meta.ts +32 -0
  155. package/src/components/ScrollScrub.astro +96 -0
  156. package/src/components/ScrollScrub.meta.ts +37 -0
  157. package/src/components/ShimmerButton.astro +109 -0
  158. package/src/components/ShimmerButton.meta.ts +37 -0
  159. package/src/components/ShinyText.astro +72 -0
  160. package/src/components/ShinyText.meta.ts +35 -0
  161. package/src/components/Skeleton.astro +76 -0
  162. package/src/components/Skeleton.meta.ts +32 -0
  163. package/src/components/Sparkline.astro +168 -0
  164. package/src/components/Sparkline.meta.ts +43 -0
  165. package/src/components/Spinner.astro +79 -0
  166. package/src/components/Spinner.meta.ts +33 -0
  167. package/src/components/SplitFlap.astro +157 -0
  168. package/src/components/SplitFlap.meta.ts +39 -0
  169. package/src/components/SplitText.astro +208 -0
  170. package/src/components/SplitText.meta.ts +50 -0
  171. package/src/components/Spotlight.astro +92 -0
  172. package/src/components/Spotlight.meta.ts +34 -0
  173. package/src/components/StaggerMenu.astro +301 -0
  174. package/src/components/StaggerMenu.meta.ts +39 -0
  175. package/src/components/StatefulButton.astro +148 -0
  176. package/src/components/StatefulButton.meta.ts +35 -0
  177. package/src/components/StickyBanner.astro +168 -0
  178. package/src/components/StickyBanner.meta.ts +45 -0
  179. package/src/components/StickyReveal.astro +256 -0
  180. package/src/components/StickyReveal.meta.ts +33 -0
  181. package/src/components/StrokeDraw.astro +110 -0
  182. package/src/components/StrokeDraw.meta.ts +43 -0
  183. package/src/components/Switch.astro +165 -0
  184. package/src/components/Switch.meta.ts +37 -0
  185. package/src/components/Tabs.astro +194 -0
  186. package/src/components/Tabs.meta.ts +47 -0
  187. package/src/components/TextRoll.astro +78 -0
  188. package/src/components/TextRoll.meta.ts +36 -0
  189. package/src/components/TextScramble.astro +107 -0
  190. package/src/components/TextScramble.meta.ts +35 -0
  191. package/src/components/ThemeScript.astro +17 -0
  192. package/src/components/ThemeToggle.astro +182 -0
  193. package/src/components/ThemeToggle.meta.ts +34 -0
  194. package/src/components/Tilt.astro +97 -0
  195. package/src/components/Tilt.meta.ts +34 -0
  196. package/src/components/Toast.astro +184 -0
  197. package/src/components/Toast.meta.ts +33 -0
  198. package/src/components/TracingBeam.astro +113 -0
  199. package/src/components/TracingBeam.meta.ts +34 -0
  200. package/src/components/Typewriter.astro +124 -0
  201. package/src/components/Typewriter.meta.ts +39 -0
  202. package/src/components/UnderlineHover.astro +61 -0
  203. package/src/components/UnderlineHover.meta.ts +34 -0
  204. package/src/components/VanishInput.astro +240 -0
  205. package/src/components/VanishInput.meta.ts +41 -0
  206. package/src/components/VideoDialog.astro +230 -0
  207. package/src/components/VideoDialog.meta.ts +40 -0
  208. package/src/components/WeightWave.astro +70 -0
  209. package/src/components/WeightWave.meta.ts +36 -0
  210. package/src/components/internal/DialogMorph.astro +97 -0
  211. package/src/components/internal/PromptInputSubmitOnEnter.astro +32 -0
  212. package/src/index.d.ts +43 -0
  213. package/src/index.js +60 -0
  214. package/src/lib/canvas.ts +124 -0
  215. package/src/lib/confetti.ts +127 -0
  216. package/src/lib/contrast.ts +116 -0
  217. package/src/lib/form.ts +231 -0
  218. package/src/lib/geo.ts +77 -0
  219. package/src/lib/gsap.ts +58 -0
  220. package/src/lib/land.ts +11 -0
  221. package/src/lib/lenis.ts +112 -0
  222. package/src/lib/meta.ts +79 -0
  223. package/src/lib/morph.ts +74 -0
  224. package/src/lib/reduced-motion-plugin.js +299 -0
  225. package/src/lib/runtime.ts +215 -0
  226. package/src/lib/support.ts +102 -0
  227. package/src/lib/theme.ts +18 -0
  228. package/src/lib/toast.ts +17 -0
  229. package/src/styles/base.css +218 -0
  230. package/src/styles/motion.css +43 -0
  231. package/src/styles/theme.css +397 -0
  232. package/src/styles/theme.tailwind.css +75 -0
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Browser support table for every platform feature a component leans on.
3
+ * Versions are the first stable release per engine, read from
4
+ * @mdn/browser-compat-data 8.1.1 on 2026-09-12, three rows on 2026-09-14 and
5
+ * four more for phase 7B, one for 7C (backdrop-filter), four for 7D (match-element, vertical range, grid-row animation, interestfor) the same day and seven for 7E on 2026-09-16 (user-invalid, base-select, picker, sibling-index, field-sizing, the switch attribute, SVG backdrop filters — the last measured, not in BCD), two for 7F the same day (relative colour, contrast-color), and on 2026-09-28 two corrected (css-math split from abs(), display in keyframes) and three added (the exponential functions, view-transition-class, dialog closedby) (`docs/browser-support.md` records the audits). `false` means not shipped; the component's meta then
6
+ * states what that browser shows instead.
7
+ *
8
+ * Kept in the package so the site, llms.txt and the MCP server print the same
9
+ * numbers, and so "0 B" is never claimed for something that runs in one engine.
10
+ */
11
+
12
+ export interface Feature {
13
+ /** Short label for a chip. */
14
+ label: string;
15
+ chrome: number | false;
16
+ firefox: number | false;
17
+ safari: number | false;
18
+ }
19
+
20
+ export const features = {
21
+ 'scroll-timeline': { label: 'animation-timeline: scroll() / view()', chrome: 115, firefox: false, safari: 26 },
22
+ 'starting-style': { label: '@starting-style', chrome: 117, firefox: 129, safari: 17.5 },
23
+ 'allow-discrete': { label: 'transition-behavior: allow-discrete', chrome: 117, firefox: 129, safari: 17.4 },
24
+ 'details-content': { label: '::details-content', chrome: 131, firefox: 143, safari: 18.4 },
25
+ 'scroll-marker': { label: '::scroll-marker / ::scroll-button()', chrome: 135, firefox: false, safari: false },
26
+ property: { label: '@property', chrome: 85, firefox: 128, safari: 16.4 },
27
+ has: { label: ':has()', chrome: 105, firefox: 121, safari: 15.4 },
28
+ popover: { label: 'popover attribute', chrome: 114, firefox: 125, safari: 17 },
29
+ anchor: { label: 'anchor positioning', chrome: 125, firefox: 147, safari: 26 },
30
+ command: { label: 'command / commandfor', chrome: 135, firefox: 144, safari: 26.2 },
31
+ 'details-name': { label: 'details name=', chrome: 120, firefox: 130, safari: 17.2 },
32
+ 'counter-style': { label: '@counter-style pad', chrome: 91, firefox: 33, safari: 17 },
33
+ 'view-transitions': { label: 'same-document view transitions', chrome: 111, firefox: 144, safari: 18 },
34
+ 'scroll-snap': { label: 'scroll-snap', chrome: 69, firefox: 99, safari: 11 },
35
+ 'bg-clip-text': { label: 'background-clip: text', chrome: 120, firefox: 49, safari: 14 },
36
+ /* BCD css.properties.display.keyframe_animatable; measured 2026-09-28: Firefox 155 ignores display in keyframes (the box stays) */
37
+ 'display-animation': { label: 'display in @keyframes', chrome: 116, firefox: false, safari: 18 },
38
+ 'font-variation': { label: 'variable font axes', chrome: 62, firefox: 62, safari: 11 },
39
+ 'mask-image': { label: 'mask-image', chrome: 120, firefox: 53, safari: 15.4 },
40
+ dialog: { label: '<dialog>', chrome: 37, firefox: 98, safari: 15.4 },
41
+ /* BCD html.elements.dialog.closedby (2026-09-28; Safari has it in Technology Preview only). Listed, not needed: in Safari
42
+ Dialog, StaggerMenu and Lightbox still close on Escape and their close button, Drawer on Escape and its drag, and
43
+ CommandPalette closes on a backdrop click by script, so no component puts it in `needs`. */
44
+ closedby: { label: 'dialog closedby', chrome: 134, firefox: 141, safari: false },
45
+ 'css-math': { label: 'mod() / round() in calc()', chrome: 125, firefox: 118, safari: 15.4 },
46
+ /* abs() and sign() reached Chrome thirteen releases after mod() and round() (BCD css.types.abs, css.types.sign);
47
+ listed, not used: the components that need an absolute value square it or take max(x, -x) */
48
+ 'css-abs': { label: 'abs() / sign() in calc()', chrome: 138, firefox: 118, safari: 15.4 },
49
+ /* the exponential family (BCD css.types.hypot, pow, sqrt, exp, log: one release each), for AnimatedBeam's hypot() */
50
+ 'css-exp': { label: 'hypot() / pow() / sqrt() in calc()', chrome: 120, firefox: 118, safari: 15.4 },
51
+ 'clip-path': { label: 'clip-path basic shapes', chrome: 55, firefox: 54, safari: 13.1 },
52
+ 'mask-composite': { label: 'mask-composite', chrome: 120, firefox: 53, safari: 15.4 },
53
+ 'container-units': { label: 'container query units (cqw / cqh)', chrome: 105, firefox: 110, safari: 16 },
54
+ 'counter-set': { label: 'counter-set', chrome: 85, firefox: 68, safari: 17.2 },
55
+ 'timeline-scope': { label: 'timeline-scope', chrome: 116, firefox: false, safari: 26 },
56
+ 'd-path': { label: 'd: path() as a CSS property', chrome: 52, firefox: 97, safari: false },
57
+ clipboard: { label: 'navigator.clipboard.writeText', chrome: 66, firefox: 63, safari: 13.1 },
58
+ 'backdrop-filter': { label: 'backdrop-filter', chrome: 76, firefox: 103, safari: 18 },
59
+ /* Chrome-only; listed so the audit prints it, not needed by any component (StatefulButton bounds its width from the label length instead). */
60
+ 'interpolate-size': { label: 'interpolate-size: allow-keywords', chrome: 129, firefox: false, safari: false },
61
+ /* phase 7D */
62
+ 'match-element': { label: 'view-transition-name: match-element', chrome: 137, firefox: 144, safari: 18.4 },
63
+ /* BCD css.properties.view-transition-class, for PillNav (2026-09-28) */
64
+ 'vt-class': { label: 'view-transition-class', chrome: 125, firefox: 144, safari: 18.2 },
65
+ 'range-vertical': { label: 'vertical <input type=range> (writing-mode)', chrome: 124, firefox: 120, safari: 16.5 },
66
+ 'grid-rows-animation': { label: 'animatable grid-template-rows', chrome: 107, firefox: 66, safari: 16 },
67
+ /* Chrome-only; listed as the proposed C route for LinkPreview (a hover-intent popover with no script), not used. */
68
+ interestfor: { label: 'interestfor (interest invokers)', chrome: 142, firefox: false, safari: false },
69
+ /* phase 7F (BCD 8.1.1, 2026-09-16): the theme contract. Neither is needed by a section — the static scale and the tested
70
+ foreground colours are what a browser without them gets — so they are listed for the audit and the theming docs. */
71
+ 'relative-color': { label: 'oklch(from …) relative colour', chrome: 122, firefox: 128, safari: 18 },
72
+ 'contrast-color': { label: 'contrast-color()', chrome: 147, firefox: 146, safari: 26 },
73
+ /* phase 7E (BCD 8.1.1, 2026-09-16) */
74
+ 'user-invalid': { label: ':user-valid / :user-invalid', chrome: 119, firefox: 88, safari: 16.5 },
75
+ 'base-select': { label: 'appearance: base-select (customizable select)', chrome: 135, firefox: false, safari: 27 },
76
+ picker: { label: '::picker(select) / ::checkmark', chrome: 135, firefox: false, safari: 27 },
77
+ 'sibling-index': { label: 'sibling-index()', chrome: 138, firefox: 154, safari: 26.2 },
78
+ 'field-sizing': { label: 'field-sizing: content', chrome: 123, firefox: 152, safari: 26.2 },
79
+ /* Safari-only; Switch sets the attribute but never depends on it (appearance: none draws the control everywhere). */
80
+ 'switch-attr': { label: '<input type=checkbox switch>', chrome: false, firefox: false, safari: 17.4 },
81
+ /* not in BCD; measured on the LiquidGlass fixture in three engines (docs/phase-7e.md): Chromium only. */
82
+ 'backdrop-svg': { label: 'backdrop-filter: url(#svg-filter)', chrome: 76, firefox: false, safari: false },
83
+ /* FluidField: WebGL2 contexts; float render targets (EXT_color_buffer_float) are checked at runtime */
84
+ webgl2: { label: 'WebGL2', chrome: 56, firefox: 51, safari: 15 },
85
+ } as const satisfies Record<string, Feature>;
86
+
87
+ export type FeatureKey = keyof typeof features;
88
+
89
+ /** `Chrome 115 · Safari 26 · not Firefox` — one line for chips and llms.txt. */
90
+ export function supportLine(keys: readonly FeatureKey[]): string {
91
+ if (keys.length === 0) return 'every browser';
92
+ const need = keys.map((k) => features[k]);
93
+ const min = (b: 'chrome' | 'firefox' | 'safari') => need.reduce<number | false>((acc, f) => (acc === false || f[b] === false ? false : Math.max(acc, f[b])), 0);
94
+ const parts: string[] = [];
95
+ const c = min('chrome');
96
+ const f = min('firefox');
97
+ const s = min('safari');
98
+ parts.push(c === false ? 'not Chrome' : `Chrome ${c}`);
99
+ parts.push(f === false ? 'not Firefox' : `Firefox ${f}`);
100
+ parts.push(s === false ? 'not Safari' : `Safari ${s}`);
101
+ return parts.join(' · ');
102
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The anti-flash theme script, as a string: ThemeScript inlines it in <head>
3
+ * so the resolved theme is on <html> before the first frame, and the site's
4
+ * stage prints its byte size. Written compact on purpose — inline scripts
5
+ * are not minified. The contract ThemeToggle relies on:
6
+ *
7
+ * localStorage[key] 'system' | 'light' | 'dark' (the choice)
8
+ * html[data-theme] the resolved theme, 'light' | 'dark'
9
+ * html[data-theme-choice] the choice
10
+ * window.__maTheme { choice(), apply(), set(choice) }
11
+ *
12
+ * The attributes are re-applied after every ClientRouter swap (the router
13
+ * replaces <html>'s attributes) and when the OS scheme changes.
14
+ */
15
+ export function themeScript(key = 'ma-theme'): string {
16
+ const k = key.replace(/[^\w-]/g, '');
17
+ return `(function(){var K='${k}',r=document.documentElement,m=matchMedia('(prefers-color-scheme: dark)');function g(){try{return localStorage.getItem(K)||'system'}catch(e){return 'system'}}function a(){var c=g();r.dataset.theme=c==='system'?(m.matches?'dark':'light'):c;r.dataset.themeChoice=c}window.__maTheme={choice:g,apply:a,set:function(c){try{localStorage.setItem(K,c)}catch(e){}a()}};a();document.addEventListener('astro:after-swap',a);m.addEventListener('change',a)})()`;
18
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * toast() — raise a notification in the page's <Toast /> region.
3
+ * Dispatches `ma:toast` on document; the region listens through the runtime,
4
+ * so this works after every navigation and costs nothing on pages without a
5
+ * region.
6
+ */
7
+ export interface ToastOptions {
8
+ title: string;
9
+ description?: string;
10
+ /** Lifetime in ms; defaults to the region's duration. */
11
+ duration?: number;
12
+ }
13
+
14
+ export function toast(options: ToastOptions | string): void {
15
+ const detail: ToastOptions = typeof options === 'string' ? { title: options } : options;
16
+ document.dispatchEvent(new CustomEvent('ma:toast', { detail }));
17
+ }
@@ -0,0 +1,218 @@
1
+ /*
2
+ * @moonarc/core — base tokens (motion.theme).
3
+ *
4
+ * Injected on every page by the integration (or imported manually). Every rule
5
+ * is wrapped in :where() so it has zero specificity: a consumer overrides any
6
+ * token with a plain `:root { --ma-foo: … }` and never needs !important.
7
+ *
8
+ * Cascade layers. Tailwind v4 puts utilities in @layer utilities, and any
9
+ * unlayered stylesheet beats a layered one regardless of specificity — so a
10
+ * library that ships unlayered CSS can never be overridden by `class="p-0"`.
11
+ * Declaring Tailwind's own order here (harmless without Tailwind) and putting
12
+ * tokens in `theme` and component rules in `components` means: consumer
13
+ * utilities, consumer classes, and consumer scoped styles always win; the
14
+ * library only beats resets (`base`).
15
+ *
16
+ * Presets. No component hard-codes a curve, a duration, a travel distance or
17
+ * a stagger; everything resolves from the six tokens under "active preset".
18
+ * Five presets ship — snap · ui · gentle · lively · ambient — each a
19
+ * mass-spring-damper sampled into linear() (stiffness/damping in the comment)
20
+ * with its settle time as the duration. Switching the whole page is one
21
+ * attribute: <html data-ma-preset="lively">. Any subtree can carry its own.
22
+ *
23
+ * Colour tokens follow the three-state rule:
24
+ * 1. light is the base definition — never define a colour only in a media query
25
+ * 2. system-dark applies unless the page has explicitly chosen light
26
+ * 3. explicit dark wins in both directions, via [data-theme] or .dark
27
+ */
28
+
29
+ @layer theme, base, components, utilities;
30
+
31
+ @layer theme {
32
+ :where(:root) {
33
+ /* springs, sampled — stiffness / damping, mass 1 */
34
+ --ma-ease-snap: linear(0, 0.013 1.7%, 0.046 3.3%, 0.092 5%, 0.447 15%, 0.599 20%, 0.718 25%, 0.805 30%, 0.884 36.7%, 0.961 50%, 1); /* 1218 / 70 */
35
+ --ma-ease-ui: linear(0, 0.008 1.7%, 0.029 3.3%, 0.099 6.7%, 0.525 21.7%, 0.675 28.3%, 0.786 35%, 0.879 43.3%, 0.971 61.7%, 1); /* 305 / 33 */
36
+ --ma-ease-gentle: linear(0, 0.008 1.7%, 0.03 3.3%, 0.101 6.7%, 0.486 20%, 0.645 26.7%, 0.764 33.3%, 0.865 41.7%, 0.956 56.7%, 1); /* 110 / 20 */
37
+ --ma-ease-lively: linear(0, 0.056 1.7%, 0.201 3.3%, 0.402 5%, 0.839 8.3%, 1.027 10%, 1.171 11.7%, 1.267 13.3%, 1.314 15%, 1.317 16.7%, 1.285 18.3%, 1.23 20%, 1.024 25%, 0.97 26.7%, 0.93 28.3%, 0.906 30%, 0.898 31.7%, 0.902 33.3%, 1.016 43.3%, 1.032 46.7%, 0.99 63.3%, 1); /* 622 / 17 */
38
+ --ma-ease-ambient: linear(0, 0.01 1.7%, 0.038 3.3%, 0.077 5%, 0.5 18.3%, 0.631 23.3%, 0.733 28.3%, 0.831 35%, 0.919 45%, 1); /* 43 / 13 */
39
+ --ma-dur-snap: 290ms;
40
+ --ma-dur-ui: 450ms;
41
+ --ma-dur-gentle: 760ms;
42
+ --ma-dur-lively: 840ms;
43
+ --ma-dur-ambient: 1390ms;
44
+
45
+ /* fixed curves — exits and continuous motion never use a spring */
46
+ --ma-ease-out: cubic-bezier(0.23, 1, 0.32, 1);
47
+ --ma-ease-in: cubic-bezier(0.5, 0, 0.75, 0);
48
+ --ma-ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
49
+ --ma-ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);
50
+ --ma-ease-spring: var(--ma-ease-lively);
51
+
52
+ /* scale */
53
+ --ma-stagger-tight: 40ms;
54
+ --ma-stagger-base: 60ms;
55
+ --ma-stagger-relaxed: 120ms;
56
+ --ma-travel-hover: 4px;
57
+ --ma-travel-enter: 24px;
58
+ --ma-travel-section: 48px;
59
+ --ma-duration-fast: 150ms;
60
+ --ma-duration-slow: 700ms;
61
+
62
+ /* active preset: ui */
63
+ --ma-preset: ui;
64
+ --ma-ease: var(--ma-ease-ui);
65
+ --ma-duration: var(--ma-dur-ui);
66
+ --ma-stagger: var(--ma-stagger-base);
67
+
68
+ /* colour — light base */
69
+ --ma-shine: rgb(255 255 255 / 0.85);
70
+ --ma-glow: rgb(0 0 0 / 0.1);
71
+ --ma-scrim: rgb(255 255 255 / 0.72);
72
+ --ma-edge: rgb(0 0 0 / 0.08);
73
+ --ma-ink: rgb(0 0 0 / 0.9);
74
+ --ma-panel: rgb(255 255 255 / 0.92);
75
+ /* secondary text (a hint, a time, an inactive tab) is --ma-ink at this opacity: the lowest that keeps 4.5 : 1 on the
76
+ panel and on a tinted surface in the light theme (0.55 read 4.0). Decoration may go fainter; text may not. */
77
+ --ma-dim: 0.7;
78
+ /* 0 on a light ground, 1 on a dark one — a scalar for calc(), so a component can lean lighter or darker with the theme */
79
+ --ma-dark: 0;
80
+ }
81
+
82
+ /* presets — on <html> or any subtree */
83
+ :where([data-ma-preset='snap']) {
84
+ --ma-preset: snap;
85
+ --ma-ease: var(--ma-ease-snap);
86
+ --ma-duration: var(--ma-dur-snap);
87
+ --ma-stagger: var(--ma-stagger-tight);
88
+ --ma-travel-enter: 16px;
89
+ --ma-travel-hover: 2px;
90
+ }
91
+ :where([data-ma-preset='ui']) {
92
+ --ma-preset: ui;
93
+ --ma-ease: var(--ma-ease-ui);
94
+ --ma-duration: var(--ma-dur-ui);
95
+ --ma-stagger: var(--ma-stagger-base);
96
+ --ma-travel-enter: 24px;
97
+ --ma-travel-hover: 4px;
98
+ }
99
+ :where([data-ma-preset='gentle']) {
100
+ --ma-preset: gentle;
101
+ --ma-ease: var(--ma-ease-gentle);
102
+ --ma-duration: var(--ma-dur-gentle);
103
+ --ma-stagger: var(--ma-stagger-base);
104
+ --ma-travel-enter: 24px;
105
+ --ma-travel-hover: 3px;
106
+ }
107
+ :where([data-ma-preset='lively']) {
108
+ --ma-preset: lively;
109
+ --ma-ease: var(--ma-ease-lively);
110
+ --ma-duration: var(--ma-dur-lively);
111
+ --ma-stagger: var(--ma-stagger-tight);
112
+ --ma-travel-enter: 32px;
113
+ --ma-travel-hover: 6px;
114
+ }
115
+ :where([data-ma-preset='ambient']) {
116
+ --ma-preset: ambient;
117
+ --ma-ease: var(--ma-ease-ambient);
118
+ --ma-duration: var(--ma-dur-ambient);
119
+ --ma-stagger: var(--ma-stagger-relaxed);
120
+ --ma-travel-enter: 48px;
121
+ --ma-travel-hover: 4px;
122
+ }
123
+
124
+ /* system dark, unless the page has explicitly chosen light */
125
+ @media (prefers-color-scheme: dark) {
126
+ :where(:root:not([data-theme='light']):not(.light)) {
127
+ --ma-shine: rgb(255 255 255 / 0.32);
128
+ --ma-glow: rgb(255 255 255 / 0.14);
129
+ --ma-scrim: rgb(10 10 12 / 0.72);
130
+ --ma-edge: rgb(255 255 255 / 0.1);
131
+ --ma-ink: rgb(255 255 255 / 0.92);
132
+ --ma-panel: rgb(24 24 27 / 0.94);
133
+ --ma-dark: 1;
134
+ }
135
+ }
136
+
137
+ /* explicit dark wins in both directions, and supports both conventions */
138
+ :where(:root[data-theme='dark'], :root.dark) {
139
+ --ma-shine: rgb(255 255 255 / 0.32);
140
+ --ma-glow: rgb(255 255 255 / 0.14);
141
+ --ma-scrim: rgb(10 10 12 / 0.72);
142
+ --ma-edge: rgb(255 255 255 / 0.1);
143
+ --ma-ink: rgb(255 255 255 / 0.92);
144
+ --ma-panel: rgb(24 24 27 / 0.94);
145
+ --ma-dark: 1;
146
+ }
147
+ }
148
+
149
+ /*
150
+ * Same-document view transitions the library starts itself (Dialog morph,
151
+ * Lightbox, FilterGrid — lib/morph.ts). For their duration <html> carries
152
+ * data-ma-vt, and these rules give the root snapshots a plain cross-fade on
153
+ * the tokens. The attribute specificity outranks Curtain's zero-specificity
154
+ * root rules, so a morph on a page with a Curtain never opens an iris; the
155
+ * mask and clip Curtain's blinds and pixels set are cleared as well.
156
+ */
157
+ @layer components {
158
+ /* The text a split, rolled or counted component is made of, for assistive tech: the animated parts are aria-hidden,
159
+ this is the string once. Not aria-label on the wrapper — a span, div or p may not be named that way (ARIA 1.2),
160
+ and screen readers skip it. user-select: none keeps a copied heading from carrying the text twice. */
161
+ :where(.ma-sr) {
162
+ position: absolute;
163
+ width: 1px;
164
+ height: 1px;
165
+ overflow: hidden;
166
+ clip-path: inset(50%);
167
+ white-space: nowrap;
168
+ user-select: none;
169
+ }
170
+ :where(:root)[data-ma-vt]::view-transition-old(root),
171
+ :where(:root)[data-ma-vt]::view-transition-new(root) {
172
+ animation-duration: var(--ma-duration);
173
+ animation-timing-function: var(--ma-ease-out);
174
+ animation-fill-mode: both;
175
+ animation-direction: normal;
176
+ mix-blend-mode: plus-lighter;
177
+ mask-image: none;
178
+ clip-path: none;
179
+ }
180
+ :where(:root)[data-ma-vt]::view-transition-old(root) {
181
+ animation-name: ma-vt-out;
182
+ }
183
+ :where(:root)[data-ma-vt]::view-transition-new(root) {
184
+ animation-name: ma-vt-in;
185
+ }
186
+ :where(:root)[data-ma-vt]::view-transition-group(*) {
187
+ animation-duration: var(--ma-duration);
188
+ animation-timing-function: var(--ma-ease);
189
+ }
190
+ @media (prefers-reduced-motion: reduce) {
191
+ :where(:root)[data-ma-vt]::view-transition-old(root),
192
+ :where(:root)[data-ma-vt]::view-transition-new(root),
193
+ :where(:root)[data-ma-vt]::view-transition-group(*) {
194
+ animation-duration: var(--ma-duration-fast);
195
+ animation-timing-function: linear;
196
+ }
197
+ }
198
+ }
199
+
200
+ /* While the library runs a transition of its own, elements named through Astro's transition:name step out of it: a
201
+ catalogue of a hundred named stages is not captured for one morphing dialog. Unlayered on purpose: Astro writes its
202
+ [data-astro-transition-scope="…"] rules into the page unlayered, and an unlayered rule beats a layered one whatever
203
+ the specificity, so inside @layer components this lost and one FilterGrid click captured 141 stages. Out here the
204
+ attribute specificity decides, and Astro's single attribute loses. */
205
+ :root[data-ma-vt] [data-astro-transition-scope] {
206
+ view-transition-name: none;
207
+ }
208
+
209
+ @keyframes ma-vt-out {
210
+ to {
211
+ opacity: 0;
212
+ }
213
+ }
214
+ @keyframes ma-vt-in {
215
+ from {
216
+ opacity: 0;
217
+ }
218
+ }
@@ -0,0 +1,43 @@
1
+ /*
2
+ * @moonarc/core/styles — opt-in Tailwind v4 token layer.
3
+ *
4
+ * Maps @theme tokens onto the same custom properties base.css defines, so
5
+ * `ease-out-quint`, `duration-ma`, `ease-snap`, etc. become real utilities
6
+ * that share the library's curves. Requires Tailwind v4; consumers without
7
+ * Tailwind simply never import this file and lose nothing.
8
+ *
9
+ * Usage: @import "tailwindcss";
10
+ * @import "@moonarc/core/styles";
11
+ */
12
+
13
+ /* inline: a utility writes var(--ma-ease) itself, so it resolves on the element and follows a data-ma-preset subtree;
14
+ a plain @theme resolves var(--ease-ma) on :root, where only the page's preset exists. Durations are
15
+ --transition-duration-*, the namespace the duration-* utilities read (--duration-* makes no utility). */
16
+ @theme inline {
17
+ /* the active preset */
18
+ --ease-ma: var(--ma-ease);
19
+ --transition-duration-ma: var(--ma-duration);
20
+
21
+ /* the five presets by name */
22
+ --ease-snap: var(--ma-ease-snap);
23
+ --ease-ui: var(--ma-ease-ui);
24
+ --ease-gentle: var(--ma-ease-gentle);
25
+ --ease-lively: var(--ma-ease-lively);
26
+ --ease-ambient: var(--ma-ease-ambient);
27
+ --transition-duration-snap: var(--ma-dur-snap);
28
+ --transition-duration-ui: var(--ma-dur-ui);
29
+ --transition-duration-gentle: var(--ma-dur-gentle);
30
+ --transition-duration-lively: var(--ma-dur-lively);
31
+ --transition-duration-ambient: var(--ma-dur-ambient);
32
+
33
+ /* fixed curves */
34
+ --ease-out-quint: var(--ma-ease-out);
35
+ --ease-in-quint: var(--ma-ease-in);
36
+ --ease-in-out-quint: var(--ma-ease-in-out);
37
+ --ease-drawer: var(--ma-ease-drawer);
38
+ --ease-spring: var(--ma-ease-spring);
39
+
40
+ --transition-duration-fast: var(--ma-duration-fast);
41
+ --transition-duration-base: var(--ma-duration);
42
+ --transition-duration-slow: var(--ma-duration-slow);
43
+ }