Merge branch 'master' into fix/instant-loading

This commit is contained in:
squidfunk
2023-09-23 16:48:31 +02:00
23 changed files with 540 additions and 285 deletions

View File

@@ -36,7 +36,35 @@ permissions:
contents: read contents: read
jobs: jobs:
npm: npm-build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Node.js runtime
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
- name: Set up Node.js dependency cache
uses: actions/cache@v3
id: cache
with:
key: ${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
path: node_modules
- name: Set up Node.js dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: npm install
- name: Build project
run: |
npm run build
git diff --name-only
npm-check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -62,12 +90,7 @@ jobs:
- name: Check project - name: Check project
run: npm run check run: npm run check
- name: Build project python:
run: |
npm run build
git diff --name-only
pypi:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -78,6 +101,10 @@ jobs:
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
python-version: ${{ env.PYTHON_VERSION }} python-version: ${{ env.PYTHON_VERSION }}
cache: pip
cache-dependency-path: |
pyproject.toml
requirements.txt
- name: Set up Python dependencies - name: Set up Python dependencies
run: pip install --upgrade build twine run: pip install --upgrade build twine

View File

@@ -29,6 +29,8 @@ env:
permissions: permissions:
contents: write contents: write
id-token: write
pages: write
jobs: jobs:
documentation: documentation:
@@ -40,20 +42,25 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
sparse-checkout: |
docs
includes
material/overrides
src/templates/partials/languages
- name: Set up Python runtime - name: Set up Python runtime
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
python-version: ${{ env.PYTHON_VERSION }} python-version: ${{ env.PYTHON_VERSION }}
cache: pip
- name: Set the date environmental variable cache-dependency-path: |
run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV pyproject.toml
requirements.txt
- name: Set up build cache - name: Set up build cache
uses: actions/cache@v3 uses: actions/cache/restore@v3
id: cache
with: with:
key: mkdocs-material-${{ env.cache_id }} key: mkdocs-material-${{ hashfiles('.cache/**') }}
path: .cache path: .cache
restore-keys: | restore-keys: |
mkdocs-material- mkdocs-material-
@@ -63,15 +70,8 @@ jobs:
- name: Install Python dependencies - name: Install Python dependencies
run: | run: |
pip install \ pip install mkdocs-material
"cairosvg>=2.5" \ pip install mkdocs-material[recommended,git,imaging]
"mkdocs-git-committers-plugin-2>=1.1.1" \
"mkdocs-git-revision-date-localized-plugin>=1.0" \
"mkdocs-minify-plugin>=0.3" \
"mkdocs-rss-plugin>=1.2" \
"mkdocs-redirects>=1.0" \
"lxml" \
"pillow<10"
- name: Install Insiders build - name: Install Insiders build
if: github.event.repository.fork == false if: github.event.repository.fork == false
@@ -88,10 +88,30 @@ jobs:
rm -rf material rm -rf material
cp -r mkdocs-material-insiders/material material cp -r mkdocs-material-insiders/material material
- name: Deploy documentation - name: Build documentation
env: env:
GH_TOKEN: ${{ secrets.GH_TOKEN }} GH_TOKEN: ${{ secrets.GH_TOKEN }}
GOOGLE_ANALYTICS_KEY: ${{ secrets.GOOGLE_ANALYTICS_KEY }} GOOGLE_ANALYTICS_KEY: ${{ secrets.GOOGLE_ANALYTICS_KEY }}
run: | run: |
mkdocs gh-deploy --force mkdocs build --clean
mkdocs --version mkdocs --version
- name: Adjust permissions
run: |
chmod -c -R +rX site/ | while read line; do
echo "::warning title=Invalid file permissions automatically fixed::$line"
done
- name: Upload to GitHub Pages
uses: actions/upload-pages-artifact@v2
with:
path: site
- name: Deploy to GitHub Pages
uses: actions/deploy-pages@v2
- name: Save build cache
uses: actions/cache/save@v3
with:
key: mkdocs-material-${{ hashfiles('.cache/**') }}
path: .cache

View File

@@ -0,0 +1,100 @@
---
date: 2023-09-22
authors: [squidfunk]
categories:
- Build
- Performance
links:
- publishing-your-site.md#with-github-actions
- creating-your-site.md#building-your-site
---
# Using `git sparse-checkout` for faster documentation builds
__Leveraging `git sparse-checkout` in GitHub Actions enabled us to speed up
documentation builds in our repository, cutting checkout times from 20 to 30
seconds to just 2 seconds.__
Developing an efficient approach to build documentation in CI workflows is
essential, especially when working in large repositories with thousands of
commits, like ours. Of course, we want to build documentation quickly and
efficiently, ensuring fast and productive workflows. When using both the
wonderful [`git-committers`][git-committers] and [`git-revision-date-localized`]
[git-revision-date-localized] plugins to display [document contributors] and
[dates] at the bottom of each page, we are required to set `fetch-depth: 0`,
which resulted in checkout times of 20 to 30 seconds on our repository. By
leveraging [`git sparse-checkout`][git sparse-checkout] within [GitHub Actions],
check out time was brought down to 2 seconds.
[git sparse-checkout]: https://git-scm.com/docs/git-sparse-checkout
[GitHub Actions]: ../../publishing-your-site.md#with-github-actions
[git-revision-date-localized]: https://github.com/timvink/mkdocs-git-revision-date-localized-plugin
[git-committers]: https://github.com/ojacques/mkdocs-git-committers-plugin-2
[document contributors]: ../../setup/adding-a-git-repository.md#document-contributors
[dates]: ../../setup/adding-a-git-repository.md#document-dates
<!-- more -->
## A Primer
[`git sparse-checkout`][git sparse-checkout] allows you to check out only a
subset of the files in a repository, making it incredibly useful for large
repositories where a full checkout takes long and includes many files that are
not relevant when building documentation.
## GitHub Actions
To enable [`git sparse-checkout`][git sparse-checkout] within [GitHub Actions]
and ensure that you are only building the documentation that you need, add the
following lines to your workflow file:
``` yaml
- uses: actions/checkout@v4
with:
fetch-depth: 0
sparse-checkout: |
docs
includes
```
[`git sparse-checkout`][git sparse-checkout] always checks out all files
residing in the repositorys root. This means that regardless of the specified
paths or directories for sparse checkout, the files located in the root of the
repository will always be included in the checkout process.
Thus, you only need to specify the directories that are necessary for building
documentation. In our case, we only need the `docs` and `includes` folders,
but if you need additional directories, you can just add them to the end of the
list. A complete example workflow for [GitHub Actions]:
``` yaml hl_lines="13-18"
name: documentation
on:
push:
branches:
- master
- main
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
sparse-checkout: |
docs
includes
- uses: actions/setup-python@v4
with:
python-version: 3.x
- run: pip install mkdocs-material
- run: mkdocs gh-deploy --force
```
## Conclusion
That's all there is! We're super happy with the results and hope that this will
help you to speed up your documentation builds in [GitHub Actions] as well. As
always, feel free to share your thoughts and experiences in the comments below.

View File

@@ -21,7 +21,7 @@
import os import os
import re import re
from glob import glob from glob import iglob
from mkdocs.config.defaults import MkDocsConfig from mkdocs.config.defaults import MkDocsConfig
from mkdocs.structure.pages import Page from mkdocs.structure.pages import Page
from urllib.parse import urlencode, urlparse from urllib.parse import urlencode, urlparse
@@ -40,7 +40,7 @@ def on_page_markdown(markdown: str, *, page: Page, config: MkDocsConfig, files):
# Collect all existing languages # Collect all existing languages
names: dict[str, str] = {} names: dict[str, str] = {}
known: dict[str, dict[str, str]] = {} known: dict[str, dict[str, str]] = {}
for path in glob("src/templates/partials/languages/*.html"): for path in iglob("src/templates/partials/languages/*.html"):
with open(path, "r", encoding = "utf-8") as f: with open(path, "r", encoding = "utf-8") as f:
data = f.read() data = f.read()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -44,7 +44,7 @@
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block styles %} {% block styles %}
<link rel="stylesheet" href="{{ 'assets/stylesheets/main.72749a73.min.css' | url }}"> <link rel="stylesheet" href="{{ 'assets/stylesheets/main.d451bc0e.min.css' | url }}">
{% if config.theme.palette %} {% if config.theme.palette %}
{% set palette = config.theme.palette %} {% set palette = config.theme.palette %}
<link rel="stylesheet" href="{{ 'assets/stylesheets/palette.a5377069.min.css' | url }}"> <link rel="stylesheet" href="{{ 'assets/stylesheets/palette.a5377069.min.css' | url }}">
@@ -250,7 +250,7 @@
</script> </script>
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}
<script src="{{ 'assets/javascripts/bundle.73f771f6.min.js' | url }}"></script> <script src="{{ 'assets/javascripts/bundle.5827baa9.min.js' | url }}"></script>
{% for script in config.extra_javascript %} {% for script in config.extra_javascript %}
{{ script | script_tag }} {{ script | script_tag }}
{% endfor %} {% endfor %}

View File

@@ -8,7 +8,7 @@
<div class="md-sidebar md-sidebar--post" data-md-component="sidebar" data-md-type="navigation"> <div class="md-sidebar md-sidebar--post" data-md-component="sidebar" data-md-type="navigation">
<div class="md-sidebar__scrollwrap"> <div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner md-post"> <div class="md-sidebar__inner md-post">
<nav class="md-nav"> <nav class="md-nav md-nav--primary">
<div class="md-post__back"> <div class="md-post__back">
<div class="md-nav__title md-nav__container"> <div class="md-nav__title md-nav__container">
<a href="{{ page.parent.url | url }}" class="md-nav__link"> <a href="{{ page.parent.url | url }}" class="md-nav__link">
@@ -35,62 +35,66 @@
</div> </div>
{% endif %} {% endif %}
<ul class="md-post__meta md-nav__list"> <ul class="md-post__meta md-nav__list">
<li class="md-nav__item md-nav__title"> <li class="md-nav__item md-nav__item--section">
<div class="md-nav__link"> <div class="md-post__title">
<span class="md-ellipsis"> <span class="md-ellipsis">
{{ lang.t("blog.meta") }} {{ lang.t("blog.meta") }}
</span> </span>
</div> </div>
<nav class="md-nav">
<ul class="md-nav__list">
<li class="md-nav__item">
<div class="md-nav__link">
{% include ".icons/material/calendar.svg" %}
<time datetime="{{ page.config.date.created }}" class="md-ellipsis">
{{- page.config.date.created | date -}}
</time>
</div>
</li>
{% if page.config.date.updated %}
<li class="md-nav__item">
<div class="md-nav__link">
{% include ".icons/material/calendar-clock.svg" %}
<time datetime="{{ page.config.date.updated }}" class="md-ellipsis">
{{- page.config.date.updated | date -}}
</time>
</div>
</li>
{% endif %}
{% if page.categories %}
<li class="md-nav__item">
<div class="md-nav__link">
{% include ".icons/material/bookshelf.svg" %}
<span class="md-ellipsis">
{{ lang.t("blog.categories.in") }}
{% for category in page.categories %}
<a href="{{ category.url | url }}">
{{- category.title -}}
</a>
{%- if loop.revindex > 1 %}, {% endif -%}
{% endfor -%}
</span>
</div>
</li>
{% endif %}
{% if page.config.readtime %}
{% set time = page.config.readtime %}
<li class="md-nav__item">
<div class="md-nav__link">
{% include ".icons/material/clock-outline.svg" %}
<span class="md-ellipsis">
{% if time == 1 %}
{{ lang.t("readtime.one") }}
{% else %}
{{ lang.t("readtime.other") | replace("#", time) }}
{% endif %}
</span>
</div>
</li>
{% endif %}
</ul>
</nav>
</li> </li>
<li class="md-nav__item">
<div class="md-nav__link">
{% include ".icons/material/calendar.svg" %}
<time datetime="{{ page.config.date.created }}" class="md-ellipsis">
{{- page.config.date.created | date -}}
</time>
</div>
</li>
{% if page.config.date.updated %}
<li class="md-nav__item">
<div class="md-nav__link">
{% include ".icons/material/calendar-clock.svg" %}
<time datetime="{{ page.config.date.updated }}" class="md-ellipsis">
{{- page.config.date.updated | date -}}
</time>
</div>
</li>
{% endif %}
{% if page.categories %}
<li class="md-nav__item">
<div class="md-nav__link">
{% include ".icons/material/bookshelf.svg" %}
<span class="md-ellipsis">
{{ lang.t("blog.categories.in") }}
{% for category in page.categories %}
<a href="{{ category.url | url }}">
{{- category.title -}}
</a>
{%- if loop.revindex > 1 %}, {% endif -%}
{% endfor -%}
</span>
</div>
</li>
{% endif %}
{% if page.config.readtime %}
{% set time = page.config.readtime %}
<li class="md-nav__item">
<div class="md-nav__link">
{% include ".icons/material/clock-outline.svg" %}
<span class="md-ellipsis">
{% if time == 1 %}
{{ lang.t("readtime.one") }}
{% else %}
{{ lang.t("readtime.other") | replace("#", time) }}
{% endif %}
</span>
</div>
</li>
{% endif %}
</ul> </ul>
</nav> </nav>
{% if "toc.integrate" in features %} {% if "toc.integrate" in features %}

View File

@@ -47,6 +47,7 @@
"select.language": "Izberi jezik", "select.language": "Izberi jezik",
"select.version": "Izberi različico", "select.version": "Izberi različico",
"source": "Pojdi na repozitorij", "source": "Pojdi na repozitorij",
"source.file.contributors": "Soavtorji",
"source.file.date.created": "Ustvarjeno", "source.file.date.created": "Ustvarjeno",
"source.file.date.updated": "Zadnja posodobitev", "source.file.date.updated": "Zadnja posodobitev",
"tabs": "Zavihki", "tabs": "Zavihki",

View File

@@ -48,25 +48,26 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% if "navigation.sections" in features and level == 1 + ( {% set tabs = "navigation.tabs" in features %}
"navigation.tabs" in features {% set sections = "navigation.sections" in features %}
) %} {% if tabs and level == 1 or sections and tabs >= level - 1 %}
{% set class = class ~ " md-nav__item--section" %} {% set class = class ~ " md-nav__item--section" %}
{% set is_section = true %}
{% elif not nav_item.active and "navigation.prune" in features %} {% elif not nav_item.active and "navigation.prune" in features %}
{% set class = class ~ " md-nav__item--pruned" %} {% set class = class ~ " md-nav__item--pruned" %}
{% set prune = true %} {% set is_pruned = true %}
{% endif %} {% endif %}
<li class="{{ class }} md-nav__item--nested"> <li class="{{ class }} md-nav__item--nested">
{% if not prune %} {% if not is_pruned %}
{% set expanded = "navigation.expand" in features %}
{% set active = nav_item.active or expanded %}
{% set checked = "checked" if nav_item.active %} {% set checked = "checked" if nav_item.active %}
{% if expanded and not checked %} {% set is_expanded = "navigation.expand" in features %}
{% if is_expanded and not checked %}
{% set indeterminate = "md-toggle--indeterminate" %} {% set indeterminate = "md-toggle--indeterminate" %}
{% endif %} {% endif %}
<input class="md-nav__toggle md-toggle {{ indeterminate }}" type="checkbox" id="{{ path }}" {{ checked }}> <input class="md-nav__toggle md-toggle {{ indeterminate }}" type="checkbox" id="{{ path }}" {{ checked }}>
{% if not indexes %} {% if not indexes %}
<label class="md-nav__link" for="{{ path }}" id="{{ path }}_label" tabindex="0"> {% set tabindex = "0" if not is_section %}
<label class="md-nav__link" for="{{ path }}" id="{{ path }}_label" tabindex="{{ tabindex }}">
{{ render_content(nav_item) }} {{ render_content(nav_item) }}
<span class="md-nav__icon md-icon"></span> <span class="md-nav__icon md-icon"></span>
</label> </label>
@@ -78,7 +79,8 @@
{{ render_content(index, nav_item) }} {{ render_content(index, nav_item) }}
</a> </a>
{% if nav_item.children | length > 1 %} {% if nav_item.children | length > 1 %}
<label class="md-nav__link {{ class }}" for="{{ path }}"> {% set tabindex = "0" if not is_section %}
<label class="md-nav__link {{ class }}" for="{{ path }}" id="{{ path }}_label" tabindex="{{ tabindex }}">
<span class="md-nav__icon md-icon"></span> <span class="md-nav__icon md-icon"></span>
</label> </label>
{% endif %} {% endif %}

View File

@@ -21,7 +21,7 @@
import os import os
import re import re
from glob import glob from glob import iglob
from mkdocs.config.defaults import MkDocsConfig from mkdocs.config.defaults import MkDocsConfig
from mkdocs.structure.pages import Page from mkdocs.structure.pages import Page
from urllib.parse import urlencode, urlparse from urllib.parse import urlencode, urlparse
@@ -40,7 +40,7 @@ def on_page_markdown(markdown: str, *, page: Page, config: MkDocsConfig, files):
# Collect all existing languages # Collect all existing languages
names: dict[str, str] = {} names: dict[str, str] = {}
known: dict[str, dict[str, str]] = {} known: dict[str, dict[str, str]] = {}
for path in glob("src/templates/partials/languages/*.html"): for path in iglob("src/templates/partials/languages/*.html"):
with open(path, "r", encoding = "utf-8") as f: with open(path, "r", encoding = "utf-8") as f:
data = f.read() data = f.read()

View File

@@ -24,6 +24,7 @@ import {
Observable, Observable,
Subject, Subject,
animationFrameScheduler, animationFrameScheduler,
asyncScheduler,
auditTime, auditTime,
combineLatest, combineLatest,
defer, defer,
@@ -36,6 +37,7 @@ import {
ignoreElements, ignoreElements,
map, map,
mergeMap, mergeMap,
observeOn,
takeUntil, takeUntil,
tap, tap,
withLatestFrom withLatestFrom
@@ -202,6 +204,7 @@ export function mountSidebar(
.pipe( .pipe(
mergeMap(label => fromEvent(label, "click") mergeMap(label => fromEvent(label, "click")
.pipe( .pipe(
observeOn(asyncScheduler),
map(() => label), map(() => label),
takeUntil(done$) takeUntil(done$)
) )

View File

@@ -30,10 +30,12 @@ import {
debounceTime, debounceTime,
distinctUntilKeyChanged, distinctUntilKeyChanged,
endWith, endWith,
filter,
fromEvent, fromEvent,
ignoreElements, ignoreElements,
map, map,
of, of,
sample,
share, share,
skip, skip,
startWith, startWith,
@@ -291,19 +293,17 @@ export function setupInstantLoading(
popstate$.pipe(map(getLocation)) popstate$.pipe(map(getLocation))
.subscribe(location$) .subscribe(location$)
// Intercept clicks on anchor links, and scroll document into position. As // Intercept clicks on anchor links, and scroll document into position - as
// we disabled scroll restoration, we need to do this manually here. // we disabled scroll restoration, we need to do this manually here
location$ location$
.pipe( .pipe(
startWith(getLocation()), startWith(getLocation()),
bufferCount(2, 1), bufferCount(2, 1),
switchMap(([prev, next]) => ( filter(([prev, next]) => (
prev.pathname === next.pathname && prev.pathname === next.pathname &&
prev.hash !== next.hash prev.hash !== next.hash
) )),
? of(next) map(([, next]) => next)
: EMPTY
)
) )
.subscribe(url => { .subscribe(url => {
if (history.state !== null || !url.hash) { if (history.state !== null || !url.hash) {
@@ -315,6 +315,29 @@ export function setupInstantLoading(
} }
}) })
// Intercept clicks on the same anchor link - we must use a distinct pipeline
// for this, or we'd end up in a loop, setting the hash again and again
location$
.pipe(
sample(instant$),
startWith(getLocation()),
bufferCount(2, 1),
filter(([prev, next]) => (
prev.pathname === next.pathname &&
prev.hash === next.hash
)),
map(([, next]) => next)
)
.subscribe(url => {
history.scrollRestoration = "auto"
setLocationHash(url.hash)
history.scrollRestoration = "manual"
// Hack: we need to make sure that we don't end up with multiple history
// entries for the same anchor link, so we just remove the last entry
history.back()
})
// After parsing the document, check if the current history entry has a state. // After parsing the document, check if the current history entry has a state.
// This may happen when users press the back or forward button to visit a page // This may happen when users press the back or forward button to visit a page
// that was already seen. If there's no state, it means a new page was visited // that was already seen. If there's no state, it means a new page was visited
@@ -332,9 +355,8 @@ export function setupInstantLoading(
// the current history state whenever the scroll position changes. This must // the current history state whenever the scroll position changes. This must
// be debounced and cannot be done in popstate, as popstate has already // be debounced and cannot be done in popstate, as popstate has already
// removed the entry from the history. // removed the entry from the history.
document$ viewport$
.pipe( .pipe(
switchMap(() => viewport$),
distinctUntilKeyChanged("offset"), distinctUntilKeyChanged("offset"),
debounceTime(100) debounceTime(100)
) )

View File

@@ -80,19 +80,10 @@
list-style: none; list-style: none;
} }
// Navigation item
&__item {
padding: 0 px2rem(12px);
// Navigation item on level 2
& & {
padding-inline-end: 0;
}
}
// Navigation link // Navigation link
&__link { &__link {
display: flex; display: flex;
gap: px2rem(8px);
align-items: flex-start; align-items: flex-start;
margin-top: 0.625em; margin-top: 0.625em;
transition: color 125ms; transition: color 125ms;
@@ -121,9 +112,9 @@
position: relative; position: relative;
} }
// Always align navigation icons to the right // Always align navigation icons to the end
.md-icon:last-child { .md-icon:last-child {
margin-left: auto; margin-inline-start: auto;
} }
// Navigation link icon // Navigation link icon
@@ -131,15 +122,10 @@
flex-shrink: 0; flex-shrink: 0;
height: 1.3em; height: 1.3em;
fill: currentcolor; fill: currentcolor;
// Adjust spacing of next child
+ * {
margin-inline-start: px2rem(8px);
}
} }
// Navigation link on focus/hover // Navigation link on focus/hover
&:not(.md-nav__container):is(:focus, :hover) { &:is([href], [for]):is(:focus, :hover) {
color: var(--md-accent-fg-color); color: var(--md-accent-fg-color);
cursor: pointer; cursor: pointer;
} }
@@ -177,6 +163,9 @@
// Stretch first child // Stretch first child
&:first-child { &:first-child {
flex-grow: 1; flex-grow: 1;
// Hack: if a very long word is used, it can push the arrow out of sight.
// Setting this property contains the text - see https://t.ly/E02vp
min-width: 0;
} }
} }
@@ -290,7 +279,6 @@
// Navigation item // Navigation item
.md-nav__item { .md-nav__item {
padding: 0;
border-top: px2rem(1px) solid var(--md-default-fg-color--lightest); border-top: px2rem(1px) solid var(--md-default-fg-color--lightest);
// Navigation link in active navigation // Navigation link in active navigation
@@ -386,7 +374,7 @@
background-color: transparent; background-color: transparent;
} }
// Toggle for nested navigation // Hide nested navigation
&__toggle ~ & { &__toggle ~ & {
display: flex; display: flex;
opacity: 0; opacity: 0;
@@ -475,62 +463,118 @@
// [tablet landscape +]: Tree-like table of contents // [tablet landscape +]: Tree-like table of contents
@include break-from-device(tablet landscape) { @include break-from-device(tablet landscape) {
margin-bottom: px2rem(-8px);
// Navigation title // Table of contents
&--secondary &__title { &--secondary {
position: sticky;
top: 0;
// Hack: because of the hack that we need to make .md-ellipsis work in
// Safari, we need to set `z-index` here as - see https://bit.ly/3s5M2jm
z-index: 1;
background: var(--md-default-bg-color);
box-shadow: 0 0 px2rem(8px) px2rem(8px) var(--md-default-bg-color);
// Adjust snapping behavior // Navigation title
&[for="__toc"] { .md-nav__title {
scroll-snap-align: start; position: sticky;
top: 0;
// Hack: because of the hack that we need to make .md-ellipsis work in
// Safari, we need to set `z-index` here as - see https://bit.ly/3s5M2jm
z-index: 1;
background: var(--md-default-bg-color);
box-shadow: 0 0 px2rem(8px) px2rem(8px) var(--md-default-bg-color);
// Adjust snapping behavior
&[for="__toc"] {
scroll-snap-align: start;
}
// Hide navigation icon
.md-nav__icon {
display: none;
}
} }
// Hide navigation icon // Adjust spacing for navigation list - same reason as below
.md-nav__icon { .md-nav__list {
display: none; padding-inline-start: px2rem(12px);
padding-bottom: px2rem(8px);
}
// Adjust spacing for navigation link - before this change, we set spacing
// on the left and right of a navigation item, but this led to the problem
// of cropped focus outlines, because we must set `overflow: hidden` on
// the navigation list for smooth expand and collapse transitions.
.md-nav__item > .md-nav__link {
margin-inline-end: px2rem(8px);
} }
} }
} }
// [screen +]: Tree-like navigation // [screen +]: Tree-like navigation
@include break-from-device(screen) { @include break-from-device(screen) {
margin-bottom: px2rem(-8px);
transition: max-height 250ms cubic-bezier(0.86, 0, 0.07, 1); transition: max-height 250ms cubic-bezier(0.86, 0, 0.07, 1);
// Navigation title // Primary navigation
&--primary &__title { &--primary {
position: sticky;
top: 0;
// Hack: because of the hack that we need to make .md-ellipsis work in
// Safari, we need to set `z-index` here as - see https://bit.ly/3s5M2jm
z-index: 1;
background: var(--md-default-bg-color);
box-shadow: 0 0 px2rem(8px) px2rem(8px) var(--md-default-bg-color);
// Adjust snapping behavior // Navigation title
&[for="__drawer"] { .md-nav__title {
scroll-snap-align: start; position: sticky;
top: 0;
// Hack: because of the hack that we need to make .md-ellipsis work in
// Safari, we need to set `z-index` here as - see https://bit.ly/3s5M2jm
z-index: 1;
background: var(--md-default-bg-color);
box-shadow: 0 0 px2rem(8px) px2rem(8px) var(--md-default-bg-color);
// Adjust snapping behavior
&[for="__drawer"] {
scroll-snap-align: start;
}
// Hide navigation icon
.md-nav__icon {
display: none;
}
} }
// Hide navigation icon // Adjust spacing for navigation list - same reason as below
.md-nav__icon { .md-nav__list {
display: none; padding-inline-start: px2rem(12px);
padding-bottom: px2rem(8px);
}
// Adjust spacing for navigation link - before this change, we set spacing
// on the left and right of a navigation item, but this led to the problem
// of cropped focus outlines, because we must set `overflow: hidden` on
// the navigation list for smooth expand and collapse transitions.
.md-nav__item > .md-nav__link {
margin-inline-end: px2rem(8px);
} }
} }
// Hide toggle for nested navigation // Hide nested navigation
&__toggle ~ & { &__toggle ~ & {
display: none; display: grid;
grid-template-rows: 0fr;
visibility: collapse;
opacity: 0;
transition:
grid-template-rows 250ms cubic-bezier(0.86, 0, 0.07, 1),
opacity 250ms,
visibility 0ms 250ms;
// Navigation list
> .md-nav__list {
overflow: hidden;
}
} }
// Show nested navigation when toggle is active or indeterminate // Show nested navigation when toggle is active or indeterminate
&__toggle:is(:checked, :indeterminate) ~ & { &__toggle:is(:checked, :indeterminate) ~ & {
display: block; grid-template-rows: 1fr;
visibility: visible;
opacity: 1;
transition:
grid-template-rows 250ms cubic-bezier(0.86, 0, 0.07, 1),
opacity 150ms 100ms,
visibility 0ms;
} }
// Hide navigation title in nested navigation // Hide navigation title in nested navigation
@@ -562,8 +606,9 @@
pointer-events: none; pointer-events: none;
} }
// Hide naviation icon // Hide navigation icon
.md-nav__icon { > [for],
.md-icon {
display: none; display: none;
} }
} }
@@ -571,6 +616,9 @@
// Navigation // Navigation
> .md-nav { > .md-nav {
display: block; display: block;
margin-inline-start: px2rem(-12px);
visibility: visible;
opacity: 1;
// Adjust spacing on next level item // Adjust spacing on next level item
> .md-nav__list > .md-nav__item { > .md-nav__list > .md-nav__item {
@@ -622,8 +670,7 @@
// Modifier for when navigation tabs are rendered // Modifier for when navigation tabs are rendered
&--lifted { &--lifted {
// Hide nested level 0 navigation items and site title // Hide site title
> .md-nav__list > .md-nav__item--nested,
> .md-nav__title { > .md-nav__title {
display: none; display: none;
} }
@@ -635,16 +682,13 @@
// Active parent navigation item // Active parent navigation item
&--active { &--active {
display: block; display: block;
padding: 0;
// Show navigation link as title // Show navigation link as title
> .md-nav__link { > .md-nav__link {
position: sticky; position: sticky;
top: 0; top: 0;
z-index: 1; z-index: 1;
padding: 0 px2rem(12px);
margin-top: 0; margin-top: 0;
font-weight: 700;
background: var(--md-default-bg-color); background: var(--md-default-bg-color);
box-shadow: 0 0 px2rem(8px) px2rem(8px) var(--md-default-bg-color); box-shadow: 0 0 px2rem(8px) px2rem(8px) var(--md-default-bg-color);
@@ -652,12 +696,17 @@
&:not(.md-nav__container) { &:not(.md-nav__container) {
pointer-events: none; pointer-events: none;
} }
// Hide naviation icon
.md-nav__icon {
display: none;
}
} }
// Adjust spacing for navigation section
&.md-nav__item--section {
margin: 0;
}
}
// Adjust spacing for nested navigation
> .md-nav {
margin-inline-start: px2rem(-12px);
} }
// Make labels discernable from links // Make labels discernable from links
@@ -667,14 +716,11 @@
} }
// Hack: Always show active navigation tab on breakpoint screen, despite // Hack: Always show active navigation tab on breakpoint screen, despite
// of checkbox being checked or not. Fixes #1655. // of checkbox being checked or not - see https://t.ly/Qc311
.md-nav[data-md-level="1"] { .md-nav[data-md-level="1"] {
display: block; grid-template-rows: 1fr;
visibility: visible;
// Adjust spacing for level 1 navigation items opacity: 1;
> .md-nav__list > .md-nav__item {
padding-inline-end: px2rem(12px);
}
} }
} }
@@ -695,7 +741,15 @@
.md-nav--secondary { .md-nav--secondary {
display: block; display: block;
margin-bottom: 1.25em; margin-bottom: 1.25em;
visibility: visible;
border-inline-start: px2rem(1px) solid var(--md-primary-fg-color); border-inline-start: px2rem(1px) solid var(--md-primary-fg-color);
opacity: 1;
// Navigation list
> .md-nav__list {
padding-bottom: 0;
overflow: visible;
}
// Hide table of contents title // Hide table of contents title
> .md-nav__title { > .md-nav__title {

View File

@@ -53,7 +53,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: px2rem(12px); gap: px2rem(12px);
margin: 0 px2rem(12px); margin: 0 px2rem(12px) px2rem(24px);
} }
// Post metadata // Post metadata
@@ -70,6 +70,12 @@
} }
} }
// Post navigation title @todo - generalize
&__title {
font-weight: 700;
color: var(--md-default-fg-color--light);
}
// Post excerpt // Post excerpt
&--excerpt { &--excerpt {
margin-bottom: px2rem(64px); margin-bottom: px2rem(64px);
@@ -105,8 +111,7 @@
} }
} }
// Adjust spacing for navigation // Add margin to table of contents
> .md-nav:first-child > .md-nav__list,
> .md-nav--secondary { > .md-nav--secondary {
margin: 1em 0; margin: 1em 0;
} }

View File

@@ -36,7 +36,6 @@
// Status // Status
.md-status { .md-status {
margin-left: px2rem(4px);
// Status icon // Status icon
&::after { &::after {

View File

@@ -36,7 +36,7 @@
> >
<div class="md-sidebar__scrollwrap"> <div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner md-post"> <div class="md-sidebar__inner md-post">
<nav class="md-nav"> <nav class="md-nav md-nav--primary">
<!-- Back to overview link --> <!-- Back to overview link -->
<div class="md-post__back"> <div class="md-post__back">
@@ -50,7 +50,7 @@
</div> </div>
</div> </div>
<!-- Page authors --> <!-- Post authors -->
{% if page.authors %} {% if page.authors %}
<div class="md-post__authors md-typeset"> <div class="md-post__authors md-typeset">
{% for author in page.authors %} {% for author in page.authors %}
@@ -67,72 +67,82 @@
</div> </div>
{% endif %} {% endif %}
<!-- Page metadata --> <!-- Post metadata -->
<ul class="md-post__meta md-nav__list"> <ul class="md-post__meta md-nav__list">
<li class="md-nav__item md-nav__title"> <li class="md-nav__item md-nav__item--section">
<div class="md-nav__link"> <div class="md-post__title">
<span class="md-ellipsis"> <span class="md-ellipsis">
{{ lang.t("blog.meta") }} {{ lang.t("blog.meta") }}
</span> </span>
</div> </div>
<nav class="md-nav">
<ul class="md-nav__list">
<!-- Post date -->
<li class="md-nav__item">
<div class="md-nav__link">
{% include ".icons/material/calendar.svg" %}
<time
datetime="{{ page.config.date.created }}"
class="md-ellipsis"
>
{{- page.config.date.created | date -}}
</time>
</div>
</li>
<!-- Post date updated -->
{% if page.config.date.updated %}
<li class="md-nav__item">
<div class="md-nav__link">
{% include ".icons/material/calendar-clock.svg" %}
<time
datetime="{{ page.config.date.updated }}"
class="md-ellipsis"
>
{{- page.config.date.updated | date -}}
</time>
</div>
</li>
{% endif %}
<!-- Post categories -->
{% if page.categories %}
<li class="md-nav__item">
<div class="md-nav__link">
{% include ".icons/material/bookshelf.svg" %}
<span class="md-ellipsis">
{{ lang.t("blog.categories.in") }}
{% for category in page.categories %}
<a href="{{ category.url | url }}">
{{- category.title -}}
</a>
{%- if loop.revindex > 1 %}, {% endif -%}
{% endfor -%}
</span>
</div>
</li>
{% endif %}
<!-- Post readtime -->
{% if page.config.readtime %}
{% set time = page.config.readtime %}
<li class="md-nav__item">
<div class="md-nav__link">
{% include ".icons/material/clock-outline.svg" %}
<span class="md-ellipsis">
{% if time == 1 %}
{{ lang.t("readtime.one") }}
{% else %}
{{ lang.t("readtime.other") | replace("#", time) }}
{% endif %}
</span>
</div>
</li>
{% endif %}
</ul>
</nav>
</li> </li>
<!-- Page date -->
<li class="md-nav__item">
<div class="md-nav__link">
{% include ".icons/material/calendar.svg" %}
<time datetime="{{ page.config.date.created }}" class="md-ellipsis">
{{- page.config.date.created | date -}}
</time>
</div>
</li>
<!-- Page date updated -->
{% if page.config.date.updated %}
<li class="md-nav__item">
<div class="md-nav__link">
{% include ".icons/material/calendar-clock.svg" %}
<time datetime="{{ page.config.date.updated }}" class="md-ellipsis">
{{- page.config.date.updated | date -}}
</time>
</div>
</li>
{% endif %}
<!-- Page categories -->
{% if page.categories %}
<li class="md-nav__item">
<div class="md-nav__link">
{% include ".icons/material/bookshelf.svg" %}
<span class="md-ellipsis">
{{ lang.t("blog.categories.in") }}
{% for category in page.categories %}
<a href="{{ category.url | url }}">
{{- category.title -}}
</a>
{%- if loop.revindex > 1 %}, {% endif -%}
{% endfor -%}
</span>
</div>
</li>
{% endif %}
<!-- Page readtime -->
{% if page.config.readtime %}
{% set time = page.config.readtime %}
<li class="md-nav__item">
<div class="md-nav__link">
{% include ".icons/material/clock-outline.svg" %}
<span class="md-ellipsis">
{% if time == 1 %}
{{ lang.t("readtime.one") }}
{% else %}
{{ lang.t("readtime.other") | replace("#", time) }}
{% endif %}
</span>
</div>
</li>
{% endif %}
</ul> </ul>
</nav> </nav>

View File

@@ -67,6 +67,7 @@
"select.language": "Izberi jezik", "select.language": "Izberi jezik",
"select.version": "Izberi različico", "select.version": "Izberi različico",
"source": "Pojdi na repozitorij", "source": "Pojdi na repozitorij",
"source.file.contributors": "Soavtorji",
"source.file.date.created": "Ustvarjeno", "source.file.date.created": "Ustvarjeno",
"source.file.date.updated": "Zadnja posodobitev", "source.file.date.updated": "Zadnja posodobitev",
"tabs": "Zavihki", "tabs": "Zavihki",

View File

@@ -101,26 +101,26 @@
{% endif %} {% endif %}
<!-- Determine whether to render item as a section --> <!-- Determine whether to render item as a section -->
{% if "navigation.sections" in features and level == 1 + ( {% set tabs = "navigation.tabs" in features %}
"navigation.tabs" in features {% set sections = "navigation.sections" in features %}
) %} {% if tabs and level == 1 or sections and tabs >= level - 1 %}
{% set class = class ~ " md-nav__item--section" %} {% set class = class ~ " md-nav__item--section" %}
{% set is_section = true %}
<!-- Determine whether to prune inactive item --> <!-- Determine whether to prune inactive item -->
{% elif not nav_item.active and "navigation.prune" in features %} {% elif not nav_item.active and "navigation.prune" in features %}
{% set class = class ~ " md-nav__item--pruned" %} {% set class = class ~ " md-nav__item--pruned" %}
{% set prune = true %} {% set is_pruned = true %}
{% endif %} {% endif %}
<!-- Nested navigation item --> <!-- Nested navigation item -->
<li class="{{ class }} md-nav__item--nested"> <li class="{{ class }} md-nav__item--nested">
{% if not prune %} {% if not is_pruned %}
{% set expanded = "navigation.expand" in features %} {% set checked = "checked" if nav_item.active %}
{% set active = nav_item.active or expanded %}
<!-- Determine checked and indeterminate state --> <!-- Determine checked and indeterminate state -->
{% set checked = "checked" if nav_item.active %} {% set is_expanded = "navigation.expand" in features %}
{% if expanded and not checked %} {% if is_expanded and not checked %}
{% set indeterminate = "md-toggle--indeterminate" %} {% set indeterminate = "md-toggle--indeterminate" %}
{% endif %} {% endif %}
@@ -134,11 +134,12 @@
<!-- Toggle to expand nested items --> <!-- Toggle to expand nested items -->
{% if not indexes %} {% if not indexes %}
{% set tabindex = "0" if not is_section %}
<label <label
class="md-nav__link" class="md-nav__link"
for="{{ path }}" for="{{ path }}"
id="{{ path }}_label" id="{{ path }}_label"
tabindex="0" tabindex="{{ tabindex }}"
> >
{{ render_content(nav_item) }} {{ render_content(nav_item) }}
<span class="md-nav__icon md-icon"></span> <span class="md-nav__icon md-icon"></span>
@@ -158,7 +159,13 @@
<!-- Only render toggle if there's at least one more page --> <!-- Only render toggle if there's at least one more page -->
{% if nav_item.children | length > 1 %} {% if nav_item.children | length > 1 %}
<label class="md-nav__link {{ class }}" for="{{ path }}"> {% set tabindex = "0" if not is_section %}
<label
class="md-nav__link {{ class }}"
for="{{ path }}"
id="{{ path }}_label"
tabindex="{{ tabindex }}"
>
<span class="md-nav__icon md-icon"></span> <span class="md-nav__icon md-icon"></span>
</label> </label>
{% endif %} {% endif %}