Archive note: This guide was originally published on July 1, 2023. It documents an older portfolio built with Docsify; that site is no longer used. The experiment eventually grew into a tiny content framework and helped shape the site you’re reading now.

Context

In today’s digital world, a strong online presence is useful for professionals in almost every field. A thoughtfully curated portfolio can showcase your skills, accomplishments, and experience to potential employers, clients, and collaborators.

Initially, I hesitated to create one. I didn’t want to build another React app or reach for a comprehensive framework such as Gatsby when both felt excessive for what I needed.

I changed my mind after writing documentation for work projects with Docsify.

What will you need?

All you need is a cup of coffee and a little Markdown. Voilà: a responsive, performant webpage.

Focus on the content first. Docsify keeps the starting point small, but it doesn’t prevent you from adding complexity later when you actually need it.

If Markdown is new to you, keep the Markdown cheat sheet nearby.

Create the project

I lied: we are going to use a few dependencies. Please don’t leave; I can justify them.

Create a folder for the portfolio:

mkdir awesome-portfolio
cd awesome-portfolio

Initialize package.json:

npm init

Press Enter to accept each default, or customize the values as you go. Then install the Docsify command-line tool:

npm install --save-dev docsify-cli

Initialize the site and start its development server:

npx docsify init .
npx docsify serve .
The initial Docsify site with a Home sidebar item, Headline heading, and short project description.
The portfolio immediately after initializing Docsify.

The page content lives in README.md. Before continuing, add a convenient development command to package.json:

{
  "scripts": {
    "start": "docsify serve . --port=3001"
  }
}

From now on, npm run start will serve the site on port 3001.

Level up the site

The first version is intentionally simple. Docsify’s community plugins let us add features by editing index.html rather than rebuilding the project around another framework.

Enable the Docsify sidebar in the configuration:

<script>
  window.$docsify = {
    name: "",
    repo: "",
    loadSidebar: true,
    subMaxLevel: 4,
  }
</script>

Create _sidebar.md:

- [Home](/)

Then add a folder for the site’s sections and create the résumé page:

mkdir sections
touch sections/resume.md

Add the new page to _sidebar.md:

- [Home](/)
- [Resume](sections/resume.md)
The Docsify site with Home and Resume links visible in its sidebar.
The résumé section added to the sidebar.

Add Docsify’s full-text search plugin beneath the existing Docsify script:

<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
The Docsify sidebar with a search field above the Home and Resume links.
Full-text search added to the sidebar.

Copy to clipboard

If you include code examples, give readers a quick way to copy them:

<script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>

Now fenced code blocks receive a copy button automatically.

A code example in Docsify with a Copy to clipboard button.
The copy-to-clipboard plugin attached to a code block.

Theme

The Docsify Darklight Theme adds theme switching and exposes colors we can customize.

Add the script near the end of index.html:

<script
  src="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js"
  type="text/javascript"
></script>

Add its stylesheet inside <head>:

<link
  rel="stylesheet"
  href="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/style.min.css"
  title="docsify-darklight-theme"
  type="text/css"
/>

The theme can be customized from window.$docsify. This was the pink palette used by the original portfolio:

window.$docsify = {
  name: "",
  repo: "",
  loadSidebar: true,
  darklightTheme: {
    siteFont: "PT Sans",
    defaultTheme: "dark",
    codeFontFamily: "Open Sans",
    bodyFontSize: "17px",
    dark: {
      accent: "#FF80AB",
      background: "#1E1E1E",
      textColor: "#F5F5F5",
      codeBackgroundColor: "#222222",
      borderColor: "#333333",
    },
    light: {
      accent: "#FF80AB",
      background: "#F5F5F5",
      textColor: "#333333",
      codeBackgroundColor: "#F0F0F0",
      borderColor: "#CCCCCC",
    },
  },
}
The customized Docsify portfolio using a dark background and bright pink accents.
The first dark theme with pink accents.

Add the actual content

The framework is ready. Now we need résumé, project, and blog content.

Résumé

Write your experience directly in sections/resume.md. A Markdown résumé template can help with the first draft, but replace every placeholder with your own information.

Portfolio

Create folders for project pages and the portfolio index:

mkdir portfolio
touch sections/portfolio.md
touch portfolio/project-1.md

Add both levels to _sidebar.md:

- [Home](/)
- [Resume](sections/resume.md)
- [Portfolio](sections/portfolio.md)
  - [Project 1](portfolio/project-1.md)

A repeatable project outline keeps each entry useful:

# Project title

## Technologies used

## Purpose

## Source code

## Demo

## Screenshots

## Code examples

## Additional information
A dark Docsify portfolio page displaying three personal projects in a row.
The generated personal-projects page.

Blog

Create a blogs folder. I organized each post by date and slug, keeping its media beside the Markdown file:

blogs/
└── 2023-07-06/
    └── how-to-create-a-blog/
        ├── assets/
        │   └── cover.png
        └── post.md

Create the blog index and add it to the sidebar:

mkdir blogs
touch sections/blog.md
- [Home](/)
- [Resume](sections/resume.md)
- [Portfolio](sections/portfolio.md)
- [Blog](sections/blog.md)

A tiny content generator

You could stop here and continue by copying existing files whenever you need a new page. I wouldn’t blame you. But generating those repeated files from templates can save time, and became the most interesting part of this experiment.

Install Plop:

npm install --save-dev plop

The generator eventually grew into this structure:

generators/
├── helpers.js
├── plopfile.js
├── section.js
├── portfolio.js
├── blog.js
└── templates/
    ├── sections/
    ├── portfolio/
    ├── blogs/
    └── variables/

The central plopfile.js registered a generator for each content type:

const sectionGenerator = require("./section.js")
const portfolioGenerator = require("./portfolio.js")
const blogGenerator = require("./blog.js")
const helpers = require("./helpers")

module.exports = (plop) => {
  plop.setHelper("capitalize", helpers.capitalize)
  plop.setHelper("lowercase", helpers.lowercase)

  plop.setGenerator("section", sectionGenerator)
  plop.setGenerator("portfolio", portfolioGenerator)
  plop.setGenerator("blog", blogGenerator)
}

Comments such as <!-- Insert new section here --> gave the generators a predictable place to insert navigation items.

Add a command to package.json:

{
  "scripts": {
    "file:generate": "plop --plopfile generators/plopfile.js"
  }
}

Running npm run file:generate could now create a section and update the sidebar in one step.

The dark Docsify site with generated Home, Resume, Portfolio, and Blog sections in its sidebar.
The navigation after generating several sections.

The portfolio generator created a project Markdown file, added its card to the portfolio index, and added a nested sidebar link. The blog generator did the same for dated post folders, cover assets, the blog index, and the latest-post area.

A generated dark Docsify blog index showing an example post and a nested Blog sidebar link.
The generated blog index and navigation entry.

The original generator source files are not part of this archive, but the experiment continued in Old Website, Part 1.

Next: publish the portfolio with GitHub Pages, then add end-to-end tests with Cypress.

Cover photograph by Hal Gatewood.