fix: Improve visual styling for transmitting/triggering (#2490)

This commit is contained in:
Dermot Duffy
2026-06-30 17:45:13 -07:00
committed by dermotduffy
parent 090138a01e
commit f21a46297a
5 changed files with 87 additions and 56 deletions
+2
View File
@@ -114,6 +114,8 @@ Allows overriding of any CSS value, can be used to tweak theming parameters.
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| Any CSS key. Overriding the [Advanced Camera Card](https://github.com/dermotduffy/advanced-camera-card/blob/main/src/scss/themes/base.scss) CSS variables allows changing individual theming parameters, e.g. `--advanced-camera-card-menu-override-background` | Any CSS value, e.g. `red` or `rgba(10, 11, 12, 0.64)`. | | Any CSS key. Overriding the [Advanced Camera Card](https://github.com/dermotduffy/advanced-camera-card/blob/main/src/scss/themes/base.scss) CSS variables allows changing individual theming parameters, e.g. `--advanced-camera-card-menu-override-background` | Any CSS value, e.g. `red` or `rgba(10, 11, 12, 0.64)`. |
> [!WARNING] Changes to CSS keys are not considered breaking changes and may not have an associated [major version change](../developing.md?id=release-philosophy).
## `triggers` ## `triggers`
The `triggers` block controls how the card reacts when a camera is triggered The `triggers` block controls how the card reacts when a camera is triggered
+2
View File
@@ -445,6 +445,8 @@ export class AdvancedCameraCardLiveCarousel extends LitElement {
<!-- --> <!-- -->
${this._renderNextPrevious('right', neighbors)} ${this._renderNextPrevious('right', neighbors)}
</advanced-camera-card-carousel> </advanced-camera-card-carousel>
<div class="status-glow status-glow--trigger"></div>
<div class="status-glow status-glow--transmitting"></div>
<advanced-camera-card-ptz <advanced-camera-card-ptz
.hass=${this.hass} .hass=${this.hass}
.config=${this.liveConfig.controls.ptz} .config=${this.liveConfig.controls.ptz}
+79
View File
@@ -35,3 +35,82 @@
flex: 0 0 100%; flex: 0 0 100%;
} }
// Triggered and transmitting cameras are highlighted with a soft inset glow,
// drawn by an overlay stacked above the camera feed. The overlays come before
// the carousel controls in source order, so they stack above the feed but
// below the controls; `pointer-events: none` keeps them inert. The glow is an
// interior effect, so it coexists with the grid selection border (see
// media-grid.scss) without competing for the `border` property.
//
// The two states differ by colour and rhythm: triggered is an amber glow that
// pulses fully out and back (a "something happened" beacon), transmitting is a
// red glow that breathes gently without ever fully fading ("you are live").
// Transmitting wins: while it is active the trigger overlay is faded out.
// Triggered: pulse from nothing to a full amber glow and back.
@keyframes trigger-vignette-pulse {
0%,
100% {
box-shadow: inset 0 0 var(--advanced-camera-card-trigger-glow-size) transparent;
}
50% {
box-shadow: inset 0 0 var(--advanced-camera-card-trigger-glow-size)
var(--advanced-camera-card-trigger-glow-color);
}
}
// Transmitting: breathe between a softer and a fuller glow — never fully fades,
// so the camera always reads as live.
@keyframes transmitting-vignette-pulse {
0%,
100% {
box-shadow: inset 0 0 calc(var(--advanced-camera-card-transmitting-glow-size) * 0.55)
var(--advanced-camera-card-transmitting-glow-color);
}
50% {
box-shadow: inset 0 0 var(--advanced-camera-card-transmitting-glow-size)
var(--advanced-camera-card-transmitting-glow-color);
}
}
.status-glow {
position: absolute;
inset: 0;
pointer-events: none;
// Hidden by default; activation/deactivation is a plain opacity fade. Each
// overlay carries its own static box-shadow (not scoped to the
// `[triggered]`/`[transmitting]` selectors) so the glow stays painted in its
// own colour throughout the fade-out, after its animation has been removed.
opacity: 0;
transition: opacity 0.5s ease-in-out;
}
.status-glow--trigger {
box-shadow: inset 0 0 var(--advanced-camera-card-trigger-glow-size)
var(--advanced-camera-card-trigger-glow-color);
}
.status-glow--transmitting {
box-shadow: inset 0 0 var(--advanced-camera-card-transmitting-glow-size)
var(--advanced-camera-card-transmitting-glow-color);
}
:host([triggered]) .status-glow--trigger {
opacity: 1;
animation: trigger-vignette-pulse 5s infinite;
}
// Transmitting takes precedence: while it is active the trigger overlay is
// faded out. This rule follows the `[triggered]` rule with equal specificity,
// so it wins the cascade.
:host([transmitting]) .status-glow--trigger {
opacity: 0;
animation: none;
}
:host([transmitting]) .status-glow--transmitting {
opacity: 1;
animation: transmitting-vignette-pulse 4s ease-in-out infinite;
}
-53
View File
@@ -1,61 +1,8 @@
@use 'basic-block.scss'; @use 'basic-block.scss';
@use 'locked.scss'; @use 'locked.scss';
@keyframes warning-pulse {
0% {
border-color: var(--trigger-border-color-base);
}
50% {
border-color: var(--trigger-border-color);
}
100% {
border-color: var(--trigger-border-color-base);
}
}
advanced-camera-card-live-carousel {
--trigger-border-color: var(--advanced-camera-card-trigger-border-color);
--trigger-border-color-base: var(
--advanced-camera-card-trigger-border-color-base,
black
);
transition: border-color 0.3s ease-out;
}
// `:host >` matches the carousel only in single display mode (in grid mode it
// is nested inside `media-grid`). The single-mode carousel has no border of its
// own, so reserve a transparent one so border changes (e.g. trigger) only
// recolour. In grid mode the border is supplied (already width-reserved) by
// `media-grid`.
:host > advanced-camera-card-live-carousel {
box-sizing: border-box;
border: solid 2px transparent;
}
advanced-camera-card-live-carousel[triggered] {
animation: warning-pulse 5s infinite;
}
// The `:host` prefix raises specificity to (0,2,1), above `media-grid`'s
// `::slotted([selected])` border (0,1,1). In grid mode that rule lives in an
// inner shadow tree and would otherwise win the cross-tree cascade tie,
// leaving the transmitting border the wrong colour. `[triggered]` needs no
// such treatment — animated values always beat the regular cascade.
:host advanced-camera-card-live-carousel[transmitting] {
// `animation: none` cancels any concurrent trigger pulse to ensure
// transmitting takes precedence.
animation: none;
border-color: var(--advanced-camera-card-transmitting-border-color);
}
// While locked, dim and disable interaction on the non-selected cameras in the // While locked, dim and disable interaction on the non-selected cameras in the
// grid so input stays on the selected camera (e.g. during a call). // grid so input stays on the selected camera (e.g. during a call).
:host([locked]) advanced-camera-card-live-carousel[unselected] { :host([locked]) advanced-camera-card-live-carousel[unselected] {
@include locked.style; @include locked.style;
} }
advanced-camera-card-live-carousel[selected] {
--trigger-border-color-base: var(
--advanced-camera-card-trigger-border-color-base,
var(--advanced-camera-card-foreground-primary)
);
}
+4 -3
View File
@@ -245,15 +245,16 @@
/********* /*********
* Trigger * Trigger
*********/ *********/
--advanced-camera-card-trigger-border-color: var(--advanced-camera-card-warning-color); --advanced-camera-card-trigger-glow-color: var(--advanced-camera-card-active-color);
--advanced-camera-card-trigger-border-color-base: unset; --advanced-camera-card-trigger-glow-size: 16px;
/************** /**************
* Transmitting * Transmitting
**************/ **************/
--advanced-camera-card-transmitting-border-color: var( --advanced-camera-card-transmitting-glow-color: var(
--advanced-camera-card-warning-color --advanced-camera-card-warning-color
); );
--advanced-camera-card-transmitting-glow-size: 28px;
/***** /*****
* Grid * Grid