-
Notifications
You must be signed in to change notification settings - Fork 361
.pr_agent_accepted_suggestions
| PR 3489 (2026-08-27) |
[correctness] Docs import deleted list paths
Docs import deleted list paths
The extraction relocates BaseList and BaseItem, but the canonical BaseList story still imports their now-deleted `core/src/components` paths, so Storybook cannot resolve either module. Rewire the story to the new package and declare that package as a direct docs dependency.The BaseList Storybook story imports BaseList and BaseItem from source directories removed by this extraction, causing unresolved-module failures.
Import both components from @vibe/base-list, and add @vibe/base-list as a direct dependency of @vibe/docs rather than relying on @vibe/core transitively.
- packages/docs/src/pages/components/BaseList/BaseList.stories.tsx[3-4]
- packages/docs/package.json[24-29]
| PR 3486 (2026-08-26) |
[maintainability] `StepsGalleryHeader` lacks component ID
`StepsGalleryHeader` lacks component ID
The PR newly exposes `StepsGalleryHeader` from the standalone package without registering a corresponding `ComponentVibeId`. The new public component therefore does not participate in the repository's component identifier system.StepsGalleryHeader is newly exported as a public standalone-package component but has no ComponentVibeId registration.
Add a distinct shared component ID and apply it as data-vibe on the component root while preserving the intentional decision not to re-export this symbol from core.
- packages/components/wizard/src/Steps/index.ts[3-3]
- packages/components/wizard/src/Steps/StepsGalleryHeader.tsx[5-5]
- packages/components/wizard/src/Steps/StepsGalleryHeader.tsx[66-69]
- packages/shared/src/tests/constants.ts[181-188]
| PR 3483 (2026-08-25) |
[correctness] Core exposes internal `FieldLabel`
Core exposes internal `FieldLabel`
The wildcard re-export exposes every `@vibe/text-inputs` root symbol through `@vibe/core`, including `FieldLabel` and `FieldLabelProps`, although core is intended to preserve only the existing `TextField`, `TextArea`, and `NumberField` API. This unintentionally expands core's supported public API and subjects future `FieldLabel` changes to compatibility constraints.@vibe/core uses a wildcard export from @vibe/text-inputs, unintentionally publishing the package-only FieldLabel and FieldLabelProps symbols through core.
Core should preserve its prior public surface by selectively re-exporting only TextField, TextArea, NumberField, and their intended public types from the standalone package. The text-inputs root barrel includes FieldLabel, so wildcard-exporting that barrel expands core's supported API and creates compatibility constraints for future FieldLabel changes.
- packages/core/src/components/index.ts[55-55]
- packages/components/text-inputs/src/index.ts[1-4]
[correctness] Slider TextField mock is stale
Slider TextField mock is stale
`SelectionIndicator` now imports the named `TextField` export from `@vibe/text-inputs`, while all three Slider suites still mock the deleted `../../TextField/TextField` module. The mock therefore no longer intercepts the rendered component, so snapshots expecting `mock-text-field-comp` fail.The Slider tests mock the removed core-local TextField path, but SelectionIndicator now imports TextField from @vibe/text-inputs. Update the mocks to intercept the named package export so existing Slider behavior and snapshots remain isolated.
Preserve any other actual @vibe/text-inputs exports if needed, and provide TextField as a named mock export rather than a default export.
- packages/core/src/components/Slider/tests/Slider-non-ranged.test.tsx[9-13]
- packages/core/src/components/Slider/tests/Slider-ranged.test.tsx[9-13]
- packages/core/src/components/Slider/tests/Slider.snapshot.test.tsx[12-16]
| PR 3478 (2026-08-24) |
[reliability] Outdated pinned dependencies
Outdated pinned dependencies
`@vibe/attention-box` pins multiple `@vibe/*` dependencies to older patch versions than the versions currently used in this repo (and by `@vibe/core`), which can force consumers to install duplicate copies of these packages. This increases bundle size and can create subtle runtime/type skew when `@vibe/core` and `@vibe/attention-box` are used together.The new @vibe/attention-box package.json pins several @vibe/* dependencies to older patch versions than the versions currently present in the monorepo and used by @vibe/core. When published, installing @vibe/core will bring in @vibe/attention-box, which will then pull older versions of @vibe/button, @vibe/shared, etc., leading to duplicate installs and potential runtime/version skew.
-
@vibe/attention-boxcurrently depends on older patch versions (e.g.@vibe/button4.0.7,@vibe/shared4.0.5). - In this same PR branch, the repo’s current versions are newer (e.g.
@vibe/button4.0.8,@vibe/shared4.0.6) and@vibe/coredepends on those newer versions.
Update packages/components/attention-box/package.json dependency versions to match the current workspace package versions (or use your repo’s standard policy such as workspace:* / ^ ranges).
- packages/components/attention-box/package.json[40-49]
| PR 3477 (2026-08-24) |
[reliability] Shared version mismatch
Shared version mismatch
@vibe/accordion pins @vibe/shared to 4.0.3 while the workspace version is 4.0.5 (and other packages depend on 4.0.5), which can prevent workspace linking and cause a different published @vibe/shared to be installed. That can break the build (missing exports like ComponentVibeId/useMergeRef) or produce multiple installed @vibe/shared copies in consumers.@vibe/accordion depends on @vibe/shared version 4.0.3, but this monorepo’s workspace @vibe/shared is 4.0.5 and other packages already pin to 4.0.5. With exact versions, this can prevent workspace resolution and pull an older published @vibe/shared, causing missing exports and/or duplicated installs.
-
@vibe/accordionimportsComponentVibeId,VibeComponentProps, anduseMergeReffrom@vibe/shared, so resolving to an older shared version is high risk.
- packages/components/accordion/package.json[39-44]
- Change
@vibe/shareddependency inpackages/components/accordion/package.jsonfrom4.0.3to4.0.5(matching the workspace and other packages).
| PR 3474 (2026-08-23) |
[reliability] Workspace version mismatches
Workspace version mismatches
@vibe/alert-banner pins @vibe/* dependencies to versions that do not match the workspace package versions in this repo, so Yarn/Lerna may treat them as external (registry) dependencies rather than local workspace links. This can break monorepo builds/tests (wrong dependency graph / missing local changes) or make the package compile against different APIs than the repo actually contains.packages/components/alert-banner/package.json pins several @vibe/* dependencies to versions older than the versions of those packages present in this monorepo. In Yarn workspaces/Lerna, a dependency is only treated as a workspace/local package when its version range satisfies the workspace package version; otherwise it can be installed from the registry and excluded from the local package graph.
This repo uses Yarn workspaces and Lerna for build/test orchestration. Version mismatches can cause:
- Building/testing against published packages instead of the local workspace packages.
- Incorrect
lerna --include-dependenciesbuild ordering (dependency edges not recognized).
- packages/components/alert-banner/package.json[40-50]
- package.json[5-23]
- scripts/build-dependencies.sh[1-1]
Update @vibe/alert-banner dependency versions to match the workspace package versions in this repo (or use a workspace protocol/range that satisfies them). Concretely, align at least:
-
@vibe/buttonto4.0.7 -
@vibe/hooksto4.0.7 -
@vibe/icon-buttonto4.0.12 -
@vibe/sharedto4.0.5 -
@vibe/tooltipto4.0.12 -
@vibe/typographyto4.0.12(Keep@vibe/linkat4.0.3and@vibe/iconsat4.1.0if those already match.)
[maintainability] Duplicate icons dependency
Duplicate icons dependency
@vibe/icons is listed in both dependencies and devDependencies in @vibe/alert-banner, which increases the chance of duplicate installs/version skew and is inconsistent with other packages’ patterns. It should be listed only where needed (typically dependencies only if used at runtime).@vibe/icons is declared in both dependencies and devDependencies for @vibe/alert-banner.
Other component packages typically keep @vibe/icons in devDependencies when only used for tests/examples, or in dependencies when used at runtime. Duplicating it in both sections is unnecessary and can lead to drift.
- packages/components/alert-banner/package.json[40-61]
Keep @vibe/icons only in dependencies (since AlertBanner.tsx imports CloseSmall at runtime) and remove it from devDependencies.
| PR 3473 (2026-08-23) |
[reliability] Shared version mismatch
Shared version mismatch
`@vibe/transitions` depends on `@vibe/shared` v4.0.3 while the workspace `@vibe/shared` package is v4.0.5, which can prevent workspace linking and/or pull a different published version with potentially different exports. This can break builds/tests or cause runtime/type inconsistencies depending on which `@vibe/shared` gets resolved.@vibe/transitions pins @vibe/shared to 4.0.3, but the repo’s @vibe/shared workspace package is 4.0.5. With exact version pinning, this can prevent Yarn/Lerna from linking the workspace package and instead resolve a different registry version (or fail if unavailable), causing build/test failures or missing exports.
Most packages in this monorepo pin internal dependencies to the exact workspace version (e.g. @vibe/shared: 4.0.5). The new package should follow the same pattern.
- packages/components/transitions/package.json[39-43]
Update @vibe/shared dependency version in @vibe/transitions to match the workspace version (currently 4.0.5), consistent with other packages.
[correctness] Exports snapshot will fail
Exports snapshot will fail
`@vibe/core` now re-exports `SlideTransition` from `@vibe/transitions`, which changes `Object.keys(components)` in `packages/core/src/__tests__/exports.test.ts`. The snapshot currently contains `TransitionView` but not `SlideTransition`, so this PR likely breaks the exports snapshot test.The @vibe/core components barrel now exports SlideTransition, which changes the set of exported keys used by the exports snapshot test. The snapshot currently expects TransitionView but does not include SlideTransition, so tests will fail.
packages/core/src/__tests__/exports.test.ts snapshots Object.keys(components).sort(). Any new named export from packages/core/src/components/index.ts must be reflected in exports.test.ts.snap, or the export must be removed if it’s unintended.
- packages/core/src/components/index.ts[77-84]
- packages/core/src/tests/exports.test.ts[6-9]
- packages/core/src/tests/snapshots/exports.test.ts.snap[101-117]
Either:
- If
SlideTransitionis intended to be part of@vibe/corepublic exports, update the snapshot to includeSlideTransition(run the test suite with snapshot update). - If it’s not intended to be exported from core, remove
SlideTransitionfrom the re-export list inpackages/core/src/components/index.ts.
[performance] Build ignores skip flag
Build ignores skip flag
`@vibe/transitions` always builds mocked classnames (`mock_classnames=on rollup -c`) and does not respect `SKIP_RELEASE_ARTIFACTS`, unlike other packages. This undermines the CI workflow’s `SKIP_RELEASE_ARTIFACTS` optimization and can increase build time/artifact generation unexpectedly.@vibe/transitions build script always runs the mocked-classnames rollup build, ignoring the repo-wide SKIP_RELEASE_ARTIFACTS flag used in CI.
The GitHub workflow sets SKIP_RELEASE_ARTIFACTS when running lerna run build. Other packages implement a build:mocked-classnames script that conditionally skips the second build when SKIP_RELEASE_ARTIFACTS=true.
- packages/components/transitions/package.json[34-37]
- .github/workflows/build-and-upload.yml[38-45]
Match the standard pattern used by other component packages:
- Set
buildtorollup -c && yarn build:mocked-classnames - Add
build:mocked-classnames:[ "$SKIP_RELEASE_ARTIFACTS" = "true" ] || mock_classnames=on rollup -c
| PR 3472 (2026-08-23) |
[performance] Pinned deps cause duplication
Pinned deps cause duplication
@vibe/toggle pins older exact versions of @vibe/shared/@vibe/style/@vibe/typography than @vibe/core, so installing @vibe/core can pull multiple versions of these libraries and inflate bundle size (and potentially duplicate styles/runtime utilities). This is introduced by the new package.json and is amplified because @vibe/core now depends on @vibe/toggle.@vibe/toggle pins exact dependency versions that are older than the ones @vibe/core uses. Because @vibe/core now depends on @vibe/toggle, consumers of @vibe/core may end up with multiple installed copies of @vibe/shared, @vibe/style, and @vibe/typography, increasing bundle size and risking subtle inconsistencies.
-
@vibe/toggledependencies:@vibe/shared@4.0.3,@vibe/style@4.0.0,@vibe/typography@4.0.6. -
@vibe/coredependencies:@vibe/shared@4.0.5,@vibe/style@4.1.0,@vibe/typography@4.0.12.
- packages/components/toggle/package.json[39-45]
- packages/core/package.json[118-126]
Update @vibe/toggle to use the same versions as @vibe/core (or adopt an internal policy such as keeping all @vibe/* deps aligned to the repo’s current patch), so package managers can dedupe cleanly.
[reliability] Hardcoded node_modules paths
Hardcoded node_modules paths
@vibe/toggle’s tsconfig hardcodes `typeRoots` and `paths` to `../../../node_modules`, unlike the other component packages, making builds fragile under non-hoisted installs (and incompatible with environments without node_modules such as Yarn PnP). This configuration increases maintenance risk and can cause module/type resolution to behave differently from the rest of the monorepo.packages/components/toggle/tsconfig.json hardcodes typeRoots and a paths mapping pointing to ../../../node_modules. This is inconsistent with other component packages and can break type/module resolution in setups that don’t have a physical node_modules tree at that location.
Other component packages (e.g. @vibe/checkbox) rely on the shared @vibe/config/tsconfig without overriding typeRoots/paths, keeping resolution consistent across the monorepo.
- packages/components/toggle/tsconfig.json[1-10]
- packages/components/checkbox/tsconfig.json[1-8]
Remove the typeRoots and paths overrides unless there is a proven, documented need. If path mapping is required, prefer a workspace-consistent approach (e.g. using standard Node resolution, or a repo-wide TS path config) rather than hardcoding to ../../../node_modules.
| PR 3471 (2026-08-23) |
[performance] Build script skips convention
Build script skips convention
`@vibe/theme-provider`’s `build` script always runs the mocked_classnames rollup build and does not honor the repo’s common `SKIP_RELEASE_ARTIFACTS` guard used by other component packages. This increases build time and deviates from established release-artifact behavior.The new package uses "build": "rollup -c && mock_classnames=on rollup -c" with no SKIP_RELEASE_ARTIFACTS check and no separate build:mocked-classnames script. Other component packages standardize on build:mocked-classnames with a guard to skip these artifacts.
Keeping consistent build scripts across component packages helps CI performance and predictable release behavior.
- packages/components/theme-provider/package.json[34-37]
- Update scripts to match the established pattern:
"build": "rollup -c && yarn build:mocked-classnames""build:mocked-classnames": "[ \"$SKIP_RELEASE_ARTIFACTS\" = \"true\" ] || mock_classnames=on rollup -c"- Ensure lerna/yarn workflows that rely on
SKIP_RELEASE_ARTIFACTSbehave the same for this new package.
| PR 3469 (2026-08-20) |
[correctness] Missing style dependency
Missing style dependency
`@vibe/tabs` SCSS imports `@vibe/style`, but `packages/components/tabs/package.json` does not declare `@vibe/style` in `dependencies`, so consumers may fail to resolve Sass imports when using `@vibe/tabs` outside the monorepo. This is a publish-time/runtime build break for the new package.@vibe/tabs imports @vibe/style from SCSS (~@vibe/style/dist/mixins), but the package does not list @vibe/style in dependencies. When published/consumed independently, Sass compilation (or any build step resolving these imports) can fail with a module-not-found error.
Other component packages that import @vibe/style in their SCSS declare it as a direct dependency.
- packages/components/tabs/package.json[40-48]
- packages/components/tabs/src/Tab/Tab.module.scss[1-3]
Add "@vibe/style": "4.1.0" (or the repo-standard current @vibe/style version) to dependencies in packages/components/tabs/package.json.
| PR 3468 (2026-08-20) |
[performance] Artifact skip flag ignored
Artifact skip flag ignored
The Skeleton build always runs the mocked-classnames Rollup pass, so builds using `SKIP_RELEASE_ARTIFACTS=true` still generate and upload `dist/mocked_classnames`. This adds an unnecessary second compilation and defeats the workflow's artifact-skip control for this package.The Skeleton package always builds mocked-classname release artifacts, even when SKIP_RELEASE_ARTIFACTS=true requests that these artifacts be skipped.
Other extracted component packages expose a separate guarded build:mocked-classnames script. The shared Rollup configuration writes mocked builds to dist/mocked_classnames, and CI passes SKIP_RELEASE_ARTIFACTS into package builds.
- packages/components/skeleton/package.json[34-37]
[maintainability] Shared dependency version mismatch
Shared dependency version mismatch
`@vibe/skeleton` pins `@vibe/shared` 4.0.3 while its direct consumer `@vibe/core` and every sibling component package use 4.0.4. Installing core therefore requires incompatible exact shared versions, duplicating the package and leaving Skeleton on the older implementation.The new Skeleton package pins @vibe/shared 4.0.3, whereas the current workspace package and consuming core package are version 4.0.4.
Skeleton uses runtime utilities from @vibe/shared, not only types. Exact mismatched versions prevent the dependency graph from using one consistent shared release.
- packages/components/skeleton/package.json[39-43]
| PR 3446 (2026-07-27) |
[correctness] Missing core export
Missing core export
`@vibe/core` no longer exports `EditableTypography`, so `import { EditableTypography } from "@vibe/core"` will fail for existing consumers. Core already depends on `@vibe/editable` and re-exports `EditableHeading`/`EditableText`, but does not re-export `EditableTypography`.@vibe/core’s components barrel re-exports EditableHeading and EditableText from @vibe/editable, but it no longer re-exports EditableTypography. This is a breaking change for consumers importing EditableTypography from @vibe/core.
-
@vibe/core’s public entrypoint re-exports everything from./components. -
@vibe/editableexportsEditableTypographyfrom its own index.
- packages/core/src/components/index.ts[31-35]
Update the @vibe/editable re-export line to include EditableTypography (and EditableTypographyProps at minimum), e.g.:
[reliability] Undeclared style dependency
Undeclared style dependency
`@vibe/editable` imports Sass mixins/functions from `@vibe/style/dist/*` but `packages/components/editable/package.json` does not declare `@vibe/style` as a dependency, which can cause the package build to fail (unresolved Sass imports) when `@vibe/style` isn’t hoisted/available or isn’t built first in topological builds.@vibe/editable uses Sass imports from @vibe/style/dist/... but does not list @vibe/style in its dependencies. This creates a non-deterministic build that can fail when dependencies aren’t hoisted or when build order doesn’t build @vibe/style first.
Multiple SCSS files in the new package import @vibe/style/dist/mixins and @vibe/style/dist/functions. Other component packages that import these Sass assets (e.g. @vibe/typography) declare @vibe/style directly, and @vibe/style’s build script is responsible for producing the dist/mixins and dist/functions folders.
Add "@vibe/style": "4.0.0" (or the repo-standard compatible version) to packages/components/editable/package.json under dependencies.
- packages/components/editable/package.json[39-45]
| PR 3445 (2026-07-27) |
[correctness] Missing style dependency
Missing style dependency
@vibe/counter imports Sass mixins from @vibe/style but does not declare @vibe/style in its dependencies, which can break building/consuming the package when @vibe/style isn’t otherwise installed/hoisted.packages/components/counter/src/Counter/Counter.module.scss imports ~@vibe/style/dist/mixins, but packages/components/counter/package.json does not list @vibe/style in dependencies. This makes the package’s build and/or isolated consumption rely on incidental workspace hoisting.
Other component packages that import the same @vibe/style mixins declare @vibe/style as a direct dependency.
- packages/components/counter/package.json[39-44]
[maintainability] Unconditional mocked-classnames build
Unconditional mocked-classnames build
@vibe/counter’s build script always runs the mocked-classnames Rollup build and does not follow the repo’s common `build:mocked-classnames` + `SKIP_RELEASE_ARTIFACTS` guard pattern, which can cause inconsistent CI behavior and unnecessary extra build work.Most packages use build:mocked-classnames with a SKIP_RELEASE_ARTIFACTS guard and have build call it. @vibe/counter currently runs mock_classnames=on rollup -c unconditionally, which deviates from the established convention.
This pattern exists in multiple packages (including @vibe/core) and is likely used to skip expensive artifact generation in some CI/release flows.
- packages/components/counter/package.json[34-38]
| PR 3441 (2026-07-20) |
[reliability] Core-package dependency cycle
Core-package dependency cycle
@vibe/core now depends on @vibe/color-picker, while @vibe/color-picker imports runtime symbols from @vibe/core, creating a circular module/package dependency that can yield partially-initialized/undefined exports at runtime and unstable bundling behavior.@vibe/core re-exports from @vibe/color-picker, but the new @vibe/color-picker package imports useGridKeyboardNavigation / GridKeyboardNavigationContext from @vibe/core, forming a circular dependency. This risks circular ESM evaluation (undefined exports / initialization order bugs) and complicates builds.
-
@vibe/coredepends on@vibe/color-picker. -
@vibe/color-pickercurrently depends on and imports from@vibe/core. - The
@vibe/coreroot entry also has side-effect imports (see separate finding).
- packages/components/color-picker/package.json[40-51]
- packages/core/package.json[91-107]
- packages/components/color-picker/src/ColorPicker/components/ColorPickerContent/ColorPickerContent.tsx[19-23]
- packages/components/color-picker/src/ColorPicker/components/ColorPickerContent/ColorPickerColorsGrid.tsx[1-12]
- packages/components/color-picker/src/ColorPicker/components/ColorPickerContent/ColorPickerClearButton.tsx[1-7]
- Remove
@vibe/coreas a dependency of@vibe/color-picker. - Move the needed keyboard-navigation hook/context to a lower-level package that both can depend on (e.g.
@vibe/sharedor@vibe/hooks), or copy the minimal implementation into@vibe/color-picker. - Update
@vibe/coreto re-export those APIs from the new location (preserving public API), and update@vibe/color-pickerimports to use the new location (not@vibe/core).
[reliability] Pinned core version mismatch
Pinned core version mismatch
@vibe/color-picker pins @vibe/core to 4.0.0, but the workspace @vibe/core package is 4.5.3; depending on package-manager workspace rules this can resolve a different core version than the local source, producing inconsistent builds/tests.The new package declares an exact dependency on @vibe/core version 4.0.0, but the repo’s @vibe/core workspace version is 4.5.3. With exact pins, workspace linking can be skipped and a registry version may be used instead of local sources.
This also exacerbates the circular dependency risk by potentially introducing multiple instances/versions of @vibe/core.
- packages/components/color-picker/package.json[40-50]
- packages/core/package.json[1-5]
- If
@vibe/color-pickermust depend on@vibe/core, change the version to match the workspace (4.5.3) or use the repo’s standard workspace dependency strategy. - Preferably (and consistent with other separated component packages), remove the dependency on
@vibe/coreentirely and depend on lower-level packages instead.
[maintainability] Duplicate SVG mock
Duplicate SVG mock
packages/components/color-picker/src/ColorPicker/__tests__/ColorPicker.test.tsx adds a local react-inlinesvg mock even though Vitest already loads vitest.setup.mjs (which defines the same mock) for the package test run. Keeping both mocks is redundant and can lead to confusion or inconsistent behavior if one mock implementation changes and the other doesn’t.ColorPicker.test.tsx defines a vi.mock("react-inlinesvg"), but this mock is already provided globally by vitest.setup.mjs (loaded via @vibe/config's setupFiles). This duplication increases maintenance overhead and can cause confusion if the mocks diverge.
The base Vitest config loads ./vitest.setup.mjs automatically, and the color-picker package has its own vitest.setup.mjs with the same mock.
- Remove the redundant
vi.mock("react-inlinesvg", ...)block from the test file and rely onvitest.setup.mjs. - packages/components/color-picker/src/ColorPicker/tests/ColorPicker.test.tsx[1-9]
| PR 3440 (2026-07-20) |
[maintainability] Misleading Checkbox export TODO
Misleading Checkbox export TODO
The TODO comment in @vibe/core says the wildcard re-export is blocked by “enums”, but @vibe/checkbox currently exports only Checkbox and CheckboxProps; this mismatch can confuse future maintainers about why the export is not `export *`.@vibe/core has a TODO comment implying it can't export * from "@vibe/checkbox" due to enums, but @vibe/checkbox currently exports only Checkbox and CheckboxProps. This comment is misleading and may cause incorrect assumptions during future refactors.
-
@vibe/corecurrently does a named re-export for Checkbox. -
@vibe/checkboxentrypoints only expose Checkbox + CheckboxProps.
- packages/core/src/components/index.ts[10-11]
| PR 3435 (2026-07-14) |
[correctness] Missing radius recommendation
Missing radius recommendation
Adding numeric border-radius vars makes 4px/8px/16px map to multiple allowed CSS vars, but no recommended var is configured for border-radius values. This can cause the stylelint rule’s autofix path (when useRecommendedFixes is enabled) to emit invalid CSS (var(undefined)) and can also break the props-to-allowed-vars unit test logic that assumes recommendations exist for multi-option values.New numeric border-radius tokens create duplicate replacements for the same canonical values (e.g., 16px now matches both --border-radius-big and --border-radius-16). The stylelint rule’s autofix logic uses recommended when multiple replacements exist; for border-radius props recommended is currently undefined, leading to var(undefined) when useRecommendedFixes: true. The unit tests also assume recommended is iterable for multi-option values and will fail/throw once duplicates exist.
-
BORDER_RADIUSESnow includes both semantic and numeric vars. -
getPropsToAllowedCssVars()groups vars by canonical value; duplicates are expected after this PR. - Fix-mode behavior must remain deterministic and produce valid CSS.
- packages/style/stylelint-config/rules/use-defined-css-var-when-available/props-to-allowed-vars.js[16-24]
- packages/style/stylelint-config/rules/use-defined-css-var-when-available/props-to-allowed-vars.js[140-168]
- packages/style/stylelint-config/rules/use-defined-css-var-when-available/index.js[68-88]
- packages/style/stylelint-config/rules/use-defined-css-var-when-available/tests/props-to-allowed-vars.test.js[54-87]
- packages/style/stylelint-config/rules/use-defined-css-var-when-available/tests/index.test.js[52-96]
- Choose a single recommended token per duplicated canonical border-radius value (e.g., prefer semantic tokens
--border-radius-small/medium/bigto preserve existing behavior, or prefer numeric tokens to promote primitives) and pass them viamapPropsToAllowedVars(..., ..., recommendedList)forBORDER_RADIUSES_PROPS. - Harden autofix: if
useRecommendedFixesis enabled butrecommendedis missing/undefined for a value, do not apply a fix (avoid emittingvar(undefined)), and keep reporting the warning. - Update/adjust tests to reflect the new multi-option reality:
- Ensure the recommendation tests don’t crash when
recommendedis absent. - Update expected warning text / expected fixed output depending on your chosen recommended token.
[maintainability] Border-radius autofix stops
Border-radius autofix stops
Adding numeric border-radius vars makes 4px/8px/16px resolve to multiple allowed variables, and the stylelint plugin skips fixes for multi-option values unless `useRecommendedFixes` is enabled. `@vibe/core` runs `stylelint:fix` without that option, so raw border-radius literals may remain unfixed even when using `--fix`.After adding --border-radius-4/8/16 alongside --border-radius-small/medium/big, the same canonical values now have multiple valid token replacements. The stylelint rule only autofixes multi-option values when useRecommendedFixes is enabled, so existing stylelint --fix workflows will stop converting border-radius: 4px/8px/16px into token usage.
- The plugin fix logic returns early for multi-option values unless
secondaryOptionObject.useRecommendedFixesis true. -
packages/coreconfig does not setuseRecommendedFixes, but does provide astylelint:fixscript.
- packages/core/.stylelintrc.json[1-12]
- packages/style/stylelint-config/index.js[1-7]
- packages/style/stylelint-config/rules/use-defined-css-var-when-available/index.js[56-88]
- packages/style/stylelint-config/rules/use-defined-css-var-when-available/props-to-allowed-vars.js[157-166]
Update the stylelint rule configuration to include useRecommendedFixes: true (while preserving existing severity overrides), e.g.:
| PR 3408 (2026-06-15) |
[correctness] Fixed class not applied
Fixed class not applied
`useFixedPosition` does not actually switch the modal to `position: fixed` because `.modal { position: relative; }` is declared later with the same specificity, overriding `.fixedPosition`. Consumers enabling `useFixedPosition` will still see the RTL centering issue because the modal remains relatively positioned.The new .fixedPosition { position: fixed; } class is overridden by the later .modal { position: relative; } rule (same specificity; later wins), so useFixedPosition is ineffective.
Modal.tsx applies both styles.modal and styles.fixedPosition to the same element. With current SCSS ordering, the browser keeps position: relative.
- packages/core/src/components/Modal/Modal/Modal.module.scss[26-83]
- Increase specificity by scoping the override under
.modal, e.g. inside.modal { &.fixedPosition { position: fixed; } }, or define.modal.fixedPosition { position: fixed; }after the base.modalposition declaration. - Avoid
!importantunless there’s no cleaner cascade solution.
| PR 3362 (2026-05-21) |
[correctness] Flex wrapper blocks fullWidth
Flex wrapper blocks fullWidth
The Overview story wraps `ButtonGroup` in a `display: flex` container without making the `ButtonGroup` flex item stretch, so the component can remain shrink-to-content and toggling `fullWidth` may still show no visible width change. This happens because `fullWidth` styles apply to an inner container, not the root element, so the root must be allowed to expand to reflect the change.Overview wraps ButtonGroup with a flex container (display: "flex"). Since the ButtonGroup root isn’t given width: 100% or flex: 1, it can shrink to its contents as a flex item, meaning fullWidth toggling may still not produce any visible change.
ButtonGroup's fullWidth mainly affects the inner .buttonsContainer and button wrappers, not the root wrapper element. The root needs to be allowed to expand for width: 100% on inner elements to matter.
- packages/docs/src/pages/components/ButtonGroup/ButtonGroup.stories.tsx[20-25]
Change the wrapper to a non-flex full-width block wrapper:
| PR 3361 (2026-05-21) |
[maintainability] Duplicate day-picker versions
Duplicate day-picker versions
After bumping @vibe/core to react-day-picker@^8.10.2, yarn.lock still pins react-day-picker@^8.8.0 to 8.10.1, so the workspace resolves two different react-day-picker versions (8.10.2 and 8.10.1). This increases install/bundle surface area and makes dependency behavior harder to reason about across packages that pull different trees.The PR bumps @vibe/core to react-day-picker@^8.10.2, but the repo’s yarn.lock still resolves react-day-picker@^8.8.0 to 8.10.1 while react-day-picker@^8.10.2 resolves to 8.10.2. This results in two different installed react-day-picker versions.
storybook-addon-playground depends on @vibe/core@^3.59.0, which in turn depends on react-day-picker@^8.8.0, keeping the older lock entry alive.
- /yarn.lock[18034-18042]
- /yarn.lock[6702-6734]
- /yarn.lock[19835-19846]
- Update the lockfile so all
react-day-pickerranges resolve to the same version (preferably8.10.2), e.g. by running a targeted upgrade/dedupe (yarn upgrade react-day-picker@8.10.2and/or a dedupe flow) and committing the updatedyarn.lock. - If needed for consistency, add a workspace-level override (e.g., Yarn resolutions) to force a single
react-day-pickerversion.
| PR 3346 (2026-04-09) |
[correctness] Unlabeled active option
Unlabeled active option
ColorPickerColorsGrid sets aria-activedescendant to an element id on thearia-activedescendant points to the <li role="option">, but the option has no accessible name because aria-label is currently on a nested <Clickable role="presentation">. This prevents reliable screen reader announcement of the active color.
For the active-descendant listbox pattern, the element referenced by aria-activedescendant should be the one that exposes the accessible name.
- packages/core/src/components/ColorPicker/components/ColorPickerItemComponent/ColorPickerItemComponent.tsx[120-157]
- packages/core/src/components/ColorPicker/components/ColorPickerContent/ColorPickerColorsGrid.tsx[133-160]
- Add
aria-label={colorAriaLabel}to the<li role="option">. - Remove
aria-labelfrom the nestedClickable(and keep it presentational/hidden, e.g.aria-hidden, if you still need to avoid double announcements). - Consider moving the click handler to the
<li>(or otherwise ensure activating therole="option"element triggers selection) to align behavior with the option semantics and avoid relying on a presentational child for interaction.
[reliability] New ARIA states untested
New ARIA states untested
The PR adds key accessibility attributes (`aria-activedescendant` on the listbox and `aria-selected` on options) but the updated tests do not assert these behaviors/attributes. This weakens confidence that screen reader announcements and selection-state semantics work as intended.New accessibility behavior was added (role=listbox/option, aria-activedescendant, aria-selected), but tests do not assert these attributes/behaviors.
The PR’s goal is screen reader support during keyboard navigation and selection-state announcements; without assertions, regressions are likely.
- packages/core/src/components/ColorPicker/tests/ColorPicker.test.tsx[20-42]
- packages/core/src/components/ColorPicker/components/ColorPickerContent/ColorPickerColorsGrid.tsx[129-142]
- packages/core/src/components/ColorPicker/components/ColorPickerItemComponent/ColorPickerItemComponent.tsx[121-135]
[correctness] Invalid option IDs
Invalid option IDs
ColorPickerColorsGrid builds option `id` values by concatenating the raw `color` string, which can include spaces/characters (e.g. CSS `rgb(0, 0, 0)`) and produce invalid HTML IDs. This can break `aria-activedescendant` targeting and prevent VoiceOver from announcing the active option during keyboard navigation.Issue description
getColorItemId() builds DOM IDs from the raw color string (e.g. ...-item-rgb(0, 0, 0)), which can contain spaces/special chars and become invalid, breaking aria-activedescendant.
Issue Context
ColorPicker supports arbitrary CSS color strings (not only monday tokens), so this can occur in real usage when forceUseRawColorList is used with values like rgb(...).
Fix Focus Areas
- packages/core/src/components/ColorPicker/components/ColorPickerContent/ColorPickerColorsGrid.tsx[129-160]
Suggested approach
- Generate option IDs using a stable, safe suffix (preferably the
index): -
const getColorItemId = (index: number) => gridId ?${gridId}-item-${index}: undefined; - Pass
id={getColorItemId(index)}intoColorPickerItemComponent - Compute
activeDescendantIdusingactiveIndexdirectly:activeIndex >= 0 ? getColorItemId(activeIndex) : undefined - Keep
key={color}as-is to avoid React key churn.
| PR 3343 (2026-03-30) |
[correctness] Dry-run blocks publishing
Dry-run blocks publishing
The v3 release workflow runs `yarn lerna publish` with `--dry-run`, so it can succeed without actually publishing any packages to npm. This makes the workflow non-functional as a release pipeline.The v3 release workflow uses lerna publish ... --dry-run, which prevents publishing packages to npm.
This workflow is named and structured as a release workflow, and the existing release.yml performs a real publish.
- .github/workflows/release-v3.yml[81-82]
Update the publish step to remove --dry-run (keep --dist-tag v3 if that’s intended), e.g.:
yarn lerna publish from-package --dist-tag v3 -y
[correctness] Versioning flag inconsistency
Versioning flag inconsistency
The v3 workflow’s `lerna version` command omits `--conventional-graduate`, unlike the main `release.yml` workflow. This can change which packages get promoted to stable during versioning and lead to unexpected version outputs vs the established release process.release-v3.yml runs yarn lerna version without the --conventional-graduate flag, diverging from the repo’s existing release workflow.
release.yml includes --conventional-graduate in the analogous step. If v3 should follow the same promotion/versioning rules, this omission can change which packages are graduated/promoted during a release.
- .github/workflows/release-v3.yml[73-76]
Either:
- Add
--conventional-graduateto matchrelease.yml, or - Add an explicit comment in the workflow explaining why v3 intentionally diverges (to avoid future confusion/mis-releases).
| PR 3333 (2026-03-15) |
[correctness] Checkbox toggles hidden input
Checkbox toggles hidden input
`Checkbox.setChecked()`/`setUnchecked()` now call Playwright `check()`/`uncheck()` on the Checkbox ``, but the core component intentionally styles that input as a hidden 0×0 element, so the element is not interactable and toggle operations can fail/time out.Checkbox.setChecked()/setUnchecked() uses Playwright check()/uncheck() on the underlying &amp;amp;lt;input&amp;amp;gt;, but the core Checkbox intentionally styles that input with hidden-element() (opacity 0, width/height 0). This makes the input non-interactable and can cause toggle operations to fail.
The visual/clickable target in the core component is the rendered checkbox control (the element with data-testid derived from ComponentDefaultTestId.CHECKBOX_CHECKBOX), not the hidden input.
- packages/testkit/components/Checkbox.ts[20-58]
- packages/core/src/components/Checkbox/Checkbox.module.scss[69-75]
- packages/style/src/mixins/_common.scss[9-14]
[reliability] Toast locator non-unique
Toast locator non-unique
The new selector `div[data-testid^="toast"]` matches both the toast root (`toast_`) and the internal toast content container (`toast-content`), so the testkit `Toast` locator can resolve to multiple elements and break strict Playwright waits/assertions/actions.div[data-testid^=&amp;amp;quot;toast&amp;amp;quot;] matches both the toast root and the internal toast-content div, making the locator non-unique and breaking strict Playwright operations (waits/assertions/actions).
The Toast root has role=&amp;amp;quot;alert&amp;amp;quot;, while the toast-content container does not; this is a stable discriminator.
- packages/testkit/tests/Toast.test.ts[1-6]
- packages/core/src/components/Toast/Toast.tsx[187-205]
- packages/core/src/tests/constants.ts[89-93]
- packages/components/layout/src/Flex/Flex.tsx[68-75]
| PR 3328 (2026-03-11) |
[correctness] Input focus not cleared
Input focus not cleared
Checkbox.onMouseUpCallback blurs only the wrapper/label, but the component’s visual focus/hover styling is still driven by the hidden input’s focus selectors, so the checkbox can remain in a focused/hovered visual state after pointer interaction. This can leave a persistent focus ring/background until focus moves elsewhere, contradicting the intent of the new blur logic.onMouseUpCallback blurs only the wrapper/label, but focus-driven visuals are still controlled by the hidden &amp;lt;input&amp;gt; (.input:focus...). If the input ends up focused after pointer activation, its focus styles can persist.
The PR’s intent (per comment) is to remove persistent focus ring after pointer interaction and keep keyboard-only focus indication via :focus-visible.
- packages/core/src/components/Checkbox/Checkbox.tsx[105-114]
- packages/core/src/components/Checkbox/Checkbox.module.scss[80-96] (verify focus styles align with the updated blur behavior)
In onMouseUpCallback, after the double requestAnimationFrame, check document.activeElement and blur inputRef.current if it’s focused (and/or blur both wrapper and input safely). Ensure the callback still works for both separateLabel and non-separate modes.
| PR 3326 (2026-03-10) |
[correctness] Misses multiline imports
Misses multiline imports
detectPromotedComponentImports applies a single-line regex to each line of the file, so multi-line imports (a common formatting style) will not be detected and promotedComponentAnalysis will miss real old/new API usage.detectPromotedComponentImports() currently regex-matches imports per line, which fails to detect multi-line import blocks (common in TS/JS). This under-reports old/new API usage for promoted components and can hide required manual migrations.
The code splits file content by \n and runs /import\s+\{([^}]+)\}\s+from.../g on each single line.
- packages/mcp/src/server/tools/v4-migration.ts[743-803]
| PR 3323 (2026-03-07) |
[correctness] V4 version check weak
V4 version check weak
The v4 MCP analyzer determines v4 by `vibeCoreVersion.startsWith("4")`, which misclassifies versions expressed as ranges/prefixed formats (e.g. `^4.0.0`).analyzePackageJson uses startsWith(&amp;amp;amp;quot;4&amp;amp;amp;quot;) for major-version detection, which fails for prefixed semver ranges (e.g., ^4.0.0).
This impacts the tool’s migrationStatus and therefore the guidance/recommendations it emits.
- packages/mcp/src/server/tools/v4-migration.ts[341-365]
| PR 3322 (2026-03-07) |
[correctness] Overbroad next import rewrite
Overbroad next import rewrite
`next-imports-migration` rewrites every `@vibe/core/next` import declaration to `@vibe/core`, but the file comment states only a subset of components were promoted; if other exports remain under `/next` in v4 this will break those imports.The migration currently rewrites the entire import source for any @vibe/core/next import, even though the comment lists only a subset of components that were promoted.
A safer approach is to move only specific specifiers and split imports when necessary.
- packages/codemod/transformations/core/v3-to-v4/next-imports-migration.ts[5-15]
[reliability] Type import removal incomplete
Type import removal incomplete
`type-imports-migration` removes `VibeComponent`/`withStaticProps*` import specifiers but does not attempt to update usages, so migrated code may be left with unresolved identifiers requiring manual follow-up.The codemod removes imports for deprecated type utilities but doesn&amp;amp;amp;#x27;t update usage sites, so output may not compile.
A safer migration either updates the AST where these identifiers are used, or emits warnings when it detects remaining references.
- packages/codemod/transformations/core/v3-to-v4/type-imports-migration.ts[13-56]
| PR 3321 (2026-03-05) |
[correctness] Unsupported codemod commands
Unsupported codemod commands
The guide documents running @vibe/codemod with positional “migration names” (e.g. `aria-props-migration`, `icon-props-rename`), but the CLI only supports `--migration/-m` with a small set of choices. Users following the guide will not run the intended transformation and will likely get the interactive wizard or the wrong migration.The Vibe 4 migration guide documents several @vibe/codemod commands using positional arguments (e.g. npx @vibe/codemod aria-props-migration). The actual CLI only supports selecting a migration via --migration/-m (choices: v3/v4/enums), so these commands won’t run as documented.
The codemod CLI is implemented in packages/codemod/bin/vibe-codemod.ts and only defines option-based selection via yargs. The docs should reflect the real CLI interface.
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[174-175]
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[371-372]
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[476-492]
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[508-509]
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[683-684]
[correctness] Wrong enums codemod cmd
Wrong enums codemod cmd
The FAQ tells users to run `npx @vibe/codemod -m enums` for Vibe 4 enum migration, but the CLI’s `enums` migration actually maps to the v2→v3 enums transformation directory. Users will run the wrong codemod and not get the v4 enum-to-literal changes.The Vibe 4 migration FAQ recommends running npx @vibe/codemod -m enums, but enums is not the v3→v4 migration. The CLI maps it to v2-to-v3/enums, so users will run the wrong codemod.
The v4 migration (v3→v4 transformations) is executed via --migration v4.
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[557-560]
[correctness] Unsupported codemod commands
Unsupported codemod commands
The guide documents running @vibe/codemod with positional “migration names” (e.g. `aria-props-migration`, `icon-props-rename`), but the CLI only supports `--migration/-m` with a small set of choices. Users following the guide will not run the intended transformation and will likely get the interactive wizard or the wrong migration.The Vibe 4 migration guide documents several @vibe/codemod commands using positional arguments (e.g. npx @vibe/codemod aria-props-migration). The actual CLI only supports selecting a migration via --migration/-m (choices: v3/v4/enums), so these commands won’t run as documented.
The codemod CLI is implemented in packages/codemod/bin/vibe-codemod.ts and only defines option-based selection via yargs. The docs should reflect the real CLI interface.
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[174-175]
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[371-372]
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[476-492]
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[508-509]
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[683-684]
[correctness] Unsupported codemod commands
Unsupported codemod commands
The guide documents running @vibe/codemod with positional “migration names” (e.g. `aria-props-migration`, `icon-props-rename`), but the CLI only supports `--migration/-m` with a small set of choices. Users following the guide will not run the intended transformation and will likely get the interactive wizard or the wrong migration.The Vibe 4 migration guide documents several @vibe/codemod commands using positional arguments (e.g. npx @vibe/codemod aria-props-migration). The actual CLI only supports selecting a migration via --migration/-m (choices: v3/v4/enums), so these commands won’t run as documented.
The codemod CLI is implemented in packages/codemod/bin/vibe-codemod.ts and only defines option-based selection via yargs. The docs should reflect the real CLI interface.
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[174-175]
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[371-372]
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[476-492]
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[508-509]
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[683-684]
[correctness] Unsupported codemod commands
Unsupported codemod commands
The guide documents running @vibe/codemod with positional “migration names” (e.g. `aria-props-migration`, `icon-props-rename`), but the CLI only supports `--migration/-m` with a small set of choices. Users following the guide will not run the intended transformation and will likely get the interactive wizard or the wrong migration.The Vibe 4 migration guide documents several @vibe/codemod commands using positional arguments (e.g. npx @vibe/codemod aria-props-migration). The actual CLI only supports selecting a migration via --migration/-m (choices: v3/v4/enums), so these commands won’t run as documented.
The codemod CLI is implemented in packages/codemod/bin/vibe-codemod.ts and only defines option-based selection via yargs. The docs should reflect the real CLI interface.
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[174-175]
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[371-372]
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[476-492]
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[508-509]
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[683-684]
[correctness] Changelog link missing --docs
Changelog link missing --docs
The new Vibe 4 changelog links to `?path=/docs/vibe-4-migration-guide` (no `--docs`), while other docs links in this repo use `...--docs`. This is likely to 404 in Storybook and breaks navigation from the changelog to the guide.The Vibe 4 changelog links to a Storybook docs path without the --docs suffix, which is inconsistent with other internal doc links and likely breaks.
This repo commonly links to docs pages using ?path=/docs/&amp;amp;amp;amp;amp;amp;amp;amp;lt;slug&amp;amp;amp;amp;amp;amp;amp;amp;gt;--docs.
- packages/docs/src/pages/migration-guide/vibe-4-changelog.md[1-6]
[correctness] Bad VIBE4 changelog ref
Bad VIBE4 changelog ref
The migration guide references `VIBE4_CHANGELOG.md` for an example, but the added changelog file is `vibe-4-changelog.md`. This creates a dead/incorrect reference for readers looking for the mentioned example.The Vibe 4 migration guide references VIBE4_CHANGELOG.md, but the actual changelog file in this docs area is vibe-4-changelog.md. Readers will not be able to find the referenced example.
There is already a vibe-4-changelog.md file under the migration guide docs folder.
- packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx[470-480]
[correctness] Changelog link missing --docs
Changelog link missing --docs
The new Vibe 4 changelog links to `?path=/docs/vibe-4-migration-guide` (no `--docs`), while other docs links in this repo use `...--docs`. This is likely to 404 in Storybook and breaks navigation from the changelog to the guide.The Vibe 4 changelog links to a Storybook docs path without the --docs suffix, which is inconsistent with other internal doc links and likely breaks.
This repo commonly links to docs pages using ?path=/docs/&amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;slug&amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;--docs.
- packages/docs/src/pages/migration-guide/vibe-4-changelog.md[1-6]
| PR 3316 (2026-03-03) |
[correctness] BaseInput right padding mismatch
BaseInput right padding mismatch
After reducing BaseInput `padding-inline-start` to 8px, the default `rightThinnerPadding` modifier still sets `padding-inline-end` to 16px when `renderRight` is absent. This creates an unintended 8px/16px asymmetry for the common “no right element” case and contradicts the migration guide’s stated BaseInput padding (8px start, 4px end).BaseInput reduced base padding-inline-start to 8px, but the default modifier applied when renderRight is missing (rightThinnerPadding) still sets padding-inline-end to 16px. This yields unintended asymmetric padding (8px/16px) in common cases.
rightThinnerPadding is currently applied when !renderRight, i.e., the absence of right content. Previously this likely produced symmetric 16px/16px padding; after the change it produces 8px/16px.
- packages/base/src/BaseInput/BaseInput.module.scss[3-41]
- packages/base/src/BaseInput/BaseInput.tsx[23-33]
- VIBE4_MIGRATION_GUIDE.md[411-446]
[correctness] BaseInput right padding mismatch
BaseInput right padding mismatch
After reducing BaseInput `padding-inline-start` to 8px, the default `rightThinnerPadding` modifier still sets `padding-inline-end` to 16px when `renderRight` is absent. This creates an unintended 8px/16px asymmetry for the common “no right element” case and contradicts the migration guide’s stated BaseInput padding (8px start, 4px end).BaseInput reduced base padding-inline-start to 8px, but the default modifier applied when renderRight is missing (rightThinnerPadding) still sets padding-inline-end to 16px. This yields unintended asymmetric padding (8px/16px) in common cases.
rightThinnerPadding is currently applied when !renderRight, i.e., the absence of right content. Previously this likely produced symmetric 16px/16px padding; after the change it produces 8px/16px.
- packages/base/src/BaseInput/BaseInput.module.scss[3-41]
- packages/base/src/BaseInput/BaseInput.tsx[23-33]
- VIBE4_MIGRATION_GUIDE.md[411-446]
[correctness] BaseInput right padding mismatch
BaseInput right padding mismatch
After reducing BaseInput `padding-inline-start` to 8px, the default `rightThinnerPadding` modifier still sets `padding-inline-end` to 16px when `renderRight` is absent. This creates an unintended 8px/16px asymmetry for the common “no right element” case and contradicts the migration guide’s stated BaseInput padding (8px start, 4px end).BaseInput reduced base padding-inline-start to 8px, but the default modifier applied when renderRight is missing (rightThinnerPadding) still sets padding-inline-end to 16px. This yields unintended asymmetric padding (8px/16px) in common cases.
rightThinnerPadding is currently applied when !renderRight, i.e., the absence of right content. Previously this likely produced symmetric 16px/16px padding; after the change it produces 8px/16px.
- packages/base/src/BaseInput/BaseInput.module.scss[3-41]
- packages/base/src/BaseInput/BaseInput.tsx[23-33]
- VIBE4_MIGRATION_GUIDE.md[411-446]
[correctness] BaseInput right padding mismatch
BaseInput right padding mismatch
After reducing BaseInput `padding-inline-start` to 8px, the default `rightThinnerPadding` modifier still sets `padding-inline-end` to 16px when `renderRight` is absent. This creates an unintended 8px/16px asymmetry for the common “no right element” case and contradicts the migration guide’s stated BaseInput padding (8px start, 4px end).BaseInput reduced base padding-inline-start to 8px, but the default modifier applied when renderRight is missing (rightThinnerPadding) still sets padding-inline-end to 16px. This yields unintended asymmetric padding (8px/16px) in common cases.
rightThinnerPadding is currently applied when !renderRight, i.e., the absence of right content. Previously this likely produced symmetric 16px/16px padding; after the change it produces 8px/16px.
- packages/base/src/BaseInput/BaseInput.module.scss[3-41]
- packages/base/src/BaseInput/BaseInput.tsx[23-33]
- VIBE4_MIGRATION_GUIDE.md[411-446]
[correctness] Dropdown placeholder shifts indent
Dropdown placeholder shifts indent
After reducing `.placeholderText` left padding to `--spacing-small` (8px), the selected value in medium/large still starts further right because it’s rendered via `BaseItem` (12px horizontal padding) inside `.selectedItem` (4px left padding). Users will see the text jump horizontally when transitioning from placeholder → selected value in medium/large.In non-searchable single-select Dropdown, placeholder and selected value have different left indents for medium/large sizes after reducing .placeholderText padding. This causes a visible horizontal jump when an option is selected.
- Placeholder is rendered as
&amp;amp;lt;Text&amp;amp;gt;with.placeholderText { padding-inline-start: var(--spacing-small) }. - Selected value is rendered as
&amp;amp;lt;BaseItem size={size}&amp;amp;gt;inside.selectedItem { padding-inline-start: var(--spacing-xs) }. -
BaseItemapplies its own horizontal padding (12px for medium/large).
- packages/core/src/components/Dropdown/components/Trigger/Trigger.module.scss[52-94]
- packages/core/src/components/Dropdown/components/Trigger/SingleSelectTrigger.tsx[45-66]
- packages/core/src/components/BaseItem/BaseItem.module.scss[35-45]
| PR 3285 (2026-02-26) |
[correctness] String children not rendered
String children not rendered
`DialogProps.children` is typed to allow `string`, but non-element children are treated as invalid and the reference/trigger rendering falls back to an empty ``, effectively dropping string children at runtime. This is a type/runtime contract mismatch that can make a Dialog trigger disappear if a consumer passes a text child.DialogProps.children (and RefableProps.children) allow string, but string children are not rendered. This is a type/runtime contract mismatch and can lead to a missing trigger in the UI.
Dialog decides whether to render Refable based on React.isValidElement, and falls back to an empty &lt;span&gt; if no valid elements exist. Refable also returns null for non-elements.
- packages/components/dialog/src/Dialog/Dialog.types.ts[226-231]
- packages/components/dialog/src/Dialog/Dialog.tsx[93-95]
- packages/components/dialog/src/Dialog/Dialog.tsx[519-537]
- packages/components/dialog/src/Dialog/components/Refable/Refable.tsx[20-24]
Choose one:
-
Support string children: treat string/number children as renderable by wrapping them in a
&lt;span&gt;/&lt;div&gt;reference wrapper and rendering the text inside (so the trigger is visible and ref-able). -
Disallow string children: remove
stringfromDialogProps[&#x27;children&#x27;]andRefableProps[&#x27;children&#x27;]and update any docs accordingly. Add a unit test for&lt;Dialog content=...&gt;Plain text&lt;/Dialog&gt;behavior consistent with the chosen approach.
| PR 3274 (2026-02-26) |
[correctness] Codemod overwrites spread props
Codemod overwrites spread props
The v3→v4 codemod may add a new `tooltipProps={{ position: ... }}` even when `tooltipProps` is already provided via a JSX spread, overwriting and dropping the spread-provided `tooltipProps`. This can silently change tooltip behavior after codemod runs.The TextWithHighlight v3→v4 codemod can incorrectly overwrite tooltipProps when tooltipProps is supplied via JSX spread props (&amp;lt;TextWithHighlight {...props} ... /&amp;gt;). Because the codemod only checks for an explicit tooltipProps JSXAttribute, it may append a new explicit tooltipProps={{ position: ... }}, which can drop existing tooltip configuration.
-
isPropExistsonly checksJSXAttributenodes and ignoresJSXSpreadAttribute. - The transformation appends a new
tooltipPropsattribute whenisPropExists(..., &amp;quot;tooltipProps&amp;quot;)is false. - In React/JSX, an explicitly provided prop can override values coming from earlier spreads.
- packages/codemod/transformations/core/v3-to-v4/TextWithHighlight-component-migration.ts[26-65]
- packages/codemod/src/utils/prop-utils.ts[33-37]
- packages/codemod/transformations/core/v3-to-v4/tests/TextWithHighlight-component-migration.test.ts[1-112]
- If the JSX element contains any
JSXSpreadAttribute, the codemod must not silently overwrite potential spread-providedtooltipProps. - Add/adjust tests to cover at least one spread-props scenario and assert the codemod behavior (either safe merge or explicit manual warning + no unsafe overwrite).
| PR 3271 (2026-02-26) |
[correctness] AvatarBadge tabIndex mismatch
AvatarBadge tabIndex mismatch
`ClickableProps.tabIndex` is now `number`-only, but `AvatarBadgeProps.tabIndex` remains `string | number` and is forwarded into `ClickableWrapper` as `clickableProps`, which is typed as `ClickableProps`. This creates a type incompatibility and undermines the intended breaking-change cleanup (forces casts or breaks TS builds).ClickableProps.tabIndex is now number-only, but AvatarBadgeProps.tabIndex still allows string | number and forwards tabIndex into ClickableWrapper via clickableProps, which is typed as ClickableProps. This creates a TS type incompatibility and defeats the goal of removing string tabIndex usage.
This PR introduces a breaking change: Clickable.tabIndex: string | number -&amp;gt; number. Internal components that forward tabIndex into Clickable/ClickableWrapper must be updated accordingly.
- packages/core/src/components/Avatar/AvatarBadge.tsx[13-70]
- packages/components/clickable/src/Clickable/Clickable.tsx[49-60]
- packages/components/clickable/src/Clickable/ClickableWrapper.tsx[6-34]
- packages/docs/src/pages/components/Avatar/Avatar.stories.tsx[23-32]
[correctness] AvatarBadge tabIndex mismatch
AvatarBadge tabIndex mismatch
`ClickableProps.tabIndex` is now `number`-only, but `AvatarBadgeProps.tabIndex` remains `string | number` and is forwarded into `ClickableWrapper` as `clickableProps`, which is typed as `ClickableProps`. This creates a type incompatibility and undermines the intended breaking-change cleanup (forces casts or breaks TS builds).ClickableProps.tabIndex is now number-only, but AvatarBadgeProps.tabIndex still allows string | number and forwards tabIndex into ClickableWrapper via clickableProps, which is typed as ClickableProps. This creates a TS type incompatibility and defeats the goal of removing string tabIndex usage.
This PR introduces a breaking change: Clickable.tabIndex: string | number -&gt; number. Internal components that forward tabIndex into Clickable/ClickableWrapper must be updated accordingly.
- packages/core/src/components/Avatar/AvatarBadge.tsx[13-70]
- packages/components/clickable/src/Clickable/Clickable.tsx[49-60]
- packages/components/clickable/src/Clickable/ClickableWrapper.tsx[6-34]
- packages/docs/src/pages/components/Avatar/Avatar.stories.tsx[23-32]
[correctness] RadioButton uses string tabIndex
RadioButton uses string tabIndex
RadioButton still models/passes its children tab index as a string (default "0") into ``. Since this PR removed `Number(tabIndex)` coercion and narrowed `Clickable.tabIndex` to `number`, RadioButton becomes an internal missed migration and can cause type-check failures or inconsistent focus behavior at runtime.Clickable.tabIndex is now number-only and useClickableProps no longer coerces strings via Number(tabIndex). RadioButton still defines childrenTabIndex as a string (defaulting to &amp;quot;0&amp;quot;) and passes it directly into &amp;lt;Clickable tabIndex={childrenTabIndex}&amp;gt;, which is now incompatible.
This PR intentionally removed string coercion and narrowed types for Clickable. Any remaining internal callers passing string tabIndex values must be migrated.
- packages/core/src/components/RadioButton/RadioButton.tsx[12-106]
- packages/core/src/components/RadioButton/RadioButton.tsx[160-176]
[correctness] RadioButton uses string tabIndex
RadioButton uses string tabIndex
RadioButton still models/passes its children tab index as a string (default "0") into ``. Since this PR removed `Number(tabIndex)` coercion and narrowed `Clickable.tabIndex` to `number`, RadioButton becomes an internal missed migration and can cause type-check failures or inconsistent focus behavior at runtime.Clickable.tabIndex is now number-only and useClickableProps no longer coerces strings via Number(tabIndex). RadioButton still defines childrenTabIndex as a string (defaulting to &quot;0&quot;) and passes it directly into &lt;Clickable tabIndex={childrenTabIndex}&gt;, which is now incompatible.
This PR intentionally removed string coercion and narrowed types for Clickable. Any remaining internal callers passing string tabIndex values must be migrated.
- packages/core/src/components/RadioButton/RadioButton.tsx[12-106]
- packages/core/src/components/RadioButton/RadioButton.tsx[160-176]
[correctness] Docgen output stale
Docgen output stale
The committed `react-docgen-output.json` still documents Clickable `ariaHasPopup` as `boolean | string` and `tabIndex` as `string | number` with default `"0"`, which no longer matches the source after this PR. This will mislead consumers using the docs/playground and contradict the migration guide.The committed docgen artifact used by the docs/playground still reflects the old Clickable prop types (ariaHasPopup: boolean | string, tabIndex: string | number, default tabIndex: &quot;0&quot;). After this breaking change, the documentation is now incorrect.
The source of truth (packages/components/clickable/src/Clickable/Clickable.tsx) now defines ariaHasPopup?: boolean, tabIndex?: number, and tabIndex = 0. The docs/playground artifact should be regenerated to avoid confusing consumers.
- packages/docs/src/pages/playground/react-docgen-output.json[6852-7106]
- packages/components/clickable/src/Clickable/Clickable.tsx[49-92]
- packages/docs/src/pages/components/Avatar/Avatar.stories.tsx[23-32]
[correctness] Docgen output stale
Docgen output stale
The committed `react-docgen-output.json` still documents Clickable `ariaHasPopup` as `boolean | string` and `tabIndex` as `string | number` with default `"0"`, which no longer matches the source after this PR. This will mislead consumers using the docs/playground and contradict the migration guide.The committed docgen artifact used by the docs/playground still reflects the old Clickable prop types (ariaHasPopup: boolean | string, tabIndex: string | number, default tabIndex: "0"). After this breaking change, the documentation is now incorrect.
The source of truth (packages/components/clickable/src/Clickable/Clickable.tsx) now defines ariaHasPopup?: boolean, tabIndex?: number, and tabIndex = 0. The docs/playground artifact should be regenerated to avoid confusing consumers.
- packages/docs/src/pages/playground/react-docgen-output.json[6852-7106]
- packages/components/clickable/src/Clickable/Clickable.tsx[49-92]
- packages/docs/src/pages/components/Avatar/Avatar.stories.tsx[23-32]
[correctness] RadioButton uses string tabIndex
RadioButton uses string tabIndex
RadioButton still models/passes its children tab index as a string (default "0") into ``. Since this PR removed `Number(tabIndex)` coercion and narrowed `Clickable.tabIndex` to `number`, RadioButton becomes an internal missed migration and can cause type-check failures or inconsistent focus behavior at runtime.Clickable.tabIndex is now number-only and useClickableProps no longer coerces strings via Number(tabIndex). RadioButton still defines childrenTabIndex as a string (defaulting to &amp;amp;quot;0&amp;amp;quot;) and passes it directly into &amp;amp;lt;Clickable tabIndex={childrenTabIndex}&amp;amp;gt;, which is now incompatible.
This PR intentionally removed string coercion and narrowed types for Clickable. Any remaining internal callers passing string tabIndex values must be migrated.
- packages/core/src/components/RadioButton/RadioButton.tsx[12-106]
- packages/core/src/components/RadioButton/RadioButton.tsx[160-176]
[correctness] RadioButton uses string tabIndex
RadioButton uses string tabIndex
RadioButton still models/passes its children tab index as a string (default "0") into ``. Since this PR removed `Number(tabIndex)` coercion and narrowed `Clickable.tabIndex` to `number`, RadioButton becomes an internal missed migration and can cause type-check failures or inconsistent focus behavior at runtime.Clickable.tabIndex is now number-only and useClickableProps no longer coerces strings via Number(tabIndex). RadioButton still defines childrenTabIndex as a string (defaulting to &amp;quot;0&amp;quot;) and passes it directly into &amp;lt;Clickable tabIndex={childrenTabIndex}&amp;gt;, which is now incompatible.
This PR intentionally removed string coercion and narrowed types for Clickable. Any remaining internal callers passing string tabIndex values must be migrated.
- packages/core/src/components/RadioButton/RadioButton.tsx[12-106]
- packages/core/src/components/RadioButton/RadioButton.tsx[160-176]
| PR 2950 (2025-06-25) |
[general] Remove conflicting accessibility attributes
✅ Remove conflicting accessibility attributes
Setting aria-hidden to true while also providing ariaLabel creates conflicting accessibility signals. Remove aria-hidden since these buttons need to be accessible to screen readers for proper spinbutton functionality.
<IconButton
- aria-hidden
ariaLabel="Increase"
hideTooltip
ariaControls={inputId}
tabIndex={-1}
onClick={onIncrement}
disabled={disabled || isAtMax}
size={iconButtonSize}
icon={DropdownChevronUp}
/>
<IconButton
- aria-hidden
ariaLabel="Decrease"
hideTooltip
ariaControls={inputId}
tabIndex={-1}
onClick={onDecrement}
disabled={disabled || isAtMin}
size={iconButtonSize}
icon={DropdownChevronDown}
/>Suggestion importance[1-10]: 9
__
Why: This suggestion correctly identifies a critical accessibility issue. Using aria-hidden on the increment and decrement buttons makes them completely inaccessible to screen readers, which contradicts the presence of ariaLabel. Removing aria-hidden is essential for the component to be usable by people relying on assistive technologies.