{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "day",
  "type": "registry:ui",
  "title": "Day",
  "description": "The day as an object: a tear-off page with the date in dots, the month, weekday and clock in the pixel face, the minute filling along its foot; the year so far one dot a day, the days left and the moon; one line for the day. Tap the page to tear it off. And a small tile of the date.",
  "dependencies": [],
  "registryDependencies": [
    "https://metalui.dev/r/tokens.json"
  ],
  "files": [
    {
      "path": "packages/metalui/src/blocks/day/day.tsx",
      "type": "registry:ui",
      "target": "components/metalui/blocks/day/day.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport { Surface } from '../../components/surface/surface';\nimport { Well } from '../../components/well/well';\n\n/* ─────────────────────────────────────────────────────────\n * DAY: a custom block (an object). A tear-off page sunk into a raise slab.\n *   Day      the large widget: the date in dots at 3× (Saturday blue, Sunday amber), the month,\n *            the weekday and the clock in the pixel face down the side (the colon blinks each\n *            second), sixty small dots along the foot that fill with the minute; then the year so\n *            far (one dot a day, today pulsing), the days left and the moon, and one line for the day\n *   DayTile  the page alone: the date, the weekday and month, the days left and the moon\n * Tapping the large page tears it off: the number's dots fall away and the next day's settle in.\n * Reduced motion, or `animate={false}`, skips the tear to the next day and holds the blinks steady;\n * the seconds still count. Every ink is the day recipe's, per colorway.\n * ───────────────────────────────────────────────────────── */\n\nexport interface DayLine {\n  text: string;\n  /** Who said it, and where. */\n  by: string;\n}\n\n/** The default lines, one a day by the day of the year. */\nexport const DAY_LINES: DayLine[] = [\n  { text: 'How we spend our days is, of course, how we spend our lives.', by: 'Annie Dillard, The Writing Life' },\n  { text: 'Tell me, what is it you plan to do with your one wild and precious life?', by: 'Mary Oliver, The Summer Day' },\n  { text: 'The days are long, but the years are short.', by: 'Gretchen Rubin' },\n  { text: 'First make a plan, then carry it out.', by: 'Helmuth von Moltke' },\n  { text: 'What you do every day matters more than what you do once in a while.', by: 'Gretchen Rubin' },\n  { text: 'Well begun is half done.', by: 'Aristotle, Politics' },\n  { text: 'Nothing is worth more than this day.', by: 'Johann Wolfgang von Goethe' },\n];\n\nconst WEEKDAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];\nconst MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];\nconst MOON_PHASES = ['New moon', 'Waxing crescent', 'First quarter', 'Waxing gibbous', 'Full moon', 'Waning gibbous', 'Last quarter', 'Waning crescent'];\nconst FULL_MOON = Date.UTC(2026, 8, 26, 16, 49);\nconst SYNODIC = 29.530589;\nconst TEAR_FRAMES = 14;\nconst TEAR_MS = 55;\nconst BEAT_MS = 250;\nconst MS_PER_DAY = 86400000;\n\n// 5 × 7 digits, drawn in dots.\nconst DIGITS = [\n  ['.###.', '#...#', '#..##', '#.#.#', '##..#', '#...#', '.###.'],\n  ['..#..', '.##..', '..#..', '..#..', '..#..', '..#..', '.###.'],\n  ['.###.', '#...#', '....#', '...#.', '..#..', '.#...', '#####'],\n  ['#####', '...#.', '..#..', '...#.', '....#', '#...#', '.###.'],\n  ['...#.', '..##.', '.#.#.', '#..#.', '#####', '...#.', '...#.'],\n  ['#####', '#....', '####.', '....#', '....#', '#...#', '.###.'],\n  ['..##.', '.#...', '#....', '####.', '#...#', '#...#', '.###.'],\n  ['#####', '....#', '...#.', '..#..', '.#...', '.#...', '.#...'],\n  ['.###.', '#...#', '#...#', '.###.', '#...#', '#...#', '.###.'],\n  ['.###.', '#...#', '#...#', '.####', '....#', '...#.', '.##..'],\n];\n\nconst sq = (x: number, y: number, size: number, pitch: number, ox = 0, oy = 0) => {\n  const o = (pitch - size) / 2;\n  return `M${ox + x * pitch + o} ${oy + y * pitch + o}h${size}v${size}h-${size}Z`;\n};\nconst rank = (i: number) => {\n  const v = Math.sin(i * 12.9898 + 78.233) * 43758.5453;\n  return v - Math.floor(v);\n};\n\ninterface PageGeometry { cols: number; rows: number; scale: number; x0: number; y0: number; digitGap: number; ox: number; perfFrom: number }\nconst LARGE: PageGeometry = { cols: 41, rows: 23, scale: 3, x0: 2, y0: 2, digitGap: 2, ox: 2, perfFrom: 1 };\nconst SMALL: PageGeometry = { cols: 21, rows: 21, scale: 2, x0: 0, y0: 2, digitGap: 1, ox: 0, perfFrom: 0 };\n\n/** One frame of the page: perforations, the day's number, and (while tearing) the dots that fall. */\nfunction page(g: PageGeometry, num: number, shown: number, leaving: boolean) {\n  const cells = new Map<string, 'perf' | 'digit' | 'fall'>();\n  for (let x = g.perfFrom; x < g.cols; x += 2) cells.set(`${x},0`, 'perf');\n  const text = String(num).padStart(2, '0');\n  let idx = 0;\n  for (let d = 0; d < 2; d++) {\n    const glyph = DIGITS[+text[d]];\n    for (let gy = 0; gy < 7; gy++) for (let gx = 0; gx < 5; gx++) {\n      if (glyph[gy][gx] !== '#') continue;\n      for (let sy = 0; sy < g.scale; sy++) for (let sx = 0; sx < g.scale; sx++) {\n        const cx = g.x0 + d * (5 * g.scale + g.digitGap) + gx * g.scale + sx;\n        const cy = g.y0 + gy * g.scale + sy;\n        const r = rank(idx++);\n        if (r < shown) cells.set(`${cx},${cy}`, 'digit');\n        else if (leaving && r < shown + 0.14 && cy + 1 < g.rows && !cells.has(`${cx},${cy + 1}`)) cells.set(`${cx},${cy + 1}`, 'fall');\n      }\n    }\n  }\n  const out = { off: '', perf: '', digit: '', fall: '' };\n  for (let y = 0; y < g.rows; y++) for (let x = 0; x < g.cols; x++) out[cells.get(`${x},${y}`) ?? 'off'] += sq(x, y, 6, 8, g.ox);\n  return out;\n}\n\nfunction moon(date: Date) {\n  let age = ((date.getTime() - FULL_MOON) / MS_PER_DAY / SYNODIC + 0.5) % 1;\n  if (age < 0) age += 1;\n  const lit = (1 - Math.cos(age * 2 * Math.PI)) / 2;\n  const out = { lit: '', dark: '', name: MOON_PHASES[Math.round(age * 8) % 8] };\n  for (let y = 0; y < 7; y++) for (let x = 0; x < 7; x++) {\n    const dx = x - 3, dy = y - 3;\n    if (dx * dx + dy * dy > 10) continue;\n    const nx = dx / 3.2;\n    const on = age < 0.5 ? nx >= 1 - 2 * lit : nx <= -(1 - 2 * lit);\n    out[on ? 'lit' : 'dark'] += `M${x * 3 + 0.3} ${y * 3 + 0.3}h2.4v2.4h-2.4Z`;\n  }\n  return out;\n}\n\nfunction facts(date: Date) {\n  const y = date.getFullYear(), m = date.getMonth(), dom = date.getDate(), wd = date.getDay();\n  const start = new Date(y, 0, 1);\n  const doy = Math.round((+new Date(y, m, dom) - +start) / MS_PER_DAY) + 1;\n  const yearDays = Math.round((+new Date(y + 1, 0, 1) - +start) / MS_PER_DAY);\n  return {\n    y, m, dom, wd, doy, yearDays, left: yearDays - doy,\n    long: `${WEEKDAYS[wd]} ${dom} ${MONTHS[m]} ${y}`,\n    wdShort: WEEKDAYS[wd].slice(0, 3).toUpperCase(),\n    monShort: MONTHS[m].slice(0, 3).toUpperCase(),\n  };\n}\n\nconst DATE_INK = { weekday: 'fill-day-ink-date', saturday: 'fill-day-ink-saturday', sunday: 'fill-day-ink-sunday' };\nconst DATE_TEXT = { weekday: 'text-day-ink-date', saturday: 'text-day-ink-saturday', sunday: 'text-day-ink-sunday' };\nconst inkOf = (wd: number, weekend: boolean) => (!weekend ? 'weekday' : wd === 6 ? 'saturday' : wd === 0 ? 'sunday' : 'weekday') as keyof typeof DATE_INK;\n\n/** Now, on a beat; holds when `on` is false. */\nfunction useNow(on: boolean, beat: number) {\n  const [now, setNow] = React.useState(() => Date.now());\n  React.useEffect(() => {\n    if (!on || typeof window === 'undefined') return;\n    const id = window.setInterval(() => setNow(Date.now()), beat);\n    return () => window.clearInterval(id);\n  }, [on, beat]);\n  return now;\n}\n\nconst useReduced = () => {\n  const [reduced, setReduced] = React.useState(false);\n  React.useEffect(() => {\n    if (typeof window === 'undefined') return;\n    const q = window.matchMedia('(prefers-reduced-motion: reduce)');\n    const on = () => setReduced(q.matches);\n    on();\n    q.addEventListener('change', on);\n    return () => q.removeEventListener('change', on);\n  }, []);\n  return reduced;\n};\n\n/* ── the parts ──────────────────────────────────────────── */\n\ninterface DayState {\n  day: Date;\n  next: Date;\n  now: Date;\n  f: ReturnType<typeof facts>;\n  ink: keyof typeof DATE_INK;\n  pulse: boolean;\n  shown: number;\n  leaving: boolean;\n  tear: () => void;\n}\nconst DayContext = React.createContext<DayState | null>(null);\nconst useDay = (part: string) => {\n  const s = React.useContext(DayContext);\n  if (!s) throw new Error(`Day.${part} sits inside Day.Root`);\n  return s;\n};\n\nconst CARD = 'mu-day flex flex-col w-day-width h-day-height p-day-pad gap-day-gap';\nconst PAGE = 'mu-day-page overflow-hidden flex-none h-day-page-height rounded-day-page-radius';\nconst TEAR = 'mu-day-tear absolute top-day-page-tear-top left-day-page-tear-left w-day-page-tear-width h-day-page-tear-height m-0 p-0 border-0 bg-transparent cursor-pointer rounded-day-page-tear-radius focus-visible:focus-ring';\nconst SIDE = 'absolute top-day-page-side-top right-day-page-side-right w-day-page-side flex flex-col items-center';\nconst PIX = 'type-day-clock';\n\nexport interface DayRootProps extends React.HTMLAttributes<HTMLElement> {\n  /** The day to show first (default today). Tearing moves forward from it. */\n  date?: Date;\n  /** Saturday's date in blue and Sunday's in amber (default true). */\n  weekendInk?: boolean;\n  /** The tear, the blinks and the pulse (default true). Reduced motion turns them off regardless. */\n  animate?: boolean;\n  /** Called with the new day after the page is torn off. */\n  onTear?: (next: Date) => void;\n}\n\n/** The slab and the day it holds: the date, the clock and the tear, shared with every part inside. */\nconst Root = React.forwardRef<HTMLElement, DayRootProps>(function DayRoot(\n  { date, weekendInk = true, animate = true, onTear, className, children, ...props },\n  ref,\n) {\n  const reduced = useReduced();\n  const still = reduced || !animate;\n  const nowMs = useNow(true, BEAT_MS);\n  const [offset, setOffset] = React.useState(0);\n  const [frame, setFrame] = React.useState(-1);\n  const base = date ?? new Date(nowMs);\n  const day = new Date(base.getFullYear(), base.getMonth(), base.getDate() + offset);\n  const next = new Date(day.getFullYear(), day.getMonth(), day.getDate() + 1);\n  const f = facts(day);\n  const now = new Date(nowMs);\n\n  React.useEffect(() => {\n    if (frame < 0) return;\n    if (frame >= TEAR_FRAMES) { setFrame(-1); return; }\n    const id = window.setTimeout(() => {\n      if (frame + 1 === TEAR_FRAMES / 2) setOffset((o) => o + 1);\n      setFrame(frame + 1);\n    }, TEAR_MS);\n    return () => window.clearTimeout(id);\n  }, [frame]);\n\n  let shown = 1, leaving = false;\n  if (frame >= 0 && frame < TEAR_FRAMES / 2) { shown = 1 - (frame + 1) / (TEAR_FRAMES / 2); leaving = true; }\n  else if (frame >= TEAR_FRAMES / 2) shown = (frame - TEAR_FRAMES / 2 + 1) / (TEAR_FRAMES / 2);\n\n  const state: DayState = {\n    day, next, now, f, ink: inkOf(f.wd, weekendInk), pulse: still || now.getMilliseconds() < 500, shown, leaving,\n    tear: () => {\n      if (frame >= 0) return;\n      onTear?.(next);\n      if (still) setOffset((o) => o + 1);\n      else setFrame(0);\n    },\n  };\n  const pad = (n: number) => String(n).padStart(2, '0');\n  return (\n    <DayContext.Provider value={state}>\n      <Surface ref={ref} as=\"section\" material=\"raise\" radius=\"card\" aria-label={`${f.long}, ${pad(now.getHours())}:${pad(now.getMinutes())}`} className={className ? `${CARD} ${className}` : CARD} {...props}>\n        {children}\n      </Surface>\n    </DayContext.Provider>\n  );\n});\n\nexport interface DayPageProps extends Omit<React.HTMLAttributes<HTMLElement>, 'children'> {\n  /** The clock down the side (default true). */\n  clock?: boolean;\n  /** The sixty dots of the minute along the foot (default true). */\n  seconds?: boolean;\n}\n\n/** The page: the date in dots, the month and weekday (and the clock) down the side, the seconds along the foot. Tap it to tear. */\nfunction Page({ clock = true, seconds = true, className, ...props }: DayPageProps) {\n  const { f, now, next, ink, pulse, shown, leaving, tear } = useDay('Page');\n  const p = page(LARGE, f.dom, shown, leaving);\n  const pad = (n: number) => String(n).padStart(2, '0');\n  const sec = now.getSeconds();\n  const secs = { left: '', past: '', now: '' };\n  if (seconds) for (let i = 0; i < 60; i++) secs[i < sec ? 'past' : i === sec ? (pulse ? 'now' : 'past') : 'left'] += sq(i, 0, 3.5, 5.3, 7, 185);\n  return (\n    <Well variant=\"field\" className={className ? `${PAGE} ${className}` : PAGE} {...props}>\n      <svg aria-hidden shapeRendering=\"crispEdges\" viewBox=\"0 0 332 196\" className=\"absolute inset-0 block size-full\">\n        <path d={p.off} className=\"fill-day-ink-off\" />\n        <path d={p.perf} className=\"fill-day-ink-hole\" />\n        <path data-part=\"date\" d={p.digit} className={DATE_INK[ink]} />\n        <path d={p.fall} className={`${DATE_INK[ink]} opacity-day-ink-fall-opacity`} />\n        <path d={secs.left} className=\"fill-day-ink-left\" />\n        <path data-part=\"seconds\" d={secs.past} className=\"fill-ink2\" />\n        <path d={secs.now} className=\"fill-day-ink-sunday\" />\n      </svg>\n      <button type=\"button\" className={TEAR} aria-label={`${f.long}. Tear off this page to go to ${WEEKDAYS[next.getDay()]} ${next.getDate()} ${MONTHS[next.getMonth()]}.`} onClick={tear} />\n      <div aria-hidden className={SIDE}>\n        <span className={`${PIX} text-ink`}>{f.monShort}</span>\n        <span className={`${PIX} ${DATE_TEXT[ink]}`}>{f.wdShort}</span>\n        {clock && (\n          <>\n            <span data-part=\"hours\" className={`${PIX} text-ink mt-day-clock-gap`}>{pad(now.getHours())}</span>\n            <svg aria-hidden viewBox=\"0 0 12 12\" className={`block size-day-clock-colon my-day-clock-colon-gap ${pulse ? '' : 'opacity-day-clock-colon-dim'}`}>\n              <path d=\"M3 1h6v4H3ZM3 7h6v4H3Z\" className=\"fill-ink\" />\n            </svg>\n            <span data-part=\"minutes\" className={`${PIX} text-ink`}>{pad(now.getMinutes())}</span>\n          </>\n        )}\n      </div>\n    </Well>\n  );\n}\n\n/** The year so far: one dot a day (today pulses), the days left in the pixel face, the day's number and the moon. */\nfunction Year({ className, ...props }: Omit<React.HTMLAttributes<HTMLDivElement>, 'children'>) {\n  const { f, day, pulse } = useDay('Year');\n  const year = { left: '', past: '', today: '' };\n  for (let k = 0; k < f.yearDays; k++) year[k < f.doy - 1 ? 'past' : k === f.doy - 1 ? (pulse ? 'today' : 'past') : 'left'] += sq(k % 27, Math.floor(k / 27), 3.5, 5);\n  const own = 'flex items-center h-day-year-height gap-day-year-gap';\n  return (\n    <div className={className ? `${own} ${className}` : own} {...props}>\n      <svg role=\"img\" aria-label={`Day ${f.doy} of ${f.yearDays}: ${f.left} days left`} shapeRendering=\"crispEdges\" viewBox=\"0 0 135 70\" className=\"block flex-none w-day-year-width h-day-year-height\">\n        <path d={year.left} className=\"fill-day-ink-left\" />\n        <path d={year.past} className=\"fill-ink2\" />\n        <path data-part=\"today\" d={year.today} className=\"fill-day-ink-sunday\" />\n      </svg>\n      <div className=\"flex flex-col min-w-0 gap-day-text-gap\">\n        <div className=\"flex items-baseline gap-day-left-gap\">\n          <span className=\"type-day-left text-ink\">{f.left}</span>\n          <span className=\"type-lead text-ink\">days left</span>\n        </div>\n        <span className=\"type-lead text-ink2\">Day {f.doy} · {moon(day).name}</span>\n      </div>\n    </div>\n  );\n}\n\nexport interface DayLineProps extends Omit<React.HTMLAttributes<HTMLElement>, 'children'> {\n  /** The lines to choose from, one a day by the day of the year (default DAY_LINES). */\n  lines?: DayLine[];\n  /** One line, whatever the day. */\n  line?: DayLine;\n}\n\n/** One line for the day and who said it. */\nfunction Line({ lines = DAY_LINES, line, className, ...props }: DayLineProps) {\n  const { f } = useDay('Line');\n  const chosen = line ?? (lines.length ? lines[f.doy % lines.length] : null);\n  if (!chosen) return null;\n  const own = 'm-0 flex flex-col gap-day-text-gap';\n  return (\n    <figure className={className ? `${own} ${className}` : own} {...props}>\n      <blockquote className=\"m-0 type-title text-ink\">{chosen.text}</blockquote>\n      <figcaption className=\"type-meta text-ink2\">{chosen.by}</figcaption>\n    </figure>\n  );\n}\n\nexport interface DayProps extends Omit<DayRootProps, 'children'> {\n  lines?: DayLine[];\n}\n\n/** The large day widget: Day.Root › Day.Page › Day.Year › Day.Line. */\nconst DayWidget = React.forwardRef<HTMLElement, DayProps>(function Day({ lines, ...props }, ref) {\n  return (\n    <Root ref={ref} {...props}>\n      <Page />\n      <Year />\n      <Line lines={lines} />\n    </Root>\n  );\n});\n\nexport const Day = Object.assign(DayWidget, { Root, Page, Year, Line });\n\n/* ── the tile ───────────────────────────────────────────── */\n\nconst TILE = 'mu-day-tile size-day-tile-size p-day-tile-pad';\nconst TILE_PAGE = 'mu-day-page overflow-hidden h-day-tile-screen rounded-day-page-radius';\nconst TILE_FOOT = 'absolute inset-x-day-tile-inset bottom-day-tile-foot flex items-end justify-between';\n\nexport interface DayTileProps extends Omit<React.HTMLAttributes<HTMLElement>, 'children'> {\n  /** The day to show (default today, turning over at midnight). */\n  date?: Date;\n  /** Saturday's date in blue and Sunday's in amber (default true). */\n  weekendInk?: boolean;\n}\n\n/** A small day tile: the date in dots, the weekday and month, the days left and the moon. */\nexport const DayTile = React.forwardRef<HTMLElement, DayTileProps>(function DayTile({ date, weekendInk = true, className, ...props }, ref) {\n  const nowMs = useNow(!date, 60000);\n  const day = date ?? new Date(nowMs);\n  const f = facts(day);\n  const p = page(SMALL, f.dom, 1, false);\n  const ink = inkOf(f.wd, weekendInk);\n  const phase = moon(day);\n  return (\n    <Surface ref={ref} as=\"section\" material=\"raise\" radius=\"card\" role=\"img\" aria-label={`${f.long}, ${phase.name}`} className={className ? `${TILE} ${className}` : TILE} {...props}>\n      <Well variant=\"field\" className={TILE_PAGE}>\n        <svg aria-hidden shapeRendering=\"crispEdges\" viewBox=\"0 0 168 168\" className=\"absolute inset-0 block size-full\">\n          <path d={p.off} className=\"fill-day-ink-off\" />\n          <path d={p.perf} className=\"fill-day-ink-hole\" />\n          <path data-part=\"date\" d={p.digit} className={DATE_INK[ink]} />\n        </svg>\n        <div className={TILE_FOOT}>\n          <div className=\"flex flex-col gap-day-text-gap\">\n            <span className=\"type-label engraved\">{f.wdShort} · {f.monShort}</span>\n            <span className=\"type-readout text-ink2\">{f.left} days left</span>\n          </div>\n          <svg aria-hidden shapeRendering=\"crispEdges\" viewBox=\"0 0 21 21\" className=\"block size-day-tile-moon\">\n            <path d={phase.dark} className=\"fill-day-ink-off\" />\n            <path d={phase.lit} className=\"fill-day-ink-moon\" />\n          </svg>\n        </div>\n      </Well>\n    </Surface>\n  );\n});\n"
    }
  ],
  "docs": "Agent guide: https://metalui.dev/r/day.md. SwiftUI: MetalDay, MetalDayTile in the MetalUI Swift package."
}
