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
15 changes: 13 additions & 2 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Routes, Route } from "react-router-dom";
import { Analytics } from "@vercel/analytics/react";
import { Navbar } from "./components/layout/Navbar";
import { PageContainer } from "./components/layout/PageContainer";
import { AnimatedBackground } from "./components/ui/AnimatedBackground";
import { AnimatedBackground } from "./components/ui/AnimatedBackground";
import { FeedbackWidget } from "./components/ui/FeedbackWidget";

// pages
Expand All @@ -22,9 +22,20 @@ import { LockPdf } from "./pages/LockPdf/LockPdf";
import { EditPdf } from "./pages/EditPdf/EditPdf";
import { Admin } from "./pages/Admin/Admin";

import { useTheme } from "./hooks/useTheme";

function App() {
useTheme();

return (
<div className="relative flex flex-col min-h-screen bg-black text-white overflow-x-hidden w-full">
<div
className="relative flex flex-col min-h-screen overflow-x-hidden w-full"
style={{
background: "var(--color-background)",
color: "var(--color-primary)",
transition: "background 0.3s, color 0.3s"
}}
>
<AnimatedBackground />
<div className="relative z-10 flex flex-col flex-grow w-full">
<Navbar />
Expand Down
110 changes: 61 additions & 49 deletions src/components/layout/Navbar.jsx

Large diffs are not rendered by default.

29 changes: 10 additions & 19 deletions src/components/ui/AnimatedBackground.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,50 +4,41 @@ import { motion as Motion } from 'framer-motion';
export function AnimatedBackground() {
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });

// Track the mouse moving across the entire window
useEffect(() => {
const handleMouseMove = (e) => {
setMousePosition({ x: e.clientX, y: e.clientY });
};

window.addEventListener("mousemove", handleMouseMove);
return () => window.removeEventListener("mousemove", handleMouseMove);
}, []);

return (
<div className="fixed inset-0 z-0 bg-black overflow-hidden pointer-events-none">

<div className="fixed inset-0 z-0 bg-[var(--color-background)] overflow-hidden pointer-events-none transition-colors duration-300">
{/* 1. The Fading Grid */}
<div
<div
className="absolute inset-0 opacity-[0.1]"
style={{
// Creates a perfect square grid
backgroundImage: `
linear-gradient(to right, #ffffff 1px, transparent 1px),
linear-gradient(to bottom, #ffffff 1px, transparent 1px)
`,
backgroundImage:
'linear-gradient(to right, var(--color-primary) 1px, transparent 1px),' +
'linear-gradient(to bottom, var(--color-primary) 1px, transparent 1px)',
backgroundSize: '40px 40px',
// Fades the grid out at the edges of the screen
maskImage: 'radial-gradient(ellipse 80% 80% at 50% 50%, #000 20%, transparent 100%)',
WebkitMaskImage: 'radial-gradient(ellipse 80% 80% at 50% 50%, #000 20%, transparent 100%)',
}}
/>

{/* 2. The Interactive Mouse Spotlight */}
<Motion.div
className="absolute w-[800px] h-[800px] bg-white/[0.04] rounded-full blur-[100px]"
// The x and y subtract half the width/height (400px) so the cursor is perfectly in the center of the glow
className="absolute w-[800px] h-[800px] bg-[var(--color-primary)] opacity-[0.04] rounded-full blur-[100px]"
animate={{
x: mousePosition.x - 400,
x: mousePosition.x - 400,
y: mousePosition.y - 400,
}}
// "tween" with "easeOut" makes it follow the mouse with a slight, buttery-smooth delay
transition={{ type: "tween", ease: "easeOut", duration: 0.5 }}
/>

{/* 3. Subtle Static Top Glow (anchors the page) */}
<div className="absolute top-[-20%] left-1/2 -translate-x-1/2 w-[80%] h-[400px] bg-zinc-400/[0.03] rounded-[100%] blur-[80px]" />

<div className="absolute top-[-20%] left-1/2 -translate-x-1/2 w-[80%] h-[400px] bg-[var(--color-muted)] opacity-[0.03] rounded-[100%] blur-[80px]" />
</div>
);
}
}
23 changes: 23 additions & 0 deletions src/hooks/useTheme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// src/hooks/useTheme.js
import { useEffect, useState } from "react";

export function useTheme() {
const [theme, setTheme] = useState(() => {
if (typeof window !== "undefined") {
return localStorage.getItem("theme") || "dark";
}
return "dark";
});

useEffect(() => {
document.documentElement.classList.remove(theme === "dark" ? "light" : "dark");
document.documentElement.classList.add(theme);
localStorage.setItem("theme", theme);
}, [theme]);

function toggleTheme() {
setTheme((prev) => (prev === "dark" ? "light" : "dark"));
}

return { theme, toggleTheme };
}
25 changes: 21 additions & 4 deletions src/index.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@import "tailwindcss";

@theme {
/* Theme variables for dark mode (default) */
:root {
--color-background: #000000;
--color-surface: #0a0a0a;
--color-border: rgba(255, 255, 255, 0.1);
Expand All @@ -15,6 +16,22 @@ body {
color: var(--color-primary);
font-family: ui-sans-serif, system-ui, sans-serif;
-webkit-font-smoothing: antialiased;
/* This makes the scrollbar dark on supported browsers */
color-scheme: dark;
}
color-scheme: dark;
}

/* Light mode styles */
.light {
--color-background: #ffffff;
--color-surface: #f4f4f5;
--color-border: rgba(0, 0, 0, 0.1);
--color-border-hover: rgba(0, 0, 0, 0.2);
--color-primary: #18181b;
--color-primary-foreground: #ffffff;
--color-muted: #71717a; /* zinc-500 */
}

.light body {
background-color: var(--color-background);
color: var(--color-primary);
color-scheme: light;
}
4 changes: 2 additions & 2 deletions src/pages/Admin/Admin.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ function LoginScreen({ onAuth }) {
<div className="inline-flex items-center justify-center w-14 h-14 rounded-2xl bg-white text-black mb-4">
<Shield className="w-7 h-7" />
</div>
<h1 className="text-2xl font-black text-white tracking-tight">Admin Panel</h1>
<h1 className="text-2xl font-black text-[var(--color-primary)] tracking-tight">Admin Panel</h1>
<p className="text-zinc-500 text-sm mt-1">QuickPDF feedback dashboard</p>
</div>
<form onSubmit={submit} className="space-y-4">
Expand Down Expand Up @@ -77,7 +77,7 @@ function StatCard({ label, value, sub, color }) {

</div>
<div>
<p className="text-2xl font-black text-white">{value}</p>
<p className="text-2xl font-black text-[var(--color-primary)]">{value}</p>
<p className="text-xs text-zinc-500 mt-0.5">{label}</p>
{sub && <p className="text-xs text-zinc-700 mt-0.5">{sub}</p>}
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Compress/Compress.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export function Compress() {
<div className="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-zinc-900 border border-white/10 text-white mb-4">
<Minimize2 className="w-8 h-8" />
</div>
<h1 className="text-4xl font-extrabold text-white mb-4">Compress PDF</h1>
<h1 className="text-4xl font-extrabold text-[var(--color-primary)] mb-4">Compress PDF</h1>
<p className="text-lg text-zinc-400">
Trade quality for portability. Reduce file size directly in your browser.
{!isPremium && (
Expand Down
2 changes: 1 addition & 1 deletion src/pages/EditPdf/EditPdf.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ export function EditPdf() {
className="inline-flex items-center justify-center w-20 h-20 rounded-3xl bg-white text-black mb-6 shadow-[0_0_50px_rgba(255,255,255,0.15)]">
<FileEdit className="w-10 h-10" />
</Motion.div>
<h1 className="text-5xl font-black text-white mb-4 tracking-tighter uppercase">Edit PDF</h1>
<h1 className="text-5xl font-black text-[var(--color-primary)] mb-4 tracking-tighter uppercase">Edit PDF</h1>
<p className="text-zinc-500 text-lg font-light max-w-md mx-auto">Draw, annotate, highlight — or click existing text to edit it directly in the browser.</p>
</div>
{error && <div className="mb-6 p-4 bg-red-500/10 text-red-400 rounded-2xl border border-red-500/20 text-sm">{error}</div>}
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Grayscale/Grayscale.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export function Grayscale() {
<div className="max-w-3xl mx-auto py-12 px-4 sm:px-6">
<div className="text-center mb-10">
<div className="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-zinc-900 border border-white/10 text-white mb-4"><Contrast className="w-8 h-8" /></div>
<h1 className="text-4xl font-extrabold text-white mb-4">Grayscale PDF</h1>
<h1 className="text-4xl font-extrabold text-[var(--color-primary)] mb-4">Grayscale PDF</h1>
<p className="text-lg text-zinc-400">
Strip colors from your document to save printing ink. Processed locally in your browser.
{!isPremium && <span className="block text-sm text-zinc-600 mt-1">Free tier: files up to {FREE_LIMITS.grayscale.maxFileSizeMb} MB</span>}
Expand Down
6 changes: 3 additions & 3 deletions src/pages/Home/components/Features.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ export function Features() {
>
{features.map((feature, index) => (
<Motion.div key={index} variants={itemVariants} className="flex flex-col items-center md:items-start group">
<div className="w-14 h-14 rounded-2xl bg-[#0a0a0a] border border-white/10 flex items-center justify-center mb-6 group-hover:border-white/30 transition-colors duration-300">
<div className="w-14 h-14 rounded-2xl bg-[var(--color-surface)] border border-[var(--color-border)] flex items-center justify-center mb-6 group-hover:border-[var(--color-border-hover)] transition-colors duration-300">
{feature.icon}
</div>
<h3 className="text-xl font-semibold text-white mb-3">{feature.title}</h3>
<p className="text-base text-zinc-400 leading-relaxed font-light">
<h3 className="text-xl font-semibold text-[var(--color-primary)] mb-3">{feature.title}</h3>
<p className="text-base text-[var(--color-muted)] leading-relaxed font-light">
{feature.description}
</p>
</Motion.div>
Expand Down
22 changes: 17 additions & 5 deletions src/pages/Home/components/Hero.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,30 @@ export function Hero() {
className="relative z-10 space-y-8"
>
<Motion.div variants={item}>
<div className="inline-flex items-center gap-2 px-4 py-2 rounded-full border border-white/10 bg-white/5 text-zinc-300 text-xs font-mono uppercase tracking-widest backdrop-blur-md hover:bg-white/10 transition-colors cursor-default shadow-xl">
<ShieldCheck className="w-4 h-4 text-white" />
<div
className="inline-flex items-center gap-2 px-4 py-2 rounded-full border border-white/10 bg-white/5 text-zinc-300 text-xs font-mono uppercase tracking-widest backdrop-blur-md hover:bg-white/10 transition-colors cursor-default shadow-xl"
style={{ color: 'var(--client-side-processing-color, #d4d4d8)' }}
>
<ShieldCheck className="w-4 h-4" style={{ color: 'inherit' }} />
Client-Side Processing
</div>
<style>{`
.light [style*='--client-side-processing-color'] {
--client-side-processing-color: #000 !important;
}
`}</style>
</Motion.div>

<Motion.h1
variants={item}
className="text-6xl md:text-8xl font-extrabold text-white tracking-tighter leading-[1.1]"
className="text-6xl md:text-8xl font-extrabold tracking-tighter leading-[1.1] flex flex-col items-center justify-center text-center w-full"
>
PDF tools that <br className="hidden md:block" />
respect your <span className="text-transparent bg-clip-text bg-gradient-to-r from-zinc-300 to-zinc-600">privacy.</span>
<span className="dark:text-white text-transparent bg-clip-text bg-gradient-to-r from-zinc-300 to-zinc-600">
PDF tools that
</span>
<span className="dark:text-white text-transparent bg-clip-text bg-gradient-to-r from-zinc-300 to-zinc-600">
respect your privacy.
</span>
</Motion.h1>

<Motion.p
Expand Down
Loading
Loading