Keep the menu's frame visible beside a scrolling list in Safari

- Once the device list had enough in it to scroll, Safari painted it above
  the outline the menu is framed with, and the frame vanished beside every
  device. Not new: the microphone menu with many devices lost 384px of it.
  The camera menu only met it because the self-preview shortens its list.
- The list now holds itself a border width clear of the frame, so nothing
  inside it has to: the headings and the meter lose their own clearance.
- Frame intact in both engines in every case measured. Belongs to the
  device menu, so it can be cherry-picked on its own.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
fkwp
2026-09-23 10:30:40 +02:00
co-authored by Claude Opus 5.5
parent 8d95c76b8b
commit efe6274636
@@ -46,8 +46,19 @@ Please see LICENSE in the repository root for full details.
component: the menu is portalled outside the root, so neither a container component: the menu is portalled outside the root, so neither a container
query nor a viewport unit describes the space it is allowed to fill. */ query nor a viewport unit describes the space it is allowed to fill. */
.deviceList { .deviceList {
/* The camera menu's width, set by the component; auto everywhere else. */ /* Held one border width clear of the frame on each side. The menu draws its
inline-size: var(--device-list-inline-size, auto); frame as an outline just inside its own edge, and once this list has
enough in it to scroll, Safari paints it — rows and all — above that
outline, so the frame vanished beside every device; measured on the
microphone menu with many devices, 384 pixels of it. Everything the list
holds is inside this, so nothing in it needs to keep clear on its own. */
margin-inline: var(--cpd-border-width-1);
/* The camera menu's width, set by the component, less that clearance so the
menu still comes to it. Without the variable the calc is invalid and the
width falls back to auto, which is what every other menu wants. */
inline-size: calc(
var(--device-list-inline-size) - 2 * var(--cpd-border-width-1)
);
overflow-y: auto; overflow-y: auto;
min-block-size: 0; min-block-size: 0;
max-block-size: var(--device-list-max-height); max-block-size: var(--device-list-max-height);
@@ -96,7 +107,8 @@ Please see LICENSE in the repository root for full details.
position: sticky; position: sticky;
inset-block-start: 0; inset-block-start: 0;
background: var(--cpd-color-bg-canvas-default); background: var(--cpd-color-bg-canvas-default);
margin-inline: var(--cpd-border-width-1); /* Clear of the frame's top edge; the list it sits in already keeps it clear
of the sides. */
margin-block-start: var(--cpd-border-width-1); margin-block-start: var(--cpd-border-width-1);
} }
@@ -110,12 +122,10 @@ Please see LICENSE in the repository root for full details.
.stickyMeter { .stickyMeter {
position: sticky; position: sticky;
inset-block-end: 0; inset-block-end: 0;
/* Opaque, so the list does not show through it as it scrolls past. The menu /* Opaque, so the list does not show through it as it scrolls past, and held
draws its frame as an outline inset by one border width, and the device clear of the frame's bottom edge. The list it sits in already keeps it
rows are transparent at rest, so this is the only thing that can cover it: clear of the sides. */
hold it clear on the sides and the bottom. */
background: var(--cpd-color-bg-canvas-default); background: var(--cpd-color-bg-canvas-default);
margin-inline: var(--cpd-border-width-1);
margin-block-end: var(--cpd-border-width-1); margin-block-end: var(--cpd-border-width-1);
} }
@@ -174,7 +184,11 @@ Please see LICENSE in the repository root for full details.
control starts under its own rule: the heading's spacing supplies most of control starts under its own rule: the heading's spacing supplies most of
that, and this adds the same step a device row holds above its control. that, and this adds the same step a device row holds above its control.
Below, the menu's own padding is already the inset, so nothing is set. */ Below, the menu's own padding is already the inset, so nothing is set. */
padding: var(--cpd-space-2x) var(--cpd-space-4x) 0; /* Sixteen from the menu's edge, as the notice below it is and as design
draws both: the list this sits in is already held a border width in from
the frame, so that much less here. */
padding: var(--cpd-space-2x)
calc(var(--cpd-space-4x) - var(--cpd-border-width-1)) 0;
} }
/* /*