From f52d8e74d717f03fcbf940a5f8535bc2d9ddf5e2 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Wed, 28 Oct 2026 12:54:12 +0100 Subject: [PATCH] :recycle: Move the dashboard header styles to the DS The page header of the dashboard and settings pages took its styles from common/refactor/common-dashboard.scss. Move the rules still in use to dashboard/common/header.scss, written with DS tokens: - The page titles become heading* (title-large) with a class instead of a bare h1; the team section links get a class, and the active class moves from the li to the link. - Drop the rules that matched nothing: .element-name, .context-menu, .icon and .btn-secondary inside the header, and the global .btn-primary and .btn-secondary (only the fonts page uses them; it keeps the old import until its open change lands). - The sidebar and subscription styles imported the file without using it; drop the import. The header looks the same (0 px in the new visual test). The title line height goes from 1.15 to 1.4, which does not move the text. AI-assisted-by: claude-opus-5-5 --- .../ui/visual-specs/visual-dashboard.spec.js | 44 +++++++++ .../app/main/ui/dashboard/common/header.scss | 91 +++++++++++++++++++ .../src/app/main/ui/dashboard/deleted.cljs | 7 +- .../src/app/main/ui/dashboard/deleted.scss | 4 +- frontend/src/app/main/ui/dashboard/files.cljs | 16 +++- frontend/src/app/main/ui/dashboard/files.scss | 2 +- .../src/app/main/ui/dashboard/libraries.cljs | 7 +- .../src/app/main/ui/dashboard/libraries.scss | 2 +- .../src/app/main/ui/dashboard/projects.cljs | 11 ++- .../src/app/main/ui/dashboard/projects.scss | 2 +- .../src/app/main/ui/dashboard/search.cljs | 7 +- .../src/app/main/ui/dashboard/search.scss | 2 +- .../src/app/main/ui/dashboard/sidebar.scss | 3 - .../app/main/ui/dashboard/subscription.scss | 3 - frontend/src/app/main/ui/dashboard/team.cljs | 39 +++++--- frontend/src/app/main/ui/dashboard/team.scss | 5 +- frontend/src/app/main/ui/settings.cljs | 8 +- frontend/src/app/main/ui/settings.scss | 2 +- 18 files changed, 213 insertions(+), 42 deletions(-) create mode 100644 frontend/src/app/main/ui/dashboard/common/header.scss diff --git a/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js b/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js index 878a02d347..eba4e66d69 100644 --- a/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js +++ b/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js @@ -779,6 +779,50 @@ test.describe("Team pages controls", () => { }); }); +test.describe("Dashboard headers", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + const header = (page) => page.getByTestId("dashboard-header"); + + test("Page headers", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + + await dashboardPage.goToDashboard(); + await expect(header(page)).toHaveScreenshot(shot); + await dashboardPage.goToDrafts(); + await expect(header(page)).toHaveScreenshot(shot); + await dashboardPage.goToLibraries(); + await expect(header(page)).toHaveScreenshot(shot); + await dashboardPage.goToFonts(); + await expect(header(page)).toHaveScreenshot(shot); + await dashboardPage.goToDeleted(); + await expect(header(page)).toHaveScreenshot(shot); + await dashboardPage.goToSearch(); + await expect(dashboardPage.mainHeading).toBeVisible(); + await expect(header(page)).toHaveScreenshot(shot); + }); + + test("Team header menu", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await dashboardPage.goToSecondTeamMembersSection(); + await expect(dashboardPage.mainHeading).toHaveText("Members"); + + await page.mouse.move(10, 700); + await expect(header(page)).toHaveScreenshot(shot); + await header(page).getByText("Invitations").hover(); + await expect(header(page)).toHaveScreenshot(shot); + }); + + test("Account header", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.goToDashboard(); + await dashboardPage.goToAccount(); + await expect(page.getByTestId("account-title")).toBeVisible(); + await expect(header(page)).toHaveScreenshot(shot); + }); +}); + test.describe("Settings controls", () => { const shot = { maxDiffPixelRatio: 0, threshold: 0 }; diff --git a/frontend/src/app/main/ui/dashboard/common/header.scss b/frontend/src/app/main/ui/dashboard/common/header.scss new file mode 100644 index 0000000000..2baf23ca7b --- /dev/null +++ b/frontend/src/app/main/ui/dashboard/common/header.scss @@ -0,0 +1,91 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS SUBSIDIARY SL + +// Page header shared by the dashboard and settings pages. + +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/typography.scss" as *; + +.dashboard-header { + display: flex; + align-items: center; + justify-content: space-between; + position: relative; + block-size: $sz-64; + max-block-size: $sz-64; + padding: var(--sp-xs) var(--sp-l) var(--sp-xs) var(--sp-s); + user-select: none; + + &.team { + display: grid; + grid-template-columns: 20% 1fr 20%; + } +} + +.dashboard-title { + display: flex; + align-items: center; + margin-inline-start: var(--sp-m); +} + +.dashboard-title-text { + display: block; + flex-shrink: 0; + inline-size: 100%; + max-inline-size: $sz-712; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + user-select: all; + color: var(--color-foreground-primary); +} + +.dashboard-header-actions { + display: flex; + column-gap: var(--sp-l); +} + +.dashboard-buttons { + display: flex; + justify-content: flex-end; + align-items: center; +} + +.dashboard-header-menu { + display: flex; + align-items: flex-end; + justify-content: center; +} + +.dashboard-header-options { + display: flex; + justify-content: center; + margin: 0; +} + +.dashboard-header-link { + @include use-typography("body-large"); + + --dashboard-header-link-color: var(--color-foreground-secondary); + + display: flex; + align-items: center; + block-size: $sz-40; + padding: var(--sp-xs) var(--sp-xxl); + border-block-end: px2rem(3) solid transparent; + color: var(--dashboard-header-link-color); + + &:hover { + --dashboard-header-link-color: var(--color-foreground-primary); + + text-decoration: none; + } + + &.active { + --dashboard-header-link-color: var(--color-foreground-primary); + } +} diff --git a/frontend/src/app/main/ui/dashboard/deleted.cljs b/frontend/src/app/main/ui/dashboard/deleted.cljs index d2f8420121..014d744734 100644 --- a/frontend/src/app/main/ui/dashboard/deleted.cljs +++ b/frontend/src/app/main/ui/dashboard/deleted.cljs @@ -23,6 +23,8 @@ [app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.foundations.assets.icon :as i] + [app.main.ui.ds.foundations.typography :as t] + [app.main.ui.ds.foundations.typography.heading :refer [heading*]] [app.main.ui.ds.product.empty-placeholder :refer [empty-placeholder*]] [app.main.ui.hooks :as hooks] [app.util.dom :as dom] @@ -61,7 +63,10 @@ [:header {:class (stl/css :dashboard-header) :data-testid "dashboard-header"} [:div#dashboard-deleted-title {:class (stl/css :dashboard-title)} - [:h1 (tr "dashboard.projects-title")]] + [:> heading* {:level 1 + :typography t/title-large + :class (stl/css :dashboard-title-text)} + (tr "dashboard.projects-title")]] [:div {:class (stl/css :dashboard-header-actions)} [:> dashboard-mcp-menu*] [:> layout-toggle* {:layout layout :on-change on-change}]]]) diff --git a/frontend/src/app/main/ui/dashboard/deleted.scss b/frontend/src/app/main/ui/dashboard/deleted.scss index 7b9db012d6..48ca1cfd71 100644 --- a/frontend/src/app/main/ui/dashboard/deleted.scss +++ b/frontend/src/app/main/ui/dashboard/deleted.scss @@ -4,9 +4,7 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -// The shared dashboard header styles move in their own change: they are -// used by ten dashboard modules. -@use "common/refactor/common-dashboard"; +@use "dashboard/common/header"; @use "ds/typography.scss" as *; @use "ds/spacing.scss" as *; @use "ds/z-index.scss" as *; diff --git a/frontend/src/app/main/ui/dashboard/files.cljs b/frontend/src/app/main/ui/dashboard/files.cljs index 2d6a98f519..b2df3175e5 100644 --- a/frontend/src/app/main/ui/dashboard/files.cljs +++ b/frontend/src/app/main/ui/dashboard/files.cljs @@ -23,6 +23,8 @@ [app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.foundations.assets.icon :as i] + [app.main.ui.ds.foundations.typography :as t] + [app.main.ui.ds.foundations.typography.heading :refer [heading*]] [app.main.ui.ds.product.empty-placeholder :refer [empty-placeholder*]] [app.main.ui.hooks :as hooks] [app.util.dom :as dom] @@ -84,7 +86,10 @@ :data-testid "dashboard-header"} (if (:is-default project) [:div#dashboard-drafts-title {:class (stl/css :dashboard-title)} - [:h1 (tr "labels.drafts")]] + [:> heading* {:level 1 + :typography t/title-large + :class (stl/css :dashboard-title-text)} + (tr "labels.drafts")]] (if (and (:edition @local) can-edit) [:& inline-edition @@ -97,9 +102,12 @@ (swap! local assoc :edition false))) :max-length 250}] [:div {:class (stl/css :dashboard-title)} - [:h1 {:on-double-click on-edit - :data-testid "project-title" - :id (:id project)} + [:> heading* {:level 1 + :typography t/title-large + :class (stl/css :dashboard-title-text) + :on-double-click on-edit + :data-testid "project-title" + :id (:id project)} (:name project)]])) [:div {:class (stl/css :dashboard-header-actions)} diff --git a/frontend/src/app/main/ui/dashboard/files.scss b/frontend/src/app/main/ui/dashboard/files.scss index 5ab0695cad..f8a5186c38 100644 --- a/frontend/src/app/main/ui/dashboard/files.scss +++ b/frontend/src/app/main/ui/dashboard/files.scss @@ -4,7 +4,7 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "common/refactor/common-dashboard"; +@use "dashboard/common/header"; @use "ds/_borders.scss" as *; @use "ds/_sizes.scss" as *; @use "ds/_utils.scss" as *; diff --git a/frontend/src/app/main/ui/dashboard/libraries.cljs b/frontend/src/app/main/ui/dashboard/libraries.cljs index 335d6ce621..e899521fca 100644 --- a/frontend/src/app/main/ui/dashboard/libraries.cljs +++ b/frontend/src/app/main/ui/dashboard/libraries.cljs @@ -13,6 +13,8 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.dashboard.grid :refer [grid*]] + [app.main.ui.ds.foundations.typography :as t] + [app.main.ui.ds.foundations.typography.heading :refer [heading*]] [app.main.ui.hooks :as hooks] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -68,7 +70,10 @@ [:* [:header {:class (stl/css :dashboard-header) :data-testid "dashboard-header"} [:div#dashboard-libraries-title {:class (stl/css :dashboard-title)} - [:h1 (tr "dashboard.libraries-title")]]] + [:> heading* {:level 1 + :typography t/title-large + :class (stl/css :dashboard-title-text)} + (tr "dashboard.libraries-title")]]] [:section {:class (stl/css :dashboard-container :no-bg :dashboard-shared) :ref rowref} diff --git a/frontend/src/app/main/ui/dashboard/libraries.scss b/frontend/src/app/main/ui/dashboard/libraries.scss index ae28f34484..eb77ed0444 100644 --- a/frontend/src/app/main/ui/dashboard/libraries.scss +++ b/frontend/src/app/main/ui/dashboard/libraries.scss @@ -5,7 +5,7 @@ // Copyright (c) KALEIDOS SUBSIDIARY SL @use "refactor/common-refactor.scss" as deprecated; -@use "common/refactor/common-dashboard"; +@use "dashboard/common/header"; .dashboard-container { flex: 1 0 0; diff --git a/frontend/src/app/main/ui/dashboard/projects.cljs b/frontend/src/app/main/ui/dashboard/projects.cljs index ad7819e5b4..2371fb89ef 100644 --- a/frontend/src/app/main/ui/dashboard/projects.cljs +++ b/frontend/src/app/main/ui/dashboard/projects.cljs @@ -29,6 +29,8 @@ [app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]] + [app.main.ui.ds.foundations.typography :as t] + [app.main.ui.ds.foundations.typography.heading :refer [heading*]] [app.main.ui.ds.layout.menu :refer [context-menu*]] [app.main.ui.ds.product.empty-placeholder :refer [empty-placeholder*]] [app.main.ui.hooks :as hooks] @@ -48,9 +50,12 @@ [:header {:class (stl/css :dashboard-header) :data-testid "dashboard-header"} [:div#dashboard-projects-title {:class (stl/css :dashboard-title)} - [:h1 (if default-team? - (tr "dashboard.personal-projects") - (tr "dashboard.projects-title"))]] + [:> heading* {:level 1 + :typography t/title-large + :class (stl/css :dashboard-title-text)} + (if default-team? + (tr "dashboard.personal-projects") + (tr "dashboard.projects-title"))]] [:div {:class (stl/css :dashboard-header-actions)} [:> dashboard-mcp-menu*] [:> layout-toggle* {:layout layout diff --git a/frontend/src/app/main/ui/dashboard/projects.scss b/frontend/src/app/main/ui/dashboard/projects.scss index 7eeccbcfb3..050377b6a5 100644 --- a/frontend/src/app/main/ui/dashboard/projects.scss +++ b/frontend/src/app/main/ui/dashboard/projects.scss @@ -4,7 +4,7 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "common/refactor/common-dashboard"; +@use "dashboard/common/header"; @use "ds/typography.scss" as t; @use "ds/_borders.scss" as *; @use "ds/spacing.scss" as *; diff --git a/frontend/src/app/main/ui/dashboard/search.cljs b/frontend/src/app/main/ui/dashboard/search.cljs index 11d613f907..492b1942b3 100644 --- a/frontend/src/app/main/ui/dashboard/search.cljs +++ b/frontend/src/app/main/ui/dashboard/search.cljs @@ -13,6 +13,8 @@ [app.main.store :as st] [app.main.ui.dashboard.grid :refer [grid*]] [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] + [app.main.ui.ds.foundations.typography :as t] + [app.main.ui.ds.foundations.typography.heading :refer [heading*]] [app.main.ui.hooks :as hooks] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -55,7 +57,10 @@ [:* [:header {:class (stl/css :dashboard-header) :data-testid "dashboard-header"} [:div#dashboard-search-title {:class (stl/css :dashboard-title)} - [:h1 (tr "dashboard.title-search")]]] + [:> heading* {:level 1 + :typography t/title-large + :class (stl/css :dashboard-title-text)} + (tr "dashboard.title-search")]]] [:section {:class (stl/css :dashboard-container :search :no-bg) :ref rowref} diff --git a/frontend/src/app/main/ui/dashboard/search.scss b/frontend/src/app/main/ui/dashboard/search.scss index 4848eeaa97..a6f6543d91 100644 --- a/frontend/src/app/main/ui/dashboard/search.scss +++ b/frontend/src/app/main/ui/dashboard/search.scss @@ -5,7 +5,7 @@ // Copyright (c) KALEIDOS SUBSIDIARY SL @use "refactor/common-refactor.scss" as deprecated; -@use "common/refactor/common-dashboard"; +@use "dashboard/common/header"; @use "./placeholder"; .dashboard-container { diff --git a/frontend/src/app/main/ui/dashboard/sidebar.scss b/frontend/src/app/main/ui/dashboard/sidebar.scss index 21a5c112bf..7863b8244e 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.scss +++ b/frontend/src/app/main/ui/dashboard/sidebar.scss @@ -13,9 +13,6 @@ @use "ds/z-index.scss" as *; @use "ds/mixins.scss" as *; -// The shared dashboard header styles move in their own change: they are -// used by ten dashboard modules. -@use "common/refactor/common-dashboard"; // refactor/ stays for the menu placeholders (%menu-dropdown, // %menu-item-base) of the profile menu and its submenus. diff --git a/frontend/src/app/main/ui/dashboard/subscription.scss b/frontend/src/app/main/ui/dashboard/subscription.scss index e90a800019..db334f678d 100644 --- a/frontend/src/app/main/ui/dashboard/subscription.scss +++ b/frontend/src/app/main/ui/dashboard/subscription.scss @@ -8,9 +8,6 @@ // power up submenu. @use "refactor/common-refactor.scss" as deprecated; -// The shared dashboard header styles move in their own change: they are -// used by ten dashboard modules. -@use "common/refactor/common-dashboard"; @use "ds/typography.scss" as *; @use "ds/_borders.scss" as *; @use "ds/_utils.scss" as *; diff --git a/frontend/src/app/main/ui/dashboard/team.cljs b/frontend/src/app/main/ui/dashboard/team.cljs index 8888c6d4e3..eb7b2f9798 100644 --- a/frontend/src/app/main/ui/dashboard/team.cljs +++ b/frontend/src/app/main/ui/dashboard/team.cljs @@ -94,23 +94,34 @@ [:header {:class (stl/css :dashboard-header :team) :data-testid "dashboard-header"} [:div {:class (stl/css :dashboard-title)} - [:h1 (cond - members-section? (tr "labels.members") - settings-section? (tr "labels.settings") - invitations-section? (tr "labels.invitations") - webhooks-section? (tr "labels.webhooks") - :else nil)]] + [:> heading* {:level 1 + :typography t/title-large + :class (stl/css :dashboard-title-text)} + (cond + members-section? (tr "labels.members") + settings-section? (tr "labels.settings") + invitations-section? (tr "labels.invitations") + webhooks-section? (tr "labels.webhooks") + :else nil)]] [:nav {:class (stl/css :dashboard-header-menu)} [:ul {:class (stl/css :dashboard-header-options)} - [:li {:class (when members-section? (stl/css :active))} - [:a {:on-click on-nav-members} (tr "labels.members")]] - [:li {:class (when invitations-section? (stl/css :active))} - [:a {:on-click on-nav-invitations} (tr "labels.invitations")]] + [:li + [:a {:class (stl/css-case :dashboard-header-link true + :active members-section?) + :on-click on-nav-members} (tr "labels.members")]] + [:li + [:a {:class (stl/css-case :dashboard-header-link true + :active invitations-section?) + :on-click on-nav-invitations} (tr "labels.invitations")]] (when (contains? cfg/flags :webhooks) - [:li {:class (when webhooks-section? (stl/css :active))} - [:a {:on-click on-nav-webhooks} (tr "labels.webhooks")]]) - [:li {:class (when settings-section? (stl/css :active))} - [:a {:on-click on-nav-settings} (tr "labels.settings")]]]] + [:li + [:a {:class (stl/css-case :dashboard-header-link true + :active webhooks-section?) + :on-click on-nav-webhooks} (tr "labels.webhooks")]]) + [:li + [:a {:class (stl/css-case :dashboard-header-link true + :active settings-section?) + :on-click on-nav-settings} (tr "labels.settings")]]]] [:div {:class (stl/css :dashboard-buttons)} (when (and (or invitations-section? members-section?) (not-empty invitations)) (let [invite-button (mf/html diff --git a/frontend/src/app/main/ui/dashboard/team.scss b/frontend/src/app/main/ui/dashboard/team.scss index 2720ab6d58..4966b8ab87 100644 --- a/frontend/src/app/main/ui/dashboard/team.scss +++ b/frontend/src/app/main/ui/dashboard/team.scss @@ -5,10 +5,9 @@ // Copyright (c) KALEIDOS SUBSIDIARY SL // refactor/ stays for the component-family placeholders: %input-checkbox -// and %checkbox-icon (checkboxes, #12380) and %modal-* (modals). The shared -// dashboard header (common-dashboard) moves in its own change. +// and %checkbox-icon (checkboxes, #12380) and %modal-* (modals). @use "refactor/common-refactor.scss" as deprecated; -@use "common/refactor/common-dashboard"; +@use "dashboard/common/header"; @use "ds/_utils.scss" as *; @use "ds/typography.scss" as *; @use "ds/_sizes.scss" as *; diff --git a/frontend/src/app/main/ui/settings.cljs b/frontend/src/app/main/ui/settings.cljs index 97043fae0a..6303b7a5de 100644 --- a/frontend/src/app/main/ui/settings.cljs +++ b/frontend/src/app/main/ui/settings.cljs @@ -11,6 +11,8 @@ [app.main.refs :as refs] [app.main.router :as rt] [app.main.store :as st] + [app.main.ui.ds.foundations.typography :as t] + [app.main.ui.ds.foundations.typography.heading :refer [heading*]] [app.main.ui.hooks :as hooks] [app.main.ui.modal :refer [modal-container*]] [app.main.ui.settings.change-email] @@ -33,7 +35,11 @@ [:header {:class (stl/css :dashboard-header) :data-testid "dashboard-header"} [:div {:class (stl/css :dashboard-title)} - [:h1 {:data-testid "account-title"} (tr "dashboard.your-account-title")]]]) + [:> heading* {:level 1 + :typography t/title-large + :class (stl/css :dashboard-title-text) + :data-testid "account-title"} + (tr "dashboard.your-account-title")]]]) (mf/defc settings* [{:keys [route type error-report-id error-href]}] diff --git a/frontend/src/app/main/ui/settings.scss b/frontend/src/app/main/ui/settings.scss index 5898dbac3b..62d4e141f1 100644 --- a/frontend/src/app/main/ui/settings.scss +++ b/frontend/src/app/main/ui/settings.scss @@ -4,7 +4,7 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL -@use "common/refactor/common-dashboard"; +@use "dashboard/common/header"; @use "ds/_utils.scss" as *; @use "ds/_sizes.scss" as *; @use "ds/_borders.scss" as *;