feat: primitives (Wordmark, Container, CTA) with tests

This commit is contained in:
erikgh committed 2026-07-21 10:10:14 -06:00
1 parent 48a37395aa
commit 7d12e35600
9 files changed
+98 -2

No files matched your search

+16
View File
@@ -0,0 +1,16 @@
type Variant = 'light' | 'dark';
const styles: Record<Variant, string> = {
light: 'bg-ink text-base hover:bg-ink/90',
dark: 'bg-gold-bright text-ink hover:bg-gold-bright/90',
};
export function CTA({ href, label, variant = 'light', className = '' }: { href: string; label: string; variant?: Variant; className?: string }) {
return (
<a
href={href}
className={`inline-block font-sans font-bold uppercase tracking-wider px-7 py-3.5 text-sm transition-colors ${styles[variant]} ${className}`}
>
{label} <span aria-hidden="true">→</span>
</a>
);
}
+5
View File
@@ -0,0 +1,5 @@
import type { ElementType, ReactNode } from 'react';
export function Container({ as: Tag = 'div', className = '', children }: { as?: ElementType; className?: string; children: ReactNode }) {
return <Tag className={`mx-auto w-full max-w-content px-6 md:px-12 ${className}`}>{children}</Tag>;
}
+23
View File
@@ -0,0 +1,23 @@
import { wordmark } from '@/lib/content';
type Size = 'sm' | 'md' | 'lg' | 'xl';
const sizeClass: Record<Size, string> = {
sm: 'text-xl',
md: 'text-2xl',
lg: 'text-3xl',
xl: 'text-5xl',
};
export function Wordmark({ size = 'lg', onDark = false }: { size?: Size; onDark?: boolean }) {
const { before, accent, after, dot } = wordmark.parts;
const baseColor = onDark ? 'text-cream' : 'text-ink';
const goldColor = onDark ? 'text-gold-bright' : 'text-gold';
return (
<span className={`font-serif italic font-bold tracking-tight leading-none ${sizeClass[size]} ${baseColor}`}>
{before}
<span data-accent="true" className={`not-italic font-black ${goldColor}`}>{accent}</span>
{after}
<span className={goldColor}>{dot}</span>
</span>
);
}
@@ -0,0 +1,14 @@
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { CTA } from '../CTA';
describe('<CTA />', () => {
it('renders the label with arrow', () => {
render(<CTA href="#contacto" label="Define tu siguiente movimiento" />);
expect(screen.getByRole('link', { name: /define tu siguiente movimiento/i })).toBeInTheDocument();
});
it('uses the href', () => {
render(<CTA href="#contacto" label="x" />);
expect(screen.getByRole('link').getAttribute('href')).toBe('#contacto');
});
});
@@ -0,0 +1,22 @@
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { Wordmark } from '../Wordmark';
describe('<Wordmark />', () => {
it('renders the wordmark in lowercase', () => {
render(<Wordmark />);
const text = screen.getByText(/cre/).closest('span')?.textContent;
expect(text?.toLowerCase()).toBe('creeer.');
});
it('renders the accent "e" separately for gold styling', () => {
const { container } = render(<Wordmark />);
const accent = container.querySelector('[data-accent="true"]');
expect(accent).not.toBeNull();
expect(accent?.textContent).toBe('e');
});
it('never renders uppercase CREEER', () => {
const { container } = render(<Wordmark />);
expect(container.textContent).not.toMatch(/CREEER/);
expect(container.textContent).not.toMatch(/Creeer/);
});
});
+3 -1
View File
@@ -6,7 +6,9 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "next lint",
"test": "vitest run",
"test:watch": "vitest"
},
"dependencies": {
"framer-motion": "^11.18.2",
+1 -1
View File
@@ -22,5 +22,5 @@
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
"exclude": ["node_modules", "vitest.config.ts"]
}
+13
View File
@@ -0,0 +1,13 @@
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./vitest.setup.ts'],
},
resolve: { alias: { '@': path.resolve(__dirname, './') } },
});
+1
View File
@@ -0,0 +1 @@
import '@testing-library/jest-dom/vitest';