Skip to content

Commit 6c3297a

Browse files
feat(nav): menu responsive — sidebar drawer + hamburguesa en movil
- Hamburguesa (laton) visible <820px junto al toggle EN/ES - Sidebar drawer off-canvas derecha estilo Espresso & Brass: links Cormorant grande con indice laton, correo + ubicacion al pie - Overlay con blur, cierre por X / overlay / link / Escape, bloqueo de scroll - Animacion de hamburguesa a X, aria-expanded/aria-hidden para accesibilidad - Antes: en movil el nav simplemente desaparecia (sin menu)
1 parent 8ba58e5 commit 6c3297a

3 files changed

Lines changed: 148 additions & 4 deletions

File tree

css/style.css

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,90 @@ a { color: inherit; text-decoration: none; }
118118
letter-spacing: 0.5px;
119119
}
120120
.lang-opt.active { background: var(--brass); color: var(--bg); }
121-
@media (max-width: 820px) { .nav-links { display: none; } }
121+
.nav-right { display: flex; align-items: center; gap: 14px; }
122+
123+
/* hamburger */
124+
.nav-burger {
125+
display: none;
126+
flex-direction: column;
127+
justify-content: center;
128+
gap: 5px;
129+
width: 38px; height: 38px;
130+
padding: 0;
131+
background: transparent;
132+
border: 1px solid var(--border);
133+
border-radius: 3px;
134+
cursor: pointer;
135+
}
136+
.nav-burger span {
137+
display: block;
138+
width: 18px; height: 1.5px;
139+
margin: 0 auto;
140+
background: var(--brass);
141+
transition: transform .3s, opacity .3s;
142+
}
143+
.nav-burger.open span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }
144+
.nav-burger.open span:nth-child(2) { opacity: 0; }
145+
.nav-burger.open span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); }
146+
147+
/* overlay */
148+
.nav-overlay {
149+
position: fixed; inset: 0;
150+
background: rgba(7, 5, 4, 0.6);
151+
backdrop-filter: blur(2px);
152+
opacity: 0;
153+
pointer-events: none;
154+
transition: opacity .4s;
155+
z-index: 150;
156+
}
157+
.nav-overlay.open { opacity: 1; pointer-events: auto; }
158+
159+
/* sidebar drawer */
160+
.sidebar {
161+
position: fixed;
162+
top: 0; right: 0;
163+
height: 100%;
164+
width: min(82vw, 340px);
165+
background: var(--bg-2);
166+
border-left: 1px solid var(--border);
167+
transform: translateX(100%);
168+
transition: transform .42s cubic-bezier(.4,0,.1,1);
169+
z-index: 200;
170+
display: flex;
171+
flex-direction: column;
172+
padding: 24px 28px 32px;
173+
}
174+
.sidebar.open { transform: translateX(0); box-shadow: -20px 0 60px rgba(0,0,0,.45); }
175+
.sidebar-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 36px; }
176+
.sidebar-brand { display: flex; align-items: center; gap: 3px; font-family: 'Cormorant Garamond', serif; font-weight: 700; font-size: 24px; }
177+
.sidebar-brand .brand-mark { color: var(--brass); }
178+
.sidebar-brand .brand-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--brass); margin-bottom: 4px; }
179+
.sidebar-close { background: transparent; border: none; color: var(--text-dim); font-size: 30px; line-height: 1; cursor: pointer; transition: color .25s; padding: 0 4px; }
180+
.sidebar-close:hover { color: var(--brass); }
181+
.sidebar-links { display: flex; flex-direction: column; gap: 4px; flex: 1; }
182+
.sidebar-links a {
183+
display: flex;
184+
align-items: baseline;
185+
gap: 14px;
186+
padding: 14px 0;
187+
border-bottom: 1px solid var(--hairline);
188+
font-family: 'Cormorant Garamond', serif;
189+
font-size: 30px;
190+
font-weight: 600;
191+
color: var(--text);
192+
transition: color .25s, padding-left .25s;
193+
}
194+
.sidebar-links a:hover, .sidebar-links a:focus { color: var(--brass); padding-left: 8px; }
195+
.sidebar-links .sl-num { font-family: 'IBM Plex Mono', monospace; font-size: 12px; color: var(--brass); opacity: .8; }
196+
.sidebar-foot { display: flex; flex-direction: column; gap: 4px; margin-top: 28px; font-family: 'IBM Plex Mono', monospace; font-size: 12px; color: var(--text-mute); }
197+
.sidebar-foot a { transition: color .25s; }
198+
.sidebar-foot a:hover { color: var(--brass); }
199+
body.menu-open { overflow: hidden; }
200+
201+
@media (max-width: 820px) {
202+
.nav-links { display: none; }
203+
.nav-burger { display: flex; }
204+
}
122205

123206
/* ============= HERO ============= */
124207
.hero {

index.html

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,38 @@
3434
<a href="#skills" data-i18n="nav.skills">Stack</a>
3535
<a href="#contact" data-i18n="nav.contact">Contact</a>
3636
</div>
37-
<div class="lang-toggle" id="langToggle" role="button" aria-label="Toggle language" tabindex="0">
38-
<span class="lang-opt active" data-lang-opt="en">EN</span>
39-
<span class="lang-opt" data-lang-opt="es">ES</span>
37+
<div class="nav-right">
38+
<div class="lang-toggle" id="langToggle" role="button" aria-label="Toggle language" tabindex="0">
39+
<span class="lang-opt active" data-lang-opt="en">EN</span>
40+
<span class="lang-opt" data-lang-opt="es">ES</span>
41+
</div>
42+
<button class="nav-burger" id="navBurger" aria-label="Open menu" aria-expanded="false" aria-controls="sidebar">
43+
<span></span><span></span><span></span>
44+
</button>
4045
</div>
4146
</div>
4247
</nav>
4348

49+
<!-- Mobile sidebar drawer -->
50+
<div class="nav-overlay" id="navOverlay" hidden></div>
51+
<aside class="sidebar" id="sidebar" aria-hidden="true">
52+
<div class="sidebar-head">
53+
<span class="sidebar-brand"><span class="brand-mark">IR</span><span class="brand-dot"></span></span>
54+
<button class="sidebar-close" id="sidebarClose" aria-label="Close menu">&times;</button>
55+
</div>
56+
<nav class="sidebar-links">
57+
<a href="#about"><span class="sl-num">01</span><span data-i18n="nav.about">About</span></a>
58+
<a href="#projects"><span class="sl-num">02</span><span data-i18n="nav.projects">Work</span></a>
59+
<a href="#experience"><span class="sl-num">03</span><span data-i18n="nav.experience">Experience</span></a>
60+
<a href="#skills"><span class="sl-num">04</span><span data-i18n="nav.skills">Stack</span></a>
61+
<a href="#contact"><span class="sl-num">05</span><span data-i18n="nav.contact">Contact</span></a>
62+
</nav>
63+
<div class="sidebar-foot">
64+
<a href="mailto:ivanrr1991@gmail.com">ivanrr1991@gmail.com</a>
65+
<span>Lima, Peru</span>
66+
</div>
67+
</aside>
68+
4469
<!-- HERO -->
4570
<header id="hero" class="hero">
4671
<div class="container hero-inner">

js/app.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,41 @@ function initNavScroll() {
219219
onScroll();
220220
}
221221

222+
// ====== Mobile sidebar drawer ======
223+
function initSidebar() {
224+
const burger = document.getElementById('navBurger');
225+
const sidebar = document.getElementById('sidebar');
226+
const overlay = document.getElementById('navOverlay');
227+
const closeBtn = document.getElementById('sidebarClose');
228+
if (!burger || !sidebar || !overlay) return;
229+
230+
function open() {
231+
sidebar.classList.add('open');
232+
overlay.hidden = false;
233+
requestAnimationFrame(() => overlay.classList.add('open'));
234+
burger.classList.add('open');
235+
burger.setAttribute('aria-expanded', 'true');
236+
sidebar.setAttribute('aria-hidden', 'false');
237+
document.body.classList.add('menu-open');
238+
}
239+
function close() {
240+
sidebar.classList.remove('open');
241+
overlay.classList.remove('open');
242+
burger.classList.remove('open');
243+
burger.setAttribute('aria-expanded', 'false');
244+
sidebar.setAttribute('aria-hidden', 'true');
245+
document.body.classList.remove('menu-open');
246+
setTimeout(() => { if (!overlay.classList.contains('open')) overlay.hidden = true; }, 420);
247+
}
248+
function toggle() { sidebar.classList.contains('open') ? close() : open(); }
249+
250+
burger.addEventListener('click', toggle);
251+
overlay.addEventListener('click', close);
252+
if (closeBtn) closeBtn.addEventListener('click', close);
253+
sidebar.querySelectorAll('.sidebar-links a').forEach((a) => a.addEventListener('click', close));
254+
document.addEventListener('keydown', (e) => { if (e.key === 'Escape') close(); });
255+
}
256+
222257
// ====== Init ======
223258
document.addEventListener('DOMContentLoaded', () => {
224259
const saved = (() => { try { return localStorage.getItem('site_lang'); } catch (e) { return null; } })();
@@ -238,4 +273,5 @@ document.addEventListener('DOMContentLoaded', () => {
238273
animateCounters();
239274
revealOnScroll();
240275
initNavScroll();
276+
initSidebar();
241277
});

0 commit comments

Comments
 (0)