All components subtleoutlinexssmmdlgxl
Progress
Determinate progress bars with outline and subtle tracks in five sizes.
1234567891011121314151617import { Progress } from "#components/ui/base-ui/progress.tsx";import { indigoPalette } from "@stylexblocks/ui/themes/palettes.ts";
export function Example() { return ( // the real Base UI Progress machine wearing the recipe styles — the // machine carries value state and aria; variant/size/shape resolve once // on Root and Track/Range (Base UI's Indicator) inherit them via context <Progress.Root value={64} variant="subtle" size="md" shape="full" palette={indigoPalette}> <Progress.Label>Upload</Progress.Label> <Progress.Track> <Progress.Range striped /> </Progress.Track> <Progress.ValueText /> </Progress.Root> );}Variants
Sizes
Install
1npx stylexblocks add progressSource
components/ui/base-ui/progress.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218import { Progress as BaseProgress } from "@base-ui-components/react/progress";import { createSlotRecipeContext } from "#lib/base-ui/create-slot-recipe-context.tsx";import { defineSlotRecipe, resolveSlotStyles, type SlotRecipeVariants, type WithStyleOverrides,} from "#lib/recipe-props.ts";import * as stylex from "@stylexjs/stylex";import { colorPalette } from '@stylexblocks/ui/color-palette.stylex.ts'import { durations } from '@stylexblocks/ui/tokens/durations.stylex.ts'import { fontSizes } from '@stylexblocks/ui/tokens/font-sizes.stylex.ts'import { fontWeights } from '@stylexblocks/ui/tokens/font-weights.stylex.ts'import { lineHeights } from '@stylexblocks/ui/tokens/line-heights.stylex.ts'import { radii } from '@stylexblocks/ui/tokens/radii.stylex.ts'import { semanticColors } from '@stylexblocks/ui/semantic-tokens/colors.stylex.ts'import { semanticRadii } from '@stylexblocks/ui/semantic-tokens/radii.stylex.ts'import { semanticShadows } from '@stylexblocks/ui/semantic-tokens/shadows.stylex.ts'import { sizes } from '@stylexblocks/ui/tokens/sizes.stylex.ts'
const progressRoot = stylex.create({ base: { fontSize: fontSizes.sm, lineHeight: lineHeights.sm, position: 'relative', },})
const progressTrack = stylex.create({ base: { overflow: 'hidden', position: 'relative', },})
const progressTrackSizes = stylex.create({ xs: { height: sizes._1_5, }, sm: { height: sizes._2, }, md: { height: sizes._2_5, }, lg: { height: sizes._3, }, xl: { height: sizes._4, },})
const progressTrackVariants = stylex.create({ outline: { boxShadow: semanticShadows.inset, backgroundColor: semanticColors.bgSubtle, }, subtle: { backgroundColor: colorPalette.muted, },})
const progressTrackShapes = stylex.create({ rounded: { borderRadius: semanticRadii.control, }, full: { borderRadius: radii.full, },})
const progressRange = stylex.create({ base: { display: 'flex', alignItems: 'center', justifyContent: 'center', transitionProperty: 'width, height', transitionDuration: durations.slow, height: '100%', backgroundColor: 'var(--track-color)', },})
const progressRangeVariants = stylex.create({ outline: { backgroundColor: colorPalette.solid, }, subtle: { backgroundColor: `color-mix(in oklch, ${colorPalette.solid} 72%, transparent)`, },})
const progressRangeStripeds = stylex.create({ true: { backgroundImage: 'linear-gradient(45deg, var(--stripe-color) 25%, transparent 25%, transparent 50%, var(--stripe-color) 50%, var(--stripe-color) 75%, transparent 75%, transparent)', backgroundSize: 'var(--stripe-size) var(--stripe-size)', '--stripe-size': '1rem', '--stripe-color': 'rgba(255, 255, 255, 0.3)', },})
const progressRangeAnimateds = stylex.create({ true: { '--animate-from': 'var(--stripe-size)', animationName: 'bg-position', animationDuration: '1s', animationTimingFunction: 'linear', animationIterationCount: 'infinite', },})
const progressLabel = stylex.create({ base: { display: 'inline-flex', fontWeight: fontWeights.medium, },})
const progressValueText = stylex.create({ base: { fontSize: fontSizes.xs, lineHeight: 1, fontWeight: fontWeights.medium, },})
export type ProgressVariant = keyof typeof progressTrackVariants
export type ProgressShape = keyof typeof progressTrackShapes
export type ProgressStriped = keyof typeof progressRangeStripeds
export type ProgressAnimated = keyof typeof progressRangeAnimateds
export type ProgressSize = keyof typeof progressTrackSizes
const progressSlotRecipe = defineSlotRecipe({ slots: { root: { styles: progressRoot }, track: { styles: progressTrack, sizes: progressTrackSizes, variants: progressTrackVariants, shapes: progressTrackShapes, }, range: { styles: progressRange, variants: progressRangeVariants, }, label: { styles: progressLabel }, valueText: { styles: progressValueText }, }, defaultVariants: { variant: "outline", size: "md", shape: "rounded" },});
const { withProvider, withContext } = createSlotRecipeContext(progressSlotRecipe);
type ProgressVariants = SlotRecipeVariants<typeof progressSlotRecipe>;
/** * Progress — the real Base UI Progress machine wearing the recipe styles. * `<Progress.Root variant size shape>` resolves the recipe once and shares * it with Track/Range through context; the machine carries value state and * aria semantics (`value`, `min`, `max`). The Range (Base UI's Indicator * part) takes boolean `striped`/`animated` props layering the recipe's * stripe maps, and every part still composes via `render`. */export type ProgressRootProps = WithStyleOverrides< React.ComponentProps<typeof BaseProgress.Root>> & Partial<ProgressVariants>;
const ProgressRoot = withProvider(BaseProgress.Root, "root", "ProgressRoot");
function Root(props: ProgressRootProps) { return <ProgressRoot {...(props as React.ComponentProps<typeof ProgressRoot>)} />;}Root.displayName = "ProgressRoot";
export type ProgressRangeProps = WithStyleOverrides< React.ComponentProps<typeof BaseProgress.Indicator>> & { /** Diagonal stripe pattern from the recipe. */ striped?: boolean; /** Animates the stripes (pair with `striped`). */ animated?: boolean;};
const StyledRange = withContext( BaseProgress.Indicator, (v) => resolveSlotStyles(progressSlotRecipe.slots.range, v), "ProgressRange",);
function Range({ striped, animated, style, ...rest }: ProgressRangeProps) { return ( <StyledRange {...rest} style={[ striped && progressRangeStripeds.true, animated && progressRangeAnimateds.true, style, ]} /> );}Range.displayName = "ProgressRange";
export const Progress = { Root, Label: withContext(BaseProgress.Label, "label", "ProgressLabel"), Track: withContext(BaseProgress.Track, "track", "ProgressTrack"), Range, ValueText: withContext(BaseProgress.Value, "valueText", "ProgressValueText"),};