feat: v5 style overhaul (#2469)

Style updates:

- New "core" theme serves as base for all other themes (official and third-party)
- New CSS custom properties for simplified customization of "core" theme
  **Note:** List of available properties will be made available in documentation by embedding soruce CSS in docs after merge. Merge is required because embedded CSS needs to be in `main` branch. For now, see `_vars.css` and `_vars-advanced.css` for details.
- New theme "add-ons" modify core theme properties and/or add custom declarations as needed.
- New Prism.js theme support
- New configurable sidebar toggle design
- New typography defaults to system sans-serif and monospace fonts instead of relying on external web font.
- New "Core Dark" theme addon provide dark theme styles. Can optionally be applied based on operating system's light/dark setting using `@media` attribute on `<link>` element.
- New "Vue" theme addon. Closely replicated popular v4 theme while allowing for v5 enhancements.
- New CSS class names available for adding loading indicators, adding sidebar expand/collapse icons, adding sidebar group styles, clamping sidebar links to a single line with ellipses, and changing the sidebar toggle icon.
- New auto-generated gradient background for cover page (ensure gradient hue is > 50 degree apart, use OKLCH color if supported, randomize grandient angle, reduce brightness in dark mode)
- New button styles (basic, primary, secondary)
- New form element styles (text input, radio, checkbox, )
- New "callouts" (previously "important" and "tip" helpers)
- New default syntax highlighting theme (from [docsify-themeable](https://jhildenbiddle.github.io/docsify-themeable/))
- New auto-generated theme color shade and tint colors
- New auto-generated monochromatic color palette
- New form element styles (fields, legend, text input, text area, checkbox, radio, toggles, and select)
- New "headerless" tables
- New `kbd` styles
- New task list style
- New merged navbar styles (consistent with sidebar nav styles)
- New search plugin styles and keyboard shortcut indicators
- Add ability restore previously focused content element after hiding sidebar
- Add "focus trap" when sidebar is visible on mobile (accessibility)
- Add ability for sidebar links to wrap by default (previous single-line w/ ellipsis display available as CSS class on `<body>` option)
- Add sidebar `page-link`, `group`, and `group-title` CSS classes to sidebar markup.
- Add reduced motion media query to set all animation/transition timings to zero
- Update Google Font imports (use new variable vs older fixed width fonts)
- Update primary/secondary button order on coverpage (primary should be first)
- Fix missing merged navbar when loading at desktop resolution then resizing to mobile
- Fix inverted open/close sidebar visibility state at desktop/mobile resolutions
- Fix overflow setting to prevent clipping of element focus ring
- Fix safe area inset margins on mobile in landscape orientation
- Fix inverted "tip" and "warn" class names
- Fix scroll padding to prevent headers from touching top edge of viewport when scrolled to
- Remove Stylus dependency (now using only PostCSS)
- Remove legacy themes "Buble", "Dark", "Dolphin", and "Pure".

Documentation updates:

- New "UI Kit" page showcasing all elements styled by Docsify
- Update "Quick Start" page template
- Update "Adding pages" page with information on how to properly create sidebar group titles and navbar drop-down menus
- Update "Themes" page with theme and class toggles
- Update "Configuration" page with deprecation warnings for `themeColor` and `topMargin`
- Move "Edit Page" link to footer
- Remove [docsify-themeable](https://jhildenbiddle.github.io/docsify-themeable/) endorsement (currently not compatible with v5 and future is unknown)

Miscellaneous updates:

- New search plugin options: `insertBefore` and `insertAfter`
- Add PostCSS config file
- Update BrowserSync config (disable "ghost" mode)
- Update tests
- Fix Jest + Prettier 3 conflict
- Fix `getAndRemoveDocisfyIgnoreConfig` name type (now `Docisfy` => `Docsify`)
- Fix execution of sidebar-generating code when `hiddenSidebar` is `true`
- Remove `inBrowser` constant (SSR deprecated, so no longer needed)
This commit is contained in:
John Hildenbiddle 2024-07-19 11:34:51 -04:00 committed by GitHub
parent 90c0b02c63
commit 77d93fae78
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
74 changed files with 4969 additions and 3041 deletions

View File

@ -1,4 +1,3 @@
.eslintignore
.eslintrc.cjs
.github
.gitignore

View File

@ -4,6 +4,8 @@ dist
lib
# Files
_vars.css
_vars-advanced.css
CHANGELOG.md
emoji-data.*
HISTORY.md

View File

@ -1,4 +1,4 @@
## docsify
# docsify
> A magical documentation site generator.

View File

@ -1,12 +1,17 @@
<!-- markdownlint-disable first-line-h1 -->
![logo](_media/icon.svg)
# docsify <small>4.13.0</small>
> A magical documentation site generator.
> A magical documentation site generator
- Simple and lightweight
- No statically built html files
- No statically built HTML files
- Multiple themes
[Get Started](#docsify)
[GitHub](https://github.com/docsifyjs/docsify/)
[Getting Started](#docsify)
<!-- ![color](#f0f0f0) -->
<!-- ![](/_media/icon.svg) -->

View File

@ -1 +1,31 @@
<h1>To infinity and Beyond!</h1>
<style>
html,
body {
margin: 0;
padding: 0;
min-height: 100%;
}
body {
background-color: #4158d0;
background-image: linear-gradient(
43deg,
#4158d0 0%,
#c850c0 46%,
#ffcc70 100%
);
color: #fff;
font-family: sans-serif;
}
main {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
</style>
<main>
<p>Example HTML Page</p>
</main>

1
docs/_media/moon.svg Normal file
View File

@ -0,0 +1 @@
<svg fill="none" height="800" viewBox="0 0 24 24" width="800" xmlns="http://www.w3.org/2000/svg"><path d="m13 6v-3m5.5 9v-5m-4-2.5h-3m9.5 5h-5m-.4452 7.3151c1.2281 0 2.3945-.2645 3.4452-.7397-1.3133 2.904-4.2358 4.9246-7.6302 4.9246-4.62249 0-8.3698-3.7473-8.3698-8.3698 0-3.39444 2.02061-6.31689 4.92462-7.6302-.47518 1.05072-.7397 2.21708-.7397 3.44523 0 4.62257 3.74728 8.36987 8.36988 8.36987z" stroke="#d6f5ff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/></svg>

After

Width:  |  Height:  |  Size: 487 B

1
docs/_media/sun.svg Normal file
View File

@ -0,0 +1 @@
<svg fill="none" height="800" viewBox="0 0 24 24" width="800" xmlns="http://www.w3.org/2000/svg"><path d="m12 3v1m0 16v1m-8-9h-1m3.31412-5.68588-.81412-.81412m12.1859.81412.8141-.81412m-12.18588 12.19-.81412.8101m12.1859-.8101.8141.8101m2.5-6.5001h-1m-4 0c0 2.2091-1.7909 4-4 4-2.20914 0-4-1.7909-4-4 0-2.20914 1.79086-4 4-4 2.2091 0 4 1.79086 4 4z" stroke="#ffd333" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/></svg>

After

Width:  |  Height:  |  Size: 438 B

View File

@ -1,4 +1,7 @@
<!-- markdownlint-disable first-line-h1 -->
- Translations
- [:uk: English](/)
- [:cn: 简体中文](/zh-cn/)
- [:de: Deutsch](/de-de/)

View File

@ -1,9 +1,11 @@
<!-- markdownlint-disable first-line-h1 -->
- Getting started
- [Quick start](quickstart.md)
- [Writing more pages](more-pages.md)
- [Custom navbar](custom-navbar.md)
- [Adding pages](adding-pages.md)
- [Cover page](cover.md)
- [Custom navbar](custom-navbar.md)
- Customization
@ -23,6 +25,7 @@
- [CDN](cdn.md)
- [Offline Mode (PWA)](pwa.md)
- [Embed Files](embed-files.md)
- [UI Kit](ui-kit.md)
- [Awesome docsify](awesome.md)
- [Changelog](changelog.md)

View File

@ -1,4 +1,4 @@
# More pages
# Adding pages
If you need more pages, you can simply create more markdown files in your docsify directory. If you create a file named `guide.md`, then it is accessible via `/#/guide`.
@ -46,7 +46,23 @@ Create the `_sidebar.md`:
<!-- docs/_sidebar.md -->
- [Home](/)
- [Guide](guide.md)
- [Page 1](page-1.md)
```
To create section headers:
```markdown
<!-- docs/_sidebar.md -->
- Section Header 1
- [Home](/)
- [Page 1](page-1.md)
- Section Header 2
- [Page 2](page-2.md)
- [Page 3](page-3.md)
```
You need to create a `.nojekyll` in `./docs` to prevent GitHub Pages from ignoring files that begin with an underscore.

View File

@ -376,7 +376,7 @@ window.$docsify = {
Website logo as it appears in the sidebar. You can resize it using CSS.
!> Logo will only bee visible if `name` prop is also set. See [name](#name) configuration.
!> Logo will only be visible if `name` prop is also set. See [name](#name) configuration.
```js
window.$docsify = {
@ -917,17 +917,9 @@ If you have a link to the homepage in the sidebar and want it to be shown as act
For more details, see [#1131](https://github.com/docsifyjs/docsify/issues/1131).
## themeColor (_deprecated_)
## themeColor ⚠️
> **Warning** Deprecated. Use the CSS var `--theme-color` in your `<style>` sheet. Example:
>
> ```html
> <style>
> :root {
> --theme-color: deeppink;
> }
> </style>
> ```
!> Deprecated as of v5. Use the `--theme-color` [theme property](themes#theme-properties) to [customize](themes#customization) your theme color.
- Type: `String`
@ -939,16 +931,18 @@ window.$docsify = {
};
```
## topMargin
## topMargin ⚠️
- Type: `Number`
!> Deprecated as of v5. Use the `--scroll-padding-top` [theme property](themes#theme-properties) to specify a scroll margin when using a sticky navbar.
- Type: `Number|String`
- Default: `0`
Adds a space on top when scrolling the content page to reach the selected section. This is useful in case you have a _sticky-header_ layout and you want to align anchors to the end of your header.
Adds scroll padding to the top of the viewport. This is useful when you have added a sticky or "fixed" element and would like auto scrolling to align with the bottom of your element.
```js
window.$docsify = {
topMargin: 90, // default: 0
topMargin: 90, // 90, '90px', '2rem', etc.
};
```

View File

@ -6,15 +6,10 @@ Activate the cover feature by setting `coverpage` to **true**. See [coverpage co
Set `coverpage` to **true**, and create a `_coverpage.md`:
```html
<!-- index.html -->
<script>
window.$docsify = {
coverpage: true,
};
</script>
<script src="//cdn.jsdelivr.net/npm/docsify@5/dist/docsify.min.js"></script>
```js
window.$docsify = {
coverpage: true,
};
```
```markdown
@ -22,42 +17,50 @@ Set `coverpage` to **true**, and create a `_coverpage.md`:
![logo](_media/icon.svg)
# docsify <small>3.5</small>
# docsify
> A magical documentation site generator.
> A magical documentation site generator
- Simple and lightweight
- No statically built html files
- No statically built HTML files
- Multiple themes
[GitHub](https://github.com/docsifyjs/docsify/)
[Get Started](#docsify)
```
## Custom background
## Customization
The background color is generated randomly by default. You can customize the background color or a background image:
The cover page can be customized using [theme properties](themes#theme-properties):
<!-- prettier-ignore -->
```css
:root {
--cover-bg : url('path/to/image.png');
--cover-bg-overlay : rgba(0, 0, 0, 0.5);
--cover-color : #fff;
--cover-title-color: var(--theme-color);
--cover-title-font : 600 var(--font-size-xxxl) var(--font-family);
}
```
Alternatively, a background color or image can be specified in the cover page markdown.
```markdown
<!-- _coverpage.md -->
# docsify <small>3.5</small>
[GitHub](https://github.com/docsifyjs/docsify/)
[Get Started](#quick-start)
<!-- background image -->
![](_media/bg.png)
<!-- background color -->
![color](#f0f0f0)
```
```markdown
<!-- background image -->
![](_media/bg.png)
```
## Coverpage as homepage
Normally, the coverpage and the homepage appear at the same time. Of course, you can also separate the coverpage by [onlyCover option](configuration.md#onlycover).
Normally, the coverpage and the homepage appear at the same time. Of course, you can also separate the coverpage by [`onlyCover`](configuration.md#onlycover) option.
## Multiple covers

View File

@ -40,6 +40,17 @@ Alternatively, you can create a custom markdown-based navigation file by setting
- [chinese](/zh-cn/)
```
To create drop-down menus:
```markdown
<!-- _navbar.md -->
- Translations
- [En](/)
- [chinese](/zh-cn/)
```
!> You need to create a `.nojekyll` in `./docs` to prevent GitHub Pages from ignoring files that begin with an underscore.
`_navbar.md` is loaded from each level directory. If the current directory doesn't have `_navbar.md`, it will fall back to the parent directory. If, for example, the current path is `/guide/quick-start`, the `_navbar.md` will be loaded from `/guide/_navbar.md`.
@ -59,6 +70,7 @@ You can create sub-lists by indenting items that are under a certain parent.
- [Cover page](cover.md)
- Configuration
- [Configuration](configuration.md)
- [Themes](themes.md)
- [Using plugins](plugins.md)

View File

@ -4,7 +4,9 @@ docsify extends Markdown syntax to make your documents more readable.
> Note: For the special code syntax cases, it's better to put them within code backticks to avoid any conflict from configurations or emojis.
## Important content
## Callouts
### Important content
Important content like:
@ -16,7 +18,7 @@ is rendered as:
!> **Time** is money, my friend!
## General tips
### Tips
General tips like:
@ -28,7 +30,15 @@ are rendered as:
?> _TODO_ unit test
## Ignore to compile link
## Link attributes
### disabled
```md
[link](/demo ':disabled')
```
### href
Sometimes we will use some other relative path for the link, and we have to tell docsify that we don't need to compile this link. For example:
@ -52,20 +62,14 @@ You will get `<a href="/demo/">link</a>`html. Do not worry, you can still set th
<a href="/demo/" title="title">link</a>
```
## Set target attribute for link
### target
```md
[link](/demo ':target=_blank')
[link](/demo2 ':target=_self')
```
## Disable link
```md
[link](/demo ':disabled')
```
## GitHub Task Lists
## Task lists
```md
- [ ] foo
@ -83,9 +87,21 @@ You will get `<a href="/demo/">link</a>`html. Do not worry, you can still set th
- [ ] bim
- [ ] lim
## Image
## Images
### Resizing
### Class names
```md
![logo](https://docsify.js.org/_media/icon.svg ':class=someCssClass')
```
### IDs
```md
![logo](https://docsify.js.org/_media/icon.svg ':id=someCssId')
```
### Sizes
```md
![logo](https://docsify.js.org/_media/icon.svg ':size=WIDTHxHEIGHT')
@ -101,25 +117,13 @@ You will get `<a href="/demo/">link</a>`html. Do not worry, you can still set th
![logo](https://docsify.js.org/_media/icon.svg ':size=100')
![logo](https://docsify.js.org/_media/icon.svg ':size=10%')
### Customise class
```md
![logo](https://docsify.js.org/_media/icon.svg ':class=someCssClass')
```
### Customise ID
```md
![logo](https://docsify.js.org/_media/icon.svg ':id=someCssId')
```
## Customise ID for headings
## Heading IDs
```md
### Hello, world! :id=hello-world
```
## Markdown in html tag
## Markdown + HTML
You need to insert a space between the html and markdown content.
This is useful for rendering markdown content in the details element.
@ -156,7 +160,8 @@ Markdown content can also be wrapped in html tags.
<div style='color: red'>
- Abc
- Abc
- listitem
- listitem
- listitem
</div>

View File

@ -1,7 +1,7 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta charset="utf-8" />
<title>docsify</title>
<link rel="icon" href="_media/favicon.ico" />
<meta
@ -15,52 +15,74 @@
<meta name="description" content="A magical documentation generator." />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0"
content="width=device-width, initial-scale=1.0, viewport-fit=cover"
/>
<!-- Core Theme -->
<link
rel="stylesheet"
href="//cdn.jsdelivr.net/npm/docsify@5/dist/themes/core.min.css"
/>
<!-- Theme Add-on(s) -->
<link
rel="stylesheet"
href="//cdn.jsdelivr.net/npm/docsify@5/dist/themes/addons/core-dark.min.css"
media="(prefers-color-scheme: dark)"
data-group="addon"
data-sheet="core-dark-auto"
/>
<link
rel="stylesheet"
href="//cdn.jsdelivr.net/npm/docsify@5/dist/themes/vue.min.css"
title="vue"
/>
<link
rel="stylesheet"
href="//cdn.jsdelivr.net/npm/docsify@5/dist/themes/dark.min.css"
title="dark"
href="//cdn.jsdelivr.net/npm/docsify@5/dist/themes/addons/core-dark.min.css"
data-group="addon"
data-sheet="core-dark"
disabled
/>
<link
rel="stylesheet"
href="//cdn.jsdelivr.net/npm/docsify@5/dist/themes/buble.min.css"
title="buble"
disabled
/>
<link
rel="stylesheet"
href="//cdn.jsdelivr.net/npm/docsify@5/dist/themes/pure.min.css"
title="pure"
disabled
/>
<link
rel="stylesheet"
href="//cdn.jsdelivr.net/npm/docsify@5/dist/themes/dolphin.min.css"
title="dolphin"
href="//cdn.jsdelivr.net/npm/docsify@5/dist/themes/addons/vue.min.css"
data-group="addon"
data-sheet="vue"
disabled
/>
<!-- Site styles -->
<style>
nav.app-nav li ul {
min-width: 100px;
/* Plugin: Carbon Ads */
#carbonads > :last-child {
margin-block: calc((1em * var(--line-height)) / 2);
}
#carbonads {
box-shadow: none !important;
width: auto !important;
/* UI Kit */
.ui-kit-color {
display: flex;
justify-content: space-between;
gap: 10px;
font-size: var(--font-size-s);
}
.ui-kit-color figure {
flex-grow: 1;
margin: 0;
padding: 0;
}
.ui-kit-color figure div {
height: 4rem;
border-radius: var(--border-radius);
}
.ui-kit-color figcaption {
text-align: center;
padding: 0.5em 0;
}
</style>
<script>
// Set html "lang" attribute based on URL
(function () {
const lang = location.hash.match(/#\/(de-de|es|ru-ru|zh-cn)\//);
// Set html "lang" attribute based on URL
if (lang) {
document.documentElement.setAttribute('lang', lang[1]);
}
@ -68,8 +90,8 @@
</script>
</head>
<body>
<div id="app">Loading ...</div>
<body class="loading sidebar-chevron-right sidebar-group-box">
<div id="app"></div>
<script src="//cdn.jsdelivr.net/npm/docsify-plugin-carbon@1"></script>
<script>
// Docsify configuration
@ -92,10 +114,13 @@
auto2top: true,
coverpage: true,
executeScript: true,
// hideSidebar: true,
loadSidebar: true,
loadNavbar: true,
mergeNavbar: true,
// mergeNavbar: true,
maxLevel: 4,
// repo: 'docsifyjs/docsify',
// routerMode: 'history',
subMaxLevel: 2,
ga: 'UA-106147152-1',
matomo: {
@ -111,6 +136,8 @@
'/': '#/',
},
search: {
// insertAfter: '.app-name',
// insertBefore: '.sidebar-nav',
noData: {
'/es/': '¡No hay resultados!',
'/de-de/': 'Keine Ergebnisse!',
@ -188,6 +215,7 @@
},
plugins: [
DocsifyCarbon.create('CEBI6KQE', 'docsifyjsorg'),
// Plugin: Footer
function (hook, vm) {
hook.beforeEach(html => {
if (/githubusercontent\.com/.test(vm.route.file)) {
@ -203,13 +231,16 @@
'https://github.com/docsifyjs/docsify/blob/develop/docs/' +
vm.route.file;
}
const editHtml = '[:memo: Edit Document](' + url + ')\n';
return (
editHtml +
html +
'\n\n----\n\n' +
'<a href="https://docsify.js.org" target="_blank" style="color: inherit; font-weight: normal; text-decoration: none;">Powered by docsify</a>'
);
const footerHTML = [
'<hr>',
'<div style="display: flex; align-items: center; justify-content: space-between;">',
' <span>Powered by <a href="/">Docsify.js</a></span>',
` <a href="${url}" style="display: inline-flex; align-items: center; gap: 0.25em;">:memo: Edit Page</a>`,
'</div>',
].join('\n');
return html + footerHTML;
});
},
],

View File

@ -1,37 +1,35 @@
# Language highlighting
Docsify uses [Prism](https://prismjs.com) to highlight code blocks in your pages. Prism supports the following languages by default:
## Prism
- Markup - `markup`, `html`, `xml`, `svg`, `mathml`, `ssml`, `atom`, `rss`
- CSS - `css`
- C-like - `clike`
- JavaScript - `javascript`, `js`
Docsify uses [Prism](https://prismjs.com) for syntax highlighting within code blocks. Prism supports the following languages by default (additional [language support](#language-support) also available):
Support for [additional languages](https://prismjs.com/#supported-languages) is available by loading the language-specific [grammar files](https://cdn.jsdelivr.net/npm/prismjs@1/components/) via CDN:
- Markup: HTML, XML, SVG, MathML, SSML, Atom, RSS
- CSS
- C-like
- JavaScript
```html
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-bash.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-php.min.js"></script>
```
To enable syntax highlighting, create a markdown codeblock using backticks (` ``` `) with a [language](https://prismjs.com/#supported-languages) specified on the first line (e.g., `html`, `css`, `js`):
!> This `<script>` tag must be placed after the docisfy `<script>` to work.
To enable syntax highlighting, wrap each code block in triple backticks with the [language](https://prismjs.com/#supported-languages) specified on the first line:
````
````text
```html
<p>This is a paragraph</p>
<a href="//docsify.js.org/">Docsify</a>
```
````
```bash
echo "hello"
````text
```css
p {
color: red;
}
```
````
```php
function getAdder(int $x): int
{
return 123;
````text
```js
function add(a, b) {
return a + b;
}
```
````
@ -43,24 +41,100 @@ The above markdown will be rendered as:
<a href="//docsify.js.org/">Docsify</a>
```
```bash
echo "hello"
```
```php
function getAdder(int $x): int
{
return 123;
```css
p {
color: red;
}
```
## Highlighting Dynamic Content
```js
function add(a, b) {
return a + b;
}
```
Code blocks [dynamically created from javascript](https://docsify.js.org/#/configuration?id=executescript) can be highlighted using the method `Prism.highlightElement` like so:
## Language support
```javascript
Support for additional [languages](https://prismjs.com/#supported-languages) is available by loading the Prism [grammar files](https://cdn.jsdelivr.net/npm/prismjs@1/components/):
!> Prism grammar files must be loaded after Docsify.
```html
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-bash.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-docker.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-git.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-java.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-jsx.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-markdown.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-php.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-python.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-rust.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-sql.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-swift.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-typescript.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-yaml.min.js"></script>
```
## Theme support
Docsify's official [themes](themes) are compatible with Prism syntax highlighting themes.
!> Prism themes must be loaded after Docsify themes.
```html
<!-- Light and dark mode -->
<link
rel="stylesheet"
href="//cdn.jsdelivr.net/npm/prism-themes@1/themes/prism-one-light.min.css"
/>
```
Themes can be applied in light and/or dark mode
```html
<!-- Dark mode only -->
<link
rel="stylesheet"
media="(prefers-color-scheme: dark)"
href="//cdn.jsdelivr.net/npm/prism-themes@1/themes/prism-one-dark.min.css"
/>
<!-- Light mode only -->
<link
rel="stylesheet"
media="(prefers-color-scheme: light)"
href="//cdn.jsdelivr.net/npm/prism-themes@1/themes/prism-one-light.min.css"
/>
```
The following Docsify [theme properties](themes#theme-properties) will override Prism theme styles by default:
```text
--border-radius
--font-family-mono
--font-size-mono
```
To use the values specified in the Prism theme, set the desired theme property to `unset`:
<!-- prettier-ignore -->
```html
<style>
:root {
--border-radius : unset;
--font-family-mono: unset;
--font-size-mono : unset;
}
</style>
```
## Dynamic content
Dynamically generated Code blocks can be highlighted using Prism's [`highlightElement()`](https://prismjs.com/docs/Prism.html#.highlightElement) method:
```js
const code = document.createElement('code');
code.innerHTML = "console.log('Hello World!')";
code.setAttribute('class', 'lang-javascript');
code.setAttribute('class', 'language-javascript');
Prism.highlightElement(code);
```

View File

@ -17,8 +17,13 @@ By default, the hyperlink on the current page is recognized and the content is s
'/zh-cn/', // => /zh-cn/README.md
],
// complete configuration parameters
// Complete configuration parameters
search: {
// Location in sidebar (default: prepended as first child)
// Optionally specify insertAfter or insertBefore (not both)
insertAfter: '.app-name', // CSS selector in .sidebar scope
insertBefore: '.sidebar-nav', // CSS selector in .sidebar scope
maxAge: 86400000, // Expiration time, the default one day
paths: [], // or 'auto'
placeholder: 'Type to search',
@ -40,7 +45,7 @@ By default, the hyperlink on the current page is recognized and the content is s
// Headline depth, 1 - 6
depth: 2,
hideOtherSidebarContent: false, // whether or not to hide other sidebar content
hideOtherSidebarContent: true, // Deprecated as of v5
// To avoid search index collision
// between multiple websites under the same domain

View File

@ -36,31 +36,44 @@ docsify serve docs
## Manual initialization
If you don't like `npm` or have trouble installing the tool, you can manually create `index.html`:
Download or create an `index.html` template using the following markup:
<div id="template">
<a href="#" class="button primary" download="index.html">Download Template</a>
<!-- prettier-ignore -->
```html
<!-- index.html -->
<!DOCTYPE html>
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta charset="UTF-8" />
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@5/themes/vue.min.css" />
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<!-- Core Theme -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@5/themes/core.min.css">
</head>
<body>
<body class="loading">
<div id="app"></div>
<!-- Configuration -->
<script>
window.$docsify = {
//...
};
</script>
<!-- Docsify.js -->
<script src="//cdn.jsdelivr.net/npm/docsify@5"></script>
<!-- Plugins (optional) -->
<!-- <script src="//cdn.jsdelivr.net/npm/docsify@5/dist/plugins/search.min.js"></script> -->
</body>
</html>
```
</div>
### Specifying docsify versions
?> Note that in both of the examples below, docsify URLs will need to be manually updated when a new major version of docsify is released (e.g. `v5.x.x` => `v6.x.x`). Check the docsify website periodically to see if a new major version has been released.
@ -69,8 +82,8 @@ Specifying a major version in the URL (`@5`) will allow your site to receive non
<!-- prettier-ignore -->
```html
<!-- Theme -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@5/themes/vue.min.css" />
<!-- Core Theme -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@5/themes/core.min.css">
<!-- Docsify -->
<script src="//cdn.jsdelivr.net/npm/docsify@5"></script>
@ -80,11 +93,11 @@ If you prefer to lock docsify to a specific version, specify the full version af
<!-- prettier-ignore -->
```html
<!-- Theme -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@5/themes/vue.min.css" />
<!-- Core Theme -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@5.0.0/themes/core.min.css">
<!-- Docsify -->
<script src="//cdn.jsdelivr.net/npm/docsify@5"></script>
<script src="//cdn.jsdelivr.net/npm/docsify@5.0.0"></script>
```
### Manually preview your site
@ -126,3 +139,13 @@ You should set the `data-app` attribute if you changed `el`:
```
Compare [el configuration](configuration.md#el).
<script>
(function() {
const linkElm = document.querySelector('#template a[download="index.html"]');
const codeElm = document.querySelector('#template code');
const html = codeElm?.textContent;
linkElm?.setAttribute('href', `data:text/plain,${html}`);
})();
</script>

View File

@ -1,56 +1,425 @@
# Themes
Docsify offers several official themes to choose from. Click a theme name below to preview each theme.
## Core theme
- <a href="#" data-theme="vue">Vue</a>
- <a href="#" data-theme="buble">Buble</a>
- <a href="#" data-theme="dark">Dark</a>
- <a href="#" data-theme="pure">Pure</a>
- <a href="#" data-theme="dolphin">Dolphin</a>
Official themes are available on multiple [CDNs](cdn). Uncompressed themes are also available by omitting the `.min` from the filename.
The Docsify "core" theme contains all of the styles and [theme properties](#theme-properties) needed to render a Docsify site. This theme is designed to serve as a minimalist theme on its own, in combination with [theme add-ons](#theme-add-ons), modified using core [classes](#classes), and as a starting point for [customization](#customization).
<!-- prettier-ignore -->
```html
<!-- Vue -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@5/themes/vue.min.css" />
<!-- Buble -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@5/themes/buble.min.css" />
<!-- Dark -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@5/themes/dark.min.css" />
<!-- Pure -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@5/themes/pure.min.css" />
<!-- Dolphin -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@5/themes/dolphin.min.css" />
<!-- Core Theme -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@5/dist/themes/core.min.css" />
```
## Endorsed
## Theme add-ons
The Docsify team endorses the following third-party themes. Click a link below the learn more.
Theme add-ons are used in combination with the [core theme](#core-theme). Add-ons contain CSS rules that modify [theme properties](#theme-properties) values and/or add custom style declarations. They can often (but not always) be used with other add-ons.
- [docsify-themeable](https://jhildenbiddle.github.io/docsify-themeable) - A delightfully simple theme system for docsify.
!> Theme add-ons must be loaded after the [core theme](#core-theme).
## More Themes
<!-- prettier-ignore -->
```html
<!-- Core Theme -->
<link rel="stylesheet" href="..." />
See [Awesome Docsify](awesome) for more themes.
<!-- Theme (add-on) -->
<link rel="stylesheet" href="..." />
```
### Core Dark (Add-on)
Dark mode styles for the [core theme](#core-theme). Styles can applied only when an operating system's dark mode is active by specifying a `media` attribute.
<label>
<input
class="toggle"
type="checkbox"
value="core-dark"
data-group="addon"
data-sheet
>
Preview Core Dark
</label>
<br>
<label>
<input
class="toggle"
type="checkbox"
value="core-dark-auto"
data-group="addon"
data-sheet
>
Preview Core Dark (Dark Mode Only)
</label>
<!-- prettier-ignore -->
```html
<!-- Core Dark (add-on) -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@5/dist/themes/addons/core-dark.min.css" />
```
<!-- prettier-ignore -->
```html
<!-- Core Dark - Dark Mode Only (add-on) -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@5/dist/themes/addons/core-dark.min.css" media="(prefers-color-scheme: dark)" />
```
### Vue theme (Add-on)
The popular Docsify v4 theme.
<label>
<input
class="toggle"
type="checkbox"
value="vue"
data-group="addon"
data-sheet
>
Preview Vue
</label>
<!-- prettier-ignore -->
```html
<!-- Vue Theme (add-on) -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@5/dist/themes/addons/vue.min.css" />
```
## Classes
The [core theme](#core-theme) provides several CSS classes for customizing your Docsify site. These classes should be applied to the `<body>` element within your `index.html` page.
<!-- prettier-ignore -->
```html
<body class="...">
```
### Loading
Display a loading animation while waiting for Docsify to initialize.
<!-- prettier-ignore -->
```html
<body class="loading">
```
<output data-lang="output">
<div class="loading" style="margin: auto;"></div>
</output>
### Sidebar chevrons
Display expand/collapse icons on page links in the sidebar.
<label>
<input class="toggle" type="checkbox" value="sidebar-chevron-right" data-class data-group="sidebar-chevron"> Preview <code>sidebar-chevron-right</code>
</label>
<br>
<label>
<input class="toggle" type="checkbox" value="sidebar-chevron-left" data-class data-group="sidebar-chevron"> Preview <code>sidebar-chevron-left</code>
</label>
<!-- prettier-ignore -->
```html
<body class="sidebar-chevron-right">
```
<!-- prettier-ignore -->
```html
<body class="sidebar-chevron-left">
```
To prevent chevrons from displaying for specific page links, add a `no-chevron` class as follows:
```md
[My Page](page.md ':class=no-chevron')
```
**Theme properties**
<!-- prettier-ignore -->
```css
:root {
--sidebar-chevron-collapsed-color: var(--color-mono-3);
--sidebar-chevron-expanded-color : var(--theme-color);
}
```
### Sidebar groups
Add visual distinction between groups of links in the sidebar.
<label>
<input class="toggle" type="checkbox" value="sidebar-group-box" data-class data-group="sidebar-group"> Preview <code>sidebar-group-box</code>
</label>
<br>
<label>
<input class="toggle" type="checkbox" value="sidebar-group-underline" data-class data-group="sidebar-group"> Preview <code>sidebar-group-underline</code>
</label>
<!-- prettier-ignore -->
```html
<body class="sidebar-group-box">
```
<!-- prettier-ignore -->
```html
<body class="sidebar-group-underline">
```
### Sidebar link clamp
Limit multi-line sidebar links to a single line followed by an ellipses.
<label>
<input class="toggle" type="checkbox" value="sidebar-link-clamp" data-class>
Preview <code>sidebar-link-clamp</code>
</label>
<!-- prettier-ignore -->
```html
<body class="sidebar-link-clamp">
```
### Sidebar toggle
Display a "hamburger" icon (three lines) in the sidebar toggle button instead of the default "kebab" icon.
<label>
<input class="toggle" type="checkbox" value="sidebar-toggle-chevron" data-class data-group="sidebar-toggle">
Preview <code>sidebar-toggle-chevron</code>
</label>
<br>
<label>
<input class="toggle" type="checkbox" value="sidebar-toggle-hamburger" data-class data-group="sidebar-toggle">
Preview <code>sidebar-toggle-hamburger</code>
</label>
<!-- prettier-ignore -->
```html
<body class="sidebar-toggle-chevron">
```
<!-- prettier-ignore -->
```html
<body class="sidebar-toggle-hamburger">
```
## Customization
Docsify provides [theme properties](#theme-properties) for simplified customization of frequently modified styles.
1. Add a `<style>` tag after the theme stylesheet in your `index.html`.
<!-- prettier-ignore -->
```html
<!-- Theme -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@5/dist/themes/core.min.css" />
<!-- Custom theme styles -->
<style>
:root {
/* ... */
}
</style>
```
Theme properties can also be set on a per-page basis in markdown.
```markdown
# My Heading
Hello, World!
<style>
:root {
/* ... */
}
</style>
```
2. Set custom [theme properties](#theme-properties) within a `:root` declaration.
<!-- prettier-ignore -->
```css
:root {
--theme-color: red;
--font-size : 15px;
--line-height: 1.5;
}
```
Custom [theme properties](#theme-properties) can be conditionally applied in light and/or dark mode.
<!-- prettier-ignore -->
```css
/* Light and dark mode */
:root {
--theme-color: pink;
}
/* Light mode only */
@media (prefers-color-scheme: light) {
:root {
--color-bg : #eee;
--color-text: #444;
}
}
/* Dark mode only */
@media screen and (prefers-color-scheme: dark) {
:root {
--color-bg : #222;
--color-text: #ddd;
}
}
```
3. Custom fonts can be used by adding web font resources and modifying `--font-family` properties as needed:
<!-- prettier-ignore -->
```css
/* Fonts: Noto Sans, Noto Emoji, Noto Mono */
@import url('https://fonts.googleapis.com/css2?family=Noto+Color+Emoji&family=Noto+Sans+Mono:wght@100..900&family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap');
:root {
--font-family : 'Noto Sans', sans-serif;
--font-family-emoji: 'Noto Color Emoji', sans-serif;
--font-family-mono : 'Noto Sans Mono', monospace;
}
```
?> **Theme authors**: Consider providing instructions for loading your recommended web fonts manually instead of including them in your theme using `@import`. This allows users who prefer a different font to avoid loading your recommended web font(s) unnecessarily.
4. Advanced styling may require custom CSS declarations. This is expected, however custom CSS declarations may break when new versions of Docsify are released. When possible, leverage [theme properties](#theme-properties) instead of custom declarations or lock your [CDN](cdn) URLs to a [specific version](cdn#specific-version) to avoid potential issues when using custom CSS declarations.
```css
.sidebar li.active > a {
border-right: 3px solid var(--theme-color);
}
```
## Theme properties
The following properties are available in all official Docsify themes. Default values for the "Core" theme are shown.
?> **Theme and plugin authors**: We encourage you to leverage these custom theme properties and to offer similar customization options in your projects.
### Common
Below are the most commonly modified theme properties. [Advanced](#advanced) theme properties are also available for use but typically do not need to be modified.
<!-- TODO: Replace TBD with include CSS include below -->
**TBD**
<!-- [vars.css](https://raw.githubusercontent.com/docsifyjs/docsify/main/src/themes/shared/_vars.css ':include') -->
### Advanced
Advanced theme properties are also available for use but typically do not need to be modified. Values derived from [common](#common) theme properties but can be set explicitly if preferred.
<!-- TODO: Replace TBD with include CSS include below -->
**TBD**
<!-- [vars.css](https://raw.githubusercontent.com/docsifyjs/docsify/main/src/themes/shared/_vars.css ':include') -->
## Community
See [Awesome Docsify](awesome) for additional community themes.
<script>
const previewElm = Docsify.dom.findAll('a[data-theme]');
const stylesheetElms = Docsify.dom.findAll('link[rel="stylesheet"]');
(function () {
const toggleElms = Docsify.dom.findAll(
'input:where([data-class], [data-sheet])',
);
const previewSheets = Docsify.dom.findAll(
'link[rel="stylesheet"][data-sheet]',
);
previewElm.forEach(elm => {
elm.onclick = (e) => {
e.preventDefault();
const title = e.target.getAttribute('data-theme');
function handleChange(e) {
const elm = e.target.closest('[data-class], [data-sheet]');
const value = elm.value;
const groupVal = elm.getAttribute('data-group');
const radioGroupName = elm.matches('[type="radio"]') ? elm.name : undefined;
stylesheetElms.forEach(theme => {
theme.disabled = theme.title !== title;
});
};
});
// Toggle class
if (elm.matches('[data-class]')) {
document.body.classList.toggle(value, elm.checked);
}
// Toggle sheet
else {
const themeSheet = previewSheets.find(
sheet => sheet.getAttribute('data-sheet') === value,
);
themeSheet && (themeSheet.disabled = !elm.checked);
}
if (!elm.checked || (!groupVal && !radioGroupName)) {
return;
}
// Group elements & values
const groupElms = toggleElms.filter(elm =>
groupVal
? groupVal === elm.getAttribute('data-group')
: radioGroupName === elm.name,
);
const groupVals = groupElms.map(elm => elm.value);
if (groupElms.length <= 1) {
return;
}
if (groupVal) {
// Uncheck other group elements
groupElms.forEach(groupElm => {
if (groupElm !== elm) {
groupElm.checked = false;
}
});
}
// Remove group classes
if (elm.matches('[data-class]')) {
groupVals.forEach(className => {
if (className !== value) {
document.body.classList.remove(className);
}
});
}
// Disable group sheets
else {
const otherSheets = groupVals
.map(val =>
previewSheets.find(sheet => sheet.getAttribute('data-sheet') === val),
)
.filter(sheet => sheet && sheet.getAttribute('data-sheet') !== value);
const disableSheets = otherSheets.length ? otherSheets : previewSheets;
disableSheets.forEach(sheet => sheet.disabled = true);
}
}
// Toggle active elms
toggleElms.forEach(elm => {
const value = elm.value;
// Class toggle
if (elm.matches('[data-class]')) {
elm.checked = document.body.classList.contains(value);
}
// Sheet toggle
else {
const previewSheet = previewSheets.find(
sheet => sheet.getAttribute('data-sheet') === value,
);
elm.checked = previewSheet && !previewSheet.disabled;
}
});
toggleElms.forEach(elm => elm.addEventListener('change', handleChange));
})();
</script>

500
docs/ui-kit.md Normal file
View File

@ -0,0 +1,500 @@
<!-- markdownlint-disable single-title no-duplicate-heading -->
# UI Kit
<details>
<summary>View the markdown source for this page</summary>
<div style="max-height: 50vh; overflow: auto;">
[ui-kit.md](ui-kit.md ':include :type=code')
</div>
</details>
## Blockquotes
> Cras aliquet nulla quis metus tincidunt, sed placerat enim cursus. Etiam
> turpis nisl, posuere eu condimentum ut, interdum a risus. Sed non luctus mi.
> Quisque malesuada risus sit amet tortor aliquet, a posuere ex iaculis. Vivamus
> ultrices enim dui, eleifend porttitor elit aliquet sed.
>
> _- Quote Source_
#### Nested
<!-- prettier-ignore -->
> Level 1
> > Level 2
> > > Level 3
## Buttons
#### Default
<button>Button</button>
#### Basic
<button type="button">Button</button>
<a href="javascript:void(0);" class="button">Link Button</a>
<input type="button" value="Input Button" class="button">
#### Primary
<button type="button" class="primary">Button</button>
<a href="javascript:void(0);" class="button primary">Link Button</a>
<input type="button" value="Input Button" class="primary">
#### Secondary
<button type="button" class="secondary">Button</button>
<a href="javascript:void(0);" class="button secondary">Link Button</a>
<input type="button" value="Input Button" class="secondary">
## Callouts
!> **Important** callout with `inline code` and additional placeholder text used
to force the content to wrap and span multiple lines.
?> **Tip** callout with `inline code` and additional placeholder text used to
force the content to wrap and span multiple lines.
## Code
This is `inline code`
```javascript
const add = (num1, num2) => num1 + num2;
const total = add(1, 2);
console.log(total); // 3
```
```html
<body>
<p>Hello</p>
</body>
```
## Colors
#### Theme
<div class="ui-kit-color">
<figure>
<div style="background: var(--theme-color-1);"></div>
<figcaption>1<figcaption>
</figure>
<figure>
<div style="background: var(--theme-color-2);"></div>
<figcaption>2<figcaption>
</figure>
<figure>
<div style="background: var(--theme-color-3);"></div>
<figcaption>3<figcaption>
</figure>
<figure>
<div style="background: var(--theme-color-4);"></div>
<figcaption>4<figcaption>
</figure>
<figure>
<div style="background: var(--theme-color);"></div>
<figcaption>Theme Color<figcaption>
</figure>
<figure>
<div style="background: var(--theme-color-5);"></div>
<figcaption>5<figcaption>
</figure>
<figure>
<div style="background: var(--theme-color-6);"></div>
<figcaption>6<figcaption>
</figure>
<figure>
<div style="background: var(--theme-color-7);"></div>
<figcaption>7<figcaption>
</figure>
<figure>
<div style="background: var(--theme-color-8);"></div>
<figcaption>8<figcaption>
</figure>
</div>
#### Monochromatic
<div class="ui-kit-color">
<figure>
<div style="background: var(--color-mono-min); border: 1px solid var(--color-mono-2);"></div>
<figcaption>Min<figcaption>
</figure>
<figure>
<div style="background: var(--color-mono-1);"></div>
<figcaption>1<figcaption>
</figure>
<figure>
<div style="background: var(--color-mono-2);"></div>
<figcaption>2<figcaption>
</figure>
<figure>
<div style="background: var(--color-mono-3);"></div>
<figcaption>3<figcaption>
</figure>
<figure>
<div style="background: var(--color-mono-4);"></div>
<figcaption>4<figcaption>
</figure>
<figure>
<div style="background: var(--color-mono-5);"></div>
<figcaption>5<figcaption>
</figure>
<figure>
<div style="background: var(--color-mono-6);"></div>
<figcaption>6<figcaption>
</figure>
<figure>
<div style="background: var(--color-mono-7);"></div>
<figcaption>7<figcaption>
</figure>
<figure>
<div style="background: var(--color-mono-8);"></div>
<figcaption>8<figcaption>
</figure>
<figure>
<div style="background: var(--color-mono-9);"></div>
<figcaption>9<figcaption>
</figure>
<figure>
<div style="background: var(--color-mono-max);"></div>
<figcaption>Max<figcaption>
</figure>
</div>
## Details
<details>
<summary>Details (click to open)</summary>
Suscipit nemo aut ex suscipit voluptatem laboriosam odio velit. Ipsum eveniet labore sequi non optio vel. Ut culpa ad accusantium est aut harum ipsam voluptatum. Velit eum incidunt non sint. Et molestiae veniam natus autem vel assumenda ut numquam esse. Non nisi id qui vero corrupti quos et.
</details>
<details open>
<summary>Details (open by default)</summary>
Suscipit nemo aut ex suscipit voluptatem laboriosam odio velit. Ipsum eveniet labore sequi non optio vel. Ut culpa ad accusantium est aut harum ipsam voluptatum. Velit eum incidunt non sint. Et molestiae veniam natus autem vel assumenda ut numquam esse. Non nisi id qui vero corrupti quos et.
</details>
## Form Elements
### Fieldset
<form>
<fieldset>
<legend>Legend</legend>
<p>
<label>
Label<br>
<input type="text" placeholder="Placeholder">
</label>
</p>
</fieldset>
</form>
### Input
#### Checkbox
<form>
<label><input type="checkbox" value="HTML" checked> HTML</label><br>
<label><input type="checkbox" value="CSS"> CSS</label><br>
<label><input type="checkbox" value="JavaScript"> JavaScript</label>
</form>
#### Datalist
<form>
<label>
Label<br>
<input list="planets">
<datalist id="planets">
<option value="Earth">Earth</option>
<option value="Jupiter">Jupiter</option>
<option value="Mars">Mars</option>
<option value="Mercury">Mercury</option>
<option value="Neptune">Neptune</option>
<option value="Saturn">Saturn</option>
<option value="Uranus">Uranus</option>
<option value="Venus">Venus</option>
</datalist>
</label>
</form>
#### Radio
<form>
<label><input type="radio" name="language" value="HTML" checked> HTML</label><br>
<label><input type="radio" name="language" value="CSS"> CSS</label><br>
<label><input type="radio" name="language" value="JavaScript"> JavaScript</label>
</form>
#### Text
<form>
<label>
First name<br>
<input type="text" placeholder="Placeholder">
</label>
</form>
#### Toggles
<form>
Checkbox (multi-select)
<label><input class="toggle" type="checkbox" checked> HTML</label><br>
<label><input class="toggle" type="checkbox"> CSS</label><br>
<label><input class="toggle" type="checkbox"> JavaScript</label>
Radio (single-select)
<label><input class="toggle" type="radio" name="toggle" checked> HTML</label><br>
<label><input class="toggle" type="radio" name="toggle"> CSS</label><br>
<label><input class="toggle" type="radio" name="toggle"> JavaScript</label>
</form>
### Select
<form>
<label>
Label<br>
<select>
<option value="Earth">Select a planet...</option>
<option value="Earth">Earth</option>
<option value="Jupiter">Jupiter</option>
<option value="Mars">Mars</option>
<option value="Mercury">Mercury</option>
<option value="Neptune">Neptune</option>
<option value="Saturn">Saturn</option>
<option value="Uranus">Uranus</option>
<option value="Venus">Venus</option>
</select>
</label>
</form>
### Textarea
<textarea rows="5" cols="40">
Ipsam totam tempora. Dolorum voluptas error tempore asperiores vitae error laboriosam autem possimus.
</textarea>
## Headings
# Heading 1 {docsify-ignore}
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse luctus nulla eu ex varius, a varius elit tincidunt. Aenean arcu magna, gravida id purus a, interdum convallis turpis. Aenean id ipsum eu tortor sollicitudin scelerisque in quis elit.
## Heading 2 {docsify-ignore}
Vestibulum lobortis laoreet nunc vel vulputate. In et augue non lectus pellentesque molestie et ac justo. Sed sed turpis ut diam gravida sagittis nec at neque. Vivamus id tellus est. Nam ac dignissim mi. Vestibulum nec sem convallis, condimentum augue at, commodo diam.
### Heading 3 {docsify-ignore}
Suspendisse sit amet tincidunt nibh, ac interdum velit. Ut orci diam, dignissim at enim sit amet, placerat rutrum magna. Mauris consectetur nibh eget sem feugiat, sit amet congue quam laoreet. Curabitur sed massa metus.
#### Heading 4 {docsify-ignore}
Donec odio orci, facilisis ac vehicula in, vestibulum ut urna. Ut bibendum ullamcorper risus, ac euismod leo maximus sed. In pulvinar sagittis rutrum. Morbi quis cursus diam. Cras ac laoreet nulla, rhoncus sodales dui.
##### Heading 5 {docsify-ignore}
Commodo sit veniam nulla cillum labore ullamco aliquip quis. Consequat nulla fugiat consequat ex duis proident. Adipisicing excepteur tempor exercitation ad. Consectetur voluptate Lorem sint elit exercitation ullamco dolor.
###### Heading 6 {docsify-ignore}
Ipsum ea amet dolore mollit incididunt fugiat nulla laboris est sint voluptate. Ex culpa id amet ipsum amet pariatur ipsum officia sit laborum irure ullamco deserunt. Consequat qui tempor occaecat nostrud proident.
## Horizontal Rule
Text before rule.
---
Text after rule.
## IFrame
[Example](_media/example.html ':include height=200px')
## Images
#### Inline-style
![Docsify Logo](/_media/icon.svg 'This is the Docsify logo!')
#### Reference-style
![Docsify Logo][logo]
[logo]: /_media/icon.svg 'This is the Docsify logo!'
#### Light / Dark Theme
<picture>
<source media="(prefers-color-scheme: dark)" srcset="_media/moon.svg">
<source media="(prefers-color-scheme: light)" srcset="_media/sun.svg">
<img alt="BinaryTree" src="_media/sun.svg" width="122">
</picture>
## Keyboard
#### Default
<kbd>&#8963;</kbd><kbd>&#8997;</kbd><kbd>&#9003;</kbd>
<kbd>Ctrl</kbd><kbd>Alt</kbd><kbd>Del</kbd>
<kbd>&#8963; Control</kbd><kbd>&#8997; Alt</kbd><kbd>&#9003; Delete</kbd>
#### Alternate
<kbd class="alt">&#8963;</kbd><kbd class="alt">&#8997;</kbd><kbd class="alt">&#9003;</kbd>
<kbd class="alt">Ctrl</kbd><kbd class="alt">Alt</kbd><kbd class="alt">Del</kbd>
<kbd class="alt">&#8963; Control</kbd><kbd class="alt">&#8997; Alt</kbd><kbd class="alt">&#9003; Delete</kbd>
#### Entities
<div style="display: grid; grid-template-columns: auto auto 1fr; gap: 1em 0.2em; align-items: end;">
<div><kbd class="alt">&uarr;</kbd></div> <div><kbd>&uarr;</kbd></div> <div>Arrow Up</div>
<div><kbd class="alt">&darr;</kbd></div> <div><kbd>&darr;</kbd></div> <div>Arrow Down</div>
<div><kbd class="alt">&larr;</kbd></div> <div><kbd>&larr;</kbd></div> <div>Arrow Left</div>
<div><kbd class="alt">&rarr;</kbd></div> <div><kbd>&rarr;</kbd></div> <div>Arrow Right</div>
<div><kbd class="alt">&#8682;</kbd></div> <div><kbd>&#8682;</kbd></div> <div>Caps Lock</div>
<div><kbd class="alt">&#8984;</kbd></div> <div><kbd>&#8984;</kbd></div> <div>Command</div>
<div><kbd class="alt">&#8963;</kbd></div> <div><kbd>&#8963;</kbd></div> <div>Control</div>
<div><kbd class="alt">&#9003;</kbd></div> <div><kbd>&#9003;</kbd></div> <div>Delete</div>
<div><kbd class="alt">&#8998;</kbd></div> <div><kbd>&#8998;</kbd></div> <div>Delete (Forward)</div>
<div><kbd class="alt">&#8600;</kbd></div> <div><kbd>&#8600;</kbd></div> <div>End</div>
<div><kbd class="alt">&#8996;</kbd></div> <div><kbd>&#8996;</kbd></div> <div>Enter</div>
<div><kbd class="alt">&#9099;</kbd></div> <div><kbd>&#9099;</kbd></div> <div>Escape</div>
<div><kbd class="alt">&#8598;</kbd></div> <div><kbd>&#8598;</kbd></div> <div>Home</div>
<div><kbd class="alt">&#8670;</kbd></div> <div><kbd>&#8670;</kbd></div> <div>Page Up</div>
<div><kbd class="alt">&#8671;</kbd></div> <div><kbd>&#8671;</kbd></div> <div>Page Down</div>
<div><kbd class="alt">&#8997;</kbd></div> <div><kbd>&#8997;</kbd></div> <div>Option, Alt</div>
<div><kbd class="alt">&#8629;</kbd></div> <div><kbd>&#8629;</kbd></div> <div>Return</div>
<div><kbd class="alt">&#8679;</kbd></div> <div><kbd>&#8679;</kbd></div> <div>Shift</div>
<div><kbd class="alt">&#9251;</kbd></div> <div><kbd>&#9251;</kbd></div> <div>Space</div>
<div><kbd class="alt">&#8677;</kbd></div> <div><kbd>&#8677;</kbd></div> <div>Tab</div>
<div><kbd class="alt">&#8676;</kbd></div> <div><kbd>&#8676;</kbd></div> <div>Tab + Shift</div>
</div>
## Links
[Inline link](https://google.com)
[Inline link with title](https://google.com 'Google')
[Reference link by name][link1]
[Reference link by number][1]
[Reference link by self]
[link1]: https://google.com
[1]: https://google.com
[Reference link by self]: https://google.com
## Lists
### Ordered List
1. Ordered
1. Ordered
1. Nested
1. Nested (Wrapping): Similique tempora et. Voluptatem consequuntur ut. Rerum minus et sed beatae. Consequatur ut nemo laboriosam quo architecto quia qui. Corrupti aut omnis velit.
1. Ordered (Wrapping): Error minima modi rem sequi facere voluptatem. Est nihil veritatis doloribus et corporis ipsam. Pariatur eos ipsam qui odit labore est voluptatem enim. Veritatis est qui ut pariatur inventore.
### Unordered List
- Unordered
- Unordered
- Nested
- Nested (Wrapping): Quia consectetur sint vel ut excepturi ipsa voluptatum suscipit hic. Ipsa error qui molestiae harum laboriosam. Rerum non amet illo voluptatem odio pariatur. Ut minus enim.
- Unordered (Wrapping): Fugiat qui tempore ratione amet repellendus repudiandae non. Rerum nisi officia enim. Itaque est alias voluptatibus id molestiae accusantium. Cupiditate sequi qui omnis sed facere aliquid quia ut.
### Task List
- [x] Task
- [ ] Task
- [ ] Subtask
- [ ] Subtask
- [x] Subtask
- [ ] Task (Wrapping): Earum consequuntur itaque numquam sunt error omnis ipsum repudiandae. Est assumenda neque eum quia quisquam laborum beatae autem ad. Fuga fugiat perspiciatis harum quia dignissimos molestiae. Officia quo eveniet tempore modi voluptates consequatur. Eum odio adipisci labore.
- [x] Subtask (Wrapping): Vel possimus eaque laborum. Voluptates qui debitis quaerat atque molestiae quia explicabo doloremque. Reprehenderit perspiciatis a aut impedit temporibus aut quasi quia. Incidunt sed recusandae vitae asperiores sit in.
## Output
<output data-lang="output">
<p>Et cum fugiat nesciunt voluptates. A atque quos doloribus dolorem quo. Et dignissimos omnis nam. Recusandae voluptatem nam. Tenetur veniam et qui consequatur. Aut sequi atque fuga itaque iusto eum nihil quod iure.</p>
<ol>
<li>Item</li>
<li>Item</li>
<li>Item</li>
</ol>
</output>
## Tables
### Alignment
| Left Align | Center Align | Right Align | Non&#8209;Breaking&nbsp;Header |
| ---------- | :----------: | ----------: | ------------------------------ |
| A1 | A2 | A3 | A4 |
| B1 | B2 | B3 | B4 |
| C1 | C2 | C3 | C4 |
### Headerless
| | | | |
| --- | --- | --- | --- |
| A1 | A2 | A3 | A4 |
| B1 | B2 | B3 | B4 |
| C1 | C2 | C3 | C4 |
### Scrolling
| Header |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Dicta&nbsp;in&nbsp;nobis&nbsp;dolor&nbsp;adipisci&nbsp;qui.&nbsp;Accusantium&nbsp;voluptates&nbsp;est&nbsp;dolor&nbsp;laboriosam&nbsp;qui&nbsp;voluptatibus.&nbsp;Veritatis&nbsp;eos&nbsp;aspernatur&nbsp;iusto&nbsp;et&nbsp;dicta&nbsp;quas.&nbsp;Fugit&nbsp;voluptatem&nbsp;dolorum&nbsp;qui&nbsp;quisquam.&nbsp;nihil |
| Aut&nbsp;praesentium&nbsp;officia&nbsp;aut&nbsp;delectus.&nbsp;Quas&nbsp;atque&nbsp;reprehenderit&nbsp;saepe.&nbsp;Et&nbsp;voluptatibus&nbsp;qui&nbsp;dolores&nbsp;rem&nbsp;facere&nbsp;in&nbsp;dignissimos&nbsp;id&nbsp;aut.&nbsp;Debitis&nbsp;excepturi&nbsp;delectus&nbsp;et&nbsp;quos&nbsp;numquam&nbsp;magnam. |
| Sed&nbsp;eum&nbsp;atque&nbsp;at&nbsp;laborum&nbsp;aut&nbsp;et&nbsp;repellendus&nbsp;ullam&nbsp;dolor.&nbsp;Cupiditate&nbsp;saepe&nbsp;voluptatibus&nbsp;odit&nbsp;est&nbsp;pariatur&nbsp;qui.&nbsp;Hic&nbsp;sunt&nbsp;nihil&nbsp;optio&nbsp;enim&nbsp;eum&nbsp;laudantium.&nbsp;Repellendus&nbsp;voluptate. |
## Text Elements
<mark>Marked text</mark>
<pre>Preformatted text</pre>
<samp>Sample Output</samp>
<small>Small Text</small>
This is <sub>subscript</sub>
This is <sup>superscript</sup>
<ins>Underlined Text</ins>
## Text Styles
Body text
**Bold text**
_Italic text_
**_Bold and italic text_**
~~Strikethrough~~

View File

@ -6,10 +6,11 @@ const sharedConfig = {
errorOnDeprecated: true,
globalSetup: './test/config/jest.setup.js',
globalTeardown: './test/config/jest.teardown.js',
prettierPath: null, // Fix for Jest v29 and Prettier v3 (https://github.com/jestjs/jest/issues/14305)
resetModules: true,
restoreMocks: true,
setupFilesAfterEnv: ['<rootDir>/test/config/jest.setup-tests.js'],
testEnvironment: 'jsdom',
testEnvironment: 'jest-environment-jsdom',
testEnvironmentOptions: {
url: `${TEST_HOST}/_blank.html`,
},

1865
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -72,17 +72,19 @@
"marked": "^12.0.2",
"npm-run-all": "^4.1.5",
"postcss-cli": "^11.0.0",
"postcss-import": "^16.1.0",
"postcss-nesting": "^12.1.5",
"prettier": "^3.2.5",
"rimraf": "^5.0.7",
"rollup": "^4.17.2",
"stylus": "^0.63.0",
"rollup-plugin-import-css": "^3.5.0",
"vue": "^3.4.27",
"xhr-mock": "^2.5.1"
},
"scripts": {
"build:cover": "node build/cover.js",
"build:css:min": "postcss \"dist/themes/**/!(*.min).css\" --dir dist/themes --ext .min.css --map --use cssnano",
"build:css": "stylus src/themes --out dist/themes --sourcemap",
"build:css": "postcss \"src/themes/*.css\" \"src/themes/**/[!_]*.css\" --base src/themes --dir dist/themes --map",
"build:css:min": "cross-env NODE_ENV='production' npm run build:css -- --ext .min.css",
"build:emoji": "node ./build/emoji.js",
"build:js": "rollup -c",
"build": "run-s clean build:js build:css build:css:min build:cover",

10
postcss.config.cjs Normal file
View File

@ -0,0 +1,10 @@
module.exports = ctx => ({
map: ctx.options.map,
plugins: {
'postcss-import': {},
'postcss-nesting': {
edition: '2024-02',
},
cssnano: ctx.env === 'production' ? { preset: 'default' } : false,
},
});

View File

@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
import path from 'node:path';
import { babel } from '@rollup/plugin-babel';
import commonjs from '@rollup/plugin-commonjs';
import css from 'rollup-plugin-import-css';
import replace from '@rollup/plugin-replace';
import resolve from '@rollup/plugin-node-resolve';
import terser from '@rollup/plugin-terser';
@ -46,6 +47,7 @@ const baseConfig = {
plugins: [
resolve(),
commonjs(),
css(),
replace({
preventAssignment: true,
values: {

View File

@ -7,6 +7,7 @@ const __dirname = path.dirname(__filename);
// Production (CDN URLs, watch disabled)
export const prodConfig = {
ghostMode: false,
hostname: '127.0.0.1',
notify: false,
open: false,

View File

@ -53,7 +53,7 @@ export default function (vm) {
// eslint-disable-next-line no-console
console.warn(
stripIndent(`
$docsify.themeColor is deprecated. Use a --theme-color property in your style sheet. Example:
$docsify.themeColor is deprecated. Use the "--theme-color" theme property to set your theme color.
<style>
:root {
--theme-color: deeppink;
@ -78,11 +78,10 @@ export default function (vm) {
toggleSidebar: {
bindings: ['\\'],
callback(e) {
const toggleElm = document.querySelector('.sidebar-toggle');
const toggleElm = document.querySelector('.sidebar-toggle-button');
if (toggleElm) {
toggleElm.click();
toggleElm.focus();
}
},
},

View File

@ -1,4 +1,4 @@
import { isMobile } from '../util/env.js';
import { isMobile, mobileBreakpoint } from '../util/env.js';
import * as dom from '../util/dom.js';
/** @typedef {import('../Docsify.js').Constructor} Constructor */
@ -25,16 +25,19 @@ export function Events(Base) {
// Apply topMargin to scrolled content
if (topMargin) {
const value =
typeof topMargin === 'number' ? `${topMargin}px` : topMargin;
document.documentElement.style.setProperty(
'scroll-padding-top',
`${topMargin}px`,
'--scroll-padding-top',
value,
);
}
this.#initCover();
this.#initSkipToContent('#skip-to-content');
this.#initSidebarCollapse('.sidebar');
this.#initSidebarToggle('button.sidebar-toggle');
this.#initSkipToContent();
this.#initSidebar();
this.#initSidebarToggle();
this.#initKeyBindings();
}
@ -206,26 +209,32 @@ export function Events(Base) {
}
/**
* Initialize sidebar content expand/collapse toggle behavior
* Initialize sidebar event listeners
*
* @param {Element|string} elm Sidebar Element or CSS selector
* @void
*/
#initSidebarCollapse(elm) {
elm = typeof elm === 'string' ? document.querySelector(elm) : elm;
#initSidebar() {
const sidebarElm = document.querySelector('.sidebar');
if (!elm) {
if (!sidebarElm) {
return;
}
dom.on(elm, 'click', ({ target }) => {
if (
target.nodeName === 'A' &&
target.nextSibling &&
target.nextSibling.classList &&
target.nextSibling.classList.contains('app-sub-sidebar')
) {
dom.toggleClass(target.parentNode, 'collapse');
// Auto-toggle on resolution change
window
?.matchMedia?.(`(max-width: ${mobileBreakpoint})`)
.addEventListener('change', evt => {
this.#toggleSidebar(!evt.matches);
});
// Collapse toggle
dom.on(sidebarElm, 'click', ({ target }) => {
const linkElm = target.closest('a');
const linkParent = linkElm?.closest('li');
const subSidebar = linkParent?.querySelector('.app-sub-sidebar');
if (subSidebar) {
dom.toggleClass(linkParent, 'collapse');
}
});
}
@ -233,58 +242,53 @@ export function Events(Base) {
/**
* Initialize sidebar show/hide toggle behavior
*
* @param {Element|string} elm Toggle Element or CSS selector
* @void
*/
#initSidebarToggle(elm) {
elm = typeof elm === 'string' ? document.querySelector(elm) : elm;
#initSidebarToggle() {
const contentElm = dom.find('main > .content');
const toggleElm = dom.find('button.sidebar-toggle');
if (!elm) {
if (!toggleElm) {
return;
}
const toggle = () => {
dom.body.classList.toggle('close');
let lastContentFocusElm;
const isClosed = isMobile
? dom.body.classList.contains('close')
: !dom.body.classList.contains('close');
// Store last focused content element (restored via #toggleSidebar)
dom.on(contentElm, 'focusin', e => {
const focusAttr = 'data-restore-focus';
elm.setAttribute('aria-expanded', isClosed);
};
dom.on(elm, 'click', e => {
e.stopPropagation();
toggle();
lastContentFocusElm?.removeAttribute(focusAttr);
lastContentFocusElm = e.target;
lastContentFocusElm.setAttribute(focusAttr, '');
});
isMobile &&
dom.on(
dom.body,
'click',
() => dom.body.classList.contains('close') && toggle(),
);
// Toggle sidebar
dom.on(toggleElm, 'click', e => {
e.stopPropagation();
this.#toggleSidebar();
});
}
/**
* Initialize skip to content behavior
*
* @param {Element|string} elm Skip link Element or CSS selector
* @void
*/
#initSkipToContent(elm) {
elm = typeof elm === 'string' ? document.querySelector(elm) : elm;
#initSkipToContent() {
const skipElm = document.querySelector('#skip-to-content');
if (!elm) {
if (!skipElm) {
return;
}
elm.addEventListener('click', evt => {
skipElm.addEventListener('click', evt => {
const focusElm = this.#focusContent();
evt.preventDefault();
dom.find('main')?.scrollIntoView({
focusElm?.scrollIntoView({
behavior: 'smooth',
});
this.#focusContent();
});
}
@ -318,7 +322,7 @@ export function Events(Base) {
*/
onNavigate(source) {
const { auto2top, topMargin } = this.config;
const { query } = this.route;
const { path, query } = this.route;
this.#markSidebarActiveElm();
@ -347,7 +351,12 @@ export function Events(Base) {
}
}
// Move focus to content
// Clicked anchor link
if (path === '/' || (query.id && source === 'navigate')) {
isMobile() && this.#toggleSidebar(false);
}
// Clicked anchor link or page load with anchor ID
if (query.id || source === 'navigate') {
this.#focusContent();
}
@ -361,6 +370,7 @@ export function Events(Base) {
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus
* @param {Object} options HTMLElement focus() method options
* @returns HTMLElement|undefined
* @void
*/
#focusContent(options = {}) {
@ -379,6 +389,8 @@ export function Events(Base) {
// Move focus to content area
focusEl?.focus(settings);
return focusEl;
}
/**
@ -386,32 +398,34 @@ export function Events(Base) {
*
* @param {string} [href] Matching element HREF value. If unspecified,
* defaults to the current path (without query params)
* @returns Element|undefined
* @void
*/
#markAppNavActiveElm() {
const href = decodeURIComponent(this.router.toURL(this.route.path));
const navElm = dom.find('nav.app-nav');
if (!navElm) {
return;
}
['.app-nav', '.app-nav-merged'].forEach(selector => {
const navElm = dom.find(selector);
const newActive = dom
.findAll(navElm, 'a')
.sort((a, b) => b.href.length - a.href.length)
.find(
a =>
href.includes(a.getAttribute('href')) ||
href.includes(decodeURI(a.getAttribute('href'))),
);
const oldActive = dom.find(navElm, 'li.active');
if (!navElm) {
return;
}
if (newActive && newActive !== oldActive) {
oldActive?.classList.remove('active');
newActive.classList.add('active');
}
const newActive = dom
.findAll(navElm, 'a')
.sort((a, b) => b.href.length - a.href.length)
.find(
a =>
href.includes(a.getAttribute('href')) ||
href.includes(decodeURI(a.getAttribute('href'))),
)
?.closest('li');
const oldActive = dom.find(navElm, 'li.active');
return newActive;
if (newActive && newActive !== oldActive) {
oldActive?.classList.remove('active');
newActive.classList.add('active');
}
});
}
/**
@ -479,6 +493,53 @@ export function Events(Base) {
return newPage;
}
#toggleSidebar(force) {
const sidebarElm = dom.find('.sidebar');
if (!sidebarElm) {
return;
}
const ariaElms = dom.findAll('[aria-controls="__sidebar"]');
const inertElms = dom.findAll(
'body > *:not(main, script), main > .content',
);
const isShow = sidebarElm.classList.toggle('show', force);
// Set aria-expanded attribute
ariaElms.forEach(toggleElm => {
toggleElm.setAttribute(
'aria-expanded',
force ?? sidebarElm.classList.contains('show'),
);
});
// Add inert attributes (focus trap)
if (isShow && isMobile()) {
inertElms.forEach(elm => elm.setAttribute('inert', ''));
}
// Remove inert attributes
else {
inertElms.forEach(elm => elm.removeAttribute('inert'));
}
if (isShow) {
sidebarElm.focus();
}
// Restore focus
else {
const restoreElm = document.querySelector(
'main > .content [data-restore-focus]',
);
if (restoreElm) {
restoreElm.focus({
preventScroll: true,
});
}
}
}
/**
* Monitor next scroll start/end and set #isScrolling to true/false
* accordingly. Listeners are removed after the start/end events are fired.

View File

@ -8,7 +8,7 @@ import { emojify } from './emojify.js';
import {
getAndRemoveConfig,
removeAtag,
getAndRemoveDocisfyIgnoreConfig,
getAndRemoveDocsifyIgnoreConfig,
} from './utils.js';
import { imageCompiler } from './compiler/image.js';
import { highlightCodeCompiler } from './compiler/code.js';
@ -214,7 +214,7 @@ export class Compiler {
const nextToc = { level, title: str };
const { content, ignoreAllSubs, ignoreSubHeading } =
getAndRemoveDocisfyIgnoreConfig(str);
getAndRemoveDocsifyIgnoreConfig(str);
str = content.trim();
nextToc.title = removeAtag(str);

View File

@ -11,5 +11,5 @@ export const highlightCodeCompiler = ({ renderer }) =>
lang,
);
return /* html */ `<pre data-lang="${lang}"><code class="lang-${lang}" tabindex="0">${text}</code></pre>`;
return /* html */ `<pre data-lang="${lang}" class="language-${lang}"><code class="lang-${lang} language-${lang}" tabindex="0">${text}</code></pre>`;
});

View File

@ -1,7 +1,7 @@
import {
getAndRemoveConfig,
removeAtag,
getAndRemoveDocisfyIgnoreConfig,
getAndRemoveDocsifyIgnoreConfig,
} from '../utils.js';
import { slugify } from './slugify.js';
@ -11,7 +11,7 @@ export const headingCompiler = ({ renderer, router, _self }) =>
const nextToc = { level, title: str };
const { content, ignoreAllSubs, ignoreSubHeading } =
getAndRemoveDocisfyIgnoreConfig(str);
getAndRemoveDocsifyIgnoreConfig(str);
str = content.trim();
nextToc.title = removeAtag(str);

View File

@ -3,10 +3,11 @@ import { helper as helperTpl } from '../tpl.js';
export const paragraphCompiler = ({ renderer }) =>
(renderer.paragraph = text => {
let result;
if (/^!&gt;/.test(text)) {
result = helperTpl('tip', text);
} else if (/^\?&gt;/.test(text)) {
result = helperTpl('warn', text);
if (text.startsWith('!&gt;')) {
result = helperTpl('callout important', text);
} else if (text.startsWith('?&gt;')) {
result = helperTpl('callout tip', text);
} else {
result = /* html */ `<p>${text}</p>`;
}

View File

@ -1,7 +1,7 @@
import tinydate from 'tinydate';
import * as dom from '../util/dom.js';
import { getPath, isAbsolutePath } from '../router/util.js';
import { isMobile, inBrowser } from '../util/env.js';
import { isMobile } from '../util/env.js';
import { isPrimitive } from '../util/core.js';
import { Compiler } from './compiler.js';
import * as tpl from './tpl.js';
@ -271,7 +271,7 @@ export function Render(Base) {
if (el) {
el.innerHTML = skipLinkText;
} else {
const html = `<button id="skip-to-content">${skipLinkText}</button>`;
const html = `<button type="button" id="skip-to-content" class="primary">${skipLinkText}</button>`;
dom.body.insertAdjacentHTML('afterbegin', html);
}
}
@ -287,10 +287,10 @@ export function Render(Base) {
_renderSidebar(text) {
const { maxLevel, subMaxLevel, loadSidebar, hideSidebar } = this.config;
const sidebarEl = dom.getNode('aside.sidebar');
const sidebarNavEl = dom.getNode('.sidebar-nav');
const sidebarToggleEl = dom.getNode('button.sidebar-toggle');
if (hideSidebar) {
dom.body.classList.add('hidesidebar');
sidebarEl?.remove(sidebarEl);
sidebarToggleEl?.remove(sidebarToggleEl);
@ -298,7 +298,7 @@ export function Render(Base) {
}
this._renderTo('.sidebar-nav', this.compiler.sidebar(text, maxLevel));
sidebarToggleEl.setAttribute('aria-expanded', !isMobile);
sidebarToggleEl.setAttribute('aria-expanded', !isMobile());
const activeElmHref = this.router.toURL(this.route.path);
const activeEl = dom.find(`.sidebar-nav a[href="${activeElmHref}"]`);
@ -315,6 +315,34 @@ export function Render(Base) {
// Bind event
this._bindEventOnRendered(activeEl);
// Mark page links and groups
const pageLinks = dom.findAll(
sidebarNavEl,
'a:is(li > a, li > p > a):not(.section-link, [target="_blank"])',
);
const pageLinkGroups = dom
// NOTE: Using filter() method as a replacement for :has() selector. It
// would be preferable to use only 'li:not(:has(> a, > p > a))' selector
// but the :has() selector is not supported by our Jest test environment
// See: https://github.com/jsdom/jsdom/issues/3506#issuecomment-1769782333
.findAll(sidebarEl, 'li')
.filter(
elm =>
elm.querySelector(':scope > ul') &&
!elm.querySelectorAll(':scope > a, :scope > p > a').length,
);
pageLinks.forEach(elm => {
elm.classList.add('page-link');
});
pageLinkGroups.forEach(elm => {
elm.classList.add('group');
elm
.querySelector(':scope > p:not(:has(> *))')
?.classList.add('group-title');
});
}
_bindEventOnRendered(activeEl) {
@ -334,8 +362,16 @@ export function Render(Base) {
}
_renderNav(text) {
text && this._renderTo('nav', this.compiler.compile(text));
this.#addTextAsTitleAttribute('nav a');
if (!text) {
return;
}
const html = this.compiler.compile(text);
['.app-nav', '.app-nav-merged'].forEach(selector => {
this._renderTo(selector, html);
this.#addTextAsTitleAttribute(`${selector} a`);
});
}
_renderMain(text, opt = {}, next) {
@ -384,12 +420,15 @@ export function Render(Base) {
_renderCover(text, coverOnly) {
const el = dom.getNode('.cover');
const rootElm = document.documentElement;
const coverBg = getComputedStyle(rootElm).getPropertyValue('--cover-bg');
dom.toggleClass(
dom.getNode('main'),
coverOnly ? 'add' : 'remove',
'hidden',
);
if (!text) {
dom.toggleClass(el, 'remove', 'show');
return;
@ -399,30 +438,90 @@ export function Render(Base) {
let html = this.coverIsHTML ? text : this.compiler.cover(text);
const m = html
.trim()
.match('<p><img.*?data-origin="(.*?)"[^a]+alt="(.*?)">([^<]*?)</p>$');
if (!coverBg) {
const mdBgMatch = html
.trim()
.match(
'<p><img.*?data-origin="(.*?)".*?alt="(.*?)"[^>]*?>([^<]*?)</p>$',
);
if (m) {
if (m[2] === 'color') {
el.style.background = m[1] + (m[3] || '');
} else {
let path = m[1];
let mdCoverBg;
dom.toggleClass(el, 'add', 'has-mask');
if (!isAbsolutePath(m[1])) {
path = getPath(this.router.getBasePath(), m[1]);
if (mdBgMatch) {
const [bgMatch, bgValue, bgType] = mdBgMatch;
// Color
if (bgType === 'color') {
mdCoverBg = bgValue;
}
// Image
else {
const path = !isAbsolutePath(bgValue)
? getPath(this.router.getBasePath(), bgValue)
: bgValue;
mdCoverBg = `center center / cover url(${path})`;
}
el.style.backgroundImage = `url(${path})`;
el.style.backgroundSize = 'cover';
el.style.backgroundPosition = 'center center';
html = html.replace(bgMatch, '');
}
// Gradient background
else {
const degrees = Math.round((Math.random() * 120) / 2);
let hue1 = Math.round(Math.random() * 360);
let hue2 = Math.round(Math.random() * 360);
// Ensure hue1 and hue2 are at least 50 degrees apart
if (Math.abs(hue1 - hue2) < 50) {
const hueShift = Math.round(Math.random() * 25) + 25;
hue1 = Math.max(hue1, hue2) + hueShift;
hue2 = Math.min(hue1, hue2) - hueShift;
}
// OKLCH color
if (window?.CSS?.supports('color', 'oklch(0 0 0 / 1%)')) {
const l = 90; // Lightness
const c = 20; // Chroma
// prettier-ignore
mdCoverBg = `linear-gradient(
${degrees}deg,
oklch(${l}% ${c}% ${hue1}) 0%,
oklch(${l}% ${c}% ${hue2}) 100%
)`.replace(/\s+/g, ' ');
}
// HSL color (Legacy)
else {
const s = 100; // Saturation
const l = 85; // Lightness
const o = 100; // Opacity
// prettier-ignore
mdCoverBg = `linear-gradient(
${degrees}deg,
hsl(${hue1} ${s}% ${l}% / ${o}%) 0%,
hsl(${hue2} ${s}% ${l}% / ${o}%) 100%
)`.replace(/\s+/g, ' ');
}
}
html = html.replace(m[0], '');
rootElm.style.setProperty('--cover-bg', mdCoverBg);
}
this._renderTo('.cover-main', html);
// Button styles
dom
.findAll('.cover-main > p:last-of-type > a:not([class])')
.forEach(elm => {
const buttonType = elm.matches(':first-child')
? 'primary'
: 'secondary';
elm.classList.add('button', buttonType);
});
}
_updateRender() {
@ -438,9 +537,7 @@ export function Render(Base) {
// Init markdown compiler
this.compiler = new Compiler(config, this.router);
if (inBrowser) {
window.__current_docsify_compiler__ = this.compiler;
}
window.__current_docsify_compiler__ = this.compiler;
const id = config.el || '#app';
const el = dom.find(id);
@ -477,16 +574,19 @@ export function Render(Base) {
// Add nav
if (config.loadNavbar) {
const navEl = dom.find('nav') || dom.create('nav');
const isMergedSidebar = config.mergeNavbar && isMobile;
const isMergedSidebar = config.mergeNavbar;
navEl.classList.add('app-nav');
navEl.setAttribute('aria-label', 'secondary');
dom.body.prepend(navEl);
if (isMergedSidebar) {
dom.find('.sidebar').prepend(navEl);
} else {
dom.body.prepend(navEl);
navEl.classList.add('app-nav');
navEl.classList.toggle('no-badge', !config.repo);
const mergedNavEl = dom.create('div');
const sidebarEl = dom.find('.sidebar');
const sidebarNavEl = dom.find('.sidebar-nav');
mergedNavEl?.classList.add('app-nav-merged');
sidebarEl?.insertBefore(mergedNavEl, sidebarNavEl);
}
}

View File

@ -1,3 +1,5 @@
import { isMobile } from '../util/env.js';
/**
* Render github corner
* @param {Object} data URL for the View Source on Github link
@ -34,15 +36,18 @@ export function corner(data, cornerExternalLinkTarget) {
* @returns {String} HTML of the main content
*/
export function main(config) {
const name = config.name ? config.name : '';
const { hideSidebar, name } = config;
// const name = config.name ? config.name : '';
const aside = /* html */ `
<button class="sidebar-toggle" title="Press \\ to toggle" aria-label="Toggle primary navigation" aria-keyshortcuts="\\" aria-controls="__sidebar">
<div class="sidebar-toggle-button" aria-hidden="true">
const aside = /* html */ hideSidebar
? ''
: `
<button class="sidebar-toggle" tabindex="-1" title="Press \\ to toggle">
<div class="sidebar-toggle-button" tabindex="0" aria-label="Toggle primary navigation" aria-keyshortcuts="\\" aria-controls="__sidebar">
<span></span><span></span><span></span>
</div>
</button>
<aside id="__sidebar" class="sidebar" role="none">
<aside id="__sidebar" class="sidebar${!isMobile() ? ' show' : ''}" tabindex="-1" role="none">
${
config.name
? /* html */ `
@ -57,7 +62,8 @@ export function main(config) {
`;
return /* html */ `
<main role="presentation">${aside}
<main role="presentation">
${aside}
<section class="content">
<article id="main" class="markdown-section" role="main" tabindex="-1"><!--main--></article>
</section>
@ -70,17 +76,8 @@ export function main(config) {
* @returns {String} Cover page
*/
export function cover() {
const SL = ', 100%, 85%';
const bgc = `
linear-gradient(
to left bottom,
hsl(${Math.floor(Math.random() * 255) + SL}) 0%,
hsl(${Math.floor(Math.random() * 255) + SL}) 100%
)
`;
return /* html */ `
<section class="cover show" role="complementary" aria-label="cover" style="background: ${bgc}">
<section class="cover show" role="complementary" aria-label="cover">
<div class="mask"></div>
<div class="cover-main"><!--cover--></div>
</section>

View File

@ -55,7 +55,7 @@ export function removeAtag(str = '') {
*
* @return {{content: string, ignoreAllSubs: boolean, ignoreSubHeading: boolean}} The string after delete the docsifyIgnore configs, and whether to ignore some or all.
*/
export function getAndRemoveDocisfyIgnoreConfig(content = '') {
export function getAndRemoveDocsifyIgnoreConfig(content = '') {
let ignoreAllSubs, ignoreSubHeading;
if (/<!-- {docsify-ignore} -->/g.test(content)) {
content = content.replace('<!-- {docsify-ignore} -->', '');

View File

@ -1,5 +1,4 @@
import { isFn } from '../util/core.js';
import { inBrowser } from './env.js';
const cacheNode = {};
@ -21,11 +20,11 @@ export function getNode(el, noCache = false) {
return el;
}
export const $ = inBrowser && document;
export const $ = document;
export const body = inBrowser && $.body;
export const body = $.body;
export const head = inBrowser && $.head;
export const head = $.head;
/**
* Find elements

View File

@ -1,3 +1,9 @@
export const inBrowser = true; // True for now, may change when we add SSR.
const computedStyle = getComputedStyle(document.documentElement, null);
export const isMobile = inBrowser && document.body.clientWidth <= 600;
export const mobileBreakpoint = computedStyle.getPropertyValue(
'--_mobile-breakpoint',
);
export function isMobile() {
return window?.matchMedia?.(`(max-width: ${mobileBreakpoint})`)?.matches;
}

View File

@ -1,173 +1,47 @@
import { search } from './search.js';
import cssText from './style.css';
let NO_DATA_TEXT = '';
let options;
function style() {
const code = `
.sidebar {
padding-top: 0;
}
.search {
margin-bottom: 20px;
padding: 6px;
border-bottom: 1px solid #eee;
}
.search .input-wrap {
display: flex;
align-items: center;
}
.search .results-status:not(:empty) {
margin-top: 10px;
font-size: smaller;
}
.search .results-panel {
display: none;
}
.search .results-panel.show {
display: block;
}
.search input {
outline: none;
border: none;
width: 100%;
padding: 0.6em 7px;
font-size: inherit;
border: 1px solid transparent;
}
.search input:focus {
box-shadow: 0 0 5px var(--theme-color, #42b983);
border: 1px solid var(--theme-color, #42b983);
}
.search input::-webkit-search-decoration,
.search input::-webkit-search-cancel-button,
.search input {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
.search input::-ms-clear {
display: none;
height: 0;
width: 0;
}
.search .clear-button {
cursor: pointer;
width: 36px;
text-align: right;
display: none;
}
.search .clear-button.show {
display: block;
}
.search .clear-button svg {
transform: scale(.5);
}
.search kbd {
position: absolute;
right: 8px;
margin: 0;
}
.search input:focus ~ kbd,
.search input:not(:empty) ~ kbd {
display: none;
}
.search h2 {
font-size: 17px;
margin: 10px 0;
}
.search a {
text-decoration: none;
color: inherit;
}
.search .matching-post {
border-bottom: 1px solid #eee;
}
.search .matching-post:last-child {
border-bottom: 0;
}
.search p {
font-size: 14px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.search p.empty {
text-align: center;
}
.app-name.hide, .sidebar-nav.hide {
display: none;
}`;
Docsify.dom.style(code);
}
function tpl(defaultValue = '') {
function tpl(vm, defaultValue = '') {
const { insertAfter, insertBefore } = vm.config?.search || {};
const html = /* html */ `
<div class="input-wrap">
<input type="search" value="${defaultValue}" aria-keyshortcuts="/ control+k meta+k" />
<div class="clear-button">
<svg width="26" height="24">
<circle cx="12" cy="12" r="11" fill="#ccc" />
<path stroke="white" stroke-width="2" d="M8.25,8.25,15.75,15.75" />
<path stroke="white" stroke-width="2"d="M8.25,15.75,15.75,8.25" />
</svg>
<input type="search" value="${defaultValue}" required aria-keyshortcuts="/ control+k meta+k" />
<button class="clear-button" title="Clear search">
<span class="visually-hidden">Clear search</span>
</button>
<div class="kbd-group">
<kbd title="Press / to search">/</kbd>
<kbd title="Press Control+K to search">K</kbd>
</div>
<kbd title="Press / to search">/</kbd>
</div>
<div class="results-status" aria-live="polite"></div>
<p class="results-status" aria-live="polite"></p>
<div class="results-panel"></div>
`;
const el = Docsify.dom.create('div', html);
const aside = Docsify.dom.find('aside');
const sidebarElm = Docsify.dom.find('.sidebar');
const searchElm = Docsify.dom.create('section', html);
const insertElm = sidebarElm.querySelector(
`:scope ${insertAfter || insertBefore || '> :first-child'}`,
);
Docsify.dom.toggleClass(el, 'search');
el.setAttribute('role', 'search');
Docsify.dom.before(aside, el);
searchElm.classList.add('search');
searchElm.setAttribute('role', 'search');
sidebarElm.insertBefore(
searchElm,
insertAfter ? insertElm.nextSibling : insertElm,
);
}
function doSearch(value) {
const $search = Docsify.dom.find('div.search');
const $search = Docsify.dom.find('.search');
const $panel = Docsify.dom.find($search, '.results-panel');
const $clearBtn = Docsify.dom.find($search, '.clear-button');
const $sidebarNav = Docsify.dom.find('.sidebar-nav');
const $status = Docsify.dom.find('div.search .results-status');
const $appName = Docsify.dom.find('.app-name');
const $status = Docsify.dom.find('.search .results-status');
if (!value) {
$panel.classList.remove('show');
$clearBtn.classList.remove('show');
$panel.innerHTML = '';
$status.textContent = '';
if (options.hideOtherSidebarContent) {
$sidebarNav && $sidebarNav.classList.remove('hide');
$appName && $appName.classList.remove('hide');
}
return;
}
@ -178,28 +52,23 @@ function doSearch(value) {
html += /* html */ `
<div class="matching-post" aria-label="search result ${i + 1}">
<a href="${post.url}">
<h2>${post.title}</h2>
<p>${post.content}</p>
<p class="title clamp-1">${post.title}</p>
<p class="content clamp-2">${post.content}</p>
</a>
</div>
`;
});
$panel.classList.add('show');
$clearBtn.classList.add('show');
$panel.innerHTML = html || /* html */ `<p class="empty">${NO_DATA_TEXT}</p>`;
$status.textContent = `Found ${matches.length} results`;
if (options.hideOtherSidebarContent) {
$sidebarNav && $sidebarNav.classList.add('hide');
$appName && $appName.classList.add('hide');
}
$panel.innerHTML = html || '';
$status.textContent = matches.length
? `Found ${matches.length} results`
: NO_DATA_TEXT;
}
function bindEvents() {
const $search = Docsify.dom.find('div.search');
const $search = Docsify.dom.find('.search');
const $input = Docsify.dom.find($search, 'input');
const $inputWrap = Docsify.dom.find($search, '.input-wrap');
const $clear = Docsify.dom.find($search, '.clear-button');
let timeId;
@ -221,12 +90,9 @@ function bindEvents() {
clearTimeout(timeId);
timeId = setTimeout(_ => doSearch(e.target.value.trim()), 100);
});
Docsify.dom.on($inputWrap, 'click', e => {
// Click input outside
if (e.target.tagName !== 'INPUT') {
$input.value = '';
doSearch();
}
Docsify.dom.on($clear, 'click', e => {
$input.value = '';
doSearch();
});
}
@ -254,22 +120,22 @@ function updateNoData(text, path) {
}
}
function updateOptions(opts) {
options = opts;
}
export function init(opts, vm) {
const sidebarElm = Docsify.dom.find('.sidebar');
if (!sidebarElm) {
return;
}
const keywords = vm.router.parse().query.s;
updateOptions(opts);
style();
tpl(keywords);
Docsify.dom.style(cssText);
tpl(vm, keywords);
bindEvents();
keywords && setTimeout(_ => doSearch(keywords), 500);
}
export function update(opts, vm) {
updateOptions(opts);
updatePlaceholder(opts.placeholder, vm.route.path);
updateNoData(opts.noData, vm.route.path);
}

View File

@ -10,10 +10,11 @@ const CONFIG = {
paths: 'auto',
depth: 2,
maxAge: 86400000, // 1 day
hideOtherSidebarContent: false,
namespace: undefined,
pathNamespaces: undefined,
keyBindings: ['/', 'meta+k', 'ctrl+k'],
insertAfter: undefined, // CSS selector
insertBefore: undefined, // CSS selector
};
const install = function (hook, vm) {
@ -28,8 +29,6 @@ const install = function (hook, vm) {
CONFIG.placeholder = opts.placeholder || CONFIG.placeholder;
CONFIG.noData = opts.noData || CONFIG.noData;
CONFIG.depth = opts.depth || CONFIG.depth;
CONFIG.hideOtherSidebarContent =
opts.hideOtherSidebarContent || CONFIG.hideOtherSidebarContent;
CONFIG.namespace = opts.namespace || CONFIG.namespace;
CONFIG.pathNamespaces = opts.pathNamespaces || CONFIG.pathNamespaces;
CONFIG.keyBindings = opts.keyBindings || CONFIG.keyBindings;

View File

@ -1,6 +1,6 @@
import {
getAndRemoveConfig,
getAndRemoveDocisfyIgnoreConfig,
getAndRemoveDocsifyIgnoreConfig,
} from '../../core/render/utils.js';
let INDEXS = {};
@ -89,7 +89,7 @@ export function genIndex(path, content = '', router, depth) {
if (token.type === 'heading' && token.depth <= depth) {
const { str, config } = getAndRemoveConfig(token.text);
const text = getAndRemoveDocisfyIgnoreConfig(token.text).content;
const text = getAndRemoveDocsifyIgnoreConfig(token.text).content;
if (config.id) {
slug = router.toURL(path, { id: slugify(config.id) });
@ -98,7 +98,7 @@ export function genIndex(path, content = '', router, depth) {
}
if (str) {
title = getAndRemoveDocisfyIgnoreConfig(str).content;
title = getAndRemoveDocsifyIgnoreConfig(str).content;
}
index[slug] = { slug, title: title, body: '' };
@ -203,7 +203,7 @@ export function search(query) {
let end = 0;
start = indexContent < 11 ? 0 : indexContent - 10;
end = start === 0 ? 70 : indexContent + keyword.length + 60;
end = start === 0 ? 100 : indexContent + keyword.length + 90;
if (postContent && end > postContent.length) {
end = postContent.length;
@ -211,14 +211,9 @@ export function search(query) {
const matchContent =
handlePostContent &&
'...' +
handlePostContent
.substring(start, end)
.replace(
regEx,
word => /* html */ `<em class="search-keyword">${word}</em>`,
) +
'...';
handlePostContent
.substring(start, end)
.replace(regEx, word => /* html */ `<mark>${word}</mark>`);
resultStr += matchContent;
}

View File

@ -0,0 +1,159 @@
/* prettier-ignore */
:root {
--plugin-search-input-bg : var(--form-element-bg);
--plugin-search-input-border-color : var(--sidebar-border-color);
--plugin-search-input-border-radius: var(--form-element-border-radius);
--plugin-search-input-color : var(--form-element-color);
--plugin-search-kbd-bg : var(--color-bg);
--plugin-search-kbd-border : 1px solid var(--color-mono-3);
--plugin-search-kbd-border-radius : 4px;
--plugin-search-kbd-color : var(--color-mono-5);
--plugin-search-margin : 10px;
--plugin-search-reset-bg : var(--theme-color);
--plugin-search-reset-border : transparent;
--plugin-search-reset-border-radius: var(--border-radius);
--plugin-search-reset-color : #fff;
}
.search {
margin: var(--plugin-search-margin);
}
/* Input */
/* ================================== */
.search .input-wrap {
position: relative;
}
.search input {
width: 100%;
padding-inline-end: 36px;
border: 1px solid var(--plugin-search-input-border-color);
border-radius: var(--plugin-search-input-border-radius);
background: var(--plugin-search-input-bg);
color: var(--plugin-search-input-color);
}
.search input::-webkit-search-decoration,
.search input::-webkit-search-cancel-button {
appearance: none;
}
.search .clear-button,
.search .kbd-group {
visibility: hidden;
display: flex;
gap: 0.15em;
position: absolute;
right: 7px;
top: 50%;
opacity: 0;
translate: 0 -50%;
transition-property: opacity, visibility;
transition-duration: var(--duration-medium);
}
/* Note: invalid = empty, valid = not empty */
.search input:valid ~ .clear-button,
.search input:invalid:where(:focus, :hover) ~ .kbd-group,
.search .kbd-group:hover {
visibility: visible;
opacity: 1;
}
.search .clear-button {
--_button-size: 20px;
--_content-size: 12px;
display: flex;
align-items: center;
justify-content: center;
height: var(--_button-size);
width: var(--_button-size);
border: var(--plugin-search-reset-border);
border-radius: var(--plugin-search-reset-border-radius);
background: var(--plugin-search-reset-bg);
cursor: pointer;
}
.search .clear-button::before,
.search .clear-button::after {
content: '';
position: absolute;
height: 2px;
width: var(--_content-size);
color: var(--plugin-search-reset-color);
background: var(--plugin-search-reset-color);
}
.search .clear-button::before {
rotate: 45deg;
}
.search .clear-button::after {
rotate: -45deg;
}
.search kbd {
border: var(--plugin-search-kbd-border);
border-radius: var(--plugin-search-kbd-border-radius);
background: var(--plugin-search-kbd-bg);
color: var(--plugin-search-kbd-color);
font-size: var(--font-size-s);
}
/* Results */
/* ================================== */
.search a:hover {
color: var(--theme-color);
}
.search .results-panel:empty {
display: none;
}
/* Hide other sidebar items when results are shown */
.search:has(.results-panel:not(:empty)) ~ * {
display: none;
}
/* Dim other sidebar items when no results are found */
.search:where(:has(input:valid:focus), :has(.results-panel::empty)) ~ * {
opacity: 0.2;
}
.search .matching-post {
overflow: hidden;
padding: 1em 0 1.2em 0;
border-bottom: 1px solid var(--color-mono-2);
}
.search .matching-post:hover a {
text-decoration-color: transparent;
}
.search .matching-post:hover .title {
text-decoration: inherit;
text-decoration-color: var(--link-underline-color-hover);
}
.search .matching-post .title {
margin: 0 0 0.5em 0;
line-height: 1.4;
}
.search .matching-post .content {
margin: 0;
color: var(--color-mono-6);
font-size: var(--font-size-s);
}
.search .results-status {
margin-bottom: 0;
color: var(--color-mono-6);
font-size: var(--font-size-s);
}
.search .results-status:empty {
display: none;
}

View File

@ -0,0 +1,36 @@
/* prettier-ignore */
:root {
/* Color */
--color-bg : #1f2428;
--color-text: #ddd;
/* Cover */
--cover-bg-brightness: 0.75;
/* Elements */
--codeblock-comment : #516e7a;
--codeblock-function : #f07178;
--codeblock-keyword : #c2e78c;
--codeblock-operator : #ffcb6b;
--codeblock-punctuation: #89ddff;
--codeblock-selector : #ffcb6b;
--codeblock-tag : #f07178;
--codeblock-variable : #ffcb6b;
--heading-color : var(--strong-color);
--mark-bg : #fde047;
--mark-color : var(--color-bg);
--strong-color : color-mix(in srgb, var(--color-text), white 35%);
/* Sidebar */
--sidebar-toggle-bg : var(--color-mono-3);
--sidebar-toggle-color: var(--color-mono-5);
color-scheme: dark;
}
.cover-main {
a.button.secondary {
color: var(--color-text);
border-color: rgba(255, 255, 255, 0.5);
}
}

60
src/themes/addons/vue.css Normal file
View File

@ -0,0 +1,60 @@
@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Source+Sans+3:ital,wght@0,200..900;1,200..900&display=swap');
/* prettier-ignore */
:root {
/* Color */
--color-text : #34495e;
--theme-color: #42b983;
/* Typography */
--font-family : 'Source Sans 3', sans-serif;
--font-family-mono : 'Roboto Mono', monospace;
--font-size : 15px;
--font-size-xxxl : 2rem;
--font-size-xxl : 1.75rem;
--font-size-xl : 1.5rem;
--font-size-l : 1.25rem;
--content-max-width: 85ch;
/* Common */
--margin-block: 1.2em;
/* Elements */
--code-color : #e96900;
--codeblock-comment : #8e908c;
--codeblock-function : #c94922;
--codeblock-important : #c94922;
--codeblock-keyword : var(--theme-color);
--codeblock-operator : #22a2c9;
--codeblock-property : #c08b30;
--codeblock-punctuation: #525252;
--codeblock-selector : #6679cc;
--codeblock-tag : #2973b7;
--codeblock-variable : #3d8fd1;
}
/* Sidebar */
/* ========================================================================== */
.sidebar-nav {
li.active > a {
position: relative;
&::after {
content: '';
position: absolute;
top: 0;
right: 0;
height: 100%;
width: 3px;
background: var(--theme-color);
}
}
.app-sub-sidebar {
li::before {
content: '-';
margin-right: 0.25em;
float: left;
}
}
}

View File

@ -1,102 +0,0 @@
section.cover
position relative
align-items center
background-position center center
background-repeat no-repeat
background-size cover
min-height 100vh
width 100%
display none
&.show
display flex
&.has-mask .mask
background-color $color-bg
opacity 0.8
position absolute
top 0
bottom 0
width 100%
.cover-main
flex 1
margin 0 16px
text-align center
position: relative
a
color inherit
text-decoration none
&:hover
text-decoration none
p
line-height 1.5rem
margin 1em 0
h1
color inherit
font-size 2.5rem
font-weight 300
margin 0.625rem 0 2.5rem
position relative
text-align center
a
display block
small
bottom -0.4375rem
font-size 1rem
position absolute
blockquote
font-size 1.5rem
text-align center
ul
line-height 1.8
list-style-type none
margin 1em auto
max-width 500px
padding 0
.cover-main > p:last-child a
border-color $color-primary
border-color var(--theme-color, $color-primary)
border-radius 2rem
border-style solid
border-width 1px
box-sizing border-box
color $color-primary
color var(--theme-color, $color-primary)
display inline-block
font-size 1.05rem
letter-spacing 0.1rem
margin 0.5rem 1rem
padding 0.75em 2rem
text-decoration none
transition all 0.15s ease
&:last-child
background-color $color-primary
background-color var(--theme-color, $color-primary)
color #fff
&:hover
color inherit
opacity 0.8
&:hover
color inherit
blockquote > p > a
border-bottom 2px solid $color-primary
border-bottom 2px solid var(--theme-color, $color-primary)
transition color 0.3s
&:hover
color $color-primary
color var(--theme-color, $color-primary)

View File

@ -1,529 +0,0 @@
*
-webkit-font-smoothing antialiased
-webkit-overflow-scrolling touch
-webkit-tap-highlight-color rgba(0, 0, 0, 0)
-webkit-text-size-adjust none
-webkit-touch-callout none
box-sizing border-box
body:not(.ready)
overflow hidden
[data-cloak], .app-nav, > nav
display none
div#app
font-size 30px
font-weight lighter
margin 40vh auto
text-align center
&:empty::before
content 'Loading...'
img.emoji
height 1.2em
vertical-align middle
span.emoji
font-family "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"
font-size 1.2em
vertical-align middle
.progress
background-color $color-primary
background-color var(--theme-color, $color-primary)
height 2px
left 0px
position fixed
right 0px
top 0px
transition width 0.2s, opacity 0.4s
width 0%
z-index 999999
.search a:hover
color $color-primary
color var(--theme-color, $color-primary)
.search .search-keyword
color $color-primary
color var(--theme-color, $color-primary)
font-style normal
font-weight bold
html, body
height 100%
body
-moz-osx-font-smoothing grayscale
-webkit-font-smoothing antialiased
color $color-text
font-family 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif
font-size 15px
letter-spacing 0
margin 0
overflow-x hidden
img
max-width 100%
a[disabled]
cursor not-allowed
opacity 0.6
kbd
border solid 1px #ccc
border-radius 3px
display inline-block
font-size 12px !important
line-height 12px
margin-bottom 3px
padding 3px 5px
vertical-align middle
li input[type='checkbox']
margin 0 0.2em 0.25em 0
vertical-align middle
[tabindex="-1"]:focus
outline none !important
/* skip link */
#skip-to-content
appearance none
display block
position fixed
z-index 2147483647
top 0
left 50%
padding 0.5rem 1.5rem
border 0
border-radius: 100vw
background-color $color-primary
background-color var(--theme-color, $color-primary)
color $color-bg
color var(--theme-bg, $color-bg)
opacity 0
font-size inherit
text-decoration none
transform translate(-50%, -100%)
transition-property opacity, transform
transition-duration 0s, 0.2s
transition-delay 0.2s, 0s
&:focus
opacity 1
transform translate(-50%, 0.75rem)
transition-duration 0s, 0.2s
transition-delay 0s, 0s
/* navbar */
.app-nav
margin 25px 60px 0 0
position absolute
right 0
text-align right
z-index 10
&.no-badge
margin-right 25px
p
margin 0
> a
margin 0 1rem
padding 5px 0
ul, li
display inline-block
list-style none
margin 0
a
color inherit
font-size 16px
text-decoration none
transition color 0.3s
&:hover
color $color-primary
color var(--theme-color, $color-primary)
&.active
border-bottom 2px solid $color-primary
border-bottom 2px solid var(--theme-color, $color-primary)
color $color-primary
color var(--theme-color, $color-primary)
/* navbar dropdown */
li
display inline-block
margin 0 1rem
padding 5px 0
position relative
cursor pointer
ul
background-color #fff
border 1px solid #ddd
border-bottom-color #ccc
border-radius 4px
box-sizing border-box
max-height calc(100vh - 61px)
overflow-y auto
padding 10px 0
position absolute
right -15px
text-align left
top -100vh
white-space nowrap
li
display block
font-size 14px
line-height 1rem
margin 0
margin 8px 14px
white-space nowrap
a
display block
font-size inherit
margin 0
padding 0
&.active
border-bottom 0
&:focus-within ul,
&:hover ul
top: 100%;
/* github corner */
.github-corner
border-bottom 0
position fixed
right 0
text-decoration none
top 0
z-index 1
&:hover .octo-arm
animation octocat-wave 560ms ease-in-out
svg
color $color-bg
fill $color-primary
fill var(--theme-color, $color-primary)
height 80px
width 80px
/* main */
main
display block
position relative
width 100vw
height 100%
z-index 0
main.hidden
display none
.anchor
display inline-block
text-decoration none
transition all 0.3s
span
color $color-text
&:hover
text-decoration underline
/* sidebar */
.sidebar
border-right 1px solid rgba(0, 0, 0, 0.07)
overflow-y auto
padding 40px 0 0
position absolute
top 0
bottom 0
left 0
transition transform 250ms ease-out, visibility 250ms
width $sidebar-width
z-index 20
> h1
margin 0 auto 1rem
font-size 1.5rem
font-weight 300
text-align center
a
color inherit
text-decoration none
.app-nav
display block
position static
.sidebar-nav
line-height 2em
padding-bottom 40px
li
scroll-margin-bottom 40px
li.collapse
.app-sub-sidebar
display none
ul
margin 0 0 0 15px
padding 0
li > p
font-weight 700
margin 0
ul, ul li
list-style none
ul li a
border-bottom none
display block
ul li ul
padding-left 20px
&::-webkit-scrollbar
width 4px
&::-webkit-scrollbar-thumb
background transparent
border-radius 4px
&:hover::-webkit-scrollbar-thumb
background rgba(136, 136, 136, 0.4)
&:hover::-webkit-scrollbar-track
background rgba(136, 136, 136, 0.1)
/* sidebar toggle */
.sidebar-toggle
background-color transparent
background-color rgba($color-bg, 0.8)
border 0
outline none
padding 10px
position absolute
bottom 0
left 0
text-align center
transition opacity 0.3s
width $sidebar-width - 16px
z-index 30
cursor pointer
&:hover .sidebar-toggle-button
opacity 0.4
span
background-color $color-primary
background-color var(--theme-color, $color-primary)
display block
margin-bottom 4px
width 16px
height 2px
body.sticky
.sidebar, .sidebar-toggle
position fixed
/* main content */
.content
padding-top 60px
position absolute
top 0
right 0
bottom 0
left $sidebar-width
transition left 250ms ease
body.hidesidebar &
position relative
left unset
right unset
/* markdown content found on pages */
.markdown-section
margin 0 auto
max-width 80%
padding 30px 15px 40px 15px
position relative
> *
box-sizing border-box
font-size inherit
> :first-child
margin-top 0 !important
.markdown-section hr
border none
border-bottom 1px solid #eee
margin 2em 0
.markdown-section iframe
border 1px solid #eee
/* fix horizontal overflow on iOS Safari */
width 1px
min-width 100%
.markdown-section table
border-collapse collapse
border-spacing 0
display block
margin-bottom 1rem
overflow auto
width 100%
.markdown-section th
border 1px solid #ddd
font-weight bold
padding 6px 13px
.markdown-section td
border 1px solid #ddd
padding 6px 13px
.markdown-section tr
border-top 1px solid #ccc
&:nth-child(2n)
background-color #f8f8f8
.markdown-section p.tip
background-color #f8f8f8
border-bottom-right-radius 2px
border-left 4px solid #f66
border-top-right-radius 2px
margin 2em 0
padding 12px 24px 12px 30px
position relative
&:before
background-color #f66
border-radius 100%
color $color-bg
content '!'
font-family 'Dosis', 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif
font-size 14px
font-weight bold
left -12px
line-height 20px
position absolute
height 20px
width 20px
text-align center
top 14px
code
background-color #efefef
em
color $color-text
.markdown-section p.warn
background rgba($color-primary, 0.1)
border-radius 2px
padding 1rem
.markdown-section ul.task-list > li
list-style-type none
body.close
.sidebar
visibility hidden
transform translateX(- $sidebar-width)
.sidebar-toggle
width auto
.content
left 0
@media print
.github-corner, .sidebar-toggle, .sidebar, .app-nav
display none
@media screen and (max-width: 768px)
.github-corner, .sidebar-toggle, .sidebar
position fixed
.app-nav
margin-top 16px
.app-nav li ul
top 30px
main
height auto
min-height 100vh
overflow-x hidden
.sidebar
visibility hidden
left - $sidebar-width
transition transform 250ms ease-out, visibility 250ms
.content
left 0
max-width 100vw
position static
padding-top 20px
transition transform 250ms ease
.app-nav, .github-corner
transition transform 250ms ease-out
.sidebar-toggle
background-color transparent
width auto
padding 30px 30px 10px 10px
body.close
.sidebar
visibility visible
transform translateX($sidebar-width)
.sidebar-toggle
background-color rgba($color-bg, 0.8)
transition 1s background-color
width $sidebar-width - 16px
padding 10px
.content
transform translateX($sidebar-width)
.app-nav, .github-corner
display none
.github-corner
&:hover .octo-arm
animation none
.octo-arm
animation octocat-wave 560ms ease-in-out
@keyframes octocat-wave
0%, 100%
transform rotate(0)
20%, 60%
transform rotate(-25deg)
40%, 80%
transform rotate(10deg)

View File

@ -1,180 +0,0 @@
@import url('https://fonts.googleapis.com/css?family=Inconsolata|Inconsolata-Bold')
$color-primary = #0074d9
$color-bg = #fff
$color-text = #34495e
$sidebar-width = 16rem
@import 'basic/_layout'
@import 'basic/_coverpage'
/* sidebar */
.sidebar
color #364149
background-color $color-bg
a
color #666
text-decoration none
li
list-style none
margin 0
padding 0.2em 0 0.2em 0
ul li ul
padding 0
li.active
a
color #333
background-color #eee
.markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section strong
color #333
font-weight 400
.markdown-section strong
color #333
font-weight 600
.markdown-section a
color $color-primary
color var(--theme-color, $color-primary)
.markdown-section p, .markdown-section ul, .markdown-section ol
line-height 1.6rem
margin 0 0 1em 0
word-spacing 0.05rem
.markdown-section h1
font-size 2rem
font-weight 500
margin 0 0 1rem
.markdown-section h2
font-size 1.8rem
font-weight 400
margin 0 0 1rem 0
padding 1rem 0 0 0
.markdown-section h3
font-size 1.5rem
margin 52px 0 1.2rem
.markdown-section h4
font-size 1.25rem
.markdown-section h5
font-size 1rem
.markdown-section h6
color #777
font-size 1rem
.markdown-section figure, .markdown-section p, .markdown-section ul, .markdown-section ol
margin 1.2em 0
.markdown-section ul, .markdown-section ol
padding-left 1.5rem
.markdown-section li
line-height 1.5
margin 0
.markdown-section blockquote
border-left 4px solid $color-primary
border-left 4px solid var(--theme-color, $color-primary)
color #858585
margin 2em 0
padding-left 20px
.markdown-section blockquote p
font-weight 600
margin-left 0
.markdown-section iframe
margin 1em 0
.markdown-section em
color #7f8c8d
.markdown-section code
background-color #f9f9f9
border-radius 3px
font-family Inconsolata, monospace
padding 0.2em 0.4rem
white-space nowrap
.markdown-section pre
background-color #f9f9f9
border-left 2px solid #eee
font-family Inconsolata, monospace
font-size 16px
margin 0 0 1em 0
padding 8px
padding 0 10px 12px 0
overflow auto
word-wrap normal
position relative
/* code highlight */
.token.cdata, .token.comment, .token.doctype, .token.prolog
color #93a1a1 /* base1 */
.token.punctuation
color #586e75 /* base01 */
.namespace
opacity 0.7
.token.property, .token.tag, .token.boolean, .token.number, .token.constant, .token.symbol, .token.deleted
color #268bd2 /* blue */
.token.selector, .token.attr-name, .token.string, .token.char, .token.builtin, .token.url, .token.inserted
color #2aa198 /* cyan */
.token.entity
color #657b83 /* base00 */
background #eee8d5 /* base2 */
.token.atrule, .token.attr-value, .token.keyword
color #a11 /* green */
.token.function
color #b58900 /* yellow */
.token.regex, .token.important, .token.variable
color #cb4b16 /* orange */
.token.important, .token.bold
font-weight bold
.token.italic
font-style italic
.token.entity
cursor help
.markdown-section pre > code
background-color #f8f8f8
border-radius 2px
display block
font-family Inconsolata, monospace
line-height 1.1rem
max-width inherit
overflow inherit
padding 20px 0.8em 20px
position relative
white-space inherit
.markdown-section code::after, .markdown-section code::before
letter-spacing 0.05rem
code .token
-webkit-font-smoothing initial
-moz-osx-font-smoothing initial
min-height 1.5rem
position: relative
left: auto

1
src/themes/core.css Normal file
View File

@ -0,0 +1 @@
@import 'shared/__index.css';

View File

@ -1,237 +0,0 @@
@import url('https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600')
$color-primary = #ea6f5a
$color-bg = #3f3f3f
$color-text = #c8c8c8
$sidebar-width = 300px
@import 'basic/_layout'
@import 'basic/_coverpage'
body
background-color $color-bg
/* sidebar */
.sidebar
background-color $color-bg
color #c8c8c8
li
margin 6px 15px 6px 0
ul li a
color #c8c8c8
font-size 14px
overflow hidden
text-decoration none
text-overflow ellipsis
white-space nowrap
&:hover
text-decoration underline
ul li ul
padding 0
ul li.active > a
color $color-primary
color var(--theme-color, $color-primary)
font-weight 600
/* markdown content found on pages */
.markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section strong
color #657b83
font-weight 600
.markdown-section a
color $color-primary
color var(--theme-color, $color-primary)
font-weight 600
.markdown-section h1
font-size 2rem
margin 0 0 1rem
.markdown-section h2
font-size 1.75rem
margin 45px 0 0.8rem
.markdown-section h3
font-size 1.5rem
margin 40px 0 0.6rem
.markdown-section h4
font-size 1.25rem
.markdown-section h5
font-size 1rem
.markdown-section h6
color #777
font-size 1rem
.markdown-section figure, .markdown-section p, .markdown-section ul, .markdown-section ol
margin 1.2em 0
.markdown-section p, .markdown-section ul, .markdown-section ol
line-height 1.6rem
word-spacing 0.05rem
.markdown-section ul, .markdown-section ol
padding-left 1.5rem
.markdown-section blockquote
border-left 4px solid $color-primary
border-left 4px solid var(--theme-color, $color-primary)
color #858585
margin 2em 0
padding-left 20px
.markdown-section blockquote p
font-weight 600
margin-left 0
.markdown-section iframe
margin 1em 0
.markdown-section em
color #7f8c8d
.markdown-section code
background-color #282828
border-radius 2px
color #657b83
font-family 'Roboto Mono', Monaco, courier, monospace
margin 0 2px
padding 3px 5px
white-space pre-wrap
.markdown-section > :not(h1):not(h2):not(h3):not(h4):not(h5):not(h6) code
font-size 0.8rem
.markdown-section pre
-moz-osx-font-smoothing initial
-webkit-font-smoothing initial
background-color #282828
font-family 'Roboto Mono', Monaco, courier, monospace
line-height 1.5rem
margin 1.2em 0
overflow auto
padding 0 1.4rem
position relative
word-wrap normal
.markdown-section tr:nth-child(2n)
background-color #282828
/* code highlight */
.token.comment, .token.prolog, .token.doctype, .token.cdata
color #8e908c
.token.namespace
opacity 0.7
.token.boolean, .token.number
color #c76b29
.token.punctuation
color #525252
.token.property
color #c08b30
.token.tag
color #2973b7
.token.string
color $color-primary
color var(--theme-color, $color-primary)
.token.selector
color #6679cc
.token.attr-name
color #2973b7
.token.entity, .token.url, .language-css .token.string, .style .token.string
color #22a2c9
.token.attr-value, .token.control, .token.directive, .token.unit
color $color-primary
color var(--theme-color, $color-primary)
.token.keyword
color #e96900
.token.statement, .token.regex, .token.atrule
color #22a2c9
.token.placeholder, .token.variable
color #3d8fd1
.token.deleted
text-decoration line-through
.token.inserted
border-bottom 1px dotted #202746
text-decoration none
.token.italic
font-style italic
.token.important, .token.bold
font-weight bold
.token.important
color #c94922
.token.entity
cursor help
.markdown-section pre > code
-moz-osx-font-smoothing initial
-webkit-font-smoothing initial
background-color #282828
border-radius 2px
color #657b83
display block
font-family 'Roboto Mono', Monaco, courier, monospace
font-size 0.8rem
line-height inherit
margin 0 2px
max-width inherit
overflow inherit
padding 2.2em 5px
white-space inherit
.markdown-section code::after, .markdown-section code::before
letter-spacing 0.05rem
code .token
-moz-osx-font-smoothing initial
-webkit-font-smoothing initial
min-height 1.5rem
position: relative
left: auto
pre::after
color #ccc
content attr(data-lang)
font-size 0.6rem
font-weight 600
height 15px
line-height 15px
padding 5px 10px 0
position absolute
right 0
text-align right
top 0
.markdown-section p.tip
background-color #282828
color #657b83
input[type='search']
background #4f4f4f
border-color #4f4f4f
color #c8c8c8

View File

@ -1,237 +0,0 @@
@import url('https://fonts.googleapis.com/css?family=Thasadith:400,400i,700')
$color-primary = #00ffff
$color-bg = #f0ffff
$color-text = #34495e
$sidebar-width = 300px
@import 'basic/_layout'
@import 'basic/_coverpage'
body
background-color $color-bg
/* sidebar */
.sidebar
background-color $color-bg
color #364149
li
margin 6px 0 6px 0
ul li a
color #505d6b
font-size 14px
font-weight normal
overflow hidden
text-decoration none
text-overflow ellipsis
white-space nowrap
&:hover
text-decoration underline
ul li ul
padding 0
ul li.active > a
border-right 2px solid
color $color-primary
color var(--theme-color, $color-primary)
font-weight 600
.app-sub-sidebar
li
&::before
content '-'
padding-right 4px
float left
/* markdown content found on pages */
.markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section strong
color #2c3e50
font-weight 600
.markdown-section a
color $color-primary
color var(--theme-color, $color-primary)
font-weight 600
&:hover
text-decoration underline
.markdown-section h1
font-size 2rem
margin 0 0 1rem
.markdown-section h2
font-size 1.75rem
margin 45px 0 0.8rem
.markdown-section h3
font-size 1.5rem
margin 40px 0 0.6rem
.markdown-section h4
font-size 1.25rem
.markdown-section h5
font-size 1rem
.markdown-section h6
color #777
font-size 1rem
.markdown-section figure, .markdown-section p
margin 1.2em 0
.markdown-section p, .markdown-section ul, .markdown-section ol
line-height 1.6rem
word-spacing 0.05rem
.markdown-section ul, .markdown-section ol
padding-left 1.5rem
.markdown-section blockquote
border-left 4px solid $color-primary
border-left 4px solid var(--theme-color, $color-primary)
color #858585
margin 2em 0
padding-left 20px
.markdown-section blockquote p
font-weight 600
margin-left 0
.markdown-section iframe
margin 1em 0
.markdown-section em
color #7f8c8d
.markdown-section code
background-color #f8f8f8
border-radius 2px
color #e96900
font-family 'Roboto Mono', Monaco, courier, monospace
margin 0 2px
padding 3px 5px
white-space pre-wrap
.markdown-section > :not(h1):not(h2):not(h3):not(h4):not(h5):not(h6) code
font-size 0.8rem
.markdown-section pre
-moz-osx-font-smoothing initial
-webkit-font-smoothing initial
background-color #f8f8f8
font-family 'Roboto Mono', Monaco, courier, monospace
line-height 1.5rem
margin 1.2em 0
overflow auto
padding 0 1.4rem
position relative
word-wrap normal
/* code highlight */
.token.comment, .token.prolog, .token.doctype, .token.cdata
color #8e908c
.token.namespace
opacity 0.7
.token.boolean, .token.number
color #c76b29
.token.punctuation
color #525252
.token.property
color #c08b30
.token.tag
color #2973b7
.token.string
color $color-primary
color var(--theme-color, $color-primary)
.token.selector
color #6679cc
.token.attr-name
color #2973b7
.token.entity, .token.url, .language-css .token.string, .style .token.string
color #22a2c9
.token.attr-value, .token.control, .token.directive, .token.unit
color $color-primary
color var(--theme-color, $color-primary)
.token.keyword, .token.function
color #e96900
.token.statement, .token.regex, .token.atrule
color #22a2c9
.token.placeholder, .token.variable
color #3d8fd1
.token.deleted
text-decoration line-through
.token.inserted
border-bottom 1px dotted #202746
text-decoration none
.token.italic
font-style italic
.token.important, .token.bold
font-weight bold
.token.important
color #c94922
.token.entity
cursor help
.markdown-section pre > code
-moz-osx-font-smoothing initial
-webkit-font-smoothing initial
background-color #f8f8f8
border-radius 2px
color #525252
display block
font-family 'Roboto Mono', Monaco, courier, monospace
font-size 0.8rem
line-height inherit
margin 0 2px
max-width inherit
overflow inherit
padding 2.2em 5px
white-space inherit
.markdown-section code::after, .markdown-section code::before
letter-spacing 0.05rem
code .token
-moz-osx-font-smoothing initial
-webkit-font-smoothing initial
min-height 1.5rem
position: relative
left: auto
pre::after
color #ccc
content attr(data-lang)
font-size 0.6rem
font-weight 600
height 15px
line-height 15px
padding 5px 10px 0
position absolute
right 0
text-align right
top 0

View File

@ -1,7 +0,0 @@
$color-primary = #000
$color-bg = #fff
$color-text = #000
$sidebar-width = 300px
@import 'basic/_layout'
@import 'basic/_coverpage'

View File

@ -0,0 +1,13 @@
@import '_vars.css';
@import '_vars-advanced.css';
@import '_base.css';
@import '_elements.css';
@import '_app.css';
@import '_coverpage.css';
@import '_navbar.css';
@import '_sidebar.css';
@import '_markdown.css';
@import '_syntax.css';
@import '_util.css';
@import '_mq.css';
@import '_classes.css';

153
src/themes/shared/_app.css Normal file
View File

@ -0,0 +1,153 @@
/* App */
/* ========================================================================== */
body {
> .progress {
position: fixed;
z-index: var(--z-progress);
inset: 0 0 auto 0;
height: 2px;
width: 0%;
background: var(--theme-color);
transition:
width var(--duration-medium) ease,
opacity calc(var(--duration-medium) * 2);
}
}
main {
display: block;
position: relative;
width: 100vw;
min-height: 100vh;
/* Overlay */
&::before {
content: '';
position: fixed;
z-index: var(--z-main-overlay);
top: 0;
bottom: 0;
width: 0;
background: transparent;
transition:
width 0s var(--duration-medium),
background var(--duration-medium);
}
&.hidden {
display: none;
}
> .content {
position: absolute;
inset: 0;
transition: left var(--duration-medium) ease;
body:has(.sidebar.show) & {
left: var(--sidebar-width);
}
/* hideSidebar: true */
body:not:has(.sidebar) & {
position: static;
}
}
}
.github-corner {
position: absolute;
top: 0;
right: 0;
z-index: var(--z-github-corner);
border-bottom: 0;
text-decoration: none;
&:hover {
.octo-arm {
animation: github-corner 560ms ease-in-out;
}
}
svg {
height: var(--navbar-height);
width: var(--navbar-height);
color: var(--color-bg);
fill: var(--theme-color);
}
}
@keyframes github-corner {
0%,
100% {
rotate: 0;
}
20%,
60% {
rotate: -25deg;
}
40%,
80% {
rotate: 10deg;
}
}
.loading:empty /* Block: <div class="loading"></div> */,
.loading:not(:empty)::before /* Pseudo: <div class="loading">Content</div> */ {
--_gradient: no-repeat
radial-gradient(farthest-side, var(--theme-color) 92%, #0000);
content: '';
display: block;
width: 36px;
aspect-ratio: 1;
background:
var(--_gradient) top,
var(--_gradient) left,
var(--_gradient) right,
var(--_gradient) bottom;
background-size: 10px 10px;
animation: loading 1s infinite;
}
.loading:not(:empty)::before {
position: absolute;
z-index: 1;
top: 50%;
left: 50%;
translate: -50% -50%;
}
@keyframes loading {
to {
rotate: 0.5turn;
}
}
#app {
display: flex;
align-items: center;
justify-content: center;
gap: 0.75em;
height: 100vh;
padding-inline: 50px;
}
#skip-to-content {
position: fixed;
z-index: var(--z-skip-to-content);
top: 0;
left: 50%;
opacity: 0;
translate: -50% -100%;
transition-property: opacity, translate;
transition-duration: 0s, var(--duration-medium);
transition-delay: var(--duration-medium), 0s;
transition-timing-function: ease;
&:focus {
opacity: 1;
translate: -50% 0.75rem;
transition-duration: 0s, var(--duration-medium);
transition-delay: 0s, 0s;
}
}

View File

@ -0,0 +1,63 @@
/* Base */
/* ========================================================================== */
*,
*::before,
*::after {
box-sizing: border-box;
border: 0;
font: inherit;
-webkit-text-size-adjust: none;
text-size-adjust: none;
}
html,
body {
height: 100vh;
}
html {
background: var(--color-bg);
color: var(--color-text);
font-family: var(--font-family);
font-feature-settings:
'liga' 1,
'calt' 1; /* NOTE: Fix for Chrome */
font-optical-sizing: auto;
font-size: var(--font-size);
font-weight: var(--font-weight);
line-height: var(--line-height);
scroll-padding-top: var(--scroll-padding-top);
}
body {
overflow-x: hidden;
margin: 0;
&:not(.ready) {
overflow: hidden;
[data-cloak],
.app-nav,
> nav {
display: none;
}
}
/* Loading animation */
&.loading {
&::before {
position: fixed;
}
/* Content or cover loaded */
&:has(:where(#main, .cover-main):not(:empty)) {
&::before {
display: none;
}
}
}
}
[tabindex='-1']:focus {
outline: none !important;
}

View File

@ -0,0 +1,181 @@
/* Sidebar chevrons */
/* ========================================================================== */
/* prettier-ignore */
:root:has(body[class*='sidebar-chevron']) {
--sidebar-chevron-collapsed-color: var(--color-mono-3);
--sidebar-chevron-expanded-color : var(--theme-color);
/* Chevron right (Mono) */
--sidebar-pagelink-bg: no-repeat var(--_sidebar_pagelink-bg-left)
calc(50% - 2.5px) / 6px 5px
linear-gradient(
45deg,
transparent 2.75px,
var(--sidebar-chevron-collapsed-color) 2.75px 4.25px,
transparent 4px
),
no-repeat var(--_sidebar_pagelink-bg-left) calc(50% + 2.5px) / 6px 5px
linear-gradient(
135deg,
transparent 2.75px,
var(--sidebar-chevron-collapsed-color) 2.75px 4.25px,
transparent 4px
);
/* Chevron right (Theme color) */
--sidebar-pagelink-bg-collapsed: no-repeat var(--_sidebar_pagelink-bg-left)
calc(50% - 2.5px) / 6px 5px
linear-gradient(
45deg,
transparent 2.75px,
var(--sidebar-chevron-expanded-color) 2.75px 4.25px,
transparent 4px
),
no-repeat var(--_sidebar_pagelink-bg-left) calc(50% + 2.5px) / 6px 5px
linear-gradient(
135deg,
transparent 2.75px,
var(--sidebar-chevron-expanded-color) 2.75px 4.25px,
transparent 4px
);
/* Chevron down (Theme color) */
--sidebar-pagelink-bg-expanded: no-repeat
calc(var(--_sidebar_pagelink-bg-left) - 2px) center / 5px 6px
linear-gradient(
225deg,
transparent 2.75px,
var(--sidebar-chevron-expanded-color) 2.75px 4.25px,
transparent 4.25px
),
no-repeat calc(var(--_sidebar_pagelink-bg-left) + 3px) center / 5px 6px
linear-gradient(
135deg,
transparent 2.75px,
var(--sidebar-chevron-expanded-color) 2.75px 4.25px,
transparent 4.25px
);
/* Dot (active without children) */
--sidebar-pagelink-bg-empty: no-repeat var(--_sidebar_pagelink-bg-left) center /
7px 7px
radial-gradient(
circle,
var(--sidebar-chevron-expanded-color) 0,
var(--sidebar-chevron-expanded-color) 70%,
transparent 71%
);
}
body[class*='sidebar-chevron'] {
.sidebar-nav a.page-link.no-chevron {
background: none;
}
}
/* Left */
/* -------------------------------------------------------------------------- */
:root:has(body.sidebar-chevron-left) {
--_sidebar_pagelink-bg-left: 2px;
}
body.sidebar-chevron-left {
.sidebar-nav {
--_inset: 18px;
li {
a.page-link {
padding-left: var(--_inset);
}
&:has(> .page-link, > p > .page-link) {
> ul,
> p > ul {
margin-left: calc(var(--_sidebar-list-inset) + var(--_inset));
padding-left: 0;
}
}
}
}
}
/* Right */
/* -------------------------------------------------------------------------- */
:root:has(body.sidebar-chevron-right) {
--_sidebar_pagelink-bg-left: calc(100% - var(--_sidebar-inset));
}
body.sidebar-chevron-right {
.sidebar-nav {
li {
a {
padding-right: calc(var(--_sidebar-inset) + 15px);
}
}
}
}
/* Sidebar groups */
/* ========================================================================== */
/* prettier-ignore */
:root:has(body.sidebar-group-box) {
--sidebar-group-border : 1px solid var(--sidebar-border-color);
--sidebar-group-spacing: 1em;
}
/* prettier-ignore */
:root:has(body.sidebar-group-underline) {
--sidebar-group-spacing : 0.5em;
--sidebar-group-title-border : 1px solid var(--sidebar-border-color);
--sidebar-group-title-spacing: 0.35em;
}
/* Sidebar link clamp */
/* ========================================================================== */
body.sidebar-link-clamp {
.sidebar {
a {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
/* Sidebar toggle */
/* ========================================================================== */
body.sidebar-toggle-chevron {
.sidebar-toggle-button {
span {
--_size: 10px;
border: solid currentColor;
border-radius: 0;
background: transparent;
&:where(:not(:first-child)) {
display: none;
}
&:first-child {
height: var(--_size);
width: var(--_size);
border-width: 2px 2px 0 0;
rotate: 45deg;
translate: -20%;
body:has(.sidebar.show) & {
rotate: -135deg;
translate: 20%;
}
}
}
}
}
body.sidebar-toggle-hamburger {
.sidebar-toggle-button {
span {
height: 2px;
width: 65%;
max-width: 16px;
}
}
}

View File

@ -0,0 +1,73 @@
/* Cover */
/* ========================================================================== */
.cover {
display: none;
position: relative;
z-index: var(--z-cover);
min-height: 100vh;
width: 100%;
align-items: center;
background: var(--cover-bg);
color: var(--cover-color);
&:before {
content: '';
position: absolute;
inset: 0 0 0 0;
background: var(--cover-bg-overlay);
-webkit-backdrop-filter: brightness(var(--cover-bg-brightness));
backdrop-filter: brightness(var(--cover-bg-brightness));
}
&.show {
display: flex;
}
a:not(.anchor) {
text-decoration-color: var(--theme-color);
}
blockquote {
font-size: var(--font-size-xl);
line-height: var(--heading-line-height);
text-align: center;
&,
& > p {
margin-block: 1rem;
}
}
h1 {
position: relative;
color: var(--cover-title-color);
font: var(--cover-title-font);
a {
&,
&:hover {
text-decoration-color: transparent;
}
}
small {
font-weight: var(--font-weight);
position: absolute;
bottom: 0;
}
}
ul {
margin-bottom: 1.5rem;
padding: 0;
list-style-type: none;
}
}
.cover-main {
position: relative;
z-index: 1;
flex: 1;
margin: 5vh var(--content-margin-inline);
text-align: center;
}

View File

@ -0,0 +1,332 @@
/* Elements */
/* ========================================================================== */
small,
sub,
sup {
display: inline-block;
font-size: var(--font-size-s);
}
a {
color: var(--link-color);
/* NOTE: Safari may require a forced redraw when changing text-decoration
values (e.g., :hover). One way to do this is via translate: 0 0 0. */
&:not(.button) {
text-decoration-color: var(--link-underline-color);
text-decoration-line: underline;
text-decoration-style: solid;
text-decoration-thickness: var(--link-underline-thickness);
text-underline-offset: 2px;
&:hover {
color: var(--link-color-hover);
text-decoration-color: var(--link-underline-color-hover);
text-decoration-thickness: var(--link-underline-thickness-hover);
}
}
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
line-height: var(--heading-line-height);
}
hr {
margin-block: 1em;
border: none;
border-bottom: 1px solid var(--border-color);
}
ins {
text-decoration: underline;
}
kbd {
display: inline-block;
min-width: 2em;
padding: 0.3em 0.5em;
border: var(--kbd-border);
border-radius: var(--kbd-border-radius);
background: var(--kbd-bg);
color: var(--kbd-color);
font-size: var(--font-size-s);
line-height: 1;
text-align: center;
white-space: nowrap;
}
mark {
padding-inline: 1px;
border-radius: 1px;
background: var(--mark-bg);
color: var(--mark-color);
}
strong {
color: var(--strong-color);
font-weight: var(--strong-font-weight);
}
summary {
cursor: pointer;
> * {
display: inline;
}
}
:disabled {
cursor: not-allowed;
}
/* Buttons */
/* ---------------------------------- */
a.button,
button[type],
input.button,
input:is([type='button'], [type='reset'], [type='submit']) {
display: inline-block;
margin-block: 0.35em;
border: 2px solid var(--color-mono-2);
box-shadow: 0 0 0 3px transparent;
background: var(--color-mono-2);
color: var(--color-text);
cursor: pointer;
vertical-align: middle;
transition: outline-color var(--duration-fast);
&:not(:focus-visible) {
outline-color: transparent;
}
&:hover {
outline: 3px solid var(--theme-color-3);
outline-offset: 1px;
}
+ & {
margin-left: 0.25em;
}
&.primary,
&.secondary {
border-color: var(--button-bg);
}
/* Primary Button */
&.primary {
background: var(--button-bg);
color: var(--button-color);
}
/* Secondary Button */
&.secondary {
background: transparent;
color: var(--button-bg);
}
}
a.button,
button[type],
input:where([type='button'], [type='reset'], [type='submit']) {
padding: var(--button-padding);
border-radius: var(--button-border-radius);
text-decoration: none;
}
button[type],
input.button,
input:where([type='button'], [type='reset'], [type='submit']) {
&:disabled {
opacity: 0.6;
filter: grayscale(100%);
pointer-events: none;
}
}
button:not([type]) {
color: inherit;
}
/* Emoji */
/* ---------------------------------- */
.emoji {
&:where(img) {
height: 1.2em;
vertical-align: middle;
}
&:where(span) {
font-family: var(--font-family-emoji);
font-size: var(--font-size-emoji);
vertical-align: middle;
}
}
/* Form Elements
/* ---------------------------------- */
fieldset,
input:not([type='checkbox']),
optgroup,
option,
select,
textarea {
&:disabled {
opacity: 0.6;
}
}
fieldset,
input,
select,
textarea {
max-width: 100%;
border-radius: var(--form-element-border-radius);
}
input,
select,
textarea {
padding: 0.25em 0.5em;
border: 1px solid var(--form-element-border-color);
}
input,
select,
textarea {
background: var(--form-element-bg);
color: var(--form-element-color);
}
input,
label,
select {
vertical-align: middle;
}
fieldset {
padding: 1em;
border: 1px solid var(--color-mono-2);
> :first-child,
> legend + * {
margin-top: 0;
}
> :last-child {
margin-bottom: 0;
}
}
input:where([type='checkbox'], [type='radio']) {
--_size: 1.1em;
appearance: none;
display: inline-block;
position: relative;
top: -0.1em;
height: var(--_size);
width: var(--_size);
margin: 0;
padding: 0;
&.toggle {
--_inset: 2px;
--_handle-size: calc(var(--_size) - (var(--_inset) * 2));
height: calc(var(--_size) + 2px);
width: calc(var(--_size) * 1.9);
border-radius: 100vh;
&::before,
&::after {
all: unset;
}
&::before {
content: '';
display: inline-block;
position: absolute;
top: var(--_inset);
left: var(--_inset);
height: var(--_handle-size);
width: var(--_handle-size);
border-radius: 100vh;
background: var(--color-mono-3);
transition: all var(--duration-fast);
}
&:checked {
background: var(--theme-color);
border-color: var(--theme-color);
&::before {
left: calc(100% - var(--_handle-size) - var(--_inset));
background: var(--color-bg);
}
}
}
label & {
margin-right: 0.25em;
}
}
input:where([type='checkbox']):not(.toggle) {
border-radius: min(var(--form-element-border-radius), 3px);
&::before {
content: '';
display: inline-block;
position: absolute;
top: 43%;
left: 50%;
rotate: 40deg;
translate: -50% -50%;
height: 0.7em;
width: 0.4em;
border-bottom: 2px solid transparent;
border-right: 2px solid transparent;
}
&:checked {
border-color: var(--theme-color);
background: var(--theme-color);
&::before {
border-color: var(--color-bg);
}
}
}
input:where([type='radio']):not(.toggle) {
border-radius: 100vh;
&:checked {
border-color: var(--theme-color);
box-shadow: inset 0 0 0 0.25em var(--theme-color);
}
}
label {
display: inline-block;
}
select {
appearance: none;
padding-right: 20px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4.95 10'%3E%3Cpolygon fill='black' opacity='0.5' points='1.41 4.67 2.48 3.18 3.54 4.67 1.41 4.67'/%3E%3Cpolygon fill='black' opacity='0.5' points='3.54 5.33 2.48 6.82 1.41 5.33 3.54 5.33'/%3E%3C/svg%3E");
background-position: right 2px center;
background-repeat: no-repeat;
font-weight: normal; /* Fix for Safari using serif font is weight is bold */
@media screen and (prefers-color-scheme: dark) {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4.95 10'%3E%3Cpolygon fill='white' opacity='0.4' points='1.41 4.67 2.48 3.18 3.54 4.67 1.41 4.67'/%3E%3Cpolygon fill='white' opacity='0.4' points='3.54 5.33 2.48 6.82 1.41 5.33 3.54 5.33'/%3E%3C/svg%3E");
}
}

View File

@ -0,0 +1,430 @@
/* Markdown */
/* ========================================================================== */
.markdown-section {
position: relative;
width: var(--content-max-width);
max-width: calc(100% - (var(--content-margin-inline) * 2));
margin: var(--content-margin-inline) auto 0 auto;
padding-bottom: 2rem;
body:has(.app-nav) & {
margin-top: calc(var(--navbar-height) + (var(--content-margin-inline) / 2));
}
> :first-child {
margin-top: 0;
}
blockquote,
details,
figure,
form,
iframe,
ol,
output,
p,
pre,
table,
ul {
margin-block: var(--margin-block);
}
a {
&.anchor {
color: inherit;
&:not(:hover) {
text-decoration-color: transparent;
}
&:hover {
translate: 0 0 0; /* Safari Fix: Forced redraw */
text-decoration-color: var(--link-underline-color-hover);
}
}
}
blockquote {
position: relative;
overflow: auto;
margin-inline: 0;
padding: var(--blockquote-padding);
border: solid var(--blockquote-border-color);
border-width: var(--blockquote-border-width);
border-radius: var(--blockquote-border-radius);
background: var(--blockquote-bg);
color: var(--blockquote-color);
> :first-child {
margin-top: 0;
}
> :last-child {
margin-bottom: 0;
}
}
em {
font-style: italic;
}
hr {
margin-block: 2em;
}
iframe {
display: block;
width: 100%;
max-width: 100%;
border: 1px solid var(--color-mono-2);
}
img {
max-width: 100%;
}
kbd {
margin-inline: 0.15em;
&.alt {
padding: 0.5em;
border: var(--kbd-alt-border);
border-radius: var(--kbd-alt-border-radius);
box-shadow: var(--kbd-alt-box-shadow);
background: var(--kbd-alt-bg);
color: var(--kbd-alt-color);
font-size: var(--font-size-m);
}
}
/* Callouts */
/* ---------------------------------- */
.callout {
position: relative;
margin-block: calc(var(--margin-block) * 1.5);
padding: var(--callout-padding);
border: solid var(--callout-border-color);
border-width: var(--callout-border-width);
border-radius: var(--callout-border-radius);
background: var(--callout-bg);
color: var(--callout-color);
/* Charm */
&::before {
content: var(--callout-charm-content);
position: absolute;
inset: var(--callout-charm-inset);
height: var(--callout-charm-size);
width: var(--callout-charm-size);
translate: var(--callout-charm-translate);
border-radius: var(--callout-charm-border-radius);
background: var(--callout-charm-bg);
color: var(--callout-charm-color);
font-size: var(--callout-charm-font-size);
font-weight: var(--strong-font-weight);
line-height: var(--callout-charm-size);
text-align: center;
}
code,
strong {
color: inherit;
}
code {
background: rgba(0, 0, 0, 0.08);
}
}
/* Code, Output, Samp */
/* ---------------------------------- */
code,
output,
pre {
border-radius: var(--border-radius);
}
code,
pre,
samp {
font-family: var(--font-family-mono);
font-size: var(--font-size-mono);
}
output,
pre[data-lang] {
position: relative;
&::after {
content: attr(data-lang);
position: absolute;
top: 0.5rem;
right: 0.5rem;
color: inherit;
font-family: var(--font-family);
font-size: var(--font-size-xs);
letter-spacing: 0.02em;
line-height: 1;
opacity: 0.5;
}
}
code,
samp {
white-space: pre-wrap;
}
code {
margin: 0 0.1em;
padding: 0.2em 0.35em;
background: var(--code-bg);
color: var(--code-color);
.token {
position: relative;
left: auto;
}
}
output {
display: block;
padding: 1.7rem 1.4rem 1.4rem;
border: 1px solid var(--color-mono-2);
> :first-child {
margin-top: 0;
}
> :last-child {
margin-bottom: 0;
}
}
pre[data-lang] {
/* NOTE: !important declaration are intended to override third-party Prism theme values */
padding: 0 !important;
border-radius: var(--border-radius) !important;
font-family: var(--font-family-mono) !important;
font-size: var(--font-size-mono) !important;
line-height: inherit !important;
tab-size: 2 !important;
text-align: left;
white-space: pre;
word-spacing: normal;
word-wrap: normal;
word-break: normal;
hyphens: none;
&:only-child {
margin: 0;
}
> code {
display: block;
overflow: auto;
margin: 0 !important;
padding: 0 !important;
padding-block: 1.5rem !important;
padding-inline: 1.5rem !important;
background: inherit;
color: inherit;
font-size: inherit;
white-space: inherit;
}
}
samp {
font-weight: var(--strong-font-weight);
.token {
position: relative;
left: auto;
}
}
/* Headings */
/* --------------------------------- */
:where(h1, h2, h3, h4, h5, h6) {
margin: 2rem 0 0.5em;
color: var(--heading-color);
font-weight: var(--heading-font-weight);
/* Prevent long titles from causing horizontal scrolling */
&[id] a {
display: block;
overflow: hidden;
text-overflow: ellipsis;
}
}
h1,
h2 {
margin-top: 2.5rem;
}
:is(h1, h2, h3, h4, h5, h6) + * {
margin-top: 0;
}
h1 {
font-size: var(--font-size-xxxl);
&:first-of-type {
margin-top: 0;
}
}
h2 {
--_spacing: 0.5em;
margin-bottom: calc(
var(--_spacing) + (var(--heading-line-height) - var(--font-size-xxl))
);
padding-bottom: var(--_spacing);
border-bottom: 1px solid var(--border-color);
font-size: var(--font-size-xxl);
}
h3 {
font-size: var(--font-size-xl);
}
h4 {
font-size: var(--font-size-l);
}
h5 {
font-size: var(--font-size-m);
}
h6 {
&,
& + :not(h1, h2, h3, h4, h5) {
font-size: var(--font-size-s);
}
}
/* Lists */
/* ---------------------------------- */
ol,
ul {
margin: 0;
padding-inline-start: 1.5em;
& & {
margin-top: 0.25em;
}
}
li {
&:not(:last-child) {
margin-bottom: 0.25em;
}
}
ul.task-list {
--_checkbox-margin: 0.2em;
--_checkbox-offset: 1.6em;
padding-inline-start: 0.6em;
input[type='checkbox'] {
margin-top: -0.15em;
margin-right: var(--_checkbox-margin);
margin-left: calc(0px - var(--_checkbox-offset));
}
li {
position: relative;
margin-top: var(--_checkbox-margin);
margin-bottom: var(--_checkbox-margin);
margin-left: var(--_checkbox-offset);
list-style-type: none;
/* Vertical Connector */
&:has(.task-list) {
&::before {
content: '';
position: absolute;
z-index: -1;
top: 1em;
left: -1em;
bottom: 0;
border-left: 1px solid var(--color-mono-2);
}
}
}
ul.task-list {
padding-inline-start: 1.5em;
li {
margin-left: var(--_checkbox-margin);
}
> li {
/* Horizontal Connector */
&::after {
content: '';
position: absolute;
z-index: -1;
top: 0.9em;
left: -2.7em;
width: 1.25em;
border-top: 1px solid var(--color-mono-2);
border-left: none;
}
/* Horizontal Connector + Mask */
&:last-child {
&::after {
bottom: 0;
background: var(--color-bg);
}
}
}
}
}
/* Tables */
/* ---------------------------------- */
th,
td {
padding: 0.25em 0.75em;
border: 1px solid var(--color-mono-2);
}
table {
display: block;
width: 100%;
overflow: auto;
border-collapse: collapse;
border-spacing: 0;
}
thead {
display: none;
font-weight: var(--strong-font-weight);
text-align: left;
&:has(th:not(:empty)) {
display: table-header-group;
}
}
th {
font-weight: var(--strong-font-weight);
&:not([align]) {
text-align: left;
}
}
tr {
border-top: 1px solid var(--color-mono-2);
&:nth-child(2n) {
background: var(--table-row-alt-bg);
}
}
}

72
src/themes/shared/_mq.css Normal file
View File

@ -0,0 +1,72 @@
/* Media */
/* ========================================================================== */
:root {
--_mobile-breakpoint: 640px; /* JS Accessible. Match with MQ below. */
}
@media (prefers-reduced-motion) {
:root {
--duration-slow: 0s;
--duration-medium: 0s;
--duration-fast: 0s;
}
}
@media print {
.app-nav,
.github-corner,
.sidebar,
.sidebar-toggle {
display: none;
}
}
/* Mobile */
@media screen and (max-width: 640px) {
.app-nav-merged {
display: block;
}
body:has(.sidebar.show) {
.app-nav,
.github-corner {
pointer-events: none;
}
.app-nav {
left: 0;
}
main {
&::before {
width: 100%;
background: rgba(0, 0, 0, 0.15);
transition:
width 0s,
background var(--duration-medium);
}
}
.sidebar-toggle {
width: calc(100% - var(--sidebar-width));
&:hover {
background: transparent;
}
}
main > .content {
left: 0;
}
}
body:has(.app-nav-merged) {
.app-nav {
display: none;
}
.markdown-section {
margin-top: var(--content-margin-inline);
}
}
}

View File

@ -0,0 +1,122 @@
/* Nav Bar */
/* ========================================================================== */
.app-nav {
display: flex;
align-items: center;
justify-content: end;
gap: 1em;
position: absolute;
z-index: var(--z-app-nav);
top: 0;
right: 0;
left: 0;
height: var(--navbar-height);
padding-inline: var(--content-margin-inline);
transition: left var(--duration-medium) ease;
&:has(~ .github-corner) {
padding-inline-end: calc(var(--navbar-height) + 1em);
}
body:where(:has(.sidebar.show)) & {
left: var(--sidebar-width);
}
a {
color: var(--navbar-link-color);
text-decoration-color: transparent;
&:hover {
translate: 0; /* HACK: Force Safari to render text-decoration */
text-decoration-color: var(--navbar-link-color-active);
}
.active & {
color: var(--navbar-link-color-active);
}
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
> ul {
display: flex;
justify-content: end;
flex-wrap: wrap;
gap: 0 1em;
}
li {
display: inline-block;
position: relative;
white-space: nowrap;
cursor: pointer;
/* Dropdown */
ul {
position: absolute;
z-index: 1;
top: -9999vh;
right: 50%;
translate: 50% 0;
overflow-y: auto;
max-height: calc(100vh - 61px);
padding: 1em;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
background: var(--color-bg);
li:last-child & {
right: calc(0px - var(--content-margin-inline) / 2);
translate: 0;
}
}
li {
display: block;
margin-block: var(--navbar-drop-link-spacing);
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
a {
display: flex;
align-items: center;
gap: 0.25em;
margin: 0;
&:hover {
color: var(--navbar-link-color-active);
}
}
}
&:focus-within,
&:hover {
ul {
top: 100%;
}
}
}
p {
margin: 0;
}
> :where(a, p),
> ul > li > :where(a, p) {
white-space: nowrap;
}
}
.app-nav-merged {
display: none;
}

View File

@ -0,0 +1,285 @@
/* Sidebar */
/* ========================================================================== */
:root {
--_sidebar-inset: 20px;
--_sidebar-list-inset: 0.85em;
--_sidebar-scrollbar-width: 10px; /* macOS overlay scrollbar default */
@supports (scrollbar-width: auto) {
--_sidebar-scrollbar-width: 4px;
}
}
.app-nav-merged,
.sidebar-nav {
> * {
margin-inline: var(--_sidebar-inset);
&:first-child {
margin-top: 0;
}
}
> hr {
margin-inline: 0;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
> ul {
> * {
margin-left: var(--_sidebar-inset);
}
ul {
padding-left: var(--_sidebar-list-inset);
}
> li {
&.group {
padding-top: var(--sidebar-group-spacing);
border-top: var(--sidebar-group-border);
&:has(+ :not(.group)),
&:last-child {
border-bottom: var(--sidebar-group-border);
}
}
}
}
li {
margin-block: var(--sidebar-link-spacing);
> p {
margin: 0;
/* Text-only <p> tags */
&.group-title {
margin-right: var(--_sidebar-inset);
margin-bottom: var(--sidebar-group-title-spacing);
padding-bottom: var(--sidebar-group-title-spacing);
border-bottom: var(--sidebar-group-title-border);
color: var(--sidebar-group-title-color);
font-size: var(--sidebar-group-title-font-size);
font-weight: var(--sidebar-group-title-font-weight);
}
}
a {
display: block;
padding-right: var(--_sidebar-inset);
overflow: hidden;
&:has(img, svg) {
display: flex;
align-items: center;
gap: 0.25em;
}
}
&.active {
> a {
color: var(--sidebar-link-color-active);
}
}
&.collapse {
> :not(a) {
display: none;
}
}
}
}
.sidebar {
visibility: hidden;
position: absolute;
z-index: var(--z-sidebar);
top: 0;
bottom: 0;
left: 0;
translate: calc(0px - var(--sidebar-width));
width: var(--sidebar-width);
overscroll-behavior: contain;
border-right: 1px solid var(--sidebar-border-color);
background: var(--sidebar-bg);
color: var(--sidebar-color);
font-size: var(--sidebar-font-size);
transition:
translate var(--duration-medium) ease,
visibility var(--duration-medium);
/* Non-webkit browsers: style scrollbar for consistency */
@supports (scrollbar-width: auto) {
&::-webkit-scrollbar {
width: var(--_sidebar-scrollbar-width);
}
&::-webkit-scrollbar-thumb {
border-radius: var(--_sidebar-scrollbar-width);
background: transparent;
}
&:hover {
&::-webkit-scrollbar-thumb {
background: rgba(136, 136, 136, 0.4);
}
&::-webkit-scrollbar-track {
background: rgba(136, 136, 136, 0.1);
}
}
}
a[href] {
text-decoration-color: transparent;
color: var(--sidebar-link-color);
&:hover {
translate: 0; /* HACK: Force Safari to render text-decoration */
text-decoration-color: var(--sidebar-link-color-active);
}
}
.app-name {
margin: var(--sidebar-name-margin);
color: var(--sidebar-name-color);
font-family: var(--sidebar-name-font-family);
font-size: var(--sidebar-name-font-size);
font-weight: var(--sidebar-name-font-weight);
text-align: center;
}
a.app-name-link {
img {
display: block;
max-width: 100%;
}
&:hover {
color: var(--sidebar-link-color-active);
text-decoration-color: transparent;
}
}
body:has(.sidebar.show) & {
visibility: visible;
translate: 0;
}
body.sticky & {
position: fixed;
overflow-y: auto;
}
}
.sidebar-nav {
li {
a.page-link {
border-radius: var(--border-radius);
background: var(--sidebar-pagelink-bg);
.active > &,
:has(.active) > & {
background: var(--sidebar-pagelink-bg-expanded);
&:only-child {
background: var(--sidebar-pagelink-bg-empty);
}
}
.collapse > & {
background: var(--sidebar-pagelink-bg-collapsed);
}
}
}
}
.sidebar-toggle {
display: flex;
align-items: var(--sidebar-toggle-alignment);
justify-content: start;
position: absolute;
z-index: var(--z-sidebar-toggle);
top: 0;
bottom: 0;
width: var(--sidebar-toggle-width);
margin: 0;
padding: 0;
border: 0;
background: transparent;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
transition:
background var(--duration-medium),
translate var(--duration-medium) ease;
@media screen and (any-hover) {
&:hover {
background: color-mix(
in srgb,
var(--sidebar-toggle-bg-hover) 10%,
transparent
);
}
}
body:where(:has(.sidebar.show)) & {
translate: var(--sidebar-width);
transition:
background 0s,
translate var(--duration-medium) ease;
}
body.sticky & {
position: fixed;
}
/* Increase tap target size on touch-only devices */
@media screen and not (any-hover) {
width: calc(var(--content-margin-inline) - 10px);
}
}
.sidebar-toggle-button {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: var(--sidebar-toggle-height);
width: var(--sidebar-toggle-width);
margin-block: var(--sidebar-toggle-margin-block);
border-radius: 0 var(--border-radius) var(--border-radius) 0;
background: var(--sidebar-toggle-bg);
color: var(--sidebar-toggle-color);
transition-duration: var(--duration-medium);
transition-property: background, translate;
span {
--_size: 4px;
display: block;
height: var(--_size);
width: var(--_size);
border-radius: 100vh;
background: currentColor;
transition: background var(--duration-medium);
&:nth-child(2) {
margin-block: calc(var(--_size) - 1px);
}
}
@media screen and (any-hover) {
&:hover,
body:where(:has(.sidebar-toggle:hover)) & {
background: var(--sidebar-toggle-bg-hover);
color: var(--sidebar-toggle-color-hover);
}
}
}

View File

@ -0,0 +1,84 @@
/* Syntax Highlighting */
/* See: https://prismjs.com */
/* ========================================================================== */
pre[data-lang] {
background: var(--codeblock-bg);
color: var(--codeblock-color);
}
.namespace {
opacity: 0.7;
}
.token {
&.boolean,
&.constant,
&.deleted,
&.number,
&.property,
&.symbol,
&.tag {
color: var(--codeblock-tag);
}
&.attr-name,
&.builtin,
&.char,
&.inserted,
&.selector,
&.string {
color: var(--codeblock-selector);
}
&.entity,
&.operator,
&.url,
.language-css &.string,
.style &.string {
color: var(--codeblock-operator);
}
&.cdata,
&.comment,
&.doctype,
&.prolog {
color: var(--codeblock-comment);
}
&.atrule,
&.attr-value,
&.keyword {
color: var(--codeblock-keyword);
}
&.important,
&.regex,
&.variable {
color: var(--codeblock-variable);
}
&.bold,
&.important {
font-weight: var(--strong-font-weight);
}
&.entity {
cursor: help;
}
&.function {
color: var(--codeblock-function);
}
&.important {
color: var(--codeblock-important);
}
&.italic {
font-style: italic;
}
&.punctuation {
color: var(--codeblock-punctuation);
}
}

View File

@ -0,0 +1,32 @@
/* Utility */
/* ========================================================================== */
[class*='clamp-'] {
overflow: hidden;
text-overflow: ellipsis;
&:not(.clamp-1) {
display: -webkit-box;
-webkit-box-orient: vertical;
}
}
.clamp-1 {
white-space: nowrap;
}
.clamp-2 {
-webkit-line-clamp: 2;
}
.clamp-3 {
-webkit-line-clamp: 3;
}
.visually-hidden {
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}

View File

@ -0,0 +1,14 @@
/* Variables: Advanced */
/* ========================================================================== */
/* prettier-ignore */
:root {
/* z-index */
--z-skip-to-content: 2147483647;
--z-progress : 2147483637;
--z-sidebar : 60;
--z-sidebar-toggle : 50;
--z-main-overlay : 40;
--z-github-corner : 30;
--z-app-nav : 20;
--z-cover : 10;
}

202
src/themes/shared/_vars.css Normal file
View File

@ -0,0 +1,202 @@
/* Variables: Basic */
/* ========================================================================== */
:root {
/* Color */
--color-bg : #fff;
--color-text : #333;
--theme-color: #0b85d7;
/* Color: Monochromatic */
--color-mono-min: var(--color-bg);
--color-mono-1 : color-mix(in srgb, var(--color-mono-min), var(--color-mono-max) 5%);
--color-mono-2 : color-mix(in srgb, var(--color-mono-min), var(--color-mono-max) 10%);
--color-mono-3 : color-mix(in srgb, var(--color-mono-min), var(--color-mono-max) 20%);
--color-mono-4 : color-mix(in srgb, var(--color-mono-min), var(--color-mono-max) 32%);
--color-mono-5 : color-mix(in srgb, var(--color-mono-max), var(--color-mono-min) 50%);
--color-mono-6 : color-mix(in srgb, var(--color-mono-max), var(--color-mono-min) 32%);
--color-mono-7 : color-mix(in srgb, var(--color-mono-max), var(--color-mono-min) 20%);
--color-mono-8 : color-mix(in srgb, var(--color-mono-max), var(--color-mono-min) 10%);
--color-mono-9 : color-mix(in srgb, var(--color-mono-max), var(--color-mono-min) 5%);
--color-mono-max: var(--color-text);
/* Color: Theme Shades (darker) & Tints (lighter)*/
/* NOTE: Values derived from --theme-color */
--theme-color-1: color-mix(in srgb, var(--theme-color), var(--color-mono-min) 90%);
--theme-color-2: color-mix(in srgb, var(--theme-color), var(--color-mono-min) 75%);
--theme-color-3: color-mix(in srgb, var(--theme-color), var(--color-mono-min) 55%);
--theme-color-4: color-mix(in srgb, var(--theme-color), var(--color-mono-min) 30%);
--theme-color-5: color-mix(in srgb, var(--theme-color), var(--color-mono-max) 30%);
--theme-color-6: color-mix(in srgb, var(--theme-color), var(--color-mono-max) 55%);
--theme-color-7: color-mix(in srgb, var(--theme-color), var(--color-mono-max) 75%);
--theme-color-8: color-mix(in srgb, var(--theme-color), var(--color-mono-max) 90%);
/* Typography */
--font-family : system-ui, sans-serif;
--font-family-emoji: 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
--font-family-mono : ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace;
--font-size : 16px; /* px */
--font-size-xxxl : calc(var(--font-size-xxl) * var(--modular-scale));
--font-size-xxl : calc(var(--font-size-xl) * var(--modular-scale));
--font-size-xl : calc(var(--font-size-l) * var(--modular-scale));
--font-size-l : calc(1rem * var(--modular-scale));
--font-size-m : var(--font-size);
--font-size-s : max(13px, calc(var(--font-size-m) / var(--modular-scale)));
--font-size-xs : max(11px, calc(var(--font-size-s) / var(--modular-scale)));
--font-size-emoji : 1.2em;
--font-size-mono : 0.875rem;
--font-weight : 350;
--font-weight-mono : var(--font-weight);
--line-height : 1.6;
--modular-scale : 1.250; /* 1.067, 1.125, 1.200, 1.250, 1.333, 1.414, 1.500, 1.618 */
/* Common */
--border-color : var(--color-mono-2);
--border-radius : 3px; /* Single value */
--duration-slow : 500ms;
--duration-medium : 250ms;
--duration-fast : 150ms;
--margin-block : 1rem; /* Single value */
--scroll-padding-top: var(--margin-block);
/* Content */
--content-margin-inline: 45px; /* Single value */
--content-max-width : 72ch;
/* Cover */
--cover-bg : unset;
--cover-bg-brightness: 1;
--cover-bg-overlay : radial-gradient(transparent 60%, rgba(0, 0, 0, 0.1));
--cover-color : ;
--cover-title-color : var(--strong-color);
--cover-title-font : var(--font-size-xxxl) var(--font-family);
/* Elements */
--blockquote-bg : ;
--blockquote-border-color : var(--theme-color);
--blockquote-border-radius : 0;
--blockquote-border-width : 0 0 0 4px;
--blockquote-color : var(--color-mono-6);
--blockquote-padding : 0 0 0 1.5em;
--button-bg : var(--theme-color);
--button-border-radius : 100vh;
--button-color : #fff;
--button-padding : 0.3em 1.25em 0.315em 1.25em;
--callout-bg : ;
--callout-border-color : ;
--callout-border-radius : 0 var(--border-radius) var(--border-radius) 0;
--callout-border-width : 0 0 0 4px;
--callout-charm-bg : ;
--callout-charm-border-radius : 100vh;
--callout-charm-color : ;
--callout-charm-content : ;
--callout-charm-font-size : 1.2em;
--callout-charm-inset : 50% auto auto -2px;
--callout-charm-size : 1.3em;
--callout-charm-translate : -50% -50%;
--callout-color : ;
--callout-padding : 1em 1em 1em var(--callout-charm-size);
--code-bg : var(--color-mono-1);
--code-color : ;
--codeblock-bg : var(--code-bg);
--codeblock-color : var(--code-color);
--codeblock-comment : #6e8090;
--codeblock-function : #dd4a68;
--codeblock-important : #c94922;
--codeblock-keyword : #07a;
--codeblock-operator : #a67f59;
--codeblock-property : #c08b30;
--codeblock-punctuation : #999;
--codeblock-selector : #690;
--codeblock-tag : #905;
--codeblock-variable : #e90;
--form-element-bg : var(--color-mono-1);
--form-element-border-color : var(--color-mono-3);
--form-element-border-radius : var(--border-radius);
--form-element-color : ;
--heading-color : var(--strong-color);
--heading-font-weight : 600;
--heading-line-height : calc(2ex + 5px); /* Unit required */
--kbd-bg : var(--color-mono-1);
--kbd-border : 1px solid var(--color-mono-3);
--kbd-border-radius : 4px;
--kbd-color : var(--color-mono-5);
--kbd-alt-bg : var(--color-mono-1);
--kbd-alt-border : none;
--kbd-alt-border-radius : var(--kbd-border-radius);
--kbd-alt-box-shadow : 0 2px 0 1px var(--color-mono-3);
--kbd-alt-color : var(--kbd-color);
--link-color : ;
--link-color-hover : var(--theme-color);
--link-underline-color : var(--theme-color);
--link-underline-color-hover : var(--link-underline-color);
--link-underline-thickness : 2px;
--link-underline-thickness-hover: var(--link-underline-thickness);
--mark-bg : #fef08a;
--mark-color : ;
--strong-color : color-mix(in srgb, var(--color-text), black 35%);
--strong-font-weight : 600;
--table-row-alt-bg : var(--color-mono-1);
/* Navbar */
--navbar-font-size : var(--font-size);
--navbar-height : 4em;
--navbar-link-color : ;
--navbar-link-color-active: var(--theme-color);
--navbar-drop-link-spacing: 0.5em;
/* Sidebar */
--sidebar-bg : var(--color-bg);
--sidebar-border-color : var(--border-color);
--sidebar-color : ;
--sidebar-font-size : var(--font-size);
--sidebar-group-border : ;
--sidebar-group-spacing : ;
--sidebar-group-title-border : ;
--sidebar-group-title-color : var(--strong-color);
--sidebar-group-title-font-size : ;
--sidebar-group-title-font-weight : var(--strong-font-weight);
--sidebar-group-title-spacing : ;
--sidebar-link-color : var(--color-text);
--sidebar-link-color-active : var(--theme-color);
--sidebar-link-spacing : 0.75em;
--sidebar-name-color : var(--strong-color);
--sidebar-name-font-family : var(--font-family);
--sidebar-name-font-size : var(--font-size-xl);
--sidebar-name-font-weight : var(--strong-font-weight);
--sidebar-name-margin : 1.5rem 20px;
--sidebar-pagelink-bg : ;
--sidebar-pagelink-bg-collapsed : ;
--sidebar-pagelink-bg-empty : ;
--sidebar-pagelink-bg-expanded : ;
--sidebar-toggle-alignment : center; /* start center end */
--sidebar-toggle-bg : var(--color-mono-2);
--sidebar-toggle-bg-hover : var(--button-bg);
--sidebar-toggle-color : var(--color-mono-4);
--sidebar-toggle-color-hover : var(--button-color);
--sidebar-toggle-height : 80px;
--sidebar-toggle-margin-block : 20px;
--sidebar-toggle-width : 22px;
--sidebar-width : 280px;
}
/* Scoped Variables */
/* ========================================================================== */
/* Callout: Important */
.callout.important {
--callout-bg : var(--color-mono-1);
--callout-border-color : #f66;
--callout-charm-bg : var(--callout-border-color);
--callout-charm-color : #fff;
--callout-charm-content: '!';
--callout-color : ;
}
/* Callout: Important */
.callout.tip {
--callout-bg : var(--theme-color-1);
--callout-border-color : var(--theme-color);
--callout-charm-bg : var(--callout-border-color);
--callout-charm-color : #fff;
--callout-charm-content: 'i';
--callout-color : ;
}

View File

@ -1,260 +0,0 @@
@import url('https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600')
$color-primary = #42b983
$color-bg = #fff
$color-text = #34495e
$sidebar-width = 300px
@import 'basic/_layout'
@import 'basic/_coverpage'
body
background-color $color-bg
/* sidebar */
.sidebar
background-color $color-bg
color #364149
li
margin 6px 0 6px 0
ul li a
color #505d6b
font-size 14px
font-weight normal
overflow hidden
text-decoration none
text-overflow ellipsis
white-space nowrap
&:hover
text-decoration underline
ul li ul
padding 0
ul li.active > a
border-right 2px solid
color $color-primary
color var(--theme-color, $color-primary)
font-weight 600
.app-sub-sidebar
li
&::before
content '-'
padding-right 4px
float left
/* markdown content found on pages */
.markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section strong
color #2c3e50
font-weight 600
.markdown-section a
color $color-primary
color var(--theme-color, $color-primary)
font-weight 600
.markdown-section h1
font-size 2rem
margin 0 0 1rem
.markdown-section h2
font-size 1.75rem
margin 45px 0 0.8rem
.markdown-section h3
font-size 1.5rem
margin 40px 0 0.6rem
.markdown-section h4
font-size 1.25rem
.markdown-section h5
font-size 1rem
.markdown-section h6
color #777
font-size 1rem
.markdown-section figure, .markdown-section p
margin 1.2em 0
.markdown-section p, .markdown-section ul, .markdown-section ol
line-height 1.6rem
word-spacing 0.05rem
.markdown-section ul, .markdown-section ol
padding-left 1.5rem
.markdown-section blockquote
border-left 4px solid $color-primary
border-left 4px solid var(--theme-color, $color-primary)
color #858585
margin 2em 0
padding-left 20px
.markdown-section blockquote p
font-weight 600
margin-left 0
.markdown-section iframe
margin 1em 0
.markdown-section em
color #7f8c8d
.markdown-section code,
.markdown-section pre,
.markdown-section output::after
font-family 'Roboto Mono', Monaco, courier, monospace
.markdown-section code,
.markdown-section pre
background-color #f8f8f8
z-index 0
.markdown-section pre,
.markdown-section output
margin 1.2em 0
position relative
.markdown-section pre > code,
.markdown-section output
border-radius 2px
display block
.markdown-section pre > code,
.markdown-section output::after
-moz-osx-font-smoothing initial
-webkit-font-smoothing initial
.markdown-section code
border-radius 2px
color #e96900
margin 0 2px
padding 3px 5px
white-space pre-wrap
.markdown-section > :not(h1):not(h2):not(h3):not(h4):not(h5):not(h6) code
font-size 0.8rem
.markdown-section pre
line-height 1.5rem
overflow auto
word-wrap normal
.markdown-section pre > code
color #525252
font-size 0.8rem
padding 2.2em 1.4rem
line-height inherit
margin 5px
max-width inherit
overflow inherit
white-space inherit
.markdown-section pre > code:focus
// outline 5px auto Highlight;
outline 5px auto -webkit-focus-ring-color;
.markdown-section output
padding: 1.7rem 1.4rem
border 1px dotted #ccc
.markdown-section output > :first-child
margin-top: 0;
.markdown-section output > :last-child
margin-bottom: 0;
.markdown-section code::after, .markdown-section code::before,
.markdown-section output::after, .markdown-section output::before
letter-spacing 0.05rem
.markdown-section pre::after,
.markdown-section output::after
content attr(data-lang)
color #ccc
font-size 0.6rem
font-weight 600
height 15px
line-height 15px
padding 5px 10px 0
position absolute
right 0
text-align right
top 0
/* code highlight */
.token.comment, .token.prolog, .token.doctype, .token.cdata
color #8e908c
.token.namespace
opacity 0.7
.token.boolean, .token.number
color #c76b29
.token.punctuation
color #525252
.token.property
color #c08b30
.token.tag
color #2973b7
.token.string
color $color-primary
color var(--theme-color, $color-primary)
.token.selector
color #6679cc
.token.attr-name
color #2973b7
.token.entity, .token.url, .language-css .token.string, .style .token.string
color #22a2c9
.token.attr-value, .token.control, .token.directive, .token.unit
color $color-primary
color var(--theme-color, $color-primary)
.token.keyword, .token.function
color #e96900
.token.statement, .token.regex, .token.atrule
color #22a2c9
.token.placeholder, .token.variable
color #3d8fd1
.token.deleted
text-decoration line-through
.token.inserted
border-bottom 1px dotted #202746
text-decoration none
.token.italic
font-style italic
.token.important, .token.bold
font-weight bold
.token.important
color #c94922
.token.entity
cursor help
code .token
-moz-osx-font-smoothing initial
-webkit-font-smoothing initial
min-height 1.5rem
position: relative
left: auto

View File

@ -159,11 +159,11 @@ test.describe('keyBindings', () => {
await docsifyInit(docsifyInitConfig);
const bodyElm = page.locator('body');
const sidebarElm = page.locator('.sidebar');
await expect(bodyElm).not.toHaveClass(/close/);
await expect(sidebarElm).toHaveClass(/show/);
await page.keyboard.press('\\');
await expect(bodyElm).toHaveClass(/close/);
await expect(sidebarElm).not.toHaveClass(/show/);
});
test('handles custom binding', async ({ page }) => {

View File

@ -14,8 +14,8 @@ test.describe('Creating a Docsify site (e2e tests in Playwright)', () => {
};
});
// Inject docsify theme (vue.css)
await page.addStyleTag({ url: '/dist/themes/vue.css' });
// Inject docsify theme
await page.addStyleTag({ url: '/dist/themes/core.css' });
// Inject docsify.js
await page.addScriptTag({ url: '/dist/docsify.js' });
@ -96,7 +96,7 @@ test.describe('Creating a Docsify site (e2e tests in Playwright)', () => {
background: red !important;
}
`,
styleURLs: ['/dist/themes/vue.css'],
styleURLs: ['/dist/themes/core.css'],
};
await docsifyInit({
@ -214,7 +214,7 @@ test.describe('Creating a Docsify site (e2e tests in Playwright)', () => {
// upon billions upon billions upon billions upon billions.
// `,
// },
// styleURLs: [`/dist/themes/vue.css`],
// styleURLs: [`/dist/themes/core.css`],
// // _logHTML: true,
// });

View File

@ -38,7 +38,7 @@ test.describe('Gtag Plugin Tests', () => {
gtag: gtagList[0],
},
scriptURLs: ['/dist/plugins/gtag.js'],
styleURLs: ['/dist/themes/vue.css'],
styleURLs: ['/dist/themes/core.css'],
};
await docsifyInit({
@ -64,7 +64,7 @@ test.describe('Gtag Plugin Tests', () => {
gtag: gtagList,
},
scriptURLs: ['/dist/plugins/gtag.js'],
styleURLs: ['/dist/themes/vue.css'],
styleURLs: ['/dist/themes/core.css'],
};
await docsifyInit({

View File

@ -25,7 +25,7 @@ test.describe('Search Plugin Tests', () => {
};
const searchFieldElm = page.locator('input[type=search]');
const resultsHeadingElm = page.locator('.results-panel h2');
const resultsHeadingElm = page.locator('.results-panel .title');
await docsifyInit(docsifyInitConfig);
@ -68,7 +68,7 @@ test.describe('Search Plugin Tests', () => {
};
const searchFieldElm = page.locator('input[type=search]');
const resultsHeadingElm = page.locator('.results-panel h2');
const resultsHeadingElm = page.locator('.results-panel .title');
await docsifyInit(docsifyInitConfig);
@ -103,7 +103,7 @@ test.describe('Search Plugin Tests', () => {
};
const searchFieldElm = page.locator('input[type=search]');
const resultsHeadingElm = page.locator('.results-panel h2');
const resultsHeadingElm = page.locator('.results-panel .title');
const resultElm = page.locator('.matching-post');
await docsifyInit(docsifyInitConfig);
@ -129,7 +129,7 @@ test.describe('Search Plugin Tests', () => {
};
const searchFieldElm = page.locator('input[type=search]');
const resultsHeadingElm = page.locator('.results-panel h2');
const resultsHeadingElm = page.locator('.results-panel .title');
await docsifyInit(docsifyInitConfig);
@ -163,7 +163,7 @@ test.describe('Search Plugin Tests', () => {
};
const searchFieldElm = page.locator('input[type=search]');
const resultsHeadingElm = page.locator('.results-panel h2');
const resultsHeadingElm = page.locator('.results-panel .title');
await docsifyInit(docsifyInitConfig);
@ -190,7 +190,7 @@ test.describe('Search Plugin Tests', () => {
};
const searchFieldElm = page.locator('input[type=search]');
const resultsHeadingElm = page.locator('.results-panel h2');
const resultsHeadingElm = page.locator('.results-panel .title');
await docsifyInit(docsifyInitConfig);

View File

@ -25,7 +25,7 @@ const docsifyURL = '/dist/docsify.js'; // Playwright
* @param {String} [options.script] JS to inject via <script> tag
* @param {String|String[]} [options.scriptURLs] External JS to inject via <script src="..."> tag(s)
* @param {String} [options.style] CSS to inject via <style> tag
* @param {String|String[]} [options.styleURLs=['/dist/themes/vue.css']] External CSS to inject via <link rel="stylesheet"> tag(s)
* @param {String|String[]} [options.styleURLs=['/dist/themes/core.css']] External CSS to inject via <link rel="stylesheet"> tag(s)
* @param {String} [options.testURL] URL to set as window.location.href
* @param {String} [options.waitForSelector='#main'] Element to wait for before returning promise
* @param {Boolean|Object|String} [options._logHTML] Logs HTML to console after initialization. Accepts CSS selector.

View File

@ -1,26 +1,30 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Docs Site coverpage renders and is unchanged 1`] = `
"<section class="cover show" role="complementary" aria-label="cover" style="background:
linear-gradient(
to left bottom,
hsl(127, 100%, 85%) 0%,
hsl(127, 100%, 85%) 100%
)
">
"<section class="cover show" role="complementary" aria-label="cover">
<div class="mask"></div>
<div class="cover-main"><p><img src="http://127.0.0.1:4000/_media/icon.svg" data-origin="_media/icon.svg" alt="logo"></p><h1 id="docsify-4130" tabindex="-1"><a href="#/?id=docsify-4130" data-id="docsify-4130" class="anchor"><span>docsify <small>4.13.0</small></span></a></h1><blockquote>
<p>A magical documentation site generator.</p></blockquote>
<ul><li>Simple and lightweight</li><li>No statically built html files</li><li>Multiple themes</li></ul><p><a href="https://github.com/docsifyjs/docsify/" target="_blank" rel="noopener">GitHub</a>
<a href="#/?id=docsify">Getting Started</a></p></div>
<div class="cover-main"><!-- markdownlint-disable first-line-h1 -->
<p><img src="http://127.0.0.1:4000/_media/icon.svg" data-origin="_media/icon.svg" alt="logo"></p><h1 id="docsify-4130" tabindex="-1"><a href="#/?id=docsify-4130" data-id="docsify-4130" class="anchor"><span>docsify <small>4.13.0</small></span></a></h1><blockquote>
<p>A magical documentation site generator</p></blockquote>
<ul><li>Simple and lightweight</li><li>No statically built HTML files</li><li>Multiple themes</li></ul><p><a href="#/?id=docsify" class="button primary">Get Started</a>
<a href="https://github.com/docsifyjs/docsify/" target="_blank" rel="noopener" class="button secondary">GitHub</a></p><!-- ![color](#f0f0f0) -->
<!-- ![](/_media/icon.svg) -->
</div>
</section>"
`;
exports[`Docs Site navbar renders and is unchanged 1`] = `"<nav aria-label="secondary" class="app-nav no-badge"><ul><li>Translations<ul><li><a href="#/"><img src="https://github.githubassets.com/images/icons/emoji/unicode/1f1ec-1f1e7.png?v8.png" alt="uk" class="emoji" loading="lazy"> English</a></li><li><a href="#/zh-cn/"><img src="https://github.githubassets.com/images/icons/emoji/unicode/1f1e8-1f1f3.png?v8.png" alt="cn" class="emoji" loading="lazy"> 简体中文</a></li><li><a href="#/de-de/"><img src="https://github.githubassets.com/images/icons/emoji/unicode/1f1e9-1f1ea.png?v8.png" alt="de" class="emoji" loading="lazy"> Deutsch</a></li><li><a href="#/es/"><img src="https://github.githubassets.com/images/icons/emoji/unicode/1f1ea-1f1f8.png?v8.png" alt="es" class="emoji" loading="lazy"> Español</a></li><li><a href="#/ru-ru/"><img src="https://github.githubassets.com/images/icons/emoji/unicode/1f1f7-1f1fa.png?v8.png" alt="ru" class="emoji" loading="lazy"> Русский</a></li></ul></li></ul></nav>"`;
exports[`Docs Site navbar renders and is unchanged 1`] = `
"<nav class="app-nav" aria-label="secondary"><!-- markdownlint-disable first-line-h1 -->
<ul><li><p>Translations</p><ul><li><a href="#/"><img src="https://github.githubassets.com/images/icons/emoji/unicode/1f1ec-1f1e7.png?v8.png" alt="uk" class="emoji" loading="lazy"> English</a></li><li><a href="#/zh-cn/"><img src="https://github.githubassets.com/images/icons/emoji/unicode/1f1e8-1f1f3.png?v8.png" alt="cn" class="emoji" loading="lazy"> 简体中文</a></li><li><a href="#/de-de/"><img src="https://github.githubassets.com/images/icons/emoji/unicode/1f1e9-1f1ea.png?v8.png" alt="de" class="emoji" loading="lazy"> Deutsch</a></li><li><a href="#/es/"><img src="https://github.githubassets.com/images/icons/emoji/unicode/1f1ea-1f1f8.png?v8.png" alt="es" class="emoji" loading="lazy"> Español</a></li><li><a href="#/ru-ru/"><img src="https://github.githubassets.com/images/icons/emoji/unicode/1f1f7-1f1fa.png?v8.png" alt="ru" class="emoji" loading="lazy"> Русский</a></li></ul></li></ul></nav>"
`;
exports[`Docs Site sidebar renders and is unchanged 1`] = `
"<aside id="__sidebar" class="sidebar" role="none">
"<aside id="__sidebar" class="sidebar show" tabindex="-1" role="none">
<div class="sidebar-nav" role="navigation" aria-label="primary"><ul><li><p>Getting started</p><ul><li><a href="#/quickstart">Quick start</a></li><li><a href="#/more-pages">Writing more pages</a></li><li><a href="#/custom-navbar">Custom navbar</a></li><li><a href="#/cover">Cover page</a></li></ul></li><li><p>Customization</p><ul><li><a href="#/configuration">Configuration</a></li><li><a href="#/themes">Themes</a></li><li><a href="#/plugins">List of Plugins</a></li><li><a href="#/write-a-plugin">Write a Plugin</a></li><li><a href="#/markdown">Markdown configuration</a></li><li><a href="#/language-highlight">Language highlighting</a></li><li><a href="#/emoji">Emoji</a></li></ul></li><li><p>Guide</p><ul><li><a href="#/deploy">Deploy</a></li><li><a href="#/helpers">Helpers</a></li><li><a href="#/vue">Vue compatibility</a></li><li><a href="#/cdn">CDN</a></li><li><a href="#/pwa">Offline Mode (PWA)</a></li><li><a href="#/embed-files">Embed Files</a></li></ul></li><li><p><a href="#/awesome">Awesome docsify</a></p></li><li><p><a href="#/changelog">Changelog</a></p></li></ul></div>
<div class="sidebar-nav" role="navigation" aria-label="primary"><!-- markdownlint-disable first-line-h1 -->
<ul><li><p>Getting started</p><ul><li><a href="#/quickstart" class="page-link">Quick start</a></li><li><a href="#/adding-pages" class="page-link">Adding pages</a></li><li><a href="#/cover" class="page-link">Cover page</a></li><li><a href="#/custom-navbar" class="page-link">Custom navbar</a></li></ul></li><li><p>Customization</p><ul><li><a href="#/configuration" class="page-link">Configuration</a></li><li><a href="#/themes" class="page-link">Themes</a></li><li><a href="#/plugins" class="page-link">List of Plugins</a></li><li><a href="#/write-a-plugin" class="page-link">Write a Plugin</a></li><li><a href="#/markdown" class="page-link">Markdown configuration</a></li><li><a href="#/language-highlight" class="page-link">Language highlighting</a></li><li><a href="#/emoji" class="page-link">Emoji</a></li></ul></li><li><p>Guide</p><ul><li><a href="#/deploy" class="page-link">Deploy</a></li><li><a href="#/helpers" class="page-link">Helpers</a></li><li><a href="#/vue" class="page-link">Vue compatibility</a></li><li><a href="#/cdn" class="page-link">CDN</a></li><li><a href="#/pwa" class="page-link">Offline Mode (PWA)</a></li><li><a href="#/embed-files" class="page-link">Embed Files</a></li><li><a href="#/ui-kit" class="page-link">UI Kit</a></li></ul></li><li><p><a href="#/awesome" class="page-link">Awesome docsify</a></p></li><li><p><a href="#/changelog" class="page-link">Changelog</a></p></li></ul></div>
</aside>"
`;

View File

@ -66,7 +66,7 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
background: red !important;
}
`,
styleURLs: ['/dist/themes/vue.css'],
styleURLs: ['/dist/themes/core.css'],
};
await docsifyInit({

View File

@ -16,14 +16,16 @@ describe('render', function () {
const output = window.marked('!> Important content');
expect(output).toMatchInlineSnapshot(
'"<p class="tip">Important content</p>"',
`"<p class="callout important">Important content</p>"`,
);
});
test('general tip', () => {
const output = window.marked('?> General tip');
expect(output).toMatchInlineSnapshot('"<p class="warn">General tip</p>"');
expect(output).toMatchInlineSnapshot(
`"<p class="callout tip">General tip</p>"`,
);
});
});
@ -297,7 +299,7 @@ describe('render', function () {
expect(elm.textContent).toBe(expectText);
expect(elm.outerHTML).toMatchInlineSnapshot(
'"<button id="skip-to-content">Skip to main content</button>"',
`"<button type="button" id="skip-to-content" class="primary">Skip to main content</button>"`,
);
});

View File

@ -1,7 +1,7 @@
import {
removeAtag,
getAndRemoveConfig,
getAndRemoveDocisfyIgnoreConfig,
getAndRemoveDocsifyIgnoreConfig,
} from '../../src/core/render/utils.js';
import { tree } from '../../src/core/render/tpl.js';
import { slugify } from '../../src/core/render/slugify.js';
@ -19,12 +19,12 @@ describe('core/render/utils', () => {
});
});
// getAndRemoveDocisfyIgnorConfig()
// getAndRemoveDocsifyIgnoreConfig()
// ---------------------------------------------------------------------------
describe('getAndRemoveDocisfyIgnorConfig()', () => {
test('getAndRemoveDocisfyIgnorConfig from <!-- {docsify-ignore} -->', () => {
describe('getAndRemoveDocsifyIgnoreConfig()', () => {
test('getAndRemoveDocsifyIgnoreConfig from <!-- {docsify-ignore} -->', () => {
const { content, ignoreAllSubs, ignoreSubHeading } =
getAndRemoveDocisfyIgnoreConfig(
getAndRemoveDocsifyIgnoreConfig(
'My Ignore Title<!-- {docsify-ignore} -->',
);
expect(content).toBe('My Ignore Title');
@ -32,9 +32,9 @@ describe('core/render/utils', () => {
expect(ignoreAllSubs === undefined).toBeTruthy();
});
test('getAndRemoveDocisfyIgnorConfig from <!-- {docsify-ignore-all} -->', () => {
test('getAndRemoveDocsifyIgnoreConfig from <!-- {docsify-ignore-all} -->', () => {
const { content, ignoreAllSubs, ignoreSubHeading } =
getAndRemoveDocisfyIgnoreConfig(
getAndRemoveDocsifyIgnoreConfig(
'My Ignore Title<!-- {docsify-ignore-all} -->',
);
expect(content).toBe('My Ignore Title');
@ -42,17 +42,17 @@ describe('core/render/utils', () => {
expect(ignoreSubHeading === undefined).toBeTruthy();
});
test('getAndRemoveDocisfyIgnorConfig from {docsify-ignore}', () => {
test('getAndRemoveDocsifyIgnoreConfig from {docsify-ignore}', () => {
const { content, ignoreAllSubs, ignoreSubHeading } =
getAndRemoveDocisfyIgnoreConfig('My Ignore Title{docsify-ignore}');
getAndRemoveDocsifyIgnoreConfig('My Ignore Title{docsify-ignore}');
expect(content).toBe('My Ignore Title');
expect(ignoreSubHeading).toBeTruthy();
expect(ignoreAllSubs === undefined).toBeTruthy();
});
test('getAndRemoveDocisfyIgnorConfig from {docsify-ignore-all}', () => {
test('getAndRemoveDocsifyIgnoreConfig from {docsify-ignore-all}', () => {
const { content, ignoreAllSubs, ignoreSubHeading } =
getAndRemoveDocisfyIgnoreConfig('My Ignore Title{docsify-ignore-all}');
getAndRemoveDocsifyIgnoreConfig('My Ignore Title{docsify-ignore-all}');
expect(content).toBe('My Ignore Title');
expect(ignoreAllSubs).toBeTruthy();
expect(ignoreSubHeading === undefined).toBeTruthy();