If you use the Excalidraw Plugin in Dark Mode then you’ve more than likely experienced the problem of colours looking washed out.
For Example this Book Cover in Light Mode is in it’s original colour
If I switch to Dark Mode (Shift+Alt+D) the book cover is washed out due to dark theme inverting images.
Excalidraw has a Scripts function which allows you to run scripts inside Excalidraw to extend functionality. https://www.youtube.com/watch?v=hePJcObHIso
I created a markdown script called “Preserve Image Color” and place it in my Excalidraw Scripts Folder.
When I launch the command palette I can now revert the image to it’s original colour but preserve the background color #121212
/*
* Custom Dark Theme for Excalidraw in Obsidian
* - Preserves Obsidian's theme setting
* - Sets dark background (#121212) in Excalidraw
* - Preserves image colors
*/
// Get the Excalidraw API
const api = ea.getExcalidrawAPI();
const st = api.getAppState();
// Get the current Obsidian theme
const isObsidianDarkTheme = document.body.classList.contains('theme-dark');
// First reset Excalidraw - important to do this before other operations
api.updateScene({
appState: {
theme: "light" // Force light theme in Excalidraw to prevent image inversion
}
});
// Short delay to ensure theme change takes effect
setTimeout(() => {
// Copy elements for editing
ea.copyViewElementsToEAforEditing(ea.getViewElements());
const elements = ea.getElements();
// Adjust element colors slightly for better visibility on dark background
elements.forEach(el => {
if (el.type !== "image") {
// Only adjust very light colors to be slightly darker if we're using dark background
if (el.strokeColor) {
const strokeCM = ea.getCM(el.strokeColor);
if (strokeCM.lightness > 90) {
strokeCM.lightnessTo(85);
el.strokeColor = strokeCM.string();
}
}
if (el.backgroundColor && el.backgroundColor !== "transparent") {
const bgCM = ea.getCM(el.backgroundColor);
if (bgCM.lightness > 90) {
bgCM.lightnessTo(85);
el.backgroundColor = bgCM.string();
}
}
}
});
// Update Excalidraw with dark background while keeping light theme
ea.viewUpdateScene({
appState: {
theme: "light", // Keep light theme for Excalidraw (prevents image inversion)
viewBackgroundColor: "#121212", // Dark gray background
gridColor: "rgba(255, 255, 255, 0.1)" // Visible grid for dark background
},
elements: elements
});
// Override any CSS that might be interfering
const excalidrawElement = document.querySelector('.excalidraw');
if (excalidrawElement) {
// Apply styles directly to ensure dark background
excalidrawElement.style.setProperty('--color-canvas-background', '#121212', 'important');
// Force Obsidian to keep its current theme setting
if (isObsidianDarkTheme) {
document.body.classList.add('theme-dark');
document.body.classList.remove('theme-light');
}
}
new Notice(`Custom dark theme applied in Excalidraw while keeping Obsidian in ${isObsidianDarkTheme ? 'dark' : 'light'} mode`);
}, 100);
This fixes the issue however now Light Mode becomes Dark Mode
When I reload Obsidian the script reverts back to Dark Mode (as that what my theme is set to and I have to select light mode to get my colors showing correct again
I also tried this version below as it:
- Shows a clear notification so user knows why the “theme: light” is being shown.
- Keeps the correct image handling and colors.
javascript
// Custom Dark Theme Toggle for Excalidraw in Obsidian
const api = ea.getExcalidrawAPI();
const st = api.getAppState();
const isDarkBackground =
st.viewBackgroundColor.toLowerCase() === "#121212" ||
st.viewBackgroundColor.match(/rgba?\(18,\s*18,\s*18/);
if (isDarkBackground) {
// Reset to default light theme
api.updateScene({
appState: {
theme: "light",
viewBackgroundColor: "#ffffff"
}
});
new Notice("Excalidraw reverted to default light theme.");
} else {
// Apply custom dark theme (workaround: keep theme 'light' to preserve image fidelity)
api.updateScene({
appState: {
theme: "light"
}
});
setTimeout(() => {
ea.copyViewElementsToEAforEditing(ea.getViewElements());
const elements = ea.getElements();
const adjustForDarkTheme = (color) => {
if (!color || color.toLowerCase() === "transparent") return color;
const cm = ea.getCM(color);
if (cm.lightness > 85) {
cm.lightnessTo(80);
}
return cm.string();
};
elements.forEach(el => {
if (el.type !== "image") {
el.strokeColor = adjustForDarkTheme(el.strokeColor);
if (el.backgroundColor && el.backgroundColor.toLowerCase() !== "transparent") {
el.backgroundColor = adjustForDarkTheme(el.backgroundColor);
}
}
});
ea.viewUpdateScene({
appState: {
theme: "light", // Stay in light theme (images OK, UI "light")
viewBackgroundColor: "#121212",
gridColor: "rgba(255,255,255,0.1)"
},
elements: elements
});
new Notice(
"Custom dark theme applied!\n\nNote: Excalidraw UI still shows 'Light' theme--this preserves image colors. This is a limitation."
);
}, 100);
}
So this little script hack showing light mode as dark mode is a Technical Limitation, not a Script Bug from what I can see.
- The “Light” is shown in the hamburger menu, but your custom dark mode is working with the book image showing the correct colour.
- I would appear that I can’t fix the UI menu label without changes to Excalidraw core which I dont want to do.
- As far as I can see the script is correct and working as I want it too.
It’s was annoying to have to toggle it back each time I launch Obsidian but I got around this by changing these two settings in Theme and Styling
If you can suggest an improvement to this hack let me know below
I will continue to test it out with other images and colours and let you know my findings