KINDERAS.COM

Reducing Gatsby HTML file size by removing inline styles

The static/dynamic site generator Gatsby is a great tool for generating semi-static websites. However, if you are using CSS modules and CSS frameworks like bootstrap (or just have a lot of CSS), then individual HTML files can become quite large.

Why does this happen

When Gatsby builds a page it tries to make sure that every pages is as “standalone” as possible. Meaning that the user could request a page at any path and still get a fast response. To achieve this Gatsby will bundle all of your CSS needed for that page into an inline style tag. The idea is that this will make it unnecessary for the browser to load an external stylesheet before drawing the page. That’s actually a good idea, but a bug/oversight in the Gatsby bundler will (at the time of writing this) cause all of the CSS to be bundled into every page. This is a known issue and the people working on Gatsby will probably fix it, but in the meantime, here is how you can fix this today.

How to remedy this problem

The fix do involve removing inline styles from each page and relying on an external stylesheet instead. In modern browsers supporting http2 loading this extra file shouldn’t affect loading performance much.

Here’s how to remove the inline style from each HTML page.

In gatsby-ssr.[js|jsx|tsx] add the following (note that this is Typescript):

import { PreRenderHTMLArgs } from "gatsby";

export const onPreRenderHTML = ({ getHeadComponents }: PreRenderHTMLArgs) => {
	if (process.env.NODE_ENV !== "production") return;

	getHeadComponents().forEach((el: any) => {
		if (el.type === "style" && el.props["data-href"]) {
			el.type = "link";
			el.props.href = el.props["data-href"];
			el.props.rel = "stylesheet";
			el.props.type = "text/css";

			delete el.props["data-href"];
			delete el.props.dangerouslySetInnerHTML;
		}
	});
};

This piece of code will replace all inline styles with a link to the stylesheet instead. Note that it will only run when using gatsby build, if you want it to run for development just remove the first line of the onPreRenderHTML function.

Sources

  • Github Gatsby issue #1526

Using Visual Studio Code (VSCode) to debug Parcel and Gatsby projects

Being able to properly debug projects within your favorite editor/IDE is an essential part of being a developer. In this post I’ll take you through setting up Visual Studio Code (VSCode) in way that allows you to debug your code directly within VSCode when working with the page generator Gatsby (https://www.gatsbyjs.com) and projects build with the Parcel bundler (https://parceljs.org). This works for both Typescript and regular JavaScript projects.

Debugging projects generated with the Parcel bundler

The goal here is to be able to set breakpoints in our code files in VSCode then launch Chrome from VSCode and perform the debugging steps all from within the VSCode editor. This should work with Typescript projects, JavaScript projects and React Projects.

Steps

  • First you’ll need to install the “Debugger for Chrome” extension for Visual Studio Code (msjsdiag.debugger-for-chrome)
  • The next step is to configure a launch configuration (see below).
  • Add an npm script to start parcel, e.g: "start": "parcel src/index.html".
{
	"version": "0.2.0",
	"configurations": [
		{
			"type": "chrome",
			"request": "launch",
			"name": "Launch Chrome debugger",
			"url": "http://localhost:1234",
			"webRoot": "${workspaceFolder}",
			"breakOnLoad": true,
			"sourceMapPathOverrides": {
				"../*": "${webRoot}/*"
			}
		}
	]
}

Debugging

  • Run the npm script first, in this example it would be npm start
  • Now you can start debugging in VSCode by running «Launch Chrome debugger» from the «Run and debug» menu
  • Chrome will open and you can set breakpoints within VSCode

Debugging Gatsby projects within Visual Studio Code (VSCode)

There are two separate steps to a Gatsby project you might like to debug, the build phase and/or the runtime phase. The full launch config for both phases can be found at the bottom of this post.

Debugging the Gatsby build phase from Visual Studio Code (VSCode)

The build phase in a Gatsby project is when Gatsby is generating all pre-generated content, such and pages etc. In this phase you’ll mainly focus on debugging the «gatsby-node.[js|ts]» file, and of course any file you refer to. Since this step in Gatsby is executed by Node, this is just like debugging a Node application. Works for both JS and Typescript Gatsby projects.

  • If you are using Typescript, set "sourceMap": true, in the «tsconfig» file.
  • Add a new config to your launch.json config file in VSCode (see below)
  • Set a breakpoint in your «gatsby-node» file and run the launch config below
{
	"name": "Debug gatsby-node",
	"type": "pwa-node",
	"request": "launch",
	"program": "${workspaceRoot}/node_modules/.bin/gatsby",
	"args": ["develop"],
	"stopOnEntry": false,
	"runtimeArgs": ["--nolazy"],
	"console": "integratedTerminal"
}

Debugging the runtime React part of a Gatsby project from within Visual Studio Code (VScode)

The runtime phase is where you’ll want to debug any client side React and Typescript/JavaScript code. This setup is very similar to the Parcel setup at the start of this post. Let’s go though the steps:

  • If you haven’t done this already, install the “Debugger for Chrome” extension for Visual Studio Code (msjsdiag.debugger-for-chrome)
  • The next step is to configure a launch configuration (see below).
  • Setup a npm script to start the Gatsby server: "start": "gatsby develop"
{
	"type": "chrome",
	"request": "launch",
	"name": "Launch Chrome debugger",
	"url": "http://localhost:8000",
	"webRoot": "${workspaceFolder}"
}
Debugging the runtime React app
  • To debug you can now run the npm script npm start and when it’s done (might take a while) …
  • Now you can start debugging in VSCode by running «Launch Chrome debugger» from the «Run and debug» menu

Full launch config for both Gatsby phases

{
	"version": "0.2.0",
	"configurations": [
		{
			"name": "Debug gatsby-node",
			"type": "pwa-node",
			"request": "launch",
			"program": "${workspaceRoot}/node_modules/.bin/gatsby",
			"args": ["develop"],
			"stopOnEntry": false,
			"runtimeArgs": ["--nolazy"],
			"console": "integratedTerminal"
		},
		{
			"type": "chrome",
			"request": "launch",
			"name": "Launch Chrome debugger",
			"url": "http://localhost:8000",
			"webRoot": "${workspaceFolder}"
		}
	]
}

QuickTip: Css only tool tip

Tool tips can sometimes be useful to display things like help texts and similar. This post demonstrates how to create a tool tip without any JavaScript, only using HTML and CSS.

Example - Tooltip on hover

This example uses the hover event. This does work (isj) even on touch devices, but it’s a good idea to provide a fallback.

<div>
	<a href="https://test.com" data-tip="This is a tooltip"> Hover over me. </a>
</div>

The html is just an anchor tag inside a div. The anchor tag has a data attribute called tip, this is will be used as the source for the tooltip content.

First selector - «a[data-tip]»

All of the CSS rules uses attribute selectors. You don’t need to use attribute selectors, but it makes it easier to target only those a elements that has tool-tip data attached.

a[data-tip] {
	position: relative;
	color: #00f;
}

This selector targets the anchor tag itself. The only required property here is position:relative, this is needed to gain a working starting position for the tooltip.

Second selector - «a[data-tip]:hover/focus::after»

a[data-tip]:hover::after,
a[data-tip]:focus::after {
	content: attr(data-tip);
	position: absolute;
	left: 0;
	top: 24px;
	min-width: 200px;
	border: 1px black solid;
	border-radius: 5px;
	background-color: rgba(0, 0, 0, 0.5);
	padding: 12px;
	color: white;
	font-size: 14px;
	z-index: 1;
}

This is where it all happens. The main thing making this work is the ::after pseudo selector. When used with the content property it will insert a pseudo element as the last child of the selected element. In this example it adds the tooltip text as a child element to the anchor tag.

  • «content: attr(data-tip)» sets the content of the tooltip to the content in the «tip» data attribute.
  • «position:absolute» and the «left/top» values sets the position of the tooltip relative to the anchor tag
  • The rest of the css is mostly styling the tooltip.

How does this example work on touch-devices?

Hover doesn’t exist on touch devices. However, browsers have implemented the hover action to trigger on first tap. So, when the user taps the hover target, the hover event will trigger, on the next tap any other events (like click for links) triggers.

References and links

QuickTip: Using CSS «line-clamp» to truncate text to a set number of lines

When working with dynamic text content it can be quite useful to be able to truncate the number of text lines displayed in a HTML element. This can be done using the CSS «line-clamp» property.

The resulting line trucation

The resulting line trucation

Consider the HTML below. We have a div with a paragraph inside containing some text. For this demo we want to show max 3 lines of text in this div element.

<div class="content">
	<p>
		Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec venenatis velit sit amet dui semper dignissim.
		More dynamic text here.
	</p>
</div>

Lets say the max width of the content div is 200 pixels and max of 100 pixels in height.

.content {
	width: 200px;
	height: 100px;
	padding: 10px;
}

We want to truncate all text over 3 lines. Here we set the display to «-webkit-box» and the <code>«-webkit-line-clamp»</code> property to 3 lines of text.

.content p {
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

Resources

QuickTip: Matching the local version of Hugo on AWS Amplify

When using Hugo to generate a static website it is quite important to match the Hugo versions locally with the version used by AWS Amplify.

If you don’t you can end up in a situation where for example syntax highlighting is working fine locally, but isn’t working on the deployed site. This happened to me, and this is how I fixed it.

  1. Open your AWS Amplify deploy project and choose «Build Settings» from the left menu.
  2. Scroll all the way down to «Build image settings» and click the edit button on the right side.
  3. Add a package version override
    1. Select “Hugo” as the package
    2. The version should be set to “latest” (or the version you prefer).
    3. Save

Now Amplify will use the version of Hugo you specified for all future builds. You’ll need to trigger a new build for any changes to your site to take effect.

PS: Remember to keep your local version of Hugo updated to the same version you selected in the Amplify console.

References: