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 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
- A Storybook addon for **ordinary Markdown documentation**. Write `.md` files beside your components or in a docs folder, and browse them inside Storybook.
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
- - **Automatic discovery.** Configure file patterns once. Additions, edits, and deletions update during development.
6
- - **Component and standalone docs.** Attach guidance to existing stories, share it across components, or publish a page on its own.
7
- - **Native Docs.** Keep Storybook’s examples, generated props, and documentation styling.
8
- - **Your theme.** Customize content through CSS variables, a stylesheet, or your own layout and Markdown renderer.
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 /path/to/storybook-addon-md-0.1.0.tgz @storybook/addon-docs@10.6.0
13
+ nub add -D storybook-addon-md @storybook/addon-docs@10.6.0
59
14
  ```
60
15
 
61
- The package ships compiled JavaScript and TypeScript declarations. Consumers do not need to compile the addon.
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
- Register it after addon-docs in `.storybook/main.ts`:
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
- exclude: ['docs/private/**'],
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 your normal story patterns: referenced story files must match them. Markdown files belong in the addon’s `patterns`, not Storybook’s `stories` list.
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
- Add `storybook-markdown-generated/` to `.gitignore`.
42
+ ## Write documentation
90
43
 
91
- ## Guide
44
+ ### Component docs
92
45
 
93
- ### Standalone Pages
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
- title: Guides/Introduction
50
+ status: Stable
51
+ tags: [Actions]
102
52
  ---
103
53
 
104
- ## Getting started
105
-
106
- Write ordinary Markdown here.
107
- ```
108
-
109
- ### Component Documentation
54
+ ## Overview
110
55
 
111
- Name a document `Button.metadata.md` beside `Button.stories.tsx` to associate it automatically. The convention also checks `.stories.ts`, `.stories.jsx`, and `.stories.js`. Missing or ambiguous siblings produce an error.
56
+ Use buttons to trigger actions.
112
57
 
113
- For a different location or filename, set `stories` relative to the Markdown file:
58
+ ## When to use
114
59
 
115
- ```yaml
116
- stories: ../components/Button.stories.tsx
60
+ - Submit a form.
61
+ - Confirm a choice.
117
62
  ```
118
63
 
119
- An explicit `stories` field takes precedence over the filename convention. Existing stories and Autodocs pages remain available. Each component’s **Markdown** entry combines its attached documents in source-path order, followed by its primary example, controls/props, and remaining examples.
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
- ### Shared Documentation
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
- Each referenced component displays the shared content alongside its own documentation and examples.
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
- Shared pages deduplicate tags and statuses separately in document order. A tag and status with the same label remain separate chips. Fields such as `component` and `category` have no built-in meaning.
76
+ ### Standalone pages
146
77
 
147
- Invalid frontmatter, duplicate standalone sidebar titles, and missing references produce errors with source paths. Development errors appear in the terminal and Vite overlay or Storybook error view, then recover when corrected.
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
- ![Button states](./assets/button-states.svg)
155
-
156
- [Download the checklist](./checklist.pdf)
157
- ```
81
+ ---
82
+ title: Guides/Introduction
83
+ ---
158
84
 
159
- Local paths resolve from the source `.md` file. URL-encoded filenames and fragments are supported. Files are validated and bundled as hashed assets in static builds. Filenames containing URL-reserved characters use safe disposable copies in the generated folder.
85
+ ## Getting started
160
86
 
161
- **Links to `.md` files open their original source**, including frontmatter. They do not navigate to the rendered Storybook page or recursively bundle the linked document’s assets. For page navigation, use a Storybook URL such as `/?path=/docs/guides-introduction--docs`, adjusted for your deployment prefix.
87
+ Write ordinary Markdown here.
88
+ ```
162
89
 
163
- External, fragment-only, query-only, and root-relative URLs are unchanged. Supply root-relative assets through Storybook’s `staticDirs`. Source files and local references must stay inside `root`; referenced files become part of the static build.
90
+ Without a title, `docs/Introduction.md` appears at `Documentation/docs/Introduction`.
164
91
 
165
- ## Configuration
92
+ ### Frontmatter
166
93
 
167
- | Option | Default | Description |
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
- Keep the Storybook config directory inside `root`. Restart Storybook after changing addon options.
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
- ### Generated Files
104
+ Invalid frontmatter, missing or ambiguous story references, and missing local assets produce source-specific errors.
179
105
 
180
- The addon creates a disposable subdirectory per configuration, registers its MDX glob before indexing, and removes obsolete files. Ignore your configured `generatedDir` in Git and leave its contents to the addon.
106
+ ## Configuration
181
107
 
182
- The folder must be a visible name containing letters, digits, hyphens, or underscores. Leading-dot paths and `node_modules` interfere with Storybook 10.6’s watcher; nested paths and `storybook-static` are also rejected.
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
- ### Sidebar Order
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
- Generated filenames are opaque identifiers. Set Storybook’s `parameters.options.storySort` when sidebar order matters. The [example preview] puts Guides first and sorts component titles alphabetically.
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
- The default presentation uses native Storybook Docs blocks and one shared stylesheet. Existing `parameters.docs.container` and `parameters.docs.theme` still apply.
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
- 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()`.
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
- export default {
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
- 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.
136
+ ## Links and limitations
239
137
 
240
- ### Custom Layouts and Renderers
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
- Set `presentation: '.storybook/markdown-presentation.tsx'` and export either or both components:
144
+ ## Example and contributing
243
145
 
244
- ```tsx
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
- Open **Guides → Introduction**, **Components → Button**, or **Components → Toggle** for standalone, attached, and shared documentation.
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
- [example preview]: https://github.com/ruijdacd/storybook-addon-md/blob/main/example/.storybook/preview.ts
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, exclude, output }: ContentOptions): Promise<{
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 'fast-glob';
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, exclude = [], output }) {
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
- unique: true,
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
@@ -1,7 +1,6 @@
1
1
  export interface MarkdownOptions {
2
2
  generatedDir?: string;
3
3
  patterns: string[];
4
- exclude?: string[];
5
4
  stylesheet?: string;
6
5
  root?: string;
7
6
  presentation?: string;
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 matchers = glob
38
- .generateTasks(config.patterns, { ignore: config.exclude ?? [] })
39
- .map((task) => ({
40
- include: picomatch(task.positive),
41
- exclude: picomatch(task.negative),
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.1.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": "^4.0.3",
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.0.0",
78
- "yaml": "^2.8.1"
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.58.2",
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.0",
90
- "@types/react-dom": "^19.2.0",
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",