Site Stitcher: A scrappy but powerful website building tool

Site Stitcher: A scrappy, but powerful website template

Sorry for how long this turned out to be… AI Generated TLDR at the bottom of the post.

NOTE: This writeup assumes you are already familiar with 11ty at a basic level

Site Stitcher is a framework? website builder? mismash of code? … I think it is at least a template that can crank out simple business websites that still look good.

Overview

The original goal with Site Stitcher was that it could accommodate 80% local small business websites. Most small local businesses are service based and they mainly have static information. They might use software for bookings, payments, etc, but usually had their own interfaces and could just be linked out to.

Another goal with Site Stitcher was that a busy small business owner with only 30 minutes to 60 minutes per week who wanted to update their website could do just that. This is still considered in the development process, but isn’t a core value any more

Components are built using bookshop, liquid, tailwind, and hyperscript. I wanted anything component related to be in a single file. Tailwind is css styling done directly on each tag. Hyperscript is javascript logic done on each tag. Hyperscript was chosen because it is very easy to read and follow what is going on since it reads like English / pseudo code, but it is harder to write initially.

Why it exists

Early on on in web agency journey I only offered custom designed and developed websites. Pretty much every agency markets themselves this way. I naively thought that small business owners would care about bespoke designs with proper branding, spacing, etc. I did create some of these kind of sites, but I was turning business away because my pricing was out of budget for these kind of sites.

That is when I decided to create my own “Website Builder.” (I still don’t know how to categorize Site Stitcher). As a underdog agency owner I needed something to compete with the bigger local agencies. Calling around I found out that the agencies around me would quote 1-2 months to build them out. I decided that my edge would be that I could deliver websites faster than everyone else. “Websites done in 7 days or less” This would now become my core service, and the highly designed websites would be secondary. (There are other background business reasons for the change, but I’ll keep this focused on Site Stitcher)

Main Features

Global custom color theme file

This idea of a custom color theme file originally came from the venture template. I saw how it was structured and creating my own modified version. I wanted to have more color options. Knowing that picking colors can also be a tough task, I opted to follow the same color scheme idea set up by www.realtimecolors.com. This way if my users did want to update their colors they had a tool that could aid them.

There is a javascript file that generates the css for these custom color themes so that they can be used across the site. I made improvements to the venture implementation of this js file. The main issue came from how the venture template generated the ID for the color. The venture implementation made use the colors index position and its name to generate the ID. This meant that if a user reordered their colors or changed the name of the color group, all existing links to that color scheme would be broken. I made the improvement of using the ID attribute with cloud cannon’s ID instantiation to create a unique ID that wasn’t dependent on things could be changed by the user.

Dynamic high contrast text color based on background color

Legible text starts to become a concern when we start letting users use whatever colors they want to. I explored many JS routes, but a CSS based solution felt the most appropriate to me My solution was derived from CSS Variables + calc() + rgb() = Enforcing High Contrast Colors | CSS-Tricks

I modified the js file that generates the color themes to also generate high contract text class for each of the users selected colors. This does lead to a bloated css file, but I plan to solve this in the future with something like PurgeCSS. Its not a big enough problem yet where I feel like it needs priority.

Site banner

A global website banner for notifications, promotions, etc. Any color from any of the colors set up in the theme file can be used. An expiration date can also be set

Original code stolen from the alto template, but modified to work with my tech stack.

Tailwind like class system for using user theme colors

Because I used tailwind for pretty much all of the component styling, and I wanted to keep using tailwind, I created my own tailwind like CSS classes generated by the same js file that generates the user theme colors.

Examples:

cssString += `.text-${id}-textcolor { color: ${colorSet.textColor}; }\n`;
    cssString += `.text-${id}-primarycolor { color: ${colorSet.primaryColor}; }\n`;
    cssString += `.text-${id}-secondarycolor { color: ${colorSet.secondaryColor}; }\n`;
    cssString += `.text-${id}-accentcolor { color: ${colorSet.accentColor}; }\n`;
    cssString += `.text-${id}-backgroundcolor { color: ${colorSet.backgroundColor}; }\n`;

I did try to generate the tailwind classes dynamically into the tailwind config file. This failed because the full class name was never used when styling the components since the name of the class was created dynamically based on what theme the user selected. Tailwind requires the full class name to exist for it to generate the styling when it parses the project.

Umami analytics integration

Anything that is clickable will create an event in umami analytics with a pattern of {{text}}-{{current page url}}-{{type of clickable}}. Example how a button event would show up in umami analytics: “about us-/services/-topnav” or “Get started-/-btn”

Works with managed or self hosted umami. The only setup is done within the umami portion of site file. This is hidden from users.

Predefined collections

All collections have dedicated components that can feed them to make it easier to manage the site.

General pages

This is your standard page with full access to all component sections available.

Services

Service pages exist under the /services/ url and have a different schema that normal pages. They have access to all the component sections available. These pages feed into the services component section.

Blog

These pages are limited to the text based content editor. Blog posts feed into the blog post related components. A blog post can be converted to a happening, it well then also feed into happenings sections…

Happenings

Happenings are for promotional or marketing pages that rely on a start date. Typically anything event based can fall into this category. They have full access to all component sections and feed directly in happenings component sections.

Favicon generation

Site Stitcher handles favicon generation, like many other site builders do. If no favicon is selected, it will fall back to using the site logo as a favicon. If no site logo exists, it will fall back to the fallback favicon (my agencies logo)

Image optimization

Site Stitcher uses the 11ty image plugin for image optimization, but I have modified it from the basic out of the box. I can handle svgs via svg short-circuit and CSS classes can be passed in so that the image is generated with pre-applied styles.

User generated token system

This an agency focused feature, but with plenty of potential client use. We have all used applications that let us tokens like {{user.first_name}} to create user specific messaging or things of that nature. I’m pretty sure that pretty much every SSG already lets you retrieve data via variables in some way or fashion, but what about our users?

I used CloudCannon structures and a data file let users either create a single token, or a token group. This can nest as needed. There is also global tokens that exist and and are predefined.

This token system makes use of 11ty’s transform feature. Tokens are located by searching for [[tk.]] or [[st.]] Examples of usage could be [[tk.mytoken]] [[tk.regions.idaho]] or for the predefined tokens [[st.contactinfo.email]]

You can nest tokens within tokens and it goes to down to a depth of 3.

square brackets were used instead of the traditional {{}} as to not conflict with the liquid parsing engine.

On build - permalink sync

There is a chance that the permalink found with the cloudcannon schema files has been updated for one reason or another (bug fix or improvement). This script runs before 11ty starts building and makes sure that each page has the permalink logic that its respective cloudcannon schema file has, which is what is considered the most up to date.

On build - permalink duplicate check

11ty aborts when 2 or more pages have the same permalink. This is great for developers, but a bad experience for end users.

Before the build starts there is a script that runs to will evaluate each permalink. If duplicates are found the duplicate link has “-duplicate1” appended to it. 1 is counter of how many duplicates of said permalink have been discovered. The -duplicate1 is intentional to hopefully let the end user know that they accidentally created a duplicate.

A duplicate page permalink page existing is better than a build failing from an end user point of view.

Dynamic permalinks

Site Stitcher pages don’t allow direct modification of permalinks, but does have implicit modification via page titles and “Page locations”. This is to prevent the user from accidentally breaking the website, but still allowing complete customization of where a page lives on a website. Folder names contribute to this as well. A folders name will become part of that pages url if it is placed within that folder. If no page link is provided, it’ll fall back to using the page title. Example of the permalink logic.

permalink: >-
  {% capture varPagePath %}{% if pageLink%}{% assign pageLink = pageLink | slugify%}{{  page.filePathStem |fileSubstringFilter | append: pageLink }}{% else %}{% assign title = title | slugify%}{{  page.filePathStem |fileSubstringFilter | append: title }}{% endif %}{% endcapture %}{% if pagination.pageNumber > 0 %}/{{varPagePath | strip}}{% if pagination.pageNumber > 0 %}/page/{{ pagination.pageNumber }}{% endif %}{% else %}/{{varPagePath | strip}}{% endif %}/index.html

Other features

  • Flag pages as draft - doesn’t build them
    • Stolen from venture template
  • Robots.txt
  • RSS feed for blog
  • Automated blog time to read
  • Cache busting for fonts
  • Cloudcannon Snippets(image or embed) allowed in markdown rich text components
  • Google analytics integration. Just add measurement ID
  • Global social image
  • Blogs and happenings can have dedicated social images with fallback to global social image
  • Fonts by fontbunny - no cookie banner needed
  • Reusable testimonials
  • A bunch of stuff I can’t remember anymore

Applike experience for my clients

11ty + cloudcannon makes for an amazing experience. This template is what I have been using to create the websites of all my recent clients. The downside I forsaw was that as time went on clients would have their websites exist on different versions of template. This could lead to a poor experience from a customer management perspective on a long enough horizon with clients on early versions of the build and others on more advanced versions. I wanted to all my client websites to always have the most up to date version, like an app.

What I came up with was a solution where all my client websites point to the source repo for weekly updates and implemented automatic conflict resolution (as much as was possible)

Github workflow

The main piece responsible for updating the client websites is the github action that is found as part of the template.

Folders and certain files have been tagged whether they should retain the client websites changes, or the source repos changes in instances of conflicts. It works like a white list where white listed files and folder will retain the source changes instead of the clients. Things fall into this category are all the components.

Sync scripts

I call these tests in the project (I haven’t updated the names to reflect what they really do).

ValidateSiteFile.js

This script will sync the attributes of certain data files to make sure they match the source data files. This is done by comparing the client data file with the reference data file that exists as it would if the template was just copied. This takes care of adding adding new attributes, or removing old ones.

validateUsedComponents.js

This script goes through all components on each page, and verifies them against the components blueprint found in the component library. If a component has an attribute now found in the component blueprint, it removes it. If the componet is missing an attribute that does exist within the blueprint, it is added. The exception here is that only optional attributes are added. Optional defined whether a default value is set or not in component blueprint. This will also check for usage of components no longer exist.

These checks are done recursively to included components that use other components

FAQ

Why not use existing website builders?

Chances are that I could have learned a dedicated website builder really well and my business model would have stayed the same.

Why I didn’t

  • Most customizations need to be done in react. I learning react isn’t a near term priority of mine.
  • I wanted my websites to be portable and I could take them anywhere.
  • I just wanted to build my own thing. No greater feeling as a dev than when you build something that is actually used in the real world.

Why aren’t you using the unified configuration

I did attempt to migrate over to the unified configuration. Cloudcannon support was great in helping me fixing the issues that I found when I migrated my test site. The last issue that I was working on with support was that some of my pages were not showing up in the cloud cannon interface. It was seemingly random which pages would not show up, and after each save there was a chance a page could disappear from the UI. I believe the culprit was my dynamic permalink generation. I believe that Cloudcannon uses a pages permalinks in some way shape or form.

After working with support and voicing my concerns I found out I didn’t have to transition. I thought it was mandatory transition, but I just had read the patch notes incorrectly. After this discovery I stopped pursuing the global configuration

Improvements

As of yet, I haven’t submitted a support ticket to figure out how to correctly use the “initial-site-settings.json” It keeps starting new sites with the unified configuration, which Site Stitcher does not currently us.

TL;DR

Site Stitcher is a scrappy yet powerful website template built with 11ty, Bookshop, Tailwind, and Hyperscript. Designed to crank out fast, customizable, and easy-to-maintain business websites, it allows small business owners to keep their sites updated without the complexity of traditional website builders.

It includes custom color themes, dynamic high-contrast text handling, a site-wide banner system, Umami analytics integration, pre-built collections for services, blogs, and promotions, automatic favicon generation, advanced image optimization, and a user-friendly token system for dynamic content.

From dynamic permalinks and build-time validations to a GitHub-based update system that keeps all client sites on the latest version, Site Stitcher ensures a seamless, app-like experience for both developers and end users.

TLDR is AI generated

14 Likes

You really have been able to use Venture to bounce off of @Gio ! Love to see it :clap: :sparkles:

1 Like

This is really cool to see @Gio :star_struck:

1 Like

I realized I left out the bit of documentation that I have created incase someone wanted to learn how to use some of the features mentioned. Big oversight on my part. https://guides.vynxlabs.com/

It’s a little out of date. Working on this project primarily alone means I have to prioritize, and documentation tends to get neglected.

4 Likes

Always the way :laughing:

I love the way you’ve documented the components! Would be super useful for an editor :star_struck:

3 Likes

Oh my gosh, love the docs! Agree with @Alysha_Jo_Nolan this would be super useful :nerd_face: cc @Ella I know you were lurking on this thread :star_struck:

1 Like

Maybe I am :sweat_smile: Maybe I’ve already taken some mental notes

4 Likes