agentsclimarketplace

Html

Skill 14BryanEspinoza/agent-stack/skills/html

Reglas de HTML5 semántico - accesibilidad, SEO, formularios, multimedia, Web Components, performance, seguridadFrom its SKILL.md

Install
npx -y skills add 14BryanEspinoza/agent-stack --skill html

Assembled 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

  1. 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.
  2. Accesibilidad por defecto — No es opcional ni un afterthought. WCAG AA es el mínimo: lang, alt, labels, contraste, teclado, headings jerárquicos.
  3. Progressive Enhancement — El contenido debe ser accesible y funcional sin JavaScript. JS solo añade mejoras, no es requisito para operar.
  4. 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.
  5. 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íaVersión
HTML5 (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>&copy; 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

EtiquetaPropó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íaDescripciónEjemplos
FlowMayoría de elementos visibles<div>, <p>, <form>, <table>, <ul>
PhrasingElementos inline, texto<span>, <strong>, <a>, <img>, <input>
HeadingEncabezados<h1>-<h6>
SectioningDefine secciones del documento<article>, <section>, <nav>, <aside>
EmbeddedContenido externo o multimedia<img>, <video>, <audio>, <canvas>
InteractiveElementos con los que se interactúa<a>, <button>, <input>, <select>
MetadataInformació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

EtiquetaPropó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)

ValorUso
colEncabezado de columna
rowEncabezado de fila
colgroupEncabezado de grupo columnas
rowgroupEncabezado 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

TipoCuándo usarloValidación nativa
textTexto genéricominlength, maxlength, pattern
emailCorreo electrónicoFormato email
passwordContraseñasminlength
numberNúmerosmin, max, step
telTeléfonopattern
urlURLsFormato URL
searchBúsquedas
dateFechasmin, max
datetime-localFecha y horamin, max
monthMes y añomin, max
weekSemana del añomin, max
timeHoramin, max
fileSubida de archivosaccept
checkboxOpciones múltiples
radioOpción única
rangeValores en rangomin, max, step
colorSelector de color
hiddenDatos 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

  1. Idioma: lang en <html>
  2. Skip link: Enlace para saltar navegación
  3. Headings: Jerarquía correcta (h1 > h2 > h3...)
  4. Alt text: alt descriptivo en imágenes
  5. Labels: <label> asociado a cada <input>
  6. Contraste: Ratio 4.5:1 texto normal, 3:1 texto grande, 3:1 UI
  7. 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

RolCuándo usarlo
banner<header> (global)
navigation<nav>
main<main>
complementary<aside>
contentinfo<footer> (global)
alertMensajes de error dinámicos
dialogModales/ventanas de diálogo
tabpanelPaneles de tabs
progressbarBarras de progreso
statusZonas 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 Tab
  • tabindex="1" (o mayor) — altera el orden natural, evitar siempre
  • No usar tabindex en 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 alt en imágenes (excepto decorativas con alt="")
  • ❌ No onclick/onchange inline en HTML
  • ❌ No dejar lang sin definir
  • ❌ No saltar niveles de heading (h1h3)
  • ❌ 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 alt equivalente
  • ❌ No usar placeholder como ú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.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.