Skip to content

feat(chartkit-core): let one chart name its own colors - #176

Merged
kkukielka merged 3 commits into
mainfrom
feat/chartkit-per-chart-colors
Aug 26, 2026
Merged

feat(chartkit-core): let one chart name its own colors#176
kkukielka merged 3 commits into
mainfrom
feat/chartkit-per-chart-colors

Conversation

@kkukielka

Copy link
Copy Markdown
Contributor

Palettes live in the site's CSS, which is the right default: every chart stays
on brand through a restyle and nobody types a hex code into a CMS. It also left
the chart that needs one series picked out with no way to say so - the last item
in Chartkit's Next.

The shape

options.colors is sparse. null or a missing entry leaves that index to the
stylesheet, so highlighting the second of five series is [null, '#c00'] rather
than five colors that would then be frozen against the next restyle.

options: { colors: [null, '#d1373b'] }

An index means whatever the legend is naming - a series in a bar, line or area
chart, a slice in a pie or donut - so one Palette function covers every chart
type and the legend swatches cannot drift from the marks.

The one decision worth arguing about

A color named in the spec is written into the markup literally, not as a
var() fallback, so it beats the page's stylesheet. That is the opposite of
Chartkit's default and it is the point: a chart naming a color is overruling the
house style deliberately, and a house style that could silently repaint it would
make the option useless in the case it exists for.

Validation

Values are checked against an allowlist of CSS colors. Attributes are already
escaped, so this is not about markup injection - url(https://…) is a valid
paint value that would make a chart fetch from a third party as it renders, and
a typo is better reported than drawn as black.

Editor

A swatch per series under the data grid. Two details carry the "your stylesheet
is still in charge" decision:

  • Only entries someone sets are stored - setColor pads with null, trims
    trailing holes and drops the array when the last override goes, so an
    experimented-with chart ends up identical to one that never had colors.
  • A native color input has no "unset", so each set swatch gets a cross that
    hands that series back to the site. Without it the first click is
    irreversible.

Verified

110 tests across core and editor, including the sparse-array trimming and the
rejected paint values.

The gallery gains a second copy of one fixture rendered inside a page that
does define a palette. Every other card renders against no stylesheet at
all, so without that control a color that quietly stopped deferring to the site
would look identical. Checked in the browser:

  • unthemed card: baseline blue (fallback), highlight #d1373b, other salmon
  • themed card: baselines take the site's teal and plum, highlight keeps its red
  • donut: colors key to slices, legend agrees with the ring

And in the Strapi admin, live: setting a swatch repaints only that series and
leaves the others as var(--chart-series-N, …); the cross appears only on a set
entry and clearing the last one removes colors from the spec entirely.

Versions

Three packages gained the feature and are committed under their own scopes, so
nx release gives each a minor rather than an indirect patch - core 0.4.0,
editor 0.3.0, plugin 0.3.0. The three renderers take a patch as dependents,
which is right: they pass the SVG through.

Palettes live in the site's CSS, which is the right default: every chart stays
on brand through a restyle and nobody types a hex code into a CMS. It also
left the chart that needs one series picked out with no way to say so.

`options.colors` is that escape hatch. It is sparse - `null` or a missing entry
leaves that index to the stylesheet - so highlighting the second of five series
costs `[null, '#c00']` rather than five colors that would then be frozen
against the next restyle.

A color named here is written into the markup literally rather than as a
`var()` fallback, so it beats the page. That is the opposite of the default and
it is the point: a chart naming a color is overruling the house style
deliberately, and a house style that could silently repaint it would make the
option useless in the one case it exists for.

An index means whatever the legend is naming - a series in a bar, line or area
chart, a slice in a pie or donut - so one `Palette` function covers every chart
type and the legend swatches cannot drift from the marks.

Values are validated against an allowlist of CSS colors. Attributes are
escaped, so this is not about markup injection: `url(https://…)` is a valid
paint value that would make a chart fetch from a third party as it renders, and
a typo is better reported than drawn as black.

Two fixtures cover it, and the gallery gains a second copy of one rendered
inside a page that defines a palette - every other card renders against no
stylesheet at all, so without it a color that quietly stopped deferring to the
site would look identical.
A swatch per series - per slice, on a pie or donut - under the data grid.
Whatever the legend names is what gets a color, so the list cannot offer three
colors for a pie of eight slices.

Two details carry the design decision that the site's palette is still the
default:

- Only the entries someone sets are stored. `setColor` pads with `null`, trims
  trailing holes, and drops the array entirely when the last override goes, so
  a chart that was experimented with ends up identical to one that never had
  colors rather than carrying `[null, null, null]` forever.
- A native color input has no "unset" - it always reports a hex - so each set
  swatch gets a cross beside it that hands that series back to the site. Without
  it the first click would be irreversible.

The swatch for an unset entry shows the fallback inside `--chart-series-N`,
since a color input cannot take a `var()` and that fallback is what the chart
actually draws on a page with no palette - which the admin is.
The plugin ships the editor, so this is a capability its users gain: a Colors
section under the data grid, with the note that leaving a swatch alone keeps
your stylesheet in charge.

"No per-chart color overrides" leaves the honest-about-the-edges list, and the
roadmap's Next moves on to reading numbers from a collection - with the "the
reason this is not next" line rewritten, since it now is.
@github-actions github-actions Bot added documentation Improvements or additions to documentation pkg: chartkit @qkix/strapi-plugin-chartkit — the Strapi chart field pkg: chartkit-core @qkix/chartkit-core — chart spec and SVG rendering pkg: chartkit-editor @qkix/chartkit-editor — the chart editing UI pkg: examples The Strapi, React, Astro and Nuxt example apps labels Aug 26, 2026
@kkukielka
kkukielka merged commit e13e02e into main Aug 26, 2026
3 checks passed
@kkukielka
kkukielka deleted the feat/chartkit-per-chart-colors branch August 26, 2026 09:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation pkg: chartkit @qkix/strapi-plugin-chartkit — the Strapi chart field pkg: chartkit-core @qkix/chartkit-core — chart spec and SVG rendering pkg: chartkit-editor @qkix/chartkit-editor — the chart editing UI pkg: examples The Strapi, React, Astro and Nuxt example apps

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant