diff --git a/.changeset/cruel-plums-talk.md b/.changeset/cruel-plums-talk.md
new file mode 100644
index 0000000000..bc7a30b048
--- /dev/null
+++ b/.changeset/cruel-plums-talk.md
@@ -0,0 +1,58 @@
+---
+'@backstage/ui': minor
+---
+
+**BREAKING**: Migrated Checkbox component from Base UI to React Aria Components.
+
+API changes required:
+
+- `checked` → `isSelected`
+- `defaultChecked` → `defaultSelected`
+- `disabled` → `isDisabled`
+- `required` → `isRequired`
+- `label` prop removed - use `children` instead
+- CSS: `bui-CheckboxLabel` class removed
+- Data attribute: `data-checked` → `data-selected`
+- Use without label is no longer supported
+
+Migration examples:
+
+Before:
+
+```tsx
+
+```
+
+After:
+
+```tsx
+
+ Accept terms
+
+```
+
+Before:
+
+```tsx
+
+```
+
+After:
+
+```tsx
+Option
+```
+
+Before:
+
+```tsx
+
+```
+
+After:
+
+```tsx
+
+ Accessible label
+
+```
diff --git a/.changeset/upset-teeth-add.md b/.changeset/upset-teeth-add.md
new file mode 100644
index 0000000000..834ce7be2c
--- /dev/null
+++ b/.changeset/upset-teeth-add.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-mui-to-bui': patch
+---
+
+Updated BUI checkbox preview example to align with new component API.
diff --git a/docs-ui/src/content/components/checkbox.props.ts b/docs-ui/src/content/components/checkbox.props.ts
index c7ee69280e..ce0f1033ff 100644
--- a/docs-ui/src/content/components/checkbox.props.ts
+++ b/docs-ui/src/content/components/checkbox.props.ts
@@ -5,31 +5,32 @@ import {
} from '@/utils/propDefs';
export const checkboxPropDefs: Record = {
- label: {
- type: 'string',
- responsive: false,
- },
- defaultChecked: {
+ children: {
type: 'enum',
- values: ['boolean', "'indeterminate'"],
+ values: ['React.ReactNode'],
responsive: false,
},
- checked: {
- type: 'enum',
- values: ['boolean', "'indeterminate'"],
- responsive: false,
- },
- onChange: {
- type: 'enum',
- values: ["(checked: boolean | 'indeterminate') => void"],
- responsive: false,
- },
- disabled: {
+ isSelected: {
type: 'enum',
values: ['boolean'],
responsive: false,
},
- required: {
+ defaultSelected: {
+ type: 'enum',
+ values: ['boolean'],
+ responsive: false,
+ },
+ onChange: {
+ type: 'enum',
+ values: ['(isSelected: boolean) => void'],
+ responsive: false,
+ },
+ isDisabled: {
+ type: 'enum',
+ values: ['boolean'],
+ responsive: false,
+ },
+ isRequired: {
type: 'enum',
values: ['boolean'],
responsive: false,
@@ -48,13 +49,13 @@ export const checkboxPropDefs: Record = {
export const checkboxUsageSnippet = `import { Checkbox } from '@backstage/ui';
-`;
+Accept terms`;
-export const checkboxDefaultSnippet = ``;
+export const checkboxDefaultSnippet = `Accept terms and conditions`;
-export const checkboxVariantsSnippet = `
-
-
-
-
-`;
+export const checkboxVariantsSnippet = `
+ Unchecked
+ Checked
+ Disabled
+ Checked & Disabled
+`;
diff --git a/packages/ui/report.api.md b/packages/ui/report.api.md
index 4e762f431c..7cdee27d0e 100644
--- a/packages/ui/report.api.md
+++ b/packages/ui/report.api.md
@@ -6,6 +6,7 @@
import { Avatar as Avatar_2 } from '@base-ui-components/react/avatar';
import { ButtonProps as ButtonProps_2 } from 'react-aria-components';
import { CellProps as CellProps_2 } from 'react-aria-components';
+import { CheckboxProps as CheckboxProps_2 } from 'react-aria-components';
import { Collapsible as Collapsible_2 } from '@base-ui-components/react/collapsible';
import { ColumnProps as ColumnProps_2 } from 'react-aria-components';
import { ComponentProps } from 'react';
@@ -270,31 +271,13 @@ export interface CellProps extends CellProps_2 {
// @public (undocumented)
export const Checkbox: ForwardRefExoticComponent<
- CheckboxProps & RefAttributes
+ CheckboxProps & RefAttributes
>;
// @public (undocumented)
-export interface CheckboxProps {
+export interface CheckboxProps extends CheckboxProps_2 {
// (undocumented)
- checked?: boolean;
- // (undocumented)
- className?: string;
- // (undocumented)
- defaultChecked?: boolean;
- // (undocumented)
- disabled?: boolean;
- // (undocumented)
- label?: string;
- // (undocumented)
- name?: string;
- // (undocumented)
- onChange?: (checked: boolean) => void;
- // (undocumented)
- required?: boolean;
- // (undocumented)
- style?: React.CSSProperties;
- // (undocumented)
- value?: string;
+ children: React.ReactNode;
}
// @public
@@ -431,12 +414,11 @@ export const componentDefinitions: {
};
readonly Checkbox: {
readonly classNames: {
- readonly root: 'bui-CheckboxRoot';
- readonly label: 'bui-CheckboxLabel';
+ readonly root: 'bui-Checkbox';
readonly indicator: 'bui-CheckboxIndicator';
};
readonly dataAttributes: {
- readonly checked: readonly [true, false];
+ readonly selected: readonly [true, false];
};
};
readonly Collapsible: {
diff --git a/packages/ui/src/components/Checkbox/Checkbox.module.css b/packages/ui/src/components/Checkbox/Checkbox.module.css
index 2e08b08c1a..bc4f700bce 100644
--- a/packages/ui/src/components/Checkbox/Checkbox.module.css
+++ b/packages/ui/src/components/Checkbox/Checkbox.module.css
@@ -17,35 +17,7 @@
@layer tokens, base, components, utilities;
@layer components {
- .bui-CheckboxRoot {
- border: none;
- display: flex;
- align-items: center;
- justify-content: center;
- width: 1rem;
- height: 1rem;
- box-shadow: inset 0 0 0 1px var(--bui-border);
- cursor: pointer;
- border-radius: 2px;
- transition: background-color 0.2s ease-in-out;
- background-color: var(--bui-bg-surface-1);
- padding: 0;
- flex-shrink: 0;
- }
-
- .bui-CheckboxRoot:focus-visible {
- transition: none;
- outline: 2px solid var(--bui-ring);
- outline-offset: 2px;
- }
-
- .bui-CheckboxRoot[data-checked] {
- background-color: var(--bui-bg-solid);
- box-shadow: none;
- color: var(--bui-fg-solid);
- }
-
- .bui-CheckboxLabel {
+ .bui-Checkbox {
display: flex;
flex-direction: row;
align-items: center;
@@ -55,18 +27,48 @@
font-weight: var(--bui-font-weight-regular);
color: var(--bui-fg-primary);
user-select: none;
+ cursor: pointer;
+ }
- &:hover {
- & .bui-CheckboxRoot:not([data-checked]) {
- box-shadow: inset 0 0 0 1px var(--bui-border-hover);
+ .bui-Checkbox[data-disabled] {
+ cursor: not-allowed;
+ opacity: 0.5;
+ }
+
+ .bui-CheckboxIndicator {
+ border: none;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 1rem;
+ height: 1rem;
+ box-shadow: inset 0 0 0 1px var(--bui-border);
+ border-radius: 2px;
+ transition: background-color 0.2s ease-in-out;
+ background-color: var(--bui-bg-surface-1);
+ padding: 0;
+ flex-shrink: 0;
+ color: var(--bui-fg-solid);
+
+ @media (prefers-reduced-motion: reduce) {
+ & {
+ transition: none;
}
}
}
- .bui-CheckboxIndicator {
- display: flex;
- align-items: center;
- justify-content: center;
- color: var(--bui-fg-solid);
+ .bui-Checkbox[data-focus-visible] .bui-CheckboxIndicator {
+ transition: none;
+ outline: 2px solid var(--bui-ring);
+ outline-offset: 2px;
+ }
+
+ .bui-Checkbox[data-selected] .bui-CheckboxIndicator {
+ background-color: var(--bui-bg-solid);
+ box-shadow: none;
+ }
+
+ .bui-Checkbox[data-hovered]:not([data-selected]) .bui-CheckboxIndicator {
+ box-shadow: inset 0 0 0 1px var(--bui-border-hover);
}
}
diff --git a/packages/ui/src/components/Checkbox/Checkbox.stories.tsx b/packages/ui/src/components/Checkbox/Checkbox.stories.tsx
index 080cae40b6..a41b7f2c8d 100644
--- a/packages/ui/src/components/Checkbox/Checkbox.stories.tsx
+++ b/packages/ui/src/components/Checkbox/Checkbox.stories.tsx
@@ -17,7 +17,6 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { Checkbox } from './Checkbox';
import { Flex } from '../Flex';
-import { Text } from '../Text';
const meta = {
title: 'Backstage UI/Checkbox',
@@ -29,31 +28,20 @@ type Story = StoryObj;
export const Default: Story = {
args: {
- label: 'Accept terms and conditions',
+ children: 'Accept terms and conditions',
},
};
export const AllVariants: Story = {
+ ...Default,
render: () => (
-
-
-
-
-
-
- ),
-};
-
-export const Playground: Story = {
- render: () => (
-
- All variants
-
-
-
-
-
-
+
+ Unchecked
+ Checked
+ Disabled
+
+ Checked & Disabled
+
),
};
diff --git a/packages/ui/src/components/Checkbox/Checkbox.tsx b/packages/ui/src/components/Checkbox/Checkbox.tsx
index 2414f29f5f..49019370a4 100644
--- a/packages/ui/src/components/Checkbox/Checkbox.tsx
+++ b/packages/ui/src/components/Checkbox/Checkbox.tsx
@@ -15,7 +15,7 @@
*/
import { forwardRef } from 'react';
-import { Checkbox as CheckboxPrimitive } from '@base-ui-components/react/checkbox';
+import { Checkbox as RACheckbox } from 'react-aria-components';
import type { CheckboxProps } from './types';
import { useStyles } from '../../hooks/useStyles';
import clsx from 'clsx';
@@ -23,33 +23,24 @@ import styles from './Checkbox.module.css';
import { RiCheckLine } from '@remixicon/react';
/** @public */
-export const Checkbox = forwardRef(
+export const Checkbox = forwardRef(
(props, ref) => {
- const { classNames, cleanedProps } = useStyles('Checkbox', props);
- const { label, onChange, className, ...rest } = cleanedProps;
+ const { classNames } = useStyles('Checkbox');
+ const { className, children, ...rest } = props;
- const checkboxElement = (
-
-
-
-
- );
-
- return label ? (
-
- ) : (
- checkboxElement
+
+ {children}
+
);
},
);
diff --git a/packages/ui/src/components/Checkbox/types.ts b/packages/ui/src/components/Checkbox/types.ts
index 0240954afa..86e4b620b8 100644
--- a/packages/ui/src/components/Checkbox/types.ts
+++ b/packages/ui/src/components/Checkbox/types.ts
@@ -13,17 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+import { CheckboxProps as RACheckboxProps } from 'react-aria-components';
/** @public */
-export interface CheckboxProps {
- label?: string;
- defaultChecked?: boolean;
- checked?: boolean;
- onChange?: (checked: boolean) => void;
- disabled?: boolean;
- required?: boolean;
- className?: string;
- name?: string;
- value?: string;
- style?: React.CSSProperties;
+export interface CheckboxProps extends RACheckboxProps {
+ children: React.ReactNode;
}
diff --git a/packages/ui/src/utils/componentDefinitions.ts b/packages/ui/src/utils/componentDefinitions.ts
index 4f914cd1f9..e9ba832bf8 100644
--- a/packages/ui/src/utils/componentDefinitions.ts
+++ b/packages/ui/src/utils/componentDefinitions.ts
@@ -89,12 +89,11 @@ export const componentDefinitions = {
},
Checkbox: {
classNames: {
- root: 'bui-CheckboxRoot',
- label: 'bui-CheckboxLabel',
+ root: 'bui-Checkbox',
indicator: 'bui-CheckboxIndicator',
},
dataAttributes: {
- checked: [true, false] as const,
+ selected: [true, false] as const,
},
},
Collapsible: {
diff --git a/plugins/mui-to-bui/src/components/BuiThemerPage/BuiThemePreview.tsx b/plugins/mui-to-bui/src/components/BuiThemerPage/BuiThemePreview.tsx
index e59f4e78dd..0b62134178 100644
--- a/plugins/mui-to-bui/src/components/BuiThemerPage/BuiThemePreview.tsx
+++ b/plugins/mui-to-bui/src/components/BuiThemerPage/BuiThemePreview.tsx
@@ -92,7 +92,7 @@ export function BuiThemePreview({ mode, styleObject }: IsolatedPreviewProps) {
{ value: 'option3', label: 'Option 3' },
]}
/>
-
+ Checkbox Option
Option 1
Option 2