Compare commits

..

No commits in common. "c6084228fc123241c69c6e3064a15be4dc5db8a3" and "920e83963c36d5c4a4c2a0b1f8f37f469509f799" have entirely different histories.

11 changed files with 102 additions and 360 deletions

View File

@ -4,7 +4,7 @@
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev --turbo",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint" "lint": "next lint"

View File

@ -587,98 +587,12 @@ export interface HomeHero {
}; };
[k: string]: unknown; [k: string]: unknown;
}; };
heroItems: { heroItems?:
heroDataDesign: { | {
root: { name: string;
type: string; id?: string | null;
children: { }[]
type: string; | null;
version: number;
[k: string]: unknown;
}[];
direction: ('ltr' | 'rtl') | null;
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
indent: number;
version: number;
};
[k: string]: unknown;
};
heroDataCollection: {
root: {
type: string;
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
direction: ('ltr' | 'rtl') | null;
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
indent: number;
version: number;
};
[k: string]: unknown;
};
heroDataAnalysis: {
root: {
type: string;
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
direction: ('ltr' | 'rtl') | null;
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
indent: number;
version: number;
};
[k: string]: unknown;
};
heroDataVisualisation: {
root: {
type: string;
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
direction: ('ltr' | 'rtl') | null;
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
indent: number;
version: number;
};
[k: string]: unknown;
};
heroTraining: {
root: {
type: string;
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
direction: ('ltr' | 'rtl') | null;
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
indent: number;
version: number;
};
[k: string]: unknown;
};
heroDataSovereignty: {
root: {
type: string;
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
direction: ('ltr' | 'rtl') | null;
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
indent: number;
version: number;
};
[k: string]: unknown;
};
};
}; };
/** /**
* This title will be used for SEO purposes, and displayed in the browser tab. * This title will be used for SEO purposes, and displayed in the browser tab.
@ -744,12 +658,8 @@ export interface HomeHeroSelect<T extends boolean = true> {
heroItems?: heroItems?:
| T | T
| { | {
heroDataDesign?: T; name?: T;
heroDataCollection?: T; id?: T;
heroDataAnalysis?: T;
heroDataVisualisation?: T;
heroTraining?: T;
heroDataSovereignty?: T;
}; };
}; };
metaTitle?: T; metaTitle?: T;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 MiB

View File

@ -1,7 +1,7 @@
"use client"; "use client";
import { RichText } from "@payloadcms/richtext-lexical/react"; import { RichText } from "@payloadcms/richtext-lexical/react";
import { motion, useScroll, useTransform } from "motion/react"; import { motion, MotionValue, useScroll, useTransform } from "motion/react";
import { useRef, useEffect, useState, useCallback } from "react"; import { useRef, useEffect, useState, useCallback } from "react";
import * as d3 from "d3"; import * as d3 from "d3";
@ -34,11 +34,10 @@ export default function HeroIntro({
const drawBeam = useCallback(() => { const drawBeam = useCallback(() => {
if (!windowSize) return; if (!windowSize) return;
const isSmall = windowSize[0] < 1124;
const beam: [number, number][] = [ const beam: [number, number][] = [
[0, 1 - beamSize / 2], // modify over scroll [0, 1 - beamSize / 2], // modify over scroll
[0.9, 0.1], [0.9, 0.1],
[isSmall ? 0.7 : beamSize / 2, 1], // modify over scroll [0 + beamSize / 2, 1], // modify over scroll
[0, 1], [0, 1],
]; ];
@ -54,7 +53,7 @@ export default function HeroIntro({
}, [windowSize, beamSize]); }, [windowSize, beamSize]);
return ( return (
<section <div
className="h-screen bg-black flex justify-center relative overflow-clip" className="h-screen bg-black flex justify-center relative overflow-clip"
ref={containerRef} ref={containerRef}
> >
@ -70,10 +69,10 @@ export default function HeroIntro({
</div> </div>
<div className="w-full h-full max-w-6xl p-12 grid grid-cols-2 gap-12 z-10"> <div className="w-full h-full max-w-6xl p-12 grid grid-cols-2 gap-12 z-10">
<div className="absolute top-1/2 left-0 h-1/2 w-2/3 lg:w-1/2 flex-1 flex flex-col justify-center px-[5vw] gap-8 "> <div className="absolute top-1/2 left-0 h-1/2 w-1/2 flex-1 flex flex-col justify-center px-[5vw] gap-8">
<motion.h2 <motion.h2
style={{ opacity: scrollYProgress, y: titleY }} style={{ opacity: scrollYProgress, y: titleY }}
className="text-3xl lg:text-5xl tracking-tight leading-tight font-display" className="text-5xl tracking-tight leading-tight"
> >
{title} {title}
</motion.h2> </motion.h2>
@ -85,11 +84,11 @@ export default function HeroIntro({
> >
<RichText <RichText
data={desc} data={desc}
className="text-xl lg:text-2xl leading-tight" className="text-white text-2xl leading-tight"
/> />
</motion.div> </motion.div>
</div> </div>
</div> </div>
</section> </div>
); );
} }

View File

@ -1,86 +0,0 @@
"use client";
import { motion, useScroll, useTransform } from "motion/react";
import { useRef } from "react";
import type { HomeHero } from "@payload-types";
import { RichText } from "@payloadcms/richtext-lexical/react";
import useWindow from "@/app/(website)/hooks/useWindow";
const heroMap = {
heroDataDesign: "Data Design",
heroDataCollection: "Data Collection",
heroDataAnalysis: "Data Analysis",
heroDataVisualisation: "Data Visualisation",
heroTraining: "Training",
heroDataSovereignty: "Data Sovereignty",
} as const;
type HeroItems = HomeHero["heroGroup"]["heroItems"];
type HeroItem = HeroItems[keyof HeroItems];
export default function HeroData({
items,
}: {
items: HomeHero["heroGroup"]["heroItems"];
}) {
const itemKeys: (keyof typeof items)[] = Object.keys(items) as any;
const itemArray = itemKeys.map((k) => ({ key: k, ...items[k] }));
return (
<section className="bg-black flex flex-col items-center relative">
{itemArray.map((item) => (
<Item key={item.key} title={heroMap[item.key]} item={item} />
))}
</section>
);
}
const Item = ({ title, item }: { title: string; item: HeroItem }) => {
const { height } = useWindow();
const containerRef = useRef(null);
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ["start end", "end start"],
});
const yp = 0.7;
const yoffset = useTransform(
scrollYProgress,
[0.2, 0.8],
[-height * yp, height * yp]
);
const opacity = useTransform(
scrollYProgress,
[0.3, 0.4, 0.6, 0.7],
[0, 1, 1, 0]
);
return (
<div
ref={containerRef}
className="w-full h-screen max-w-6xl p-12 grid grid-cols-2 gap-12 z-10 items-center text-white"
>
<motion.div
style={{ opacity }}
className="flex items-center flex-col bg-accent-600 rounded aspect-video"
>
IMAGE
</motion.div>
<div className="relative">
<motion.div
style={{
y: yoffset,
opacity,
}}
className="flex flex-col gap-4 absolute top-1/2 -translate-y-1/2"
>
<h5 className="text-4xl font-display">{title}</h5>
<div>
<RichText className="text-xl" data={item} />
</div>
</motion.div>
</div>
</div>
);
};

View File

@ -5,12 +5,12 @@ import { letters } from "./letters";
export default function ScrollingNumbers({ numbers }: { numbers: string[][] }) { export default function ScrollingNumbers({ numbers }: { numbers: string[][] }) {
return ( return (
<div className="absolute top-0 h-full w-full z-0 opacity-50 pointer-events-none"> <div className="absolute top-0 h-full w-full z-0 opacity-50 overflow-clip">
<div className="h-full w-full flex gap-4 skew-24 scale-150 overflow-hidden"> <div className="h-full w-full flex gap-4 skew-24 scale-150">
{numbers.map((col, i) => ( {numbers.map((col, i) => (
<NumberCol col={col} key={i} /> <NumberCol col={col} key={i} />
))} ))}
<div className="absolute w-full h-full mask-b-from-0 bg-black"></div> <div className="absolute w-full h-full mask-b-from-0 bg-black skew"></div>
</div> </div>
</div> </div>
); );
@ -34,7 +34,7 @@ const NumberCol = ({ col }: { col: string[] }) => {
className="flex-1 flex flex-col items-center" className="flex-1 flex flex-col items-center"
> >
{col.map((num, j) => ( {col.map((num, j) => (
<div key={j} className="text-accent-800 text-8xl"> <div key={j} className="text-accent-600 text-3xl">
{num} {num}
</div> </div>
))} ))}

View File

@ -2,15 +2,8 @@
import { ReactNode, useEffect } from "react"; import { ReactNode, useEffect } from "react";
import Lenis from "lenis"; import Lenis from "lenis";
import Snap from "lenis/snap";
export default function ({ export default function ({ children }: { children: ReactNode }) {
children,
snapAt,
}: {
children: ReactNode;
snapAt?: string[];
}) {
useEffect(() => { useEffect(() => {
const lenis = new Lenis(); const lenis = new Lenis();
@ -21,17 +14,6 @@ export default function ({
} }
requestAnimationFrame(raf); requestAnimationFrame(raf);
// const snap = new Snap(lenis, {
// type: "proximity",
// velocityThreshold: 1,
// debounce: 0,
// });
// snapAt?.forEach((id) => {
// const el = document.querySelector<HTMLDivElement>(id);
// if (!el) return;
// snap.addElement(el, { align: ["center"] });
// });
}, []); }, []);
return <>{children}</>; return <>{children}</>;
} }

View File

@ -1,11 +1,11 @@
@import url("https://fonts.googleapis.com/css2?family=Inknut+Antiqua:wght@400;700&family=Work+Sans:ital,wght@0,100..900;1,100..900&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap");
@import "tailwindcss"; @import "tailwindcss";
@theme { @theme {
/* FONT FAMILIES */ /* FONT FAMILIES */
--font-sans: "Work Sans", sans; --font-sans: Inter, sans;
--font-display: "Inknut Antiqua"; --font-display: var(--font-sans);
/* COLOURS */ /* COLOURS */
--color-accent-50: #fff1f2; --color-accent-50: #fff1f2;

View File

@ -1,22 +0,0 @@
import { useEffect, useState } from "react";
export default function useWindow() {
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
useEffect(() => {
if (!window) return;
setWidth(window.innerWidth);
setHeight(window.innerHeight);
window.addEventListener("resize", () => {
setWidth(window.innerWidth);
setHeight(window.innerHeight);
});
}, []);
return {
width,
height,
};
}

View File

@ -4,13 +4,9 @@ import ScrollingNumbers from "./components/Home/ScrollingNumbers";
import { letters } from "./components/Home/letters"; import { letters } from "./components/Home/letters";
import HeroIntro from "./components/Home/Hero/01-intro"; import HeroIntro from "./components/Home/Hero/01-intro";
import SmoothScroll from "./components/SmoothScroll"; import SmoothScroll from "./components/SmoothScroll";
import HeroData from "./components/Home/Hero/02-data";
import bgImage from "./bg.jpg"; const randomNumbers = Array.from({ length: 50 }).map((i) =>
import Image from "next/image"; Array.from({ length: 200 }).map(
const randomNumbers = Array.from({ length: 10 }).map((i) =>
Array.from({ length: 20 }).map(
(j) => letters[Math.floor(Math.random() * letters.length)] (j) => letters[Math.floor(Math.random() * letters.length)]
) )
); );
@ -22,22 +18,16 @@ export default async function Home() {
}); });
return ( return (
<SmoothScroll snapAt={["section"]}> <SmoothScroll>
<div className="text-white"> <div className="text-white">
<div className="absolute h-full mt-[var(--header-height)] w-full opacity-50">
<Image src={bgImage} alt="Bg image" objectFit="cover" />
</div>
<div className="h-screen pt-[var(--header-height)] flex flex-col items-center justify-end text-white pb-[10vh] relative"> <div className="h-screen pt-[var(--header-height)] flex flex-col items-center justify-end text-white pb-[10vh] relative">
{/* <ScrollingNumbers numbers={randomNumbers} /> */} <ScrollingNumbers numbers={randomNumbers} />
<h1 className="text-4xl p-8 lg:text-7xl leading-tight max-w-6xl z-10 font-display"> <h1 className="text-8xl max-w-6xl z-10">{titleGroup.title}</h1>
{titleGroup.title}
</h1>
</div> </div>
<HeroIntro <HeroIntro
title={heroGroup.heroTitle} title={heroGroup.heroTitle}
desc={heroGroup.heroDescription} desc={heroGroup.heroDescription}
/> />
<HeroData items={heroGroup.heroItems} />
</div> </div>
</SmoothScroll> </SmoothScroll>
); );

View File

@ -1,114 +1,83 @@
import { GlobalConfig } from "payload"; import { GlobalConfig } from "payload";
export const HomeHero: GlobalConfig = { export const HomeHero: GlobalConfig = {
slug: "homeHero", slug: 'homeHero',
label: "Hero", label: 'Hero',
fields: [ fields: [
{ {
name: "titleGroup", name: 'titleGroup',
label: "Landing page", label: 'Landing page',
type: "group", type: 'group',
fields: [ fields: [
{ {
name: "title", name: 'title',
label: "Title", label: 'Title',
type: "text", type: 'text',
required: true, required: true,
defaultValue: defaultValue: 'Analytics, research, and data visualisation that make a difference'
"Analytics, research, and data visualisation that make a difference",
}, },
], ],
}, },
{ {
name: "heroGroup", name: 'heroGroup',
label: "Hero", label: 'Hero',
type: "group", type: 'group',
fields: [ fields: [
{ {
name: "heroTitle", name: 'heroTitle',
label: "Hero Title", label: 'Hero Title',
type: "text", type: 'text',
required: true, required: true,
defaultValue: "We help people tell stories with data", defaultValue: 'We help people tell stories with data'
}, },
{ {
name: "heroDescription", name: 'heroDescription',
label: "Hero Description", label: 'Hero Description',
type: "richText", type: 'richText',
required: true, required: true,
}, },
{ {
name: "heroItems", name: 'heroItems',
label: "Items", label: 'Items',
type: "group", type: 'array',
fields: [ fields: [
{ {
name: "heroDataDesign", name: 'name',
label: "Data Design", label: 'Name',
type: "richText", type: 'text',
required: true, required: true,
}, }
{
name: "heroDataCollection",
label: "Data Collection",
type: "richText",
required: true,
},
{
name: "heroDataAnalysis",
label: "Data Analysis",
type: "richText",
required: true,
},
{
name: "heroDataVisualisation",
label: "Data Visualisation",
type: "richText",
required: true,
},
{
name: "heroTraining",
label: "Training",
type: "richText",
required: true,
},
{
name: "heroDataSovereignty",
label: "Data Sovereignty",
type: "richText",
required: true,
},
], ],
defaultValue: ["Data design", "Data collection", "Data analysis", "Data visualisation", "Training", "Data sovereignty"].map((item) => ({
name: item,
})),
}, },
], ]
}, },
{ {
name: "metaTitle", name: 'metaTitle',
label: "Meta Title", label: 'Meta Title',
type: "text", type: 'text',
defaultValue: "iNZight Analytics Ltd", defaultValue: 'iNZight Analytics Ltd',
required: true, required: true,
admin: { admin: {
position: "sidebar", position: 'sidebar',
description: description: 'This title will be used for SEO purposes, and displayed in the browser tab.',
"This title will be used for SEO purposes, and displayed in the browser tab.", }
},
}, },
{ {
name: "metaDescription", name: 'metaDescription',
label: "Meta Description", label: 'Meta Description',
type: "textarea", type: 'textarea',
required: true, required: true,
defaultValue: defaultValue: 'iNZight Analytics Ltd is a New Zealand-based company that provides data analysis and visualisation services.',
"iNZight Analytics Ltd is a New Zealand-based company that provides data analysis and visualisation services.",
admin: { admin: {
position: "sidebar", position: 'sidebar',
description: description: 'This description will be used for SEO purposes (e.g., shown in search results and on social media cards).',
"This description will be used for SEO purposes (e.g., shown in search results and on social media cards).", }
},
}, },
], ],
admin: { admin: {
group: "Home page", group: 'Home page'
}, }
}; };