diff --git a/components/primitives/IlustracionConvergencia.tsx b/components/primitives/IlustracionConvergencia.tsx index 16a12ee..c88ea4f 100644 --- a/components/primitives/IlustracionConvergencia.tsx +++ b/components/primitives/IlustracionConvergencia.tsx @@ -6,6 +6,11 @@ import { useEnVista } from '@/lib/motion'; // Three strands — comunicación, digital, experiencias — meeting and leaving as // a single gold line. The section's own lead, drawn: "No son la promesa, son // los medios." The three are grey; only what they produce is gold. +// +// `activa` lights the strand belonging to the discipline the reader is pointing +// at, and dims the other two. Until then the drawing sat beside the three cards +// with no relationship to them; now the card and its strand are visibly the +// same thing, and the reader can see which of the three produces what. const HEBRAS = [ 'M6 18 C 60 20, 90 52, 120 60', 'M6 60 C 60 60, 90 60, 120 60', @@ -13,15 +18,28 @@ const HEBRAS = [ ]; const ESTRATEGIA = 'M120 60 L 214 60'; -export function IlustracionConvergencia({ className = '' }: { className?: string }) { +export function IlustracionConvergencia({ + className = '', + activa = null, +}: { + className?: string; + activa?: number | null; +}) { const { ref, enVista, reduced } = useEnVista(); + // `stroke` follows `currentColor`, so moving the text colour moves the line — + // and transition-colors carries it across instead of snapping. + const tono = (i: number) => + activa === null ? 'text-gris-medio' : activa === i ? 'text-dorado' : 'text-gris-medio/35'; + const grosor = (i: number) => (activa === i ? 3.5 : 2); + const transicion = 'transition-all duration-300 ease-out motion-reduce:transition-none'; + // Plain under reduced motion — motion.path would hide the drawing. if (reduced) { return ( @@ -37,7 +55,7 @@ export function IlustracionConvergencia({ className = '' }: { className?: string return ( diff --git a/components/primitives/__tests__/IlustracionConvergencia.test.tsx b/components/primitives/__tests__/IlustracionConvergencia.test.tsx index 700ff46..3c5bb5e 100644 --- a/components/primitives/__tests__/IlustracionConvergencia.test.tsx +++ b/components/primitives/__tests__/IlustracionConvergencia.test.tsx @@ -21,4 +21,29 @@ describe('', () => { expect(anchos.length).toBeGreaterThan(0); anchos.forEach((a) => expect(['1.5', '2', '3']).toContain(a)); }); -}); + + it('lights one strand and dims the rest when a discipline is pointed at', () => { + const { container } = render(); + const hebras = Array.from(container.querySelectorAll('[data-hebra]')); + expect(hebras).toHaveLength(3); + // The point of the highlight is the pairing: the reader has to be able to + // tell WHICH strand belongs to the card under the cursor. One gold, two + // faded — if all three stayed grey the drawing would still be decoration. + const doradas = hebras.filter((h) => (h.getAttribute('class') ?? '').includes('text-dorado')); + expect(doradas).toHaveLength(1); + expect(doradas[0].getAttribute('data-hebra')).toBe('1'); + hebras + .filter((h) => h.getAttribute('data-hebra') !== '1') + .forEach((h) => expect(h.getAttribute('class') ?? '').toMatch(/text-gris-medio\/35/)); + }); + + it('keeps the three strands equal when nothing is pointed at', () => { + const { container } = render(); + const hebras = Array.from(container.querySelectorAll('[data-hebra]')); + hebras.forEach((h) => { + const cls = h.getAttribute('class') ?? ''; + expect(cls).toContain('text-gris-medio'); + expect(cls).not.toContain('text-dorado'); + }); + }); +}); \ No newline at end of file diff --git a/components/sections/Disciplinas.tsx b/components/sections/Disciplinas.tsx index 9afca53..a20cf57 100644 --- a/components/sections/Disciplinas.tsx +++ b/components/sections/Disciplinas.tsx @@ -1,4 +1,5 @@ 'use client'; +import { useState } from 'react'; import { motion } from 'framer-motion'; import { Container } from '@/components/primitives/Container'; import { TarjetaSpotlight } from '@/components/primitives/TarjetaSpotlight'; @@ -11,6 +12,11 @@ const ICONOS: IconoNombre[] = ['comunicacion', 'digital', 'experiencias']; export function Disciplinas() { const reduced = usePrefersReducedMotion(); + // Which discipline the reader is pointing at, so its strand lights up in the + // drawing above. A pointer-only enhancement on purpose: it reveals nothing — + // all three cards and all three strands are fully there without it — so + // nobody loses anything by not having a mouse. + const [senalada, setSenalada] = useState(null); return (
@@ -21,12 +27,14 @@ export function Disciplinas() {

{disciplinas.lead}

- +
{disciplinas.items.map((item, i) => ( setSenalada(i)} + onPointerLeave={() => setSenalada(null)} className="transition-transform duration-300 ease-out hover:-translate-y-1.5 motion-reduce:transform-none motion-reduce:transition-none">
diff --git a/components/sections/__tests__/Disciplinas.test.tsx b/components/sections/__tests__/Disciplinas.test.tsx index b5b29af..905d291 100644 --- a/components/sections/__tests__/Disciplinas.test.tsx +++ b/components/sections/__tests__/Disciplinas.test.tsx @@ -1,4 +1,4 @@ -import { render, screen } from '@testing-library/react'; +import { render, screen, fireEvent } from '@testing-library/react'; import { describe, it, expect } from 'vitest'; import { Disciplinas } from '../Disciplinas'; @@ -16,4 +16,26 @@ describe('', () => { const items = list?.querySelectorAll('li'); expect(items?.length).toBe(3); }); -}); + + it('lights the strand of the discipline being pointed at', () => { + const { container } = render(); + const tarjetas = container.querySelectorAll('ul > li'); + expect(tarjetas).toHaveLength(3); + + // Before anything is pointed at, the drawing is neutral. + const hebrasDoradas = () => + Array.from(container.querySelectorAll('[data-hebra]')).filter((h) => + (h.getAttribute('class') ?? '').includes('text-dorado'), + ); + expect(hebrasDoradas()).toHaveLength(0); + + // Pointing at "Digital", the second card, has to light the second strand + // and nothing else. This wiring is the whole change: without it the three + // cards and the drawing beside them are unrelated objects on a page. + fireEvent.pointerEnter(tarjetas[1]); + expect(hebrasDoradas().map((h) => h.getAttribute('data-hebra'))).toEqual(['1']); + + fireEvent.pointerLeave(tarjetas[1]); + expect(hebrasDoradas()).toHaveLength(0); + }); +}); \ No newline at end of file