♻️ Make the workspace logo and project name real links (#12699)

* ♻️ Give the workspace logo a real link and the DS logo asset

The Penpot logo in the workspace header was an <a> without href, so the
keyboard could not reach it and it could not open in a new tab. It now
links to the dashboard Recent page; a plain click still runs the in-app
navigation (prevent default), and Ctrl/Cmd-click opens the link in a
new tab.

The logo uses the DS raw-svg* asset instead of the legacy icon (same
drawing), with its own 32px class. The legacy svg kept its 500px height
attribute, so it overflowed the header by 222px above and below. This
was the last legacy icon in left_header.cljs.

New visual test "Shows the Penpot logo link": 0 px of difference at rest
and on hover.

AI-assisted-by: claude-opus-5-5

* ♻️ Make the workspace project name a real link

The project name in the workspace header was a div with on-click that
opened the project files in a new tab, so the keyboard could not reach
it. It is now a link to the same page with target "_blank": the browser
opens the new tab, and Tab, Enter and "Open link" work as for any link.

The left header visual test gives 0 px of difference.

AI-assisted-by: claude-opus-5-5
This commit is contained in:
Eva Marco 2026-11-05 11:32:08 +01:00 committed by GitHub
parent 1d8f2fc717
commit 9349c3b778
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 50 additions and 24 deletions

View File

@ -744,6 +744,26 @@ test.describe("Left header", () => {
threshold: 0,
});
});
test("Shows the Penpot logo link", async ({ page }) => {
const workspace = new WorkspacePage(page);
await workspace.setupEmptyFile();
await workspace.goToWorkspace();
const logo = page.locator("header a[class*='main-icon']");
await expect(logo).toBeVisible();
await page.mouse.move(640, 600);
await expect(logo).toHaveScreenshot("left-header-logo.png", {
maxDiffPixelRatio: 0,
threshold: 0,
});
await logo.hover();
await expect(logo).toHaveScreenshot("left-header-logo-hover.png", {
maxDiffPixelRatio: 0,
threshold: 0,
});
});
});
test.describe("Right header", () => {

View File

@ -16,7 +16,7 @@
[app.main.router :as rt]
[app.main.store :as st]
[app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]]
[app.main.ui.icons :as deprecated-icon]
[app.main.ui.ds.foundations.assets.raw-svg :as raw-svg :refer [raw-svg*]]
[app.main.ui.workspace.main-menu :as main-menu]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
@ -70,33 +70,40 @@
#(st/emit! (dc/stop-picker)
(modal/hide)))
router (mf/deref refs/router)
recent-href (rt/resolve-uri router :dashboard-recent {:team-id (:team-id project)})
project-href (rt/resolve-uri router :dashboard-files {:team-id (:team-id project)
:project-id project-id})
go-back
(mf/use-fn
(fn []
(close-modals)
;; FIXME: move set-mode to uri?
(st/emit! :interrupt
(dw/set-options-mode :design)
(dcm/go-to-dashboard-recent))))
nav-to-project
(mf/use-fn
(mf/deps project-id)
#(st/emit! :interrupt
(dcm/go-to-dashboard-files ::rt/new-window true :project-id project-id)))]
(fn [event]
;; Ctrl/Cmd-click opens the href in a new tab, as any link.
(when-not (kbd/mod? event)
(dom/prevent-default event)
(close-modals)
;; FIXME: move set-mode to uri?
(st/emit! :interrupt
(dw/set-options-mode :design)
(dcm/go-to-dashboard-recent)))))]
(mf/with-effect [editing?]
(when ^boolean editing?
(dom/select-text! (mf/ref-val input-ref))))
[:header {:class (dm/str class " " (stl/css :workspace-header-left))}
[:a {:on-click go-back
:class (stl/css :main-icon)} deprecated-icon/logo-icon]
[:a {:href recent-href
:on-click go-back
:class (stl/css :main-icon)}
[:> raw-svg* {:id raw-svg/penpot-logo-icon
:class (stl/css :logo)}]]
[:div {:alt (tr "workspace.sitemap")
:class (stl/css :project-tree)}
[:div
{:class (stl/css :project-name)
:on-click nav-to-project}
;; Opens the project files in a new tab, as it always did.
[:a {:class (stl/css :project-name)
:href project-href
:target "_blank"
:rel "noopener"}
(:name project)]
(if ^boolean editing?
[:input

View File

@ -24,13 +24,12 @@
block-size: $sz-32;
min-block-size: $sz-32;
margin-inline-end: var(--sp-xs);
}
// The Penpot logo is brand art from the legacy icon set and has no class.
svg {
min-block-size: $sz-32;
inline-size: $sz-32;
fill: var(--color-foreground-primary);
}
.logo {
inline-size: $sz-32;
block-size: $sz-32;
fill: var(--color-foreground-primary);
}
.project-tree {