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: {
|
||||
unoptimized: true,
|
||||
},
|
||||
devIndicators: false,
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
--background: #f9fafb;
|
||||
--foreground: #111827;
|
||||
--background: #000000;
|
||||
--foreground: #e5e5e5;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
|
|
@ -12,31 +12,17 @@
|
|||
--font-mono: var(--font-geist-mono);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #030712;
|
||||
--foreground: #f3f4f6;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background);
|
||||
background: #000000;
|
||||
color: var(--foreground);
|
||||
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 {
|
||||
min-height: 90px;
|
||||
background: repeating-linear-gradient(
|
||||
-45deg,
|
||||
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;
|
||||
background: #050505;
|
||||
border: 1px solid rgba(255, 255, 255, 0.04);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
|
@ -44,9 +30,15 @@ body {
|
|||
}
|
||||
|
||||
.ad-container ins.adsbygoogle::after {
|
||||
content: "Ad Space";
|
||||
font-size: 0.75rem;
|
||||
color: rgba(128, 128, 128, 0.4);
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.05em;
|
||||
content: "Ad";
|
||||
font-size: 0.6rem;
|
||||
color: rgba(255, 255, 255, 0.1);
|
||||
font-weight: 400;
|
||||
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"
|
||||
/>
|
||||
</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 />
|
||||
<main className="flex-1">{children}</main>
|
||||
<Footer />
|
||||
|
|
|
|||
|
|
@ -6,26 +6,40 @@ export default function Home() {
|
|||
const toolsByCategory = getToolsByCategory();
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-4xl px-4 py-8">
|
||||
<h1 className="text-xl font-bold text-gray-900 dark:text-white">Developer Tools</h1>
|
||||
<p className="mb-6 mt-1 text-sm text-gray-400">Free. Private. Browser-based.</p>
|
||||
<div className="flex w-full">
|
||||
{/* Left side ad */}
|
||||
<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]) => (
|
||||
<section key={category} className="mb-6">
|
||||
<h2 className="mb-2 text-xs font-semibold uppercase tracking-wider text-gray-400">
|
||||
{category}
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{categoryTools.map((tool) => (
|
||||
<ToolCard key={tool.slug} tool={tool} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
{Object.entries(toolsByCategory).map(([category, categoryTools]) => (
|
||||
<section key={category} className="mb-6">
|
||||
<h2 className="mb-2 text-xs font-semibold uppercase tracking-wider text-gray-500">
|
||||
{category}
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 gap-px sm:grid-cols-2 lg:grid-cols-3 border border-gray-900">
|
||||
{categoryTools.map((tool) => (
|
||||
<ToolCard key={tool.slug} tool={tool} />
|
||||
))}
|
||||
</div>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
export default function Footer() {
|
||||
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">
|
||||
© {new Date().getFullYear()} DevToolbox — Free, private, browser-based developer tools.
|
||||
<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
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,55 @@
|
|||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { tools } from "@/lib/tools";
|
||||
|
||||
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 (
|
||||
<header className="border-b border-gray-200 dark:border-gray-800">
|
||||
<div className="mx-auto flex max-w-5xl items-center justify-between px-4 py-3">
|
||||
<Link href="/" className="text-sm font-semibold text-gray-900 dark:text-white">
|
||||
DevToolbox
|
||||
</Link>
|
||||
<Link href="/" className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
|
||||
All Tools
|
||||
</Link>
|
||||
<header className="border-b border-gray-900 bg-black">
|
||||
<div className="mx-auto max-w-5xl px-4 py-2.5 relative">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search tools..."
|
||||
value={query}
|
||||
onChange={(e) => {
|
||||
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>
|
||||
</header>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ export default function ToolCard({ tool }: { tool: Tool }) {
|
|||
return (
|
||||
<Link
|
||||
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}
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,15 +8,32 @@ interface ToolLayoutProps {
|
|||
|
||||
export default function ToolLayout({ title, description, children }: ToolLayoutProps) {
|
||||
return (
|
||||
<div className="mx-auto w-full max-w-4xl px-4 py-6">
|
||||
<AdSlot slot="1234567890" format="horizontal" />
|
||||
<div className="flex w-full">
|
||||
{/* 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>
|
||||
<p className="mb-4 text-sm text-gray-400">{description}</p>
|
||||
{/* Main content */}
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue