464 lines
16 KiB
TypeScript
464 lines
16 KiB
TypeScript
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>
|
||
);
|
||
}
|