Add AI documentation skills
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user