Area Chart

Build stacked area charts with theme-aware SVG gradients.
alpha

Area charts emphasize magnitude as well as direction. They work best for totals, volume, and cumulative series where the filled region has meaning.

Stacked gradient ​

Use VisArea inside ChartAreaGradient when each stacked series needs a gradient derived from the ChartRoot palette. The slot exposes svgDefs for the Unovis container and a matching color accessor for the area.

Visitors
Signups
vue
<script setup lang="ts">
import { ref } from 'vue'
import { VisArea, VisAxis, VisLine, VisXYContainer } from '@unovis/vue'
import {
  ChartAreaGradient,
  ChartCrosshair,
  ChartLegend,
  ChartRoot,
  ChartTooltip,
  type ChartConfig,
} from '@typlog/ui/charts'

interface Point {
  month: string
  visitors: number
  signups: number
}

const data = ref<Point[]>([
  { month: 'Jan', visitors: 320, signups: 120 },
  { month: 'Feb', visitors: 410, signups: 160 },
  { month: 'Mar', visitors: 380, signups: 145 },
  { month: 'Apr', visitors: 520, signups: 210 },
  { month: 'May', visitors: 610, signups: 260 },
  { month: 'Jun', visitors: 720, signups: 305 },
])
const config = {
  month: { label: 'Month', role: 'x' },
  visitors: { label: 'Visitors' },
  signups: { label: 'Signups' },
} satisfies ChartConfig
const x = (_d: Point, index: number) => index
const yArea = [(d: Point) => d.visitors, (d: Point) => d.signups]
const yLine = [(d: Point) => d.visitors, (d: Point) => d.visitors + d.signups]
const xTickFormat = (value: number) => data.value[value]?.month ?? ''
</script>

<template>
  <ChartRoot v-slot="{ mounted }" :config="config">
    <ChartAreaGradient v-slot="{ color, svgDefs }">
      <VisXYContainer v-if="mounted" :data="data" :height="260" :svg-defs="svgDefs">
        <VisArea :x="x" :y="yArea" :color="color" :opacity="0.4" />
        <VisLine :x="x" :y="yLine" :line-width="1" />
        <VisAxis
          type="x"
          :tick-format="xTickFormat"
          :tick-values="data.map((_item, index) => index)"
          :tick-line="false"
          :domain-line="false"
          :grid-line="false"
        />
        <VisAxis
          type="y"
          :tick-line="false"
          :domain-line="false"
          :grid-line="true"
        />
        <ChartTooltip />
        <ChartCrosshair />
      </VisXYContainer>
    </ChartAreaGradient>
    <ChartLegend class="mt-3" align="center" />
  </ChartRoot>
</template>

Pass svgDefs to VisXYContainer; Unovis inserts the definitions into its internal SVG so area paths can resolve their gradient URLs. Keep the series order in VisArea.y aligned with ChartRoot.config.

Single series ​

A single gradient area is useful for request volume, traffic, or storage over time. Enabling the area line preserves a crisp boundary while the fill communicates magnitude.

vue
<script setup lang="ts">
import { VisArea, VisAxis, VisXYContainer } from '@unovis/vue'
import {
  ChartAreaGradient,
  ChartCrosshair,
  ChartRoot,
  ChartTooltip,
  type ChartConfig,
} from '@typlog/ui/charts'

interface Point {
  hour: string
  requests: number
}

const data: Point[] = [
  { hour: '00:00', requests: 18 },
  { hour: '04:00', requests: 12 },
  { hour: '08:00', requests: 38 },
  { hour: '12:00', requests: 64 },
  { hour: '16:00', requests: 52 },
  { hour: '20:00', requests: 31 },
]
const config = {
  hour: { label: 'Time', role: 'x' },
  requests: { label: 'Requests', color: 'var(--violet-9)' },
} satisfies ChartConfig
const x = (_item: Point, index: number) => index
const y = (item: Point) => item.requests
const xTickFormat = (value: number) => data[value]?.hour ?? ''
</script>

<template>
  <ChartRoot v-slot="{ mounted }" :config="config">
    <ChartAreaGradient v-slot="{ color, svgDefs }">
      <VisXYContainer v-if="mounted" :data="data" :height="220" :svg-defs="svgDefs">
        <VisArea
          :x="x"
          :y="y"
          :color="color"
          :opacity="0.4"
          :line="true"
          :line-width="1"
        />
        <VisAxis
          type="x"
          :tick-format="xTickFormat"
          :tick-values="data.map((_item, index) => index)"
          :tick-line="false"
          :domain-line="false"
          :grid-line="false"
        />
        <VisAxis type="y" :tick-format="() => ''" :tick-line="false" :domain-line="false" />
        <ChartTooltip />
        <ChartCrosshair />
      </VisXYContainer>
    </ChartAreaGradient>
  </ChartRoot>
</template>

Solid fill ​

Gradients are optional. Use palette colors directly for a quieter stacked area chart, and lower the opacity so overlapping grid lines and boundaries stay legible.

New users
Returning users
vue
<script setup lang="ts">
import { VisArea, VisAxis, VisXYContainer } from '@unovis/vue'
import {
  ChartCrosshair,
  ChartLegend,
  ChartRoot,
  ChartTooltip,
  type ChartConfig,
} from '@typlog/ui/charts'

interface Point {
  quarter: string
  newUsers: number
  returningUsers: number
}

const data: Point[] = [
  { quarter: 'Q1', newUsers: 36, returningUsers: 22 },
  { quarter: 'Q2', newUsers: 48, returningUsers: 29 },
  { quarter: 'Q3', newUsers: 55, returningUsers: 37 },
  { quarter: 'Q4', newUsers: 68, returningUsers: 43 },
]
const config = {
  quarter: { label: 'Quarter', role: 'x' },
  newUsers: { label: 'New users' },
  returningUsers: { label: 'Returning users' },
} satisfies ChartConfig
const x = (_item: Point, index: number) => index
const y = [(item: Point) => item.newUsers, (item: Point) => item.returningUsers]
const xTickFormat = (value: number) => data[value]?.quarter ?? ''
</script>

<template>
  <ChartRoot v-slot="{ mounted }" :config="config">
    <VisXYContainer v-if="mounted" :data="data" :height="220">
      <VisArea
        :x="x"
        :y="y"
        :opacity="0.22"
        :line="true"
        :line-width="2"
      />
      <VisAxis
        type="x"
        :tick-format="xTickFormat"
        :tick-values="data.map((_item, index) => index)"
        :tick-line="false"
        :domain-line="false"
        :grid-line="false"
      />
      <VisAxis type="y" :tick-format="() => ''" :tick-line="false" :domain-line="false" />
      <ChartTooltip />
      <ChartCrosshair />
    </VisXYContainer>
    <ChartLegend class="mt-3" align="center" />
  </ChartRoot>
</template>

Stacked areas communicate totals and composition, not precise comparison between individual series. Prefer grouped bars when readers need to compare exact values across categories.

Last updated Sep 15, 2026