vojo/src/app/pages/HorseshoeContainer.css.ts

98 lines
3.1 KiB
TypeScript

import { style } from '@vanilla-extract/css';
import { toRem } from 'folds';
import {
VOJO_HORSESHOE_VOID_COLOR,
VOJO_HORSESHOE_GAP_PX,
VOJO_HORSESHOE_RADIUS_PX,
} from '../styles/horseshoe';
// Color of the «void» between the app shell and the bottom call rail —
// shared across every horseshoe surface, see `styles/horseshoe.ts`.
// Painted only when the call rail is mounted, so the rest of the app
// keeps its normal page background.
const SURFACE_GAP_COLOR = VOJO_HORSESHOE_VOID_COLOR;
const HORSESHOE_RADIUS = toRem(VOJO_HORSESHOE_RADIUS_PX);
const HORSESHOE_GAP = toRem(VOJO_HORSESHOE_GAP_PX);
// Outer flex column hosting app shell + bottom call rail.
// `min-height: 0` is required for nested scroll containers inside
// ClientLayout to shrink correctly.
export const surface = style({
display: 'flex',
flexDirection: 'column',
flex: 1,
minWidth: 0,
minHeight: 0,
});
export const surfaceActive = style({
backgroundColor: SURFACE_GAP_COLOR,
});
// === App shell ===
//
// Wraps the whole client UI. Gets rounded BOTTOM corners + 8px
// margin pushing the rest of the app up while a call surface is
// active.
export const appShell = style({
display: 'flex',
flex: 1,
minWidth: 0,
minHeight: 0,
});
export const appShellBottomRound = style({
borderBottomLeftRadius: HORSESHOE_RADIUS,
borderBottomRightRadius: HORSESHOE_RADIUS,
overflow: 'hidden',
marginBottom: HORSESHOE_GAP,
});
// === Bottom horseshoe (call rail) ===
//
// Rounded *top* corners only because it sits flush against the
// safe-area inset; the bottom is the screen edge. `position: relative`
// carries the absolute-positioned orbit border.
export const bottomRail = style({
display: 'flex',
flexDirection: 'column',
flexShrink: 0,
});
export const bottomRailActive = style({
position: 'relative',
borderTopLeftRadius: HORSESHOE_RADIUS,
borderTopRightRadius: HORSESHOE_RADIUS,
overflow: 'hidden',
});
// Orbit border on the bottom rail — small green segment of a
// conic-gradient that runs around the rail's perimeter when a ring is
// incoming. The mask trick (content-box layer XOR full-box layer)
// cuts the inner area, so only the 2px rim shows the gradient. The
// angle is driven by `--vojo-orbit-angle`, registered via `@property`
// in `src/index.css`.
export const ringOrbit = style({
position: 'absolute',
inset: 0,
borderRadius: 'inherit',
padding: toRem(2),
pointerEvents: 'none',
background:
'conic-gradient(from var(--vojo-orbit-angle, 0deg), transparent 0deg 280deg, rgba(91, 227, 197, 0.15) 300deg, #5BE3C5 335deg, rgba(91, 227, 197, 0.15) 350deg, transparent 360deg)',
filter: 'drop-shadow(0 0 6px rgba(91, 227, 197, 0.55))',
WebkitMask:
'linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0)',
WebkitMaskComposite: 'xor',
mask: 'linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0)',
maskComposite: 'exclude',
animationName: 'vojo-orbit-sweep',
animationDuration: '1.8s',
animationTimingFunction: 'linear',
animationIterationCount: 'infinite',
'@media': {
'(prefers-reduced-motion: reduce)': {
animation: 'none',
},
},
});