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..c91fd10fcd4 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,
+ Ghost as InputGhost,
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,
+ Ghost: InputGhost,
+};
const popoverComponentModule: StoryModule = {
meta: popoverComponentMeta,
diff --git a/packages/swingset/src/stories/input.mdx b/packages/swingset/src/stories/input.mdx
index 7726e8e690a..85cb11ca3ee 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}
/>
+
+### Ghost
+
+The ghost 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 e1584557c83..29fca1a6fd6 100644
--- a/packages/swingset/src/stories/input.stories.tsx
+++ b/packages/swingset/src/stories/input.stories.tsx
@@ -9,15 +9,17 @@ export { default as __source } from './input.stories?raw';
export const meta: StoryMeta = {
group: 'Components',
- status: 'stable',
+ status: 'wip',
title: 'Input',
source: 'packages/ui/src/mosaic/components/input/input.tsx',
styles: {
_variants: {
size: { sm: {}, md: {}, lg: {} },
+ variant: { default: {}, ghost: {} },
},
_defaultVariants: {
size: 'md',
+ variant: 'default',
},
},
};
@@ -76,3 +78,13 @@ export function Invalid(props: Record) {
/>
);
}
+
+export function Ghost(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..8a84186ecff 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'],
},
},
+ ghost: {
+ 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..7cc1a522ecb 100644
--- a/packages/ui/src/mosaic/components/input/input.test.tsx
+++ b/packages/ui/src/mosaic/components/input/input.test.tsx
@@ -24,6 +24,20 @@ describe('Mosaic Input', () => {
expect(screen.getByRole('textbox', { name: 'Name' })).toHaveAttribute('data-size', size);
});
+ it('reflects the ghost variant', () => {
+ render(
+ ,
+ );
+
+ const input = screen.getByRole('textbox', { name: 'Search' });
+ expect(input).toHaveClass('cl-input');
+ expect(input).toHaveAttribute('data-variant', 'ghost');
+ expect(input).not.toHaveFocus();
+ });
+
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 +58,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 === 'ghost' && styles.ghost,
+ 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';