Cómo crear una sección about en split con foto en React
Una sección about en split en React coloca una imagen vertical a la izquierda y el texto de misión y visión etiquetado a la derecha dentro de un grid CSS de dos columnas. El whileInView de Framer Motion activa una animación de opacidad y deslizamiento horizontal en cada columna a medida que la sección entra en el viewport.
- Stack: React + Framer Motion + lucide-react, ~173 líneas, cero dependencias adicionales.
- Animación: whileInView con once:true, duración de 0,6 s, curva de Bézier personalizada [0.16, 1, 0.3, 1], desfase de 0,1 s entre las columnas.
- Bloque de imagen: relación de aspecto 4/5 fija, esquinas redondeadas mediante un token CSS, fondo con el color de acento como placeholder.
- Accesible: jerarquía semántica h2 + h3, ancla estándar para el CTA, sin trucos de ARIA.
- Salvedad responsive: el grid de dos columnas está en estilos inline; añade una media query o un breakpoint de Tailwind para apilar las columnas en móvil.
About Split Photo es una sección React que combina una gran imagen vertical con un texto conciso de misión y visión en una disposición de dos columnas. Ancla la credibilidad de una marca o un equipo por encima de la línea de flotación de cualquier página about, dejando que la imagen haga la mitad del trabajo narrativo mientras el texto se mantiene fácil de escanear.
Anatomía
Una sección a todo el ancho envuelve un contenedor centrado limitado a `--container-max-width`. Dentro, un grid CSS de dos columnas `1fr 1fr` con 4rem de gap contiene dos bloques animados. El bloque izquierdo es un motion.div con una relación de aspecto 4/5 fija, esquinas redondeadas mediante `--radius-lg` y un fondo con el color de acento como placeholder de imagen. El bloque derecho apila un h2 con un acento en cursiva serif, dos subsecciones etiquetadas (Mission, Vision) con títulos en mayúsculas con tracking, y un enlace CTA en forma de píldora con un icono ArrowRight.
Cómo funciona
Cada columna es un `motion.div` que arranca desplazado: la foto comienza en `{ opacity: 0, x: -24 }` y el texto en `{ opacity: 0, x: 24 }`. Ambos se animan hacia `{ opacity: 1, x: 0 }` al entrar en el viewport (`whileInView`, `viewport={{ once: true }}`). La curva de Bézier personalizada `[0.16, 1, 0.3, 1]` da al movimiento un arranque enérgico y un aterrizaje suave, de modo que las columnas parecen encajar en su sitio. Un retardo de 0,1 s en la columna de texto crea un efecto de desfase sin ninguna utilidad de orquestación de terceros.
Cómo crearlo en React
Configurar el grid de dos columnas
Envuelve el contenido de la sección en un div con `display: grid`, `gridTemplateColumns: '1fr 1fr'`, `gap: '4rem'` y `alignItems: 'center'`. Usa tokens CSS para el padding y el ancho máximo para que la disposición se adapte a cada preset de tema sin overrides adicionales.
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "4rem", alignItems: "center", maxWidth: "var(--container-max-width)", padding: "0 var(--container-padding-x)", margin: "0 auto", }}>Animar la columna de la foto al entrar en el viewport
Reemplaza el div de la foto por un `motion.div`. Define `initial={{ opacity: 0, x: -24 }}` y `whileInView={{ opacity: 1, x: 0 }}` con `viewport={{ once: true }}`. Usa el array de ease personalizado para un resultado ágil y natural. Fija la relación de aspecto a 4/5 para que el bloque se mantenga proporcional con cualquier ancho de columna.
const EASE = [0.16, 1, 0.3, 1] as const; <motion.div initial={{ opacity: 0, x: -24 }} whileInView={{ opacity: 1, x: 0 }} viewport={{ once: true }} transition={{ duration: 0.6, ease: EASE }} style={{ aspectRatio: "4/5", borderRadius: "var(--radius-lg)" }} />Desfasar la columna de texto
Envuelve el contenido de texto en un segundo `motion.div` con `initial={{ opacity: 0, x: 24 }}` (se desliza desde la derecha) y un `delay: 0.1` en la transición. Los 100 ms de offset bastan para leerse como un desfase intencional sin hacer esperar al usuario.
<motion.div initial={{ opacity: 0, x: 24 }} whileInView={{ opacity: 1, x: 0 }} viewport={{ once: true }} transition={{ duration: 0.6, delay: 0.1, ease: EASE }} />Estructurar el texto de misión y visión
Da a cada subsección un h3 en mayúsculas con tracking (font-size 0,75 rem, letter-spacing 0,1 em, color de acento) como etiqueta, y luego un párrafo para el cuerpo del texto. Mantén el h2 por encima y usa un span serif en cursiva para la parte acentuada del título. Termina con un enlace CTA en forma de píldora.
Cuándo usarlo
Recurre a esta disposición en páginas about de agencias, estudios o SaaS donde una imagen potente ancla la historia de la marca. Funciona igual de bien a media página como interludio entre bloques de funcionalidades que en la parte superior de una ruta /about dedicada. Evítala cuando la imagen no esté disponible o sea puramente decorativa, una sección about solo de texto evita la incomodidad del placeholder. En móvil, apila siempre las columnas en vertical para que la foto no quede aplastada en una franja estrecha.
Usado por
- Stripe, Usa un split foto/texto amplio en su página About para anclar el liderazgo y la cultura de empresa.
- Notion, Combina fotografía editorial con un texto centrado en la misión en una disposición de dos columnas en su página about.
- Loom, Disposición en split con la foto del equipo a la izquierda y los valores a la derecha, un análogo directo de este patrón.
FAQ
¿Cómo apilo las dos columnas en móvil?
El grid se define mediante estilos inline, así que añade una media query CSS o una clase responsive de Tailwind para cambiar `gridTemplateColumns` a `1fr` por debajo del breakpoint `md`. Una forma rápida es reemplazar el estilo inline por una clase de Tailwind como `grid-cols-1 md:grid-cols-2`.
¿Puedo reemplazar el placeholder por una imagen real?
Sí. Sustituye el div con color por un `<Image>` de Next.js o un `<img>` normal con `object-fit: cover` y `width: 100%; height: 100%` dentro del motion.div. Mantén `aspectRatio: '4/5'` en el contenedor para evitar cualquier salto en la disposición.
¿Por qué la animación se dispara solo una vez?
`viewport={{ once: true }}` le indica a Framer Motion que dispare whileInView una sola vez y deje el elemento en su estado animado. Quita esa opción si quieres que las columnas se reinicien y se vuelvan a animar cada vez que el usuario pasa por delante con el scroll.
¿Cómo cambio el estilo del CTA de relleno a contorno?
Quita `background: 'var(--color-accent)'` del enlace y añade `border: '2px solid var(--color-accent)'` junto con `color: 'var(--color-accent)'`. Ajusta el estado hover en consecuencia mediante una clase CSS o el whileHover de Framer Motion.