Skip to content

Commit

Permalink
Dynamic Product Swimlane & Mobile Layouts (#1412)
Browse files Browse the repository at this point in the history
* Product Swimlane now works with product recommendations

* Updates Header for mobile

* Mobile layout for header, footer, and key layout components

* Adds expanding menus to footer

* Adds mobile layout for search
  • Loading branch information
benjaminsehl authored Jun 2, 2022
1 parent 636481a commit 0c456db
Show file tree
Hide file tree
Showing 19 changed files with 297 additions and 125 deletions.
12 changes: 8 additions & 4 deletions templates/demo-store-neue/src/components/blocks/ProductCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ import {Text} from '~/components/elements';
import {isDiscounted, isNewArrival} from '~/lib/utils';
import {product as mockProduct} from '~/lib/placeholders';

export default function ProductCard({product = mockProduct, label, className}) {
export default function ProductCard({product, label, className}) {
let cardLabel;

const cardData = product?.variants ? product : mockProduct;

const {
image,
priceV2: price,
compareAtPriceV2: compareAtPrice,
} = product?.variants?.nodes[0];
} = cardData?.variants?.nodes[0];

if (label) {
cardLabel = label;
Expand All @@ -26,15 +28,17 @@ export default function ProductCard({product = mockProduct, label, className}) {
return (
<Link to={`/products/${product.handle}`}>
<div className={styles}>
<div className="relative rounded overflow-clip image-border">
<div className="card-image">
<Text
as="label"
size="fine"
className="absolute top-0 right-0 m-4 text-right text-notice"
>
{cardLabel}
</Text>
<Image className="aspect-[4/5]" data={image} alt="Alt Tag" />
{image && (
<Image className="aspect-[4/5]" data={image} alt="Alt Tag" />
)}
</div>
<div className="grid gap-1">
<Text
Expand Down
7 changes: 5 additions & 2 deletions templates/demo-store-neue/src/components/elements/Grid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@ export default function Grid({
const Component = as;

const layouts = {
default: `grid-cols-2 ${items >= 3 && 'md:grid-cols-3'} ${
default: `grid-cols-1 md:grid-cols-2 ${items >= 3 && 'md:grid-cols-3'} ${
items >= 4 && 'lg:grid-cols-4'
}`,
products: `grid-cols-2 ${items >= 3 && 'md:grid-cols-3'} ${
items >= 4 && 'lg:grid-cols-4'
}`,
auto: 'auto-cols-auto',
};

const gaps = {
default: 'grid gap-2 md:gap-4 lg:gap-6',
default: 'grid gap-2 gap-y-6 md:gap-4 lg:gap-6',
};

const flows = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default function Heading({
const styles = clsx(
missingClass(className, 'whitespace-') && 'whitespace-pre-wrap',
missingClass(className, 'max-w-') && widths[width],
sizes[size],
missingClass(className, 'font-') && sizes[size],
className,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default function Input({type, variant, className = '', ...props}) {
search:
'bg-transparent px-0 py-2 text-heading w-full focus:ring-0 border-x-0 border-t-0 transition border-b-2 border-primary/10 focus:border-primary/90',
minisearch:
'bg-transparent text-right border-b transition border-transparent -mb-px border-x-0 border-t-0 appearance-none px-0 py-1 focus:ring-transparent placeholder:opacity-20 placeholder:text-inherit',
'bg-transparent hidden md:inline-block text-left lg:text-right border-b transition border-transparent -mb-px border-x-0 border-t-0 appearance-none px-0 py-1 focus:ring-transparent placeholder:opacity-20 placeholder:text-inherit',
};

const styles = clsx(variants[variant], className);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default function FeaturedCollections({
<div className="grid gap-4">
{collection?.image && (
<Image
className="rounded shadow-border overflow-clip inline-block aspect-square md:aspect-[3/2] object-cover"
className="rounded shadow-border overflow-clip inline-block aspect-[5/4] md:aspect-[3/2] object-cover"
width={'100%'}
height={336}
alt={`Image of ${collection.title}`}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {Link} from '@shopify/hydrogen';
import {CountrySelector} from '~/components/blocks';
import {Heading} from '../elements';
import {footer as mockData} from '~/lib/placeholders';
import Section from './Section';
import {Disclosure} from '@headlessui/react';

/**
* A server component that specifies the content of the footer on the website
*/

export default function Footer({menus = mockData}) {
const styles = {
footer:
'grid items-start w-full grid-flow-row grid-cols-1 gap-6 py-8 border-b md:gap-8 lg:gap-12 md:grid-cols-2 lg:grid-cols-4 bg-primary dark:bg-contrast dark:text-primary text-contrast',
section: 'grid gap-4',
nav: 'grid gap-2 pb-6',
};

return (
<Section
as="footer"
role="contentinfo"
divider="top"
className={styles.footer}
>
{menus.map((menu) => (
<section key={menu.id} className={styles.section}>
<Disclosure>
{({open}) => (
<>
<Disclosure.Button className="text-left md:cursor-default">
<Heading size="lead" as="h4">
{menu.title}
</Heading>
</Disclosure.Button>
<div className={open ? `block` : `hidden md:block`}>
<Disclosure.Panel static>
<nav className={styles.nav}>
{menu.links.map((link) => (
<Link key={link.id} to={link.url}>
{link.title}
</Link>
))}
</nav>
</Disclosure.Panel>
</div>
</>
)}
</Disclosure>
</section>
))}
<section className={styles.section}>
<Heading size="lead" className="cursor-default" as="h4">
Country
</Heading>
<CountrySelector />
</section>
<div className="pt-8 opacity-50 md:col-span-2 lg:col-span-4">
&copy; {new Date().getFullYear()} / Shopify, Inc. Hydrogen is an MIT
Licensed Open Source project. This website is carbon neutral.
</div>
</Section>
);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {Link, useUrl} from '@shopify/hydrogen';
import {useMedia} from 'react-use';

import {
IconSearch,
IconHelp,
IconAccount,
IconBag,
Input,
Heading,
} from '~/components/elements';

/**
Expand All @@ -14,15 +16,80 @@ import {
export default function Header({title}) {
const {pathname} = useUrl();

const dark = pathname === '/';
const home = pathname === '/';
const isDesktop = useMedia('(min-width: 60em)', false);

return isDesktop ? (
<DesktopHeader home={home} title={title} />
) : (
<MobileHeader home={home} title={title} />
);
}

function MobileHeader({title, home}) {
const styles = {
button: 'relative flex items-center justify-center w-8 h-8',
container: `${
home
? 'bg-primary/80 dark:bg-contrast/60 text-contrast dark:text-primary shadow-darkHeader'
: 'bg-contrast/80 text-primary'
} flex items-center h-12 md:h-16 sticky backdrop-blur-lg z-40 top-0 justify-between w-full leading-none gap-4 px-4 md:px-8`,
};

return (
<header role="banner" className={styles.container}>
<div className="flex items-center justify-start w-full gap-4">
<button className={styles.button}>
<IconAccount />
</button>
<form action={'/search'} className="items-center gap-2 sm:flex">
<button type="submit" className={styles.button}>
<IconSearch />
</button>
<Input
className={
home
? 'focus:border-contrast/20 dark:focus:border-primary/20'
: 'focus:border-primary/20'
}
type="search"
variant="minisearch"
placeholder="Search"
name="q"
/>
</form>
</div>

<Link
className="flex items-center self-stretch leading-[3rem] md:leading-[4rem] justify-center flex-grow w-full h-full"
to="/"
>
<Heading className="font-bold text-center" as={home ? 'h1' : 'h2'}>
{title}
</Heading>
</Link>

<div className="flex items-center justify-end w-full gap-4">
<button className={styles.button}>
<IconAccount />
</button>
<Link to={'/cart'} className={styles.button}>
<IconBag />
<CartBadge dark={home} quantity={1} />
</Link>
</div>
</header>
);
}

function DesktopHeader({title, home}) {
const styles = {
button: 'relative flex items-center justify-center w-8 h-8',
container: `${
dark
home
? 'bg-primary/80 dark:bg-contrast/60 text-contrast dark:text-primary shadow-darkHeader'
: 'bg-contrast/80 text-primary'
} flex items-center sticky backdrop-blur-lg z-40 top-0 justify-between w-full leading-none gap-16 px-12 py-8`,
} flex items-center sticky backdrop-blur-lg z-40 top-0 justify-between w-full leading-none gap-8 px-12 py-8`,
};

return (
Expand All @@ -43,7 +110,7 @@ export default function Header({title}) {
<form action={'/search'} className="flex items-center gap-2">
<Input
className={
dark
home
? 'focus:border-contrast/20 dark:focus:border-primary/20'
: 'focus:border-primary/20'
}
Expand All @@ -64,7 +131,7 @@ export default function Header({title}) {
</button>
<Link to={'/cart'} className={styles.button}>
<IconBag />
<CartBadge dark={dark} quantity={1} />
<CartBadge dark={home} quantity={1} />
</Link>
</div>
</header>
Expand Down
20 changes: 14 additions & 6 deletions templates/demo-store-neue/src/components/sections/Hero.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,22 @@ export default function Hero({data = mockData, height, top}) {
<section
className={`relative justify-end flex flex-col w-full ${
top && '-mt-nav'
} ${height === 'full' ? 'h-screen' : 'h-[50rem]'}`}
} ${
height === 'full'
? 'h-screen'
: 'aspect-[4/5] sm:aspect-square md:aspect-[5/4] lg:aspect-[3/2] xl:aspect-[2/1]'
}`}
>
<div className="absolute inset-0 grid flex-grow grid-flow-col pointer-events-none auto-cols-fr -z-10 content-stretch overflow-clip">
{spread?.reference && <SpreadMedia data={spread.reference} />}
{spread?.reference && (
<div className="">
<SpreadMedia data={spread.reference} />
</div>
)}
{spread_secondary?.reference && (
<SpreadMedia data={spread_secondary.reference} />
<div className="hidden md:block">
<SpreadMedia data={spread_secondary.reference} />
</div>
)}
</div>
<div className="flex flex-col items-baseline justify-between gap-4 px-12 py-8 bg-gradient-to-t dark:from-contrast/60 dark:text-primary from-primary/60 text-contrast">
Expand All @@ -27,9 +37,7 @@ export default function Hero({data = mockData, height, top}) {
{byline.value}
</Text>
)}
<Link to={url.value}>
<Text size="lead">{cta.value}</Text>
</Link>
<Text size="lead">{cta.value}</Text>
</div>
</section>
</Link>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ export default function Locations({title = 'Locations', data}) {
function Card({to, data}) {
return (
<Link to={to || `/locations/${data.handle}`} className="grid gap-4">
<div className="rounded image-border overflow-clip">
<div className="card-image">
<Image
alt={
data.featured_image.reference.image.altText ||
`Image of the ${data.title.value} location`
}
className="object-cover aspect-[3/2]"
data={data.featured_image.reference.image}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ export default function PageHeader({
children,
}) {
const variants = {
default: 'grid w-full gap-8 p-12 justify-items-start',
allCollections: 'flex justify-between items-baseline gap-8 p-12',
default: 'grid w-full gap-8 p-4 py-8 md:p-8 lg:p-12 justify-items-start',
allCollections:
'flex justify-between items-baseline gap-8 p-4 md:p-8 lg:p-12',
};
const styles = clsx(variants[variant], className);
return (
Expand Down
Loading

0 comments on commit 0c456db

Please sign in to comment.