Then check in to repo.
then push to main.
then publish to actual site.
code section
the end!
]]>It's checked in to one repo then a request is made to integrate it into the main repo.
]]>Edited this post.
]]>A bunch of good practices:
*Considerate means semantic, accessible mark-up, written for both humans and machines.
Additional features:
Additional features:
These additional features that have persistent data including user generated content. On JAMstack sites including rich content usually means complicated build processes and multiple third-party services. This doesn't sit well with me since owning my own data and tinkering with the platform is a big motivation for building a static personal site.
That's why I opted to pair Supermaya with KeystoneJS to create a unified API that you own and manage. Each of these features are added progressively with JavaScript and will fail gracefully if not configured or the server can't be reached. You can opt-out on a per feature basis by modifying the data in site/_data/site.js
, or on a per-page basis with front-matter.
To get started deploy the Keystone JAMstack plus starter kit platform to Heroku.
Once installed visit the site on Heroku and copy your Keystone API URL. Follow the instructions to automatically deploy Supermaya to Netlify where you will be asked to enter the Keystone API URL.
Supermaya is designed work with the Keystone JAMstack plus platform to be a launch pad for larger ideas. Allowing you to go all the way from a simple static website to a feature rich application if you want.
Note: Rich features are optional. Supermaya is a perfectly good starter template for 11ty without any other services. You can deploy Supermaya on it's own and add a KEYSTONE_API
environmental variable later if you wish.
To deploy Supermaya on its own you can use this link to deploy to Netlify. Leave the KEYSTONE_API
blank if you don't have one. Alternatively you can clone this repository and run it locally.
To start the project run:
npm install
Then:
npm start
Configure important site-wide information like the site name, description and default author information:
site/_data/site.js
Change the site navigation by modifying:
site/_data/navigation.json
Supermaya includes basic theming. Select an alternative to the black and white feature colors by modifying the theme
in:
site/_data/site.js
If you connected Supermaya to a keystone backend you can toggle features under keystone
in:
site/_data/site.js
In Supermaya SCSS files are compiled on-the-fly by 11ty and added to data. This means you can write inline CSS directly into templates and partials like this: <style>{{css["compilation-target"] | safe}}</style>
. Where "compilation-target" is the key added to the list of SCSS files to compile in:
site/_data/css.js
Each entry added to the targets
array will be available as site data and a static file will also be written to css/[compilation-target].css
.
Source files for scss have been added to the directory site/src/scss
.
Similar to how SCSS works, JavaScript files in Supermaya are also compiled on-the-fly using Webpack.
The Webpack configuration contains a loader for .js
files that will transpile ES6 to ES5 meaning you can safely write modern JavaScript. The Webpack configuration can be extended or modified in: site/utils/compile-webpack.js
.
Files generated by Webpack are added to site data so you can write inline JavaScript in templates and partials like this: <script>{{css["output-filename"] | safe}}</script>
. The "output-filename" should be the full name of a file generated by Webpack including the extension.
You can add additional entry points by modifying the targets
array in:
site/_data/js.js
Each file generated will also be available as a static file at the path: js/[output-filename]
.
Source files for javascript have been added to the directory site/src/js
.
Supermaya is capable of generating criticalCSS although this is turned off by default for performance reasons. You can turn this feature on by modifying:
site/_data/site.js
A friend and designer Matt Barron, helped me out with some initial design work and used it as a placeholder for the site name in early mock-ups. As usual development names grow on you. I later learned it was the name of his family dog. It's a combination of Superman and Maya, one of his kids favourite cartoon characters. That settles it, right? And here's a picture:
]]>When you add static site generators to this, things can get a little weird. Most static site generators have a development server that watches for changes and will rebuild the site as needed. They are often smart and will only rebuild pages that have changed. You can end up with situations where the site generator rebuilds multiple times as each of the build steps is run.
This results in complex build processes where multiple passes are required and sometimes it takes several minutes just to generate a few pages. This isn't fun and trying to get them to work together can be a lot of effort.
Supermaya avoids these pitfalls by wrapping all the build processes into Eleventy itself.
Eleventy has global data files that allow asynchronous fetching and resolution of data before each build as well as transforms that can completely modify the output of generated HTML.
Supermaya makes use of both these features to compile Sass, Webpack JavaScript files, Generate Critical CSS, as well as minify and optimise content.
In Supermaya SCSS files are compiled on-the-fly and added to global data. This means you can write inline CSS directly into templates and partials like this: <style>{{css["compilation-target"] | safe}}</style>
. Where "compilation-target" is the key added to the list of SCSS files to compile in the data file:
site/_data/css.js
Each entry in the targets
array will also be available as a static file will written to the path /css/[compilation-target].css
.
Source files for scss
can be found in the directory site/src/scss
.
Similar to the CSS, JavaScript in Supermaya are also compiled on-the-fly and added to global data.
This is done with Babel and Webpack. The Webpack configuration contains a loader for .js
files that will transpile from ES6 to ES5 meaning you can safely write modern JavaScript. The Webpack configuration can be extended or modified in: site/utils/compile-webpack.js
.
Files generated by Webpack are added to site data so you can write inline JavaScript in templates and partials like this: <script>{{css["output-filename"] | safe}}</script>
. The "output-filename" should be the full name of a file generated by Webpack including the extension.
You can add additional entry points by modifying the targets
array in:
site/_data/js.js
Each file generated will also be available as a static file at the path: js/[output-filename]
.
Source files for javascript have been added to the directory site/src/js
.
Supermaya is capable of generating criticalCSS although, for performance reasons, this is turned off by default. You can turn this feature on by modifying:
site/_data/site.js
]]>
These optional features are powered by a JAMstack Plus starter kit which is powered by Keystone 5 a Node based application development framework and CMS.
Keystone is a back-end service and therefore can't be deployed to Netlify. Not all websites need a back-end, but what you get by adding this additional layer to the stack is the ability to store and manage user generated content.
If you want to add any kind of rich features to a static site and want to retain control of the platform, and ownership of data, Keystone is a good choice. It's open source and highly flexible in ways that third-party services can't provide.
To help add user generated content to static sites I built the Keystone JAMstack Plus starter kit for Keystone that ties in with Supermaya. It allows you to skip a lot of the overhead of deploying and hosting a CMS like WordPress or attempting to unify many different APIs in a build process. Keystone's package based architecture means you can build tiny streamlined CMS and API designed specifically for your content.
Don't need rich features? Although Supermaya has ties into Keystone it absolutely stands on it's own as a great static site template. It was not created for Keystone but it was created to make it a little easier to go beyond purely static content if you need.
If you've already deployed Supermaya you will need to add a KEYSTONE_API
environmental variable to your front-end.
Note: Rich features are optional. Supermaya is a perfectly good starter template for 11ty without any other services.
]]>This doesn't sit well with me since owning my own data and tinkering with the platform has always a big motivation for building a personal site.
Even good platforms and services don't always match what I'm trying to build and since I can't modify these services I'm forced to pre-process or post-process data.
This is where JamStack sites can get complicated fast. Small compromises and clever work-arounds stack-up. Eventually some static sites can feel more stifling than the CMS monoliths and server configurations that caused many of us to move to static in the first place.
I know I'm not the only one that has noticed this trend or remarked that the growing complexity of JAMstack sites is now often comparable to the set-up and matainance costs of platforms like WordPress. But usually without anywhere close to the equivalent editoral and administration experience.
Despite these gripes, I still really love the simplicity of static sites, together with the fact they are 100% configurable and self-managed. That's the feeling I want and not the feeling I've been getting from JAMstack recently.
I want a static site but I also want the ability to add features like comments, or likes or to take ownership of data I' normally share through social media.
For a long time I've wanted to be able to set-up a tailored back-end or CMS experience with the equivlent easy and flexibly I experienced when I first used Jekyll.
The idea is to extend the things I like about JAMstack into an easily deployable back-end platform that can be paired with a static front-end. I call this idea JAMstack Plus and the things I's expect from a platform include:
For me Keystone 5 ticked many of these boxes. With an index.js
file and a few lines of code I can define a content structure, configure options for data storage, get an API and set-up an admin interface that exactly matches my content.
I set-out to build on this and created the Keystone JAMstack plus starter kit platform. This is an example project that includes a bunch of services you might want to incorporate on a static. It's configured with access-control and aims to provide sensible defaults that protect user data. Feel free to build on this platform or take the idea of JAMstack plus and build your own.
]]>If you want to extend the styles in this project you might notice they are written in a particular way. You might find this un-familiar or difficult to reason about at first. I've use specific conventions around style architecture that I've found to be scalable and maintainable for any size project. If you want to follow the same conventions I explain the methodologies here.
Layout classes in Supermaya are denoted with an l-
prefix. I'm pretty specific about what a layout class is. It should have only display
, grid
or flex
properties, sometimes width
, height
or padding
, but never presentational properties like background
or font-size
or color
. It should be mostly intuitive what is layout and what is presentational.
I also believe that layout components should own both sides of the parent > child relationship. What this means is layout classes are responsible for applying layout properties to child elements. The reason for this is I try wherever possible to co-locate layout concerns. In modern CSS layout there is always a parent child relationship E.g. flex-container
> flex-item
, or grid-container
> grid-item
. When these styles are not co-located, this relationship is like a hidden dependency. Things break when we modify the styles on a child item or use it in a different container.
By separating layout styles from presentation styles and co-locating layout concerns we're able to refactor HTML and CSS much more freely. Adding items to a layout container is usually enough for them to acquire the correct styles for the child items. In practical terms it usually looks something like this:
.l-component {
display: flex;
flex-wrap: wrap;
}
.l-component > * {
flex-basis: 25%;
}
Liberal use of > *
will target all immediate child elements and you can use > .l-child-layout
where you need to be more specific.
I try to avoid child items that are presentational elements. For example if I was adding a styled button to a flex container I might wrap it in a child element, such as l-media-object-button
in the example below:
<div class="l-media-object">
<div class="l-media-object-button">
<button class="button">Submit</button>
</div>
<p>Some text</p>
</div>
This helps avoid the temptation to add layout styles to the presentational component button
later. Without it, when we want to add more specific layout classes to the media object with a button we need to refactor the HTML to add a class to the button container.
Another approach to styling specific child elements in a layout component is to use a modifier class such as l-media-object--button
a technique borrowed from OOCSS and BEM.
<div class="l-media-object l-media-object--button">
<button class="button">Submit</button>
<p>Some text</p>
</div>
Here l-media-object--button
is a variation of l-media-object
where the first child is always a button. we use this class to override the default flex-basis
on the first-child
within the container:
.l-media-object {
display: flex;
}
.l-media-object > *:first-child {
flex-basis: 25%;
}
.l-media-object--button > *:first-child {
flex-basis: 200px;
}
I've used both modifier classes and nested layout containers in Supermaya. The important thing is to keep the separation of layout and presentation and co-locate hiddent layout dependencies wherever possible. I strongly suggest avoiding layout and presentation classes on the same element.
When it's done well it should be possible to move towards a common set of reusable abstractions such as described by https://every-layout.dev/. This results in far less CSS which is easier to maintain.
Presentational classes in Supermaya are not prefixed. Any class without an l-
prefix is a presentational class or a utility class (or both). Presentational classes should not have width
, height
, padding
, display
or any other layout properties. This sometimes feels hard to do when you are not used to it. When it's done properly it means that all presentational components will be completely size-agnostic. They should be able to fill the space available inside any layout container.
If they don't have padding
or margin
it means the layout class is responsible for spacing and alignment. It should then be possible to move components around the page without refactoring. If something is not aligned, or spacing is not right it means the layout container is wrong.
Some of the hardest components to work with in this way are icons and images. For these I still recommend avoiding width and heights. If it's really needed I recommend using a utility classes.
There are not many utility classes in Supermaya which is why they are not prefixed. If you need more, it might be a good idea to prefix them with something like u-
. Utility classes can be combined with presentational components or used on their own. They should do just one thing. Some examples are:
Use this as an escape hatch when needed but try wherever possible to follow the principals regarding the separation of layout and presentation.
There are two types of variables used in Supermaya. Static scss
variables and dynamic custom-properties
. I try to define all variables including custom properties at the top of the a stylesheet. I keep the number of global variables to an absolute minimum.
If anything in my CSS changes because of a dynamic condition such as a media-query I make this a custom property. This allow me to define the logic separate to the presentation. An example looks like this:
$bp-container: 800px;
.l-container {
--container-margin: 0.5rem;
}
@media (min-width: $bp-container) {
.l-container {
--container-margin: 1rem;
}
}
.l-container {
margin: var(--container-margin);
}
Organising variables in this way means that there is only once place where I apply declarations to a class. I don't need to search the CSS to find out what is changing. Anywhere I see a var()
statment I know this is a dynamic property and I can look to the top of the page to see the logic. If you are interested in finding out more about this I wrote a blog post about this approach.