**Docup** is a single JavaScript file that fetches Markdown file and renders it as a beautiful one-page documentation.
Docup is built with Preact, the entire bundle (with CSS) is just 30kB minified and gzipped.
## Quick Start
Create an HTML file: `index.html` which will be be homepage of your documentation website:
```html
My Awesome Doc
```
Then populate a `README.md` file to the same directory where `index.html` is located.
```md
## Introduction
How about this.
## Advanced
How about that.
```
Finally serve this directory as a static website:
- **node.js**: `npm i -g static-server && static-server .`
- **deno**: `deno install --allow-net --allow-read https://deno.land/std/http/file_server.ts && file_server .`
- **python**: `python -m SimpleHTTPServer`
- ...etc, you can use any static file server, for real.
### How Files Are Resolved
If current `location.pathname` is `/`, i.e. the homepage, it fetches `/README.md`.
If current `location.pathname` is `/docs/`, it fetches `/docs/README.md`.
If current `location.pathname` is `/docs/en`, it fetches `/docs/en.md`.
Basically if the pathname ends with a slash, we treat it as a directory and try to load the `README.md` file under that path, you can also use [indexFile](#indexfile) option to change `README.md` to other file if you want. If the pathname does not end with slash, we would fetch `pathname + '.md'`.
You can also use [root](#root) option to set the origin of the files, for example if you want to load files from other domain, you can set `root: 'https://sub.domain.com/data'`.
## Guide
### Site Title
We use the value of `document.title` if it's not `undefined`, you can also set a title via options:
```js
docup.init({
title: 'My Website',
})
```
### Markdown Features
We use the blazing fast [marked](https://marked.js.org) to parse Markdown, all [GitHub Flavored Markdown](https://github.github.com/gfm/) features are supported.
### Message Blocks
To highlight some messages in your documentation, use the following format to write a `blockquote`:
```md
> [TYPE]: This is a very dangerous action!
```
Where `[TYPE]` can be:
- `Alert`
- `Warning`
- `Info`
- `Success`
- `Note`
And they look like:
> **Alert**: This is an alert!
> **Warning**: This is a warning!
> **Info**: This is a info!
> **Success**: This is a success!
> **Note**: This is just a note!
### Embedding
Embedding and running code snippets is easy if your provider supports iframe, like [codesandbox.io](https://codesandbox.io):
```html
```
### Highlight
Docup uses [Prism.js](http://prismjs.com/) to highlight code blocks, by default only a few languages are supported, namely: `html` `css` `js` `markdown` `bash` `json`, you can manually load Prism language components to support more languages, e.g. for Go programming language:
```js
docup.init({
highlightLanguages: ['go'],
})
```
Available languages:
```js preact
const { useState } = hooks
export default ({ langs }) => {
const [showAll, setShowAll] = useState(false)
return html`
${(showAll ? langs : langs.slice(0, 5)).map(
(lang) => html`
${lang}
`
)}
`
}
```
### Inline Component
You can inline Preact components inside Markdown file like this:
````markdown
```js preact
const { useState } = hooks
export default () => {
const [count, setCount] = useState(0)
return html``
}
```
````
Write `preact` next to the language name and we will render the code as a Preact component in place:
```js preact
const { useState } = hooks
export default () => {
const [count, setCount] = useState(0)
return html``
}
```
> Warning: Note that you can't use JSX here, because it's not supported by browsers natively. But you can use the `html` function which is powered by [developit/htm](https://github.com/developit/htm).
### CSS Variables
```js preact
const { useEffect, useState } = hooks
// could pass in an array of specific stylesheets for optimization
function getAllCSSVariableNames(styleSheets = document.styleSheets) {
var cssVars = []
// loop each stylesheet
for (var i = 0; i < styleSheets.length; i++) {
// loop stylesheet's cssRules
try {
// try/catch used because 'hasOwnProperty' doesn't work
for (var j = 0; j < styleSheets[i].cssRules.length; j++) {
try {
// loop stylesheet's cssRules' style (property names)
for (var k = 0; k < styleSheets[i].cssRules[j].style.length; k++) {
let name = styleSheets[i].cssRules[j].style[k]
// test name for css variable signature and uniqueness
if (name.startsWith('--') && cssVars.indexOf(name) == -1) {
cssVars.push(name)
}
}
} catch (error) {}
}
} catch (error) {}
}
return cssVars
}
function getElementCSSVariables(allCSSVars, element = document.body, pseudo) {
var elStyles = window.getComputedStyle(element, pseudo)
var cssVars = {}
for (var i = 0; i < allCSSVars.length; i++) {
let key = allCSSVars[i]
let value = elStyles.getPropertyValue(key)
if (value) {
cssVars[key] = value.trim()
}
}
return cssVars
}
export default () => {
const vars = getElementCSSVariables(
getAllCSSVariableNames(),
document.documentElement
)
return html`
${Object.keys(vars).map((name) => {
const value = vars[name]
return html`