{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "reading-time",
  "type": "registry:ui",
  "title": "Reading Time",
  "description": "A compact \"N min read\" badge for article headers, blog cards and docs index rows. It renders an inline `<span>`, so it sits beside a date or a tag row without imposing block layout.",
  "author": "ofri-peretz <https://github.com/ofri-peretz>",
  "categories": [
    "blog",
    "primitive"
  ],
  "dependencies": [
    "lucide-react"
  ],
  "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/reading-time.tsx",
      "target": "components/ui/reading-time.tsx",
      "type": "registry:ui",
      "content": "import * as React from 'react';\n\n// @interlace/reading-time v1.2.0 — Interlace design system.\n// Docs, props and live preview: https://ds.interlace.tools/c/reading-time\n// What changed since: https://ds.interlace.tools/c/reading-time#history\n// Generated banner — keep it, the upgrade diff reads this version.\n\n/**\n * @interlace/ui — ReadingTime\n *\n * A compact \"N min read\" badge for article headers, blog cards and docs index\n * rows. It renders an inline `<span>`, so it sits beside a date or a tag row\n * without imposing block layout.\n *\n * The number is repeated on `data-reading-time`, so analytics, scrapers and\n * JSON-LD builders never have to parse the rendered text.\n *\n * The label is a literal: `{minutes} min read`, English, hard-coded. There is\n * no children slot and no way to translate it from a call site — localisation\n * means a wrapper at a higher altitude, not a prop here. Nothing rounds or\n * validates `minutes` either; a `2.5` prints as \"2.5 min read\".\n *\n * Server primitive — no hooks, no client boundary.\n *\n * ## Anatomy\n *\n *   ReadingTime                        (span — data-min-viewport=320)\n *     ├─ {showIcon && <Clock />}       (optional 14px lucide outline, aria-hidden)\n *     └─ \"<N> min read\"                (text node)\n *\n * ## MIN_VIEWPORT — 320\n *\n * Article metadata is a content-surface fixture; it must read on every\n * device that renders text, including a 320 CSS-px iPhone SE. The badge\n * stays on one line because the label is short and the icon collapses\n * to `gap-1`.\n *\n * | Rule | Concept                          | Where in this file                                          |\n * | ---- | -------------------------------- | ----------------------------------------------------------- |\n * | R4   | Extends native el                | `React.ComponentProps<'span'> & ReadingTimeProps`           |\n * | R6   | data-slot on root                | `data-slot=\"reading-time\"`                                  |\n * | R7   | className merged + ...rest       | `cn(BASE, className)` + `{...props}`                        |\n * | R8   | No `isXxx`                       | `showIcon` is a presence flag, not a state machine          |\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` + `gap-1` token utilities |\n * | R20  | AA contrast                      | `--muted-foreground` clears AA on `--background`            |\n * | R25  | Server component                 | No hooks → no `'use client'`                                |\n * | R26  | A11y from native el              | `<span>` is inert text; icon marked `aria-hidden`           |\n */\n\nimport { Clock } from 'lucide-react';\n\nimport { cn } from '@/lib/utils';\nimport { Skeleton } from '@/components/ui/skeleton';\n\nexport const MIN_VIEWPORT = 320 as const;\n\ntype ReadingTimeProps = Omit<React.ComponentProps<'span'>, 'children'> & {\n  /**\n   * Estimated reading time in whole minutes. Rendered verbatim and emitted\n   * on `data-reading-time` for downstream consumers. Optional when\n   * `loading={true}` (no value to render).\n   */\n  minutes?: number;\n  /**\n   * When true, prefixes the label with a small `lucide-react` Clock icon.\n   * The icon is decorative (`aria-hidden`) — the text label carries the\n   * semantic.\n   */\n  showIcon?: boolean;\n  /**\n   * When true, render a `<Skeleton variant=\"text\" />` (short width)\n   * placeholder in place of the badge. Shape-matched to the typical\n   * \"<N> min read\" footprint so article-header metadata rows don't\n   * shift on data arrival.\n   */\n  loading?: boolean;\n};\n\nexport const ReadingTime = React.forwardRef<HTMLSpanElement, ReadingTimeProps>(\n  ({ className, minutes, showIcon, loading, ...props }, ref) => {\n    if (loading) {\n      return (\n        <Skeleton\n          variant=\"text\"\n          data-slot=\"reading-time\"\n          data-min-viewport={String(MIN_VIEWPORT)}\n          className={cn('inline-block h-4 w-20', className)}\n        />\n      );\n    }\n    return (\n      <span\n        ref={ref}\n        data-slot=\"reading-time\"\n        data-min-viewport={String(MIN_VIEWPORT)}\n        data-reading-time={minutes}\n        className={cn(\n          'inline-flex items-center gap-1 text-muted-foreground text-sm',\n          className,\n        )}\n        {...props}\n      >\n        {showIcon ? <Clock aria-hidden className=\"size-3.5\" /> : null}\n        {minutes} min read\n      </span>\n    );\n  },\n);\nReadingTime.displayName = 'ReadingTime';\n\nexport type { ReadingTimeProps };\n"
    }
  ],
  "meta": {
    "tier": "primitive",
    "client": false,
    "minViewport": 320,
    "loading": true,
    "version": "1.2.0",
    "since": "1.0.0"
  },
  "docs": "## @interlace/reading-time\n\nInstalled to `components/ui/reading-time.tsx`.\n\n```tsx\nimport { /* … */ } from '@/components/ui/reading-time';\n```\n\nProps, a11y contract, live preview and source: https://ds.interlace.tools/c/reading-time\n\nRequires the `@interlace/theme` CSS baseline (installed automatically as a registry dependency)."
}
