Skip to content
Merged
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
Binary file added client/src/assets/food/foodPairing.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 8 additions & 9 deletions client/src/components/ExploreByTaste.css
Original file line number Diff line number Diff line change
Expand Up @@ -287,20 +287,19 @@ main.taste-layout {
line-height: 1.15;
}

.taste-glass-box {
.taste-wine-image-box {
flex: none;
display: grid;
place-items: center;
width: 3.25rem;
height: 4.25rem;
border-radius: 0.5rem;
background: var(--color-sunk);
color: var(--color-muted);
width: 3.5rem;
height: 5rem;
}

.taste-glass {
width: 28px;
height: 44px;
.taste-wine-image {
width: auto;
height: 100%;
max-width: 100%;
object-fit: contain;
}

.taste-winery {
Expand Down
90 changes: 79 additions & 11 deletions client/src/components/FindWineByFood.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,53 @@
/* ---------- Hero ---------- */

.food-hero {
background:
radial-gradient(circle at 82% 50%, rgb(123 30 43 / 0.12), transparent 45%),
linear-gradient(90deg, var(--color-bg) 35%, var(--color-panel));
background: var(--color-bg);
border-bottom: 1px solid var(--color-border);
overflow: hidden;
}

.food-hero-inner {
max-width: 78rem;
margin: 0 auto;
padding: 3rem 1rem;
width: 100%;
min-height: 17rem;
margin: 0;
padding-left: max(1rem, calc((100vw - 78rem) / 2 + 1rem));
padding-right: 0;

display: grid;
grid-template-columns: minmax(22rem, 0.7fr) minmax(0, 1.6fr);
align-items: stretch;
}

.food-hero-copy {
align-self: center;
padding: 3rem 2.5rem 3rem 0;
}

.food-hero-media {
position: relative;
min-height: 17rem;
overflow: hidden;
}

/* Makes the transition between text and image softer */
.food-hero-media::before {
content: '';
position: absolute;
inset: 0 auto 0 0;
z-index: 1;
width: 5rem;
pointer-events: none;

background: linear-gradient(90deg, var(--color-bg) 0%, transparent 100%);
}

.food-hero-image {
display: block;
width: 100%;
height: 100%;
min-height: 17rem;
object-fit: cover;
object-position: center;
}

.food-eyebrow {
Expand Down Expand Up @@ -194,19 +231,27 @@ main.food-page {
.food-card {
height: 100%;
display: grid;
grid-template-columns: 4.5rem minmax(0, 1fr);
grid-template-columns: 6rem minmax(0, 1fr);
gap: 1rem;
padding: 1.1rem;
border: 1px solid var(--color-border);
border-radius: 0.75rem;
background: var(--color-surface);
}

.food-bottle-wrap {
display: flex;
justify-content: center;
align-items: center;
width: 6rem;
min-height: 11rem;
}

.food-bottle {
width: 100%;
height: auto;
max-height: 10rem;
align-self: center;
width: auto;
height: 11rem;
max-width: 5.5rem;
object-fit: contain;
}

.food-card-body {
Expand Down Expand Up @@ -335,6 +380,29 @@ main.food-page {
color: var(--color-muted);
}

@media (max-width: 48rem) {
.food-hero-inner {
grid-template-columns: 1fr;
padding: 0;
}

.food-hero-copy {
padding: 2.25rem 1rem;
}

.food-hero-media {
min-height: 12rem;
}

.food-hero-image {
min-height: 12rem;
}

.food-hero-media::before {
display: none;
}
}

@media (prefers-reduced-motion: reduce) {
.food-category {
transition: none;
Expand Down
22 changes: 16 additions & 6 deletions client/src/components/FindWineByFood.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { useSearchParams } from 'react-router';
import { useQuery } from '@apollo/client/react';
import FoodWineCard from './FoodWineCard';
import Pagination from './Pagination';
import foodPairingImage from '../assets/food/foodPairing.png';

import {
FOOD_CATEGORIES_QUERY,
WINES_BY_FOOD_QUERY,
Expand Down Expand Up @@ -86,12 +88,20 @@ export default function FindWineByFood() {
<>
<section className="food-hero">
<div className="food-hero-inner">
<p className="food-eyebrow">Explore</p>
<h1>Find wine by food</h1>
<p>
Choose what you’re eating and discover wines that make it even
better.
</p>
<div className="food-hero-copy">
<p className="food-eyebrow">Explore</p>

<h1>Find wine by food</h1>

<p>
Choose what you’re eating and discover wines that make it even
better.
</p>
</div>

<div className="food-hero-media">
<img src={foodPairingImage} alt="" className="food-hero-image" />
</div>
</div>
</section>

Expand Down
47 changes: 5 additions & 42 deletions client/src/components/FoodWineCard.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,7 @@
import { Link } from 'react-router';
import type { FoodWine } from '../types/wine';
import { ACIDITY_LEVELS, BODY_LEVELS, wineTypeColor } from '../data/taste';

/**
* A wine bottle in the colour of the wine type. Decorative only.
* X-Wines has no pictures of the wines, so we draw one.
*/
function Bottle({ type }: { type: string }) {
return (
<svg viewBox="0 0 40 120" aria-hidden="true" className="food-bottle">
<path
d="M15 4h10v26c0 6 11 10 11 24v58c0 3-2 5-5 5H9c-3 0-5-2-5-5V54c0-14 11-18 11-24z"
style={{ fill: wineTypeColor(type) }}
stroke="var(--color-text)"
strokeOpacity="0.35"
strokeWidth="1.5"
/>
<rect
x="14"
y="2"
width="12"
height="9"
rx="1.5"
fill="var(--color-text)"
/>
<rect
x="8"
y="66"
width="24"
height="30"
rx="2"
fill="var(--color-field)"
opacity="0.9"
/>
<path
d="M14 76h12M14 82h12M16 88h8"
stroke="var(--color-muted)"
strokeWidth="1.5"
/>
</svg>
);
}
import { ACIDITY_LEVELS, BODY_LEVELS } from '../data/taste';
import WineTypeImage from './WineTypeImage';

/** One row with label and bar, e.g. "Body ████░". The exact value is read by screen readers and shown on hover. */
function Bar(props: { label: string; share: number; value: string }) {
Expand Down Expand Up @@ -68,7 +29,9 @@ function Bar(props: { label: string; share: number; value: string }) {
export default function FoodWineCard({ wine }: { wine: FoodWine }) {
return (
<article className="food-card">
<Bottle type={wine.type} />
<div className="food-bottle-wrap">
<WineTypeImage type={wine.type} className="food-bottle" />
</div>

<div className="food-card-body">
<h3>
Expand Down
26 changes: 4 additions & 22 deletions client/src/components/SelectedWine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,13 @@ import { Link } from 'react-router';
import { useQuery } from '@apollo/client/react';
import type { TasteWine } from '../types/wine';
import { WINE_GRAPES_QUERY, type WineGrapesData } from '../graphql/queries';
import { ACIDITY_LEVELS, BODY_LEVELS, wineTypeColor } from '../data/taste';
import { ACIDITY_LEVELS, BODY_LEVELS } from '../data/taste';
import WineTypeImage from './WineTypeImage';

type Props = {
wine: TasteWine | null;
};

/** A wine glass filled with the colour of the wine type. Decorative only. */
function WineGlass({ type }: { type: string }) {
return (
<svg viewBox="0 0 28 44" aria-hidden="true" className="taste-glass">
<path
d="M3 2h22c0 12-4 17-11 17S3 14 3 2z"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
/>
<path
d="M4.5 9h19c-1 7-4 9-9.5 9S5.5 16 4.5 9z"
style={{ fill: wineTypeColor(type) }}
/>
<path d="M14 19v18M7 41h14" stroke="currentColor" strokeWidth="1.5" />
</svg>
);
}

/** One row with label, bar and value, e.g. "Body ████░ Full-bodied". */
function TasteBar(props: { label: string; share: number; value: string }) {
return (
Expand Down Expand Up @@ -74,8 +56,8 @@ export default function SelectedWine({ wine }: Props) {
aria-live="polite"
>
<div className="taste-selected-top">
<div className="taste-glass-box">
<WineGlass type={wine.type} />
<div className="taste-wine-image-box">
<WineTypeImage type={wine.type} className="taste-wine-image" />
</div>
<div>
<h2 id="selected-wine-name">{wine.name}</h2>
Expand Down
27 changes: 4 additions & 23 deletions client/src/components/WineList.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,11 @@
import type { WineListItem } from '../types/wine';
import './WineList.css';
import redWine from '../assets/wines/redWine.png';
import whiteWine from '../assets/wines/whiteWine.png';
import roseWine from '../assets/wines/roseWine.png';
import sparklingWine from '../assets/wines/sparklingWine.png';
import dessertWine from '../assets/wines/dessertWine.png';
import portWine from '../assets/wines/portWine.png';
import WineTypeImage from './WineTypeImage';

type Props = {
wines: WineListItem[];
};

const wineImages: Record<string, string> = {
Red: redWine,
White: whiteWine,
Rosé: roseWine,
Sparkling: sparklingWine,
Dessert: dessertWine,
'Dessert/Port': portWine,
};
/**
* Shows a list of wines as cards.
*
Expand Down Expand Up @@ -54,15 +41,9 @@ export default function WineList({ wines }: Props) {
aria-labelledby={`wine-${wine.id}`}
data-wine-type={wine.type.toLowerCase()}
>
{wineImages[wine.type] && (
<div className="wine-card-image-wrapper">
<img
src={wineImages[wine.type]}
alt=""
className="wine-card-image"
/>
</div>
)}
<div className="wine-card-image-wrapper">
<WineTypeImage type={wine.type} className="wine-card-image" />
</div>

<div className="wine-card-top">
<span className="wine-type-badge">{wine.type}</span>
Expand Down
30 changes: 30 additions & 0 deletions client/src/components/WineTypeImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import redWine from '../assets/wines/redWine.png';
import whiteWine from '../assets/wines/whiteWine.png';
import roseWine from '../assets/wines/roseWine.png';
import sparklingWine from '../assets/wines/sparklingWine.png';
import dessertWine from '../assets/wines/dessertWine.png';
import portWine from '../assets/wines/portWine.png';

const wineImages: Record<string, string> = {
Red: redWine,
White: whiteWine,
Rosé: roseWine,
Sparkling: sparklingWine,
Dessert: dessertWine,
'Dessert/Port': portWine,
};

type Props = {
type: string;
className?: string;
};

export default function WineTypeImage({ type, className }: Props) {
const image = wineImages[type];

if (!image) {
return null;
}

return <img src={image} alt="" className={className} />;
}