{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "appointment-card",
  "type": "registry:component",
  "title": "Appointment card",
  "description": "Groups supplied appointment time, timezone, clinician, location, and status with an optional action slot.",
  "registryDependencies": [
    "https://vlak.dev/r/vlak-base.json",
    "https://vlak.dev/r/inter.json",
    "https://vlak.dev/r/vlak-lib.json"
  ],
  "dependencies": [
    "@stylexjs/stylex"
  ],
  "devDependencies": [
    "@stylexjs/babel-plugin"
  ],
  "docs": "Vlak leaves are StyleX. Compile them with @stylexjs/babel-plugin (Vite: @stylexjs/unplugin, Next: @stylexjs/nextjs-plugin). If you would rather not run a compiler, import @noorddev/vlak-react instead: it ships precompiled with one stylesheet.",
  "files": [
    {
      "path": "vlak/appointment-card.tsx",
      "content": "import * as React from \"react\";\nimport * as stylex from \"@stylexjs/stylex\";\nimport { vlak, mq } from \"./tokens.stylex\";\nimport { rs } from \"./rs\";\n\nexport interface AppointmentCardProps extends React.HTMLAttributes<HTMLDivElement> {\n  appointmentTitle: string;\n  dateLabel: React.ReactNode;\n  timeLabel: React.ReactNode;\n  /** Supplied display timezone; the component performs no date conversion. */\n  timeZone: React.ReactNode;\n  dateTime?: string;\n  clinician?: React.ReactNode;\n  location?: React.ReactNode;\n  status: React.ReactNode;\n  /** Actual links or buttons whose actions are owned by the application. */\n  children?: React.ReactNode;\n}\n\nconst styles = stylex.create({\n  root: { boxSizing: \"border-box\", width: \"100%\", minWidth: 0, padding: \"1.25rem\", color: vlak.ink, backgroundColor: vlak.paper, borderWidth: vlak.hairline, borderStyle: \"solid\", borderColor: vlak.divider, display: \"flex\", flexDirection: \"column\", gap: \"1.25rem\", overflowWrap: \"anywhere\", lineHeight: 1.45 },\n  header: { display: \"flex\", flexWrap: \"wrap\", justifyContent: \"space-between\", alignItems: \"baseline\", gap: \"0.75rem\" },\n  title: { margin: 0, fontSize: \"1.125rem\", fontWeight: 600, lineHeight: 1.3, letterSpacing: \"-0.025em\" },\n  status: { fontSize: \"0.75rem\", borderWidth: vlak.hairline, borderStyle: \"solid\", borderColor: vlak.divider, padding: \"0.25rem 0.5rem\" },\n  when: { display: \"flex\", flexDirection: \"column\", gap: \"0.25rem\", fontVariantNumeric: \"tabular-nums\" },\n  date: { fontSize: \"1.375rem\", fontWeight: 500, letterSpacing: \"-0.025em\" },\n  time: { fontSize: \"0.875rem\", color: vlak.gray },\n  details: { margin: 0, display: \"grid\", gridTemplateColumns: { default: \"repeat(2, minmax(0, 1fr))\", [mq.phone]: \"minmax(0, 1fr)\" }, gap: \"1rem\" },\n  field: { minWidth: 0 },\n  label: { margin: 0, color: vlak.gray, fontSize: \"0.75rem\" },\n  value: { margin: 0, marginTop: \"0.25rem\", fontSize: \"0.875rem\" },\n  actions: { display: \"flex\", flexWrap: \"wrap\", gap: \"0.5rem\", borderTopWidth: vlak.hairline, borderTopStyle: \"solid\", borderTopColor: vlak.divider, paddingTop: \"1rem\" },\n});\n\n/** Appointment details exactly as supplied, with an application-owned action slot. */\nexport const AppointmentCard = React.forwardRef<HTMLDivElement, AppointmentCardProps>(function AppointmentCard(\n  { appointmentTitle, dateLabel, timeLabel, timeZone, dateTime, clinician, location, status, children, className, style, ...props }, ref,\n) {\n  const root = rs([\"rs-appointment-card\", className], styles.root);\n  const header = rs([\"rs-appointment-card-header\"], styles.header);\n  const title = rs([\"rs-appointment-card-title\"], styles.title);\n  const statusStyle = rs([\"rs-appointment-card-status\"], styles.status);\n  const when = rs([\"rs-appointment-card-when\"], styles.when);\n  const date = rs([\"rs-appointment-card-date\"], styles.date);\n  const time = rs([\"rs-appointment-card-time\"], styles.time);\n  const details = rs([\"rs-appointment-card-details\"], styles.details);\n  const field = rs([\"rs-appointment-card-field\"], styles.field);\n  const label = rs([\"rs-appointment-card-label\"], styles.label);\n  const value = rs([\"rs-appointment-card-value\"], styles.value);\n  const actions = rs([\"rs-appointment-card-actions\"], styles.actions);\n  return <div ref={ref} role=\"group\" aria-label={appointmentTitle} {...props} className={root.className} style={{ ...root.style, ...style }}>\n    <div {...header}><p {...title}>{appointmentTitle}</p><span {...statusStyle}>{status}</span></div>\n    <div {...when}>\n      {dateTime ? <time {...date} dateTime={dateTime}>{dateLabel}</time> : <span {...date}>{dateLabel}</span>}\n      <span {...time}>{timeLabel} · {timeZone}</span>\n    </div>\n    <dl {...details}>\n      <div {...field}><dt {...label}>Clinician</dt><dd {...value}>{clinician ?? \"Clinician not supplied\"}</dd></div>\n      <div {...field}><dt {...label}>Location</dt><dd {...value}>{location ?? \"Location not supplied\"}</dd></div>\n    </dl>\n    {children != null && <div {...actions}>{children}</div>}\n  </div>;\n});\n",
      "type": "registry:component",
      "target": "components/vlak/appointment-card.tsx"
    },
    {
      "path": "vlak/styles/appointment-card.css",
      "content": "/* ── appointment-card: generated from packages/react/src/components/appointment-card.tsx ── */\n.rs-appointment-card{box-sizing:border-box;width:100%;min-width:0;padding:1.25rem;color:var(--text);background-color:var(--bg);border-width:1px;border-style:solid;border-color:var(--divider);display:flex;flex-direction:column;gap:1.25rem;overflow-wrap:anywhere;line-height:1.45}\n.rs-appointment-card-header{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:baseline;gap:0.75rem}\n.rs-appointment-card-title{margin:0;font-size:1.125rem;font-weight:600;line-height:1.3;letter-spacing:-0.025em}\n.rs-appointment-card-status{font-size:0.75rem;border-width:1px;border-style:solid;border-color:var(--divider);padding:0.25rem 0.5rem}\n.rs-appointment-card-when{display:flex;flex-direction:column;gap:0.25rem;font-variant-numeric:tabular-nums}\n.rs-appointment-card-date{font-size:1.375rem;font-weight:500;letter-spacing:-0.025em}\n.rs-appointment-card-time{font-size:0.875rem;color:var(--text-secondary)}\n.rs-appointment-card-details{margin:0;display:grid;grid-template-columns:repeat(2, minmax(0, 1fr));gap:1rem}\n@media (max-width: 640px){.rs-appointment-card-details{grid-template-columns:minmax(0, 1fr)}}\n.rs-appointment-card-field{min-width:0}\n.rs-appointment-card-label{margin:0;color:var(--text-secondary);font-size:0.75rem}\n.rs-appointment-card-value{margin:0;margin-top:0.25rem;font-size:0.875rem}\n.rs-appointment-card-actions{display:flex;flex-wrap:wrap;gap:0.5rem;border-top-width:1px;border-top-style:solid;border-top-color:var(--divider);padding-top:1rem}\n",
      "type": "registry:file",
      "target": "styles/vlak/appointment-card.css"
    }
  ],
  "meta": {
    "vlak": {
      "category": "health",
      "classes": [
        "rs-appointment-card",
        "rs-appointment-card-header",
        "rs-appointment-card-title",
        "rs-appointment-card-status",
        "rs-appointment-card-when",
        "rs-appointment-card-date",
        "rs-appointment-card-time",
        "rs-appointment-card-details",
        "rs-appointment-card-field",
        "rs-appointment-card-label",
        "rs-appointment-card-value",
        "rs-appointment-card-actions"
      ],
      "snippet": "<div class=\"rs-appointment-card\" role=\"group\" aria-label=\"Care team check-in\"><div class=\"rs-appointment-card-header\"><p class=\"rs-appointment-card-title\">Care team check-in</p><span class=\"rs-appointment-card-status\">Confirmed</span></div><div class=\"rs-appointment-card-when\"><span class=\"rs-appointment-card-date\">18 September 2026</span><span class=\"rs-appointment-card-time\">10:30–11:00 · Europe/Amsterdam, UTC+02:00</span></div><dl class=\"rs-appointment-card-details\"><div class=\"rs-appointment-card-field\"><dt class=\"rs-appointment-card-label\">Clinician</dt><dd class=\"rs-appointment-card-value\">Alex Morgan</dd></div><div class=\"rs-appointment-card-field\"><dt class=\"rs-appointment-card-label\">Location</dt><dd class=\"rs-appointment-card-value\">Room 04, demo clinic</dd></div></dl></div>",
      "cssOnly": false,
      "registryDependencies": [
        "vlak-lib"
      ],
      "aliases": [
        "AppointmentCard",
        "Visit card",
        "Booking card",
        "Care appointment",
        "Consultation details"
      ],
      "example": "import { AppointmentCard } from \"@noorddev/vlak-react\";\n\n<AppointmentCard appointmentTitle=\"Care team check-in\" dateLabel=\"18 September 2026\" timeLabel=\"10:30–11:00\" timeZone=\"Europe/Amsterdam, UTC+02:00\" dateTime=\"2026-09-18T10:30:00+02:00\" clinician=\"Alex Morgan\" location=\"Room 04, demo clinic\" status=\"Confirmed\" />\n\n// Supply children for real navigation links or application-owned actions.",
      "usage": {
        "use": [
          "Confirmed, proposed, cancelled, or otherwise explicitly supplied appointments.",
          "Add real links or buttons as children when navigation or an action is available."
        ],
        "avoid": [
          "Inferring confirmation, converting timezones, or deriving a relative date from the system clock.",
          "Rendering placeholder Join, Cancel, or Reschedule buttons without working actions."
        ]
      },
      "keyboard": [
        {
          "keys": "Tab, Enter, Space",
          "does": "Supplied links and buttons retain their native keyboard interactions. The card adds no tab stop."
        }
      ],
      "a11y": [
        "Appointment title names a group. Date, time, timezone, and status are text; clinician and location use a description list.",
        "Missing clinician and location are explicitly labelled as not supplied.",
        "Use native action controls with accessible names and at least 44px targets in the children slot.",
        "Native root attributes and the div ref pass through. The caller owns status updates and announcements."
      ]
    }
  }
}
