athlefi-ui 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/atoms/auxfile.astro +32 -111
- package/src/components/molecules/LinkGroup.astro +70 -0
- package/src/components/molecules/NavMenu.astro +79 -0
- package/src/components/molecules/SocialGroup.astro +64 -0
- package/src/components/organisms/Footer.astro +214 -0
- package/src/components/organisms/Header.astro +407 -0
- package/src/index.ts +5 -2
package/package.json
CHANGED
|
@@ -1,120 +1,41 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
* Copyright (c) 2025 Grupo DedSec S.A. de C.V.
|
|
4
|
-
* Este código fuente es propiedad exclusiva de Grupo DedSec S.A. de C.V.
|
|
5
|
-
* Se prohíbe su uso, reproducción, distribución o modificación, total o parcial, sin autorización expresa por escrito.
|
|
6
|
-
* No se permite el uso comercial ni la redistribución pública del mismo.
|
|
7
|
-
* Para licenciamiento, contactar a: admin@dedsec.com.mx
|
|
8
|
-
*/
|
|
2
|
+
import NavLink from "../atoms/NavLink.astro";
|
|
9
3
|
|
|
10
|
-
|
|
11
|
-
* Átomo: TimelineStep
|
|
12
|
-
* @description Paso individual de una línea de tiempo con ícono, título y descripción
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
export interface Props {
|
|
16
|
-
icon: string;
|
|
17
|
-
title: string;
|
|
18
|
-
description: string;
|
|
19
|
-
stepNumber: number;
|
|
20
|
-
isVertical?: boolean;
|
|
21
|
-
animationDelay?: number;
|
|
4
|
+
export interface Props {
|
|
22
5
|
class?: string;
|
|
6
|
+
isMobile?: boolean;
|
|
23
7
|
}
|
|
24
8
|
|
|
25
|
-
const {
|
|
26
|
-
icon,
|
|
27
|
-
title,
|
|
28
|
-
description,
|
|
29
|
-
stepNumber,
|
|
30
|
-
isVertical = false,
|
|
31
|
-
animationDelay = 0,
|
|
32
|
-
class: className = ""
|
|
33
|
-
} = Astro.props;
|
|
34
|
-
|
|
35
|
-
// Importar los iconos SVG desde assets
|
|
36
|
-
import shieldIcon from '@assets/shield-checkmark-outline.svg?raw';
|
|
37
|
-
import searchIcon from '@assets/search-outline.svg?raw';
|
|
38
|
-
import cardIcon from '@assets/card-outline.svg?raw';
|
|
39
|
-
import chartIcon from '@assets/bar-chart-outline.svg?raw';
|
|
40
|
-
import trophyIcon from '@assets/trophy-outline.svg?raw';
|
|
41
|
-
import userIcon from '@assets/person-outline.svg?raw';
|
|
42
|
-
import documentIcon from '@assets/document-text-outline.svg?raw';
|
|
43
|
-
import laptopIcon from '@assets/laptop-outline.svg?raw';
|
|
44
|
-
import cashIcon from '@assets/cash-outline.svg?raw';
|
|
45
|
-
import pieIcon from '@assets/pie-chart-outline.svg?raw';
|
|
46
|
-
|
|
47
|
-
// Mapeo de iconos disponibles
|
|
48
|
-
const iconMap: Record<string, string> = {
|
|
49
|
-
shield: shieldIcon,
|
|
50
|
-
search: searchIcon,
|
|
51
|
-
card: cardIcon,
|
|
52
|
-
chart: chartIcon,
|
|
53
|
-
trophy: trophyIcon,
|
|
54
|
-
user: userIcon,
|
|
55
|
-
'document-text': documentIcon,
|
|
56
|
-
laptop: laptopIcon,
|
|
57
|
-
cash: cashIcon,
|
|
58
|
-
pie: pieIcon
|
|
59
|
-
};
|
|
9
|
+
const { class: className = "", isMobile = false } = Astro.props;
|
|
60
10
|
|
|
61
|
-
const
|
|
11
|
+
const navigationItems = [
|
|
12
|
+
{ href: "#atleta", label: "Atleta" },
|
|
13
|
+
{ href: "/us", label: "Nosotros" },
|
|
14
|
+
{ href: "/how-work", label: "Cómo trabajamos" },
|
|
15
|
+
{ href: "/contact", label: "Contacto" },
|
|
16
|
+
];
|
|
62
17
|
---
|
|
63
18
|
|
|
64
|
-
<
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
<h3 class={`subtitle-m-style text-white ${isVertical ? ' ' : 'mb-2 mt-4'}`}>
|
|
87
|
-
{stepNumber}. {title}
|
|
88
|
-
</h3>
|
|
89
|
-
<p class={`body-s-style text-gray-300 ${!isVertical ? 'max-w-48' : ''}`}>
|
|
90
|
-
{description}
|
|
91
|
-
</p>
|
|
92
|
-
</div>
|
|
93
|
-
</div>
|
|
19
|
+
<nav class={`${className}`} aria-label="Navegación principal">
|
|
20
|
+
<ul
|
|
21
|
+
class={`
|
|
22
|
+
${
|
|
23
|
+
isMobile
|
|
24
|
+
? "flex flex-col space-y-4 py-4"
|
|
25
|
+
: "hidden lg:flex lg:items-center lg:space-x-8"
|
|
26
|
+
}
|
|
27
|
+
`}
|
|
28
|
+
role="menubar"
|
|
29
|
+
>
|
|
30
|
+
{
|
|
31
|
+
navigationItems.map((item) => (
|
|
32
|
+
<li role="none">
|
|
33
|
+
<NavLink href={item.href} class={isMobile ? "w-full text-left" : ""}>
|
|
34
|
+
{item.label}
|
|
35
|
+
</NavLink>
|
|
36
|
+
</li>
|
|
37
|
+
))
|
|
38
|
+
}
|
|
39
|
+
</ul>
|
|
40
|
+
</nav>
|
|
94
41
|
|
|
95
|
-
<style>
|
|
96
|
-
.timeline-step.horizontal {
|
|
97
|
-
@apply flex flex-col items-center w-1/5 relative;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
.timeline-step.vertical {
|
|
101
|
-
@apply flex items-start;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
.timeline-step.vertical .timeline-icon-container {
|
|
105
|
-
@apply flex-shrink-0 relative;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
.timeline-step.horizontal .timeline-icon-container {
|
|
109
|
-
@apply relative;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
@keyframes pulse {
|
|
113
|
-
0%, 100% { opacity: 0.3; }
|
|
114
|
-
50% { opacity: 0.1; }
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
.animate-pulse {
|
|
118
|
-
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
119
|
-
}
|
|
120
|
-
</style>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
import FooterLink from "../atoms/FooterLink.astro";
|
|
3
|
+
|
|
4
|
+
export interface Props {
|
|
5
|
+
title: string;
|
|
6
|
+
links: Array<{
|
|
7
|
+
href: string;
|
|
8
|
+
label: string;
|
|
9
|
+
external?: boolean;
|
|
10
|
+
ariaLabel?: string;
|
|
11
|
+
}>;
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { title, links, class: className = "" } = Astro.props;
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<div class={`link-group ${className}`}>
|
|
19
|
+
<h3 class="link-group__title">
|
|
20
|
+
{title}
|
|
21
|
+
</h3>
|
|
22
|
+
|
|
23
|
+
<nav aria-label={`${title} navigation`}>
|
|
24
|
+
<ul class="link-group__list" role="list">
|
|
25
|
+
{
|
|
26
|
+
links.map((link) => (
|
|
27
|
+
<li class="link-group__item">
|
|
28
|
+
<FooterLink
|
|
29
|
+
href={link.href}
|
|
30
|
+
external={link.external || false}
|
|
31
|
+
ariaLabel={link.ariaLabel}
|
|
32
|
+
>
|
|
33
|
+
{link.label}
|
|
34
|
+
</FooterLink>
|
|
35
|
+
</li>
|
|
36
|
+
))
|
|
37
|
+
}
|
|
38
|
+
</ul>
|
|
39
|
+
</nav>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<style>
|
|
43
|
+
.link-group {
|
|
44
|
+
/* Base styles */
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.link-group__title {
|
|
48
|
+
font-family: var(--font-family-manrope);
|
|
49
|
+
font-size: var(--font-size-h6);
|
|
50
|
+
font-weight: var(--font-weight-h6);
|
|
51
|
+
line-height: var(--line-height-h6);
|
|
52
|
+
letter-spacing: var(--letter-spacing-h6);
|
|
53
|
+
color: var(--color-white);
|
|
54
|
+
margin-bottom: 16px;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.link-group__list {
|
|
58
|
+
list-style: none;
|
|
59
|
+
margin: 0;
|
|
60
|
+
padding: 0;
|
|
61
|
+
display: flex;
|
|
62
|
+
flex-direction: column;
|
|
63
|
+
gap: 12px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.link-group__item {
|
|
67
|
+
/* Item styles */
|
|
68
|
+
}
|
|
69
|
+
</style>
|
|
70
|
+
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
import NavLink from "../atoms/NavLink.astro";
|
|
3
|
+
|
|
4
|
+
export interface Props {
|
|
5
|
+
class?: string;
|
|
6
|
+
isMobile?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { class: className = "", isMobile = false } = Astro.props;
|
|
10
|
+
|
|
11
|
+
const navigationItems = [
|
|
12
|
+
{ href: "#atleta", label: "Atleta" },
|
|
13
|
+
{ href: "/us", label: "Nosotros" },
|
|
14
|
+
{ href: "/how-work", label: "Cómo trabajamos" },
|
|
15
|
+
{ href: "/contact", label: "Contacto" },
|
|
16
|
+
];
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
<nav class={`nav-menu ${className}`} aria-label="Navegación principal">
|
|
20
|
+
<ul
|
|
21
|
+
class={`nav-menu__list ${isMobile ? 'nav-menu__list--mobile' : 'nav-menu__list--desktop'}`}
|
|
22
|
+
role="menubar"
|
|
23
|
+
>
|
|
24
|
+
{
|
|
25
|
+
navigationItems.map((item) => (
|
|
26
|
+
<li role="none" class="nav-menu__item">
|
|
27
|
+
<NavLink href={item.href} class={isMobile ? "nav-menu__link--mobile" : ""}>
|
|
28
|
+
{item.label}
|
|
29
|
+
</NavLink>
|
|
30
|
+
</li>
|
|
31
|
+
))
|
|
32
|
+
}
|
|
33
|
+
</ul>
|
|
34
|
+
</nav>
|
|
35
|
+
|
|
36
|
+
<style>
|
|
37
|
+
.nav-menu {
|
|
38
|
+
/* Base styles */
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.nav-menu__list {
|
|
42
|
+
list-style: none;
|
|
43
|
+
margin: 0;
|
|
44
|
+
padding: 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Desktop menu */
|
|
48
|
+
.nav-menu__list--desktop {
|
|
49
|
+
display: none;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@media (min-width: 1024px) {
|
|
53
|
+
.nav-menu__list--desktop {
|
|
54
|
+
display: flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
gap: 32px;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Mobile menu */
|
|
61
|
+
.nav-menu__list--mobile {
|
|
62
|
+
display: flex;
|
|
63
|
+
flex-direction: column;
|
|
64
|
+
gap: 16px;
|
|
65
|
+
padding: 16px 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.nav-menu__item {
|
|
69
|
+
/* Item styles */
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Mobile link full width */
|
|
73
|
+
.nav-menu__list--mobile :global(.nav-menu__link--mobile) {
|
|
74
|
+
display: block;
|
|
75
|
+
width: 100%;
|
|
76
|
+
text-align: left;
|
|
77
|
+
}
|
|
78
|
+
</style>
|
|
79
|
+
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
import SocialIcon from "../atoms/SocialIcon.astro";
|
|
3
|
+
|
|
4
|
+
export interface Props {
|
|
5
|
+
title: string;
|
|
6
|
+
socialLinks: Array<{
|
|
7
|
+
platform: "youtube" | "facebook" | "twitter" | "instagram";
|
|
8
|
+
href: string;
|
|
9
|
+
}>;
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { title, socialLinks, class: className = "" } = Astro.props;
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<div class={`social-group ${className}`}>
|
|
17
|
+
<h3 class="social-group__title">
|
|
18
|
+
{title}
|
|
19
|
+
</h3>
|
|
20
|
+
|
|
21
|
+
<nav aria-label="Redes sociales">
|
|
22
|
+
<ul class="social-group__list" role="list">
|
|
23
|
+
{
|
|
24
|
+
socialLinks.map((social) => (
|
|
25
|
+
<li class="social-group__item">
|
|
26
|
+
<SocialIcon platform={social.platform} href={social.href} />
|
|
27
|
+
</li>
|
|
28
|
+
))
|
|
29
|
+
}
|
|
30
|
+
</ul>
|
|
31
|
+
</nav>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<style>
|
|
35
|
+
.social-group {
|
|
36
|
+
/* Base styles */
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.social-group__title {
|
|
40
|
+
font-family: var(--font-family-manrope);
|
|
41
|
+
font-size: var(--font-size-h6);
|
|
42
|
+
font-weight: var(--font-weight-h6);
|
|
43
|
+
line-height: var(--line-height-h6);
|
|
44
|
+
letter-spacing: var(--letter-spacing-h6);
|
|
45
|
+
color: var(--color-white);
|
|
46
|
+
text-align: center;
|
|
47
|
+
margin-bottom: 16px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.social-group__list {
|
|
51
|
+
list-style: none;
|
|
52
|
+
margin: 0;
|
|
53
|
+
padding: 0;
|
|
54
|
+
display: flex;
|
|
55
|
+
justify-content: center;
|
|
56
|
+
align-items: center;
|
|
57
|
+
gap: 16px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.social-group__item {
|
|
61
|
+
/* Item styles */
|
|
62
|
+
}
|
|
63
|
+
</style>
|
|
64
|
+
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Logo from "../atoms/Logo.astro";
|
|
3
|
+
import LinkGroup from "../molecules/LinkGroup.astro";
|
|
4
|
+
import SocialGroup from "../molecules/SocialGroup.astro";
|
|
5
|
+
|
|
6
|
+
export interface Props {
|
|
7
|
+
class?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { class: className = "" } = Astro.props;
|
|
11
|
+
|
|
12
|
+
// Configuración de enlaces del sitio
|
|
13
|
+
const siteMapLinks = [
|
|
14
|
+
{ href: "#atletas", label: "Atletas" },
|
|
15
|
+
{ href: "#nosotros", label: "Nosotros" },
|
|
16
|
+
{ href: "#como-trabajamos", label: "Cómo trabajamos" },
|
|
17
|
+
{ href: "#contacto", label: "Contacto" },
|
|
18
|
+
{ href: "#ayuda", label: "Ayuda" },
|
|
19
|
+
{ href: "#faq", label: "FAQ" },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
// Configuración de enlaces legales
|
|
23
|
+
const legalLinks = [
|
|
24
|
+
{ href: "/privacy-policy", label: "Políticas de Privacidad" },
|
|
25
|
+
{ href: "/terms-conditions", label: "Términos y Condiciones" },
|
|
26
|
+
{ href: "/cookie-policy", label: "Políticas de Cookies" },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
// Configuración de redes sociales
|
|
30
|
+
const socialLinks = [
|
|
31
|
+
{ platform: "youtube" as const, href: "https://youtube.com/@athlefi" },
|
|
32
|
+
{ platform: "facebook" as const, href: "https://facebook.com/athlefi" },
|
|
33
|
+
{ platform: "twitter" as const, href: "https://twitter.com/athlefi" },
|
|
34
|
+
{ platform: "instagram" as const, href: "https://instagram.com/athlefi" },
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const currentYear = new Date().getFullYear();
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
<footer
|
|
41
|
+
class={`footer ${className}`}
|
|
42
|
+
role="contentinfo"
|
|
43
|
+
aria-label="Información del sitio y enlaces adicionales"
|
|
44
|
+
>
|
|
45
|
+
<div class="footer__container">
|
|
46
|
+
<!-- Secciones principales del footer -->
|
|
47
|
+
<div class="footer__grid">
|
|
48
|
+
<!-- Logo -->
|
|
49
|
+
<div class="footer__logo">
|
|
50
|
+
<div class="footer__logo-wrapper">
|
|
51
|
+
<Logo size="lg" class="footer__logo-image" />
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<!-- Separador mobile -->
|
|
56
|
+
<div class="footer__separator footer__separator--mobile"></div>
|
|
57
|
+
|
|
58
|
+
<!-- Mapa del sitio -->
|
|
59
|
+
<div class="footer__sitemap">
|
|
60
|
+
<LinkGroup title="Mapa del sitio" links={siteMapLinks} />
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<!-- Legales -->
|
|
64
|
+
<div class="footer__legal">
|
|
65
|
+
<LinkGroup title="Legales" links={legalLinks} />
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<!-- Redes sociales -->
|
|
69
|
+
<div class="footer__social">
|
|
70
|
+
<SocialGroup title="Síguenos" socialLinks={socialLinks} />
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<!-- Separador -->
|
|
75
|
+
<div class="footer__separator"></div>
|
|
76
|
+
|
|
77
|
+
<!-- Sección inferior con copyright -->
|
|
78
|
+
<div class="footer__copyright">
|
|
79
|
+
<div class="footer__copyright-content">
|
|
80
|
+
<p class="footer__copyright-text">
|
|
81
|
+
© {currentYear} Athlefi. Todos los derechos reservados.
|
|
82
|
+
</p>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
</footer>
|
|
87
|
+
|
|
88
|
+
<style>
|
|
89
|
+
.footer {
|
|
90
|
+
background-color: var(--color-primary-90);
|
|
91
|
+
border-top: 1px solid rgb(31, 41, 55);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.footer__container {
|
|
95
|
+
max-width: 1280px;
|
|
96
|
+
margin: 0 auto;
|
|
97
|
+
padding: 0 1rem;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@media (min-width: 640px) {
|
|
101
|
+
.footer__container {
|
|
102
|
+
padding: 0 1.5rem;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@media (min-width: 1024px) {
|
|
107
|
+
.footer__container {
|
|
108
|
+
padding: 0 2rem;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Grid Layout */
|
|
113
|
+
.footer__grid {
|
|
114
|
+
display: grid;
|
|
115
|
+
grid-template-columns: 1fr;
|
|
116
|
+
gap: 2rem;
|
|
117
|
+
padding: 3rem 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@media (min-width: 1024px) {
|
|
121
|
+
.footer__grid {
|
|
122
|
+
grid-template-columns: repeat(16, 1fr);
|
|
123
|
+
gap: 1.5rem;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* Logo Section */
|
|
128
|
+
.footer__logo {
|
|
129
|
+
grid-column: 1;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@media (min-width: 1024px) {
|
|
133
|
+
.footer__logo {
|
|
134
|
+
grid-column: 1 / span 4;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.footer__logo-wrapper {
|
|
139
|
+
display: flex;
|
|
140
|
+
flex-direction: column;
|
|
141
|
+
align-items: flex-start;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.footer__logo-image {
|
|
145
|
+
margin-bottom: 1rem;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Sitemap Section */
|
|
149
|
+
.footer__sitemap {
|
|
150
|
+
grid-column: 1;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
@media (min-width: 1024px) {
|
|
154
|
+
.footer__sitemap {
|
|
155
|
+
grid-column: 5 / span 4;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* Legal Section */
|
|
160
|
+
.footer__legal {
|
|
161
|
+
grid-column: 1;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
@media (min-width: 1024px) {
|
|
165
|
+
.footer__legal {
|
|
166
|
+
grid-column: 9 / span 4;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* Social Section */
|
|
171
|
+
.footer__social {
|
|
172
|
+
grid-column: 1;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
@media (min-width: 1024px) {
|
|
176
|
+
.footer__social {
|
|
177
|
+
grid-column: 13 / span 4;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/* Separators */
|
|
182
|
+
.footer__separator {
|
|
183
|
+
border-top: 1px solid var(--color-gray-medium);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.footer__separator--mobile {
|
|
187
|
+
display: block;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
@media (min-width: 1024px) {
|
|
191
|
+
.footer__separator--mobile {
|
|
192
|
+
display: none;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* Copyright Section */
|
|
197
|
+
.footer__copyright {
|
|
198
|
+
padding: 1.5rem 0;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.footer__copyright-content {
|
|
202
|
+
text-align: center;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.footer__copyright-text {
|
|
206
|
+
font-family: var(--font-family-manrope);
|
|
207
|
+
font-size: var(--font-size-body-s);
|
|
208
|
+
font-weight: var(--font-weight-body-s);
|
|
209
|
+
line-height: var(--line-height-body-s);
|
|
210
|
+
letter-spacing: var(--letter-spacing-body-s);
|
|
211
|
+
color: rgb(156, 163, 175);
|
|
212
|
+
}
|
|
213
|
+
</style>
|
|
214
|
+
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Logo from "../atoms/Logo.astro";
|
|
3
|
+
import NavMenu from "../molecules/NavMenu.astro";
|
|
4
|
+
|
|
5
|
+
export interface Props {
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { class: className = "" } = Astro.props;
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<header
|
|
13
|
+
class={`header ${className}`}
|
|
14
|
+
role="banner"
|
|
15
|
+
>
|
|
16
|
+
<div class="header__container">
|
|
17
|
+
<!-- Distribución con grid para centrado perfecto -->
|
|
18
|
+
<div class="header__grid">
|
|
19
|
+
<!-- Logo -->
|
|
20
|
+
<div class="header__logo">
|
|
21
|
+
<Logo />
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<!-- Navegación Desktop -->
|
|
25
|
+
<div class="header__nav-desktop">
|
|
26
|
+
<NavMenu class="header__nav-center" />
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<!-- Botones Auth Desktop - Slot -->
|
|
30
|
+
<div class="header__auth-desktop">
|
|
31
|
+
<slot name="auth-buttons" />
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<!-- Botón Hamburguesa Mobile -->
|
|
35
|
+
<div class="header__mobile-toggle">
|
|
36
|
+
<button
|
|
37
|
+
id="mobile-menu-button"
|
|
38
|
+
type="button"
|
|
39
|
+
class="header__hamburger"
|
|
40
|
+
aria-controls="mobile-menu"
|
|
41
|
+
aria-expanded="false"
|
|
42
|
+
aria-label="Abrir menú de navegación"
|
|
43
|
+
>
|
|
44
|
+
<span class="sr-only">Abrir menú principal</span>
|
|
45
|
+
<!-- Hamburger Icon -->
|
|
46
|
+
<svg
|
|
47
|
+
id="hamburger-icon"
|
|
48
|
+
class="header__icon header__icon--visible"
|
|
49
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
50
|
+
fill="none"
|
|
51
|
+
viewBox="0 0 24 24"
|
|
52
|
+
stroke="currentColor"
|
|
53
|
+
aria-hidden="true"
|
|
54
|
+
>
|
|
55
|
+
<path
|
|
56
|
+
stroke-linecap="round"
|
|
57
|
+
stroke-linejoin="round"
|
|
58
|
+
stroke-width="2"
|
|
59
|
+
d="M4 6h16M4 12h16M4 18h16"></path>
|
|
60
|
+
</svg>
|
|
61
|
+
<!-- Close Icon -->
|
|
62
|
+
<svg
|
|
63
|
+
id="close-icon"
|
|
64
|
+
class="header__icon header__icon--hidden"
|
|
65
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
66
|
+
fill="none"
|
|
67
|
+
viewBox="0 0 24 24"
|
|
68
|
+
stroke="currentColor"
|
|
69
|
+
aria-hidden="true"
|
|
70
|
+
>
|
|
71
|
+
<path
|
|
72
|
+
stroke-linecap="round"
|
|
73
|
+
stroke-linejoin="round"
|
|
74
|
+
stroke-width="2"
|
|
75
|
+
d="M6 18L18 6M6 6l12 12"></path>
|
|
76
|
+
</svg>
|
|
77
|
+
</button>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<!-- Mobile Menu -->
|
|
83
|
+
<div
|
|
84
|
+
id="mobile-menu"
|
|
85
|
+
class="header__mobile-menu header__mobile-menu--hidden"
|
|
86
|
+
role="navigation"
|
|
87
|
+
aria-label="Menú de navegación móvil"
|
|
88
|
+
>
|
|
89
|
+
<div class="header__mobile-content">
|
|
90
|
+
<!-- Mobile Navigation -->
|
|
91
|
+
<NavMenu isMobile={true} />
|
|
92
|
+
|
|
93
|
+
<!-- Mobile Auth Buttons - Slot -->
|
|
94
|
+
<div class="header__mobile-auth">
|
|
95
|
+
<slot name="auth-buttons-mobile">
|
|
96
|
+
<slot name="auth-buttons" />
|
|
97
|
+
</slot>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</header>
|
|
102
|
+
|
|
103
|
+
<style>
|
|
104
|
+
.header {
|
|
105
|
+
background-color: var(--color-primary-90);
|
|
106
|
+
border-bottom: 1px solid var(--color-gray-medium);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.header__container {
|
|
110
|
+
max-width: 1280px;
|
|
111
|
+
margin: 0 auto;
|
|
112
|
+
padding: 0 1rem;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@media (min-width: 640px) {
|
|
116
|
+
.header__container {
|
|
117
|
+
padding: 0 1.5rem;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@media (min-width: 1024px) {
|
|
122
|
+
.header__container {
|
|
123
|
+
padding: 0 2rem;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* Grid Layout */
|
|
128
|
+
.header__grid {
|
|
129
|
+
display: grid;
|
|
130
|
+
grid-template-columns: repeat(4, 1fr);
|
|
131
|
+
align-items: center;
|
|
132
|
+
gap: 1rem;
|
|
133
|
+
padding: 1rem 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@media (min-width: 1024px) {
|
|
137
|
+
.header__grid {
|
|
138
|
+
grid-template-columns: repeat(16, 1fr);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* Logo */
|
|
143
|
+
.header__logo {
|
|
144
|
+
grid-column: span 2;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@media (min-width: 1024px) {
|
|
148
|
+
.header__logo {
|
|
149
|
+
grid-column: 1 / span 3;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* Desktop Navigation */
|
|
154
|
+
.header__nav-desktop {
|
|
155
|
+
display: none;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@media (min-width: 1024px) {
|
|
159
|
+
.header__nav-desktop {
|
|
160
|
+
display: block;
|
|
161
|
+
grid-column: 5 / span 8;
|
|
162
|
+
margin-left: 5rem;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.header__nav-center {
|
|
167
|
+
display: flex;
|
|
168
|
+
justify-content: center;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* Desktop Auth Buttons */
|
|
172
|
+
.header__auth-desktop {
|
|
173
|
+
display: none;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@media (min-width: 1024px) {
|
|
177
|
+
.header__auth-desktop {
|
|
178
|
+
display: block;
|
|
179
|
+
grid-column: 14 / span 3;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* Mobile Toggle */
|
|
184
|
+
.header__mobile-toggle {
|
|
185
|
+
grid-column: span 2;
|
|
186
|
+
display: flex;
|
|
187
|
+
justify-content: flex-end;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
@media (min-width: 1024px) {
|
|
191
|
+
.header__mobile-toggle {
|
|
192
|
+
display: none;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* Hamburger Button */
|
|
197
|
+
.header__hamburger {
|
|
198
|
+
display: inline-flex;
|
|
199
|
+
align-items: center;
|
|
200
|
+
justify-content: center;
|
|
201
|
+
padding: 0.5rem;
|
|
202
|
+
color: var(--color-white);
|
|
203
|
+
background-color: transparent;
|
|
204
|
+
border: none;
|
|
205
|
+
border-radius: 6px;
|
|
206
|
+
cursor: pointer;
|
|
207
|
+
transition: background-color 0.2s ease, color 0.2s ease;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.header__hamburger:hover {
|
|
211
|
+
background-color: rgb(31, 41, 55);
|
|
212
|
+
color: var(--color-primary-60);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.header__hamburger:focus-visible {
|
|
216
|
+
outline: 2px solid var(--color-primary-60);
|
|
217
|
+
outline-offset: 2px;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* Icons */
|
|
221
|
+
.header__icon {
|
|
222
|
+
width: 24px;
|
|
223
|
+
height: 24px;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.header__icon--visible {
|
|
227
|
+
display: block;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.header__icon--hidden {
|
|
231
|
+
display: none;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/* Screen reader only */
|
|
235
|
+
.sr-only {
|
|
236
|
+
position: absolute;
|
|
237
|
+
width: 1px;
|
|
238
|
+
height: 1px;
|
|
239
|
+
padding: 0;
|
|
240
|
+
margin: -1px;
|
|
241
|
+
overflow: hidden;
|
|
242
|
+
clip: rect(0, 0, 0, 0);
|
|
243
|
+
white-space: nowrap;
|
|
244
|
+
border-width: 0;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/* Mobile Menu */
|
|
248
|
+
.header__mobile-menu {
|
|
249
|
+
border-top: 1px solid var(--color-primary-60);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
@media (min-width: 1024px) {
|
|
253
|
+
.header__mobile-menu {
|
|
254
|
+
display: none !important;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.header__mobile-menu--hidden {
|
|
259
|
+
display: none;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.header__mobile-menu--visible {
|
|
263
|
+
display: block;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.header__mobile-content {
|
|
267
|
+
max-width: 1280px;
|
|
268
|
+
margin: 0 auto;
|
|
269
|
+
padding: 1rem;
|
|
270
|
+
display: flex;
|
|
271
|
+
flex-direction: column;
|
|
272
|
+
gap: 1rem;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
@media (min-width: 640px) {
|
|
276
|
+
.header__mobile-content {
|
|
277
|
+
padding: 1rem 1.5rem;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/* Mobile Auth */
|
|
282
|
+
.header__mobile-auth {
|
|
283
|
+
border-top: 1px solid var(--color-primary-60);
|
|
284
|
+
padding-top: 1rem;
|
|
285
|
+
}
|
|
286
|
+
</style>
|
|
287
|
+
|
|
288
|
+
<script>
|
|
289
|
+
// Mobile menu toggle functionality
|
|
290
|
+
document.addEventListener("DOMContentLoaded", function () {
|
|
291
|
+
// Get DOM elements
|
|
292
|
+
const mobileMenuButton = document.getElementById("mobile-menu-button");
|
|
293
|
+
const mobileMenu = document.getElementById("mobile-menu");
|
|
294
|
+
const hamburgerIcon = document.getElementById("hamburger-icon");
|
|
295
|
+
const closeIcon = document.getElementById("close-icon");
|
|
296
|
+
|
|
297
|
+
// Early return if any required element is missing
|
|
298
|
+
if (!mobileMenuButton || !mobileMenu || !hamburgerIcon || !closeIcon) {
|
|
299
|
+
console.warn("Mobile menu: Required DOM elements not found");
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
let isMenuOpen = false;
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Toggles the mobile menu visibility and updates UI states
|
|
307
|
+
*/
|
|
308
|
+
function toggleMenu(): void {
|
|
309
|
+
try {
|
|
310
|
+
isMenuOpen = !isMenuOpen;
|
|
311
|
+
|
|
312
|
+
// Toggle menu visibility
|
|
313
|
+
if (isMenuOpen) {
|
|
314
|
+
mobileMenu!.classList.remove("header__mobile-menu--hidden");
|
|
315
|
+
mobileMenu!.classList.add("header__mobile-menu--visible");
|
|
316
|
+
} else {
|
|
317
|
+
mobileMenu!.classList.remove("header__mobile-menu--visible");
|
|
318
|
+
mobileMenu!.classList.add("header__mobile-menu--hidden");
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Toggle icons visibility
|
|
322
|
+
if (isMenuOpen) {
|
|
323
|
+
hamburgerIcon!.classList.remove("header__icon--visible");
|
|
324
|
+
hamburgerIcon!.classList.add("header__icon--hidden");
|
|
325
|
+
closeIcon!.classList.remove("header__icon--hidden");
|
|
326
|
+
closeIcon!.classList.add("header__icon--visible");
|
|
327
|
+
} else {
|
|
328
|
+
hamburgerIcon!.classList.remove("header__icon--hidden");
|
|
329
|
+
hamburgerIcon!.classList.add("header__icon--visible");
|
|
330
|
+
closeIcon!.classList.remove("header__icon--visible");
|
|
331
|
+
closeIcon!.classList.add("header__icon--hidden");
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Update ARIA attributes for accessibility
|
|
335
|
+
mobileMenuButton!.setAttribute("aria-expanded", isMenuOpen.toString());
|
|
336
|
+
mobileMenuButton!.setAttribute(
|
|
337
|
+
"aria-label",
|
|
338
|
+
isMenuOpen ? "Cerrar menú de navegación" : "Abrir menú de navegación",
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
// Manage body scroll and focus
|
|
342
|
+
if (isMenuOpen) {
|
|
343
|
+
document.body.style.overflow = "hidden";
|
|
344
|
+
// Focus first focusable element in mobile menu for keyboard navigation
|
|
345
|
+
const firstFocusableElement = mobileMenu!.querySelector(
|
|
346
|
+
'a, button, input, [tabindex]:not([tabindex="-1"])',
|
|
347
|
+
) as HTMLElement;
|
|
348
|
+
firstFocusableElement?.focus();
|
|
349
|
+
} else {
|
|
350
|
+
document.body.style.overflow = "";
|
|
351
|
+
// Return focus to menu button when closing
|
|
352
|
+
mobileMenuButton!.focus();
|
|
353
|
+
}
|
|
354
|
+
} catch (error) {
|
|
355
|
+
console.error("Error toggling mobile menu:", error);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Closes the menu if it's currently open
|
|
361
|
+
*/
|
|
362
|
+
function closeMenu(): void {
|
|
363
|
+
if (isMenuOpen) {
|
|
364
|
+
toggleMenu();
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Event Listeners
|
|
369
|
+
|
|
370
|
+
// Toggle menu on button click
|
|
371
|
+
mobileMenuButton.addEventListener("click", toggleMenu);
|
|
372
|
+
|
|
373
|
+
// Close menu when clicking outside
|
|
374
|
+
document.addEventListener("click", function (event: Event) {
|
|
375
|
+
const target = event.target as Node;
|
|
376
|
+
if (
|
|
377
|
+
isMenuOpen &&
|
|
378
|
+
!mobileMenu!.contains(target) &&
|
|
379
|
+
!mobileMenuButton!.contains(target)
|
|
380
|
+
) {
|
|
381
|
+
closeMenu();
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// Close menu on escape key
|
|
386
|
+
document.addEventListener("keydown", function (event: KeyboardEvent) {
|
|
387
|
+
if (event.key === "Escape" && isMenuOpen) {
|
|
388
|
+
closeMenu();
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// Close menu when window is resized to desktop size
|
|
393
|
+
let resizeTimeout: number;
|
|
394
|
+
window.addEventListener("resize", function () {
|
|
395
|
+
clearTimeout(resizeTimeout);
|
|
396
|
+
resizeTimeout = window.setTimeout(() => {
|
|
397
|
+
if (window.innerWidth >= 1024 && isMenuOpen) {
|
|
398
|
+
closeMenu();
|
|
399
|
+
}
|
|
400
|
+
}, 150);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// Close menu on page navigation (for SPAs)
|
|
404
|
+
window.addEventListener("beforeunload", closeMenu);
|
|
405
|
+
});
|
|
406
|
+
</script>
|
|
407
|
+
|
package/src/index.ts
CHANGED
|
@@ -21,14 +21,17 @@ export { default as TimelineStep } from "./components/atoms/TimelineStep.astro";
|
|
|
21
21
|
// ============================================
|
|
22
22
|
// MOLÉCULAS
|
|
23
23
|
// ============================================
|
|
24
|
+
export { default as LinkGroup } from "./components/molecules/LinkGroup.astro";
|
|
25
|
+
export { default as NavMenu } from "./components/molecules/NavMenu.astro";
|
|
26
|
+
export { default as SocialGroup } from "./components/molecules/SocialGroup.astro";
|
|
24
27
|
// export { default as SearchBar } from "./components/molecules/SearchBar.astro";
|
|
25
28
|
// export { default as Card } from "./components/molecules/Card.astro";
|
|
26
29
|
|
|
27
30
|
// ============================================
|
|
28
31
|
// ORGANISMOS
|
|
29
32
|
// ============================================
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
export { default as Header } from "./components/organisms/Header.astro";
|
|
34
|
+
export { default as Footer } from "./components/organisms/Footer.astro";
|
|
32
35
|
|
|
33
36
|
// ============================================
|
|
34
37
|
// TEMPLATES
|