{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"section","type":"registry:ui","title":"Section","description":"One part of a landing page: the room around it, an eyebrow, title, description and buttons, and its content in a Container.","dependencies":[],"registryDependencies":["https://dashral.com/r/utils.json","https://dashral.com/r/container.json"],"files":[{"path":"registry/dashral/section.tsx","type":"registry:file","target":"components/dashral/section.tsx","content":"\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"./lib/utils\"\nimport { Container, type ContainerProps } from \"./container\"\n\ntype SectionProps = Omit<React.ComponentProps<\"section\">, \"title\"> & {\n  /** A short line above the title: what the part is about (\"Pricing\", \"Why teams switch\"). */\n  eyebrow?: React.ReactNode\n  /** The part's heading. It names the section for screen readers too. */\n  title?: React.ReactNode\n  /** A sentence or two under the title. */\n  description?: React.ReactNode\n  /** Under the description: the part's buttons. */\n  actions?: React.ReactNode\n  /** The heading's alignment: centred over the content, or at its start. */\n  align?: \"center\" | \"start\"\n  /** The heading's level: h2 (default) for a part of the page, h1 for the first one. */\n  titleAs?: \"h1\" | \"h2\"\n  /** The content's width, as Container's sizes. */\n  size?: ContainerProps[\"size\"]\n  /** The room above and below: sm (48px, 64px from sm up), default (64px, 96px), lg (96px, 128px). */\n  spacing?: \"sm\" | \"default\" | \"lg\"\n  /** muted lays a faint band behind it, to set a part apart from its neighbours. */\n  tone?: \"default\" | \"muted\"\n  /** The heading rises into place the first time it scrolls into view. On by default; with reduced motion it's still. */\n  reveal?: boolean\n  classNames?: { container?: string; header?: string; eyebrow?: string; title?: string; description?: string; actions?: string; content?: string }\n}\n\nconst spacings = { sm: \"py-12 sm:py-16\", default: \"py-16 sm:py-24\", lg: \"py-24 sm:py-32\" }\n\n// The heading's hidden state, only while scripts run and motion is welcome: without either it's simply there. Keyed on\n// the heading's data-reveal, so the server's HTML is already hidden and nothing flashes before it plays.\nconst hidden =\n  \"[@media(scripting:enabled)]:motion-safe:group-data-[reveal=pending]/section:translate-y-3 [@media(scripting:enabled)]:motion-safe:group-data-[reveal=pending]/section:opacity-0\"\nconst arrive = \"transition-[opacity,translate] duration-(--duration-considered) ease-enter motion-reduce:transition-none\"\n\n/**\n * One part of a landing page, in one tag: `<Section eyebrow=\"Pricing\" title=\"Simple plans\" description=\"…\">…</Section>`.\n * The room above and below, a heading (an eyebrow, the title, a description and buttons) centred or at the start, and\n * the content under it in a Container. The section is named by its title, and the heading rises into place the first\n * time it scrolls into view, a beat between its lines.\n */\nfunction Section({\n  eyebrow,\n  title,\n  description,\n  actions,\n  align = \"center\",\n  titleAs: Title = \"h2\",\n  size = \"lg\",\n  spacing = \"default\",\n  tone = \"default\",\n  reveal = true,\n  id,\n  className,\n  classNames,\n  children,\n  ...props\n}: SectionProps) {\n  const generatedId = React.useId()\n  const titleId = `${id ?? generatedId}-title`\n  const centered = align === \"center\"\n  const hasHeader = Boolean(eyebrow || title || description || actions)\n  const header = React.useRef<HTMLDivElement>(null)\n  const [shown, setShown] = React.useState(!reveal)\n\n  React.useEffect(() => {\n    const node = header.current\n    if (!reveal || !node) return\n    // Settle the hidden state first, so the switch to shown has something to transition from.\n    void node.getBoundingClientRect()\n    const observer = new IntersectionObserver(\n      ([entry]) => {\n        if (!entry?.isIntersecting) return\n        setShown(true)\n        observer.disconnect()\n      },\n      { rootMargin: \"0px 0px -12% 0px\" }\n    )\n    observer.observe(node)\n    return () => observer.disconnect()\n  }, [reveal])\n\n  // Each line of the heading arrives a beat after the one before.\n  let line = 0\n  const next = () => ({ transitionDelay: `${line++ * 70}ms` })\n\n  return (\n    <section\n      data-slot=\"section\"\n      id={id}\n      aria-labelledby={title ? titleId : undefined}\n      className={cn(spacings[spacing], tone === \"muted\" && \"bg-muted/50\", className)}\n      {...props}\n    >\n      <Container size={size} className={classNames?.container}>\n        {hasHeader && (\n          <div\n            ref={header}\n            data-reveal={shown ? \"shown\" : \"pending\"}\n            className={cn(\"group/section max-w-2xl\", centered && \"mx-auto text-center\", classNames?.header)}\n          >\n            {eyebrow && (\n              <p className={cn(\"text-sm font-medium text-primary-text\", arrive, hidden, classNames?.eyebrow)} style={next()}>\n                {eyebrow}\n              </p>\n            )}\n            {title && (\n              <Title\n                id={titleId}\n                className={cn(\"text-3xl font-semibold tracking-[-0.03em] text-balance sm:text-4xl\", eyebrow && \"mt-3\", arrive, hidden, classNames?.title)}\n                style={next()}\n              >\n                {title}\n              </Title>\n            )}\n            {description && (\n              <p\n                className={cn(\"text-base leading-[1.6] text-pretty text-muted-foreground sm:text-lg\", (eyebrow || title) && \"mt-4\", arrive, hidden, classNames?.description)}\n                style={next()}\n              >\n                {description}\n              </p>\n            )}\n            {actions && (\n              <div\n                className={cn(\"flex flex-wrap items-center gap-3\", centered && \"justify-center\", hasHeader && \"mt-8\", arrive, hidden, classNames?.actions)}\n                style={next()}\n              >\n                {actions}\n              </div>\n            )}\n          </div>\n        )}\n        {children !== undefined && children !== null && (\n          <div data-slot=\"section-content\" className={cn(hasHeader && \"mt-12 sm:mt-16\", classNames?.content)}>\n            {children}\n          </div>\n        )}\n      </Container>\n    </section>\n  )\n}\n\nexport { Section, type SectionProps }\n"}],"cssVars":{"theme":{"color-primary-text":"var(--primary-text, var(--primary))","ease-standard":"var(--ease-standard)","ease-enter":"var(--ease-enter)","ease-exit":"var(--ease-exit)","ease-spring":"var(--ease-spring)"},"light":{"duration-fast":"160ms","duration-standard":"240ms","duration-considered":"480ms","ease-standard":"cubic-bezier(0.2, 0, 0, 1)","ease-enter":"cubic-bezier(0, 0, 0, 1)","ease-exit":"cubic-bezier(0.3, 0, 1, 1)","ease-spring":"linear(0, 0.071 4%, 0.234 8%, 0.433 12%, 0.626 17%, 0.791 21%, 0.917 25%, 1.004 29%, 1.055 33%, 1.079 38%, 1.083 42%, 1.075 46%, 1.06 50%, 1.043 54%, 1.027 58%, 1.015 62%, 1.005 67%, 0.998 71%, 0.995 75%, 0.993 79%, 0.993 83%, 0.994 88%, 0.995 92%, 0.997 96%, 1)"},"dark":{}}}