I'll Do It Myself: A Substack Plugin For Syntax Highlighting
First I submitted a request to Substack’s support team (who are very hard to get a hold of!), and then I published an open letter about the lack of syntax highlighting on the platform. But the request has blown to the wayside.
A few days ago, I got this comment on my open letter.

I thought it was an exaggeration. It would probably be, like, a dozen or so at least, right?
Then I had an errant thought last night—I could create a bookmarklet that injects style into Substack newsletters! This would be a pretty low-tech way to circumvent the platform’s styles.
But… I use the Arc browser and Arc doesn’t support bookmarks. (They just have “tab pinning”, which I love, but doesn’t allow for bookmarklets. And they do have “Arc boosts” which are meant to serve the same purpose, but then I wouldn’t be able to share it with you all!).
Making of the Substack Syntax Highlighting Extension
So, I wrote a bookmarklet.
I decided that adding syntax highlighting based on programming languages to the code blocks would be a bit too much to solve (at least with this first version), so I put my sights on the inline code style
.
My first iteration just looked like this:
javascript:(function() {
const linkColor = getComputedStyle(document.querySelector('a'))?.color || '#1a0dab';
const style = document.createElement('style');
style.innerHTML = `
code:not(pre code) {
background-color: #f9f9f9 !important;
padding: 0.2em 0.4em;
border-radius: 4px;
font-family: monospace;
font-size: 0.95em;
border: 1px solid ${linkColor};
}
`;
document.head.appendChild(style);
})();
If I used Chrome, or something else with bookmarks, I could just paste this in a bookmark, and press that while on a Substack newsletter and it would inject the style into the inline code examples (like this!)
.
But since I use Arc, I needed to write a Chromium extension.
Luckily, someone (Peter Legierski) made a “Bookmarklet-to-Extension” tool!
NOTE: As of my writing this, the tool needs upgrading from manifest v2 to v3, but that should happen pretty soon.
So I pasted the bookmarklet in, and voila!

I just had to take that folder, unzip it, and upload it to my extensions.



And now I have my extension!

More Iteration
So, it turns out I didn’t really want a bookmarklet. I want this to run every time I open a Substack newsletter—I don’t want to have to press a button to inject the style.
So, I had to iterate a bit on what the Bookmarklet-to-Extension tool gave me.
The tool gives you a structure like:
bookmarklet2extension/
├── background.js
├── bookmarklet.js
├── icon-128.png
├── icon-16.png
├── icon-48.png
└── manifest.json
I ended up removing the bookmarklet.js
file and creating an inject.js
file that holds all of the complexity.
This is the manifest.json
I ended up going with:
{
"manifest_version": 2,
"name": "Substack Inline Code Styler",
"version": "1.0",
"description": "Automatically styles inline code on Substack posts.",
"background": {
"scripts": ["background.js"],
"persistent": false
},
"permissions": ["tabs", "*://*.substack.com/*"],
"icons": {
"16": "icon-16.png",
"48": "icon-48.png",
"128": "icon-128.png"
}
}
NOTE: I’ve given permission to any substack.com domains, but this notably won’t work if people are using custom domains for their newsletter.
And then I rewrote the background.js
file to run automatically (instead of on the extension click event), as well as reference inject.js
.
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (
changeInfo.status === "complete" &&
tab.url &&
tab.url.includes("substack.com")
) {
chrome.tabs.executeScript(tabId, { file: "inject.js" });
}
});
inject.js
If you want to see the full source code for inject.js
, check out the extension repo. It’s a little too long to include here.
Highlights:
Adds a 1px border to the inline code style
Sets the border color to match the primary link color used in the Substack post
Adds a custom background color to the inline code style
This background is derived from the post’s original background color:
It calculates the luminance of the original background
If the background is bright, it darkens it slightly
If it’s dark, it brightens it slightly
The adjustment factor is 1.05 for both cases
Screenshots
Here is a before and after of my Django + HTMX tutorial:
Before:

After:

And here is a “dark mode” example from Simon Willison’s blog that I opened in from the Substack web app. (This way of viewing newsletters introduces different HTML and CSS classes, which our browser extension has to target).
Before:

After:

Conclusion, What’s Next?
The source code for this extension can be found here. If you want, try cloning it and adding it to your browser.
This was really just a proof-of-concept. I don’t plan on publishing this to the extension store.
But I am interested in the technical limits of this approach. Could it be used to inject actual programming-language-specific syntax highlighting to code blocks? Probably?
Thanks for your time,
Nick