fix: improve mobile header and board menus (#6715)

* fix: improve mobile header and board menus

* fix: keep mobile header logo condensed

* refactor: share mobile breakpoint handling
This commit is contained in:
Thomas authored and GitHub committed 2026-08-27 11:08:27 +02:00
1 parent 643ecf3df7
commit 9c441571bc
13 files changed
+208 -64

No files matched your search

@@ -2,6 +2,15 @@
overflow: hidden;
}
.header[data-desktop-visible="false"] {
display: none;
}
.desktopContent {
width: 100%;
height: 100%;
}
.content {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
@@ -64,6 +73,41 @@
flex: 0 0 auto;
}
.mobileContent {
display: none;
align-items: center;
justify-content: space-between;
gap: var(--mantine-spacing-xs);
width: 100%;
height: 100%;
padding-inline: var(--mantine-spacing-xs);
}
.mobileIdentity,
.mobileActions {
min-width: 0;
}
.mobileIdentity {
flex: 1 1 auto;
overflow: hidden;
}
.mobileIdentity > :last-child {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
}
.mobileIdentity h2 {
overflow: hidden;
text-overflow: ellipsis;
}
.mobileActions {
flex: 0 0 auto;
}
.floatingControls {
position: fixed;
z-index: var(--homarr-z-index-board-header);
@@ -111,18 +155,21 @@
}
@media (max-width: $mantine-breakpoint-sm) {
.content {
gap: var(--mantine-spacing-xs);
padding-inline: var(--mantine-spacing-xs);
.header[data-desktop-visible="false"] {
display: block;
}
.zone {
gap: var(--mantine-spacing-xs);
.desktopContent {
display: none;
}
.search {
width: auto;
flex: 0 0 auto;
.mobileContent {
display: flex;
}
.floatingControls,
.floatingControlsCorner {
display: none;
}
}
@@ -2,15 +2,7 @@
import type { ReactNode } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import {
AppShellHeader,
Avatar,
Group,
Indicator,
Loader,
Tooltip,
useMantineColorScheme,
} from "@mantine/core";
import { AppShellHeader, Avatar, Group, Indicator, Loader, Tooltip, useMantineColorScheme } from "@mantine/core";
import {
IconBrandDocker,
IconHome,
@@ -116,14 +108,15 @@ export const ConfigurableHeader = ({
return (
<>
{headerPreferences.visible ? (
<AppShellHeader
maw="100vw"
zIndex="var(--homarr-z-index-board-header)"
className={classes.header}
data-advanced-focus-background
data-app-shell-header
>
<AppShellHeader
maw="100vw"
zIndex="var(--homarr-z-index-board-header)"
className={classes.header}
data-desktop-visible={headerPreferences.visible}
data-advanced-focus-background
data-app-shell-header
>
<div className={classes.desktopContent}>
<div className={classes.content}>
<div className={classes.zone} data-zone="left">
{hasNavigation ? <ClientBurger /> : null}
@@ -137,8 +130,31 @@ export const ConfigurableHeader = ({
{actions ? <Group className={classes.contextActions}>{actions}</Group> : null}
</div>
</div>
</AppShellHeader>
) : (
</div>
<div className={classes.mobileContent}>
<Group className={classes.mobileIdentity} gap="xs" wrap="nowrap">
{hasNavigation ? <ClientBurger /> : null}
<HeaderLogo display="logo" logo={logo} logoWithTitle={logoWithTitle} label={t("items.logo")} />
</Group>
<Group className={classes.mobileActions} gap="xs" wrap="nowrap">
{actions}
{boardEditAction}
{boardSettingsAction}
<TourTarget id="board-search">
<MobileSearchButton alwaysVisible />
</TourTarget>
<TourTarget id="board-user-menu">
<UserButtonClient
avatar={avatar}
isAdmin={isAdmin}
isDockerEnabled={isDockerEnabled}
boardSwitcher={boardSwitcher}
/>
</TourTarget>
</Group>
</div>
</AppShellHeader>
{!headerPreferences.visible ? (
<FloatingHeaderControls>
{hasNavigation ? <ClientBurger /> : null}
<UserButtonClient
@@ -148,7 +164,7 @@ export const ConfigurableHeader = ({
boardSwitcher={boardSwitcher}
/>
</FloatingHeaderControls>
)}
) : null}
<LazySpotlight />
</>
);
@@ -291,7 +307,7 @@ const HeaderItem = ({
<Tooltip label={board.name}>
<HeaderButton href={`/boards/${encodeURIComponent(board.name)}`} aria-label={board.name}>
<Avatar src={board.logoImageUrl} size={22} radius="sm">
<IconLayoutDashboard size={17} stroke={1.5} />
{getBoardInitial(board.name)}
</Avatar>
</HeaderButton>
</Tooltip>
@@ -299,14 +315,7 @@ const HeaderItem = ({
}
if (item.id === "logo") {
return (
<HeaderLogo
display={logoDisplay}
logo={logo}
logoWithTitle={logoWithTitle}
label={label("logo")}
/>
);
return <HeaderLogo display={logoDisplay} logo={logo} logoWithTitle={logoWithTitle} label={label("logo")} />;
}
if (item.id === "boardEdit") return boardEditAction;
@@ -416,3 +425,5 @@ const HeaderItem = ({
</TourTarget>
);
};
const getBoardInitial = (name: string) => Array.from(name.trim())[0]?.toLocaleUpperCase() ?? "?";