Note
Since writing this post I've changed provider entirely due to my frustrations with Squarespace.
I'm leaving the post up, but know that it is wildly outdated and does not represent my current workflow, nor does it represent Squarespace and how it works today. Things that used to work no longer works.
Squarespace is most likely not for you if you want code highlighting, custom scripts and so forth.
Later I moved on to a more static splash page until I just redirected the whole site to LinkedIn.
A big gripe I have with having a website is making it look acceptable. I am not a web developer, and I'm terrible at UX design, which is why I decided to go with Squarespace in the first place. I hoped that Squarespace would provide me with sensible defaults and easy to configure the layout because I can't deal with CSS for the life of me. All the while allowing me to tweak the small stuff without bogging me down with detail. For the most part, I must admit that Squarespace has delivered. I think making new pages are drag and drop, while code blocks make working with small details very easy. As it stands, I'm very much tied to Squarespace's proprietary ecosystem, which is a downside.
My current writing process looks like the following:
While the writing process in the diagram above is simplified, the process boils down to that I:
For writing, I use Google Docs, which works perfectly fine. The only thing I miss is code blocks with code highlighting. Currently, there does not seem to be an easy way of achieving this effect using Google Docs.
If we take a closer look at the flowchart, we can see that there is a considerable amount of work required to move the Google Docs document over to Squarespace. There's a lot of bits and pieces which does not translate well between the two platforms. It takes a lot of time trying to make these platforms cooperate, and one of the goals of this post is to reduce the effort it takes to get a post into Squarespace. I will go over my concrete pains later in this post, but the overall summary is that writing isn't fun when I have to spend hours migrating a post from Google Docs to Squarespace.
The goal of this post is to take a close look at the current way I'm working and assess how I can simplify it. The current way I'm doing things is not sustainable. I would love posts with a bunch of code snippets, but I cannot bear the thought of having to do all that work in Squarespace. I don't want my platform to limit me, nor the content I produce, which is why I'm taking action now.
The main issue is that getting the text to Squarespace is easy, but if something requires a block that isn't a text block, then I have to add it manually.
After I’ve done all that work, I have to manually click through every link in the table of contents just to make sure that they work!
Squarespace already has a text block with support for various headings, yet they don’t have this feature. The only way of doing this is to resort to HTML, yet Squarespace advertises themselves as a no-code platform.
We're talking everything from visual glitches where new blocks don't show up where the "indication line" is and the indication line not showing up at all to the whole browser freezing up on me - having to restart the entire browser. These issues occurred in multiple browsers, with and without extensions. The even worse experience is that when the browser freezes up, then the changes aren't saved, because on Squarespace you must explicitly hit save. There's no backed-up version, or "unreleased version". If the browser freezes then all unsaved edits will be gone, which is a massive problem for me as I tend to write lengthy posts.
I get that Squarespace doesn’t want to change the live version of pages automatically. In 2020 any online system like Squarespace should have one live version and one “unpublished and edited” version. The edited version can be made changes and saved, but the differences aren’t visible on the site until the user explicitly decides to republish it. If I have to make a significant edit to a site, I cannot just hit save, because that will push those changes to the site. At the same time, I cannot do all the changes in one go, because then the Squarespace editor will break on me.
While I could technically change out Squarespace as a platform, but that is more work than I am willing to do right now. Therefore we will treat Squarespace as an immutable dependency which we cannot change. By Squarespace hosting the website force some dependencies upon this project.
There are features which Google Docs has which I refuse to give up on, and I have a few new requirements myself which I want to see realised.
This is one of the cases where the external tool (Squarespace) enforces requirements on me. I.e. how the post should be formatted. My blog posts must support the following:
In Squarespace, we have three blocks we can use to display content:
Supported functionality:
Another challenge with the HTML block is the HTML format itself. I don't want to write blog posts in HTML. I don't mind HTML as a language, but it is not one I want to fiddle around with every time I want to make a paragraph. I did attempt to look at how Google Docs' HTML conversion worked, but that gives me a whole website with styling, relative images and so forth. While it could be feasible doing something with the HTML export from Google Drive, it would also take a lot of effort and fragile code to make it happen. I can use the HTML format from Google Docs; I must still juggle its stylesheet and HTML to make it fit Squarespace. While a reasonable approach, it is not a solution I'd be content with using.
Supported functionality:
I like Markdown. It is easy to use, and it allows me to do some fancy things. One thing that surprised me is the amount of the support which the Squarespace Markdown block has (more on that later). As it turns out, I can write pure HTML using the Markdown block, which should theoretically give me all the benefits of the HTML block in addition to write code snippets.
Supported functionality:
The result of this is that we must find a way to generate the post in Markdown.
At its core, HackMD isn't all that advanced, but it has some handy features, especially for the flow I am envisioning:
Most of all, HackMD is sleek and just pleasant to use.
The only big gotcha with this setup is that Grammarly doesn't understand Markdown, and will simply refuse to work with HackMD. It is something I will have to deal with for the time being, and maybe I can see if I can switch out either the editor or the grammar checking tool. The good thing is that these services are now de-coupled. If I decide to switch the text editor, I am not giving up the grammar checking. If I choose to change the tool that checks my terrible writing, I don't have to switch my editor.
The envisioned workflow will go something like this:
Its a bit painful having to juggle two different editors, but I believe this phase will be immensely less painful than having to fiddle with Squarespace's block system. The goal isn't to make everything easy, but making the overall experience less tedious.
There is the aspect that different environments allow people to see different sides of a post. When I do code reviews, I do it both in the online web tool as well as in the IDE. Reading code in the IDE puts me into one mindset while reading it in the web editor puts me into another. I might be wrong, but I hope that having two editors with each specific goal can have a similar effect on my posts as well (we'll see though).
NOTE
While working on this post, I did also use Grammarly, and I can confirm a few things:
Unfortunately, we have a long way to go before the requirements are complete. This is where Github comes into play. By using Github Actions, we can start generating a whole bunch of stuff which we require, and automate issues which annoy us. This is made more accessible by the fact that we are now using Markdown; a standardised and open format.
In my new approach, I don't want Squarespace to be the main version. If I ever want to migrate from Squarespace, I like my posts to be accessible and up to date outside Squarespace. I usually do not buy into vendor lock-in, and this time is no different. Rather than having Squarespace as the main, I want Github to store the main versions from now on. Squarespace will only hold copies of whatever's in Github, which means that the way I get my post into Squarespace must be as easy as possible.
Here is the planned Github flow:
The typical writing process will now take place within a Git branch using standard Git practices. I make some edits to some text, I commit and push the changes. Rinse and repeat until the post is complete.
What is more exciting is what happens as we push the Markdown file to the Main branch, as that while triggering a whole set of operations. Before I go into the specific actions that will happen, I'd want to explain the difference between the "final" and original file.
What is probably more exciting is the "final-Post.md", or as I like to call it, the "final version". In essence, this file behaves much like a build artifact. It is our original file with some added elements which is generated through the Github Actions job. It is the final version which I will copy from when I am transferring the post over to Squarespace.
While working on this post, I tried out, refined and re-worked this workflow into its current form. Initially, this workflow triggered and edited the original file, which was fine, but it quickly caused problems with my local version suddenly being out of date and caused Git merge issues. Instead, a new final file is generated, but it is considered "illegal" to edit it directly.
I did stumble over a Python script called md-toc which takes a Markdown file and generates a ToC. We can even insert a keyword into our Markdown file and have the script inserting a ToC into the file itself.
I ended up with this command which can be run in a Github Action:
md_toc -p -s 1 -m "[comment]: <> (TOC)" redcarpet $1
-p
tells the script to insert the TOC into the file-s 1
tells the script to skip the first line. In my Markdown files, the first line will always be the title of the post, but that won't make it into Squarespace. This way, the title should be left out from the ToC.-m "[comment]: <> (TOC)"
tells the script only to put the ToC where it finds this command. This allows me to generate the ToC wherever I want the ToC to appear or exclude it altogetherredcarpet
tells the script to generate the ToC in a format which Squarespace accepts$1
this will be the name of the file that the script should take a look atNOTE
I can also specify "github" rather than "redcarpet" md-toc that it should generate the ToC in a format which Github accepts. Unfortunately, this is not something which Squarespace understands. In the future, I might decide to split the file generation into two different files rather than just "final". Instead, we can have a "github-post.md" file and a "squarespace-post.md", both with its quirks ironed out.
Wrapping that command with a Python script and I can generate ToCs for multiple files:
import sys
import os
for file in sys.argv:
print(str.format('Checking file {0}', file))
if (".md" in file):
print(str.format('Generating for file {0}', file))
os.system(str.format('md_toc -p -s 1 -m "[comment]: <> (TOC)" redcarpet {0}', file))
By doing it this way I am responsible for hosting my images somewhere accessible, but I am not beholden to the automatic approach set by HackMD
import sys
import os
import re
import requests
for file in sys.argv:
if (".md" not in file):
continue
lines = open(file, 'r')
image_path = str.format('{0}/images', os.path.dirname(file))
if not os.path.exists(image_path):
os.makedirs(image_path)
for line in lines:
for image_url in re.findall('https://.{0,2}imgur.com/.*\.png', line):
print(str.format('Downloading {0}', image_url))
image_name = re.findall('[a-zA-Z0-9]*\.png', image_url)[0]
response = requests.get(image_url)
file = open(str.format('{0}/{1}', image_path, image_name), "wb")
file.write(response.content)
file.close()
While being super hacky it basically takes imgur images and downloads it to an image folder in whatever directory the post is stored in. Currently it only works on png images, but that seem to be how HackMD decides to store it, but I may add support for other images in the future. The first version is simply to cover the main usage.
There is a second script which switches out the imgur URLs for my hosted one. Since I retain the same image name, I only need to switch out the base URL, and it will work.
NOTE
The current downside with this is that working versions (non-final) does currently not get the Github hosted image URL. While it is not a problem now, I am considering having a Github Action job which executes once a week or something which goes through all posts and matches image URL with existing images on Github pages. That way we have eventual consistency and I won't run into Git collisions. Rarely, I am still working on a post a week after it has been "released" (i.e. pushed to main).
While not being too exciting I need a way to reliably generate this code, which is why I turned to Markdown comments. Since the ToC script triggers on [comment]: <> (TOC)
then I feel we already have a pattern established. Therefore I wrote a script which replaces [comment]: <> (NOTE)
with <div class="note">
and [comment]: <> (END-DIV)
with </div>
.
The script is basic enough, similar to the previous ones where we just iterate through each line of the file and replacing what needs to be replaced. This allows me to keep the working version of my posts utterly free from HTML while being able to generate what I need on the fly. If I need some new structure, I simply use the pattern already established and edit the script to support a new keyword.
There has been a lot of complexity added to my workflow, and complexity must be justified. The high-level overview of my original workflow was easy enough to understand:
Now we are messing around with Git, Github Pages, Github Actions, a handful of custom Python scripts, image hosting, code generation and whatnot. We have made a process that was easy to understand, but time-consuming into something that is not so easy to understand. So what was achieved? Let's compare the results to the requirements:
Requirement 1: The solution must output Markdown Well… I am now writing using Markdown, so yes, that requirement is met.
Requirement 2: I should be able to write a post from any computer or device HackMD runs in the browser, so I can work on any computer that has an internet browser and a connection. In reality, though I don't need a browser and a live connection. All I need is a recent enough version from Git, and then I can use something like VS Code to write while offline. If I want to, I can also edit the file directly from Github.
While there's no app for android for Markdown which works as well as Google Drive does, I can still reach HackMD which does adjust well to a mobile form factor. It is unlikely that I would want to write my blog posts on a mobile device, but the option is there. Now it is at least feasible to copy the final version to Squarespace on a mobile device as there's less fiddling with blocks.
Requirement 3: Automatic backup HackMD has its own backup of the file, so while working in HackMD, I get the same backup functionality as I get from Google Docs. The main version is still stored in Github; thus if I work in some other editor, I need to commit and push manually. Currently, HackMD serves as my primary writing tool, which is why I don't worry.
If Git is good enough for large enterprises, then it is good enough for my blog. In my planned workflow, I will use HackMD and occasionally push the changes to Github so that I will have the benefits of both worlds.
Requirement 4: I need grammar and spell checker While not a perfect solution, I have Grammarly. The best approach would be if I could do grammar and spell checking within MarkMD (or whatever editor I use) with Grammarly, but that is currently not possible. Therefore I will be forced to do some copying back and forth. However, that is a small price to pay, considering the overall benefit I get.
Requirement 5: A table of content should be generated when needed
A ToC is generated as I push the post to main, but only if I have defined the [comment]: <> (TOC)
keyword. This allows me to have some posts with a ToC, and some without.
Requirement 6: Limit the number of required blocks on Squarespace The essence of this requirement is to limit how much fiddling I have to do to get a post into Squarespace. With this solution, I only need one block, the Markdown block. I'm meeting this requirement due to the following features:
Previously I had to do a whole bunch of fiddling around with correcting bits and pieces to make my post fit into Squarespace's way of doing things. Now I simply take the generated Markdown content and paste it into a single Markdown block. It is just that simple.
The main achievement of this process is that we have taken a job which was manually exhaustive and automated those parts. While the overall technical complexity has increased significantly, we are left with much cleaner writing and publishing process. The core of the posts are no longer proprietary, but rather an open platform and an accessible format.
For the particular interested of you, the pipeline ended up looking like this:
Such a check should first and foremost be as part of a branch job which happens before the merge. Doing it that way can indicate whether or not something is wrong before the post is merged to main.
There is a slightly more angle to this, though. Since the version hosted in Git is now considered the main version, we can do periodical checks of all URLs for every published post. Let's say that I have a post which links to some website which, for whatever reason, ceases to exist after a year. Now there is an automatic check which can identify broken link!
This post might sound like me having a go at Squarespace, but that is not the case. The things Squarespace can do, as a platform, is perfectly tailored towards their target audience. If anything, it is my fault that I hoped to take a shortcut when making this website. By buying into a proprietary platform, I have limited my ability to automate and perform specific tasks. Squarespace has forced me to rework my entire workflow, which in the end may have been for the better. I doubt that Squarespace would be happy that their customers have to revise their entire workflow to be able to work with their platform, rather than the platform merely facilitating the original workflow.
The most important achievement is that my entire blog is now based on open standards and hosted on a versatile platform which prevents me from being vendor locked-in for the future. If I, at some point, decide to migrate my website to a new platform, I don't have to worry about how I am supposed to get my data out of Squarespace.
The result of this whole operation is a simplified workflow. It is now more comfortable for me to write my post and get it to Squarespace. Rather than spending hours on managing a post before I can release it, I can spend a minute. Would it be better if I could have posted it through an API? Definitely, but that is not an option provided by Squarespace.
The biggest question might be "Hey John, will all this result in higher quality posts?", and the answer is "probably not". It is still me writing them. Shit in, shit out, and no automatic system can change that.