Motion
MotionScale
The shared motion scale made visible: the same distance travelled on each duration token and each easing, with a Replay button.
Guidelines#
Durations: --duration-stagger 40ms, -micro 80, -quick 150 (closes, tooltips), -fast 250 (opens), -medium 350, -slow 400 (panels), -very-slow 500 (emphasis). Easings: --ease-smooth-out cubic-bezier(0.22, 1, 0.36, 1) for anything entering or moving; --ease-bounce cubic-bezier(0.34, 1.36, 0.64, 1) for badges popping in; --ease-bounce-strong cubic-bezier(0.34, 3.85, 0.64, 1); --ease-linear for loops; agent surfaces and public-site reveals use cubic-bezier(0.16, 1, 0.3, 1).
Use the tokens, never literal values: transition: transform var(--duration-fast) var(--ease-smooth-out), or in Tailwind duration-(--acc-chevron) ease-(--acc-ease). Radix overlays animate with keyframes on scale and opacity. Under reduced motion every duration collapses to 0.01ms.
Facts#
| Group | Motion |
|---|---|
| Product | Crew OS and Max |
| Kind | Showcase page (tokens and patterns, not a single export) |
| Source | : app/transitions.css |
Example#
The code of the preview above: plain React.createElement against window.DigitalCrew, with realistic data.
<div id="root" class="p-6"></div>
<script>
(function () {
var h = React.createElement, DC = window.DigitalCrew, I = DC.Icons;
var DUR = [["--duration-stagger", 40], ["--duration-micro", 80], ["--duration-quick", 150], ["--duration-fast", 250], ["--duration-medium", 350], ["--duration-slow", 400], ["--duration-very-slow", 500]];
var EASE = [["--ease-smooth-out", "cubic-bezier(0.22, 1, 0.36, 1)", "opens, panels, moves"], ["--ease-bounce", "cubic-bezier(0.34, 1.36, 0.64, 1)", "badges popping in"], ["--ease-bounce-strong", "cubic-bezier(0.34, 3.85, 0.64, 1)", "bouncy hover-out"], ["--ease-linear", "linear", "shimmer, spinners"], ["agent surfaces", "cubic-bezier(0.16, 1, 0.3, 1)", "chat, Ask Max, reveals"]];
function Track(label, meta, style, on) {
return h("div", { className: "grid grid-cols-[180px_1fr_170px] items-center gap-3" },
h("code", { className: "font-mono text-xs" }, label),
h("div", { className: "relative h-5 rounded-full bg-muted" },
h("span", { className: "absolute top-0.5 size-4 rounded-full bg-primary", style: Object.assign({ left: on ? "calc(100% - 18px)" : "2px" }, style) })),
h("span", { className: "text-xs text-muted-foreground" }, meta));
}
function App() {
var s = React.useState(false), on = s[0];
React.useEffect(function () { var t = setTimeout(function () { s[1](true); }, 300); return function () { clearTimeout(t); }; }, []);
return h("div", { className: "grid gap-4" },
h("div", { className: "flex items-center justify-between" },
h("p", { className: "text-sm text-muted-foreground" }, "Same distance, different clocks. Reference the tokens: transition: transform var(--duration-fast) var(--ease-smooth-out)."),
h(DC.Button, { size: "sm", variant: "outline", onClick: function () { s[1](!on); } }, "Replay")),
h("div", { className: "grid gap-2" }, DUR.map(function (d) {
return h("div", { key: d[0] }, Track(d[0], d[1] + "ms · smooth-out", { transition: "left var(" + d[0] + ") var(--ease-smooth-out)" }, on));
})),
h("div", { className: "grid gap-2 pt-2" }, EASE.map(function (e) {
return h("div", { key: e[0] }, Track(e[0], e[2], { transition: "left 900ms " + e[1] }, on));
})));
}
ReactDOM.createRoot(document.getElementById("root")).render(h(DC.TooltipProvider, { delayDuration: 80 }, h(App)));
})();
</script>