Pricing

CircularChart handbook

v1.4.5 · @snowui-design-system/components

License: Before reading component documentation, source, or npm usage instructions for the first time, read the full LICENSE. Reuse that review for the same task and version. Public API configuration and internal implementation changes have different license implications. All components use the unified runtime package; source rights remain governed by the license.

Overview

Show proportions in a ring or pie chart, with segments, rounded corners, ring thickness, and hover emphasis for composition analysis.

When to use it

Use CircularChart for proportions in a ring or pie. Choose GaugeChart or Progress for completion metrics according to their semantics. Configure colors and labels through public props.

  • Data uses name and finite positive value; only the previous maxItems valid items are displayed, and the score is the sum of these display items. The explicit empty array shows the empty picture.

  • It is recommended to use the number size; string geometry only takes the numerical part, and cannot rely on percentage or CSS variables to complete the adaptation. The central content uses the public center-label / segment-value / segment-name role.

  • The default root accessible name follows LocaleProvider through circularChart.name; an explicit aria-label takes priority. Business names, data values, and numeric formatting remain controlled by the caller.

Quick start

This example shows the recommended basic composition. See the sections below for more cases and composition limits.

<script lang="ts">	import { Button, Frame, Icon, Text } from '@snowui-design-system/components';	import { DotCircle } from '@snowui-design-system/resource-svelte';	import { CircularChart } from '@snowui-design-system/components';	// Illustrative SnowUI site visits over the last 30 days; these are not actual statistics.	const channels = [	    { name: 'Search', value: 12840, color: 'var(--primary)' },	    { name: 'Direct', value: 8560, color: 'var(--secondary-purple)' },	    { name: 'GitHub', value: 5350, color: 'var(--secondary-cyan)' },	    { name: 'Figma', value: 4280, color: 'var(--secondary-mint)' },	    { name: 'Community', value: 3210, color: 'var(--secondary-green)' }	];	const total = channels.reduce((sum, item) => sum + item.value, 0);	let trafficChart: { replay: () => void; reverseAppearance: () => void } | undefined;</script> <div style="display: flex; flex-direction: column; align-items: center; width: var(--size-full); gap: var(--gap-4) var(--gap-3);">    <div style="display: flex; align-items: center; justify-content: center; flex-wrap: wrap; min-width: 0; max-width: var(--size-full); gap: var(--gap-4) var(--gap-7);">        <CircularChart bind:this={trafficChart} data={channels} ringThickness={48} appearOnView defaultAppearanceProgress={0} role="group" aria-label="SnowUI website visits by source, sample last 30 days" />        <div style="display: flex; flex-direction: column; gap: var(--gap-3);">            <div style="display: flex; flex-direction: column; gap: var(--gap-0);">                <Text style="font-size: var(--text-18); line-height: var(--leading-18); color: var(--black-100); font-weight: 600;">Website traffic</Text>                <Text style="font-size: var(--text-12); line-height: var(--leading-12); color: var(--black-40);">Sample · last 30 days · {total.toLocaleString('en-US')} visits</Text>            </div>            <div style="display: grid; grid-template-columns: auto auto; align-items: center; gap: var(--gap-2) var(--gap-6);">    {#each channels as item}        <Frame singleLine gap={0}><Icon as="leading" size={12} style={`color: ${item.color};`}><DotCircle weight="fill" /></Icon><Text lines={1} style="font-size: var(--text-14); line-height: var(--leading-14); color: var(--black-100);">{item.name}</Text></Frame>        <Text style="font-size: var(--text-14); line-height: var(--leading-14); color: var(--black-100); text-align: right; font-variant-numeric: tabular-nums;">{item.value.toLocaleString('en-US')}</Text>    {/each}</div>        </div>    </div>    <div style="display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: var(--gap-4) var(--gap-3);">        <Button onclick={() => trafficChart?.replay()}>Replay animation</Button>        <Button onclick={() => trafficChart?.reverseAppearance()}>Reverse animation</Button>    </div></div>

API

Keyword Type Default Description
data CircularChartItem[] DEFAULT_DATA Proportional data array; each item must be filled in name and value, and color and percentFormat are optional. Filter non-positive numbers and illegal values in the input order, and then take the previous maxItems item; the percentage denominator is the sum of the actual display items. Use the built-in sample data when omitting data; keep the final size and render the empty chart when the explicit empty array or all are invalid. Default behavior: Built-in 5 sample data.
size number | string 240 The square size of the chart; it is recommended to pass the positive number, and the number is converted to rem according to the px design value. The string is directly used as the CSS length, but SVG geometry only takes the number through parseFloat and does not perform unit conversion, so it does not support reliable percentage, calc() or CSS variable adaptive geometry.
minWidth number | string `${SECTOR_CHART_MIN_WIDTH}px` Responsive minimum display width; numbers and size use the same px design value to rem rules, and strings are directly used as CSS length. The default is fixed at 40 CSS px; setting the same as size can prevent automatic shrinking, and setting a smaller value allows shrinking to the lower limit. It only takes effect when enableResponsive=true.
ringThickness number | string 32 Ring thickness, recommended non-negative number (px design value); inner radius is max(0, outer radius - ring thickness). The outer radius is size / 2 − hoverThicknessGrowth − 1, and a solid pie chart is formed when the ring thickness reaches it. Strings only extract values and do not convert CSS units.
cornerRadius number | string | "auto" "auto" The corner of the end of the sector; non-negative values or digital strings represent the px design value under the 160×160 benchmark, scaled by size / 160; auto uses the baseline value 4. The final rounded corner is subject to the angle limit of the ring thickness and the sector; 0 is a right angle.
separatorWidth number | string | "auto" "auto" Fan interval transparent partition; non-negative values or digital strings represent the px design value under the 160×160 benchmark, scaled by size / 160; auto uses the baseline value 2, 0 to close the interval. Leave the real background through the path geometry.
colors (CircularChartColor | undefined)[] — After filtering, the index overlay color is displayed; each item supports semantic color, CSS color/variable, linear-gradient string or { type?, angle?, stop } gradient object. Undefined empty space back to the current colorMode; the data item color takes precedence over the color palette. Background utility class names are not supported.
colorMode CircularChartColorMode "sequence" The sequence is cycled by Primary and 9 Secondary color sequences; monochromeFade uses the same main color from 100% uniformity of the first item to 20% of the last item (100% for a single item); all items of monochrome use the same color. All three modes allow data items and color palettes to be overwritten one by one.
color CircularChartColor — Override the fallback main color for both monochromeFade and monochrome, taking priority over monochromeColor and mutedMonochromeColor. sequence ignores this prop. Accepts solid CSS colors, semantic colors, and gradients like item color.
monochromeColor CircularChartColor "Primary" The fallback main color is only used when monochromeFade and color is not transmitted; solid colors and gradients are supported. The transparency is reduced from 100% in the first item to 20% in the last item, and the data items color and colors still take precedence.
mutedMonochromeColor CircularChartColor "Black4" The fallback color is only used when it is monochrome and does not transmit color; the default Black4 is a low-contrast color adapted to the theme, and the sector filling itself no longer reduces the transparency. The data items color and colors still take precedence.
hoverThicknessGrowth number | string — The non-negative px design value of outward thickening when hover / focus; the inner radius and center of the circle remain unchanged. When not transmitted, read the old alias hoverScaleSize and return 4; 0 Turn off the geometric thickening but keep the data prompt. Strings only extract numerical values.
startDirection StartDirection "top" Starting from the sector hit by top, top-right, right, bottom-right, bottom, bottom-left, left, top-left, play in clockwise order; only select the first playback sector, and do not rotate the data layout. Multiple directions covered by the same large sector may produce the same order.
showBaseLayer boolean false Whether to display the bottom layer of the same color 4% transparency of the final geometry; no rendering by default. After opening, the bottom layer does not participate in the appearance of motions and hits, which is convenient for observing the filling progress; it does not change the real sector and data.
appearMotion boolean true Whether to enable graphics to appear motion effects; when false, the full fill is directly displayed, and replay / reverseAppearance is not played. This switch does not turn off the independent hover thickening feedback.
appearOnView boolean true In the uncontrolled mode, the anchor point is automatically played once when it reaches the specified viewport position; in the same source iframe, it is judged according to the real position of the outermost source page. False can be played with the instance method instead. appearanceProgress is preferred; scrollAppearance is driven by scrolling when it is turned on.
appearanceProgress number — The controlled filling progress is limited to 0–1; after provision, the display is decided by the caller, and the automatic playback and instance playback methods do not change the controlled screen. appearMotion=false is still displayed directly and completely; external progress changes will not trigger onAppearanceProgressChange.
defaultAppearanceProgress number 0 The progress of the first initialization of uncontrolled mode is limited to 0.1, and only read once; subsequent changes to this attribute do not reset the animation. When there is appearanceProgress, it does not determine the picture; the default 0 is the stroke state.
appearanceDuration number — The total duration of instance playback and automatic appearance, in ms; fixed at 1000 when not transmitted, does not change with the number of sectors. Non-positive numbers go directly to the end point in the next animation frame; do not change the scrolling or controlled progress mapping.
appearAnchorId string — The id of the anchor element is triggered in the page; the chart root element is used when it is not transmitted, and the non-existent id is not returned to the root element. It is only used for automatic appearance or scrolling timeline, and does not change the position of the chart.
appearAnchorPoint AnchorPoint "top" The top (top), center (vertical center) or bottom (bottom) of the anchor point is compared with the viewport trigger line; it does not affect the direction of sector drawing.
appearViewportPoint number | "top" | "center" | "bottom" 0.7 Viewport trigger line: the number is limited to 0–1, or use top=0, center=0.5, bottom=1. The default 0.7 is 70% of the viewport height; cooperate with appearAnchorPoint to determine the trigger/rolling starting point.
scrollAppearance boolean | { distance?: number | string } false True turns on the uncontrolled timeline of window scrolling driver; the same source iframe responds to the outer page scrolling at the same time. The object can cover the scrolling distance with distance. After the anchor point crosses the trigger line, the progress is determined by the distance, and the backward scrolling will be restored in reverse. Do not monitor the nested ScrollArea; appearanceProgress / appearMotion=false is preferred.
scrollAppearanceDistance number | string 320 The CSS px distance required for the scrolling progress from 0 to 1; numbers or number strings, processed as a minimum of 1. scrollAppearance.distance is preferred; it only takes effect when the rolling timeline is opened, and the string unit is not converted.
onAppearanceProgressChange (progress: number) => void — Receive progress (0..1) when writing internal progress, including direct completion in initialization, automatic playback, instance method and rolling update; external progress readings can be synchronized. The default value initialization itself is not notified; it is not used to listen to external appearanceProgress, the return value is ignored, and the update cannot be canceled.
hoverScaleSize number | string — The unit and behavior of the abandoned thickening alias are the same as hoverThicknessGrowth; only the latter is effective when it is not transmitted. The new code uses hoverThicknessGrowth, which cannot be understood as the overall scaling ratio.
maxItems number 10 The number of the first N items retained after filtering, not sorted, not automatically merged Other; rounding and limited to 1..10, 0 / NaN back 10. The percentage is only based on reserved items. Business with more than 10 classes should first aggregate subcategores to retain the total amount; when all are invalid, an empty chart will be rendered.
showCenterLabel boolean true Whether to render the center content area; false hides the default dynamic label, segment-value / segment-name template and static center-label at the same time. Hide the center does not turn off the explicitly enabled HoverPanel.
hoverMode ChartHoverMode "cell" Cell hits according to the angle of the ring band, crossing the transparent partition and still maintains the sector it belongs to, excluding the central void and outside the picture; item only responds to the sector path hit. Both modes retain sector keyboard focus prompts.
showHoverPanel boolean true By default, the structured panel is displayed according to the current hover / focus sector, while retaining the dynamic center label. False Close the panel; but when there is a visible static center-label, the component automatically enables the panel to carry sector information.
hoverPanelVariant ChartHoverPanelVariant "light" Panel vision: light is the default light glass style, dark is the dark style, and surface is the theme surface style; only change the appearance of the panel, do not change the data and hit.
hoverPanelPlacement ChartHoverPanelPlacement "pointer" Panel position: pointer follows the pointer; anchor-top / anchor-side relative to the current pointer point positioning (keyboard focus uses the center of the picture); pin-top-left / pin-top / pin-top-right / pin-right / pin-bottom-right / pin-bottom / pin-bottom-left / pin-left / pin-center fixed in the corresponding position of the chart, the edge is kept 8px and participates in the viewport collision processing.
showHoverPanelLabel boolean false Whether to display the current sector name additionally above the panel row data; false only displays the data row. There is a visible effect only when the panel is open and there is a current item.
percentFormat NumberFormatOptions — The number format of center percentage, panel percentage and sector aria-label; support fractionDigits, locale and other NumberFormatOptions. The priority is the data item percentFormat → this property → NumberFormatContext → default one decimal place; labelFormatter only covers the central text. Default behavior: NumberFormats percentage default.
labelFormatter (item: CircularChartItem, percent: number) => string — When hover / focus, receive item (currently valid data item) and percent (0..100 percentage), and return the central text; split into numerical lines and name lines with a new line, and use up to two lines. Do not call when static center-label or showCenterLabel=false; do not change the percentage of panel and sector aria-label.
loading boolean — Data waiting status. Keep the final place and center content corresponding to size, fix the sector at the starting point of the motion and pause the interaction; after changing to false, play the motion according to the current viewport conditions. The chart does not render Skeleton, and also reads the loading context of the upper Frame; the explicit empty data={[]} does not return the default demonstration data.
children Snippet — No parameter Snippet, rendered in the central area of showCenterLabel=true. The direct Frame as="center-label" provides permanent content and automatically enables the panel, in which the direct Text as="segment-value" / "segment-name" can reuse the default central layout and retain the custom text; the direct Text as="segment-value" / "segment-name" is a dynamic label style template, and two lines of text are written by the component. The center is a display area and is not used for the interactive toolbar.
class string "" The class string added to the root Frame of the chart; it is empty when not passed. Only express the public root style of the caller, and do not penetrate the internal SVG and tag structure.
style string "" The inline style string added to the root Frame is merged after the variable injected by the component. It can express the external layout; chart geometry gives priority to size, ringThickness and other public Props.
enableResponsive boolean true By default, the chart body is reduced proportionally when the container cannot be placed, and flex shrinks side by side. The minimum display width is determined by minWidth (default 40px), and does not shrink when it is the same as the chart width; HoverPanel does not scale; false retains the declaration size. It is also transmitted to Frame.
enableContext boolean true Through the context ability switch of Frame; the current size, color and appearance progress of the current component are still determined by the corresponding Props.
enableScaleObserver boolean false The size observation compatibility ability of the old version of the transmission Frame is turned off by default; it is not equivalent to the size of the percentage chart, and it is not recommended for the new example to rely on this migration period entrance.
enableDefaultMotion boolean true The default outer dynamic performance of the frame; do not take over the timeline and sector hover thickening inside the chart, which is controlled by appearMotion / appearanceProgress and hoverThicknessGrowth respectively.

See the base component documentation for inherited capabilities not redeclared here. This table lists only named public Props declared by this component.

Usage boundaries and details

HoverPanel shares a three-column layout. The dot-to-name gap prefers --gap-1 and the name-to-value gap prefers --gap-2; both retain a 4 CSS px floor in LTR and RTL. At a 16px root font size the defaults remain 4px / 8px, and larger tokens are preserved. This floor does not apply to vertical panel sections or chart center annotations.

Design principle

  • Professional chart components: The current maintenance location is components/src/components/; this directory is the technical location during the migration period. THE RUNNING PACKAGE AND ORIGINAL SOURCE CODE OF ALL COMPONENTS CAN BE OBTAINED FREE OF CHARGE; THE PURPOSE, PROJECT, SEAT AND PRODUCTION USE RIGHTS AND INTERESTS ARE EXECUTED ACCORDING TO LICENSE, AND COMPONENTS ARE NOT DIVIDED ACCORDING TO ADVANCED FUNCTIONS.

  • Up to 10 items: Default maxItems=10. The color only uses primary and 9 secondary-* tokens. After 10 items, the color will be repeated and the recognition of the chart will decrease, so the component is truncated to 10 items by default.

  • Color mode: Default colorMode="sequence" press primary → secondary-purple → secondary-cyan → secondary-mint → secondary-green → secondary-indigo → secondary-blue → secondary-yellow → secondary-orange → secondary-red cycle; monochromeFade uses Primary from 100% to the last 20%; monochrome defaults to use Black4 in all sectors. Each data item can define the sector color separately by color; colors can be used as a color palette overlay entry by index, and the blank item can be returned to the current color mode.

  • Geometric scaling: The values of cornerRadius and separatorWidth are both design reference values under the 160x160 chart, and the components will be scaled according to the actual size / 160 equiproportio. auto uses the default reference value: rounded corner 4, interval 2.

  • Hover emphasis: When hover / focus is the current item, the sector is not displaced, and only the outer radius slows down to increase hoverThicknessGrowth, and the default outward thickness is 4px; the percentage is displayed above the center label, and the name of the current item is displayed below.


2.1 Data and form

  • Support ring chart and solid pie chart: when ringThickness is less than the outer radius, it is a ring, and when ringThickness >= outerRadius, the inner radius is zero and a solid pie chart is formed.

  • Support up to 10 sectors: maxItems defaults to 10, the internal upper limit is fixed at 10, and the excess items will be truncated.

  • Automatically filter invalid data: value <= 0 or non-numeric items do not participate in rendering; render empty charts when explicit empty arrays or all invalid, and only use built-in sample data when data is omitted.

  • Support size, ring thickness, rounded corner, partition and hover outer thickening adjustment: digital values are written to component private CSS variables according to px semantics.

  • Support 160x160 reference geometry scaling: the incoming values of cornerRadius and separatorWidth are both design reference values, and the actual rendering is enlarged or reduced in equal ratio of size / 160.

2.2 Sector drawing

  • The sector path includes the outer arc, the inner arc and the rounded corners of the head and tail tangent. The rounded corners are generated according to the shape of the real sector and do not depend on the later cutting.

  • The transparent interval between sectors is generated in the path geometry stage: separatorWidth will be converted to padAngle of d3-shape, and the inner and outer arc will be contracted with padRadius. Do not draw white divides, and do not use the rear gap mask to hollow out the sector.

  • The rounded corner is still generated by the contracted sector path, so the transparent interval through the ring thickness and the rounded corner at the end can be established at the same time.

  • When showBaseLayer=true, the bottom layer of the same color 4% transparency occupies the place sector, which is not drawn by default and does not participate in the motion; the real filling layer is played above it.

  • The top layer uses the complete sector path as the transparent hit layer to ensure the stability of the interaction range of hover/focus.

2.3 Color ability

  • The default color order is Primary, Purple, Cyan, Mint, Green, Indigo, Blue, Yellow, Orange and Red.

  • Each sector can define the color separately through data[i].color; sectors with undefined colors continue to use the default color sequence.

  • colors can be used as the whole set of color palettes to override the entrance, and allow the array item to be undefined, and the empty item will revert the current colorMode corresponding color.

  • Support Chart's general three color modes: sequence default color sequence, monochromeFade monochrome fade, monochrome monochrome. All three modes support data[i].color and colors[i] specified sector custom colors.

  • color supports ordinary colors, CSS variables, linear-gradient(...) Strings and gradient objects.

  • Background tool class names such as .bg-gradient-blue are not directly supported; the SVG sector uses fill, and the background / background-image of the background class will not be consumed by path.

2.4 Dynamic ability

  • Support the automatic playback of the visual port for the first time, which can be turned off by appearOnView.

  • Support 8 starting directions: top / top-right / right / bottom-right / bottom / bottom-left / left / top-left.

  • Support scroll bar timeline: After scrollAppearance is turned on, the scrolling distance is directly mapped to appearanceProgress.

  • Support controlled timeline: After the external input of appearanceProgress, the component only renders according to the progress and does not play by itself.

  • Support uncontrolled methods: replay() refill, resetAppearance() reset, reverseAppearance() reverse return.

  • The filling layer and the transparency layer are started at the same time: the next sector can be started after the filling is completed, and the transparency completion continues independently without blocking the subsequent sectors.

  • Support reverse timeline: When appearanceProgress returns from 1 to 0, the fill width and transparency are reversed according to the same formula.

2.5 Interaction and accessibility

  • When hover / focus is in the current sector, the outer radius of the sector thickens outwards, the inner radius remains unchanged, and the sector does not shift.

  • When hover / focus is in the current sector, the center label shows the percentage and name of the current item. The default percentage is above and the name is below.

  • labelFormatter can customize the content of the central label, and supports disassembly into two lines with \n; the first line is rendered according to the numerical style, and the second line is rendered according to the name style.

  • Direct sub-elements <Text as="segment-value" /> / <Text as="segment-name" /> Custom center label style template can be passed; the component will automatically write the real percentage and name of the current sector, and the default center label will not be rendered when any character sub-element appears.

  • showCenterLabel={false} can turn off the central label and the content of the central label role written externally.

  • The root uses role="group" and a locale-aware default accessible name ("Circular chart" in en-US); an explicit aria-label takes priority. The SVG keeps focusable segments exposed; each hit target uses role="button" and its own accessible name containing the business name and formatted percentage.

  • The sector hit layer can get the keyboard focus, and the focus behavior is consistent with the hover; the center tag uses aria-live="polite".

2.6 Frame Integration

  • The root layer uses Frame; loading retains the declaration size and central content, fixes the sector at the starting point of the motion and suspends the interaction. The root layer of the chart explicitly closes Skeleton, and also reads the loading context of the upper Frame.

  • Support enableResponsive, enableContext, enableScaleObserver, enableDefaultMotion transmitted to Frame.

  • class, style and other undeclared attributes will be transmitted to the root Frame.

  • The size of the chart is determined by the SVG geometric algorithm, which is expressed as Hug as an external layout sub-item.


3.1 Props

The rest of the undeclared attributes will be passed to the root layer Frame.

3.2 Component method

Way Description
replay() Uncontrolled motion effect is replayed from 0 to 1
resetAppearance() Uncontrolled motion is immediately reset to 0
reverseAppearance() The uncontrolled motion first enters the completion state, and then reverses from 1 to 0 along the same timeline.

3.3 Data structure

interface CircularChartItem {  name: string;  value: number;  color?: CircularChartColor;  percentFormat?: NumberFormatOptions;}

Percentage number format priority: labelFormatter (center label) → item.percentFormat → component percentFormat → NumberFormatContext → NumberFormats default value. Sector hit layer aria-label does not use labelFormatter, but consumes percentFormat.

value <= 0 or non-numeric items will be filtered; empty charts are rendered when explicit empty arrays or all are invalid, and data is omitted to use the built-in sample data.

type CircularChartColor = string | CircularChartGradient; interface CircularChartGradient {  type?: "linear";  angle?: number;  stops: (string | { color: string; offset?: number | string; opacity?: number })[];}

Color priority: data[i].color → colors[i] → colorMode corresponds to the back color. Sectors with undefined colors will not be affected by other sector custom colors, and the current color mode will continue to be used. The gradient string supports the form of linear-gradient(135deg, #111111 0%, #6BE6D3 100%); the gradient object supports explicit declaration with angle and stops.


Layout model

Main layout model: internal atomic layout.

  • The root layer is Frame, and the internal chart body uniformly carries the SVG drawing area and the center label, and is responsible for the equal scaling; HoverPanel is side by side with the chart body to maintain the original size.

  • The size of the chart is determined by the size and SVG geometric algorithms; it is expressed as Hug as an external layout sub-item.

  • It does not access the three-slot protocol and does not undertake the arrangement of general sub-elements.

For details, please refer to LayoutProtocol.md §12.3 (source code reference: specs/maintainers/LayoutProtocol.md).


Motion

Scene Realize Bridge variable
First appearance / scrolling appearance All sectors first display a complete 0.5px inner stroke of the same color; then start from the starting boundary of the sector hit by startDirection, and execute sector by sector in a clockwise direction. The default total duration is fixed at 1000ms, which does not change with the number of sectors; the next sector only starts after the previous sector is filled, and does not wait for the transparency of the previous sector to be completed; the filling direction is from the starting point of the sector to the end point of the sector; the whole chart uses the chart level to slow down the time allocation, and the duration of the previous segment is close, and the longer the duration of the later sector; the internal filling of a single sector remains linear; the rolling timeline is directly pushed back by appearanceProgress to the same timeline, which is naturally reversible. Component internal determinism timeline; appearanceProgress controls the overall progress
Sector order and speed The first sector starts immediately; the 2nd sector starts after the 1st sector is filled, the 3rd sector starts after the 2nd sector is filled, and so on. CC-01 The duration of the first three sections in the five sectors is close, and the last two sections gradually become longer, forming a slowdown of the whole chart level. Internal timeline of components
Fill the transparency Ring sector overlay transparency mask: the transparency of the tail is 0%, the middle section is 62%, the scanning head is 100% at startup, and it is completed within 0.75s. The solid pie chart directly draws revealPath that increases with the progress, avoiding the browser from redrawing the dynamically nested SVG mask; both forms are driven by the same progress timeline. appearanceProgress direct rendering
The bottom-level space sector When showBaseLayer=true, draw a layer of 4% transparency sector of the same color under the real filling layer, and do not participate in the motion; the complete chart structure can also be seen before the motion is completed. Internal base layer of components
Fan divide the dividing line The interval is adjusted by separatorWidth; its value represents the design reference value under the 160x160 chart, and the component is scaled according to size / 160. The interval in the path geometry stage is generated by padAngle + padRadius, so that the real background can be seen naturally. separatorWidth
Sector hover emphasis Each sector only retains the current animation; when the hover / focus target changes, cancel the old frame immediately, transition from the current outer radius to the latest target, do not queue or make up; thicken to R + hoverThicknessGrowth when entering, return to R when leaving and there is no focus, and keep the original 300ms cubicOut; the inner radius remains unchanged, the sector is not displaced, and directly reaches the target when reducing the motion Internal component requestAnimationFrame + cubicOut geometric animation
Sector brightness feedback filter: brightness(1.04) --snowui-frame-color-motion-*
Enter the center label opacity + scale --snowui-frame-appear-motion-*(life cycle enters timing) / --snowui-frame-control-motion-*

6.1 The starting direction of the motion

startDirection supports 8 directions: top / top-right / right / bottom-right / bottom / bottom-left / left / top-left.

Judgment rules:

  1. Emit rays from the center of the circle to startDirection.

  2. Find the sector that the ray hits.

  3. No matter where the ray falls in the sector, the motion starts from the starting boundary of the sector itself.

  4. Then fill in sector by sector in a clockwise direction until all sectors are completed.

Usage examples

<CircularChart  data={[    { name: "Search Engine", value: 1048 },    { name: "Direct", value: 735 },    { name: "Email", value: 580 },    { name: "Union Ads", value: 484 },    { name: "Video Ads", value: 300 }  ]}/>
<CircularChart  size={320}  ringThickness={88}  cornerRadius={16}  separatorWidth={4}  hoverThicknessGrowth={4}/>

In the above example, cornerRadius={16} and separatorWidth={4} represent the design reference value under the 160x160 chart. When the actual size is 320, it is finally presented as a 32px rounded corner with an 8px interval.

<CircularChart  size={220}  ringThickness={64}  data={[    {      name: "Primary Gradient",      value: 42,      color: "linear-gradient(135deg, var(--primary, #111111) 0%, var(--secondary-mint, #6BE6D3) 100%)"    },    { name: "Default Purple", value: 30 },    {      name: "Object Gradient",      value: 18,      color: {        angle: 45,        stops: [          "var(--secondary-purple, #B899EB) 0%",          "var(--secondary-cyan, #A0BCE8) 100%"        ]      }    },    { name: "Default Mint", value: 10 }  ]}/>
<CircularChart  data={[    { name: "Direct", value: 735 },    { name: "Email", value: 580 }  ]}>  <Text as="segment-value" class="text-18 font-bold text-black-100" />  <Text as="segment-name" class="text-14 text-black-40" /></CircularChart>
<CircularChart  bind:this={chart}  startDirection="left"  appearOnView={false}/> <Button onclick={() => chart.replay()}>Replay fill</Button><Button onclick={() => chart.reverseAppearance()}>Reverse appearance</Button>
 <CircularChart  size={220}  ringThickness={140}  cornerRadius={8}  separatorWidth={3}/>

CircularChart style overlay

Read the full LICENSE first; reuse that review for the same task, version, and scope.

First use public component Props to match the desired appearance, preserving the existing look where possible. If changes are necessary, follow the style selection and override order: use your own class (including Tailwind already in the project) or an instance style. The names below are public overrides actually consumed by source; check their scope and do not modify private DOM. Choose the correct SnowUI token family. External apps need not install SnowUI website utilities.

Variable Purpose and default reference
--snowui-circular-chart-label-color-override The main color of the center label (default var(--black-100))
--snowui-circular-chart-label-font-size-override Center label numerical font size (160 benchmark default 14px, scale with the chart)
--snowui-circular-chart-label-font-weight-override The main line of the center label is heavy (default 600)
--snowui-circular-chart-label-muted-color-override Central label secondary color (default var(--black-40))
--snowui-circular-chart-label-muted-font-weight-override Center label sub-line word weight (default 400)
--snowui-circular-chart-size-override Overall size (default 240px)

Design Guide · Token · Combination Boundary

CircularChart Type and Status

Required Props: no named required Props.

Bindable Props: this component declares no named two-way binding; handle state updates through public callbacks or its host.

Inheritance statement: no interface inheritance. The inheritance ability is subject to the public document of the base class; the fields in Omit have been excluded and cannot be used as they are according to the base class.

Share Frame API · Return API

The following type declarations are actually referenced by public Props and come from source. A Svelte Snippet denotes a content fragment; ComponentProps denotes the Props of the corresponding component.

CircularChartItem

Source: components/src/components/CircularChart.svelte

export interface CircularChartItem {        name: string;        value: number;        color?: CircularChartColor;        percentFormat?: NumberFormatOptions;    }

CircularChartColor

Source: components/src/components/CircularChart.svelte

export type CircularChartColor = ChartColor;

ChartColor

Source: components/src/lib/chart/colors.ts

export type ChartColor = SnowUIColor | string | ChartGradient;

SnowUIColor

Source: components/src/lib/color.ts

export type SnowUIColor = keyof typeof SNOWUI_COLOR_TOKENS;

ChartGradient

Source: components/src/lib/chart/colors.ts

export interface ChartGradient {    type?: "linear";    angle?: number;    stops: (string | ChartGradientStop)[];}

ChartGradientStop

Source: components/src/lib/chart/colors.ts

export interface ChartGradientStop {    color: SnowUIColor | string;    offset?: number | string;    opacity?: number;}

NumberFormatOptions

Source: components/src/lib/number-format.ts

export interface NumberFormatOptions {    fractionDigits?: number;    valueLabelFractionDigits?: number;    percentFractionDigits?: number;    preserveTrailingZeros?: boolean;    useGrouping?: boolean;    emptyValue?: string;    locale?: string;    notation?: NumberNotation;    compactThreshold?: number;    compactFractionDigits?: number;}

NumberNotation

Source: components/src/lib/number-format.ts

export type NumberNotation = "standard" | "compact" | "auto";

CircularChartColorMode

Source: components/src/components/CircularChart.svelte

export type CircularChartColorMode = ChartColorMode;

ChartColorMode

Source: components/src/lib/chart/colors.ts

export type ChartColorMode = "sequence" | "monochromeFade" | "monochrome";

StartDirection

Source: components/src/components/CircularChart.svelte

type StartDirection =        | "top"        | "top-right"        | "right"        | "bottom-right"        | "bottom"        | "bottom-left"        | "left"        | "top-left";

AnchorPoint

Source: components/src/components/CircularChart.svelte

type AnchorPoint = "top" | "center" | "bottom";

ChartHoverMode

Source: components/src/components/CircularChart.svelte

type ChartHoverMode = "item" | "cell";

ChartHoverPanelVariant

Source: components/src/lib/chart/hover-panel.ts

export type ChartHoverPanelVariant = "dark" | "light" | "surface";

ChartHoverPanelPlacement

Source: components/src/lib/chart/hover-panel.ts

export type ChartHoverPanelPlacement =    | "anchor-top"    | "anchor-side"    | "pointer"    | "pin-top-left"    | "pin-top"    | "pin-top-right"    | "pin-right"    | "pin-bottom-right"    | "pin-bottom"    | "pin-bottom-left"    | "pin-left"    | "pin-center";