# CartesianChart

A flexible, low-level chart component for displaying data in an x/y coordinate space. Provides a foundation for building custom chart visualizations with full control over rendering.

## Import

```tsx
import { CartesianChart } from '@coinbase/cds-web-visualization'
```

## Examples

CartesianChart is a customizable, SVG based component that can be used to display a variety of data in a x/y coordinate space. The underlying logic is handled by D3.

### Basic Example

[AreaChart](/components/graphs/AreaChart/), [BarChart](/components/graphs/BarChart/), and [LineChart](/components/graphs/LineChart/) are built on top of CartesianChart and have default functionality for your chart.

```jsx live
<VStack gap={2}>
  <AreaChart
    enableScrubbing
    height={{ base: 150, tablet: 200, desktop: 250 }}
    series={[
      {
        id: 'prices',
        data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
      },
    ]}
    showYAxis
    yAxis={{
      showGrid: true,
    }}
  >
    <Scrubber />
  </AreaChart>
  <BarChart
    enableScrubbing
    height={{ base: 150, tablet: 200, desktop: 250 }}
    series={[
      {
        id: 'prices',
        data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
      },
    ]}
    showYAxis
    yAxis={{
      showGrid: true,
    }}
  >
    <Scrubber hideOverlay seriesIds={[]} />
  </BarChart>
  <LineChart
    enableScrubbing
    height={{ base: 150, tablet: 200, desktop: 250 }}
    series={[
      {
        id: 'prices',
        data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
      },
    ]}
    showYAxis
    showArea
    yAxis={{
      showGrid: true,
    }}
  >
    <Scrubber />
  </LineChart>
</VStack>
```

### Series

Series are the data that will be displayed on the chart. Each series must have a defined `id`.

#### Series Data

You can pass in an array of numbers or an array of tuples for the `data` prop. Passing in null values is equivalent to no data at that index.

```jsx live
function ForecastedPrice() {
  const ForecastRect = memo(({ startIndex, endIndex }) => {
    const { drawingArea, getXScale } = useCartesianChartContext();

    const xScale = getXScale();

    if (!xScale) return;

    const startX = xScale(startIndex);
    const endX = xScale(endIndex);
    return (
      <rect
        x={startX}
        y={drawingArea.y}
        width={endX - startX}
        height={drawingArea.height}
        fill="var(--color-accentSubtleBlue)"
      />
    );
  });
  return (
    <CartesianChart
      enableScrubbing
      height={{ base: 150, tablet: 200, desktop: 250 }}
      series={[
        {
          id: 'prices',
          data: [10, 22, 29, 45, 98, 45, 22, 52, 54, 60, 64, 68, 72, 76],
          color: 'var(--color-accentBoldBlue)',
        },
        {
          id: 'variance',
          data: [
            null,
            null,
            null,
            null,
            null,
            null,
            null,
            [52, 52],
            [50, 57],
            [52, 63],
            [55, 75],
            [57, 77],
            [59, 79],
            [60, 80],
          ],
          color: 'var(--color-accentBoldBlue)',
        },
      ]}
      yAxis={{
        showGrid: true,
      }}
    >
      <ForecastRect startIndex={7} endIndex={13} />
      <Area seriesId="variance" type="solid" fillOpacity={0.3} />
      <Line seriesId="prices" />
    </CartesianChart>
  );
}
```

#### Series Axis IDs

Each series can have a different `yAxisId`, allowing you to compare data from different contexts.

```jsx live
<CartesianChart
  height={{ base: 150, tablet: 200, desktop: 250 }}
  series={[
    {
      id: 'revenue',
      data: [455, 520, 380, 455, 190, 235],
      yAxisId: 'revenue',
      color: 'var(--color-accentBoldYellow)',
    },
    {
      id: 'profit',
      data: [23, 15, 30, 56, 4, 12],
      yAxisId: 'profit',
      color: 'var(--color-fgPositive)',
    },
  ]}
  xAxis={{
    data: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
    scaleType: 'band',
  }}
  yAxis={[
    {
      id: 'revenue',
    },
    {
      id: 'profit',
    },
  ]}
>
  <XAxis showLine showTickMarks />
  <YAxis
    showGrid
    showLine
    showTickMarks
    axisId="revenue"
    position="left"
    requestedTickCount={5}
    tickLabelFormatter={(value) => `$${value}k`}
    width={60}
  />
  <YAxis
    showLine
    showTickMarks
    axisId="profit"
    requestedTickCount={5}
    tickLabelFormatter={(value) => `$${value}k`}
  />
  <BarPlot />
</CartesianChart>
```

#### Series Stacks

You can provide a `stackId` to stack series together.

```jsx live
<AreaChart
  enableScrubbing
  height={{ base: 150, tablet: 200, desktop: 250 }}
  series={[
    {
      id: 'pricesA',
      data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
      stackId: 'prices',
      color: 'var(--color-accentBoldGreen)',
    },
    {
      id: 'pricesB',
      data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
      stackId: 'prices',
      color: 'var(--color-accentBoldPurple)',
    },
  ]}

  showYAxis
  yAxis={{
    showGrid: true,
  }}
>
  <Scrubber />
</LineChart>
```

### Axes

You can configure your x and y axes with the `xAxis` and `yAxis` props. `xAxis` accepts an object while `yAxis` accepts an object or array.

```jsx live
<CartesianChart
  height={{ base: 150, tablet: 200, desktop: 250 }}
  series={[
    {
      id: 'prices',
      data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
    },
  ]}
  xAxis={{
    scaleType: 'band',
  }}
  yAxis={{
    domain: { min: 0 },
  }}
>
  <YAxis showLine showTickMarks showGrid />
  <XAxis showLine showTickMarks />
  <BarPlot />
</CartesianChart>
```

For more info, learn about [XAxis](/components/graphs/XAxis/#axis-config) and [YAxis](/components/graphs/YAxis/#axis-config) configuration.

### Inset

You can adjust the inset around the entire chart (outside the axes) with the `inset` prop. This is useful for when you want to have components that are outside of the drawing area of the data but still within the chart svg.

You can also remove the default inset, such as to have a compact line chart.

```jsx live
function Insets() {
  const data = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];

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

  return (
    <HStack gap={2}>
      <VStack gap={1}>
        <Text font="label1">No inset</Text>
        <LineChart
          height={100}
          inset={0}
          series={[
            {
              id: 'prices',
              data,
            },
          ]}
          yAxis={{ domainLimit: 'strict' }}

          showArea
          style={{ border: '2px solid var(--color-fgPrimary)' }}
        />
      </VStack>
      <VStack gap={1}>
        <Text font="label1">Custom inset</Text>
        <LineChart
          enableScrubbing
          height={100}
          inset={{ left: 10, top: 16, right: 10, bottom: 10 }}
          series={[
            {
              id: 'prices',
              data,
            },
          ]}
          yAxis={{ domainLimit: 'strict' }}

          showArea
          style={{ border: '2px solid var(--color-fgPrimary)' }}
        >
          <Scrubber label={formatPrice} />
        </LineChart>
      </VStack>
      <VStack gap={1}>
        <Text font="label1">Default inset</Text>
        <LineChart
          enableScrubbing
          height={100}
          series={[
            {
              id: 'prices',
              data,
            },
          ]}
          yAxis={{ domainLimit: 'strict' }}

          showArea
          style={{ border: '2px solid var(--color-fgPrimary)' }}
        >
          <Scrubber label={formatPrice} />
        </LineChart>
      </VStack>
    </HStack>
  );
}
```

### Scrubbing

CartesianChart has built-in scrubbing functionality that can be enabled with the `enableScrubbing` prop. This will then enable the usage of `onScrubberPositionChange` to get the current position of the scrubber as the user interacts with the chart.

```jsx live
function Scrubbing() {
  const [scrubIndex, setScrubIndex] = useState(undefined);

  const onScrubberPositionChange = useCallback((index: number | undefined) => {
    setScrubIndex(index);
  }, []);

  return (
    <VStack gap={2}>
    <Text font="label1">Scrubber index: {scrubIndex ?? 'none'}</Text>
      <LineChart
        enableScrubbing
        onScrubberPositionChange={onScrubberPositionChange}
        height={{ base: 150, tablet: 200, desktop: 250 }}
        series={[
          {
            id: 'prices',
            data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
          },
        ]}

        showYAxis
        showArea
        yAxis={{
          showGrid: true,
          width: 32
        }}
        inset={{ right: 0 }}
      >
        <Scrubber />
      </LineChart>
    </VStack>
  );
}
```

### Customization

#### Price with Volume

You can showcase the price and volume of an asset over time within one chart.

```jsx live
function PriceWithVolume() {
  const [scrubIndex, setScrubIndex] = useState(null);
  const btcData = btcCandles
    .slice(0, 180)
    .reverse()

  const btcPrices = btcData.map((candle) => parseFloat(candle.close));
  const btcVolumes = btcData.map((candle) => parseFloat(candle.volume));
  const btcDates = btcData.map((candle) => new Date(parseInt(candle.start) * 1000));

  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: 2,
    })}k`;
  }, []);

  const formatVolume = useCallback((volume: number) => {
    return `${(volume / 1000).toFixed(2)}K`;
  }, []);

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

  const displayIndex = scrubIndex ?? btcPrices.length - 1;
  const currentPrice = btcPrices[displayIndex];
  const currentVolume = btcVolumes[displayIndex];
  const currentDate = btcDates[displayIndex];
  const priceChange = displayIndex > 0
    ? ((currentPrice - btcPrices[displayIndex - 1]) / btcPrices[displayIndex - 1])
    : 0;

  const accessibilityLabel = useMemo(() => {
    if (scrubIndex === null) return `Current Bitcoin price: ${formatPrice(currentPrice)}, Volume: ${formatVolume(currentVolume)}`;
    return `Bitcoin price at ${formatDate(currentDate)}: ${formatPrice(currentPrice)}, Volume: ${formatVolume(currentVolume)}`;
  }, [scrubIndex, currentPrice, currentVolume, currentDate, formatPrice, formatVolume, formatDate]);

  const ThinSolidLine = memo((props: SolidLineProps) => <SolidLine {...props} strokeWidth={1} />);

  const headerId = useId();

  return (
    <VStack gap={2}>
     <SectionHeader
        id={headerId}
        style={{ padding: 0 }}
        title={<Text font="title1">Bitcoin</Text>}
        balance={<Text font="title2">{formatPrice(currentPrice)}</Text>}
        end={
          <HStack gap={2}>
            <VStack justifyContent="center" alignItems="flex-end">
              <Text font="label1">{formatDate(currentDate)}</Text>
              <Text font="label2">{formatVolume(currentVolume)}</Text>
            </VStack>
            <VStack justifyContent="center">
              <RemoteImage source={assets.btc.imageUrl} size="xl" shape="circle" />
            </VStack>
          </HStack>
        }
      />
      <CartesianChart
        enableScrubbing
        onScrubberPositionChange={setScrubIndex}
        height={250}
        series={[
          {
            id: 'prices',
            data: btcPrices,
            color: assets.btc.color,
            yAxisId: 'price',
          },
          {
            id: 'volume',
            data: btcVolumes,
            color: 'var(--color-fgMuted)',
            yAxisId: 'volume',
          },
        ]}
        style={{ outlineColor: assets.btc.color }}
        xAxis={{ scaleType: 'band', range: ({ min, max }) => ({ min, max: max - 16 }) }}
        yAxis={[
          {
            id: 'price',
            domain: ({ min, max }) => ({ min: min * 0.9, max }),
          },
          {
            id: 'volume',
            range: ({ min, max }) => ({ min: max - 32, max }),
          },
        ]}
        accessibilityLabel={accessibilityLabel}
        aria-labelledby={headerId}
        inset={{ top: 8, left: 8, right: 0, bottom: 0 }}
      >
        <YAxis axisId="price" showGrid tickLabelFormatter={formatPriceInThousands} width={48} GridLineComponent={ThinSolidLine} />
        <BarPlot seriesIds={['volume']} />
        <Line seriesId="prices"  showArea />
        <Scrubber seriesIds={['prices']} />
      </CartesianChart>
    </VStack>
  );
}
```

#### Earnings History

You can also create your own type of cartesian chart by using `getSeriesData`, `getXScale`, and `getYScale` directly.

```jsx live
function EarningsHistory() {
  const CirclePlot = memo(({ seriesId, opacity = 1 }: { seriesId: string, opacity?: number }) => {
    const { drawingArea, getSeries, getSeriesData, getXScale, getYScale } = useCartesianChartContext();
    const series = getSeries(seriesId);
    const data = getSeriesData(seriesId);
    const xScale = getXScale();
    const yScale = getYScale(series?.yAxisId);

    if (!xScale || !yScale || !data || !isCategoricalScale(xScale)) return null;

    const yScaleSize = Math.abs(yScale.range()[1] - yScale.range()[0]);

    // Have circle diameter be the smaller of the x scale bandwidth or 10% of the y space available
    const diameter = Math.min(xScale.bandwidth(), yScaleSize / 10);

    return (
      <g>
        {data.map((value, index) => {
          if (value === null || value === undefined) return null;

          // Get x position from band scale - center of the band
          const xPos = xScale(index);
          if (xPos === undefined) return null;

          const centerX = xPos + xScale.bandwidth() / 2;

          // Get y position from value
          const yValue = Array.isArray(value) ? value[1] : value;
          const centerY = yScale(yValue);
          if (centerY === undefined) return null;

          return (
            <circle
              key={`${seriesId}-${index}`}
              cx={centerX}
              cy={centerY}
              fill={series?.color || 'var(--color-fgPrimary)'}
              opacity={opacity}
              r={diameter / 2}
            />
          );
        })}
      </g>
    );
  });

  const quarters = useMemo(() => ['Q1', 'Q2', 'Q3', 'Q4'], []);
  const estimatedEPS = useMemo(() => [1.71, 1.82, 1.93, 2.34], []);
  const actualEPS = useMemo(() => [1.68, 1.83, 2.01, 2.24], []);

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

  const surprisePercentage = useCallback(
    (index: number): ChartTextChildren => {
      const percentage = (actualEPS[index] - estimatedEPS[index]) / estimatedEPS[index];
      const percentageString = percentage.toLocaleString('en-US', {
        style: 'percent',
        minimumFractionDigits: 2,
        maximumFractionDigits: 2,
      });

      return (
        <tspan
          style={{
            fill: percentage > 0 ? 'var(--color-fgPositive)' : 'var(--color-fgNegative)',
            fontWeight: 'bold',
          }}
        >
          {percentage > 0 ? '+' : ''}
          {percentageString}
        </tspan>
      );
    },
    [actualEPS, estimatedEPS],
  );

  const LegendItem = memo(({ opacity = 1, label }: { opacity?: number, label: string }) => {
    return (
      <Box alignItems="center" gap={0.5}>
        <LegendDot opacity={opacity} />
        <Text font="label2">{label}</Text>
      </Box>
    );
  });

  const LegendDot = memo((props: BoxBaseProps) => {
    return <Box borderRadius={1000} width={10} height={10} background="bgPositive" {...props} />;
  });

  return (
    <VStack gap={0.5}>
      <CartesianChart
        animate={false}
        height={250}
        padding={0}
        series={[
          {
            id: 'estimatedEPS',
            data: estimatedEPS,
            color: 'var(--color-bgPositive)',
          },
          { id: 'actualEPS', data: actualEPS, color: 'var(--color-bgPositive)' },
        ]}
        xAxis={{ scaleType: 'band', categoryPadding: 0.25 }}
      >
        <YAxis
          showGrid
          position="left"
          requestedTickCount={3}
          tickLabelFormatter={formatEarningAmount}
        />
        <XAxis height={20} tickLabelFormatter={(index) => quarters[index]} />
        <XAxis height={20} tickLabelFormatter={surprisePercentage} />
        <CirclePlot opacity={0.5} seriesId="estimatedEPS" />
        <CirclePlot seriesId="actualEPS" />
      </CartesianChart>
      <HStack justifyContent="flex-end" gap={2}>
        <LegendItem opacity={0.5} label="Estimated EPS" />
        <LegendItem label="Actual EPS" />
      </HStack>
    </VStack>
  );
}
```

#### Trading Trends

You can have multiple axes with different domains and ranges to showcase different pieces of data over the time time period.

```jsx live
function TradingTrends() {
  const profitData = [34, 24, 28, -4, 8, -16, -3, 12, 24, 18, 20, 28];
  const gains = profitData.map((value) => (value > 0 ? value : 0));
  const losses = profitData.map((value) => (value < 0 ? value : 0));

  const renderProfit = useCallback((value: number) => {
    return `$${value}M`;
  }, []);

  const ThinSolidLine = memo((props: SolidLineProps) => <SolidLine {...props} strokeWidth={1} strokeLinecap="butt" />);
  const ThickSolidLine = memo((props: SolidLineProps) => <SolidLine {...props} strokeWidth={2} strokeLinecap="butt" />);

  return (
    <CartesianChart
      height={250}
      series={[
        { id: 'gains', data: gains, yAxisId: 'profit', color: 'var(--color-bgPositive)', stackId: 'bars' },
        { id: 'losses', data: losses, yAxisId: 'profit', color: 'var(--color-bgNegative)', stackId: 'bars' },
        {
          id: 'revenue',
          data: [128, 118, 122, 116, 120, 114, 118, 122, 126, 130, 134, 138],
          yAxisId: 'revenue',
          color: 'var(--color-fgMuted)',
        },
      ]}
      xAxis={{ scaleType: 'band', data: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] }}
      yAxis={[
        { id: 'profit', range: ({ min, max }) => ({ min: min, max: max - 64 }), domain: { min: -40, max: 40 } },
        { id: 'revenue', range: ({ min, max }) => ({ min: max - 64, max }), domain: { min: 100 } },
      ]}
    >
      <YAxis axisId="profit" position="left" showGrid tickLabelFormatter={renderProfit} GridLineComponent={ThinSolidLine} />
      <XAxis />
      <ReferenceLine LineComponent={ThickSolidLine} dataY={0} yAxisId="profit" stroke="rgb(var(--gray15))" />
      <BarPlot seriesIds={['gains', 'losses']} />
      <Line seriesId="revenue"  showArea />
    </CartesianChart>
  );
}
```

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `alignContent` | `ResponsiveProp<center \| normal \| start \| end \| flex-start \| flex-end \| space-between \| space-around \| space-evenly \| stretch \| baseline \| first baseline \| last baseline>` | No | `-` | - |
| `alignItems` | `ResponsiveProp<center \| normal \| start \| end \| flex-start \| flex-end \| stretch \| baseline \| first baseline \| last baseline \| self-start \| self-end>` | No | `-` | - |
| `alignSelf` | `ResponsiveProp<center \| auto \| normal \| start \| end \| flex-start \| flex-end \| stretch \| baseline \| first baseline \| last baseline \| self-start \| self-end>` | No | `-` | - |
| `animate` | `boolean` | No | `true` | Whether to animate the chart. |
| `as` | `div` | No | `-` | - |
| `aspectRatio` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
| `background` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `-` | - |
| `borderBottomLeftRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
| `borderBottomRightRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
| `borderBottomWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - |
| `borderColor` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `-` | - |
| `borderEndWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - |
| `borderRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
| `borderStartWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - |
| `borderTopLeftRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
| `borderTopRightRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
| `borderTopWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - |
| `borderWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - |
| `bordered` | `boolean` | No | `-` | Add a border around all sides of the box. |
| `borderedBottom` | `boolean` | No | `-` | Add a border to the bottom side of the box. |
| `borderedEnd` | `boolean` | No | `-` | Add a border to the trailing side of the box. |
| `borderedHorizontal` | `boolean` | No | `-` | Add a border to the leading and trailing sides of the box. |
| `borderedStart` | `boolean` | No | `-` | Add a border to the leading side of the box. |
| `borderedTop` | `boolean` | No | `-` | Add a border to the top side of the box. |
| `borderedVertical` | `boolean` | No | `-` | Add a border to the top and bottom sides of the box. |
| `bottom` | `ResponsiveProp<Bottom<string \| number>>` | No | `-` | - |
| `classNames` | `{ root?: string; chart?: string \| undefined; } \| undefined` | No | `-` | Custom class names for the component. |
| `color` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `-` | - |
| `columnGap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
| `dangerouslySetBackground` | `string` | No | `-` | - |
| `display` | `ResponsiveProp<grid \| none \| block \| inline \| inline-block \| flex \| inline-flex \| inline-grid \| contents \| flow-root \| revert \| list-item>` | No | `-` | - |
| `elevation` | `0 \| 1 \| 2` | No | `-` | - |
| `enableScrubbing` | `boolean` | No | `-` | Enables scrubbing interactions. When true, allows scrubbing and makes scrubber components interactive. |
| `flexBasis` | `ResponsiveProp<FlexBasis<string \| number>>` | No | `-` | - |
| `flexDirection` | `ResponsiveProp<row \| row-reverse \| column \| column-reverse>` | No | `-` | - |
| `flexGrow` | `inherit \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
| `flexShrink` | `inherit \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
| `flexWrap` | `ResponsiveProp<nowrap \| wrap \| wrap-reverse>` | No | `-` | - |
| `font` | `ResponsiveProp<FontFamily \| inherit>` | No | `-` | - |
| `fontFamily` | `ResponsiveProp<FontFamily \| inherit>` | No | `-` | - |
| `fontSize` | `ResponsiveProp<inherit \| FontSize>` | No | `-` | - |
| `fontWeight` | `ResponsiveProp<inherit \| FontWeight>` | No | `-` | - |
| `gap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
| `grid` | `inherit \| none \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
| `gridArea` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
| `gridAutoColumns` | `ResponsiveProp<GridAutoColumns<string \| number>>` | No | `-` | - |
| `gridAutoFlow` | `inherit \| revert \| row \| column \| -moz-initial \| initial \| revert-layer \| unset \| dense` | No | `-` | - |
| `gridAutoRows` | `ResponsiveProp<GridAutoRows<string \| number>>` | No | `-` | - |
| `gridColumn` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
| `gridColumnEnd` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
| `gridColumnStart` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
| `gridRow` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
| `gridRowEnd` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
| `gridRowStart` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
| `gridTemplate` | `inherit \| none \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
| `gridTemplateAreas` | `inherit \| none \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
| `gridTemplateColumns` | `ResponsiveProp<GridTemplateColumns<string \| number>>` | No | `-` | - |
| `gridTemplateRows` | `ResponsiveProp<GridTemplateRows<string \| number>>` | No | `-` | - |
| `height` | `ResponsiveProp<Height<string \| number>>` | No | `-` | - |
| `inset` | `number \| Partial<ChartInset>` | No | `-` | Inset around the entire chart (outside the axes). |
| `justifyContent` | `ResponsiveProp<left \| right \| center \| normal \| start \| end \| flex-start \| flex-end \| space-between \| space-around \| space-evenly \| stretch>` | No | `-` | - |
| `key` | `Key \| null` | No | `-` | - |
| `left` | `ResponsiveProp<Left<string \| number>>` | No | `-` | - |
| `lineHeight` | `ResponsiveProp<inherit \| LineHeight>` | No | `-` | - |
| `margin` | `ResponsiveProp<0 \| -1 \| -2 \| -0.25 \| -0.5 \| -0.75 \| -1.5 \| -3 \| -4 \| -5 \| -6 \| -7 \| -8 \| -9 \| -10>` | No | `-` | - |
| `marginBottom` | `ResponsiveProp<0 \| -1 \| -2 \| -0.25 \| -0.5 \| -0.75 \| -1.5 \| -3 \| -4 \| -5 \| -6 \| -7 \| -8 \| -9 \| -10>` | No | `-` | - |
| `marginEnd` | `ResponsiveProp<0 \| -1 \| -2 \| -0.25 \| -0.5 \| -0.75 \| -1.5 \| -3 \| -4 \| -5 \| -6 \| -7 \| -8 \| -9 \| -10>` | No | `-` | - |
| `marginStart` | `ResponsiveProp<0 \| -1 \| -2 \| -0.25 \| -0.5 \| -0.75 \| -1.5 \| -3 \| -4 \| -5 \| -6 \| -7 \| -8 \| -9 \| -10>` | No | `-` | - |
| `marginTop` | `ResponsiveProp<0 \| -1 \| -2 \| -0.25 \| -0.5 \| -0.75 \| -1.5 \| -3 \| -4 \| -5 \| -6 \| -7 \| -8 \| -9 \| -10>` | No | `-` | - |
| `marginX` | `ResponsiveProp<0 \| -1 \| -2 \| -0.25 \| -0.5 \| -0.75 \| -1.5 \| -3 \| -4 \| -5 \| -6 \| -7 \| -8 \| -9 \| -10>` | No | `-` | - |
| `marginY` | `ResponsiveProp<0 \| -1 \| -2 \| -0.25 \| -0.5 \| -0.75 \| -1.5 \| -3 \| -4 \| -5 \| -6 \| -7 \| -8 \| -9 \| -10>` | No | `-` | - |
| `maxHeight` | `ResponsiveProp<MaxHeight<string \| number>>` | No | `-` | - |
| `maxWidth` | `ResponsiveProp<MaxWidth<string \| number>>` | No | `-` | - |
| `minHeight` | `ResponsiveProp<MinHeight<string \| number>>` | No | `-` | - |
| `minWidth` | `ResponsiveProp<MinWidth<string \| number>>` | No | `-` | - |
| `onChange` | `FormEventHandler<HTMLDivElement>` | No | `-` | - |
| `onScrubberPositionChange` | `((index: number) => void) \| undefined` | No | `-` | Callback fired when the scrubber position changes. Receives the dataIndex of the scrubber or undefined when not scrubbing. |
| `opacity` | `inherit \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
| `overflow` | `ResponsiveProp<hidden \| auto \| visible \| clip \| scroll>` | No | `-` | - |
| `padding` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
| `paddingBottom` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
| `paddingEnd` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
| `paddingStart` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
| `paddingTop` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
| `paddingX` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
| `paddingY` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
| `pin` | `top \| bottom \| left \| right \| all` | No | `-` | Direction in which to absolutely pin the box. |
| `position` | `ResponsiveProp<static \| relative \| absolute \| fixed \| sticky>` | No | `-` | - |
| `ref` | `((instance: SVGSVGElement \| null) => void) \| RefObject<SVGSVGElement> \| null` | No | `-` | - |
| `right` | `ResponsiveProp<Right<string \| number>>` | No | `-` | - |
| `rowGap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
| `series` | `Series[]` | No | `-` | Configuration objects that define how to visualize the data. Each series contains its own data array. |
| `style` | `CSSProperties` | No | `-` | Custom styles for the root element. |
| `styles` | `{ root?: CSSProperties; chart?: CSSProperties \| undefined; } \| undefined` | No | `-` | Custom styles for the component. |
| `testID` | `string` | No | `-` | Used to locate this element in unit and end-to-end tests. Under the hood, testID translates to data-testid on Web. On Mobile, testID stays the same - testID |
| `textAlign` | `ResponsiveProp<center \| start \| end \| justify>` | No | `-` | - |
| `textDecoration` | `ResponsiveProp<none \| underline \| overline \| line-through \| underline overline \| underline double>` | No | `-` | - |
| `textTransform` | `ResponsiveProp<none \| uppercase \| lowercase \| capitalize>` | No | `-` | - |
| `top` | `ResponsiveProp<Top<string \| number>>` | No | `-` | - |
| `transform` | `inherit \| none \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
| `userSelect` | `ResponsiveProp<text \| none \| all \| auto>` | No | `-` | - |
| `visibility` | `ResponsiveProp<hidden \| visible>` | No | `-` | - |
| `width` | `ResponsiveProp<Width<string \| number>>` | No | `-` | - |
| `xAxis` | `Partial<Omit<AxisConfigProps, id>>` | No | `-` | Configuration for x-axis. |
| `yAxis` | `Partial<Omit<AxisConfigProps, data>> \| Partial<Omit<AxisConfigProps, data>>[]` | No | `-` | Configuration for y-axis(es). Can be a single config or array of configs. |
| `zIndex` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |


