diff --git a/.changeset/serious-drinks-work.md b/.changeset/serious-drinks-work.md
new file mode 100644
index 00000000000..f10a071bc79
--- /dev/null
+++ b/.changeset/serious-drinks-work.md
@@ -0,0 +1,7 @@
+---
+'@primer/react': minor
+---
+
+Adds a new prop, `animated`, to the ProgressBar component. This allows the "filled" segment(s) to animate and indicate some process is still in progress.
+
+
diff --git a/src/ProgressBar/ProgressBar.docs.json b/src/ProgressBar/ProgressBar.docs.json
index 6534d5a08ed..77b5fb3f109 100644
--- a/src/ProgressBar/ProgressBar.docs.json
+++ b/src/ProgressBar/ProgressBar.docs.json
@@ -5,6 +5,11 @@
"a11yReviewed": false,
"stories": [],
"props": [
+ {
+ "name": "animated",
+ "type": "boolean",
+ "description": "Whether the filled in area(s) of the progress bar will be animated or not"
+ },
{
"name": "progress",
"type": "number",
diff --git a/src/ProgressBar/ProgressBar.features.stories.tsx b/src/ProgressBar/ProgressBar.features.stories.tsx
index 5c1e860a26a..5af0a689c19 100644
--- a/src/ProgressBar/ProgressBar.features.stories.tsx
+++ b/src/ProgressBar/ProgressBar.features.stories.tsx
@@ -25,3 +25,5 @@ export const MultipleItems = () => (
)
+
+export const Animated = () =>
diff --git a/src/ProgressBar/ProgressBar.tsx b/src/ProgressBar/ProgressBar.tsx
index bc7fb26a5be..d6723c0d769 100644
--- a/src/ProgressBar/ProgressBar.tsx
+++ b/src/ProgressBar/ProgressBar.tsx
@@ -1,5 +1,5 @@
import React, {forwardRef} from 'react'
-import styled from 'styled-components'
+import styled, {keyframes} from 'styled-components'
import {width, WidthProps} from 'styled-system'
import {get} from '../constants'
import sx, {SxProp} from '../sx'
@@ -7,9 +7,25 @@ import {warning} from '../utils/warning'
type ProgressProp = {progress?: string | number}
+const shimmer = keyframes`
+ from { mask-position: 200%; }
+ to { mask-position: 0%; }
+`
+
export const Item = styled.span`
width: ${props => (props.progress ? `${props.progress}%` : 0)};
background-color: ${get('colors.success.emphasis')};
+
+ @media (prefers-reduced-motion: no-preference) {
+ &[data-animated='true'] {
+ mask-image: linear-gradient(75deg, #000 30%, rgba(0, 0, 0, 0.65) 80%);
+ mask-size: 200%;
+ animation: ${shimmer};
+ animation-duration: 1s;
+ animation-iteration-count: infinite;
+ }
+ }
+
${sx};
`
@@ -24,6 +40,7 @@ const sizeMap = {
type StyledProgressContainerProps = {
inline?: boolean
barSize?: keyof typeof sizeMap
+ animated?: boolean
} & WidthProps &
SxProp
@@ -42,7 +59,10 @@ export type ProgressBarProps = React.HTMLAttributes & {bg?: str
ProgressProp
export const ProgressBar = forwardRef(
- ({progress, bg = 'success.emphasis', barSize = 'default', children, ...rest}: ProgressBarProps, forwardRef) => {
+ (
+ {animated, progress, bg = 'success.emphasis', barSize = 'default', children, ...rest}: ProgressBarProps,
+ forwardRef,
+ ) => {
if (children && progress) {
throw new Error('You should pass `progress` or children, not both.')
}
@@ -65,7 +85,7 @@ export const ProgressBar = forwardRef(
return (
- {children ?? }
+ {children ?? }
)
},
diff --git a/src/__tests__/__snapshots__/ProgressBar.test.tsx.snap b/src/__tests__/__snapshots__/ProgressBar.test.tsx.snap
index dad58b54e9e..7e516f82971 100644
--- a/src/__tests__/__snapshots__/ProgressBar.test.tsx.snap
+++ b/src/__tests__/__snapshots__/ProgressBar.test.tsx.snap
@@ -18,6 +18,21 @@ exports[`ProgressBar renders consistently 1`] = `
height: 8px;
}
+@media (prefers-reduced-motion:no-preference) {
+ .c1[data-animated='true'] {
+ -webkit-mask-image: linear-gradient(75deg,#000 30%,rgba(0,0,0,0.65) 80%);
+ mask-image: linear-gradient(75deg,#000 30%,rgba(0,0,0,0.65) 80%);
+ -webkit-mask-size: 200%;
+ mask-size: 200%;
+ -webkit-animation: jiNfbM;
+ animation: jiNfbM;
+ -webkit-animation-duration: 1s;
+ animation-duration: 1s;
+ -webkit-animation-iteration-count: infinite;
+ animation-iteration-count: infinite;
+ }
+}
+