diff --git a/.github/lighthouse/lighthouserc.json b/.github/lighthouse/lighthouserc.json new file mode 100644 index 0000000..10ba861 --- /dev/null +++ b/.github/lighthouse/lighthouserc.json @@ -0,0 +1,67 @@ +{ + "ci": { + "collect": { + "startServerCommand": "npm run preview", + "url": [ + "http://localhost:4173/" + ], + "numberOfRuns": 3 + }, + "assert": { + "preset": "lighthouse:recommended", + "assertions": { + "categories:performance": [ + "error", + { + "minScore": 0.9 + } + ], + "categories:accessibility": [ + "warn", + { + "minScore": 0.9 + } + ], + "categories:best-practices": [ + "warn", + { + "minScore": 0.9 + } + ], + "categories:seo": [ + "warn", + { + "minScore": 0.9 + } + ], + "first-contentful-paint": [ + "warn", + { + "maxNumericValue": 2000 + } + ], + "largest-contentful-paint": [ + "error", + { + "maxNumericValue": 2500 + } + ], + "cumulative-layout-shift": [ + "error", + { + "maxNumericValue": 0.1 + } + ], + "total-blocking-time": [ + "warn", + { + "maxNumericValue": 200 + } + ] + } + }, + "upload": { + "target": "temporary-public-storage" + } + } +} \ No newline at end of file diff --git a/.github/workflows/lighthouse-ci.yml b/.github/workflows/lighthouse-ci.yml new file mode 100644 index 0000000..be9f69e --- /dev/null +++ b/.github/workflows/lighthouse-ci.yml @@ -0,0 +1,32 @@ +name: Lighthouse CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + lighthouse: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + - name: Run Lighthouse CI + uses: treosh/lighthouse-ci-action@v11 + with: + uploadArtifacts: true + temporaryPublicStorage: true + configPath: ./.github/lighthouse/lighthouserc.json diff --git a/App.tsx b/App.tsx index cc16c89..3d6653c 100644 --- a/App.tsx +++ b/App.tsx @@ -5,10 +5,12 @@ import { Navbar } from './components/Navbar'; import { Footer } from './components/Footer'; import { ErrorBoundary } from './components/ErrorBoundary'; import { AuthProvider } from './components/AuthProvider'; -// Halaman yang dimuat secara eager +// Halaman yang dimuat secara eager (Cuma Home buat LCP) import { Home } from './pages/Home'; -import { Login } from './pages/Auth/Login'; -import { Register } from './pages/Auth/Register'; + +// Halaman yang dimuat secara lazy (biar enteng pas buka awal) +const Login = React.lazy(() => import('./pages/Auth/Login').then(module => ({ default: module.Login }))); +const Register = React.lazy(() => import('./pages/Auth/Register').then(module => ({ default: module.Register }))); // Halaman yang dimuat secara lazy (biar enteng pas buka awal) const Karya = React.lazy(() => import('./pages/Karya')); @@ -137,13 +139,13 @@ const AppContent = () => { return (
-
+
{/* Efek noise tekstur */} -
- {/* Gradien dekoratif buat estetika zen */} -
-
-
+
+ {/* Gradien dekoratif buat estetika zen - Dioptimalkan blurnya biar enteng */} +
+
+
diff --git a/components/BentoGrid.tsx b/components/BentoGrid.tsx index 4be8de0..ff61a48 100644 --- a/components/BentoGrid.tsx +++ b/components/BentoGrid.tsx @@ -10,11 +10,14 @@ interface CardProps { } // Efek Noise Modern - Biar tekstur gak flat +// Inlined SVG data URI to eliminate external fetch (LCP optimization) +const NOISE_SVG_DATA_URI = `data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E`; + const Noise = () => (
{ alt="Art" loading="lazy" decoding="async" + width="800" + height="600" + style={{ aspectRatio: '800 / 600' }} className="absolute inset-0 w-full h-full object-cover opacity-60 group-hover:opacity-80 group-hover:scale-105 transition-all duration-700" />
diff --git a/components/Hero.tsx b/components/Hero.tsx index 2484bd4..1473c0e 100644 --- a/components/Hero.tsx +++ b/components/Hero.tsx @@ -25,10 +25,10 @@ export const Hero = () => { {/* Judul Utama - Pelacakan Ketat & Kontras Tinggi */} Merangkai diff --git a/components/KaryaCard.tsx b/components/KaryaCard.tsx index a80bba9..39162c2 100644 --- a/components/KaryaCard.tsx +++ b/components/KaryaCard.tsx @@ -70,8 +70,8 @@ export const KaryaCard: React.FC = ({ art, index, onClick, rende className="break-inside-avoid group relative rounded-[1.5rem] md:rounded-[2.5rem] overflow-hidden cursor-pointer bg-[#0a0a0a] shadow-2xl hover:shadow-rose-500/20 transition-all border border-white/5" > {/* Area Konten Utama - Aspek Rasio Standar biar Gridnya gak berantakan */} -
-
+
+
{renderContent(art)}
diff --git a/components/Navbar.tsx b/components/Navbar.tsx index 7a61df5..23fe875 100644 --- a/components/Navbar.tsx +++ b/components/Navbar.tsx @@ -106,7 +106,7 @@ export const Navbar = () => { transition={springTransition as any} onHoverStart={() => setIsHovered(true)} onHoverEnd={() => setIsHovered(false)} - className="bg-[#0a0a0a]/90 backdrop-blur-xl border border-white/10 shadow-[0_8px_40px_-12px_rgba(0,0,0,0.5)] flex flex-col overflow-hidden relative" + className="bg-[#0a0a0a]/90 backdrop-blur-xl border border-white/10 shadow-[0_8px_40px_-12px_rgba(0,0,0,0.5)] flex flex-col overflow-hidden relative will-change-transform" >
{/* Pembungkus Logo & Judul */} @@ -127,7 +127,7 @@ export const Navbar = () => { animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: -10 }} transition={{ duration: motionConfig.durations.fast, ease: "easeOut" }} - className="font-serif font-bold text-lg tracking-tight text-white whitespace-nowrap" + className="font-serif font-bold text-lg tracking-tight text-white whitespace-nowrap will-change-transform" > Our Creativity. diff --git a/index.html b/index.html index f230d5f..677dc86 100644 --- a/index.html +++ b/index.html @@ -1,90 +1,94 @@ - - - - - - -Our Creativity. - - - - - + + + + + + + + Our Creativity. + + + + - - - + + + - - - + + + - -
- - + } + + + + +
diff --git a/pages/Gate.tsx b/pages/Gate.tsx index 44c4b27..dd07c91 100644 --- a/pages/Gate.tsx +++ b/pages/Gate.tsx @@ -25,7 +25,7 @@ const divisions = [ desc: "Wadah bagi para penulis untuk mengekspresikan ide, puisi, dan cerita yang menggugah jiwa.", logo: "/logo-oc-karyatulis.jpg", icon: Type, - link: "https://forms.gle/placeholder-karyatulis", + link: "https://chat.whatsapp.com/CQz2xHzSxGT4YojQ3EWrbE", color: "from-rose-500 to-rose-700", text: "text-rose-400" }, @@ -36,7 +36,7 @@ const divisions = [ desc: "Eksplorasi estetika visual, tipografi, dan komposisi untuk menciptakan karya yang memukau.", logo: "/logo-oc-desain.jpg", icon: PenTool, - link: "https://forms.gle/placeholder-desain", + link: "https://forms.gle/RZJgCeZWgBQF8DDd8", color: "from-purple-500 to-purple-700", text: "text-purple-400" }, @@ -44,10 +44,10 @@ const divisions = [ id: 'video', name: "Video Editing", tagline: "Sinematografi & Momen", - desc: "Menggabungkan potongan momen menjadi narasi visual yang hidup dan bercerita.", + desc: "Grup vid edit. Menggabungkan potongan momen menjadi narasi visual yang hidup dan bercerita.", logo: "/logo-oc-video.jpg", icon: Youtube, - link: "https://forms.gle/placeholder-video", + link: "https://chat.whatsapp.com/CQz2xHzSxGT4YojQ3EWrbE", color: "from-blue-500 to-blue-700", text: "text-blue-400" }, @@ -58,7 +58,7 @@ const divisions = [ desc: "Seni menyebarkan tawa melalui gambar dan konteks yang relavan dengan budaya pop.", logo: "/logo-oc-meme.jpg", icon: Smile, - link: "https://forms.gle/placeholder-meme", + link: "https://discord.com/invite/YrUSt4kjBm", color: "from-yellow-500 to-yellow-700", text: "text-yellow-400" }, @@ -66,10 +66,10 @@ const divisions = [ id: 'coding', name: "Coding", tagline: "Logika Pembangun Masa Depan", - desc: "Menciptakan solusi digital melalui barisan kode dan algoritma yang presisi.", + desc: "Beradaptasi atau Mati. Menciptakan solusi digital melalui barisan kode dan algoritma yang presisi.", logo: "/logo-oc-coding.jpg", icon: Terminal, - link: "https://forms.gle/placeholder-coding", + link: "https://forms.gle/d1SBHkeCWdDfYLGHA", color: "from-emerald-500 to-emerald-700", text: "text-emerald-400" }, @@ -84,7 +84,7 @@ const Gate = () => { const lockBtn = useRef(null); const [isUnlocked, setIsUnlocked] = useState(false); - const [isMaintenance] = useState(true); // Toggle this for maintenance + const [isMaintenance] = useState(false); // Mode pemeliharaan dinonaktifkan const navigate = useNavigate(); useGSAP(() => { @@ -163,6 +163,8 @@ const Gate = () => { return (
+ {/* SEO & FCP Optimization */} +

OurCreativity Portal Divisi

{/* IMMERSIVE BACKGROUND (Revealed behind the gate) */}
@@ -222,7 +224,7 @@ const Gate = () => {
- STATUS: MAINTENANCE + STATUS: PEMELIHARAAN
@@ -291,13 +293,13 @@ const Gate = () => { {/* MAIN CONTENT (Story Revealed) */}
{/* Story Header */}
-

INITIATED_SUCCESSFULLY

+

DIINISIASI_BERHASIL

Selamat Datang di
Portal Divisi diff --git a/pages/divisions/Graphics.tsx b/pages/divisions/Graphics.tsx index 46f786f..787eb8b 100644 --- a/pages/divisions/Graphics.tsx +++ b/pages/divisions/Graphics.tsx @@ -1,18 +1,18 @@ -import React from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import { Link } from 'react-router-dom'; -import { motion } from 'framer-motion'; -import { ArrowLeft, Maximize2, Crosshair, Users, Trophy, Zap, Star, ArrowUpRight } from 'lucide-react'; +import { motion, useMotionValue, useSpring } from 'framer-motion'; +import { ArrowLeft, Maximize2, Crosshair, Users, Trophy, Zap, Star, ArrowUpRight, Pin } from 'lucide-react'; // --- Komponen Marquee Lokal --- const Marquee = ({ text }: { text: string }) => ( -
+
{[...Array(4)].map((_, i) => ( - + {text} ))} @@ -33,200 +33,355 @@ const highlights = [ desc: "OurCreativity 0.7 - Total 80+ Participant", image: "/tagwall-90s-design.jpg", rotate: 6, - margin: "mt-10", - zIndex: "z-10" + x: "15%", + y: "20%", + zIndex: 10 }, { title: "TAGWALL BRUTALISM", desc: "OurCreativity 1.2 - Part 1", image: "/tagwall-brutalism.webp", rotate: -3, - margin: "mt-0", - zIndex: "z-20" + x: "45%", + y: "15%", + zIndex: 20 }, { title: "TAGWALL KEMERDEKAAN", desc: "Lekas Sembuh Indonesiaku. 70+", image: "/tagwall-kemerdekaan.webp", rotate: 3, - margin: "mt-20", - zIndex: "z-10" + x: "10%", + y: "60%", + zIndex: 15 }, { title: "TAGWALL SUPERHERO", desc: "OurCreativity 1.1 - Total 80+ Participant", image: "/tagwall-superhero.webp", rotate: -6, - margin: "mt-5", - zIndex: "z-0" + x: "70%", + y: "55%", + zIndex: 5 } ]; +// --- Sub-komponen Lampu Gantung --- +const HangingLamp = () => { + const mouseX = useMotionValue(0); + const mouseY = useMotionValue(0); + + const springX = useSpring(mouseX, { damping: 25, stiffness: 120 }); + const springY = useSpring(mouseY, { damping: 25, stiffness: 120 }); + + useEffect(() => { + const handleMouseMove = (e: MouseEvent) => { + mouseX.set(e.clientX); + mouseY.set(e.clientY); + }; + window.addEventListener('mousemove', handleMouseMove); + return () => window.removeEventListener('mousemove', handleMouseMove); + }, [mouseX, mouseY]); + + return ( + <> + {/* Cahaya Senter */} + + {/* Cahaya Spotlight Terfokus */} + + + {/* Lampu Visual di Sudut */} +
+ { e.currentTarget.style.display = 'none'; }} /> +
+ + ); +}; + export const Graphics = () => { + const boardRef = useRef(null); + return ( -
+
+ + {/* CUTTING MAT BACKGROUND SYSTEM */} +
+ {/* Warna Dasar Hijau Cutting Mat */} +
+ + {/* Grid Halus */} +
+ + {/* Grid Utama (Setiap 5 kotak) */} +
+ + {/* Markings Penggaris di Tepi */} +
+ {[...Array(10)].map((_, i) => {i * 10})} +
+
+ {[...Array(15)].map((_, i) => {i * 10})} +
+ + {/* Tekstur Material Berpori */} +
+
+ + + {/* Navigasi Mengambang */} -