fix: a11y (gold-deep token AA, Disciplinas ul/li, Cierre nav) + scaffold cleanup

- Add gold-deep #7A5F15 token (~5.97:1 on white, WCAG AA) for small-text eyebrows
- Apply text-gold-deep to 4 eyebrows (Hero, Disciplinas, ComoEmpezar, Estratega)
- Disciplinas: div grid → semantic ul/li for screen-reader consistency
- Cierre: wrap contact links in <nav aria-label="Contacto">
- Estratega: portrait gradient → Tailwind tokens (from-ink to-gold), drop inline hex
- Delete unused Geist fonts (app/fonts/, ~134KB)
- Replace default README with creeer README
- Tests: +2 structural (Disciplinas ul/li, Cierre nav aria-label)
This commit is contained in:
erikgh committed 2026-07-22 10:06:46 -06:00
1 parent 3f0a15eac9
commit b3bd698e9b
11 files changed
+54 -41

No files matched your search

+27 -27
View File
@@ -1,36 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). # creeer
## Getting Started Landing page one-page para **creeer**, marca personal de una estratega de crecimiento. Next.js 14 (App Router) + TypeScript + Tailwind CSS + Framer Motion, exportada como sitio estático.
First, run the development server: ## Requisitos
- Node.js 20+
- pnpm 11+ (`corepack enable`)
## Comandos
```bash ```bash
npm run dev pnpm install # instalar dependencias
# or pnpm dev # servidor de desarrollo (http://localhost:3000)
yarn dev pnpm build # build estático → out/
# or pnpm test # tests (Vitest)
pnpm dev pnpm lint # ESLint
# or
bun dev
``` ```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. ## Estructura
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. ```
app/ # App Router (layout, page, globals.css)
components/
primitives/ # Wordmark, Container, CTA (reutilizables)
sections/ # Hero, FraseAncla, ParaQuien, Mecanismo, Disciplinas, Estratega, Cierre, ComoEmpezar
layout/ # Header, Footer
lib/
content.ts # todo el copy (source of truth)
motion.ts # variantes Framer Motion + reduced-motion
docs/ # spec, plan, directrices de marca
```
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. ## Decisiones de diseño
## Learn More Ver `docs/dueña-directrices.md` (directrices de marca) y `docs/superpowers/specs/2026-07-20-creeer-landing-design.md` (spec completo).
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
Binary file not shown.
Binary file not shown.
+4 -2
View File
@@ -22,13 +22,15 @@ export function Cierre() {
<motion.div variants={fadeInUp} className="mt-10"> <motion.div variants={fadeInUp} className="mt-10">
<CTA href={whatsapp.href} label={cierre.cta} variant="dark" /> <CTA href={whatsapp.href} label={cierre.cta} variant="dark" />
</motion.div> </motion.div>
<motion.ul variants={fadeInUp} className="mt-12 flex flex-wrap justify-center gap-6 text-sm"> <motion.nav variants={fadeInUp} aria-label="Contacto" className="mt-12">
<ul className="flex flex-wrap justify-center gap-6 text-sm">
<li><a href={whatsapp.href} className="hover:text-gold-bright transition-colors">{whatsapp.label}</a></li> <li><a href={whatsapp.href} className="hover:text-gold-bright transition-colors">{whatsapp.label}</a></li>
<li aria-hidden="true" className="text-muted-dark">·</li> <li aria-hidden="true" className="text-muted-dark">·</li>
<li><a href={email.href} className="hover:text-gold-bright transition-colors">{email.label}</a></li> <li><a href={email.href} className="hover:text-gold-bright transition-colors">{email.label}</a></li>
<li aria-hidden="true" className="text-muted-dark">·</li> <li aria-hidden="true" className="text-muted-dark">·</li>
<li><a href={linkedin.href} className="hover:text-gold-bright transition-colors">{linkedin.label}</a></li> <li><a href={linkedin.href} className="hover:text-gold-bright transition-colors">{linkedin.label}</a></li>
</motion.ul> </ul>
</motion.nav>
</motion.div> </motion.div>
</Container> </Container>
</section> </section>
+1 -1
View File
@@ -13,7 +13,7 @@ export function ComoEmpezar() {
<section className="bg-base"> <section className="bg-base">
<Container className="py-24 md:py-32"> <Container className="py-24 md:py-32">
<motion.p variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)} <motion.p variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="text-xs tracking-[0.3em] uppercase font-semibold text-gold">{comoEmpezar.eyebrow}</motion.p> className="text-xs tracking-[0.3em] uppercase font-semibold text-gold-deep">{comoEmpezar.eyebrow}</motion.p>
<motion.h2 variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)} <motion.h2 variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="mt-4 font-serif italic font-bold text-3xl md:text-5xl text-ink">{comoEmpezar.title}</motion.h2> className="mt-4 font-serif italic font-bold text-3xl md:text-5xl text-ink">{comoEmpezar.title}</motion.h2>
<motion.ol variants={staggerContainer} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)} <motion.ol variants={staggerContainer} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
+6 -6
View File
@@ -10,20 +10,20 @@ export function Disciplinas() {
<section className="bg-base"> <section className="bg-base">
<Container className="py-24 md:py-32"> <Container className="py-24 md:py-32">
<motion.p variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)} <motion.p variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="text-xs tracking-[0.3em] uppercase font-semibold text-gold">{disciplinas.eyebrow}</motion.p> className="text-xs tracking-[0.3em] uppercase font-semibold text-gold-deep">{disciplinas.eyebrow}</motion.p>
<motion.h2 variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)} <motion.h2 variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="mt-4 font-serif italic font-bold text-3xl md:text-5xl text-ink">{disciplinas.title}</motion.h2> className="mt-4 font-serif italic font-bold text-3xl md:text-5xl text-ink">{disciplinas.title}</motion.h2>
<motion.p variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)} <motion.p variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="mt-4 text-muted text-lg">{disciplinas.lead}</motion.p> className="mt-4 text-muted text-lg">{disciplinas.lead}</motion.p>
<motion.div variants={staggerContainer} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)} <motion.ul variants={staggerContainer} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="mt-12 grid gap-8 md:grid-cols-3"> className="mt-12 grid gap-8 md:grid-cols-3 list-none p-0">
{disciplinas.items.map((item) => ( {disciplinas.items.map((item) => (
<motion.div key={item.name} variants={fadeInUp} className="border-t-2 border-gold pt-5"> <motion.li key={item.name} variants={fadeInUp} className="border-t-2 border-gold pt-5">
<h3 className="font-serif italic font-bold text-2xl text-ink">{item.name}</h3> <h3 className="font-serif italic font-bold text-2xl text-ink">{item.name}</h3>
<p className="mt-3 text-muted leading-relaxed">{item.desc}</p> <p className="mt-3 text-muted leading-relaxed">{item.desc}</p>
</motion.div> </motion.li>
))} ))}
</motion.div> </motion.ul>
</Container> </Container>
</section> </section>
); );
+2 -3
View File
@@ -13,13 +13,12 @@ export function Estratega() {
className="grid gap-12 md:grid-cols-[auto_1fr] md:items-center"> className="grid gap-12 md:grid-cols-[auto_1fr] md:items-center">
{/* Portrait — placeholder gradient until dueña provides a photo */} {/* Portrait — placeholder gradient until dueña provides a photo */}
<motion.div variants={fadeInUp} <motion.div variants={fadeInUp}
className="w-40 h-40 md:w-56 md:h-56 rounded-full flex-shrink-0" className="w-40 h-40 md:w-56 md:h-56 rounded-full flex-shrink-0 bg-gradient-to-br from-ink to-gold"
style={{ background: 'linear-gradient(135deg, #0A1F44 0%, #B8901f 100%)' }}
aria-label="Retrato de la estratega" aria-label="Retrato de la estratega"
role="img" role="img"
/> />
<div> <div>
<motion.p variants={fadeInUp} className="text-xs tracking-[0.3em] uppercase font-semibold text-gold">{estratega.eyebrow}</motion.p> <motion.p variants={fadeInUp} className="text-xs tracking-[0.3em] uppercase font-semibold text-gold-deep">{estratega.eyebrow}</motion.p>
<motion.h2 variants={fadeInUp} className="mt-4 font-serif italic font-bold text-3xl md:text-5xl text-ink">{estratega.title}</motion.h2> <motion.h2 variants={fadeInUp} className="mt-4 font-serif italic font-bold text-3xl md:text-5xl text-ink">{estratega.title}</motion.h2>
<motion.p variants={fadeInUp} className="mt-6 text-lg text-muted leading-relaxed max-w-2xl">{estratega.bio}</motion.p> <motion.p variants={fadeInUp} className="mt-6 text-lg text-muted leading-relaxed max-w-2xl">{estratega.bio}</motion.p>
<motion.blockquote variants={fadeInUp} className="mt-6 font-serif italic text-xl md:text-2xl text-ink border-l-4 border-gold pl-4"> <motion.blockquote variants={fadeInUp} className="mt-6 font-serif italic text-xl md:text-2xl text-ink border-l-4 border-gold pl-4">
+1 -1
View File
@@ -22,7 +22,7 @@ export function Hero({ variant = 'editorial' }: { variant?: Variant }) {
<Container className="min-h-[88vh] flex flex-col items-center justify-center text-center py-24"> <Container className="min-h-[88vh] flex flex-col items-center justify-center text-center py-24">
<motion.div variants={staggerContainer} initial="hidden" animate="visible" {...motionProps(reduced)} className="flex flex-col items-center"> <motion.div variants={staggerContainer} initial="hidden" animate="visible" {...motionProps(reduced)} className="flex flex-col items-center">
<motion.div variants={fadeInUp}><Wordmark size="xl" /></motion.div> <motion.div variants={fadeInUp}><Wordmark size="xl" /></motion.div>
<motion.p variants={fadeInUp} className="mt-6 text-xs md:text-sm tracking-[0.3em] uppercase font-semibold text-gold"> <motion.p variants={fadeInUp} className="mt-6 text-xs md:text-sm tracking-[0.3em] uppercase font-semibold text-gold-deep">
{hero.eyebrow} {hero.eyebrow}
</motion.p> </motion.p>
<motion.h1 variants={fadeInUp} className={`mt-8 ${headlineBase} ${headlineSize} text-ink`}> <motion.h1 variants={fadeInUp} className={`mt-8 ${headlineBase} ${headlineSize} text-ink`}>
@@ -16,4 +16,8 @@ describe('<Cierre />', () => {
expect(mail.getAttribute('href')).toMatch(/^mailto:/); expect(mail.getAttribute('href')).toMatch(/^mailto:/);
expect(li.getAttribute('href')).toMatch(/^https:\/\/(www\.)?linkedin\./); expect(li.getAttribute('href')).toMatch(/^https:\/\/(www\.)?linkedin\./);
}); });
it('wraps contact links in a nav with aria-label="Contacto"', () => {
render(<Cierre />);
expect(screen.getByRole('navigation', { name: /contacto/i })).toBeInTheDocument();
});
}); });
@@ -9,4 +9,11 @@ describe('<Disciplinas />', () => {
expect(screen.getByText('Digital')).toBeInTheDocument(); expect(screen.getByText('Digital')).toBeInTheDocument();
expect(screen.getByText('Experiencias')).toBeInTheDocument(); expect(screen.getByText('Experiencias')).toBeInTheDocument();
}); });
it('renders disciplines in a semantic list (ul/li) for a11y', () => {
const { container } = render(<Disciplinas />);
const list = container.querySelector('ul');
expect(list).not.toBeNull();
const items = list?.querySelectorAll('li');
expect(items?.length).toBe(3);
});
}); });
+2 -1
View File
@@ -11,7 +11,8 @@ const config: Config = {
ink: '#0A1F44', // texto principal ink: '#0A1F44', // texto principal
cream: '#F5EBD0', // texto sobre oscuro (marfil) cream: '#F5EBD0', // texto sobre oscuro (marfil)
gold: { gold: {
DEFAULT: '#B8901f', // dorado tostado (sobre claro) DEFAULT: '#B8901f', // dorado tostado (sobre claro) — usar solo en texto grande/decorativo
deep: '#7A5F15', // dorado profundo (AA 4.5:1 sobre claro para texto pequeño)
bright: '#D4AF37', // dorado (sobre oscuro) bright: '#D4AF37', // dorado (sobre oscuro)
}, },
muted: '#5a5168', muted: '#5a5168',