ham7a311

Reactive Prose

Component

About

An editorial paragraph where the numbers themselves are the controls — drag, arrow-key, or type any underlined figure and every dependent figure in the sentence and chart recalculates and highlights.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Reactive Prose to your project and fits it to your theme.

Code

How Reactive Prose is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

"use client";
import {
  Out,
  ReactiveProse,
  Scrub,
  Trend,
} from "@/components/ui/reactive-prose";

const TODAY = Date.UTC(2026, 9, 5);
const month = new Intl.DateTimeFormat("en-US", {
  month: "long",
  year: "numeric",
  timeZone: "UTC",
});
const num = new Intl.NumberFormat("en-US");
const usd = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  maximumFractionDigits: 0,
});

// Weekly model: new sign-ups arrive, a share of readers leave each month.
function grow({
  readers,
  perWeek,
  churn,
  target,
  cost,
}: Record<string, number>) {
  const leave = churn / 100 / 4.345;
  const ceiling = leave > 0 ? perWeek / leave : Infinity;
  const series = [readers];
  let r = readers,
    weeks = 0;
  while (r < target && weeks < 520) {
    r += perWeek - r * leave;
    weeks++;
    if (weeks <= 104) series.push(Math.round(r));
  }
  const reached = r >= target;
  return {
    when:
      readers >= target
        ? "already"
        : reached
          ? month.format(new Date(TODAY + weeks * 7 * 86_400_000))
          : "never",
    weeks: reached
      ? `${num.format(weeks)} weeks from now`
      : `it levels off near ${num.format(Math.round(ceiling))}`,
    monthly: usd.format(target * cost),
    goal: target,
    series,
  };
}

export default function ReactiveProseDemo({
  variant = "light",
}: {
  variant?: string;
}) {
  const dark = variant === "dark";
  return (
    <div
      className={`min-h-full w-full px-4 py-10 ${dark ? "bg-[#0c0b08]" : "bg-[#e9e3d6]"}`}
    >
      <div className="mx-auto w-full max-w-[42rem]">
        <ReactiveProse
          initial={{
            readers: 120,
            perWeek: 40,
            churn: 2,
            target: 1000,
            cost: 0.04,
          }}
          compute={grow}
          theme={dark ? "dark" : "light"}
        >
          <h3>When does the newsletter pass a thousand?</h3>
          <p>
            Masar&apos;s field notes go out to{" "}
            <Scrub
              name="readers"
              label="readers today"
              min={0}
              max={5000}
              step={10}
              format={(v) => num.format(v)}
            />{" "}
            readers today. If{" "}
            <Scrub
              name="perWeek"
              label="new sign-ups per week"
              min={0}
              max={400}
              step={5}
            />{" "}
            people sign up each week and{" "}
            <Scrub
              name="churn"
              label="percent who unsubscribe each month"
              min={0}
              max={30}
              step={0.5}
              format={(v) => `${v}%`}
            />{" "}
            unsubscribe each month, the list passes{" "}
            <Scrub
              name="target"
              label="target readers"
              min={100}
              max={20000}
              step={100}
              format={(v) => num.format(v)}
            />{" "}
            readers{" "}
            <Out
              name="when"
              label="Passes the target"
              format={(v) =>
                v === "never"
                  ? "never"
                  : v === "already"
                    ? "already"
                    : `in ${v}`
              }
            />
            , <Out name="weeks" label="Timing" />.
          </p>
          <Trend
            name="series"
            target="goal"
            caption="Readers, week by week, for up to two years. The dashed line is the target."
          />
          <p>
            At{" "}
            <Scrub
              name="cost"
              label="sending cost per reader per month, in dollars"
              min={0}
              max={1}
              step={0.01}
              format={(v) => `$${v.toFixed(2)}`}
            />{" "}
            a reader, sending to that many costs about{" "}
            <Out name="monthly" label="Monthly cost" /> a month.
          </p>
        </ReactiveProse>
        <p
          className={`mt-4 px-1 font-sans text-[13px] ${dark ? "text-[#a69f90]" : "text-[#6a655c]"}`}
        >
          Drag any blue number sideways, or click it and type. Try unsubscribes
          above 4%.
        </p>
      </div>
    </div>
  );
}

More by ham7a311

View all
  • Route Globe

    Route Globe

    12
  • Wave Mesh

    Wave Mesh

    8
  • Uptime Ribbon

    Uptime Ribbon

    6
  • Glass Carousel

    Glass Carousel

    6
  • Glass Tiles

    Glass Tiles

    6
  • Streak Field Stats

    Streak Field Stats

    5
  • Wind Map

    Wind Map

    5
  • Time Window

    Time Window

    4