AbiirSkincare/Hydrogen/web/app/components/AboutPage.tsx

464 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {Image} from '@shopify/hydrogen';
import {motion} from 'motion/react';
import {Newsletter} from '~/components/Newsletter';
import type {AbiirAboutImageCms, AbiirAboutPageCms} from '~/lib/about';
import {abiirAboutImageIsRemote, MOCK_ABIIR_ABOUT_CMS} from '~/lib/about';
import {PAGE_TITLE_TYPOGRAPHY_CLASS} from '~/lib/pageTitle';
import type {NewsletterSectionCms} from '~/lib/newsletter';
/**
* GROQ: `aboutPage` singleton (`_id == "aboutPage"`).
* Use with `context.sanity.loadQuery(ABOUT_PAGE_GROQ)` or `useQuery` from `@sanity/react-loader`.
*/
export const ABOUT_PAGE_GROQ = `*[_type == "aboutPage" && _id == "aboutPage"][0]{
seo{ title, description },
heroEyebrow,
heroHeadlineBefore,
heroHeadlineItalic,
heroImage{
alt,
asset->{
_id,
url,
metadata{dimensions{width,height}}
}
},
beginningEyebrow,
beginningHeadlineBefore,
beginningHeadlineItalic,
beginningHeadlineAfter,
storyParagraphs,
beginningImage{
alt,
asset->{
_id,
url,
metadata{dimensions{width,height}}
}
},
beginningQuote,
beginningQuoteAttribution,
philosophyEyebrow,
philosophyHeadlineBefore,
philosophyHeadlineItalic,
philosophyBody,
philosophyImage{
alt,
asset->{
_id,
url,
metadata{dimensions{width,height}}
}
},
valuesTitle,
values[]{ title, description },
teamEyebrow,
teamTitle,
founderImage{
alt,
asset->{
_id,
url,
metadata{dimensions{width,height}}
}
},
founderRole,
founderName,
founderBio,
founderQuote,
quoteIcon{
alt,
asset->{
_id,
url,
metadata{dimensions{width,height}}
}
}
}` as const;
export {MOCK_ABIIR_ABOUT_CMS};
export type {AbiirAboutPageCms};
const ACCENT = '#c17d5c';
const CHARCOAL = '#1a1a18';
const MUTED = '#5a5550';
const MUTED_MID = '#7a7068';
const CREAM_TEXT = '#f8f4ef';
function AboutCmsImage({
image,
className,
sizes,
style,
}: {
image: AbiirAboutImageCms;
className?: string;
sizes: string;
style?: CSSProperties;
}) {
if (abiirAboutImageIsRemote(image)) {
const w = image.width ?? 1600;
const h = image.height ?? 1200;
return (
<Image
alt={image.alt}
className={className}
data={{
url: image.url,
altText: image.alt,
width: w,
height: h,
}}
loader={({src}) => src ?? ''}
sizes={sizes}
style={style}
/>
);
}
return (
<Image
alt={image.alt}
className={className}
height={image.height ?? 1200}
sizes={sizes}
src={image.url}
style={style}
width={image.width ?? 1600}
/>
);
}
export type AboutPageProps = {
cms: AbiirAboutPageCms;
newsletterCms: NewsletterSectionCms;
};
/**
* Our Story / About — layout from `React/src/app/pages/About.tsx`;
* main titles use `PAGE_TITLE_TYPOGRAPHY_CLASS` (same as homepage `Testimonials` heading).
* Pass `cms` from the route loader, or from `useQuery` with `ABOUT_PAGE_GROQ`.
*/
export function AboutPage({cms, newsletterCms}: AboutPageProps) {
/** Hero band height tracks banner aspect ratio (default `about_banner.png` 1710×495). */
const heroW = cms.heroImage.width ?? 1710;
const heroH = cms.heroImage.height ?? 495;
const heroBandStyle: CSSProperties = {
minHeight: `max(22rem, min(90svh, calc(100vw * ${heroH} / ${heroW})))`,
maxHeight: '90svh',
};
return (
<div className="min-h-screen bg-[#f8f4ef]">
{/* Hero — full-bleed banner + headline */}
<div className="relative w-full overflow-hidden">
<AboutCmsImage
className="absolute inset-0 size-full object-cover object-center"
image={cms.heroImage}
sizes="100vw"
/>
<div
className="absolute inset-0"
style={{
background:
'linear-gradient(to bottom, rgba(26,26,24,0.35), rgba(26,26,24,0.45))',
}}
/>
<div
className="relative flex w-full flex-col items-center justify-center px-6 py-16 text-center sm:py-24"
style={heroBandStyle}
>
<motion.span
animate={{opacity: 1, y: 0}}
className="m-0 font-['Jost',sans-serif] text-[11.688px] font-medium uppercase leading-[17.531px] tracking-[2.9219px] not-italic"
initial={{opacity: 0, y: 16}}
style={{color: ACCENT}}
transition={{duration: 0.55}}
>
{cms.heroEyebrow}
</motion.span>
<motion.h1
animate={{opacity: 1, y: 0}}
className={`m-0 mt-[21.25px] max-w-[680px] ${PAGE_TITLE_TYPOGRAPHY_CLASS}`}
initial={{opacity: 0, y: 24}}
style={{color: CREAM_TEXT}}
transition={{duration: 0.65, delay: 0.08}}
>
{cms.heroHeadlineBefore}
<span className="font-['Cormorant_Garamond',serif] font-light italic">
{cms.heroHeadlineItalic}
</span>
</motion.h1>
</div>
</div>
{/* The Beginning — Figma 123:5133 */}
<section className="w-full bg-[#f8f4ef]">
<div className="mx-auto max-w-[1710px] px-6 py-16 sm:px-10 lg:px-16 lg:py-[137px] 2xl:px-[206px]">
<div className="mx-auto grid max-w-[1400px] items-start gap-12 lg:grid-cols-2 lg:gap-[85px]">
<motion.div
className="max-w-[649px]"
initial={{opacity: 0, y: 24}}
transition={{duration: 0.65}}
viewport={{once: true}}
whileInView={{opacity: 1, y: 0}}
>
<p
className="m-0 mb-[21px] font-['Jost',sans-serif] text-[11.05px] font-medium uppercase leading-[16.575px] tracking-[2.7625px] not-italic"
style={{color: ACCENT}}
>
{cms.beginningEyebrow}
</p>
<h2
className={`m-0 mb-8 ${PAGE_TITLE_TYPOGRAPHY_CLASS}`}
style={{color: CHARCOAL}}
>
{cms.beginningHeadlineBefore}
<span className="font-['Cormorant_Garamond',serif] font-light italic">
{cms.beginningHeadlineItalic}
</span>
{cms.beginningHeadlineAfter}
</h2>
<div
className="flex flex-col gap-y-6 font-['Raleway',sans-serif] text-[16px] font-medium leading-[32px] not-italic"
style={{color: MUTED}}
>
{cms.storyParagraphs.map((p, i) => (
<p key={i} className="m-0">
{p}
</p>
))}
</div>
</motion.div>
<motion.div
className="relative w-full max-w-[649px] lg:justify-self-end"
initial={{opacity: 0, y: 24}}
transition={{duration: 0.7, delay: 0.05}}
viewport={{once: true}}
whileInView={{opacity: 1, y: 0}}
>
<div className="abiir-about-beginning-image-wrap">
<AboutCmsImage
className="abiir-about-beginning-image block w-full h-auto"
image={cms.beginningImage}
sizes="(min-width: 1024px) 50vw, 100vw"
/>
</div>
<div
className="relative mt-6 box-border w-full max-w-[220px] bg-[#ede8e0] p-[21.25px] px-[25.5px] lg:absolute lg:bottom-[95px] lg:left-[-25.5px] lg:mt-0"
>
<p
className="m-0 font-['Cormorant_Garamond',serif] text-[17.85px] font-normal italic leading-[25.882px]"
style={{color: CHARCOAL}}
>
{cms.beginningQuote}
</p>
<p
className="m-0 mt-2 font-['Jost',sans-serif] text-[10.2px] font-light uppercase leading-[18.36px] tracking-[1.02px] not-italic"
style={{color: ACCENT}}
>
{cms.beginningQuoteAttribution}
</p>
</div>
</motion.div>
</div>
</div>
</section>
{/* Philosophy — Figma 123:5226 */}
<section className="relative min-h-[420px] w-full overflow-hidden">
<div className="pointer-events-none absolute inset-0 overflow-hidden">
<AboutCmsImage
className="absolute left-0 w-full max-w-none"
image={cms.philosophyImage}
sizes="100vw"
style={{height: '508.93%', top: '-226.67%'}}
/>
</div>
<div className="absolute inset-0 bg-[rgba(26,26,24,0.5)]" />
<div className="relative flex min-h-[420px] items-center justify-center px-6 py-16 lg:py-20">
<div className="mx-auto w-full min-w-0 max-w-[872px] text-center">
<motion.p
className="m-0 font-['Jost',sans-serif] text-[11.05px] font-medium uppercase leading-[16.575px] tracking-[3.094px] not-italic"
initial={{opacity: 0}}
style={{color: ACCENT}}
viewport={{once: true}}
whileInView={{opacity: 1}}
>
{cms.philosophyEyebrow}
</motion.p>
<motion.h2
className="m-0 mb-8 mt-[22px] whitespace-nowrap text-center font-['Cormorant_Garamond',serif] font-light tracking-[-0.969px] not-italic"
initial={{opacity: 0, y: 16}}
style={{
color: CREAM_TEXT,
fontSize: 'clamp(0.8125rem, 4.2vw, 48px)',
lineHeight: 1.12,
fontWeight: 300,
}}
transition={{delay: 0.06}}
viewport={{once: true}}
whileInView={{opacity: 1, y: 0}}
>
{cms.philosophyHeadlineBefore}
<span className="font-['Cormorant_Garamond',serif] font-light italic">
{cms.philosophyHeadlineItalic}
</span>
</motion.h2>
<div className="mx-auto w-full max-w-[712px] px-[80px] box-border max-[520px]:px-6">
<motion.p
className="m-0 w-full text-center font-['Raleway',sans-serif] text-[16px] font-medium leading-[24px] not-italic"
initial={{opacity: 0, y: 12}}
style={{color: CREAM_TEXT}}
transition={{delay: 0.1}}
viewport={{once: true}}
whileInView={{opacity: 1, y: 0}}
>
{cms.philosophyBody}
</motion.p>
</div>
</div>
</div>
</section>
{/* What We Stand For — Figma 123:5153 */}
<section className="w-full bg-[#ede8e0]">
<div className="mx-auto max-w-[1710px] px-6 pb-16 pt-16 sm:pb-20 sm:pt-20 lg:pb-24 lg:pt-[102px] sm:px-10 lg:px-16 2xl:px-[206px]">
<h2
className={`m-0 mb-10 text-center lg:mb-[59.5px] ${PAGE_TITLE_TYPOGRAPHY_CLASS}`}
style={{color: CHARCOAL}}
>
{cms.valuesTitle}
</h2>
<div
className="grid grid-cols-1 gap-px bg-[rgba(26,26,24,0.06)] sm:grid-cols-2 lg:grid-cols-4"
>
{cms.values.map((value, i) => (
<motion.div
key={value.title}
className="relative box-border min-h-[241px] bg-[#ede8e0] px-[34px] pb-10 pt-[42.5px]"
initial={{opacity: 0, y: 16}}
transition={{duration: 0.5, delay: i * 0.06}}
viewport={{once: true}}
whileInView={{opacity: 1, y: 0}}
>
<span
className="block font-['Cormorant_Garamond',serif] text-[51px] font-light leading-[51px] not-italic"
style={{color: 'rgba(26,26,24,0.28)'}}
>
{String(i + 1).padStart(2, '0')}
</span>
<h3
className="m-0 mt-5 font-['Cormorant_Garamond',serif] text-[24px] font-medium leading-[25.5px] tracking-[-0.204px] not-italic"
style={{color: CHARCOAL}}
>
{value.title}
</h3>
<p
className="m-0 mt-3 max-w-[243px] font-['Raleway',sans-serif] text-[16px] font-medium leading-[23.698px] not-italic"
style={{color: MUTED_MID}}
>
{value.description}
</p>
</motion.div>
))}
</div>
</div>
</section>
{/* Meet the Team — Figma 123:5185 */}
<section className="w-full bg-[#f8f4ef]">
<div className="mx-auto max-w-[1710px] px-6 py-16 sm:px-10 sm:py-20 lg:px-16 lg:py-[94px] 2xl:px-[206px]">
<div className="mx-auto flex max-w-[1400px] flex-col gap-10 lg:gap-[68px]">
<div className="flex w-full flex-col items-center gap-[17px] text-center">
<p
className="m-0 w-full font-['Jost',sans-serif] text-[11.05px] font-medium uppercase leading-[16.575px] tracking-[2.7625px] not-italic"
style={{color: ACCENT}}
>
{cms.teamEyebrow}
</p>
<h2
className={`m-0 ${PAGE_TITLE_TYPOGRAPHY_CLASS}`}
style={{color: CHARCOAL}}
>
{cms.teamTitle}
</h2>
</div>
<div className="flex flex-col items-start justify-center gap-12 lg:flex-row lg:gap-[59px]">
<motion.div
className="mx-auto w-full max-w-[427px] shrink-0 overflow-hidden lg:mx-0"
initial={{opacity: 0, y: 20}}
style={{maxHeight: '640px'}}
viewport={{once: true}}
whileInView={{opacity: 1, y: 0}}
>
<AboutCmsImage
className="size-full object-cover"
image={cms.founderImage}
sizes="(min-width: 1024px) 427px, 100vw"
style={{minHeight: '480px'}}
/>
</motion.div>
<motion.div
className="flex w-full max-w-[816px] flex-col gap-10 lg:gap-[69px]"
initial={{opacity: 0, y: 20}}
transition={{delay: 0.08}}
viewport={{once: true}}
whileInView={{opacity: 1, y: 0}}
>
<div className="flex flex-col gap-2.5">
<p
className="m-0 font-['Jost',sans-serif] text-[10.2px] font-medium uppercase leading-[18.36px] tracking-[1.836px] not-italic"
style={{color: ACCENT}}
>
{cms.founderRole}
</p>
<h3
className="m-0 font-['Cormorant_Garamond',serif] text-[32px] font-normal leading-normal tracking-[-0.2465px] not-italic"
style={{color: CHARCOAL}}
>
{cms.founderName}
</h3>
<div
className="mt-2 flex flex-col gap-4 font-['Raleway',sans-serif] text-[16px] font-medium leading-[24px] not-italic"
style={{color: MUTED}}
>
{cms.founderBio.map((para, i) => (
<p key={i} className="m-0">
{para}
</p>
))}
</div>
</div>
<div
className="box-border flex w-full flex-col items-center gap-7 bg-[#f1e6d9] p-8 text-center"
>
<span aria-hidden className="inline-flex size-8 shrink-0">
<AboutCmsImage
className="size-8"
image={cms.quoteIcon}
sizes="32px"
/>
</span>
<p
className="m-0 max-w-[708px] font-['Cormorant_Garamond',serif] text-[22.95px] font-normal italic leading-[36.72px]"
style={{color: CHARCOAL}}
>
{cms.founderQuote}
</p>
</div>
</motion.div>
</div>
</div>
</div>
</section>
<Newsletter cms={newsletterCms} />
</div>
);
}