diff --git a/CHANGELOG.md b/CHANGELOG.md
index bf91442..2943bd1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
All notable changes to Epicure are documented here. This file is mirrored in-app at `/changelog` (and in the admin dashboard) via `apps/web/lib/changelog.ts` — update both together.
+## 0.28.0 — 2026-07-14 14:05
+
+### Added
+- **Meal plan and shopping list header buttons now match the recipe page's icon+tooltip style** — Share, Generate shopping list, Shopping lists, Print, Export to calendar, Send to grocery delivery all collapsed to icon-only with a tooltip, instead of crowding the header with text labels.
+- **Week-switch arrows on the meal plan now sit right next to the week date**, instead of at the far end of the button row.
+
## 0.27.2 — 2026-07-14 13:20
### Fixed
diff --git a/apps/web/app/(app)/meal-plan/page.tsx b/apps/web/app/(app)/meal-plan/page.tsx
index fbd330e..bcc1c9a 100644
--- a/apps/web/app/(app)/meal-plan/page.tsx
+++ b/apps/web/app/(app)/meal-plan/page.tsx
@@ -5,6 +5,7 @@ import { ChevronLeft, ChevronRight, ShoppingCart, Printer, CalendarDays } from "
import { auth } from "@/lib/auth/server";
import { db, mealPlans, mealPlanMembers, recipes, userNutritionGoals, eq, and, desc } from "@epicure/db";
import { buttonVariants } from "@/components/ui/button";
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { MealPlanner } from "@/components/meal-plan/meal-planner";
import { ShareMealPlanButton } from "@/components/meal-plan/share-meal-plan-button";
import { NewShoppingListButton } from "@/components/meal-plan/new-shopping-list-button";
@@ -110,10 +111,29 @@ export default async function MealPlanPage({
return (
+
{msgs.mealPlan.title}
-
{label}
+
+
+
+
+
+ } />
+ {msgs.mealPlan.previousWeek}
+
+
{label}
+
+
+
+
+ } />
+ {msgs.mealPlan.nextWeek}
+
+
@@ -121,30 +141,37 @@ export default async function MealPlanPage({
defaultWeekStart={weekStart}
defaultName={formatMessage(msgs.mealPlan.shoppingListWeekName, { week: label })}
/>
-
-
- {msgs.mealPlan.shoppingLists}
-
-
-
- {msgs.common.print}
-
+
+
+
+
+ } />
+ {msgs.mealPlan.shoppingLists}
+
+
+
+
+
+ } />
+ {msgs.common.print}
+
-
-
- {msgs.mealPlan.exportCalendar}
-
-
-
-
-
-
-
+
+
+
+
+ } />
+ {msgs.mealPlan.exportCalendar}
+
+
diff --git a/apps/web/app/(app)/shopping-lists/[id]/page.tsx b/apps/web/app/(app)/shopping-lists/[id]/page.tsx
index c47b401..e7687c9 100644
--- a/apps/web/app/(app)/shopping-lists/[id]/page.tsx
+++ b/apps/web/app/(app)/shopping-lists/[id]/page.tsx
@@ -10,6 +10,7 @@ import { ShareShoppingListButton } from "@/components/shopping-lists/share-shopp
import { GroceryExportButton } from "@/components/shopping-lists/grocery-export-button";
import { ShoppingListActionsMenu } from "@/components/shopping-lists/shopping-list-actions-menu";
import { buttonVariants } from "@/components/ui/button";
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
import { getShoppingListAccess, canWriteShoppingList } from "@/lib/shopping-list-access";
import { ExportMarkdownButton } from "@/components/shared/export-markdown-button";
@@ -48,15 +49,20 @@ export default async function ShoppingListPage({ params }: Params) {
{list.generatedAt ? m.shoppingLists.fromMealPlan : ""}
+
{access.role === "owner" && (
)}
-
-
- {m.common.print}
-
+
+
+
+
+ } />
+ {m.common.print}
+
)}
+
-
- {defaultWeekStart ? : }
- {defaultWeekStart ? t("generateFromThisWeek") : t("newList")}
-
+ {defaultWeekStart ? (
+
+
+
+
+
+ } />
+ {t("generateFromThisWeek")}
+
+
+ ) : (
+
+
+ {t("newList")}
+
+ )}
{t("newListTitle")}
diff --git a/apps/web/components/meal-plan/share-meal-plan-button.tsx b/apps/web/components/meal-plan/share-meal-plan-button.tsx
index e52ab76..f3e2c53 100644
--- a/apps/web/components/meal-plan/share-meal-plan-button.tsx
+++ b/apps/web/components/meal-plan/share-meal-plan-button.tsx
@@ -21,6 +21,7 @@ import {
SelectValue,
} from "@/components/ui/select";
import { Badge } from "@/components/ui/badge";
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
type Role = "viewer" | "editor";
@@ -116,10 +117,16 @@ export function ShareMealPlanButton({ weekStart }: Props) {
return (
<>
- handleOpenChange(true)}>
-
- {tCommon("share")}
-
+
+
+ handleOpenChange(true)} aria-label={tCommon("share")}>
+
+
+ } />
+ {tCommon("share")}
+
+
diff --git a/apps/web/components/shopping-lists/grocery-export-button.tsx b/apps/web/components/shopping-lists/grocery-export-button.tsx
index 9efd580..c486cd1 100644
--- a/apps/web/components/shopping-lists/grocery-export-button.tsx
+++ b/apps/web/components/shopping-lists/grocery-export-button.tsx
@@ -11,6 +11,7 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import type { GroceryExportPayload } from "@/lib/grocery-export";
import { groceryExportToText } from "@/lib/grocery-export";
@@ -62,12 +63,18 @@ export function GroceryExportButton({ listId, instacartEnabled }: Props) {
return (
-
-
- {t("sendToGroceryDelivery")}
-
- } />
+
+
+
+
+
+ } />
+ } />
+ {t("sendToGroceryDelivery")}
+
+
void handleCopy()}>
diff --git a/apps/web/components/shopping-lists/share-shopping-list-button.tsx b/apps/web/components/shopping-lists/share-shopping-list-button.tsx
index e97f270..90493bd 100644
--- a/apps/web/components/shopping-lists/share-shopping-list-button.tsx
+++ b/apps/web/components/shopping-lists/share-shopping-list-button.tsx
@@ -23,6 +23,7 @@ import {
} from "@/components/ui/select";
import { Badge } from "@/components/ui/badge";
import { Separator } from "@/components/ui/separator";
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
type Role = "viewer" | "editor";
@@ -185,10 +186,16 @@ export function ShareShoppingListButton({ listId, initialIsPublic, initialPublic
return (
<>
- handleOpenChange(true)}>
-
- {tCommon("share")}
-
+
+
+ handleOpenChange(true)} aria-label={tCommon("share")}>
+
+
+ } />
+ {tCommon("share")}
+
+
diff --git a/apps/web/lib/changelog.ts b/apps/web/lib/changelog.ts
index 2e0869d..7d493fd 100644
--- a/apps/web/lib/changelog.ts
+++ b/apps/web/lib/changelog.ts
@@ -1,5 +1,5 @@
// Mirrors CHANGELOG.md at the repo root — update both together.
-export const APP_VERSION = "0.27.2";
+export const APP_VERSION = "0.28.0";
export type ChangelogEntry = {
version: string;
@@ -11,6 +11,14 @@ export type ChangelogEntry = {
};
export const CHANGELOG: ChangelogEntry[] = [
+ {
+ version: "0.28.0",
+ date: "2026-07-14 14:05",
+ added: [
+ "**Meal plan and shopping list header buttons now match the recipe page's icon+tooltip style** — Share, Generate shopping list, Shopping lists, Print, Export to calendar, Send to grocery delivery all collapsed to icon-only with a tooltip, instead of crowding the header with text labels.",
+ "**Week-switch arrows on the meal plan now sit right next to the week date**, instead of at the far end of the button row.",
+ ],
+ },
{
version: "0.27.2",
date: "2026-07-14 13:20",
diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json
index 90d90ea..bdc2b53 100644
--- a/apps/web/messages/en.json
+++ b/apps/web/messages/en.json
@@ -856,6 +856,8 @@
"markCookedSuccess": "Marked as cooked",
"markCookedFailed": "Failed to mark as cooked",
"exportCalendar": "Export to calendar",
+ "previousWeek": "Previous week",
+ "nextWeek": "Next week",
"clearDay": "Clear day",
"clearWeek": "Clear week",
"clearDayConfirmTitle": "Clear {day}?",
diff --git a/apps/web/messages/fr.json b/apps/web/messages/fr.json
index fd50bfd..d736eba 100644
--- a/apps/web/messages/fr.json
+++ b/apps/web/messages/fr.json
@@ -847,6 +847,8 @@
"markCookedSuccess": "Marqué comme cuisiné",
"markCookedFailed": "Échec du marquage comme cuisiné",
"exportCalendar": "Exporter vers le calendrier",
+ "previousWeek": "Semaine précédente",
+ "nextWeek": "Semaine suivante",
"clearDay": "Vider la journée",
"clearWeek": "Vider la semaine",
"clearDayConfirmTitle": "Vider {day} ?",
diff --git a/apps/web/package.json b/apps/web/package.json
index b6e808b..99a80c3 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -1,6 +1,6 @@
{
"name": "@epicure/web",
- "version": "0.27.2",
+ "version": "0.28.0",
"private": true,
"scripts": {
"dev": "next dev",
diff --git a/package.json b/package.json
index d88f389..05ad25d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "epicure",
- "version": "0.27.2",
+ "version": "0.28.0",
"private": true,
"scripts": {
"dev": "pnpm --filter web dev",