All components overlayfill
Loading Overlay
Busy states that fill a container, cover it, or take over the screen while content loads.
Loading projects…
1234567891011121314import { LoadingOverlay } from "#components/ui/base-ui/loading-overlay.tsx";import { Spinner } from "#components/ui/base-ui/spinner.tsx";
export function Example() { return ( // variant="overlay" covers the nearest positioned ancestor; // "fill" fills a flex parent; "fullscreen" fixes over the viewport. // Parts compose via render. <LoadingOverlay.Root variant="overlay" role="status"> <Spinner size="lg" /> <LoadingOverlay.Text>Loading projects…</LoadingOverlay.Text> </LoadingOverlay.Root> );}Variants
Loading projects…
Loading projects…
fullscreen
Install
1npx stylexblocks add loading-overlaySource
components/ui/base-ui/loading-overlay.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081import * as stylex from "@stylexjs/stylex";import { createSlotRecipeContext } from "#lib/base-ui/create-slot-recipe-context.tsx";import { defineSlotRecipe } from "#lib/recipe-props.ts";import { fontSizes } from "@stylexblocks/ui/tokens/font-sizes.stylex.ts";import { colors } from '@stylexblocks/ui/tokens/colors.stylex.ts'import { durations } from '@stylexblocks/ui/tokens/durations.stylex.ts'import { semanticColors } from '@stylexblocks/ui/semantic-tokens/colors.stylex.ts'import { spacing } from '@stylexblocks/ui/tokens/spacing.stylex.ts'import { zIndices } from '@stylexblocks/ui/tokens/z-indices.stylex.ts'
const loadingOverlayRoot = stylex.create({ base: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: spacing._4, transitionProperty: 'opacity', transitionDuration: durations.slower, },})
const loadingOverlayRootVariants = stylex.create({ fill: { flex: 1, height: '100%', // Chakra's virtual `currentBg` token has no StyleX equivalent; the page // background is the closest semantic token. backgroundColor: semanticColors.bg, }, fullscreen: { position: 'fixed', inset: 0, zIndex: zIndices.modal, backgroundColor: semanticColors.bg, }, overlay: { position: 'absolute', inset: 0, // `currentBg/50` — a half-transparent wash of the page background backgroundColor: `color-mix(in oklch, ${semanticColors.bg} 50%, ${colors.transparent})`, },})
export type LoadingOverlayVariant = keyof typeof loadingOverlayRootVariants
// defineSlotRecipe validates the shape at compile time: default values must// exist in the axis maps, and slot keys must be `styles` or a declared axisconst loadingOverlayRecipe = defineSlotRecipe({ slots: { root: { styles: loadingOverlayRoot, variants: loadingOverlayRootVariants }, }, defaultVariants: { variant: "fill" },});
// The recipe has a single `root` slot; the label part is local chrome so// busy text sits consistently under the spinner.const chrome = stylex.create({ text: { marginTop: spacing._2_5, fontSize: fontSizes.sm, color: semanticColors.fgMuted, },});
/** * Loading Overlay — a presentational busy state composed from the * loading-overlay slot recipe: `variant="fill"` fills its flex parent, * `variant="overlay"` covers the nearest positioned ancestor, and * `variant="fullscreen"` fixes itself over the viewport (above sticky page * chrome via the modal z-index). Pair it with a spinner and a short label; * every part composes via `render` through Base UI's `useRender` primitives. * Add `role="status"` so the busy state is announced. */const { withProvider, withContext } = createSlotRecipeContext(loadingOverlayRecipe);
export const LoadingOverlay = { Root: withProvider("div", "root", "LoadingOverlayRoot"), Text: withContext("span", () => [chrome.text], "LoadingOverlayText"),};