agentsclimarketplace

Html

Skill 14BryanEspinoza/agent-stack/skills/html

Skills modulares para asistentes de codificación por IA. Define reglas, flujos de trabajo y estándares técnicos mediante archivos Markdowm.

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.

2 things to look at

  • 21 days oldThe repository was created 21 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
  • 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.

What its author says it does

Copied from the file, not written here

Reglas de HTML5 semántico - accesibilidad, SEO, formularios, multimedia, Web Components, performance, seguridad

SKILL.md

29.4 KB, 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

Keep looking

Skills are one crate of 328,083. 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.