diff --git a/frontend/src/lib/onboarding/CategorySection.svelte b/frontend/src/lib/onboarding/CategorySection.svelte
new file mode 100644
index 0000000..d9e4f4d
--- /dev/null
+++ b/frontend/src/lib/onboarding/CategorySection.svelte
@@ -0,0 +1,26 @@
+
+
+
+
+ {name}
+
+
+ {#each ingredients as ingredient (ingredient.id)}
+ onToggle(ingredient.id, value)}
+ />
+ {/each}
+
+
diff --git a/frontend/src/lib/onboarding/CategorySection.test.ts b/frontend/src/lib/onboarding/CategorySection.test.ts
new file mode 100644
index 0000000..b37579f
--- /dev/null
+++ b/frontend/src/lib/onboarding/CategorySection.test.ts
@@ -0,0 +1,55 @@
+import { describe, it, expect, vi } from 'vitest';
+import { render, screen } from '@testing-library/svelte';
+import CategorySection from './CategorySection.svelte';
+
+const mockIngredients = [
+ { id: '1', name: 'Olivenöl', isStaple: true },
+ { id: '2', name: 'Butter', isStaple: false },
+ { id: '3', name: 'Kokosöl', isStaple: false }
+];
+
+describe('CategorySection', () => {
+ it('renders the category name as a heading', () => {
+ render(CategorySection, {
+ props: { name: 'Öle & Fette', ingredients: mockIngredients, onToggle: vi.fn() }
+ });
+ expect(screen.getByText('Öle & Fette')).toBeInTheDocument();
+ });
+
+ it('renders a chip for each ingredient', () => {
+ render(CategorySection, {
+ props: { name: 'Öle & Fette', ingredients: mockIngredients, onToggle: vi.fn() }
+ });
+ expect(screen.getByRole('button', { name: 'Olivenöl' })).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: 'Butter' })).toBeInTheDocument();
+ expect(screen.getByRole('button', { name: 'Kokosöl' })).toBeInTheDocument();
+ });
+
+ it('reflects isStaple state on each chip', () => {
+ render(CategorySection, {
+ props: { name: 'Öle & Fette', ingredients: mockIngredients, onToggle: vi.fn() }
+ });
+ expect(screen.getByRole('button', { name: 'Olivenöl' })).toHaveAttribute('aria-pressed', 'true');
+ expect(screen.getByRole('button', { name: 'Butter' })).toHaveAttribute('aria-pressed', 'false');
+ });
+
+ it('calls onToggle with ingredient id and new value when chip is clicked', async () => {
+ const { userEvent } = await import('@testing-library/user-event');
+ const user = userEvent.setup();
+ const onToggle = vi.fn();
+ render(CategorySection, {
+ props: { name: 'Öle & Fette', ingredients: mockIngredients, onToggle }
+ });
+
+ await user.click(screen.getByRole('button', { name: 'Butter' }));
+ expect(onToggle).toHaveBeenCalledWith('2', true);
+ });
+
+ it('renders an empty category without crashing', () => {
+ render(CategorySection, {
+ props: { name: 'Leer', ingredients: [], onToggle: vi.fn() }
+ });
+ expect(screen.getByText('Leer')).toBeInTheDocument();
+ expect(screen.queryByRole('button')).not.toBeInTheDocument();
+ });
+});