The cultivation of knowledge entails a process, and visualizing this construction process becomes imperative. I am endeavoring to establish a system of identification to denote the maturity level of each note within my digital garden, ensuring a keen grasp on their completeness. The development of this system not only delineates the process of knowledge production but also pushes me to conduct a whole development of thoughts.

This note serves as a record of the construction.

Overview

Generally, I employ the growth stages of a tree (sprout, sapling, tree) to depict the completion status of articles, using raindrops to signify inspirational content, guide boards to represent guiding material, and withered leaves to denote outdated content.

In each note, a front-matter property maturity is used to classify different types of maturity. A component and a plugin are constructed to create pages listing notes based on their maturity level.

Modification Process

Front-matter property

In front-matter part of a note, I use a property maturity to identify the type:

---
...
maturity: sprout|sapling|tree|withered|guideboard|raindrop
---

Icons preparation

I use a series of icons to visually represent the maturity level of the notes. These icons are all chosen from lucide. Just choose icons you like and get their SVG files (or codes). Encode the SVG into Base64 format for embedding these icons within the CSS file. These base64 codes are documented within the /quartz/styles/custom.scss file:

quartz/styles/custom.scss
body {
  --note-icon-1: url(data:image/svg+xml;base64,YOUR_BASE64_CODES_HERE);
  --note-icon-2: url(data:image/svg+xml;base64,YOUR_BASE64_CODES_HERE);
  --note-icon-3: url(data:image/svg+xml;base64,YOUR_BASE64_CODES_HERE);
  --note-icon-4: url(data:image/svg+xml;base64,YOUR_BASE64_CODES_HERE);
  --note-icon-5: url(data:image/svg+xml;base64,YOUR_BASE64_CODES_HERE);
  --note-icon-6: url(data:image/svg+xml;base64,YOUR_BASE64_CODES_HERE);
}

Data type of maturity

To generate listing pages of each maturity, function flatMap is used, which can only deal with string[] type. We shall convert the maturity value in front-matter into array type. We use frontmatter.ts transformer to deal with it:

quartz/plugins/transformers/frontmatter.ts
export const FrontMatter: QuartzTransformerPlugin<Partial<Options> | undefined> = (userOpts) => {
  const opts = { ...defaultOptions, ...userOpts }
  return {
    name: "FrontMatter",
    markdownPlugins({ cfg }) {
      return [
        [remarkFrontmatter, ["yaml", "toml"]],
        () => {
          return (_, file) => {
          ...
          ...  
            if (data.maturity && !Array.isArray(data.maturity)) {
              data.maturity = data.maturity
                .toString()
                .split(",")
                .map((maturity: string) => maturity.trim())
            } else {
              data.maturity = ["sprout"]
            }
            data.maturity = [...new Set(data.maturity?.map((maturity: string) => slugTag(maturity)))]
            ...
          }
        },
      ]
    },
  }
}
 
declare module "vfile" {
  interface DataMap {
    frontmatter: { [key: string]: unknown } & {
      title: string
    } & Partial<{
        ...
        ...
        maturity: string[]
      }>
  }
}

i18n of maturity

Edit the i18n file for proper display of texts.

quartz/i18n/locales/definition.ts
...
export interface Translation {
  ...
  .pages: {
    maturityContent: {
      itemsUnderTag: (variables: { count: number }) => string
      showingFirst: (variables: { count: number }) => string
      totalTags: (variables: { count: number }) => string
    }
  }
}

Choose your own language, take en-US as an example:

quartz/i18n/locales/en-US.ts
export default {
  ...
  pages {
    ...
    maturityContent: {
      itemsUnderTag: ({ count }) =>
        count === 1 ? "1 page under this maturity." : `${count} pages under this maturity.`,
      showingFirst: ({ count }) => `Showing recent ${count} pages.`,
      totalTags: ({ count }) => `${count} total types of maturity in all.`,
    },
  },
}

Add maturity in tags field

Maturity value of each note is added in the tags field in the rendered page.

quartz/components/TagList.tsx
...
const TagList: QuartzComponent = ({ fileData, displayClass }: QuartzComponentProps) => {
  const tags = fileData.frontmatter?.tags
  let maturity : string[]
  maturity = fileData.frontmatter?.maturity ?? ["sprout"]
  let icon = ""
  switch(maturity[0]) {
    case "sprout":
      icon = "Sprout"
      break
    case "sapling":
      icon = "Sapling"
      break
    case "tree":
      icon = "Tree"
      break
    case "withered":
      icon = "Withered"
      break
    case "guideboard":
      icon = "Guideboard"
      break
    case "raindrop":
      icon = "Raindrop"
      break
  }
  const maturityLink = `/maturity/${maturity}`
 
  const baseDir = pathToRoot(fileData.slug!)
  if (tags && tags.length > 0) {
    return (
      <ul class={classNames(displayClass, "tags")}>
        <li>
          <a href={maturityLink} class="internal tag-link" data-icon={maturity}>
            {icon}
          </a>
        </li>
        {tags.map((tag) => {
          ...
        })}
      </ul>
    )
  } else {
    return (
      <ul class={classNames(displayClass, "tags")}>
        <li>
          <a href={maturityLink} class="internal tag-link" data-icon={maturity}>
            {icon}
          </a>
        </li>
      </ul>
    )
  }
}
...

Generate listing pages of each maturity

To generate pages which lists notes based on their maturity, we need new component and plugin/emitter.

The source code of these two file can be found here:

Necessary styles

We need some necessary styles for proper display.

quartz/styles/custom.scss
*[data-icon]::before {
  content: " ";
  display: inline-block;
  padding-right: 0.3em;
  background-size: contain;
  background-repeat: no-repeat;
}
 
a.internal.tag-link[data-icon]::before{
  width: 1.4em;
}
 
h2[data-icon]::before{
  height: 1.2em;
  width: 1.2em;
  margin-bottom: -0.15em;
}
 
*[data-icon="sprout"]::before {
  background-image: var(--note-icon-1)
}
 
*[data-icon="sapling"]::before {
  background-image: var(--note-icon-2)
}
 
*[data-icon="tree"]::before {
  background-image: var(--note-icon-3)
}
 
*[data-icon="withered"]::before {
  background-image: var(--note-icon-4)
}
 
*[data-icon="guideboard"]::before {
  background-image: var(--note-icon-5)
}
 
*[data-icon="raindrop"]::before {
  background-image: var(--note-icon-6)
}
 
span.maturity-count {
  font-weight: bold;
  font-style: italic;
  font-family: var(--headerFont);
}

At this stage, the modification has finished.