@yewdiel21/motion-kit 0.0.0-stage → 0.9.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.
- package/NOTICE +38 -0
- package/README.md +80 -2
- package/bin/motion-kit.mjs +242 -0
- package/css.d.ts +2 -0
- package/dist/auto/index.d.ts +63 -0
- package/dist/auto/index.js +207 -0
- package/dist/auto/index.js.map +1 -0
- package/dist/chunk-2BQGFOA7.js +177 -0
- package/dist/chunk-2BQGFOA7.js.map +1 -0
- package/dist/chunk-34XYOXHG.js +2374 -0
- package/dist/chunk-34XYOXHG.js.map +1 -0
- package/dist/chunk-6OJAP6ZL.js +832 -0
- package/dist/chunk-6OJAP6ZL.js.map +1 -0
- package/dist/chunk-ABNRDDO7.js +28 -0
- package/dist/chunk-ABNRDDO7.js.map +1 -0
- package/dist/chunk-FS2HJVVY.js +3320 -0
- package/dist/chunk-FS2HJVVY.js.map +1 -0
- package/dist/chunk-J27KH2A2.js +157 -0
- package/dist/chunk-J27KH2A2.js.map +1 -0
- package/dist/chunk-KLY5BRRI.js +18 -0
- package/dist/chunk-KLY5BRRI.js.map +1 -0
- package/dist/chunk-Q4LVFNLN.js +1617 -0
- package/dist/chunk-Q4LVFNLN.js.map +1 -0
- package/dist/chunk-RFDIZYFX.js +304 -0
- package/dist/chunk-RFDIZYFX.js.map +1 -0
- package/dist/chunk-TGV27ZWM.js +17 -0
- package/dist/chunk-TGV27ZWM.js.map +1 -0
- package/dist/confetti-BSX1CLLV.d.ts +41 -0
- package/dist/css/components/chat-background.css +87 -0
- package/dist/css/components/chat.css +237 -0
- package/dist/css/components/cursor.css +178 -0
- package/dist/css/components/dialog.css +259 -0
- package/dist/css/components/island.css +301 -0
- package/dist/css/components/menu.css +105 -0
- package/dist/css/components/nav.css +1054 -0
- package/dist/css/components/steps.css +50 -0
- package/dist/css/components/theme-picker.css +115 -0
- package/dist/css/components/toast.css +115 -0
- package/dist/css/controls.css +950 -0
- package/dist/css/effects.css +587 -0
- package/dist/css/fonts.css +3 -0
- package/dist/css/themes/black-green.css +126 -0
- package/dist/css/themes/black.css +126 -0
- package/dist/css/themes/blue.css +126 -0
- package/dist/css/themes/brown.css +126 -0
- package/dist/css/themes/cold-blue.css +126 -0
- package/dist/css/themes/funny.css +126 -0
- package/dist/css/themes/gray.css +126 -0
- package/dist/css/themes/green.css +126 -0
- package/dist/css/themes/modern-1.css +126 -0
- package/dist/css/themes/neogreen.css +126 -0
- package/dist/css/themes/new.css +126 -0
- package/dist/css/themes/new2.css +126 -0
- package/dist/css/themes/oled.css +126 -0
- package/dist/css/themes/orange.css +126 -0
- package/dist/css/themes/pastel-pink.css +126 -0
- package/dist/css/themes/pink.css +126 -0
- package/dist/css/themes/purple.css +126 -0
- package/dist/css/themes/red.css +126 -0
- package/dist/css/themes/super-blue.css +126 -0
- package/dist/css/themes.css +2431 -0
- package/dist/css/tokens.css +452 -0
- package/dist/elements/index.d.ts +183 -0
- package/dist/elements/index.js +472 -0
- package/dist/elements/index.js.map +1 -0
- package/dist/engine-CUr9QijM.d.ts +46 -0
- package/dist/gl/index.d.ts +61 -0
- package/dist/gl/index.js +18 -0
- package/dist/gl/index.js.map +1 -0
- package/dist/index.css +5296 -0
- package/dist/index.d.ts +440 -0
- package/dist/index.js +390 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.d.ts +476 -0
- package/dist/react/index.js +969 -0
- package/dist/react/index.js.map +1 -0
- package/dist/reduced-BM4Ls0g5.d.ts +566 -0
- package/dist/reveal-WFUWDrei.d.ts +416 -0
- package/dist/store-Bqy_obnD.d.ts +195 -0
- package/dist/theme/generate.d.ts +18 -0
- package/dist/theme/generate.js +64 -0
- package/dist/theme/generate.js.map +1 -0
- package/dist/theme/index.d.ts +17 -0
- package/dist/theme/index.js +32 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/theme-BFMoCzi6.d.ts +158 -0
- package/dist/types-BkS_raL8.d.ts +7 -0
- package/package.json +114 -3
- package/registry.json +469 -0
- package/skills/motion-kit/SKILL.md +189 -0
- package/skills/motion-kit/reference/components.md +86 -0
- package/skills/motion-kit/reference/design.md +92 -0
- package/skills/motion-kit/reference/recipes.md +172 -0
- package/src/auto/auto-init.ts +151 -0
- package/src/auto/index.ts +3 -0
- package/src/auto/parse-options.ts +68 -0
- package/src/components/chat/README.md +90 -0
- package/src/components/chat/chat.css +236 -0
- package/src/components/chat/element.ts +98 -0
- package/src/components/chat/engine.ts +295 -0
- package/src/components/chat/react.tsx +196 -0
- package/src/components/chat-background/README.md +50 -0
- package/src/components/chat-background/audio.ts +56 -0
- package/src/components/chat-background/chat-background.css +86 -0
- package/src/components/chat-background/element.ts +31 -0
- package/src/components/chat-background/engine.ts +108 -0
- package/src/components/chat-background/react.tsx +36 -0
- package/src/components/cursor/README.md +53 -0
- package/src/components/cursor/cursor.css +177 -0
- package/src/components/cursor/element.ts +23 -0
- package/src/components/cursor/engine.ts +254 -0
- package/src/components/cursor/react.tsx +26 -0
- package/src/components/dialog/README.md +106 -0
- package/src/components/dialog/dialog.css +258 -0
- package/src/components/dialog/element.ts +118 -0
- package/src/components/dialog/engine.ts +432 -0
- package/src/components/dialog/react.tsx +200 -0
- package/src/components/icon.tsx +10 -0
- package/src/components/icons.ts +24 -0
- package/src/components/island/README.md +91 -0
- package/src/components/island/element.ts +45 -0
- package/src/components/island/engine.ts +200 -0
- package/src/components/island/island.css +300 -0
- package/src/components/island/react.tsx +115 -0
- package/src/components/menu/README.md +67 -0
- package/src/components/menu/element.ts +55 -0
- package/src/components/menu/engine.ts +142 -0
- package/src/components/menu/menu.css +104 -0
- package/src/components/menu/react.tsx +82 -0
- package/src/components/nav/README.md +85 -0
- package/src/components/nav/element.ts +39 -0
- package/src/components/nav/engine.ts +164 -0
- package/src/components/nav/nav.css +1053 -0
- package/src/components/nav/react.tsx +90 -0
- package/src/components/steps/README.md +70 -0
- package/src/components/steps/element.ts +43 -0
- package/src/components/steps/engine.ts +185 -0
- package/src/components/steps/react.tsx +91 -0
- package/src/components/steps/steps.css +49 -0
- package/src/components/theme-picker/README.md +57 -0
- package/src/components/theme-picker/element.ts +20 -0
- package/src/components/theme-picker/engine.ts +91 -0
- package/src/components/theme-picker/react.tsx +31 -0
- package/src/components/theme-picker/theme-picker.css +114 -0
- package/src/components/toast/README.md +60 -0
- package/src/components/toast/element.ts +25 -0
- package/src/components/toast/engine.ts +483 -0
- package/src/components/toast/react.tsx +22 -0
- package/src/components/toast/store.ts +122 -0
- package/src/components/toast/toast.css +114 -0
- package/src/core/channels.ts +165 -0
- package/src/core/curves.ts +35 -0
- package/src/core/frame.ts +12 -0
- package/src/core/gsap.ts +65 -0
- package/src/core/loop.ts +44 -0
- package/src/core/math.ts +9 -0
- package/src/core/physics-spring.ts +59 -0
- package/src/core/pointer.ts +63 -0
- package/src/core/reduced.ts +21 -0
- package/src/core/scope.ts +70 -0
- package/src/core/scroll-track.ts +106 -0
- package/src/core/split.ts +68 -0
- package/src/core/spring.ts +22 -0
- package/src/core/styles.ts +31 -0
- package/src/core/timing.ts +204 -0
- package/src/core/types.ts +20 -0
- package/src/core/visible.ts +19 -0
- package/src/css/base.css +142 -0
- package/src/css/controls/README.md +122 -0
- package/src/css/controls/burger.css +67 -0
- package/src/css/controls/button.css +226 -0
- package/src/css/controls/checkbox.css +53 -0
- package/src/css/controls/chip.css +85 -0
- package/src/css/controls/field.css +124 -0
- package/src/css/controls/glass.css +220 -0
- package/src/css/controls/icon.css +26 -0
- package/src/css/controls/segmented.css +60 -0
- package/src/css/controls/switch.css +72 -0
- package/src/css/default-theme.css +123 -0
- package/src/css/effects/README.md +134 -0
- package/src/css/effects/anim.css +224 -0
- package/src/css/effects/fade.css +15 -0
- package/src/css/effects/float.css +64 -0
- package/src/css/effects/frost.css +62 -0
- package/src/css/effects/grain.css +58 -0
- package/src/css/effects/loading.css +60 -0
- package/src/css/effects/success.css +22 -0
- package/src/css/effects/text.css +67 -0
- package/src/css/fonts.css +3 -0
- package/src/css/states.css +51 -0
- package/src/css/themes/black-green.css +126 -0
- package/src/css/themes/black.css +126 -0
- package/src/css/themes/blue.css +126 -0
- package/src/css/themes/brown.css +126 -0
- package/src/css/themes/cold-blue.css +126 -0
- package/src/css/themes/funny.css +126 -0
- package/src/css/themes/gray.css +126 -0
- package/src/css/themes/green.css +126 -0
- package/src/css/themes/modern-1.css +126 -0
- package/src/css/themes/neogreen.css +126 -0
- package/src/css/themes/new.css +126 -0
- package/src/css/themes/new2.css +126 -0
- package/src/css/themes/oled.css +126 -0
- package/src/css/themes/orange.css +126 -0
- package/src/css/themes/pastel-pink.css +126 -0
- package/src/css/themes/pink.css +126 -0
- package/src/css/themes/purple.css +126 -0
- package/src/css/themes/red.css +126 -0
- package/src/css/themes/super-blue.css +126 -0
- package/src/css/tokens.css +182 -0
- package/src/elements/base.ts +76 -0
- package/src/elements/index.ts +48 -0
- package/src/gl/index.ts +15 -0
- package/src/gl/liquid-blend/README.md +23 -0
- package/src/gl/liquid-blend/create-liquid-blend.ts +7 -0
- package/src/gl/liquid-blend/shader.ts +9 -0
- package/src/gl/matcap/README.md +23 -0
- package/src/gl/matcap/create-matcap.ts +7 -0
- package/src/gl/matcap/shader.ts +16 -0
- package/src/gl/particle-hero/README.md +41 -0
- package/src/gl/particle-hero/create-particle-hero.ts +544 -0
- package/src/gl/particle-hero/particle-hero.css +19 -0
- package/src/gl/particle-hero/shaders.ts +84 -0
- package/src/gl/shader-blur/README.md +23 -0
- package/src/gl/shader-blur/create-shader-blur.ts +7 -0
- package/src/gl/shader-blur/shader.ts +11 -0
- package/src/gl/shader-grain/README.md +23 -0
- package/src/gl/shader-grain/create-shader-grain.ts +7 -0
- package/src/gl/shader-grain/shader.ts +6 -0
- package/src/gl/shared/create-shader-effect.ts +186 -0
- package/src/gl/shared/shader.css +3 -0
- package/src/gl/shared/shaders.ts +30 -0
- package/src/index.ts +177 -0
- package/src/motion/anchor.ts +45 -0
- package/src/motion/chat.ts +97 -0
- package/src/motion/confetti.ts +130 -0
- package/src/motion/count.ts +95 -0
- package/src/motion/details.ts +70 -0
- package/src/motion/drag-dismiss.ts +231 -0
- package/src/motion/effects.ts +81 -0
- package/src/motion/entrances.ts +27 -0
- package/src/motion/floating.ts +389 -0
- package/src/motion/flow.ts +115 -0
- package/src/motion/indicator.ts +44 -0
- package/src/motion/layout.ts +39 -0
- package/src/motion/liquid.ts +227 -0
- package/src/motion/morph.ts +540 -0
- package/src/motion/reveal.ts +112 -0
- package/src/motion/sheet.ts +65 -0
- package/src/motion/surface.ts +108 -0
- package/src/react/animated-number.tsx +30 -0
- package/src/react/flip-group.tsx +44 -0
- package/src/react/frost.tsx +33 -0
- package/src/react/index.ts +80 -0
- package/src/react/slot-text.tsx +44 -0
- package/src/react/theme-provider.tsx +42 -0
- package/src/react/use-card-dock.ts +9 -0
- package/src/react/use-cascade.ts +9 -0
- package/src/react/use-contrast.ts +9 -0
- package/src/react/use-counter.ts +8 -0
- package/src/react/use-curved-carousel.ts +10 -0
- package/src/react/use-depth-grid.ts +9 -0
- package/src/react/use-details.ts +7 -0
- package/src/react/use-dock-pill.ts +10 -0
- package/src/react/use-drag-ruler.ts +10 -0
- package/src/react/use-fill-text.ts +9 -0
- package/src/react/use-fit-text.ts +8 -0
- package/src/react/use-flow.ts +8 -0
- package/src/react/use-frame-zoom.ts +9 -0
- package/src/react/use-infinite-reel.ts +8 -0
- package/src/react/use-letters.ts +9 -0
- package/src/react/use-liquid-blend.ts +8 -0
- package/src/react/use-marquee.ts +9 -0
- package/src/react/use-mask-text.ts +9 -0
- package/src/react/use-matcap.ts +8 -0
- package/src/react/use-parallax.ts +9 -0
- package/src/react/use-particle-hero.ts +9 -0
- package/src/react/use-pile.ts +8 -0
- package/src/react/use-pop.ts +8 -0
- package/src/react/use-radial-menu.ts +10 -0
- package/src/react/use-recipe.ts +38 -0
- package/src/react/use-reflow.ts +9 -0
- package/src/react/use-repel-bento.ts +8 -0
- package/src/react/use-reveal.ts +8 -0
- package/src/react/use-rotator.ts +10 -0
- package/src/react/use-shader-blur.ts +8 -0
- package/src/react/use-shader-grain.ts +8 -0
- package/src/react/use-slot-text.ts +17 -0
- package/src/react/use-snap-strip.ts +9 -0
- package/src/react/use-sortable.ts +14 -0
- package/src/react/use-spring-cards.ts +8 -0
- package/src/react/use-stretch-footer.ts +20 -0
- package/src/react/use-tabs.ts +10 -0
- package/src/react/use-text-roll.ts +9 -0
- package/src/react/use-ticket-menu.ts +15 -0
- package/src/react/use-words.ts +8 -0
- package/src/recipes/card-dock/README.md +36 -0
- package/src/recipes/card-dock/card-dock.css +178 -0
- package/src/recipes/card-dock/create-card-dock.ts +97 -0
- package/src/recipes/cascade/README.md +33 -0
- package/src/recipes/cascade/create-cascade.ts +111 -0
- package/src/recipes/contrast/README.md +29 -0
- package/src/recipes/contrast/create-contrast.ts +68 -0
- package/src/recipes/counter/README.md +42 -0
- package/src/recipes/counter/create-counter.ts +84 -0
- package/src/recipes/curved-carousel/README.md +50 -0
- package/src/recipes/curved-carousel/carousel-math.ts +20 -0
- package/src/recipes/curved-carousel/create-curved-carousel.ts +257 -0
- package/src/recipes/curved-carousel/curved-carousel.css +50 -0
- package/src/recipes/depth-grid/README.md +30 -0
- package/src/recipes/depth-grid/create-depth-grid.ts +120 -0
- package/src/recipes/details/README.md +31 -0
- package/src/recipes/details/create-details.ts +27 -0
- package/src/recipes/dock-pill/README.md +23 -0
- package/src/recipes/dock-pill/create-dock-pill.ts +68 -0
- package/src/recipes/dock-pill/dock-pill.css +5 -0
- package/src/recipes/drag-ruler/README.md +21 -0
- package/src/recipes/drag-ruler/create-drag-ruler.ts +95 -0
- package/src/recipes/drag-ruler/drag-ruler.css +16 -0
- package/src/recipes/fill-text/README.md +27 -0
- package/src/recipes/fill-text/create-fill-text.ts +93 -0
- package/src/recipes/fit-text/README.md +37 -0
- package/src/recipes/fit-text/create-fit-text.ts +55 -0
- package/src/recipes/fit-text/fit-text.css +6 -0
- package/src/recipes/flow/README.md +46 -0
- package/src/recipes/flow/create-flow.ts +55 -0
- package/src/recipes/frame-zoom/README.md +29 -0
- package/src/recipes/frame-zoom/create-frame-zoom.ts +91 -0
- package/src/recipes/infinite-reel/README.md +23 -0
- package/src/recipes/infinite-reel/create-infinite-reel.ts +140 -0
- package/src/recipes/infinite-reel/infinite-reel.css +8 -0
- package/src/recipes/letters/README.md +30 -0
- package/src/recipes/letters/create-letters.ts +113 -0
- package/src/recipes/marquee/README.md +32 -0
- package/src/recipes/marquee/create-marquee.ts +74 -0
- package/src/recipes/marquee/marquee.css +41 -0
- package/src/recipes/mask-text/README.md +39 -0
- package/src/recipes/mask-text/create-mask-text.ts +134 -0
- package/src/recipes/parallax/README.md +31 -0
- package/src/recipes/parallax/create-parallax.ts +74 -0
- package/src/recipes/pile/README.md +48 -0
- package/src/recipes/pile/create-pile.ts +50 -0
- package/src/recipes/pile/pile-math.ts +27 -0
- package/src/recipes/pile/pile.css +63 -0
- package/src/recipes/pill/README.md +30 -0
- package/src/recipes/pill/pill.css +48 -0
- package/src/recipes/pop/README.md +36 -0
- package/src/recipes/pop/create-pop.ts +52 -0
- package/src/recipes/radial-menu/README.md +44 -0
- package/src/recipes/radial-menu/create-radial-menu.ts +96 -0
- package/src/recipes/radial-menu/radial-menu.css +45 -0
- package/src/recipes/reflow/README.md +47 -0
- package/src/recipes/reflow/create-reflow.ts +49 -0
- package/src/recipes/repel-bento/README.md +21 -0
- package/src/recipes/repel-bento/create-repel-bento.ts +65 -0
- package/src/recipes/repel-bento/repel-bento.css +3 -0
- package/src/recipes/reveal/README.md +36 -0
- package/src/recipes/reveal/create-reveal.ts +48 -0
- package/src/recipes/rotator/README.md +21 -0
- package/src/recipes/rotator/create-rotator.ts +66 -0
- package/src/recipes/rotator/rotator.css +3 -0
- package/src/recipes/slot-text/README.md +44 -0
- package/src/recipes/slot-text/create-slot-text.ts +244 -0
- package/src/recipes/slot-text/slot-text.css +42 -0
- package/src/recipes/snap-strip/README.md +31 -0
- package/src/recipes/snap-strip/create-snap-strip.ts +133 -0
- package/src/recipes/snap-strip/snap-strip.css +23 -0
- package/src/recipes/sortable/README.md +88 -0
- package/src/recipes/sortable/create-sortable.ts +863 -0
- package/src/recipes/sortable/sortable.css +40 -0
- package/src/recipes/spring-cards/README.md +48 -0
- package/src/recipes/spring-cards/create-spring-cards.ts +140 -0
- package/src/recipes/spring-cards/spring-cards.css +23 -0
- package/src/recipes/stretch-footer/README.md +44 -0
- package/src/recipes/stretch-footer/create-stretch-footer.ts +69 -0
- package/src/recipes/stretch-footer/stretch-footer.css +28 -0
- package/src/recipes/tabs/README.md +39 -0
- package/src/recipes/tabs/create-tabs.ts +91 -0
- package/src/recipes/tabs/tabs.css +19 -0
- package/src/recipes/text-roll/README.md +35 -0
- package/src/recipes/text-roll/create-text-roll.ts +137 -0
- package/src/recipes/text-roll/text-roll.css +21 -0
- package/src/recipes/ticket-menu/README.md +24 -0
- package/src/recipes/ticket-menu/create-ticket-menu.ts +6 -0
- package/src/recipes/words/README.md +43 -0
- package/src/recipes/words/create-words.ts +114 -0
- package/src/theme/css-format.ts +32 -0
- package/src/theme/generate.ts +72 -0
- package/src/theme/index.ts +15 -0
- package/src/theme/presets.ts +79 -0
- package/src/theme/theme.ts +119 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { whileVisible } from './loop';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* How far an element has travelled through the viewport, 0 → 1, between two
|
|
5
|
+
* ScrollTrigger-style positions ("<element edge> <viewport line>"), read from
|
|
6
|
+
* its box every frame while it is near the screen. Siena's trackers read a
|
|
7
|
+
* smooth-scrolled position (Lenis, .1 a frame); on a page that scrolls natively
|
|
8
|
+
* the effects follow it exactly (lerp 1), so a picture never drifts behind its
|
|
9
|
+
* frame — pass lerp .1 to smooth the effect alone. Parallax, depth grid and
|
|
10
|
+
* frame zoom render from it.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface ScrollTrackOptions {
|
|
14
|
+
/** "<top|center|bottom|n%> <top|center|bottom|n%>" — default "top bottom" (the element enters). */
|
|
15
|
+
start?: string;
|
|
16
|
+
/** Default "bottom top" (the element has left). */
|
|
17
|
+
end?: string;
|
|
18
|
+
/** Share of the distance covered each 60 fps frame (default 1: follow the page exactly). */
|
|
19
|
+
lerp?: number;
|
|
20
|
+
onUpdate?: (value: number) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ScrollTrack {
|
|
24
|
+
readonly value: number;
|
|
25
|
+
readonly target: number;
|
|
26
|
+
/** Raw progress now, from the element's box. */
|
|
27
|
+
measure(): number;
|
|
28
|
+
/** Measure and ease one frame (dt = 1 at 60 Hz). */
|
|
29
|
+
update(dt?: number): void;
|
|
30
|
+
/** Jump to a value (manual control). */
|
|
31
|
+
set(value: number): void;
|
|
32
|
+
destroy(): void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const clamp01 = (value: number) => Math.min(1, Math.max(0, value));
|
|
36
|
+
|
|
37
|
+
function edgeOf(box: DOMRect, edge: string) {
|
|
38
|
+
if (edge === 'top') return box.top;
|
|
39
|
+
if (edge === 'bottom') return box.bottom;
|
|
40
|
+
if (edge === 'center') return box.top + box.height / 2;
|
|
41
|
+
if (edge.endsWith('%')) return box.top + (box.height * parseFloat(edge)) / 100;
|
|
42
|
+
return box.top + (parseFloat(edge) || 0);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function lineOf(height: number, line: string) {
|
|
46
|
+
if (line === 'top') return 0;
|
|
47
|
+
if (line === 'bottom') return height;
|
|
48
|
+
if (line === 'center') return height / 2;
|
|
49
|
+
if (line.endsWith('%')) return (height * parseFloat(line)) / 100;
|
|
50
|
+
return parseFloat(line) || 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function trackScroll(
|
|
54
|
+
el: Element,
|
|
55
|
+
{ start = 'top bottom', end = 'bottom top', lerp = 1, onUpdate }: ScrollTrackOptions = {},
|
|
56
|
+
): ScrollTrack {
|
|
57
|
+
const [startEdge = 'top', startLine = 'bottom'] = start.trim().split(/\s+/);
|
|
58
|
+
const [endEdge = 'bottom', endLine = 'top'] = end.trim().split(/\s+/);
|
|
59
|
+
let destroyed = false;
|
|
60
|
+
|
|
61
|
+
const measure = () => {
|
|
62
|
+
const box = el.getBoundingClientRect();
|
|
63
|
+
const height = window.innerHeight;
|
|
64
|
+
// Both grow as the page scrolls down; their difference is the distance travelled.
|
|
65
|
+
const fromStart = lineOf(height, startLine) - edgeOf(box, startEdge);
|
|
66
|
+
const fromEnd = lineOf(height, endLine) - edgeOf(box, endEdge);
|
|
67
|
+
const distance = fromStart - fromEnd;
|
|
68
|
+
if (distance <= 0) return fromStart >= 0 ? 1 : 0;
|
|
69
|
+
return clamp01(fromStart / distance);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
let target = measure();
|
|
73
|
+
let value = target;
|
|
74
|
+
onUpdate?.(value);
|
|
75
|
+
|
|
76
|
+
const update = (dt = 1) => {
|
|
77
|
+
if (destroyed) return;
|
|
78
|
+
target = measure();
|
|
79
|
+
const step = lerp >= 1 ? 1 : 1 - Math.pow(1 - Math.max(lerp, 0), dt);
|
|
80
|
+
value += (target - value) * step;
|
|
81
|
+
if (Math.abs(target - value) < 1e-4) value = target;
|
|
82
|
+
onUpdate?.(value);
|
|
83
|
+
};
|
|
84
|
+
const stop = whileVisible(el, update, '200px');
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
get value() {
|
|
88
|
+
return value;
|
|
89
|
+
},
|
|
90
|
+
get target() {
|
|
91
|
+
return target;
|
|
92
|
+
},
|
|
93
|
+
measure,
|
|
94
|
+
update,
|
|
95
|
+
set(next: number) {
|
|
96
|
+
if (destroyed) return;
|
|
97
|
+
value = target = clamp01(next);
|
|
98
|
+
onUpdate?.(value);
|
|
99
|
+
},
|
|
100
|
+
destroy() {
|
|
101
|
+
if (destroyed) return;
|
|
102
|
+
destroyed = true;
|
|
103
|
+
stop();
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Split text for animation and put it back afterwards. Words become inline-block
|
|
3
|
+
* spans (so they can move); spaces stay plain text so lines still wrap. Elements
|
|
4
|
+
* matched by `exclude` (tables, code) are left whole.
|
|
5
|
+
*/
|
|
6
|
+
export interface Split {
|
|
7
|
+
parts: HTMLElement[];
|
|
8
|
+
revert(): void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface SplitWordsOptions {
|
|
12
|
+
exclude?: string[];
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function splitWords(el: HTMLElement, { exclude = ['table', 'pre'], className = 'mk-split-word' }: SplitWordsOptions = {}): Split {
|
|
17
|
+
const parts: HTMLElement[] = [];
|
|
18
|
+
const replaced: Array<[Text, Node[]]> = [];
|
|
19
|
+
const skip = exclude.join(',');
|
|
20
|
+
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
|
|
21
|
+
const texts: Text[] = [];
|
|
22
|
+
for (let node = walker.nextNode(); node; node = walker.nextNode()) {
|
|
23
|
+
const parent = (node as Text).parentElement;
|
|
24
|
+
if (skip && parent?.closest(skip) && el.contains(parent.closest(skip))) continue;
|
|
25
|
+
if ((node as Text).data.trim()) texts.push(node as Text);
|
|
26
|
+
}
|
|
27
|
+
for (const text of texts) {
|
|
28
|
+
const pieces = text.data.split(/(\s+)/);
|
|
29
|
+
const nodes: Node[] = [];
|
|
30
|
+
for (const piece of pieces) {
|
|
31
|
+
if (!piece) continue;
|
|
32
|
+
if (/^\s+$/.test(piece)) {
|
|
33
|
+
nodes.push(document.createTextNode(piece));
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const span = document.createElement('span');
|
|
37
|
+
span.className = className;
|
|
38
|
+
span.style.display = 'inline-block';
|
|
39
|
+
span.textContent = piece;
|
|
40
|
+
parts.push(span);
|
|
41
|
+
nodes.push(span);
|
|
42
|
+
}
|
|
43
|
+
text.replaceWith(...nodes);
|
|
44
|
+
replaced.push([text, nodes]);
|
|
45
|
+
}
|
|
46
|
+
let reverted = false;
|
|
47
|
+
return {
|
|
48
|
+
parts,
|
|
49
|
+
revert() {
|
|
50
|
+
if (reverted) return;
|
|
51
|
+
reverted = true;
|
|
52
|
+
for (const [text, nodes] of replaced.reverse()) {
|
|
53
|
+
nodes[0]?.parentNode?.insertBefore(text, nodes[0]);
|
|
54
|
+
for (const node of nodes) node.parentNode?.removeChild(node);
|
|
55
|
+
}
|
|
56
|
+
el.normalize();
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type Segmenter = { segment(text: string): Iterable<{ segment: string }> };
|
|
62
|
+
|
|
63
|
+
/** User-perceived characters (emoji with modifiers stay whole), at most `limit`. */
|
|
64
|
+
export function splitGraphemes(text: string, limit = 64): string[] {
|
|
65
|
+
const Ctor = (Intl as unknown as { Segmenter?: new (locale?: string, options?: { granularity: string }) => Segmenter }).Segmenter;
|
|
66
|
+
const all = Ctor ? Array.from(new Ctor(undefined, { granularity: 'grapheme' }).segment(text), (part) => part.segment) : Array.from(text);
|
|
67
|
+
return all.slice(0, limit);
|
|
68
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface Vec { x: number; y: number }
|
|
2
|
+
export interface Spring {
|
|
3
|
+
current: Vec;
|
|
4
|
+
target: Vec;
|
|
5
|
+
velocity: Vec;
|
|
6
|
+
update(dt?: number): void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Spencer Gabor's spring (stiffness .05, friction .8), normalised so dt = 1 is one 60 Hz frame. */
|
|
10
|
+
export function createSpring(stiffness = 0.05, friction = 0.8): Spring {
|
|
11
|
+
const current = { x: 0, y: 0 };
|
|
12
|
+
const target = { x: 0, y: 0 };
|
|
13
|
+
const velocity = { x: 0, y: 0 };
|
|
14
|
+
function update(dt = 1) {
|
|
15
|
+
const decay = Math.pow(friction, dt);
|
|
16
|
+
for (const axis of ['x', 'y'] as const) {
|
|
17
|
+
velocity[axis] = velocity[axis] * decay + (target[axis] - current[axis]) * stiffness * dt;
|
|
18
|
+
current[axis] = Math.round((current[axis] + velocity[axis] * dt) * 100) / 100;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return { current, target, velocity, update };
|
|
22
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove inline properties GSAP wrote, without asking GSAP. `gsap.set(el,
|
|
3
|
+
* { clearProps: 'transform' })` measures the element first, and an element
|
|
4
|
+
* that is not rendered (a closed <dialog>, display: none) is moved to the end of
|
|
5
|
+
* the document and back to be measured — which reorders text around it and
|
|
6
|
+
* disconnects custom elements inside. This only edits the style attribute and
|
|
7
|
+
* drops GSAP's cache so the next tween starts from scratch.
|
|
8
|
+
*/
|
|
9
|
+
export function clearStyles(targets: Iterable<Element>, properties: string[]) {
|
|
10
|
+
for (const element of targets) {
|
|
11
|
+
const style = (element as HTMLElement).style;
|
|
12
|
+
if (!style) continue;
|
|
13
|
+
for (const property of properties) style.removeProperty(property);
|
|
14
|
+
if (element.getAttribute('style') === '') element.removeAttribute('style');
|
|
15
|
+
delete (element as unknown as { _gsap?: unknown })._gsap;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** What the surface morphs write. */
|
|
20
|
+
export const MORPH_STYLES = [
|
|
21
|
+
'transform',
|
|
22
|
+
'translate',
|
|
23
|
+
'rotate',
|
|
24
|
+
'scale',
|
|
25
|
+
'transform-origin',
|
|
26
|
+
'clip-path',
|
|
27
|
+
'opacity',
|
|
28
|
+
'visibility',
|
|
29
|
+
'filter',
|
|
30
|
+
'will-change',
|
|
31
|
+
];
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every Stepbro duration in one place (seconds, like GSAP, unless the name says
|
|
3
|
+
* ms). Recipes and components take their defaults from here, so a timing is
|
|
4
|
+
* changed once. Sources: Stepbro replication kit, tasks/especificacion-replicacion.json
|
|
5
|
+
* and money/replication-catalog.json.
|
|
6
|
+
*/
|
|
7
|
+
export const TIMING = Object.freeze({
|
|
8
|
+
flow: 0.5,
|
|
9
|
+
flowStagger: 0.05,
|
|
10
|
+
stepChange: 0.5,
|
|
11
|
+
reveal: Object.freeze({
|
|
12
|
+
fast: [0.5, 0.25],
|
|
13
|
+
normal: [0.7, 0.35],
|
|
14
|
+
slow: [1.05, 0.5],
|
|
15
|
+
slower: [1.6, 0.75],
|
|
16
|
+
} as const),
|
|
17
|
+
details: 0.32,
|
|
18
|
+
chevron: 0.25,
|
|
19
|
+
indicator: 0.45,
|
|
20
|
+
number: 0.6,
|
|
21
|
+
numberPricing: 0.7,
|
|
22
|
+
revealCounter: 0.9,
|
|
23
|
+
layoutTravel: 0.62,
|
|
24
|
+
/** New items in a reflow enter in this share of the travel time. */
|
|
25
|
+
layoutEnterFactor: 0.6,
|
|
26
|
+
menu: Object.freeze({ duration: 0.35, stagger: 0.04, delay: 0.1 }),
|
|
27
|
+
palette: Object.freeze({ duration: 0.1, stagger: 0.01 }),
|
|
28
|
+
form: Object.freeze({ duration: 0.35, stagger: 0.04 }),
|
|
29
|
+
landing: Object.freeze({ duration: 0.4, stagger: 0.05, delay: 0.15 }),
|
|
30
|
+
content: Object.freeze({ duration: 0.6, stagger: 0.06 }),
|
|
31
|
+
bubble: 0.7,
|
|
32
|
+
bubbleStagger: 0.06,
|
|
33
|
+
word: 0.25,
|
|
34
|
+
wordStagger: 0.05,
|
|
35
|
+
chatScroll: 0.35,
|
|
36
|
+
composer: 0.18,
|
|
37
|
+
slot: Object.freeze({ durationMs: 300, staggerMs: 45, exitOffsetMs: 50, colorFadeMs: 280, maxTilt: 5, minResizeMs: 140 }),
|
|
38
|
+
swatch: 0.5,
|
|
39
|
+
swatchLight: 3.5,
|
|
40
|
+
swatchBackground: 4.5,
|
|
41
|
+
/** ModalHost (modal-origin, modal-close-origin, modal-fallback, modal-auto-resize). */
|
|
42
|
+
dialog: Object.freeze({
|
|
43
|
+
/** The surface travels from its opener. */
|
|
44
|
+
open: 0.7,
|
|
45
|
+
/** The opener's colour hands over to the surface's. */
|
|
46
|
+
surfaceColor: 0.3,
|
|
47
|
+
/** Back into the opener. */
|
|
48
|
+
close: 0.5,
|
|
49
|
+
/** While closing, the content heads for the opener on a 2 s path cut at `close`. */
|
|
50
|
+
contentReturn: 2,
|
|
51
|
+
originHide: 0.08,
|
|
52
|
+
originShow: 0.3,
|
|
53
|
+
originShowDelay: 0.2,
|
|
54
|
+
/** No opener on screen: scale .92 → 1. */
|
|
55
|
+
fallback: 0.35,
|
|
56
|
+
autoResize: 0.45,
|
|
57
|
+
/** Backdrop tint on the signature curve. */
|
|
58
|
+
backdrop: 0.5,
|
|
59
|
+
/** Items of a list that opens from its control. */
|
|
60
|
+
list: 0.25,
|
|
61
|
+
}),
|
|
62
|
+
/** modal-liquid (spring channels; springs are stiffness/damping, not times). */
|
|
63
|
+
liquid: Object.freeze({
|
|
64
|
+
size: 0.3,
|
|
65
|
+
radius: 0.7,
|
|
66
|
+
content: 0.3,
|
|
67
|
+
color: 0.4,
|
|
68
|
+
colorDelay: 0.15,
|
|
69
|
+
ghost: 0.25,
|
|
70
|
+
ghostDelay: 0.05,
|
|
71
|
+
closeSize: 0.25,
|
|
72
|
+
closeContent: 0.3,
|
|
73
|
+
originRestore: 0.3,
|
|
74
|
+
originRestoreDelay: 0.2,
|
|
75
|
+
safetyMs: 1100,
|
|
76
|
+
}),
|
|
77
|
+
/** modal-drag: the spring back when a drag is not far enough. */
|
|
78
|
+
drag: Object.freeze({ restore: 0.5, radiusRestore: 0.3 }),
|
|
79
|
+
/** modal-float. */
|
|
80
|
+
float: Object.freeze({ shadow: 0.45, glideDecay: 0.18, sizeSmoothing: 0.08 }),
|
|
81
|
+
/** chat-loader-delay, chat-confirmation-bar. */
|
|
82
|
+
chat: Object.freeze({ pendingDelayMs: 500, confirmWordStagger: 0.1 }),
|
|
83
|
+
/** onboarding-busy-background, step-change, onboarding-reveal-counter. */
|
|
84
|
+
onboarding: Object.freeze({ busy: 0.8, idle: 0.6, step: 0.5, counter: 0.9 }),
|
|
85
|
+
/** task-drag: lift, land (FA .62), crossfade (66 % of the land), neighbours shifting (FA .52). */
|
|
86
|
+
sortable: Object.freeze({ lift: 0.18, land: 0.42, landFade: 0.42 * 0.66, shift: 0.62 }),
|
|
87
|
+
/** toast-deck, toast-morph, toast-character-reveal. */
|
|
88
|
+
toast: Object.freeze({
|
|
89
|
+
relayout: 0.6,
|
|
90
|
+
expand: 0.55,
|
|
91
|
+
expandStagger: 0.06,
|
|
92
|
+
enter: 0.5,
|
|
93
|
+
exit: 0.3,
|
|
94
|
+
collapseDelayMs: 150,
|
|
95
|
+
morphSize: 0.32,
|
|
96
|
+
morphRadius: 0.7,
|
|
97
|
+
morphContent: 0.32,
|
|
98
|
+
morphColor: 0.4,
|
|
99
|
+
morphColorDelay: 0.15,
|
|
100
|
+
morphShadow: 0.6,
|
|
101
|
+
morphShadowDelay: 0.05,
|
|
102
|
+
safetyMs: 1100,
|
|
103
|
+
chars: 0.4,
|
|
104
|
+
charsDelay: 0.225,
|
|
105
|
+
charsStagger: 0.02,
|
|
106
|
+
}),
|
|
107
|
+
/** Dousan's glass (CSS): surface/shadow transitions, ring and shine. */
|
|
108
|
+
glass: Object.freeze({ hover: 0.4, ring: 0.5 }),
|
|
109
|
+
/** Stepbro's utility animations (css/effects/anim.css), by name. */
|
|
110
|
+
effects: Object.freeze({
|
|
111
|
+
out: 0.5,
|
|
112
|
+
'out-left': 0.5,
|
|
113
|
+
'out-bottom': 0.5,
|
|
114
|
+
collapse: 0.25,
|
|
115
|
+
highlight: 1,
|
|
116
|
+
shake: 0.5,
|
|
117
|
+
'shake-strong': 0.4,
|
|
118
|
+
fade: 0.3,
|
|
119
|
+
wipe: 0.7,
|
|
120
|
+
'progress-in': 1,
|
|
121
|
+
}),
|
|
122
|
+
/** Dousan's success confetti, per 60 fps frame. */
|
|
123
|
+
confetti: Object.freeze({
|
|
124
|
+
count: 45,
|
|
125
|
+
speed: [2, 8] as const,
|
|
126
|
+
lift: 2,
|
|
127
|
+
radius: [3, 7] as const,
|
|
128
|
+
decay: [0.015, 0.03] as const,
|
|
129
|
+
gravity: 0.15,
|
|
130
|
+
drag: 0.98,
|
|
131
|
+
spin: 2.5,
|
|
132
|
+
}),
|
|
133
|
+
/** Dousan's project sheet. */
|
|
134
|
+
sheet: Object.freeze({ open: 0.72, close: 0.72, underlayScale: 0.98, underlayOpacity: 0.4 }),
|
|
135
|
+
/** Dousan's springs as Motion declares them: [stiffness, damping]. */
|
|
136
|
+
springs: Object.freeze({
|
|
137
|
+
press: [400, 10] as const,
|
|
138
|
+
success: [260, 18] as const,
|
|
139
|
+
underlay: [300, 30] as const,
|
|
140
|
+
letters: [300, 10] as const,
|
|
141
|
+
cursorFollow: [800, 35] as const,
|
|
142
|
+
cursorAppear: [500, 30] as const,
|
|
143
|
+
bentoEnter: [60, 15] as const,
|
|
144
|
+
bentoRepel: [300, 25] as const,
|
|
145
|
+
dockPill: [380, 30] as const,
|
|
146
|
+
}),
|
|
147
|
+
/** OFFFICE's masked type (Siena's case titles start at 120 %). */
|
|
148
|
+
maskText: Object.freeze({ duration: 1.2, delay: 0.1, stagger: 0.02, exitStagger: 0.002, y: 100, threshold: 0.8 }),
|
|
149
|
+
/** Siena's reveal group (rules, fades, rises). */
|
|
150
|
+
cascade: Object.freeze({ duration: 1.2, step: 0.03, rise: 200, threshold: 0.3 }),
|
|
151
|
+
/** Siena's slot title: each letter turns 8–14 times inside a 2 s expo.out envelope. */
|
|
152
|
+
textRoll: Object.freeze({ duration: 2, stagger: 0.04, turns: [8, 14] as const, interval: 6000, quietMs: 1000 }),
|
|
153
|
+
/** Dousan's hero letters: scale on hover, pastel blink. */
|
|
154
|
+
letters: Object.freeze({
|
|
155
|
+
scale: 1.3,
|
|
156
|
+
y: -5,
|
|
157
|
+
step: 0.03,
|
|
158
|
+
blink: 0.6,
|
|
159
|
+
blinkStart: 0.6,
|
|
160
|
+
blinkRepeatDelay: 0.1,
|
|
161
|
+
blinkDelays: [0.1, 0.6, 0.2, 1, 0.4, 1.2, 0.15, 0.8, 0.3] as const,
|
|
162
|
+
}),
|
|
163
|
+
/** Siena's image parallax. */
|
|
164
|
+
parallax: Object.freeze({ travel: 10, scale: 1.2, strongScale: 1.3, lerp: 1 }),
|
|
165
|
+
/** Siena's About grid gathering from depth. */
|
|
166
|
+
depthGrid: Object.freeze({
|
|
167
|
+
perspective: 600,
|
|
168
|
+
distance: 900,
|
|
169
|
+
depth: -3000,
|
|
170
|
+
rotate: -160,
|
|
171
|
+
opacity: 0.05,
|
|
172
|
+
scale: 0.4,
|
|
173
|
+
stagger: 0.15,
|
|
174
|
+
lerp: 1,
|
|
175
|
+
}),
|
|
176
|
+
/** Siena's video mask: the frame and the picture inside scale in opposite directions. */
|
|
177
|
+
frameZoom: Object.freeze({
|
|
178
|
+
outer: [80, 20] as const,
|
|
179
|
+
inner: [130, 20] as const,
|
|
180
|
+
phoneOuter: [95, 5] as const,
|
|
181
|
+
phoneInner: [105, 5] as const,
|
|
182
|
+
open: 1,
|
|
183
|
+
lerp: 1,
|
|
184
|
+
}),
|
|
185
|
+
/** Siena's marquee: seconds per item, at most 14 counted. */
|
|
186
|
+
marquee: Object.freeze({ perItem: 2, maxItems: 14 }),
|
|
187
|
+
/** Dousan's glass chip and Siena's dot cursor. */
|
|
188
|
+
cursor: Object.freeze({ gutter: 10, circle: 96, dotLerp: 0.2, light: 180 }),
|
|
189
|
+
/** iyo.ai's floating navbar: enters from −300 % over 1.8 s; rows follow 0.15 s + 0.025 s each. */
|
|
190
|
+
island: Object.freeze({ entrance: 1.8, offset: -300, delay: 0.15, step: 0.025, focus: 150 }),
|
|
191
|
+
/** iyo.ai's product dock: spread for 1.6 s on load; tablet range re-checked 250 ms after a resize. */
|
|
192
|
+
cardDock: Object.freeze({ intro: 1600, resizeDebounce: 250 }),
|
|
193
|
+
/** iyo.ai's phone cards: 5 s a card, 2 s wait after a touch, 0.5 s after it shows. */
|
|
194
|
+
snapStrip: Object.freeze({ interval: 5000, resume: 2000, delay: 500 }),
|
|
195
|
+
/** iyo.ai's fill-text: letters from .2, 0.5 s each, 0.1 s apart, top 90 % → centre 40 %. */
|
|
196
|
+
fillText: Object.freeze({ from: 0.2, duration: 0.5, stagger: 0.1, start: 'top 90%', end: 'center 40%' }),
|
|
197
|
+
/** iyo.ai's particle hero (adapted): fade in 1 s after 0.3 s, morph cycles, pointer push. */
|
|
198
|
+
particleHero: Object.freeze({ fadeIn: 1, fadeDelay: 0.3, size: 128, pointSize: 1.7, maxDpr: 1.5, stillSteps: 110 }),
|
|
199
|
+
ticketMenu: Object.freeze({ open: 0.9, content: 1.1, stagger: 0.02, from: 150 }),
|
|
200
|
+
infiniteReel: Object.freeze({ friction: 0.95, wheel: 0.05, dragThreshold: 5 }),
|
|
201
|
+
repelBento: Object.freeze({ offset: 0.6, stagger: 0.006, strength: 5000, distance: 30, breakpoint: 1024 }),
|
|
202
|
+
rotator: Object.freeze({ interval: 8000, duration: 0.25, y: 5 }),
|
|
203
|
+
dragRuler: Object.freeze({ min: 0, max: 8, step: 0.1, pixelsPerUnit: 60 }),
|
|
204
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Every recipe returns at least this. `destroy()` is idempotent and leaves the DOM as it found it. */
|
|
2
|
+
export interface Instance {
|
|
3
|
+
destroy(): void;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export type Cleanup = () => void;
|
|
7
|
+
|
|
8
|
+
/** Instance returned when a recipe cannot start (missing root or children). */
|
|
9
|
+
export function inert<T extends Instance = Instance>(extra?: Omit<T, 'destroy'>): T {
|
|
10
|
+
return { destroy() {}, ...extra } as T;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const warned = new Set<string>();
|
|
14
|
+
|
|
15
|
+
/** Warn once per message, so a misconfigured recipe inside a loop does not flood the console. */
|
|
16
|
+
export function warnOnce(message: string) {
|
|
17
|
+
if (warned.has(message)) return;
|
|
18
|
+
warned.add(message);
|
|
19
|
+
console.warn(`[motion-kit] ${message}`);
|
|
20
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* IntersectionObserver helpers. Chromium only reports an entry as intersecting
|
|
3
|
+
* once it crosses the first threshold; Firefox and Safari report the first
|
|
4
|
+
* visible pixel. Entrances check the share themselves so they start at the same
|
|
5
|
+
* point everywhere.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** Thresholds that keep reporting while a block moves through the screen (and `threshold` itself). */
|
|
9
|
+
export function thresholds(threshold: number): number[] {
|
|
10
|
+
const steps = Array.from({ length: 11 }, (_, i) => i / 10);
|
|
11
|
+
return [...new Set([...steps, threshold])].sort((a, b) => a - b);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** `threshold` of the target shows — or, for a target taller than the screen, that share of the screen. */
|
|
15
|
+
export function shows(entry: IntersectionObserverEntry, threshold: number): boolean {
|
|
16
|
+
if (!entry.isIntersecting) return false;
|
|
17
|
+
if (entry.intersectionRatio >= threshold) return true;
|
|
18
|
+
return entry.rootBounds ? entry.intersectionRect.height >= entry.rootBounds.height * threshold : false;
|
|
19
|
+
}
|
package/src/css/base.css
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/* Base helpers: visually hidden text, the type scale as classes, and an app shell. */
|
|
2
|
+
.mk-sr-only {
|
|
3
|
+
position: absolute;
|
|
4
|
+
width: 1px;
|
|
5
|
+
height: 1px;
|
|
6
|
+
margin: -1px;
|
|
7
|
+
padding: 0;
|
|
8
|
+
overflow: hidden;
|
|
9
|
+
clip: rect(0 0 0 0);
|
|
10
|
+
clip-path: inset(50%);
|
|
11
|
+
white-space: nowrap;
|
|
12
|
+
border: 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/* Page colours and font from the theme: put it on <body> (or any root). */
|
|
16
|
+
.mk-app {
|
|
17
|
+
background: var(--mk-bg);
|
|
18
|
+
color: var(--mk-text);
|
|
19
|
+
font-family: var(--mk-font);
|
|
20
|
+
font-size: var(--md-sys-typescale-body-large-size);
|
|
21
|
+
line-height: var(--md-sys-typescale-body-large-line-height);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.mk-type-display-large {
|
|
25
|
+
font-family: var(--md-sys-typescale-display-large-font);
|
|
26
|
+
font-size: var(--md-sys-typescale-display-large-size);
|
|
27
|
+
line-height: var(--md-sys-typescale-display-large-line-height);
|
|
28
|
+
font-weight: var(--md-sys-typescale-display-large-weight);
|
|
29
|
+
letter-spacing: var(--md-sys-typescale-display-large-tracking);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.mk-type-display-medium {
|
|
33
|
+
font-family: var(--md-sys-typescale-display-medium-font);
|
|
34
|
+
font-size: var(--md-sys-typescale-display-medium-size);
|
|
35
|
+
line-height: var(--md-sys-typescale-display-medium-line-height);
|
|
36
|
+
font-weight: var(--md-sys-typescale-display-medium-weight);
|
|
37
|
+
letter-spacing: var(--md-sys-typescale-display-medium-tracking);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.mk-type-display-small {
|
|
41
|
+
font-family: var(--md-sys-typescale-display-small-font);
|
|
42
|
+
font-size: var(--md-sys-typescale-display-small-size);
|
|
43
|
+
line-height: var(--md-sys-typescale-display-small-line-height);
|
|
44
|
+
font-weight: var(--md-sys-typescale-display-small-weight);
|
|
45
|
+
letter-spacing: var(--md-sys-typescale-display-small-tracking);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.mk-type-headline-large {
|
|
49
|
+
font-family: var(--md-sys-typescale-headline-large-font);
|
|
50
|
+
font-size: var(--md-sys-typescale-headline-large-size);
|
|
51
|
+
line-height: var(--md-sys-typescale-headline-large-line-height);
|
|
52
|
+
font-weight: var(--md-sys-typescale-headline-large-weight);
|
|
53
|
+
letter-spacing: var(--md-sys-typescale-headline-large-tracking);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.mk-type-headline-medium {
|
|
57
|
+
font-family: var(--md-sys-typescale-headline-medium-font);
|
|
58
|
+
font-size: var(--md-sys-typescale-headline-medium-size);
|
|
59
|
+
line-height: var(--md-sys-typescale-headline-medium-line-height);
|
|
60
|
+
font-weight: var(--md-sys-typescale-headline-medium-weight);
|
|
61
|
+
letter-spacing: var(--md-sys-typescale-headline-medium-tracking);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.mk-type-headline-small {
|
|
65
|
+
font-family: var(--md-sys-typescale-headline-small-font);
|
|
66
|
+
font-size: var(--md-sys-typescale-headline-small-size);
|
|
67
|
+
line-height: var(--md-sys-typescale-headline-small-line-height);
|
|
68
|
+
font-weight: var(--md-sys-typescale-headline-small-weight);
|
|
69
|
+
letter-spacing: var(--md-sys-typescale-headline-small-tracking);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.mk-type-title-large {
|
|
73
|
+
font-family: var(--md-sys-typescale-title-large-font);
|
|
74
|
+
font-size: var(--md-sys-typescale-title-large-size);
|
|
75
|
+
line-height: var(--md-sys-typescale-title-large-line-height);
|
|
76
|
+
font-weight: var(--md-sys-typescale-title-large-weight);
|
|
77
|
+
letter-spacing: var(--md-sys-typescale-title-large-tracking);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.mk-type-title-medium {
|
|
81
|
+
font-family: var(--md-sys-typescale-title-medium-font);
|
|
82
|
+
font-size: var(--md-sys-typescale-title-medium-size);
|
|
83
|
+
line-height: var(--md-sys-typescale-title-medium-line-height);
|
|
84
|
+
font-weight: var(--md-sys-typescale-title-medium-weight);
|
|
85
|
+
letter-spacing: var(--md-sys-typescale-title-medium-tracking);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.mk-type-title-small {
|
|
89
|
+
font-family: var(--md-sys-typescale-title-small-font);
|
|
90
|
+
font-size: var(--md-sys-typescale-title-small-size);
|
|
91
|
+
line-height: var(--md-sys-typescale-title-small-line-height);
|
|
92
|
+
font-weight: var(--md-sys-typescale-title-small-weight);
|
|
93
|
+
letter-spacing: var(--md-sys-typescale-title-small-tracking);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.mk-type-body-large {
|
|
97
|
+
font-family: var(--md-sys-typescale-body-large-font);
|
|
98
|
+
font-size: var(--md-sys-typescale-body-large-size);
|
|
99
|
+
line-height: var(--md-sys-typescale-body-large-line-height);
|
|
100
|
+
font-weight: var(--md-sys-typescale-body-large-weight);
|
|
101
|
+
letter-spacing: var(--md-sys-typescale-body-large-tracking);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.mk-type-body-medium {
|
|
105
|
+
font-family: var(--md-sys-typescale-body-medium-font);
|
|
106
|
+
font-size: var(--md-sys-typescale-body-medium-size);
|
|
107
|
+
line-height: var(--md-sys-typescale-body-medium-line-height);
|
|
108
|
+
font-weight: var(--md-sys-typescale-body-medium-weight);
|
|
109
|
+
letter-spacing: var(--md-sys-typescale-body-medium-tracking);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.mk-type-body-small {
|
|
113
|
+
font-family: var(--md-sys-typescale-body-small-font);
|
|
114
|
+
font-size: var(--md-sys-typescale-body-small-size);
|
|
115
|
+
line-height: var(--md-sys-typescale-body-small-line-height);
|
|
116
|
+
font-weight: var(--md-sys-typescale-body-small-weight);
|
|
117
|
+
letter-spacing: var(--md-sys-typescale-body-small-tracking);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.mk-type-label-large {
|
|
121
|
+
font-family: var(--md-sys-typescale-label-large-font);
|
|
122
|
+
font-size: var(--md-sys-typescale-label-large-size);
|
|
123
|
+
line-height: var(--md-sys-typescale-label-large-line-height);
|
|
124
|
+
font-weight: var(--md-sys-typescale-label-large-weight);
|
|
125
|
+
letter-spacing: var(--md-sys-typescale-label-large-tracking);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.mk-type-label-medium {
|
|
129
|
+
font-family: var(--md-sys-typescale-label-medium-font);
|
|
130
|
+
font-size: var(--md-sys-typescale-label-medium-size);
|
|
131
|
+
line-height: var(--md-sys-typescale-label-medium-line-height);
|
|
132
|
+
font-weight: var(--md-sys-typescale-label-medium-weight);
|
|
133
|
+
letter-spacing: var(--md-sys-typescale-label-medium-tracking);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.mk-type-label-small {
|
|
137
|
+
font-family: var(--md-sys-typescale-label-small-font);
|
|
138
|
+
font-size: var(--md-sys-typescale-label-small-size);
|
|
139
|
+
line-height: var(--md-sys-typescale-label-small-line-height);
|
|
140
|
+
font-weight: var(--md-sys-typescale-label-small-weight);
|
|
141
|
+
letter-spacing: var(--md-sys-typescale-label-small-tracking);
|
|
142
|
+
}
|