Repository: melt-ui/melt-ui
Pull request #849 (2023-12-22 23:02:39)

Slider: Refactor Thumb and Tick
This pr changes and `thumb` and `tick` elements from being functions to just arrays of builders.

```html
<!-- the old API -->
{#each { length: $ticks } as _}
  <span use:melt={$tick()} />
{/each}

{#each $value as _}
  <span use:melt={$thumb()} />
{/each}

<!-- the new API -->
{#each $thumbs as thumb}
  <span use:melt={thumb} />
{/each}

{#each $ticks as tick}
  <span use:melt={tick} />
{/each}
```

Limitations:
The melt pp does not seem to work with the new loop syntax. I added TODOs to replace the loops later on when this issue is fixed.

Update:
The pp works fine. The problem is in the docs itself.



Patch:
diff --git a/.changeset/lazy-zoos-itch.md b/.changeset/lazy-zoos-itch.md
new file mode 100644
index 00000000..d9af9734
--- /dev/null
+++ b/.changeset/lazy-zoos-itch.md
@@ -0,0 +1,5 @@
+---
+"@melt-ui/svelte": minor
+---
+
+Slider: Refactor thumb and tick
diff --git a/.changeset/plenty-doors-rest.md b/.changeset/plenty-doors-rest.md
new file mode 100644
index 00000000..7e87577e
--- /dev/null
+++ b/.changeset/plenty-doors-rest.md
@@ -0,0 +1,5 @@
+---
+'@melt-ui/svelte': minor
+---
+
+Combobox: add `group` and `groupLabel` elements (closes: #831)
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index acaf9237..00000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,40 +0,0 @@
----
-name: Bug report
-about: Create a report to help us improve
-title: "\U0001F41BBug:"
-labels: bug
-assignees: ''
-
----
-
-**Describe the bug**
-A clear and concise description of what the bug is.
-
-**To Reproduce**
-Steps to reproduce the behavior:
-1. Go to '...'
-2. Click on '....'
-3. Scroll down to '....'
-4. See error
-
-**Expected behavior**
-A clear and concise description of what you expected to happen.
-
-**Screenshots**
-If applicable, add screenshots to help explain your problem.
-
-**Desktop (please complete the following information):**
- - OS: [e.g. iOS]
- - Browser [e.g. chrome, safari]
- - Version [e.g. 22]
-
-**Smartphone (please complete the following information):**
- - Device: [e.g. iPhone6]
- - OS: [e.g. iOS8.1]
- - Browser [e.g. stock browser, safari]
- - Version [e.g. 22]
-
-**Additional context**
-Add any other context about the problem here.
-
-To reproduce using Stackblitz, use this [template](https://stackblitz.com/edit/melt-ui-playground) and modify the `src/routes/+page.svelte` file.
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 00000000..420ef513
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,50 @@
+name: "🐛 Bug report"
+description: Report an issue with Melt UI
+labels: ["bug"]
+body:
+  - type: markdown
+    attributes:
+      value: |
+        Thanks for taking the time to fill out this bug report!
+  - type: textarea
+    id: bug-description
+    attributes:
+      label: Describe the bug
+      description: A clear and concise description of what the bug is. Feel free to include screenshots or video recordings here as well! If you intend to submit a PR for this issue, tell us how in the description. Thanks!
+      placeholder: Bug description
+    validations:
+      required: true
+  - type: textarea
+    id: reproduction
+    attributes:
+      label: Reproduction
+      description: Please provide a link to a repo or SvelteLab (https://www.sveltelab.dev/?t=melt_ui) that can reproduce the problem you ran into. If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "need reproduction" label. If no reproduction is provided within a reasonable time-frame, the issue will be closed.
+      placeholder: Reproduction
+    validations:
+      required: true
+  - type: textarea
+    id: logs
+    attributes:
+      label: Logs
+      description: "Please include browser console and server logs around the time this bug occurred. Optional if provided reproduction. Please try not to insert an image but copy paste the log text."
+      render: bash
+  - type: textarea
+    id: system-info
+    attributes:
+      label: System Info
+      description: Output of `npx envinfo --system --npmPackages svelte,@sveltejs/kit,@melt-ui/svelte --binaries --browsers`
+      render: bash
+      placeholder: System, Binaries, Browsers
+    validations:
+      required: true
+  - type: dropdown
+    id: severity
+    attributes:
+      label: Severity
+      description: Select the severity of this issue
+      options:
+        - annoyance
+        - blocking an upgrade
+        - blocking all usage of Melt UI
+    validations:
+      required: true
diff --git a/.storybook/main.ts b/.storybook/main.ts
index fd190337..53258db2 100644
--- a/.storybook/main.ts
+++ b/.storybook/main.ts
@@ -8,7 +8,6 @@ const config: StorybookConfig = {
 		'@storybook/addon-interactions',
 		'@storybook/addon-styling',
 		'@storybook/addon-a11y',
-		// '@chromaui/addon-visual-tests',
 	],
 	framework: '@storybook/sveltekit',
 	docs: {
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 81dd081a..0885a99a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,57 @@
 # @melt-ui/svelte
 
+## 0.69.0
+
+### Minor Changes
+
+- Feature: `createSync` utility ([`f018010f`](https://github.com/melt-ui/melt-ui/commit/f018010ff922b70a5eeaaa5b94ae519e8ccbf7c1))
+
+## 0.68.0
+
+### Minor Changes
+
+- Menubar: Add `preventScroll` prop. Removes it from the returned `createMenu` builders (closes #821) ([#882](https://github.com/melt-ui/melt-ui/pull/882))
+
+### Patch Changes
+
+- Radio Group: fix keyboard navigation when disabled (closes #820) ([#884](https://github.com/melt-ui/melt-ui/pull/884))
+
+- fix: missing tooltip data-attrs ([#889](https://github.com/melt-ui/melt-ui/pull/889))
+
+- DateField & DateRangeField: add `readonlySegments` prop ([#846](https://github.com/melt-ui/melt-ui/pull/846))
+
+- chore: upgrade nanoid to v5 ([#851](https://github.com/melt-ui/melt-ui/pull/851))
+
+- Fix(calendar): extraDays array should add days starting from 1 ([#869](https://github.com/melt-ui/melt-ui/pull/869))
+
+- fix(menu): focus on the trigger when rootActiveTrigger is defined ([#871](https://github.com/melt-ui/melt-ui/pull/871))
+
+- Progress: allow `null` value for indeterminate state ([#870](https://github.com/melt-ui/melt-ui/pull/870))
+
+## 0.67.0
+
+### Minor Changes
+
+- feat: expose `onOutsideClick` handler ([#863](https://github.com/melt-ui/melt-ui/pull/863))
+
+## 0.66.4
+
+### Patch Changes
+
+- Toast: return `aria-live` type as `'assertive' | 'polite'` ([#859](https://github.com/melt-ui/melt-ui/pull/859))
+
+- Tags Input: Fix addOnPaste (closes #830) ([#847](https://github.com/melt-ui/melt-ui/pull/847))
+
+- escape-keydown: remove `capture` listener (fixes [#842](https://github.com/melt-ui/melt-ui/issues/842)) ([#845](https://github.com/melt-ui/melt-ui/pull/845))
+
+- Combobox: change default Value props type ([`835c285a`](https://github.com/melt-ui/melt-ui/commit/835c285a774cc00455980693a5978f0969ef05ee))
+
+## 0.66.3
+
+### Patch Changes
+
+- Menu: fix default active subtrigger ([`84270042`](https://github.com/melt-ui/melt-ui/commit/84270042ee257a9dff0efaa97eacd3ec9ce04a6c))
+
 ## 0.66.2
 
 ### Patch Changes
diff --git a/package.json b/package.json
index a28f035c..6739715c 100644
--- a/package.json
+++ b/package.json
@@ -1,13 +1,13 @@
 {
 	"name": "@melt-ui/svelte",
-	"version": "0.66.2",
+	"version": "0.69.0",
 	"license": "MIT",
 	"repository": "github:melt-ui/melt-ui",
 	"author": "Thomas G. Lopes",
 	"scripts": {
 		"dev": "vite dev",
 		"build": "vite build && npm run package",
-		"deploy": "export PREDEPLOY=0 && vite build",
+		"deploy": "vite build && pnpm run pagefind",
 		"preview": "vite preview",
 		"package": "svelte-kit sync && svelte-package && publint",
 		"prepublishOnly": "npm run package",
@@ -25,8 +25,7 @@
 		"pw": "pnpm exec playwright test",
 		"pw:ui": "pnpm exec playwright test --ui",
 		"changeset": "changeset",
-		"pagefind": "node ./scripts/pagefind.js",
-		"pre-deploy": "export PREDEPLOY=1 && vite build && pnpm run pagefind"
+		"pagefind": "node ./scripts/pagefind.js"
 	},
 	"exports": {
 		".": {
@@ -68,15 +67,12 @@
 		"@internationalized/date": "^3.5.0",
 		"dequal": "^2.0.3",
 		"focus-trap": "^7.5.2",
-		"nanoid": "^4.0.2"
+		"nanoid": "^5.0.4"
 	},
 	"devDependencies": {
 		"@changesets/cli": "^2.26.2",
-		"@chromaui/addon-visual-tests": "^0.0.124",
 		"@fontsource/inter": "^5.0.8",
 		"@huntabyte/mdsvex": "0.16.5",
-		"@iconify-json/lucide": "^1.1.104",
-		"@iconify-json/simple-icons": "^1.1.57",
 		"@melt-ui/pp": "0.1.0",
 		"@playwright/test": "^1.39.0",
 		"@storybook/addon-a11y": "^7.6.6",
@@ -88,13 +84,9 @@
 		"@storybook/svelte": "^7.6.6",
 		"@storybook/sveltekit": "^7.6.6",
 		"@storybook/testing-library": "^0.2.2",
-		"@sveltejs/adapter-auto": "^2.1.0",
-		"@sveltejs/adapter-cloudflare": "^2.3.4",
 		"@sveltejs/adapter-static": "^2.0.3",
-		"@sveltejs/adapter-vercel": "^3.0.2",
 		"@sveltejs/kit": "^1.22.3",
 		"@sveltejs/package": "^2.2.0",
-		"@sveltejs/vite-plugin-svelte": "^3.0.1",
 		"@svitejs/changesets-changelog-github-compact": "^1.1.0",
 		"@tailwindcss/typography": "^0.5.9",
 		"@testing-library/dom": "^9.3.1",
@@ -102,24 +94,20 @@
 		"@testing-library/svelte": "^4.0.3",
 		"@testing-library/user-event": "^14.4.3",
 		"@types/jest-axe": "^3.5.5",
-		"@types/prettier": "^2.7.3",
-		"@types/testing-library__jest-dom": "^5.14.9",
 		"@typescript-eslint/eslint-plugin": "^5.62.0",
+		"@types/testing-library__jest-dom": "^5.14.9",
 		"@typescript-eslint/parser": "^5.62.0",
 		"@vitest/coverage-v8": "^0.33.0",
 		"@vitest/ui": "^0.34.6",
 		"autoprefixer": "^10.4.14",
-		"clsx": "^1.2.1",
+		"clsx": "^2.1.0",
 		"escape-html": "^1.0.3",
 		"eslint": "^8.45.0",
 		"eslint-config-prettier": "^8.9.0",
 		"eslint-plugin-storybook": "^0.6.15",
 		"eslint-plugin-svelte": "^2.33.0",
-		"fathom-client": "^3.5.0",
-		"glob": "^9.3.2",
 		"happy-dom": "^10.5.2",
 		"hast-util-to-html": "^8.0.4",
-		"highlight.js": "^11.8.0",
 		"jest-axe": "^8.0.0",
 		"jsdom": "^22.1.0",
 		"lucide-svelte": "^0.263.0",
@@ -144,11 +132,8 @@
 		"svelte": "^4.0.0",
 		"svelte-action-balancer": "^1.0.4",
 		"svelte-check": "^3.4.6",
-		"svelte-highlight": "^7.3.0",
-		"svelte-htm": "^1.2.0",
-		"svelte-preprocess": "^5.0.4",
 		"svelte-sequential-preprocessor": "^2.0.1",
-		"tailwind-merge": "^1.14.0",
+		"tailwind-merge": "^2.2.0",
 		"tailwind-variants": "^0.1.8",
 		"tailwindcss": "^3.3.3",
 		"tslib": "^2.6.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d492c258..484589d7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -21,31 +21,22 @@ dependencies:
     specifier: ^7.5.2
     version: 7.5.2
   nanoid:
-    specifier: ^4.0.2
-    version: 4.0.2
+    specifier: ^5.0.4
+    version: 5.0.4
 
 devDependencies:
   '@changesets/cli':
     specifier: ^2.26.2
     version: 2.26.2
-  '@chromaui/addon-visual-tests':
-    specifier: ^0.0.124
-    version: 0.0.124(@storybook/blocks@7.6.6)(@storybook/client-logger@7.6.6)(@storybook/components@7.6.6)(@storybook/core-events@7.6.6)(@storybook/manager-api@7.6.6)(@storybook/preview-api@7.6.6)(@storybook/theming@7.6.6)(@storybook/types@7.6.6)(react-dom@18.2.0)(react@18.2.0)
   '@fontsource/inter':
     specifier: ^5.0.8
     version: 5.0.8
   '@huntabyte/mdsvex':
     specifier: 0.16.5
     version: 0.16.5(svelte@4.0.0)
-  '@iconify-json/lucide':
-    specifier: ^1.1.104
-    version: 1.1.104
-  '@iconify-json/simple-icons':
-    specifier: ^1.1.57
-    version: 1.1.57
   '@melt-ui/pp':
     specifier: 0.1.0
-    version: 0.1.0(@melt-ui/svelte@0.63.1)(svelte@4.0.0)
+    version: 0.1.0(@melt-ui/svelte@0.67.0)(svelte@4.0.0)
   '@playwright/test':
     specifier: ^1.39.0
     version: 1.39.0
@@ -76,27 +67,15 @@ devDependencies:
   '@storybook/testing-library':
     specifier: ^0.2.2
     version: 0.2.2
-  '@sveltejs/adapter-auto':
-    specifier: ^2.1.0
-    version: 2.1.0(@sveltejs/kit@1.22.3)
-  '@sveltejs/adapter-cloudflare':
-    specifier: ^2.3.4
-    version: 2.3.4(@sveltejs/kit@1.22.3)
   '@sveltejs/adapter-static':
     specifier: ^2.0.3
     version: 2.0.3(@sveltejs/kit@1.22.3)
-  '@sveltejs/adapter-vercel':
-    specifier: ^3.0.2
-    version: 3.0.2(@sveltejs/kit@1.22.3)
   '@sveltejs/kit':
     specifier: ^1.22.3
     version: 1.22.3(svelte@4.0.0)(vite@4.4.7)
   '@sveltejs/package':
     specifier: ^2.2.0
     version: 2.2.0(svelte@4.0.0)(typescript@5.1.6)
-  '@sveltejs/vite-plugin-svelte':
-    specifier: ^3.0.1
-    version: 3.0.1(svelte@4.0.0)(vite@4.4.7)
   '@svitejs/changesets-changelog-github-compact':
     specifier: ^1.1.0
     version: 1.1.0
@@ -118,9 +97,6 @@ devDependencies:
   '@types/jest-axe':
     specifier: ^3.5.5
     version: 3.5.5
-  '@types/prettier':
-    specifier: ^2.7.3
-    version: 2.7.3
   '@types/testing-library__jest-dom':
     specifier: ^5.14.9
     version: 5.14.9
@@ -140,8 +116,8 @@ devDependencies:
     specifier: ^10.4.14
     version: 10.4.14(postcss@8.4.27)
   clsx:
-    specifier: ^1.2.1
-    version: 1.2.1
+    specifier: ^2.1.0
+    version: 2.1.0
   escape-html:
     specifier: ^1.0.3
     version: 1.0.3
@@ -157,21 +133,12 @@ devDependencies:
   eslint-plugin-svelte:
     specifier: ^2.33.0
     version: 2.33.0(eslint@8.45.0)(svelte@4.0.0)
-  fathom-client:
-    specifier: ^3.5.0
-    version: 3.5.0
-  glob:
-    specifier: ^9.3.2
-    version: 9.3.4
   happy-dom:
     specifier: ^10.5.2
     version: 10.5.2
   hast-util-to-html:
     specifier: ^8.0.4
     version: 8.0.4
-  highlight.js:
-    specifier: ^11.8.0
-    version: 11.8.0
   jest-axe:
     specifier: ^8.0.0
     version: 8.0.0
@@ -244,21 +211,12 @@ devDependencies:
   svelte-check:
     specifier: ^3.4.6
     version: 3.4.6(@babel/core@7.23.6)(less@4.2.0)(postcss-load-config@4.0.1)(postcss@8.4.27)(svelte@4.0.0)
-  svelte-highlight:
-    specifier: ^7.3.0
-    version: 7.3.0
-  svelte-htm:
-    specifier: ^1.2.0
-    version: 1.2.0(svelte@4.0.0)
-  svelte-preprocess:
-    specifier: ^5.0.4
-    version: 5.0.4(@babel/core@7.23.6)(less@4.2.0)(postcss-load-config@4.0.1)(postcss@8.4.27)(svelte@4.0.0)(typescript@5.1.6)
   svelte-sequential-preprocessor:
     specifier: ^2.0.1
     version: 2.0.1
   tailwind-merge:
-    specifier: ^1.14.0
-    version: 1.14.0
+    specifier: ^2.2.0
+    version: 2.2.0
   tailwind-variants:
     specifier: ^0.1.8
     version: 0.1.8(tailwindcss@3.3.3)
@@ -283,15 +241,6 @@ devDependencies:
 
 packages:
 
-  /@0no-co/graphql.web@1.0.4:
-    resolution: {integrity: sha512-W3ezhHGfO0MS1PtGloaTpg0PbaT8aZSmmaerL7idtU5F7oCI+uu25k+MsMS31BVFlp4aMkHSrNRxiD72IlK8TA==}
-    peerDependencies:
-      graphql: ^14.0.0 || ^15.0.0 || ^16.0.0
-    peerDependenciesMeta:
-      graphql:
-        optional: true
-    dev: true
-
   /@aashutoshrathi/word-wrap@1.2.6:
     resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
     engines: {node: '>=0.10.0'}
@@ -1766,19 +1715,18 @@ packages:
     resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==}
     dev: true
 
-  /@babel/runtime-corejs3@7.21.0:
-    resolution: {integrity: sha512-TDD4UJzos3JJtM+tHX+w2Uc+KWj7GV+VKKFdMVd2Rx8sdA19hcc3P3AHFYd5LVOw+pYuSd5lICC3gm52B6Rwxw==}
+  /@babel/runtime@7.21.0:
+    resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      core-js-pure: 3.30.0
       regenerator-runtime: 0.13.11
     dev: true
 
-  /@babel/runtime@7.21.0:
-    resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==}
+  /@babel/runtime@7.23.7:
+    resolution: {integrity: sha512-w06OXVOFso7LcbzMiDGt+3X7Rh7Ho8MmgPoWU3rarH+8upf+wSU/grlGbWzQyr3DkdN6ZeuMFjpdwW0Q+HxobA==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      regenerator-runtime: 0.13.11
+      regenerator-runtime: 0.14.1
     dev: true
 
   /@babel/template@7.22.15:
@@ -2050,55 +1998,6 @@ packages:
       prettier: 2.8.7
     dev: true
 
-  /@chromaui/addon-visual-tests@0.0.124(@storybook/blocks@7.6.6)(@storybook/client-logger@7.6.6)(@storybook/components@7.6.6)(@storybook/core-events@7.6.6)(@storybook/manager-api@7.6.6)(@storybook/preview-api@7.6.6)(@storybook/theming@7.6.6)(@storybook/types@7.6.6)(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-zPHwOGrX6xIFug2XlvzkQA/KAFrv2yVqh8jEM4pIdVOvcwIO4XK1jlikJqtdccdzDmnjtkuYc+nJA5SdNaJy+Q==}
-    engines: {node: '>=16.0.0', yarn: '>=1.22.18'}
-    peerDependencies:
-      '@storybook/blocks': ^7.2.0
-      '@storybook/client-logger': ^7.2.0
-      '@storybook/components': ^7.2.0
-      '@storybook/core-events': ^7.2.0
-      '@storybook/manager-api': ^7.2.0
-      '@storybook/preview-api': ^7.2.0
-      '@storybook/theming': ^7.2.0
-      '@storybook/types': ^7.2.0
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      react:
-        optional: true
-      react-dom:
-        optional: true
-    dependencies:
-      '@storybook/blocks': 7.6.6(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/client-logger': 7.6.6
-      '@storybook/components': 7.6.6(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.6.6
-      '@storybook/design-system': 7.15.15(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/manager-api': 7.6.6(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.6.6
-      '@storybook/theming': 7.6.6(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.6.6
-      '@urql/exchange-auth': 2.1.6
-      chromatic: 9.1.0
-      date-fns: 2.30.0
-      filesize: 10.1.0
-      jsonfile: 6.1.0
-      pluralize: 8.0.0
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-      ts-dedent: 2.2.0
-      urql: 4.0.6(react@18.2.0)
-      uuid: 9.0.0
-      zod: 3.22.4
-    transitivePeerDependencies:
-      - graphql
-    dev: true
-
-  /@cloudflare/workers-types@4.20231121.0:
-    resolution: {integrity: sha512-+kWfpCkqiepwAKXyHoE0gnkPgkLhz0/9HOBIGhHRsUvUKvhUtm3mbqqoGRWgF1qcjzrDUBbrrOq4MYHfFtc2RA==}
-    dev: true
-
   /@colors/colors@1.5.0:
     resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
     engines: {node: '>=0.1.90'}
@@ -2119,10 +2018,6 @@ packages:
       react: 18.2.0
     dev: true
 
-  /@emotion/weak-memoize@0.3.1:
-    resolution: {integrity: sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==}
-    dev: true
-
   /@esbuild/android-arm64@0.18.17:
     resolution: {integrity: sha512-9np+YYdNDed5+Jgr1TdWBsozZ85U1Oa3xW0c7TWqH0y2aGghXtZsuT8nYRbzOMcl0bXZXjOGbksoTtVOlWrRZg==}
     engines: {node: '>=12'}
@@ -2389,6 +2284,12 @@ packages:
       '@floating-ui/utils': 0.1.6
     dev: true
 
+  /@floating-ui/core@1.5.2:
+    resolution: {integrity: sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==}
+    dependencies:
+      '@floating-ui/utils': 0.1.6
+    dev: true
+
   /@floating-ui/dom@1.4.5:
     resolution: {integrity: sha512-96KnRWkRnuBSSFbj0sFGwwOUd8EkiecINVl0O9wiZlZ64EkpyAOG3Xc2vKKNJmru0Z7RqWNymA+6b8OZqjgyyw==}
     dependencies:
@@ -2464,38 +2365,17 @@ packages:
       vfile-message: 2.0.4
     dev: true
 
-  /@hypnosphi/create-react-context@0.3.1(prop-types@15.8.1)(react@18.2.0):
-    resolution: {integrity: sha512-V1klUed202XahrWJLLOT3EXNeCpFHCcJntdFGI15ntCwau+jfT386w7OFTMaCqOgXUH1fa0w/I1oZs+i/Rfr0A==}
-    peerDependencies:
-      prop-types: ^15.0.0
-      react: '>=0.14.0'
-    dependencies:
-      gud: 1.0.0
-      prop-types: 15.8.1
-      react: 18.2.0
-      warning: 4.0.3
-    dev: true
-
-  /@iconify-json/lucide@1.1.104:
-    resolution: {integrity: sha512-X1qsjZA7ySZI1PQl7w/9NDk5aFWKvYapdXd5XS/mdZJm+wZbZy6QqDpdy28X2YVxwKXeGp63WHcJNRqHHk1BtQ==}
-    dependencies:
-      '@iconify/types': 2.0.0
-    dev: true
-
-  /@iconify-json/simple-icons@1.1.57:
-    resolution: {integrity: sha512-s6vCsqkxkrbsmNkpspTvpN+e852FQ5QWpD0n3N8CfB75AZQ42n8gmwSrst99qH9yFwN3yQQVLwwJwPwIhxefsQ==}
-    dependencies:
-      '@iconify/types': 2.0.0
-    dev: true
-
-  /@iconify/types@2.0.0:
-    resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
-    dev: true
-
   /@internationalized/date@3.5.0:
     resolution: {integrity: sha512-nw0Q+oRkizBWMioseI8+2TeUPEyopJVz5YxoYVzR0W1v+2YytiYah7s/ot35F149q/xAg4F1gT/6eTd+tsUpFQ==}
     dependencies:
       '@swc/helpers': 0.5.3
+    dev: false
+
+  /@internationalized/date@3.5.1:
+    resolution: {integrity: sha512-LUQIfwU9e+Fmutc/DpRTGXSdgYZLBegi4wygCWDSVmUdLTaMHsQyASDiJtREwanwKuQLq0hY76fCJ9J/9I2xOQ==}
+    dependencies:
+      '@swc/helpers': 0.5.3
+    dev: true
 
   /@isaacs/cliui@8.0.2:
     resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
@@ -2662,24 +2542,6 @@ packages:
       read-yaml-file: 1.1.0
     dev: true
 
-  /@mapbox/node-pre-gyp@1.0.10:
-    resolution: {integrity: sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==}
-    hasBin: true
-    dependencies:
-      detect-libc: 2.0.1
-      https-proxy-agent: 5.0.1
-      make-dir: 3.1.0
-      node-fetch: 2.6.11
-      nopt: 5.0.0
-      npmlog: 5.0.1
-      rimraf: 3.0.2
-      semver: 7.5.4
-      tar: 6.1.15
-    transitivePeerDependencies:
-      - encoding
-      - supports-color
-    dev: true
-
   /@mdx-js/react@2.3.0(react@18.2.0):
     resolution: {integrity: sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g==}
     peerDependencies:
@@ -2690,25 +2552,25 @@ packages:
       react: 18.2.0
     dev: true
 
-  /@melt-ui/pp@0.1.0(@melt-ui/svelte@0.63.1)(svelte@4.0.0):
+  /@melt-ui/pp@0.1.0(@melt-ui/svelte@0.67.0)(svelte@4.0.0):
     resolution: {integrity: sha512-GbzqhLDjfxh3BwC0It7kVUHvx05f+IdBad+mXWMO76xgfdqBkAO/pS6Q+tAEntKglQvxXugV3gHAyEJgSI0AlA==}
     engines: {pnpm: '>=8.6.3'}
     peerDependencies:
       '@melt-ui/svelte': '>= 0.29.0'
       svelte: ^3.55.0 || ^4.0.0
     dependencies:
-      '@melt-ui/svelte': 0.63.1(svelte@4.0.0)
+      '@melt-ui/svelte': 0.67.0(svelte@4.0.0)
       svelte: 4.0.0
     dev: true
 
-  /@melt-ui/svelte@0.63.1(svelte@4.0.0):
-    resolution: {integrity: sha512-WzuI4uePPsHMlk1aq95UAgb32gXYRjBSi8DmXApWuM9CpJgcvUt2YcQ5+BPSOyrDOWVvPJd9gL7QA0NPDSCtNw==}
+  /@melt-ui/svelte@0.67.0(svelte@4.0.0):
+    resolution: {integrity: sha512-fd9PsDE6sKbeyExagqH0nOpZEnDqyr2efbkjfmCRRYXVW5vlDEOPaSB+mg4Tjch121102sFH1Od+MlXwmeHy3A==}
     peerDependencies:
       svelte: '>=3 <5'
     dependencies:
-      '@floating-ui/core': 1.5.0
+      '@floating-ui/core': 1.5.2
       '@floating-ui/dom': 1.5.3
-      '@internationalized/date': 3.5.0
+      '@internationalized/date': 3.5.1
       dequal: 2.0.3
       focus-trap: 7.5.4
       nanoid: 4.0.2
@@ -3322,14 +3184,6 @@ packages:
       '@babel/runtime': 7.21.0
     dev: true
 
-  /@rollup/pluginutils@4.2.1:
-    resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
-    engines: {node: '>= 8.0.0'}
-    dependencies:
-      estree-walker: 2.0.2
-      picomatch: 2.3.1
-    dev: true
-
   /@sinclair/typebox@0.27.8:
     resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
     dev: true
@@ -4028,32 +3882,6 @@ packages:
       type-fest: 2.19.0
     dev: true
 
-  /@storybook/design-system@7.15.15(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-jBCNQcUEk359Qvml/iDS1sAhUt6VKFN0sR6iWPAKBWgweAohtNzjZ1xPnEzu+mopMK3IdUj1hQHvNpsi3/pjMQ==}
-    engines: {node: '>=14', npm: '>=6'}
-    peerDependencies:
-      react: ^15.0.0 || ^16.0.0 || ^17.0.0
-      react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0
-    dependencies:
-      '@emotion/weak-memoize': 0.3.1
-      '@storybook/theming': 7.6.6(react-dom@18.2.0)(react@18.2.0)
-      '@types/pluralize': 0.0.29
-      '@types/prismjs': 1.26.3
-      '@types/react-modal': 3.16.3
-      '@types/uuid': 8.3.4
-      copy-to-clipboard: 3.3.3
-      human-format: 0.11.0
-      pluralize: 8.0.0
-      polished: 3.7.2
-      prismjs: 1.25.0
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-      react-github-button: 0.1.11
-      react-modal: 3.16.1(react-dom@18.2.0)(react@18.2.0)
-      react-popper-tooltip: 2.11.1(react-dom@18.2.0)(react@18.2.0)
-      uuid: 3.4.0
-    dev: true
-
   /@storybook/docs-mdx@0.1.0:
     resolution: {integrity: sha512-JDaBR9lwVY4eSH5W8EGHrhODjygPd6QImRbwjAuJNEnY0Vw4ie3bPkeGfnacB3OBW6u/agqPv2aRlR46JcAQLg==}
     dev: true
@@ -4455,26 +4283,6 @@ packages:
       file-system-cache: 2.3.0
     dev: true
 
-  /@sveltejs/adapter-auto@2.1.0(@sveltejs/kit@1.22.3):
-    resolution: {integrity: sha512-o2pZCfATFtA/Gw/BB0Xm7k4EYaekXxaPGER3xGSY3FvzFJGTlJlZjBseaXwYSM94lZ0HniOjTokN3cWaLX6fow==}
-    peerDependencies:
-      '@sveltejs/kit': ^1.0.0
-    dependencies:
-      '@sveltejs/kit': 1.22.3(svelte@4.0.0)(vite@4.4.7)
-      import-meta-resolve: 3.0.0
-    dev: true
-
-  /@sveltejs/adapter-cloudflare@2.3.4(@sveltejs/kit@1.22.3):
-    resolution: {integrity: sha512-+5Msbm7svrZjaBt0KapNh2zEaMRfrhbTRBWRXrFa+w+KFRQnHjFIPvWz/NQ11XKVu7dRe7mkzqvqieL2b0hpJw==}
-    peerDependencies:
-      '@sveltejs/kit': ^1.0.0
-    dependencies:
-      '@cloudflare/workers-types': 4.20231121.0
-      '@sveltejs/kit': 1.22.3(svelte@4.0.0)(vite@4.4.7)
-      esbuild: 0.18.17
-      worktop: 0.8.0-next.15
-    dev: true
-
   /@sveltejs/adapter-static@2.0.3(@sveltejs/kit@1.22.3):
     resolution: {integrity: sha512-VUqTfXsxYGugCpMqQv1U0LIdbR3S5nBkMMDmpjGVJyM6Q2jHVMFtdWJCkeHMySc6mZxJ+0eZK3T7IgmUCDrcUQ==}
     peerDependencies:
@@ -4483,19 +4291,6 @@ packages:
       '@sveltejs/kit': 1.22.3(svelte@4.0.0)(vite@4.4.7)
     dev: true
 
-  /@sveltejs/adapter-vercel@3.0.2(@sveltejs/kit@1.22.3):
-    resolution: {integrity: sha512-CQcLoKeVmF5Si+sJax0v8KMtXviJXfDXozJt3zfPHHYytCwMcGpJXsgqpqUbkdaF2xxgXuhTD7ClhQRFThbprw==}
-    peerDependencies:
-      '@sveltejs/kit': ^1.5.0
-    dependencies:
-      '@sveltejs/kit': 1.22.3(svelte@4.0.0)(vite@4.4.7)
-      '@vercel/nft': 0.22.6
-      esbuild: 0.18.17
-    transitivePeerDependencies:
-      - encoding
-      - supports-color
-    dev: true
-
   /@sveltejs/kit@1.22.3(svelte@4.0.0)(vite@4.4.7):
     resolution: {integrity: sha512-IpHD5wvuoOIHYaHQUBJ1zERD2Iz+fB/rBXhXjl8InKw6X4VKE9BSus+ttHhE7Ke+Ie9ecfilzX8BnWE3FeQyng==}
     engines: {node: ^16.14 || >=18}
@@ -4556,22 +4351,6 @@ packages:
       - supports-color
     dev: true
 
-  /@sveltejs/vite-plugin-svelte-inspector@2.0.0(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.0.0)(vite@4.4.7):
-    resolution: {integrity: sha512-gjr9ZFg1BSlIpfZ4PRewigrvYmHWbDrq2uvvPB1AmTWKuM+dI1JXQSUu2pIrYLb/QncyiIGkFDFKTwJ0XqQZZg==}
-    engines: {node: ^18.0.0 || >=20}
-    peerDependencies:
-      '@sveltejs/vite-plugin-svelte': ^3.0.0
-      svelte: ^4.0.0 || ^5.0.0-next.0
-      vite: ^5.0.0
-    dependencies:
-      '@sveltejs/vite-plugin-svelte': 3.0.1(svelte@4.0.0)(vite@4.4.7)
-      debug: 4.3.4
-      svelte: 4.0.0
-      vite: 4.4.7(@types/node@20.3.1)(less@4.2.0)
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
   /@sveltejs/vite-plugin-svelte@2.4.2(svelte@4.0.0)(vite@4.4.7):
     resolution: {integrity: sha512-ePfcC48ftMKhkT0OFGdOyycYKnnkT6i/buzey+vHRTR/JpQvuPzzhf1PtKqCDQfJRgoPSN2vscXs6gLigx/zGw==}
     engines: {node: ^14.18.0 || >= 16}
@@ -4592,26 +4371,6 @@ packages:
       - supports-color
     dev: true
 
-  /@sveltejs/vite-plugin-svelte@3.0.1(svelte@4.0.0)(vite@4.4.7):
-    resolution: {integrity: sha512-CGURX6Ps+TkOovK6xV+Y2rn8JKa8ZPUHPZ/NKgCxAmgBrXReavzFl8aOSCj3kQ1xqT7yGJj53hjcV/gqwDAaWA==}
-    engines: {node: ^18.0.0 || >=20}
-    peerDependencies:
-      svelte: ^4.0.0 || ^5.0.0-next.0
-      vite: ^5.0.0
-    dependencies:
-      '@sveltejs/vite-plugin-svelte-inspector': 2.0.0(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.0.0)(vite@4.4.7)
-      debug: 4.3.4
-      deepmerge: 4.3.1
-      kleur: 4.1.5
-      magic-string: 0.30.5
-      svelte: 4.0.0
-      svelte-hmr: 0.15.3(svelte@4.0.0)
-      vite: 4.4.7(@types/node@20.3.1)(less@4.2.0)
-      vitefu: 0.2.5(vite@4.4.7)
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
   /@svitejs/changesets-changelog-github-compact@1.1.0:
     resolution: {integrity: sha512-qhUGGDHcpbY2zpjW3SwqchuW8J/5EzlPFud7xNntHKA7f3a/mx5+g+ruJKFHSAiVZYo30PALt+AyhmPUNKH/Og==}
     engines: {node: ^14.13.1 || ^16.0.0 || >=18}
@@ -4783,12 +4542,12 @@ packages:
   /@types/eslint-scope@3.7.7:
     resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==}
     dependencies:
-      '@types/eslint': 8.44.8
+      '@types/eslint': 8.56.1
       '@types/estree': 1.0.5
     dev: true
 
-  /@types/eslint@8.44.8:
-    resolution: {integrity: sha512-4K8GavROwhrYl2QXDXm0Rv9epkA8GBFu0EI+XrrnnuCl7u8CWBRusX7fXJfanhZTDWSAL24gDI/UqXyUM0Injw==}
+  /@types/eslint@8.56.1:
+    resolution: {integrity: sha512-18PLWRzhy9glDQp3+wOgfLYRWlhgX0azxgJ63rdpoUHyrC9z0f5CkFburjQx4uD7ZCruw85ZtMt6K+L+R8fLJQ==}
     dependencies:
       '@types/estree': 1.0.5
       '@types/json-schema': 7.0.15
@@ -4869,7 +4628,7 @@ packages:
     resolution: {integrity: sha512-mSoZVJF5YzGVCk+FsDxzDuH7s+SCkzrgKZzf0Z0T2WudhBUPoF6ktoTPC4R0ZoCPCV5xUvuU6ias5NvxcBcMMg==}
     dependencies:
       expect: 29.5.0
-      pretty-format: 29.5.0
+      pretty-format: 29.6.2
     dev: true
 
   /@types/json-schema@7.0.12:
@@ -4937,6 +4696,12 @@ packages:
       undici-types: 5.26.5
     dev: true
 
+  /@types/node@20.10.6:
+    resolution: {integrity: sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==}
+    dependencies:
+      undici-types: 5.26.5
+    dev: true
+
   /@types/node@20.3.1:
     resolution: {integrity: sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==}
     dev: true
@@ -4949,22 +4714,10 @@ packages:
     resolution: {integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==}
     dev: true
 
-  /@types/pluralize@0.0.29:
-    resolution: {integrity: sha512-BYOID+l2Aco2nBik+iYS4SZX0Lf20KPILP5RGmM1IgzdwNdTs0eebiFriOPcej1sX9mLnSoiNte5zcFxssgpGA==}
-    dev: true
-
-  /@types/prettier@2.7.3:
-    resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==}
-    dev: true
-
   /@types/pretty-hrtime@1.0.1:
     resolution: {integrity: sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ==}
     dev: true
 
-  /@types/prismjs@1.26.3:
-    resolution: {integrity: sha512-A0D0aTXvjlqJ5ZILMz3rNfDBOx9hHxLZYv2by47Sm/pqW35zzjusrZTryatjN/Rf8Us2gZrJD+KeHbUSTux1Cw==}
-    dev: true
-
   /@types/prop-types@15.7.5:
     resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
     dev: true
@@ -4981,12 +4734,6 @@ packages:
     resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==}
     dev: true
 
-  /@types/react-modal@3.16.3:
-    resolution: {integrity: sha512-xXuGavyEGaFQDgBv4UVm8/ZsG+qxeQ7f77yNrW3n+1J6XAstUy5rYHeIHPh1KzsGc6IkCIdu6lQ2xWzu1jBTLg==}
-    dependencies:
-      '@types/react': 18.2.7
-    dev: true
-
   /@types/react@18.2.7:
     resolution: {integrity: sha512-ojrXpSH2XFCmHm7Jy3q44nXDyN54+EYKP2lBhJ2bqfyPj6cIUW/FZW/Csdia34NQgq7KYcAlHi5184m4X88+yw==}
     dependencies:
@@ -5031,10 +4778,6 @@ packages:
     resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==}
     dev: true
 
-  /@types/uuid@8.3.4:
-    resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==}
-    dev: true
-
   /@types/uuid@9.0.7:
     resolution: {integrity: sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==}
     dev: true
@@ -5185,45 +4928,6 @@ packages:
       eslint-visitor-keys: 3.4.1
     dev: true
 
-  /@urql/core@4.2.2:
-    resolution: {integrity: sha512-TP1kheq9bnrEdnVbJqh0g0ZY/wfdpPeAzjiiDK+Tm+Pbi0O1Xdu6+fUJ/wJo5QpHZzkIyya4/AecG63e6scFqQ==}
-    dependencies:
-      '@0no-co/graphql.web': 1.0.4
-      wonka: 6.3.4
-    transitivePeerDependencies:
-      - graphql
-    dev: true
-
-  /@urql/exchange-auth@2.1.6:
-    resolution: {integrity: sha512-snOlt7p5kYq0KnPDuXkKe2qW3/BucQZOElvTeo3svLQuk9JiNJVnm6ffQ6QGiGO+G3AtMrctnno1+X44fLtDuQ==}
-    dependencies:
-      '@urql/core': 4.2.2
-      wonka: 6.3.4
-    transitivePeerDependencies:
-      - graphql
-    dev: true
-
-  /@vercel/nft@0.22.6:
-    resolution: {integrity: sha512-gTsFnnT4mGxodr4AUlW3/urY+8JKKB452LwF3m477RFUJTAaDmcz2JqFuInzvdybYIeyIv1sSONEJxsxnbQ5JQ==}
-    engines: {node: '>=14'}
-    hasBin: true
-    dependencies:
-      '@mapbox/node-pre-gyp': 1.0.10
-      '@rollup/pluginutils': 4.2.1
-      acorn: 8.9.0
-      async-sema: 3.1.1
-      bindings: 1.5.0
-      estree-walker: 2.0.2
-      glob: 7.1.6
-      graceful-fs: 4.2.11
-      micromatch: 4.0.5
-      node-gyp-build: 4.6.0
-      resolve-from: 5.0.0
-    transitivePeerDependencies:
-      - encoding
-      - supports-color
-    dev: true
-
   /@vitest/coverage-v8@0.33.0(vitest@0.33.0):
     resolution: {integrity: sha512-Rj5IzoLF7FLj6yR7TmqsfRDSeaFki6NAJ/cQexqhbWkHEV2htlVGrmuOde3xzvFsCbLCagf4omhcIaVmfU8Okg==}
     peerDependencies:
@@ -5450,10 +5154,6 @@ packages:
     resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
     dev: true
 
-  /abbrev@1.1.1:
-    resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
-    dev: true
-
   /accepts@1.3.8:
     resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
     engines: {node: '>= 0.6'}
@@ -5462,12 +5162,12 @@ packages:
       negotiator: 0.6.3
     dev: true
 
-  /acorn-import-assertions@1.9.0(acorn@8.11.2):
+  /acorn-import-assertions@1.9.0(acorn@8.11.3):
     resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==}
     peerDependencies:
       acorn: ^8
     dependencies:
-      acorn: 8.11.2
+      acorn: 8.11.3
     dev: true
 
   /acorn-jsx@5.3.2(acorn@8.11.2):
@@ -5497,6 +5197,12 @@ packages:
     hasBin: true
     dev: true
 
+  /acorn@8.11.3:
+    resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+    dev: true
+
   /acorn@8.9.0:
     resolution: {integrity: sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==}
     engines: {node: '>=0.4.0'}
@@ -5614,18 +5320,6 @@ packages:
     resolution: {integrity: sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==}
     dev: true
 
-  /aproba@2.0.0:
-    resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
-    dev: true
-
-  /are-we-there-yet@2.0.0:
-    resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
-    engines: {node: '>=10'}
-    dependencies:
-      delegates: 1.0.0
-      readable-stream: 3.6.2
-    dev: true
-
   /arg@5.0.2:
     resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
     dev: true
@@ -5724,10 +5418,6 @@ packages:
     resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==}
     dev: true
 
-  /async-sema@3.1.1:
-    resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==}
-    dev: true
-
   /async@3.2.4:
     resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
     dev: true
@@ -5874,12 +5564,6 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
-  /bindings@1.5.0:
-    resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
-    dependencies:
-      file-uri-to-path: 1.0.0
-    dev: true
-
   /bl@4.1.0:
     resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
     dependencies:
@@ -6156,11 +5840,6 @@ packages:
     engines: {node: '>=10'}
     dev: true
 
-  /chromatic@9.1.0:
-    resolution: {integrity: sha512-9ib8k4LIfg/88kKufxz1N8vgCB7nlLhJqmx+Vx55cM/6DCB/M6oqroirVRXfdeC7qaZuhyUemPF2QHxBh7GXtQ==}
-    hasBin: true
-    dev: true
-
   /chrome-trace-event@1.0.3:
     resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==}
     engines: {node: '>=6.0'}
@@ -6228,8 +5907,8 @@ packages:
     engines: {node: '>=0.8'}
     dev: true
 
-  /clsx@1.2.1:
-    resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
+  /clsx@2.1.0:
+    resolution: {integrity: sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==}
     engines: {node: '>=6'}
     dev: true
 
@@ -6264,11 +5943,6 @@ packages:
     resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
     dev: true
 
-  /color-support@1.1.3:
-    resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
-    hasBin: true
-    dev: true
-
   /colorette@2.0.20:
     resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
     dev: true
@@ -6338,10 +6012,6 @@ packages:
       typedarray: 0.0.6
     dev: true
 
-  /console-control-strings@1.1.0:
-    resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
-    dev: true
-
   /content-disposition@0.5.4:
     resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
     engines: {node: '>= 0.6'}
@@ -6377,12 +6047,6 @@ packages:
       is-what: 3.14.1
     dev: true
 
-  /copy-to-clipboard@3.3.3:
-    resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==}
-    dependencies:
-      toggle-selection: 1.0.6
-    dev: true
-
   /core-js-compat@3.31.1:
     resolution: {integrity: sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA==}
     dependencies:
@@ -6395,16 +6059,6 @@ packages:
       browserslist: 4.22.2
     dev: true
 
-  /core-js-pure@3.30.0:
-    resolution: {integrity: sha512-+2KbMFGeBU0ln/csoPqTe0i/yfHbrd2EUhNMObsGtXMKS/RTtlkYyi+/3twLcevbgNR0yM/r0Psa3TEoQRpFMQ==}
-    requiresBuild: true
-    dev: true
-
-  /core-js@3.30.0:
-    resolution: {integrity: sha512-hQotSSARoNh1mYPi9O2YaWeiq/cEB95kOrFb4NCrO4RIFt1qqNpKsaE+vy/L3oiqvND5cThqXzUU3r9F7Efztg==}
-    requiresBuild: true
-    dev: true
-
   /core-util-is@1.0.3:
     resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
     dev: true
@@ -6526,13 +6180,6 @@ packages:
     resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==}
     dev: true
 
-  /date-fns@2.30.0:
-    resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
-    engines: {node: '>=0.11'}
-    dependencies:
-      '@babel/runtime': 7.21.0
-    dev: true
-
   /debug@2.6.9:
     resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
     peerDependencies:
@@ -6544,19 +6191,6 @@ packages:
       ms: 2.0.0
     dev: true
 
-  /debug@3.2.7:
-    resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
-    requiresBuild: true
-    peerDependencies:
-      supports-color: '*'
-    peerDependenciesMeta:
-      supports-color:
-        optional: true
-    dependencies:
-      ms: 2.1.3
-    dev: true
-    optional: true
-
   /debug@4.3.4:
     resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
     engines: {node: '>=6.0'}
@@ -6603,18 +6237,6 @@ packages:
       type-detect: 4.0.8
     dev: true
 
-  /deep-equal@1.1.2:
-    resolution: {integrity: sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      is-arguments: 1.1.1
-      is-date-object: 1.0.5
-      is-regex: 1.1.4
-      object-is: 1.1.5
-      object-keys: 1.1.1
-      regexp.prototype.flags: 1.5.1
-    dev: true
-
   /deep-equal@2.2.2:
     resolution: {integrity: sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==}
     dependencies:
@@ -6661,15 +6283,6 @@ packages:
       clone: 1.0.4
     dev: true
 
-  /define-data-property@1.1.1:
-    resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      get-intrinsic: 1.2.1
-      gopd: 1.0.1
-      has-property-descriptors: 1.0.0
-    dev: true
-
   /define-lazy-prop@2.0.0:
     resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
     engines: {node: '>=8'}
@@ -6706,10 +6319,6 @@ packages:
     engines: {node: '>=0.4.0'}
     dev: true
 
-  /delegates@1.0.0:
-    resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
-    dev: true
-
   /depd@2.0.0:
     resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
     engines: {node: '>= 0.8'}
@@ -6729,11 +6338,6 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
-  /detect-libc@2.0.1:
-    resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==}
-    engines: {node: '>=8'}
-    dev: true
-
   /detect-node-es@1.1.0:
     resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
     dev: true
@@ -7359,10 +6963,6 @@ packages:
     engines: {node: '>=4.0'}
     dev: true
 
-  /estree-walker@2.0.2:
-    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
-    dev: true
-
   /estree-walker@3.0.3:
     resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
     dependencies:
@@ -7399,10 +6999,6 @@ packages:
       strip-final-newline: 2.0.0
     dev: true
 
-  /exenv@1.2.2:
-    resolution: {integrity: sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==}
-    dev: true
-
   /expect@29.5.0:
     resolution: {integrity: sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -7522,10 +7118,6 @@ packages:
       reusify: 1.0.4
     dev: true
 
-  /fathom-client@3.5.0:
-    resolution: {integrity: sha512-BiRDS9Q9a8Zma0H717FWC5cvf545K/CsxBpxKT22TcSl1EbRhhlHWIJgrdeiQUfdorBK2ppy09TwMOhRsbos/A==}
-    dev: true
-
   /fb-watchman@2.0.2:
     resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
     dependencies:
@@ -7560,21 +7152,12 @@ packages:
       ramda: 0.29.0
     dev: true
 
-  /file-uri-to-path@1.0.0:
-    resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
-    dev: true
-
   /filelist@1.0.4:
     resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
     dependencies:
       minimatch: 5.1.6
     dev: true
 
-  /filesize@10.1.0:
-    resolution: {integrity: sha512-GTLKYyBSDz3nPhlLVPjPWZCnhkd9TrrRArNcy8Z+J2cqScB7h2McAzR6NBX6nYOoWafql0roY8hrocxnZBv9CQ==}
-    engines: {node: '>= 10.4.0'}
-    dev: true
-
   /fill-range@7.0.1:
     resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
     engines: {node: '>=8'}
@@ -7800,21 +7383,6 @@ packages:
     resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
     dev: true
 
-  /gauge@3.0.2:
-    resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
-    engines: {node: '>=10'}
-    dependencies:
-      aproba: 2.0.0
-      color-support: 1.1.3
-      console-control-strings: 1.1.0
-      has-unicode: 2.0.1
-      object-assign: 4.1.1
-      signal-exit: 3.0.7
-      string-width: 4.2.3
-      strip-ansi: 6.0.1
-      wide-align: 1.1.5
-    dev: true
-
   /gensync@1.0.0-beta.2:
     resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
     engines: {node: '>=6.9.0'}
@@ -7950,16 +7518,6 @@ packages:
       once: 1.4.0
     dev: true
 
-  /glob@9.3.4:
-    resolution: {integrity: sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA==}
-    engines: {node: '>=16 || 14 >=14.17'}
-    dependencies:
-      fs.realpath: 1.0.0
-      minimatch: 8.0.3
-      minipass: 4.2.5
-      path-scurry: 1.6.3
-    dev: true
-
   /globals@11.12.0:
     resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
     engines: {node: '>=4'}
@@ -8009,10 +7567,6 @@ packages:
     resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
     dev: true
 
-  /gud@1.0.0:
-    resolution: {integrity: sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==}
-    dev: true
-
   /gunzip-maybe@1.4.2:
     resolution: {integrity: sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw==}
     hasBin: true
@@ -8091,10 +7645,6 @@ packages:
       has-symbols: 1.0.3
     dev: true
 
-  /has-unicode@2.0.1:
-    resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
-    dev: true
-
   /has@1.0.3:
     resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
     engines: {node: '>= 0.4.0'}
@@ -8216,19 +7766,10 @@ packages:
       space-separated-tokens: 2.0.2
     dev: true
 
-  /highlight.js@11.8.0:
-    resolution: {integrity: sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==}
-    engines: {node: '>=12.0.0'}
-    dev: true
-
   /hosted-git-info@2.8.9:
     resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
     dev: true
 
-  /htm@3.1.1:
-    resolution: {integrity: sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ==}
-    dev: true
-
   /html-encoding-sniffer@3.0.0:
     resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==}
     engines: {node: '>=12'}
@@ -8295,11 +7836,6 @@ packages:
       - supports-color
     dev: true
 
-  /human-format@0.11.0:
-    resolution: {integrity: sha512-g4UtoBnhfitCjkGjjiOlY8tkmArIcrstBa5adihxSJwSde1A7iQzvrNLB5ceX89FHXzYi9yTi04t3m82J/XIag==}
-    engines: {node: '>=4'}
-    dev: true
-
   /human-id@1.0.2:
     resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==}
     dev: true
@@ -8369,10 +7905,6 @@ packages:
       resolve-from: 4.0.0
     dev: true
 
-  /import-meta-resolve@3.0.0:
-    resolution: {integrity: sha512-4IwhLhNNA8yy445rPjD/lWh++7hMDOml2eHtd58eG7h+qK3EryMuuRbsHGPikCoAgIkkDnckKfWSk2iDla/ejg==}
-    dev: true
-
   /imurmurhash@0.1.4:
     resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
     engines: {node: '>=0.8.19'}
@@ -8853,7 +8385,7 @@ packages:
     resolution: {integrity: sha512-vnIGYEjoPSuRqV8W9t+Wow95SDp6KPX2Uf7EoeG9G99J2OVh7OSwpS4B6J0NfpEIpfkBNHlBZpA2rblEuEFhZQ==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
-      '@babel/code-frame': 7.22.5
+      '@babel/code-frame': 7.23.5
       '@jest/types': 29.6.1
       '@types/stack-utils': 2.0.1
       chalk: 4.1.2
@@ -8893,7 +8425,7 @@ packages:
     resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
     engines: {node: '>= 10.13.0'}
     dependencies:
-      '@types/node': 20.10.1
+      '@types/node': 20.10.6
       merge-stream: 2.0.0
       supports-color: 8.1.1
     dev: true
@@ -9110,10 +8642,8 @@ packages:
       image-size: 0.5.5
       make-dir: 2.1.0
       mime: 1.6.0
-      needle: 3.2.0
+      needle: 3.3.1
       source-map: 0.6.1
-    transitivePeerDependencies:
-      - supports-color
     dev: true
 
   /leven@3.1.0:
@@ -9273,11 +8803,6 @@ packages:
       yallist: 4.0.0
     dev: true
 
-  /lru-cache@7.18.3:
-    resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
-    engines: {node: '>=12'}
-    dev: true
-
   /lucide-svelte@0.263.0(svelte@4.0.0):
     resolution: {integrity: sha512-1cB+DPsX7PbHJ+aDbc2nKKRdv3DYHK81mRVUw7kNb7bVw2Dh9QsNVON918WjnSlmXEt9lUnJ7TkNymreZwnLgg==}
     peerDependencies:
@@ -9845,13 +9370,6 @@ packages:
       brace-expansion: 2.0.1
     dev: true
 
-  /minimatch@8.0.3:
-    resolution: {integrity: sha512-tEEvU9TkZgnFDCtpnrEYnPsjT7iUx42aXfs4bzmQ5sMA09/6hZY0jeZcGkXyDagiBOvkUjNo8Viom+Me6+2x7g==}
-    engines: {node: '>=16 || 14 >=14.17'}
-    dependencies:
-      brace-expansion: 2.0.1
-    dev: true
-
   /minimatch@9.0.3:
     resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
     engines: {node: '>=16 || 14 >=14.17'}
@@ -9879,11 +9397,6 @@ packages:
       yallist: 4.0.0
     dev: true
 
-  /minipass@4.2.5:
-    resolution: {integrity: sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==}
-    engines: {node: '>=8'}
-    dev: true
-
   /minipass@5.0.0:
     resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
     engines: {node: '>=8'}
@@ -9966,8 +9479,8 @@ packages:
       thenify-all: 1.6.0
     dev: true
 
-  /nanoid@3.3.6:
-    resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
+  /nanoid@3.3.7:
+    resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
     engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
     hasBin: true
     dev: true
@@ -9976,6 +9489,13 @@ packages:
     resolution: {integrity: sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==}
     engines: {node: ^14 || ^16 || >=18}
     hasBin: true
+    dev: true
+
+  /nanoid@5.0.4:
+    resolution: {integrity: sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==}
+    engines: {node: ^18 || >=20}
+    hasBin: true
+    dev: false
 
   /natural-compare-lite@1.4.0:
     resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
@@ -9985,17 +9505,14 @@ packages:
     resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
     dev: true
 
-  /needle@3.2.0:
-    resolution: {integrity: sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==}
+  /needle@3.3.1:
+    resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==}
     engines: {node: '>= 4.4.x'}
     hasBin: true
     requiresBuild: true
     dependencies:
-      debug: 3.2.7
       iconv-lite: 0.6.3
       sax: 1.3.0
-    transitivePeerDependencies:
-      - supports-color
     dev: true
     optional: true
 
@@ -10038,11 +9555,6 @@ packages:
       whatwg-url: 5.0.0
     dev: true
 
-  /node-gyp-build@4.6.0:
-    resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==}
-    hasBin: true
-    dev: true
-
   /node-int64@0.4.0:
     resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
     dev: true
@@ -10059,14 +9571,6 @@ packages:
     resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
     dev: true
 
-  /nopt@5.0.0:
-    resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==}
-    engines: {node: '>=6'}
-    hasBin: true
-    dependencies:
-      abbrev: 1.1.1
-    dev: true
-
   /normalize-package-data@2.5.0:
     resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
     dependencies:
@@ -10120,15 +9624,6 @@ packages:
       path-key: 3.1.1
     dev: true
 
-  /npmlog@5.0.1:
-    resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
-    dependencies:
-      are-we-there-yet: 2.0.0
-      console-control-strings: 1.1.0
-      gauge: 3.0.2
-      set-blocking: 2.0.0
-    dev: true
-
   /nth-check@2.1.1:
     resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
     dependencies:
@@ -10407,14 +9902,6 @@ packages:
       minipass: 5.0.0
     dev: true
 
-  /path-scurry@1.6.3:
-    resolution: {integrity: sha512-RAmB+n30SlN+HnNx6EbcpoDy9nwdpcGPnEKrJnu6GZoDWBdIjo1UQMVtW2ybtC7LC2oKLcMq8y5g8WnKLiod9g==}
-    engines: {node: '>=16 || 14 >=14.17'}
-    dependencies:
-      lru-cache: 7.18.3
-      minipass: 4.2.5
-    dev: true
-
   /path-to-regexp@0.1.7:
     resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==}
     dev: true
@@ -10521,18 +10008,6 @@ packages:
       fsevents: 2.3.2
     dev: true
 
-  /pluralize@8.0.0:
-    resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /polished@3.7.2:
-    resolution: {integrity: sha512-pQKtpZGmsZrW8UUpQMAnR7s3ppHeMQVNyMDKtUyKwuvDmklzcEyM5Kllb3JyE/sE/x7arDmyd35i+4vp99H6sQ==}
-    engines: {node: '>=10'}
-    dependencies:
-      '@babel/runtime': 7.21.0
-    dev: true
-
   /polished@4.2.2:
     resolution: {integrity: sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==}
     engines: {node: '>=10'}
@@ -10540,11 +10015,6 @@ packages:
       '@babel/runtime': 7.21.0
     dev: true
 
-  /popper.js@1.16.1:
-    resolution: {integrity: sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==}
-    deprecated: You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1
-    dev: true
-
   /postcss-import@15.1.0(postcss@8.4.27):
     resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
     engines: {node: '>=14.0.0'}
@@ -10709,7 +10179,7 @@ packages:
     resolution: {integrity: sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==}
     engines: {node: ^10 || ^12 || >=14}
     dependencies:
-      nanoid: 3.3.6
+      nanoid: 3.3.7
       picocolors: 1.0.0
       source-map-js: 1.0.2
     dev: true
@@ -10718,7 +10188,7 @@ packages:
     resolution: {integrity: sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw==}
     engines: {node: ^10 || ^12 || >=14}
     dependencies:
-      nanoid: 3.3.6
+      nanoid: 3.3.7
       picocolors: 1.0.0
       source-map-js: 1.0.2
     dev: true
@@ -10846,10 +10316,6 @@ packages:
     resolution: {integrity: sha512-yABh19CYbM24V7aS7TuPYRNMqthxwbvx6FF/Rw920YbyBWO3tnyPIqRMgHuSVsLmuHkkBS1Akyof463FVdkeDQ==}
     dev: true
 
-  /prismjs@1.25.0:
-    resolution: {integrity: sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==}
-    dev: true
-
   /prismjs@1.29.0:
     resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==}
     engines: {node: '>=6'}
@@ -10877,14 +10343,6 @@ packages:
       sisteransi: 1.0.5
     dev: true
 
-  /prop-types@15.8.1:
-    resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
-    dependencies:
-      loose-envify: 1.4.0
-      object-assign: 4.1.1
-      react-is: 16.13.1
-    dev: true
-
   /property-information@6.2.0:
     resolution: {integrity: sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg==}
     dev: true
@@ -11044,16 +10502,6 @@ packages:
       scheduler: 0.23.0
     dev: true
 
-  /react-github-button@0.1.11:
-    resolution: {integrity: sha512-KL/kieQiR5DXd1RxWMegr4Igyz9+Lm6ZVwjpN5rQyttz/sdEq8DF1R/vzLl2f58nChJe0sKE3U3A7QRK+Zb01w==}
-    dependencies:
-      prop-types: 15.8.1
-    dev: true
-
-  /react-is@16.13.1:
-    resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
-    dev: true
-
   /react-is@17.0.2:
     resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
     dev: true
@@ -11062,52 +10510,6 @@ packages:
     resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
     dev: true
 
-  /react-lifecycles-compat@3.0.4:
-    resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==}
-    dev: true
-
-  /react-modal@3.16.1(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==}
-    engines: {node: '>=8'}
-    peerDependencies:
-      react: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18
-      react-dom: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18
-    dependencies:
-      exenv: 1.2.2
-      prop-types: 15.8.1
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-      react-lifecycles-compat: 3.0.4
-      warning: 4.0.3
-    dev: true
-
-  /react-popper-tooltip@2.11.1(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-04A2f24GhyyMicKvg/koIOQ5BzlrRbKiAgP6L+Pdj1MVX3yJ1NeZ8+EidndQsbejFT55oW1b++wg2Z8KlAyhfQ==}
-    peerDependencies:
-      react: ^16.6.0
-      react-dom: ^16.6.0
-    dependencies:
-      '@babel/runtime': 7.21.0
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-      react-popper: 1.3.11(react@18.2.0)
-    dev: true
-
-  /react-popper@1.3.11(react@18.2.0):
-    resolution: {integrity: sha512-VSA/bS+pSndSF2fiasHK/PTEEAyOpX60+H5EPAjoArr8JGm+oihu4UbrqcEBpQibJxBVCpYyjAX7abJ+7DoYVg==}
-    peerDependencies:
-      react: 0.14.x || ^15.0.0 || ^16.0.0 || ^17.0.0
-    dependencies:
-      '@babel/runtime': 7.21.0
-      '@hypnosphi/create-react-context': 0.3.1(prop-types@15.8.1)(react@18.2.0)
-      deep-equal: 1.1.2
-      popper.js: 1.16.1
-      prop-types: 15.8.1
-      react: 18.2.0
-      typed-styles: 0.0.7
-      warning: 4.0.3
-    dev: true
-
   /react-remove-scroll-bar@2.3.4(react@18.2.0):
     resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==}
     engines: {node: '>=10'}
@@ -11272,6 +10674,10 @@ packages:
     resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
     dev: true
 
+  /regenerator-runtime@0.14.1:
+    resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
+    dev: true
+
   /regenerator-transform@0.15.2:
     resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==}
     dependencies:
@@ -11300,20 +10706,6 @@ packages:
       functions-have-names: 1.2.3
     dev: true
 
-  /regexp.prototype.flags@1.5.1:
-    resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      call-bind: 1.0.2
-      define-properties: 1.2.0
-      set-function-name: 2.0.1
-    dev: true
-
-  /regexparam@2.0.2:
-    resolution: {integrity: sha512-A1PeDEYMrkLrfyOwv2jwihXbo9qxdGD3atBYQA9JJgreAx8/7rC6IUkWOw2NQlOxLp2wL0ifQbh1HuidDfYA6w==}
-    engines: {node: '>=8'}
-    dev: true
-
   /regexpp@3.2.0:
     resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==}
     engines: {node: '>=8'}
@@ -11654,15 +11046,6 @@ packages:
     resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==}
     dev: true
 
-  /set-function-name@2.0.1:
-    resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==}
-    engines: {node: '>= 0.4'}
-    dependencies:
-      define-data-property: 1.1.1
-      functions-have-names: 1.2.3
-      has-property-descriptors: 1.0.0
-    dev: true
-
   /setprototypeof@1.2.0:
     resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
     dev: true
@@ -12120,21 +11503,6 @@ packages:
       svelte: 4.0.0
     dev: true
 
-  /svelte-fragment-component@1.2.0(svelte@4.0.0):
-    resolution: {integrity: sha512-rRstmz2oAy2Y/7X57tRaIAJdMYsa2K/MOx/YJN/ETb7Bj9U3vjgioz27dMG1hl2vAKFTtQpxDhC31ur7ECwpog==}
-    engines: {node: '>=10'}
-    peerDependencies:
-      svelte: 3.x
-    dependencies:
-      svelte: 4.0.0
-    dev: true
-
-  /svelte-highlight@7.3.0:
-    resolution: {integrity: sha512-59oE9/xOFXAdT97qXIt6HMlzL2f+0YNQ+BArzRONwCW96ElxX7TGme1kU5s3tsk1D88G5dhBixcP1chOGOkVsg==}
-    dependencies:
-      highlight.js: 11.8.0
-    dev: true
-
   /svelte-hmr@0.15.2(svelte@4.0.0):
     resolution: {integrity: sha512-q/bAruCvFLwvNbeE1x3n37TYFb3mTBJ6TrCq6p2CoFbSTNhDE9oAtEfpy+wmc9So8AG0Tja+X0/mJzX9tSfvIg==}
     engines: {node: ^12.20 || ^14.13.1 || >= 16}
@@ -12144,40 +11512,6 @@ packages:
       svelte: 4.0.0
     dev: true
 
-  /svelte-hmr@0.15.3(svelte@4.0.0):
-    resolution: {integrity: sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==}
-    engines: {node: ^12.20 || ^14.13.1 || >= 16}
-    peerDependencies:
-      svelte: ^3.19.0 || ^4.0.0
-    dependencies:
-      svelte: 4.0.0
-    dev: true
-
-  /svelte-htm@1.2.0(svelte@4.0.0):
-    resolution: {integrity: sha512-6YFNncbyXbCa3PSoMQc/JR6O/Yg1OgNioH5rMgE88RVPzU8714y2Urfenlqs+ryRS+Inv+m6TJ9jH3W7wDCS1A==}
-    engines: {node: '>=10'}
-    peerDependencies:
-      svelte: 3.x
-    dependencies:
-      '@babel/runtime-corejs3': 7.21.0
-      core-js: 3.30.0
-      htm: 3.1.1
-      svelte: 4.0.0
-      svelte-fragment-component: 1.2.0(svelte@4.0.0)
-      svelte-hyperscript: 1.2.1(svelte@4.0.0)
-    dev: true
-
-  /svelte-hyperscript@1.2.1(svelte@4.0.0):
-    resolution: {integrity: sha512-IVk91VDSOrhOUe0KI+Jfu7yCruUTOXgr4WysrK7hBNbFMDjeBOcPhk+LMYjUjdWxJx3+QSt3bUXj09YrUfRkjg==}
-    engines: {node: '>=10'}
-    peerDependencies:
-      svelte: 3.x
-    dependencies:
-      '@babel/runtime-corejs3': 7.21.0
-      core-js: 3.30.0
-      svelte: 4.0.0
-    dev: true
-
   /svelte-preprocess@5.0.4(@babel/core@7.23.6)(less@4.2.0)(postcss-load-config@4.0.1)(postcss@8.4.27)(svelte@4.0.0)(typescript@5.1.6):
     resolution: {integrity: sha512-ABia2QegosxOGsVlsSBJvoWeXy1wUKSfF7SWJdTjLAbx/Y3SrVevvvbFNQqrSJw89+lNSsM58SipmZJ5SRi5iw==}
     engines: {node: '>= 14.10.0'}
@@ -12313,6 +11647,12 @@ packages:
     resolution: {integrity: sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==}
     dev: true
 
+  /tailwind-merge@2.2.0:
+    resolution: {integrity: sha512-SqqhhaL0T06SW59+JVNfAqKdqLs0497esifRrZ7jOaefP3o64fdFNDMrAQWZFMxTLJPiHVjRLUywT8uFz1xNWQ==}
+    dependencies:
+      '@babel/runtime': 7.23.7
+    dev: true
+
   /tailwind-variants@0.1.8(tailwindcss@3.3.3):
     resolution: {integrity: sha512-U4wzqr3GN1lvHU4XKJ0Th7Rn007SKB4cLqU0eed4bUEiydw/83G48EQ8MPo2VwEiuFOGcL1IAlAHU81wxDB9fQ==}
     engines: {node: '>=16.x', pnpm: '>=7.x'}
@@ -12425,8 +11765,8 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
-  /terser-webpack-plugin@5.3.9(esbuild@0.18.17)(webpack@5.89.0):
-    resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==}
+  /terser-webpack-plugin@5.3.10(esbuild@0.18.17)(webpack@5.89.0):
+    resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==}
     engines: {node: '>= 10.13.0'}
     peerDependencies:
       '@swc/core': '*'
@@ -12446,17 +11786,17 @@ packages:
       jest-worker: 27.5.1
       schema-utils: 3.3.0
       serialize-javascript: 6.0.1
-      terser: 5.24.0
+      terser: 5.26.0
       webpack: 5.89.0(esbuild@0.18.17)
     dev: true
 
-  /terser@5.24.0:
-    resolution: {integrity: sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==}
+  /terser@5.26.0:
+    resolution: {integrity: sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==}
     engines: {node: '>=10'}
     hasBin: true
     dependencies:
       '@jridgewell/source-map': 0.3.5
-      acorn: 8.11.2
+      acorn: 8.11.3
       commander: 2.20.3
       source-map-support: 0.5.21
     dev: true
@@ -12552,10 +11892,6 @@ packages:
     resolution: {integrity: sha512-IfajhBTeg0HlMXu1f+VMbPef05QpDTsZ9X2Yn1+8npdaXsXg/+wrm9Ze1WG5OS1UDC3qJ5EQN/XOZ3gfXjPFCw==}
     dev: true
 
-  /toggle-selection@1.0.6:
-    resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==}
-    dev: true
-
   /toidentifier@1.0.1:
     resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
     engines: {node: '>=0.6'}
@@ -12703,10 +12039,6 @@ packages:
       is-typed-array: 1.1.10
     dev: true
 
-  /typed-styles@0.0.7:
-    resolution: {integrity: sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q==}
-    dev: true
-
   /typedarray@0.0.6:
     resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
     dev: true
@@ -12929,18 +12261,6 @@ packages:
       requires-port: 1.0.0
     dev: true
 
-  /urql@4.0.6(react@18.2.0):
-    resolution: {integrity: sha512-meXJ2puOd64uCGKh7Fse2R7gPa8+ZpBOoA62jN7CPXXUt7SVZSdeXWSpB3HvlfzLUkEqsWbvshwrgeWRYNNGaQ==}
-    peerDependencies:
-      react: '>= 16.8.0'
-    dependencies:
-      '@urql/core': 4.2.2
-      react: 18.2.0
-      wonka: 6.3.4
-    transitivePeerDependencies:
-      - graphql
-    dev: true
-
   /use-callback-ref@1.3.0(react@18.2.0):
     resolution: {integrity: sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==}
     engines: {node: '>=10'}
@@ -13000,12 +12320,6 @@ packages:
     engines: {node: '>= 0.4.0'}
     dev: true
 
-  /uuid@3.4.0:
-    resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==}
-    deprecated: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
-    hasBin: true
-    dev: true
-
   /uuid@9.0.0:
     resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==}
     hasBin: true
@@ -13156,17 +12470,6 @@ packages:
       vite: 4.4.7(@types/node@20.3.1)(less@4.2.0)
     dev: true
 
-  /vitefu@0.2.5(vite@4.4.7):
-    resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==}
-    peerDependencies:
-      vite: ^3.0.0 || ^4.0.0 || ^5.0.0
-    peerDependenciesMeta:
-      vite:
-        optional: true
-    dependencies:
-      vite: 4.4.7(@types/node@20.3.1)(less@4.2.0)
-    dev: true
-
   /vitest@0.33.0(@vitest/ui@0.34.6)(happy-dom@10.5.2)(jsdom@22.1.0)(less@4.2.0):
     resolution: {integrity: sha512-1CxaugJ50xskkQ0e969R/hW47za4YXDUfWJDxip1hwbnhUjYolpfUn2AMOulqG/Dtd9WYAtkHmM/m3yKVrEejQ==}
     engines: {node: '>=v14.18.0'}
@@ -13256,12 +12559,6 @@ packages:
       makeerror: 1.0.12
     dev: true
 
-  /warning@4.0.3:
-    resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==}
-    dependencies:
-      loose-envify: 1.4.0
-    dev: true
-
   /watchpack@2.4.0:
     resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==}
     engines: {node: '>=10.13.0'}
@@ -13313,8 +12610,8 @@ packages:
       '@webassemblyjs/ast': 1.11.6
       '@webassemblyjs/wasm-edit': 1.11.6
       '@webassemblyjs/wasm-parser': 1.11.6
-      acorn: 8.11.2
-      acorn-import-assertions: 1.9.0(acorn@8.11.2)
+      acorn: 8.11.3
+      acorn-import-assertions: 1.9.0(acorn@8.11.3)
       browserslist: 4.22.2
       chrome-trace-event: 1.0.3
       enhanced-resolve: 5.15.0
@@ -13329,7 +12626,7 @@ packages:
       neo-async: 2.6.2
       schema-utils: 3.3.0
       tapable: 2.2.1
-      terser-webpack-plugin: 5.3.9(esbuild@0.18.17)(webpack@5.89.0)
+      terser-webpack-plugin: 5.3.10(esbuild@0.18.17)(webpack@5.89.0)
       watchpack: 2.4.0
       webpack-sources: 3.2.3
     transitivePeerDependencies:
@@ -13432,28 +12729,10 @@ packages:
       stackback: 0.0.2
     dev: true
 
-  /wide-align@1.1.5:
-    resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
-    dependencies:
-      string-width: 4.2.3
-    dev: true
-
-  /wonka@6.3.4:
-    resolution: {integrity: sha512-CjpbqNtBGNAeyNS/9W6q3kSkKE52+FjIj7AkFlLr11s/VWGUu6a2CdYSdGxocIhIVjaW/zchesBQUKPVU69Cqg==}
-    dev: true
-
   /wordwrap@1.0.0:
     resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==}
     dev: true
 
-  /worktop@0.8.0-next.15:
-    resolution: {integrity: sha512-0ycNO52P6nVwsjr1y20zuf0nqJatAb8L7MODBfQIxbxndHV5O4s50oZZMHWhJG1RLpHwbK0Epq8aaQK4E2GlgQ==}
-    engines: {node: '>=12'}
-    dependencies:
-      mrmime: 1.0.1
-      regexparam: 2.0.2
-    dev: true
-
   /wrap-ansi@6.2.0:
     resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
     engines: {node: '>=8'}
@@ -13633,10 +12912,6 @@ packages:
     engines: {node: '>=12.20'}
     dev: true
 
-  /zod@3.22.4:
-    resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
-    dev: true
-
   /zwitch@2.0.4:
     resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
     dev: true
diff --git a/scripts/pagefind.js b/scripts/pagefind.js
index 196dcdd5..f8671979 100644
--- a/scripts/pagefind.js
+++ b/scripts/pagefind.js
@@ -11,15 +11,9 @@ await index.addDirectory({
 });
 
 await index.writeFiles({
-	outputPath: 'src/pagefind',
+	outputPath: 'static/pagefind',
 });
 
 await index.writeFiles({
-	outputPath: 'static/pagefind',
+	outputPath: 'build/pagefind',
 });
-
-// Write ts-nocheck to the top of the file
-import fs from 'fs';
-const file = 'src/pagefind/pagefind.js';
-const data = fs.readFileSync(file);
-fs.writeFileSync(file, '// @ts-nocheck\n' + data);
diff --git a/src/app.html b/src/app.html
index de08f804..1fc98f2c 100644
--- a/src/app.html
+++ b/src/app.html
@@ -30,7 +30,35 @@
 			content="An open-source Svelte component library for building high-quality, accessible design systems and web apps."
 		/>
 		<meta name="twitter:image" content="https://www.melt-ui.com/banner.png" />
-		<script src="/pagefind/pagefind.js" type="module"></script>
+
+		<link
+			rel="preload"
+			as="font"
+			href="https://fonts-c75.pages.dev/fonts/GTMaru-Bold.woff"
+			type="font/woff"
+			crossorigin="anonymous"
+		/>
+		<link
+			rel="preload"
+			as="font"
+			href="https://fonts-c75.pages.dev/fonts/GTMaru-Bold.woff2"
+			type="font/woff2"
+			crossorigin="anonymous"
+		/>
+		<link
+			rel="preload"
+			as="font"
+			href="https://fonts-c75.pages.dev/fonts/GTMaru-RegularOblique.woff"
+			type="font/woff"
+			crossorigin="anonymous"
+		/>
+		<link
+			rel="preload"
+			as="font"
+			href="https://fonts-c75.pages.dev/fonts/GTMaru-RegularOblique.woff2"
+			type="font/woff2"
+			crossorigin="anonymous"
+		/>
 
 		%sveltekit.head%
 	</head>
diff --git a/src/docs/components/preview.svelte b/src/docs/components/preview.svelte
index 822b4a46..62d475e9 100644
--- a/src/docs/components/preview.svelte
+++ b/src/docs/components/preview.svelte
@@ -48,7 +48,7 @@
 	};
 
 	export let code: $$Props['code'];
-	export let variant: $$Props['variant'] = 'default';
+	export let variant: $$Props['variant'] = 'dark';
 	export let size: $$Props['size'] = 'default';
 
 	const usingPreprocessor = getUsingPreprocessor();
diff --git a/src/docs/content/builders/collapsible.md b/src/docs/content/builders/collapsible.md
index ccb75ae5..f1201da4 100644
--- a/src/docs/content/builders/collapsible.md
+++ b/src/docs/content/builders/collapsible.md
@@ -7,6 +7,8 @@ description: An interactive component which expands/collapses a panel.
     import { KbdTable, APIReference, Preview } from '$docs/components'
     export let keyboard
     export let schemas
+    export let snippets
+    export let previews
 </script>
 
 ## Anatomy
@@ -15,6 +17,14 @@ description: An interactive component which expands/collapses a panel.
 - **Trigger**: The element that triggers the collapsible to expand/collapse
 - **Content**: The element that is revealed when the collapsible is expanded
 
+## Componentization
+
+If you want to create a `Collapsible` component with reactive props, you can use our sync utilities.
+
+<Preview code={snippets.sync}>
+    <svelte:component this={previews.sync} />
+</Preview>
+
 ## API Reference
 
 <APIReference {schemas} />
diff --git a/src/docs/content/builders/combobox.md b/src/docs/content/builders/combobox.md
index 01db4bb8..1829280d 100644
--- a/src/docs/content/builders/combobox.md
+++ b/src/docs/content/builders/combobox.md
@@ -18,9 +18,10 @@ description:
 - **Input**: The input that opens, closes, filters the list, and displays the selected value from
   the list
 - **Menu**: The popover menu
-  - **Item**: The individual list item
+  - **Option**: The individual combobox items
   - **Label**: The label for the input
   - **Arrow**: An optional arrow element which points to the menu's input
+  - **Group**: Optional group and group label elements which can be used to group options
 
 ## Examples
 
@@ -42,6 +43,14 @@ server, for example.
   <svelte:component this={previews.debounce} /> 
 </Preview>
 
+### Group
+
+The `group` and `groupLabel` elements can be used to group combobox list items.
+
+<Preview code={snippets.group}> 
+  <svelte:component this={previews.group} /> 
+</Preview>
+
 ## API Reference
 
 <APIReference {schemas} />
diff --git a/src/docs/content/builders/date-field.md b/src/docs/content/builders/date-field.md
index 8349ca91..c3ba250d 100644
--- a/src/docs/content/builders/date-field.md
+++ b/src/docs/content/builders/date-field.md
@@ -543,6 +543,36 @@ Here's an example showcasing a few different locales:
 Notice that they all have the same `defaultPlaceholder`, yet the segments are formatted differently
 depending on the locale, and all it took was changing the `locale` prop. Pretty cool, right?
 
+### Readonly Segments
+
+The entire Date Field builder can be set as readonly using the `readonly` prop. But in some
+situations, you may want a user to be able to edit the time but not the date, or the month but not
+the year.
+
+The `readonlySegments` prop can be used for this.
+
+```svelte showLineNumbers {9-10}
+<script lang="ts">
+	import { createDateField, melt } from '$lib'
+	import { CalendarDate } from '@internationalized/date'
+
+	const {
+		elements: { field, segment, label },
+		states: { segmentContents }
+	} = createDateField({
+		defaultValue: new CalendarDate(2023, 10, 11),
+		readonlySegments: ['year']
+	})
+</script>
+```
+
+In this example, the `year` segment is readonly, but the `month` and `day` segments are focusable
+and editable.
+
+<Preview code={snippets.readonlySegments} variant="dark" size="sm">
+	<svelte:component this={previews.readonlySegments} />
+</Preview>
+
 ## API Reference
 
 <APIReference {schemas} />
diff --git a/src/docs/content/builders/date-range-field.md b/src/docs/content/builders/date-range-field.md
index 6a73ca94..e34492ab 100644
--- a/src/docs/content/builders/date-range-field.md
+++ b/src/docs/content/builders/date-range-field.md
@@ -563,6 +563,44 @@ Here's an example showcasing a few different locales:
 Notice that they all have the same `defaultPlaceholder`, yet the segments are formatted differently
 depending on the locale, and all it took was changing the `locale` prop. Pretty cool, right?
 
+### Readonly Segments
+
+Like the Date Field builder, Date Range Fields can be set as readonly using the `readonly` prop. But
+in some situations, you may want a user to be able to edit the time but not the date, or the month
+but not the year. You can configure which segments are readonly for the start and end fields
+separately using the `readonlySegments` prop.
+
+```svelte showLineNumbers {13-16}
+<script lang="ts">
+	import { createDateRangeField, melt } from '$lib'
+	import { CalendarDate } from '@internationalized/date'
+
+	const {
+		elements: { field, startSegment, endSegment, label },
+		states: { segmentContents }
+	} = createDateRangeField({
+		defaultValue: {
+			start: new CalendarDate(2023, 10, 11),
+			end: new CalendarDate(2023, 10, 12)
+		},
+		readonlySegments: {
+			start: ['month', 'year'],
+			end: ['year']
+		}
+	})
+</script>
+```
+
+In this example, the `year` segment is readonly on both start and end fields, `month` is also
+readonly on start, but the `day` segment is focusable and editable on both.
+
+<Preview code={snippets.readonlySegments} variant="dark" size="sm">
+	<svelte:component this={previews.readonlySegments} />
+</Preview>
+
+This could be used to ensure the end year can not be set different from the start year, using a
+controlled value prop, for example.
+
 ## API Reference
 
 <APIReference {schemas} />
diff --git a/src/docs/content/builders/select.md b/src/docs/content/builders/select.md
index e6f85191..db305669 100644
--- a/src/docs/content/builders/select.md
+++ b/src/docs/content/builders/select.md
@@ -15,8 +15,10 @@ description: Presents a selection of choices to the user, activated by a button.
 
 - **Trigger**: The button that opens/closes the select menu
 - **Menu**: The popover select menu
-- **Option**: The individual select options
-- **Arrow**: An optional arrow component
+  - **Option**: The individual select options
+  - **Label**: The label for the input
+  - **Arrow**: An optional arrow element which points to the menu's input
+  - **Group**: An optional group and group label element which can be used to group options
 
 ## Usage
 
diff --git a/src/docs/content/controlled.md b/src/docs/content/controlled.md
index 53ade692..926d1254 100644
--- a/src/docs/content/controlled.md
+++ b/src/docs/content/controlled.md
@@ -18,10 +18,26 @@ you're unsure if you should be using the components in a controlled way, you lik
 
 There are various definitions for controlled/uncontrolled components, but in the context of Melt UI,
 uncontrolled means that the state and stores of a component are created and managed automatically.
-Controlled means that the user (you) can create and manage the state and stores.
+This is the default behavior, and in most cases, it is more than enough.
 
-All components are uncontrolled by default, and in most cases, this is more than enough. However,
-our goal is to provide as much flexibility as possible, so we offer a few ways to give you more
+```svelte {6}
+<script lang="ts">
+	import { createDialog } from '@melt-ui/svelte'
+
+	const {
+		elements: { trigger, overlay, content, title, description, close },
+		states: { open }
+	} = createDialog()
+</script>
+```
+
+In the example above, `open` is a store that `createDialog` returns. You can read its value to
+determine whether the `Dialog` is open or not. Since we're not altering the state in any way besides
+user interaction, we consider this to be **uncontrolled**.
+
+**Controlled** means that the user (you) can create and/or manage the state and/or stores.
+
+Our goal is to provide as much flexibility as possible, so we offer a few ways to give you more
 control over the state and behavior of the components.
 
 <Callout>
@@ -54,6 +70,88 @@ interaction.
 </script>
 ```
 
+## Syncing State
+
+A common use case for controlling the state of a component is to sync it with props, or other
+internal state. You can do it manually...
+
+```svelte {11,12}
+<script lang="ts">
+	import { createDialog } from '@melt-ui/svelte'
+
+	export let open = false
+
+	const {
+		elements: { trigger, overlay, content, title, description, close },
+		states: { open: localOpen }
+	} = createDialog()
+
+	$: $localOpen = open
+	$: open = $localOpen
+</script>
+```
+
+But it's harder to manage, can be error prone, and with multiple states and options, it can get
+messy quickly.
+
+```svelte {14-23}
+<script lang="ts">
+	import { createDialog } from '@melt-ui/svelte'
+
+	export let a
+	export let b
+	export let c
+	export let d
+	export let e
+
+	const {
+		states: { a: localA, b: localB, c: localC, d: localD, e: localE }
+	} = createDialog()
+
+	$: $localA = a
+	$: a = $localA
+	$: $localB = b
+	$: b = $localB
+	$: $localC = c
+	$: c = $localC
+	$: $localD = d
+	$: d = $localD
+	$: $localE = e
+	$: e = $localE
+</script>
+```
+
+We provide a `createSync` function that will improve this situation.
+
+```svelte {12-18}
+<script lang="ts">
+	import { createDialog, createSync } from '@melt-ui/svelte'
+
+	export let a
+	export let b
+	export let c
+	export let d
+	export let e
+
+	const { states } = createDialog()
+
+	const sync = createSync(states)
+	$: sync.a(a, (v) => (a = v))
+	$: sync.b(b, (v) => (b = v))
+	$: sync.c(c, (v) => (c = v))
+	$: sync.d(d, (v) => (d = v))
+	$: sync.e(e, (v) => (e = v))
+</script>
+```
+
+`createSync` takes an object of writable stores and returns an object with `sync` functions. The
+**first argument** of the `sync` function is the value that the store should be set to, and the
+**second argument** is a setter function that will be called with the new value of the store.
+
+It's still a bit to write, but it's much more manageable. The `sync` function will also ignore
+updates that are the same as the current value, so you don't have to worry about unnecessary
+updates.
+
 ## Bring Your Own Store
 
 If you wanted to define your own `open` store so that its state could be shared and updated by other
diff --git a/src/docs/content/usage.md b/src/docs/content/usage.md
index c300d0e0..65007ac6 100644
--- a/src/docs/content/usage.md
+++ b/src/docs/content/usage.md
@@ -114,8 +114,50 @@ purposes, should you desire to do so.
 ```
 
 <Callout>
-Svelte transitions can also be utilized. However, it is important to note that this may interfere with default functionality in particular components, such as focus management, so proceed with caution.
-</Callout>
+Svelte transitions can also be utilized. However, it is important to note that this may interfere with default functionality in particular components, such as focus management, so proceed with caution.<br/><br/>When using Svelte transitions, it is recommended to set the `forceVisible` prop to `true` to prevent
+the component from being hidden while the transition is in progress. </Callout>
+
+### Componentization
+
+Usually, you'll use Melt's builders to **build** components. You can use them in an uncontrolled
+manner, where props don't reactively affect the builder's internal state.
+
+```svelte
+<!-- Uncontrolled -->
+<script>
+	import { createCollapsible, melt } from '@melt-ui/svelte'
+
+	// This prop only affects the initial state of the component
+	export let defaultOpen = false
+
+	const {
+		elements: { root, content, trigger }
+	} = createCollapsible({ defaultOpen })
+</script>
+```
+
+Or you can use them in a controlled manner, where props do affect the builder's internal state.
+
+```svelte {12-14}
+<!-- Controlled -->
+<script>
+	import { createCollapsible, melt, createSync } from '@melt-ui/svelte'
+
+	export let open = false
+
+	const {
+		elements: { root, content, trigger },
+		states
+	} = createCollapsible()
+
+	const sync = createSync(states)
+	// Whenever the open prop changes, update the local state, and vice versa
+	$: sync.open(open, (value) => (open = value))
+</script>
+```
+
+To get a clearer picture of the contrast between uncontrolled and controlled behavior,
+[read more about it here](/docs/controlled).
 
 ## Need help?
 
diff --git a/src/docs/data/builders/accordion.ts b/src/docs/data/builders/accordion.ts
index a0e5d406..783803d5 100644
--- a/src/docs/data/builders/accordion.ts
+++ b/src/docs/data/builders/accordion.ts
@@ -9,11 +9,10 @@ import type { BuilderData } from './index.js';
  */
 const OPTION_PROPS = [
 	{
-		name: 'type',
-		type: ["'single'", "'multiple'"],
-		default: "'single'",
-		description:
-			'The type of accordion to create. A `"single"` accordion only allows one item to be open at a time. A `"multiple"` accordion allows multiple items to be open at a time.',
+		name: 'multiple',
+		type: 'boolean',
+		default: 'false',
+		description: 'If `true`, multiple accordion items can be open at the same time.',
 	},
 	PROPS.DISABLED,
 	PROPS.FORCE_VISIBLE,
diff --git a/src/docs/data/builders/combobox.ts b/src/docs/data/builders/combobox.ts
index f8521006..443b9fff 100644
--- a/src/docs/data/builders/combobox.ts
+++ b/src/docs/data/builders/combobox.ts
@@ -88,6 +88,22 @@ const builder = builderSchema(BUILDER_NAME, {
 			name: 'label',
 			description: 'The builder store used to create the label for the combobox.',
 		},
+		{
+			name: 'group',
+			description: 'The builder store used to create the combobox group.',
+		},
+		{
+			name: 'groupLabel',
+			description: 'The builder store used to create the combobox group label.',
+		},
+		{
+			name: 'hiddenInput',
+			description: 'The builder store used to create the combobox hidden input.',
+		},
+		{
+			name: 'arrow',
+			description: 'The builder store used to create the combobox arrow.',
+		},
 	],
 	states: [
 		{
@@ -217,6 +233,40 @@ const label = elementSchema('label', {
 	],
 });
 
+const group = elementSchema('group', {
+	description: 'A function which takes in a unique key to group options together.',
+	props: [
+		{
+			name: 'key',
+			type: 'string',
+			description: 'A unique key for the group.',
+		},
+	],
+	dataAttributes: [
+		{
+			name: 'data-melt-combobox-group',
+			value: ATTRS.MELT('group'),
+		},
+	],
+});
+
+const groupLabel = elementSchema('group-label', {
+	description: 'A function which takes in a unique key to group options together.',
+	props: [
+		{
+			name: 'key',
+			type: 'string',
+			description: 'A unique key for the group.',
+		},
+	],
+	dataAttributes: [
+		{
+			name: 'data-melt-combobox-group-label',
+			value: ATTRS.MELT('group-label'),
+		},
+	],
+});
+
 const hiddenInput = elementSchema('hidden-input', {
 	description: 'The hidden input element. Used for form submission.',
 	dataAttributes: [
@@ -265,7 +315,7 @@ const keyboard: KeyboardSchema = [
 	},
 ];
 
-const schemas = [builder, menu, input, item, label, arrow, hiddenInput];
+const schemas = [builder, menu, input, item, label, group, groupLabel, arrow, hiddenInput];
 
 const features = [
 	'Full keyboard navigation',
diff --git a/src/docs/data/builders/date-field.ts b/src/docs/data/builders/date-field.ts
index 88674d36..fe56888a 100644
--- a/src/docs/data/builders/date-field.ts
+++ b/src/docs/data/builders/date-field.ts
@@ -71,6 +71,11 @@ const dateFieldProps = [
 		default: 'false',
 		description: 'Whether the date field is readonly.',
 	},
+	{
+		name: 'readonlySegments',
+		type: 'EditableSegmentPart[]',
+		description: 'The set of segments that are readonly.',
+	},
 	{
 		name: 'hourCycle',
 		type: 'HourCycle',
diff --git a/src/docs/data/builders/date-range-field.ts b/src/docs/data/builders/date-range-field.ts
index 0f20b6a6..9d770555 100644
--- a/src/docs/data/builders/date-range-field.ts
+++ b/src/docs/data/builders/date-range-field.ts
@@ -70,6 +70,11 @@ const dateRangeFieldProps = [
 		default: 'false',
 		description: 'Whether the date field is readonly.',
 	},
+	{
+		name: 'readonlySegments',
+		type: '{ start: EditableSegmentPart[], end: EditableSegmentPart[] }',
+		description: 'The sets of segments that are readonly on the start and end fields.',
+	},
 	{
 		name: 'hourCycle',
 		type: 'HourCycle',
diff --git a/src/docs/data/builders/menubar.ts b/src/docs/data/builders/menubar.ts
index 868122a1..01863b5b 100644
--- a/src/docs/data/builders/menubar.ts
+++ b/src/docs/data/builders/menubar.ts
@@ -7,7 +7,7 @@ import { getMenuSchemas, getMenuTriggerDataAttrs } from './menu.js';
 import { menubarEvents } from '$lib/builders/menubar/events.js';
 import { menubarIdParts } from '$lib';
 
-const OPTION_PROPS = [PROPS.CLOSE_ON_ESCAPE, PROPS.LOOP];
+const OPTION_PROPS = [PROPS.CLOSE_ON_ESCAPE, PROPS.LOOP, PROPS.PREVENT_SCROLL];
 const BUILDER_NAME = 'menubar';
 
 const builder = builderSchema(BUILDER_NAME, {
@@ -23,9 +23,16 @@ const builder = builderSchema(BUILDER_NAME, {
 	options: OPTION_PROPS,
 });
 
+const menubarMenuBuilderProps = dropdownBuilder.props?.filter((p) => p.name !== 'preventScroll');
+const menubarMenuBuilderOptions = dropdownBuilder.options?.filter(
+	(opt) => opt.name !== 'preventScroll'
+);
+
 const menuBuilder = {
 	...dropdownBuilder,
 	title: 'createMenu',
+	props: menubarMenuBuilderProps,
+	options: menubarMenuBuilderOptions,
 };
 const {
 	menu,
diff --git a/src/docs/data/builders/tooltip.ts b/src/docs/data/builders/tooltip.ts
index cdda15c0..6afc60fb 100644
--- a/src/docs/data/builders/tooltip.ts
+++ b/src/docs/data/builders/tooltip.ts
@@ -95,6 +95,10 @@ const trigger = elementSchema('trigger', {
 const content = elementSchema('content', {
 	description: 'The tooltip content element.',
 	dataAttributes: [
+		{
+			name: 'data-state',
+			value: ATTRS.OPEN_CLOSED,
+		},
 		{
 			name: 'data-melt-tooltip-content',
 			value: ATTRS.MELT('tooltip content'),
diff --git a/src/docs/pp.js b/src/docs/pp.js
index 9d8c8fa9..690f6f52 100644
--- a/src/docs/pp.js
+++ b/src/docs/pp.js
@@ -1,14 +1,21 @@
 /**
- * @typedef {Object} MeltAttribute
+ * @typedef {Object} Builder
  * @property {string} builder - The builder of the melt attribute.
- * @property {string|null} args - The arguments of the melt attribute.
+ *
+ * @typedef {Object} Args
+ * @property {string} args - The arguments of the melt attribute.
+ *
+ * @typedef {Object} Index
+ * @property {string} index - The index of the melt attribute in the string.
+ *
+ * @typedef {Builder | Builder & Args | Builder & Index} MeltAttribute
  */
 
 /**
  * Extracts the builder and arguments from a melt attribute in a string.
  *
  * @param {string} input - The string containing the melt attribute.
- * @returns {MeltAttribute|null} - The builder and arguments of the melt attribute, or null if no melt attribute is found.
+ * @returns {MeltAttribute | null} - The builder and arguments of the melt attribute, or null if no melt attribute is found.
  */
 export function extractMeltAttribute(input) {
 	const meltStart = input.indexOf('use:melt={');
@@ -26,20 +33,28 @@ export function extractMeltAttribute(input) {
 
 	if (balance !== 0) return null;
 
-	const meltContent = substring.slice(1, index - 1);
+	const meltContent = substring.slice(0, index - 1);
 	const argStart = meltContent.indexOf('(');
 	const argEnd = meltContent.lastIndexOf(')');
 
-	// If there are no arguments
-	if (argStart === -1 || argEnd === -1) {
-		return { builder: meltContent, args: null };
+	// If there are arguments
+	if (argStart !== -1 && argEnd !== -1) {
+		const builder = meltContent.slice(0, argStart);
+		const args = meltContent.slice(argStart + 1, argEnd);
+		return { builder, args };
 	}
 
-	// If there are arguments
-	const builder = meltContent.slice(0, argStart);
-	const args = meltContent.slice(argStart + 1, argEnd);
+	const indexStart = meltContent.indexOf('[');
+	const indexEnd = meltContent.lastIndexOf(']');
+
+	// If there is an index
+	if (indexStart !== -1 && indexEnd !== -1) {
+		const builder = meltContent.slice(0, indexStart);
+		const index = meltContent.slice(indexStart + 1, indexEnd);
+		return { builder, index };
+	}
 
-	return { builder, args };
+	return { builder: meltContent };
 }
 
 /**
@@ -55,16 +70,25 @@ export function processMeltAttributes(input) {
 	let tries = 0;
 	while (meltAttribute !== null && tries < 100000) {
 		tries++;
-		const oldMelt = `use:melt={$${
-			meltAttribute.args !== null
-				? `${meltAttribute.builder}(${meltAttribute.args})`
-				: meltAttribute.builder
-		}}`;
-		const newMelt = `{...$${
-			meltAttribute.args !== null
-				? `${meltAttribute.builder}(${meltAttribute.args})`
-				: meltAttribute.builder
-		}} use:${meltAttribute.builder}`;
+		const { builder } = meltAttribute;
+		const store = builder.startsWith('$') ? builder.slice(1) : builder;
+
+		let /** @type {string} */ oldMelt, /** @type {string} */ newMelt;
+		if ('args' in meltAttribute) {
+			const { args } = meltAttribute;
+			oldMelt = `use:melt={${builder}(${args})}`;
+			newMelt = `{...${builder}(${args})} use:${store}`;
+		} else if ('index' in meltAttribute) {
+			const { index } = meltAttribute;
+			oldMelt = `use:melt={${builder}[${index}]}`;
+			newMelt = `{...${builder}[${index}]} use:${store}`;
+		} else if (builder.startsWith('$')) {
+			oldMelt = `use:melt={${builder}}`;
+			newMelt = `{...${builder}} use:${store}`;
+		} else {
+			oldMelt = `use:melt={${builder}}`;
+			newMelt = `{...${builder}} use:${builder}.action`;
+		}
 
 		output = output.replace(oldMelt, newMelt);
 		meltAttribute = extractMeltAttribute(output);
diff --git a/src/docs/pp.spec.ts b/src/docs/pp.spec.ts
index 6cbadfef..a9a6ef5a 100644
--- a/src/docs/pp.spec.ts
+++ b/src/docs/pp.spec.ts
@@ -2,15 +2,29 @@ import { test, expect } from 'vitest';
 import { extractMeltAttribute, processMeltAttributes } from './pp.js';
 
 const extractMeltAttributeCases = [
-	{ input: 'use:melt={$trigger(id)}', expected: { builder: 'trigger', args: 'id' } },
-	{ input: 'use:melt={$trigger}', expected: { builder: 'trigger', args: null } },
+	{
+		input: 'use:melt={$trigger(id)}',
+		expected: { builder: '$trigger', args: 'id' },
+	},
+	{
+		input: 'use:melt={$trigger}',
+		expected: { builder: '$trigger' },
+	},
 	{
 		input: "use:melt={$trigger({helpme: 'god'} )}",
-		expected: { builder: 'trigger', args: "{helpme: 'god'} " },
+		expected: { builder: '$trigger', args: "{helpme: 'god'} " },
 	},
 	{
 		input: "use:melt={$trigger({helpme: 'god'} )} awesome={coolio}",
-		expected: { builder: 'trigger', args: "{helpme: 'god'} " },
+		expected: { builder: '$trigger', args: "{helpme: 'god'} " },
+	},
+	{
+		input: 'use:melt={$thumbs[0]}',
+		expected: { builder: '$thumbs', index: '0' },
+	},
+	{
+		input: 'use:melt={thumb}',
+		expected: { builder: 'thumb' },
 	},
 ];
 
@@ -34,6 +48,14 @@ const processMeltAttributesCases = [
 		input: "use:melt={$trigger({helpme: 'god'} )} awesome={coolio}",
 		expected: "{...$trigger({helpme: 'god'} )} use:trigger awesome={coolio}",
 	},
+	{
+		input: 'use:melt={$thumbs[0]}',
+		expected: '{...$thumbs[0]} use:thumbs',
+	},
+	{
+		input: 'use:melt={thumb}',
+		expected: '{...thumb} use:thumb.action',
+	},
 ];
 
 processMeltAttributesCases.forEach(({ input, expected }) => {
diff --git a/src/docs/previews/collapsible/main/tailwind/index.svelte b/src/docs/previews/collapsible/main/tailwind/index.svelte
index f668bc3f..738865be 100644
--- a/src/docs/previews/collapsible/main/tailwind/index.svelte
+++ b/src/docs/previews/collapsible/main/tailwind/index.svelte
@@ -1,12 +1,14 @@
 <script lang="ts">
 	import { createCollapsible, melt } from '$lib/index.js';
-	import { slide } from 'svelte/transition';
 	import { ChevronsUpDown, X } from 'lucide-svelte';
+	import { slide } from 'svelte/transition';
 
 	const {
 		elements: { root, content, trigger },
 		states: { open },
-	} = createCollapsible();
+	} = createCollapsible({
+		forceVisible: true,
+	});
 </script>
 
 <div
diff --git a/src/docs/previews/collapsible/sync/tailwind/Collapsible.svelte b/src/docs/previews/collapsible/sync/tailwind/Collapsible.svelte
new file mode 100644
index 00000000..cdeedba4
--- /dev/null
+++ b/src/docs/previews/collapsible/sync/tailwind/Collapsible.svelte
@@ -0,0 +1,78 @@
+<script lang="ts">
+	import { createCollapsible, melt } from '$lib/index.js';
+	import { createSync } from '$lib/sync';
+	import { ChevronsUpDown, X } from 'lucide-svelte';
+	import { slide } from 'svelte/transition';
+
+	export let open = false;
+	export let disabled = false;
+
+	const {
+		elements: { root, content, trigger },
+		states,
+		options,
+	} = createCollapsible({ forceVisible: true });
+
+	const sync = createSync({ ...states, ...options });
+	$: sync.open(open, (v) => (open = v));
+	$: sync.disabled(disabled);
+</script>
+
+<div
+	use:melt={$root}
+	class="relative mx-auto mb-28 w-[18rem] max-w-full sm:w-[25rem]"
+>
+	<div class="flex items-center justify-between">
+		<span class="text-sm font-semibold text-magnum-200">
+			@thomasglopes starred 3 repositories
+		</span>
+		<button
+			use:melt={$trigger}
+			class="relative h-6 w-6 place-items-center rounded-md bg-white text-sm
+			  text-magnum-800 shadow hover:opacity-75 data-[disabled]:cursor-not-allowed
+			  data-[disabled]:opacity-75"
+			aria-label="Toggle"
+		>
+			<div class="abs-center">
+				{#if open}
+					<X class="square-4" />
+				{:else}
+					<ChevronsUpDown class="square-4" />
+				{/if}
+			</div>
+		</button>
+	</div>
+
+	<div class="my-2 rounded-lg bg-white p-3 shadow">
+		<span class="text-base text-black">melt-ui/melt-ui</span>
+	</div>
+
+	<div
+		style:position="absolute"
+		style:top="calc(100% + 10px)"
+		style:right="0"
+		style:left="0"
+	>
+		{#if open}
+			<div use:melt={$content} transition:slide>
+				<div class="flex flex-col gap-2">
+					<div class="rounded-lg bg-white p-3 shadow">
+						<span class="text-base text-black">sveltejs/svelte</span>
+					</div>
+					<div class="rounded-lg bg-white p-3 shadow">
+						<span class="text-base text-black">sveltejs/kit</span>
+					</div>
+				</div>
+			</div>
+		{/if}
+	</div>
+</div>
+
+<style lang="postcss">
+	.abs-center {
+		position: absolute;
+		left: 50%;
+		top: 50%;
+		transform: translate(-50%, -50%);
+	}
+</style>
diff --git a/src/docs/previews/collapsible/sync/tailwind/index.svelte b/src/docs/previews/collapsible/sync/tailwind/index.svelte
new file mode 100644
index 00000000..e44aa160
--- /dev/null
+++ b/src/docs/previews/collapsible/sync/tailwind/index.svelte
@@ -0,0 +1,25 @@
+<script lang="ts">
+	import Collapsible from './Collapsible.svelte';
+
+	let open = false;
+	let disabled = false;
+</script>
+
+<div class="mb-4 flex justify-center gap-4">
+	<button
+		on:click={() => (open = !open)}
+		class="rounded-xl bg-white px-4 py-3
+	font-medium leading-none text-magnum-700 shadow hover:opacity-75"
+	>
+		Open prop: {open}
+	</button>
+	<button
+		on:click={() => (disabled = !disabled)}
+		class="rounded-xl bg-white px-4 py-3
+	font-medium leading-none text-magnum-700 shadow hover:opacity-75"
+	>
+		Disabled prop: {disabled}
+	</button>
+</div>
+
+<Collapsible bind:open bind:disabled />
diff --git a/src/docs/previews/combobox/group/tailwind/index.svelte b/src/docs/previews/combobox/group/tailwind/index.svelte
new file mode 100644
index 00000000..897341f5
--- /dev/null
+++ b/src/docs/previews/combobox/group/tailwind/index.svelte
@@ -0,0 +1,188 @@
+<script lang="ts">
+	import {
+		createCombobox,
+		melt,
+		type ComboboxOptionProps,
+	} from '$lib/index.js';
+	import { Check, ChevronDown, ChevronUp } from 'lucide-svelte';
+	import { fly } from 'svelte/transition';
+
+	type Manga = {
+		author: string;
+		title: string;
+		disabled: boolean;
+	};
+
+	let mangas = {
+		shonen: [
+			{
+				author: 'ONE',
+				title: 'Mob Psycho 100',
+				disabled: false,
+			},
+			{
+				author: 'Hajime Isayama',
+				title: 'Attack on Titan',
+				disabled: false,
+			},
+			{
+				author: 'Tite Kubo',
+				title: 'Bleach',
+				disabled: false,
+			},
+			{
+				author: 'Masashi Kishimoto',
+				title: 'Naruto',
+				disabled: true,
+			},
+			{
+				author: 'Katsura Hoshino',
+				title: 'D.Gray Man',
+				disabled: false,
+			},
+			{
+				author: 'Tsugumi Ohba',
+				title: 'Death Note',
+				disabled: false,
+			},
+			{
+				author: 'Hiromu Arakawa',
+				title: 'Fullmetal Alchemist',
+				disabled: false,
+			},
+		],
+		seinen: [
+			{
+				author: 'Kentaro Miura',
+				title: 'Berserk',
+				disabled: false,
+			},
+			{
+				author: 'Junji Ito',
+				title: 'Uzumaki',
+				disabled: false,
+			},
+			{
+				author: 'Yomi Sarachi',
+				title: 'Steins Gate',
+				disabled: false,
+			},
+		],
+	};
+
+	const toOption = (manga: Manga): ComboboxOptionProps<Manga> => ({
+		value: manga,
+		label: manga.title,
+		disabled: manga.disabled,
+	});
+
+	const {
+		elements: { menu, input, option, label, group, groupLabel },
+		states: { open, inputValue, touchedInput, selected },
+		helpers: { isSelected },
+	} = createCombobox<Manga>({
+		forceVisible: true,
+	});
+
+	$: if (!$open) {
+		$inputValue = $selected?.label ?? '';
+	}
+
+	$: filteredMangas = $touchedInput
+		? {
+				shonen: mangas.shonen.filter(({ title, author }) => {
+					const normalizedInput = $inputValue.toLowerCase();
+					return (
+						title.toLowerCase().includes(normalizedInput) ||
+						author.toLowerCase().includes(normalizedInput)
+					);
+				}),
+				seinen: mangas.seinen.filter(({ title, author }) => {
+					const normalizedInput = $inputValue.toLowerCase();
+					return (
+						title.toLowerCase().includes(normalizedInput) ||
+						author.toLowerCase().includes(normalizedInput)
+					);
+				}),
+		  }
+		: mangas;
+</script>
+
+<div class="flex flex-col gap-1">
+	<!-- svelte-ignore a11y-label-has-associated-control - $label contains the 'for' attribute -->
+	<label use:melt={$label}>
+		<span class="text-sm font-medium text-magnum-900"
+			>Choose your favorite manga:</span
+		>
+	</label>
+
+	<div class="relative">
+		<input
+			use:melt={$input}
+			class="flex h-10 items-center justify-between rounded-lg bg-white
+					px-3 pr-12 text-black"
+			placeholder="Best book ever"
+		/>
+		<div class="absolute right-2 top-1/2 z-10 -translate-y-1/2 text-magnum-900">
+			{#if $open}
+				<ChevronUp class="square-4" />
+			{:else}
+				<ChevronDown class="square-4" />
+			{/if}
+		</div>
+	</div>
+</div>
+{#if $open}
+	<ul
+		class="force-dark z-10 flex max-h-[300px] flex-col overflow-hidden rounded-lg"
+		use:melt={$menu}
+		transition:fly={{ duration: 150, y: -5 }}
+	>
+		<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
+		<div
+			class="flex max-h-full flex-col gap-0 overflow-y-auto bg-white px-2 py-2 text-black"
+			tabindex="0"
+		>
+			{#each Object.entries(filteredMangas) as [key, arr]}
+				<div use:melt={$group(key)}>
+					<div
+						class="py-1 pl-4 pr-4 font-semibold capitalize text-neutral-800"
+						use:melt={$groupLabel(key)}
+					>
+						{key}
+					</div>
+					{#each arr as manga, index (index)}
+						<li
+							use:melt={$option(toOption(manga))}
+							class="relative cursor-pointer scroll-my-2 rounded-md py-2 pl-4 pr-4
+						hover:bg-magnum-100
+						data-[highlighted]:bg-magnum-200 data-[highlighted]:text-magnum-900
+							data-[disabled]:opacity-50"
+						>
+							{#if $isSelected(manga)}
+								<div class="check absolute left-2 top-1/2 z-10 text-magnum-900">
+									<Check class="square-4" />
+								</div>
+							{/if}
+							<div class="pl-4">
+								<span class="font-medium">{manga.title}</span>
+								<span class="block text-sm opacity-75">{manga.author}</span>
+							</div>
+						</li>
+					{:else}
+						<li class="relative cursor-pointer rounded-md py-1 pl-8 pr-4">
+							No results found
+						</li>
+					{/each}
+				</div>
+			{/each}
+		</div>
+	</ul>
+{/if}
+
+<style lang="postcss">
+	.check {
+		@apply absolute left-2 top-1/2 text-magnum-500;
+		translate: 0 calc(-50% + 1px);
+	}
+</style>
diff --git a/src/docs/previews/date-field/readonlySegments/css/index.svelte b/src/docs/previews/date-field/readonlySegments/css/index.svelte
new file mode 100644
index 00000000..5093640f
--- /dev/null
+++ b/src/docs/previews/date-field/readonlySegments/css/index.svelte
@@ -0,0 +1,69 @@
+<script lang="ts">
+	import { createDateField, melt } from '$lib';
+	import { CalendarDate } from '@internationalized/date';
+
+	const {
+		elements: { field, segment, label },
+		states: { segmentContents },
+	} = createDateField({
+		defaultValue: new CalendarDate(2023, 10, 11),
+		readonlySegments: ['year'],
+	});
+</script>
+
+<div>
+	<div>
+		<span use:melt={$label}>Appointment Date</span>
+		<div use:melt={$field}>
+			{#each $segmentContents as seg, i (i)}
+				<div use:melt={$segment(seg.part)}>
+					{seg.value}
+				</div>
+			{/each}
+		</div>
+	</div>
+</div>
+
+<style lang="postcss">
+	[data-melt-datefield-label] {
+		user-select: none;
+		font-weight: 500;
+		color: rgb(var(--color-white) / 1);
+	}
+
+	[data-melt-datefield-label][data-invalid] {
+		color: rgb(var(--color-magnum-500) / 1);
+	}
+
+	[data-melt-datefield-field] {
+		width: 100%;
+		min-width: 200px;
+		margin-top: 0.375rem;
+		padding: 0.375rem;
+
+		display: flex;
+		align-items: center;
+
+		background-color: rgb(var(--color-neutral-800) / 0.8);
+		color: rgb(var(--color-magnum-400) / 1);
+		border: 1px solid rgb(var(--color-magnum-400) / 0.6);
+		border-radius: 0.5rem;
+	}
+
+	[data-melt-datefield-field][data-invalid] {
+		border: 1px solid rgb(var(--color-magnum-400) / 0.6);
+	}
+
+	[data-melt-datefield-segment][data-invalid] {
+		color: rgb(var(--color-magnum-500) / 1);
+	}
+
+	[data-melt-datefield-segment]:not([data-segment='literal']) {
+		padding: 0 0.125rem;
+	}
+
+	[data-melt-datefield-validation] {
+		align-self: self-start;
+		color: rgb(var(--color-magnum-500) / 1);
+	}
+</style>
diff --git a/src/docs/previews/date-field/readonlySegments/tailwind/index.svelte b/src/docs/previews/date-field/readonlySegments/tailwind/index.svelte
new file mode 100644
index 00000000..294b66d8
--- /dev/null
+++ b/src/docs/previews/date-field/readonlySegments/tailwind/index.svelte
@@ -0,0 +1,55 @@
+<script lang="ts">
+	import { createDateField, melt } from '$lib';
+	import { CalendarDate } from '@internationalized/date';
+
+	const {
+		elements: { field, segment, label },
+		states: { segmentContents },
+	} = createDateField({
+		defaultValue: new CalendarDate(2023, 10, 11),
+		readonlySegments: ['year'],
+	});
+</script>
+
+<div>
+	<div>
+		<span use:melt={$label}>Appointment Date</span>
+		<div use:melt={$field}>
+			{#each $segmentContents as seg, i (i)}
+				<div use:melt={$segment(seg.part)}>
+					{seg.value}
+				</div>
+			{/each}
+		</div>
+	</div>
+</div>
+
+<style lang="postcss">
+	[data-melt-datefield-label] {
+		@apply select-none font-medium text-white;
+	}
+
+	[data-melt-datefield-label][data-invalid] {
+		@apply text-red-500;
+	}
+
+	[data-melt-datefield-field] {
+		@apply mt-1.5 flex w-full min-w-[200px] items-center rounded-lg border border-magnum-400/60 bg-neutral-800/80 p-1.5 text-magnum-400;
+	}
+
+	[data-melt-datefield-field][data-invalid] {
+		@apply border-red-400;
+	}
+
+	[data-melt-datefield-segment][data-invalid] {
+		@apply text-red-500;
+	}
+
+	[data-melt-datefield-segment]:not([data-segment='literal']) {
+		@apply px-0.5;
+	}
+
+	[data-melt-datefield-validation] {
+		@apply self-start text-red-500;
+	}
+</style>
diff --git a/src/docs/previews/date-range-field/readonlySegments/css/index.svelte b/src/docs/previews/date-range-field/readonlySegments/css/index.svelte
new file mode 100644
index 00000000..9d76e4ac
--- /dev/null
+++ b/src/docs/previews/date-range-field/readonlySegments/css/index.svelte
@@ -0,0 +1,88 @@
+<script lang="ts">
+	import { createDateRangeField, melt } from '$lib';
+	import { CalendarDate } from '@internationalized/date';
+
+	const {
+		elements: { field, startSegment, endSegment, label },
+		states: { segmentContents },
+	} = createDateRangeField({
+		defaultValue: {
+			start: new CalendarDate(2023, 10, 11),
+			end: new CalendarDate(2023, 10, 12),
+		},
+		readonlySegments: {
+			start: ['month', 'year'],
+			end: ['year'],
+		},
+	});
+</script>
+
+<span use:melt={$label}>Trip Dates</span>
+<div use:melt={$field}>
+	<div>
+		{#each $segmentContents.start as seg, i (i)}
+			<div use:melt={$startSegment(seg.part)}>
+				{seg.value}
+			</div>
+		{/each}
+	</div>
+	<div aria-hidden="true">-</div>
+	<div>
+		{#each $segmentContents.end as seg, i (i)}
+			<div use:melt={$endSegment(seg.part)}>
+				{seg.value}
+			</div>
+		{/each}
+	</div>
+</div>
+
+<style lang="postcss">
+	[data-melt-datefield-label] {
+		user-select: none;
+		font-weight: 500;
+		color: rgb(var(--color-white) / 1);
+	}
+
+	[data-melt-datefield-label][data-invalid] {
+		color: rgb(var(--color-magnum-500) / 1);
+	}
+
+	[data-melt-datefield-field] {
+		width: 100%;
+		min-width: 300px;
+		margin-top: 0.375rem;
+		padding: 0.375rem;
+
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		gap: 0.5rem;
+
+		background-color: rgb(var(--color-neutral-800) / 0.8);
+		color: rgb(var(--color-magnum-400) / 1);
+		border: 1px solid rgb(var(--color-magnum-400) / 0.6);
+		border-radius: 0.5rem;
+	}
+
+	[data-melt-datefield-field] > div {
+		display: flex;
+		align-items: center;
+	}
+
+	[data-melt-datefield-field][data-invalid] {
+		border: 1px solid rgb(var(--color-magnum-400) / 0.6);
+	}
+
+	[data-melt-datefield-segment][data-invalid] {
+		color: rgb(var(--color-magnum-500) / 1);
+	}
+
+	[data-melt-datefield-segment]:not([data-segment='literal']) {
+		padding: 0 0.125rem;
+	}
+
+	[data-melt-datefield-validation] {
+		align-self: self-start;
+		color: rgb(var(--color-magnum-500) / 1);
+	}
+</style>
diff --git a/src/docs/previews/date-range-field/readonlySegments/tailwind/index.svelte b/src/docs/previews/date-range-field/readonlySegments/tailwind/index.svelte
new file mode 100644
index 00000000..862fb01c
--- /dev/null
+++ b/src/docs/previews/date-range-field/readonlySegments/tailwind/index.svelte
@@ -0,0 +1,71 @@
+<script lang="ts">
+	import { createDateRangeField, melt } from '$lib';
+	import { CalendarDate } from '@internationalized/date';
+
+	const {
+		elements: { field, startSegment, endSegment, label },
+		states: { segmentContents },
+	} = createDateRangeField({
+		defaultValue: {
+			start: new CalendarDate(2023, 10, 11),
+			end: new CalendarDate(2023, 10, 12),
+		},
+		readonlySegments: {
+			start: ['month', 'year'],
+			end: ['year'],
+		},
+	});
+</script>
+
+<span use:melt={$label}>Trip Dates</span>
+<div use:melt={$field}>
+	<div>
+		{#each $segmentContents.start as seg, i (i)}
+			<div use:melt={$startSegment(seg.part)}>
+				{seg.value}
+			</div>
+		{/each}
+	</div>
+	<div aria-hidden="true">-</div>
+	<div>
+		{#each $segmentContents.end as seg, i (i)}
+			<div use:melt={$endSegment(seg.part)}>
+				{seg.value}
+			</div>
+		{/each}
+	</div>
+</div>
+
+<style lang="postcss">
+	[data-melt-datefield-label] {
+		@apply select-none font-medium text-white;
+	}
+
+	[data-melt-datefield-label][data-invalid] {
+		@apply text-red-500;
+	}
+
+	[data-melt-datefield-field] {
+		@apply mt-1.5 flex w-full min-w-[300px] items-center justify-center gap-2 rounded-lg border border-magnum-400/60 bg-neutral-800/80 p-1.5 text-magnum-400;
+
+		& > div {
+			@apply flex items-center;
+		}
+	}
+
+	[data-melt-datefield-field][data-invalid] {
+		@apply border-red-400;
+	}
+
+	[data-melt-datefield-segment][data-invalid] {
+		@apply text-red-500;
+	}
+
+	[data-melt-datefield-segment]:not([data-segment='literal']) {
+		@apply px-0.5;
+	}
+
+	[data-melt-datefield-validation] {
+		@apply self-start text-red-500;
+	}
+</style>
diff --git a/src/docs/previews/select/main/tailwind/index.svelte b/src/docs/previews/select/main/tailwind/index.svelte
index 3f297259..b19391b1 100644
--- a/src/docs/previews/select/main/tailwind/index.svelte
+++ b/src/docs/previews/select/main/tailwind/index.svelte
@@ -12,7 +12,7 @@
 		elements: { trigger, menu, option, group, groupLabel, label },
 		states: { selectedLabel, open },
 		helpers: { isSelected },
-	} = createSelect({
+	} = createSelect<string>({
 		forceVisible: true,
 		positioning: {
 			placement: 'bottom',
diff --git a/src/docs/previews/slider/main/tailwind/index.svelte b/src/docs/previews/slider/main/tailwind/index.svelte
index 252d15b9..2f2c0db6 100644
--- a/src/docs/previews/slider/main/tailwind/index.svelte
+++ b/src/docs/previews/slider/main/tailwind/index.svelte
@@ -2,7 +2,7 @@
 	import { createSlider, melt } from '$lib/index.js';
 
 	const {
-		elements: { root, range, thumb },
+		elements: { root, range, thumbs },
 	} = createSlider({
 		defaultValue: [30],
 		min: 0,
@@ -15,8 +15,9 @@
 	<span class="h-[3px] w-full bg-black/40">
 		<span use:melt={$range} class="h-[3px] bg-white" />
 	</span>
+
 	<span
-		use:melt={$thumb()}
+		use:melt={$thumbs[0]}
 		class="h-5 w-5 rounded-full bg-white focus:ring-4 focus:!ring-black/40"
 	/>
 </span>
diff --git a/src/docs/previews/slider/range/tailwind/index.svelte b/src/docs/previews/slider/range/tailwind/index.svelte
index 9350372b..c6423540 100644
--- a/src/docs/previews/slider/range/tailwind/index.svelte
+++ b/src/docs/previews/slider/range/tailwind/index.svelte
@@ -2,8 +2,7 @@
 	import { createSlider, melt } from '$lib/index.js';
 
 	const {
-		elements: { root, range, thumb },
-		states: { value },
+		elements: { root, range, thumbs },
 	} = createSlider({
 		defaultValue: [20, 80],
 		max: 100,
@@ -15,9 +14,9 @@
 		<span use:melt={$range} class="h-[3px] bg-white" />
 	</span>
 
-	{#each $value as _}
+	{#each $thumbs as thumb}
 		<span
-			use:melt={$thumb()}
+			use:melt={thumb}
 			class="h-5 w-5 rounded-full bg-white focus:ring-4 focus:!ring-black/40"
 		/>
 	{/each}
diff --git a/src/docs/previews/slider/rtl_horizontal/tailwind/index.svelte b/src/docs/previews/slider/rtl_horizontal/tailwind/index.svelte
index aeb6483e..5606844d 100644
--- a/src/docs/previews/slider/rtl_horizontal/tailwind/index.svelte
+++ b/src/docs/previews/slider/rtl_horizontal/tailwind/index.svelte
@@ -2,8 +2,7 @@
 	import { createSlider, melt } from '$lib/index.js';
 
 	const {
-		elements: { root, range, thumb },
-		states: { value },
+		elements: { root, range, thumbs },
 	} = createSlider({
 		dir: 'rtl',
 		defaultValue: [30],
@@ -16,10 +15,8 @@
 		<span use:melt={$range} class="h-[3px] bg-white" />
 	</span>
 
-	{#each $value as _}
-		<span
-			use:melt={$thumb()}
-			class="h-5 w-5 rounded-full bg-white focus:ring-4 focus:!ring-black/40"
-		/>
-	{/each}
+	<span
+		use:melt={$thumbs[0]}
+		class="h-5 w-5 rounded-full bg-white focus:ring-4 focus:!ring-black/40"
+	/>
 </span>
diff --git a/src/docs/previews/slider/rtl_vertical/tailwind/index.svelte b/src/docs/previews/slider/rtl_vertical/tailwind/index.svelte
index 85192e80..318a492a 100644
--- a/src/docs/previews/slider/rtl_vertical/tailwind/index.svelte
+++ b/src/docs/previews/slider/rtl_vertical/tailwind/index.svelte
@@ -2,8 +2,7 @@
 	import { createSlider, melt } from '$lib/index.js';
 
 	const {
-		elements: { root, range, thumb },
-		states: { value },
+		elements: { root, range, thumbs },
 	} = createSlider({
 		dir: 'rtl',
 		orientation: 'vertical',
@@ -20,10 +19,8 @@
 		<span use:melt={$range} class="w-[3px] bg-white" />
 	</span>
 
-	{#each $value as _}
-		<span
-			use:melt={$thumb()}
-			class="h-5 w-5 rounded-full bg-white focus:ring-4 focus:!ring-black/40"
-		/>
-	{/each}
+	<span
+		use:melt={$thumbs[0]}
+		class="h-5 w-5 rounded-full bg-white focus:ring-4 focus:!ring-black/40"
+	/>
 </span>
diff --git a/src/docs/previews/slider/ticks/tailwind/index.svelte b/src/docs/previews/slider/ticks/tailwind/index.svelte
index 2a80925d..d252ed49 100644
--- a/src/docs/previews/slider/ticks/tailwind/index.svelte
+++ b/src/docs/previews/slider/ticks/tailwind/index.svelte
@@ -2,8 +2,7 @@
 	import { createSlider, melt } from '$lib/index.js';
 
 	const {
-		elements: { root, range, thumb, tick },
-		states: { ticks },
+		elements: { root, range, thumbs, ticks },
 	} = createSlider({
 		defaultValue: [5],
 		min: 0,
@@ -17,15 +16,15 @@
 		<span use:melt={$range} class="h-[3px] bg-white" />
 	</span>
 
-	{#each { length: $ticks } as _}
+	{#each $ticks as tick}
 		<span
-			use:melt={$tick()}
+			use:melt={tick}
 			class="h-[3px] w-[3px] rounded-full bg-white/50 data-[bounded]:bg-magnum-800/75"
 		/>
 	{/each}
 
 	<span
-		use:melt={$thumb()}
+		use:melt={$thumbs[0]}
 		class="h-5 w-5 rounded-full bg-white focus:ring-4 focus:!ring-black/40"
 	/>
 </span>
diff --git a/src/docs/previews/slider/vertical/tailwind/index.svelte b/src/docs/previews/slider/vertical/tailwind/index.svelte
index 0dc08e12..e3ac42ba 100644
--- a/src/docs/previews/slider/vertical/tailwind/index.svelte
+++ b/src/docs/previews/slider/vertical/tailwind/index.svelte
@@ -2,7 +2,7 @@
 	import { createSlider, melt } from '$lib/index.js';
 
 	const {
-		elements: { root, range, thumb },
+		elements: { root, range, thumbs },
 	} = createSlider({
 		defaultValue: [30],
 		max: 100,
@@ -17,8 +17,9 @@
 	<span class="h-[200px] w-full bg-black/40">
 		<span use:melt={$range} class="w-full bg-white" />
 	</span>
+
 	<span
-		use:melt={$thumb()}
+		use:melt={$thumbs[0]}
 		class="h-5 w-5 rounded-full bg-white focus:ring-4 focus:!ring-black/40"
 	/>
 </span>
diff --git a/src/docs/previews/tags-input/main/tailwind/index.svelte b/src/docs/previews/tags-input/main/tailwind/index.svelte
index 440df9a5..38e2d29e 100644
--- a/src/docs/previews/tags-input/main/tailwind/index.svelte
+++ b/src/docs/previews/tags-input/main/tailwind/index.svelte
@@ -11,6 +11,7 @@
 		add(tag) {
 			return { id: tag, value: tag };
 		},
+		addOnPaste: true,
 	});
 </script>
 
diff --git a/src/fonts.css b/src/fonts.css
index 4299e291..fbc58c6e 100644
--- a/src/fonts.css
+++ b/src/fonts.css
@@ -1,7 +1,7 @@
 @font-face {
 	font-family: 'GT Maru Mono';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-Black.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-Black.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaruMono-Black.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaruMono-Black.woff') format('woff');
 	font-weight: 900;
 	font-style: normal;
 	font-display: swap;
@@ -9,8 +9,8 @@
 
 @font-face {
 	font-family: 'GT Maru';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-Regular.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-Regular.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaru-Regular.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaru-Regular.woff') format('woff');
 	font-weight: normal;
 	font-style: normal;
 	font-display: swap;
@@ -18,8 +18,8 @@
 
 @font-face {
 	font-family: 'GT Maru';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-Black.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-Black.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaru-Black.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaru-Black.woff') format('woff');
 	font-weight: 900;
 	font-style: normal;
 	font-display: swap;
@@ -27,8 +27,8 @@
 
 @font-face {
 	font-family: 'GT Maru Mono';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-BoldOblique.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-BoldOblique.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaruMono-BoldOblique.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaruMono-BoldOblique.woff') format('woff');
 	font-weight: bold;
 	font-style: italic;
 	font-display: swap;
@@ -36,8 +36,8 @@
 
 @font-face {
 	font-family: 'GT Maru';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-BoldOblique.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-BoldOblique.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaru-BoldOblique.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaru-BoldOblique.woff') format('woff');
 	font-weight: bold;
 	font-style: italic;
 	font-display: swap;
@@ -45,8 +45,8 @@
 
 @font-face {
 	font-family: 'GT Maru';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-LightOblique.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-LightOblique.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaru-LightOblique.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaru-LightOblique.woff') format('woff');
 	font-weight: 300;
 	font-style: italic;
 	font-display: swap;
@@ -54,8 +54,8 @@
 
 @font-face {
 	font-family: 'GT Maru Mono';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-MediumOblique.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-MediumOblique.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaruMono-MediumOblique.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaruMono-MediumOblique.woff') format('woff');
 	font-weight: 500;
 	font-style: italic;
 	font-display: swap;
@@ -63,8 +63,8 @@
 
 @font-face {
 	font-family: 'GT Maru';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-Medium.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-Medium.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaru-Medium.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaru-Medium.woff') format('woff');
 	font-weight: 500;
 	font-style: normal;
 	font-display: swap;
@@ -72,8 +72,8 @@
 
 @font-face {
 	font-family: 'GT Maru Mono';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-Medium.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-Medium.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaruMono-Medium.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaruMono-Medium.woff') format('woff');
 	font-weight: 500;
 	font-style: normal;
 	font-display: swap;
@@ -81,8 +81,8 @@
 
 @font-face {
 	font-family: 'GT Maru';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-RegularOblique.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-RegularOblique.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaru-RegularOblique.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaru-RegularOblique.woff') format('woff');
 	font-weight: normal;
 	font-style: italic;
 	font-display: swap;
@@ -90,8 +90,8 @@
 
 @font-face {
 	font-family: 'GT Maru';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-MediumOblique.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-MediumOblique.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaru-MediumOblique.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaru-MediumOblique.woff') format('woff');
 	font-weight: 500;
 	font-style: italic;
 	font-display: swap;
@@ -99,8 +99,8 @@
 
 @font-face {
 	font-family: 'GT Maru Mono';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-Bold.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-Bold.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaruMono-Bold.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaruMono-Bold.woff') format('woff');
 	font-weight: bold;
 	font-style: normal;
 	font-display: swap;
@@ -108,8 +108,8 @@
 
 @font-face {
 	font-family: 'GT Maru Mono';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-Regular.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-Regular.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaruMono-Regular.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaruMono-Regular.woff') format('woff');
 	font-weight: normal;
 	font-style: normal;
 	font-display: swap;
@@ -117,8 +117,8 @@
 
 @font-face {
 	font-family: 'GT Maru';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-BlackOblique.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-BlackOblique.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaru-BlackOblique.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaru-BlackOblique.woff') format('woff');
 	font-weight: 900;
 	font-style: italic;
 	font-display: swap;
@@ -126,8 +126,8 @@
 
 @font-face {
 	font-family: 'GT Maru Mono';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-BlackOblique.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-BlackOblique.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaruMono-BlackOblique.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaruMono-BlackOblique.woff') format('woff');
 	font-weight: 900;
 	font-style: italic;
 	font-display: swap;
@@ -135,8 +135,8 @@
 
 @font-face {
 	font-family: 'GT Maru Mono';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-Light.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-Light.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaruMono-Light.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaruMono-Light.woff') format('woff');
 	font-weight: 300;
 	font-style: normal;
 	font-display: swap;
@@ -144,9 +144,8 @@
 
 @font-face {
 	font-family: 'GT Maru Mono';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-RegularOblique.woff2')
-			format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-RegularOblique.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaruMono-RegularOblique.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaruMono-RegularOblique.woff') format('woff');
 	font-weight: normal;
 	font-style: italic;
 	font-display: swap;
@@ -154,8 +153,8 @@
 
 @font-face {
 	font-family: 'GT Maru';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-Bold.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-Bold.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaru-Bold.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaru-Bold.woff') format('woff');
 	font-weight: bold;
 	font-style: normal;
 	font-display: swap;
@@ -163,8 +162,8 @@
 
 @font-face {
 	font-family: 'GT Maru';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-Light.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaru-Light.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaru-Light.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaru-Light.woff') format('woff');
 	font-weight: 300;
 	font-style: normal;
 	font-display: swap;
@@ -172,8 +171,8 @@
 
 @font-face {
 	font-family: 'GT Maru Mono';
-	src: url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-LightOblique.woff2') format('woff2'),
-		url('https://fonts-hazel-mu.vercel.app/fonts/GTMaruMono-LightOblique.woff') format('woff');
+	src: url('https://fonts-c75.pages.dev/fonts/GTMaruMono-LightOblique.woff2') format('woff2'),
+		url('https://fonts-c75.pages.dev/fonts/GTMaruMono-LightOblique.woff') format('woff');
 	font-weight: 300;
 	font-style: italic;
 	font-display: swap;
diff --git a/src/lib/builders/combobox/types.ts b/src/lib/builders/combobox/types.ts
index 1b707139..72127e2c 100644
--- a/src/lib/builders/combobox/types.ts
+++ b/src/lib/builders/combobox/types.ts
@@ -10,7 +10,7 @@ export type {
 } from '../listbox/types.js';
 
 export type CreateComboboxProps<
-	Value,
+	Value = unknown,
 	Multiple extends boolean = false,
 	S extends ListboxSelected<Multiple, Value> = ListboxSelected<Multiple, Value>
 > = Omit<CreateListboxProps<Value, Multiple, S>, 'builder' | 'typeahead'>;
diff --git a/src/lib/builders/context-menu/create.ts b/src/lib/builders/context-menu/create.ts
index db93db00..47a61232 100644
--- a/src/lib/builders/context-menu/create.ts
+++ b/src/lib/builders/context-menu/create.ts
@@ -55,6 +55,7 @@ const defaults = {
 	disableFocusFirstItem: true,
 	closeFocus: undefined,
 	closeOnItemClick: true,
+	onOutsideClick: undefined,
 } satisfies CreateContextMenuProps;
 
 const { name, selector } = createElHelpers<_MenuParts>('context-menu');
@@ -110,6 +111,7 @@ export function createContextMenu(props?: CreateContextMenuProps) {
 	const longPressTimer = writable(0);
 
 	function handleClickOutside(e: PointerEvent) {
+		get(rootOptions.onOutsideClick)?.(e);
 		if (e.defaultPrevented) return;
 
 		const target = e.target;
diff --git a/src/lib/builders/date-field/create.ts b/src/lib/builders/date-field/create.ts
index 2b9f03bb..5e5b0a22 100644
--- a/src/lib/builders/date-field/create.ts
+++ b/src/lib/builders/date-field/create.ts
@@ -73,6 +73,7 @@ const defaults = {
 	hideTimeZone: false,
 	disabled: false,
 	readonly: false,
+	readonlySegments: undefined,
 	name: undefined,
 	required: false,
 	minValue: undefined,
@@ -111,6 +112,7 @@ export function createDateField(props?: CreateDateFieldProps) {
 		isDateUnavailable,
 		disabled,
 		readonly,
+		readonlySegments,
 		name: nameStore,
 		required,
 		minValue,
@@ -168,6 +170,11 @@ export function createDateField(props?: CreateDateFieldProps) {
 	 */
 	const updatingDayPeriod = writable<DayPeriod>(null);
 
+	const readonlySegmentsSet = derived(
+		readonlySegments,
+		($readonlySegments) => new Set<SegmentPart>($readonlySegments)
+	);
+
 	const ids = toWritableStores({ ...generateIds(dateFieldIdParts), ...withDefaults.ids });
 
 	const idValues = derived(
@@ -424,6 +431,7 @@ export function createDateField(props?: CreateDateFieldProps) {
 			isInvalid,
 			disabled,
 			readonly,
+			readonlySegmentsSet,
 			idValues,
 			locale,
 		],
@@ -435,6 +443,7 @@ export function createDateField(props?: CreateDateFieldProps) {
 			$isInvalid,
 			$disabled,
 			$readonly,
+			$readonlySegmentsSet,
 			$idValues,
 			_,
 		]) => {
@@ -445,11 +454,12 @@ export function createDateField(props?: CreateDateFieldProps) {
 				ids: $idValues,
 			};
 			return (part: SegmentPart) => {
+				const inReadonlySegments = $readonlySegmentsSet.has(part);
 				const defaultAttrs = {
 					...getSegmentAttrs(part, props),
 					'aria-invalid': $isInvalid ? ('true' as const) : undefined,
 					'aria-disabled': $disabled ? ('true' as const) : undefined,
-					'aria-readonly': $readonly ? ('true' as const) : undefined,
+					'aria-readonly': $readonly || inReadonlySegments ? ('true' as const) : undefined,
 					'data-invalid': $isInvalid ? '' : undefined,
 					'data-disabled': $disabled ? '' : undefined,
 					'data-segment': `${part}`,
@@ -467,7 +477,7 @@ export function createDateField(props?: CreateDateFieldProps) {
 					...defaultAttrs,
 					id: $idValues[part],
 					'aria-labelledby': getLabelledBy(part),
-					contentEditable: $readonly || $disabled ? false : true,
+					contenteditable: $readonly || inReadonlySegments || $disabled ? false : true,
 					'aria-describedby': describedBy,
 					tabindex: $disabled ? undefined : 0,
 				};
@@ -485,7 +495,7 @@ export function createDateField(props?: CreateDateFieldProps) {
 			? Updater<TimeSegmentObj[T]>
 			: Updater<DateAndTimeSegmentObj[T]>
 	) {
-		if (get(disabled) || get(readonly)) return;
+		if (get(disabled) || get(readonly) || get(readonlySegmentsSet).has(part)) return;
 		segmentValues.update((prev) => {
 			const dateRef = get(placeholder);
 			if (isDateAndTimeSegmentObj(prev)) {
diff --git a/src/lib/builders/date-field/types.ts b/src/lib/builders/date-field/types.ts
index 6d94a0c1..161a0b9a 100644
--- a/src/lib/builders/date-field/types.ts
+++ b/src/lib/builders/date-field/types.ts
@@ -3,6 +3,7 @@ import type { ChangeFn, IdObj } from '$lib/internal/helpers/index.js';
 import type { DateFieldIdParts, createDateField } from './create.js';
 import type { DateValue } from '@internationalized/date';
 import type { Granularity, Matcher } from '$lib/index.js';
+import type { EditableSegmentPart } from '$lib/shared/index.js';
 
 export type DateFieldProps = {
 	/**
@@ -116,6 +117,14 @@ export type DateFieldProps = {
 	 */
 	readonly?: boolean;
 
+	/**
+	 *
+	 * List of segment names that will be readonly.
+	 *
+	 * @default undefined
+	 */
+	readonlySegments?: EditableSegmentPart[];
+
 	/**
 	 * The format to use for displaying the time in the input.
 	 * If using a 12 hour clock, ensure you also include the
diff --git a/src/lib/builders/date-picker/create.ts b/src/lib/builders/date-picker/create.ts
index 7564f008..16f21a86 100644
--- a/src/lib/builders/date-picker/create.ts
+++ b/src/lib/builders/date-picker/create.ts
@@ -27,6 +27,7 @@ const defaults = {
 	},
 	closeOnEscape: true,
 	closeOnOutsideClick: true,
+	onOutsideClick: undefined,
 	preventScroll: false,
 	forceVisible: false,
 	locale: 'en',
@@ -72,6 +73,7 @@ export function createDatePicker(props?: CreateDatePickerProps) {
 		forceVisible: withDefaults.forceVisible,
 		openFocus: pickerOpenFocus,
 		ids: withDefaults.popoverIds,
+		onOutsideClick: withDefaults.onOutsideClick,
 	});
 
 	const trigger = builder('popover-trigger', {
diff --git a/src/lib/builders/date-picker/types.ts b/src/lib/builders/date-picker/types.ts
index 02a20402..c69771a2 100644
--- a/src/lib/builders/date-picker/types.ts
+++ b/src/lib/builders/date-picker/types.ts
@@ -125,7 +125,11 @@ export type DatePickerProps = {
 	locale?: string;
 };
 
-type ModifiedDateFieldProps = RenameProperties<CreateDateFieldProps, { ids: 'dateFieldIds' }>;
+type ModifiedDateFieldProps = Omit<
+	RenameProperties<CreateDateFieldProps, { ids: 'dateFieldIds' }>,
+	'readonlySegments'
+>;
+
 type ModifiedCalendarProps = RenameProperties<CreateCalendarProps, { ids: 'calendarIds' }>;
 type ModifiedPopoverProps = Omit<
 	RenameProperties<CreatePopoverProps, { ids: 'popoverIds' }>,
diff --git a/src/lib/builders/date-range-field/create.ts b/src/lib/builders/date-range-field/create.ts
index 174bd634..d0f21f76 100644
--- a/src/lib/builders/date-range-field/create.ts
+++ b/src/lib/builders/date-range-field/create.ts
@@ -40,6 +40,7 @@ const defaults = {
 	endName: undefined,
 	disabled: false,
 	readonly: false,
+	readonlySegments: undefined,
 	minValue: undefined,
 	maxValue: undefined,
 } satisfies CreateDateRangeFieldProps;
@@ -86,9 +87,17 @@ export function createDateRangeField(props?: CreateDateRangeFieldProps) {
 	);
 
 	const startField = createDateField({
-		...omit(withDefaults, 'defaultValue', 'onValueChange', 'startName', 'endName'),
+		...omit(
+			withDefaults,
+			'defaultValue',
+			'onValueChange',
+			'startName',
+			'endName',
+			'readonlySegments'
+		),
 		value: startValue,
 		name: withDefaults.startName,
+		readonlySegments: withDefaults.readonlySegments?.start,
 		ids: {
 			...generatedIds,
 			...withDefaults.ids,
@@ -97,9 +106,17 @@ export function createDateRangeField(props?: CreateDateRangeFieldProps) {
 	});
 
 	const endField = createDateField({
-		...omit(withDefaults, 'defaultValue', 'onValueChange', 'endName', 'startName'),
+		...omit(
+			withDefaults,
+			'defaultValue',
+			'onValueChange',
+			'endName',
+			'startName',
+			'readonlySegments'
+		),
 		value: endValue,
 		name: withDefaults.endName,
+		readonlySegments: withDefaults.readonlySegments?.end,
 		ids: {
 			...generatedIds,
 			...withDefaults.ids,
@@ -315,6 +332,10 @@ export function createDateRangeField(props?: CreateDateRangeFieldProps) {
 		startField.options.readonly.set($readonly);
 		endField.options.readonly.set($readonly);
 	});
+	effect([options.readonlySegments], ([$readonlySegments]) => {
+		startField.options.readonlySegments.set($readonlySegments?.start);
+		endField.options.readonlySegments.set($readonlySegments?.end);
+	});
 	effect([options.minValue], ([$minValue]) => {
 		startField.options.minValue.set($minValue);
 		endField.options.minValue.set($minValue);
diff --git a/src/lib/builders/date-range-field/types.ts b/src/lib/builders/date-range-field/types.ts
index 8ffe1948..e7fd3ea8 100644
--- a/src/lib/builders/date-range-field/types.ts
+++ b/src/lib/builders/date-range-field/types.ts
@@ -3,6 +3,7 @@ import type { ChangeFn, IdObj } from '$lib/internal/helpers/index.js';
 import type { DateRangeFieldIdParts, createDateRangeField } from './create.js';
 import type { DateValue } from '@internationalized/date';
 import type { CreateDateFieldProps, DateFieldIdParts, DateRange, Matcher } from '$lib/index.js';
+import type { EditableSegmentPart } from '$lib/shared/index.js';
 
 export type DateRangeFieldProps = {
 	/**
@@ -99,6 +100,14 @@ export type DateRangeFieldProps = {
 	 */
 	endName?: string;
 
+	/**
+	 *
+	 * Lists of segment names that will be readonly on the start and end fields.
+	 *
+	 * @default undefined
+	 */
+	readonlySegments?: { start: EditableSegmentPart[]; end: EditableSegmentPart[] };
+
 	/**
 	 * Override any of the element IDs set by the builder.
 	 *
@@ -114,6 +123,9 @@ export type DateRangeFieldProps = {
 
 export type CreateDateRangeFieldProps = Expand<
 	DateRangeFieldProps &
-		Omit<CreateDateFieldProps, 'value' | 'defaultValue' | 'onValueChange' | 'ids' | 'name'>
+		Omit<
+			CreateDateFieldProps,
+			'value' | 'defaultValue' | 'onValueChange' | 'ids' | 'name' | 'readonlySegments'
+		>
 >;
 export type DateRangeField = ReturnType<typeof createDateRangeField>;
diff --git a/src/lib/builders/date-range-picker/create.ts b/src/lib/builders/date-range-picker/create.ts
index 6caecbdb..d704966b 100644
--- a/src/lib/builders/date-range-picker/create.ts
+++ b/src/lib/builders/date-range-picker/create.ts
@@ -37,6 +37,7 @@ const defaults = {
 	minValue: undefined,
 	maxValue: undefined,
 	weekdayFormat: 'narrow',
+	onOutsideClick: undefined,
 } satisfies CreateDateRangePickerProps;
 
 export function createDateRangePicker(props?: CreateDateRangePickerProps) {
@@ -68,6 +69,7 @@ export function createDateRangePicker(props?: CreateDateRangePickerProps) {
 		portal: withDefaults.portal,
 		forceVisible: withDefaults.forceVisible,
 		openFocus: pickerOpenFocus,
+		onOutsideClick: withDefaults.onOutsideClick,
 	});
 
 	const options = toWritableStores({
@@ -151,6 +153,10 @@ export function createDateRangePicker(props?: CreateDateRangePickerProps) {
 		}
 	});
 
+	effect([options.onOutsideClick], ([$onOutsideClick]) => {
+		popover.options.onOutsideClick.set($onOutsideClick);
+	});
+
 	const rangeFieldOptions = omit(
 		rangeField.options,
 		'locale',
diff --git a/src/lib/builders/date-range-picker/types.ts b/src/lib/builders/date-range-picker/types.ts
index 35fa6734..c224bff3 100644
--- a/src/lib/builders/date-range-picker/types.ts
+++ b/src/lib/builders/date-range-picker/types.ts
@@ -8,7 +8,7 @@ import type { RenameProperties } from '$lib/internal/types.js';
 import type { CreateDateRangeFieldProps } from '../date-range-field/types.js';
 import type { CreateRangeCalendarProps } from '../range-calendar/types.js';
 
-export type DateRangePickerProps = {
+type DateRangePickerProps = {
 	/**
 	 * The default value for the date field. When provided,
 	 * the `placeholder` will also assume this value.
@@ -125,7 +125,10 @@ export type DateRangePickerProps = {
 	locale?: string;
 };
 
-type ModifiedDateFieldProps = RenameProperties<CreateDateRangeFieldProps, { ids: 'dateFieldIds' }>;
+type ModifiedDateFieldProps = Omit<
+	RenameProperties<CreateDateRangeFieldProps, { ids: 'dateFieldIds' }>,
+	'readonlySegments'
+>;
 type ModifiedCalendarProps = RenameProperties<CreateRangeCalendarProps, { ids: 'calendarIds' }>;
 type ModifiedPopoverProps = Omit<
 	RenameProperties<CreatePopoverProps, { ids: 'popoverIds' }>,
diff --git a/src/lib/builders/dialog/create.ts b/src/lib/builders/dialog/create.ts
index ffa9beab..9fc989b4 100644
--- a/src/lib/builders/dialog/create.ts
+++ b/src/lib/builders/dialog/create.ts
@@ -23,11 +23,11 @@ import {
 	sleep,
 	styleToString,
 	toWritableStores,
+	omit,
 } from '$lib/internal/helpers/index.js';
 import type { Defaults, MeltActionReturn } from '$lib/internal/types.js';
 import { tick } from 'svelte';
 import { derived, get, writable } from 'svelte/store';
-import { omit } from '../../internal/helpers/object';
 import type { DialogEvents } from './events.js';
 import type { CreateDialogProps } from './types.js';
 
@@ -51,6 +51,7 @@ const defaults = {
 	forceVisible: false,
 	openFocus: undefined,
 	closeFocus: undefined,
+	onOutsideClick: undefined,
 } satisfies Defaults<CreateDialogProps>;
 
 const openDialogIds = writable<string[]>([]);
@@ -72,6 +73,7 @@ export function createDialog(props?: CreateDialogProps) {
 		forceVisible,
 		openFocus,
 		closeFocus,
+		onOutsideClick,
 	} = options;
 
 	const activeTrigger = writable<HTMLElement | null>(null);
@@ -229,6 +231,7 @@ export function createDialog(props?: CreateDialogProps) {
 					return useClickOutside(node, {
 						enabled: $open,
 						handler: (e: PointerEvent) => {
+							get(onOutsideClick)?.(e);
 							if (e.defaultPrevented) return;
 
 							const $openDialogIds = get(openDialogIds);
diff --git a/src/lib/builders/dialog/types.ts b/src/lib/builders/dialog/types.ts
index d6087b47..7154acdb 100644
--- a/src/lib/builders/dialog/types.ts
+++ b/src/lib/builders/dialog/types.ts
@@ -4,13 +4,62 @@ import type { Writable } from 'svelte/store';
 import type { DialogIdParts, createDialog } from './create.js';
 export type { DialogComponentEvents } from './events.js';
 export type CreateDialogProps = {
+	/**
+	 * If true, the dialog will prevent scrolling on the body
+	 * when it is open.
+	 *
+	 * @default true
+	 */
 	preventScroll?: boolean;
+
+	/**
+	 * If true, the dialog will close when the user presses the escape key.
+	 *
+	 * @default true
+	 */
 	closeOnEscape?: boolean;
+
+	/**
+	 * If true, the dialog will close when the user clicks outside of it.
+	 *
+	 * @default true
+	 */
 	closeOnOutsideClick?: boolean;
+
+	/**
+	 * A custom event handler for the "outside click" event, which
+	 * is handled by the `document`.
+	 * If `event.preventDefault()` is called within the function,
+	 * the dialog will not close when the user clicks outside of it.
+	 */
+	onOutsideClick?: (event: PointerEvent) => void;
+
+	/**
+	 * The `role` attribute to apply to the dialog.
+	 *
+	 * @default 'dialog'
+	 */
 	role?: 'dialog' | 'alertdialog';
+
+	/**
+	 * If true, the dialog will be open by default.
+	 *
+	 * @default false
+	 */
 	defaultOpen?: boolean;
+
+	/**
+	 * A writable store that controls the open state of the dialog.
+	 * @see [Controlled Usage](https://melt-ui.com/docs/controlled#bring-your-own-store)
+	 */
 	open?: Writable<boolean>;
+
+	/**
+	 * A function that will be called when the open state of the dialog changes.
+	 * @see [Controlled Usage](https://melt-ui.com/docs/controlled#change-functions)
+	 */
 	onOpenChange?: ChangeFn<boolean>;
+
 	/**
 	 * If not undefined, the dialog content will be rendered within the provided element or selector.
 	 *
@@ -18,6 +67,13 @@ export type CreateDialogProps = {
 	 */
 	portal?: HTMLElement | string | null;
 
+	/**
+	 * If true, the dialog will be visible regardless of the open state.
+	 * Use this when you want to conditionally render the content of the dialog
+	 * using an `{#if ...}` block.
+	 *
+	 * @default false
+	 */
 	forceVisible?: boolean;
 
 	/**
diff --git a/src/lib/builders/dropdown-menu/create.ts b/src/lib/builders/dropdown-menu/create.ts
index 0d055ef9..8ba8fddf 100644
--- a/src/lib/builders/dropdown-menu/create.ts
+++ b/src/lib/builders/dropdown-menu/create.ts
@@ -21,6 +21,7 @@ const defaults = {
 	closeFocus: undefined,
 	disableFocusFirstItem: false,
 	closeOnItemClick: true,
+	onOutsideClick: undefined,
 } satisfies CreateDropdownMenuProps;
 
 export function createDropdownMenu(props?: CreateDropdownMenuProps) {
diff --git a/src/lib/builders/link-preview/create.ts b/src/lib/builders/link-preview/create.ts
index 99f92273..7152609b 100644
--- a/src/lib/builders/link-preview/create.ts
+++ b/src/lib/builders/link-preview/create.ts
@@ -42,6 +42,7 @@ const defaults = {
 	forceVisible: false,
 	portal: 'body',
 	closeOnEscape: true,
+	onOutsideClick: undefined,
 } satisfies CreateLinkPreviewProps;
 
 export const linkPreviewIdParts = ['trigger', 'content'] as const;
@@ -71,6 +72,7 @@ export function createLinkPreview(props: CreateLinkPreviewProps = {}) {
 		forceVisible,
 		portal,
 		closeOnEscape,
+		onOutsideClick,
 	} = options;
 
 	const ids = toWritableStores({ ...generateIds(linkPreviewIdParts), ...withDefaults.ids });
@@ -190,7 +192,22 @@ export function createLinkPreview(props: CreateLinkPreviewProps = {}) {
 						open: open,
 						options: {
 							floating: $positioning,
-							clickOutside: $closeOnOutsideClick ? undefined : null,
+							clickOutside: $closeOnOutsideClick
+								? {
+										handler: (e) => {
+											get(onOutsideClick)?.(e);
+											if (e.defaultPrevented) return;
+
+											if (
+												isHTMLElement($activeTrigger) &&
+												!$activeTrigger.contains(e.target as Element)
+											) {
+												open.set(false);
+												$activeTrigger.focus();
+											}
+										},
+								  }
+								: null,
 							portal: getPortalDestination(node, $portal),
 							focusTrap: null,
 							escapeKeydown: $closeOnEscape ? undefined : null,
diff --git a/src/lib/builders/link-preview/types.ts b/src/lib/builders/link-preview/types.ts
index 49b772b7..21094a75 100644
--- a/src/lib/builders/link-preview/types.ts
+++ b/src/lib/builders/link-preview/types.ts
@@ -57,6 +57,14 @@ export type CreateLinkPreviewProps = {
 	 */
 	closeOnOutsideClick?: boolean;
 
+	/**
+	 * A custom event handler for the "outside click" event, which
+	 * is handled by the `document`.
+	 * If `event.preventDefault()` is called within the function,
+	 * the dialog will not close when the user clicks outside of it.
+	 */
+	onOutsideClick?: (event: PointerEvent) => void;
+
 	/**
 	 * Whether or not to close the linkpreview when the escape key is pressed
 	 * while it is open.
diff --git a/src/lib/builders/listbox/create.ts b/src/lib/builders/listbox/create.ts
index 168bf2fb..dede2524 100644
--- a/src/lib/builders/listbox/create.ts
+++ b/src/lib/builders/listbox/create.ts
@@ -75,11 +75,23 @@ const defaults = {
 	name: undefined,
 	typeahead: true,
 	highlightOnHover: true,
+	onOutsideClick: undefined,
 } satisfies Defaults<CreateListboxProps<unknown>>;
 
 export const listboxIdParts = ['trigger', 'menu', 'label'] as const;
 export type ListboxIdParts = typeof listboxIdParts;
 
+type ListboxParts =
+	| 'trigger'
+	| 'menu'
+	| 'item'
+	| 'label'
+	| 'option'
+	| 'group'
+	| 'group-label'
+	| 'hidden-input'
+	| 'arrow';
+
 /**
  * Creates an ARIA-1.2-compliant listbox.
  *
@@ -133,8 +145,9 @@ export function createListbox<
 		typeahead,
 		name: nameProp,
 		highlightOnHover,
+		onOutsideClick,
 	} = options;
-	const { name, selector } = createElHelpers(withDefaults.builder);
+	const { name, selector } = createElHelpers<ListboxParts>(withDefaults.builder);
 
 	const ids = toWritableStores({ ...generateIds(listboxIdParts), ...withDefaults.ids });
 
@@ -458,15 +471,8 @@ export function createListbox<
 			const unsubscribe = executeCallbacks(
 				// Bind the popper portal to the input element.
 				effect(
-					[isVisible, closeOnEscape, portal, closeOnOutsideClick, positioning, activeTrigger],
-					([
-						$isVisible,
-						$closeOnEscape,
-						$portal,
-						$closeOnOutsideClick,
-						$positioning,
-						$activeTrigger,
-					]) => {
+					[isVisible, portal, closeOnOutsideClick, positioning, activeTrigger],
+					([$isVisible, $portal, $closeOnOutsideClick, $positioning, $activeTrigger]) => {
 						unsubPopper();
 
 						if (!$isVisible || !$activeTrigger) return;
@@ -482,6 +488,9 @@ export function createListbox<
 								clickOutside: $closeOnOutsideClick
 									? {
 											handler: (e) => {
+												get(onOutsideClick)?.(e);
+												if (e.defaultPrevented) return;
+
 												const target = e.target;
 												if (!isElement(target)) return;
 												if (target === $activeTrigger || $activeTrigger.contains(target)) {
@@ -492,13 +501,7 @@ export function createListbox<
 											ignore: ignoreHandler,
 									  }
 									: null,
-								escapeKeydown: $closeOnEscape
-									? {
-											handler: () => {
-												closeMenu();
-											},
-									  }
-									: null,
+								escapeKeydown: null,
 								portal: getPortalDestination(node, $portal),
 							},
 						});
@@ -585,6 +588,23 @@ export function createListbox<
 		},
 	});
 
+	const group = builder(name('group'), {
+		returned: () => {
+			return (groupId: string) => ({
+				role: 'group',
+				'aria-labelledby': groupId,
+			});
+		},
+	});
+
+	const groupLabel = builder(name('group-label'), {
+		returned: () => {
+			return (groupId: string) => ({
+				id: groupId,
+			});
+		},
+	});
+
 	const hiddenInput = builder(name('hidden-input'), {
 		stores: [selected, required, nameProp],
 		returned: ([$selected, $required, $name]) => {
@@ -663,8 +683,10 @@ export function createListbox<
 		ids,
 		elements: {
 			trigger,
+			group,
 			option,
 			menu,
+			groupLabel,
 			label,
 			hiddenInput,
 			arrow,
diff --git a/src/lib/builders/listbox/types.ts b/src/lib/builders/listbox/types.ts
index f62a7bc4..ba1bc0a7 100644
--- a/src/lib/builders/listbox/types.ts
+++ b/src/lib/builders/listbox/types.ts
@@ -111,6 +111,14 @@ export type CreateListboxProps<
 	 */
 	closeOnEscape?: boolean;
 
+	/**
+	 * A custom event handler for the "outside click" event, which
+	 * is handled by the `document`.
+	 * If `event.preventDefault()` is called within the function,
+	 * the dialog will not close when the user clicks outside of it.
+	 */
+	onOutsideClick?: (event: PointerEvent) => void;
+
 	/**
 	 * Whether or not to prevent scrolling the page when the
 	 * listbox menu is open.
diff --git a/src/lib/builders/menu/create.ts b/src/lib/builders/menu/create.ts
index 61fc3fa3..1fb3b168 100644
--- a/src/lib/builders/menu/create.ts
+++ b/src/lib/builders/menu/create.ts
@@ -76,6 +76,7 @@ const defaults = {
 	defaultOpen: false,
 	typeahead: true,
 	closeOnItemClick: true,
+	onOutsideClick: undefined,
 } satisfies Defaults<_CreateMenuProps>;
 
 export function createMenuBuilder(opts: _MenuBuilderOptions) {
@@ -94,6 +95,7 @@ export function createMenuBuilder(opts: _MenuBuilderOptions) {
 		closeFocus,
 		disableFocusFirstItem,
 		closeOnItemClick,
+		onOutsideClick,
 	} = opts.rootOptions;
 
 	const rootOpen = opts.rootOpen;
@@ -189,7 +191,22 @@ export function createMenuBuilder(opts: _MenuBuilderOptions) {
 							open: rootOpen,
 							options: {
 								floating: $positioning,
-								clickOutside: $closeOnOutsideClick ? undefined : null,
+								clickOutside: $closeOnOutsideClick
+									? {
+											handler: (e) => {
+												get(onOutsideClick)?.(e);
+												if (e.defaultPrevented) return;
+
+												if (
+													isHTMLElement($rootActiveTrigger) &&
+													!$rootActiveTrigger.contains(e.target as Element)
+												) {
+													rootOpen.set(false);
+													$rootActiveTrigger.focus();
+												}
+											},
+									  }
+									: null,
 								portal: getPortalDestination(node, $portal),
 								escapeKeydown: $closeOnEscape ? undefined : null,
 							},
@@ -849,6 +866,10 @@ export function createMenuBuilder(opts: _MenuBuilderOptions) {
 			action: (node: HTMLElement): MeltActionReturn<MenuEvents['subTrigger']> => {
 				setMeltMenuAttribute(node, selector);
 				applyAttrsIfDisabled(node);
+				subActiveTrigger.update((p) => {
+					if (p) return p;
+					return node;
+				});
 
 				const unsubTimer = () => {
 					clearTimerStore(subOpenTimer);
@@ -1117,55 +1138,50 @@ export function createMenuBuilder(opts: _MenuBuilderOptions) {
 		}
 	});
 
-	effect(
-		[rootOpen, rootActiveTrigger, preventScroll],
-		([$rootOpen, $rootActiveTrigger, $preventScroll]) => {
-			if (!isBrowser) return;
-
-			const unsubs: Array<() => void> = [];
+	effect([rootOpen], ([$rootOpen]) => {
+		if (!isBrowser) return;
+		if (!$rootOpen) {
+			const $rootActiveTrigger = get(rootActiveTrigger);
+			if (!$rootActiveTrigger) return;
+			const $closeFocus = get(closeFocus);
 
-			if (opts.removeScroll && $rootOpen && $preventScroll) {
-				unsubs.push(removeScroll());
+			if (!$rootOpen && $rootActiveTrigger) {
+				handleFocus({ prop: $closeFocus, defaultEl: $rootActiveTrigger });
 			}
+		}
+	});
 
-			const $closeFocus = get(closeFocus);
+	effect([rootOpen, preventScroll], ([$rootOpen, $preventScroll]) => {
+		if (!isBrowser) return;
 
-			if (!$rootOpen) {
-				if ($rootActiveTrigger) {
-					// If we already have a reference to the trigger, focus it
-					handleFocus({ prop: $closeFocus, defaultEl: $rootActiveTrigger });
-				} else {
-					// otherwise we'll get the trigger el and focus it
-					handleFocus({
-						prop: $closeFocus,
-						defaultEl: document.getElementById(get(rootIds.trigger)),
-					});
-				}
-			}
+		const unsubs: Array<() => void> = [];
 
-			// if the menu is open, we'll sleep for a sec so the menu can render
-			// before we focus on either the first item or the menu itself.
-			sleep(1).then(() => {
-				const menuEl = document.getElementById(get(rootIds.menu));
-				if (menuEl && $rootOpen && get(isUsingKeyboard)) {
-					if (get(disableFocusFirstItem)) {
-						handleRovingFocus(menuEl);
-						return;
-					}
-					// Get menu items belonging to the root menu
-					const menuItems = getMenuItems(menuEl);
-					if (!menuItems.length) return;
+		if (opts.removeScroll && $rootOpen && $preventScroll) {
+			unsubs.push(removeScroll());
+		}
 
-					// Focus on first menu item
-					handleRovingFocus(menuItems[0]);
+		// if the menu is open, we'll sleep for a sec so the menu can render
+		// before we focus on either the first item or the menu itself.
+		sleep(1).then(() => {
+			const menuEl = document.getElementById(get(rootIds.menu));
+			if (menuEl && $rootOpen && get(isUsingKeyboard)) {
+				if (get(disableFocusFirstItem)) {
+					handleRovingFocus(menuEl);
+					return;
 				}
-			});
+				// Get menu items belonging to the root menu
+				const menuItems = getMenuItems(menuEl);
+				if (!menuItems.length) return;
 
-			return () => {
-				unsubs.forEach((unsub) => unsub());
-			};
-		}
-	);
+				// Focus on first menu item
+				handleRovingFocus(menuItems[0]);
+			}
+		});
+
+		return () => {
+			unsubs.forEach((unsub) => unsub());
+		};
+	});
 
 	effect(rootOpen, ($rootOpen) => {
 		if (!isBrowser) return;
diff --git a/src/lib/builders/menu/types.ts b/src/lib/builders/menu/types.ts
index 38e4df10..a5ff239f 100644
--- a/src/lib/builders/menu/types.ts
+++ b/src/lib/builders/menu/types.ts
@@ -60,6 +60,14 @@ export type _CreateMenuProps = {
 	 */
 	closeOnOutsideClick?: boolean;
 
+	/**
+	 * A custom event handler for the "outside click" event, which
+	 * is handled by the `document`.
+	 * If `event.preventDefault()` is called within the function,
+	 * the dialog will not close when the user clicks outside of it.
+	 */
+	onOutsideClick?: (event: PointerEvent) => void;
+
 	/**
 	 * Whether or not to loop the menu navigation.
 	 *
@@ -181,6 +189,7 @@ export type _MenuBuilderOptions = {
 		closeFocus: Writable<FocusProp | undefined>;
 		disableFocusFirstItem: Writable<boolean>;
 		closeOnItemClick: Writable<boolean>;
+		onOutsideClick: Writable<((event: PointerEvent) => void) | undefined>;
 	};
 
 	nextFocusable: Writable<HTMLElement | null>;
diff --git a/src/lib/builders/menubar/create.ts b/src/lib/builders/menubar/create.ts
index 3c923720..6da83a83 100644
--- a/src/lib/builders/menubar/create.ts
+++ b/src/lib/builders/menubar/create.ts
@@ -23,13 +23,13 @@ import {
 	removeScroll,
 	styleToString,
 	toWritableStores,
+	generateIds,
+	omit,
 } from '$lib/internal/helpers/index.js';
 import { safeOnDestroy, safeOnMount } from '$lib/internal/helpers/lifecycle.js';
 import type { MeltActionReturn } from '$lib/internal/types.js';
 import { tick } from 'svelte';
 import { get, writable } from 'svelte/store';
-import { generateIds } from '../../internal/helpers/id';
-import { omit } from '../../internal/helpers/object';
 import {
 	applyAttrsIfDisabled,
 	createMenuBuilder,
@@ -48,6 +48,7 @@ const { name } = createElHelpers<_MenuParts | 'menu'>('menubar');
 const defaults = {
 	loop: true,
 	closeOnEscape: true,
+	preventScroll: false,
 } satisfies CreateMenubarProps;
 
 export const menubarIdParts = ['menubar'] as const;
@@ -57,7 +58,7 @@ export function createMenubar(props?: CreateMenubarProps) {
 	const withDefaults = { ...defaults, ...props } satisfies CreateMenubarProps;
 
 	const options = toWritableStores(omit(withDefaults, 'ids'));
-	const { loop, closeOnEscape } = options;
+	const { loop, closeOnEscape, preventScroll } = options;
 	const activeMenu = writable<string>('');
 
 	const nextFocusable = writable<HTMLElement | null>(null);
@@ -93,7 +94,6 @@ export function createMenubar(props?: CreateMenubarProps) {
 		positioning: {
 			placement: 'bottom-start',
 		},
-		preventScroll: true,
 		arrowSize: 8,
 		dir: 'ltr',
 		loop: false,
@@ -106,6 +106,7 @@ export function createMenubar(props?: CreateMenubarProps) {
 		closeFocus: undefined,
 		disableFocusFirstItem: false,
 		closeOnItemClick: true,
+		onOutsideClick: undefined,
 	} satisfies CreateMenubarMenuProps;
 
 	const createMenu = (props?: CreateMenubarMenuProps) => {
@@ -115,10 +116,10 @@ export function createMenubar(props?: CreateMenubarProps) {
 
 		// options
 		const options = toWritableStores(withDefaults);
-		const { positioning, portal, forceVisible } = options;
+		const { positioning, portal, forceVisible, closeOnOutsideClick, onOutsideClick } = options;
 
 		const m = createMenuBuilder({
-			rootOptions: options,
+			rootOptions: { ...options, preventScroll },
 			rootOpen,
 			rootActiveTrigger,
 			nextFocusable,
@@ -154,8 +155,8 @@ export function createMenubar(props?: CreateMenubarProps) {
 				let unsubPopper = noop;
 
 				const unsubDerived = effect(
-					[rootOpen, rootActiveTrigger, positioning, portal],
-					([$rootOpen, $rootActiveTrigger, $positioning, $portal]) => {
+					[rootOpen, rootActiveTrigger, positioning, portal, closeOnOutsideClick],
+					([$rootOpen, $rootActiveTrigger, $positioning, $portal, $closeOnOutsideClick]) => {
 						unsubPopper();
 						if (!($rootOpen && $rootActiveTrigger)) return;
 
@@ -166,17 +167,21 @@ export function createMenubar(props?: CreateMenubarProps) {
 								options: {
 									floating: $positioning,
 									portal: getPortalDestination(node, $portal),
-									clickOutside: {
-										ignore: (e) => {
-											const target = e.target;
-											const menubarEl = document.getElementById(get(ids.menubar));
-											if (!menubarEl || !isElement(target)) return false;
-											return menubarEl.contains(target);
-										},
-										handler: () => {
-											activeMenu.set('');
-										},
-									},
+									clickOutside: $closeOnOutsideClick
+										? {
+												ignore: (e) => {
+													const target = e.target;
+													const menubarEl = document.getElementById(get(ids.menubar));
+													if (!menubarEl || !isElement(target)) return false;
+													return menubarEl.contains(target);
+												},
+												handler: (e) => {
+													get(onOutsideClick)?.(e);
+													if (e.defaultPrevented) return;
+													activeMenu.set('');
+												},
+										  }
+										: null,
 								},
 							});
 
@@ -583,8 +588,8 @@ export function createMenubar(props?: CreateMenubarProps) {
 	});
 
 	const unsubs: Array<() => void> = [];
-	effect([activeMenu], ([$activeMenu]) => {
-		if (!isBrowser) return;
+	effect([activeMenu, preventScroll], ([$activeMenu, $preventScroll]) => {
+		if (!isBrowser || !$preventScroll) return;
 
 		/**
 		 * To prevent adding/removing the scroll as we cycle through
diff --git a/src/lib/builders/menubar/types.ts b/src/lib/builders/menubar/types.ts
index 9fd7b415..4e12e494 100644
--- a/src/lib/builders/menubar/types.ts
+++ b/src/lib/builders/menubar/types.ts
@@ -16,16 +16,26 @@ export type CreateMenubarProps = {
 
 	/**
 	 * Whether to close the active menu when the escape key is pressed.
+	 *
+	 * @default true
 	 */
 	closeOnEscape?: boolean;
 
+	/**
+	 * Whether to prevent scrolling the body when any menu within
+	 * the menubar is open.
+	 *
+	 * @default true
+	 */
+	preventScroll?: boolean;
+
 	/**
 	 * Optionally override the default ids we assign to the elements
 	 */
 	ids?: Partial<IdObj<MenubarIdParts>>;
 };
 
-export type CreateMenubarMenuProps = _Menu['builder'];
+export type CreateMenubarMenuProps = Omit<_Menu['builder'], 'preventScroll'>;
 export type CreateMenubarSubmenuProps = _Menu['submenu'];
 export type MenubarMenuItemProps = _Menu['item'];
 export type CreateMenuRadioGroupProps = _Menu['radioGroup'];
diff --git a/src/lib/builders/popover/create.ts b/src/lib/builders/popover/create.ts
index fb5cab2d..485b99af 100644
--- a/src/lib/builders/popover/create.ts
+++ b/src/lib/builders/popover/create.ts
@@ -43,6 +43,7 @@ const defaults = {
 	forceVisible: false,
 	openFocus: undefined,
 	closeFocus: undefined,
+	onOutsideClick: undefined,
 } satisfies Defaults<CreatePopoverProps>;
 
 type PopoverParts = 'trigger' | 'content' | 'arrow' | 'close';
@@ -66,6 +67,7 @@ export function createPopover(args?: CreatePopoverProps) {
 		forceVisible,
 		openFocus,
 		closeFocus,
+		onOutsideClick,
 	} = options;
 
 	const openWritable = withDefaults.open ?? writable(withDefaults.defaultOpen);
@@ -179,6 +181,8 @@ export function createPopover(args?: CreatePopoverProps) {
 	}
 
 	function handleClickOutside(e: PointerEvent) {
+		get(onOutsideClick)?.(e);
+		if (e.defaultPrevented) return;
 		const target = e.target;
 		const triggerEl = document.getElementById(get(ids.trigger));
 
diff --git a/src/lib/builders/popover/types.ts b/src/lib/builders/popover/types.ts
index d4f42779..48731e8e 100644
--- a/src/lib/builders/popover/types.ts
+++ b/src/lib/builders/popover/types.ts
@@ -60,6 +60,14 @@ export type CreatePopoverProps = {
 	 */
 	closeOnOutsideClick?: boolean;
 
+	/**
+	 * A custom event handler for the "outside click" event, which
+	 * is handled by the `document`.
+	 * If `event.preventDefault()` is called within the function,
+	 * the dialog will not close when the user clicks outside of it.
+	 */
+	onOutsideClick?: (event: PointerEvent) => void;
+
 	/**
 	 * Whether or not to prevent scrolling when the popover is open.
 	 *
diff --git a/src/lib/builders/progress/types.ts b/src/lib/builders/progress/types.ts
index 9113037c..e840e18e 100644
--- a/src/lib/builders/progress/types.ts
+++ b/src/lib/builders/progress/types.ts
@@ -9,7 +9,7 @@ export type CreateProgressProps = {
 	 *
 	 * @default 0
 	 */
-	defaultValue?: number;
+	defaultValue?: number | null;
 
 	/**
 	 * The controlled value store for the radio group.
@@ -17,14 +17,14 @@ export type CreateProgressProps = {
 	 *
 	 * @see https://melt-ui.com/docs/controlled#bring-your-own-store
 	 */
-	value?: Writable<number>;
+	value?: Writable<number | null>;
 
 	/**
 	 * The callback invoked when the value store of the progress changes.
 	 *
 	 * @see https://melt-ui.com/docs/controlled#change-functions
 	 */
-	onValueChange?: ChangeFn<number>;
+	onValueChange?: ChangeFn<number | null>;
 
 	/**
 	 * The maximum value of the progress.
diff --git a/src/lib/builders/radio-group/create.ts b/src/lib/builders/radio-group/create.ts
index be95c8b5..1c693bc4 100644
--- a/src/lib/builders/radio-group/create.ts
+++ b/src/lib/builders/radio-group/create.ts
@@ -128,7 +128,7 @@ export function createRadioGroup(props?: CreateRadioGroupProps) {
 					if (!isHTMLElement(root)) return;
 
 					const items = Array.from(root.querySelectorAll(selector('item'))).filter(
-						(el): el is HTMLElement => isHTMLElement(el)
+						(el): el is HTMLElement => isHTMLElement(el) && !el.hasAttribute('data-disabled')
 					);
 					const currentIndex = items.indexOf(el);
 
diff --git a/src/lib/builders/select/create.ts b/src/lib/builders/select/create.ts
index 83731751..9ea23484 100644
--- a/src/lib/builders/select/create.ts
+++ b/src/lib/builders/select/create.ts
@@ -1,20 +1,7 @@
-import { builder, createElHelpers } from '$lib/internal/helpers/index.js';
 import { derived } from 'svelte/store';
 import { createListbox } from '../listbox/create.js';
 import type { CreateSelectProps, SelectSelected } from './types.js';
 
-type SelectParts =
-	| 'menu'
-	| 'trigger'
-	| 'option'
-	| 'group'
-	| 'group-label'
-	| 'arrow'
-	| 'hidden-input'
-	| 'label';
-
-const { name } = createElHelpers<SelectParts>('select');
-
 export function createSelect<
 	Value = unknown,
 	Multiple extends boolean = false,
@@ -23,23 +10,6 @@ export function createSelect<
 >(props?: CreateSelectProps<Value, Multiple, S>) {
 	const listbox = createListbox({ ...props, builder: 'select' });
 
-	const group = builder(name('group'), {
-		returned: () => {
-			return (groupId: string) => ({
-				role: 'group',
-				'aria-labelledby': groupId,
-			});
-		},
-	});
-
-	const groupLabel = builder(name('group-label'), {
-		returned: () => {
-			return (groupId: string) => ({
-				id: groupId,
-			});
-		},
-	});
-
 	const selectedLabel = derived(listbox.states.selected, ($selected) => {
 		if (Array.isArray($selected)) {
 			return $selected.map((o) => o.label).join(', ');
@@ -52,8 +22,6 @@ export function createSelect<
 
 		elements: {
 			...listbox.elements,
-			group,
-			groupLabel,
 		},
 		states: {
 			...listbox.states,
diff --git a/src/lib/builders/slider/create.ts b/src/lib/builders/slider/create.ts
index 427cb93e..5361bc38 100644
--- a/src/lib/builders/slider/create.ts
+++ b/src/lib/builders/slider/create.ts
@@ -2,6 +2,7 @@ import {
 	addEventListener,
 	addMeltEventListener,
 	builder,
+	builderArray,
 	createElHelpers,
 	disabledAttr,
 	effect,
@@ -109,27 +110,6 @@ export const createSlider = (props?: CreateSliderProps) => {
 		}
 	});
 
-	const ticks = derived([min, max, step], ([$min, $max, $step]) => {
-		const difference = $max - $min;
-
-		// min = 0, max = 8, step = 3:
-		// ----------------------------
-		// 0, 3, 6
-		// (8 - 0) / 3 = 2.666... = 3 ceiled
-		let count = Math.ceil(difference / $step);
-
-		// min = 0, max = 9, step = 3:
-		// ---------------------------
-		// 0, 3, 6, 9
-		// (9 - 0) / 3 = 3
-		// We need to add 1 because `difference` is a multiple of `step`.
-		if (difference % $step == 0) {
-			count++;
-		}
-
-		return count;
-	});
-
 	// Elements
 	const root = builder(name(), {
 		stores: [disabled, orientation, dir],
@@ -187,21 +167,17 @@ export const createSlider = (props?: CreateSliderProps) => {
 		},
 	});
 
-	const thumb = builder(name('thumb'), {
+	const thumbs = builderArray(name('thumb'), {
 		stores: [value, position, min, max, disabled, orientation, direction],
 		returned: ([$value, $position, $min, $max, $disabled, $orientation, $direction]) => {
-			let index = -1;
-
-			return () => {
-				index++;
-
+			return Array.from({ length: $value.length || 1 }, (_, i) => {
 				const currentThumb = get(currentThumbIndex);
 
 				if (currentThumb < $value.length) {
 					currentThumbIndex.update((prev) => prev + 1);
 				}
 
-				const thumbValue = $value[index];
+				const thumbValue = $value[i];
 				const thumbPosition = `${$position(thumbValue)}%`;
 
 				const style: StyleObject = {
@@ -243,7 +219,7 @@ export const createSlider = (props?: CreateSliderProps) => {
 					style: styleToString(style),
 					tabindex: $disabled ? -1 : 0,
 				} as const;
-			};
+			});
 		},
 		action: (node: HTMLElement): MeltActionReturn<SliderEvents['thumb']> => {
 			const unsub = addMeltEventListener(node, 'keydown', (event) => {
@@ -346,21 +322,35 @@ export const createSlider = (props?: CreateSliderProps) => {
 		},
 	});
 
-	const tick = builder(name('tick'), {
-		stores: [ticks, value, min, max, step, direction],
-		returned: ([$ticks, $value, $min, $max, $step, $direction]) => {
-			let index = -1;
-			return () => {
-				index++;
+	const ticks = builderArray(name('tick'), {
+		stores: [value, min, max, step, direction],
+		returned: ([$value, $min, $max, $step, $direction]) => {
+			const difference = $max - $min;
+
+			// min = 0, max = 8, step = 3:
+			// ----------------------------
+			// 0, 3, 6
+			// (8 - 0) / 3 = 2.666... = 3 ceiled
+			let count = Math.ceil(difference / $step);
+
+			// min = 0, max = 9, step = 3:
+			// ---------------------------
+			// 0, 3, 6, 9
+			// (9 - 0) / 3 = 3
+			// We need to add 1 because `difference` is a multiple of `step`.
+			if (difference % $step == 0) {
+				count++;
+			}
 
+			return Array.from({ length: count }, (_, i) => {
 				// The track is divided into sections of ratio `step / (max - min)`
-				const tickPosition = `${index * ($step / ($max - $min)) * 100}%`;
+				const tickPosition = `${i * ($step / ($max - $min)) * 100}%`;
 
 				// Offset each tick by -50% to center it, except the first and last ticks.
 				// The first tick is already positioned at the start of the slider.
 				// The last tick is offset by -100% to prevent it from being rendered outside.
-				const isFirst = index === 0;
-				const isLast = index === $ticks - 1;
+				const isFirst = i === 0;
+				const isLast = i === count - 1;
 				const offsetPercentage = isFirst ? 0 : isLast ? -100 : -50;
 
 				const style: StyleObject = {
@@ -390,7 +380,7 @@ export const createSlider = (props?: CreateSliderProps) => {
 					}
 				}
 
-				const tickValue = $min + index * $step;
+				const tickValue = $min + i * $step;
 				const bounded =
 					$value.length === 1
 						? tickValue <= $value[0]
@@ -401,7 +391,7 @@ export const createSlider = (props?: CreateSliderProps) => {
 					'data-value': tickValue,
 					style: styleToString(style),
 				};
-			};
+			});
 		},
 	});
 
@@ -552,13 +542,12 @@ export const createSlider = (props?: CreateSliderProps) => {
 	return {
 		elements: {
 			root,
-			thumb,
+			thumbs,
 			range,
-			tick,
+			ticks,
 		},
 		states: {
 			value,
-			ticks,
 		},
 		options,
 	};
diff --git a/src/lib/builders/tags-input/create.ts b/src/lib/builders/tags-input/create.ts
index c38c75d4..1c0b2970 100644
--- a/src/lib/builders/tags-input/create.ts
+++ b/src/lib/builders/tags-input/create.ts
@@ -324,13 +324,12 @@ export function createTagsInput(props?: CreateTagsInputProps) {
 					}
 				}),
 				addMeltEventListener(node, 'paste', async (e) => {
-					// Do nothing when there is nothing to paste
-					if (!e.clipboardData) return;
-					const pastedText = e.clipboardData.getData('text');
+					const pastedText = e.clipboardData?.getData('text');
 					if (!pastedText) return;
 
 					// Do nothing when addOnPaste is false
 					if (!get(addOnPaste)) return;
+					e.preventDefault();
 
 					// Update value with the pasted text or set invalid
 					if (isInputValid(pastedText) && (await addTag(pastedText))) {
diff --git a/src/lib/builders/toast/create.ts b/src/lib/builders/toast/create.ts
index c85fa038..5e3bd8f9 100644
--- a/src/lib/builders/toast/create.ts
+++ b/src/lib/builders/toast/create.ts
@@ -111,7 +111,7 @@ export function createToaster<T = object>(props?: CreateToasterProps) {
 					role: 'alert',
 					'aria-describedby': toast.ids.description,
 					'aria-labelledby': toast.ids.title,
-					'aria-live': toast.type === 'foreground' ? 'assertive' : 'polite',
+					'aria-live': toast.type === 'foreground' ? ('assertive' as const) : ('polite' as const),
 					tabindex: -1,
 				};
 			};
diff --git a/src/lib/builders/tooltip/create.ts b/src/lib/builders/tooltip/create.ts
index 5dc622a5..5d5aa3de 100644
--- a/src/lib/builders/tooltip/create.ts
+++ b/src/lib/builders/tooltip/create.ts
@@ -126,12 +126,17 @@ export function createTooltip(props?: CreateTooltipProps) {
 		}
 	}
 
+	const isVisible = derived([open, forceVisible], ([$open, $forceVisible]) => {
+		return $open || $forceVisible;
+	});
+
 	const trigger = builder(name('trigger'), {
-		stores: [ids.content, ids.trigger],
-		returned: ([$contentId, $triggerId]) => {
+		stores: [ids.content, ids.trigger, open],
+		returned: ([$contentId, $triggerId, $open]) => {
 			return {
 				'aria-describedby': $contentId,
 				id: $triggerId,
+				'data-state': $open ? 'open' : 'closed',
 			};
 		},
 		action: (node: HTMLElement): MeltActionReturn<TooltipEvents['trigger']> => {
@@ -183,13 +188,9 @@ export function createTooltip(props?: CreateTooltipProps) {
 		},
 	});
 
-	const isVisible = derived([open, forceVisible], ([$open, $forceVisible]) => {
-		return $open || $forceVisible;
-	});
-
 	const content = builder(name('content'), {
-		stores: [isVisible, portal, ids.content],
-		returned: ([$isVisible, $portal, $contentId]) => {
+		stores: [isVisible, open, portal, ids.content],
+		returned: ([$isVisible, $open, $portal, $contentId]) => {
 			return {
 				role: 'tooltip',
 				hidden: $isVisible ? undefined : true,
@@ -199,6 +200,7 @@ export function createTooltip(props?: CreateTooltipProps) {
 				}),
 				id: $contentId,
 				'data-portal': $portal ? '' : undefined,
+				'data-state': $open ? 'open' : 'closed',
 			};
 		},
 		action: (node: HTMLElement): MeltActionReturn<TooltipEvents['content']> => {
diff --git a/src/lib/index.ts b/src/lib/index.ts
index 104c9f8e..764aded3 100644
--- a/src/lib/index.ts
+++ b/src/lib/index.ts
@@ -1,3 +1,4 @@
 export * from './builders/index.js';
 export * from './shared/index.js';
 export { melt } from './internal/actions/index.js';
+export { createSync } from './sync.js';
diff --git a/src/lib/internal/actions/escape-keydown/action.ts b/src/lib/internal/actions/escape-keydown/action.ts
index 276b4fb6..4181b555 100644
--- a/src/lib/internal/actions/escape-keydown/action.ts
+++ b/src/lib/internal/actions/escape-keydown/action.ts
@@ -28,7 +28,6 @@ const documentEscapeKeyStore = readable<KeyboardEvent | undefined>(
 		// Adds a keydown event listener to the document, calling the keydown function when triggered.
 		const unsubscribe = addEventListener(document, 'keydown', keydown, {
 			passive: false,
-			capture: true,
 		});
 
 		// Returns a function to unsubscribe from the event listener and stop tracking keydown events.
diff --git a/src/lib/internal/helpers/builder.ts b/src/lib/internal/helpers/builder.ts
index a6b6a3dd..83425ee8 100644
--- a/src/lib/internal/helpers/builder.ts
+++ b/src/lib/internal/helpers/builder.ts
@@ -135,6 +135,60 @@ export type ExplicitBuilderReturn<
 	Name extends string
 > = BuilderStore<S, A, R, Name> & A;
 
+type BuilderArrayArgs<
+	S extends Stores,
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
+	A extends Action<any, any>,
+	R
+> = {
+	stores: S;
+	returned: (values: StoresValues<S>) => R[];
+	action?: A;
+};
+
+export function builderArray<
+	S extends Stores,
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
+	A extends Action<any, any>,
+	R,
+	Name extends string
+>(name: Name, args: BuilderArrayArgs<S, A, R>): ExplicitBuilderArrayReturn<A, R, Name> {
+	const { stores, returned, action } = args;
+
+	const { subscribe } = derived(stores, (values) =>
+		returned(values).map(
+			(value) =>
+				hiddenAction({
+					...value,
+					[`data-melt-${name}`]: '',
+					action: action ?? noop,
+				}) as BuilderArrayElement<A, R, Name>
+		)
+	);
+
+	const actionFn = (action ??
+		(() => {
+			/** noop */
+		})) as A & { subscribe: typeof subscribe };
+	actionFn.subscribe = subscribe;
+
+	return actionFn;
+}
+
+type BuilderArrayElement<
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
+	A extends Action<any, any>,
+	R,
+	Name extends string
+> = R & { [K in `data-melt-${Name}`]: '' } & { action: A };
+
+export type ExplicitBuilderArrayReturn<
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
+	A extends Action<any, any>,
+	R,
+	Name extends string
+> = Readable<BuilderArrayElement<A, R, Name>[]> & A;
+
 export function createElHelpers<Part extends string = string>(prefix: string) {
 	const name = (part?: Part) => (part ? `${prefix}-${part}` : prefix);
 	const attribute = (part?: Part) => `data-melt-${prefix}${part ? `-${part}` : ''}`;
diff --git a/src/lib/internal/helpers/date/calendar.ts b/src/lib/internal/helpers/date/calendar.ts
index 4d595534..4e056364 100644
--- a/src/lib/internal/helpers/date/calendar.ts
+++ b/src/lib/internal/helpers/date/calendar.ts
@@ -98,7 +98,7 @@ function createMonth(props: CreateMonthProps): Month<DateValue> {
 		}
 
 		const extraDaysArray = Array.from({ length: extraDays }, (_, i) => {
-			const incr = i;
+			const incr = i + 1;
 			return startFrom.add({ days: incr });
 		});
 		nextMonthDays.push(...extraDaysArray);
diff --git a/src/lib/shared/index.ts b/src/lib/shared/index.ts
index b9513b12..f9b788d1 100644
--- a/src/lib/shared/index.ts
+++ b/src/lib/shared/index.ts
@@ -4,8 +4,17 @@
  * use, they should be exported from this file to prevent conflicts
  * with other builders that may export the same types or functions.
  */
-import type { SegmentPart } from '$lib/builders/date-field/_internal/types.js';
+import type { SegmentPart, EditableSegmentPart } from '$lib/builders/date-field/_internal/types.js';
 import type { Granularity, Matcher, DateRange, Month } from '$lib/internal/helpers/date/index.js';
 import type { FocusProp, FocusTarget } from '$lib/internal/helpers/index.js';
 
-export type { Granularity, FocusProp, FocusTarget, Matcher, DateRange, Month, SegmentPart };
+export type {
+	Granularity,
+	FocusProp,
+	FocusTarget,
+	Matcher,
+	DateRange,
+	Month,
+	SegmentPart,
+	EditableSegmentPart,
+};
diff --git a/src/lib/sync.ts b/src/lib/sync.ts
new file mode 100644
index 00000000..cb7c5c4e
--- /dev/null
+++ b/src/lib/sync.ts
@@ -0,0 +1,58 @@
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-ignore
+import { effect } from '$lib/internal/helpers';
+import { dequal } from 'dequal';
+import type { Writable } from 'svelte/store';
+
+type WritableValue<T> = T extends Writable<infer V> ? V : never;
+
+/**
+ * Typed Object.keys
+ *
+ * @export
+ * @template {Record<string, unknown>} T
+ * @param {T} obj
+ * @returns {(keyof T)[]}
+ */
+function keys<T extends Record<string, unknown>>(obj: T): (keyof T)[] {
+	return Object.keys(obj);
+}
+
+export function createSync<Stores extends Record<string, Writable<unknown>>>(stores: Stores) {
+	let setters = {} as {
+		[K in keyof Stores]?: (value: WritableValue<Stores[K]>) => void;
+	};
+	keys(stores).forEach((key) => {
+		const store = stores[key];
+		effect(store, (value) => {
+			if (key in setters) {
+				// eslint-disable-next-line @typescript-eslint/no-explicit-any
+				setters[key]?.(value as any);
+			}
+		});
+	});
+
+	return keys(stores).reduce(
+		(acc, key) => {
+			type Value = WritableValue<Stores[typeof key]>;
+			return {
+				...acc,
+				[key]: function sync(value: Value, setter?: (value: Value) => void) {
+					stores[key].update((p) => {
+						if (dequal(p, value)) return p;
+						return value;
+					});
+					if (setter) {
+						setters = { ...setters, [key]: setter };
+					}
+				},
+			};
+		},
+		{} as {
+			[K in keyof Stores]: (
+				value: WritableValue<Stores[K]>,
+				setter?: (value: WritableValue<Stores[K]>) => void
+			) => void;
+		}
+	);
+}
diff --git a/src/pagefind/pagefind.js b/src/pagefind/pagefind.js
deleted file mode 100644
index f44a521c..00000000
--- a/src/pagefind/pagefind.js
+++ /dev/null
@@ -1,10 +0,0 @@
-// @ts-nocheck
-const pagefind_version="1.0.3";let wasm_bindgen;(function(){const __exports={};let script_src;if(typeof document==='undefined'){script_src=location.href}else{script_src=new URL("UNHANDLED",location.href).toString()}let wasm;let cachedUint8Memory0=null;function getUint8Memory0(){if(cachedUint8Memory0===null||cachedUint8Memory0.byteLength===0){cachedUint8Memory0=new Uint8Array(wasm.memory.buffer)}return cachedUint8Memory0}let WASM_VECTOR_LEN=0;function passArray8ToWasm0(arg,malloc){const ptr=malloc(arg.length*1);getUint8Memory0().set(arg,ptr/1);WASM_VECTOR_LEN=arg.length;return ptr}__exports.init_pagefind=function(metadata_bytes){const ptr0=passArray8ToWasm0(metadata_bytes,wasm.__wbindgen_malloc);const len0=WASM_VECTOR_LEN;const ret=wasm.init_pagefind(ptr0,len0);return ret};__exports.load_index_chunk=function(ptr,chunk_bytes){const ptr0=passArray8ToWasm0(chunk_bytes,wasm.__wbindgen_malloc);const len0=WASM_VECTOR_LEN;const ret=wasm.load_index_chunk(ptr,ptr0,len0);return ret};__exports.load_filter_chunk=function(ptr,chunk_bytes){const ptr0=passArray8ToWasm0(chunk_bytes,wasm.__wbindgen_malloc);const len0=WASM_VECTOR_LEN;const ret=wasm.load_filter_chunk(ptr,ptr0,len0);return ret};const cachedTextEncoder=new TextEncoder('utf-8');const encodeString=(typeof cachedTextEncoder.encodeInto==='function'?function(arg,view){return cachedTextEncoder.encodeInto(arg,view)}:function(arg,view){const buf=cachedTextEncoder.encode(arg);view.set(buf);return{read:arg.length,written:buf.length}});function passStringToWasm0(arg,malloc,realloc){if(realloc===undefined){const buf=cachedTextEncoder.encode(arg);const ptr=malloc(buf.length);getUint8Memory0().subarray(ptr,ptr+buf.length).set(buf);WASM_VECTOR_LEN=buf.length;return ptr}let len=arg.length;let ptr=malloc(len);const mem=getUint8Memory0();let offset=0;for(;offset<len;offset++){const code=arg.charCodeAt(offset);if(code>0x7F)break;mem[ptr+offset]=code}if(offset!==len){if(offset!==0){arg=arg.slice(offset)}ptr=realloc(ptr,len,len=offset+arg.length*3);const view=getUint8Memory0().subarray(ptr+offset,ptr+len);const ret=encodeString(arg,view);offset+=ret.written}WASM_VECTOR_LEN=offset;return ptr}__exports.add_synthetic_filter=function(ptr,filter){const ptr0=passStringToWasm0(filter,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;const ret=wasm.add_synthetic_filter(ptr,ptr0,len0);return ret};let cachedInt32Memory0=null;function getInt32Memory0(){if(cachedInt32Memory0===null||cachedInt32Memory0.byteLength===0){cachedInt32Memory0=new Int32Array(wasm.memory.buffer)}return cachedInt32Memory0}const cachedTextDecoder=new TextDecoder('utf-8',{ignoreBOM:true,fatal:true});cachedTextDecoder.decode();function getStringFromWasm0(ptr,len){return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr,ptr+len))}__exports.request_indexes=function(ptr,query){try{const retptr=wasm.__wbindgen_add_to_stack_pointer(-16);const ptr0=passStringToWasm0(query,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;wasm.request_indexes(retptr,ptr,ptr0,len0);var r0=getInt32Memory0()[retptr/4+0];var r1=getInt32Memory0()[retptr/4+1];return getStringFromWasm0(r0,r1)}finally{wasm.__wbindgen_add_to_stack_pointer(16);wasm.__wbindgen_free(r0,r1)}};__exports.request_filter_indexes=function(ptr,filters){try{const retptr=wasm.__wbindgen_add_to_stack_pointer(-16);const ptr0=passStringToWasm0(filters,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;wasm.request_filter_indexes(retptr,ptr,ptr0,len0);var r0=getInt32Memory0()[retptr/4+0];var r1=getInt32Memory0()[retptr/4+1];return getStringFromWasm0(r0,r1)}finally{wasm.__wbindgen_add_to_stack_pointer(16);wasm.__wbindgen_free(r0,r1)}};__exports.request_all_filter_indexes=function(ptr){try{const retptr=wasm.__wbindgen_add_to_stack_pointer(-16);wasm.request_all_filter_indexes(retptr,ptr);var r0=getInt32Memory0()[retptr/4+0];var r1=getInt32Memory0()[retptr/4+1];return getStringFromWasm0(r0,r1)}finally{wasm.__wbindgen_add_to_stack_pointer(16);wasm.__wbindgen_free(r0,r1)}};__exports.filters=function(ptr){try{const retptr=wasm.__wbindgen_add_to_stack_pointer(-16);wasm.filters(retptr,ptr);var r0=getInt32Memory0()[retptr/4+0];var r1=getInt32Memory0()[retptr/4+1];return getStringFromWasm0(r0,r1)}finally{wasm.__wbindgen_add_to_stack_pointer(16);wasm.__wbindgen_free(r0,r1)}};__exports.search=function(ptr,query,filter,sort,exact){try{const retptr=wasm.__wbindgen_add_to_stack_pointer(-16);const ptr0=passStringToWasm0(query,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;const ptr1=passStringToWasm0(filter,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len1=WASM_VECTOR_LEN;const ptr2=passStringToWasm0(sort,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len2=WASM_VECTOR_LEN;wasm.search(retptr,ptr,ptr0,len0,ptr1,len1,ptr2,len2,exact);var r0=getInt32Memory0()[retptr/4+0];var r1=getInt32Memory0()[retptr/4+1];return getStringFromWasm0(r0,r1)}finally{wasm.__wbindgen_add_to_stack_pointer(16);wasm.__wbindgen_free(r0,r1)}};async function load(module,imports){if(typeof Response==='function'&&module instanceof Response){if(typeof WebAssembly.instantiateStreaming==='function'){try{return await WebAssembly.instantiateStreaming(module,imports)}catch(e){if(module.headers.get('Content-Type')!='application/wasm'){console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",e)}else{throw e}}}const bytes=await module.arrayBuffer();return await WebAssembly.instantiate(bytes,imports)}else{const instance=await WebAssembly.instantiate(module,imports);if(instance instanceof WebAssembly.Instance){return{instance,module}}else{return instance}}}function getImports(){const imports={};imports.wbg={};return imports}function initMemory(imports,maybe_memory){}function finalizeInit(instance,module){wasm=instance.exports;init.__wbindgen_wasm_module=module;cachedInt32Memory0=null;cachedUint8Memory0=null;return wasm}function initSync(module){const imports=getImports();initMemory(imports);if(!(module instanceof WebAssembly.Module)){module=new WebAssembly.Module(module)}const instance=new WebAssembly.Instance(module,imports);return finalizeInit(instance,module)}async function init(input){if(typeof input==='undefined'){input=script_src.replace(/\.js$/,'_bg.wasm')}const imports=getImports();if(typeof input==='string'||(typeof Request==='function'&&input instanceof Request)||(typeof URL==='function'&&input instanceof URL)){input=fetch(input)}initMemory(imports);const{instance,module}=await load(await input,imports);return finalizeInit(instance,module)}wasm_bindgen=Object.assign(init,{initSync},__exports)})();var u8=Uint8Array;var u16=Uint16Array;var u32=Uint32Array;var fleb=new u8([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]);var fdeb=new u8([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]);var clim=new u8([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]);var freb=function(eb,start){var b=new u16(31);for(var i2=0;i2<31;++i2){b[i2]=start+=1<<eb[i2-1]}var r=new u32(b[30]);for(var i2=1;i2<30;++i2){for(var j=b[i2];j<b[i2+1];++j){r[j]=j-b[i2]<<5|i2}}return[b,r]};var _a=freb(fleb,2);var fl=_a[0];var revfl=_a[1];fl[28]=258,revfl[258]=28;var _b=freb(fdeb,0);var fd=_b[0];var revfd=_b[1];var rev=new u16(32768);for(i=0;i<32768;++i){x=(i&43690)>>>1|(i&21845)<<1;x=(x&52428)>>>2|(x&13107)<<2;x=(x&61680)>>>4|(x&3855)<<4;rev[i]=((x&65280)>>>8|(x&255)<<8)>>>1}var x;var i;var hMap=function(cd,mb,r){var s=cd.length;var i2=0;var l=new u16(mb);for(;i2<s;++i2){if(cd[i2])++l[cd[i2]-1]}var le=new u16(mb);for(i2=0;i2<mb;++i2){le[i2]=le[i2-1]+l[i2-1]<<1}var co;if(r){co=new u16(1<<mb);var rvb=15-mb;for(i2=0;i2<s;++i2){if(cd[i2]){var sv=i2<<4|cd[i2];var r_1=mb-cd[i2];var v=le[cd[i2]-1]++<<r_1;for(var m=v|(1<<r_1)-1;v<=m;++v){co[rev[v]>>>rvb]=sv}}}}else{co=new u16(s);for(i2=0;i2<s;++i2){if(cd[i2]){co[i2]=rev[le[cd[i2]-1]++]>>>15-cd[i2]}}}return co};var flt=new u8(288);for(i=0;i<144;++i)flt[i]=8;var i;for(i=144;i<256;++i)flt[i]=9;var i;for(i=256;i<280;++i)flt[i]=7;var i;for(i=280;i<288;++i)flt[i]=8;var i;var fdt=new u8(32);for(i=0;i<32;++i)fdt[i]=5;var i;var flrm=hMap(flt,9,1);var fdrm=hMap(fdt,5,1);var max=function(a){var m=a[0];for(var i2=1;i2<a.length;++i2){if(a[i2]>m)m=a[i2]}return m};var bits=function(d,p,m){var o=p/8|0;return(d[o]|d[o+1]<<8)>>(p&7)&m};var bits16=function(d,p){var o=p/8|0;return(d[o]|d[o+1]<<8|d[o+2]<<16)>>(p&7)};var shft=function(p){return(p+7)/8|0};var slc=function(v,s,e){if(s==null||s<0)s=0;if(e==null||e>v.length)e=v.length;var n=new(v.BYTES_PER_ELEMENT==2?u16:v.BYTES_PER_ELEMENT==4?u32:u8)(e-s);n.set(v.subarray(s,e));return n};var ec=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"];var err=function(ind,msg,nt){var e=new Error(msg||ec[ind]);e.code=ind;if(Error.captureStackTrace)Error.captureStackTrace(e,err);if(!nt)throw e;return e};var inflt=function(dat,buf,st){var sl=dat.length;if(!sl||st&&st.f&&!st.l)return buf||new u8(0);var noBuf=!buf||st;var noSt=!st||st.i;if(!st)st={};if(!buf)buf=new u8(sl*3);var cbuf=function(l2){var bl=buf.length;if(l2>bl){var nbuf=new u8(Math.max(bl*2,l2));nbuf.set(buf);buf=nbuf}};var final=st.f||0,pos=st.p||0,bt=st.b||0,lm=st.l,dm=st.d,lbt=st.m,dbt=st.n;var tbts=sl*8;do{if(!lm){final=bits(dat,pos,1);var type=bits(dat,pos+1,3);pos+=3;if(!type){var s=shft(pos)+4,l=dat[s-4]|dat[s-3]<<8,t=s+l;if(t>sl){if(noSt)err(0);break}if(noBuf)cbuf(bt+l);buf.set(dat.subarray(s,t),bt);st.b=bt+=l,st.p=pos=t*8,st.f=final;continue}else if(type==1)lm=flrm,dm=fdrm,lbt=9,dbt=5;else if(type==2){var hLit=bits(dat,pos,31)+257,hcLen=bits(dat,pos+10,15)+4;var tl=hLit+bits(dat,pos+5,31)+1;pos+=14;var ldt=new u8(tl);var clt=new u8(19);for(var i2=0;i2<hcLen;++i2){clt[clim[i2]]=bits(dat,pos+i2*3,7)}pos+=hcLen*3;var clb=max(clt),clbmsk=(1<<clb)-1;var clm=hMap(clt,clb,1);for(var i2=0;i2<tl;){var r=clm[bits(dat,pos,clbmsk)];pos+=r&15;var s=r>>>4;if(s<16){ldt[i2++]=s}else{var c=0,n=0;if(s==16)n=3+bits(dat,pos,3),pos+=2,c=ldt[i2-1];else if(s==17)n=3+bits(dat,pos,7),pos+=3;else if(s==18)n=11+bits(dat,pos,127),pos+=7;while(n--)ldt[i2++]=c}}var lt=ldt.subarray(0,hLit),dt=ldt.subarray(hLit);lbt=max(lt);dbt=max(dt);lm=hMap(lt,lbt,1);dm=hMap(dt,dbt,1)}else err(1);if(pos>tbts){if(noSt)err(0);break}}if(noBuf)cbuf(bt+131072);var lms=(1<<lbt)-1,dms=(1<<dbt)-1;var lpos=pos;for(;;lpos=pos){var c=lm[bits16(dat,pos)&lms],sym=c>>>4;pos+=c&15;if(pos>tbts){if(noSt)err(0);break}if(!c)err(2);if(sym<256)buf[bt++]=sym;else if(sym==256){lpos=pos,lm=null;break}else{var add=sym-254;if(sym>264){var i2=sym-257,b=fleb[i2];add=bits(dat,pos,(1<<b)-1)+fl[i2];pos+=b}var d=dm[bits16(dat,pos)&dms],dsym=d>>>4;if(!d)err(3);pos+=d&15;var dt=fd[dsym];if(dsym>3){var b=fdeb[dsym];dt+=bits16(dat,pos)&(1<<b)-1,pos+=b}if(pos>tbts){if(noSt)err(0);break}if(noBuf)cbuf(bt+131072);var end=bt+add;for(;bt<end;bt+=4){buf[bt]=buf[bt-dt];buf[bt+1]=buf[bt+1-dt];buf[bt+2]=buf[bt+2-dt];buf[bt+3]=buf[bt+3-dt]}bt=end}}st.l=lm,st.p=lpos,st.b=bt,st.f=final;if(lm)final=1,st.m=lbt,st.d=dm,st.n=dbt}while(!final);return bt==buf.length?buf:slc(buf,0,bt)};var et=new u8(0);var gzs=function(d){if(d[0]!=31||d[1]!=139||d[2]!=8)err(6,"invalid gzip data");var flg=d[3];var st=10;if(flg&4)st+=d[10]|(d[11]<<8)+2;for(var zs=(flg>>3&1)+(flg>>4&1);zs>0;zs-=!d[st++]);return st+(flg&2)};var gzl=function(d){var l=d.length;return(d[l-4]|d[l-3]<<8|d[l-2]<<16|d[l-1]<<24)>>>0};function gunzipSync(data,out){return inflt(data.subarray(gzs(data),-8),out||new u8(gzl(data)))}var td=typeof TextDecoder!="undefined"&&new TextDecoder();var tds=0;try{td.decode(et,{stream:true});tds=1}catch(e){}var gz_default=gunzipSync;var calculate_excerpt_region=(word_positions,excerpt_length)=>{if(word_positions.length===0){return 0}let words=[];for(const word of word_positions){words[word.location]=words[word.location]||0;words[word.location]+=word.balanced_score}if(words.length<=excerpt_length){return 0}let densest=words.slice(0,excerpt_length).reduce((partialSum,a)=>partialSum+a,0);let working_sum=densest;let densest_at=[0];for(let i2=0;i2<words.length;i2++){const boundary=i2+excerpt_length;working_sum+=(words[boundary]??0)-(words[i2]??0);if(working_sum>densest){densest=working_sum;densest_at=[i2]}else if(working_sum===densest&&densest_at[densest_at.length-1]===i2-1){densest_at.push(i2)}}let midpoint=densest_at[Math.floor(densest_at.length/2)];return midpoint};var build_excerpt=(content,start,length,locations,not_before,not_from)=>{let is_zws_delimited=content.includes("\u200B");let fragment_words=[];if(is_zws_delimited){fragment_words=content.split("\u200B")}else{fragment_words=content.split(/[\r\n\s]+/g)}for(let word of locations){if(fragment_words[word]?.startsWith(`<mark>`)){continue}fragment_words[word]=`<mark>${fragment_words[word]}</mark>`}let endcap=not_from??fragment_words.length;let startcap=not_before??0;if(endcap-startcap<length){length=endcap-startcap}if(start+length>endcap){start=endcap-length}if(start<startcap){start=startcap}return fragment_words.slice(start,start+length).join(is_zws_delimited?"":" ").trim()};var calculate_sub_results=(fragment,desired_excerpt_length)=>{const anchors=fragment.anchors.filter((a)=>/h\d/i.test(a.element)&&a.text?.length&&/\w/.test(a.text)).sort((a,b)=>a.location-b.location);const results=[];let current_anchor_position=0;let current_anchor={title:fragment.meta["title"],url:fragment.url,weighted_locations:[],locations:[],excerpt:""};const add_result=(end_range)=>{if(current_anchor.locations.length){const relative_weighted_locations=current_anchor.weighted_locations.map((l)=>{return{weight:l.weight,balanced_score:l.balanced_score,location:l.location-current_anchor_position}});const excerpt_start=calculate_excerpt_region(relative_weighted_locations,desired_excerpt_length)+current_anchor_position;const excerpt_length=end_range?Math.min(end_range-excerpt_start,desired_excerpt_length):desired_excerpt_length;current_anchor.excerpt=build_excerpt(fragment.raw_content??"",excerpt_start,excerpt_length,current_anchor.locations,current_anchor_position,end_range);results.push(current_anchor)}};for(let word of fragment.weighted_locations){if(!anchors.length||word.location<anchors[0].location){current_anchor.weighted_locations.push(word);current_anchor.locations.push(word.location)}else{let next_anchor=anchors.shift();add_result(next_anchor.location);while(anchors.length&&word.location>=anchors[0].location){next_anchor=anchors.shift()}let anchored_url=fragment.url;try{const url_is_fq=/^((https?:)?\/\/)/.test(anchored_url);if(url_is_fq){let fq_url=new URL(anchored_url);fq_url.hash=next_anchor.id;anchored_url=fq_url.toString()}else{if(!/^\//.test(anchored_url)){anchored_url=`/${anchored_url}`}let fq_url=new URL(`https://example.com${anchored_url}`);fq_url.hash=next_anchor.id;anchored_url=fq_url.toString().replace(/^https:\/\/example.com/,"")}}catch(e){console.error(`Pagefind: Couldn't process ${anchored_url} for a search result`)}current_anchor_position=next_anchor.location;current_anchor={title:next_anchor.text,url:anchored_url,anchor:next_anchor,weighted_locations:[word],locations:[word.location],excerpt:""}}}add_result(anchors[0]?.location);return results};var asyncSleep=async(ms=100)=>{return new Promise((r)=>setTimeout(r,ms))};var PagefindInstance=class{constructor(opts={}){this.version=pagefind_version;this.backend=wasm_bindgen;this.decoder=new TextDecoder("utf-8");this.wasm=null;this.basePath=opts.basePath||"/pagefind/";this.primary=opts.primary||false;if(this.primary&&!opts.basePath){this.initPrimary()}if(/[^\/]$/.test(this.basePath)){this.basePath=`${this.basePath}/`}if(window?.location?.origin&&this.basePath.startsWith(window.location.origin)){this.basePath=this.basePath.replace(window.location.origin,"")}this.baseUrl=opts.baseUrl||this.defaultBaseUrl();if(!/^(\/|https?:\/\/)/.test(this.baseUrl)){this.baseUrl=`/${this.baseUrl}`}this.indexWeight=opts.indexWeight??1;this.excerptLength=opts.excerptLength??30;this.mergeFilter=opts.mergeFilter??{};this.loaded_chunks={};this.loaded_filters={};this.loaded_fragments={};this.raw_ptr=null;this.searchMeta=null;this.languages=null}initPrimary(){let derivedBasePath=import.meta.url.match(/^(.*\/)pagefind.js.*$/)?.[1];if(derivedBasePath){this.basePath=derivedBasePath}else{console.warn(["Pagefind couldn't determine the base of the bundle from the import path. Falling back to the default.","Set a basePath option when initialising Pagefind to ignore this message."].join("\n"))}}defaultBaseUrl(){let default_base=this.basePath.match(/^(.*\/)_?pagefind/)?.[1];return default_base||"/"}async options(options2){const opts=["basePath","baseUrl","indexWeight","excerptLength","mergeFilter"];for(const[k,v]of Object.entries(options2)){if(k==="mergeFilter"){let filters2=this.stringifyFilters(v);let ptr=await this.getPtr();this.raw_ptr=this.backend.add_synthetic_filter(ptr,filters2)}else if(opts.includes(k)){if(k==="basePath"&&typeof v==="string")this.basePath=v;if(k==="baseUrl"&&typeof v==="string")this.baseUrl=v;if(k==="indexWeight"&&typeof v==="number")this.indexWeight=v;if(k==="excerptLength"&&typeof v==="number")this.excerptLength=v;if(k==="mergeFilter"&&typeof v==="object")this.mergeFilter=v}else{console.warn(`Unknown Pagefind option ${k}. Allowed options: [${opts.join(", ")}]`)}}}decompress(data,file="unknown file"){if(this.decoder.decode(data.slice(0,12))==="pagefind_dcd"){return data.slice(12)}data=gz_default(data);if(this.decoder.decode(data.slice(0,12))!=="pagefind_dcd"){console.error(`Decompressing ${file} appears to have failed: Missing signature`);return data}return data.slice(12)}async init(language,opts){await this.loadEntry();let index=this.findIndex(language);let lang_wasm=index.wasm?index.wasm:"unknown";let resources=[this.loadMeta(index.hash)];if(opts.load_wasm===true){resources.push(this.loadWasm(lang_wasm))}await Promise.all(resources);this.raw_ptr=this.backend.init_pagefind(new Uint8Array(this.searchMeta));if(Object.keys(this.mergeFilter)?.length){let filters2=this.stringifyFilters(this.mergeFilter);let ptr=await this.getPtr();this.raw_ptr=this.backend.add_synthetic_filter(ptr,filters2)}}async loadEntry(){try{let entry_response=await fetch(`${this.basePath}pagefind-entry.json?ts=${Date.now()}`);let entry_json=await entry_response.json();this.languages=entry_json.languages;if(entry_json.version!==this.version){if(this.primary){console.warn(["Pagefind JS version doesn't match the version in your search index.",`Pagefind JS: ${this.version}. Pagefind index: ${entry_json.version}`,"If you upgraded Pagefind recently, you likely have a cached pagefind.js file.","If you encounter any search errors, try clearing your cache."].join("\n"))}else{console.warn(["Merging a Pagefind index from a different version than the main Pagefind instance.",`Main Pagefind JS: ${this.version}. Merged index (${this.basePath}): ${entry_json.version}`,"If you encounter any search errors, make sure that both sites are running the same version of Pagefind."].join("\n"))}}}catch(e){console.error(`Failed to load Pagefind metadata:
-${e?.toString()}`);throw new Error("Failed to load Pagefind metadata")}}findIndex(language){if(this.languages){let index=this.languages[language];if(index)return index;index=this.languages[language.split("-")[0]];if(index)return index;let topLang=Object.values(this.languages).sort((a,b)=>b.page_count-a.page_count);if(topLang[0])return topLang[0]}throw new Error("Pagefind Error: No language indexes found.")}async loadMeta(index){try{let compressed_resp=await fetch(`${this.basePath}pagefind.${index}.pf_meta`);let compressed_meta=await compressed_resp.arrayBuffer();this.searchMeta=this.decompress(new Uint8Array(compressed_meta),"Pagefind metadata")}catch(e){console.error(`Failed to load the meta index:
-${e?.toString()}`)}}async loadWasm(language){try{const wasm_url=`${this.basePath}wasm.${language}.pagefind`;let compressed_resp=await fetch(wasm_url);let compressed_wasm=await compressed_resp.arrayBuffer();const final_wasm=this.decompress(new Uint8Array(compressed_wasm),"Pagefind WebAssembly");if(!final_wasm){throw new Error("No WASM after decompression")}this.wasm=await this.backend(final_wasm)}catch(e){console.error(`Failed to load the Pagefind WASM:
-${e?.toString()}`);throw new Error(`Failed to load the Pagefind WASM:
-${e?.toString()}`)}}async _loadGenericChunk(url,method){try{let compressed_resp=await fetch(url);let compressed_chunk=await compressed_resp.arrayBuffer();let chunk=this.decompress(new Uint8Array(compressed_chunk),url);let ptr=await this.getPtr();this.raw_ptr=this.backend[method](ptr,chunk)}catch(e){console.error(`Failed to load the index chunk ${url}:
-${e?.toString()}`)}}async loadChunk(hash){if(!this.loaded_chunks[hash]){const url=`${this.basePath}index/${hash}.pf_index`;this.loaded_chunks[hash]=this._loadGenericChunk(url,"load_index_chunk")}return await this.loaded_chunks[hash]}async loadFilterChunk(hash){if(!this.loaded_filters[hash]){const url=`${this.basePath}filter/${hash}.pf_filter`;this.loaded_filters[hash]=this._loadGenericChunk(url,"load_filter_chunk")}return await this.loaded_filters[hash]}async _loadFragment(hash){let compressed_resp=await fetch(`${this.basePath}fragment/${hash}.pf_fragment`);let compressed_fragment=await compressed_resp.arrayBuffer();let fragment=this.decompress(new Uint8Array(compressed_fragment),`Fragment ${hash}`);return JSON.parse(new TextDecoder().decode(fragment))}async loadFragment(hash,weighted_locations=[]){if(!this.loaded_fragments[hash]){this.loaded_fragments[hash]=this._loadFragment(hash)}let fragment=await this.loaded_fragments[hash];fragment.weighted_locations=weighted_locations;fragment.locations=weighted_locations.map((l)=>l.location);if(!fragment.raw_content){fragment.raw_content=fragment.content.replace(/</g,"&lt;").replace(/>/g,"&gt;");fragment.content=fragment.content.replace(/\u200B/g,"")}if(!fragment.raw_url){fragment.raw_url=fragment.url;fragment.url=this.fullUrl(fragment.raw_url)}const excerpt_start=calculate_excerpt_region(weighted_locations,this.excerptLength);fragment.excerpt=build_excerpt(fragment.raw_content,excerpt_start,this.excerptLength,fragment.locations);fragment.sub_results=calculate_sub_results(fragment,this.excerptLength);return fragment}fullUrl(raw){return`${this.baseUrl}/${raw}`.replace(/\/+/g,"/").replace(/^(https?:\/)/,"$1/")}async getPtr(){while(this.raw_ptr===null){await asyncSleep(50)}if(!this.raw_ptr){console.error("Pagefind: WASM Error (No pointer)");throw new Error("Pagefind: WASM Error (No pointer)")}return this.raw_ptr}parseFilters(str){let output={};if(!str)return output;for(const block of str.split("__PF_FILTER_DELIM__")){let[filter,values]=block.split(/:(.*)$/);output[filter]={};if(values){for(const valueBlock of values.split("__PF_VALUE_DELIM__")){if(valueBlock){let extract=valueBlock.match(/^(.*):(\d+)$/);if(extract){let[,value,count]=extract;output[filter][value]=parseInt(count)??count}}}}}return output}stringifyFilters(obj={}){return JSON.stringify(obj)}stringifySorts(obj={}){let sorts=Object.entries(obj);for(let[sort,direction]of sorts){if(sorts.length>1){console.warn(`Pagefind was provided multiple sort options in this search, but can only operate on one. Using the ${sort} sort.`)}if(direction!=="asc"&&direction!=="desc"){console.warn(`Pagefind was provided a sort with unknown direction ${direction}. Supported: [asc, desc]`)}return`${sort}:${direction}`}return``}async filters(){let ptr=await this.getPtr();let filters2=this.backend.request_all_filter_indexes(ptr);let filter_chunks=filters2.split(" ").filter((v)=>v).map((chunk)=>this.loadFilterChunk(chunk));await Promise.all([...filter_chunks]);ptr=await this.getPtr();let results=this.backend.filters(ptr);return this.parseFilters(results)}async preload(term,options2={}){options2.preload=true;await this.search(term,options2)}async search(term,options2={}){options2={verbose:false,filters:{},sort:{},...options2};const log=(str)=>{if(options2.verbose)console.log(str)};log(`Starting search on ${this.basePath}`);let start=Date.now();let ptr=await this.getPtr();let filter_only=term===null;term=term??"";let exact_search=/^\s*".+"\s*$/.test(term);if(exact_search){log(`Running an exact search`)}term=term.toLowerCase().trim().replace(/[\.`~!@#\$%\^&\*\(\)\{\}\[\]\\\|:;'",<>\/\?\-]/g,"").replace(/\s{2,}/g," ").trim();log(`Normalized search term to ${term}`);if(!term?.length&&!filter_only){return{results:[],unfilteredResultCount:0,filters:{},totalFilters:{},timings:{preload:Date.now()-start,search:Date.now()-start,total:Date.now()-start}}}let sort_list=this.stringifySorts(options2.sort);log(`Stringified sort to ${sort_list}`);const filter_list=this.stringifyFilters(options2.filters);log(`Stringified filters to ${filter_list}`);let index_resp=this.backend.request_indexes(ptr,term);let filter_resp=this.backend.request_filter_indexes(ptr,filter_list);let chunks=index_resp.split(" ").filter((v)=>v).map((chunk)=>this.loadChunk(chunk));let filter_chunks=filter_resp.split(" ").filter((v)=>v).map((chunk)=>this.loadFilterChunk(chunk));await Promise.all([...chunks,...filter_chunks]);log(`Loaded necessary chunks to run search`);if(options2.preload){log(`Preload \u2014 bailing out of search operation now.`);return null}ptr=await this.getPtr();let searchStart=Date.now();let result=this.backend.search(ptr,term,filter_list,sort_list,exact_search);log(`Got the raw search result: ${result}`);let[unfilteredResultCount,all_results,filters2,totalFilters]=result.split(/:([^:]*):(.*)__PF_UNFILTERED_DELIM__(.*)$/);let filterObj=this.parseFilters(filters2);let totalFilterObj=this.parseFilters(totalFilters);log(`Remaining filters: ${JSON.stringify(result)}`);let results=all_results.length?all_results.split(" "):[];let resultsInterface=results.map((result2)=>{let[hash,score,all_locations]=result2.split("@");log(`Processing result: 
-  hash:${hash}
-  score:${score}
-  locations:${all_locations}`);let weighted_locations=all_locations.length?all_locations.split(",").map((l)=>{let[weight,balanced_score,location]=l.split(">");return{weight:parseInt(weight)/24,balanced_score:parseFloat(balanced_score),location:parseInt(location)}}):[];let locations=weighted_locations.map((l)=>l.location);return{id:hash,score:parseFloat(score)*this.indexWeight,words:locations,data:async()=>await this.loadFragment(hash,weighted_locations)}});const searchTime=Date.now()-searchStart;const realTime=Date.now()-start;log(`Found ${results.length} result${results.length == 1 ? "" : "s"} for "${term}" in ${Date.now() - searchStart}ms (${Date.now() - start}ms realtime)`);return{results:resultsInterface,unfilteredResultCount:parseInt(unfilteredResultCount),filters:filterObj,totalFilters:totalFilterObj,timings:{preload:realTime-searchTime,search:searchTime,total:realTime}}}};var Pagefind=class{constructor(options2={}){this.backend=wasm_bindgen;this.primaryLanguage="unknown";this.searchID=0;this.primary=new PagefindInstance({...options2,primary:true});this.instances=[this.primary];this.init(options2?.language)}async options(options2){await this.primary.options(options2)}async init(overrideLanguage){if(document?.querySelector){const langCode=document.querySelector("html")?.getAttribute("lang")||"unknown";this.primaryLanguage=langCode.toLocaleLowerCase()}await this.primary.init(overrideLanguage?overrideLanguage:this.primaryLanguage,{load_wasm:true})}async mergeIndex(indexPath,options2={}){if(this.primary.basePath.startsWith(indexPath)){console.warn(`Skipping mergeIndex ${indexPath} that appears to be the same as the primary index (${this.primary.basePath})`);return}let newInstance=new PagefindInstance({primary:false,basePath:indexPath});this.instances.push(newInstance);while(this.primary.wasm===null){await asyncSleep(50)}await newInstance.init(options2.language||this.primaryLanguage,{load_wasm:false});delete options2["language"];await newInstance.options(options2)}mergeFilters(filters2){const merged={};for(const searchFilter of filters2){for(const[filterKey,values]of Object.entries(searchFilter)){if(!merged[filterKey]){merged[filterKey]=values;continue}else{const filter=merged[filterKey];for(const[valueKey,count]of Object.entries(values)){filter[valueKey]=(filter[valueKey]||0)+count}}}}return merged}async filters(){let filters2=await Promise.all(this.instances.map((i2)=>i2.filters()));return this.mergeFilters(filters2)}async preload(term,options2={}){await Promise.all(this.instances.map((i2)=>i2.preload(term,options2)))}async debouncedSearch(term,options2,debounceTimeoutMs){const thisSearchID=++this.searchID;this.preload(term,options2);await asyncSleep(debounceTimeoutMs);if(thisSearchID!==this.searchID){return null}const searchResult=await this.search(term,options2);if(thisSearchID!==this.searchID){return null}return searchResult}async search(term,options2={}){let search2=await Promise.all(this.instances.map((i2)=>i2.search(term,options2)));const filters2=this.mergeFilters(search2.map((s)=>s.filters));const totalFilters=this.mergeFilters(search2.map((s)=>s.totalFilters));const results=search2.map((s)=>s.results).flat().sort((a,b)=>b.score-a.score);const timings=search2.map((s)=>s.timings);const unfilteredResultCount=search2.reduce((sum,s)=>sum+s.unfilteredResultCount,0);return{results,unfilteredResultCount,filters:filters2,totalFilters,timings}}};var pagefind=void 0;var initial_options=void 0;var init_pagefind=()=>{if(!pagefind){pagefind=new Pagefind(initial_options??{})}};var options=async(new_options)=>{if(pagefind){await pagefind.options(new_options)}else{initial_options=new_options}};var init=async()=>{init_pagefind()};var mergeIndex=async(indexPath,options2)=>{init_pagefind();return await pagefind.mergeIndex(indexPath,options2)};var search=async(term,options2)=>{init_pagefind();return await pagefind.search(term,options2)};var debouncedSearch=async(term,options2,debounceTimeoutMs=300)=>{init_pagefind();return await pagefind.debouncedSearch(term,options2,debounceTimeoutMs)};var preload=async(term,options2)=>{init_pagefind();return await pagefind.preload(term,options2)};var filters=async()=>{init_pagefind();return await pagefind.filters()};export{debouncedSearch,filters,init,mergeIndex,options,preload,search}
\ No newline at end of file
diff --git a/src/routes/(landing-ui)/search.svelte b/src/routes/(landing-ui)/search.svelte
index fa1c6930..774d61c7 100644
--- a/src/routes/(landing-ui)/search.svelte
+++ b/src/routes/(landing-ui)/search.svelte
@@ -20,8 +20,21 @@
 	let pagefind: Pagefind | null = null;
 	let results: AwaitedResult[] = [];
 
+	async function getPagefind() {
+		const res = await fetch('/pagefind/pagefind.js');
+		if (!res.ok) {
+			return null;
+		}
+		const text = await res.text();
+		const blob = new Blob([text], { type: 'application/javascript' });
+		const url = URL.createObjectURL(blob);
+
+		return (await import(/* @vite-ignore */ url)) as Pagefind;
+	}
+
 	onMount(async () => {
-		pagefind = await import('../../pagefind/pagefind.js');
+		pagefind = await getPagefind();
+		if (!pagefind) return;
 
 		await pagefind.init();
 		const promisedResults = (await pagefind.search('')).results;
diff --git a/src/routes/(landing-ui)/slider.svelte b/src/routes/(landing-ui)/slider.svelte
index c1c08f4b..1e2f07a6 100644
--- a/src/routes/(landing-ui)/slider.svelte
+++ b/src/routes/(landing-ui)/slider.svelte
@@ -3,21 +3,22 @@
 	import { createSlider, melt } from '$lib/index.js';
 
 	const {
-		elements: { root, range, thumb },
+		elements: { root, range, thumbs },
 	} = createSlider({
 		defaultValue: [60],
 		max: 100,
 	});
+
 	let className = '';
 	export { className as class };
 </script>
 
-<span use:melt={$root} class={cn('relative flex  w-[15rem] items-center', className)}>
+<span use:melt={$root} class={cn('relative flex w-[15rem] items-center', className)}>
 	<span class="block h-1.5 w-full rounded-full bg-neutral-400 dark:bg-neutral-700">
 		<span use:melt={$range} class="h-1.5 rounded-full bg-magnum-400" />
 	</span>
 	<span
-		use:melt={$thumb()}
+		use:melt={$thumbs[0]}
 		class="block rounded-full bg-white shadow square-6 focus:ring-4 focus:ring-magnum-600 dark:bg-white dark:shadow-none"
 	/>
 </span>
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index e609d692..74064a51 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -34,7 +34,7 @@
 			class="z-10 mt-[clamp(2rem,5vh,6rem)] max-w-2xl text-center font-display text-5xl sm:text-6xl
 		md:max-w-3xl md:text-7xl lg:max-w-5xl lg:text-[6rem] lg:leading-none"
 		>
-			<Logo class="inline-block h-12 md:-my-2 md:h-20 lg:-my-4 lg:h-32" /> <b>melt</b> away complexity
+			<Logo class="inline-block h-12 md:-my-2 md:h-20 lg:-my-4 lg:h-32" /> <b>melt</b> away<br /> complexity
 		</h1>
 
 		<p
diff --git a/src/routes/docs/builders/[name]/+page.svelte b/src/routes/docs/builders/[name]/+page.svelte
index ae7c346c..ac854603 100644
--- a/src/routes/docs/builders/[name]/+page.svelte
+++ b/src/routes/docs/builders/[name]/+page.svelte
@@ -38,7 +38,7 @@
 		</div>
 		<div use:melt={$separator} class="my-4 md:my-6" />
 		<div class="mdsvex" id="mdsvex">
-			<Preview code={snippets.main} viewCode={false}>
+			<Preview code={snippets.main} viewCode={false} variant="default">
 				<svelte:component this={mainPreview} />
 			</Preview>
 			<Features {features} />
diff --git a/src/stories/DateField/DateField.svelte b/src/stories/DateField/DateField.svelte
index 900a6ae7..214850f3 100644
--- a/src/stories/DateField/DateField.svelte
+++ b/src/stories/DateField/DateField.svelte
@@ -11,6 +11,7 @@
 	export let isDateUnavailable: CreateDateFieldProps['isDateUnavailable'] = undefined;
 	export let disabled: CreateDateFieldProps['disabled'] = undefined;
 	export let readonly: CreateDateFieldProps['readonly'] = undefined;
+	export let readonlySegments: CreateDateFieldProps['readonlySegments'] = undefined;
 	export let hourCycle: CreateDateFieldProps['hourCycle'] = undefined;
 	export let locale: CreateDateFieldProps['locale'] = 'en';
 	export let hideTimeZone: CreateDateFieldProps['hideTimeZone'] = undefined;
@@ -29,6 +30,7 @@
 		isDateUnavailable,
 		disabled,
 		readonly,
+		readonlySegments,
 		hourCycle,
 		locale,
 		hideTimeZone,
diff --git a/svelte.config.js b/svelte.config.js
index 6d258347..58c849e1 100644
--- a/svelte.config.js
+++ b/svelte.config.js
@@ -1,7 +1,7 @@
 import { mdsvex } from '@huntabyte/mdsvex';
 import { preprocessMeltUI } from '@melt-ui/pp';
 import adapterStatic from '@sveltejs/adapter-static';
-import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
+import { vitePreprocess } from '@sveltejs/kit/vite';
 import sequence from 'svelte-sequential-preprocessor';
 import { mdsvexOptions } from './mdsvex.config.js';
 


Test patch:
diff --git a/src/lib/internal/helpers/date/tests/calendar.spec.ts b/src/lib/internal/helpers/date/tests/calendar.spec.ts
new file mode 100644
index 00000000..85ad4c4b
--- /dev/null
+++ b/src/lib/internal/helpers/date/tests/calendar.spec.ts
@@ -0,0 +1,34 @@
+import { describe, expect, it } from 'vitest';
+import { createMonths } from '../calendar.js';
+import { CalendarDate } from '@internationalized/date';
+
+const calendarDate = new CalendarDate(2024, 1, 1);
+
+describe('Calendar', () => {
+	it('should create a month with the given locale, no fixed weeks and week starting on 0', () => {
+		const month = createMonths({
+			dateObj: calendarDate,
+			weekStartsOn: 0,
+			fixedWeeks: false,
+			locale: 'en-US',
+			numberOfMonths: 1,
+		});
+		expect(month[0].value.toString()).toBe(calendarDate.toString());
+	});
+
+	it.each([0, 1, 2, 3, 4, 5, 6])(
+		'should create a month not containing duplicate dates when fixed weeks is true',
+		(i) => {
+			const month = createMonths({
+				dateObj: calendarDate,
+				weekStartsOn: i,
+				fixedWeeks: true,
+				locale: 'en-US',
+				numberOfMonths: 1,
+			});
+			const dateStringArray = month[0].dates.map((date) => date.toString());
+			const dateStringSet = new Set(dateStringArray);
+			expect(dateStringArray.length).toBe(dateStringSet.size);
+		}
+	);
+});
diff --git a/src/tests/combobox/Combobox.spec.ts b/src/tests/combobox/Combobox.spec.ts
index d47e538a..708b57c9 100644
--- a/src/tests/combobox/Combobox.spec.ts
+++ b/src/tests/combobox/Combobox.spec.ts
@@ -140,6 +140,47 @@ describe('Combobox', () => {
 		expect(label.id).toBe(ids.label);
 	});
 
+	test("Doesn't close on outside click if defaultPrevented on `onOutsideClick` handler", async () => {
+		const user = userEvent.setup();
+		const { getByTestId } = render(ComboboxTest, { onOutsideClick: (e) => e.preventDefault() });
+		const input = getByTestId('input');
+
+		await user.click(input);
+		expect(getByTestId('menu')).toBeVisible();
+
+		const outsideClick = getByTestId('outside-click');
+		await user.click(outsideClick);
+		expect(getByTestId('menu')).toBeVisible();
+	});
+
+	test('Closes on escape by default', async () => {
+		const user = userEvent.setup();
+		const { getByTestId } = render(ComboboxTest);
+
+		const input = getByTestId('input');
+		const menu = getByTestId('menu');
+
+		await user.click(input);
+		expect(menu).toBeVisible();
+
+		await user.keyboard(kbd.ESCAPE);
+		expect(menu).not.toBeVisible();
+	});
+
+	test('Respects the `closeOnEscape` prop', async () => {
+		const user = userEvent.setup();
+		const { getByTestId } = render(ComboboxTest, { closeOnEscape: false });
+
+		const input = getByTestId('input');
+		const menu = getByTestId('menu');
+
+		await user.click(input);
+		expect(menu).toBeVisible();
+
+		await user.keyboard(kbd.ESCAPE);
+		expect(menu).toBeVisible();
+	});
+
 	test.todo('Selects multiple items when `multiple` is true');
 	test.todo('Manually setting the value updates the label');
 	test.todo('Updating options and setting the value updates the label');
diff --git a/src/tests/combobox/ComboboxTest.svelte b/src/tests/combobox/ComboboxTest.svelte
index 50b79ce7..7fe4aa0f 100644
--- a/src/tests/combobox/ComboboxTest.svelte
+++ b/src/tests/combobox/ComboboxTest.svelte
@@ -17,6 +17,8 @@
 	export let multiple = false;
 	export let defaultValue: string | undefined = undefined;
 	export let ids: CreateComboboxProps<unknown>['ids'] = undefined;
+	export let onOutsideClick: CreateComboboxProps<unknown>['onOutsideClick'] = undefined;
+	export let closeOnEscape: CreateComboboxProps<unknown>['closeOnEscape'] = undefined;
 
 	const {
 		elements: { menu, input, option, label },
@@ -31,6 +33,8 @@
 				  }
 				: undefined,
 			ids,
+			onOutsideClick,
+			closeOnEscape,
 		})
 	);
 
@@ -60,4 +64,5 @@
 			{/each}
 		</div>
 	</ul>
+	<div data-testid="outside-click" />
 </main>
diff --git a/src/tests/context-menu/ContextMenu.spec.ts b/src/tests/context-menu/ContextMenu.spec.ts
index 2157e7dd..39b823b9 100644
--- a/src/tests/context-menu/ContextMenu.spec.ts
+++ b/src/tests/context-menu/ContextMenu.spec.ts
@@ -205,4 +205,16 @@ describe('Context Menu', () => {
 		const submenu = getByTestId('submenu');
 		expect(submenu.id).toBe(subIds.menu);
 	});
+
+	test("Doesn't close on outsideClick if defaultPrevented in onOutsideClick", async () => {
+		const { user, menu, getByTestId } = await open({
+			onOutsideClick: (e) => {
+				e.preventDefault();
+			},
+		});
+		expect(menu).toBeVisible();
+		const outsideClick = getByTestId('outside-click');
+		await user.click(outsideClick);
+		expect(menu).toBeVisible();
+	});
 });
diff --git a/src/tests/date-field/DateField.spec.ts b/src/tests/date-field/DateField.spec.ts
index 1c998349..63da56dd 100644
--- a/src/tests/date-field/DateField.spec.ts
+++ b/src/tests/date-field/DateField.spec.ts
@@ -258,6 +258,29 @@ describe('DateField', () => {
 		}
 	});
 
+	test('readonlySegments prop prevents modifying given segments', async () => {
+		const { getByTestId, user } = setup({
+			readonlySegments: ['month'],
+			defaultValue: calendarDateOther,
+		});
+
+		// month should not change
+		const monthSegment = getByTestId('month');
+		expect(monthSegment).toHaveTextContent(String(calendarDateOther['month']));
+		await user.click(monthSegment);
+		expect(monthSegment).toHaveFocus();
+		await user.keyboard(kbd.ARROW_UP);
+		expect(monthSegment).toHaveTextContent(String(calendarDateOther['month']));
+
+		// day should change
+		const daySegment = getByTestId('day');
+		expect(daySegment).toHaveTextContent(String(calendarDateOther['day']));
+		await user.click(daySegment);
+		expect(daySegment).toHaveFocus();
+		await user.keyboard(kbd.ARROW_UP);
+		expect(daySegment).toHaveTextContent(String(calendarDateOther['day'] + 1));
+	});
+
 	test('if selected date unavailable, mark field as invalid', async () => {
 		const { getByTestId, user } = setup({
 			granularity: 'day',
diff --git a/src/tests/date-field/DateFieldTest.svelte b/src/tests/date-field/DateFieldTest.svelte
index 82db773f..32bbb35f 100644
--- a/src/tests/date-field/DateFieldTest.svelte
+++ b/src/tests/date-field/DateFieldTest.svelte
@@ -1,6 +1,7 @@
 <script lang="ts">
 	import { createDateField, type CreateDateFieldProps } from '$lib/builders';
 	import { melt } from '$lib';
+	import { removeUndefined } from '../utils';
 
 	export let value: CreateDateFieldProps['value'] = undefined;
 	export let defaultValue: CreateDateFieldProps['defaultValue'] = undefined;
@@ -10,6 +11,7 @@
 	export let isDateUnavailable: CreateDateFieldProps['isDateUnavailable'] = undefined;
 	export let disabled: CreateDateFieldProps['disabled'] = undefined;
 	export let readonly: CreateDateFieldProps['readonly'] = undefined;
+	export let readonlySegments: CreateDateFieldProps['readonlySegments'] = undefined;
 	export let hourCycle: CreateDateFieldProps['hourCycle'] = undefined;
 	export let locale: CreateDateFieldProps['locale'] = 'en';
 	export let hideTimeZone: CreateDateFieldProps['hideTimeZone'] = undefined;
@@ -19,21 +21,24 @@
 	const {
 		elements: { field, segment, label, validation },
 		states: { value: insideValue, segmentContents },
-	} = createDateField({
-		value,
-		defaultValue,
-		defaultPlaceholder,
-		onValueChange,
-		onPlaceholderChange,
-		isDateUnavailable,
-		disabled,
-		readonly,
-		hourCycle,
-		locale,
-		hideTimeZone,
-		ids,
-		granularity,
-	});
+	} = createDateField(
+		removeUndefined({
+			value,
+			defaultValue,
+			defaultPlaceholder,
+			onValueChange,
+			onPlaceholderChange,
+			isDateUnavailable,
+			disabled,
+			readonly,
+			readonlySegments,
+			hourCycle,
+			locale,
+			hideTimeZone,
+			ids,
+			granularity,
+		})
+	);
 </script>
 
 <main>
diff --git a/src/tests/date-picker/DatePicker.spec.ts b/src/tests/date-picker/DatePicker.spec.ts
index 30570601..4b269a8f 100644
--- a/src/tests/date-picker/DatePicker.spec.ts
+++ b/src/tests/date-picker/DatePicker.spec.ts
@@ -293,7 +293,7 @@ describe('DatePicker', () => {
 			}
 		});
 
-		test('increments segment on arrow down', async () => {
+		test('decrements segment on arrow down', async () => {
 			const user = userEvent.setup();
 			const { getByTestId } = setup({
 				defaultPlaceholder: calendarDateToday,
@@ -307,25 +307,13 @@ describe('DatePicker', () => {
 			const currentMonth = calendarDateToday.month;
 			expect(firstSegment).toHaveTextContent(String(currentMonth));
 
-			await user.keyboard(kbd.ARROW_DOWN);
-			expect(firstSegment).toHaveTextContent(String(calendarDateToday.month - 1));
-		});
-
-		test('increments segment on arrow down', async () => {
-			const { getByTestId, user } = setup({
-				defaultPlaceholder: calendarDateToday,
-			});
-
-			const firstSegment = getByTestId('month');
-			await user.click(firstSegment);
-
 			await user.keyboard(kbd.ARROW_DOWN);
 
-			const currentMonth = calendarDateToday.month;
-			expect(firstSegment).toHaveTextContent(String(currentMonth));
-
-			await user.keyboard(kbd.ARROW_DOWN);
-			expect(firstSegment).toHaveTextContent(String(calendarDateToday.month - 1));
+			if (calendarDateToday.month === 1) {
+				expect(firstSegment).toHaveTextContent(String(12));
+			} else {
+				expect(firstSegment).toHaveTextContent(String(calendarDateToday.month - 1));
+			}
 		});
 
 		test('navigates segments using arrow keys', async () => {
@@ -377,6 +365,7 @@ describe('DatePicker', () => {
 		test('when no time selected, selecting', async () => {
 			const { getByTestId, queryByTestId, user, trigger } = setup({
 				granularity: 'minute',
+				defaultValue: new CalendarDateTime(2021, 1, 1),
 			});
 
 			await user.click(trigger);
@@ -393,7 +382,7 @@ describe('DatePicker', () => {
 			const minuteSegment = getByTestId('minute');
 			expect(minuteSegment).not.toHaveTextContent(String(undefined));
 
-			const firstDayInMonth = getByTestId('month-1-date-1');
+			const firstDayInMonth = getByTestId('month-2-date-1');
 			await user.click(firstDayInMonth);
 
 			await tick();
@@ -545,4 +534,22 @@ describe('DatePicker', () => {
 		await user.click(label);
 		expect(monthSegment).toHaveFocus();
 	});
+
+	test("doesn't close on outside click if preventDefault called in `onOutsideClick`", async () => {
+		const { getByTestId, trigger, user } = setup({
+			onOutsideClick: (e) => {
+				e.preventDefault();
+			},
+		});
+		const content = getByTestId('content');
+
+		expect(content).not.toBeVisible();
+		await user.click(trigger);
+		await waitFor(() => expect(content).toBeVisible());
+		await user.click(content);
+		await waitFor(() => expect(content).toBeVisible());
+		const outside = getByTestId('inside-value');
+		await user.click(outside);
+		await waitFor(() => expect(content).toBeVisible());
+	});
 });
diff --git a/src/tests/date-picker/DatePickerTest.svelte b/src/tests/date-picker/DatePickerTest.svelte
index 4e30dd2d..fa7c38f3 100644
--- a/src/tests/date-picker/DatePickerTest.svelte
+++ b/src/tests/date-picker/DatePickerTest.svelte
@@ -2,7 +2,6 @@
 	import { createDatePicker, type CreateDatePickerProps } from '$lib/builders';
 	import { ChevronRight, ChevronLeft, Calendar } from 'lucide-svelte';
 	import { melt } from '$lib';
-	import { fade } from 'svelte/transition';
 	import { removeUndefined } from '../utils';
 
 	export let value: CreateDatePickerProps['value'] = undefined;
@@ -28,6 +27,7 @@
 	export let placeholder: CreateDatePickerProps['placeholder'] = undefined;
 	export let weekStartsOn: CreateDatePickerProps['weekStartsOn'] = undefined;
 	export let weekdayFormat: CreateDatePickerProps['weekdayFormat'] = undefined;
+	export let onOutsideClick: CreateDatePickerProps['onOutsideClick'] = undefined;
 
 	const {
 		elements: {
@@ -71,6 +71,7 @@
 			isDateDisabled,
 			weekdayFormat,
 			popoverIds,
+			onOutsideClick,
 		})
 	);
 
@@ -118,7 +119,6 @@
 	</div>
 	<div
 		class="z-10 w-80 rounded-[4px] bg-white p-3 shadow-sm"
-		transition:fade={{ duration: 100 }}
 		use:melt={$content}
 		data-testid="content"
 	>
diff --git a/src/tests/date-range-field/DateRangeField.spec.ts b/src/tests/date-range-field/DateRangeField.spec.ts
index e59405f2..107c3784 100644
--- a/src/tests/date-range-field/DateRangeField.spec.ts
+++ b/src/tests/date-range-field/DateRangeField.spec.ts
@@ -258,4 +258,68 @@ describe('DateField', () => {
 		expect(getByTestId('start-value')).toHaveTextContent(overrideDay.start.toString());
 		expect(getByTestId('end-value')).toHaveTextContent(overrideDay.end.toString());
 	});
+
+	test('readonlySegments prop prevents modifying given segments', async () => {
+		const { getByTestId, user } = setup({
+			defaultValue: exampleDate,
+			readonlySegments: {
+				start: ['month'],
+				end: ['month', 'year'],
+			},
+		});
+
+		// start field
+		{
+			// start month should not change
+			const monthSegment = getByTestId('start-month');
+			expect(monthSegment).toHaveTextContent(String(exampleDate.start['month']));
+			await user.click(monthSegment);
+			expect(monthSegment).toHaveFocus();
+			await user.keyboard(kbd.ARROW_UP);
+			expect(monthSegment).toHaveTextContent(String(exampleDate.start['month']));
+
+			// start day should change
+			const daySegment = getByTestId('start-day');
+			expect(daySegment).toHaveTextContent(String(exampleDate.start['day']));
+			await user.click(daySegment);
+			expect(daySegment).toHaveFocus();
+			await user.keyboard(kbd.ARROW_UP);
+			expect(daySegment).toHaveTextContent(String(exampleDate.start['day'] + 1));
+
+			// start year should change
+			const yearSegment = getByTestId('start-year');
+			expect(yearSegment).toHaveTextContent(String(exampleDate.start['year']));
+			await user.click(yearSegment);
+			expect(yearSegment).toHaveFocus();
+			await user.keyboard(kbd.ARROW_UP);
+			expect(yearSegment).toHaveTextContent(String(exampleDate.start['year'] + 1));
+		}
+
+		// end field
+		{
+			// end month should not change
+			const monthSegment = getByTestId('end-month');
+			expect(monthSegment).toHaveTextContent(String(exampleDate.end['month']));
+			await user.click(monthSegment);
+			expect(monthSegment).toHaveFocus();
+			await user.keyboard(kbd.ARROW_UP);
+			expect(monthSegment).toHaveTextContent(String(exampleDate.end['month']));
+
+			// end day should change
+			const daySegment = getByTestId('end-day');
+			expect(daySegment).toHaveTextContent(String(exampleDate.end['day']));
+			await user.click(daySegment);
+			expect(daySegment).toHaveFocus();
+			await user.keyboard(kbd.ARROW_UP);
+			expect(daySegment).toHaveTextContent(String(exampleDate.end['day'] + 1));
+
+			// end year should not change
+			const yearSegment = getByTestId('end-year');
+			expect(yearSegment).toHaveTextContent(String(exampleDate.end['year']));
+			await user.click(yearSegment);
+			expect(yearSegment).toHaveFocus();
+			await user.keyboard(kbd.ARROW_UP);
+			expect(yearSegment).toHaveTextContent(String(exampleDate.end['year']));
+		}
+	});
 });
diff --git a/src/tests/date-range-field/DateRangeFieldTest.svelte b/src/tests/date-range-field/DateRangeFieldTest.svelte
index cefd943b..b7aca709 100644
--- a/src/tests/date-range-field/DateRangeFieldTest.svelte
+++ b/src/tests/date-range-field/DateRangeFieldTest.svelte
@@ -14,6 +14,7 @@
 	export let isDateUnavailable: CreateDateRangeFieldProps['isDateUnavailable'] = undefined;
 	export let disabled: CreateDateRangeFieldProps['disabled'] = undefined;
 	export let readonly: CreateDateRangeFieldProps['readonly'] = undefined;
+	export let readonlySegments: CreateDateRangeFieldProps['readonlySegments'] = undefined;
 	export let hourCycle: CreateDateRangeFieldProps['hourCycle'] = undefined;
 	export let locale: CreateDateRangeFieldProps['locale'] = 'en';
 	export let hideTimeZone: CreateDateRangeFieldProps['hideTimeZone'] = undefined;
@@ -35,6 +36,7 @@
 			isDateUnavailable,
 			disabled,
 			readonly,
+			readonlySegments,
 			hourCycle,
 			locale,
 			hideTimeZone,
diff --git a/src/tests/dialog/Dialog.spec.ts b/src/tests/dialog/Dialog.spec.ts
index aedfbb86..d34459ea 100644
--- a/src/tests/dialog/Dialog.spec.ts
+++ b/src/tests/dialog/Dialog.spec.ts
@@ -77,6 +77,25 @@ describe('Dialog', () => {
 		expect(content).not.toBeVisible();
 	});
 
+	it('Prevents closing on outside click if `defaultPrevented` in `onOutsideClick` callback', async () => {
+		const { getByTestId, user, trigger } = setup({
+			onOutsideClick: (e) => {
+				e.preventDefault();
+			},
+		});
+		const overlay = getByTestId('overlay');
+		const content = getByTestId('content');
+
+		expect(trigger).toBeVisible();
+		expect(content).not.toBeVisible();
+		await user.click(trigger);
+		expect(content).toBeVisible();
+		await sleep(100);
+		expect(overlay).toBeVisible();
+		await user.click(overlay);
+		expect(content).toBeVisible();
+	});
+
 	it('Portalled el attaches dialog to body', async () => {
 		const { getByTestId, user, trigger } = setup();
 		await user.click(trigger);
diff --git a/src/tests/dropdown-menu/DropdownMenu.spec.ts b/src/tests/dropdown-menu/DropdownMenu.spec.ts
index 28842689..0dfe270c 100644
--- a/src/tests/dropdown-menu/DropdownMenu.spec.ts
+++ b/src/tests/dropdown-menu/DropdownMenu.spec.ts
@@ -1,12 +1,12 @@
-import { render, act, waitFor } from '@testing-library/svelte';
+import type { CreateDropdownMenuProps } from '$lib';
+import { sleep } from '$lib/internal/helpers/sleep.js';
+import { act, render, waitFor } from '@testing-library/svelte';
+import userEvent from '@testing-library/user-event';
 import { axe } from 'jest-axe';
 import { describe, vi } from 'vitest';
-import userEvent from '@testing-library/user-event';
 import { testKbd as kbd } from '../utils.js';
-import DropdownMenuTest from './DropdownMenuTest.svelte';
 import DropdownMenuForceVisible from './DropdownMenuForceVisibleTest.svelte';
-import type { CreateDropdownMenuProps } from '$lib';
-import { sleep } from '$lib/internal/helpers/sleep.js';
+import DropdownMenuTest from './DropdownMenuTest.svelte';
 
 const OPEN_KEYS = [kbd.ENTER, kbd.ARROW_DOWN, kbd.SPACE];
 
@@ -45,6 +45,19 @@ describe('Dropdown Menu (Default)', () => {
 		expect(menu).not.toBeVisible();
 	});
 
+	test('Refocuses the trigger when menu closed with keyboard', async () => {
+		const { user, trigger, getByTestId } = setup();
+		const menu = getByTestId('menu');
+
+		expect(menu).not.toBeVisible();
+		await user.click(trigger);
+		expect(menu).toBeVisible();
+
+		await user.keyboard(kbd.ESCAPE);
+		expect(menu).not.toBeVisible();
+		expect(trigger).toHaveFocus();
+	});
+
 	test.each(OPEN_KEYS)('Opens when %s is pressed', async (key) => {
 		const { user, trigger, getByTestId } = setup();
 		const menu = getByTestId('menu');
@@ -244,6 +257,20 @@ describe('Dropdown Menu (Default)', () => {
 		expect(submenu.id).toBe(subIds.menu);
 	});
 
+	test("Doesn't close on outsideClick if defaultPrevented in onOutsideClick", async () => {
+		const { user, trigger, getByTestId } = setup({
+			onOutsideClick: (e) => {
+				e.preventDefault();
+			},
+		});
+		const menu = getByTestId('menu');
+		await user.click(trigger);
+		expect(menu).toBeVisible();
+		const outsideClick = getByTestId('outside-click');
+		await user.click(outsideClick);
+		expect(menu).toBeVisible();
+	});
+
 	test.todo('Radio items');
 });
 
@@ -289,6 +316,21 @@ describe('Dropdown Menu (forceVisible)', () => {
 		await waitFor(() => expect(queryByTestId('menu')).toBeNull());
 	});
 
+	test('Refocuses the trigger when menu closed with keyboard', async () => {
+		const { queryByTestId, user, trigger } = setupForceVis();
+
+		expect(queryByTestId('menu')).toBeNull();
+		await user.click(trigger);
+		await waitFor(() => expect(queryByTestId('menu')).not.toBeNull());
+		await user.keyboard(kbd.ARROW_DOWN);
+
+		expect(trigger).not.toHaveFocus();
+		const spy = vi.spyOn(trigger, 'focus');
+		await user.keyboard(kbd.ESCAPE);
+		await waitFor(() => expect(queryByTestId('menu')).toBeNull());
+		expect(spy).toHaveBeenCalled();
+	});
+
 	test.each(OPEN_KEYS)('Opens when %s is pressed', async (key) => {
 		const { queryByTestId, user, trigger } = setupForceVis();
 		expect(queryByTestId('menu')).toBeNull();
diff --git a/src/tests/dropdown-menu/DropdownMenuTest.svelte b/src/tests/dropdown-menu/DropdownMenuTest.svelte
index 538ea60f..4afc5b2f 100644
--- a/src/tests/dropdown-menu/DropdownMenuTest.svelte
+++ b/src/tests/dropdown-menu/DropdownMenuTest.svelte
@@ -18,13 +18,15 @@
 	const {
 		elements: { trigger, menu, item, separator, arrow },
 		builders: { createSubmenu, createMenuRadioGroup, createCheckboxItem },
-	} = createDropdownMenu({
-		loop,
-		closeFocus,
-		closeOnEscape,
-		closeOnOutsideClick,
-		...$$restProps,
-	});
+	} = createDropdownMenu(
+		removeUndefined({
+			loop,
+			closeFocus,
+			closeOnEscape,
+			closeOnOutsideClick,
+			...$$restProps,
+		})
+	);
 
 	const {
 		elements: { checkboxItem: settingsSyncCheckbox },
diff --git a/src/tests/link-preview/LinkPreview.spec.ts b/src/tests/link-preview/LinkPreview.spec.ts
index 6287d1ed..6f689bd4 100644
--- a/src/tests/link-preview/LinkPreview.spec.ts
+++ b/src/tests/link-preview/LinkPreview.spec.ts
@@ -74,6 +74,20 @@ describe('LinkPreview (Default)', () => {
 		await waitFor(() => expect(content).not.toBeVisible());
 	});
 
+	test.skip('Closes on outside click by default', async () => {
+		const { getByTestId, trigger, user } = setup();
+		const content = getByTestId('content');
+
+		expect(content).not.toBeVisible();
+		await user.hover(trigger);
+		await waitFor(() => expect(content).toBeVisible());
+		await user.click(content);
+		await waitFor(() => expect(content).toBeVisible());
+		const start = getByTestId('start');
+		await user.click(start);
+		await waitFor(() => expect(content).not.toBeVisible());
+	});
+
 	test('Custom ids are applied when provided', async () => {
 		const ids = {
 			content: 'id-content',
diff --git a/src/tests/popover/Popover.spec.ts b/src/tests/popover/Popover.spec.ts
index 4acb626c..0143c7ca 100644
--- a/src/tests/popover/Popover.spec.ts
+++ b/src/tests/popover/Popover.spec.ts
@@ -57,6 +57,23 @@ describe('Popover (Default)', () => {
 		await waitFor(() => expect(content).not.toBeVisible());
 	});
 
+	test('doesnt close when preventDefault called in `onOutsideClick`', async () => {
+		const { content, trigger, user, getByTestId } = setup({
+			onOutsideClick: (e) => {
+				e.preventDefault();
+			},
+		});
+
+		expect(content).not.toBeVisible();
+		await user.click(trigger);
+		await waitFor(() => expect(content).toBeVisible());
+		await user.click(content);
+		await waitFor(() => expect(content).toBeVisible());
+		const outside = getByTestId('outside');
+		await user.click(outside);
+		await waitFor(() => expect(content).toBeVisible());
+	});
+
 	test('Toggles open state on trigger click', async () => {
 		const { content, trigger, user } = setup();
 
diff --git a/src/tests/radio-group/RadioGroup.spec.ts b/src/tests/radio-group/RadioGroup.spec.ts
new file mode 100644
index 00000000..032c0a4b
--- /dev/null
+++ b/src/tests/radio-group/RadioGroup.spec.ts
@@ -0,0 +1,87 @@
+import type { CreateRadioGroupProps } from '$lib';
+import { render } from '@testing-library/svelte';
+import userEvent from '@testing-library/user-event';
+import { axe } from 'jest-axe';
+import { describe, it } from 'vitest';
+import { testKbd as kbd } from '../utils.js';
+import RadioGroupTest from './RadioGroupTest.svelte';
+
+const items = [
+	{
+		value: 'a',
+		disabled: false,
+	},
+	{
+		value: 'b',
+		disabled: false,
+	},
+	{
+		value: 'c',
+		disabled: false,
+	},
+	{
+		value: 'd',
+		disabled: false,
+	},
+];
+
+function setup(
+	props: CreateRadioGroupProps & { items?: { value: string; disabled: boolean }[] } = {}
+) {
+	const user = userEvent.setup();
+	return { ...render(RadioGroupTest, props), user };
+}
+
+describe('Radio Group', () => {
+	it('No accessibility violations', async () => {
+		const { container } = render(RadioGroupTest);
+		expect(await axe(container)).toHaveNoViolations();
+	});
+
+	it('doesnt require a default value', async () => {
+		const { getByTestId } = setup();
+		expect(getByTestId('value')).toHaveTextContent('undefined');
+	});
+
+	it('allows changing value by clicking on a radio button', async () => {
+		const { user, getByTestId } = setup();
+		const a = getByTestId('a');
+		const b = getByTestId('b');
+		const value = getByTestId('value');
+		await user.click(a);
+		expect(value).toHaveTextContent('a');
+		await user.click(b);
+		expect(value).toHaveTextContent('b');
+	});
+
+	it('navigates through the buttons with arrow keys', async () => {
+		const { user, getByTestId } = setup();
+
+		const els = ['a', 'b', 'c', 'd'].map((v) => getByTestId(v));
+
+		await user.click(els[0]);
+		expect(els[0]).toHaveFocus();
+		await user.keyboard(kbd.ARROW_DOWN);
+		expect(els[1]).toHaveFocus();
+		await user.keyboard(kbd.ARROW_DOWN);
+		expect(els[2]).toHaveFocus();
+		await user.keyboard(kbd.ARROW_DOWN);
+		expect(els[3]).toHaveFocus();
+		await user.keyboard(kbd.ARROW_DOWN);
+		expect(els[0]).toHaveFocus();
+	});
+
+	it('skips over disabled buttons', async () => {
+		const { user, getByTestId } = setup({
+			items: items.map((item) => ({ ...item, disabled: item.value === 'b' })),
+		});
+
+		const els = ['a', 'b', 'c', 'd'].map((v) => getByTestId(v));
+
+		await user.click(els[0]);
+		expect(els[0]).toHaveFocus();
+		await user.keyboard(kbd.ARROW_DOWN);
+		expect(els[2]).toHaveFocus();
+		expect(els[1]).not.toHaveFocus();
+	});
+});
diff --git a/src/tests/radio-group/RadioGroupTest.svelte b/src/tests/radio-group/RadioGroupTest.svelte
new file mode 100644
index 00000000..111c1785
--- /dev/null
+++ b/src/tests/radio-group/RadioGroupTest.svelte
@@ -0,0 +1,49 @@
+<script lang="ts">
+	import { createRadioGroup, melt, type CreateRadioGroupProps } from '$lib/index.js';
+	import { removeUndefined } from '../utils.js';
+
+	type $$Props = CreateRadioGroupProps & {
+		items?: Item[];
+	};
+
+	type Item = {
+		value: string;
+		disabled: boolean;
+	};
+
+	export let value: CreateRadioGroupProps['value'] = undefined;
+	export let defaultValue: CreateRadioGroupProps['defaultValue'] = undefined;
+	export let disabled: CreateRadioGroupProps['disabled'] = undefined;
+	export let onValueChange: CreateRadioGroupProps['onValueChange'] = undefined;
+	export let required: CreateRadioGroupProps['required'] = undefined;
+	export let items: Item[] = [
+		{ value: 'a', disabled: false },
+		{ value: 'b', disabled: false },
+		{ value: 'c', disabled: false },
+		{ value: 'd', disabled: false },
+	];
+
+	const {
+		elements: { root, item },
+		states: { value: localValue },
+	} = createRadioGroup({
+		value,
+		defaultValue,
+		onValueChange,
+		disabled,
+		required,
+		...removeUndefined($$restProps),
+	});
+</script>
+
+<main>
+	<div data-testid="value">{$localValue}</div>
+	<form>
+		<label id="radio-group-label" for="radio-group" data-testid="label"> Airplane mode </label>
+		<div use:melt={$root} data-testid="root">
+			{#each items as radioItem}
+				<button use:melt={$item(radioItem)} data-testid={radioItem.value}>{radioItem.value}</button>
+			{/each}
+		</div>
+	</form>
+</main>
diff --git a/src/tests/select/Select.spec.ts b/src/tests/select/Select.spec.ts
index 4155874d..d68dfc47 100644
--- a/src/tests/select/Select.spec.ts
+++ b/src/tests/select/Select.spec.ts
@@ -14,10 +14,10 @@ describe('Select', () => {
 	});
 
 	test('Opens/Closes when trigger is clicked', async () => {
+		const user = userEvent.setup();
 		const { getByTestId } = render(SelectTest);
 		const trigger = getByTestId('trigger');
 		const menu = getByTestId('menu');
-		const user = userEvent.setup();
 
 		expect(menu).not.toBeVisible();
 		await user.click(trigger);
@@ -27,11 +27,26 @@ describe('Select', () => {
 		expect(menu).not.toBeVisible();
 	});
 
-	test.each(OPEN_KEYS)('Opens when %s is pressed', async (key) => {
+	test('Closes on escape keydown', async () => {
+		const user = userEvent.setup();
 		const { getByTestId } = render(SelectTest);
 		const trigger = getByTestId('trigger');
 		const menu = getByTestId('menu');
+
+		expect(menu).not.toBeVisible();
+		await user.click(trigger);
+		expect(menu).toBeVisible();
+		expect(trigger).toHaveFocus();
+
+		await user.keyboard(kbd.ESCAPE);
+		expect(menu).not.toBeVisible();
+	});
+
+	test.each(OPEN_KEYS)('Opens when %s is pressed', async (key) => {
 		const user = userEvent.setup();
+		const { getByTestId } = render(SelectTest);
+		const trigger = getByTestId('trigger');
+		const menu = getByTestId('menu');
 
 		expect(menu).not.toBeVisible();
 		await act(() => trigger.focus());
@@ -40,10 +55,10 @@ describe('Select', () => {
 	});
 
 	test('Toggles when trigger is clicked', async () => {
+		const user = userEvent.setup();
 		const { getByTestId } = render(SelectTest);
 		const trigger = getByTestId('trigger');
 		const menu = getByTestId('menu');
-		const user = userEvent.setup();
 
 		expect(menu).not.toBeVisible();
 		await user.click(trigger);
@@ -54,10 +69,10 @@ describe('Select', () => {
 	});
 
 	test('Toggles when icon within trigger is clicked', async () => {
+		const user = userEvent.setup();
 		const { getByTestId } = render(SelectTest);
 		const icon = getByTestId('icon');
 		const menu = getByTestId('menu');
-		const user = userEvent.setup();
 
 		expect(menu).not.toBeVisible();
 		await user.click(icon);
@@ -68,10 +83,10 @@ describe('Select', () => {
 	});
 
 	test('Selects item when clicked', async () => {
+		const user = userEvent.setup();
 		const { getByTestId } = render(SelectTest);
 		const trigger = getByTestId('trigger');
 		const menu = getByTestId('menu');
-		const user = userEvent.setup();
 
 		expect(trigger).not.toHaveTextContent('Caramel');
 
@@ -98,10 +113,10 @@ describe('Select', () => {
 	});
 
 	test('Selects multiple items when `multiple` is true', async () => {
+		const user = userEvent.setup();
 		const { getByTestId } = render(SelectTest, { multiple: true });
 		const trigger = getByTestId('trigger');
 		const menu = getByTestId('menu');
-		const user = userEvent.setup();
 
 		expect(trigger).not.toHaveTextContent('Caramel');
 
@@ -156,10 +171,10 @@ describe('Select', () => {
 	});
 
 	test('Respects the `closeOnEscape` prop', async () => {
+		const user = userEvent.setup();
 		const { getByTestId } = render(SelectTest, { closeOnEscape: false });
 		const trigger = getByTestId('trigger');
 		const menu = getByTestId('menu');
-		const user = userEvent.setup();
 
 		expect(menu).not.toBeVisible();
 		await user.click(trigger);
@@ -170,10 +185,29 @@ describe('Select', () => {
 	});
 
 	test('Respects the `closeOnOutsideClick` prop', async () => {
+		const user = userEvent.setup();
 		const { getByTestId } = render(SelectTest, { closeOnOutsideClick: false });
 		const trigger = getByTestId('trigger');
 		const menu = getByTestId('menu');
+
+		expect(menu).not.toBeVisible();
+		await user.click(trigger);
+		expect(menu).toBeVisible();
+
+		const outside = getByTestId('outside');
+		await user.click(outside);
+		expect(menu).toBeVisible();
+	});
+
+	test("doesn't close when preventDefault called in `onOutsideClick`", async () => {
 		const user = userEvent.setup();
+		const { getByTestId } = render(SelectTest, {
+			onOutsideClick: (e) => {
+				e.preventDefault();
+			},
+		});
+		const trigger = getByTestId('trigger');
+		const menu = getByTestId('menu');
 
 		expect(menu).not.toBeVisible();
 		await user.click(trigger);
diff --git a/src/tests/select/SelectTest.svelte b/src/tests/select/SelectTest.svelte
index 984fe574..268a7508 100644
--- a/src/tests/select/SelectTest.svelte
+++ b/src/tests/select/SelectTest.svelte
@@ -9,6 +9,7 @@
 	export let closeOnEscape = true;
 	export let closeOnOutsideClick = true;
 	export let ids: CreateSelectProps['ids'] = undefined;
+	export let onOutsideClick: CreateSelectProps['onOutsideClick'] = undefined;
 	const {
 		elements: { trigger, menu, option, group, groupLabel, label },
 		states: { selected, selectedLabel },
@@ -26,6 +27,7 @@
 			closeOnEscape,
 			closeOnOutsideClick,
 			ids,
+			onOutsideClick,
 		})
 	);
 
diff --git a/src/tests/slider/RangeSlider.svelte b/src/tests/slider/RangeSlider.svelte
index 09215f1f..478a5a27 100644
--- a/src/tests/slider/RangeSlider.svelte
+++ b/src/tests/slider/RangeSlider.svelte
@@ -4,8 +4,7 @@
 	export let values = [20, 80];
 
 	const {
-		elements: { root, range, thumb, tick },
-		states: { value, ticks },
+		elements: { root, range, thumbs, ticks },
 	} = createSlider({
 		defaultValue: values,
 		max: 100,
@@ -18,17 +17,17 @@
 			<span data-testid="range" use:melt={$range} class="h-[3px] bg-white" />
 		</span>
 
-		{#each $value as _, i}
+		{#each $ticks as tick}
+			<span use:melt={tick} data-testid="tick" />
+		{/each}
+
+		{#each $thumbs as thumb, i}
 			<span
 				aria-label="Volume"
 				data-testid="thumb-{i}"
-				use:melt={$thumb()}
+				use:melt={thumb}
 				class="block h-5 w-5 rounded-full bg-white focus:ring-4 focus:ring-black/40"
 			/>
 		{/each}
-
-		{#each { length: $ticks } as _}
-			<span use:melt={$tick()} data-testid="tick" />
-		{/each}
 	</span>
 </main>
diff --git a/src/tests/slider/Slider.svelte b/src/tests/slider/Slider.svelte
index 6adbe487..62777706 100644
--- a/src/tests/slider/Slider.svelte
+++ b/src/tests/slider/Slider.svelte
@@ -1,5 +1,6 @@
 <script lang="ts">
 	import { createSlider, melt, type CreateSliderProps } from '$lib/index.js';
+
 	export let value = [30];
 	export let max = 100;
 	export let min = 0;
@@ -10,8 +11,7 @@
 	export let resetStep: number | undefined = undefined;
 
 	const {
-		elements: { root, range, thumb, tick },
-		states: { ticks },
+		elements: { root, range, thumbs, ticks },
 		options: { min: optionsMin, max: optionsMax, step: optionsStep },
 	} = createSlider({
 		defaultValue: value,
@@ -37,14 +37,16 @@
 		<span class="block h-[3px] w-full bg-black/40">
 			<span data-testid="range" use:melt={$range} class="h-[3px] bg-white" />
 		</span>
+
+		{#each $ticks as tick}
+			<span use:melt={tick} data-testid="tick" />
+		{/each}
+
 		<span
 			aria-label="Volume"
 			data-testid="thumb"
-			use:melt={$thumb()}
+			use:melt={$thumbs[0]}
 			class="block h-5 w-5 rounded-full bg-white focus:ring-4 focus:ring-black/40"
 		/>
 	</span>
-	{#each { length: $ticks } as _}
-		<span use:melt={$tick()} data-testid="tick" />
-	{/each}
 </main>
diff --git a/src/tests/tabs/Tabs.spec.ts b/src/tests/tabs/Tabs.spec.ts
new file mode 100644
index 00000000..7875f77c
--- /dev/null
+++ b/src/tests/tabs/Tabs.spec.ts
@@ -0,0 +1,48 @@
+import { kbd } from '$lib/internal/helpers/keyboard.js';
+import { render } from '@testing-library/svelte';
+import userEvent from '@testing-library/user-event';
+import { axe } from 'jest-axe';
+import { describe } from 'vitest';
+import TabsTest from './TabsTest.svelte';
+
+const tabs = ['1', '2', '3', '4', '5'];
+
+describe('Tabs', () => {
+	test('has no accessibility violations', async () => {
+		const { container } = render(TabsTest);
+		expect(await axe(container)).toHaveNoViolations();
+	});
+
+	test('tablist renders tabs', async () => {
+		const { getByTestId } = render(TabsTest, {
+			tabValues: tabs,
+		});
+
+		const tabList = getByTestId('list');
+
+		const tabTriggers = tabList.querySelectorAll('[role="tab"]');
+		expect(tabTriggers.length).toBe(tabs.length);
+	});
+
+	test('can navigate tabs with arrow keys', async () => {
+		const { getByTestId } = render(TabsTest, {
+			tabValues: tabs,
+		});
+
+		const firstTrigger = getByTestId('1-trigger');
+		firstTrigger.focus();
+
+		for (const tab of tabs) {
+			expect(getByTestId(`${tab}-trigger`)).toHaveFocus();
+			await userEvent.keyboard(`{${kbd.ARROW_RIGHT}}`);
+		}
+
+		const lastTrigger = getByTestId(`${tabs[tabs.length - 1]}-trigger`);
+		lastTrigger.focus();
+
+		for (const tab of [...tabs].reverse()) {
+			expect(getByTestId(`${tab}-trigger`)).toHaveFocus();
+			await userEvent.keyboard(`{${kbd.ARROW_LEFT}}`);
+		}
+	});
+});
diff --git a/src/tests/tabs/TabsTest.svelte b/src/tests/tabs/TabsTest.svelte
new file mode 100644
index 00000000..490fa1cf
--- /dev/null
+++ b/src/tests/tabs/TabsTest.svelte
@@ -0,0 +1,32 @@
+<script lang="ts">
+	import { type CreateTabsProps, melt, createTabs } from '$lib/index.js';
+
+	type $$Props = CreateTabsProps & {
+		tabValues: string[];
+	};
+
+	export let tabValues: string[] = [];
+
+	const {
+		elements: { content, root, list, trigger },
+	} = createTabs({
+		...($$restProps as $$Props),
+	});
+</script>
+
+<main>
+	<div use:melt={$root} data-testid="root">
+		<div use:melt={$list} data-testid="list">
+			{#each tabValues as tab}
+				<button use:melt={$trigger(tab)} data-testid="{tab}-trigger">
+					{tab}
+				</button>
+			{/each}
+		</div>
+		{#each tabValues as tab}
+			<div use:melt={$content(tab)} data-testid="{tab}-content">
+				{tab}
+			</div>
+		{/each}
+	</div>
+</main>