Html
Reglas de HTML5 semántico - accesibilidad, SEO, formularios, multimedia, Web Components, performance, seguridadFrom its SKILL.md
npx -y skills add 14BryanEspinoza/agent-stack --skill htmlAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
29.4 KB, ~8.3k tokens by cl100k_base, as published. Nobody here has run it
HTML5 - Reglas y Convenciones
1. Filosofía
- Semántica primero — Cada etiqueta tiene un significado. Usar
<nav>,<article>,<aside>en lugar de<div>genéricos. El HTML debe ser legible y comprensible incluso sin CSS. - Accesibilidad por defecto — No es opcional ni un afterthought. WCAG AA es el mínimo:
lang,alt, labels, contraste, teclado, headings jerárquicos. - Progressive Enhancement — El contenido debe ser accesible y funcional sin JavaScript. JS solo añade mejoras, no es requisito para operar.
- Estructura sobre presentación — El HTML define la estructura y el significado. La presentación es responsabilidad exclusiva de CSS. No usar HTML para efectos visuales.
- Menos es más — HTML mínimo y semántico. Sin etiquetas innecesarias, sin contenedores que no aportan estructura.
2. Versión Mínima
| Tecnología | Versión |
|---|---|
| HTML | 5 (HTML semántico) |
3. Estructura Base
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Descripción de la página" />
<title>Título de la página</title>
<link rel="stylesheet" href="css/styles.css" />
</head>
<body>
<header>
<nav aria-label="Principal">
<ul>
<li><a href="/">Inicio</a></li>
<li><a href="/about">Sobre nosotros</a></li>
</ul>
</nav>
</header>
<main id="main">
<article>
<h1>Título principal</h1>
<p>Contenido del artículo</p>
</article>
</main>
<footer>
<p>© 2026</p>
</footer>
</body>
</html>
Doctype y Lang
<!-- Siempre HTML5 -->
<!DOCTYPE html>
<!-- Idioma correcto -->
<html lang="es">
<!-- ... -->
</html>
<!-- Alternativa: página en inglés -->
<html lang="en">
<!-- ... -->
</html>
<!-- Páginas multilingüe con hreflang -->
<html lang="es">
<link rel="alternate" hreflang="en" href="https://ejemplo.com/en/" />
<link rel="alternate" hreflang="es" href="https://ejemplo.com/es/" />
</html>
4. Etiquetas Semánticas
Landmarks
| Etiqueta | Propósito |
|---|---|
<header> | Cabecera de página o sección |
<nav> | Navegación principal |
<main> | Contenido principal (único por página) |
<article> | Contenido independiente (blog, post) |
<section> | Agrupación temática |
<aside> | Contenido complementario |
<footer> | Pie de página o sección |
Content Models
Los elementos HTML se clasifican en categorías de contenido que determinan dónde pueden usarse:
| Categoría | Descripción | Ejemplos |
|---|---|---|
| Flow | Mayoría de elementos visibles | <div>, <p>, <form>, <table>, <ul> |
| Phrasing | Elementos inline, texto | <span>, <strong>, <a>, <img>, <input> |
| Heading | Encabezados | <h1>-<h6> |
| Sectioning | Define secciones del documento | <article>, <section>, <nav>, <aside> |
| Embedded | Contenido externo o multimedia | <img>, <video>, <audio>, <canvas> |
| Interactive | Elementos con los que se interactúa | <a>, <button>, <input>, <select> |
| Metadata | Información del documento | <base>, <link>, <meta>, <script>, <title> |
Cada elemento puede pertenecer a una o más categorías. Ej: <input> es Flow, Phrasing, Interactive y (con type=hidden) Metadata.
Texto
| Etiqueta | Propósito |
|---|---|
<h1>-<h6> | Jerarquía de títulos (1 h1 por página) |
<p> | Párrafo |
<blockquote> | Cita larga (con cite opcional) |
<cite> | Título de obra citada |
<code> | Código en línea |
<pre> | Código preformateado |
<abbr> | Abreviatura (con title para expansión) |
<address> | Información de contacto |
<time> | Fecha/hora machine-readable |
<mark> | Texto resaltado contextualmente |
<small> | Letra pequeña, notas legales |
<strong> | Énfasis fuerte (semántico) |
<em> | Énfasis (semántico) |
<!-- Ejemplos de texto semántico -->
<abbr title="HyperText Markup Language">HTML</abbr>
<time datetime="2026-07-15T10:00:00Z">15 de julio de 2026</time>
<address>[email protected]</address>
<mark>Texto resaltado</mark>
Listas
<!-- Ordenada -->
<ol type="1" start="1">
<li>Primero</li>
<li>Segundo</li>
</ol>
<!-- No ordenada -->
<ul>
<li>Item</li>
<li>Item</li>
</ul>
<!-- Descripción -->
<dl>
<dt>Término</dt>
<dd>Definición</dd>
</dl>
Citas
<!-- Cita en línea -->
<q cite="https://fuente.com">Texto citado</q>
<!-- Cita larga -->
<blockquote cite="https://fuente.com">
<p>Texto de la cita larga.</p>
<footer>— <cite>Nombre de la obra</cite></footer>
</blockquote>
5. Tablas
<table>
<caption>
Ventas por trimestre 2026
</caption>
<thead>
<tr>
<th scope="col">Trimestre</th>
<th scope="col">Ventas</th>
<th scope="col">Crecimiento</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Q1</th>
<td>$10,000</td>
<td>+5%</td>
</tr>
<tr>
<th scope="row">Q2</th>
<td>$12,000</td>
<td>+20%</td>
</tr>
</tbody>
<tfoot>
<tr>
<th scope="row">Total</th>
<td>$22,000</td>
<td>+12.5%</td>
</tr>
</tfoot>
</table>
scope (obligatorio para accesibilidad)
| Valor | Uso |
|---|---|
col | Encabezado de columna |
row | Encabezado de fila |
colgroup | Encabezado de grupo columnas |
rowgroup | Encabezado de grupo filas |
Columnas con colgroup
<table>
<colgroup>
<col span="2" style="background: #f0f0f0" />
<col style="background: #fff" />
</colgroup>
<tr>
<th>Nombre</th>
<th>Edad</th>
<th>Ciudad</th>
</tr>
</table>
6. Formularios
Estructura Accesible
<form action="/api/contact" method="POST" novalidate>
<fieldset>
<legend>Información de contacto</legend>
<div class="form-group">
<label for="name">Nombre completo</label>
<input
type="text"
id="name"
name="name"
required
minlength="3"
maxlength="100"
aria-describedby="name-hint name-error"
autocomplete="name"
/>
<span id="name-hint" class="hint">Mínimo 3 caracteres</span>
<span id="name-error" class="error" role="alert"></span>
</div>
<div class="form-group">
<label for="email">Correo electrónico</label>
<input
type="email"
id="email"
name="email"
required
autocomplete="email"
aria-describedby="email-error"
/>
<span id="email-error" class="error" role="alert"></span>
</div>
<div class="form-group">
<label for="country">País</label>
<select id="country" name="country" required>
<option value="">Selecciona un país</option>
<option value="mx">México</option>
<option value="es">España</option>
<option value="ar">Argentina</option>
</select>
</div>
<div class="form-group">
<label for="message">Mensaje</label>
<textarea
id="message"
name="message"
rows="5"
required
minlength="10"
></textarea>
</div>
<fieldset>
<legend>Preferencias de contacto</legend>
<label
><input type="checkbox" name="preferences" value="email" checked />
Email</label
>
<label
><input type="checkbox" name="preferences" value="phone" />
Teléfono</label
>
</fieldset>
<button type="submit">Enviar</button>
</fieldset>
</form>
Tipos de Input
| Tipo | Cuándo usarlo | Validación nativa |
|---|---|---|
text | Texto genérico | minlength, maxlength, pattern |
email | Correo electrónico | Formato email |
password | Contraseñas | minlength |
number | Números | min, max, step |
tel | Teléfono | pattern |
url | URLs | Formato URL |
search | Búsquedas | — |
date | Fechas | min, max |
datetime-local | Fecha y hora | min, max |
month | Mes y año | min, max |
week | Semana del año | min, max |
time | Hora | min, max |
file | Subida de archivos | accept |
checkbox | Opciones múltiples | — |
radio | Opción única | — |
range | Valores en rango | min, max, step |
color | Selector de color | — |
hidden | Datos ocultos (no sensible) | — |
Elementos de formulario avanzados
<!-- Datalist (autocomplete personalizado) -->
<label for="browser">Elige un navegador:</label>
<input list="browsers" id="browser" name="browser" />
<datalist id="browsers">
<option value="Chrome" />
<option value="Firefox" />
<option value="Safari" />
<option value="Edge" />
</datalist>
<!-- Output (resultado de cálculo) -->
<form oninput="result.value = parseInt(a.value) + parseInt(b.value)">
<input type="range" id="a" value="50" />
<input type="number" id="b" value="50" />
<output name="result" for="a b">100</output>
</form>
<!-- Progress bar -->
<label for="upload">Subida:</label>
<progress id="upload" value="70" max="100">70%</progress>
<!-- Meter (valor en rango conocido) -->
<label
>Disco:
<meter value="65" min="0" max="100" low="20" high="80" optimum="50">
65%
</meter></label
>
Validación Nativa
<input type="text" required minlength="3" maxlength="50" pattern="[A-Za-z]+" />
<!-- Validación en tiempo real -->
<input
type="email"
required
oninvalid="this.setCustomValidity('Por favor ingresa un email válido')"
oninput="this.setCustomValidity('')"
/>
Autocomplete
<input autocomplete="name" />
<input autocomplete="email" />
<input autocomplete="tel" />
<input autocomplete="street-address" />
<input autocomplete="country-name" />
<input autocomplete="postal-code" />
<input autocomplete="cc-number" />
<input autocomplete="new-password" />
<input autocomplete="current-password" />
7. Multimedia
Imágenes
<!-- Imagen responsive con srcset -->
<img
src="img/foto-800.jpg"
srcset="img/foto-400.jpg 400w, img/foto-800.jpg 800w, img/foto-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, 50vw"
alt="Descripción de la imagen"
loading="lazy"
decoding="async"
fetchpriority="low"
/>
<!-- Imagen con picture (art direction) -->
<picture>
<source
media="(min-width: 1024px)"
srcset="img/hero-desktop.webp"
type="image/webp"
/>
<source
media="(min-width: 768px)"
srcset="img/hero-tablet.webp"
type="image/webp"
/>
<source srcset="img/hero-mobile.webp" type="image/webp" />
<img
src="img/hero-mobile.jpg"
alt="Hero banner"
loading="eager"
fetchpriority="high"
/>
</picture>
<!-- Figura con caption -->
<figure>
<img src="diagrama.png" alt="Diagrama de arquitectura del sistema" />
<figcaption>Figura 1: Arquitectura de microservicios</figcaption>
</figure>
Video
<video
controls
width="100%"
poster="img/thumbnail.jpg"
preload="metadata"
playsinline
>
<source src="video/intro.webm" type="video/webm" />
<source src="video/intro.mp4" type="video/mp4" />
<track
src="captions/intro.vtt"
kind="captions"
srclang="es"
label="Español"
/>
<track
src="captions/intro-en.vtt"
kind="captions"
srclang="en"
label="English"
/>
<p>Tu navegador no soporta video. <a href="video/intro.mp4">Descargar</a></p>
</video>
<!-- Video sin controles (autoplay) -->
<video autoplay muted loop playsinline>
<source src="video/background.mp4" type="video/mp4" />
</video>
Audio
<audio controls preload="metadata">
<source src="audio/podcast.mp3" type="audio/mpeg" />
<source src="audio/podcast.ogg" type="audio/ogg" />
<p>
Tu navegador no soporta audio. <a href="audio/podcast.mp3">Descargar</a>
</p>
</audio>
8. Componentes Interactivos
Dialog
<dialog id="modal">
<form method="dialog">
<h2>Confirmar acción</h2>
<p>¿Estás seguro de eliminar este elemento?</p>
<menu>
<button value="cancel">Cancelar</button>
<button value="confirm" autofocus>Confirmar</button>
</menu>
</form>
</dialog>
<button id="open-modal" type="button">Abrir modal</button>
<script>
document.getElementById("open-modal")?.addEventListener("click", () => {
document.getElementById("modal")?.showModal();
});
</script>
Details / Summary
<details>
<summary>Más información</summary>
<p>Contenido expandible. Ideal para FAQs, acordeones, etc.</p>
</details>
<!-- Anidado -->
<details>
<summary>Sección 1</summary>
<details>
<summary>Sub-sección 1.1</summary>
<p>Contenido anidado</p>
</details>
</details>
9. Web Components
Template + Slot
<!-- Definir template -->
<template id="card-template">
<style>
.card {
border: 1px solid #ddd;
padding: 1rem;
border-radius: 0.5rem;
}
.card__title {
font-size: 1.25rem;
font-weight: 600;
}
::slotted(img) {
max-width: 100%;
}
</style>
<article class="card">
<h2 class="card__title"><slot name="title">Título por defecto</slot></h2>
<div class="card__body">
<slot></slot>
</div>
</article>
</template>
<!-- Usar template -->
<my-card>
<span slot="title">Mi título</span>
<p>Contenido del card</p>
</my-card>
10. Accesibilidad (WCAG AA)
Reglas Obligatorias
- Idioma:
langen<html> - Skip link: Enlace para saltar navegación
- Headings: Jerarquía correcta (
h1>h2>h3...) - Alt text:
altdescriptivo en imágenes - Labels:
<label>asociado a cada<input> - Contraste: Ratio 4.5:1 texto normal, 3:1 texto grande, 3:1 UI
- Teclado: Todos los elementos interactivos accesibles con Tab
Touch Targets
Los elementos interactivos deben tener un tamaño mínimo de 44x44px para ser accesibles en dispositivos táctiles (WCAG 2.5.5):
button,
a.btn,
input[type="checkbox"],
input[type="radio"] {
min-height: 44px;
min-width: 44px;
padding: 0.75rem 1rem;
}
Skip Link
<a href="#main" class="skip-link">Saltar al contenido principal</a>
ARIA
Usar ARIA solo cuando HTML semántico no es suficiente.
<!-- ❌ Malo - ARIA innecesario -->
<div role="button" tabindex="0" onclick="submit()">Enviar</div>
<!-- ✅ Bueno - HTML semántico -->
<button type="submit">Enviar</button>
Roles
| Rol | Cuándo usarlo |
|---|---|
banner | <header> (global) |
navigation | <nav> |
main | <main> |
complementary | <aside> |
contentinfo | <footer> (global) |
alert | Mensajes de error dinámicos |
dialog | Modales/ventanas de diálogo |
tabpanel | Paneles de tabs |
progressbar | Barras de progreso |
status | Zonas de contenido dinámico (live regions) |
Estados (aria-*)
<button aria-expanded="false" aria-controls="menu">Menú</button>
<div role="tab" aria-selected="true">Tab activo</div>
<div role="alert" aria-live="assertive">Error al guardar</div>
<div aria-live="polite">Contenido actualizado cada 30s</div>
Propiedades
<!-- aria-label (cuando no hay label visible) -->
<button aria-label="Cerrar sesión">
<svg><!-- icono --></svg>
</button>
<!-- aria-describedby (descripción adicional) -->
<input aria-describedby="password-hint" type="password" />
<span id="password-hint">Mínimo 8 caracteres, 1 mayúscula</span>
<!-- aria-labelledby (label compuesto) -->
<div role="group" aria-labelledby="group-title">
<h2 id="group-title">Opciones de pago</h2>
</div>
Focus Management
tabindex
<!-- ✅ Orden natural - no usar tabindex -->
<button>Primero</button>
<input />
<button>Segundo</button>
<!-- ❌ Malo - tabindex altera orden -->
<button tabindex="3">Tercero</button>
<button tabindex="1">Primero</button>
<button tabindex="2">Segundo</button>
<!-- ✅ Solo para elementos no interactivos que necesitan foco -->
<div tabindex="0">Contenido custom con teclado</div>
<!-- ❌ tabindex > 0 en elementos nativos -->
<button tabindex="5">No hacer</button>
Reglas:
tabindex="0"— el elemento sigue el orden natural del DOM (para hacer focuseable un div)tabindex="-1"— el elemento es focuseable solo por JS, no por Tabtabindex="1"(o mayor) — altera el orden natural, evitar siempre- No usar
tabindexen elementos nativos como<button>,<input>,<a>— ya son focuseables
Autofocus y Trap
<!-- Autofocus en diálogos -->
<dialog id="modal">
<button autofocus>Aceptar</button>
</dialog>
<!-- Trap focus en modales (requiere JS) -->
<div role="dialog" aria-modal="true" aria-labelledby="modal-title">
<h2 id="modal-title">Confirmación</h2>
</div>
Imágenes (accesibilidad)
<!-- Imagen decorativa -->
<img src="decorative.jpg" alt="" aria-hidden="true" />
<!-- Imagen informativa -->
<img
src="chart.png"
alt="Gráfico de ventas 2026: 40% Q1, 30% Q2, 20% Q3, 10% Q4"
/>
<!-- Imagen link -->
<a href="/producto">
<img src="product.jpg" alt="Producto: Zapatos deportivos color azul" />
</a>
<!-- SVG decorativo -->
<svg aria-hidden="true" focusable="false">
<use href="#icon-search" />
</svg>
<!-- SVG informativo -->
<svg role="img" aria-label="Icono de búsqueda">
<title>Buscar</title>
<use href="#icon-search" />
</svg>
11. SEO
Meta Tags Esenciales
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Descripción única de 150-160 caracteres" />
<meta name="robots" content="index, follow" />
<link rel="canonical" href="https://ejemplo.com/pagina" />
<!-- Open Graph -->
<meta property="og:title" content="Título para redes" />
<meta property="og:description" content="Descripción para redes" />
<meta property="og:image" content="https://ejemplo.com/imagen.jpg" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:url" content="https://ejemplo.com/pagina" />
<meta property="og:type" content="website" />
<meta property="og:locale" content="es_ES" />
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Título" />
<meta name="twitter:description" content="Descripción" />
<meta name="twitter:image" content="https://ejemplo.com/imagen.jpg" />
Microdata (Schema.org)
<!-- Article -->
<article itemscope itemtype="https://schema.org/Article">
<h1 itemprop="headline">Título del artículo</h1>
<p itemprop="description">Descripción breve</p>
<meta itemprop="datePublished" content="2026-07-15" />
<span itemprop="author" itemscope itemtype="https://schema.org/Person">
<span itemprop="name">Autor</span>
</span>
</article>
<!-- Breadcrumb -->
<nav
aria-label="Breadcrumb"
itemscope
itemtype="https://schema.org/BreadcrumbList"
>
<ol>
<li
itemprop="itemListElement"
itemscope
itemtype="https://schema.org/ListItem"
>
<a itemprop="item" href="/"><span itemprop="name">Inicio</span></a>
<meta itemprop="position" content="1" />
</li>
<li
itemprop="itemListElement"
itemscope
itemtype="https://schema.org/ListItem"
>
<a itemprop="item" href="/productos"
><span itemprop="name">Productos</span></a
>
<meta itemprop="position" content="2" />
</li>
</ol>
</nav>
<!-- Product -->
<div itemscope itemtype="https://schema.org/Product">
<h1 itemprop="name">Producto</h1>
<span itemprop="offers" itemscope itemtype="https://schema.org/Offer">
<meta itemprop="priceCurrency" content="MXN" />
<span itemprop="price">299.00</span>
</span>
</div>
Headings
- Una sola
<h1>por página - Jerarquía estricta:
h1>h2>h3(no saltar niveles) - Los headings describen el contenido, no son solo para estilos
12. Performance
Carga de recursos
<!-- Preload (recursos críticos) -->
<link
rel="preload"
href="fonts/inter.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link rel="preload" href="css/critical.css" as="style" />
<!-- Prefetch (próxima navegación) -->
<link rel="prefetch" href="/productos" as="document" />
<!-- Preconnect (orígenes externos) -->
<link rel="preconnect" href="https://api.ejemplo.com" />
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin />
<!-- DNS-Prefetch (fallback preconnect) -->
<link rel="dns-prefetch" href="https://analytics.ejemplo.com" />
<!-- Prerender (navegación probable) -->
<link rel="prerender" href="/checkout" />
Scripts
<!-- ✅ Diferir carga (orden respetado) -->
<script src="js/app.js" defer></script>
<script src="js/vendor.js" defer></script>
<!-- ✅ Async para independientes (sin orden) -->
<script src="js/analytics.js" async></script>
<!-- ❌ Bloqueante (evitar) -->
<script src="js/app.js"></script>
<!-- Module scripts -->
<script type="module" src="js/main.js"></script>
Lazy loading
<!-- Imágenes fuera del viewport -->
<img src="foto.jpg" loading="lazy" />
<!-- Iframes -->
<iframe src="widget.html" loading="lazy"></iframe>
<!-- Imagen crítica (hero) -->
<img src="hero.jpg" loading="eager" fetchpriority="high" />
13. Seguridad
Content Security Policy (CSP)
<meta
http-equiv="Content-Security-Policy"
content="
default-src 'self';
script-src 'self' https://analytics.ejemplo.com;
style-src 'self' 'unsafe-inline';
img-src 'self' https: data:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.ejemplo.com;
frame-ancestors 'none';
form-action 'self';
"
/>
Iframe sandbox
<iframe
src="https://widget.externo.com"
sandbox="allow-scripts allow-same-origin"
loading="lazy"
title="Widget externo"
></iframe>
<!-- Valores de sandbox -->
<!--
allow-scripts → permite JavaScript
allow-same-origin → permite acceso al DOM del padre
allow-forms → permite formularios
allow-popups → permite ventanas emergentes
allow-presentation → permite fullscreen
allow-top-navigation → permite navegar el padre
(vacío) → máxima restricción
-->
Prevención XSS
<!-- Escapar output dinámico -->
<!-- ❌ Peligroso -->
<div>{{ user_input }}</div>
<!-- ✅ Seguro (escapar HTML) -->
<div>{{ escapeHtml(user_input) }}</div>
<!-- Trusted Types (CSP nivel 2) -->
<meta
http-equiv="Content-Security-Policy"
content="require-trusted-types-for 'script'"
/>
<!-- Evitar javascript: en URLs -->
<a href="javascript:void(0)">❌ No hacer</a>
<button type="button">✅ Usar button</button>
CORS
<!-- crossorigin en recursos externos -->
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin />
<img src="https://cdn.ejemplo.com/foto.jpg" crossorigin="anonymous" />
<script src="https://cdn.ejemplo.com/lib.js" crossorigin="anonymous"></script>
14. Favicons y PWA
<!-- Favicon clásico -->
<link rel="icon" href="/favicon.ico" sizes="any" />
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<!-- Apple Touch Icon -->
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<!-- Android / PWA -->
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#6366f1" />
<!-- Windows -->
<meta name="msapplication-TileColor" content="#6366f1" />
manifest.json
{
"name": "Mi App",
"short_name": "App",
"description": "Descripción",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#6366f1",
"icons": [
{ "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }
]
}
Service Worker (registro)
<script>
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js");
}
</script>
15. Canvas y SVG
SVG inline
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path d="M12 2L2 7l10 5 10-5-10-5z" />
<path d="M2 17l10 5 10-5" />
<path d="M2 12l10 5 10-5" />
</svg>
<!-- SVG sprite system -->
<svg aria-hidden="true" style="display: none">
<defs>
<path id="icon-search" d="M15.5 14h-.79l-.28-.27..." />
<path id="icon-close" d="M19 6.41L17.59 5 12 10.59..." />
</defs>
</svg>
<svg aria-hidden="true" focusable="false">
<use href="#icon-search" />
</svg>
Canvas (usar solo cuando sea necesario)
<canvas
id="chart"
width="400"
height="300"
role="img"
aria-label="Gráfico de ventas"
>
Tu navegador no soporta canvas.
</canvas>
16. Buenas Prácticas
Links vs Botones
<!-- ✅ Link → navegación -->
<a href="/pagina">Ir a página</a>
<!-- ✅ Botón → acción -->
<button type="button">Guardar</button>
<button type="submit">Enviar formulario</button>
<!-- ❌ Malo - link usado como botón -->
<a onclick="guardar()">Guardar</a>
<!-- ❌ Malo - botón usado como link -->
<button onclick="location.href='/pagina'">Ir a página</button>
Links
<!-- ✅ Descriptivo -->
<a href="/productos">Ver todos los productos</a>
<!-- ❌ Vacío -->
<a href="/productos">Click aquí</a>
<!-- ✅ Abrir externo con seguridad -->
<a href="https://externo.com" target="_blank" rel="noopener noreferrer"
>Sitio externo</a
>
<!-- ✅ Link de teléfono -->
<a href="tel:+521234567890">(123) 456-7890</a>
<!-- ✅ Link de email -->
<a href="mailto:[email protected]">[email protected]</a>
<!-- ✅ Descargar archivo -->
<a href="/docs/manual.pdf" download="Manual-2026.pdf"
>Descargar manual (PDF, 2.4MB)</a
>
Carga de recursos (Buenas Prácticas)
<!-- CSS en <head> -->
<link rel="stylesheet" href="css/styles.css" />
<!-- JS al final de <body> o con defer -->
<script src="js/app.js" defer></script>
i18n
<!-- Dirección del texto -->
<html lang="ar" dir="rtl">
<!-- Cambio de idioma en el texto -->
<p>El término <span lang="en">callback</span> se refiere a...</p>
</html>
17. Prohibiciones
- ❌ NO usar
<div>para todo (usar etiquetas semánticas) - ❌ No usar
<br>para separar párrafos (usar<p>) - ❌ No usar
<b>/<i>(usar<strong>/<em>) - ❌ No usar tablas para layout
- ❌ No inline styles (
style="...") - usar CSS classes - ❌ No omitir
alten imágenes (excepto decorativas conalt="") - ❌ No
onclick/onchangeinline en HTML - ❌ No dejar
langsin definir - ❌ No saltar niveles de heading (
h1→h3) - ❌ No usar
javascript:void(0)en links - ❌ No usar
<marquee>,<blink>,<font>,<center>(obsoletos) - ❌ No duplicar
<main>en una misma página - ❌ No poner texto en imágenes sin
altequivalente - ❌ No usar
placeholdercomo único label (falta accesibilidad) - ❌ No enviar contraseñas por GET en formularios
18. Referencias
Nota: Para CSS, ver CSS Nota: Para JavaScript, ver JavaScript
Última actualización: 2026-07
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.