Repository: radix-ui/primitives (ts)

Issue:
@radix-ui/one-time-password-field: prop `type` ('text' | 'password') do nothing
## Bug report

### Current Behavior

Setting `type` prop on a `<OneTimePasswordField.Root>` component do nothing.

### Expected behavior

`type` property changes the representation of the symbols inside input:
- `text` - current behavior;
- `password` - dots (•) are shown instead of characters.

### Reproducible example

https://codesandbox.io/p/sandbox/red-dream-87jv8y

### Suggested solution

`type` property should belongs to `<OneTimePasswordField.Input />` and sets the type of the generated `<input>`.

### Your environment

<!-- Very important for us to help you debug. Please fill this out! -->

| Software         | Name(s) | Version |
| ---------------- | ------- | ------- |
| Radix Package(s) |    @radix-ui/react-one-time-password-field     |  0.1.3       |
| React            | n/a     |  19       |


Pull request description:
fix: when specifying the type of One-Time Password Field, the input is displayed as pw.
<!--

Thank you for contributing! Please follow the steps below to help us process your PR quickly.

- 📝 Use a meaningful title for the pull request and include the name of the package modified.
- ✅ Add or edit tests to reflect the change (run `pnpm test`).
- 🔍 Add or edit Storybook examples to reflect the change (run `pnpm dev`).
- 🙏 Please review your own PR to check for anything you may have missed.

-->

### Description

close #3493 

<!-- Describe the change you are introducing -->
- As stated in the [documentation](https://www.radix-ui.com/primitives/docs/components/one-time-password-field#root), change default `type` to text.
- Connect a type to input to display the input value in a different way when the type changes.
- Add test for this feature

<img width="796" alt="스크린샷 2025-04-28 오후 9 20 30" src="https://github.com/user-attachments/assets/ec35e71f-5a89-4e3c-a68f-102fa117968d" />

<img width="790" alt="스크린샷 2025-04-28 오후 9 20 39" src="https://github.com/user-attachments/assets/2e6deb09-5417-4b72-bfbc-eb3bf3304c0f" />



Patch:
diff --git a/.changeset/fluffy-days-kneel.md b/.changeset/fluffy-days-kneel.md
new file mode 100644
index 00000000..da958817
--- /dev/null
+++ b/.changeset/fluffy-days-kneel.md
@@ -0,0 +1,10 @@
+---
+'@radix-ui/react-one-time-password-field': patch
+---
+
+
+
+- As stated in the [documentation](https://www.radix-ui.com/primitives/docs/components/one-time-password-field#root), change default to text.
+- Connect a type to input to display the input value in a different way when the type changes.
+- Add test for this feature
+
diff --git a/.changeset/heavy-rockets-allow.md b/.changeset/heavy-rockets-allow.md
new file mode 100644
index 00000000..642be586
--- /dev/null
+++ b/.changeset/heavy-rockets-allow.md
@@ -0,0 +1,6 @@
+---
+'@radix-ui/react-checkbox': minor
+'radix-ui': minor
+---
+
+Add unstable `Provider`, `Trigger` and `BubbleInput` parts to Checkbox (#3459)
diff --git a/.changeset/icy-areas-poke.md b/.changeset/icy-areas-poke.md
new file mode 100644
index 00000000..32365657
--- /dev/null
+++ b/.changeset/icy-areas-poke.md
@@ -0,0 +1,5 @@
+---
+'@radix-ui/react-radio-group': patch
+---
+
+Allow passing value of `null` to controlled Radio Group when all items are unchecked
diff --git a/apps/storybook/stories/checkbox.stories.tsx b/apps/storybook/stories/checkbox.stories.tsx
index e280a754..103e6a65 100644
--- a/apps/storybook/stories/checkbox.stories.tsx
+++ b/apps/storybook/stories/checkbox.stories.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable react/jsx-pascal-case */
 import * as React from 'react';
 import { Checkbox, Label as LabelPrimitive } from 'radix-ui';
 import styles from './checkbox.stories.module.css';
@@ -5,6 +6,194 @@ import styles from './checkbox.stories.module.css';
 export default { title: 'Components/Checkbox' };
 
 export const Styled = () => (
+  <>
+    <p>This checkbox is nested inside a label. The state is uncontrolled.</p>
+
+    <h1>Custom label</h1>
+    <Label>
+      Label{' '}
+      <Checkbox.unstable_Provider>
+        <Checkbox.unstable_Trigger className={styles.root}>
+          <Checkbox.Indicator className={styles.indicator} />
+        </Checkbox.unstable_Trigger>
+      </Checkbox.unstable_Provider>
+    </Label>
+
+    <br />
+    <br />
+
+    <h1>Native label</h1>
+    <label>
+      Label{' '}
+      <Checkbox.unstable_Provider>
+        <Checkbox.unstable_Trigger className={styles.root}>
+          <Checkbox.Indicator className={styles.indicator} />
+        </Checkbox.unstable_Trigger>
+      </Checkbox.unstable_Provider>
+    </label>
+
+    <h1>Native label + native checkbox</h1>
+    <label>
+      Label <input type="checkbox" />
+    </label>
+
+    <h1>Custom label + htmlFor</h1>
+    <Label htmlFor="one">Label</Label>
+    <Checkbox.unstable_Provider>
+      <Checkbox.unstable_Trigger className={styles.root} id="one">
+        <Checkbox.Indicator className={styles.indicator} />
+      </Checkbox.unstable_Trigger>
+    </Checkbox.unstable_Provider>
+
+    <br />
+    <br />
+
+    <h1>Native label + htmlFor</h1>
+    <label htmlFor="two">Label</label>
+    <Checkbox.unstable_Provider>
+      <Checkbox.unstable_Trigger className={styles.root} id="two">
+        <Checkbox.Indicator className={styles.indicator} />
+      </Checkbox.unstable_Trigger>
+    </Checkbox.unstable_Provider>
+
+    <h1>Native label + native checkbox</h1>
+    <label htmlFor="three">Label</label>
+    <input type="checkbox" id="three" />
+  </>
+);
+
+export const Controlled = () => {
+  const [checked, setChecked] = React.useState<boolean | 'indeterminate'>(true);
+
+  return (
+    <>
+      <p>This checkbox is placed adjacent to its label. The state is controlled.</p>
+      <Label htmlFor="randBox">Label</Label>{' '}
+      <Checkbox.unstable_Provider checked={checked} onCheckedChange={setChecked}>
+        <Checkbox.unstable_Trigger className={styles.root} id="randBox">
+          <Checkbox.Indicator className={styles.indicator} />
+        </Checkbox.unstable_Trigger>
+      </Checkbox.unstable_Provider>
+    </>
+  );
+};
+
+export const Indeterminate = () => {
+  const [checked, setChecked] = React.useState<boolean | 'indeterminate'>('indeterminate');
+
+  return (
+    <>
+      <p>
+        <Checkbox.unstable_Provider checked={checked} onCheckedChange={setChecked}>
+          <Checkbox.unstable_Trigger className={styles.root}>
+            <Checkbox.Indicator className={styles.indicator} />
+          </Checkbox.unstable_Trigger>
+        </Checkbox.unstable_Provider>
+      </p>
+
+      <button
+        type="button"
+        onClick={() =>
+          setChecked((prevIsChecked) =>
+            prevIsChecked === 'indeterminate' ? false : 'indeterminate'
+          )
+        }
+      >
+        Toggle indeterminate
+      </button>
+    </>
+  );
+};
+
+export const WithinForm = () => {
+  const [data, setData] = React.useState({ optional: false, required: false, stopprop: false });
+  const [checked, setChecked] = React.useState<boolean | 'indeterminate'>('indeterminate');
+
+  return (
+    <form
+      onSubmit={(event) => event.preventDefault()}
+      onChange={(event) => {
+        const input = event.target as HTMLInputElement;
+        setData((prevData) => ({ ...prevData, [input.name]: input.checked }));
+      }}
+    >
+      <fieldset>
+        <legend>optional checked: {String(data.optional)}</legend>
+        <label>
+          <Checkbox.Root
+            className={styles.root}
+            name="optional"
+            checked={checked}
+            onCheckedChange={setChecked}
+          >
+            <Checkbox.Indicator className={styles.indicator} />
+          </Checkbox.Root>{' '}
+          with label
+        </label>
+        <br />
+        <br />
+
+        <button
+          type="button"
+          onClick={() => {
+            setChecked((v) => (v === 'indeterminate' ? false : 'indeterminate'));
+          }}
+        >
+          Toggle indeterminate
+        </button>
+      </fieldset>
+
+      <br />
+      <br />
+
+      <fieldset>
+        <legend>required checked: {String(data.required)}</legend>
+        <Checkbox.unstable_Provider name="required" required>
+          <Checkbox.unstable_Trigger className={styles.root}>
+            <Checkbox.Indicator className={styles.indicator} />
+          </Checkbox.unstable_Trigger>
+          <Checkbox.unstable_BubbleInput />
+        </Checkbox.unstable_Provider>
+      </fieldset>
+
+      <br />
+      <br />
+
+      <fieldset>
+        <legend>stop propagation checked: {String(data.stopprop)}</legend>
+        <Checkbox.unstable_Provider name="stopprop">
+          <Checkbox.unstable_Trigger
+            className={styles.root}
+            onClick={(event) => event.stopPropagation()}
+          >
+            <Checkbox.Indicator className={styles.indicator} />
+          </Checkbox.unstable_Trigger>
+          <Checkbox.unstable_BubbleInput />
+        </Checkbox.unstable_Provider>
+      </fieldset>
+
+      <br />
+      <br />
+
+      <fieldset>
+        <legend>no bubble input checked: {String(data.stopprop)}</legend>
+        <Checkbox.unstable_Provider name="stopprop">
+          <Checkbox.unstable_Trigger className={styles.root}>
+            <Checkbox.Indicator className={styles.indicator} />
+          </Checkbox.unstable_Trigger>
+        </Checkbox.unstable_Provider>
+      </fieldset>
+
+      <br />
+      <br />
+
+      <button type="reset">Reset</button>
+      <button>Submit</button>
+    </form>
+  );
+};
+
+export const LegacyStyled = () => (
   <>
     <p>This checkbox is nested inside a label. The state is uncontrolled.</p>
 
@@ -53,7 +242,7 @@ export const Styled = () => (
   </>
 );
 
-export const Controlled = () => {
+export const LegacyControlled = () => {
   const [checked, setChecked] = React.useState<boolean | 'indeterminate'>(true);
 
   return (
@@ -72,7 +261,7 @@ export const Controlled = () => {
   );
 };
 
-export const Indeterminate = () => {
+export const LegacyIndeterminate = () => {
   const [checked, setChecked] = React.useState<boolean | 'indeterminate'>('indeterminate');
 
   return (
@@ -97,7 +286,7 @@ export const Indeterminate = () => {
   );
 };
 
-export const WithinForm = () => {
+export const LegacyWithinForm = () => {
   const [data, setData] = React.useState({ optional: false, required: false, stopprop: false });
   const [checked, setChecked] = React.useState<boolean | 'indeterminate'>('indeterminate');
 
@@ -170,7 +359,7 @@ export const WithinForm = () => {
   );
 };
 
-export const Animated = () => {
+export const LegacyAnimated = () => {
   const [checked, setChecked] = React.useState<boolean | 'indeterminate'>('indeterminate');
 
   return (
@@ -195,7 +384,7 @@ export const Animated = () => {
   );
 };
 
-export const Chromatic = () => (
+export const LegacyChromatic = () => (
   <>
     <h1>Uncontrolled</h1>
     <h2>Unchecked</h2>
@@ -261,6 +450,6 @@ export const Chromatic = () => (
     </Checkbox.Root>
   </>
 );
-Chromatic.parameters = { chromatic: { disable: false } };
+LegacyChromatic.parameters = { chromatic: { disable: false } };
 
 const Label = (props: any) => <LabelPrimitive.Root {...props} className={styles.label} />;
diff --git a/apps/storybook/stories/radio-group.stories.tsx b/apps/storybook/stories/radio-group.stories.tsx
index 518b44a3..77ba9565 100644
--- a/apps/storybook/stories/radio-group.stories.tsx
+++ b/apps/storybook/stories/radio-group.stories.tsx
@@ -4,7 +4,7 @@ import styles from './radio-group.stories.module.css';
 
 export default { title: 'Components/RadioGroup' };
 
-export const Styled = () => (
+export const LegacyStyled = () => (
   <Label>
     Favourite pet
     <RadioGroup.Root className={styles.root} defaultValue="1">
@@ -30,7 +30,7 @@ export const Styled = () => (
   </Label>
 );
 
-export const Controlled = () => {
+export const LegacyControlled = () => {
   const [value, setValue] = React.useState('2');
 
   return (
@@ -48,7 +48,7 @@ export const Controlled = () => {
   );
 };
 
-export const Unset = () => (
+export const LegacyUnset = () => (
   <Label>
     Favourite pet
     <RadioGroup.Root className={styles.root}>
@@ -74,7 +74,7 @@ export const Unset = () => (
   </Label>
 );
 
-export const WithinForm = () => {
+export const LegacyWithinForm = () => {
   const [data, setData] = React.useState({ optional: '', required: '', stopprop: '' });
 
   return (
@@ -156,7 +156,7 @@ export const WithinForm = () => {
   );
 };
 
-export const Animated = () => {
+export const LegacyAnimated = () => {
   const indicatorClass = [styles.indicator, styles.animatedIndicator].join(' ');
   return (
     <Label>
@@ -185,7 +185,7 @@ export const Animated = () => {
   );
 };
 
-export const Chromatic = () => {
+export const LegacyChromatic = () => {
   const manualFocusRef = React.useRef<React.ElementRef<typeof RadioGroup.Item>>(null);
 
   React.useEffect(() => {
@@ -223,7 +223,7 @@ export const Chromatic = () => {
 
       <h1>Controlled</h1>
       <h2>Unset</h2>
-      <RadioGroup.Root className={styles.root} value="">
+      <RadioGroup.Root className={styles.root} value={null}>
         <RadioGroup.Item className={styles.item} value="1">
           <RadioGroup.Indicator className={styles.indicator} />
         </RadioGroup.Item>
@@ -411,6 +411,6 @@ export const Chromatic = () => {
     </>
   );
 };
-Chromatic.parameters = { chromatic: { disable: false } };
+LegacyChromatic.parameters = { chromatic: { disable: false } };
 
 const Label = (props: any) => <LabelPrimitive.Root {...props} className={styles.label} />;
diff --git a/packages/react/checkbox/src/checkbox.tsx b/packages/react/checkbox/src/checkbox.tsx
index 000fcea5..ae0cb621 100644
--- a/packages/react/checkbox/src/checkbox.tsx
+++ b/packages/react/checkbox/src/checkbox.tsx
@@ -10,10 +10,6 @@ import { Primitive } from '@radix-ui/react-primitive';
 
 import type { Scope } from '@radix-ui/react-context';
 
-/* -------------------------------------------------------------------------------------------------
- * Checkbox
- * -----------------------------------------------------------------------------------------------*/
-
 const CHECKBOX_NAME = 'Checkbox';
 
 type ScopedProps<P> = P & { __scopeCheckbox?: Scope };
@@ -21,14 +17,179 @@ const [createCheckboxContext, createCheckboxScope] = createContextScope(CHECKBOX
 
 type CheckedState = boolean | 'indeterminate';
 
-type CheckboxContextValue = {
-  state: CheckedState;
-  disabled?: boolean;
+type CheckboxContextValue<State extends CheckedState | boolean = CheckedState> = {
+  checked: State | boolean;
+  setChecked: React.Dispatch<React.SetStateAction<State | boolean>>;
+  disabled: boolean | undefined;
+  control: HTMLButtonElement | null;
+  setControl: React.Dispatch<React.SetStateAction<HTMLButtonElement | null>>;
+  name: string | undefined;
+  form: string | undefined;
+  value: string | number | readonly string[];
+  hasConsumerStoppedPropagationRef: React.RefObject<boolean>;
+  required: boolean | undefined;
+  defaultChecked: boolean | undefined;
+  isFormControl: boolean;
+  bubbleInput: HTMLInputElement | null;
+  setBubbleInput: React.Dispatch<React.SetStateAction<HTMLInputElement | null>>;
 };
 
-const [CheckboxProvider, useCheckboxContext] =
+const [CheckboxProviderImpl, useCheckboxContext] =
   createCheckboxContext<CheckboxContextValue>(CHECKBOX_NAME);
 
+/* -------------------------------------------------------------------------------------------------
+ * CheckboxProvider
+ * -----------------------------------------------------------------------------------------------*/
+
+interface CheckboxProviderProps<State extends CheckedState = CheckedState> {
+  checked?: State | boolean;
+  defaultChecked?: State | boolean;
+  required?: boolean;
+  onCheckedChange?(checked: State | boolean): void;
+  name?: string;
+  form?: string;
+  disabled?: boolean;
+  value?: string | number | readonly string[];
+  children?: React.ReactNode;
+}
+
+function CheckboxProvider<State extends CheckedState = CheckedState>({
+  __scopeCheckbox,
+  checked: checkedProp,
+  children,
+  defaultChecked,
+  disabled,
+  form,
+  name,
+  onCheckedChange,
+  required,
+  value = 'on',
+  // @ts-expect-error
+  internal_do_not_use_render,
+}: ScopedProps<CheckboxProviderProps<State>>) {
+  const [checked, setChecked] = useControllableState({
+    prop: checkedProp,
+    defaultProp: defaultChecked ?? false,
+    onChange: onCheckedChange,
+    caller: CHECKBOX_NAME,
+  });
+  const [control, setControl] = React.useState<HTMLButtonElement | null>(null);
+  const [bubbleInput, setBubbleInput] = React.useState<HTMLInputElement | null>(null);
+  const hasConsumerStoppedPropagationRef = React.useRef(false);
+  const isFormControl = control
+    ? !!form || !!control.closest('form')
+    : // We set this to true by default so that events bubble to forms without JS (SSR)
+      true;
+
+  const context: CheckboxContextValue<State> = {
+    checked: checked,
+    disabled: disabled,
+    setChecked: setChecked,
+    control: control,
+    setControl: setControl,
+    name: name,
+    form: form,
+    value: value,
+    hasConsumerStoppedPropagationRef: hasConsumerStoppedPropagationRef,
+    required: required,
+    defaultChecked: isIndeterminate(defaultChecked) ? false : defaultChecked,
+    isFormControl: isFormControl,
+    bubbleInput,
+    setBubbleInput,
+  };
+
+  return (
+    <CheckboxProviderImpl
+      scope={__scopeCheckbox}
+      {...(context as unknown as CheckboxContextValue<CheckedState>)}
+    >
+      {isFunction(internal_do_not_use_render) ? internal_do_not_use_render(context) : children}
+    </CheckboxProviderImpl>
+  );
+}
+
+/* -------------------------------------------------------------------------------------------------
+ * CheckboxTrigger
+ * -----------------------------------------------------------------------------------------------*/
+
+const TRIGGER_NAME = 'CheckboxTrigger';
+
+interface CheckboxTriggerProps
+  extends Omit<
+    React.ComponentPropsWithoutRef<typeof Primitive.button>,
+    keyof CheckboxProviderProps
+  > {
+  children?: React.ReactNode;
+}
+
+const CheckboxTrigger = React.forwardRef<HTMLButtonElement, CheckboxTriggerProps>(
+  (
+    { __scopeCheckbox, onKeyDown, onClick, ...checkboxProps }: ScopedProps<CheckboxTriggerProps>,
+    forwardedRef
+  ) => {
+    const {
+      control,
+      value,
+      disabled,
+      checked,
+      required,
+      setControl,
+      setChecked,
+      hasConsumerStoppedPropagationRef,
+      isFormControl,
+      bubbleInput,
+    } = useCheckboxContext(TRIGGER_NAME, __scopeCheckbox);
+    const composedRefs = useComposedRefs(forwardedRef, setControl);
+
+    const initialCheckedStateRef = React.useRef(checked);
+    React.useEffect(() => {
+      const form = control?.form;
+      if (form) {
+        const reset = () => setChecked(initialCheckedStateRef.current);
+        form.addEventListener('reset', reset);
+        return () => form.removeEventListener('reset', reset);
+      }
+    }, [control, setChecked]);
+
+    return (
+      <Primitive.button
+        type="button"
+        role="checkbox"
+        aria-checked={isIndeterminate(checked) ? 'mixed' : checked}
+        aria-required={required}
+        data-state={getState(checked)}
+        data-disabled={disabled ? '' : undefined}
+        disabled={disabled}
+        value={value}
+        {...checkboxProps}
+        ref={composedRefs}
+        onKeyDown={composeEventHandlers(onKeyDown, (event) => {
+          // According to WAI ARIA, Checkboxes don't activate on enter keypress
+          if (event.key === 'Enter') event.preventDefault();
+        })}
+        onClick={composeEventHandlers(onClick, (event) => {
+          setChecked((prevChecked) => (isIndeterminate(prevChecked) ? true : !prevChecked));
+          if (bubbleInput && isFormControl) {
+            hasConsumerStoppedPropagationRef.current = event.isPropagationStopped();
+            // if checkbox has a bubble input and is a form control, stop
+            // propagation from the button so that we only propagate one click
+            // event (from the input). We propagate changes from an input so
+            // that native form validation works and form events reflect
+            // checkbox updates.
+            if (!hasConsumerStoppedPropagationRef.current) event.stopPropagation();
+          }
+        })}
+      />
+    );
+  }
+);
+
+CheckboxTrigger.displayName = TRIGGER_NAME;
+
+/* -------------------------------------------------------------------------------------------------
+ * Checkbox
+ * -----------------------------------------------------------------------------------------------*/
+
 type CheckboxElement = React.ElementRef<typeof Primitive.button>;
 type PrimitiveButtonProps = React.ComponentPropsWithoutRef<typeof Primitive.button>;
 interface CheckboxProps extends Omit<PrimitiveButtonProps, 'checked' | 'defaultChecked'> {
@@ -43,82 +204,35 @@ const Checkbox = React.forwardRef<CheckboxElement, CheckboxProps>(
     const {
       __scopeCheckbox,
       name,
-      checked: checkedProp,
+      checked,
       defaultChecked,
       required,
       disabled,
-      value = 'on',
+      value,
       onCheckedChange,
       form,
       ...checkboxProps
     } = props;
-    const [button, setButton] = React.useState<HTMLButtonElement | null>(null);
-    const composedRefs = useComposedRefs(forwardedRef, (node) => setButton(node));
-    const hasConsumerStoppedPropagationRef = React.useRef(false);
-    // We set this to true by default so that events bubble to forms without JS (SSR)
-    const isFormControl = button ? form || !!button.closest('form') : true;
-    const [checked, setChecked] = useControllableState({
-      prop: checkedProp,
-      defaultProp: defaultChecked ?? false,
-      onChange: onCheckedChange,
-      caller: CHECKBOX_NAME,
-    });
-    const initialCheckedStateRef = React.useRef(checked);
-    React.useEffect(() => {
-      const form = button?.form;
-      if (form) {
-        const reset = () => setChecked(initialCheckedStateRef.current);
-        form.addEventListener('reset', reset);
-        return () => form.removeEventListener('reset', reset);
-      }
-    }, [button, setChecked]);
 
     return (
-      <CheckboxProvider scope={__scopeCheckbox} state={checked} disabled={disabled}>
-        <Primitive.button
-          type="button"
-          role="checkbox"
-          aria-checked={isIndeterminate(checked) ? 'mixed' : checked}
-          aria-required={required}
-          data-state={getState(checked)}
-          data-disabled={disabled ? '' : undefined}
-          disabled={disabled}
-          value={value}
-          {...checkboxProps}
-          ref={composedRefs}
-          onKeyDown={composeEventHandlers(props.onKeyDown, (event) => {
-            // According to WAI ARIA, Checkboxes don't activate on enter keypress
-            if (event.key === 'Enter') event.preventDefault();
-          })}
-          onClick={composeEventHandlers(props.onClick, (event) => {
-            setChecked((prevChecked) => (isIndeterminate(prevChecked) ? true : !prevChecked));
-            if (isFormControl) {
-              hasConsumerStoppedPropagationRef.current = event.isPropagationStopped();
-              // if checkbox is in a form, stop propagation from the button so that we only propagate
-              // one click event (from the input). We propagate changes from an input so that native
-              // form validation works and form events reflect checkbox updates.
-              if (!hasConsumerStoppedPropagationRef.current) event.stopPropagation();
-            }
-          })}
-        />
-        {isFormControl && (
-          <CheckboxBubbleInput
-            control={button}
-            bubbles={!hasConsumerStoppedPropagationRef.current}
-            name={name}
-            value={value}
-            checked={checked}
-            required={required}
-            disabled={disabled}
-            form={form}
-            // We transform because the input is absolutely positioned but we have
-            // rendered it **after** the button. This pulls it back to sit on top
-            // of the button.
-            style={{ transform: 'translateX(-100%)' }}
-            defaultChecked={isIndeterminate(defaultChecked) ? false : defaultChecked}
-          />
+      <CheckboxProvider
+        __scopeCheckbox={__scopeCheckbox}
+        checked={checked}
+        defaultChecked={defaultChecked}
+        disabled={disabled}
+        required={required}
+        onCheckedChange={onCheckedChange}
+        name={name}
+        form={form}
+        value={value}
+        // @ts-expect-error
+        internal_do_not_use_render={({ isFormControl }: CheckboxContextValue) => (
+          <>
+            <CheckboxTrigger {...checkboxProps} ref={forwardedRef} />
+            {isFormControl && <CheckboxBubbleInput />}
+          </>
         )}
-      </CheckboxProvider>
+      />
     );
   }
 );
@@ -146,9 +260,11 @@ const CheckboxIndicator = React.forwardRef<CheckboxIndicatorElement, CheckboxInd
     const { __scopeCheckbox, forceMount, ...indicatorProps } = props;
     const context = useCheckboxContext(INDICATOR_NAME, __scopeCheckbox);
     return (
-      <Presence present={forceMount || isIndeterminate(context.state) || context.state === true}>
+      <Presence
+        present={forceMount || isIndeterminate(context.checked) || context.checked === true}
+      >
         <Primitive.span
-          data-state={getState(context.state)}
+          data-state={getState(context.checked)}
           data-disabled={context.disabled ? '' : undefined}
           {...indicatorProps}
           ref={forwardedRef}
@@ -168,32 +284,38 @@ CheckboxIndicator.displayName = INDICATOR_NAME;
 const BUBBLE_INPUT_NAME = 'CheckboxBubbleInput';
 
 type InputProps = React.ComponentPropsWithoutRef<typeof Primitive.input>;
-interface CheckboxBubbleInputProps extends Omit<InputProps, 'checked'> {
-  checked: CheckedState;
-  control: HTMLElement | null;
-  bubbles: boolean;
-}
+interface CheckboxBubbleInputProps extends Omit<InputProps, 'checked'> {}
 
 const CheckboxBubbleInput = React.forwardRef<HTMLInputElement, CheckboxBubbleInputProps>(
   (
     {
       __scopeCheckbox,
-      control,
-      checked,
-      bubbles = true,
-      defaultChecked,
+
       ...props
     }: ScopedProps<CheckboxBubbleInputProps>,
     forwardedRef
   ) => {
-    const ref = React.useRef<HTMLInputElement>(null);
-    const composedRefs = useComposedRefs(ref, forwardedRef);
+    const {
+      control,
+      hasConsumerStoppedPropagationRef,
+      checked,
+      defaultChecked,
+      required,
+      disabled,
+      name,
+      value,
+      form,
+      bubbleInput,
+      setBubbleInput,
+    } = useCheckboxContext(BUBBLE_INPUT_NAME, __scopeCheckbox);
+
+    const composedRefs = useComposedRefs(forwardedRef, setBubbleInput);
     const prevChecked = usePrevious(checked);
     const controlSize = useSize(control);
 
     // Bubble checked change to parents (e.g form change event)
     React.useEffect(() => {
-      const input = ref.current;
+      const input = bubbleInput;
       if (!input) return;
 
       const inputProto = window.HTMLInputElement.prototype;
@@ -203,13 +325,14 @@ const CheckboxBubbleInput = React.forwardRef<HTMLInputElement, CheckboxBubbleInp
       ) as PropertyDescriptor;
       const setChecked = descriptor.set;
 
+      const bubbles = !hasConsumerStoppedPropagationRef.current;
       if (prevChecked !== checked && setChecked) {
         const event = new Event('click', { bubbles });
         input.indeterminate = isIndeterminate(checked);
         setChecked.call(input, isIndeterminate(checked) ? false : checked);
         input.dispatchEvent(event);
       }
-    }, [prevChecked, checked, bubbles]);
+    }, [bubbleInput, prevChecked, checked, hasConsumerStoppedPropagationRef]);
 
     const defaultCheckedRef = React.useRef(isIndeterminate(checked) ? false : checked);
     return (
@@ -217,6 +340,11 @@ const CheckboxBubbleInput = React.forwardRef<HTMLInputElement, CheckboxBubbleInp
         type="checkbox"
         aria-hidden
         defaultChecked={defaultChecked ?? defaultCheckedRef.current}
+        required={required}
+        disabled={disabled}
+        name={name}
+        value={value}
+        form={form}
         {...props}
         tabIndex={-1}
         ref={composedRefs}
@@ -227,6 +355,10 @@ const CheckboxBubbleInput = React.forwardRef<HTMLInputElement, CheckboxBubbleInp
           pointerEvents: 'none',
           opacity: 0,
           margin: 0,
+          // We transform because the input is absolutely positioned but we have
+          // rendered it **after** the button. This pulls it back to sit on top
+          // of the button.
+          transform: 'translateX(-100%)',
         }}
       />
     );
@@ -237,6 +369,10 @@ CheckboxBubbleInput.displayName = BUBBLE_INPUT_NAME;
 
 /* ---------------------------------------------------------------------------------------------- */
 
+function isFunction(value: unknown): value is (...args: any[]) => any {
+  return typeof value === 'function';
+}
+
 function isIndeterminate(checked?: CheckedState): checked is 'indeterminate' {
   return checked === 'indeterminate';
 }
@@ -245,16 +381,26 @@ function getState(checked: CheckedState) {
   return isIndeterminate(checked) ? 'indeterminate' : checked ? 'checked' : 'unchecked';
 }
 
-const Root = Checkbox;
-const Indicator = CheckboxIndicator;
-
 export {
   createCheckboxScope,
   //
   Checkbox,
+  CheckboxProvider,
+  CheckboxTrigger,
   CheckboxIndicator,
+  CheckboxBubbleInput,
   //
-  Root,
-  Indicator,
+  Checkbox as Root,
+  CheckboxProvider as Provider,
+  CheckboxTrigger as Trigger,
+  CheckboxIndicator as Indicator,
+  CheckboxBubbleInput as BubbleInput,
+};
+export type {
+  CheckboxProps,
+  CheckboxProviderProps,
+  CheckboxTriggerProps,
+  CheckboxIndicatorProps,
+  CheckboxBubbleInputProps,
+  CheckedState,
 };
-export type { CheckboxProps, CheckboxIndicatorProps, CheckedState };
diff --git a/packages/react/checkbox/src/index.ts b/packages/react/checkbox/src/index.ts
index 8b824ca3..3727c7ff 100644
--- a/packages/react/checkbox/src/index.ts
+++ b/packages/react/checkbox/src/index.ts
@@ -3,9 +3,22 @@ export {
   createCheckboxScope,
   //
   Checkbox,
+  CheckboxProvider as unstable_CheckboxProvider,
+  CheckboxTrigger as unstable_CheckboxTrigger,
   CheckboxIndicator,
+  CheckboxBubbleInput as unstable_CheckboxBubbleInput,
   //
   Root,
+  Provider as unstable_Provider,
+  Trigger as unstable_Trigger,
   Indicator,
+  BubbleInput as unstable_BubbleInput,
+} from './checkbox';
+export type {
+  CheckboxProps,
+  CheckboxProviderProps as unstable_CheckboxProviderProps,
+  CheckboxTriggerProps as unstable_CheckboxTriggerProps,
+  CheckboxIndicatorProps,
+  CheckboxBubbleInputProps as unstable_CheckboxBubbleInputProps,
+  CheckedState,
 } from './checkbox';
-export type { CheckboxProps, CheckboxIndicatorProps, CheckedState } from './checkbox';
diff --git a/packages/react/one-time-password-field/src/one-time-password-field.tsx b/packages/react/one-time-password-field/src/one-time-password-field.tsx
index 382af41c..f585b61d 100644
--- a/packages/react/one-time-password-field/src/one-time-password-field.tsx
+++ b/packages/react/one-time-password-field/src/one-time-password-field.tsx
@@ -211,7 +211,7 @@ const OneTimePasswordField = React.forwardRef<HTMLDivElement, OneTimePasswordFie
       form,
       name,
       placeholder,
-      type = 'password',
+      type = 'text',
       // TODO: Change default to vertical when inputs use vertical writing mode
       orientation = 'horizontal',
       dir,
@@ -638,7 +638,7 @@ const OneTimePasswordFieldInput = React.forwardRef<
           return (
             <Primitive.Root.input
               ref={composedInputRef}
-              type="text"
+              type={context.type}
               aria-label={`Character ${index + 1} of ${collection.size}`}
               autoComplete={supportsAutoComplete ? context.autoComplete : 'off'}
               data-1p-ignore={supportsAutoComplete ? undefined : 'true'}
diff --git a/packages/react/radio-group/src/radio-group.tsx b/packages/react/radio-group/src/radio-group.tsx
index 82ade8bf..6fb5ff8f 100644
--- a/packages/react/radio-group/src/radio-group.tsx
+++ b/packages/react/radio-group/src/radio-group.tsx
@@ -30,7 +30,7 @@ type RadioGroupContextValue = {
   name?: string;
   required: boolean;
   disabled: boolean;
-  value: string;
+  value: string | null;
   onValueChange(value: string): void;
 };
 
@@ -48,7 +48,7 @@ interface RadioGroupProps extends PrimitiveDivProps {
   orientation?: RovingFocusGroupProps['orientation'];
   loop?: RovingFocusGroupProps['loop'];
   defaultValue?: string;
-  value?: RadioGroupContextValue['value'];
+  value?: string | null;
   onValueChange?: RadioGroupContextValue['onValueChange'];
 }
 
@@ -71,8 +71,8 @@ const RadioGroup = React.forwardRef<RadioGroupElement, RadioGroupProps>(
     const direction = useDirection(dir);
     const [value, setValue] = useControllableState({
       prop: valueProp,
-      defaultProp: defaultValue ?? '',
-      onChange: onValueChange,
+      defaultProp: defaultValue ?? null,
+      onChange: onValueChange as (value: string | null) => void,
       caller: RADIO_GROUP_NAME,
     });
 


Test patch:
diff --git a/packages/react/checkbox/src/checkbox.test.tsx b/packages/react/checkbox/src/checkbox.test.tsx
index da6a1570..51e79ea0 100644
--- a/packages/react/checkbox/src/checkbox.test.tsx
+++ b/packages/react/checkbox/src/checkbox.test.tsx
@@ -1,8 +1,8 @@
 import * as React from 'react';
 import { axe } from 'vitest-axe';
 import type { RenderResult } from '@testing-library/react';
-import { cleanup, render, fireEvent } from '@testing-library/react';
-import { Checkbox, CheckboxIndicator } from './checkbox';
+import { cleanup, render, fireEvent, screen, waitFor, act } from '@testing-library/react';
+import * as Checkbox from '.';
 import { afterEach, describe, it, beforeEach, vi, expect } from 'vitest';
 
 const CHECKBOX_ROLE = 'checkbox';
@@ -20,184 +20,464 @@ global.ResizeObserver = class ResizeObserver {
   disconnect() {}
 };
 
-describe('given a default Checkbox', () => {
-  let rendered: RenderResult;
-  let checkbox: HTMLElement;
-  let indicator: HTMLElement | null;
-
-  beforeEach(() => {
-    rendered = render(<CheckboxTest />);
-    checkbox = rendered.getByRole(CHECKBOX_ROLE);
-    indicator = rendered.queryByTestId(INDICATOR_TEST_ID);
-  });
-
+describe('Checkbox', () => {
   afterEach(cleanup);
 
-  it('should have no accessibility violations', async () => {
-    expect(await axe(rendered.container)).toHaveNoViolations();
-  });
-
-  describe('when clicking the checkbox', () => {
-    beforeEach(async () => {
-      fireEvent.click(checkbox);
-      indicator = rendered.queryByTestId(INDICATOR_TEST_ID);
+  describe('given a default Checkbox', () => {
+    let rendered: RenderResult;
+    beforeEach(() => {
+      rendered = render(
+        <Checkbox.unstable_Provider>
+          <Checkbox.unstable_Trigger aria-label="basic checkbox">
+            <Checkbox.Indicator data-testid={INDICATOR_TEST_ID} />
+          </Checkbox.unstable_Trigger>
+        </Checkbox.unstable_Provider>
+      );
     });
 
-    it('should render a visible indicator', () => {
-      expect(indicator).toBeVisible();
+    it('should have no accessibility violations', async () => {
+      expect(await axe(rendered.container)).toHaveNoViolations();
     });
 
-    describe('and clicking the checkbox again', () => {
-      beforeEach(async () => {
-        fireEvent.click(checkbox);
-      });
+    it('should toggle the indicator when clicked', async () => {
+      const checkbox = screen.getByRole(CHECKBOX_ROLE);
+      await act(async () => fireEvent.click(checkbox));
 
-      it('should remove the indicator', () => {
-        expect(indicator).not.toBeInTheDocument();
-      });
+      let indicator = screen.queryByTestId(INDICATOR_TEST_ID);
+      expect(indicator).toBeVisible();
+
+      await act(async () => fireEvent.click(checkbox));
+      indicator = screen.queryByTestId(INDICATOR_TEST_ID);
+      expect(indicator).not.toBeInTheDocument();
     });
   });
-});
 
-describe('given a disabled Checkbox', () => {
-  let rendered: RenderResult;
+  describe('given a disabled Checkbox', () => {
+    let rendered: RenderResult;
+    beforeEach(() => {
+      rendered = render(
+        <Checkbox.unstable_Provider disabled>
+          <Checkbox.unstable_Trigger aria-label="basic checkbox">
+            <Checkbox.Indicator data-testid={INDICATOR_TEST_ID} />
+          </Checkbox.unstable_Trigger>
+        </Checkbox.unstable_Provider>
+      );
+    });
 
-  beforeEach(() => {
-    rendered = render(<CheckboxTest disabled />);
-  });
+    it('should have no accessibility violations', async () => {
+      expect(await axe(rendered.container)).toHaveNoViolations();
+    });
 
-  afterEach(cleanup);
+    it('should not toggle the indicator when clicked', async () => {
+      const checkbox = screen.getByRole(CHECKBOX_ROLE);
+
+      await act(async () => fireEvent.click(checkbox));
+      const indicator = screen.queryByTestId(INDICATOR_TEST_ID);
+      expect(indicator).not.toBeInTheDocument();
 
-  it('should have no accessibility violations', async () => {
-    expect(await axe(rendered.container)).toHaveNoViolations();
+      await act(async () => fireEvent.click(checkbox));
+      expect(indicator).not.toBeInTheDocument();
+    });
   });
-});
 
-describe('given an uncontrolled `checked` Checkbox', () => {
-  let rendered: RenderResult;
-  let checkbox: HTMLElement;
-  let indicator: HTMLElement | null;
-  const onCheckedChange = vi.fn();
+  describe('given an uncontrolled `checked` Checkbox', () => {
+    const onCheckedChange = vi.fn();
+    let rendered: RenderResult;
 
-  beforeEach(() => {
-    rendered = render(<CheckboxTest defaultChecked onCheckedChange={onCheckedChange} />);
-    checkbox = rendered.getByRole(CHECKBOX_ROLE);
-    indicator = rendered.queryByTestId(INDICATOR_TEST_ID);
-  });
+    beforeEach(() => {
+      rendered = render(
+        <Checkbox.unstable_Provider defaultChecked onCheckedChange={onCheckedChange}>
+          <Checkbox.unstable_Trigger aria-label="basic checkbox">
+            <Checkbox.Indicator data-testid={INDICATOR_TEST_ID} />
+          </Checkbox.unstable_Trigger>
+        </Checkbox.unstable_Provider>
+      );
+    });
 
-  afterEach(cleanup);
+    afterEach(cleanup);
 
-  it('should have no accessibility violations', async () => {
-    expect(await axe(rendered.container)).toHaveNoViolations();
-  });
+    it('should have no accessibility violations', async () => {
+      expect(await axe(rendered.container)).toHaveNoViolations();
+    });
 
-  describe('when clicking the checkbox', () => {
-    beforeEach(async () => {
-      fireEvent.click(checkbox);
+    it('should render a visible indicator', () => {
+      const indicator = screen.queryByTestId(INDICATOR_TEST_ID);
+      expect(indicator).toBeVisible();
     });
 
-    it('should remove the indicator', () => {
+    it('should toggle the indicator when clicked', async () => {
+      const checkbox = screen.getByRole(CHECKBOX_ROLE);
+      await act(async () => fireEvent.click(checkbox));
+      let indicator = screen.queryByTestId(INDICATOR_TEST_ID);
       expect(indicator).not.toBeInTheDocument();
+
+      await act(async () => fireEvent.click(checkbox));
+      indicator = screen.queryByTestId(INDICATOR_TEST_ID);
+      expect(indicator).toBeVisible();
     });
 
     it('should call `onCheckedChange` prop', () => {
-      expect(onCheckedChange).toHaveBeenCalled();
+      const checkbox = screen.getByRole(CHECKBOX_ROLE);
+      fireEvent.click(checkbox);
+      waitFor(() => {
+        expect(onCheckedChange).toHaveBeenCalledWith(false);
+      });
     });
   });
-});
 
-describe('given a controlled `checked` Checkbox', () => {
-  let rendered: RenderResult;
-  let checkbox: HTMLElement;
-  const onCheckedChange = vi.fn();
+  describe('given a controlled Checkbox', () => {
+    const onCheckedChange = vi.fn();
+    let rendered: RenderResult;
 
-  beforeEach(() => {
-    rendered = render(<CheckboxTest checked onCheckedChange={onCheckedChange} />);
-    checkbox = rendered.getByRole(CHECKBOX_ROLE);
-  });
-
-  afterEach(cleanup);
+    function ControlledCheckbox() {
+      const [checked, setChecked] = React.useState(false);
+      const [blockToggle, setBlockToggle] = React.useState(false);
+      return (
+        <div>
+          <Checkbox.unstable_Provider
+            checked={checked}
+            onCheckedChange={(checked) => {
+              onCheckedChange(checked);
+              if (!blockToggle) {
+                setChecked(checked);
+              }
+            }}
+          >
+            <Checkbox.unstable_Trigger aria-label="basic checkbox">
+              <Checkbox.Indicator data-testid={INDICATOR_TEST_ID} />
+            </Checkbox.unstable_Trigger>
+          </Checkbox.unstable_Provider>
+          <button type="button" onClick={() => setChecked((prev) => !prev)}>
+            Toggle checkbox
+          </button>
+          <button type="button" onClick={() => setBlockToggle((prev) => !prev)}>
+            {blockToggle ? 'Unblock' : 'Block'} checkbox
+          </button>
+        </div>
+      );
+    }
 
-  describe('when clicking the checkbox', () => {
     beforeEach(() => {
-      fireEvent.click(checkbox);
+      rendered = render(<ControlledCheckbox />);
     });
 
-    it('should call `onCheckedChange` prop', () => {
-      expect(onCheckedChange).toHaveBeenCalled();
+    it('should have no accessibility violations', async () => {
+      expect(await axe(rendered.container)).toHaveNoViolations();
+    });
+
+    it('should toggle the indicator', async () => {
+      const checkbox = screen.getByRole(CHECKBOX_ROLE);
+      await act(async () => fireEvent.click(checkbox));
+      const indicator = screen.queryByTestId(INDICATOR_TEST_ID);
+      expect(indicator).toBeVisible();
+    });
+
+    it('should call `onCheckedChange` prop', async () => {
+      const checkbox = screen.getByRole(CHECKBOX_ROLE);
+      await act(async () => fireEvent.click(checkbox));
+      expect(onCheckedChange).toHaveBeenCalledWith(true);
+    });
+
+    it('should not toggle unless state is updated', async () => {
+      const checkbox = screen.getByRole(CHECKBOX_ROLE);
+      const blocker = screen.getByText('Block checkbox');
+      await act(async () => fireEvent.click(blocker));
+      await act(async () => fireEvent.click(checkbox));
+      const indicator = screen.queryByTestId(INDICATOR_TEST_ID);
+      expect(indicator).not.toBeInTheDocument();
     });
   });
-});
 
-describe('given an uncontrolled Checkbox in form', () => {
-  afterEach(cleanup);
+  describe('given an uncontrolled Checkbox in form with bubble input', () => {
+    const onChange = vi.fn();
 
-  describe('when clicking the checkbox', () => {
-    it('should receive change event with target `defaultChecked` same as the `defaultChecked` prop of Checkbox', () =>
-      new Promise((done) => {
-        const rendered = render(
-          <form
-            onChange={(event) => {
-              const target = event.target as HTMLInputElement;
-              expect(target.defaultChecked).toBe(true);
-            }}
-          >
-            <CheckboxTest defaultChecked />
-          </form>
-        );
-        const checkbox = rendered.getByRole(CHECKBOX_ROLE);
-        fireEvent.click(checkbox);
-        rendered.rerender(
+    it('should receive change event with next state', async () => {
+      render(
+        <form
+          onChange={(event) => {
+            const target = event.target as HTMLInputElement;
+            onChange(target.checked);
+          }}
+        >
+          <Checkbox.unstable_Provider>
+            <Checkbox.unstable_Trigger aria-label="basic checkbox">
+              <Checkbox.Indicator data-testid={INDICATOR_TEST_ID} />
+            </Checkbox.unstable_Trigger>
+            <Checkbox.unstable_BubbleInput />
+          </Checkbox.unstable_Provider>
+        </form>
+      );
+
+      const checkbox = screen.getByRole(CHECKBOX_ROLE);
+      act(() => fireEvent.click(checkbox));
+      expect(onChange).toHaveBeenCalledWith(true);
+    });
+
+    describe('when `defaultChecked` is true', () => {
+      it('should receive change event with next state', async () => {
+        console.log(parseInt(React.version));
+        render(
           <form
             onChange={(event) => {
               const target = event.target as HTMLInputElement;
-              expect(target.defaultChecked).toBe(false);
-              done(null);
+              onChange(target.checked);
             }}
           >
-            <CheckboxTest defaultChecked={false} />
+            <Checkbox.unstable_Provider defaultChecked>
+              <Checkbox.unstable_Trigger aria-label="basic checkbox">
+                <Checkbox.Indicator data-testid={INDICATOR_TEST_ID} />
+              </Checkbox.unstable_Trigger>
+              <Checkbox.unstable_BubbleInput />
+            </Checkbox.unstable_Provider>
           </form>
         );
-        fireEvent.click(checkbox);
-      }));
+
+        const checkbox = screen.getByRole(CHECKBOX_ROLE);
+        act(() => fireEvent.click(checkbox));
+        expect(onChange).toHaveBeenCalledWith(false);
+      });
+    });
+  });
+
+  describe('given a controlled Checkbox in form with bubble input', () => {
+    const onChange = vi.fn();
+
+    function ControlledCheckbox() {
+      const [checked, setChecked] = React.useState(false);
+      return (
+        <>
+          <Checkbox.unstable_Provider checked={checked} onCheckedChange={setChecked as any}>
+            <Checkbox.unstable_Trigger aria-label="basic checkbox">
+              <Checkbox.Indicator data-testid={INDICATOR_TEST_ID} />
+            </Checkbox.unstable_Trigger>
+            <Checkbox.unstable_BubbleInput />
+          </Checkbox.unstable_Provider>
+          <button type="button" onClick={() => setChecked((prev) => !prev)}>
+            Toggle checkbox
+          </button>
+        </>
+      );
+    }
+
+    it('should receive change event with controlled state when clicked', async () => {
+      render(
+        <form
+          onChange={(event) => {
+            const target = event.target as HTMLInputElement;
+            onChange(target.checked);
+          }}
+        >
+          <ControlledCheckbox />
+        </form>
+      );
+
+      const checkbox = screen.getByRole(CHECKBOX_ROLE);
+      act(() => fireEvent.click(checkbox));
+      expect(onChange).toHaveBeenCalledWith(true);
+
+      act(() => fireEvent.click(checkbox));
+      expect(onChange).toHaveBeenCalledWith(false);
+    });
+
+    it('should receive change event with controlled state when set externally', async () => {
+      render(
+        <form
+          onChange={(event) => {
+            const target = event.target as HTMLInputElement;
+            onChange(target.checked);
+          }}
+        >
+          <ControlledCheckbox />
+        </form>
+      );
+
+      const toggleButton = screen.getByText('Toggle checkbox');
+      act(() => fireEvent.click(toggleButton));
+      expect(onChange).toHaveBeenCalledWith(false);
+    });
   });
 });
 
-describe('given a controlled Checkbox in a form', () => {
-  afterEach(cleanup);
+describe('Legacy Checkbox', () => {
+  describe('given a default Checkbox', () => {
+    let rendered: RenderResult;
+    let checkbox: HTMLElement;
+    let indicator: HTMLElement | null;
 
-  describe('when clicking the checkbox', () => {
-    it('should receive change event with target `defaultChecked` same as initial value of `checked` of Checkbox', () =>
-      new Promise((done) => {
-        const rendered = render(
-          <form
-            onChange={(event) => {
-              const target = event.target as HTMLInputElement;
-              expect(target.defaultChecked).toBe(true);
-            }}
-          >
-            <CheckboxTest checked />
-          </form>
-        );
-        const checkbox = rendered.getByRole(CHECKBOX_ROLE);
+    beforeEach(() => {
+      rendered = render(<LegacyCheckbox />);
+      checkbox = rendered.getByRole(CHECKBOX_ROLE);
+      indicator = rendered.queryByTestId(INDICATOR_TEST_ID);
+    });
+
+    afterEach(cleanup);
+
+    it('should have no accessibility violations', async () => {
+      expect(await axe(rendered.container)).toHaveNoViolations();
+    });
+
+    describe('when clicking the checkbox', () => {
+      beforeEach(async () => {
         fireEvent.click(checkbox);
-        rendered.rerender(
-          <form
-            onChange={(event) => {
-              const target = event.target as HTMLInputElement;
-              expect(target.defaultChecked).toBe(true);
-              done(null);
-            }}
-          >
-            <CheckboxTest checked={false} />
-          </form>
-        );
+        indicator = rendered.queryByTestId(INDICATOR_TEST_ID);
+      });
+
+      it('should render a visible indicator', () => {
+        expect(indicator).toBeVisible();
+      });
+
+      describe('and clicking the checkbox again', () => {
+        beforeEach(async () => {
+          fireEvent.click(checkbox);
+        });
+
+        it('should remove the indicator', () => {
+          expect(indicator).not.toBeInTheDocument();
+        });
+      });
+    });
+  });
+
+  describe('given a disabled Checkbox', () => {
+    let rendered: RenderResult;
+
+    beforeEach(() => {
+      rendered = render(<LegacyCheckbox disabled />);
+    });
+
+    afterEach(cleanup);
+
+    it('should have no accessibility violations', async () => {
+      expect(await axe(rendered.container)).toHaveNoViolations();
+    });
+  });
+
+  describe('given an uncontrolled `checked` Checkbox', () => {
+    let rendered: RenderResult;
+    let checkbox: HTMLElement;
+    let indicator: HTMLElement | null;
+    const onCheckedChange = vi.fn();
+
+    beforeEach(() => {
+      rendered = render(<LegacyCheckbox defaultChecked onCheckedChange={onCheckedChange} />);
+      checkbox = rendered.getByRole(CHECKBOX_ROLE);
+      indicator = rendered.queryByTestId(INDICATOR_TEST_ID);
+    });
+
+    afterEach(cleanup);
+
+    it('should have no accessibility violations', async () => {
+      expect(await axe(rendered.container)).toHaveNoViolations();
+    });
+
+    describe('when clicking the checkbox', () => {
+      beforeEach(async () => {
+        fireEvent.click(checkbox);
+      });
+
+      it('should remove the indicator', () => {
+        expect(indicator).not.toBeInTheDocument();
+      });
+
+      it('should call `onCheckedChange` prop', () => {
+        expect(onCheckedChange).toHaveBeenCalled();
+      });
+    });
+  });
+
+  describe('given a controlled `checked` Checkbox', () => {
+    let rendered: RenderResult;
+    let checkbox: HTMLElement;
+    const onCheckedChange = vi.fn();
+
+    beforeEach(() => {
+      rendered = render(<LegacyCheckbox checked onCheckedChange={onCheckedChange} />);
+      checkbox = rendered.getByRole(CHECKBOX_ROLE);
+    });
+
+    afterEach(cleanup);
+
+    describe('when clicking the checkbox', () => {
+      beforeEach(() => {
         fireEvent.click(checkbox);
-      }));
+      });
+
+      it('should call `onCheckedChange` prop', () => {
+        expect(onCheckedChange).toHaveBeenCalled();
+      });
+    });
+  });
+
+  describe('given an uncontrolled Checkbox in form', () => {
+    afterEach(cleanup);
+
+    describe('when clicking the checkbox', () => {
+      it('should receive change event with target `defaultChecked` same as the `defaultChecked` prop of Checkbox', () =>
+        new Promise((done) => {
+          const rendered = render(
+            <form
+              onChange={(event) => {
+                const target = event.target as HTMLInputElement;
+                expect(target.defaultChecked).toBe(true);
+              }}
+            >
+              <LegacyCheckbox defaultChecked />
+            </form>
+          );
+          const checkbox = rendered.getByRole(CHECKBOX_ROLE);
+          fireEvent.click(checkbox);
+          rendered.rerender(
+            <form
+              onChange={(event) => {
+                const target = event.target as HTMLInputElement;
+                expect(target.defaultChecked).toBe(false);
+                done(null);
+              }}
+            >
+              <LegacyCheckbox defaultChecked={false} />
+            </form>
+          );
+          fireEvent.click(checkbox);
+        }));
+    });
+  });
+
+  describe('given a controlled Checkbox in a form', () => {
+    afterEach(cleanup);
+
+    describe('when clicking the checkbox', () => {
+      it('should receive change event with target `defaultChecked` same as initial value of `checked` of Checkbox', () =>
+        new Promise((done) => {
+          const rendered = render(
+            <form
+              onChange={(event) => {
+                const target = event.target as HTMLInputElement;
+                expect(target.defaultChecked).toBe(true);
+              }}
+            >
+              <LegacyCheckbox checked />
+            </form>
+          );
+          const checkbox = rendered.getByRole(CHECKBOX_ROLE);
+          fireEvent.click(checkbox);
+          rendered.rerender(
+            <form
+              onChange={(event) => {
+                const target = event.target as HTMLInputElement;
+                expect(target.defaultChecked).toBe(true);
+                done(null);
+              }}
+            >
+              <LegacyCheckbox checked={false} />
+            </form>
+          );
+          fireEvent.click(checkbox);
+        }));
+    });
   });
 });
 
-function CheckboxTest(props: React.ComponentProps<typeof Checkbox>) {
+function LegacyCheckbox(props: React.ComponentProps<typeof Checkbox.Root>) {
   const containerRef = React.useRef<HTMLDivElement>(null);
   React.useEffect(() => {
     // We use the `hidden` attribute to hide the nested input from both sighted users and the
@@ -210,9 +490,9 @@ function CheckboxTest(props: React.ComponentProps<typeof Checkbox>) {
   }, []);
   return (
     <div ref={containerRef}>
-      <Checkbox aria-label="basic checkbox" {...props}>
-        <CheckboxIndicator data-testid={INDICATOR_TEST_ID} />
-      </Checkbox>
+      <Checkbox.Root aria-label="basic checkbox" {...props}>
+        <Checkbox.Indicator data-testid={INDICATOR_TEST_ID} />
+      </Checkbox.Root>
     </div>
   );
 }
diff --git a/packages/react/one-time-password-field/src/one-time-password-field.test.tsx b/packages/react/one-time-password-field/src/one-time-password-field.test.tsx
index 6ff2a79a..77c5badc 100644
--- a/packages/react/one-time-password-field/src/one-time-password-field.test.tsx
+++ b/packages/react/one-time-password-field/src/one-time-password-field.test.tsx
@@ -32,6 +32,27 @@ describe('given a default OneTimePasswordField', () => {
     expect(await axe(rendered.container)).toHaveNoViolations();
   });
 
+  it('should mask input value when type is password', async () => {
+    rendered.rerender(
+      <OneTimePasswordField.Root type="password">
+        <OneTimePasswordField.Input />
+        <OneTimePasswordField.HiddenInput name="code" />
+      </OneTimePasswordField.Root>
+    );
+
+    const input = rendered.container.querySelector(
+      'input:not([type="hidden"])'
+    ) as HTMLInputElement;
+
+    await userEvent.type(input, '1');
+    expect(input.type).toBe('password');
+
+    const hiddenInput = rendered.container.querySelector(
+      'input[type="hidden"]'
+    ) as HTMLInputElement;
+    expect(hiddenInput.value).toBe('1');
+  });
+
   // TODO: userEvent paste not behaving as expected. Debug and unskip.
   // Replicated in storybook for now.
   it.todo('pastes the code into the input', async () => {