diff --git a/src/components/breadcrumbs/Breadcrumbs.tsx b/src/components/breadcrumbs/Breadcrumbs.tsx
new file mode 100644
index 0000000..56fcf26
--- /dev/null
+++ b/src/components/breadcrumbs/Breadcrumbs.tsx
@@ -0,0 +1,28 @@
+import { useMatches } from 'react-router-dom';
+
+import classes from './breadcrumbs.module.css';
+
+export function Breadcrumbs() {
+ const matches = useMatches();
+ const crumbs = matches
+ // first get rid of any matches that don't have handle and crumb
+ .filter((match) => Boolean(match.handle?.crumb))
+ // now map them into an array of elements, passing the loader
+ // data to each one
+ .map((match) => match.handle.crumb(match.data));
+
+ return (
+
+
+ {crumbs.map((crumb, index) => (
+ // eslint-disable-next-line react/no-array-index-key
+
+ {crumb}
+
+ ))}
+
+
+ );
+}
+
+export default Breadcrumbs;
diff --git a/src/components/breadcrumbs/breadcrumbs.module.css b/src/components/breadcrumbs/breadcrumbs.module.css
new file mode 100644
index 0000000..4ee12f8
--- /dev/null
+++ b/src/components/breadcrumbs/breadcrumbs.module.css
@@ -0,0 +1,18 @@
+.breadcrumbs {
+ display: flex;
+ flex-direction: row;
+ gap: 0.4rem;
+}
+
+.crumb {
+ &::after {
+ content: '>';
+ margin-left: 0.4rem;
+ }
+
+ &:last-child {
+ &::after {
+ content: '';
+ }
+ }
+}
diff --git a/src/components/breadcrumbs/index.ts b/src/components/breadcrumbs/index.ts
new file mode 100644
index 0000000..28140a2
--- /dev/null
+++ b/src/components/breadcrumbs/index.ts
@@ -0,0 +1 @@
+export { Breadcrumbs } from './Breadcrumbs';
diff --git a/src/components/input/Input.stories.ts b/src/components/input/Input.stories.ts
new file mode 100644
index 0000000..3ce5537
--- /dev/null
+++ b/src/components/input/Input.stories.ts
@@ -0,0 +1,27 @@
+import type { Meta, StoryObj } from '@storybook/react';
+
+import { Input } from './Input';
+
+// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
+const meta = {
+ title: 'Components/Input',
+ component: Input,
+ tags: ['autodocs'],
+ argTypes: {},
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
+export const Default: Story = {
+ args: {
+ size: 'large',
+ },
+};
+
+export const Medium: Story = {
+ args: {
+ size: 'medium',
+ },
+};
diff --git a/src/components/input/Input.tsx b/src/components/input/Input.tsx
new file mode 100644
index 0000000..eab2589
--- /dev/null
+++ b/src/components/input/Input.tsx
@@ -0,0 +1,49 @@
+interface InputProps {
+ /**
+ * How large should the button be?
+ */
+ size?: 'medium' | 'large';
+ /**
+ * What placeholder color to use
+ */
+ placeholder?: string;
+ /**
+ * Button contents
+ */
+ label?: string;
+ /**
+ * Input value
+ */
+ value?: string;
+ /**
+ * Optional change handler
+ */
+ onChange?: (e: React.ChangeEvent) => void;
+}
+
+/**
+ * Primary UI component for user interaction
+ */
+export function Input({
+ size = 'large',
+ placeholder,
+ value,
+ onChange,
+ label,
+ ...props
+}: InputProps) {
+ const sizeAdjusted = size === 'large' ? '3xl' : 'xl';
+ return (
+
+ );
+}
+
+export default Input;
diff --git a/src/components/input/index.ts b/src/components/input/index.ts
new file mode 100644
index 0000000..6322cf3
--- /dev/null
+++ b/src/components/input/index.ts
@@ -0,0 +1 @@
+export { Input } from './Input';