3

My website uses many external SVG files with their own <style> elements within the SVG files themselves. I have dark mode media queries within the main website CSS and within the SVG files' own styles: @media (prefers-color-scheme:light) and @media (prefers-color-scheme:dark)

On initial load, or a hard refresh, everything works as expected, both the website CSS and the SVG CSS load the appropriate styles depending on whether the system OS is set to dark or light mode.

But, if I switch the mode in the OS, only the website CSS is updated, not the external SVG file's CSS.

So, I need to either hard reload the cached SVG images in full or somehow perhaps just refresh the SVG CSS.

I have an event listener set up in javascript which correctly detects an OS mode change, but I don't know how to trigger the SVGs to refresh when the OS dark/light mode is changed. I've searched high and low and am not sure how to do this, hopefully, in the simplest, most elegant manner.

window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
    // WHAT GOES HERE TO REFRESH SVGs TO CORRECTLY REFLECT OS MODE?
});

window.location.reload(true); does not hard reload cached files. I'm not sure if cache.delete is appropriate here, or how I would use it for force a reload of all SVG images (<img src="whatever.svg"/>). Perhaps there is another method I'm overlooking.

Thank you for any help.

david
  • 243
  • 3
  • 17
  • [Do any of these suggestions help?](https://stackoverflow.com/questions/1077041/refresh-image-with-a-new-one-at-the-same-url) – Paul LeBeau Dec 05 '21 at 08:36
  • Is it an option to have SVG as background images? Because if so, you can use `@media (prefers-color-scheme:light) {svgcontainer { background-image: url(light.svg); }}` and `@media (prefers-color-scheme:dark) { background-image: url(dark.svg); }}` – mrmonsieur Dec 05 '21 at 10:04
  • @PaulLeBeau: I saw that previously, and am trying to figure out how implement it for multiple images using a forEach loop. once I do that and test it, I'll know if a cachebreaker works in this case. – david Dec 05 '21 at 10:14
  • @mrmonsieur: The website is very large and complicated and it would be a monumental task to rebuild tons of icons as background images, plus many are hand-coded by admins within text in a CMS interface and the HTML needs to be as simple and user friendly for them as possible. I need a solution via javascript. Thanks. – david Dec 05 '21 at 10:16
  • Maybe load those SVGs with a Web Component, so global CSS can style them: https://dev.to/dannyengelman/load-file-web-component-add-external-content-to-the-dom-1nd – Danny '365CSI' Engelman Dec 05 '21 at 12:33
  • @danny-365csi-engelman: Thanks, but see my previous reply about the complexity of the site and the need for simple HTML usage in the CMS. – david Dec 06 '21 at 05:09

1 Answers1

2

This does the trick, a few lines of javascript. First, on page load, identify all SVG images and determine the current prefers-color-scheme theme. Then, detect when that theme changes and append a dynamic cachebreaker to those filenames, which forces SVGs to reload.

// Dark mode changes needs cache refresh for external SVG file CSS.
allImg = document.querySelectorAll('img[src$=".svg"');
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
  thisMode = 'dark';
} else {
  thisMode = 'light';
}
window.matchMedia('(prefers-color-scheme: '+ thisMode + ')').addEventListener('change', e => {
  allImg.forEach(img => {
    imgTime = Date.now();
    newImgSrc = img.src + '?' + imgTime;
    img.src = newImgSrc;
  });
});

Updated original answer 12/13/21. Now cachebuster, which forces reload of SVGs, is only called after mode change, not at every page load.

Update 12/19/21: This solution does not work with Safari (there's a known bug whereby Safari doesn't respect the prefers-color-scheme media query within SVG file CSS). For Safari, I wrote extra JS that detects Safari on page load, uses the prefers-color-scheme conditional above, and, if OS is dark mode, replaces all SVG filenames from "image.svg" to "image-dark.svg", calling light or dark versions of each SVG. It also reloads page in Safari when OS dark/light theme changed. If Apple ever fixes the bug, I can go with the simpler solution above.

david
  • 243
  • 3
  • 17
  • There is a downside to this approach of course. Each SVG will be downloaded twice at first page load. If you don't have @david's constraints, it would be better to bake the query string (`"?" + imgTime`) into the original URLs. – Paul LeBeau Dec 06 '21 at 05:49
  • @PaulLeBeau: Yes, with the constraints here, it's not feasible to bake the cachebreakers into the original IMG SRC. The site relies on admins with limited HTML knowledge to manually insert SVGs into CMS text areas. Fortunately, the SVGs are 1kb or smaller, and there's only about 10 unique SVGs max on any given page. – david Dec 06 '21 at 20:20