2 |
3 | **Docup** is a single JavaScript file that fetches Markdown file and renders it as a beautiful one-page documentation.
4 |
5 | Docup is built with Preact, the entire bundle (with CSS) is just 30kB minified and gzipped.
6 |
7 |
8 |
9 | ## Quick Start
10 |
11 | Create an HTML file: `index.html` which will be be homepage of your documentation website:
12 |
13 | ```html
14 |
15 |
16 |
17 |
18 |
19 |
23 | My Awesome Doc
24 |
25 |
29 |
30 |
31 |
32 |
39 |
40 |
41 | ```
42 |
43 | Then populate a `README.md` file to the same directory where `index.html` is located.
44 |
45 | ```md
46 | ## Introduction
47 |
48 | How about this.
49 |
50 | ## Advanced
51 |
52 | How about that.
53 | ```
54 |
55 | Finally serve this directory as a static website:
56 |
57 | - **node.js**: `npm i -g static-server && static-server .`
58 | - **deno**: `deno install --allow-net --allow-read https://deno.land/std/http/file_server.ts && file_server .`
59 | - **python**: `python -m SimpleHTTPServer`
60 | - ...etc, you can use any static file server, for real.
61 |
62 | ### How Files Are Resolved
63 |
64 | If current `location.pathname` is `/`, i.e. the homepage, it fetches `/README.md`.
65 |
66 | If current `location.pathname` is `/docs/`, it fetches `/docs/README.md`.
67 |
68 | If current `location.pathname` is `/docs/en`, it fetches `/docs/en.md`.
69 |
70 | 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'`.
71 |
72 | 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'`.
73 |
74 | ## Guide
75 |
76 | ### Site Title
77 |
78 | We use the value of `document.title` if it's not `undefined`, you can also set a title via options:
79 |
80 | ```js
81 | docup.init({
82 | title: 'My Website',
83 | })
84 | ```
85 |
86 | ### Markdown Features
87 |
88 | 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.
89 |
90 | ### Message Blocks
91 |
92 | To highlight some messages in your documentation, use the following format to write a `blockquote`:
93 |
94 | ```md
95 | > [TYPE]: This is a very dangerous action!
96 | ```
97 |
98 | Where `[TYPE]` can be:
99 |
100 | - `Alert`
101 | - `Warning`
102 | - `Info`
103 | - `Success`
104 | - `Note`
105 |
106 | And they look like:
107 |
108 | > **Alert**: This is an alert!
109 |
110 | > **Warning**: This is a warning!
111 |
112 | > **Info**: This is a info!
113 |
114 | > **Success**: This is a success!
115 |
116 | > **Note**: This is just a note!
117 |
118 | ### Embedding
119 |
120 | Embedding and running code snippets is easy if your provider supports iframe, like [codesandbox.io](https://codesandbox.io):
121 |
122 | ```html
123 |
129 | ```
130 |
131 | ### Highlight
132 |
133 | 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:
134 |
135 | ```js
136 | docup.init({
137 | highlightLanguages: ['go'],
138 | })
139 | ```
140 |
141 | Available languages:
142 |
143 | ```js preact
144 | import { useState, html } from 'docup'
145 | export default ({ langs }) => {
146 | return html``
149 | }
150 | ```
151 |
152 | ### Inline Component
153 |
154 | You can inline Preact, React and Vue 3 components inside Markdown file like this:
155 |
156 | ````markdown
157 | ```js preact
158 | import { useState, html } from 'docup'
159 |
160 | export default () => {
161 | const [count, setCount] = useState(0)
162 | return html``
168 | }
169 | ```
170 | ````
171 |
172 | Write `preact` next to the language name and we will render the code as a Preact component in place:
173 |
174 | ```js preact
175 | import { useState, html } from 'docup'
176 |
177 | export default () => {
178 | const [count, setCount] = useState(0)
179 | return html``
185 | }
186 | ```
187 |
188 | > 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). By default `docup` re-exports all functions from `preact` plus a preact-powered `html` function.
189 |
190 | See another example with React:
191 |
192 | ```js react,keep
193 | import React from 'react'
194 | import Trend from 'react-trend'
195 | import htm from 'htm'
196 |
197 | const html = htm.bind(React.createElement)
198 |
199 | export default () => html`<${Trend}
200 | smooth
201 | autoDraw
202 | autoDrawDuration="{3000}"
203 | autoDrawEasing="ease-out"
204 | data=${[0, 2, 5, 9, 5, 10, 3, 5, 0, 0, 1, 8, 2, 9, 0]}
205 | gradient=${['#00c6ff', '#F0F', '#FF0']}
206 | radius=${10}
207 | strokeWidth=${2}
208 | strokeLinecap=${'butt'}
209 | />`
210 | ```
211 |
212 | When the code block is recognized as a component, the code itself will be removed from the markdown, if you want to show the code block below the rendered component, you can use the `keep` keyword:
213 |
214 | ````markdown
215 | ```js react,keep
216 | export default () => {}
217 | ```
218 | ````
219 |
220 | If you want to show the code block above the component, use `keepAbove` instead.
221 |
222 | ### CSS Variables
223 |
224 | ```js preact
225 | import { html } from 'docup'
226 |
227 | function getAllCSSVariableNames(styleSheets = document.styleSheets) {
228 | var cssVars = []
229 | // loop each stylesheet
230 | for (var i = 0; i < styleSheets.length; i++) {
231 | // loop stylesheet's cssRules
232 | try {
233 | // try/catch used because 'hasOwnProperty' doesn't work
234 | for (var j = 0; j < styleSheets[i].cssRules.length; j++) {
235 | try {
236 | // loop stylesheet's cssRules' style (property names)
237 | for (var k = 0; k < styleSheets[i].cssRules[j].style.length; k++) {
238 | let name = styleSheets[i].cssRules[j].style[k]
239 | // test name for css variable signature and uniqueness
240 | if (
241 | name.startsWith('--') &&
242 | cssVars.indexOf(name) == -1 &&
243 | !name.startsWith('--tw-')
244 | ) {
245 | cssVars.push(name)
246 | }
247 | }
248 | } catch (error) {}
249 | }
250 | } catch (error) {}
251 | }
252 | return cssVars
253 | }
254 |
255 | function getElementCSSVariables(allCSSVars, element = document.body, pseudo) {
256 | var elStyles = window.getComputedStyle(element, pseudo)
257 | var cssVars = {}
258 | for (var i = 0; i < allCSSVars.length; i++) {
259 | let key = allCSSVars[i]
260 | let value = elStyles.getPropertyValue(key)
261 | if (value) {
262 | cssVars[key] = value.trim()
263 | }
264 | }
265 | return cssVars
266 | }
267 |
268 | export default () => {
269 | const vars = getElementCSSVariables(
270 | getAllCSSVariableNames(),
271 | document.documentElement
272 | )
273 |
274 | return html`
275 |