From a4341cafa9a0b8de4c093509d9800b3722a3912c Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Tue, 5 May 2026 10:46:22 -0400 Subject: [PATCH 1/7] feat(headless): add package foundation with Dialog primitive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces the @clerk/headless package — a zero-style React component library providing accessible headless UI primitives. This first PR establishes the core infrastructure and patterns: - Package scaffold: Vite build, Vitest browser tests (Chromium), TypeScript config - Core utils: renderElement (polymorphic rendering with render prop support), mergeProps (event handler chaining, style/className merging) - Hooks: useControllableState, useTransitionStatus, useAnimationsFinished, useTransition - First primitive: Dialog (modal with focus trapping, scroll lock, portal support) Co-Authored-By: Claude Opus 4.6 --- pnpm-lock.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7f5f20af86a..bffe58b405a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21501,6 +21501,19 @@ snapshots: optionalDependencies: typescript: 5.8.3 + '@vue/language-core@2.2.0(typescript@5.8.3)': + dependencies: + '@volar/language-core': 2.4.27 + '@vue/compiler-dom': 3.5.33 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.33 + alien-signals: 0.4.14 + minimatch: 9.0.9 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.8.3 + '@vue/language-core@3.1.4(typescript@5.8.3)': dependencies: '@volar/language-core': 2.4.23 From e1672499a61d8ec5543561005bc920dd714b1c32 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Tue, 5 May 2026 10:46:22 -0400 Subject: [PATCH 2/7] feat(headless): add package foundation with Dialog primitive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces the @clerk/headless package — a zero-style React component library providing accessible headless UI primitives. This first PR establishes the core infrastructure and patterns: - Package scaffold: Vite build, Vitest browser tests (Chromium), TypeScript config - Core utils: renderElement (polymorphic rendering with render prop support), mergeProps (event handler chaining, style/className merging) - Hooks: useControllableState, useTransitionStatus, useAnimationsFinished, useTransition - First primitive: Dialog (modal with focus trapping, scroll lock, portal support) Co-Authored-By: Claude Opus 4.6 --- pnpm-lock.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bffe58b405a..930ca130a23 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21514,6 +21514,19 @@ snapshots: optionalDependencies: typescript: 5.8.3 + '@vue/language-core@2.2.0(typescript@5.8.3)': + dependencies: + '@volar/language-core': 2.4.27 + '@vue/compiler-dom': 3.5.33 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.33 + alien-signals: 0.4.14 + minimatch: 9.0.9 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.8.3 + '@vue/language-core@3.1.4(typescript@5.8.3)': dependencies: '@volar/language-core': 2.4.23 From ed793733846dbce193645f76222c9e8ab2320e0d Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Tue, 5 May 2026 10:46:22 -0400 Subject: [PATCH 3/7] feat(headless): add package foundation with Dialog primitive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces the @clerk/headless package — a zero-style React component library providing accessible headless UI primitives. This first PR establishes the core infrastructure and patterns: - Package scaffold: Vite build, Vitest browser tests (Chromium), TypeScript config - Core utils: renderElement (polymorphic rendering with render prop support), mergeProps (event handler chaining, style/className merging) - Hooks: useControllableState, useTransitionStatus, useAnimationsFinished, useTransition - First primitive: Dialog (modal with focus trapping, scroll lock, portal support) Co-Authored-By: Claude Opus 4.6 --- pnpm-lock.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 930ca130a23..2c3e4114bf9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21527,6 +21527,19 @@ snapshots: optionalDependencies: typescript: 5.8.3 + '@vue/language-core@2.2.0(typescript@5.8.3)': + dependencies: + '@volar/language-core': 2.4.27 + '@vue/compiler-dom': 3.5.33 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.33 + alien-signals: 0.4.14 + minimatch: 9.0.9 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.8.3 + '@vue/language-core@3.1.4(typescript@5.8.3)': dependencies: '@volar/language-core': 2.4.23 From dcfd8fe54b68a4a20bb3254b621dd720fd0f7932 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Tue, 5 May 2026 11:38:36 -0400 Subject: [PATCH 4/7] feat(headless): add cssVars utility and Tooltip primitive --- packages/headless/package.json | 4 + .../headless/src/primitives/tooltip/README.md | 115 ++++++ .../headless/src/primitives/tooltip/index.ts | 10 + .../src/primitives/tooltip/tooltip.test.tsx | 309 ++++++++++++++++ .../src/primitives/tooltip/tooltip.tsx | 333 ++++++++++++++++++ packages/headless/src/utils/css-vars.test.ts | 269 ++++++++++++++ packages/headless/src/utils/css-vars.ts | 76 ++++ packages/headless/src/utils/index.ts | 1 + packages/headless/vite.config.ts | 1 + 9 files changed, 1118 insertions(+) create mode 100644 packages/headless/src/primitives/tooltip/README.md create mode 100644 packages/headless/src/primitives/tooltip/index.ts create mode 100644 packages/headless/src/primitives/tooltip/tooltip.test.tsx create mode 100644 packages/headless/src/primitives/tooltip/tooltip.tsx create mode 100644 packages/headless/src/utils/css-vars.test.ts create mode 100644 packages/headless/src/utils/css-vars.ts diff --git a/packages/headless/package.json b/packages/headless/package.json index 8a255213ca1..6745f1917b9 100644 --- a/packages/headless/package.json +++ b/packages/headless/package.json @@ -13,6 +13,10 @@ "import": "./dist/primitives/tabs/index.js", "types": "./dist/primitives/tabs/index.d.ts" }, + "./tooltip": { + "import": "./dist/primitives/tooltip/index.js", + "types": "./dist/primitives/tooltip/index.d.ts" + }, "./dialog": { "import": "./dist/primitives/dialog/index.js", "types": "./dist/primitives/dialog/index.d.ts" diff --git a/packages/headless/src/primitives/tooltip/README.md b/packages/headless/src/primitives/tooltip/README.md new file mode 100644 index 00000000000..41cca266d07 --- /dev/null +++ b/packages/headless/src/primitives/tooltip/README.md @@ -0,0 +1,115 @@ +# Tooltip + +A floating label that appears on hover or focus. Non-interactive, used for supplementary descriptions. No focus trapping — tooltips never receive focus. + +## When to Use + +- Describing icon buttons, truncated text, or any element that benefits from a short label. +- When the content is display-only (no interactive elements inside). +- Prefer Tooltip over Popover when the content is a simple text label that should appear on hover/focus and disappear immediately. + +## Usage + +```tsx +import { Tooltip } from '@/primitives/tooltip'; + + + + + + + + Settings + + + +; +``` + +### Controlled + +```tsx +const [open, setOpen] = useState(false); + + + {/* ... */} +; +``` + +### Custom Delay + +```tsx + + {/* Opens after 500ms hover, closes 100ms after leaving */} + +``` + +## Parts + +| Part | Default Element | Description | +| -------------------- | --------------- | ------------------------------------------------ | +| `Tooltip` | — | Root context provider | +| `Tooltip.Trigger` | `