import { ProgressCircle } from '@coinbase/cds-web/visualizations/ProgressCircle'
- framer-motion: ^10.18.0
Default
<HStack gap={2} flexWrap="wrap"> <ProgressCircle progress={0} size={100} /> <ProgressCircle progress={0.5} size={100} /> <ProgressCircle progress={1} size={100} /> </HStack>
Thin
<HStack gap={2} flexWrap="wrap"> <ProgressCircle progress={0} weight="thin" size={100} /> <ProgressCircle progress={0.5} weight="thin" size={100} /> <ProgressCircle progress={1} weight="thin" size={100} /> </HStack>
Semiheavy
<HStack gap={2} flexWrap="wrap"> <ProgressCircle progress={0} weight="semiheavy" size={100} /> <ProgressCircle progress={0.5} weight="semiheavy" size={100} /> <ProgressCircle progress={1} weight="semiheavy" size={100} /> </HStack>
Heavy
<HStack gap={2} flexWrap="wrap"> <ProgressCircle progress={0} weight="heavy" size={100} /> <ProgressCircle progress={0.5} weight="heavy" size={100} /> <ProgressCircle progress={1} weight="heavy" size={100} /> </HStack>
No Text
<HStack gap={2} flexWrap="wrap"> <ProgressCircle progress={0} hideContent size={25} /> <ProgressCircle progress={0.5} hideContent size={25} /> <ProgressCircle progress={1} hideContent size={25} /> </HStack>
Disabled
<HStack gap={2} flexWrap="wrap"> <ProgressCircle progress={0} disabled size={100} /> <ProgressCircle progress={0.5} disabled size={100} /> <ProgressCircle progress={1} disabled size={100} /> </HStack>
Colors
<HStack gap={2} flexWrap="wrap"> <ProgressCircle progress={0.5} color="bgPositive" size={100} /> <ProgressCircle progress={0.5} color="bgNegative" size={100} /> <ProgressCircle progress={0.5} color="bgPrimary" size={100} /> <ProgressCircle progress={0.5} color="fg" size={100} /> </HStack>
Fill Parent
The progress circle can be dynamically sized to fit its parent. If you drag the browser window smaller or larger then the ProgressCircle will resize accordingly.
<HStack gap={2} flexWrap="wrap"> <div style={{ height: '15vw', width: '15vw', minWidth: '60px', minHeight: '60px' }}> <ProgressCircle progress={1} /> </div> <div style={{ height: '10vw', width: '10vw', minWidth: '60px', minHeight: '60px' }}> <ProgressCircle progress={1} /> </div> <div style={{ height: '5vw', width: '5vw', minWidth: '60px', minHeight: '60px' }}> <ProgressCircle progress={1} /> </div> </HStack>
Content Node Customization
You can override the default content node to display a custom node. Note that the content node is clipped to the circle.
With Asset
You can provide an image, such as an asset, as the content node.
<VStack gap={2}> <HStack gap={2} flexWrap="wrap"> <ProgressCircle progress={1} size={56} styles={{ progress: { stroke: assets.eth.color, }, }} contentNode={ <Box height="100%" padding={0.25} width="100%"> <RemoteImage alt={assets.eth.name} shape="circle" source={assets.eth.imageUrl} style={{ width: '100%', height: '100%' }} /> </Box> } weight="thin" /> <ProgressCircle progress={0.75} size={56} styles={{ progress: { stroke: assets.ltc.color, }, }} contentNode={ <Box height="100%" padding={0.25} width="100%"> <RemoteImage alt={assets.ltc.name} shape="circle" source={assets.ltc.imageUrl} style={{ width: '100%', height: '100%' }} /> </Box> } weight="thin" /> <ProgressCircle progress={0.5} size={56} styles={{ progress: { stroke: assets.dai.color, }, }} contentNode={ <Box height="100%" padding={0.25} width="100%"> <RemoteImage shape="circle" source={assets.dai.imageUrl} style={{ width: '100%', height: '100%' }} /> </Box> } weight="thin" /> <ProgressCircle progress={0.25} size={56} styles={{ progress: { stroke: assets.sushi.color, }, }} contentNode={ <Box height="100%" padding={0.25} width="100%"> <RemoteImage alt={assets.sushi.name} shape="circle" source={assets.sushi.imageUrl} style={{ width: '100%', height: '100%' }} /> </Box> } weight="thin" /> <ProgressCircle progress={0} size={56} styles={{ progress: { stroke: assets.xrp.color, }, }} contentNode={ <Box height="100%" padding={0.25} width="100%"> <RemoteImage alt={assets.xrp.name} shape="circle" source={assets.xrp.imageUrl} style={{ width: '100%', height: '100%' }} /> </Box> } weight="thin" /> </HStack> <HStack gap={2} flexWrap="wrap"> <ProgressCircle styles={{ progress: { stroke: assets.btc.color, }, }} progress={0.24} size={24} contentNode={ <Box height="100%" padding={0.25} width="100%"> <RemoteImage alt={assets.btc.name} shape="circle" source={assets.btc.imageUrl} style={{ width: '100%', height: '100%' }} /> </Box> } weight="thin" /> <ProgressCircle styles={{ progress: { stroke: assets.btc.color, }, }} progress={0.24} size={32} contentNode={ <Box height="100%" padding={0.25} width="100%"> <RemoteImage alt={assets.btc.name} shape="circle" source={assets.btc.imageUrl} style={{ width: '100%', height: '100%' }} /> </Box> } weight="thin" /> <ProgressCircle styles={{ progress: { stroke: assets.btc.color, }, }} progress={0.24} size={40} contentNode={ <Box height="100%" padding={0.25} width="100%"> <RemoteImage alt={assets.btc.name} shape="circle" source={assets.btc.imageUrl} style={{ width: '100%', height: '100%' }} /> </Box> } weight="thin" /> <ProgressCircle styles={{ progress: { stroke: assets.btc.color, }, }} progress={0.24} size={48} contentNode={ <Box height="100%" padding={0.25} width="100%"> <RemoteImage alt={assets.btc.name} shape="circle" source={assets.btc.imageUrl} style={{ width: '100%', height: '100%' }} /> </Box> } weight="thin" /> <ProgressCircle styles={{ progress: { stroke: assets.btc.color, }, }} progress={0.24} size={56} contentNode={ <Box height="100%" padding={0.25} width="100%"> <RemoteImage alt={assets.btc.name} shape="circle" source={assets.btc.imageUrl} style={{ width: '100%', height: '100%' }} /> </Box> } weight="thin" /> </HStack> </VStack>
Custom Text Color
The progress circle's default content can be customized to display a custom text color.
<HStack gap={2}> <ProgressCircle color="fgPrimary" progress={0.2} size={100} contentNode={<DefaultProgressCircleContent color="fgPrimary" progress={0.2} />} /> <ProgressCircle color="fgPositive" progress={0.4} size={100} contentNode={<DefaultProgressCircleContent color="fgPositive" progress={0.4} />} /> </HStack>
Custom Styles
The progress circle can be customized with styles and class names.
<HStack gap={2}> <ProgressCircle progress={0.4} size={100} styles={{ circle: { stroke: 'transparent', }, }} contentNode={ <Text font="title1" color="fgPrimary"> 40% </Text> } weight="semiheavy" /> <ProgressCircle color="fgPositive" progress={0.6} size={100} styles={{ progress: { strokeLinecap: 'square', }, }} contentNode={<Icon color="fgPositive" name="circleCheckmark" size="l" />} /> </HStack>
Interactive Demo
This is for demo purposes. ProgressContainerWithButtons isn't designed for production usage.
<ProgressContainerWithButtons> {({ calculateProgress }) => ( <HStack gap={2}> <ProgressCircle progress={calculateProgress(0)} size={100} /> <ProgressCircle progress={calculateProgress(0.2)} size={100} /> </HStack> )} </ProgressContainerWithButtons>
Animation
By default, ProgressCircle animates progress changes. Use disableAnimateOnMount to skip the initial animation while still animating subsequent changes.
<ProgressContainerWithButtons> {({ calculateProgress }) => ( <HStack gap={2}> <VStack gap={1} alignItems="center"> <Text variant="label2">Normal animation</Text> <ProgressCircle progress={calculateProgress(0)} size={100} /> </VStack> <VStack gap={1} alignItems="center"> <Text variant="label2">Disable animation on mount</Text> <ProgressCircle disableAnimateOnMount progress={calculateProgress(0.3)} size={100} /> </VStack> </HStack> )} </ProgressContainerWithButtons>
Callbacks
You can use the onAnimationStart and onAnimationEnd callbacks to track the progress of the animation.
function Example() { const [animationStatus, setAnimationStatus] = React.useState('Ready'); const handleAnimationStart = useCallback(() => { setAnimationStatus('Animating...'); }, []); const handleAnimationEnd = useCallback(() => { setAnimationStatus('Animation Ended'); }, []); return ( <ProgressContainerWithButtons> {({ calculateProgress }) => ( <VStack gap={2}> <Text>Animation Status: {animationStatus}</Text> <ProgressCircle onAnimationEnd={handleAnimationEnd} onAnimationStart={handleAnimationStart} progress={calculateProgress(0.2)} size={100} /> </VStack> )} </ProgressContainerWithButtons> ); }