70 lines
2.4 KiB
TypeScript
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))',
|
|
},
|
|
]);
|