@santi020k/lumen-mcp 3.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -69,7 +69,7 @@
69
69
  "Kbd": "14825287aa0d6ffcec6b51b61b61d8a7c44d7cba1c7c40a77a7d46d0632c25de",
70
70
  "Label": "7ad8e319a0bdbdfcfac72f32c6fd31fe332ce2da8fd3a9919fd5c92e1123e0c6",
71
71
  "Link": "1c1688ea85a844a67e9c3e45d0092ca7e364a3f762b21db86234d0ad0de87600",
72
- "LineChart": "faafccbed95fa067b89809c4249df7bb109edab2f0b4514f11b7bd92d88e2554",
72
+ "LineChart": "097099c8d5e178058eac0114c1598be39ac90c21e207672e02e0e899b54e933d",
73
73
  "ListBox": "65724ac8eeeb0a9e43c05017ef152cfff5388627103e19bc14dcad8d5ee4d02c",
74
74
  "Marker": "b2c3679b841cee6cc4ce6322d6dd7758f85a9371bc4817a750f6de4e7c0cc3d2",
75
75
  "Menubar": "ceaa113420c1a99cc1d7e856c04b5a75d8569473ebd8a24a83c947ec3b6608e5",
@@ -14760,7 +14760,7 @@
14760
14760
  "values": "HTML attributes"
14761
14761
  }
14762
14762
  ],
14763
- "astroSource": "---\nimport type { HTMLAttributes } from 'astro/types'\n\nimport {\n alignLumenChartSeries,\n createLumenLineGeometry,\n formatLumenChartSummary,\n getLumenChartCategories,\n getLumenChartDomain,\n getLumenChartTicks,\n getLumenChartToneClassName,\n hasLumenChartData,\n type LumenChartLabels,\n type LumenChartSeries,\n resolveLumenChartLabels,\n resolveLumenChartTone,\n scaleLumenChartValue\n} from '@santi020k/lumen-core'\n\nimport Chart from './Chart.astro'\n\ninterface Props extends HTMLAttributes<'figure'> {\n area?: boolean\n caption?: string\n class?: string | null\n className?: string\n description?: string\n emptyLabel?: string\n formatCategory?: (category: number | string) => string\n formatValue?: (value: number) => string\n glass?: boolean | 'subtle' | 'strong'\n heading?: string\n labels?: Partial<LumenChartLabels>\n markers?: 'all' | 'auto' | 'none' | boolean | number\n presentation?: 'bare' | 'default'\n referenceValue?: number\n series: readonly LumenChartSeries[]\n showLegend?: boolean\n showTable?: boolean\n summary?: string\n}\n\nconst {\n area = false,\n caption,\n class: classProp = '',\n className = '',\n description,\n emptyLabel,\n formatCategory = String,\n formatValue = String,\n glass = false,\n heading,\n labels,\n markers = 'auto',\n referenceValue,\n series,\n showLegend = series.length > 1,\n showTable = true,\n summary: summaryProp,\n ...rest\n} = Astro.props\n\nconst resolvedLabels = resolveLumenChartLabels(labels)\nconst summary = summaryProp ?? formatLumenChartSummary(series, formatValue, resolvedLabels)\nconst width = 640\nconst height = 320\nconst padding = 44\nconst categories = getLumenChartCategories(series)\nconst hasData = hasLumenChartData(series)\nconst alignedSeries = series.map(item => alignLumenChartSeries(item, categories))\n\nconst domain = getLumenChartDomain([\n ...alignedSeries.flatMap(item => item.data.map(datum => datum.y)),\n referenceValue ?? null\n], false)\n\nconst geometries = alignedSeries.map(item => createLumenLineGeometry(item.data, {\n domain,\n height,\n includeZero: false,\n padding,\n width\n}))\n\nconst ticks = getLumenChartTicks(domain)\nconst labelStep = Math.max(1, Math.ceil(categories.length / 8))\nlet markerStep = 1\n\nif (markers === false || markers === 'none') markerStep = Number.POSITIVE_INFINITY\nelse if (typeof markers === 'number') markerStep = Math.max(1, Math.round(markers))\nelse if (markers === 'auto') markerStep = Math.max(1, Math.ceil(categories.length / 24))\n\nconst valueFor = (item: LumenChartSeries, category: number | string) => item.data.find(datum => datum.x === category)\n\nconst categoryLabelFor = (category: number | string) => series.flatMap(item => item.data)\n .find(datum => datum.x === category)?.xLabel ?? formatCategory(category)\n\nconst referenceY =\n referenceValue === undefined ?\n undefined :\n scaleLumenChartValue(referenceValue, domain, height - padding, padding)\n---\n\n<Chart\n class:list={['ui-line-chart', classProp, className]}\n {caption}\n {description}\n {glass}\n {heading}\n {summary}\n {...rest}\n>\n {\n showLegend && hasData && (\n <ul class=\"ui-chart__legend\" aria-label={resolvedLabels.chartLegend}>\n {series.map((item, index) => (\n <li class={getLumenChartToneClassName(item.tone, index)}>\n <span aria-hidden=\"true\" />\n {item.label}\n </li>\n ))}\n </ul>\n )\n }\n {!hasData && <p class=\"ui-chart__empty\" role=\"status\">{emptyLabel ?? resolvedLabels.empty}</p>}\n <div class=\"ui-chart__plot\" hidden={!hasData}>\n <svg\n aria-hidden=\"true\"\n preserveAspectRatio=\"xMidYMid meet\"\n viewBox={`0 0 ${width} ${height}`}\n >\n <g class=\"ui-chart__grid\">\n {\n ticks.map(tick => {\n const y = scaleLumenChartValue(\n tick, domain, height - padding, padding\n )\n\n return (\n <>\n <line x1={padding} x2={width - padding} y1={y} y2={y} />\n <text x={padding - 8} y={y}>\n {formatValue(tick)}\n </text>\n </>\n )\n })\n }\n </g>\n <g class=\"ui-chart__axis-labels\">\n {\n categories.map((category, index) => {\n if (\n index % labelStep !== 0 &&\n index !== categories.length - 1\n )\n return null\n\n const denominator = Math.max(1, categories.length - 1)\n\n const x =\n padding + (index / denominator) * (width - padding * 2)\n\n return (\n <text text-anchor=\"middle\" x={x} y={height - 14}>\n {categoryLabelFor(category)}\n </text>\n )\n })\n }\n </g>\n {\n referenceY !== undefined && (\n <line\n class=\"ui-chart__reference\"\n x1={padding}\n x2={width - padding}\n y1={referenceY}\n y2={referenceY}\n />\n )\n }\n {\n geometries.map((geometry, index) => {\n const item = series[index]\n\n if (!item) return null\n\n const tone = resolveLumenChartTone(item.tone, index)\n\n return (\n <g class:list={['ui-line-chart__series', getLumenChartToneClassName(tone)]}>\n {area &&\n geometry.areaPaths.map(path => (\n <path class=\"ui-line-chart__area\" d={path} />\n ))}\n <path class=\"ui-line-chart__line\" d={geometry.path} />\n {Number.isFinite(markerStep) &&\n geometry.points.map((point, pointIndex) => pointIndex % markerStep === 0 && (\n <circle\n class=\"ui-line-chart__point\"\n cx={point.xCoordinate}\n cy={point.yCoordinate}\n r=\"3\"\n >\n <title>\n {`${point.xLabel ?? formatCategory(point.x)} · ${item.label}: ${formatValue(point.y ?? 0)}`}\n </title>\n </circle>\n ))}\n </g>\n )\n })\n }\n </svg>\n </div>\n {\n showTable && hasData && (\n <details class=\"ui-chart__data\">\n <summary>{resolvedLabels.viewData}</summary>\n <div>\n <table>\n <thead>\n <tr>\n <th scope=\"col\">{resolvedLabels.category}</th>\n {series.map(item => <th scope=\"col\">{item.label}</th>)}\n </tr>\n </thead>\n <tbody>\n {categories.map(category => (\n <tr>\n <th scope=\"row\">{categoryLabelFor(category)}</th>\n {series.map(item => {\n const datum = valueFor(item, category)\n\n return (\n <td>\n {datum?.label ??\n (datum?.y === undefined || datum.y === null || !Number.isFinite(datum.y) ?\n resolvedLabels.notAvailable :\n formatValue(datum.y))}\n </td>\n )\n })}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </details>\n )\n }\n</Chart>\n",
14763
+ "astroSource": "---\nimport type { HTMLAttributes } from 'astro/types'\n\nimport {\n alignLumenChartSeries,\n createLumenLineGeometry,\n formatLumenChartSummary,\n getLumenChartAxisPadding,\n getLumenChartCategories,\n getLumenChartDomain,\n getLumenChartTicks,\n getLumenChartToneClassName,\n hasLumenChartData,\n type LumenChartLabels,\n type LumenChartSeries,\n resolveLumenChartLabels,\n resolveLumenChartTone,\n scaleLumenChartValue\n} from '@santi020k/lumen-core'\n\nimport Chart from './Chart.astro'\n\ninterface Props extends HTMLAttributes<'figure'> {\n area?: boolean\n caption?: string\n class?: string | null\n className?: string\n description?: string\n emptyLabel?: string\n formatCategory?: (category: number | string) => string\n formatValue?: (value: number) => string\n glass?: boolean | 'subtle' | 'strong'\n heading?: string\n labels?: Partial<LumenChartLabels>\n markers?: 'all' | 'auto' | 'none' | boolean | number\n presentation?: 'bare' | 'default'\n referenceValue?: number\n series: readonly LumenChartSeries[]\n showLegend?: boolean\n showTable?: boolean\n summary?: string\n}\n\nconst {\n area = false,\n caption,\n class: classProp = '',\n className = '',\n description,\n emptyLabel,\n formatCategory = String,\n formatValue = String,\n glass = false,\n heading,\n labels,\n markers = 'auto',\n referenceValue,\n series,\n showLegend = series.length > 1,\n showTable = true,\n summary: summaryProp,\n ...rest\n} = Astro.props\n\nconst resolvedLabels = resolveLumenChartLabels(labels)\nconst summary = summaryProp ?? formatLumenChartSummary(series, formatValue, resolvedLabels)\nconst width = 640\nconst height = 320\nconst padding = 44\nconst categories = getLumenChartCategories(series)\nconst hasData = hasLumenChartData(series)\nconst alignedSeries = series.map(item => alignLumenChartSeries(item, categories))\n\nconst domain = getLumenChartDomain([\n ...alignedSeries.flatMap(item => item.data.map(datum => datum.y)),\n referenceValue ?? null\n], false)\n\nconst ticks = getLumenChartTicks(domain)\nconst paddingLeft = getLumenChartAxisPadding(ticks.map(tick => formatValue(tick)))\n\nconst geometries = alignedSeries.map(item => createLumenLineGeometry(item.data, {\n domain,\n height,\n includeZero: false,\n padding,\n paddingLeft,\n width\n}))\n\nconst labelStep = Math.max(1, Math.ceil(categories.length / 8))\nlet markerStep = 1\n\nif (markers === false || markers === 'none') markerStep = Number.POSITIVE_INFINITY\nelse if (typeof markers === 'number') markerStep = Math.max(1, Math.round(markers))\nelse if (markers === 'auto') markerStep = Math.max(1, Math.ceil(categories.length / 24))\n\nconst valueFor = (item: LumenChartSeries, category: number | string) => item.data.find(datum => datum.x === category)\n\nconst categoryLabelFor = (category: number | string) => series.flatMap(item => item.data)\n .find(datum => datum.x === category)?.xLabel ?? formatCategory(category)\n\nconst referenceY =\n referenceValue === undefined ?\n undefined :\n scaleLumenChartValue(referenceValue, domain, height - padding, padding)\n---\n\n<Chart\n class:list={['ui-line-chart', classProp, className]}\n {caption}\n {description}\n {glass}\n {heading}\n {summary}\n {...rest}\n>\n {\n showLegend && hasData && (\n <ul class=\"ui-chart__legend\" aria-label={resolvedLabels.chartLegend}>\n {series.map((item, index) => (\n <li class={getLumenChartToneClassName(item.tone, index)}>\n <span aria-hidden=\"true\" />\n {item.label}\n </li>\n ))}\n </ul>\n )\n }\n {!hasData && <p class=\"ui-chart__empty\" role=\"status\">{emptyLabel ?? resolvedLabels.empty}</p>}\n <div class=\"ui-chart__plot\" hidden={!hasData}>\n <svg\n aria-hidden=\"true\"\n preserveAspectRatio=\"xMidYMid meet\"\n viewBox={`0 0 ${width} ${height}`}\n >\n <g class=\"ui-chart__grid\">\n {\n ticks.map(tick => {\n const y = scaleLumenChartValue(\n tick, domain, height - padding, padding\n )\n\n return (\n <>\n <line x1={paddingLeft} x2={width - padding} y1={y} y2={y} />\n <text x={paddingLeft - 8} y={y}>\n {formatValue(tick)}\n </text>\n </>\n )\n })\n }\n </g>\n <g class=\"ui-chart__axis-labels\">\n {\n categories.map((category, index) => {\n if (\n index % labelStep !== 0 &&\n index !== categories.length - 1\n )\n return null\n\n const denominator = Math.max(1, categories.length - 1)\n\n const x =\n paddingLeft + (index / denominator) * (width - paddingLeft - padding)\n\n return (\n <text text-anchor=\"middle\" x={x} y={height - 14}>\n {categoryLabelFor(category)}\n </text>\n )\n })\n }\n </g>\n {\n referenceY !== undefined && (\n <line\n class=\"ui-chart__reference\"\n x1={paddingLeft}\n x2={width - padding}\n y1={referenceY}\n y2={referenceY}\n />\n )\n }\n {\n geometries.map((geometry, index) => {\n const item = series[index]\n\n if (!item) return null\n\n const tone = resolveLumenChartTone(item.tone, index)\n\n return (\n <g class:list={['ui-line-chart__series', getLumenChartToneClassName(tone)]}>\n {area &&\n geometry.areaPaths.map(path => (\n <path class=\"ui-line-chart__area\" d={path} />\n ))}\n <path class=\"ui-line-chart__line\" d={geometry.path} />\n {Number.isFinite(markerStep) &&\n geometry.points.map((point, pointIndex) => pointIndex % markerStep === 0 && (\n <circle\n class=\"ui-line-chart__point\"\n cx={point.xCoordinate}\n cy={point.yCoordinate}\n r=\"3\"\n >\n <title>\n {`${point.xLabel ?? formatCategory(point.x)} · ${item.label}: ${formatValue(point.y ?? 0)}`}\n </title>\n </circle>\n ))}\n </g>\n )\n })\n }\n </svg>\n </div>\n {\n showTable && hasData && (\n <details class=\"ui-chart__data\">\n <summary>{resolvedLabels.viewData}</summary>\n <div>\n <table>\n <thead>\n <tr>\n <th scope=\"col\">{resolvedLabels.category}</th>\n {series.map(item => <th scope=\"col\">{item.label}</th>)}\n </tr>\n </thead>\n <tbody>\n {categories.map(category => (\n <tr>\n <th scope=\"row\">{categoryLabelFor(category)}</th>\n {series.map(item => {\n const datum = valueFor(item, category)\n\n return (\n <td>\n {datum?.label ??\n (datum?.y === undefined || datum.y === null || !Number.isFinite(datum.y) ?\n resolvedLabels.notAvailable :\n formatValue(datum.y))}\n </td>\n )\n })}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </details>\n )\n }\n</Chart>\n",
14764
14764
  "category": "Data display",
14765
14765
  "collections": [
14766
14766
  "Data visualization"
@@ -14881,7 +14881,7 @@
14881
14881
  }
14882
14882
  ],
14883
14883
  "propsExtends": "HTMLAttributes<'figure'>",
14884
- "source": "---\nimport type { HTMLAttributes } from 'astro/types'\n\nimport {\n alignLumenChartSeries,\n createLumenLineGeometry,\n formatLumenChartSummary,\n getLumenChartCategories,\n getLumenChartDomain,\n getLumenChartTicks,\n getLumenChartToneClassName,\n hasLumenChartData,\n type LumenChartLabels,\n type LumenChartSeries,\n resolveLumenChartLabels,\n resolveLumenChartTone,\n scaleLumenChartValue\n} from '@santi020k/lumen-core'\n\nimport Chart from './Chart.astro'\n\ninterface Props extends HTMLAttributes<'figure'> {\n area?: boolean\n caption?: string\n class?: string | null\n className?: string\n description?: string\n emptyLabel?: string\n formatCategory?: (category: number | string) => string\n formatValue?: (value: number) => string\n glass?: boolean | 'subtle' | 'strong'\n heading?: string\n labels?: Partial<LumenChartLabels>\n markers?: 'all' | 'auto' | 'none' | boolean | number\n presentation?: 'bare' | 'default'\n referenceValue?: number\n series: readonly LumenChartSeries[]\n showLegend?: boolean\n showTable?: boolean\n summary?: string\n}\n\nconst {\n area = false,\n caption,\n class: classProp = '',\n className = '',\n description,\n emptyLabel,\n formatCategory = String,\n formatValue = String,\n glass = false,\n heading,\n labels,\n markers = 'auto',\n referenceValue,\n series,\n showLegend = series.length > 1,\n showTable = true,\n summary: summaryProp,\n ...rest\n} = Astro.props\n\nconst resolvedLabels = resolveLumenChartLabels(labels)\nconst summary = summaryProp ?? formatLumenChartSummary(series, formatValue, resolvedLabels)\nconst width = 640\nconst height = 320\nconst padding = 44\nconst categories = getLumenChartCategories(series)\nconst hasData = hasLumenChartData(series)\nconst alignedSeries = series.map(item => alignLumenChartSeries(item, categories))\n\nconst domain = getLumenChartDomain([\n ...alignedSeries.flatMap(item => item.data.map(datum => datum.y)),\n referenceValue ?? null\n], false)\n\nconst geometries = alignedSeries.map(item => createLumenLineGeometry(item.data, {\n domain,\n height,\n includeZero: false,\n padding,\n width\n}))\n\nconst ticks = getLumenChartTicks(domain)\nconst labelStep = Math.max(1, Math.ceil(categories.length / 8))\nlet markerStep = 1\n\nif (markers === false || markers === 'none') markerStep = Number.POSITIVE_INFINITY\nelse if (typeof markers === 'number') markerStep = Math.max(1, Math.round(markers))\nelse if (markers === 'auto') markerStep = Math.max(1, Math.ceil(categories.length / 24))\n\nconst valueFor = (item: LumenChartSeries, category: number | string) => item.data.find(datum => datum.x === category)\n\nconst categoryLabelFor = (category: number | string) => series.flatMap(item => item.data)\n .find(datum => datum.x === category)?.xLabel ?? formatCategory(category)\n\nconst referenceY =\n referenceValue === undefined ?\n undefined :\n scaleLumenChartValue(referenceValue, domain, height - padding, padding)\n---\n\n<Chart\n class:list={['ui-line-chart', classProp, className]}\n {caption}\n {description}\n {glass}\n {heading}\n {summary}\n {...rest}\n>\n {\n showLegend && hasData && (\n <ul class=\"ui-chart__legend\" aria-label={resolvedLabels.chartLegend}>\n {series.map((item, index) => (\n <li class={getLumenChartToneClassName(item.tone, index)}>\n <span aria-hidden=\"true\" />\n {item.label}\n </li>\n ))}\n </ul>\n )\n }\n {!hasData && <p class=\"ui-chart__empty\" role=\"status\">{emptyLabel ?? resolvedLabels.empty}</p>}\n <div class=\"ui-chart__plot\" hidden={!hasData}>\n <svg\n aria-hidden=\"true\"\n preserveAspectRatio=\"xMidYMid meet\"\n viewBox={`0 0 ${width} ${height}`}\n >\n <g class=\"ui-chart__grid\">\n {\n ticks.map(tick => {\n const y = scaleLumenChartValue(\n tick, domain, height - padding, padding\n )\n\n return (\n <>\n <line x1={padding} x2={width - padding} y1={y} y2={y} />\n <text x={padding - 8} y={y}>\n {formatValue(tick)}\n </text>\n </>\n )\n })\n }\n </g>\n <g class=\"ui-chart__axis-labels\">\n {\n categories.map((category, index) => {\n if (\n index % labelStep !== 0 &&\n index !== categories.length - 1\n )\n return null\n\n const denominator = Math.max(1, categories.length - 1)\n\n const x =\n padding + (index / denominator) * (width - padding * 2)\n\n return (\n <text text-anchor=\"middle\" x={x} y={height - 14}>\n {categoryLabelFor(category)}\n </text>\n )\n })\n }\n </g>\n {\n referenceY !== undefined && (\n <line\n class=\"ui-chart__reference\"\n x1={padding}\n x2={width - padding}\n y1={referenceY}\n y2={referenceY}\n />\n )\n }\n {\n geometries.map((geometry, index) => {\n const item = series[index]\n\n if (!item) return null\n\n const tone = resolveLumenChartTone(item.tone, index)\n\n return (\n <g class:list={['ui-line-chart__series', getLumenChartToneClassName(tone)]}>\n {area &&\n geometry.areaPaths.map(path => (\n <path class=\"ui-line-chart__area\" d={path} />\n ))}\n <path class=\"ui-line-chart__line\" d={geometry.path} />\n {Number.isFinite(markerStep) &&\n geometry.points.map((point, pointIndex) => pointIndex % markerStep === 0 && (\n <circle\n class=\"ui-line-chart__point\"\n cx={point.xCoordinate}\n cy={point.yCoordinate}\n r=\"3\"\n >\n <title>\n {`${point.xLabel ?? formatCategory(point.x)} · ${item.label}: ${formatValue(point.y ?? 0)}`}\n </title>\n </circle>\n ))}\n </g>\n )\n })\n }\n </svg>\n </div>\n {\n showTable && hasData && (\n <details class=\"ui-chart__data\">\n <summary>{resolvedLabels.viewData}</summary>\n <div>\n <table>\n <thead>\n <tr>\n <th scope=\"col\">{resolvedLabels.category}</th>\n {series.map(item => <th scope=\"col\">{item.label}</th>)}\n </tr>\n </thead>\n <tbody>\n {categories.map(category => (\n <tr>\n <th scope=\"row\">{categoryLabelFor(category)}</th>\n {series.map(item => {\n const datum = valueFor(item, category)\n\n return (\n <td>\n {datum?.label ??\n (datum?.y === undefined || datum.y === null || !Number.isFinite(datum.y) ?\n resolvedLabels.notAvailable :\n formatValue(datum.y))}\n </td>\n )\n })}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </details>\n )\n }\n</Chart>\n",
14884
+ "source": "---\nimport type { HTMLAttributes } from 'astro/types'\n\nimport {\n alignLumenChartSeries,\n createLumenLineGeometry,\n formatLumenChartSummary,\n getLumenChartAxisPadding,\n getLumenChartCategories,\n getLumenChartDomain,\n getLumenChartTicks,\n getLumenChartToneClassName,\n hasLumenChartData,\n type LumenChartLabels,\n type LumenChartSeries,\n resolveLumenChartLabels,\n resolveLumenChartTone,\n scaleLumenChartValue\n} from '@santi020k/lumen-core'\n\nimport Chart from './Chart.astro'\n\ninterface Props extends HTMLAttributes<'figure'> {\n area?: boolean\n caption?: string\n class?: string | null\n className?: string\n description?: string\n emptyLabel?: string\n formatCategory?: (category: number | string) => string\n formatValue?: (value: number) => string\n glass?: boolean | 'subtle' | 'strong'\n heading?: string\n labels?: Partial<LumenChartLabels>\n markers?: 'all' | 'auto' | 'none' | boolean | number\n presentation?: 'bare' | 'default'\n referenceValue?: number\n series: readonly LumenChartSeries[]\n showLegend?: boolean\n showTable?: boolean\n summary?: string\n}\n\nconst {\n area = false,\n caption,\n class: classProp = '',\n className = '',\n description,\n emptyLabel,\n formatCategory = String,\n formatValue = String,\n glass = false,\n heading,\n labels,\n markers = 'auto',\n referenceValue,\n series,\n showLegend = series.length > 1,\n showTable = true,\n summary: summaryProp,\n ...rest\n} = Astro.props\n\nconst resolvedLabels = resolveLumenChartLabels(labels)\nconst summary = summaryProp ?? formatLumenChartSummary(series, formatValue, resolvedLabels)\nconst width = 640\nconst height = 320\nconst padding = 44\nconst categories = getLumenChartCategories(series)\nconst hasData = hasLumenChartData(series)\nconst alignedSeries = series.map(item => alignLumenChartSeries(item, categories))\n\nconst domain = getLumenChartDomain([\n ...alignedSeries.flatMap(item => item.data.map(datum => datum.y)),\n referenceValue ?? null\n], false)\n\nconst ticks = getLumenChartTicks(domain)\nconst paddingLeft = getLumenChartAxisPadding(ticks.map(tick => formatValue(tick)))\n\nconst geometries = alignedSeries.map(item => createLumenLineGeometry(item.data, {\n domain,\n height,\n includeZero: false,\n padding,\n paddingLeft,\n width\n}))\n\nconst labelStep = Math.max(1, Math.ceil(categories.length / 8))\nlet markerStep = 1\n\nif (markers === false || markers === 'none') markerStep = Number.POSITIVE_INFINITY\nelse if (typeof markers === 'number') markerStep = Math.max(1, Math.round(markers))\nelse if (markers === 'auto') markerStep = Math.max(1, Math.ceil(categories.length / 24))\n\nconst valueFor = (item: LumenChartSeries, category: number | string) => item.data.find(datum => datum.x === category)\n\nconst categoryLabelFor = (category: number | string) => series.flatMap(item => item.data)\n .find(datum => datum.x === category)?.xLabel ?? formatCategory(category)\n\nconst referenceY =\n referenceValue === undefined ?\n undefined :\n scaleLumenChartValue(referenceValue, domain, height - padding, padding)\n---\n\n<Chart\n class:list={['ui-line-chart', classProp, className]}\n {caption}\n {description}\n {glass}\n {heading}\n {summary}\n {...rest}\n>\n {\n showLegend && hasData && (\n <ul class=\"ui-chart__legend\" aria-label={resolvedLabels.chartLegend}>\n {series.map((item, index) => (\n <li class={getLumenChartToneClassName(item.tone, index)}>\n <span aria-hidden=\"true\" />\n {item.label}\n </li>\n ))}\n </ul>\n )\n }\n {!hasData && <p class=\"ui-chart__empty\" role=\"status\">{emptyLabel ?? resolvedLabels.empty}</p>}\n <div class=\"ui-chart__plot\" hidden={!hasData}>\n <svg\n aria-hidden=\"true\"\n preserveAspectRatio=\"xMidYMid meet\"\n viewBox={`0 0 ${width} ${height}`}\n >\n <g class=\"ui-chart__grid\">\n {\n ticks.map(tick => {\n const y = scaleLumenChartValue(\n tick, domain, height - padding, padding\n )\n\n return (\n <>\n <line x1={paddingLeft} x2={width - padding} y1={y} y2={y} />\n <text x={paddingLeft - 8} y={y}>\n {formatValue(tick)}\n </text>\n </>\n )\n })\n }\n </g>\n <g class=\"ui-chart__axis-labels\">\n {\n categories.map((category, index) => {\n if (\n index % labelStep !== 0 &&\n index !== categories.length - 1\n )\n return null\n\n const denominator = Math.max(1, categories.length - 1)\n\n const x =\n paddingLeft + (index / denominator) * (width - paddingLeft - padding)\n\n return (\n <text text-anchor=\"middle\" x={x} y={height - 14}>\n {categoryLabelFor(category)}\n </text>\n )\n })\n }\n </g>\n {\n referenceY !== undefined && (\n <line\n class=\"ui-chart__reference\"\n x1={paddingLeft}\n x2={width - padding}\n y1={referenceY}\n y2={referenceY}\n />\n )\n }\n {\n geometries.map((geometry, index) => {\n const item = series[index]\n\n if (!item) return null\n\n const tone = resolveLumenChartTone(item.tone, index)\n\n return (\n <g class:list={['ui-line-chart__series', getLumenChartToneClassName(tone)]}>\n {area &&\n geometry.areaPaths.map(path => (\n <path class=\"ui-line-chart__area\" d={path} />\n ))}\n <path class=\"ui-line-chart__line\" d={geometry.path} />\n {Number.isFinite(markerStep) &&\n geometry.points.map((point, pointIndex) => pointIndex % markerStep === 0 && (\n <circle\n class=\"ui-line-chart__point\"\n cx={point.xCoordinate}\n cy={point.yCoordinate}\n r=\"3\"\n >\n <title>\n {`${point.xLabel ?? formatCategory(point.x)} · ${item.label}: ${formatValue(point.y ?? 0)}`}\n </title>\n </circle>\n ))}\n </g>\n )\n })\n }\n </svg>\n </div>\n {\n showTable && hasData && (\n <details class=\"ui-chart__data\">\n <summary>{resolvedLabels.viewData}</summary>\n <div>\n <table>\n <thead>\n <tr>\n <th scope=\"col\">{resolvedLabels.category}</th>\n {series.map(item => <th scope=\"col\">{item.label}</th>)}\n </tr>\n </thead>\n <tbody>\n {categories.map(category => (\n <tr>\n <th scope=\"row\">{categoryLabelFor(category)}</th>\n {series.map(item => {\n const datum = valueFor(item, category)\n\n return (\n <td>\n {datum?.label ??\n (datum?.y === undefined || datum.y === null || !Number.isFinite(datum.y) ?\n resolvedLabels.notAvailable :\n formatValue(datum.y))}\n </td>\n )\n })}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </details>\n )\n }\n</Chart>\n",
14885
14885
  "styleImport": "import '@santi020k/lumen-astro/styles.css'"
14886
14886
  },
14887
14887
  "elements": {
@@ -14974,7 +14974,7 @@
14974
14974
  }
14975
14975
  ],
14976
14976
  "propsExtends": "Omit<ChartProps, 'children'>",
14977
- "source": "export interface LineChartProps extends Omit<ChartProps, 'children'> {\n area?: boolean\n emptyLabel?: ReactNode\n formatCategory?: (category: number | string) => string\n formatValue?: (value: number) => string\n labels?: Partial<LumenChartLabels>\n markers?: 'all' | 'auto' | 'none' | boolean | number\n referenceValue?: number\n series?: readonly LumenChartSeries[]\n showLegend?: boolean\n showTable?: boolean\n}\n\nexport const LineChart = ({\n area = false,\n className,\n emptyLabel,\n formatCategory = String,\n formatValue = String,\n labels,\n markers = 'auto',\n referenceValue,\n series = emptyChartSeries,\n showLegend = series.length > 1,\n showTable = true,\n summary,\n ...props\n}: LineChartProps) => {\n const resolvedLabels = resolveLumenChartLabels(labels)\n const width = 640\n const height = 320\n const padding = 44\n const categories = getLumenChartCategories(series)\n const hasData = hasLumenChartData(series)\n\n const alignedSeries = series.map(item => ({\n ...item,\n data: alignLumenChartSeries(item, categories).data\n }))\n\n const domain = getLumenChartDomain(\n [\n ...alignedSeries.flatMap(item => item.data.map(datum => datum.y)),\n referenceValue ?? null\n ], false\n )\n\n const geometries = alignedSeries.map(item => createLumenLineGeometry(item.data, {\n domain,\n height,\n includeZero: false,\n padding,\n width\n }))\n\n const ticks = getLumenChartTicks(domain)\n const labelStep = Math.max(1, Math.ceil(categories.length / 8))\n const markerStep = getLineChartMarkerStep(markers, categories.length)\n\n const referenceY =\n referenceValue === undefined ?\n undefined :\n scaleLumenChartValue(referenceValue, domain, height - padding, padding)\n\n return (\n <Chart\n className={composeClassName('ui-line-chart', className)}\n summary={summary ?? formatLumenChartSummary(series, formatValue, resolvedLabels)}\n {...props}\n >\n {showLegend && hasData && <ChartLegend label={resolvedLabels.chartLegend} series={series} />}\n {!hasData && (\n <p className=\"ui-chart__empty\" role=\"status\">\n {emptyLabel ?? resolvedLabels.empty}\n </p>\n )}\n <div className=\"ui-chart__plot\" hidden={!hasData}>\n <svg\n aria-hidden=\"true\"\n preserveAspectRatio=\"xMidYMid meet\"\n viewBox={`0 0 ${width} ${height}`}\n >\n <g className=\"ui-chart__grid\">\n {ticks.map(tick => {\n const y = scaleLumenChartValue(\n tick, domain, height - padding, padding\n )\n\n return (\n <Fragment key={tick}>\n <line x1={padding} x2={width - padding} y1={y} y2={y} />\n <text x={padding - 8} y={y}>\n {formatValue(tick)}\n </text>\n </Fragment>\n )\n })}\n </g>\n <g className=\"ui-chart__axis-labels\">\n {categories.map((category, index) => {\n if (index % labelStep !== 0 && index !== categories.length - 1)\n return null\n\n const denominator = Math.max(1, categories.length - 1)\n const x = padding + (index / denominator) * (width - padding * 2)\n\n return (\n <text\n key={getChartCategoryKey(category)}\n textAnchor=\"middle\"\n x={x}\n y={height - 14}\n >\n {series\n .flatMap(item => item.data)\n .find(datum => datum.x === category)?.xLabel ??\n formatCategory(category)}\n </text>\n )\n })}\n </g>\n {referenceY !== undefined && (\n <line\n className=\"ui-chart__reference\"\n x1={padding}\n x2={width - padding}\n y1={referenceY}\n y2={referenceY}\n />\n )}\n {geometries.map((geometry, index) => {\n const item = series[index]\n\n if (!item) return null\n\n const tone = resolveLumenChartTone(item.tone, index)\n\n return (\n <g\n className={composeClassName(\n 'ui-line-chart__series', getLumenChartToneClassName(tone)\n )}\n key={item.id}\n >\n {area &&\n geometry.areaPaths.map(path => (\n <path className=\"ui-line-chart__area\" d={path} key={path} />\n ))}\n <path className=\"ui-line-chart__line\" d={geometry.path} />\n {Number.isFinite(markerStep) &&\n geometry.points.map(\n (point, pointIndex) => pointIndex % markerStep === 0 && (\n <circle\n className=\"ui-line-chart__point\"\n cx={point.xCoordinate}\n cy={point.yCoordinate}\n key={getChartCategoryKey(point.x)}\n r=\"3\"\n >\n <title>\n {`${point.xLabel ?? formatCategory(point.x)} · ${item.label}: ${formatValue(point.y ?? 0)}`}\n </title>\n </circle>\n )\n )}\n </g>\n )\n })}\n </svg>\n </div>\n {showTable && hasData && (\n <ChartDataTable\n categories={categories}\n formatCategory={formatCategory}\n formatValue={formatValue}\n labels={resolvedLabels}\n series={series}\n />\n )}\n </Chart>\n )\n}",
14977
+ "source": "export interface LineChartProps extends Omit<ChartProps, 'children'> {\n area?: boolean\n emptyLabel?: ReactNode\n formatCategory?: (category: number | string) => string\n formatValue?: (value: number) => string\n labels?: Partial<LumenChartLabels>\n markers?: 'all' | 'auto' | 'none' | boolean | number\n referenceValue?: number\n series?: readonly LumenChartSeries[]\n showLegend?: boolean\n showTable?: boolean\n}\n\nexport const LineChart = ({\n area = false,\n className,\n emptyLabel,\n formatCategory = String,\n formatValue = String,\n labels,\n markers = 'auto',\n referenceValue,\n series = emptyChartSeries,\n showLegend = series.length > 1,\n showTable = true,\n summary,\n ...props\n}: LineChartProps) => {\n const resolvedLabels = resolveLumenChartLabels(labels)\n const width = 640\n const height = 320\n const padding = 44\n const categories = getLumenChartCategories(series)\n const hasData = hasLumenChartData(series)\n\n const alignedSeries = series.map(item => ({\n ...item,\n data: alignLumenChartSeries(item, categories).data\n }))\n\n const domain = getLumenChartDomain(\n [\n ...alignedSeries.flatMap(item => item.data.map(datum => datum.y)),\n referenceValue ?? null\n ], false\n )\n\n const ticks = getLumenChartTicks(domain)\n const paddingLeft = getLumenChartAxisPadding(ticks.map(tick => formatValue(tick)))\n\n const geometries = alignedSeries.map(item => createLumenLineGeometry(item.data, {\n domain,\n height,\n includeZero: false,\n padding,\n paddingLeft,\n width\n }))\n\n const labelStep = Math.max(1, Math.ceil(categories.length / 8))\n const markerStep = getLineChartMarkerStep(markers, categories.length)\n\n const referenceY =\n referenceValue === undefined ?\n undefined :\n scaleLumenChartValue(referenceValue, domain, height - padding, padding)\n\n return (\n <Chart\n className={composeClassName('ui-line-chart', className)}\n summary={summary ?? formatLumenChartSummary(series, formatValue, resolvedLabels)}\n {...props}\n >\n {showLegend && hasData && <ChartLegend label={resolvedLabels.chartLegend} series={series} />}\n {!hasData && (\n <p className=\"ui-chart__empty\" role=\"status\">\n {emptyLabel ?? resolvedLabels.empty}\n </p>\n )}\n <div className=\"ui-chart__plot\" hidden={!hasData}>\n <svg\n aria-hidden=\"true\"\n preserveAspectRatio=\"xMidYMid meet\"\n viewBox={`0 0 ${width} ${height}`}\n >\n <g className=\"ui-chart__grid\">\n {ticks.map(tick => {\n const y = scaleLumenChartValue(\n tick, domain, height - padding, padding\n )\n\n return (\n <Fragment key={tick}>\n <line x1={paddingLeft} x2={width - padding} y1={y} y2={y} />\n <text x={paddingLeft - 8} y={y}>\n {formatValue(tick)}\n </text>\n </Fragment>\n )\n })}\n </g>\n <g className=\"ui-chart__axis-labels\">\n {categories.map((category, index) => {\n if (index % labelStep !== 0 && index !== categories.length - 1)\n return null\n\n const denominator = Math.max(1, categories.length - 1)\n const x = paddingLeft + (index / denominator) * (width - paddingLeft - padding)\n\n return (\n <text\n key={getChartCategoryKey(category)}\n textAnchor=\"middle\"\n x={x}\n y={height - 14}\n >\n {series\n .flatMap(item => item.data)\n .find(datum => datum.x === category)?.xLabel ??\n formatCategory(category)}\n </text>\n )\n })}\n </g>\n {referenceY !== undefined && (\n <line\n className=\"ui-chart__reference\"\n x1={paddingLeft}\n x2={width - padding}\n y1={referenceY}\n y2={referenceY}\n />\n )}\n {geometries.map((geometry, index) => {\n const item = series[index]\n\n if (!item) return null\n\n const tone = resolveLumenChartTone(item.tone, index)\n\n return (\n <g\n className={composeClassName(\n 'ui-line-chart__series', getLumenChartToneClassName(tone)\n )}\n key={item.id}\n >\n {area &&\n geometry.areaPaths.map(path => (\n <path className=\"ui-line-chart__area\" d={path} key={path} />\n ))}\n <path className=\"ui-line-chart__line\" d={geometry.path} />\n {Number.isFinite(markerStep) &&\n geometry.points.map(\n (point, pointIndex) => pointIndex % markerStep === 0 && (\n <circle\n className=\"ui-line-chart__point\"\n cx={point.xCoordinate}\n cy={point.yCoordinate}\n key={getChartCategoryKey(point.x)}\n r=\"3\"\n >\n <title>\n {`${point.xLabel ?? formatCategory(point.x)} · ${item.label}: ${formatValue(point.y ?? 0)}`}\n </title>\n </circle>\n )\n )}\n </g>\n )\n })}\n </svg>\n </div>\n {showTable && hasData && (\n <ChartDataTable\n categories={categories}\n formatCategory={formatCategory}\n formatValue={formatValue}\n labels={resolvedLabels}\n series={series}\n />\n )}\n </Chart>\n )\n}",
14978
14978
  "styleImport": "import '@santi020k/lumen-react/styles.css'"
14979
14979
  }
14980
14980
  },
@@ -35314,11 +35314,11 @@
35314
35314
  }
35315
35315
  ],
35316
35316
  "docs": {
35317
- "aiUsage": "# AI Usage Guide\n\nUse this guide when an AI agent is building an app with Lumen or updating examples that teach\nanother agent how to use the library.\n\n## Install in ChatGPT or Codex\n\nThe [Lumen UI plugin](https://chatgpt.com/plugins/plugin_asdk_app_6a8f6c526c5481918eb8a48806fa112b)\nis published in the shared Plugins Directory for ChatGPT and Codex.\n\n1. Open the listing and select **Install plugin**. Sign in to ChatGPT if prompted.\n2. Start a request with **@Lumen UI**, or choose one of the listing's starter prompts.\n3. Describe the interface and target framework, for example: “Find the right Lumen components for\n an accessible React settings screen.”\n\nThe plugin combines the Lumen workflow skill with the hosted public MCP catalog. It requires no\nseparate Lumen account, API key, or local server. Its tools only read public Lumen documentation;\nthey do not access private repositories or modify files and services. Your coding agent performs\nimplementation work using the retrieved contracts and its own permissions.\n\nSee the [publication record](./openai-plugin-submission.md) for version and verification details.\n\n## Install the Lumen Agent Skill\n\nLumen ships a portable Agent Skill for AI coding tools that support the open `SKILL.md` format:\n\n```bash\nnpx skills add santi020k/lumen --skill lumen-ui\n```\n\nThe source lives in [`skills/lumen-ui`](../skills/lumen-ui). The skill provides the durable\nframework-selection, component-composition, theming, accessibility, and verification workflow.\nUse it by naming `$lumen-ui` in a prompt, or let a compatible agent trigger it from Lumen-specific\nrequests.\n\nThe skill and MCP server complement each other: the skill explains how to work, while\n`@santi020k/lumen-mcp` supplies structured catalog search, framework-specific contracts, recipes,\nsource, tokens, resources, and rules.\n\n## Pick the Right Package\n\nUse the framework requested by the user. Every adapter shares the same Lumen foundation.\n\n| App Target | Install | Import Components From | Load Styles From |\n| -------------------------- | ---------------------------------------------------------------------- | -------------------------------------------- | -------------------------------------- |\n| Astro | `@santi020k/lumen-astro` | `@santi020k/lumen-astro` | `@santi020k/lumen-astro/styles.css` |\n| React | `@santi020k/lumen-react` | `@santi020k/lumen-react` | `@santi020k/lumen-react/styles.css` |\n| React Hook Form composites | `@santi020k/lumen-react-hook-form` | `@santi020k/lumen-react-hook-form` | Uses React styles |\n| Web Components | `@santi020k/lumen-elements` | `@santi020k/lumen-elements/define` | `@santi020k/lumen-elements/styles.css` |\n| React Native / Expo | `@santi020k/lumen-react-native` | `@santi020k/lumen-react-native` | Not applicable |\n| Apple / SwiftUI | Swift Package `https://github.com/santi020k/lumen`, pinned to `3.0.0` | `LumenUI` | Not applicable |\n| Android / Compose | Maven Central `com.santi020k:lumen-compose:3.0.0` | `com.santi020k.lumen` | Not applicable |\n| Wear OS / Compose | Maven Central `com.santi020k:lumen-compose-wear:3.0.0` | `com.santi020k.lumen` | Not applicable |\n| Package metadata | `@santi020k/lumen-core` | `@santi020k/lumen-core` | Not applicable |\n| Optional web brand icons | `@santi020k/lumen-icons-brand` | Register once, then use the framework `Icon` | Uses framework styles |\n\nEach framework package includes the shared foundation. Install `@santi020k/lumen` separately only\nwhen you need its framework-neutral CLI or registry metadata.\n\nFor React Native, mount one `LumenProvider` near the app root. For SwiftUI, attach the `LumenUI`\nSwift Package product to the application target, use an exact or compatible release-version rule,\nand apply `.lumenTheme(...)` near the root. For Compose, install\n`com.santi020k:lumen-compose:3.0.0` from Maven Central and wrap content in `LumenTheme`. Native\nadapters do not load CSS or the Astro runtime.\n\nReact Native can use ordinary React hooks and platform-neutral application hooks. Import native\nLumen controllers such as `useDialog`, `useTabs`, `useSelect`, `useLanguageToggle`,\n`useThemeToggle`, and `useToast` from `@santi020k/lumen-react-native`; their state semantics track\nthe reusable React contracts while their returned props target native Lumen components. Never\nimport DOM-dependent hooks from `@santi020k/lumen-react` into a native bundle. Share business state\nin an application workspace package and keep the Lumen rendering adapter at each app boundary.\n\nFor watchOS, use the focused `LumenWatch*` contracts from the same Swift package. For Wear OS,\ninstall `com.santi020k:lumen-compose-wear:3.0.0` and wrap wearable content in `LumenWearTheme`\ninside the application-owned Wear Material theme. Keep complications, tiles, rotary input,\nAlways On behavior, haptics, synchronization, navigation, and health or safety logic app-owned.\n\nBrand marks are intentionally excluded from the default web Lucide bundle. In Astro, React, or Web\nComponents, install and register the optional pack once before rendering a namespaced icon:\n\n```ts\nimport { registerLumenBrandIcons } from \"@santi020k/lumen-icons-brand\";\n\nregisterLumenBrandIcons();\n```\n\nThen use any canonical Font Awesome Free brand name, such as `brand:github`, `brand:discord`,\n`brand:figma`, `brand:linkedin`, `brand:whatsapp`, or `brand:youtube`, through the existing Astro,\nReact, or Web Component `Icon` API. The package also provides `brand:x` as an alias for\n`brand:x-twitter`.\n\nBrand marks keep their recognizable filled geometry instead of being redrawn as Lucide outlines.\nThey still inherit the same Lumen size, alignment, `currentColor`, and accessible-label behavior as\nthe default icon catalog. React Native, SwiftUI, and Compose already include the corresponding\ncomplete brand catalog and accept `brand:github`, `.brandGithub`, and\n`LumenIconName.BrandGithub`, respectively, without a separate package.\n\n## MCP-Assisted Workflow\n\nConnect `@santi020k/lumen-mcp` when the agent supports Model Context Protocol. The server returns\nvalidated structured content as well as readable text, so agents can select components without\nscraping documentation.\n\nThe MCP per-component catalog covers Astro, React, Web Components, React Native, SwiftUI, and\nCompose. Use `lumen_list_native_components` to discover platform availability and\n`lumen_get_native_component` for installation, setup, API, examples, accessibility guidance, and\noptional reference source. The skill still verifies installed packages when local versions differ.\n\n```bash\ncodex mcp add lumen -- npx -y @santi020k/lumen-mcp\n```\n\nFor Codex project configuration:\n\n```toml\n[mcp_servers.lumen]\nenabled = true\ncommand = \"npx\"\nargs = [\"-y\", \"@santi020k/lumen-mcp\"]\n```\n\nUse this sequence:\n\n1. Read `lumen://meta` and `lumen://diagnostics` to identify and verify the snapshot.\n2. Read `lumen://rules`.\n3. Search for the use case with `lumen_search`, passing the target `framework` when known.\n4. Inspect the selected framework with `lumen_get_component` and `detail: \"usage\"`.\n5. Follow its framework behavior setup and hook/controller contract.\n6. Read related multi-component guidance with `lumen_get_recipe`.\n7. Request `detail: \"source\"` only when the usage contract is insufficient.\n8. Read `lumen://tokens` before introducing custom styling.\n9. Retain `lumen://catalog-manifest` and use `lumen_diff_catalog` after upgrades to identify contracts that need refreshing.\n\nExample framework-aware component request:\n\n```json\n{\n \"name\": \"lumen_get_component\",\n \"arguments\": {\n \"name\": \"DateRangePicker\",\n \"framework\": \"react\",\n \"detail\": \"usage\"\n }\n}\n```\n\nStable resources are available at `lumen://rules`, `lumen://tokens`, `lumen://components`,\n`lumen://meta`, `lumen://catalog-manifest`, `lumen://diagnostics`,\n`lumen://components/{name}`, and `lumen://recipes/{name}`. See\n[`packages/mcp/README.md`](../packages/mcp/README.md) for Claude Desktop, Cursor, generic stdio,\nlocal repository, and programmatic examples.\n\n## Minimal Setup\n\nImport the stylesheet once in the app's global CSS, root layout, or app entry.\n\nThis section applies to web targets. Native adapters use their provider or theme APIs instead of a\nstylesheet.\n\n```css\n@import \"@santi020k/lumen-react/styles.css\";\n```\n\nIf Tailwind is present, keep both imports in the same shared CSS entry.\n\n```css\n@import \"@santi020k/lumen-react/layers.css\";\n@import \"tailwindcss\";\n@import \"@santi020k/lumen-react/styles.css\";\n```\n\nKeep the layer prelude first. It places Tailwind base styles below Lumen components and Tailwind\nutilities above them, so consumer spacing, display, radius, width, and responsive visibility\nclasses override Lumen defaults predictably.\n\nAstro apps should mount `UIPrimitives` once in the root layout when using interactive primitives\nsuch as dialogs, menus, popovers, tabs, carousels, command lists, toggles, validated forms, or\ntoasts. Do not place `UIPrimitives` in each component example or next to each primitive instance;\none root include enhances all matching `data-ui-*` markup on the page.\n\n```astro\n---\nimport UIPrimitives from '@santi020k/lumen-astro/runtime'\n---\n\n<UIPrimitives />\n```\n\n## Astro\n\nUse direct named imports from `@santi020k/lumen-astro`.\n\n```astro\n---\nimport { Button, Card, Input, Label } from '@santi020k/lumen-astro'\n---\n\n<Card>\n <Label for=\"email\">Email</Label>\n <Input id=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button type=\"submit\">Subscribe</Button>\n</Card>\n```\n\n`Stat` defaults to a `div`. Choose `as=\"article\"` for a self-contained metric with supporting\ncontent, or `as=\"section\"` for a labeled region. Do not use a semantic root solely for styling.\nIts visual variants are `default`, `accent`, and `glass`; reserve `accent` for featured metrics and\nkeep glass selective.\n\nFor long reading surfaces, use `Anchor` for local section navigation and pass each item’s optional\nheading `depth`, `index`, and `description` when hierarchy matters. Use `activationOffset` for fixed\napplication headers. Use `ScrollProgress` for document position; keep `Progress` for\napplication-controlled task completion. In Astro, dispatch `ui:progress-change` with\n`{ value, max? }` from the `Progress` root when browser state changes after hydration. `Pill`\naccepts `neutral`, `brand`, and `outline`\nvariants, plus native link and count composition.\n\n## React\n\nReact components share the same visual classes and data attributes as Astro components. For\nbehavior-heavy primitives, use the React behavior hooks from `@santi020k/lumen-react` (`useDialog`,\n`usePopover`, `useDropdownMenu`, `useTabs`, `useSelect`, `useToast`, and `useTooltip`), which track\nthe Astro runtime's ARIA, keyboard, Escape, dismissal, and toast controller semantics.\n\nThe React package entry is a client boundary. In Next.js, it can be imported by a Server Component\nwithout evaluating Lumen's contexts and hooks in the React server runtime; the rendered Lumen\nsubtree becomes a Client Component boundary. Vite and other client-rendered React applications use\nthe same entry without additional setup.\nFor stateless Badge, Card composition, layout, form-label, progress, loading, typography, and\nvisually-hidden usage in a React Server Component, import from\n`@santi020k/lumen-react/server`. That entrypoint contains the implementations directly and avoids\ncreating a client boundary. Keep event handlers, hooks, and interactive primitives on the package\nroot inside a Client Component.\n`Button`, `Link`, `ButtonLink`, and `Input` accept React 19 DOM refs directly as props.\n`Button asChild` composes the button contract onto one child element. Across adapters, native\nnumeric `size` remains available on `Input` and `NativeSelect`; use `visualSize` in Astro and React,\nor `visual-size` in Elements, for presentation.\n`Stat as` and `variant` follow the same semantic-root and visual guidance as the Astro component.\n\n```tsx\nimport \"@santi020k/lumen-react/styles.css\";\nimport { Button, Card, Input, Label } from \"@santi020k/lumen-react\";\n\nexport function SubscribeForm() {\n return (\n <Card>\n <Label htmlFor=\"email\">Email</Label>\n <Input id=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button type=\"submit\">Subscribe</Button>\n </Card>\n );\n}\n```\n\nFor React Hook Form, spread `register()` directly onto native-backed controls. Install\n`@santi020k/lumen-react-hook-form` only for controlled composites such as `Select`, `DatePicker`,\n`InputOTP`, and `ListBox`. Do not combine React Hook Form and `useFormValidation` unless the\napplication intentionally coordinates two validation sources. For schema validation, use\n`zodResolver` or `yupResolver` from `@hookform/resolvers`; keep Zod or Yup in the application and\nrender the resulting React Hook Form errors through Lumen's `FieldError` contract.\n\n## Web Components\n\nRegister custom elements once, then use `lumen-*` tags in HTML. The elements adapter includes\ninteractive behavior parity for DataTable, Dialog, Popover, DropdownMenu, ContextMenu, Tabs,\nSelect, ThemeBuilder, Toast, Tooltip, forms, RichTextEditor, Schedule, and VirtualList: selection,\nvalidation, rich text command, context menu, schedule drag/drop, theme, and range events, ARIA\nstate, roving/listbox keyboard paths, Escape/outside dismissal, focus return/trapping, native\nselect form participation, and the same toast controller events as the Astro runtime.\n\n```html\n<script type=\"module\">\n import { defineLumenElements } from \"@santi020k/lumen-elements/define\";\n\n defineLumenElements();\n</script>\n\n<lumen-card>\n <label for=\"email\">Email</label>\n <lumen-input\n id=\"email\"\n type=\"email\"\n placeholder=\"you@example.com\"\n ></lumen-input>\n <lumen-button type=\"submit\">Subscribe</lumen-button>\n</lumen-card>\n```\n\nFor a surface that uses only Badge, Button, Card, and Combobox, import\n`@santi020k/lumen-elements/components/badge`, `/button`, `/card`, and `/combobox` and call their\ncorresponding `defineLumenBadge`, `defineLumenButton`, `defineLumenCard`, and `defineLumenCombobox`\nfunctions. Those implementation-level entrypoints avoid the complete catalog bundle; the Combobox\nentrypoint includes its filtering and keyboard behavior. Continue using `defineLumenElements` for\nany component without a granular entrypoint.\n\nFor a foundation-only custom-element surface, import `defineLumenFoundations` from\n`@santi020k/lumen-elements/components/foundations`. It registers Card compound parts, Container,\nDirection, Grid, Label, Separator, Skeleton, Spinner, Stack, Typography, and VisuallyHidden without\npulling the complete catalog implementation.\n\nPut input elements inside a native `<form data-ui-form>`. Scalar Lumen controls participate in\n`FormData`, validity, disabled state, focus, and reset through form-associated custom-element\nbehavior with a native-control fallback. Do not generate `<lumen-form>` as the submission\ncontainer.\n\n## Component Selection\n\nThe shared catalog includes:\n\n`Accordion`, `Alert`, `AlertDialog`, `Agenda`, `AspectRatio`, `Attachment`, `Autocomplete`,\n`Avatar`, `Badge`, `BarChart`, `Breadcrumb`, `Bubble`, `Button`, `ButtonGroup`, `Calendar`, `Card`,\n`Carousel`, `Chart`, `Checkbox`, `CheckboxGroup`, `Collapsible`, `Code`, `Combobox`, `Command`,\n`Container`, `ColorPicker`,\n`ContextMenu`, `DataTable`, `DatePicker`, `DateRangePicker`, `Dialog`, `Direction`, `Drawer`,\n`DropdownMenu`, `Empty`, `ErrorState`, `ErrorSummary`, `Field`, `FieldError`, `Form`, `Grid`, `HoverCard`, `Icon`,\n`Graphic`, `Backdrop`, `Illustration`, `Input`, `InputGroup`, `InputOTP`, `Item`, `KanbanBoard`, `KanbanColumn`, `Kbd`, `Label`, `LineChart`, `ListBox`, `Marker`, `Menubar`,\n`Message`, `MessageScroller`, `NativeSelect`, `ContextNavigation`, `NavigationMenu`, `NumberField`, `Pagination`,\n`PasswordField`, `PieChart`, `Popover`, `Progress`, `CopyButton`, `RadioGroup`, `Resizable`, `RichTextEditor`,\n`ScrollArea`, `Schedule`, `SearchField`, `Select`, `Separator`, `Sheet`, `Sidebar`, `Skeleton`,\n`Slider`, `Sparkline`, `Spinner`, `Stack`, `Switch`, `Table`, `Tabs`, `TagGroup`, `Textarea`, `ThemeBuilder`,\n`TimeField`, `Toast`, `Toggle`, `ToggleGroup`, `Tooltip`, `Tree`, `TreeGrid`, `Typography`, and\n`VirtualList`, and `VisuallyHidden`.\n\n## Product Recipes\n\n- Follow [the error-handling guide](./error-handling.md) when a generated interface needs failure\n feedback. Use `FieldError` and `ErrorSummary` for validation, `Alert` for persistent inline\n problems, `Toast` for brief non-blocking failures, and `ErrorState` only when a page or region\n cannot show its primary content. Keep exception capture, logging, retry policy, and safe message\n mapping in application code. For React Native, SwiftUI, and Compose, follow the guide's native API\n mapping and announcement rules; do not invent a cross-platform error manager inside the component\n layer.\n\n- Start full product surfaces from the bundled `analytics-dashboard`, `saas-admin`,\n `commerce-dashboard`, `project-workspace`, or `auth-onboarding` recipe. Install with\n `lumen add <recipe>` for Astro, or add `--target react` / `--target elements`. Keep the generated\n shared template stylesheet, then replace sample data and routes while preserving public Lumen\n components, semantic tokens, accessible names, focus paths, and responsive navigation.\n- Use `Sparkline` for a compact trend beside a labelled metric, `BarChart` for categorical\n comparison, `LineChart` for ordered or time-series data, `PieChart` for a small\n part-to-whole breakdown. Pass already-aggregated `LumenChartSeries` data; keep statistics,\n resampling, model evaluation, and domain calculations in application code. Keep `showTable`\n enabled unless the same values are already available in a nearby semantic table. Prefer a bar\n chart when precise slice comparison matters or the pie would exceed roughly seven categories.\n Use `ScatterChart` for numeric relationships, `Heatmap` for a labeled matrix, `RangeChart` for\n low-to-high intervals, and `ComboChart` only when mixed marks share a meaningful domain. Use\n `Chart` as the custom SVG/canvas escape hatch. Preserve `null` gaps and the default accessible\n summary and fallback data. See `docs/data-visualization.md` for the full contract.\n Pass `labels` to replace every library-owned visible and assistive phrase. Finite zero is valid\n data, while an empty chart has no finite values; do not silently reinterpret an all-zero series\n as missing. Set `showTable={false}` (`showData={false}` on native adapters) only when an\n equivalent accessible table or scoreboard is adjacent.\n Keep stable identity in `datum.x` and put display text in `datum.xLabel`. Use `formatCategory`\n and `formatValue` for localized axes, SVG titles, and data tables. Choose\n `presentation=\"bare\"` inside an existing Card, `categoryWidth` for long horizontal labels,\n `markers=\"auto\"` or a numeric marker step for dense lines, and `emptyLabel` for actionable\n source-specific guidance.\n- Use `Icon name=\"search\"` or any Lucide icon name for standard interface pictograms. Kebab-case\n names and Lucide aliases work. Pair decorative icons with `decorative`, and pass `label` only\n when the icon carries meaning on its own. On the web, install and register\n `@santi020k/lumen-icons-brand` for supported brand marks; native adapters include those names.\n Then use `brand:github` or the adapter's generated equivalent. Render project-specific or\n unsupported brand SVGs through the default `Icon` slot. Astro reports unknown named icons during\n development instead of failing silently.\n- Use `Schedule` with `Agenda`, `Calendar`, and `DatePicker` for booking, CRM, content calendar, and\n event planning screens. Mark slots with `data-ui-schedule-slot` and events with\n `data-ui-schedule-event`; the runtime emits `ui:schedule-change` when a draggable event is moved.\n `Calendar` renders a hidden input plus selectable date grid; Astro `UIPrimitives` and registered\n Web Components wire month navigation and roving keyboard focus directly, while React apps can use\n the `Calendar` component or `useCalendar` for the same date selection contract.\n Astro `UIPrimitives` and registered Web Components bind schedule drag/drop directly; React apps\n can use `useSchedule` for `rootProps`, `getEventProps`, `getSlotProps`, and `ui:schedule-change`\n parity. Use `createScheduleSlots`, `moveScheduleEvent`, and `expandRecurringScheduleEvent` from\n `@santi020k/lumen-core` for reusable schedule state; use `getScheduleConflicts` and\n `canPlaceScheduleEvent` before committing drag/drop changes. Persist local schedule edits with\n `createScheduleStorageKey`, `saveScheduleEvents`, and `loadScheduleEvents` against any\n `localStorage`-compatible adapter. Use `resizeScheduleEvent` and `resizeScheduleEvents` for\n snap-to-grid resize handles.\n- Use `KanbanBoard` and `KanbanColumn` for status-based workspaces while continuing to use `Card` for\n items. Mark items with `data-ui-kanban-item` and put `data-ui-kanban-handle` on a dedicated button;\n do not make the whole card draggable. Astro and Elements emit the cancellable, controlled\n `ui:kanban-move-request` event, and React provides `useKanban`. The event reports `itemId`,\n `fromColumn`, `toColumn`, optional `beforeId`, and keyboard or pointer input, but never moves DOM or\n persists application data. The host owns validation, `aria-busy`, optimistic state, rollback,\n status names, and APIs. Use compact `Empty` states inside columns, preserve every column with\n `Skeleton` blocks while loading, and paginate large columns. See [Kanban composition and movement](kanban.md).\n- Use `DataTable`, `Tree`, `TreeGrid`, `VirtualList`, `Pagination`, and `Command` for dense data\n collection workflows. Astro and Elements wire selectable/sortable `DataTable` behavior and\n `VirtualList` range events; React emits the same data attributes for app-level adapters. `Tree`\n and `TreeGrid` use roving keyboard focus, and `VirtualList` emits `ui:virtual-list-range` as the\n rendered range changes. Use `createDataViewState`,\n `serializeDataViewState`, `parseDataViewState`, `applyDataViewState`, and `getVirtualRange` for\n saved views, filtering, sorting, pagination, and virtualization math. Use\n `createDataViewStorageKey`, `saveDataViewState`, and `loadDataViewState` when saved views should\n survive navigation or reloads. Use `pinDataViewColumn`, `unpinDataViewColumn`,\n `toggleDataViewSelection`, `createDataViewSearchParams`, and `createDataViewServerRequest` when\n connecting dense views to router state or server-backed data.\n- Use `Resizable` for split-pane editors, dashboards, inspectors, and side-by-side review layouts.\n Astro `UIPrimitives` and registered Web Components generate separator handles and pane sizing\n directly; React apps can use the `Resizable` component or `useResizable` for pointer, keyboard,\n min/max, and double-click reset parity.\n- Use `ThemeBuilder`, `ColorPicker`, `Card`, `Button`, and `Input` to build token preview and export\n tools. Add `data-ui-theme-brand-hue`, `data-ui-theme-accent-hue`, `data-ui-theme-scheme`,\n `data-ui-theme-mode`, manual color inputs such as `data-ui-theme-primary-hex`, and\n `data-ui-theme-target` for scoped live previews. Add `data-ui-theme-export-format`,\n `data-ui-theme-export`, and `data-ui-theme-output` to copy CSS, Figma variable JSON, or design\n token JSON. Astro `UIPrimitives` and registered Web Components wire those controls directly;\n React apps can use `useThemeBuilder` for the same tokens, exports, and control props. Use\n `createThemePalette`, `createThemeBuilderTokens`, `exportThemeBuilderValue`, `exportThemeCss`,\n `parseThemeCss`, and `scoreThemeContrast`\n for theme import/export and accessibility checks. Use `suggestReadableInk`, `mergeThemeTokens`,\n and `tuneThemeContrast` when generated palettes need readable foreground repair.\n- Use `RichTextEditor` with `ButtonGroup`, `ToggleGroup`, and `Textarea` for editor compositions.\n Controls with `data-ui-editor-command` dispatch browser editing commands and emit\n `ui:editor-command`; use `data-ui-editor-value` for headings, links, and other value-bearing\n commands. Editable surfaces support common formatting shortcuts, synchronize toggle\n `aria-pressed` state, and emit `ui:editor-change` with HTML and plain text. Astro `UIPrimitives`\n and registered Web Components bind those controls directly; React apps can use\n `useRichTextEditor` for `rootProps`, `getCommandProps`, `getEditableProps`, and `executeCommand`.\n For Tiptap, ProseMirror, Lexical, or Markdown engines, keep the external editor\n package in the app and bridge toolbar buttons through the emitted command event instead of adding\n the editor engine to Lumen.\n- Use `Autocomplete`, `SearchField`, `NumberField`, `TimeField`, `DateRangePicker`, `ColorPicker`,\n and `TagGroup` when forms need more than plain text inputs. Add `data-ui-form` to forms that\n should reflect native Constraint Validation API state into `Field` error slots on blur and submit.\n For `InputOTP`, Astro `UIPrimitives` and registered Web Components render/sync a real native input\n with visual OTP segments; React apps can use the `InputOTP` component or `useInputOTP` for the\n same sanitization, paste handling, and caret navigation.\n For `DateRangePicker`, Astro `UIPrimitives` and registered Web Components sync the paired native\n date input `min`/`max` constraints directly; React apps can use `useDateRangePicker` for the same\n start/end syncing props.\n Use native attributes such as `required`, `pattern`, `min`, `max`, and `type`; add\n `data-error-required`, `data-error-pattern`, `data-error-min`, `data-error-max`, or\n `data-error-custom` when a control needs custom copy. Listen for `ui:validate` to call\n `control.setCustomValidity(message)` before the runtime reads validity, and listen for\n `ui:invalid` or `ui:valid` on the form for submission state. Astro `UIPrimitives` and registered\n Web Components bind `data-ui-form` directly; React apps can use `useFormValidation` with `Field`\n to get the same control props, CustomEvents, and error slot updates.\n- Use `Toast` for transient feedback. Static `<Toast>` markup works without\n JavaScript. With Astro `UIPrimitives` or registered Web Components, create toasts with\n `window.LumenToast.create(detail)`, `LumenToast.create(detail)` from `@santi020k/lumen-elements`,\n or `document.dispatchEvent(new CustomEvent('ui:toast', { detail }))`; update them with\n `.update(id, detail)` or `ui:toast-update`; and dismiss them with `.dismiss(id?)` or\n `ui:toast-dismiss`. `detail` supports `title`, `description`, `variant`, `duration`, `placement`,\n `max`, and `action: { label, value }`. Destructive toasts use `role=\"alert\"`; other variants use\n `role=\"status\"`. Runtime toasts pause their timeout on hover or focus, and Escape dismisses the\n focused toast.\n\n## Runtime CustomEvents\n\nWhen `UIPrimitives` is mounted, or when Web Components are registered for matching behavior, Lumen\nuses these CustomEvents:\n\n| Event | Target | Detail | Fires When |\n| -------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |\n| `ui:data-table-selection-change` | `DataTable` root `[data-ui-datatable]` | `{ values: string[] }` | Selectable row checkboxes, the select-all checkbox, or form reset changes selected row values. |\n| `ui:schedule-change` | `Schedule` root `[data-ui-schedule]` | `{ eventId?: string, slot?: string }` | A draggable schedule event is dropped on a `[data-ui-schedule-slot]`. |\n| `ui:virtual-list-range` | `VirtualList` root `[data-ui-virtual-list]` | `{ startIndex: number, endIndex: number }` | The virtual list calculates its visible range on init or scroll. |\n| `ui:kanban-move-request` | `KanbanBoard` root `[data-ui-kanban]` | `{ itemId: string, fromColumn: string, toColumn: string, beforeId?: string, input: 'keyboard' \\| 'pointer' }` | A handle requests a controlled move; the event is cancellable and never moves data or DOM. |\n| `ui:tag-remove` | `TagGroup` root `.ui-tag-group` or `[data-ui-tag-group]` | `{ value?: string }` | A `[data-ui-tag-remove]` control removes its closest tag or list item. |\n| `ui:editor-command` | `RichTextEditor` root `[data-ui-rich-text-editor]` | `{ command: string, executed: boolean, value?: string }` | A toolbar control or keyboard shortcut runs an editor command. |\n| `ui:editor-change` | `RichTextEditor` root `[data-ui-rich-text-editor]` | `{ html: string, text: string }` | Editable content changes or an editor command runs. |\n| `ui:theme-change` | `ThemeBuilder` root `[data-ui-theme-builder]` | `{ hue: number, accentHue: number, mode: 'generated' \\| 'manual', scheme: 'dark' \\| 'light', tokens: Record<string, string> }` | Hue, manual color, mode, or scheme controls update generated tokens. |\n| `ui:theme-export` | `ThemeBuilder` root `[data-ui-theme-builder]` | `{ css?: string, format: 'css' \\| 'figma' \\| 'tokens', tokens: Record<string, string> \\| null, value: string }` | A `[data-ui-theme-export]` control writes the selected export to the output and clipboard when available. |\n| `ui:language-change` | `LanguageToggle` control | `{ previousValue: string, value: string }` | Requests or applies the next configured locale. |\n| `ui:validate` | Form `[data-ui-form]` | `{ control, form, value }` | Before validation state is read, so custom code can call `control.setCustomValidity(message)`. |\n| `ui:invalid` | Form `[data-ui-form]` | `{ control?, controls?, form }` | Blur or submit validation finds invalid controls. Failed submits focus the first invalid control. |\n| `ui:valid` | Form `[data-ui-form]` | `{ control?, controls?, form }` | Blur or submit validation finds the checked control or form valid. |\n| `ui:toast` | `document` | `{ id?, title?, description?, variant?, duration?, placement?, action? }` | Creates a runtime toast in the matching notification viewport. |\n| `ui:toast-update` | `document` | `{ id, title?, description?, variant?, duration? }` | Updates a runtime toast. |\n| `ui:toast-dismiss` | `document` | `{ id? }` | Dismisses one runtime toast, or all runtime toasts when `id` is omitted. |\n| `ui:toast-action` | Toast `[data-ui-toast]` | `{ id, value? }` | A runtime toast action button is pressed, unless the action specifies a custom event name. |\n\n## Registry Helper\n\n`@santi020k/lumen` exports typed component, recipe, and documentation metadata from\n`@santi020k/lumen/registry`. The umbrella package and every web adapter expose the same small\n`lumen` binary, so projects that install Astro, React, or Elements can use discovery and diagnostics\nwithout another direct dependency. Individual components are addressable by name, including\nkebab-case aliases such as `data-table`:\n\n```bash\nlumen list\nlumen show Button\nlumen show data-table\nlumen add Button\nlumen add Button --target react\nlumen add Button --target elements\nlumen add data-table\nlumen show scheduler\nlumen add scheduler\nlumen add scheduler --target react\nlumen add scheduler --target elements\nlumen add scheduler --dry-run\nlumen add scheduler --fail-on-conflict\nlumen add scheduler --force\nlumen add scheduler --merge\nlumen list --registry ./registry/lumen.registry.json\nlumen add custom-recipe --registry ./registry/custom.registry.json\nlumen add private-recipe --registry https://example.com/lumen.registry.json --registry-token \"$TOKEN\"\nlumen install\nlumen doctor --json\nlumen doctor-native --json\nlumen init --framework astro --tailwind\n```\n\n`lumen doctor-native` emits the same adoption inventory and Android toolchain preflight in readable\ntext or JSON. Treat direct-primitive matches as review suggestions, not rewrite instructions: first\nconfirm whether a surface is product-owned and whether the public Lumen contract preserves its\nbehavior. A failed JDK, SDK, platform-package, or required-NDK preflight should be fixed before\nstarting Gradle so native compilation does not obscure the setup problem.\n\n`lumen add <component>` defaults to an Astro wrapper. Use `--target react` for a local React wrapper\nor `--target elements` for a custom-elements starter. Bundled recipes also accept `--target astro`,\n`--target react`, and `--target elements`; external registry inline files keep their authored paths\nand sources. Those paths must be unique, relative forward-slash paths: the installer rejects\nabsolute paths, parent traversal, and symbolic-link path segments before writing any file. Registry\nmanifests are limited to 5 MiB by default; programmatic `loadLumenRegistry` callers can lower the\nlimit with `maxBytes` and control remote request time with `timeoutMs`.\n\nBefore inventing a wrapper or custom class pattern, check the component source in\n`packages/astro/components`. Astro is the reference implementation for props, class names, data\nattributes, and accessible markup.\n\n## Styling Rules for Generated Code\n\n- Do not require consumers to configure Tailwind for Lumen components.\n- Prefer component props and composition before adding custom CSS.\n- Use the shared token names for custom surfaces: `canvas`, `surface`, `surface-muted`,\n `surface-strong`, `line`, `ink`, `ink-soft`, `ink-muted`, `brand`, `brand-solid`, `brand-soft`,\n `accent`, `success`, `warning`, and `danger`.\n- Use `glass` as a boolean prop or attribute for glass surfaces when supported.\n- Preserve semantic labels, keyboard paths, focus states, and native form attributes.\n\n## Figma Handoff\n\nUse `exportThemeFigmaVariables` from `@santi020k/lumen-core/figma` when a design workflow needs\nFigma variable-friendly color values, and `exportThemeDesignTokens` when exporting standard design\ntoken JSON for token importers. See [docs/figma.md](figma.md) for the recommended Figma workflow.\n\n## When Editing This Repo\n\n- Shared contracts belong in `packages/core`.\n- Astro implementation and standalone CSS belong in `packages/astro`.\n- React-specific primitives belong in `packages/react`.\n- Web Component-specific primitives belong in `packages/elements`.\n- Update the relevant package README when public usage changes.\n- Add tests beside package code as `*.test.ts` when behavior, exported metadata, or component\n contracts change.\n",
35318
- "readme": "<p align=\"center\">\n <a href=\"https://lumen.santi020k.com\">\n <img src=\"./apps/docs/public/logo.svg\" alt=\"Lumen UI\" width=\"233\" height=\"60\">\n </a>\n</p>\n\n<h1 align=\"center\">Lumen UI</h1>\n\n<p align=\"center\">\n A cross-platform UI system with 150+ web primitives and shared native foundations.\n</p>\n\n<p align=\"center\">\n <a href=\"https://lumen.santi020k.com\">Documentation</a>\n ·\n <a href=\"https://lumen.santi020k.com/docs/components\">Components</a>\n ·\n <a href=\"https://www.figma.com/community/file/1662337342676541513\">Figma library</a>\n ·\n <a href=\"https://apps.apple.com/app/id6805250815\">iPhone and iPad app</a>\n ·\n <a href=\"https://github.com/santi020k/lumen/issues\">Issues</a>\n</p>\n\n<p align=\"center\">\n <a href=\"https://github.com/santi020k/lumen/actions\"><img alt=\"GitHub Actions\" src=\"https://img.shields.io/github/actions/workflow/status/santi020k/lumen/ci.yml?branch=main&style=flat-square\"></a>\n <a href=\"https://github.com/santi020k/lumen/actions/workflows/codeql.yml\"><img alt=\"CodeQL\" src=\"https://github.com/santi020k/lumen/actions/workflows/codeql.yml/badge.svg\"></a>\n <a href=\"https://www.npmjs.com/package/@santi020k/lumen-astro\"><img alt=\"npm version\" src=\"https://img.shields.io/npm/v/@santi020k/lumen-astro?style=flat-square&label=npm\"></a>\n <a href=\"./LICENSE\"><img alt=\"MIT license\" src=\"https://img.shields.io/badge/license-MIT-111827?style=flat-square\"></a>\n</p>\n\nLumen gives applications a consistent visual language without tying them to one rendering model.\nIts packages share tokens, component contracts, styles, and interaction patterns while keeping each\nframework and platform's native authoring experience.\n\n- **Framework native:** Astro components, React primitives, and standards-based custom elements.\n- **Native foundations:** generated React Native, SwiftUI, and Jetpack Compose tokens plus the\n complete shared interface and namespaced brand icon catalogs preserve the same semantic roles and\n artwork without introducing DOM or CSS assumptions.\n- **Accessible by default:** semantic markup, keyboard paths, focus management, and reduced-motion\n support are built into the primitives.\n- **Standalone CSS:** no Tailwind configuration is required. Tailwind users get an explicit layer\n integration.\n- **Progressively enhanced:** Astro interactions use a small client runtime instead of requiring an\n application framework.\n- **Ready for design and AI workflows:** published Figma resources, a portable agent skill, an MCP\n server, `llms.txt`, a machine-readable registry, and a cross-platform release manifest ship\n alongside the component system.\n- **Product-ready templates:** five responsive dashboard and application families are available as\n live previews and installable Astro, React, and Elements recipes.\n\n## Dashboards and templates\n\nExplore the [template gallery](https://lumen.santi020k.com/templates) for complete analytics,\nSaaS admin, commerce, project workspace, and authentication/onboarding experiences. Each family\nuses public Lumen primitives and semantic tokens, includes responsive and accessibility coverage,\nand ships through the CLI for all three framework targets:\n\n```bash\nlumen add analytics-dashboard\nlumen add commerce-dashboard --target react\nlumen add auth-onboarding --target elements\n```\n\n## Web quick start\n\nInstall the package for your framework:\n\n```bash\n# Astro\npnpm add @santi020k/lumen-astro\n\n# React\npnpm add @santi020k/lumen-react\n\n# Web Components\npnpm add @santi020k/lumen-elements\n```\n\n### Astro\n\nImport the stylesheet and mount `UIPrimitives` once in your root layout. The runtime enhances all\ninteractive Lumen markup on the page.\n\n```astro\n---\nimport '@santi020k/lumen-astro/styles.css'\nimport UIPrimitives from '@santi020k/lumen-astro/runtime'\n---\n\n<html lang=\"en\">\n <body>\n <slot />\n <UIPrimitives />\n </body>\n</html>\n```\n\n```astro\n---\nimport { Button, Card, Input } from '@santi020k/lumen-astro'\n---\n\n<Card>\n <label for=\"email\">Email</label>\n <Input id=\"email\" name=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button>Subscribe</Button>\n</Card>\n```\n\n### React\n\nLoad the stylesheet once from your app entry or global CSS:\n\n```tsx\nimport '@santi020k/lumen-react/styles.css'\nimport { Button, Card, Input } from '@santi020k/lumen-react'\n\nexport function SubscribeForm() {\n return (\n <Card>\n <label htmlFor=\"email\">Email</label>\n <Input id=\"email\" name=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button>Subscribe</Button>\n </Card>\n )\n}\n```\n\n### Web Components\n\nImport the styles and register the elements once:\n\n```html\n<style>\n @import \"@santi020k/lumen-elements/styles.css\";\n</style>\n\n<script type=\"module\">\n import { defineLumenElements } from '@santi020k/lumen-elements/define'\n\n defineLumenElements()\n</script>\n\n<lumen-card>\n <label for=\"email\">Email</label>\n <lumen-input id=\"email\" name=\"email\" type=\"email\" placeholder=\"you@example.com\"></lumen-input>\n <lumen-button>Subscribe</lumen-button>\n</lumen-card>\n```\n\nSee the [Web documentation](https://lumen.santi020k.com/docs/web) for installation, theming,\ncomponent examples, and API details.\n\nFor failure states, use the [error-handling guide](./docs/error-handling.md) to choose between field\nfeedback, summaries, persistent alerts, transient toasts, and the `ErrorState` recovery surface.\nLumen owns presentation and accessibility; applications retain logging, retry, and exception policy.\n\nFor native applications, choose the [React Native](https://lumen.santi020k.com/docs/react-native),\n[Apple / SwiftUI](https://lumen.santi020k.com/docs/apple), or\n[Android / Compose](https://lumen.santi020k.com/docs/android) guide. The\n[shared foundations](https://lumen.santi020k.com/docs/foundations) explain the cross-platform token\nand component contract; repository contributors can also use the\n[cross-platform architecture](./docs/cross-platform.md) and\n[native component reference](./docs/native-components.md).\n\n> **Native platform status for Lumen 2.** React Native, SwiftUI, Compose, and Wear OS use the frozen\n> supported contract. Package, migration, build, security, artifact, and stability checks gate the\n> initial release. The incomplete external-consumer and physical-device matrices remain published\n> as post-release qualification work; review them before choosing a production adapter.\n\n## Native playgrounds\n\nThe repository includes three searchable, interactive galleries built from the real native\npackages:\n\n- [`apps/playground-react-native`](./apps/playground-react-native) runs through Expo on the web,\n iOS, and Android and includes EAS profiles for TestFlight, Android App Bundles, and APKs.\n- [`apps/playground-apple`](./apps/playground-apple) is available on the\n [App Store](https://apps.apple.com/app/id6805250815) for iPhone, iPad, and Mac. It also builds as\n an iOS Xcode app or macOS Swift Package executable for local exploration.\n- [`apps/playground-android`](./apps/playground-android) builds a native Compose application and a\n directly installable debug APK. Its latest Google Play submission was rejected for a screenshot\n mismatch and is pending a corrected resubmission.\n\nSee the [playground workflow](./docs/playgrounds.md) for run, capture, and distribution commands.\n\n## Tailwind CSS\n\nKeep the layer prelude, Tailwind import, and Lumen stylesheet in the same shared CSS entry. Replace\n`lumen-astro` with the package for your framework.\n\n```css\n@import \"@santi020k/lumen-astro/layers.css\";\n@import \"tailwindcss\";\n@import \"@santi020k/lumen-astro/styles.css\";\n```\n\nThis order places Tailwind base styles before Lumen components and Tailwind utilities above Lumen\ncomponent defaults.\n\n## Packages\n\n| Package | Purpose |\n| --- | --- |\n| [`@santi020k/lumen-astro`](./packages/astro) | Reference implementation, Astro components, and progressive-enhancement runtime |\n| [`@santi020k/lumen-react`](./packages/react) | React components and behavior hooks |\n| [`@santi020k/lumen-react-hook-form`](./packages/react-hook-form) | Optional React Hook Form adapters for composite controls |\n| [`@santi020k/lumen-elements`](./packages/elements) | Standards-based Web Components |\n| [`@santi020k/lumen-core`](./packages/core) | Shared tokens, metadata, class helpers, and behavior contracts |\n| [`@santi020k/lumen-tokens`](./packages/tokens) | Canonical platform-neutral design token source |\n| [`@santi020k/lumen-react-native`](./packages/react-native) | React Native foundations and native primitives |\n| [`LumenUI`](./packages/swift) | Swift Package for iOS, iPadOS, macOS, tvOS, visionOS, and watchOS foundations and native primitives |\n| [`LumenWidgetUI`](./packages/swift-widget) | Focused WidgetKit-safe semantic text, icon, badge, and compact-stat foundations |\n| [`lumen-compose`](./packages/compose) | Android library for Jetpack Compose foundations and native primitives |\n| [`lumen-compose-wear`](./packages/compose/wear) | Focused Wear OS primitives for at-a-glance round-screen actions, progress, status, metrics, and rows |\n| [`@santi020k/lumen-icons-brand`](./packages/icons-brand) | Optional namespaced brand icons for web `Icon` adapters |\n| [`@santi020k/lumen`](./packages/lumen) | Umbrella package, CLI, registry metadata, and public package map |\n| [`@santi020k/lumen-mcp`](./packages/mcp) | MCP server for component discovery and source retrieval |\n\n## AI and design workflows\n\nInstall the portable Lumen skill in Codex, Claude Code, Cursor, Windsurf, and other compatible\ncoding agents:\n\n```bash\nnpx skills add santi020k/lumen --skill lumen-ui\n```\n\nThe skill teaches agents how to select, compose, theme, and verify Lumen primitives. Pair it with\n[`@santi020k/lumen-mcp`](./packages/mcp) when an agent needs to search the live catalog or retrieve\ncurrent source, props, tokens, and usage rules.\n\nFor ChatGPT and Codex, install the published\n[Lumen UI plugin](https://chatgpt.com/plugins/plugin_asdk_app_6a8f6c526c5481918eb8a48806fa112b)\nfrom the Plugins Directory. Select **Install plugin**, then mention **@Lumen UI** in a request such\nas “Find the right Lumen components for an accessible React settings screen.” Version 1.0.0 bundles\nthe skill with the hosted, read-only catalog; no separate Lumen account or local MCP setup is needed.\n\nAdditional machine-readable surfaces include:\n\n- [`llms.txt`](./llms.txt) for a concise project map.\n- [`docs/ai-usage.md`](./docs/ai-usage.md) for downstream generation examples.\n- [`docs/styling-contract.md`](./docs/styling-contract.md) for stable parts and component variables.\n- [`docs/import-and-icon-performance.md`](./docs/import-and-icon-performance.md) for repeatable import and icon evidence.\n- [`docs/graphics.md`](./docs/graphics.md) for portable decorative artwork across web and native platforms.\n- [`docs/consumer-regression-fixtures.md`](./docs/consumer-regression-fixtures.md) for production-shaped local coverage.\n- [`docs/project-adoption.md`](./docs/project-adoption.md) for the sibling-project audit and staged\n web and native migration strategy.\n- [`registry/lumen.registry.json`](./registry/lumen.registry.json) for recipes and installable file\n groups.\n- [`docs/figma.md`](./docs/figma.md) for Figma variables and Code Connect workflows.\n- [`plugins/lumen-ui`](./plugins/lumen-ui) for the packaged Codex and ChatGPT plugin, with its\n [submission and review record](./docs/openai-plugin-submission.md).\n- [`.claude-plugin/marketplace.json`](./.claude-plugin/marketplace.json) for the Claude Code\n marketplace, with its [installation and submission guide](./docs/claude-code-plugin.md).\n- [`docs/cross-platform.md`](./docs/cross-platform.md) for the native architecture, support tiers,\n and component-parity policy.\n- [`docs/native-patterns.md`](./docs/native-patterns.md) for developed native compositions and the\n boundary between Lumen presentation and platform-owned application structure.\n\n## Contributing\n\nLumen is a pnpm workspace. Astro is the reference implementation; shared contracts belong in\n`packages/core`, while framework-specific behavior stays in its adapter package.\n\n```bash\npnpm install\npnpm run dev\npnpm run validate\n```\n\nRead [`CONTRIBUTING.md`](./CONTRIBUTING.md) before opening a pull request. User-visible package\nchanges require a changeset.\n\n## Community\n\nLumen is free and MIT licensed while the project focuses on adoption, public examples, and\nreal-world feedback.\n\n- Explore task-oriented [guides](https://lumen.santi020k.com/guides).\n- Share a public project in the [community showcase](https://lumen.santi020k.com/community).\n- Evaluate the system with the open [team adoption path](https://lumen.santi020k.com/teams).\n- Tell us what would improve your next build through the\n [adoption survey](https://github.com/santi020k/lumen/issues/new?template=adoption-feedback.yml).\n\nMaintainers can use the [exposure playbook](./docs/exposure-playbook.md) for publishing cadence,\ncampaign measurement, video scripts, and launch copy, and the\n[marketing package](./docs/marketing/README.md) for execution-ready copy, channel decisions, a\nfour-week calendar, asset briefs, an approval queue, and a community-response library.\n\n## License\n\nLumen's original code and artwork are available under the [MIT License](./LICENSE). Generated\ninterface icons retain the Lucide ISC or Feather MIT terms, and generated brand icons retain Font\nAwesome Free's CC BY 4.0 terms. The native packages include the required attribution and license\nnotices; see [`icons/THIRD_PARTY_NOTICES.md`](./icons/THIRD_PARTY_NOTICES.md).\n"
35317
+ "aiUsage": "# AI Usage Guide\n\nUse this guide when an AI agent is building an app with Lumen or updating examples that teach\nanother agent how to use the library.\n\n## Install in ChatGPT or Codex\n\nThe [Lumen UI plugin](https://chatgpt.com/plugins/plugin_asdk_app_6a8f6c526c5481918eb8a48806fa112b)\nis published in the shared Plugins Directory for ChatGPT and Codex.\n\n1. Open the listing and select **Install plugin**. Sign in to ChatGPT if prompted.\n2. Start a request with **@Lumen UI**, or choose one of the listing's starter prompts.\n3. Describe the interface and target framework, for example: “Find the right Lumen components for\n an accessible React settings screen.”\n\nThe plugin combines the Lumen workflow skill with the hosted public MCP catalog. It requires no\nseparate Lumen account, API key, or local server. Its tools only read public Lumen documentation;\nthey do not access private repositories or modify files and services. Your coding agent performs\nimplementation work using the retrieved contracts and its own permissions.\n\nSee the [publication record](./openai-plugin-submission.md) for version and verification details.\n\n## Install the Lumen Agent Skill\n\nLumen ships a portable Agent Skill for AI coding tools that support the open `SKILL.md` format:\n\n```bash\nnpx skills add santi020k/lumen --skill lumen-ui\n```\n\nThe source lives in [`skills/lumen-ui`](../skills/lumen-ui). The skill provides the durable\nframework-selection, component-composition, theming, accessibility, and verification workflow.\nUse it by naming `$lumen-ui` in a prompt, or let a compatible agent trigger it from Lumen-specific\nrequests.\n\nThe skill and MCP server complement each other: the skill explains how to work, while\n`@santi020k/lumen-mcp` supplies structured catalog search, framework-specific contracts, recipes,\nsource, tokens, resources, and rules.\n\n## Pick the Right Package\n\nUse the framework requested by the user. Every adapter shares the same Lumen foundation.\n\n| App Target | Install | Import Components From | Load Styles From |\n| -------------------------- | ---------------------------------------------------------------------- | -------------------------------------------- | -------------------------------------- |\n| Astro | `@santi020k/lumen-astro` | `@santi020k/lumen-astro` | `@santi020k/lumen-astro/styles.css` |\n| React | `@santi020k/lumen-react` | `@santi020k/lumen-react` | `@santi020k/lumen-react/styles.css` |\n| React Hook Form composites | `@santi020k/lumen-react-hook-form` | `@santi020k/lumen-react-hook-form` | Uses React styles |\n| Web Components | `@santi020k/lumen-elements` | `@santi020k/lumen-elements/define` | `@santi020k/lumen-elements/styles.css` |\n| React Native / Expo | `@santi020k/lumen-react-native` | `@santi020k/lumen-react-native` | Not applicable |\n| Apple / SwiftUI | Swift Package `https://github.com/santi020k/lumen`, pinned to `3.0.1` | `LumenUI` | Not applicable |\n| Android / Compose | Maven Central `com.santi020k:lumen-compose:3.0.0` | `com.santi020k.lumen` | Not applicable |\n| Wear OS / Compose | Maven Central `com.santi020k:lumen-compose-wear:3.0.0` | `com.santi020k.lumen` | Not applicable |\n| Package metadata | `@santi020k/lumen-core` | `@santi020k/lumen-core` | Not applicable |\n| Optional web brand icons | `@santi020k/lumen-icons-brand` | Register once, then use the framework `Icon` | Uses framework styles |\n\nEach framework package includes the shared foundation. Install `@santi020k/lumen` separately only\nwhen you need its framework-neutral CLI or registry metadata.\n\nFor React Native, mount one `LumenProvider` near the app root. For SwiftUI, attach the `LumenUI`\nSwift Package product to the application target, use an exact or compatible release-version rule,\nand apply `.lumenTheme(...)` near the root. For Compose, install\n`com.santi020k:lumen-compose:3.0.0` from Maven Central and wrap content in `LumenTheme`. Native\nadapters do not load CSS or the Astro runtime.\n\nReact Native can use ordinary React hooks and platform-neutral application hooks. Import native\nLumen controllers such as `useDialog`, `useTabs`, `useSelect`, `useLanguageToggle`,\n`useThemeToggle`, and `useToast` from `@santi020k/lumen-react-native`; their state semantics track\nthe reusable React contracts while their returned props target native Lumen components. Never\nimport DOM-dependent hooks from `@santi020k/lumen-react` into a native bundle. Share business state\nin an application workspace package and keep the Lumen rendering adapter at each app boundary.\n\nFor watchOS, use the focused `LumenWatch*` contracts from the same Swift package. For Wear OS,\ninstall `com.santi020k:lumen-compose-wear:3.0.0` and wrap wearable content in `LumenWearTheme`\ninside the application-owned Wear Material theme. Keep complications, tiles, rotary input,\nAlways On behavior, haptics, synchronization, navigation, and health or safety logic app-owned.\n\nBrand marks are intentionally excluded from the default web Lucide bundle. In Astro, React, or Web\nComponents, install and register the optional pack once before rendering a namespaced icon:\n\n```ts\nimport { registerLumenBrandIcons } from \"@santi020k/lumen-icons-brand\";\n\nregisterLumenBrandIcons();\n```\n\nThen use any canonical Font Awesome Free brand name, such as `brand:github`, `brand:discord`,\n`brand:figma`, `brand:linkedin`, `brand:whatsapp`, or `brand:youtube`, through the existing Astro,\nReact, or Web Component `Icon` API. The package also provides `brand:x` as an alias for\n`brand:x-twitter`.\n\nBrand marks keep their recognizable filled geometry instead of being redrawn as Lucide outlines.\nThey still inherit the same Lumen size, alignment, `currentColor`, and accessible-label behavior as\nthe default icon catalog. React Native, SwiftUI, and Compose already include the corresponding\ncomplete brand catalog and accept `brand:github`, `.brandGithub`, and\n`LumenIconName.BrandGithub`, respectively, without a separate package.\n\n## MCP-Assisted Workflow\n\nConnect `@santi020k/lumen-mcp` when the agent supports Model Context Protocol. The server returns\nvalidated structured content as well as readable text, so agents can select components without\nscraping documentation.\n\nThe MCP per-component catalog covers Astro, React, Web Components, React Native, SwiftUI, and\nCompose. Use `lumen_list_native_components` to discover platform availability and\n`lumen_get_native_component` for installation, setup, API, examples, accessibility guidance, and\noptional reference source. The skill still verifies installed packages when local versions differ.\n\n```bash\ncodex mcp add lumen -- npx -y @santi020k/lumen-mcp\n```\n\nFor Codex project configuration:\n\n```toml\n[mcp_servers.lumen]\nenabled = true\ncommand = \"npx\"\nargs = [\"-y\", \"@santi020k/lumen-mcp\"]\n```\n\nUse this sequence:\n\n1. Read `lumen://meta` and `lumen://diagnostics` to identify and verify the snapshot.\n2. Read `lumen://rules`.\n3. Search for the use case with `lumen_search`, passing the target `framework` when known.\n4. Inspect the selected framework with `lumen_get_component` and `detail: \"usage\"`.\n5. Follow its framework behavior setup and hook/controller contract.\n6. Read related multi-component guidance with `lumen_get_recipe`.\n7. Request `detail: \"source\"` only when the usage contract is insufficient.\n8. Read `lumen://tokens` before introducing custom styling.\n9. Retain `lumen://catalog-manifest` and use `lumen_diff_catalog` after upgrades to identify contracts that need refreshing.\n\nExample framework-aware component request:\n\n```json\n{\n \"name\": \"lumen_get_component\",\n \"arguments\": {\n \"name\": \"DateRangePicker\",\n \"framework\": \"react\",\n \"detail\": \"usage\"\n }\n}\n```\n\nStable resources are available at `lumen://rules`, `lumen://tokens`, `lumen://components`,\n`lumen://meta`, `lumen://catalog-manifest`, `lumen://diagnostics`,\n`lumen://components/{name}`, and `lumen://recipes/{name}`. See\n[`packages/mcp/README.md`](../packages/mcp/README.md) for Claude Desktop, Cursor, generic stdio,\nlocal repository, and programmatic examples.\n\n## Minimal Setup\n\nImport the stylesheet once in the app's global CSS, root layout, or app entry.\n\nThis section applies to web targets. Native adapters use their provider or theme APIs instead of a\nstylesheet.\n\n```css\n@import \"@santi020k/lumen-react/styles.css\";\n```\n\nIf Tailwind is present, keep both imports in the same shared CSS entry.\n\n```css\n@import \"@santi020k/lumen-react/layers.css\";\n@import \"tailwindcss\";\n@import \"@santi020k/lumen-react/styles.css\";\n```\n\nKeep the layer prelude first. It places Tailwind base styles below Lumen components and Tailwind\nutilities above them, so consumer spacing, display, radius, width, and responsive visibility\nclasses override Lumen defaults predictably.\n\nAstro apps should mount `UIPrimitives` once in the root layout when using interactive primitives\nsuch as dialogs, menus, popovers, tabs, carousels, command lists, toggles, validated forms, or\ntoasts. Do not place `UIPrimitives` in each component example or next to each primitive instance;\none root include enhances all matching `data-ui-*` markup on the page.\n\n```astro\n---\nimport UIPrimitives from '@santi020k/lumen-astro/runtime'\n---\n\n<UIPrimitives />\n```\n\n## Astro\n\nUse direct named imports from `@santi020k/lumen-astro`.\n\n```astro\n---\nimport { Button, Card, Input, Label } from '@santi020k/lumen-astro'\n---\n\n<Card>\n <Label for=\"email\">Email</Label>\n <Input id=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button type=\"submit\">Subscribe</Button>\n</Card>\n```\n\n`Stat` defaults to a `div`. Choose `as=\"article\"` for a self-contained metric with supporting\ncontent, or `as=\"section\"` for a labeled region. Do not use a semantic root solely for styling.\nIts visual variants are `default`, `accent`, and `glass`; reserve `accent` for featured metrics and\nkeep glass selective.\n\nFor long reading surfaces, use `Anchor` for local section navigation and pass each item’s optional\nheading `depth`, `index`, and `description` when hierarchy matters. Use `activationOffset` for fixed\napplication headers. Use `ScrollProgress` for document position; keep `Progress` for\napplication-controlled task completion. In Astro, dispatch `ui:progress-change` with\n`{ value, max? }` from the `Progress` root when browser state changes after hydration. `Pill`\naccepts `neutral`, `brand`, and `outline`\nvariants, plus native link and count composition.\n\n## React\n\nReact components share the same visual classes and data attributes as Astro components. For\nbehavior-heavy primitives, use the React behavior hooks from `@santi020k/lumen-react` (`useDialog`,\n`usePopover`, `useDropdownMenu`, `useTabs`, `useSelect`, `useToast`, and `useTooltip`), which track\nthe Astro runtime's ARIA, keyboard, Escape, dismissal, and toast controller semantics.\n\nThe React package entry is a client boundary. In Next.js, it can be imported by a Server Component\nwithout evaluating Lumen's contexts and hooks in the React server runtime; the rendered Lumen\nsubtree becomes a Client Component boundary. Vite and other client-rendered React applications use\nthe same entry without additional setup.\nFor stateless Badge, Card composition, layout, form-label, progress, loading, typography, and\nvisually-hidden usage in a React Server Component, import from\n`@santi020k/lumen-react/server`. That entrypoint contains the implementations directly and avoids\ncreating a client boundary. Keep event handlers, hooks, and interactive primitives on the package\nroot inside a Client Component.\n`Button`, `Link`, `ButtonLink`, and `Input` accept React 19 DOM refs directly as props.\n`Button asChild` composes the button contract onto one child element. Across adapters, native\nnumeric `size` remains available on `Input` and `NativeSelect`; use `visualSize` in Astro and React,\nor `visual-size` in Elements, for presentation.\n`Stat as` and `variant` follow the same semantic-root and visual guidance as the Astro component.\n\n```tsx\nimport \"@santi020k/lumen-react/styles.css\";\nimport { Button, Card, Input, Label } from \"@santi020k/lumen-react\";\n\nexport function SubscribeForm() {\n return (\n <Card>\n <Label htmlFor=\"email\">Email</Label>\n <Input id=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button type=\"submit\">Subscribe</Button>\n </Card>\n );\n}\n```\n\nFor React Hook Form, spread `register()` directly onto native-backed controls. Install\n`@santi020k/lumen-react-hook-form` only for controlled composites such as `Select`, `DatePicker`,\n`InputOTP`, and `ListBox`. Do not combine React Hook Form and `useFormValidation` unless the\napplication intentionally coordinates two validation sources. For schema validation, use\n`zodResolver` or `yupResolver` from `@hookform/resolvers`; keep Zod or Yup in the application and\nrender the resulting React Hook Form errors through Lumen's `FieldError` contract.\n\n## Web Components\n\nRegister custom elements once, then use `lumen-*` tags in HTML. The elements adapter includes\ninteractive behavior parity for DataTable, Dialog, Popover, DropdownMenu, ContextMenu, Tabs,\nSelect, ThemeBuilder, Toast, Tooltip, forms, RichTextEditor, Schedule, and VirtualList: selection,\nvalidation, rich text command, context menu, schedule drag/drop, theme, and range events, ARIA\nstate, roving/listbox keyboard paths, Escape/outside dismissal, focus return/trapping, native\nselect form participation, and the same toast controller events as the Astro runtime.\n\n```html\n<script type=\"module\">\n import { defineLumenElements } from \"@santi020k/lumen-elements/define\";\n\n defineLumenElements();\n</script>\n\n<lumen-card>\n <label for=\"email\">Email</label>\n <lumen-input\n id=\"email\"\n type=\"email\"\n placeholder=\"you@example.com\"\n ></lumen-input>\n <lumen-button type=\"submit\">Subscribe</lumen-button>\n</lumen-card>\n```\n\nFor a surface that uses only Badge, Button, Card, and Combobox, import\n`@santi020k/lumen-elements/components/badge`, `/button`, `/card`, and `/combobox` and call their\ncorresponding `defineLumenBadge`, `defineLumenButton`, `defineLumenCard`, and `defineLumenCombobox`\nfunctions. Those implementation-level entrypoints avoid the complete catalog bundle; the Combobox\nentrypoint includes its filtering and keyboard behavior. Continue using `defineLumenElements` for\nany component without a granular entrypoint.\n\nFor a foundation-only custom-element surface, import `defineLumenFoundations` from\n`@santi020k/lumen-elements/components/foundations`. It registers Card compound parts, Container,\nDirection, Grid, Label, Separator, Skeleton, Spinner, Stack, Typography, and VisuallyHidden without\npulling the complete catalog implementation.\n\nPut input elements inside a native `<form data-ui-form>`. Scalar Lumen controls participate in\n`FormData`, validity, disabled state, focus, and reset through form-associated custom-element\nbehavior with a native-control fallback. Do not generate `<lumen-form>` as the submission\ncontainer.\n\n## Component Selection\n\nThe shared catalog includes:\n\n`Accordion`, `Alert`, `AlertDialog`, `Agenda`, `AspectRatio`, `Attachment`, `Autocomplete`,\n`Avatar`, `Badge`, `BarChart`, `Breadcrumb`, `Bubble`, `Button`, `ButtonGroup`, `Calendar`, `Card`,\n`Carousel`, `Chart`, `Checkbox`, `CheckboxGroup`, `Collapsible`, `Code`, `Combobox`, `Command`,\n`Container`, `ColorPicker`,\n`ContextMenu`, `DataTable`, `DatePicker`, `DateRangePicker`, `Dialog`, `Direction`, `Drawer`,\n`DropdownMenu`, `Empty`, `ErrorState`, `ErrorSummary`, `Field`, `FieldError`, `Form`, `Grid`, `HoverCard`, `Icon`,\n`Graphic`, `Backdrop`, `Illustration`, `Input`, `InputGroup`, `InputOTP`, `Item`, `KanbanBoard`, `KanbanColumn`, `Kbd`, `Label`, `LineChart`, `ListBox`, `Marker`, `Menubar`,\n`Message`, `MessageScroller`, `NativeSelect`, `ContextNavigation`, `NavigationMenu`, `NumberField`, `Pagination`,\n`PasswordField`, `PieChart`, `Popover`, `Progress`, `CopyButton`, `RadioGroup`, `Resizable`, `RichTextEditor`,\n`ScrollArea`, `Schedule`, `SearchField`, `Select`, `Separator`, `Sheet`, `Sidebar`, `Skeleton`,\n`Slider`, `Sparkline`, `Spinner`, `Stack`, `Switch`, `Table`, `Tabs`, `TagGroup`, `Textarea`, `ThemeBuilder`,\n`TimeField`, `Toast`, `Toggle`, `ToggleGroup`, `Tooltip`, `Tree`, `TreeGrid`, `Typography`, and\n`VirtualList`, and `VisuallyHidden`.\n\n## Product Recipes\n\n- Follow [the error-handling guide](./error-handling.md) when a generated interface needs failure\n feedback. Use `FieldError` and `ErrorSummary` for validation, `Alert` for persistent inline\n problems, `Toast` for brief non-blocking failures, and `ErrorState` only when a page or region\n cannot show its primary content. Keep exception capture, logging, retry policy, and safe message\n mapping in application code. For React Native, SwiftUI, and Compose, follow the guide's native API\n mapping and announcement rules; do not invent a cross-platform error manager inside the component\n layer.\n\n- Start full product surfaces from the bundled `analytics-dashboard`, `saas-admin`,\n `commerce-dashboard`, `project-workspace`, or `auth-onboarding` recipe. Install with\n `lumen add <recipe>` for Astro, or add `--target react` / `--target elements`. Keep the generated\n shared template stylesheet, then replace sample data and routes while preserving public Lumen\n components, semantic tokens, accessible names, focus paths, and responsive navigation.\n- Use `Sparkline` for a compact trend beside a labelled metric, `BarChart` for categorical\n comparison, `LineChart` for ordered or time-series data, `PieChart` for a small\n part-to-whole breakdown. Pass already-aggregated `LumenChartSeries` data; keep statistics,\n resampling, model evaluation, and domain calculations in application code. Keep `showTable`\n enabled unless the same values are already available in a nearby semantic table. Prefer a bar\n chart when precise slice comparison matters or the pie would exceed roughly seven categories.\n Use `ScatterChart` for numeric relationships, `Heatmap` for a labeled matrix, `RangeChart` for\n low-to-high intervals, and `ComboChart` only when mixed marks share a meaningful domain. Use\n `Chart` as the custom SVG/canvas escape hatch. Preserve `null` gaps and the default accessible\n summary and fallback data. See `docs/data-visualization.md` for the full contract.\n Pass `labels` to replace every library-owned visible and assistive phrase. Finite zero is valid\n data, while an empty chart has no finite values; do not silently reinterpret an all-zero series\n as missing. Set `showTable={false}` (`showData={false}` on native adapters) only when an\n equivalent accessible table or scoreboard is adjacent.\n Keep stable identity in `datum.x` and put display text in `datum.xLabel`. Use `formatCategory`\n and `formatValue` for localized axes, SVG titles, and data tables. Choose\n `presentation=\"bare\"` inside an existing Card, `categoryWidth` for long horizontal labels,\n `markers=\"auto\"` or a numeric marker step for dense lines, and `emptyLabel` for actionable\n source-specific guidance.\n- Use `Icon name=\"search\"` or any Lucide icon name for standard interface pictograms. Kebab-case\n names and Lucide aliases work. Pair decorative icons with `decorative`, and pass `label` only\n when the icon carries meaning on its own. On the web, install and register\n `@santi020k/lumen-icons-brand` for supported brand marks; native adapters include those names.\n Then use `brand:github` or the adapter's generated equivalent. Render project-specific or\n unsupported brand SVGs through the default `Icon` slot. Astro reports unknown named icons during\n development instead of failing silently.\n- Use `Schedule` with `Agenda`, `Calendar`, and `DatePicker` for booking, CRM, content calendar, and\n event planning screens. Mark slots with `data-ui-schedule-slot` and events with\n `data-ui-schedule-event`; the runtime emits `ui:schedule-change` when a draggable event is moved.\n `Calendar` renders a hidden input plus selectable date grid; Astro `UIPrimitives` and registered\n Web Components wire month navigation and roving keyboard focus directly, while React apps can use\n the `Calendar` component or `useCalendar` for the same date selection contract.\n Astro `UIPrimitives` and registered Web Components bind schedule drag/drop directly; React apps\n can use `useSchedule` for `rootProps`, `getEventProps`, `getSlotProps`, and `ui:schedule-change`\n parity. Use `createScheduleSlots`, `moveScheduleEvent`, and `expandRecurringScheduleEvent` from\n `@santi020k/lumen-core` for reusable schedule state; use `getScheduleConflicts` and\n `canPlaceScheduleEvent` before committing drag/drop changes. Persist local schedule edits with\n `createScheduleStorageKey`, `saveScheduleEvents`, and `loadScheduleEvents` against any\n `localStorage`-compatible adapter. Use `resizeScheduleEvent` and `resizeScheduleEvents` for\n snap-to-grid resize handles.\n- Use `KanbanBoard` and `KanbanColumn` for status-based workspaces while continuing to use `Card` for\n items. Mark items with `data-ui-kanban-item` and put `data-ui-kanban-handle` on a dedicated button;\n do not make the whole card draggable. Astro and Elements emit the cancellable, controlled\n `ui:kanban-move-request` event, and React provides `useKanban`. The event reports `itemId`,\n `fromColumn`, `toColumn`, optional `beforeId`, and keyboard or pointer input, but never moves DOM or\n persists application data. The host owns validation, `aria-busy`, optimistic state, rollback,\n status names, and APIs. Use compact `Empty` states inside columns, preserve every column with\n `Skeleton` blocks while loading, and paginate large columns. See [Kanban composition and movement](kanban.md).\n- Use `DataTable`, `Tree`, `TreeGrid`, `VirtualList`, `Pagination`, and `Command` for dense data\n collection workflows. Astro and Elements wire selectable/sortable `DataTable` behavior and\n `VirtualList` range events; React emits the same data attributes for app-level adapters. `Tree`\n and `TreeGrid` use roving keyboard focus, and `VirtualList` emits `ui:virtual-list-range` as the\n rendered range changes. Use `createDataViewState`,\n `serializeDataViewState`, `parseDataViewState`, `applyDataViewState`, and `getVirtualRange` for\n saved views, filtering, sorting, pagination, and virtualization math. Use\n `createDataViewStorageKey`, `saveDataViewState`, and `loadDataViewState` when saved views should\n survive navigation or reloads. Use `pinDataViewColumn`, `unpinDataViewColumn`,\n `toggleDataViewSelection`, `createDataViewSearchParams`, and `createDataViewServerRequest` when\n connecting dense views to router state or server-backed data.\n- Use `Resizable` for split-pane editors, dashboards, inspectors, and side-by-side review layouts.\n Astro `UIPrimitives` and registered Web Components generate separator handles and pane sizing\n directly; React apps can use the `Resizable` component or `useResizable` for pointer, keyboard,\n min/max, and double-click reset parity.\n- Use `ThemeBuilder`, `ColorPicker`, `Card`, `Button`, and `Input` to build token preview and export\n tools. Add `data-ui-theme-brand-hue`, `data-ui-theme-accent-hue`, `data-ui-theme-scheme`,\n `data-ui-theme-mode`, manual color inputs such as `data-ui-theme-primary-hex`, and\n `data-ui-theme-target` for scoped live previews. Add `data-ui-theme-export-format`,\n `data-ui-theme-export`, and `data-ui-theme-output` to copy CSS, Figma variable JSON, or design\n token JSON. Astro `UIPrimitives` and registered Web Components wire those controls directly;\n React apps can use `useThemeBuilder` for the same tokens, exports, and control props. Use\n `createThemePalette`, `createThemeBuilderTokens`, `exportThemeBuilderValue`, `exportThemeCss`,\n `parseThemeCss`, and `scoreThemeContrast`\n for theme import/export and accessibility checks. Use `suggestReadableInk`, `mergeThemeTokens`,\n and `tuneThemeContrast` when generated palettes need readable foreground repair.\n- Use `RichTextEditor` with `ButtonGroup`, `ToggleGroup`, and `Textarea` for editor compositions.\n Controls with `data-ui-editor-command` dispatch browser editing commands and emit\n `ui:editor-command`; use `data-ui-editor-value` for headings, links, and other value-bearing\n commands. Editable surfaces support common formatting shortcuts, synchronize toggle\n `aria-pressed` state, and emit `ui:editor-change` with HTML and plain text. Astro `UIPrimitives`\n and registered Web Components bind those controls directly; React apps can use\n `useRichTextEditor` for `rootProps`, `getCommandProps`, `getEditableProps`, and `executeCommand`.\n For Tiptap, ProseMirror, Lexical, or Markdown engines, keep the external editor\n package in the app and bridge toolbar buttons through the emitted command event instead of adding\n the editor engine to Lumen.\n- Use `Autocomplete`, `SearchField`, `NumberField`, `TimeField`, `DateRangePicker`, `ColorPicker`,\n and `TagGroup` when forms need more than plain text inputs. Add `data-ui-form` to forms that\n should reflect native Constraint Validation API state into `Field` error slots on blur and submit.\n For `InputOTP`, Astro `UIPrimitives` and registered Web Components render/sync a real native input\n with visual OTP segments; React apps can use the `InputOTP` component or `useInputOTP` for the\n same sanitization, paste handling, and caret navigation.\n For `DateRangePicker`, Astro `UIPrimitives` and registered Web Components sync the paired native\n date input `min`/`max` constraints directly; React apps can use `useDateRangePicker` for the same\n start/end syncing props.\n Use native attributes such as `required`, `pattern`, `min`, `max`, and `type`; add\n `data-error-required`, `data-error-pattern`, `data-error-min`, `data-error-max`, or\n `data-error-custom` when a control needs custom copy. Listen for `ui:validate` to call\n `control.setCustomValidity(message)` before the runtime reads validity, and listen for\n `ui:invalid` or `ui:valid` on the form for submission state. Astro `UIPrimitives` and registered\n Web Components bind `data-ui-form` directly; React apps can use `useFormValidation` with `Field`\n to get the same control props, CustomEvents, and error slot updates.\n- Use `Toast` for transient feedback. Static `<Toast>` markup works without\n JavaScript. With Astro `UIPrimitives` or registered Web Components, create toasts with\n `window.LumenToast.create(detail)`, `LumenToast.create(detail)` from `@santi020k/lumen-elements`,\n or `document.dispatchEvent(new CustomEvent('ui:toast', { detail }))`; update them with\n `.update(id, detail)` or `ui:toast-update`; and dismiss them with `.dismiss(id?)` or\n `ui:toast-dismiss`. `detail` supports `title`, `description`, `variant`, `duration`, `placement`,\n `max`, and `action: { label, value }`. Destructive toasts use `role=\"alert\"`; other variants use\n `role=\"status\"`. Runtime toasts pause their timeout on hover or focus, and Escape dismisses the\n focused toast.\n\n## Runtime CustomEvents\n\nWhen `UIPrimitives` is mounted, or when Web Components are registered for matching behavior, Lumen\nuses these CustomEvents:\n\n| Event | Target | Detail | Fires When |\n| -------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |\n| `ui:data-table-selection-change` | `DataTable` root `[data-ui-datatable]` | `{ values: string[] }` | Selectable row checkboxes, the select-all checkbox, or form reset changes selected row values. |\n| `ui:schedule-change` | `Schedule` root `[data-ui-schedule]` | `{ eventId?: string, slot?: string }` | A draggable schedule event is dropped on a `[data-ui-schedule-slot]`. |\n| `ui:virtual-list-range` | `VirtualList` root `[data-ui-virtual-list]` | `{ startIndex: number, endIndex: number }` | The virtual list calculates its visible range on init or scroll. |\n| `ui:kanban-move-request` | `KanbanBoard` root `[data-ui-kanban]` | `{ itemId: string, fromColumn: string, toColumn: string, beforeId?: string, input: 'keyboard' \\| 'pointer' }` | A handle requests a controlled move; the event is cancellable and never moves data or DOM. |\n| `ui:tag-remove` | `TagGroup` root `.ui-tag-group` or `[data-ui-tag-group]` | `{ value?: string }` | A `[data-ui-tag-remove]` control removes its closest tag or list item. |\n| `ui:editor-command` | `RichTextEditor` root `[data-ui-rich-text-editor]` | `{ command: string, executed: boolean, value?: string }` | A toolbar control or keyboard shortcut runs an editor command. |\n| `ui:editor-change` | `RichTextEditor` root `[data-ui-rich-text-editor]` | `{ html: string, text: string }` | Editable content changes or an editor command runs. |\n| `ui:theme-change` | `ThemeBuilder` root `[data-ui-theme-builder]` | `{ hue: number, accentHue: number, mode: 'generated' \\| 'manual', scheme: 'dark' \\| 'light', tokens: Record<string, string> }` | Hue, manual color, mode, or scheme controls update generated tokens. |\n| `ui:theme-export` | `ThemeBuilder` root `[data-ui-theme-builder]` | `{ css?: string, format: 'css' \\| 'figma' \\| 'tokens', tokens: Record<string, string> \\| null, value: string }` | A `[data-ui-theme-export]` control writes the selected export to the output and clipboard when available. |\n| `ui:language-change` | `LanguageToggle` control | `{ previousValue: string, value: string }` | Requests or applies the next configured locale. |\n| `ui:validate` | Form `[data-ui-form]` | `{ control, form, value }` | Before validation state is read, so custom code can call `control.setCustomValidity(message)`. |\n| `ui:invalid` | Form `[data-ui-form]` | `{ control?, controls?, form }` | Blur or submit validation finds invalid controls. Failed submits focus the first invalid control. |\n| `ui:valid` | Form `[data-ui-form]` | `{ control?, controls?, form }` | Blur or submit validation finds the checked control or form valid. |\n| `ui:toast` | `document` | `{ id?, title?, description?, variant?, duration?, placement?, action? }` | Creates a runtime toast in the matching notification viewport. |\n| `ui:toast-update` | `document` | `{ id, title?, description?, variant?, duration? }` | Updates a runtime toast. |\n| `ui:toast-dismiss` | `document` | `{ id? }` | Dismisses one runtime toast, or all runtime toasts when `id` is omitted. |\n| `ui:toast-action` | Toast `[data-ui-toast]` | `{ id, value? }` | A runtime toast action button is pressed, unless the action specifies a custom event name. |\n\n## Registry Helper\n\n`@santi020k/lumen` exports typed component, recipe, and documentation metadata from\n`@santi020k/lumen/registry`. The umbrella package and every web adapter expose the same small\n`lumen` binary, so projects that install Astro, React, or Elements can use discovery and diagnostics\nwithout another direct dependency. Individual components are addressable by name, including\nkebab-case aliases such as `data-table`:\n\n```bash\nlumen list\nlumen show Button\nlumen show data-table\nlumen add Button\nlumen add Button --target react\nlumen add Button --target elements\nlumen add data-table\nlumen show scheduler\nlumen add scheduler\nlumen add scheduler --target react\nlumen add scheduler --target elements\nlumen add scheduler --dry-run\nlumen add scheduler --fail-on-conflict\nlumen add scheduler --force\nlumen add scheduler --merge\nlumen list --registry ./registry/lumen.registry.json\nlumen add custom-recipe --registry ./registry/custom.registry.json\nlumen add private-recipe --registry https://example.com/lumen.registry.json --registry-token \"$TOKEN\"\nlumen install\nlumen doctor --json\nlumen doctor-native --json\nlumen init --framework astro --tailwind\n```\n\n`lumen doctor-native` emits the same adoption inventory and Android toolchain preflight in readable\ntext or JSON. Treat direct-primitive matches as review suggestions, not rewrite instructions: first\nconfirm whether a surface is product-owned and whether the public Lumen contract preserves its\nbehavior. A failed JDK, SDK, platform-package, or required-NDK preflight should be fixed before\nstarting Gradle so native compilation does not obscure the setup problem.\n\n`lumen add <component>` defaults to an Astro wrapper. Use `--target react` for a local React wrapper\nor `--target elements` for a custom-elements starter. Bundled recipes also accept `--target astro`,\n`--target react`, and `--target elements`; external registry inline files keep their authored paths\nand sources. Those paths must be unique, relative forward-slash paths: the installer rejects\nabsolute paths, parent traversal, and symbolic-link path segments before writing any file. Registry\nmanifests are limited to 5 MiB by default; programmatic `loadLumenRegistry` callers can lower the\nlimit with `maxBytes` and control remote request time with `timeoutMs`.\n\nBefore inventing a wrapper or custom class pattern, check the component source in\n`packages/astro/components`. Astro is the reference implementation for props, class names, data\nattributes, and accessible markup.\n\n## Styling Rules for Generated Code\n\n- Do not require consumers to configure Tailwind for Lumen components.\n- Prefer component props and composition before adding custom CSS.\n- Use the shared token names for custom surfaces: `canvas`, `surface`, `surface-muted`,\n `surface-strong`, `line`, `ink`, `ink-soft`, `ink-muted`, `brand`, `brand-solid`, `brand-soft`,\n `accent`, `success`, `warning`, and `danger`.\n- Use `glass` as a boolean prop or attribute for glass surfaces when supported.\n- Preserve semantic labels, keyboard paths, focus states, and native form attributes.\n\n## Figma Handoff\n\nUse `exportThemeFigmaVariables` from `@santi020k/lumen-core/figma` when a design workflow needs\nFigma variable-friendly color values, and `exportThemeDesignTokens` when exporting standard design\ntoken JSON for token importers. See [docs/figma.md](figma.md) for the recommended Figma workflow.\n\n## When Editing This Repo\n\n- Shared contracts belong in `packages/core`.\n- Astro implementation and standalone CSS belong in `packages/astro`.\n- React-specific primitives belong in `packages/react`.\n- Web Component-specific primitives belong in `packages/elements`.\n- Update the relevant package README when public usage changes.\n- Add tests beside package code as `*.test.ts` when behavior, exported metadata, or component\n contracts change.\n",
35318
+ "readme": "<p align=\"center\">\n <a href=\"https://lumen.santi020k.com\">\n <img src=\"./apps/docs/public/logo.svg\" alt=\"Lumen UI\" width=\"233\" height=\"60\">\n </a>\n</p>\n\n<h1 align=\"center\">Lumen UI</h1>\n\n<p align=\"center\">\n A cross-platform UI system with 150+ web primitives and shared native foundations.\n</p>\n\n<p align=\"center\">\n <a href=\"https://lumen.santi020k.com\">Documentation</a>\n ·\n <a href=\"https://lumen.santi020k.com/docs/components\">Components</a>\n ·\n <a href=\"https://www.figma.com/community/file/1662337342676541513\">Figma library</a>\n ·\n <a href=\"https://apps.apple.com/app/id6805250815\">iPhone and iPad app</a>\n ·\n <a href=\"https://play.google.com/store/apps/details?id=com.santi020k.lumen.playground.compose\">Android app</a>\n ·\n <a href=\"https://github.com/santi020k/lumen/issues\">Issues</a>\n</p>\n\n<p align=\"center\">\n <a href=\"https://github.com/santi020k/lumen/actions\"><img alt=\"GitHub Actions\" src=\"https://img.shields.io/github/actions/workflow/status/santi020k/lumen/ci.yml?branch=main&style=flat-square\"></a>\n <a href=\"https://github.com/santi020k/lumen/actions/workflows/codeql.yml\"><img alt=\"CodeQL\" src=\"https://github.com/santi020k/lumen/actions/workflows/codeql.yml/badge.svg\"></a>\n <a href=\"https://www.npmjs.com/package/@santi020k/lumen-astro\"><img alt=\"npm version\" src=\"https://img.shields.io/npm/v/@santi020k/lumen-astro?style=flat-square&label=npm\"></a>\n <a href=\"./LICENSE\"><img alt=\"MIT license\" src=\"https://img.shields.io/badge/license-MIT-111827?style=flat-square\"></a>\n</p>\n\nLumen gives applications a consistent visual language without tying them to one rendering model.\nIts packages share tokens, component contracts, styles, and interaction patterns while keeping each\nframework and platform's native authoring experience.\n\n- **Framework native:** Astro components, React primitives, and standards-based custom elements.\n- **Native foundations:** generated React Native, SwiftUI, and Jetpack Compose tokens plus the\n complete shared interface and namespaced brand icon catalogs preserve the same semantic roles and\n artwork without introducing DOM or CSS assumptions.\n- **Accessible by default:** semantic markup, keyboard paths, focus management, and reduced-motion\n support are built into the primitives.\n- **Standalone CSS:** no Tailwind configuration is required. Tailwind users get an explicit layer\n integration.\n- **Progressively enhanced:** Astro interactions use a small client runtime instead of requiring an\n application framework.\n- **Ready for design and AI workflows:** published Figma resources, a portable agent skill, an MCP\n server, `llms.txt`, a machine-readable registry, and a cross-platform release manifest ship\n alongside the component system.\n- **Product-ready templates:** five responsive dashboard and application families are available as\n live previews and installable Astro, React, and Elements recipes.\n\n## Dashboards and templates\n\nExplore the [template gallery](https://lumen.santi020k.com/templates) for complete analytics,\nSaaS admin, commerce, project workspace, and authentication/onboarding experiences. Each family\nuses public Lumen primitives and semantic tokens, includes responsive and accessibility coverage,\nand ships through the CLI for all three framework targets:\n\n```bash\nlumen add analytics-dashboard\nlumen add commerce-dashboard --target react\nlumen add auth-onboarding --target elements\n```\n\n## Web quick start\n\nInstall the package for your framework:\n\n```bash\n# Astro\npnpm add @santi020k/lumen-astro\n\n# React\npnpm add @santi020k/lumen-react\n\n# Web Components\npnpm add @santi020k/lumen-elements\n```\n\n### Astro\n\nImport the stylesheet and mount `UIPrimitives` once in your root layout. The runtime enhances all\ninteractive Lumen markup on the page.\n\n```astro\n---\nimport '@santi020k/lumen-astro/styles.css'\nimport UIPrimitives from '@santi020k/lumen-astro/runtime'\n---\n\n<html lang=\"en\">\n <body>\n <slot />\n <UIPrimitives />\n </body>\n</html>\n```\n\n```astro\n---\nimport { Button, Card, Input } from '@santi020k/lumen-astro'\n---\n\n<Card>\n <label for=\"email\">Email</label>\n <Input id=\"email\" name=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button>Subscribe</Button>\n</Card>\n```\n\n### React\n\nLoad the stylesheet once from your app entry or global CSS:\n\n```tsx\nimport '@santi020k/lumen-react/styles.css'\nimport { Button, Card, Input } from '@santi020k/lumen-react'\n\nexport function SubscribeForm() {\n return (\n <Card>\n <label htmlFor=\"email\">Email</label>\n <Input id=\"email\" name=\"email\" type=\"email\" placeholder=\"you@example.com\" />\n <Button>Subscribe</Button>\n </Card>\n )\n}\n```\n\n### Web Components\n\nImport the styles and register the elements once:\n\n```html\n<style>\n @import \"@santi020k/lumen-elements/styles.css\";\n</style>\n\n<script type=\"module\">\n import { defineLumenElements } from '@santi020k/lumen-elements/define'\n\n defineLumenElements()\n</script>\n\n<lumen-card>\n <label for=\"email\">Email</label>\n <lumen-input id=\"email\" name=\"email\" type=\"email\" placeholder=\"you@example.com\"></lumen-input>\n <lumen-button>Subscribe</lumen-button>\n</lumen-card>\n```\n\nSee the [Web documentation](https://lumen.santi020k.com/docs/web) for installation, theming,\ncomponent examples, and API details.\n\nFor failure states, use the [error-handling guide](./docs/error-handling.md) to choose between field\nfeedback, summaries, persistent alerts, transient toasts, and the `ErrorState` recovery surface.\nLumen owns presentation and accessibility; applications retain logging, retry, and exception policy.\n\nFor native applications, choose the [React Native](https://lumen.santi020k.com/docs/react-native),\n[Apple / SwiftUI](https://lumen.santi020k.com/docs/apple), or\n[Android / Compose](https://lumen.santi020k.com/docs/android) guide. The\n[shared foundations](https://lumen.santi020k.com/docs/foundations) explain the cross-platform token\nand component contract; repository contributors can also use the\n[cross-platform architecture](./docs/cross-platform.md) and\n[native component reference](./docs/native-components.md).\n\n> **Native platform status for Lumen 2.** React Native, SwiftUI, Compose, and Wear OS use the frozen\n> supported contract. Package, migration, build, security, artifact, and stability checks gate the\n> initial release. The incomplete external-consumer and physical-device matrices remain published\n> as post-release qualification work; review them before choosing a production adapter.\n\n## Native playgrounds\n\nThe repository includes three searchable, interactive galleries built from the real native\npackages:\n\n- [`apps/playground-react-native`](./apps/playground-react-native) runs through Expo on the web,\n iOS, and Android and includes EAS profiles for TestFlight, Android App Bundles, and APKs.\n- [`apps/playground-apple`](./apps/playground-apple) is available on the\n [App Store](https://apps.apple.com/app/id6805250815) for iPhone, iPad, and Mac. It also builds as\n an iOS Xcode app or macOS Swift Package executable for local exploration.\n- [`apps/playground-android`](./apps/playground-android) builds a native Compose application and a\n directly installable debug APK. It is also available on\n [Google Play](https://play.google.com/store/apps/details?id=com.santi020k.lumen.playground.compose)\n for Android phones and tablets.\n\nSee the [playground workflow](./docs/playgrounds.md) for run, capture, and distribution commands.\n\n## Tailwind CSS\n\nKeep the layer prelude, Tailwind import, and Lumen stylesheet in the same shared CSS entry. Replace\n`lumen-astro` with the package for your framework.\n\n```css\n@import \"@santi020k/lumen-astro/layers.css\";\n@import \"tailwindcss\";\n@import \"@santi020k/lumen-astro/styles.css\";\n```\n\nThis order places Tailwind base styles before Lumen components and Tailwind utilities above Lumen\ncomponent defaults.\n\n## Packages\n\n| Package | Purpose |\n| --- | --- |\n| [`@santi020k/lumen-astro`](./packages/astro) | Reference implementation, Astro components, and progressive-enhancement runtime |\n| [`@santi020k/lumen-react`](./packages/react) | React components and behavior hooks |\n| [`@santi020k/lumen-react-hook-form`](./packages/react-hook-form) | Optional React Hook Form adapters for composite controls |\n| [`@santi020k/lumen-elements`](./packages/elements) | Standards-based Web Components |\n| [`@santi020k/lumen-core`](./packages/core) | Shared tokens, metadata, class helpers, and behavior contracts |\n| [`@santi020k/lumen-tokens`](./packages/tokens) | Canonical platform-neutral design token source |\n| [`@santi020k/lumen-react-native`](./packages/react-native) | React Native foundations and native primitives |\n| [`LumenUI`](./packages/swift) | Swift Package for iOS, iPadOS, macOS, tvOS, visionOS, and watchOS foundations and native primitives |\n| [`LumenWidgetUI`](./packages/swift-widget) | Focused WidgetKit-safe semantic text, icon, badge, and compact-stat foundations |\n| [`lumen-compose`](./packages/compose) | Android library for Jetpack Compose foundations and native primitives |\n| [`lumen-compose-wear`](./packages/compose/wear) | Focused Wear OS primitives for at-a-glance round-screen actions, progress, status, metrics, and rows |\n| [`@santi020k/lumen-icons-brand`](./packages/icons-brand) | Optional namespaced brand icons for web `Icon` adapters |\n| [`@santi020k/lumen`](./packages/lumen) | Umbrella package, CLI, registry metadata, and public package map |\n| [`@santi020k/lumen-mcp`](./packages/mcp) | MCP server for component discovery and source retrieval |\n\n## AI and design workflows\n\nInstall the portable Lumen skill in Codex, Claude Code, Cursor, Windsurf, and other compatible\ncoding agents:\n\n```bash\nnpx skills add santi020k/lumen --skill lumen-ui\n```\n\nThe skill teaches agents how to select, compose, theme, and verify Lumen primitives. Pair it with\n[`@santi020k/lumen-mcp`](./packages/mcp) when an agent needs to search the live catalog or retrieve\ncurrent source, props, tokens, and usage rules.\n\nFor ChatGPT and Codex, install the published\n[Lumen UI plugin](https://chatgpt.com/plugins/plugin_asdk_app_6a8f6c526c5481918eb8a48806fa112b)\nfrom the Plugins Directory. Select **Install plugin**, then mention **@Lumen UI** in a request such\nas “Find the right Lumen components for an accessible React settings screen.” Version 1.0.0 bundles\nthe skill with the hosted, read-only catalog; no separate Lumen account or local MCP setup is needed.\n\nAdditional machine-readable surfaces include:\n\n- [`llms.txt`](./llms.txt) for a concise project map.\n- [`docs/ai-usage.md`](./docs/ai-usage.md) for downstream generation examples.\n- [`docs/styling-contract.md`](./docs/styling-contract.md) for stable parts and component variables.\n- [`docs/import-and-icon-performance.md`](./docs/import-and-icon-performance.md) for repeatable import and icon evidence.\n- [`docs/graphics.md`](./docs/graphics.md) for portable decorative artwork across web and native platforms.\n- [`docs/consumer-regression-fixtures.md`](./docs/consumer-regression-fixtures.md) for production-shaped local coverage.\n- [`docs/project-adoption.md`](./docs/project-adoption.md) for the sibling-project audit and staged\n web and native migration strategy.\n- [`registry/lumen.registry.json`](./registry/lumen.registry.json) for recipes and installable file\n groups.\n- [`docs/figma.md`](./docs/figma.md) for Figma variables and Code Connect workflows.\n- [`plugins/lumen-ui`](./plugins/lumen-ui) for the packaged Codex and ChatGPT plugin, with its\n [submission and review record](./docs/openai-plugin-submission.md).\n- [`.claude-plugin/marketplace.json`](./.claude-plugin/marketplace.json) for the Claude Code\n marketplace, with its [installation and submission guide](./docs/claude-code-plugin.md).\n- [`docs/cross-platform.md`](./docs/cross-platform.md) for the native architecture, support tiers,\n and component-parity policy.\n- [`docs/native-patterns.md`](./docs/native-patterns.md) for developed native compositions and the\n boundary between Lumen presentation and platform-owned application structure.\n\n## Contributing\n\nLumen is a pnpm workspace. Astro is the reference implementation; shared contracts belong in\n`packages/core`, while framework-specific behavior stays in its adapter package.\n\n```bash\npnpm install\npnpm run dev\npnpm run validate\n```\n\nRead [`CONTRIBUTING.md`](./CONTRIBUTING.md) before opening a pull request. User-visible package\nchanges require a changeset.\n\n## Community\n\nLumen is free and MIT licensed while the project focuses on adoption, public examples, and\nreal-world feedback.\n\n- Explore task-oriented [guides](https://lumen.santi020k.com/guides).\n- Share a public project in the [community showcase](https://lumen.santi020k.com/community).\n- Evaluate the system with the open [team adoption path](https://lumen.santi020k.com/teams).\n- Tell us what would improve your next build through the\n [adoption survey](https://github.com/santi020k/lumen/issues/new?template=adoption-feedback.yml).\n\nMaintainers can use the [exposure playbook](./docs/exposure-playbook.md) for publishing cadence,\ncampaign measurement, video scripts, and launch copy, and the\n[marketing package](./docs/marketing/README.md) for execution-ready copy, channel decisions, a\nfour-week calendar, asset briefs, an approval queue, and a community-response library.\n\n## License\n\nLumen's original code and artwork are available under the [MIT License](./LICENSE). Generated\ninterface icons retain the Lucide ISC or Feather MIT terms, and generated brand icons retain Font\nAwesome Free's CC BY 4.0 terms. The native packages include the required attribution and license\nnotices; see [`icons/THIRD_PARTY_NOTICES.md`](./icons/THIRD_PARTY_NOTICES.md).\n"
35319
35319
  },
35320
35320
  "meta": {
35321
- "catalogHash": "83c4e5f4fd5928d5b7df773f4deda3a3f054972dc608181295402630d0002929",
35321
+ "catalogHash": "2597097984279ba0de11b13c9c116095c2f628e90b640d69548cda60d4d4c85c",
35322
35322
  "componentCount": 161,
35323
35323
  "nativeComponentCount": 79,
35324
35324
  "packages": [
@@ -35332,11 +35332,11 @@
35332
35332
  "com.santi020k:lumen-compose"
35333
35333
  ],
35334
35334
  "packageVersions": {
35335
- "@santi020k/lumen-astro": "3.0.0",
35336
- "@santi020k/lumen-core": "3.0.0",
35337
- "@santi020k/lumen-elements": "3.0.0",
35338
- "@santi020k/lumen-mcp": "3.0.0",
35339
- "@santi020k/lumen-react": "3.0.0",
35335
+ "@santi020k/lumen-astro": "3.0.1",
35336
+ "@santi020k/lumen-core": "3.0.1",
35337
+ "@santi020k/lumen-elements": "3.0.1",
35338
+ "@santi020k/lumen-mcp": "3.0.1",
35339
+ "@santi020k/lumen-react": "3.0.1",
35340
35340
  "@santi020k/lumen-react-native": "3.0.0",
35341
35341
  "@santi020k/lumen-tokens": "3.0.0",
35342
35342
  "LumenUI": "workspace",
@@ -35346,7 +35346,7 @@
35346
35346
  "registryName": "lumen",
35347
35347
  "registryVersion": 1,
35348
35348
  "schemaVersion": 5,
35349
- "serverVersion": "3.0.0"
35349
+ "serverVersion": "3.0.1"
35350
35350
  },
35351
35351
  "nativeComponents": [
35352
35352
  {
@@ -44531,15 +44531,15 @@
44531
44531
  "peerDependencies": {
44532
44532
  "astro": ">=5"
44533
44533
  },
44534
- "version": "3.0.0"
44534
+ "version": "3.0.1"
44535
44535
  },
44536
44536
  "@santi020k/lumen-core": {
44537
44537
  "peerDependencies": {},
44538
- "version": "3.0.0"
44538
+ "version": "3.0.1"
44539
44539
  },
44540
44540
  "@santi020k/lumen-elements": {
44541
44541
  "peerDependencies": {},
44542
- "version": "3.0.0"
44542
+ "version": "3.0.1"
44543
44543
  },
44544
44544
  "@santi020k/lumen-icons-brand": {
44545
44545
  "peerDependencies": {},
@@ -44547,17 +44547,17 @@
44547
44547
  },
44548
44548
  "@santi020k/lumen": {
44549
44549
  "peerDependencies": {},
44550
- "version": "3.0.0"
44550
+ "version": "3.0.1"
44551
44551
  },
44552
44552
  "@santi020k/lumen-mcp": {
44553
44553
  "peerDependencies": {},
44554
- "version": "3.0.0"
44554
+ "version": "3.0.1"
44555
44555
  },
44556
44556
  "@santi020k/lumen-react": {
44557
44557
  "peerDependencies": {
44558
44558
  "react": ">=19"
44559
44559
  },
44560
- "version": "3.0.0"
44560
+ "version": "3.0.1"
44561
44561
  },
44562
44562
  "@santi020k/lumen-react-hook-form": {
44563
44563
  "peerDependencies": {
@@ -44585,9 +44585,9 @@
44585
44585
  "swift": {
44586
44586
  "package": "https://github.com/santi020k/lumen.git",
44587
44587
  "revision": null,
44588
- "tag": "v3.0.0"
44588
+ "tag": "v3.0.1"
44589
44589
  },
44590
- "version": "3.0.0"
44590
+ "version": "3.0.1"
44591
44591
  },
44592
44592
  "migration": {
44593
44593
  "codemod": null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@santi020k/lumen-mcp",
3
- "version": "3.0.0",
3
+ "version": "3.0.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },