Skip to content

Commit

Permalink
Tweak "How it Works" section's styles and reveal animation
Browse files Browse the repository at this point in the history
  • Loading branch information
leandroiohk authored and ch1bo committed Aug 8, 2024
1 parent 61705da commit c6c2966
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 64 deletions.
2 changes: 1 addition & 1 deletion docs/docs/homepage/how-it-works.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const HowItWorksContent = {
}),
descriptionParagraphOne: translate({
id: "homepage.featureList.lowLatency.description.paragraphOne",
message: `Traditional blockchain systems face scalability limitationsdue to
message: `Traditional blockchain systems face scalability limitations due to
the trade-off between decentralization, security, and scalability
(the blockchain trilemma). Cardano's consensus algorithm, while
efficient, still requires massive global replication of state
Expand Down
10 changes: 10 additions & 0 deletions docs/helpers/debounce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export function debounce<T extends (...args: any[]) => void>(
func: T,
wait: number
): (...args: Parameters<T>) => void {
let timeout: ReturnType<typeof setTimeout>;
return function (...args: Parameters<T>) {
clearTimeout(timeout);
timeout = setTimeout(() => func(...args), wait);
};
}
106 changes: 43 additions & 63 deletions docs/src/components/homepage/HowItWorks.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import React, { FC, useState } from "react";

import clsx from "clsx";
import Arrow from "../icons/Arrow";
import { forLaptop, forTablet } from "../../../helpers/media-queries";
import useMediaQuery from "../../hooks/useMediaQuery";
import { motion } from "framer-motion";
import { HowItWorksContent } from "../../../docs/homepage/how-it-works";
import { useWindowSize } from "../../hooks/useWindowResize";

const HowItWorks: FC = () => {
const windowSize = useWindowSize(300);
const [expanded, setExpanded] = useState(false);
const isTabletUp = useMediaQuery(forTablet);
const isLaptopUp = useMediaQuery(forLaptop);

// Reset inline height set by framer motion
const key = `${windowSize.width}-${windowSize.height}`;

return (
<motion.section
className="component"
Expand All @@ -25,96 +30,71 @@ const HowItWorks: FC = () => {
>
<h5 className="text-base text-teal pb-14">/ HOW IT WORKS</h5>
<motion.div
className="laptop:flex laptop:flex-row grid z-10"
key={key}
className="grid laptop:flex laptop:flex-row laptop:gap-6"
initial={"hidden"}
animate={expanded ? "visible" : "hidden"}
transition={{ duration: 0.15 }}
variants={
isLaptopUp
? {
visible: { height: "100%", overflow: "visible" },
hidden: { height: 280, overflow: "hidden" },
}
: {
visible: { height: "100%", overflow: "visible" },
hidden: {
height: isTabletUp ? 450 : 200,
overflow: "hidden",
},
}
}
variants={{
visible: { height: "auto", overflow: "visible" },
hidden: { overflow: "hidden" },
}}
>
<div className="flex flex-col gap-4 laptop:w-[472px] basis-[32%] laptop:pt-0 pt-4 order-2 laptop:-order-1">
<h4 className="text-2xl color-darkRed font-medium text-darkRed">
<div className="flex flex-col basis-[41%] pt-4 order-2 laptop:-order-1 laptop:w-[472px] laptop:pt-0">
<h4 className="text-2xl color-darkRed font-medium text-darkRed pb-4">
{HowItWorksContent.title}
</h4>
<p>{HowItWorksContent.descriptionParagraphOne}</p>

<motion.p
className="z-10"
initial="hidden"
animate={expanded ? "visible" : "hidden"}
transition={{ duration: 0.3, ease: "easeInOut" }}
variants={{
visible: { opacity: 1, y: 0, height: "100%" },
hidden: { opacity: 0, y: -30, height: 0 },
}}
>
{HowItWorksContent.descriptionParagraphTwo}
</motion.p>
<motion.p
className="z-10"
<motion.div
initial="hidden"
animate={expanded ? "visible" : "hidden"}
transition={{ duration: 0.3, ease: "easeInOut" }}
transition={{ duration: 0.1, ease: "easeInOut" }}
variants={{
visible: { opacity: 1, y: 0, height: "100%" },
hidden: { opacity: 0, y: -50, height: 0 },
visible: { opacity: 1, height: "auto" },
hidden: { opacity: 0, height: 0 },
}}
>
{HowItWorksContent.descriptionParagraphThree}
</motion.p>
<p className="py-4">{HowItWorksContent.descriptionParagraphTwo}</p>
<p>{HowItWorksContent.descriptionParagraphThree}</p>
</motion.div>
<div className="w-full relative mt-4">
<button
className="bg-none text-teal self-start inline-flex gap-3 group"
onClick={() => setExpanded(!expanded)}
>
{`Read ${expanded ? "less" : "more"}`}{" "}
<Arrow
className={clsx(
"mt-1 rounded-full group-hover:bg-teal/15",
expanded ? "-rotate-90" : "rotate-90"
)}
/>
</button>
</div>
</div>
{isTabletUp && (
<motion.div
className="basis-[60%] image border-b border-solid border-teal inline-block -order-1 laptop:order-2 laptop:relative"
className="basis-[59%] relative border-b border-solid border-teal -order-1 laptop:order-2"
initial="hidden"
animate={expanded ? "visible" : "hidden"}
transition={{ duration: 0.15 }}
variants={{
visible: {
height: isLaptopUp ? 400 : "100%",
overflow: "visible",
},
hidden: { height: 280, overflow: "hidden" },
visible: isLaptopUp
? { overflow: "visible" }
: { overflow: "visible", height: "auto" },
hidden: isLaptopUp
? { overflow: "hidden" }
: { overflow: "hidden", height: 280 },
}}
>
<img
src="hydra-docs-landing-graphic.png"
className="-z-10 overflow-hidden laptop:absolute laptop:h-[390px]"
className="w-full laptop:absolute"
/>
</motion.div>
)}
</motion.div>
<div
className={clsx(
"w-full z-30 relative",
expanded ? "mt-4 laptop:mt-2" : "laptop:-mt-4 mt-4"
)}
>
<button
className="bg-none text-teal self-start inline-flex gap-3 group"
onClick={() => setExpanded(!expanded)}
>
{`Read ${expanded ? "less" : "more"}`}{" "}
<Arrow
className={clsx(
"mt-1 rounded-full group-hover:bg-teal/15",
expanded ? "-rotate-90" : "rotate-90"
)}
/>
</button>
</div>
</motion.section>
);
};
Expand Down
36 changes: 36 additions & 0 deletions docs/src/hooks/useWindowResize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useLayoutEffect, useState } from "react";
import { debounce } from "../../helpers/debounce";

export function useWindowSize(debounceTime?: number) {
const [size, setSize] = useState<{
width: number | null;
height: number | null;
}>({
width: null,
height: null,
});

useLayoutEffect(() => {
const handleResize = () => {
setSize({
width: window.innerWidth,
height: window.innerHeight,
});
};

// Set initial size
handleResize();

const onResize = debounceTime
? debounce(handleResize, debounceTime)
: handleResize;

window.addEventListener("resize", onResize);

return () => {
window.removeEventListener("resize", onResize);
};
}, []);

return size;
}

0 comments on commit c6c2966

Please sign in to comment.