diff --git a/src/app/(tabs)/compare.tsx b/src/app/(tabs)/compare.tsx index 7c4fef7..9f3bc2e 100644 --- a/src/app/(tabs)/compare.tsx +++ b/src/app/(tabs)/compare.tsx @@ -61,13 +61,13 @@ export default function CompareScreen() { ))} - + ({ opacity: pressed ? 0.75 : 1 })}> - Grind guide + Water - What fine, medium and coarse actually mean + Temperature and minerals, the other 98% of the cup diff --git a/src/app/_layout.tsx b/src/app/_layout.tsx index 69a9531..55101f9 100644 --- a/src/app/_layout.tsx +++ b/src/app/_layout.tsx @@ -21,7 +21,7 @@ export default function RootLayout() { - + diff --git a/src/app/grind.tsx b/src/app/grind.tsx deleted file mode 100644 index ae0dbcd..0000000 --- a/src/app/grind.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { Stack } from 'expo-router'; -import { StyleSheet, View } from 'react-native'; - -import { Card, Chip, Eyebrow, Rule, Screen, Type } from '@/components/ui'; -import { Spacing } from '@/constants/theme'; -import { GRIND_NOTES, GRIND_SCALE, METHODS } from '@/data/methods'; -import { usePalette } from '@/hooks/use-palette'; - -export default function GrindScreen() { - const c = usePalette(); - return ( - <> - - - - Particle size - Grind - - Grind size sets how fast water can move through the bed. Everything else — dose, - temperature, time — is a correction on top of it. - - - - - {GRIND_SCALE.map((g, i) => { - const users = METHODS.filter((m) => m.grind === g); - return ( - - {i > 0 && } - - - - {Array.from({ length: 5 - i }, (_, d) => ( - - ))} - - - {g} - - - - {GRIND_NOTES[g]} - - {users.length > 0 && ( - - {users.map((m) => ( - - {m.name} - - ))} - - )} - - - ); - })} - - - - Rule of thumb - - Sour and thin means the water left too early — go finer. Harsh and drying means it - stayed too long — go coarser. Change one thing at a time. - - - - - ); -} - -const styles = StyleSheet.create({ - masthead: { gap: Spacing.two, paddingTop: Spacing.two }, - scale: { paddingVertical: 0 }, - step: { paddingVertical: Spacing.four, gap: Spacing.two }, - stepHead: { flexDirection: 'row', alignItems: 'center', gap: Spacing.three }, - dots: { flexDirection: 'row', alignItems: 'center', gap: 2, width: 46 }, - dot: { borderRadius: 4 }, - stepName: { textTransform: 'capitalize' }, - users: { flexDirection: 'row', flexWrap: 'wrap', gap: Spacing.two, paddingTop: Spacing.one }, - note: { gap: Spacing.two }, -}); diff --git a/src/app/water.tsx b/src/app/water.tsx new file mode 100644 index 0000000..4eefd28 --- /dev/null +++ b/src/app/water.tsx @@ -0,0 +1,126 @@ +import { Stack } from 'expo-router'; +import { StyleSheet, View } from 'react-native'; + +import { Card, Chip, Eyebrow, Rule, Screen, Type } from '@/components/ui'; +import { Spacing } from '@/constants/theme'; +import { usePalette } from '@/hooks/use-palette'; + +/* Temperature bands, coolest first. The chips name the methods that sit in + each band, which is the same shape the grind guide used. */ +const BANDS = [ + { + range: '80–88°C', + name: 'cool', + note: 'Light roasts stay sweet instead of turning sharp. Slower to extract, so give it time.', + methods: ['AeroPress'], + }, + { + range: '90–94°C', + name: 'standard', + note: 'Where most recipes land. Hot enough to pull sugars, not so hot it strips the cup.', + methods: ['V60', 'French press'], + }, + { + range: '95–100°C', + name: 'hot', + note: 'Dark roasts and stale beans need the extra energy. Anything fresh will taste harsh.', + methods: [], + }, +]; + +const MINERALS = [ + ['Calcium + magnesium', 'Carry flavour. Too little and the cup reads flat and watery.'], + ['Bicarbonate', 'Buffers acidity. Too much and everything tastes dull and chalky.'], + ['Chlorine', 'Ruins a cup on its own. Filter it out before anything else.'], +]; + +export default function WaterScreen() { + const c = usePalette(); + return ( + <> + + + + 98% of the cup + Water + + Coffee is almost entirely water, so the water decides how much of the bean you ever + taste. Temperature sets the pace, minerals decide what comes through. + + + + + {BANDS.map((band, i) => ( + + {i > 0 && } + + + + + + + {band.range} + + {band.name} + + + + + {band.note} + + {band.methods.length > 0 && ( + + {band.methods.map((m) => ( + + {m} + + ))} + + )} + + + ))} + + + + What is in it + {MINERALS.map(([label, text]) => ( + + {label} + + {text} + + + ))} + + + + ); +} + +const styles = StyleSheet.create({ + masthead: { gap: Spacing.two, paddingTop: Spacing.two }, + bands: { paddingVertical: 0 }, + band: { paddingVertical: Spacing.four, gap: Spacing.two }, + bandHead: { flexDirection: 'row', alignItems: 'center', gap: Spacing.three }, + gauge: { + width: 14, + height: 34, + borderWidth: 1, + borderRadius: 3, + justifyContent: 'flex-end', + overflow: 'hidden', + }, + gaugeFill: { width: '100%' }, + bandTitle: { gap: 2 }, + methods: { flexDirection: 'row', flexWrap: 'wrap', gap: Spacing.two, paddingTop: Spacing.one }, + note: { gap: Spacing.three }, + mineral: { gap: 2 }, +});