{"_id":"@bernierllc/site-preview-ui","_rev":"4-4db503fa7795b8e54d1a715ef1f469be","name":"@bernierllc/site-preview-ui","dist-tags":{"latest":"0.1.2"},"versions":{"0.0.1":{"name":"@bernierllc/site-preview-ui","version":"0.0.1","keywords":["oidc","trusted-publishing","setup"],"_id":"@bernierllc/site-preview-ui@0.0.1","maintainers":[{"name":"alikhan410","email":"mczeyo@gmail.com"},{"name":"mkbernier","email":"mkbernier@gmail.com"}],"dist":{"shasum":"1bb36e651afe0dc90dc09d783ee81fc6ff36cf5b","tarball":"https://registry.npmjs.org/@bernierllc/site-preview-ui/-/site-preview-ui-0.0.1.tgz","fileCount":2,"integrity":"sha512-4vyGqMBwEuNi2xCXuvBjNhvf8RjVY/l6VYABTdC2qHLThNTTIbK9peYWJYOAOEg/c81rfNTfe/0nYfB6CKcgPg==","signatures":[{"sig":"MEUCIGArUVB5Uy+0O69pH0msxz7Bz9YMB715Iv8IkZy3a2JGAiEAxjoEfBoeDLgipXaQ+NPW4WCWo9QaYQN9uS4w8GSRyZA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":2071},"_npmUser":{"name":"mkbernier","email":"mkbernier@gmail.com"},"_npmVersion":"11.12.1","description":"OIDC trusted publishing setup package for @bernierllc/site-preview-ui","directories":{},"_nodeVersion":"25.9.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/site-preview-ui_0.0.1_1781984322946_0.3098697706781641","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@bernierllc/site-preview-ui","version":"0.1.0","keywords":["react","site-preview","website-builder","ui","components","bernierllc"],"author":{"name":"Bernier LLC"},"license":"SEE LICENSE IN LICENSE","_id":"@bernierllc/site-preview-ui@0.1.0","maintainers":[{"name":"alikhan410","email":"mczeyo@gmail.com"},{"name":"mkbernier","email":"mkbernier@gmail.com"}],"dist":{"shasum":"61542e2febd6eab41fd2afaa005b902b6b8ad1bd","tarball":"https://registry.npmjs.org/@bernierllc/site-preview-ui/-/site-preview-ui-0.1.0.tgz","fileCount":39,"integrity":"sha512-Eq9PJUf6AATT1qVXPvbLB9yU/EwZ4zXC+vmRoaZRT/H4OTWq0b9LlinvfFkD33QDOMdzn3c8Oz04Xj/g7dovqw==","signatures":[{"sig":"MEUCIQD4ym80rrrIhRWgpIQz8COadhEEFWRE22l3/nFYpyVCBgIgb2TiflDjp70XspS+gr4Rv9jhAAJimmBfzonohbfYNp8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":51716},"main":"dist/index.js","_from":"file:bernierllc-site-preview-ui-0.1.0.tgz","types":"dist/index.d.ts","scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts,.tsx","test":"echo 'Tests skipped for initial publish'","build":"tsc","clean":"rm -rf dist","test:run":"jest --watchAll=false","test:watch":"jest --watch","test:coverage":"jest --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:d9003b52-cac2-452e-bacf-a992d76a4dbc"}},"_resolved":"/home/runner/work/tools/tools/packages/ui/site-preview-ui/bernierllc-site-preview-ui-0.1.0.tgz","_integrity":"sha512-Eq9PJUf6AATT1qVXPvbLB9yU/EwZ4zXC+vmRoaZRT/H4OTWq0b9LlinvfFkD33QDOMdzn3c8Oz04Xj/g7dovqw==","bernierllc":{"runtime":"browser","category":"ui","integration":{"logger":"not-applicable","neverhub":"not-applicable","neveradmin":"optional"}},"_npmVersion":"11.17.0","description":"React components and hooks for live site config preview: brand swatches, completeness progress, and iframe embedding.","directories":{},"_nodeVersion":"24.16.0","dependencies":{"@bernierllc/site-config-core":"0.3.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.5.0","react":"^18.0.0","eslint":"^8.45.0","ts-jest":"^29.1.0","react-dom":"^18.0.0","typescript":"^5.0.0","@types/jest":"^29.5.0","@types/react":"^18.0.0","@types/react-dom":"^18.0.0","@testing-library/react":"^13.4.0","jest-environment-jsdom":"^29.5.0","@testing-library/jest-dom":"^5.16.0","@typescript-eslint/parser":"^6.0.0","@testing-library/user-event":"^14.4.0","@typescript-eslint/eslint-plugin":"^6.0.0"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"_npmOperationalInternal":{"tmp":"tmp/site-preview-ui_0.1.0_1781986017168_0.31857096094198756","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@bernierllc/site-preview-ui","version":"0.1.1","keywords":["react","site-preview","website-builder","ui","components","bernierllc"],"author":{"name":"Bernier LLC"},"license":"SEE LICENSE IN LICENSE","_id":"@bernierllc/site-preview-ui@0.1.1","maintainers":[{"name":"alikhan410","email":"mczeyo@gmail.com"},{"name":"mkbernier","email":"mkbernier@gmail.com"}],"dist":{"shasum":"17c7ace06a2a342ee8760b133741a4c679cf70ba","tarball":"https://registry.npmjs.org/@bernierllc/site-preview-ui/-/site-preview-ui-0.1.1.tgz","fileCount":39,"integrity":"sha512-8ku7BgGCdsHh4+xVvwUktd+8XsQe4RUuoN+3lHRzsSohq9op1+8IjGT+GHPluKVaOJj2OHaFTU89XkwqR9w2pA==","signatures":[{"sig":"MEYCIQCioQAhwbOWuSrnRulJqhtOnrAf1kUN82/jFSKGHC87DwIhAO48dNJEfQr0fSzDWl4++B2iVtLmWDHlwO7PSE06PpHX","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":51716},"main":"dist/index.js","_from":"file:bernierllc-site-preview-ui-0.1.1.tgz","types":"dist/index.d.ts","scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts,.tsx","test":"echo 'Tests skipped for initial publish'","build":"tsc","clean":"rm -rf dist","test:run":"jest --watchAll=false","test:watch":"jest --watch","test:coverage":"jest --coverage"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:d9003b52-cac2-452e-bacf-a992d76a4dbc"}},"_resolved":"/home/runner/work/tools/tools/packages/ui/site-preview-ui/bernierllc-site-preview-ui-0.1.1.tgz","_integrity":"sha512-8ku7BgGCdsHh4+xVvwUktd+8XsQe4RUuoN+3lHRzsSohq9op1+8IjGT+GHPluKVaOJj2OHaFTU89XkwqR9w2pA==","bernierllc":{"runtime":"browser","category":"ui","integration":{"logger":"not-applicable","neverhub":"not-applicable","neveradmin":"optional"}},"_npmVersion":"12.0.1","description":"React components and hooks for live site config preview: brand swatches, completeness progress, and iframe embedding.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"@bernierllc/site-config-core":"0.3.1"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.5.0","react":"^18.0.0","eslint":"^8.45.0","ts-jest":"^29.1.0","react-dom":"^18.0.0","typescript":"^5.0.0","@types/jest":"^29.5.0","@types/react":"^18.0.0","@types/react-dom":"^18.0.0","@testing-library/react":"^13.4.0","jest-environment-jsdom":"^29.5.0","@testing-library/jest-dom":"^5.16.0","@typescript-eslint/parser":"^6.0.0","@testing-library/user-event":"^14.4.0","@typescript-eslint/eslint-plugin":"^6.0.0"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"_npmOperationalInternal":{"tmp":"tmp/site-preview-ui_0.1.1_1784016180309_0.41805600013594124","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"_id":"@bernierllc/site-preview-ui@0.1.2","dist":{"shasum":"315e9a43d2996484206835cdbdacee32eb28293d","tarball":"https://registry.npmjs.org/@bernierllc/site-preview-ui/-/site-preview-ui-0.1.2.tgz","fileCount":39,"integrity":"sha512-j9Sp5usQCk6ACrY2Cckwf1zuzbZ9TcEhuUMIV5dpxCOoly3ifgtMxwDgUyhf/6k2VhY7bJK8pZWpdf0bNyBGzQ==","signatures":[{"sig":"MEUCICeR2PpGXDgCBeCu07un8MZ/9tSXjKOKUNQ/mA7GWk2vAiEAvzKIccV8GcVz9XlAPfZTXNcFYt1DvLbucU+NBJjBHC0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIF8QnWSNvUVyUlDekZGVOBeMjn3Cc038lhpoyYJ+JFHwAiEAvChSJMYfuMh5wE5ptMCgYOCoJm+wMy09fUTgD1q6GPk="}],"unpackedSize":51717},"main":"dist/index.js","name":"@bernierllc/site-preview-ui","_from":"file:bernierllc-site-preview-ui-0.1.2.tgz","types":"dist/index.d.ts","author":{"name":"Bernier LLC"},"license":"SEE LICENSE IN LICENSE","scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts,.tsx","test":"echo 'Tests skipped for initial publish'","build":"tsc","clean":"rm -rf dist","test:run":"jest --watchAll=false","test:watch":"jest --watch","test:coverage":"jest --coverage"},"version":"0.1.2","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"d9003b52-cac2-452e-bacf-a992d76a4dbc"}},"keywords":["react","site-preview","website-builder","ui","components","bernierllc"],"_resolved":"/home/runner/work/tools/tools/packages/ui/site-preview-ui/bernierllc-site-preview-ui-0.1.2.tgz","_integrity":"sha512-j9Sp5usQCk6ACrY2Cckwf1zuzbZ9TcEhuUMIV5dpxCOoly3ifgtMxwDgUyhf/6k2VhY7bJK8pZWpdf0bNyBGzQ==","bernierllc":{"runtime":"browser","category":"ui","integration":{"logger":"not-applicable","neverhub":"not-applicable","neveradmin":"optional"}},"_npmVersion":"12.2.0","description":"React components and hooks for live site config preview: brand swatches, completeness progress, and iframe embedding.","directories":{},"maintainers":[{"name":"alikhan410","email":"mczeyo@gmail.com"},{"name":"mkbernier","email":"mkbernier@gmail.com"}],"_nodeVersion":"24.21.0","dependencies":{"@bernierllc/site-config-core":"^0.3.1"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.5.0","react":"^18.0.0","eslint":"^8.45.0","ts-jest":"^29.1.0","react-dom":"^18.0.0","typescript":"^5.0.0","@types/jest":"^29.5.0","@types/react":"^18.0.0","@types/react-dom":"^18.0.0","@testing-library/react":"^13.4.0","jest-environment-jsdom":"^29.5.0","@testing-library/jest-dom":"^5.16.0","@typescript-eslint/parser":"^6.0.0","@testing-library/user-event":"^14.4.0","@typescript-eslint/eslint-plugin":"^6.0.0"},"peerDependencies":{"react":">=16.8.0","react-dom":">=16.8.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/site-preview-ui_0.1.2_1790797404610_0.2722053643424005"}}},"time":{"created":"2026-06-20T19:38:42.835Z","modified":"2026-09-30T19:43:24.888Z","0.0.1":"2026-06-20T19:38:43.092Z","0.1.0":"2026-06-20T20:06:57.304Z","0.1.1":"2026-07-14T08:03:00.450Z","0.1.2":"2026-09-30T19:43:24.697Z"},"author":{"name":"Bernier LLC"},"license":"SEE LICENSE IN LICENSE","keywords":["react","site-preview","website-builder","ui","components","bernierllc"],"description":"React components and hooks for live site config preview: brand swatches, completeness progress, and iframe embedding.","maintainers":[{"name":"alikhan410","email":"mczeyo@gmail.com"},{"name":"mkbernier","email":"mkbernier@gmail.com"}],"readme":"# @bernierllc/site-preview-ui\n\nReact components and hooks for live site configuration preview — brand swatches, completeness progress, and iframe embedding.\n\n## Overview\n\nThis package provides headless-friendly React components for previewing a `SiteConfig` as it is being built by an onboarding agent or admin UI. The centrepiece is `<SitePreviewCard>`, which generalises the `nevarPro/components/ai/site-card.tsx` auto-refresh pattern into a reusable, framework-agnostic component.\n\n**Key features:**\n\n- `<SitePreviewCard>` — live summary card (logo, name, tagline, color swatches, section list, completeness bar)\n- `<SiteFramePreview>` — iframe wrapper for the rendered live site, with open-in-new-tab control\n- `<ThemeSwatches>` — standalone primary / secondary / accent color display\n- `<CompletenessBar>` — 6-checkpoint progress bar driven by `CompletenessResult` from `site-config-core`\n- `useSitePreview` hook — fetches a `SiteRenderModel` via a consumer-provided fetch function; re-fetches on `refreshTrigger` change\n\nAll data is prop-injected or fetched via a consumer-provided function — no built-in network calls, no server coupling, browser-safe.\n\n---\n\n## Installation\n\n```bash\nnpm install @bernierllc/site-preview-ui\n# Peer dependencies (if not already installed):\nnpm install react react-dom\n```\n\n---\n\n## Components\n\n### `<SitePreviewCard>`\n\nThe main preview card. Accepts either a raw `SiteConfig` (computed locally) or a pre-fetched `SiteRenderModel`. Automatically swaps to `<SiteFramePreview>` when the site status is `ready` or `published` and a `livePreviewUrl` is provided.\n\n#### Props\n\n| Prop | Type | Required | Description |\n|------|------|----------|-------------|\n| `config` | `SiteConfig` | No | Raw site config. Computes CSS variables locally. |\n| `renderModel` | `SiteRenderModel` | No | Pre-computed render model. Takes precedence over `config`. |\n| `livePreviewUrl` | `string` | No | When provided and status is `ready`/`published`, renders an iframe instead of the card. |\n| `refreshTrigger` | `number` | No | Increment this to call `onRefresh`. See [refreshTrigger pattern](#refreshtrigger-pattern). |\n| `onRefresh` | `() => void \\| Promise<void>` | No | Called when `refreshTrigger` increments. |\n| `className` | `string` | No | CSS class added to the root element. |\n\n#### Example\n\n```tsx\nimport { SitePreviewCard } from '@bernierllc/site-preview-ui';\n\nfunction OnboardingPanel({ config, agentTurnCount, onReload, liveUrl }) {\n  return (\n    <SitePreviewCard\n      config={config}\n      livePreviewUrl={liveUrl}\n      refreshTrigger={agentTurnCount}\n      onRefresh={onReload}\n      className=\"panel__preview\"\n    />\n  );\n}\n```\n\n---\n\n### `<SiteFramePreview>`\n\nIframe wrapper for the rendered live site. Shows an accessible iframe and an open-in-new-tab anchor. Renders an error fallback when `src` is empty.\n\n#### Props\n\n| Prop | Type | Required | Default | Description |\n|------|------|----------|---------|-------------|\n| `src` | `string` | Yes | — | URL to embed. |\n| `title` | `string` | No | `\"Site preview\"` | Accessible iframe title. |\n| `height` | `string \\| number` | No | `600` | Iframe height. |\n| `openInNewTabLabel` | `string` | No | `\"Open in new tab\"` | Label for the open-in-new-tab anchor. |\n| `className` | `string` | No | — | CSS class added to the wrapper. |\n\n#### Example\n\n```tsx\nimport { SiteFramePreview } from '@bernierllc/site-preview-ui';\n\n<SiteFramePreview\n  src=\"https://acme.myapp.com\"\n  title=\"Acme Plumbing — live site\"\n  height={700}\n  openInNewTabLabel=\"View your site\"\n/>\n```\n\n---\n\n### `<ThemeSwatches>`\n\nRenders primary, secondary, and accent color swatches from a `SiteConfig['theme']` object.\n\n#### Props\n\n| Prop | Type | Required | Default | Description |\n|------|------|----------|---------|-------------|\n| `theme` | `SiteConfig['theme']` | Yes | — | The theme sub-object from a `SiteConfig`. |\n| `size` | `'sm' \\| 'md' \\| 'lg'` | No | `'md'` | Swatch circle size. |\n| `showLabels` | `boolean` | No | `false` | Show \"Primary\", \"Secondary\", \"Accent\" labels. |\n| `className` | `string` | No | — | CSS class on the container. |\n\n#### Example\n\n```tsx\nimport { ThemeSwatches } from '@bernierllc/site-preview-ui';\n\n<ThemeSwatches theme={config.theme} showLabels size=\"lg\" />\n```\n\n---\n\n### `<CompletenessBar>`\n\n6-checkpoint progress indicator driven by a `CompletenessResult` from `@bernierllc/site-config-core`.\n\n#### Props\n\n| Prop | Type | Required | Default | Description |\n|------|------|----------|---------|-------------|\n| `completeness` | `CompletenessResult` | Yes | — | Result of `completenessScore(config)`. |\n| `showLabels` | `boolean` | No | `false` | Show checkpoint label text beside each dot. |\n| `showScore` | `boolean` | No | `false` | Show aggregate percentage (e.g. \"67%\"). |\n| `className` | `string` | No | — | CSS class on the container. |\n\n#### Example\n\n```tsx\nimport { completenessScore } from '@bernierllc/site-config-core';\nimport { CompletenessBar } from '@bernierllc/site-preview-ui';\n\nconst result = completenessScore(config);\n\n<CompletenessBar completeness={result} showScore showLabels />\n```\n\n---\n\n## Hook\n\n### `useSitePreview`\n\nFetches a `SiteRenderModel` via a consumer-provided async function and exposes loading / error / refresh state. Re-fetches when `refreshTrigger` changes or when `refresh()` is called imperatively.\n\n#### Options\n\n| Option | Type | Required | Description |\n|--------|------|----------|-------------|\n| `fetchFn` | `() => Promise<SiteRenderModel>` | Yes | Async function that resolves the render model (e.g. a server action or fetch call). Memoize with `useCallback` to avoid unnecessary re-fetches. |\n| `refreshTrigger` | `number` | No | Re-fetches when this value changes. |\n| `pollingIntervalMs` | `number` | No | Poll on a fixed interval (milliseconds). |\n\n#### Result\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `renderModel` | `SiteRenderModel \\| null` | Most recent resolved model, or `null` before first fetch. |\n| `loading` | `boolean` | `true` while a fetch is in-flight. |\n| `error` | `Error \\| null` | Last fetch error, wrapped in `SitePreviewUIError`, or `null`. |\n| `refresh` | `() => void` | Imperatively trigger a re-fetch. |\n\n#### Example\n\n```tsx\nimport { useSitePreview, SitePreviewCard } from '@bernierllc/site-preview-ui';\nimport { useCallback } from 'react';\n\nfunction SiteDashboard({ siteId }: { siteId: string }) {\n  const fetchFn = useCallback(\n    () => fetch(`/api/sites/${siteId}/render-model`).then((r) => r.json()),\n    [siteId]\n  );\n\n  const { renderModel, loading, error } = useSitePreview({ fetchFn });\n\n  if (loading) return <div>Loading preview...</div>;\n  if (error) return <div>Error: {error.message}</div>;\n  if (!renderModel) return null;\n\n  return <SitePreviewCard renderModel={renderModel} />;\n}\n```\n\n---\n\n## `refreshTrigger` Pattern\n\nThe `refreshTrigger` prop (on `<SitePreviewCard>`) and option (on `useSitePreview`) implement the **counter-increment refresh** pattern used in the nevarPro onboarding agent: the parent tracks a counter and increments it after each agent turn; the component detects the change and calls `onRefresh` (or re-fetches). This avoids prop-drilling a boolean flag that needs to be reset.\n\n```tsx\nimport { useState, useCallback } from 'react';\nimport { SitePreviewCard, useSitePreview } from '@bernierllc/site-preview-ui';\n\nfunction OnboardingView({ siteId }: { siteId: string }) {\n  const [agentTurnCount, setAgentTurnCount] = useState(0);\n\n  // Called after each agent streaming turn completes\n  const onAgentTurnComplete = useCallback(() => {\n    setAgentTurnCount((c) => c + 1);\n  }, []);\n\n  const fetchFn = useCallback(\n    () => fetch(`/api/sites/${siteId}/render`).then((r) => r.json()),\n    [siteId]\n  );\n\n  const { renderModel, refresh } = useSitePreview({\n    fetchFn,\n    refreshTrigger: agentTurnCount, // re-fetches when counter increments\n  });\n\n  return (\n    <SitePreviewCard\n      renderModel={renderModel ?? undefined}\n      refreshTrigger={agentTurnCount}\n      onRefresh={refresh}           // card calls this when trigger increments\n      livePreviewUrl={`https://${siteId}.myapp.com`}\n    />\n  );\n}\n```\n\n**Behaviour:**\n- `refreshTrigger={0}` on initial render — no `onRefresh` call.\n- `refreshTrigger={1}` on rerender — `onRefresh` is called once.\n- `refreshTrigger={2}` on next rerender — `onRefresh` called again.\n\n---\n\n## Error Handling\n\nFetch errors from `useSitePreview` are wrapped in `SitePreviewUIError`:\n\n```typescript\nimport { SitePreviewUIError } from '@bernierllc/site-preview-ui';\n\nconst { error } = useSitePreview({ fetchFn });\n\nif (error instanceof SitePreviewUIError) {\n  console.error(error.code);    // 'FETCH_FAILED'\n  console.error(error.cause);   // original Error\n  console.error(error.context); // optional key-value context\n}\n```\n\nThe error class follows the ES2022 `Error.cause` chaining pattern used across the BernierLLC package suite.\n\n---\n\n## `SiteRenderModel` Type\n\nSince `SiteRenderModel` is not defined in `site-config-core` (which only ships `SiteConfig` / `CompletenessResult`), this package defines and exports it:\n\n```typescript\nexport interface SiteRenderModel {\n  config: SiteConfig;\n  cssVariables: Record<string, string>; // from toCssVariables(config.theme)\n}\n```\n\nThe `cssVariables` shape matches the output of `toCssVariables` from `@bernierllc/site-config-core`:\n\n```\n--color-primary, --color-secondary, --color-accent, --font-heading, --font-body\n```\n\n---\n\n## Styling\n\nComponents ship with minimal inline styles (for swatch sizes and iframe dimensions only). All layout and colour classes follow BEM naming (`site-preview-card__name`, `completeness-checkpoint--passed`, etc.) and can be overridden via CSS. Pass `className` to any component for custom targeting.\n\nCSS custom properties from `SiteRenderModel.cssVariables` are applied as inline styles on `<SitePreviewCard>`'s root element, so child elements can consume `var(--color-primary)` etc. without additional configuration.\n\n---\n\n## Browser Safety\n\nThis package imports no Node.js built-ins and performs no built-in network requests. All data access is delegated to the consumer via prop injection or the `fetchFn` option. Safe to bundle for browser-only applications.\n\n---\n\n## License\n\nCopyright (c) 2025 Bernier LLC. Licensed to the client under a limited-use license. See LICENSE for details.\n","readmeFilename":"README.md"}