Skip to content

Chart

Get Ritla UI

Every Ritla chart, from bars and lines to heatmaps, candlesticks and the small insight charts for cards, running right to left in Arabic and readable by keyboard.

Preview

Mirrored is the same English content laid out right to left: it separates a mirroring bug from a translation one.

PreviewCode
EnglishMirroredArabic
LTR →

Installation

  1. Add the component

    npx ritla add chart

    Also works with shadcn: npx shadcn@latest add @ritla/chart

  2. Or copy by hand

    Installing by hand copies the component's source into your project. The source comes with a Ritla UI license, so the CLI is the short way to the same files.

Usage

chart.tsx
import { BarChart, ChartCard, ChartLegend } from "@/components/ui/chart";<ChartCard title="Income and spend" subtitle="SAR" legend={<ChartLegend items={series} />}>  <BarChart label="Income and spend by month" data={data} series={series} /></ChartCard>

Examples

Stacked bars

A total and its parts per category.

EnglishMirroredArabic
LTR →

Horizontal bars

Rank categories with long names, largest first.

EnglishMirroredArabic
LTR →

Line

A smooth line with a soft area, last year as a grey dashed line and the average as a dashed guide.

EnglishMirroredArabic
LTR →

Stacked area

How parts add up to a total over time.

EnglishMirroredArabic
LTR →

Floating bars

A low-to-high range per category.

EnglishMirroredArabic
LTR →

Histogram

How many values fall in each bin.

EnglishMirroredArabic
LTR →

Combo

Two measures together: bars and a dotted line on one axis.

EnglishMirroredArabic
LTR →

Step

A value that holds and then jumps, like a policy rate.

EnglishMirroredArabic
LTR →

Line in a card, with a range

The axis on the end side starting at 30K, and a shaded range with its change. Hover or use the arrow keys for the crosshair.

EnglishMirroredArabic
LTR →

Scatter

Two measures per item, as a dot.

EnglishMirroredArabic
LTR →

Bubble

Height is one measure, the bubble's area a third.

EnglishMirroredArabic
LTR →

Box plot

The range, the middle half and the median per category.

EnglishMirroredArabic
LTR →

Lollipop

A light ranking: a stick, a dot and the value.

EnglishMirroredArabic
LTR →

Bullet

Progress toward a target over poor, fair and good bands.

EnglishMirroredArabic
LTR →

Heatmap

When and how often, in six steps of the brand ramp.

EnglishMirroredArabic
LTR →

Insight bars

One month highlighted, a forecast with hatched bars, and money in and out around zero.

EnglishMirroredArabic
LTR →

Density bars

A value a day at a glance, with a marker that follows the pointer and the arrow keys.

EnglishMirroredArabic
LTR →

Segmented bar

Segments toward a limit, stacked shares, and the remainder hatched.

EnglishMirroredArabic
LTR →

Radial

Gauge, arc segments, rings and a progress ring, filling anticlockwise in Arabic.

EnglishMirroredArabic
LTR →

Insight heatmaps

Activity by hour and day, retention by start week, and a daily strip.

EnglishMirroredArabic
LTR →

Candlestick

An asset's daily price, with volume and the last price on the axis.

EnglishMirroredArabic
LTR →

Donut

Two to five parts of one whole, with the total in the centre.

EnglishMirroredArabic
LTR →

Meter

One value toward a limit, or one total split into parts.

EnglishMirroredArabic
LTR →

API reference

ChartCard

PropTypeDefaultDescription
title / subtitleReactNodeNoneThe title, and the unit and period.
legendReactNodeNoneA ChartLegend under the plot.

BarChart

PropTypeDefaultDescription
dataChartDatum[]NoneOne row per category: label and a value per series.
seriesChartSeries[]Nonekey and label, in the chart colour order.
stackedbooleanfalseStacked instead of side by side.
layout"vertical" | "horizontal""vertical"Horizontal for ranking.
series[].fromstringNoneThe key of the low end: floating bars.
histogrambooleanfalseBars filling their bins, 2px apart.
lineChartSeriesNoneA second measure as a dotted line over the bars.
max / valueFormatnumber / (n) => stringNoneThe axis maximum and its value format.
labelstringNoneNames the hidden data table.

LineChart

PropTypeDefaultDescription
areabooleanfalseA soft area under the line.
stackedbooleanfalseFilled stacked layers.
curve"smooth" | "linear" | "step""smooth"Curved, straight or in steps.
average{ value, label }NoneThe average line and its tag.
range{ from, to, label, tone }NoneA shaded range between two indexes, with a tag for its change.
axis / min"start" | "end" / number"start" / 0The value axis side and its lowest value.
series[].comparisonbooleanNoneA grey dashed line for the last period.

ScatterChart / BubbleChart

PropTypeDefaultDescription
series{ label, points: { x, y }[] }[]NoneScatter: each series' points.
xLabel / yLabelstringNoneWhat each axis is.
data{ label, value, size, series }[]NoneBubbles: height, area and series.

BoxPlot / LollipopChart / BulletChart

PropTypeDefaultDescription
data / names (BoxPlot){ label, min, q1, median, q3, max }[] / RecordNoneThe five numbers per category, and their names for the tooltip and table.
data (Lollipop){ label, value }[]NoneRanked, largest first.
data (Bullet){ label, value, target, bands? }[]NoneActual, target and the two band edges.

Heatmap

PropTypeDefaultDescription
rows / columns / valuesstring[] / string[] / number[][]NoneRows, columns and values[row][column].
labelEverynumber1Label every nth column.
less / moreReactNodeNoneThe two ends of the key.

InsightBarChart

PropTypeDefaultDescription
style"highlight" | "forecast" | "diverging""highlight"Figma's Style.
highlightnumberNoneThe bar in brand: the month in question, or the current one.
average / labelEvery{ value, label } / numberNoneThe average guide; label every nth bar.

DensityBars / SegmentedBar

PropTypeDefaultDescription
emphasis / marker[from, to] / numberNoneA darker stretch, and where the marker starts.
type"segments" | "stacked" | "remainder""segments"Figma's Type for the segmented bar.
value / max / segments / partsnumber / number / number / { label, value }[]NoneThe progress, or the shares in percent.

RadialChart

PropTypeDefaultDescription
type"gauge" | "arc-segments" | "rings" | "progress-ring""gauge"Figma's Type.
value / max / ringsnumber / number / { label, value, max? }[]NoneThe value, or up to three rings, outermost first.
childrenReactNodeNoneThe number and line in the centre.

ActivityHeatmap / CohortHeatmap / HeatStrip / CandlestickChart

PropTypeDefaultDescription
columns / rows / valuesstring[] / string[] / number[][]NoneActivity: days, hours and values.
rows (Cohort){ label, values: number[] }[]NoneThe active share each week.
days / labels("up" | "down" | "flat")[] / { up, down, flat }NoneThe daily strip and its key words.
data / last / names{ label, open, high, low, close, volume? }[] / number / { open, high, low, close }NoneThe candles, the last price and the names of the four values.

ChartValueTooltip / ChartAxisTag / ChartDot

PropTypeDefaultDescription
label / valueReactNodeNoneTooltip: the label over the value.
tone"inverse" | "brand" | "positive" | "negative" | "neutral""neutral" / "brand"The tag's or dot's tone, by meaning.

DonutChart

PropTypeDefaultDescription
data{ label, value }[]NoneThe parts.
total / totalLabelReactNodeNoneThe centre number and the line under it.

ChartMeter

PropTypeDefaultDescription
type"single" | "proportional""single"Figma's Type.
value / maxnumberNoneFor a single meter.
parts{ label, value }[]NoneFor a proportional meter.
start / endReactNodeNoneUnder the bar at the start and end.

Anatomy

Every chart is the same card: header, plot with its axes, legend. The parts are shared internal components, so a change reaches every chart.

  1. 1. Headertitle / subtitleThe title says what is measured; the subtitle gives the unit and the period.
  2. 2. Value axis and gridlinesFive gridlines from zero, labels on the start side, Western digits.
  3. 3. DataseriesColours follow the series order; bars start at zero.
  4. 4. Category axisdata[].labelMonths, days, ranges or cities, centred under each slot.
  5. 5. LegendChartLegendOne entry per series, in the same order as the data.
  6. 6. TooltipThe category, then one row per series with its value; on hover and on focus.
  7. 7. MarkerA vertical line that follows the pointer and anchors the tooltip.

Usage guidelines

A chart shows the shape of numbers across categories or time. When bars beat slices, keeping series apart, saying the unit and the period, and how the whole chart mirrors in Arabic.

1. Rank many parts in bars

Five or more parts read better as ranked bars than as slices.

Rank many parts in bars

Do: Ranked barsLargest first, values at the bar ends: every part is easy to compare.
Don't: Six slicesSlices of similar size cannot be compared by eye; the reader ends up decoding the legend.

2. Keep neighbouring series apart

Use the series colours in order; they change hue and lightness from one to the next. Six series at most, in token order: blue, navy, grey, then teal, yellow and orange. Past six, keep the top five and group the rest as Other in grey.

Keep neighbouring series apart

Do: Series 1 and 2Blue and navy differ in hue and in lightness, and the legend sits under the plot.
Don't: Two bluesTwo shades of one hue blur together, more so for colour-blind readers.

3. Say the unit and the period

The subtitle carries the unit and the period, so the numbers on the axis mean something. Bars and areas start at zero. A line may start elsewhere when the change is the point; say so in the subtitle.

Say the unit and the period

Do: Unit and periodThousands of SAR, January to June: the axis now reads as money over half a year.
Don't: Numbers only1,000 of what, and when? The reader has to guess.

4. Mirror the whole chart in Arabic

In Arabic the whole chart mirrors: the value axis moves to the right and time runs right to left. Numbers do not mirror. Use the RTL variant; translating the copy alone leaves time running the wrong way.

PartIn Arabic (RTL)Why
Value axisOn the right; zero stays at the bottomThe axis sits where reading starts; up is still more
Categories and timeRight to left, the first month on the rightMatches the reading order of the labels
Horizontal barsLabels on the right, bars grow to the leftThe label is read before its bar
Donut and pieSegments run counterclockwise from the topThe first segment starts where reading starts
LegendThe first series on the rightSame order as the data
DigitsWestern digits 0123456789, as in all of RitlaNumbers keep their own order inside Arabic text
Percent and signed valuesInside a left-to-right isolate (U+2066 to U+2069)So 80% never shows as %80 and a minus sign stays in front

Mirror the whole chart in Arabic

Do: RTL variantJanuary on the right, the value axis on the right, the legend on the right.
Don't: Arabic copy on an LTR chartTime runs left to right against the reading order, and the axis sits on the wrong side.

Tokens & specs

Every colour and measure in a chart is a variable. The six series colours in order, then the variables for the card's parts.

Series colours

Series 1 to 6, in this order. Contrast is measured against background/surface/lowest in each theme; WCAG asks 3:1 for chart marks. The first three are calm and cover most charts; the other three come in only past three series.

TokenContrast, light / darkUse
chart/series-13.35:1 / 6.27:1Blue. Series 1, and every single-series chart
chart/series-211.31:1 / 16.01:1Navy (pale blue in dark mode). Series 2
chart/series-35.25:1 / 5.85:1Grey. Series 3, also Other and the last period
chart/series-43.13:1 / 12.36:1Teal. Series 4, only past three series
chart/series-53.08:1 / 12.49:1Yellow. Series 5
chart/series-63.05:1 / 8.50:1Orange. Series 6
theme/brand/50 to theme/brand/800Six steps: 50, 100, 200, 400, 600, 800Heatmap scale, light to dark; it turns around in dark mode

Parts

Variables for the card, the grid, the text and the tooltip. The card is 560 wide, and charts in a dashboard row share one height so the plots line up.

TokenWhereUse
background/surface/lowestCardCard fill; also the 2px ring around points
border/gray/softCard, gridlinesCard border and gridlines
border/gray/dimBaselineThe zero line
text/gray/intenseTitlebody-16/Semibold
text/gray/dimSubtitle, axis labelsbody-12/Regular
text/gray/mutedLegend, category labelsbody-12/Regular
background/surface/inverseTooltipWith text/invert/white, label-12/Medium and body-12/Regular
radius/12CardCard corners
radius/4BarsOuter corners only, so a stack stays square at the joins
space-24, space-16CardPadding, and the gap between header, plot and legend

Colour is never alone

Never let colour alone carry the meaning: keep the legend, label values where they fit, and use position or a 2px gap between parts (WCAG 1.4.1).

Accessibility

Keyboard

Keys are written for left-to-right. In right-to-left the arrow keys swap: the one pointing toward the reading direction moves forward.

KeyAction
TabMoves focus to the plot.
ArrowRightArrowLeftNext or previous category, with its tooltip. In Arabic, ArrowLeft is next.
HomeEndThe first or last category.
EscapeHides the tooltip.

ARIA

  • Every chart also renders a visually hidden table, so the numbers reach screen readers without the picture.
  • The card is a figure with its title as the figcaption.
  • A single meter is a role="meter" with its values.
  • The plot is a role="group" named by the chart, and the category the cursor reaches is read out in a live region.
  • Segmented bars, gauges and the progress ring are meters; rings are a named list.
  • Don't rely on colour alone: name the series in the legend and tooltip.

Do

  • Show the tooltip on hover and on keyboard focus.
  • Offer the numbers as a table for screen readers.
  • All six series colours reach 3:1 on both surfaces, light and dark.
  • Keep the legend and a 2px gap between parts, so colour never carries the meaning alone.

Arabic and RTL notes

Mirrors

  • Categories and time run right to left, and the value axis sits on the right.
  • Horizontal bars and meters grow from the right, and the donut runs anticlockwise.
  • Gauges and rings fill anticlockwise, and candles and density bars run from the right.

Does not mirror

  • The numeric axis still runs bottom to top, and stacks build from the bottom.
  • Digits stay Western in Arabic, as in Figma.
  • Percent and signed values sit in a left-to-right isolate, so 80% never shows as %80 and a minus stays in front.
Numerals
Chart values always use Western digits; pass valueFormat for another format.
Arabic typography
Title body-16 Semibold; labels body-12 and numeric-12.

Do

  • The RTL variant: the whole chart mirrors, with the value axis on the right, time running right to left and the legend on the right.
  • Keep digits Western, and percent and signed values in a left-to-right isolate.

Don't

  • Translate the copy alone on the left-to-right chart.
  • Mirror the digits, or move zero to the top.