continue re-design

This commit is contained in:
Developers Digest
2025-09-05 13:06:17 -04:00
parent b96d048dbd
commit 836b085f75
270 changed files with 32269 additions and 5182 deletions
@@ -0,0 +1,219 @@
import CurvyRect from "@/components/shared/layout/curvy-rect";
import { cn } from "@/utils/cn";
import { ArrowUpRight } from "lucide-react";
interface Props {
navigationItems: {
label: string;
items: {
icon: React.ReactNode;
label: string;
description: string;
href: string;
target?: string;
big?: boolean;
ctas?: { label: string; href: string; target?: string }[];
sectionLabel?: string;
iconClassName?: string;
}[];
}[];
sideLabel: string;
sideContent: React.ReactNode;
sideItem: {
icon: React.ReactNode;
label: string;
description: string;
href: string;
target?: string;
};
}
export default function HeaderDropdownContent({
navigationItems,
sideLabel,
sideContent,
sideItem,
}: Props) {
return (
<div className="lg-max:max-w-[unset] container lg:flex gap-16">
{navigationItems.map((item, index) => (
<div className="flex-1" key={index}>
<GroupLabel label={item.label} />
{/* Default section (items without sectionLabel) */}
<div
className={cn(
"grid gap-x-16",
navigationItems.length === 1 && "lg:grid-cols-2",
)}
>
{item.items
.filter((it) => !it.sectionLabel && !it.big)
.map((it) => (
<Item item={it} key={it.label} />
))}
{item.items
.filter((it) => !it.sectionLabel && it.big)
.map((it) => (
<ItemBig item={it} key={it.label} />
))}
</div>
{/* Additional sections within the same column */}
{Array.from(
new Set(
item.items
.map((it) => it.sectionLabel)
.filter(Boolean) as string[],
),
).map((section) => (
<div key={section}>
{/* <GroupLabel label={section} /> */}
<div
className={cn(
"grid gap-x-16",
navigationItems.length === 1 && "lg:grid-cols-2",
)}
>
{item.items
.filter((it) => it.sectionLabel === section && !it.big)
.map((it) => (
<Item item={it} key={it.label} />
))}
{item.items
.filter((it) => it.sectionLabel === section && it.big)
.map((it) => (
<ItemBig item={it} key={it.label} />
))}
</div>
</div>
))}
<div className="h-42 border-t border-border-faint border-x -mt-1 relative lg-max:hidden">
<CurvyRect
className="-top-1 absolute -left-1 w-[calc(100%+2px)]"
top
/>
<CurvyRect
className="bottom-full absolute -left-1 w-[calc(100%+2px)]"
bottom
/>
</div>
</div>
))}
<div className="flex-1 max-w-360 relative">
<div className="h-full w-1 absolute top-0 left-0 bg-border-faint" />
<GroupLabel label={sideLabel} />
<div className="hidden lg:contents">{sideContent}</div>
<Item item={sideItem} />
<div className="h-42 border-t border-border-faint border-r -mt-1 relative lg-max:hidden">
<CurvyRect
className="-top-1 absolute left-0 w-[calc(100%+1px)]"
top
/>
<CurvyRect
className="bottom-full absolute left-0 w-[calc(100%+1px)]"
bottom
/>
</div>
</div>
</div>
);
}
const GroupLabel = ({ label }: { label: string }) => {
return (
<div className="text-body-medium py-16 lg:py-20 lg:border-x border-b border-border-faint px-24 lg:px-44 text-black-alpha-64">
{label}
</div>
);
};
const Item = ({
item,
}: {
item: {
icon: React.ReactNode;
label: string;
description: string;
href: string;
target?: string;
iconClassName?: string;
};
}) => {
return (
<a
className="flex items-start gap-16 py-16 pl-24 lg-max:[&_svg]:size-24 lg:pl-44 group border-x hover:bg-black-alpha-2 border-b border-border-faint transition-all hover:text-heat-100"
href={item.href}
key={item.label}
target={item.target}
>
<div className={item.iconClassName}>{item.icon}</div>
<div className="min-w-0 flex-1">
<div className="text-label-medium">{item.label}</div>
<div className="text-body-medium mt-4 text-black-alpha-64 lg-max:hidden">
{item.description}
</div>
</div>
</a>
);
};
const ItemBig = ({
item,
}: {
item: {
icon: React.ReactNode;
label: string;
description: string;
href: string;
target?: string;
ctas?: { label: string; href: string; target?: string }[];
iconClassName?: string;
};
}) => {
return (
<div
className="flex items-start gap-16 py-22 pl-24 lg-max:[&_svg]:size-24 lg:pl-44 group border-x border-b transition-colors border-border-faint"
key={item.label}
>
<div className={item.iconClassName}>{item.icon}</div>
<div className="min-w-0 flex-1">
<a
href={item.href}
target={item.target}
className="text-label-medium inline-block hover:text-heat-100 transition-colors"
>
{item.label}
</a>
<div className="text-body-medium mt-4 text-black-alpha-64 lg-max:hidden">
{item.description}
</div>
{item.ctas && item.ctas.length > 0 && (
<div className="mt-12 flex items-center gap-8 lg-max:hidden">
{item.ctas.map((cta) => (
<a
key={cta.label}
href={cta.href}
target={cta.target}
className="inline-flex items-center gap-6 px-12 py-6 rounded-6 text-label-small text-heat-100 bg-heat-4 hover:bg-heat-8 transition-colors whitespace-nowrap shrink-0"
>
<span>{cta.label}</span>
<ArrowUpRight className="size-14" aria-hidden="true" />
</a>
))}
</div>
)}
</div>
</div>
);
};
@@ -0,0 +1,91 @@
import { ArrowUpRight } from "lucide-react";
import { cn } from "@/utils/cn";
export interface NavItemRowProps {
icon: React.ReactNode;
label: string;
description: string;
href: string;
target?: string;
iconClassName?: string;
className?: string;
}
export function NavItemRow({
icon,
label,
description,
href,
target,
iconClassName,
className,
}: NavItemRowProps) {
return (
<a
className={cn(
"flex items-start gap-16 py-16 pl-24 lg-max:[&_svg]:size-24 lg:pl-44 group border-x hover:bg-black-alpha-2 border-b border-border-faint transition-all hover:text-heat-100",
className,
)}
href={href}
target={target}
>
<div className={iconClassName}>{icon}</div>
<div className="min-w-0 flex-1">
<div className="text-label-medium">{label}</div>
<div className="text-body-medium mt-4 text-black-alpha-64 lg-max:hidden">
{description}
</div>
</div>
</a>
);
}
export interface NavItemRowBigProps extends Omit<NavItemRowProps, "target"> {
ctas?: { label: string; href: string; target?: string }[];
}
export function NavItemRowBig({
icon,
label,
description,
href,
iconClassName,
ctas,
}: NavItemRowBigProps) {
return (
<div className="flex items-start gap-16 py-22 pl-24 lg-max:[&_svg]:size-24 lg:pl-44 group border-x border-b transition-colors border-border-faint">
<div className={iconClassName}>{icon}</div>
<div className="min-w-0 flex-1">
<a
href={href}
className="text-label-medium inline-block hover:text-heat-100 transition-colors"
>
{label}
</a>
<div className="text-body-medium mt-4 text-black-alpha-64 lg-max:hidden">
{description}
</div>
{ctas && ctas.length > 0 && (
<div className="mt-12 flex items-center gap-8 lg-max:hidden">
{ctas.map((cta) => (
<a
key={cta.label}
href={cta.href}
target={cta.target}
className="inline-flex items-center gap-6 px-12 py-6 rounded-6 text-label-small text-heat-100 bg-heat-4 hover:bg-heat-8 transition-colors whitespace-nowrap shrink-0"
>
<span>{cta.label}</span>
<ArrowUpRight className="size-14" aria-hidden="true" />
</a>
))}
</div>
)}
</div>
</div>
);
}
@@ -0,0 +1,63 @@
"use client";
import { HTMLAttributes, useEffect, useRef } from "react";
import { cn } from "@/utils/cn";
import { setIntervalOnVisible } from "@/utils/set-timeout-on-visible";
import data from "./data.json";
export default function GithubFlame(attrs: HTMLAttributes<HTMLDivElement>) {
const ref = useRef<HTMLDivElement>(null);
const wrapperRef = useRef<HTMLDivElement>(null);
useEffect(() => {
let index = 0;
const interval = setIntervalOnVisible({
element: wrapperRef.current,
callback: () => {
index++;
if (index >= data.length) index = 0;
const newStr = data[index];
if (!ref.current) return;
ref.current!.innerHTML = newStr;
},
interval: 60,
});
return () => interval?.();
}, []);
return (
<div
className="absolute -top-20 left-180 w-194 h-192"
style={{
maskImage: "url('/assets-original/github-mask.png')",
maskSize: "100% 100%",
}}
>
<div
ref={wrapperRef}
{...attrs}
className={cn(
"w-308 h-380 -top-20 -left-40 absolute pointer-events-none select-none",
attrs.className,
)}
>
<div
className="text-black-alpha-20 relative top-0 left-0 font-ascii fc-decoration"
ref={ref}
style={{
whiteSpace: "pre",
fontSize: 8,
lineHeight: "10px",
}}
/>
</div>
</div>
);
}
@@ -0,0 +1,25 @@
[
" \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ''.-_,.'' \n '.:;==+^_.' \n '-\";+;:,' \n ''''' \n \n ",
" \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ''' \n '-:\"\":,.' \n .:;+++;^:-' \n '-\"+===+;\"-' \n ',^+==++;:.' \n ',\"^^\":,.' \n ''''' \n ",
" \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n .::,'' \n '-\";;^\"\":,' \n '.:\";++;;;\"_-'' \n .:;+===++;\",' \n '_;+====+;:,'' \n '_;++=++;\",.' \n '-\"^^^\":_.'' \n '.--.''' \n ",
" \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n '-::,' \n ':;+;^^\",.' \n .:\";+++;;^\":. \n '-\"^;+====+;\":-''' \n '-::;=====++;^:-'' \n ',\"+======+^:,-' \n '-:;+=====;\",.'' \n ._^;++=+;\"-'' \n '-::::,.''' \n ''-..'' \n ",
" \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n '-_:_' \n -^;++++;;;. \n .\"+======+^:,_' \n .\";;+=====+;:__- \n -:_^+=====+;\"::,''' \n .-.\"+=======+^:__-.' \n '.._\"+======+\",-.'' \n '..,;======;_'''' \n '.._;+===++:.' \n '',::^\":.''' \n '''..'' \n ''' \n ",
" \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n -\"^++\". \n ';++=====+;^:- \n '\"+========+;;^' \n -;;;;++====+;\",,,. \n ':,__:;;;+===+^___-. \n '...-^;++++==+;::^:,' \n ''--,\"++=====;\"_,,-'' \n ''''_+++===++;-.,_-' \n '''.\";;+;^\"^_-''''' \n ''._;\"\":_--' \n ''._:_-' ' \n ''''''' \n ''' \n \n ",
" \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n '-_,-' \n '':^+=====^' \n ':++========+;. \n ^+============+^;;- \n ,;;;;;+++====+^\";^: \n ';;^\":\";++++++;;:::_' \n ,,:_-,,\";^\"^;+=+;:::_-' \n '-.'''..,--,:,-_:;;+;^_ \n ''''--.-..'._:\";\"_.' \n '''..-,_-''.''..-.'' \n ''.-:,:_'''''''..' \n ' ''.,_,-'' \n '''--.' \n '..' \n ' \n \n \n ",
" \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n '^+++\"::- \n ''':^;+=======^ \n '++=============+ \n -;;++===+======++^: \n .++++++++;;+++++++^\"\"\", \n '_\";;;;++;;^;;^;++;;;;;. \n ';;:_,_\"^\"_,_^++;;;;^:- \n \"\":,--:+;_-,,---,__\"^::.' \n '.-,.''..-.''''''''.-\";++^. \n ''''-----,.'''''',:\"\"^, \n '',--,-.'''''.-_,'' \n ''-.'''''''''' ' \n ''-.''' '' \n '.,' \n '.--' \n ''''' \n ' \n \n \n ",
" \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n '''' \n '\"+====^;+\"- \n ''-+=============+' \n :+==========+=+====+- \n '+========++++++++==+ \n :^+++==++;;+;;+=====+;- \n '++++++++;^\"\"\";++;+;\"\"^\":- \n -^;;;;++;\"__:\";;^;;\":\";++: \n '.\"^;^:::\"\"::\":::\"\":::\";+:. \n .:\":_,,__:,--''--,___::;;:' \n -_,,-,,__--.'''''..._:;++;- \n ''-::::_::--....''-,:\"^;\" \n '''':^\":__----...-,::^- \n '''''--'''''.'-:-' \n '.' ''' \n '.-' \n '.-'' \n '..-.' \n '..'' \n '' \n \n \n ",
" \n \n \n \n \n \n \n \n \n \n \n \n \n '-:_' \n '.' -;+++++:-\":' \n ,+===++==++++=====+- \n .:^+=========;+===+====' \n ';+=========++++;;++++==+- \n .;====++++++;;;;;+++++++. \n '-:;==++;^;;;;^;;+++==++=+ \n '+++++++;;;;^^\"^^^^^;++^\"^^_'' \n ,:;+;;;+;;\"_:::::\"\"::__::^;;+: \n .,:\"^;;^:,::_:,,_:\":_,__\"\";;- \n ,:\"\":_,___,-...---_,,___::;\"' \n .::_,,::::_-..,-.--,__,:^;+;' \n ''.,:\"\"\";:-,,---...,:^\";;;:' \n '-'\"^_\":_::_-----,,:\"^: \n ''''.....''.--,,-_: \n ''' '-_- \n ''''' \n '.-'' \n '.---.' \n '---' \n ''..' \n ' \n \n \n ",
" \n \n \n \n \n \n \n \n \n \n \n '-,.' \n '..' ':++++++_ \n '+====+;+++++;+;+;;;^' \n +=====++=++=+;;++;+===+_ \n '\";=======++++++;;++=++===+' \n ,++=======++++;;;;;;+++++=+: \n '++==++++++++;;^;;;;+++++++^- \n '\"==+++;;;;^^\":::^;+++;;++;' \n -;;+++;;;^^^\":::::\"^+++++\"^\"::-,.' \n _++;;;^;;;;;\"::_:\"\"\"::_:\":___:\"^;^ \n -.,\"^^^^^\"::___::::__,--,,:::\"^\":' \n .-:\";\"::::_:::__,-,_,,-----_:_, \n .:\"^\"::_::::::::,,--,,,---,:^^- \n .,_::__,_\"\":\"^:--_,,,,_:\"^;;^' \n '--.-,_\"\"::__,_,,-,::\"\"\"\", \n '''------,___,-,:_\"\"' \n '' ''',,.''.' \n ''' \n .--'' \n '..--' \n '.--' \n .---. \n ''..' \n \n \n \n ",
" \n \n \n \n \n \n \n \n \n '' \n ''' '-;;++;;\"' \n .^+++++::;;;;;;;;;+^.' \n ';======++++++;+;;;;;;;^\"' \n .+====+++++++++++;;;^;;++=+\"' \n -:+==++=+==+++;;;;^;;++++===+: \n ,;+====+++++++;;;;;;;;++++++++, \n ,+++++++++;++++;;;;;;;;++++;;\"' \n :+==++;;+;;;;^^^\"^;;;;;;;;;;^: \n ':+++;;;;;;^^\"\"::_::\";;;^,:_,-_,:,-' \n -;;;;;^^^;^\"\"\"::::___::^^:,,----,:^\":' \n ';;^:::::\"\":::::::::_,,_::-,,__:\"\"::-' \n '''-_:\"\"\"^\"::\"::__:::_-,,,,:----_:,' \n '.-:\"^^^^\":::_:\":\":___,_:_-,-,:_. \n ._:^\"\"\":,,__::::____-----_:_:^_ \n '''''.-.-::_,,--,---_::\"\"^^\"' \n ' ''---..-:::,____,,,_- \n '' '''''.'.--,- \n '' \n '.-.' \n ''.-' \n '..'' \n '.-..' \n ...-' \n ''''' \n \n \n \n ",
" \n \n \n \n \n \n \n \n .^^^;\".'' \n '_^;^-''',:^;;;+;;;;;, \n '\"+==++++++;;^\"\"^;;;;;;;\". \n '+======+++++;;;;^;^\"^;^^;^_' \n \"+++===++++++++++;++\"\"^;^;;;++\" \n -\"+++++++++=++++;;;;;;^;++++++++: \n :;+===++++++++++;;;;;;;;;+++++++\"' \n .^+++++++;;+++;;^;;^;;;;;;++;;;;^. \n _+++++;;+;;;;;;;;^^^^^;;;;;;^:,. \n _++++;;;;;;;^^^\"\"::::\"\"::\"\"::,..-,,'' \n ,++;;^^^^^\":::::______:_-------..-:::- \n -^;;^::::::::\"\"\":_______,,---__,-_:::__' \n :^:,,_::::\"\"^^^:_:::__,__,,-,,__:__.'' \n '.,::\"\"^^\"::_::\":::__,::_,_:_,,-' \n '',:\"^^^:.--,::_____,,,__,_,,__- \n ''-,-.'''''',-----__,,____,_::- \n ''''''__---_,__:::- \n '''''''.-..-. \n ''' '''' \n '''' \n '''' \n ''''' \n ''''''' \n '''''' \n ''''' \n \n \n \n \n ",
" \n \n \n \n \n \n .--.--' \n '''' ',\"^^;;;;:,-' \n ,;++;;\"\"..__\"\"\"\"^;;;^^^;\"' \n :+===++;;;;;;;\"\"\":::\"^^;^\"\"_' \n ';+====+++;;;++;;;^:::\"\"^^^^^\":' \n :++++++++++;;++++++^\";;\"^^^;;\"^;;\"' \n .;;+++++;;++++++;;;;;^^^^^^;;;;+++\"' \n ,\"+++=+++;;++++;;;;;;;;;\"\"^;++;+++;;' \n :;+++=++;;;;++;;;;^^;;;;;^;;;;;;;;-' \n -^;++;;+;;^^;;^^\"^^^^^;;;;;;;^\"::_- \n '^+++;;;;;;;^^^\"^;\"\":\":::\"::,--''''''' \n :++;^\"^^^\"\"\"\"\":::,,-,_,,------.'''.---. \n _;^\"::__:::::\"::,,,--------''------,,_, \n ':\"\":__,,:::\"::___:_,------..-,__,-,,,,-' \n '--..-_:::\"\":---_:::,-,_:,----,,--.'' \n ''-_:\"\"\"_''.__::,,,,,,,--.-.---' \n '-_\"_. '''''-_,---,-..--,,- \n '--.'..----,,-' \n '''''''''....' \n ''''' \n \n \n \n \n \n \n \n \n \n \n \n ",
" \n \n \n \n \n '_:::\"^^\"_ \n '._-'' -\"\"\"\"\"^;;;:_-' \n '\"++;;^^\"\"\":_:\"^\"\"\":\"\"\"^^\"\"^:. \n -+===++;;;;;;;;^\"::::_:\"\"\"\"\"::-' \n ,+=====++;;;;;+;;^^::_:\"\"\"\"\"\"^^\":' \n '++==++=++;;;;+++;++;^_::::\"^^^^\":\":,- \n \"++++++;;;;;++++;;;;;:\"\":\"\"\"^^^^\";++\" \n ':++==+;;;;;++++;;;;;;\"\"^\"::^;+;^\";++_ \n _^++++=+;;;;++++;;;^^^^^^\"::\";;;;;;\"\"\"- \n -^+++++++;;^;+;;^^;^\"^^^^;^^^\"\":\"\":.' \n _;++;;;;;;;^^^\"\"\"\"\"^\"^^\"\"^^^\":_,-.'' \n ':++;;^\"\"^^\"\"::_______,---,,.'''..''''' \n .;;\"::::__:___,-,,--......''''...-.''''. \n '-___----_:::_-,_-,-..-....''.-......-.' \n '.__---,::::::,,_,-------.......''''.-.' \n '..-,_::::-'-__-.--,-...'''..'..' \n '.--_:' '.'''.--''''''''''..' \n '--.'''''''..'' \n '' ''''.'' \n \n \n \n \n \n \n \n \n \n \n \n \n \n ",
" \n \n \n '''''''' \n ._::_:\"^^^, \n '...__.' ',_\":::\"\"^^^:,' \n '_+++;;;\"::__::\"^\"\"::____:\"\":::_' \n -;==++;;;;;;^;;^^^\"\":_,,_:::::,_,. \n .;+=+++;;;^;^;;^^^:::_,_::::::::\":- \n ^+++=++=+;^^^^^++;^;;^\",---:\"\"\"^\"\":_.' ' \n :;;++++;;;;^\"\"^;+;^;^^\":_,,,::\"\":__,:\"^:' \n -\"+++++;;;^\":\";;;^;;^;^::_:::\"^\"\"^-,\"^^' \n -;+;++++;^^;;;;;;;;^;^\":::.-\"^^\"\"_:^^^: \n -:;+;;;;;;^^^;;+;;\"^\"::::\":,-_\"^^^:\"_-,-' \n '_^;;;;;;;;\"\"^;;;^\"\"\":::\"\"\"\"::::____' \n ,;;;;;\"::^^^\"\":_:_:______::\"_-.'''' \n -;;^\":::::::_,----.'''''....'''''''' \n ,,--,,--,::,-,--..''''''''''' '''' ' \n ''.-...-_::,---..-.''..''''''' '''''' \n ''...--_--,-'-.''...''''' '''''' ''' \n '''''.'.. '.'''.''''' ''''''' \n ''''''' ''' \n '' ''''' \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ",
" \n \n '.-,-'.,..' \n '-,::__:\"\"\"\"- \n '..-\":.' '_::::_::::::_.' \n .\";+;;;;;^\"\"\"::\"\"^^\"::_,-,:_:::_,-' \n -;+==+;;;;^^^^^^;^\":::_,-..-___,----' \n _;++;;;^^\"\"^\"\";^\"::,-.,-.--_:::,-__,' \n -\";;;;++;^^\"^\"\"\";;^:\"::__-'.,_::\"\":::. \n .;;;++++;;^^\":::^+;;^\"\"\"::,''---_::-,,..--.. \n -:\";;+;;^\"\"\"::_:\"^\"^^\":::,----,,_:,-.'.,_- \n ',;;;;;;;^^\"::::^^^^^^^\":_--,::::-,.'-::^. \n '_;;;;;^\"\":^;^\"^;;^^\"\"\"::,,._:::__--,__,' \n '-_^;;+;;\"::::^;;;;^\":_:___-..,,::_,,-''.' \n '-\"^;;;\"\"\"\"::\"\"^\"^\"\":_,__,,,_,-----. \n '_\":\"^\"::::\"\"\":,.-,-.'''..-,_.' '''' \n ''-:_--.--,_:-....'' ''''''' ' \n ''...''.-_,-.'''.''''''' \n '''...-.'''''''' \n ''''''' '''' \n '''' \n ' \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ",
" \n '.,--.-_-.' \n ' '-_:__,_:::_,' \n ..._^\"-' ' .:;\"_,,,,_,---'' \n -\";++;;;;^^^^\":^^^;^\",-.-..---,__.'' \n \"+==++;;;;^\"\"^\"\"\"^^:_--.'''''.-,,-'''' \n ':;++;;^^^^^\"::_\"^^:,-..''.'''.___-.''-. \n ,^^^^^;;\"\"\"\":_:\"\"\"::,---..''-,,__::::.' \n ':^;;;;;;;\"::::::;+;;^\":\"__-'''..-,_--_,' \n ':\"\"^;;;^\":,_:_,_\"\":\"^\":_,--''.''''..'.' '..'' \n '.-:;+;^\"\":___,--_:\"^^\":::_.'-_-.--.'' '.,. \n ._\";;;;;\":::_:_::^^\"^\"\"\":-'._::,...' .,.,-' \n ',^;;+;\"_,_:\"\":\";;^\":::_-.'-,---..'''.''' \n '.-:\";+;^:-_,_:\"^;;^:_---..''''.--.'''''' \n '.-_\"\"\"::::___::_:\":_''...--.'''''' \n ',-.,_...-,__:,.''''' '''' \n '-.'''''..''''' \n ''''''''' ' \n '' \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ",
" '.,--.'-.'' \n '' ''_:,..-,__'-' \n '..'::^_'''' '-_\"^-..''..''''' \n '^;+++;;^\"\"^\":-:\"^^\"\":-''''''''''''' \n ':+==++;+;;;^\"\"^\":::_-.''' '' ''.--' \n _+==+;\"^^^^^\"_,-,_:\"-..''' ' '-,,' \n ',:^\":\"\"::::::,,___:,-_,' '' '''---.-,,.-' \n ,^^::\"^;:____,_^;^\"\"::,,..''''''''...--' \n ::\"^\"^^\"\"_-.----,\":::\":__..'' '' '''''.' \n .,_,\";^^\"_-.'''..--_\"^:_,,-''''' '' ' '' \n '-^;+;;;\":-,_,--.,\"^:::::,''-,.'''' '' \n .-\";;;;\"_-,_____::;^\"::_'''.,.''''' '''''' \n '':^;;;\"::,___,_:;^:_--'''''''''' \n ''._:\"\"\"\":_,,,::\"^\":.''''''' '' \n '.----.--..---''._-' \n ''''''''''''' \n '' \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ",
" '-' '.:,' ''''' ' \n ''',::,''''' '.-,. '' \n '\";;+;;^\":::_,,'-,,,,-.. \n .\"++++;;;;;::,,_:,,,.'''' \n _+==;^\"\":::::,..-_---''' '..' \n _^;;\":__-..--.'.'..--.''.' ''''.''' \n '.__-,_,-......'-_.'..'.''' ''' ''''' \n ..---_-__-''.''-::---.-,.' \n '--,,::::,''''''''.'.,_-..'' \n '''.\"^;;^:..'--.'''-::..--'''' \n .:\";+;;:----..'.._::_:-'''''' \n '.,:^^^^\":,,..-.--:\"_-.' '' \n '.-_:_,,:_-.--.-::-' \n '.-.'''''''.''--. \n '.'''' '.' \n '' \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ",
" '-__.' '' ''''' ' \n _;+++;^\"_--.'''''''''''' \n _;++++;;;\"_,..'..-,''''''' \n ':++^\"\":-..'.'''.'.-''' ' \n :==+^:_-' ''' ''''''''' ' \n '_\"^_.-.' '''''' ''''' \n '''''''''''''''..''''''' \n ''''----''''''..''''''''' \n ''''-\"^\"\"_.''..''''''--'''' \n ':^;+;^-'.-.''''-,-...' \n -:;++;^::::-''''.-:_.' \n ''_:,,:,..__'''''':-' \n '''''''' '''.-' \n '.'''' '' \n ''' \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ",
" ':;;+\"::' \n .;+++=;:,.''' '' \n ,:\"\"^\",'' ' '' \n ,\"=^:_--. '.' ' \n :++;:-'' '' \n '-:-' ' \n '' '''''''' \n ',\":,-..'.''' ' \n ._\"^\"_-'''-.-' '' \n ':;=++:.'.'.' ''''-' \n .\"+;;\"__,.-' ',. \n '''''' '' \n ''' \n '' \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ",
" '_;^;=;'' \n '-,:^^,' \n '.'-_-' '' \n '-+_.' '' \n ,\"^^,' \n '.' ''' \n ..'''''''' \n ',,'' \n .._,,' \n ';=+;. ' ''' \n '''-,''' \n \n '' \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n "
]
@@ -0,0 +1,27 @@
import Image from "@/components/shared/image/Image";
import GithubFlame from "./Flame/Flame";
export default function HeaderDropdownGithub() {
return (
<div className="py-24 px-44 border-b border-border-faint relative overflow-clip">
<div className="size-40 relative mb-17">
<Image
alt="Firecrawl icon (blueprint)"
className="cw-80 ch-80 absolute top-0 left-0 max-w-[unset]"
height={80}
src="developer-os-icon"
width={80}
raw
/>
</div>
<div className="text-label-large">
Firecrawl is open source. <br />
Star us to show your support!
</div>
<GithubFlame />
</div>
);
}
@@ -0,0 +1,84 @@
"use client";
import { AnimatePresence, cubicBezier, motion } from "motion/react";
import { useState } from "react";
import {
ConnectorToLeft,
ConnectorToRight,
} from "@/components/shared/layout/curvy-rect";
import { NAV_ITEMS } from "@/components/shared/header/Nav/Nav";
import { cn } from "@/utils/cn";
export default function HeaderDropdownMobileItem({
item,
}: {
item: (typeof NAV_ITEMS)[number];
}) {
const [open, setOpen] = useState(false);
return (
<>
<a
className="p-24 flex group relative"
href={item.href}
onClick={() => {
setOpen((v) => !v);
}}
>
<div className="h-1 bottom-0 absolute left-0 w-full bg-border-faint" />
<ConnectorToRight className="-top-11 left-0" />
<ConnectorToRight className="-bottom-10 left-0" />
<ConnectorToLeft className="-top-11 right-0" />
<ConnectorToLeft className="-bottom-10 right-0" />
<span className="px-4 flex-1 text-label-medium text-accent-black">
{item.label}
</span>
{item.dropdown && (
<svg
className={cn(
"transition-all duration-200",
open ? "rotate-180 text-accent-black" : "text-black-alpha-48",
)}
fill="none"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.4001 10.2L12.0001 13.8L15.6001 10.2"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.25"
/>
</svg>
)}
</a>
<AnimatePresence>
{open && (
<motion.div
animate={{ height: "auto", opacity: 1, filter: "blur(0px)" }}
className="overflow-hidden"
exit={{ height: 0, opacity: 0, filter: "blur(4px)" }}
initial={{ height: 0, opacity: 0, filter: "blur(4px)" }}
transition={{ duration: 0.3, ease: cubicBezier(0.4, 0, 0.2, 1) }}
>
{item.dropdown}
<div className="h-44 relative">
<ConnectorToRight className="-top-11 left-0" />
<ConnectorToRight className="-bottom-10 left-0" />
<ConnectorToLeft className="-top-11 right-0" />
<ConnectorToLeft className="-bottom-10 right-0" />
<div className="h-1 bottom-0 absolute left-0 w-full bg-border-faint" />
</div>
</motion.div>
)}
</AnimatePresence>
</>
);
}
@@ -0,0 +1,49 @@
import { Fragment } from "react";
import Button from "@/components/ui/shadcn/button";
import {
ConnectorToBottom,
ConnectorToLeft,
ConnectorToRight,
} from "@/components/shared/layout/curvy-rect";
import HeaderGithubClient from "@/components/shared/header/Github/GithubClient";
import { NAV_ITEMS } from "@/components/shared/header/Nav/Nav";
import HeaderDropdownMobileItem from "./Item/Item";
import Link from "next/link";
export default function HeaderDropdownMobile({
ctaHref = "/signin/signup",
ctaLabel = "Sign up",
}: {
ctaHref?: string;
ctaLabel?: string;
}) {
return (
<div className="container relative">
<div className="overlay border-x pointer-events-none border-border-faint" />
<ConnectorToBottom className="-top-1 -left-10" />
<ConnectorToBottom className="-top-1 -right-10" />
<div>
{NAV_ITEMS.map((item) => (
<Fragment key={item.label}>
<HeaderDropdownMobileItem item={item} />
</Fragment>
))}
</div>
<div className="p-24 flex flex-col gap-8 border-b border-border-faint relative -mt-1">
<HeaderGithubClient />
<Link href={ctaHref}>
<Button variant="secondary"> {ctaLabel} </Button>
</Link>
<ConnectorToRight className="left-0 -bottom-11" />
<ConnectorToLeft className="right-0 -bottom-11" />
</div>
<div className="h-36" />
</div>
);
}
@@ -0,0 +1,62 @@
"use client";
import { HTMLAttributes, useEffect, useRef } from "react";
import data from "@/components/app/(home)/sections/hero-flame/data.json";
import { cn } from "@/utils/cn";
import { setIntervalOnVisible } from "@/utils/set-timeout-on-visible";
export default function StoriesFlame(attrs: HTMLAttributes<HTMLDivElement>) {
const ref = useRef<HTMLDivElement>(null);
const wrapperRef = useRef<HTMLDivElement>(null);
useEffect(() => {
let index = 0;
const interval = setIntervalOnVisible({
element: wrapperRef.current,
callback: () => {
index++;
if (index >= data.length) index = 0;
const newStr = data[index];
if (!ref.current) return;
ref.current!.innerHTML = newStr;
},
interval: 60,
});
return () => interval?.();
}, []);
return (
<div
className="absolute right-10 bottom-10 w-194 h-165"
style={{
maskImage: "url('/assets-original/replit-mask.png')",
maskSize: "100% 100%",
}}
>
<div
ref={wrapperRef}
{...attrs}
className={cn(
"w-308 h-380 -top-20 -left-40 absolute pointer-events-none select-none",
attrs.className,
)}
>
<div
className="text-black-alpha-20 relative top-0 left-0 font-ascii fc-decoration"
ref={ref}
style={{
whiteSpace: "pre",
fontSize: 8,
lineHeight: "10px",
}}
/>
</div>
</div>
);
}
@@ -0,0 +1,31 @@
import ArrowUp from "./_svg/ArrowUp";
import Replit from "./_svg/Replit";
import StoriesFlame from "./Flame/Flame";
export default function HeaderDropdownStories() {
return (
<a
className="pt-32 pr-32 pl-44 pb-48 group block border-b border-border-faint relative overflow-clip"
href="/blog/how-replit-uses-firecrawl-to-power-ai-agents"
>
<div className="flex mb-40 justify-between items-center">
<div className="py-4 px-8 text-heat-100 text-[12px]/[16px] font-[450] bg-heat-8 rounded-6">
Customer story
</div>
<div className="p-2 text-black-alpha-56 group-hover:text-heat-100 transition-all group-hover:translate-x-1 group-hover:translate-y-[-1px]">
<ArrowUp />
</div>
</div>
<Replit />
<div className="text-title-h5 mt-31 pr-32">
How Replit uses <span className="text-heat-100">Firecrawl</span> to
power Replit Agent
</div>
<StoriesFlame />
</a>
);
}
@@ -0,0 +1,19 @@
export default function ArrowUp() {
return (
<svg
fill="none"
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M17.2083 14.7082V6.7915M17.2083 6.7915H9.29167M17.2083 6.7915L7 16.9998"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.25"
/>
</svg>
);
}
@@ -0,0 +1,24 @@
export default function Replit() {
return (
<svg
fill="none"
height="32"
viewBox="0 0 32 32"
width="32"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M15.54 10.8196H5.27864C4.26819 10.8196 3.4668 10.0148 3.4668 9.03879V3.11427C3.4668 2.12115 4.28561 1.3335 5.27864 1.3335H13.7281C14.7386 1.3335 15.54 2.13827 15.54 3.11427V10.8196Z"
fill="#262626"
/>
<path
d="M26.5452 21.1531H15.5508V10.8047H26.5452C27.6091 10.8047 28.4864 11.6811 28.4864 12.7439V19.214C28.4864 20.2955 27.6091 21.1531 26.5452 21.1531Z"
fill="#262626"
/>
<path
d="M13.7281 30.6668H5.27864C4.28561 30.6668 3.4668 29.8635 3.4668 28.8892V22.9583C3.4668 21.9841 4.28561 21.1807 5.27864 21.1807H15.54V28.8892C15.54 29.8635 14.7212 30.6668 13.7281 30.6668Z"
fill="#262626"
/>
</svg>
);
}
@@ -0,0 +1,106 @@
// @ts-nocheck
"use client";
import { AnimatePresence, cubicBezier, motion } from "motion/react";
import { useEffect } from "react";
import { Connector } from "@/components/shared/layout/curvy-rect";
import { useHeaderContext } from "@/components/shared/header/HeaderContext";
import { lockBody } from "@/components/shared/lockBody";
import AnimatedHeight from "@/components/shared/layout/animated-height";
export default function HeaderDropdownWrapper() {
const {
dropdownContent,
resetDropdownTimeout,
clearDropdown,
dropdownKey,
headerHeight,
headerTop,
} = useHeaderContext();
useEffect(() => {
lockBody("header-dropdown", !!dropdownContent);
}, [dropdownContent]);
return (
<AnimatePresence>
{dropdownContent && (
<motion.div
animate={{ opacity: 1 }}
className="h-screen w-screen fixed left-0 z-[2000] bg-black-alpha-40"
exit={{
opacity: 0,
transition: {
duration: 0.3,
delay: 0.1,
ease: cubicBezier(0.4, 0, 0.2, 1),
},
}}
initial={{ opacity: 0 }}
style={{
top: headerTop.current + headerHeight.current + 1,
}}
transition={{ duration: 0.3, ease: cubicBezier(0.4, 0, 0.2, 1) }}
>
<div
className="overlay"
onClick={() => {
if (window.innerWidth < 996) {
clearDropdown(true);
}
}}
onMouseEnter={() => {
if (window.innerWidth > 996) {
clearDropdown(true);
}
}}
/>
<AnimatedHeight
animate={{
transition: { duration: 0.5, ease: cubicBezier(0.4, 0, 0.2, 1) },
}}
className="overflow-clip relative"
exit={{
height: 0,
transition: { duration: 0.3, ease: cubicBezier(0.4, 0, 0.2, 1) },
}}
initial={{ height: 0 }}
>
<AnimatePresence mode="popLayout">
<motion.div
className="bg-background-base hide-scrollbar relative overflow-x-clip overflow-y-auto"
key={dropdownKey}
style={{
maxHeight: `calc(100vh - ${headerTop.current + headerHeight.current + 1}px)`,
}}
onMouseEnter={resetDropdownTimeout}
onMouseLeave={() => {
if (window.innerWidth < 996) return;
clearDropdown();
}}
>
<div className="cmw-[1112px] absolute h-full pointer-events-none top-0 border-x border-border-faint">
<Connector className="absolute -left-[11.5px] -top-11" />
<Connector className="absolute -right-[11.5px] -top-11" />
</div>
<motion.div
animate={{ opacity: 1 }}
exit={{ opacity: 0, pointerEvents: "none" }}
initial={{ opacity: 0 }}
transition={{
duration: 0.3,
ease: cubicBezier(0.4, 0, 0.2, 1),
}}
>
{dropdownContent}
</motion.div>
</motion.div>
</AnimatePresence>
</AnimatedHeight>
</motion.div>
)}
</AnimatePresence>
);
}