# Polar Area Chart

> A customizable polar area chart component with smooth animations and flexible styling for displaying radial data.

**BNA UI** — a React Native / Expo component library.
These components render through `react-native`, not the DOM: there are no HTML
elements, no Tailwind classes and no Radix primitives. Source is copied into your
project and imported through `@/components/ui/*`, `@/components/charts/*`,
`@/hooks/*` and `@/theme/*`. Colours come from the `useColor` hook rather than
hardcoded hex; sizing tokens (`HEIGHT`, `FONT_SIZE`, `BORDER_RADIUS`, `CORNERS`)
come from `@/theme/globals`.

- Docs: https://ui.ahmedbna.com/docs/charts/polar-area-chart
- Markdown: https://ui.ahmedbna.com/docs/charts/polar-area-chart.md
- Structured JSON (props, usage, source, examples): https://ui.ahmedbna.com/r/ai/polar-area-chart.json
- Install payload (source plus every file it imports): https://ui.ahmedbna.com/r/polar-area-chart.json
- Install: `npx bna-ui add polar-area-chart`
- npm dependencies: `react-native-reanimated`, `react-native-svg`, `react-native-worklets`
- Registry dependencies: `mode-provider`, `useColorScheme`, `colors`, `useColor`, `globals`, `text`
- Preview recording: https://demo.ahmedbna.com/0367-polar-area-chart-demo.MOV

---

**Example:** A polar area chart with smooth animations

```tsx
// components/demo/charts/polar-area-chart/polar-area-chart-demo.tsx
import { PolarAreaChart } from '@/components/charts/polar-area-chart';
import { ChartContainer } from '@/components/charts/chart-container';
import React from 'react';

const sampleData = [
  { label: 'Sales', value: 120 },
  { label: 'Marketing', value: 98 },
  { label: 'Support', value: 86 },
  { label: 'Development', value: 140 },
  { label: 'Design', value: 75 },
  { label: 'HR', value: 65 },
];

export function PolarAreaChartDemo() {
  return (
    <ChartContainer
      title='Department Performance'
      description='Quarterly performance metrics by department'
    >
      <PolarAreaChart
        data={sampleData}
        config={{
          height: 300,
          showLabels: true,
          animated: true,
          duration: 1000,
        }}
      />
    </ChartContainer>
  );
}
```

## Installation

### CLI

```bash
npx bna-ui add polar-area-chart
```

### Manual

**1.** Install the required dependencies.

```bash
npm install react-native-svg react-native-reanimated react-native-worklets
```

**2.** Copy and paste the following code into your project.

```tsx
// components/charts/polar-area-chart.tsx
import { Text } from '@/components/ui/text';
import { useColor } from '@/hooks/useColor';
import React, { useEffect, useState } from 'react';
import { LayoutChangeEvent, View, ViewStyle } from 'react-native';
import Animated, {
  SharedValue,
  useAnimatedProps,
  useSharedValue,
  withTiming,
} from 'react-native-reanimated';
import Svg, { Circle, G, Path, Text as SvgText } from 'react-native-svg';

// Animated SVG Components
const AnimatedPath = Animated.createAnimatedComponent(Path);

type AnimatedSliceProps = {
  d: string;
  fill: string;
  animationProgress: SharedValue<number>;
};

// Per-item hook must live in its own mounted subcomponent, not in the
// parent's .map() body — calling useAnimatedProps per loop iteration
// violates Rules of Hooks the moment data.length changes.
const AnimatedSlice = React.memo(
  ({ d, fill, animationProgress }: AnimatedSliceProps) => {
    const sliceAnimatedProps = useAnimatedProps(() => ({
      opacity: animationProgress.value * 0.8,
    }));

    return (
      <AnimatedPath
        d={d}
        fill={fill}
        stroke='white'
        strokeWidth={1}
        animatedProps={sliceAnimatedProps}
      />
    );
  }
);

interface ChartConfig {
  width?: number;
  height?: number;
  showLabels?: boolean;
  animated?: boolean;
  duration?: number;
}

interface ChartDataPoint {
  label: string;
  value: number;
  color?: string;
}

type Props = {
  data: ChartDataPoint[];
  config?: ChartConfig;
  style?: ViewStyle;
};

export const PolarAreaChart = ({ data, config = {}, style }: Props) => {
  const [containerWidth, setContainerWidth] = useState(300);

  const {
    height = 200,
    showLabels = true,
    animated = true,
    duration = 1000,
  } = config;

  const chartWidth = containerWidth || config.width || 300;

  const primaryColor = useColor('primary');
  const mutedColor = useColor('mutedForeground');

  const animationProgress = useSharedValue(0);

  const handleLayout = (event: LayoutChangeEvent) => {
    const { width: measuredWidth } = event.nativeEvent.layout;
    if (measuredWidth > 0) {
      setContainerWidth(measuredWidth);
    }
  };

  useEffect(() => {
    if (animated) {
      animationProgress.value = withTiming(1, { duration });
    } else {
      animationProgress.value = 1;
    }
  }, [data, animated, duration]);

  if (!data.length) return null;

  const maxValue = Math.max(...data.map((d) => d.value));
  if (maxValue === 0) return null;

  const centerX = chartWidth / 2;
  const centerY = height / 2;
  const maxRadius = Math.min(chartWidth, height) / 2 - 20;

  const angleStep = (2 * Math.PI) / data.length;

  const colors = [
    primaryColor,
    useColor('blue'),
    useColor('green'),
    useColor('orange'),
    useColor('purple'),
    useColor('pink'),
  ];

  return (
    <View
      style={[{ width: '100%', height }, style]}
      onLayout={handleLayout}
      accessibilityRole='image'
      accessibilityLabel={`Polar area chart with ${data.length} segments, maximum value ${Math.round(maxValue)}`}
    >
      <Svg width={chartWidth} height={height}>
        {data.map((item, index) => {
          const angle = index * angleStep - Math.PI / 2;
          const nextAngle = (index + 1) * angleStep - Math.PI / 2;
          const radius = (item.value / maxValue) * maxRadius;

          const x1 = centerX + radius * Math.cos(angle);
          const y1 = centerY + radius * Math.sin(angle);
          const x2 = centerX + radius * Math.cos(nextAngle);
          const y2 = centerY + radius * Math.sin(nextAngle);

          const pathData = [
            `M ${centerX} ${centerY}`,
            `L ${x1} ${y1}`,
            `A ${radius} ${radius} 0 0 1 ${x2} ${y2}`,
            'Z',
          ].join(' ');

          // Label position
          const labelAngle = angle + angleStep / 2;
          const labelRadius = radius * 0.7;
          const labelX = centerX + labelRadius * Math.cos(labelAngle);
          const labelY = centerY + labelRadius * Math.sin(labelAngle);

          return (
            <G key={`slice-${index}`}>
              <AnimatedSlice
                d={pathData}
                fill={item.color || colors[index % colors.length]}
                animationProgress={animationProgress}
              />

              {showLabels && (
                <SvgText
                  x={labelX}
                  y={labelY}
                  textAnchor='middle'
                  fontSize={10}
                  fill='#FFFFFF'
                  fontWeight='600'
                  alignmentBaseline='middle'
                >
                  {item.value}
                </SvgText>
              )}
            </G>
          );
        })}

        {/* Grid circles for reference */}
        {[0.25, 0.5, 0.75, 1].map((ratio, index) => (
          <Circle
            key={`grid-${index}`}
            cx={centerX}
            cy={centerY}
            r={maxRadius * ratio}
            stroke={mutedColor}
            strokeWidth={0.5}
            fill='none'
            opacity={0.2}
          />
        ))}
      </Svg>

      {/* Legend */}
      <View style={{ marginTop: 10 }}>
        {data.map((item, index) => (
          <View
            key={`legend-${index}`}
            style={{
              flexDirection: 'row',
              alignItems: 'center',
              marginBottom: 5,
            }}
          >
            <View
              style={{
                width: 12,
                height: 12,
                borderRadius: 6,
                backgroundColor: item.color || colors[index % colors.length],
                marginRight: 8,
              }}
            />
            <Text variant='caption'>
              {item.label}: {item.value}
            </Text>
          </View>
        ))}
      </View>
    </View>
  );
};
```

**3.** Update the import paths to match your project setup.

## Usage

```tsx
import { PolarAreaChart } from '@/components/charts/polar-area-chart';
```

```tsx
const data = [
  { label: 'Sales', value: 120 },
  { label: 'Marketing', value: 98 },
  { label: 'Support', value: 86 },
  { label: 'Development', value: 140 },
  { label: 'Design', value: 75 },
];

<PolarAreaChart
  data={data}
  config={{
    height: 300,
    showLabels: true,
    animated: true,
  }}
/>;
```

## Examples

#### Basic Polar Area Chart

**Example:** A polar area chart with smooth animations

```tsx
// components/demo/charts/polar-area-chart/polar-area-chart-demo.tsx
import { PolarAreaChart } from '@/components/charts/polar-area-chart';
import { ChartContainer } from '@/components/charts/chart-container';
import React from 'react';

const sampleData = [
  { label: 'Sales', value: 120 },
  { label: 'Marketing', value: 98 },
  { label: 'Support', value: 86 },
  { label: 'Development', value: 140 },
  { label: 'Design', value: 75 },
  { label: 'HR', value: 65 },
];

export function PolarAreaChartDemo() {
  return (
    <ChartContainer
      title='Department Performance'
      description='Quarterly performance metrics by department'
    >
      <PolarAreaChart
        data={sampleData}
        config={{
          height: 300,
          showLabels: true,
          animated: true,
          duration: 1000,
        }}
      />
    </ChartContainer>
  );
}
```

#### Sample Polar Area Chart

**Example:** A sample polar area chart

```tsx
// components/demo/charts/polar-area-chart/polar-area-chart-sample.tsx
import { PolarAreaChart } from '@/components/charts/polar-area-chart';
import { ChartContainer } from '@/components/charts/chart-container';
import { useColor } from '@/hooks/useColor';
import React from 'react';

const skillsData = [
  { label: 'JavaScript', value: 95 },
  { label: 'React', value: 88 },
  { label: 'TypeScript', value: 82 },
  { label: 'Node.js', value: 78 },
  { label: 'Python', value: 65 },
];

export function PolarAreaChartSample() {
  const primaryColor = useColor('primary');
  const blueColor = useColor('blue');
  const greenColor = useColor('green');
  const orangeColor = useColor('orange');
  const purpleColor = useColor('purple');

  const dataWithColors = skillsData.map((item, index) => ({
    ...item,
    color: [primaryColor, blueColor, greenColor, orangeColor, purpleColor][
      index
    ],
  }));

  return (
    <ChartContainer
      title='Skills Assessment'
      description='Technical skills proficiency levels'
    >
      <PolarAreaChart
        data={dataWithColors}
        config={{
          height: 280,
          showLabels: true,
          animated: true,
          duration: 1200,
        }}
      />
    </ChartContainer>
  );
}
```

#### Styled Polar Area Chart

**Example:** A customized polar area chart with custom colors and styling

```tsx
// components/demo/charts/polar-area-chart/polar-area-chart-styled.tsx
import { PolarAreaChart } from '@/components/charts/polar-area-chart';
import { ChartContainer } from '@/components/charts/chart-container';
import { useColor } from '@/hooks/useColor';
import React from 'react';

const marketData = [
  { label: 'Mobile Apps', value: 45, color: '#FF6B6B' },
  { label: 'Web Apps', value: 38, color: '#4ECDC4' },
  { label: 'Desktop', value: 25, color: '#45B7D1' },
  { label: 'IoT', value: 18, color: '#96CEB4' },
  { label: 'AI/ML', value: 32, color: '#FFEAA7' },
  { label: 'Blockchain', value: 15, color: '#DDA0DD' },
];

export function PolarAreaChartStyled() {
  return (
    <ChartContainer
      title='Market Share Analysis'
      description='Technology sector market distribution'
    >
      <PolarAreaChart
        data={marketData}
        config={{
          height: 320,
          showLabels: true,
          animated: true,
          duration: 1500,
        }}
        style={{
          backgroundColor: 'rgba(0, 0, 0, 0.02)',
          borderRadius: 12,
          padding: 16,
        }}
      />
    </ChartContainer>
  );
}
```

#### Large Polar Area Chart

**Example:** A polar area chart with large dataset

```tsx
// components/demo/charts/polar-area-chart/polar-area-chart-large.tsx
import { PolarAreaChart } from '@/components/charts/polar-area-chart';
import { ChartContainer } from '@/components/charts/chart-container';
import React from 'react';

const largeDataset = [
  { label: 'Q1 Sales', value: 185 },
  { label: 'Q2 Sales', value: 220 },
  { label: 'Q3 Sales', value: 195 },
  { label: 'Q4 Sales', value: 240 },
  { label: 'Marketing', value: 156 },
  { label: 'Support', value: 134 },
  { label: 'Development', value: 189 },
  { label: 'Design', value: 123 },
  { label: 'HR', value: 98 },
  { label: 'Finance', value: 145 },
  { label: 'Operations', value: 167 },
  { label: 'Research', value: 112 },
];

export function PolarAreaChartLarge() {
  return (
    <ChartContainer
      title='Annual Performance Overview'
      description='Comprehensive performance metrics across all departments and quarters'
    >
      <PolarAreaChart
        data={largeDataset}
        config={{
          height: 400,
          showLabels: true,
          animated: true,
          duration: 2000,
        }}
      />
    </ChartContainer>
  );
}
```

## API Reference

### PolarAreaChart

A customizable polar area chart component with smooth animations and flexible styling. Perfect for displaying multivariate data in a radial format, where each segment represents a different category with varying magnitudes.

| Prop     | Type               | Default | Description                                   |
| -------- | ------------------ | ------- | --------------------------------------------- |
| `data`   | `ChartDataPoint[]` | -       | Array of data points to display on the chart. |
| `config` | `ChartConfig`      | `{}`    | Configuration object for chart appearance.    |
| `style`  | `ViewStyle`        | -       | Additional styles to apply to the chart.      |

### ChartDataPoint

| Prop    | Type     | Description                            |
| ------- | -------- | -------------------------------------- |
| `label` | `string` | The label for the data point.          |
| `value` | `number` | The value for the data point.          |
| `color` | `string` | Optional custom color for the segment. |

### ChartConfig

| Prop         | Type      | Default | Description                                       |
| ------------ | --------- | ------- | ------------------------------------------------- |
| `width`      | `number`  | -       | Fixed width of the chart (auto-sizes if omitted). |
| `height`     | `number`  | `200`   | Height of the chart.                              |
| `showLabels` | `boolean` | `true`  | Whether to show value labels on segments.         |
| `animated`   | `boolean` | `true`  | Whether to animate the chart on load.             |
| `duration`   | `number`  | `1000`  | Animation duration in milliseconds.               |

## Features

- **Radial Layout**: Displays data segments in a circular pattern radiating from center
- **Smooth Animations**: Built-in animations using React Native Reanimated
- **Responsive Design**: Automatically adapts to container width
- **Custom Colors**: Support for individual segment colors
- **Value Labels**: Shows values directly on chart segments
- **Grid Lines**: Concentric circles provide visual reference for magnitude
- **Theme Integration**: Uses theme colors for consistent styling
- **Interactive Legend**: Color-coded legend with labels and values

## Use Cases

Polar area charts are particularly effective for:

- **Performance Metrics**: Displaying multi-dimensional performance data
- **Survey Results**: Showing ratings across different categories
- **Skills Assessment**: Visualizing competency levels across various skills
- **Budget Allocation**: Showing spending distribution across departments
- **Quality Metrics**: Displaying quality scores across different criteria
- **Market Analysis**: Comparing market share or performance across segments

## Design Considerations

The polar area chart design makes it ideal for:

- **Comparative Analysis**: Easy visual comparison of magnitudes across categories
- **Radial Data**: Natural representation of data that radiates from a central point
- **Equal Categories**: All categories get equal angular space regardless of value
- **Magnitude Emphasis**: Radius represents value magnitude, making differences clear
- **Compact Display**: Efficient use of space for multivariate data

## Accessibility

The PolarAreaChart component includes several accessibility features:

- Semantic SVG structure for screen readers
- High contrast white text on colored segments
- Descriptive legend with clear labels and values
- Proper color contrast ratios
- Text labels for both categories and values
- Supports dynamic text sizing

## Performance

The component is optimized for performance:

- Uses React Native Reanimated for smooth 60fps animations
- Efficient SVG rendering with minimal re-renders
- Automatic cleanup of animation values
- Responsive layout calculations
- Optimized path calculations for segments

## Styling

The component integrates with your theme system:

- Uses theme colors (primary, blue, green, orange, purple, pink) for segments
- Uses `mutedForeground` color for grid lines and legend text
- Supports custom colors per data point
- Semi-transparent segments (80% opacity) for visual appeal
- White stroke borders for segment separation

## Animation

The chart features smooth entry animations:

- Segments animate from 0 opacity to full opacity
- Configurable animation duration (default 1000ms)
- Can be disabled for instant rendering
- Uses React Native Reanimated for optimal performance
- Smooth transitions when data changes

## Mathematical Implementation

The chart uses polar coordinates for accurate segment positioning:

- Each segment occupies equal angular space (360° / number of segments)
- Radius is proportional to value magnitude
- Segments are drawn as SVG paths using arc commands
- Grid circles provide visual reference at 25%, 50%, 75%, and 100% of max radius
- Labels are positioned at 70% of segment radius for optimal readability

## Comparison with Other Chart Types

**Polar Area Chart vs Pie Chart:**

- Polar area: Equal angles, varying radius (emphasizes magnitude)
- Pie chart: Varying angles, equal radius (emphasizes proportion)

**Polar Area Chart vs Radar Chart:**

- Polar area: Filled segments, individual values
- Radar chart: Connected lines, relationship between values

**Polar Area Chart vs Bar Chart:**

- Polar area: Radial layout, compact display
- Bar chart: Linear layout, easier value comparison
