Skip to content

Instantly share code, notes, and snippets.

@bartkl
Created January 21, 2024 19:11
Show Gist options
  • Save bartkl/0134c6ce4a5d532464b40d3bfbcb64e4 to your computer and use it in GitHub Desktop.
Save bartkl/0134c6ce4a5d532464b40d3bfbcb64e4 to your computer and use it in GitHub Desktop.
Quartz Excalidraw render
diff --git a/quartz/components/scripts/darkmode.inline.ts b/quartz/components/scripts/darkmode.inline.ts
index c42a367..06767b5 100644
--- a/quartz/components/scripts/darkmode.inline.ts
+++ b/quartz/components/scripts/darkmode.inline.ts
@@ -1,5 +1,6 @@
-const userPref = window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark"
-const currentTheme = localStorage.getItem("theme") ?? userPref
+import { getUserPreferredColorScheme, renderExcalidrawLinks } from "./util"
+
+const currentTheme = localStorage.getItem("theme") ?? getUserPreferredColorScheme()
document.documentElement.setAttribute("saved-theme", currentTheme)
document.addEventListener("nav", () => {
@@ -11,6 +12,7 @@ document.addEventListener("nav", () => {
document.documentElement.setAttribute("saved-theme", "light")
localStorage.setItem("theme", "light")
}
+ renderExcalidrawLinks(localStorage.getItem("theme"))
}
// Darkmode toggle
diff --git a/quartz/components/scripts/util.ts b/quartz/components/scripts/util.ts
index 5fcabad..444e145 100644
--- a/quartz/components/scripts/util.ts
+++ b/quartz/components/scripts/util.ts
@@ -23,3 +23,31 @@ export function removeAllChildren(node: HTMLElement) {
node.removeChild(node.firstChild)
}
}
+
+export function renderExcalidrawLinks(theme: "dark" | "light") {
+ let currentTheme = theme == "dark" ? "light" : "dark"
+ Object.values(document.getElementsByTagName("img")).forEach(img => {
+ if (img.src.endsWith(`.excalidraw.${currentTheme}.svg`)) {
+ let srcParts = img.src.split(".")
+ srcParts.splice(-2, 1, theme)
+ img.src = srcParts.join(".")
+ }
+ })
+}
+
+export function getUserPreferredColorScheme() {
+ return window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark"
+}
+
+// Have SVG images in the article adhere to the correct color scheme.
+document.addEventListener("nav", (e) => {
+ let theme = localStorage.getItem("theme") ?? getUserPreferredColorScheme()
+ Object.values(document.getElementsByTagName("article")[0]
+ .getElementsByTagName("a")).forEach(a => {
+ if (a.href.endsWith(".excalidraw")) {
+ let img = document.createElement("img")
+ img.src = `${a.href}.${theme}.svg`
+ a.replaceWith(img)
+ }
+ })
+})
@bartkl
Copy link
Author

bartkl commented Sep 27, 2024

The version of the code I'm currently using can be found in my repo:

https://github.com/bartkl/bartkl.github.io/blob/v4/quartz/components/RenderExcalidraw.tsx

This component is configured in the quartz.layout.tsx file in the header of the page I believe. Anyways, you can look that up in the repo as well.

Sadly I'm very busy currently so I don't have the time to help making this more accessible or make improvements/enhancements.

@jacobeverist
Copy link

@bartkl Thanks for the update. I pointed to this gist in an ongoing quartz pull request.

jackyzha0/quartz#1389

I think your example helped with the light/dark switching. I'm not really a quartz developer so I can't say much else about the code.

@Guillaume-Fernandez
Copy link

Thanks for this code.

I was a little disappointed by your approach. I'm saving all my images in light mode and let the browser revert colors with css filter.

darkmode.inline.ts

// https://francoisbest.com/posts/2020/dark-mode-for-excalidraw
function applyDarkModeFilter(): void {
  const savedTheme = document.documentElement.getAttribute("saved-theme");
  const images: HTMLCollectionOf<HTMLImageElement> = document.getElementsByTagName('img');

  for (let i = 0; i < images.length; i++) {
    const image = images[i];

    if (image.src && image.src.endsWith('excalidraw.svg')) {
      // It consider image as light by default
      if (savedTheme === "dark")
        image.classList.add('image-dark');
      else
        image.classList.remove('image-dark');
    }
  }
}
document.addEventListener('DOMContentLoaded', applyDarkModeFilter);
document.addEventListener('themechange', applyDarkModeFilter);

darkmode.scss

.image-dark {
  filter: invert(100%) hue-rotate(180deg);
}

@bartkl
Copy link
Author

bartkl commented Feb 5, 2025

@Guillaume-Fernandez that's an interesting alternative way of doing it. I'm not mainly a front-end developer, so I wouldn't have thought of a CSS solution like that :).

However, I don't think if I prefer your solution:

  1. You are moving rendering logic to the client (the color inversion) and I'm not sure if this could become noticable on slower clients for example for bigger images. (This might be nitpicky, just thinking out loud.)
  2. I'm not sure if simply inverting the colors is the best way to get a dark mode flavour of the images. I honestly doubt that's all the Excalidraw dark mode rendering does. Anyway, I rather rely on them for implementing that logic.

@Guillaume-Fernandez
Copy link

@bartkl
You made me curious!
I just checked, this is how excalidraw dark mode works.
image

I will test my approach for few days. I think it's simpler and does not need two excalidraw exports (light and dark).
I'm using quartz with VSCode Foam, this is why I can't easily export both images from Obsidian.

@bartkl
Copy link
Author

bartkl commented Feb 9, 2025

@Guillaume-Fernandez Nice find! That's really interesting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment