Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ This repository is a high-performance webpage built on **Next.js 16 (App Router)

---

## Website Navigation

- [Documentation](https://pictopy.aossie.org/documentation)
- [Contribute](https://pictopy.aossie.org/contribute)
- [About Us](https://pictopy.aossie.org/about)
- [Contact](https://pictopy.aossie.org/contact)

## Project Resources

- [PictoPy Website](https://pictopy.aossie.org/)
- [PictoPy Website GitHub Repository](https://github.com/AOSSIE-Org/PictoPy-Website)

## 📂 Project Architecture

- **`src/app/[locale]/`**: Localized dynamic route group. All pages and layouts live here.
Expand Down
54 changes: 0 additions & 54 deletions src/app/[locale]/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,6 @@


@theme {
/* Bind Tailwind v4 variables directly to semantic CSS variables */
--color-background-primary: var(--background);
--color-background-secondary: var(--background-secondary);
--color-foreground-primary: var(--foreground);
--color-foreground-secondary: var(--foreground-secondary);
--color-foreground-muted: var(--foreground-muted);
--color-border-default: var(--border);

--color-button-primary-bg: var(--button-primary-bg);
--color-button-primary-text: var(--button-primary-text);
--color-button-primary-border: var(--button-primary-border);
--color-button-primary-hover-bg: var(--button-primary-hover-bg);
--color-button-primary-hover-border: var(--button-primary-hover-border);

--color-card-bg: var(--card-bg);
--color-card-border: var(--card-border);
--shadow-card: var(--card-shadow);

--color-status-error-bg: var(--status-error-bg);
--color-status-error-text: var(--status-error-text);
--color-status-neutral-bg: var(--status-neutral-bg);
Expand All @@ -34,24 +16,6 @@

/* --- LIGHT MODE (Default) --- */
:root {
--background: #f5f5f5;
--background-secondary: #ffffff;

--foreground: #1c1c1c;
--foreground-secondary: #121212;
--foreground-muted: #4d4d4d;

--border: #e2e2e7;

--button-primary-bg: #121212;
--button-primary-text: #ffffff;
--button-primary-border: #121212;
--button-primary-hover-bg: #2d2d2d;
--button-primary-hover-border: #2d2d2d;

--card-bg: rgba(255, 255, 255, 0.85);
--card-border: #e2e2e7;
--card-shadow: 0 4px 12px rgba(0, 0, 0, 0.04);

--status-error-bg: #fee2e2;
--status-error-text: #dc2626;
Expand All @@ -65,24 +29,6 @@

/* --- DARK MODE --- */
.dark {
--background: #101010;
--background-secondary: #000000;

--foreground: #d3d3d3;
--foreground-secondary: #b4b4b4;
--foreground-muted: #7f8188;

--border: #1e1e24;

--button-primary-bg: #ffffff;
--button-primary-text: #101010;
--button-primary-border: transparent;
--button-primary-hover-bg: #e4e4e7;
--button-primary-hover-border: #e4e4e7;

--card-bg: rgba(22, 22, 22, 0.85);
--card-border: #1e1e24;
--card-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);

--status-error-bg: rgba(69, 10, 10, 0.5);
--status-error-text: #f87171;
Expand Down
30 changes: 30 additions & 0 deletions src/components/CTA.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { getTranslations } from "next-intl/server";
import Download from "./Download";

async function CTA() {
const t = await getTranslations("Home.cta");
return (
<section aria-labelledby="cta-heading">
<div>
<div className="text-center tracking-normal">
<h2
id="cta-heading"
className="text-[20px] font-medium dark:text-text text-black leading-8"
>
{t("title")}
</h2>

<p className="text-[20px] font-medium leading-none text-text2">
{t("description")}
</p>
</div>

<div>
<Download />
</div>
</div>
</section>
);
}

export default CTA;
2 changes: 1 addition & 1 deletion src/components/Download.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function DownloadButton({ value }: DownloadButtonProps) {
}}
className="h-9 px-3 rounded-lg flex items-center gap-2 text-sm font-medium transition"
>
<value.icon />
<value.icon aria-label="Download" />
{value.label}
</Button>
);
Expand Down
64 changes: 64 additions & 0 deletions src/components/FAQ.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { getTranslations } from "next-intl/server";

interface FAQItem {
question: string;
answer: string;
}

export default async function FAQ() {
const t = await getTranslations("Home.faq");
const faqs: FAQItem[] = t.raw("items") as FAQItem[];
return (
<section className="bg-bg text-text tracking-normal leading-6.5">
<div className="px-6 py-12 md:px-8 md:py-20">
{/* Header */}
<div className="mb-16 text-center">
<h2 className="text-4xl font-semibold tracking-normal md:text-[64px] md:leading-18 bg-[linear-gradient(91.3deg,#202020_71.33%,#8F8F8F_152.13%)] bg-clip-text text-transparent [text-shadow:0px_4px_4px_0px_#00000026] dark:text-[#FFFFFF]">
{t("title")}
</h2>

<p className="mt-5 max-w-125 place-self-center text-[16px] leading-6.5 md:text-base tracking-normal font-medium dark:text-text2 text-text3">
{t("description")}
</p>
</div>

{/* FAQ list */}
<div className="w-full">
{faqs.map((faq, index) => (
<details
key={faq.question}
open={index === 0}
className="group border-b-2 dark:border-[#1B1C1E] border-[#E5E5E5] "
>
<summary className="flex cursor-pointer list-none items-center justify-between py-7 outline-none">
<span className="text-[16px] font-medium dark:text-text text-black ">
{faq.question}
</span>

{/* Plus / Minus */}
<span
aria-hidden="true"
className="relative flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-white/35"
>
<span className="absolute h-[1.5px] w-3 bg-black rounded-2xl" />

<span
className="
absolute rounded-2xl h-3 w-[1.5px] bg-black
transition-opacity duration-200
group-open:opacity-0
"
/>
</span>
</summary>

<div className="pb-12.5 pr-25 text-[16px] font-medium leading-6.5 text-text2">
{faq.answer}
</div>
</details>
))}
</div>
</div>
</section>
);
}
8 changes: 8 additions & 0 deletions src/components/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import MockUpWithDesc from "@/components/MockUpWithDesc";
import Metrics from "@/components/Metrics";
import SocialMediaCTA from "@/components/SocialMediaCTA";
import MacMockDesc from "@/components/MacMockDesc";
import FAQ from "./FAQ";
import CTA from "./CTA";
import PictoPyText from "./PictpPyText";
import Footer from "./ui/Footer";

export default function HomePage() {
return (
Expand All @@ -18,6 +22,10 @@ export default function HomePage() {
<MockUpWithDesc image={image} />
<Metrics />
<SocialMediaCTA />
<FAQ />
<CTA />
<PictoPyText />
<Footer />
</>
);
}
14 changes: 14 additions & 0 deletions src/components/PictpPyText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default function PictoPyText() {
return (
<div className="place-self-center">
<span
className="select-none text-[100px] font-semibold leading-none tracking-[0%] bg-[linear-gradient(178.49deg,rgba(230,230,230,0.1)_21.66%,rgba(128,128,128,0.1)_82.66%)] bg-clip-text text-transparent sm:text-[150px] md:text-[220px] lg:text-[280px] xl:text-[330px]"
style={{
filter: "drop-shadow(0 0 1px rgba(255,255,255,0.01))",
}}
>
PictoPy
</span>
</div>
);
}
40 changes: 25 additions & 15 deletions src/components/ShuffleGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { motion } from "framer-motion";
import { useEffect, useRef, useState } from "react";
import { squareData } from "@/const/const";
import Image from "next/image";

const shuffle = (array: (typeof squareData)[0][]) => {
let currentIndex = array.length,
Expand All @@ -20,7 +21,6 @@ const shuffle = (array: (typeof squareData)[0][]) => {
return array;
};


const ShuffleGrid = () => {
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const [squares, setSquares] = useState(squareData);
Expand Down Expand Up @@ -49,13 +49,18 @@ const ShuffleGrid = () => {
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 0.1, scale: 1 }}
transition={{ duration: 1.5, type: "spring" }}
className="w-full h-full"
style={{
backgroundImage: `url(${sq.src})`,
backgroundSize: "cover",
}}
/>
))}{" "}
className="w-full h-full relative overflow-hidden"
>
<Image
src={sq.src}
alt=""
fill
sizes="(max-width: 768px) 25vw, 25vw"
className="object-cover"
priority={sq.id < 4}
/>
</motion.div>
))}
</div>
<div className="absolute top-119.25 h-119.25 visible min-[411px]:hidden inset-0 grid bg-bg grid-cols-4 grid-rows-4 gap-2 z-0 overflow-hidden">
{squares.map((sq) => (
Expand All @@ -65,13 +70,18 @@ const ShuffleGrid = () => {
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 0.1, scale: 1 }}
transition={{ duration: 1.5, type: "spring" }}
className="w-full h-full"
style={{
backgroundImage: `url(${sq.src})`,
backgroundSize: "cover",
}}
/>
))}{" "}
className="w-full h-full relative overflow-hidden"
>
<Image
src={sq.src}
alt=""
fill
sizes="(max-width: 768px) 25vw, 25vw"
className="object-cover"
priority={sq.id < 4}
/>
</motion.div>
))}
</div>
</>
);
Expand Down
Loading
Loading