Repository: melt-ui/melt-ui
Pull request #1050 (2024-03-04 12:18:57)

fix: listbox arrowSize jsdoc default
In the listbox builder, there is no default value for `arrowSize`. However the JSDoc claims the default is 8. 
I set the default in the JSDoc to `undefined`.

![image](https://github.com/melt-ui/melt-ui/assets/53095479/8464d5a9-4184-48a5-a71f-e5946427a1ee)


changeset-bot[bot]: ###  ⚠️  No Changeset found

Latest commit: dd8a1fe2f35e7e7534f54d68ab936030f96924a5

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/anatolzak/melt-ui/new/fix/listbox-arrow-size-default-jsdoc?filename=.changeset/strange-wombats-wash.md&value=---%0A%22%40melt-ui%2Fsvelte%22%3A%20patch%0A---%0A%0Afix%20listbox%20arrowSize%20jsdoc%20default%0A)



Patch:
diff --git a/.changeset/fifty-pigs-promise.md b/.changeset/fifty-pigs-promise.md
new file mode 100644
index 00000000..c749be3b
--- /dev/null
+++ b/.changeset/fifty-pigs-promise.md
@@ -0,0 +1,5 @@
+---
+'@melt-ui/svelte': patch
+---
+
+Fixed bug where focus trap deactivates in dialog and popover when clicking outside provided `closeOnOutsideClick: false` (closes #1084)
diff --git a/.changeset/giant-queens-drive.md b/.changeset/giant-queens-drive.md
new file mode 100644
index 00000000..6f55d3ea
--- /dev/null
+++ b/.changeset/giant-queens-drive.md
@@ -0,0 +1,5 @@
+---
+"@melt-ui/svelte": patch
+---
+
+Combobox: menu no longer closes when composing (Closes: #1106)
diff --git a/.changeset/many-rockets-cough.md b/.changeset/many-rockets-cough.md
new file mode 100644
index 00000000..ef373a82
--- /dev/null
+++ b/.changeset/many-rockets-cough.md
@@ -0,0 +1,5 @@
+---
+'@melt-ui/svelte': patch
+---
+
+Fixed bug where focus trap deactivates in dialog and popover when pressing escape provided `closeOnEscape: false` (closes #1091)
diff --git a/.changeset/sharp-files-deliver.md b/.changeset/sharp-files-deliver.md
new file mode 100644
index 00000000..a6162305
--- /dev/null
+++ b/.changeset/sharp-files-deliver.md
@@ -0,0 +1,5 @@
+---
+'@melt-ui/svelte': patch
+---
+
+Fixed memory leak caused by race conditions for various components (closes #1086)
diff --git a/.changeset/silly-eels-search.md b/.changeset/silly-eels-search.md
new file mode 100644
index 00000000..01c56cb6
--- /dev/null
+++ b/.changeset/silly-eels-search.md
@@ -0,0 +1,5 @@
+---
+"@melt-ui/svelte": patch
+---
+
+performance: avoid effect multiple runs
diff --git a/.changeset/slimy-years-film.md b/.changeset/slimy-years-film.md
new file mode 100644
index 00000000..24e3b06f
--- /dev/null
+++ b/.changeset/slimy-years-film.md
@@ -0,0 +1,5 @@
+---
+"@melt-ui/svelte": patch
+---
+
+Listbox: correct `arrowSize` JSDoc defaultValue
diff --git a/.changeset/witty-pandas-argue.md b/.changeset/witty-pandas-argue.md
new file mode 100644
index 00000000..c492fae5
--- /dev/null
+++ b/.changeset/witty-pandas-argue.md
@@ -0,0 +1,5 @@
+---
+'@melt-ui/svelte': patch
+---
+
+Popover: fixed a bug that has the potential to introduce a memory leak via the overlay
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 8a67c523..67262874 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,6 +1,6 @@
 # These are supported funding model platforms
 
-github: [tglide]
+github: [tglide, huntabyte]
 patreon: # Replace with a single Patreon username
 open_collective: # Replace with a single Open Collective username
 ko_fi: thomasglopes
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3cdd6cb4..96bb9e73 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,29 @@
 # @melt-ui/svelte
 
+## 0.76.0
+
+### Minor Changes
+
+- fix: datepicker not syncing with calendar + add defaults from calendar to datepicker ([#1076](https://github.com/melt-ui/melt-ui/pull/1076))
+
+### Patch Changes
+
+- Fix incorrect arrow position for Tooltip, Popover, Listbox, Link Preview provided forceVisible: false (closes #1048) ([#1049](https://github.com/melt-ui/melt-ui/pull/1049))
+
+- fix: months not updating when changing options ([#1070](https://github.com/melt-ui/melt-ui/pull/1070))
+
+- Floating: `data-side` & `data-align` for floating content ([#1082](https://github.com/melt-ui/melt-ui/pull/1082))
+
+- fix: months not updating when changing options (range calendar) ([#1072](https://github.com/melt-ui/melt-ui/pull/1072))
+
+## 0.75.3
+
+### Patch Changes
+
+- fix(Tooltip, Link Preview, Menu, Popover, Listbox) fixed bug where content jumps to top left of page during external unmounting when using out transition on the content and else if block to render the content (closes #1058, #1039) ([#1059](https://github.com/melt-ui/melt-ui/pull/1059))
+
+- Portalled Elements: fixed a bug where clicking inside a nested 'portalled' element would cause the parent to close ([#1066](https://github.com/melt-ui/melt-ui/pull/1066))
+
 ## 0.75.2
 
 ### Patch Changes
diff --git a/package.json b/package.json
index 21787e82..046b25e9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@melt-ui/svelte",
-	"version": "0.75.2",
+	"version": "0.76.0",
 	"license": "MIT",
 	"repository": "github:melt-ui/melt-ui",
 	"author": "Thomas G. Lopes",
diff --git a/src/docs/components/tooltip.svelte b/src/docs/components/tooltip.svelte
index e381cca6..29fc37d8 100644
--- a/src/docs/components/tooltip.svelte
+++ b/src/docs/components/tooltip.svelte
@@ -1,6 +1,6 @@
 <script lang="ts">
-	import { browser } from '$app/environment';
 	import { createTooltip, melt } from '$lib/index.js';
+	import { fade } from 'svelte/transition';
 
 	const {
 		elements: { trigger, content, arrow },
@@ -20,30 +20,20 @@
 	<slot />
 </div>
 
-<div
-	use:melt={$content}
-	class="z-50 rounded-md bg-neutral-700 px-2 py-1 text-sm text-neutral-50 shadow-sm"
-	data-open={$open ? '' : undefined}
-	class:hidden={!browser}
->
-	<div use:melt={$arrow} />
-	{text}
-</div>
+{#if $open}
+	<div
+		use:melt={$content}
+		in:fade={{ duration: 150 }}
+		class="z-50 rounded-md bg-neutral-700 px-2 py-1 text-sm text-neutral-50 shadow-sm"
+	>
+		<div use:melt={$arrow} />
+		{text}
+	</div>
+{/if}
 
 <style lang="postcss">
 	[data-melt-tooltip-trigger] {
 		display: grid;
 		place-items: center;
 	}
-
-	[data-melt-tooltip-content] {
-		opacity: 0;
-		visibility: hidden;
-		transition: opacity 150ms ease;
-
-		&[data-open] {
-			opacity: 1;
-			visibility: visible;
-		}
-	}
 </style>
diff --git a/src/docs/data/builders/combobox.ts b/src/docs/data/builders/combobox.ts
index 443b9fff..71c3b728 100644
--- a/src/docs/data/builders/combobox.ts
+++ b/src/docs/data/builders/combobox.ts
@@ -1,6 +1,6 @@
 import { ATTRS, KBD, PROPS, SEE } from '$docs/constants.js';
 import type { KeyboardSchema } from '$docs/types.js';
-import { builderSchema, elementSchema } from '$docs/utils/index.js';
+import { builderSchema, elementSchema, floatingSideAndAlignDataAttrs } from '$docs/utils/index.js';
 import { comboboxEvents } from '$lib/builders/combobox/events.js';
 import { listboxIdParts } from '$lib/builders/listbox/create.js';
 import type { BuilderData } from './index.js';
@@ -153,6 +153,7 @@ const builder = builderSchema(BUILDER_NAME, {
 const menu = elementSchema('menu', {
 	description: 'The combobox menu element',
 	dataAttributes: [
+		...floatingSideAndAlignDataAttrs,
 		{
 			name: 'data-melt-combobox-menu',
 			value: ATTRS.MELT('menu'),
diff --git a/src/docs/data/builders/link-preview.ts b/src/docs/data/builders/link-preview.ts
index e37da8ab..9fa6f412 100644
--- a/src/docs/data/builders/link-preview.ts
+++ b/src/docs/data/builders/link-preview.ts
@@ -1,5 +1,5 @@
 import { ATTRS, PROPS } from '$docs/constants.js';
-import { builderSchema, elementSchema } from '$docs/utils/index.js';
+import { builderSchema, elementSchema, floatingSideAndAlignDataAttrs } from '$docs/utils/index.js';
 import { linkPreviewIdParts } from '$lib/index.js';
 import { linkPreviewEvents } from '$lib/builders/link-preview/events.js';
 import type { BuilderData } from './index.js';
@@ -77,6 +77,7 @@ const trigger = elementSchema('trigger', {
 const content = elementSchema('content', {
 	description: 'The content displayed in the linkpreview',
 	dataAttributes: [
+		...floatingSideAndAlignDataAttrs,
 		{
 			name: 'data-state',
 			value: ATTRS.OPEN_CLOSED,
diff --git a/src/docs/data/builders/menu.ts b/src/docs/data/builders/menu.ts
index 6c90cb76..a7846582 100644
--- a/src/docs/data/builders/menu.ts
+++ b/src/docs/data/builders/menu.ts
@@ -7,6 +7,8 @@ import {
 	genElements,
 	genProps,
 	propsToOptions,
+	floatingSideAndAlignDataAttrs,
+	floatingSideDataAttr,
 } from '$docs/utils/index.js';
 
 import { menuEvents } from '$lib/builders/menu/events.js';
@@ -194,6 +196,7 @@ function getMenuMenuSchema(builderName: string) {
 	return elementSchema('menu', {
 		description: `The element which wraps the entire menu.`,
 		dataAttributes: [
+			...floatingSideAndAlignDataAttrs,
 			{
 				name: 'data-state',
 				value: ATTRS.OPEN_CLOSED,
@@ -228,6 +231,7 @@ export function getMenuArrowSchema(builderName: string) {
 	return elementSchema('arrow', {
 		description: `An optional arrow element which points to the menu's trigger.`,
 		dataAttributes: [
+			floatingSideDataAttr,
 			{
 				name: 'data-arrow',
 				value: ATTRS.TRUE,
@@ -370,6 +374,7 @@ function getMenuSubmenuSchema(name: string) {
 	return elementSchema('submenu', {
 		description: 'A submenu element displayed when its trigger is selected.',
 		dataAttributes: [
+			...floatingSideAndAlignDataAttrs,
 			{
 				name: 'data-state',
 				value: ATTRS.OPEN_CLOSED,
diff --git a/src/docs/data/builders/popover.ts b/src/docs/data/builders/popover.ts
index 9d978a74..d31d4173 100644
--- a/src/docs/data/builders/popover.ts
+++ b/src/docs/data/builders/popover.ts
@@ -1,6 +1,11 @@
 import { ATTRS, KBD, PROPS } from '$docs/constants.js';
 import type { KeyboardSchema } from '$docs/types.js';
-import { builderSchema, elementSchema } from '$docs/utils/index.js';
+import {
+	builderSchema,
+	elementSchema,
+	floatingSideAndAlignDataAttrs,
+	floatingSideDataAttr,
+} from '$docs/utils/index.js';
 import { popoverIdParts } from '$lib/index.js';
 import { popoverEvents } from '$lib/builders/popover/events.js';
 import type { BuilderData } from './index.js';
@@ -82,6 +87,7 @@ const trigger = elementSchema('trigger', {
 const content = elementSchema('content', {
 	description: 'The popover content.',
 	dataAttributes: [
+		...floatingSideAndAlignDataAttrs,
 		{
 			name: 'data-state',
 			value: ATTRS.OPEN_CLOSED,
@@ -112,6 +118,7 @@ const arrow = elementSchema('arrow', {
 	title: 'arrow',
 	description: 'The optional arrow element.',
 	dataAttributes: [
+		floatingSideDataAttr,
 		{
 			name: 'data-arrow',
 			value: ATTRS.TRUE,
diff --git a/src/docs/data/builders/select.ts b/src/docs/data/builders/select.ts
index 264c7016..6eb233a3 100644
--- a/src/docs/data/builders/select.ts
+++ b/src/docs/data/builders/select.ts
@@ -1,6 +1,11 @@
 import { ATTRS, KBD, PROPS, SEE } from '$docs/constants.js';
 import type { KeyboardSchema } from '$docs/types.js';
-import { builderSchema, elementSchema } from '$docs/utils/index.js';
+import {
+	builderSchema,
+	elementSchema,
+	floatingSideAndAlignDataAttrs,
+	floatingSideDataAttr,
+} from '$docs/utils/index.js';
 import { listboxIdParts } from '$lib/builders/listbox/create.js';
 import { selectEvents } from '$lib/builders/select/events.js';
 import type { BuilderData } from './index.js';
@@ -154,6 +159,7 @@ const trigger = elementSchema('trigger', {
 const menu = elementSchema('menu', {
 	description: 'The menu element',
 	dataAttributes: [
+		...floatingSideAndAlignDataAttrs,
 		{
 			name: 'data-melt-select-menu',
 			value: ATTRS.MELT('menu'),
@@ -216,6 +222,7 @@ const label = elementSchema('label', {
 const arrow = elementSchema('arrow', {
 	description: 'The optional arrow element',
 	dataAttributes: [
+		floatingSideDataAttr,
 		{
 			name: 'data-arrow',
 			value: ATTRS.TRUE,
diff --git a/src/docs/data/builders/tooltip.ts b/src/docs/data/builders/tooltip.ts
index 9ebdd71a..05243a89 100644
--- a/src/docs/data/builders/tooltip.ts
+++ b/src/docs/data/builders/tooltip.ts
@@ -1,6 +1,11 @@
 import { ATTRS, KBD, PROPS } from '$docs/constants.js';
 import type { KeyboardSchema } from '$docs/types.js';
-import { builderSchema, elementSchema } from '$docs/utils/index.js';
+import {
+	builderSchema,
+	elementSchema,
+	floatingSideAndAlignDataAttrs,
+	floatingSideDataAttr,
+} from '$docs/utils/index.js';
 import { tooltipIdParts } from '$lib/index.js';
 import { tooltipEvents } from '$lib/builders/tooltip/events.js';
 import type { BuilderData } from './index.js';
@@ -99,6 +104,7 @@ const content = elementSchema('content', {
 			name: 'data-state',
 			value: ATTRS.OPEN_CLOSED,
 		},
+		...floatingSideAndAlignDataAttrs,
 		{
 			name: 'data-melt-tooltip-content',
 			value: ATTRS.MELT('tooltip content'),
@@ -114,6 +120,7 @@ const arrow = elementSchema('arrow', {
 			name: 'data-arrow',
 			value: ATTRS.TRUE,
 		},
+		floatingSideDataAttr,
 		{
 			name: 'data-melt-tooltip-arrow',
 			value: ATTRS.MELT('tooltip arrow'),
diff --git a/src/docs/utils/content.ts b/src/docs/utils/content.ts
index 9fca1509..498bc192 100644
--- a/src/docs/utils/content.ts
+++ b/src/docs/utils/content.ts
@@ -136,3 +136,16 @@ export function elementSchema(name: string, schema: ElementSchema): APISchema {
 		events: customEvents,
 	};
 }
+
+export const floatingSideDataAttr = {
+	name: 'data-side',
+	value: "'top' | 'right' | 'bottom' | 'left'",
+};
+
+export const floatingSideAndAlignDataAttrs = [
+	floatingSideDataAttr,
+	{
+		name: 'data-align',
+		value: "'start' | 'center' | 'end'",
+	},
+];
diff --git a/src/lib/builders/calendar/create.ts b/src/lib/builders/calendar/create.ts
index b3073b0a..1987b1ba 100644
--- a/src/lib/builders/calendar/create.ts
+++ b/src/lib/builders/calendar/create.ts
@@ -44,7 +44,7 @@ import { derived, writable } from 'svelte/store';
 import type { CalendarEvents } from './events.js';
 import type { CalendarValue, CreateCalendarProps } from './types.js';
 
-const defaults = {
+export const defaults = {
 	isDateDisabled: undefined,
 	isDateUnavailable: undefined,
 	value: undefined,
@@ -514,20 +514,48 @@ export function createCalendar<
 	 * Updates the displayed months based on changes in the placeholder value,
 	 * which determines the months to show in the calendar.
 	 */
-	effect(
-		[placeholder, weekStartsOn, locale, fixedWeeks, numberOfMonths],
-		([$placeholder, $weekStartsOn, $locale, $fixedWeeks, $numberOfMonths]) => {
-			if (!isBrowser || !$placeholder) return;
+	effect([placeholder], ([$placeholder]) => {
+		if (!isBrowser || !$placeholder) return;
 
-			const $visibleMonths = visibleMonths.get();
+		const $visibleMonths = visibleMonths.get();
 
-			/**
-			 * If the placeholder's month is already in the visible months,
-			 * we don't need to do anything.
-			 */
-			if ($visibleMonths.some((month) => isSameMonth(month, $placeholder))) {
-				return;
-			}
+		/**
+		 * If the placeholder's month is already in the visible months,
+		 * we don't need to do anything.
+		 */
+		if ($visibleMonths.some((month) => isSameMonth(month, $placeholder))) {
+			return;
+		}
+
+		const $weekStartsOn = weekStartsOn.get();
+		const $locale = locale.get();
+		const $fixedWeeks = fixedWeeks.get();
+		const $numberOfMonths = numberOfMonths.get();
+
+		const defaultMonthProps = {
+			weekStartsOn: $weekStartsOn,
+			locale: $locale,
+			fixedWeeks: $fixedWeeks,
+			numberOfMonths: $numberOfMonths,
+		};
+
+		months.set(
+			createMonths({
+				...defaultMonthProps,
+				dateObj: $placeholder,
+			})
+		);
+	});
+
+	/**
+	 * Updates the displayed months based on changes in the the options values,
+	 * which determines the months to show in the calendar.
+	 */
+	effect(
+		[weekStartsOn, locale, fixedWeeks, numberOfMonths],
+		([$weekStartsOn, $locale, $fixedWeeks, $numberOfMonths]) => {
+			const $placeholder = placeholder.get();
+			if (!isBrowser || !$placeholder) return;
 
 			const defaultMonthProps = {
 				weekStartsOn: $weekStartsOn,
diff --git a/src/lib/builders/context-menu/create.ts b/src/lib/builders/context-menu/create.ts
index 11099d65..7365de14 100644
--- a/src/lib/builders/context-menu/create.ts
+++ b/src/lib/builders/context-menu/create.ts
@@ -159,9 +159,10 @@ export function createContextMenu(props?: CreateContextMenuProps) {
 					unsubPopper();
 					if (!$isVisible || !$rootActiveTrigger) return;
 					tick().then(() => {
+						unsubPopper();
 						setMeltMenuAttribute(node, selector);
 						const $virtual = virtual.get();
-						const popper = usePopper(node, {
+						unsubPopper = usePopper(node, {
 							anchorElement: $virtual ? $virtual : $rootActiveTrigger,
 							open: rootOpen,
 							options: {
@@ -177,9 +178,7 @@ export function createContextMenu(props?: CreateContextMenuProps) {
 								portal: getPortalDestination(node, $portal),
 								escapeKeydown: $closeOnEscape ? undefined : null,
 							},
-						});
-						if (!popper || !popper.destroy) return;
-						unsubPopper = popper.destroy;
+						}).destroy;
 					});
 				}
 			);
diff --git a/src/lib/builders/date-picker/create.ts b/src/lib/builders/date-picker/create.ts
index 3adbfce7..090a273a 100644
--- a/src/lib/builders/date-picker/create.ts
+++ b/src/lib/builders/date-picker/create.ts
@@ -16,6 +16,7 @@ import { pickerOpenFocus } from '$lib/internal/helpers/date/focus.js';
 import { createFormatter, dateStore, getDefaultDate } from '$lib/internal/helpers/date/index.js';
 import type { MeltActionReturn } from '$lib/internal/types.js';
 import type { DatePickerEvents } from './events.js';
+import { defaults as calendarDefaults } from '../calendar/create.js';
 
 const defaults = {
 	isDateDisabled: undefined,
@@ -36,6 +37,18 @@ const defaults = {
 	minValue: undefined,
 	maxValue: undefined,
 	weekdayFormat: 'narrow',
+	...omit(
+		calendarDefaults,
+		'isDateDisabled',
+		'isDateUnavailable',
+		'value',
+		'locale',
+		'disabled',
+		'readonly',
+		'minValue',
+		'maxValue',
+		'weekdayFormat'
+	),
 } satisfies CreateDatePickerProps;
 
 export function createDatePicker(props?: CreateDatePickerProps) {
@@ -131,6 +144,18 @@ export function createDatePicker(props?: CreateDatePickerProps) {
 		calendar.options.maxValue.set($maxValue);
 	});
 
+	effect([options.numberOfMonths], ([$numberOfMonths]) => {
+		calendar.options.numberOfMonths.set($numberOfMonths);
+	});
+
+	effect([options.fixedWeeks], ([$fixedWeeks]) => {
+		calendar.options.fixedWeeks.set($fixedWeeks);
+	});
+
+	effect([options.weekStartsOn], ([$weekStartsOn]) => {
+		calendar.options.weekStartsOn.set($weekStartsOn);
+	});
+
 	const dateFieldOptions = omit(
 		dateField.options,
 		'locale',
diff --git a/src/lib/builders/dialog/create.ts b/src/lib/builders/dialog/create.ts
index 9f028bb2..35b98984 100644
--- a/src/lib/builders/dialog/create.ts
+++ b/src/lib/builders/dialog/create.ts
@@ -186,26 +186,30 @@ export function createDialog(props?: CreateDialogProps) {
 			let deactivate = noop;
 
 			const destroy = executeCallbacks(
-				effect([open], ([$open]) => {
-					if (!$open) return;
-
-					const focusTrap = createFocusTrap({
-						immediate: false,
-						escapeDeactivates: true,
-						clickOutsideDeactivates: true,
-						returnFocusOnDeactivate: false,
-						fallbackFocus: node,
-					});
-
-					activate = focusTrap.activate;
-					deactivate = focusTrap.deactivate;
-					const ac = focusTrap.useFocusTrap(node);
-					if (ac && ac.destroy) {
-						return ac.destroy;
-					} else {
-						return focusTrap.deactivate;
+				effect(
+					[open, closeOnOutsideClick, closeOnEscape],
+					([$open, $closeOnOutsideClick, $closeOnEscape]) => {
+						if (!$open) return;
+
+						const focusTrap = createFocusTrap({
+							immediate: false,
+							escapeDeactivates: $closeOnEscape,
+							clickOutsideDeactivates: $closeOnOutsideClick,
+							allowOutsideClick: true,
+							returnFocusOnDeactivate: false,
+							fallbackFocus: node,
+						});
+
+						activate = focusTrap.activate;
+						deactivate = focusTrap.deactivate;
+						const ac = focusTrap.useFocusTrap(node);
+						if (ac && ac.destroy) {
+							return ac.destroy;
+						} else {
+							return focusTrap.deactivate;
+						}
 					}
-				}),
+				),
 				effect([closeOnOutsideClick, open], ([$closeOnOutsideClick, $open]) => {
 					return useModal(node, {
 						open: $open,
@@ -223,15 +227,7 @@ export function createDialog(props?: CreateDialogProps) {
 				effect([closeOnEscape], ([$closeOnEscape]) => {
 					if (!$closeOnEscape) return noop;
 
-					const escapeKeydown = useEscapeKeydown(node, {
-						handler: () => {
-							handleClose();
-						},
-					});
-					if (escapeKeydown && escapeKeydown.destroy) {
-						return escapeKeydown.destroy;
-					}
-					return noop;
+					return useEscapeKeydown(node, { handler: handleClose }).destroy;
 				}),
 
 				effect([isVisible], ([$isVisible]) => {
@@ -264,12 +260,7 @@ export function createDialog(props?: CreateDialogProps) {
 				if ($portal === null) return noop;
 				const portalDestination = getPortalDestination(node, $portal);
 				if (portalDestination === null) return noop;
-				const portalAction = usePortal(node, portalDestination);
-				if (portalAction && portalAction.destroy) {
-					return portalAction.destroy;
-				} else {
-					return noop;
-				}
+				return usePortal(node, portalDestination).destroy;
 			});
 
 			return {
diff --git a/src/lib/builders/link-preview/create.ts b/src/lib/builders/link-preview/create.ts
index bb013909..3d5ea706 100644
--- a/src/lib/builders/link-preview/create.ts
+++ b/src/lib/builders/link-preview/create.ts
@@ -28,6 +28,7 @@ import { generateIds } from '../../internal/helpers/id.js';
 import { omit } from '../../internal/helpers/object.js';
 import type { LinkPreviewEvents } from './events.js';
 import type { CreateLinkPreviewProps } from './types.js';
+import { tick } from 'svelte';
 
 type LinkPreviewParts = 'trigger' | 'content' | 'arrow';
 const { name } = createElHelpers<LinkPreviewParts>('hover-card');
@@ -189,35 +190,37 @@ export function createLinkPreview(props: CreateLinkPreviewProps = {}) {
 					unsubPopper();
 					if (!$isVisible || !$activeTrigger) return;
 
-					const popper = usePopper(node, {
-						anchorElement: $activeTrigger,
-						open: open,
-						options: {
-							floating: $positioning,
-							modal: {
-								closeOnInteractOutside: $closeOnOutsideClick,
-								onClose: () => {
-									open.set(false);
-									$activeTrigger.focus();
+					tick().then(() => {
+						unsubPopper();
+						unsubPopper = usePopper(node, {
+							anchorElement: $activeTrigger,
+							open: open,
+							options: {
+								floating: $positioning,
+								modal: {
+									closeOnInteractOutside: $closeOnOutsideClick,
+									onClose: () => {
+										open.set(false);
+										$activeTrigger.focus();
+									},
+									shouldCloseOnInteractOutside: (e) => {
+										onOutsideClick.get()?.(e);
+										if (e.defaultPrevented) return false;
+										if (
+											isHTMLElement($activeTrigger) &&
+											$activeTrigger.contains(e.target as Element)
+										)
+											return false;
+										return true;
+									},
+									open: $isVisible,
 								},
-								shouldCloseOnInteractOutside: (e) => {
-									onOutsideClick.get()?.(e);
-									if (e.defaultPrevented) return false;
-									if (isHTMLElement($activeTrigger) && $activeTrigger.contains(e.target as Element))
-										return false;
-									return true;
-								},
-								open: $isVisible,
+								portal: getPortalDestination(node, $portal),
+								focusTrap: null,
+								escapeKeydown: $closeOnEscape ? undefined : null,
 							},
-							portal: getPortalDestination(node, $portal),
-							focusTrap: null,
-							escapeKeydown: $closeOnEscape ? undefined : null,
-						},
+						}).destroy;
 					});
-
-					if (popper && popper.destroy) {
-						unsubPopper = popper.destroy;
-					}
 				}
 			);
 
diff --git a/src/lib/builders/listbox/create.ts b/src/lib/builders/listbox/create.ts
index f15a19fe..2d8bc620 100644
--- a/src/lib/builders/listbox/create.ts
+++ b/src/lib/builders/listbox/create.ts
@@ -32,7 +32,6 @@ import {
 	prev,
 	removeHighlight,
 	removeScroll,
-	sleep,
 	stripValues,
 	styleToString,
 	toWritableStores,
@@ -213,7 +212,7 @@ export function createListbox<
 		if (!triggerEl) return;
 
 		// The active trigger is used to anchor the menu to the input element.
-		activeTrigger.set(triggerEl);
+		if (triggerEl !== activeTrigger.get()) activeTrigger.set(triggerEl);
 
 		// Wait a tick for the menu to open then highlight the selected item.
 		await tick();
@@ -227,8 +226,7 @@ export function createListbox<
 	}
 
 	/** Closes the menu & clears the active trigger */
-	async function closeMenu() {
-		await sleep(0);
+	function closeMenu() {
 		open.set(false);
 		highlightedItem.set(null);
 	}
@@ -368,7 +366,10 @@ export function createListbox<
 						return;
 					}
 					// Pressing enter with a highlighted item should select it.
-					if (e.key === kbd.ENTER || (e.key === kbd.SPACE && isHTMLButtonElement(node))) {
+					if (
+						(e.key === kbd.ENTER && !e.isComposing) ||
+						(e.key === kbd.SPACE && isHTMLButtonElement(node))
+					) {
 						e.preventDefault();
 						const $highlightedItem = highlightedItem.get();
 						if ($highlightedItem) {
@@ -481,39 +482,39 @@ export function createListbox<
 
 						if (!$isVisible || !$activeTrigger) return;
 
-						const ignoreHandler = createClickOutsideIgnore(ids.trigger.get());
-
-						const popper = usePopper(node, {
-							anchorElement: $activeTrigger,
-							open,
-							options: {
-								floating: $positioning,
-								focusTrap: null,
-								modal: {
-									closeOnInteractOutside: $closeOnOutsideClick,
-									onClose: closeMenu,
-									open: $isVisible,
-									shouldCloseOnInteractOutside: (e) => {
-										onOutsideClick.get()?.(e);
-										if (e.defaultPrevented) return false;
-										const target = e.target;
-										if (!isElement(target)) return false;
-										if (target === $activeTrigger || $activeTrigger.contains(target)) {
-											return false;
-										}
-										// return opposite of the result of the ignoreHandler
-										if (ignoreHandler(e)) return false;
-										return true;
+						tick().then(() => {
+							unsubPopper();
+							const ignoreHandler = createClickOutsideIgnore(ids.trigger.get());
+
+							unsubPopper = usePopper(node, {
+								anchorElement: $activeTrigger,
+								open,
+								options: {
+									floating: $positioning,
+									focusTrap: null,
+									modal: {
+										closeOnInteractOutside: $closeOnOutsideClick,
+										onClose: closeMenu,
+										open: $isVisible,
+										shouldCloseOnInteractOutside: (e) => {
+											onOutsideClick.get()?.(e);
+											if (e.defaultPrevented) return false;
+											const target = e.target;
+											if (!isElement(target)) return false;
+											if (target === $activeTrigger || $activeTrigger.contains(target)) {
+												return false;
+											}
+											// return opposite of the result of the ignoreHandler
+											if (ignoreHandler(e)) return false;
+											return true;
+										},
 									},
-								},
 
-								escapeKeydown: null,
-								portal: getPortalDestination(node, $portal),
-							},
+									escapeKeydown: null,
+									portal: getPortalDestination(node, $portal),
+								},
+							}).destroy;
 						});
-						if (popper && popper.destroy) {
-							unsubPopper = popper.destroy;
-						}
 					}
 				)
 			);
diff --git a/src/lib/builders/listbox/types.ts b/src/lib/builders/listbox/types.ts
index a35c9a01..550b0ef5 100644
--- a/src/lib/builders/listbox/types.ts
+++ b/src/lib/builders/listbox/types.ts
@@ -30,7 +30,7 @@ export type CreateListboxProps<
 
 	/**
 	 * The size of the arrow in pixels.
-	 * @default 8
+	 * @default undefined
 	 */
 	arrowSize?: number;
 
diff --git a/src/lib/builders/menu/create.ts b/src/lib/builders/menu/create.ts
index c275a30a..da9ac52e 100644
--- a/src/lib/builders/menu/create.ts
+++ b/src/lib/builders/menu/create.ts
@@ -185,8 +185,9 @@ export function createMenuBuilder(opts: _MenuBuilderOptions) {
 					unsubPopper();
 					if (!$isVisible || !$rootActiveTrigger) return;
 					tick().then(() => {
+						unsubPopper();
 						setMeltMenuAttribute(node, selector);
-						const popper = usePopper(node, {
+						unsubPopper = usePopper(node, {
 							anchorElement: $rootActiveTrigger,
 							open: rootOpen,
 							options: {
@@ -214,11 +215,7 @@ export function createMenuBuilder(opts: _MenuBuilderOptions) {
 								portal: getPortalDestination(node, $portal),
 								escapeKeydown: $closeOnEscape ? undefined : null,
 							},
-						});
-
-						if (popper && popper.destroy) {
-							unsubPopper = popper.destroy;
-						}
+						}).destroy;
 					});
 				}
 			);
@@ -369,12 +366,7 @@ export function createMenuBuilder(opts: _MenuBuilderOptions) {
 				if ($portal === null) return noop;
 				const portalDestination = getPortalDestination(node, $portal);
 				if (portalDestination === null) return noop;
-				const portalAction = usePortal(node, portalDestination);
-				if (portalAction && portalAction.destroy) {
-					return portalAction.destroy;
-				} else {
-					return noop;
-				}
+				return usePortal(node, portalDestination).destroy;
 			});
 
 			return {
@@ -792,9 +784,10 @@ export function createMenuBuilder(opts: _MenuBuilderOptions) {
 						const activeTrigger = subActiveTrigger.get();
 						if (!activeTrigger) return;
 						tick().then(() => {
+							unsubPopper();
 							const parentMenuEl = getParentMenu(activeTrigger);
 
-							const popper = usePopper(node, {
+							unsubPopper = usePopper(node, {
 								anchorElement: activeTrigger,
 								open: subOpen,
 								options: {
@@ -804,11 +797,7 @@ export function createMenuBuilder(opts: _MenuBuilderOptions) {
 									focusTrap: null,
 									escapeKeydown: null,
 								},
-							});
-
-							if (popper && popper.destroy) {
-								unsubPopper = popper.destroy;
-							}
+							}).destroy;
 						});
 					}
 				);
diff --git a/src/lib/builders/menubar/create.ts b/src/lib/builders/menubar/create.ts
index 0eaff1f0..9777b3cb 100644
--- a/src/lib/builders/menubar/create.ts
+++ b/src/lib/builders/menubar/create.ts
@@ -163,7 +163,8 @@ export function createMenubar(props?: CreateMenubarProps) {
 						if (!($rootOpen && $rootActiveTrigger)) return;
 
 						tick().then(() => {
-							const popper = usePopper(node, {
+							unsubPopper();
+							unsubPopper = usePopper(node, {
 								anchorElement: $rootActiveTrigger,
 								open: rootOpen,
 								options: {
@@ -186,11 +187,7 @@ export function createMenubar(props?: CreateMenubarProps) {
 										open: $rootOpen,
 									},
 								},
-							});
-
-							if (popper && popper.destroy) {
-								unsubPopper = popper.destroy;
-							}
+							}).destroy;
 						});
 					}
 				);
diff --git a/src/lib/builders/popover/create.ts b/src/lib/builders/popover/create.ts
index 9dcae99f..14a0e413 100644
--- a/src/lib/builders/popover/create.ts
+++ b/src/lib/builders/popover/create.ts
@@ -17,9 +17,9 @@ import {
 	removeScroll,
 	styleToString,
 	toWritableStores,
-	sleep,
 	portalAttr,
 	generateIds,
+	withGet,
 } from '$lib/internal/helpers/index.js';
 
 import {
@@ -80,7 +80,7 @@ export function createPopover(args?: CreatePopoverProps) {
 	const openWritable = withDefaults.open ?? writable(withDefaults.defaultOpen);
 	const open = overridable(openWritable, withDefaults?.onOpenChange);
 
-	const activeTrigger = writable<HTMLElement | null>(null);
+	const activeTrigger = withGet.writable<HTMLElement | null>(null);
 
 	const ids = toWritableStores({ ...generateIds(popoverIdParts), ...withDefaults.ids });
 
@@ -88,8 +88,7 @@ export function createPopover(args?: CreatePopoverProps) {
 		activeTrigger.set(document.getElementById(ids.trigger.get()));
 	});
 
-	async function handleClose() {
-		await sleep(0);
+	function handleClose() {
 		open.set(false);
 		const triggerEl = document.getElementById(ids.trigger.get());
 		handleFocus({ prop: closeFocus.get(), defaultEl: triggerEl });
@@ -136,38 +135,38 @@ export function createPopover(args?: CreatePopoverProps) {
 					unsubPopper();
 					if (!$isVisible || !$activeTrigger) return;
 
-					const popper = usePopper(node, {
-						anchorElement: $activeTrigger,
-						open,
-						options: {
-							floating: $positioning,
-							focusTrap: $disableFocusTrap
-								? null
-								: {
-										returnFocusOnDeactivate: false,
-										clickOutsideDeactivates: true,
-										escapeDeactivates: true,
-								  },
-							modal: {
-								shouldCloseOnInteractOutside: shouldCloseOnInteractOutside,
-								onClose: handleClose,
-								open: $isVisible,
-								closeOnInteractOutside: $closeOnOutsideClick,
+					tick().then(() => {
+						unsubPopper();
+						unsubPopper = usePopper(node, {
+							anchorElement: $activeTrigger,
+							open,
+							options: {
+								floating: $positioning,
+								focusTrap: $disableFocusTrap
+									? null
+									: {
+											returnFocusOnDeactivate: false,
+											clickOutsideDeactivates: $closeOnOutsideClick,
+											allowOutsideClick: true,
+											escapeDeactivates: $closeOnEscape,
+									  },
+								modal: {
+									shouldCloseOnInteractOutside: shouldCloseOnInteractOutside,
+									onClose: handleClose,
+									open: $isVisible,
+									closeOnInteractOutside: $closeOnOutsideClick,
+								},
+								escapeKeydown: $closeOnEscape
+									? {
+											handler: () => {
+												handleClose();
+											},
+									  }
+									: null,
+								portal: getPortalDestination(node, $portal),
 							},
-							escapeKeydown: $closeOnEscape
-								? {
-										handler: () => {
-											handleClose();
-										},
-								  }
-								: null,
-							portal: getPortalDestination(node, $portal),
-						},
+						}).destroy;
 					});
-
-					if (popper && popper.destroy) {
-						unsubPopper = popper.destroy;
-					}
 				}
 			);
 
@@ -184,7 +183,7 @@ export function createPopover(args?: CreatePopoverProps) {
 		open.update((prev) => {
 			return !prev;
 		});
-		if (triggerEl) {
+		if (triggerEl && triggerEl !== activeTrigger.get()) {
 			activeTrigger.set(triggerEl);
 		}
 	}
@@ -245,6 +244,8 @@ export function createPopover(args?: CreatePopoverProps) {
 		},
 		action: (node: HTMLElement) => {
 			let unsubEscapeKeydown = noop;
+			let unsubDerived = noop;
+			let unsubPortal = noop;
 
 			if (closeOnEscape.get()) {
 				const escapeKeydown = useEscapeKeydown(node, {
@@ -257,21 +258,18 @@ export function createPopover(args?: CreatePopoverProps) {
 				}
 			}
 
-			const unsubPortal = effect([portal], ([$portal]) => {
-				if ($portal === null) return noop;
+			unsubDerived = effect([portal], ([$portal]) => {
+				unsubPortal();
+				if ($portal === null) return;
 				const portalDestination = getPortalDestination(node, $portal);
-				if (portalDestination === null) return noop;
-				const portalAction = usePortal(node, portalDestination);
-				if (portalAction && portalAction.destroy) {
-					return portalAction.destroy;
-				} else {
-					return noop;
-				}
+				if (portalDestination === null) return;
+				unsubPortal = usePortal(node, portalDestination).destroy;
 			});
 
 			return {
 				destroy() {
 					unsubEscapeKeydown();
+					unsubDerived();
 					unsubPortal();
 				},
 			};
diff --git a/src/lib/builders/range-calendar/create.ts b/src/lib/builders/range-calendar/create.ts
index 9ba83009..a4ff495c 100644
--- a/src/lib/builders/range-calendar/create.ts
+++ b/src/lib/builders/range-calendar/create.ts
@@ -545,20 +545,49 @@ export function createRangeCalendar<T extends DateValue = DateValue>(
 	 * Updates the displayed months based on changes in the placeholder value,
 	 * which determines the months to show in the calendar.
 	 */
-	effect(
-		[placeholder, weekStartsOn, locale, fixedWeeks, numberOfMonths],
-		([$placeholder, $weekStartsOn, $locale, $fixedWeeks, $numberOfMonths]) => {
-			if (!isBrowser || !$placeholder) return;
+	effect([placeholder], ([$placeholder]) => {
+		if (!isBrowser || !$placeholder) return;
 
-			const $visibleMonths = visibleMonths.get();
+		const $visibleMonths = visibleMonths.get();
 
-			/**
-			 * If the placeholder's month is already in the visible months,
-			 * we don't need to do anything.
-			 */
-			if ($visibleMonths.some((month) => isSameMonth(month, $placeholder))) {
-				return;
-			}
+		/**
+		 * If the placeholder's month is already in the visible months,
+		 * we don't need to do anything.
+		 */
+		if ($visibleMonths.some((month) => isSameMonth(month, $placeholder))) {
+			return;
+		}
+
+		const $weekStartsOn = weekStartsOn.get();
+		const $locale = locale.get();
+		const $fixedWeeks = fixedWeeks.get();
+		const $numberOfMonths = numberOfMonths.get();
+
+		const defaultMonthProps = {
+			weekStartsOn: $weekStartsOn,
+			locale: $locale,
+			fixedWeeks: $fixedWeeks,
+			numberOfMonths: $numberOfMonths,
+		};
+
+		months.set(
+			createMonths({
+				...defaultMonthProps,
+				dateObj: $placeholder,
+			})
+		);
+	});
+
+	/**
+	 * Updates the displayed months based on changes in the options values,
+	 * which determines the months to show in the calendar.
+	 */
+	effect(
+		[weekStartsOn, locale, fixedWeeks, numberOfMonths],
+		([$weekStartsOn, $locale, $fixedWeeks, $numberOfMonths]) => {
+			const $placeholder = placeholder.get();
+
+			if (!isBrowser || !$placeholder) return;
 
 			const defaultMonthProps = {
 				weekStartsOn: $weekStartsOn,
diff --git a/src/lib/builders/tooltip/create.ts b/src/lib/builders/tooltip/create.ts
index cd20300d..22985056 100644
--- a/src/lib/builders/tooltip/create.ts
+++ b/src/lib/builders/tooltip/create.ts
@@ -19,7 +19,6 @@ import {
 	styleToString,
 	toWritableStores,
 	removeUndefined,
-	sleep,
 	portalAttr,
 } from '$lib/internal/helpers/index.js';
 
@@ -29,6 +28,7 @@ import { derived, writable, type Writable } from 'svelte/store';
 import { generateIds } from '../../internal/helpers/id.js';
 import type { TooltipEvents } from './events.js';
 import type { CreateTooltipProps } from './types.js';
+import { tick } from 'svelte';
 
 const defaults = {
 	positioning: {
@@ -182,10 +182,7 @@ export function createTooltip(props?: CreateTooltipProps) {
 					if (clickedTrigger) return;
 					openTooltip('focus');
 				}),
-				addMeltEventListener(node, 'blur', async () => {
-					await sleep(0);
-					closeTooltip(true);
-				}),
+				addMeltEventListener(node, 'blur', () => closeTooltip(true)),
 				addMeltEventListener(node, 'keydown', keydownHandler),
 				addEventListener(document, 'keydown', keydownHandler)
 			);
@@ -216,26 +213,18 @@ export function createTooltip(props?: CreateTooltipProps) {
 			const unsubDerived = effect(
 				[isVisible, positioning, portal],
 				([$isVisible, $positioning, $portal]) => {
+					unsubPortal();
+					unsubFloating();
 					const triggerEl = getEl('trigger');
-					if (!$isVisible || !triggerEl) {
-						unsubPortal();
-						unsubFloating();
-						return;
-					}
+					if (!$isVisible || !triggerEl) return;
 
-					const floatingReturn = useFloating(triggerEl, node, $positioning);
-					unsubFloating = floatingReturn.destroy;
-					if ($portal === null) {
+					tick().then(() => {
 						unsubPortal();
-						return;
-					}
-					const portalDest = getPortalDestination(node, $portal);
-					if (portalDest) {
-						const portalReturn = usePortal(node, portalDest);
-						if (portalReturn && portalReturn.destroy) {
-							unsubPortal = portalReturn.destroy;
-						}
-					}
+						unsubFloating();
+						const portalDest = getPortalDestination(node, $portal);
+						if (portalDest) unsubPortal = usePortal(node, portalDest).destroy;
+						unsubFloating = useFloating(triggerEl, node, $positioning).destroy;
+					});
 				}
 			);
 
diff --git a/src/lib/internal/actions/escape-keydown/action.ts b/src/lib/internal/actions/escape-keydown/action.ts
index 4181b555..04c6190c 100644
--- a/src/lib/internal/actions/escape-keydown/action.ts
+++ b/src/lib/internal/actions/escape-keydown/action.ts
@@ -3,6 +3,7 @@ import { isFunction, isHTMLElement, isReadable } from '$lib/internal/helpers/is.
 import { get, readable, type Readable } from 'svelte/store';
 import { effect, executeCallbacks, kbd, noop } from '../../helpers/index.js';
 import type { EscapeKeydownConfig } from './types.js';
+import type { Action } from 'svelte/action';
 
 /**
  * Creates a readable store that tracks the latest Escape Keydown that occurred on the document.
@@ -35,7 +36,7 @@ const documentEscapeKeyStore = readable<KeyboardEvent | undefined>(
 	}
 );
 
-export const useEscapeKeydown = (node: HTMLElement, config: EscapeKeydownConfig = {}) => {
+export const useEscapeKeydown = ((node, config = {}) => {
 	let unsub = noop;
 	function update(config: EscapeKeydownConfig = {}) {
 		unsub();
@@ -96,4 +97,4 @@ export const useEscapeKeydown = (node: HTMLElement, config: EscapeKeydownConfig
 			unsub();
 		},
 	};
-};
+}) satisfies Action<HTMLElement, EscapeKeydownConfig>;
diff --git a/src/lib/internal/actions/floating/action.ts b/src/lib/internal/actions/floating/action.ts
index a1d34801..4847a13e 100644
--- a/src/lib/internal/actions/floating/action.ts
+++ b/src/lib/internal/actions/floating/action.ts
@@ -14,7 +14,7 @@ import {
 } from '@floating-ui/dom';
 import type { FloatingConfig } from './types.js';
 import { isHTMLElement, noop } from '$lib/internal/helpers/index.js';
-import type { VirtualElement } from '@floating-ui/core';
+import type { Placement, VirtualElement } from '@floating-ui/core';
 
 const defaultConfig = {
 	strategy: 'absolute',
@@ -32,6 +32,12 @@ const ARROW_TRANSFORM = {
 	right: 'rotate(315deg)',
 };
 
+const SIDE_OPTIONS = ['top', 'right', 'bottom', 'left'] as const;
+const ALIGN_OPTIONS = ['start', 'center', 'end'] as const;
+
+type Side = (typeof SIDE_OPTIONS)[number];
+type Align = (typeof ALIGN_OPTIONS)[number];
+
 export function useFloating(
 	reference: HTMLElement | VirtualElement | undefined,
 	floating: HTMLElement | undefined,
@@ -100,6 +106,9 @@ export function useFloating(
 
 	function compute() {
 		if (!reference || !floating) return;
+		// if the reference is no longer in the document (e.g. it was removed), ignore it
+		if (isHTMLElement(reference) && !reference.ownerDocument.documentElement.contains(reference))
+			return;
 		const { placement, strategy } = options;
 
 		computePosition(reference, floating, {
@@ -110,6 +119,13 @@ export function useFloating(
 			const x = Math.round(data.x);
 			const y = Math.round(data.y);
 
+			// get the chosen side and align from the placement to apply as attributes
+			// to the floating element and arrow
+			const [side, align] = getSideAndAlignFromPlacement(data.placement);
+
+			floating.setAttribute('data-side', side);
+			floating.setAttribute('data-align', align);
+
 			Object.assign(floating.style, {
 				position: options.strategy,
 				top: `${y}px`,
@@ -121,6 +137,8 @@ export function useFloating(
 
 				const dir = data.placement.split('-')[0] as 'top' | 'bottom' | 'left' | 'right';
 
+				arrowEl.setAttribute('data-side', dir);
+
 				Object.assign(arrowEl.style, {
 					position: 'absolute',
 					left: x != null ? `${x}px` : '',
@@ -145,3 +163,8 @@ export function useFloating(
 		destroy: autoUpdate(reference, floating, compute),
 	};
 }
+
+function getSideAndAlignFromPlacement(placement: Placement) {
+	const [side, align = 'center'] = placement.split('-');
+	return [side as Side, align as Align] as const;
+}
diff --git a/src/lib/internal/actions/focus-trap/action.ts b/src/lib/internal/actions/focus-trap/action.ts
index 4003c4f8..8cd1e7f2 100644
--- a/src/lib/internal/actions/focus-trap/action.ts
+++ b/src/lib/internal/actions/focus-trap/action.ts
@@ -7,7 +7,7 @@ import type { FocusTrapConfig, FocusTrapReturn } from './types.js';
 import { writable, readonly } from 'svelte/store';
 import { createFocusTrap as _createFocusTrap } from 'focus-trap';
 
-export function createFocusTrap(config: FocusTrapConfig = {}): FocusTrapReturn {
+export function createFocusTrap(config: FocusTrapConfig = {}) {
 	let trap: undefined | FocusTrap;
 
 	const { immediate, ...focusTrapOptions } = config;
@@ -66,5 +66,5 @@ export function createFocusTrap(config: FocusTrapConfig = {}): FocusTrapReturn {
 		deactivate,
 		pause,
 		unpause,
-	};
+	} satisfies FocusTrapReturn;
 }
diff --git a/src/lib/internal/actions/interact-outside/action.ts b/src/lib/internal/actions/interact-outside/action.ts
index 4fcdbb2d..dd48b5b7 100644
--- a/src/lib/internal/actions/interact-outside/action.ts
+++ b/src/lib/internal/actions/interact-outside/action.ts
@@ -5,8 +5,9 @@ import {
 	noop,
 } from '$lib/internal/helpers/index.js';
 import type { InteractOutsideConfig, InteractOutsideEvent } from './types.js';
+import type { Action } from 'svelte/action';
 
-export function useInteractOutside(node: HTMLElement, config: InteractOutsideConfig) {
+export const useInteractOutside = ((node, config) => {
 	let unsub = noop;
 
 	let isPointerDown = false;
@@ -96,7 +97,7 @@ export function useInteractOutside(node: HTMLElement, config: InteractOutsideCon
 		update,
 		destroy: unsub,
 	};
-}
+}) satisfies Action<HTMLElement, InteractOutsideConfig>;
 
 function isValidEvent(e: InteractOutsideEvent, node: HTMLElement): boolean {
 	if ('button' in e && e.button > 0) return false;
diff --git a/src/lib/internal/actions/modal/action.ts b/src/lib/internal/actions/modal/action.ts
index 5c4fb7b4..8cd452f3 100644
--- a/src/lib/internal/actions/modal/action.ts
+++ b/src/lib/internal/actions/modal/action.ts
@@ -2,10 +2,10 @@ import { isElement, last, noop, sleep } from '$lib/internal/helpers/index.js';
 import type { InteractOutsideEvent } from '../interact-outside/types.js';
 import { useInteractOutside } from '../index.js';
 import type { ModalConfig } from './types.js';
+import type { Action } from 'svelte/action';
 
 const visibleModals: Element[] = [];
-
-export function useModal(node: HTMLElement, config: ModalConfig) {
+export const useModal = ((node, config) => {
 	let unsubInteractOutside = noop;
 
 	function removeNodeFromVisibleModals() {
@@ -69,4 +69,4 @@ export function useModal(node: HTMLElement, config: ModalConfig) {
 			unsubInteractOutside();
 		},
 	};
-}
+}) satisfies Action<HTMLElement, ModalConfig>;
diff --git a/src/lib/internal/actions/popper/action.ts b/src/lib/internal/actions/popper/action.ts
index f17366a3..1b692c76 100644
--- a/src/lib/internal/actions/popper/action.ts
+++ b/src/lib/internal/actions/popper/action.ts
@@ -22,7 +22,7 @@ const defaultConfig = {
 	portal: 'body',
 } satisfies PopperConfig;
 
-export const usePopper: Action<HTMLElement, PopperArgs> = (popperElement, args) => {
+export const usePopper = ((popperElement, args) => {
 	popperElement.dataset.escapee = '';
 	const { anchorElement, open, options } = args as PopperArgs;
 	if (!anchorElement || !open || !options) {
@@ -34,10 +34,7 @@ export const usePopper: Action<HTMLElement, PopperArgs> = (popperElement, args)
 	const callbacks: Callback[] = [];
 
 	if (opts.portal !== null) {
-		const portal = usePortal(popperElement, opts.portal);
-		if (portal?.destroy) {
-			callbacks.push(portal.destroy);
-		}
+		callbacks.push(usePortal(popperElement, opts.portal).destroy);
 	}
 
 	callbacks.push(useFloating(anchorElement, popperElement, opts.floating).destroy);
@@ -53,11 +50,7 @@ export const usePopper: Action<HTMLElement, PopperArgs> = (popperElement, args)
 			...opts.focusTrap,
 		});
 
-		const usedFocusTrap = useFocusTrap(popperElement);
-
-		if (usedFocusTrap?.destroy) {
-			callbacks.push(usedFocusTrap.destroy);
-		}
+		callbacks.push(useFocusTrap(popperElement).destroy);
 	}
 
 	if (opts.modal !== null) {
@@ -103,4 +96,4 @@ export const usePopper: Action<HTMLElement, PopperArgs> = (popperElement, args)
 			unsubscribe();
 		},
 	};
-};
+}) satisfies Action<HTMLElement, PopperArgs>;
diff --git a/src/lib/internal/actions/portal.ts b/src/lib/internal/actions/portal.ts
index 50e0e3c6..f2581099 100644
--- a/src/lib/internal/actions/portal.ts
+++ b/src/lib/internal/actions/portal.ts
@@ -4,7 +4,7 @@ import { isHTMLElement, noop } from '$lib/internal/helpers/index.js';
 
 export type PortalConfig = string | HTMLElement | undefined;
 
-export const usePortal: Action<HTMLElement, PortalConfig> = (el, target = 'body') => {
+export const usePortal = ((el, target = 'body') => {
 	let targetEl;
 
 	if (!isHTMLElement(target) && typeof target !== 'string') {
@@ -47,4 +47,4 @@ export const usePortal: Action<HTMLElement, PortalConfig> = (el, target = 'body'
 		update,
 		destroy,
 	};
-};
+}) satisfies Action<HTMLElement, PortalConfig>;
diff --git a/src/lib/internal/helpers/attr.ts b/src/lib/internal/helpers/attr.ts
index 621863a9..69a6c053 100644
--- a/src/lib/internal/helpers/attr.ts
+++ b/src/lib/internal/helpers/attr.ts
@@ -24,7 +24,7 @@ export const hiddenInputAttrs = {
  */
 export function portalAttr(portal: string | HTMLElement | null | undefined) {
 	if (portal !== null) {
-		return portal;
+		return '';
 	}
 	return undefined;
 }


Test patch:
diff --git a/src/tests/calendar/Calendar.spec.ts b/src/tests/calendar/Calendar.spec.ts
index 9cca7573..b00bc355 100644
--- a/src/tests/calendar/Calendar.spec.ts
+++ b/src/tests/calendar/Calendar.spec.ts
@@ -1,14 +1,14 @@
-import { testKbd as kbd } from '../utils.js';
+import type { CreateCalendarProps } from '$lib/builders/index.js';
+import { CalendarDate, CalendarDateTime, toZoned, type DateValue } from '@internationalized/date';
 import { render } from '@testing-library/svelte';
 import { userEvent } from '@testing-library/user-event';
 import { axe } from 'jest-axe';
-import { describe } from 'vitest';
-import CalendarTest from './CalendarTest.svelte';
-import { CalendarDate, CalendarDateTime, toZoned, type DateValue } from '@internationalized/date';
-import { writable } from 'svelte/store';
 import { tick } from 'svelte';
+import { writable } from 'svelte/store';
+import { describe } from 'vitest';
+import { testKbd as kbd } from '../utils.js';
 import CalendarMultiTest from './CalendarMultiTest.svelte';
-import type { CreateCalendarProps } from '$lib/builders/index.js';
+import CalendarTest from './CalendarTest.svelte';
 
 const calendarDate = new CalendarDate(1980, 1, 20);
 const calendarDateTime = new CalendarDateTime(1980, 1, 20, 12, 30, 0, 0);
@@ -679,6 +679,75 @@ describe('Calendar', () => {
 		}
 	});
 
+	test('dynamically change numberOfMonths option', async () => {
+		const { queryByTestId, getByTestId, user } = setup();
+
+		let grid0 = queryByTestId('grid-0');
+		let grid1 = queryByTestId('grid-1');
+		expect(grid0).toBeInTheDocument();
+		expect(grid1).toBeNull();
+
+		const numberOfMonthsButton = getByTestId('numberOfMonths');
+		await user.click(numberOfMonthsButton);
+
+		grid0 = queryByTestId('grid-0');
+		grid1 = queryByTestId('grid-1');
+		expect(grid0).toBeInTheDocument();
+		expect(grid1).toBeInTheDocument();
+	});
+
+	test('dynamically change weekStartsOn option', async () => {
+		const { getByTestId, user } = setup();
+
+		const weekDaysCopy = [...narrowWeekdays];
+
+		let weekdayElement = getByTestId(`weekdays`);
+
+		for (let i = 0; i < weekDaysCopy.length; i++) {
+			expect(weekdayElement.children[i]).toHaveTextContent(weekDaysCopy[i]);
+		}
+
+		const weekStartsOnButton = getByTestId('weekStartsOn');
+		await user.click(weekStartsOnButton);
+		const first = weekDaysCopy.shift();
+		if (first) {
+			weekDaysCopy.push(first);
+		}
+
+		weekdayElement = getByTestId(`weekdays`);
+		for (let i = 0; i < weekDaysCopy.length; i++) {
+			expect(weekdayElement.children[i]).toHaveTextContent(weekDaysCopy[i]);
+		}
+	});
+
+	test('dynamically change fixedWeeks option', async () => {
+		const { getByTestId, queryByTestId, user } = setup();
+
+		const nextButton = getByTestId('next-button');
+
+		while (queryByTestId('week-6') !== null) {
+			await user.click(nextButton);
+		}
+
+		const fixedWeeksButton = getByTestId('fixedWeeks');
+		await user.click(fixedWeeksButton);
+
+		expect(queryByTestId('week-6')).not.toBeNull();
+	});
+
+	test('dynamically changing the locale option also update weekStartsOn', async () => {
+		const { getByTestId, user } = setup();
+
+		let weekdayElement = getByTestId(`weekday-0`);
+		expect(weekdayElement).toHaveTextContent('S');
+
+		const localeButton = getByTestId('locale');
+		await user.click(localeButton);
+		weekdayElement = getByTestId(`weekday-0`);
+		// L is italian for Lunedì which is Monday (which is different from S which is Sunday)
+		expect(weekdayElement).toHaveTextContent('L');
+	});
+
 	test('custom ids are applied when provided', async () => {
 		const ids = {
 			accessibleHeading: 'id-heading',
diff --git a/src/tests/calendar/CalendarTest.svelte b/src/tests/calendar/CalendarTest.svelte
index 7159efac..81b5246f 100644
--- a/src/tests/calendar/CalendarTest.svelte
+++ b/src/tests/calendar/CalendarTest.svelte
@@ -30,7 +30,13 @@
 	const {
 		elements: { calendar, heading, grid, cell, prevButton, nextButton },
 		states: { value: insideValue, months, headingValue, weekdays },
-		options: { weekdayFormat: weekdayFormatOption },
+		options: {
+			weekdayFormat: weekdayFormatOption,
+			numberOfMonths: numberOfMonthsOption,
+			fixedWeeks: fixedWeeksOption,
+			weekStartsOn: weekStartsOnOption,
+			locale: localeOption,
+		},
 	} = createCalendar(
 		removeUndefined({
 			value,
@@ -58,6 +64,10 @@
 		})
 	);
 
+	function cycleWeekStart() {
+		$weekStartsOnOption = (($weekStartsOnOption + 1) % 7) as typeof $weekStartsOnOption;
+	}
+
 	function cycleWeekdayFormat() {
 		weekdayFormatOption.update((prev) => {
 			switch (prev) {
@@ -95,7 +105,7 @@
 					{#each $months as month, i}
 						<table use:melt={$grid} class="w-full" data-testid="grid-{i}">
 							<thead aria-hidden="true">
-								<tr>
+								<tr data-testid="weekdays">
 									{#each $weekdays as day, idx}
 										<th class="text-sm font-semibold text-magnum-800">
 											<div
@@ -134,6 +144,28 @@
 	<button on:click={cycleWeekdayFormat} data-testid="cycle-weekday-format">
 		Cycle weekdayFormat
 	</button>
+	<button
+		data-testid="numberOfMonths"
+		on:click={() => {
+			$numberOfMonthsOption++;
+		}}>numberOfMonths</button
+	>
+	<br />
+	<button data-testid="weekStartsOn" on:click={cycleWeekStart}>weekStartsOn</button>
+	<br />
+	<button
+		data-testid="fixedWeeks"
+		on:click={() => {
+			$fixedWeeksOption = !$fixedWeeksOption;
+		}}>fixedWeeksOption</button
+	>
+	<br />
+	<button
+		data-testid="locale"
+		on:click={() => {
+			$localeOption = 'it';
+		}}>setLocaleToIt</button
+	>
 </main>
 
 <style lang="postcss">
diff --git a/src/tests/combobox/Combobox.spec.ts b/src/tests/combobox/Combobox.spec.ts
index a8165609..57532d02 100644
--- a/src/tests/combobox/Combobox.spec.ts
+++ b/src/tests/combobox/Combobox.spec.ts
@@ -315,6 +315,11 @@ describe('Combobox (forceVisible)', () => {
 		const { getByTestId, queryByTestId } = render(ComboboxForceVisibleTest, { options });
 		const input = getByTestId('input');
 		const getMenu = () => queryByTestId('menu');
+		const getFirstItem = () => {
+			const firstItem = getMenu()?.querySelector('[data-melt-combobox-option]');
+			if (!firstItem) throw new Error('No option found');
+			return firstItem;
+		};
 
 		expect(input).not.toHaveValue(options[0].label);
 
@@ -323,10 +328,7 @@ describe('Combobox (forceVisible)', () => {
 		expect(getMenu()).not.toBeNull();
 		expect(getMenu()).toBeVisible();
 
-		const firstItem = getMenu()?.querySelector('[data-melt-combobox-option]');
-		if (!firstItem) throw new Error('No option found');
-
-		await user.click(firstItem);
+		await user.click(getFirstItem());
 
 		expect(getMenu()).toBeNull();
 		expect(input).toHaveValue(options[0].label);
@@ -334,7 +336,7 @@ describe('Combobox (forceVisible)', () => {
 		await user.click(input);
 		expect(getMenu()).not.toBeNull();
 		expect(getMenu()).toBeVisible();
-		expect(firstItem).toHaveAttribute('data-selected');
+		expect(getFirstItem()).toHaveAttribute('data-selected');
 	});
 
 	test('Applies custom ids when provided', async () => {
diff --git a/src/tests/date-field/DateField.spec.ts b/src/tests/date-field/DateField.spec.ts
index 1e89ce5e..34e5b186 100644
--- a/src/tests/date-field/DateField.spec.ts
+++ b/src/tests/date-field/DateField.spec.ts
@@ -1,5 +1,5 @@
 import { testKbd as kbd } from '../utils.js';
-import { render } from '@testing-library/svelte';
+import { render, waitFor } from '@testing-library/svelte';
 import { userEvent } from '@testing-library/user-event';
 import { axe } from 'jest-axe';
 import { describe } from 'vitest';
@@ -125,13 +125,14 @@ describe('DateField', () => {
 		});
 		expect(queryByTestId('dayPeriod')).not.toBeNull();
 	});
+
 	test('focuses first segment on click', async () => {
 		const { getByTestId, user } = setup();
 
 		const firstSegment = getByTestId('month');
 		await user.click(firstSegment);
 
-		expect(firstSegment).toHaveFocus();
+		await waitFor(() => expect(firstSegment).toHaveFocus());
 	});
 
 	test('increments segment on arrow up', async () => {
@@ -193,16 +194,16 @@ describe('DateField', () => {
 
 		await user.click(monthSegment);
 		await user.keyboard(kbd.ARROW_RIGHT);
-		expect(daySegment).toHaveFocus();
+		await waitFor(() => expect(daySegment).toHaveFocus());
 
 		await user.keyboard(kbd.ARROW_RIGHT);
-		expect(yearSegment).toHaveFocus();
+		await waitFor(() => expect(yearSegment).toHaveFocus());
 
 		await user.keyboard(kbd.ARROW_LEFT);
-		expect(daySegment).toHaveFocus();
+		await waitFor(() => expect(daySegment).toHaveFocus());
 
 		await user.keyboard(kbd.ARROW_LEFT);
-		expect(monthSegment).toHaveFocus();
+		await waitFor(() => expect(monthSegment).toHaveFocus());
 	});
 
 	test('navigates segments using tab', async () => {
@@ -214,10 +215,10 @@ describe('DateField', () => {
 
 		await user.click(monthSegment);
 		await user.keyboard(kbd.TAB);
-		expect(daySegment).toHaveFocus();
+		await waitFor(() => expect(daySegment).toHaveFocus());
 
 		await user.keyboard(kbd.TAB);
-		expect(yearSegment).toHaveFocus();
+		await waitFor(() => expect(yearSegment).toHaveFocus());
 	});
 
 	test('disabled prop prevents interaction', async () => {
@@ -229,16 +230,17 @@ describe('DateField', () => {
 		const yearSegment = getByTestId('year');
 
 		await user.click(monthSegment);
-		expect(monthSegment).not.toHaveFocus();
+		await waitFor(() => expect(monthSegment).not.toHaveFocus());
 		await user.keyboard(kbd.ARROW_RIGHT);
-		expect(daySegment).not.toHaveFocus();
+		await waitFor(() => expect(daySegment).not.toHaveFocus());
 		await user.keyboard(kbd.ARROW_RIGHT);
+		await waitFor(() => expect(yearSegment).not.toHaveFocus());
 		expect(yearSegment).not.toHaveFocus();
 
 		await user.click(daySegment);
-		expect(daySegment).not.toHaveFocus();
+		await waitFor(() => expect(daySegment).not.toHaveFocus());
 		await user.click(yearSegment);
-		expect(yearSegment).not.toHaveFocus();
+		await waitFor(() => expect(yearSegment).not.toHaveFocus());
 	});
 
 	test('readonly prop prevents modifying segments', async () => {
@@ -250,11 +252,12 @@ describe('DateField', () => {
 
 		for (const segment of segments) {
 			const el = getByTestId(segment);
+
 			expect(el).toHaveTextContent(String(calendarDateOther[segment]));
 			await user.click(el);
-			expect(el).toHaveFocus();
+			await waitFor(() => expect(el).toHaveFocus());
 			await user.keyboard(kbd.ARROW_UP);
-			expect(el).toHaveTextContent(String(calendarDateOther[segment]));
+			await waitFor(() => expect(el).toHaveTextContent(String(calendarDateOther[segment])));
 		}
 	});
 
@@ -268,17 +271,17 @@ describe('DateField', () => {
 		const monthSegment = getByTestId('month');
 		expect(monthSegment).toHaveTextContent(String(calendarDateOther['month']));
 		await user.click(monthSegment);
-		expect(monthSegment).toHaveFocus();
+		await waitFor(() => expect(monthSegment).toHaveFocus());
 		await user.keyboard(kbd.ARROW_UP);
-		expect(monthSegment).toHaveTextContent(String(calendarDateOther['month']));
+		await waitFor(() => expect(monthSegment).toHaveTextContent(String(calendarDateOther['month'])));
 
 		// day should change
 		const daySegment = getByTestId('day');
-		expect(daySegment).toHaveTextContent(String(calendarDateOther['day']));
+		await waitFor(() => expect(daySegment).toHaveTextContent(String(calendarDateOther['day'])));
 		await user.click(daySegment);
-		expect(daySegment).toHaveFocus();
+		await waitFor(() => expect(daySegment).toHaveFocus());
 		await user.keyboard(kbd.ARROW_UP);
-		expect(daySegment).toHaveTextContent(String(calendarDateOther['day'] + 1));
+		await waitFor(() => expect(daySegment).toHaveTextContent(String(calendarDateOther['day'] + 1)));
 	});
 
 	test('if selected date unavailable, mark field as invalid', async () => {
@@ -586,7 +589,7 @@ describe('DateField', () => {
 		expect(secondSegment).toHaveTextContent('00');
 	});
 
-	test('displays correct timezone with ZonedDateTime value - now', async () => {
+	test.skip('displays correct timezone with ZonedDateTime value - now', async () => {
 		const { getByTestId } = setup({
 			defaultValue: now('America/Los_Angeles'),
 		});
@@ -669,14 +672,11 @@ describe('DateField', () => {
 	});
 });
 
-function isDaylightSavingsTime(): boolean {
+function isDaylightSavingsTime() {
 	const now = new Date();
-	const january = new Date(now.getFullYear(), 0, 1);
-	const july = new Date(now.getFullYear(), 6, 1);
-	const timezoneOffset = now.getTimezoneOffset();
-	const isDaylightSavingsTime =
-		timezoneOffset < Math.max(january.getTimezoneOffset(), july.getTimezoneOffset());
-	return isDaylightSavingsTime;
+	const januaryOffset = new Date(now.getFullYear(), 0, 1).getTimezoneOffset();
+	const julyOffset = new Date(now.getFullYear(), 6, 1).getTimezoneOffset();
+	return januaryOffset !== julyOffset;
 }
 
 function thisTimeZone(date: string): string {
diff --git a/src/tests/date-picker/DatePicker.spec.ts b/src/tests/date-picker/DatePicker.spec.ts
index 2ff9089e..e7f973b4 100644
--- a/src/tests/date-picker/DatePicker.spec.ts
+++ b/src/tests/date-picker/DatePicker.spec.ts
@@ -560,4 +560,66 @@ describe('DatePicker', () => {
 
 		expect(trigger).toBeDisabled();
 	});
+
+	test('dynamically change numberOfMonths option even if numberOfMonths is specified as an option', async () => {
+		const { queryByTestId, getByTestId, user } = setup({
+			numberOfMonths: 1,
+		});
+
+		let grid0 = queryByTestId('grid-0');
+		let grid1 = queryByTestId('grid-1');
+		expect(grid0).toBeInTheDocument();
+		expect(grid1).toBeNull();
+
+		const numberOfMonthsButton = getByTestId('numberOfMonths');
+		await user.click(numberOfMonthsButton);
+
+		grid0 = queryByTestId('grid-0');
+		grid1 = queryByTestId('grid-1');
+		expect(grid0).toBeInTheDocument();
+		expect(grid1).toBeInTheDocument();
+	});
+
+	test('dynamically change weekStartsOn option even if weekStartsOn option is set', async () => {
+		const { getByTestId, user } = setup({
+			weekStartsOn: 0,
+		});
+
+		const weekDaysCopy = [...narrowWeekdays];
+
+		let weekdayElement = getByTestId(`weekdays`);
+
+		for (let i = 0; i < weekDaysCopy.length; i++) {
+			expect(weekdayElement.children[i]).toHaveTextContent(weekDaysCopy[i]);
+		}
+
+		const weekStartsOnButton = getByTestId('weekStartsOn');
+		await user.click(weekStartsOnButton);
+		const first = weekDaysCopy.shift();
+		if (first) {
+			weekDaysCopy.push(first);
+		}
+
+		weekdayElement = getByTestId(`weekdays`);
+		for (let i = 0; i < weekDaysCopy.length; i++) {
+			expect(weekdayElement.children[i]).toHaveTextContent(weekDaysCopy[i]);
+		}
+	});
+
+	test('dynamically change fixedWeeks option even if fixedWeeks option is set', async () => {
+		const { getByTestId, queryByTestId, user } = setup({
+			fixedWeeks: false,
+		});
+
+		const nextButton = getByTestId('next-button');
+
+		while (queryByTestId('week-6') !== null) {
+			await user.click(nextButton);
+		}
+
+		const fixedWeeksButton = getByTestId('fixedWeeks');
+		await user.click(fixedWeeksButton);
+
+		expect(queryByTestId('week-6')).not.toBeNull();
+	});
 });
diff --git a/src/tests/date-picker/DatePickerTest.svelte b/src/tests/date-picker/DatePickerTest.svelte
index 86596a9b..56a2ece7 100644
--- a/src/tests/date-picker/DatePickerTest.svelte
+++ b/src/tests/date-picker/DatePickerTest.svelte
@@ -27,6 +27,7 @@
 	export let placeholder: CreateDatePickerProps['placeholder'] = undefined;
 	export let weekStartsOn: CreateDatePickerProps['weekStartsOn'] = undefined;
 	export let weekdayFormat: CreateDatePickerProps['weekdayFormat'] = undefined;
+	export let fixedWeeks: CreateDatePickerProps['fixedWeeks'] = undefined;
 	export let onOutsideClick: CreateDatePickerProps['onOutsideClick'] = undefined;
 
 	const {
@@ -45,7 +46,13 @@
 			validation,
 		},
 		states: { value: insideValue, months, headingValue, weekdays, segmentContents },
-		options: { locale: insideLocale, weekdayFormat: weekdayFormatOption },
+		options: {
+			locale: insideLocale,
+			weekdayFormat: weekdayFormatOption,
+			numberOfMonths: numberOfMonthsOption,
+			fixedWeeks: fixedWeeksOption,
+			weekStartsOn: weekStartsOnOption,
+		},
 	} = createDatePicker(
 		removeUndefined({
 			value,
@@ -72,9 +79,14 @@
 			weekdayFormat,
 			popoverIds,
 			onOutsideClick,
+			fixedWeeks,
 		})
 	);
 
+	function cycleWeekStart() {
+		$weekStartsOnOption = (($weekStartsOnOption + 1) % 7) as typeof $weekStartsOnOption;
+	}
+
 	function cycleWeekdayFormat() {
 		weekdayFormatOption.update((prev) => {
 			switch (prev) {
@@ -138,7 +150,7 @@
 				{@const { weeks } = month}
 				<table use:melt={$grid} class="w-full" data-testid="grid-{i}">
 					<thead aria-hidden="true">
-						<tr>
+						<tr data-testid="weekdays">
 							{#each $weekdays as day, idx}
 								<th class="text-sm font-semibold text-magnum-800">
 									<div
@@ -152,8 +164,8 @@
 						</tr>
 					</thead>
 					<tbody>
-						{#each weeks as days}
-							<tr>
+						{#each weeks as days, i}
+							<tr data-testid="week-{i + 1}">
 								{#each days as date}
 									<td role="gridcell">
 										<div
@@ -174,6 +186,21 @@
 		<button on:click={cycleWeekdayFormat} data-testid="cycle-weekday-format">
 			Cycle weekdayFormat
 		</button>
+		<button
+			data-testid="numberOfMonths"
+			on:click={() => {
+				$numberOfMonthsOption++;
+			}}>numberOfMonths</button
+		>
+		<br />
+		<button data-testid="weekStartsOn" on:click={cycleWeekStart}>weekStartsOn</button>
+		<br />
+		<button
+			data-testid="fixedWeeks"
+			on:click={() => {
+				$fixedWeeksOption = !$fixedWeeksOption;
+			}}>fixedWeeksOption</button
+		>
 	</div>
 </main>
 
diff --git a/src/tests/dialog/Dialog.spec.ts b/src/tests/dialog/Dialog.spec.ts
index c9dedae1..384a138a 100644
--- a/src/tests/dialog/Dialog.spec.ts
+++ b/src/tests/dialog/Dialog.spec.ts
@@ -230,6 +230,30 @@ describe('Dialog', () => {
 		await waitFor(() => expect(content).not.toBeVisible());
 	});
 
+	it('Respects the `closeOnEscape` prop', async () => {
+		const { getByTestId, user, trigger } = setup({
+			closeOnEscape: false,
+		});
+		expect(trigger).toBeVisible();
+		const content = getByTestId('content');
+		expect(content).not.toBeVisible();
+		await user.click(trigger);
+		expect(content).toBeVisible();
+		await user.keyboard(kbd.ESCAPE);
+		expect(content).toBeVisible();
+	});
+
+	it("Doesn't close on escape if child intercepts event", async () => {
+		const { getByTestId, user, trigger } = setup();
+		await user.click(trigger);
+		const content = getByTestId('content');
+		expect(content).toBeVisible();
+		const input = getByTestId('input-keydown-interceptor');
+		input.focus();
+		await user.keyboard(kbd.ESCAPE);
+		expect(content).toBeVisible();
+	});
+
 	it('Applies custom ids when provided', async () => {
 		const ids = {
 			content: 'id-content',
@@ -277,4 +301,34 @@ describe('Dialog', () => {
 		await user.pointer({ target: overlay, offset: 2, keys: '[/MouseLeft]' });
 		expect(content).not.toBeVisible();
 	});
+
+	it("Doesn't deactivate focus trap on escape provided `closeOnEscape` false", async () => {
+		const { getByTestId, user, trigger } = setup({
+			closeOnEscape: false,
+		});
+		const content = getByTestId('content');
+		expect(content).not.toBeVisible();
+		await user.click(trigger);
+		expect(content).toBeVisible();
+		await user.keyboard(kbd.ESCAPE);
+		expect(content).toBeVisible();
+		expect(getByTestId('content')).toHaveFocus();
+		await user.tab({ shift: true });
+		expect(getByTestId('floating-closer')).not.toHaveFocus();
+	});
+
+	it("Doesn't deactivate focus trap on outside click provided `closeOnOutsideClick` false", async () => {
+		const { getByTestId, user, trigger } = setup({
+			closeOnOutsideClick: false,
+		});
+		const content = getByTestId('content');
+		expect(content).not.toBeVisible();
+		await user.click(trigger);
+		expect(content).toBeVisible();
+		await user.click(getByTestId('overlay'));
+		expect(content).toBeVisible();
+		expect(getByTestId('content')).toHaveFocus();
+		await user.tab({ shift: true });
+		expect(getByTestId('floating-closer')).not.toHaveFocus();
+	});
 });
diff --git a/src/tests/dialog/DialogTest.svelte b/src/tests/dialog/DialogTest.svelte
index 0c0b213a..60d8e2ea 100644
--- a/src/tests/dialog/DialogTest.svelte
+++ b/src/tests/dialog/DialogTest.svelte
@@ -1,5 +1,6 @@
 <script lang="ts">
 	import { createDialog, melt, type CreateDialogProps } from '$lib/index.js';
+	import { kbd } from '$lib/internal/helpers/keyboard.js';
 
 	type $$Props = CreateDialogProps;
 
@@ -19,6 +20,11 @@
 		<div use:melt={$content} data-testid="content">
 			<h2 use:melt={$title} data-testid="title">Title</h2>
 			<p use:melt={$description} data-testid="description">Description</p>
+			<input
+				data-testid="input-keydown-interceptor"
+				type="text"
+				on:keydown={(e) => e.key === kbd.ESCAPE && e.stopPropagation()}
+			/>
 
 			<button use:melt={$close} data-testid="closer">Close</button>
 			<button use:melt={$close} data-testid="last">Close</button>
diff --git a/src/tests/popover/Popover.spec.ts b/src/tests/popover/Popover.spec.ts
index 676c4f24..7d1f5664 100644
--- a/src/tests/popover/Popover.spec.ts
+++ b/src/tests/popover/Popover.spec.ts
@@ -135,4 +135,33 @@ describe('Popover (Default)', () => {
 		expect(trigger.id).toBe(ids.trigger);
 		expect(content.id).toBe(ids.content);
 	});
+
+	it("Doesn't deactivate focus trap on escape provided `closeOnEscape` false", async () => {
+		const { getByTestId, user, trigger } = setup({
+			closeOnEscape: false,
+		});
+		const content = getByTestId('content');
+		expect(content).not.toBeVisible();
+		await user.click(trigger);
+		expect(content).toBeVisible();
+		await user.keyboard(kbd.ESCAPE);
+		expect(content).toBeVisible();
+		expect(getByTestId('content')).toHaveFocus();
+		await user.tab({ shift: true });
+		expect(getByTestId('trigger')).not.toHaveFocus();
+	});
+
+	it("Doesn't deactivate focus trap on outside click provided `closeOnOutsideClick` false", async () => {
+		const { getByTestId, user, trigger } = setup({
+			closeOnOutsideClick: false,
+		});
+		const content = getByTestId('content');
+		expect(content).not.toBeVisible();
+		await user.click(trigger);
+		expect(content).toBeVisible();
+		await user.click(getByTestId('outside'));
+		expect(content).toBeVisible();
+		await user.tab();
+		expect(getByTestId('closeFocus')).not.toHaveFocus();
+	});
 });
diff --git a/src/tests/portal/PopoverTooltip.svelte b/src/tests/portal/PopoverTooltip.svelte
new file mode 100644
index 00000000..c73ea7f3
--- /dev/null
+++ b/src/tests/portal/PopoverTooltip.svelte
@@ -0,0 +1,35 @@
+<script lang="ts">
+	import { createPopover, createTooltip, melt } from '$lib/index.js';
+	import { Settings2 } from '$icons/index.js';
+
+	const {
+		elements: { trigger, content, arrow, close },
+	} = createPopover();
+
+	const {
+		elements: { trigger: ttTrigger, content: ttContent },
+	} = createTooltip({
+		openDelay: 0,
+		closeDelay: 0,
+	});
+</script>
+
+<button
+	type="button"
+	class="trigger"
+	use:melt={$trigger}
+	aria-label="Update dimensions"
+	data-testid="popover-trigger"
+>
+	<Settings2 class="h-4 w-4" />
+	<span class="sr-only">Open Popover</span>
+</button>
+
+<div use:melt={$content} data-testid="popover-content">
+	<div use:melt={$arrow} data-testid="popover-arrow" />
+	<button use:melt={$ttTrigger} data-testid="tooltip-trigger"> Open tooltip </button>
+	<div use:melt={$ttContent} data-testid="tooltip-content">Tooltip content</div>
+	<button use:melt={$close} data-testid="popover-close"> Close </button>
+</div>
+<div class="mt-48 h-[500px] w-[500px] bg-black" />
+<div data-testid="outside" class="h-5 w-5" />
diff --git a/src/tests/portal/Portal.spec.ts b/src/tests/portal/Portal.spec.ts
new file mode 100644
index 00000000..89a3c56e
--- /dev/null
+++ b/src/tests/portal/Portal.spec.ts
@@ -0,0 +1,69 @@
+import { describe, it } from 'vitest';
+import { render, waitFor } from '@testing-library/svelte';
+import { userEvent } from '@testing-library/user-event';
+import PopoverTooltip from './PopoverTooltip.svelte';
+import { sleep } from '$lib/internal/helpers/sleep.js';
+
+function setupPopoverTooltip() {
+	const user = userEvent.setup();
+	const returned = render(PopoverTooltip);
+	const popoverTrigger = returned.getByTestId('popover-trigger');
+	const popoverContent = returned.getByTestId('popover-content');
+	const tooltipTrigger = returned.getByTestId('tooltip-trigger');
+	const tooltipContent = returned.getByTestId('tooltip-content');
+	const outside = returned.getByTestId('outside');
+	const elements = { popoverTrigger, popoverContent, tooltipTrigger, tooltipContent, outside };
+	return { user, ...returned, elements };
+}
+
+describe('Portal Behaviors - Popover & Tooltip', () => {
+	it('should not close the popover when the tooltip content is clicked', async () => {
+		const { elements, user } = setupPopoverTooltip();
+
+		expect(elements.popoverContent).not.toBeVisible();
+		await user.click(elements.popoverTrigger);
+		expect(elements.popoverContent).toBeVisible();
+		expect(elements.tooltipContent).not.toBeVisible();
+		await user.hover(elements.tooltipTrigger);
+		expect(elements.tooltipContent).toBeVisible();
+		await user.click(elements.tooltipContent);
+		expect(elements.tooltipContent).toBeVisible();
+		expect(elements.popoverContent).toBeVisible();
+	});
+
+	it('should close the tooltip content when the popover content is clicked and tooltip is open', async () => {
+		const { elements, user } = setupPopoverTooltip();
+
+		expect(elements.popoverContent).not.toBeVisible();
+		await user.click(elements.popoverTrigger);
+		expect(elements.popoverContent).toBeVisible();
+		expect(elements.tooltipContent).not.toBeVisible();
+		await user.hover(elements.tooltipTrigger);
+		expect(elements.tooltipContent).toBeVisible();
+		await user.click(elements.tooltipContent);
+		expect(elements.tooltipContent).toBeVisible();
+		expect(elements.popoverContent).toBeVisible();
+		await user.click(elements.popoverContent);
+		expect(elements.tooltipContent).not.toBeVisible();
+		expect(elements.popoverContent).toBeVisible();
+	});
+
+	it('should close both tooltip and popover content when clicked outside of popover content', async () => {
+		const { elements, user } = setupPopoverTooltip();
+
+		expect(elements.popoverContent).not.toBeVisible();
+		await user.click(elements.popoverTrigger);
+		expect(elements.popoverContent).toBeVisible();
+		expect(elements.tooltipContent).not.toBeVisible();
+		await user.hover(elements.tooltipTrigger);
+		expect(elements.tooltipContent).toBeVisible();
+		await user.click(elements.tooltipContent);
+		expect(elements.tooltipContent).toBeVisible();
+		expect(elements.popoverContent).toBeVisible();
+		await sleep(100);
+		expect(elements.outside).toBeVisible();
+		await user.click(elements.outside);
+		await waitFor(() => expect(elements.tooltipContent).not.toBeVisible());
+		await waitFor(() => expect(elements.popoverContent).not.toBeVisible());
+	});
+});
diff --git a/src/tests/range-calendar/RangeCalendar.spec.ts b/src/tests/range-calendar/RangeCalendar.spec.ts
index 5f9744a6..3bb62e3e 100644
--- a/src/tests/range-calendar/RangeCalendar.spec.ts
+++ b/src/tests/range-calendar/RangeCalendar.spec.ts
@@ -501,6 +501,75 @@ describe('Range Calendar', () => {
 		}
 	});
 
+	test('dynamically change numberOfMonths option', async () => {
+		const { queryByTestId, getByTestId, user } = setup();
+
+		let grid0 = queryByTestId('grid-0');
+		let grid1 = queryByTestId('grid-1');
+		expect(grid0).toBeInTheDocument();
+		expect(grid1).toBeNull();
+
+		const numberOfMonthsButton = getByTestId('numberOfMonths');
+		await user.click(numberOfMonthsButton);
+
+		grid0 = queryByTestId('grid-0');
+		grid1 = queryByTestId('grid-1');
+		expect(grid0).toBeInTheDocument();
+		expect(grid1).toBeInTheDocument();
+	});
+
+	test('dynamically change weekStartsOn option', async () => {
+		const { getByTestId, user } = setup();
+
+		const weekDaysCopy = [...narrowWeekdays];
+
+		let weekdayElement = getByTestId(`weekdays`);
+
+		for (let i = 0; i < weekDaysCopy.length; i++) {
+			expect(weekdayElement.children[i]).toHaveTextContent(weekDaysCopy[i]);
+		}
+
+		const weekStartsOnButton = getByTestId('weekStartsOn');
+		await user.click(weekStartsOnButton);
+		const first = weekDaysCopy.shift();
+		if (first) {
+			weekDaysCopy.push(first);
+		}
+
+		weekdayElement = getByTestId(`weekdays`);
+		for (let i = 0; i < weekDaysCopy.length; i++) {
+			expect(weekdayElement.children[i]).toHaveTextContent(weekDaysCopy[i]);
+		}
+	});
+
+	test('dynamically change fixedWeeks option', async () => {
+		const { getByTestId, queryByTestId, user } = setup();
+
+		const nextButton = getByTestId('next-button');
+
+		while (queryByTestId('week-6') !== null) {
+			await user.click(nextButton);
+		}
+
+		const fixedWeeksButton = getByTestId('fixedWeeks');
+		await user.click(fixedWeeksButton);
+
+		expect(queryByTestId('week-6')).not.toBeNull();
+	});
+
+	test('dynamically changing the locale option also update weekStartsOn', async () => {
+		const { getByTestId, user } = setup();
+
+		let weekdayElement = getByTestId(`weekday-0`);
+		expect(weekdayElement).toHaveTextContent('S');
+
+		const localeButton = getByTestId('locale');
+		await user.click(localeButton);
+		weekdayElement = getByTestId(`weekday-0`);
+		// L is italian for Lunedì which is Monday (which is different from S which is Sunday)
+		expect(weekdayElement).toHaveTextContent('L');
+	});
+
 	test('custom ids are applied when provided', async () => {
 		const ids = {
 			accessibleHeading: 'id-heading',
diff --git a/src/tests/range-calendar/RangeCalendarTest.svelte b/src/tests/range-calendar/RangeCalendarTest.svelte
index f9160fe1..4961f63b 100644
--- a/src/tests/range-calendar/RangeCalendarTest.svelte
+++ b/src/tests/range-calendar/RangeCalendarTest.svelte
@@ -27,7 +27,13 @@
 	const {
 		elements: { calendar, heading, grid, cell, prevButton, nextButton },
 		states: { value: insideValue, months, headingValue, weekdays },
-		options: { weekdayFormat: weekdayFormatOption },
+		options: {
+			weekdayFormat: weekdayFormatOption,
+			numberOfMonths: numberOfMonthsOption,
+			fixedWeeks: fixedWeeksOption,
+			locale: localeOption,
+			weekStartsOn: weekStartsOnOption,
+		},
 	} = createRangeCalendar(
 		removeUndefined({
 			value,
@@ -64,6 +70,10 @@
 			}
 		});
 	}
+
+	function cycleWeekStart() {
+		$weekStartsOnOption = (($weekStartsOnOption + 1) % 7) as typeof $weekStartsOnOption;
+	}
 </script>
 
 <main class="flex h-full">
@@ -91,7 +101,7 @@
 					{@const { weeks } = month}
 					<table use:melt={$grid} class="w-full" data-testid="grid-{i}">
 						<thead aria-hidden="true">
-							<tr>
+							<tr data-testid="weekdays">
 								{#each $weekdays as day, idx}
 									<th class="text-sm font-semibold text-magnum-800">
 										<div
@@ -129,6 +139,28 @@
 	<button on:click={cycleWeekdayFormat} data-testid="cycle-weekday-format">
 		Cycle weekdayFormat
 	</button>
+	<button
+		data-testid="numberOfMonths"
+		on:click={() => {
+			$numberOfMonthsOption++;
+		}}>numberOfMonths</button
+	>
+	<br />
+	<button data-testid="weekStartsOn" on:click={cycleWeekStart}>weekStartsOn</button>
+	<br />
+	<button
+		data-testid="fixedWeeks"
+		on:click={() => {
+			$fixedWeeksOption = !$fixedWeeksOption;
+		}}>fixedWeeksOption</button
+	>
+	<br />
+	<button
+		data-testid="locale"
+		on:click={() => {
+			$localeOption = 'it';
+		}}>setLocaleToIt</button
+	>
 </main>
 
 <style lang="postcss">