mirror of
https://github.com/element-hq/element-call.git
synced 2026-09-28 22:08:51 +00:00
- Five of one's own at most, so the grid is whole: at the limit the add tile goes, and it comes back once one is removed. The store holds the limit too. - An added image is refused if it isn't an image, is animated, or can't be decoded; reduced to 1920px on its longest edge; and laid on an opaque ground, since a transparent pixel would be a hole in the background. - Animation is read from the file, as a still and an animated WebP share a type. Chromium has no selected track until the decoder's tracks are ready; without waiting for them an animated GIF got through. - A refusal says why, in place of the notice, and is scrolled into view. It can be dismissed, is gone once the menu closes, and the next shows even if it reads the same. What was in force stays. - End to end on Chromium and Firefox, from what the device then keeps: an animated GIF is refused with its message, a 4000x3000 image is kept at 1920x1440, and a half transparent one is kept opaque. All three fail with the preparation bypassed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
123 lines
3.3 KiB
CSS
123 lines
3.3 KiB
CSS
/*
|
|
Copyright 2026 Element Creations Ltd.
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
Please see LICENSE in the repository root for full details.
|
|
*/
|
|
|
|
.container {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
background-color: var(--cpd-color-bg-subtle-secondary);
|
|
border-radius: 32px;
|
|
transition: background-color 0.2s ease-in-out;
|
|
}
|
|
.containerOpen {
|
|
background-color: var(--cpd-color-bg-action-primary-pressed);
|
|
}
|
|
.chevronIconOpen > svg {
|
|
color: var(--cpd-color-icon-on-solid-primary);
|
|
}
|
|
.menuButton {
|
|
width: 40px;
|
|
background-color: transparent !important;
|
|
}
|
|
.itemIcon {
|
|
color: var(--cpd-color-text-secondary);
|
|
}
|
|
|
|
.rotate {
|
|
animation: spinner 1.5s linear infinite;
|
|
}
|
|
@keyframes spinner {
|
|
to {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
|
|
.menu {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
/* Only the device lists scroll, bounded by the measured call area: the menu is
|
|
portalled out of the root, so CSS can't size it against the call. */
|
|
.deviceList {
|
|
/* A list that scrolls can paint above the menu's outline, hiding it. */
|
|
margin-inline: var(--cpd-border-width-1);
|
|
inline-size: calc(
|
|
var(--device-list-inline-size) - 2 * var(--cpd-border-width-1)
|
|
);
|
|
overflow-y: auto;
|
|
min-block-size: 0;
|
|
max-block-size: var(--device-list-max-height);
|
|
/* Keeps a row reached by keyboard clear of the sticky heading and meter. */
|
|
scroll-padding-block: var(--device-list-scroll-padding-start, 0)
|
|
var(--device-list-scroll-padding-end, 0);
|
|
}
|
|
|
|
/* Compound's heading rule heads each section; the design adds space below it. */
|
|
.menu h3 {
|
|
margin-block-end: var(--cpd-space-5x);
|
|
}
|
|
|
|
/* And more above a heading that follows another section. */
|
|
.deviceList [role="group"] + [role="group"] h3 {
|
|
margin-block-start: var(--cpd-space-7x);
|
|
}
|
|
|
|
/* Sticky within its own section. Opaque and inset a border width so it doesn't
|
|
paint over the menu's outline; raised, or the positioned tiles scroll over
|
|
it. */
|
|
.sectionHeading {
|
|
position: sticky;
|
|
z-index: 1;
|
|
inset-block-start: 0;
|
|
background: var(--cpd-color-bg-canvas-default);
|
|
margin-block-start: var(--cpd-border-width-1);
|
|
}
|
|
|
|
/* Sticky at the foot of the microphone section; opaque and inset as above. */
|
|
.stickyMeter {
|
|
position: sticky;
|
|
inset-block-end: 0;
|
|
background: var(--cpd-color-bg-canvas-default);
|
|
margin-block-end: var(--cpd-border-width-1);
|
|
}
|
|
|
|
/* Radix focuses whatever the pointer is over, so the browser's ring would
|
|
follow the mouse. Replaced by one shown only for keyboard focus. */
|
|
.menu [role^="menuitem"]:focus,
|
|
.menu [role^="menuitem"]:focus-visible {
|
|
outline: none;
|
|
}
|
|
|
|
.menu[data-focus-source="keyboard"] [role^="menuitem"]:focus {
|
|
outline: var(--cpd-border-width-2) solid var(--cpd-color-border-focused);
|
|
outline-offset: calc(-1 * var(--cpd-border-width-2));
|
|
}
|
|
|
|
.notice {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: var(--cpd-space-3x);
|
|
margin: var(--cpd-space-2x)
|
|
calc(var(--cpd-space-4x) - var(--cpd-border-width-1)) 0;
|
|
padding: var(--cpd-space-3x);
|
|
border-radius: var(--cpd-space-2x);
|
|
background: var(--cpd-color-bg-subtle-secondary);
|
|
color: var(--cpd-color-text-primary);
|
|
font: var(--cpd-font-body-md-regular);
|
|
}
|
|
|
|
.notice > svg {
|
|
flex-shrink: 0;
|
|
color: var(--cpd-color-icon-secondary);
|
|
}
|
|
|
|
.refusal {
|
|
margin: var(--cpd-space-2x)
|
|
calc(var(--cpd-space-4x) - var(--cpd-border-width-1)) 0;
|
|
}
|