Add AI documentation skills

This commit is contained in:
2026-04-25 09:08:55 -05:00
parent 100a4d5419
commit 9d15ab56c6
255 changed files with 68574 additions and 0 deletions

View File

@@ -0,0 +1,373 @@
# Basic blogs
Blogs are a great way to engage with your audience. Software developers can use
a blog to announce new features, demonstrate their usage and provide background
information. You can demonstrate competence by commenting on the state of the
art or document your own work as best practice. Posts on current topics can help
draw in visitors for your main website and can keep your audience engaged. Of
course, you can blog about any topics close to your heart.
The [blog plugin] makes running a blog alongside your other content easy but you
can also configure it to run a stand-alone blog if posts are the only kind
of content you need.
After a brief overview of the basic concepts of a blog, this tutorial guides you
through the process of configuring the [blog plugin], setting up your blog,
creating posts, and defining post metadata.
[blog plugin]: ../../plugins/blog.md
__Time required:__ typically 20 minutes
## Key concepts
**Post, excerpt**: a blog consists of a number of self-contained _posts_ (often called
articles) and an index page that shows the posts in reverse chronological order, with
the most recent post at the top. The index page usually shows only a short _excerpt_ and a
link that the user can click to navigate to the full post.
**Metadata**: both the index page and the post itself list information such as
when you published the post, when you updated it, who the author is, and what the
expected reading time is.
**Slug**: since the blog posts are primarily arranged by time and not into a hierarchy,
their URLs do not reflect such a structure. Instead, each post's URL
contains a shortened description, the _slug_, which is usually derived from
the first heading in the post.
**Navigation**: the main navigation structure is the timeline, which you can
subdivide into _categories_. The main index page shows the more recent posts
while an _archive_ section allows access to older ones, organized by year.
In addition, posts can be _tagged_ and _tag index pages_ provide an additional
navigation structure based on content.
You can see all these elements on the [Material for MkDocs blog].
[Material for MkDocs blog]: https://squidfunk.github.io/mkdocs-material/blog/
## Setting up your blog
The blog plugin is part of Material for MkDocs but you need to configure it
in the `mkdocs.yml`.
!!! example "Set up a blog"
If you have not done so already, create a project for your blog,
then edit the `mkdocs.yml` file to make sure it has the following content:
```yaml
site_name: Blog Tutorial
site_description: an example blog set up following the tutorial
site_url: http://www.example.com
theme:
name: material
plugins:
- search
- blog
```
The blog plugin will create a directory structure for your blog posts if it
does not exist, so simply run `mkdocs serve` to get:
```
docs
├── blog
│   ├── index.md
│   └── posts
└── index.md
```
Now create your first blog post in `docs/blog/posts`. You can use any
naming convention and directory structure you like for your posts, as long as
they are inside `docs/blog/posts`.
Each post _must_ have a page header, which appears at the top of the Markdown
code between lines with three dashes. Within this header, you need to have at
least a `date` entry but you can add other data, as you will see below.
Following the header comes the page content. Note that it is important
to have a level one heading as the plugin uses it to produce the _slug_. Also,
by adding `<!-- more -->` to the page, you can define where the excerpt will end
that the index page shows.
!!! example "Write your first post"
Create a file `docs/blog/posts/myfirst.md` with the following contents:
```
---
date:
created: 2023-12-31
---
# Happy new years eve!
We hope you are all having fun and wish you all the best for the new year!
<!-- more -->
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
```
Then, run `mkdocs serve` and point your web browser at
`http://localhost:8000/blog`.
The blog plugin automatically creates navigation elements for
the blog. The index page shows only the extract. When you select the
"Continue reading" link, you will get to the full blog post. Note how it
has a URL generated from the first-level heading.
!!! tip "Navigation"
We also have a [tutorial on navigation] that shows you how to change the
automatically created navigation and integrate the blog into your existing
navigation structure. It shows how to create secondary navigation, produce
author pages, and control pagination.
[tutorial on navigation]: navigation.md
## Post metadata
In addition to the date, you can provide other metadata and give the plugin
instructions, such as to treat a post as a draft or to pin it.
### Drafts
You may want to produce a draft of a blog post and work with it locally but
exclude it from the build that you publish. Simply add a field to the page
header to indicate that a post is still in draft form.
!!! example "Create a draft"
Create a second blog post in `docs/blogs/posts/draft.md` with the following
contents:
```hl_lines="4"
---
date:
created: 2024-01-01
draft: true
---
# Happy new year!
Happy 2024 to everyone. Wishing you all the best!
<!-- more -->
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
```
Now, note how the draft appears on the index page but with a label that
indicates that it is a draft. When you run `mkdocs build`, the draft will
_not_ appear in the output:
```
$ mkdocs build
$ ls site/blog
site/blog
├── 2023
│   └── 12
│   └── 31
│   └── happy-new-years-eve
│   └── index.html
...
```
The first blog post for 2024 is not there yet because it is still in draft
stage. Remember to remove the `draft` setting in the header when it is time
to publish it.
You can also create a folder to keep your drafts in and use the [Meta plugin]
to add the `draft` header setting to all the posts in that folder. This has the
advantage that it is easier to see which posts are still in draft form. We will
cover the Meta plugin later on.
[Meta plugin]: ../../plugins/meta.md
### Edits
Sometimes, bloggers need to update a post. This might happen when you make
a mistake or when something changes that you need to reflect in the post. To
indicate you have edited a post, you can include an `updated` date in the page
header.
!!! example "Editing a post"
Make a change to your first blog post, then add an edit date to the header:
```hl_lines="3 4"
---
date:
created: 2023-12-31
updated: 2024-01-02
---
```
The Metadata section of the blog post itself will contain the edit date,
though the index page omits this detail by default.
### Reading time
To give the reader some idea of how long it might take them to read a post,
a read time is automatically calculated. If you want to override this, you can
do so in the page header by specifying the number of minutes you estimate
your readers will take the read the post.
!!! example "Overriding the reading time"
Add a reading time override to your first blog post:
```hl_lines="5"
---
date:
created: 2023-12-31
updated: 2024-01-02
readtime: 15
---
```
### Pinning
Sometimes, blog authors want to 'pin' a specific post so that it will always
appear at the top of the index page, no matter what else gets published. You can
achieve this by adding the `pin` attribute in the page header:
!!! example "Pin a post"
Add the `pin` attribute to your first blog post:
```hl_lines="6"
---
date:
created: 2023-12-31
updated: 2024-01-02
readtime: 15
pin: true
---
```
Observe how this makes the post appear on top of the index page even though
its publication date is prior to other posts. A small pin icon shows that the
post has been pinned.
### Related links
When your blog is part of a wider site such as technical documentation, you
will want to provide links from blog posts into your other content. One way you
can do this is to have a related links section. The blog plugin can create one
for you if you provide link targets in your page header:
!!! example "Add a related links section"
Add the following to a blog post:
``` hl_lines="5-7"
---
date:
created: 2023-12-31
...
links:
- index.md
- blog/index.md
---
```
The related links appear underneath the Metadata section.
The nice thing here is that you do not need to provide a page title. The plugin
will deduce the link text by applying the same logic that MkDocs uses for the
main navigation. In fact, the syntax is the same as that of the `nav` section
in the `mkdocs.yml`, so you can override the title if you want and even define
subsections:
!!! example "Override the page titles"
Change the link section to override the page titles:
```hl_lines="6-9"
---
date:
created: 2023-12-31
...
links:
- Homepage: index.md
- Blog index: blog/index.md
- External links:
- Material documentation: https://squidfunk.github.io/mkdocs-material
---
```
The plugin renders related links in the left sidebar on screens that are wide
enough and at the bottom of the post on narrow screens. Change the size of your
browser window to see this in action.
## Meta plugin
The Meta plugin helps simplify the management of metadata that is common to a
group of files in the same subdirectory. Instead of having to repeat the same
metadata in the page headers of a number of files, you can add a `.meta.yml`
file in the directory and the Meta plugin will merge its contents into the
headers of all the pages contained. Settings from the page header take
precedence, so you can always override settings by adding them to a post's
header.
For example, you may want to manage drafts by keeping them in a directory
together so that they are not only flagged as drafts but also easier to find.
(Otherwise, you would need to inspect the page headers or trace back from the
output to the files to figure out which posts are drafts.)
!!! example "Drafts using the Meta plugin"
You first need to activate the plugin in your `mkdocs.yaml`:
```yaml hl_lines="4"
plugins:
- search
- blog
- meta
```
Now create the folder for the drafts:
=== "MacOS/Linux"
```bash
$ mkdir docs/blog/posts/drafts
```
=== "Windows"
```powershell
$ mkdir docs\blog\posts\drafts
```
Now, within this folder, crate a file `.meta.yml` that contains:
```yaml
draft: true
```
Add another blog post and store it in `docs/blog/posts/drafts`. When you
look at it locally, you will see the label that identifies it as a draft,
while in the version built for publication it does not appear. To move a
post from draft status to published, simply move it outside `drafts/`.
[meta]: ../../plugins/meta.md
## What's next?
You should now have a working blog. However, as it accumulates content, you
may want to make sure that people can find posts they are interested in, so
you may want to add secondary navigation with tags and categories. You may
have more than one author and want to attribute posts to them as well as
generate author pages for them. We have a [tutorial on navigation, pagination,
and authors] that covers these topics.
[tutorial on navigation, pagination, and authors]: navigation.md
You may want to increase engagement with your blog by allowing people to
subscribe to an RSS feed or by setting up a comment system. The [engagement
and dissemination tutorial] walks you through setting these up.
[engagement and dissemination tutorial]: engage.md

View File

@@ -0,0 +1,369 @@
# Engagement and dissemination
You can foster reader engagement and improve the dissemination of content
on your blog by providing an RSS feed that people can subscribe to and by
integrating a discussion system. To learn more about who is or is not reading
your posts, you may want to integrate an analytics system. You may also want
to post on social media when you publish a new blog post. This tutorial gives
you a leg up on all of these topics.
__Time required:__ typically 30 minutes
## RSS feeds
An _RSS feed_ allows users to subscribe to a blog so that they get notified when
you publish new posts. RSS Feed readers are often used to access blogs that a
user follows. They usually support downloading the blog content for offline
consumption.
An easy way to create an RSS feed for your blog is to use the
[MkDocs RSS Plugin], which is well integrated with Material for MkDocs.
Since it is a third-party plugin, you need to install it before using it.
[MkDocs RSS Plugin]: https://guts.github.io/mkdocs-rss-plugin
!!! example "Add an RSS feed"
Install the RSS plugin into your project:
```
$ pip install mkdocs-rss-plugin
```
It is important that have the `site_name`, `site_description` and
`site_url` settings configured as [instructed in the basic blog tutorial].
The RSS plugin makes use of this information to construct the feed, so make
sure you have configured them.
[instructed in the basic blog tutorial]: basic.md#setting-up-your-blog
Now, configure the plugin in the `mkdocs.yml`. The options provided restrict
the pages that RSS entries are created for to the blog posts, which is
probably what you want. Also note the configuration of the date fields to
match the format that Material for MkDocs uses to accommodate both a
creation date and a date for updates.
```yaml hl_lines="9"
plugins:
- ...
- rss:
match_path: "blog/posts/.*"
date_from_meta:
as_creation: date.created
as_update: date.updated
```
Have a look at http://localhost:8000/feed_rss_created.xml to see the RSS
feed in all its XML glory. You can use a browser like Firefox or Chrome that
can display the raw RSS feed or use `curl` to get the feed and `xmllint` to
format it. (You may need to install these tools.)
```
curl -s http://localhost:8000/feed_rss_created.xml | xmllint --format -
```
You may also want to try your feed with a feed reader. There are various desktop
and mobile apps as well as online services. Of course, to use the latter you
will need to deploy your project somewhere that is accessible to them.
This minimal configuration should work well if you have not made any changes
to the default configuration of the blog plugin. For more information on adapting
the feed to your needs, see [the RSS plugin's documentation].
[the RSS plugin's documentation]: https://guts.github.io/mkdocs-rss-plugin/
## Social media buttons
Social media buttons can serve two purposes: to allow your readers to navigate
to your social media profiles or to share content you have published via their
own accounts.
### Profile links
Links to social media profiles a usually provided in the footer of pages and
Material for MkDocs makes this easy. All you need to do is to provide the
necessary links and define the icons to use.
!!! example "Adding social media profile links"
Add an `extra` section to your `mkdocs.yml` and, within it, a `social`
section to contain a list of link definitions. These consist of the logo
to use and the link to the profile.
```yaml
extra:
social:
- icon: fontawesome/brands/mastodon
name: squidfunk on Mastodon
link: https://fosstodon.org/@squidfunk
```
For the `icon`, you can choose any valid path to an icon bundled with the
theme. The `name` will be used as the title attribute for the icon and
including this improves accessibility.
For popular social media systems, the link needs to be absolute and
needs to include the scheme, most likely `https://`.
You can also use other schemes. For example, to create an icon that allows
people to create an email, add this:
```yaml
extra:
social:
- icon: /fontawesome/regular/envelope
name: send me an email
link: mailto:<email-address>
```
Finally, you can specify a URL within your site, such as to your contact
page. It is possible to specify only the path to the page:
```yaml
extra:
social:
- icon: /material/mailbox
name: contact us
link: /contact
```
### Share and like buttons
Adding buttons that let people share your content on social media is a bit
more involved, which is why there are companies offering components for this.
!!! tip "Data Protection"
"Share" and "Like" buttons that use integrations provided by social media
companies often leave copious data traces even when the user does not
interact with these buttons. If you choose to integrate such feature on
your site please be aware of the data protection implications and your
duties as a provider to ensure that processing occurs only once the user
has granted consent.
This implementation of share buttons deliberately does not use third party code.
It supports sharing to Twitter/X and Facebook without causing a data flow to
these companies whenever someone views the pages. Only when someone clicks a
share button will there be interactions with those companies' servers.
!!! example "Add share buttons"
In order to add the share buttons, you can add a hook that appends buttons
for sharing the current page.
Create a directory `hooks` in your project root and configure it
in your `mkdocs.yml`:
```yaml
hooks:
- hooks/socialmedia.py
```
Add the file `hooks/socialmedia.py` with the following Python code:
```python
from textwrap import dedent
import urllib.parse
import re
x_intent = "https://x.com/intent/tweet"
fb_sharer = "https://www.facebook.com/sharer/sharer.php"
include = re.compile(r"blog/[1-9].*")
def on_page_markdown(markdown, **kwargs):
page = kwargs['page']
config = kwargs['config']
if not include.match(page.url):
return markdown
page_url = config.site_url+page.url
page_title = urllib.parse.quote(page.title+'\n')
return markdown + dedent(f"""
[Share on :simple-x:]({x_intent}?text={page_title}&url={page_url}){{ .md-button }}
[Share on :simple-facebook:]({fb_sharer}?u={page_url}){{ .md-button }}
""")
```
The hook first checks if the current page is a blog post and then appends
Markdown code for the share buttons. The buttons use icons, so you also need
to configure the following markdown extensions:
```yaml
markdown_extensions:
- attr_list
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
```
## Add a discussion system
Allowing your readers to comment on your posts is a great way of receiving
feedback, learning something, as well as giving readers the opportunity to
discuss the content and the topic it is about.
There are plenty of discussion system out there and you will need to consider
your audience when choosing one appropriate for your blog. Likewise, you will
also need to consider existing commitments to communication channels. If you
are a heavy user Slack, for example, you may have a string preference for this
system. Consider that when you add a communication channel, you will need to
be prepared to use it regularly and to moderate discussions.
### Giscus integration
In this tutorial, we will be using [Giscus] because it is free, open source,
and uses [GitHub Discussions] as a backend. Because a lot of users of Material
for MkDocs use GitHub, this seems like an obvious choice.
[Giscus]: https://giscus.app/
[GitHub Discussions]: https://docs.github.com/en/discussions
To add Giscus to your blog you will need to go through a number of steps:
1. Create a GitHub repository if there is not already one
2. Turn on discussions and install the [Giscus app]
3. Configure the code needed to embed Giscus into your blog
4. Add the code to your MkDocs project
[Giscus app]: https://github.com/apps/giscus
You may want to create a test repository for this tutorial that you can
scrap later on. The instructions below assume that you are user "example"
and that you create a repository "giscus-test." The repository will need
to be public for people to be able to use the discussions.
In the instructions given below, you will need to replace at least the username
but also the repository name if you chose another name such as when you
want to work directly on an existing repository.
!!! example "Turn on discussions and install the Giscus app"
Once the repository is set up, go to its settings page and find
`Features` in the `General` section. Tick the checkbox for `Discussions`.
You will see that `Discussions` appears in the top navigation for the
repository. If you are using a live repository then you may want to add some
minimal content to the discussions section at this point and come back to the
tutorial.
Next, you need to install the [Giscus app] by following the link in this
sentence, and choosing `Install`, then following the instructions to choose
where the Giscus app is to be installed:
1. Choose the account or organization for the repository you want to use.
2. Choose to install only on select repositories and select the one you
want to use. Note that you can choose more than one repository here.
3. Select `Install` at the end. You may need to authenticate to give
permission for this to happen.
4. You will end up on the `Applications` page in your settings, where you
can control the Gicsus app and uninstall it if so desired.
That is all the preparation you will need for the repository. Next, it is time
to generate a piece of code that embeds Giscus in your site. The resulting code
snippet will look something like this:
```html
<script src="https://giscus.app/client.js"
data-repo="<username>/<repository>"
data-repo-id="..."
data-category="Announcements"
data-category-id="..."
data-mapping="title"
data-strict="1"
data-reactions-enabled="1"
data-emit-metadata="1"
data-input-position="top"
data-theme="preferred_color_scheme"
data-lang="en"
data-loading="lazy"
crossorigin="anonymous"
async>
</script>
```
!!! example "Configure the code needed to embed Giscus into your blog"
Go to the [Giscus homepage] and configure the embedding code. There are a
number of settings:
1. Choose the language
2. Enter the username / organization name and repository name
3. Choose how the discussions are to be mapped to the page on your blog.
Because for a blog post the title is the basis of the URL, it makes
sense to use the `Discussion title contains page <title>` option.
4. Under `Discussion Category` choose `Announcements` to limit the creation
of new discussions to Giscus and people with maintainer or admin
permissions.
5. Under `Features`, select the following:
1. Enable reactions for the main post
2. Emit discussion metadata
3. Place the comment box above the comments
6. Under `Theme`, select `Preferred color scheme` so that Giscus matches
the color scheme selected by the user for your site.
[Giscus homepage]: https://giscus.app/
With these settings in place, you now need to integrate the code into your
site. There is a partial `partials/comments.html` that exists for this purpose
and is empty be default. It is included by the `content.html` partial, so will
be included for every page on your site. You may or may not want this. In this
tutorial, you will limit the Giscus integration to only blog posts but it is
easy enough to leave out the code that achieves this if you want to have Giscus
discussions active for every page.
!!! example "Add Giscus integration code"
First, you need to create an `overrides` directory that will contain the
templates and partials you want to override.
```
mkdir -p overrides/partials
```
You need to declare it in your `mkdocs.yaml`:
```yaml hl_lines="3"
theme:
name: material
custom_dir: overrides
```
Now add a file `overrides/partials/comments.html` and paste in the code
snippet you obtained from the Giscus homepage. Look at the result locally
and you will see that the integration is active on all pages of the site.
If you want to restrict it to your blog posts, you need to add a conditional
around the Giscus script that tests if comments should be included. A simple
way of doing this is to test for a metadata flag:
```html
{% if page.meta.comments %}
<script>...</script>
{% endif %}
```
The disadvantage is that you now need to manually turn on comments for each
blog post - unless you want to turn them off on some. To get the comments
section on all blog posts, use code like this:
```html
{% if page.file.src_uri.startswith('blog/posts') %}
<script>...</script>
{% endif %}
```
You should see now that the Giscus comments are added at the bottom of your
blog posts but not on other pages.
## What's next?
This is the end of the blog tutorial. We hope you have enjoyed it and manage to
set up your blog the way you like it. There are numerous other features and
options that we have not been able to cover here. The [blog plugin reference]
provides comprehensive documentation for the plugin. You may also want to
look at the [social plugin tutorial] to generate social cards for your blog
posts that get displayed when you post links to social media systems.
[blog plugin reference]: https://squidfunk.github.io/mkdocs-material/plugins/blog/
[social plugin tutorial]: ../social/basic.md

View File

@@ -0,0 +1,524 @@
# Navigation, authors, and pagination
The Blog plugin provides blog-style navigation with a reverse-chronological
index page and an archive organized by year by default. This tutorial shows
how you can configure details of the default navigation, configure authors, and
add more navigation options using categories and the [Tags plugin].
[Tags plugin]: ../../plugins/tags.md
__Time required:__ typically 30 minutes
## Integrating navigation
So far, you have let the Blog plugin and MkDocs worry about navigation. For some
use cases, this might be enough and it is simply sufficient to not declare a
`nav` section in the `mkdocs.yml`.
However, you may want to integrate a blog with other content and a navigation
structure that you have defined in the `nav` section of the configuration.
In such cases, you need to provide a place where the Blog plugin should
attach the blog navigation to the rest of the navigation structure.
!!! example "Integrate with site navigation"
Add the following to your `mkdocs.yml` to see how the Blog plugin can
integrate the blog navigation with the overall navigation structure.
Note that the only thing you need to specify at this point is the
index page for the blog and its path must match the `blog_dir` setting,
which is `blog` by default:
```yaml hl_lines="5 6"
nav:
- Home: index.md
- Install: install.md
- Usage: usage.md
- Blog:
- blog/index.md
```
You will notice that "Blog" is duplicated in the navigation structure. To
avoid this, you can use the `navigation.indexes` feature to make the blog
index the section index page for the blog:
```yaml hl_lines="3 4"
theme:
name: material
features:
- navigation.indexes
```
!!! tip "Stand-alone blog"
If what you need is a stand-alone blog instead of one that is integrated with
a larger site, this can be done by using the `blog_dir` configuration option.
To see how this is done, see [setting up a blog].
The rest of the tutorial assumes that you are integrating the blog with
a wider site.
[Setting up a blog]: ../../setup/setting-up-a-blog.md#blog-only
!!! tip "Adding pages"
You can add additional pages to the blog section by putting them into
`docs/blog` (and adding them to the navigation). The blog archive will be
added to the navigation after these pages.
## Configuring the archive
By default, the blog archive lists posts by year only. If you want to add
listings by month, you can configure the date format for the archive.
!!! example "Organize posts by month"
Add the following to your `mkdocs.yml` to get a listing with the month
name (in the language selected in the theme options):
```yaml hl_lines="2"
- blog:
archive_date_format: MMMM yyyy
```
If you do not want the full month name, you can make the date
configuration `MM/yyyy`, for example.
If you want to add the day, you can add a placeholder for them.
For example, to get an American-style output, make it `MM/dd/yyyy`.
For the plugin to sort the blog posts by the full date, you will
also need to set the `archive_url_date_format` to include the month
and day, so make it `MM/dd/yyyy` as well.
## Using categories
Categories are a way to make blog posts accessible by topic while retaining
the navigation structure based on chronology within each category listing.
Use them when there is a limited set of non-overlapping categories that
you can sort your posts into.
Categories appear in the main navigation, so are directly accessible from there.
This implies that there are relatively few categories as otherwise the
`categories` section in your main navigation will become too crowded.
!!! example "Add a category"
Add a category to your first blog post by adding it to the page header:
``` hl_lines="4 5""
---
date: 2023-12-31
updated: 2024-01-02
categories:
- Holidays
---
```
Now that the blog post has been categorised, `Holidays` appears under
`Categories` in the main navigation and the blog post appears in the
index page for this category.
!!! tip "Single or multiple categories?"
While it is traditionally the case that a blog post would belong to only
one category, Material for MkDocs actually allows you to assign more
than one. While this gives you a degree of freedom, you should
probably not use this too much, not least because you can use tags to
deal with multiple classifications. We will cover them in the next step.
Material allows you to control which categories blog authors can use. You
declare them in the `mkdocs.yml`. This way you can make sure everyone sticks
to agreed categories and that the plugin detects typos.
!!! example "Control your categories"
Add a `categories_allowed` entry to the configuration of the Blog plugin
with the entries "Holidays" and "News":
```yaml hl_lines="5-7"
plugins:
- search
- blog:
archive_date_format: MMMM yyyy
categories_allowed:
- Holidays
- News
```
Now, when you add a category to a blog post that does not match one of these
two, you should get a build error.
## Using tags
The [Tags plugin] provides another way to classify blog posts and to make
them accessible independently of the main navigation structure. Tags are useful
for making related content easily discoverable even if it is in different parts
of the navigation hierarchy.
[Tags plugin]: https://squidfunk.github.io/mkdocs-material/plugins/tags/
You may have a tutorial like this one as well as a more comprehensive setup guide
and reference documentation. Adding the same tag to all three shows that they
are related. As you will see, it is possible to navigate from a tagged page to
the tag index and, from there, to other pages that carry the same tag.
!!! example "Enable the plugin and add tags"
First, you need to add the plugin to your `mkdocs.yml`:
```yaml hl_lines="8"
plugins:
- search
- blog:
archive_date_format: MMMM yyyy
categories_allowed:
- Holidays
- News
- tags
```
Once this is done, you can add tags to posts in the page header:
``` hl_lines="9-12""
---
date:
created: 2023-12-31
updated: 2024-01-02
authors:
- material
categories:
- Holidays
tags:
- new year
- hogmanay
- festive season
---
```
You should see the tags that you defined at the top of the post. However, at the
moment that is it. While the blog plugin automatically creates an index page for
categories, the tags plugin does not do the same for tags. This is because the
tags plugin is not specific for blogs. You can use it for any site content, so
it is not obvious were the tag index should go.
You can configure a basic tag index using the public version of Material for
MkDocs. The Insider Edition supports this as well, of course, but also provides
an alternative index mechanism that allows for an arbitrary number of tag
indexes, scoped listings, shadow tags, nested tags, and much more.
!!! example "Adding a tags index"
=== "Basic tag index"
To configure a tag index using the public version, add a `tags_file` entry
to your configuration of the tags plugin and configure it in your `nav`
section. Remember to add a colon at the end of the existing `tags` entry.
```yaml hl_lines="8-9 17"
plugins:
- search
- blog:
archive_date_format: MMMM yyyy
categories_allowed:
- Holidays
- News
- tags:
tags_file: blog/tags.md
nav:
- Home: index.md
- Install: install.md
- Usage: usage.md
- Blog:
- blog/index.md
- Tags: blog/tags.md
```
The tag index will be appended to the configured page, which you should
now create at the location specified.
Note that you can put the tag index page anywhere in your primary
navigation, so if you are using tags elsewhere instead of just in your
blog then you may want to have the tag index outside the blog section
of the navigation.
=== "Insider Edition"
To add a tag index, you add a placeholder in a Markdown file to tell
the plugin to insert an index at that point. This means that you
can add content before and after the index. Crucially, you can add
placeholders in multiple pages, each with a configuration of what
subset of tags should be displayed in the index.
The simplest index page looks like this. Create it under `docs/tags.md`.
```markdown
# Tag index
<!-- material/tags -->
```
Now, you may want to keep the tags for your blog separate from tags
you use in the rest of your page. You can achieve this by assigning
the tag index a scope. Put the following under `docs/blog/tags.md`:
```markdown
# Tag index for the blog
<!-- material/tags { scope: true } -->
```
You now have two index pages: one covers the whole site and one
covers only the blog. Add both to the navigation:
```yaml
nav:
- Home: index.md
- Tags: tags.md
- Blog:
- blog/index.md
- blog/tags.md
```
The tags plugin in the Insider Edition is an incredibly powerful tool
and we can only scratch the surface of what is possible with it. If you
want to explore more after you have worked for this part of the tutorial,
have a look at the [tags plugin reference].
[tags plugin reference]: ../../plugins/tags.md
## Defining authors
If your blog has more than one author then you may want to identify the author
for each blog post. The blog plugin allows you to create a file that contains
the author information and to then reference the authors of a particular post in
the page header.
!!! example "Create author info"
Create a file `docs/blog/.authors.yml` with this content:
```yaml
authors:
team:
name: Team
description: Creator
avatar: https://simpleicons.org/icons/materialformkdocs.svg
squidfunk:
name: Martin Donath
description: Creator
avatar: https://github.com/squidfunk.png
```
and then add a line to the header of the first post:
```hl_lines="5-6"
---
date:
created: 2023-12-31
updated: 2024-01-02
authors:
- team
---
```
Note that `authors` is a list, so you can specify multiple authors.
You can create custom author index pages that can highlight the contributions
of an author as well as provide additional information about them.
!!! example "Add author page"
First, you need to enable author profiles in the `mkdocs.yml`:
```yaml hl_lines="8"
plugins:
- search
- blog:
archive_date_format: MMMM yyyy
categories_allowed:
- Holidays
- News
authors_profiles: true
```
Check your blog to see that there is now an extra entry in the main
navigation next to `archive` and `categories` that lists the authors and
their contributions.
To customize the author page, you can create a page that overrides the one
generated by default. First, create the `author` directory that the profile
pages will live in:
```hl_lines="3"
docs
├── blog
│   ├── author
│   ├── index.md
│   └── posts
│   ├── draft.md
│   └── myfirst.md
└── index.md
```
Then create a page `docs/blog/author/team.md`:
```
# The Material Team
A small group of people dedicated to making writing documentation easy, if
not outright fun! Here are some of the things we have blogged about:
```
As you can see, the author index gets appended to the content you have
written in the Markdown file.
## Pagination
Once your blog starts growing, you may not want to pay attention to the number
of posts displayed per page. By default, the plugin displays up to 10 posts on
the index pages. You can change this number separately for the main index,
the archive index pages, and the category index pages.
!!! example "Changing pagination"
Add five more blog posts, then set the pagination setting to show five per
page only:
```yaml hl_lines="7"
- blog:
archive_date_format: MMMM yyyy
categories_allowed:
- Holidays
- News
authors_profiles: true
pagination_per_page: 5
```
You will see that the pagination setting for archive and category pages
are inherited from the setting you added. If you want to have different
settings for the different index pages, you can specify each setting
separately:
```yaml
- blog:
archive_date_format: MMMM yyyy
categories_allowed:
- Holidays
- News
authors_profiles: true
pagination_per_page: 5
archive_pagination_per_page: 10
categories_pagination_per_page: 10
```
## Blog table of contents
Another thing you may want to do once you have a large enough number of posts
is to turn on the function that produces a table of contents for the blog
index pages, giving your readers the opportunity to quickly scan the content
of each page for something that interests them without having to scroll
(assuming that the number of post per page is not too big).
!!! example "Turn on the table of contents feature"
To produce a table of contents for the blog index pages, add the following
to the configuration of the blog plugin:
```yaml hl_lines="2"
- blog:
blog_toc: true
archive_date_format: MMMM yyyy
# ...
```
## Custom slugs
If, for some reason, you are not happy with the way that Material for MkDocs
turns headings into slugs, you can create your own slugify function or you
can manually define a slug for a specific post.
!!! example "Slugify function"
To define your own slugify function, you need to write a Python function
that converts text into a slug given additional arguments from the
configuration. You also need to write a function that returns that
function.
Say you want to define two slugify functions that you can switch between.
The first one returns a slug similar to what the default slugify function
produces. The second one cuts the result of that up into words and returns
a slug based on a maximum of five of them:
```python
import re, functools, unicodedata
RE_HTML_TAGS = re.compile(r'</?[^>]*>', re.UNICODE)
RE_INVALID_SLUG_CHAR = re.compile(r'[^\w\- ]', re.UNICODE)
RE_WHITESPACE = re.compile(r'\s', re.UNICODE)
def _make_slug(text, sep, **kwargs):
slug = unicodedata.normalize('NFC', text)
slug = RE_HTML_TAGS.sub('', slug)
slug = RE_INVALID_SLUG_CHAR.sub('', slug)
slug = slug.strip().lower()
slug = RE_WHITESPACE.sub(sep, slug)
return slug
def _make_slug_short(text, sep, **kwargs):
words = _make_slug(text, sep, **kwargs).split(sep)
return sep.join(words[:5])
def slugify(**kwargs):
if 'short' in kwargs and kwargs['short']:
return functools.partial(_make_slug_short, **kwargs)
return functools.partial(_make_slug, **kwargs)
```
Save this code in `ext/slugs.py` and also add an (empty) `__init__.py`
file to indicate that the directory is a module. Now you can configure
your custom slugify code like this:
```yaml hl_lines="4-6"
plugins:
- blog:
# other entries omitted
post_slugify: !!python/object/apply:ext.slugs.slugify
kwds:
short: true
```
Change the heading of a blog post to be longer than five words and observe
how the slugify function shortens the URL. Change the `short` attribute to
`false` and you can turn this off again.
If you want to influence the slug only for a single blog post, you can define
it manually by specifying it in the header of the post. Note that this is meant
as a last resort option. Specifying a custom slug manually for every post would
be tedious.
!!! example "Manually define slug"
If, for example, you wanted the slug to be 'ny-eve' instead of the somewhat
lengthy 'happy-new-years-eve', you could add the following:
```hl_lines="7"
---
date:
created: 2023-12-31
updated: 2024-01-02
readtime: 15
pin: true
slug: ny-eve
---
```
The URL for this post should now be
`http://localhost:8000/blog/2023/01/31/ny-eve/`.
## What's next?
You may want to increase engagement with your blog by allowing people to
subscribe to an RSS feed, by providing links to your social media profiles, by
providing share and like buttons, or by setting up a comment system.
The [engagement and dissemination tutorial] walks you through setting these up.
[engagement and dissemination tutorial]: engage.md