layout · v0.9.0

BentoCard

Card individual del layout bento. Hija de BentoGrid (CSS Grid). Cierra el círculo con v0.8.0 consumiendo useIntersectionObserver para entry animation con semántica triggerOnce. Respeta prefers-reduced-motion vía CSS @media.

Demo

size = "sm" span 3×1

Contenido del bloque. Stat compacto, KPI o badge.

size = "md" span 4×1

Contenido del bloque. Section secundaria.

size = "lg" span 6×2

Contenido del bloque. Project showcase, gallery.

size = "xl" span 8×2

Contenido del bloque. Hero, About largo, o feature destacada.

Pulsa Reset grid para volver a montar las cards y ver la entry animation desde cero. Activa el toggle keyboardHint para ver los badges Alt+N en cada esquina.

Uso

page.svelte
<script>
  import { BentoGrid, BentoCard } from '@codigosinsiesta/theme/layout';
</script>

<BentoGrid cols={12} gap="20px" dense>
  <BentoCard size="xl" ariaLabel="Hero" id="hero" keyboardHint="Alt+1">
    <h1>Technical Leader Specialist</h1>
    <p>Bio del autor...</p>
  </BentoCard>

  <BentoCard size="md" ariaLabel="Stats">
    <span class="big-number">42</span>
  </BentoCard>
</BentoGrid>

Tamaños

Cada size mapea a un span de grid (asumiendo BentoGrid con 12 cols):

sizegrid-columngrid-rowUso típico
smspan 3span 1Stat compacto, KPI, badge.
mdspan 4span 1Section secundaria, callout.
lgspan 6span 2Project showcase, gallery.
xlspan 8span 2Hero, feature destacada, about largo.

Responsive cascade: a ≤ 768px las cards lg/xl bajan a span 4 × 1; sm/md a span 2. A ≤ 480px todas ocupan el ancho completo del grid colapsado (span 2). Coordinado con los breakpoints de BentoGrid (12 → 4 → 2 cols).

Entry animation

Con entryAnimation={true} (default), la card empieza con opacity: 0 + translateY(20px) y transiciona a opacity: 1 + none en 600ms cuando entra en viewport. La detección usa useIntersectionObserver con threshold: 0.2 y triggerOnce: true — tras la primera intersección, el observer se desconecta.

Reduced motion: si el usuario tiene prefers-reduced-motion: reduce activo, una @media rule del CSS fuerza opacity: 1 + transition: none. La card aparece sin animar. El composable sigue corriendo pero su efecto visual queda neutralizado — diseño deliberado para no requerir lógica JS extra.

Accesibilidad

  • ariaLabel obligatorio: cada card es un landmark <article>. El label da contexto a screen readers cuando navegan por landmarks.
  • keyboardHint renderiza un <kbd> visible. Es solo decorativo (aria-hidden); el atajo real lo registra el consumer con useKeyboardNav apuntando al id de la card.
  • Entry animation: respeta prefers-reduced-motion sin código extra (CSS-only).

API

PropTipoDefaultDescripción
size'sm' | 'md' | 'lg' | 'xl''md'Mapea a grid-column/grid-row span.
ariaLabelstringObligatorio. Label del landmark <article>.
keyboardHintstring''Texto del badge <kbd>. Si vacío, no se renderiza.
entryAnimationbooleantrueActiva la entry animation on-viewport.
idstring''id HTML para coordinar con useKeyboardNav (ej. scrollTo('#hero')).
childrenSnippetContenido de la card.

Casos de uso

  • Portfolio CsS: cada bloque del bento (Hero, About, Skills, Projects, Contact...) con sizes mixtos.
  • Landings de talleres: feature cards en grid asimétrico.
  • Dashboards: KPI cards individuales con entry animation al scroll.