From b4cdec3fd963885880f8f46ed2e94c38ccbf6f1a Mon Sep 17 00:00:00 2001 From: MichaelArestad Date: Tue, 23 Sep 2025 14:25:31 -0600 Subject: [PATCH 01/25] accidentally created a token structure while adding dark status colors --- .../src/components/components/Badge/Badge.tsx | 34 ++++------ .../components/sidebar/SearchResults.tsx | 9 +-- .../src/manager/components/sidebar/Tree.tsx | 7 +- code/core/src/manager/globals/exports.ts | 1 + code/core/src/manager/utils/status.tsx | 48 +++++++------ code/core/src/theming/base.ts | 67 ++++++++++++++++++- code/core/src/theming/convert.ts | 10 ++- code/core/src/theming/create.ts | 13 ++-- code/core/src/theming/themes/dark.ts | 34 +++++----- code/core/src/theming/themes/light.ts | 34 +++++----- code/core/src/theming/types.ts | 7 +- 11 files changed, 172 insertions(+), 92 deletions(-) diff --git a/code/core/src/components/components/Badge/Badge.tsx b/code/core/src/components/components/Badge/Badge.tsx index 038af02e8897..c971b2babff0 100644 --- a/code/core/src/components/components/Badge/Badge.tsx +++ b/code/core/src/components/components/Badge/Badge.tsx @@ -31,45 +31,37 @@ const BadgeWrapper = styled.div( switch (status) { case 'critical': { return { - color: theme.color.critical, - background: theme.background.critical, + color: theme.fgColor.critical, + background: theme.bgColor.critical, + boxShadow: `inset 0 0 0 1px ${theme.borderColor.critical}`, }; } case 'negative': { return { - color: theme.color.negativeText, - background: theme.background.negative, - boxShadow: - theme.base === 'light' - ? `inset 0 0 0 1px ${transparentize(0.9, theme.color.negativeText)}` - : 'none', + color: theme.fgColor.negative, + background: theme.bgColor.negative, + boxShadow: `inset 0 0 0 1px ${theme.borderColor.negative}`, }; } case 'warning': { return { - color: theme.color.warningText, - background: theme.background.warning, - boxShadow: - theme.base === 'light' - ? `inset 0 0 0 1px ${transparentize(0.9, theme.color.warningText)}` - : 'none', + color: theme.fgColor.warning, + background: theme.bgColor.warning, + boxShadow: `inset 0 0 0 1px ${theme.borderColor.warning}`, }; } case 'neutral': { return { - color: theme.textMutedColor, + color: theme.fgColor.muted, background: theme.base === 'light' ? theme.background.app : theme.barBg, boxShadow: `inset 0 0 0 1px ${transparentize(0.8, theme.textMutedColor)}`, }; } case 'positive': { return { - color: theme.color.positiveText, - background: theme.background.positive, - boxShadow: - theme.base === 'light' - ? `inset 0 0 0 1px ${transparentize(0.9, theme.color.positiveText)}` - : 'none', + color: theme.fgColor.positive, + background: theme.bgColor.positive, + boxShadow: `inset 0 0 0 1px ${theme.borderColor.positive}`, }; } case 'active': { diff --git a/code/core/src/manager/components/sidebar/SearchResults.tsx b/code/core/src/manager/components/sidebar/SearchResults.tsx index f3ce89bf8af0..442ac742fa26 100644 --- a/code/core/src/manager/components/sidebar/SearchResults.tsx +++ b/code/core/src/manager/components/sidebar/SearchResults.tsx @@ -10,10 +10,10 @@ import { TrashIcon } from '@storybook/icons'; import type { ControllerStateAndHelpers } from 'downshift'; import { transparentize } from 'polished'; import { useStorybookApi } from 'storybook/manager-api'; -import { styled } from 'storybook/theming'; +import { styled, useTheme } from 'storybook/theming'; import { matchesKeyCode, matchesModifiers } from '../../keybinding'; -import { statusMapping } from '../../utils/status'; +import { getStatus } from '../../utils/status'; import { UseSymbol } from './IconSymbols'; import { StatusLabel } from './StatusButton'; import { TypeIcon } from './TreeNode'; @@ -170,6 +170,7 @@ const Result: FC< isHighlighted: boolean; } & React.DetailedHTMLProps, HTMLLIElement> > = React.memo(function Result({ item, matches, onClick, ...props }) { + const theme = useTheme(); const click: MouseEventHandler = useCallback( (event) => { event.preventDefault(); @@ -183,12 +184,12 @@ const Result: FC< if (api && props.isHighlighted && item.type === 'component') { api.emit(PRELOAD_ENTRIES, { ids: [item.children[0]] }, { options: { target: item.refId } }); } - }, [props.isHighlighted, item]); + }, [api, props.isHighlighted, item]); const nameMatch = matches.find((match: Match) => match.key === 'name'); const pathMatches = matches.filter((match: Match) => match.key === 'path'); - const [icon] = item.status ? statusMapping[item.status] : []; + const [icon] = item.status ? getStatus(theme, item.status) : []; return ( diff --git a/code/core/src/manager/components/sidebar/Tree.tsx b/code/core/src/manager/components/sidebar/Tree.tsx index 9169274c1cad..a6cf5b55b0b6 100644 --- a/code/core/src/manager/components/sidebar/Tree.tsx +++ b/code/core/src/manager/components/sidebar/Tree.tsx @@ -34,7 +34,7 @@ import { styled, useTheme } from 'storybook/theming'; import type { Link } from '../../../components/components/tooltip/TooltipLinkList'; import { MEDIA_DESKTOP_BREAKPOINT } from '../../constants'; -import { getGroupStatus, getMostCriticalStatusValue, statusMapping } from '../../utils/status'; +import { getGroupStatus, getMostCriticalStatusValue, getStatus } from '../../utils/status'; import { createId, getAncestorIds, @@ -226,6 +226,7 @@ const Node = React.memo(function Node({ onSelectStoryId, api, }) { + const theme = useTheme(); const { isDesktop, isMobile, setMobileMenuOpen } = useLayout(); const { counts, statusesByValue } = useStatusSummary(item); @@ -299,7 +300,7 @@ const Node = React.memo(function Node({ const statusValue = getMostCriticalStatusValue( Object.values(statuses || {}).map((s) => s.value) ); - const [icon, textColor] = statusMapping[statusValue]; + const [icon, textColor] = getStatus(theme, statusValue); return ( (function Node({ if (item.type === 'component' || item.type === 'group') { const itemStatus = groupStatus?.[item.id]; - const color = itemStatus ? statusMapping[itemStatus][1] : null; + const color = itemStatus ? getStatus(theme, itemStatus)[1] : null; const BranchNode = item.type === 'component' ? ComponentNode : GroupNode; return ( diff --git a/code/core/src/manager/globals/exports.ts b/code/core/src/manager/globals/exports.ts index 5c1b64126639..fd82ad10593c 100644 --- a/code/core/src/manager/globals/exports.ts +++ b/code/core/src/manager/globals/exports.ts @@ -378,6 +378,7 @@ export default { 'lighten', 'styled', 'themes', + 'tokens', 'typography', 'useTheme', 'withTheme', diff --git a/code/core/src/manager/utils/status.tsx b/code/core/src/manager/utils/status.tsx index 03be3475d52b..8bb0fa29c229 100644 --- a/code/core/src/manager/utils/status.tsx +++ b/code/core/src/manager/utils/status.tsx @@ -6,7 +6,7 @@ import { type API_HashEntry, type StatusesByStoryIdAndTypeId } from 'storybook/i import { CircleIcon } from '@storybook/icons'; -import { styled } from 'storybook/theming'; +import { type Theme, styled } from 'storybook/theming'; import { UseSymbol } from '../components/sidebar/IconSymbols'; import { getDescendantIds } from './tree'; @@ -31,27 +31,31 @@ export const statusPriority: StatusValue[] = [ 'status-value:warning', 'status-value:error', ]; -export const statusMapping: Record = { - ['status-value:unknown']: [null, null], - ['status-value:pending']: [, 'currentColor'], - ['status-value:success']: [ - - - , - 'currentColor', - ], - ['status-value:warning']: [ - - - , - '#A15C20', - ], - ['status-value:error']: [ - - - , - '#D43900', - ], + +export const getStatus = (theme: Theme, status: StatusValue) => { + const statusMapping: Record = { + ['status-value:unknown']: [null, null], + ['status-value:pending']: [, 'currentColor'], + ['status-value:success']: [ + + + , + 'currentColor', + ], + ['status-value:warning']: [ + + + , + theme.fgColor.warning, + ], + ['status-value:error']: [ + + + , + theme.fgColor.negative, + ], + }; + return statusMapping[status]; }; export const getMostCriticalStatusValue = (statusValues: StatusValue[]): StatusValue => { diff --git a/code/core/src/theming/base.ts b/code/core/src/theming/base.ts index 475cfdee659f..ea67409f1c97 100644 --- a/code/core/src/theming/base.ts +++ b/code/core/src/theming/base.ts @@ -27,7 +27,7 @@ export const color = { darkest: '#2E3338', // For borders - border: 'hsla(212, 50%, 30%, 0.15)', + border: 'hsl(212 50% 30% / 0.15)', // Status positive: '#66BF3C', @@ -102,3 +102,68 @@ export const typography = { code: 90, }, }; + +export const tokens = { + light: { + fgColor: { + default: color.darkest, + muted: color.dark, + accent: color.secondary, + inverse: color.lightest, + // TODO: add 'disabled' + positive: '#427C27', + warning: '#955B1E', + negative: '#C23400', + critical: '#FFFFFF', + }, + bgColor: { + default: color.lightest, + muted: background.app, + // TODO: add 'accent'? white or blue? + positive: '#F1FFEB', + warning: '#FFF9EB', + negative: '#FFF0EB', + critical: '#D13800', + }, + borderColor: { + default: color.border, + muted: 'hsl(0 0% 0% / 0.1)', + inverse: 'hsl(0 0% 100% / 0.1)', + positive: '#BFE7AC', + warning: '#F3D491', + negative: '#FFC3AD', + critical: 'hsl(16 100% 100% / 0)', + }, + }, + dark: { + fgColor: { + default: '#C9CCCF', + muted: '#95999D', + accent: '#479DFF', + inverse: '#1B1C1D', + // TODO: add 'disabled' + positive: '#86CE64', + warning: '#EBB747', + negative: '#FF6933', + critical: '#FF6933', + }, + bgColor: { + default: '#222325', + muted: '#1B1C1D', + // TODO: add 'accent'? white or blue? + positive: 'hsl(101 100% 100% / 0)', + warning: 'hsl(101 100% 100% / 0)', + negative: 'hsl(101 100% 100% / 0)', + critical: 'hsl(101 100% 100% / 0)', + }, + borderColor: { + default: 'hsl(0 0% 100% / 0.1)', + muted: 'hsl(0 0% 100% / 0.5)', + inverse: 'hsl(0 0% 0% / 0.1)', + positive: 'hsl(101 52% 64% / 0.15)', + warning: 'hsl(41 67% 64% / 0.15)', + negative: 'hsl(16 100% 64% / 0.15)', + critical: '#FF6933', + }, + }, +}; diff --git a/code/core/src/theming/convert.ts b/code/core/src/theming/convert.ts index 8c72490ecd6c..a4300ae733ca 100644 --- a/code/core/src/theming/convert.ts +++ b/code/core/src/theming/convert.ts @@ -1,7 +1,7 @@ import { opacify } from 'polished'; import { animation, easing } from './animation'; -import { background, color, typography } from './base'; +import { background, color, tokens, typography } from './base'; import { themes } from './create'; import { chromeDark, chromeLight, create as createSyntax } from './modules/syntax'; import type { Color, StorybookTheme, ThemeVars, ThemeVarsColors } from './types'; @@ -113,6 +113,9 @@ export const convert = (inherit: ThemeVars = themes[getPreferredColorScheme()]): ...rest, base, + + ...(base === 'dark' ? tokens.dark : tokens.light), + color: createColors(inherit), background: { app: appBg, @@ -126,6 +129,7 @@ export const convert = (inherit: ThemeVars = themes[getPreferredColorScheme()]): warning: background.warning, critical: background.critical, }, + typography: { fonts: { base: fontBase, @@ -174,14 +178,14 @@ export const convert = (inherit: ThemeVars = themes[getPreferredColorScheme()]): }, code: createSyntax({ - colors: base === 'light' ? lightSyntaxColors : darkSyntaxColors, + colors: base === 'dark' ? darkSyntaxColors : lightSyntaxColors, mono: fontCode, }), // Addon actions theme // API example https://github.com/storybookjs/react-inspector/blob/master/src/styles/themes/chromeLight.tsx addonActionsTheme: { - ...(base === 'light' ? chromeLight : chromeDark), + ...(base === 'dark' ? chromeDark : chromeLight), BASE_FONT_FAMILY: fontCode, BASE_FONT_SIZE: typography.size.s2 - 1, diff --git a/code/core/src/theming/create.ts b/code/core/src/theming/create.ts index b5f97a34aee5..bf06c7a4448b 100644 --- a/code/core/src/theming/create.ts +++ b/code/core/src/theming/create.ts @@ -11,24 +11,27 @@ export const themes: { light: ThemeVars; dark: ThemeVars; normal: ThemeVars } = }; interface Rest { - [key: string]: any; + [key: string]: unknown; } const preferredColorScheme = getPreferredColorScheme(); export const create = ( - vars: ThemeVarsPartial = { base: preferredColorScheme }, + vars: ThemeVarsPartial = { + base: preferredColorScheme, + }, rest?: Rest ): ThemeVars => { + const base = themes[vars.base] ? vars.base : preferredColorScheme; const inherit: ThemeVars = { ...themes[preferredColorScheme], - ...(themes[vars.base] || {}), + ...themes[vars.base], ...vars, - ...{ base: themes[vars.base] ? vars.base : preferredColorScheme }, + base, }; return { ...rest, ...inherit, - ...{ barSelectedColor: vars.barSelectedColor || inherit.colorSecondary }, + barSelectedColor: vars.barSelectedColor || inherit.colorSecondary, }; }; diff --git a/code/core/src/theming/themes/dark.ts b/code/core/src/theming/themes/dark.ts index 5f8140081bff..2926e34736bc 100644 --- a/code/core/src/theming/themes/dark.ts +++ b/code/core/src/theming/themes/dark.ts @@ -1,18 +1,20 @@ -import { color, typography } from '../base'; +import { color, tokens, typography } from '../base'; import type { ThemeVars } from '../types'; +const { fgColor, bgColor, borderColor } = tokens.dark; + const theme: ThemeVars = { base: 'dark', // Storybook-specific color palette colorPrimary: '#FF4785', // coral - colorSecondary: '#479DFF', + colorSecondary: fgColor.accent, // UI - appBg: '#1B1C1D', - appContentBg: '#222325', + appBg: bgColor.muted, + appContentBg: bgColor.default, appPreviewBg: color.lightest, - appBorderColor: 'rgba(255,255,255,.1)', + appBorderColor: borderColor.default, appBorderRadius: 4, // Fonts @@ -20,24 +22,24 @@ const theme: ThemeVars = { fontCode: typography.fonts.mono, // Text colors - textColor: '#C9CCCF', - textInverseColor: '#1B1C1D', - textMutedColor: '#95999D', + textColor: fgColor.default, + textInverseColor: fgColor.inverse, + textMutedColor: fgColor.muted, // Toolbar default and active colors - barTextColor: '#95999D', + barTextColor: fgColor.muted, barHoverColor: '#70B3FF', barSelectedColor: '#479DFF', - barBg: '#222325', + barBg: bgColor.default, // Form colors - buttonBg: '#1B1C1D', - buttonBorder: 'hsl(0 0 100 / 0.1)', - booleanBg: '#1B1C1D', + buttonBg: bgColor.muted, + buttonBorder: borderColor.default, + booleanBg: bgColor.muted, booleanSelectedBg: '#292B2E', - inputBg: '#1B1C1D', - inputBorder: 'hsl(0 0 100 / 0.1)', - inputTextColor: '#C9CCCF', + inputBg: bgColor.muted, + inputBorder: borderColor.default, + inputTextColor: fgColor.default, inputBorderRadius: 4, }; diff --git a/code/core/src/theming/themes/light.ts b/code/core/src/theming/themes/light.ts index 43621f050161..24ec43dbb844 100644 --- a/code/core/src/theming/themes/light.ts +++ b/code/core/src/theming/themes/light.ts @@ -1,18 +1,20 @@ -import { background, color, typography } from '../base'; +import { background, color, tokens, typography } from '../base'; import type { ThemeVars } from '../types'; +const { fgColor, bgColor, borderColor } = tokens.light; + const theme: ThemeVars = { base: 'light', // Storybook-specific color palette colorPrimary: color.primary, - colorSecondary: color.secondary, + colorSecondary: fgColor.accent, // UI - appBg: background.app, - appContentBg: color.lightest, - appPreviewBg: color.lightest, - appBorderColor: color.border, + appBg: bgColor.muted, + appContentBg: bgColor.default, + appPreviewBg: bgColor.default, + appBorderColor: borderColor.default, appBorderRadius: 4, // Fonts @@ -20,24 +22,24 @@ const theme: ThemeVars = { fontCode: typography.fonts.mono, // Text colors - textColor: color.darkest, - textInverseColor: color.lightest, - textMutedColor: color.dark, + textColor: fgColor.default, + textInverseColor: fgColor.inverse, + textMutedColor: fgColor.muted, // Toolbar default and active colors - barTextColor: color.dark, + barTextColor: fgColor.muted, barHoverColor: '#005CC7', barSelectedColor: '#0063D6', - barBg: color.lightest, + barBg: bgColor.default, // Form colors - buttonBg: background.app, + buttonBg: bgColor.muted, buttonBorder: color.medium, booleanBg: color.mediumlight, - booleanSelectedBg: color.lightest, - inputBg: color.lightest, - inputBorder: color.border, - inputTextColor: color.darkest, + booleanSelectedBg: bgColor.default, + inputBg: bgColor.default, + inputBorder: borderColor.default, + inputTextColor: fgColor.default, inputBorderRadius: 4, }; diff --git a/code/core/src/theming/types.ts b/code/core/src/theming/types.ts index 1f386bfebd8a..75dcf76cba7f 100644 --- a/code/core/src/theming/types.ts +++ b/code/core/src/theming/types.ts @@ -1,5 +1,5 @@ import type { animation, easing } from './animation'; -import type { background, color, typography } from './base'; +import type { background, color, tokens, typography } from './base'; export interface ThemeVars extends ThemeVarsBase, ThemeVarsColors {} @@ -69,6 +69,11 @@ export interface Brand { export interface StorybookTheme { color: Color; + + fgColor: typeof tokens.light.fgColor; + bgColor: typeof tokens.light.bgColor; + borderColor: typeof tokens.light.borderColor; + background: Background; typography: Typography; animation: Animation; From 818d81fca634bc46a8421acd396b64e29f60833d Mon Sep 17 00:00:00 2001 From: ritoban23 Date: Wed, 1 Oct 2025 11:34:07 +0530 Subject: [PATCH 02/25] fix: make search clear button keyboard accessible --- code/core/src/manager/components/sidebar/Search.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/code/core/src/manager/components/sidebar/Search.tsx b/code/core/src/manager/components/sidebar/Search.tsx index a6113815b4e6..ce04a05ed644 100644 --- a/code/core/src/manager/components/sidebar/Search.tsx +++ b/code/core/src/manager/components/sidebar/Search.tsx @@ -334,6 +334,7 @@ export const Search = React.memo(function Search({ getMenuProps, getRootProps, highlightedIndex, + reset, }) => { const input = inputValue ? inputValue.trim() : ''; let results: DownshiftItem[] = input ? getResults(input) : []; @@ -409,8 +410,14 @@ export const Search = React.memo(function Search({ )} - {isOpen && ( - clearSelection()}> + {input && ( + { + reset({ inputValue: '' }); + closeMenu(); + }} + > )} From 5c94f69bd369c4efeaaa8ef9c429741e9f0b75cf Mon Sep 17 00:00:00 2001 From: MichaelArestad Date: Mon, 17 Nov 2025 14:10:50 -0700 Subject: [PATCH 03/25] Increase contrast of checkbox, radio, and range borders to meet 3:1 requirement --- .../addons/docs/src/blocks/controls/Range.tsx | 220 ++++++++---------- .../components/components/Form/Checkbox.tsx | 19 +- .../src/components/components/Form/Radio.tsx | 19 +- 3 files changed, 122 insertions(+), 136 deletions(-) diff --git a/code/addons/docs/src/blocks/controls/Range.tsx b/code/addons/docs/src/blocks/controls/Range.tsx index 5baea1088edb..28f0149cdc21 100644 --- a/code/addons/docs/src/blocks/controls/Range.tsx +++ b/code/addons/docs/src/blocks/controls/Range.tsx @@ -11,148 +11,128 @@ import type { ControlProps, NumberValue, RangeConfig } from './types'; type RangeProps = ControlProps & RangeConfig; const RangeInput = styled.input<{ min: number; max: number; value: number }>( - ({ theme, min, max, value, disabled }) => ({ - // Resytled using http://danielstern.ca/range.css/#/ - '&': { - width: '100%', - backgroundColor: 'transparent', - appearance: 'none', - }, - - '&::-webkit-slider-runnable-track': { - background: - theme.base === 'light' - ? `linear-gradient(to right, - ${theme.color.green} 0%, ${theme.color.green} ${((value - min) / (max - min)) * 100}%, - ${darken(0.02, theme.input.background)} ${((value - min) / (max - min)) * 100}%, - ${darken(0.02, theme.input.background)} 100%)` - : `linear-gradient(to right, - ${theme.color.green} 0%, ${theme.color.green} ${((value - min) / (max - min)) * 100}%, - ${lighten(0.02, theme.input.background)} ${((value - min) / (max - min)) * 100}%, - ${lighten(0.02, theme.input.background)} 100%)`, - boxShadow: `${theme.appBorderColor} 0 0 0 1px inset`, + ({ theme, min, max, value, disabled }) => { + // Shared track background gradient + const trackBackground = + theme.base === 'light' + ? `linear-gradient(to right, + ${theme.color.green} 0%, ${theme.color.green} ${((value - min) / (max - min)) * 100}%, + ${darken(0.02, theme.input.background)} ${((value - min) / (max - min)) * 100}%, + ${darken(0.02, theme.input.background)} 100%)` + : `linear-gradient(to right, + ${theme.color.green} 0%, ${theme.color.green} ${((value - min) / (max - min)) * 100}%, + ${lighten(0.02, theme.input.background)} ${((value - min) / (max - min)) * 100}%, + ${lighten(0.02, theme.input.background)} 100%)`; + + // Shared track base styles + const trackBaseStyles = { + background: trackBackground, borderRadius: 6, - width: '100%', - height: 6, + boxShadow: `${theme.base == 'dark' ? 'hsl(0 0 100 / 0.4)' : 'hsl(0 0 0 / 0.44)'} 0 0 0 1px inset`, cursor: disabled ? 'not-allowed' : 'pointer', - }, + height: 6, + width: '100%', + }; + + const trackFocusStyles = { + borderColor: rgba(theme.color.secondary, 0.4), + }; - '&::-webkit-slider-thumb': { - marginTop: '-6px', + // Shared thumb base styles + const thumbBaseStyles = { width: 16, height: 16, - - border: `1px solid ${theme.appBorderColor}`, - borderRadius: '50px', - boxShadow: - theme.base === 'light' ? `0 1px 3px 0px ${rgba(theme.appBorderColor, 0.2)}` : 'unset', + borderRadius: 50, cursor: disabled ? 'not-allowed' : 'grab', - appearance: 'none', background: theme.input.background, + border: `1px solid ${theme.base == 'dark' ? 'hsl(0 0 100 / 0.4)' : 'hsl(0 0 0 / 0.44)'}`, + boxShadow: + theme.base === 'light' ? `0 1px 3px 0px ${rgba(theme.appBorderColor, 0.2)}` : 'unset', transition: 'all 150ms ease-out', + }; + + // Shared thumb hover styles + const thumbHoverStyles = { + background: `${darken(0.05, theme.input.background)}`, + transform: 'scale3d(1.1, 1.1, 1.1) translateY(-1px)', + transition: 'all 50ms ease-out', + }; + + // Shared thumb active styles + const thumbActiveStyles = { + background: `${theme.input.background}`, + transform: 'scale3d(1, 1, 1) translateY(0px)', + }; + + const thumbFocusStyles = { + borderColor: theme.color.secondary, + boxShadow: theme.base === 'light' ? `0 0px 5px 0px ${theme.color.secondary}` : 'unset', + }; + + return { + // Restyled using http://danielstern.ca/range.css/#/ + appearance: 'none', + backgroundColor: 'transparent', + width: '100%', - '&:hover': { - background: `${darken(0.05, theme.input.background)}`, - transform: 'scale3d(1.1, 1.1, 1.1) translateY(-1px)', - transition: 'all 50ms ease-out', - }, + // Track styles + '&::-webkit-slider-runnable-track': trackBaseStyles, + + '&::-moz-range-track': trackBaseStyles, - '&:active': { - background: `${theme.input.background}`, - transform: 'scale3d(1, 1, 1) translateY(0px)', - cursor: disabled ? 'not-allowed' : 'grab', + '&::-ms-track': { + ...trackBaseStyles, + color: 'transparent', }, - }, - '&:focus': { - outline: 'none', + // Thumb styles + '&::-moz-range-thumb': { + ...thumbBaseStyles, - '&::-webkit-slider-runnable-track': { - borderColor: rgba(theme.color.secondary, 0.4), + '&:hover': thumbHoverStyles, + '&:active': thumbActiveStyles, }, '&::-webkit-slider-thumb': { - borderColor: theme.color.secondary, - boxShadow: theme.base === 'light' ? `0 0px 5px 0px ${theme.color.secondary}` : 'unset', + ...thumbBaseStyles, + marginTop: '-6px', + appearance: 'none', + + '&:hover': thumbHoverStyles, + '&:active': thumbActiveStyles, }, - }, - - '&::-moz-range-track': { - background: - theme.base === 'light' - ? `linear-gradient(to right, - ${theme.color.green} 0%, ${theme.color.green} ${((value - min) / (max - min)) * 100}%, - ${darken(0.02, theme.input.background)} ${((value - min) / (max - min)) * 100}%, - ${darken(0.02, theme.input.background)} 100%)` - : `linear-gradient(to right, - ${theme.color.green} 0%, ${theme.color.green} ${((value - min) / (max - min)) * 100}%, - ${lighten(0.02, theme.input.background)} ${((value - min) / (max - min)) * 100}%, - ${lighten(0.02, theme.input.background)} 100%)`, - boxShadow: `${theme.appBorderColor} 0 0 0 1px inset`, - borderRadius: 6, - width: '100%', - height: 6, - cursor: disabled ? 'not-allowed' : 'pointer', - outline: 'none', - }, - '&::-moz-range-thumb': { - width: 16, - height: 16, - border: `1px solid ${theme.appBorderColor}`, - borderRadius: '50px', - boxShadow: - theme.base === 'light' ? `0 1px 3px 0px ${rgba(theme.appBorderColor, 0.2)}` : 'unset', - cursor: disabled ? 'not-allowed' : 'grab', - background: theme.input.background, - transition: 'all 150ms ease-out', + '&::-ms-thumb': { + ...thumbBaseStyles, + marginTop: 0, - '&:hover': { - background: `${darken(0.05, theme.input.background)}`, - transform: 'scale3d(1.1, 1.1, 1.1) translateY(-1px)', - transition: 'all 50ms ease-out', + '&:hover': thumbHoverStyles, + '&:active': thumbActiveStyles, }, - '&:active': { - background: `${theme.input.background}`, - transform: 'scale3d(1, 1, 1) translateY(0px)', - cursor: 'grabbing', + '&:focus': { + outline: 'none', + + '&::-webkit-slider-runnable-track': trackFocusStyles, + '&::-moz-range-track': trackFocusStyles, + '&::-ms-track': trackFocusStyles, + + '&::-webkit-slider-thumb': thumbFocusStyles, + '&::-moz-range-thumb': thumbFocusStyles, + '&::-ms-thumb': thumbFocusStyles, }, - }, - '&::-ms-track': { - background: - theme.base === 'light' - ? `linear-gradient(to right, - ${theme.color.green} 0%, ${theme.color.green} ${((value - min) / (max - min)) * 100}%, - ${darken(0.02, theme.input.background)} ${((value - min) / (max - min)) * 100}%, - ${darken(0.02, theme.input.background)} 100%)` - : `linear-gradient(to right, - ${theme.color.green} 0%, ${theme.color.green} ${((value - min) / (max - min)) * 100}%, - ${lighten(0.02, theme.input.background)} ${((value - min) / (max - min)) * 100}%, - ${lighten(0.02, theme.input.background)} 100%)`, - boxShadow: theme.base === 'light' ? `${theme.appBorderColor} 0 0 0 1px inset` : 'unset', - color: 'transparent', - width: '100%', - height: '6px', - cursor: 'pointer', - }, - '&::-ms-fill-lower': { - borderRadius: 6, - }, - '&::-ms-fill-upper': { - borderRadius: 6, - }, - '&::-ms-thumb': { - width: 16, - height: 16, - background: theme.input.background, - border: `1px solid ${rgba(theme.appBorderColor, 0.2)}`, - borderRadius: 50, - cursor: disabled ? 'not-allowed' : 'grab', - marginTop: 0, - }, - '@supports (-ms-ime-align:auto)': { 'input[type=range]': { margin: '0' } }, - }) + + '&::-ms-fill-lower': { + borderRadius: 6, + }, + + '&::-ms-fill-upper': { + borderRadius: 6, + }, + + '@supports (-ms-ime-align:auto)': { 'input[type=range]': { margin: '0' } }, + }; + } ); const RangeLabel = styled.span({ diff --git a/code/core/src/components/components/Form/Checkbox.tsx b/code/core/src/components/components/Form/Checkbox.tsx index aadbd979e9f9..4cc2df6b4725 100644 --- a/code/core/src/components/components/Form/Checkbox.tsx +++ b/code/core/src/components/components/Form/Checkbox.tsx @@ -4,27 +4,30 @@ import { color, styled } from 'storybook/theming'; const Input = styled.input(({ theme }) => ({ appearance: 'none', + backgroundColor: theme.input.background, + border: `1px solid ${theme.base == 'dark' ? 'hsl(0 0 100 / 0.4)' : 'hsl(0 0 0 / 0.44)'}`, + //border: `1px solid ${theme.input.border}`, + borderRadius: 2, display: 'grid', - placeContent: 'center', - width: 14, - height: 14, flexShrink: 0, + height: 14, margin: 0, - border: `1px solid ${theme.input.border}`, - borderRadius: 2, - backgroundColor: theme.input.background, + placeContent: 'center', transition: 'background-color 0.1s', + width: 14, '&:enabled': { cursor: 'pointer', }, '&:disabled': { - backgroundColor: theme.base === 'light' ? color.light : 'transparent', + backgroundColor: 'transparent', + borderColor: theme.input.border, }, '&:disabled:checked, &:disabled:indeterminate': { - backgroundColor: theme.base === 'light' ? color.mediumdark : theme.color.dark, + backgroundColor: theme.base === 'dark' ? color.dark : theme.color.mediumdark, }, '&:checked, &:indeterminate': { + border: 'none', backgroundColor: color.secondary, }, '&:checked::before': { diff --git a/code/core/src/components/components/Form/Radio.tsx b/code/core/src/components/components/Form/Radio.tsx index 2ea6e9cd7310..ceec9cfbd35a 100644 --- a/code/core/src/components/components/Form/Radio.tsx +++ b/code/core/src/components/components/Form/Radio.tsx @@ -4,28 +4,31 @@ import { color, styled } from 'storybook/theming'; const Input = styled.input(({ theme }) => ({ appearance: 'none', + backgroundColor: theme.input.background, + border: `1px solid ${theme.base == 'dark' ? 'hsl(0 0 100 / 0.4)' : 'hsl(0 0 0 / 0.44)'}`, + borderRadius: 8, display: 'grid', - placeContent: 'center', - width: 16, - height: 16, flexShrink: 0, + height: 16, margin: -1, - border: `1px solid ${theme.input.border}`, - borderRadius: 8, - backgroundColor: theme.input.background, + placeContent: 'center', transition: 'background-color 0.1s', + width: 16, '&:enabled': { cursor: 'pointer', }, '&:disabled': { - backgroundColor: theme.base === 'light' ? color.light : 'transparent', + backgroundColor: 'transparent', + borderColor: theme.input.border, }, '&:disabled:checked': { - backgroundColor: theme.base === 'light' ? color.light : theme.color.mediumdark, + backgroundColor: theme.base === 'dark' ? color.dark : theme.color.mediumdark, + borderColor: theme.base === 'dark' ? color.dark : theme.color.mediumdark, }, '&:checked': { backgroundColor: color.secondary, + borderColor: color.secondary, boxShadow: `inset 0 0 0 2px ${theme.input.background}`, }, '&:enabled:focus-visible': { From a0e1c795e5904b30f9621004ff86e87659b4e1dd Mon Sep 17 00:00:00 2001 From: MichaelArestad Date: Mon, 17 Nov 2025 15:06:11 -0700 Subject: [PATCH 04/25] attempt to fix bigs --- code/core/src/manager/components/sidebar/Tree.tsx | 4 ++-- code/core/src/theming/themes/dark.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/core/src/manager/components/sidebar/Tree.tsx b/code/core/src/manager/components/sidebar/Tree.tsx index 4fd13e91e3fe..bdf5baeba09f 100644 --- a/code/core/src/manager/components/sidebar/Tree.tsx +++ b/code/core/src/manager/components/sidebar/Tree.tsx @@ -393,7 +393,7 @@ const Node = React.memo(function Node(props) { } const itemStatus = getMostCriticalStatusValue(Object.values(statuses || {}).map((s) => s.value)); - const [itemIcon, itemColor] = statusMapping[itemStatus]; + const [itemIcon, itemColor] = getStatus(theme, itemStatus); const itemStatusButton = itemIcon ? ( (function Node(props) { const { children = [] } = item; const BranchNode = { component: ComponentNode, group: GroupNode, story: StoryNode }[item.type]; const status = getMostCriticalStatusValue([itemStatus, groupStatus?.[item.id]]); - const color = status ? statusMapping[status][1] : null; + const color = status ? getStatus(theme, status)[1] : null; const showBranchStatus = status === 'status-value:error' || status === 'status-value:warning'; return ( diff --git a/code/core/src/theming/themes/dark.ts b/code/core/src/theming/themes/dark.ts index 9752e985a4bb..6d75424fef6c 100644 --- a/code/core/src/theming/themes/dark.ts +++ b/code/core/src/theming/themes/dark.ts @@ -13,7 +13,7 @@ const theme: ThemeVars = { // UI appBg: bgColor.muted, appContentBg: bgColor.default, - appHoverBg: '#70B3FF'. + appHoverBg: '#70B3FF', appPreviewBg: color.lightest, appBorderColor: borderColor.default, appBorderRadius: 4, From 703b32ca7d1d8ab48d1eddfe5752e81eb6c191f3 Mon Sep 17 00:00:00 2001 From: MichaelArestad Date: Mon, 17 Nov 2025 15:25:56 -0700 Subject: [PATCH 05/25] small tweaks --- code/core/src/components/components/Badge/Badge.tsx | 2 +- code/core/src/theming/themes/dark.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/core/src/components/components/Badge/Badge.tsx b/code/core/src/components/components/Badge/Badge.tsx index c971b2babff0..b6ffe656dded 100644 --- a/code/core/src/components/components/Badge/Badge.tsx +++ b/code/core/src/components/components/Badge/Badge.tsx @@ -53,7 +53,7 @@ const BadgeWrapper = styled.div( case 'neutral': { return { color: theme.fgColor.muted, - background: theme.base === 'light' ? theme.background.app : theme.barBg, + background: theme.base === 'dark' ? theme.barBg : theme.background.app, boxShadow: `inset 0 0 0 1px ${transparentize(0.8, theme.textMutedColor)}`, }; } diff --git a/code/core/src/theming/themes/dark.ts b/code/core/src/theming/themes/dark.ts index 6d75424fef6c..c436b26a90c5 100644 --- a/code/core/src/theming/themes/dark.ts +++ b/code/core/src/theming/themes/dark.ts @@ -13,7 +13,7 @@ const theme: ThemeVars = { // UI appBg: bgColor.muted, appContentBg: bgColor.default, - appHoverBg: '#70B3FF', + appHoverBg: '#233952', appPreviewBg: color.lightest, appBorderColor: borderColor.default, appBorderRadius: 4, From 7dd9f924db1e9408fd13d73483d5802afc1e120a Mon Sep 17 00:00:00 2001 From: MichaelArestad Date: Mon, 17 Nov 2025 15:55:17 -0700 Subject: [PATCH 06/25] fixed type errors hopefully --- code/core/src/manager/components/sidebar/Tree.tsx | 8 ++++++-- code/core/src/theming/base.ts | 10 +++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/code/core/src/manager/components/sidebar/Tree.tsx b/code/core/src/manager/components/sidebar/Tree.tsx index bdf5baeba09f..df743f996dd6 100644 --- a/code/core/src/manager/components/sidebar/Tree.tsx +++ b/code/core/src/manager/components/sidebar/Tree.tsx @@ -288,7 +288,12 @@ const Node = React.memo(function Node(props) { ? useContextMenu(item, statusLinks, api) : { node: null, onMouseEnter: () => {} }; - if (item.type === 'story' || item.type === 'docs') { + if ( + (item.type === 'story' && + !('children' in item && item.children) && + (!('subtype' in item) || item.subtype !== 'test')) || + item.type === 'docs' + ) { const LeafNode = item.type === 'docs' ? DocumentNode : StoryNode; const statusValue = getMostCriticalStatusValue( @@ -520,7 +525,6 @@ const Node = React.memo(function Node(props) { setMobileMenuOpen(false); } }} - {...(item.type === 'docs' && { docsMode })} > {(item.renderLabel as (i: typeof item, api: API) => React.ReactNode)?.(item, api) || item.name} diff --git a/code/core/src/theming/base.ts b/code/core/src/theming/base.ts index 94a79caf03b4..676cb3f94707 100644 --- a/code/core/src/theming/base.ts +++ b/code/core/src/theming/base.ts @@ -110,7 +110,7 @@ export const tokens = { inverse: color.lightest, // TODO: add 'disabled' positive: '#427C27', - warning: '#955B1E', + warning: '#7A4100', negative: '#C23400', critical: '#FFFFFF', }, @@ -119,7 +119,7 @@ export const tokens = { muted: background.app, // TODO: add 'accent'? white or blue? positive: '#F1FFEB', - warning: '#FFF9EB', + warning: '#FFF7EB', negative: '#FFF0EB', critical: '#D13800', }, @@ -128,7 +128,7 @@ export const tokens = { muted: 'hsl(0 0% 0% / 0.1)', inverse: 'hsl(0 0% 100% / 0.1)', positive: '#BFE7AC', - warning: '#F3D491', + warning: '#FFCE85', negative: '#FFC3AD', critical: 'hsl(16 100% 100% / 0)', }, @@ -141,7 +141,7 @@ export const tokens = { inverse: '#1B1C1D', // TODO: add 'disabled' positive: '#86CE64', - warning: '#EBB747', + warning: '#FFAD33', negative: '#FF6933', critical: '#FF6933', }, @@ -159,7 +159,7 @@ export const tokens = { muted: 'hsl(0 0% 100% / 0.5)', inverse: 'hsl(0 0% 0% / 0.1)', positive: 'hsl(101 52% 64% / 0.15)', - warning: 'hsl(41 67% 64% / 0.15)', + warning: 'hsl(36 100% 64% / 0.15)', negative: 'hsl(16 100% 64% / 0.15)', critical: '#FF6933', }, From 9702ffb51e8f2c31c139ac09ad9125159c04ea61 Mon Sep 17 00:00:00 2001 From: MichaelArestad Date: Tue, 18 Nov 2025 10:39:54 -0700 Subject: [PATCH 07/25] Reduce scope of changes a little --- code/core/src/theming/themes/dark.ts | 34 +++++++++++++-------------- code/core/src/theming/themes/light.ts | 34 +++++++++++++-------------- 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/code/core/src/theming/themes/dark.ts b/code/core/src/theming/themes/dark.ts index c436b26a90c5..e2aa337335d0 100644 --- a/code/core/src/theming/themes/dark.ts +++ b/code/core/src/theming/themes/dark.ts @@ -1,21 +1,19 @@ -import { color, tokens, typography } from '../base'; +import { color, typography } from '../base'; import type { ThemeVars } from '../types'; -const { fgColor, bgColor, borderColor } = tokens.dark; - const theme: ThemeVars = { base: 'dark', // Storybook-specific color palette colorPrimary: '#FF4785', // coral - colorSecondary: fgColor.accent, + colorSecondary: '#479DFF', // UI - appBg: bgColor.muted, - appContentBg: bgColor.default, + appBg: '#1B1C1D', + appContentBg: '#222325', appHoverBg: '#233952', appPreviewBg: color.lightest, - appBorderColor: borderColor.default, + appBorderColor: 'hsl(0 0% 100% / 0.1)', appBorderRadius: 4, // Fonts @@ -23,24 +21,24 @@ const theme: ThemeVars = { fontCode: typography.fonts.mono, // Text colors - textColor: fgColor.default, - textInverseColor: fgColor.inverse, - textMutedColor: fgColor.muted, + textColor: '#C9CCCF', + textInverseColor: '#1B1C1D', + textMutedColor: '#95999D', // Toolbar default and active colors - barTextColor: fgColor.muted, + barTextColor: '#95999D', barHoverColor: '#70B3FF', barSelectedColor: '#479DFF', - barBg: bgColor.default, + barBg: '#222325', // Form colors - buttonBg: bgColor.muted, - buttonBorder: borderColor.default, - booleanBg: bgColor.muted, + buttonBg: '#1B1C1D', + buttonBorder: 'hsl(0 0% 100% / 0.1)', + booleanBg: '#1B1C1D', booleanSelectedBg: '#292B2E', - inputBg: bgColor.muted, - inputBorder: borderColor.default, - inputTextColor: fgColor.default, + inputBg: '#1B1C1D', + inputBorder: 'hsl(0 0% 100% / 0.1)', + inputTextColor: '#C9CCCF', inputBorderRadius: 4, }; diff --git a/code/core/src/theming/themes/light.ts b/code/core/src/theming/themes/light.ts index c0e799c0fc9b..d896cb9deb51 100644 --- a/code/core/src/theming/themes/light.ts +++ b/code/core/src/theming/themes/light.ts @@ -1,21 +1,19 @@ -import { background, color, tokens, typography } from '../base'; +import { background, color, typography } from '../base'; import type { ThemeVars } from '../types'; -const { fgColor, bgColor, borderColor } = tokens.light; - const theme: ThemeVars = { base: 'light', // Storybook-specific color palette colorPrimary: color.primary, - colorSecondary: fgColor.accent, + colorSecondary: color.secondary, // UI - appBg: bgColor.muted, - appContentBg: bgColor.default, + appBg: background.app, + appContentBg: color.lightest, appHoverBg: '#DBECFF', - appPreviewBg: bgColor.default, - appBorderColor: borderColor.default, + appPreviewBg: color.lightest, + appBorderColor: color.border, appBorderRadius: 4, // Fonts @@ -23,24 +21,24 @@ const theme: ThemeVars = { fontCode: typography.fonts.mono, // Text colors - textColor: fgColor.default, - textInverseColor: fgColor.inverse, - textMutedColor: fgColor.muted, + textColor: color.darkest, + textInverseColor: color.lightest, + textMutedColor: color.dark, // Toolbar default and active colors - barTextColor: fgColor.muted, + barTextColor: color.dark, barHoverColor: '#005CC7', barSelectedColor: '#0063D6', - barBg: bgColor.default, + barBg: color.lightest, // Form colors - buttonBg: bgColor.muted, + buttonBg: background.app, buttonBorder: color.medium, booleanBg: color.mediumlight, - booleanSelectedBg: bgColor.default, - inputBg: bgColor.default, - inputBorder: borderColor.default, - inputTextColor: fgColor.default, + booleanSelectedBg: color.lightest, + inputBg: color.lightest, + inputBorder: color.border, + inputTextColor: color.darkest, inputBorderRadius: 4, }; From b7bcfe005d357a94d516632dd58696b76856f7b9 Mon Sep 17 00:00:00 2001 From: MichaelArestad Date: Tue, 18 Nov 2025 11:01:42 -0700 Subject: [PATCH 08/25] Update convert.test.js --- code/core/src/theming/tests/convert.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/core/src/theming/tests/convert.test.js b/code/core/src/theming/tests/convert.test.js index 6cfa924323a7..72955e843129 100644 --- a/code/core/src/theming/tests/convert.test.js +++ b/code/core/src/theming/tests/convert.test.js @@ -18,7 +18,7 @@ describe('convert', () => { expect(result).toMatchObject({ color: expect.objectContaining({ ancillary: '#22a699', - border: 'hsla(212, 50%, 30%, 0.15)', + border: 'hsl(212 50% 30% / 0.15)', critical: '#FFFFFF', dark: '#5C6570', darker: '#454C54', @@ -68,7 +68,7 @@ describe('convert', () => { expect(result).toMatchObject({ color: expect.objectContaining({ ancillary: '#22a699', - border: 'hsla(212, 50%, 30%, 0.15)', + border: 'hsl(212 50% 30% / 0.15)', critical: '#FFFFFF', dark: '#5C6570', darker: '#454C54', From d8e002660b19028ed31fd15d1693cc05a8bd3b93 Mon Sep 17 00:00:00 2001 From: Steve Dodier-Lazaro Date: Wed, 19 Nov 2025 13:19:23 +0100 Subject: [PATCH 09/25] UI: Remove unused prop --- code/core/src/manager/components/sidebar/Search.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/code/core/src/manager/components/sidebar/Search.tsx b/code/core/src/manager/components/sidebar/Search.tsx index 4f2827b34d01..b5f955b60b2b 100644 --- a/code/core/src/manager/components/sidebar/Search.tsx +++ b/code/core/src/manager/components/sidebar/Search.tsx @@ -328,7 +328,6 @@ export const Search = React.memo(function Search({ openMenu, closeMenu, inputValue, - clearSelection, getInputProps, getItemProps, getLabelProps, From 46bd4d548c4b9afb3aad2715b424a14a8430c893 Mon Sep 17 00:00:00 2001 From: Bill Collins Date: Wed, 19 Nov 2025 18:09:22 +0000 Subject: [PATCH 10/25] Remove yarn esbuild pnp plugin --- code/core/package.json | 1 - code/core/src/builder-manager/index.ts | 4 +--- code/yarn.lock | 12 ------------ 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/code/core/package.json b/code/core/package.json index 5604f58be055..0d07865ac537 100644 --- a/code/core/package.json +++ b/code/core/package.json @@ -257,7 +257,6 @@ "@types/semver": "^7.5.8", "@types/ws": "^8", "@vitest/utils": "^3.2.4", - "@yarnpkg/esbuild-plugin-pnp": "^3.0.0-rc.10", "@yarnpkg/fslib": "2.10.3", "@yarnpkg/libzip": "2.3.0", "ansi-to-html": "^0.7.2", diff --git a/code/core/src/builder-manager/index.ts b/code/core/src/builder-manager/index.ts index 7daba1650222..265b37e102d8 100644 --- a/code/core/src/builder-manager/index.ts +++ b/code/core/src/builder-manager/index.ts @@ -4,8 +4,6 @@ import { stringifyProcessEnvs } from 'storybook/internal/common'; import { logger } from 'storybook/internal/node-logger'; import { globalExternals } from '@fal-works/esbuild-plugin-global-externals'; -// TODO: Remove in SB11 -import { pnpPlugin } from '@yarnpkg/esbuild-plugin-pnp'; import { resolveModulePath } from 'exsolve'; import { join, parse } from 'pathe'; import sirv from 'sirv'; @@ -104,7 +102,7 @@ export const getConfig: ManagerBuilder['getConfig'] = async (options) => { tsconfig: tsconfigPath, legalComments: 'external', - plugins: [globalExternals(globalsModuleInfoMap), pnpPlugin()], + plugins: [globalExternals(globalsModuleInfoMap)], banner: { js: 'try{', diff --git a/code/yarn.lock b/code/yarn.lock index 8e9a3a02d44c..0f7802515671 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -10825,17 +10825,6 @@ __metadata: languageName: node linkType: hard -"@yarnpkg/esbuild-plugin-pnp@npm:^3.0.0-rc.10": - version: 3.0.0-rc.15 - resolution: "@yarnpkg/esbuild-plugin-pnp@npm:3.0.0-rc.15" - dependencies: - tslib: "npm:^2.4.0" - peerDependencies: - esbuild: ">=0.10.0" - checksum: 10c0/5095bc316862971add31ca1fadb0095b6ad15f25120f6ab3a06086bb6a7be93c2f3c45bff80d5976689fc89b0e9bf82bd3d410e205c852739874d32d050c4e57 - languageName: node - linkType: hard - "@yarnpkg/fslib@npm:2.10.3": version: 2.10.3 resolution: "@yarnpkg/fslib@npm:2.10.3" @@ -25937,7 +25926,6 @@ __metadata: "@vitest/expect": "npm:3.2.4" "@vitest/spy": "npm:3.2.4" "@vitest/utils": "npm:^3.2.4" - "@yarnpkg/esbuild-plugin-pnp": "npm:^3.0.0-rc.10" "@yarnpkg/fslib": "npm:2.10.3" "@yarnpkg/libzip": "npm:2.3.0" ansi-to-html: "npm:^0.7.2" From 1c6e4b2f815c1e92d9c2f8d1e545455b17a9dc16 Mon Sep 17 00:00:00 2001 From: Michael Arestad Date: Wed, 19 Nov 2025 11:44:30 -0700 Subject: [PATCH 11/25] Update code/core/src/theming/create.ts Co-authored-by: Steve Dodier-Lazaro --- code/core/src/theming/create.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/core/src/theming/create.ts b/code/core/src/theming/create.ts index d9b447636726..02acac976f20 100644 --- a/code/core/src/theming/create.ts +++ b/code/core/src/theming/create.ts @@ -35,7 +35,7 @@ export const create = ( const base = themes[vars.base] ? vars.base : preferredColorScheme; const inherit: ThemeVars = { ...themes[preferredColorScheme], - ...themes[vars.base], + ...(themes[vars.base] || {}), ...vars, base, }; From 258e11af5b56901b9ea9a3a42902b501d6cabed2 Mon Sep 17 00:00:00 2001 From: MichaelArestad Date: Wed, 19 Nov 2025 13:05:47 -0700 Subject: [PATCH 12/25] Fix linting error --- code/core/src/theming/create.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/core/src/theming/create.ts b/code/core/src/theming/create.ts index 02acac976f20..04d2204e1abb 100644 --- a/code/core/src/theming/create.ts +++ b/code/core/src/theming/create.ts @@ -35,7 +35,7 @@ export const create = ( const base = themes[vars.base] ? vars.base : preferredColorScheme; const inherit: ThemeVars = { ...themes[preferredColorScheme], - ...(themes[vars.base] || {}), + ...(themes[vars.base] || {}), ...vars, base, }; From c24a238e0c2dd56f1dd0a10b48941642558d817d Mon Sep 17 00:00:00 2001 From: Michael Arestad Date: Wed, 19 Nov 2025 13:07:05 -0700 Subject: [PATCH 13/25] Update code/core/src/components/components/Form/Checkbox.tsx Co-authored-by: Steve Dodier-Lazaro --- code/core/src/components/components/Form/Checkbox.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code/core/src/components/components/Form/Checkbox.tsx b/code/core/src/components/components/Form/Checkbox.tsx index 4cc2df6b4725..f298e1db5115 100644 --- a/code/core/src/components/components/Form/Checkbox.tsx +++ b/code/core/src/components/components/Form/Checkbox.tsx @@ -5,8 +5,7 @@ import { color, styled } from 'storybook/theming'; const Input = styled.input(({ theme }) => ({ appearance: 'none', backgroundColor: theme.input.background, - border: `1px solid ${theme.base == 'dark' ? 'hsl(0 0 100 / 0.4)' : 'hsl(0 0 0 / 0.44)'}`, - //border: `1px solid ${theme.input.border}`, + border: `1px solid ${theme.base === 'dark' ? 'hsl(0 0 100 / 0.4)' : 'hsl(0 0 0 / 0.44)'}`, borderRadius: 2, display: 'grid', flexShrink: 0, From 58454345fa0fac4068233044724732c851e1fd01 Mon Sep 17 00:00:00 2001 From: Michael Arestad Date: Wed, 19 Nov 2025 13:07:21 -0700 Subject: [PATCH 14/25] Update code/core/src/components/components/Form/Radio.tsx Co-authored-by: Steve Dodier-Lazaro --- code/core/src/components/components/Form/Radio.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/core/src/components/components/Form/Radio.tsx b/code/core/src/components/components/Form/Radio.tsx index ceec9cfbd35a..53a3c8759e84 100644 --- a/code/core/src/components/components/Form/Radio.tsx +++ b/code/core/src/components/components/Form/Radio.tsx @@ -5,7 +5,7 @@ import { color, styled } from 'storybook/theming'; const Input = styled.input(({ theme }) => ({ appearance: 'none', backgroundColor: theme.input.background, - border: `1px solid ${theme.base == 'dark' ? 'hsl(0 0 100 / 0.4)' : 'hsl(0 0 0 / 0.44)'}`, + border: `1px solid ${theme.base === 'dark' ? 'hsl(0 0 100 / 0.4)' : 'hsl(0 0 0 / 0.44)'}`, borderRadius: 8, display: 'grid', flexShrink: 0, From af9f16775d98422a0fc418fc0022d597956ffd96 Mon Sep 17 00:00:00 2001 From: Steve Dodier-Lazaro Date: Fri, 21 Nov 2025 10:32:58 +0100 Subject: [PATCH 15/25] style: Comment and reorganise code to avoid confusion on theme creation logic --- code/core/src/theming/create.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/code/core/src/theming/create.ts b/code/core/src/theming/create.ts index 04d2204e1abb..5167fa191818 100644 --- a/code/core/src/theming/create.ts +++ b/code/core/src/theming/create.ts @@ -32,12 +32,15 @@ export const create = ( }, rest?: Rest ): ThemeVars => { - const base = themes[vars.base] ? vars.base : preferredColorScheme; const inherit: ThemeVars = { + // We always inherit the preferred color scheme. ...themes[preferredColorScheme], + // And then the declared theme base if it exists. ...(themes[vars.base] || {}), + // And then the actual theme content. ...vars, - base, + // If no theme base was declared, we declare the preferred color scheme as the base. + base: themes[vars.base] ? vars.base : preferredColorScheme, }; return { ...rest, From 312112898c93e6199389478b537b66f3bd74e2c1 Mon Sep 17 00:00:00 2001 From: Steve Dodier-Lazaro Date: Fri, 21 Nov 2025 10:33:23 +0100 Subject: [PATCH 16/25] fix: Memoize getStatus to avoid performance drawbacks --- code/core/src/manager/utils/status.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/code/core/src/manager/utils/status.tsx b/code/core/src/manager/utils/status.tsx index 984e85e4c85e..5465353c27f8 100644 --- a/code/core/src/manager/utils/status.tsx +++ b/code/core/src/manager/utils/status.tsx @@ -32,7 +32,10 @@ export const statusPriority: StatusValue[] = [ 'status-value:error', ]; -export const getStatus = (theme: Theme, status: StatusValue) => { +// We might not want to make this a hook because it is used in the Tree after multiple returns. +// There could be scenarios where creating a story changes the type of an item (e.g. story now +// has children because it has a test child), so we could end up with rule of hooks violations. +export const getStatus = memoizerific(5)((theme: Theme, status: StatusValue) => { const statusMapping: Record = { ['status-value:unknown']: [null, null], ['status-value:pending']: [, 'currentColor'], @@ -56,7 +59,7 @@ export const getStatus = (theme: Theme, status: StatusValue) => { ], }; return statusMapping[status]; -}; +}); export const getMostCriticalStatusValue = (statusValues: StatusValue[]): StatusValue => { return statusPriority.reduce( From 7a7907743acb834cdb7bc9f157d9089df6cf743a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 08:42:56 +0000 Subject: [PATCH 17/25] Fix broken CSF open standard links in documentation Co-authored-by: Sidnioulz <5108577+Sidnioulz@users.noreply.github.com> --- docs/_snippets/addon-consume-and-update-globaltype.md | 4 ++-- docs/_snippets/main-config-builder-custom-config.md | 2 -- docs/_snippets/storybook-addon-css-example.md | 2 +- docs/_snippets/storybook-addon-toolkit-types.md | 2 +- docs/_snippets/storybook-addons-api-getchannel.md | 4 ++-- docs/_snippets/storybook-preview-use-global-type.md | 2 +- docs/api/csf/index.mdx | 2 +- docs/api/index.mdx | 2 +- docs/get-started/why-storybook.mdx | 4 ++-- 9 files changed, 11 insertions(+), 13 deletions(-) diff --git a/docs/_snippets/addon-consume-and-update-globaltype.md b/docs/_snippets/addon-consume-and-update-globaltype.md index cc594f4f099a..cf43e00b49ad 100644 --- a/docs/_snippets/addon-consume-and-update-globaltype.md +++ b/docs/_snippets/addon-consume-and-update-globaltype.md @@ -14,11 +14,11 @@ const ExampleToolbar = () => { // Function that will update the global value and trigger a UI refresh. const refreshAndUpdateGlobal = () => { // Updates Storybook global value - updateGlobals({ + (updateGlobals({ ['my-param-key']: !isActive, }), // Invokes Storybook's addon API method (with the FORCE_RE_RENDER) event to trigger a UI refresh - addons.getChannel().emit(FORCE_RE_RENDER); + addons.getChannel().emit(FORCE_RE_RENDER)); }; const toggleOutline = useCallback(() => refreshAndUpdateGlobal(), [isActive]); diff --git a/docs/_snippets/main-config-builder-custom-config.md b/docs/_snippets/main-config-builder-custom-config.md index da1b7996a571..e2f1a0838ea9 100644 --- a/docs/_snippets/main-config-builder-custom-config.md +++ b/docs/_snippets/main-config-builder-custom-config.md @@ -46,7 +46,6 @@ export default defineMain({ }, }, }); - ``` @@ -67,5 +66,4 @@ export default defineMain({ }, }, }); - ``` diff --git a/docs/_snippets/storybook-addon-css-example.md b/docs/_snippets/storybook-addon-css-example.md index 43717ddff9b7..1990f58d798d 100644 --- a/docs/_snippets/storybook-addon-css-example.md +++ b/docs/_snippets/storybook-addon-css-example.md @@ -2,7 +2,7 @@ import { dedent } from 'ts-dedent'; export default function outlineCSS(selector: string) { - return dedent/* css */ ` + return dedent /* css */ ` ${selector} body { outline: 1px solid #2980b9 !important; } diff --git a/docs/_snippets/storybook-addon-toolkit-types.md b/docs/_snippets/storybook-addon-toolkit-types.md index 5a2bb4054ce7..2898cca64c31 100644 --- a/docs/_snippets/storybook-addon-toolkit-types.md +++ b/docs/_snippets/storybook-addon-toolkit-types.md @@ -18,7 +18,7 @@ export const Tool = memo(function MyAddonSelector() { [PARAM_KEY]: !isActive, }); }, [isActive]); - + useEffect(() => { api.setAddonShortcut(ADDON_ID, { label: 'Toggle Outline', diff --git a/docs/_snippets/storybook-addons-api-getchannel.md b/docs/_snippets/storybook-addons-api-getchannel.md index 97408b651752..73add964516b 100644 --- a/docs/_snippets/storybook-addons-api-getchannel.md +++ b/docs/_snippets/storybook-addons-api-getchannel.md @@ -13,11 +13,11 @@ const ExampleToolbar = () => { // Function that will update the global value and trigger a UI refresh. const refreshAndUpdateGlobal = () => { - updateGlobals({ + (updateGlobals({ ['my-param-key']: !isActive, }), // Invokes Storybook's addon API method (with the FORCE_RE_RENDER) event to trigger a UI refresh - addons.getChannel().emit(FORCE_RE_RENDER); + addons.getChannel().emit(FORCE_RE_RENDER)); }; const toggleToolbarAddon = useCallback(() => refreshAndUpdateGlobal(), [isActive]); diff --git a/docs/_snippets/storybook-preview-use-global-type.md b/docs/_snippets/storybook-preview-use-global-type.md index b83528fddf70..d03a178280b7 100644 --- a/docs/_snippets/storybook-preview-use-global-type.md +++ b/docs/_snippets/storybook-preview-use-global-type.md @@ -8,7 +8,7 @@ const preview: Preview = { (story) => `
${story}
`, ({ globals }) => { return { myTheme: globals['theme'] }; - } + }, ), ], }; diff --git a/docs/api/csf/index.mdx b/docs/api/csf/index.mdx index 50afb12d6183..06b9d5f8f7e1 100644 --- a/docs/api/csf/index.mdx +++ b/docs/api/csf/index.mdx @@ -9,7 +9,7 @@ tab: title: CSF 3 --- -Component Story Format (CSF) is the recommended way to [write stories](../../writing-stories/index.mdx). It's an [open standard](https://github.com/ComponentDriven/csf) based on ES6 modules that is portable beyond Storybook. +Component Story Format (CSF) is the recommended way to [write stories](../../writing-stories/index.mdx). It's an [open standard](https://github.com/storybookjs/storybook/tree/next/code/core/src/csf) based on ES6 modules that is portable beyond Storybook. In CSF, stories and component metadata are defined as ES Modules. Every component story file consists of a required [default export](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export#Using_the_default_export) and one or more [named exports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export). diff --git a/docs/api/index.mdx b/docs/api/index.mdx index 5a957e12bf3a..3611ea52a271 100644 --- a/docs/api/index.mdx +++ b/docs/api/index.mdx @@ -80,7 +80,7 @@ An overview of all available API references for Storybook. Component Story Format (CSF) is the API for writing stories. It's an - open standard based on ES6 modules that + open standard based on ES6 modules that is portable beyond Storybook. diff --git a/docs/get-started/why-storybook.mdx b/docs/get-started/why-storybook.mdx index df8269db386a..7a5b0238bcb3 100644 --- a/docs/get-started/why-storybook.mdx +++ b/docs/get-started/why-storybook.mdx @@ -29,7 +29,7 @@ Storybook is packaged as a small, development-only, [workshop](https://bradfrost ### Capture UI variations as “stories” -When developing a component variation in isolation, save it as a story. [Stories](https://github.com/ComponentDriven/csf) are a declarative syntax for supplying props and mock data to simulate component variations. Each component can have multiple stories. Each story allows you to demonstrate a specific variation of that component to verify appearance and behavior. +When developing a component variation in isolation, save it as a story. [Stories](https://github.com/storybookjs/storybook/tree/next/code/core/src/csf) are a declarative syntax for supplying props and mock data to simulate component variations. Each component can have multiple stories. Each story allows you to demonstrate a specific variation of that component to verify appearance and behavior. You write stories for granular UI component variation and then use those stories in development, testing, and documentation. @@ -100,7 +100,7 @@ Storybook is compatible with your continuous integration workflow. Add it as a C ## Write stories once, reuse everywhere -Storybook is powered by [Component Story Format](https://github.com/ComponentDriven/csf), an open standard based on JavaScript ES6 modules. This enables stories to interoperate between development, testing, and design tools. Each story is exported as a JavaScript function enabling you to reuse it with other tools. No vendor lock-in. +Storybook is powered by [Component Story Format](https://github.com/storybookjs/storybook/tree/next/code/core/src/csf), an open standard based on JavaScript ES6 modules. This enables stories to interoperate between development, testing, and design tools. Each story is exported as a JavaScript function enabling you to reuse it with other tools. No vendor lock-in. Reuse stories with [Jest](https://jestjs.io/) or [Vitest](https://vitest.dev/) and [Testing Library](https://testing-library.com/) to verify interactions. Put them in [Chromatic](https://www.chromatic.com/?utm_source=storybook_website\&utm_medium=link\&utm_campaign=storybook) for visual testing. Audit story accessibility with [Axe](https://github.com/dequelabs/axe-core). Or test user flows with [Playwright](https://playwright.dev/) and [Cypress](https://www.cypress.io/). Reuse unlocks more workflows at no extra cost. From 73c2e7347aa355b148a0b55ccf23009c76b0b307 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 08:52:05 +0000 Subject: [PATCH 18/25] Revert unintended prettier changes to snippet files Co-authored-by: Sidnioulz <5108577+Sidnioulz@users.noreply.github.com> --- docs/_snippets/addon-consume-and-update-globaltype.md | 4 ++-- docs/_snippets/main-config-builder-custom-config.md | 2 ++ docs/_snippets/storybook-addon-css-example.md | 2 +- docs/_snippets/storybook-addon-toolkit-types.md | 2 +- docs/_snippets/storybook-addons-api-getchannel.md | 4 ++-- docs/_snippets/storybook-preview-use-global-type.md | 2 +- 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/_snippets/addon-consume-and-update-globaltype.md b/docs/_snippets/addon-consume-and-update-globaltype.md index cf43e00b49ad..cc594f4f099a 100644 --- a/docs/_snippets/addon-consume-and-update-globaltype.md +++ b/docs/_snippets/addon-consume-and-update-globaltype.md @@ -14,11 +14,11 @@ const ExampleToolbar = () => { // Function that will update the global value and trigger a UI refresh. const refreshAndUpdateGlobal = () => { // Updates Storybook global value - (updateGlobals({ + updateGlobals({ ['my-param-key']: !isActive, }), // Invokes Storybook's addon API method (with the FORCE_RE_RENDER) event to trigger a UI refresh - addons.getChannel().emit(FORCE_RE_RENDER)); + addons.getChannel().emit(FORCE_RE_RENDER); }; const toggleOutline = useCallback(() => refreshAndUpdateGlobal(), [isActive]); diff --git a/docs/_snippets/main-config-builder-custom-config.md b/docs/_snippets/main-config-builder-custom-config.md index e2f1a0838ea9..da1b7996a571 100644 --- a/docs/_snippets/main-config-builder-custom-config.md +++ b/docs/_snippets/main-config-builder-custom-config.md @@ -46,6 +46,7 @@ export default defineMain({ }, }, }); + ``` @@ -66,4 +67,5 @@ export default defineMain({ }, }, }); + ``` diff --git a/docs/_snippets/storybook-addon-css-example.md b/docs/_snippets/storybook-addon-css-example.md index 1990f58d798d..43717ddff9b7 100644 --- a/docs/_snippets/storybook-addon-css-example.md +++ b/docs/_snippets/storybook-addon-css-example.md @@ -2,7 +2,7 @@ import { dedent } from 'ts-dedent'; export default function outlineCSS(selector: string) { - return dedent /* css */ ` + return dedent/* css */ ` ${selector} body { outline: 1px solid #2980b9 !important; } diff --git a/docs/_snippets/storybook-addon-toolkit-types.md b/docs/_snippets/storybook-addon-toolkit-types.md index 2898cca64c31..5a2bb4054ce7 100644 --- a/docs/_snippets/storybook-addon-toolkit-types.md +++ b/docs/_snippets/storybook-addon-toolkit-types.md @@ -18,7 +18,7 @@ export const Tool = memo(function MyAddonSelector() { [PARAM_KEY]: !isActive, }); }, [isActive]); - + useEffect(() => { api.setAddonShortcut(ADDON_ID, { label: 'Toggle Outline', diff --git a/docs/_snippets/storybook-addons-api-getchannel.md b/docs/_snippets/storybook-addons-api-getchannel.md index 73add964516b..97408b651752 100644 --- a/docs/_snippets/storybook-addons-api-getchannel.md +++ b/docs/_snippets/storybook-addons-api-getchannel.md @@ -13,11 +13,11 @@ const ExampleToolbar = () => { // Function that will update the global value and trigger a UI refresh. const refreshAndUpdateGlobal = () => { - (updateGlobals({ + updateGlobals({ ['my-param-key']: !isActive, }), // Invokes Storybook's addon API method (with the FORCE_RE_RENDER) event to trigger a UI refresh - addons.getChannel().emit(FORCE_RE_RENDER)); + addons.getChannel().emit(FORCE_RE_RENDER); }; const toggleToolbarAddon = useCallback(() => refreshAndUpdateGlobal(), [isActive]); diff --git a/docs/_snippets/storybook-preview-use-global-type.md b/docs/_snippets/storybook-preview-use-global-type.md index d03a178280b7..b83528fddf70 100644 --- a/docs/_snippets/storybook-preview-use-global-type.md +++ b/docs/_snippets/storybook-preview-use-global-type.md @@ -8,7 +8,7 @@ const preview: Preview = { (story) => `
${story}
`, ({ globals }) => { return { myTheme: globals['theme'] }; - }, + } ), ], }; From c6ef3f99a896b6d0c421df00bb6724656e347c99 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 10:44:26 +0000 Subject: [PATCH 19/25] Replace GitHub links with Storybook documentation links Co-authored-by: Sidnioulz <5108577+Sidnioulz@users.noreply.github.com> --- docs/api/csf/index.mdx | 2 +- docs/api/index.mdx | 2 +- docs/get-started/why-storybook.mdx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/api/csf/index.mdx b/docs/api/csf/index.mdx index 06b9d5f8f7e1..b0a50147f1f4 100644 --- a/docs/api/csf/index.mdx +++ b/docs/api/csf/index.mdx @@ -9,7 +9,7 @@ tab: title: CSF 3 --- -Component Story Format (CSF) is the recommended way to [write stories](../../writing-stories/index.mdx). It's an [open standard](https://github.com/storybookjs/storybook/tree/next/code/core/src/csf) based on ES6 modules that is portable beyond Storybook. +Component Story Format (CSF) is the recommended way to [write stories](../../writing-stories/index.mdx). It's an open standard based on ES6 modules that is portable beyond Storybook. In CSF, stories and component metadata are defined as ES Modules. Every component story file consists of a required [default export](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export#Using_the_default_export) and one or more [named exports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export). diff --git a/docs/api/index.mdx b/docs/api/index.mdx index 3611ea52a271..7fcbe13736fe 100644 --- a/docs/api/index.mdx +++ b/docs/api/index.mdx @@ -80,7 +80,7 @@ An overview of all available API references for Storybook. Component Story Format (CSF) is the API for writing stories. It's an - open standard based on ES6 modules that + open standard based on ES6 modules that is portable beyond Storybook. diff --git a/docs/get-started/why-storybook.mdx b/docs/get-started/why-storybook.mdx index 7a5b0238bcb3..1aea8eba9366 100644 --- a/docs/get-started/why-storybook.mdx +++ b/docs/get-started/why-storybook.mdx @@ -29,7 +29,7 @@ Storybook is packaged as a small, development-only, [workshop](https://bradfrost ### Capture UI variations as “stories” -When developing a component variation in isolation, save it as a story. [Stories](https://github.com/storybookjs/storybook/tree/next/code/core/src/csf) are a declarative syntax for supplying props and mock data to simulate component variations. Each component can have multiple stories. Each story allows you to demonstrate a specific variation of that component to verify appearance and behavior. +When developing a component variation in isolation, save it as a story. [Stories](https://storybook.js.org/docs/writing-stories) are a declarative syntax for supplying props and mock data to simulate component variations. Each component can have multiple stories. Each story allows you to demonstrate a specific variation of that component to verify appearance and behavior. You write stories for granular UI component variation and then use those stories in development, testing, and documentation. @@ -100,7 +100,7 @@ Storybook is compatible with your continuous integration workflow. Add it as a C ## Write stories once, reuse everywhere -Storybook is powered by [Component Story Format](https://github.com/storybookjs/storybook/tree/next/code/core/src/csf), an open standard based on JavaScript ES6 modules. This enables stories to interoperate between development, testing, and design tools. Each story is exported as a JavaScript function enabling you to reuse it with other tools. No vendor lock-in. +Storybook is powered by [Component Story Format](https://storybook.js.org/docs/api/csf/index), an open standard based on JavaScript ES6 modules. This enables stories to interoperate between development, testing, and design tools. Each story is exported as a JavaScript function enabling you to reuse it with other tools. No vendor lock-in. Reuse stories with [Jest](https://jestjs.io/) or [Vitest](https://vitest.dev/) and [Testing Library](https://testing-library.com/) to verify interactions. Put them in [Chromatic](https://www.chromatic.com/?utm_source=storybook_website\&utm_medium=link\&utm_campaign=storybook) for visual testing. Audit story accessibility with [Axe](https://github.com/dequelabs/axe-core). Or test user flows with [Playwright](https://playwright.dev/) and [Cypress](https://www.cypress.io/). Reuse unlocks more workflows at no extra cost. From d741df9e132d21edb3a7d94474baee020e055da2 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 24 Nov 2025 14:02:03 +0000 Subject: [PATCH 20/25] CLI: Fix framework config validation path and messages --- code/core/src/common/utils/validate-config.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/core/src/common/utils/validate-config.ts b/code/core/src/common/utils/validate-config.ts index e8c3de208e73..a0b401bf1133 100644 --- a/code/core/src/common/utils/validate-config.ts +++ b/code/core/src/common/utils/validate-config.ts @@ -6,6 +6,7 @@ import { import { resolveModulePath } from 'exsolve'; +import { extractFrameworkPackageName } from '..'; import { frameworkPackages } from './get-storybook-info'; const renderers = ['html', 'preact', 'react', 'server', 'svelte', 'vue', 'vue3', 'web-components']; @@ -27,7 +28,8 @@ export function validateFrameworkName( } // If we know about the framework, we don't need to validate it - if (Object.keys(frameworkPackages).includes(frameworkName)) { + const normalizedFrameworkName = extractFrameworkPackageName(frameworkName); + if (Object.keys(frameworkPackages).includes(normalizedFrameworkName)) { return; } From fa8067d180506d861428d8d9c53c732cb23dcb69 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 16:35:45 +0000 Subject: [PATCH 21/25] Use plain anchor tags instead of links for Stories and CSF Co-authored-by: jonniebigodes <22988955+jonniebigodes@users.noreply.github.com> --- docs/get-started/why-storybook.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/get-started/why-storybook.mdx b/docs/get-started/why-storybook.mdx index 1aea8eba9366..0217f9d08dd5 100644 --- a/docs/get-started/why-storybook.mdx +++ b/docs/get-started/why-storybook.mdx @@ -29,7 +29,7 @@ Storybook is packaged as a small, development-only, [workshop](https://bradfrost ### Capture UI variations as “stories” -When developing a component variation in isolation, save it as a story. [Stories](https://storybook.js.org/docs/writing-stories) are a declarative syntax for supplying props and mock data to simulate component variations. Each component can have multiple stories. Each story allows you to demonstrate a specific variation of that component to verify appearance and behavior. +When developing a component variation in isolation, save it as a story. Stories are a declarative syntax for supplying props and mock data to simulate component variations. Each component can have multiple stories. Each story allows you to demonstrate a specific variation of that component to verify appearance and behavior. You write stories for granular UI component variation and then use those stories in development, testing, and documentation. @@ -100,7 +100,7 @@ Storybook is compatible with your continuous integration workflow. Add it as a C ## Write stories once, reuse everywhere -Storybook is powered by [Component Story Format](https://storybook.js.org/docs/api/csf/index), an open standard based on JavaScript ES6 modules. This enables stories to interoperate between development, testing, and design tools. Each story is exported as a JavaScript function enabling you to reuse it with other tools. No vendor lock-in. +Storybook is powered by Component Story Format, an open standard based on JavaScript ES6 modules. This enables stories to interoperate between development, testing, and design tools. Each story is exported as a JavaScript function enabling you to reuse it with other tools. No vendor lock-in. Reuse stories with [Jest](https://jestjs.io/) or [Vitest](https://vitest.dev/) and [Testing Library](https://testing-library.com/) to verify interactions. Put them in [Chromatic](https://www.chromatic.com/?utm_source=storybook_website\&utm_medium=link\&utm_campaign=storybook) for visual testing. Audit story accessibility with [Axe](https://github.com/dequelabs/axe-core). Or test user flows with [Playwright](https://playwright.dev/) and [Cypress](https://www.cypress.io/). Reuse unlocks more workflows at no extra cost. From 320d1351dbb3b306b40669f2e90e02cf17d5f277 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 16:46:14 +0000 Subject: [PATCH 22/25] Use markdown links with internal paths for Stories and CSF Co-authored-by: jonniebigodes <22988955+jonniebigodes@users.noreply.github.com> --- docs/get-started/why-storybook.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/get-started/why-storybook.mdx b/docs/get-started/why-storybook.mdx index 0217f9d08dd5..887d98dad48b 100644 --- a/docs/get-started/why-storybook.mdx +++ b/docs/get-started/why-storybook.mdx @@ -29,7 +29,7 @@ Storybook is packaged as a small, development-only, [workshop](https://bradfrost ### Capture UI variations as “stories” -When developing a component variation in isolation, save it as a story. Stories are a declarative syntax for supplying props and mock data to simulate component variations. Each component can have multiple stories. Each story allows you to demonstrate a specific variation of that component to verify appearance and behavior. +When developing a component variation in isolation, save it as a story. [Stories](../writing-stories/index.mdx) are a declarative syntax for supplying props and mock data to simulate component variations. Each component can have multiple stories. Each story allows you to demonstrate a specific variation of that component to verify appearance and behavior. You write stories for granular UI component variation and then use those stories in development, testing, and documentation. @@ -100,7 +100,7 @@ Storybook is compatible with your continuous integration workflow. Add it as a C ## Write stories once, reuse everywhere -Storybook is powered by Component Story Format, an open standard based on JavaScript ES6 modules. This enables stories to interoperate between development, testing, and design tools. Each story is exported as a JavaScript function enabling you to reuse it with other tools. No vendor lock-in. +Storybook is powered by [Component Story Format](../api/csf/index.mdx), an open standard based on JavaScript ES6 modules. This enables stories to interoperate between development, testing, and design tools. Each story is exported as a JavaScript function enabling you to reuse it with other tools. No vendor lock-in. Reuse stories with [Jest](https://jestjs.io/) or [Vitest](https://vitest.dev/) and [Testing Library](https://testing-library.com/) to verify interactions. Put them in [Chromatic](https://www.chromatic.com/?utm_source=storybook_website\&utm_medium=link\&utm_campaign=storybook) for visual testing. Audit story accessibility with [Axe](https://github.com/dequelabs/axe-core). Or test user flows with [Playwright](https://playwright.dev/) and [Cypress](https://www.cypress.io/). Reuse unlocks more workflows at no extra cost. From a5954cea6f3efb18c80fb44cd6e9979845856eb2 Mon Sep 17 00:00:00 2001 From: Steve Dodier-Lazaro Date: Mon, 24 Nov 2025 18:04:51 +0100 Subject: [PATCH 23/25] fix: Add missing import from GitHub suggestion --- code/core/src/manager/utils/status.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/code/core/src/manager/utils/status.tsx b/code/core/src/manager/utils/status.tsx index 5465353c27f8..7a0d1179de53 100644 --- a/code/core/src/manager/utils/status.tsx +++ b/code/core/src/manager/utils/status.tsx @@ -6,6 +6,7 @@ import { type API_HashEntry, type StatusesByStoryIdAndTypeId } from 'storybook/i import { CircleIcon } from '@storybook/icons'; +import memoizerific from 'memoizerific'; import { type Theme, styled } from 'storybook/theming'; import { UseSymbol } from '../components/sidebar/IconSymbols'; From 0430acc85ee692a8091a5020b8e1ee033f0e793f Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 24 Nov 2025 18:22:04 +0000 Subject: [PATCH 24/25] Angular: Add preset entry point for framework --- code/frameworks/angular/preset.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 code/frameworks/angular/preset.js diff --git a/code/frameworks/angular/preset.js b/code/frameworks/angular/preset.js new file mode 100644 index 000000000000..4bd63d324002 --- /dev/null +++ b/code/frameworks/angular/preset.js @@ -0,0 +1 @@ +export * from './dist/preset.js'; From c6851979a7d556a7af4a39233d74ed6c200973f2 Mon Sep 17 00:00:00 2001 From: storybook-bot <32066757+storybook-bot@users.noreply.github.com> Date: Mon, 24 Nov 2025 20:10:47 +0000 Subject: [PATCH 25/25] Write changelog for 10.1.0-beta.3 [skip ci] --- CHANGELOG.prerelease.md | 9 +++++++++ code/package.json | 3 ++- docs/versions/next.json | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md index 47b4af1e1323..fa9c9e593a0f 100644 --- a/CHANGELOG.prerelease.md +++ b/CHANGELOG.prerelease.md @@ -1,3 +1,12 @@ +## 10.1.0-beta.3 + +- A11y: Make search clear button keyboard accessible - [#32590](https://github.com/storybookjs/storybook/pull/32590), thanks @ritoban23! +- Angular: Add preset entry point for framework - [#33154](https://github.com/storybookjs/storybook/pull/33154), thanks @valentinpalkovic! +- CLI: Fix framework config validation path and messages - [#33146](https://github.com/storybookjs/storybook/pull/33146), thanks @valentinpalkovic! +- Manager: Added tokens and a dark color scheme for status colors - [#33081](https://github.com/storybookjs/storybook/pull/33081), thanks @MichaelArestad! +- Remove yarn esbuild pnp plugin - [#33097](https://github.com/storybookjs/storybook/pull/33097), thanks @mrginglymus! +- UI: Increase border contrast of Checkbox, Radio, and Range - [#33064](https://github.com/storybookjs/storybook/pull/33064), thanks @MichaelArestad! + ## 10.1.0-beta.2 - Automigration: Update description and link for addon-a11y-addon-test - [#33133](https://github.com/storybookjs/storybook/pull/33133), thanks @valentinpalkovic! diff --git a/code/package.json b/code/package.json index 2d42d1fcbeee..60b62440f25b 100644 --- a/code/package.json +++ b/code/package.json @@ -286,5 +286,6 @@ "Dependency Upgrades" ] ] - } + }, + "deferredNextVersion": "10.1.0-beta.3" } diff --git a/docs/versions/next.json b/docs/versions/next.json index eade4140dec0..0e951cd74e58 100644 --- a/docs/versions/next.json +++ b/docs/versions/next.json @@ -1 +1 @@ -{"version":"10.1.0-beta.2","info":{"plain":"- Automigration: Update description and link for addon-a11y-addon-test - [#33133](https://github.com/storybookjs/storybook/pull/33133), thanks @valentinpalkovic!\n- CLI: Fix Vitest v3 installs and refactor AddonVitestService; align create‑storybook usage - [#33131](https://github.com/storybookjs/storybook/pull/33131), thanks @valentinpalkovic!\n- CLI: Update postAction hook to use command parameter for logfile retrieval - [#33137](https://github.com/storybookjs/storybook/pull/33137), thanks @valentinpalkovic!\n- Core: Fix `getDocsUrl` for canary versions - [#33128](https://github.com/storybookjs/storybook/pull/33128), thanks @ghengeveld!"}} \ No newline at end of file +{"version":"10.1.0-beta.3","info":{"plain":"- A11y: Make search clear button keyboard accessible - [#32590](https://github.com/storybookjs/storybook/pull/32590), thanks @ritoban23!\n- Angular: Add preset entry point for framework - [#33154](https://github.com/storybookjs/storybook/pull/33154), thanks @valentinpalkovic!\n- CLI: Fix framework config validation path and messages - [#33146](https://github.com/storybookjs/storybook/pull/33146), thanks @valentinpalkovic!\n- Manager: Added tokens and a dark color scheme for status colors - [#33081](https://github.com/storybookjs/storybook/pull/33081), thanks @MichaelArestad!\n- Remove yarn esbuild pnp plugin - [#33097](https://github.com/storybookjs/storybook/pull/33097), thanks @mrginglymus!\n- UI: Increase border contrast of Checkbox, Radio, and Range - [#33064](https://github.com/storybookjs/storybook/pull/33064), thanks @MichaelArestad!"}} \ No newline at end of file