Diseño visual: tema NovaWoW (tokens Tailwind), hero en la home, cabecera pulida

- globals.css: tokens @theme (nw-bg/panel/border/gold/text/muted), fondo con
  degradado + brillo radial, y componentes reutilizables (.nw-btn, .nw-btn-ghost,
  .nw-card, .nw-heading con oro degradado).
- Header: sticky con backdrop-blur y marca en oro degradado.
- Home: sección hero (nombre del server + tagline + CTAs Crear cuenta / Foros) y
  estado del servidor + noticias en tarjetas (nw-card). Textos hero en catálogos.

Verificado: build OK, home renderiza el hero/tarjetas, los tokens nw compilan en el
CSS. Sistema visual base listo para aplicar al resto de páginas.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-13 00:09:58 +00:00
parent f9609aad99
commit c9ba6c35e3
5 changed files with 134 additions and 54 deletions
+85 -47
View File
@@ -1,4 +1,5 @@
import { getTranslations, setRequestLocale } from 'next-intl/server' import { getTranslations, setRequestLocale } from 'next-intl/server'
import { Link } from '@/i18n/navigation'
import { getNoticias, getServerStatus, type Noticia, type ServerStatus } from '@/lib/home' import { getNoticias, getServerStatus, type Noticia, type ServerStatus } from '@/lib/home'
// SSR por petición: los datos se leen DIRECTAMENTE de MySQL desde el servidor Next. // SSR por petición: los datos se leen DIRECTAMENTE de MySQL desde el servidor Next.
@@ -19,54 +20,91 @@ export default async function HomePage({ params }: { params: Promise<{ locale: s
const { noticias, status } = await getData() const { noticias, status } = await getData()
return ( return (
<main className="mx-auto max-w-3xl px-4 py-8"> <div>
<h1 className="text-3xl font-bold text-amber-500">{t('brand')}</h1> {/* Hero */}
<section className="relative overflow-hidden border-b border-nw-border/50">
<section className="my-6 rounded-lg border border-amber-900/60 bg-[#241812] p-4"> <div className="mx-auto max-w-4xl px-4 py-16 text-center sm:py-24">
<h2 className="mb-3 text-lg font-semibold">{t('serverStatus')}</h2> <p className="mb-3 text-sm font-semibold uppercase tracking-[0.3em] text-nw-gold/80">
{status ? ( WotLK Classic 3.4.3
<ul className="space-y-1 text-sm"> </p>
<li> <h1 className="nw-heading text-5xl sm:text-6xl">Nova WoW</h1>
{t('server')}: <span className="text-amber-400">{status.server_name ?? '—'}</span> ({status.expansion}) <p className="mx-auto mt-5 max-w-2xl text-nw-muted">{t('tagline')}</p>
</li> <div className="mt-8 flex flex-wrap justify-center gap-3">
<li> <Link href="/register" className="nw-btn">
{t('onlineCharacters')}: {status.online_characters} {t('ctaRegister')}
</li> </Link>
<li> <Link href="/forum" className="nw-btn-ghost">
{t('login')}:{' '} {t('ctaForum')}
<span className={status.status === 'Online' ? 'text-green-400' : 'text-red-400'}>{status.status}</span> </Link>
</li> </div>
<li> </div>
{t('address')}: {status.address ?? '—'}
</li>
</ul>
) : (
<p>{t('notAvailable')}</p>
)}
</section> </section>
<section> <main className="mx-auto max-w-4xl px-4 py-10">
<h2 className="mb-3 text-lg font-semibold">{t('news')}</h2> <div className="grid gap-6 md:grid-cols-3">
{noticias.length === 0 ? ( {/* Estado del servidor */}
<p className="text-amber-200/70">{t('noNews')}</p> <aside className="nw-card md:col-span-1">
) : ( <h2 className="mb-3 text-lg font-semibold text-nw-gold-light">{t('serverStatus')}</h2>
noticias.map((n) => ( {status ? (
<article key={n.id} className="mb-4 border-b border-amber-900/60 pb-4"> <ul className="space-y-2 text-sm">
<h3 className="text-xl font-semibold">{n.titulo}</h3> <li className="flex justify-between">
{n.fecha && <small className="text-amber-200/60">{new Date(n.fecha).toLocaleString(locale)}</small>} <span className="text-nw-muted">{t('server')}</span>
<div className="prose prose-invert mt-2 max-w-none" dangerouslySetInnerHTML={{ __html: n.contenido }} /> <span>
{n.enlace && ( {status.server_name ?? '—'} <span className="text-nw-muted">({status.expansion})</span>
<p className="mt-2"> </span>
{t('link')}:{' '} </li>
<a className="text-sky-400 underline" href={n.enlace} target="_blank" rel="noopener noreferrer"> <li className="flex justify-between">
{t('here')} <span className="text-nw-muted">{t('onlineCharacters')}</span>
</a> <span className="text-green-400">{status.online_characters}</span>
</p> </li>
)} <li className="flex justify-between">
</article> <span className="text-nw-muted">{t('login')}</span>
)) <span className={status.status === 'Online' ? 'text-green-400' : 'text-red-400'}>
)} {status.status}
</section> </span>
</main> </li>
<li className="flex justify-between">
<span className="text-nw-muted">{t('address')}</span>
<span className="text-nw-gold-light">{status.address ?? '—'}</span>
</li>
</ul>
) : (
<p className="text-nw-muted">{t('notAvailable')}</p>
)}
</aside>
{/* Noticias */}
<section className="md:col-span-2">
<h2 className="mb-4 text-xl font-semibold text-nw-gold-light">{t('news')}</h2>
{noticias.length === 0 ? (
<p className="text-nw-muted">{t('noNews')}</p>
) : (
<div className="space-y-5">
{noticias.map((n) => (
<article key={n.id} className="nw-card">
<h3 className="text-lg font-semibold">{n.titulo}</h3>
{n.fecha && (
<small className="text-nw-muted">{new Date(n.fecha).toLocaleString(locale)}</small>
)}
<div
className="prose prose-invert mt-3 max-w-none text-sm"
dangerouslySetInnerHTML={{ __html: n.contenido }}
/>
{n.enlace && (
<p className="mt-3">
{t('link')}:{' '}
<a href={n.enlace} target="_blank" rel="noopener noreferrer" className="text-sky-400 underline">
{t('here')}
</a>
</p>
)}
</article>
))}
</div>
)}
</section>
</div>
</main>
</div>
) )
} }
+39 -3
View File
@@ -1,11 +1,47 @@
@import 'tailwindcss'; @import 'tailwindcss';
/* Tema base NovaWoW (provisional; se afinará al portar el diseño). */ /* Tokens de diseño NovaWoW (paleta oscura fantasía WotLK). */
@theme {
--color-nw-bg: #140d08;
--color-nw-panel: #241812;
--color-nw-panel-2: #2c1e14;
--color-nw-border: #4a3320;
--color-nw-gold: #d79602;
--color-nw-gold-light: #f0b93f;
--color-nw-text: #e8dccb;
--color-nw-muted: #b1997f;
}
:root { :root {
color-scheme: dark; color-scheme: dark;
} }
body { body {
background: #1b120b; background:
color: #e8dccb; radial-gradient(1100px 520px at 50% -8%, rgba(215, 150, 2, 0.12), transparent 62%),
linear-gradient(180deg, #1b120b 0%, #120b07 100%);
background-attachment: fixed;
color: var(--color-nw-text);
min-height: 100vh;
}
/* Componentes reutilizables */
@layer components {
.nw-btn {
@apply inline-flex items-center justify-center rounded-md bg-nw-gold px-5 py-2.5 font-semibold text-[#1b120b] transition hover:bg-nw-gold-light;
}
.nw-btn-ghost {
@apply inline-flex items-center justify-center rounded-md border border-nw-border px-5 py-2.5 font-semibold text-nw-text transition hover:border-nw-gold hover:text-nw-gold-light;
}
.nw-card {
@apply rounded-xl border border-nw-border/70 bg-nw-panel/80 p-5 shadow-lg shadow-black/30 backdrop-blur;
}
.nw-heading {
@apply bg-gradient-to-b from-nw-gold-light to-nw-gold bg-clip-text font-extrabold tracking-wide text-transparent;
}
}
/* Contenido HTML de noticias/foro */
.prose :where(a) {
color: #7cc0f7;
} }
+2 -2
View File
@@ -10,9 +10,9 @@ export async function Header() {
const loggedIn = Boolean(session.username) const loggedIn = Boolean(session.username)
return ( return (
<header className="border-b border-amber-900/50 bg-[#241812]"> <header className="sticky top-0 z-50 border-b border-nw-border/60 bg-nw-bg/80 backdrop-blur supports-[backdrop-filter]:bg-nw-bg/60">
<div className="mx-auto flex max-w-5xl items-center justify-between gap-4 px-4 py-3"> <div className="mx-auto flex max-w-5xl items-center justify-between gap-4 px-4 py-3">
<Link href="/" className="text-xl font-bold tracking-wide text-amber-500"> <Link href="/" className="nw-heading text-xl">
Nova WoW Nova WoW
</Link> </Link>
<nav className="flex items-center gap-4 text-sm"> <nav className="flex items-center gap-4 text-sm">
+4 -1
View File
@@ -24,7 +24,10 @@
"news": "News", "news": "News",
"noNews": "There are no news available at the moment.", "noNews": "There are no news available at the moment.",
"link": "Link", "link": "Link",
"here": "here" "here": "here",
"tagline": "The best WoW WotLK Classic 3.4.3 server in Spanish and Latino. Join a community of thousands of players for free.",
"ctaRegister": "Create free account",
"ctaForum": "View forums"
}, },
"Login": { "Login": {
"title": "Sign in to Nova WoW", "title": "Sign in to Nova WoW",
+4 -1
View File
@@ -24,7 +24,10 @@
"news": "Noticias", "news": "Noticias",
"noNews": "No hay noticias disponibles en este momento.", "noNews": "No hay noticias disponibles en este momento.",
"link": "Enlace", "link": "Enlace",
"here": "aquí" "here": "aquí",
"tagline": "El mejor servidor de WoW WotLK Classic 3.4.3 en español y latino. Únete gratis a una comunidad de miles de jugadores.",
"ctaRegister": "Crear cuenta gratis",
"ctaForum": "Ver foros"
}, },
"Login": { "Login": {
"title": "Conectar a Nova WoW", "title": "Conectar a Nova WoW",