docpensieve 0.1.3 → 0.1.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.
- package/README.md +22 -3
- package/package.json +5 -5
- package/src/commands/dev.js +27 -3
- package/src/commands/init.js +60 -4
- package/src/templates/custom.css +20 -0
- package/src/theme.js +30 -7
- package/starter/01-guide/01-installation.md +16 -5
- package/starter/01-guide/03-writing-pages.md +2 -2
- package/starter/01-guide/05-themes.md +5 -3
- package/starter/02-components/01-card.mdx +155 -7
- package/starter/02-components/02-columns.mdx +98 -5
- package/starter/02-components/03-time-timer.mdx +1 -1
- package/starter/02-components/05-tree.mdx +32 -2
- package/starter/02-components/06-scroll-to-top.mdx +40 -0
- package/starter/02-components/07-skill.mdx +44 -12
- package/starter/02-components/08-logo-icon.mdx +44 -16
- package/starter/02-components/09-for-theme.mdx +45 -0
- package/starter/02-components/{index.md → index.mdx} +44 -1
- package/starter/03-reference/01-cli.md +5 -0
- package/starter/03-reference/02-configuration.md +5 -0
- package/starter/03-reference/04-theme.md +4 -3
- package/starter/examples.css +156 -0
- package/types/theme.d.ts +12 -2
package/README.md
CHANGED
|
@@ -6,16 +6,35 @@ Part of [DocPensieve](https://github.com/Juniors017/docpensieve), a static docum
|
|
|
6
6
|
Markdown and MDX in, static HTML out, one version per orphan branch, JSON-LD
|
|
7
7
|
structured data from the frontmatter.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Getting started
|
|
10
|
+
|
|
11
|
+
Nothing to install first: `npx` fetches the package.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx docpensieve init my-site # asks a few questions, then sets up the project
|
|
15
|
+
cd my-site
|
|
16
|
+
npx docpensieve dev # builds, serves, watches and reloads
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Installing it in a project
|
|
20
|
+
|
|
21
|
+
To pin the version a project uses — what continuous integration needs:
|
|
10
22
|
|
|
11
23
|
```bash
|
|
24
|
+
npm init -y # only when the folder has no package.json yet
|
|
12
25
|
npm install docpensieve
|
|
26
|
+
npx docpensieve init .
|
|
13
27
|
```
|
|
14
28
|
|
|
15
|
-
|
|
29
|
+
`npm install` only installs: it creates no site and asks nothing — `init`
|
|
30
|
+
does. And npm installs in the nearest folder that has a `package.json`, going
|
|
31
|
+
up from the current one: in a folder without one, the package lands in a parent
|
|
32
|
+
folder and nothing appears where you are. Hence `npm init -y` first.
|
|
33
|
+
|
|
34
|
+
## Commands
|
|
16
35
|
|
|
17
36
|
```bash
|
|
18
|
-
npx docpensieve init
|
|
37
|
+
npx docpensieve init [dir] # sets up a project and picks the theme
|
|
19
38
|
npx docpensieve dev # builds, serves, watches and reloads
|
|
20
39
|
npx docpensieve build [ver] # builds every version, or a single one
|
|
21
40
|
npx docpensieve check # reads the produced site back: links, markup
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docpensieve",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.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.1.
|
|
28
|
-
"@docpensieve/core": "0.1.
|
|
29
|
-
"@docpensieve/shared": "0.1.
|
|
30
|
-
"@docpensieve/theme": "0.1.
|
|
27
|
+
"@docpensieve/components": "0.1.4",
|
|
28
|
+
"@docpensieve/core": "0.1.4",
|
|
29
|
+
"@docpensieve/shared": "0.1.4",
|
|
30
|
+
"@docpensieve/theme": "0.1.4",
|
|
31
31
|
"chalk": "^6.0.0",
|
|
32
32
|
"chokidar": "^5.0.0",
|
|
33
33
|
"commander": "^15.0.0"
|
package/src/commands/dev.js
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
* @module docpensieve/commands/dev
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
7
8
|
import path from 'node:path';
|
|
8
9
|
|
|
9
10
|
import { componentsCss, createRegistry, setSiteContext } from '@docpensieve/components';
|
|
10
11
|
import { SiteGenerator, loadConfig } from '@docpensieve/core';
|
|
11
|
-
import { CONFIG_FILENAME, DocPensieveError } from '@docpensieve/shared';
|
|
12
|
+
import { CONFIG_FILENAME, DocPensieveError, THEME_FOLDER } from '@docpensieve/shared';
|
|
12
13
|
import chokidar from 'chokidar';
|
|
13
14
|
|
|
14
15
|
import { RELOAD_PATH, createStaticServer, listen } from '../server.js';
|
|
@@ -20,6 +21,9 @@ const DEFAULT_PORT = 3000;
|
|
|
20
21
|
/** Delay for grouping file events, in milliseconds. */
|
|
21
22
|
const DEBOUNCE = 120;
|
|
22
23
|
|
|
24
|
+
/** How often a missing theme folder is looked for, in ms. */
|
|
25
|
+
const THEME_POLL = 1000;
|
|
26
|
+
|
|
23
27
|
/**
|
|
24
28
|
* Reload script injected on the fly, never written to disk.
|
|
25
29
|
*
|
|
@@ -79,9 +83,12 @@ export async function dev(options = {}) {
|
|
|
79
83
|
const url = `http://localhost:${port}${config.baseUrl}`;
|
|
80
84
|
console.log(`served at ${url}`);
|
|
81
85
|
|
|
86
|
+
// The project's own stylesheets, re-read by every rebuild.
|
|
87
|
+
const themeFolder = path.resolve(cwd, THEME_FOLDER);
|
|
82
88
|
const watched = [
|
|
83
89
|
config.configFile ?? path.resolve(cwd, CONFIG_FILENAME),
|
|
84
90
|
...config.versions.map((version) => path.resolve(cwd, version.folder)),
|
|
91
|
+
...(existsSync(themeFolder) ? [themeFolder] : []),
|
|
85
92
|
];
|
|
86
93
|
const watcher = chokidar.watch(watched, { ignoreInitial: true });
|
|
87
94
|
|
|
@@ -92,7 +99,8 @@ export async function dev(options = {}) {
|
|
|
92
99
|
|
|
93
100
|
/** @type {NodeJS.Timeout | undefined} */
|
|
94
101
|
let pending;
|
|
95
|
-
|
|
102
|
+
/** @param {string} changed */
|
|
103
|
+
const schedule = (changed) => {
|
|
96
104
|
// An editor emits several events per save: group them.
|
|
97
105
|
clearTimeout(pending);
|
|
98
106
|
pending = setTimeout(async () => {
|
|
@@ -111,7 +119,22 @@ export async function dev(options = {}) {
|
|
|
111
119
|
}
|
|
112
120
|
}
|
|
113
121
|
}, DEBOUNCE);
|
|
114
|
-
}
|
|
122
|
+
};
|
|
123
|
+
watcher.on('all', (_event, changed) => schedule(changed));
|
|
124
|
+
|
|
125
|
+
// Handed a path that does not exist, chokidar loses the events of all the
|
|
126
|
+
// others. A missing theme folder is therefore looked for every second, and
|
|
127
|
+
// watched from the moment it appears — its creation itself triggers no
|
|
128
|
+
// event, hence the rebuild asked for here.
|
|
129
|
+
const lookForTheme = existsSync(themeFolder)
|
|
130
|
+
? undefined
|
|
131
|
+
: setInterval(() => {
|
|
132
|
+
if (!existsSync(themeFolder)) return;
|
|
133
|
+
clearInterval(lookForTheme);
|
|
134
|
+
watcher.add(themeFolder);
|
|
135
|
+
schedule(themeFolder);
|
|
136
|
+
}, THEME_POLL);
|
|
137
|
+
lookForTheme?.unref();
|
|
115
138
|
|
|
116
139
|
console.log('watching — Ctrl+C to stop');
|
|
117
140
|
|
|
@@ -122,6 +145,7 @@ export async function dev(options = {}) {
|
|
|
122
145
|
url,
|
|
123
146
|
close: async () => {
|
|
124
147
|
clearTimeout(pending);
|
|
148
|
+
clearInterval(lookForTheme);
|
|
125
149
|
await watcher.close();
|
|
126
150
|
// Without this, close() waits for keep-alive connections to expire —
|
|
127
151
|
// the reload stream keeps one open permanently.
|
package/src/commands/init.js
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
CONFIG_FILENAME,
|
|
16
16
|
CONFIG_FILENAMES,
|
|
17
17
|
DocPensieveError,
|
|
18
|
+
THEME_FOLDER,
|
|
18
19
|
THEME_FRAMEWORKS,
|
|
19
20
|
} from '@docpensieve/shared';
|
|
20
21
|
|
|
@@ -24,7 +25,7 @@ import {
|
|
|
24
25
|
*/
|
|
25
26
|
const FRAMEWORK_LABELS = {
|
|
26
27
|
tailwind: 'Tailwind CSS — ships with the tool, nothing to install',
|
|
27
|
-
custom: 'custom theme, light stylesheet,
|
|
28
|
+
custom: 'custom theme, light stylesheet, your own classes in theme/',
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
/** Answers used when there is no dialogue. */
|
|
@@ -39,10 +40,20 @@ const DEFAULTS = { name: 'My documentation', siteUrl: '', theme: 'tailwind', ver
|
|
|
39
40
|
const DOCS_FOLDER = '99-docpensieve';
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
43
|
+
* Stylesheet of the documentation's examples, shipped next to its pages.
|
|
44
|
+
* Under the custom theme, it goes to the theme folder, not among the pages.
|
|
44
45
|
*/
|
|
45
|
-
const
|
|
46
|
+
const EXAMPLES_CSS = 'examples.css';
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Entries of DocPensieve's documentation that are not installed with the
|
|
50
|
+
* pages: its home page and the icons only that page uses belong to
|
|
51
|
+
* DocPensieve's own site, and the examples' stylesheet has a place of its own.
|
|
52
|
+
*/
|
|
53
|
+
const NOT_INSTALLED = new Set(['index.md', 'index.mdx', 'icons', EXAMPLES_CSS]);
|
|
54
|
+
|
|
55
|
+
/** Starting point of the project's own stylesheet, under the custom theme. */
|
|
56
|
+
const CUSTOM_CSS = fileURLToPath(new URL('../templates/custom.css', import.meta.url));
|
|
46
57
|
|
|
47
58
|
/** Where the generated configuration sends readers for every field. */
|
|
48
59
|
const DOCUMENTATION_URL = 'https://juniors017.github.io/docpensieve/';
|
|
@@ -225,6 +236,9 @@ function renderConfig({ name, theme, siteUrl, version }) {
|
|
|
225
236
|
' // CSS appended to the stylesheet, outside any layer: it wins over the',
|
|
226
237
|
' // default rules.',
|
|
227
238
|
" // css: '.dp-article h2 { letter-spacing: -0.01em; }',",
|
|
239
|
+
'',
|
|
240
|
+
' // Longer rules go in the theme/ folder: every .css file in it is',
|
|
241
|
+
' // appended after the theme, and "docpensieve dev" picks up changes.',
|
|
228
242
|
...(theme === 'tailwind'
|
|
229
243
|
? [
|
|
230
244
|
'',
|
|
@@ -433,6 +447,17 @@ export async function init(dir = '.', options = {}) {
|
|
|
433
447
|
hint: 'Reinstall docpensieve, or run init with --minimal to go without it.',
|
|
434
448
|
});
|
|
435
449
|
}
|
|
450
|
+
// Under the custom theme, its examples also need their stylesheet: without
|
|
451
|
+
// it, every one of them would render unstyled, and nothing would say why.
|
|
452
|
+
if (
|
|
453
|
+
documentation &&
|
|
454
|
+
answers.theme === 'custom' &&
|
|
455
|
+
!existsSync(path.join(documentation, EXAMPLES_CSS))
|
|
456
|
+
) {
|
|
457
|
+
throw new DocPensieveError("The stylesheet of the documentation's examples is missing.", {
|
|
458
|
+
hint: 'Reinstall docpensieve, or run init with --minimal to go without the documentation.',
|
|
459
|
+
});
|
|
460
|
+
}
|
|
436
461
|
|
|
437
462
|
const slug = versionSlug(answers.version);
|
|
438
463
|
const docsDir = path.join(target, 'docs', slug);
|
|
@@ -448,6 +473,8 @@ export async function init(dir = '.', options = {}) {
|
|
|
448
473
|
if (documentation) {
|
|
449
474
|
await installDocumentation(documentation, path.join(docsDir, DOCS_FOLDER), slug);
|
|
450
475
|
}
|
|
476
|
+
const stylesheets =
|
|
477
|
+
answers.theme === 'custom' ? await writeStylesheets(target, documentation) : [];
|
|
451
478
|
await ignoreOutput(target);
|
|
452
479
|
|
|
453
480
|
console.log(`\nProject initialised in ${target}`);
|
|
@@ -457,12 +484,41 @@ export async function init(dir = '.', options = {}) {
|
|
|
457
484
|
if (documentation) {
|
|
458
485
|
console.log(` docs/${slug}/${DOCS_FOLDER}/ DocPensieve's documentation, to delete when done`);
|
|
459
486
|
}
|
|
487
|
+
for (const line of stylesheets) console.log(` ${line}`);
|
|
460
488
|
console.log(`\nTheme: ${answers.theme} — ${FRAMEWORK_LABELS[answers.theme]}`);
|
|
461
489
|
console.log('\nNext: npx docpensieve dev');
|
|
462
490
|
|
|
463
491
|
return { dir: target, theme: answers.theme, docs: answers.docs };
|
|
464
492
|
}
|
|
465
493
|
|
|
494
|
+
/**
|
|
495
|
+
* Gives the custom theme its stylesheets, in the project's theme folder.
|
|
496
|
+
*
|
|
497
|
+
* The custom theme loads no utility framework: the classes a page uses are
|
|
498
|
+
* defined by the project. `custom.css` is where they go — never overwritten,
|
|
499
|
+
* even with `--force`, since it holds the project's own rules. The installed
|
|
500
|
+
* documentation brings the classes of its examples in a file of its own, to
|
|
501
|
+
* delete along with it.
|
|
502
|
+
*
|
|
503
|
+
* @param {string} target Project folder.
|
|
504
|
+
* @param {string | null} documentation Source of the installed documentation.
|
|
505
|
+
* @returns {Promise<string[]>} One line per file, for the summary.
|
|
506
|
+
*/
|
|
507
|
+
async function writeStylesheets(target, documentation) {
|
|
508
|
+
const folder = path.join(target, THEME_FOLDER);
|
|
509
|
+
await mkdir(folder, { recursive: true });
|
|
510
|
+
|
|
511
|
+
const own = path.join(folder, 'custom.css');
|
|
512
|
+
if (!existsSync(own)) cpSync(CUSTOM_CSS, own);
|
|
513
|
+
const lines = [`${THEME_FOLDER}/custom.css your own styles`];
|
|
514
|
+
|
|
515
|
+
if (documentation) {
|
|
516
|
+
cpSync(path.join(documentation, EXAMPLES_CSS), path.join(folder, `${DOCS_FOLDER}.css`));
|
|
517
|
+
lines.push(`${THEME_FOLDER}/${DOCS_FOLDER}.css classes of its examples, to delete with it`);
|
|
518
|
+
}
|
|
519
|
+
return lines;
|
|
520
|
+
}
|
|
521
|
+
|
|
466
522
|
/**
|
|
467
523
|
* Adds the output folder to .gitignore, without overwriting what is there.
|
|
468
524
|
*
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Your own styles.
|
|
3
|
+
*
|
|
4
|
+
* Every .css file of this theme/ folder is appended to the site's stylesheet,
|
|
5
|
+
* after the theme's, in name order, and "docpensieve dev" picks up every
|
|
6
|
+
* change. The rules sit outside any layer: they win over the default look of
|
|
7
|
+
* the components.
|
|
8
|
+
*
|
|
9
|
+
* The custom theme loads no utility framework: a className written in a page
|
|
10
|
+
* names a class defined here. Write the colours on the --dp-* tokens
|
|
11
|
+
* (--dp-accent, --dp-accent-soft, --dp-bg, --dp-text, --dp-text-soft,
|
|
12
|
+
* --dp-border…) so that they follow the light and dark palettes.
|
|
13
|
+
*
|
|
14
|
+
* For instance, for <Card className="accent-card">:
|
|
15
|
+
*
|
|
16
|
+
* .accent-card {
|
|
17
|
+
* border-color: var(--dp-accent);
|
|
18
|
+
* background: var(--dp-accent-soft);
|
|
19
|
+
* }
|
|
20
|
+
*/
|
package/src/theme.js
CHANGED
|
@@ -4,18 +4,39 @@
|
|
|
4
4
|
* @module docpensieve/theme
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
7
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
|
|
10
|
+
import { setThemeClasses, setThemeFramework } from '@docpensieve/components';
|
|
11
|
+
import { ConfigError, THEME_FOLDER, THEME_FRAMEWORKS } from '@docpensieve/shared';
|
|
9
12
|
import { CustomProvider, TailwindProvider, ThemeEngine } from '@docpensieve/theme';
|
|
10
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Reads the project's own stylesheets: every `.css` file of its `theme/`
|
|
16
|
+
* folder, in name order. None when the folder does not exist.
|
|
17
|
+
*
|
|
18
|
+
* @param {string} [rootDir] Project root.
|
|
19
|
+
* @returns {string[]} The contents of each file.
|
|
20
|
+
*/
|
|
21
|
+
export function projectCss(rootDir) {
|
|
22
|
+
const folder = rootDir ? path.join(rootDir, THEME_FOLDER) : '';
|
|
23
|
+
if (!folder || !existsSync(folder)) return [];
|
|
24
|
+
return readdirSync(folder)
|
|
25
|
+
.filter((name) => name.toLowerCase().endsWith('.css'))
|
|
26
|
+
.sort()
|
|
27
|
+
.map((name) => readFileSync(path.join(folder, name), 'utf8').trim());
|
|
28
|
+
}
|
|
29
|
+
|
|
11
30
|
/**
|
|
12
31
|
* Mounts the ThemeEngine matching the declared framework.
|
|
13
32
|
*
|
|
14
33
|
* The CLI does this wiring: `core` deliberately ignores the `theme` package
|
|
15
34
|
* and receives the engine by injection (ADR-002).
|
|
16
35
|
*
|
|
17
|
-
* Along the way, the class table
|
|
18
|
-
* lets them ask for their class instead of
|
|
36
|
+
* Along the way, the class table and the framework are handed to the
|
|
37
|
+
* components: that is what lets them ask for their class instead of
|
|
38
|
+
* hard-coding it (ADR-007), and lets `ForTheme` keep the variant of the active
|
|
39
|
+
* theme.
|
|
19
40
|
*
|
|
20
41
|
* @param {Record<string, any>} config Normalised configuration.
|
|
21
42
|
* @param {string} [extraCss] CSS appended after the theme's — the look of the
|
|
@@ -27,16 +48,18 @@ export function createTheme(config, extraCss = '') {
|
|
|
27
48
|
const theme = config.theme ?? {};
|
|
28
49
|
const framework = theme.framework ?? 'tailwind';
|
|
29
50
|
|
|
30
|
-
// Components first, project CSS
|
|
31
|
-
// them.
|
|
51
|
+
// Components first, project CSS after: the latter must be able to correct
|
|
52
|
+
// them. The theme folder comes last — the project's final word, and the
|
|
53
|
+
// only place the custom theme finds the classes a page uses.
|
|
32
54
|
const options = {
|
|
33
55
|
tokens: theme.tokens,
|
|
34
|
-
css: [extraCss, theme.css].filter(Boolean).join('\n\n'),
|
|
56
|
+
css: [extraCss, theme.css, ...projectCss(config.rootDir)].filter(Boolean).join('\n\n'),
|
|
35
57
|
};
|
|
36
58
|
|
|
37
59
|
/** @param {ThemeEngine} engine */
|
|
38
60
|
const mount = (engine) => {
|
|
39
61
|
setThemeClasses(engine.classes);
|
|
62
|
+
setThemeFramework(framework);
|
|
40
63
|
return engine;
|
|
41
64
|
};
|
|
42
65
|
|
|
@@ -50,22 +50,33 @@ npx docpensieve init my-site --yes --name "My documentation"
|
|
|
50
50
|
| `-f, --force` | Overwrites an existing configuration |
|
|
51
51
|
| `--minimal` | Leaves DocPensieve's documentation out of the site |
|
|
52
52
|
|
|
53
|
-
##
|
|
53
|
+
## Installing it in a project
|
|
54
|
+
|
|
55
|
+
Through `npx` alone, the version used is the latest. To pin the one a project
|
|
56
|
+
uses — what continuous integration needs — install it in the project:
|
|
54
57
|
|
|
55
58
|
```bash
|
|
59
|
+
npm init -y # only when the folder has no package.json yet
|
|
56
60
|
npm install docpensieve
|
|
57
|
-
npx docpensieve init .
|
|
61
|
+
npx docpensieve init .
|
|
58
62
|
```
|
|
59
63
|
|
|
60
|
-
`
|
|
61
|
-
|
|
64
|
+
`npm install` only installs: it creates no site and asks nothing — `init` does.
|
|
65
|
+
It also installs in the nearest folder that has a `package.json`, going up from
|
|
66
|
+
the current one: in a folder without one, the package lands in a parent folder
|
|
67
|
+
and nothing appears where you are. Hence `npm init -y` first. `npx docpensieve`
|
|
68
|
+
then runs the installed copy.
|
|
69
|
+
|
|
70
|
+
`init` refuses to overwrite an existing configuration: you have to ask for it
|
|
71
|
+
with `--force`. The refusal is deliberate — a
|
|
62
72
|
configuration overwritten by mistake is only noticed at the next deployment.
|
|
63
73
|
|
|
64
74
|
## Choosing the theme
|
|
65
75
|
|
|
66
76
|
`tailwind` is the default and installs Tailwind as a dependency. `custom` does
|
|
67
77
|
without it entirely: the site is then styled by a stylesheet written in the
|
|
68
|
-
package, with no styling dependency.
|
|
78
|
+
package, with no styling dependency. Your own classes then go in the `theme/`
|
|
79
|
+
folder, which `init` starts with `theme/custom.css`.
|
|
69
80
|
|
|
70
81
|
Both are equivalent in use — the templates are the same, only the styling
|
|
71
82
|
changes. The choice is not final: it fits in one field of the configuration,
|
|
@@ -126,8 +126,8 @@ Three ways to guard against it:
|
|
|
126
126
|
|
|
127
127
|
- prefer `<div>` to `<p>` as a wrapper — a paragraph is valid inside it;
|
|
128
128
|
- write short content on the same line as its tags;
|
|
129
|
-
- for repeated cases, put a class in `theme
|
|
130
|
-
utilities, so that the line stays short.
|
|
129
|
+
- for repeated cases, put a class in the `theme/` folder rather than a long
|
|
130
|
+
string of utilities, so that the line stays short.
|
|
131
131
|
|
|
132
132
|
`docpensieve check` reports these nestings on the produced site.
|
|
133
133
|
|
|
@@ -100,9 +100,11 @@ Without this layer, a component rule written after a utility would beat it at
|
|
|
100
100
|
equal specificity, and the author's `className` would be ignored without a
|
|
101
101
|
word.
|
|
102
102
|
|
|
103
|
-
The `custom` theme has no utilities: a `className` there
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
The `custom` theme has no utilities: a `className` there names classes of your
|
|
104
|
+
own. Write them in the `theme/` folder, at the root of the project: every `.css`
|
|
105
|
+
file in it is appended to the stylesheet, outside any layer, so they come
|
|
106
|
+
before the component rules. Under this theme, `init` starts the folder with
|
|
107
|
+
`theme/custom.css`.
|
|
106
108
|
|
|
107
109
|
## The stylesheet is compiled last
|
|
108
110
|
|
|
@@ -7,19 +7,22 @@ description: Structured card, with header, body, footer and image.
|
|
|
7
7
|
|
|
8
8
|
## The simplest
|
|
9
9
|
|
|
10
|
-
<Card
|
|
10
|
+
<Card style={{ maxWidth: '24rem' }}>
|
|
11
11
|
<CardBody>A card reduced to its body.</CardBody>
|
|
12
12
|
</Card>
|
|
13
13
|
|
|
14
14
|
```mdx
|
|
15
|
-
<Card
|
|
15
|
+
<Card>
|
|
16
16
|
<CardBody>A card reduced to its body.</CardBody>
|
|
17
17
|
</Card>
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
The examples of this page narrow some cards through `style`, which every theme
|
|
21
|
+
understands; the look proper comes further down.
|
|
22
|
+
|
|
20
23
|
## With its three parts
|
|
21
24
|
|
|
22
|
-
<Card
|
|
25
|
+
<Card style={{ maxWidth: '24rem' }}>
|
|
23
26
|
<CardHeader>Header</CardHeader>
|
|
24
27
|
<CardBody>
|
|
25
28
|
The body of the card. It takes the remaining space when several cards sit side by side.
|
|
@@ -62,7 +65,12 @@ meaning deserves a written `alt`.
|
|
|
62
65
|
|
|
63
66
|
## A grid of cards
|
|
64
67
|
|
|
65
|
-
It is the most common use.
|
|
68
|
+
It is the most common use. Stretching each card to the height of its column
|
|
69
|
+
aligns them whatever the content.
|
|
70
|
+
|
|
71
|
+
<ForTheme framework="tailwind">
|
|
72
|
+
|
|
73
|
+
With the Tailwind theme, the `h-full` utility does it:
|
|
66
74
|
|
|
67
75
|
<Columns>
|
|
68
76
|
<Column>
|
|
@@ -97,9 +105,56 @@ It is the most common use. `h-full` aligns the heights whatever the content.
|
|
|
97
105
|
</Columns>
|
|
98
106
|
```
|
|
99
107
|
|
|
108
|
+
</ForTheme>
|
|
109
|
+
<ForTheme framework="custom">
|
|
110
|
+
|
|
111
|
+
With the custom theme, a class of your own does it — here `full-height`, from
|
|
112
|
+
`theme/99-docpensieve.css`:
|
|
113
|
+
|
|
114
|
+
<Columns>
|
|
115
|
+
<Column>
|
|
116
|
+
<Card className="full-height">
|
|
117
|
+
<CardHeader>Stateless</CardHeader>
|
|
118
|
+
<CardBody>
|
|
119
|
+
Components are rendered at build time. None of them keeps state or listens to events.
|
|
120
|
+
</CardBody>
|
|
121
|
+
</Card>
|
|
122
|
+
</Column>
|
|
123
|
+
<Column>
|
|
124
|
+
<Card className="full-height">
|
|
125
|
+
<CardHeader>Dependency-free</CardHeader>
|
|
126
|
+
<CardBody>The delivered HTML loads nothing.</CardBody>
|
|
127
|
+
</Card>
|
|
128
|
+
</Column>
|
|
129
|
+
<Column>
|
|
130
|
+
<Card className="full-height">
|
|
131
|
+
<CardHeader>No surprises</CardHeader>
|
|
132
|
+
<CardBody>
|
|
133
|
+
What cannot be rendered stops the build rather than disappearing silently.
|
|
134
|
+
</CardBody>
|
|
135
|
+
</Card>
|
|
136
|
+
</Column>
|
|
137
|
+
</Columns>
|
|
138
|
+
|
|
139
|
+
```mdx
|
|
140
|
+
<Columns>
|
|
141
|
+
<Column>
|
|
142
|
+
<Card className="full-height">…</Card>
|
|
143
|
+
</Column>
|
|
144
|
+
</Columns>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```css
|
|
148
|
+
.full-height {
|
|
149
|
+
block-size: 100%;
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
</ForTheme>
|
|
154
|
+
|
|
100
155
|
## Elevation
|
|
101
156
|
|
|
102
|
-
<Card
|
|
157
|
+
<Card style={{ maxWidth: '24rem' }} elevated>
|
|
103
158
|
<CardBody>This card carries a shadow.</CardBody>
|
|
104
159
|
</Card>
|
|
105
160
|
|
|
@@ -109,7 +164,7 @@ It is the most common use. `h-full` aligns the heights whatever the content.
|
|
|
109
164
|
|
|
110
165
|
## Fully clickable
|
|
111
166
|
|
|
112
|
-
<Card
|
|
167
|
+
<Card style={{ maxWidth: '24rem' }} href="./columns/">
|
|
113
168
|
<CardHeader>Go to the columns</CardHeader>
|
|
114
169
|
<CardBody>The whole card is a link, not just the title.</CardBody>
|
|
115
170
|
</Card>
|
|
@@ -126,6 +181,8 @@ root — the same rules as for a Markdown link.
|
|
|
126
181
|
Nothing specific to the component: a card, a little typography, and the footer
|
|
127
182
|
carrying the caption.
|
|
128
183
|
|
|
184
|
+
<ForTheme framework="tailwind">
|
|
185
|
+
|
|
129
186
|
<Columns>
|
|
130
187
|
<Column span={4}>
|
|
131
188
|
<Card className="h-full text-center">
|
|
@@ -139,7 +196,7 @@ carrying the caption.
|
|
|
139
196
|
<Column span={4}>
|
|
140
197
|
<Card className="h-full text-center">
|
|
141
198
|
<CardBody>
|
|
142
|
-
<div className="text-4xl font-semibold text-indigo-600">
|
|
199
|
+
<div className="text-4xl font-semibold text-indigo-600">9</div>
|
|
143
200
|
<div className="text-sm">components</div>
|
|
144
201
|
</CardBody>
|
|
145
202
|
<CardFooter>usable without an import</CardFooter>
|
|
@@ -156,8 +213,45 @@ carrying the caption.
|
|
|
156
213
|
</Column>
|
|
157
214
|
</Columns>
|
|
158
215
|
|
|
216
|
+
</ForTheme>
|
|
217
|
+
<ForTheme framework="custom">
|
|
218
|
+
|
|
219
|
+
<Columns>
|
|
220
|
+
<Column span={4}>
|
|
221
|
+
<Card className="full-height centered">
|
|
222
|
+
<CardBody>
|
|
223
|
+
<div className="figure">0</div>
|
|
224
|
+
<div className="small">bytes of JavaScript</div>
|
|
225
|
+
</CardBody>
|
|
226
|
+
<CardFooter>on every delivered page</CardFooter>
|
|
227
|
+
</Card>
|
|
228
|
+
</Column>
|
|
229
|
+
<Column span={4}>
|
|
230
|
+
<Card className="full-height centered">
|
|
231
|
+
<CardBody>
|
|
232
|
+
<div className="figure">9</div>
|
|
233
|
+
<div className="small">components</div>
|
|
234
|
+
</CardBody>
|
|
235
|
+
<CardFooter>usable without an import</CardFooter>
|
|
236
|
+
</Card>
|
|
237
|
+
</Column>
|
|
238
|
+
<Column span={4}>
|
|
239
|
+
<Card className="full-height centered">
|
|
240
|
+
<CardBody>
|
|
241
|
+
<div className="figure">1</div>
|
|
242
|
+
<div className="small">stylesheet</div>
|
|
243
|
+
</CardBody>
|
|
244
|
+
<CardFooter>per version</CardFooter>
|
|
245
|
+
</Card>
|
|
246
|
+
</Column>
|
|
247
|
+
</Columns>
|
|
248
|
+
|
|
249
|
+
</ForTheme>
|
|
250
|
+
|
|
159
251
|
## A warning card
|
|
160
252
|
|
|
253
|
+
<ForTheme framework="tailwind">
|
|
254
|
+
|
|
161
255
|
<Card className="border-amber-400 bg-amber-50 dark:bg-amber-950/30">
|
|
162
256
|
<CardHeader className="text-amber-800 dark:text-amber-200">To check before publishing</CardHeader>
|
|
163
257
|
<CardBody className="text-sm">
|
|
@@ -166,8 +260,23 @@ carrying the caption.
|
|
|
166
260
|
</CardBody>
|
|
167
261
|
</Card>
|
|
168
262
|
|
|
263
|
+
</ForTheme>
|
|
264
|
+
<ForTheme framework="custom">
|
|
265
|
+
|
|
266
|
+
<Card className="warning">
|
|
267
|
+
<CardHeader className="warning-title">To check before publishing</CardHeader>
|
|
268
|
+
<CardBody className="small">
|
|
269
|
+
A generated site can compile without error and contain dead links.
|
|
270
|
+
<code>docpensieve check</code> reads the output back.
|
|
271
|
+
</CardBody>
|
|
272
|
+
</Card>
|
|
273
|
+
|
|
274
|
+
</ForTheme>
|
|
275
|
+
|
|
169
276
|
## The look through className
|
|
170
277
|
|
|
278
|
+
<ForTheme framework="tailwind">
|
|
279
|
+
|
|
171
280
|
The component offers no typography prop: the theme's utilities take care of
|
|
172
281
|
it, and win over the default style.
|
|
173
282
|
|
|
@@ -193,3 +302,42 @@ The component rules live in the `components` layer, below the utilities: a
|
|
|
193
302
|
<Card className="max-w-sm border-0 shadow-none bg-transparent">
|
|
194
303
|
<CardBody className="px-0">No border, no background, no inner padding.</CardBody>
|
|
195
304
|
</Card>
|
|
305
|
+
|
|
306
|
+
</ForTheme>
|
|
307
|
+
<ForTheme framework="custom">
|
|
308
|
+
|
|
309
|
+
The component offers no typography prop: the look comes from classes of your
|
|
310
|
+
own, written in the `theme/` folder — here those of `theme/99-docpensieve.css`.
|
|
311
|
+
|
|
312
|
+
<Card className="narrow highlight">
|
|
313
|
+
<CardHeader className="centered caps accent-text">Centred, in capitals</CardHeader>
|
|
314
|
+
<CardBody className="small italic">Small, italic body.</CardBody>
|
|
315
|
+
</Card>
|
|
316
|
+
|
|
317
|
+
```mdx
|
|
318
|
+
<Card className="highlight">
|
|
319
|
+
<CardHeader className="centered caps accent-text">Centred, in capitals</CardHeader>
|
|
320
|
+
<CardBody className="small italic">Small, italic body.</CardBody>
|
|
321
|
+
</Card>
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
```css
|
|
325
|
+
.highlight {
|
|
326
|
+
border-color: var(--dp-accent);
|
|
327
|
+
background: var(--dp-accent-soft);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.caps {
|
|
331
|
+
text-transform: uppercase;
|
|
332
|
+
letter-spacing: 0.05em;
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
The component rules live in the `components` layer, those of the theme folder
|
|
337
|
+
in none: a class of yours always wins, even to undo the default style.
|
|
338
|
+
|
|
339
|
+
<Card className="narrow plain">
|
|
340
|
+
<CardBody className="flush">No border, no background, no inner padding.</CardBody>
|
|
341
|
+
</Card>
|
|
342
|
+
|
|
343
|
+
</ForTheme>
|