lucent-ui 0.18.0 → 0.18.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.
File without changes
File without changes
File without changes
@@ -55,6 +55,13 @@ export const COMPONENT_MANIFEST = {
55
55
  default: 'false',
56
56
  description: 'Prevents interaction and dims the label.',
57
57
  },
58
+ {
59
+ name: 'portalContainer',
60
+ type: 'HTMLElement | null',
61
+ required: false,
62
+ description: 'DOM element to portal the popover into. Defaults to document.body. ' +
63
+ 'Set this to a wrapper element to preserve CSS custom property inheritance for per-section theming.',
64
+ },
58
65
  {
59
66
  name: 'id',
60
67
  type: 'string',
@@ -24,9 +24,10 @@ export const COMPONENT_MANIFEST = {
24
24
  '`-end`, or centered) is preserved during the flip. Position is computed with `getBoundingClientRect` ' +
25
25
  'on mount and rendered via `position: fixed` in a portal.\n\n' +
26
26
  '## Portal rendering\n' +
27
- 'The popover is portaled to `document.body` via `createPortal`. This prevents overflow clipping from ' +
27
+ 'The popover is portaled via `createPortal` (default: `document.body`). This prevents overflow clipping from ' +
28
28
  'parent containers with `overflow: hidden`. The trigger wrapper stays inline in the DOM tree so it ' +
29
- 'participates in layout normally.\n\n' +
29
+ 'participates in layout normally. The `portalContainer` prop lets consumers render the portal inside a ' +
30
+ 'wrapper element that sets `--lucent-*` CSS custom property overrides, preserving per-section theming.\n\n' +
30
31
  '## Keyboard navigation\n' +
31
32
  'Follows WAI-ARIA Menu Button pattern. Arrow keys cycle through enabled items (wrapping). Enter/Space ' +
32
33
  'selects the active item and closes the menu. Escape closes without selection. Tab closes and lets ' +
@@ -88,6 +89,13 @@ export const COMPONENT_MANIFEST = {
88
89
  required: false,
89
90
  description: 'Callback fired when the menu opens or closes. Receives the new open state.',
90
91
  },
92
+ {
93
+ name: 'portalContainer',
94
+ type: 'HTMLElement | null',
95
+ required: false,
96
+ description: 'DOM element to portal the popover into. Defaults to document.body. ' +
97
+ 'Set this to a wrapper element to preserve CSS custom property inheritance for per-section theming.',
98
+ },
91
99
  {
92
100
  name: 'style',
93
101
  type: 'object',
@@ -267,6 +275,6 @@ export const COMPONENT_MANIFEST = {
267
275
  notes: 'Focus returns to the trigger element after the menu is dismissed via Escape or selection. ' +
268
276
  'Disabled items are skipped during keyboard navigation and have aria-disabled. ' +
269
277
  'Selected items use role="menuitemcheckbox" with aria-checked for screen reader announcement. ' +
270
- 'The popover is portaled to document.body but remains semantically linked to the trigger via aria-controls.',
278
+ 'The popover is portaled to document.body (or portalContainer) but remains semantically linked to the trigger via aria-controls.',
271
279
  },
272
280
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lucent-ui",
3
- "version": "0.18.0",
3
+ "version": "0.18.1",
4
4
  "description": "An AI-first React component library with machine-readable manifests.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -25,18 +25,6 @@
25
25
  "dist-server",
26
26
  "dist-cli"
27
27
  ],
28
- "scripts": {
29
- "dev": "vite --config vite.dev.config.ts",
30
- "build": "vite build",
31
- "build:server": "tsc -p server/tsconfig.json",
32
- "build:cli": "tsc -p cli/tsconfig.json && cp cli/template.manifest.json dist-cli/cli/template.manifest.json",
33
- "test": "vitest run",
34
- "test:watch": "vitest",
35
- "prepublishOnly": "tsc --noEmit && pnpm build && pnpm build:server && pnpm build:cli",
36
- "changeset": "changeset",
37
- "version-packages": "changeset version",
38
- "release": "pnpm prepublishOnly && changeset publish"
39
- },
40
28
  "keywords": [
41
29
  "react",
42
30
  "component-library",
@@ -51,7 +39,6 @@
51
39
  },
52
40
  "author": "Rozina Szogyenyi",
53
41
  "license": "MIT",
54
- "packageManager": "pnpm@10.30.3",
55
42
  "peerDependencies": {
56
43
  "react": "^18.0.0 || ^19.0.0",
57
44
  "react-dom": "^18.0.0 || ^19.0.0"
@@ -73,5 +60,16 @@
73
60
  "dependencies": {
74
61
  "@modelcontextprotocol/sdk": "^1.27.1",
75
62
  "zod": "^4.3.6"
63
+ },
64
+ "scripts": {
65
+ "dev": "vite --config vite.dev.config.ts",
66
+ "build": "vite build",
67
+ "build:server": "tsc -p server/tsconfig.json",
68
+ "build:cli": "tsc -p cli/tsconfig.json && cp cli/template.manifest.json dist-cli/cli/template.manifest.json",
69
+ "test": "vitest run",
70
+ "test:watch": "vitest",
71
+ "changeset": "changeset",
72
+ "version-packages": "changeset version",
73
+ "release": "pnpm prepublishOnly && changeset publish"
76
74
  }
77
- }
75
+ }
@@ -1,28 +0,0 @@
1
- import { describe, test, expect } from 'vitest';
2
- import { validateManifest } from './validate.js';
3
- // Auto-discover all component manifests
4
- const manifestModules = import.meta.glob('../components/**/*.manifest.ts', { eager: true });
5
- const manifests = Object.entries(manifestModules).map(([path, mod]) => {
6
- const m = mod;
7
- const manifest = m['COMPONENT_MANIFEST'];
8
- return { path, manifest };
9
- });
10
- describe('Component manifests', () => {
11
- test('at least one manifest was discovered', () => {
12
- expect(manifests.length).toBeGreaterThan(0);
13
- });
14
- for (const { path, manifest } of manifests) {
15
- const label = path.replace('../components/', '').replace('.manifest.ts', '');
16
- test(`${label} — exports COMPONENT_MANIFEST`, () => {
17
- expect(manifest).toBeDefined();
18
- });
19
- test(`${label} — passes schema validation`, () => {
20
- const result = validateManifest(manifest);
21
- if (!result.valid) {
22
- const messages = result.errors.map(e => ` ${e.field}: ${e.message}`).join('\n');
23
- throw new Error(`Invalid manifest:\n${messages}`);
24
- }
25
- expect(result.valid).toBe(true);
26
- });
27
- }
28
- });