Tag: Blog

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

, ,

Cool Little CSS Grid Tricks for Your Blog

I discovered CSS about a decade ago while trying to modify the look of a blog I had created. Pretty soon, I was able to code cool things with more mathematical and, therefore, easier-to-understand features like transforms. However, other areas of CSS, such as layout, have remained a constant source of pain.

This post is about a problem I encountered about a decade ago and, until recently, did not know how to solve in a smart way. Specifically, it’s about how I found a solution to a long-running problem using a modern CSS grid technique that, in the process, gave me even cooler results than I originally imagined.

That this is not a tutorial on how to best use CSS grid, but more of a walk through my own learning process.

The problem

One of the first things I used to dump on that blog were random photos from the city, so I had this idea about having a grid of thumbnails with a fixed size. For a nicer look, I wanted this grid to be middle-aligned with respect to the paragraphs above and below it, but, at the same time, I wanted the thumbnails on the last row to be left-aligned with respect to the grid. Meanwhile, the width of the post (and the width of the grid within it) would depend on the viewport.

The HTML looks something like this:

<section class='post__content'>   <p><!-- some text --></p>   <div class='grid--thumbs'>     <a href='full-size-image.jpg'>       <img src='thumb-image.jpg' alt='image description'/>     </a>     <!-- more such thumbnails -->   </div>   <p><!-- some more text --></p> </section>

It may seem simple, but it turned out to be one of the most difficult CSS problems I’ve ever encountered.

Less than ideal solutions

These are things I have tried or seen suggested over the years, but that never really got me anywhere.

Floating impossibility

Floats turned out to be a dead end because I couldn’t figure out how to make the grid be middle aligned this way.

.grid--thumbs { overflow: hidden; }  .grid--thumbs a { float: left; }

The demo below shows the float attempt. Resize the embed to see how they behave at different viewport widths.

inline-block madness

At first, this seemed like a better idea:

.grid--thumbs { text-align: center }  .grid--thumbs a { display: inline-block }

Except it turned out it wasn’t:

The last row isn’t left aligned in this case.

At a certain point, thanks to an accidental CSS auto-complete on CodePen, I found out about a property called text-align-last, which determines how the last line of a block is aligned.

Unfortunately, setting text-align-last: left on the grid wasn’t the solution I was looking for either:

At this point, I actually considered dropping the idea of a middle aligned grid. Could a combo of text-align: justified and text-align-last: left on the grid produce a better result?

Well, turns out it doesn’t. That is, unless there’s only a thumbnail on the last row and the gaps between the columns aren’t too big. Resize the embed below to see what I mean.

This is pretty where I was at two years ago, after nine years of trying and failing to come up with a solution to this problem.

Messy flexbox hacks

A flexbox solution that seemed like it would work at first was to add an ::after pseudo-element on the grid and set flex: 1 on both the thumbnails and this pseudo-element:

.grid--thumbs {   display: flex;   flex-wrap: wrap; 	   a, &::after { flex: 1; } 	   img { margin: auto; } 	   &:after { content: 'AFTER'; } }

The demo below shows how this method works. I’ve given the thumbnails and the ::after pseudo-element purple outlines to make it easier to see what is going on.

This is not quite what I wanted because the grid of thumbnails is not middle-aligned. Thats said, it doesn’t look too bad… as long as the last row has exactly one item less image than the others. As soon as that changes, however, the layout breaks if it’s missing more items or none.

Screenshot collage. Shows how the layout breaks when the last row is not missing exactly one item to be full.
Why the ::after hack is not reliable.

That was one hacky idea. Another is to use a pseudo-element again, but add as many empty divs after the thumbnails as there are columns that we’re expecting to have. That number is something we should be able to approximate since the size of the thumbnails is fixed. We probably want to set a maximum width for the post since text that stretches across the width of a full screen can visually exhausting for eyes to read.

The first empty elements will take up the full width of the row that’s not completely filled with thumbnails, while the rest will spill into other rows. But since their height is zero, it won’t matter visually.

This kind of does the trick but, again, it’s hacky and still doesn’t produce the exact result I want since it sometimes ends up with big and kind of ugly-looking gaps between the columns.

A grid solution?

The grid layout has always sounded like the answer, given its name. The problem was that all examples I had seen by then were using a predefined number of columns and that doesn’t work for this particular pattern where the number of columns is determined by the viewport width.

Last year, while coding a collection of one element, pure CSS background patterns, I had the idea of generating a bunch of media queries that would modify a CSS variable, --n, corresponding to the number of columns used to set grid-template-columns.

$ w: 13em; $ h: 19em; $ f: $ h/$ w; $ n: 7; $ g: 1em;  --h: #{$ f*$ w}; display: grid; grid-template-columns: repeat(var(--n, #{$ n}), var(--w, #{$ w})); grid-gap: $ g; place-content: center; 	 @for $ i from 1 to $ n {   @media (max-width: ($ n - $ i + 1)*$ w + ($ n - $ i + 2)*$ g) {     --n: #{$ n - $ i}   } }

I was actually super proud of this idea at the time, even though I cringe looking back on it now. One media query for every number of columns possible is not exactly ideal, not to mention it doesn’t work so well when the grid width doesn’t equal the viewport width, but is still somewhat flexible and also depends on the width of its siblings.

A magic solution

I finally came across a better solution while working with CSS grid and failing to understand why the repeat() function wasn’t working in a particular situation. It was so frustrating and prompted me to go to MDN, where I happened to notice the auto-fit keyword and, while I didn’t understand the explanation, I had a hunch that it could help with this other problem, so I dropped everything else I was doing and gave it a try.

Here’s what I got:

.grid--thumbs {   display: grid;   justify-content: center;   grid-gap: .25em;   grid-template-columns: repeat(auto-fit, 8em); }

I also discovered the minmax() function, which can be used in place of fixed sizes on grid items. I still haven’t been able to understand exactly how minmax() works — and the more I play with it, the less I understand it — but what it looks like it does in this situation is create the grid then stretch its columns equally until they fill all of the available space:

grid-template-columns: repeat(auto-fit, minmax(8em, 1fr));

Another cool thing we can do here is prevent the image from overflowing when it’s wider than the grid element. We can do this by replacing the minimum 8em with min(8em, 100%) That essentially ensures that images will never exceed 100%, but never below 8em. Thanks to Chris for this suggestion!

Note that the min() function doesn’t work in pre-Chromium Edge!

Keep in mind that this only produces a nice result if all of the images have the same aspect ratio — like the square images I’ve used here. For my blog, this was not an issue since all photos were taken with my Sony Ericsson W800i phone, and they all had the same aspect ratio. But if we were to drop images with different aspect ratios, the grid wouldn’t look as good anymore:

We can, of course, set the image height to a fixed value, but that distorts the images… unless we set object-fit to cover, which solves our problem!

Another idea would be to turn the first thumbnail into a sort of banner that spans all grid columns. The one problem is that we don’t know the number of columns because that depends on the viewport. But, there is a solution — we can set grid-column-end to -1!

.grid--thumbs {   /* same styles as before */ 	   a:first-child {     grid-column: 1/ -1; 		     img { height: 13em }   } }

The first image gets a bigger height than all the others.

Of course, if we wanted the image to span all columns except the last, one we’d set it to -2 and so on… negative column indices are a thing!

auto-fill is another grid property keyword I noticed on MDN. The explanations for both are long walls of text without visuals, so I didn’t find them particularly useful. Even worse, replacing auto-fit with auto-fill in any of the grid demos above produces absolutely no difference. How they really work and how they differ still remains a mystery, even after checking out articles or toying with examples.

However, trying out different things and seeing what happens in various scenarios at one point led me to the conclusion that, if we’re using a minmax() column width and not a fixed one (like 8em), then it’s probably better to use auto-fill instead of auto-fit because, the result looks better if we happen to only have a few images, as illustrated by the interactive demo below:

I think what I personally like best is the initial idea of a thumbnail grid that’s middle-aligned and has a mostly fixed column width (but still uses min(100%, 15em) instead of just 15em though). At the end of the day, it’s a matter of personal preference and what can be seen in the demo below just happens to look better to me:

I’m using auto-fit in this demo because it produces the same result as auto-fill and is one character shorter. However, what I didn’t understand when making this is that both keywords produce the same result because there are more items in the gallery than we need to fill a row.

But once that changes, auto-fit and auto-fill produce different results, as illustrated below. You can change the justify-content value and the number of items placed on the grid:

I’m not really sure which is the better choice. I guess this also depends on personal preference. Coupled with justify-content: center, auto-fill seems to be the more logical option, but, at the same time, auto-fit produces a better-looking result.

The post Cool Little CSS Grid Tricks for Your Blog appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Let’s Say You Were Going to Write a Blog Post About Dark Mode

This is not that blog post. I’m saying let’s say you were.

This is not a knock any other blog posts out there about Dark Mode. There are lots of good ones, and I’m a fan of any information-sharing blog post. This is more of a thought exercise on what I think it would take to write a really great blog post on this subject.

  • You’d explain what Dark Mode is. You wouldn’t dwell on it though, because chances are are people reading a blog post like this already essentially know what it is.
  • You’d definitely have a nice demo. Probably multiple demos. One that is very basic so the most important lines of code can be easily seen. Perhaps something that swaps out background-color and color. The other demo(s) will deal with more complex and real-world scenarios. What do you do with images and background images? SVG strokes and fills? Buttons? Borders? Shadows? These are rare things that sites have, so anyone looking at designing a Dark Mode UI will come across them.
  • You’d deal with the fact that Dark Mode is a choice that can happen at the operating system level itself. Fortunately, we can detect that in CSS, so you’ll have to cover how.
  • JavaScript might need to know about the operating system choice as well. Perhaps because some styling is happening at the JavaScript level, but also because of this next thing.
  • Dark Mode could (should?) be a choice on the website as well. That servers cases where, on this particular site, a user prefers a choice opposite of what their operating system preference is.
  • Building a theme toggle isn’t a small job. If your site has authentication, that choice should probably be remembered at the account level. If it doesn’t, the choice should be remembered in some other way. One possibility is localStorage, but that can have problems, like the fact that CSS is generally applied to a page before JavaScript executes, meaning you’re facing a “flash of incorrect theme” situation. You might be needing to deal with cookies so that you can send theme-specific CSS on each page load.
  • Your blog post would include real-world examples of people already doing this. That way, you can investigate how they’ve done it and evaluate how successful they were. Perhaps you can reach out to them for comment as well.
  • You’ll be aware of other writing on this subject. That should not dissuade you from writing about the subject yourself, but a blog post that sounds like you’re the first and only person writing about a subject when you clearly aren’t has an awkward tone to it that doesn’t come across well. Not only can you learn from others’ writing, but you can also pull from it and potentially take it further.
  • Since you’ll be covering browser technology, you’ll be covering the support of that technology across the browser landscape. Are there notable exceptions in support? Is that support coming? Have you researched what browsers themselves are saying about the technology?
  • There are accessibility implications abound. Dark Mode itself can be considered an accessibility feature, and there are tangential accessibility issues here too, like how the toggle works, how mode changes are announced, and a whole new set of color contrasts to calculate and get right. A blog post is a great opportunity to talk about all that. Have you researched it? Have you talked to any people who have special needs around these features? Any experts? Have you read what accessibility people are saying about Dark Mode?

That was all about Dark Mode, but I bet you could imagine how considering all these points could benefit any blog post covering a technical concept.

The post Let’s Say You Were Going to Write a Blog Post About Dark Mode appeared first on CSS-Tricks.

CSS-Tricks

, , , , , , , ,
[Top]

Embed a Blog Onto Any Website With DropInBlog

With DropInBlog, you can embed a blog into your site in only three minutes. A quick JavaScript/HTML widget, or a full-featured JSON API, is all it takes.

A headless blog you can take anywhere

Ever been working on your existing static site or anything that wasn’t built with WordPress, wanted to integrate a blog, but then couldn’t find a clean solution?

DropInBlog to the rescue.

Now you can quickly integrate a full featured blog into any existing site. Actually, any existing page. Drop it right into your existing template. It’s so easy you can be up and running in just a few minutes. Check out this live example:

See Project

Blogging is simple with the full-featured editor

You’ll feel at home with the simple admin panel. Effortlessly create posts, categories, and authors. Tweak the blog output settings and enable features like share buttons, Disqus or Facebook comments with a couple of clicks.

Try it Free

Embed your blog in no time

You can embed your blog on a site in about three quick minutes. From there, you have a ton of control to customize and even put a variety of widgets to use, like Author List, Recent Post List, Category List… and more!

Check out this short video to see how quickly you can get up and running:

Need help getting setup? Hit up their friendly support on Messenger.

Simple JavaScript or JSON API… it’s your call.

There are a few integration options ranging from the drop-dead simple JavaScript method to the super flexible JSON API. You get to choose what works best for you.

Ready to give DropInBlog a try? Get started for free.

The post Embed a Blog Onto Any Website With DropInBlog appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]