Tag: Static

Comparing Static Site Generator Build Times

There are so many static site generators (SSGs). It’s overwhelming trying to decide where to start. While an abundance of helpful articles may help wade through the (popular) options, they don’t magically make the decision easy.

I’ve been on a quest to help make that decision easier. A colleague of mine built a static site generator evaluation cheatsheet. It provides a really nice snapshot across numerous popular SSG choices. What’s missing is how they actually perform in action.

One feature every static site generator has in common is that it takes input data, runs it through a templating engine, and outputs HTML files. We typically refer to this process as The Build.

There’s too much nuance, context, and variability needed to compare how various SSGs perform during the build process to display on a spreadsheet — and thus begins our test to benchmark build times against popular static site generators.

This isn’t just to determine which SSG is fastest. Hugo already has that reputation. I mean, they say it on their website — The world’s fastest framework for building websites — so it must be true!

This is an in-depth comparison of build times across multiple popular SSGs and, more importantly, to analyze why those build times look the way they do. Blindly choosing the fastest or discrediting the slowest would be a mistake. Let’s find out why.

The tests

The testing process is designed to start simple — with just a few popular SSGs and a simple data format. A foundation on which to expand to more SSGs and more nuanced data. For today, the test includes six popular SSG choices:

Each test used the following approach and conditions:

  • The data source for each build are Markdown files with a randomly-generated title (as frontmatter) and body (containing three paragraphs of content).
  • The content contains no images.
  • Tests are run in series on a single machine, making the actual values less relevant than the relative comparison among the lot.
  • The output is plain text on an HTML page, run through the default starter, following each SSG’s respective guide on getting started.
  • Each test is a cold run. Caches are cleared and Markdown files are regenerated for every test.

These tests are considered benchmark tests. They are using basic Markdown files and outputting unstyled HTML into the built output.

In other words, the output is technically a website that could be deployed to production, though it’s not really a real-world scenario. Instead, this provides a baseline comparison among these frameworks. The choices you make as a developer using one of these frameworks will adjust the build times in various ways (usually by slowing it down).

For example, one way in which this doesn’t represent the real-world is that we’re testing cold builds. In the real-world, if you have 10,000 Markdown files as your data source and are using Gatsby, you’re going to make use of Gatsby’s cache, which will greatly reduce the build times (by as much as half).

The same can be said for incremental builds, which are related to warm versus cold runs in that they only build the file that changed. We’re not testing the incremental approach in these tests (at this time).

The two tiers of static site generators

Before we do that, let’s first consider that there are really two tiers of static site generators. Let’s call them basic and advanced.

  • Basic generators (which are not basic under the hood) are essentially a command-line interface (CLI) that takes in data and outputs HTML, and can often be extended to process assets (which we’re not doing here).
  • Advanced generators offer something in addition to outputting a static site, such as server-side rendering, serverless functions, and framework integration. They tend to be configured to be more dynamic right out of the box.

I intentionally chose three of each type of generator in this test. Falling into the basic bucket would be Eleventy, Hugo, and Jekyll. The other three are based on a front-end framework and ship with various amounts of tooling. Gatsby and Next are built on React, while Nuxt is built atop Vue.

Basic generators Advanced generators
Eleventy Gatsby
Hugo Next
Jekyll Nuxt

My hypothesis

Let’s apply the scientific method to this approach because science is fun (and useful)!

My hypothesis is that if an SSG is advanced, then it will perform slower than a basic SSG. I believe the results will reflect that because advanced SSGs have more overhead than basic SSGs. Thus, it’s likely that we’re going to see both groups of generators — basic and advanced — bundled together, in the results with basic generators moving significantly quicker.

Let me expand on that hypothesis a bit.

Linear(ish) and fast

Hugo and Eleventy will fly with smaller datasets. They are (relatively) simple processes in Go and Node.js, respectively, and their build output will reflect that. While both SSG will slow down as the number of files grows, I expect them to remain at the top of the class, though Eleventy may be a little less linear at scale, simply because Go tends to be more performant than Node.

Slow, then fast, but still slow

The advanced, or framework-bound SSGs, will start out and appear slow. I suspect a single-file test to contain a significant difference — milliseconds for the basic ones, compared to several seconds for Gatsby, Next, and Nuxt.

The framework-based SSGs are each built using webpack, bringing a significant amount of overhead along with it, regardless of the amount of content they are processing. That’s the baggage we sign up for in using those tools (more on this later).

But, as we add thousands of files, I suspect we’ll see the gap between the buckets close, though the advanced SSG group will stay farther behind by some significant amount.

In the advanced SSG group, I expect Gatsby to be the fastest, only because it doesn’t have a server-side component to worry about — but that’s just a gut feeling. Next and Nuxt may have optimized this to the point where, if we’re not using that feature, it won’t affect build times. And I suspect Nuxt will beat out Next, only because there is a little less overhead with Vue, compared to React.

Jekyll: The odd child

Ruby is infamously slow. It’s gotten more performant over time, but I don’t expect it to scale with Node, and certainly not with Go. And yet, at the same time, it doesn’t have the baggage of a framework.

At first, I think we’ll see Jekyll as pretty speedy, perhaps even indistinguishable from Eleventy. But as we get to the thousands of files, the performance will take a hit. My gut feeling is that there may exist a point at which Jekyll becomes the slowest of all six. We’ll push up to the 100,000 mark to see for sure.

A hand-drawn line chart showing build time on the y-axis and number of files on the x-asix, where Next is a green line, then nuxt is a yellow line, gatsby is a pink line jekyll is a blue line, eleventy is a teal line and hugo is an orange line. All lines show the build time increasing as the number of files increase, where jekyll has the sharpest slope.

The results are in!

The code that powers these tests are on GitHub. There’s also a site that shows the relative results.

After many iterations of building out a foundation on which these tests could be run, I ended up with a series of 10 runs in three different datasets:

  • Base: A single file, to compare the base build times
  • Small sites: From 1 to 1024 files, doubling each to time (to make it easier to determine if the SSGs scaled linearly)
  • Large sites: From 1,000 to 64,000 files, double on each run. I originally wanted to go up to 128,000 files, but hit some bottlenecks with a few of the frameworks. 64,000 ended up being enough to produce an idea of how the players would scale with even larger sites.

Click or tap the images to view them larger.

Summarizing the results

A few results were surprising to me, while others were expected. Here are the high-level points:

  • As expected, Hugo was the fastest, regardless of size. What I didn’t expect is that it wasn’t even close to any other generator, even at base builds (nor was it linear, but more on that below.)
  • The basic and advanced groups of SSGs are quite obvious when looking at the results for small sites. That was expected, but it was surprising to see Next is faster than Jekyll at 32,000 files, and faster than both Eleventy and Jekyll at 64,000 files. Also surprising is that Jekyll performed faster than Eleventy at 64,000 files.
  • None of the SSGs scale linearly. Next was the closest. Hugo has the appearance of being linear, but only because it’s so much faster than the rest.
  • I figured Gatsby to be the fastest among the advanced frameworks, and suspected it would be the one to get closer to the basics. But Gatsby turned out to be the slowest, producing the most dramatic curve.
  • While it wasn’t specifically mentioned in the hypothesis, the scale of differences was larger than I would have imagined. At one file, Hugo was approximately 170 times faster than Gatsby. But at 64,000 files, it was closer — about 25 times faster. That means that, while Hugo remains the fastest, it actually has the most dramatic exponential growth shape among the lot. It just looks linear because of the scale of the chart.

What does it all mean?

When I shared my results with the creators and maintainers of these SSGs, I generally received the same message. To paraphrase:

The generators that take more time to build do so because they are doing more. They are bringing more to the table for developers to work with, whereas the faster sites (i.e. the “basic” tools) focus their efforts largely in converting templates into HTML files.

I agree.

To sum it up: Scaling Jamstack sites is hard.

The challenges that will present themselves to you, Developer, as you scale a site will vary depending on the site you’re trying to build. That data isn’t captured here because it can’t be — every project is unique in some way.

What it really comes down to is your level of tolerance for waiting in exchange for developer experience.

For example, if you’re going to build a large, image-heavy site with Gatsby, you’re going to pay for it with build times, but you’re also given an immense network of plugins and a foundation on which to build a solid, organized, component-based website. Do the same with Jekyll, and it’s going to take a lot more effort to stay organized and efficient throughout the process, though your builds may run faster.

At work, I typically build sites with Gatsby (or Next, depending on the level of dynamic interactivity required). We’ve worked with the Gatsby framework to build a core on which we can rapidly build highly-customized, image-rich websites, packed with an abundance of components. Our builds become slower as the sites scale, but that’s when we get creative by implementing micro front-ends, offloading image processing, implementing content previews, along with many other optimizations.

On the side, I tend to prefer working with Eleventy. It’s usually just me writing code, and my needs are much simpler. (I like to think of myself as a good client for myself.) I feel I have more control over the output files, which makes it easier for me to get 💯s on client-side performance, and that’s important to me.

In the end, this isn’t only about what is fast or slow. It’s about what works best for you and how long you’re willing to wait.

Wrapping up

This is just the beginning! The goal of this effort was to create a foundation on which we can, together, benchmark relative build times across popular static site generators.

What ideas do you have? What holes can you poke in the process? What can we do to tighten up these tests? How can we make them more like real-world scenarios? Should we offload the processing to a dedicated machine?

These are the questions I’d love for you to help me answer. Let’s talk about it.

The post Comparing Static Site Generator Build Times appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , , , ,

WTF is a Static API

Just like there is a movement to make more websites (and more of websites) from pre-rendered static files (Jamstack), so to might we consider moving content-based APIs to be static. Sean C Davis:

static API is simply a collection of flat JSON files that live on a content delivery network (CDN). It doesn’t perform any action other than delivering content (static JSON files) to the requesting user.

But that doesn’t mean a static API is simple. And every file doesn’t have to be manually generated or updated. A static API can still use a database and it can still pull data from external services. In other words, it can be dynamically generated, but it is statically delivered.

The use cases are probably somewhat limited, but I still like it. In a sense, all RSS feeds (or, more directly, JSON feeds) could be done this way, and many are.

Direct Link to ArticlePermalink

The post WTF is a Static API appeared first on CSS-Tricks.



Static Hoisting

The other day in “Static or not?” I said:

[…] serving HTML from a CDN is some feat.

What I meant is that serving resources like images, CSS, and JavaScript from a CDN is fairly straightforward. The industry at large has been doing that for many years. An asset with a URL can be moved to a CDN and served from it. Changes to that asset are usually handled by changing the URL (e.g. style.324535.css, style.css?v=345434 or the like) so that we can take full advantage of browser cache. But HTML is a little different. The URLs to our HTML are the URLs of our public-facing websites and those URLs don’t change.

Historically, we’ve said “oh well” to this. Our web servers will serve our HTML and we’ll just do the best we can with performance there. But the Jamstack approach is changing that by saying, actually, we’ll serve that HTML from a CDN as well.

Guillermo Rauch calls that “hoisting” and likens it to how JavaScript hoists declarations higher in code. Jamstack hoists static assets higher in the hosting stack.

What Jamstack as a software architecture has now made possible, however, is to hoisting the results of computation to the edge, right next to where your visitors are.

A core tenet of Jamstack has been to pre-render (pre-compute) as much as possible, which has given prominence to static site generation. The key idea is that computation that would have happened later on, in the request’s timeline, has now been shifted to the build phase, performed once and made available for all users to share.

Hoisting, notably, happens automatically. What can be hoisted will be hoisted. But things that need servers to run (e.g. cloud functions and API stuff) can still do that. Getting even more complex, in our talk with Brian Leroux, Dave and I got into how even the results of cloud function execution can be put on a CDN and cached.

Direct Link to ArticlePermalink

The post Static Hoisting appeared first on CSS-Tricks.



Static or Not?

A quick opinion piece by Kev Quirk: Why I Don’t Use A Static Site Generator. Kev uses WordPress:

Want to blog on my iPad? I can. Want to do it on my phone? No problem. On a machine I don’t normally use? Not an issue, as long as it has a browser.

First, it’s worth understanding that by using WordPress it doesn’t opt you out of using a static site generator. WordPress has an API, and that opens the door to hit that API during a build process and build your site that way. That’s what Gatsby does, there is a plugin that exports a static site, and projects like Frontity really blur the lines.

But I agree with Kev here on his reasoning. For all his reasons, and 1,000 more, it’s a perfectly acceptable and often smart choice to run a WordPress site. I think about it in terms of robustness and feature-readiness. Need e-commerce? It’s there. Need forms? There are great plugins. Need to augment how the CMS works? You have control over the types of content and what is in them. Need auth? That’s a core feature. Wish you had a great editing experience? Gutenberg is glorious.

Time and time again, I build what I wanted to build with WordPress quickly and efficiently and it’s made me feel productive and powerful. But I don’t wanna make this specifically about WordPress; this can be true of any “classic” CMS. Craft CMS has a GraphQL API out of the box. We just posted about a Drupal + Jamstack webinar.

In the relatively new world of static sites, a little thing can end up a journey of research and implementation, like you’re the only third person on Earth to ever do it.

Now all that said…

What do I think of static site generators and the Jamstack world? They are awesome.

I think there is a lot to be said about building sites this way. The decoupling of data and front-end is smart. The security is great. The DX, what with the deploy previews and git-based everything is great. The speed you get out of the gate is amazing (serving HTML from a CDN is some feat).

Just like a classic server-side CMS doesn’t opt you out of building a static site, building with a static site doesn’t opt you out of doing dynamic things — even super duper fancy dynamic things. Josh Comeau has a great new post going into this. He built a fancy little app that does a ton in the browser with React, but that doesn’t mean he still can’t deliver a good amount of it statically. He calls it a “mindset shift,” referring to the idea that you might think you need a database call, but do you really? Could that database call have already happened and generated a static file? And if not, still, some of it could have been generated with the last bits coming over dynamically.

I can’t wait for a world where we start really seeing the best of both worlds. We do as much statically as possible, we get whatever we can’t do that way with APIs, and we don’t compromise on the best tools along the way.

When to go with a static site…

  • If you can, you should consider it, as the speed and security can’t be beaten.
  • If you’re working with a Greenfield project.
  • If your project builds from and uses accessible APIs, you could hit that API during the build process as well as use it after the initial HTML loads.
  • If some static site generator looks like a perfect fit for something you’re doing.
  • If a cost analysis says it would be cheaper.
  • If functionality (like build previews) would be extremely helpful for a workflow.

When to go with server-side software…

  • If you need the features of a classic CMS (e.g. WordPress), and the technical debt of going static from there is too high.
  • If you’re already in deep with a server-rendered project (Ruby on Rails, Python, etc.) and don’t have any existing trouble.
  • If that is where you have the most team expertise.
  • If a cost analytics says it would be cheaper.
  • If there aren’t good static solutions around for what want to build (e.g. forums software).
  • If you have an extreme situation, like millions of URLs, and the build time for static is too high.

Bad reasons to avoid a static site…

  • You need to do things with servers. (Why? You can still hit APIs on servers, either at build or during runtime.)
  • You need auth. (Why? Jamstack is perfectly capable of auth with JWTs and such.)
  • You haven’t even looked into doing things Jamstack-style.

Bad reasons to choose server-side software…

  • You haven’t even looked into doing things Jamstack-style.
  • Because you think using comfortable / existing / classic / well-established / well-supported tools opt you out of building anything statically.
  • Something something SEO. (If anything, statically rendered content should perform better. But it’s understandable if a move to static means moving to client-side calls for something like product data.)

The post Static or Not? appeared first on CSS-Tricks.



Get Static

In this piece, Eric Meyer argues that performance is more important than ever right now — especially for websites that contain critical information for the public:

If you are in charge of a web site that provides even slightly important information, or important services, it’s time to get static. I’m thinking here of sites for places like health departments (and pretty much all government services), hospitals and clinics, utility services, food delivery and ordering, and I’m sure there are more that haven’t occurred to me. As much as you possibly can, get it down to static HTML and CSS and maybe a tiny bit of enhancing JS, and pare away every byte you can.

What Eric means by “it’s time to get static” is that we need to serve regular ol’ HTML, CSS, and JavaScript files to the browser with server-side rendering. That way, our sites are faster and with fewer bottlenecks that can render the whole website useless.

On this note, Zach Leatherman recently looked at 200 sites built with Eleventy and found that the mean Lighthouse performance score was 93.7! In other words: static site generators are gosh darn fast. And if that’s not a great reason to make the switch or to start learning about static site generators in general, then I don’t know what is.

Direct Link to ArticlePermalink

The post Get Static appeared first on CSS-Tricks.



How We Created a Static Site That Generates Tartan Patterns in SVG

Tartan is a patterned cloth that’s typically associated with Scotland, particularly their fashionable kilts. On tartanify.com, we gathered over 5,000 tartan patterns (as SVG and PNG files), taking care to filter out any that have explicit usage restrictions.

The idea was cooked up by Sylvain Guizard during our summer holidays in Scotland. At the very beginning, we were thinking of building the pattern library manually in some graphics software, like Adobe Illustrator or Sketch. But that was before we discovered that the number of tartan patterns comes in thousands. We felt overwhelmed and gave up… until I found out that tartans have a specific anatomy and are referenced by simple strings composed of the numbers of threads and color codes.

Tartan anatomy and SVG

Tartan is made with alternating bands of colored threads woven at right angles that are parallel to each other. The vertical and horizontal bands follow the same pattern of colors and widths. The rectangular areas where the horizontal and vertical bands cross give the appearance of new colors by blending the original ones. Moreover, tartans are woven with a specific technique called twill, which results in visible diagonal lines. I tried to recreate the technique with SVG rectangles as threads here:

Let’s analyze the following SVG structure:

 <svg viewBox="0 0 280 280" width="280" height="280" x="0"  y="0" xmlns="http://www.w3.org/2000/svg">   <defs>     <mask id="grating" x="0" y="0" width="1" height="1">       <rect x="0" y="0" width="100%" height="100%" fill="url(#diagonalStripes)"/>     </mask>   </defs>   <g id="horizontalStripes">     <rect fill="#FF8A00" height="40" width="100%" x="0" y="0"/>         <rect fill="#E52E71" height="10" width="100%" x="0" y="40"/>     <rect fill="#FFFFFF" height="10" width="100%" x="0" y="50"/>     <rect fill="#E52E71" height="70" width="100%" x="0" y="60"/>        <rect fill="#100E17" height="20" width="100%" x="0" y="130"/>         <rect fill="#E52E71" height="70" width="100%" x="0" y="150"/>     <rect fill="#FFFFFF" height="10" width="100%" x="0" y="220"/>     <rect fill="#E52E71" height="10" width="100%" x="0" y="230"/>        <rect fill="#FF8A00" height="40" width="100%" x="0" y="240"/>   </g>   <g id="verticalStripes" mask="url(#grating)">     <rect fill="#FF8A00" width="40" height="100%" x="0" y="0" />       <rect fill="#E52E71" width="10" height="100%" x="40" y="0" />     <rect fill="#FFFFFF" width="10" height="100%" x="50" y="0" />     <rect fill="#E52E71" width="70" height="100%" x="60" y="0" />     <rect fill="#100E17" width="20" height="100%" x="130" y="0" />        <rect fill="#E52E71" width="70" height="100%" x="150" y="0" />     <rect fill="#FFFFFF" width="10" height="100%" x="220" y="0" />     <rect fill="#E52E71" width="10" height="100%" x="230" y="0" />        <rect fill="#FF8A00" width="40" height="100%" x="240" y="0" />   </g> </svg>

The horizontalStripes group creates a 280×280 square with horizontal stripes. The verticalStripes group creates the same square, but rotated by 90 degrees. Both squares start at (0,0) coordinates. That means the horizontalStripes are completely covered by the verticalStripes; that is, unless we apply a mask on the upper one.

<defs>   <mask id="grating" x="0" y="0" width="1" height="1">     <rect x="0" y="0" width="100%" height="100%" fill="url(#diagonalStripes)"/>   </mask> </defs>

The mask SVG element defines an alpha mask. By default, the coordinate system used for its x, y, width, and height attributes is the objectBoundingBox. Setting width and height to 1 (or 100%) means that the mask covers the verticalStripes resulting in just the white parts within the mask being full visible.

Can we fill our mask with a pattern? Yes, we can! Let’s reflect the tartan weaving technique using a pattern tile, like this:

In the pattern definition we change the patternUnits from the default  objectBoundingBox to userSpaceOnUse so that now, width and height are defined in pixels.

<svg width="0" height="0">   <defs>     <pattern id="diagonalStripes" x="0" y="0" patternUnits="userSpaceOnUse" width="8" height="8">       <polygon points="0,4 0,8 8,0 4,0" fill="white"/>       <polygon points="4,8 8,8 8,4" fill="white"/>     </pattern>       </defs>  </svg>

Using React for tartan weaving

We just saw how we can create a manual “weave” with SVG. Now let’s automatize this process with React. 

The SvgDefs component is straightforward — it returns the defs markup.

const SvgDefs = () => {   return (     <defs>       <pattern         id="diagonalStripes"         x="0"         y="0"         width="8"         height="8"         patternUnits="userSpaceOnUse"       >         <polygon points="0,4 0,8 8,0 4,0" fill="#ffffff" />         <polygon points="4,8 8,8 8,4" fill="#ffffff" />       </pattern>       <mask id="grating" x="0" y="0" width="1" height="1">         <rect           x="0"           y="0"           width="100%"           height="100%"           fill="url(#diagonalStripes)"         />       </mask>     </defs>   ) }

We will represent a tartan as an array of stripes. Each stripe is an object with two properties: fill (a hex color) and size (a number).

const tartan = [   { fill: "#FF8A00", size: 40 },   { fill: "#E52E71", size: 10 },   { fill: "#FFFFFF", size: 10 },   { fill: "#E52E71", size: 70 },   { fill: "#100E17", size: 20 },   { fill: "#E52E71", size: 70 },   { fill: "#FFFFFF", size: 10 },   { fill: "#E52E71", size: 10 },   { fill: "#FF8A00", size: 40 }, ]

Tartans data is often available as a pair of strings: Palette and Threadcount that could look like this:

// Palette O#FF8A00 P#E52E71 W#FFFFFF K#100E17  // Threadcount O/40 P10 W10 P70 K/10.

I won’t cover how to convert this string representation into the stripes array but, if you are interested, you can find my method in this Gist.

The SvgTile component takes the tartan array as props and returns an SVG structure.

const SvgTile = ({ tartan }) => {    // We need to calculate the starting position of each stripe and the total size of the tile   const cumulativeSizes = tartan     .map(el => el.size)     .reduce(function(r, a) {       if (r.length > 0) a += r[r.length - 1]       r.push(a)       return r     }, [])      // The tile size   const size = cumulativeSizes[cumulativeSizes.length - 1]    return (     <svg       viewBox={`0 0 $ {size} $ {size}`}       width={size}       height={size}       x="0"       y="0"       xmlns="http://www.w3.org/2000/svg"     >       <SvgDefs />       <g id="horizontalStripes">         {tartan.map((el, index) => {           return (             <rect               fill={el.fill}               width="100%"               height={el.size}               x="0"               y={cumulativeSizes[index - 1] || 0}             />           )         })}       </g>       <g id="verticalStripes" mask="url(#grating)">         {tartan.map((el, index) => {           return (             <rect               fill={el.fill}               width={el.size}               height="100%"               x={cumulativeSizes[index - 1] || 0}               y="0"             />           )         })}       </g>     </svg>   ) }

Using a tartan SVG tile as a background image

On tartanify.com, each individual tartan is used as a background image on a full-screen element. This requires some extra manipulation since we don’t have our tartan pattern tile as an SVG image. We’re also unable to use an inline SVG directly in the background-image property.

Fortunately, encoding the SVG as a background image does work:

.bg-element {   background-image: url('data:image/svg+xml;charset=utf-8,<svg>...</svg>'); }

Let’s now create an SvgBg component. It takes the tartan array as props and returns a full-screen div with the tartan pattern as background.

We need to convert the SvgTile React object into a string. The ReactDOMServer object allows us to render components to static markup. Its method renderToStaticMarkup is available both in the browser and on the Node server. The latter is important since later we will server render the tartan pages with Gatsby.

const tartanStr = ReactDOMServer.renderToStaticMarkup(<SvgTile tartan={tartan} />)

Our SVG string contains hex color codes starting with the # symbol. At the same time, # starts a fragment identifier in a URL. It means our code will break unless we escape all of those instances. That’s where the built-in JavaScript encodeURIComponent function comes in handy.

const SvgBg = ({ tartan }) => {   const tartanStr = ReactDOMServer.renderToStaticMarkup(<SvgTile tartan={tartan} />)   const tartanData = encodeURIComponent(tartanStr)   return (     <div       style={{         width: "100%",         height: "100vh",         backgroundImage: `url("data:image/svg+xml;utf8,$ {tartanData}")`,       }}     />   ) }

Making an SVG tartan tile downloadable

Let’s now download our SVG image.

The SvgDownloadLink component takes svgData (the already encoded SVG string) and fileName as props and creates an anchor (<a>) element. The download attribute prompts the user to save the linked URL instead of navigating to it. When used with a value, it suggests the name of the destination file.

const SvgDownloadLink = ({ svgData, fileName = "file" }) => {   return (     <a       download={`$ {fileName}.svg`}       href={`data:image/svg+xml;utf8,$ {svgData}`}     >       Download as SVG     </a>   ) }

Converting an SVG tartan tile to a high-res PNG image file

What about users that prefer the PNG image format over SVG? Can we provide them with high resolution PNGs?

The PngDownloadLink component, just like SvgDownloadLink, creates an anchor tag and has the tartanData and fileName as props. In this case however, we also need to provide the tartan tile size since we need to set the canvas dimensions.

const Tile = SvgTile({tartan}) // Tartan tiles are always square const tartanSize = Tile.props.width

In the browser, once the component is ready, we draw the SVG tile on a <canvas> element. We’ll use the canvas toDataUrl() method that returns the image as a data URI. Finally, we set the date URI as the href attribute of our anchor tag.

Notice that we use double dimensions for the canvas and double scale the ctx. This way, we will output a PNG that’s double the size, which is great for high-resolution usage.

const PngDownloadLink = ({ svgData, width, height, fileName = "file" }) => {   const aEl = React.createRef()   React.useEffect(() => {     const canvas = document.createElement("canvas")     canvas.width = 2 * width     canvas.height = 2 * height     const ctx = canvas.getContext("2d")     ctx.scale(2, 2)     let img = new Image()     img.src = `data:image/svg+xml, $ {svgData}`     img.onload = () => {       ctx.drawImage(img, 0, 0)       const href = canvas.toDataURL("image/png")       aEl.current.setAttribute("href", href)     }   }, [])   return (     <a        ref={aEl}        download={`$ {fileName}.png`}     >       Download as PNG     </a>   ) }

For that demo, I could have skipped React’s useEffect hook and the code would worked fine. Nevertheless, our code is executed both on the server and in the browser, thanks to Gatsby. Before we start creating the canvas, we need to be sure that we are in a browser. We should also make sure the anchor element is ”ready” before we modify its attribute. 

Making a static website out of CSV with Gatsby

If you haven’t already heard of Gatsby, it’s a free and open source framework that allows you to pull data from almost anywhere and generate static websites that are powered by React.

Tartanify.com is a Gatsby website coded by myself and designed by Sylvain. At the beginning of the project, all we had was a huge CSV file (seriously, 5,495 rows), a method to convert the palette and threadcount strings into the tartan SVG structure, and an objective to give Gatsby a try.

In order to use a CSV file as the data source, we need two Gatsby plugins: gatsby-transformer-csv and gatsby-source-filesystem. Under the hood, the source plugin reads the files in the /src/data folder (which is where we put the tartans.csv file), then the transformer plugin parses the CSV file into JSON arrays.

// gatsby-config.js module.exports = {   /* ... */   plugins: [     'gatsby-transformer-csv',     {       resolve: 'gatsby-source-filesystem',       options: {         path: `$ {__dirname}/src/data`,         name: 'data',       },     },   ], }

Now, let’s see what happens in the gatsby-node.js file. The file is run during the site-building process. That’s where we can use two Gatsby Node APIs: createPages and onCreateNode. onCreateNode is called when a new node is created. We will add two additional fields to a tartan node: its unique slug and a unique name. It is necessary since the CSV file contains a number of tartan variants that are stored under the same name.

// gatsby-node.js // We add slugs here and use this array to check if a slug is already in use let slugs = [] // Then, if needed, we append a number let i = 1  exports.onCreateNode = ({ node, actions }) => {   if (node.internal.type === 'TartansCsv') {     // This transforms any string into slug     let slug = slugify(node.Name)     let uniqueName = node.Name     // If the slug is already in use, we will attach a number to it and the uniqueName     if (slugs.indexOf(slug) !== -1) {       slug += `-$ {i}`       uniqueName += ` $ {i}`       i++     } else {       i = 1     }     slugs.push(slug)        // Adding fields to the node happen here     actions.createNodeField({       name: 'slug',       node,       value: slug,     })     actions.createNodeField({       name: 'Unique_Name',       node,       value: uniqueName,     })   } }

Next, we create pages for each individual tartan. We want to have access to its siblings so that we can navigate easily. We will query the previous and next edges and add the result to the tartan page context.

// gatsby-node.js exports.createPages = async ({ graphql, actions }) => {   const { createPage } = actions   const allTartans = await graphql(`     query {       allTartansCsv {         edges {           node {             id             fields {               slug             }           }           previous {             fields {               slug               Unique_Name             }           }           next {             fields {               slug               Unique_Name             }           }         }       }     }   `)   if (allTartans.errors) {     throw allTartans.errors   }   allTartans.data.allTartansCsv.edges.forEach(     ({ node, next, previous }) => {       createPage({         path: `/tartan/$ {node.fields.slug}`,         component: path.resolve(`./src/templates/tartan.js`),         context: {           id: node.id,           previous,           next,         },       })     }   ) }

We decided to index tartans by letters and create paginated letter pages. These pages list tartans with links to their individual pages. We display a maximum of 60 tartans per page, and the number of pages per letter varies. For example, the letter “a” will have have four pages: tartans/a, tartans/a/2, tartans/a/3 and tartans/a/4. The highest number of pages (15) belongs to “m” due to a high number of traditional names starting with “Mac.”

The tartans/a/4 page should point to tartans/b as its next page and tartans/b should point to tartans/a/4 as its previous page.

We will run a for of loop through the letters array ["a", "b", ... , "z"] and query all tartans that start with a given letter. This can be done with filter and regex operator:

allTartansCsv(filter: { Name: { regex: "/^$ {letter}/i" } })

The previousLetterLastIndex variable will be updated at the end of each loop and store the number of pages per letter. The /tartans/b page need to know the number of a pages (4) since its previous link should be tartans/a/4.

// gatsby-node.js const letters = "abcdefghijklmnopqrstuvwxyz".split("") exports.createPages = async ({ graphql, actions }) => {   const { createPage } = actions   // etc.    let previousLetterLastIndex = 1   for (const letter of letters) {     const allTartansByLetter = await graphql(`       query {         allTartansCsv(filter: {Name: {regex: "/^$ {letter}/i"}}) {           nodes {             Palette             fields {               slug               Unique_Name             }           }           totalCount         }       }     `)     if (allTartansByLetter.errors) {       throw allTartansByLetter.errors     }     const nodes = allTartansByLetter.data.allTartansCsv.nodes     const totalCountByLetter = allTartansByLetter.data.allTartansCsv.totalCount     const paginatedNodes = paginateNodes(nodes, pageLength)     paginatedNodes.forEach((group, index, groups) => {       createPage({         path:           index > 0 ? `/tartans/$ {letter}/$ {index + 1}` : `/tartans/$ {letter}`,         component: path.resolve(`./src/templates/tartans.js`),         context: {           group,           index,           last: index === groups.length - 1,           pageCount: groups.length,           letter,           previousLetterLastIndex,         },       })     })     previousLetterLastIndex = Math.ceil(totalCountByLetter / pageLength)   } }

The paginateNode function returns an array where initial elements are grouped by pageLength

const paginateNodes = (array, pageLength) => {   const result = Array()   for (let i = 0; i < Math.ceil(array.length / pageLength); i++) {     result.push(array.slice(i * pageLength, (i + 1) * pageLength))   }   return result }

Now let’s look into the tartan template. Since Gatsby is a React application, we can use the components that we were building in the first part of this article.

// ./src/templates/tartan.js import React from "react" import { graphql } from "gatsby" import Layout from "../components/layout" import SvgTile from "../components/svgtile" import SvgBg from "../components/svgbg" import svgAsString from "../components/svgasstring" import SvgDownloadLink from "../components/svgdownloadlink" import PngDownloadLink from "../components/pngdownloadlink"  export const query = graphql`   query($ id: String!) {     tartansCsv(id: { eq: $ id }) {       Palette       Threadcount       Origin_URL       fields {         slug         Unique_Name       }     }   } ` const TartanTemplate = props => {   const { fields, Palette, Threadcount } = props.data.tartansCsv   const {slug} = fields   const svg = SvgTile({     palette: Palette,     threadcount: Threadcount,   })   const svgData = svgAsString(svg)   const svgSize = svg.props.width      return (     <Layout>       <SvgBg svg={svg} />       {/* title and navigation component comes here */}       <div className="downloads">         <SvgDownloadLink svgData={svgData} fileName={slug} />         <PngDownloadLink svgData={svgData} size={svgSize} fileName={slug} />       </div>     </Layout>   ) } export default TartanTemplate

Finally let’s focus on the tartans index pages (the letter pages).

// ./src/templates/tartans.js import React from "react" import Layout from "../components/layout" import {Link} from "gatsby" import TartansNavigation from "../components/tartansnavigation" const TartansTemplate = ({ pageContext }) => {   const {     group,     index,     last,     pageCount,     letter,     previousLetterLastIndex,   } = pageContext    return (     <Layout>       <header>         <h1>{letter}</h1>       </header>       <ul>         {group.map(node => {           return (             <li key={node.fields.slug}>               <Link to={`/tartan/$ {node.fields.slug}`}>                 <span>{node.fields.Unique_Name}</span>               </Link>             </li>           )         })}       </ul>       <TartansNavigation         letter={letter}         index={index}         last={last}         previousLetterLastIndex={previousLetterLastIndex}       />     </Layout>   ) } export default TartansTemplate

The TartansNavigation component adds next-previous navigation between the index pages.

// ./src/components/tartansnavigation.js import React from "react" import {Link} from "gatsby"  const letters = "abcdefghijklmnopqrstuvwxyz".split("") const TartansNavigation = ({   className,   letter,   index,   last,   previousLetterLastIndex, }) => {   const first = index === 0   const letterIndex = letters.indexOf(letter)   const previousLetter = letterIndex > 0 ? letters[letterIndex - 1] : ""   const nextLetter =     letterIndex < letters.length - 1 ? letters[letterIndex + 1] : ""      let previousUrl = null, nextUrl = null    // Check if previousUrl exists and create it   if (index === 0 && previousLetter) {     // First page of each new letter except "a"     // If the previous letter had more than one page we need to attach the number      const linkFragment =       previousLetterLastIndex === 1 ? "" : `/$ {previousLetterLastIndex}`     previousUrl = `/tartans/$ {previousLetter}$ {linkFragment}`   } else if (index === 1) {     // The second page for a letter     previousUrl = `/tartans/$ {letter}`   } else if (index > 1) {     // Third and beyond     previousUrl = `/tartans/$ {letter}/$ {index}`   }      // Check if `nextUrl` exists and create it   if (last && nextLetter) {     // Last page of any letter except "z"     nextUrl = `/tartans/$ {nextLetter}`   } else if (!last) {     nextUrl = `/tartans/$ {letter}/$ {(index + 2).toString()}`   }    return (     <nav>       {previousUrl && (         <Link to={previousUrl} aria-label="Go to Previous Page" />       )}       {nextUrl && (         <Link to={nextUrl} aria-label="Go to Next Page" />       )}     </nav>   ) } export default TartansNavigation

Final thoughts

Let’s stop here. I tried to cover all of the key aspects of this project. You can find all the tartanify.com code on GitHub. The structure of this article reflects my personal journey — understanding the specificity of tartans, translating them into SVG, automating the process, generating image versions, and discovering Gatsby to build a user-friendly website. It was maybe not as fun as our Scottish journey itself 😉, but I truly enjoyed it. Once again, a side project proved to be the best way to dig into new technology.

The post How We Created a Static Site That Generates Tartan Patterns in SVG appeared first on CSS-Tricks.


, , , , ,

Create a Static Site Using Angular & Scully

The team at HeroDevs has just released the alpha version of Scully, a static site generator for Angular. That’s right, Angular didn’t have an intuitive way to create JAMstack applications before, but now it’s possible!

Scully uses a node CLI application to run Angular schematics so you don’t have to learn any new language or syntax. You can see your static files in a new dist folder called static alongside your application folder.

To watch the full setup, Tara Manicsic went through the process of setting it up on a Learn with Jason Twitch stream. You can check it out here if you want to get started.

Direct Link to ArticlePermalink

The post Create a Static Site Using Angular & Scully appeared first on CSS-Tricks.


, , , , ,

The future is bright, because the future is static

I’ve been doing this web thing for money for 10 years this year and although I haven’t been around as long as some folks, I feel like I’ve seen a few cycles come and go now, so let’s say that hot new things are often cynically viewed, initially. This milestone of mine has also got me in a retrospective mood, too, and the question “What about building websites has you interested this year?“ has only encouraged that.

When I first came into the industry, I was an out-and-out designer, delivering static comps to developers until after one-too-many poor builds of my work: I decided to get into code myself. Naturally I focused purely on the front-end—specifically HTML and CSS. Yes, I got a bit into JavaScript too (once Flash became fully irrelevant), but markup and styles have always been my favorite things about web technology. I’ve never really been into back-end development, either. Sure, I can do it, but it’s certainly not my strong suit and usually this weakens my offering a touch—especially as a freelance web designer. Well, it did weaken me, until now.

JAMstack: an awful name, but awfully empowering.

I love JAMstack because it empowers people like me, who aren’t very strong with back-end stuff, and the aspect of JAMstack that I like the most—and which I think is the best part—is static site generators (SSGs). I’m talking specifically about SSGs like Eleventy and less-so Gatsby here, for reference.

The biggest reason that I like SSGs like Eleventy is that I can have a completely flexible, component-driven codebase that at build-time, compiles down to nothing but lovely, static HTML. You still get the power of JavaScript too, but instead of forcing it down the pipe, you run it at compile-time. This has enabled me to do some pretty darn complex stuff. Eleventy does all of this at lightning speed, too.

Mix Eleventy with Netlify and in some cases, Heroku, and suddenly you have a powerful development setup which results in a fast, performant website that auto-deploys. It’s perfect setup for me.

This stuff excites me so much that I made an Eleventy starter kit this year called Hylia. I did this for two reasons:

  1. I wanted to test the viability of a content-managed static-site that uses source controlled content. I chose Netlify CMS to do this
  2. I wanted to empower people without tech skills to publish a performant, accessible blog of their own, so they didn’t have to rely on centralised systems

The platform went down really well and I think part of the reason for its success is that even though it’s (optionally) content managed, powered by design tokens and fully componentized, it performs really well because all you get is HTML and CSS with a bit of progressively enhanced JavaScript.

This is the magic of SSGs, because they give us developer experience, but much more importantly, because the output is static and lightweight (unless you prevent that with lots of code), it creates a really solid basis for a good user experience, too! This isn’t just the case for small projects like Hylia, too, because SSGs can even power huge projects like the Duet Design System, for example.

Looking back at the empowerment that SSGs enable, I’ll just list some things that they have enabled me, a web designer, to do this year:

  • Self-publish a book
  • Create rapid, interactive prototypes for clients which has completely transformed the decision making process
  • Build actual, full websites for clients
  • Completely transform my design process to use HTML and CSS as a deliverables, rather than static comps
  • Build and document an incredibly comprehensive, multi-platform design system (WIP)
  • Re-platform my CSS newsletter (WIP)

These are huge things that have had a massive, positive impact on me and next year, SSGs are only going to feature more in my work as I transition into providing educational material, too.

Wrapping up

The future is bright with the JAMstack and SSGs—especially when what is delivered to the end-user is fast, progressively enhanced websites. I honestly think that they are creating a momentum wave towards a bigger focus in performance, too.

If we chuck in some serverless technology: suddenly, designers and front-end developers really are all powerful and this really excites me because suddenly, we give lots of people power to have great ideas that might not have been able to before.

The post The future is bright, because the future is static appeared first on CSS-Tricks.


, , ,

Images Are Not Static Content

We constantly hear about the importance of keeping websites lean and fast. A fast-loading website makes users more satisfied, and satisfied users spend more time and money on your website. However, website optimization is a complex task, as there is not one silver bullet to fix all of the issues causing poor performance.

We also hear that addressing the performance of images is a low hanging fruit if you want to improve your site’s user experience However, anyone who has gotten their hands dirty trying to optimize images and cover major use cases and scenarios with responsive images knows that the complexity of this task escalates quickly. For most medium to large sites, image optimization is not a task suited to humans. This is why image content delivery networks (CDN) exist.

An image CDN is indeed a content delivery network built especially for images. Just like the name suggests. So, why would we need a special CDN to serve images? Why not use a regular CDN to serve static files? Short answer is that images are not static files…

Most image CDNs treat an image as dynamic content by optimizing the image in different ways based on context where the image is consumed.

Explained a bit differently; if you’re using responsive images on your website, an image cdn will automatically generate the derivatives of the image according to the sizes specified in the markup, usually based on some URL parameters. For example, the below code selects from 3 derivatives specified in the srcset attribute based on 3 breakpoints:

<img src="//i.foo.com/image.jpg" alt="cat"    srcset="//i.foo.com/image.jpg?width=320 320w, //i.foo.com/image.jpg?width=640 640w, //i.foo.com/image.jpg?width=1280 1280w"    sizes="(max-width: 480px) 100vw, (max-width: 900px) 33vw, 254px">

This way, the developer or designer doesn’t have to worry about creating all the image versions beforehand. Which is very good news, because the number of derivative images may quickly grow exponentially based on many break points, image formats, and screen resolutions. And that is before we’ve started talking about art direction.

Dynamic Image Optimization on Autopilot

Now that we’ve seen how an image CDN can create different sizes of an image on the fly, let’s examine how this improves web performance.

Before we go further to choose an image CDN for the examples later, it is important to point out the difference between an image CDN and a digital asset management tool (DAM). A DAM, such as Cloudinary, is mostly focused for file management aspect and often allows you to edit images and apply art direction like filters. Usually these DAMs need a general purpose CDN in front and there is little support for automation of image optimization tasks.

On the opposite end of the scale is ImageEngine. ImageEngine is the most effective image CDN on the market thanks to its built in device detection that enables superior image optimization for mobile traffic. Since mobile devices account for more than 50% of the traffic in many countries, ImageEngine truly has an advantage over other CDNs. While most other image CDNs only offer little or no automatic optimization, ImageEngine has more advanced approach thanks to its focus on mobile traffic. Hence, ImageEngine will be able to produce the best results with less implementation effort and maintenance.

How ImageEngine Improves Web Performance

With ImageEngine handling all image traffic, images are no longer static content. Images are now adapted and served exactly in the size, format, compression rate and resolution needed. Fine. But how do we measure the improvement?

These days, the “go to tool” for identifying performance issues and measuring performance is Google Lighthouse. Lighthouse is available as a standalone app and in your Chrome developer tools.

We’ll run a performance audit on an e-commerce demo page listing product images.

The page has a typical responsive grid layout with product images. The layout has a few breakpoints where the display size of the images change because number of items per row changes. Moreover, there is a mouse over feature displaying a different image of the product. The mouseover effect is handled by JavaScript and even the hidden image is always loaded in our example. So all in all, quite a few images and potential sizes.

Step One: Assess Current State

Running the Lighthouse audit on the demo-page we see a number of issues, summarized in a performance score of 98. The best score is 100, so 98 might not seem that bad. Which is true, but pay more attention to the metrics below the score. The performance score is calculated based on a few metrics with varied weighting. The images on our page have direct and indirect impact on these metrics.

In the details of the report, we see a few opportunities related to images listed:

  • Properly size images. The images does not have the right pixel size. This is quite common on pages with a responsive or fluid layout.
  • Serve images in next-gen formats. For Chrome this basically mean to convert images to webp. Usually webp is a more efficient format than most others when it comes to byte size and decode speed.
  • Efficiently encode images. There is more compression that can be applied to the images before impacting perceived visual quality.

The estimated savings (to the right in the report) are huge. This demonstrates why addressing images is considered a low hanging fruit for performance.

If you haven’t signed up already, create a free ImageEngine trial account. Once you’ve completed signup you can define the image origin (usually your website) and a domain from which you want to serve images from. The image may be something like images.mydomain.com. You point this domain name to ImageEngine with a CNAME record in your DNS, and you’re good to go.

The next step is changing the markup to make the most out of ImageEngine’s automatic features.

If our previous image tag looked like this:

<img class="pic-1" src="images/demo9/img-1.jpg">

Our new image tag will look like this when the ImageEngine domain name is serving the images:

<img class="pic-1" src="https://images.mydomain.com/images/demo9/img-1.jpg">

Because our grid layout is fluid with 4 breakpoints, we might also consider to use responsive images syntax:

<img   class="pic-1"    src="https://images.mydomain.com/images/demo9/img-1.jpg"    sizes="(max-width: 576px) 93vw,           (max-width: 768px) 238px, (max-width: 768px) 238px,           (max-width: 992px) 148px, 253px" >

Thanks to ImageEngine’s support for Client hints, ImageEngine will now generate the exact pixel size needed. Client hints are additional HTTP headers the browser can send to enable more accurate image resizing. Client hints are currently only supported by Chrome browsers

Step Three: Measure the Improvement

Running the Lighthouse audit again, we see that the score is now 100. But more importantly, look at the improvements in timings. “Time to interactive” for example. 0.7 seconds less waiting for the user in order to interact with the page. All because images are optimized properly.

What does really “optimized” mean in this case? Why is the page faster and user experience better with ImageEngine? Most of the positive impact is due to reduction in byte size of the images. The less bytes, the faster are the images transferred from the host (or ImageEngine’s edge servers) to the browser. Moreover, lighter images are usually faster to decode and render onto the users screen. This is very simplified, but let’s see how much ImageEngine reduces the image payload using WebPageTest.org to compare our demo site with-, and without ImageEngine:

ImageEngine reduces the image payload to only 25% of the original size.

Bonus: Fix Caching

In the continuous hunt for improved performance, you may have seen this alert from Lighthouse.

Lighthouse thinks the images have a too short Time To Live (TTL) -measured in seconds- in the browser cache. By default, ImageEngine passes on the cache directives given by the origin but luckily this can be changed in ImageEngine’s management interface.

Next Step: Automate Image Optimization

We’ve seen how images should no longer can be treated as static content if we want a high performing web site. Because images have such a high impact on website performance, images must be tailored according to the capabilities and context of the browser and user.

A purpose-built image CDN will relieve humans of the responsibility of trying to accommodate all possible combinations of image formats, sizes and compression levels. Managing image derivatives, is not a task for humans as it will quickly grow to become unmanageable.

Using tools like Lighthouse and WebPageTest.org document the positive impact image CDNs like ImageEngine has on important performance metrics.

The post Images Are Not Static Content appeared first on CSS-Tricks.


, ,

Using GitHub Template Repos to Jump-Start Static Site Projects

If you’re getting started with static site generators, did you know you can use GitHub template repositories to quickly start new projects and reduce your setup time?

Most static site generators make installation easy, but each project still requires configuration after installation. When you build a lot of similar projects, you may duplicate effort during the setup phase. GitHub template repositories may save you a lot of time if you find yourself:

  • creating the same folder structures from previous projects,
  • copying and pasting config files from previous projects, and
  • copying and pasting boilerplate code from previous projects.

Unlike forking a repository, which allows you to use someone else’s code as a starting point, template repositories allow you to use your own code as a starting point, where each new project gets its own, independent Git history. Check it out!

Let’s take a look at how we can set up a convenient workflow. We’ll set up a boilerplate Eleventy project, turn it into a Git repository, host the repository on GitHub, and then configure that repository to be a template. Then, next time you have a static site project, you’ll be able to come back to the repository, click a button, and start working from an exact copy of your boilerplate.

Are you ready to try it out? Let’s set up our own static site using GitHub templates to see just how much templates can help streamline a static site project.

I’m using Eleventy as an example of a static site generator because it’s my personal go-to, but this process will work for Hugo, Jekyll, Nuxt, or any other flavor of static site generator you prefer.

If you want to see the finished product, check out my static site template repository.

First off, let’s create a template folder

We’re going to kick things off by running each of these in the command line:

cd ~ mkdir static-site-template cd static-site-template

These three commands change directory into your home directory (~ in Unix-based systems), make a new directory called static-site-template, and then change directory into the static-site-template directory.

Next, we’ll initialize the Node project

In order to work with Eleventy, we need to install Node.js which allows your computer to run JavaScript code outside of a web browser.

Node.js comes with node package manager, or npm, which downloads node packages to your computer. Eleventy is a node package, so we can use npm to fetch it.

Assuming Node.js is installed, let’s head back to the command line and run:

npm init

This creates a file called package.json in the directory. npm will prompt you for a series of questions to fill out the metadata in your package.json. After answering the questions, the Node.js project is initialized.

Now we can install Eleventy

Initializing the project gave us a package.json file which lets npm install packages, run scripts, and do other tasks for us inside that project. npm uses package.json as an entry point in the project to figure out precisely how and what it should do when we give it commands.

We can tell npm to install Eleventy as a development dependency by running:

npm install -D @11ty/eleventy

This will add a devDependency entry to the package.json file and install the Eleventy package to a node_modules folder in the project.

The cool thing about the package.json file is that any other computer with Node.js and npm can read it and know to install Eleventy in the project node_modules directory without having to install it manually. See, we’re already streamlining things!

Configuring Eleventy

There are tons of ways to configure an Eleventy project. Flexibility is Eleventy’s strength. For the purposes of this tutorial, I’m going to demonstrate a configuration that provides:

  • A folder to cleanly separate website source code from overall project files
  • An HTML document for a single page website
  • CSS to style the document
  • JavaScript to add functionality to the document

Hop back in the command line. Inside the static-site-template folder, run these commands one by one (excluding the comments that appear after each # symbol):

mkdir src           # creates a directory for your website source code mkdir src/css       # creates a directory for the website styles mkdir src/js        # creates a directory for the website JavaScript touch index.html    # creates the website HTML document touch css/style.css # creates the website styles touch js/main.js    # creates the website JavaScript

This creates the basic file structure that will inform the Eleventy build. However, if we run Eleventy right now, it won’t generate the website we want. We still have to configure Eleventy to understand that it should only use files in the src folder for building, and that the css and js folders should be processed with passthrough file copy.

You can give this information to Eleventy through a file called .eleventy.js in the root of the static-site-template folder. You can create that file by running this command inside the static-site-template folder:

touch .eleventy.js

Edit the file in your favorite text editor so that it contains this:

module.exports = function(eleventyConfig) {   eleventyConfig.addPassthroughCopy("src/css");   eleventyConfig.addPassthroughCopy("src/js");   return {     dir: {       input: "src"     }   }; };

Lines 2 and 3 tell Eleventy to use passthrough file copy for CSS and JavaScript. Line 6 tells Eleventy to use only the src directory to build its output.

Eleventy will now give us the expected output we want. Let’s put that to the test by putting this In the command line:

npx @11ty/eleventy

The npx command allows npm to execute code from the project node_module directory without touching the global environment. You’ll see output like this:

Writing _site/index.html from ./src/index.html. Copied 2 items and Processed 1 file in 0.04 seconds (v0.9.0)

The static-site-template folder should now have a new directory in it called _site. If you dig into that folder, you’ll find the css and js directories, along with the index.html file.

This _site folder is the final output from Eleventy. It is the entirety of the website, and you can host it on any static web host.

Without any content, styles, or scripts, the generated site isn’t very interesting:

Let’s create a boilerplate website

Next up, we’re going to put together the baseline for a super simple website we can use as the starting point for all projects moving forward.

It’s worth mentioning that Eleventy has a ton of boilerplate files for different types of projects. It’s totally fine to go with one of these though I often find I wind up needing to roll my own. So that’s what we’re doing here.

<!DOCTYPE html> <html lang="en">   <head>     <meta charset="utf-8">     <meta http-equiv="X-UA-Compatible" content="IE=edge">     <title>Static site template</title>     <meta name="description" content="A static website">     <meta name="viewport" content="width=device-width, initial-scale=1">     <link rel="stylesheet" href="css/style.css">   </head>   <body>   <h1>Great job making your website template!</h1>   <script src="js/main.js"></script>   </body> </html>

We may as well style things a tiny bit, so let’s add this to src/css/style.css:

body {   font-family: sans-serif; }

And we can confirm JavaScript is hooked up by adding this to src/js/main.js:

(function() {   console.log('Invoke the static site template JavaScript!'); })();

Want to see what we’ve got? Run npx @11ty/eleventy --serve in the command line. Eleventy will spin up a server with Browsersync and provide the local URL, which is probably something like localhost:8080.

Even the console tells us things are ready to go!

Let’s move this over to a GitHub repo

Git is the most commonly used version control system in software development. Most Unix-based computers come with it installed, and you can turn any directory into a Git repository by running this command:

git init

We should get a message like this:

Initialized empty Git repository in /path/to/static-site-template/.git/

That means a hidden .git folder was added inside the project directory, which allows the Git program to run commands against the project.

Before we start running a bunch of Git commands on the project, we need to tell Git about files we don’t want it to touch.

Inside the static-site-template directory, run:

touch .gitignore

Then open up that file in your favorite text editor. Add this content to the file:

_site/ node_modules/

This tells Git to ignore the node_modules directory and the _site directory. Committing every single Node.js module to the repo could make things really messy and tough to manage. All that information is already in package.json anyway.

Similarly, there’s no need to version control _site. Eleventy can generate it from the files in src, so no need to take up space in GitHub. It’s also possible that if we were to:

  • version control _site,
  • change files in src, or
  • forget to run Eleventy again,

then _site will reflect an older build of the website, and future developers (or a future version of yourself) may accidentally use an outdated version of the site.

Git is version control software, and GitHub is a Git repository host. There are other Git host providers like BitBucket or GitLab, but since we’re talking about a GitHub-specific feature (template repositories), we’ll push our work up to GitHub. If you don’t already have an account, go ahead and join GitHub. Once you have an account, create a GitHub repository and name it static-site-template.

GitHub will ask a few questions when setting up a new repository. One of those is whether we want to create a new repository on the command line or push an existing repository from the command line. Neither of these choices are exactly what we need. They assume we either don’t have anything at all, or we have been using Git locally already. The static-site-template project already exists, has a Git repository initialized, but doesn’t yet have any commits on it.

So let’s ignore the prompts and instead run the following commands in the command line. Make sure to have the URL GitHub provides in the command from line 3 handy:

git add . git commit -m "first commit" git remote add origin https://github.com/your-username/static-site-template.git git push -u origin master

This adds the entire static-site-template folder to the Git staging area. It commits it with the message “first commit,” adds a remote repository (the GitHub repository), and then pushes up the master branch to that repository.

Let’s template-ize this thing

OK, this is the crux of what we have been working toward. GitHub templates allows us to use the repository we’ve just created as the foundation for other projects in the future — without having to do all the work we’ve done to get here!

Click Settings on the GitHub landing page of the repository to get started. On the settings page, check the button for Template repository.

Now when we go back to the repository page, we’ll get a big green button that says Use this template. Click it and GitHub will create a new repository that’s a mirror of our new template. The new repository will start with the same files and folders as static-site-template. From there, download or clone that new repository to start a new project with all the base files and configuration we set up in the template project.

We can extend the template for future projects

Now that we have a template repository, we can use it for any new static site project that comes up. However, You may find that a new project has additional needs than what’s been set up in the template. For example, let’s say you need to tap into Eleventy’s templating engine or data processing power.

Go ahead and build on top of the template as you work on the new project. When you finish that project, identify pieces you want to reuse in future projects. Perhaps you figured out a cool hover effect on buttons. Or you built your own JavaScript carousel element. Or maybe you’re really proud of the document design and hierarchy of information.

If you think anything you did on a project might come up again on your next run, remove the project-specific details and add the new stuff to your template project. Push those changes up to GitHub, and the next time you use static-site-template to kick off a project, your reusable code will be available to you.

There are some limitations to this, of course

GitHub template repositories are a useful tool for avoiding repetitive setup on new web development projects. I find this especially useful for static site projects. These template repositories might not be as appropriate for more complex projects that require external services like databases with configuration that cannot be version-controlled in a single directory.

Template repositories allow you to ship reusable code you have written so you can solve a problem once and use that solution over and over again. But while your new solutions will carry over to future projects, they won’t be ported backwards to old projects.

This is a useful process for sites with very similar structure, styles, and functionality. Projects with wildly varied requirements may not benefit from this code-sharing, and you could end up bloating your project with unnecessary code.

Wrapping up

There you have it! You now have everything you need to not only start a static site project using Eleventy, but the power to re-purpose it on future projects. GitHub templates are so handy for kicking off projects quickly where we otherwise would have to re-build the same wheel over and over. Use them to your advantage and enjoy a jump start on your projects moving forward!

The post Using GitHub Template Repos to Jump-Start Static Site Projects appeared first on CSS-Tricks.


, , , , , , ,