diff --git a/src/components/SearchBar/SearchBar.module.css b/src/components/SearchBar/SearchBar.module.css
new file mode 100644
index 000000000..924c31471
--- /dev/null
+++ b/src/components/SearchBar/SearchBar.module.css
@@ -0,0 +1,11 @@
+/*------------------------------------*\
+ # SEARCH FIELD
+\*------------------------------------*/
+
+/**
+ * Search bar wrapper for the SearchField and SearchButton
+ */
+.search-bar {
+ display: flex;
+ gap: var(--eds-size-1);
+}
diff --git a/src/components/SearchBar/SearchBar.stories.tsx b/src/components/SearchBar/SearchBar.stories.tsx
new file mode 100644
index 000000000..a10443cae
--- /dev/null
+++ b/src/components/SearchBar/SearchBar.stories.tsx
@@ -0,0 +1,78 @@
+import { BADGE } from '@geometricpanda/storybook-addon-badges';
+import { StoryObj, Meta } from '@storybook/react';
+import React from 'react';
+
+import { SearchBar } from '../SearchBar/SearchBar';
+
+export default {
+ title: 'Organisms/Interactive/SearchBar',
+ component: SearchBar,
+ parameters: {
+ badges: [BADGE.BETA],
+ },
+ decorators: [
+ (Story) => (
+
+
+
+ ),
+ ],
+} as Meta;
+
+type Args = React.ComponentProps;
+
+export const Default: StoryObj = {
+ args: {
+ children: (
+ <>
+
+
+ >
+ ),
+ },
+};
+
+export const Disabled: StoryObj = {
+ args: {
+ children: (
+ <>
+
+
+ >
+ ),
+ },
+ parameters: {
+ axe: {
+ disabledRules: ['color-contrast'],
+ },
+ },
+};
+
+export const Custom: StoryObj = {
+ render: () => (
+ <>
+
+
+
+
+
+
+
+
+ >
+ ),
+};
+
+export const SearchField: StoryObj<
+ React.ComponentProps
+> = {
+ argTypes: { onChange: { action: 'onChange' } },
+ render: (args) => ,
+};
+
+export const SearchButton: StoryObj<
+ React.ComponentProps
+> = {
+ argTypes: { onClick: { action: 'onClick' } },
+ render: (args) => ,
+};
diff --git a/src/components/SearchBar/SearchBar.test.tsx b/src/components/SearchBar/SearchBar.test.tsx
new file mode 100644
index 000000000..0e056ecf2
--- /dev/null
+++ b/src/components/SearchBar/SearchBar.test.tsx
@@ -0,0 +1,6 @@
+import { generateSnapshots } from '@chanzuckerberg/story-utils';
+import * as stories from './SearchBar.stories';
+
+describe('', () => {
+ generateSnapshots(stories);
+});
diff --git a/src/components/SearchBar/SearchBar.tsx b/src/components/SearchBar/SearchBar.tsx
new file mode 100644
index 000000000..efaddc9a7
--- /dev/null
+++ b/src/components/SearchBar/SearchBar.tsx
@@ -0,0 +1,34 @@
+import clsx from 'clsx';
+import React, { ReactNode } from 'react';
+import styles from './SearchBar.module.css';
+import SearchButton from '../SearchButton';
+import SearchField from '../SearchField';
+
+export type Props = {
+ /**
+ * SearchBar subcomponents to be wrapped.
+ */
+ children: ReactNode;
+ /**
+ * CSS class names that can be appended to the component.
+ */
+ className?: string;
+};
+
+/**
+ * BETA: This component is still a work in progress and is subject to change.
+ *
+ * ```ts
+ * import {SearchBar} from "@chanzuckerberg/eds";
+ * ```
+ *
+ * Input field and button used for searching through various data fields.
+ */
+export const SearchBar = ({ children, className }: Props) => {
+ const componentClassName = clsx(styles['search-bar'], className);
+
+ return {children}
;
+};
+
+SearchBar.InputField = SearchField;
+SearchBar.Button = SearchButton;
diff --git a/src/components/SearchBar/__snapshots__/SearchBar.test.tsx.snap b/src/components/SearchBar/__snapshots__/SearchBar.test.tsx.snap
new file mode 100644
index 000000000..3d0807393
--- /dev/null
+++ b/src/components/SearchBar/__snapshots__/SearchBar.test.tsx.snap
@@ -0,0 +1,223 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` Custom story renders snapshot 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[` Default story renders snapshot 1`] = `
+
+
+
+
+
+
+
+
+
+`;
+
+exports[` Disabled story renders snapshot 1`] = `
+
+
+
+
+
+
+
+
+
+`;
+
+exports[` SearchButton story renders snapshot 1`] = `
+
+
+
+`;
+
+exports[` SearchField story renders snapshot 1`] = `
+
+
+
+
+
+
+`;
diff --git a/src/components/SearchBar/index.ts b/src/components/SearchBar/index.ts
new file mode 100644
index 000000000..ffe57e791
--- /dev/null
+++ b/src/components/SearchBar/index.ts
@@ -0,0 +1 @@
+export { SearchBar as default } from './SearchBar';
diff --git a/src/components/SearchButton/SearchButton.module.css b/src/components/SearchButton/SearchButton.module.css
new file mode 100644
index 000000000..c0c31e9ef
--- /dev/null
+++ b/src/components/SearchButton/SearchButton.module.css
@@ -0,0 +1,14 @@
+/*------------------------------------*\
+ # SEARCH BUTTON
+\*------------------------------------*/
+
+/**
+ * Button to trigger search in a Search Bar.
+ */
+.search-button {
+ padding-top: 1px;
+ padding-bottom: 1px;
+ height: var(--eds-size-5);
+ box-sizing: content-box;
+ border: 0;
+}
diff --git a/src/components/SearchButton/SearchButton.tsx b/src/components/SearchButton/SearchButton.tsx
new file mode 100644
index 000000000..5f9347c87
--- /dev/null
+++ b/src/components/SearchButton/SearchButton.tsx
@@ -0,0 +1,38 @@
+import clsx from 'clsx';
+import React, { MouseEventHandler } from 'react';
+import styles from './SearchButton.module.css';
+import Button from '../Button';
+
+export type SearchButtonProps = {
+ /**
+ * CSS class names that can be appended to the component.
+ */
+ className?: string;
+ /**
+ * Disables the button and prevents button use.
+ */
+ disabled?: boolean;
+ /**
+ * On click handler for component
+ */
+ onClick?: MouseEventHandler;
+};
+
+/**
+ * BETA: This component is still a work in progress and is subject to change.
+ *
+ * ```ts
+ * import {SearchButton} from "@chanzuckerberg/eds";
+ * ```
+ *
+ * A button styled for use with the SearchBar.
+ */
+export const SearchButton = ({ className, ...other }: SearchButtonProps) => {
+ const componentClassName = clsx(styles['search-button'], className);
+
+ return (
+
+ );
+};
diff --git a/src/components/SearchButton/index.ts b/src/components/SearchButton/index.ts
new file mode 100644
index 000000000..d853af5d0
--- /dev/null
+++ b/src/components/SearchButton/index.ts
@@ -0,0 +1 @@
+export { SearchButton as default } from './SearchButton';
diff --git a/src/components/SearchField/SearchField.module.css b/src/components/SearchField/SearchField.module.css
new file mode 100644
index 000000000..d82c2f2ec
--- /dev/null
+++ b/src/components/SearchField/SearchField.module.css
@@ -0,0 +1,45 @@
+/*------------------------------------*\
+ # SEARCH FIELD
+\*------------------------------------*/
+
+/**
+ * Search Input Field for gathering input text.
+ */
+.search-field {
+ flex: 1;
+ position: relative;
+}
+
+/**
+ * The InputField component.
+ */
+.search-field__input {
+ padding-left: 2.25rem;
+}
+.search-field__input::-webkit-search-cancel-button {
+ display: none;
+}
+
+/**
+ * The search icon that overlays the InputField.
+ */
+.search-field__icon {
+ color: var(--eds-theme-color-icon-neutral-default);
+ position: absolute;
+ top: 0.6875rem;
+ left: 0.625rem;
+}
+
+/**
+ * Focused variant of the Search Icon.
+ */
+.search-field__input:focus + .search-field__icon {
+ color: var(--eds-theme-color-icon-brand-primary);
+}
+
+/**
+ * Disabled variant of the Search Icon.
+ */
+.search-field__icon--disabled {
+ color: var(--eds-theme-color-icon-disabled);
+}
diff --git a/src/components/SearchField/SearchField.tsx b/src/components/SearchField/SearchField.tsx
new file mode 100644
index 000000000..0c6967105
--- /dev/null
+++ b/src/components/SearchField/SearchField.tsx
@@ -0,0 +1,45 @@
+import clsx from 'clsx';
+import React from 'react';
+import styles from './SearchField.module.css';
+import Icon from '../Icon';
+import TextInput, { TextInputProps } from '../TextInput';
+
+/**
+ * BETA: This component is still a work in progress and is subject to change.
+ *
+ * ```ts
+ * import {SearchField} from "@chanzuckerberg/eds";
+ * ```
+ *
+ * A search TextInput component styled for use with the SearchBar.
+ */
+export const SearchField = ({
+ className,
+ disabled,
+ ...other
+}: TextInputProps) => {
+ const inputClassName = clsx(styles['search-field__input'], className);
+ const iconClassName = clsx(
+ styles['search-field__icon'],
+ disabled && styles['search-field__icon--disabled'],
+ );
+
+ return (
+
+
+
+
+ );
+};
diff --git a/src/components/SearchField/index.ts b/src/components/SearchField/index.ts
new file mode 100644
index 000000000..8e5e54550
--- /dev/null
+++ b/src/components/SearchField/index.ts
@@ -0,0 +1 @@
+export { SearchField as default } from './SearchField';
diff --git a/src/components/TextInput/TextInput.module.css b/src/components/TextInput/TextInput.module.css
index 9c3605f4c..4fe78ee8e 100644
--- a/src/components/TextInput/TextInput.module.css
+++ b/src/components/TextInput/TextInput.module.css
@@ -7,7 +7,7 @@
/**
* Default text input styles
*/
-.text-input {
+:where(.text-input) {
@mixin inputStyles;
padding-left: var(--eds-size-2);
}
diff --git a/src/components/TextInput/TextInput.tsx b/src/components/TextInput/TextInput.tsx
index f8b22c0d4..400bc659d 100644
--- a/src/components/TextInput/TextInput.tsx
+++ b/src/components/TextInput/TextInput.tsx
@@ -2,7 +2,7 @@ import clsx from 'clsx';
import React, { ChangeEventHandler } from 'react';
import styles from './TextInput.module.css';
-export type Props = React.InputHTMLAttributes & {
+export type TextInputProps = React.InputHTMLAttributes & {
/**
* String that describes a type of file that may be selected by the user
* https://developer.mozilla.org/en-US/docs/Web/*HTML/Element/input/file#Unique_file_type_specifiers
@@ -127,7 +127,7 @@ export const TextInput = ({
id,
isError,
...other
-}: Props) => {
+}: TextInputProps) => {
const componentClassName = clsx(
styles['text-input'],
isError && styles['error'],
diff --git a/src/components/TextInput/index.ts b/src/components/TextInput/index.ts
index 8f098ddec..0da9ef983 100644
--- a/src/components/TextInput/index.ts
+++ b/src/components/TextInput/index.ts
@@ -1 +1,2 @@
export { TextInput as default } from './TextInput';
+export type { TextInputProps } from './TextInput';
diff --git a/src/index.ts b/src/index.ts
index b48d8ee41..1d83ba758 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -65,6 +65,9 @@ export { default as Radio } from './components/Radio';
export { default as RadioInput } from './components/RadioInput';
export { default as RadioLabel } from './components/RadioLabel';
export { default as Score } from './components/Score';
+export { default as SearchBar } from './components/SearchBar';
+export { default as SearchButton } from './components/SearchButton';
+export { default as SearchField } from './components/SearchField';
export { default as Section } from './components/Section';
export { default as StackedBlock } from './components/StackedBlock';
export { default as Tab } from './components/Tab';