docpensieve 0.5.0-beta.2 → 0.5.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/bin/docpensieve.js +2 -0
  2. package/package.json +5 -5
  3. package/src/commands/build.js +2 -0
  4. package/src/commands/dev.js +14 -3
  5. package/src/commands/init.js +14 -0
  6. package/src/commands/serve.js +5 -3
  7. package/src/server.js +48 -14
  8. package/starter/01-guide/02-first-site.md +4 -0
  9. package/starter/01-guide/06-deployment.md +30 -0
  10. package/starter/01-guide/07-migrate-to-beta.md +18 -2
  11. package/starter/02-components/01-card.mdx +25 -0
  12. package/starter/02-components/02-columns.mdx +12 -0
  13. package/starter/02-components/03-time-timer.mdx +19 -0
  14. package/starter/02-components/04-tooltip.mdx +9 -0
  15. package/starter/02-components/05-tree.mdx +13 -0
  16. package/starter/02-components/06-scroll-to-top.mdx +10 -0
  17. package/starter/02-components/07-skill.mdx +16 -0
  18. package/starter/02-components/08-logo-icon.mdx +10 -0
  19. package/starter/02-components/09-for-theme.mdx +6 -0
  20. package/starter/02-components/10-cards.mdx +9 -0
  21. package/starter/02-components/11-menu.mdx +22 -0
  22. package/starter/02-components/12-admonition.mdx +9 -0
  23. package/starter/02-components/13-snippet.mdx +230 -0
  24. package/starter/02-components/14-hero.mdx +230 -0
  25. package/starter/02-components/15-calendar.mdx +130 -0
  26. package/starter/02-components/index.mdx +9 -3
  27. package/starter/02-components/snippet-example.js +32 -0
  28. package/starter/02-components/snippet-example.py +19 -0
  29. package/starter/03-reference/01-cli.md +13 -7
  30. package/starter/03-reference/02-configuration.md +52 -0
  31. package/starter/03-reference/05-api.md +315 -3
  32. package/starter/05-whats-new.md +57 -0
  33. package/types/commands/dev.d.ts +2 -1
  34. package/types/commands/serve.d.ts +2 -1
  35. package/types/server.d.ts +24 -1
@@ -63,6 +63,7 @@ program
63
63
  .command('dev')
64
64
  .description('Development server with reload')
65
65
  .option('-p, --port <number>', 'listening port', Number, 3000)
66
+ .option('--host <address>', 'address to listen on; 0.0.0.0 opens it to the network', '127.0.0.1')
66
67
  .action(async (options) => {
67
68
  await dev(options);
68
69
  });
@@ -71,6 +72,7 @@ program
71
72
  .command('serve')
72
73
  .description('Serves the output folder statically')
73
74
  .option('-p, --port <number>', 'listening port', Number, 4000)
75
+ .option('--host <address>', 'address to listen on; 0.0.0.0 opens it to the network', '127.0.0.1')
74
76
  .option('-d, --dir <dir>', 'folder to serve')
75
77
  .action(async (options) => {
76
78
  await serve(options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docpensieve",
3
- "version": "0.5.0-beta.2",
3
+ "version": "0.5.0-beta.4",
4
4
  "description": "DocPensieve command-line interface (init, build, check, dev, serve)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -24,10 +24,10 @@
24
24
  "types"
25
25
  ],
26
26
  "dependencies": {
27
- "@docpensieve/components": "0.5.0-beta.2",
28
- "@docpensieve/core": "0.5.0-beta.2",
29
- "@docpensieve/shared": "0.5.0-beta.2",
30
- "@docpensieve/theme": "0.5.0-beta.2",
27
+ "@docpensieve/components": "0.5.0-beta.4",
28
+ "@docpensieve/core": "0.5.0-beta.4",
29
+ "@docpensieve/shared": "0.5.0-beta.4",
30
+ "@docpensieve/theme": "0.5.0-beta.4",
31
31
  "chalk": "^6.0.0",
32
32
  "chokidar": "^5.0.0",
33
33
  "commander": "^15.0.0"
@@ -11,6 +11,7 @@ import {
11
11
  createRegistry,
12
12
  setSiteContext,
13
13
  setAdmonitionKinds,
14
+ setSnippetIcons,
14
15
  } from '@docpensieve/components';
15
16
  import { SiteGenerator, loadConfig, resolveVersion } from '@docpensieve/core';
16
17
 
@@ -28,6 +29,7 @@ export async function build(versionSlug, options = {}) {
28
29
 
29
30
  // The kinds a project declares, beside the ones shipped.
30
31
  setAdmonitionKinds(config.admonitions);
32
+ setSnippetIcons(config.snippetIcons);
31
33
 
32
34
  const generator = new SiteGenerator(config, {
33
35
  // `globalComponents: false` removes the shipped components: a project that
@@ -12,12 +12,20 @@ import {
12
12
  createRegistry,
13
13
  setSiteContext,
14
14
  setAdmonitionKinds,
15
+ setSnippetIcons,
15
16
  } from '@docpensieve/components';
16
17
  import { SiteGenerator, loadConfig } from '@docpensieve/core';
17
18
  import { CONFIG_FILENAME, DocPensieveError, THEME_FOLDER } from '@docpensieve/shared';
18
19
  import chokidar from 'chokidar';
19
20
 
20
- import { RELOAD_PATH, createStaticServer, listen } from '../server.js';
21
+ import {
22
+ DEFAULT_HOST,
23
+ RELOAD_PATH,
24
+ createStaticServer,
25
+ exposedNotice,
26
+ isExposed,
27
+ listen,
28
+ } from '../server.js';
21
29
  import { createTheme } from '../theme.js';
22
30
 
23
31
  /** Default port of `dev`, distinct from that of `serve`. */
@@ -40,7 +48,7 @@ const RELOAD_SCRIPT =
40
48
  `.addEventListener("message",()=>location.reload())</script>`;
41
49
 
42
50
  /**
43
- * @param {{ port?: number, cwd?: string }} [options]
51
+ * @param {{ port?: number, host?: string, cwd?: string }} [options]
44
52
  * @returns {Promise<{
45
53
  * server: import('node:http').Server,
46
54
  * watcher: import('chokidar').FSWatcher,
@@ -62,6 +70,7 @@ export async function dev(options = {}) {
62
70
  // Read again with the rest: a kind added to the configuration must show
63
71
  // without restarting the command.
64
72
  setAdmonitionKinds(current.admonitions);
73
+ setSnippetIcons(current.snippetIcons);
65
74
  const generator = new SiteGenerator(current, {
66
75
  // `globalComponents: false` removes the shipped components: a project
67
76
  // that defines its own thus avoids a name collision. The option was
@@ -87,8 +96,10 @@ export async function dev(options = {}) {
87
96
  },
88
97
  });
89
98
 
90
- const port = await listen(server, options.port ?? DEFAULT_PORT);
99
+ const host = options.host ?? DEFAULT_HOST;
100
+ const port = await listen(server, options.port ?? DEFAULT_PORT, 10, host);
91
101
  const url = `http://localhost:${port}${config.baseUrl}`;
102
+ if (isExposed(host)) console.log(exposedNotice(host));
92
103
  console.log(`served at ${url}`);
93
104
 
94
105
  // The project's own stylesheets, re-read by every rebuild.
@@ -368,6 +368,16 @@ function renderConfig({ name, theme, siteUrl, version, translation = '' }) {
368
368
  ' // Back-to-top button on every page.',
369
369
  ' scrollToTop: true,',
370
370
  '',
371
+ ' // A button copying each block of code. It is the one thing that makes a',
372
+ ' // content page load a script — a small file, shared by every page of the',
373
+ ' // version — so it stays off until you ask for it.',
374
+ ' copyCode: false,',
375
+ '',
376
+ ' // Icon collection a code block takes the mark of its language from, for',
377
+ " // instance 'simple-icons' once @iconify-json/simple-icons is installed.",
378
+ ' // Left empty, the short name of the language is shown instead.',
379
+ " snippetIcons: '',",
380
+ '',
371
381
  ' // The header stays at the top of the screen. false lets it scroll away',
372
382
  ' // with the page, and gives its height back to the text.',
373
383
  ' stickyHeader: true,',
@@ -389,6 +399,10 @@ function renderConfig({ name, theme, siteUrl, version, translation = '' }) {
389
399
  ' // A search field in the header, and a search page built with the site.',
390
400
  ' // Content pages load no script: the search page alone does.',
391
401
  ' search: true,',
402
+ '',
403
+ ' // llms.txt at the root of the site, and a Markdown copy beside each',
404
+ ' // page: what a program reads instead of the HTML. It needs no siteUrl.',
405
+ ' llms: false,',
392
406
  '};',
393
407
  '',
394
408
  '// Every field is described in the reference of the DocPensieve documentation:',
@@ -10,13 +10,13 @@ import path from 'node:path';
10
10
  import { loadConfig } from '@docpensieve/core';
11
11
  import { DocPensieveError } from '@docpensieve/shared';
12
12
 
13
- import { createStaticServer, listen } from '../server.js';
13
+ import { DEFAULT_HOST, createStaticServer, exposedNotice, isExposed, listen } from '../server.js';
14
14
 
15
15
  /** Default port of `serve`, distinct from that of `dev`. */
16
16
  const DEFAULT_PORT = 4000;
17
17
 
18
18
  /**
19
- * @param {{ port?: number, dir?: string, cwd?: string }} [options]
19
+ * @param {{ port?: number, host?: string, dir?: string, cwd?: string }} [options]
20
20
  * @returns {Promise<{ server: import('node:http').Server, port: number, url: string }>}
21
21
  * @throws {DocPensieveError} When the folder to serve does not exist.
22
22
  */
@@ -34,11 +34,13 @@ export async function serve(options = {}) {
34
34
  // The site is mounted under the configuration's baseUrl: otherwise the links
35
35
  // of the generated pages would not resolve locally.
36
36
  const server = createStaticServer({ root, basePath: config.baseUrl });
37
- const port = await listen(server, options.port ?? DEFAULT_PORT);
37
+ const host = options.host ?? DEFAULT_HOST;
38
+ const port = await listen(server, options.port ?? DEFAULT_PORT, 10, host);
38
39
  const url = `http://localhost:${port}${config.baseUrl}`;
39
40
 
40
41
  console.log(`${root}`);
41
42
  console.log(`served at ${url}`);
43
+ if (isExposed(host)) console.log(exposedNotice(host));
42
44
 
43
45
  return { server, port, url };
44
46
  }
package/src/server.js CHANGED
@@ -4,8 +4,7 @@
4
4
  * @module docpensieve/server
5
5
  */
6
6
 
7
- import { createReadStream } from 'node:fs';
8
- import { stat } from 'node:fs/promises';
7
+ import { readFile } from 'node:fs/promises';
9
8
  import { createServer } from 'node:http';
10
9
  import path from 'node:path';
11
10
 
@@ -134,9 +133,15 @@ export function createStaticServer({ root, basePath = '/', inject = null, onRelo
134
133
  return;
135
134
  }
136
135
 
137
- let stats;
136
+ // Read whole, then answer. `dev` empties the version folder at every
137
+ // rebuild while a browser may still be asking for it: between a `stat`
138
+ // and a stream the file could vanish, the stream then threw with nobody
139
+ // listening, and the server went down in the middle of a save. Read at
140
+ // once, a file is either there or answered 404 — and its length is the
141
+ // one actually sent, not the one a rebuild was busy changing.
142
+ let body;
138
143
  try {
139
- stats = await stat(filepath);
144
+ body = await readFile(filepath);
140
145
  } catch {
141
146
  response.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' });
142
147
  response.end(`404 — ${url.pathname}`);
@@ -147,31 +152,49 @@ export function createStaticServer({ root, basePath = '/', inject = null, onRelo
147
152
  const headers = { 'content-type': type, 'cache-control': 'no-cache' };
148
153
 
149
154
  if (inject && type.startsWith('text/html')) {
150
- const { readFile } = await import('node:fs/promises');
151
- const html = await readFile(filepath, 'utf8');
152
- const body = html.includes('</body>')
155
+ const html = body.toString('utf8');
156
+ const page = html.includes('</body>')
153
157
  ? html.replace('</body>', `${inject}</body>`)
154
158
  : html + inject;
155
- response.writeHead(200, { ...headers, 'content-length': Buffer.byteLength(body) });
156
- response.end(body);
159
+ response.writeHead(200, { ...headers, 'content-length': Buffer.byteLength(page) });
160
+ response.end(page);
157
161
  return;
158
162
  }
159
163
 
160
- response.writeHead(200, { ...headers, 'content-length': stats.size });
161
- createReadStream(filepath).pipe(response);
164
+ response.writeHead(200, { ...headers, 'content-length': body.length });
165
+ response.end(body);
162
166
  });
163
167
  }
164
168
 
169
+ /** The loopback address: the site is served to this machine and to no other. */
170
+ export const DEFAULT_HOST = '127.0.0.1';
171
+
172
+ /**
173
+ * Whether an address reaches beyond this machine.
174
+ *
175
+ * @param {string} host
176
+ * @returns {boolean}
177
+ */
178
+ export function isExposed(host) {
179
+ return !['127.0.0.1', 'localhost', '::1'].includes(host);
180
+ }
181
+
165
182
  /**
166
183
  * Starts listening, looking for a free port if needed.
167
184
  *
185
+ * On this machine only, unless asked otherwise. Called without a host, Node
186
+ * listens on every interface: anyone on the same network — a café, a
187
+ * coworking space — could read the site being written, drafts included.
188
+ *
168
189
  * @param {import('node:http').Server} server
169
190
  * @param {number} port Desired port.
170
191
  * @param {number} [attempts] Number of ports tried from `port` on.
192
+ * @param {string} [host] Address to listen on. `0.0.0.0` opens it to the
193
+ * network, which a phone on the same wifi needs.
171
194
  * @returns {Promise<number>} The port actually used.
172
195
  * @throws {DocPensieveError} When no port is free in the range.
173
196
  */
174
- export function listen(server, port, attempts = 10) {
197
+ export function listen(server, port, attempts = 10, host = DEFAULT_HOST) {
175
198
  return new Promise((resolve, reject) => {
176
199
  let current = port;
177
200
 
@@ -181,7 +204,7 @@ export function listen(server, port, attempts = 10) {
181
204
  // the next one beats forcing --port.
182
205
  if (error.code === 'EADDRINUSE' && current < port + attempts - 1) {
183
206
  current += 1;
184
- server.listen(current);
207
+ server.listen(current, host);
185
208
  return;
186
209
  }
187
210
  server.off('error', onError);
@@ -202,6 +225,17 @@ export function listen(server, port, attempts = 10) {
202
225
  const address = server.address();
203
226
  resolve(typeof address === 'object' && address !== null ? address.port : current);
204
227
  });
205
- server.listen(current);
228
+ server.listen(current, host);
206
229
  });
207
230
  }
231
+
232
+ /**
233
+ * Said when the site is opened to the network, so that it is never done
234
+ * without the person knowing.
235
+ *
236
+ * @param {string} host
237
+ * @returns {string}
238
+ */
239
+ export function exposedNotice(host) {
240
+ return `listening on ${host}: anyone on this network can read the site.`;
241
+ }
@@ -98,6 +98,10 @@ online.
98
98
  → ignores the deployment prefix "/my-project/"
99
99
  ```
100
100
 
101
+ Two commands, and the second reads what the first wrote — so they belong
102
+ together. [Deployment](./deployment/) shows how to say that once, in a
103
+ `package.json`, and have a workflow run the same thing you run.
104
+
101
105
  It is the check to run after any change of `baseUrl`, of folder structure or of
102
106
  URL. When something does not add up, [When it breaks](./when-it-breaks/) lists
103
107
  the failures this tool produces, by the symptom you see.
@@ -88,6 +88,32 @@ the tool through `npx`, which takes the latest published version. To pin it
88
88
  from one build to the next, declare `docpensieve` in a `package.json` and add
89
89
  `npm ci` before the build.
90
90
 
91
+ ## One command, for you and for the machine
92
+
93
+ Two commands are one too many to remember, and a workflow should run exactly
94
+ what you run. A `package.json` is the place to say it once:
95
+
96
+ ```json
97
+ {
98
+ "scripts": {
99
+ "docs": "docpensieve dev",
100
+ "check": "docpensieve build && docpensieve check"
101
+ }
102
+ }
103
+ ```
104
+
105
+ `npm run check` then generates the site and reads it back. A dead link, a
106
+ nesting the markup cannot carry, a page that stops the build: each one fails
107
+ the command, on your machine and in your workflow, in the same way and with
108
+ the same message.
109
+
110
+ The `&&` is the point of it. `check` reads what `build` wrote, so a check run
111
+ on its own tells you about the site as it was the last time it was generated —
112
+ which, the day you forget, is the day it matters.
113
+
114
+ With that in place the recipe above becomes `npm ci` and `npm run check`, and
115
+ the version of the tool stops depending on the day the workflow ran.
116
+
91
117
  ## Reading back before publishing
92
118
 
93
119
  A generated site can compile without error and contain dead links.
@@ -134,6 +160,10 @@ For pages that are news rather than reference — release notes, a changelog —
134
160
  give them a `date` and set `feed: true`: `feed.xml` then lists them, newest
135
161
  first. The fields are in the [configuration reference](../reference/configuration/).
136
162
 
163
+ For programs that read documentation on someone's behalf, `llms: true` writes
164
+ `llms.txt` at the root and a Markdown copy beside each page — the text
165
+ without the markup. It needs no `siteUrl`.
166
+
137
167
  ## What the reader downloads
138
168
 
139
169
  The build keeps it light on its own. The stylesheet is minified — a third to
@@ -31,8 +31,24 @@ npx docpensieve check
31
31
 
32
32
  ## What changes on its own
33
33
 
34
- Nothing yet: the 0.5 has just opened. Each change is listed here as it lands,
35
- beside what it asks of a project already built on the 0.4.
34
+ - **`dev` and `serve` listen on this machine only.** They used to listen on
35
+ every interface, so anyone on the same network could read the site being
36
+ written. To look at it from a phone on the same wifi, say so:
37
+ `npx docpensieve dev --host 0.0.0.0`.
38
+ - **Code blocks carry a background of their own.** A block in a language the
39
+ highlighter does not know used to sit on the bare page; it now looks like
40
+ every other block.
41
+ - **An error keeps its hint.** An error the tool raises while a page compiles
42
+ no longer comes out as a generic syntax complaint.
43
+
44
+ ## What is worth turning on
45
+
46
+ - [Snippet](../components/snippet/), to show a real file rather than a copy.
47
+ - [Hero](../components/hero/) and [Calendar](../components/calendar/).
48
+ - `copyCode`, a copy button on every code block — off by default, since it is
49
+ what makes a page load a script. See [the reference](../reference/configuration/).
50
+ - `snippetIcons`, if your project installs an icon collection.
51
+ - `llms`, for `llms.txt` and a Markdown copy of each page.
36
52
 
37
53
  ## What to check afterwards
38
54
 
@@ -341,3 +341,28 @@ in none: a class of yours always wins, even to undo the default style.
341
341
  </Card>
342
342
 
343
343
  </ForTheme>
344
+
345
+ The commonest row is a row of cards; [Columns](./columns/) shows how to keep
346
+ them the same height, which a grid alone does not.
347
+
348
+ ## Properties
349
+
350
+ `Card`:
351
+
352
+ | Property | Type | Effect |
353
+ | ----------- | --------- | ---------------------------------------------------------------- |
354
+ | `elevated` | `boolean` | Adds a shadow. |
355
+ | `href` | `string` | Makes the whole card a link, resolved like any link of the page. |
356
+ | `className` | `string` | Classes added to the card. |
357
+ | `style` | `object` | Inline styles on the card. |
358
+
359
+ `CardImage`:
360
+
361
+ | Property | Type | Effect |
362
+ | -------------------------- | -------- | ---------------------------------------------------------------- |
363
+ | `src` | `string` | The image, resolved like any link of the page. |
364
+ | `alt` | `string` | What the image shows. Empty by default: the image is decorative. |
365
+ | `loading` | `string` | `lazy`, the default, or `eager`. |
366
+ | `title`, `srcSet`, `sizes` | `string` | Passed to the image as they are. |
367
+
368
+ `CardHeader`, `CardBody` and `CardFooter` take `className` and `style`.
@@ -287,3 +287,15 @@ window to check.
287
287
 
288
288
  The most frequent row is a row of cards; [Card](./card/) shows how to keep
289
289
  them to the same height, which a grid alone does not do.
290
+
291
+ ## Properties
292
+
293
+ `Column`:
294
+
295
+ | Property | Type | Effect |
296
+ | ----------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
297
+ | `span` | `number` | Tracks taken out of twelve — `6` for a half, `8` for two thirds. Without it, the columns share the space equally. |
298
+ | `className` | `string` | Classes added to the column. |
299
+ | `style` | `object` | Inline styles on the column. |
300
+
301
+ `Columns` takes `className` and `style`. A `Column` outside a `Columns` stops the build.
@@ -122,3 +122,22 @@ names the faulty value and recalls the expected format.
122
122
  Announcing a release is what this is usually for, and [Versions](../guide/versions/)
123
123
  covers the rest of that cycle — the banner a prerelease carries on its own,
124
124
  and the day the two tracks swap.
125
+
126
+ ## Properties
127
+
128
+ `TimeTimer`:
129
+
130
+ | Property | Type | Effect |
131
+ | ---------- | --------- | ------------------------------------------------------------------------------------- |
132
+ | `date` | `string` | A single day, `DD/MM/YYYY`, optionally followed by `HH:mm`. The year may be left out. |
133
+ | `start` | `string` | Start of a period, in the same format, with `duration`. |
134
+ | `duration` | `string` | Length of the period from `start`: `30d`, `2h`, `45m`. |
135
+ | `strict` | `boolean` | Reads the dates in UTC rather than in local time. |
136
+ | `now` | `Date` | For tests only: without it, the moment of the build stands. |
137
+
138
+ The fallbacks each take one bound:
139
+
140
+ | Property | Type | Effect |
141
+ | --------------------------- | -------- | ----------------------------------------------------------------------- |
142
+ | `start` on `FallbackBefore` | `string` | From when the content before the period shows. Without it, from always. |
143
+ | `end` on `FallbackAfter` | `string` | Until when the content after the period shows. Without it, for good. |
@@ -138,3 +138,12 @@ unnoticed for a long time. So does an unknown side.
138
138
 
139
139
  A tooltip explains a term without leaving the sentence. A remark that needs
140
140
  its own space is an [Admonition](./admonition/) instead.
141
+
142
+ ## Properties
143
+
144
+ | Property | Type | Effect |
145
+ | ----------- | -------- | ------------------------------------------------------------------ |
146
+ | `text` | `string` | Content of the bubble. Required: an empty bubble would never show. |
147
+ | `placement` | `string` | `top`, the default, `bottom`, `left` or `right`. |
148
+ | `className` | `string` | Classes added to the term. |
149
+ | `style` | `object` | Inline styles on the term. |
@@ -195,3 +195,16 @@ label is refused too.
195
195
  A tree is how this documentation shows the shape of a project. What those
196
196
  folders become once built — the order, the addresses — is in
197
197
  [Writing pages](../guide/writing-pages/).
198
+
199
+ ## Properties
200
+
201
+ `TreeItem`:
202
+
203
+ | Property | Type | Effect |
204
+ | ----------- | --------------- | ------------------------------------------- |
205
+ | `label` | text or element | What the line shows. |
206
+ | `open` | `boolean` | Opens the branch as soon as the page opens. |
207
+ | `className` | `string` | Classes added to the line. |
208
+ | `style` | `object` | Inline styles on the line. |
209
+
210
+ `Tree` takes `className` and `style`.
@@ -144,3 +144,13 @@ build is enough to keep it right.
144
144
 
145
145
  Setting one up takes a few lines in the pipeline that already builds the
146
146
  site: [Deployment](../guide/deployment/) has the recipe.
147
+
148
+ ## Properties
149
+
150
+ | Property | Type | Effect |
151
+ | ----------- | -------- | ------------------------------------------------------------------------------------------------------------- |
152
+ | `label` | `string` | Name read by a screen reader. By default, the words of the page's language — "Back to top", "Retour en haut". |
153
+ | `className` | `string` | Classes added to the button. |
154
+ | `style` | `object` | Inline styles on the button. |
155
+
156
+ Its children, when it has any, replace the arrow.
@@ -274,3 +274,19 @@ is full from the start — the value stays readable, which is what matters.
274
274
  A level outside 0–100 stops the build, as does a gauge without a name, a level
275
275
  that is not a number, or an unknown `shape`. A silent or overflowing bar goes
276
276
  unnoticed on review.
277
+
278
+ ## Properties
279
+
280
+ | Property | Type | Effect |
281
+ | ----------- | --------------- | ------------------------------------------------------------------------------ |
282
+ | `name` | text or element | What is measured. |
283
+ | `level` | `number` | The level, as a percentage. |
284
+ | `showValue` | `boolean` | Shows the percentage. On by default; off, the gauge still announces its value. |
285
+ | `shape` | `string` | `bar`, the default, or `circle`. |
286
+ | `icon` | element | Shown before the name — a `LogoIcon` fits there. |
287
+ | `color` | `string` | Colour of the fill: any CSS colour, the accent by default. |
288
+ | `label` | `string` | Name for screen readers, when `name` is not text. |
289
+ | `className` | `string` | Classes added to the gauge. |
290
+ | `style` | `object` | Inline styles on the gauge. |
291
+
292
+ Its children stand as a comment under the gauge.
@@ -171,3 +171,13 @@ A component loads no JavaScript, and this one is not going to introduce any.
171
171
 
172
172
  A kind of admonition takes its mark through this same component — see
173
173
  [Admonition](./admonition/).
174
+
175
+ ## Properties
176
+
177
+ | Property | Type | Effect |
178
+ | ----------- | -------- | --------------------------------------------------------------------------------------- |
179
+ | `src` | `string` | An SVG of the project, or `prefix:name` from an installed icon collection. |
180
+ | `label` | `string` | What the icon means. Without it, the icon is decorative and hidden from screen readers. |
181
+ | `size` | `string` | Any CSS length. |
182
+ | `className` | `string` | Classes added to the icon. |
183
+ | `style` | `object` | Inline styles on the icon. |
@@ -46,3 +46,9 @@ without a word.
46
46
 
47
47
  Which theme a project runs is one field of its configuration, and the whole
48
48
  of what it changes is in [Themes](../guide/themes/).
49
+
50
+ ## Properties
51
+
52
+ | Property | Type | Effect |
53
+ | ----------- | -------- | ---------------------------------------------------------------- |
54
+ | `framework` | `string` | `tailwind` or `custom`: the content shows only under that theme. |
@@ -74,3 +74,12 @@ themes, so this needs no variant:
74
74
  Like every component here, the grid is built at the build. What reaches the
75
75
  reader is a list of links: it works with JavaScript turned off, and a card
76
76
  never waits for anything to become clickable.
77
+
78
+ ## Properties
79
+
80
+ | Property | Type | Effect |
81
+ | ----------- | -------- | ----------------------------------------------------------------------------------------------------- |
82
+ | `of` | `string` | `all`, the default; `series` for the sub-folders alone; `pages` for the pages beside the index alone. |
83
+ | `from` | `string` | Slug of another folder to list, instead of the one holding the page. |
84
+ | `className` | `string` | Classes added to the grid. |
85
+ | `style` | `object` | Inline styles on the grid. |
@@ -127,3 +127,25 @@ A `MenuLink` or a `MenuGroup` written outside a `Menu` stops the build, and so
127
127
  does an entry without a target or a group without a title. Each of these would
128
128
  otherwise render something that looks like a menu and leads nowhere — the kind
129
129
  of fault a review does not catch.
130
+
131
+ ## Properties
132
+
133
+ `Menu`:
134
+
135
+ | Property | Type | Effect |
136
+ | -------- | -------- | ----------------------------------------------------------------------------------------------------------- |
137
+ | `label` | `string` | Name for screen readers, and label of the button it folds into. By default, the page's own word for a menu. |
138
+
139
+ `MenuLink`:
140
+
141
+ | Property | Type | Effect |
142
+ | -------- | -------- | ------------------------------------------------- |
143
+ | `href` | `string` | Where it leads, resolved like any link. Required. |
144
+
145
+ `MenuGroup`:
146
+
147
+ | Property | Type | Effect |
148
+ | -------- | -------- | ------------------------------- |
149
+ | `title` | `string` | Heading of the group. Required. |
150
+
151
+ All three take `className` and `style`.
@@ -152,3 +152,12 @@ Its orphan branch keeps what was built, and the history keeps the sources.
152
152
  Like every component here, it is rendered at the build. It is an `aside`
153
153
  announced as a note, with its icon hidden from screen readers — the title
154
154
  beside it already says the kind, and saying it twice helps nobody.
155
+
156
+ ## Properties
157
+
158
+ | Property | Type | Effect |
159
+ | ----------- | -------- | ---------------------------------------------------------------------------------------------------------- |
160
+ | `type` | `string` | The kind: `note`, the default, `info`, `tip`, `attention`, `alert`, `danger`, or one the project declares. |
161
+ | `title` | `string` | Replaces the label of the kind, for this block alone. |
162
+ | `className` | `string` | Classes added to the block. |
163
+ | `style` | `object` | Inline styles on the block. |