diff --git a/src/app/notifications/page.tsx b/src/app/notifications/page.tsx index ba17b08c5..f84bc2fbd 100644 --- a/src/app/notifications/page.tsx +++ b/src/app/notifications/page.tsx @@ -201,6 +201,7 @@ export default function NotificationsPage() { title={t("notifications.title")} description={t("notifications.subtitle")} actions={headerActions} + stackActionsOnPhone /> {/* Desktop: table layout */} diff --git a/src/components/ui/page-header.tsx b/src/components/ui/page-header.tsx index e806e5294..c6e8ef28e 100644 --- a/src/components/ui/page-header.tsx +++ b/src/components/ui/page-header.tsx @@ -26,6 +26,15 @@ import { cn } from "@/lib/utils"; * shells hand-rolled the whole block to get the second copy, which is how * they drifted apart from every module page in the first place. */ +/** + * A one-word German title ("Krankheitstagebuch") is wider than the column a + * phone leaves beside the actions, and without a break opportunity it ran + * underneath them. `hyphens-auto` breaks it by the page's `lang`; the + * `wrap-break-word` floor covers a word the hyphenation dictionary does not. + */ +const HEADING_CLASS = + "text-2xl font-bold tracking-tight hyphens-auto wrap-break-word"; + export function PageHeader({ title, titleId, @@ -34,6 +43,7 @@ export function PageHeader({ backLink, topSlot, actions, + stackActionsOnPhone = false, className, }: { title: ReactNode; @@ -44,37 +54,65 @@ export function PageHeader({ /** Optional block above the title (e.g. a hub back-link the shell owns). */ topSlot?: ReactNode; actions?: ReactNode; + /** + * Below `sm`, move the actions onto their own row under the description. + * For text-labelled actions (two outline links, say): beside the title they + * left the H1 a column narrower than its own word. Icon actions fit beside + * the title and leave this off. + */ + stackActionsOnPhone?: boolean; className?: string; }) { const heading = headingAs === "div" ? ( -
+
{title}
) : ( -

+

{title}

); + // Title, description and actions share one grid. On a phone the + // description spans the full row under the title + actions: a header with + // three or four icon actions otherwise left the sentence a ~130 px column + // that wrapped five or six times. From `sm` up the actions span both rows, + // so the description sits beside them exactly as before. return (
{backLink ? : null} {topSlot} -
-
- {heading} - {description ? ( -

{description}

- ) : null} -
+
+
{heading}
{actions ? ( -
{actions}
+
+ {actions} +
+ ) : null} + {description ? ( +

+ {description} +

) : null}