feat(camera): el boton de camara pide el click con borde de hormigas en marcha

Cuatro tiras de gradiente repetido, una por borde, deslizando un periodo de
guion por ciclo — fondo y no border porque un borde CSS no puede animar su
dash-offset. currentColor hereda el color de estado (gris en reposo, rojo en
error). Se apaga en streaming/solicitando, y con prefers-reduced-motion.

Ojo del gato encerrado: el estilo inline usaba el shorthand background, que
pisa el background-image de la clase — pasa a backgroundColor.
This commit is contained in:
David Montero Crespo 2026-07-27 18:27:30 +02:00
parent 5177b82791
commit 126ff4fafc
2 changed files with 36 additions and 2 deletions

View File

@ -448,3 +448,26 @@ body {
0%, 100% { opacity: 0.4; transform: scale(0.85); }
50% { opacity: 1.0; transform: scale(1.0); }
}
/* Marching-ants attention border (CameraToggle while it wants a click):
four thin repeating-gradient strips, one per edge, sliding one dash
period per cycle. Drawn as backgrounds rather than a border because a
CSS border cannot animate its dash offset. Uses currentColor so the
ants inherit the button's state colour (grey idle, red on error). */
@keyframes velxio-marching-ants {
to { background-position: 12px 0, -12px 100%, 0 -12px, 100% 12px; }
}
.velxio-ants {
background-image:
repeating-linear-gradient(90deg, currentColor 0 6px, transparent 6px 12px),
repeating-linear-gradient(90deg, currentColor 0 6px, transparent 6px 12px),
repeating-linear-gradient(0deg, currentColor 0 6px, transparent 6px 12px),
repeating-linear-gradient(0deg, currentColor 0 6px, transparent 6px 12px);
background-repeat: repeat-x, repeat-x, repeat-y, repeat-y;
background-size: 12px 1px, 12px 1px, 1px 12px, 1px 12px;
background-position: 0 0, 0 100%, 0 0, 100% 0;
animation: velxio-marching-ants 0.7s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
.velxio-ants { animation: none; }
}

View File

@ -103,9 +103,20 @@ export const CameraToggle: React.FC<CameraToggleProps> = ({ boardId }) => {
onClick={handleClick}
disabled={!boardId || status === 'requesting'}
title={tooltip}
// Ants while the button wants a click (idle after a failed/stopped
// auto-start, denied, error). Solid border once streaming or asking.
className={
boardId && status !== 'streaming' && status !== 'requesting'
? 'velxio-ants'
: undefined
}
style={{
background: isOn ? 'rgba(63,185,80,0.15)' : 'transparent',
border: `1px solid ${isOn ? '#3fb950' : '#555'}`,
// backgroundColor, NOT the background shorthand: inline shorthand
// would wipe the class's background-image and kill the ants.
backgroundColor: isOn ? 'rgba(63,185,80,0.15)' : 'transparent',
border: `1px solid ${
isOn ? '#3fb950' : status === 'requesting' ? '#555' : 'transparent'
}`,
borderRadius: 4,
padding: '4px 10px',
display: 'flex',