diff --git a/docs/general/colors-plugin-palette.png b/docs/general/colors-plugin-palette.png
new file mode 100644
index 00000000000..ac6994ee272
Binary files /dev/null and b/docs/general/colors-plugin-palette.png differ
diff --git a/docs/general/colors.md b/docs/general/colors.md
index c9b101693ca..913cf548207 100644
--- a/docs/general/colors.md
+++ b/docs/general/colors.md
@@ -1,55 +1,142 @@
# Colors
-When supplying colors to Chart options, you can use a number of formats. You can specify the color as a string in hexadecimal, RGB, or HSL notations. If a color is needed, but not specified, Chart.js will use the global default color. There are 3 color options, stored at `Chart.defaults`, to set:
+Charts support three color options:
+* for geometric elements, you can change *background* and *border* colors;
+* for textual elements, you can change the *font* color.
-| Name | Type | Default | Description
-| ---- | ---- | ------- | -----------
-| `backgroundColor` | `Color` | `rgba(0, 0, 0, 0.1)` | Background color.
-| `borderColor` | `Color` | `rgba(0, 0, 0, 0.1)` | Border color.
-| `color` | `Color` | `#666` | Font color.
+Also, you can change the whole [canvas background](.../configuration/canvas-background.html).
-You can also pass a [CanvasGradient](https://developer.mozilla.org/en-US/docs/Web/API/CanvasGradient) object. You will need to create this before passing to the chart, but using it you can achieve some interesting effects.
+## Default colors
-## Patterns and Gradients
+If a color is not specified, a global default color from `Chart.defaults` is used:
+
+| Name | Type | Description | Default value
+| ---- | ---- | ----------- | -------------
+| `backgroundColor` | [`Color`](../api/#color) | Background color | `rgba(0, 0, 0, 0.1)`
+| `borderColor` | [`Color`](../api/#color) | Border color | `rgba(0, 0, 0, 0.1)`
+| `color` | [`Color`](../api/#color) | Font color | `#666`
+
+You can reset default colors by updating these properties of `Chart.defaults`:
+
+```javascript
+Chart.defaults.backgroundColor = '#9BD0F5';
+Chart.defaults.borderColor = '#36A2EB';
+Chart.defaults.color = '#000';
+```
+
+### Per-dataset color settings
+
+If your chart has multiple datasets, using default colors would make individual datasets indistiguishable. In that case, you can set `backgroundColor` and `borderColor` for each dataset:
+
+```javascript
+const data = {
+ labels: ['A', 'B', 'C'],
+ datasets: [
+ {
+ label: 'Dataset 1',
+ data: [1, 2, 3],
+ borderColor: '#36A2EB',
+ backgroundColor: '#9BD0F5',
+ },
+ {
+ label: 'Dataset 2',
+ data: [2, 3, 4],
+ borderColor: '#FF6384',
+ backgroundColor: '#FFB1C1',
+ }
+ ]
+};
+```
+
+However, setting colors for each dataset might require additional work that you'd rather not do. In that case, consider using the following plugins with pre-defined or generated palettes.
+
+### Default color palette
+
+If you don't have any preference for colors, you can use the built-in `Colors` plugin. It will cycle through a palette of seven Chart.js brand colors:
+
+
+
+
+
+
+
+All you need is to import and register the plugin:
-An alternative option is to pass a [CanvasPattern](https://developer.mozilla.org/en-US/docs/Web/API/CanvasPattern) or [CanvasGradient](https://developer.mozilla.org/en/docs/Web/API/CanvasGradient) object instead of a string colour.
+```javascript
+import { Colors } from 'chart.js';
+
+Chart.register(Colors);
+```
+
+:::tip Note
+
+If you are using the UMD version of Chart.js, this plugin will be enabled by default. You can disable it by setting the `enabled` option to `false`:
+
+```js
+const options = {
+ plugins: {
+ colors: {
+ enabled: false
+ }
+ }
+};
+```
+
+:::
+
+### Advanced color palettes
-For example, if you wanted to fill a dataset with a pattern from an image you could do the following.
+See the [awesome list](https://github.com/chartjs/awesome#plugins) for plugins that would give you more flexibility defining color palettes.
+
+## Color formats
+
+You can specify the color as a string in either of the following notations:
+
+| Notation | Example | Example with transparency
+| -------- | ------- | -------------------------
+| [Hexademical](https://developer.mozilla.org/en-US/docs/Web/CSS/hex-color) | `#36A2EB` | `#36A2EB80`
+| [RGB](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/rgb) or [RGBA](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/rgba) | `rgb(54, 162, 235)` | `rgba(54, 162, 235, 0.5)`
+| [HSL](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl) or [HSLA](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsla) | `hsl(204, 82%, 57%)` | `hsla(204, 82%, 57%, 0.5)`
+
+Alternatively, you can pass a [CanvasPattern](https://developer.mozilla.org/en-US/docs/Web/API/CanvasPattern) or [CanvasGradient](https://developer.mozilla.org/en/docs/Web/API/CanvasGradient) object instead of a string color to achieve some interesting effects.
+
+## Patterns and Gradients
+
+For example, you can fill a dataset with a pattern from an image.
```javascript
const img = new Image();
img.src = 'https://example.com/my_image.png';
-img.onload = function() {
- const ctx = document.getElementById('canvas').getContext('2d');
- const fillPattern = ctx.createPattern(img, 'repeat');
-
- const chart = new Chart(ctx, {
- data: {
- labels: ['Item 1', 'Item 2', 'Item 3'],
- datasets: [{
- data: [10, 20, 30],
- backgroundColor: fillPattern
- }]
- }
- });
+img.onload = () => {
+ const ctx = document.getElementById('canvas').getContext('2d');
+ const fillPattern = ctx.createPattern(img, 'repeat');
+
+ const chart = new Chart(ctx, {
+ data: {
+ labels: ['Item 1', 'Item 2', 'Item 3'],
+ datasets: [{
+ data: [10, 20, 30],
+ backgroundColor: fillPattern
+ }]
+ }
+ });
};
```
+Pattern fills can help viewers with vision deficiencies (e.g., color-blindness or partial sight) [more easily understand your data](http://betweentwobrackets.com/data-graphics-and-colour-vision/).
-Using pattern fills for data graphics can help viewers with vision deficiencies (e.g. color-blindness or partial sight) to [more easily understand your data](http://betweentwobrackets.com/data-graphics-and-colour-vision/).
-
-Using the [Patternomaly](https://github.com/ashiguruma/patternomaly) library you can generate patterns to fill datasets.
+You can use the [Patternomaly](https://github.com/ashiguruma/patternomaly) library to generate patterns to fill datasets:
```javascript
const chartData = {
- datasets: [{
- data: [45, 25, 20, 10],
- backgroundColor: [
- pattern.draw('square', '#ff6384'),
- pattern.draw('circle', '#36a2eb'),
- pattern.draw('diamond', '#cc65fe'),
- pattern.draw('triangle', '#ffce56')
- ]
- }],
- labels: ['Red', 'Blue', 'Purple', 'Yellow']
+ datasets: [{
+ data: [45, 25, 20, 10],
+ backgroundColor: [
+ pattern.draw('square', '#ff6384'),
+ pattern.draw('circle', '#36a2eb'),
+ pattern.draw('diamond', '#cc65fe'),
+ pattern.draw('triangle', '#ffce56')
+ ]
+ }],
+ labels: ['Red', 'Blue', 'Purple', 'Yellow']
};
```
diff --git a/src/plugins/index.js b/src/plugins/index.js
index eb76545ed4f..e9a0be35aa6 100644
--- a/src/plugins/index.js
+++ b/src/plugins/index.js
@@ -1,3 +1,4 @@
+export {default as Colors} from './plugin.colors';
export {default as Decimation} from './plugin.decimation';
export {default as Filler} from './plugin.filler';
export {default as Legend} from './plugin.legend';
diff --git a/src/plugins/plugin.colors.ts b/src/plugins/plugin.colors.ts
new file mode 100644
index 00000000000..8d4766667d4
--- /dev/null
+++ b/src/plugins/plugin.colors.ts
@@ -0,0 +1,106 @@
+import type {Chart, ChartDataset} from '../types';
+
+export interface ColorsPluginOptions {
+ enabled?: boolean;
+}
+
+type DatasetColorizer = (dataset: ChartDataset, i: number) => void;
+
+interface ColorsDescriptor {
+ backgroundColor?: unknown;
+ borderColor?: unknown;
+}
+
+const BORDER_COLORS = [
+ 'rgb(54, 162, 235)', // blue
+ 'rgb(255, 99, 132)', // red
+ 'rgb(255, 159, 64)', // orange
+ 'rgb(255, 205, 86)', // yellow
+ 'rgb(75, 192, 192)', // green
+ 'rgb(153, 102, 255)', // purple
+ 'rgb(201, 203, 207)' // grey
+];
+
+// Border colors with 50% transparency
+const BACKGROUND_COLORS = /* #__PURE__ */ BORDER_COLORS.map(color => color.replace('rgb(', 'rgba(').replace(')', ', 0.5)'));
+
+function getBorderColor(i: number) {
+ return BORDER_COLORS[i % BORDER_COLORS.length];
+}
+
+function getBackgroundColor(i: number) {
+ return BACKGROUND_COLORS[i % BACKGROUND_COLORS.length];
+}
+
+function createDefaultDatasetColorizer() {
+ return (dataset: ChartDataset, i: number) => {
+ dataset.borderColor = getBorderColor(i);
+ dataset.backgroundColor = getBackgroundColor(i);
+ };
+}
+
+function createDoughnutDatasetColorizer() {
+ let i = 0;
+
+ return (dataset: ChartDataset) => {
+ dataset.backgroundColor = dataset.data.map(() => getBorderColor(i++));
+ };
+}
+
+function createPolarAreaDatasetColorizer() {
+ let i = 0;
+
+ return (dataset: ChartDataset) => {
+ dataset.backgroundColor = dataset.data.map(() => getBackgroundColor(i++));
+ };
+}
+
+function containsColorsDefinitions(
+ descriptors: ColorsDescriptor[] | Record
+) {
+ let k: number | string;
+
+ for (k in descriptors) {
+ if (descriptors[k].borderColor || descriptors[k].backgroundColor) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+export default {
+ id: 'colors',
+
+ defaults: {
+ enabled: true,
+ },
+
+ beforeLayout(chart: Chart, _args, options: ColorsPluginOptions) {
+ if (!options.enabled) {
+ return;
+ }
+
+ const {
+ type,
+ options: {elements},
+ data: {datasets}
+ } = chart.config;
+
+ if (containsColorsDefinitions(datasets) || elements && containsColorsDefinitions(elements)) {
+ return;
+ }
+
+ let colorizer: DatasetColorizer;
+
+ if (type === 'doughnut') {
+ colorizer = createDoughnutDatasetColorizer();
+ } else if (type === 'polarArea') {
+ colorizer = createPolarAreaDatasetColorizer();
+ } else {
+ colorizer = createDefaultDatasetColorizer();
+ }
+
+ datasets.forEach(colorizer);
+ }
+};
diff --git a/test/fixtures/plugin.colors/bar.js b/test/fixtures/plugin.colors/bar.js
new file mode 100644
index 00000000000..0ca99e3b970
--- /dev/null
+++ b/test/fixtures/plugin.colors/bar.js
@@ -0,0 +1,36 @@
+module.exports = {
+ config: {
+ type: 'bar',
+ data: {
+ labels: [0, 1, 2, 3, 4, 5],
+ datasets: [
+ {
+ data: [0, 5, 10, null, -10, -5],
+ },
+ {
+ data: [10, 2, 3, null, 10, 5]
+ }
+ ]
+ },
+ options: {
+ scales: {
+ x: {
+ ticks: {
+ display: false,
+ }
+ },
+ y: {
+ ticks: {
+ display: false,
+ }
+ }
+ },
+ plugins: {
+ legend: false,
+ colors: {
+ enabled: true
+ }
+ }
+ }
+ }
+};
diff --git a/test/fixtures/plugin.colors/bar.png b/test/fixtures/plugin.colors/bar.png
new file mode 100644
index 00000000000..72c10f05ddd
Binary files /dev/null and b/test/fixtures/plugin.colors/bar.png differ
diff --git a/test/fixtures/plugin.colors/bubble.js b/test/fixtures/plugin.colors/bubble.js
new file mode 100644
index 00000000000..3e7c25a8192
--- /dev/null
+++ b/test/fixtures/plugin.colors/bubble.js
@@ -0,0 +1,32 @@
+module.exports = {
+ config: {
+ type: 'bubble',
+ data: {
+ datasets: [{
+ data: [{x: 12, y: 54, r: 22.4}]
+ }, {
+ data: [{x: 18, y: 38, r: 25}]
+ }]
+ },
+ options: {
+ scales: {
+ x: {
+ ticks: {
+ display: false,
+ }
+ },
+ y: {
+ ticks: {
+ display: false,
+ }
+ }
+ },
+ plugins: {
+ legend: false,
+ colors: {
+ enabled: true
+ }
+ }
+ }
+ }
+};
diff --git a/test/fixtures/plugin.colors/bubble.png b/test/fixtures/plugin.colors/bubble.png
new file mode 100644
index 00000000000..539eed1cc7c
Binary files /dev/null and b/test/fixtures/plugin.colors/bubble.png differ
diff --git a/test/fixtures/plugin.colors/doughnut.js b/test/fixtures/plugin.colors/doughnut.js
new file mode 100644
index 00000000000..077ad624a6f
--- /dev/null
+++ b/test/fixtures/plugin.colors/doughnut.js
@@ -0,0 +1,23 @@
+module.exports = {
+ config: {
+ type: 'doughnut',
+ data: {
+ datasets: [
+ {
+ data: [0, 2, 4, null, 6, 8]
+ },
+ {
+ data: [5, 1, 6, 2, null, 9]
+ }
+ ]
+ },
+ options: {
+ plugins: {
+ legend: false,
+ colors: {
+ enabled: true
+ }
+ }
+ }
+ }
+};
diff --git a/test/fixtures/plugin.colors/doughnut.png b/test/fixtures/plugin.colors/doughnut.png
new file mode 100644
index 00000000000..d6c28c6c114
Binary files /dev/null and b/test/fixtures/plugin.colors/doughnut.png differ
diff --git a/test/fixtures/plugin.colors/line.js b/test/fixtures/plugin.colors/line.js
new file mode 100644
index 00000000000..9658956a495
--- /dev/null
+++ b/test/fixtures/plugin.colors/line.js
@@ -0,0 +1,36 @@
+module.exports = {
+ config: {
+ type: 'line',
+ data: {
+ labels: [0, 1, 2, 3, 4, 5],
+ datasets: [
+ {
+ data: [0, 5, 10, null, -10, -5],
+ },
+ {
+ data: [10, 2, 3, null, 10, 5]
+ }
+ ]
+ },
+ options: {
+ scales: {
+ x: {
+ ticks: {
+ display: false,
+ }
+ },
+ y: {
+ ticks: {
+ display: false,
+ }
+ }
+ },
+ plugins: {
+ legend: false,
+ colors: {
+ enabled: true
+ }
+ }
+ }
+ }
+};
diff --git a/test/fixtures/plugin.colors/line.png b/test/fixtures/plugin.colors/line.png
new file mode 100644
index 00000000000..b9ce7585ec8
Binary files /dev/null and b/test/fixtures/plugin.colors/line.png differ
diff --git a/test/fixtures/plugin.colors/polarArea.js b/test/fixtures/plugin.colors/polarArea.js
new file mode 100644
index 00000000000..37e4d4af3a4
--- /dev/null
+++ b/test/fixtures/plugin.colors/polarArea.js
@@ -0,0 +1,28 @@
+module.exports = {
+ config: {
+ type: 'polarArea',
+ data: {
+ labels: [0, 1, 2, 3, 4, 5],
+ datasets: [
+ {
+ data: [0, 2, 4, null, 6, 8]
+ }
+ ]
+ },
+ options: {
+ scales: {
+ r: {
+ ticks: {
+ display: false
+ }
+ }
+ },
+ plugins: {
+ legend: false,
+ colors: {
+ enabled: true
+ }
+ }
+ }
+ }
+};
diff --git a/test/fixtures/plugin.colors/polarArea.png b/test/fixtures/plugin.colors/polarArea.png
new file mode 100644
index 00000000000..8671ff67aaa
Binary files /dev/null and b/test/fixtures/plugin.colors/polarArea.png differ
diff --git a/test/fixtures/plugin.colors/radar.js b/test/fixtures/plugin.colors/radar.js
new file mode 100644
index 00000000000..d5fd4318ec7
--- /dev/null
+++ b/test/fixtures/plugin.colors/radar.js
@@ -0,0 +1,34 @@
+module.exports = {
+ config: {
+ type: 'radar',
+ data: {
+ labels: [0, 1, 2, 3, 4, 5],
+ datasets: [
+ {
+ data: [0, 5, 10, null, -10, -5]
+ },
+ {
+ data: [4, -5, -10, null, 10, 5]
+ }
+ ]
+ },
+ options: {
+ scales: {
+ r: {
+ ticks: {
+ display: false
+ },
+ pointLabels: {
+ display: false,
+ }
+ }
+ },
+ plugins: {
+ legend: false,
+ colors: {
+ enabled: true
+ }
+ }
+ }
+ }
+};
diff --git a/test/fixtures/plugin.colors/radar.png b/test/fixtures/plugin.colors/radar.png
new file mode 100644
index 00000000000..5a39a08ebe9
Binary files /dev/null and b/test/fixtures/plugin.colors/radar.png differ
diff --git a/test/fixtures/plugin.colors/scatter.js b/test/fixtures/plugin.colors/scatter.js
new file mode 100644
index 00000000000..8822de1d736
--- /dev/null
+++ b/test/fixtures/plugin.colors/scatter.js
@@ -0,0 +1,37 @@
+module.exports = {
+ config: {
+ type: 'scatter',
+ data: {
+ datasets: [{
+ data: [{x: 10, y: 15}, {x: 15, y: 10}],
+ pointRadius: 10,
+ showLine: true,
+ label: 'dataset1'
+ }, {
+ data: [{x: 20, y: 45}, {x: 5, y: 15}],
+ pointRadius: 20,
+ label: 'dataset2'
+ }],
+ },
+ options: {
+ scales: {
+ x: {
+ ticks: {
+ display: false,
+ }
+ },
+ y: {
+ ticks: {
+ display: false,
+ }
+ }
+ },
+ plugins: {
+ legend: false,
+ colors: {
+ enabled: true
+ },
+ }
+ }
+ }
+};
diff --git a/test/fixtures/plugin.colors/scatter.png b/test/fixtures/plugin.colors/scatter.png
new file mode 100644
index 00000000000..12534c720b2
Binary files /dev/null and b/test/fixtures/plugin.colors/scatter.png differ
diff --git a/test/index.js b/test/index.js
index 8f1b7ced3c6..f250fb6a889 100644
--- a/test/index.js
+++ b/test/index.js
@@ -22,10 +22,15 @@ jasmine.triggerMouseEvent = triggerMouseEvent;
// more stable test results.
window.moment.tz.setDefault('Etc/UTC');
-beforeEach(function() {
+beforeAll(() => {
+ // Disable colors plugin for tests.
+ window.Chart.defaults.plugins.colors.enabled = false;
+});
+
+beforeEach(() => {
addMatchers();
});
-afterEach(function() {
+afterEach(() => {
releaseCharts();
});
diff --git a/test/specs/plugin.colors.tests.js b/test/specs/plugin.colors.tests.js
new file mode 100644
index 00000000000..df5ff8fb993
--- /dev/null
+++ b/test/specs/plugin.colors.tests.js
@@ -0,0 +1,3 @@
+describe('Plugin.colors', () => {
+ describe('auto', jasmine.fixture.specs('plugin.colors'));
+});
diff --git a/types/index.d.ts b/types/index.d.ts
index 2e0369a093a..3c55cf0104d 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -3651,6 +3651,7 @@ export interface ChartConfigurationCustomTypesPerDataset<
TData = DefaultDataPoint,
TLabel = unknown
> {
+ type: TType;
data: ChartDataCustomTypesPerDataset;
options?: ChartOptions;
plugins?: Plugin[];