Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
38dc3bf
feat: add chart baseline
hcopp Mar 13, 2026
4ccb8ba
Improve web baseline
hcopp Mar 16, 2026
0bdc959
Continue work
hcopp Mar 24, 2026
4e4acad
Merge branch 'master' into hunter/chart-baseline
hcopp Mar 26, 2026
23a8707
Cleanup logic
hcopp Mar 26, 2026
9090810
Cleanup types
hcopp Mar 27, 2026
5bdc43e
Cleanup gradient logic and drop areaBaseline
hcopp Mar 27, 2026
f2c0e7c
Cleanup baseline calculation
hcopp Mar 27, 2026
b53656f
Cleanup baseline logic and use CartesianAxis
hcopp Mar 27, 2026
53b5e5c
Cleanup examples
hcopp Mar 27, 2026
60efea4
Simplify stack gap
hcopp Mar 27, 2026
88f0093
Cleanup types
hcopp Mar 27, 2026
1c11506
Cleanup logic
hcopp Mar 27, 2026
f53db73
Fix lint
hcopp Mar 27, 2026
5f3a7d1
Cleanup logic
hcopp Mar 27, 2026
a594fb4
Cleanup visreg
hcopp Mar 27, 2026
452921b
Merge branch 'master' into hunter/chart-baseline
hcopp Apr 13, 2026
8000727
Merge branch 'master' into hunter/chart-baseline
hcopp Apr 19, 2026
02dfc96
Add back baseline as deprecated
hcopp Apr 19, 2026
75d89cd
Update jsdocs
hcopp Apr 19, 2026
5affa2c
Undo docs changes
hcopp Apr 20, 2026
83d9ea4
Cleanup new docs
hcopp Apr 20, 2026
7f0b919
Fix layout issues
hcopp Apr 20, 2026
380c77c
Fix formatting
hcopp Apr 20, 2026
8afe48a
Drop description
hcopp Apr 20, 2026
f1c96fc
Update examples
hcopp Apr 20, 2026
8b6a955
Merge branch 'master' into hunter/chart-baseline
hcopp Apr 21, 2026
fe87258
Cleanup storybook
hcopp Apr 21, 2026
83de3d9
Bump version
hcopp Apr 21, 2026
5e82787
Increase tests
hcopp Apr 21, 2026
3dc6234
Fix formatting
hcopp Apr 21, 2026
267819c
Fix chart docs disclaimer
hcopp Apr 21, 2026
4a5217a
Fix stories and cleanup axis
hcopp Apr 21, 2026
7899223
Cleanup props
hcopp Apr 21, 2026
f4eba3f
Update deprecation note
hcopp Apr 21, 2026
7948ab3
Merge branch 'master' into hunter/chart-baseline
hcopp Apr 22, 2026
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
2 changes: 2 additions & 0 deletions .percy.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ module.exports = {
'Components/LottieStatusAnimation: Default',
'Components/Loaders/MaterialSpinner: Material Spinner Default',
'Components/Chart/CartesianChart: Transitions',
// Visreg tested in other story, this is for manual testing
'Components/Chart/CartesianChart: Advanced',
],
include: [
// 'Core Components/SparklineInteractive:*',
Expand Down
338 changes: 89 additions & 249 deletions apps/docs/docs/components/charts/AreaChart/_mobileExamples.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
AreaChart is a cartesian chart variant that allows for easy visualization of stacked data.
AreaChart is a wrapper for [CartesianChart](/components/charts/CartesianChart) that has some unique features over [LineChart](/components/charts/LineChart), such as the ability to stack areas on top of each other and a default value-axis minimum that follows the baseline (`0` when baseline is not set). Charts are built using `@shopify/react-native-skia`.

## Basic Example

Expand Down Expand Up @@ -67,7 +67,7 @@ function StackingExample() {
3520, 3900,
],
color: theme.color.fgPositive,
LineComponent: DottedLine,
LineComponent: (props) => <DottedLine {...props} dashIntervals={[6, 6]} />,
},
]}
AreaComponent={(props) => <DottedArea {...props} peakOpacity={0.4} baselineOpacity={0.4} />}
Expand All @@ -79,7 +79,7 @@ function StackingExample() {

## Negative Values

When an area chart contains negative values, the baseline automatically adjusts to zero instead of the bottom of the chart. The area fills from the data line to the zero baseline, properly showing both positive and negative regions.
AreaChart uses the value-axis baseline as the default minimum when `domain.min` is not set (baseline defaults to `0`). If your data crosses below that baseline, the domain expands to include those values so both positive and negative regions render correctly.

```jsx
<AreaChart
Expand Down Expand Up @@ -131,264 +131,104 @@ You can have different area styles for each series.
/>
```

## Animations
## Composed Examples

You can configure chart transitions using the `transitions` prop.
### Custom Baseline

```jsx
<AreaChart
{...props}
transitions={{
enter: { type: 'spring', stiffness: 700, damping: 80 },
update: { type: 'spring', stiffness: 700, damping: 20 },
}}
/>
```

Also, you can toggle animations by setting `animate` to `true` or `false`.

```jsx
<AreaChart {...props} animate={false} />
```

## Gradients

You can use the `gradient` prop on `series` to enable gradients.
You can combine a custom baseline with a scrubber label that shows both price and date.

Each stop requires an `offset`, which is based on the data within the x/y scale and `color`, with an optional `opacity` (defaults to 1).

Values in between stops will be interpolated smoothly.

```jsx
function ContinuousGradient() {
```tsx
function CustomBaseline() {
const theme = useTheme();
const spectrumColors = [
'blue',
'green',
'orange',
'yellow',
'gray',
'indigo',
'pink',
'purple',
'red',
'teal',
'chartreuse',
];
const data = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];

const [currentSpectrumColor, setCurrentSpectrumColor] = useState('pink');

return (
<VStack gap={2}>
<HStack flexWrap="wrap" gap={1} justifyContent="flex-end">
{spectrumColors.map((color) => (
<Pressable
key={color}
accessibilityLabel={`Select ${color}`}
height={16}
onPress={() => setCurrentSpectrumColor(color)}
style={{
backgroundColor: `rgb(${theme.spectrum[`${color}20`]})`,
borderColor: `rgb(${theme.spectrum[`${color}50`]})`,
borderWidth: 2,
borderRadius: 1000,
}}
width={16}
/>
))}
</HStack>
<AreaChart
enableScrubbing
height={250}
series={[
{
id: 'prices',
data: data,
gradient: {
stops: ({ min, max }) => [
{ offset: min, color: `rgb(${theme.spectrum[`${currentSpectrumColor}80`]})` },
{ offset: max, color: `rgb(${theme.spectrum[`${currentSpectrumColor}20`]})` },
],
},
},
]}
showYAxis
yAxis={{
showGrid: true,
}}
>
<Scrubber />
</AreaChart>
</VStack>
const candles = [...btcCandles].reverse().slice(0, 180);
const prices = candles.map((candle) => parseFloat(candle.close));
const dates = candles.map((candle) => new Date(parseInt(candle.start, 10) * 1000));

const startingPrice = prices[0];

const formatPrice = useCallback((price: number) => {
return `$${price.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}`;
}, []);

const formatPriceInThousands = useCallback((price: number) => {
return `$${(price / 1000).toLocaleString('en-US', {
minimumFractionDigits: 0,
maximumFractionDigits: 0,
})}k`;
}, []);

const formatDate = useCallback((date: Date) => {
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
}, []);

const formatLabel = useCallback(
(dataIndex: number) => `${formatPrice(prices[dataIndex])} ${formatDate(dates[dataIndex])}`,
[dates, formatDate, formatPrice, prices],
);
}
```

### Discrete

You can set multiple stops at the same offset to create a discrete gradient.

```jsx
function DiscreteGradient() {
const theme = useTheme();
const spectrumColors = [
'blue',
'green',
'orange',
'yellow',
'gray',
'indigo',
'pink',
'purple',
'red',
'teal',
'chartreuse',
];
const data = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];
const PriceLabel = memo((props: ReferenceLineLabelComponentProps) => (
<DefaultReferenceLineLabel
{...props}
background={theme.color.bgSecondary}
borderRadius={12.5}
color={theme.color.fg}
dx={12}
font="label1"
horizontalAlignment="left"
inset={{ top: 4, bottom: 4, left: 8, right: 8 }}
/>
));

const [currentSpectrumColor, setCurrentSpectrumColor] = useState('pink');
const chartAccessibilityLabel = `Bitcoin area chart with custom baseline. Current price: ${formatPrice(
prices[prices.length - 1],
)}. Swipe to navigate.`;

return (
<VStack gap={2}>
<HStack flexWrap="wrap" gap={1} justifyContent="flex-end">
{spectrumColors.map((color) => (
<Pressable
key={color}
accessibilityLabel={`Select ${color}`}
height={16}
onPress={() => setCurrentSpectrumColor(color)}
style={{
backgroundColor: `rgb(${theme.spectrum[`${color}20`]})`,
borderColor: `rgb(${theme.spectrum[`${color}50`]})`,
borderWidth: 2,
borderRadius: 1000,
}}
width={16}
/>
))}
</HStack>
<AreaChart
enableScrubbing
height={250}
series={[
{
id: 'prices',
data: data,
gradient: {
stops: ({ min, max }) => [
{ offset: min, color: `rgb(${theme.spectrum[`${currentSpectrumColor}80`]})` },
{
offset: min + (max - min) / 3,
color: `rgb(${theme.spectrum[`${currentSpectrumColor}80`]})`,
},
{
offset: min + (max - min) / 3,
color: `rgb(${theme.spectrum[`${currentSpectrumColor}50`]})`,
},
{
offset: min + ((max - min) / 3) * 2,
color: `rgb(${theme.spectrum[`${currentSpectrumColor}50`]})`,
},
{
offset: min + ((max - min) / 3) * 2,
color: `rgb(${theme.spectrum[`${currentSpectrumColor}20`]})`,
},
{ offset: max, color: `rgb(${theme.spectrum[`${currentSpectrumColor}20`]})` },
],
},
},
]}
showLines
strokeWidth={4}
showYAxis
yAxis={{
showGrid: true,
}}
fillOpacity={0.5}
>
<Scrubber />
</AreaChart>
</VStack>
const getScrubberAccessibilityLabel = useCallback(
(index: number) => `${formatPrice(prices[index])} ${formatDate(dates[index])}`,
[dates, formatDate, formatPrice, prices],
);
}
```

### Axes

By default, gradients will be applied to the y-axis. You can apply a gradient to the x-axis by setting `axis` to `x` in the gradient definition.

```jsx
function XAxisGradient() {
const theme = useTheme();
const spectrumColors = [
'blue',
'green',
'orange',
'yellow',
'gray',
'indigo',
'pink',
'purple',
'red',
'teal',
'chartreuse',
];
const data = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];

const [currentSpectrumColor, setCurrentSpectrumColor] = useState('pink');

return (
<VStack gap={2}>
<HStack flexWrap="wrap" gap={1} justifyContent="flex-end">
{spectrumColors.map((color) => (
<Pressable
key={color}
accessibilityLabel={`Select ${color}`}
height={16}
onPress={() => setCurrentSpectrumColor(color)}
style={{
backgroundColor: `rgb(${theme.spectrum[`${color}20`]})`,
borderColor: `rgb(${theme.spectrum[`${color}50`]})`,
borderWidth: 2,
borderRadius: 1000,
}}
width={16}
/>
))}
</HStack>
<AreaChart
enableScrubbing
height={250}
series={[
{
id: 'prices',
data: data,
gradient: {
axis: 'x',
stops: ({ min, max }) => [
{
offset: min,
color: `rgb(${theme.spectrum[`${currentSpectrumColor}80`]})`,
opacity: 0,
},
{
offset: max,
color: `rgb(${theme.spectrum[`${currentSpectrumColor}20`]})`,
opacity: 1,
},
],
},
<AreaChart
enableScrubbing
showLines
showYAxis
accessibilityLabel={chartAccessibilityLabel}
getScrubberAccessibilityLabel={getScrubberAccessibilityLabel}
fillOpacity={0.5}
height={300}
series={[
{
id: 'prices',
data: prices,
gradient: {
stops: [
{ offset: startingPrice, color: theme.color.fgNegative },
{ offset: startingPrice, color: theme.color.fgPositive },
],
},
]}
showYAxis
yAxis={{
showGrid: true,
}}
>
<Scrubber />
</AreaChart>
</VStack>
},
]}
yAxis={{
baseline: startingPrice,
showGrid: true,
tickLabelFormatter: formatPriceInThousands,
domain: { min: 70000, max: 120000 },
ticks: [80000, 100000, 120000],
}}
>
<Scrubber label={formatLabel} labelElevated />
<ReferenceLine
LabelComponent={PriceLabel}
LineComponent={(props) => <DottedLine {...props} dashIntervals={[0, 16]} strokeWidth={3} />}
dataY={startingPrice}
stroke={theme.color.fg}
label={formatPrice(startingPrice)}
/>
</AreaChart>
);
}
```
Loading
Loading