diff --git a/frontend/editor/src/core/components/pageTracks/PageTracks.module.css b/frontend/editor/src/core/components/pageTracks/PageTracks.module.css
index 0b3f8ea5c2..44b07525c4 100644
--- a/frontend/editor/src/core/components/pageTracks/PageTracks.module.css
+++ b/frontend/editor/src/core/components/pageTracks/PageTracks.module.css
@@ -233,6 +233,25 @@
pointer-events: auto;
}
+/* ── Unsaved-changes marker on the save button ───────────────────────────── */
+
+.saveButton {
+ position: relative;
+}
+
+.unsavedDot {
+ position: absolute;
+ top: 0.125rem;
+ right: 0.125rem;
+ width: 0.4375rem;
+ height: 0.4375rem;
+ border-radius: 999px;
+ background: var(--c-danger);
+ /* Ringed in the bar's own surface so it reads against the icon behind it. */
+ box-shadow: 0 0 0 1.5px var(--c-bg-raised);
+ pointer-events: none;
+}
+
/* ── Track reorder ───────────────────────────────────────────────────────── */
.trackDragging {
diff --git a/frontend/editor/src/core/components/pageTracks/hooks/usePageTracksWorkbenchBarButtons.tsx b/frontend/editor/src/core/components/pageTracks/hooks/usePageTracksWorkbenchBarButtons.tsx
index 5f2f2f8816..9eb074c38c 100644
--- a/frontend/editor/src/core/components/pageTracks/hooks/usePageTracksWorkbenchBarButtons.tsx
+++ b/frontend/editor/src/core/components/pageTracks/hooks/usePageTracksWorkbenchBarButtons.tsx
@@ -9,6 +9,9 @@ import {
WorkbenchBarButtonWithAction,
} from "@app/hooks/useWorkbenchBarButtons";
import LocalIcon from "@app/components/shared/LocalIcon";
+import { ActionIcon } from "@app/ui/ActionIcon";
+import { Tooltip } from "@app/components/shared/Tooltip";
+import styles from "@app/components/pageTracks/PageTracks.module.css";
export interface PageTracksBarParams {
totalPages: number;
@@ -151,15 +154,32 @@ export function usePageTracksWorkbenchBarButtons(params: PageTracksBarParams) {
},
{
id: "tracks-save",
- icon: ,
tooltip: labels.save,
ariaLabel: labels.save,
section: "bottom" as const,
order: 30,
- active: isDirty,
disabled: !isDirty || saving,
visible: hasPages,
onClick: onSave,
+ // Custom render for the unsaved-changes dot. A custom render also
+ // bypasses the bar's own tooltip wrapper, hence the Tooltip here.
+ render: ({ disabled, triggerAction }) => (
+
+
+
+ {isDirty && }
+
+
+ ),
},
],
[