mkdocs-material/docs/tutorials/blogs/navigation.md
Alexander Voss 50d7c54bdf
Added blog and social cards tutorials (#7014)
* added tutorials page and blog tutorial

* proof reading, some language improvements

* split blog tutorial into parts

and reworked on the basis of comments, improved language, added missing
things

* + custom slugify function example

* +blog tutorial on engagement

* + section blog ToC

* + instructions for X/FB share buttons and started discussion system

* fix: module name `code` shadowed Python standard library module

* added What's next section

* +blog tutorial engagement part

* fix: updated Giscus code snippet

* trying multiple tutorials with headings for each group

* + basic social cards tutorial

* + custom social card tutorial

* + estimated time for custom card tutorial

* added tutorial sections to nav

* fixed typos, removed sponsor icon from level one heading

* removed "tutorial" from level one heading

* added headings to custom layout example

* fixed broken link

* fixes after run-through, added links to template repos

* added comment for @squidfunk

* clarified use of logos, some proof-reading

* removed question I left for @squidfunk

* clarified relationship between background color and image

* added override for the background image

* fixed wording and indentation

* changed example to be releases, not events

* being more specific where to add the last bit of code in the layout

---------

Co-authored-by: squidfunk <martin.donath@squidfunk.com>
2024-05-28 12:21:00 +01:00

525 lines
17 KiB
Markdown

# 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 seciton 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 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.
With the Insiders edition, 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 <!-- md:sponsors -->"
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