Custom Properties

Animate any CSS property using the Custom Properties panel in the builder.

Custom Properties lets you animate any CSS property that isn’t covered by the built-in property panels — from border-radius and letter-spacing to box-shadow and padding.

Location

Left Panel → Animation tab → Visual Properties → Custom

Enable Custom Properties

Click the toggle next to Custom to enable it. The row expands to show a two-column input with a Property field and a Value field.

Custom Properties expanded — key/value rows with property and value inputs

Controls

ControlDescription
Property inputThe CSS property name. Accepts camelCase (borderRadius) or kebab-case (border-radius).
Value inputThe target value for the property. Accepts any valid CSS value string or number.
Add row button (+)Appends a new empty Property/Value row. Use this to animate multiple custom properties in one animation step.
Code field toggleSwitches the panel between Key/Value mode and Advanced Code Field mode. The icon is a </> code symbol.
Right-click context menuRight-click any row in column view to Duplicate, Copy, Paste, or Delete row. The last remaining row cannot be deleted.

Key/Value mode (default)

Each row has two inputs side by side:

  • Property — the CSS property you want to animate, e.g. border-radius, letter-spacing, padding-top
  • Value — the CSS value to animate to (or from), e.g. 50%, 0.2em, 24px

Use camelCase or kebab-case for the property name — both resolve correctly. When you enter a value that contains a cubic-bezier() expression or a four-number easing string, the builder normalises the decimal format automatically.

Click the + button in the panel header to append additional rows. Each row animates a separate CSS property within the same tween.

Advanced Code Field mode

Click the code field icon (the </> button) to switch to Advanced Code Field mode. This replaces the row inputs with a freeform code editor that accepts any valid from/to object key-value pairs.

Use the Advanced Code Field when:

  • You need to enter complex multi-value strings like box-shadow or backdrop-filter
  • You want to paste properties copied from existing SDK code
  • You need to enter properties with special formatting that the row inputs make awkward

Click the code field icon again to toggle back to Key/Value mode.

From mode — animate from a custom value

Switch to the From tab, enable Custom, and add property/value pairs. The element starts at those values and animates to its natural CSS state.

Example: set border-radius to 0 on the From tab. If the element’s CSS has border-radius: 50%, it starts as a sharp square and morphs into a circle.

To mode — animate to a custom value

Switch to the To tab, enable Custom, and set the target property/value. The element starts at its natural CSS state and animates to the specified value.

Example: set border-radius to 50% on the To tab. The element morphs from whatever its CSS defines into a circle.

The box starts with border-radius: 0 (the From value) and animates to border-radius: 50% — a full circle.

SDK equivalent

Custom Properties map directly to any key-value pair in the from and to objects of the SDK.

Border-radius morph (From mode):

typescript
import { Motion } from '@motion.page/sdk';

Motion('circle-in', '.avatar', {
  from: { borderRadius: '0%' },
  to:   { borderRadius: '50%' },
  duration: 0.8,
  ease: 'power2.out',
}).onPageLoad();

Letter-spacing expand (To mode):

typescript
Motion('text-expand', '.headline', {
  to: { letterSpacing: '0.15em' },
  duration: 0.6,
  ease: 'power2.out',
}).onScroll({ scrub: true });

Multiple custom properties in one tween:

typescript
Motion('card-reveal', '.card', {
  from: {
    borderRadius: '4px',
    boxShadow: '0 0px 0px rgba(0,0,0,0)',
  },
  to: {
    borderRadius: '20px',
    boxShadow: '0 16px 40px rgba(0,0,0,0.18)',
  },
  duration: 0.5,
  ease: 'power2.out',
}).onHover({ each: true, onLeave: 'reverse' });

For the full SDK reference, see Custom Properties in the SDK.

Common patterns

Border-radius morph

Animate a sharp element into a pill or circle shape:

typescript
Motion('pill-in', '.button', {
  from: { borderRadius: '4px' },
  to:   { borderRadius: '999px' },
  duration: 0.5,
  ease: 'power2.inOut',
}).onHover({ each: true, onLeave: 'reverse' });

Letter-spacing expand on scroll

Widen letter-spacing as the element enters the viewport — a common typographic reveal:

typescript
Motion('type-reveal', '.tagline', {
  from: { letterSpacing: '-0.05em', opacity: 0 },
  duration: 0.9,
  ease: 'power3.out',
}).onScroll();

Padding push on hover

Animate padding to give a button a “breath” effect on hover:

typescript
Motion('btn-breathe', '.cta', {
  to: { paddingLeft: '32px', paddingRight: '32px' },
  duration: 0.25,
  ease: 'power1.out',
}).onHover({ each: true, onLeave: 'reverse' });

Box-shadow lift

Elevate a card with a growing shadow on scroll entry:

typescript
Motion('card-lift', '.card', {
  from: { boxShadow: '0 0px 0px rgba(0,0,0,0)' },
  to:   { boxShadow: '0 16px 40px rgba(0,0,0,0.15)' },
  duration: 0.6,
  ease: 'power2.out',
}).onScroll({ toggleActions: 'play none none reverse' });

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.

  • From, To & Set — How the three animation modes work and when to use each
  • Custom Properties in the SDK — SDK reference for custom key-value animation pairs
  • Filter — Animate blur, brightness, contrast, and other CSS filter functions
  • Opacity — Animate element transparency with a dedicated slider