We need more inclusive web performance metrics

Scott Jehl argues that performance metrics such as First Contentful Paint and Largest Contentful Paint don’t really capture the full picture of everyone’s experience with websites:

These metrics are often touted as measures of usability or meaning, but they are not necessarily meaningful for everyone. In particular, users relying on assistive technology (such as a screenreader) may not perceive steps in the page loading process until after the DOM is complete, or even later depending on how JavaScript may block that process. Also, a page may not be usable to A.T. until it becomes fully interactive, since many applications often deliver accessible interactivity via external JavaScript

Scott then jots down some thoughts on how we might do that. I think this is always so very useful to keep in mind: what we experience on our site, and what we measure too, might not be the full picture.

Direct Link to ArticlePermalink

The post We need more inclusive web performance metrics appeared first on CSS-Tricks.

CSS-Tricks

, , , ,

An Eleventy Starter with Tailwind CSS and Alpine.js

When I decided to try to base my current personal website on Eleventy, I didn’t want to reinvent the wheel: I tested all the Eleventy starters built with Tailwind CSS that I could find in Starter Projects from the documentation.

Many of the starters seemed to integrate Tailwind CSS in a contrived way. Also, some of them seemed to assume that no one updates Tailwind’s configuration on the fly while working on a website. That’s why I integrated Eleventy with Tailwind CSS and Alpine.js myself. I have reason to believe that you’ll like the simplicity of my solution.

Good design is as little design as possible.

—Dieter Rams, 10 Principles for Good Design

If you’re uninterested in the details, feel free to grab my starter and jump right in.

Getting started

I’m going to assume you have a general understanding of Tailwind CSS, HTML, JavaScript, Nunjucks, the command line, and npm.

Let’s start by with a new a folder, then cd to it in the command line, and initialize it with a package.json file:

npm init -y

Now we can install Eleventy and Tailwind CSS. We’ll throw in PostCSS as well:

npm install --save-dev @11ty/eleventy tailwindcss postcss-cli

We need to create a page to test whether we’ve successfully set things up. In a real use case, our pages will use templates, so we’ll do that here as well. That’s where Nunjucks fits into the mix, serving as a templating engine.

Let’s make a new file called index.njk in the project folder. We’ll designate it as the homepage:

{% extends "_includes/default.njk" %} 
 {% block title %}It does work{% endblock %} 
 {% block content %}   <div class="fixed inset-0 flex justify-center items-center">     <div>       <span class="text-change">Good design</span><br/>       <span class="change">is<br/>as little design<br/>as possible</span>     </div>   </div> {% endblock %}

Basic templating

Now let’s create a new folder in the project folder called _includes (and yes, the folder name matters). Inside this new folder, we’ll create a file called default.njk that we’ll use as the default template for our layout. We’ll keep things simple with a basic HTML boilerplate:

<!DOCTYPE html> <html lang="en">   <head>     <title>       {% block title %}Does it work?{% endblock %}     </title>     <meta charset="UTF-8"/>     {% if description %}       <meta name="description" content="{{description}}"/>     {% endif %}     <meta http-equiv="x-ua-compatible" content="ie=edge"/>     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, viewport-fit=cover"/>     <link rel="stylesheet" href="/style.css?v={% version %}"/>     {% block head %}{% endblock %}   </head>   <body>     {% block content %}       {{ content | safe }}     {% endblock %}   </body> </html>

Configuring Tailwind CSS

Let’s take care of a test for Tailwind CSS in as few moves as possible. First, create a new subfolder called styles and a file in it called tailwind.config.js:

module.exports = {   theme: {     colors: {       change: "transparent"     }   },   variants: {},   plugins: [], }

Then, create a file called tailwind.css in that same styles folder:

/* purgecss start ignore */ @tailwind base; /* purgecss end ignore */ 
 .change {   color: transparent; } 
 /* purgecss start ignore */ @tailwind components; /* purgecss end ignore */ 
 @tailwind utilities;

We’re done with the styles folder for now. What we do need is a configuration file that tells PostCSS to use Tailwind CSS, which we can get by creating a new file in the root directory of the project folder called postcss.config.js. Here’s how we require Tailwind CSS and its configuration file with PostCSS:

module.exports = {   plugins: [     require(`tailwindcss`)(`./styles/tailwind.config.js`)   ], };

Starting and building the project

Now let’s create another new file in the same root directory called .gitignore. This will allow us to define what files to skip when committing the project to a repo, like on GitHub:

_site/ _tmp/ .DS_Store node_modules/ package-lock.json

Next, is another new file, this time one that tells Eleventy what it can ignore, called .eleventyignore. It only needs one line:

node_modules

OK, now we will create a file called .eleventy.js (note the leading dot!) that basically configures Eleventy, telling it what files to watch and where to save its work:

module.exports = function (eleventyConfig) {   eleventyConfig.setUseGitIgnore(false); 
   eleventyConfig.addWatchTarget("./_tmp/style.css"); 
   eleventyConfig.addPassthroughCopy({ "./_tmp/style.css": "./style.css" }); 
   eleventyConfig.addShortcode("version", function () {     return String(Date.now());   }); };

We can now update the package.json file with all of the scripts we need to start and build the site during development. The dependencies should already be there from the initial setup.

{   "scripts": {     "start": "eleventy --serve & postcss styles/tailwind.css --o _tmp/style.css --watch",     "build": "ELEVENTY_PRODUCTION=true eleventy & ELEVENTY_PRODUCTION=true postcss styles/tailwind.css --o _site/style.css"   },   "devDependencies": {     "@11ty/eleventy": "^0.11.0",     "postcss-cli": "^7.1.0",     "tailwindcss": "^1.4.6"   } }

Hey, great job! We made it. Build the project to generate the initial CSS — this step is only required the very first time we set up. From the command line:

npm run build

And — drumroll, please — let’s officially start the site:

npm run start

Open the page http://localhost:8080 in your browser. It’s not gonna look like much, but check out the page title in the browser tab:

It does work!

We can still do a little more checking to make sure everything’s good. Open up /styles/tailwind.config.js and change the transparent color value to something else, say black. Tailwind’s configuration should reload, along with the page in your browser.

Don’t lose sight of your browser and edit /styles/tailwind.css by changing transparent to black again. Your CSS file should reload and refresh in your browser.

Now we can work nicely with Eleventy and Tailwind CSS!

Optimizing the output

At this point, Tailwind CSS works with Eleventy, but the CSS file is huge. The generated HTML isn’t perfect either because it contains stuff like redundant newline characters. Let’s clean it up:

npm install --save-dev @fullhuman/postcss-purgecss postcss-clean html-minifier

Open up postcss.config.js and replace what’s in there with this:

module.exports = {   plugins: [     require(`tailwindcss`)(`./styles/tailwind.config.js`),     require(`autoprefixer`),     ...(process.env.ELEVENTY_PRODUCTION       ? [           require(`postcss-clean`),           require(`@fullhuman/postcss-purgecss`)({             content: ["_site/**/*.html"],             defaultExtractor: (content) =>               content.match(/[w-/:]+(?<!:)/g) || [],             whitelist: [],             whitelistPatterns: [/body/, /headroom/, /ril/],           }),         ]       : []),   ], };

In the future, in the process of creating your website, if something looks wrong after you build the project and you have the impression that fragments of CSS are missing, add “ignored” class names to the whitelist in the file above.

Add the following line to the beginning of the .eleventy.js file:

const htmlmin = require("html-minifier");

We also need to configure htmlmin in .eleventy.js as well:

eleventyConfig.addTransform("htmlmin", function (content, outputPath) {     if (       process.env.ELEVENTY_PRODUCTION &&       outputPath &&       outputPath.endsWith(".html")     ) {       let minified = htmlmin.minify(content, {         useShortDoctype: true,         removeComments: true,         collapseWhitespace: true,       });       return minified;     } 
     return content; });

We’re using a transform here which is an Eleventy thing. Transforms can modify a template’s output. At this point, .eleventy.js should look like this:

const htmlmin = require("html-minifier"); 
 module.exports = function (eleventyConfig) {   eleventyConfig.setUseGitIgnore(false); 
   eleventyConfig.addWatchTarget("./_tmp/style.css"); 
   eleventyConfig.addPassthroughCopy({ "./_tmp/style.css": "./style.css" }); 
   eleventyConfig.addShortcode("version", function () {     return String(Date.now());   }); 
   eleventyConfig.addTransform("htmlmin", function (content, outputPath) {     if (       process.env.ELEVENTY_PRODUCTION &&       outputPath &&       outputPath.endsWith(".html")     ) {       let minified = htmlmin.minify(content, {         useShortDoctype: true,         removeComments: true,         collapseWhitespace: true,       });       return minified;     } 
     return content;   }); };

Alright, let’s run npm run start once again. You’ll see that nothing has changed and that’s because optimization only happens during build. So, instead, let’s try npm run build and then look at the _site folder. There shouldn’t be a single unnecessary character in the index.html file. The same goes for the style.css file.

A project built like this is now ready to deploy. Good job! 🏆

Integrating Alpine.js

I decided to switch to Eleventy from Gatsby.js because it just felt like too much JavaScript to me. I’m more into the reasonable dose of vanilla JavaScript mixed with Alpine.js. We won’t get into the specifics of Alpine.js here, but it’s worth checking out Hugo DiFrancesco’s primer because it’s a perfect starting point.

Here’s how we can install it to our project from the command line:

npm install --save-dev alpinejs

Now we need to update .eleventy.js with this to the function that passes things through Alpine.js:

eleventyConfig.addPassthroughCopy({   "./node_modules/alpinejs/dist/alpine.js": "./js/alpine.js", }); 

Lastly, we’ll open up _includes/default.njk and add Alpine.js right before the closing </head> tag:

<script src="/js/alpine.js?v={% version %}"></script>

We can check if Alpine is working by adding this to index.njk:

{% extends "_includes/default.njk" %} 
 {% block title %}It does work{% endblock %} 
 {% block content %}   <div class="fixed inset-0 flex justify-center items-center">     <div>       <span class="text-change">Good design</span><br/>       <span class="change">is<br/>as little design<br/>as possible</span><br/>       <span x-data="{message:'🤖 Hello World 🤓'}" x-text="message"></span>     </div>   </div> {% endblock %}

Launch the project:

npm run start

If Alpine.js works, you’ll see “Hello World” in your browser. Congratulations, times two! 🏆🏆


I hope you can see how quick it can be to set up an Eleventy project, including integrations with Nunjucks for templating, Tailwind for styles and Alpine.js for scripts. I know working with new tech can be overwhelming and even confusing, so feel free to email me at csstricks@gregwolanski.com if you have problems starting up or have an idea for how to simplify this even further.

The post An Eleventy Starter with Tailwind CSS and Alpine.js appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Memorize Scroll Position Across Page Loads

Hakim El Hattab tweeted a really nice little UX enhancement for a static site that includes a scrollable sidebar of navigation.

The trick is to throw the scroll position into localStorage right before the page is exited, and when loaded, grab that value and scroll to it. I’ll retype it from the tweet…

let sidebar = document.querySelector(".sidebar");  let top = localStorage.getItem("sidebar-scroll"); if (top !== null) {   sidebar.scrollTop = parseInt(top, 10); }  window.addEventListener("beforeunload", () => {   localStorage.setItem("sidebar-scroll", sidebar.scrollTop); });

What is surprising is that you don’t get a flash-of-wrong-scroll-position. I wonder why? Maybe it has to do with fancy paint holding stuff browsers are doing now? Not sure.

The post Memorize Scroll Position Across Page Loads appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

Frontity is React for WordPress

Some developers just prefer working in React. I don’t blame them really, because I like React too. Maybe that’s what they learned first. I’ve been using it long enough there is just some comfort to it. But mostly it is the strong component model that I like. There is just something nice about a codebase where things are constructed from components with clear jobs and responsibilities.

It’s not terribly common to see WordPress sites built with React though. The standard way to use WordPress is through themes that are essentially styles and PHP files that handle the templating. Frontity is changing that though. Frontity is a React-powered framework that digests your WordPress site’s API and builds the entire front end in React with all the powerful tools you’ve come to expect from that type of environment.

OMG, Now That’s a Fast Setup

This is how I was able to get started. At the command line, I did:

npx frontity create my-app

Then I went into the folder it created and did:

npx frontity dev

That instantly spins up a site for you to start working with.

To make it feel more real for me, I did went into frontity.settings.js and changed the source API to point at CSS-Tricks:

{   name: "@frontity/wp-source",   state: {     source: {       api: "https://css-tricks.com/wp-json",     },   }, },

And now look at what I get:

That’s wild. For some projects, that’s straight up ready to deploy.

Check out their intro video which steps through this exact thing

Getting to Work

My first instinct with things like this is to get my hands into the styling right away. The theme that installs by default is the Mars theme and they have a nice guide to help wrap your mind around how it works. The theme uses Emotion for styling, so the components have styles you can mess with right in them. I found the <HeadContainer> component in index.js and immediately did the background: red change!

const HeadContainer = styled.div`   display: flex;   align-items: center;   flex-direction: column;   background-color: red; `;

It hot-module-reloaded that sucker instantly:

Is this one of those client-side only technologies?

That’s what I thought to myself. I mean, one of the advantages of using WordPress as-is is that you get the server rendering for free. That means no SEO worries (we know client-side rendered sites can take a week or more to be crawled for every change). That means resiliency and speed.

Frontity does do server side rendering! It uses Isomorphic rendering, meaning you need a Node server to render the pages, but that means the browser will get fully formed HTML for pages!

It’s a perfect match for Vercel, basically.

Similarly to how easy a new site is to scaffold and run in development, all you have to do to prep it for production is:

npx frontity build

Then run the Node server:

npx frontity serve

Cool.

I also really like that there is community around all this. If you need help, you’ll get it.

This is a best-of-all-worlds scenario.

I’m always very happy building sites with WordPress, and doubly so now that we have the block editor to use. I really like having an editor experience that helps me write and craft the kind of pages I want to create.

But I also like working with component-based architectures that have fast, easy-to-use, hot refreshing local development environments. Once you work in this kind of dev environment, it’s hard to use anything else! Beautiful DX.

And I also also want to make damn sure the sites I deploy to production are fast, robust, resilient, accessible, and SEO friendly.

I’d get all that with a Frontity site.


Another thing I like here is that Automattic themselves is on board with all this. Not just in spirit, but they are literally big investors. I think they are very smart to see this as an important part of the WordPress ecosystem. Building with WordPress doesn’t mean not building with React, especially with Frontity doing so much of the heavy lifting.

The post Frontity is React for WordPress appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

Building a Blog with Next.js

In this article, we will use Next.js to build a static blog framework with the design and structure inspired by Jekyll. I’ve always been a big fan of how Jekyll makes it easier for beginners to setup a blog and at the same time also provides a great degree of control over every aspect of the blog for the advanced users.

With the introduction of Next.js in recent years, combined with the popularity of React, there is a new avenue to explore for static blogs. Next.js makes it super easy to build static websites based on the file system itself with little to no configuration required.

The directory structure of a typical bare-bones Jekyll blog looks like this:

. ├─── _posts/          ...blog posts in markdown ├─── _layouts/        ...layouts for different pages ├─── _includes/       ...re-usable components ├─── index.md         ...homepage └─── config.yml       ...blog config

The idea is to design our framework around this directory structure as much as possible so that it becomes easier to  migrate a blog from Jekyll by simply reusing the posts and configs defined in the blog.

For those unfamiliar with Jekyll, it is a static site generator that can transform your plain text into static websites and blogs. Refer the quick start guide to get up and running with Jekyll.

This article also assumes that you have a basic knowledge of React. If not, React’s getting started page is a good place to start.

Installation

Next.js is powered by React and written in Node.js. So we need to install npm first, before adding next, react and react-dom to the project.

mkdir nextjs-blog && cd $ _ npm init -y npm install next react react-dom --save

To run Next.js scripts on the command line, we have to add the next command to the scripts section of our package.json.

"scripts": {   "dev": "next" }

We can now run npm run dev on the command line for the first time. Let’s see what happens.

$  npm run dev > nextjs-blog@1.0.0 dev /~user/nextjs-blog > next  ready - started server on http://localhost:3000 Error: > Couldn't find a `pages` directory. Please create one under the project root

The compiler is complaining about a missing pages directory in the root of the project. We’ll learn about the concept of pages in the next section.

Concept of pages

Next.js is built around the concept of pages. Each page is a React component that can be of type .js or .jsx which is mapped to a route based on the filename. For example:

File                            Route ----                            ----- /pages/about.js                 /about /pages/projects/work1.js        /projects/work1 /pages/index.js                 /

Let’s create the pages directory in the root of the project and populate our first page, index.js, with a basic React component.

// pages/index.js export default function Blog() {   return <div>Welcome to the Next.js blog</div> }

Run npm run dev once again to start the server and navigate to http://localhost:3000 in the browser to view your blog for the first time.

Screenshot of the homepage in the browser. The content says welcome to the next.js blog.

Out of the box, we get:

  • Hot reloading so we don’t have to refresh the browser for every code change.
  • Static generation of all pages inside the /pages/** directory.
  • Static file serving for assets living in the/public/** directory.
  • 404 error page.

Navigate to a random path on localhost to see the 404 page in action. If you need a custom 404 page, the Next.js docs have great information.

Screenshot of the 404 page. It says 404 This page could not be found.

Dynamic pages

Pages with static routes are useful to build the homepage, about page, etc. However, to dynamically build all our posts, we will use the dynamic route capability of Next.js. For example:

File                        Route ----                        ----- /pages/posts/[slug].js      /posts/1                             /posts/abc                             /posts/hello-world

Any route, like /posts/1, /posts/abc, etc., will be matched by /posts/[slug].js and the slug parameter will be sent as a query parameter to the page. This is especially useful for our blog posts because we don’t want to create one file per post; instead we could dynamically pass the slug to render the corresponding post.

Anatomy of a blog

Now, since we understand the basic building blocks of Next.js, let’s define the anatomy of our blog.

. ├─ api │  └─ index.js             # fetch posts, load configs, parse .md files etc ├─ _includes │  ├─ footer.js            # footer component │  └─ header.js            # header component ├─ _layouts │  ├─ default.js           # default layout for static pages like index, about │  └─ post.js              # post layout inherts from the default layout ├─ pages │  ├─ index.js             # homepage |  └─ posts                # posts will be available on the route /posts/ |     └─ [slug].js       # dynamic page to build posts └─ _posts    ├─ welcome-to-nextjs.md    └─ style-guide-101.md

Blog API

A basic blog framework needs two API functions: 

  • A function to fetch the metadata of all the posts in _posts directory
  • A function to fetch a single post for a given slug with the complete HTML and metadata

Optionally, we would also like all the site’s configuration defined in config.yml to be available across all the components. So we need a function that will parse the YAML config into a native object.

Since, we would be dealing with a lot of non-JavaScript files, like Markdown (.md), YAML (.yml), etc, we’ll use the raw-loader library to load such files as strings to make it easier to process them. 

npm install raw-loader --save-dev

Next we need to tell Next.js to use raw-loader when we import .md and .yml file formats by creating a next.config.js file in the root of the project (more info on that).

module.exports = {   target: 'serverless',   webpack: function (config) {     config.module.rules.push({test:  /.md$ /, use: 'raw-loader'})     config.module.rules.push({test: /.yml$ /, use: 'raw-loader'})     return config   } }

Next.js 9.4 introduced aliases for relative imports which helps clean up the import statement spaghetti caused by relative paths. To use aliases, create a jsconfig.json file in the project’s root directory specifying the base path and all the module aliases needed for the project.

{   "compilerOptions": {     "baseUrl": "./",     "paths": {       "@includes/*": ["_includes/*"],       "@layouts/*": ["_layouts/*"],       "@posts/*": ["_posts/*"],       "@api": ["api/index"],     }   } }

For example, this allows us to import our layouts by just using:

import DefaultLayout from '@layouts/default'

Fetch all the posts

This function will read all the Markdown files in the _posts directory, parse the front matter defined at the beginning of the post using gray-matter and return the array of metadata for all the posts.

// api/index.js import matter from 'gray-matter' 
 export async function getAllPosts() {   const context = require.context('../_posts', false, /.md$ /)   const posts = []   for(const key of context.keys()){     const post = key.slice(2);     const content = await import(`../_posts/$ {post}`);     const meta = matter(content.default)     posts.push({       slug: post.replace('.md',''),       title: meta.data.title     })   }   return posts; }

A typical Markdown post looks like this:

--- title:  "Welcome to Next.js blog!" --- **Hello world**, this is my first Next.js blog post and it is written in Markdown. I hope you like it!

The section outlined by --- is called the front matter which holds the metadata of the post like, title, permalink, tags, etc. Here’s the output:

[   { slug: 'style-guide-101', title: 'Style Guide 101' },   { slug: 'welcome-to-nextjs', title: 'Welcome to Next.js blog!' } ]

Make sure you install the gray-matter library from npm first using the command npm install gray-matter --save-dev.

Fetch a single post

For a given slug, this function will locate the file in the _posts directory, parse the Markdown with the marked library and return the output HTML with metadata.

// api/index.js import matter from 'gray-matter' import marked from 'marked' 
 export async function getPostBySlug(slug) {   const fileContent = await import(`../_posts/$ {slug}.md`)   const meta = matter(fileContent.default)   const content = marked(meta.content)       return {     title: meta.data.title,      content: content   } }

Sample output:

{   title: 'Style Guide 101',   content: '<p>Incididunt cupidatat eiusmod ...</p>' }

Make sure you install the marked library from npm first using the command npm install marked --save-dev.

Config

In order to re-use the Jekyll config for our Next.js blog, we’ll parse the YAML file using the js-yaml library and export this config so that it can be used across components.

// config.yml title: "Next.js blog" description: "This blog is powered by Next.js" 
 // api/index.js import yaml from 'js-yaml' export async function getConfig() {   const config = await import(`../config.yml`)   return yaml.safeLoad(config.default) }

Make sure you install js-yaml from npm first using the command npm install js-yaml --save-dev.

Includes

Our _includes directory contains two basic React components, <Header> and <Footer>, which will be used in the different layout components defined in the _layouts directory.

// _includes/header.js export default function Header() {   return <header><p>Blog | Powered by Next.js</p></header> } 
 // _includes/footer.js export default function Footer() {   return <footer><p>©2020 | Footer</p></footer> }

Layouts

We have two layout components in the _layouts directory. One is the <DefaultLayout> which is the base layout on top of which every other layout component will be built.

// _layouts/default.js import Head from 'next/head' import Header from '@includes/header' import Footer from '@includes/footer' 
 export default function DefaultLayout(props) {   return (     <main>       <Head>         <title>{props.title}</title>         <meta name='description' content={props.description}/>       </Head>       <Header/>       {props.children}       <Footer/>     </main>   ) }

The second layout is the <PostLayout> component that will override the title defined in the <DefaultLayout> with the post title and render the HTML of the post. It also includes a link back to the homepage.

// _layouts/post.js import DefaultLayout from '@layouts/default' import Head from 'next/head' import Link from 'next/link' 
 export default function PostLayout(props) {   return (     <DefaultLayout>       <Head>         <title>{props.title}</title>       </Head>       <article>         <h1>{props.title}</h1>         <div dangerouslySetInnerHTML={{__html:props.content}}/>         <div><Link href='/'><a>Home</a></Link></div>        </article>     </DefaultLayout>   ) }

next/head is a built-in component to append elements to the <head> of the page. next/link is a built-in component that handles client-side transitions between the routes defined in the pages directory.

Homepage

As part of the index page, aka homepage, we will list all the posts inside the _posts directory. The list will contain the post title and the permalink to the individual post page. The index page will use the <DefaultLayout> and we’ll import the config in the homepage to pass the title and description to the layout.

// pages/index.js import DefaultLayout from '@layouts/default' import Link from 'next/link' import { getConfig, getAllPosts } from '@api' 
 export default function Blog(props) {   return (     <DefaultLayout title={props.title} description={props.description}>       <p>List of posts:</p>       <ul>         {props.posts.map(function(post, idx) {           return (             <li key={idx}>               <Link href={'/posts/'+post.slug}>                 <a>{post.title}</a>               </Link>             </li>           )         })}       </ul>     </DefaultLayout>   ) }  
 export async function getStaticProps() {   const config = await getConfig()   const allPosts = await getAllPosts()   return {     props: {       posts: allPosts,       title: config.title,       description: config.description     }   } }

getStaticProps is called at the build time to pre-render pages by passing props to the default component of the page. We use this function to fetch the list of all posts at build time and render the posts archive on the homepage.

Screenshot of the homepage showing the page title, a list with two post titles, and the footer.

Post page

This page will render the title and contents of the post for the slug supplied as part of the context. The post page will use the <PostLayout> component.

// pages/posts/[slug].js import PostLayout from '@layouts/post' import { getPostBySlug, getAllPosts } from "@api" 
 export default function Post(props) {   return <PostLayout title={props.title} content={props.content}/> } 
 export async function getStaticProps(context) {   return {     props: await getPostBySlug(context.params.slug)   } } 
 export async function getStaticPaths() {   let paths = await getAllPosts()   paths = paths.map(post => ({     params: { slug:post.slug }   }));   return {     paths: paths,     fallback: false   } }

If a page has dynamic routes, Next.js needs to know all the possible paths at build time. getStaticPaths supplies the list of paths that has to be rendered to HTML at build time. The fallback property ensures that if you visit a route that does not exist in the list of paths, it will return a 404 page.

Screenshot of the blog page showing a welcome header and a hello world blue above the footer.

Production ready

Add the following commands for build and start in package.json, under the scripts section and then run npm run build followed by npm run start to build the static blog and start the production server.

// package.json "scripts": {   "dev": "next",   "build": "next build",   "start": "next start" }

The entire source code in this article is available on this GitHub repository. Feel free to clone it locally and play around with it. The repository also includes some basic placeholders to apply CSS to your blog.

Improvements

The blog, although functional, is perhaps too basic for most average cases. It would be nice to extend the framework or submit a patch to include some more features like:

  • Pagination
  • Syntax highlighting
  • Categories and Tags for posts
  • Styling

Overall, Next.js seems really very promising to build static websites, like a blog. Combined with its ability to export static HTML, we can built a truly standalone app without the need of a server!

The post Building a Blog with Next.js appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

A little bit of plain Javascript can do a lot

Julia Evans:

I decided to implement almost all of the UI by just adding & removing CSS classes, and using CSS transitions if I want to animate a transition.

An awful lot of the JavaScript on sites (that aren’t otherwise entirely constructed from JavaScript) is click the thing, toggle the class — which is why jQuery was so good and libraries like Alpine.js are finding happy developer audiences.

I once did a screencast called “Hey designers, if you only know one thing about JavaScript, this is what I would recommend which was basically: learn to toggle classes. From that:

Sometimes, to start a journey into learning something huge and complex, you need to learn something small and simple. JavaScript is huge and complex, but you can baby step into it by learning small and simple things. If you’re a web designer, I think there is one thing in particular that you can learn that is extremely empowering.

This is the thing I want you to learn: When you click on some element, change a class on some element.

Direct Link to ArticlePermalink

The post A little bit of plain Javascript can do a lot appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

How to delete all node_modules directories from your computer

Nice tip from Chris Ferdinandi:

My node_modules directories contained 50mb of stuff on the small side, and over 200mb of files in some cases. Over a few dozen projects, that really adds up!

Two dozen projects with 200 MB worth of node_modules? That’s nearly 5 GB of space for a bunch of stuff you’ve probably forgotten is even there, isn’t doing anything, and if you need again is a single command away. I feel like there should almost be a reaper app for these folders, deleting them if they haven’t been touched in a few weeks.

Nuke ’em:

# Mac/Linux find . -name "node_modules" -type d -prune -print | xargs du -chs  # Windows FOR /d /r . %d in (node_modules) DO @IF EXIST "%d" echo %d"

Direct Link to ArticlePermalink

The post How to delete all node_modules directories from your computer appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

How to Make a List Component with Emotion

I’ve been doing a bit of refactoring this week at Sentry and I noticed that we didn’t have a generic List component that we could use across projects and features. So, I started one, but here’s the rub: we style things at Sentry using Emotion, which I have only passing experience with and is described in the docs as…

[…] a library designed for writing css styles with JavaScript. It provides powerful and predictable style composition in addition to a great developer experience with features such as source maps, labels, and testing utilities. Both string and object styles are supported.

If you’ve never heard of Emotion, the general idea is this: when we’re working on big codebases with lots of components, we want to ensure that we can control the cascade of our CSS. So, let’s say you have an .active class in one file and you want to make sure that doesn’t impact the styles of a completely separate component in another file that also has a class of.active.

Emotion tackles this problem by adding custom strings to your classnames so they don’t conflict with other components. Here’s an example of the HTML it might output:

<div class="css-1tfy8g7-List e13k4qzl9"></div>

Pretty neat, huh? There’s lots of other tools and workflows out there though that do something very similar, such as CSS Modules.

To get started making the component, we first need to install Emotion into our project. I’m not going to walkthrough that stuff because it’s going to be different depending on your environment and setup. But once that’s complete we can go ahead and create a new component like this:

import React from 'react'; import styled from '@emotion/styled';  export const List = styled('ul')`   list-style: none;   padding: 0; `;

This looks pretty weird to me because, not only are we writing styles for the <ul> element, but we’re defining that the component should render a <ul>, too. Combining both the markup and the styles in one place feels odd but I do like how simple it is. It just sort of messes with my mental model and the separation of concerns between HTML, CSS, and JavaScript.

In another component, we can import this <List> and use it like this:

import List from 'components/list';  <List>This is a list item.</List>

The styles we added to our list component will then be turned into a classname, like .oefioaueg, and then added to the <ul> element we defined in the component.

But we’re not done yet! With the list design, I needed to be able to render a <ul> and an <ol> with the same component. I also needed a version that allows me to place an icon within each list item. Just like this:

The cool (and also kind of weird) thing about Emotion is that we can use the as attribute to select which HTML element we’d like to render when we import our component. We can use this attribute to create our <ol> variant without having to make a custom type property or something. And that happens to look just like this:

<List>This will render a ul.</List> <List as="ol">This will render an ol.</List>

That’s not just weird to me, right? It’s super neat, however, because it means that we don’t have to do any bizarro logic in the component itself just to change the markup.

It was at this point that I started to jot down what the perfect API for this component might look like though because then we can work our way back from there. This is what I imagined:

<List>   <ListItem>Item 1</ListItem>   <ListItem>Item 2</ListItem>   <ListItem>Item 3</ListItem> </List>  <List>   <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 1</ListItem>   <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 2</ListItem>   <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 3</ListItem> </List>  <List as="ol">   <ListItem>Item 1</ListItem>   <ListItem>Item 2</ListItem>   <ListItem>Item 3</ListItem> </List>

So after making this sketch I knew we’d need two components, along with the ability to nest icon subcomponents within the <ListItem>. We can start like this:

import React from 'react'; import styled from '@emotion/styled';  export const List = styled('ul')`   list-style: none;   padding: 0;   margin-bottom: 20px;    ol& {     counter-reset: numberedList;   } `;

That peculiar ol& syntax is how we tell emotion that these styles only apply to an element when it’s rendered as an <ol>. It’s often a good idea to just add a background: red; to this element to make sure your component is rendering things correctly.

Next up is our subcomponent, the <ListItem>. It’s important to note that at Sentry we also use TypeScript, so before we define our <ListItem> component, we’ll need to set our props up first:

type ListItemProps = {   icon?: React.ReactNode;   children?: string | React.ReactNode;   className?: string; };

Now we can add our <IconWrapper> component that will size an <Icon> component within the ListItem. If you remember from the example above, I wanted it to look something like this:

<List>   <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 1</ListItem>   <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 2</ListItem>   <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 3</ListItem> </List>

That IconBusiness component is a preexisting component and we want to wrap it in a span so that we can style it. Thankfully, we’ll need just a tiny bit of CSS to align the icon properly with the text and the <IconWrapper> can handle all of that for us:

type ListItemProps = {   icon?: React.ReactNode;   children?: string | React.ReactNode;   className?: string; };  const IconWrapper = styled('span')`   display: flex;   margin-right: 15px;   height: 16px;   align-items: center; `;

Once we’ve done this we can finally add our <ListItem> component beneath these two, although it is considerably more complex. We’ll need to add the props, then we can render the <IconWrapper> above when the icon prop exists, and render the icon component that’s passed into it as well. I’ve also added all the styles below so you can see how I’m styling each of these variants:

export const ListItem = styled(({icon, className, children}: ListItemProps) => (   <li className={className}>     {icon && (       <IconWrapper>         {icon}       </IconWrapper>     )}     {children}   </li> ))<ListItemProps>`   display: flex;   align-items: center;   position: relative;   padding-left: 34px;   margin-bottom: 20px; 	   /* Tiny circle and icon positioning */   &:before, 	& > $ {IconWrapper} {     position: absolute;     left: 0;   }    ul & {     color: #aaa;     /* This pseudo is the tiny circle for ul items */      &:before {       content: '';       width: 6px;       height: 6px;       border-radius: 50%;       margin-right: 15px;       border: 1px solid #aaa;       background-color: transparent;       left: 5px;       top: 10px;     } 		     /* Icon styles */     $ {p =>       p.icon &&       `       span {         top: 4px;       }       /* Removes tiny circle pseudo if icon is present */       &:before {         content: none;       }     `}   }   /* When the list is rendered as an <ol> */   ol & {     &:before {       counter-increment: numberedList;       content: counter(numberedList);       top: 3px;       display: flex;       align-items: center;       justify-content: center;       text-align: center;       width: 18px;       height: 18px;       font-size: 10px;       font-weight: 600;       border: 1px solid #aaa;       border-radius: 50%;       background-color: transparent;       margin-right: 20px;     }   } `;

And there you have it! A relatively simple <List> component built with Emotion. Although, after going through this exercise I’m still not sure that I like the syntax. I reckon it sort of makes the simple stuff really simple but the medium-sized components much more complicated than they should be. Plus, it could be pretty darn confusing to a newcomer and that worries me a bit.

But everything is a learning experience, I guess. Either way, I’m glad I had the opportunity to work on this tiny component because it taught me a few good things about TypeScript, React, and trying to make our styles somewhat readable.

The post How to Make a List Component with Emotion appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

These Startups Created by Designers are Proof You Can Do It Too

[Top]

Displaying the Current Step with CSS Counters

Say you have five buttons. Each button is a step. If you click on the fourth button, you’re on step 4 of 5, and you want to display that.

This kind of counting and displaying could be hard-coded, but that’s no fun. JavaScript could do this job as well. But CSS? Hmmmm. Can it? CSS has counters, so we can certainly count the number of buttons. But how do we calculate only up to a certain button? Turns out it can be done.

Thanks to Jan Enning for emailing in about this trick, it’s very clever!

HTML

It doesn’t have to be buttons; it just needs to be some sibling elements we can count. But we’ll go ahead and use buttons here:

<div class="steps">    <button class="active">Shop</button>   <button>Cart</button>   <button>Shipping</button>   <button>Checkout</button>   <button>Thank You</button>    <div class="message"></div>  </div>

The empty .message div there will be where we output our step messaging with CSS content.

CSS

The trick is that we’re actually going to use three counters:

  1. A total count of all the buttons
  2. A count of the current step
  3. A count of how many remaining steps are after the current step
.steps {   counter-reset:      currentStep 0      remainder 0      totalStep 0; }

Now let’s actually do the counting. To count all buttons is straightforward:

button {   counter-increment: totalStep; }

Next, we need another thing to count that will also count the buttons. We can use a pseudo-element that’s only purpose is to count buttons:

button::before {   content: "";   counter-increment: currentStep; }

The trick is to stop counting that pseudo-element on all the elements after the active element. If we’re using an .active class that looks like this:

button.active ~ button::before {   /* prevents currentStep from being incremented! */   counter-increment: remainder; }

We’re counting the remainder there, which might also be useful, but because we’re only incrementing the remainder, that means we’re not counting the currentStep counter. Fancy, fancy.

Then we can use the counters to output our messaging:

message::before {   content: "Step: " counter(currentStep) " / " counter(totalStep); }

Here it is!

There is a little JavaScript there so you can play with moving the active state on the button, but the counting and messaging is all CSS.

The post Displaying the Current Step with CSS Counters appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]