Design
llms.txt digitalcrew.tech

Motion

ContainerScroll

A hero block where a device-like frame starts tilted back in 3D and straightens as the page scrolls, with the title drifting up.

Crew OSMotionDigitalCrew.ContainerScroll
ContainerScroll · live · 960px wideOpen

Guidelines#

Use it for one product showcase per landing page: a screenshot, video or live mock of the workspace under a headline. Crew OS uses it for the scrolling product video.

What you provide: titleComponent (your heading and subtitle) and children (what shows on the screen; it fills the frame). Nothing else is configurable.

Anatomy: the outer block is 960px tall (h-[60rem]), 8px padding (80px from md), with perspective: 1000px. The frame is max-w-5xl, 480px tall (640px from md), rounded-[30px], a 4px #6C6C6C border, bg-[#222222], 8px padding (24px from md) and a six-layer drop shadow; it overlaps the title by 48px. The screen inside is rounded-2xl, bg-gray-100 (dark:bg-zinc-900), 16px padding from md.

Behaviour: progress is the block's position in the window scroll (Framer Motion useScroll). Across it the frame rotates on X from 20° to 0°, scales from 1.05 to 1 (0.7 to 0.9 at 768px wide and below) and the title moves up 100px. Only window scroll drives it, so it cannot live inside a scrolling panel; this preview makes the card itself scrollable. It does not check reduced motion.

Don't use it twice on a page, put interactive forms on the tilted screen, or rely on text inside the frame being readable before it flattens.

Facts#

GroupMotion
ProductCrew OS
Globalwindow.DigitalCrew.ContainerScroll
Sourcedigitalcrew-orchestrator: components/ui/container-scroll-animation.tsx
Import in the appimport { ContainerScroll } from "@/components/ui/container-scroll-animation";

API#

TypeScript declarations emitted from components/ui/container-scroll-animation.tsx.

export declare const ContainerScroll: ({ titleComponent, children, }: {
    titleComponent: string | React.ReactNode;
    children: React.ReactNode;
}) => any;
export declare const Header: ({ translate, titleComponent }: {
    translate: MotionValue<number>;
    titleComponent: React.ReactNode;
}) => any;
export declare const Card: ({ rotate, scale, children, }: {
    rotate: MotionValue<number>;
    scale: MotionValue<number>;
    translate: MotionValue<number>;
    children: React.ReactNode;
}) => any;

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 rows = [
    ["Claire Dubois", "CFO, Northwind Traders", "Meeting booked", "Tue 14:30"],
    ["Omar Haddad", "VP Finance, Brightline", "Replied", "18 min ago"],
    ["Lena Fischer", "Head of Sales, Kestrel Labs", "Opened 3×", "1 h ago"],
    ["Marc Lefèvre", "Founder, Atlas Freight", "Connected", "3 h ago"],
    ["Aiko Tanaka", "COO, Vantage Health", "In sequence", "Step 2 of 4"]
  ];
  function Screen() {
    return h("div", { className: "grid h-full content-start gap-4 bg-background p-4 text-foreground" },
      h("div", { className: "flex items-center justify-between" },
        h("div", null, h("p", { className: "text-lg font-semibold" }, "Good morning, Julie"), h("p", { className: "text-muted-foreground text-sm" }, "Max worked through 214 prospects overnight.")),
        h(DC.Button, null, h(I.Sparkles), "Ask Max")),
      h("div", { className: "grid grid-cols-3 gap-3" },
        [["Meetings booked", "11"], ["Reply rate", "8.9%"], ["Pipeline added", "€184k"]].map(function (k) {
          return h("div", { key: k[0], className: "rounded-xl border bg-card p-4" }, h("p", { className: "text-muted-foreground text-xs" }, k[0]), h("p", { className: "text-2xl font-semibold tabular-nums" }, k[1]));
        })),
      h("div", { className: "divide-y rounded-xl border bg-card" }, rows.map(function (r) {
        return h("div", { key: r[0], className: "flex items-center gap-3 px-4 py-3 text-sm" },
          h("span", { className: "bg-muted flex size-8 items-center justify-center rounded-full text-xs font-medium" }, r[0].split(" ").map(function (w) { return w[0]; }).join("")),
          h("span", { className: "flex-1" }, h("span", { className: "font-medium" }, r[0]), h("span", { className: "text-muted-foreground" }, " · " + r[1])),
          h(DC.Badge, { variant: "secondary" }, r[2]),
          h("span", { className: "text-muted-foreground w-24 text-right text-xs" }, r[3]));
      })));
  }
  function App() {
    return h("div", null,
      h(DC.ContainerScroll, { titleComponent: h("div", { className: "mb-8 text-center" },
        h("h2", { className: "mb-3 text-4xl font-bold" }, "Your sales floor, on autopilot"),
        h("p", { className: "text-muted-foreground mx-auto max-w-lg" }, "Digital Workers prospect, follow up and book meetings while you sleep.")) },
        h(Screen)),
      h("div", { className: "text-muted-foreground flex h-[560px] items-start justify-center text-sm" }, "Scroll back up to tilt the screen again."));
  }
  ReactDOM.createRoot(document.getElementById("root")).render(h(DC.TooltipProvider, { delayDuration: 80 }, h(App)));
})();
</script>