You got me there: I don’t know!
![]()
@Masterpiga here is my take, as a CSS tweak on the 30 Sept build. The goal was not a new look but the panel in the theme’s own terms, so that it reads like the module it sits in and retints with any theme. Screenshot below, snippet at the end.
What it does:
-
Theme tokens only, no literal colors. Rows rest on the collapsible section color, text is the theme’s text color. Nothing in the panel is white-on-alpha any more.
-
Row states are steps from the rest color toward the text color, not theme tokens for selected/hover. I tried the tokens first and it collapsed on the default theme: on darktable-elegant-grey the collapsible and selected colors are five grey steps apart and its hover token lands exactly on selected, so a selected row was invisible. Relative steps hold on every theme.
-
Hover is fill only, one step on whatever is beneath, so it is a smaller step than selection on a rest row and still shows on a selected one. Text and icons never change on hover. I tried the button-style inversion (light fill, dark text) and it looked good in a still but the app-painted icons repaint a frame late, so the row flickers. @mino I think this also answers your hover/selected point without unifying the two.
-
Tone instead of frames, no gradients. The editor cards keep their box but paint no border; the header gradients are flat fills.
-
One 2px radius on rows, cards and chips, squared where a header meets its rail or card so the rail does not notch. The rail elbow keeps a curve, at 6px.
-
A shape being drawn is a finished row with a dashed left edge and its name in italic, the way the metadata module marks a value not yet applied. No four-sided frame, sliders at full strength.
Four things I could only do from the outside, which would make the CSS interface cleaner if changed in code:
-
The pending row’s header line never gets
.mask-element-header, so a theme has to reach it with> box:first-childto shade it like other headers. -
The lead icon is painted at 85% of its style color (
_drag_handle_draw), so it always sits a step dimmer than the name; the tweak hands it a lighter color to cancel that. A plain 100% would let the theme decide. -
The
* { color }rule in darktable.css hits the opacity badge’s label directly, so a theme cannot color the badge and let the label inherit; it has to name.mask-inline-opacity-value label. -
The rail elbow radius is clamped between the rail width and the tick length, about 3 to 11px, which is fine, but worth a comment next to the CSS token.
CSS Snippet
/* flexi mask panel in the current theme's own terms:
theme tokens only, row states as steps from the rest color toward the
text color, tone instead of frames, no gradients, one 2px radius */
@define-color mask_rest @collapsible_bg_color;
@define-color mask_implied mix(@collapsible_bg_color, @fg_color, 0.06);
@define-color mask_hover mix(@collapsible_bg_color, @fg_color, 0.13);
@define-color mask_hover_top @mask_hover;
@define-color mask_selected mix(@collapsible_bg_color, @fg_color, 0.28);
@define-color mask_selected_top @mask_selected;
@define-color mask_selected_hover mix(@mask_selected, @fg_color, 0.13);
@define-color mask_card alpha(@collapsible_bg_color, 0.5);
@define-color mask_list_bg shade(@plugin_bg_color, 0.9);
@define-color mask_text_rest @fg_color;
@define-color mask_text_implied @fg_color;
@define-color mask_text_selected shade(@fg_color, 1.12);
@define-color mask_text_hover @fg_color;
@define-color mask_text_value alpha(@fg_color, 0.7);
@define-color mask_text_value_hover @fg_color;
/* colors hard-coded in darktable.css, rescoped under #blending-box (the
panel's container, embedded or docked) so they win on specificity */
#blending-box .mask-list-row-selected
{
background-image: linear-gradient(@mask_selected, @mask_selected);
}
#blending-box .mask-group-block.mask-list-row-selected,
#blending-box .mask-group-block.mask-list-row-implied,
#blending-box #mask-shape-row.mask-list-row-selected
{
background-image: none;
}
#blending-box .mask-panel-row.mask-list-row-hover,
#blending-box .mask-panel-row.mask-list-row-drop,
#blending-box .mask-group-block.mask-list-row-drop
{
background-color: @mask_hover;
}
/* hovered headers paint their gradient alone, as darktable.css has it */
#blending-box #mask-group-header-row.mask-list-row-hover,
#blending-box #mask-shape-row.mask-list-row-hover
{
background-color: transparent;
}
/* the same wash over a selected header, so hovering the selection still
shows, one step above it instead of dropping to the rest hover */
#blending-box .mask-group-block.mask-list-row-selected > box > * > #mask-group-header-row.mask-list-row-hover,
#blending-box .mask-panel-row.mask-group-header.mask-list-row-selected.mask-list-row-hover,
#blending-box #mask-shape-row.mask-list-row-selected.mask-list-row-hover > * > .mask-element-header
{
background-image: linear-gradient(@mask_selected_hover, @mask_selected_hover);
}
/* solo edit is a hover that stays, as the theme has it */
#blending-box .mask-panel-row.mask-list-row-solo,
#blending-box #mask-group-header-row.mask-list-row-solo
{
background-color: @mask_hover;
}
/* drag and drop insertion lines */
#blending-box #mask-shape-row.mask-list-row-drop-above,
#blending-box .mask-cluster.mask-list-row-drop-above
{
box-shadow: inset 0 2px @fg_color;
}
#blending-box #mask-shape-row.mask-list-row-drop-below,
#blending-box .mask-cluster.mask-list-row-drop-below
{
box-shadow: inset 0 -2px @fg_color;
}
#blending-box .mask-panel-row.mask-list-row-drop-above,
#blending-box .mask-group-block.mask-list-row-drop-above
{
border-top-color: @fg_color;
}
#blending-box .mask-panel-row.mask-list-row-drop-below,
#blending-box .mask-group-block.mask-list-row-drop-below
{
border-bottom-color: @fg_color;
}
#blending-box .mask-op-combo
{
border-color: alpha(@fg_color, 0.3);
}
/* soloed badge and inverted drag handle */
#blending-box .mask-list-handle-inverted,
#blending-box .mask-power-solo
{
background-color: @fg_color;
color: @plugin_bg_color;
}
#blending-box .mask-noop-warn
{
color: @disabled_fg_color;
}
#blending-box .mask-inline-opacity-value:hover
{
background-color: alpha(@button_hover_bg, 0.5);
}
/* the lead icon is painted at 85% of its style color (_drag_handle_draw), so
handed the name's color it lands a step dimmer. A lighter color that comes
out at the text's tone after the 85%, at rest and on selected rows. Not
on an inverted handle, whose glyph is the panel background on a light
plate (the rule above), or these would outrank that at equal weight */
#blending-box .mask-list-handle:not(.mask-list-handle-inverted)
{
color: shade(@fg_color, 1.18);
}
#blending-box .mask-group-block.mask-list-row-selected > box > * > .mask-group-header .mask-list-handle:not(.mask-list-handle-inverted),
#blending-box .mask-panel-row.mask-group-header.mask-list-row-selected .mask-list-handle:not(.mask-list-handle-inverted),
#blending-box .mask-list-row-selected > * > .mask-element-header .mask-list-handle:not(.mask-list-handle-inverted)
{
color: shade(@mask_text_selected, 1.12);
}
/* shape: one radius for everything, 2px. The list, its rows, the cards and
the chips all take it; the rail elbow is set on its own below */
#blending-box #masks-list-box,
#blending-box .mask-panel-row,
#blending-box .mask-element-header,
#blending-box .mask-list-row-selected,
#blending-box .mask-op-combo,
#blending-box .mask-list-handle,
#blending-box .mask-list-handle-inverted,
#blending-box .mask-power-solo,
#blending-box .mask-inline-opacity-value
{
border-radius: 2px;
}
/* the theme squares an open group header where its rail hangs, and an open
element header along its card; the radius rule above outranks those, so
they are said again here or the corner notches the rail */
#blending-box .mask-panel-row.mask-group-header.mask-group-open
{
border-bottom-left-radius: 0;
}
#blending-box .mask-row-open > * > .mask-element-header
{
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
/* the rail elbow is the one curve kept: a connector, not a card corner. The
code clamps it between the rail width (3px) and the tick length (about
11px); the theme's own is 8px, this sits between that and square */
#blending-box .mask-group-elements
{
border-top-left-radius: 6px;
}
#blending-box .mask-param-row-editor,
#blending-box .mask-props-row-editor
{
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
/* the rail runs past this card's bottom-left corner, so only the right one */
#blending-box .mask-group-opacity-editor
{
border-radius: 0 0 2px 0;
}
/* borders: darktable separates by surface tone, not frames. The editor cards
keep their 1px box so nothing shifts, but the frame is never painted, in any
row state. The hover rule below needs the extra id to outrank the theme's
#mask-shape-row.mask-list-row-hover > * > .mask-*-row-editor. The theme
clips the card ground to the padding box so the frame stays translucent;
with no frame the ground has to run out to the border box, or the card is
1px narrower than its header on each side */
#blending-box .mask-param-row-editor,
#blending-box .mask-props-row-editor,
#blending-box .mask-group-opacity-editor
{
background-clip: border-box;
}
#blending-box .mask-param-row-editor,
#blending-box .mask-props-row-editor,
#blending-box .mask-group-opacity-editor,
#blending-box #mask-shape-row.mask-list-row-hover > * > .mask-param-row-editor,
#blending-box #mask-shape-row.mask-list-row-hover > * > .mask-props-row-editor
{
border-color: transparent;
}
/* a shape being drawn: a dashed edge on the left says "in progress", and
the name in italic says "not applied yet" the way the metadata module's
edited fields do. The rest reads like a finished row, sliders live. The
theme frames it on all four sides; the header line never gets
.mask-element-header (only committed rows do, see _make_shape_row), so
the first box, which is that line, takes the rest shade here */
#blending-box #mask-shape-row.mask-row-pending
{
border-width: 0 0 0 2px;
border-style: dashed;
border-color: alpha(@fg_color, 0.5);
}
#blending-box #mask-shape-row.mask-row-pending .mask-row-name
{
font-style: italic;
}
#blending-box #mask-shape-row.mask-row-pending > box:first-child
{
background-color: @mask_rest;
background-origin: border-box;
border: 2px solid transparent;
border-left-width: 0;
}
Thanks for your contribution @anry.
This is basically what I started from
I really do not find it contrasty enough, though. It is quite important to be able to see clearly the selected, transitively selected and hovered elements in the panel, because they have an effect on the usage, it’s not just cosmetic. Transitive selection is also important, because when you deselect an element its parent is selected.
For me, all the headers in your screenshot look almost the same. This is exactly why I had darkened the background and increased the step size, it was the only way that I found to make stuff stand out.
Well, the elegant grey theme is not contrasty in general. That’s its feature, which I am not a fan of, honestly.
What I am proposing may be not the final version, further adjustments more than welcome. However, it is crucial to keep default mask panel coherent with elegant theme, which is default right now in darktable.
First time trying the new masking module. I would say I quite like the new possibilities it adds. There was a case just recently where I couldn’t achieve a particular mask with the previous implementation as easily (had to duplicate modules and keep them mutually exclusive)
But I can’t seem to find a way to reuse the mask from another module? I’m a heavy user of this feature because it allows me to “share” masks between modules. Is there a way to do this now?
For the new look and usability, I would say that it’s a bad pattern to select something you didn’t click on - I’m talking about the deselect functionality when you click on the mask window, which makes it fall back to selecting its parent. I understand that this is essentially a deselect, but it also selects something else, and it’s hard to see that your selection was changed. I would say removing this interaction and just doing nothing on subsequent window clicks would be a more robust design.
For the design, I quite like @anry 's CSS. I think it’s not even that important to show the parent windows through recolouring their headers, because this interaction is also displayed via the nesting “tree” to the left, and it’s also reinforced by padding. It’s already quite clear which window is parent to the current selection and where in the tree we’re currently at. And I find the overall contrast quite nice too, but this is just my opinion.
I’ll try playing around with this fork more to provide some feedback but in my general workflow I seldom use masks in general, and when I do, it’s usually either a single gradient or a couple of brushes with heavy feathering. So I’m not sure how much feedback I would be able to gather
Thanks, I didn’t notice it at first, because I thought it was a mask shape (as it is in the row with shapes). I think maybe it could use a different placement or some other way to disambiguate it from the other icons in this row?
Also I noticed a bug with the opacity slider: pressing anywhere on the slider doesn’t move the slider head to that position.
And another bug: dragging the slider head to the left (outside of the modules panel) keeps it contained at 0, as is expected, but then dragging in the opposite direction (while the cursor is outside of the panel) also moves the slider head, which it shouldn’t.
I can’t seem to reproduce it at any time, I’ll try to figure out what causes it. But while this bug is happening, other sliders in any other modules work just fine.

