diff --git a/frontend/src/App.css b/frontend/src/App.css index 47df9d9c..c5791c9b 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -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; } +} diff --git a/frontend/src/components/simulator/CameraToggle.tsx b/frontend/src/components/simulator/CameraToggle.tsx index fe2c95f6..d6e8ef93 100644 --- a/frontend/src/components/simulator/CameraToggle.tsx +++ b/frontend/src/components/simulator/CameraToggle.tsx @@ -103,9 +103,20 @@ export const CameraToggle: React.FC = ({ 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',