diff --git a/.changeset/proxy-tab-focus-portal.md b/.changeset/proxy-tab-focus-portal.md
new file mode 100644
index 0000000000..4978400cbd
--- /dev/null
+++ b/.changeset/proxy-tab-focus-portal.md
@@ -0,0 +1,6 @@
+---
+"@zag-js/dom-query": patch
+"@zag-js/popover": patch
+---
+
+Fix `proxyTabFocus` so tabbing out of portalled content moves to the next tabbable after the trigger, instead of looping back into the content when the trigger is last on the page.
diff --git a/e2e/popover.e2e.ts b/e2e/popover.e2e.ts
index 29fa7cd69b..01a0d39c89 100644
--- a/e2e/popover.e2e.ts
+++ b/e2e/popover.e2e.ts
@@ -95,6 +95,28 @@ test.describe("popover", () => {
await I.seeContent()
})
+ test("[keyboard / non-modal] when trigger is last tabbable: tab out should not loop into content", async ({
+ page,
+ }) => {
+ await page.goto("/popover/trigger-last")
+
+ const trigger = page.getByTestId("popover-trigger")
+ const link = page.getByTestId("focusable-link")
+ const close = page.getByTestId("popover-close-button")
+
+ await trigger.focus()
+ await page.keyboard.press("Enter")
+ await expect(link).toBeFocused()
+
+ await page.keyboard.press("Tab")
+ await page.keyboard.press("Tab")
+ await expect(close).toBeFocused()
+
+ await page.keyboard.press("Tab")
+ await expect(link).not.toBeFocused()
+ await expect(close).not.toBeFocused()
+ })
+
test("[pointer] close the popover on click close button", async () => {
await I.clickTrigger()
await I.seeContent()
diff --git a/examples/next-ts/pages/popover/trigger-last.tsx b/examples/next-ts/pages/popover/trigger-last.tsx
new file mode 100644
index 0000000000..4a94b3a53d
--- /dev/null
+++ b/examples/next-ts/pages/popover/trigger-last.tsx
@@ -0,0 +1,40 @@
+import * as popover from "@zag-js/popover"
+import { normalizeProps, Portal, useMachine } from "@zag-js/react"
+import { useId } from "react"
+import { Presence } from "../../components/presence"
+
+export default function Page() {
+ const service = useMachine(popover.machine, {
+ id: useId(),
+ modal: false,
+ portalled: true,
+ })
+
+ const api = popover.connect(service, normalizeProps)
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/package.json b/package.json
index 8efcb80bb7..07c7672408 100644
--- a/package.json
+++ b/package.json
@@ -72,14 +72,14 @@
]
},
"dependencies": {
- "@axe-core/playwright": "4.11.3",
+ "@axe-core/playwright": "4.12.1",
"@changesets/changelog-github": "0.7.0",
"@changesets/cli": "2.31.0",
"@commitlint/cli": "21.0.2",
"@commitlint/config-conventional": "21.0.2",
"@eslint/js": "^10.0.1",
"@internationalized/date": "3.12.2",
- "@playwright/test": "1.61.0",
+ "@playwright/test": "1.61.1",
"@swc/core": "1.15.41",
"@types/jsdom": "^28.0.1",
"@types/node": "25.9.4",
diff --git a/packages/utilities/dom-query/src/proxy-tab-focus.ts b/packages/utilities/dom-query/src/proxy-tab-focus.ts
index 3a3e02d17c..baf65870eb 100644
--- a/packages/utilities/dom-query/src/proxy-tab-focus.ts
+++ b/packages/utilities/dom-query/src/proxy-tab-focus.ts
@@ -1,7 +1,7 @@
import { addDomEvent } from "./event"
-import { isActiveElement } from "./node"
+import { contains, isActiveElement } from "./node"
import { raf } from "./raf"
-import { getNextTabbable, getTabbableEdges, type GetShadowRootOption } from "./tabbable"
+import { getTabbableEdges, getTabbables, type GetShadowRootOption } from "./tabbable"
import type { MaybeElement, MaybeElementOrFn } from "./types"
export interface ProxyTabFocusOptions {
@@ -12,44 +12,66 @@ export interface ProxyTabFocusOptions {
getShadowRoot?: GetShadowRootOption | undefined
}
-function proxyTabFocusImpl(container: MaybeElement, options: ProxyTabFocusOptions = {}) {
+function resolveElement(value: MaybeElementOrFn | undefined): MaybeElement {
+ if (value == null) return null
+ return typeof value === "function" ? value() : value
+}
+
+/** Next tabbable after `trigger`, skipping anything inside `container` (portalled content). */
+function getNextTabbableAfterTrigger(
+ container: HTMLElement,
+ trigger: MaybeElement,
+ getShadowRoot: GetShadowRootOption,
+) {
+ if (!trigger) return null
+ const tabbables = getTabbables(container.ownerDocument.body, { getShadowRoot })
+ const triggerIndex = tabbables.indexOf(trigger)
+ if (triggerIndex === -1) return null
+
+ for (let i = triggerIndex + 1; i < tabbables.length; i++) {
+ const el = tabbables[i]
+ if (!contains(container, el)) return el
+ }
+ return null
+}
+
+function proxyTabFocusImpl(container: MaybeElementOrFn, options: ProxyTabFocusOptions = {}) {
const { triggerElement, onFocus, onFocusEnter, getShadowRoot } = options
- const doc = container?.ownerDocument || document
- const body = doc.body
+ const initial = resolveElement(container) ?? resolveElement(triggerElement)
+ const doc = initial?.ownerDocument || document
function onKeyDown(event: KeyboardEvent) {
if (event.key !== "Tab") return
- let elementToFocus: MaybeElement | undefined = null
-
- // get all tabbable elements within the container
- const [firstTabbable, lastTabbable] = getTabbableEdges(container, { includeContainer: true, getShadowRoot })
- const nextTabbableAfterTrigger = getNextTabbable(body, { current: triggerElement, getShadowRoot })
+ // Resolve per keypress so late-mounted / portalled nodes stay correct
+ const content = resolveElement(container)
+ const trigger = resolveElement(triggerElement)
+ if (!content) return
+ const [firstTabbable, lastTabbable] = getTabbableEdges(content, { includeContainer: true, getShadowRoot })
const noTabbableElements = !firstTabbable && !lastTabbable
- // if we're focused on the element after the reference element and the user tabs backwards
- // we want to focus the last tabbable element
- if (event.shiftKey && isActiveElement(nextTabbableAfterTrigger)) {
- onFocusEnter?.()
- elementToFocus = lastTabbable
- }
- // if we're focused on the first tabbable element and the user tabs backwards
- // we want to focus the reference element
- else if (event.shiftKey && (isActiveElement(firstTabbable) || noTabbableElements)) {
- elementToFocus = triggerElement
- }
- // if we're focused on the reference element and the user tabs forwards
- // we want to focus the first tabbable element
- else if (!event.shiftKey && isActiveElement(triggerElement)) {
+ let elementToFocus: MaybeElement | undefined = null
+
+ // Cheap paths first — avoid scanning body tabbables unless exiting/re-entering content
+ if (event.shiftKey && (isActiveElement(firstTabbable) || noTabbableElements)) {
+ // Shift+Tab from first item → trigger
+ elementToFocus = trigger
+ } else if (!event.shiftKey && isActiveElement(trigger)) {
+ // Tab from trigger → first item
onFocusEnter?.()
elementToFocus = firstTabbable
- }
- // if we're focused on the last tabbable element and the user tabs forwards
- // we want to focus the next tabbable element after the reference element
- else if (!event.shiftKey && (isActiveElement(lastTabbable) || noTabbableElements)) {
- elementToFocus = nextTabbableAfterTrigger
+ } else if (!event.shiftKey && (isActiveElement(lastTabbable) || noTabbableElements)) {
+ // Tab from last item → next after trigger (outside content)
+ elementToFocus = getNextTabbableAfterTrigger(content, trigger, getShadowRoot)
+ } else if (event.shiftKey) {
+ // Shift+Tab from next-after-trigger → last item
+ const nextTabbableAfterTrigger = getNextTabbableAfterTrigger(content, trigger, getShadowRoot)
+ if (isActiveElement(nextTabbableAfterTrigger)) {
+ onFocusEnter?.()
+ elementToFocus = lastTabbable
+ }
}
if (!elementToFocus) return
@@ -63,19 +85,16 @@ function proxyTabFocusImpl(container: MaybeElement, options: ProxyTabFocusOption
}
}
- // listen for the tab key in the capture phase
return addDomEvent(doc, "keydown", onKeyDown, true)
}
export function proxyTabFocus(container: MaybeElementOrFn, options: ProxyTabFocusOptions) {
- const { defer, triggerElement, ...restOptions } = options
+ const { defer, ...restOptions } = options
const func = defer ? raf : (v: any) => v()
const cleanups: (VoidFunction | undefined)[] = []
cleanups.push(
func(() => {
- const node = typeof container === "function" ? container() : container
- const trigger = typeof triggerElement === "function" ? triggerElement() : triggerElement
- cleanups.push(proxyTabFocusImpl(node, { triggerElement: trigger, ...restOptions }))
+ cleanups.push(proxyTabFocusImpl(container, restOptions))
}),
)
return () => {
diff --git a/packages/utilities/dom-query/tests/proxy-tab-focus.test.ts b/packages/utilities/dom-query/tests/proxy-tab-focus.test.ts
new file mode 100644
index 0000000000..8cb4777343
--- /dev/null
+++ b/packages/utilities/dom-query/tests/proxy-tab-focus.test.ts
@@ -0,0 +1,148 @@
+// @vitest-environment jsdom
+
+import { afterEach, describe, expect, it, vi } from "vitest"
+import { proxyTabFocus } from "../src"
+
+function markVisible(element: T): T {
+ Object.defineProperty(element, "offsetWidth", { configurable: true, get: () => 1 })
+ Object.defineProperty(element, "offsetHeight", { configurable: true, get: () => 1 })
+ Object.defineProperty(element, "getClientRects", {
+ configurable: true,
+ value: () => [{ width: 1, height: 1 }],
+ })
+ return element
+}
+
+function createButton(text: string) {
+ const button = markVisible(document.createElement("button"))
+ button.type = "button"
+ button.textContent = text
+ return button
+}
+
+function pressTab(shiftKey = false) {
+ document.dispatchEvent(
+ new KeyboardEvent("keydown", {
+ key: "Tab",
+ bubbles: true,
+ cancelable: true,
+ shiftKey,
+ }),
+ )
+}
+
+describe("proxyTabFocus", () => {
+ afterEach(() => {
+ document.body.innerHTML = ""
+ })
+
+ it("does not loop into portalled content when the trigger is the last page tabbable", () => {
+ const trigger = createButton("Trigger")
+ const content = markVisible(document.createElement("div"))
+ const first = createButton("First")
+ const last = createButton("Last")
+ content.append(first, last)
+ // Portalled: content is after the trigger at the end of the body
+ document.body.append(trigger, content)
+
+ const onFocus = vi.fn((el: HTMLElement) => el.focus())
+ const cleanup = proxyTabFocus(content, { triggerElement: trigger, onFocus })
+
+ last.focus()
+ pressTab()
+
+ expect(onFocus).not.toHaveBeenCalled()
+ expect(document.activeElement).toBe(last)
+
+ cleanup()
+ })
+
+ it("tabs from the last content item to the next tabbable after the trigger", () => {
+ const trigger = createButton("Trigger")
+ const after = createButton("After")
+ const content = markVisible(document.createElement("div"))
+ const first = createButton("First")
+ const last = createButton("Last")
+ content.append(first, last)
+ document.body.append(trigger, after, content)
+
+ const onFocus = vi.fn((el: HTMLElement) => el.focus())
+ const cleanup = proxyTabFocus(content, { triggerElement: trigger, onFocus })
+
+ last.focus()
+ pressTab()
+
+ expect(onFocus).toHaveBeenCalledWith(after)
+ expect(document.activeElement).toBe(after)
+
+ cleanup()
+ })
+
+ it("shift+tabs from the next after trigger into the last content item", () => {
+ const trigger = createButton("Trigger")
+ const after = createButton("After")
+ const content = markVisible(document.createElement("div"))
+ const first = createButton("First")
+ const last = createButton("Last")
+ content.append(first, last)
+ document.body.append(trigger, after, content)
+
+ const onFocus = vi.fn((el: HTMLElement) => el.focus())
+ const cleanup = proxyTabFocus(content, { triggerElement: trigger, onFocus })
+
+ after.focus()
+ pressTab(true)
+
+ expect(onFocus).toHaveBeenCalledWith(last)
+ expect(document.activeElement).toBe(last)
+
+ cleanup()
+ })
+
+ it("tabs from the trigger into the first content item", () => {
+ const trigger = createButton("Trigger")
+ const content = markVisible(document.createElement("div"))
+ const first = createButton("First")
+ const last = createButton("Last")
+ content.append(first, last)
+ document.body.append(trigger, content)
+
+ const onFocus = vi.fn((el: HTMLElement) => el.focus())
+ const cleanup = proxyTabFocus(content, { triggerElement: trigger, onFocus })
+
+ trigger.focus()
+ pressTab()
+
+ expect(onFocus).toHaveBeenCalledWith(first)
+ expect(document.activeElement).toBe(first)
+
+ cleanup()
+ })
+
+ it("resolves container and trigger getters on keydown", () => {
+ const trigger = createButton("Trigger")
+ const after = createButton("After")
+ document.body.append(trigger, after)
+
+ let content: HTMLElement | null = null
+ const onFocus = vi.fn((el: HTMLElement) => el.focus())
+ const cleanup = proxyTabFocus(() => content, {
+ triggerElement: () => trigger,
+ onFocus,
+ })
+
+ content = markVisible(document.createElement("div"))
+ const first = createButton("First")
+ const last = createButton("Last")
+ content.append(first, last)
+ document.body.append(content)
+
+ last.focus()
+ pressTab()
+
+ expect(onFocus).toHaveBeenCalledWith(after)
+ expect(document.activeElement).toBe(after)
+
+ cleanup()
+ })
+})
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b18a6bfb4b..3523726d01 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -9,8 +9,8 @@ importers:
.:
dependencies:
'@axe-core/playwright':
- specifier: 4.11.3
- version: 4.11.3(playwright-core@1.61.0)
+ specifier: 4.12.1
+ version: 4.12.1(playwright-core@1.61.1)
'@changesets/changelog-github':
specifier: 0.7.0
version: 0.7.0
@@ -30,8 +30,8 @@ importers:
specifier: 3.12.2
version: 3.12.2
'@playwright/test':
- specifier: 1.61.0
- version: 1.61.0
+ specifier: 1.61.1
+ version: 1.61.1
'@swc/core':
specifier: 1.15.41
version: 1.15.41(@swc/helpers@0.5.23)
@@ -379,10 +379,10 @@ importers:
version: 1.16.0(react@19.2.6)
next:
specifier: 16.2.6
- version: 16.2.6(@babel/core@7.29.7)(@playwright/test@1.61.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ version: 16.2.6(@babel/core@7.29.7)(@playwright/test@1.61.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
next-cloudinary:
specifier: 6.17.5
- version: 6.17.5(next@16.2.6(@babel/core@7.29.7)(@playwright/test@1.61.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)
+ version: 6.17.5(next@16.2.6(@babel/core@7.29.7)(@playwright/test@1.61.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6)
react:
specifier: 19.2.6
version: 19.2.6
@@ -3814,7 +3814,7 @@ importers:
version: 8.3.0
next:
specifier: 16.2.9
- version: 16.2.9(@playwright/test@1.61.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ version: 16.2.9(@playwright/test@1.61.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
react:
specifier: 19.2.7
version: 19.2.7
@@ -4056,10 +4056,10 @@ importers:
version: 8.3.0
next:
specifier: 16.2.9
- version: 16.2.9(@playwright/test@1.61.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ version: 16.2.9(@playwright/test@1.61.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
next-seo:
specifier: 7.2.0
- version: 7.2.0(next@16.2.9(@playwright/test@1.61.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)
+ version: 7.2.0(next@16.2.9(@playwright/test@1.61.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)
next-themes:
specifier: ^0.4.6
version: 0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
@@ -4151,8 +4151,8 @@ packages:
'@asamuzakjp/nwsapi@2.3.9':
resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
- '@axe-core/playwright@4.11.3':
- resolution: {integrity: sha512-h/kfksv4F0cVIDlKpT4700OehdRgpvuVskuQ2nb7/JmtWUXpe9ftHAPtwyXGvVSsa6SJ64A9ER7Zrzc/sIvC4w==}
+ '@axe-core/playwright@4.12.1':
+ resolution: {integrity: sha512-rMd7xriptqKpP+w5265i4Hdkv2X5kbu6uiBi/B2I7uf3hieRBM3qDCfaKPtxfiYb2mKXfF+yLODJwIx+Jv1GDw==}
peerDependencies:
playwright-core: '>= 1.0.0'
@@ -6217,8 +6217,8 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
- '@playwright/test@1.61.0':
- resolution: {integrity: sha512-cKA5B6lpFEMyMGjxF54QihfYpB4FkEGH+qZhtArDEG+wezQAJY8Pq6C7T1SjWz+FFzt3TbyoXBQYk/0292TdJA==}
+ '@playwright/test@1.61.1':
+ resolution: {integrity: sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==}
engines: {node: '>=18'}
hasBin: true
@@ -7754,10 +7754,6 @@ packages:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
- axe-core@4.11.4:
- resolution: {integrity: sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA==}
- engines: {node: '>=4'}
-
axe-core@4.12.1:
resolution: {integrity: sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==}
engines: {node: '>=4'}
@@ -11138,13 +11134,13 @@ packages:
pkg-types@2.3.1:
resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==}
- playwright-core@1.61.0:
- resolution: {integrity: sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==}
+ playwright-core@1.61.1:
+ resolution: {integrity: sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==}
engines: {node: '>=18'}
hasBin: true
- playwright@1.61.0:
- resolution: {integrity: sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==}
+ playwright@1.61.1:
+ resolution: {integrity: sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==}
engines: {node: '>=18'}
hasBin: true
@@ -13395,10 +13391,10 @@ snapshots:
'@asamuzakjp/nwsapi@2.3.9': {}
- '@axe-core/playwright@4.11.3(playwright-core@1.61.0)':
+ '@axe-core/playwright@4.12.1(playwright-core@1.61.1)':
dependencies:
- axe-core: 4.11.4
- playwright-core: 1.61.0
+ axe-core: 4.12.1
+ playwright-core: 1.61.1
'@babel/code-frame@7.26.2':
dependencies:
@@ -15529,9 +15525,9 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
- '@playwright/test@1.61.0':
+ '@playwright/test@1.61.1':
dependencies:
- playwright: 1.61.0
+ playwright: 1.61.1
'@pnpm/constants@6.1.0': {}
@@ -17235,8 +17231,6 @@ snapshots:
dependencies:
possible-typed-array-names: 1.1.0
- axe-core@4.11.4: {}
-
axe-core@4.12.1: {}
axobject-query@4.1.0: {}
@@ -20637,17 +20631,17 @@ snapshots:
neo-async@2.6.2: {}
- next-cloudinary@6.17.5(next@16.2.6(@babel/core@7.29.7)(@playwright/test@1.61.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6):
+ next-cloudinary@6.17.5(next@16.2.6(@babel/core@7.29.7)(@playwright/test@1.61.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react@19.2.6):
dependencies:
'@cloudinary-util/types': 1.5.10
'@cloudinary-util/url-loader': 5.10.4
'@cloudinary-util/util': 4.0.0
- next: 16.2.6(@babel/core@7.29.7)(@playwright/test@1.61.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ next: 16.2.6(@babel/core@7.29.7)(@playwright/test@1.61.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
react: 19.2.6
- next-seo@7.2.0(next@16.2.9(@playwright/test@1.61.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7):
+ next-seo@7.2.0(next@16.2.9(@playwright/test@1.61.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7):
dependencies:
- next: 16.2.9(@playwright/test@1.61.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ next: 16.2.9(@playwright/test@1.61.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
react: 19.2.7
next-themes@0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
@@ -20655,7 +20649,7 @@ snapshots:
react: 19.2.7
react-dom: 19.2.7(react@19.2.7)
- next@16.2.6(@babel/core@7.29.7)(@playwright/test@1.61.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
+ next@16.2.6(@babel/core@7.29.7)(@playwright/test@1.61.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
dependencies:
'@next/env': 16.2.6
'@swc/helpers': 0.5.15
@@ -20674,13 +20668,13 @@ snapshots:
'@next/swc-linux-x64-musl': 16.2.6
'@next/swc-win32-arm64-msvc': 16.2.6
'@next/swc-win32-x64-msvc': 16.2.6
- '@playwright/test': 1.61.0
+ '@playwright/test': 1.61.1
sharp: 0.34.5
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
- next@16.2.9(@playwright/test@1.61.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
+ next@16.2.9(@playwright/test@1.61.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
dependencies:
'@next/env': 16.2.9
'@swc/helpers': 0.5.15
@@ -20699,7 +20693,7 @@ snapshots:
'@next/swc-linux-x64-musl': 16.2.9
'@next/swc-win32-arm64-msvc': 16.2.9
'@next/swc-win32-x64-msvc': 16.2.9
- '@playwright/test': 1.61.0
+ '@playwright/test': 1.61.1
sharp: 0.34.5
transitivePeerDependencies:
- '@babel/core'
@@ -21554,11 +21548,11 @@ snapshots:
exsolve: 1.1.0
pathe: 2.0.3
- playwright-core@1.61.0: {}
+ playwright-core@1.61.1: {}
- playwright@1.61.0:
+ playwright@1.61.1:
dependencies:
- playwright-core: 1.61.0
+ playwright-core: 1.61.1
optionalDependencies:
fsevents: 2.3.2