From 1d54d170cfb822e076c4b9d96c5271ca26f65abe Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Wed, 21 Oct 2026 14:11:15 +0200 Subject: [PATCH] :recycle: Move layers panel styles off the deprecated button styles (#12478) The layers panel already uses DS icons and buttons; its two raw buttons (the layer filter pill and the focus mode title) stay raw, since they are custom widgets. Their styles still used the deprecated %button-tag placeholder and color aliases. - Inline %button-tag in the filter pill: border, cursor, centering, and its focus and pressed states through local variables. Its hover rule set the icons to the color they already had, so it goes away. - Resolve the icon, title, pill and menu color aliases to DS tokens. The filter menu item texts are the same color in every state, so only the background changes, through a local variable. The refactor import stays for the shared %menu-dropdown placeholder. Add a visual test of the focus mode title, a filter pill and the filter menu; the panel looks the same as before, pixel for pixel. AI-assisted-by: claude-opus-5-5 --- .../ui/visual-specs/workspace.spec.js | 45 ++++++++ .../app/main/ui/workspace/sidebar/layers.scss | 101 ++++++++---------- 2 files changed, 91 insertions(+), 55 deletions(-) diff --git a/frontend/playwright/ui/visual-specs/workspace.spec.js b/frontend/playwright/ui/visual-specs/workspace.spec.js index 2d0752bbad..f324163b4a 100644 --- a/frontend/playwright/ui/visual-specs/workspace.spec.js +++ b/frontend/playwright/ui/visual-specs/workspace.spec.js @@ -418,6 +418,51 @@ test.describe("Debug shape panel", () => { }); }); +test.describe("Layers panel", () => { + test("Shows the focus mode title and the layer filters", async ({ page }) => { + const workspace = new WorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockRPC(/get\-file\?/, "workspace/get-file-9066.json"); + await workspace.goToWorkspace({ + fileId: "e179d9df-de35-80bf-8005-283bbd5516b0", + pageId: "e179d9df-de35-80bf-8005-283bbd5516b1", + }); + + await workspace.clickLeafLayer("Board"); + await page.keyboard.press("f"); + await expect(workspace.sidebar.getByText("Focus mode")).toBeVisible(); + await page.mouse.move(640, 600); + await expect(workspace.sidebar).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await page.keyboard.press("f"); + await expect(workspace.sidebar.getByText("Focus mode")).toBeHidden(); + await workspace.sidebar.getByRole("button", { name: "Search" }).click(); + await workspace.sidebar.getByRole("button", { name: "Filter" }).click(); + await workspace.sidebar.getByText("Boards", { exact: true }).click(); + await page.mouse.move(640, 600); + // The "Boards" filter pill. The clip leaves out the pages divider and + // the search bar, whose transitions may still be running. + const filtersClip = { x: 0, y: 340, width: 318, height: 380 }; + await expect(page).toHaveScreenshot({ + clip: filtersClip, + maxDiffPixelRatio: 0, + threshold: 0, + }); + + await workspace.sidebar.getByRole("button", { name: "Filter" }).click(); + await page.mouse.move(640, 600); + // The filter menu open, with its option ticked. + await expect(page).toHaveScreenshot({ + clip: filtersClip, + maxDiffPixelRatio: 0, + threshold: 0, + }); + }); +}); + test.describe("Guides", () => { test("Shows the advanced options of a square guide", async ({ page }) => { // A taller window keeps the whole Design tab visible: a scrolled diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.scss b/frontend/src/app/main/ui/workspace/sidebar/layers.scss index 4ff85ab137..e702e9c9fa 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.scss @@ -4,6 +4,7 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// Only for the shared %menu-dropdown placeholder of the filters menu. @use "refactor/common-refactor.scss" as deprecated; @use "ds/borders.scss" as *; @use "ds/mixins.scss" as *; @@ -63,7 +64,7 @@ justify-content: center; block-size: var(--sp-xxxl); padding-inline-start: var(--sp-s); - color: var(--icon-foreground); + color: var(--color-foreground-secondary); } .focus-name { @@ -71,7 +72,7 @@ @include text-ellipsis; padding-inline-end: var(--sp-xs); - color: var(--title-foreground-color); + color: var(--color-foreground-secondary); } .focus-mode-tag-wrapper { @@ -91,18 +92,36 @@ } .layer-filter { - @extend %button-tag; + --layer-filter-background-color: var(--color-background-tertiary); + --layer-filter-border: none; + display: flex; + justify-content: center; + align-items: center; gap: px2rem(6); block-size: var(--sp-xxl); margin: var(--sp-xxs) 0; + border: var(--layer-filter-border); border-radius: $br-6; - background-color: var(--pill-background-color); + background-color: var(--layer-filter-background-color); + cursor: pointer; + + &:focus-visible { + --layer-filter-background-color: var(--color-background-secondary); + --layer-filter-border: #{$b-1} solid var(--color-accent-primary); + + outline: none; + } + + &:active { + --layer-filter-background-color: transparent; + --layer-filter-border: none; + } } .layer-filter-icon, .layer-filter-close { - color: var(--pill-foreground-color); + color: var(--color-foreground-primary); } .layer-filter-name { @@ -111,7 +130,7 @@ display: flex; align-items: center; justify-content: center; - color: var(--pill-foreground-color); + color: var(--color-foreground-primary); } .layers { @@ -213,6 +232,8 @@ inline-size: $sz-192; .filter-menu-item { + --filter-menu-item-background-color: transparent; + @include t.use-typography("body-small"); display: flex; @@ -221,60 +242,30 @@ inline-size: 100%; padding: px2rem(6); border-radius: $br-8; - - .filter-menu-item-name-wrapper { - display: flex; - align-items: center; - gap: var(--sp-s); - - .filter-menu-item-icon { - color: var(--menu-foreground-color); - } - - .filter-menu-item-name { - padding-block-start: var(--sp-xxs); - color: var(--menu-foreground-color); - } - } - - .filter-menu-item-tick { - color: var(--menu-foreground-color); - } + background-color: var(--filter-menu-item-background-color); &.selected { - background-color: var(--menu-background-color-selected); - - .filter-menu-item-name-wrapper { - .filter-menu-item-icon { - color: var(--menu-foreground-color); - } - - .filter-menu-item-name { - color: var(--menu-foreground-color); - } - } - - .filter-menu-item-tick { - color: var(--menu-foreground-color); - } + --filter-menu-item-background-color: var(--color-background-tertiary); } &:hover { - background-color: var(--menu-background-color-hover); - - .filter-menu-item-name-wrapper { - .filter-menu-item-icon { - color: var(--menu-foreground-color-hover); - } - - .filter-menu-item-name { - color: var(--menu-foreground-color-hover); - } - } - - .filter-menu-item-tick { - color: var(--menu-foreground-color-hover); - } + --filter-menu-item-background-color: var(--color-background-quaternary); } } + + .filter-menu-item-name-wrapper { + display: flex; + align-items: center; + gap: var(--sp-s); + } + + .filter-menu-item-icon, + .filter-menu-item-name, + .filter-menu-item-tick { + color: var(--color-foreground-primary); + } + + .filter-menu-item-name { + padding-block-start: var(--sp-xxs); + } }