208 lines
5.3 KiB
TypeScript
208 lines
5.3 KiB
TypeScript
import { style } from '@vanilla-extract/css';
|
|
import { recipe, RecipeVariants } from '@vanilla-extract/recipes';
|
|
import { DefaultReset, color, config, toRem } from 'folds';
|
|
import { VOJO_HORSESHOE_RADIUS_PX } from '../../styles/horseshoe';
|
|
|
|
export const PageNavResizable = style({
|
|
position: 'relative',
|
|
flexShrink: 0,
|
|
flexGrow: 0,
|
|
});
|
|
|
|
export const PageNavResizeHandle = style({
|
|
position: 'absolute',
|
|
top: 0,
|
|
bottom: 0,
|
|
// Native default — sits across the (original) `<Line>` separator
|
|
// between page-nav and content. Web shifts the handle into the
|
|
// horseshoe void via an inline style override in `ResizablePageNav`.
|
|
right: -3,
|
|
width: 7,
|
|
cursor: 'col-resize',
|
|
zIndex: 1,
|
|
background: 'transparent',
|
|
touchAction: 'none',
|
|
outline: 'none',
|
|
selectors: {
|
|
'&::before': {
|
|
content: '""',
|
|
position: 'absolute',
|
|
top: '50%',
|
|
left: '50%',
|
|
width: 2,
|
|
height: toRem(36),
|
|
transform: 'translate(-50%, -50%)',
|
|
borderRadius: 1,
|
|
backgroundColor: color.Surface.OnContainer,
|
|
opacity: 0,
|
|
transition:
|
|
'opacity 140ms ease, height 140ms ease, width 140ms ease, background-color 140ms ease',
|
|
},
|
|
'&:hover::before, &:focus-visible::before': {
|
|
opacity: 0.25,
|
|
},
|
|
'&:focus-visible::before': {
|
|
backgroundColor: color.Primary.Main,
|
|
opacity: 0.45,
|
|
},
|
|
'&[data-dragging="true"]::before': {
|
|
opacity: 0.55,
|
|
height: toRem(48),
|
|
backgroundColor: color.Primary.Main,
|
|
},
|
|
// Limit feedback: when the user drags past a clamp the width stops
|
|
// moving and the indicator deforms to signal it. Min = spring crushed
|
|
// against a wall (slight squish, thicker). Max = rubber band stretched
|
|
// (taller, full opacity). Activates only during drag so the resting
|
|
// state stays calm.
|
|
'&[data-dragging="true"][data-at-min="true"]::before': {
|
|
height: toRem(28),
|
|
width: 3,
|
|
opacity: 0.85,
|
|
},
|
|
'&[data-dragging="true"][data-at-max="true"]::before': {
|
|
height: toRem(76),
|
|
width: 2,
|
|
opacity: 0.9,
|
|
},
|
|
},
|
|
});
|
|
|
|
export const PageNav = recipe({
|
|
variants: {
|
|
size: {
|
|
'500': {
|
|
width: toRem(320),
|
|
},
|
|
'400': {
|
|
width: toRem(256),
|
|
},
|
|
'300': {
|
|
width: toRem(222),
|
|
},
|
|
},
|
|
},
|
|
defaultVariants: {
|
|
size: '400',
|
|
},
|
|
});
|
|
export type PageNavVariants = RecipeVariants<typeof PageNav>;
|
|
|
|
// Web-only horseshoe shell wrapping every page-nav's inner column.
|
|
// `overflow:hidden + border-radius` clips the nav's content into a
|
|
// shape with rounded TR and BR corners; the wrapper PageRoot puts
|
|
// behind the nav paints `#090909` at those same corners via background-
|
|
// image, so the carved area reads as the horseshoe void. An explicit
|
|
// `Background.Container` bg is required: folds `<Header>` is fully
|
|
// transparent (it only sets color/border), and some routes (Bots,
|
|
// Channels) don't paint anything at the bottom — without this bg the
|
|
// PageRoot void would bleed through the entire header / footer instead
|
|
// of just the carved corner. Applied conditionally in `PageNav` /
|
|
// `ResizablePageNav` below; native skips it entirely.
|
|
export const PageNavInnerWebHorseshoe = style({
|
|
overflow: 'hidden',
|
|
borderTopRightRadius: toRem(VOJO_HORSESHOE_RADIUS_PX),
|
|
borderBottomRightRadius: toRem(VOJO_HORSESHOE_RADIUS_PX),
|
|
backgroundColor: color.Background.Container,
|
|
});
|
|
|
|
export const PageNavHeader = recipe({
|
|
base: {
|
|
padding: `0 ${config.space.S200} 0 ${config.space.S300}`,
|
|
flexShrink: 0,
|
|
selectors: {
|
|
'button&': {
|
|
cursor: 'pointer',
|
|
},
|
|
'button&[aria-pressed=true]': {
|
|
backgroundColor: color.Background.ContainerActive,
|
|
},
|
|
'button&:hover, button&:focus-visible': {
|
|
backgroundColor: color.Background.ContainerHover,
|
|
},
|
|
'button&:active': {
|
|
backgroundColor: color.Background.ContainerActive,
|
|
},
|
|
},
|
|
},
|
|
|
|
variants: {
|
|
outlined: {
|
|
true: {
|
|
borderBottomWidth: 1,
|
|
},
|
|
},
|
|
},
|
|
defaultVariants: {
|
|
outlined: true,
|
|
},
|
|
});
|
|
export type PageNavHeaderVariants = RecipeVariants<typeof PageNavHeader>;
|
|
|
|
export const PageNavContent = style({
|
|
minHeight: '100%',
|
|
padding: config.space.S200,
|
|
paddingRight: 0,
|
|
paddingBottom: config.space.S700,
|
|
});
|
|
|
|
export const PageHeader = recipe({
|
|
base: {
|
|
paddingLeft: config.space.S400,
|
|
paddingRight: config.space.S200,
|
|
},
|
|
variants: {
|
|
balance: {
|
|
true: {
|
|
paddingLeft: config.space.S200,
|
|
},
|
|
},
|
|
outlined: {
|
|
true: {
|
|
borderBottomWidth: config.borderWidth.B300,
|
|
},
|
|
},
|
|
},
|
|
defaultVariants: {
|
|
outlined: true,
|
|
},
|
|
});
|
|
export type PageHeaderVariants = RecipeVariants<typeof PageHeader>;
|
|
|
|
export const PageContent = style([
|
|
DefaultReset,
|
|
{
|
|
paddingTop: config.space.S400,
|
|
paddingLeft: config.space.S400,
|
|
paddingRight: 0,
|
|
paddingBottom: toRem(100),
|
|
},
|
|
]);
|
|
|
|
export const PageHeroEmpty = style([
|
|
DefaultReset,
|
|
{
|
|
padding: config.space.S400,
|
|
borderRadius: config.radii.R400,
|
|
minHeight: toRem(450),
|
|
},
|
|
]);
|
|
|
|
export const PageHeroSection = style([
|
|
DefaultReset,
|
|
{
|
|
padding: '40px 0',
|
|
maxWidth: toRem(466),
|
|
width: '100%',
|
|
margin: 'auto',
|
|
},
|
|
]);
|
|
|
|
export const PageContentCenter = style([
|
|
DefaultReset,
|
|
{
|
|
maxWidth: toRem(964),
|
|
width: '100%',
|
|
margin: 'auto',
|
|
},
|
|
]);
|