Add: experimental mobile version

This commit is contained in:
Jose
2026-02-16 18:18:34 +01:00
parent aec029b670
commit 95dd13595e
12 changed files with 297 additions and 28 deletions

View File

@@ -1,11 +1,11 @@
import '@/css/NavBar.css'; import { useWindowWidth } from '@/hooks/useWindowWidth';
import NavItem from './NavItem'; import NavBarDesktop from '@/components/NavBar/desktop/NavBarDesktop';
import LanguageButton from '../LanguageButton'; import NavBarMobile from '@/components/NavBar/mobile/NavBarMobile';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const NavBar = () => { const NavBar = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const width = useWindowWidth();
const navItems = [ const navItems = [
{ label: t("nav.inicio.label"), href: `/#${t("nav.inicio.href")}` }, { label: t("nav.inicio.label"), href: `/#${t("nav.inicio.href")}` },
{ label: t("nav.muestras.label"), href: `/#${t("nav.muestras.href")}` }, { label: t("nav.muestras.label"), href: `/#${t("nav.muestras.href")}` },
@@ -14,26 +14,12 @@ const NavBar = () => {
{ label: t("nav.login.label"), href: `/${t("nav.login.href")}` } { label: t("nav.login.label"), href: `/${t("nav.login.href")}` }
]; ];
const centerItems = navItems.slice(0, navItems.length - 1); return width > 991.98 ? (
const rightItems = navItems.slice(-1); <nav className="nav-container p-0">
<NavBarDesktop navItems={navItems} />
return (
<nav className="nav-container">
<ul className="d-flex align-items-center list-unstyled m-0 w-100 justify-content-center position-relative">
{centerItems.map((item, index) => (
<NavItem key={index} item={item} index={index} total={centerItems.length} />
))}
<li className="position-absolute end-0 d-flex">
<LanguageButton as='navitem' index={0} total={rightItems.length + 1} />
{rightItems.map((item, index) => (
<NavItem key={index} item={item} index={index + 1} total={rightItems.length + 1} />
))}
</li>
</ul>
</nav> </nav>
) : (
<NavBarMobile navItems={navItems} />
); );
}; };

View File

@@ -1,6 +1,6 @@
import { HashLink } from "react-router-hash-link/dist/react-router-hash-link.cjs.production"; import { HashLink } from "react-router-hash-link/dist/react-router-hash-link.cjs.production";
const NavItem = ({ item, index, onClick }) => { const NavItem = ({ item, index, onClick, onCloseNav }) => {
let borderClass = `${index === 0 ? "border-left border-right" : "border-right"}`; let borderClass = `${index === 0 ? "border-left border-right" : "border-right"}`;
const handleClick = (e) => { const handleClick = (e) => {
@@ -10,6 +10,10 @@ const NavItem = ({ item, index, onClick }) => {
} else if (item.href === "#") { } else if (item.href === "#") {
e.preventDefault(); e.preventDefault();
} }
if (onCloseNav) {
onCloseNav();
}
}; };
return ( return (

View File

@@ -0,0 +1,30 @@
import NavItem from '@/components/NavBar/NavItem';
import LanguageButton from '@/components/LanguageButton';
import '@/css/NavBar.css';
const NavBarDesktop = ({ navItems }) => {
const centerItems = navItems.slice(0, navItems.length - 1);
const rightItems = navItems.slice(-1);
return (
<ul className="d-flex align-items-center list-unstyled m-0 w-100 justify-content-center position-relative">
<li className="position-absolute start-0 d-flex">
<LanguageButton as='navitem' index={0} total={rightItems.length + 1} />
</li>
{centerItems.map((item, index) => (
<NavItem key={index} item={item} index={index} total={centerItems.length} />
))}
<li className="position-absolute end-0 d-flex">
{rightItems.map((item, index) => (
<NavItem key={index} item={item} index={index} total={rightItems.length} />
))}
</li>
</ul>
);
};
export default NavBarDesktop;

View File

@@ -0,0 +1,41 @@
import { useState } from 'react';
import NavItem from '@/components/NavBar/NavItem';
import LanguageButton from '@/components/LanguageButton';
import '@/css/NavBarMobile.css';
const NavBarMobile = ({ navItems }) => {
const [open, setOpen] = useState(false);
const handleClose = () => {
setOpen(false);
}
return (
<nav className="nav-container d-lg-none">
<div className="nav-mobile-header d-flex justify-content-between align-items-center">
{/* Por si algun dia se pone
<div className="nav-mobile-brand"></div>
*/}
<button
className={`nav-mobile-toggle ms-2 ${open ? 'open' : ''}`}
onClick={() => setOpen(!open)}
>
<span></span>
<span></span>
<span></span>
</button>
</div>
{open && (
<ul className="nav-list-mobile d-flex flex-column align-items-center list-unstyled m-0 w-100">
{navItems.map((item, index) => (
<NavItem key={index} item={item} onCloseNav={handleClose} />
))}
<LanguageButton as="navitem" />
</ul>
)}
</nav>
);
};
export default NavBarMobile;

View File

@@ -23,4 +23,31 @@ header p {
letter-spacing: 3px; letter-spacing: 3px;
font-weight: bold; font-weight: bold;
text-shadow: 0 0 5px var(--blood-god); text-shadow: 0 0 5px var(--blood-god);
}
@media (max-width: 991.98px) {
header h1 {
font-size: 2.6rem;
letter-spacing: 4px;
}
header p {
font-size: 1rem;
letter-spacing: 2px;
}
}
@media (max-width: 575.98px) {
header {
padding: 2.5rem 1rem !important;
}
header h1 {
font-size: 2rem;
letter-spacing: 3px;
}
header p {
font-size: 0.9rem;
}
} }

View File

@@ -73,4 +73,25 @@ input.form-control:focus {
border-color: var(--plasma-glow) !important; border-color: var(--plasma-glow) !important;
box-shadow: 0 0 20px var(--blood-god) !important; box-shadow: 0 0 20px var(--blood-god) !important;
text-shadow: 0 0 5px #fff !important; text-shadow: 0 0 5px #fff !important;
}
@media (max-width: 991.98px) {
.login-card {
padding: 28px;
}
}
@media (max-width: 575.98px) {
.login-card {
padding: 20px;
}
.login-card h1 {
font-size: 1.6rem;
}
.login-button {
padding: 12px 24px !important;
width: 100% !important;
}
} }

67
src/css/NavBarMobile.css Normal file
View File

@@ -0,0 +1,67 @@
.nav-container {
background-color: #0f1114;
border-bottom: 1px solid var(--dirty-gold);
position: sticky;
top: 0;
z-index: 900;
padding: 0;
}
.nav-mobile-header {
padding: 10px 0;
}
.nav-mobile-brand {
color: #666;
font-family: 'Cinzel', serif;
font-weight: 700;
text-transform: uppercase;
}
.nav-mobile-toggle {
all: unset;
cursor: pointer;
display: flex;
flex-direction: column;
gap: 4px;
padding: 5px;
}
.nav-mobile-toggle span {
display: block;
width: 25px;
height: 2px;
background-color: var(--dirty-gold);
transition: all 0.3s ease;
}
.nav-mobile-toggle.open span:nth-child(1) {
transform: rotate(45deg) translate(5px, 5px);
}
.nav-mobile-toggle.open span:nth-child(2) {
opacity: 0;
}
.nav-mobile-toggle.open span:nth-child(3) {
transform: rotate(-45deg) translate(5px, -5px);
}
.nav-list-mobile {
width: 100%;
margin: 0;
padding: 0;
}
.nav-list-mobile li {
width: 100%;
text-align: center;
}
.nav-list-mobile .nav-link-custom,
.nav-list-mobile .nav-button {
padding: 12px 0;
font-size: 1rem;
width: 100%;
border-bottom: 1px solid #222;
}

View File

@@ -28,4 +28,20 @@
.tech-card p { .tech-card p {
line-height: 1.6; line-height: 1.6;
font-size: 1.1rem; font-size: 1.1rem;
}
@media (max-width: 991.98px) {
.tech-card {
padding: 28px;
}
.tech-card p {
font-size: 1rem;
}
}
@media (max-width: 575.98px) {
.tech-card {
padding: 20px;
}
} }

View File

@@ -241,4 +241,68 @@ section {
98% { opacity: 0.5; } 98% { opacity: 0.5; }
} }
h1, h2 { animation: textFlicker 5s infinite; } h1, h2 { animation: textFlicker 5s infinite; }
@media (max-width: 991.98px) {
section {
padding: 60px 16px !important;
}
.section-title {
font-size: 2rem;
}
.section-title::before,
.section-title::after {
margin: 0 10px;
font-size: 1.1rem;
}
.foto-placeholder {
height: 240px;
}
.btn-imperial {
padding: 12px 28px;
font-size: 0.9rem;
}
.tech-input,
.tech-textarea {
font-size: 0.95rem;
}
}
@media (max-width: 575.98px) {
section {
padding: 40px 12px !important;
}
.section-title {
font-size: 1.6rem;
letter-spacing: 1px;
}
.section-title::before,
.section-title::after {
margin: 0 8px;
font-size: 1rem;
}
.foto-placeholder {
height: 200px;
}
.foto-placeholder span {
font-size: 2.5rem;
}
.foto-caption {
font-size: 0.75rem;
}
.btn-imperial {
width: 100%;
text-align: center;
}
}

View File

@@ -0,0 +1,13 @@
import { useState, useEffect } from "react";
export const useWindowWidth = () => {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return width;
};

View File

@@ -6,7 +6,7 @@
"inicio": { "label": "Home", "href": "home" }, "inicio": { "label": "Home", "href": "home" },
"muestras": { "label": "Portfolio", "href": "portfolio" }, "muestras": { "label": "Portfolio", "href": "portfolio" },
"pedidos": { "label": "Comissions", "href": "comissions" }, "pedidos": { "label": "Comissions", "href": "comissions" },
"sobre": { "label": "About Markcus", "href": "about-me" }, "sobre": { "label": "About", "href": "about-me" },
"login": { "label": "Login", "href": "login" } "login": { "label": "Login", "href": "login" }
}, },
"home": { "home": {
@@ -30,7 +30,7 @@
"title": "Sample C-3: Xenos Filth" "title": "Sample C-3: Xenos Filth"
} }
], ],
"pedidos_title": "+++ Plea to the Manufactorum +++", "pedidos_title": "+++ Comission +++",
"pedidos_text": "Fill in the details to request the attention of the Artificer. Be precise, time is an Emperor-limited resource.", "pedidos_text": "Fill in the details to request the attention of the Artificer. Be precise, time is an Emperor-limited resource.",
"pedidos_name": "Commander Designation (Name)", "pedidos_name": "Commander Designation (Name)",
"pedidos_email": "Vox Frequency (Email)", "pedidos_email": "Vox Frequency (Email)",

View File

@@ -23,7 +23,7 @@
{ "icon": "⚔", "title": "Muestra C-3: Xenos Filth" } { "icon": "⚔", "title": "Muestra C-3: Xenos Filth" }
], ],
"pedidos_title": "+++ Súplica al Manufactorum +++", "pedidos_title": "+++ Comisión +++",
"pedidos_text": "Rellena los datos para solicitar la atención del Artífice. Sé preciso, el tiempo es un recurso limitado del Emperador.", "pedidos_text": "Rellena los datos para solicitar la atención del Artífice. Sé preciso, el tiempo es un recurso limitado del Emperador.",
"pedidos_name": "Designación del Comandante (Nombre)", "pedidos_name": "Designación del Comandante (Nombre)",
"pedidos_email": "Frecuencia Vox (Email)", "pedidos_email": "Frecuencia Vox (Email)",