make the landing page look beautiful 😍😍😍

This commit is contained in:
grngxd 2025-06-14 17:05:49 +01:00
parent a72ebe853f
commit d36c98cb49
22 changed files with 481 additions and 29 deletions

View file

@ -0,0 +1,23 @@
import { component$ } from "@builder.io/qwik";
import { OAUTH_LINK } from "~/lib/constants";
export default component$(() => (
<div
data-aos="fade-up"
data-aos-duration="1000"
class="flex flex-col gap-1.5 items-center justify-center w-4/5 bg-gradient-to-b from-stereo/30 to-transparent rounded-2xl h-96 py-4"
>
<p class="text-4xl text-center">
ready to try the <span class="text-stereo">stereo</span> experience?
</p>
<p class="text-xl text-white/80 text-center">
join over <span class="text-stereo">100k</span> other people hosting their files with <span class="text-stereo">stereo</span>!
</p>
<a
href={OAUTH_LINK}
class="px-12 py-1.5 mt-1.5 text-lg font-medium text-white bg-stereo rounded-full hover:text-stereo hover:bg-white transition duration-300"
>
get started
</a>
</div>
));

View file

@ -0,0 +1,29 @@
import { component$ } from "@builder.io/qwik";
import StereoLogo from "../misc/StereoLogo";
export default component$(() => {
return (
<div class="w-screen flex py-16 px-16">
<div class="flex flex-col flex-shrink h-full justify-start items-start gap-4">
<div class="flex flex-col">
<span class="flex gap-[1ch]">
<StereoLogo class="w-8 h-8 text-stereo" />
<span class="text-white font-medium text-2xl">stereo<span class="text-stereo font-bold">.</span>cat</span>
</span>
<span class="text-white/80 font-light text-lg">
store all your precious moments with <span class="text-stereo font-medium">stereo</span>.
</span>
<span class="text-white/80 font-light text-md">
copyright © {new Date().getFullYear()} stereo.cat - all rights reserved.
</span>
</div>
</div>
<div class="flex flex-grow justify-end items-start gap-12">
</div>
</div>
)
})

View file

@ -0,0 +1,83 @@
import { component$, useSignal } from "@builder.io/qwik";
import { useRelativeMouse } from "~/hooks/mouse";
import { OAUTH_LINK } from "~/lib/constants";
import GradientBorder from "../misc/GradientBorder";
export default component$(() => {
const ref1 = useSignal<HTMLElement>();
const mouse1 = useRelativeMouse(ref1);
const ref2 = useSignal<HTMLElement>();
const mouse2 = useRelativeMouse(ref2);
return (
<div class="flex flex-col w-full bg-gradient-to-b from-stereo/30 to-transparent overflow-x-clip select-none">
<div class="mt-62 flex flex-col justify-center w-full h-full text-center">
<div class="flex flex-col items-center justify-center gap-2 font-light">
<p class="text-6xl">
you bring the files, we'll bring the <span class="text-stereo">magic</span>.
</p>
<p class="text-2xl text-white/80">
stereo is no-bs file-host inspired by Tixte, that you'll love to use
</p>
<div class="flex gap-4 mt-1">
<a
href={OAUTH_LINK}
class="px-6 py-1 text-lg font-medium text-white bg-stereo rounded-full hover:text-stereo hover:bg-white transition duration-300"
>
get started
</a>
<a
href="discord.gg"
target="_blank"
rel="noopener noreferrer"
class="px-6 py-1 text-lg font-medium text-stereo rounded-full border border-stereo hover:bg-stereo hover:text-white transition duration-300"
>
learn more
</a>
</div>
<div class="mt-10 flex items-center" style={{ perspective: "1000px" }}>
<GradientBorder
ref={ref1}
size="3px"
from="#ff264e"
to="transparent"
direction="to bottom"
style={{
transformStyle: "preserve-3d",
transformOrigin: "center center",
transform: `
rotateX(${(mouse1.y / 120) + 30}deg)
rotateY(${(-mouse1.x / 120) + 25}deg)
rotateZ(-10deg)
`,
}}
class="rounded-2xl -mr-12"
>
<img src="dashboard 1.png" class="h-[32rem] rounded-2xl shadow-2xl shadow-stereo/10" />
</GradientBorder>
<GradientBorder
ref={ref2}
size="3px"
from="#ff264e"
to="transparent"
direction="to bottom"
style={{
transformStyle: "preserve-3d",
transformOrigin: "center center",
transform: `
rotateX(${(mouse2.y / 120) + 30}deg)
rotateY(${(-mouse2.x / 60) - 25}deg)
rotateZ(10deg)
`,
}}
class="rounded-2xl -ml-12 shadow-2xl shadow-stereo/10"
>
<img src="dashboard 2.png" class="h-[36rem] rounded-2xl" />
</GradientBorder>
</div>
</div>
</div>
</div>
);
});

View file

@ -0,0 +1,41 @@
import { component$ } from "@builder.io/qwik";
import StereoLogo from "../misc/StereoLogo";
export default component$(() => {
const items = [
{ text: "Synopsis", href: "#" },
{ text: "Discord", href: "#" },
{ text: "Dashboard", href: "/dashboard", highlighted: true },
{ text: "Pricing", href: "#" },
{ text: "Terms", href: "#" }
];
return (
<div
data-aos="fade-down"
data-aos-anchor-placement="top-center"
data-aos-duration="1000"
class="fixed flex items-center justify-start top-6 left-1/2 transform -translate-x-1/2 bg-neutral-950 p-8 h-10 rounded-full lg:w-2/3 md:w-4/5 w-4/5 z-[9999999] shadow-lg">
<div class="flex flex-1/3">
<StereoLogo class="w-10 h-10 text-stereo hover:text-white transition-all duration-300 hover:cursor-pointer" />
</div>
<div class="w-full hidden md:flex flex-grow gap-4 items-center justify-center">
{items.map(({ text, href, highlighted: h }) => (
<a
key={text}
href={href}
class={
h ? "px-4 py-0.5 hover:text-stereo hover:bg-white rounded-full font-medium bg-stereo text-white transition duration-300 text-lg"
: "text-white font-light hover:text-stereo transition duration-300 text-lg"
}
>
{text}
</a>
))}
</div>
<div class="flex flex-1/3 items-center justify-end">
<span class="text-white group hover:text-stereo transition duration-300 font-medium text-2xl">stereo<span class="text-stereo group-hover:text-white transition duration-300 font-bold">.</span>cat</span>
</div>
</div>
)
});

View file

@ -0,0 +1,40 @@
import { component$ } from "@builder.io/qwik";
export default component$(() => {
type StatProps = {
stat: string,
description: string;
}
const Stat = component$(({ stat, description }: StatProps) => (
<div class="group hover:scale-105 transition-all duration-300 bg-gradient-to-t from-stereo/30 to-transparent p-8 px-12 rounded-2xl h-96 flex flex-col items-center text-center shadow-2xl shadow-stereo/15">
<div class="flex-grow flex items-center justify-center">
<p class="text-7xl font-bold text-stereo group-hover:text-white transition-colours duration-300">
{stat.includes("+") ? stat.substring(0, stat.length - 1) : stat}
{stat.endsWith("+") ? <span class="text-stereo/80 text-4xl align-super">+</span> : ""}
</p>
</div>
<p class="text-xl w-3/4 text-wrap text-white/30 group-hover:text-white/60 transition-colours duration-300">{description}</p>
</div>
) );
return (
<div
data-aos="fade-up"
data-aos-duration="1000"
class="flex flex-col gap-12 items-center justify-center w-full"
>
<div class="flex flex-col gap-1 items-center justify-center">
<p class="text-lg text-stereo font-bold uppercase">statistics</p>
<p class="text-2xl text-white/80">
we know what you're thinking, and we have the numbers to prove it.
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<Stat stat="1.5M+" description="files hosted on stereo" />
<Stat stat="100k+" description="active users on stereo" />
<Stat stat="99.9%" description="uptime guarantee for all files" />
</div>
</div>
)
});

View file

@ -0,0 +1,112 @@
import { $, component$, useOnDocument, useSignal } from "@builder.io/qwik";
import ky from "ky";
export default component$(() => {
type TestimonialProps = {
nickname: string;
pfp?: string;
id?: string;
quote: string;
};
type LanyardResponse = {
data: {
discord_status: "online" | "idle" | "dnd";
discord_user: {
avatar: string;
}
},
success: boolean;
}
const Testimonial = component$(({ nickname, id, quote, pfp }: TestimonialProps) => {
const lanyard = useSignal<LanyardResponse>();
useOnDocument("DOMContentLoaded", $(async () => {
if (!id) return;
try {
const response = await ky.get(`https://api.lanyard.rest/v1/users/${id}`).json<LanyardResponse>();
lanyard.value = response;
console.log("Lanyard data:", lanyard.value);
} catch (error) {
console.error("Error fetching lanyard data:", error);
}
}));
return (
<div
data-aos="fade-up"
data-aos-duration="1000"
class="flex gap-6 items-center bg-gradient-to-t from-stereo/15 to-stereo/5 rounded-2xl p-6 max-w-2xl shadow-2xl shadow-stereo/15"
>
<div class="relative h-30 aspect-square flex-shrink-0 rounded-full"
style={{
border: lanyard.value ? `3px solid ${
lanyard.value.data.discord_status === "online"
? "#43b581"
: lanyard.value.data.discord_status === "idle"
? "#faa61a"
: lanyard.value.data.discord_status === "dnd"
? "#f04747"
: "#7289da"
}` : "",
padding: lanyard.value ? `3px` : "0px",
}}>
<img
src={pfp ? pfp : (
lanyard.value
? `https://cdn.discordapp.com/avatars/${id}/${lanyard.value.data.discord_user.avatar}.png`
: `https://api.dicebear.com/9.x/shapes/svg?seed=${Math.random().toString(36).substring(2, 15)}.png`
)}
class="rounded-full h-full w-full object-cover"
/>
</div>
<div class="flex flex-col gap-2">
<p class="text-stereo text-9xl h-min -mb-18"></p>
<p class="text-lg text-white/90">
{quote}
</p>
<p class="text-white/60 font-light italic"> {nickname}</p>
</div>
</div>
);
});
return (
<div
data-aos="fade-up"
data-aos-duration="1000"
class="flex flex-col gap-6 items-center justify-center w-4/5"
>
<div class="flex flex-col gap-1 items-center justify-center">
<p class="text-lg text-stereo font-bold uppercase">testimonials</p>
<p class="text-2xl text-white/80">
don't just take our word for it, hear it from our users.
</p>
</div>
<div class="flex flex-col gap-6">
<Testimonial
nickname="grng"
id="829372486780715018"
quote="stereo is the best file host I've ever used, it's fast, reliable, and the interface is so clean and easy to use. I love it!"
/>
<Testimonial
nickname="hexlocation"
id="1325924978805440522"
quote="I've been using stereo for a while now, and I can't imagine going back to any other file host. It's just that good!"
/>
<Testimonial
nickname="an anonymous user"
quote="stereo has changed the way I share files, it's so easy to use and the performance is top-notch. Highly recommend!"
/>
</div>
<p class="text-xl text-white/50">
and many, many more...
</p>
</div>
);
});