├── .node-version
├── src
├── scss
│ ├── section
│ │ ├── _index.scss
│ │ ├── _footer.scss
│ │ └── _header.scss
│ ├── config
│ │ ├── _styles.scss
│ │ ├── _index.scss
│ │ ├── _dark-mode.scss
│ │ ├── _font.scss
│ │ ├── _config.scss
│ │ └── _dark-colors.scss
│ ├── component
│ │ ├── _group.scss
│ │ ├── _accordion-list.scss
│ │ ├── _skip-link.scss
│ │ ├── _open-search.scss
│ │ ├── _toc.scss
│ │ ├── _index.scss
│ │ ├── _theme-switcher.scss
│ │ ├── _modal.scss
│ │ ├── _changelog-item.scss
│ │ ├── _sidebar.scss
│ │ ├── _post-card.scss
│ │ ├── _post-heading.scss
│ │ ├── _breadcrumb-list.scss
│ │ ├── _post-content.scss
│ │ ├── _pagefind.scss
│ │ ├── _sidebar-section.scss
│ │ ├── _post-navigation.scss
│ │ ├── _accordion-card.scss
│ │ └── _prism.scss
│ ├── layout
│ │ ├── _index.scss
│ │ ├── _front-page.scss
│ │ ├── _card.scss
│ │ ├── _list.scss
│ │ ├── _container.scss
│ │ ├── _post.scss
│ │ └── _main.scss
│ └── main.scss
├── posts
│ ├── posts.json
│ ├── customization
│ │ ├── customization.md
│ │ └── posts
│ │ │ ├── posts.json
│ │ │ ├── themes.md
│ │ │ └── spruce-css.md
│ └── getting-started
│ │ ├── getting-started.md
│ │ └── posts
│ │ ├── posts.json
│ │ ├── setup.md
│ │ ├── content-management.md
│ │ ├── introduction.md
│ │ ├── features.md
│ │ └── structure.md
├── img
│ ├── cover
│ │ ├── sprucecss.png
│ │ └── sprucecss-light.png
│ ├── icon
│ │ ├── system-mode.svg
│ │ ├── arrow-outward.svg
│ │ ├── arrow-forward.svg
│ │ ├── arrow-back.svg
│ │ ├── menu.svg
│ │ ├── search.svg
│ │ ├── dark-mode.svg
│ │ ├── twitter.svg
│ │ ├── github.svg
│ │ ├── discord.svg
│ │ └── light-mode.svg
│ ├── favicon.svg
│ └── cone-docs-logo.svg
├── filters
│ ├── w3-date-filter.js
│ ├── date-filter.js
│ └── parent-filter.js
├── font
│ ├── manrope-v14-latin-600.woff2
│ ├── manrope-v14-latin-800.woff2
│ ├── manrope-v14-latin-regular.woff2
│ ├── open-sans-v35-latin-700.woff2
│ └── open-sans-v35-latin-regular.woff2
├── shortcodes
│ ├── markdown-render.js
│ └── svg-icon.js
├── _data
│ ├── global.js
│ ├── site.json
│ ├── helpers.js
│ └── navigation.json
├── _includes
│ ├── partials
│ │ ├── search-modal.html
│ │ ├── search-toggle.html
│ │ ├── breadcrumbs.html
│ │ ├── preload.html
│ │ ├── theme-switcher.html
│ │ ├── post-heading.html
│ │ ├── post-navigation.html
│ │ ├── footer.html
│ │ ├── meta.html
│ │ ├── sidebar.html
│ │ └── header.html
│ └── layouts
│ │ ├── post.html
│ │ ├── list.html
│ │ ├── changelog.html
│ │ ├── faq.html
│ │ ├── base.html
│ │ └── front-page.html
├── transforms
│ └── html-min-transform.js
├── js
│ ├── theme-detection.js
│ ├── theme-change-assets.js
│ ├── navigation.js
│ ├── theme-switcher.js
│ ├── modal.js
│ └── accordion-card.js
├── changelog.md
├── index.md
├── faq.md
└── css
│ └── main.css
├── .github
├── sprucecss-cover.png
├── spruce-docs-preview-mockup-2.png
├── workflows
│ └── test.yml
├── spruce-logo-dark.svg
└── spruce-logo-light.svg
├── .editorconfig
├── .eslintrc.json
├── .stylelintrc.json
├── LICENSE
├── package.json
├── .eleventy.js
├── .gitignore
├── CODE_OF_CONDUCT.md
└── README.md
/.node-version:
--------------------------------------------------------------------------------
1 | 18
2 |
--------------------------------------------------------------------------------
/src/scss/section/_index.scss:
--------------------------------------------------------------------------------
1 | @forward 'header';
2 | @forward 'footer';
3 |
--------------------------------------------------------------------------------
/src/scss/config/_styles.scss:
--------------------------------------------------------------------------------
1 | @use 'sprucecss/scss/spruce' as *;
2 |
3 | @include generate-styles;
4 |
--------------------------------------------------------------------------------
/src/posts/posts.json:
--------------------------------------------------------------------------------
1 | {
2 | "layout": "layouts/list.html",
3 | "permalink": "/{{ title | slug }}/index.html"
4 | }
5 |
--------------------------------------------------------------------------------
/src/scss/component/_group.scss:
--------------------------------------------------------------------------------
1 | @use 'sprucecss/scss/spruce' as *;
2 |
3 | .group {
4 | @include layout-stack;
5 | }
6 |
--------------------------------------------------------------------------------
/.github/sprucecss-cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conedevelopment/sprucecss-eleventy-documentation-template/HEAD/.github/sprucecss-cover.png
--------------------------------------------------------------------------------
/src/img/cover/sprucecss.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conedevelopment/sprucecss-eleventy-documentation-template/HEAD/src/img/cover/sprucecss.png
--------------------------------------------------------------------------------
/src/scss/component/_accordion-list.scss:
--------------------------------------------------------------------------------
1 | @use 'sprucecss/scss/spruce' as *;
2 |
3 | .accordion-list {
4 | @include layout-stack('s');
5 | }
6 |
--------------------------------------------------------------------------------
/src/scss/config/_index.scss:
--------------------------------------------------------------------------------
1 | @forward 'config';
2 | @forward 'font';
3 | @forward 'styles';
4 | @forward 'dark-colors';
5 | @forward 'dark-mode';
6 |
--------------------------------------------------------------------------------
/src/filters/w3-date-filter.js:
--------------------------------------------------------------------------------
1 | module.exports = (value) => {
2 | const dateObject = new Date(value);
3 | return dateObject.toISOString();
4 | };
5 |
--------------------------------------------------------------------------------
/src/posts/customization/customization.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Customization"
3 | eleventyNavigation:
4 | key: Customization
5 | order: 2
6 | ---
7 |
--------------------------------------------------------------------------------
/src/posts/customization/posts/posts.json:
--------------------------------------------------------------------------------
1 | {
2 | "layout": "layouts/post.html",
3 | "permalink": "/customization/{{ title | slug }}/index.html"
4 | }
5 |
--------------------------------------------------------------------------------
/src/posts/getting-started/getting-started.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Getting Started"
3 | eleventyNavigation:
4 | key: Getting Started
5 | order: 1
6 | ---
7 |
--------------------------------------------------------------------------------
/src/img/cover/sprucecss-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conedevelopment/sprucecss-eleventy-documentation-template/HEAD/src/img/cover/sprucecss-light.png
--------------------------------------------------------------------------------
/src/posts/getting-started/posts/posts.json:
--------------------------------------------------------------------------------
1 | {
2 | "layout": "layouts/post.html",
3 | "permalink": "/getting-started/{{ title | slug }}/index.html"
4 | }
5 |
--------------------------------------------------------------------------------
/src/font/manrope-v14-latin-600.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conedevelopment/sprucecss-eleventy-documentation-template/HEAD/src/font/manrope-v14-latin-600.woff2
--------------------------------------------------------------------------------
/src/font/manrope-v14-latin-800.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conedevelopment/sprucecss-eleventy-documentation-template/HEAD/src/font/manrope-v14-latin-800.woff2
--------------------------------------------------------------------------------
/src/scss/layout/_index.scss:
--------------------------------------------------------------------------------
1 | @forward 'container';
2 | @forward 'main';
3 | @forward 'card';
4 | @forward 'front-page';
5 | @forward 'post';
6 | @forward 'list';
7 |
--------------------------------------------------------------------------------
/.github/spruce-docs-preview-mockup-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conedevelopment/sprucecss-eleventy-documentation-template/HEAD/.github/spruce-docs-preview-mockup-2.png
--------------------------------------------------------------------------------
/src/font/manrope-v14-latin-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conedevelopment/sprucecss-eleventy-documentation-template/HEAD/src/font/manrope-v14-latin-regular.woff2
--------------------------------------------------------------------------------
/src/font/open-sans-v35-latin-700.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conedevelopment/sprucecss-eleventy-documentation-template/HEAD/src/font/open-sans-v35-latin-700.woff2
--------------------------------------------------------------------------------
/src/font/open-sans-v35-latin-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/conedevelopment/sprucecss-eleventy-documentation-template/HEAD/src/font/open-sans-v35-latin-regular.woff2
--------------------------------------------------------------------------------
/src/scss/layout/_front-page.scss:
--------------------------------------------------------------------------------
1 | @use 'sprucecss/scss/spruce' as *;
2 |
3 | .l-front-page {
4 | display: grid;
5 | gap: spacer('xl');
6 | grid-template-columns: minmax(0, 1fr);
7 | }
8 |
--------------------------------------------------------------------------------
/src/filters/date-filter.js:
--------------------------------------------------------------------------------
1 | const moment = require('moment');
2 |
3 | module.exports = (value, locale) => {
4 | moment.locale(locale);
5 | const dateObject = moment(value);
6 | return dateObject.format('MMMM DD, YYYY');
7 | };
8 |
--------------------------------------------------------------------------------
/src/shortcodes/markdown-render.js:
--------------------------------------------------------------------------------
1 | const markdownIt = require('markdown-it'); // eslint-disable-line
2 |
3 | module.exports = async (children) => {
4 | const md = new markdownIt(); // eslint-disable-line
5 | return md.render(children);
6 | };
7 |
--------------------------------------------------------------------------------
/src/_data/global.js:
--------------------------------------------------------------------------------
1 | const crypto = require('crypto');
2 |
3 | module.exports = {
4 | random() {
5 | return crypto.randomUUID();
6 | },
7 | year() {
8 | const date = new Date();
9 | return date.getFullYear();
10 | },
11 | };
12 |
--------------------------------------------------------------------------------
/src/scss/component/_skip-link.scss:
--------------------------------------------------------------------------------
1 | @use 'sprucecss/scss/spruce' as *;
2 |
3 | .skip-link {
4 | inset: -50vh auto auto spacer('m');
5 | position: fixed;
6 | z-index: 100;
7 |
8 | &:focus {
9 | inset-block-start: spacer('m');
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/filters/parent-filter.js:
--------------------------------------------------------------------------------
1 | module.exports = (collection, parent) => {
2 | if (!parent) return collection;
3 | const filtered = collection.filter((item) => item.data.eleventyNavigation?.parent === parent);
4 | return filtered.sort((a, b) => a.data.eleventyNavigation.order - b.data.eleventyNavigation.order);
5 | };
6 |
--------------------------------------------------------------------------------
/src/_includes/partials/search-modal.html:
--------------------------------------------------------------------------------
1 |
4 | {% set breadcrumbs = collections.all | eleventyNavigationBreadcrumb(title, { includeSelf: true }) %}
5 | {% include "partials/post-heading.html" %}
6 |
7 | {% set posts = collections.all | parentFilter(eleventyNavigation.key) %}
8 | {% for post in posts %}
9 |
10 |
11 |
{{ post.data.title }}
12 |
{{ post.data.summary }}
13 |
14 | Read More
15 | {{ post.title }}
16 |
17 |
18 | {% endfor %}
19 |
20 |
21 | {% endblock %}
22 |
--------------------------------------------------------------------------------
/src/_includes/partials/post-heading.html:
--------------------------------------------------------------------------------
1 |
2 | {% if headline %}
3 |
{{ headline }}
4 | {% endif %}
5 | {% if breadcrumbs %}
6 | {% include "partials/breadcrumbs.html" %}
7 | {% endif %}
8 | {% if title %}
9 |
10 | {{ title }}
11 |
12 | {% endif %}
13 | {% if summary and displaySummary %}
14 |
15 | {{ summary }}
16 |
17 | {% endif %}
18 | {% if btns %}
19 |
20 | {% for btn in btns %}
21 | {% set cls = 'btn--primary btn--primary-shadow' %}
22 | {% if btn.type === 'outline' %}
23 | {% set cls = 'btn--outline-primary' %}
24 | {% endif %}
25 |
{{ btn.caption }}
26 | {% endfor %}
27 |
28 | {% endif %}
29 |
30 |
--------------------------------------------------------------------------------
/src/js/theme-switcher.js:
--------------------------------------------------------------------------------
1 | (() => {
2 | const themeSwitcher = document.querySelector('#theme-switcher');
3 | const preferredTheme = localStorage.getItem('preferred-theme') ?? 'system';
4 |
5 | if (!themeSwitcher) {
6 | return;
7 | }
8 |
9 | themeSwitcher.addEventListener('click', (e) => {
10 | if (!e.target.matches('[data-action]')) {
11 | return;
12 | }
13 |
14 | const theme = e.target.getAttribute('data-action');
15 | const systemMode = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
16 | document.documentElement.classList.add('no-transition');
17 |
18 | localStorage.setItem('preferred-theme', theme);
19 | document.documentElement.setAttribute('data-theme-mode', theme === 'system' ? systemMode : theme);
20 | themeSwitcher.setAttribute('data-theme-mode', theme);
21 | themeSwitcher.querySelector(`.theme-switcher__${theme}-mode`).focus();
22 | document.documentElement.classList.remove('no-transition');
23 | });
24 |
25 | themeSwitcher.setAttribute('data-theme-mode', preferredTheme);
26 | })();
27 |
--------------------------------------------------------------------------------
/src/scss/component/_breadcrumb-list.scss:
--------------------------------------------------------------------------------
1 | @use 'sprucecss/scss/spruce' as *;
2 |
3 | .breadcrumb-list {
4 | @include clear-list;
5 | align-items: center;
6 | display: flex;
7 | max-inline-size: 100%;
8 | overflow-x: auto;
9 | white-space: nowrap;
10 |
11 | > li {
12 | align-items: center;
13 | display: inline-flex;
14 | margin-block: 0;
15 |
16 | + li::before {
17 | block-size: 0.4em;
18 | border-block-end: 2px solid color('separator', 'breadcrumb');
19 | border-inline-end: 2px solid color('separator', 'breadcrumb');
20 | content: '';
21 | display: inline-flex;
22 | inline-size: 0.4em;
23 | margin-inline: 0.75em;
24 | transform: rotate(-45deg);
25 |
26 | @at-root {
27 | [dir='rtl'] & {
28 | transform: rotate(45deg);
29 | }
30 | }
31 | }
32 | }
33 |
34 | a {
35 | text-decoration: none;
36 | }
37 |
38 | [aria-current='page'] {
39 | @include text-ellipsis(1);
40 | display: inline-block;
41 | max-inline-size: 20ch;
42 | text-align: start;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/img/icon/twitter.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Cone Development
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/_includes/layouts/changelog.html:
--------------------------------------------------------------------------------
1 | {% extends "layouts/base.html" %}
2 | {% block content %}
3 |
4 | {% set breadcrumbs = [{
5 | "title": title,
6 | "url": page.url
7 | }] %}
8 | {% include "partials/post-heading.html" %}
9 |
10 | {% for change in changelog %}
11 |
12 |
13 |
14 |
{{ change.title }}
15 |
16 | {% for row in change.items %}
17 | - {% markdownRender row %}
18 | {% endfor %}
19 |
20 |
21 |
22 | {% endfor %}
23 |
24 |
25 | {% endblock %}
26 |
--------------------------------------------------------------------------------
/src/posts/getting-started/posts/setup.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Setup"
3 | summary: "We use eleventy --serve and compile Sass with sass-cli with npm scripts."
4 | eleventyNavigation:
5 | key: Setup
6 | parent: Getting Started
7 | order: 4
8 | ---
9 |
10 | ## Clone the repository
11 |
12 | Today more people and experts write about accessibility. For the better progression it is a good idea to read them.
13 |
14 | ## Install the dependencies
15 |
16 | In the `package.json` file, you will find all of the dependencies (and scripts) to install them using the following command:
17 |
18 | ```shell
19 | npm install
20 | ```
21 |
22 | ## Run the development mode
23 |
24 | To run the development mode, use the `npm script`. This script will also watch for changes.
25 |
26 | ```shell
27 | npm start
28 | ```
29 |
30 | ## Run the production mode
31 |
32 | Before you go live, you should use the production script to compress the Sass files.
33 |
34 | ```shell
35 | npm run prod
36 | ```
37 |
38 | ## Additional Scripts
39 |
40 | You can find some more npm scripts in the [package.json](https://github.com/conedevelopment/sprucecss-eleventy-documentation-template/blob/main/package.json) that can be helpful.
41 |
--------------------------------------------------------------------------------
/src/scss/component/_post-content.scss:
--------------------------------------------------------------------------------
1 | @use 'sprucecss/scss/spruce' as *;
2 |
3 | .post-content {
4 | @include layout-stack;
5 |
6 | @include breakpoint('md') {
7 | font-size: 1.0375rem;
8 | }
9 |
10 | :is(dd, dl, dl, h1, h2, h3:not(.accordion-card__title), h4, h5, h6, hr, ul, ol, p, blockquote) {
11 | max-inline-size: 40rem;
12 | }
13 |
14 | * + h2,
15 | * + h3 {
16 | margin-top: spacer('l');
17 | }
18 |
19 | h2 + *,
20 | h3 + *,
21 | h4 + *,
22 | h5 + *,
23 | h6 + * {
24 | margin-top: spacer('s');
25 | }
26 |
27 | h2[id] {
28 | align-items: flex-start;
29 | display: flex;
30 | justify-content: space-between;
31 |
32 | &:hover .anchor,
33 | &:focus-within .anchor {
34 | opacity: 1;
35 | }
36 |
37 | .anchor {
38 | font-weight: 600;
39 | opacity: 0;
40 | text-decoration: none;
41 | }
42 | }
43 |
44 | img,
45 | iframe {
46 | border-radius: config('border-radius-sm', $display);
47 | }
48 |
49 | iframe {
50 | aspect-ratio: 16 / 9;
51 | }
52 |
53 | a > code {
54 | color: color('link');
55 | }
56 |
57 | strong {
58 | color: color('heading');
59 | }
60 |
61 | picture {
62 | display: flex;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/_includes/layouts/faq.html:
--------------------------------------------------------------------------------
1 | {% extends "layouts/base.html" %}
2 | {% block content %}
3 |
4 | {% set breadcrumbs = [{
5 | "title": title,
6 | "url": page.url
7 | }] %}
8 | {% include "partials/post-heading.html" %}
9 |
10 | {% for faq in faqs %}
11 |
12 | {% if faq.title %}
13 |
{{ faq.title }}
14 | {% endif %}
15 |
16 | {% for post in faq.items %}
17 |
18 |
{{ post.title }}
19 |
20 | {% markdownRender post.description %}
21 |
22 |
23 | {% endfor %}
24 |
25 |
26 | {% endfor %}
27 |
28 |
29 | {% endblock %}
30 |
--------------------------------------------------------------------------------
/src/scss/layout/_main.scss:
--------------------------------------------------------------------------------
1 | @use 'sprucecss/scss/spruce' as *;
2 |
3 | .l-main {
4 | --gtc: minmax(0, 1fr);
5 |
6 | display: grid;
7 | gap: spacer('l');
8 | grid-template-columns: var(--gtc);
9 | margin-block: spacer-clamp('m', 'l') spacer-clamp('l', 'xl');
10 |
11 | @include breakpoint('sm') {
12 | --gtc: minmax(0, 16rem) minmax(0, 1fr);
13 | gap: calc(#{get-css-variable(--container-gap)} * 1.5);
14 | }
15 |
16 | @include breakpoint('md') {
17 | --gtc: minmax(0, 18.5rem) minmax(0, 1fr);
18 | }
19 |
20 | @include breakpoint('lg') {
21 | --gtc: minmax(0, 20rem) minmax(0, 1fr);
22 | }
23 |
24 | &__sidebar {
25 | display: none !important;
26 |
27 | @include breakpoint('sm') {
28 | border-inline-end: 1px solid color('border');
29 | display: flex !important;
30 | inset-block-start: spacer('l');
31 | max-block-size: calc(100vh - #{spacer('l')} * 2);
32 | position: sticky;
33 | }
34 |
35 | &--open {
36 | display: flex !important;
37 | }
38 | }
39 |
40 | &__body {
41 | display: flex;
42 | flex-direction: column;
43 | inline-size: 100%;
44 | margin-inline: auto;
45 | max-inline-size: get-css-variable(--max-content-inline-size);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/_includes/partials/post-navigation.html:
--------------------------------------------------------------------------------
1 | {% set previousPost = collections.posts | getPreviousCollectionItem %}
2 | {% set nextPost = collections.posts | getNextCollectionItem %}
3 |
4 |
28 |
--------------------------------------------------------------------------------
/src/scss/component/_pagefind.scss:
--------------------------------------------------------------------------------
1 | @use 'sprucecss/scss/spruce' as *;
2 |
3 | @include generate-form-control('.pagefind-ui__search-input', false, false, false);
4 |
5 | .pagefind-ui {
6 | position: relative;
7 |
8 | &__search-input {
9 | padding: config('padding', $form-control, false) !important;
10 | }
11 |
12 | &__form {
13 | @include layout-stack('s');
14 | }
15 |
16 | &__search-clear {
17 | background: none;
18 | border: 0;
19 | font-size: config('font-size-sm', $typography);
20 | inset-block-start: 0.8em;
21 | inset-inline-end: 0.5em;
22 | margin-block-start: 0;
23 | position: absolute;
24 | text-transform: uppercase;
25 | }
26 |
27 | &__drawer {
28 | @include scrollbar;
29 | max-block-size: 20rem;
30 | overflow-y: auto;
31 | }
32 |
33 | &__results {
34 | @include clear-list;
35 | @include layout-stack('s');
36 | padding-inline-end: spacer('m');
37 |
38 | &:empty {
39 | display: none;
40 | }
41 | }
42 |
43 | &__results-area {
44 | @include layout-stack('xs');
45 | }
46 |
47 | &__result-inner {
48 | @include layout-stack('xxs');
49 | }
50 |
51 | &__result-title {
52 | font-weight: 700;
53 | }
54 |
55 | &__hidden {
56 | display: none;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/posts/getting-started/posts/content-management.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Content Management"
3 | summary: "Today more people and experts write about accessibility. For the better progression it is a good idea to read them."
4 | eleventyNavigation:
5 | key: Content Management
6 | parent: Getting Started
7 | order: 5
8 | ---
9 |
10 | Adding content to the template is easy as almost everything is in Eleventy.
11 |
12 | ## The Basic Structure
13 |
14 | Our base folder for the documentation pages is the `posts` folder. You must follow the folder structure, which means the `category` here. If you create a folder, you must make a list page with the same name as the folder. You must also create another `posts` folder under the `category` folder where your posts go. You must create the `posts.json` file that will parameter your `layout` and `permalink` values.
15 |
16 | ## Eleventy Navigation
17 |
18 | The theme utilizes the [Eleventy Navigation plugin](https://www.11ty.dev/docs/plugins/navigation/), so you must explicitly set up the hierarchy. This is needed for the automatic sidebar navigation, the navigation order, and breadcrumb generation.
19 |
20 | ## Other Pages
21 |
22 | To create simple pages, make a file directly under the `src` folder and configure it with the available front matter.
23 |
--------------------------------------------------------------------------------
/src/posts/getting-started/posts/introduction.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Introduction"
3 | summary: "Welcome to the official documentation of Spruce Docs Elventy theme. A small template that you can use to document any of your projects."
4 | eleventyNavigation:
5 | key: Introduction
6 | parent: Getting Started
7 | order: 1
8 | ---
9 |
10 | Welcome to the official documentation of **Spruce Docs** Elventy theme. A small template that you can use to document any of your projects.
11 |
12 | ## About the Template
13 |
14 | A documentation template is always helpful. There are a lot of solutions to make one; we wanted to create our self-hosted version based on our favorite static site generator.
15 |
16 | By structure, it is simple, with two levels and additional custom templates like [FAQ](/faq/) and [Changelog](/changelog/).
17 |
18 | ## Spruce CSS
19 |
20 | The template is built on [Spruce CSS](https://sprucecss.com/), a small and customizable CSS framework. The main benefit of this is that you can use the Spruce UI components with dark mode and RTL support.
21 |
22 |

29 |
--------------------------------------------------------------------------------
/src/js/modal.js:
--------------------------------------------------------------------------------
1 | (() => {
2 | let activeElement = null;
3 | const siteWrapper = document.querySelector('.site-wrapper');
4 | const button = document.querySelector('[data-action="open-search"]');
5 | const input = document.querySelector('.pagefind-ui__search-input');
6 | const modal = document.querySelector('.modal-backdrop');
7 |
8 | if (!button || !modal) return;
9 |
10 | function openModal() {
11 | activeElement = document.activeElement;
12 | siteWrapper.setAttribute('inert', '');
13 | modal.classList.add('modal-backdrop--open');
14 | input.focus();
15 | }
16 |
17 | function closeModal() {
18 | siteWrapper.removeAttribute('inert');
19 | modal.classList.remove('modal-backdrop--open');
20 | activeElement.focus();
21 | }
22 |
23 | function handleKeyDown(e) {
24 | if (e.code === 'Escape') {
25 | closeModal();
26 | }
27 |
28 | if (e.ctrlKey && e.code === 'KeyK') {
29 | e.preventDefault();
30 | openModal();
31 | }
32 | }
33 |
34 | modal.addEventListener('click', (e) => {
35 | if (e.target === e.currentTarget) {
36 | closeModal();
37 | }
38 | });
39 |
40 | button.addEventListener('click', () => {
41 | openModal();
42 | });
43 |
44 | window.addEventListener('keydown', handleKeyDown);
45 | })();
46 |
--------------------------------------------------------------------------------
/src/changelog.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Changelog"
3 | layout: "layouts/changelog.html"
4 | changelog:
5 | - date: "2023-05-24"
6 | title: "v1.1.0"
7 | items:
8 | - "**This is a test changelog record for demonstration.**"
9 | - "**Improvement:** add `$btn-font-family` to control the button's font family."
10 | - "**Improvement:** add `$heading-font-weight` to control the heading's font-weight."
11 | - "**Improvement:** rename some keys in the `$colors` map (`mark-color` : `mark-foreground`, `code-color` : `code-foreground`)."
12 | - "**Improvement:** reorganize the recurrent colors into variables."
13 | - "**Fix:** modify `btn-variant()` mixin: add hover foreground color."
14 | - "Global switch to `color()` function's fallback value under the [`$settings`](https://sprucecss.com/docs/sass/variables#settings) map."
15 | - "Modify the [`scrollbar()`](https://sprucecss.com/docs/sass/mixins#scrollbar) mixin to accept hover thumb background-color value."
16 | - "Make `$breakpoints` overwriteable by key."
17 | - "Modify `font-size()` and `responsive-font-size()` function to accept optimal size value. The optimal value will fallback to a global settnigs under the `$settings` map."
18 | - date: "2023-05-23"
19 | title: "v1.0.0"
20 | items:
21 | - "Initial release"
22 | ---
23 |
--------------------------------------------------------------------------------
/src/scss/section/_footer.scss:
--------------------------------------------------------------------------------
1 | @use 'sprucecss/scss/spruce' as *;
2 |
3 | .footer {
4 | --logo-block-size: 4rem;
5 | --logo-inline-size: 4rem;
6 |
7 | border-block-start: 1px solid color('border');
8 | margin-block-start: spacer-clamp('l', 'xl');
9 | padding-block-start: spacer-clamp('l', 'xl');
10 |
11 | &__inner {
12 | @include layout-grid('l', 12rem);
13 | }
14 |
15 | &__logo {
16 | block-size: var(--logo-block-size);
17 | display: flex;
18 | inline-size: var(--logo-inline-size);
19 | }
20 |
21 | &__title {
22 | font-size: font-size('h4');
23 | }
24 |
25 | &__navigation {
26 | @include clear-list;
27 | @include layout-stack('xs');
28 |
29 | a {
30 | align-items: center;
31 | color: color('text');
32 | display: inline-flex;
33 | gap: spacer('xs');
34 | text-decoration: none;
35 |
36 | &:hover,
37 | &:focus,
38 | &:active,
39 | &[aria-current='page'] {
40 | color: color('primary');
41 | }
42 |
43 | &[aria-current='page'] {
44 | font-weight: 700;
45 | }
46 |
47 | svg {
48 | --dimension: 0.65em;
49 | block-size: var(--dimension);
50 | color: color('arrow', 'navigation');
51 | inline-size: var(--dimension);
52 | }
53 | }
54 | }
55 |
56 | &__column {
57 | @include layout-stack('m');
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/js/accordion-card.js:
--------------------------------------------------------------------------------
1 | (() => {
2 | document.querySelectorAll('.accordion-card').forEach((accordion) => {
3 | const heading = accordion.querySelector('.accordion-card__title');
4 |
5 | accordion.classList.add('accordion-card--js');
6 |
7 | heading.nextElementSibling.hidden = true;
8 | heading.innerHTML = `
9 |
16 | `;
17 |
18 | const btn = heading.querySelector('button');
19 |
20 | btn.addEventListener('click', () => {
21 | const expanded = btn.getAttribute('aria-expanded') === 'true';
22 |
23 | btn.setAttribute('aria-expanded', !expanded);
24 | heading.nextElementSibling.hidden = expanded;
25 | });
26 | });
27 | })();
28 |
--------------------------------------------------------------------------------
/src/img/icon/github.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/src/_includes/partials/footer.html:
--------------------------------------------------------------------------------
1 |
35 |
--------------------------------------------------------------------------------
/src/posts/getting-started/posts/features.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Features"
3 | summary: "We tried to make a simple but well-structured theme. Managing the content is straightforward but still comes with some helpful features."
4 | eleventyNavigation:
5 | key: Features
6 | parent: Getting Started
7 | order: 2
8 | ---
9 |
10 | We tried to make a simple but well-structured theme. Managing the content is straightforward but still comes with some helpful features.
11 |
12 | ## Eleventy
13 |
14 | - Breadcrumb navigation built on [11ty Navigation Plugin](https://www.11ty.dev/docs/plugins/navigation/).
15 | - HTML minification in production mode.
16 | - Anchor headings.
17 | - Table of Content.
18 | - FAQ template.
19 | - Changelog template.
20 | - Static search integration with [pagefind](https://pagefind.app/).
21 | - Code highlighting.
22 | - [svgIcon](https://github.com/conedevelopment/sprucecss-eleventy-documentation-template/blob/main/src/shortcodes/svg-icon.js) shortcode: render any SVG icon inline and add optional classes.
23 | - [markdownRenderer](https://github.com/conedevelopment/sprucecss-eleventy-documentation-template/blob/main/src/shortcodes/markdown-render.js): render any string (markdown) into HTML.
24 |
25 | ## CSS Customization
26 |
27 | The template utilizes Spruce CSS and some of its components. You can learn more about the customization on the [official documentation](https://sprucecss.com/).
28 |
29 | - RTL support.
30 | - Dark mode.
31 |
--------------------------------------------------------------------------------
/src/posts/getting-started/posts/structure.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Structure"
3 | summary: "The structure is a generic Eleventy theme with the standard folder and file names."
4 | eleventyNavigation:
5 | key: Structure
6 | parent: Getting Started
7 | order: 3
8 | ---
9 |
10 | The structure is a generic Eleventy theme with the standard folder and file names.
11 |
12 | ## Tree View
13 |
14 | ```html
15 | spruecss-eleventy-documentation-template/
16 | ├─ node_modules/
17 | ├─ dist/
18 | ├─ src/
19 | │ ├─ _data/
20 | │ ├─ _includes/
21 | │ ├─ css/
22 | │ ├─ filters/
23 | │ ├─ font/
24 | │ ├─ img/
25 | │ ├─ js/
26 | │ ├─ posts/
27 | │ ├─ scss/
28 | │ ├─ shortcodes/
29 | │ ├─ transforms/
30 | │ ├─ changelog.md
31 | │ ├─ faq.md
32 | │ ├─ index.md
33 | ├─ .eleventy.js
34 | ├─ package.json
35 | ├─ README.md
36 | ├─ ...
37 |
38 | ```
39 |
40 | ## _data
41 |
42 | Some global data, like the name of your site and helpers like the active navigation element or current year.
43 |
44 | ## __includes
45 |
46 | All of the layout and partial templates.
47 |
48 | ## css
49 |
50 | The compiled CSS.
51 |
52 | ## filters
53 |
54 | The additional filters that you can use.
55 |
56 | ## font
57 |
58 | The custom fonts.
59 |
60 | ## img
61 |
62 | The static image files.
63 |
64 | ## posts
65 |
66 | The markdown contents.
67 |
68 | ## scss
69 |
70 | The Sass files.
71 |
72 | ## shortcodes
73 |
74 | The available shortcodes.
75 |
76 | ## transforms
77 |
78 | The transformations.
79 |
--------------------------------------------------------------------------------
/src/scss/component/_sidebar-section.scss:
--------------------------------------------------------------------------------
1 | @use 'sprucecss/scss/spruce' as *;
2 |
3 | .sidebar-section {
4 | &--navigation {
5 | @include breakpoint('sm') {
6 | display: none;
7 | }
8 | }
9 |
10 | &__title {
11 | color: color('heading');
12 | font-size: config('font-size-base', $typography);
13 | margin-block: 0;
14 | }
15 |
16 | &__navigation {
17 | border-inline-start: 1px solid color('border');
18 | font-size: 1rem;
19 | margin-block-start: 1rem;
20 | padding-inline-start: 1rem;
21 |
22 | ul {
23 | @include clear-list;
24 | }
25 |
26 | a {
27 | align-items: center;
28 | color: color('text');
29 | display: inline-flex;
30 | gap: spacer('xs');
31 | text-decoration: none;
32 |
33 | &[aria-current='page'] {
34 | color: color('heading');
35 | font-weight: 700;
36 | position: relative;
37 |
38 | &::before {
39 | background-color: color('primary');
40 | border-end-end-radius: config('border-radius-sm', $display);
41 | border-start-end-radius: config('border-radius-sm', $display);
42 | content: '';
43 | inline-size: 0.3rem;
44 | inset-block: 0;
45 | inset-inline-start: -1rem;
46 | position: absolute;
47 | }
48 | }
49 |
50 | svg {
51 | --dimension: 0.65em;
52 | block-size: var(--dimension);
53 | color: color('arrow', 'navigation');
54 | inline-size: var(--dimension);
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/_includes/layouts/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {% include "partials/preload.html" %}
6 |
7 |
8 |
9 |
10 | {% include "partials/meta.html" %}
11 |
12 |
13 |
14 |
15 |
Skip to content
16 |
17 | {% include "partials/header.html" %}
18 |
19 | {% include "partials/sidebar.html" %}
20 |
21 | {% block content %}{% endblock %}
22 | {% include "partials/footer.html" %}
23 |
24 |
25 |
26 |
27 | {% include "partials/search-modal.html" %}
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/posts/customization/posts/spruce-css.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Spruce CSS"
3 | eleventyNavigation:
4 | key: "Spruce CSS"
5 | parent: "Customization"
6 | order: 6
7 | ---
8 |
9 | **Spruce CSS is an open-source, lightweight and modernish CSS design system, framework built on Sass. Give your project a solid foundation.**
10 |
11 | ## What is Spruce CSS?
12 |
13 | - It is a Sass-based, small framework that operates with just a few utility classes.
14 | - It takes advantage of the Sass members: variables, mixins, and functions.
15 | - It embraces Sass modules, so it uses @use and namespacing for import.
16 | - Spruce is a good choice if you prefer writing CSS instead of HTML. It uses just a few classic utility classes.
17 | - It is a relatively small (~7kb gzipped) framework with a smaller learning curve. The codebase is small but can add more to any project with the available mixins and functions.
18 | - It is that bunch of code you keep manually carrying from project to project.
19 | - It is themeable. You can create different themes using CSS custom properties like a dark one.
20 | - The generated CSS code is separated from the framework. You can use only the tools (variables, mixins, functions) in your project [without the generated styles](https://sprucecss.com/docs/elements/generators).
21 | - Include just a few components. For UI, we have a separate project named [Spruce UI](/ui/getting-started/introduction), where you can find drop-in components.
22 | - [It comes with dark-mode](https://sprucecss.com/docs/customization/themes) (or any theme mode) support. It uses CSS custom properties, so it isn’t that hard to create a new color theme.
23 | - It doesn’t come with a classical grid system.
24 |
--------------------------------------------------------------------------------
/src/scss/component/_post-navigation.scss:
--------------------------------------------------------------------------------
1 | @use 'sprucecss/scss/spruce' as *;
2 |
3 | .post-navigation {
4 | align-items: center;
5 | border-block-start: 1px solid color('border');
6 | display: flex;
7 | flex-wrap: wrap;
8 | gap: spacer('m');
9 | justify-content: space-between;
10 | margin-block-start: spacer('xl');
11 | padding-block: spacer('m');
12 | }
13 |
14 | .post-navigation-item {
15 | $this: &;
16 |
17 | align-items: center;
18 | display: flex;
19 | gap: spacer('s');
20 | text-decoration: none;
21 |
22 | &:hover {
23 | #{$this}__icon {
24 | background-color: color('icon-background-hover', 'navigation');
25 | color: color('icon-foreground-hover', 'navigation');
26 | }
27 | }
28 |
29 | &--next {
30 | margin-inline-start: auto;
31 | text-align: end;
32 | }
33 |
34 | &__icon {
35 | @include transition;
36 | align-items: center;
37 | background-color: color('icon-background', 'navigation');
38 | block-size: 3rem;
39 | border-radius: config('border-radius', $btn, false);
40 | color: color('icon-foreground', 'navigation');
41 | display: flex;
42 | flex-shrink: 0;
43 | inline-size: 3rem;
44 | justify-content: center;
45 |
46 | svg {
47 | --dimension: 1rem;
48 |
49 | block-size: var(--dimension);
50 | inline-size: var(--dimension);
51 |
52 | @at-root {
53 | [dir='rtl'] & {
54 | transform: rotate(180deg);
55 | }
56 | }
57 | }
58 | }
59 |
60 | &__caption {
61 | color: color('text');
62 | line-height: config('line-height-md', $typography);
63 | }
64 |
65 | &__title {
66 | color: color('primary');
67 | display: flex;
68 | font-weight: 700;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/_includes/layouts/front-page.html:
--------------------------------------------------------------------------------
1 | {% extends "layouts/base.html" %}
2 | {% block content %}
3 |
4 | {% include "partials/post-heading.html" %}
5 | {% if overview %}
6 |
22 | {% endif %}
23 | {% if faqs %}
24 |
25 |
Frequently Asked Questions
26 |
27 | {% for faq in faqs %}
28 |
29 |
{{ faq.title }}
30 |
31 | {% markdownRender faq.description %}
32 |
33 |
34 | {% endfor %}
35 |
36 |
37 | {% endif %}
38 |
39 | {% endblock %}
40 |
--------------------------------------------------------------------------------
/src/scss/component/_accordion-card.scss:
--------------------------------------------------------------------------------
1 | @use 'sprucecss/scss/spruce' as *;
2 |
3 | .accordion-card {
4 | $this: &;
5 |
6 | background-color: color('background');
7 | border: 1px solid color('border');
8 | border-radius: config('border-radius-sm', $display);
9 | max-inline-size: 75ch;
10 |
11 | &--js {
12 | #{$this}__title {
13 | padding: 0;
14 | }
15 | }
16 |
17 | &__title {
18 | font-family: config('font-family-base', $typography);
19 | font-size: font-size('h4');
20 | margin-block: 0;
21 | padding: spacer('m');
22 | }
23 |
24 | &__toggle {
25 | @include clear-btn;
26 | align-items: center;
27 | display: flex;
28 | gap: spacer('m');
29 | inline-size: 100%;
30 | justify-content: space-between;
31 | padding: spacer('m');
32 | text-align: start;
33 |
34 | &:focus-visible {
35 | svg {
36 | @include focus-ring(
37 | $type: config('focus-ring-type', $btn, false),
38 | $ring-color: color('primary'),
39 | $ring-size: config('focus-ring-size', $btn, false),
40 | $ring-offset: config('focus-ring-offset', $btn, false)
41 | );
42 | }
43 | }
44 |
45 | svg {
46 | --dimension: 1.75rem;
47 |
48 | background-color: color('primary-background', 'btn');
49 | block-size: var(--dimension);
50 | border-radius: config('border-radius-sm', $display);
51 | color: color('primary-foreground', 'btn');
52 | flex-shrink: 0;
53 | inline-size: var(--dimension);
54 | }
55 |
56 | &[aria-expanded='true'] .vertical-line {
57 | display: none;
58 | }
59 | }
60 |
61 | &__content {
62 | @include layout-stack('xs');
63 | padding-block-end: spacer('m');
64 | padding-inline: spacer('m');
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/_includes/partials/meta.html:
--------------------------------------------------------------------------------
1 | {% if page.url.length > 1 %}
2 | {% set pageTitle = title + ' - ' + site.name %}
3 | {% else %}
4 | {% set pageTitle = site.name %}
5 | {% endif %}
6 |
7 | {% if site.name === title %}
8 | {% set pageTitle = title %}
9 | {% endif %}
10 |
11 | {% set siteTitle = site.name %}
12 | {% set currentUrl = site.url + page.url %}
13 |
14 | {% if not socialImage %}
15 | {% set socialImage = site.url + '/img/social-share.png' %}
16 | {% else %}
17 | {% set socialImage = site.url + socialImage %}
18 | {% endif %}
19 |
20 | {% if metaTitle %}
21 | {% set pageTitle = metaTitle %}
22 | {% endif %}
23 |
24 | {% if not metaDesc %}
25 | {% set metaDesc = summary %}
26 | {% endif %}
27 |
28 |
{{ pageTitle }}
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | {% if socialImage %}
39 |
40 |
41 |
42 |
43 |
44 | {% endif %}
45 |
46 | {% if metaDesc %}
47 |
48 |
49 |
50 | {% endif %}
51 |
52 |
53 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sprucecss-eleventy-documentation-template",
3 | "version": "1.2.0",
4 | "author": "Cone (https://conedevelopment.com)",
5 | "main": ".eleventy.js",
6 | "scripts": {
7 | "delete:dist": "del-cli --force dist",
8 | "eleventy:dev": "npx eleventy --serve",
9 | "eleventy:prod": "npx eleventy",
10 | "js:lint": "npx eslint \"src/**/*.js\"",
11 | "js:lint:fix": "npx eslint \"src/**/*.js**\" --fix",
12 | "prod": "npm-run-all delete:dist eleventy:prod sass:prod",
13 | "sass:dev": "sass --load-path=node_modules --watch --no-source-map --update --style=expanded src/scss:src/css",
14 | "sass:prod": "sass --load-path=node_modules --no-source-map --style=compressed src/scss:src/css",
15 | "sass:lint": "stylelint \"src/scss/**/*.scss\"",
16 | "sass:lint:fix": "stylelint \"src/scss/**/*.scss\" --fix",
17 | "start": "npm-run-all --parallel eleventy:dev sass:dev"
18 | },
19 | "dependencies": {
20 | "@11ty/eleventy-img": "^3.1.0",
21 | "@11ty/eleventy": "^2.0.1",
22 | "@11ty/eleventy-navigation": "^0.3.5",
23 | "@11ty/eleventy-plugin-syntaxhighlight": "^5.0.0",
24 | "del-cli": "^5.0.1",
25 | "dotenv": "^16.3.1",
26 | "eleventy-plugin-toc": "^1.1.5",
27 | "eslint": "^8.48.0",
28 | "eslint-config-airbnb-base": "^15.0.0",
29 | "eslint-plugin-import": "^2.28.1",
30 | "himalaya": "^1.1.0",
31 | "html-minifier": "^4.0.0",
32 | "moment": "^2.29.4",
33 | "markdown-it-anchor": "^8.6.7",
34 | "npm-run-all": "^4.1.5",
35 | "pagefind": "^0.12.0",
36 | "sass": "^1.66.1",
37 | "sprucecss": "^2.3.0",
38 | "stylelint": "^15.10.3",
39 | "stylelint-config-sass-guidelines": "^10.0.0",
40 | "stylelint-order": "^6.0.3"
41 | },
42 | "engines": {
43 | "node": "^18.12",
44 | "npm": "^9.2",
45 | "yarn": "please-use-npm"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/img/icon/discord.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/src/img/icon/light-mode.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/src/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Document your next project a little bit better."
3 | headline: "Eleventy / Spruce CSS"
4 | btns:
5 | -
6 | caption: "Introduction"
7 | url: "/getting-started/introduction/"
8 | type: "regular"
9 | -
10 | caption: "Changelog"
11 | url: "/changelog/"
12 | type: "outline"
13 | summary: "Do you work on a project that requires a documentation? This theme is for you. It's a simple, clean and responsive theme for Eleventy."
14 | displaySummary: true
15 | layout: "layouts/front-page.html"
16 | overview:
17 | -
18 | title: "Getting Started"
19 | url: "/getting-started/"
20 | description: "Start here and get to know this minimalistic Eleventy theme."
21 | -
22 | title: "Customization"
23 | url: "/customization/"
24 | description: "Built on the top of Spruce CSS, you can easily customize its look."
25 | faqs:
26 | -
27 | title: "Why Make Another CSS Framework?"
28 | description: "As you may know, there are many CSS frameworks (hundreds of them, and a lot of them are not maintained today). Everybody can choose one that suits their work style or project requirements. So why make another one? It is certainly not because we can do it better but because we want to do it our way. We want to be in control and make decisions."
29 | -
30 | title: "It Is Opinionated"
31 | description: "Each system is opinionated but on a different level; this is valid for Spruce too. We don’t want to vote for (strictly) any particular solution (because there is always more than one), but we will show you what we think is the best for us (and maybe for you too). We don’t believe there is a good or bad solution, but we can learn from any of them."
32 | -
33 | title: "We Left the Grid Out"
34 | description: "One controversial decision we made with Spruce is to leave a classical grid system out. Because of the late CSS layout model developments like Flexbox and Grid, we think it can be eliminated; this doesn’t mean that we won’t show you how to make layouts with ease, but we try to make it the modern way."
35 | -
36 | title: "Coding Style Guide and Practices"
37 | description: "Where it is possible, we use elements and/or attributes to style elements, but it is still a class-based framework."
38 | ---
39 |
--------------------------------------------------------------------------------
/src/_includes/partials/sidebar.html:
--------------------------------------------------------------------------------
1 |
49 |
--------------------------------------------------------------------------------
/src/faq.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Frequently Asked Questions"
3 | summary: "Have some questions? You may find it here."
4 | displaySummary: true
5 | layout: "layouts/faq.html"
6 | faqs:
7 | -
8 | title: "Spruce CSS"
9 | items:
10 | -
11 | title: "Why Make Another CSS Framework?"
12 | description: "As you may know, there are many CSS frameworks (hundreds of them, and a lot of them are not maintained today). Everybody can choose one that suits their work style or project requirements. So why make another one? It is certainly not because we can do it better but because we want to do it our way. We want to be in control and make decisions."
13 | -
14 | title: "It Is Opinionated"
15 | description: "Each system is opinionated but on a different level; this is valid for Spruce too. We don’t want to vote for (strictly) any particular solution (because there is always more than one), but we will show you what we think is the best for us (and maybe for you too). We don’t believe there is a good or bad solution, but we can learn from any of them."
16 | -
17 | title: "We Left the Grid Out"
18 | description: "One controversial decision we made with Spruce is to leave a classical grid system out. Because of the late CSS layout model developments like Flexbox and Grid, we think it can be eliminated; this doesn’t mean that we won’t show you how to make layouts with ease, but we try to make it the modern way."
19 | -
20 | title: "Coding Style Guide and Practices"
21 | description: "Where it is possible, we use elements and/or attributes to style elements, but it is still a class-based framework."
22 | -
23 | title: "Demo FAQ"
24 | items:
25 | -
26 | title: "Lorem ipsum dolor sit amet"
27 | description: "Nulla porta felis mollis est suscipit vestibulum. Integer fermentum ullamcorper leo a pulvinar."
28 | -
29 | title: "Donec rhoncus facilisis velit, efficitur interdum"
30 | description: "Ut at nunc tristique, tincidunt tortor eget, consequat magna. Phasellus cursus nisi et orci porttitor feugiat. Etiam porttitor consequat sapien eu elementum. Nulla in interdum enim, non molestie tellus. In id sagittis nulla. Morbi ultrices eros libero, quis vehicula mauris egestas vitae. Fusce varius tortor risus. Aliquam id cursus massa."
31 | ---
32 |
--------------------------------------------------------------------------------
/src/img/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
--------------------------------------------------------------------------------
/src/_data/navigation.json:
--------------------------------------------------------------------------------
1 | {
2 | "header": [
3 | {
4 | "caption": "FAQ",
5 | "url": "/faq/",
6 | "external": false
7 | },
8 | {
9 | "caption": "Changelog",
10 | "url": "/changelog/",
11 | "external": false
12 | },
13 | {
14 | "caption": "Spruce CSS",
15 | "url": "https://sprucecss.com",
16 | "external": true
17 | }
18 | ],
19 | "footer": [
20 | {
21 | "title": "Getting Started",
22 | "items": [
23 | {
24 | "caption": "Introduction",
25 | "url": "/getting-started/introduction/",
26 | "external": false
27 | },
28 | {
29 | "caption": "Features",
30 | "url": "/getting-started/features/",
31 | "external": false
32 | },
33 | {
34 | "caption": "Structure",
35 | "url": "/getting-started/structure/",
36 | "external": false
37 | },
38 | {
39 | "caption": "Setup",
40 | "url": "/getting-started/setup/",
41 | "external": false
42 | }
43 | ]
44 | },
45 | {
46 | "title": "Spruce CSS",
47 | "items": [
48 | {
49 | "caption": "Introduction",
50 | "url": "https://sprucecss.com/docs/getting-started/introduction/",
51 | "external": true
52 | },
53 | {
54 | "caption": "Themes",
55 | "url": "https://sprucecss.com/docs/customization/themes/",
56 | "external": true
57 | },
58 | {
59 | "caption": "Components",
60 | "url": "https://sprucecss.com/ui/getting-started/introduction/",
61 | "external": true
62 | }
63 | ]
64 | },
65 | {
66 | "title": "More From Us",
67 | "items": [
68 | {
69 | "caption": "Services",
70 | "url": "https://conedevelopment.com/services/",
71 | "external": true
72 | },
73 | {
74 | "caption": "Pine development blog",
75 | "url": "https://pineco.de/",
76 | "external": true
77 | },
78 | {
79 | "caption": "Root Laravel admin",
80 | "url": "https://root.conedevelopment.com/",
81 | "external": true
82 | },
83 | {
84 | "caption": "Bite-Sized Accessibility",
85 | "url": "https://bite-sized-a11y.com/",
86 | "external": true
87 | }
88 | ]
89 | }
90 | ]
91 | }
92 |
--------------------------------------------------------------------------------
/src/_includes/partials/header.html:
--------------------------------------------------------------------------------
1 |
56 |
--------------------------------------------------------------------------------
/.eleventy.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config();
2 |
3 | const { execSync } = require('child_process');
4 | const dateFilter = require('./src/filters/date-filter.js');
5 | const eleventyNavigationPlugin = require('@11ty/eleventy-navigation');
6 | const htmlMinTransform = require('./src/transforms/html-min-transform.js');
7 | const isProduction = process.env.NODE_ENV === 'production';
8 | const markdownIt = require('markdown-it');
9 | const markdownItAnchor = require('markdown-it-anchor');
10 | const pluginTOC = require('eleventy-plugin-toc');
11 | const syntaxHighlight = require('@11ty/eleventy-plugin-syntaxhighlight');
12 | const w3DateFilter = require('./src/filters/w3-date-filter.js');
13 | const parentFilter = require('./src/filters/parent-filter.js');
14 | const markdownRenderShortcode = require('./src/shortcodes/markdown-render.js');
15 | const svgIconShortcode = require('./src/shortcodes/svg-icon.js');
16 |
17 | module.exports = config => {
18 | config.addFilter('dateFilter', dateFilter);
19 | config.addFilter('w3DateFilter', w3DateFilter);
20 | config.addFilter('parentFilter', parentFilter);
21 | config.addFilter('debugger', (...args) => {
22 | console.log(...args);
23 | debugger;
24 | });
25 |
26 | config.addPlugin(eleventyNavigationPlugin);
27 | config.addPlugin(syntaxHighlight);
28 | config.addPlugin(pluginTOC);
29 |
30 | config.addPassthroughCopy({ './src/robots.txt': '/robots.txt' });
31 | config.addPassthroughCopy('./src/img/**');
32 | config.addPassthroughCopy('./src/css/**');
33 | config.addPassthroughCopy('./src/js/**');
34 | config.addPassthroughCopy('./src/font/**');
35 |
36 | config.addCollection('posts', collection => {
37 | const items = collection.getFilteredByGlob('./src/posts/**/posts/*.md');
38 | return items.sort((a, b) => a.data.eleventyNavigation.order - b.data.eleventyNavigation.order);
39 | });
40 |
41 | config.addAsyncShortcode('svgIcon', svgIconShortcode);
42 | config.addAsyncShortcode('markdownRender', markdownRenderShortcode);
43 |
44 | if (isProduction) {
45 | config.addTransform('htmlmin', htmlMinTransform);
46 | }
47 |
48 | const markdownLib = markdownIt({ html: true }).use(
49 | markdownItAnchor,
50 | {
51 | permalink: true,
52 | permalinkClass: 'anchor',
53 | permalinkSymbol: '#'
54 | }
55 | );
56 |
57 | config.setLibrary('md', markdownLib);
58 |
59 | config.on('eleventy.after', () => {
60 | execSync(`npx pagefind --source dist --glob \"**/*.html\"`, { encoding: 'utf-8' })
61 | });
62 |
63 | return {
64 | markdownTemplateEngine: 'njk',
65 | dataTemplateEngine: 'njk',
66 | htmlTemplateEngine: 'njk',
67 | dir: {
68 | input: 'src',
69 | output: 'dist'
70 | },
71 | passthroughFileCopy: true,
72 | pathPrefix: './',
73 | };
74 | };
75 |
--------------------------------------------------------------------------------
/src/scss/component/_prism.scss:
--------------------------------------------------------------------------------
1 | @use 'sprucecss/scss/spruce' as *;
2 |
3 | code[class*='language-'],
4 | pre[class*='language-'] {
5 | border-radius: config('border-radius-sm', $display);
6 | color: color('color', 'prism');
7 | font-family: config('font-family-cursive', $typography);
8 | font-size: config('font-size-base', $typography);
9 | hyphens: none;
10 | line-height: 1.5;
11 | tab-size: 4;
12 | text-align: left;
13 | white-space: pre;
14 | word-break: normal;
15 | word-spacing: normal;
16 | word-wrap: normal;
17 | }
18 |
19 | @media print {
20 | code[class*='language-'],
21 | pre[class*='language-'] {
22 | text-shadow: none;
23 | }
24 | }
25 |
26 | /* Code blocks */
27 | pre[class*='language-'] {
28 | display: grid;
29 | overflow: auto;
30 | padding: spacer('m');
31 | }
32 |
33 | pre[class*='language-'] code {
34 | background-color: transparent;
35 | padding: 0;
36 | }
37 |
38 | :not(pre) > code[class*='language-'],
39 | pre[class*='language-'] {
40 | background: color('background', 'prism');
41 | overflow-x: auto;
42 | }
43 |
44 | .token.comment,
45 | .token.prolog,
46 | .token.cdata {
47 | color: color('comment', 'prism');
48 | font-style: italic;
49 | }
50 |
51 | .token.punctuation {
52 | color: color('punctuation', 'prism');
53 | }
54 |
55 | .namespace {
56 | color: color('namespace', 'prism');
57 | }
58 |
59 | .token.deleted {
60 | color: color('deleted', 'prism');
61 | font-style: italic;
62 | }
63 |
64 | .token.symbol,
65 | .token.operator,
66 | .token.keyword,
67 | .token.property {
68 | color: color('namespace', 'prism');
69 | }
70 |
71 | .token.tag {
72 | color: color('punctuation', 'prism');
73 | }
74 |
75 | .token.boolean {
76 | color: color('boolean', 'prism');
77 | }
78 |
79 | .token.number {
80 | color: color('number', 'prism');
81 | }
82 |
83 | .token.constant,
84 | .token.builtin,
85 | .token.string,
86 | .token.url,
87 | .token.entity,
88 | .language-css .token.string,
89 | .style .token.string,
90 | .token.char {
91 | color: color('constant', 'prism');
92 | }
93 |
94 | .token.selector,
95 | .token.function,
96 | .token.doctype {
97 | color: color('punctuation', 'prism');
98 | font-style: italic;
99 | }
100 |
101 | .token.attr-name,
102 | .token.inserted {
103 | color: color('constant', 'prism');
104 | font-style: italic;
105 | }
106 |
107 | .token.class-name,
108 | .token.atrule {
109 | color: color('class-name', 'prism');
110 | }
111 |
112 | .token.regex,
113 | .token.important,
114 | .token.variable {
115 | color: color('regex', 'prism');
116 | }
117 |
118 | .token.important,
119 | .token.bold {
120 | font-weight: bold;
121 | }
122 |
123 | .token.italic {
124 | font-style: italic;
125 | }
126 |
--------------------------------------------------------------------------------
/src/scss/config/_config.scss:
--------------------------------------------------------------------------------
1 | @use 'sass:color';
2 | @use 'dark-colors' as dark;
3 |
4 | $color-primary: hsl(238deg 100% 50%);
5 | $color-secondary: hsl(186deg 100% 60%);
6 |
7 | @use 'sprucecss/scss/spruce' with (
8 | $btn: (
9 | 'font-family': #{'Manrope', sans-serif},
10 | 'font-weight': 700,
11 | 'padding': 0.75em 1.5em,
12 | ),
13 | $btn-lg: (
14 | 'font-size': 1rem,
15 | 'padding': 1em 1.75em,
16 | ),
17 | $color-black: hsl(245 31% 21%),
18 | $color-primary: $color-primary,
19 | $color-secondary: $color-secondary,
20 | $colors: (
21 | 'base': (
22 | 'border': hsl(0deg 0% 0% / 5%),
23 | 'code-background': hsl(224deg 94% 97%),
24 | 'heading': hsl(232deg 68% 11%),
25 | 'text': hsl(208, 21%, 39%),
26 | ),
27 | 'btn': (
28 | 'secondary-foreground': color.adjust($color-secondary, $lightness: -50%),
29 | 'secondary-shadow': color.adjust($color-secondary, $lightness: 20%),
30 | ),
31 | 'breadcrumb': (
32 | 'separator': hsl(0deg 0% 80%),
33 | ),
34 | 'card': (
35 | 'background': hsl(0deg 0% 100%),
36 | ),
37 | 'footer': (
38 | 'background': hsl(224deg 94% 98%),
39 | ),
40 | 'navigation': (
41 | 'arrow': hsl(0deg 0% 0% / 15%),
42 | 'icon-background': hsl(224deg 94% 98%),
43 | 'icon-background-hover': $color-primary,
44 | 'icon-foreground': $color-primary,
45 | 'icon-foreground-hover': hsl(0deg 0% 100%),
46 | ),
47 | 'search': (
48 | 'icon': hsl(229deg 26% 48% / 25%),
49 | ),
50 | 'scrollbar': (
51 | 'thumb-background': hsl(229deg 26% 48% / 15%),
52 | 'thumb-background-hover': hsl(0deg 0% 0% / 25%),
53 | 'track-background': hsl(226deg 100% 87% / 15%),
54 | ),
55 | 'prism': (
56 | 'color': hsl(243deg 14% 29%),
57 | 'background': hsl(224deg 94% 99%),
58 | 'comment': hsl(225deg 14% 46%),
59 | 'punctuation': hsl(279deg 50% 53%),
60 | 'namespace': hsl(173deg 100% 24%),
61 | 'deleted': hsl(1deg 83% 63% / 56%),
62 | 'boolean': hsl(0deg 44% 53%),
63 | 'number': hsl(315deg 90% 35%),
64 | 'constant': hsl(221deg 57% 52%),
65 | 'class-name': hsl(0deg 0% 7%),
66 | 'regex': hsl(1deg 48% 59%),
67 | ),
68 | ),
69 | $dark-colors: dark.$colors,
70 | $generators: (
71 | 'form': (
72 | 'file-btn': false,
73 | 'form-check': false,
74 | 'form-control': false,
75 | 'form-description': false,
76 | 'form-feedback': false,
77 | 'form-fieldset': false,
78 | 'form-group': false,
79 | 'form-label': false,
80 | 'form-range': false,
81 | 'form-row': false,
82 | 'form-switch': false,
83 | ),
84 | ),
85 | $typography: (
86 | 'font-family-base': #{'Open Sans', sans-serif},
87 | 'font-family-heading': #{'Manrope', sans-serif},
88 | 'font-size-lead': clamp(1.1rem, 2vw, 1.25rem),
89 | ),
90 | $settings: (
91 | 'css-custom-properties': true,
92 | ),
93 | );
94 |
--------------------------------------------------------------------------------
/src/scss/section/_header.scss:
--------------------------------------------------------------------------------
1 | @use 'sprucecss/scss/spruce' as *;
2 |
3 | .header {
4 | align-items: center;
5 | border-block-end: 1px solid color('border');
6 | display: flex;
7 | flex-wrap: wrap;
8 | gap: spacer('s') spacer-clamp('m', 'l');
9 | justify-content: space-between;
10 | order: 2;
11 | padding-block: 1.75rem;
12 |
13 | @include breakpoint('sm') {
14 | order: -1;
15 | }
16 |
17 | &__column {
18 | align-items: center;
19 | display: flex;
20 | flex-grow: 1;
21 | gap: spacer-clamp('m', 'l');
22 | justify-content: space-between;
23 |
24 | @include breakpoint('sm') {
25 | justify-content: flex-start;
26 | }
27 | }
28 |
29 | &__logo {
30 | display: inline-flex;
31 |
32 | img,
33 | svg {
34 | block-size: 2rem;
35 | display: inline-flex;
36 | inline-size: auto;
37 | }
38 | }
39 |
40 | &__toggle {
41 | @include set-css-variable((
42 | --border-radius: 1rem 1rem 0 1rem
43 | ));
44 |
45 | @include breakpoint('sm') {
46 | display: none;
47 | }
48 | }
49 |
50 | &__actions {
51 | align-items: center;
52 | display: flex;
53 | flex-wrap: wrap;
54 | gap: spacer-clamp('s', 'm');
55 | margin-inline-start: auto;
56 | }
57 |
58 | &__navigation {
59 | display: none;
60 |
61 | @include breakpoint('sm') {
62 | display: flex;
63 | }
64 |
65 | ul {
66 | @include clear-list;
67 | align-items: center;
68 | background-color: transparent;
69 | display: flex;
70 | flex-direction: row;
71 | flex-wrap: wrap;
72 | gap: spacer('xs') spacer('m');
73 | inset: auto;
74 | padding: 0;
75 | position: relative;
76 |
77 | @include breakpoint('lg') {
78 | gap: spacer('xs') spacer('l');
79 | }
80 | }
81 |
82 | li {
83 | margin-block: 0;
84 | }
85 |
86 | a {
87 | align-items: center;
88 | color: color('heading');
89 | display: flex;
90 | gap: spacer('xs');
91 | text-decoration: none;
92 |
93 | &:hover {
94 | color: color('primary');
95 | }
96 |
97 | &[aria-current='page'] {
98 | font-weight: 700;
99 | }
100 | }
101 |
102 | svg {
103 | --dimension: 0.65em;
104 | block-size: var(--dimension);
105 | color: color('arrow', 'navigation');
106 | inline-size: var(--dimension);
107 | }
108 | }
109 |
110 | &__socials {
111 | align-items: center;
112 | display: flex;
113 | gap: spacer('s');
114 |
115 | a {
116 | color: color('heading');
117 | display: inline-flex;
118 |
119 | &:hover,
120 | &:focus {
121 | color: color('primary');
122 | }
123 | }
124 |
125 | svg {
126 | --dimension: 1.25rem;
127 | block-size: var(--dimension);
128 | inline-size: var(--dimension);
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### macOS ###
2 | # General
3 | .DS_Store
4 | .AppleDouble
5 | .LSOverride
6 |
7 | # Thumbnails
8 | ._*
9 |
10 | # Files that might appear in the root of a volume
11 | .DocumentRevisions-V100
12 | .fseventsd
13 | .Spotlight-V100
14 | .TemporaryItems
15 | .Trashes
16 | .VolumeIcon.icns
17 | .com.apple.timemachine.donotpresent
18 |
19 | # Directories potentially created on remote AFP share
20 | .AppleDB
21 | .AppleDesktop
22 | Network Trash Folder
23 | Temporary Items
24 | .apdisk
25 |
26 | ### Node ###
27 | # Logs
28 | logs
29 | *.log
30 | npm-debug.log*
31 | yarn-debug.log*
32 | yarn-error.log*
33 | lerna-debug.log*
34 |
35 | # Diagnostic reports (https://nodejs.org/api/report.html)
36 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
37 |
38 | # Runtime data
39 | pids
40 | *.pid
41 | *.seed
42 | *.pid.lock
43 |
44 | # Directory for instrumented libs generated by jscoverage/JSCover
45 | lib-cov
46 |
47 | # Coverage directory used by tools like istanbul
48 | coverage
49 | *.lcov
50 |
51 | # nyc test coverage
52 | .nyc_output
53 |
54 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
55 | .grunt
56 |
57 | # Bower dependency directory (https://bower.io/)
58 | bower_components
59 |
60 | # node-waf configuration
61 | .lock-wscript
62 |
63 | # Compiled binary addons (https://nodejs.org/api/addons.html)
64 | build/Release
65 |
66 | # Dependency directories
67 | node_modules/
68 | jspm_packages/
69 |
70 | # TypeScript v1 declaration files
71 | typings/
72 |
73 | # TypeScript cache
74 | *.tsbuildinfo
75 |
76 | # Optional npm cache directory
77 | .npm
78 |
79 | # Optional eslint cache
80 | .eslintcache
81 |
82 | # Optional stylelint cache
83 | .stylelintcache
84 |
85 | # Microbundle cache
86 | .rpt2_cache/
87 | .rts2_cache_cjs/
88 | .rts2_cache_es/
89 | .rts2_cache_umd/
90 |
91 | # Optional REPL history
92 | .node_repl_history
93 |
94 | # Output of 'npm pack'
95 | *.tgz
96 |
97 | # Yarn Integrity file
98 | .yarn-integrity
99 |
100 | # dotenv environment variables file
101 | .env
102 | .env.test
103 | .env*.local
104 |
105 | # parcel-bundler cache (https://parceljs.org/)
106 | .cache
107 | .parcel-cache
108 |
109 | # Next.js build output
110 | .next
111 |
112 | # Nuxt.js build / generate output
113 | .nuxt
114 | dist
115 |
116 | # Storybook build outputs
117 | .out
118 | .storybook-out
119 | storybook-static
120 |
121 | # rollup.js default build output
122 | dist/
123 |
124 | # Gatsby files
125 | .cache/
126 | # Comment in the public line in if your project uses Gatsby and not Next.js
127 | # https://nextjs.org/blog/next-9-1#public-directory-support
128 | # public
129 |
130 | # vuepress build output
131 | .vuepress/dist
132 |
133 | # Serverless directories
134 | .serverless/
135 |
136 | # FuseBox cache
137 | .fusebox/
138 |
139 | # DynamoDB Local files
140 | .dynamodb/
141 |
142 | # TernJS port file
143 | .tern-port
144 |
145 | # Stores VSCode versions used for testing VSCode extensions
146 | .vscode-test
147 |
148 | # Temporary folders
149 | tmp/
150 | temp/
151 |
152 | ### Windows ###
153 | # Windows thumbnail cache files
154 | Thumbs.db
155 | Thumbs.db:encryptable
156 | ehthumbs.db
157 | ehthumbs_vista.db
158 |
159 | # Dump file
160 | *.stackdump
161 |
162 | # Folder config file
163 | [Dd]esktop.ini
164 |
165 | # Recycle Bin used on file shares
166 | $RECYCLE.BIN/
167 |
168 | # Windows Installer files
169 | *.cab
170 | *.msi
171 | *.msix
172 | *.msm
173 | *.msp
174 |
175 | # Windows shortcuts
176 | *.lnk
177 |
--------------------------------------------------------------------------------
/src/scss/config/_dark-colors.scss:
--------------------------------------------------------------------------------
1 | @use 'sass:color';
2 |
3 | $dark-color-black: hsl(245deg 38% 11%);
4 | $dark-color-danger: hsl(0deg 71% 51%);
5 | $dark-color-gray-dark: hsl(0deg 0% 100% / 8%);
6 | $dark-color-gray: hsl(0deg 0% 97%);
7 | $dark-color-primary: hsl(186deg 100% 60%);
8 | $dark-color-secondary: hsl(227deg 92% 55%);
9 | $dark-color-success: hsl(150deg 100% 33%);
10 | $dark-color-white: hsl(0deg 0% 95%);
11 |
12 | $colors: (
13 | 'base': (
14 | 'background': $dark-color-black,
15 | 'blockquote-border': $dark-color-primary,
16 | 'border': $dark-color-gray-dark,
17 | 'card-border': hsl(207deg 90% 13%),
18 | 'code-background': hsl(207deg 64% 21%),
19 | 'code-foreground': $dark-color-white,
20 | 'footer-background': hsl(0deg 0% 0% / 15%),
21 | 'heading': $dark-color-white,
22 | 'link-hover': color.scale($dark-color-primary, $lightness: 10%),
23 | 'link': $dark-color-primary,
24 | 'mark-background': hsl(50deg 100% 80%),
25 | 'mark-foreground': $dark-color-black,
26 | 'marker': $dark-color-primary,
27 | 'primary': $dark-color-primary,
28 | 'secondary': $dark-color-secondary,
29 | 'text': $dark-color-gray,
30 | ),
31 | 'breadcrumb': (
32 | 'arrow': hsl(0deg 0% 100% / 0.1),
33 | ),
34 | 'btn': (
35 | 'primary-background': $dark-color-primary,
36 | 'primary-background-hover': hsl(186deg 100% 45%),
37 | 'primary-foreground': hsl(186deg 100% 5%),
38 | 'primary-shadow': hsl(186deg 100% 25%),
39 | 'secondary-background': $dark-color-secondary,
40 | 'secondary-background-hover': color.adjust($dark-color-secondary, $lightness: 5%),
41 | 'secondary-foreground': $dark-color-white,
42 | 'secondary-shadow': color.adjust($dark-color-secondary, $lightness: -20%),
43 | ),
44 | 'card': (
45 | 'background': $dark-color-black,
46 | ),
47 | 'container': (
48 | 'background': hsl(207deg 92% 12%),
49 | ),
50 | 'form': (
51 | 'background': color.scale($dark-color-black, $lightness: 5%),
52 | 'background-disabled': $dark-color-black,
53 | 'border-disabled': $dark-color-gray-dark,
54 | 'border-focus': $dark-color-primary,
55 | 'border': $dark-color-gray-dark,
56 | 'check-background': $dark-color-primary,
57 | 'check-foreground': $dark-color-black,
58 | 'invalid': $dark-color-danger,
59 | 'invalid-shadow': color.adjust($dark-color-danger, $alpha: -0.75),
60 | 'label': $dark-color-white,
61 | 'legend': $dark-color-white,
62 | 'placeholder': hsl(0deg 0% 60%),
63 | 'select-foreground': hsl(0deg 0% 100%),
64 | 'shadow-focus': color.adjust($dark-color-primary, $alpha: -0.75),
65 | 'text': $dark-color-gray,
66 | 'valid': $dark-color-success,
67 | 'valid-shadow': color.adjust($dark-color-success, $alpha: -0.75),
68 | ),
69 | 'footer': (
70 | 'background': hsl(245deg 38% 10%),
71 | ),
72 | 'navigation': (
73 | 'arrow': hsl(0deg 0% 100% / 15%),
74 | 'icon-background': hsl(245deg 38% 10%),
75 | 'icon-background-hover': $dark-color-primary,
76 | 'icon-foreground': $dark-color-primary,
77 | 'icon-foreground-hover': hsl(186deg 100% 5%),
78 | ),
79 | 'prism': (
80 | 'color': hsl(217deg 34% 88%),
81 | 'background': hsl(245deg 38% 7%),
82 | 'comment': hsl(180deg 9% 55%),
83 | 'punctuation': hsl(276deg 68% 75%),
84 | 'namespace': hsl(197deg 31% 77%),
85 | 'deleted': hsl(1deg 83% 63% / 56%),
86 | 'boolean': hsl(350deg 100% 67%),
87 | 'number': hsl(14deg 90% 70%),
88 | 'constant': hsl(221deg 100% 75%),
89 | 'class-name': hsl(33deg 100% 77%),
90 | 'regex': hsl(217deg 34% 88%),
91 | ),
92 | 'table': (
93 | 'border': $dark-color-gray-dark,
94 | 'caption': $dark-color-gray,
95 | 'heading': $dark-color-white,
96 | 'hover': hsl(0deg 0% 100% / 5%),
97 | 'stripe': hsl(0deg 0% 100% / 2.5%),
98 | 'text': $dark-color-gray,
99 | ),
100 | 'selection': (
101 | 'background': $dark-color-primary,
102 | 'foreground': hsl(186deg 100% 5%),
103 | ),
104 | 'search': (
105 | 'icon': hsl(0deg 0% 100% / 25%),
106 | ),
107 | 'scrollbar': (
108 | 'thumb-background': hsl(0 0% 100% / 15%),
109 | 'thumb-background-hover': hsl(0 0% 100% / 25%),
110 | 'track-background': hsl(0 0% 100% / 5%),
111 | ),
112 | );
113 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
24 | * Focusing on what is best not just for us as individuals, but for the overall community
25 |
26 | Examples of unacceptable behavior include:
27 |
28 | * The use of sexualized language or imagery, and sexual attention or advances of any kind
29 | * Trolling, insulting or derogatory comments, and personal or political attacks
30 | * Public or private harassment
31 | * Publishing others' private information, such as a physical or email address, without their explicit permission
32 | * Other conduct which could reasonably be considered inappropriate in a professional setting
33 |
34 | ## Enforcement Responsibilities
35 |
36 | Community leaders are responsible for clarifying and enforcing our standards of
37 | acceptable behavior and will take appropriate and fair corrective action in
38 | response to any behavior that they deem inappropriate, threatening, offensive,
39 | or harmful.
40 |
41 | Community leaders have the right and responsibility to remove, edit, or reject
42 | comments, commits, code, wiki edits, issues, and other contributions that are
43 | not aligned to this Code of Conduct, and will communicate reasons for moderation
44 | decisions when appropriate.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies within all community spaces, and also applies when
49 | an individual is officially representing the community in public spaces.
50 | Examples of representing our community include using an official e-mail address,
51 | posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event.
53 |
54 | ## Enforcement
55 |
56 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
57 | reported to the community leaders responsible for enforcement at
58 | hello@conedevelopment.com.
59 | All complaints will be reviewed and investigated promptly and fairly.
60 |
61 | All community leaders are obligated to respect the privacy and security of the
62 | reporter of any incident.
63 |
64 | ## Enforcement Guidelines
65 |
66 | Community leaders will follow these Community Impact Guidelines in determining
67 | the consequences for any action they deem in violation of this Code of Conduct:
68 |
69 | ### 1. Correction
70 |
71 | **Community Impact**: Use of inappropriate language or other behavior deemed
72 | unprofessional or unwelcome in the community.
73 |
74 | **Consequence**: A private, written warning from community leaders, providing
75 | clarity around the nature of the violation and an explanation of why the
76 | behavior was inappropriate. A public apology may be requested.
77 |
78 | ### 2. Warning
79 |
80 | **Community Impact**: A violation through a single incident or series
81 | of actions.
82 |
83 | **Consequence**: A warning with consequences for continued behavior. No
84 | interaction with the people involved, including unsolicited interaction with
85 | those enforcing the Code of Conduct, for a specified period of time. This
86 | includes avoiding interactions in community spaces as well as external channels
87 | like social media. Violating these terms may lead to a temporary or
88 | permanent ban.
89 |
90 | ### 3. Temporary Ban
91 |
92 | **Community Impact**: A serious violation of community standards, including
93 | sustained inappropriate behavior.
94 |
95 | **Consequence**: A temporary ban from any sort of interaction or public
96 | communication with the community for a specified period of time. No public or
97 | private interaction with the people involved, including unsolicited interaction
98 | with those enforcing the Code of Conduct, is allowed during this period.
99 | Violating these terms may lead to a permanent ban.
100 |
101 | ### 4. Permanent Ban
102 |
103 | **Community Impact**: Demonstrating a pattern of violation of community
104 | standards, including sustained inappropriate behavior, harassment of an
105 | individual, or aggression toward or disparagement of classes of individuals.
106 |
107 | **Consequence**: A permanent ban from any sort of public interaction within
108 | the community.
109 |
110 | ## Attribution
111 |
112 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
113 | version 2.0, available at
114 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
115 |
116 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
117 | enforcement ladder](https://github.com/mozilla/diversity).
118 |
119 | [homepage]: https://www.contributor-covenant.org
120 |
121 | For answers to common questions about this code of conduct, see the FAQ at
122 | https://www.contributor-covenant.org/faq. Translations are available at
123 | https://www.contributor-covenant.org/translations.
124 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | **Welcome to the official documentation of **Spruce Docs** Elventy theme. A small template you can use to document any of your projects.**
14 |
15 |
16 |
17 | 
18 |
19 |
20 | [](https://github.com/conedevelopment/sprucecss-eleventy-documentation-template/releases/latest)
21 | [](https://github.com/conedevelopment/sprucecss-eleventy-documentation-template/blob/main/LICENSE)
22 |
23 | A documentation template is always helpful. There are a lot of solutions to make one; we wanted to create our self-hosted version based on our favorite static site generator [Eleventy](https://www.11ty.dev/).
24 |
25 | By structure, it is simple, with two levels and additional custom templates like [FAQ]([/faq/](https://eleventy-documentation.sprucecss.com/faq/)) and [Changelog]([/changelog/](https://eleventy-documentation.sprucecss.com/changelog/)).
26 |
27 | ## Spruce CSS
28 |
29 | The template is built on [Spruce CSS](https://sprucecss.com/), a small and customizable CSS framework. The main benefit of this is that you can use the Spruce UI components with dark mode and RTL support.
30 |
31 | ## Features
32 |
33 | - Breadcrumb navigation built on [11ty Navigation Plugin](https://www.11ty.dev/docs/plugins/navigation/).
34 | - HTML minification in production mode.
35 | - Anchor headings.
36 | - Table of Content.
37 | - FAQ template.
38 | - Changelog template.
39 | - Static search integration with [pagefind](https://pagefind.app/).
40 | - Code highlighting.
41 | - RTL support.
42 | - Dark theme mode.
43 | - [svgIcon](https://github.com/conedevelopment/sprucecss-eleventy-documentation-template/blob/main/src/shortcodes/svg-icon.js) shortcode: render any SVG icon inline and add optional classes.
44 | - [markdownRenderer](https://github.com/conedevelopment/sprucecss-eleventy-documentation-template/blob/main/src/shortcodes/markdown-render.js): render any string (markdown) into HTML.
45 |
46 | ## Setup
47 |
48 | 1. **Clone the repository.**
49 |
50 | 2. **Install the dependencies.**
51 |
52 | In the `package.json` file, you will find all of the dependencies (and scripts) to install them using the following command:
53 |
54 | ```shell
55 | npm install
56 | ```
57 |
58 | 3. **Run the development mode**
59 |
60 | To run the development mode, use the `npm script`. This script will also watch for changes.
61 |
62 | ```shell
63 | npm start
64 | ```
65 |
66 | 4. **Run the production mode**
67 |
68 | Before you go live, you should use the production script to compress the Sass files.
69 |
70 | ```shell
71 | npm run prod
72 | ```
73 |
74 | You can find some more npm scripts in the [package.json](https://github.com/conedevelopment/sprucecss-eleventy-documentation-template/blob/main/package.json) that can be helpful.
75 |
76 | ## Content Managment
77 |
78 | Adding content to the template is easy as almost everything is in Eleventy.
79 |
80 | ### The Basic Structure
81 |
82 | Our base folder for the documentation pages is the `posts` folder. You must follow the folder structure, which means the `category` here. If you create a folder, you must make a list page with the same name as the folder. You must also create another `posts` folder under the `category` folder where your posts go. You must create the `posts.json` file that will parameter your `layout` and `permalink` values.
83 |
84 | ### Eleventy Navigation
85 |
86 | The theme utilizes the [Eleventy Navigation plugin](https://www.11ty.dev/docs/plugins/navigation/), so you must explicitly set up the hierarchy. This is needed for the automatic sidebar navigation, the navigation order, and breadcrumb generation.
87 |
88 | ### Other Pages
89 |
90 | To create simple pages, make a file directly under the `src` folder and configure it with the available front matter.
91 |
92 | ## Structure
93 |
94 | ```html
95 | spruecss-eleventy-documentation-template/
96 | ├─ node_modules/
97 | ├─ dist/
98 | ├─ src/
99 | │ ├─ _data/
100 | │ ├─ _includes/
101 | │ ├─ css/
102 | │ ├─ filters/
103 | │ ├─ font/
104 | │ ├─ img/
105 | │ ├─ js/
106 | │ ├─ posts/
107 | │ ├─ scss/
108 | │ ├─ shortcodes/
109 | │ ├─ transforms/
110 | │ ├─ changelog.md
111 | │ ├─ faq.md
112 | │ ├─ index.md
113 | ├─ .eleventy.js
114 | ├─ package.json
115 | ├─ README.md
116 | ├─ ...
117 |
118 | ```
119 |
120 | - **_data**: Some global data, like the name of your site and helpers like the active navigation element or current year.
121 | - **__includes**: All of the layout and partial templates.
122 | - **css**: The compiled CSS.
123 | - **filters**: The additional filters that you can use.
124 | - **font**: The custom fonts.
125 | - **img**: The static image files.
126 | - **posts**: The markdown contents.
127 | - **scss**: The Sass files.
128 | - **shortcodes**: The available shortcodes.
129 | - **transforms**: The transformations.
130 |
131 |
132 |
133 | [](https://app.netlify.com/sites/sprucecss-eleventy-documentation/deploys)
134 |
--------------------------------------------------------------------------------
/src/img/cone-docs-logo.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/.github/spruce-logo-dark.svg:
--------------------------------------------------------------------------------
1 |
21 |
--------------------------------------------------------------------------------
/.github/spruce-logo-light.svg:
--------------------------------------------------------------------------------
1 |
21 |
--------------------------------------------------------------------------------
/src/css/main.css:
--------------------------------------------------------------------------------
1 | @font-face{font-display:swap;font-family:"Manrope";font-style:normal;font-weight:400;src:url("../../font/manrope-v14-latin-regular.woff2") format("woff2")}@font-face{font-display:swap;font-family:"Manrope";font-style:normal;font-weight:600;src:url("../../font/manrope-v14-latin-600.woff2") format("woff2")}@font-face{font-display:swap;font-family:"Manrope";font-style:normal;font-weight:700;src:url("../../font/manrope-v14-latin-800.woff2") format("woff2")}@font-face{font-display:swap;font-family:"Open Sans";font-style:normal;font-weight:400;src:url("../../font/open-sans-v35-latin-regular.woff2") format("woff2")}@font-face{font-display:swap;font-family:"Open Sans";font-style:normal;font-weight:700;src:url("../../font/open-sans-v35-latin-700.woff2") format("woff2")}/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;block-size:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:rgba(0,0,0,0)}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-inline-size:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{block-size:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}:root{--spruce-alert-color-danger: hsl(0, 71%, 51%);--spruce-alert-color-info: hsl(195, 100%, 42%);--spruce-alert-color-success: hsl(150, 100%, 33%);--spruce-alert-color-warning: hsl(48, 89%, 55%)}:root{--spruce-base-color-background: hsl(0, 0%, 100%);--spruce-base-color-blockquote-border: hsl(238, 100%, 50%);--spruce-base-color-border: hsla(0, 0%, 0%, 0.05);--spruce-base-color-code-background: hsl(224, 94%, 97%);--spruce-base-color-code-foreground: hsl(245, 31%, 21%);--spruce-base-color-heading: hsl(232, 68%, 11%);--spruce-base-color-link: hsl(238, 100%, 50%);--spruce-base-color-link-hover: #0007cc;--spruce-base-color-mark-background: hsl(50, 100%, 80%);--spruce-base-color-mark-foreground: hsl(245, 31%, 21%);--spruce-base-color-marker: hsl(238, 100%, 50%);--spruce-base-color-primary: hsl(238, 100%, 50%);--spruce-base-color-secondary: hsl(186, 100%, 60%);--spruce-base-color-strong: hsl(245, 31%, 21%);--spruce-base-color-text: hsl(208, 21%, 39%)}:root{--spruce-btn-color-primary-background: hsl(238, 100%, 50%);--spruce-btn-color-primary-background-hover: #0007cc;--spruce-btn-color-primary-foreground: hsl(0, 0%, 100%);--spruce-btn-color-primary-shadow: #b3b5ff;--spruce-btn-color-secondary-background: hsl(186, 100%, 60%);--spruce-btn-color-secondary-background-hover: #00e6ff;--spruce-btn-color-secondary-foreground: #002e33;--spruce-btn-color-secondary-shadow: #99f5ff}:root{--spruce-form-color-background: hsl(0, 0%, 100%);--spruce-form-color-background-disabled: hsl(0, 0%, 95%);--spruce-form-color-border: hsl(260, 4%, 70%);--spruce-form-color-border-disabled: hsl(215, 63%, 93%);--spruce-form-color-border-focus: hsl(238, 100%, 50%);--spruce-form-color-check-background: hsl(238, 100%, 50%);--spruce-form-color-check-focus-ring: hsl(238, 100%, 50%);--spruce-form-color-check-foreground: hsl(0, 0%, 100%);--spruce-form-color-group-label-background: hsl(210, 60%, 98%);--spruce-form-color-group-label-foreground: hsl(208, 9%, 42%);--spruce-form-color-invalid: hsl(0, 71%, 51%);--spruce-form-color-invalid-focus-ring: rgba(219, 41, 41, 0.25);--spruce-form-color-label: hsl(245, 31%, 21%);--spruce-form-color-legend: hsl(245, 31%, 21%);--spruce-form-color-placeholder: hsl(208, 7%, 40%);--spruce-form-color-range-thumb-background: hsl(238, 100%, 50%);--spruce-form-color-range-thumb-focus-ring: hsl(238, 100%, 50%);--spruce-form-color-range-track-background: hsl(215, 63%, 93%);--spruce-form-color-ring-focus: rgba(0, 9, 255, 0.25);--spruce-form-color-select-foreground: hsl(245, 31%, 21%);--spruce-form-color-text: hsl(208, 9%, 42%);--spruce-form-color-valid: hsl(150, 100%, 33%);--spruce-form-color-valid-focus-ring: rgba(0, 168, 84, 0.25)}:root{--spruce-selection-color-foreground: hsl(0, 0%, 100%);--spruce-selection-color-background: hsl(238, 100%, 50%)}:root{--spruce-scrollbar-color-thumb-background: hsla(229, 26%, 48%, 0.15);--spruce-scrollbar-color-thumb-background-hover: hsla(0, 0%, 0%, 0.25);--spruce-scrollbar-color-track-background: hsla(226, 100%, 87%, 0.15)}:root{--spruce-table-color-border: hsl(215, 63%, 93%);--spruce-table-color-caption: hsl(208, 9%, 42%);--spruce-table-color-heading: hsl(245, 31%, 21%);--spruce-table-color-hover: hsla(0, 0%, 0%, 0.05);--spruce-table-color-stripe: hsla(0, 0%, 0%, 0.025);--spruce-table-color-text: hsl(208, 9%, 42%)}:root{--spruce-breadcrumb-color-separator: hsl(0, 0%, 80%)}:root{--spruce-card-color-background: hsl(0, 0%, 100%)}:root{--spruce-footer-color-background: hsl(224, 94%, 98%)}:root{--spruce-navigation-color-arrow: hsla(0, 0%, 0%, 0.15);--spruce-navigation-color-icon-background: hsl(224, 94%, 98%);--spruce-navigation-color-icon-background-hover: hsl(238, 100%, 50%);--spruce-navigation-color-icon-foreground: hsl(238, 100%, 50%);--spruce-navigation-color-icon-foreground-hover: hsl(0, 0%, 100%)}:root{--spruce-search-color-icon: hsla(229, 26%, 48%, 0.25)}:root{--spruce-prism-color-color: hsl(243, 14%, 29%);--spruce-prism-color-background: hsl(224, 94%, 99%);--spruce-prism-color-comment: hsl(225, 14%, 46%);--spruce-prism-color-punctuation: hsl(279, 50%, 53%);--spruce-prism-color-namespace: hsl(173, 100%, 24%);--spruce-prism-color-deleted: hsla(1, 83%, 63%, 0.56);--spruce-prism-color-boolean: hsl(0, 44%, 53%);--spruce-prism-color-number: hsl(315, 90%, 35%);--spruce-prism-color-constant: hsl(221, 57%, 52%);--spruce-prism-color-class-name: hsl(0, 0%, 7%);--spruce-prism-color-regex: hsl(1, 48%, 59%)}:root{--spruce-border-radius: 0.425rem;--spruce-font-family-base: Open Sans, sans-serif;--spruce-font-family-cursive: ui-monospace, Cascadia Code, Source Code Pro, Menlo, Consolas, DejaVu Sans Mono, monospace;--spruce-font-family-heading: Manrope, sans-serif;--spruce-font-size-base: 1rem;--spruce-font-size-lead: clamp(1.1rem, 2vw, 1.25rem);--spruce-font-size-lg: 1.125rem;--spruce-font-size-ratio: 1.25;--spruce-font-size-sm: 0.875rem;--spruce-font-weight-heading: 700;--spruce-inline-padding: 0.1em 0.3em;--spruce-line-height-base: 1.8;--spruce-line-height-heading: calc(2px + 2ex + 2px);--spruce-line-height-lg: 1.8;--spruce-line-height-md: 1.5;--spruce-line-height-sm: 1.2;--spruce-border-radius-lg: 0.925rem;--spruce-border-radius-sm: 0.425rem;--spruce-container-inline-size: 84rem;--spruce-page-margin: 2cm;--spruce-hidden-elements: header, footer, aside, nav, form, iframe, [class^="aspect-ratio"]}@media(prefers-reduced-motion: no-preference){:root{--spruce-duration: 0.15s;--spruce-timing-function: ease-in-out}}.sr-only{block-size:1px !important;border:0 !important;clip:rect(0, 0, 0, 0) !important;inline-size:1px !important;margin:-1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;white-space:nowrap !important}[tabindex="-1"]:focus{outline:none !important}::selection{background-color:var(--spruce-selection-color-background);color:var(--spruce-selection-color-foreground);text-shadow:none}html{box-sizing:border-box}@media(prefers-reduced-motion: no-preference){html{scroll-behavior:smooth}}*,::before,::after{box-sizing:inherit}body{background:var(--spruce-base-color-background);color:var(--spruce-base-color-text)}a{color:var(--spruce-base-color-link);text-decoration:underline;transition-duration:var(--spruce-duration);transition-property:color;transition-timing-function:var(--spruce-timing-function)}a:hover{color:var(--spruce-base-color-link-hover)}button{color:inherit}a,button{touch-action:manipulation}hr{border:0;border-block-start:1px solid var(--spruce-base-color-border)}img{block-size:auto;display:block;max-inline-size:100%;user-select:none}iframe{block-size:100%;display:block;inline-size:100%}figure{margin-inline:0}figure figcaption{margin-block-start:.5rem;text-align:center}.table-responsive{--inline-size: 40rem;-webkit-overflow-scrolling:touch;overflow-x:auto}.table-responsive table{min-inline-size:var(--inline-size)}.table{--spruce-line-height: 1.5;--spruce-padding: 1rem;--spruce-responsive-inline-size: 40rem;border-collapse:collapse;color:var(--spruce-table-color-text);inline-size:100%}.table caption{color:var(--spruce-table-color-caption);margin-block-end:1rem}.table th,.table td{border-block-end:1px solid var(--spruce-table-color-border);line-height:var(--spruce-line-height);padding:var(--spruce-padding)}.table th{color:var(--spruce-table-color-heading);text-align:inherit;text-align:-webkit-match-parent}.table--striped>tbody>tr:nth-child(odd){background-color:var(--spruce-table-color-stripe)}.table--hover>tbody>tr:hover{background:var(--spruce-table-color-hover)}.table--clear-border th,.table--clear-border td{border:0}.table--in-line th:first-child,.table--in-line td:first-child{padding-inline-start:0}.table--in-line th:last-child,.table--in-line td:last-child{padding-inline-end:0}.table--sm{--spruce-padding: 0.5rem}.table--sm th,.table--sm td{padding:var(--spruce-padding)}.table--rounded th:first-child,.table--rounded td:first-child{border-end-start-radius:var(--spruce-border-radius-sm);border-start-start-radius:var(--spruce-border-radius-sm)}.table--rounded th:last-child,.table--rounded td:last-child{border-end-end-radius:var(--spruce-border-radius-sm);border-start-end-radius:var(--spruce-border-radius-sm)}html{-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:var(--spruce-font-family-base);font-size:var(--spruce-font-size-base);line-height:var(--spruce-line-height-base)}p,li,h1,h2,h3,h4,h5,h6{hyphens:auto;overflow-wrap:break-word}h1,h2,h3,h4,h5,h6{color:var(--spruce-base-color-heading);font-family:var(--spruce-font-family-heading);font-weight:var(--spruce-font-weight-heading);line-height:var(--spruce-line-height-heading)}h1{font-size:clamp(2.0751953125rem, 2vw + 1rem, 2.44140625rem)}h2{font-size:clamp(1.66015625rem, 2vw + 1rem, 1.953125rem)}h3{font-size:clamp(1.328125rem, 2vw + 1rem, 1.5625rem)}h4{font-size:clamp(1.0625rem, 2vw + 1rem, 1.25rem)}h5{font-size:1rem}h6{font-size:1rem}ul,ol{list-style-position:inside}ul>*,ol>*{margin-block-end:0;margin-block-start:0}ul>*+*,ol>*+*{margin-block-start:.25rem}ul li,ol li{list-style-position:outside}ul li::marker,ol li::marker{color:var(--spruce-base-color-marker)}li>ul,li>ol{margin-block-start:.25rem}dl dt{color:var(--spruce-base-color-heading);font-weight:bold}dl dd{margin:0}dl dd+dt{margin-block-start:1rem}.quote{border-inline-start:.5rem solid var(--spruce-base-color-blockquote-border);padding-inline-start:1.5rem}.quote>*{margin-block-end:0;margin-block-start:0}.quote>*+*{margin-block-start:.5rem}.quote blockquote{border-inline-start:0;padding-inline-start:0}.quote figcaption{text-align:start}blockquote{border-inline-start:.5rem solid var(--spruce-base-color-blockquote-border);margin-inline-start:0;padding-inline-start:1.5rem}blockquote>*{margin-block-end:0;margin-block-start:0}blockquote>*+*{margin-block-start:.5rem}abbr[title]{border-block-end:1px dotted;cursor:help;text-decoration:none}mark{background-color:var(--spruce-base-color-mark-background);border-radius:var(--spruce-border-radius);color:var(--spruce-base-color-mark-foreground);padding:var(--spruce-inline-padding)}code,kbd,samp{background-color:var(--spruce-base-color-code-background);border-radius:var(--spruce-border-radius);color:var(--spruce-base-color-code-foreground);padding:var(--spruce-inline-padding)}strong{color:var(--spruce-base-color-strong)}.lead{font-size:var(--spruce-font-size-lead)}.hidden,[hidden]{display:none !important}.h1{font-size:clamp(2.0751953125rem, 2vw + 1rem, 2.44140625rem)}.h2{font-size:clamp(1.66015625rem, 2vw + 1rem, 1.953125rem)}.h3{font-size:clamp(1.328125rem, 2vw + 1rem, 1.5625rem)}.h4{font-size:clamp(1.0625rem, 2vw + 1rem, 1.25rem)}.h5{font-size:1rem}.h6{font-size:1rem}.btn{--spruce-border-radius: 0.425rem;--spruce-border-width: 1px;--spruce-font-family: Manrope, sans-serif;--spruce-font-size: 1rem;--spruce-font-weight: 700;--spruce-gap: 0.5rem;--spruce-icon-padding: 0.75em;--spruce-icon-size: 1em;--spruce-padding: 0.75em 1.5em;--spruce-shadow-size: 0.25rem;align-items:center;border-radius:var(--spruce-border-radius);border-style:solid;border-width:var(--spruce-border-width);cursor:pointer;display:inline-flex;font-family:var(--spruce-font-family);font-size:var(--spruce-font-size);font-weight:var(--spruce-font-weight);gap:var(--spruce-gap);justify-content:center;line-height:1;padding:var(--spruce-padding);text-align:start;text-decoration:none;transition-duration:var(--spruce-duration);transition-property:background-color,border-color,box-shadow,color;transition-timing-function:var(--spruce-timing-function)}.btn:focus{outline-color:rgba(0,0,0,0);outline-style:solid}.btn:disabled{opacity:.5;pointer-events:none}.btn--icon{padding:var(--spruce-icon-padding)}.btn--icon.btn--sm{padding:var(--spruce-icon-padding)}.btn--icon.btn--lg{padding:var(--spruce-icon-padding)}.btn__icon{block-size:var(--spruce-icon-size);flex-shrink:0;inline-size:var(--spruce-icon-size);pointer-events:none}.btn__icon--sm{block-size:var(--spruce-icon-size);inline-size:var(--spruce-icon-size)}.btn--sm{--spruce-font-size: 0.8rem;--spruce-gap: 0.25rem;--spruce-icon-padding: 0.5em;--spruce-icon-size: 0.8rem;--spruce-padding: 0.5em 0.75em;font-size:var(--spruce-font-size);gap:var(--spruce-gap);padding:var(--spruce-padding)}.btn--lg{--spruce-font-size: 1rem;--spruce-gap: 0.5rem;--spruce-icon-padding: 0.9em;--spruce-padding: 1em 1.75em}.btn--block{inline-size:100%}.btn--primary{background-color:var(--spruce-btn-color-primary-background);border-color:var(--spruce-btn-color-primary-background);color:var(--spruce-btn-color-primary-foreground)}.btn--primary:focus-visible{outline:2px solid var(--spruce-btn-color-primary-background);outline-offset:2px}.btn--primary:hover{background-color:var(--spruce-btn-color-primary-background-hover);border-color:var(--spruce-btn-color-primary-background-hover);color:var(--spruce-btn-color-primary-foreground)}.btn--primary-shadow{box-shadow:0 .55em 1em -0.2em var(--spruce-btn-color-primary-shadow),0 .15em .35em -0.185em var(--spruce-btn-color-primary-shadow)}.btn--secondary{background-color:var(--spruce-btn-color-secondary-background);border-color:var(--spruce-btn-color-secondary-background);color:var(--spruce-btn-color-secondary-foreground)}.btn--secondary:focus-visible{outline:2px solid var(--spruce-btn-color-secondary-background);outline-offset:2px}.btn--secondary:hover{background-color:var(--spruce-btn-color-secondary-background-hover);border-color:var(--spruce-btn-color-secondary-background-hover);color:var(--spruce-btn-color-secondary-foreground)}.btn--secondary-shadow{box-shadow:0 .55em 1em -0.2em var(--spruce-btn-color-secondary-shadow),0 .15em .35em -0.185em var(--spruce-btn-color-secondary-shadow)}.btn--outline-primary{background-color:rgba(0,0,0,0);border-color:var(--spruce-btn-color-primary-background);color:var(--spruce-btn-color-primary-background)}.btn--outline-primary:focus-visible{outline:2px solid var(--spruce-btn-color-primary-background);outline-offset:2px}.btn--outline-primary:hover{background-color:var(--spruce-btn-color-primary-background);border-color:var(--spruce-btn-color-primary-background);color:var(--spruce-btn-color-primary-foreground)}.btn--outline-secondary{background-color:rgba(0,0,0,0);border-color:var(--spruce-btn-color-secondary-background);color:var(--spruce-btn-color-secondary-background)}.btn--outline-secondary:focus-visible{outline:2px solid var(--spruce-btn-color-secondary-background);outline-offset:2px}.btn--outline-secondary:hover{background-color:var(--spruce-btn-color-secondary-background);border-color:var(--spruce-btn-color-secondary-background);color:var(--spruce-btn-color-secondary-foreground)}.form-group-label{--spruce-border-radius: 0.425rem;--spruce-border-width: 1px;align-items:center;background-color:var(--spruce-form-color-group-label-background);border:var(--spruce-border-width) solid var(--spruce-form-color-border);border-radius:var(--spruce-border-radius);color:var(--spruce-form-color-group-label-foreground);display:flex;padding-inline:1rem}:root[data-theme-mode=dark]{--spruce-base-color-background: hsl(245, 38%, 11%);--spruce-base-color-blockquote-border: hsl(186, 100%, 60%);--spruce-base-color-border: hsla(0, 0%, 100%, 0.08);--spruce-base-color-card-border: hsl(207, 90%, 13%);--spruce-base-color-code-background: hsl(207, 64%, 21%);--spruce-base-color-code-foreground: hsl(0, 0%, 95%);--spruce-base-color-footer-background: hsla(0, 0%, 0%, 0.15);--spruce-base-color-heading: hsl(0, 0%, 95%);--spruce-base-color-link-hover: #47edff;--spruce-base-color-link: hsl(186, 100%, 60%);--spruce-base-color-mark-background: hsl(50, 100%, 80%);--spruce-base-color-mark-foreground: hsl(245, 38%, 11%);--spruce-base-color-marker: hsl(186, 100%, 60%);--spruce-base-color-primary: hsl(186, 100%, 60%);--spruce-base-color-secondary: hsl(227, 92%, 55%);--spruce-base-color-text: hsl(0, 0%, 97%)}:root[data-theme-mode=dark]{--spruce-breadcrumb-color-arrow: hsla(0, 0%, 100%, 0.1)}:root[data-theme-mode=dark]{--spruce-btn-color-primary-background: hsl(186, 100%, 60%);--spruce-btn-color-primary-background-hover: hsl(186, 100%, 45%);--spruce-btn-color-primary-foreground: hsl(186, 100%, 5%);--spruce-btn-color-primary-shadow: hsl(186, 100%, 25%);--spruce-btn-color-secondary-background: hsl(227, 92%, 55%);--spruce-btn-color-secondary-background-hover: #3b64f7;--spruce-btn-color-secondary-foreground: hsl(0, 0%, 95%);--spruce-btn-color-secondary-shadow: #072bab}:root[data-theme-mode=dark]{--spruce-card-color-background: hsl(245, 38%, 11%)}:root[data-theme-mode=dark]{--spruce-container-color-background: hsl(207, 92%, 12%)}:root[data-theme-mode=dark]{--spruce-form-color-background: #1b1836;--spruce-form-color-background-disabled: hsl(245, 38%, 11%);--spruce-form-color-border-disabled: hsla(0, 0%, 100%, 0.08);--spruce-form-color-border-focus: hsl(186, 100%, 60%);--spruce-form-color-border: hsla(0, 0%, 100%, 0.08);--spruce-form-color-check-background: hsl(186, 100%, 60%);--spruce-form-color-check-foreground: hsl(245, 38%, 11%);--spruce-form-color-invalid: hsl(0, 71%, 51%);--spruce-form-color-invalid-shadow: rgba(219, 41, 41, 0.25);--spruce-form-color-label: hsl(0, 0%, 95%);--spruce-form-color-legend: hsl(0, 0%, 95%);--spruce-form-color-placeholder: hsl(0, 0%, 60%);--spruce-form-color-select-foreground: hsl(0, 0%, 100%);--spruce-form-color-shadow-focus: rgba(51, 235, 255, 0.25);--spruce-form-color-text: hsl(0, 0%, 97%);--spruce-form-color-valid: hsl(150, 100%, 33%);--spruce-form-color-valid-shadow: rgba(0, 168, 84, 0.25)}:root[data-theme-mode=dark]{--spruce-footer-color-background: hsl(245, 38%, 10%)}:root[data-theme-mode=dark]{--spruce-navigation-color-arrow: hsla(0, 0%, 100%, 0.15);--spruce-navigation-color-icon-background: hsl(245, 38%, 10%);--spruce-navigation-color-icon-background-hover: hsl(186, 100%, 60%);--spruce-navigation-color-icon-foreground: hsl(186, 100%, 60%);--spruce-navigation-color-icon-foreground-hover: hsl(186, 100%, 5%)}:root[data-theme-mode=dark]{--spruce-prism-color-color: hsl(217, 34%, 88%);--spruce-prism-color-background: hsl(245, 38%, 7%);--spruce-prism-color-comment: hsl(180, 9%, 55%);--spruce-prism-color-punctuation: hsl(276, 68%, 75%);--spruce-prism-color-namespace: hsl(197, 31%, 77%);--spruce-prism-color-deleted: hsla(1, 83%, 63%, 0.56);--spruce-prism-color-boolean: hsl(350, 100%, 67%);--spruce-prism-color-number: hsl(14, 90%, 70%);--spruce-prism-color-constant: hsl(221, 100%, 75%);--spruce-prism-color-class-name: hsl(33, 100%, 77%);--spruce-prism-color-regex: hsl(217, 34%, 88%)}:root[data-theme-mode=dark]{--spruce-table-color-border: hsla(0, 0%, 100%, 0.08);--spruce-table-color-caption: hsl(0, 0%, 97%);--spruce-table-color-heading: hsl(0, 0%, 95%);--spruce-table-color-hover: hsla(0, 0%, 100%, 0.05);--spruce-table-color-stripe: hsla(0, 0%, 100%, 0.025);--spruce-table-color-text: hsl(0, 0%, 97%)}:root[data-theme-mode=dark]{--spruce-selection-color-background: hsl(186, 100%, 60%);--spruce-selection-color-foreground: hsl(186, 100%, 5%)}:root[data-theme-mode=dark]{--spruce-search-color-icon: hsla(0, 0%, 100%, 0.25)}:root[data-theme-mode=dark]{--spruce-scrollbar-color-thumb-background: hsla(0, 0%, 100%, 0.15);--spruce-scrollbar-color-thumb-background-hover: hsla(0, 0%, 100%, 0.25);--spruce-scrollbar-color-track-background: hsla(0, 0%, 100%, 0.05)}[data-theme-mode=dark]{color-scheme:dark}[data-theme-mode=dark] select.form-control:not([multiple]):not([size]){background-image:url('data:image/svg+xml,%3csvg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"%3e%3cpath d="M12,12.507l-3.816,-3.815c-0.171,-0.172 -0.45,-0.172 -0.622,-0l-0.933,0.933c-0.172,0.172 -0.172,0.451 0,0.623l5.06,5.06c0.172,0.172 0.45,0.172 0.622,0l5.06,-5.06c0.172,-0.172 0.172,-0.451 -0,-0.623l-0.933,-0.933c-0.172,-0.172 -0.451,-0.172 -0.622,-0l-3.816,3.815Z" style="fill:hsl%280, 0%, 100%%29;"/%3e%3c/svg%3e')}.container{--inline-size: var(--spruce-container-inline-size);--gap: var(--spruce-container-gap);inline-size:100%;margin-inline:auto;max-inline-size:var(--inline-size);padding-inline:var(--gap)}.container--wide{--inline-size: 94rem}.container--narrow{--inline-size: 50rem}.l-main{--gtc: minmax(0, 1fr);display:grid;gap:3rem;grid-template-columns:var(--gtc);margin-block:clamp(1.5rem, 5vw, 3rem) clamp(3rem, 5vw, 4.5rem)}@media(min-width: 48em){.l-main{--gtc: minmax(0, 16rem) minmax(0, 1fr);gap:calc(var(--spruce-container-gap) * 1.5)}}@media(min-width: 64em){.l-main{--gtc: minmax(0, 18.5rem) minmax(0, 1fr)}}@media(min-width: 80em){.l-main{--gtc: minmax(0, 20rem) minmax(0, 1fr)}}.l-main__sidebar{display:none !important}@media(min-width: 48em){.l-main__sidebar{border-inline-end:1px solid var(--spruce-base-color-border);display:flex !important;inset-block-start:3rem;max-block-size:calc(100vh - 3rem * 2);position:sticky}}.l-main__sidebar--open{display:flex !important}.l-main__body{display:flex;flex-direction:column;inline-size:100%;margin-inline:auto;max-inline-size:var(--spruce-max-content-inline-size)}.l-card{--columns: 1;counter-reset:card;display:grid;gap:3rem;grid-template-columns:repeat(var(--columns), minmax(0, 1fr))}@media(min-width: 80em){.l-card{--columns: 2}}.l-front-page{display:grid;gap:4.5rem;grid-template-columns:minmax(0, 1fr)}.l-post{--gtc: minmax(0, 1fr);display:grid;gap:var(--spruce-container-gap);grid-template-columns:var(--gtc)}@media(min-width: 80em){.l-post{--gtc: minmax(0, 1fr) minmax(0, 15rem)}}@media(min-width: 80em){.l-post__toc{order:2}}.l-post__toc .toc{inset-block-start:3rem;position:sticky}@media(min-width: 80em){.l-post .post-heading{grid-column:1/3}}.l-list{--gtc: minmax(0, 1fr);display:grid;gap:3rem;grid-template-columns:var(--gtc)}.l-list__inner--changelog>*,.l-list__inner--faq>*{margin-block-end:0;margin-block-start:0}.l-list__inner--changelog>*+*,.l-list__inner--faq>*+*{margin-block-start:3rem}.header{align-items:center;border-block-end:1px solid var(--spruce-base-color-border);display:flex;flex-wrap:wrap;gap:1rem clamp(1.5rem, 5vw, 3rem);justify-content:space-between;order:2;padding-block:1.75rem}@media(min-width: 48em){.header{order:-1}}.header__column{align-items:center;display:flex;flex-grow:1;gap:clamp(1.5rem, 5vw, 3rem);justify-content:space-between}@media(min-width: 48em){.header__column{justify-content:flex-start}}.header__logo{display:inline-flex}.header__logo img,.header__logo svg{block-size:2rem;display:inline-flex;inline-size:auto}.header__toggle{--spruce-border-radius:1rem 1rem 0 1rem}@media(min-width: 48em){.header__toggle{display:none}}.header__actions{align-items:center;display:flex;flex-wrap:wrap;gap:clamp(1rem, 5vw, 1.5rem);margin-inline-start:auto}.header__navigation{display:none}@media(min-width: 48em){.header__navigation{display:flex}}.header__navigation ul{list-style:none;margin:0;padding:0;align-items:center;background-color:rgba(0,0,0,0);display:flex;flex-direction:row;flex-wrap:wrap;gap:.5rem 1.5rem;inset:auto;padding:0;position:relative}@media(min-width: 80em){.header__navigation ul{gap:.5rem 3rem}}.header__navigation li{margin-block:0}.header__navigation a{align-items:center;color:var(--spruce-base-color-heading);display:flex;gap:.5rem;text-decoration:none}.header__navigation a:hover{color:var(--spruce-base-color-primary)}.header__navigation a[aria-current=page]{font-weight:700}.header__navigation svg{--dimension: 0.65em;block-size:var(--dimension);color:var(--spruce-navigation-color-arrow);inline-size:var(--dimension)}.header__socials{align-items:center;display:flex;gap:1rem}.header__socials a{color:var(--spruce-base-color-heading);display:inline-flex}.header__socials a:hover,.header__socials a:focus{color:var(--spruce-base-color-primary)}.header__socials svg{--dimension: 1.25rem;block-size:var(--dimension);inline-size:var(--dimension)}.footer{--logo-block-size: 4rem;--logo-inline-size: 4rem;border-block-start:1px solid var(--spruce-base-color-border);margin-block-start:clamp(3rem, 5vw, 4.5rem);padding-block-start:clamp(3rem, 5vw, 4.5rem)}.footer__inner{display:grid;gap:3rem}@supports(inline-size: min(12rem, 100%)){.footer__inner{grid-template-columns:repeat(auto-fit, minmax(min(12rem, 100%), 1fr))}}.footer__logo{block-size:var(--logo-block-size);display:flex;inline-size:var(--logo-inline-size)}.footer__title{font-size:clamp(1.0625rem, 2vw + 1rem, 1.25rem)}.footer__navigation{list-style:none;margin:0;padding:0}.footer__navigation>*{margin-block-end:0;margin-block-start:0}.footer__navigation>*+*{margin-block-start:.5rem}.footer__navigation a{align-items:center;color:var(--spruce-base-color-text);display:inline-flex;gap:.5rem;text-decoration:none}.footer__navigation a:hover,.footer__navigation a:focus,.footer__navigation a:active,.footer__navigation a[aria-current=page]{color:var(--spruce-base-color-primary)}.footer__navigation a[aria-current=page]{font-weight:700}.footer__navigation a svg{--dimension: 0.65em;block-size:var(--dimension);color:var(--spruce-navigation-color-arrow);inline-size:var(--dimension)}.footer__column>*{margin-block-end:0;margin-block-start:0}.footer__column>*+*{margin-block-start:1.5rem}.skip-link{inset:-50vh auto auto 1.5rem;position:fixed;z-index:100}.skip-link:focus{inset-block-start:1.5rem}.breadcrumb-list{list-style:none;margin:0;padding:0;align-items:center;display:flex;max-inline-size:100%;overflow-x:auto;white-space:nowrap}.breadcrumb-list>li{align-items:center;display:inline-flex;margin-block:0}.breadcrumb-list>li+li::before{block-size:.4em;border-block-end:2px solid var(--spruce-breadcrumb-color-separator);border-inline-end:2px solid var(--spruce-breadcrumb-color-separator);content:"";display:inline-flex;inline-size:.4em;margin-inline:.75em;transform:rotate(-45deg)}[dir=rtl] .breadcrumb-list>li+li::before{transform:rotate(45deg)}.breadcrumb-list a{text-decoration:none}.breadcrumb-list [aria-current=page]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block;max-inline-size:20ch;text-align:start}.open-search{position:relative;align-items:center;display:flex;gap:.5rem}.open-search__btn::before{content:"";inset:0;position:absolute}.open-search__icon{--dimension: 1rem;block-size:var(--dimension);color:var(--spruce-search-color-icon);inline-size:var(--dimension)}.no-transition *{transition:none !important}.theme-switcher{color:var(--spruce-base-color-text);display:inline-flex;position:relative}.theme-switcher[data-theme-mode=system] .theme-switcher__system-mode{display:flex}.theme-switcher[data-theme-mode=light] .theme-switcher__light-mode{display:flex}.theme-switcher[data-theme-mode=dark] .theme-switcher__dark-mode{display:flex}.theme-switcher button{display:none}.theme-switcher button>*{pointer-events:none}.post-heading>*{margin-block-end:0;margin-block-start:0}.post-heading>*+*{margin-block-start:1rem}.post-heading__headline{color:var(--spruce-base-color-primary);font-family:var(--spruce-font-family-heading);font-weight:700;letter-spacing:.1em;text-transform:uppercase}.post-heading__title{font-size:clamp(2.45rem, 4.5vw, 3.5rem);line-height:var(--spruce-line-height-sm);margin-block-start:.5rem;max-inline-size:20ch}.post-heading__description{font-size:var(--spruce-font-size-lead);max-inline-size:60ch}.post-heading__actions{display:flex;flex-wrap:wrap;gap:1rem;margin-block-start:1.5rem}.post-content>*{margin-block-end:0;margin-block-start:0}.post-content>*+*{margin-block-start:1.5rem}@media(min-width: 64em){.post-content{font-size:1.0375rem}}.post-content :is(dd,dl,dl,h1,h2,h3:not(.accordion-card__title),h4,h5,h6,hr,ul,ol,p,blockquote){max-inline-size:40rem}.post-content *+h2,.post-content *+h3{margin-top:3rem}.post-content h2+*,.post-content h3+*,.post-content h4+*,.post-content h5+*,.post-content h6+*{margin-top:1rem}.post-content h2[id]{align-items:flex-start;display:flex;justify-content:space-between}.post-content h2[id]:hover .anchor,.post-content h2[id]:focus-within .anchor{opacity:1}.post-content h2[id] .anchor{font-weight:600;opacity:0;text-decoration:none}.post-content img,.post-content iframe{border-radius:var(--spruce-border-radius-sm)}.post-content iframe{aspect-ratio:16/9}.post-content a>code{color:var(--spruce-base-color-link)}.post-content strong{color:var(--spruce-base-color-heading)}.post-content picture{display:flex}.toc>*{margin-block-end:0;margin-block-start:0}.toc>*+*{margin-block-start:1rem}.toc__title{font-size:clamp(1.0625rem, 2vw + 1rem, 1.25rem)}.toc__navigation ol{list-style:none;margin:0;padding:0}.toc__navigation ol>*{margin-block-end:0;margin-block-start:0}.toc__navigation ol>*+*{margin-block-start:.5rem}.toc__navigation a{color:var(--spruce-base-color-text);text-decoration:none}.toc__navigation a:hover,.toc__navigation a:focus{color:var(--spruce-base-color-primary)}.sidebar{display:flex;flex-direction:column;gap:1.5rem}@media(min-width: 48em){.sidebar{padding-inline-end:var(--spruce-container-gap)}}.sidebar__footer{align-items:center;display:flex;justify-content:space-between}.sidebar__body{display:flex;flex-direction:column;flex-grow:1;gap:1.5rem;max-block-size:calc(100vh - var(--dimension) - 1.5rem * 3);overflow-y:auto}.sidebar__body::-webkit-scrollbar{block-size:.5rem;inline-size:.5rem}.sidebar__body::-webkit-scrollbar-thumb{background:var(--spruce-scrollbar-color-thumb-background);border-radius:.05rem}.sidebar__body::-webkit-scrollbar-thumb:hover{background:var(--spruce-scrollbar-color-thumb-background-hover)}.sidebar__body::-webkit-scrollbar-track{background:var(--spruce-scrollbar-color-track-background);border-radius:.05rem}@media(min-width: 48em){.sidebar__body{max-block-size:initial}}@media(min-width: 48em){.sidebar-section--navigation{display:none}}.sidebar-section__title{color:var(--spruce-base-color-heading);font-size:var(--spruce-font-size-base);margin-block:0}.sidebar-section__navigation{border-inline-start:1px solid var(--spruce-base-color-border);font-size:1rem;margin-block-start:1rem;padding-inline-start:1rem}.sidebar-section__navigation ul{list-style:none;margin:0;padding:0}.sidebar-section__navigation a{align-items:center;color:var(--spruce-base-color-text);display:inline-flex;gap:.5rem;text-decoration:none}.sidebar-section__navigation a[aria-current=page]{color:var(--spruce-base-color-heading);font-weight:700;position:relative}.sidebar-section__navigation a[aria-current=page]::before{background-color:var(--spruce-base-color-primary);border-end-end-radius:var(--spruce-border-radius-sm);border-start-end-radius:var(--spruce-border-radius-sm);content:"";inline-size:.3rem;inset-block:0;inset-inline-start:-1rem;position:absolute}.sidebar-section__navigation a svg{--dimension: 0.65em;block-size:var(--dimension);color:var(--spruce-navigation-color-arrow);inline-size:var(--dimension)}.post-card{position:relative;background-color:var(--spruce-card-color-background);border:1px solid var(--spruce-base-color-border);border-radius:var(--spruce-border-radius-sm);padding:3rem clamp(1.5rem, 5vw, 3rem)}.post-card>*{margin-block-end:0;margin-block-start:0}.post-card>*+*{margin-block-start:1rem}.card__link::before{content:"";inset:0;position:absolute}.post-card__serial-number{color:var(--spruce-base-color-primary);font-family:var(--spruce-font-family-heading);font-size:clamp(2.8rem, 4vw + 1rem, 4rem);font-weight:800;line-height:.8}.post-card__serial-number::before{content:counter(card, decimal-leading-zero);counter-increment:card}.post-card__title{font-size:clamp(1.328125rem, 2vw + 1rem, 1.5625rem)}code[class*=language-],pre[class*=language-]{border-radius:var(--spruce-border-radius-sm);color:var(--spruce-prism-color-color);font-family:var(--spruce-font-family-cursive);font-size:var(--spruce-font-size-base);hyphens:none;line-height:1.5;tab-size:4;text-align:left;white-space:pre;word-break:normal;word-spacing:normal;word-wrap:normal}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{display:grid;overflow:auto;padding:1.5rem}pre[class*=language-] code{background-color:rgba(0,0,0,0);padding:0}:not(pre)>code[class*=language-],pre[class*=language-]{background:var(--spruce-prism-color-background);overflow-x:auto}.token.comment,.token.prolog,.token.cdata{color:var(--spruce-prism-color-comment);font-style:italic}.token.punctuation{color:var(--spruce-prism-color-punctuation)}.namespace{color:var(--spruce-prism-color-namespace)}.token.deleted{color:var(--spruce-prism-color-deleted);font-style:italic}.token.symbol,.token.operator,.token.keyword,.token.property{color:var(--spruce-prism-color-namespace)}.token.tag{color:var(--spruce-prism-color-punctuation)}.token.boolean{color:var(--spruce-prism-color-boolean)}.token.number{color:var(--spruce-prism-color-number)}.token.constant,.token.builtin,.token.string,.token.url,.token.entity,.language-css .token.string,.style .token.string,.token.char{color:var(--spruce-prism-color-constant)}.token.selector,.token.function,.token.doctype{color:var(--spruce-prism-color-punctuation);font-style:italic}.token.attr-name,.token.inserted{color:var(--spruce-prism-color-constant);font-style:italic}.token.class-name,.token.atrule{color:var(--spruce-prism-color-class-name)}.token.regex,.token.important,.token.variable{color:var(--spruce-prism-color-regex)}.token.important,.token.bold{font-weight:bold}.token.italic{font-style:italic}.modal-backdrop{backdrop-filter:blur(0.35rem);background-color:rgba(0,0,0,.35);display:none;inset:0;padding:1rem;position:fixed}.modal-backdrop--open{display:block}.modal{background-color:var(--spruce-base-color-background);border-radius:var(--spruce-border-radius-sm);box-shadow:var(--spruce-box-shadow);margin-block:7rem;margin-inline:auto;max-inline-size:40rem;padding:clamp(1rem, 5vw, 1.5rem)}.pagefind-ui__search-input{--webkit-date-line-height: 1.375;--spruce-border-radius: 0.425rem;--spruce-border-width: 1px;--spruce-font-size: 1rem;--spruce-line-height: 1.5;--spruce-padding: 0.5em 0.75em;--spruce-textarea-block-size: 6rem;appearance:none;background-color:var(--spruce-form-color-background);border:var(--spruce-border-width) solid var(--spruce-form-color-border);border-radius:var(--spruce-border-radius);box-sizing:border-box;color:var(--spruce-form-color-text);display:block;font-size:var(--spruce-font-size);inline-size:100%;line-height:var(--spruce-line-height);padding:var(--spruce-padding);transition-duration:var(--spruce-duration);transition-property:border,box-shadow;transition-timing-function:var(--spruce-timing-function)}.pagefind-ui__search-input::placeholder{color:var(--spruce-form-color-placeholder)}.pagefind-ui__search-input::-webkit-datetime-edit{line-height:var(--webkit-date-line-height)}.pagefind-ui__search-input:focus{border-color:var(--spruce-form-color-border-focus);box-shadow:0 0 0 .25rem var(--spruce-form-color-ring-focus);outline:2px solid rgba(0,0,0,0)}.pagefind-ui__search-input[type=color]{--spruce-aspect-ratio: 1;--spruce-block-size: 100%;--spruce-inline-size: 2.625rem;--spruce-padding: 0.5em;aspect-ratio:var(--spruce-aspect-ratio);block-size:var(--spruce-block-size);inline-size:var(--spruce-inline-size);padding:var(--spruce-padding)}.pagefind-ui__search-input[type=color]::-webkit-color-swatch-wrapper{padding:0}.pagefind-ui__search-input[type=color]::-moz-color-swatch{border:0;border-radius:var(--spruce-border-radius)}.pagefind-ui__search-input[type=color]::-webkit-color-swatch{border:0;border-radius:var(--spruce-border-radius)}.pagefind-ui__search-input[disabled],.pagefind-ui__search-input[disabled=true]{background-color:var(--spruce-form-color-background-disabled);border-color:var(--spruce-form-color-border-disabled);cursor:not-allowed}textarea.pagefind-ui__search-input{block-size:var(--spruce-textarea-block-size);min-block-size:var(--spruce-textarea-block-size);resize:vertical}.pagefind-ui{position:relative}.pagefind-ui__search-input{padding:.5em .75em !important}.pagefind-ui__form>*{margin-block-end:0;margin-block-start:0}.pagefind-ui__form>*+*{margin-block-start:1rem}.pagefind-ui__search-clear{background:none;border:0;font-size:var(--spruce-font-size-sm);inset-block-start:.8em;inset-inline-end:.5em;margin-block-start:0;position:absolute;text-transform:uppercase}.pagefind-ui__drawer{max-block-size:20rem;overflow-y:auto}.pagefind-ui__drawer::-webkit-scrollbar{block-size:.5rem;inline-size:.5rem}.pagefind-ui__drawer::-webkit-scrollbar-thumb{background:var(--spruce-scrollbar-color-thumb-background);border-radius:var(--spruce-border-radius-sm)}.pagefind-ui__drawer::-webkit-scrollbar-thumb:hover{background:var(--spruce-scrollbar-color-thumb-background-hover)}.pagefind-ui__drawer::-webkit-scrollbar-track{background:var(--spruce-scrollbar-color-track-background);border-radius:var(--spruce-border-radius-sm)}.pagefind-ui__results{list-style:none;margin:0;padding:0;padding-inline-end:1.5rem}.pagefind-ui__results>*{margin-block-end:0;margin-block-start:0}.pagefind-ui__results>*+*{margin-block-start:1rem}.pagefind-ui__results:empty{display:none}.pagefind-ui__results-area>*{margin-block-end:0;margin-block-start:0}.pagefind-ui__results-area>*+*{margin-block-start:.5rem}.pagefind-ui__result-inner>*{margin-block-end:0;margin-block-start:0}.pagefind-ui__result-inner>*+*{margin-block-start:.25rem}.pagefind-ui__result-title{font-weight:700}.pagefind-ui__hidden{display:none}.accordion-list>*{margin-block-end:0;margin-block-start:0}.accordion-list>*+*{margin-block-start:1rem}.accordion-card{background-color:var(--spruce-base-color-background);border:1px solid var(--spruce-base-color-border);border-radius:var(--spruce-border-radius-sm);max-inline-size:75ch}.accordion-card--js .accordion-card__title{padding:0}.accordion-card__title{font-family:var(--spruce-font-family-base);font-size:clamp(1.0625rem, 2vw + 1rem, 1.25rem);margin-block:0;padding:1.5rem}.accordion-card__toggle{background:none;border:0;color:inherit;cursor:pointer;font:inherit;outline:inherit;padding:0;align-items:center;display:flex;gap:1.5rem;inline-size:100%;justify-content:space-between;padding:1.5rem;text-align:start}.accordion-card__toggle:focus-visible svg{outline:2px solid var(--spruce-base-color-primary);outline-offset:2px}.accordion-card__toggle svg{--dimension: 1.75rem;background-color:var(--spruce-btn-color-primary-background);block-size:var(--dimension);border-radius:var(--spruce-border-radius-sm);color:var(--spruce-btn-color-primary-foreground);flex-shrink:0;inline-size:var(--dimension)}.accordion-card__toggle[aria-expanded=true] .vertical-line{display:none}.accordion-card__content{padding-block-end:1.5rem;padding-inline:1.5rem}.accordion-card__content>*{margin-block-end:0;margin-block-start:0}.accordion-card__content>*+*{margin-block-start:.5rem}.changelog-item{--gtc: minmax(0, 1fr);align-items:flex-start;display:grid;gap:1.5rem 3rem;grid-template-columns:var(--gtc)}@media(min-width: 64em){.changelog-item{--gtc: minmax(0, 9rem) minmax(0, 1fr)}}.changelog-item__date{background-color:var(--spruce-btn-color-primary-background);border-radius:var(--spruce-border-radius-sm);color:var(--spruce-btn-color-primary-foreground);display:inline-flex;flex-shrink:0;font-weight:700;justify-self:start;padding:.25rem 1rem}.changelog-item ul p{margin-block:0}.group>*{margin-block-end:0;margin-block-start:0}.group>*+*{margin-block-start:1.5rem}.post-navigation{align-items:center;border-block-start:1px solid var(--spruce-base-color-border);display:flex;flex-wrap:wrap;gap:1.5rem;justify-content:space-between;margin-block-start:4.5rem;padding-block:1.5rem}.post-navigation-item{align-items:center;display:flex;gap:1rem;text-decoration:none}.post-navigation-item:hover .post-navigation-item__icon{background-color:var(--spruce-navigation-color-icon-background-hover);color:var(--spruce-navigation-color-icon-foreground-hover)}.post-navigation-item--next{margin-inline-start:auto;text-align:end}.post-navigation-item__icon{transition-duration:var(--spruce-duration);transition-property:all;transition-timing-function:var(--spruce-timing-function);align-items:center;background-color:var(--spruce-navigation-color-icon-background);block-size:3rem;border-radius:.425rem;color:var(--spruce-navigation-color-icon-foreground);display:flex;flex-shrink:0;inline-size:3rem;justify-content:center}.post-navigation-item__icon svg{--dimension: 1rem;block-size:var(--dimension);inline-size:var(--dimension)}[dir=rtl] .post-navigation-item__icon svg{transform:rotate(180deg)}.post-navigation-item__caption{color:var(--spruce-base-color-text);line-height:var(--spruce-line-height-md)}.post-navigation-item__title{color:var(--spruce-base-color-primary);display:flex;font-weight:700}:root{--spruce-section-gap:clamp(5rem, 7vw, 7rem);--spruce-container-gap:clamp(1.5rem, 5vw, 3rem);--spruce-max-content-inline-size:70rem;--spruce-box-shadow:0 0.75rem 1.25rem hsla(0, 0%, 0%, 0.05)}
2 |
--------------------------------------------------------------------------------