ui: sharp AMOLED theme, search bar, side ads
This commit is contained in:
parent
f9f17164cd
commit
f66a105ea1
10 changed files with 134 additions and 62 deletions
9
AGENTS.md
Normal file
9
AGENTS.md
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<!-- BEGIN:nextjs-agent-rules -->
|
||||||
|
|
||||||
|
# This is NOT the Next.js you know
|
||||||
|
|
||||||
|
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.
|
||||||
|
|
||||||
|
This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.
|
||||||
|
|
||||||
|
<!-- END:nextjs-agent-rules -->
|
||||||
1
CLAUDE.md
Normal file
1
CLAUDE.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
@AGENTS.md
|
||||||
|
|
@ -5,6 +5,7 @@ const nextConfig: NextConfig = {
|
||||||
images: {
|
images: {
|
||||||
unoptimized: true,
|
unoptimized: true,
|
||||||
},
|
},
|
||||||
|
devIndicators: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background: #f9fafb;
|
--background: #000000;
|
||||||
--foreground: #111827;
|
--foreground: #e5e5e5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
|
|
@ -12,31 +12,17 @@
|
||||||
--font-mono: var(--font-geist-mono);
|
--font-mono: var(--font-geist-mono);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root {
|
|
||||||
--background: #030712;
|
|
||||||
--foreground: #f3f4f6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: var(--background);
|
background: #000000;
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
font-family: var(--font-sans), Arial, Helvetica, sans-serif;
|
font-family: var(--font-sans), Arial, Helvetica, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* AdSense placeholder styling — remove once real ads are connected */
|
/* AdSense placeholder styling — sharp theme */
|
||||||
.ad-container ins.adsbygoogle {
|
.ad-container ins.adsbygoogle {
|
||||||
min-height: 90px;
|
min-height: 90px;
|
||||||
background: repeating-linear-gradient(
|
background: #050505;
|
||||||
-45deg,
|
border: 1px solid rgba(255, 255, 255, 0.04);
|
||||||
transparent,
|
|
||||||
transparent 10px,
|
|
||||||
rgba(128, 128, 128, 0.03) 10px,
|
|
||||||
rgba(128, 128, 128, 0.03) 20px
|
|
||||||
);
|
|
||||||
border: 1px dashed rgba(128, 128, 128, 0.2);
|
|
||||||
border-radius: 8px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
@ -44,9 +30,15 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.ad-container ins.adsbygoogle::after {
|
.ad-container ins.adsbygoogle::after {
|
||||||
content: "Ad Space";
|
content: "Ad";
|
||||||
font-size: 0.75rem;
|
font-size: 0.6rem;
|
||||||
color: rgba(128, 128, 128, 0.4);
|
color: rgba(255, 255, 255, 0.1);
|
||||||
font-weight: 500;
|
font-weight: 400;
|
||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.1em;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ad-side ins.adsbygoogle {
|
||||||
|
min-height: 600px;
|
||||||
|
min-width: 160px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
/>
|
/>
|
||||||
</head>
|
</head>
|
||||||
<body className="flex min-h-full flex-col bg-background text-foreground">
|
<body className="flex min-h-full flex-col bg-black text-foreground">
|
||||||
<Header />
|
<Header />
|
||||||
<main className="flex-1">{children}</main>
|
<main className="flex-1">{children}</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|
|
||||||
|
|
@ -6,26 +6,40 @@ export default function Home() {
|
||||||
const toolsByCategory = getToolsByCategory();
|
const toolsByCategory = getToolsByCategory();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-4xl px-4 py-8">
|
<div className="flex w-full">
|
||||||
<h1 className="text-xl font-bold text-gray-900 dark:text-white">Developer Tools</h1>
|
{/* Left side ad */}
|
||||||
<p className="mb-6 mt-1 text-sm text-gray-400">Free. Private. Browser-based.</p>
|
<div className="ad-side hidden xl:flex w-[200px] shrink-0 justify-center pt-6 pl-2">
|
||||||
|
<div className="sticky top-16">
|
||||||
|
<AdSlot slot="home-left" format="vertical" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<AdSlot slot="homepage-top" format="horizontal" />
|
{/* Main content */}
|
||||||
|
<div className="flex-1 min-w-0 max-w-4xl mx-auto px-4 py-6">
|
||||||
|
<AdSlot slot="homepage-top" format="horizontal" />
|
||||||
|
|
||||||
{Object.entries(toolsByCategory).map(([category, categoryTools]) => (
|
{Object.entries(toolsByCategory).map(([category, categoryTools]) => (
|
||||||
<section key={category} className="mb-6">
|
<section key={category} className="mb-6">
|
||||||
<h2 className="mb-2 text-xs font-semibold uppercase tracking-wider text-gray-400">
|
<h2 className="mb-2 text-xs font-semibold uppercase tracking-wider text-gray-500">
|
||||||
{category}
|
{category}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-3">
|
<div className="grid grid-cols-1 gap-px sm:grid-cols-2 lg:grid-cols-3 border border-gray-900">
|
||||||
{categoryTools.map((tool) => (
|
{categoryTools.map((tool) => (
|
||||||
<ToolCard key={tool.slug} tool={tool} />
|
<ToolCard key={tool.slug} tool={tool} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<AdSlot slot="homepage-bottom" format="horizontal" />
|
<AdSlot slot="homepage-bottom" format="horizontal" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right side ad */}
|
||||||
|
<div className="ad-side hidden xl:flex w-[200px] shrink-0 justify-center pt-6 pr-2">
|
||||||
|
<div className="sticky top-16">
|
||||||
|
<AdSlot slot="home-right" format="vertical" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
export default function Footer() {
|
export default function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer className="mt-auto border-t border-gray-200 px-4 py-4 text-center text-xs text-gray-400 dark:border-gray-800">
|
<footer className="mt-auto border-t border-gray-900 px-4 py-4 text-center text-xs text-gray-600 bg-black">
|
||||||
© {new Date().getFullYear()} DevToolbox — Free, private, browser-based developer tools.
|
© {new Date().getFullYear()} DevToolbox
|
||||||
</footer>
|
</footer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,55 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { tools } from "@/lib/tools";
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
|
const [query, setQuery] = useState("");
|
||||||
|
const [showResults, setShowResults] = useState(false);
|
||||||
|
|
||||||
|
const filtered = query.trim()
|
||||||
|
? tools.filter(
|
||||||
|
(t) =>
|
||||||
|
t.name.toLowerCase().includes(query.toLowerCase()) ||
|
||||||
|
t.description.toLowerCase().includes(query.toLowerCase())
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="border-b border-gray-200 dark:border-gray-800">
|
<header className="border-b border-gray-900 bg-black">
|
||||||
<div className="mx-auto flex max-w-5xl items-center justify-between px-4 py-3">
|
<div className="mx-auto max-w-5xl px-4 py-2.5 relative">
|
||||||
<Link href="/" className="text-sm font-semibold text-gray-900 dark:text-white">
|
<input
|
||||||
DevToolbox
|
type="text"
|
||||||
</Link>
|
placeholder="Search tools..."
|
||||||
<Link href="/" className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
|
value={query}
|
||||||
All Tools
|
onChange={(e) => {
|
||||||
</Link>
|
setQuery(e.target.value);
|
||||||
|
setShowResults(true);
|
||||||
|
}}
|
||||||
|
onFocus={() => setShowResults(true)}
|
||||||
|
onBlur={() => setTimeout(() => setShowResults(false), 150)}
|
||||||
|
className="w-full border border-gray-800 bg-black px-3 py-2 text-sm text-gray-200 placeholder-gray-600 outline-none focus:border-gray-600 font-mono"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{showResults && filtered.length > 0 && (
|
||||||
|
<div className="absolute left-4 right-4 top-full z-50 mt-0 max-h-64 overflow-y-auto border border-gray-800 border-t-0 bg-black shadow-2xl">
|
||||||
|
{filtered.map((tool) => (
|
||||||
|
<Link
|
||||||
|
key={tool.slug}
|
||||||
|
href={`/tools/${tool.slug}`}
|
||||||
|
className="block border-b border-gray-900 px-3 py-2 text-sm text-gray-300 hover:bg-gray-950"
|
||||||
|
onClick={() => {
|
||||||
|
setQuery("");
|
||||||
|
setShowResults(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tool.icon} {tool.name}
|
||||||
|
<span className="ml-2 text-xs text-gray-600">{tool.description}</span>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@ export default function ToolCard({ tool }: { tool: Tool }) {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
href={`/tools/${tool.slug}`}
|
href={`/tools/${tool.slug}`}
|
||||||
className="block rounded-lg border border-gray-200 px-4 py-3 transition-colors hover:border-gray-400 dark:border-gray-800 dark:hover:border-gray-600"
|
className="block border border-gray-900 px-4 py-3 transition-colors hover:border-gray-600 hover:bg-gray-950 bg-black"
|
||||||
>
|
>
|
||||||
<div className="text-sm font-medium text-gray-900 dark:text-white">
|
<div className="text-sm font-medium text-gray-200">
|
||||||
{tool.icon} {tool.name}
|
{tool.icon} {tool.name}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-0.5 text-xs text-gray-400">{tool.description}</div>
|
<div className="mt-0.5 text-xs text-gray-600">{tool.description}</div>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,32 @@ interface ToolLayoutProps {
|
||||||
|
|
||||||
export default function ToolLayout({ title, description, children }: ToolLayoutProps) {
|
export default function ToolLayout({ title, description, children }: ToolLayoutProps) {
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-4xl px-4 py-6">
|
<div className="flex w-full">
|
||||||
<AdSlot slot="1234567890" format="horizontal" />
|
{/* Left side ad — flush to edge */}
|
||||||
|
<div className="ad-side hidden xl:flex w-[200px] shrink-0 justify-center pt-6 pl-2">
|
||||||
|
<div className="sticky top-16">
|
||||||
|
<AdSlot slot="left-sidebar" format="vertical" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h1 className="mb-1 text-lg font-semibold text-gray-900 dark:text-white">{title}</h1>
|
{/* Main content */}
|
||||||
<p className="mb-4 text-sm text-gray-400">{description}</p>
|
<div className="flex-1 min-w-0 max-w-4xl mx-auto px-4 py-6">
|
||||||
|
<AdSlot slot="1234567890" format="horizontal" />
|
||||||
|
|
||||||
{children}
|
<h1 className="mb-1 text-lg font-semibold text-white">{title}</h1>
|
||||||
|
<p className="mb-4 text-sm text-gray-500">{description}</p>
|
||||||
|
|
||||||
<AdSlot slot="0987654321" format="horizontal" className="mt-6" />
|
{children}
|
||||||
|
|
||||||
|
<AdSlot slot="0987654321" format="horizontal" className="mt-6" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right side ad — flush to edge */}
|
||||||
|
<div className="ad-side hidden xl:flex w-[200px] shrink-0 justify-center pt-6 pr-2">
|
||||||
|
<div className="sticky top-16">
|
||||||
|
<AdSlot slot="right-sidebar" format="vertical" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue