Primeira versão templates pessoal
This commit is contained in:
commit
cf928d2675
42 changed files with 13034 additions and 0 deletions
BIN
.DS_Store
vendored
Normal file
BIN
.DS_Store
vendored
Normal file
Binary file not shown.
20
Template-01/README.md
Normal file
20
Template-01/README.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<div align="center">
|
||||
<img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
|
||||
</div>
|
||||
|
||||
# Run and deploy your AI Studio app
|
||||
|
||||
This contains everything you need to run your app locally.
|
||||
|
||||
View your app in AI Studio: https://ai.studio/apps/7acb8a3c-e597-4c44-ace5-d65ec976ac1b
|
||||
|
||||
## Run Locally
|
||||
|
||||
**Prerequisites:** Node.js
|
||||
|
||||
|
||||
1. Install dependencies:
|
||||
`npm install`
|
||||
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
|
||||
3. Run the app:
|
||||
`npm run dev`
|
||||
15
Template-01/index.html
Normal file
15
Template-01/index.html
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<!-- INJECT_SEO -->
|
||||
<link rel="icon" 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#18181b%22/><text y=%2250%%22 x=%2250%%22 text-anchor=%22middle%22 dominant-baseline=%22central%22 font-family=%22system-ui, sans-serif%22 font-weight=%22bold%22 font-size=%2250%22 fill=%22#10b981%22>L</text></svg>">
|
||||
<title>Leonardo Silva | Front-end Engineer</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
6
Template-01/metadata.json
Normal file
6
Template-01/metadata.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Design-Driven Personal Blog",
|
||||
"description": "A sleek, professional personal blog with a minimal, tech-focused aesthetic inspired by Vercel.",
|
||||
"requestFramePermissions": [],
|
||||
"majorCapabilities": []
|
||||
}
|
||||
4410
Template-01/package-lock.json
generated
Normal file
4410
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
39
Template-01/package.json
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"name": "react-example",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "tsx server.ts",
|
||||
"build": "vite build && tsc --noEmit",
|
||||
"preview": "NODE_ENV=production tsx server.ts",
|
||||
"start": "NODE_ENV=production tsx server.ts",
|
||||
"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",
|
||||
"date-fns": "^4.1.0",
|
||||
"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-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"
|
||||
}
|
||||
}
|
||||
127
Template-01/server.ts
Normal file
127
Template-01/server.ts
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import express from "express";
|
||||
import { createServer as createViteServer } from "vite";
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { BLOG_POSTS } from "./src/lib/data.ts";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
async function startServer() {
|
||||
const app = express();
|
||||
const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3000;
|
||||
|
||||
let vite: any;
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
vite = await createViteServer({
|
||||
server: { middlewareMode: true },
|
||||
appType: "custom",
|
||||
});
|
||||
app.use(vite.middlewares);
|
||||
} else {
|
||||
// Serve static files from dist/assets and others, but not index.html directly
|
||||
app.use(express.static(path.join(process.cwd(), "dist"), { index: false }));
|
||||
}
|
||||
|
||||
app.get("/sitemap.xml", (req, res) => {
|
||||
const baseUrl = process.env.APP_URL || "https://leonardo.dev";
|
||||
|
||||
let xml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url><loc>${baseUrl}/</loc><changefreq>weekly</changefreq><priority>1.0</priority></url>
|
||||
<url><loc>${baseUrl}/blog</loc><changefreq>daily</changefreq><priority>0.9</priority></url>
|
||||
<url><loc>${baseUrl}/curriculo</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||
<url><loc>${baseUrl}/contato</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>`;
|
||||
|
||||
BLOG_POSTS.forEach((post) => {
|
||||
xml += `
|
||||
<url><loc>${baseUrl}/blog/${post.slug}</loc><changefreq>monthly</changefreq><priority>0.7</priority></url>`;
|
||||
});
|
||||
|
||||
xml += `
|
||||
</urlset>`;
|
||||
|
||||
res.header("Content-Type", "application/xml");
|
||||
res.send(xml);
|
||||
});
|
||||
|
||||
app.get("/robots.txt", (req, res) => {
|
||||
const baseUrl = process.env.APP_URL || "https://leonardo.dev";
|
||||
const txt = `User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: ${baseUrl}/sitemap.xml`;
|
||||
res.header("Content-Type", "text/plain");
|
||||
res.send(txt);
|
||||
});
|
||||
|
||||
app.get("*", async (req, res) => {
|
||||
try {
|
||||
let template, render;
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
template = await fs.readFile(path.join(__dirname, "index.html"), "utf-8");
|
||||
template = await vite.transformIndexHtml(req.originalUrl, template);
|
||||
} else {
|
||||
template = await fs.readFile(path.join(process.cwd(), "dist", "index.html"), "utf-8");
|
||||
}
|
||||
|
||||
// Default tags
|
||||
let title = "Leonardo Silva | Front-end Engineer & Designer";
|
||||
let description = "Engenheiro de Software & Designer de Interfaces. Construindo experiências web de alta performance.";
|
||||
let image = "https://images.unsplash.com/photo-1544725176-7c40e5a71c5e?auto=format&fit=crop&q=80&w=1200&h=630";
|
||||
const appUrl = process.env.APP_URL || "https://leonardo.dev";
|
||||
|
||||
// If viewing a post
|
||||
if (req.originalUrl.startsWith("/blog/")) {
|
||||
const slug = req.originalUrl.split("/")[2];
|
||||
const post = BLOG_POSTS.find(p => p.slug === slug);
|
||||
if (post) {
|
||||
title = `${post.title} | Leonardo Silva`;
|
||||
description = post.excerpt;
|
||||
if (post.coverImage) {
|
||||
image = post.coverImage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replace generic meta tags in index.html with specific ones
|
||||
let html = template
|
||||
.replace(/<title>.*<\/title>/i, `<title>${title}</title>`)
|
||||
.replace(
|
||||
/<!-- INJECT_SEO -->/,
|
||||
`
|
||||
<meta name="description" content="${description}" />
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="${appUrl}${req.originalUrl}" />
|
||||
<meta property="og:title" content="${title}" />
|
||||
<meta property="og:description" content="${description}" />
|
||||
<meta property="og:image" content="${image}" />
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:url" content="${appUrl}${req.originalUrl}" />
|
||||
<meta property="twitter:title" content="${title}" />
|
||||
<meta property="twitter:description" content="${description}" />
|
||||
<meta property="twitter:image" content="${image}" />
|
||||
`
|
||||
);
|
||||
|
||||
res.status(200).set({ "Content-Type": "text/html" }).end(html);
|
||||
} catch (e) {
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
vite.ssrFixStacktrace(e as Error);
|
||||
}
|
||||
console.error(e);
|
||||
res.status(500).end((e as Error).message);
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(PORT, "0.0.0.0", () => {
|
||||
console.log(`Server running on http://0.0.0.0:${PORT}`);
|
||||
});
|
||||
}
|
||||
|
||||
startServer();
|
||||
30
Template-01/src/App.tsx
Normal file
30
Template-01/src/App.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* @license
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
||||
import { RootLayout } from "./layouts/RootLayout";
|
||||
import { Home } from "./pages/Home";
|
||||
import { Blog } from "./pages/Blog";
|
||||
import { Post } from "./pages/Post";
|
||||
import { Resume } from "./pages/Resume";
|
||||
import { Contact } from "./pages/Contact";
|
||||
import { NotFound } from "./pages/NotFound";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<RootLayout />}>
|
||||
<Route index element={<Home />} />
|
||||
<Route path="blog" element={<Blog />} />
|
||||
<Route path="blog/:slug" element={<Post />} />
|
||||
<Route path="curriculo" element={<Resume />} />
|
||||
<Route path="contato" element={<Contact />} />
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
||||
14
Template-01/src/index.css
Normal file
14
Template-01/src/index.css
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500&display=swap');
|
||||
@import "tailwindcss";
|
||||
|
||||
@theme {
|
||||
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
||||
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
@apply bg-zinc-950 text-zinc-50 antialiased selection:bg-zinc-100 selection:text-zinc-900;
|
||||
}
|
||||
}
|
||||
|
||||
82
Template-01/src/layouts/RootLayout.tsx
Normal file
82
Template-01/src/layouts/RootLayout.tsx
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import { ReactNode } from "react";
|
||||
import { Link, Outlet, useLocation } from "react-router-dom";
|
||||
import { Github, Twitter, Linkedin, Mail } from "lucide-react";
|
||||
import { cn } from "../lib/utils";
|
||||
import { motion } from "motion/react";
|
||||
|
||||
function NavLink({ to, children, active }: { to: string; children: ReactNode; active?: boolean }) {
|
||||
return (
|
||||
<Link
|
||||
to={to}
|
||||
className={cn(
|
||||
"relative text-sm transition-colors hover:text-zinc-50",
|
||||
active ? "text-zinc-50 font-medium" : "text-zinc-400"
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
{active && (
|
||||
<motion.div
|
||||
layoutId="nav-indicator"
|
||||
className="absolute -bottom-[21px] left-0 right-0 h-[1px] bg-zinc-50"
|
||||
initial={false}
|
||||
transition={{ type: "spring", stiffness: 500, damping: 30 }}
|
||||
/>
|
||||
)}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export function RootLayout() {
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-zinc-950 font-sans text-zinc-50 flex flex-col selection:bg-zinc-100 selection:text-zinc-900 print:bg-white print:text-zinc-900">
|
||||
{/* Header */}
|
||||
<header className="sticky top-0 z-50 border-b border-white/[0.08] bg-zinc-950/80 backdrop-blur-md print:hidden">
|
||||
<div className="mx-auto max-w-4xl px-4 h-16 flex items-center justify-between">
|
||||
<Link to="/" className="flex flex-col">
|
||||
<span className="text-sm font-semibold tracking-tight">Leonardo.<span className="text-zinc-500 font-mono">dev</span></span>
|
||||
</Link>
|
||||
|
||||
<nav className="flex items-center gap-4 sm:gap-6">
|
||||
<NavLink to="/" active={location.pathname === "/"}>Início</NavLink>
|
||||
<NavLink to="/blog" active={location.pathname.startsWith("/blog")}>Artigos</NavLink>
|
||||
<NavLink to="/curriculo" active={location.pathname === "/curriculo"}>Currículo</NavLink>
|
||||
<NavLink to="/contato" active={location.pathname === "/contato"}>Contato</NavLink>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="flex-1 w-full max-w-4xl mx-auto px-4 py-12 md:py-24 print:py-0 print:px-0 print:max-w-none">
|
||||
<Outlet />
|
||||
</main>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="border-t border-white/[0.08] py-12 text-zinc-500 print:hidden">
|
||||
<div className="max-w-4xl mx-auto px-4 flex flex-col md:flex-row justify-between items-center gap-6">
|
||||
<div className="flex items-center gap-2 text-sm font-mono">
|
||||
<span>© {new Date().getFullYear()}</span>
|
||||
<span className="w-1 h-1 rounded-full bg-zinc-700" />
|
||||
<span>São Paulo, BR</span>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4">
|
||||
<a href="#" className="hover:text-zinc-50 transition-colors">
|
||||
<Twitter className="w-4 h-4" />
|
||||
</a>
|
||||
<a href="#" className="hover:text-zinc-50 transition-colors">
|
||||
<Github className="w-4 h-4" />
|
||||
</a>
|
||||
<a href="#" className="hover:text-zinc-50 transition-colors">
|
||||
<Linkedin className="w-4 h-4" />
|
||||
</a>
|
||||
<a href="#" className="hover:text-zinc-50 transition-colors">
|
||||
<Mail className="w-4 h-4" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
307
Template-01/src/lib/data.ts
Normal file
307
Template-01/src/lib/data.ts
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
export interface BlogPost {
|
||||
slug: string;
|
||||
title: string;
|
||||
excerpt: string;
|
||||
content: string;
|
||||
date: string;
|
||||
readTime: string;
|
||||
tags: string[];
|
||||
coverImage?: string;
|
||||
}
|
||||
|
||||
export const BLOG_POSTS: BlogPost[] = [
|
||||
{
|
||||
"slug": "entendendo-o-state-management-em-2026",
|
||||
"title": "Entendendo o State Management em 2026",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA evolução de **entendendo o state management em 2026** transcendeu a mera estética. Hoje, caminhamos para um paradigma onde a tecnologia dita as regras de forma invisível.\n\n## Menos Ruído, Mais Foco\n\nA inspiração em tech leads e empresas gigantes nos mostra que os usuários não querem ser surpreendidos por abordagens complexas; eles querem previsibilidade e clareza absoluta quando lidam com entendendo o state management em 2026.\n\n> \"Qualquer tolo consegue escrever código que um computador entenda. Bons programadores escrevem código que humanos entendam.\" — Martin Fowler\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Foco absoluto no desempenho** desde o primeiro commit.\n- **Feedback visual constante** usando padrões eficientes.\n- **Integração contínua** com ferramentas limpas.\n\n\n\n### Conclusão e Próximos Passos\n\nA simplicidade e a eficiência não são acidentais. São construídas linha por linha, com paciência e método. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-12-12",
|
||||
"readTime": "4 min",
|
||||
"tags": [
|
||||
"React",
|
||||
"State",
|
||||
"Gerenciamento"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1484417894907-623942c8ee29?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "animações-e-motion-design-na-web",
|
||||
"title": "Animações e Motion Design na Web",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA discussão sobre **animações e motion design na web** nunca foi tão relevante. Com empresas buscando eficiência máxima, dominar este conceito se tornou obrigatório para o engenheiro moderno.\n\n## Caminhos para o Futuro\n\nImplementar estas soluções requer um entendimento sólido de como as peças se encaixam no grande quebra-cabeça da engenharia de software atual.\n\n> \"A restrição gera criatividade. Não pense fora da caixa. Pense no que você pode fazer com a caixa recebida.\" — Princípios de Design Minimalista\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n1. Analise o problema estrutural principal.\n2. Simplifique a solução até sua essência básica.\n3. Escreva documentação clara para a equipe.\n\n\n\n### Conclusão e Próximos Passos\n\nNo final do dia, escrever um código ou desenhar uma interface é sempre, fundamentalmente, criar uma forma de comunicação humana. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-11-11",
|
||||
"readTime": "3 min",
|
||||
"tags": [
|
||||
"Motion",
|
||||
"Animação",
|
||||
"UX"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1481481600465-3652ea6bbec8?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "otimização-de-performance-no-front-end",
|
||||
"title": "Otimização de Performance no Front-end",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA discussão sobre **otimização de performance no front-end** nunca foi tão relevante. Com empresas buscando eficiência máxima, dominar este conceito se tornou obrigatório para o engenheiro moderno.\n\n## Caminhos para o Futuro\n\nImplementar estas soluções requer um entendimento sólido de como as peças se encaixam no grande quebra-cabeça da engenharia de software atual.\n\n> \"A restrição gera criatividade. Não pense fora da caixa. Pense no que você pode fazer com a caixa recebida.\" — Princípios de Design Minimalista\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n1. Analise o problema estrutural principal.\n2. Simplifique a solução até sua essência básica.\n3. Escreva documentação clara para a equipe.\n\n\n\n### Conclusão e Próximos Passos\n\nNo final do dia, escrever um código ou desenhar uma interface é sempre, fundamentalmente, criar uma forma de comunicação humana. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-10-10",
|
||||
"readTime": "7 min",
|
||||
"tags": [
|
||||
"Performance",
|
||||
"Web Vitals",
|
||||
"Web"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1498050108023-c5249f4df085?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "por-que-o-minimalismo-nunca-morre",
|
||||
"title": "Por que o Minimalismo nunca morre",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nAo explorar as nuances do mundo de **Por que o Minimalismo nunca morre**, percebemos o impacto profundo que pequenas decisões têm a longo prazo em nossa área de atuação.\n\n## A Prática Fundamenta a Teoria\n\nO verdadeiro poder de dominar este conceito está em suas fundações e base teórica. Ao priorizar a estabilidade, garantimos escala e facilidade de manutenção no código fonte e na interface.\n\n> \"A simplicidade é o último grau de sofisticação.\" — Leonardo da Vinci\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Arquitetura modularizada** em componentes independentes.\n- **Testes rigorosos** de usabilidade e fluxos complexos.\n- **Eliminação de código inativo** de forma sistemática.\n\n\n\n### Conclusão e Próximos Passos\n\nO futuro será moldado por quem entende de complexidade, mas a oculta atrás de interfaces simples. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-09-21",
|
||||
"readTime": "3 min",
|
||||
"tags": [
|
||||
"Minimalismo",
|
||||
"Design",
|
||||
"Filosofia"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1593640408182-31c70c8268f5?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "server-components-a-nova-era-do-react",
|
||||
"title": "Server Components: A nova era do React",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nAo explorar as nuances do mundo de **Server Components: A nova era do React**, percebemos o impacto profundo que pequenas decisões têm a longo prazo em nossa área de atuação.\n\n## A Prática Fundamenta a Teoria\n\nO verdadeiro poder de dominar este conceito está em suas fundações e base teórica. Ao priorizar a estabilidade, garantimos escala e facilidade de manutenção no código fonte e na interface.\n\n> \"A simplicidade é o último grau de sofisticação.\" — Leonardo da Vinci\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Arquitetura modularizada** em componentes independentes.\n- **Testes rigorosos** de usabilidade e fluxos complexos.\n- **Eliminação de código inativo** de forma sistemática.\n\n\n\n### Conclusão e Próximos Passos\n\nO futuro será moldado por quem entende de complexidade, mas a oculta atrás de interfaces simples. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-09-09",
|
||||
"readTime": "6 min",
|
||||
"tags": [
|
||||
"React",
|
||||
"Server",
|
||||
"Performance"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1550751827-4bd374c3f58b?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "testes-e2e-sem-dor-de-cabeça",
|
||||
"title": "Testes E2E sem dor de cabeça",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nAo explorar as nuances do mundo de **Testes E2E sem dor de cabeça**, percebemos o impacto profundo que pequenas decisões têm a longo prazo em nossa área de atuação.\n\n## A Prática Fundamenta a Teoria\n\nO verdadeiro poder de dominar este conceito está em suas fundações e base teórica. Ao priorizar a estabilidade, garantimos escala e facilidade de manutenção no código fonte e na interface.\n\n> \"A simplicidade é o último grau de sofisticação.\" — Leonardo da Vinci\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Arquitetura modularizada** em componentes independentes.\n- **Testes rigorosos** de usabilidade e fluxos complexos.\n- **Eliminação de código inativo** de forma sistemática.\n\n\n\n### Conclusão e Próximos Passos\n\nO futuro será moldado por quem entende de complexidade, mas a oculta atrás de interfaces simples. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-08-20",
|
||||
"readTime": "7 min",
|
||||
"tags": [
|
||||
"Testes",
|
||||
"QA",
|
||||
"Engenharia"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1550439062-609e1531270e?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "o-poder-da-tipografia-no-design-de-software",
|
||||
"title": "O Poder da Tipografia no Design de Software",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA discussão sobre **o poder da tipografia no design de software** nunca foi tão relevante. Com empresas buscando eficiência máxima, dominar este conceito se tornou obrigatório para o engenheiro moderno.\n\n## Caminhos para o Futuro\n\nImplementar estas soluções requer um entendimento sólido de como as peças se encaixam no grande quebra-cabeça da engenharia de software atual.\n\n> \"A restrição gera criatividade. Não pense fora da caixa. Pense no que você pode fazer com a caixa recebida.\" — Princípios de Design Minimalista\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n1. Analise o problema estrutural principal.\n2. Simplifique a solução até sua essência básica.\n3. Escreva documentação clara para a equipe.\n\n\n\n### Conclusão e Próximos Passos\n\nNo final do dia, escrever um código ou desenhar uma interface é sempre, fundamentalmente, criar uma forma de comunicação humana. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-08-08",
|
||||
"readTime": "5 min",
|
||||
"tags": [
|
||||
"Tipografia",
|
||||
"Design",
|
||||
"Acesso"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1517694712202-14dd9538aa97?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "webassembly-wasm-no-dia-a-dia",
|
||||
"title": "WebAssembly (Wasm) no dia a dia",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA evolução de **webassembly (wasm) no dia a dia** transcendeu a mera estética. Hoje, caminhamos para um paradigma onde a tecnologia dita as regras de forma invisível.\n\n## Menos Ruído, Mais Foco\n\nA inspiração em tech leads e empresas gigantes nos mostra que os usuários não querem ser surpreendidos por abordagens complexas; eles querem previsibilidade e clareza absoluta quando lidam com webassembly (wasm) no dia a dia.\n\n> \"Qualquer tolo consegue escrever código que um computador entenda. Bons programadores escrevem código que humanos entendam.\" — Martin Fowler\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Foco absoluto no desempenho** desde o primeiro commit.\n- **Feedback visual constante** usando padrões eficientes.\n- **Integração contínua** com ferramentas limpas.\n\n\n\n### Conclusão e Próximos Passos\n\nA simplicidade e a eficiência não são acidentais. São construídas linha por linha, com paciência e método. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-07-19",
|
||||
"readTime": "6 min",
|
||||
"tags": [
|
||||
"Wasm",
|
||||
"Performance",
|
||||
"Web"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1499951360447-b19be8fe80f5?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "design-system-criando-a-fonte-da-verdade",
|
||||
"title": "Design System: Criando a fonte da verdade",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nAo explorar as nuances do mundo de **Design System: Criando a fonte da verdade**, percebemos o impacto profundo que pequenas decisões têm a longo prazo em nossa área de atuação.\n\n## A Prática Fundamenta a Teoria\n\nO verdadeiro poder de dominar este conceito está em suas fundações e base teórica. Ao priorizar a estabilidade, garantimos escala e facilidade de manutenção no código fonte e na interface.\n\n> \"A simplicidade é o último grau de sofisticação.\" — Leonardo da Vinci\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Arquitetura modularizada** em componentes independentes.\n- **Testes rigorosos** de usabilidade e fluxos complexos.\n- **Eliminação de código inativo** de forma sistemática.\n\n\n\n### Conclusão e Próximos Passos\n\nO futuro será moldado por quem entende de complexidade, mas a oculta atrás de interfaces simples. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-07-07",
|
||||
"readTime": "4 min",
|
||||
"tags": [
|
||||
"Design System",
|
||||
"Figma",
|
||||
"UI"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1542831371-29b0f74f9713?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "como-criar-dark-modes-elegantes",
|
||||
"title": "Como criar Dark Modes elegantes",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nAo explorar as nuances do mundo de **Como criar Dark Modes elegantes**, percebemos o impacto profundo que pequenas decisões têm a longo prazo em nossa área de atuação.\n\n## A Prática Fundamenta a Teoria\n\nO verdadeiro poder de dominar este conceito está em suas fundações e base teórica. Ao priorizar a estabilidade, garantimos escala e facilidade de manutenção no código fonte e na interface.\n\n> \"A simplicidade é o último grau de sofisticação.\" — Leonardo da Vinci\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Arquitetura modularizada** em componentes independentes.\n- **Testes rigorosos** de usabilidade e fluxos complexos.\n- **Eliminação de código inativo** de forma sistemática.\n\n\n\n### Conclusão e Próximos Passos\n\nO futuro será moldado por quem entende de complexidade, mas a oculta atrás de interfaces simples. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-06-18",
|
||||
"readTime": "5 min",
|
||||
"tags": [
|
||||
"CSS",
|
||||
"Design",
|
||||
"UI"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1633356122544-f134324a6cee?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "micro-frontends-com-next-js",
|
||||
"title": "Micro-frontends com Next.js",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA discussão sobre **micro-frontends com next.js** nunca foi tão relevante. Com empresas buscando eficiência máxima, dominar este conceito se tornou obrigatório para o engenheiro moderno.\n\n## Caminhos para o Futuro\n\nImplementar estas soluções requer um entendimento sólido de como as peças se encaixam no grande quebra-cabeça da engenharia de software atual.\n\n> \"A restrição gera criatividade. Não pense fora da caixa. Pense no que você pode fazer com a caixa recebida.\" — Princípios de Design Minimalista\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n1. Analise o problema estrutural principal.\n2. Simplifique a solução até sua essência básica.\n3. Escreva documentação clara para a equipe.\n\n\n\n### Conclusão e Próximos Passos\n\nNo final do dia, escrever um código ou desenhar uma interface é sempre, fundamentalmente, criar uma forma de comunicação humana. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-06-06",
|
||||
"readTime": "3 min",
|
||||
"tags": [
|
||||
"Next.js",
|
||||
"Micro-frontends",
|
||||
"Arquitetura"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1451187580459-43490279c0fa?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "o-papel-do-desenvolvedor-full-stack-hoje",
|
||||
"title": "O Papel do Desenvolvedor Full-stack hoje",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA evolução de **o papel do desenvolvedor full-stack hoje** transcendeu a mera estética. Hoje, caminhamos para um paradigma onde a tecnologia dita as regras de forma invisível.\n\n## Menos Ruído, Mais Foco\n\nA inspiração em tech leads e empresas gigantes nos mostra que os usuários não querem ser surpreendidos por abordagens complexas; eles querem previsibilidade e clareza absoluta quando lidam com o papel do desenvolvedor full-stack hoje.\n\n> \"Qualquer tolo consegue escrever código que um computador entenda. Bons programadores escrevem código que humanos entendam.\" — Martin Fowler\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Foco absoluto no desempenho** desde o primeiro commit.\n- **Feedback visual constante** usando padrões eficientes.\n- **Integração contínua** com ferramentas limpas.\n\n\n\n### Conclusão e Próximos Passos\n\nA simplicidade e a eficiência não são acidentais. São construídas linha por linha, com paciência e método. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-05-17",
|
||||
"readTime": "4 min",
|
||||
"tags": [
|
||||
"Carreira",
|
||||
"Engenharia",
|
||||
"Web"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1614332287897-cdc485fa562d?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "webgl-e-experiências-imersivas",
|
||||
"title": "WebGL e Experiências Imersivas",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA evolução de **webgl e experiências imersivas** transcendeu a mera estética. Hoje, caminhamos para um paradigma onde a tecnologia dita as regras de forma invisível.\n\n## Menos Ruído, Mais Foco\n\nA inspiração em tech leads e empresas gigantes nos mostra que os usuários não querem ser surpreendidos por abordagens complexas; eles querem previsibilidade e clareza absoluta quando lidam com webgl e experiências imersivas.\n\n> \"Qualquer tolo consegue escrever código que um computador entenda. Bons programadores escrevem código que humanos entendam.\" — Martin Fowler\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Foco absoluto no desempenho** desde o primeiro commit.\n- **Feedback visual constante** usando padrões eficientes.\n- **Integração contínua** com ferramentas limpas.\n\n\n\n### Conclusão e Próximos Passos\n\nA simplicidade e a eficiência não são acidentais. São construídas linha por linha, com paciência e método. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-05-05",
|
||||
"readTime": "7 min",
|
||||
"tags": [
|
||||
"3D",
|
||||
"WebGL",
|
||||
"Performance"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1558655146-d09347e92766?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "graphql-vs-rest-dilemas-modernos",
|
||||
"title": "GraphQL vs REST: Dilemas Modernos",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nAo explorar as nuances do mundo de **GraphQL vs REST: Dilemas Modernos**, percebemos o impacto profundo que pequenas decisões têm a longo prazo em nossa área de atuação.\n\n## A Prática Fundamenta a Teoria\n\nO verdadeiro poder de dominar este conceito está em suas fundações e base teórica. Ao priorizar a estabilidade, garantimos escala e facilidade de manutenção no código fonte e na interface.\n\n> \"A simplicidade é o último grau de sofisticação.\" — Leonardo da Vinci\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Arquitetura modularizada** em componentes independentes.\n- **Testes rigorosos** de usabilidade e fluxos complexos.\n- **Eliminação de código inativo** de forma sistemática.\n\n\n\n### Conclusão e Próximos Passos\n\nO futuro será moldado por quem entende de complexidade, mas a oculta atrás de interfaces simples. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-04-16",
|
||||
"readTime": "3 min",
|
||||
"tags": [
|
||||
"API",
|
||||
"GraphQL",
|
||||
"Backend"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1605379399642-870262d3d051?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "acessibilidade-na-web-muito-além-do-básico",
|
||||
"title": "Acessibilidade na Web: Muito além do básico",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nAo explorar as nuances do mundo de **Acessibilidade na Web: Muito além do básico**, percebemos o impacto profundo que pequenas decisões têm a longo prazo em nossa área de atuação.\n\n## A Prática Fundamenta a Teoria\n\nO verdadeiro poder de dominar este conceito está em suas fundações e base teórica. Ao priorizar a estabilidade, garantimos escala e facilidade de manutenção no código fonte e na interface.\n\n> \"A simplicidade é o último grau de sofisticação.\" — Leonardo da Vinci\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Arquitetura modularizada** em componentes independentes.\n- **Testes rigorosos** de usabilidade e fluxos complexos.\n- **Eliminação de código inativo** de forma sistemática.\n\n\n\n### Conclusão e Próximos Passos\n\nO futuro será moldado por quem entende de complexidade, mas a oculta atrás de interfaces simples. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-04-04",
|
||||
"readTime": "6 min",
|
||||
"tags": [
|
||||
"Acessibilidade",
|
||||
"a11y",
|
||||
"UX"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1507721999472-8ed4409c4b17?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "integrando-ia-generativa-no-front-end",
|
||||
"title": "Integrando IA Generativa no Front-end",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA discussão sobre **integrando ia generativa no front-end** nunca foi tão relevante. Com empresas buscando eficiência máxima, dominar este conceito se tornou obrigatório para o engenheiro moderno.\n\n## Caminhos para o Futuro\n\nImplementar estas soluções requer um entendimento sólido de como as peças se encaixam no grande quebra-cabeça da engenharia de software atual.\n\n> \"A restrição gera criatividade. Não pense fora da caixa. Pense no que você pode fazer com a caixa recebida.\" — Princípios de Design Minimalista\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n1. Analise o problema estrutural principal.\n2. Simplifique a solução até sua essência básica.\n3. Escreva documentação clara para a equipe.\n\n\n\n### Conclusão e Próximos Passos\n\nNo final do dia, escrever um código ou desenhar uma interface é sempre, fundamentalmente, criar uma forma de comunicação humana. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-03-15",
|
||||
"readTime": "7 min",
|
||||
"tags": [
|
||||
"IA",
|
||||
"LLMs",
|
||||
"Inovação"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1627398246734-d8df98de2fc5?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "tailwind-css-na-prática",
|
||||
"title": "Tailwind CSS na Prática",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA evolução de **tailwind css na prática** transcendeu a mera estética. Hoje, caminhamos para um paradigma onde a tecnologia dita as regras de forma invisível.\n\n## Menos Ruído, Mais Foco\n\nA inspiração em tech leads e empresas gigantes nos mostra que os usuários não querem ser surpreendidos por abordagens complexas; eles querem previsibilidade e clareza absoluta quando lidam com tailwind css na prática.\n\n> \"Qualquer tolo consegue escrever código que um computador entenda. Bons programadores escrevem código que humanos entendam.\" — Martin Fowler\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Foco absoluto no desempenho** desde o primeiro commit.\n- **Feedback visual constante** usando padrões eficientes.\n- **Integração contínua** com ferramentas limpas.\n\n\n\n### Conclusão e Próximos Passos\n\nA simplicidade e a eficiência não são acidentais. São construídas linha por linha, com paciência e método. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-03-03",
|
||||
"readTime": "5 min",
|
||||
"tags": [
|
||||
"CSS",
|
||||
"Tailwind",
|
||||
"Frameworks"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1618761714954-0b8cd0026356?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "edge-computing-para-aplicações-front-end",
|
||||
"title": "Edge Computing para Aplicações Front-end",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA evolução de **edge computing para aplicações front-end** transcendeu a mera estética. Hoje, caminhamos para um paradigma onde a tecnologia dita as regras de forma invisível.\n\n## Menos Ruído, Mais Foco\n\nA inspiração em tech leads e empresas gigantes nos mostra que os usuários não querem ser surpreendidos por abordagens complexas; eles querem previsibilidade e clareza absoluta quando lidam com edge computing para aplicações front-end.\n\n> \"Qualquer tolo consegue escrever código que um computador entenda. Bons programadores escrevem código que humanos entendam.\" — Martin Fowler\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Foco absoluto no desempenho** desde o primeiro commit.\n- **Feedback visual constante** usando padrões eficientes.\n- **Integração contínua** com ferramentas limpas.\n\n\n\n### Conclusão e Próximos Passos\n\nA simplicidade e a eficiência não são acidentais. São construídas linha por linha, com paciência e método. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-02-14",
|
||||
"readTime": "6 min",
|
||||
"tags": [
|
||||
"Edge",
|
||||
"Serverless",
|
||||
"Performance"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1563986768494-4dee2763ff0f?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "arquitetura-escalável-em-react",
|
||||
"title": "Arquitetura Escalável em React",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nAo explorar as nuances do mundo de **Arquitetura Escalável em React**, percebemos o impacto profundo que pequenas decisões têm a longo prazo em nossa área de atuação.\n\n## A Prática Fundamenta a Teoria\n\nO verdadeiro poder de dominar este conceito está em suas fundações e base teórica. Ao priorizar a estabilidade, garantimos escala e facilidade de manutenção no código fonte e na interface.\n\n> \"A simplicidade é o último grau de sofisticação.\" — Leonardo da Vinci\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Arquitetura modularizada** em componentes independentes.\n- **Testes rigorosos** de usabilidade e fluxos complexos.\n- **Eliminação de código inativo** de forma sistemática.\n\n\n\n### Conclusão e Próximos Passos\n\nO futuro será moldado por quem entende de complexidade, mas a oculta atrás de interfaces simples. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-02-02",
|
||||
"readTime": "4 min",
|
||||
"tags": [
|
||||
"React",
|
||||
"Arquitetura",
|
||||
"Engenharia"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1555066931-4365d14bab8c?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "o-fim-das-senhas-autenticação-com-passkeys",
|
||||
"title": "O Fim das Senhas: Autenticação com Passkeys",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA discussão sobre **o fim das senhas: autenticação com passkeys** nunca foi tão relevante. Com empresas buscando eficiência máxima, dominar este conceito se tornou obrigatório para o engenheiro moderno.\n\n## Caminhos para o Futuro\n\nImplementar estas soluções requer um entendimento sólido de como as peças se encaixam no grande quebra-cabeça da engenharia de software atual.\n\n> \"A restrição gera criatividade. Não pense fora da caixa. Pense no que você pode fazer com a caixa recebida.\" — Princípios de Design Minimalista\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n1. Analise o problema estrutural principal.\n2. Simplifique a solução até sua essência básica.\n3. Escreva documentação clara para a equipe.\n\n\n\n### Conclusão e Próximos Passos\n\nNo final do dia, escrever um código ou desenhar uma interface é sempre, fundamentalmente, criar uma forma de comunicação humana. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-01-13",
|
||||
"readTime": "5 min",
|
||||
"tags": [
|
||||
"Segurança",
|
||||
"Autenticação",
|
||||
"WebAuthn"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1515879218367-8466d910aaa4?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "o-futuro-das-interfaces-de-usuário",
|
||||
"title": "O Futuro das Interfaces de Usuário",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA evolução de **o futuro das interfaces de usuário** transcendeu a mera estética. Hoje, caminhamos para um paradigma onde a tecnologia dita as regras de forma invisível.\n\n## Menos Ruído, Mais Foco\n\nA inspiração em tech leads e empresas gigantes nos mostra que os usuários não querem ser surpreendidos por abordagens complexas; eles querem previsibilidade e clareza absoluta quando lidam com o futuro das interfaces de usuário.\n\n> \"Qualquer tolo consegue escrever código que um computador entenda. Bons programadores escrevem código que humanos entendam.\" — Martin Fowler\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Foco absoluto no desempenho** desde o primeiro commit.\n- **Feedback visual constante** usando padrões eficientes.\n- **Integração contínua** com ferramentas limpas.\n\n\n\n### Conclusão e Próximos Passos\n\nA simplicidade e a eficiência não são acidentais. São construídas linha por linha, com paciência e método. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-01-01",
|
||||
"readTime": "3 min",
|
||||
"tags": [
|
||||
"Design",
|
||||
"UI/UX",
|
||||
"Tendências"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1523726495923-c81aff907fb7?auto=format&fit=crop&q=80&w=800"
|
||||
}
|
||||
];
|
||||
6
Template-01/src/lib/utils.ts
Normal file
6
Template-01/src/lib/utils.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import { clsx, type ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
10
Template-01/src/main.tsx
Normal file
10
Template-01/src/main.tsx
Normal 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>,
|
||||
);
|
||||
85
Template-01/src/pages/Blog.tsx
Normal file
85
Template-01/src/pages/Blog.tsx
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import { motion } from "motion/react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { BLOG_POSTS } from "../lib/data";
|
||||
|
||||
const transition = { type: "spring", bounce: 0, duration: 0.8 };
|
||||
|
||||
export function Blog() {
|
||||
return (
|
||||
<div className="flex flex-col gap-12">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={transition}
|
||||
className="flex flex-col gap-4"
|
||||
>
|
||||
<h1 className="text-3xl font-semibold tracking-tight text-white">
|
||||
Artigos
|
||||
</h1>
|
||||
<p className="text-zinc-400 max-w-xl">
|
||||
Explorações sobre desenvolvimento front-end, design de interfaces, arquitetura de software e tecnologia em geral.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="flex flex-col">
|
||||
{BLOG_POSTS.map((post, i) => (
|
||||
<motion.div
|
||||
key={post.slug}
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ ...transition, delay: i * 0.1 }}
|
||||
>
|
||||
<Link
|
||||
to={`/blog/${post.slug}`}
|
||||
className="group flex flex-col sm:flex-row py-8 border-b border-white/[0.08] hover:bg-zinc-900/40 px-4 -mx-4 rounded-2xl transition-colors gap-6 sm:gap-8 items-start"
|
||||
>
|
||||
{post.coverImage && (
|
||||
<div className="w-full sm:w-64 h-48 sm:h-40 shrink-0 rounded-xl overflow-hidden border border-zinc-800/80 hidden sm:block">
|
||||
<img
|
||||
src={post.coverImage}
|
||||
alt={post.title}
|
||||
className="w-full h-full object-cover grayscale group-hover:grayscale-0 transition-all duration-700 scale-100 group-hover:scale-105"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-col flex-1">
|
||||
{post.coverImage && (
|
||||
<div className="w-full h-48 shrink-0 rounded-xl overflow-hidden border border-zinc-800/80 mb-4 sm:hidden">
|
||||
<img
|
||||
src={post.coverImage}
|
||||
alt={post.title}
|
||||
className="w-full h-full object-cover grayscale group-hover:grayscale-0 transition-all duration-700"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<time className="text-xs font-mono text-zinc-500">
|
||||
{new Date(post.date).toLocaleDateString('pt-BR', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
</time>
|
||||
<span className="w-1 h-1 rounded-full bg-zinc-800" />
|
||||
<span className="text-xs font-mono text-zinc-500">{post.readTime}</span>
|
||||
</div>
|
||||
|
||||
<h3 className="text-xl font-medium text-zinc-200 group-hover:text-white transition-colors mb-2">
|
||||
{post.title}
|
||||
</h3>
|
||||
|
||||
<p className="text-sm text-zinc-400 mb-4 max-w-2xl">
|
||||
{post.excerpt}
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap gap-2 mt-auto">
|
||||
{post.tags.map(tag => (
|
||||
<span key={tag} className="px-2 py-1 bg-zinc-900 text-zinc-400 text-xs rounded-md border border-zinc-800 font-mono">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
209
Template-01/src/pages/Contact.tsx
Normal file
209
Template-01/src/pages/Contact.tsx
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
import { motion } from "motion/react";
|
||||
import { Mail, ArrowRight, Linkedin, Github } from "lucide-react";
|
||||
import { FormEvent, useState } from "react";
|
||||
|
||||
const transition = { type: "spring", bounce: 0, duration: 0.8 };
|
||||
|
||||
function WhatsAppIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className={className}
|
||||
>
|
||||
<path d="M3 21l1.65-3.8a9 9 0 1 1 3.4 2.9L3 21" />
|
||||
<path d="M9 10a.5.5 0 0 0 1 0V9a.5.5 0 0 0-1 0v1a5 5 0 0 0 5 5h1a.5.5 0 0 0 0-1h-1a.5.5 0 0 0 0 1" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function Contact() {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [isSuccess, setIsSuccess] = useState(false);
|
||||
|
||||
const handleSubmit = (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
setIsSubmitting(true);
|
||||
|
||||
// Simulating form submission
|
||||
setTimeout(() => {
|
||||
setIsSubmitting(false);
|
||||
setIsSuccess(true);
|
||||
|
||||
setTimeout(() => {
|
||||
setIsSuccess(false);
|
||||
(e.target as HTMLFormElement).reset();
|
||||
}, 5000);
|
||||
}, 1500);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-12 max-w-4xl mx-auto">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={transition}
|
||||
className="flex flex-col gap-4"
|
||||
>
|
||||
<h1 className="text-3xl sm:text-4xl font-semibold tracking-tight text-white">
|
||||
Vamos conversar.
|
||||
</h1>
|
||||
<p className="text-zinc-400 text-lg max-w-2xl leading-relaxed">
|
||||
Estou sempre aberto a discutir desenvolvimento de produtos, novas oportunidades ou apenas para trocar uma ideia sobre tech e design.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-[1fr_400px] gap-12 lg:gap-20">
|
||||
|
||||
{/* Contact Form */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ ...transition, delay: 0.1 }}
|
||||
>
|
||||
<form className="flex flex-col gap-6" onSubmit={handleSubmit}>
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="name" className="text-sm font-medium text-zinc-300">
|
||||
Nome completo
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
required
|
||||
className="h-12 w-full bg-zinc-900 border border-zinc-800 rounded-lg px-4 text-zinc-100 placeholder:text-zinc-600 focus:outline-none focus:ring-2 focus:ring-zinc-400 focus:border-transparent transition-all"
|
||||
placeholder="João da Silva"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="email" className="text-sm font-medium text-zinc-300">
|
||||
E-mail profissional
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
required
|
||||
className="h-12 w-full bg-zinc-900 border border-zinc-800 rounded-lg px-4 text-zinc-100 placeholder:text-zinc-600 focus:outline-none focus:ring-2 focus:ring-zinc-400 focus:border-transparent transition-all"
|
||||
placeholder="joao@empresa.com"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="message" className="text-sm font-medium text-zinc-300">
|
||||
Mensagem
|
||||
</label>
|
||||
<textarea
|
||||
id="message"
|
||||
required
|
||||
rows={5}
|
||||
className="w-full bg-zinc-900 border border-zinc-800 rounded-lg p-4 text-zinc-100 placeholder:text-zinc-600 focus:outline-none focus:ring-2 focus:ring-zinc-400 focus:border-transparent transition-all resize-none"
|
||||
placeholder="Como posso te ajudar?"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSubmitting || isSuccess}
|
||||
className="h-12 w-full sm:w-auto sm:self-start px-8 bg-zinc-50 text-zinc-950 text-sm font-semibold rounded-lg hover:bg-zinc-200 transition-colors shadow-[0_0_20px_rgba(255,255,255,0.05)] hover:shadow-[0_0_25px_rgba(255,255,255,0.1)] flex items-center justify-center gap-2 disabled:opacity-70 disabled:cursor-not-allowed"
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<div className="w-4 h-4 border-2 border-zinc-950 border-t-transparent rounded-full animate-spin" />
|
||||
) : isSuccess ? (
|
||||
"Mensagem enviada!"
|
||||
) : (
|
||||
<>
|
||||
Enviar mensagem
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{isSuccess && (
|
||||
<p className="text-sm text-emerald-400 font-medium">
|
||||
Sua mensagem foi enviada com sucesso. Retornarei o mais breve possível.
|
||||
</p>
|
||||
)}
|
||||
</form>
|
||||
</motion.div>
|
||||
|
||||
{/* Direct Contacts */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ ...transition, delay: 0.2 }}
|
||||
className="flex flex-col gap-8"
|
||||
>
|
||||
<div className="flex flex-col gap-6">
|
||||
<h2 className="text-lg font-medium text-zinc-100 tracking-tight">Contato direto</h2>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<a
|
||||
href="mailto:contato@leonardo.dev"
|
||||
className="flex items-center gap-4 p-4 rounded-xl border border-zinc-800/60 bg-zinc-900/30 hover:bg-zinc-900/60 hover:border-zinc-700 transition-all group"
|
||||
>
|
||||
<div className="w-10 h-10 rounded-lg bg-zinc-800 flex items-center justify-center text-zinc-400 group-hover:text-white group-hover:bg-zinc-700 transition-colors shrink-0">
|
||||
<Mail className="w-5 h-5" />
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium text-zinc-200">E-mail</span>
|
||||
<span className="text-sm text-zinc-500 font-mono">contato@leonardo.dev</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://wa.me/5511999999999"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-4 p-4 rounded-xl border border-zinc-800/60 bg-zinc-900/30 hover:bg-zinc-900/60 hover:border-zinc-700 transition-all group"
|
||||
>
|
||||
<div className="w-10 h-10 rounded-lg bg-zinc-800 flex items-center justify-center text-zinc-400 group-hover:text-emerald-400 group-hover:bg-zinc-700 transition-colors shrink-0">
|
||||
<WhatsAppIcon className="w-5 h-5" />
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium text-zinc-200">WhatsApp</span>
|
||||
<span className="text-sm text-zinc-500 font-mono">+55 11 99999-9999</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="h-px bg-white/[0.08]" />
|
||||
|
||||
<div className="flex flex-col gap-6">
|
||||
<h2 className="text-lg font-medium text-zinc-100 tracking-tight">Redes</h2>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<a
|
||||
href="https://linkedin.com/in/leosilva"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-4 group"
|
||||
>
|
||||
<Linkedin className="w-5 h-5 text-zinc-500 group-hover:text-blue-400 transition-colors" />
|
||||
<span className="text-sm text-zinc-400 group-hover:text-zinc-200 transition-colors">linkedin.com/in/leosilva</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://github.com/leosilva"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-4 group"
|
||||
>
|
||||
<Github className="w-5 h-5 text-zinc-500 group-hover:text-white transition-colors" />
|
||||
<span className="text-sm text-zinc-400 group-hover:text-zinc-200 transition-colors">github.com/leosilva</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
136
Template-01/src/pages/Home.tsx
Normal file
136
Template-01/src/pages/Home.tsx
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
import { motion } from "motion/react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { ArrowRight, Download, Briefcase } from "lucide-react";
|
||||
import { BLOG_POSTS } from "../lib/data";
|
||||
|
||||
const transition = { type: "spring", bounce: 0, duration: 0.8 };
|
||||
|
||||
export function Home() {
|
||||
const recentPosts = BLOG_POSTS.slice(0, 3);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-24">
|
||||
{/* Hero Section */}
|
||||
<section className="flex flex-col gap-8 pt-8">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={transition}
|
||||
className="flex flex-col gap-6"
|
||||
>
|
||||
{/* Avatar and Status */}
|
||||
<div className="flex items-center gap-5 mb-2">
|
||||
<div className="relative">
|
||||
<div className="w-16 h-16 rounded-full overflow-hidden border border-white/10 shadow-xl">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1544725176-7c40e5a71c5e?auto=format&fit=crop&q=80&w=150&h=150"
|
||||
alt="Leonardo"
|
||||
className="w-full h-full object-cover grayscale hover:grayscale-0 transition-all duration-500"
|
||||
/>
|
||||
</div>
|
||||
<span className="absolute bottom-0 right-0 w-4 h-4 rounded-full border-[3px] border-zinc-950 bg-emerald-500 shadow-[0_0_8px_rgba(16,185,129,0.5)] animate-pulse" />
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-zinc-100 font-medium tracking-tight">Leonardo Silva</span>
|
||||
<span className="text-sm font-mono text-zinc-500">Disponível para novos projetos</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 className="text-4xl sm:text-5xl md:text-6xl font-semibold tracking-tight text-white max-w-3xl leading-[1.1] relative">
|
||||
Engenheiro de Software &<br /> Designer de Interfaces.
|
||||
</h1>
|
||||
</motion.div>
|
||||
|
||||
<motion.p
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ ...transition, delay: 0.1 }}
|
||||
className="text-lg md:text-xl text-zinc-400 max-w-2xl leading-relaxed"
|
||||
>
|
||||
Construo aplicações web modernas focadas em performance, tipografia refinada e experiências de usuário memoráveis. Apaixonado pela intersecção entre bom design e código limpo.
|
||||
</motion.p>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ ...transition, delay: 0.2 }}
|
||||
className="flex flex-wrap items-center gap-4 pt-2"
|
||||
>
|
||||
<Link
|
||||
to="/curriculo"
|
||||
className="h-11 px-5 inline-flex items-center gap-2 justify-center bg-zinc-50 text-zinc-950 text-sm font-semibold rounded-full hover:bg-zinc-200 transition-colors shadow-[0_0_20px_rgba(255,255,255,0.1)] hover:shadow-[0_0_25px_rgba(255,255,255,0.2)]"
|
||||
>
|
||||
<Briefcase className="w-4 h-4" />
|
||||
Ver Currículo
|
||||
</Link>
|
||||
<Link
|
||||
to="/blog"
|
||||
className="h-11 px-5 inline-flex items-center gap-2 justify-center bg-zinc-900 text-zinc-50 text-sm font-medium rounded-full border border-zinc-800 hover:bg-zinc-800 transition-colors"
|
||||
>
|
||||
Ler Artigos
|
||||
</Link>
|
||||
</motion.div>
|
||||
</section>
|
||||
|
||||
{/* Recent Posts Grid-like Section */}
|
||||
<section className="flex flex-col gap-8">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={transition}
|
||||
className="flex items-center justify-between border-b border-white/[0.08] pb-4"
|
||||
>
|
||||
<h2 className="text-xl font-medium tracking-tight text-zinc-100">Escritos Recentes</h2>
|
||||
<Link to="/blog" className="text-sm font-medium text-zinc-500 hover:text-zinc-300 transition-colors flex items-center gap-1 group">
|
||||
Ver todos
|
||||
<ArrowRight className="w-4 h-4 group-hover:translate-x-0.5 transition-transform" />
|
||||
</Link>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{recentPosts.map((post, i) => (
|
||||
<motion.div
|
||||
key={post.slug}
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ ...transition, delay: i * 0.1 }}
|
||||
className="h-full"
|
||||
>
|
||||
<Link
|
||||
to={`/blog/${post.slug}`}
|
||||
className="group flex flex-col h-full gap-4 p-6 bg-zinc-900/30 border border-zinc-800/60 hover:border-zinc-700 hover:bg-zinc-900/50 rounded-2xl transition-all duration-300"
|
||||
>
|
||||
{post.coverImage && (
|
||||
<div className="w-full h-40 rounded-xl overflow-hidden mb-1 border border-zinc-800/80 shrink-0">
|
||||
<img
|
||||
src={post.coverImage}
|
||||
alt={post.title}
|
||||
className="w-full h-full object-cover grayscale group-hover:grayscale-0 transition-all duration-700 scale-100 group-hover:scale-105"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<time className="text-xs font-mono text-zinc-500">
|
||||
{new Date(post.date).toLocaleDateString('pt-BR', { year: 'numeric', month: 'short', day: 'numeric' })}
|
||||
</time>
|
||||
<div className="flex flex-col gap-2 flex-grow">
|
||||
<h3 className="text-lg font-medium text-zinc-200 group-hover:text-white transition-colors leading-tight">
|
||||
{post.title}
|
||||
</h3>
|
||||
<p className="text-sm text-zinc-400 line-clamp-3">
|
||||
{post.excerpt}
|
||||
</p>
|
||||
</div>
|
||||
<div className="pt-4 border-t border-zinc-800/50 flex align-center justify-between">
|
||||
<span className="text-xs font-mono text-zinc-500">{post.readTime}</span>
|
||||
<ArrowRight className="w-4 h-4 text-zinc-600 group-hover:text-zinc-300 transition-colors group-hover:translate-x-1" />
|
||||
</div>
|
||||
</Link>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
32
Template-01/src/pages/NotFound.tsx
Normal file
32
Template-01/src/pages/NotFound.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { motion } from "motion/react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
|
||||
export function NotFound() {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center min-h-[60vh] text-center gap-6">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ type: "spring", bounce: 0, duration: 0.8 }}
|
||||
className="flex flex-col items-center gap-4"
|
||||
>
|
||||
<span className="text-6xl font-mono font-bold text-zinc-800">404</span>
|
||||
<h1 className="text-2xl font-semibold tracking-tight text-zinc-200">
|
||||
Página não encontrada
|
||||
</h1>
|
||||
<p className="text-zinc-400 max-w-sm">
|
||||
A página que você está procurando não existe ou foi movida.
|
||||
</p>
|
||||
|
||||
<Link
|
||||
to="/"
|
||||
className="mt-4 h-11 px-5 inline-flex items-center gap-2 justify-center bg-zinc-50 text-zinc-950 text-sm font-medium rounded-full hover:bg-zinc-200 transition-colors"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4" />
|
||||
Voltar para o início
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
98
Template-01/src/pages/Post.tsx
Normal file
98
Template-01/src/pages/Post.tsx
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import { useParams, Navigate, Link } from "react-router-dom";
|
||||
import { motion } from "motion/react";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
import { BLOG_POSTS } from "../lib/data";
|
||||
|
||||
const transition = { type: "spring", bounce: 0, duration: 0.8 };
|
||||
|
||||
export function Post() {
|
||||
const { slug } = useParams();
|
||||
const post = BLOG_POSTS.find(p => p.slug === slug);
|
||||
|
||||
if (!post) {
|
||||
return <Navigate to="/blog" replace />;
|
||||
}
|
||||
|
||||
// Simple markdown-to-html converter just for the demo content
|
||||
const renderContent = (content: string) => {
|
||||
return content.split('\n').map((line, i) => {
|
||||
if (line.startsWith('## ')) {
|
||||
return <h2 key={i} className="text-2xl font-semibold text-white mt-12 mb-6 tracking-tight">{line.replace('## ', '')}</h2>;
|
||||
}
|
||||
if (line.startsWith('### ')) {
|
||||
return <h3 key={i} className="text-xl font-medium text-zinc-200 mt-8 mb-4">{line.replace('### ', '')}</h3>;
|
||||
}
|
||||
if (line.startsWith('> ')) {
|
||||
return <blockquote key={i} className="border-l-2 border-zinc-700 pl-4 italic text-zinc-400 my-6">{line.replace('> ', '')}</blockquote>;
|
||||
}
|
||||
if (line.startsWith('- ')) {
|
||||
// Regex to parse _italic_ inside lists
|
||||
const formatted = line.replace('- ', '').replace(/_(.*?)_/g, '<em class="text-zinc-200 font-medium not-italic">$1</em>');
|
||||
return <li key={i} className="text-zinc-400 ml-4 list-disc mb-2" dangerouslySetInnerHTML={{ __html: formatted }} />;
|
||||
}
|
||||
if (line.startsWith('1. ') || line.startsWith('2. ') || line.startsWith('3. ') || line.startsWith('4. ') || line.startsWith('5. ')) {
|
||||
const formatted = line.substring(3).replace(/\*\*(.*?)\*\*/g, '<strong class="text-zinc-200 font-medium">$1</strong>');
|
||||
return <li key={i} className="text-zinc-400 ml-4 list-decimal mb-2" dangerouslySetInnerHTML={{ __html: formatted }} />;
|
||||
}
|
||||
if (line.trim() === '') {
|
||||
return <br key={i} />;
|
||||
}
|
||||
|
||||
if (line.match(/!\[(.*?)\]\((.*?)\)/)) {
|
||||
const formatted = line.replace(/!\[(.*?)\]\((.*?)\)/g, '<img src="$2" alt="$1" class="w-full rounded-2xl my-10 border border-white/[0.08] grayscale hover:grayscale-0 transition-all duration-700 object-cover" />');
|
||||
return <div key={i} dangerouslySetInnerHTML={{ __html: formatted }} />;
|
||||
}
|
||||
|
||||
const formatted = line.replace(/`([^`]+)`/g, '<code class="bg-zinc-900 border border-zinc-800 text-zinc-300 px-1.5 py-0.5 rounded-md text-[0.85em] font-mono">$1</code>');
|
||||
return <p key={i} className="text-zinc-400 leading-relaxed mb-6" dangerouslySetInnerHTML={{ __html: formatted }} />;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<article className="flex flex-col max-w-2xl mx-auto">
|
||||
<Link
|
||||
to="/blog"
|
||||
className="inline-flex items-center gap-2 text-sm text-zinc-500 hover:text-zinc-300 transition-colors mb-12 w-fit group"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-0.5 transition-transform" />
|
||||
Voltar para Artigos
|
||||
</Link>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={transition}
|
||||
>
|
||||
<header className="flex flex-col gap-6 mb-12">
|
||||
<h1 className="text-3xl md:text-4xl font-semibold tracking-tight text-white leading-[1.2]">
|
||||
{post.title}
|
||||
</h1>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-4 text-sm font-mono text-zinc-500 border-y border-zinc-800/60 py-4">
|
||||
<time>
|
||||
{new Date(post.date).toLocaleDateString('pt-BR', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
</time>
|
||||
<span className="w-1 h-1 rounded-full bg-zinc-700" />
|
||||
<span>{post.readTime} de leitura</span>
|
||||
<span className="w-1 h-1 rounded-full bg-zinc-700" />
|
||||
<div className="flex gap-2">
|
||||
{post.tags.map(tag => (
|
||||
<span key={tag} className="uppercase tracking-wider">{tag}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{post.coverImage && (
|
||||
<div className="w-full h-64 md:h-[400px] rounded-3xl overflow-hidden border border-white/[0.08] mb-12 shadow-2xl shrink-0">
|
||||
<img src={post.coverImage} className="w-full h-full object-cover grayscale hover:grayscale-0 transition-all duration-700" alt={post.title} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="prose prose-invert max-w-none">
|
||||
{renderContent(post.content)}
|
||||
</div>
|
||||
</motion.div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
218
Template-01/src/pages/Resume.tsx
Normal file
218
Template-01/src/pages/Resume.tsx
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
import { motion } from "motion/react";
|
||||
import { MapPin, Mail, Globe, Printer, Download, Github, Linkedin, Calendar, ExternalLink } from "lucide-react";
|
||||
|
||||
const transition = { type: "spring", bounce: 0, duration: 0.8 };
|
||||
|
||||
export function Resume() {
|
||||
const handlePrint = () => {
|
||||
window.print();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col max-w-3xl mx-auto gap-16 pb-12 print:text-zinc-900 print:gap-10">
|
||||
|
||||
{/* Action Bar (Hidden in Print) */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={transition}
|
||||
className="flex items-center justify-end gap-3 print:hidden -mb-8"
|
||||
>
|
||||
<button
|
||||
onClick={handlePrint}
|
||||
className="flex items-center gap-2 h-9 px-4 rounded-md bg-zinc-900 border border-zinc-800 text-sm font-medium text-zinc-300 hover:bg-zinc-800 hover:text-white transition-colors"
|
||||
>
|
||||
<Printer className="w-4 h-4" />
|
||||
<span>Imprimir / PDF</span>
|
||||
</button>
|
||||
</motion.div>
|
||||
|
||||
{/* Header */}
|
||||
<motion.header
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ ...transition, delay: 0.1 }}
|
||||
className="flex flex-col-reverse md:flex-row md:items-start justify-between gap-8 md:gap-12"
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col gap-1">
|
||||
<h1 className="text-4xl sm:text-5xl font-semibold tracking-tight text-white print:text-zinc-900">
|
||||
Leonardo Silva
|
||||
</h1>
|
||||
<h2 className="text-xl text-zinc-400 print:text-zinc-600">
|
||||
Engenheiro Front-end Sênior & UI Designer
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<p className="text-zinc-400 print:text-zinc-700 max-w-xl leading-relaxed mt-2 text-base">
|
||||
Especialista em construir produtos digitais de alta performance e acessibilidade.
|
||||
Focado na criação de experiências ricas, combinando arquitetura sólida de front-end com um design milimetricamente polido.
|
||||
Apaixonado pela cultura open-source e pelos ecossistemas React/TypeScript.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row flex-wrap gap-x-6 gap-y-3 mt-2 text-sm font-mono text-zinc-500 print:text-zinc-600">
|
||||
<div className="flex items-center gap-2">
|
||||
<MapPin className="w-4 h-4" />
|
||||
<span>São Paulo, BR</span>
|
||||
</div>
|
||||
<a href="mailto:contato@leonardo.dev" className="flex items-center gap-2 hover:text-white transition-colors print:text-zinc-600">
|
||||
<Mail className="w-4 h-4" />
|
||||
<span>contato@leonardo.dev</span>
|
||||
</a>
|
||||
<a href="#" className="flex items-center gap-2 hover:text-white transition-colors print:text-zinc-600">
|
||||
<Globe className="w-4 h-4" />
|
||||
<span>leonardo.dev</span>
|
||||
</a>
|
||||
<a href="#" className="flex items-center gap-2 hover:text-white transition-colors print:hidden">
|
||||
<Github className="w-4 h-4" />
|
||||
<span>github.com/leosilva</span>
|
||||
</a>
|
||||
<a href="#" className="flex items-center gap-2 hover:text-white transition-colors print:hidden">
|
||||
<Linkedin className="w-4 h-4" />
|
||||
<span>linkedin.com/in/leosilva</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative shrink-0">
|
||||
<div className="w-32 h-32 md:w-40 md:h-40 rounded-3xl overflow-hidden border border-white/10 shadow-2xl print:shadow-none print:border-zinc-300">
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1544725176-7c40e5a71c5e?auto=format&fit=crop&q=80&w=300&h=300"
|
||||
alt="Leonardo Silva"
|
||||
className="w-full h-full object-cover grayscale hover:grayscale-0 transition-all duration-700"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</motion.header>
|
||||
|
||||
<div className="h-px bg-white/[0.08] w-full print:bg-zinc-200" />
|
||||
|
||||
{/* Experience */}
|
||||
<motion.section
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ ...transition, delay: 0.2 }}
|
||||
className="flex flex-col gap-8"
|
||||
>
|
||||
<h3 className="text-2xl font-semibold tracking-tight text-white flex items-center gap-3 print:text-zinc-900">
|
||||
Experiência Profissional
|
||||
</h3>
|
||||
|
||||
<div className="flex flex-col gap-10">
|
||||
|
||||
{/* Job 1 */}
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex justify-between items-start gap-4 flex-col sm:flex-row">
|
||||
<div className="flex flex-col">
|
||||
<h4 className="text-lg font-medium text-zinc-100 print:text-zinc-900">Engenheiro Front-end Sênior</h4>
|
||||
<a href="#" className="text-zinc-400 print:text-zinc-600 hover:text-white transition-colors inline-flex items-center gap-1">
|
||||
Vercel <ExternalLink className="w-3 h-3 print:hidden" />
|
||||
</a>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-sm font-mono text-zinc-500 bg-zinc-900/50 print:bg-transparent px-3 py-1 rounded-full border border-zinc-800 print:border-none print:p-0">
|
||||
<Calendar className="w-4 h-4" />
|
||||
<span>2023 - Presente</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-zinc-400 print:text-zinc-700 leading-relaxed text-sm">
|
||||
Liderou a equipe principal de front-end responsável pelo dashboard de analytics.
|
||||
Implementou micro-frontends inovadores e arquitetura baseada em Server Components (Next.js 14+), melhorando o TTI (Time to Interactive) em 45%. Desenvolveu e manteve o Design System interno consumido por +50 desenvolvedores.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap gap-2 mt-2 print:hidden">
|
||||
{["React", "Next.js", "TypeScript", "Tailwind CSS", "Framer Motion"].map(skill => (
|
||||
<span key={skill} className="px-2.5 py-1 text-xs font-mono bg-zinc-900 text-zinc-300 rounded border border-zinc-800">
|
||||
{skill}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="hidden print:block text-xs font-mono text-zinc-500 mt-1">
|
||||
Stack: React, Next.js, TypeScript, Tailwind CSS, Framer Motion
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Job 2 */}
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex justify-between items-start gap-4 flex-col sm:flex-row">
|
||||
<div className="flex flex-col">
|
||||
<h4 className="text-lg font-medium text-zinc-100 print:text-zinc-900">Desenvolvedor Full-stack Pleno</h4>
|
||||
<a href="#" className="text-zinc-400 print:text-zinc-600 hover:text-white transition-colors inline-flex items-center gap-1">
|
||||
Nubank <ExternalLink className="w-3 h-3 print:hidden" />
|
||||
</a>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-sm font-mono text-zinc-500 bg-zinc-900/50 print:bg-transparent px-3 py-1 rounded-full border border-zinc-800 print:border-none print:p-0">
|
||||
<Calendar className="w-4 h-4" />
|
||||
<span>2020 - 2023</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-zinc-400 print:text-zinc-700 leading-relaxed text-sm">
|
||||
Trabalhou na equipe de aquisição e onboarding, otimizando o funil de usuários e desenhando fluxos mais seguros de KYC.
|
||||
Responsável por migrar aplicações legadas de SPA (Single Page Application) tradicional para Node.js com SSR (Server-Side Rendering).
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap gap-2 mt-2 print:hidden">
|
||||
{["React", "Node.js", "GraphQL", "PostgreSQL", "Jest"].map(skill => (
|
||||
<span key={skill} className="px-2.5 py-1 text-xs font-mono bg-zinc-900 text-zinc-300 rounded border border-zinc-800">
|
||||
{skill}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="hidden print:block text-xs font-mono text-zinc-500 mt-1">
|
||||
Stack: React, Node.js, GraphQL, PostgreSQL, Jest
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</motion.section>
|
||||
|
||||
<div className="h-px bg-white/[0.08] w-full print:bg-zinc-200" />
|
||||
|
||||
{/* Education & Skills Grid */}
|
||||
<motion.section
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ ...transition, delay: 0.3 }}
|
||||
className="grid grid-cols-1 md:grid-cols-2 gap-12"
|
||||
>
|
||||
<div className="flex flex-col gap-6">
|
||||
<h3 className="text-2xl font-semibold tracking-tight text-white print:text-zinc-900">
|
||||
Formação Acadêmica
|
||||
</h3>
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
<h4 className="text-lg font-medium text-zinc-100 print:text-zinc-900">Ciência da Computação (Bacharelado)</h4>
|
||||
<span className="text-zinc-400 print:text-zinc-600">Universidade de São Paulo (USP)</span>
|
||||
<div className="flex items-center gap-2 text-sm font-mono text-zinc-500">
|
||||
<Calendar className="w-4 h-4" />
|
||||
<span>2016 - 2020</span>
|
||||
</div>
|
||||
<p className="text-zinc-400 print:text-zinc-700 leading-relaxed text-sm mt-2">
|
||||
Foco em Arquitetura de Software e Sistemas Distribuídos. Trabalho de Conclusão sobre Otimização de Compiladores JIT.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-6">
|
||||
<h3 className="text-2xl font-semibold tracking-tight text-white print:text-zinc-900">
|
||||
Hard Skills
|
||||
</h3>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{[
|
||||
"JavaScript (ESNext)", "TypeScript", "React", "Next.js", "Node.js",
|
||||
"Tailwind CSS", "GraphQL", "Figma", "UI/UX", "Web Performance",
|
||||
"Acessibilidade (WCAG)", "CI/CD", "Git"
|
||||
].map(skill => (
|
||||
<span key={skill} className="px-3 py-1.5 text-sm font-medium bg-zinc-900/80 text-zinc-300 rounded-md border border-zinc-800 print:bg-transparent print:border-zinc-300 print:text-zinc-800">
|
||||
{skill}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</motion.section>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
26
Template-01/tsconfig.json
Normal file
26
Template-01/tsconfig.json
Normal 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
|
||||
}
|
||||
}
|
||||
24
Template-01/vite.config.ts
Normal file
24
Template-01/vite.config.ts
Normal 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',
|
||||
},
|
||||
};
|
||||
});
|
||||
20
Template-02/README.md
Normal file
20
Template-02/README.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<div align="center">
|
||||
<img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
|
||||
</div>
|
||||
|
||||
# Run and deploy your AI Studio app
|
||||
|
||||
This contains everything you need to run your app locally.
|
||||
|
||||
View your app in AI Studio: https://ai.studio/apps/7acb8a3c-e597-4c44-ace5-d65ec976ac1b
|
||||
|
||||
## Run Locally
|
||||
|
||||
**Prerequisites:** Node.js
|
||||
|
||||
|
||||
1. Install dependencies:
|
||||
`npm install`
|
||||
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
|
||||
3. Run the app:
|
||||
`npm run dev`
|
||||
15
Template-02/index.html
Normal file
15
Template-02/index.html
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<!-- INJECT_SEO -->
|
||||
<link rel="icon" 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#1c1917%22/><text y=%2250%%22 x=%2250%%22 text-anchor=%22middle%22 dominant-baseline=%22central%22 font-family=%22Playfair Display, serif%22 font-style=%22italic%22 font-size=%2255%22 fill=%22#FDFBF7%22>H</text></svg>">
|
||||
<title>Helena Costa | Front-end Engineer</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
6
Template-02/metadata.json
Normal file
6
Template-02/metadata.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "Design-Driven Personal Blog",
|
||||
"description": "A sleek, professional personal blog with a minimal, tech-focused aesthetic inspired by Vercel.",
|
||||
"requestFramePermissions": [],
|
||||
"majorCapabilities": []
|
||||
}
|
||||
5596
Template-02/package-lock.json
generated
Normal file
5596
Template-02/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
40
Template-02/package.json
Normal file
40
Template-02/package.json
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"name": "react-example",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "tsx server.ts",
|
||||
"build": "vite build && tsc --noEmit",
|
||||
"preview": "NODE_ENV=production tsx server.ts",
|
||||
"start": "NODE_ENV=production tsx server.ts",
|
||||
"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",
|
||||
"date-fns": "^4.1.0",
|
||||
"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-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"
|
||||
}
|
||||
}
|
||||
127
Template-02/server.ts
Normal file
127
Template-02/server.ts
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import express from "express";
|
||||
import { createServer as createViteServer } from "vite";
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { BLOG_POSTS } from "./src/lib/data.ts";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
async function startServer() {
|
||||
const app = express();
|
||||
const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3000;
|
||||
|
||||
let vite: any;
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
vite = await createViteServer({
|
||||
server: { middlewareMode: true },
|
||||
appType: "custom",
|
||||
});
|
||||
app.use(vite.middlewares);
|
||||
} else {
|
||||
// Serve static files from dist/assets and others, but not index.html directly
|
||||
app.use(express.static(path.join(process.cwd(), "dist"), { index: false }));
|
||||
}
|
||||
|
||||
app.get("/sitemap.xml", (req, res) => {
|
||||
const baseUrl = process.env.APP_URL || "https://helenacosta.dev";
|
||||
|
||||
let xml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url><loc>${baseUrl}/</loc><changefreq>weekly</changefreq><priority>1.0</priority></url>
|
||||
<url><loc>${baseUrl}/blog</loc><changefreq>daily</changefreq><priority>0.9</priority></url>
|
||||
<url><loc>${baseUrl}/curriculo</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>
|
||||
<url><loc>${baseUrl}/contato</loc><changefreq>monthly</changefreq><priority>0.8</priority></url>`;
|
||||
|
||||
BLOG_POSTS.forEach((post) => {
|
||||
xml += `
|
||||
<url><loc>${baseUrl}/blog/${post.slug}</loc><changefreq>monthly</changefreq><priority>0.7</priority></url>`;
|
||||
});
|
||||
|
||||
xml += `
|
||||
</urlset>`;
|
||||
|
||||
res.header("Content-Type", "application/xml");
|
||||
res.send(xml);
|
||||
});
|
||||
|
||||
app.get("/robots.txt", (req, res) => {
|
||||
const baseUrl = process.env.APP_URL || "https://helenacosta.dev";
|
||||
const txt = `User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: ${baseUrl}/sitemap.xml`;
|
||||
res.header("Content-Type", "text/plain");
|
||||
res.send(txt);
|
||||
});
|
||||
|
||||
app.get("*", async (req, res) => {
|
||||
try {
|
||||
let template, render;
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
template = await fs.readFile(path.join(__dirname, "index.html"), "utf-8");
|
||||
template = await vite.transformIndexHtml(req.originalUrl, template);
|
||||
} else {
|
||||
template = await fs.readFile(path.join(process.cwd(), "dist", "index.html"), "utf-8");
|
||||
}
|
||||
|
||||
// Default tags
|
||||
let title = "Helena Costa | Front-end Engineer & Designer";
|
||||
let description = "Engenheira de Software & Designer de Interfaces. Construindo experiências web de alta performance.";
|
||||
let image = "https://images.unsplash.com/photo-1544725176-7c40e5a71c5e?auto=format&fit=crop&q=80&w=1200&h=630";
|
||||
const appUrl = process.env.APP_URL || "https://helenacosta.dev";
|
||||
|
||||
// If viewing a post
|
||||
if (req.originalUrl.startsWith("/blog/")) {
|
||||
const slug = req.originalUrl.split("/")[2];
|
||||
const post = BLOG_POSTS.find(p => p.slug === slug);
|
||||
if (post) {
|
||||
title = `${post.title} | Helena Costa`;
|
||||
description = post.excerpt;
|
||||
if (post.coverImage) {
|
||||
image = post.coverImage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replace generic meta tags in index.html with specific ones
|
||||
let html = template
|
||||
.replace(/<title>.*<\/title>/i, `<title>${title}</title>`)
|
||||
.replace(
|
||||
/<!-- INJECT_SEO -->/,
|
||||
`
|
||||
<meta name="description" content="${description}" />
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="${appUrl}${req.originalUrl}" />
|
||||
<meta property="og:title" content="${title}" />
|
||||
<meta property="og:description" content="${description}" />
|
||||
<meta property="og:image" content="${image}" />
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:url" content="${appUrl}${req.originalUrl}" />
|
||||
<meta property="twitter:title" content="${title}" />
|
||||
<meta property="twitter:description" content="${description}" />
|
||||
<meta property="twitter:image" content="${image}" />
|
||||
`
|
||||
);
|
||||
|
||||
res.status(200).set({ "Content-Type": "text/html" }).end(html);
|
||||
} catch (e) {
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
vite.ssrFixStacktrace(e as Error);
|
||||
}
|
||||
console.error(e);
|
||||
res.status(500).end((e as Error).message);
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(PORT, "0.0.0.0", () => {
|
||||
console.log(`Server running on http://0.0.0.0:${PORT}`);
|
||||
});
|
||||
}
|
||||
|
||||
startServer();
|
||||
30
Template-02/src/App.tsx
Normal file
30
Template-02/src/App.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* @license
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
||||
import { RootLayout } from "./layouts/RootLayout";
|
||||
import { Home } from "./pages/Home";
|
||||
import { Blog } from "./pages/Blog";
|
||||
import { Post } from "./pages/Post";
|
||||
import { Resume } from "./pages/Resume";
|
||||
import { Contact } from "./pages/Contact";
|
||||
import { NotFound } from "./pages/NotFound";
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<RootLayout />}>
|
||||
<Route index element={<Home />} />
|
||||
<Route path="blog" element={<Blog />} />
|
||||
<Route path="blog/:slug" element={<Post />} />
|
||||
<Route path="curriculo" element={<Resume />} />
|
||||
<Route path="contato" element={<Contact />} />
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
||||
56
Template-02/src/hooks/usePosts.ts
Normal file
56
Template-02/src/hooks/usePosts.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import { BLOG_POSTS, BlogPost } from '../lib/data';
|
||||
|
||||
export function usePosts() {
|
||||
const [posts, setPosts] = useState<BlogPost[]>(BLOG_POSTS);
|
||||
|
||||
const loadPosts = () => {
|
||||
const localPostsData = localStorage.getItem('custom_posts');
|
||||
let localPosts: BlogPost[] = [];
|
||||
if (localPostsData) {
|
||||
try {
|
||||
localPosts = JSON.parse(localPostsData);
|
||||
} catch(e) {}
|
||||
}
|
||||
const combined = [...localPosts, ...BLOG_POSTS].sort((a,b) => new Date(b.date).getTime() - new Date(a.date).getTime());
|
||||
setPosts(combined);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadPosts();
|
||||
window.addEventListener('posts_updated', loadPosts);
|
||||
return () => window.removeEventListener('posts_updated', loadPosts);
|
||||
}, []);
|
||||
|
||||
const savePost = (post: BlogPost) => {
|
||||
const localPostsData = localStorage.getItem('custom_posts');
|
||||
let localPosts: BlogPost[] = [];
|
||||
if (localPostsData) {
|
||||
try {
|
||||
localPosts = JSON.parse(localPostsData);
|
||||
} catch(e) {}
|
||||
}
|
||||
const existingIndex = localPosts.findIndex(p => p.slug === post.slug);
|
||||
if (existingIndex >= 0) {
|
||||
localPosts[existingIndex] = post;
|
||||
} else {
|
||||
localPosts.push(post);
|
||||
}
|
||||
localStorage.setItem('custom_posts', JSON.stringify(localPosts));
|
||||
window.dispatchEvent(new Event('posts_updated'));
|
||||
};
|
||||
|
||||
const deletePost = (slug: string) => {
|
||||
const localPostsData = localStorage.getItem('custom_posts');
|
||||
if (localPostsData) {
|
||||
try {
|
||||
let localPosts: BlogPost[] = JSON.parse(localPostsData);
|
||||
localPosts = localPosts.filter(p => p.slug !== slug);
|
||||
localStorage.setItem('custom_posts', JSON.stringify(localPosts));
|
||||
window.dispatchEvent(new Event('posts_updated'));
|
||||
} catch(e) {}
|
||||
}
|
||||
};
|
||||
|
||||
return { posts, savePost, deletePost };
|
||||
}
|
||||
15
Template-02/src/index.css
Normal file
15
Template-02/src/index.css
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500&family=JetBrains+Mono:wght@400;500&display=swap');
|
||||
@import "tailwindcss";
|
||||
|
||||
@theme {
|
||||
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
||||
--font-serif: "Playfair Display", ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
|
||||
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
@apply bg-[#FDFBF7] text-stone-800 antialiased selection:bg-stone-200 selection:text-stone-900;
|
||||
}
|
||||
}
|
||||
|
||||
82
Template-02/src/layouts/RootLayout.tsx
Normal file
82
Template-02/src/layouts/RootLayout.tsx
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import { ReactNode } from "react";
|
||||
import { Link, Outlet, useLocation } from "react-router-dom";
|
||||
import { Github, Twitter, Linkedin, Mail } from "lucide-react";
|
||||
import { cn } from "../lib/utils";
|
||||
import { motion } from "motion/react";
|
||||
|
||||
function NavLink({ to, children, active }: { to: string; children: ReactNode; active?: boolean }) {
|
||||
return (
|
||||
<Link
|
||||
to={to}
|
||||
className={cn(
|
||||
"relative text-sm tracking-wide transition-colors hover:text-stone-900",
|
||||
active ? "text-stone-900 font-medium" : "text-stone-500"
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
{active && (
|
||||
<motion.div
|
||||
layoutId="nav-indicator"
|
||||
className="absolute -bottom-[21px] left-0 right-0 h-[1px] bg-stone-900"
|
||||
initial={false}
|
||||
transition={{ type: "spring", stiffness: 500, damping: 30 }}
|
||||
/>
|
||||
)}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export function RootLayout() {
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#FDFBF7] font-sans text-stone-800 flex flex-col selection:bg-stone-200 selection:text-stone-900 print:bg-white print:text-stone-900">
|
||||
{/* Header */}
|
||||
<header className="sticky top-0 z-50 border-b border-stone-200/50 bg-[#FDFBF7]/80 backdrop-blur-xl print:hidden">
|
||||
<div className="mx-auto max-w-5xl px-4 md:px-8 h-20 flex items-center justify-between">
|
||||
<Link to="/" className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-xl bg-stone-900 text-[#FDFBF7] flex items-center justify-center font-serif italic text-2xl pb-1 shadow-sm">H</div>
|
||||
<span className="text-xl font-serif italic font-medium tracking-tight text-stone-900 hidden sm:block">Helena.</span>
|
||||
</Link>
|
||||
|
||||
<nav className="flex items-center gap-4 sm:gap-6">
|
||||
<NavLink to="/" active={location.pathname === "/"}>Início</NavLink>
|
||||
<NavLink to="/blog" active={location.pathname.startsWith("/blog")}>Artigos</NavLink>
|
||||
<NavLink to="/curriculo" active={location.pathname === "/curriculo"}>Currículo</NavLink>
|
||||
<NavLink to="/contato" active={location.pathname === "/contato"}>Contato</NavLink>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="flex-1 w-full max-w-5xl mx-auto px-4 md:px-8 py-12 md:py-24 print:py-0 print:px-0 print:max-w-none">
|
||||
<Outlet />
|
||||
</main>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="border-t border-stone-200/50 py-16 text-stone-500 print:hidden mt-auto">
|
||||
<div className="max-w-5xl mx-auto px-4 md:px-8 flex flex-col md:flex-row justify-between items-center gap-8">
|
||||
<div className="flex flex-col items-center md:items-start gap-2">
|
||||
<div className="w-8 h-8 rounded-lg bg-stone-200 text-stone-600 flex items-center justify-center font-serif italic text-xl pb-1">H</div>
|
||||
<span className="text-sm font-serif italic text-stone-600">© {new Date().getFullYear()} Helena Costa. Direitos reservados.</span>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4">
|
||||
<a href="#" className="hover:text-stone-900 transition-colors">
|
||||
<Twitter className="w-4 h-4" />
|
||||
</a>
|
||||
<a href="#" className="hover:text-stone-900 transition-colors">
|
||||
<Github className="w-4 h-4" />
|
||||
</a>
|
||||
<a href="#" className="hover:text-stone-900 transition-colors">
|
||||
<Linkedin className="w-4 h-4" />
|
||||
</a>
|
||||
<a href="#" className="hover:text-stone-900 transition-colors">
|
||||
<Mail className="w-4 h-4" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
307
Template-02/src/lib/data.ts
Normal file
307
Template-02/src/lib/data.ts
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
export interface BlogPost {
|
||||
slug: string;
|
||||
title: string;
|
||||
excerpt: string;
|
||||
content: string;
|
||||
date: string;
|
||||
readTime: string;
|
||||
tags: string[];
|
||||
coverImage?: string;
|
||||
}
|
||||
|
||||
export const BLOG_POSTS: BlogPost[] = [
|
||||
{
|
||||
"slug": "entendendo-o-state-management-em-2026",
|
||||
"title": "Entendendo o State Management em 2026",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA evolução de **entendendo o state management em 2026** transcendeu a mera estética. Hoje, caminhamos para um paradigma onde a tecnologia dita as regras de forma invisível.\n\n## Menos Ruído, Mais Foco\n\nA inspiração em tech leads e empresas gigantes nos mostra que os usuários não querem ser surpreendidos por abordagens complexas; eles querem previsibilidade e clareza absoluta quando lidam com entendendo o state management em 2026.\n\n> \"Qualquer tolo consegue escrever código que um computador entenda. Bons programadores escrevem código que humanos entendam.\" — Martin Fowler\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Foco absoluto no desempenho** desde o primeiro commit.\n- **Feedback visual constante** usando padrões eficientes.\n- **Integração contínua** com ferramentas limpas.\n\n\n\n### Conclusão e Próximos Passos\n\nA simplicidade e a eficiência não são acidentais. São construídas linha por linha, com paciência e método. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-12-12",
|
||||
"readTime": "4 min",
|
||||
"tags": [
|
||||
"React",
|
||||
"State",
|
||||
"Gerenciamento"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1484417894907-623942c8ee29?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "animações-e-motion-design-na-web",
|
||||
"title": "Animações e Motion Design na Web",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA discussão sobre **animações e motion design na web** nunca foi tão relevante. Com empresas buscando eficiência máxima, dominar este conceito se tornou obrigatório para o engenheiro moderno.\n\n## Caminhos para o Futuro\n\nImplementar estas soluções requer um entendimento sólido de como as peças se encaixam no grande quebra-cabeça da engenharia de software atual.\n\n> \"A restrição gera criatividade. Não pense fora da caixa. Pense no que você pode fazer com a caixa recebida.\" — Princípios de Design Minimalista\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n1. Analise o problema estrutural principal.\n2. Simplifique a solução até sua essência básica.\n3. Escreva documentação clara para a equipe.\n\n\n\n### Conclusão e Próximos Passos\n\nNo final do dia, escrever um código ou desenhar uma interface é sempre, fundamentalmente, criar uma forma de comunicação humana. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-11-11",
|
||||
"readTime": "3 min",
|
||||
"tags": [
|
||||
"Motion",
|
||||
"Animação",
|
||||
"UX"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1481481600465-3652ea6bbec8?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "otimização-de-performance-no-front-end",
|
||||
"title": "Otimização de Performance no Front-end",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA discussão sobre **otimização de performance no front-end** nunca foi tão relevante. Com empresas buscando eficiência máxima, dominar este conceito se tornou obrigatório para o engenheiro moderno.\n\n## Caminhos para o Futuro\n\nImplementar estas soluções requer um entendimento sólido de como as peças se encaixam no grande quebra-cabeça da engenharia de software atual.\n\n> \"A restrição gera criatividade. Não pense fora da caixa. Pense no que você pode fazer com a caixa recebida.\" — Princípios de Design Minimalista\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n1. Analise o problema estrutural principal.\n2. Simplifique a solução até sua essência básica.\n3. Escreva documentação clara para a equipe.\n\n\n\n### Conclusão e Próximos Passos\n\nNo final do dia, escrever um código ou desenhar uma interface é sempre, fundamentalmente, criar uma forma de comunicação humana. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-10-10",
|
||||
"readTime": "7 min",
|
||||
"tags": [
|
||||
"Performance",
|
||||
"Web Vitals",
|
||||
"Web"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1498050108023-c5249f4df085?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "por-que-o-minimalismo-nunca-morre",
|
||||
"title": "Por que o Minimalismo nunca morre",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nAo explorar as nuances do mundo de **Por que o Minimalismo nunca morre**, percebemos o impacto profundo que pequenas decisões têm a longo prazo em nossa área de atuação.\n\n## A Prática Fundamenta a Teoria\n\nO verdadeiro poder de dominar este conceito está em suas fundações e base teórica. Ao priorizar a estabilidade, garantimos escala e facilidade de manutenção no código fonte e na interface.\n\n> \"A simplicidade é o último grau de sofisticação.\" — Leonardo da Vinci\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Arquitetura modularizada** em componentes independentes.\n- **Testes rigorosos** de usabilidade e fluxos complexos.\n- **Eliminação de código inativo** de forma sistemática.\n\n\n\n### Conclusão e Próximos Passos\n\nO futuro será moldado por quem entende de complexidade, mas a oculta atrás de interfaces simples. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-09-21",
|
||||
"readTime": "3 min",
|
||||
"tags": [
|
||||
"Minimalismo",
|
||||
"Design",
|
||||
"Filosofia"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1593640408182-31c70c8268f5?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "server-components-a-nova-era-do-react",
|
||||
"title": "Server Components: A nova era do React",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nAo explorar as nuances do mundo de **Server Components: A nova era do React**, percebemos o impacto profundo que pequenas decisões têm a longo prazo em nossa área de atuação.\n\n## A Prática Fundamenta a Teoria\n\nO verdadeiro poder de dominar este conceito está em suas fundações e base teórica. Ao priorizar a estabilidade, garantimos escala e facilidade de manutenção no código fonte e na interface.\n\n> \"A simplicidade é o último grau de sofisticação.\" — Leonardo da Vinci\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Arquitetura modularizada** em componentes independentes.\n- **Testes rigorosos** de usabilidade e fluxos complexos.\n- **Eliminação de código inativo** de forma sistemática.\n\n\n\n### Conclusão e Próximos Passos\n\nO futuro será moldado por quem entende de complexidade, mas a oculta atrás de interfaces simples. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-09-09",
|
||||
"readTime": "6 min",
|
||||
"tags": [
|
||||
"React",
|
||||
"Server",
|
||||
"Performance"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1550751827-4bd374c3f58b?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "testes-e2e-sem-dor-de-cabeça",
|
||||
"title": "Testes E2E sem dor de cabeça",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nAo explorar as nuances do mundo de **Testes E2E sem dor de cabeça**, percebemos o impacto profundo que pequenas decisões têm a longo prazo em nossa área de atuação.\n\n## A Prática Fundamenta a Teoria\n\nO verdadeiro poder de dominar este conceito está em suas fundações e base teórica. Ao priorizar a estabilidade, garantimos escala e facilidade de manutenção no código fonte e na interface.\n\n> \"A simplicidade é o último grau de sofisticação.\" — Leonardo da Vinci\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Arquitetura modularizada** em componentes independentes.\n- **Testes rigorosos** de usabilidade e fluxos complexos.\n- **Eliminação de código inativo** de forma sistemática.\n\n\n\n### Conclusão e Próximos Passos\n\nO futuro será moldado por quem entende de complexidade, mas a oculta atrás de interfaces simples. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-08-20",
|
||||
"readTime": "7 min",
|
||||
"tags": [
|
||||
"Testes",
|
||||
"QA",
|
||||
"Engenharia"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1550439062-609e1531270e?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "o-poder-da-tipografia-no-design-de-software",
|
||||
"title": "O Poder da Tipografia no Design de Software",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA discussão sobre **o poder da tipografia no design de software** nunca foi tão relevante. Com empresas buscando eficiência máxima, dominar este conceito se tornou obrigatório para o engenheiro moderno.\n\n## Caminhos para o Futuro\n\nImplementar estas soluções requer um entendimento sólido de como as peças se encaixam no grande quebra-cabeça da engenharia de software atual.\n\n> \"A restrição gera criatividade. Não pense fora da caixa. Pense no que você pode fazer com a caixa recebida.\" — Princípios de Design Minimalista\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n1. Analise o problema estrutural principal.\n2. Simplifique a solução até sua essência básica.\n3. Escreva documentação clara para a equipe.\n\n\n\n### Conclusão e Próximos Passos\n\nNo final do dia, escrever um código ou desenhar uma interface é sempre, fundamentalmente, criar uma forma de comunicação humana. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-08-08",
|
||||
"readTime": "5 min",
|
||||
"tags": [
|
||||
"Tipografia",
|
||||
"Design",
|
||||
"Acesso"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1517694712202-14dd9538aa97?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "webassembly-wasm-no-dia-a-dia",
|
||||
"title": "WebAssembly (Wasm) no dia a dia",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA evolução de **webassembly (wasm) no dia a dia** transcendeu a mera estética. Hoje, caminhamos para um paradigma onde a tecnologia dita as regras de forma invisível.\n\n## Menos Ruído, Mais Foco\n\nA inspiração em tech leads e empresas gigantes nos mostra que os usuários não querem ser surpreendidos por abordagens complexas; eles querem previsibilidade e clareza absoluta quando lidam com webassembly (wasm) no dia a dia.\n\n> \"Qualquer tolo consegue escrever código que um computador entenda. Bons programadores escrevem código que humanos entendam.\" — Martin Fowler\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Foco absoluto no desempenho** desde o primeiro commit.\n- **Feedback visual constante** usando padrões eficientes.\n- **Integração contínua** com ferramentas limpas.\n\n\n\n### Conclusão e Próximos Passos\n\nA simplicidade e a eficiência não são acidentais. São construídas linha por linha, com paciência e método. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-07-19",
|
||||
"readTime": "6 min",
|
||||
"tags": [
|
||||
"Wasm",
|
||||
"Performance",
|
||||
"Web"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1499951360447-b19be8fe80f5?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "design-system-criando-a-fonte-da-verdade",
|
||||
"title": "Design System: Criando a fonte da verdade",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nAo explorar as nuances do mundo de **Design System: Criando a fonte da verdade**, percebemos o impacto profundo que pequenas decisões têm a longo prazo em nossa área de atuação.\n\n## A Prática Fundamenta a Teoria\n\nO verdadeiro poder de dominar este conceito está em suas fundações e base teórica. Ao priorizar a estabilidade, garantimos escala e facilidade de manutenção no código fonte e na interface.\n\n> \"A simplicidade é o último grau de sofisticação.\" — Leonardo da Vinci\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Arquitetura modularizada** em componentes independentes.\n- **Testes rigorosos** de usabilidade e fluxos complexos.\n- **Eliminação de código inativo** de forma sistemática.\n\n\n\n### Conclusão e Próximos Passos\n\nO futuro será moldado por quem entende de complexidade, mas a oculta atrás de interfaces simples. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-07-07",
|
||||
"readTime": "4 min",
|
||||
"tags": [
|
||||
"Design System",
|
||||
"Figma",
|
||||
"UI"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1542831371-29b0f74f9713?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "como-criar-dark-modes-elegantes",
|
||||
"title": "Como criar Dark Modes elegantes",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nAo explorar as nuances do mundo de **Como criar Dark Modes elegantes**, percebemos o impacto profundo que pequenas decisões têm a longo prazo em nossa área de atuação.\n\n## A Prática Fundamenta a Teoria\n\nO verdadeiro poder de dominar este conceito está em suas fundações e base teórica. Ao priorizar a estabilidade, garantimos escala e facilidade de manutenção no código fonte e na interface.\n\n> \"A simplicidade é o último grau de sofisticação.\" — Leonardo da Vinci\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Arquitetura modularizada** em componentes independentes.\n- **Testes rigorosos** de usabilidade e fluxos complexos.\n- **Eliminação de código inativo** de forma sistemática.\n\n\n\n### Conclusão e Próximos Passos\n\nO futuro será moldado por quem entende de complexidade, mas a oculta atrás de interfaces simples. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-06-18",
|
||||
"readTime": "5 min",
|
||||
"tags": [
|
||||
"CSS",
|
||||
"Design",
|
||||
"UI"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1633356122544-f134324a6cee?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "micro-frontends-com-next-js",
|
||||
"title": "Micro-frontends com Next.js",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA discussão sobre **micro-frontends com next.js** nunca foi tão relevante. Com empresas buscando eficiência máxima, dominar este conceito se tornou obrigatório para o engenheiro moderno.\n\n## Caminhos para o Futuro\n\nImplementar estas soluções requer um entendimento sólido de como as peças se encaixam no grande quebra-cabeça da engenharia de software atual.\n\n> \"A restrição gera criatividade. Não pense fora da caixa. Pense no que você pode fazer com a caixa recebida.\" — Princípios de Design Minimalista\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n1. Analise o problema estrutural principal.\n2. Simplifique a solução até sua essência básica.\n3. Escreva documentação clara para a equipe.\n\n\n\n### Conclusão e Próximos Passos\n\nNo final do dia, escrever um código ou desenhar uma interface é sempre, fundamentalmente, criar uma forma de comunicação humana. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-06-06",
|
||||
"readTime": "3 min",
|
||||
"tags": [
|
||||
"Next.js",
|
||||
"Micro-frontends",
|
||||
"Arquitetura"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1451187580459-43490279c0fa?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "o-papel-do-desenvolvedor-full-stack-hoje",
|
||||
"title": "O Papel do Desenvolvedor Full-stack hoje",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA evolução de **o papel do desenvolvedor full-stack hoje** transcendeu a mera estética. Hoje, caminhamos para um paradigma onde a tecnologia dita as regras de forma invisível.\n\n## Menos Ruído, Mais Foco\n\nA inspiração em tech leads e empresas gigantes nos mostra que os usuários não querem ser surpreendidos por abordagens complexas; eles querem previsibilidade e clareza absoluta quando lidam com o papel do desenvolvedor full-stack hoje.\n\n> \"Qualquer tolo consegue escrever código que um computador entenda. Bons programadores escrevem código que humanos entendam.\" — Martin Fowler\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Foco absoluto no desempenho** desde o primeiro commit.\n- **Feedback visual constante** usando padrões eficientes.\n- **Integração contínua** com ferramentas limpas.\n\n\n\n### Conclusão e Próximos Passos\n\nA simplicidade e a eficiência não são acidentais. São construídas linha por linha, com paciência e método. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-05-17",
|
||||
"readTime": "4 min",
|
||||
"tags": [
|
||||
"Carreira",
|
||||
"Engenharia",
|
||||
"Web"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1614332287897-cdc485fa562d?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "webgl-e-experiências-imersivas",
|
||||
"title": "WebGL e Experiências Imersivas",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA evolução de **webgl e experiências imersivas** transcendeu a mera estética. Hoje, caminhamos para um paradigma onde a tecnologia dita as regras de forma invisível.\n\n## Menos Ruído, Mais Foco\n\nA inspiração em tech leads e empresas gigantes nos mostra que os usuários não querem ser surpreendidos por abordagens complexas; eles querem previsibilidade e clareza absoluta quando lidam com webgl e experiências imersivas.\n\n> \"Qualquer tolo consegue escrever código que um computador entenda. Bons programadores escrevem código que humanos entendam.\" — Martin Fowler\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Foco absoluto no desempenho** desde o primeiro commit.\n- **Feedback visual constante** usando padrões eficientes.\n- **Integração contínua** com ferramentas limpas.\n\n\n\n### Conclusão e Próximos Passos\n\nA simplicidade e a eficiência não são acidentais. São construídas linha por linha, com paciência e método. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-05-05",
|
||||
"readTime": "7 min",
|
||||
"tags": [
|
||||
"3D",
|
||||
"WebGL",
|
||||
"Performance"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1558655146-d09347e92766?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "graphql-vs-rest-dilemas-modernos",
|
||||
"title": "GraphQL vs REST: Dilemas Modernos",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nAo explorar as nuances do mundo de **GraphQL vs REST: Dilemas Modernos**, percebemos o impacto profundo que pequenas decisões têm a longo prazo em nossa área de atuação.\n\n## A Prática Fundamenta a Teoria\n\nO verdadeiro poder de dominar este conceito está em suas fundações e base teórica. Ao priorizar a estabilidade, garantimos escala e facilidade de manutenção no código fonte e na interface.\n\n> \"A simplicidade é o último grau de sofisticação.\" — Leonardo da Vinci\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Arquitetura modularizada** em componentes independentes.\n- **Testes rigorosos** de usabilidade e fluxos complexos.\n- **Eliminação de código inativo** de forma sistemática.\n\n\n\n### Conclusão e Próximos Passos\n\nO futuro será moldado por quem entende de complexidade, mas a oculta atrás de interfaces simples. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-04-16",
|
||||
"readTime": "3 min",
|
||||
"tags": [
|
||||
"API",
|
||||
"GraphQL",
|
||||
"Backend"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1605379399642-870262d3d051?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "acessibilidade-na-web-muito-além-do-básico",
|
||||
"title": "Acessibilidade na Web: Muito além do básico",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nAo explorar as nuances do mundo de **Acessibilidade na Web: Muito além do básico**, percebemos o impacto profundo que pequenas decisões têm a longo prazo em nossa área de atuação.\n\n## A Prática Fundamenta a Teoria\n\nO verdadeiro poder de dominar este conceito está em suas fundações e base teórica. Ao priorizar a estabilidade, garantimos escala e facilidade de manutenção no código fonte e na interface.\n\n> \"A simplicidade é o último grau de sofisticação.\" — Leonardo da Vinci\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Arquitetura modularizada** em componentes independentes.\n- **Testes rigorosos** de usabilidade e fluxos complexos.\n- **Eliminação de código inativo** de forma sistemática.\n\n\n\n### Conclusão e Próximos Passos\n\nO futuro será moldado por quem entende de complexidade, mas a oculta atrás de interfaces simples. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-04-04",
|
||||
"readTime": "6 min",
|
||||
"tags": [
|
||||
"Acessibilidade",
|
||||
"a11y",
|
||||
"UX"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1507721999472-8ed4409c4b17?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "integrando-ia-generativa-no-front-end",
|
||||
"title": "Integrando IA Generativa no Front-end",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA discussão sobre **integrando ia generativa no front-end** nunca foi tão relevante. Com empresas buscando eficiência máxima, dominar este conceito se tornou obrigatório para o engenheiro moderno.\n\n## Caminhos para o Futuro\n\nImplementar estas soluções requer um entendimento sólido de como as peças se encaixam no grande quebra-cabeça da engenharia de software atual.\n\n> \"A restrição gera criatividade. Não pense fora da caixa. Pense no que você pode fazer com a caixa recebida.\" — Princípios de Design Minimalista\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n1. Analise o problema estrutural principal.\n2. Simplifique a solução até sua essência básica.\n3. Escreva documentação clara para a equipe.\n\n\n\n### Conclusão e Próximos Passos\n\nNo final do dia, escrever um código ou desenhar uma interface é sempre, fundamentalmente, criar uma forma de comunicação humana. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-03-15",
|
||||
"readTime": "7 min",
|
||||
"tags": [
|
||||
"IA",
|
||||
"LLMs",
|
||||
"Inovação"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1627398246734-d8df98de2fc5?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "tailwind-css-na-prática",
|
||||
"title": "Tailwind CSS na Prática",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA evolução de **tailwind css na prática** transcendeu a mera estética. Hoje, caminhamos para um paradigma onde a tecnologia dita as regras de forma invisível.\n\n## Menos Ruído, Mais Foco\n\nA inspiração em tech leads e empresas gigantes nos mostra que os usuários não querem ser surpreendidos por abordagens complexas; eles querem previsibilidade e clareza absoluta quando lidam com tailwind css na prática.\n\n> \"Qualquer tolo consegue escrever código que um computador entenda. Bons programadores escrevem código que humanos entendam.\" — Martin Fowler\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Foco absoluto no desempenho** desde o primeiro commit.\n- **Feedback visual constante** usando padrões eficientes.\n- **Integração contínua** com ferramentas limpas.\n\n\n\n### Conclusão e Próximos Passos\n\nA simplicidade e a eficiência não são acidentais. São construídas linha por linha, com paciência e método. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-03-03",
|
||||
"readTime": "5 min",
|
||||
"tags": [
|
||||
"CSS",
|
||||
"Tailwind",
|
||||
"Frameworks"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1618761714954-0b8cd0026356?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "edge-computing-para-aplicações-front-end",
|
||||
"title": "Edge Computing para Aplicações Front-end",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA evolução de **edge computing para aplicações front-end** transcendeu a mera estética. Hoje, caminhamos para um paradigma onde a tecnologia dita as regras de forma invisível.\n\n## Menos Ruído, Mais Foco\n\nA inspiração em tech leads e empresas gigantes nos mostra que os usuários não querem ser surpreendidos por abordagens complexas; eles querem previsibilidade e clareza absoluta quando lidam com edge computing para aplicações front-end.\n\n> \"Qualquer tolo consegue escrever código que um computador entenda. Bons programadores escrevem código que humanos entendam.\" — Martin Fowler\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Foco absoluto no desempenho** desde o primeiro commit.\n- **Feedback visual constante** usando padrões eficientes.\n- **Integração contínua** com ferramentas limpas.\n\n\n\n### Conclusão e Próximos Passos\n\nA simplicidade e a eficiência não são acidentais. São construídas linha por linha, com paciência e método. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-02-14",
|
||||
"readTime": "6 min",
|
||||
"tags": [
|
||||
"Edge",
|
||||
"Serverless",
|
||||
"Performance"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1563986768494-4dee2763ff0f?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "arquitetura-escalável-em-react",
|
||||
"title": "Arquitetura Escalável em React",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nAo explorar as nuances do mundo de **Arquitetura Escalável em React**, percebemos o impacto profundo que pequenas decisões têm a longo prazo em nossa área de atuação.\n\n## A Prática Fundamenta a Teoria\n\nO verdadeiro poder de dominar este conceito está em suas fundações e base teórica. Ao priorizar a estabilidade, garantimos escala e facilidade de manutenção no código fonte e na interface.\n\n> \"A simplicidade é o último grau de sofisticação.\" — Leonardo da Vinci\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Arquitetura modularizada** em componentes independentes.\n- **Testes rigorosos** de usabilidade e fluxos complexos.\n- **Eliminação de código inativo** de forma sistemática.\n\n\n\n### Conclusão e Próximos Passos\n\nO futuro será moldado por quem entende de complexidade, mas a oculta atrás de interfaces simples. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-02-02",
|
||||
"readTime": "4 min",
|
||||
"tags": [
|
||||
"React",
|
||||
"Arquitetura",
|
||||
"Engenharia"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1555066931-4365d14bab8c?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "o-fim-das-senhas-autenticação-com-passkeys",
|
||||
"title": "O Fim das Senhas: Autenticação com Passkeys",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA discussão sobre **o fim das senhas: autenticação com passkeys** nunca foi tão relevante. Com empresas buscando eficiência máxima, dominar este conceito se tornou obrigatório para o engenheiro moderno.\n\n## Caminhos para o Futuro\n\nImplementar estas soluções requer um entendimento sólido de como as peças se encaixam no grande quebra-cabeça da engenharia de software atual.\n\n> \"A restrição gera criatividade. Não pense fora da caixa. Pense no que você pode fazer com a caixa recebida.\" — Princípios de Design Minimalista\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n1. Analise o problema estrutural principal.\n2. Simplifique a solução até sua essência básica.\n3. Escreva documentação clara para a equipe.\n\n\n\n### Conclusão e Próximos Passos\n\nNo final do dia, escrever um código ou desenhar uma interface é sempre, fundamentalmente, criar uma forma de comunicação humana. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-01-13",
|
||||
"readTime": "5 min",
|
||||
"tags": [
|
||||
"Segurança",
|
||||
"Autenticação",
|
||||
"WebAuthn"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1515879218367-8466d910aaa4?auto=format&fit=crop&q=80&w=800"
|
||||
},
|
||||
{
|
||||
"slug": "o-futuro-das-interfaces-de-usuário",
|
||||
"title": "O Futuro das Interfaces de Usuário",
|
||||
"excerpt": "Uma reflexão profunda sobre os desafios, as tendências e as melhores práticas evolutivas dentro do cenário atual da engenharia de software.",
|
||||
"content": "\nA evolução de **o futuro das interfaces de usuário** transcendeu a mera estética. Hoje, caminhamos para um paradigma onde a tecnologia dita as regras de forma invisível.\n\n## Menos Ruído, Mais Foco\n\nA inspiração em tech leads e empresas gigantes nos mostra que os usuários não querem ser surpreendidos por abordagens complexas; eles querem previsibilidade e clareza absoluta quando lidam com o futuro das interfaces de usuário.\n\n> \"Qualquer tolo consegue escrever código que um computador entenda. Bons programadores escrevem código que humanos entendam.\" — Martin Fowler\n\nA chave aqui é consistência. Veja as melhores práticas e regras inegociáveis:\n\n- **Foco absoluto no desempenho** desde o primeiro commit.\n- **Feedback visual constante** usando padrões eficientes.\n- **Integração contínua** com ferramentas limpas.\n\n\n\n### Conclusão e Próximos Passos\n\nA simplicidade e a eficiência não são acidentais. São construídas linha por linha, com paciência e método. A constante iteração sobre as melhores práticas nos permite não apenas acompanhar o mercado, mas moldar a engenharia web para um ambiente mais escalável e agradável para todos nós.\n",
|
||||
"date": "2026-01-01",
|
||||
"readTime": "3 min",
|
||||
"tags": [
|
||||
"Design",
|
||||
"UI/UX",
|
||||
"Tendências"
|
||||
],
|
||||
"coverImage": "https://images.unsplash.com/photo-1523726495923-c81aff907fb7?auto=format&fit=crop&q=80&w=800"
|
||||
}
|
||||
];
|
||||
6
Template-02/src/lib/utils.ts
Normal file
6
Template-02/src/lib/utils.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import { clsx, type ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
10
Template-02/src/main.tsx
Normal file
10
Template-02/src/main.tsx
Normal 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>,
|
||||
);
|
||||
79
Template-02/src/pages/Blog.tsx
Normal file
79
Template-02/src/pages/Blog.tsx
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
import { motion } from "motion/react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { usePosts } from "../hooks/usePosts";
|
||||
|
||||
const transition = { type: "spring" as const, bounce: 0, duration: 0.8 };
|
||||
|
||||
export function Blog() {
|
||||
const { posts } = usePosts();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-16">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={transition}
|
||||
className="flex flex-col gap-4 text-center items-center"
|
||||
>
|
||||
<span className="text-xs font-mono uppercase tracking-widest text-stone-400 mb-2">Escritos & Notas</span>
|
||||
<h1 className="text-4xl md:text-5xl font-serif text-stone-900">
|
||||
O Diário de Engenharia
|
||||
</h1>
|
||||
<p className="text-stone-500 max-w-xl text-lg mt-2">
|
||||
Reflexões sobre design, arquitetura de software e as nuances invisíveis
|
||||
que compõem boas interfaces.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-10 lg:gap-14">
|
||||
{posts.map((post, i) => (
|
||||
<motion.div
|
||||
key={post.slug}
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ ...transition, delay: (i % 6) * 0.1 }}
|
||||
>
|
||||
<Link
|
||||
to={`/blog/${post.slug}`}
|
||||
className="group flex flex-col h-full gap-5 border-t border-stone-200 pt-6"
|
||||
>
|
||||
<div className="flex flex-col gap-3 flex-1">
|
||||
<div className="flex items-center gap-3">
|
||||
<time className="text-xs font-mono font-medium tracking-wide uppercase text-stone-400">
|
||||
{new Date(post.date).toLocaleDateString('pt-BR', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
</time>
|
||||
</div>
|
||||
|
||||
<h3 className="text-2xl font-serif leading-snug text-stone-900 group-hover:text-stone-600 transition-colors">
|
||||
{post.title}
|
||||
</h3>
|
||||
|
||||
<p className="text-stone-600 text-[15px] leading-relaxed max-w-xl">
|
||||
{post.excerpt}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{post.coverImage && (
|
||||
<div className="w-full aspect-video rounded-xl overflow-hidden mt-4 shrink-0 bg-stone-100 border border-stone-200/60">
|
||||
<img
|
||||
src={post.coverImage}
|
||||
alt={post.title}
|
||||
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-wrap gap-2 mt-4 pt-4 border-t border-stone-100">
|
||||
{post.tags.map(tag => (
|
||||
<span key={tag} className="text-xs font-mono text-stone-500 bg-stone-100 px-2 py-1 rounded">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</Link>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
208
Template-02/src/pages/Contact.tsx
Normal file
208
Template-02/src/pages/Contact.tsx
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
import { motion } from "motion/react";
|
||||
import { Mail, ArrowRight, Linkedin, Github } from "lucide-react";
|
||||
import { FormEvent, useState } from "react";
|
||||
|
||||
const transition = { type: "spring" as const, bounce: 0, duration: 0.8 };
|
||||
|
||||
function WhatsAppIcon({ className }: { className?: string }) {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className={className}
|
||||
>
|
||||
<path d="M3 21l1.65-3.8a9 9 0 1 1 3.4 2.9L3 21" />
|
||||
<path d="M9 10a.5.5 0 0 0 1 0V9a.5.5 0 0 0-1 0v1a5 5 0 0 0 5 5h1a.5.5 0 0 0 0-1h-1a.5.5 0 0 0 0 1" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function Contact() {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [isSuccess, setIsSuccess] = useState(false);
|
||||
|
||||
const handleSubmit = (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
setIsSubmitting(true);
|
||||
|
||||
// Simulating form submission
|
||||
setTimeout(() => {
|
||||
setIsSubmitting(false);
|
||||
setIsSuccess(true);
|
||||
|
||||
setTimeout(() => {
|
||||
setIsSuccess(false);
|
||||
(e.target as HTMLFormElement).reset();
|
||||
}, 5000);
|
||||
}, 1500);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-12 max-w-4xl mx-auto">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={transition}
|
||||
className="flex flex-col gap-4"
|
||||
>
|
||||
<span className="text-xs font-mono uppercase tracking-widest text-stone-400">Comunicações</span>
|
||||
<h1 className="text-4xl sm:text-6xl font-serif tracking-tight text-stone-900">
|
||||
Vamos conversar.
|
||||
</h1>
|
||||
<p className="text-stone-600 text-lg max-w-2xl leading-relaxed mt-2">
|
||||
Estou sempre aberta a discutir desenvolvimento de produtos, novas oportunidades ou apenas para trocar uma ideia sobre tech e design.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-[1fr_350px] gap-12 lg:gap-24">
|
||||
|
||||
{/* Contact Form */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ ...transition, delay: 0.1 }}
|
||||
>
|
||||
<form className="flex flex-col gap-6" onSubmit={handleSubmit}>
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="name" className="text-sm font-medium text-stone-700">
|
||||
Nome completo
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
required
|
||||
className="h-12 w-full bg-white border border-stone-300 rounded-lg px-4 text-stone-900 placeholder:text-stone-400 focus:outline-none focus:ring-2 focus:ring-stone-900 focus:border-transparent transition-all shadow-sm"
|
||||
placeholder="João da Silva"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="email" className="text-sm font-medium text-stone-700">
|
||||
E-mail profissional
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
required
|
||||
className="h-12 w-full bg-white border border-stone-300 rounded-lg px-4 text-stone-900 placeholder:text-stone-400 focus:outline-none focus:ring-2 focus:ring-stone-900 focus:border-transparent transition-all shadow-sm"
|
||||
placeholder="joao@empresa.com"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="message" className="text-sm font-medium text-stone-700">
|
||||
Mensagem
|
||||
</label>
|
||||
<textarea
|
||||
id="message"
|
||||
required
|
||||
rows={5}
|
||||
className="w-full bg-white border border-stone-300 rounded-lg p-4 text-stone-900 placeholder:text-stone-400 focus:outline-none focus:ring-2 focus:ring-stone-900 focus:border-transparent transition-all resize-none shadow-sm"
|
||||
placeholder="Como posso te ajudar?"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSubmitting || isSuccess}
|
||||
className="h-12 mt-2 w-full sm:w-auto sm:self-start px-8 bg-stone-900 text-[#FDFBF7] text-sm font-medium rounded-lg hover:bg-stone-800 transition-colors flex items-center justify-center gap-2 disabled:opacity-70 disabled:cursor-not-allowed shadow-sm"
|
||||
>
|
||||
{isSubmitting ? (
|
||||
<div className="w-4 h-4 border-2 border-[#FDFBF7] border-t-transparent rounded-full animate-spin" />
|
||||
) : isSuccess ? (
|
||||
"Mensagem enviada!"
|
||||
) : (
|
||||
<>
|
||||
Enviar mensagem
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{isSuccess && (
|
||||
<p className="text-sm text-green-700 font-medium">
|
||||
Sua mensagem foi enviada com sucesso. Retornarei o mais breve possível.
|
||||
</p>
|
||||
)}
|
||||
</form>
|
||||
</motion.div>
|
||||
|
||||
{/* Direct Contacts */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ ...transition, delay: 0.2 }}
|
||||
className="flex flex-col gap-8"
|
||||
>
|
||||
<div className="flex flex-col gap-6">
|
||||
<h2 className="text-lg font-serif text-stone-900 border-b border-stone-200 pb-2">Contato direto</h2>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<a
|
||||
href="mailto:contato@helenacosta.dev"
|
||||
className="flex items-center gap-4 transition-all group"
|
||||
>
|
||||
<div className="w-10 h-10 rounded-lg bg-stone-100 flex items-center justify-center text-stone-500 group-hover:text-stone-900 group-hover:bg-stone-200 transition-colors shrink-0">
|
||||
<Mail className="w-4 h-4" />
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium text-stone-900">E-mail</span>
|
||||
<span className="text-sm text-stone-500 font-mono group-hover:text-stone-700 transition-colors">contato@helenacosta.dev</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://wa.me/5511999999999"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-4 transition-all group"
|
||||
>
|
||||
<div className="w-10 h-10 rounded-lg bg-stone-100 flex items-center justify-center text-stone-500 group-hover:text-green-700 group-hover:bg-green-50 transition-colors shrink-0">
|
||||
<WhatsAppIcon className="w-4 h-4" />
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium text-stone-900">WhatsApp</span>
|
||||
<span className="text-sm text-stone-500 font-mono group-hover:text-stone-700 transition-colors">+55 11 99999-9999</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-6 mt-4">
|
||||
<h2 className="text-lg font-serif text-stone-900 border-b border-stone-200 pb-2">Redes</h2>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<a
|
||||
href="https://linkedin.com/in/helenacosta"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-4 group"
|
||||
>
|
||||
<Linkedin className="w-4 h-4 text-stone-400 group-hover:text-sky-700 transition-colors" />
|
||||
<span className="text-sm text-stone-500 group-hover:text-stone-900 transition-colors font-mono">linkedin/in/helenacosta</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://github.com/helenacosta"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-4 group"
|
||||
>
|
||||
<Github className="w-4 h-4 text-stone-400 group-hover:text-stone-900 transition-colors" />
|
||||
<span className="text-sm text-stone-500 group-hover:text-stone-900 transition-colors font-mono">github/helenacosta</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
169
Template-02/src/pages/Home.tsx
Normal file
169
Template-02/src/pages/Home.tsx
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
import { motion } from "motion/react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { ArrowRight, ArrowUpRight, Github, Linkedin, Mail } from "lucide-react";
|
||||
import { usePosts } from "../hooks/usePosts";
|
||||
|
||||
const transition = { type: "spring" as const, bounce: 0, duration: 0.9 };
|
||||
|
||||
export function Home() {
|
||||
const { posts } = usePosts();
|
||||
const recentPosts = posts.slice(0, 4);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-24 md:gap-32 w-full">
|
||||
{/* Hero Section */}
|
||||
<section className="flex flex-col md:flex-row items-center gap-12 md:gap-16 pt-8 md:pt-16">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95, filter: "blur(4px)" }}
|
||||
animate={{ opacity: 1, scale: 1, filter: "blur(0px)" }}
|
||||
transition={transition}
|
||||
className="w-48 h-48 sm:w-56 sm:h-56 md:w-72 md:h-72 shrink-0 relative group"
|
||||
>
|
||||
<div className="absolute inset-0 bg-stone-200 rounded-[2rem] rotate-3 group-hover:rotate-6 transition-transform duration-500 ease-out" />
|
||||
<div className="absolute inset-0 bg-stone-100 rounded-[2rem] -rotate-3 group-hover:-rotate-6 transition-transform duration-500 ease-out" />
|
||||
<img
|
||||
src="https://images.unsplash.com/photo-1544725176-7c40e5a71c5e?auto=format&fit=crop&q=80&w=600&h=600"
|
||||
alt="Helena Costa"
|
||||
className="w-full h-full object-cover rounded-[2rem] relative z-10 shadow-lg"
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
<div className="flex flex-col gap-6 flex-1 text-center md:text-left">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ ...transition, delay: 0.1 }}
|
||||
>
|
||||
<span className="text-xs font-mono tracking-widest uppercase text-stone-400 mb-4 block">
|
||||
Helena Costa — Front-end Engineer
|
||||
</span>
|
||||
<h1 className="text-4xl md:text-6xl lg:text-7xl font-serif font-medium tracking-tight text-stone-900 leading-[1.05]">
|
||||
Interfaces com <span className="italic text-stone-500">alma</span> & precisão.
|
||||
</h1>
|
||||
</motion.div>
|
||||
|
||||
<motion.p
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ ...transition, delay: 0.2 }}
|
||||
className="text-lg text-stone-600 max-w-xl leading-relaxed"
|
||||
>
|
||||
Sou uma Engenheira de Software focada em front-end com uma paixão inata por
|
||||
tipografia, animações fluidas e arquitetura escalável. Crio pontes entre
|
||||
design refinado e código robusto.
|
||||
</motion.p>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ ...transition, delay: 0.3 }}
|
||||
className="flex flex-wrap items-center justify-center md:justify-start gap-4 pt-2"
|
||||
>
|
||||
<Link
|
||||
to="/curriculo"
|
||||
className="h-12 px-6 inline-flex items-center gap-2 justify-center bg-stone-900 text-[#FDFBF7] text-sm font-medium rounded-xl hover:bg-stone-800 transition-all hover:-translate-y-0.5 shadow-md hover:shadow-xl"
|
||||
>
|
||||
Conheça minha trajetória
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</Link>
|
||||
|
||||
<div className="flex items-center gap-2 pl-2">
|
||||
<a href="#" className="w-10 h-10 rounded-full border border-stone-200 flex items-center justify-center text-stone-500 hover:text-stone-900 hover:bg-stone-100 transition-colors">
|
||||
<Github className="w-4 h-4" />
|
||||
</a>
|
||||
<a href="#" className="w-10 h-10 rounded-full border border-stone-200 flex items-center justify-center text-stone-500 hover:text-stone-900 hover:bg-stone-100 transition-colors">
|
||||
<Linkedin className="w-4 h-4" />
|
||||
</a>
|
||||
<a href="mailto:contato@helenacosta.dev" className="w-10 h-10 rounded-full border border-stone-200 flex items-center justify-center text-stone-500 hover:text-stone-900 hover:bg-stone-100 transition-colors">
|
||||
<Mail className="w-4 h-4" />
|
||||
</a>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Featured Articles */}
|
||||
<section className="flex flex-col gap-10 border-t border-stone-200 pt-16 relative">
|
||||
<div className="absolute top-0 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-[#FDFBF7] px-4">
|
||||
<span className="text-xl font-serif text-stone-400 italic">O Diário</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-3xl font-serif tracking-tight text-stone-900">Escritos Recentes</h2>
|
||||
<Link to="/blog" className="text-sm font-medium text-stone-500 hover:text-stone-900 transition-colors flex items-center gap-1 group">
|
||||
Ver tudo
|
||||
<ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-12">
|
||||
{recentPosts.map((post, i) => (
|
||||
<motion.div
|
||||
key={post.slug}
|
||||
initial={{ opacity: 0, y: 15 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-50px" }}
|
||||
transition={{ ...transition, delay: i * 0.1 }}
|
||||
>
|
||||
<Link to={`/blog/${post.slug}`} className="flex flex-col h-full gap-5 group">
|
||||
{post.coverImage && (
|
||||
<div className="w-full aspect-[16/9] rounded-2xl overflow-hidden bg-stone-100 relative shadow-sm border border-black/5">
|
||||
<img
|
||||
src={post.coverImage}
|
||||
alt={post.title}
|
||||
className="w-full h-full object-cover transition-transform duration-700 ease-out group-hover:scale-105"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-stone-900/0 group-hover:bg-stone-900/10 transition-colors duration-300" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<time className="text-xs font-mono font-medium tracking-wide uppercase text-stone-400">
|
||||
{new Date(post.date).toLocaleDateString('pt-BR', { year: 'numeric', month: 'short', day: 'numeric' })}
|
||||
</time>
|
||||
<span className="w-1 h-1 rounded-full bg-stone-300" />
|
||||
<span className="text-xs font-mono text-stone-400">{post.readTime}</span>
|
||||
</div>
|
||||
|
||||
<h3 className="text-2xl font-serif leading-snug text-stone-900 group-hover:text-stone-600 transition-colors flex items-start justify-between gap-4">
|
||||
<span>{post.title}</span>
|
||||
<ArrowUpRight className="w-5 h-5 shrink-0 opacity-0 -translate-y-1 translate-x-1 group-hover:opacity-100 group-hover:translate-y-0 group-hover:translate-x-0 transition-all text-stone-400" />
|
||||
</h3>
|
||||
|
||||
<p className="text-stone-600 line-clamp-2 leading-relaxed">
|
||||
{post.excerpt}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Mini About Section */}
|
||||
<section className="bg-stone-100 rounded-3xl p-8 md:p-12 border border-stone-200/50 mt-8 mb-8">
|
||||
<div className="flex flex-col md:flex-row gap-12 items-center">
|
||||
<div className="flex flex-col gap-4 flex-1">
|
||||
<h2 className="text-2xl font-serif text-stone-900">Por que o minimalismo?</h2>
|
||||
<p className="text-stone-600 leading-relaxed">
|
||||
Acredito que interfaces devem ser invisíveis. Quanto menos fricção visual,
|
||||
mais o conteúdo brilha. Especialista em React, Tailwind CSS e Framer Motion,
|
||||
busco sempre o equilíbrio perfeito entre estética e tecnologia de ponta.
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4 shrink-0 w-full md:w-auto">
|
||||
<div className="bg-white rounded-2xl p-6 flex flex-col gap-2 shadow-sm border border-stone-100">
|
||||
<span className="text-3xl font-serif text-stone-900">8+</span>
|
||||
<span className="text-sm font-mono text-stone-500 uppercase">Anos Exp.</span>
|
||||
</div>
|
||||
<div className="bg-white rounded-2xl p-6 flex flex-col gap-2 shadow-sm border border-stone-100">
|
||||
<span className="text-3xl font-serif text-stone-900">50+</span>
|
||||
<span className="text-sm font-mono text-stone-500 uppercase">Projetos</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
32
Template-02/src/pages/NotFound.tsx
Normal file
32
Template-02/src/pages/NotFound.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { motion } from "motion/react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
|
||||
export function NotFound() {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center min-h-[60vh] text-center gap-6">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ type: "spring", bounce: 0, duration: 0.8 }}
|
||||
className="flex flex-col items-center gap-4"
|
||||
>
|
||||
<span className="text-6xl font-mono font-bold text-stone-300">404</span>
|
||||
<h1 className="text-3xl font-serif tracking-tight text-stone-900">
|
||||
Página não encontrada
|
||||
</h1>
|
||||
<p className="text-stone-500 max-w-sm">
|
||||
A página que você está procurando não existe ou foi movida.
|
||||
</p>
|
||||
|
||||
<Link
|
||||
to="/"
|
||||
className="mt-6 h-11 px-6 inline-flex items-center gap-2 justify-center bg-stone-900 text-[#FDFBF7] text-sm font-medium rounded-full hover:bg-stone-800 transition-colors"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4" />
|
||||
Voltar para o início
|
||||
</Link>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
81
Template-02/src/pages/Post.tsx
Normal file
81
Template-02/src/pages/Post.tsx
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import { motion } from "motion/react";
|
||||
import { useParams, Link } from "react-router-dom";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import { usePosts } from "../hooks/usePosts";
|
||||
|
||||
export function Post() {
|
||||
const { slug } = useParams();
|
||||
const { posts } = usePosts();
|
||||
const post = posts.find((p) => p.slug === slug);
|
||||
|
||||
if (!post) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center min-h-[60vh] text-center gap-4">
|
||||
<h1 className="text-2xl font-serif text-stone-900">Artigo não encontrado</h1>
|
||||
<p className="text-stone-500">O artigo que você procurava não existe ou foi removido.</p>
|
||||
<Link to="/blog" className="text-stone-900 hover:text-stone-600 border-b border-stone-900 mt-4 transition-colors">
|
||||
Voltar para os artigos
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<article className="flex flex-col max-w-3xl mx-auto w-full">
|
||||
<Link
|
||||
to="/blog"
|
||||
className="inline-flex items-center gap-2 text-sm text-stone-500 hover:text-stone-900 transition-colors mb-12 w-fit group"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4 group-hover:-translate-x-0.5 transition-transform" />
|
||||
Voltar para artigos
|
||||
</Link>
|
||||
|
||||
<motion.header
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ type: "spring", bounce: 0, duration: 0.8 }}
|
||||
className="flex flex-col gap-6 mb-12"
|
||||
>
|
||||
<div className="flex flex-wrap items-center gap-4 text-sm font-mono text-stone-500">
|
||||
<time>
|
||||
{new Date(post.date).toLocaleDateString('pt-BR', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
</time>
|
||||
<span className="w-1 h-1 rounded-full bg-stone-300" />
|
||||
<span>{post.readTime}</span>
|
||||
</div>
|
||||
|
||||
<h1 className="text-4xl sm:text-5xl md:text-6xl font-serif tracking-tight text-stone-900 leading-[1.15]">
|
||||
{post.title}
|
||||
</h1>
|
||||
|
||||
{post.coverImage && (
|
||||
<div className="w-full aspect-video rounded-2xl overflow-hidden my-6 border border-stone-200/60 shadow-sm bg-stone-100">
|
||||
<img
|
||||
src={post.coverImage}
|
||||
alt={post.title}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-wrap gap-2 pt-4">
|
||||
{post.tags.map(tag => (
|
||||
<span key={tag} className="px-3 py-1 bg-stone-100 text-stone-600 text-xs rounded-full font-mono">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</motion.header>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ type: "spring", bounce: 0, duration: 0.8, delay: 0.1 }}
|
||||
className="prose prose-stone prose-lg max-w-none prose-headings:font-serif prose-headings:font-medium prose-p:leading-relaxed prose-p:text-stone-600 prose-headings:text-stone-900 prose-pre:bg-stone-50 prose-pre:border prose-pre:border-stone-200 prose-pre:text-stone-800"
|
||||
>
|
||||
<ReactMarkdown>{post.content}</ReactMarkdown>
|
||||
</motion.div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
211
Template-02/src/pages/Resume.tsx
Normal file
211
Template-02/src/pages/Resume.tsx
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
import { motion } from "motion/react";
|
||||
import { Download, ExternalLink, Mail, Phone, MapPin, Globe } from "lucide-react";
|
||||
|
||||
const transition = { type: "spring" as const, bounce: 0, duration: 0.8 };
|
||||
|
||||
const EXPERIENCES = [
|
||||
{
|
||||
role: "Senior Front-end Engineer",
|
||||
company: "Tech corp",
|
||||
period: "2021 — Presente",
|
||||
description: "Liderando a arquitetura front-end de produtos B2B utilizando React e TypeScript. Implementei um Design System do zero que reduziu o tempo de desenvolvimento em 40%. Otimizei a performance focando em Core Web Vitals, melhorando o LCP em 2.5s nas páginas principais.",
|
||||
technologies: ["React", "TypeScript", "Tailwind CSS", "Next.js", "Framer Motion"]
|
||||
},
|
||||
{
|
||||
role: "UI/UX Designer & Developer",
|
||||
company: "Agência Criativa",
|
||||
period: "2018 — 2021",
|
||||
description: "Design e desenvolvimento de interfaces web para grandes e-commerces (clientes com MAU > 1M). Foco em conversão (CRO), testes A/B, e acessibilidade (WCAG 2.1 AA).",
|
||||
technologies: ["Figma", "Vue.js", "SASS", "Node.js", "Cypress"]
|
||||
},
|
||||
{
|
||||
role: "Web Developer",
|
||||
company: "Startup Lab",
|
||||
period: "2016 — 2018",
|
||||
description: "Desenvolvimento de landing pages e dashboards internos para monitoramento de KPIs. Migração paulatina de jQuery para React.",
|
||||
technologies: ["JavaScript", "HTML/CSS", "PHP", "MySQL"]
|
||||
}
|
||||
];
|
||||
|
||||
const SKILLS = [
|
||||
{ category: "Frameworks & Libs", items: "React, Next.js, Vue, Tailwind, Framer Motion" },
|
||||
{ category: "Linguagens", items: "TypeScript, JavaScript, HTML5, CSS3, Node.js" },
|
||||
{ category: "Ferramentas", items: "Git, Vite, Webpack, Figma, Jest, Cypress" },
|
||||
{ category: "Design", items: "UI/UX, Prototipagem, Design Systems, Acessibilidade" }
|
||||
];
|
||||
|
||||
export function Resume() {
|
||||
const handlePrint = () => {
|
||||
window.print();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-12 w-full max-w-5xl mx-auto">
|
||||
{/* Header / Actions (Hidden in Print) */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={transition}
|
||||
className="flex flex-col sm:flex-row sm:items-end justify-between gap-6 pb-8 border-b border-stone-200/60 print:hidden"
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
<span className="text-xs font-mono tracking-widest text-stone-400 uppercase">Trajetória</span>
|
||||
<h1 className="text-4xl md:text-6xl font-serif tracking-tight text-stone-900">Currículo</h1>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={handlePrint}
|
||||
className="h-12 px-6 inline-flex items-center gap-2 justify-center bg-stone-900 w-fit text-[#FDFBF7] text-sm font-medium rounded-2xl hover:bg-stone-800 transition-all hover:-translate-y-0.5 shadow-md hover:shadow-xl shrink-0"
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
Salvar em PDF
|
||||
</button>
|
||||
</motion.div>
|
||||
|
||||
{/* The Resume Document */}
|
||||
<div className="bg-white md:p-12 md:rounded-[2.5rem] md:shadow-sm md:border border-stone-200/50 print:p-0 print:border-none print:shadow-none print:bg-transparent">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, filter: "blur(4px)" }}
|
||||
animate={{ opacity: 1, filter: "blur(0px)" }}
|
||||
transition={{ ...transition, delay: 0.1 }}
|
||||
className="flex flex-col gap-16 print:gap-10"
|
||||
>
|
||||
{/* Resume Header / Profile */}
|
||||
<header className="flex flex-col md:flex-row items-start md:items-center justify-between gap-8 pb-12 border-b border-stone-200/60 print:pb-6 print:gap-4">
|
||||
<div className="flex flex-col gap-3">
|
||||
<h1 className="text-4xl md:text-5xl font-serif tracking-tight text-stone-900 print:text-4xl">Helena Costa</h1>
|
||||
<span className="text-lg text-stone-500 font-medium">Front-end Engineer & Designer</span>
|
||||
<p className="mt-2 text-stone-600 max-w-lg leading-relaxed print:text-sm">
|
||||
Especialista em construir produtos digitais modernos, unindo a precisão da engenharia com o refinamento do design de interface. Minha missão é traduzir complexidade em experiências simples e memoráveis.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-3 text-sm text-stone-600 font-mono tracking-tight print:text-xs">
|
||||
<a href="mailto:contato@helenacosta.dev" className="flex items-center gap-3 hover:text-stone-900 transition-colors">
|
||||
<Mail className="w-4 h-4 shrink-0 text-stone-400" />
|
||||
contato@helenacosta.dev
|
||||
</a>
|
||||
<div className="flex items-center gap-3">
|
||||
<Phone className="w-4 h-4 shrink-0 text-stone-400" />
|
||||
+55 (11) 99999-9999
|
||||
</div>
|
||||
<a href="https://helenacosta.dev" target="_blank" rel="noreferrer" className="flex items-center gap-3 hover:text-stone-900 transition-colors">
|
||||
<Globe className="w-4 h-4 shrink-0 text-stone-400" />
|
||||
helenacosta.dev
|
||||
</a>
|
||||
<div className="flex items-center gap-3">
|
||||
<MapPin className="w-4 h-4 shrink-0 text-stone-400" />
|
||||
São Paulo, SP
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12 lg:gap-16 print:grid-cols-1 print:gap-8">
|
||||
|
||||
{/* Left Column (Main Content) */}
|
||||
<div className="col-span-1 lg:col-span-8 flex flex-col gap-12 print:gap-8">
|
||||
|
||||
{/* Experience Section */}
|
||||
<section className="flex flex-col gap-8">
|
||||
<h2 className="text-2xl font-serif text-stone-900 flex items-center gap-4 print:text-xl">
|
||||
<span className="w-8 h-px bg-stone-300"></span>
|
||||
Experiência
|
||||
</h2>
|
||||
|
||||
<div className="flex flex-col gap-10 print:gap-6">
|
||||
{EXPERIENCES.map((exp, i) => (
|
||||
<div key={i} className="flex flex-col gap-2">
|
||||
<div className="flex flex-col sm:flex-row sm:items-baseline justify-between gap-1 print:flex-row print:justify-between">
|
||||
<h3 className="text-lg font-medium text-stone-900 print:text-base">{exp.role}</h3>
|
||||
<span className="text-sm font-mono text-stone-500 uppercase tracking-wider print:text-xs">{exp.period}</span>
|
||||
</div>
|
||||
<span className="text-stone-500 italic font-serif print:text-sm">{exp.company}</span>
|
||||
|
||||
<p className="text-stone-600 leading-relaxed mt-2 text-[15px] print:text-xs">
|
||||
{exp.description}
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap gap-2 mt-4 print:mt-2">
|
||||
{exp.technologies.map(tech => (
|
||||
<span key={tech} className="px-2.5 py-1 bg-stone-50 text-stone-600 text-[11px] rounded uppercase tracking-wider font-mono border border-stone-200/60 print:border-stone-300 print:bg-transparent">
|
||||
{tech}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Education Section */}
|
||||
<section className="flex flex-col gap-8">
|
||||
<h2 className="text-2xl font-serif text-stone-900 flex items-center gap-4 print:text-xl">
|
||||
<span className="w-8 h-px bg-stone-300"></span>
|
||||
Educação
|
||||
</h2>
|
||||
|
||||
<div className="flex flex-col gap-8 print:gap-4">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex flex-col sm:flex-row sm:items-baseline justify-between gap-1 print:flex-row print:justify-between">
|
||||
<h3 className="text-lg font-medium text-stone-900 print:text-base">Bacharelado em Ciência da Computação</h3>
|
||||
<span className="text-sm font-mono text-stone-500 uppercase tracking-wider print:text-xs">2014 — 2018</span>
|
||||
</div>
|
||||
<span className="text-stone-500 italic font-serif print:text-sm">Universidade de São Paulo (USP)</span>
|
||||
<p className="text-stone-600 text-[15px] mt-1 print:text-xs">
|
||||
Bolsista em Iniciação Científica no Laboratório de Interação Humano-Computador (IHC). TCC sobre Acessibilidade em Single Page Applications.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Right Column (Sidebar) */}
|
||||
<div className="col-span-1 lg:col-span-4 flex flex-col gap-12 print:gap-6 print:flex-row print:flex-wrap">
|
||||
|
||||
{/* Skills Section */}
|
||||
<section className="flex flex-col gap-8 print:w-full">
|
||||
<h2 className="text-2xl font-serif text-stone-900 flex items-center gap-4 print:text-xl">
|
||||
<span className="w-8 h-px bg-stone-300 lg:hidden"></span>
|
||||
Especialidades
|
||||
</h2>
|
||||
|
||||
<div className="flex flex-col gap-6 print:grid print:grid-cols-2 print:gap-4 border-l-2 border-stone-100 pl-4 lg:pl-6 print:border-none print:pl-0">
|
||||
{SKILLS.map((skillGroup, i) => (
|
||||
<div key={i} className="flex flex-col gap-1">
|
||||
<span className="text-sm font-medium text-stone-900 print:text-sm">{skillGroup.category}</span>
|
||||
<span className="text-[13px] text-stone-500 leading-relaxed print:text-xs">{skillGroup.items}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Notable Projects Section */}
|
||||
<section className="flex flex-col gap-8 print:hidden">
|
||||
<h2 className="text-2xl font-serif text-stone-900">Destaques</h2>
|
||||
|
||||
<div className="flex flex-col gap-6 border-l-2 border-stone-100 pl-4 lg:pl-6">
|
||||
<div className="flex flex-col gap-1 group">
|
||||
<a href="#" className="text-sm font-medium text-stone-900 hover:text-stone-600 transition-colors flex items-center gap-1">
|
||||
Design System Aurora <ExternalLink className="w-3 h-3 opacity-0 group-hover:opacity-100 -translate-x-1 group-hover:translate-x-0 transition-all" />
|
||||
</a>
|
||||
<span className="text-[13px] text-stone-500 leading-relaxed">Biblioteca open-source de componentes React acessíveis, 2k+ stars no GitHub.</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1 group">
|
||||
<a href="#" className="text-sm font-medium text-stone-900 hover:text-stone-600 transition-colors flex items-center gap-1">
|
||||
Banco Digital Redesign <ExternalLink className="w-3 h-3 opacity-0 group-hover:opacity-100 -translate-x-1 group-hover:translate-x-0 transition-all" />
|
||||
</a>
|
||||
<span className="text-[13px] text-stone-500 leading-relaxed">Liderei a reescrita do app web, aumentando o índice de retenção em 15%.</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
26
Template-02/tsconfig.json
Normal file
26
Template-02/tsconfig.json
Normal 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
|
||||
}
|
||||
}
|
||||
24
Template-02/vite.config.ts
Normal file
24
Template-02/vite.config.ts
Normal 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',
|
||||
},
|
||||
};
|
||||
});
|
||||
Loading…
Reference in a new issue