* ♻️ Explain why layout_container.scss keeps the deprecated import
The TODO at the top said to remove the refactor/ import once the buttons
were replaced, and sat above the DS imports instead of that one. The
buttons are now DS buttons (#12428, #12431), but the import is still
needed by the numeric inputs (%input-element, %disabled-input) and by the
two raw track header buttons, which keep the secondary and tertiary
placeholders. Replace the TODO with a comment on the import that says so.
AI-assisted-by: claude-opus-5-5
* 📎 Format the workspace visual spec with Prettier
Three expectations added in #12423, #12428 and #12431 broke a call chain
in a way Prettier rewrites. check-fmt:js did not catch it: its
playwright/**/*.js glob is expanded by the shell without globstar, so it
does not reach visual-specs/.
AI-assisted-by: claude-opus-5-5
* ♻️ Use a DS icon button for the component pill menu
Replace the raw three dots button of the component pill with the DS
secondary icon button. It had no label, so it reuses the "Options" one,
shown in the DS tooltip above and to the left (it sits at the right edge
of the sidebar), and aria-pressed while the menu is open instead of the
selected class.
Its class drops the button placeholders and only keeps the pill segment
shape: full height and square corners on the left. The tooltip wrapper
fills the segment too.
Add a visual test that selects a component copy so the pill shows.
AI-assisted-by: claude-opus-5-5
* ♻️ Use DS icon buttons for the component annotation actions
The save/create, discard, edit and delete actions of a component
annotation were divs with a click handler: no keyboard focus and no button
role. Replace them with DS ghost icon buttons. Their title moves to the
label, shown in the DS tooltip above and to the left, and save is disabled
while the text is blank, which it already ignored.
The class that hides them until the title is hovered, or while editing or
creating, now goes on the tooltip wrapper. The green and red hover colors
and the fixed size go away with the DS look. While editing or creating,
the actions get a small padding so their hover background does not touch
the annotation border; the title row grows from 32 to 36px there.
Add a visual test that creates an annotation on a main component and
shows both sets of actions.
AI-assisted-by: claude-opus-5-5
* 💄 Open component options tooltips above and to the left
The variants help, create variant, add property and remove property icon
buttons sit at the right edge of the sidebar: open their tooltips above
and to the left, like the other actions of the component options.
AI-assisted-by: claude-opus-5-5
* ♻️ Move component options styles off the deprecated styles
Inline the dropdown placeholders of the component pill menu with DS
tokens and a local variable for the item colors that change on hover, and
give the item label its own class instead of styling the span. Resolve
the annotation title border alias to its DS token and delete the unused
.component-title-actions rule.
The item label uses the DS body-small typography; its line-height is 1.3
instead of 1.4, with no visible change since the item centers it.
component.scss no longer imports refactor/.
AI-assisted-by: claude-opus-5-5
* ♻️ Use a DS icon button for the guide defaults menu
Replace the raw three dots button that opens the use default / set as
default menu of a guide with the DS ghost icon button. It had no label, so
it gets a new "Guide defaults" one, shown in the DS tooltip, and
aria-pressed while the menu is open instead of the selected class.
The add, show/hide and remove guide buttons, and the new one, sit at the
right edge of the sidebar: open their tooltips above and to the left.
The advanced options toggle stays a raw button: it is one segment of the
guide type control and an icon button would break that shape.
Add a visual test that adds a guide to a board and opens its advanced
options.
AI-assisted-by: claude-opus-5-5
* ♻️ Remove guide defaults button styles
The three dots guide defaults button is a DS icon button now: delete the
.show-more-options rule, its state variables and its svg rule. The rest of
frame_grid.scss already uses DS tokens.
AI-assisted-by: claude-opus-5-5
* ♻️ Use a DS button for edit grid in the grid cell options
Replace the raw "Edit grid" button of the grid cell options with the DS
secondary button. Drop its alt attribute, which is not valid on a button;
the label already names it.
The column and row icons label the numeric inputs, so they move with the
numeric inputs migration.
Add a visual test that selects a cell in grid edit mode so the options
show.
AI-assisted-by: claude-opus-5-5
* ♻️ Move grid cell options styles to DS tokens
The edit grid button is a DS button now, so its button placeholder and
typography mixin go away; the class only stretches it and centers its
label. Merge the two .grid-cell-menu-container rules, inline the flex
mixins, and move spacing, borders and color aliases to DS tokens and
logical properties.
Use the DS body-small typography in the inputs; its line-height is 1.3
instead of 1.4.
The refactor/ import stays only for %input-element and %button-icon, used
by the inputs and their legacy icons, which move with the numeric inputs
migration.
AI-assisted-by: claude-opus-5-5
* ♻️ Use the DS icon in the workspace left header
The shared library badge of the left header used a deprecated icon; use
the DS library icon instead.
Drop the deprecated icons inside the save status dot. They rendered as
500px SVGs with no stroke, so they never showed: the dot is only its
background color, and its title keeps the status text. The Penpot logo is
brand art and stays.
Add a visual test that opens a shared file and shows the header with the
badge, then the saved status after drawing a shape.
AI-assisted-by: claude-opus-5-5
* ♻️ Move workspace left header styles off the deprecated styles
Inline the flex and ellipsis mixins, move spacing, sizes and borders to
DS tokens and logical properties, and resolve the color aliases to their
DS tokens. The status dot takes its color from a local variable. The
badge icon gets its color through a class instead of an svg selector.
The project name uses the DS headline-small typography: 12px instead of
11px. The file name matches title-small exactly.
Left with a TODO: the rename input font size and z-index have no DS
match, and the pending status orange (#f5a91b) has no DS color.
left_header.scss no longer imports refactor/.
AI-assisted-by: claude-opus-5-5
* ♻️ Resolve the TODOs left in the workspace left header styles
The status dot used the legacy warning orange #f5a91b, which drifted from
the DS one when both replaced #fe4811 a few days apart in early 2025. Use
the DS --color-accent-warning (#fe9c07), the orange of every other DS
warning, so the dot looks a little more orange while saving.
The rename input had z-index 20, which did nothing: the input is neither
positioned nor a flex or grid item. Remove it.
The rename input only set its font size and got the rest from the global
reset; it now uses the DS title-small typography, like the file name. Its
line-height goes from normal to 1.2, so it grows from 16 to 16.8px.
AI-assisted-by: claude-opus-5-5
* ♻️ Use DS buttons and icons in the grid layout options
The "Edit grid" button of a grid layout and the "Exit" button of the grid
editor were raw buttons with the deprecated secondary style. Replace them
with DS secondary buttons; their text and actions stay the same. Drop the
"alt" attribute of "Edit grid": it is not valid on a button.
The expand and add buttons of the column and row headers are 52px parts of
a custom header, not DS buttons, so they stay raw; only their deprecated
icons move to the DS icon, as does the direction icon of each track.
Add a visual test that shows the grid layout options and the grid editor
with the column tracks expanded.
AI-assisted-by: claude-opus-5-5
* ♻️ Remove deprecated button styles from the grid layout options
"Edit grid" and "Exit" are now DS buttons, so their secondary placeholder
and typography go away; the classes only place them in the grid and center
their label. The track header and direction icons drop the deprecated icon
placeholder and get their colors through classes. The header buttons keep
their secondary and tertiary placeholders, since they stay raw.
AI-assisted-by: claude-opus-5-5
* ♻️ Use DS icon buttons for the flex wrap and padding mode
The wrap button of a flex layout and the button that shows the four
padding sides (flex and grid layouts) were raw buttons with deprecated
icons. Replace them with DS ghost icon buttons that show their state
through aria-pressed, with the tooltip above and to the left since they
sit at the right edge of the sidebar.
The wrap label is now always "Wrap": its title switched to "No wrap"
while pressed, which conflicts with the pressed state.
Add a visual test that selects a flex board and shows both buttons at
rest and pressed.
AI-assisted-by: claude-opus-5-5
* ♻️ Remove deprecated wrap and padding toggle styles
The wrap and padding mode buttons are now DS icon buttons, so their
tertiary button, icon and selected placeholders go away. The tooltip
wrappers take the same grid cells the buttons had.
AI-assisted-by: claude-opus-5-5
* ♻️ Use a DS icon button for the layout element margin mode
The button that switches a flex or grid element between simple and
multiple margins was a raw button with a deprecated icon. Replace it with
a DS ghost icon button that shows the multiple state through aria-pressed.
Its title moves to the label, shown in the DS tooltip above and to the
left since the button sits at the right edge of the sidebar. The grid
column class goes on the tooltip wrapper.
The legacy margin icons label the numeric inputs and stay for the numeric
inputs group; the radio buttons are out of scope.
Add a visual test that selects a flex element and shows both margin modes.
AI-assisted-by: claude-opus-5-5
* ♻️ Remove deprecated button styles from layout element options
The margin mode button is now a DS icon button, so its tertiary button,
icon and selected placeholders and the deprecated 32px height go away.
The class only places the tooltip wrapper in the third grid column.
The refactor/ import stays for %input-element, still used by the legacy
numeric inputs.
AI-assisted-by: claude-opus-5-5
* ♻️ Use a DS icon button to remove imported SVG attributes
Replace the raw remove button of each imported SVG attribute with the DS
ghost icon button and the DS remove icon. The button had no title, so it
gets a new "Remove attribute" label, which the DS tooltip shows. Drop the
wrapper div that only held the button.
Add a visual test that selects a group imported from SVG so the section
shows.
AI-assisted-by: claude-opus-5-5
* ♻️ Move imported SVG attributes styles to DS tokens
The remove button is a DS icon button now, so .attr-actions and
.attr-action-btn with their button placeholders go away. Inline the flex
and ellipsis mixins, move spacing to DS tokens and logical properties,
sizes to px2rem and the title color alias to its DS token.
Use the DS body-small typography; its line-height is 1.3 instead of 1.4.
Drop the line clamp rules of the attribute name: its later display:
inline-block already overrode the -webkit-box they need.
The refactor/ import stays only for %input-element on the value input,
which moves with the inputs migration.
AI-assisted-by: claude-opus-5-5
* ♻️ Use a DS icon button to close the nudge amount modal
The close button of the nudge amount modal (main menu, Preferences) was a
raw button with a deprecated icon and no accessible name. Replace it with
a DS ghost icon button labeled "Close", shown in its DS tooltip. The class
that places it in the corner goes on the tooltip wrapper, so the tooltip
anchors to the button. The numeric inputs stay for the inputs group.
Add a visual test that opens the modal from the main menu.
AI-assisted-by: claude-opus-5-5
* ♻️ Move nudge modal styles off the deprecated styles
The close button is a DS icon button, so its placeholder goes away; it is
now 32px wide instead of 28px, and sits 4px from the edge instead of 6px
so the icon stays in the same place. Spacing, sizes and the color aliases
move to DS tokens and logical properties, the flex mixin is inlined, and
the label rule targets its class instead of the label element.
The title matches the DS headline-medium typography exactly. The content
and labels use body-large and the wrapper body-small, whose line-heights
differ by 0.1 from the legacy ones; the labels keep their 1.5 override,
so nothing visible changes.
refactor/ stays only for the shared modal and labeled input placeholders.
AI-assisted-by: claude-opus-5-5
The libraries modal now opens on the "This file" tab, so the visual test
timed out looking for the shared library. Open the "Libraries" tab first,
as the functional test does, mock the file with the library linked, and
check the "Added" label instead of the old "File library" text.
AI-assisted-by: claude-opus-5-5
* ♻️ Use a DS icon button for flatten in the boolean options
Replace the raw flatten button and its deprecated icon with icon-button*
and the DS boolean-flatten icon. The title text moves to aria-label, so
the DS tooltip shows it. The grid column goes on the tooltip wrapper
through tooltip-class.
Add a visual test that selects two shapes so the boolean options show.
AI-assisted-by: claude-opus-5-5
* ♻️ Remove deprecated button styles from the boolean options
The flatten button is a DS icon button now, so its placeholders, sizes
and icon colors go away; only the grid column stays, on the tooltip
wrapper. Use block-size and drop the refactor/ import.
AI-assisted-by: claude-opus-5-5
* ♻️ Use DS buttons for more colors in the selected colors
Replace the three raw "more colors" buttons (library colors, colors and
color tokens) with the DS secondary button.
Add a visual test that selects a board with more than three colors so
the button shows.
AI-assisted-by: claude-opus-5-5
* ♻️ Move selected colors styles to DS tokens
The "more colors" buttons are DS buttons now, so their placeholder and
typography mixin go away; the class only centers the label, since the
column stretches the button. Delete the unused .add-fill rule, move
spacing to DS tokens and logical properties, and drop the refactor/
import.
AI-assisted-by: claude-opus-5-5
* ♻️ Use DS icon buttons in the align options
Replace the eight raw align and distribute buttons with icon-button*
and DS icons. The title text moves to aria-label, so the DS tooltip
shows the same text and shortcut. Disabled distribute buttons now use
the DS disabled color.
Add a visual test that selects two shapes so the align options show.
AI-assisted-by: claude-opus-5-5
* ♻️ Remove deprecated button styles from the align options
The align buttons are DS icon buttons now, so the .align-button rules
and their placeholders go away. Move the row height to a DS size token
and drop the refactor/ import.
AI-assisted-by: claude-opus-5-5
Move SPA routing out of the URL fragment into the normal query
string. The screen travels in a reserved `screen` key holding
the route name (`?screen=workspace&team-id=…`); every other
param keeps its name. `rt/nav` and `rt/resolve` keep their
signatures.
This deletes the fragment-mirroring URL surgery, simplifies
link-preview (the server sees everything) and nginx (single
path, no SPA fallback rules needed), and migrates OIDC
redirects, email links, e2e helpers and plugin test utils to
the new format.
Legacy `#/…` URLs translate client-side for one Penpot version
(`legacy-routes`, marked TODO(next-version)); non-SPA paths
are untouched.
AI-assisted-by: muse-spark-1.3-contributor