diff --git a/.changeset/date-picker-open-on-value.md b/.changeset/date-picker-open-on-value.md
new file mode 100644
index 000000000..5773f32f9
--- /dev/null
+++ b/.changeset/date-picker-open-on-value.md
@@ -0,0 +1,5 @@
+---
+"@tailor-platform/app-shell": patch
+---
+
+`DatePicker` and `DateRangePicker` now open their calendar on the current field value. Previously a date typed into the segments (or set from outside while closed) was ignored, so the popover opened on today's month — or on whatever month was last viewed — and arrow-key + Enter selected relative to that. The calendar now focuses the selected/typed date on every open (the range start, else a typed end), falling back to today when empty.
diff --git a/packages/core/src/components/date-field/date-field.test.tsx b/packages/core/src/components/date-field/date-field.test.tsx
index e3e8df6fc..7f82f8c1e 100644
--- a/packages/core/src/components/date-field/date-field.test.tsx
+++ b/packages/core/src/components/date-field/date-field.test.tsx
@@ -33,6 +33,9 @@ function getCalendarCells() {
return screen.getAllByRole("button", { hidden: true }).filter((c) => c.closest('[role="grid"]'));
}
+/** Accessible name of the focused element — the roving-focus day cell once the popover opens. */
+const focusedLabel = () => document.activeElement?.getAttribute("aria-label") ?? "";
+
function getEnabledCalendarCells() {
return getCalendarCells().filter(
(c) => !c.hasAttribute("data-disabled") && !c.hasAttribute("data-outside-month"),
@@ -1116,6 +1119,66 @@ describe("DatePicker keyboard", () => {
});
});
+ // Regression: the calendar's roving focus was seeded once at mount and never
+ // followed the field, so a typed date opened the popover on today's month.
+ it("opens the calendar on a date typed into an empty field (Alt+↓)", async () => {
+ const user = userEvent.setup();
+ const onChange = vi.fn();
+ render();
+
+ await user.click(screen.getByRole("spinbutton", { name: "month" }));
+ await user.keyboard("12202025");
+ await waitFor(() => expect(onChange.mock.calls.at(-1)?.[0]?.toString()).toBe("2025-12-20"));
+
+ await user.keyboard("{Alt>}{ArrowDown}{/Alt}");
+ await waitFor(() => {
+ expect(document.activeElement?.getAttribute("aria-label")).toMatch(/December 20, 2025/);
+ });
+
+ await user.keyboard("{ArrowRight}{Enter}");
+ await waitFor(() => expect(onChange.mock.calls.at(-1)?.[0]?.toString()).toBe("2025-12-21"));
+ });
+
+ it("re-lands the calendar on the current value each time it opens", async () => {
+ const user = userEvent.setup();
+ function Controlled() {
+ const [value, setValue] = useState(new CalendarDate(2025, 6, 15));
+ return (
+ <>
+
+
+
+ >
+ );
+ }
+ render();
+ const trigger = () => screen.getByRole("button", { name: "Open calendar" });
+
+ // Page away, dismiss, change the value externally — reopening follows the value.
+ await user.click(trigger());
+ await waitFor(() => expect(focusedLabel()).toMatch(/June 15, 2025/));
+ await user.keyboard("{PageDown}{PageDown}{Escape}");
+ await user.click(screen.getByRole("button", { name: "set" }));
+ await user.click(trigger());
+ await waitFor(() => expect(focusedLabel()).toMatch(/February 10, 2024/));
+
+ // Cleared → falls back to today, not the last-viewed month.
+ await user.keyboard("{Escape}");
+ await user.click(screen.getByRole("button", { name: "clear" }));
+ await user.click(trigger());
+ const now = today(getLocalTimeZone()).toDate(getLocalTimeZone());
+ const todayLabel = now.toLocaleDateString("en-US", {
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ });
+ await waitFor(() => expect(focusedLabel()).toContain(todayLabel));
+ });
+
it("does not blur when focus moves from the field into the popup", async () => {
const user = userEvent.setup();
const onBlur = vi.fn();
diff --git a/packages/core/src/components/date-field/date-field.tsx b/packages/core/src/components/date-field/date-field.tsx
index 351958931..660e53144 100644
--- a/packages/core/src/components/date-field/date-field.tsx
+++ b/packages/core/src/components/date-field/date-field.tsx
@@ -8,7 +8,7 @@ import {
type ReactElement,
type Ref,
} from "react";
-import type { DateValue } from "@internationalized/date";
+import { toCalendarDate, today, type DateValue } from "@internationalized/date";
import { useFieldRootContext } from "@base-ui/react/internals/field-root-context";
import { cn } from "@/lib/utils";
import { useResolvedLocale, useTimeZone } from "@/contexts/appshell-context";
@@ -287,6 +287,15 @@ const DatePicker = forwardRef(function DatePicker {
+ setOpen(nextOpen);
+ // The calendar's focus outlives the popover, so re-land it on the current
+ // (possibly typed) value each time it opens — today when empty (APG).
+ if (nextOpen) {
+ calState.setFocusedDate(val != null ? toCalendarDate(val as never) : today(resolvedTz));
+ }
+ };
+
const localValidationMessage = useMemo(() => {
const key = invalidMessageKey(fieldState.invalidReason);
return key ? t(key) : undefined;
@@ -360,7 +369,7 @@ const DatePicker = forwardRef(function DatePicker setOpen(true)}
+ onOpenCalendar={() => handleOpenChange(true)}
isDisabled={bindings.isDisabled}
isReadOnly={bindings.isReadOnly}
isInvalid={bindings.isInvalid}
diff --git a/packages/core/src/components/date-field/date-range-picker.test.tsx b/packages/core/src/components/date-field/date-range-picker.test.tsx
index fd6e6a3a2..b46310882 100644
--- a/packages/core/src/components/date-field/date-range-picker.test.tsx
+++ b/packages/core/src/components/date-field/date-range-picker.test.tsx
@@ -37,6 +37,9 @@ async function typeDate(user: ReturnType, field: Field,
await user.keyboard(year);
}
+/** Accessible name of the focused element — the roving-focus day cell once the popover opens. */
+const focusedLabel = () => document.activeElement?.getAttribute("aria-label") ?? "";
+
const normalizeIds = (html: string) => html.replace(/id="base-ui-[^"]+"/g, 'id="base-ui-ID"');
// ─── Snapshot ─────────────────────────────────────────────────────────────────
@@ -448,6 +451,21 @@ describe("DateRangePicker popover", () => {
expect(await screen.findByRole("dialog")).toBeDefined();
});
+ it("opens on a typed end date when only the end is set, not a stale month", async () => {
+ const user = userEvent.setup();
+ render();
+
+ // Page away from today and dismiss — the next open must not reuse that month.
+ await user.click(screen.getByRole("button", { name: "Open calendar" }));
+ await screen.findByRole("dialog");
+ await user.keyboard("{Shift>}{PageUp}{/Shift}{Escape}");
+
+ await typeDate(user, "end date", "2025-12-20");
+ seg("end date", "year").focus();
+ await user.keyboard("{Alt>}{ArrowDown}{/Alt}");
+ await waitFor(() => expect(focusedLabel()).toMatch(/December 20, 2025/));
+ });
+
it("drops a dangling anchor when the popover is dismissed mid-selection", async () => {
const user = userEvent.setup();
const onChange = vi.fn();
diff --git a/packages/core/src/components/date-field/date-range-picker.tsx b/packages/core/src/components/date-field/date-range-picker.tsx
index 548c1f569..3daf965a1 100644
--- a/packages/core/src/components/date-field/date-range-picker.tsx
+++ b/packages/core/src/components/date-field/date-range-picker.tsx
@@ -9,7 +9,7 @@ import {
type ReactElement,
type Ref,
} from "react";
-import { toCalendarDate, type DateValue } from "@internationalized/date";
+import { toCalendarDate, today, type DateValue } from "@internationalized/date";
import { cn } from "@/lib/utils";
import { useFieldRootContext } from "@base-ui/react/internals/field-root-context";
import { useResolvedLocale, useTimeZone } from "@/contexts/appshell-context";
@@ -266,8 +266,13 @@ const DateRangePicker = forwardRef(function DateRangePicker {
setOpen(nextOpen);
if (nextOpen) {
- // Land the roving focus on the range start (react-aria's autofocus target).
- if (startVal != null) calendarState.setFocusedDate(toCalendarDate(startVal as never));
+ // Land the roving focus on the range start (react-aria's autofocus target),
+ // else whichever end is typed, else today — never a stale month from a
+ // previous open.
+ const target = startVal ?? endVal;
+ calendarState.setFocusedDate(
+ target != null ? toCalendarDate(target as never) : today(resolvedTz),
+ );
} else {
// Dismissed mid-selection — drop the dangling anchor.
calendarState.cancelSelection();