# FocusPull

Blur and dim a background layer to direct attention to a foreground result.

Page: https://20cuts.com/docs/components/focus-pull

## Installation

Run from your product repository:

```sh
npx -y https://20cuts.com/releases/v0.3.0/20cuts-create-0.3.0.tgz video/20cuts
cd video/20cuts
npm ci
npm run studio
```

The installer includes this component, its source dependencies, runnable examples,
and skills for Codex and Claude Code. If 20cuts is already installed, import the
component from its existing source directory.

## Usage

```tsx
import { FocusPull } from "./system/motion";

export default function VideoScene() {
  return (
    <FocusPull at={1.5} duration={0.7} blur={8} dim={0.4}><div style={{fontSize: 100}}>Your product screen</div></FocusPull>
  );
}
```


## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| children (required) | ReactNode | None | React content to display. |
| at | number | 0 | Start time in seconds within the current Remotion Sequence. |
| duration | number | 0.7 | Animation duration in seconds. |
| direction | "out" \| "in" | "out" | Out adds blur and dimming; in removes them. |
| blur | number | 6 | Maximum blur radius in pixels. |
| dim | number | 0.45 | Opacity at full defocus, from 0 to 1. |
| style | CSSProperties | None | Styles applied to the component wrapper. |

Place foreground content outside this wrapper to keep it sharp.

## Complete example

This is the source of the fictional component demonstration. Replace its content
with supported behavior and assets from your product.

```tsx
import React, { type CSSProperties, type ReactNode } from "react";

import { AbsoluteFill } from "remotion";

import { AppFrame, FocusPull, ScaleIn } from "./system";

const ink = "#141414", lime = "#b6f36b";

const center: CSSProperties = { display: "flex", alignItems: "center", justifyContent: "center" };

const card: CSSProperties = { background: "#fff", border: "1px solid #ddd", borderRadius: 20, padding: 32 };

function Stage({ children, dark = false, style }: {
    children: ReactNode;
    dark?: boolean;
    style?: CSSProperties;
}) {
    return <AbsoluteFill style={{ fontFamily: "Arial, Helvetica, sans-serif", color: dark ? "#fff" : ink, background: dark ? ink : "#f7f7f5", ...center, ...style }}>{children}</AbsoluteFill>;
}

function Dashboard({ compact = false, emphasizeMetric = false }: {
    compact?: boolean;
    emphasizeMetric?: boolean;
}) {
    return <div style={{ padding: compact ? 24 : 36, display: "grid", gap: 26, color: ink }}>
    <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}><strong style={{ fontSize: 30, letterSpacing: "-0.035em" }}>Project overview</strong><span style={{ background: lime, padding: "10px 18px", borderRadius: 8, fontSize: 16 }}>New project +</span></div>
    <div style={{ display: "grid", gridTemplateColumns: "repeat(3,1fr)", gap: 16 }}>{[["In progress", "08"], ["Completed", "24"], ["This week", "+12%"]].map(([label, value], i) => <div key={label} style={{ ...card, padding: 20, background: "#fafafa", outline: emphasizeMetric && i === 2 ? `4px solid ${lime}` : undefined }}><div style={{ fontSize: 16, color: "#666" }}>{label}</div><div style={{ fontSize: 42, marginTop: 12, letterSpacing: "-0.05em" }}>{value}</div></div>)}</div>
    <div style={{ display: "flex", height: compact ? 110 : 150, alignItems: "flex-end", gap: 14, borderBottom: "1px solid #ddd" }}>{[24, 42, 35, 68, 54, 78, 96, 74, 84, 100].map((n, i) => <div key={i} style={{ flex: 1, height: `${n}%`, background: i === 9 ? lime : "#e4e5e0", borderRadius: "8px 8px 0 0" }}/>)}</div>
  </div>;
}

export function FocusPullExample() { return <Stage><FocusPull at={1.4} duration={0.8} blur={8} dim={0.35}><AppFrame width={980} height={550}><Dashboard /></AppFrame></FocusPull><ScaleIn at={1.8} duration={0.8} style={{ position: "absolute" }}><div style={{ ...card, padding: "38px 52px", fontSize: 52, boxShadow: "0 20px 80px #0002", letterSpacing: "-0.04em" }}>All clear. <span style={{ color: "#62972d" }}>✓</span></div></ScaleIn></Stage>; }
```

Save it as src/FocusPullExample.tsx. This demonstration uses
1280×720, 30fps and 180 frames.
For a separate entry point, replace src/index.tsx with this registration, then
run npm run studio:

```tsx
import { Composition, registerRoot } from "remotion";
import { FocusPullExample } from "./FocusPullExample";

registerRoot(() => (
  <Composition
    id="FocusPullExample"
    component={FocusPullExample}
    width={1280}
    height={720}
    fps={30}
    durationInFrames={180}
  />
));
```

## Source

- https://20cuts.com/animation-system/library/source/motion/direction.tsx
- https://20cuts.com/animation-system/library/source/motion/core.ts

## Agent instructions

Use the 20cuts FocusPull component in this project's Remotion composition. Defocus the product background when a result callout appears; keep the callout outside the FocusPull wrapper. Keep motion frame-driven and put timing in seconds relative to the current Sequence. Use the product's actual copy and assets. Preview the composition before rendering.
