Skip to content

Commit 80a1ae0

Browse files
committed
feat: added nutrition info style
1 parent 4b404ec commit 80a1ae0

File tree

2 files changed

+192
-85
lines changed

2 files changed

+192
-85
lines changed

src/components/TabbedInfoBox.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ const TabbedInfoBox: React.FC<TabbedInfoBoxProps> = ({
276276
backgroundColor: getCategoryBackground(),
277277
border: `2px solid ${colours.content.border}`,
278278
height: boxHeight ? boxHeight + 32 : undefined,
279-
minHeight: isCollapsed ? 40 : 210,
279+
minHeight: isCollapsed ? 40 : 280,
280280
transition: "height 0.4s cubic-bezier(0.4,0,0.2,1), background-color 0.3s ease",
281281
position: "relative"
282282
}}

src/components/tabs/NutritionTabContent.tsx

Lines changed: 191 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -12,123 +12,230 @@ const NutritionTabContent: React.FC<NutritionTabContentProps> = ({
1212
animatedNutrition,
1313
}) => {
1414
return (
15-
<div className="w-full flex flex-col items-center opacity-0 animate-fade-in" style={{ animationDelay: '0.05s' }}>
15+
<div className="w-full flex flex-col items-center opacity-0 animate-fade-in px-1" style={{ animationDelay: '0.05s' }}>
1616
<h2
1717
className="text-lg font-bold mb-2 self-start"
1818
style={{ color: colours.text.primary }}
1919
>
2020
Nutrition
2121
</h2>
22-
<div className="flex flex-col items-center justify-center gap-4 w-full">
23-
{/* Nutrition Grade Circle */}
24-
<div className="flex flex-col items-center gap-2">
25-
<span className="relative inline-block w-24 h-24 align-middle">
26-
<svg width="96" height="96" viewBox="0 0 96 96" className="absolute top-0 left-0" style={{ zIndex: 1 }}>
27-
<circle
28-
cx="48" cy="48" r="40"
29-
fill="none"
30-
stroke={(() => {
22+
{/* Nutriscore Card */}
23+
<div
24+
className="w-full p-3 sm:p-4 rounded-xl border-2 mb-4"
25+
style={{
26+
backgroundColor: '#f1f5fb', // baby blue
27+
borderColor: colours.content.border
28+
}}
29+
>
30+
<div className="flex items-center justify-between gap-3">
31+
<div className="flex flex-col flex-1 min-w-0">
32+
<span
33+
className="text-lg font-medium"
34+
style={{ color: colours.text.primary }}
35+
>
36+
Nutriscore
37+
</span>
38+
<span
39+
className="text-[10px]"
40+
style={{ color: colours.text.muted }}
41+
>
42+
Certified by the Food Standards Agency
43+
</span>
44+
</div>
45+
<div className="flex-shrink-0">
46+
<div
47+
className="relative w-16 h-16 rounded-full border-2 border-dashed flex items-center justify-center"
48+
style={{
49+
borderColor: (() => {
3150
const score = animatedNutrition;
3251
if (score <= 2) return colours.score.low;
3352
if (score <= 3) return colours.score.medium;
3453
return colours.score.high;
35-
})()}
36-
strokeWidth="8"
37-
strokeDasharray={Math.PI * 2 * 40}
38-
strokeDashoffset={Math.PI * 2 * 40 * (1 - (animatedNutrition / 5))}
39-
strokeLinecap="round"
40-
style={{
41-
transition: 'stroke-dashoffset 0.7s cubic-bezier(0.4,0,0.2,1), stroke 0.7s cubic-bezier(0.4,0,0.2,1)',
42-
transform: 'rotate(-90deg)',
43-
transformOrigin: 'center center',
44-
}}
45-
/>
46-
</svg>
47-
<div className="absolute inset-0 flex flex-col items-center justify-center">
48-
<span
49-
className="text-4xl font-bold"
50-
style={{
51-
color: (() => {
52-
const score = animatedNutrition;
53-
if (score <= 2) return colours.score.low;
54-
if (score <= 3) return colours.score.medium;
55-
return colours.score.high;
56-
})()
57-
}}
58-
>
59-
{product.combinedNutritionGrade?.toUpperCase() || '?'}
54+
})(),
55+
backgroundColor: (() => {
56+
const score = animatedNutrition;
57+
if (score <= 2) return colours.score.low + '20'; // 20% opacity
58+
if (score <= 3) return colours.score.medium + '20';
59+
return colours.score.high + '20';
60+
})(),
61+
}}
62+
>
63+
<span className="relative inline-block w-12 h-12 align-middle">
64+
<svg width="48" height="48" viewBox="0 0 48 48" className="absolute top-0 left-0" style={{ zIndex: 1 }}>
65+
<circle
66+
cx="24" cy="24" r="18"
67+
fill="none"
68+
stroke={(() => {
69+
const score = animatedNutrition;
70+
if (score <= 2) return colours.score.low;
71+
if (score <= 3) return colours.score.medium;
72+
return colours.score.high;
73+
})()}
74+
strokeWidth="3"
75+
strokeDasharray={Math.PI * 2 * 18}
76+
strokeDashoffset={Math.PI * 2 * 18 * (1 - (animatedNutrition / 5))}
77+
strokeLinecap="round"
78+
style={{
79+
transition: 'stroke-dashoffset 0.7s cubic-bezier(0.4,0,0.2,1), stroke 0.7s cubic-bezier(0.4,0,0.2,1)',
80+
transform: 'rotate(-90deg)',
81+
transformOrigin: 'center center',
82+
}}
83+
/>
84+
</svg>
85+
<div className="absolute inset-0 flex flex-col items-center justify-center">
86+
<span
87+
className="text-xl font-bold"
88+
style={{
89+
color: (() => {
90+
const score = animatedNutrition;
91+
if (score <= 2) return colours.score.low;
92+
if (score <= 3) return colours.score.medium;
93+
return colours.score.high;
94+
})()
95+
}}
96+
>
97+
{product.combinedNutritionGrade?.toUpperCase() || '?'}
98+
</span>
99+
</div>
60100
</span>
61101
</div>
62-
</span>
63-
<span
64-
className="text-sm font-medium"
65-
style={{ color: colours.text.secondary }}
66-
>
67-
Nutrition Grade
68-
</span>
102+
</div>
69103
</div>
104+
</div>
70105

71-
{/* Nutrition Macros per 100g */}
72-
{product.nutritionMacros && Object.values(product.nutritionMacros).some(value => value !== undefined) && (
73-
<div className="w-full mt-4">
74-
<h3
75-
className="text-md font-semibold mb-3 text-center"
106+
{/* Nutrition Information Card */}
107+
<div
108+
className="w-full p-3 sm:p-4 rounded-xl border-2"
109+
style={{
110+
backgroundColor: '#f1f5fb', // baby blue
111+
borderColor: colours.content.border
112+
}}
113+
>
114+
<div className="w-full min-w-0">
115+
<div className="flex items-baseline gap-2 mb-3">
116+
<span
117+
className="text-lg font-medium"
76118
style={{ color: colours.text.primary }}
77119
>
78-
Nutrition Facts (per 100g)
79-
</h3>
80-
<div className="grid grid-cols-2 gap-3 w-full max-w-sm mx-auto">
120+
Nutrition Facts
121+
</span>
122+
<span
123+
className="text-xs"
124+
style={{ color: colours.text.secondary }}
125+
>
126+
per 100g
127+
</span>
128+
</div>
129+
{product.nutritionMacros && Object.values(product.nutritionMacros).some(value => value !== undefined) ? (
130+
<div className="w-full">
131+
<div className="flex flex-wrap gap-1.5 pb-2 break-words">
81132
{product.nutritionMacros.energy !== undefined && (
82-
<div className="flex justify-between items-center p-2 rounded-lg" style={{ backgroundColor: colours.content.surfaceSecondary }}>
83-
<span className="text-sm font-medium" style={{ color: colours.text.secondary }}>Energy</span>
84-
<span className="text-sm font-bold" style={{ color: colours.text.primary }}>{product.nutritionMacros.energy} kcal</span>
85-
</div>
133+
<span
134+
className="text-xs px-2 py-1 rounded-full border-2 shadow-lg"
135+
style={{
136+
backgroundColor: '#ffffff',
137+
color: colours.text.primary,
138+
borderColor: colours.content.border + 'CC' // 80% opacity
139+
}}
140+
>
141+
Energy: {product.nutritionMacros.energy} kcal
142+
</span>
86143
)}
87144
{product.nutritionMacros.proteins !== undefined && (
88-
<div className="flex justify-between items-center p-2 rounded-lg" style={{ backgroundColor: colours.content.surfaceSecondary }}>
89-
<span className="text-sm font-medium" style={{ color: colours.text.secondary }}>Protein</span>
90-
<span className="text-sm font-bold" style={{ color: colours.text.primary }}>{product.nutritionMacros.proteins}g</span>
91-
</div>
145+
<span
146+
className="text-xs px-2 py-1 rounded-full border-2 shadow-lg"
147+
style={{
148+
backgroundColor: '#ffffff',
149+
color: colours.text.primary,
150+
borderColor: colours.content.border + 'CC' // 80% opacity
151+
}}
152+
>
153+
Protein: {product.nutritionMacros.proteins}g
154+
</span>
92155
)}
93156
{product.nutritionMacros.carbohydrates !== undefined && (
94-
<div className="flex justify-between items-center p-2 rounded-lg" style={{ backgroundColor: colours.content.surfaceSecondary }}>
95-
<span className="text-sm font-medium" style={{ color: colours.text.secondary }}>Carbs</span>
96-
<span className="text-sm font-bold" style={{ color: colours.text.primary }}>{product.nutritionMacros.carbohydrates}g</span>
97-
</div>
157+
<span
158+
className="text-xs px-2 py-1 rounded-full border-2 shadow-lg"
159+
style={{
160+
backgroundColor: '#ffffff',
161+
color: colours.text.primary,
162+
borderColor: colours.content.border + 'CC' // 80% opacity
163+
}}
164+
>
165+
Carbs: {product.nutritionMacros.carbohydrates}g
166+
</span>
98167
)}
99168
{product.nutritionMacros.sugars !== undefined && (
100-
<div className="flex justify-between items-center p-2 rounded-lg" style={{ backgroundColor: colours.content.surfaceSecondary }}>
101-
<span className="text-sm font-medium" style={{ color: colours.text.secondary }}>Sugars</span>
102-
<span className="text-sm font-bold" style={{ color: colours.text.primary }}>{product.nutritionMacros.sugars}g</span>
103-
</div>
169+
<span
170+
className="text-xs px-2 py-1 rounded-full border-2 shadow-lg"
171+
style={{
172+
backgroundColor: '#ffffff',
173+
color: colours.text.primary,
174+
borderColor: colours.content.border + 'CC' // 80% opacity
175+
}}
176+
>
177+
Sugars: {product.nutritionMacros.sugars}g
178+
</span>
104179
)}
105180
{product.nutritionMacros.fat !== undefined && (
106-
<div className="flex justify-between items-center p-2 rounded-lg" style={{ backgroundColor: colours.content.surfaceSecondary }}>
107-
<span className="text-sm font-medium" style={{ color: colours.text.secondary }}>Fat</span>
108-
<span className="text-sm font-bold" style={{ color: colours.text.primary }}>{product.nutritionMacros.fat}g</span>
109-
</div>
181+
<span
182+
className="text-xs px-2 py-1 rounded-full border-2 shadow-lg"
183+
style={{
184+
backgroundColor: '#ffffff',
185+
color: colours.text.primary,
186+
borderColor: colours.content.border + 'CC' // 80% opacity
187+
}}
188+
>
189+
Fat: {product.nutritionMacros.fat}g
190+
</span>
110191
)}
111192
{product.nutritionMacros.saturatedFat !== undefined && (
112-
<div className="flex justify-between items-center p-2 rounded-lg" style={{ backgroundColor: colours.content.surfaceSecondary }}>
113-
<span className="text-sm font-medium" style={{ color: colours.text.secondary }}>Sat. Fat</span>
114-
<span className="text-sm font-bold" style={{ color: colours.text.primary }}>{product.nutritionMacros.saturatedFat}g</span>
115-
</div>
193+
<span
194+
className="text-xs px-2 py-1 rounded-full border-2 shadow-lg"
195+
style={{
196+
backgroundColor: '#ffffff',
197+
color: colours.text.primary,
198+
borderColor: colours.content.border + 'CC' // 80% opacity
199+
}}
200+
>
201+
Sat. Fat: {product.nutritionMacros.saturatedFat}g
202+
</span>
116203
)}
117204
{product.nutritionMacros.fiber !== undefined && (
118-
<div className="flex justify-between items-center p-2 rounded-lg" style={{ backgroundColor: colours.content.surfaceSecondary }}>
119-
<span className="text-sm font-medium" style={{ color: colours.text.secondary }}>Fiber</span>
120-
<span className="text-sm font-bold" style={{ color: colours.text.primary }}>{product.nutritionMacros.fiber}g</span>
121-
</div>
205+
<span
206+
className="text-xs px-2 py-1 rounded-full border-2 shadow-lg"
207+
style={{
208+
backgroundColor: '#ffffff',
209+
color: colours.text.primary,
210+
borderColor: colours.content.border + 'CC' // 80% opacity
211+
}}
212+
>
213+
Fiber: {product.nutritionMacros.fiber}g
214+
</span>
122215
)}
123216
{product.nutritionMacros.sodium !== undefined && (
124-
<div className="flex justify-between items-center p-2 rounded-lg" style={{ backgroundColor: colours.content.surfaceSecondary }}>
125-
<span className="text-sm font-medium" style={{ color: colours.text.secondary }}>Sodium</span>
126-
<span className="text-sm font-bold" style={{ color: colours.text.primary }}>{(product.nutritionMacros.sodium * 1000).toFixed(0)}mg</span>
127-
</div>
217+
<span
218+
className="text-xs px-2 py-1 rounded-full border-2 shadow-lg"
219+
style={{
220+
backgroundColor: '#ffffff',
221+
color: colours.text.primary,
222+
borderColor: colours.content.border + 'CC' // 80% opacity
223+
}}
224+
>
225+
Sodium: {(product.nutritionMacros.sodium * 1000).toFixed(0)}mg
226+
</span>
128227
)}
228+
</div>
129229
</div>
130-
</div>
131-
)}
230+
) : (
231+
<div
232+
className="text-sm text-center py-4"
233+
style={{ color: colours.text.secondary }}
234+
>
235+
No nutrition information available
236+
</div>
237+
)}
238+
</div>
132239
</div>
133240
</div>
134241
);

0 commit comments

Comments
 (0)