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

@ -4,6 +4,9 @@
"": {
"name": "my-qwik-empty-starter",
"dependencies": {
"@cloudgakkai/qwik-aos": "github:CloudGakkai/qwik-aos",
"@types/aos": "^3.0.7",
"aos": "^3.0.0-beta.6",
"ky": "^1.8.1",
"nanostores": "^1.0.1",
"tailwind-scrollbar": "^4.0.2",
@ -35,6 +38,8 @@
"@builder.io/qwik-city": ["@builder.io/qwik-city@1.14.1", "", { "dependencies": { "@mdx-js/mdx": "^3", "@types/mdx": "^2", "source-map": "^0.7.4", "svgo": "^3.3", "undici": "*", "valibot": ">=0.36.0 <2", "vfile": "6.0.2", "vite": "^5", "vite-imagetools": "^7", "zod": "3.22.4" } }, "sha512-VsAvk7u2HyyTnL9GhpT+h10t2XAIlxtv6LFL3Xt9/1QZ6lMfGWMcMEAMuZB1Ib+D/oTfu7QRqZngRg3FsrIKyg=="],
"@cloudgakkai/qwik-aos": ["@cloudgakkai/qwik-aos@github:CloudGakkai/qwik-aos#affd212", {}, "CloudGakkai-qwik-aos-affd212"],
"@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
@ -245,6 +250,8 @@
"@trysound/sax": ["@trysound/sax@0.2.0", "", {}, "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA=="],
"@types/aos": ["@types/aos@3.0.7", "", {}, "sha512-sEhyFqvKauUJZDbvAB3Pggynrq6g+2PS4XB3tmUr+mDL1gfDJnwslUC4QQ7/l8UD+LWpr3RxZVR/rHoZrLqZVg=="],
"@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
@ -297,6 +304,8 @@
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"aos": ["aos@3.0.0-beta.6", "", { "dependencies": { "classlist-polyfill": "^1.2.0", "lodash.debounce": "^4.0.8", "lodash.throttle": "^4.1.1" } }, "sha512-VLWrpq8bfAWcetynVHMMrqdC+89Qq/Ym6UBJbHB4crIwp3RR8uq1dNGgsFzoDl03S43rlVMK+na3r5+oUCZsYw=="],
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
"array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="],
@ -345,6 +354,8 @@
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
"classlist-polyfill": ["classlist-polyfill@1.2.0", "", {}, "sha512-GzIjNdcEtH4ieA2S8NmrSxv7DfEV5fmixQeyTmqmRmRJPGpRBaSnA2a0VrCjyT8iW8JjEdMbKzDotAJf+ajgaQ=="],
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
"collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="],
@ -655,8 +666,12 @@
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
"lodash.debounce": ["lodash.debounce@4.0.8", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="],
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
"lodash.throttle": ["lodash.throttle@4.1.1", "", {}, "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="],
"longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
"magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],

View file

@ -42,6 +42,8 @@
"vite-tsconfig-paths": "^4.2.1"
},
"dependencies": {
"@types/aos": "^3.0.7",
"aos": "^3.0.0-beta.6",
"ky": "^1.8.1",
"nanostores": "^1.0.1",
"tailwind-scrollbar": "^4.0.2"

BIN
public/dashboard 1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

BIN
public/dashboard 2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 KiB

View file

@ -3,8 +3,8 @@ import { useNanostore$ } from "~/hooks/nanostores";
import { api } from "~/lib/api";
import { areFilesLoaded, dashboardFiles } from "~/lib/stores";
import { StereoFile } from "~/lib/types";
import { SolarUploadLinear, SvgSpinnersBarsRotateFade } from "./Icons";
import StereoLogo from "./StereoLogo";
import { SolarUploadLinear, SvgSpinnersBarsRotateFade } from "../misc/Icons";
import StereoLogo from "../misc/StereoLogo";
export default component$(() => {
const loaded = useNanostore$<boolean>(areFilesLoaded);

View file

@ -3,7 +3,7 @@ import { useNanostore$ } from "~/hooks/nanostores";
import { api } from "~/lib/api";
import { dashboardFiles } from "~/lib/stores";
import { StereoFile } from "~/lib/types";
import { SolarClipboardAddBold, SolarDownloadMinimalisticBold, SolarTrashBin2Bold } from "./Icons";
import { SolarClipboardAddBold, SolarDownloadMinimalisticBold, SolarTrashBin2Bold } from "../misc/Icons";
type FileProps = {
file: StereoFile;

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>
);
});

View file

@ -0,0 +1,40 @@
import { component$, CSSProperties, QwikIntrinsicElements, Slot } from "@builder.io/qwik";
type GradientProps = {
size: string;
from: string;
to: string;
direction?: string;
};
export default component$((props: GradientProps & QwikIntrinsicElements["div"]) => {
const {
size,
from,
to,
direction,
style: userStyle,
...rest
} = props;
const borderStyle: CSSProperties = {
padding: size,
background: `linear-gradient(${direction || "to bottom"}, ${from}, ${to})`,
display: "inline-block",
};
// Only spread userStyle if it's an object
const mergedStyle =
userStyle && typeof userStyle === "object"
? { ...borderStyle, ...userStyle }
: borderStyle;
return (
<div
style={mergedStyle}
{...rest}
>
<Slot />
</div>
);
});

View file

@ -3,6 +3,5 @@
@theme {
--font-sans: 'DM Sans', sans-serif;
}
@plugin 'tailwind-scrollbar';
--color-stereo: #ff264e;
}

27
src/hooks/mouse.ts Normal file
View file

@ -0,0 +1,27 @@
import { $, Signal, useOnDocument, useStore } from "@builder.io/qwik";
export const useMouse = () => {
const pos = useStore({ x: 0, y: 0 });
useOnDocument("mousemove", $((event: MouseEvent) => {
const { clientX, clientY } = event;
pos.x = clientX;
pos.y = clientY;
}));
return pos
}
export const useRelativeMouse = (ref: Signal<HTMLElement | undefined>) => {
const pos = useStore({ x: 0, y: 0 });
useOnDocument("mousemove", $((event: MouseEvent) => {
const el = ref.value;
if (!el) return;
const rect = el.getBoundingClientRect();
pos.x = event.clientX - rect.left;
pos.y = event.clientY - rect.top;
}));
return pos;
};

View file

@ -1,9 +1,9 @@
import { component$, useVisibleTask$ } from "@builder.io/qwik";
import { routeLoader$, type DocumentHead } from "@builder.io/qwik-city";
import Controlbar from "~/components/Controlbar";
import Controlbar from "~/components/dashboard/Controlbar";
// import Dropzone from "~/components/Dropzone";
import File from "~/components/File";
import { SolarUploadLinear, SvgSpinnersBarsRotateFade } from "~/components/Icons";
import File from "~/components/dashboard/File";
import { SolarUploadLinear, SvgSpinnersBarsRotateFade } from "~/components/misc/Icons";
import { useNanostore$ } from "~/hooks/nanostores";
import { api } from "~/lib/api";
import { areFilesLoaded, dashboardFiles } from "~/lib/stores";
@ -12,7 +12,6 @@ import { StereoFile } from "~/lib/types";
export const useAuthCheck = routeLoader$(({ cookie, redirect: r }) => {
const jwt = cookie.get("jwt");
if (jwt) return {};
throw r(302, "/");
});

View file

@ -1,18 +1,44 @@
/* eslint-disable qwik/jsx-img */
import { component$ } from "@builder.io/qwik";
import { routeLoader$ } from "@builder.io/qwik-city";
import { OAUTH_LINK } from "~/lib/constants";
import { DocumentHead, routeLoader$ } from "@builder.io/qwik-city";
import CallToAction from "~/components/landing/CallToAction";
import Footer from "~/components/landing/Footer";
import Hero from "~/components/landing/Hero";
import Navbar from "~/components/landing/Navbar";
import Stats from "~/components/landing/Stats";
import Testimonials from "~/components/landing/Testimonials";
export const useAuthCheck = routeLoader$(({ cookie, redirect: r }) => {
const jwt = cookie.get("jwt");
if (!jwt) return {};
export const useAuthCheck = routeLoader$(({ cookie, redirect: r, query }) => {
const jwt = cookie.get("jwt");
const set = Boolean(query.get("jwt_set"));
throw r(302, "/dashboard");
if (jwt && set) {
throw r(302, "/dashboard");
}
return {};
});
export default component$(() => {
return (
<div class="flex flex-col items-center justify-center flex-grow">
<a href={OAUTH_LINK} class="bg-white text-black p-2 rounded-lg">clik heir 2 auth!!!!!!</a>
</div>
);
});
return (
<>
<Navbar />
<div class="flex flex-col flex-grow w-screen items-center mb-24 gap-56">
<Hero />
<Stats />
<Testimonials />
<CallToAction />
</div>
<Footer />
</>
);
});
export const head: DocumentHead = {
title: "Welcome to Qwik",
meta: [
{
name: "description",
content: "Qwik site description",
},
],
};

View file

@ -1,9 +1,25 @@
import { component$, Slot } from '@builder.io/qwik';
import { $, component$, Slot, useOnDocument } from '@builder.io/qwik';
import AOS from 'aos';
import 'aos/dist/aos.css';
export default component$(() => {
return (
<div class="flex flex-col min-h-screen bg-neutral-950 text-white scrollbar-thin overflow-scroll">
<Slot />
</div>
);
useOnDocument("DOMContentLoaded", $(() => {
AOS.init({
once: true,
duration: 1000,
offset: 100,
easing: 'ease-in-out',
});
}))
return (
<div
class="
flex flex-col
min-h-screen w-full overflow-x-clip
bg-neutral-950 text-white
">
<Slot />
</div>
);
});