Compare commits
16 Commits
93ffacb124
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d6fef8775f | |||
| 5db9856f9a | |||
| c3d748aa29 | |||
| 6376fc4a1c | |||
| 4c496eff23 | |||
| e70b13a7f4 | |||
| 18ae0deae1 | |||
| 842c10ac58 | |||
| 77d30b30f3 | |||
| ac5d571d28 | |||
| 521c4b02ad | |||
| ffa92bbee1 | |||
| fbe23f9536 | |||
| ab116e4824 | |||
| 50059bb3a5 | |||
| 0e92466fd6 |
@@ -0,0 +1,28 @@
|
|||||||
|
name: Deploy Portfolio
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Build Astro
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Publish Site
|
||||||
|
run: |
|
||||||
|
docker exec gitea-runner sh -c 'rm -rf /sites/portfolio/*'
|
||||||
|
docker cp dist/. gitea-runner:/sites/portfolio/
|
||||||
|
|
||||||
|
- name: Verify Deployment
|
||||||
|
run: |
|
||||||
|
docker exec gitea-runner ls -la /sites/portfolio
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
.astro/
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# Astro Starter Kit: Minimal
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm create astro@latest -- --template minimal
|
||||||
|
```
|
||||||
|
|
||||||
|
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
|
||||||
|
|
||||||
|
## 🚀 Project Structure
|
||||||
|
|
||||||
|
Inside of your Astro project, you'll see the following folders and files:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/
|
||||||
|
├── public/
|
||||||
|
├── src/
|
||||||
|
│ └── pages/
|
||||||
|
│ └── index.astro
|
||||||
|
└── package.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
|
||||||
|
|
||||||
|
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
|
||||||
|
|
||||||
|
Any static assets, like images, can be placed in the `public/` directory.
|
||||||
|
|
||||||
|
## 🧞 Commands
|
||||||
|
|
||||||
|
All commands are run from the root of the project, from a terminal:
|
||||||
|
|
||||||
|
| Command | Action |
|
||||||
|
| :------------------------ | :----------------------------------------------- |
|
||||||
|
| `npm install` | Installs dependencies |
|
||||||
|
| `npm run dev` | Starts local dev server at `localhost:4321` |
|
||||||
|
| `npm run build` | Build your production site to `./dist/` |
|
||||||
|
| `npm run preview` | Preview your build locally, before deploying |
|
||||||
|
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
|
||||||
|
| `npm run astro -- --help` | Get help using the Astro CLI |
|
||||||
|
|
||||||
|
## 👀 Want to learn more?
|
||||||
|
|
||||||
|
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// @ts-check
|
||||||
|
import { defineConfig } from 'astro/config';
|
||||||
|
|
||||||
|
// https://astro.build/config
|
||||||
|
export default defineConfig({
|
||||||
|
i18n: {
|
||||||
|
locales: ["pt-br", "en", "es"],
|
||||||
|
defaultLocale: "pt-br",
|
||||||
|
routing: {
|
||||||
|
prefixDefaultLocale: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
max-width: 900px;
|
|
||||||
margin: auto;
|
|
||||||
padding: 20px;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
border-bottom: 1px solid #ddd;
|
|
||||||
padding-bottom: 5px;
|
|
||||||
}
|
|
||||||
-74
@@ -1,74 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
|
|
||||||
<html lang="pt-BR">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Clezio Penha</title>
|
|
||||||
<link rel="stylesheet" href="css/style.css">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<header>
|
|
||||||
<h1>Clezio Penha</h1>
|
|
||||||
<p>Backend Developer | C++ | Java | Cloud</p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<h2>Sobre</h2>
|
|
||||||
|
|
||||||
```
|
|
||||||
<p>
|
|
||||||
Desenvolvedor backend com experiência em C++, Qt e Java.
|
|
||||||
Atuo com desenvolvimento de aplicações, APIs REST,
|
|
||||||
Docker, Linux e Cloud.
|
|
||||||
</p>
|
|
||||||
```
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<h2>Tecnologias</h2>
|
|
||||||
|
|
||||||
```
|
|
||||||
<ul>
|
|
||||||
<li>C++</li>
|
|
||||||
<li>Qt</li>
|
|
||||||
<li>Java</li>
|
|
||||||
<li>Spring Boot</li>
|
|
||||||
<li>Quarkus</li>
|
|
||||||
<li>Docker</li>
|
|
||||||
<li>Linux</li>
|
|
||||||
<li>Oracle Cloud</li>
|
|
||||||
</ul>
|
|
||||||
```
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<h2>Projetos</h2>
|
|
||||||
|
|
||||||
```
|
|
||||||
<ul>
|
|
||||||
<li>Infraestrutura Oracle Cloud</li>
|
|
||||||
<li>Servidor Git Privado com Gitea</li>
|
|
||||||
<li>APIs Java com Spring Boot</li>
|
|
||||||
</ul>
|
|
||||||
```
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<h2>Contato</h2>
|
|
||||||
|
|
||||||
```
|
|
||||||
<p>Email: clezio.penha@gmail.com</p>
|
|
||||||
<p>Git: https://git.clezio.com.br</p>
|
|
||||||
```
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
Generated
+4849
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "portfolio",
|
||||||
|
"type": "module",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22.12.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "astro dev",
|
||||||
|
"build": "astro build",
|
||||||
|
"preview": "astro preview",
|
||||||
|
"astro": "astro"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"astro": "^6.4.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 655 B |
@@ -0,0 +1,9 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
|
||||||
|
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
|
||||||
|
<style>
|
||||||
|
path { fill: #000; }
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
path { fill: #FFF; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 749 B |
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
import type { Translation } from "../data/translations";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
t: Translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { t } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<section id="about" class="section">
|
||||||
|
<h2>{t.about.title}</h2>
|
||||||
|
<p>{t.about.description}</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="section">
|
||||||
|
<h2>{t.about.workTitle}</h2>
|
||||||
|
|
||||||
|
<div class="cards-grid">
|
||||||
|
{
|
||||||
|
t.workPrinciples.map(work => (
|
||||||
|
<article class="card">
|
||||||
|
<h3>{work.title}</h3>
|
||||||
|
<p>{work.description}</p>
|
||||||
|
</article>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
import type { Translation } from "../data/translations";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
t: Translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { t } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<section id="contact" class="section">
|
||||||
|
|
||||||
|
<h2>{t.contact.title}</h2>
|
||||||
|
|
||||||
|
<p>{t.contact.description}</p>
|
||||||
|
|
||||||
|
<div class="contact-links">
|
||||||
|
|
||||||
|
<a href={`mailto:${t.profile.email}`}>
|
||||||
|
{t.profile.email}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href={t.profile.linkedin} target="_blank" rel="noopener noreferrer">
|
||||||
|
{t.contact.linkedin}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
import type { Translation } from "../data/translations";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
t: Translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { t } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<section id="experience" class="section">
|
||||||
|
|
||||||
|
<h2>{t.sections.experience}</h2>
|
||||||
|
|
||||||
|
<div class="timeline">
|
||||||
|
|
||||||
|
{
|
||||||
|
t.timeline.map(item => (
|
||||||
|
<article class="timeline-item">
|
||||||
|
<h3>{item.title}</h3>
|
||||||
|
<p>{item.description}</p>
|
||||||
|
</article>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
import type { Translation } from "../data/translations";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
t: Translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { t } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
|
||||||
|
<p>
|
||||||
|
© 2026 {t.profile.name}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{t.profile.footerRole}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<button id="backToTop" aria-label={t.ui.backToTop}>
|
||||||
|
↑
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const backToTopButton = document.querySelector("#backToTop");
|
||||||
|
|
||||||
|
const toggleBackToTop = () => {
|
||||||
|
backToTopButton?.classList.toggle("show", window.scrollY > 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("scroll", toggleBackToTop, { passive: true });
|
||||||
|
toggleBackToTop();
|
||||||
|
|
||||||
|
backToTopButton?.addEventListener("click", () => {
|
||||||
|
window.scrollTo({
|
||||||
|
top: 0,
|
||||||
|
behavior: "smooth"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
---
|
||||||
|
import { languageLinks, type Locale, type Translation } from "../data/translations";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
locale: Locale;
|
||||||
|
t: Translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { locale, t } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<header class="header">
|
||||||
|
<nav class="navbar">
|
||||||
|
|
||||||
|
<div class="logo">
|
||||||
|
{t.profile.name}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul id="primary-navigation" class="nav-links">
|
||||||
|
<li><a href="#home">{t.nav.home}</a></li>
|
||||||
|
<li><a href="#about">{t.nav.about}</a></li>
|
||||||
|
<li><a href="#specialties">{t.nav.specialties}</a></li>
|
||||||
|
<li><a href="#experience">{t.nav.experience}</a></li>
|
||||||
|
<li><a href="#projects">{t.nav.projects}</a></li>
|
||||||
|
<li><a href="#contact">{t.nav.contact}</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="menu-toggle"
|
||||||
|
type="button"
|
||||||
|
aria-label={t.ui.menuToggle}
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-controls="primary-navigation"
|
||||||
|
>
|
||||||
|
☰
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
|
||||||
|
<button
|
||||||
|
id="theme-toggle"
|
||||||
|
type="button"
|
||||||
|
aria-label={t.ui.themeToggle}
|
||||||
|
aria-pressed="false"
|
||||||
|
>◐</button>
|
||||||
|
|
||||||
|
<div class="language-switcher" aria-label={t.ui.languageSwitcher}>
|
||||||
|
{
|
||||||
|
languageLinks.map(language => (
|
||||||
|
<a
|
||||||
|
href={language.href}
|
||||||
|
aria-label={language.label}
|
||||||
|
aria-current={language.locale === locale ? "true" : undefined}
|
||||||
|
class:list={{ active: language.locale === locale }}
|
||||||
|
>
|
||||||
|
{language.flag}
|
||||||
|
</a>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const themeToggle = document.querySelector("#theme-toggle");
|
||||||
|
const menuToggle = document.querySelector(".menu-toggle");
|
||||||
|
const navLinks = document.querySelector(".nav-links");
|
||||||
|
const navAnchors = document.querySelectorAll(".nav-links a");
|
||||||
|
const themeStorageKey = "portfolio-theme";
|
||||||
|
const themeIcons = {
|
||||||
|
light: "☀️",
|
||||||
|
dark: "🌙"
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPreferredTheme = () => {
|
||||||
|
const storedTheme = localStorage.getItem(themeStorageKey);
|
||||||
|
|
||||||
|
if (storedTheme === "light" || storedTheme === "dark") {
|
||||||
|
return storedTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
return window.matchMedia("(prefers-color-scheme: light)").matches
|
||||||
|
? "light"
|
||||||
|
: "dark";
|
||||||
|
};
|
||||||
|
|
||||||
|
const applyTheme = (theme) => {
|
||||||
|
document.documentElement.dataset.theme = theme;
|
||||||
|
|
||||||
|
if (themeToggle instanceof HTMLButtonElement) {
|
||||||
|
const isLight = theme === "light";
|
||||||
|
|
||||||
|
themeToggle.textContent = isLight ? themeIcons.dark : themeIcons.light;
|
||||||
|
themeToggle.setAttribute("aria-pressed", String(isLight));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
applyTheme(getPreferredTheme());
|
||||||
|
|
||||||
|
themeToggle?.addEventListener("click", () => {
|
||||||
|
const currentTheme = document.documentElement.dataset.theme === "light"
|
||||||
|
? "light"
|
||||||
|
: "dark";
|
||||||
|
const nextTheme = currentTheme === "light" ? "dark" : "light";
|
||||||
|
|
||||||
|
localStorage.setItem(themeStorageKey, nextTheme);
|
||||||
|
applyTheme(nextTheme);
|
||||||
|
});
|
||||||
|
|
||||||
|
const closeMenu = () => {
|
||||||
|
navLinks?.classList.remove("open");
|
||||||
|
|
||||||
|
if (menuToggle instanceof HTMLButtonElement) {
|
||||||
|
menuToggle.setAttribute("aria-expanded", "false");
|
||||||
|
menuToggle.textContent = "☰";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
menuToggle?.addEventListener("click", () => {
|
||||||
|
const isOpen = navLinks?.classList.toggle("open") ?? false;
|
||||||
|
|
||||||
|
if (menuToggle instanceof HTMLButtonElement) {
|
||||||
|
menuToggle.setAttribute("aria-expanded", String(isOpen));
|
||||||
|
menuToggle.textContent = isOpen ? "×" : "☰";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
navAnchors.forEach((anchor) => {
|
||||||
|
anchor.addEventListener("click", closeMenu);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("keydown", (event) => {
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
closeMenu();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const sections = Array.from(navAnchors)
|
||||||
|
.map((anchor) => document.querySelector(anchor.getAttribute("href") ?? ""))
|
||||||
|
.filter((section) => section instanceof HTMLElement);
|
||||||
|
|
||||||
|
const setActiveLink = (id) => {
|
||||||
|
navAnchors.forEach((anchor) => {
|
||||||
|
anchor.classList.toggle("active", anchor.getAttribute("href") === `#${id}`);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const sectionObserver = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
const visibleEntry = entries.find((entry) => entry.isIntersecting);
|
||||||
|
|
||||||
|
if (visibleEntry?.target instanceof HTMLElement) {
|
||||||
|
setActiveLink(visibleEntry.target.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rootMargin: "-45% 0px -50% 0px",
|
||||||
|
threshold: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
sections.forEach((section) => sectionObserver.observe(section));
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
import type { Translation } from "../data/translations";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
t: Translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { t } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<section id="home" class="hero">
|
||||||
|
|
||||||
|
<div class="hero-content">
|
||||||
|
|
||||||
|
<span class="hero-badge">{t.hero.badge}</span>
|
||||||
|
|
||||||
|
<h1>{t.hero.title}</h1>
|
||||||
|
|
||||||
|
<p>{t.hero.description}</p>
|
||||||
|
|
||||||
|
<div class="hero-actions">
|
||||||
|
<a href="#projects" class="btn-primary">
|
||||||
|
{t.hero.primaryButton}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="#contact" class="btn-secondary">
|
||||||
|
{t.hero.secondaryButton}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
import type { Translation } from "../data/translations";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
t: Translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { t } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<section class="metrics">
|
||||||
|
|
||||||
|
{
|
||||||
|
t.metrics.map(metric => (
|
||||||
|
<article class="metric-card">
|
||||||
|
<h3>{metric.value}</h3>
|
||||||
|
<p>{metric.label}</p>
|
||||||
|
</article>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
import MainLayout from "../layouts/MainLayout.astro";
|
||||||
|
|
||||||
|
import Header from "./Header.astro";
|
||||||
|
import Hero from "./Hero.astro";
|
||||||
|
import Metrics from "./Metrics.astro";
|
||||||
|
import About from "./About.astro";
|
||||||
|
import Specialties from "./Specialties.astro";
|
||||||
|
import Experience from "./Experience.astro";
|
||||||
|
import Projects from "./Projects.astro";
|
||||||
|
import Contact from "./Contact.astro";
|
||||||
|
import Footer from "./Footer.astro";
|
||||||
|
|
||||||
|
import { translations, type Locale } from "../data/translations";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
locale: Locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { locale } = Astro.props;
|
||||||
|
const t = translations[locale];
|
||||||
|
---
|
||||||
|
|
||||||
|
<MainLayout
|
||||||
|
title={t.meta.title}
|
||||||
|
description={t.meta.description}
|
||||||
|
lang={t.lang}
|
||||||
|
canonical={t.canonical}
|
||||||
|
ogTitle={t.meta.ogTitle}
|
||||||
|
ogDescription={t.meta.ogDescription}
|
||||||
|
>
|
||||||
|
|
||||||
|
<Header locale={locale} t={t} />
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<Hero t={t} />
|
||||||
|
<Metrics t={t} />
|
||||||
|
<About t={t} />
|
||||||
|
<Specialties t={t} />
|
||||||
|
<Experience t={t} />
|
||||||
|
<Projects t={t} />
|
||||||
|
<Contact t={t} />
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<Footer t={t} />
|
||||||
|
|
||||||
|
</MainLayout>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
import type { Translation } from "../data/translations";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
t: Translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { t } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<section id="projects" class="section">
|
||||||
|
|
||||||
|
<h2>{t.sections.projects}</h2>
|
||||||
|
|
||||||
|
<div class="cards-grid">
|
||||||
|
|
||||||
|
{
|
||||||
|
t.projects.map(project => (
|
||||||
|
<article class="project-card">
|
||||||
|
<h3>{project.title}</h3>
|
||||||
|
|
||||||
|
<p>{project.description}</p>
|
||||||
|
</article>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
import type { Translation } from "../data/translations";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
t: Translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { t } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<section id="specialties" class="section">
|
||||||
|
|
||||||
|
<h2>{t.sections.specialties}</h2>
|
||||||
|
|
||||||
|
<div class="cards-grid">
|
||||||
|
|
||||||
|
{
|
||||||
|
t.specialties.map(specialty => (
|
||||||
|
<article class="card">
|
||||||
|
<h3>{specialty}</h3>
|
||||||
|
</article>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="section">
|
||||||
|
|
||||||
|
<h2>{t.sections.stack}</h2>
|
||||||
|
|
||||||
|
<div class="tech-stack">
|
||||||
|
|
||||||
|
{
|
||||||
|
t.stack.map(technology => (
|
||||||
|
<span>{technology}</span>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,488 @@
|
|||||||
|
export const defaultLocale = "pt-br";
|
||||||
|
|
||||||
|
export const locales = ["pt-br", "en", "es"] as const;
|
||||||
|
|
||||||
|
export type Locale = (typeof locales)[number];
|
||||||
|
|
||||||
|
export const languageLinks = [
|
||||||
|
{
|
||||||
|
locale: "pt-br",
|
||||||
|
href: "/",
|
||||||
|
label: "Português",
|
||||||
|
flag: "🇧🇷"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
locale: "en",
|
||||||
|
href: "/en/",
|
||||||
|
label: "English",
|
||||||
|
flag: "🇺🇸"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
locale: "es",
|
||||||
|
href: "/es/",
|
||||||
|
label: "Español",
|
||||||
|
flag: "🇪🇸"
|
||||||
|
}
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export const translations = {
|
||||||
|
"pt-br": {
|
||||||
|
lang: "pt-BR",
|
||||||
|
canonical: "https://clezio.com.br/",
|
||||||
|
meta: {
|
||||||
|
title: "Clezio Penha | Senior Backend Engineer",
|
||||||
|
description:
|
||||||
|
"Programador Sênior Backend com mais de 15 anos de experiência em Java, C++, APIs REST, Cloud Computing, DevOps e Arquitetura de Software.",
|
||||||
|
ogTitle: "Clezio Penha | Programador Sênior Backend",
|
||||||
|
ogDescription: "Mais de 15 anos construindo sistemas robustos, escaláveis e seguros."
|
||||||
|
},
|
||||||
|
profile: {
|
||||||
|
name: "Clezio Penha",
|
||||||
|
role: "Senior Backend Engineer",
|
||||||
|
footerRole: "Senior Backend Engineer",
|
||||||
|
email: "clezio.penha@gmail.com",
|
||||||
|
linkedin: "https://www.linkedin.com/in/clezio"
|
||||||
|
},
|
||||||
|
nav: {
|
||||||
|
home: "Início",
|
||||||
|
about: "Sobre",
|
||||||
|
specialties: "Especialidades",
|
||||||
|
experience: "Experiência",
|
||||||
|
projects: "Projetos",
|
||||||
|
contact: "Contato"
|
||||||
|
},
|
||||||
|
hero: {
|
||||||
|
badge: "Programador Sênior • Backend Engineer • Tech Lead",
|
||||||
|
title: "Transformando desafios de negócio em soluções robustas, escaláveis e seguras.",
|
||||||
|
description:
|
||||||
|
"Programador Sênior com mais de 15 anos de experiência em desenvolvimento backend, arquitetura de software, cloud computing e liderança técnica.",
|
||||||
|
primaryButton: "Ver Projetos",
|
||||||
|
secondaryButton: "Entrar em Contato"
|
||||||
|
},
|
||||||
|
metrics: [
|
||||||
|
{
|
||||||
|
value: "15+",
|
||||||
|
label: "Anos de Experiência"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Tech Lead",
|
||||||
|
label: "Liderança Técnica"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Cloud",
|
||||||
|
label: "Oracle Cloud & Azure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Backend",
|
||||||
|
label: "APIs e Sistemas Corporativos"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
about: {
|
||||||
|
title: "Sobre Mim",
|
||||||
|
description:
|
||||||
|
"Ao longo da minha carreira participei da construção e evolução de sistemas corporativos, integrações críticas, automações de processos e plataformas backend voltadas para desempenho, escalabilidade e confiabilidade.",
|
||||||
|
workTitle: "Como Eu Trabalho"
|
||||||
|
},
|
||||||
|
workPrinciples: [
|
||||||
|
{
|
||||||
|
title: "Arquitetura",
|
||||||
|
description: "Planejamento técnico antes da implementação."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Escalabilidade",
|
||||||
|
description: "Sistemas preparados para crescimento contínuo."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Performance",
|
||||||
|
description: "Eficiência operacional e otimização de recursos."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Segurança",
|
||||||
|
description: "Boas práticas desde a concepção."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Qualidade",
|
||||||
|
description: "Código limpo e manutenção simplificada."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
sections: {
|
||||||
|
specialties: "Especialidades",
|
||||||
|
stack: "Stack Tecnológica",
|
||||||
|
experience: "Experiência Profissional",
|
||||||
|
projects: "Projetos em Destaque"
|
||||||
|
},
|
||||||
|
specialties: [
|
||||||
|
"APIs REST",
|
||||||
|
"Arquitetura de Software",
|
||||||
|
"Cloud Computing",
|
||||||
|
"DevOps",
|
||||||
|
"Bancos de Dados",
|
||||||
|
"Microsserviços"
|
||||||
|
],
|
||||||
|
stack: [
|
||||||
|
"Java",
|
||||||
|
"C++",
|
||||||
|
"Spring Boot",
|
||||||
|
"Quarkus",
|
||||||
|
"Docker",
|
||||||
|
"Kubernetes",
|
||||||
|
"Jenkins",
|
||||||
|
"GitLab CI",
|
||||||
|
"PostgreSQL",
|
||||||
|
"MySQL",
|
||||||
|
"SQL Server",
|
||||||
|
"Oracle Cloud",
|
||||||
|
"Azure"
|
||||||
|
],
|
||||||
|
timeline: [
|
||||||
|
{
|
||||||
|
title: "Programador Sênior",
|
||||||
|
description: "Desenvolvimento de sistemas corporativos e soluções backend."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Tech Lead",
|
||||||
|
description: "Liderança técnica e apoio arquitetural."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Cloud & DevOps",
|
||||||
|
description: "Modernização de infraestrutura e automação."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
title: "Sistema de Processamento SPED",
|
||||||
|
description:
|
||||||
|
"Plataforma responsável pela geração e processamento automatizado de arquivos fiscais SPED."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Infraestrutura Oracle Cloud",
|
||||||
|
description: "Implementação e gerenciamento de infraestrutura cloud."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Servidor Git Privado com Gitea",
|
||||||
|
description: "Ambiente Git privado para versionamento e colaboração."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "APIs Corporativas",
|
||||||
|
description: "Desenvolvimento de APIs REST utilizando Spring Boot."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
contact: {
|
||||||
|
title: "Vamos Conversar",
|
||||||
|
description: "Disponível para oportunidades profissionais e novos desafios.",
|
||||||
|
linkedin: "LinkedIn"
|
||||||
|
},
|
||||||
|
ui: {
|
||||||
|
menuToggle: "Abrir menu",
|
||||||
|
themeToggle: "Alternar tema",
|
||||||
|
languageSwitcher: "Selecionar idioma",
|
||||||
|
backToTop: "Voltar ao topo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
lang: "en",
|
||||||
|
canonical: "https://clezio.com.br/en/",
|
||||||
|
meta: {
|
||||||
|
title: "Clezio Penha | Senior Backend Engineer",
|
||||||
|
description:
|
||||||
|
"Senior Backend Engineer with 15+ years of experience in Java, C++, REST APIs, cloud computing, DevOps and software architecture.",
|
||||||
|
ogTitle: "Clezio Penha | Backend Engineer",
|
||||||
|
ogDescription: "15+ years building robust, scalable and secure systems."
|
||||||
|
},
|
||||||
|
profile: {
|
||||||
|
name: "Clezio Penha",
|
||||||
|
role: "Senior Backend Engineer",
|
||||||
|
footerRole: "Senior Backend Engineer",
|
||||||
|
email: "clezio.penha@gmail.com",
|
||||||
|
linkedin: "https://www.linkedin.com/in/clezio"
|
||||||
|
},
|
||||||
|
nav: {
|
||||||
|
home: "Home",
|
||||||
|
about: "About",
|
||||||
|
specialties: "Specialties",
|
||||||
|
experience: "Experience",
|
||||||
|
projects: "Projects",
|
||||||
|
contact: "Contact"
|
||||||
|
},
|
||||||
|
hero: {
|
||||||
|
badge: "Senior Developer • Backend Engineer • Tech Lead",
|
||||||
|
title: "Turning business challenges into robust, scalable and secure solutions.",
|
||||||
|
description:
|
||||||
|
"Senior developer with 15+ years of experience in backend development, software architecture, cloud computing and technical leadership.",
|
||||||
|
primaryButton: "View Projects",
|
||||||
|
secondaryButton: "Get in Touch"
|
||||||
|
},
|
||||||
|
metrics: [
|
||||||
|
{
|
||||||
|
value: "15+",
|
||||||
|
label: "Years of Experience"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Tech Lead",
|
||||||
|
label: "Technical Leadership"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Cloud",
|
||||||
|
label: "Oracle Cloud & Azure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Backend",
|
||||||
|
label: "APIs and Enterprise Systems"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
about: {
|
||||||
|
title: "About Me",
|
||||||
|
description:
|
||||||
|
"Throughout my career, I have helped build and evolve enterprise systems, critical integrations, process automations and backend platforms focused on performance, scalability and reliability.",
|
||||||
|
workTitle: "How I Work"
|
||||||
|
},
|
||||||
|
workPrinciples: [
|
||||||
|
{
|
||||||
|
title: "Architecture",
|
||||||
|
description: "Technical planning before implementation."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Scalability",
|
||||||
|
description: "Systems prepared for continuous growth."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Performance",
|
||||||
|
description: "Operational efficiency and resource optimization."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Security",
|
||||||
|
description: "Good practices from the design phase."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Quality",
|
||||||
|
description: "Clean code and simplified maintenance."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
sections: {
|
||||||
|
specialties: "Specialties",
|
||||||
|
stack: "Technology Stack",
|
||||||
|
experience: "Professional Experience",
|
||||||
|
projects: "Featured Projects"
|
||||||
|
},
|
||||||
|
specialties: [
|
||||||
|
"REST APIs",
|
||||||
|
"Software Architecture",
|
||||||
|
"Cloud Computing",
|
||||||
|
"DevOps",
|
||||||
|
"Databases",
|
||||||
|
"Microservices"
|
||||||
|
],
|
||||||
|
stack: [
|
||||||
|
"Java",
|
||||||
|
"C++",
|
||||||
|
"Spring Boot",
|
||||||
|
"Quarkus",
|
||||||
|
"Docker",
|
||||||
|
"Kubernetes",
|
||||||
|
"Jenkins",
|
||||||
|
"GitLab CI",
|
||||||
|
"PostgreSQL",
|
||||||
|
"MySQL",
|
||||||
|
"SQL Server",
|
||||||
|
"Oracle Cloud",
|
||||||
|
"Azure"
|
||||||
|
],
|
||||||
|
timeline: [
|
||||||
|
{
|
||||||
|
title: "Senior Developer",
|
||||||
|
description: "Development of enterprise systems and backend solutions."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Tech Lead",
|
||||||
|
description: "Technical leadership and architectural support."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Cloud & DevOps",
|
||||||
|
description: "Infrastructure modernization and automation."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
title: "SPED Processing System",
|
||||||
|
description: "Platform responsible for the automated generation and processing of SPED tax files."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Oracle Cloud Infrastructure",
|
||||||
|
description: "Implementation and management of cloud infrastructure."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Private Git Server with Gitea",
|
||||||
|
description: "Private Git environment for version control and collaboration."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Enterprise APIs",
|
||||||
|
description: "Development of REST APIs using Spring Boot."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
contact: {
|
||||||
|
title: "Let's Talk",
|
||||||
|
description: "Available for professional opportunities and new challenges.",
|
||||||
|
linkedin: "LinkedIn"
|
||||||
|
},
|
||||||
|
ui: {
|
||||||
|
menuToggle: "Open menu",
|
||||||
|
themeToggle: "Toggle theme",
|
||||||
|
languageSwitcher: "Select language",
|
||||||
|
backToTop: "Back to top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
es: {
|
||||||
|
lang: "es",
|
||||||
|
canonical: "https://clezio.com.br/es/",
|
||||||
|
meta: {
|
||||||
|
title: "Clezio Penha | Senior Backend Engineer",
|
||||||
|
description:
|
||||||
|
"Programador Backend Sénior con más de 15 años de experiencia en Java, C++, APIs REST, cloud computing, DevOps y arquitectura de software.",
|
||||||
|
ogTitle: "Clezio Penha | Backend Engineer",
|
||||||
|
ogDescription: "Más de 15 años construyendo sistemas robustos, escalables y seguros."
|
||||||
|
},
|
||||||
|
profile: {
|
||||||
|
name: "Clezio Penha",
|
||||||
|
role: "Senior Backend Engineer",
|
||||||
|
footerRole: "Senior Backend Engineer",
|
||||||
|
email: "clezio.penha@gmail.com",
|
||||||
|
linkedin: "https://www.linkedin.com/in/clezio"
|
||||||
|
},
|
||||||
|
nav: {
|
||||||
|
home: "Inicio",
|
||||||
|
about: "Sobre mí",
|
||||||
|
specialties: "Especialidades",
|
||||||
|
experience: "Experiencia",
|
||||||
|
projects: "Proyectos",
|
||||||
|
contact: "Contacto"
|
||||||
|
},
|
||||||
|
hero: {
|
||||||
|
badge: "Programador Sénior • Backend Engineer • Tech Lead",
|
||||||
|
title: "Transformando desafíos de negocio en soluciones robustas, escalables y seguras.",
|
||||||
|
description:
|
||||||
|
"Programador sénior con más de 15 años de experiencia en desarrollo backend, arquitectura de software, cloud computing y liderazgo técnico.",
|
||||||
|
primaryButton: "Ver Proyectos",
|
||||||
|
secondaryButton: "Entrar en Contacto"
|
||||||
|
},
|
||||||
|
metrics: [
|
||||||
|
{
|
||||||
|
value: "15+",
|
||||||
|
label: "Años de Experiencia"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Tech Lead",
|
||||||
|
label: "Liderazgo Técnico"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Cloud",
|
||||||
|
label: "Oracle Cloud y Azure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Backend",
|
||||||
|
label: "APIs y Sistemas Corporativos"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
about: {
|
||||||
|
title: "Sobre Mí",
|
||||||
|
description:
|
||||||
|
"A lo largo de mi carrera he participado en la construcción y evolución de sistemas corporativos, integraciones críticas, automatizaciones de procesos y plataformas backend enfocadas en rendimiento, escalabilidad y confiabilidad.",
|
||||||
|
workTitle: "Cómo Trabajo"
|
||||||
|
},
|
||||||
|
workPrinciples: [
|
||||||
|
{
|
||||||
|
title: "Arquitectura",
|
||||||
|
description: "Planificación técnica antes de la implementación."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Escalabilidad",
|
||||||
|
description: "Sistemas preparados para el crecimiento continuo."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Rendimiento",
|
||||||
|
description: "Eficiencia operativa y optimización de recursos."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Seguridad",
|
||||||
|
description: "Buenas prácticas desde la concepción."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Calidad",
|
||||||
|
description: "Código limpio y mantenimiento simplificado."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
sections: {
|
||||||
|
specialties: "Especialidades",
|
||||||
|
stack: "Stack Tecnológico",
|
||||||
|
experience: "Experiencia Profesional",
|
||||||
|
projects: "Proyectos Destacados"
|
||||||
|
},
|
||||||
|
specialties: [
|
||||||
|
"APIs REST",
|
||||||
|
"Arquitectura de Software",
|
||||||
|
"Cloud Computing",
|
||||||
|
"DevOps",
|
||||||
|
"Bases de Datos",
|
||||||
|
"Microservicios"
|
||||||
|
],
|
||||||
|
stack: [
|
||||||
|
"Java",
|
||||||
|
"C++",
|
||||||
|
"Spring Boot",
|
||||||
|
"Quarkus",
|
||||||
|
"Docker",
|
||||||
|
"Kubernetes",
|
||||||
|
"Jenkins",
|
||||||
|
"GitLab CI",
|
||||||
|
"PostgreSQL",
|
||||||
|
"MySQL",
|
||||||
|
"SQL Server",
|
||||||
|
"Oracle Cloud",
|
||||||
|
"Azure"
|
||||||
|
],
|
||||||
|
timeline: [
|
||||||
|
{
|
||||||
|
title: "Programador Sénior",
|
||||||
|
description: "Desarrollo de sistemas corporativos y soluciones backend."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Tech Lead",
|
||||||
|
description: "Liderazgo técnico y apoyo arquitectónico."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Cloud & DevOps",
|
||||||
|
description: "Modernización de infraestructura y automatización."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
title: "Sistema de Procesamiento SPED",
|
||||||
|
description: "Plataforma responsable de la generación y procesamiento automatizado de archivos fiscales SPED."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Infraestructura Oracle Cloud",
|
||||||
|
description: "Implementación y gestión de infraestructura cloud."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Servidor Git Privado con Gitea",
|
||||||
|
description: "Entorno Git privado para versionamiento y colaboración."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "APIs Corporativas",
|
||||||
|
description: "Desarrollo de APIs REST utilizando Spring Boot."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
contact: {
|
||||||
|
title: "Hablemos",
|
||||||
|
description: "Disponible para oportunidades profesionales y nuevos desafíos.",
|
||||||
|
linkedin: "LinkedIn"
|
||||||
|
},
|
||||||
|
ui: {
|
||||||
|
menuToggle: "Abrir menú",
|
||||||
|
themeToggle: "Alternar tema",
|
||||||
|
languageSwitcher: "Seleccionar idioma",
|
||||||
|
backToTop: "Volver arriba"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type Translation = (typeof translations)[Locale];
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
---
|
||||||
|
import "../styles/global.css";
|
||||||
|
import { languageLinks } from "../data/translations";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
lang: "pt" | "en" | "es";
|
||||||
|
canonical?: string;
|
||||||
|
ogTitle?: string;
|
||||||
|
ogDescription?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
lang,
|
||||||
|
canonical,
|
||||||
|
ogTitle = title,
|
||||||
|
ogDescription = description
|
||||||
|
} = Astro.props;
|
||||||
|
|
||||||
|
const schema = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Person",
|
||||||
|
|
||||||
|
name: "Clezio Penha",
|
||||||
|
jobTitle: "Senior Software Engineer",
|
||||||
|
description,
|
||||||
|
email: "mailto:clezio.penha@gmail.com",
|
||||||
|
|
||||||
|
knowsAbout: [
|
||||||
|
"Java",
|
||||||
|
"C++",
|
||||||
|
"Spring Boot",
|
||||||
|
"Quarkus",
|
||||||
|
"Docker",
|
||||||
|
"Kubernetes",
|
||||||
|
"PostgreSQL",
|
||||||
|
"Oracle Cloud",
|
||||||
|
"Azure"
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const localeMap: Record<string, string> = {
|
||||||
|
"pt": "pt_BR",
|
||||||
|
"pt-BR": "pt_BR",
|
||||||
|
"en": "en_US",
|
||||||
|
"es": "es_ES"
|
||||||
|
};
|
||||||
|
---
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang={lang}>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<title>{title}</title>
|
||||||
|
|
||||||
|
<meta name="author" content="Clezio Penha">
|
||||||
|
<meta name="description" content={description}>
|
||||||
|
<link rel="canonical" href={canonical}>
|
||||||
|
<meta name="keywords" content="Java, Spring Boot, Backend, Cloud, Docker, Kubernetes, Oracle Cloud, Azure, APIs REST, DevOps">
|
||||||
|
<meta property="og:title" content={ogTitle}>
|
||||||
|
<meta property="og:description" content={ogDescription}>
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:url" content={canonical}>
|
||||||
|
<meta property="og:site_name" content="Clezio Penha">
|
||||||
|
<meta property="og:locale" content={localeMap[lang] ?? "pt_BR"}>
|
||||||
|
<meta property="og:image" content="https://clezio.com.br/og-image.png"/>
|
||||||
|
<meta property="og:image:width" content="1200" />
|
||||||
|
<meta property="og:image:height" content="630" />
|
||||||
|
<meta property="og:image:alt" content="Clezio Penha Portfolio" />
|
||||||
|
|
||||||
|
{
|
||||||
|
languageLinks.map(language => (
|
||||||
|
<link rel="alternate" hreflang={language.locale} href={language.href} />
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
<link rel="alternate" hreflang="x-default" href="/">
|
||||||
|
|
||||||
|
<script type="application/ld+json" set:html={JSON.stringify(schema)} />
|
||||||
|
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<slot />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
import PortfolioPage from "../../components/PortfolioPage.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<PortfolioPage locale="en" />
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
import PortfolioPage from "../../components/PortfolioPage.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<PortfolioPage locale="es" />
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
import PortfolioPage from "../components/PortfolioPage.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<PortfolioPage locale="pt-br" />
|
||||||
@@ -0,0 +1,691 @@
|
|||||||
|
/* =========================
|
||||||
|
RESET
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
|
||||||
|
font-family: "Inter", sans-serif;
|
||||||
|
|
||||||
|
background:
|
||||||
|
radial-gradient(
|
||||||
|
circle at top,
|
||||||
|
rgba(59,130,246,.08),
|
||||||
|
transparent 45%
|
||||||
|
),
|
||||||
|
var(--background);
|
||||||
|
|
||||||
|
color: var(--text-primary);
|
||||||
|
|
||||||
|
line-height: 1.7;
|
||||||
|
|
||||||
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
transition:
|
||||||
|
background .3s ease,
|
||||||
|
color .3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
VARIABLES
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--background: #09090b;
|
||||||
|
--surface: #111827;
|
||||||
|
--surface-hover: #1f2937;
|
||||||
|
--border: #27272a;
|
||||||
|
--text-primary: #fafafa;
|
||||||
|
--text-secondary: #a1a1aa;
|
||||||
|
--accent: #3b82f6;
|
||||||
|
--accent-hover: #2563eb;
|
||||||
|
--header-bg: rgba(9, 9, 11, 0.75);
|
||||||
|
--header-border: rgba(255, 255, 255, 0.05);
|
||||||
|
--hero-title-start: #ffffff;
|
||||||
|
--hero-title-end: #dbeafe;
|
||||||
|
--container: 1200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="light"] {
|
||||||
|
--background: #f8fafc;
|
||||||
|
--surface: #ffffff;
|
||||||
|
--surface-hover: #f1f5f9;
|
||||||
|
--border: #e2e8f0;
|
||||||
|
--text-primary: #0f172a;
|
||||||
|
--text-secondary: #475569;
|
||||||
|
--accent: #2563eb;
|
||||||
|
--accent-hover: #1d4ed8;
|
||||||
|
--header-bg: rgba(248, 250, 252, 0.82);
|
||||||
|
--header-border: rgba(15, 23, 42, 0.08);
|
||||||
|
--hero-title-start: #0f172a;
|
||||||
|
--hero-title-end: #2563eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
GLOBAL
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
max-width: var(--container);
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 120px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section h2 {
|
||||||
|
font-size: 2.4rem;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section p {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
max-width: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
HEADER
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
.header {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 1000;
|
||||||
|
backdrop-filter: blur(18px);
|
||||||
|
background: var(--header-bg);
|
||||||
|
border-bottom: 1px solid var(--header-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
max-width: var(--container);
|
||||||
|
margin: auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 18px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-links {
|
||||||
|
display: flex;
|
||||||
|
gap: 28px;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-links a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
transition: 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-links a:hover {
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
HERO
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
.hero {
|
||||||
|
min-height: 85vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 120px 24px 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-content {
|
||||||
|
max-width: 900px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-badge {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
padding: 8px 16px;
|
||||||
|
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 999px;
|
||||||
|
|
||||||
|
background: color-mix(in srgb,
|
||||||
|
var(--surface) 80%,
|
||||||
|
transparent);
|
||||||
|
|
||||||
|
color: var(--text-secondary);
|
||||||
|
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero h1 {
|
||||||
|
font-size: clamp(2.8rem, 6vw, 4.8rem);
|
||||||
|
|
||||||
|
background: linear-gradient(180deg,
|
||||||
|
var(--hero-title-start) 0%,
|
||||||
|
var(--hero-title-end) 100%);
|
||||||
|
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
|
||||||
|
max-width: 1000px;
|
||||||
|
|
||||||
|
line-height: 1.1;
|
||||||
|
|
||||||
|
margin: 0 auto 28px;
|
||||||
|
|
||||||
|
font-weight: 800;
|
||||||
|
|
||||||
|
letter-spacing: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero p {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
|
||||||
|
font-size: 1.2rem;
|
||||||
|
|
||||||
|
max-width: 700px;
|
||||||
|
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-actions {
|
||||||
|
margin-top: 40px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
BUTTONS
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
.btn-primary,
|
||||||
|
.btn-secondary {
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
padding: 14px 26px;
|
||||||
|
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
|
transition: 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: var(--accent);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
background: var(--accent-hover);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
METRICS
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
.metrics {
|
||||||
|
max-width: var(--container);
|
||||||
|
|
||||||
|
margin: auto;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
|
||||||
|
gap: 24px;
|
||||||
|
|
||||||
|
padding: 0 24px 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-card {
|
||||||
|
background: var(--surface);
|
||||||
|
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
|
||||||
|
padding: 32px;
|
||||||
|
|
||||||
|
border-radius: 20px;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-card h3 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metric-card p {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
GRID CARDS
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
.cards-grid {
|
||||||
|
display: grid;
|
||||||
|
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||||
|
|
||||||
|
gap: 24px;
|
||||||
|
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card,
|
||||||
|
.project-card {
|
||||||
|
background: var(--surface);
|
||||||
|
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
|
||||||
|
border-radius: 20px;
|
||||||
|
|
||||||
|
padding: 28px;
|
||||||
|
|
||||||
|
transition: all .3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:hover,
|
||||||
|
.project-card:hover {
|
||||||
|
transform: translateY(-6px);
|
||||||
|
|
||||||
|
border-color: rgba(59, 130, 246, .35);
|
||||||
|
|
||||||
|
background: var(--surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card h3,
|
||||||
|
.project-card h3 {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card p,
|
||||||
|
.project-card p {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
TECH STACK
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
.tech-stack {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
gap: 14px;
|
||||||
|
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-stack span {
|
||||||
|
padding: 10px 18px;
|
||||||
|
|
||||||
|
background: var(--surface);
|
||||||
|
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
|
||||||
|
border-radius: 999px;
|
||||||
|
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
TIMELINE
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
.timeline {
|
||||||
|
margin-top: 40px;
|
||||||
|
|
||||||
|
border-left: 2px solid var(--border);
|
||||||
|
|
||||||
|
padding-left: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-item {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
margin-bottom: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-item::before {
|
||||||
|
content: "";
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
left: -41px;
|
||||||
|
top: 8px;
|
||||||
|
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-item h3 {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-item p {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
CONTACT
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
.contact-links {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
margin-top: 30px;
|
||||||
|
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-links a {
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
color: var(--accent);
|
||||||
|
|
||||||
|
transition: 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-links a:hover {
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
FOOTER
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
padding: 50px 24px;
|
||||||
|
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer p:first-child {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================= BACK TO TOP ========================= */
|
||||||
|
#backToTop {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 30px;
|
||||||
|
right: 30px;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #3b82f6;
|
||||||
|
color: white;
|
||||||
|
font-size: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
#backToTop:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#backToTop.show {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================= SECTION ANIMATION ========================= */
|
||||||
|
/*
|
||||||
|
.section,
|
||||||
|
.metric-card,
|
||||||
|
.project-card,
|
||||||
|
.card {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
transition: all 0.7s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.show-element {
|
||||||
|
opacity: 1 !important;
|
||||||
|
transform: translateY(0) !important;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ========================= ACTIVE NAV ========================= */
|
||||||
|
.nav-links a.active {
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-links a.active::after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
margin-top: 6px;
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
background: #3b82f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
THEME BUTTON
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#theme-toggle {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 50%;
|
||||||
|
background: color-mix(in srgb, var(--surface) 72%, transparent);
|
||||||
|
|
||||||
|
color: var(--text-primary);
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
font-size: 1.05rem;
|
||||||
|
line-height: 1;
|
||||||
|
|
||||||
|
transition: .3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#theme-toggle:hover {
|
||||||
|
|
||||||
|
background: var(--surface-hover);
|
||||||
|
transform: rotate(18deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-switcher {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 4px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 999px;
|
||||||
|
background: color-mix(in srgb, var(--surface) 72%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-switcher a {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 36px;
|
||||||
|
min-height: 32px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
font: inherit;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
line-height: 1;
|
||||||
|
text-decoration: none;
|
||||||
|
transition:
|
||||||
|
background .2s ease,
|
||||||
|
color .2s ease,
|
||||||
|
transform .2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-switcher a:hover,
|
||||||
|
.language-switcher a.active {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-switcher a.active {
|
||||||
|
transform: scale(1.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* =========================
|
||||||
|
RESPONSIVE
|
||||||
|
========================= */
|
||||||
|
|
||||||
|
@media (max-width: 960px) {
|
||||||
|
|
||||||
|
.metrics {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-links {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero h1 {
|
||||||
|
font-size: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
padding: 90px 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
|
||||||
|
.metrics {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero h1 {
|
||||||
|
font-size: 2.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero p {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section h2 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
padding: 14px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#theme-toggle {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-switcher a {
|
||||||
|
min-width: 32px;
|
||||||
|
min-height: 30px;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-toggle {
|
||||||
|
display: none;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:hover,
|
||||||
|
.project-card:hover {
|
||||||
|
transform: translateY(-6px) scale(1.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width:960px) {
|
||||||
|
.navbar {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-toggle {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-links.open {
|
||||||
|
display: flex !important;
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 1px);
|
||||||
|
left: 24px;
|
||||||
|
right: 24px;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 18px;
|
||||||
|
background: var(--surface);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 14px;
|
||||||
|
box-shadow: 0 18px 40px rgba(0, 0, 0, .22);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"extends": "astro/tsconfigs/strict",
|
||||||
|
"include": [".astro/types.d.ts", "**/*"],
|
||||||
|
"exclude": ["dist"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user