UIInternal UI

App patterns

Not everything reusable belongs in the kit. These shapes were audited during the library build and deliberately left where they are — coupled to an app's router, store, or copy. They are documented here so the next builder finds them instead of reinventing them.

AnimatedNumber

Odometer number via NumberFlow. Auditioning for the kit — held out only because it would add @number-flow/react tointernal-ui dependencies. Renders live from the console source below.

apps/ui/src/components/patterns/animated-number.tsx (vendored from console)
$128,400
"use client";

// Vendored from apps/console/src/components/animated-number.tsx for the
// /patterns showcase (kept in sync by hand until internal-ui takes the
// `@number-flow/react` dependency and promotes it — see patterns page).

import NumberFlow, { type Format } from "@number-flow/react";
import { cn } from "@general-design-company/internal-ui/lib/utils";

/** Full-cents USD, the same output shape as `formatUsd`. */
export const USD: Format = {
  style: "currency",
  currency: "USD",
};

/** Whole-dollar USD for headline figures, matching `formatUsdCompact`. */
export const USD_COMPACT: Format = {
  style: "currency",
  currency: "USD",
  maximumFractionDigits: 0,
};

type AnimatedNumberProps = {
  /** The value to display. Changing this rolls each digit to the new value. */
  value: number;
  /** Intl format options for the rendered figure (e.g. `USD`, `USD_COMPACT`). */
  format?: Format;
  prefix?: string;
  suffix?: string;
  className?: string;
};

/**
 * A figure whose digits roll in place (odometer style) whenever the value
 * changes, via NumberFlow. NumberFlow honors `prefers-reduced-motion` on its
 * own, so a reduced-motion user sees the target value with no animation.
 */
export function AnimatedNumber({
  value,
  format,
  prefix,
  suffix,
  className,
}: AnimatedNumberProps) {
  return (
    <NumberFlow
      value={value}
      format={format}
      prefix={prefix}
      suffix={suffix}
      className={cn("tabular-nums", className)}
    />
  );
}

Kept app-specific

Audited, documented, staying put.

Console domain components apps/console/src/components/…

~195 components bound to tickets, ledgers, records, and agent loops. The kit's RecordLink provider seam is the one piece extracted so library controls never import the router.

AgentAvatar apps/console/src/components/data-view/agent-avatar.tsx

Coupled to the console's Phosphor icon catalog. Promote together with the catalog or not at all.

Marketing site chrome & configs apps/*/src/components/chrome/*-config.tsx, wordmark.tsx

Thin wiring over the kit's SiteHeader/Footer plus brand wordmarks. Configuration, not components.

Billing portal apps/main-street-ad-agency/src/portal/

Stripe/D1 checkout flows. Page logic, not a kit.

Console showcase wrappers removed with the /kit route

Card + row wrappers for the console's internal /kit page. This site's DemoCard is their public successor.

Promotion log

What moved into the kit for this site.

  • console → primitives: page-header, stat-tile, prompt-loading, highlighted-text, record-link, dialog-form, error-state (+ lib/friendly-error)
  • console → layout: glass-section
  • general-design-company → layout: dvd-bounce