@repobuddy/storybook 2.3.0 → 2.5.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/esm/index.d.ts CHANGED
@@ -28,14 +28,7 @@ declare function ShowHtml({
28
28
  ...props
29
29
  }: ShowHtmlProps): react_jsx_runtime0.JSX.Element;
30
30
  //#endregion
31
- //#region src/decorators/show_doc_source.d.ts
32
- /**
33
- * A decorator that shows the source code of a story above the rendered story.
34
- * The source code is taken from the story's `parameters.docs.source.code`.
35
- */
36
- declare function showDocSource<TRenderer extends Renderer = Renderer, TArgs = Args>(): DecoratorFunction<TRenderer, TArgs>;
37
- //#endregion
38
- //#region src/decorators/with_story_card.d.ts
31
+ //#region src/components/story_card.d.ts
39
32
  type StoryCardProps = {
40
33
  /**
41
34
  * Optional title displayed as a heading in the card.
@@ -44,9 +37,9 @@ type StoryCardProps = {
44
37
  title?: ReactNode | undefined;
45
38
  /**
46
39
  * Visual status of the card, affecting its background color.
47
- * - `'error'`: Red background (bg-red-100 dark:bg-red-900)
48
- * - `'warn'`: Yellow background (bg-yellow-100 dark:bg-yellow-900)
49
- * - `'info'`: Blue background (bg-sky-100 dark:bg-sky-900) - default
40
+ * - `'error'`: Red background (rbsb:bg-red-100 rbsb:dark:bg-red-900)
41
+ * - `'warn'`: Yellow background (rbsb:bg-yellow-100 rbsb:dark:bg-yellow-900)
42
+ * - `'info'`: Blue background (rbsb:bg-sky-100 rbsb:dark:bg-sky-900) - default
50
43
  */
51
44
  status?: 'error' | 'warn' | 'info' | undefined;
52
45
  /**
@@ -56,7 +49,40 @@ type StoryCardProps = {
56
49
  * If a function is provided, it receives the card state and default className,
57
50
  * and should return the final className string.
58
51
  */
59
- className?: ((state: Required<Pick<StoryCardProps, 'status'>> & {
52
+ className?: ((state: Pick<StoryCardProps, 'status'> & {
53
+ defaultClassName: string;
54
+ }) => string) | string | undefined;
55
+ /**
56
+ * Content to display in the card body.
57
+ * Can be any React node (string, JSX, etc.).
58
+ */
59
+ children?: ReactNode | undefined;
60
+ };
61
+ //#endregion
62
+ //#region src/decorators/show_doc_source.d.ts
63
+ /**
64
+ * A decorator that shows the source code of a story above the rendered story.
65
+ * The source code is taken from the story's `parameters.docs.source.code`.
66
+ *
67
+ * @param options - Options for the showDocSource decorator
68
+ * @param options.showOriginalSource - Whether to show the original source code in a card
69
+ * @param options.className - Class name to apply to the card
70
+ * @returns A decorator function that shows the source code of a story above the rendered story
71
+ */
72
+ declare function showDocSource<TRenderer extends Renderer = Renderer, TArgs = Args>(options?: Pick<StoryCardProps, 'className'> & {
73
+ showOriginalSource?: boolean | undefined;
74
+ }): DecoratorFunction<TRenderer, TArgs>;
75
+ //#endregion
76
+ //#region src/decorators/with_story_card.d.ts
77
+ type WithStoryCardProps = Omit<StoryCardProps, 'children' | 'className'> & {
78
+ /**
79
+ * Additional CSS classes or a function to compute classes.
80
+ *
81
+ * If a string is provided, it will be merged with the default classes.
82
+ * If a function is provided, it receives the card state and default className,
83
+ * and should return the final className string.
84
+ */
85
+ className?: ((state: Pick<StoryCardProps, 'status'> & {
60
86
  defaultClassName: string;
61
87
  }) => string) | string | undefined;
62
88
  /**
@@ -140,7 +166,7 @@ declare function withStoryCard<TRenderer extends Renderer = Renderer>({
140
166
  status,
141
167
  content: contentProp,
142
168
  ...rest
143
- }?: StoryCardProps): DecoratorFunction<TRenderer>;
169
+ }?: WithStoryCardProps): DecoratorFunction<TRenderer>;
144
170
  //#endregion
145
171
  //#region src/parameters/define_actions_param.d.ts
146
172
  interface ActionsParam {
@@ -760,4 +786,4 @@ type ExtendsStoryObj<S extends {
760
786
  tags?: ExtractStringLiterals<NonNullable<S['tags']>[number]> extends infer MT ? IsStringLiteral<MT> extends true ? Array<(string & {}) | MT | E['tag']> | undefined : Array<(string & {}) | E['tag']> | undefined : never;
761
787
  };
762
788
  //#endregion
763
- export { ActionsParam, BackgroundsParam, DocsParam, ExtendMeta, ExtendStoryObj, ExtendsMeta, ExtendsStoryObj, GlobalApiBackgroundsParam, GlobalApiViewportParam, LayoutParam, ShowHtml, ShowHtmlProps, SourceProps, StoryCardProps, StorySortParam, StorybookBuiltInParams, TestParam, Viewport, ViewportParam, defineActionsParam, defineBackgroundsParam, defineDocsParam, defineLayoutParam, defineParameters, defineTestParam, defineViewportParam, showDocSource, whenRunningInTest, withStoryCard };
789
+ export { ActionsParam, BackgroundsParam, DocsParam, ExtendMeta, ExtendStoryObj, ExtendsMeta, ExtendsStoryObj, GlobalApiBackgroundsParam, GlobalApiViewportParam, LayoutParam, ShowHtml, ShowHtmlProps, SourceProps, StorySortParam, StorybookBuiltInParams, TestParam, Viewport, ViewportParam, WithStoryCardProps, defineActionsParam, defineBackgroundsParam, defineDocsParam, defineLayoutParam, defineParameters, defineTestParam, defineViewportParam, showDocSource, whenRunningInTest, withStoryCard };
package/esm/index.js CHANGED
@@ -6,8 +6,8 @@ import { jsx, jsxs } from "react/jsx-runtime";
6
6
  import { SyntaxHighlighter } from "storybook/internal/components";
7
7
  import { addons } from "storybook/preview-api";
8
8
  import { ThemeProvider, convert, themes } from "storybook/theming";
9
+ import { twJoin, twMerge } from "tailwind-merge";
9
10
  import { cva } from "class-variance-authority";
10
- import { twMerge } from "tailwind-merge";
11
11
 
12
12
  export * from "@repobuddy/test"
13
13
 
@@ -37,14 +37,53 @@ function ShowHtml({ selector = "[data-testid=\"subject\"]", config, ...props })
37
37
  });
38
38
  }
39
39
 
40
+ //#endregion
41
+ //#region src/components/story_card.tsx
42
+ function storyCardTheme(state, className) {
43
+ const defaultClassName = storyCardVariants(state);
44
+ if (!className) return defaultClassName;
45
+ return twMerge(typeof className === "function" ? className({
46
+ ...state,
47
+ defaultClassName
48
+ }) : twJoin(defaultClassName, className));
49
+ }
50
+ const storyCardVariants = cva("rbsb:flex rbsb:flex-col rbsb:gap-1 rbsb:py-3 rbsb:px-4 rbsb:rounded rbsb:text-black rbsb:dark:text-gray-100", {
51
+ variants: { status: {
52
+ error: "rbsb:bg-red-100 rbsb:dark:bg-red-900",
53
+ warn: "rbsb:bg-yellow-100 rbsb:dark:bg-yellow-900",
54
+ info: "rbsb:bg-sky-100 rbsb:dark:bg-sky-900"
55
+ } },
56
+ defaultVariants: { status: "info" }
57
+ });
58
+ /**
59
+ * A card component that displays information with optional title and status styling.
60
+ *
61
+ * @param props - StoryCard component props
62
+ * @returns A section element containing the card content
63
+ */
64
+ function StoryCard({ status, className, children, title }) {
65
+ return /* @__PURE__ */ jsxs("section", {
66
+ className: storyCardTheme({ status }, className),
67
+ children: [title && /* @__PURE__ */ jsx("h2", {
68
+ className: "rbsb:text-lg rbsb:font-bold",
69
+ children: title
70
+ }), children]
71
+ });
72
+ }
73
+
40
74
  //#endregion
41
75
  //#region src/decorators/show_doc_source.tsx
42
76
  const channel = addons.getChannel();
43
77
  /**
44
78
  * A decorator that shows the source code of a story above the rendered story.
45
79
  * The source code is taken from the story's `parameters.docs.source.code`.
80
+ *
81
+ * @param options - Options for the showDocSource decorator
82
+ * @param options.showOriginalSource - Whether to show the original source code in a card
83
+ * @param options.className - Class name to apply to the card
84
+ * @returns A decorator function that shows the source code of a story above the rendered story
46
85
  */
47
- function showDocSource() {
86
+ function showDocSource(options) {
48
87
  return (Story, { parameters: { docs, darkMode } }) => {
49
88
  const storedItem = window.localStorage.getItem("sb-addon-themes-3");
50
89
  const current = typeof storedItem === "string" ? JSON.parse(storedItem).current : darkMode?.current;
@@ -53,6 +92,13 @@ function showDocSource() {
53
92
  channel.on("DARK_MODE", setIsDark);
54
93
  return () => channel.off("DARK_MODE", setIsDark);
55
94
  }, []);
95
+ const code = options?.showOriginalSource ? docs?.source?.originalSource : docs?.source?.code ?? docs?.source?.originalSource;
96
+ const language = code === docs?.source?.originalSource ? void 0 : docs?.source?.language;
97
+ const isOriginalSource = code === docs?.source?.originalSource;
98
+ const content = /* @__PURE__ */ jsx(SyntaxHighlighter, {
99
+ language,
100
+ children: code
101
+ });
56
102
  return /* @__PURE__ */ jsx(ThemeProvider, {
57
103
  theme: convert(docs?.theme ?? (isDark ? themes.dark : themes.light)),
58
104
  children: /* @__PURE__ */ jsxs("section", {
@@ -61,10 +107,17 @@ function showDocSource() {
61
107
  flexDirection: "column",
62
108
  gap: "1rem"
63
109
  },
64
- children: [/* @__PURE__ */ jsx(SyntaxHighlighter, {
65
- language: docs?.source?.language,
66
- children: docs?.source?.code
67
- }), /* @__PURE__ */ jsx(Story, {})]
110
+ children: [/* @__PURE__ */ jsx(Story, {}), /* @__PURE__ */ jsx(StoryCard, {
111
+ className: (state) => {
112
+ const modifiedState = {
113
+ ...state,
114
+ defaultClassName: twJoin(state.defaultClassName, isOriginalSource ? "rbsb:bg-transparent rbsb:dark:bg-transparent" : "rbsb:bg-gray-100 rbsb:dark:bg-gray-900")
115
+ };
116
+ const className = options?.className;
117
+ return typeof className === "function" ? className(modifiedState) : twJoin(modifiedState.defaultClassName, className);
118
+ },
119
+ children: content
120
+ })]
68
121
  })
69
122
  });
70
123
  };
@@ -178,43 +231,27 @@ function StoryCardContainer({ children }) {
178
231
  const contextValue = useMemo(() => ({
179
232
  addCard(card) {
180
233
  const key = generateKey("story-card");
181
- setCards((cards$1) => [...cards$1, {
234
+ setCards((cards) => [...cards, {
182
235
  ...card,
183
236
  key
184
237
  }]);
185
238
  return key;
186
239
  },
187
240
  removeCard(key) {
188
- setCards((cards$1) => cards$1.filter((card) => card.key !== key));
241
+ setCards((cards) => cards.filter((card) => card.key !== key));
189
242
  }
190
243
  }), []);
191
244
  return /* @__PURE__ */ jsx(StoryCardContext.Provider, {
192
245
  value: contextValue,
193
246
  children: /* @__PURE__ */ jsxs("div", {
194
- className: "flex flex-col gap-2",
195
- children: [cards.map(({ key, status, className, content, title }) => /* @__PURE__ */ jsxs("section", {
196
- className: storyCardTheme({ status }, className),
197
- children: [title && /* @__PURE__ */ jsx("h2", {
198
- className: "text-lg font-bold",
199
- children: title
200
- }), content]
247
+ className: "rbsb:flex rbsb:flex-col rbsb:gap-2",
248
+ children: [cards.map(({ content, key, ...rest }) => /* @__PURE__ */ jsx(StoryCard, {
249
+ ...rest,
250
+ children: content
201
251
  }, key)), children]
202
252
  })
203
253
  });
204
254
  }
205
- function storyCardTheme(state, className) {
206
- const defaultClassName = storyCardVariants(state);
207
- if (!className) return defaultClassName;
208
- return typeof className === "function" ? className({
209
- ...state,
210
- defaultClassName
211
- }) : twMerge(defaultClassName, className);
212
- }
213
- const storyCardVariants = cva("flex flex-col gap-1 py-3 px-4 rounded text-black dark:text-gray-100", { variants: { status: {
214
- error: "bg-red-100 dark:bg-red-900",
215
- warn: "bg-yellow-100 dark:bg-yellow-900",
216
- info: "bg-sky-100 dark:bg-sky-900"
217
- } } });
218
255
  function StoryCardCollector({ Story, title, status, className, content }) {
219
256
  const context = useContext(StoryCardContext);
220
257
  const cardIdRef = useRef(null);
@@ -343,7 +380,7 @@ const defineLayoutParam = (layout) => ({ layout });
343
380
  * ```
344
381
  */
345
382
  function defineParameters(param, ...rest) {
346
- return rest.reduce((acc, param$1) => Object.assign(acc, param$1), param);
383
+ return rest.reduce((acc, param) => Object.assign(acc, param), param);
347
384
  }
348
385
 
349
386
  //#endregion
@@ -8,7 +8,7 @@ type TagBadgeParameter = TagBadgeParameters[0];
8
8
  /**
9
9
  * Type representing the names of predefined tags used in Storybook stories.
10
10
  */
11
- type TagNames = 'editor' | 'new' | 'beta' | 'props' | 'deprecated' | 'outdated' | 'danger' | 'todo' | 'code-only' | 'snapshot' | 'unit' | 'integration' | 'keyboard' | 'internal' | 'usecase';
11
+ type TagNames = 'editor' | 'new' | 'beta' | 'props' | 'deprecated' | 'outdated' | 'danger' | 'todo' | 'code-only' | 'snapshot' | 'unit' | 'integration' | 'keyboard' | 'internal' | 'usecase' | 'version:next';
12
12
  /**
13
13
  * Configuration for story tag badges that appear in the Storybook sidebar.
14
14
  * Each badge is associated with a specific tag and displays an emoji with a tooltip.
@@ -7,7 +7,6 @@ import { CSSProperties } from "@just-web/css";
7
7
  import { DecoratorFunction } from "storybook/internal/types";
8
8
 
9
9
  //#region src/storybook-dark-mode/dark_mode_docs_container.d.ts
10
-
11
10
  /**
12
11
  * Creates a `DocsContainer` for `storybook` that works with `@storybook-community/storybook-dark-mode`.
13
12
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@repobuddy/storybook",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "description": "Storybook repo buddy",
5
5
  "keywords": [
6
6
  "storybook",
@@ -50,7 +50,7 @@
50
50
  "!**/*.mdx"
51
51
  ],
52
52
  "dependencies": {
53
- "@just-web/css": "^0.7.0",
53
+ "@just-web/css": "^0.8.0",
54
54
  "@repobuddy/test": "^1.0.0",
55
55
  "class-variance-authority": "^0.7.1",
56
56
  "htmlfy": "^1.0.0",
@@ -76,7 +76,7 @@
76
76
  "storybook": "^10.1.10",
77
77
  "storybook-addon-tag-badges": "^3.0.2",
78
78
  "tailwindcss": "^4.1.17",
79
- "tsdown": "^0.18.0",
79
+ "tsdown": "^0.20.0",
80
80
  "vite": "^7.3.0",
81
81
  "vitest": "^4.0.16"
82
82
  },
package/readme.md CHANGED
@@ -135,6 +135,16 @@ export const preview: Preview = {
135
135
  }
136
136
  ```
137
137
 
138
+ ## Styles
139
+
140
+ [`@repobuddy/storybook`][`@repobuddy/storybook`] uses Tailwind CSS 4 and the prefix `rbsb:` to avoid conflicts with user styles.
141
+
142
+ To use the styles, import `@repobuddy/storybook/styles.css`:
143
+
144
+ ```ts
145
+ import '@repobuddy/storybook/styles.css'
146
+ ```
147
+
138
148
  [`@repobuddy/storybook`]: https://github.com/repobuddy/storybook
139
149
  [`storybook-addon-tag-badges`]: https://github.com/Sidnioulz/storybook-addon-tag-badges
140
150
  [`@storybook-community/storybook-dark-mode`]: https://github.com/repobuddy/@storybook-community/storybook-dark-mode
@@ -0,0 +1,70 @@
1
+ import { cva } from 'class-variance-authority'
2
+ import type { ReactNode } from 'react'
3
+ import { twJoin, twMerge } from 'tailwind-merge'
4
+
5
+ export type StoryCardProps = {
6
+ /**
7
+ * Optional title displayed as a heading in the card.
8
+ * Can be any React node (string, JSX, etc.).
9
+ */
10
+ title?: ReactNode | undefined
11
+ /**
12
+ * Visual status of the card, affecting its background color.
13
+ * - `'error'`: Red background (rbsb:bg-red-100 rbsb:dark:bg-red-900)
14
+ * - `'warn'`: Yellow background (rbsb:bg-yellow-100 rbsb:dark:bg-yellow-900)
15
+ * - `'info'`: Blue background (rbsb:bg-sky-100 rbsb:dark:bg-sky-900) - default
16
+ */
17
+ status?: 'error' | 'warn' | 'info' | undefined
18
+ /**
19
+ * Additional CSS classes or a function to compute classes.
20
+ *
21
+ * If a string is provided, it will be merged with the default classes.
22
+ * If a function is provided, it receives the card state and default className,
23
+ * and should return the final className string.
24
+ */
25
+ className?: ((state: Pick<StoryCardProps, 'status'> & { defaultClassName: string }) => string) | string | undefined
26
+ /**
27
+ * Content to display in the card body.
28
+ * Can be any React node (string, JSX, etc.).
29
+ */
30
+ children?: ReactNode | undefined
31
+ }
32
+
33
+ function storyCardTheme(state: Pick<StoryCardProps, 'status'>, className: StoryCardProps['className']) {
34
+ const defaultClassName = storyCardVariants(state)
35
+ if (!className) return defaultClassName
36
+ return twMerge(
37
+ typeof className === 'function' ? className({ ...state, defaultClassName }) : twJoin(defaultClassName, className)
38
+ )
39
+ }
40
+
41
+ const storyCardVariants = cva(
42
+ 'rbsb:flex rbsb:flex-col rbsb:gap-1 rbsb:py-3 rbsb:px-4 rbsb:rounded rbsb:text-black rbsb:dark:text-gray-100',
43
+ {
44
+ variants: {
45
+ status: {
46
+ error: 'rbsb:bg-red-100 rbsb:dark:bg-red-900',
47
+ warn: 'rbsb:bg-yellow-100 rbsb:dark:bg-yellow-900',
48
+ info: 'rbsb:bg-sky-100 rbsb:dark:bg-sky-900'
49
+ }
50
+ },
51
+ defaultVariants: {
52
+ status: 'info'
53
+ }
54
+ }
55
+ )
56
+
57
+ /**
58
+ * A card component that displays information with optional title and status styling.
59
+ *
60
+ * @param props - StoryCard component props
61
+ * @returns A section element containing the card content
62
+ */
63
+ export function StoryCard({ status, className, children, title }: StoryCardProps) {
64
+ return (
65
+ <section className={storyCardTheme({ status }, className)}>
66
+ {title && <h2 className="rbsb:text-lg rbsb:font-bold">{title}</h2>}
67
+ {children}
68
+ </section>
69
+ )
70
+ }
@@ -3,17 +3,25 @@ import { SyntaxHighlighter } from 'storybook/internal/components'
3
3
  import type { Args, DecoratorFunction, Renderer } from 'storybook/internal/csf'
4
4
  import { addons } from 'storybook/preview-api'
5
5
  import { convert, ThemeProvider, themes } from 'storybook/theming'
6
+ import { twJoin } from 'tailwind-merge'
7
+ import { StoryCard, type StoryCardProps } from '../components/story_card'
6
8
 
7
9
  const channel = addons.getChannel()
8
10
 
9
11
  /**
10
12
  * A decorator that shows the source code of a story above the rendered story.
11
13
  * The source code is taken from the story's `parameters.docs.source.code`.
14
+ *
15
+ * @param options - Options for the showDocSource decorator
16
+ * @param options.showOriginalSource - Whether to show the original source code in a card
17
+ * @param options.className - Class name to apply to the card
18
+ * @returns A decorator function that shows the source code of a story above the rendered story
12
19
  */
13
- export function showDocSource<TRenderer extends Renderer = Renderer, TArgs = Args>(): DecoratorFunction<
14
- TRenderer,
15
- TArgs
16
- > {
20
+ export function showDocSource<TRenderer extends Renderer = Renderer, TArgs = Args>(
21
+ options?: Pick<StoryCardProps, 'className'> & {
22
+ showOriginalSource?: boolean | undefined
23
+ }
24
+ ): DecoratorFunction<TRenderer, TArgs> {
17
25
  return (Story, { parameters: { docs, darkMode } }) => {
18
26
  // This is a workaround to get the current dark mode from `@storybook-community/storybook-dark-mode` without referencing it.
19
27
  const storedItem = window.localStorage.getItem('sb-addon-themes-3')
@@ -26,6 +34,16 @@ export function showDocSource<TRenderer extends Renderer = Renderer, TArgs = Arg
26
34
  return () => channel.off('DARK_MODE', setIsDark)
27
35
  }, [])
28
36
 
37
+ const code = options?.showOriginalSource
38
+ ? docs?.source?.originalSource
39
+ : (docs?.source?.code ?? docs?.source?.originalSource)
40
+
41
+ const language = code === docs?.source?.originalSource ? undefined : docs?.source?.language
42
+
43
+ const isOriginalSource = code === docs?.source?.originalSource
44
+
45
+ const content = <SyntaxHighlighter language={language}>{code}</SyntaxHighlighter>
46
+
29
47
  return (
30
48
  <ThemeProvider theme={convert(docs?.theme ?? (isDark ? themes.dark : themes.light))}>
31
49
  <section
@@ -35,8 +53,27 @@ export function showDocSource<TRenderer extends Renderer = Renderer, TArgs = Arg
35
53
  gap: '1rem'
36
54
  }}
37
55
  >
38
- <SyntaxHighlighter language={docs?.source?.language}>{docs?.source?.code}</SyntaxHighlighter>
39
56
  <Story />
57
+ <StoryCard
58
+ className={(state) => {
59
+ const modifiedState = {
60
+ ...state,
61
+ defaultClassName: twJoin(
62
+ state.defaultClassName,
63
+ isOriginalSource
64
+ ? 'rbsb:bg-transparent rbsb:dark:bg-transparent'
65
+ : 'rbsb:bg-gray-100 rbsb:dark:bg-gray-900'
66
+ )
67
+ }
68
+
69
+ const className = options?.className
70
+ return typeof className === 'function'
71
+ ? className(modifiedState)
72
+ : twJoin(modifiedState.defaultClassName, className)
73
+ }}
74
+ >
75
+ {content}
76
+ </StoryCard>
40
77
  </section>
41
78
  </ThemeProvider>
42
79
  )
@@ -1,4 +1,3 @@
1
- import { cva } from 'class-variance-authority'
2
1
  import {
3
2
  createContext,
4
3
  useContext,
@@ -10,23 +9,11 @@ import {
10
9
  type ReactNode
11
10
  } from 'react'
12
11
  import type { DecoratorFunction, Renderer } from 'storybook/internal/csf'
13
- import { twMerge } from 'tailwind-merge'
14
12
  import type { RequiredPick } from 'type-plus'
13
+ import { StoryCard, type StoryCardProps } from '../components/story_card.js'
15
14
  import { generateKey } from '../utils/generate_key.js'
16
15
 
17
- export type StoryCardProps = {
18
- /**
19
- * Optional title displayed as a heading in the card.
20
- * Can be any React node (string, JSX, etc.).
21
- */
22
- title?: ReactNode | undefined
23
- /**
24
- * Visual status of the card, affecting its background color.
25
- * - `'error'`: Red background (bg-red-100 dark:bg-red-900)
26
- * - `'warn'`: Yellow background (bg-yellow-100 dark:bg-yellow-900)
27
- * - `'info'`: Blue background (bg-sky-100 dark:bg-sky-900) - default
28
- */
29
- status?: 'error' | 'warn' | 'info' | undefined
16
+ export type WithStoryCardProps = Omit<StoryCardProps, 'children' | 'className'> & {
30
17
  /**
31
18
  * Additional CSS classes or a function to compute classes.
32
19
  *
@@ -34,10 +21,7 @@ export type StoryCardProps = {
34
21
  * If a function is provided, it receives the card state and default className,
35
22
  * and should return the final className string.
36
23
  */
37
- className?:
38
- | ((state: Required<Pick<StoryCardProps, 'status'>> & { defaultClassName: string }) => string)
39
- | string
40
- | undefined
24
+ className?: ((state: Pick<StoryCardProps, 'status'> & { defaultClassName: string }) => string) | string | undefined
41
25
  /**
42
26
  * Content to display in the card body.
43
27
  * Can be any React node (string, JSX, etc.).
@@ -120,7 +104,7 @@ export function withStoryCard<TRenderer extends Renderer = Renderer>({
120
104
  status = 'info',
121
105
  content: contentProp,
122
106
  ...rest
123
- }: StoryCardProps = {}): DecoratorFunction<TRenderer> {
107
+ }: WithStoryCardProps = {}): DecoratorFunction<TRenderer> {
124
108
  return (Story, { parameters, viewMode }) => {
125
109
  if (viewMode === 'docs') return <Story />
126
110
 
@@ -131,7 +115,7 @@ export function withStoryCard<TRenderer extends Renderer = Renderer>({
131
115
  }
132
116
  }
133
117
 
134
- interface StoryCardContainerWrapperProps extends RequiredPick<StoryCardProps, 'status'> {
118
+ interface StoryCardContainerWrapperProps extends RequiredPick<WithStoryCardProps, 'status'> {
135
119
  Story: ComponentType
136
120
  }
137
121
 
@@ -165,12 +149,11 @@ function StoryCardContainer({ children }: { children: ReactNode }) {
165
149
 
166
150
  return (
167
151
  <StoryCardContext.Provider value={contextValue}>
168
- <div className="flex flex-col gap-2">
169
- {cards.map(({ key, status, className, content, title }) => (
170
- <section key={key} className={storyCardTheme({ status }, className)}>
171
- {title && <h2 className="text-lg font-bold">{title}</h2>}
152
+ <div className="rbsb:flex rbsb:flex-col rbsb:gap-2">
153
+ {cards.map(({ content, key, ...rest }) => (
154
+ <StoryCard key={key} {...rest}>
172
155
  {content}
173
- </section>
156
+ </StoryCard>
174
157
  ))}
175
158
  {children}
176
159
  </div>
@@ -178,27 +161,9 @@ function StoryCardContainer({ children }: { children: ReactNode }) {
178
161
  )
179
162
  }
180
163
 
181
- type StoryCardWithKey = RequiredPick<StoryCardProps, 'status'> & { key: string }
182
-
183
- function storyCardTheme(state: Required<Pick<StoryCardProps, 'status'>>, className: StoryCardProps['className']) {
184
- const defaultClassName = storyCardVariants(state)
185
- if (!className) return defaultClassName
186
- return typeof className === 'function'
187
- ? className({ ...state, defaultClassName })
188
- : twMerge(defaultClassName, className)
189
- }
190
-
191
- const storyCardVariants = cva('flex flex-col gap-1 py-3 px-4 rounded text-black dark:text-gray-100', {
192
- variants: {
193
- status: {
194
- error: 'bg-red-100 dark:bg-red-900',
195
- warn: 'bg-yellow-100 dark:bg-yellow-900',
196
- info: 'bg-sky-100 dark:bg-sky-900'
197
- }
198
- }
199
- })
164
+ type StoryCardWithKey = RequiredPick<WithStoryCardProps, 'status'> & { key: string }
200
165
 
201
- interface StoryCardCollectorProps extends RequiredPick<StoryCardProps, 'status'> {
166
+ interface StoryCardCollectorProps extends RequiredPick<WithStoryCardProps, 'status'> {
202
167
  Story: ComponentType
203
168
  }
204
169
 
@@ -226,7 +191,7 @@ function StoryCardCollector({ Story, title, status, className, content }: StoryC
226
191
  }
227
192
 
228
193
  interface StoryCardContextValue {
229
- addCard: (card: RequiredPick<StoryCardProps, 'status'>) => string
194
+ addCard: (card: RequiredPick<WithStoryCardProps, 'status'>) => string
230
195
  removeCard: (id: string) => void
231
196
  }
232
197
 
@@ -23,6 +23,7 @@ export type TagNames =
23
23
  | 'keyboard'
24
24
  | 'internal'
25
25
  | 'usecase'
26
+ | 'version:next'
26
27
 
27
28
  /**
28
29
  * Configuration for story tag badges that appear in the Storybook sidebar.
package/styles.css CHANGED
@@ -3,243 +3,221 @@
3
3
  @layer theme, base, components, utilities;
4
4
  @layer theme {
5
5
  :root, :host {
6
- --color-red-100: oklch(93.6% 0.032 17.717);
7
- --color-red-800: oklch(44.4% 0.177 26.899);
8
- --color-red-900: oklch(39.6% 0.141 25.723);
9
- --color-amber-300: oklch(87.9% 0.169 91.605);
10
- --color-amber-900: oklch(41.4% 0.112 45.904);
11
- --color-yellow-100: oklch(97.3% 0.071 103.193);
12
- --color-yellow-900: oklch(42.1% 0.095 57.708);
13
- --color-green-200: oklch(92.5% 0.084 155.995);
14
- --color-green-800: oklch(44.8% 0.119 151.328);
15
- --color-sky-100: oklch(95.1% 0.026 236.824);
16
- --color-sky-500: oklch(68.5% 0.169 237.323);
17
- --color-sky-900: oklch(39.1% 0.09 240.876);
18
- --color-blue-500: oklch(62.3% 0.214 259.815);
19
- --color-rose-400: oklch(71.2% 0.194 13.428);
20
- --color-rose-900: oklch(41% 0.159 10.272);
21
- --color-gray-100: oklch(96.7% 0.003 264.542);
22
- --color-gray-500: oklch(55.1% 0.027 264.364);
23
- --color-black: #000;
24
- --color-white: #fff;
25
- --spacing: 0.25rem;
26
- --text-lg: 1.125rem;
27
- --text-lg--line-height: calc(1.75 / 1.125);
28
- --font-weight-extralight: 200;
29
- --font-weight-bold: 700;
30
- --font-weight-extrabold: 800;
6
+ --rbsb-color-red-100: oklch(93.6% 0.032 17.717);
7
+ --rbsb-color-red-800: oklch(44.4% 0.177 26.899);
8
+ --rbsb-color-red-900: oklch(39.6% 0.141 25.723);
9
+ --rbsb-color-amber-300: oklch(87.9% 0.169 91.605);
10
+ --rbsb-color-amber-900: oklch(41.4% 0.112 45.904);
11
+ --rbsb-color-yellow-100: oklch(97.3% 0.071 103.193);
12
+ --rbsb-color-yellow-900: oklch(42.1% 0.095 57.708);
13
+ --rbsb-color-green-200: oklch(92.5% 0.084 155.995);
14
+ --rbsb-color-green-800: oklch(44.8% 0.119 151.328);
15
+ --rbsb-color-sky-100: oklch(95.1% 0.026 236.824);
16
+ --rbsb-color-sky-500: oklch(68.5% 0.169 237.323);
17
+ --rbsb-color-sky-900: oklch(39.1% 0.09 240.876);
18
+ --rbsb-color-blue-200: oklch(88.2% 0.059 254.128);
19
+ --rbsb-color-blue-500: oklch(62.3% 0.214 259.815);
20
+ --rbsb-color-blue-800: oklch(42.4% 0.199 265.638);
21
+ --rbsb-color-blue-900: oklch(37.9% 0.146 265.522);
22
+ --rbsb-color-purple-500: oklch(62.7% 0.265 303.9);
23
+ --rbsb-color-rose-400: oklch(71.2% 0.194 13.428);
24
+ --rbsb-color-rose-900: oklch(41% 0.159 10.272);
25
+ --rbsb-color-gray-100: oklch(96.7% 0.003 264.542);
26
+ --rbsb-color-gray-500: oklch(55.1% 0.027 264.364);
27
+ --rbsb-color-gray-900: oklch(21% 0.034 264.665);
28
+ --rbsb-color-black: #000;
29
+ --rbsb-color-white: #fff;
30
+ --rbsb-spacing: 0.25rem;
31
+ --rbsb-text-lg: 1.125rem;
32
+ --rbsb-text-lg--line-height: calc(1.75 / 1.125);
33
+ --rbsb-font-weight-extralight: 200;
34
+ --rbsb-font-weight-bold: 700;
35
+ --rbsb-font-weight-extrabold: 800;
36
+ --rbsb-radius-lg: 0.5rem;
31
37
  }
32
38
  }
33
39
  @layer utilities {
34
- .static {
35
- position: static;
36
- }
37
- .container {
38
- width: 100%;
39
- @media (width >= 40rem) {
40
- max-width: 40rem;
41
- }
42
- @media (width >= 48rem) {
43
- max-width: 48rem;
44
- }
45
- @media (width >= 64rem) {
46
- max-width: 64rem;
47
- }
48
- @media (width >= 80rem) {
49
- max-width: 80rem;
50
- }
51
- @media (width >= 96rem) {
52
- max-width: 96rem;
53
- }
54
- }
55
- .flex {
40
+ .rbsb\:flex {
56
41
  display: flex;
57
42
  }
58
- .hidden {
59
- display: none;
60
- }
61
- .inline {
62
- display: inline;
63
- }
64
- .transform {
65
- transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
66
- }
67
- .flex-col {
43
+ .rbsb\:flex-col {
68
44
  flex-direction: column;
69
45
  }
70
- .gap-1 {
71
- gap: calc(var(--spacing) * 1);
46
+ .rbsb\:gap-1 {
47
+ gap: calc(var(--rbsb-spacing) * 1);
72
48
  }
73
- .gap-2 {
74
- gap: calc(var(--spacing) * 2);
49
+ .rbsb\:gap-2 {
50
+ gap: calc(var(--rbsb-spacing) * 2);
75
51
  }
76
- .gap-4 {
77
- gap: calc(var(--spacing) * 4);
52
+ .rbsb\:gap-4 {
53
+ gap: calc(var(--rbsb-spacing) * 4);
78
54
  }
79
- .rounded {
55
+ .rbsb\:rounded {
80
56
  border-radius: 0.25rem;
81
57
  }
82
- .border {
83
- border-style: var(--tw-border-style);
84
- border-width: 1px;
58
+ .rbsb\:rounded-lg {
59
+ border-radius: var(--rbsb-radius-lg);
85
60
  }
86
- .border-2 {
61
+ .rbsb\:border-2 {
87
62
  border-style: var(--tw-border-style);
88
63
  border-width: 2px;
89
64
  }
90
- .border-blue-500 {
91
- border-color: var(--color-blue-500);
65
+ .rbsb\:border-blue-500 {
66
+ border-color: var(--rbsb-color-blue-500);
67
+ }
68
+ .rbsb\:border-purple-500 {
69
+ border-color: var(--rbsb-color-purple-500);
92
70
  }
93
- .bg-amber-300 {
94
- background-color: var(--color-amber-300);
71
+ .rbsb\:bg-amber-300 {
72
+ background-color: var(--rbsb-color-amber-300);
95
73
  }
96
- .bg-black {
97
- background-color: var(--color-black);
74
+ .rbsb\:bg-black {
75
+ background-color: var(--rbsb-color-black);
98
76
  }
99
- .bg-gray-100 {
100
- background-color: var(--color-gray-100);
77
+ .rbsb\:bg-blue-500 {
78
+ background-color: var(--rbsb-color-blue-500);
101
79
  }
102
- .bg-green-200 {
103
- background-color: var(--color-green-200);
80
+ .rbsb\:bg-gray-100 {
81
+ background-color: var(--rbsb-color-gray-100);
104
82
  }
105
- .bg-green-800 {
106
- background-color: var(--color-green-800);
83
+ .rbsb\:bg-green-200 {
84
+ background-color: var(--rbsb-color-green-200);
107
85
  }
108
- .bg-red-100 {
109
- background-color: var(--color-red-100);
86
+ .rbsb\:bg-green-800 {
87
+ background-color: var(--rbsb-color-green-800);
110
88
  }
111
- .bg-red-800 {
112
- background-color: var(--color-red-800);
89
+ .rbsb\:bg-red-100 {
90
+ background-color: var(--rbsb-color-red-100);
113
91
  }
114
- .bg-rose-400 {
115
- background-color: var(--color-rose-400);
92
+ .rbsb\:bg-red-800 {
93
+ background-color: var(--rbsb-color-red-800);
116
94
  }
117
- .bg-sky-100 {
118
- background-color: var(--color-sky-100);
95
+ .rbsb\:bg-rose-400 {
96
+ background-color: var(--rbsb-color-rose-400);
119
97
  }
120
- .bg-sky-500 {
121
- background-color: var(--color-sky-500);
98
+ .rbsb\:bg-sky-100 {
99
+ background-color: var(--rbsb-color-sky-100);
122
100
  }
123
- .bg-white {
124
- background-color: var(--color-white);
101
+ .rbsb\:bg-sky-500 {
102
+ background-color: var(--rbsb-color-sky-500);
125
103
  }
126
- .bg-yellow-100 {
127
- background-color: var(--color-yellow-100);
104
+ .rbsb\:bg-transparent {
105
+ background-color: transparent;
128
106
  }
129
- .p-2 {
130
- padding: calc(var(--spacing) * 2);
107
+ .rbsb\:bg-yellow-100 {
108
+ background-color: var(--rbsb-color-yellow-100);
131
109
  }
132
- .p-4 {
133
- padding: calc(var(--spacing) * 4);
110
+ .rbsb\:p-2 {
111
+ padding: calc(var(--rbsb-spacing) * 2);
134
112
  }
135
- .px-4 {
136
- padding-inline: calc(var(--spacing) * 4);
113
+ .rbsb\:p-4 {
114
+ padding: calc(var(--rbsb-spacing) * 4);
137
115
  }
138
- .py-3 {
139
- padding-block: calc(var(--spacing) * 3);
116
+ .rbsb\:px-4 {
117
+ padding-inline: calc(var(--rbsb-spacing) * 4);
140
118
  }
141
- .text-lg {
142
- font-size: var(--text-lg);
143
- line-height: var(--tw-leading, var(--text-lg--line-height));
119
+ .rbsb\:py-3 {
120
+ padding-block: calc(var(--rbsb-spacing) * 3);
144
121
  }
145
- .font-bold {
146
- --tw-font-weight: var(--font-weight-bold);
147
- font-weight: var(--font-weight-bold);
122
+ .rbsb\:text-lg {
123
+ font-size: var(--rbsb-text-lg);
124
+ line-height: var(--tw-leading, var(--rbsb-text-lg--line-height));
148
125
  }
149
- .font-extrabold {
150
- --tw-font-weight: var(--font-weight-extrabold);
151
- font-weight: var(--font-weight-extrabold);
126
+ .rbsb\:font-bold {
127
+ --tw-font-weight: var(--rbsb-font-weight-bold);
128
+ font-weight: var(--rbsb-font-weight-bold);
152
129
  }
153
- .font-extralight {
154
- --tw-font-weight: var(--font-weight-extralight);
155
- font-weight: var(--font-weight-extralight);
130
+ .rbsb\:font-extrabold {
131
+ --tw-font-weight: var(--rbsb-font-weight-extrabold);
132
+ font-weight: var(--rbsb-font-weight-extrabold);
156
133
  }
157
- .text-black {
158
- color: var(--color-black);
134
+ .rbsb\:font-extralight {
135
+ --tw-font-weight: var(--rbsb-font-weight-extralight);
136
+ font-weight: var(--rbsb-font-weight-extralight);
159
137
  }
160
- .text-white {
161
- color: var(--color-white);
138
+ .rbsb\:text-black {
139
+ color: var(--rbsb-color-black);
162
140
  }
163
- .shadow-lg {
141
+ .rbsb\:text-white {
142
+ color: var(--rbsb-color-white);
143
+ }
144
+ .rbsb\:shadow-lg {
164
145
  --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
165
146
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
166
147
  }
167
- .dark\:bg-amber-900 {
168
- &:where(.dark, .dark *) {
169
- background-color: var(--color-amber-900);
148
+ .rbsb\:ring-2 {
149
+ --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
150
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
151
+ }
152
+ .rbsb\:ring-blue-200 {
153
+ --tw-ring-color: var(--rbsb-color-blue-200);
154
+ }
155
+ .rbsb\:dark\:bg-amber-900 {
156
+ @media (prefers-color-scheme: dark) {
157
+ background-color: var(--rbsb-color-amber-900);
170
158
  }
171
159
  }
172
- .dark\:bg-black {
173
- &:where(.dark, .dark *) {
174
- background-color: var(--color-black);
160
+ .rbsb\:dark\:bg-blue-900 {
161
+ @media (prefers-color-scheme: dark) {
162
+ background-color: var(--rbsb-color-blue-900);
175
163
  }
176
164
  }
177
- .dark\:bg-gray-500 {
178
- &:where(.dark, .dark *) {
179
- background-color: var(--color-gray-500);
165
+ .rbsb\:dark\:bg-gray-500 {
166
+ @media (prefers-color-scheme: dark) {
167
+ background-color: var(--rbsb-color-gray-500);
180
168
  }
181
169
  }
182
- .dark\:bg-green-800 {
183
- &:where(.dark, .dark *) {
184
- background-color: var(--color-green-800);
170
+ .rbsb\:dark\:bg-gray-900 {
171
+ @media (prefers-color-scheme: dark) {
172
+ background-color: var(--rbsb-color-gray-900);
185
173
  }
186
174
  }
187
- .dark\:bg-red-900 {
188
- &:where(.dark, .dark *) {
189
- background-color: var(--color-red-900);
175
+ .rbsb\:dark\:bg-green-800 {
176
+ @media (prefers-color-scheme: dark) {
177
+ background-color: var(--rbsb-color-green-800);
190
178
  }
191
179
  }
192
- .dark\:bg-rose-900 {
193
- &:where(.dark, .dark *) {
194
- background-color: var(--color-rose-900);
180
+ .rbsb\:dark\:bg-red-900 {
181
+ @media (prefers-color-scheme: dark) {
182
+ background-color: var(--rbsb-color-red-900);
195
183
  }
196
184
  }
197
- .dark\:bg-sky-900 {
198
- &:where(.dark, .dark *) {
199
- background-color: var(--color-sky-900);
185
+ .rbsb\:dark\:bg-rose-900 {
186
+ @media (prefers-color-scheme: dark) {
187
+ background-color: var(--rbsb-color-rose-900);
200
188
  }
201
189
  }
202
- .dark\:bg-yellow-900 {
203
- &:where(.dark, .dark *) {
204
- background-color: var(--color-yellow-900);
190
+ .rbsb\:dark\:bg-sky-900 {
191
+ @media (prefers-color-scheme: dark) {
192
+ background-color: var(--rbsb-color-sky-900);
205
193
  }
206
194
  }
207
- .dark\:font-extrabold {
208
- &:where(.dark, .dark *) {
209
- --tw-font-weight: var(--font-weight-extrabold);
210
- font-weight: var(--font-weight-extrabold);
195
+ .rbsb\:dark\:bg-transparent {
196
+ @media (prefers-color-scheme: dark) {
197
+ background-color: transparent;
211
198
  }
212
199
  }
213
- .dark\:text-gray-100 {
214
- &:where(.dark, .dark *) {
215
- color: var(--color-gray-100);
200
+ .rbsb\:dark\:bg-yellow-900 {
201
+ @media (prefers-color-scheme: dark) {
202
+ background-color: var(--rbsb-color-yellow-900);
216
203
  }
217
204
  }
218
- .dark\:text-white {
219
- &:where(.dark, .dark *) {
220
- color: var(--color-white);
205
+ .rbsb\:dark\:font-extrabold {
206
+ @media (prefers-color-scheme: dark) {
207
+ --tw-font-weight: var(--rbsb-font-weight-extrabold);
208
+ font-weight: var(--rbsb-font-weight-extrabold);
209
+ }
210
+ }
211
+ .rbsb\:dark\:text-gray-100 {
212
+ @media (prefers-color-scheme: dark) {
213
+ color: var(--rbsb-color-gray-100);
214
+ }
215
+ }
216
+ .rbsb\:dark\:ring-blue-800 {
217
+ @media (prefers-color-scheme: dark) {
218
+ --tw-ring-color: var(--rbsb-color-blue-800);
221
219
  }
222
220
  }
223
- }
224
- @property --tw-rotate-x {
225
- syntax: "*";
226
- inherits: false;
227
- }
228
- @property --tw-rotate-y {
229
- syntax: "*";
230
- inherits: false;
231
- }
232
- @property --tw-rotate-z {
233
- syntax: "*";
234
- inherits: false;
235
- }
236
- @property --tw-skew-x {
237
- syntax: "*";
238
- inherits: false;
239
- }
240
- @property --tw-skew-y {
241
- syntax: "*";
242
- inherits: false;
243
221
  }
244
222
  @property --tw-border-style {
245
223
  syntax: "*";
@@ -318,11 +296,6 @@
318
296
  @layer properties {
319
297
  @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
320
298
  *, ::before, ::after, ::backdrop {
321
- --tw-rotate-x: initial;
322
- --tw-rotate-y: initial;
323
- --tw-rotate-z: initial;
324
- --tw-skew-x: initial;
325
- --tw-skew-y: initial;
326
299
  --tw-border-style: solid;
327
300
  --tw-font-weight: initial;
328
301
  --tw-shadow: 0 0 #0000;