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:
1 parent
3f0a15eac9
commit
b3bd698e9b
11 files changed
+54
-41
No files matched your search
@@ -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
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
pnpm install # instalar dependencias
|
||||
pnpm dev # servidor de desarrollo (http://localhost:3000)
|
||||
pnpm build # build estático → out/
|
||||
pnpm test # tests (Vitest)
|
||||
pnpm lint # ESLint
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
Ver `docs/dueña-directrices.md` (directrices de marca) y `docs/superpowers/specs/2026-07-20-creeer-landing-design.md` (spec completo).
|
||||
Binary file not shown.
Binary file not shown.
@@ -22,13 +22,15 @@ export function Cierre() {
|
||||
<motion.div variants={fadeInUp} className="mt-10">
|
||||
<CTA href={whatsapp.href} label={cierre.cta} variant="dark" />
|
||||
</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 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 aria-hidden="true" className="text-muted-dark">·</li>
|
||||
<li><a href={linkedin.href} className="hover:text-gold-bright transition-colors">{linkedin.label}</a></li>
|
||||
</motion.ul>
|
||||
</ul>
|
||||
</motion.nav>
|
||||
</motion.div>
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
@@ -13,7 +13,7 @@ export function ComoEmpezar() {
|
||||
<section className="bg-base">
|
||||
<Container className="py-24 md:py-32">
|
||||
<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)}
|
||||
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)}
|
||||
|
||||
@@ -10,20 +10,20 @@ export function Disciplinas() {
|
||||
<section className="bg-base">
|
||||
<Container className="py-24 md:py-32">
|
||||
<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)}
|
||||
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)}
|
||||
className="mt-4 text-muted text-lg">{disciplinas.lead}</motion.p>
|
||||
<motion.div variants={staggerContainer} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
|
||||
className="mt-12 grid gap-8 md:grid-cols-3">
|
||||
<motion.ul variants={staggerContainer} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
|
||||
className="mt-12 grid gap-8 md:grid-cols-3 list-none p-0">
|
||||
{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>
|
||||
<p className="mt-3 text-muted leading-relaxed">{item.desc}</p>
|
||||
</motion.div>
|
||||
</motion.li>
|
||||
))}
|
||||
</motion.div>
|
||||
</motion.ul>
|
||||
</Container>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -13,13 +13,12 @@ export function Estratega() {
|
||||
className="grid gap-12 md:grid-cols-[auto_1fr] md:items-center">
|
||||
{/* Portrait — placeholder gradient until dueña provides a photo */}
|
||||
<motion.div variants={fadeInUp}
|
||||
className="w-40 h-40 md:w-56 md:h-56 rounded-full flex-shrink-0"
|
||||
style={{ background: 'linear-gradient(135deg, #0A1F44 0%, #B8901f 100%)' }}
|
||||
className="w-40 h-40 md:w-56 md:h-56 rounded-full flex-shrink-0 bg-gradient-to-br from-ink to-gold"
|
||||
aria-label="Retrato de la estratega"
|
||||
role="img"
|
||||
/>
|
||||
<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.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">
|
||||
|
||||
@@ -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">
|
||||
<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.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}
|
||||
</motion.p>
|
||||
<motion.h1 variants={fadeInUp} className={`mt-8 ${headlineBase} ${headlineSize} text-ink`}>
|
||||
|
||||
@@ -16,4 +16,8 @@ describe('<Cierre />', () => {
|
||||
expect(mail.getAttribute('href')).toMatch(/^mailto:/);
|
||||
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('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
@@ -11,7 +11,8 @@ const config: Config = {
|
||||
ink: '#0A1F44', // texto principal
|
||||
cream: '#F5EBD0', // texto sobre oscuro (marfil)
|
||||
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)
|
||||
},
|
||||
muted: '#5a5168',
|
||||
|
||||
Reference in new issue
Block a user