feat(primitivas): capa gráfica y de movimiento reutilizable
Diez primitivas que dan a la landing el "gráfico y movimiento" que pidió la dueña, todas con guarda de prefers-reduced-motion: - FondoPuntos / PatronPuntos — el patrón de puntos del manual - TarjetaSpotlight, TextoDesenfoque, ContadorNumero — realce y reveal - Carrusel — deslizable, con contador, autoplay pausable - Icono, SimboloTresLineas, IlustracionInflexion — línea, sin relleno - FondoFoto — duotono azul/dorado sobre foto useAutoStep en lib/motion.ts comparte la forma del autoplay del carrusel para indicadores de paso que no son carrusel. IlustracionInflexion documenta un detalle que muerde: motion.path aplica su propio andamiaje de dasharray aunque no reciba initial/animate, así que la rama de reduced motion renderiza <path> planos en vez de intentar neutralizar los animados. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a9758a92f9
commit
5b400cb161
19 files changed
+881
No files matched your search
@@ -0,0 +1,26 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { PatronPuntos } from '../PatronPuntos';
|
||||
|
||||
describe('<PatronPuntos />', () => {
|
||||
it('draws rows x cols dots', () => {
|
||||
const { container } = render(<PatronPuntos rows={3} cols={8} />);
|
||||
expect(container.querySelectorAll('circle')).toHaveLength(24);
|
||||
});
|
||||
it('shrinks the dots left to right, as the manual does', () => {
|
||||
const { container } = render(<PatronPuntos rows={1} cols={6} />);
|
||||
const radii = Array.from(container.querySelectorAll('circle')).map((c) => Number(c.getAttribute('r')));
|
||||
for (let i = 1; i < radii.length; i++) expect(radii[i]).toBeLessThan(radii[i - 1]);
|
||||
});
|
||||
it('is decorative, so it stays out of the accessibility tree', () => {
|
||||
const { container } = render(<PatronPuntos />);
|
||||
const svg = container.querySelector('svg');
|
||||
expect(svg?.getAttribute('aria-hidden')).toBe('true');
|
||||
});
|
||||
it('takes its colour from currentColor so it works on light and dark', () => {
|
||||
const { container } = render(<PatronPuntos rows={1} cols={2} />);
|
||||
for (const c of Array.from(container.querySelectorAll('circle'))) {
|
||||
expect(c.getAttribute('fill')).toBe('currentColor');
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user