diff --git a/apps/calendar-web/src/components/SelectView/SelectView.test.tsx b/apps/calendar-web/src/components/SelectView/SelectView.test.tsx
index 5bc6cbbfe..de2077ca8 100644
--- a/apps/calendar-web/src/components/SelectView/SelectView.test.tsx
+++ b/apps/calendar-web/src/components/SelectView/SelectView.test.tsx
@@ -8,6 +8,7 @@ import userEvent from "@testing-library/user-event";
import { createTestRouter } from "@web/__tests__/utils/providers/createTestRouter";
import * as trackModule from "@web/auth/posthog/track";
import { ROOT_ROUTES } from "@web/common/constants/routes";
+import { nowCueActions } from "@web/grid/now-cue/now-cue.store";
import {
initialPointerHintState,
usePointerHintStore,
@@ -82,6 +83,7 @@ describe("SelectView", () => {
afterEach(() => {
resetPointerIntentSessionForTests();
usePointerHintStore.setState(initialPointerHintState, true);
+ nowCueActions.resetForTests();
});
const renderWithRouter = async (
@@ -127,6 +129,26 @@ describe("SelectView", () => {
expect(button).toHaveAttribute("aria-expanded", "false");
});
+ it("pulses the day heading text when the now cue is active", async () => {
+ nowCueActions.pulse();
+ const router = createTestRouter(
+ ,
+ { initialEntries: [ROOT_ROUTES.DAY] },
+ );
+ render();
+ await waitFor(() => {
+ expect(router.state.status).toBe("idle");
+ });
+
+ expect(screen.getByText("Monday, July 20")).toHaveClass(
+ "c-now-cue-label-pulse",
+ );
+ });
+
it("renders the label as the page heading", async () => {
await renderWithRouter("Monday, July 20", ROOT_ROUTES.DAY);
diff --git a/apps/calendar-web/src/components/SelectView/SelectView.tsx b/apps/calendar-web/src/components/SelectView/SelectView.tsx
index 68b09167c..4b9ec76d1 100644
--- a/apps/calendar-web/src/components/SelectView/SelectView.tsx
+++ b/apps/calendar-web/src/components/SelectView/SelectView.tsx
@@ -188,7 +188,7 @@ export const SelectView = ({
{label}
diff --git a/apps/calendar-web/src/index.css b/apps/calendar-web/src/index.css
index 1822a4668..53a921861 100644
--- a/apps/calendar-web/src/index.css
+++ b/apps/calendar-web/src/index.css
@@ -159,6 +159,10 @@
--animate-keycap-flash: keycapFlash 700ms ease-out both;
--animate-level-pulse: levelPulse 700ms cubic-bezier(0.16, 1, 0.3, 1) both;
--animate-now-cue-pulse: nowCuePulse 700ms cubic-bezier(0.16, 1, 0.3, 1) both;
+ --animate-now-cue-label-pulse: nowCueLabelPulse 700ms
+ cubic-bezier(0.16, 1, 0.3, 1) both;
+ --animate-now-cue-label-gradient-pulse: nowCueLabelGradientPulse 700ms
+ cubic-bezier(0.16, 1, 0.3, 1) both;
--animate-keycap-next: keycapNext 1.1s ease-in-out infinite;
--animate-game-howto-in: gameHowtoIn 500ms cubic-bezier(0.16, 1, 0.3, 1) both;
--animate-game-hint-in: gameHintIn 300ms ease-out both;
@@ -259,7 +263,7 @@
}
}
- /* Go-to-today no-op: briefly highlight today's label and the now line. */
+ /* Go-to-today no-op: ring pulse on the now line only. */
@keyframes nowCuePulse {
0%,
100% {
@@ -273,6 +277,30 @@
}
}
+ /* Go-to-today no-op: accent color pulse on day labels (solid text). */
+ @keyframes nowCueLabelPulse {
+ 0%,
+ 100% {
+ color: inherit;
+ filter: brightness(1);
+ }
+ 45% {
+ color: var(--color-accent);
+ filter: brightness(1.2);
+ }
+ }
+
+ /* Same cue on gradient day numbers: brightness only, no layout shift. */
+ @keyframes nowCueLabelGradientPulse {
+ 0%,
+ 100% {
+ filter: brightness(1);
+ }
+ 45% {
+ filter: brightness(1.35);
+ }
+ }
+
/* The exact next key to press pulses until it is pressed. */
@keyframes keycapNext {
0%,
@@ -766,6 +794,16 @@
@apply motion-reduce:animate-none;
}
+@utility c-now-cue-label-pulse {
+ animation: var(--animate-now-cue-label-pulse);
+ @apply motion-reduce:animate-none;
+}
+
+@utility c-now-cue-label-gradient-pulse {
+ animation: var(--animate-now-cue-label-gradient-pulse);
+ @apply motion-reduce:animate-none;
+}
+
/* The task card's "press this next" chip: accent glow, insistent pulse. */
@utility c-keycap-next {
border-color: var(--color-accent);
diff --git a/apps/calendar-web/src/views/Week/components/Header/DayLabels.test.tsx b/apps/calendar-web/src/views/Week/components/Header/DayLabels.test.tsx
index 1cfb807e9..ead64fb8b 100644
--- a/apps/calendar-web/src/views/Week/components/Header/DayLabels.test.tsx
+++ b/apps/calendar-web/src/views/Week/components/Header/DayLabels.test.tsx
@@ -1,5 +1,6 @@
import { cleanup, render, screen } from "@testing-library/react";
import dayjs from "@core/util/date/dayjs";
+import { nowCueActions, useNowCueStore } from "@web/grid/now-cue/now-cue.store";
import { pageJumpHintActions } from "@web/shortcuts/page-jump/page-jump.store";
import { eventJumpActions } from "@web/shortcuts/shift-hint/event-jump.store";
import { getEffectiveTimeZone } from "@web/timezone/effective-timezone.store";
@@ -30,6 +31,21 @@ describe("DayLabels", () => {
cleanup();
eventJumpActions.reset();
pageJumpHintActions.reset();
+ nowCueActions.resetForTests();
+ });
+
+ it("pulses today label text when the now cue is active", () => {
+ nowCueActions.pulse();
+ renderLabels();
+
+ expect(useNowCueStore.getState().active).toBe(true);
+ expect(screen.getByText("17")).toHaveClass(
+ "c-now-cue-label-gradient-pulse",
+ );
+ expect(screen.getByText("Mon")).toHaveClass("c-now-cue-label-pulse");
+ expect(
+ screen.getByText("17").closest("[class*='c-now-cue-pulse']"),
+ ).toBeNull();
});
it("shows the effective timezone in the grid corner", () => {
diff --git a/apps/calendar-web/src/views/Week/components/Header/DayLabels.tsx b/apps/calendar-web/src/views/Week/components/Header/DayLabels.tsx
index 5f918279c..aba19c689 100644
--- a/apps/calendar-web/src/views/Week/components/Header/DayLabels.tsx
+++ b/apps/calendar-web/src/views/Week/components/Header/DayLabels.tsx
@@ -114,7 +114,6 @@ export const DayLabels: FC = ({
className={cn(
"flex items-end justify-center gap-1 rounded-sm px-1 transition-colors duration-150 motion-reduce:transition-none",
isJumpDay && "bg-accent/15 text-accent",
- isToday && pulseTodayLabel && "c-now-cue-pulse",
)}
data-jump-day={isJumpDay ? "true" : undefined}
key={getWeekDayLabel(day)}
@@ -125,6 +124,9 @@ export const DayLabels: FC = ({
className={cn(
"relative text-[clamp(var(--font-size-xl),2.7cqw,var(--font-size-xxl))] leading-none",
isToday && "c-text-gradient",
+ isToday &&
+ pulseTodayLabel &&
+ "c-now-cue-label-gradient-pulse motion-reduce:transition-none",
)}
>
{dayNumber}
@@ -136,7 +138,14 @@ export const DayLabels: FC = ({
{isEventJumpActive ? prefix : `⇧${prefix}`}
) : (
-
+
{day.format("ddd")}
)}