vojo/src/app/pages/client/SyncIndicator.css.ts

70 lines
2.4 KiB
TypeScript

import { keyframes, style } from '@vanilla-extract/css';
import { config } from 'folds';
// One sweep, left → right. The bar is full-viewport-wide; the radial gradient
// fades to transparent at both edges, so the keyframe loop from
// `translateX(100%)` back to `translateX(-100%)` happens entirely off-screen
// and the snap is invisible.
const slide = keyframes({
'0%': { transform: 'translateX(-100%)' },
'100%': { transform: 'translateX(100%)' },
});
// Container is taller than the visible bar so the drop-shadow halo isn't
// clipped by `overflow: hidden`. The bar itself sits flush to the bottom
// edge of the safe-area inset (the 26px above is a transparent
// pointer-events: none zone where only the glow renders).
export const root = style({
position: 'fixed',
left: 'env(safe-area-inset-left, 0px)',
right: 'env(safe-area-inset-right, 0px)',
bottom: 'env(safe-area-inset-bottom, 0px)',
height: '28px',
pointerEvents: 'none',
// Z400 sits above app chrome and below folds modals/popouts (which use
// Max=9999). Connection state should be visible on the page, not over
// dialogs.
zIndex: config.zIndex.Z400,
overflow: 'hidden',
});
const barBase = style({
position: 'absolute',
bottom: 0,
left: 0,
width: '100%',
height: '2px',
// 250ms opacity transition carries progress↔hidden and progress↔error
// crossfades smoothly. Both layers are always mounted; the React side
// toggles opacity.
transition: 'opacity 250ms ease',
});
export const barProgress = style([
barBase,
{
background:
'radial-gradient(ellipse 50% 100% at center, #5BE3C5 0%, rgba(91, 227, 197, 0.45) 35%, rgba(91, 227, 197, 0) 70%)',
filter: 'drop-shadow(0 0 6px rgba(91, 227, 197, 0.8))',
animation: `${slide} 1.8s linear infinite`,
willChange: 'transform',
'@media': {
'(prefers-reduced-motion: reduce)': {
animation: 'none',
},
},
},
]);
export const barError = style([
barBase,
{
// Slightly brighter, denser-fade red — the gradient extends closer to
// the viewport edges than the green sweep so the static error visual
// reads as a stronger statement than the moving progress one. Same
// 2px height as the progress bar.
background:
'radial-gradient(ellipse 50% 100% at center, #FF3030 0%, rgba(255, 48, 48, 0.7) 50%, rgba(255, 48, 48, 0) 90%)',
filter: 'drop-shadow(0 0 12px rgba(255, 48, 48, 0.95))',
},
]);