diff --git a/.changeset/quiet-pans-smile.md b/.changeset/quiet-pans-smile.md
new file mode 100644
index 00000000000..a845151cc84
--- /dev/null
+++ b/.changeset/quiet-pans-smile.md
@@ -0,0 +1,2 @@
+---
+---
diff --git a/packages/swingset/src/lib/registry.ts b/packages/swingset/src/lib/registry.ts
index 8f326162f60..dd765d1559d 100644
--- a/packages/swingset/src/lib/registry.ts
+++ b/packages/swingset/src/lib/registry.ts
@@ -65,6 +65,7 @@ import {
import {
Default,
Disabled as InputDisabled,
+ Headless as InputHeadless,
Invalid,
meta as inputMeta,
Sizes as InputSizes,
@@ -261,7 +262,14 @@ const bannerModule: StoryModule = {
const buttonModule: StoryModule = { meta: buttonMeta, Primary, Sizes, Disabled };
-const inputModule: StoryModule = { meta: inputMeta, Default, Sizes: InputSizes, Disabled: InputDisabled, Invalid };
+const inputModule: StoryModule = {
+ meta: inputMeta,
+ Default,
+ Sizes: InputSizes,
+ Disabled: InputDisabled,
+ Invalid,
+ Headless: InputHeadless,
+};
const popoverComponentModule: StoryModule = {
meta: popoverComponentMeta,
diff --git a/packages/swingset/src/stories/input.mdx b/packages/swingset/src/stories/input.mdx
index 7726e8e690a..6c27ef03b7f 100644
--- a/packages/swingset/src/stories/input.mdx
+++ b/packages/swingset/src/stories/input.mdx
@@ -49,3 +49,12 @@ The snippet below reflects the props selected in the table above — change a pr
name='Invalid'
storyModule={InputStories}
/>
+
+### Headless
+
+The headless variant keeps the input behavior and sizing while allowing a parent composition to provide the field chrome.
+
+
diff --git a/packages/swingset/src/stories/input.stories.tsx b/packages/swingset/src/stories/input.stories.tsx
index c5f8600ad7b..262d496dc0e 100644
--- a/packages/swingset/src/stories/input.stories.tsx
+++ b/packages/swingset/src/stories/input.stories.tsx
@@ -14,9 +14,11 @@ export const meta: StoryMeta = {
styles: {
_variants: {
size: { sm: {}, md: {}, lg: {} },
+ variant: { default: {}, headless: {} },
},
_defaultVariants: {
size: 'md',
+ variant: 'default',
},
},
};
@@ -75,3 +77,15 @@ export function Invalid(props: Record) {
/>
);
}
+
+export function Headless(props: Record) {
+ return (
+
+
+
+ );
+}
diff --git a/packages/ui/src/mosaic/components/input/index.ts b/packages/ui/src/mosaic/components/input/index.ts
index 188cbaa0d71..8739b78cda1 100644
--- a/packages/ui/src/mosaic/components/input/index.ts
+++ b/packages/ui/src/mosaic/components/input/index.ts
@@ -1,2 +1,2 @@
export { Input } from './input';
-export type { InputProps } from './input';
+export type { InputProps, InputVariant } from './input';
diff --git a/packages/ui/src/mosaic/components/input/input.styles.ts b/packages/ui/src/mosaic/components/input/input.styles.ts
index 935d5003a0a..0cbdedc0aeb 100644
--- a/packages/ui/src/mosaic/components/input/input.styles.ts
+++ b/packages/ui/src/mosaic/components/input/input.styles.ts
@@ -23,6 +23,14 @@ export const styles = stylex.create({
color: colorVars['--cl-color-input-placeholder'],
},
},
+ headless: {
+ borderRadius: 0,
+ borderStyle: 'none',
+ borderWidth: 0,
+ outline: 'none',
+ backgroundColor: 'transparent',
+ boxShadow: 'none',
+ },
});
export const sizes = stylex.create({
diff --git a/packages/ui/src/mosaic/components/input/input.test.tsx b/packages/ui/src/mosaic/components/input/input.test.tsx
index 103361daac4..31b820079cd 100644
--- a/packages/ui/src/mosaic/components/input/input.test.tsx
+++ b/packages/ui/src/mosaic/components/input/input.test.tsx
@@ -24,6 +24,19 @@ describe('Mosaic Input', () => {
expect(screen.getByRole('textbox', { name: 'Name' })).toHaveAttribute('data-size', size);
});
+ it('removes field chrome with the headless variant', () => {
+ render(
+ ,
+ );
+
+ const input = screen.getByRole('textbox', { name: 'Search' });
+ expect(input).toHaveClass('cl-input');
+ expect(input).toHaveAttribute('data-variant', 'headless');
+ });
+
it('reflects and forwards the disabled state', () => {
render(
, 'size'> {
size?: 'sm' | 'md' | 'lg';
+ /** Removes field chrome so a parent composition can provide it. @default 'default' */
+ variant?: InputVariant;
}
export const Input = React.forwardRef(function MosaicInput(
{
size = 'md',
+ variant = 'default',
disabled: disabledProp,
required: requiredProp,
render,
@@ -52,8 +57,15 @@ export const Input = React.forwardRef(function Mos
'aria-labelledby': fieldProps?.['aria-labelledby'] ?? ariaLabelledBy,
'aria-describedby': fieldProps?.['aria-describedby'] ?? ariaDescribedBy,
...mergeStyleProps(
- themeProps('input', { size, disabled }),
- stylex.props(reset.base, inputStyles.base, styles.base, sizes[size], disabled && inputStyles.disabled),
+ themeProps('input', { size, variant, disabled }),
+ stylex.props(
+ reset.base,
+ styles.base,
+ sizes[size],
+ variant === 'default' && inputStyles.base,
+ variant === 'headless' && styles.headless,
+ variant === 'default' && disabled && inputStyles.disabled,
+ ),
className,
style,
),
diff --git a/packages/ui/src/mosaic/styles/index.ts b/packages/ui/src/mosaic/styles/index.ts
index bd0cfde4645..f0829c50674 100644
--- a/packages/ui/src/mosaic/styles/index.ts
+++ b/packages/ui/src/mosaic/styles/index.ts
@@ -49,7 +49,7 @@ export type { HeadingProps } from '../components/heading';
export { Icon, IconFrame } from '../components/icon';
export type { IconFrameProps, IconProps } from '../components/icon';
export { Input } from '../components/input';
-export type { InputProps } from '../components/input';
+export type { InputProps, InputVariant } from '../components/input';
export { Item } from '../components/item';
export type { ItemProps } from '../components/item';
export { Menu } from '../components/menu';