,
+): CompoundButtonState => {
+ const state: CompoundButtonState = useCompoundButtonBase_unstable(props, ref);
+ const hasSecondaryContent = Boolean(!state.iconOnly && state.secondaryContent);
+
+ // eslint-disable-next-line react-hooks/immutability
+ state.root['data-disabled'] = stringifyDataAttribute(state.disabled);
+ // eslint-disable-next-line react-hooks/immutability
+ state.root['data-disabled-focusable'] = stringifyDataAttribute(state.disabledFocusable);
+ // eslint-disable-next-line react-hooks/immutability
+ state.root['data-icon-only'] = stringifyDataAttribute(state.iconOnly);
+ // eslint-disable-next-line react-hooks/immutability
+ state.root['data-has-secondary-content'] = stringifyDataAttribute(hasSecondaryContent);
+
+ return state;
+};
diff --git a/packages/react-components/react-headless-components-preview/library/src/compound-button.ts b/packages/react-components/react-headless-components-preview/library/src/compound-button.ts
new file mode 100644
index 00000000000000..cfeb2db8705e31
--- /dev/null
+++ b/packages/react-components/react-headless-components-preview/library/src/compound-button.ts
@@ -0,0 +1,2 @@
+export { CompoundButton, renderCompoundButton, useCompoundButton } from './components/CompoundButton';
+export type { CompoundButtonProps, CompoundButtonSlots, CompoundButtonState } from './components/CompoundButton';
diff --git a/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/AlphaSliderDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/AlphaSliderDefault.stories.tsx
new file mode 100644
index 00000000000000..cf316874328005
--- /dev/null
+++ b/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/AlphaSliderDefault.stories.tsx
@@ -0,0 +1,52 @@
+import * as React from 'react';
+import { AlphaSlider } from '@fluentui/react-headless-components-preview/color-picker';
+import { type HSVA, tinycolor } from '@ctrl/tinycolor';
+
+import styles from './color-picker.module.css';
+
+export const AlphaSliderDefault = (): React.ReactElement => {
+ const [color, setColor] = React.useState({ h: 320, s: 0.7, v: 0.8, a: 0.75 });
+
+ return (
+
+
setColor({ a: 1, ...data.color })}
+ input={{ className: styles.sliderInput }}
+ rail={{ className: styles.rail }}
+ thumb={{ className: styles.thumb }}
+ />
+
+
+
+ );
+};
+
+AlphaSliderDefault.storyName = 'AlphaSlider';
+AlphaSliderDefault.parameters = {
+ docs: {
+ description: {
+ story:
+ 'An alpha slider component that allows users to select the opacity of a color. The slider displays a gradient from fully transparent to fully opaque, and the thumb can be moved along the slider to adjust the opacity value.',
+ },
+ },
+};
+
+const ColorPreview = ({ color }: { color: HSVA }): React.ReactElement => {
+ return (
+
+ {tinycolor(color).toHex8String()}
+
+
+
+
+ );
+};
diff --git a/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/ColorAreaDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/ColorAreaDefault.stories.tsx
new file mode 100644
index 00000000000000..821232e40e9d72
--- /dev/null
+++ b/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/ColorAreaDefault.stories.tsx
@@ -0,0 +1,51 @@
+import * as React from 'react';
+import { ColorArea } from '@fluentui/react-headless-components-preview/color-picker';
+import { type HSVA, tinycolor } from '@ctrl/tinycolor';
+
+import styles from './color-picker.module.css';
+
+export const ColorAreaDefault = (): React.ReactElement => {
+ const [color, setColor] = React.useState({ h: 320, s: 0.7, v: 0.8, a: 0.75 });
+
+ return (
+
+ setColor({ a: 1, ...data.color })}
+ inputX={{ 'aria-label': 'Saturation', className: styles.areaInput }}
+ inputY={{ 'aria-label': 'Value', className: styles.areaInput }}
+ thumb={{ className: styles.areaThumb }}
+ />
+
+
+
+ );
+};
+
+ColorAreaDefault.storyName = 'ColorArea';
+ColorAreaDefault.parameters = {
+ docs: {
+ description: {
+ story:
+ 'A color area component that allows users to select the saturation and value of a color. The area displays a gradient representing the range of saturation and value, and the thumb can be moved within the area to adjust these values.',
+ },
+ },
+};
+
+const ColorPreview = ({ color }: { color: HSVA }): React.ReactElement => {
+ return (
+
+ {tinycolor(color).toHex8String()}
+
+
+
+
+ );
+};
diff --git a/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/ColorPickerDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/ColorPickerDefault.stories.tsx
new file mode 100644
index 00000000000000..013b6c8e722eed
--- /dev/null
+++ b/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/ColorPickerDefault.stories.tsx
@@ -0,0 +1,74 @@
+import * as React from 'react';
+import {
+ AlphaSlider,
+ ColorArea,
+ ColorPicker,
+ ColorSlider,
+} from '@fluentui/react-headless-components-preview/color-picker';
+import { type HSVA, tinycolor } from '@ctrl/tinycolor';
+
+import styles from './color-picker.module.css';
+
+export const ColorPickerDefault = (): React.ReactElement => {
+ const [color, setColor] = React.useState({ h: 320, s: 0.7, v: 0.8, a: 0.75 });
+
+ return (
+
+ setColor({ a: 1, ...data.color })}
+ >
+
+
+
+
+
+
+ );
+};
+
+ColorPickerDefault.storyName = 'ColorPicker';
+ColorPickerDefault.parameters = {
+ docs: {
+ description: {
+ story:
+ 'A color picker component that allows users to select a color using a color area, color sliders, and an alpha slider.',
+ },
+ },
+};
+
+const ColorPreview = ({ color }: { color: HSVA }): React.ReactElement => {
+ return (
+
+ {tinycolor(color).toHex8String()}
+
+
+
+
+ );
+};
diff --git a/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/ColorPickerDescription.md b/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/ColorPickerDescription.md
new file mode 100644
index 00000000000000..e606cf448494f3
--- /dev/null
+++ b/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/ColorPickerDescription.md
@@ -0,0 +1,3 @@
+A color picker lets users choose a color by adjusting its hue, saturation, value, and alpha channels.
+
+Use `ColorPicker` to coordinate an HSV color across `ColorArea`, `ColorSlider`, and `AlphaSlider` — it supplies the current color and the change handler to every descendant control. Each of those controls also works on its own with its own controlled or uncontrolled color state, so you can compose only the parts an experience needs, such as a single hue slider. Every control renders a native range input, so give each one an accessible name and, where the raw number is not meaningful, an `aria-valuetext`.
diff --git a/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/ColorSliderDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/ColorSliderDefault.stories.tsx
new file mode 100644
index 00000000000000..1b689f107edfc7
--- /dev/null
+++ b/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/ColorSliderDefault.stories.tsx
@@ -0,0 +1,61 @@
+import * as React from 'react';
+import { ColorSlider } from '@fluentui/react-headless-components-preview/color-picker';
+import { type HSVA, tinycolor } from '@ctrl/tinycolor';
+
+import styles from './color-picker.module.css';
+
+export const ColorSliderDefault = (): React.ReactElement => {
+ const [color, setColor] = React.useState({ h: 320, s: 0.7, v: 0.8, a: 0.75 });
+
+ return (
+
+ setColor({ a: 1, ...data.color })}
+ input={{ className: styles.sliderInput }}
+ rail={{ className: styles.rail }}
+ thumb={{ className: styles.thumb }}
+ />
+
+ setColor({ a: 1, ...data.color })}
+ vertical
+ input={{ className: styles.sliderInput }}
+ rail={{ className: styles.rail }}
+ thumb={{ className: styles.thumb }}
+ />
+
+
+ );
+};
+
+ColorSliderDefault.storyName = 'ColorSlider';
+ColorSliderDefault.parameters = {
+ docs: {
+ description: {
+ story:
+ 'A color slider component that allows users to select a color channel (hue, saturation, or value) using a slider control, in horizontal or vertical orientation.',
+ },
+ },
+};
+
+const ColorPreview = ({ color }: { color: HSVA }): React.ReactElement => {
+ return (
+
+ {tinycolor(color).toHex8String()}
+
+
+
+
+ );
+};
diff --git a/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/color-picker.module.css b/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/color-picker.module.css
new file mode 100644
index 00000000000000..f2f440e808b46a
--- /dev/null
+++ b/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/color-picker.module.css
@@ -0,0 +1,235 @@
+.card {
+ display: grid;
+ gap: var(--space-4);
+ inline-size: fit-content;
+ max-inline-size: 100%;
+ padding: var(--space-4);
+ color: var(--text);
+ background: var(--bg-elev);
+ border: var(--stroke-thin) solid var(--border);
+ border-radius: var(--radius-lg);
+ box-shadow: var(--shadow-2);
+}
+
+/* ColorArea */
+
+.area {
+ position: relative;
+ display: grid;
+ align-items: start;
+ justify-items: start;
+ aspect-ratio: 1;
+ inline-size: 100%;
+ margin-block-end: var(--space-1);
+ overflow: hidden;
+ touch-action: none;
+ background: linear-gradient(to bottom, transparent, #000), linear-gradient(to right, #fff, transparent),
+ var(--fui-Area--main-color);
+ border: var(--stroke-thin) solid var(--border-strong);
+ border-radius: var(--radius-md);
+}
+
+.areaThumb {
+ position: absolute;
+ inset-inline-start: var(--fui-AreaX--progress);
+ inset-block-end: var(--fui-AreaY--progress);
+ inline-size: var(--space-5);
+ block-size: var(--space-5);
+ background: var(--fui-Area__thumb--color);
+ border: var(--stroke-thin) solid var(--text-faint);
+ border-radius: var(--radius-pill);
+ box-shadow: var(--shadow-2);
+ transform: translate(-50%, 50%);
+ pointer-events: none;
+}
+
+.areaThumb::before {
+ position: absolute;
+ inset: 0;
+ content: '';
+ border: var(--stroke-thick) solid var(--bg-elev);
+ border-radius: inherit;
+}
+
+.areaInput {
+ position: absolute;
+ inset: 0;
+ inline-size: 100%;
+ block-size: 100%;
+ padding: 0;
+ margin: 0;
+ overflow: hidden;
+ opacity: 0;
+ pointer-events: none;
+}
+
+.area:has(.areaInput:focus-visible),
+.sliderInput:focus-visible {
+ outline: none;
+}
+
+.area:has(.areaInput:focus-visible) .areaThumb {
+ border-color: var(--accent);
+ outline: var(--stroke-thick) solid var(--accent);
+ outline-offset: var(--stroke-thin);
+}
+
+/* ColorSlider and AlphaSlider */
+
+.slider {
+ --slider-thumb-size: var(--space-5);
+ --slider-progress: var(--fui-Slider--progress);
+ --slider-direction: to right;
+
+ position: relative;
+ display: grid;
+ align-items: center;
+ justify-items: center;
+ touch-action: none;
+}
+
+.alphaSlider {
+ --slider-progress: var(--fui-AlphaSlider--progress);
+}
+
+/* 3x3 grid: rail and thumb sit in the center cell, the input stretches across all cells */
+.slider[data-orientation='horizontal'] {
+ min-inline-size: 200px;
+ min-block-size: var(--space-8);
+ grid-template-rows: 1fr var(--slider-thumb-size) 1fr;
+ grid-template-columns: 1fr 100% 1fr;
+}
+
+.slider[data-orientation='vertical'] {
+ --slider-direction: to top;
+
+ min-inline-size: var(--space-8);
+ min-block-size: 280px;
+ grid-template-rows: 1fr 100% 1fr;
+ grid-template-columns: 1fr var(--slider-thumb-size) 1fr;
+}
+
+.sliderInput {
+ z-index: 1;
+ grid-row: 1 / -1;
+ grid-column: 1 / -1;
+ inline-size: 100%;
+ block-size: 100%;
+ padding: 0;
+ margin: 0;
+ opacity: 0;
+ cursor: pointer;
+}
+
+.slider[data-orientation='vertical'] .sliderInput {
+ writing-mode: vertical-lr;
+ direction: rtl;
+}
+
+.rail {
+ position: relative;
+ grid-row: 2;
+ grid-column: 2;
+ inline-size: 100%;
+ block-size: 100%;
+ pointer-events: none;
+ border: var(--stroke-thin) solid var(--border);
+ border-radius: var(--radius-md);
+}
+
+.thumb {
+ position: absolute;
+ grid-row: 2;
+ grid-column: 2;
+ inline-size: var(--slider-thumb-size);
+ block-size: var(--slider-thumb-size);
+ pointer-events: none;
+ background: var(--fui-Slider__thumb--color);
+ border: var(--stroke-thin) solid var(--text-faint);
+ border-radius: var(--radius-pill);
+ box-shadow: var(--shadow-2);
+}
+
+.thumb::before {
+ position: absolute;
+ inset: 0;
+ content: '';
+ border: var(--stroke-thick) solid var(--bg-elev);
+ border-radius: inherit;
+}
+
+.slider[data-orientation='horizontal'] .thumb {
+ inset-block: 0;
+ inset-inline-start: var(--slider-progress);
+ transform: translateX(-50%);
+}
+
+.slider[data-orientation='vertical'] .thumb {
+ inset-inline: 0;
+ inset-block-end: var(--slider-progress);
+ transform: translateY(50%);
+}
+
+.sliderInput:focus-visible ~ .thumb {
+ border-color: var(--accent);
+ outline: var(--stroke-thick) solid var(--accent);
+ outline-offset: var(--stroke-thin);
+}
+
+.slider[data-channel='hue'] .rail {
+ background: linear-gradient(var(--fui-Slider--direction), red, fuchsia, blue, aqua, lime, yellow, red);
+}
+
+.slider[data-channel='saturation'] .rail {
+ background: linear-gradient(var(--slider-direction), #808080, var(--fui-Slider__rail--color));
+}
+
+.slider[data-channel='value'] .rail {
+ background: linear-gradient(var(--slider-direction), #000, var(--fui-Slider__rail--color));
+}
+
+.alphaSlider .rail {
+ background: linear-gradient(var(--fui-AlphaSlider--direction), transparent, var(--fui-AlphaSlider__rail--color)),
+ repeating-conic-gradient(var(--border-strong) 0 25%, #fff 0 50%);
+ background-size: auto, var(--space-4) var(--space-4);
+}
+
+.alphaSlider .thumb {
+ background: var(--bg-elev);
+}
+
+.alphaSlider .thumb::before {
+ background: var(--fui-AlphaSlider__thumb--color);
+}
+
+/* Color Preview */
+
+.colorPreview {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ gap: var(--space-2);
+ align-items: end;
+ inline-size: 300px;
+ max-inline-size: 100%;
+ padding-block-start: var(--space-3);
+}
+
+.output {
+ display: flex;
+ align-items: center;
+ height: 100%;
+ padding: var(--space-2) var(--space-3);
+ color: var(--text);
+ font-family: var(--font-mono);
+ background: var(--surface-muted);
+ border: var(--stroke-thin) solid var(--border);
+ border-radius: var(--radius-sm);
+}
+
+.color {
+ inline-size: var(--space-12);
+ block-size: var(--space-12);
+ border: var(--stroke-thin) solid var(--border-strong);
+ border-radius: var(--radius-md);
+ box-shadow: var(--shadow-1);
+}
diff --git a/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/index.stories.tsx
new file mode 100644
index 00000000000000..a80b1fbb3d7ec5
--- /dev/null
+++ b/packages/react-components/react-headless-components-preview/stories/src/ColorPicker/index.stories.tsx
@@ -0,0 +1,31 @@
+import {
+ AlphaSlider,
+ ColorArea,
+ ColorPicker,
+ ColorSlider,
+} from '@fluentui/react-headless-components-preview/color-picker';
+
+import descriptionMd from './ColorPickerDescription.md';
+import './color-picker.module.css';
+
+export { ColorPickerDefault } from './ColorPickerDefault.stories';
+export { ColorAreaDefault } from './ColorAreaDefault.stories';
+export { ColorSliderDefault } from './ColorSliderDefault.stories';
+export { AlphaSliderDefault } from './AlphaSliderDefault.stories';
+
+export default {
+ title: 'Components/ColorPicker',
+ component: ColorPicker,
+ subcomponents: {
+ AlphaSlider,
+ ColorArea,
+ ColorSlider,
+ },
+ parameters: {
+ docs: {
+ description: {
+ component: descriptionMd,
+ },
+ },
+ },
+};
diff --git a/packages/react-components/react-headless-components-preview/stories/src/CompoundButton/CompoundButtonDefault.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/CompoundButton/CompoundButtonDefault.stories.tsx
new file mode 100644
index 00000000000000..a00146d3c67681
--- /dev/null
+++ b/packages/react-components/react-headless-components-preview/stories/src/CompoundButton/CompoundButtonDefault.stories.tsx
@@ -0,0 +1,89 @@
+import * as React from 'react';
+import { CompoundButton } from '@fluentui/react-headless-components-preview/compound-button';
+import { AddRegular, ArrowDownloadRegular, DeleteRegular, OpenRegular, SettingsRegular } from '@fluentui/react-icons';
+
+import styles from './compound-button.module.css';
+
+export const Default = (): React.ReactNode => {
+ const [completedActions, setCompletedActions] = React.useState(0);
+
+ const iconSlot = (icon: React.ReactNode) => ({
+ children: icon,
+ className: styles.actionIcon,
+ });
+
+ const contentContainerSlot = {
+ className: styles.actionContent,
+ };
+
+ const secondaryContentSlot = (children: React.ReactNode) => ({
+ children,
+ className: styles.actionSecondary,
+ });
+
+ return (
+
+
+
Deployment workspace
+
Compound actions
+
+ One semantic component, styled here as a compact action system with CSS Modules.
+
+
+
+
+ Actions completed: {completedActions}
+
+
+
+ )}
+ contentContainer={contentContainerSlot}
+ secondaryContent={secondaryContentSlot('Starts a new deployment')}
+ onClick={() => setCompletedActions(value => value + 1)}
+ >
+ Create release
+
+
+ )}
+ contentContainer={contentContainerSlot}
+ secondaryContent={secondaryContentSlot('Opens release details')}
+ >
+ Review release
+
+
+ )}
+ contentContainer={contentContainerSlot}
+ secondaryContent={secondaryContentSlot('Unavailable while deployment is active')}
+ disabled
+ onClick={() => setCompletedActions(value => value + 1)}
+ >
+ Delete release
+
+
+ )}
+ contentContainer={contentContainerSlot}
+ secondaryContent={secondaryContentSlot('Focusable for feature discovery')}
+ disabledFocusable
+ >
+ Configure policy
+
+
+ )}
+ aria-label="Add deployment target"
+ />
+
+
+ );
+};
diff --git a/packages/react-components/react-headless-components-preview/stories/src/CompoundButton/CompoundButtonDescription.md b/packages/react-components/react-headless-components-preview/stories/src/CompoundButton/CompoundButtonDescription.md
new file mode 100644
index 00000000000000..261a61aa38a4e3
--- /dev/null
+++ b/packages/react-components/react-headless-components-preview/stories/src/CompoundButton/CompoundButtonDescription.md
@@ -0,0 +1,16 @@
+A compound button presents a primary action with optional secondary text that adds concise context. Both text lines contribute to the accessible name, so keep them brief and avoid repeating the same information.
+
+`CompoundButton` renders a native button by default and can render an anchor with `as="a"` and `href`. A disabled native button uses the `disabled` attribute and cannot be focused or activated. A disabled anchor cannot use a native disabled attribute, so it exposes `aria-disabled="true"`, removes `href`, blocks activation, and still emits the root `data-disabled` attribute. Disabled native buttons also emit root `data-disabled`. A disabled-focusable button remains in the tab order, exposes `aria-disabled="true"`, and blocks activation.
+
+The `contentContainer` slot groups the primary and secondary text, while `secondaryContent` represents the second textual line. These semantic slots let consumers apply their own layout and typography without changing the component's behavior.
+
+## State attributes
+
+State attributes are applied to the root, omitted when false, and form the supported CSS targeting contract.
+
+| Attribute | Meaning |
+| ---------------------------- | ---------------------------------------------------------------------------- |
+| `data-disabled` | Disabled prop/state is active |
+| `data-disabled-focusable` | Disabled-focusable state active |
+| `data-icon-only` | Only icon content rendered |
+| `data-has-secondary-content` | Normalized secondary-content slot is present and the button is not icon-only |
diff --git a/packages/react-components/react-headless-components-preview/stories/src/CompoundButton/compound-button.module.css b/packages/react-components/react-headless-components-preview/stories/src/CompoundButton/compound-button.module.css
new file mode 100644
index 00000000000000..36051afcac3ca5
--- /dev/null
+++ b/packages/react-components/react-headless-components-preview/stories/src/CompoundButton/compound-button.module.css
@@ -0,0 +1,153 @@
+.demo {
+ display: grid;
+ gap: var(--space-6);
+ padding: var(--space-6);
+ border: var(--stroke-thin) solid var(--border);
+ border-radius: var(--radius-2xl);
+ background: var(--bg-soft);
+ color: var(--text);
+ box-shadow: var(--shadow-2);
+}
+
+.introduction {
+ display: grid;
+ gap: var(--space-2);
+}
+
+.eyebrow,
+.heading,
+.summary {
+ margin: 0;
+}
+
+.eyebrow {
+ color: var(--accent);
+ font-family: var(--font-sans);
+ letter-spacing: var(--tracking-heading);
+}
+
+.heading {
+ color: var(--text);
+ font-family: var(--font-display);
+ letter-spacing: var(--tracking-display);
+}
+
+.summary {
+ color: var(--text-muted);
+}
+
+.status {
+ justify-self: start;
+ padding-block: var(--space-2);
+ padding-inline: var(--space-3);
+ border: var(--stroke-thin) solid var(--border-strong);
+ border-radius: var(--radius-pill);
+ background: var(--bg-elev);
+ color: var(--text);
+ font-family: var(--font-mono);
+}
+
+.actions {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: stretch;
+ gap: var(--space-3);
+}
+
+.action {
+ display: inline-grid;
+ grid-template-columns: auto 1fr;
+ align-items: center;
+ gap: var(--space-3);
+ min-block-size: var(--space-16);
+ min-inline-size: calc(var(--space-16) + var(--space-16) + var(--space-16));
+ padding-block: var(--space-3);
+ padding-inline: var(--space-4);
+ border: var(--stroke-thin) solid var(--border-strong);
+ border-radius: var(--radius-lg);
+ background: var(--bg-elev);
+ color: var(--text);
+ box-shadow: var(--shadow-1);
+ cursor: pointer;
+ text-align: start;
+ text-decoration: none;
+}
+
+.action:hover:not(:disabled, [data-disabled], [data-disabled-focusable], [aria-disabled='true']) {
+ border-color: var(--accent);
+ background: var(--accent-soft);
+}
+
+.action:active:not(:disabled, [data-disabled], [data-disabled-focusable], [aria-disabled='true']) {
+ border-color: var(--accent-strong);
+ box-shadow: none;
+}
+
+.action:focus-visible {
+ outline: var(--stroke-thick) solid var(--accent);
+ outline-offset: var(--space-1);
+}
+
+.action:disabled,
+.action[data-disabled],
+.action[data-disabled-focusable],
+.action[aria-disabled='true'] {
+ border-color: var(--border);
+ background: var(--surface-muted);
+ color: var(--text-faint);
+ box-shadow: none;
+ cursor: not-allowed;
+}
+
+.action[data-disabled-focusable]:focus-visible,
+.action[aria-disabled='true']:focus-visible {
+ outline-color: var(--accent);
+}
+
+.actionIcon {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ inline-size: var(--space-6);
+ block-size: var(--space-6);
+ color: var(--accent);
+}
+
+.actionIcon > svg {
+ inline-size: var(--space-5);
+ block-size: var(--space-5);
+}
+
+.actionContent {
+ display: grid;
+ gap: var(--space-1);
+ min-inline-size: 0;
+ color: inherit;
+}
+
+.actionSecondary {
+ color: var(--text-muted);
+}
+
+.action:disabled .actionIcon,
+.action[data-disabled] .actionIcon,
+.action[data-disabled-focusable] .actionIcon,
+.action[aria-disabled='true'] .actionIcon,
+.action:disabled .actionSecondary,
+.action[data-disabled] .actionSecondary,
+.action[data-disabled-focusable] .actionSecondary,
+.action[aria-disabled='true'] .actionSecondary {
+ color: inherit;
+}
+
+.anchorAction {
+ border-color: var(--accent);
+}
+
+.iconAction[data-icon-only] {
+ display: inline-flex;
+ min-inline-size: var(--space-16);
+ inline-size: var(--space-16);
+ padding: 0;
+ justify-content: center;
+}
diff --git a/packages/react-components/react-headless-components-preview/stories/src/CompoundButton/index.stories.tsx b/packages/react-components/react-headless-components-preview/stories/src/CompoundButton/index.stories.tsx
new file mode 100644
index 00000000000000..42be7f8c2092b3
--- /dev/null
+++ b/packages/react-components/react-headless-components-preview/stories/src/CompoundButton/index.stories.tsx
@@ -0,0 +1,17 @@
+import { CompoundButton } from '@fluentui/react-headless-components-preview/compound-button';
+
+import './compound-button.module.css';
+import descriptionMd from './CompoundButtonDescription.md';
+export { Default } from './CompoundButtonDefault.stories';
+
+export default {
+ title: 'Components/CompoundButton',
+ component: CompoundButton,
+ parameters: {
+ docs: {
+ description: {
+ component: descriptionMd,
+ },
+ },
+ },
+};
diff --git a/yarn.lock b/yarn.lock
index 59dca6f0f0833c..ff372e8e73a616 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4329,6 +4329,7 @@ __metadata:
"@fluentui/react-button": "npm:^9.10.1"
"@fluentui/react-card": "npm:^9.7.1"
"@fluentui/react-checkbox": "npm:^9.6.3"
+ "@fluentui/react-color-picker": "npm:^9.2.19"
"@fluentui/react-combobox": "npm:^9.17.4"
"@fluentui/react-context-selector": "npm:^9.2.18"
"@fluentui/react-dialog": "npm:^9.18.2"