Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/app/(tabs)/compare.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@ export default function CompareScreen() {
))}
</View>

<Link href="/grind" asChild>
<Link href="/water" asChild>
<Pressable style={({ pressed }) => ({ opacity: pressed ? 0.75 : 1 })}>
<View style={[styles.cta, { borderColor: c.line, backgroundColor: c.card }]}>
<View style={styles.ctaText}>
<Type variant="heading">Grind guide</Type>
<Type variant="heading">Water</Type>
<Type variant="caption" tone="muted">
What fine, medium and coarse actually mean
Temperature and minerals, the other 98% of the cup
</Type>
</View>
<Type variant="title" tone="roast" style={styles.chevron}>
Expand Down
2 changes: 1 addition & 1 deletion src/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default function RootLayout() {
<Stack.Screen name="method/aeropress" options={{ title: 'AeroPress' }} />
<Stack.Screen name="method/french-press" options={{ title: 'French Press' }} />
<Stack.Screen name="brew/[id]" options={{ title: 'Brew timer' }} />
<Stack.Screen name="grind" options={{ title: 'Grind guide' }} />
<Stack.Screen name="water" options={{ title: 'Water' }} />
<Stack.Screen name="about" options={{ presentation: 'modal', title: 'About Brew' }} />
</Stack>
</ThemeProvider>
Expand Down
87 changes: 0 additions & 87 deletions src/app/grind.tsx

This file was deleted.

126 changes: 126 additions & 0 deletions src/app/water.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<>
<Stack.Screen options={{ title: 'Water' }} />
<Screen>
<View style={styles.masthead}>
<Eyebrow>98% of the cup</Eyebrow>
<Type variant="display">Water</Type>
<Type variant="body" tone="muted">
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.
</Type>
</View>

<Card style={styles.bands}>
{BANDS.map((band, i) => (
<View key={band.range}>
{i > 0 && <Rule />}
<View style={styles.band}>
<View style={styles.bandHead}>
<View
style={[styles.gauge, { borderColor: c.line }]}
>
<View
style={[
styles.gaugeFill,
{ backgroundColor: c.roast, height: `${34 + i * 33}%` },
]}
/>
</View>
<View style={styles.bandTitle}>
<Type variant="heading">{band.range}</Type>
<Type variant="label" tone="muted">
{band.name}
</Type>
</View>
</View>
<Type variant="caption" tone="muted">
{band.note}
</Type>
{band.methods.length > 0 && (
<View style={styles.methods}>
{band.methods.map((m) => (
<Chip key={m} tone="roast">
{m}
</Chip>
))}
</View>
)}
</View>
</View>
))}
</Card>

<View style={styles.note}>
<Eyebrow>What is in it</Eyebrow>
{MINERALS.map(([label, text]) => (
<View key={label} style={styles.mineral}>
<Type variant="label">{label}</Type>
<Type variant="caption" tone="muted">
{text}
</Type>
</View>
))}
</View>
</Screen>
</>
);
}

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 },
});
Loading