adiabatic
adiabatic

Reputation: 396

Chart.js with prefers-color-scheme on Safari: scales' color doesn't change

I'm generating an HTML file that has a Chart.js chart in it. When I change the system color scheme from light to dark (or the reverse), the X and Y axes' font color doesn't change, although other colors do change. However, every color is as it should be when I refresh the page, whether the system color scheme is light or dark.

I'm only testing this in Safari 12.1 as it's the only browser out there that supports the prefers-color-scheme media query in a non-beta veresion.

Here's the contents of the <script> element that I'm using:

const white = 'hsl(42, 0%, 90%)';
const black = 'hsl(42, 0%, 10%)';

const perfectPrimaryLight = 'hsl(270, 50%, 40%)';
const perfectSecondaryLight = 'hsla(270, 50%, 40%, .2)';

const myPrimaryLight = 'hsl(0, 50%, 40%)';
const mySecondaryLight = 'hsla(0, 50%, 40%, .2)';

const perfectPrimaryDark = 'hsl(340, 50%, 80%)';
const perfectSecondaryDark = 'hsla(340, 50%, 80%, .2)';

const myPrimaryDark = 'hsl(0, 50%, 80%)';
const mySecondaryDark = 'hsla(0, 50%, 80%, .2)';

Chart.defaults.global.defaultFontFamily = 'system-ui, sans-serif';
Chart.defaults.global.defaultFontSize = 16; 
Chart.defaults.global.defaultFontColor = black;

function updateToLight(chart) {
    chart.options.legend.labels.fontColor = black;

    chart.options.scales.yAxes[0].ticks.fontColor = black;
    chart.options.scales.xAxes[0].ticks.fontColor = black;

    chart.data.datasets[0].borderColor = perfectPrimaryLight;
    chart.data.datasets[0].backgroundColor = perfectSecondaryLight;

    chart.data.datasets[1].borderColor = myPrimaryLight;
    chart.data.datasets[1].backgroundColor = mySecondaryLight;

    
    chart.update();
}

function updateToDark(chart) {
    chart.options.legend.labels.fontColor = white;

    chart.options.scales.yAxes[0].ticks.fontColor = white;
    chart.options.scales.xAxes[0].ticks.fontColor = white;

    chart.data.datasets[0].borderColor = perfectPrimaryDark;
    chart.data.datasets[0].backgroundColor = perfectSecondaryDark;

    chart.data.datasets[1].borderColor = myPrimaryDark;
    chart.data.datasets[1].backgroundColor = mySecondaryDark;

    
    chart.update();
}

const ctx = document.getElementById('myChart').getContext('2d');
const data = {
    datasets: [{
        label: 'Perfect-calibration dataset',
        data: [{"x":0.01,"y":0.01},{"x":0.05,"y":0.05},{"x":0.10,"y":0.10},{"x":0.15,"y":0.15},{"x":0.20,"y":0.20},{"x":0.25,"y":0.25},{"x":0.30,"y":0.30},{"x":0.35,"y":0.35},{"x":0.40,"y":0.40},{"x":0.45,"y":0.45},{"x":0.50,"y":0.50},{"x":0.55,"y":0.55},{"x":0.60,"y":0.60},{"x":0.65,"y":0.65},{"x":0.70,"y":0.70},{"x":0.75,"y":0.75},{"x":0.80,"y":0.80},{"x":0.85,"y":0.85},{"x":0.90,"y":0.90},{"x":0.95,"y":0.95},{"x":0.99,"y":0.99}],
    }, {
        label: 'My predictions',
        data: [{"x":0.10,"y":0.00},{"x":0.50,"y":0.25},{"x":0.60,"y":0.00},{"x":0.70,"y":0.62},{"x":0.80,"y":0.25},{"x":0.90,"y":0.68},{"x":0.95,"y":0.83}]
    }]
};

const options = {
    title: {
        display: false,
        text: 'A Chart.js Scatter Chart'
    },
    scales: {
        yAxes: [{
            ticks: {
                min: 0,
                max: 1
            }
        }],
        xAxes: [{
            ticks: {
                min: 0,
                max: 1
            }
        }]
    }
}

const c = new Chart(ctx, {
    type: 'scatter',
    data: data,
    options: options
});

const query = window.matchMedia("(prefers-color-scheme: dark)");

let darkMode = false;

function toggleDark() {
    darkMode = !darkMode;
    if (darkMode) {
        document.body.style.background = 'black';
        updateToDark(c);
    } else {
        document.body.style.background = 'white';
        updateToLight(c);    
    }
}

updateToLight(c);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.bundle.min.js"></script>
<canvas id="myChart" onclick="toggleDark()"></canvas>

Here's how to see this in action:

  1. Set the system color scheme to light.
  2. Open the file in Safari.
  3. Notice the black axes labels and the purple perfect-calibration dataset label color.
  4. Toggle the system color scheme to dark. (In the above sample, simply click the chart.)
  5. Notice how the axes labels are still black, but now on a black background.
  6. Notice how the perfect-calibration dataset color has changed to a much more red color.
  7. Notice how the labels for the datasets are now white.

Now, to see all this in reverse. Let's reload the page while the system is set to dark mode. Notice that the axis label colors are now white.


  1. Set the system color scheme back to light.
  2. Notice how the axis labels are almost-white on a white background.

Any ideas why chart.options.scales.yAxes[0].ticks.fontColor isn't changing while the other properties are?

Upvotes: 4

Views: 504

Answers (1)

dumbass
dumbass

Reputation: 27214

This was a bug in Chart.js 2.8.0. It was fixed in version 2.9.0:

const white = 'hsl(42, 0%, 90%)';
const black = 'hsl(42, 0%, 10%)';

const perfectPrimaryLight = 'hsl(270, 50%, 40%)';
const perfectSecondaryLight = 'hsla(270, 50%, 40%, .2)';

const myPrimaryLight = 'hsl(0, 50%, 40%)';
const mySecondaryLight = 'hsla(0, 50%, 40%, .2)';

const perfectPrimaryDark = 'hsl(340, 50%, 80%)';
const perfectSecondaryDark = 'hsla(340, 50%, 80%, .2)';

const myPrimaryDark = 'hsl(0, 50%, 80%)';
const mySecondaryDark = 'hsla(0, 50%, 80%, .2)';

Chart.defaults.global.defaultFontFamily = 'system-ui, sans-serif';
Chart.defaults.global.defaultFontSize = 16; 
Chart.defaults.global.defaultFontColor = black;

function updateToLight(chart) {
    chart.options.legend.labels.fontColor = black;

    chart.options.scales.yAxes[0].ticks.fontColor = black;
    chart.options.scales.xAxes[0].ticks.fontColor = black;

    chart.data.datasets[0].borderColor = perfectPrimaryLight;
    chart.data.datasets[0].backgroundColor = perfectSecondaryLight;

    chart.data.datasets[1].borderColor = myPrimaryLight;
    chart.data.datasets[1].backgroundColor = mySecondaryLight;

    
    chart.update();
}

function updateToDark(chart) {
    chart.options.legend.labels.fontColor = white;

    chart.options.scales.yAxes[0].ticks.fontColor = white;
    chart.options.scales.xAxes[0].ticks.fontColor = white;

    chart.data.datasets[0].borderColor = perfectPrimaryDark;
    chart.data.datasets[0].backgroundColor = perfectSecondaryDark;

    chart.data.datasets[1].borderColor = myPrimaryDark;
    chart.data.datasets[1].backgroundColor = mySecondaryDark;

    
    chart.update();
}

const ctx = document.getElementById('myChart').getContext('2d');
const data = {
    datasets: [{
        label: 'Perfect-calibration dataset',
        data: [{"x":0.01,"y":0.01},{"x":0.05,"y":0.05},{"x":0.10,"y":0.10},{"x":0.15,"y":0.15},{"x":0.20,"y":0.20},{"x":0.25,"y":0.25},{"x":0.30,"y":0.30},{"x":0.35,"y":0.35},{"x":0.40,"y":0.40},{"x":0.45,"y":0.45},{"x":0.50,"y":0.50},{"x":0.55,"y":0.55},{"x":0.60,"y":0.60},{"x":0.65,"y":0.65},{"x":0.70,"y":0.70},{"x":0.75,"y":0.75},{"x":0.80,"y":0.80},{"x":0.85,"y":0.85},{"x":0.90,"y":0.90},{"x":0.95,"y":0.95},{"x":0.99,"y":0.99}],
    }, {
        label: 'My predictions',
        data: [{"x":0.10,"y":0.00},{"x":0.50,"y":0.25},{"x":0.60,"y":0.00},{"x":0.70,"y":0.62},{"x":0.80,"y":0.25},{"x":0.90,"y":0.68},{"x":0.95,"y":0.83}]
    }]
};

const options = {
    title: {
        display: false,
        text: 'A Chart.js Scatter Chart'
    },
    scales: {
        yAxes: [{
            ticks: {
                min: 0,
                max: 1
            }
        }],
        xAxes: [{
            ticks: {
                min: 0,
                max: 1
            }
        }]
    }
}

const c = new Chart(ctx, {
    type: 'scatter',
    data: data,
    options: options
});

const query = window.matchMedia("(prefers-color-scheme: dark)");

let darkMode = false;

function toggleDark() {
    darkMode = !darkMode;
    if (darkMode) {
        document.body.style.background = 'black';
        updateToDark(c);
    } else {
        document.body.style.background = 'white';
        updateToLight(c);    
    }
}

updateToLight(c);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.0/Chart.bundle.min.js"></script>
<canvas id="myChart" onclick="toggleDark()"></canvas>

Version 2.9.0 is also obsolete now, though. Adapting the code sample to 3.8.0 would involve some work, though.

Upvotes: 1

Related Questions