Primeira versão templates tecnologia

This commit is contained in:
Marcio Bevervanso 2026-05-13 19:24:26 -03:00
commit 14dc6d0fca
85 changed files with 26670 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

14
Template-01/README.md Normal file
View file

@ -0,0 +1,14 @@
# Nexus Tech Blog
Very professional static blog.
## Setup
Install dependencies:
`npm install`
## Technologies
- React 18
- Vite
- Tailwind CSS
- Motion (framer-motion)
- Lucide React
- React Helmet Async (SEO)

13
Template-01/index.html Normal file
View file

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><rect width=%22100%22 height=%22100%22 rx=%2220%22 fill=%22%230070F3%22/><text y=%22.9em%22 font-size=%2280%22 x=%2250%%22 text-anchor=%22middle%22 fill=%22white%22 font-family=%22monospace%22 font-weight=%22bold%22>N</text></svg>" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View file

@ -0,0 +1,6 @@
{
"name": "Nexus Tech Blog",
"description": "A premium, multi-language static technology blog optimized for performance and SEO.",
"requestFramePermissions": [],
"majorCapabilities": []
}

5633
Template-01/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

39
Template-01/package.json Normal file
View file

@ -0,0 +1,39 @@
{
"name": "react-example",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --port=3000 --host=0.0.0.0",
"build": "vite build",
"preview": "vite preview",
"clean": "rm -rf dist",
"lint": "tsc --noEmit"
},
"dependencies": {
"@google/genai": "^1.29.0",
"@tailwindcss/vite": "^4.1.14",
"@vitejs/plugin-react": "^5.0.4",
"clsx": "^2.1.1",
"dotenv": "^17.2.3",
"express": "^4.21.2",
"lucide-react": "^0.546.0",
"motion": "^12.23.24",
"react": "^19.0.1",
"react-dom": "^19.0.1",
"react-helmet-async": "^3.0.0",
"react-markdown": "^10.1.0",
"react-router-dom": "^7.15.0",
"tailwind-merge": "^3.5.0",
"vite": "^6.2.3"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/node": "^22.14.0",
"autoprefixer": "^10.4.21",
"tailwindcss": "^4.1.14",
"tsx": "^4.21.0",
"typescript": "~5.8.2",
"vite": "^6.2.3"
}
}

View file

@ -0,0 +1,4 @@
User-agent: *
Allow: /
Sitemap: https://nexus-blog.tech/sitemap.xml

View file

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<!-- Main Pages -->
<url><loc>https://nexus-blog.tech/pt</loc><priority>1.0</priority></url>
<url><loc>https://nexus-blog.tech/en</loc><priority>1.0</priority></url>
<url><loc>https://nexus-blog.tech/es</loc><priority>1.0</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog</loc><priority>0.8</priority></url>
<url><loc>https://nexus-blog.tech/en/blog</loc><priority>0.8</priority></url>
<url><loc>https://nexus-blog.tech/es/blog</loc><priority>0.8</priority></url>
<!-- Categories -->
<url><loc>https://nexus-blog.tech/pt/blog/category/ai</loc><priority>0.6</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/category/code</loc><priority>0.6</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/category/startups</loc><priority>0.6</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/category/tools</loc><priority>0.6</priority></url>
<!-- PT Posts -->
<url><loc>https://nexus-blog.tech/pt/blog/futuro-ia-generativa-2024</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/typescript-moderno-guia</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/escalando-startups-bootstrap</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/ferramentas-indispensaveis-dev</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/web-components-nativos</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/clean-architecture-js</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/seguranca-ciber-ia</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/trabalho-remoto-fatos</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/design-system-flexivel</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/webgpu-ias-locais</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/precificacao-saas</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/nextjs-14-enterprise</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/etica-algoritmica</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/figma-dev-mode</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/rust-backends</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/growth-hacking-real</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/documentacao-agil</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/medicina-ia</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/testes-frontend</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/nomadismo-dev</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/sustentabilidade-tech</loc><priority>0.7</priority></url>
<!-- EN Posts -->
<url><loc>https://nexus-blog.tech/en/blog/future-generative-ai-2024</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/modern-typescript-guide</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/scaling-startups-bootstrap</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/essential-dev-tools</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/native-web-components</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/clean-architecture-js</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/ai-cybersecurity</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/remote-work-facts</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/flexible-design-system</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/webgpu-local-ai</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/saas-pricing</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/nextjs-14-enterprise</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/algorithmic-ethics</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/figma-dev-mode</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/rust-backends</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/real-growth-hacking</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/agile-documentation</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/medicine-ai</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/frontend-testing</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/dev-nomadism</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/tech-sustainability</loc><priority>0.7</priority></url>
</urlset>

51
Template-01/src/App.tsx Normal file
View file

@ -0,0 +1,51 @@
import React, { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route, Navigate, useParams } from 'react-router-dom';
import { HelmetProvider } from 'react-helmet-async';
import { Layout } from './components/Layout';
import Home from './pages/Home';
import BlogList from './pages/BlogList';
import PostDetail from './pages/PostDetail';
// Wrapper to handle language validation and layout injection
const LangWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const { lang } = useParams<{ lang: string }>();
const validLangs = ['en', 'pt', 'es'];
if (!lang || !validLangs.includes(lang)) {
return <Navigate to="/pt" replace />;
}
return <Layout>{children}</Layout>;
};
const Contact = lazy(() => import('./pages/Contact'));
const Legal = lazy(() => import('./pages/Legal'));
import { ScrollToTop } from './components/ScrollToTop';
export default function App() {
return (
<HelmetProvider>
<BrowserRouter>
<ScrollToTop />
<Suspense fallback={<div className="min-h-screen bg-white flex items-center justify-center font-mono text-[10px] uppercase tracking-widest text-tech-muted animate-pulse">loading_module...</div>}>
<Routes>
<Route path="/" element={<Navigate to="/pt" replace />} />
<Route path="/:lang" element={<LangWrapper><Home /></LangWrapper>} />
<Route path="/:lang/blog" element={<LangWrapper><BlogList /></LangWrapper>} />
<Route path="/:lang/blog/category/:category" element={<LangWrapper><BlogList /></LangWrapper>} />
<Route path="/:lang/blog/:slug" element={<LangWrapper><PostDetail /></LangWrapper>} />
<Route path="/:lang/contact" element={<LangWrapper><Contact /></LangWrapper>} />
<Route path="/:lang/privacy" element={<LangWrapper><Legal /></LangWrapper>} />
<Route path="/:lang/terms" element={<LangWrapper><Legal /></LangWrapper>} />
<Route path="/:lang/ethics" element={<LangWrapper><Legal /></LangWrapper>} />
<Route path="*" element={<Navigate to="/pt" replace />} />
</Routes>
</Suspense>
</BrowserRouter>
</HelmetProvider>
);
}

View file

@ -0,0 +1,186 @@
import React from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { useI18n } from '../hooks/useI18n';
import { Language } from '../types';
import { Globe, Menu, X, Github, Twitter, Linkedin, Terminal, Command } from 'lucide-react';
import { motion, AnimatePresence } from 'motion/react';
import { cn } from '../lib/utils';
export const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const { lang, t, changeLanguage } = useI18n();
const [isMenuOpen, setIsMenuOpen] = React.useState(false);
const [isScrolled, setIsScrolled] = React.useState(false);
React.useEffect(() => {
const handleScroll = () => setIsScrolled(window.scrollY > 10);
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
const languages: { code: Language; label: string }[] = [
{ code: 'pt', label: 'PT' },
{ code: 'en', label: 'EN' },
{ code: 'es', label: 'ES' },
];
return (
<div className="min-h-screen flex flex-col font-sans bg-white">
{/* Engineering Navigation */}
<header
className={cn(
"fixed top-0 left-0 right-0 z-50 transition-all duration-300 border-b",
isScrolled ? "bg-white/95 backdrop-blur-md border-tech-border py-3 shadow-xs" : "bg-transparent border-transparent py-5"
)}
>
<div className="container mx-auto px-6 lg:px-12 flex items-center justify-between">
<Link to={`/${lang}`} className="flex items-center gap-2 group">
<div className="w-8 h-8 rounded bg-tech-text flex items-center justify-center text-white font-mono font-bold text-lg group-hover:bg-tech-primary transition-colors">
N
</div>
<span className="font-bold text-lg tracking-tighter text-tech-text uppercase font-mono">
nexus_
</span>
</Link>
{/* Desktop Nav */}
<nav className="hidden md:flex items-center gap-10">
<div className="flex gap-8">
<Link to={`/${lang}`} className="text-[11px] font-mono font-bold text-tech-muted hover:text-tech-text transition-colors uppercase tracking-widest">{t.nav.home}</Link>
<Link to={`/${lang}/blog/category/ai`} className="text-[11px] font-mono font-bold text-tech-muted hover:text-tech-primary transition-colors uppercase tracking-widest">{t.blog.categories.ai}</Link>
<Link to={`/${lang}/blog/category/code`} className="text-[11px] font-mono font-bold text-tech-muted hover:text-tech-primary transition-colors uppercase tracking-widest">{t.blog.categories.code}</Link>
<Link to={`/${lang}/blog/category/startups`} className="text-[11px] font-mono font-bold text-tech-muted hover:text-tech-primary transition-colors uppercase tracking-widest">{t.blog.categories.startups}</Link>
<Link to={`/${lang}/blog/category/tools`} className="text-[11px] font-mono font-bold text-tech-muted hover:text-tech-primary transition-colors uppercase tracking-widest">{t.blog.categories.tools}</Link>
</div>
<div className="h-4 w-px bg-tech-border" />
<div className="flex items-center gap-1 bg-tech-surface p-1 rounded border border-tech-border">
{languages.map((l) => (
<button
key={l.code}
onClick={() => changeLanguage(l.code)}
className={cn(
"text-[9px] font-mono font-bold px-2 py-1 rounded transition-all",
lang === l.code
? "bg-white text-tech-text shadow-sm ring-1 ring-tech-border"
: "text-tech-muted hover:text-tech-text"
)}
>
{l.label}
</button>
))}
</div>
<button className="bg-tech-text text-white px-5 py-2 font-mono font-bold text-[10px] uppercase tracking-widest shadow-lg hover:bg-slate-800 transition-all active:scale-95 flex items-center gap-2">
<Terminal size={12} /> ./subscribe
</button>
</nav>
<button
className="md:hidden text-slate-900"
onClick={() => setIsMenuOpen(!isMenuOpen)}
>
{isMenuOpen ? <X size={20} /> : <Menu size={20} />}
</button>
</div>
</header>
{/* Mobile Menu */}
<AnimatePresence>
{isMenuOpen && (
<motion.div
initial={{ opacity: 0, x: '100%' }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: '100%' }}
className="fixed inset-0 z-40 bg-white pt-24 px-6 md:hidden"
>
<div className="flex flex-col gap-6">
<Link onClick={() => setIsMenuOpen(false)} to={`/${lang}`} className="text-2xl font-mono font-bold text-slate-900 flex items-center gap-2">
<Command size={20} /> {t.nav.home}
</Link>
<Link onClick={() => setIsMenuOpen(false)} to={`/${lang}/blog`} className="text-2xl font-mono font-bold text-slate-900 flex items-center gap-2">
<Terminal size={20} /> {t.nav.blog}
</Link>
<div className="flex flex-col gap-4 pl-6 border-l border-slate-100">
<Link onClick={() => setIsMenuOpen(false)} to={`/${lang}/blog/category/ai`} className="text-lg font-mono font-medium text-slate-500 hover:text-tech-primary transition-colors uppercase tracking-tight">{t.blog.categories.ai}</Link>
<Link onClick={() => setIsMenuOpen(false)} to={`/${lang}/blog/category/code`} className="text-lg font-mono font-medium text-slate-500 hover:text-tech-primary transition-colors uppercase tracking-tight">{t.blog.categories.code}</Link>
<Link onClick={() => setIsMenuOpen(false)} to={`/${lang}/blog/category/startups`} className="text-lg font-mono font-medium text-slate-500 hover:text-tech-primary transition-colors uppercase tracking-tight">{t.blog.categories.startups}</Link>
<Link onClick={() => setIsMenuOpen(false)} to={`/${lang}/blog/category/tools`} className="text-lg font-mono font-medium text-slate-500 hover:text-tech-primary transition-colors uppercase tracking-tight">{t.blog.categories.tools}</Link>
</div>
<div className="h-px w-full bg-slate-100 my-4" />
<div className="flex flex-wrap gap-2">
{languages.map((l) => (
<button
key={l.code}
onClick={() => { changeLanguage(l.code); setIsMenuOpen(false); }}
className={cn(
"text-xs font-mono font-bold px-4 py-2 rounded border",
lang === l.code ? "bg-slate-900 text-white border-slate-900" : "text-slate-500 border-slate-200"
)}
>
{l.label}
</button>
))}
</div>
</div>
</motion.div>
)}
</AnimatePresence>
<main className="flex-grow">
{children}
</main>
{/* Structured Technical Footer */}
<footer className="bg-white border-t border-tech-border pt-20 pb-12">
<div className="container mx-auto px-6 lg:px-12">
<div className="grid grid-cols-1 md:grid-cols-4 gap-12 mb-20">
<div className="col-span-1 md:col-span-2">
<Link to={`/${lang}`} className="flex items-center gap-2 mb-6 group">
<div className="w-8 h-8 rounded bg-tech-text flex items-center justify-center text-white font-mono font-bold text-lg group-hover:bg-tech-primary transition-colors">N</div>
<span className="font-bold text-xl tracking-tighter text-tech-text uppercase font-mono">nexus_labs</span>
</Link>
<p className="text-tech-muted max-w-sm text-sm font-medium leading-relaxed mb-8">
{t.footer.about}
</p>
<div className="flex gap-3">
<a href="#" className="w-8 h-8 rounded bg-tech-surface border border-tech-border flex items-center justify-center text-tech-muted hover:text-tech-text hover:border-tech-text transition-all"><Twitter size={14} /></a>
<a href="#" className="w-8 h-8 rounded bg-tech-surface border border-tech-border flex items-center justify-center text-tech-muted hover:text-tech-text hover:border-tech-text transition-all"><Github size={14} /></a>
<a href="#" className="w-8 h-8 rounded bg-tech-surface border border-tech-border flex items-center justify-center text-tech-muted hover:text-tech-text hover:border-tech-text transition-all"><Linkedin size={14} /></a>
</div>
</div>
<div>
<h4 className="font-mono text-[10px] font-bold text-tech-muted mb-6 uppercase tracking-widest">./nodes</h4>
<ul className="flex flex-col gap-3 text-tech-text text-sm font-bold uppercase tracking-tight">
<li><Link to={`/${lang}/blog/category/ai`} className="hover:text-tech-primary transition-colors">AI_Core</Link></li>
<li><Link to={`/${lang}/blog/category/code`} className="hover:text-tech-primary transition-colors">Dev_Stack</Link></li>
<li><Link to={`/${lang}/blog/category/startups`} className="hover:text-tech-primary transition-colors">Bus_Logic</Link></li>
<li><Link to={`/${lang}/blog/category/tools`} className="hover:text-tech-primary transition-colors">Lib_Tools</Link></li>
</ul>
</div>
<div>
<h4 className="font-mono text-[10px] font-bold text-tech-muted mb-6 uppercase tracking-widest">./connect</h4>
<ul className="flex flex-col gap-3 text-tech-text text-sm font-bold uppercase tracking-tight">
<li><a href="#" className="hover:text-tech-primary transition-colors">{t.footer.newsletter}</a></li>
<li><a href="#" className="hover:text-tech-primary transition-colors">API_RSS</a></li>
<li><Link to={`/${lang}/contact`} className="hover:text-tech-primary transition-colors">{t.footer.contact}</Link></li>
<li><Link to={`/${lang}/ethics`} className="hover:text-tech-primary transition-colors">Legal_Notice</Link></li>
</ul>
</div>
</div>
<div className="flex flex-col md:flex-row items-center justify-between gap-6 border-t border-tech-border pt-10 text-tech-muted font-mono text-[10px] font-bold uppercase tracking-widest">
<p>{t.footer.rights}</p>
<div className="flex gap-8">
<Link to={`/${lang}/terms`} className="hover:text-tech-text transition-colors">Terms_01</Link>
<Link to={`/${lang}/ethics`} className="hover:text-tech-text transition-colors">Ethics_02</Link>
<Link to={`/${lang}/privacy`} className="hover:text-tech-text transition-colors">Privacy_03</Link>
</div>
</div>
</div>
</footer>
</div>
);
};

View file

@ -0,0 +1,72 @@
import React from 'react';
import { useI18n } from '../hooks/useI18n';
import { Send, Sparkles } from 'lucide-react';
export const Newsletter = () => {
const { t } = useI18n();
return (
<section className="py-24 relative overflow-hidden bg-tech-surface border-t border-tech-border">
<div className="container mx-auto px-6 lg:px-12 relative z-10">
<div className="max-w-5xl mx-auto bg-tech-text rounded-lg overflow-hidden shadow-2xl border border-slate-800">
<div className="flex items-center gap-2 px-4 py-3 bg-slate-900 border-b border-slate-800">
<div className="flex gap-1.5">
<div className="w-3 h-3 rounded-full bg-slate-700" />
<div className="w-3 h-3 rounded-full bg-slate-700" />
<div className="w-3 h-3 rounded-full bg-slate-700" />
</div>
<div className="flex-grow text-center">
<span className="text-[10px] font-mono font-bold text-slate-500 uppercase tracking-widest">nexus_newsletter.sh</span>
</div>
</div>
<div className="flex flex-col md:flex-row">
<div className="flex-1 p-8 lg:p-12 border-b md:border-b-0 md:border-r border-slate-800">
<div className="inline-flex items-center gap-2 px-2 py-0.5 bg-tech-primary/10 rounded text-tech-primary text-[10px] font-mono font-bold mb-6 border border-tech-primary/20">
<Sparkles size={12} />
<span>TECHNICAL_FEED</span>
</div>
<h2 className="text-3xl font-bold mb-4 text-white tracking-tight">
Nexus Intelligence Report
</h2>
<p className="text-slate-400 text-sm leading-relaxed font-mono">
&gt; Receba semanalmente análises profundas sobre LLMs, Infraestrutura e Engenharia de Software.
</p>
</div>
<div className="flex-1 p-8 lg:p-12 flex flex-col justify-center bg-tech-text">
<form
className="w-full flex flex-col gap-4"
onSubmit={(e) => e.preventDefault()}
>
<div className="space-y-1">
<div className="flex justify-between items-center text-[10px] font-mono font-bold text-slate-500">
<span>INPUT_EMAIL</span>
<span className="text-tech-primary">STABLE</span>
</div>
<input
id="emailAddress"
type="email"
placeholder="user@nexus_blog.tech"
className="w-full bg-slate-900 border border-slate-800 rounded px-4 py-3 text-slate-200 focus:outline-hidden focus:border-tech-primary transition-all font-mono text-sm"
required
/>
</div>
<button
className="w-full bg-white hover:bg-slate-200 text-tech-text px-8 py-3 font-mono font-bold text-xs uppercase tracking-widest rounded transition-all shadow-md active:scale-[0.98] mt-2 flex items-center justify-center gap-2"
>
./subscribe.sh <Send size={14} />
</button>
<div className="flex items-center gap-4 mt-4">
<div className="flex-grow h-px bg-slate-800" />
<span className="text-[10px] font-mono text-slate-600">NULL_SPAM_POLICY</span>
<div className="flex-grow h-px bg-slate-800" />
</div>
</form>
</div>
</div>
</div>
</div>
</section>
);
};

View file

@ -0,0 +1,67 @@
import React from 'react';
import { Post } from '../types';
import { Link } from 'react-router-dom';
import { useI18n } from '../hooks/useI18n';
import { formatDate } from '../lib/utils';
import { ArrowRight, Clock } from 'lucide-react';
import { motion } from 'motion/react';
interface PostCardProps {
post: Post;
}
export const PostCard: React.FC<PostCardProps> = ({ post }) => {
const { lang } = useI18n();
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="group h-full"
>
<Link to={`/${lang}/blog/${post.slug}`} className="tech-card group">
<div className="relative aspect-video overflow-hidden bg-white border-b border-tech-border">
<img
src={post.image}
alt={post.title}
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105 opacity-90 group-hover:opacity-100"
loading="lazy"
referrerPolicy="no-referrer"
/>
<div className="absolute top-3 left-3">
<span className="category-tag">
{post.category}
</span>
</div>
</div>
<div className="p-5 flex flex-col flex-grow">
<div className="flex items-center gap-3 mb-3 font-mono text-[10px] font-bold text-tech-muted uppercase tracking-tighter">
<span>{formatDate(post.date, lang)}</span>
<span className="text-tech-border">/</span>
<span className="flex items-center gap-1"><Clock size={10} /> {post.readingTime}</span>
</div>
<h3 className="text-lg font-bold mb-2 text-tech-text group-hover:text-tech-primary transition-colors leading-tight">
{post.title}
</h3>
<p className="text-tech-muted text-sm line-clamp-2 leading-relaxed mb-6 font-medium">
{post.description}
</p>
<div className="mt-auto flex items-center justify-between border-t border-tech-surface pt-4">
<div className="flex items-center gap-2">
<div className="w-6 h-6 rounded bg-tech-surface flex items-center justify-center text-[10px] font-mono font-bold text-tech-muted border border-tech-border">
{post.author[0]}
</div>
<span className="text-[10px] text-tech-muted font-mono font-bold uppercase">{post.author}</span>
</div>
<ArrowRight size={14} className="text-tech-border group-hover:text-tech-primary transition-all" />
</div>
</div>
</Link>
</motion.div>
);
};

View file

@ -0,0 +1,127 @@
import React from 'react';
import { Helmet } from 'react-helmet-async';
import { useLocation } from 'react-router-dom';
interface SEOProps {
title: string;
description: string;
image?: string;
article?: boolean;
author?: string;
datePublished?: string;
category?: string;
}
export const SEO: React.FC<SEOProps> = ({
title,
description,
image,
article,
author = 'Nexus Tech Team',
datePublished,
category
}) => {
const siteName = 'Nexus Intelligence';
const fullTitle = `${title} | ${siteName}`;
const location = useLocation();
const canonicalUrl = `https://nexus-blog.tech${location.pathname}`;
const defaultImage = 'https://images.unsplash.com/photo-1677442136019-21780ecad995?q=80&w=2000';
const ogImage = image || defaultImage;
const structuredData = article ? {
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": title,
"description": description,
"image": ogImage,
"author": {
"@type": "Person",
"name": author
},
"datePublished": datePublished,
"mainEntityOfPage": {
"@type": "WebPage",
"@id": canonicalUrl
},
"publisher": {
"@type": "Organization",
"name": siteName,
"logo": {
"@type": "ImageObject",
"url": "https://nexus-blog.tech/logo.png"
}
}
} : {
"@context": "https://schema.org",
"@type": "WebSite",
"name": siteName,
"url": "https://nexus-blog.tech",
"description": description
};
const breadcrumbData = article ? {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": `https://nexus-blog.tech/${location.pathname.split('/')[1]}`
},
{
"@type": "ListItem",
"position": 2,
"name": "Blog",
"item": `https://nexus-blog.tech/${location.pathname.split('/')[1]}/blog`
},
{
"@type": "ListItem",
"position": 3,
"name": title,
"item": canonicalUrl
}
]
} : null;
return (
<Helmet
htmlAttributes={{
lang: location.pathname.split('/')[1] || 'pt'
}}
>
{/* Basic Meta Tags */}
<title>{fullTitle}</title>
<meta name="description" content={description} />
<link rel="canonical" href={canonicalUrl} />
{/* Open Graph / Facebook */}
<meta property="og:site_name" content={siteName} />
<meta property="og:url" content={canonicalUrl} />
<meta property="og:title" content={fullTitle} />
<meta property="og:description" content={description} />
<meta property="og:type" content={article ? 'article' : 'website'} />
<meta property="og:image" content={ogImage} />
{article && category && <meta property="article:section" content={category} />}
{article && datePublished && <meta property="article:published_time" content={datePublished} />}
{/* Twitter */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:domain" content="nexus-blog.tech" />
<meta name="twitter:url" content={canonicalUrl} />
<meta name="twitter:title" content={fullTitle} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={ogImage} />
{/* Google Rich Results */}
<script type="application/ld+json">
{JSON.stringify(structuredData)}
</script>
{breadcrumbData && (
<script type="application/ld+json">
{JSON.stringify(breadcrumbData)}
</script>
)}
</Helmet>
);
};

View file

@ -0,0 +1,12 @@
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
export const ScrollToTop = () => {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
};

View file

@ -0,0 +1,33 @@
import React from 'react';
import { useI18n } from '../hooks/useI18n';
import { Hash } from 'lucide-react';
interface TOCProps {
content: string;
}
export const TableOfContents: React.FC<TOCProps> = ({ content }) => {
const { t } = useI18n();
const headings = content.split('\n').filter(line => line.startsWith('## ')).map(line => line.replace('## ', '').trim());
if (headings.length === 0) return null;
return (
<div className="hidden lg:block bg-white p-6 rounded-3xl border border-slate-200/60 shadow-sm min-w-[240px]">
<h4 className="font-bold text-xs uppercase tracking-widest text-slate-900 mb-6 flex items-center gap-2">
<Hash size={14} className="text-accent-blue" /> {t.blog.toc}
</h4>
<nav className="flex flex-col gap-3">
{headings.map((heading) => (
<a
key={heading}
href={`#${heading.toLowerCase().replace(/ /g, '-')}`}
className="text-sm font-medium text-slate-500 hover:text-accent-blue transition-all border-l-2 border-slate-100 pl-4 hover:border-accent-blue py-1"
>
{heading}
</a>
))}
</nav>
</div>
);
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,26 @@
import { useLocation, useNavigate, useParams } from 'react-router-dom';
import { content } from '../content';
import { Language } from '../types';
export function useI18n() {
const { lang } = useParams<{ lang?: string }>();
const location = useLocation();
const navigate = useNavigate();
const currentLang: Language = (lang as Language) || 'pt';
const t = content.ui[currentLang];
const posts = content.posts[currentLang];
const changeLanguage = (newLang: Language) => {
const pathParts = location.pathname.split('/');
pathParts[1] = newLang; // Replace the language segment
navigate(pathParts.join('/'));
};
return {
lang: currentLang,
t,
posts,
changeLanguage,
};
}

107
Template-01/src/index.css Normal file
View file

@ -0,0 +1,107 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap');
@import "tailwindcss";
@theme {
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
--color-tech-primary: #0070F3;
--color-tech-secondary: #0ea5e9;
--color-tech-accent: #06b6d4;
--color-tech-surface: #f8fafc;
--color-tech-border: #e2e8f0;
--color-tech-text: #0f172a;
--color-tech-muted: #64748b;
--color-tech-glow: rgba(0, 112, 243, 0.15);
--animate-float: float 6s ease-in-out infinite;
--animate-pulse-slow: pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite;
--animate-fade-in: fadeIn 0.4s ease-out forwards;
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
}
@layer base {
body {
@apply bg-white text-tech-text selection:bg-tech-primary/20 selection:text-tech-primary antialiased overflow-x-hidden;
background-image: radial-gradient(var(--color-tech-border) 1px, transparent 1px);
background-size: 24px 24px;
}
h1, h2, h3, h4, h5, h6 {
@apply font-sans tracking-tight text-slate-950 font-bold;
}
code {
@apply font-mono bg-slate-100 px-1.5 py-0.5 rounded text-[0.9em] text-tech-primary;
}
}
@layer components {
.glass-card {
@apply bg-white/70 backdrop-blur-xl border border-tech-border shadow-sm transition-all duration-300;
}
.tech-card {
@apply bg-white border border-tech-border rounded-lg transition-all duration-200 hover:border-tech-muted hover:shadow-lg hover:shadow-tech-glow flex flex-col h-full;
}
.gradient-text {
@apply bg-clip-text text-transparent bg-linear-to-r from-tech-text to-tech-muted;
}
.blob {
@apply absolute rounded-full filter blur-[100px] opacity-20 pointer-events-none bg-tech-primary;
}
.markdown-body {
@apply text-tech-muted leading-relaxed text-base font-sans;
}
.markdown-body h2 {
@apply text-2xl font-bold mt-12 mb-6 text-tech-text tracking-tight border-b border-tech-border pb-4;
}
.markdown-body h3 {
@apply text-xl font-bold mt-8 mb-4 text-tech-text tracking-tight;
}
.markdown-body p {
@apply mb-6;
}
.markdown-body pre {
@apply bg-slate-900 text-slate-100 p-6 rounded-xl overflow-x-auto my-8 font-mono text-sm shadow-xl;
}
.markdown-body blockquote {
@apply border-l-2 border-tech-text pl-6 py-2 my-8 italic text-tech-muted bg-tech-surface;
}
.markdown-body ul {
@apply list-disc list-inside mb-6 space-y-2;
}
.markdown-body ol {
@apply list-decimal list-inside mb-6 space-y-2;
}
.markdown-body li {
@apply font-medium;
}
.category-tag {
@apply inline-flex items-center px-2 py-0.5 rounded bg-tech-surface border border-tech-border text-[10px] font-mono font-bold text-tech-muted uppercase tracking-wider;
}
.btn-tech {
@apply inline-flex items-center justify-center px-4 py-2 bg-tech-text text-white rounded font-mono text-xs font-bold uppercase tracking-widest transition-all hover:bg-slate-800 active:scale-95 shadow-sm;
}
}

View file

@ -0,0 +1,14 @@
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
export function formatDate(dateString: string, lang: string) {
return new Date(dateString).toLocaleDateString(lang, {
year: 'numeric',
month: 'long',
day: 'numeric',
});
}

10
Template-01/src/main.tsx Normal file
View file

@ -0,0 +1,10 @@
import {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import App from './App.tsx';
import './index.css';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
);

View file

@ -0,0 +1,87 @@
import React from 'react';
import { useParams, Link } from 'react-router-dom';
import { useI18n } from '../hooks/useI18n';
import { PostCard } from '../components/PostCard';
import { SEO } from '../components/SEO';
import { cn } from '../lib/utils';
import { motion } from 'motion/react';
export default function BlogList() {
const { category } = useParams<{ category?: string }>();
const { lang, posts, t } = useI18n();
const validPosts = posts || [];
const categories = ['all', 'ai', 'code', 'startups', 'tools'];
const filteredPosts = category && category !== 'all'
? validPosts.filter(p => p.category === category)
: validPosts;
const activeCategory = category || 'all';
return (
<>
<SEO
title={t.nav.blog}
description="Explora nuestros artículos sobre tecnología, programación e inteligencia artificial."
/>
<section className="py-24 lg:py-32 container mx-auto px-6 lg:px-12 bg-white min-h-screen">
<header className="mb-20 max-w-4xl">
<div className="inline-flex items-center gap-2 text-tech-primary text-[10px] font-mono font-bold uppercase tracking-[0.2em] mb-4">
<div className="w-8 h-px bg-tech-primary" />
<span>BLOG_ARCHIVE</span>
</div>
<motion.h1
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="text-4xl md:text-6xl font-extrabold mb-8 text-tech-text tracking-tighter"
>
{t.nav.blog}
</motion.h1>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.1 }}
className="flex flex-wrap gap-2"
>
{categories.map((cat) => (
<Link
key={cat}
to={cat === 'all' ? `/${lang}/blog` : `/${lang}/blog/category/${cat}`}
className={cn(
"px-4 py-1.5 rounded font-mono text-[10px] font-bold uppercase tracking-widest transition-all border",
activeCategory === cat
? "bg-tech-text text-white border-tech-text shadow-md"
: "bg-white border-tech-border text-tech-muted hover:border-tech-muted hover:text-tech-text hover:bg-tech-surface"
)}
>
{t.blog.categories[cat as keyof typeof t.blog.categories]}
</Link>
))}
</motion.div>
</header>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-0 border-t border-l border-tech-border">
{filteredPosts.map((post, idx) => (
<motion.div
key={post.id}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: idx * 0.02 }}
className="border-r border-b border-tech-border p-1"
>
<PostCard post={post} />
</motion.div>
))}
</div>
{filteredPosts.length === 0 && (
<div className="text-center py-20 text-slate-400 font-mono text-xs font-bold uppercase tracking-widest border border-dashed border-slate-200 rounded-lg mt-10">
[error] no_modules_found_in_category
</div>
)}
</section>
</>
);
}

View file

@ -0,0 +1,132 @@
import React from 'react';
import { useI18n } from '../hooks/useI18n';
import { SEO } from '../components/SEO';
import { motion } from 'motion/react';
import { ArrowLeft, Send, Terminal, Mail, MapPin, Phone } from 'lucide-react';
import { Link } from 'react-router-dom';
export default function Contact() {
const { t, lang } = useI18n();
return (
<div className="bg-white min-h-screen pt-32 pb-24">
<SEO title={t.contact.title} description={t.contact.subtitle} />
<div className="container mx-auto px-6 lg:px-12">
<header className="max-w-4xl mb-20">
<Link to={`/${lang}`} className="inline-flex items-center gap-2 text-tech-muted text-[10px] font-mono font-bold mb-8 hover:text-tech-text transition-colors uppercase tracking-widest">
<ArrowLeft size={12} /> cd ..
</Link>
<div className="inline-flex items-center gap-2 text-tech-primary text-[10px] font-mono font-bold uppercase tracking-[0.2em] mb-4">
<div className="w-8 h-px bg-tech-primary" />
<span>COMMUNICATION_LINK</span>
</div>
<motion.h1
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="text-5xl md:text-7xl font-extrabold mb-8 text-tech-text tracking-tighter"
>
{t.contact.title}
</motion.h1>
<p className="text-xl text-tech-muted font-medium max-w-2xl leading-relaxed">
{t.contact.subtitle}
</p>
</header>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-20">
{/* Contact Form */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
className="bg-tech-surface border border-tech-border rounded-lg p-8 lg:p-12"
>
<form className="space-y-6" onSubmit={(e) => e.preventDefault()}>
<div className="space-y-1">
<label className="text-[10px] font-mono font-bold text-tech-muted uppercase">{t.contact.form.name}</label>
<input
type="text"
className="w-full bg-white border border-tech-border rounded px-4 py-3 focus:outline-hidden focus:border-tech-primary transition-all font-mono text-sm"
placeholder="Ex: Alex Nexus"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-mono font-bold text-tech-muted uppercase">{t.contact.form.email}</label>
<input
type="email"
className="w-full bg-white border border-tech-border rounded px-4 py-3 focus:outline-hidden focus:border-tech-primary transition-all font-mono text-sm"
placeholder="alex@nexus-blog.tech"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-mono font-bold text-tech-muted uppercase">{t.contact.form.subject}</label>
<input
type="text"
className="w-full bg-white border border-tech-border rounded px-4 py-3 focus:outline-hidden focus:border-tech-primary transition-all font-mono text-sm"
placeholder="Support / Inquiry / Collaboration"
/>
</div>
<div className="space-y-1">
<label className="text-[10px] font-mono font-bold text-tech-muted uppercase">{t.contact.form.message}</label>
<textarea
rows={5}
className="w-full bg-white border border-tech-border rounded px-4 py-3 focus:outline-hidden focus:border-tech-primary transition-all font-mono text-sm resize-none"
placeholder="> Type your message here..."
></textarea>
</div>
<button className="btn-tech !w-full !py-4">
{t.contact.form.submit} <Send size={14} className="ml-2" />
</button>
</form>
</motion.div>
{/* Info Side */}
<div className="space-y-12">
<div>
<h3 className="text-[10px] font-mono font-bold text-tech-muted uppercase tracking-widest mb-6 border-b border-tech-border pb-2">./{t.contact.info.title.toLowerCase().replace(' ', '_')}</h3>
<ul className="space-y-6">
<li className="flex items-start gap-4">
<div className="w-10 h-10 rounded bg-tech-surface border border-tech-border flex items-center justify-center text-tech-primary">
<Mail size={18} />
</div>
<div>
<span className="block text-[10px] font-mono font-bold text-tech-muted uppercase">Email</span>
<span className="font-bold text-tech-text">{t.contact.info.email}</span>
</div>
</li>
<li className="flex items-start gap-4">
<div className="w-10 h-10 rounded bg-tech-surface border border-tech-border flex items-center justify-center text-tech-primary">
<MapPin size={18} />
</div>
<div>
<span className="block text-[10px] font-mono font-bold text-tech-muted uppercase">Location</span>
<span className="font-bold text-tech-text">{t.contact.info.location}</span>
</div>
</li>
</ul>
</div>
<div className="p-6 bg-tech-text text-white rounded-lg border border-slate-800">
<div className="flex items-center gap-2 mb-4">
<Terminal size={16} className="text-tech-primary" />
<span className="font-mono text-[10px] font-bold uppercase tracking-widest">{t.contact.status.title.toLowerCase().replace(' ', '_')}</span>
</div>
<div className="text-xs font-mono text-slate-400 mb-4 space-y-1">
<div>{t.contact.status.nodes}: 12</div>
<div>{t.contact.status.uptime}: 99.998%</div>
<div>{t.contact.status.latency}: 45ms</div>
</div>
<div className="h-1 w-full bg-slate-900 rounded-full overflow-hidden">
<motion.div
initial={{ width: 0 }}
animate={{ width: '85%' }}
transition={{ duration: 2, repeat: Infinity }}
className="h-full bg-tech-primary"
/>
</div>
</div>
</div>
</div>
</div>
</div>
);
}

View file

@ -0,0 +1,140 @@
import React from 'react';
import { useI18n } from '../hooks/useI18n';
import { PostCard } from '../components/PostCard';
import { Newsletter } from '../components/Newsletter';
import { SEO } from '../components/SEO';
import { motion } from 'motion/react';
import { ArrowRight, Sparkles, Terminal, Cpu, Database, Code2 } from 'lucide-react';
import { Link } from 'react-router-dom';
import { formatDate } from '../lib/utils';
export default function Home() {
const { t, posts, lang } = useI18n();
const validPosts = posts || [];
const recentPosts = validPosts.slice(0, 6);
return (
<div className="relative">
<SEO title={t.home.hero.title} description={t.home.hero.subtitle} />
{/* Technical Lab Hero Section */}
<section className="pt-32 pb-20 relative border-b border-slate-100 bg-white">
<div className="container mx-auto px-6 lg:px-12 relative z-10">
<div className="max-w-5xl">
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
className="inline-flex items-center gap-2 px-2 py-1 bg-tech-surface border border-tech-border rounded text-tech-muted text-[10px] font-mono font-bold mb-8 uppercase tracking-widest"
>
<div className="w-2 h-2 rounded-full bg-tech-primary animate-pulse" />
<span>nexus_core_system v4.02 // stable</span>
</motion.div>
<motion.h1
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="text-6xl md:text-8xl font-extrabold mb-8 text-slate-950 tracking-tighter leading-[0.9] lg:max-w-4xl"
>
{t.home.hero.title.replace('Nexus Tech Blog', 'NEXUS_ENGINEERING')}
</motion.h1>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.1 }}
className="flex flex-col md:flex-row gap-8 items-start mb-12"
>
<p className="text-xl text-slate-500 font-medium max-w-2xl leading-relaxed">
{t.home.hero.subtitle}
</p>
<div className="hidden lg:grid grid-cols-2 gap-4 flex-shrink-0">
<div className="flex items-center gap-2 text-[10px] font-mono font-bold text-tech-muted border border-tech-border p-2 rounded">
<Cpu size={14} className="text-tech-primary" />
<span>CPU_LOAD: 12%</span>
</div>
<div className="flex items-center gap-2 text-[10px] font-mono font-bold text-tech-muted border border-tech-border p-2 rounded">
<Database size={14} className="text-tech-accent" />
<span>MODELS: GPT-O1</span>
</div>
</div>
</motion.div>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.2 }}
className="flex flex-wrap gap-4"
>
<Link to={`/${lang}/blog`} className="btn-tech !px-8 !py-4 bg-slate-900 text-white !text-sm">
Executar Exploração <ArrowRight size={16} className="ml-2" />
</Link>
<div className="flex items-center gap-3 px-6 py-4 bg-white border border-slate-200 rounded font-mono text-xs font-bold text-slate-500">
<Terminal size={14} className="text-slate-400" />
<span>LAST_DEPLOY: {new Date().toISOString().split('T')[0]}</span>
</div>
</motion.div>
</div>
</div>
</section>
{/* Grid Lab - Featured Articles */}
<section className="py-24 bg-white relative">
<div className="container mx-auto px-6 lg:px-12">
<div className="flex items-center justify-between mb-16">
<div>
<div className="flex items-center gap-2 text-tech-primary text-[10px] font-mono font-bold uppercase tracking-[0.2em] mb-4">
<div className="w-8 h-px bg-tech-primary" />
<span>FETCHED_MODULES</span>
</div>
<h2 className="text-4xl font-bold tracking-tight text-tech-text">{t.home.recent}</h2>
</div>
<Link to={`/${lang}/blog`} className="hidden md:flex items-center gap-2 text-xs font-mono font-bold text-tech-muted hover:text-tech-text transition-all border border-tech-border px-4 py-2 rounded">
LIST_ALL.SH <ArrowRight size={14} />
</Link>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-0 border-t border-l border-tech-border">
{recentPosts.map((post) => (
<div key={post.id} className="border-r border-b border-tech-border p-1 group">
<PostCard post={post} />
</div>
))}
</div>
</div>
</section>
{/* Categories Tech Selector */}
<section className="py-24 bg-slate-50 border-y border-slate-200 overflow-hidden">
<div className="container mx-auto px-6 lg:px-12 lg:flex items-center gap-20">
<div className="max-w-sm mb-12 lg:mb-0">
<h3 className="text-2xl font-bold mb-4 tracking-tight">Filtrar por Domínio</h3>
<p className="text-slate-500 font-medium text-sm leading-relaxed">
Explore tópicos específicos isolando as frequências de conhecimento.
</p>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 flex-grow">
{[
{ id: 'ai', icon: Sparkles, label: 'Inteligência Artificial' },
{ id: 'code', icon: Code2, label: 'Engenharia de Software' },
{ id: 'startups', icon: Cpu, label: 'Product & Startup' },
{ id: 'tools', icon: Terminal, label: 'Technical Tools' }
].map(cat => (
<Link
key={cat.id}
to={`/${lang}/blog/category/${cat.id}`}
className="p-6 bg-white border border-tech-border rounded hover:border-tech-primary hover:shadow-lg transition-all group"
>
<cat.icon size={24} className="mb-4 text-tech-muted group-hover:text-tech-primary transition-colors" />
<span className="block font-mono text-[10px] font-bold text-tech-muted uppercase group-hover:text-tech-text">{cat.id}</span>
<span className="block font-bold text-tech-text text-sm mt-1">{cat.label}</span>
</Link>
))}
</div>
</div>
</section>
<Newsletter />
</div>
);
}

View file

@ -0,0 +1,65 @@
import React from 'react';
import { useI18n } from '../hooks/useI18n';
import { SEO } from '../components/SEO';
import { motion } from 'motion/react';
import { ArrowLeft, Shield, FileText, Scale } from 'lucide-react';
import { Link, useLocation } from 'react-router-dom';
export default function Legal() {
const { t, lang } = useI18n();
const location = useLocation();
const isPrivacy = location.pathname.includes('privacy');
const isTerms = location.pathname.includes('terms');
const isEthics = location.pathname.includes('ethics');
let config = t.legal.privacy;
let Icon = Shield;
let code = "PRIVACY_PROTOCOL";
if (isTerms) {
config = t.legal.terms;
Icon = FileText;
code = "SERVICE_TERMS";
} else if (isEthics) {
config = t.legal.ethics;
Icon = Scale;
code = "ETHICS_V4.0";
}
return (
<div className="bg-white min-h-screen pt-32 pb-24">
<SEO title={config.title} description={config.content} />
<div className="container mx-auto px-6 lg:px-12">
<header className="max-w-4xl mb-20">
<Link to={`/${lang}`} className="inline-flex items-center gap-2 text-tech-muted text-[10px] font-mono font-bold mb-8 hover:text-tech-text transition-colors uppercase tracking-widest">
<ArrowLeft size={12} /> cd ..
</Link>
<div className="inline-flex items-center gap-2 text-tech-primary text-[10px] font-mono font-bold uppercase tracking-[0.2em] mb-4">
<Icon size={14} />
<span>{code}</span>
</div>
<motion.h1
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="text-5xl md:text-7xl font-extrabold mb-8 text-tech-text tracking-tighter"
>
{config.title}
</motion.h1>
</header>
<article className="max-w-3xl prose prose-slate prose-invert prose-tech">
<div className="bg-tech-surface border border-tech-border rounded p-8 lg:p-12 font-medium text-tech-muted leading-loose whitespace-pre-line">
{config.content}
<div className="mt-12 pt-8 border-t border-tech-border text-[10px] font-mono uppercase">
Last_Modified: {new Date().toISOString().split('T')[0]}<br/>
Version: 4.2.0-STABLE
</div>
</div>
</article>
</div>
</div>
);
}

View file

@ -0,0 +1,199 @@
import React from 'react';
import { useParams, Link } from 'react-router-dom';
import { useI18n } from '../hooks/useI18n';
import { SEO } from '../components/SEO';
import { TableOfContents } from '../components/TableOfContents';
import { Newsletter } from '../components/Newsletter';
import { PostCard } from '../components/PostCard';
import { formatDate } from '../lib/utils';
import { ArrowLeft, Clock, Twitter, Linkedin, Link as LinkIcon, Cpu, Terminal } from 'lucide-react';
import { motion } from 'motion/react';
import Markdown from 'react-markdown';
export default function PostDetail() {
const { slug } = useParams<{ slug: string }>();
const { lang, posts, t } = useI18n();
const validPosts = posts || [];
const post = validPosts.find(p => p.slug === slug);
const relatedPosts = validPosts.filter(p => p.slug !== slug && (p.category === post?.category || p.featured)).slice(0, 3);
if (!post) {
return (
<div className="container mx-auto px-6 py-40 text-center font-mono uppercase tracking-widest text-tech-muted">
<h1 className="text-4xl font-bold mb-8 text-tech-text">[error] 404_NOT_FOUND</h1>
<Link to={`/${lang}/blog`} className="text-tech-primary inline-flex items-center gap-2 font-bold bg-tech-surface px-6 py-2 rounded">
<ArrowLeft size={16} /> RETURN_TO_BASE
</Link>
</div>
);
}
return (
<div className="bg-white min-h-screen">
<SEO
title={post.title}
description={post.description}
image={post.image}
article
author={post.author}
datePublished={post.date}
category={post.category}
/>
{/* Technical Header */}
<section className="pt-32 pb-16 relative bg-white border-b border-slate-100">
<div className="container mx-auto px-6 lg:px-12 relative z-10">
<motion.div
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
>
<Link to={`/${lang}/blog`} className="inline-flex items-center gap-2 text-tech-muted text-[10px] font-mono font-bold mb-8 hover:text-tech-text transition-colors uppercase tracking-widest">
<ArrowLeft size={12} /> cd ../blog
</Link>
<div className="flex items-center gap-3 mb-8">
<span className="category-tag">
{post.category}
</span>
<div className="w-1 h-1 rounded-full bg-tech-border" />
<span className="text-tech-muted text-[10px] uppercase font-mono font-bold tracking-widest flex items-center gap-1">
<Clock size={10} /> READ_TIME: {post.readingTime}
</span>
</div>
<h1 className="text-4xl md:text-5xl lg:text-7xl font-extrabold text-tech-text leading-[1] tracking-tighter mb-8 max-w-5xl">
{post.title}
</h1>
<p className="text-lg md:text-xl text-tech-muted mb-12 max-w-3xl leading-relaxed font-medium">
{post.description}
</p>
<div className="flex flex-col md:flex-row items-center gap-6 pt-8 border-t border-tech-border">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded bg-tech-surface flex items-center justify-center font-mono font-bold text-tech-muted border border-tech-border">
{post.author[0]}
</div>
<div className="text-left leading-tight">
<span className="font-bold text-tech-text block text-sm">{post.author}</span>
<span className="text-tech-muted text-[10px] font-mono font-bold uppercase">system_editor</span>
</div>
</div>
<div className="hidden md:block h-8 w-px bg-tech-border" />
<div className="text-left leading-tight">
<span className="text-tech-muted text-[10px] font-mono font-bold uppercase block">timestamp</span>
<span className="font-bold text-tech-text text-sm">{formatDate(post.date, lang)}</span>
</div>
</div>
</motion.div>
</div>
</section>
{/* Featured Technical Image */}
<section className="container mx-auto px-6 lg:px-12 py-12">
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.1 }}
className="aspect-video lg:aspect-[21/7] rounded-lg overflow-hidden border border-tech-border bg-tech-surface relative"
>
<img src={post.image} className="w-full h-full object-cover opacity-90 transition-opacity hover:opacity-100" alt={post.title} referrerPolicy="no-referrer" />
<div className="absolute bottom-4 right-4 bg-tech-text/80 backdrop-blur-md px-3 py-1 rounded text-[10px] font-mono text-white border border-slate-700 uppercase">IMG_REF: {post.slug}.jpg</div>
</motion.div>
</section>
{/* Content Section */}
<section className="container mx-auto px-6 lg:px-12 pb-32">
<div className="grid grid-cols-1 lg:grid-cols-[1fr_300px] gap-16">
{/* Main Article body */}
<article className="markdown-body w-full max-w-3xl border-r border-slate-50 pr-0 lg:pr-16">
<Markdown>{post.content}</Markdown>
</article>
{/* Sidebar */}
<aside className="space-y-12">
<div className="sticky top-32">
<TableOfContents content={post.content} />
<div className="mt-8 p-6 bg-tech-surface rounded border border-tech-border">
<h4 className="text-[10px] font-mono font-bold uppercase tracking-widest text-tech-muted mb-4">./bio_metadata</h4>
<div className="flex items-center gap-3 mb-4">
<div className="w-10 h-10 rounded bg-white flex items-center justify-center font-bold text-tech-muted border border-tech-border">
{post.author[0]}
</div>
<div>
<span className="font-bold text-tech-text block text-sm">{post.author}</span>
<span className="text-tech-primary text-[10px] font-mono font-bold uppercase">@alex_nexus</span>
</div>
</div>
<p className="text-xs text-tech-muted leading-relaxed font-medium">
Technical architect with 10+ years optimizing distributed systems and multimodal AI agents.
</p>
</div>
<div className="mt-8 flex flex-col gap-2">
<div className="text-[10px] font-mono font-bold text-tech-muted uppercase tracking-widest mb-2">Compartilhar_Modulo</div>
<div className="flex gap-2">
<a
href={`https://twitter.com/intent/tweet?text=${encodeURIComponent(post.title)}&url=${encodeURIComponent(window.location.href)}`}
target="_blank"
rel="noopener noreferrer"
className="w-8 h-8 rounded bg-white border border-tech-border flex items-center justify-center text-tech-muted hover:text-tech-primary hover:border-tech-primary transition-all cursor-pointer"
>
<Twitter size={14} />
</a>
<a
href={`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(window.location.href)}`}
target="_blank"
rel="noopener noreferrer"
className="w-8 h-8 rounded bg-white border border-tech-border flex items-center justify-center text-tech-muted hover:text-tech-primary hover:border-tech-primary transition-all cursor-pointer"
>
<Linkedin size={14} />
</a>
<button
onClick={() => {
navigator.clipboard.writeText(window.location.href);
alert('Link copiado!');
}}
className="w-8 h-8 rounded bg-white border border-tech-border flex items-center justify-center text-tech-muted hover:text-tech-text hover:border-tech-text transition-all cursor-pointer"
>
<LinkIcon size={14} />
</button>
</div>
</div>
</div>
</aside>
</div>
</section>
{/* Related Technical Modules */}
{relatedPosts.length > 0 && (
<section className="py-24 bg-tech-surface border-t border-tech-border">
<div className="container mx-auto px-6 lg:px-12">
<div className="flex items-center justify-between mb-12">
<div>
<div className="flex items-center gap-2 text-tech-primary text-[10px] font-mono font-bold uppercase tracking-widest mb-4">
<div className="w-8 h-px bg-tech-primary" />
<span>REL_MODULES</span>
</div>
<h2 className="text-3xl font-bold tracking-tight text-tech-text">Módulos Relacionados</h2>
</div>
<Link to={`/${lang}/blog`} className="text-[10px] font-mono font-bold text-tech-muted hover:text-tech-text transition-colors uppercase tracking-widest border border-tech-border px-4 py-2 rounded">view_all_entries</Link>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-0 border-t border-l border-tech-border">
{relatedPosts.map(post => (
<div key={post.id} className="border-r border-b border-tech-border p-1 bg-white">
<PostCard post={post} />
</div>
))}
</div>
</div>
</section>
)}
<Newsletter />
</div>
);
}

97
Template-01/src/types.ts Normal file
View file

@ -0,0 +1,97 @@
export type Language = 'pt' | 'en' | 'es';
export interface Post {
id: string;
slug: string;
title: string;
description: string;
content: string;
category: string;
author: string;
date: string;
readingTime: string;
image: string;
featured?: boolean;
}
export interface Translations {
nav: {
home: string;
blog: string;
about: string;
categories: string;
};
home: {
hero: {
title: string;
subtitle: string;
cta: string;
};
featured: string;
recent: string;
newsletter: {
title: string;
desc: string;
placeholder: string;
button: string;
};
};
blog: {
categories: {
all: string;
ai: string;
code: string;
startups: string;
tools: string;
};
readMore: string;
related: string;
back: string;
toc: string;
};
footer: {
about: string;
connect: string;
rights: string;
terms: string;
ethics: string;
newsletter: string;
rss: string;
contact: string;
privacy: string;
};
legal: {
privacy: { title: string; content: string; };
terms: { title: string; content: string; };
ethics: { title: string; content: string; };
};
contact: {
title: string;
subtitle: string;
info: {
title: string;
email: string;
location: string;
};
status: {
title: string;
nodes: string;
uptime: string;
latency: string;
};
form: {
name: string;
email: string;
subject: string;
message: string;
submit: string;
sending: string;
success: string;
};
};
}
export interface SiteContent {
posts: Record<Language, Post[]>;
ui: Record<Language, Translations>;
}

26
Template-01/tsconfig.json Normal file
View file

@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "ES2022",
"experimentalDecorators": true,
"useDefineForClassFields": false,
"module": "ESNext",
"lib": [
"ES2022",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true,
"moduleResolution": "bundler",
"isolatedModules": true,
"moduleDetection": "force",
"allowJs": true,
"jsx": "react-jsx",
"paths": {
"@/*": [
"./*"
]
},
"allowImportingTsExtensions": true,
"noEmit": true
}
}

View file

@ -0,0 +1,24 @@
import tailwindcss from '@tailwindcss/vite';
import react from '@vitejs/plugin-react';
import path from 'path';
import {defineConfig, loadEnv} from 'vite';
export default defineConfig(({mode}) => {
const env = loadEnv(mode, '.', '');
return {
plugins: [react(), tailwindcss()],
define: {
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY),
},
resolve: {
alias: {
'@': path.resolve(__dirname, '.'),
},
},
server: {
// HMR is disabled in AI Studio via DISABLE_HMR env var.
// Do not modify—file watching is disabled to prevent flickering during agent edits.
hmr: process.env.DISABLE_HMR !== 'true',
},
};
});

14
Template-02/README.md Normal file
View file

@ -0,0 +1,14 @@
# Nexus Tech Blog
Very professional static blog.
## Setup
Install dependencies:
`npm install`
## Technologies
- React 18
- Vite
- Tailwind CSS
- Motion (framer-motion)
- Lucide React
- React Helmet Async (SEO)

13
Template-02/index.html Normal file
View file

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><rect width=%22100%22 height=%22100%22 rx=%2220%22 fill=%22%230070F3%22/><text y=%22.9em%22 font-size=%2280%22 x=%2250%%22 text-anchor=%22middle%22 fill=%22white%22 font-family=%22monospace%22 font-weight=%22bold%22>N</text></svg>" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View file

@ -0,0 +1,6 @@
{
"name": "Lumix Tech Blog",
"description": "A cutting-edge, future-focused technology blog with neo-futuristic design and bento layouts.",
"requestFramePermissions": [],
"majorCapabilities": []
}

5633
Template-02/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

39
Template-02/package.json Normal file
View file

@ -0,0 +1,39 @@
{
"name": "react-example",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --port=3000 --host=0.0.0.0",
"build": "vite build",
"preview": "vite preview",
"clean": "rm -rf dist",
"lint": "tsc --noEmit"
},
"dependencies": {
"@google/genai": "^1.29.0",
"@tailwindcss/vite": "^4.1.14",
"@vitejs/plugin-react": "^5.0.4",
"clsx": "^2.1.1",
"dotenv": "^17.2.3",
"express": "^4.21.2",
"lucide-react": "^0.546.0",
"motion": "^12.23.24",
"react": "^19.0.1",
"react-dom": "^19.0.1",
"react-helmet-async": "^3.0.0",
"react-markdown": "^10.1.0",
"react-router-dom": "^7.15.0",
"tailwind-merge": "^3.5.0",
"vite": "^6.2.3"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/node": "^22.14.0",
"autoprefixer": "^10.4.21",
"tailwindcss": "^4.1.14",
"tsx": "^4.21.0",
"typescript": "~5.8.2",
"vite": "^6.2.3"
}
}

View file

@ -0,0 +1,4 @@
User-agent: *
Allow: /
Sitemap: https://nexus-blog.tech/sitemap.xml

View file

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<!-- Main Pages -->
<url><loc>https://nexus-blog.tech/pt</loc><priority>1.0</priority></url>
<url><loc>https://nexus-blog.tech/en</loc><priority>1.0</priority></url>
<url><loc>https://nexus-blog.tech/es</loc><priority>1.0</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog</loc><priority>0.8</priority></url>
<url><loc>https://nexus-blog.tech/en/blog</loc><priority>0.8</priority></url>
<url><loc>https://nexus-blog.tech/es/blog</loc><priority>0.8</priority></url>
<!-- Categories -->
<url><loc>https://nexus-blog.tech/pt/blog/category/ai</loc><priority>0.6</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/category/code</loc><priority>0.6</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/category/startups</loc><priority>0.6</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/category/tools</loc><priority>0.6</priority></url>
<!-- PT Posts -->
<url><loc>https://nexus-blog.tech/pt/blog/futuro-ia-generativa-2024</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/typescript-moderno-guia</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/escalando-startups-bootstrap</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/ferramentas-indispensaveis-dev</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/web-components-nativos</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/clean-architecture-js</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/seguranca-ciber-ia</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/trabalho-remoto-fatos</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/design-system-flexivel</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/webgpu-ias-locais</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/precificacao-saas</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/nextjs-14-enterprise</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/etica-algoritmica</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/figma-dev-mode</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/rust-backends</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/growth-hacking-real</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/documentacao-agil</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/medicina-ia</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/testes-frontend</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/nomadismo-dev</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/sustentabilidade-tech</loc><priority>0.7</priority></url>
<!-- EN Posts -->
<url><loc>https://nexus-blog.tech/en/blog/future-generative-ai-2024</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/modern-typescript-guide</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/scaling-startups-bootstrap</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/essential-dev-tools</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/native-web-components</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/clean-architecture-js</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/ai-cybersecurity</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/remote-work-facts</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/flexible-design-system</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/webgpu-local-ai</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/saas-pricing</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/nextjs-14-enterprise</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/algorithmic-ethics</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/figma-dev-mode</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/rust-backends</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/real-growth-hacking</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/agile-documentation</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/medicine-ai</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/frontend-testing</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/dev-nomadism</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/tech-sustainability</loc><priority>0.7</priority></url>
</urlset>

51
Template-02/src/App.tsx Normal file
View file

@ -0,0 +1,51 @@
import React, { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route, Navigate, useParams } from 'react-router-dom';
import { HelmetProvider } from 'react-helmet-async';
import { Layout } from './components/Layout';
import Home from './pages/Home';
import BlogList from './pages/BlogList';
import PostDetail from './pages/PostDetail';
// Wrapper to handle language validation and layout injection
const LangWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const { lang } = useParams<{ lang: string }>();
const validLangs = ['en', 'pt', 'es'];
if (!lang || !validLangs.includes(lang)) {
return <Navigate to="/pt" replace />;
}
return <Layout>{children}</Layout>;
};
const Contact = lazy(() => import('./pages/Contact'));
const Legal = lazy(() => import('./pages/Legal'));
import { ScrollToTop } from './components/ScrollToTop';
export default function App() {
return (
<HelmetProvider>
<BrowserRouter>
<ScrollToTop />
<Suspense fallback={<div className="min-h-screen bg-black flex items-center justify-center font-mono text-[10px] font-black uppercase tracking-[0.5em] text-tech-primary animate-pulse italic">VANTA_KERN_INIT...</div>}>
<Routes>
<Route path="/" element={<Navigate to="/pt" replace />} />
<Route path="/:lang" element={<LangWrapper><Home /></LangWrapper>} />
<Route path="/:lang/blog" element={<LangWrapper><BlogList /></LangWrapper>} />
<Route path="/:lang/blog/category/:category" element={<LangWrapper><BlogList /></LangWrapper>} />
<Route path="/:lang/blog/:slug" element={<LangWrapper><PostDetail /></LangWrapper>} />
<Route path="/:lang/contact" element={<LangWrapper><Contact /></LangWrapper>} />
<Route path="/:lang/privacy" element={<LangWrapper><Legal /></LangWrapper>} />
<Route path="/:lang/terms" element={<LangWrapper><Legal /></LangWrapper>} />
<Route path="/:lang/ethics" element={<LangWrapper><Legal /></LangWrapper>} />
<Route path="*" element={<Navigate to="/pt" replace />} />
</Routes>
</Suspense>
</BrowserRouter>
</HelmetProvider>
);
}

View file

@ -0,0 +1,210 @@
import React from 'react';
import { Link, useLocation, useNavigate, useParams } from 'react-router-dom';
import { useI18n } from '../hooks/useI18n';
import { Language } from '../types';
import { Menu, X, Github, Twitter, Linkedin, Terminal, Search, User, Globe } from 'lucide-react';
import { motion, AnimatePresence } from 'motion/react';
import { cn } from '../lib/utils';
export const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const { lang, t, changeLanguage } = useI18n();
const navigate = useNavigate();
const location = useLocation();
const [isMenuOpen, setIsMenuOpen] = React.useState(false);
const [isScrolled, setIsScrolled] = React.useState(false);
React.useEffect(() => {
// If no language in URL, redirect to default PT
if (location.pathname === '/' || location.pathname === '') {
navigate('/pt', { replace: true });
}
}, [location.pathname, navigate]);
React.useEffect(() => {
const handleScroll = () => setIsScrolled(window.scrollY > 20);
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
const languages: { code: Language; label: string }[] = [
{ code: 'pt', label: 'PT' },
{ code: 'en', label: 'EN' },
{ code: 'es', label: 'ES' },
];
return (
<div className="min-h-screen flex flex-col font-sans">
<div className="h-1 w-full bg-tech-primary fixed top-0 left-0 z-[60]" />
{/* Top Meta Bar */}
<div className="hidden md:block bg-black border-b border-white/5 py-1.5 text-[9px] font-bold text-tech-muted uppercase tracking-[0.2em]">
<div className="container mx-auto px-6 lg:px-12 flex justify-between items-center">
<div className="flex gap-6">
<span>Terminal: <span className="text-tech-primary">Active</span></span>
<span>Region: <span className="text-tech-primary">Global</span></span>
</div>
<div className="flex gap-4">
<span className="flex items-center gap-1 cursor-pointer hover:text-white transition-colors"><Search size={10} /> Search_Database</span>
<span className="flex items-center gap-1 cursor-pointer hover:text-white transition-colors"><User size={10} /> Node_Access</span>
</div>
</div>
</div>
{/* Magazine Header */}
<header
className={cn(
"sticky top-0 z-50 transition-all duration-300 border-b",
isScrolled
? "bg-black/98 backdrop-blur-md border-tech-primary/20 py-1.5"
: "bg-black border-white/5 py-3"
)}
>
<div className="container mx-auto px-6 lg:px-12 flex items-center justify-between">
<Link to={`/${lang}`} className="flex items-center gap-2 group">
<div className="w-7 h-7 bg-tech-primary flex items-center justify-center text-black font-black text-lg italic skew-x-[-10deg]">
L
</div>
<span className="font-black text-lg tracking-tighter text-white uppercase italic">
Lumix<span className="text-tech-primary">.</span>Tech
</span>
</Link>
<nav className="hidden lg:flex items-center gap-1">
<Link to={`/${lang}`} className="px-3 py-1 text-[10px] font-black text-white hover:text-tech-primary transition-colors uppercase tracking-[0.2em] italic">{t.nav.home}</Link>
<div className="w-px h-3 bg-white/10 mx-2" />
<Link to={`/${lang}/blog/category/ai`} className="px-3 py-1 text-[10px] font-black text-white hover:text-tech-primary transition-colors uppercase tracking-[0.2em] italic">{t.blog.categories.ai}</Link>
<Link to={`/${lang}/blog/category/code`} className="px-3 py-1 text-[10px] font-black text-white hover:text-tech-primary transition-colors uppercase tracking-[0.2em] italic">{t.blog.categories.code}</Link>
<Link to={`/${lang}/blog/category/tools`} className="px-3 py-1 text-[10px] font-black text-white hover:text-tech-primary transition-colors uppercase tracking-[0.2em] italic">{t.blog.categories.tools}</Link>
</nav>
<div className="flex items-center gap-4">
<div className="hidden sm:flex items-center gap-1 bg-white/5 p-0.5 rounded-xs border border-white/5">
{languages.map((l) => (
<button
key={l.code}
onClick={() => changeLanguage(l.code)}
className={cn(
"text-[9px] font-black uppercase px-2 py-0.5 transition-all outline-hidden rounded-xs",
lang === l.code
? "bg-tech-primary text-black"
: "text-tech-muted hover:text-white"
)}
>
{l.code}
</button>
))}
</div>
<button className="lg:hidden text-white/60 hover:text-white transition-colors" onClick={() => setIsMenuOpen(!isMenuOpen)}>
{isMenuOpen ? <X size={20} /> : <Menu size={20} />}
</button>
</div>
</div>
</header>
<AnimatePresence>
{isMenuOpen && (
<motion.div
initial={{ opacity: 0, y: '-100%' }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: '-100%' }}
transition={{ type: 'spring', damping: 25, stiffness: 200 }}
className="fixed inset-0 z-40 bg-black/98 backdrop-blur-xl pt-32 px-6 lg:hidden"
>
<div className="flex flex-col h-full justify-between pb-12">
<nav className="flex flex-col gap-6">
<Link onClick={() => setIsMenuOpen(false)} to={`/${lang}`} className="text-3xl font-black text-white uppercase italic tracking-tighter hover:text-tech-primary transition-colors">
{t.nav.home}
</Link>
<div className="w-12 h-1 bg-tech-primary" />
<Link onClick={() => setIsMenuOpen(false)} to={`/${lang}/blog/category/ai`} className="text-2xl font-black text-white/50 hover:text-tech-primary uppercase italic tracking-tighter transition-colors">{t.blog.categories.ai}</Link>
<Link onClick={() => setIsMenuOpen(false)} to={`/${lang}/blog/category/code`} className="text-2xl font-black text-white/50 hover:text-tech-primary uppercase italic tracking-tighter transition-colors">{t.blog.categories.code}</Link>
<Link onClick={() => setIsMenuOpen(false)} to={`/${lang}/blog/category/tools`} className="text-2xl font-black text-white/50 hover:text-tech-primary uppercase italic tracking-tighter transition-colors">{t.blog.categories.tools}</Link>
</nav>
<div className="space-y-8">
<div className="flex items-center gap-4 py-4 border-y border-white/5">
<span className="text-[10px] font-black text-tech-muted uppercase tracking-[0.2em]">Select_Language:</span>
{languages.map((l) => (
<button
key={`mobile-${l.code}`}
onClick={() => { changeLanguage(l.code); setIsMenuOpen(false); }}
className={cn(
"text-xs font-black uppercase tracking-widest",
lang === l.code ? "text-tech-primary" : "text-white/40"
)}
>
{l.code.toUpperCase()}
</button>
))}
</div>
<div className="flex gap-6">
<Twitter size={20} className="text-white/40 hover:text-tech-primary transition-colors" />
<Github size={20} className="text-white/40 hover:text-tech-primary transition-colors" />
<Linkedin size={20} className="text-white/40 hover:text-tech-primary transition-colors" />
</div>
</div>
</div>
<button className="absolute top-6 right-6 text-white/60 hover:text-white" onClick={() => setIsMenuOpen(false)}>
<X size={24} />
</button>
</motion.div>
)}
</AnimatePresence>
<main className="flex-grow">
{children}
</main>
<footer className="bg-black border-t border-white/10 py-16 md:py-24 mt-20">
<div className="container mx-auto px-6 lg:px-12">
<div className="grid grid-cols-1 md:grid-cols-12 gap-12 mb-16">
<div className="md:col-span-5">
<Link to={`/${lang}`} className="flex items-center gap-2 mb-6 group">
<div className="w-8 h-8 bg-tech-primary flex items-center justify-center text-black font-black text-xl italic skew-x-[-10deg]">
L
</div>
<span className="font-black text-xl tracking-tighter text-white uppercase italic">
Lumix Tech
</span>
</Link>
<p className="text-tech-muted leading-relaxed text-xs max-w-sm uppercase font-bold tracking-tight opacity-70">
{t.footer.about}
</p>
</div>
<div className="md:col-span-7 grid grid-cols-2 lg:grid-cols-3 gap-8 md:gap-12">
<div className="space-y-4">
<h4 className="text-white font-black uppercase tracking-[0.2em] text-[10px] italic">Protocol_Modules</h4>
<ul className="space-y-2 text-[10px] text-tech-muted font-bold uppercase tracking-widest">
<li><Link to={`/${lang}/blog`} className="hover:text-tech-primary transition-colors">Intelligence_Archive</Link></li>
<li><Link to={`/${lang}/blog/category/ai`} className="hover:text-tech-primary transition-colors">Neural_Core</Link></li>
<li><Link to={`/${lang}/blog/category/code`} className="hover:text-tech-primary transition-colors">System_Logic</Link></li>
</ul>
</div>
<div className="space-y-4">
<h4 className="text-white font-black uppercase tracking-[0.2em] text-[10px] italic">Security_Clearance</h4>
<ul className="space-y-2 text-[10px] text-tech-muted font-bold uppercase tracking-widest">
<li><Link to={`/${lang}/privacy`} className="hover:text-tech-primary transition-colors">Protocol_Privacy</Link></li>
<li><Link to={`/${lang}/ethics`} className="hover:text-tech-primary transition-colors">Neural_Ethics</Link></li>
</ul>
</div>
<div className="space-y-4 col-span-2 lg:col-span-1">
<h4 className="text-white font-black uppercase tracking-[0.2em] text-[10px] italic">External_Links</h4>
<div className="flex gap-4">
<a href="#" className="w-8 h-8 bg-white/5 border border-white/5 flex items-center justify-center text-white/60 hover:text-tech-primary hover:border-tech-primary transition-all"><Twitter size={14} /></a>
<a href="#" className="w-8 h-8 bg-white/5 border border-white/5 flex items-center justify-center text-white/60 hover:text-tech-primary hover:border-tech-primary transition-all"><Github size={14} /></a>
<a href="#" className="w-8 h-8 bg-white/5 border border-white/5 flex items-center justify-center text-white/60 hover:text-tech-primary hover:border-tech-primary transition-all"><Linkedin size={14} /></a>
</div>
</div>
</div>
</div>
<div className="pt-10 border-t border-white/5 flex flex-col md:flex-row justify-between items-center gap-6 text-[9px] font-black uppercase tracking-[0.3em] text-tech-muted">
<p>{t.footer.rights}</p>
<div className="flex gap-6">
<span>EST_2026 // NODE_LUMIX_v1.0</span>
</div>
</div>
</div>
</footer>
</div>
);
};

View file

@ -0,0 +1,57 @@
import React from 'react';
import { useI18n } from '../hooks/useI18n';
import { Send, Sparkles } from 'lucide-react';
export const Newsletter = () => {
const { t } = useI18n();
return (
<section className="py-20 md:py-32 bg-tech-primary border-y-4 md:border-y-8 border-black overflow-hidden relative">
<div className="absolute top-0 right-0 text-[100px] md:text-[180px] font-black text-black/10 leading-none select-none pointer-events-none -translate-y-8 md:-translate-y-12 translate-x-8 md:translate-x-12 italic">
NEWS
</div>
<div className="container mx-auto px-6 lg:px-12 relative z-10">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 md:gap-16 items-center">
<div>
<div className="inline-block bg-black text-tech-primary px-3 py-1 text-[9px] md:text-[10px] font-black uppercase tracking-[0.4em] mb-4 md:mb-6 skew-x-[-10deg]">
Intelligence_Unit
</div>
<h2 className="text-3xl md:text-6xl font-black text-black leading-[1] tracking-[-0.04em] uppercase mb-4 md:mb-6 italic">
Join the <br />
<span className="text-white drop-shadow-[2px_2px_0_rgba(249,115,22,0.4)]">Elite Network</span>
</h2>
<p className="text-black font-bold text-xs md:text-sm max-w-xs uppercase tracking-tight opacity-80">
Deep-level systems architecture analysis delivered to your node.
</p>
</div>
<div className="bg-black p-6 md:p-8 skew-x-[-1deg] shadow-[8px_8px_0_0_rgba(255,255,255,1)] md:shadow-[12px_12px_0_0_rgba(255,255,255,1)]">
<form
className="space-y-4"
onSubmit={(e) => e.preventDefault()}
>
<div className="space-y-1">
<label htmlFor="emailAddress" className="text-[8px] font-black text-tech-primary uppercase tracking-[0.2em]">Neural_Endpoint</label>
<input
id="emailAddress"
type="email"
placeholder="USER@LUMIX.TECH"
className="w-full bg-white/5 border border-white/10 text-white px-5 py-3 outline-hidden focus:border-tech-primary transition-all font-bold text-xs uppercase placeholder:text-white/10 italic"
required
/>
</div>
<button
className="w-full bg-tech-primary text-black font-black py-4 text-xs uppercase tracking-[0.1em] italic flex items-center justify-center gap-2 hover:bg-white transition-colors cursor-pointer group"
>
Establish Sync <Send size={14} className="group-hover:translate-x-1 transition-transform" />
</button>
<p className="text-[9px] font-bold text-white/40 uppercase tracking-widest text-center">
By connecting, you agree to our protocol terms and neural ethics guidelines.
</p>
</form>
</div>
</div>
</div>
</section>
);
};

View file

@ -0,0 +1,65 @@
import React from 'react';
import { Post } from '../types';
import { Link } from 'react-router-dom';
import { useI18n } from '../hooks/useI18n';
import { formatDate } from '../lib/utils';
import { ArrowRight, User } from 'lucide-react';
import { motion } from 'motion/react';
interface PostCardProps {
post: Post;
}
export const PostCard: React.FC<PostCardProps> = ({ post }) => {
const { lang } = useI18n();
return (
<motion.div
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
viewport={{ once: true }}
className="group h-full"
>
<Link to={`/${lang}/blog/${post.slug}`} className="flex flex-col h-full bg-white/[0.01] border border-white/5 hover:bg-white/[0.03] hover:border-tech-primary/30 transition-all duration-300">
<div className="relative aspect-[16/10] overflow-hidden">
<img
src={post.image}
alt={post.title}
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
loading="lazy"
referrerPolicy="no-referrer"
/>
<div className="absolute top-2 left-2 md:top-3 md:left-3">
<span className="category-tag !text-[8px] !px-1.5 !py-0.5">
{post.category}
</span>
</div>
</div>
<div className="p-4 md:p-5 flex flex-col flex-grow">
<div className="flex items-center gap-3 md:gap-4 mb-2 md:mb-3 text-[9px] md:text-[10px] font-bold text-tech-muted uppercase tracking-widest">
<span>{formatDate(post.date, lang)}</span>
<span className="w-1 h-1 rounded-full bg-tech-primary" />
<span>{post.readingTime}</span>
</div>
<h3 className="text-base md:text-lg font-bold mb-3 text-white group-hover:text-tech-primary transition-colors leading-tight line-clamp-2 uppercase italic tracking-tighter">
{post.title}
</h3>
<p className="text-tech-muted text-[11px] line-clamp-2 leading-relaxed mb-4 md:mb-6">
{post.description}
</p>
<div className="mt-auto flex items-center justify-between pt-3 md:pt-4 border-t border-white/5">
<span className="text-[9px] md:text-[10px] font-bold text-white uppercase flex items-center gap-2 tracking-tight">
<User size={10} className="text-tech-primary" /> {post.author}
</span>
<ArrowRight size={12} className="text-tech-primary group-hover:translate-x-1 transition-transform" />
</div>
</div>
</Link>
</motion.div>
);
};

View file

@ -0,0 +1,127 @@
import React from 'react';
import { Helmet } from 'react-helmet-async';
import { useLocation } from 'react-router-dom';
interface SEOProps {
title: string;
description: string;
image?: string;
article?: boolean;
author?: string;
datePublished?: string;
category?: string;
}
export const SEO: React.FC<SEOProps> = ({
title,
description,
image,
article,
author = 'Vanta Architecture Team',
datePublished,
category
}) => {
const siteName = 'Vanta Technical Journal';
const fullTitle = `${title} | ${siteName}`;
const location = useLocation();
const canonicalUrl = `https://vanta-journal.io${location.pathname}`;
const defaultImage = 'https://images.unsplash.com/photo-1620121692029-d088224efc74?q=80&w=2000';
const ogImage = image || defaultImage;
const structuredData = article ? {
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": title,
"description": description,
"image": ogImage,
"author": {
"@type": "Person",
"name": author
},
"datePublished": datePublished,
"mainEntityOfPage": {
"@type": "WebPage",
"@id": canonicalUrl
},
"publisher": {
"@type": "Organization",
"name": siteName,
"logo": {
"@type": "ImageObject",
"url": "https://nexus-blog.tech/logo.png"
}
}
} : {
"@context": "https://schema.org",
"@type": "WebSite",
"name": siteName,
"url": "https://nexus-blog.tech",
"description": description
};
const breadcrumbData = article ? {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": `https://nexus-blog.tech/${location.pathname.split('/')[1]}`
},
{
"@type": "ListItem",
"position": 2,
"name": "Blog",
"item": `https://nexus-blog.tech/${location.pathname.split('/')[1]}/blog`
},
{
"@type": "ListItem",
"position": 3,
"name": title,
"item": canonicalUrl
}
]
} : null;
return (
<Helmet
htmlAttributes={{
lang: location.pathname.split('/')[1] || 'pt'
}}
>
{/* Basic Meta Tags */}
<title>{fullTitle}</title>
<meta name="description" content={description} />
<link rel="canonical" href={canonicalUrl} />
{/* Open Graph / Facebook */}
<meta property="og:site_name" content={siteName} />
<meta property="og:url" content={canonicalUrl} />
<meta property="og:title" content={fullTitle} />
<meta property="og:description" content={description} />
<meta property="og:type" content={article ? 'article' : 'website'} />
<meta property="og:image" content={ogImage} />
{article && category && <meta property="article:section" content={category} />}
{article && datePublished && <meta property="article:published_time" content={datePublished} />}
{/* Twitter */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:domain" content="vanta-journal.io" />
<meta name="twitter:url" content={canonicalUrl} />
<meta name="twitter:title" content={fullTitle} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={ogImage} />
{/* Google Rich Results */}
<script type="application/ld+json">
{JSON.stringify(structuredData)}
</script>
{breadcrumbData && (
<script type="application/ld+json">
{JSON.stringify(breadcrumbData)}
</script>
)}
</Helmet>
);
};

View file

@ -0,0 +1,12 @@
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
export const ScrollToTop = () => {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
};

View file

@ -0,0 +1,30 @@
import React from 'react';
import { ArrowRight } from 'lucide-react';
interface TOCProps {
content: string;
}
export const TableOfContents: React.FC<TOCProps> = ({ content }) => {
const headings = content.split('\n').filter(line => line.startsWith('## ')).map(line => line.replace('## ', '').trim());
if (headings.length === 0) return null;
return (
<nav className="flex flex-col">
{headings.map((heading, index) => (
<a
key={heading}
href={`#${heading.toLowerCase().replace(/ /g, '-')}`}
className="group flex items-center justify-between py-3 border-b border-white/5 text-[11px] font-black uppercase italic tracking-widest text-tech-muted hover:text-tech-primary transition-all overflow-hidden"
>
<span className="flex items-center gap-3">
<span className="text-[8px] text-white/20 group-hover:text-tech-primary transition-colors">0{index + 1}</span>
{heading}
</span>
<ArrowRight size={12} className="opacity-0 group-hover:opacity-100 transition-all -translate-x-4 group-hover:translate-x-0" />
</a>
))}
</nav>
);
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,26 @@
import { useLocation, useNavigate, useParams } from 'react-router-dom';
import { content } from '../content';
import { Language } from '../types';
export function useI18n() {
const { lang } = useParams<{ lang?: string }>();
const location = useLocation();
const navigate = useNavigate();
const currentLang: Language = (lang as Language) || 'pt';
const t = content.ui[currentLang];
const posts = content.posts[currentLang];
const changeLanguage = (newLang: Language) => {
const pathParts = location.pathname.split('/');
pathParts[1] = newLang; // Replace the language segment
navigate(pathParts.join('/'));
};
return {
lang: currentLang,
t,
posts,
changeLanguage,
};
}

139
Template-02/src/index.css Normal file
View file

@ -0,0 +1,139 @@
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap');
@import "tailwindcss";
@theme {
--font-sans: "Inter", system-ui, sans-serif;
--font-mono: "Fira Code", monospace;
--color-tech-primary: #f97316; /* Vivid Orange */
--color-tech-secondary: #64748b; /* Slate Gray */
--color-tech-accent: #ffffff; /* White */
--color-tech-surface: #181818; /* Lighter Industrial Dark */
--color-tech-border: rgba(255, 255, 255, 0.08);
--color-tech-text: #f1f5f9;
--color-tech-muted: #94a3b8;
--color-tech-glow: rgba(249, 115, 22, 0.15);
--animate-float: float 6s ease-in-out infinite;
--animate-pulse-slow: pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite;
--animate-fade-in: fadeIn 0.4s ease-out forwards;
--animate-marquee: marquee 30s linear infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-15px); }
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes marquee {
0% { transform: translateX(0); }
100% { transform: translateX(-50%); }
}
@utility article-content {
@apply text-tech-text leading-relaxed;
& h2 {
@apply text-xl md:text-2xl font-black text-white mt-12 mb-6 uppercase italic tracking-tighter border-l-4 border-tech-primary pl-4;
}
& h3 {
@apply text-lg font-black text-white mt-8 mb-4 uppercase tracking-tight;
}
& p {
@apply mb-6 text-sm md:text-base text-tech-muted leading-[1.6];
}
& blockquote {
@apply my-8 md:my-10 p-5 md:p-8 bg-white/5 border-l-4 border-tech-primary italic text-base md:text-xl font-bold text-white relative;
&::before {
content: '"';
@apply absolute -top-1 -left-1 text-4xl md:text-5xl text-white/10 font-serif leading-none;
}
}
& ul {
@apply mb-8 space-y-3;
& li {
@apply flex items-start gap-3 text-tech-muted;
&::before {
content: "→";
@apply text-tech-primary font-bold;
}
}
}
& img {
@apply my-12 border border-white/10 shadow-2xl grayscale hover:grayscale-0 transition-all duration-500;
}
}
@layer base {
body {
@apply bg-tech-surface text-white selection:bg-tech-primary/30 selection:text-white antialiased overflow-x-hidden;
background-image:
radial-gradient(at 0% 0%, rgba(249, 115, 22, 0.05) 0px, transparent 50%),
radial-gradient(at 100% 100%, rgba(100, 116, 139, 0.03) 0px, transparent 50%);
}
h1, h2, h3, h4, h5, h6 {
@apply font-sans tracking-tight text-white font-bold leading-tight;
}
}
@utility glass-card {
@apply bg-white/[0.02] backdrop-blur-xl border border-white/5 transition-all duration-500;
}
@utility tech-card {
@apply glass-card hover:bg-white/[0.05] hover:border-tech-primary/40 flex flex-col h-full overflow-hidden;
}
@utility mag-feature-card {
@apply relative overflow-hidden cursor-pointer aspect-video md:aspect-auto;
}
@utility gradient-text {
@apply bg-clip-text text-transparent bg-linear-to-r from-tech-primary via-tech-secondary to-tech-accent;
}
@utility category-tag {
@apply inline-flex items-center px-2 py-0.5 rounded-sm bg-tech-primary text-[10px] font-bold text-white uppercase tracking-wider;
}
@utility btn-mag {
@apply inline-flex items-center justify-center px-5 py-2 bg-tech-primary text-white font-bold text-sm transition-all hover:bg-tech-primary/80 active:scale-95;
}
@layer components {
.markdown-body {
@apply text-tech-muted leading-relaxed text-lg font-sans;
}
.markdown-body h2 {
@apply text-3xl font-bold mt-16 mb-8 text-white tracking-tight flex items-center gap-4;
}
.markdown-body h2::before {
content: "";
@apply w-1.5 h-8 bg-tech-primary;
}
.markdown-body p {
@apply mb-8;
}
.markdown-body pre {
@apply bg-[#0f172a] border border-white/10 p-6 rounded-lg my-8 font-mono text-sm overflow-x-auto shadow-2xl;
}
.markdown-body blockquote {
@apply border-l-4 border-tech-primary pl-8 py-2 my-10 italic text-white bg-tech-primary/5 rounded-r-lg;
}
}

View file

@ -0,0 +1,14 @@
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
export function formatDate(dateString: string, lang: string) {
return new Date(dateString).toLocaleDateString(lang, {
year: 'numeric',
month: 'long',
day: 'numeric',
});
}

10
Template-02/src/main.tsx Normal file
View file

@ -0,0 +1,10 @@
import {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import App from './App.tsx';
import './index.css';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
);

View file

@ -0,0 +1,88 @@
import React from 'react';
import { useParams, Link } from 'react-router-dom';
import { useI18n } from '../hooks/useI18n';
import { PostCard } from '../components/PostCard';
import { SEO } from '../components/SEO';
import { cn } from '../lib/utils';
import { motion } from 'motion/react';
export default function BlogList() {
const { category } = useParams<{ category?: string }>();
const { lang, posts, t } = useI18n();
const validPosts = posts || [];
const categories = ['all', 'ai', 'code', 'startups', 'tools'];
const filteredPosts = category && category !== 'all'
? validPosts.filter(p => p.category === category)
: validPosts;
const activeCategory = category || 'all';
return (
<>
<SEO
title={t.nav.blog}
description="Explora nuestros artículos sobre tecnología, programación e inteligencia artificial."
/>
<section className="py-24 lg:py-48 container mx-auto px-6 lg:px-12 bg-black min-h-screen">
<header className="mb-24 max-w-5xl">
<div className="inline-flex items-center gap-3 text-tech-primary text-[11px] font-mono font-black uppercase tracking-[0.4em] mb-6">
<div className="w-12 h-1 bg-tech-primary" />
<span>ARCHIVE_STREAM</span>
</div>
<motion.h1
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="text-4xl md:text-6xl font-bold mb-8 text-white tracking-tight"
>
{t.nav.blog}
</motion.h1>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.1 }}
className="flex flex-wrap gap-4"
>
{categories.map((cat) => (
<Link
key={cat}
to={cat === 'all' ? `/${lang}/blog` : `/${lang}/blog/category/${cat}`}
className={cn(
"px-6 py-2 font-mono text-[11px] font-black uppercase tracking-widest transition-all border-2",
activeCategory === cat
? "bg-tech-primary text-black border-tech-primary shadow-[4px_4px_0px_0px_rgba(255,255,255,1)]"
: "bg-black border-tech-border text-tech-muted hover:border-white hover:text-white"
)}
>
{t.blog.categories[cat as keyof typeof t.blog.categories]}
</Link>
))}
</motion.div>
</header>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-12">
{filteredPosts.map((post, idx) => (
<motion.div
key={post.id}
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: idx * 0.05 }}
className="relative"
>
<div className="absolute -top-3 -left-3 w-6 h-6 border-t-2 border-l-2 border-tech-primary pointer-events-none z-10" />
<PostCard post={post} />
</motion.div>
))}
</div>
{filteredPosts.length === 0 && (
<div className="text-center py-20 text-slate-400 font-mono text-xs font-bold uppercase tracking-widest border border-dashed border-slate-200 rounded-lg mt-10">
[error] no_modules_found_in_category
</div>
)}
</section>
</>
);
}

View file

@ -0,0 +1,125 @@
import React from 'react';
import { useI18n } from '../hooks/useI18n';
import { SEO } from '../components/SEO';
import { motion } from 'motion/react';
import { ArrowLeft, Send, Terminal, Mail, MapPin, Phone } from 'lucide-react';
import { Link } from 'react-router-dom';
export default function Contact() {
const { t, lang } = useI18n();
return (
<div className="bg-black min-h-screen pt-40 pb-32">
<SEO title={t.contact.title} description={t.contact.subtitle} />
<div className="container mx-auto px-6 lg:px-12">
<header className="max-w-4xl mb-24">
<Link to={`/${lang}`} className="inline-flex items-center gap-3 text-tech-primary text-[11px] font-mono font-black mb-12 hover:text-white transition-colors uppercase tracking-[0.3em] bg-tech-surface px-4 py-2 border border-tech-border">
<ArrowLeft size={14} /> ./RETURN_TO_ROOT
</Link>
<div className="inline-flex items-center gap-3 text-tech-primary text-[11px] font-mono font-black uppercase tracking-[0.4em] mb-6">
<div className="w-12 h-1 bg-tech-primary" />
<span>ENCRYPTED_COMMS</span>
</div>
<motion.h1
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="text-4xl md:text-6xl font-bold mb-8 text-white tracking-tight"
>
{t.contact.title}
</motion.h1>
<p className="text-xl text-tech-muted font-medium max-w-2xl leading-relaxed">
{t.contact.subtitle}
</p>
</header>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-24">
{/* Contact Form */}
<motion.div
initial={{ opacity: 0, scale: 0.98 }}
animate={{ opacity: 1, scale: 1 }}
className="bg-black border-4 border-white p-10 lg:p-16 shadow-[16px_16px_0px_0px_rgba(204,255,0,1)]"
>
<form className="space-y-10" onSubmit={(e) => e.preventDefault()}>
<div className="space-y-4">
<label className="text-[11px] font-mono font-black text-tech-primary uppercase tracking-widest">INPUT::NAME</label>
<input
type="text"
className="w-full bg-black border-b-2 border-tech-border py-4 focus:outline-hidden focus:border-tech-primary transition-all font-mono text-lg text-white uppercase tracking-tight"
placeholder="ANONYMOUS_USER"
/>
</div>
<div className="space-y-4">
<label className="text-[11px] font-mono font-black text-tech-primary uppercase tracking-widest">INPUT::EMAIL</label>
<input
type="email"
className="w-full bg-black border-b-2 border-tech-border py-4 focus:outline-hidden focus:border-tech-primary transition-all font-mono text-lg text-white uppercase tracking-tight"
placeholder="NODE@VANTA_JOURNAL.IO"
/>
</div>
<div className="space-y-4">
<label className="text-[11px] font-mono font-black text-tech-primary uppercase tracking-widest">INPUT::MESSAGE_PAYLOAD</label>
<textarea
rows={4}
className="w-full bg-black border-b-2 border-tech-border py-4 focus:outline-hidden focus:border-tech-primary transition-all font-mono text-lg text-white uppercase tracking-tight resize-none"
placeholder="> SYSTEM_LOGS: INIT_MESSAGE..."
></textarea>
</div>
<button className="btn-tech !w-full !py-6 !text-lg">
{t.contact.form.submit} <Send size={20} className="ml-3" />
</button>
</form>
</motion.div>
{/* Info Side */}
<div className="space-y-20">
<div>
<h3 className="text-[12px] font-mono font-black text-white uppercase tracking-[0.4em] mb-12 border-l-4 border-tech-primary pl-6 leading-none">NODE_COORDINATES</h3>
<ul className="space-y-10">
<li className="flex items-start gap-6">
<div className="w-14 h-14 bg-tech-border flex items-center justify-center text-tech-primary shadow-[4px_4px_0px_0px_rgba(255,255,255,1)]">
<Mail size={24} />
</div>
<div className="uppercase italic">
<span className="block text-[10px] font-mono font-black text-tech-muted tracking-widest mb-1">DIGITAL_MAIL</span>
<span className="font-black text-white text-2xl tracking-tighter">{t.contact.info.email}</span>
</div>
</li>
<li className="flex items-start gap-6">
<div className="w-14 h-14 bg-tech-border flex items-center justify-center text-tech-primary shadow-[4px_4px_0px_0px_rgba(255,255,255,1)]">
<MapPin size={24} />
</div>
<div className="uppercase italic">
<span className="block text-[10px] font-mono font-black text-tech-muted tracking-widest mb-1">PHYSICAL_SECTOR</span>
<span className="font-black text-white text-2xl tracking-tighter">{t.contact.info.location}</span>
</div>
</li>
</ul>
</div>
<div className="p-10 bg-tech-surface border-4 border-tech-primary relative text-white">
<div className="absolute -top-1 -right-1 w-6 h-6 bg-white" />
<div className="flex items-center gap-4 mb-8">
<Terminal size={20} className="text-tech-primary" />
<span className="font-mono font-black uppercase tracking-[0.3em] overflow-hidden whitespace-nowrap">CORE_SYSTEM_STATUS</span>
</div>
<div className="text-sm font-mono text-tech-muted mb-10 space-y-3 uppercase tracking-widest">
<div className="flex justify-between"><span>ACTIVE_SATELLITES:</span> <span className="text-white">12</span></div>
<div className="flex justify-between"><span>UPTIME_INDEX:</span> <span className="text-white">99.99%</span></div>
<div className="flex justify-between"><span>GLOBAL_PING:</span> <span className="text-white">45MS</span></div>
</div>
<div className="h-4 w-full bg-black border border-white p-1">
<motion.div
initial={{ width: 0 }}
animate={{ width: '85%' }}
transition={{ duration: 1.5, repeat: Infinity, ease: "linear" }}
className="h-full bg-tech-primary shadow-[0_0_15px_rgba(204,255,0,0.5)]"
/>
</div>
</div>
</div>
</div>
</div>
</div>
);
}

View file

@ -0,0 +1,240 @@
import React from 'react';
import { useI18n } from '../hooks/useI18n';
import { PostCard } from '../components/PostCard';
import { Newsletter } from '../components/Newsletter';
import { SEO } from '../components/SEO';
import { motion } from 'motion/react';
import { ArrowRight, Sparkles, Terminal, Cpu, Database, Code2, TrendingUp, Clock, User } from 'lucide-react';
import { Link } from 'react-router-dom';
import { cn, formatDate } from '../lib/utils';
export default function Home() {
const { t, posts, lang } = useI18n();
const validPosts = (posts || []).filter(p => p.id !== 'privacy-policy' && p.id !== 'terms-of-service');
// Featured posts for the masonry hero
const featuredPosts = validPosts.slice(0, 3);
// Posts for the main list
const recentPosts = validPosts.slice(3, 9);
// Posts for the sidebar
const sidebarPosts = validPosts.slice(0, 5);
return (
<div className="min-h-screen pb-20">
<SEO title={t.home.hero.title} description={t.home.hero.subtitle} />
{/* Magazine Hero Masonry */}
<section className="container mx-auto px-4 md:px-6 lg:px-12 pt-6 md:pt-12 pb-16">
<div className="grid grid-cols-1 lg:grid-cols-12 gap-1 mb-8 md:mb-16 h-auto lg:h-[600px] bg-white/5 border border-white/5 p-1">
{/* Main Hero (Slot 1) */}
<div className="lg:col-span-8 h-[350px] md:h-[450px] lg:h-full relative overflow-hidden group">
<Link to={`/${lang}/blog/${featuredPosts[0]?.slug}`} className="block h-full cursor-pointer">
<img
src={featuredPosts[0]?.image}
className="w-full h-full object-cover transition-transform duration-1000 group-hover:scale-105"
alt={featuredPosts[0]?.title}
/>
<div className="absolute inset-0 bg-linear-to-t from-black via-black/40 to-transparent" />
<div className="absolute bottom-0 left-0 p-6 md:p-10 w-full">
<span className="category-tag mb-4 shadow-lg">{featuredPosts[0]?.category}</span>
<h2 className="text-xl md:text-3xl lg:text-4xl font-extrabold text-white mb-4 md:mb-6 leading-tight max-w-3xl group-hover:text-tech-primary transition-colors uppercase italic tracking-tighter">
{featuredPosts[0]?.title}
</h2>
<div className="flex items-center gap-4 md:gap-6 text-[10px] md:text-[11px] font-bold text-white/50 uppercase tracking-widest">
<span className="flex items-center gap-2"><User size={12} className="text-tech-primary" /> {featuredPosts[0]?.author}</span>
<span className="flex items-center gap-2"><Clock size={12} className="text-tech-primary" /> {featuredPosts[0]?.readingTime}</span>
</div>
</div>
</Link>
</div>
{/* Secondary Heroes (Slots 2 & 3) */}
<div className="lg:col-span-4 flex flex-col md:flex-row lg:flex-col gap-1 h-auto lg:h-full">
{featuredPosts.slice(1, 3).map((post) => (
<div key={post.id} className="flex-1 relative overflow-hidden group min-h-[200px] md:min-h-[250px]">
<Link to={`/${lang}/blog/${post.slug}`} className="block h-full cursor-pointer">
<img
src={post.image}
className="w-full h-full object-cover transition-transform duration-1000 group-hover:scale-105"
alt={post.title}
/>
<div className="absolute inset-0 bg-linear-to-t from-black via-black/40 to-transparent" />
<div className="absolute bottom-0 left-0 p-5 md:p-8">
<span className="category-tag mb-3 !text-[8px]">{post.category}</span>
<h3 className="text-lg md:text-xl font-bold text-white leading-tight group-hover:text-tech-primary transition-colors uppercase italic tracking-tighter">
{post.title}
</h3>
</div>
</Link>
</div>
))}
</div>
</div>
{/* Trending Marquee Bar */}
<div className="flex flex-col md:flex-row items-center gap-4 md:gap-6 py-4 border-y border-white/5 text-[10px] md:text-sm">
<div className="flex items-center gap-2 text-tech-primary font-black whitespace-nowrap">
<TrendingUp size={16} />
<span className="uppercase tracking-widest text-[9px] md:text-xs">Trending:</span>
</div>
<div className="flex-grow overflow-hidden relative grayscale hover:grayscale-0 transition-all">
<div className="flex gap-8 md:gap-12 animate-marquee whitespace-nowrap">
{validPosts.slice(0, 5).map((post) => (
<Link
key={`trend-${post.id}`}
to={`/${lang}/blog/${post.slug}`}
className="hover:text-tech-primary transition-colors font-bold text-tech-muted uppercase tracking-tight"
>
{post.title}
</Link>
))}
</div>
</div>
</div>
</section>
{/* Main Magazine Layout */}
<section className="container mx-auto px-4 md:px-6 lg:px-12 grid grid-cols-1 lg:grid-cols-12 gap-12 pt-12 md:pt-16">
{/* Primary Content Area */}
<div className="lg:col-span-8">
<div className="flex items-center justify-between mb-8 pb-4 border-b border-white/10">
<h2 className="text-xl md:text-2xl font-black uppercase tracking-tighter skew-x-[-10deg] italic">
<span className="bg-tech-primary text-black px-3 py-1 mr-2 tracking-widest">Latest</span>
Intelligence
</h2>
<Link to={`/${lang}/blog`} className="text-[9px] font-bold text-tech-muted hover:text-tech-primary flex items-center gap-2 uppercase tracking-[0.2em]">
Archive <ArrowRight size={12} />
</Link>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 md:gap-8">
{recentPosts.map((post) => (
<PostCard key={post.id} post={post} />
))}
</div>
{/* Mid-page Feature */}
{validPosts[10] && (
<div className="mt-16 relative aspect-[4/3] md:aspect-[21/9] overflow-hidden group border border-white/5">
<img src={validPosts[10].image} className="w-full h-full object-cover grayscale opacity-60 group-hover:grayscale-0 group-hover:opacity-100 transition-all duration-1000" alt="Featured" />
<div className="absolute inset-0 bg-linear-to-r from-black via-black/50 to-transparent flex flex-col justify-end md:justify-center p-6 md:p-16">
<span className="category-tag mb-4 w-fit !text-[8px]">{validPosts[10].category}</span>
<h2 className="text-2xl md:text-5xl font-black text-white max-w-2xl mb-6 leading-tight uppercase italic tracking-tighter">
{validPosts[10].title}
</h2>
<Link to={`/${lang}/blog/${validPosts[10].slug}`} className="btn-mag w-fit uppercase tracking-tighter !px-4 !py-1.5 !text-xs">
Read Depth Feature
</Link>
</div>
</div>
)}
</div>
{/* Intelligence Sidebar */}
<aside className="lg:col-span-4 space-y-12">
{/* Section: Categories */}
<div>
<h3 className="text-sm font-black uppercase tracking-[0.2em] mb-8 flex items-center gap-3">
<div className="w-8 h-1 bg-tech-primary" />
Knowledge Nodes
</h3>
<div className="grid grid-cols-1 gap-2">
{[
{ id: 'ai', label: 'NEURAL_NETWORKS', count: 12, icon: Sparkles },
{ id: 'code', label: 'SYSTEM_LOGIC', count: 8, icon: Code2 },
{ id: 'tools', label: 'ENGINEERING_LAB', count: 15, icon: Terminal },
{ id: 'startups', label: 'GROWTH_STRAT', count: 5, icon: Cpu }
].map((cat) => (
<Link
key={cat.id}
to={`/${lang}/blog/category/${cat.id}`}
className="flex items-center justify-between p-4 bg-white/[0.01] border-l-2 border-transparent hover:border-tech-primary hover:bg-white/[0.03] transition-all group"
>
<div className="flex items-center gap-4">
<cat.icon size={20} className="text-tech-muted group-hover:text-tech-primary transition-colors" />
<span className="font-bold text-xs uppercase tracking-widest">{cat.label}</span>
</div>
<span className="text-[10px] bg-white/5 px-2 py-1 font-mono text-tech-muted">{cat.count}</span>
</Link>
))}
</div>
</div>
{/* Section: Must Read */}
<div>
<h3 className="text-xs font-black uppercase tracking-[0.2em] mb-8 flex items-center gap-3">
<div className="w-6 h-1 bg-tech-secondary" />
Priority Protocol
</h3>
<div className="space-y-6">
{sidebarPosts.map((post, i) => (
<Link key={`popular-${post.id}`} to={`/${lang}/blog/${post.slug}`} className="flex gap-4 group items-center border-b border-white/5 pb-6 last:border-0">
<div className="text-xl font-black text-white/5 group-hover:text-tech-primary/20 transition-colors leading-none">0{i+1}</div>
<div className="flex-grow">
<span className="text-[8px] font-bold text-tech-primary uppercase tracking-widest block mb-1">{post.category}</span>
<h4 className="font-bold text-xs text-white group-hover:text-tech-primary transition-colors leading-tight uppercase tracking-tight">{post.title}</h4>
</div>
<div className="w-12 h-12 shrink-0 bg-white/5 overflow-hidden">
<img src={post.image} className="w-full h-full object-cover group-hover:scale-110 transition-transform grayscale opacity-50 group-hover:grayscale-0 group-hover:opacity-100" alt="" />
</div>
</Link>
))}
</div>
</div>
{/* Sidebar Newsletter Module */}
<div className="relative group p-8 bg-tech-surface border border-white/5 overflow-hidden">
<div className="absolute top-0 right-0 p-2 opacity-20">
<Terminal size={40} className="text-tech-primary" />
</div>
<div className="relative z-10">
<div className="flex items-center gap-2 text-tech-primary text-[10px] font-black uppercase tracking-[0.3em] mb-4">
<span className="w-6 h-[2px] bg-tech-primary"></span>
<span>Sub_Protocol</span>
</div>
<h3 className="text-2xl font-black text-white italic tracking-tighter uppercase mb-2 leading-none">
Direct <br /> Intel_Feed
</h3>
<p className="text-tech-muted text-[11px] font-bold uppercase tracking-tight mb-8 leading-tight">
Architectural insights delivered to your local node weekly.
</p>
<form className="space-y-4" onSubmit={(e) => e.preventDefault()}>
<div className="relative">
<input
type="email"
placeholder="USER_EMAIL@PROTO.IO"
className="w-full bg-white/5 border-b-2 border-white/10 px-0 py-3 text-xs font-mono text-white placeholder:text-white/20 focus:outline-none focus:border-tech-primary transition-colors uppercase"
/>
<div className="absolute bottom-0 left-0 w-0 h-[2px] bg-tech-primary transition-all duration-500 group-focus-within:w-full"></div>
</div>
<button className="w-full bg-tech-primary text-black font-black text-[10px] uppercase tracking-[0.4em] py-4 skew-x-[-10deg] hover:bg-white hover:text-black transition-all active:scale-95 italic">
[ Execute_Sync ]
</button>
</form>
<div className="mt-6 flex items-center justify-between text-[8px] font-mono text-white/20 uppercase tracking-widest">
<span>Status: Ready</span>
<span>Enc_V2.4</span>
</div>
</div>
{/* Design Detail */}
<div className="absolute -bottom-4 -right-4 w-24 h-24 bg-tech-primary/5 rounded-full blur-3xl group-hover:bg-tech-primary/10 transition-colors"></div>
</div>
{/* Connect Tags */}
<div>
<h3 className="text-xs font-black uppercase tracking-[0.2em] mb-6">Popular_Tags</h3>
<div className="flex flex-wrap gap-2">
{['#NEXTJS', '#CYBERSECURITY', '#VECTOR_DB', '#LLM', '#ARCHITECTURE', '#RUST', '#SCALABILITY'].map(tag => (
<span key={tag} className="px-3 py-1 bg-white/5 hover:bg-tech-primary/10 transition-colors border border-white/5 text-[10px] font-bold text-tech-muted hover:text-white cursor-pointer uppercase tracking-tighter">
{tag}
</span>
))}
</div>
</div>
</aside>
</section>
</div>
);
}

View file

@ -0,0 +1,65 @@
import React from 'react';
import { useI18n } from '../hooks/useI18n';
import { SEO } from '../components/SEO';
import { motion } from 'motion/react';
import { ArrowLeft, Shield, FileText, Scale } from 'lucide-react';
import { Link, useLocation } from 'react-router-dom';
export default function Legal() {
const { t, lang } = useI18n();
const location = useLocation();
const isPrivacy = location.pathname.includes('privacy');
const isTerms = location.pathname.includes('terms');
const isEthics = location.pathname.includes('ethics');
let config = t.legal.privacy;
let Icon = Shield;
let code = "PRIVACY_PROTOCOL";
if (isTerms) {
config = t.legal.terms;
Icon = FileText;
code = "SERVICE_TERMS";
} else if (isEthics) {
config = t.legal.ethics;
Icon = Scale;
code = "ETHICS_V4.0";
}
return (
<div className="bg-black min-h-screen pt-40 pb-32">
<SEO title={config.title} description={config.content} />
<div className="container mx-auto px-6 lg:px-12">
<header className="max-w-4xl mb-24">
<Link to={`/${lang}`} className="inline-flex items-center gap-3 text-tech-primary text-[11px] font-mono font-black mb-12 hover:text-white transition-colors uppercase tracking-[0.3em] bg-tech-surface px-4 py-2 border border-tech-border">
<ArrowLeft size={14} /> ./GET_BACK
</Link>
<div className="inline-flex items-center gap-3 text-tech-primary text-[11px] font-mono font-black uppercase tracking-[0.4em] mb-6">
<Icon size={18} />
<span>{code}</span>
</div>
<motion.h1
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="text-4xl md:text-6xl font-bold mb-8 text-white tracking-tight"
>
{config.title}
</motion.h1>
</header>
<article className="max-w-5xl">
<div className="bg-tech-surface border-4 border-tech-border p-12 lg:p-20 font-medium text-tech-muted text-xl leading-relaxed whitespace-pre-line italic shadow-[16px_16px_0px_0px_rgba(255,255,255,1)]">
{config.content}
<div className="mt-20 pt-10 border-t border-tech-border text-[12px] font-mono font-black uppercase tracking-widest text-tech-primary">
LAST_COMMIT: {new Date().toISOString().split('T')[0]}<br/>
REVISION: v2.0.18-FINAL
</div>
</div>
</article>
</div>
</div>
);
}

View file

@ -0,0 +1,232 @@
import React from 'react';
import { useParams, Link } from 'react-router-dom';
import { useI18n } from '../hooks/useI18n';
import { SEO } from '../components/SEO';
import { TableOfContents } from '../components/TableOfContents';
import { Newsletter } from '../components/Newsletter';
import { PostCard } from '../components/PostCard';
import { formatDate } from '../lib/utils';
import { ArrowLeft, Clock, Twitter, Linkedin, Link as LinkIcon, Cpu, Terminal } from 'lucide-react';
import { motion } from 'motion/react';
import Markdown from 'react-markdown';
export default function PostDetail() {
const { slug } = useParams<{ slug: string }>();
const { lang, posts, t } = useI18n();
const validPosts = posts || [];
const post = validPosts.find(p => p.slug === slug);
const relatedPosts = validPosts.filter(p => p.slug !== slug && (p.category === post?.category || p.featured)).slice(0, 3);
if (!post) {
return (
<div className="container mx-auto px-6 py-40 text-center font-mono uppercase tracking-widest text-tech-muted">
<h1 className="text-4xl font-bold mb-8 text-tech-text">[error] 404_NOT_FOUND</h1>
<Link to={`/${lang}/blog`} className="text-tech-primary inline-flex items-center gap-2 font-bold bg-tech-surface px-6 py-2 rounded">
<ArrowLeft size={16} /> RETURN_TO_BASE
</Link>
</div>
);
}
return (
<div className="bg-black min-h-screen">
<SEO
title={post.title}
description={post.description}
image={post.image}
article
author={post.author}
datePublished={post.date}
category={post.category}
/>
{/* Magazine Article Header */}
<section className="pt-20 pb-10 bg-black border-b border-white/5">
<div className="container mx-auto px-6 lg:px-12">
<div className="max-w-4xl">
<div className="flex items-center gap-4 mb-6">
<Link to={`/${lang}/blog`} className="text-[9px] font-black uppercase text-tech-muted hover:text-white transition-colors tracking-[0.2em]">
{t.nav.blog}
</Link>
<span className="text-white/20">/</span>
<span className="category-tag !text-[9px] !px-2 !py-0.5">
{post.category}
</span>
</div>
<h1 className="text-3xl md:text-5xl lg:text-6xl font-black text-white leading-[1.05] tracking-tighter mb-8 uppercase italic">
{post.title}
</h1>
<div className="flex flex-wrap items-center gap-6 mb-8 py-4 border-y border-white/5">
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-tech-primary flex items-center justify-center text-black font-black italic skew-x-[-10deg]">
{post.author[0]}
</div>
<div className="text-left">
<span className="text-[8px] font-bold text-tech-muted uppercase block tracking-[0.2em] mb-0.5">Author</span>
<span className="font-bold text-xs text-white uppercase">{post.author}</span>
</div>
</div>
<div className="hidden sm:block w-px h-6 bg-white/10" />
<div className="text-left">
<span className="text-[8px] font-bold text-tech-muted uppercase block tracking-[0.2em] mb-0.5">Record</span>
<span className="font-bold text-xs text-white uppercase">{formatDate(post.date, lang)}</span>
</div>
<div className="hidden sm:block w-px h-6 bg-white/10" />
<div className="text-left">
<span className="text-[8px] font-bold text-tech-muted uppercase block tracking-[0.2em] mb-0.5">Complexity</span>
<span className="font-bold text-xs text-white uppercase">{post.readingTime}</span>
</div>
</div>
</div>
</div>
</section>
{/* Hero Image Section */}
<section className="container mx-auto px-6 lg:px-12 -mt-4 mb-16">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="aspect-video lg:aspect-[21/9] overflow-hidden border border-white/5 bg-white/5 relative"
>
<img
src={post.image}
className="w-full h-full object-cover grayscale opacity-90 hover:grayscale-0 hover:opacity-100 transition-all duration-700"
alt={post.title}
referrerPolicy="no-referrer"
/>
<div className="absolute inset-0 bg-linear-to-t from-black/60 to-transparent" />
<div className="absolute bottom-6 left-6 text-[10px] font-mono font-bold text-white/40 uppercase tracking-[0.3em]">
Lumix_Asset_ID: {post.slug}
</div>
</motion.div>
</section>
{/* Main Content Layout */}
<section className="container mx-auto px-6 lg:px-12 pb-32">
<div className="grid grid-cols-1 lg:grid-cols-12 gap-16">
{/* Main Body */}
<div className="lg:col-span-8">
<article className="markdown-body">
<Markdown>{post.content}</Markdown>
</article>
{/* Post Footer/Sharing */}
<div className="mt-20 pt-10 border-t border-white/10 flex flex-col md:flex-row justify-between items-center gap-8">
<div className="flex flex-wrap gap-2 text-[10px] font-bold">
{['#TECH', '#FUTURE', '#AI', post.category.toUpperCase()].map(tag => (
<span key={tag} className="px-3 py-1 bg-white/5 border border-white/5 text-tech-muted hover:text-white cursor-pointer uppercase">
{tag}
</span>
))}
</div>
<div className="flex gap-4">
<span className="text-[10px] font-bold text-tech-muted uppercase tracking-widest self-center mr-2">Share Transmission:</span>
<a href="#" className="w-10 h-10 bg-white/5 border border-white/5 flex items-center justify-center text-white hover:border-tech-primary hover:text-tech-primary transition-all"><Twitter size={18} /></a>
<a href="#" className="w-10 h-10 bg-white/5 border border-white/5 flex items-center justify-center text-white hover:border-tech-primary hover:text-tech-primary transition-all"><Linkedin size={18} /></a>
<button
onClick={() => navigator.clipboard.writeText(window.location.href)}
className="w-10 h-10 bg-white/5 border border-white/5 flex items-center justify-center text-white hover:border-tech-primary hover:text-tech-primary transition-all"
>
<LinkIcon size={18} />
</button>
</div>
</div>
</div>
{/* Sidebar Area */}
<aside className="lg:col-span-4 space-y-16">
<div className="sticky top-32 space-y-16">
{/* TOC Table of Contents */}
<div>
<h3 className="text-sm font-black uppercase tracking-[0.2em] mb-8 flex items-center gap-3">
<div className="w-8 h-1 bg-tech-primary" />
Archive Index
</h3>
<TableOfContents content={post.content} />
</div>
{/* Author Extra */}
<div className="p-6 border border-white/5 bg-white/[0.01] relative overflow-hidden group">
<div className="absolute top-0 right-0 w-1.5 h-1.5 bg-tech-primary" />
<h4 className="text-[9px] font-black uppercase tracking-[0.2em] text-tech-primary mb-4">Database_Entry</h4>
<div className="flex items-center gap-3 mb-4">
<div className="w-10 h-10 bg-tech-primary flex items-center justify-center text-black font-black text-xl italic skew-x-[-10deg]">
{post.author[0]}
</div>
<div>
<span className="font-black text-white block text-sm uppercase italic tracking-tighter leading-tight">{post.author}</span>
<span className="text-tech-muted text-[8px] font-bold uppercase tracking-widest">Senior Analyst</span>
</div>
</div>
<p className="text-[11px] text-tech-muted leading-relaxed italic mb-4">
Decoding technical architectures and future protocols.
</p>
<button className="text-[9px] font-bold text-tech-primary uppercase tracking-widest hover:underline">View Intelligence</button>
</div>
{/* Priority Recs */}
<div>
<h3 className="text-[10px] font-black uppercase tracking-[0.2em] mb-6 flex items-center gap-2">
<div className="w-6 h-1 bg-tech-secondary" />
Sync Related
</h3>
<div className="space-y-6">
{relatedPosts.map((rp, i) => (
<Link key={rp.id} to={`/${lang}/blog/${rp.slug}`} className="flex gap-4 group items-center">
<div className="text-xl font-black text-white/5 group-hover:text-tech-primary/20 transition-colors leading-none">0{i+1}</div>
<div className="flex-grow">
<span className="text-[8px] font-bold text-tech-primary uppercase tracking-widest block mb-1">{rp.category}</span>
<h4 className="font-bold text-[11px] text-white group-hover:text-tech-primary transition-colors leading-tight">{rp.title}</h4>
</div>
<div className="w-12 h-12 shrink-0 bg-white/5">
<img src={rp.image} className="w-full h-full object-cover grayscale opacity-50 group-hover:grayscale-0 group-hover:opacity-100 transition-all" alt="" />
</div>
</Link>
))}
</div>
</div>
</div>
</aside>
</div>
</section>
{/* Related Technical Modules */}
{relatedPosts.length > 0 && (
<section className="py-32 bg-tech-surface border-t border-tech-border relative overflow-hidden">
<div className="absolute top-0 left-0 w-full h-full bg-[radial-gradient(#1a1a1a_1px,transparent_1px)] bg-[size:32px_32px] opacity-30" />
<div className="container mx-auto px-6 lg:px-12 relative z-10">
<div className="flex flex-col md:flex-row items-start md:items-end justify-between mb-12 md:mb-20 gap-8">
<div>
<div className="flex items-center gap-3 text-tech-primary text-[10px] md:text-[11px] font-mono font-black uppercase tracking-[0.4em] mb-4 md:mb-6">
<div className="w-8 md:w-12 h-1 bg-tech-primary" />
<span>SYNC_REL_NODES</span>
</div>
<h2 className="text-3xl md:text-5xl lg:text-6xl font-black tracking-tighter text-white uppercase italic leading-none">PEER_STREAM</h2>
</div>
<Link to={`/${lang}/blog`} className="text-[10px] md:text-[11px] font-mono font-black text-white hover:text-tech-primary transition-colors uppercase tracking-widest border-b-2 border-tech-primary pb-1 transform hover:translate-x-2 transition-transform italic w-fit">[ LIST_FULL_RECORDS ]</Link>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-12">
{relatedPosts.map(post => (
<div key={post.id} className="relative">
<div className="absolute -top-3 -left-3 w-6 h-6 border-t-2 border-l-2 border-tech-primary pointer-events-none z-10" />
<PostCard post={post} />
</div>
))}
</div>
</div>
</section>
)}
<Newsletter />
</div>
);
}

97
Template-02/src/types.ts Normal file
View file

@ -0,0 +1,97 @@
export type Language = 'pt' | 'en' | 'es';
export interface Post {
id: string;
slug: string;
title: string;
description: string;
content: string;
category: string;
author: string;
date: string;
readingTime: string;
image: string;
featured?: boolean;
}
export interface Translations {
nav: {
home: string;
blog: string;
about: string;
categories: string;
};
home: {
hero: {
title: string;
subtitle: string;
cta: string;
};
featured: string;
recent: string;
newsletter: {
title: string;
desc: string;
placeholder: string;
button: string;
};
};
blog: {
categories: {
all: string;
ai: string;
code: string;
startups: string;
tools: string;
};
readMore: string;
related: string;
back: string;
toc: string;
};
footer: {
about: string;
connect: string;
rights: string;
terms: string;
ethics: string;
newsletter: string;
rss: string;
contact: string;
privacy: string;
};
legal: {
privacy: { title: string; content: string; };
terms: { title: string; content: string; };
ethics: { title: string; content: string; };
};
contact: {
title: string;
subtitle: string;
info: {
title: string;
email: string;
location: string;
};
status: {
title: string;
nodes: string;
uptime: string;
latency: string;
};
form: {
name: string;
email: string;
subject: string;
message: string;
submit: string;
sending: string;
success: string;
};
};
}
export interface SiteContent {
posts: Record<Language, Post[]>;
ui: Record<Language, Translations>;
}

26
Template-02/tsconfig.json Normal file
View file

@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "ES2022",
"experimentalDecorators": true,
"useDefineForClassFields": false,
"module": "ESNext",
"lib": [
"ES2022",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true,
"moduleResolution": "bundler",
"isolatedModules": true,
"moduleDetection": "force",
"allowJs": true,
"jsx": "react-jsx",
"paths": {
"@/*": [
"./*"
]
},
"allowImportingTsExtensions": true,
"noEmit": true
}
}

View file

@ -0,0 +1,24 @@
import tailwindcss from '@tailwindcss/vite';
import react from '@vitejs/plugin-react';
import path from 'path';
import {defineConfig, loadEnv} from 'vite';
export default defineConfig(({mode}) => {
const env = loadEnv(mode, '.', '');
return {
plugins: [react(), tailwindcss()],
define: {
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY),
},
resolve: {
alias: {
'@': path.resolve(__dirname, '.'),
},
},
server: {
// HMR is disabled in AI Studio via DISABLE_HMR env var.
// Do not modify—file watching is disabled to prevent flickering during agent edits.
hmr: process.env.DISABLE_HMR !== 'true',
},
};
});

14
Template-03/README.md Normal file
View file

@ -0,0 +1,14 @@
# Nexus Tech Blog
Very professional static blog.
## Setup
Install dependencies:
`npm install`
## Technologies
- React 18
- Vite
- Tailwind CSS
- Motion (framer-motion)
- Lucide React
- React Helmet Async (SEO)

13
Template-03/index.html Normal file
View file

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><rect width=%22100%22 height=%22100%22 rx=%2220%22 fill=%22%230070F3%22/><text y=%22.9em%22 font-size=%2280%22 x=%2250%%22 text-anchor=%22middle%22 fill=%22white%22 font-family=%22monospace%22 font-weight=%22bold%22>N</text></svg>" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View file

@ -0,0 +1,6 @@
{
"name": "Lumix Tech Blog",
"description": "A cutting-edge, future-focused technology blog with neo-futuristic design and bento layouts.",
"requestFramePermissions": [],
"majorCapabilities": []
}

5633
Template-03/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

39
Template-03/package.json Normal file
View file

@ -0,0 +1,39 @@
{
"name": "react-example",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --port=3000 --host=0.0.0.0",
"build": "vite build",
"preview": "vite preview",
"clean": "rm -rf dist",
"lint": "tsc --noEmit"
},
"dependencies": {
"@google/genai": "^1.29.0",
"@tailwindcss/vite": "^4.1.14",
"@vitejs/plugin-react": "^5.0.4",
"clsx": "^2.1.1",
"dotenv": "^17.2.3",
"express": "^4.21.2",
"lucide-react": "^0.546.0",
"motion": "^12.23.24",
"react": "^19.0.1",
"react-dom": "^19.0.1",
"react-helmet-async": "^3.0.0",
"react-markdown": "^10.1.0",
"react-router-dom": "^7.15.0",
"tailwind-merge": "^3.5.0",
"vite": "^6.2.3"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/node": "^22.14.0",
"autoprefixer": "^10.4.21",
"tailwindcss": "^4.1.14",
"tsx": "^4.21.0",
"typescript": "~5.8.2",
"vite": "^6.2.3"
}
}

View file

@ -0,0 +1,4 @@
User-agent: *
Allow: /
Sitemap: https://nexus-blog.tech/sitemap.xml

View file

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<!-- Main Pages -->
<url><loc>https://nexus-blog.tech/pt</loc><priority>1.0</priority></url>
<url><loc>https://nexus-blog.tech/en</loc><priority>1.0</priority></url>
<url><loc>https://nexus-blog.tech/es</loc><priority>1.0</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog</loc><priority>0.8</priority></url>
<url><loc>https://nexus-blog.tech/en/blog</loc><priority>0.8</priority></url>
<url><loc>https://nexus-blog.tech/es/blog</loc><priority>0.8</priority></url>
<!-- Categories -->
<url><loc>https://nexus-blog.tech/pt/blog/category/ai</loc><priority>0.6</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/category/code</loc><priority>0.6</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/category/startups</loc><priority>0.6</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/category/tools</loc><priority>0.6</priority></url>
<!-- PT Posts -->
<url><loc>https://nexus-blog.tech/pt/blog/futuro-ia-generativa-2024</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/typescript-moderno-guia</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/escalando-startups-bootstrap</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/ferramentas-indispensaveis-dev</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/web-components-nativos</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/clean-architecture-js</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/seguranca-ciber-ia</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/trabalho-remoto-fatos</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/design-system-flexivel</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/webgpu-ias-locais</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/precificacao-saas</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/nextjs-14-enterprise</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/etica-algoritmica</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/figma-dev-mode</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/rust-backends</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/growth-hacking-real</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/documentacao-agil</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/medicina-ia</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/testes-frontend</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/nomadismo-dev</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/pt/blog/sustentabilidade-tech</loc><priority>0.7</priority></url>
<!-- EN Posts -->
<url><loc>https://nexus-blog.tech/en/blog/future-generative-ai-2024</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/modern-typescript-guide</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/scaling-startups-bootstrap</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/essential-dev-tools</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/native-web-components</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/clean-architecture-js</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/ai-cybersecurity</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/remote-work-facts</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/flexible-design-system</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/webgpu-local-ai</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/saas-pricing</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/nextjs-14-enterprise</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/algorithmic-ethics</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/figma-dev-mode</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/rust-backends</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/real-growth-hacking</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/agile-documentation</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/medicine-ai</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/frontend-testing</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/dev-nomadism</loc><priority>0.7</priority></url>
<url><loc>https://nexus-blog.tech/en/blog/tech-sustainability</loc><priority>0.7</priority></url>
</urlset>

57
Template-03/src/App.tsx Normal file
View file

@ -0,0 +1,57 @@
import React, { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route, Navigate, useParams } from 'react-router-dom';
import { HelmetProvider } from 'react-helmet-async';
import { Layout } from './components/Layout';
import Home from './pages/Home';
import BlogList from './pages/BlogList';
import PostDetail from './pages/PostDetail';
// Wrapper to handle language validation and layout injection
const LangWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const { lang } = useParams<{ lang: string }>();
const validLangs = ['en', 'pt', 'es'];
if (!lang || !validLangs.includes(lang)) {
return <Navigate to="/pt" replace />;
}
return <Layout>{children}</Layout>;
};
const Contact = lazy(() => import('./pages/Contact'));
const Legal = lazy(() => import('./pages/Legal'));
const About = lazy(() => import('./pages/About'));
const Write = lazy(() => import('./pages/Write'));
const Login = lazy(() => import('./pages/Login'));
import { ScrollToTop } from './components/ScrollToTop';
export default function App() {
return (
<HelmetProvider>
<BrowserRouter>
<ScrollToTop />
<Suspense fallback={<div className="min-h-screen bg-black flex items-center justify-center font-mono text-[10px] font-black uppercase tracking-[0.5em] text-tech-primary animate-pulse italic">VANTA_KERN_INIT...</div>}>
<Routes>
<Route path="/" element={<Navigate to="/pt" replace />} />
<Route path="/:lang" element={<LangWrapper><Home /></LangWrapper>} />
<Route path="/:lang/blog" element={<LangWrapper><BlogList /></LangWrapper>} />
<Route path="/:lang/blog/category/:category" element={<LangWrapper><BlogList /></LangWrapper>} />
<Route path="/:lang/blog/:slug" element={<LangWrapper><PostDetail /></LangWrapper>} />
<Route path="/:lang/contact" element={<LangWrapper><Contact /></LangWrapper>} />
<Route path="/:lang/about" element={<LangWrapper><About /></LangWrapper>} />
<Route path="/:lang/write" element={<LangWrapper><Write /></LangWrapper>} />
<Route path="/:lang/login" element={<LangWrapper><Login /></LangWrapper>} />
<Route path="/:lang/privacy" element={<LangWrapper><Legal /></LangWrapper>} />
<Route path="/:lang/terms" element={<LangWrapper><Legal /></LangWrapper>} />
<Route path="/:lang/ethics" element={<LangWrapper><Legal /></LangWrapper>} />
<Route path="*" element={<Navigate to="/pt" replace />} />
</Routes>
</Suspense>
</BrowserRouter>
</HelmetProvider>
);
}

View file

@ -0,0 +1,275 @@
import React from 'react';
import { Link, useLocation, useNavigate } from 'react-router-dom';
import { useI18n } from '../hooks/useI18n';
import { Language } from '../types';
import { Menu, X, Github, Twitter, Linkedin, Search, User, ChevronDown } from 'lucide-react';
import { motion, AnimatePresence } from 'motion/react';
import { cn } from '../lib/utils';
export const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const { lang, t, changeLanguage } = useI18n();
const navigate = useNavigate();
const location = useLocation();
const [isMenuOpen, setIsMenuOpen] = React.useState(false);
const [isScrolled, setIsScrolled] = React.useState(false);
const [isSearchOpen, setIsSearchOpen] = React.useState(false);
const [searchQuery, setSearchQuery] = React.useState('');
React.useEffect(() => {
if (location.pathname === '/' || location.pathname === '') {
navigate('/pt', { replace: true });
}
}, [location.pathname, navigate]);
React.useEffect(() => {
const handleScroll = () => setIsScrolled(window.scrollY > 50);
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
const handleSearch = (e: React.FormEvent) => {
e.preventDefault();
if (searchQuery.trim()) {
setIsSearchOpen(false);
navigate(`/${lang}/blog?q=${encodeURIComponent(searchQuery)}`);
setSearchQuery('');
}
};
const languages: { code: Language; label: string }[] = [
{ code: 'pt', label: 'PT' },
{ code: 'en', label: 'EN' },
{ code: 'es', label: 'ES' },
];
return (
<div className="min-h-screen flex flex-col font-sans bg-gray-50">
{/* Top Bar */}
<div className="bg-gray-900 border-b border-gray-800 py-2 hidden md:block">
<div className="container-inner flex justify-between items-center text-[12px] font-medium text-gray-400">
<div className="flex gap-6 items-center">
<span>Quinta-feira, 14 de Maio de 2026</span>
<div className="flex gap-4">
<Link to={`/${lang}/about`} className="hover:text-white transition-colors">Sobre Nós</Link>
<Link to={`/${lang}/contact`} className="hover:text-white transition-colors">Contato</Link>
<Link to={`/${lang}/write`} className="hover:text-white transition-colors">Escreva para Nós</Link>
</div>
</div>
<div className="flex gap-4 items-center">
<div className="flex gap-3">
<a href="#" className="hover:text-white transition-colors"><Twitter size={14} /></a>
<a href="#" className="hover:text-white transition-colors"><Github size={14} /></a>
<a href="#" className="hover:text-white transition-colors"><Linkedin size={14} /></a>
</div>
<div className="w-px h-3 bg-gray-700 mx-2"></div>
<Link to={`/${lang}/login`} className="flex items-center gap-1 hover:text-white transition-colors">
<User size={14} /> Entrar
</Link>
</div>
</div>
</div>
{/* Main Header */}
<header className="bg-white border-b border-gray-200">
<div className="container-inner py-6 flex items-center justify-between">
<Link to={`/${lang}`} className="flex items-center gap-2">
<div className="w-10 h-10 bg-brand-primary text-white rounded-md flex items-center justify-center font-bold text-2xl font-heading">
C
</div>
<div className="flex flex-col">
<span className="font-bold text-2xl tracking-tight text-gray-900 font-heading leading-none">CodeChronicle</span>
<span className="text-[11px] font-medium text-gray-500 uppercase tracking-widest mt-0.5">Tech & Tutorials</span>
</div>
</Link>
<div className="lg:hidden flex items-center gap-4">
<button onClick={() => setIsSearchOpen(true)}><Search size={22} className="text-gray-700" /></button>
<button onClick={() => setIsMenuOpen(!isMenuOpen)}>
{isMenuOpen ? <X size={26} className="text-gray-900" /> : <Menu size={26} className="text-gray-900" />}
</button>
</div>
</div>
</header>
{/* Main Nav (Sticky) */}
<div className={cn(
"bg-white transition-all shadow-sm z-50",
isScrolled ? "fixed top-0 left-0 w-full animate-in fade-in slide-in-from-top-4" : "border-b border-gray-100"
)}>
<div className="container-inner flex items-center justify-between">
<nav className="hidden lg:flex items-center space-x-1">
<Link to={`/${lang}`} className="px-3 py-4 text-[14px] font-bold text-gray-900 hover:text-brand-primary transition-colors flex items-center gap-1">
{t.nav.home}
</Link>
<div className="relative group/nav">
<Link to={`/${lang}/blog/category/code`} className="px-3 py-4 text-[14px] font-bold text-gray-900 hover:text-brand-primary transition-colors flex items-center gap-1">
Dev Web <ChevronDown size={14} className="text-gray-400" />
</Link>
</div>
<Link to={`/${lang}/blog/category/ai`} className="px-3 py-4 text-[14px] font-bold text-gray-900 hover:text-brand-primary transition-colors flex items-center gap-1">
{t.blog.categories.ai}
</Link>
<Link to={`/${lang}/blog/category/tools`} className="px-3 py-4 text-[14px] font-bold text-gray-900 hover:text-brand-primary transition-colors flex items-center gap-1">
Tópicos <ChevronDown size={14} className="text-gray-400" />
</Link>
<Link to={`/${lang}/blog`} className="px-3 py-4 text-[14px] font-bold text-gray-900 hover:text-brand-primary transition-colors flex items-center gap-1">
Tutoriais
</Link>
</nav>
<div className="hidden lg:flex items-center gap-6">
{/* Language Switcher */}
<div className="flex gap-2 items-center">
{languages.map((l) => (
<button
key={l.code}
onClick={() => changeLanguage(l.code)}
className={cn(
"text-[11px] font-bold uppercase transition-colors px-1.5 py-0.5 rounded",
lang === l.code ? "bg-brand-primary text-white" : "text-gray-500 hover:text-gray-900"
)}
>
{l.code}
</button>
))}
</div>
<button
onClick={() => setIsSearchOpen(true)}
className="text-gray-900 hover:text-brand-primary transition-colors py-4"
>
<Search size={18} />
</button>
</div>
</div>
</div>
<AnimatePresence>
{isMenuOpen && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
className="lg:hidden bg-white border-b border-gray-200 overflow-hidden"
>
<div className="px-4 py-4 space-y-2">
<Link to={`/${lang}`} className="block py-2 text-base font-bold text-gray-900">{t.nav.home}</Link>
<Link to={`/${lang}/blog/category/code`} className="block py-2 text-base font-bold text-gray-900">Dev Web</Link>
<Link to={`/${lang}/blog/category/ai`} className="block py-2 text-base font-bold text-gray-900">{t.blog.categories.ai}</Link>
<Link to={`/${lang}/blog/category/tools`} className="block py-2 text-base font-bold text-gray-900">Tópicos</Link>
<Link to={`/${lang}/blog`} className="block py-2 text-base font-bold text-gray-900">Tutoriais</Link>
</div>
<div className="px-4 py-4 bg-gray-50 border-t border-gray-200 flex gap-4">
{languages.map((l) => (
<button
key={`mobile-${l.code}`}
onClick={() => { changeLanguage(l.code); setIsMenuOpen(false); }}
className={cn(
"text-xs font-bold uppercase px-3 py-1.5 rounded-md border",
lang === l.code ? "border-brand-primary bg-brand-primary text-white" : "border-gray-300 text-gray-600 bg-white"
)}
>
{l.code}
</button>
))}
</div>
</motion.div>
)}
</AnimatePresence>
{/* Search Overlay */}
<AnimatePresence>
{isSearchOpen && (
<>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => setIsSearchOpen(false)}
className="fixed inset-0 bg-gray-900/40 z-[65] backdrop-blur-sm"
/>
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
className="fixed top-0 left-0 w-full z-[70] bg-white border-b border-gray-200 shadow-xl"
>
<div className="container-inner py-6 flex items-center justify-between gap-4">
<form onSubmit={handleSearch} className="flex-1 flex items-center gap-3">
<Search size={24} className="text-gray-400 shrink-0" />
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
autoFocus
placeholder="Pesquisar artigos..."
className="w-full text-xl md:text-2xl font-bold font-heading text-gray-900 focus:outline-none placeholder:text-gray-300"
/>
</form>
<button
onClick={() => setIsSearchOpen(false)}
className="p-2 text-gray-500 hover:text-gray-900 bg-gray-100 rounded-full transition-colors shrink-0"
>
<X size={24} />
</button>
</div>
</motion.div>
</>
)}
</AnimatePresence>
<main className="flex-grow">
{children}
</main>
<footer className="bg-gray-900 text-gray-400 mt-16 pb-8 pt-16">
<div className="container-inner mb-12">
<div className="grid grid-cols-1 md:grid-cols-3 gap-10">
<div>
<Link to={`/${lang}`} className="flex items-center gap-2 mb-6">
<div className="w-8 h-8 bg-brand-primary text-white rounded flex items-center justify-center font-bold text-lg font-heading">C</div>
<span className="font-bold text-xl text-white font-heading tracking-tight">CodeChronicle</span>
</Link>
<p className="text-sm leading-relaxed mb-6 font-medium">
Sua dose diária de notícias de tecnologia, tutoriais práticos e insights de desenvolvimento. Explore o futuro hoje.
</p>
<div className="flex gap-3">
<a href="#" className="w-9 h-9 rounded-full bg-gray-800 flex items-center justify-center hover:bg-brand-primary hover:text-white transition-colors text-white"><Twitter size={14} /></a>
<a href="#" className="w-9 h-9 rounded-full bg-gray-800 flex items-center justify-center hover:bg-brand-primary hover:text-white transition-colors text-white"><Github size={14} /></a>
</div>
</div>
<div>
<h4 className="text-white font-bold text-base mb-6 font-heading border-l-4 border-brand-primary pl-3">Links Úteis</h4>
<ul className="space-y-3 text-sm font-medium">
<li><Link to={`/${lang}/blog`} className="hover:text-brand-primary transition-colors">Todos os Tutoriais</Link></li>
<li><Link to={`/${lang}/blog/category/code`} className="hover:text-brand-primary transition-colors">Dev Web</Link></li>
<li><Link to={`/${lang}/blog/category/ai`} className="hover:text-brand-primary transition-colors">Machine Learning</Link></li>
<li><Link to={`/${lang}/contact`} className="hover:text-brand-primary transition-colors">Fale Conosco</Link></li>
</ul>
</div>
<div>
<h4 className="text-white font-bold text-base mb-6 font-heading border-l-4 border-brand-primary pl-3">Inscreva-se</h4>
<p className="text-sm mb-4">Receba os melhores artigos de tecnologia direto na caixa de entrada.</p>
<form className="flex flex-col gap-3">
<input type="email" placeholder="Seu email..." className="w-full bg-gray-800 border border-gray-700 text-white rounded px-4 py-2.5 text-sm focus:outline-none focus:border-brand-primary" />
<button className="bg-brand-primary text-white font-bold text-sm px-4 py-2.5 rounded hover:bg-opacity-90 transition-all">Inscrever-se</button>
</form>
</div>
</div>
</div>
<div className="container-inner border-t border-gray-800 pt-8 flex flex-col md:flex-row justify-between items-center text-xs font-medium">
<p>&copy; 2026 CodeChronicle Labs. Todos os direitos reservados.</p>
<div className="flex gap-4 mt-4 md:mt-0">
<Link to={`/${lang}/privacy`} className="hover:text-white">Privacidade</Link>
<Link to={`/${lang}/terms`} className="hover:text-white">Termos de Serviço</Link>
</div>
</div>
</footer>
</div>
);
};

View file

@ -0,0 +1,46 @@
import React from 'react';
import { useI18n } from '../hooks/useI18n';
import { Mail } from 'lucide-react';
export const Newsletter = () => {
const { lang } = useI18n();
return (
<section className="py-20 bg-brand-primary text-white">
<div className="container-inner">
<div className="flex flex-col md:flex-row items-center justify-between gap-10">
<div className="flex-1 text-center md:text-left">
<h2 className="text-3xl font-bold font-heading mb-4">
Assine nossa Newsletter
</h2>
<p className="text-white/80 max-w-lg mx-auto md:mx-0">
Receba as últimas novidades, tutoriais de desenvolvimento web e análises de tecnologia diretamente na sua caixa de entrada.
</p>
</div>
<div className="w-full max-w-md">
<form
className="flex flex-col sm:flex-row gap-3"
onSubmit={(e) => e.preventDefault()}
>
<input
type="email"
placeholder="Seu melhor e-mail"
className="flex-1 bg-white border-none rounded px-4 py-3 text-gray-900 focus:outline-none focus:ring-2 focus:ring-white/50"
required
/>
<button
className="bg-gray-900 text-white font-bold px-6 py-3 rounded hover:bg-gray-800 transition-colors flex items-center justify-center gap-2 whitespace-nowrap"
>
<Mail size={18} /> Assinar
</button>
</form>
<p className="text-xs text-white/60 mt-3 text-center md:text-left">
Não enviamos spam. Cancele sua assinatura quando quiser.
</p>
</div>
</div>
</div>
</section>
);
};

View file

@ -0,0 +1,110 @@
import React from 'react';
import { Post } from '../types';
import { Link } from 'react-router-dom';
import { useI18n } from '../hooks/useI18n';
import { formatDate } from '../lib/utils';
import { Clock } from 'lucide-react';
interface PostCardProps {
post: Post;
variant?: 'grid' | 'list' | 'overlay';
}
export const PostCard: React.FC<PostCardProps> = ({ post, variant = 'grid' }) => {
const { lang } = useI18n();
if (variant === 'overlay') {
return (
<Link to={`/${lang}/blog/${post.slug}`} className="group relative block w-full h-full overflow-hidden rounded-md">
<img
src={post.image}
alt={post.title}
referrerPolicy="no-referrer"
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
/>
<div className="absolute inset-0 bg-gradient-to-t from-gray-900 via-gray-900/40 to-transparent opacity-80" />
<div className="absolute bottom-0 left-0 w-full p-6">
<span className="bg-brand-primary text-white text-[10px] font-bold uppercase tracking-wider px-2 py-1 rounded inline-block mb-3">
{post.category}
</span>
<h3 className="text-white text-xl md:text-2xl font-bold font-heading line-clamp-2 leading-tight mb-3 group-hover:underline">
{post.title}
</h3>
<div className="flex items-center text-gray-300 text-[11px] font-medium gap-3">
<span>{post.author}</span>
<span>-</span>
<span>{formatDate(post.date, lang)}</span>
</div>
</div>
</Link>
);
}
if (variant === 'list') {
return (
<div className="flex flex-col sm:flex-row gap-5 group items-start">
<Link to={`/${lang}/blog/${post.slug}`} className="w-full sm:w-1/3 aspect-[4/3] relative overflow-hidden rounded-md shrink-0">
<img
src={post.image}
alt={post.title}
referrerPolicy="no-referrer"
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
/>
<div className="absolute top-2 left-2">
<span className="bg-brand-primary text-white text-[10px] font-bold uppercase tracking-wider px-2 py-1 rounded shadow-sm">
{post.category}
</span>
</div>
</Link>
<div className="flex-grow flex flex-col justify-center">
<h3 className="text-xl font-bold font-heading text-gray-900 line-clamp-2 leading-tight mb-3 group-hover:text-brand-primary transition-colors">
<Link to={`/${lang}/blog/${post.slug}`}>{post.title}</Link>
</h3>
<div className="flex items-center text-gray-500 text-[12px] font-medium gap-3 mb-3">
<span className="text-gray-800 font-bold">{post.author}</span>
<span>-</span>
<span>{formatDate(post.date, lang)}</span>
</div>
<p className="text-gray-600 text-[14px] line-clamp-2 leading-relaxed">
{post.description}
</p>
</div>
</div>
);
}
return (
<div className="flex flex-col group h-full">
<Link to={`/${lang}/blog/${post.slug}`} className="relative aspect-[16/10] overflow-hidden rounded-md mb-4 block">
<img
src={post.image}
alt={post.title}
referrerPolicy="no-referrer"
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
/>
<div className="absolute top-3 left-3">
<span className="bg-brand-primary text-white text-[10px] font-bold uppercase tracking-wider px-2 py-1 rounded shadow-sm">
{post.category}
</span>
</div>
</Link>
<div className="flex flex-col flex-grow">
<h3 className="text-[18px] font-bold font-heading mb-3 text-gray-900 group-hover:text-brand-primary transition-colors leading-tight line-clamp-2">
<Link to={`/${lang}/blog/${post.slug}`}>{post.title}</Link>
</h3>
<div className="flex items-center text-gray-500 text-[11px] font-medium gap-3 mb-3">
<span className="text-gray-800 font-bold">{post.author}</span>
<span>-</span>
<span>{formatDate(post.date, lang)}</span>
<span className="flex items-center gap-1"><Clock size={12}/> {post.readingTime}</span>
</div>
<p className="text-gray-600 text-[14px] line-clamp-2 leading-relaxed mt-auto">
{post.description}
</p>
</div>
</div>
);
};

View file

@ -0,0 +1,127 @@
import React from 'react';
import { Helmet } from 'react-helmet-async';
import { useLocation } from 'react-router-dom';
interface SEOProps {
title: string;
description: string;
image?: string;
article?: boolean;
author?: string;
datePublished?: string;
category?: string;
}
export const SEO: React.FC<SEOProps> = ({
title,
description,
image,
article,
author = 'Vanta Architecture Team',
datePublished,
category
}) => {
const siteName = 'CodeChronicle';
const fullTitle = `${title} | ${siteName}`;
const location = useLocation();
const canonicalUrl = `https://codechronicle.com${location.pathname}`;
const defaultImage = 'https://images.unsplash.com/photo-1620121692029-d088224efc74?q=80&w=2000';
const ogImage = image || defaultImage;
const structuredData = article ? {
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": title,
"description": description,
"image": ogImage,
"author": {
"@type": "Person",
"name": author
},
"datePublished": datePublished,
"mainEntityOfPage": {
"@type": "WebPage",
"@id": canonicalUrl
},
"publisher": {
"@type": "Organization",
"name": siteName,
"logo": {
"@type": "ImageObject",
"url": "https://codechronicle.com/logo.png"
}
}
} : {
"@context": "https://schema.org",
"@type": "WebSite",
"name": siteName,
"url": "https://codechronicle.com",
"description": description
};
const breadcrumbData = article ? {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": `https://codechronicle.com/${location.pathname.split('/')[1]}`
},
{
"@type": "ListItem",
"position": 2,
"name": "Blog",
"item": `https://codechronicle.com/${location.pathname.split('/')[1]}/blog`
},
{
"@type": "ListItem",
"position": 3,
"name": title,
"item": canonicalUrl
}
]
} : null;
return (
<Helmet
htmlAttributes={{
lang: location.pathname.split('/')[1] || 'pt'
}}
>
{/* Basic Meta Tags */}
<title>{fullTitle}</title>
<meta name="description" content={description} />
<link rel="canonical" href={canonicalUrl} />
{/* Open Graph / Facebook */}
<meta property="og:site_name" content={siteName} />
<meta property="og:url" content={canonicalUrl} />
<meta property="og:title" content={fullTitle} />
<meta property="og:description" content={description} />
<meta property="og:type" content={article ? 'article' : 'website'} />
<meta property="og:image" content={ogImage} />
{article && category && <meta property="article:section" content={category} />}
{article && datePublished && <meta property="article:published_time" content={datePublished} />}
{/* Twitter */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:domain" content="codechronicle.com" />
<meta name="twitter:url" content={canonicalUrl} />
<meta name="twitter:title" content={fullTitle} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={ogImage} />
{/* Google Rich Results */}
<script type="application/ld+json">
{JSON.stringify(structuredData)}
</script>
{breadcrumbData && (
<script type="application/ld+json">
{JSON.stringify(breadcrumbData)}
</script>
)}
</Helmet>
);
};

View file

@ -0,0 +1,12 @@
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
export const ScrollToTop = () => {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
};

View file

@ -0,0 +1,30 @@
import React from 'react';
import { ArrowRight } from 'lucide-react';
interface TOCProps {
content: string;
}
export const TableOfContents: React.FC<TOCProps> = ({ content }) => {
const headings = content.split('\n').filter(line => line.startsWith('## ')).map(line => line.replace('## ', '').trim());
if (headings.length === 0) return null;
return (
<nav className="flex flex-col">
{headings.map((heading, index) => (
<a
key={heading}
href={`#${heading.toLowerCase().replace(/ /g, '-')}`}
className="group flex items-center justify-between py-3 border-b border-white/5 text-[11px] font-black uppercase italic tracking-widest text-tech-muted hover:text-tech-primary transition-all overflow-hidden"
>
<span className="flex items-center gap-3">
<span className="text-[8px] text-white/20 group-hover:text-tech-primary transition-colors">0{index + 1}</span>
{heading}
</span>
<ArrowRight size={12} className="opacity-0 group-hover:opacity-100 transition-all -translate-x-4 group-hover:translate-x-0" />
</a>
))}
</nav>
);
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,26 @@
import { useLocation, useNavigate, useParams } from 'react-router-dom';
import { content } from '../content';
import { Language } from '../types';
export function useI18n() {
const { lang } = useParams<{ lang?: string }>();
const location = useLocation();
const navigate = useNavigate();
const currentLang: Language = (lang as Language) || 'pt';
const t = content.ui[currentLang];
const posts = content.posts[currentLang];
const changeLanguage = (newLang: Language) => {
const pathParts = location.pathname.split('/');
pathParts[1] = newLang; // Replace the language segment
navigate(pathParts.join('/'));
};
return {
lang: currentLang,
t,
posts,
changeLanguage,
};
}

71
Template-03/src/index.css Normal file
View file

@ -0,0 +1,71 @@
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700;900&family=Montserrat:wght@400;600;700;800;900&display=swap');
@import "tailwindcss";
@theme {
--font-sans: "Roboto", system-ui, sans-serif;
--font-heading: "Montserrat", sans-serif;
--color-brand-primary: #0088cc;
--color-brand-secondary: #005580;
--color-text-main: #333333;
--color-text-muted: #777777;
--color-bg-light: #f5f5f5;
--color-border: #eeeeee;
}
@layer base {
body {
@apply bg-bg-light text-text-main antialiased selection:bg-brand-primary selection:text-white;
}
h1, h2, h3, h4, h5, h6 {
@apply font-heading tracking-tight font-bold text-gray-900;
}
}
@layer components {
.container-inner {
@apply max-w-[1200px] mx-auto px-4;
}
/* Article Content Styles */
.prose-custom {
@apply text-text-main leading-[1.8] text-[17px] font-sans;
}
.prose-custom h2 {
@apply text-[28px] font-bold mt-10 mb-5 relative pb-3 border-b border-gray-200;
}
.prose-custom h3 {
@apply text-[22px] font-bold mt-8 mb-4;
}
.prose-custom p {
@apply mb-6;
}
.prose-custom a {
@apply text-brand-primary font-medium hover:underline;
}
.prose-custom ul {
@apply list-disc list-inside mb-6 space-y-2;
}
.prose-custom blockquote {
@apply border-l-4 border-brand-primary pl-5 py-2 my-8 italic text-xl font-medium text-gray-700 bg-gray-50;
}
.prose-custom img {
@apply my-8 rounded-md shadow-sm;
}
.prose-custom pre {
@apply bg-gray-900 text-gray-100 p-5 rounded-md my-8 overflow-x-auto text-sm font-mono leading-relaxed;
}
.prose-custom code {
@apply bg-gray-100 text-brand-primary px-1.5 py-0.5 rounded text-[0.9em];
}
}

View file

@ -0,0 +1,14 @@
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
export function formatDate(dateString: string, lang: string) {
return new Date(dateString).toLocaleDateString(lang, {
year: 'numeric',
month: 'long',
day: 'numeric',
});
}

10
Template-03/src/main.tsx Normal file
View file

@ -0,0 +1,10 @@
import {StrictMode} from 'react';
import {createRoot} from 'react-dom/client';
import App from './App.tsx';
import './index.css';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
);

View file

@ -0,0 +1,37 @@
import React from 'react';
import { useI18n } from '../hooks/useI18n';
import { SEO } from '../components/SEO';
export default function About() {
const { lang } = useI18n();
return (
<>
<SEO title="Sobre Nós" description="Conheça a história e a equipe por trás do CodeChronicle." />
<div className="bg-gray-50 min-h-screen py-20">
<div className="container-inner max-w-3xl mx-auto">
<div className="bg-white p-8 md:p-12 border border-gray-200 rounded-md">
<h1 className="text-3xl md:text-4xl font-bold font-heading text-gray-900 mb-8">Sobre Nós</h1>
<div className="prose-custom">
<p className="text-lg leading-relaxed text-gray-700 mb-6">
Bem-vindo ao <strong>CodeChronicle</strong>, sua fonte diária das melhores notícias de tecnologia,
tutoriais de desenvolvimento web e análises de software.
</p>
<h2 className="text-xl font-bold font-heading text-gray-900 mt-10 mb-4">Nossa Missão</h2>
<p className="text-gray-700 mb-6">
Nossa missão é democratizar o conhecimento em programação e ajudar desenvolvedores de
todos os níveis a aprimorarem suas habilidades por meio de tutoriais práticos, artigos de
código aberto e guias aprofundados.
</p>
<h2 className="text-xl font-bold font-heading text-gray-900 mt-10 mb-4">Por que criamos o site?</h2>
<p className="text-gray-700 mb-6">
Com as rápidas mudanças no cenário tecnológico moderno, nós entendemos que a comunidade
precisava de um espaço seguro, rápido e sem complicações para se manter atualizada e discutir as novidades em ferramentas e arquiteturas.
</p>
</div>
</div>
</div>
</div>
</>
);
}

View file

@ -0,0 +1,156 @@
import React from 'react';
import { useParams, Link, useSearchParams } from 'react-router-dom';
import { useI18n } from '../hooks/useI18n';
import { PostCard } from '../components/PostCard';
import { SEO } from '../components/SEO';
import { cn } from '../lib/utils';
import { Facebook, Twitter, Instagram, Youtube } from 'lucide-react';
const SocialWidget = () => (
<div className="bg-white p-6 border border-gray-200 rounded-md">
<h4 className="text-sm font-bold font-heading uppercase tracking-wider mb-5 pb-3 border-b border-gray-200">Fique Conectado</h4>
<div className="grid grid-cols-2 gap-3">
<div className="flex flex-col items-center justify-center p-4 bg-blue-50 text-blue-600 rounded">
<Facebook size={24} className="mb-2" />
<span className="text-xs font-bold">128k</span>
<span className="text-[10px] text-gray-500 uppercase">Fãs</span>
</div>
<div className="flex flex-col items-center justify-center p-4 bg-sky-50 text-sky-500 rounded">
<Twitter size={24} className="mb-2" />
<span className="text-xs font-bold">85k</span>
<span className="text-[10px] text-gray-500 uppercase">Seguidores</span>
</div>
<div className="flex flex-col items-center justify-center p-4 bg-pink-50 text-pink-600 rounded">
<Instagram size={24} className="mb-2" />
<span className="text-xs font-bold">450k</span>
<span className="text-[10px] text-gray-500 uppercase">Seguidores</span>
</div>
<div className="flex flex-col items-center justify-center p-4 bg-red-50 text-red-600 rounded">
<Youtube size={24} className="mb-2" />
<span className="text-xs font-bold">1.2M</span>
<span className="text-[10px] text-gray-500 uppercase">Inscritos</span>
</div>
</div>
</div>
);
export default function BlogList() {
const { category } = useParams<{ category?: string }>();
const [searchParams] = useSearchParams();
const { lang, posts, t } = useI18n();
const currentLang = lang || 'pt';
const query = searchParams.get('q');
const validPosts = (posts || []).filter(p => !['privacy-policy', 'terms-of-service'].includes(p.id));
const categories = ['all', 'ai', 'code', 'startups', 'tools'];
let filteredPosts = category && category !== 'all'
? validPosts.filter(p => p.category.toLowerCase() === category.toLowerCase())
: validPosts;
if (query) {
const q = query.toLowerCase();
filteredPosts = filteredPosts.filter(p =>
p.title.toLowerCase().includes(q) ||
(p.description && p.description.toLowerCase().includes(q)) ||
p.content.toLowerCase().includes(q)
);
}
const activeCategory = category || 'all';
return (
<>
<SEO
title={category ? `${category.toUpperCase()} Novidades` : "Últimos Tutoriais"}
description="Explore nossos artigos sobre tecnologia, programação e IA."
/>
<div className="bg-gray-50 min-h-screen pb-24 pt-10">
<div className="container-inner">
<div className="flex flex-wrap gap-4 mb-10 border-b border-gray-200 pb-4">
{categories.map((cat) => (
<Link
key={cat}
to={cat === 'all' ? `/${currentLang}/blog` : `/${currentLang}/blog/category/${cat}`}
className={cn(
"px-4 py-2 font-bold uppercase tracking-wider text-xs rounded transition-colors",
activeCategory === cat
? "bg-brand-primary text-white"
: "bg-white border border-gray-200 text-gray-600 hover:text-brand-primary hover:border-brand-primary"
)}
>
{t.blog.categories[cat as keyof typeof t.blog.categories] || cat}
</Link>
))}
</div>
<div className="grid grid-cols-1 lg:grid-cols-12 gap-10">
{/* Main Content */}
<main className="lg:col-span-8">
<div className="flex items-center justify-between border-b-2 border-brand-primary pb-2 mb-8">
<h2 className="text-[20px] font-bold font-heading uppercase text-gray-900 bg-brand-primary text-white px-4 py-1.5 -mb-[10px]">
{query ? `Busca: "${query}"` : category ? `Artigos de ${category}` : "Todos os Artigos"}
</h2>
</div>
<div className="flex flex-col gap-8">
{filteredPosts.map((post) => (
<div key={post.id} className="pb-8 border-b border-gray-100 last:border-0 last:pb-0">
<PostCard post={post} variant="list" />
</div>
))}
</div>
{filteredPosts.length === 0 && (
<div className="text-center py-20 text-gray-500 font-bold bg-white border border-gray-200 rounded-md mt-10">
{query ? `Nenhum artigo encontrado para "${query}".` : "Nenhum artigo encontrado nesta categoria."}
</div>
)}
{filteredPosts.length > 0 && (
<div className="mt-12 flex justify-center">
<button className="bg-white border border-gray-200 text-gray-900 font-bold font-heading text-sm px-8 py-3 rounded hover:bg-brand-primary hover:text-white hover:border-brand-primary transition-colors">
Carregar Mais Artigos
</button>
</div>
)}
</main>
{/* Sidebar */}
<aside className="lg:col-span-4 space-y-10">
{/* Social Widget */}
<SocialWidget />
{/* Categories Widget */}
<div className="bg-white p-6 border border-gray-200 rounded-md">
<h4 className="text-[16px] font-bold font-heading uppercase border-b border-gray-200 pb-3 mb-4">
Categorias
</h4>
<ul className="flex flex-col">
<li className="border-b border-gray-100">
<Link to={`/${currentLang}/blog/category/code`} className="flex justify-between items-center py-2.5 text-[14px] text-gray-700 hover:text-brand-primary font-medium">
Dev Web <span className="bg-gray-100 text-gray-500 text-[10px] px-2 py-0.5 rounded">12</span>
</Link>
</li>
<li className="border-b border-gray-100">
<Link to={`/${currentLang}/blog/category/ai`} className="flex justify-between items-center py-2.5 text-[14px] text-gray-700 hover:text-brand-primary font-medium">
Machine Learning <span className="bg-gray-100 text-gray-500 text-[10px] px-2 py-0.5 rounded">8</span>
</Link>
</li>
<li className="border-b border-gray-100">
<Link to={`/${currentLang}/blog/category/tools`} className="flex justify-between items-center py-2.5 text-[14px] text-gray-700 hover:text-brand-primary font-medium">
DevOps & Cloud <span className="bg-gray-100 text-gray-500 text-[10px] px-2 py-0.5 rounded">5</span>
</Link>
</li>
</ul>
</div>
</aside>
</div>
</div>
</div>
</>
);
}

View file

@ -0,0 +1,94 @@
import React from 'react';
import { useI18n } from '../hooks/useI18n';
import { SEO } from '../components/SEO';
import { Send, MapPin, Mail, Phone } from 'lucide-react';
export default function Contact() {
const { lang, t } = useI18n();
return (
<>
<SEO title="Contato" description="Entre em contato conosco." />
<div className="bg-gray-50 min-h-screen py-20">
<div className="container-inner max-w-5xl mx-auto">
<div className="bg-white p-8 md:p-12 border border-gray-200 rounded-md">
<h1 className="text-3xl md:text-4xl font-bold font-heading text-gray-900 mb-8">Entre em Contato</h1>
<p className="text-gray-700 max-w-2xl text-lg mb-12">
Você tem alguma dúvida, sugestão ou proposta de parceria? Ficaremos felizes em ouvir você. Preencha o formulário abaixo e entraremos em contato o mais rápido possível.
</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-16">
<div>
<form className="space-y-6">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Nome</label>
<input type="text" className="w-full bg-gray-50 border border-gray-200 rounded p-3 text-sm focus:outline-none focus:border-brand-primary focus:bg-white" placeholder="Seu nome" />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">E-mail</label>
<input type="email" className="w-full bg-gray-50 border border-gray-200 rounded p-3 text-sm focus:outline-none focus:border-brand-primary focus:bg-white" placeholder="seu@email.com" />
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Assunto</label>
<input type="text" className="w-full bg-gray-50 border border-gray-200 rounded p-3 text-sm focus:outline-none focus:border-brand-primary focus:bg-white" placeholder="Como podemos ajudar?" />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Mensagem</label>
<textarea rows={5} className="w-full bg-gray-50 border border-gray-200 rounded p-3 text-sm focus:outline-none focus:border-brand-primary focus:bg-white" placeholder="Escreva sua mensagem..."></textarea>
</div>
<button type="submit" className="bg-brand-primary text-white font-bold px-8 py-3 rounded hover:bg-opacity-90 transition-opacity flex items-center gap-2">
Enviar Mensagem <Send size={16} />
</button>
</form>
</div>
<div className="space-y-10">
<div>
<h3 className="text-lg font-bold font-heading text-gray-900 mb-6">Informações de Contato</h3>
<ul className="space-y-6">
<li className="flex items-start gap-4">
<div className="w-12 h-12 bg-gray-50 text-brand-primary rounded flex items-center justify-center shrink-0">
<Mail size={20} />
</div>
<div>
<span className="block text-sm font-bold text-gray-900 mb-1">E-mail</span>
<a href="mailto:hello@codechronicle.com" className="text-gray-600 hover:text-brand-primary transition-colors">hello@codechronicle.com</a>
</div>
</li>
<li className="flex items-start gap-4">
<div className="w-12 h-12 bg-gray-50 text-brand-primary rounded flex items-center justify-center shrink-0">
<Phone size={20} />
</div>
<div>
<span className="block text-sm font-bold text-gray-900 mb-1">Telefone</span>
<span className="text-gray-600">(11) 98765-4321</span>
</div>
</li>
<li className="flex items-start gap-4">
<div className="w-12 h-12 bg-gray-50 text-brand-primary rounded flex items-center justify-center shrink-0">
<MapPin size={20} />
</div>
<div>
<span className="block text-sm font-bold text-gray-900 mb-1">Localização</span>
<span className="text-gray-600">São Paulo, SP - Brasil<br />Trabalhamos remotamente</span>
</div>
</li>
</ul>
</div>
<div className="bg-gray-50 p-6 rounded border border-gray-200">
<h4 className="font-bold text-gray-900 mb-2">Suporte Técnico</h4>
<p className="text-sm text-gray-600 leading-relaxed mb-4">
Para questões não relacionadas a negócios, por favor acesse nosso fórum comunitário ou consulte nossas seções de perguntas frequentes nos artigos.
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
}

View file

@ -0,0 +1,172 @@
import React from 'react';
import { Link, useParams } from 'react-router-dom';
import {
ChevronRight,
TrendingUp,
Facebook,
Twitter,
Instagram,
Youtube
} from 'lucide-react';
import { useI18n } from '../hooks/useI18n';
import { Language } from '../types';
import { PostCard } from '../components/PostCard';
import { SEO } from '../components/SEO';
const SocialWidget = () => (
<div className="bg-white p-6 border border-gray-200 rounded-md">
<h4 className="text-sm font-bold font-heading uppercase tracking-wider mb-5 pb-3 border-b border-gray-200">Fique Conectado</h4>
<div className="grid grid-cols-2 gap-3">
<div className="flex flex-col items-center justify-center p-4 bg-blue-50 text-blue-600 rounded">
<Facebook size={24} className="mb-2" />
<span className="text-xs font-bold">128k</span>
<span className="text-[10px] text-gray-500 uppercase">Fãs</span>
</div>
<div className="flex flex-col items-center justify-center p-4 bg-sky-50 text-sky-500 rounded">
<Twitter size={24} className="mb-2" />
<span className="text-xs font-bold">85k</span>
<span className="text-[10px] text-gray-500 uppercase">Seguidores</span>
</div>
<div className="flex flex-col items-center justify-center p-4 bg-pink-50 text-pink-600 rounded">
<Instagram size={24} className="mb-2" />
<span className="text-xs font-bold">450k</span>
<span className="text-[10px] text-gray-500 uppercase">Seguidores</span>
</div>
<div className="flex flex-col items-center justify-center p-4 bg-red-50 text-red-600 rounded">
<Youtube size={24} className="mb-2" />
<span className="text-xs font-bold">1.2M</span>
<span className="text-[10px] text-gray-500 uppercase">Inscritos</span>
</div>
</div>
</div>
);
export default function Home() {
const { t, posts, lang } = useI18n();
const currentLang = (lang as Language) || 'pt';
const validPosts = (posts || []).filter(p => !['privacy-policy', 'terms-of-service'].includes(p.id));
if (validPosts.length === 0) return null;
const heroLarge = validPosts[0];
const heroSmall = validPosts.slice(1, 3);
const latestPosts = validPosts.slice(3, 9);
const trendingPosts = validPosts.slice(0, 4);
return (
<div className="bg-white pb-20">
<SEO title={t.home.hero.title} description={t.home.hero.subtitle} />
{/* Hero Grid Section */}
<section className="container-inner pt-4 pb-12">
<div className="grid grid-cols-1 lg:grid-cols-12 gap-4">
{/* Main Left Feature */}
<div className="lg:col-span-8">
<div className="w-full h-[300px] md:h-[450px]">
<PostCard post={heroLarge} variant="overlay" />
</div>
</div>
{/* Right Stack */}
<div className="lg:col-span-4 flex flex-col gap-4">
{heroSmall.map(post => (
<div key={post.id} className="h-[200px] md:h-[217px]">
<PostCard post={post} variant="overlay" />
</div>
))}
</div>
</div>
</section>
{/* Main Content Area */}
<section className="container-inner mt-8">
<div className="grid grid-cols-1 lg:grid-cols-12 gap-10">
{/* Main Column */}
<main className="lg:col-span-8">
<div className="flex items-center justify-between border-b-2 border-brand-primary pb-2 mb-8">
<h2 className="text-[20px] font-bold font-heading uppercase text-gray-900 bg-brand-primary text-white px-4 py-1.5 -mb-[10px]">
Últimas Notícias
</h2>
<Link to={`/${currentLang}/blog`} className="text-[12px] font-bold text-gray-500 hover:text-brand-primary transition-colors flex items-center">
Ver Tudo <ChevronRight size={14} />
</Link>
</div>
<div className="flex flex-col gap-8">
{latestPosts.map(post => (
<div key={post.id} className="pb-8 border-b border-gray-100 last:border-0 last:pb-0">
<PostCard post={post} variant="list" />
</div>
))}
</div>
<div className="mt-12 flex justify-center">
<Link to={`/${currentLang}/blog`} className="bg-brand-primary text-white font-bold text-sm px-8 py-3 rounded hover:bg-opacity-90 transition-colors">
Carregar Mais Artigos
</Link>
</div>
</main>
{/* Sidebar */}
<aside className="lg:col-span-4 space-y-10">
{/* Social Widget */}
<SocialWidget />
{/* Trending Widget */}
<div className="bg-white">
<h4 className="text-[16px] font-bold font-heading uppercase border-b-2 border-gray-900 pb-2 mb-6">
<span className="bg-gray-900 text-white px-3 py-1">Em Alta</span>
</h4>
<div className="flex flex-col gap-6">
{trendingPosts.map((post, idx) => (
<div key={post.id} className="flex gap-4 group">
<span className="text-4xl font-bold font-heading text-gray-200 mt-1">{idx + 1}</span>
<div>
<h5 className="font-bold text-gray-900 text-[15px] leading-tight mb-2 group-hover:text-brand-primary transition-colors">
<Link to={`/${currentLang}/blog/${post.slug}`}>{post.title}</Link>
</h5>
<span className="text-[11px] text-gray-500 font-medium">{post.date}</span>
</div>
</div>
))}
</div>
</div>
{/* Categories Widget */}
<div className="bg-white">
<h4 className="text-[16px] font-bold font-heading uppercase border-b-2 border-gray-900 pb-2 mb-6">
Categorias
</h4>
<ul className="flex flex-col">
<li className="border-b border-gray-100">
<Link to={`/${currentLang}/blog/category/code`} className="flex justify-between items-center py-3 text-[14px] text-gray-700 hover:text-brand-primary font-medium">
Dev Web <span className="bg-gray-100 text-gray-500 text-[10px] px-2 py-0.5 rounded">12</span>
</Link>
</li>
<li className="border-b border-gray-100">
<Link to={`/${currentLang}/blog/category/ai`} className="flex justify-between items-center py-3 text-[14px] text-gray-700 hover:text-brand-primary font-medium">
Machine Learning <span className="bg-gray-100 text-gray-500 text-[10px] px-2 py-0.5 rounded">8</span>
</Link>
</li>
<li className="border-b border-gray-100">
<Link to={`/${currentLang}/blog/category/tools`} className="flex justify-between items-center py-3 text-[14px] text-gray-700 hover:text-brand-primary font-medium">
DevOps & Cloud <span className="bg-gray-100 text-gray-500 text-[10px] px-2 py-0.5 rounded">5</span>
</Link>
</li>
</ul>
</div>
</aside>
</div>
</section>
</div>
);
}

View file

@ -0,0 +1,53 @@
import React from 'react';
import { useI18n } from '../hooks/useI18n';
import { SEO } from '../components/SEO';
import { Shield, FileText, Scale } from 'lucide-react';
import { useLocation } from 'react-router-dom';
export default function Legal() {
const { t, lang } = useI18n();
const location = useLocation();
const isPrivacy = location.pathname.includes('privacy');
const isTerms = location.pathname.includes('terms');
const isEthics = location.pathname.includes('ethics');
let config = t.legal.privacy;
let Icon = Shield;
if (isTerms) {
config = t.legal.terms;
Icon = FileText;
} else if (isEthics) {
config = t.legal.ethics;
Icon = Scale;
}
return (
<>
<SEO title={config.title} description={config.content} />
<div className="bg-gray-50 min-h-screen py-20">
<div className="container-inner max-w-4xl mx-auto">
<div className="bg-white p-8 md:p-12 border border-gray-200 rounded-md">
<div className="flex items-center gap-4 mb-8">
<div className="w-12 h-12 bg-gray-50 text-brand-primary rounded flex items-center justify-center shrink-0">
<Icon size={24} />
</div>
<h1 className="text-3xl md:text-4xl font-bold font-heading text-gray-900">{config.title}</h1>
</div>
<article className="prose-custom max-w-none">
<div className="whitespace-pre-line text-gray-700 leading-relaxed text-lg">
{config.content}
</div>
</article>
<div className="mt-12 pt-8 border-t border-gray-100 text-sm text-gray-500 font-medium">
Última atualização: {new Date().toLocaleDateString('pt-BR')}
</div>
</div>
</div>
</div>
</>
);
}

View file

@ -0,0 +1,44 @@
import React from 'react';
import { SEO } from '../components/SEO';
import { Link } from 'react-router-dom';
import { useI18n } from '../hooks/useI18n';
export default function Login() {
const { lang } = useI18n();
return (
<>
<SEO title="Entrar" description="Faça o login em sua conta." />
<div className="bg-gray-50 min-h-[calc(100vh-200px)] py-20 flex items-center justify-center">
<div className="container-inner w-full max-w-md mx-auto">
<div className="bg-white p-8 md:p-10 border border-gray-200 rounded-md text-center shadow-sm">
<h1 className="text-2xl font-bold font-heading text-gray-900 mb-2">Bem-vindo de volta!</h1>
<p className="text-sm text-gray-500 mb-8">Faça login com a sua conta para continuar.</p>
<form className="space-y-5 text-left">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1.5">Endereço de E-mail</label>
<input type="email" className="w-full bg-gray-50 border border-gray-200 rounded p-3 text-sm focus:outline-none focus:border-brand-primary focus:bg-white" placeholder="seu@email.com" />
</div>
<div>
<div className="flex justify-between items-center mb-1.5">
<label className="block text-sm font-medium text-gray-700">Senha</label>
<a href="#" className="text-xs text-brand-primary hover:underline">Esqueceu a senha?</a>
</div>
<input type="password" className="w-full bg-gray-50 border border-gray-200 rounded p-3 text-sm focus:outline-none focus:border-brand-primary focus:bg-white" placeholder="Sua senha secreta" />
</div>
<button type="submit" className="w-full bg-brand-primary text-white font-bold py-3.5 rounded mt-4 hover:bg-opacity-90 transition-opacity">
Entrar
</button>
</form>
<div className="mt-8 pt-8 border-t border-gray-100 text-sm">
<span className="text-gray-500">Não tem uma conta ainda? </span>
<a href="#" className="font-bold text-brand-primary hover:underline">Criar agora</a>
</div>
</div>
</div>
</div>
</>
);
}

View file

@ -0,0 +1,213 @@
import React, { useMemo } from 'react';
import { useParams, Link } from 'react-router-dom';
import Markdown from 'react-markdown';
import { Helmet } from 'react-helmet-async';
import {
Calendar,
Clock,
User,
Share2,
Facebook,
Twitter,
Linkedin,
Folder,
MessageSquare,
Instagram
} from 'lucide-react';
import { useI18n } from '../hooks/useI18n';
import { formatDate } from '../lib/utils';
import { TableOfContents } from '../components/TableOfContents';
import { Language } from '../types';
export default function PostDetail() {
const { lang, slug } = useParams<{ lang: string; slug: string }>();
const currentLang = (lang as Language) || 'pt';
const { posts } = useI18n();
const post = useMemo(() =>
posts.find(p => p.slug === slug),
[posts, slug]
);
if (!post) return (
<div className="min-h-screen pt-40 px-6 text-center bg-gray-50">
<h1 className="text-4xl font-bold font-heading text-gray-900 mb-8">404: Page Not Found</h1>
<Link to={`/${currentLang}/blog`} className="bg-brand-primary text-white font-bold px-8 py-3 rounded">Return to Home</Link>
</div>
);
const relatedPosts = posts
.filter(p => p.id !== post.id)
.slice(0, 3);
return (
<div className="bg-gray-50 min-h-screen pb-20">
<Helmet>
<title>{post.title} | CodeChronicle</title>
<meta name="description" content={post.description} />
</Helmet>
{/* Main Top Header */}
<div className="bg-white border-b border-gray-200 pt-16 pb-12 mb-10">
<div className="container-inner">
<div className="max-w-4xl mx-auto text-center">
<div className="flex justify-center items-center gap-2 mb-6">
<span className="bg-brand-primary text-white text-[10px] font-bold uppercase tracking-wider px-2 py-1 rounded">
{post.category}
</span>
</div>
<h1 className="text-3xl md:text-5xl font-bold font-heading text-gray-900 leading-tight mb-8">
{post.title}
</h1>
<div className="flex items-center justify-center gap-6 text-sm text-gray-500 font-medium">
<div className="flex items-center gap-2">
<img src="https://ui-avatars.com/api/?name=Admin&background=0088cc&color=fff" className="w-6 h-6 rounded-full" alt={post.author} />
<span className="text-gray-900 font-bold">{post.author}</span>
</div>
<div className="flex items-center gap-1.5 hidden sm:flex">
<Calendar size={14} />
{formatDate(post.date, currentLang)}
</div>
<div className="flex items-center gap-1.5 hidden sm:flex">
<Folder size={14} />
Tutoriais
</div>
<div className="flex items-center gap-1.5">
<MessageSquare size={14} />
0 Comentários
</div>
</div>
</div>
</div>
</div>
<div className="container-inner">
<div className="grid grid-cols-1 lg:grid-cols-12 gap-10">
{/* Main Content */}
<main className="lg:col-span-8 bg-white p-6 md:p-10 border border-gray-200 rounded-md">
<div className="mb-10 rounded-md overflow-hidden shadow-sm">
<img src={post.image} alt={post.title} referrerPolicy="no-referrer" className="w-full h-auto" />
</div>
<div className="prose-custom max-w-none">
<Markdown
components={{
img: ({node, ...props}) => <img referrerPolicy="no-referrer" {...props} />
}}
>
{post.content}
</Markdown>
</div>
{/* Tags and Share */}
<div className="mt-12 pt-8 border-t border-gray-100 flex flex-col md:flex-row items-center justify-between gap-6">
<div className="flex items-center gap-2">
<span className="text-sm font-bold text-gray-900 mr-2">Tags:</span>
<span className="bg-gray-100 text-gray-600 px-3 py-1 text-xs font-medium rounded hover:bg-brand-primary hover:text-white transition-colors cursor-pointer">Tutorial</span>
<span className="bg-gray-100 text-gray-600 px-3 py-1 text-xs font-medium rounded hover:bg-brand-primary hover:text-white transition-colors cursor-pointer">Design</span>
</div>
<div className="flex items-center gap-3 border-l pl-6 border-gray-200">
<span className="text-sm font-bold text-gray-900">Compartilhar:</span>
<button className="w-8 h-8 rounded-full bg-blue-600 text-white flex items-center justify-center hover:opacity-80 transition-opacity"><Facebook size={14} /></button>
<button className="w-8 h-8 rounded-full bg-sky-500 text-white flex items-center justify-center hover:opacity-80 transition-opacity"><Twitter size={14} /></button>
<button className="w-8 h-8 rounded-full bg-blue-800 text-white flex items-center justify-center hover:opacity-80 transition-opacity"><Linkedin size={14} /></button>
</div>
</div>
{/* Author Box */}
<div className="mt-10 bg-gray-50 border border-gray-200 p-8 flex flex-col md:flex-row items-center md:items-start gap-6 rounded-md">
<img src="https://ui-avatars.com/api/?name=Admin&background=0088cc&color=fff&size=100" className="w-20 h-20 rounded-full shadow-sm" alt={post.author} />
<div className="text-center md:text-left">
<h4 className="text-xl font-bold font-heading text-gray-900 mb-2">{post.author}</h4>
<p className="text-gray-600 text-sm leading-relaxed mb-4">
Escritor Técnico Principal cobrindo arquitetura frontend moderna, padrões de design UI/UX e frameworks fullstack. Apaixonado por criar aplicações limpas e escaláveis.
</p>
</div>
</div>
{/* Prev/Next Post Link Mockup */}
<div className="mt-10 grid grid-cols-2 border-t border-b border-gray-100 divide-x divide-gray-100">
<div className="p-6 text-left hover:bg-gray-50 transition-colors cursor-pointer">
<span className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Artigo Anterior</span>
<h4 className="text-base font-bold font-heading text-gray-900 group-hover:text-brand-primary line-clamp-2">Como escolher a hospedagem certa para o seu blog</h4>
</div>
<div className="p-6 text-right hover:bg-gray-50 transition-colors cursor-pointer">
<span className="block text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Próximo Artigo</span>
<h4 className="text-base font-bold font-heading text-gray-900 group-hover:text-brand-primary line-clamp-2">Criando um Servidor Local</h4>
</div>
</div>
{/* Comments Mockup */}
<div className="mt-16">
<h3 className="text-2xl font-bold font-heading mb-8">Deixe uma Resposta</h3>
<p className="text-sm text-gray-500 mb-6">Seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *</p>
<form className="space-y-4">
<textarea placeholder="Comentário" rows={6} className="w-full bg-gray-50 border border-gray-200 rounded p-4 text-sm focus:outline-none focus:border-brand-primary focus:bg-white"></textarea>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<input type="text" placeholder="Nome *" className="w-full bg-gray-50 border border-gray-200 rounded p-4 text-sm focus:outline-none focus:border-brand-primary focus:bg-white" />
<input type="email" placeholder="Email *" className="w-full bg-gray-50 border border-gray-200 rounded p-4 text-sm focus:outline-none focus:border-brand-primary focus:bg-white" />
</div>
<button type="button" className="bg-brand-primary text-white font-bold px-8 py-3 rounded">Publicar Comentário</button>
</form>
</div>
</main>
{/* Sidebar */}
<aside className="lg:col-span-4 space-y-8 relative">
<div className="sticky top-24 space-y-8">
{/* Table of Contents */}
<div className="bg-white p-6 border border-gray-200 rounded-md">
<TableOfContents content={post.content} />
</div>
{/* Related/Trending Content */}
<div className="bg-white p-6 border border-gray-200 rounded-md">
<h4 className="text-sm font-bold font-heading uppercase tracking-wider mb-5 pb-3 border-b border-gray-200">Artigos Relacionados</h4>
<div className="flex flex-col gap-5">
{relatedPosts.map(p => (
<Link key={p.id} to={`/${currentLang}/blog/${p.slug}`} className="flex gap-4 group">
<img src={p.image} referrerPolicy="no-referrer" className="w-20 h-20 object-cover rounded shadow-sm" alt={p.title} />
<div className="flex-1">
<h5 className="text-[14px] font-bold font-heading text-gray-900 leading-tight mb-2 group-hover:text-brand-primary line-clamp-2">
{p.title}
</h5>
<span className="text-[11px] text-gray-500 font-medium">
{formatDate(p.date, currentLang)}
</span>
</div>
</Link>
))}
</div>
</div>
{/* Stay Connected */}
<div className="bg-white p-6 border border-gray-200 rounded-md">
<h4 className="text-sm font-bold font-heading uppercase tracking-wider mb-5 pb-3 border-b border-gray-200">Fique Conectado</h4>
<div className="flex flex-col gap-3">
<button className="flex items-center justify-between px-4 py-3 bg-blue-50 text-blue-600 rounded">
<div className="flex items-center gap-3"><Facebook size={18} /> <span className="text-sm font-bold">Facebook</span></div>
<span className="text-xs font-medium">128k Fãs</span>
</button>
<button className="flex items-center justify-between px-4 py-3 bg-sky-50 text-sky-500 rounded">
<div className="flex items-center gap-3"><Twitter size={18} /> <span className="text-sm font-bold">Twitter</span></div>
<span className="text-xs font-medium">85k Seguidores</span>
</button>
<button className="flex items-center justify-between px-4 py-3 bg-pink-50 text-pink-600 rounded">
<div className="flex items-center gap-3"><Instagram size={18} /> <span className="text-sm font-bold">Instagram</span></div>
<span className="text-xs font-medium">450k Seguidores</span>
</button>
</div>
</div>
</div>
</aside>
</div>
</div>
</div>
);
}

View file

@ -0,0 +1,50 @@
import React from 'react';
import { SEO } from '../components/SEO';
export default function Write() {
return (
<>
<SEO title="Escreva para Nós" description="Envie seus artigos de tecnologia e desenvolvimento web." />
<div className="bg-gray-50 min-h-screen py-20">
<div className="container-inner max-w-3xl mx-auto">
<div className="bg-white p-8 md:p-12 border border-gray-200 rounded-md">
<h1 className="text-3xl md:text-4xl font-bold font-heading text-gray-900 mb-8">Escreva para Nós</h1>
<div className="prose-custom mb-10">
<p className="text-gray-700">
Você é apaixonado por tecnologia e gosta de compartilhar seus conhecimentos?
Estamos sempre em busca de novos talentos interessados em produzir artigos sobre desenvolvimento web, ferramentas, IA e DevOps para a comunidade.
</p>
</div>
<form className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Nome Completo</label>
<input type="text" className="w-full bg-gray-50 border border-gray-200 rounded p-3 text-sm focus:outline-none focus:border-brand-primary focus:bg-white" placeholder="Seu nome" />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">E-mail</label>
<input type="email" className="w-full bg-gray-50 border border-gray-200 rounded p-3 text-sm focus:outline-none focus:border-brand-primary focus:bg-white" placeholder="seu@email.com" />
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Tópico ou Título Proposto</label>
<input type="text" className="w-full bg-gray-50 border border-gray-200 rounded p-3 text-sm focus:outline-none focus:border-brand-primary focus:bg-white" placeholder="Sobre o que você gostaria de escrever?" />
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Mensagem ou Resumo</label>
<textarea rows={5} className="w-full bg-gray-50 border border-gray-200 rounded p-3 text-sm focus:outline-none focus:border-brand-primary focus:bg-white" placeholder="Nos dê uma breve introdução de quem você é ou coloque o resumo de seu artigo..."></textarea>
</div>
<button type="submit" className="bg-brand-primary text-white font-bold px-8 py-3 rounded hover:bg-opacity-90 transition-opacity">
Enviar Proposta
</button>
</form>
</div>
</div>
</div>
</>
);
}

97
Template-03/src/types.ts Normal file
View file

@ -0,0 +1,97 @@
export type Language = 'pt' | 'en' | 'es';
export interface Post {
id: string;
slug: string;
title: string;
description: string;
content: string;
category: string;
author: string;
date: string;
readingTime: string;
image: string;
featured?: boolean;
}
export interface Translations {
nav: {
home: string;
blog: string;
about: string;
categories: string;
};
home: {
hero: {
title: string;
subtitle: string;
cta: string;
};
featured: string;
recent: string;
newsletter: {
title: string;
desc: string;
placeholder: string;
button: string;
};
};
blog: {
categories: {
all: string;
ai: string;
code: string;
startups: string;
tools: string;
};
readMore: string;
related: string;
back: string;
toc: string;
};
footer: {
about: string;
connect: string;
rights: string;
terms: string;
ethics: string;
newsletter: string;
rss: string;
contact: string;
privacy: string;
};
legal: {
privacy: { title: string; content: string; };
terms: { title: string; content: string; };
ethics: { title: string; content: string; };
};
contact: {
title: string;
subtitle: string;
info: {
title: string;
email: string;
location: string;
};
status: {
title: string;
nodes: string;
uptime: string;
latency: string;
};
form: {
name: string;
email: string;
subject: string;
message: string;
submit: string;
sending: string;
success: string;
};
};
}
export interface SiteContent {
posts: Record<Language, Post[]>;
ui: Record<Language, Translations>;
}

26
Template-03/tsconfig.json Normal file
View file

@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "ES2022",
"experimentalDecorators": true,
"useDefineForClassFields": false,
"module": "ESNext",
"lib": [
"ES2022",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true,
"moduleResolution": "bundler",
"isolatedModules": true,
"moduleDetection": "force",
"allowJs": true,
"jsx": "react-jsx",
"paths": {
"@/*": [
"./*"
]
},
"allowImportingTsExtensions": true,
"noEmit": true
}
}

View file

@ -0,0 +1,24 @@
import tailwindcss from '@tailwindcss/vite';
import react from '@vitejs/plugin-react';
import path from 'path';
import {defineConfig, loadEnv} from 'vite';
export default defineConfig(({mode}) => {
const env = loadEnv(mode, '.', '');
return {
plugins: [react(), tailwindcss()],
define: {
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY),
},
resolve: {
alias: {
'@': path.resolve(__dirname, '.'),
},
},
server: {
// HMR is disabled in AI Studio via DISABLE_HMR env var.
// Do not modify—file watching is disabled to prevent flickering during agent edits.
hmr: process.env.DISABLE_HMR !== 'true',
},
};
});