使用 Eleventy 创建自己的 Bragdoc

Avatar of Emily Leung
Emily Leung

DigitalOcean 为您旅程的每个阶段提供云产品。从 免费赠送的 $200 信用额度开始使用!

无论您处于开发过程中的哪个阶段,我们完成的任务(无论大小)都会对我们的个人和职业发展产生巨大影响。不幸的是,这些任务并不总是得到认可,因为它们很容易在需要完成的其他事情的海洋中迷失。

我们所做的未被注意到的任务属于被称为“无形工作”的范畴,这是我在一个名为 “为无形工作争取认可” 的演讲中偶然发现的概念,该演讲由 Ryan T. Harter 主持。这种类型的工作会渗透到缝隙中,因为我们的大脑不是为记忆事物而设计的。然而,到了审查时间,我们却发现自己在尝试回忆过去 6 个月或 12 个月所做的事情时,反复陷入困境。

为了解决这个由来已久的问题,Julia Evans 撰写了一篇 文章,建议我们保留自己的“吹嘘文档”。一个 吹嘘文档 正如它听起来的那样。它是一个文档,您可以在其中允许自己吹嘘自己所做的所有有价值的工作。无论它是

  • 您对项目的贡献
  • 帮助他人
  • 改进现有流程
  • 发表演讲或举办研讨会
  • 您学到的东西
  • 课外活动(例如博客、演讲、个人项目)
  • 奖项和职业发展

没有一种正确的方法来写吹嘘文档,但这并没有阻止 Jonny BurchProgression 团队构建 bragdocs.com.

使用他们的网站构建一个是一个好主意,但有什么比从头开始创建自己的吹嘘文档更好的方法来吹嘘你的工作呢?

今天,我想向您展示如何使用静态网站生成器 Eleventy 重新创建 bragdocs.com。只需一点点 JavaScript 和 CSS,您就可以让自己的吹嘘文档运行起来!

我们将构建什么?

以下是本教程的最终结果。您可以在 此处找到实时演示。它模仿 bragdocs.com 作为您从头开始创建吹嘘文档并使其成为自己的起点。

要求

  • 在 Node.js(版本 10 或更高版本)中安装软件包
  • 对 HTML 和 CSS 的一般了解
  • Markdown、Nunjucks 模板和 JavaScript(都是可选的,但很有帮助)
  • 基本的编程概念,包括 if 语句、循环和访问 JSON 中的变量

什么是 Eleventy?

Eleventy 是一个静态网站生成器。这意味着,您不必构建一个完整的网站(前端和后端),而是可以灵活地在 Eleventy 接受的任何模板语言中编写内容:HTML、Markdown、Liquid、Nunjucks、Mustache 等。然后处理内容(如果您愿意,可以使用自定义模板)以生成静态 HTML 页面,准备作为功能齐全的网站进行托管。

设置我们的“Hello, World!”Eleventy 项目

在本教程中,我将要提到的存储库是 eleventy-bragdoc,我们最终要实现的产品将被称为“bragdoc”。

使用具有 README.md .gitignore (用于 Node)的 GitHub 存储库,我开始设置 Eleventy 项目。

创建新项目

eleventy-bragdoc 中,我从以下文件开始

eleventy-bragdoc
├── README.md
└── .gitignore // .gitignore for node

在终端导航到 eleventy-bragdoc 内部后,我通过运行以下命令初始化了项目

npm init -y

这为我的 node 包创建了一个 package.json 文件。

eleventy-bragdoc
├── package.json // new file
├── README.md
└── .gitignore

接下来,我安装了 Eleventy。

npm install @11ty/eleventy

这给了我以下文件和文件夹列表

eleventy-bragdoc
├── node_modules  // new folder
├── package.json
├── package-lock.json  // new file
├── README.md
└── .gitignore

配置 Eleventy 项目

安装完 Eleventy 后,我更新了 package.json 文件中的 scripts 以包含以下命令

  • start 命令在开发期间提供项目,它运行 Browsersync 以进行热重载。
  • build 命令创建生产就绪的 HTML 文件,以便它可以托管到服务器上。
{
  // ...
  "scripts": {
    "start": "eleventy --serve",
    "build": "eleventy"
  },
 //  ...
}

接下来,我创建了名为 .eleventy.js 的必需配置文件以指定自定义输入和输出目录。

eleventy-bragdoc
├── .eleventy.js  // new file
├── node_modules
├── package.json
├── package-lock.json
├── README.md
└── .gitignore

.eleventy.js 中,我告诉 Eleventy 它将引用 src 文件夹中的内容来构建 HTML 文件。输出然后存储在名为 public 的文件夹中

module.exports = function(eleventyConfig) {
  return {
    dir: {
      input: "src",
      output: "public"
    }
  }
}

创建面向用户的内容

为了制作我的第一个页面,我创建了 src 文件夹,我已在 .eleventy.js 中将其声明为输入目录。在其中,我添加了我的第一个页面,一个名为 index.md 的 Markdown 文件

Eleventy 支持 多种模板语言,您可以随意组合使用:HTML、Markdown、Liquid、Nunjucks、JavaScript、Handlebars、Mustache、EJS、Haml、Pug。

eleventy-bragdoc
├── src
│   └── index.md  // new file
├── .eleventy.js
├── node_modules
├── package.json
├── package-lock.json
├── README.md
└── .gitignore

在 Eleventy 中,任何在上面和下面的破折号(---)之间编写的键值对都被认为是前置信息。

index.md 中,我包含了一个值为“11ty x Bragdocs”的 title 属性,以及前置信息下的一些测试内容。

---
title: "11ty x Bragdocs"
---

This is the home page.

构建模板

接下来,我创建了一个 Eleventy 预期的文件夹,名为 _includes ,位于 src 内部。这是模板(或 Eleventy 所称的布局)必须存放的位置。在该文件夹中,我为我的第一个模板 base.njk 创建了一个名为 layouts 的子文件夹

.njk 文件类型指的是 Nunjucks 模板语言。

eleventy-bragdoc
├── src
│   ├── _includes  // new folder
│   │   └── layouts  // new folder
│   │       └── base.njk  // new file
│   └── index.md
├── .eleventy.js
├── node_modules
├── package.json
├── package-lock.json
├── README.md
└── .gitignore

我在 base.njk 中添加了一个 HTML5 模板

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
    
</body>
</html>

使用模板和前置信息创建页面

base.njk 中,在 <title> 标签之间,我想拉取 index.md 前置信息中定义的 title 属性,因此我使用了双大括号,即 {{title}},来访问此变量。类似地,在正文中,我添加了 <h1> 标签,并将其设置为相同的 title 属性。

接下来,我使用 content 属性引入了 index.md 中的其余正文内容。使用提供的 safe 过滤器,我告诉 Eleventy 渲染而不是转义 Markdown 文件内容中的任何 HTML。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{ title }}</title>
</head>
<body>
  <h1>{{ title }}</h1>
  {{ content | safe }}
</body>
</html>

然后我跳回 index.md 并将 layout 属性添加到前置信息并引用 base.njk

---
title: "11ty x Bragdocs"
layout: "layouts/base.njk"
---

This is the home page.

为了让您了解运行构建时会发生什么,前置信息 layout 属性中指定的模板用于包装 Markdown 内容。在这个例子中,编译后的 HTML 将如下所示

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>11ty x Bragdocs</title>
</head>
<body>
  <h1>11ty x Bragdocs</h1>
  <p>This is the home page.</p>
</body>
</html>

在构建过程中连接 CSS 和图像文件夹

虽然这部分可能不是所有 Eleventy 项目都需要的,但 CSS 和自托管图像始终是值得添加的功能。因此,我在 **src** 目录中创建了两个文件夹:**css** 和 **images**。

eleventy-bragdoc
├── src
│   ├── css  // new folder
│   ├── images  // new folder
│   ├── _includes
│   │   └── layouts
│   │       └── base.njk
│   └── index.md
├── .eleventy.js
├── node_modules
├── package.json
├── package-lock.json
├── README.md
└── .gitignore

然后,在 **.eleventy.js** 中,因为我希望这些文件夹中的内容在托管时可访问,所以我通过添加以下配置来引用这些文件夹。

  • **addWatchTarget** 告诉 Eleventy,当我们对该目录中的文件进行更改时(例如,**css** 文件夹中的 **styles.css**),它应该重新编译。
  • **addPassthroughCopy** 告诉 Eleventy,在文件编译完成后,将目录内容复制到 **public** 目录。

您可以在 文档 中了解更多关于如何传递文件复制的信息。

由于我使用的是 Nunjucks 模板系统,所以我添加了 **markdownTemplateEngine** 属性并将其设置为 **njk**,以确保它知道在其他任何操作之前先通过 Nunjucks。

module.exports = function(eleventyConfig) {
  eleventyConfig.addWatchTarget("./src/css/")
  eleventyConfig.addWatchTarget("./src/images/")
  eleventyConfig.addPassthroughCopy("./src/css/")
  eleventyConfig.addPassthroughCopy("./src/images/")

  return {
    dir: {
      input: "src",
      output: "public"
    },
    markdownTemplateEngine: "njk"
  }
}

然后我在 **css** 文件夹中创建了一个 **styles.css** 文件,并提供了一些内容来测试其是否正常工作。

* {
  color: teal;
}

由于我已经在 **.eleventy.js** 中配置了 **css** 和 **images** 文件夹,所以我能够使用 Eleventy 的 URL 过滤器来引用这些文件。

为了访问这些自托管文件,我在 css 和图像标签的 **href** 和 **src** 属性中分别使用了 Eleventy 的 URL 过滤器。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{ title }}</title>

  <link rel="stylesheet" href="{{ '/css/styles.css' | url }}">

</head>
<body>
  <h1>{{ title }}</h1>

  <img src="{{ '/images/test_image.jpg' | url }}">

  {{ content | safe }}
</body>
</html>

现在我准备提供我的 Eleventy 项目了。

在开发中提供 Eleventy

由于我已经在 **package.json** 中定义了自定义开发脚本,所以我能够运行以下命令

npm start

这会编译 **src** 目录中的 **index.md**,并在 **public** 文件夹中生成一个 HTML 文件。此外,它还通过 Browsersync 启动了一个热重载服务器,我可以在 **http://localhost:8080/** 上看到结果。

到目前为止的结果

随着 Eleventy 在开发中运行,我可以开始构建 bragdoc 的其余部分。

构建 bragdoc 系统

在具有与下面所示类似的文件夹结构的 Eleventy 项目的基础上,我开始构建我的 bragdoc。

eleventy-bragdoc
├── src
│   ├── css
│   │   └── styles.css
│   ├── images
│   │   └── test_image.jpg
│   ├── _includes
│   │   └── layouts
│   │       └── base.njk
│   └── index.md
├── .eleventy.js
├── node_modules
├── package.json
├── package-lock.json
├── README.md
└── .gitignore

为 bragdoc 条目创建集合

Eleventy 能够创建将类似内容分组在一起的集合。因此,我创建了一个名为 **posts** 的文件夹,用于存放我的 bragdoc 条目。在该文件夹中,我创建了多个 Markdown 文件来代表每个条目。

文件名 **post-1.md**、**post-2.md**、**post-3.md** 不会影响网页上渲染的任何内容。

eleventy-bragdoc
├── src
│   ├── posts
│   │   ├── post-1.md  // new file
│   │   ├── post-2.md  // new file
│   │   └── post-3.md  // new file
│   ├── css
│   │   └── styles.css
│   ├── images
│   │   └── test_image.jpg
│   ├── _includes
│   │   └── layouts
│   │       └── base.njk
│   └── index.md
├── .eleventy.js
├── node_modules
├── package.json
├── package-lock.json
├── README.md
└── .gitignore

我认为有用的自定义属性包括:

  • 标题
  • 日期(默认情况下,帖子按 时间顺序排序
  • 类别(一组值,用于组织条目)
  • 公开/私有(布尔值 - true 或 false - 用于确定是否要将其显示在 bragdoc 上)
  • 图标(一个 Notion 风格的设计元素,用于直观地组织条目)

我决定将每个条目的描述作为 Markdown 文件的正文内容,因为这样可以让我自由地添加段落、图像、代码块等。此外,我不仅限于 Markdown 元素,因为我也可以包含 HTML 并使用 CSS 对其进行样式设置。

以下是一个 Markdown 文件中 bragdoc 条目的示例

---
title: Build my own Bragdoc using Eleventy
date: 2021-09-19
categories:
  - Learning
  - Eleventy
public: True
icon: 🎈
---

I learned how to use Eleventy to build my own bragdoc!

一些注意事项

  • 默认情况下,Markdown 中编写的链接不会在新空白窗口中打开。因此,在进行了一些研究之后,我偶然发现了 Mark Thomas Miller 编写的片段,我将其添加到 **<body>** 标签的结束标记之前,位于 **base.njk** 中。这可能不是你的菜(这绝对 不是 Chris 的菜),但以防万一你需要它
<script>
// Making all external links open in new tabs
// Snippet by Mark Thomas Miller

(function () {
  const links = document.querySelectorAll("a[href^='https://'], a[href^='http://']")
  const host = window.location.hostname

  const isInternalLink = link => new URL(link).hostname === host

  links.forEach(link => {
    if (isInternalLink(link)) return

    link.setAttribute("target", "_blank")
    link.setAttribute("rel", "noopener")
  })
})()
</script>
  • **date** 前端 matter 属性必须以 **YYYY-MM-DD** 格式编写。
  • 你可以根据需要分配任意数量的自定义前端 matter 属性。只要确保,如果你计划在模板中访问该属性,则该属性必须存在于所有使用相同模板的 Markdown 文件中;否则可能会导致构建失败。
  • 前端 matter 中的列表可以用 多种方式 编写(例如,数组或单行)。

将前端 matter 属性分配给集合

与其在每个 Markdown 文件中重复分配具有相同值的 Frontend Matter 属性,不如创建一个数据目录 JSON 文件,以便仅在整个集合中分配一次相同的键值对。

要创建数据目录文件,其名称必须与集合相同,即 **posts.json**。此外,该文件还必须放在集合文件夹中,即 **posts** 文件夹中。

eleventy-bragdoc
├── src
│   ├── posts
│   │   ├── posts.json  // new file
│   │   ├── post-1.md
│   │   ├── post-2.md
│   │   └── post-3.md
│   ├── css
│   │   └── styles.css
│   ├── images
│   │   └── test_image.jpg
│   ├── _includes
│   │   └── layouts
│   │       └── base.njk
│   └── index.md
├── .eleventy.js
├── node_modules
├── package.json
├── package-lock.json
├── README.md
└── .gitignore

此时,bragdoc 的帖子尚未定义为集合。为此,我在 **posts.json** 中添加了 **tags** 属性。在这里,我将值 “posts” 分配给该属性,以便我能够通过调用 **collections.posts** 来访问集合。

由于我不需要每个帖子都有自己的页面,例如 **http://localhost:8080/posts/post-1/**,所以我关闭了它的自动生成的 永久链接

{
  "tags": "posts",
  "permalink": false
}

列出 bragdoc 条目

简而言之,bragdoc 是由 **posts** 集合中的条目组成的页面。为了访问 Markdown 文件的前端 matter 属性和正文内容,这些条目通过 Nunjucks 循环访问。

为此,我回到了 **index.md**,并将文件类型从 Markdown 更改为 Nunjucks,即 **index.njk**。

eleventy-bragdoc
├── src
│   ├── posts
│   │   ├── posts.json
│   │   ├── post-1.md
│   │   ├── post-2.md
│   │   └── post-3.md
│   ├── css
│   │   └── styles.css
│   ├── images
│   │   └── test_image.jpg
│   ├── _includes
│   │   └── layouts
│   │       └── base.njk
│   └── index.njk  // changed filetype
├── .eleventy.js
├── node_modules
├── package.json
├── package-lock.json
├── README.md
└── .gitignore

接下来,我用 Nunjucks **for** 循环替换了 **index.njk** 的内容。

Nunjucks 函数(**for** 循环、**if** 语句等)必须包含开始和结束标签。

由于帖子的默认顺序是时间顺序(最旧的帖子排在最前面),所以我添加了 **reverse** 过滤器,以便将最新的帖子显示在顶部。

为了访问前端 matter 并将其渲染为 HTML(例如帖子的 **date** 和 **title**),我必须通过另一个 “数据” 层。访问前端 matter 中的属性需要使用双花括号。

---
title: "11ty x Bragdocs"
layout: "layouts/base.njk"
---

{% for post in collections.posts | reverse %}
  <p>
    {{ post.data.date }} - {{ post.data.title }}
  </p>
{% endfor %}
又向前迈进了一步

过滤 bragdoc 条目

为了过滤某些条目,我使用了前端 matter 数据来检查 **public** 属性是否设置为 **True**。如果该属性设置为 **False**,则该条目不会出现在 bragdoc 中。

类似地,当通过 Nunjucks 函数访问前端 matter 属性(如 **public**)时,我再次需要通过另一个 “数据” 层。

---
title: "11ty x Bragdocs"
layout: "layouts/base.njk"
---

{% for post in collections.posts | reverse %}
  {% if post.data.public %}
    <p>
      {{ post.data.date }} - {{ post.data.title }}
    </p>
  {% endif %}
{% endfor %}
帖子按标题排序。

添加自定义数据过滤器

默认情况下,**date** 属性会渲染一些我们通常不熟悉的内容。因此,在进行了一些研究之后,我找到了由 Phil Hawksworth 编写的 自定义过滤器。为了使用该过滤器,我创建了一个名为 **dates.js** 的文件,并将它放在一个名为 **_filters** 的新文件夹中。

eleventy-bragdoc
├── src
│   ├── _filters  // new folder
│   │   └── dates.js  // new file
│   ├── posts
│   │   ├── posts.json
│   │   ├── post-1.md
│   │   ├── post-2.md
│   │   └── post-3.md
│   ├── css
│   │   └── styles.css
│   ├── images
│   │   └── test_image.jpg
│   ├── _includes
│   │   └── layouts
│   │       └── base.njk
│   └── index.njk
├── .eleventy.js
├── node_modules
├── package.json
├── package-lock.json
├── README.md
└── .gitignore

然后,在 **dates.js** 中,我添加了以下内容

/*
A date formatter filter for Nunjucks 
Written by Phil Hawksworth
*/
module.exports = function(date, part) {
  var d = new Date(date);
  if(part == 'year') {
    return d.getUTCFullYear();
  }
  var month = [
    "January",
    "February",
    "March",
    "April",
    "May",
    "June",
    "July",
    "August",
    "September",
    "October",
    "November",
    "December"
  ];
  var ordinal = {
    1 : "st",
    2 : "nd",
    3 : "rd",
    21 : "st",
    22 : "nd",
    23 : "rd",
    31 : "st"
  };
  return month[d.getMonth()] + " " + d.getDate() + (ordinal[d.getDate()] || "th") + " " +d.getUTCFullYear();
}

为了在项目中访问日期过滤器,我在 **.eleventy.js** 中添加了一个新的过滤器,我可以使用自定义名称 **dateDisplay** 来调用它。

module.exports = function (eleventyConfig) {

  // Add filter
  eleventyConfig.addFilter("dateDisplay", require("./src/_filters/dates.js") );
  
  eleventyConfig.addPassthroughCopy("./src/css/")
  eleventyConfig.addPassthroughCopy("./src/images/")
  eleventyConfig.addWatchTarget("./src/css/")
  eleventyConfig.addWatchTarget("./src/images/")

  return {
    dir: {
      input: "src",
      output: "public"
    },
    markdownTemplateEngine: "njk"
  }
}

在 **index.njk** 中,我将 **dateDisplay** 过滤器分配给了 **date** 变量,并以人类可读的格式将其渲染出来。

---
title: "11ty x Bragdocs"
layout: "layouts/base.njk"
---

{% for post in collections.posts | reverse %}
  {% if post.data.public %}
    <p>
      {{ post.data.date | dateDisplay }} - {{ post.data.title }}
    </p>
  {% endif %}
{% endfor %}

每次更改配置文件时,都需要重新启动服务器。

带有更新日期格式的帖子。

要返回帖子的正文内容,我调用了templateContent 并添加了safe 过滤器,以便它呈现 Markdown 文件中的任何 HTML,而不是对其进行转义。

---
title: "11ty x Bragdocs"
layout: "layouts/base.njk"
---

{% for post in collections.posts | reverse %}
  {% if post.data.public %}
    <p>
      {{ post.data.date | dateDisplay }} - {{ post.data.title }} 
      <br/>
      {{ post.templateContent | safe }}
    </p>
    <br/>
  {% endif %}
{% endfor %}
带有正文内容的帖子。

最后,我包含了另一个for 循环来列出categories 前置 matter 属性中的值。

---
title: "11ty x Bragdocs"
layout: "layouts/base.njk"
---

{% for post in collections.posts | reverse %}
  {% if post.data.public %}
    <p>
      {{ post.data.date | dateDisplay }} - {{ post.data.title }}
      <br/>
      {{ post.templateContent | safe }}
      {% for category in post.data.categories %}
        <span># {{category}}</span>
      {% endfor %}
    </p>
    <br/>
  {% endif %}
{% endfor %}

完成从帖子集合中提取数据后,该构建 HTML 结构了。

构建 bragdoc

Eleventy 中的 Partials 允许我们重复使用 HTML 或模板片段。这也简化了代码,从一个庞大的模板文件变成了可以组合在一起的可管理的片段。

<body> 标签的base.njk 中,我删除了除内容和片段之外的所有内容。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{ title }}</title>

  <link rel="stylesheet" href="{{ '/css/styles.css' | url }}">
</head>
<body>
  {{ content | safe }}
  <script>
    (function () {
      const links = document.querySelectorAll("a[href^='https://'], a[href^='http://']")
      const host = window.location.hostname

      const isInternalLink = link => new URL(link).hostname === host

      links.forEach(link => {
        if (isInternalLink(link)) return

        link.setAttribute("target", "_blank")
        link.setAttribute("rel", "noopener")
      })
    })()
  </script>
</body>
</html>

接下来,我创建了bragdoc-entry.njk,它位于名为partials 的新文件夹中。

eleventy-bragdoc
├── src
│   ├── _filters
│   │   └── dates.js
│   ├── posts
│   │   ├── posts.json
│   │   ├── post-1.md
│   │   ├── post-2.md
│   │   └── post-3.md
│   ├── css
│   │   └── styles.css
│   ├── images
│   │   └── test_image.jpg
│   ├── _includes
│   │   ├── partials  // new folder
│   │   │   └── bragdoc-entry.njk  // new file
│   │   └── layouts
│   │       └── base.njk
│   └── index.njk
├── .eleventy.js
├── node_modules
├── package.json
├── package-lock.json
├── README.md
└── .gitignore

bragdoc-entry.njk 中,我将构成 bragdoc 条目的内容从index.njk 中迁移过来。请注意,它不需要任何前置 matter,因为它被视为片段。

Partials 不会扩展模板,因此它们不需要任何前置 matter。

<p>
  {{ post.data.date | dateDisplay }} - {{ post.data.title }}
  <br/>
  {{ post.templateContent | safe }}
  {% for category in post.data.categories %}
      <span># {{category}}</span>
  {% endfor %}
</p>
<br/>

然后,在index.njk 中的if 语句之间,我添加了一个include 标签,它引用了bragdoc-entry.njk 部分。通过这样做,bragdoc-entry.njk 中的内容将被重复添加,直到for 循环结束。

---
title: "11ty x Bragdocs"
layout: "layouts/base.njk"
---

{% for post in collections.posts | reverse %}
  {% if post.data.public %}
    {% include 'partials/bragdoc-entry.njk' %}
  {% endif %}
{% endfor %}

接下来,我用一些自定义 HTML 将整个for 循环包围起来,包括标题、配置文件容器和页脚。在这一点上,我还将个人资料图片包含在images 文件夹中,并在自定义 HTML 中使用 Eleventy 的 URL 过滤器引用它。

---
title: "11ty x Bragdocs"
layout: "layouts/base.njk"
---

<div class="bragdoc__section" id="bragdoc__section">
<h1 class="bragdoc__header">{{ title }}</h1>
<div class="bragdoc__container">
  <div class="bragdoc__profile">
    <img class="bragdoc__photo" src="{{ '/images/profile_picture.jpg' | url }}">
    <h1 class="bragdoc__name">Emily Y Leung</h1>
    <div class="role">Computational Designer</div>
  </div>
  {% for post in collections.posts | reverse %}
    {% if post.data.public -%}
      {% include 'partials/bragdoc-entry.njk' %}
    {% endif %}
  {% endfor %}
  </div>
  <footer>
    <div><a target="_blank" href="https://www.bragdocs.com/">Bragdocs</a> inspired theme built with <a target="_blank" href="https://www.11ty.dev/">11ty</a></div>
    <div>Made with ♥ by <a target="_blank" href="https://emilyyleung.github.io/">Emily Y Leung</a></div>
  </footer>
</div>

然后,在bragdoc-entry.njk 中,我更新了 HTML 结构,并包含了用于样式的类。

<div class="bragdoc__entry">
  <div class="bragdoc__entry-milestone"></div>
  <div class="bragdoc__entry-block">
    <span class="bragdoc__entry-date">
      {{ post.data.date | dateDisplay }}
    </span>
    <br/>
    <h2 class="bragdoc__entry-title"><span class="bragdoc__icon">{{ post.data.icon }}</span> {{ post.data.title }}</h2>
    <div class="bragdoc__entry-content">
        {{ post.templateContent | safe }}
    </div>
  </div>
  <div class="bragdoc__taglist">
  {% for category in post.data.categories %}
    <span># {{category}}</span>
  {% endfor %}
  </div>
</div>

访问全局数据

了解全局数据的一个好方法是想象构建一个 HTML 模板,有人可以将其用作其网站的基础。他们不必搜索要替换文本的特定 HTML 标签,而只需要替换外部文件中的某些值,然后该值就会更新内容。这是全局数据文件可以为我们做的很多事情之一

当全局数据文件以 JSON 格式写入并放置在名为_data 的文件夹中时,Eleventy 可以访问它们。因此,我创建了一个data.json 文件,当调用{{data}} 时可以访问该文件,然后选出我在 JSON 对象中提供的任何属性。

eleventy-bragdoc
├── src
│   ├── _data  // new folder
│   │   └── data.json  // new file
│   ├── _filters
│   │   └── dates.js
│   ├── posts
│   │   ├── posts.json
│   │   ├── post-1.md
│   │   ├── post-2.md
│   │   └── post-3.md
│   ├── css
│   │   └── styles.css
│   ├── images
│   │   ├── profile_picture.jpg
│   │   └── test_image.jpg
│   ├── _includes
│   │   ├── partials
│   │   │   └── bragdoc-entry.njk
│   │   └── layouts
│   │       └── base.njk
│   └── index.njk
├── .eleventy.js
├── node_modules
├── package.json
├── package-lock.json
├── README.md
└── .gitignore

data.json 中,我包含了在整个项目中重复使用的属性。

{
  "mywebsite": "https://emilyyleung.github.io/",
  "myname": "Emily Y Leung",
  "myrole": "Computational Designer"
}

一个很好的用例是替换index.njk 中个人资料和页脚中的内容。

<!-- Profile -->
<div class="bragdoc__profile">
  <img class="bragdoc__photo" src="{{ '/images/profile_picture.jpg' | url }}">
  <h1 class="bragdoc__name">{{ data.myname }}</h1>
  <div class="role">{{ data.myrole }}</div>
</div>
<!-- Footer -->
<footer>
  <div><a target="_blank" href="https://www.bragdocs.com/">Bragdocs</a> inspired theme built with <a target="_blank" href="https://www.11ty.dev/">11ty</a></div>
  <div>Made with ♥ by <a target="_blank" href="{{ data.mywebsite }}">{{ data.myname }}</a></div>
</footer>

为 bragdoc 设置样式

完成 bragdoc 结构后,我在styles.css 中更新了样式。

为了模仿bragdocs.com,我选择了一些他们的颜色并将其存储在根变量中。

此外,我还想创建多个主题,因此我在:root 变量之上添加了一个自定义data-theme 属性。在这种情况下,默认颜色主题为“light”,无论是否将data-theme 分配给<html> 标签。但这同时也意味着,如果我想创建一个“dark”主题,我可以在 CSS 中创建一个新的选择器html[data-theme="dark"],并将备用颜色分配给与:root 中指定的相同变量。

:root, html[data-theme="light"] {
  --logo: black;
  --name: black;
  --entry-title: black;
  --date: #BDBDBD;
  --text: #676a6c;
  --entry-line: #f1f1f1;
  --entry-circle: #ddd;
  --background: white;
  --text-code: grey;
  --code-block: rgba(0,0,0,0.05);
  --link-text: #676a6c;
  --link-hover: orange;
  --quote-block-edge: rgba(255, 165, 0, 0.5);
  --quote-block-text: #676a6c;
  --table-border: #676a6c;
  --footer: #BDBDBD;
  --tag: #BDBDBD;
}

要引用根变量,请调用var(),其中参数是属性的名称。

以下是如何使用根变量为<p> 标签中的文本颜色设置样式的示例。

:root {
  --text: teal;
}

p {
  color: var(--text)
}

为了好玩,我添加了一个受Google Material 启发的深色版本。

html[data-theme="dark"] {
  --logo: #FFF;
  --name: #FFF;
  --entry-title: #dedede;
  --date: rgba(255,255,255,0.3);
  --text: #999999;
  --entry-line: rgba(255,255,255,0.2);
  --entry-circle: rgba(255,255,255,0.3);
  --background: #121212;
  --code-text: rgba(255,255,255,0.5);
  --code-block: rgba(255,255,255,0.1);
  --link-text: rgba(255,255,255,0.5);
  --link-hover: orange;
  --quote-block-edge: rgb(255, 165, 0);
  --quote-block-text: rgba(255, 165, 0,0.5);
  --table-border: #999999;
  --footer: rgba(255,255,255,0.3);
  --tag: rgba(255,255,255,0.3);
}

要控制要使用哪个主题,请将data-theme 属性添加到base.njk 中的<html> 标签。从那里,分配与相应 CSS 选择器相关联的值,即“light”或“dark”。

<!DOCTYPE html>
<html lang="en" data-theme="light">

接下来,我为<body><footer>、bragdoc 部分和徽标添加了样式。

body {
  font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: 13px;
  color: var(--text);
  background-color: var(--background);
  margin: 0;
  height: 100vh;
}

footer {
  margin: 0 auto;
  max-width: 500px;
  padding-bottom: 1.5em;
  text-align: center;
  color: var(--footer);
  padding-top: 2em;
  margin-top: 2em;
}

/* Bragdoc Logo */

.bragdoc__header {
  margin: 0;
  padding: 1em;
  font-size: 1.5em;
  color: var(--logo)
}

/* Bragdoc Body */

.bragdoc__section {
  height: 100%;
  display: grid;
  grid-template-rows: auto 1fr auto;
  margin: 0;
  padding: 0;
}

此时,HTML 中的自定义标签和类使复制 bragdoc 布局变得很简单。

/* Bragdoc User Profile */

.bragdoc__profile {
  padding-top: 3em;
  padding-bottom: 2em;
}

.bragdoc__photo {
  width: 8em;
  border-radius: 100%;
  padding: 0;
  height: 8em;
  object-fit: cover;
}

.bragdoc__name {
  color: var(--name);
  margin-bottom: 0.25em;
}

.bragdoc__icon {
  font-family: "Segoe UI Emoji", Times, serif;
}

.bragdoc__container {
  max-width: 800px;
  margin: 0 0 0 30em;
  height: 100%;
}

.bragdoc__profile-role {
  margin: 0;
}

接下来,我为条目设置了样式以复制bragdocs.com 的时间线设计。

/* Individual Bragdoc Entry Blocks */

.bragdoc__entry {
  position: relative;
}

.bragdoc__entry:first-child {
  margin-top: 0;
}

.bragdoc__entry:before {
  height: 100%;
  position: absolute;
  background-color: var(--entry-line);
  width: 2px;
  content: "";
  top: 30px;
}

.bragdoc__entry:last-child:before {
  background-color: var(--background);
}

.bragdoc__taglist {
  margin-left: 1em;
  padding: 1em;
}

.bragdoc__taglist > * {
  border: 1px solid var(--tag);
  padding: 0.25em 0.5em 0.25em 0.5em;
  border-radius: 0.5em;
  margin-right: 1em;
}

/* Entry Content */

.bragdoc__entry-block {
  margin-left: 1em;
  padding: 1em;
}

.bragdoc__entry-title {
  margin-top: 4px;
  color: var(--entry-title);
  font-size: 1.5em;
}

.bragdoc__entry-date {
  line-height: 3em;
  color: var(--date);
}

/* Bragdoc milestone circle */

.bragdoc__entry-milestone {
  position: absolute;
  height: 5px;
  width: 5px;
  border: 2px solid var(--entry-circle);
  background-color: var(--background);
  left: 0;
  top: 30px;
  margin-top: -2px;
  margin-left: -3px;
  border-radius: 100px;
}

/* Bragdoc Entry Content */

.bragdoc__entry-content > * {
  margin-bottom: 0.5em;
  margin-left: 0;
}

.bragdoc__entry-content > h1 {
  font-size: 1.15em;
}

.bragdoc__entry-content > h2, h3, h4, h5, h6 {
  font-size: 1em;
  color: var(--text);
}

使用 CSS 媒体查询,我还可以控制文本大小以及 HTML 元素的位置。这使其在移动设备上查看时也能正常工作。

/* Make it responsive */

@media only screen and (max-width: 1400px) {

  .bragdoc__container {
    /* Center the bragdoc*/
    margin: 0 auto;
  }

  .bragdoc__entry-title {
    font-size: 1.25em;
  }
}

@media only screen and (max-width: 870px) {

  .bragdoc__container {
    padding-left: 2em;
    padding-right: 2em;
  }

  .bragdoc__entry-title {
    font-size: 1.15em;
  }
}

设计的最后修饰需要考虑每个条目中的描述(即 Markdown 正文内容),您可以在此 Gist 中找到

鉴于 CSS 是通过参考根变量构建的,我们可以继续创建更多主题。尝试探索Color HuntCooolers 中的颜色调色板。

将 bragdoc 部署到 GitHub Pages

从头开始构建一个项目很棒,但与世界分享它更棒!

虽然有无数种方法来托管 bragdoc,但我决定将其托管在 GitHub Pages 上。这意味着我可以使用我的 GitHub 帐户的基本 URL 并将/eleventy-bragdoc/ 添加到它的末尾。

此时,我一直从eleventy-bragdoc 存储库中工作,并且已经创建了一个gh-pages 分支。

按照本教程 了解如何为您的存储库设置 GitHub Pages。

配置 URL 路径

要配置用于部署的 URL 路径,我在.eleventy.js 中包含了一个pathPrefix 来定义相对于基本 URL 的路由。

如果不指定pathPrefix,默认值为/,它链接到基本 URL,即https://emilyyleung.github.io/

由于我在基本 URL 上已经有了内容,我想将其托管在子页面上,即https://emilyyleung.github.io/eleventy-bragdoc/

要为子页面设置pathPrefix,它必须以斜杠开头和结尾。

module.exports = function (eleventyConfig) {
  // ...
  return {
    dir: {
      input: "src",
      output: "public"
    },
    markdownTemplateEngine: "njk",
    pathPrefix: "/eleventy-bragdoc/"
  }
}

添加 GitHub Pages 依赖项

配置后,我使用终端安装了 GitHub Pages。

npm install gh-pages --save-dev

这会自动将依赖项添加到package.json 中。

{
  // ...  
  "devDependencies": {
    "gh-pages": "^3.2.3"
  },
  // ...
}

添加自定义终端脚本

要部署public 文件夹,我添加了一个deploy 脚本并引用了public 文件夹。

{
  // ...
  "scripts": {
    "start": "eleventy --serve",
    "build": "eleventy",
    "deploy": "gh-pages -d public"
  }
  // ...
}

运行构建

就像在开发中一样,我将我的终端导航到eleventy-bragdoc 文件夹。但这次,我运行了以下命令将文件重新构建到public 文件夹中。

npm run-script build

然后,要部署到 GitHub Pages,我运行了以下命令。

npm run deploy

授予部署权限

此时,终端可能会要求您通过终端或 GitHub Desktop 应用程序登录。如果登录失败,终端可能会要求您生成一个身份验证令牌,以代替密码使用。 这里有一份关于如何创建令牌的指南。

终端成功响应后,我就可以看到我的 bragdoc 实时运行了!

维护您的 bragdoc

与报告和书籍不同,bragdoc 必须作为您的进度和成就的实时记录不断维护。将您的 bragdoc 想象成一个花园,需要定期关注和照顾。虽然您可能不会立即看到好处,但投入在维护文档上的时间将带来更大的回报。养成这个习惯的一些好处是即时回忆和分享您所做工作的的能力。

虽然您可能无法在事情发生时记下所有内容,但 Julia Evans 建议 设置一段固定的时间来回顾您的进度并更新文档。甚至可以将其变成一个双周期的集体活动,来庆祝所有大小胜利。

对许多人来说,完成某件事所需的时间越短越好。使用此 bragdoc 设置,添加新条目和重建网站根本不需要很长时间!为了让您了解其简单性,我将引导您完成向教程添加另一个条目的过程。

添加新的 bragdoc 条目

从上次部署开始,我将首先在我的 posts 文件夹中添加一个新的 Markdown 文件。

eleventy-bragdoc
├── src
│   ├── _data
│   │   └── data.json
│   ├── _filters
│   │   └── dates.js
│   ├── posts
│   │   ├── posts.json
│   │   ├── post-1.md
│   │   ├── post-2.md
│   │   ├── post-3.md
│   │   └── post-4.md  // new entry goes here
│   ├── css
│   │   └── styles.css
│   ├── images
│   │   ├── profile_picture.jpg
│   │   └── test_image.jpg
│   ├── _includes
│   │   ├── partials
│   │   │   └── bragdoc-entry.njk
│   │   └── layouts
│   │       └── base.njk
│   └── index.njk
├── .eleventy.js
├── node_modules
├── package.json
├── package-lock.json
├── README.md
└── .gitignore

post-4.md 中,我将添加我的前置内容和描述内容。

---
title: Working towards publishing my first article on CSS-Tricks
date: 2021-10-02
categories:
  - Writing
  - Eleventy
public: True
icon: ✍🏻
---

Since re-creating [bragdocs.com](https://www.bragdocs.com/) using Eleventy, I am now in the process of writing the steps on how I did it.

运行构建

添加并保存条目后,我就可以告诉 Eleventy 从 src 引用我的 Markdown 文件,以便在 public 文件夹中生成静态 HTML 文件。因此,我将终端导航到 eleventy-bragdoc ,并在其中运行以下命令

npm run-script build

运行部署

由于我已经部署过一次,因此我的 GitHub 凭据应该在运行以下命令时授予我立即部署访问权限

npm run deploy

然后,这些更改将反映在我的网站上,与之前配置的 URL 相同。

接下来是什么?

首先,恭喜您从头开始创建自己的 bragdoc!它属于您,可以保留、维护和分享。

虽然本教程只触及了 Eleventy 功能的皮毛,但一小步可以引导您走向各种方向。为了激发您的好奇心,请查看 其他人如何使用 Eleventy

欢迎 联系我,我很想看看您的成果!