Components (registry)Layout
Navbar Mega
Navbar con mega-dropdowns morficos: paneles multi-columna, quick links y card promo. El motion vive en mega-nav-animation (initMegaNav); sin el behavior los paneles quedan cerrados.
Contrato del layout
Un layout transporta anatomia, no pintura: se rellena por slots y depende del CSS de los componentes que lista. Instalarlo sin ellos lo deja sin estilos.
- Slots
{{group1_label}}{{group2_label}}{{link1_url}}{{link1_label}}{{g1c1_label}}{{g1c1_l1_url}}{{g1c1_l1_title}}{{g1c1_l1_desc}}{{g1c1_l2_url}}{{g1c1_l2_title}}{{g1c1_l2_desc}}{{g1c2_label}}{{g1c2_l1_url}}{{g1c2_l1_title}}{{g1c2_l1_desc}}{{g1c2_l2_url}}{{g1c2_l2_title}}{{g1c2_l2_desc}}{{g1c3_label}}{{g1c3_l1_url}}{{g1c3_l1_title}}{{g1c3_l2_url}}{{g1c3_l2_title}}{{g2c1_label}}{{g2c1_l1_url}}{{g2c1_l1_title}}{{g2c1_l1_desc}}{{g2c1_l2_url}}{{g2c1_l2_title}}{{g2c1_l2_desc}}{{card_img}}{{card_alt}}{{card_title}}{{card_desc}}{{card_url}}{{card_cta}}- Componentes requeridos
logonav-linkbuttonburger-iconitemdropdown-menuimage
Anatomía CSS
<div class="layoutNavbarMega"> </div>
Tokens resueltos
Valores finales tras seguir la cadena de tokens. Derivados del source: si un token cambia, esta tabla cambia sola.
| Variante | Prop | Valor | Token |
|---|---|---|---|
| all | bg | #fafafa | background |
| all | border | 1px | stroke.hairline |
| all | fg | #0a0a0a | foreground |
CSS mínimo funcional
Autocontenido: sin imports ni tokens. Para previews y prototipos — en producción se consume el CSS del DS.
/* Layout: navbar-mega — structure only.
Misma pildora sticky de navbar-simple (radio 0.375em, hairline, controles
2.5em). Los paneles nacen ocultos (estado estructural); TODO el motion vive
en el behavior mega-nav-animation via data-attrs — este css NO anima.
--l-nm-bar: alto de la barra, referencia para anclar el drop movil. */
.l-navbar-mega {
--l-nm-bar: 3.375em;
position: sticky;
top: 20px;
margin-block-start: 20px;
z-index: 30;
width: calc(100% - 2 * var(--container-padding));
max-width: var(--size-container-content);
margin-inline: auto;
}
.l-navbar-mega__bar {
position: relative;
z-index: 3;
background-color: #fafafa;
border: 1px solid #e5e5e5;
border-radius: 0.375em;
}
.l-navbar-mega__row {
position: relative;
display: flex;
align-items: center;
gap: 16px;
min-height: var(--l-nm-bar);
padding: 0.4375em;
}
.l-navbar-mega__logo {
flex-shrink: 0;
display: flex;
align-items: center;
height: 2.5em;
padding-inline: 0.5em;
margin-inline-end: 24px;
}
.l-navbar-mega__inner {
display: flex;
align-items: center;
width: 100%;
gap: 16px;
}
.l-navbar-mega__list {
display: flex;
align-items: center;
flex: 1;
gap: 20px;
list-style: none;
margin: 0;
padding: 0;
}
/* El CTA es el ultimo item de la lista; auto lo empuja al extremo derecho sin
sacarlo de la estructura. */
.l-navbar-mega__list-item--cta {
margin-inline-start: auto;
flex-shrink: 0;
}
.l-navbar-mega__burger {
display: none;
width: 2.5rem;
height: 2.5rem;
padding: 8px;
background: none;
border: 0;
color: #0a0a0a;
cursor: pointer;
}
.l-navbar-mega__back {
position: absolute;
left: 0.5em;
z-index: 2;
opacity: 0;
visibility: hidden;
}
/* ---- Dropdown (desktop): clip + fondo + paneles apilados ----
AUTO-CONTENIDO: el clip no ocupa el ancho de la barra; el behavior le fija
width/x medidos del panel para que quede bajo su trigger y del tamano de su
contenido. El wrapper SI cubre la barra completa a proposito: es el puente de
hover entre el trigger y el panel (sin el, cruzar el hueco cierra el menu). */
.l-navbar-mega__drop {
position: absolute;
top: 100%;
left: 0;
right: 0;
z-index: 2;
pointer-events: none;
}
.l-navbar-mega__drop-clip {
position: absolute;
top: 0.375em;
left: 0;
width: 0;
overflow: hidden;
}
.l-navbar-mega__drop-bg {
position: absolute;
inset: 0;
background-color: #ffffff;
border: 1px solid #e5e5e5;
border-radius: 0.375em;
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
}
.l-navbar-mega__panel {
position: absolute;
top: 0;
left: 0;
/* max-content: el panel se mide por su contenido, no por el clip — que es
justo lo que el behavior esta animando. Sin esto la medicion es circular. */
width: max-content;
overflow: hidden;
opacity: 0;
visibility: hidden;
pointer-events: none;
}
/* Helper de Designer: forzar un panel visible para maquetar sin el behavior */
.l-navbar-mega__panel[data-panel-state="active"] {
position: static;
opacity: 1;
visibility: visible;
pointer-events: auto;
}
.l-navbar-mega__panel-row {
display: flex;
}
.l-navbar-mega__col {
display: flex;
flex-direction: column;
align-items: stretch;
/* auto, no flex:1: las columnas se dimensionan por su contenido para que el
panel tenga un ancho natural. max-width acota descripciones largas. */
flex: 0 0 auto;
min-width: 12em;
max-width: 18em;
gap: 16px;
padding: 24px 16px;
border-right: 1px solid #e5e5e5;
}
.l-navbar-mega__col:last-of-type {
border-right: 0;
}
.l-navbar-mega__col--tinted {
background-color: #f5f5f5;
}
.l-navbar-mega__col--card {
padding: 16px;
}
.l-navbar-mega__links {
display: flex;
flex-direction: column;
list-style: none;
margin: 0;
padding: 0;
}
.l-navbar-mega__card {
display: flex;
flex-direction: column;
overflow: hidden;
width: 100%;
background-color: #ffffff;
border: 1px solid #e5e5e5;
border-radius: 12px;
}
.l-navbar-mega__card-media {
width: 100%;
height: 10em;
}
.l-navbar-mega__card-media img {
width: 100%;
height: 100%;
object-fit: cover;
}
.l-navbar-mega__card-body {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 16px;
padding: 20px;
}
/* ---- Movil (<=991px, borde tablet de scaling.css; el behavior usa el mismo corte) ---- */
@media (max-width: 991px) {
.l-navbar-mega {
top: 0;
width: 100%;
max-width: none;
margin-block-start: 0;
}
.l-navbar-mega__bar {
border-radius: 0;
border-inline: 0;
border-top: 0;
}
.l-navbar-mega__inner {
position: fixed;
inset: var(--l-nm-bar) 0 0 0;
flex-direction: column;
align-items: stretch;
justify-content: space-between;
gap: 32px;
padding: 32px 24px;
background-color: #fafafa;
overflow: auto;
opacity: 0;
visibility: hidden;
}
/* Helper de Designer: forzar la lista movil visible para maquetar */
.l-navbar-mega__inner[data-mobile-nav="active"] {
opacity: 1;
visibility: visible;
}
.l-navbar-mega__list {
flex-direction: column;
align-items: stretch;
gap: 20px;
}
.l-navbar-mega__list-item--cta {
margin-inline-start: 0;
margin-block-start: 20px;
}
.l-navbar-mega__burger {
/* flex = display propio del atomo burger-icon; con block sus lineas colapsan a 0x0 */
display: flex;
}
.l-navbar-mega__drop {
position: fixed;
inset: var(--l-nm-bar) 0 0 0;
z-index: 4;
}
/* Movil: el drop vuelve a ser full-screen; el behavior limpia el width/x
inline que puso en desktop. */
.l-navbar-mega__drop-clip {
position: static;
width: auto;
height: 100%;
overflow: auto;
}
.l-navbar-mega__drop-bg {
display: none;
}
.l-navbar-mega__panel {
inset: 0;
width: auto;
background-color: #fafafa;
overflow: auto;
}
.l-navbar-mega__col {
max-width: none;
}
.l-navbar-mega__panel-row {
flex-direction: column;
}
.l-navbar-mega__col {
border-right: 0;
border-bottom: 1px solid #e5e5e5;
padding: 24px 16px;
}
}
Codigo fuente
layouts/navbar-mega.css
/* Layout: navbar-mega — structure only. Misma pildora sticky de navbar-simple (radio 0.375em, hairline, controles 2.5em). Los paneles nacen ocultos (estado estructural); TODO el motion vive en el behavior mega-nav-animation via data-attrs — este css NO anima. --l-nm-bar: alto de la barra, referencia para anclar el drop movil. */ .l-navbar-mega { --l-nm-bar: 3.375em; position: sticky; top: var(--spacing-5); margin-block-start: var(--spacing-5); z-index: var(--z-index-30); width: calc(100% - 2 * var(--container-padding)); max-width: var(--size-container-content); margin-inline: auto; } .l-navbar-mega__bar { position: relative; z-index: 3; background-color: var(--background); border: var(--stroke-hairline) solid var(--border); border-radius: 0.375em; } .l-navbar-mega__row { position: relative; display: flex; align-items: center; gap: var(--spacing-4); min-height: var(--l-nm-bar); padding: 0.4375em; } .l-navbar-mega__logo { flex-shrink: 0; display: flex; align-items: center; height: 2.5em; padding-inline: 0.5em; margin-inline-end: var(--spacing-6); } .l-navbar-mega__inner { display: flex; align-items: center; width: 100%; gap: var(--spacing-4); } .l-navbar-mega__list { display: flex; align-items: center; flex: 1; gap: var(--spacing-5); list-style: none; margin: 0; padding: 0; } /* El CTA es el ultimo item de la lista; auto lo empuja al extremo derecho sin sacarlo de la estructura. */ .l-navbar-mega__list-item--cta { margin-inline-start: auto; flex-shrink: 0; } .l-navbar-mega__burger { display: none; width: 2.5rem; height: 2.5rem; padding: var(--spacing-2); background: none; border: 0; color: var(--foreground); cursor: pointer; } .l-navbar-mega__back { position: absolute; left: 0.5em; z-index: 2; opacity: 0; visibility: hidden; } /* ---- Dropdown (desktop): clip + fondo + paneles apilados ---- AUTO-CONTENIDO: el clip no ocupa el ancho de la barra; el behavior le fija width/x medidos del panel para que quede bajo su trigger y del tamano de su contenido. El wrapper SI cubre la barra completa a proposito: es el puente de hover entre el trigger y el panel (sin el, cruzar el hueco cierra el menu). */ .l-navbar-mega__drop { position: absolute; top: 100%; left: 0; right: 0; z-index: 2; pointer-events: none; } .l-navbar-mega__drop-clip { position: absolute; top: 0.375em; left: 0; width: 0; overflow: hidden; } .l-navbar-mega__drop-bg { position: absolute; inset: 0; background-color: var(--popover); border: var(--stroke-hairline) solid var(--border); border-radius: 0.375em; box-shadow: var(--shadow-lg); } .l-navbar-mega__panel { position: absolute; top: 0; left: 0; /* max-content: el panel se mide por su contenido, no por el clip — que es justo lo que el behavior esta animando. Sin esto la medicion es circular. */ width: max-content; overflow: hidden; opacity: 0; visibility: hidden; pointer-events: none; } /* Helper de Designer: forzar un panel visible para maquetar sin el behavior */ .l-navbar-mega__panel[data-panel-state="active"] { position: static; opacity: 1; visibility: visible; pointer-events: auto; } .l-navbar-mega__panel-row { display: flex; } .l-navbar-mega__col { display: flex; flex-direction: column; align-items: stretch; /* auto, no flex:1: las columnas se dimensionan por su contenido para que el panel tenga un ancho natural. max-width acota descripciones largas. */ flex: 0 0 auto; min-width: 12em; max-width: 18em; gap: var(--spacing-4); padding: var(--spacing-6) var(--spacing-4); border-right: var(--stroke-hairline) solid var(--border); } .l-navbar-mega__col:last-of-type { border-right: 0; } .l-navbar-mega__col--tinted { background-color: var(--muted); } .l-navbar-mega__col--card { padding: var(--spacing-4); } .l-navbar-mega__links { display: flex; flex-direction: column; list-style: none; margin: 0; padding: 0; } .l-navbar-mega__card { display: flex; flex-direction: column; overflow: hidden; width: 100%; background-color: var(--card); border: var(--stroke-hairline) solid var(--border); border-radius: var(--radius-lg); } .l-navbar-mega__card-media { width: 100%; height: 10em; } .l-navbar-mega__card-media img { width: 100%; height: 100%; object-fit: cover; } .l-navbar-mega__card-body { display: flex; flex-direction: column; align-items: flex-start; gap: var(--spacing-4); padding: var(--spacing-5); } /* ---- Movil (<=991px, borde tablet de scaling.css; el behavior usa el mismo corte) ---- */ @media (max-width: 991px) { .l-navbar-mega { top: 0; width: 100%; max-width: none; margin-block-start: 0; } .l-navbar-mega__bar { border-radius: 0; border-inline: 0; border-top: 0; } .l-navbar-mega__inner { position: fixed; inset: var(--l-nm-bar) 0 0 0; flex-direction: column; align-items: stretch; justify-content: space-between; gap: var(--spacing-8); padding: var(--spacing-8) var(--spacing-6); background-color: var(--background); overflow: auto; opacity: 0; visibility: hidden; } /* Helper de Designer: forzar la lista movil visible para maquetar */ .l-navbar-mega__inner[data-mobile-nav="active"] { opacity: 1; visibility: visible; } .l-navbar-mega__list { flex-direction: column; align-items: stretch; gap: var(--spacing-5); } .l-navbar-mega__list-item--cta { margin-inline-start: 0; margin-block-start: var(--spacing-5); } .l-navbar-mega__burger { /* flex = display propio del atomo burger-icon; con block sus lineas colapsan a 0x0 */ display: flex; } .l-navbar-mega__drop { position: fixed; inset: var(--l-nm-bar) 0 0 0; z-index: 4; } /* Movil: el drop vuelve a ser full-screen; el behavior limpia el width/x inline que puso en desktop. */ .l-navbar-mega__drop-clip { position: static; width: auto; height: 100%; overflow: auto; } .l-navbar-mega__drop-bg { display: none; } .l-navbar-mega__panel { inset: 0; width: auto; background-color: var(--background); overflow: auto; } .l-navbar-mega__col { max-width: none; } .l-navbar-mega__panel-row { flex-direction: column; } .l-navbar-mega__col { border-right: 0; border-bottom: var(--stroke-hairline) solid var(--border); padding: var(--spacing-6) var(--spacing-4); } }