storybook-addon-md 0.1.0 → 0.2.0
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/CHANGELOG.md +16 -0
- package/README.md +66 -249
- package/STYLING.md +78 -0
- package/dist/content.d.ts +1 -1
- package/dist/content.js +3 -11
- package/dist/index.d.ts +0 -1
- package/dist/preset.js +11 -10
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# storybook-addon-md
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 58f8a64: Remove the `exclude` option. Move exclusions into `patterns` with a leading `!`:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
patterns: ['docs/**/*.md', '!docs/private/**'];
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Negative globs take precedence regardless of order. Configurations that still use `exclude` now report a migration error instead of silently including excluded files.
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- 58f8a64: Upgrade Chokidar to v5 and replace fast-glob with tinyglobby. Keep explicit glob matching, negative-pattern exclusions, and live Markdown updates.
|
|
18
|
+
|
|
3
19
|
## 0.1.0
|
|
4
20
|
|
|
5
21
|
Initial release.
|
package/README.md
CHANGED
|
@@ -1,70 +1,24 @@
|
|
|
1
1
|
# Storybook Markdown
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Write ordinary `.md` files and browse them inside Storybook. Attach documentation to component stories or create standalone pages, with no JSX, imports, or MDX wrappers to maintain.
|
|
4
4
|
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
- **Static builds.** Relative images and downloads are bundled with your documentation.
|
|
10
|
-
|
|
11
|
-
Write `Button.metadata.md` beside `Button.stories.tsx`:
|
|
12
|
-
|
|
13
|
-
```md
|
|
14
|
-
---
|
|
15
|
-
status: Stable
|
|
16
|
-
tags: [Actions]
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## Overview
|
|
20
|
-
|
|
21
|
-
Use buttons to trigger actions.
|
|
22
|
-
|
|
23
|
-
## When to use
|
|
24
|
-
|
|
25
|
-
- Submit a form.
|
|
26
|
-
- Confirm a choice.
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
The component gets a **Markdown** Docs entry with your guidance, status and tag chips, examples, and props. Authors manage Markdown files; the addon manages disposable MDX wrappers.
|
|
30
|
-
|
|
31
|
-
## Table of Contents
|
|
32
|
-
|
|
33
|
-
- [Install](#install)
|
|
34
|
-
- [Guide](#guide)
|
|
35
|
-
- [Configuration](#configuration)
|
|
36
|
-
- [Styling](#styling)
|
|
37
|
-
- [Example](#example)
|
|
38
|
-
- [Development](#development)
|
|
39
|
-
- [Releasing](#releasing)
|
|
40
|
-
- [Limitations](#limitations)
|
|
5
|
+
- Discover Markdown automatically, including live additions, edits, and deletions.
|
|
6
|
+
- Show component docs alongside existing examples and generated props.
|
|
7
|
+
- Bundle relative images and downloads in static builds.
|
|
8
|
+
- Customize native Docs styling with CSS variables or your own renderer.
|
|
41
9
|
|
|
42
10
|
## Install
|
|
43
11
|
|
|
44
|
-
The tested setup is **Storybook 10.6.0**, **@storybook/react-vite 10.6.0**, **@storybook/addon-docs 10.6.0**, **Vite 7.3.6**, and **React 19.2.4**. Node 22.13+ is required; local verification uses Node 24.21.0 and CI uses Node 24 on Linux.
|
|
45
|
-
|
|
46
|
-
This repository uses [Nub](https://nubjs.com/docs) 0.7.5. The checked-in `nub.lock` pins dependencies, and `.npmrc` selects the hoisted layout for Storybook and Vitest. `nub.jsonc` keeps scripts on standard Node without Nub runtime hooks. CI installs with `nub install --frozen-lockfile`.
|
|
47
|
-
|
|
48
|
-
To build an installable tarball from this repository:
|
|
49
|
-
|
|
50
|
-
```sh
|
|
51
|
-
nub install
|
|
52
|
-
nub pack
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
Install it in your Storybook project:
|
|
56
|
-
|
|
57
12
|
```sh
|
|
58
|
-
nub add -D
|
|
13
|
+
nub add -D storybook-addon-md @storybook/addon-docs@10.6.0
|
|
59
14
|
```
|
|
60
15
|
|
|
61
|
-
|
|
16
|
+
Tested with **Storybook 10.6.0**, **React Vite 10.6.0**, **Vite 7.3.6**, and **React 19.2.4**. Requires Node 22.13+. Other builders and renderers are not tested.
|
|
62
17
|
|
|
63
|
-
|
|
18
|
+
Add the addon after `@storybook/addon-docs` in `.storybook/main.ts`:
|
|
64
19
|
|
|
65
20
|
```ts
|
|
66
21
|
import type { StorybookConfig } from '@storybook/react-vite';
|
|
67
|
-
import type { MarkdownOptions } from 'storybook-addon-md';
|
|
68
22
|
|
|
69
23
|
const config: StorybookConfig = {
|
|
70
24
|
framework: '@storybook/react-vite',
|
|
@@ -74,9 +28,8 @@ const config: StorybookConfig = {
|
|
|
74
28
|
{
|
|
75
29
|
name: 'storybook-addon-md',
|
|
76
30
|
options: {
|
|
77
|
-
patterns: ['src/**/*.md', 'docs/**/*.md'],
|
|
78
|
-
|
|
79
|
-
} satisfies MarkdownOptions,
|
|
31
|
+
patterns: ['src/**/*.md', 'docs/**/*.md', '!docs/private/**'],
|
|
32
|
+
},
|
|
80
33
|
},
|
|
81
34
|
],
|
|
82
35
|
};
|
|
@@ -84,43 +37,33 @@ const config: StorybookConfig = {
|
|
|
84
37
|
export default config;
|
|
85
38
|
```
|
|
86
39
|
|
|
87
|
-
Keep
|
|
40
|
+
Keep Markdown globs in the addon’s `patterns` and story globs in Storybook’s `stories`. Add `storybook-markdown-generated/` to `.gitignore`.
|
|
88
41
|
|
|
89
|
-
|
|
42
|
+
## Write documentation
|
|
90
43
|
|
|
91
|
-
|
|
44
|
+
### Component docs
|
|
92
45
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
A plain `docs/Introduction.md` appears at `Documentation/docs/Introduction`. No frontmatter, component, or story file is required.
|
|
96
|
-
|
|
97
|
-
Use `title` to choose its sidebar location:
|
|
46
|
+
Place `Button.metadata.md` beside `Button.stories.tsx`:
|
|
98
47
|
|
|
99
48
|
```md
|
|
100
49
|
---
|
|
101
|
-
|
|
50
|
+
status: Stable
|
|
51
|
+
tags: [Actions]
|
|
102
52
|
---
|
|
103
53
|
|
|
104
|
-
##
|
|
105
|
-
|
|
106
|
-
Write ordinary Markdown here.
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Component Documentation
|
|
54
|
+
## Overview
|
|
110
55
|
|
|
111
|
-
|
|
56
|
+
Use buttons to trigger actions.
|
|
112
57
|
|
|
113
|
-
|
|
58
|
+
## When to use
|
|
114
59
|
|
|
115
|
-
|
|
116
|
-
|
|
60
|
+
- Submit a form.
|
|
61
|
+
- Confirm a choice.
|
|
117
62
|
```
|
|
118
63
|
|
|
119
|
-
|
|
64
|
+
The component gets a **Markdown** Docs entry with the content, status and tag chips, examples, and props. Existing stories and Autodocs remain available.
|
|
120
65
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
Use an array to attach one document to several story files:
|
|
66
|
+
The sibling convention supports `.stories.tsx`, `.stories.ts`, `.stories.jsx`, and `.stories.js`. To associate a different file, or share a document across components, set `stories` relative to the Markdown file:
|
|
124
67
|
|
|
125
68
|
```yaml
|
|
126
69
|
stories:
|
|
@@ -128,211 +71,85 @@ stories:
|
|
|
128
71
|
- ../components/Toggle.stories.tsx
|
|
129
72
|
```
|
|
130
73
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
### Frontmatter
|
|
134
|
-
|
|
135
|
-
Frontmatter is optional YAML with lowercase top-level keys.
|
|
136
|
-
|
|
137
|
-
| Field | Value | Behavior |
|
|
138
|
-
| ------------ | ----------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
139
|
-
| `title` | Non-empty string | Sidebar location for a standalone page. Does not relocate attached docs. |
|
|
140
|
-
| `stories` | Relative path or non-empty array of paths | Associates the document with story files. |
|
|
141
|
-
| `tags` | Array of non-empty strings | Renders chips below the title. These are content labels, not Storybook indexing tags. |
|
|
142
|
-
| `status` | Non-empty string | Renders a chip after the tags, preserving the value in `data-status`. |
|
|
143
|
-
| Other fields | YAML values | Preserved as metadata for custom layouts and renderers. |
|
|
74
|
+
A single path is also accepted. Explicit `stories` takes precedence over the filename convention, and referenced files must match Storybook’s story globs.
|
|
144
75
|
|
|
145
|
-
|
|
76
|
+
### Standalone pages
|
|
146
77
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
### Links and Assets
|
|
150
|
-
|
|
151
|
-
Use Markdown links and images, including reference-style syntax:
|
|
78
|
+
Any discovered Markdown file can stand alone. Use `title` to choose its sidebar location:
|
|
152
79
|
|
|
153
80
|
```md
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
```
|
|
81
|
+
---
|
|
82
|
+
title: Guides/Introduction
|
|
83
|
+
---
|
|
158
84
|
|
|
159
|
-
|
|
85
|
+
## Getting started
|
|
160
86
|
|
|
161
|
-
|
|
87
|
+
Write ordinary Markdown here.
|
|
88
|
+
```
|
|
162
89
|
|
|
163
|
-
|
|
90
|
+
Without a title, `docs/Introduction.md` appears at `Documentation/docs/Introduction`.
|
|
164
91
|
|
|
165
|
-
|
|
92
|
+
### Frontmatter
|
|
166
93
|
|
|
167
|
-
|
|
168
|
-
| -------------- | ------------------------------ | ------------------------------------------------------------------------ |
|
|
169
|
-
| `patterns` | Required | Array of Markdown globs relative to `root`. Supports negative globs. |
|
|
170
|
-
| `exclude` | `[]` | Excluded globs relative to `root`. Exclusions take precedence. |
|
|
171
|
-
| `root` | `..` | Content root relative to the Storybook config directory. |
|
|
172
|
-
| `generatedDir` | `storybook-markdown-generated` | Visible folder name under the working directory. |
|
|
173
|
-
| `stylesheet` | None | CSS file relative to `root`, loaded for documentation pages. |
|
|
174
|
-
| `presentation` | None | Module relative to `root`, exporting `Layout` and/or `MarkdownRenderer`. |
|
|
94
|
+
YAML frontmatter is optional. Use lowercase field names.
|
|
175
95
|
|
|
176
|
-
|
|
96
|
+
| Field | Meaning |
|
|
97
|
+
| ------------ | -------------------------------------------------- |
|
|
98
|
+
| `title` | Sidebar location for standalone pages. |
|
|
99
|
+
| `stories` | Relative story-file path or array of paths. |
|
|
100
|
+
| `tags` | Array of labels rendered as chips below the title. |
|
|
101
|
+
| `status` | A chip with its value preserved in `data-status`. |
|
|
102
|
+
| Other fields | Preserved as metadata for custom presentation. |
|
|
177
103
|
|
|
178
|
-
|
|
104
|
+
Invalid frontmatter, missing or ambiguous story references, and missing local assets produce source-specific errors.
|
|
179
105
|
|
|
180
|
-
|
|
106
|
+
## Configuration
|
|
181
107
|
|
|
182
|
-
|
|
108
|
+
| Option | Default | Purpose |
|
|
109
|
+
| -------------- | ------------------------------ | ------------------------------------------------------------- |
|
|
110
|
+
| `patterns` | Required | Markdown globs; prefix with `!` to exclude files. |
|
|
111
|
+
| `root` | `..` | Project folder, resolved from the Storybook config directory. |
|
|
112
|
+
| `generatedDir` | `storybook-markdown-generated` | Disposable output folder under the working directory. |
|
|
113
|
+
| `stylesheet` | None | Custom stylesheet path. |
|
|
114
|
+
| `presentation` | None | Module exporting `Layout` and/or `MarkdownRenderer`. |
|
|
183
115
|
|
|
184
|
-
|
|
116
|
+
Globs and customization paths start from your project folder (the parent of `.storybook` by default). Keep the config and local files inside that folder. Restart Storybook after changing options.
|
|
185
117
|
|
|
186
|
-
|
|
118
|
+
`generatedDir` must be a visible folder name using letters, digits, hyphens, or underscores. Hidden folders, nested paths, `node_modules`, and `storybook-static` are unsupported. Ignore the folder in Git; the addon manages its contents.
|
|
187
119
|
|
|
188
120
|
## Styling
|
|
189
121
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
### CSS Variables
|
|
193
|
-
|
|
194
|
-
Set `stylesheet: '.storybook/markdown.css'` in the addon options, then define your overrides:
|
|
122
|
+
Set `stylesheet: '.storybook/markdown.css'` to override the defaults:
|
|
195
123
|
|
|
196
124
|
```css
|
|
197
125
|
.storybook-addon-md-page {
|
|
198
126
|
--sbmd-font-size: 16px;
|
|
199
127
|
--sbmd-line-height: 1.8;
|
|
200
|
-
--sbmd-heading-color: currentColor;
|
|
201
128
|
--sbmd-tag-radius: 6px;
|
|
202
|
-
--sbmd-tag-border: 1px solid currentColor;
|
|
203
|
-
}
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
Variables cover typography, spacing, links, code, tables, images, and chips. They inherit from your theme container, and default text and link colors follow the active Docs theme. See the [CSS variable reference](STYLING.md) for the complete list.
|
|
207
|
-
|
|
208
|
-
Use ordinary CSS for other properties. `.storybook-addon-md` wraps Markdown content, including custom renderer output; titles, props, and examples sit outside it. Other stable selectors are `.storybook-addon-md-page`, `.storybook-addon-md-title`, `.storybook-addon-md-tags`, and `.storybook-addon-md-tag`.
|
|
209
|
-
|
|
210
|
-
The stylesheet is global to the preview, so scope selectors and account for Storybook’s specificity. For example, use `.sbdocs-content .storybook-addon-md h2` when overriding its heading rules. Vite handles CSS edits, imports, and relative `url()` assets.
|
|
211
|
-
|
|
212
|
-
### Status Chips
|
|
213
|
-
|
|
214
|
-
Status chips share the tag variables. Use `data-status` to map values to your theme:
|
|
215
|
-
|
|
216
|
-
```css
|
|
217
|
-
.storybook-addon-md-tag[data-status='stable' i] {
|
|
218
|
-
--sbmd-tag-color: light-dark(#1a7f37, #3fb950);
|
|
219
|
-
--sbmd-tag-background: light-dark(#dafbe1, #12261e);
|
|
220
|
-
--sbmd-tag-border: 1px solid currentColor;
|
|
221
129
|
}
|
|
222
130
|
```
|
|
223
131
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
### Light and Dark Themes
|
|
227
|
-
|
|
228
|
-
Use Storybook’s standard Docs theme configuration for a fixed theme:
|
|
229
|
-
|
|
230
|
-
```ts
|
|
231
|
-
import { themes } from 'storybook/theming';
|
|
132
|
+
Variables cover typography, spacing, links, code, tables, images, and chips. Defaults follow Storybook’s Docs theme in light and dark mode.
|
|
232
133
|
|
|
233
|
-
|
|
234
|
-
parameters: { docs: { theme: themes.dark } },
|
|
235
|
-
};
|
|
236
|
-
```
|
|
134
|
+
See [Styling](STYLING.md) for all variables, status colors, theme switching, and custom layouts or Markdown renderers. The [example stylesheet](https://github.com/ruijdacd/storybook-addon-md/blob/main/example/.storybook/markdown.css) provides a complete GitHub-inspired theme.
|
|
237
135
|
|
|
238
|
-
|
|
136
|
+
## Links and limitations
|
|
239
137
|
|
|
240
|
-
|
|
138
|
+
- Relative links and images resolve from the Markdown source and are included in static builds. Root-relative assets use Storybook’s `staticDirs`.
|
|
139
|
+
- Links to `.md` files open the original source, not a rendered Docs page. Use a Storybook URL such as `/?path=/docs/guides-introduction--docs` for page navigation.
|
|
140
|
+
- Braces and JSX-like text are treated as content. Raw HTML renders as text by default.
|
|
141
|
+
- Set Storybook’s `parameters.options.storySort` for explicit sidebar ordering. See the [example preview](https://github.com/ruijdacd/storybook-addon-md/blob/main/example/.storybook/preview.ts).
|
|
142
|
+
- The attached Docs entry name **Markdown** is reserved. Multiple development Storybooks sharing one config directory are unsupported.
|
|
241
143
|
|
|
242
|
-
|
|
144
|
+
## Example and contributing
|
|
243
145
|
|
|
244
|
-
|
|
245
|
-
import { DefaultLayout, DefaultMarkdownRenderer } from 'storybook-addon-md/runtime';
|
|
246
|
-
import type { LayoutProps, MarkdownDocument } from 'storybook-addon-md/runtime';
|
|
247
|
-
|
|
248
|
-
export function Layout(props: LayoutProps) {
|
|
249
|
-
return <DefaultLayout {...props} />;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
export function MarkdownRenderer(document: MarkdownDocument) {
|
|
253
|
-
return <DefaultMarkdownRenderer {...document} />;
|
|
254
|
-
}
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
`MarkdownRenderer` receives `{ markdown, metadata, source }`: processed Markdown with resolved asset URLs, preserved frontmatter, and the source path relative to `root`.
|
|
258
|
-
|
|
259
|
-
`Layout` receives `{ documents, title, attached, children, examples }`. Render `children` and `examples` to keep documentation and native example/props blocks. `examples` is `null` for standalone pages. `title` contains the standalone sidebar title and is empty for attached pages; `DefaultLayout` uses Storybook’s `Title` block for those.
|
|
260
|
-
|
|
261
|
-
Both customization files must stay inside `root`. Missing files produce source-specific errors. Styling and presentation are independent options.
|
|
262
|
-
|
|
263
|
-
## Example
|
|
264
|
-
|
|
265
|
-
Run the included Storybook:
|
|
146
|
+
Run the included Storybook with **Nub 0.7.5** and **Node 24**:
|
|
266
147
|
|
|
267
148
|
```sh
|
|
268
149
|
nub install
|
|
269
150
|
nub run storybook
|
|
270
151
|
```
|
|
271
152
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
The theme takes its direction from [GitHub Primer]. [Markdown styles] reference [Tailwind theme variables] for shared colors, typography, spacing, and radii. The example uses Tailwind v4, `clsx`, and `class-variance-authority`; these are development dependencies, not addon requirements. Tailwind Preflight is omitted to preserve native Docs styles.
|
|
275
|
-
|
|
276
|
-
Components use `light-dark()` and `color-scheme: light dark` on `:root`. The manager and Docs container also follow system-preference changes live.
|
|
277
|
-
|
|
278
|
-
## Development
|
|
279
|
-
|
|
280
|
-
Install Chromium for the browser suites:
|
|
281
|
-
|
|
282
|
-
```sh
|
|
283
|
-
nub exec playwright install chromium
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
| Command | Purpose |
|
|
287
|
-
| ------------------------- | ------------------------------------------------------------- |
|
|
288
|
-
| `nub run build` | Compile the addon and its declarations. |
|
|
289
|
-
| `nub run check` | Build the addon, then type-check source, examples, and tests. |
|
|
290
|
-
| `nub run lint` | Run Oxlint. Use `lint:fix` for automatic fixes. |
|
|
291
|
-
| `nub run format:check` | Check Oxfmt formatting. Use `format` to write changes. |
|
|
292
|
-
| `nub run test` | Run Vitest unit tests. |
|
|
293
|
-
| `nub run test:browser` | Run Vitest Browser Mode with Playwright/Chromium. |
|
|
294
|
-
| `nub run test:e2e` | Verify development and static Storybooks in Chromium. |
|
|
295
|
-
| `nub run test:package` | Install and verify a packed addon in an isolated consumer. |
|
|
296
|
-
| `nub run build-storybook` | Build the example as a static site. |
|
|
297
|
-
| `nub pack` | Build and package the addon. |
|
|
298
|
-
|
|
299
|
-
Use `test:watch` or `test:browser:watch` while developing. End-to-end tests use ports 16006/16007, and the package smoke check uses 16008. Browser screenshots and failure traces go to `test-results/`.
|
|
300
|
-
|
|
301
|
-
[CI] runs the checks on Node 24 and Linux, including a dependency audit. Tests cover discovery, associations, watcher recovery, assets, customization, keyboard interaction, and live theme switching. The package smoke check supplies its own image fixture.
|
|
302
|
-
|
|
303
|
-
Content parsing lives in `src/content.ts`, disposable generation in `src/generator.ts`, Storybook integration in `src/preset.ts`, and presentation in `src/runtime.tsx`. The addon uses Storybook’s MDX compilation and indexing; it does not install a custom indexer.
|
|
304
|
-
|
|
305
|
-
Report reproducible bugs in the [issue tracker].
|
|
306
|
-
|
|
307
|
-
## Releasing
|
|
308
|
-
|
|
309
|
-
Releases use [Changesets](https://changesets.dev/guide/automating). For a user-facing change, run `nub run changeset`, choose a patch/minor/major bump, and include the generated release note in your PR. Tooling-only changes do not need a release note.
|
|
310
|
-
|
|
311
|
-
After CI passes for a push to `main`, `release.yml` opens or updates a release PR with the version and changelog. Merge that PR to publish after CI passes again. Nub manages dependencies and scripts; Changesets invokes npm for publishing.
|
|
312
|
-
|
|
313
|
-
One-time setup:
|
|
314
|
-
|
|
315
|
-
1. If the package does not exist on npm yet, publish the initial version from a clean checkout: `nub run build`, `npm login`, then `npm publish --access public`.
|
|
316
|
-
2. In the npm package’s **Settings → Trusted publishing**, select GitHub Actions, owner `ruijdacd`, repository `storybook-addon-md`, workflow `release.yml`, and allow publishing. Leave the environment empty.
|
|
317
|
-
3. In GitHub’s **Settings → Actions → General**, enable **Allow GitHub Actions to create and approve pull requests**.
|
|
318
|
-
|
|
319
|
-
No `NPM_TOKEN` secret is needed. The workflow uses GitHub’s automatic token for release PRs and OIDC for [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/). It installs npm 11 with Node 24 to support OIDC.
|
|
320
|
-
|
|
321
|
-
Release PRs created with the automatic GitHub token do not trigger PR workflows. If branch protection requires those checks, close and reopen the release PR yourself to trigger CI before merging. The release workflow always waits for CI on the merged commit.
|
|
322
|
-
|
|
323
|
-
## Limitations
|
|
324
|
-
|
|
325
|
-
- Only the Storybook, React, and Vite setup listed under [Install](#install) has been tested. Other renderers and builders are unsupported.
|
|
326
|
-
- Markdown supports tables, lists, fenced code, and reference links. Braces and JSX-like text are content, never evaluated. The default renderer displays raw HTML as text; use Markdown syntax for links and images.
|
|
327
|
-
- The **Markdown** page name is reserved under attached components. Avoid giving a hand-written MDX page the same name there.
|
|
328
|
-
- The watcher observes `root` and rescans matching Markdown when documentation or its dependencies change. Interactive story state may reset. Keep `root` focused on your project.
|
|
329
|
-
- Large monorepos, simultaneous Storybooks sharing one config directory, symlinked content directories, and MDX authoring are outside the initial scope.
|
|
153
|
+
Browse **Guides → Introduction**, **Components → Button**, and **Components → Toggle** for standalone, attached, and shared docs with system light/dark styling.
|
|
330
154
|
|
|
331
|
-
[
|
|
332
|
-
[Docs container]: https://github.com/ruijdacd/storybook-addon-md/blob/main/example/.storybook/SystemDocsContainer.tsx
|
|
333
|
-
[manager configuration]: https://github.com/ruijdacd/storybook-addon-md/blob/main/example/.storybook/manager.ts
|
|
334
|
-
[GitHub Primer]: https://primer.style/product/
|
|
335
|
-
[Markdown styles]: https://github.com/ruijdacd/storybook-addon-md/blob/main/example/.storybook/markdown.css
|
|
336
|
-
[Tailwind theme variables]: https://github.com/ruijdacd/storybook-addon-md/blob/main/example/.storybook/tailwind.css
|
|
337
|
-
[CI]: https://github.com/ruijdacd/storybook-addon-md/actions/workflows/ci.yml
|
|
338
|
-
[issue tracker]: https://github.com/ruijdacd/storybook-addon-md/issues
|
|
155
|
+
See [Contributing](CONTRIBUTING.md) for tests and releases, or [open an issue](https://github.com/ruijdacd/storybook-addon-md/issues).
|
package/STYLING.md
CHANGED
|
@@ -103,3 +103,81 @@ Use your theme selector to override colors in dark mode. The [complete example](
|
|
|
103
103
|
Status chips have `data-status` set to the original frontmatter value. Override `--sbmd-tag-*` on selectors such as `.storybook-addon-md-tag[data-status="stable" i]` to assign a status-specific appearance.
|
|
104
104
|
|
|
105
105
|
These styles target Markdown content and its title/chips. Story canvases, props controls, and syntax highlighting still use Storybook’s theme. Custom renderers can use the shared styles when they produce matching HTML elements; custom layouts own any additional structure. Internal `--sbmd-native-*` variables carry Storybook theme values and are not customization hooks.
|
|
106
|
+
|
|
107
|
+
## Customization
|
|
108
|
+
|
|
109
|
+
The default presentation uses native Storybook Docs blocks and one shared stylesheet. Existing `parameters.docs.container` and `parameters.docs.theme` still apply.
|
|
110
|
+
|
|
111
|
+
### CSS Variables
|
|
112
|
+
|
|
113
|
+
Set `stylesheet: '.storybook/markdown.css'` in the addon options, then define your overrides:
|
|
114
|
+
|
|
115
|
+
```css
|
|
116
|
+
.storybook-addon-md-page {
|
|
117
|
+
--sbmd-font-size: 16px;
|
|
118
|
+
--sbmd-line-height: 1.8;
|
|
119
|
+
--sbmd-heading-color: currentColor;
|
|
120
|
+
--sbmd-tag-radius: 6px;
|
|
121
|
+
--sbmd-tag-border: 1px solid currentColor;
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Variables cover typography, spacing, links, code, tables, images, and chips. They inherit from your theme container, and default text and link colors follow the active Docs theme. See [Variables](#variables) for the complete list.
|
|
126
|
+
|
|
127
|
+
Use ordinary CSS for other properties. `.storybook-addon-md` wraps Markdown content, including custom renderer output; titles, props, and examples sit outside it. Other stable selectors are `.storybook-addon-md-page`, `.storybook-addon-md-title`, `.storybook-addon-md-tags`, and `.storybook-addon-md-tag`.
|
|
128
|
+
|
|
129
|
+
The stylesheet is global to the preview, so scope selectors and account for Storybook’s specificity. For example, use `.sbdocs-content .storybook-addon-md h2` when overriding its heading rules. Vite handles CSS edits, imports, and relative `url()` assets.
|
|
130
|
+
|
|
131
|
+
### Status Chips
|
|
132
|
+
|
|
133
|
+
Status chips share the tag variables. Use `data-status` to map values to your theme:
|
|
134
|
+
|
|
135
|
+
```css
|
|
136
|
+
.storybook-addon-md-tag[data-status='stable' i] {
|
|
137
|
+
--sbmd-tag-color: light-dark(#1a7f37, #3fb950);
|
|
138
|
+
--sbmd-tag-background: light-dark(#dafbe1, #12261e);
|
|
139
|
+
--sbmd-tag-border: 1px solid currentColor;
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The `i` flag matches both `Stable` and `stable`. The addon accepts any status; your stylesheet decides its colors. Set `color-scheme: light dark` on the theme container when using `light-dark()`.
|
|
144
|
+
|
|
145
|
+
### Light and Dark Themes
|
|
146
|
+
|
|
147
|
+
Use Storybook’s standard Docs theme configuration for a fixed theme:
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import { themes } from 'storybook/theming';
|
|
151
|
+
|
|
152
|
+
export default {
|
|
153
|
+
parameters: { docs: { theme: themes.dark } },
|
|
154
|
+
};
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
For live system-preference switching, follow the example’s [Docs container] and [manager configuration]. They subscribe to preference changes so Storybook’s interface and documentation update together without reloading.
|
|
158
|
+
|
|
159
|
+
### Custom Layouts and Renderers
|
|
160
|
+
|
|
161
|
+
Set `presentation: '.storybook/markdown-presentation.tsx'` and export either or both components:
|
|
162
|
+
|
|
163
|
+
```tsx
|
|
164
|
+
import { DefaultLayout, DefaultMarkdownRenderer } from 'storybook-addon-md/runtime';
|
|
165
|
+
import type { LayoutProps, MarkdownDocument } from 'storybook-addon-md/runtime';
|
|
166
|
+
|
|
167
|
+
export function Layout(props: LayoutProps) {
|
|
168
|
+
return <DefaultLayout {...props} />;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function MarkdownRenderer(document: MarkdownDocument) {
|
|
172
|
+
return <DefaultMarkdownRenderer {...document} />;
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`MarkdownRenderer` receives `{ markdown, metadata, source }`: processed Markdown with resolved asset URLs, preserved frontmatter, and the source path relative to the project folder.
|
|
177
|
+
|
|
178
|
+
`Layout` receives `{ documents, title, attached, children, examples }`. Render `children` and `examples` to keep documentation and native example/props blocks. `examples` is `null` for standalone pages. `title` contains the standalone sidebar title and is empty for attached pages; `DefaultLayout` uses Storybook’s `Title` block for those.
|
|
179
|
+
|
|
180
|
+
Customization paths are relative to the project folder and must stay inside it. Missing files produce source-specific errors. Styling and presentation are independent options.
|
|
181
|
+
|
|
182
|
+
[Docs container]: https://github.com/ruijdacd/storybook-addon-md/blob/main/example/.storybook/SystemDocsContainer.tsx
|
|
183
|
+
[manager configuration]: https://github.com/ruijdacd/storybook-addon-md/blob/main/example/.storybook/manager.ts
|
package/dist/content.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export declare function resolveAssets(body: string, file: string, root: string):
|
|
|
25
25
|
token: string;
|
|
26
26
|
}[];
|
|
27
27
|
}>;
|
|
28
|
-
export declare function discover({ root, patterns,
|
|
28
|
+
export declare function discover({ root, patterns, output }: ContentOptions): Promise<{
|
|
29
29
|
markdown: string;
|
|
30
30
|
assets: {
|
|
31
31
|
file: string;
|
package/dist/content.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFile, realpath, stat } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import glob from '
|
|
3
|
+
import { glob } from 'tinyglobby';
|
|
4
4
|
import { isMap, parseDocument } from 'yaml';
|
|
5
5
|
import { unified } from 'unified';
|
|
6
6
|
import remarkParse from 'remark-parse';
|
|
@@ -137,7 +137,7 @@ export async function resolveAssets(body, file, root) {
|
|
|
137
137
|
}
|
|
138
138
|
return { markdown: markdown.stringify(tree), assets };
|
|
139
139
|
}
|
|
140
|
-
export async function discover({ root, patterns,
|
|
140
|
+
export async function discover({ root, patterns, output }) {
|
|
141
141
|
if (!Array.isArray(patterns) ||
|
|
142
142
|
!patterns.length ||
|
|
143
143
|
patterns.some((item) => typeof item !== 'string' ||
|
|
@@ -146,21 +146,13 @@ export async function discover({ root, patterns, exclude = [], output }) {
|
|
|
146
146
|
item.split('/').includes('..'))) {
|
|
147
147
|
throw fail(root, 'patterns must be a non-empty array of globs relative to root');
|
|
148
148
|
}
|
|
149
|
-
if (!Array.isArray(exclude) ||
|
|
150
|
-
exclude.some((item) => typeof item !== 'string' ||
|
|
151
|
-
!item ||
|
|
152
|
-
path.isAbsolute(item) ||
|
|
153
|
-
item.split('/').includes('..'))) {
|
|
154
|
-
throw fail(root, 'exclude must be an array of globs relative to root');
|
|
155
|
-
}
|
|
156
149
|
const files = await glob(patterns, {
|
|
157
150
|
cwd: root,
|
|
158
151
|
absolute: true,
|
|
159
152
|
onlyFiles: true,
|
|
160
|
-
|
|
153
|
+
expandDirectories: false,
|
|
161
154
|
followSymbolicLinks: false,
|
|
162
155
|
ignore: [
|
|
163
|
-
...exclude,
|
|
164
156
|
'**/node_modules/**',
|
|
165
157
|
'**/.git/**',
|
|
166
158
|
'**/storybook-static/**',
|
package/dist/index.d.ts
CHANGED
package/dist/preset.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
import { watch } from 'chokidar';
|
|
4
|
-
import glob from 'fast-glob';
|
|
5
4
|
import picomatch from 'picomatch';
|
|
6
5
|
import { generate, writeChanged } from './generator.js';
|
|
7
6
|
import { fail, slash } from './content.js';
|
|
@@ -10,6 +9,9 @@ function settings(options) {
|
|
|
10
9
|
const configDir = path.resolve(options.configDir);
|
|
11
10
|
const root = path.resolve(configDir, options.root ?? '..');
|
|
12
11
|
const generatedDir = options.generatedDir ?? 'storybook-markdown-generated';
|
|
12
|
+
if ('exclude' in options) {
|
|
13
|
+
throw fail(configDir, 'exclude has been removed; use negative globs in patterns, such as !docs/private/**');
|
|
14
|
+
}
|
|
13
15
|
if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(generatedDir) ||
|
|
14
16
|
['node_modules', 'storybook-static'].includes(generatedDir)) {
|
|
15
17
|
throw fail(configDir, 'generatedDir must be a visible folder name containing only letters, digits, hyphens or underscores');
|
|
@@ -18,7 +20,6 @@ function settings(options) {
|
|
|
18
20
|
root,
|
|
19
21
|
output: path.join(process.cwd(), generatedDir, createHash('sha256').update(configDir).digest('hex').slice(0, 12)),
|
|
20
22
|
patterns: options.patterns,
|
|
21
|
-
exclude: options.exclude,
|
|
22
23
|
stylesheet: options.stylesheet ? path.resolve(root, options.stylesheet) : undefined,
|
|
23
24
|
presentation: options.presentation ? path.resolve(root, options.presentation) : undefined,
|
|
24
25
|
};
|
|
@@ -34,12 +35,13 @@ export async function stories(existing = [], options) {
|
|
|
34
35
|
return [...existing, { directory: session.config.output, files: '*.mdx' }];
|
|
35
36
|
}
|
|
36
37
|
export function watchDocumentation(config, { onError = () => { }, onUpdate = () => { }, } = {}) {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
})
|
|
38
|
+
const isNegative = (pattern) => pattern.startsWith('!') && !pattern.startsWith('!(');
|
|
39
|
+
const positive = config.patterns.filter((pattern) => !isNegative(pattern));
|
|
40
|
+
const negative = config.patterns
|
|
41
|
+
.filter((pattern) => isNegative(pattern) && !isNegative(pattern.slice(1)))
|
|
42
|
+
.map((pattern) => pattern.slice(1));
|
|
43
|
+
const include = picomatch(positive.map((pattern) => path.posix.normalize(pattern)), { posix: true });
|
|
44
|
+
const exclude = picomatch(negative.map((pattern) => path.posix.normalize(pattern)), { posix: true });
|
|
43
45
|
const dependencies = new Set();
|
|
44
46
|
const configuredDependencies = [config.presentation, config.stylesheet].filter((file) => Boolean(file));
|
|
45
47
|
let timer;
|
|
@@ -92,8 +94,7 @@ export function watchDocumentation(config, { onError = () => { }, onUpdate = ()
|
|
|
92
94
|
if (event === 'addDir' || event === 'unlinkDir')
|
|
93
95
|
return;
|
|
94
96
|
const relative = slash(path.relative(config.root, file));
|
|
95
|
-
const markdown = file.endsWith('.md') &&
|
|
96
|
-
matchers.some(({ include, exclude }) => include(relative) && !exclude(relative));
|
|
97
|
+
const markdown = file.endsWith('.md') && include(relative) && !exclude(relative);
|
|
97
98
|
if (!markdown && !dependencies.has(file) && !configuredDependencies.includes(file))
|
|
98
99
|
return;
|
|
99
100
|
clearTimeout(timer);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "storybook-addon-md",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Discover ordinary Markdown and render it in Storybook Docs.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"docs",
|
|
@@ -67,27 +67,27 @@
|
|
|
67
67
|
"release": "nub run build && changeset publish"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"chokidar": "^
|
|
71
|
-
"fast-glob": "^3.3.3",
|
|
70
|
+
"chokidar": "^5.0.0",
|
|
72
71
|
"picomatch": "^4.0.7",
|
|
73
72
|
"remark-gfm": "^4.0.1",
|
|
74
73
|
"remark-parse": "^11.0.0",
|
|
75
74
|
"remark-stringify": "^11.0.0",
|
|
75
|
+
"tinyglobby": "^0.2.17",
|
|
76
76
|
"unified": "^11.0.5",
|
|
77
|
-
"unist-util-visit": "^5.
|
|
78
|
-
"yaml": "^2.
|
|
77
|
+
"unist-util-visit": "^5.1.0",
|
|
78
|
+
"yaml": "^2.9.0"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
81
|
"@changesets/cli": "3.0.2",
|
|
82
|
-
"@playwright/test": "^1.
|
|
82
|
+
"@playwright/test": "^1.63.0",
|
|
83
83
|
"@storybook/addon-docs": "10.6.0",
|
|
84
84
|
"@storybook/react-vite": "10.6.0",
|
|
85
85
|
"@tailwindcss/vite": "^4.3.3",
|
|
86
86
|
"@types/mdast": "^4.0.4",
|
|
87
87
|
"@types/node": "^24.13.3",
|
|
88
88
|
"@types/picomatch": "^4.0.3",
|
|
89
|
-
"@types/react": "^19.2.
|
|
90
|
-
"@types/react-dom": "^19.2.
|
|
89
|
+
"@types/react": "^19.2.18",
|
|
90
|
+
"@types/react-dom": "^19.2.7",
|
|
91
91
|
"@vitest/browser-playwright": "^4.1.11",
|
|
92
92
|
"class-variance-authority": "^0.7.1",
|
|
93
93
|
"clsx": "^2.1.1",
|