John Mikael Lindbakk

View Original

Blogging on Squarespace is a pain (and here's how I fixed it)

Content

Introduction

Before we discuss the particulars of my experience with Squarespace, I should probably talk about my history so far. My website started as a WordPress website with a custom theme. It looked horrible, but it worked. At the time, it was challenging to make fancy changes on a per-post level, and I was never a big fan of PHP, which didn't make my experience any better. Working with it made me unhappy. Due to hosting this site myself, I felt miserable every time I made a change. The reason for this isn't that I think any of the technologies involved were not up to snuff. The problem more lied within the fact that the tools were overkill for my need. PHP and WordPress can do pretty much anything, which is very troublesome for me, which just wants to do a few things.

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:

  • Doing some research on the topic which I'm going to write about
  • Writing the required code if there the post requires it
  • Writing the post itself
  • Proofreading the post
  • Iterating on this process until I'm happy with it

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.

Squarespace pains

Squarespace’s main strength is also what I dislike about it: blocks. They are great at managing a different section of a page, but they can be a pain when you need a bunch of different ones in a single post.

Different blocks add extra work

As previously explained, I copy the text from my Google Docs document over to the editor in Squarespace. The issue is that my posts tend to require multiple blocks, and Squarespace only recognises text when I'm copying from Google Docs. My posts primarily consist of these blocks:

  • Text block: For ordinary texts
  • Image block: To display images
  • Code blocks: To display highlighted code in the form of Markdown, or to render unique HTML content

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.

Copying text from Google Docs to Squarespace has iffy results

One would think that the parts that are pure text would copy over just fine, but that is not true:

  • Empty lines in Google Docs becomes two blank lines in Squarespace
  • Links in Google Docs gets two underlines in Squarespace

There’s no easy way of inserting a “table of contents” in Squarespace

This issue is kind of baffling to me, but it still is a feature that is lacking. In a blog post, there is no “table of contents” block. Instead, I have to manually create every header into an appropriate HTML header and give it a proper Id. Then I have to make the table of contents manually and make sure to link it to the correct anchor tag in the post.

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.

Squarespace’s web editor is has a lot of quirks

The block editor works excellent when setting up a simple splash page. It has worked wonders for my front page. The editor is easy to use, powerful, and I am generally pleased with it. The editor does have one fatal flaw though: It starts breaking on longer posts with a bunch of different blocks.

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.

Requirements

Solutions are solutions because they adhere to the requirements. Before we can embark on a path to a solution, we must first figure out what problems we're trying to solve.

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.

Requirements from Squarespace

Squarespace is a fickle beast, but at this point, I must adhere to its rules. One of the more annoying things is that Squarespace has no blogging API; thus, I am forced to deal with the Squarespace's block system every time I make a post. Unfortunately, there's no way to automate this process without breaking the terms of service.

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:

  • Display text
  • Display images
  • Display code snippets
  • Display custom HTML

In Squarespace, we have three blocks we can use to display content:

Text Block

All code within this block must be text. The block support headings and other text styles, but it is a very high-level way of working with a blog post. View it as your regular text field on Reddit or Facebook. If we want a picture, we need an image block. If we need to show some highlighted code, we must use a markdown block.

Supported functionality:

  • [x] Display text
  • [ ] Display images
  • [ ] Display code snippets
  • [ ] Display custom HTML

HTML Block

An HTML block might seem like the right choice, but it has one severe limitation: I cannot make code snippets happen in any straightforward way. To display code snippets, I need to add custom CSS and Javascript, something which quickly can become a hassle in Squarespace. The rule of Squarespace is, the more custom stuff you need, the more you have to manage yourself.

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:

  • [x] Display text
  • [x] Display images
  • [ ] Display code snippets
  • [x] Display custom HTML

Markdown Block

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:

  • [x] Display text
  • [x] Display images
  • [x] Display code snippets
  • [x] Display custom HTML

The result of this is that we must find a way to generate the post in Markdown.

Requirements from Google Docs

Historically I've been using Google Docs to write my posts, and any new solution should not sacrifice accessibility, nor spell and grammar checking. Therefore the requirements from Google Docs are as follow:

  • A new solution must facilitate grammar and spell checking
  • A new solution must be available online, through whatever device I wish to use, but editable offline
  • A new solution must be able back itself up as I type

My requirements

The requirements forced upon me by Squarespace is constant. I cannot change them, so I have a handful of conditions, which I think will make the overall experience much more manageable:

  • A new solution must facilitate generating a table of contents
  • A new solution must minimize the required blocks in Squarespace

Requirements summarized

If we group all these requirements, we end up with a solution that must:

  1. Output Markdown
  2. Be able to edit a post online and offline, on any device
  3. Have automatic backup
  4. Have grammar and spell checking
  5. Be able to generate a table of contents
  6. Limit the required blocks from Squarespace

New tools

Sometimes it turns out that our tools are not suitable for the problem we're trying to solve. Using a hammer where a screwdriver is necessary won't produce good results, nor be a good experience. The original workflow made it clear that Google Docs and Squarespace simply isn't compatible.

A new text editor

My first attempt at this was to connect to Google Docs' API using Python and manually extract the text and do the conversion. It turned out to be more challenging than initially expected. The first challenge was the fact that the quickstart project simply does not work as the authentication popup threw exceptions left and right. The other challenge is that there is an authentication popup. I planned to automate this through Github Actions, which would require authentication without any popup. One can automate things without this authentification using a "service account", but that comes with its other quirks. At Introductiontext editors as long as they support it. However, we cannot just choose something like VS Code and call it a day. It must fulfil our requirement, and VS Code alone does not. I cannot just close VS Code and have the backed up unless I script something, and even then the script can't do much if the computer decides to give up. I demand an in-browser editor, and that editor is HackMD.

At its core, HackMD isn't all that advanced, but it has some handy features, especially for the flow I am envisioning:

  • Supports syncing files with Github (This will become important later).
  • Supports just copy/pasting images directly into the editor

Most of all, HackMD is sleek and just pleasant to use.

Grammar checking

What HackMD does lack is any way to check grammar and the built-in spell checker is rudimentary at best. This is where our next tool comes into play: Grammarly. Grammarly is a potent tool to fix my terrible, terrible writing. It also goes way beyond what Google Docs ever did, and yes, I did go for the pro version (because I need it).

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:

  1. Write the blog post in HackMD
  2. At some point, the blog post can be considered "feature complete", upon which we move it over to a proofreading phase
  3. We copy the rendered Markdown text into the Grammarly editor
  4. Grammarly will do its thing and suggest a bunch of changes, and when we make changes we make sure to copy these changes back to HackMD (or whatever text editor we decide to use)

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:

  • Moving content between Grammarly and HackMD is less painful than between Squarespace and any other editor. There are a few quirks when copying things like lists and code snippets. Overall it was a decent experience. Formatting is not essential to proofreading.
  • Switching between editors, even if both lives in the browser, did affect me. I did rewrite a lot more sentences, scrapped more sections and generally viewed my work differently. This is the outcome I hoped for, though we'll see how it pans out in the long run.

Working through the quirks

HackMD and Grammarly do fulfil some of the requirements:

  • We get spellchecking
  • We get Markdown
  • We get code highlighting

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.

Overall strategy

The "main version" of my posts has historically been the one which can be found on Squarespace. Historically this would be called the "master version". While seemingly reasonable at first, it had a nasty habit of causing my Google Docs version and Squarespace version to go out of sync. When I saw an error in the post, I didn't bother going through Google Docs for then to go through the whole process for copying the change over to Squarespace. Instead, I simply fixed it in Squarespace and called it a day.

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.

Final and non-final

As we can see on the workflow image, the post is split into two files. One "Post.md" and "final-Post.md". The "Post.md" is the original working file. If we make edits to a post, this is the post we will edit.

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.

Table of Contents

One of the biggest annoyances I've had with the whole Google Docs to Squarespace conversion has to be the ToC. Either I am forced to write HTML in Google Docs, which is far from its intended usage. Alternatively, I must go through and make it manually in Squarespace after the fact. When I have eventually managed to make a ToC, I have to manually test one link at a time to make sure it works. This work is highly time-consuming and, most of all, not fun.

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 altogether
  • redcarpet 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 at

NOTE
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))

Images

By default images uploaded to HackMD is hosted by imgur. While I don't have anything against the service, I am also not happy with having my blog images there. If the service goes down, or if the URL changes then my posts will be left without any pictures. This is why I will utilise Github Pages as my image repository, and the image flow will look something like this:

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).

Custom code

sometimes require custom code to have certain effects. For example, the "note" block in the previous section is just my custom CSS which runs whenever it sees the class "note":
<div class="note">

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.

The results

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:

  • Step 1: Write the post in Google Docs
  • Step 2: Copy the post from Google Docs to Squarespace

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:

  • Hosting images on Github Pages allows me not needing the image block on Squarespace
  • Code generation will enable me to not having to deal with the HTML block, or even write it manually in Squarespace

The overall goal

The overall goal was to make the process for writing the post and getting that post into Squarespace easier, and I can wholeheartedly say that it has. Remember the previous workflow? Now look at the new one:

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.

Ideas for the future

A lot of work has gone into this project, but that does not mean that it is near completion. Since there's now a pipeline, we can automate a whole bunch of tasks and do automatic verifications. I haven't included everything possible, but these are the steps I can think of for the immediate future.

For the particular interested of you, the pipeline ended up looking like this:

In my resume post, I had this automatic URL check. This is something we should have in the pipeline as well.

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!

Automatic cleanup of unused images

At some point, there will be dangling images, which I am not interested in hosting. I should get around to have an automatic cleanup of images which no blog post are using.

Automatic spell checker

My resume post also featured a spell checker running as a Github Action. Due to the number of technical words, I fear that there might be a bunch of false positives. It could be more of a burden than a help, but it might be something worth trying out.

Conclusions

If there's any takeaway from this whole process, it must be that proprietary platforms are complicated. Squarespace is probably an excellent platform for the average Joe, but for someone highly technical like myself, it can often get in the way. My initial goal with Squarespace was to avoid having to deal with front-end development while producing content. As it turns out, my use cases tend to be a bit more involved than so. Being this highly technical person within a platform which is tailored towards non-technical people has introduced a bunch of challenges, especially the lack of any usable API. The only thing that saved the platform for me were the Markdown block.

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.