Custom CSS Properties
Animate any CSS property using custom key-value pairs and the advanced code field.
The from and to objects accept any animatable CSS property — not just named shortcuts like opacity or scale. Pass properties in camelCase (boxShadow) or as a quoted kebab-case string ('box-shadow'), and the SDK animates them directly.
Basic Usage
Add any CSS property directly to from or to:
import { Motion } from "@motion.page/sdk";
Motion("card-hover", ".card", {
to: {
boxShadow: "0 20px 40px rgba(0, 0, 0, 0.2)",
outline: "2px solid #6633EE",
outlineOffset: "4px",
},
duration: 0.3,
ease: "power2.out",
}).onHover({ each: true, onLeave: "reverse" }); Quoted kebab-case strings work identically — both forms map to the same CSS property:
Motion("shadow-in", ".card", {
to: { "box-shadow": "0 8px 24px rgba(0, 0, 0, 0.15)" },
duration: 0.3,
ease: "power2.out",
}).onHover({ each: true, onLeave: "reverse" }); CSS Custom Properties (Variables)
Animate CSS custom properties (CSS variables) using a quoted '--property-name' key:
Motion("palette-shift", ":root", {
from: { "--brand-hue": "220" },
to: { "--brand-hue": "300" },
duration: 1,
}).onScroll({ scrub: true }); @property --brand-hue {
syntax: "<number>";
initial-value: 220;
inherits: true;
}
.button {
background: hsl(var(--brand-hue), 70%, 55%);
} Registering the variable with @property tells the browser its syntax type, enabling smooth CSS-level transitions. Without registration, the SDK still sets the value, but the browser cannot interpolate it — simpler numeric-like strings (e.g. '220', '0.5') that the SDK can parse directly will still tween correctly regardless.
String vs Numeric Values
| Value | Behavior | Example |
|---|---|---|
| Number | The SDK appends px for length properties; unitless for others | borderRadius: 8 → 8px |
| String | Passed verbatim — required for units other than px or multi-value properties | borderRadius: '50%', boxShadow: '0 4px 12px #000' |
Always use a string for:
- Multi-value properties:
boxShadow,textShadow,backdropFilter,backgroundImage - Non-
pxunits:'1.5rem','50%','0.1em'
Always use a number for:
- Unitless properties:
opacity,zIndex - Properties where
pxis the correct unit and you don’t need to specify it:outlineOffset: 4
Commonly Used Custom Properties
Properties beyond the named SDK shortcuts that animate cleanly:
| Property | camelCase | Example Value |
|---|---|---|
| Box shadow | boxShadow | '0 8px 24px rgba(0,0,0,0.15)' |
| Text shadow | textShadow | '0 2px 8px rgba(0,0,0,0.4)' |
| Backdrop filter | backdropFilter | 'blur(12px)' |
| Outline | outline | '2px solid #6633EE' |
| Outline offset | outlineOffset | '4px' |
| Line height | lineHeight | '1.6' |
| Max width | maxWidth | '800px' |
| Border color | borderColor | 'rgba(102, 51, 238, 0.6)' |
| Border width | borderWidth | '2px' |
| Column gap | columnGap | '32px' |
| Object position | objectPosition | '50% 0%' |
| Padding (single side) | paddingTop | '24px' |
| CSS variable | '--my-var' | '220' |
Named shortcuts like
borderRadius,letterSpacing,fontSize,filter,clipPath,backgroundColor,color,width, andheightare already part ofAnimationVarsand work exactly the same way — no special handling needed.
Common Patterns
Box Shadow Lift on Hover
The classic card lift effect — start from no shadow and animate to a deep shadow on hover:
Motion("lift", ".card", {
to: { boxShadow: "0 16px 32px rgba(0, 0, 0, 0.18)" },
duration: 0.3,
ease: "power2.out",
}).onHover({ each: true, onLeave: "reverse" }); Because boxShadow: none (no shadow) is the element’s natural CSS state, omitting from lets the SDK read it automatically.
Frosted Glass Reveal
Animate a backdrop blur overlay into view on scroll:
Motion("glass", ".overlay", {
from: {
backdropFilter: "blur(0px)",
opacity: 0,
},
to: {
backdropFilter: "blur(16px)",
opacity: 1,
},
duration: 0.6,
ease: "power2.out",
}).onScroll({ scrub: false, toggleActions: "play none none none" }); Glowing Text Shadow on Scroll
Drive a text glow effect with scroll progress:
Motion("glow", ".headline", {
from: { textShadow: "0 0 0px rgba(102, 51, 238, 0)" },
to: { textShadow: "0 0 32px rgba(102, 51, 238, 0.8)" },
duration: 1,
}).onScroll({ scrub: true }); For shadow interpolation to tween smoothly, keep the same number of shadow layers in both from and to. Mismatched layer counts cause a hard swap at the midpoint.
CSS Variable Theme Shift
Drive a hue-based color scheme with scroll, affecting every element that references the variable:
Motion("hue-scroll", ":root", {
from: { "--accent-hue": "200" },
to: { "--accent-hue": "320" },
duration: 1,
}).onScroll({ scrub: true }); @property --accent-hue {
syntax: "<number>";
initial-value: 200;
inherits: true;
}
a, button, .badge {
color: hsl(var(--accent-hue), 80%, 60%);
} Multi-Property Transition (Timeline)
Chain custom properties across multiple animation steps:
Motion("reveal-card", ".card", [
{
target: ".card",
from: { opacity: 0, boxShadow: "0 0px 0px rgba(0,0,0,0)" },
to: { opacity: 1, boxShadow: "0 8px 24px rgba(0,0,0,0.12)" },
duration: 0.4,
ease: "power2.out",
},
{
target: ".card",
to: { borderRadius: "20px", outlineOffset: "6px" },
duration: 0.3,
ease: "power2.inOut",
},
]).onScroll({ scrub: false, toggleActions: "play none none none" }); In the Motion.page Builder
In the builder, the Custom Property field lets you add any animatable CSS property as a key-value pair. To remove a custom property in column view, right-click the property row and choose Remove from the context menu.
Click the code field icon to switch between the standard input and the Advanced Code Field. The Advanced Code Field accepts any valid key/value pair from the animation from/to object — useful for complex multi-value strings like boxShadow or backdropFilter that are difficult to express in a single-value input.
Notes
- Use camelCase (
boxShadow) or a quoted kebab-case string ('box-shadow') — both resolve to the same property. - For shadow properties (
boxShadow,textShadow), keep the number of shadow layers identical on both ends for smooth interpolation. Mismatched layers hard-swap at progress 0.5. - CSS custom properties (
'--my-var') with numeric string values (e.g.'220') tween correctly without@property. Use@propertyregistration when you need CSS-level fallback interpolation or when the value is not a simple number. - Not every CSS property can be animated — only properties the browser can interpolate (numeric, color, or parseable values). Properties like
display,position, orfont-familycannot be tweened. - Custom properties work alongside all named shortcuts in the same
from/toobject — mix freely.