From 262357acef2f5b0d6e7d5ca8d322e43501d8161d Mon Sep 17 00:00:00 2001 From: bbassie <17990055+bbassie@users.noreply.github.com> Date: Mon, 22 Jun 2026 21:16:49 +0000 Subject: [PATCH] Rework site layout into a fixed sidebar Remove the top header and move its contents into the sidebar, which is now sticky and full height: logo and version at the top, navigation in the middle, and the account menu pinned to the bottom (opening to the right). The sidebar is rebuilt with antd Flex, Typography and Menu so almost all styling comes from antd, reducing the custom css to a single sticky rule. The nav menus also use theme="dark" to match the sider and fix the low contrast active item. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/web_ui/src/components/SiteLayout.tsx | 119 +++++++++--------- packages/web_ui/src/style.css | 80 +----------- 2 files changed, 65 insertions(+), 134 deletions(-) diff --git a/packages/web_ui/src/components/SiteLayout.tsx b/packages/web_ui/src/components/SiteLayout.tsx index 76338056..d2a9f1f9 100644 --- a/packages/web_ui/src/components/SiteLayout.tsx +++ b/packages/web_ui/src/components/SiteLayout.tsx @@ -1,6 +1,6 @@ import React, { Fragment, useContext, useEffect, useState } from "react"; import { Route, Routes, useNavigate } from "react-router-dom"; -import { Button, Dropdown, Layout, Menu, MenuProps, Tooltip } from "antd"; +import { Button, Flex, Layout, Menu, MenuProps, Tooltip, Typography } from "antd"; import { UserOutlined, DownloadOutlined } from "@ant-design/icons"; import ErrorBoundary from "./ErrorBoundary"; @@ -14,12 +14,13 @@ import { saveJson } from "../util/save_file"; import { useAccount } from "../model/account"; import { DraggingContext } from "../model/is_dragging"; import webUiPackage from "../../package.json"; +import logo from "../images/logo.png"; import { ControlConfig } from "@clusterio/lib"; type MenuItem = Required["items"][number]; -const { Header, Sider } = Layout; +const { Sider } = Layout; function isActiveDropzone(element: HTMLElement | null): boolean { if (!element) { @@ -127,6 +128,8 @@ export default function SiteLayout() { } return setDraggingProxy(1)} onDragLeave={() => setDraggingProxy(-1)} @@ -170,70 +173,68 @@ export default function SiteLayout() { }} /> 0}> -
-
- Clusterio - - setChangeLogOpen(true)} - > - {webUiPackage.version} - - - - - - -
- - + + + + Clusterio logo + + Clusterio + + setChangeLogOpen(true)} + > + {webUiPackage.version} + + + + navigate(key)} items={menuItems} /> - - - - {combinedPages.map(({ path, sidebarPath, content }) => - - - {content} - - } - />)} - } /> - - + , + label: account.name, + children: accountMenuProps.items, + }]} + /> + + + + + {combinedPages.map(({ path, sidebarPath, content }) => + + + {content} + + } + />)} + } /> + ; diff --git a/packages/web_ui/src/style.css b/packages/web_ui/src/style.css index b1f53739..9a07fc34 100644 --- a/packages/web_ui/src/style.css +++ b/packages/web_ui/src/style.css @@ -56,13 +56,12 @@ body { } } +/* Keep the sidebar fixed in place while the page content scrolls */ .site-layout-sider { - z-index: 0; -} - -.header { - padding-left: 250px; - padding-right: 64px; + position: sticky; + top: 0; + height: 100vh; + align-self: flex-start; } .page-header { @@ -105,75 +104,6 @@ body { margin-left: auto; } -.site-name { - position: absolute; - top: -10px; - left: 112px; - font-size: 18pt; - font-weight: 400; -} - -.site-version { - position: absolute; - top: 10px; - left: 120px; - color: #f00; -} - -.site-logo { - position: absolute; - left: 36px; - width: 64px; - height: 64px; - background-image: url("./images/logo.png"); - background-size: 64px; -} - -@media not all and (min-width: 576px) { - .header { - padding-left: 88px; - } - - .site-logo { - left: 12px; - } - - .site-name { - display: none; - } - - .site-version { - display: none; - } -} - -.account-dropdown-header { - position: absolute; - right: 0; - top: 0; - width: 64px; - height: 64px; - font-size: 18px; - line-height: 72px; - display: flex; - justify-content: center; -} - -.account-dropdown-header::after { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - background: transparent; - transition: all 0.3s; - content: ""; -} - -.account-dropdown-header:hover::after { - background: rgba(255, 255, 255, 0.1); -} - .error-traceback code { display: block; margin: 0;