{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "published-date",
  "type": "registry:ui",
  "title": "Published Date",
  "description": "Article publish-date stamp. A pure surface primitive that renders a semantic `<time dateTime={iso}>` with a human-readable label produced by `Intl.DateTimeFormat` at render time. The native element owns the machine value (the `dateTime` attribute is what…",
  "author": "ofri-peretz <https://github.com/ofri-peretz>",
  "categories": [
    "blog",
    "primitive"
  ],
  "dependencies": [],
  "registryDependencies": [
    "https://ds.interlace.tools/r/theme.json",
    "https://ds.interlace.tools/r/cn.json",
    "https://ds.interlace.tools/r/skeleton.json"
  ],
  "files": [
    {
      "path": "registry/interlace-ui/published-date.tsx",
      "target": "components/ui/published-date.tsx",
      "type": "registry:ui",
      "content": "import * as React from 'react';\n\n// @interlace/published-date v1.1.0 — Interlace design system.\n// Docs, props and live preview: https://ds.interlace.tools/c/published-date\n// What changed since: https://ds.interlace.tools/c/published-date#history\n// Generated banner — keep it, the upgrade diff reads this version.\n\n/**\n * @interlace/ui — PublishedDate\n *\n * Article publish-date stamp. A pure surface primitive that renders a\n * semantic `<time dateTime={iso}>` with a human-readable label produced by\n * `Intl.DateTimeFormat` at render time. The native element owns the machine\n * value (the `dateTime` attribute is what RSS readers, search engines, and\n * assistive tech read); the formatted string is what humans read. No state,\n * no hooks — a server component.\n *\n * The `format` enum keeps the call-site declarative:\n *   - `long`  → `May 30, 2026`   (article header, list cards)\n *   - `short` → `5/30/26`        (tight metadata rows, footers)\n *\n * Locale follows the runtime default (`Intl.DateTimeFormat` with no `locale`\n * arg). Consumers that need a fixed locale pass `lang=` on an ancestor or\n * wrap with their own formatter — this primitive trusts the page's locale\n * contract rather than baking one in.\n *\n * ## Anatomy\n *\n *   <time data-slot=\"published-date\" data-min-viewport=\"320\" dateTime=\"2026-05-30\">\n *     May 30, 2026\n *   </time>\n *\n * ## MIN_VIEWPORT — 320\n *\n * Article metadata must render on every device that reads the post. A\n * narrow phone is the LCP-critical viewport for blog content; if the\n * publish stamp does not fit at 320px, the article header is broken.\n *\n * | Rule | Concept                          | Where in this file                                          |\n * | ---- | -------------------------------- | ----------------------------------------------------------- |\n * | R4   | Extends native el                | `React.ComponentProps<'time'> & PublishedDateProps`         |\n * | R6   | data-slot on root                | `data-slot=\"published-date\"`                                |\n * | R7   | className merged + ...rest       | `cn(BASE, className)` + `{...props}`                        |\n * | R8   | No isXxx; enums for variants     | `format` is an enum (`long` | `short`)                      |\n * | R14  | Declares min viewport            | `data-min-viewport={String(MIN_VIEWPORT)}` + exported const |\n * | R18  | Tailwind only                    | Zero inline `style`; utility classes only                   |\n * | R19  | Tokens only                      | `text-muted-foreground` + `text-sm` from semantic tokens    |\n * | R20  | AA contrast                      | `text-muted-foreground` clears AA on `--background`         |\n * | R25  | Server component                 | No hooks → no `'use client'`; Intl runs at render           |\n * | R26  | A11y from native el              | `<time dateTime=...>` is the canonical machine-readable date |\n */\n\nimport { cn } from '@/lib/utils';\nimport { Skeleton } from '@/components/ui/skeleton';\n\n/**\n * Minimum viable viewport (CSS px) for this primitive. Below it, the\n * preflight contract draws a dev-mode outline; in prod the component still\n * renders. Exported so consumers / tests can read it.\n */\nexport const MIN_VIEWPORT = 320 as const;\n\n/** Human-readable format. `long` is the article-header default; `short` is for tight rows. */\nexport type PublishedDateFormat = 'long' | 'short';\n\ntype PublishedDateProps = Omit<React.ComponentProps<'time'>, 'dateTime' | 'children'> & {\n  /**\n   * ISO 8601 timestamp (e.g. `'2026-05-30'` or `'2026-05-30T14:00:00Z'`).\n   * Becomes the `dateTime` attribute verbatim. Optional when\n   * `loading={true}` (the skeleton has no value to render).\n   */\n  dateIso?: string;\n  /** Display format. Defaults to `'long'`. */\n  format?: PublishedDateFormat;\n  /**\n   * When true, render a `<Skeleton variant=\"text\" />` (short width)\n   * placeholder. Shape-matched to the typical \"Month DD, YYYY\" footprint.\n   */\n  loading?: boolean;\n};\n\nconst LONG_OPTIONS: Intl.DateTimeFormatOptions = {\n  year: 'numeric',\n  month: 'long',\n  day: 'numeric',\n};\n\nconst SHORT_OPTIONS: Intl.DateTimeFormatOptions = {\n  year: '2-digit',\n  month: 'numeric',\n  day: 'numeric',\n};\n\nfunction formatPublishedDate(iso: string, format: PublishedDateFormat): string {\n  const date = new Date(iso);\n  const options = format === 'short' ? SHORT_OPTIONS : LONG_OPTIONS;\n  return new Intl.DateTimeFormat(undefined, options).format(date);\n}\n\n/** Article publish-date stamp. Server component (no hooks). */\nconst PublishedDate = React.forwardRef<HTMLTimeElement, PublishedDateProps>(\n  ({ className, dateIso, format = 'long', loading, ...props }, ref) => {\n    if (loading || !dateIso) {\n      return (\n        <Skeleton\n          variant=\"text\"\n          data-slot=\"published-date\"\n          data-min-viewport={String(MIN_VIEWPORT)}\n          className={cn('inline-block h-4 w-24', className)}\n        />\n      );\n    }\n    const label = formatPublishedDate(dateIso, format);\n    return (\n      <time\n        ref={ref}\n        data-slot=\"published-date\"\n        data-min-viewport={String(MIN_VIEWPORT)}\n        data-format={format}\n        dateTime={dateIso}\n        className={cn('text-muted-foreground text-sm', className)}\n        {...props}\n      >\n        {label}\n      </time>\n    );\n  },\n);\nPublishedDate.displayName = 'PublishedDate';\n\nexport { PublishedDate };\nexport type { PublishedDateProps };\n"
    }
  ],
  "meta": {
    "tier": "primitive",
    "client": false,
    "minViewport": 320,
    "loading": true,
    "version": "1.1.0",
    "since": "1.0.0"
  },
  "docs": "## @interlace/published-date\n\nInstalled to `components/ui/published-date.tsx`.\n\n```tsx\nimport { /* … */ } from '@/components/ui/published-date';\n```\n\nProps, a11y contract, live preview and source: https://ds.interlace.tools/c/published-date\n\nRequires the `@interlace/theme` CSS baseline (installed automatically as a registry dependency)."
}
