{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "page",
  "title": "Page",
  "type": "registry:ui",
  "description": "Page-level layout shell — title row, optional breadcrumbs, action bar, and slotted content well. The standard wrapper for route pages so every screen looks consistent.",
  "categories": [
    "layout"
  ],
  "files": [
    {
      "path": "packages/elements-lit/src/components/page/page.ts",
      "content": "import { LitElement, css, html, nothing } from 'lit'\nimport { tailwind } from '../../lib/styles'\nimport { ThemeController } from '../../lib/theme'\n\n/**\n * Page layout shell — the registry Page / PageBody / PageHeader /\n * PageHeaderHeading as four small elements (no id references between them,\n * so separate parts are fine).\n *\n * Slotted children aren't shadow-tree children, so React's `space-y-4` on\n * Page can't reach them — and a `::slotted()` margin loses to the host page's\n * preflight (`* { margin: 0 }`), since outer styles beat ::slotted in the\n * cascade. It is expressed as `flex flex-col gap-4` instead (the <slot> is\n * display: contents, so slotted children are the flex items). Consumer\n * classes go on the hosts.\n */\nexport class UipPage extends LitElement {\n  // :host display can't be a utility class (the host has no template of its own).\n  static styles = [tailwind, css`:host { display: block; }`]\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'page')\n  }\n\n  render() {\n    return html`<div part=\"base\" data-slot=\"page\" class=\"flex flex-col gap-4\"><slot></slot></div>`\n  }\n}\n\n/** <uip-page-body> — plain content region (React's PageBody). */\nexport class UipPageBody extends LitElement {\n  static styles = [tailwind, css`:host { display: block; }`]\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'page-body')\n  }\n\n  render() {\n    return html`<div part=\"base\" data-slot=\"page-body\"><slot></slot></div>`\n  }\n}\n\n/**\n * <uip-page-header> — title row. Default slot: heading content (React's\n * `children`, wrapped in `flex-1`); `actions` slot: the right-aligned action\n * bar (React's `actions` prop), in a wrapping `gap-2` row that is dropped when\n * nothing is slotted.\n */\nexport class UipPageHeader extends LitElement {\n  static styles = [tailwind, css`:host { display: block; }`]\n  static properties = { hasActions: { state: true } }\n  private hasActions = false\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'page-header')\n  }\n\n  render() {\n    return html`<div\n      part=\"base\"\n      data-slot=\"page-header\"\n      class=\"flex flex-col justify-between gap-4 sm:flex-row sm:items-center\"\n    >\n      <div class=\"flex-1\"><slot></slot></div>\n      <div\n        part=\"actions\"\n        data-slot=\"page-header-actions\"\n        class=\"flex flex-wrap items-center gap-2 self-start sm:self-auto\"\n        ?hidden=${!this.hasActions}\n      >\n        <slot\n          name=\"actions\"\n          @slotchange=${(e: Event) => (this.hasActions = (e.target as HTMLSlotElement).assignedNodes().length > 0)}\n        ></slot>\n      </div>\n    </div>`\n  }\n}\n\n/**\n * <uip-page-header-heading> — `heading` (React's `title`; `title` is an\n * HTMLElement attribute and would show a tooltip) and optional `description`.\n */\nexport class UipPageHeaderHeading extends LitElement {\n  static styles = [tailwind, css`:host { display: block; }`]\n\n  static properties = {\n    heading: {},\n    description: {},\n  }\n\n  heading = ''\n  description?: string\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'page-header-heading')\n  }\n\n  render() {\n    return html`<div part=\"base\" data-slot=\"page-header-heading\">\n      <h2 class=\"text-2xl font-semibold tracking-tight\">${this.heading}</h2>\n      ${this.description\n        ? html`<p class=\"text-muted-foreground mt-1 text-sm\">${this.description}</p>`\n        : nothing}\n    </div>`\n  }\n}\n\ncustomElements.get('uip-page') || customElements.define('uip-page', UipPage)\ncustomElements.get('uip-page-body') || customElements.define('uip-page-body', UipPageBody)\ncustomElements.get('uip-page-header') || customElements.define('uip-page-header', UipPageHeader)\ncustomElements.get('uip-page-header-heading') || customElements.define('uip-page-header-heading', UipPageHeaderHeading)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-page': UipPage\n    'uip-page-body': UipPageBody\n    'uip-page-header': UipPageHeader\n    'uip-page-header-heading': UipPageHeaderHeading\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/components/page/page.ts"
    }
  ],
  "dependencies": [
    "lit"
  ],
  "registryDependencies": [
    "https://uipkge.dev/r/lit/lit-core.json"
  ]
}
