diff --git a/frontend/src/components/MealList.jsx b/frontend/src/components/MealList.jsx
index 7a6390f..f1f293d 100644
--- a/frontend/src/components/MealList.jsx
+++ b/frontend/src/components/MealList.jsx
@@ -869,7 +869,7 @@ export default function MealList({ refreshTrigger, onMealDeleted, onMealUpdated,
min="0"
value={component.base_calories}
onChange={(e) => updateComponentNutrition(idx, 'base_calories', e.target.value)}
- className="w-full px-1 sm:px-2 py-1 border text-xs sm:text-sm text-white"
+ className={`w-full px-1 sm:px-2 py-1 border ${colors.inputBorder} ${colors.inputBg} text-xs sm:text-sm text-white`}
/>
@@ -880,7 +880,7 @@ export default function MealList({ refreshTrigger, onMealDeleted, onMealUpdated,
min="0"
value={component.base_protein_g}
onChange={(e) => updateComponentNutrition(idx, 'base_protein_g', e.target.value)}
- className="w-full px-1 sm:px-2 py-1 border text-xs sm:text-sm text-white"
+ className={`w-full px-1 sm:px-2 py-1 border ${colors.inputBorder} ${colors.inputBg} text-xs sm:text-sm text-white`}
/>
@@ -891,7 +891,7 @@ export default function MealList({ refreshTrigger, onMealDeleted, onMealUpdated,
min="0"
value={component.base_carbs_g}
onChange={(e) => updateComponentNutrition(idx, 'base_carbs_g', e.target.value)}
- className="w-full px-1 sm:px-2 py-1 border text-xs sm:text-sm text-white"
+ className={`w-full px-1 sm:px-2 py-1 border ${colors.inputBorder} ${colors.inputBg} text-xs sm:text-sm text-white`}
/>
@@ -902,7 +902,7 @@ export default function MealList({ refreshTrigger, onMealDeleted, onMealUpdated,
min="0"
value={component.base_fat_g}
onChange={(e) => updateComponentNutrition(idx, 'base_fat_g', e.target.value)}
- className="w-full px-1 sm:px-2 py-1 border text-xs sm:text-sm text-white"
+ className={`w-full px-1 sm:px-2 py-1 border ${colors.inputBorder} ${colors.inputBg} text-xs sm:text-sm text-white`}
/>
@@ -913,7 +913,7 @@ export default function MealList({ refreshTrigger, onMealDeleted, onMealUpdated,
min="0"
value={component.base_fiber_g}
onChange={(e) => updateComponentNutrition(idx, 'base_fiber_g', e.target.value)}
- className="w-full px-1 sm:px-2 py-1 border text-xs sm:text-sm text-white"
+ className={`w-full px-1 sm:px-2 py-1 border ${colors.inputBorder} ${colors.inputBg} text-xs sm:text-sm text-white`}
/>
@@ -1027,7 +1027,7 @@ export default function MealList({ refreshTrigger, onMealDeleted, onMealUpdated,
min="0"
value={simpleMacros.base_calories}
onChange={(e) => updateSimpleMacro('base_calories', e.target.value)}
- className="w-full px-1 sm:px-2 py-1 border text-xs sm:text-sm text-white"
+ className={`w-full px-1 sm:px-2 py-1 border ${colors.inputBorder} ${colors.inputBg} text-xs sm:text-sm text-white`}
/>
@@ -1038,7 +1038,7 @@ export default function MealList({ refreshTrigger, onMealDeleted, onMealUpdated,
min="0"
value={simpleMacros.base_protein_g}
onChange={(e) => updateSimpleMacro('base_protein_g', e.target.value)}
- className="w-full px-1 sm:px-2 py-1 border text-xs sm:text-sm text-white"
+ className={`w-full px-1 sm:px-2 py-1 border ${colors.inputBorder} ${colors.inputBg} text-xs sm:text-sm text-white`}
/>
@@ -1049,7 +1049,7 @@ export default function MealList({ refreshTrigger, onMealDeleted, onMealUpdated,
min="0"
value={simpleMacros.base_carbs_g}
onChange={(e) => updateSimpleMacro('base_carbs_g', e.target.value)}
- className="w-full px-1 sm:px-2 py-1 border text-xs sm:text-sm text-white"
+ className={`w-full px-1 sm:px-2 py-1 border ${colors.inputBorder} ${colors.inputBg} text-xs sm:text-sm text-white`}
/>
@@ -1060,7 +1060,7 @@ export default function MealList({ refreshTrigger, onMealDeleted, onMealUpdated,
min="0"
value={simpleMacros.base_fat_g}
onChange={(e) => updateSimpleMacro('base_fat_g', e.target.value)}
- className="w-full px-1 sm:px-2 py-1 border text-xs sm:text-sm text-white"
+ className={`w-full px-1 sm:px-2 py-1 border ${colors.inputBorder} ${colors.inputBg} text-xs sm:text-sm text-white`}
/>
@@ -1071,7 +1071,7 @@ export default function MealList({ refreshTrigger, onMealDeleted, onMealUpdated,
min="0"
value={simpleMacros.base_fiber_g}
onChange={(e) => updateSimpleMacro('base_fiber_g', e.target.value)}
- className="w-full px-1 sm:px-2 py-1 border text-xs sm:text-sm text-white"
+ className={`w-full px-1 sm:px-2 py-1 border ${colors.inputBorder} ${colors.inputBg} text-xs sm:text-sm text-white`}
/>
diff --git a/frontend/src/components/__tests__/MealList.test.jsx b/frontend/src/components/__tests__/MealList.test.jsx
new file mode 100644
index 0000000..c11561d
--- /dev/null
+++ b/frontend/src/components/__tests__/MealList.test.jsx
@@ -0,0 +1,84 @@
+/**
+ * Tests for MealList component - edit mode input contrast (issue #3)
+ */
+import { describe, it, expect, vi, beforeEach } from 'vitest';
+import { render, screen, fireEvent } from '@testing-library/react';
+import MealList from '../MealList';
+
+// All vi.mock calls MUST use inline factory functions (no external const references),
+// so shared fixtures go through vi.hoisted.
+const { mockMeal, createBuilder } = vi.hoisted(() => {
+ const mockMeal = {
+ id: 'meal-1',
+ user_id: 'test-user-id',
+ meal_name: 'Grilled Chicken',
+ meal_type: 'lunch',
+ consumed_at: '2024-01-15T12:00:00.000Z',
+ total_calories: 200,
+ total_protein_g: 30,
+ total_carbs_g: 5,
+ total_fat_g: 8,
+ total_fiber_g: 2,
+ portion_size: null,
+ portion_unit: null,
+ is_compound: false,
+ notes: '',
+ };
+
+ const createBuilder = (result) => {
+ const builder = {};
+ ['select', 'eq', 'gte', 'lte', 'order', 'limit', 'delete', 'insert', 'update'].forEach((fn) => {
+ builder[fn] = () => builder;
+ });
+ builder.single = () => Promise.resolve(result);
+ builder.then = (resolve, reject) => Promise.resolve(result).then(resolve, reject);
+ return builder;
+ };
+
+ return { mockMeal, createBuilder };
+});
+
+vi.mock('../../supabaseClient', () => ({
+ supabase: {
+ auth: {
+ getUser: vi.fn().mockResolvedValue({ data: { user: { id: 'test-user-id' } } }),
+ },
+ from: vi.fn((table) => {
+ if (table === 'meals') {
+ return createBuilder({ data: [mockMeal], error: null });
+ }
+ if (table === 'meal_components') {
+ return createBuilder({ data: [], error: null });
+ }
+ return createBuilder({ data: [], error: null });
+ }),
+ storage: {
+ from: vi.fn(() => ({
+ remove: vi.fn().mockResolvedValue({ data: null, error: null }),
+ })),
+ },
+ },
+}));
+
+describe('MealList - edit mode input contrast', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it('gives every macro number input a dark background in manual edit mode', async () => {
+ render();
+
+ const editButton = await screen.findByRole('button', { name: 'Edit' });
+ fireEvent.click(editButton);
+
+ const editManuallyButton = await screen.findByText('Edit Manually');
+ fireEvent.click(editManuallyButton);
+
+ const numberInputs = screen.getAllByRole('spinbutton');
+ expect(numberInputs).toHaveLength(5);
+ numberInputs.forEach((input) => {
+ expect(input).toHaveClass('bg-black');
+ expect(input).toHaveClass('border-[#1a1a1a]');
+ });
+ });
+});
diff --git a/frontend/src/index.css b/frontend/src/index.css
index 41c44d6..2248c32 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -16,6 +16,7 @@
body {
@apply bg-black text-white antialiased;
font-family: 'Plus Jakarta Sans', system-ui, sans-serif;
+ color-scheme: dark;
}
h1, h2, h3, h4, h5, h6 {