Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,65 @@
</HStack>
```

## Indeterminate

Use the `indeterminate` prop when progress is unknown (e.g. loading). The circle shows a spinning partial arc with no percentage text. This is the recommended replacement for the deprecated [Spinner](/components/feedback/Spinner) in loading contexts such as [IconButton](/components/buttons/IconButton) or button loading states.

When `indeterminate` is true, the default color is `fgMuted`; you can override `color` as needed. Always provide `accessibilityLabel` so screen readers announce the loading state.

### Thickness (weight)

Indeterminate uses the same `weight` prop as determinate progress. The default is **`"normal"`** (4px stroke). Use `"thin"` (2px), `"semiheavy"` (8px), or `"heavy"` (12px) to change thickness.

```jsx
<HStack gap={2} alignItems="center">
<VStack gap={1} alignItems="center">
<Text variant="label2">Default (normal)</Text>
<ProgressCircle accessibilityLabel="Loading" indeterminate size={64} />
</VStack>
<VStack gap={1} alignItems="center">
<Text variant="label2">weight="thin"</Text>
<ProgressCircle accessibilityLabel="Loading" indeterminate size={64} weight="thin" />
</VStack>
<VStack gap={1} alignItems="center">
<Text variant="label2">weight="semiheavy"</Text>
<ProgressCircle accessibilityLabel="Loading" indeterminate size={64} weight="semiheavy" />
</VStack>
</HStack>
```

### Progress (arc length)

When `indeterminate` is true, the **`progress` prop controls the length of the visible arc** (how much of the circle is drawn), not a completion percentage. It defaults to `0.75` (a 270° arc). Override it to change the arc length—e.g. `0.5` for a half circle or `0.25` for a shorter arc.

```jsx
<HStack gap={2} alignItems="center">
<VStack gap={1} alignItems="center">
<Text variant="label2">progress=0.25</Text>
<ProgressCircle accessibilityLabel="Loading" indeterminate progress={0.25} size={56} />
</VStack>
<VStack gap={1} alignItems="center">
<Text variant="label2">progress=0.5</Text>
<ProgressCircle accessibilityLabel="Loading" indeterminate progress={0.5} size={56} />
</VStack>
<VStack gap={1} alignItems="center">
<Text variant="label2">progress=0.75 (default)</Text>
<ProgressCircle accessibilityLabel="Loading" indeterminate progress={0.75} size={56} />
</VStack>
</HStack>
```

### Sizes and color

```jsx
<HStack gap={2} alignItems="center">
<ProgressCircle accessibilityLabel="Loading" indeterminate size={32} />
<ProgressCircle accessibilityLabel="Loading" indeterminate size={56} />
<ProgressCircle accessibilityLabel="Loading" indeterminate size={100} />
<ProgressCircle accessibilityLabel="Loading" indeterminate color="bgPrimary" size={56} />
</HStack>
```

## Thin

```jsx
Expand Down
59 changes: 59 additions & 0 deletions apps/docs/docs/components/feedback/ProgressCircle/_webExamples.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,65 @@
</HStack>
```

## Indeterminate

Use the `indeterminate` prop when progress is unknown (e.g. loading). The circle shows a spinning partial arc with no percentage text. This is the recommended replacement for the deprecated [Spinner](/components/feedback/Spinner) in loading contexts such as [IconButton](/components/inputs/IconButton) or button loading states.

When `indeterminate` is true, the default color is `fgMuted`; you can override `color` as needed. Always provide `accessibilityLabel` so screen readers announce the loading state.

### Thickness (weight)

Indeterminate uses the same `weight` prop as determinate progress. The default is **`"normal"`** (4px stroke). Use `"thin"` (2px), `"semiheavy"` (8px), or `"heavy"` (12px) to change thickness.

```jsx live
<HStack gap={2} flexWrap="wrap" alignItems="center">
<VStack gap={1} alignItems="center">
<Text variant="label2">Default (normal)</Text>
<ProgressCircle accessibilityLabel="Loading" indeterminate size={64} />
</VStack>
<VStack gap={1} alignItems="center">
<Text variant="label2">weight="thin"</Text>
<ProgressCircle accessibilityLabel="Loading" indeterminate size={64} weight="thin" />
</VStack>
<VStack gap={1} alignItems="center">
<Text variant="label2">weight="semiheavy"</Text>
<ProgressCircle accessibilityLabel="Loading" indeterminate size={64} weight="semiheavy" />
</VStack>
</HStack>
```

### Progress (arc length)

When `indeterminate` is true, the **`progress` prop controls the length of the visible arc** (how much of the circle is drawn), not a completion percentage. It defaults to `0.75` (a 270° arc). Override it to change the arc length—e.g. `0.5` for a half circle or `0.25` for a shorter arc.

```jsx live
<HStack gap={2} flexWrap="wrap" alignItems="center">
<VStack gap={1} alignItems="center">
<Text variant="label2">progress=0.25</Text>
<ProgressCircle accessibilityLabel="Loading" indeterminate progress={0.25} size={56} />
</VStack>
<VStack gap={1} alignItems="center">
<Text variant="label2">progress=0.5</Text>
<ProgressCircle accessibilityLabel="Loading" indeterminate progress={0.5} size={56} />
</VStack>
<VStack gap={1} alignItems="center">
<Text variant="label2">progress=0.75 (default)</Text>
<ProgressCircle accessibilityLabel="Loading" indeterminate progress={0.75} size={56} />
</VStack>
</HStack>
```

### Sizes and color

```jsx live
<HStack gap={2} flexWrap="wrap" alignItems="center">
<ProgressCircle accessibilityLabel="Loading" indeterminate size={32} />
<ProgressCircle accessibilityLabel="Loading" indeterminate size={56} />
<ProgressCircle accessibilityLabel="Loading" indeterminate size={100} />
<ProgressCircle accessibilityLabel="Loading" indeterminate color="bgPrimary" size={56} />
</HStack>
```

## Thin

```jsx live
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"import": "import { ProgressCircle } from '@coinbase/cds-mobile/visualizations/ProgressCircle'",
"source": "https://github.com/coinbase/cds/blob/master/packages/mobile/src/visualizations/ProgressCircle.tsx",
"description": "A circular visual indicator of completion progress.",
"description": "A circular visual indicator of completion progress. Supports both determinate progress (0–100%) and an indeterminate variant for loading states.",
"relatedComponents": [
{
"label": "ProgressBar",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"import": "import { ProgressCircle } from '@coinbase/cds-web/visualizations/ProgressCircle'",
"source": "https://github.com/coinbase/cds/blob/master/packages/web/src/visualizations/ProgressCircle.tsx",
"storybook": "https://cds-storybook.coinbase.com/?path=/story/components-progresscircle--default",
"description": "A circular visual indicator of completion progress.",
"description": "A circular visual indicator of completion progress. Supports both determinate progress (0–100%) and an indeterminate variant for loading states.",
"relatedComponents": [
{
"label": "ProgressBar",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"import": "import { Spinner } from '@coinbase/cds-mobile/loaders/Spinner'",
"source": "https://github.com/coinbase/cds/blob/master/packages/mobile/src/loaders/Spinner.tsx",
"description": "A loading indicator that displays a rotating animation to communicate that content is loading or a background process is in progress.",
"warning": "This component is deprecated. Use indeterminate ProgressCircle for loading indicators instead.",
"relatedComponents": [
{
"label": "Fallback",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"source": "https://github.com/coinbase/cds/blob/master/packages/web/src/loaders/Spinner.tsx",
"storybook": "https://cds-storybook.coinbase.com/?path=/story/components-loaders-spinner--spinner-default",
"description": "A loading indicator that displays a rotating animation to communicate that content is loading or a background process is in progress.",
"warning": "This component is deprecated. Use indeterminate ProgressCircle for loading indicators instead.",
"relatedComponents": [
{
"label": "Fallback",
Expand Down
29 changes: 28 additions & 1 deletion apps/docs/docs/components/inputs/Button/_mobileExamples.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,34 @@ Use transparent buttons for supplementary actions with lower prominence. The con

### Loading

Use the `loading` prop to indicate an action is in progress. The button becomes non-interactive and displays a spinner while preserving its width.
Use the `loading` prop to indicate an action is in progress. The button becomes non-interactive and displays a loading indicator (indeterminate [ProgressCircle](/components/feedback/ProgressCircle)) while preserving its width.

#### Loading by variant

Loading works with all variants and transparent. The label is hidden and the progress circle is shown in the button’s accent color.

```jsx
<HStack gap={2} flexWrap="wrap" alignItems="center">
<Button loading>Primary</Button>
<Button loading variant="secondary">
Secondary
</Button>
<Button loading variant="tertiary">
Tertiary
</Button>
<Button loading variant="negative">
Negative
</Button>
<Button loading transparent variant="secondary">
Transparent
</Button>
<Button loading compact>
Compact
</Button>
</HStack>
```

#### Basic loading

```jsx
<HStack gap={2}>
Expand Down
31 changes: 30 additions & 1 deletion apps/docs/docs/components/inputs/Button/_webExamples.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,36 @@ Use transparent buttons for supplementary actions with lower prominence. The con

### Loading

Use the `loading` prop to indicate an action is in progress. The button becomes non-interactive and displays a spinner while preserving its width.
Use the `loading` prop to indicate an action is in progress. The button becomes non-interactive and displays a loading indicator (indeterminate [ProgressCircle](/components/feedback/ProgressCircle)) while preserving its width.

#### Loading by variant

Loading works with all variants and transparent. The label is hidden and the progress circle is shown in the button’s accent color.

```jsx live
<HStack gap={2} flexWrap="wrap" alignItems="center">
<Button loading>Primary</Button>
<Button loading variant="secondary">
Secondary
</Button>
<Button loading variant="tertiary">
Tertiary
</Button>
<Button loading variant="negative">
Negative
</Button>
<Button loading transparent variant="secondary">
Transparent
</Button>
<Button loading compact>
Compact
</Button>
</HStack>
```

#### Interactive loading

Toggle loading state to see the transition. Use for async actions like save or submit.

```jsx live
function LoadingExample() {
Expand Down
59 changes: 58 additions & 1 deletion apps/docs/docs/components/inputs/IconButton/_webExamples.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,64 @@ Use the `transparent` prop to remove the background until the user interacts wit

### Loading

Use the `loading` prop to show a spinner when an action is in progress. The button becomes non-interactive and displays a loading spinner instead of the icon.
Use the `loading` prop when an action is in progress. The button becomes non-interactive and shows an indeterminate [ProgressCircle](/components/feedback/ProgressCircle) instead of the icon. The circle size follows the button’s `iconSize`.

#### Loading by variant

Loading works with all variants, transparent, and compact. Provide `accessibilityLabel` so screen readers announce the loading state (e.g. "Loading").

```jsx live
<HStack gap={2} flexWrap="wrap" alignItems="center">
<IconButton
loading
name="refresh"
accessibilityLabel="Loading"
variant="primary"
onClick={console.log}
/>
<IconButton
loading
name="refresh"
accessibilityLabel="Loading"
variant="secondary"
onClick={console.log}
/>
<IconButton
loading
name="refresh"
accessibilityLabel="Loading"
variant="tertiary"
onClick={console.log}
/>
<IconButton
loading
name="refresh"
accessibilityLabel="Loading"
variant="foregroundMuted"
onClick={console.log}
/>
<IconButton
loading
transparent
name="refresh"
accessibilityLabel="Loading"
variant="secondary"
onClick={console.log}
/>
<IconButton loading compact name="refresh" accessibilityLabel="Loading" onClick={console.log} />
<IconButton
loading
name="refresh"
accessibilityLabel="Loading"
compact={false}
onClick={console.log}
/>
</HStack>
```

#### Interactive loading

Toggle loading to simulate an async action. The button’s `accessibilityLabel` can reflect the state (e.g. "Submit form" vs "Processing submission").

```jsx live
function LoadingExample() {
Expand Down
6 changes: 6 additions & 0 deletions packages/common/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ All notable changes to this project will be documented in this file.

<!-- template-start -->

## 8.60.0 (3/29/2026 PST)

#### 🚀 Updates

- Deprecate useProgressSize and replace with getProgressSize. [[#501](https://github.com/coinbase/cds/pull/501)]

## 8.59.0 ((3/27/2026, 05:43 AM PST))

This is an artificial version bump with no new change.
Expand Down
2 changes: 1 addition & 1 deletion packages/common/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@coinbase/cds-common",
"version": "8.59.0",
"version": "8.60.0",
"description": "Coinbase Design System - Common",
"repository": {
"type": "git",
Expand Down
14 changes: 14 additions & 0 deletions packages/common/src/visualizations/getProgressSize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { Weight } from '../types/Weight';

Comment thread
adrienzheng-cb marked this conversation as resolved.
export const getProgressSize = (weight: Weight) => {
switch (weight) {
case 'semiheavy':
return 8;
case 'heavy':
return 12;
case 'thin':
return 2;
default:
return 4;
}
};
3 changes: 3 additions & 0 deletions packages/common/src/visualizations/useProgressSize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { useMemo } from 'react';

import type { Weight } from '../types/Weight';

/** @deprecated Use getProgressSize instead. This will be removed in a future major release.
* @deprecationExpectedRemoval v10
*/
Comment thread
hcopp marked this conversation as resolved.
export const useProgressSize = (weight: Weight) => {
return useMemo(() => {
switch (weight) {
Expand Down
4 changes: 4 additions & 0 deletions packages/mcp-server/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ All notable changes to this project will be documented in this file.

<!-- template-start -->

## 8.60.0 ((3/29/2026, 10:49 AM PST))

This is an artificial version bump with no new change.

## 8.59.0 ((3/27/2026, 05:43 AM PST))

This is an artificial version bump with no new change.
Expand Down
2 changes: 1 addition & 1 deletion packages/mcp-server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@coinbase/cds-mcp-server",
"version": "8.59.0",
"version": "8.60.0",
"description": "Coinbase Design System - MCP Server",
"repository": {
"type": "git",
Expand Down
6 changes: 6 additions & 0 deletions packages/mobile/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ All notable changes to this project will be documented in this file.

<!-- template-start -->

## 8.60.0 (3/29/2026 PST)

#### 🚀 Updates

- Add indeterminate ProgressCircle. [[#501](https://github.com/coinbase/cds/pull/501)]

## 8.59.0 (3/27/2026 PST)

#### 🚀 Updates
Expand Down
2 changes: 1 addition & 1 deletion packages/mobile/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@coinbase/cds-mobile",
"version": "8.59.0",
"version": "8.60.0",
"description": "Coinbase Design System - Mobile",
"repository": {
"type": "git",
Expand Down
Loading
Loading