Even Easier Dark Mode for Slack

If you’ve found this post, chances are you’ve already seen Hunter Chang’s post Easy Dark Mode for Slack. The solution described there works fine, but why manually copy and paste when you can automate?

This post will show you how to write a bash script to re-darken Slack on MacOS with a single command.

Rejoice: save your dark-mode-accustomed eyes from the stress of unnecessary light theme exposure. If you want the finished script and don’t care about how it works, skip to the end or check out the gist.

Otherwise, open up a new file and prepare to follow along.

First, we’ll set up our variables – the path to the JavaScript file we need to modify and the code snippet we’re going to add to it. This inserts a new dark mode stylesheet along with a few CSS overrides to tweak the styles to our liking.

#!/bin/bash -xe


CSS_OVERRIDES='code, pre { background-color: #535353; color: #85c5ff; }
        .c-mrkdwn__pre, .c-mrkdwn__quote, pre { background: #535353 !important; background-color: #535353 !important; }
        #client_body:not(.onboarding):not(.feature_global_nav_layout):before {display: none;}
        .p-message_input_field, .p-message_input_plus { color: #e6e6e6 !important; background: padding-box #545454 !important; border-color: #424242 !important;}
        .ql-placeholder, .p-threads_flexpane__header_channel_name { color: #e6e6e6 !important; }
        .c-message_kit__text { color: #FFF; }
        .c-message_kit__background, .c-message_kit__message, .p-threads_view__default_background,
        .p_threads_view_load_newer_button--with-gutter, .p_threads_view_load_older_button--with-gutter,
        .p-threads_view {background-color: #222;},
        .c-channel_name__text {color: #FFF}'

SLACKDARKMODE="document.addEventListener('DOMContentLoaded', function darkMode() {
			success: function success(css) {
				let overrides = \`$CSS_OVERRIDES\`;
				\$('<style></style>').appendTo('head').html(css + overrides);

Next up, we append the code snippet:

echo "$SLACKDARKMODE" | sudo tee -a $FILE

This pipes the output of echo "$SLACKDARKMODE" into the sudo tee -a command, which writes stdout to a file. Normally we would be able to just use the >> operator, but we need to be able to use sudo to write to the /Applications directory.

Now run the script:

$ chmod +x ./script.sh
$ ./script.sh

If you restart Slack now, you should see it in all its dark mode glory. But wouldn’t it be cool if the script restarted the application for us too? We can do that by adding another line to our script!

killall Slack && open /Applications/Slack.app

Now the script works, but we still have some problems:

  • document.addEventListener... gets echoed to the terminal as well, which is noisy
  • every time this script is run, it appends a new copy of the code snippet to the target file. Adding more and more event listeners will eventually slow down Slack, so we need a way to make sure we don’t append duplicates.

We can solve the first problem by updating the offending line to output to /dev/null:

echo "$SLACKDARKMODE" | sudo tee -a $FILE > /dev/null

For the second problem, we need something unique we can search for to see if our code snippet has already been appended. We’ll call this our FLAG since we’ll need to use it in a few different places:

SLACKDARKMODE="document.addEventListener[...] //$FLAG"

Now we can add a guard so the script will only append our code snippet if it’s not already there in the file:

(! grep -q "//$FLAG" $FILEPATH) && echo "$SLACKDARKMODE" | sudo tee -a $FILE > /dev/null

Adding the -q option to grep means it will output just the exit code: 0 if grep successfully finds at least one instance of the string, 1 if it finds zero instances of the string. However, we want it to be falsy when it does find the string, so we can halt execution. The ! operator flips those results.

Now we have a perfectly serviceable script – it does what we need it to and doesn’t make a mess if you run it too many times. And yet… it could still be better.

What if you wanted to edit the CSS? Currently you’d need to open up ssb-interop.js, manually remove the event listener, edit the CSS in the script, then finally run the script again. Wouldn’t it be easier if the script removed the old snippet for you and inserted your updated version in its place?

This is a blog post and all the questions are rhetorical, so the answer is yes – we can use sed to edit the file and delete our added snippet before re-adding it.

Unfortunately, the way we currently have the snippet set up is going to make this tricky. sed works by processing input line-by-line, so it doesn’t deal with matching across line breaks very well at all.

There are a few different ways we could handle this, but I’ll be charging ahead with sed because removing a particular line that matches our flag is extremely straightforward:

sudo sed -i "" "/$FLAG/d" $FILE

The -i flag tells sed to edit the file in place, and the suffix on the regex tells sed what to do with the matching lines it finds – in this case, d for delete.

Now we just need a way to “minify” our JavaScript and get it all on one line so sed can do its thing. Luckily, there’s another utility to rescue us from our predicament: tr.

Using tr, we can remove all the linebreaks from our snippet before it gets added to the JavaScript file, so the whole thing is on one long line and ready for sed. We’ll add it to our append pipe chain:

o "$SLACKDARKMODE" | tr '\n' ' ' | sudo tee -a $FILE > /dev/null

So now our script looks something like this:

# remove the snippet if there's already a copy there
if grep -q "//$FLAG" $FILE
  sudo sed -i "" "/$FLAG/d" $FILE

# append the new snippet
echo "$SLACKDARKMODE" | tr '\n' ' ' | sudo tee -a $FILE > /dev/null

# restart slack
killall Slack && open /Applications/Slack.app

And that’s it! Now you’re all set – the next time Slack updates, all you need to do is run your script to enable Dark Mode and fiddle with the CSS to your heart’s content.

If you’d like to see the full finished script all together, check out the gist on GitHub.

At Revelry, we use Slack channels for open communication across our projects.

Read: inviting clients into project conversations

Also check out: The best custom emoji for Slack


More Posts by CJ Horton: