diff --git a/.gitignore b/.gitignore
index 1eb2038..471aa24 100644
--- a/.gitignore
+++ b/.gitignore
@@ -62,3 +62,4 @@ pids
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+.sprint-plan.md
diff --git a/apps/web/components/TagMultiSelect.tsx b/apps/web/components/TagMultiSelect.tsx
new file mode 100644
index 0000000..94a871f
--- /dev/null
+++ b/apps/web/components/TagMultiSelect.tsx
@@ -0,0 +1,46 @@
+'use client';
+
+interface TagMultiSelectProps {
+ options: readonly string[];
+ selected: string[];
+ onChange: (selected: string[]) => void;
+ max?: number;
+ label: string;
+}
+
+export function TagMultiSelect({ options, selected, onChange, max, label }: TagMultiSelectProps) {
+ function toggle(tag: string) {
+ if (selected.includes(tag)) {
+ onChange(selected.filter((t) => t !== tag));
+ } else {
+ if (max && selected.length >= max) return;
+ onChange([...selected, tag]);
+ }
+ }
+
+ return (
+
+
{label}
+
+ {options.map((tag) => {
+ const isSelected = selected.includes(tag);
+ const isDisabled = !isSelected && max !== undefined && selected.length >= max;
+
+ return (
+
+ );
+ })}
+
+ {max &&
{selected.length} of {max} selected
}
+
+ );
+}
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts
index 3205808..cf8f814 100644
--- a/packages/shared/src/index.ts
+++ b/packages/shared/src/index.ts
@@ -1,2 +1,4 @@
export * from './types/auth.js';
export * from './validation/auth.js';
+export * from './types/tags.js';
+export * from './validation/tags.js';
diff --git a/packages/shared/src/types/tags.ts b/packages/shared/src/types/tags.ts
new file mode 100644
index 0000000..5ec3066
--- /dev/null
+++ b/packages/shared/src/types/tags.ts
@@ -0,0 +1,41 @@
+export const CUISINE_TAGS = [
+ 'american',
+ 'barbecue',
+ 'breakfast',
+ 'burgers',
+ 'caribbean',
+ 'chinese',
+ 'desserts',
+ 'fast-food',
+ 'french',
+ 'greek',
+ 'indian',
+ 'italian',
+ 'japanese',
+ 'korean',
+ 'lebanese',
+ 'mediterranean',
+ 'mexican',
+ 'pizza',
+ 'seafood',
+ 'sushi',
+ 'thai',
+ 'turkish',
+ 'vegan-friendly',
+ 'vietnamese',
+] as const;
+
+export type CuisineTag = (typeof CUISINE_TAGS)[number];
+
+export const DIETARY_TAGS = [
+ 'vegetarian',
+ 'vegan',
+ 'gluten-free',
+ 'halal',
+ 'kosher',
+ 'dairy-free',
+ 'nut-free',
+ 'low-carb',
+] as const;
+
+export type DietaryTag = (typeof DIETARY_TAGS)[number];
diff --git a/packages/shared/src/validation/tags.ts b/packages/shared/src/validation/tags.ts
new file mode 100644
index 0000000..fc4eb9d
--- /dev/null
+++ b/packages/shared/src/validation/tags.ts
@@ -0,0 +1,5 @@
+import { z } from 'zod';
+import { CUISINE_TAGS, DIETARY_TAGS } from '../types/tags.js';
+
+export const cuisineTagSchema = z.enum(CUISINE_TAGS);
+export const dietaryTagSchema = z.enum(DIETARY_TAGS);