Chart
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.
Installation
Add the component
npx ritla add chartAlso works with shadcn:
npx shadcn@latest add @ritla/chartOr 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
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
Horizontal bars
Rank categories with long names, largest first.
Line
A smooth line with a soft area, last year as a grey dashed line and the average as a dashed guide.
Combo
Two measures together: bars and a dotted line on one axis.
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.
Box plot
The range, the middle half and the median per category.
Bullet
Progress toward a target over poor, fair and good bands.
Insight bars
One month highlighted, a forecast with hatched bars, and money in and out around zero.
Density bars
A value a day at a glance, with a marker that follows the pointer and the arrow keys.
Segmented bar
Segments toward a limit, stacked shares, and the remainder hatched.
Radial
Gauge, arc segments, rings and a progress ring, filling anticlockwise in Arabic.
Insight heatmaps
Activity by hour and day, retention by start week, and a daily strip.
Candlestick
An asset's daily price, with volume and the last price on the axis.
Donut
Two to five parts of one whole, with the total in the centre.
API reference
ChartCard
| Prop | Type | Default | Description |
|---|---|---|---|
title / subtitle | ReactNode | None | The title, and the unit and period. |
legend | ReactNode | None | A ChartLegend under the plot. |
BarChart
| Prop | Type | Default | Description |
|---|---|---|---|
data | ChartDatum[] | None | One row per category: label and a value per series. |
series | ChartSeries[] | None | key and label, in the chart colour order. |
stacked | boolean | false | Stacked instead of side by side. |
layout | "vertical" | "horizontal" | "vertical" | Horizontal for ranking. |
series[].from | string | None | The key of the low end: floating bars. |
histogram | boolean | false | Bars filling their bins, 2px apart. |
line | ChartSeries | None | A second measure as a dotted line over the bars. |
max / valueFormat | number / (n) => string | None | The axis maximum and its value format. |
label | string | None | Names the hidden data table. |
LineChart
| Prop | Type | Default | Description |
|---|---|---|---|
area | boolean | false | A soft area under the line. |
stacked | boolean | false | Filled stacked layers. |
curve | "smooth" | "linear" | "step" | "smooth" | Curved, straight or in steps. |
average | { value, label } | None | The average line and its tag. |
range | { from, to, label, tone } | None | A shaded range between two indexes, with a tag for its change. |
axis / min | "start" | "end" / number | "start" / 0 | The value axis side and its lowest value. |
series[].comparison | boolean | None | A grey dashed line for the last period. |
ScatterChart / BubbleChart
| Prop | Type | Default | Description |
|---|---|---|---|
series | { label, points: { x, y }[] }[] | None | Scatter: each series' points. |
xLabel / yLabel | string | None | What each axis is. |
data | { label, value, size, series }[] | None | Bubbles: height, area and series. |
BoxPlot / LollipopChart / BulletChart
| Prop | Type | Default | Description |
|---|---|---|---|
data / names (BoxPlot) | { label, min, q1, median, q3, max }[] / Record | None | The five numbers per category, and their names for the tooltip and table. |
data (Lollipop) | { label, value }[] | None | Ranked, largest first. |
data (Bullet) | { label, value, target, bands? }[] | None | Actual, target and the two band edges. |
Heatmap
| Prop | Type | Default | Description |
|---|---|---|---|
rows / columns / values | string[] / string[] / number[][] | None | Rows, columns and values[row][column]. |
labelEvery | number | 1 | Label every nth column. |
less / more | ReactNode | None | The two ends of the key. |
InsightBarChart
| Prop | Type | Default | Description |
|---|---|---|---|
style | "highlight" | "forecast" | "diverging" | "highlight" | Figma's Style. |
highlight | number | None | The bar in brand: the month in question, or the current one. |
average / labelEvery | { value, label } / number | None | The average guide; label every nth bar. |
DensityBars / SegmentedBar
| Prop | Type | Default | Description |
|---|---|---|---|
emphasis / marker | [from, to] / number | None | A darker stretch, and where the marker starts. |
type | "segments" | "stacked" | "remainder" | "segments" | Figma's Type for the segmented bar. |
value / max / segments / parts | number / number / number / { label, value }[] | None | The progress, or the shares in percent. |
RadialChart
| Prop | Type | Default | Description |
|---|---|---|---|
type | "gauge" | "arc-segments" | "rings" | "progress-ring" | "gauge" | Figma's Type. |
value / max / rings | number / number / { label, value, max? }[] | None | The value, or up to three rings, outermost first. |
children | ReactNode | None | The number and line in the centre. |
ActivityHeatmap / CohortHeatmap / HeatStrip / CandlestickChart
| Prop | Type | Default | Description |
|---|---|---|---|
columns / rows / values | string[] / string[] / number[][] | None | Activity: days, hours and values. |
rows (Cohort) | { label, values: number[] }[] | None | The active share each week. |
days / labels | ("up" | "down" | "flat")[] / { up, down, flat } | None | The daily strip and its key words. |
data / last / names | { label, open, high, low, close, volume? }[] / number / { open, high, low, close } | None | The candles, the last price and the names of the four values. |
ChartValueTooltip / ChartAxisTag / ChartDot
| Prop | Type | Default | Description |
|---|---|---|---|
label / value | ReactNode | None | Tooltip: the label over the value. |
tone | "inverse" | "brand" | "positive" | "negative" | "neutral" | "neutral" / "brand" | The tag's or dot's tone, by meaning. |
DonutChart
| Prop | Type | Default | Description |
|---|---|---|---|
data | { label, value }[] | None | The parts. |
total / totalLabel | ReactNode | None | The centre number and the line under it. |
ChartMeter
| Prop | Type | Default | Description |
|---|---|---|---|
type | "single" | "proportional" | "single" | Figma's Type. |
value / max | number | None | For a single meter. |
parts | { label, value }[] | None | For a proportional meter. |
start / end | ReactNode | None | Under 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. Header
title / subtitleThe title says what is measured; the subtitle gives the unit and the period. - 2. Value axis and gridlinesFive gridlines from zero, labels on the start side, Western digits.
- 3. Data
seriesColours follow the series order; bars start at zero. - 4. Category axis
data[].labelMonths, days, ranges or cities, centred under each slot. - 5. Legend
ChartLegendOne entry per series, in the same order as the data. - 6. TooltipThe category, then one row per series with its value; on hover and on focus.
- 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
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
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
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.
| Part | In Arabic (RTL) | Why |
|---|---|---|
| Value axis | On the right; zero stays at the bottom | The axis sits where reading starts; up is still more |
| Categories and time | Right to left, the first month on the right | Matches the reading order of the labels |
| Horizontal bars | Labels on the right, bars grow to the left | The label is read before its bar |
| Donut and pie | Segments run counterclockwise from the top | The first segment starts where reading starts |
| Legend | The first series on the right | Same order as the data |
| Digits | Western digits 0123456789, as in all of Ritla | Numbers keep their own order inside Arabic text |
| Percent and signed values | Inside 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
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.
| Token | Contrast, light / dark | Use |
|---|---|---|
chart/series-1 | 3.35:1 / 6.27:1 | Blue. Series 1, and every single-series chart |
chart/series-2 | 11.31:1 / 16.01:1 | Navy (pale blue in dark mode). Series 2 |
chart/series-3 | 5.25:1 / 5.85:1 | Grey. Series 3, also Other and the last period |
chart/series-4 | 3.13:1 / 12.36:1 | Teal. Series 4, only past three series |
chart/series-5 | 3.08:1 / 12.49:1 | Yellow. Series 5 |
chart/series-6 | 3.05:1 / 8.50:1 | Orange. Series 6 |
theme/brand/50 to theme/brand/800 | Six steps: 50, 100, 200, 400, 600, 800 | Heatmap 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.
| Token | Where | Use |
|---|---|---|
background/surface/lowest | Card | Card fill; also the 2px ring around points |
border/gray/soft | Card, gridlines | Card border and gridlines |
border/gray/dim | Baseline | The zero line |
text/gray/intense | Title | body-16/Semibold |
text/gray/dim | Subtitle, axis labels | body-12/Regular |
text/gray/muted | Legend, category labels | body-12/Regular |
background/surface/inverse | Tooltip | With text/invert/white, label-12/Medium and body-12/Regular |
radius/12 | Card | Card corners |
radius/4 | Bars | Outer corners only, so a stack stays square at the joins |
space-24, space-16 | Card | Padding, 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.
| Key | Action |
|---|---|
| Tab | Moves focus to the plot. |
| ArrowRightArrowLeft | Next or previous category, with its tooltip. In Arabic, ArrowLeft is next. |
| HomeEnd | The first or last category. |
| Escape | Hides the tooltip. |
ARIA
- Every chart also renders a visually hidden table, so the numbers reach screen readers without the picture.
- The card is a
figurewith its title as thefigcaption. - 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
valueFormatfor another format. - Arabic typography
- Title
body-16Semibold; labelsbody-12andnumeric-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.