Compare commits

...

2 Commits

Author SHA1 Message Date
Cyril 640e0c041d 💄(frontend) clean up spinner styles
remove inline styles for better maintainability
2026-02-06 23:24:15 +01:00
Cyril 8c3e71d2ab ️(frontend) add reduced-motion spinner fallback
show an hourglass when animations are reduced
2026-02-06 09:21:55 +01:00
2 changed files with 74 additions and 33 deletions
+1
View File
@@ -10,6 +10,7 @@ and this project adheres to
### Changed
- ♿️(frontend) improve spinner reducedmotion fallback #931
- ♿️(frontend) fix form labels and autocomplete wiring #932
- 🥅(summary) catch file-related exceptions when handling recording #944
- 📝(frontend) update legal terms #956
+73 -33
View File
@@ -1,5 +1,12 @@
import { ProgressBar } from 'react-aria-components'
import { css } from '@/styled-system/css'
import { RiHourglassFill } from '@remixicon/react'
import { css, cx } from '@/styled-system/css'
const rotatingArcClassName = css({
animation: 'rotate 1s ease-in-out infinite',
transformOrigin: 'center',
transition: 'transform 16ms linear',
})
export const Spinner = ({
size = 56,
@@ -13,44 +20,77 @@ export const Spinner = ({
const r = 14 - strokeWidth
const c = 2 * r * Math.PI
return (
<ProgressBar aria-label="Loading" value={30}>
<ProgressBar aria-label="Loading..." value={30}>
{({ percentage }) => (
<svg
width={size}
height={size}
viewBox="0 0 28 28"
fill="none"
strokeWidth={strokeWidth}
<div
className={css({
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
})}
>
<circle
cx={center}
cy={center}
r={r}
strokeDasharray={0}
strokeDashoffset={0}
strokeLinecap="round"
<svg
width={size}
height={size}
viewBox="0 0 28 28"
fill="none"
strokeWidth={strokeWidth}
className={css({
stroke: variant == 'light' ? 'primary.100' : 'transparent',
'@media (prefers-reduced-motion: reduce)': {
display: 'none',
},
})}
style={{}}
/>
<circle
cx={center}
cy={center}
r={r}
strokeDasharray={`${c} ${c}`}
strokeDashoffset={percentage && c - (percentage / 100) * c}
strokeLinecap="round"
>
<circle
cx={center}
cy={center}
r={r}
strokeDasharray={0}
strokeDashoffset={0}
strokeLinecap="round"
className={css({
stroke: variant == 'light' ? 'primary.100' : 'transparent',
})}
/>
<circle
cx={center}
cy={center}
r={r}
strokeDasharray={`${c} ${c}`}
strokeDashoffset={
typeof percentage === 'number'
? c - (percentage / 100) * c
: undefined
}
strokeLinecap="round"
className={cx(
rotatingArcClassName,
css({
stroke: variant == 'light' ? 'primary.800' : 'white',
})
)}
/>
</svg>
<span
aria-hidden="true"
className={css({
stroke: variant == 'light' ? 'primary.800' : 'white',
display: 'none',
color: 'black',
'@media (prefers-reduced-motion: reduce)': {
display: 'inline-flex',
},
})}
style={{
animation: `rotate 1s ease-in-out infinite`,
transformOrigin: 'center',
transition: 'transform 16ms linear',
}}
/>
</svg>
>
<RiHourglassFill
size={Math.max(16, Math.round(size * 0.4))}
style={{
display: 'block',
transform: 'translateY(1px)',
color: variant == 'light' ? 'primary.800' : 'white',
}}
/>
</span>
</div>
)}
</ProgressBar>
)