---
title: "Modo Oscuro en Carrd: Cómo Añadir un Alternador de Tema (Guía 2026)"
description: "Añade un alternador de modo oscuro en Carrd con CSS y JS para copiar y pegar. Evita el destello blanco, sigue el tema del sistema y recuerda la elección del usuario. Guía paso a paso."
date: 2026-01-21
categories: ["web-development"]
tags: ["carrd","css","dark-mode"]
---

import Button from "@components/widgets/Button.astro";
import Notice from "@components/widgets/Notice.astro";
import ListCheck from "@components/widgets/ListCheck.astro";
import Accordion from "@components/widgets/Accordion.astro";
import Tabs from "@components/widgets/Tabs.astro";
import Tab from "@components/widgets/Tab.astro";
import { Picture } from "astro:assets";
import imag1 from "@assets/images/24/02/carrd-back-to-top-embed.png";

El modo oscuro pasó de ser una preferencia minoritaria a una expectativa generalizada. Muchos visitantes navegan de noche con el móvil ya en modo oscuro, y una web blanca y brillante les golpea como una linterna en la cara.

Añadir un alternador de **modo oscuro en Carrd** da a los visitantes el control sobre su experiencia. Cambian a lo que resulte más cómodo para sus ojos, y el sitio recuerda su elección para la próxima vez. Esta guía trae CSS y JS listos para copiar y pegar que además evitan el destello blanco al cargar y siguen el tema del sistema hasta que el visitante toma partido.

<Button link="https://go.bitdoze.com/carrd" text="Carrd.co" />

Algunos tutoriales de Carrd:

- [Añadir un Modal Emergente a Carrd](https://www.bitdoze.com/carrd-popup-modal/)
- [Añadir Scroll Suave a Carrd](https://www.bitdoze.com/carrd-smooth-scroll/)
- [Añadir un Slider de Testimonios a Carrd](https://www.bitdoze.com/carrd-testimonial-slider/)
- [Añadir un Menú Flotante a Carrd](https://www.bitdoze.com/carrd-floating-menu/)
- [Añadir un Menú Lateral a Carrd](https://www.bitdoze.com/carrd-sidebar-menu/)
- [Análisis de Carrd.co](https://www.bitdoze.com/carrd-review/)

> Encontrarás la lista completa de plugins, temas y tutoriales de Carrd en mi web **[carrdme.com](https://carrdme.com/)**, incluidos un [widget de pestañas](https://go.carrdme.com/tabs) y un [widget de acordeón](https://go.carrdme.com/accordion) ya listos para usar.

## Por Qué Añadir Modo Oscuro a tu Sitio Carrd

Cinco razones, una línea cada una:

1. Menos fatiga visual: las interfaces oscuras son más cómodas con poca luz.
2. Ahorro de batería: en pantallas OLED los píxeles oscuros consumen menos energía.
3. Preferencia del usuario: mucha gente simplemente prefiere una interfaz oscura. Ofrecer la opción es un gesto de respeto barato.
4. Sensibilidad a la luz: algunos visitantes necesitan de verdad interfaces más oscuras, y para ellos el modo oscuro es una función de accesibilidad.
5. Expectativa moderna: todas las grandes apps ya traen un tema oscuro.

El modo oscuro ya no es una novedad. Los visitantes buscan el interruptor, y cuando no lo encuentran se van a un sitio que sí lo tenga.

## Requisitos Previos: Necesitas Carrd Pro Standard para los Embeds

Todo este tutorial funciona con el elemento Embed de Carrd. Dos cosas lo dejan resuelto antes de pegar código.

<ListCheck>
<ul>
<li>Cuenta Carrd Pro Standard o superior (los Embeds no están en el plan gratis ni en Pro Lite)</li>
<li>Un sitio publicado, o listo para publicar (los embeds no se renderizan en el editor)</li>
<li>Unos 10 minutos</li>
<li>Sin otras herramientas, sin paso de compilación, sin archivos externos</li>
</ul>
</ListCheck>

<Notice type="warning" title="Los Embeds necesitan Carrd Pro Standard o superior">
El elemento Embed requiere Pro Standard o superior. Pro Lite cuesta 9 $/año para 3 sitios y **no** incluye embeds. Pro Standard cuesta 19 $/año para 10 sitios (39 $/año para 25 sitios) y añade embeds, formularios, dominios personalizados y analítica. Pro Plus cuesta 49 $/año para 25 sitios con ajustes avanzados. Si solo quieres seguir la guía, [inicia la prueba Pro de 7 días](https://go.bitdoze.com/carrd). Sin tarjeta. Precios comprobados en carrd.co/pro en septiembre de 2026; revísalos antes de comprar.
</Notice>

<Notice type="info" title="Los Embeds no aparecen en el editor">
La documentación de Carrd lo dice sin rodeos: el elemento Embed no muestra nada en el editor, así que tienes que publicar el sitio para verlo en acción. Si tu editor parece vacío después de pegar el código de abajo, nada está roto. Publica y luego prueba.
</Notice>

Para añadir un embed, haz clic en el signo `+` y elige Embed. En una página muy cargada también puedes usar el Panel de Elementos (More Actions → Elements…), añadido en agosto de 2026, para encontrar y gestionar elementos sin tener que buscarlos por todo el diseño.

Es el mismo patrón de embed con código personalizado que se usa en la guía para [añadir un aviso de cookies a tu sitio Carrd](https://www.bitdoze.com/add-cookie-notice-carrd/). Una vez has hecho un embed, el resto son trabajos de copiar y pegar.

## Cómo Funciona el Modo Oscuro en Carrd

Carrd no tiene modo oscuro integrado. Revisé de nuevo la documentación y el changelog en septiembre de 2026: sigue sin existir una función de cambio de tema. El código personalizado es el único camino.

El enfoque es el de siempre:

1. Un atributo `data-theme` en la raíz de la página (`light` o `dark`)
2. Variables CSS para los colores, intercambiadas por `[data-theme="dark"]`
3. Un botón alternador que invierte el atributo
4. `localStorage` para que la elección sobreviva a las recargas
5. `prefers-color-scheme` como valor por defecto antes de que el visitante elija nada

La mejora frente a un snippet ingenuo es el **patrón de dos embeds**. El Embed n.º 1 es un pequeño bootstrap en un embed Hidden → Head que establece `data-theme` antes de que la página se pinte. El Embed n.º 2 contiene la interfaz del alternador y el manejador del clic (Inline o Hidden → Body End).

<Notice type="info" title="¿Por qué dos embeds?">
Un embed Inline único aplica el tema desde JavaScript después del primer pintado. Los usuarios con el sistema en oscuro ven un destello blanco en cada carga. El bootstrap del head se ejecuta antes del pintado, así que el primer frame ya lleva el tema correcto.
</Notice>

Dos cronologías comparadas. La fila A es el enfoque de un solo embed. La fila B es el enfoque de dos embeds que usa esta guía:

<svg viewBox="0 0 740 300" width="100%" role="img" aria-label="Cronología de carga: un único embed en línea pinta en blanco antes de que JavaScript aplique el modo oscuro, mientras que un embed Hidden en Head establece data-theme antes del primer pintado y la página nunca parpadea">
  <text x="12" y="24" fontSize="15" fontWeight="700" fill="#1e293b">A. Un solo embed en línea</text>
  <rect x="12" y="42" width="110" height="48" rx="8" fill="#f8fafc" stroke="#64748b" strokeWidth="1.5" />
  <text x="67" y="71" fontSize="13" fill="#1e293b" textAnchor="middle">Petición</text>
  <line x1="122" y1="66" x2="152" y2="66" stroke="#64748b" strokeWidth="1.5" />
  <polygon points="152,60 164,66 152,72" fill="#64748b" />
  <rect x="164" y="42" width="130" height="48" rx="8" fill="#fee2e2" stroke="#dc2626" strokeWidth="1.5" />
  <text x="229" y="63" fontSize="13" fill="#1e293b" textAnchor="middle">Primer pintado</text>
  <text x="229" y="80" fontSize="13" fill="#dc2626" textAnchor="middle">(blanco)</text>
  <line x1="294" y1="66" x2="324" y2="66" stroke="#64748b" strokeWidth="1.5" />
  <polygon points="324,60 336,66 324,72" fill="#64748b" />
  <rect x="336" y="42" width="110" height="48" rx="8" fill="#f8fafc" stroke="#64748b" strokeWidth="1.5" />
  <text x="391" y="71" fontSize="13" fill="#1e293b" textAnchor="middle">JS corre</text>
  <line x1="446" y1="66" x2="476" y2="66" stroke="#64748b" strokeWidth="1.5" />
  <polygon points="476,60 488,66 476,72" fill="#64748b" />
  <rect x="488" y="42" width="130" height="48" rx="8" fill="#dcfce7" stroke="#16a34a" strokeWidth="1.5" />
  <text x="553" y="63" fontSize="13" fill="#1e293b" textAnchor="middle">Oscuro aplicado</text>
  <text x="553" y="80" fontSize="12" fill="#16a34a" textAnchor="middle">(tarde)</text>
  <text x="164" y="116" fontSize="13" fill="#dc2626" fontWeight="700">Destello blanco visible para usuarios de modo oscuro</text>

  <text x="12" y="172" fontSize="15" fontWeight="700" fill="#1e293b">B. Patrón de dos embeds (bootstrap en Hidden → Head)</text>
  <rect x="12" y="190" width="110" height="48" rx="8" fill="#f8fafc" stroke="#64748b" strokeWidth="1.5" />
  <text x="67" y="219" fontSize="13" fill="#1e293b" textAnchor="middle">Petición</text>
  <line x1="122" y1="214" x2="152" y2="214" stroke="#64748b" strokeWidth="1.5" />
  <polygon points="152,208 164,214 152,220" fill="#64748b" />
  <rect x="164" y="190" width="185" height="48" rx="8" fill="#f8fafc" stroke="#64748b" strokeWidth="1.5" />
  <text x="256" y="210" fontSize="13" fill="#1e293b" textAnchor="middle">El script del head fija</text>
  <text x="256" y="227" fontSize="13" fill="#1e293b" textAnchor="middle">data-theme</text>
  <line x1="349" y1="214" x2="379" y2="214" stroke="#64748b" strokeWidth="1.5" />
  <polygon points="379,208 391,214 379,220" fill="#64748b" />
  <rect x="391" y="190" width="185" height="48" rx="8" fill="#dcfce7" stroke="#16a34a" strokeWidth="1.5" />
  <text x="483" y="210" fontSize="13" fill="#1e293b" textAnchor="middle">Primer pintado</text>
  <text x="483" y="227" fontSize="13" fill="#16a34a" textAnchor="middle">(ya oscuro)</text>
  <text x="164" y="264" fontSize="13" fill="#16a34a" fontWeight="700">Sin destello: el tema está activo antes del primer frame</text>
</svg>

Orden de resolución del tema, de mayor a menor prioridad:

1. Una elección guardada en `localStorage` bajo la clave `theme` (el visitante pulsó el alternador en algún momento)
2. La preferencia del SO/navegador vía `prefers-color-scheme: dark`
3. El modo claro como valor por defecto fijo

<svg viewBox="0 0 640 300" width="100%" role="img" aria-label="Flujo de resolución del tema: usa la elección guardada en localStorage si existe; si no, sigue el ajuste prefers-color-scheme del sistema; si no, modo claro por defecto">
  <rect x="30" y="20" width="270" height="52" rx="8" fill="#f8fafc" stroke="#64748b" strokeWidth="1.5" />
  <text x="165" y="51" fontSize="13" fill="#1e293b" textAnchor="middle">¿localStorage('theme') fijado?</text>
  <line x1="300" y1="46" x2="360" y2="46" stroke="#64748b" strokeWidth="1.5" />
  <polygon points="360,40 372,46 360,52" fill="#64748b" />
  <text x="322" y="36" fontSize="12" fill="#64748b" textAnchor="middle">sí</text>
  <rect x="372" y="20" width="200" height="52" rx="8" fill="#e0e7ff" stroke="#4f46e5" strokeWidth="1.5" />
  <text x="472" y="51" fontSize="13" fill="#1e293b" textAnchor="middle">Usa la elección guardada</text>

  <line x1="165" y1="72" x2="165" y2="128" stroke="#64748b" strokeWidth="1.5" />
  <polygon points="159,128 165,140 171,128" fill="#64748b" />
  <text x="185" y="106" fontSize="12" fill="#64748b">no</text>

  <rect x="30" y="140" width="270" height="52" rx="8" fill="#f8fafc" stroke="#64748b" strokeWidth="1.5" />
  <text x="165" y="163" fontSize="13" fill="#1e293b" textAnchor="middle">prefers-color-scheme:</text>
  <text x="165" y="180" fontSize="13" fill="#1e293b" textAnchor="middle">¿dark?</text>
  <line x1="300" y1="166" x2="360" y2="166" stroke="#64748b" strokeWidth="1.5" />
  <polygon points="360,160 372,166 360,172" fill="#64748b" />
  <text x="322" y="156" fontSize="12" fill="#64748b" textAnchor="middle">sí</text>
  <rect x="372" y="140" width="120" height="52" rx="8" fill="#dcfce7" stroke="#16a34a" strokeWidth="1.5" />
  <text x="432" y="171" fontSize="13" fill="#1e293b" textAnchor="middle">dark</text>

  <line x1="165" y1="192" x2="165" y2="248" stroke="#64748b" strokeWidth="1.5" />
  <polygon points="159,248 165,260 171,248" fill="#64748b" />
  <text x="185" y="230" fontSize="12" fill="#64748b">no</text>
  <rect x="105" y="260" width="120" height="40" rx="8" fill="#f8fafc" stroke="#64748b" strokeWidth="1.5" />
  <text x="165" y="285" fontSize="13" fill="#1e293b" textAnchor="middle">light</text>
</svg>

Las páginas más grandes aguantan bien. Los sitios con varias secciones construidos con el elemento Page de Carrd mantienen el tema automáticamente. Todo es un único documento, así que `data-theme` y `localStorage` persisten entre secciones.

Los scripts del head se ejecutan antes del pintado por especificación, así que la solución es sólida en principio. El orden exacto de inyección de Carrd aun así merece una confirmación en tu propio sitio publicado. Mira la checklist de pruebas más abajo.

## Cómo Añadir Modo Oscuro a Carrd (Paso a Paso)

Dos embeds y luego eliges uno de los tres estilos de alternador. Cada bloque de abajo se puede copiar y pegar.

### Paso 1: Añade un Embed en Head para Evitar el Destello Blanco

Haz clic en el signo `+` (o More Actions → Elements…) y añade un elemento Embed. Pon Type en Code, Style en Hidden y Location en Head.

<svg viewBox="0 0 520 230" width="100%" role="img" aria-label="Elemento Embed de Carrd con estilo Hidden y ubicación Head seleccionada">
  <rect x="10" y="10" width="500" height="210" rx="10" fill="#ffffff" stroke="#cbd5e1" strokeWidth="1.5" />
  <text x="30" y="42" fontSize="15" fontWeight="700" fill="#1e293b">Embed: Code</text>
  <text x="30" y="82" fontSize="13" fill="#64748b">Style</text>
  <circle cx="120" cy="77" r="8" fill="#ffffff" stroke="#64748b" strokeWidth="1.5" />
  <text x="136" y="82" fontSize="13" fill="#1e293b">Inline</text>
  <circle cx="230" cy="77" r="8" fill="#4f46e5" stroke="#4f46e5" strokeWidth="1.5" />
  <circle cx="230" cy="77" r="3.5" fill="#ffffff" />
  <text x="246" y="82" fontSize="13" fill="#1e293b">Hidden</text>
  <text x="30" y="132" fontSize="13" fill="#64748b">Location</text>
  <circle cx="130" cy="127" r="8" fill="#4f46e5" stroke="#4f46e5" strokeWidth="1.5" />
  <circle cx="130" cy="127" r="3.5" fill="#ffffff" />
  <text x="146" y="132" fontSize="13" fill="#1e293b">Head</text>
  <circle cx="250" cy="127" r="8" fill="#ffffff" stroke="#64748b" strokeWidth="1.5" />
  <text x="266" y="132" fontSize="13" fill="#1e293b">Body Top</text>
  <circle cx="390" cy="127" r="8" fill="#ffffff" stroke="#64748b" strokeWidth="1.5" />
  <text x="406" y="132" fontSize="13" fill="#1e293b">Body End</text>
  <text x="30" y="182" fontSize="12" fill="#64748b">Style: Hidden con Location: Head (la corrección del FOUC depende de ello)</text>
</svg>

<Notice type="warning" title="Style: Hidden, Location: Head (no Inline)">
La corrección del destello blanco necesita un embed Hidden dirigido al Head. Una configuración solo Inline lo hace imposible, porque el código Inline se renderiza en el cuerpo de la página después del pintado. Usaremos ambos estilos: Hidden → Head para el bootstrap, e Inline (o Hidden → Body End) para el alternador.
</Notice>

Pega esto en el campo Code:

```html
<script>
(function () {
  var t;
  try { t = localStorage.getItem('theme'); } catch (e) {}
  if (!t) t = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
  document.documentElement.setAttribute('data-theme', t);
})();
</script>
<style>
  :root { color-scheme: light; }
  [data-theme="dark"] { color-scheme: dark; }
</style>
```

Qué hace:

- Lee `localStorage.theme` dentro de `try/catch`, de modo que los modos privados que lanzan error al acceder al almacenamiento no rompan la página
- Recurre a `prefers-color-scheme: dark` cuando el visitante nunca eligió un tema
- Establece `data-theme` en la raíz del documento antes del primer pintado
- Emite `color-scheme: light|dark` para que los controles de formulario nativos, las barras de scroll y el autocompletado sigan el tema (`color-scheme` está soportado en Chrome/Edge 81+, Firefox 96+, Safari 13+)

Carrd no inyecta ninguna pista de `color-scheme` en los sitios publicados. Revisé el HTML en bruto de un ejemplo publicado en septiembre de 2026: no hay nada. El snippet lo establece por sí mismo. No des por hecho que la plataforma lo hace por ti.

**Comprueba:** publica y abre el sitio en una ventana de incógnito en un equipo o móvil configurado en modo oscuro. La página debería cargar oscura sin destello blanco. Si aún ves un destello, el embed de head falta, está mal ubicado, o estás viendo una página en caché (fuerza la recarga).

### Paso 2: Añade el Embed del Alternador de Modo Oscuro

Añade un segundo elemento Embed. Pon Type en Code. Para Style, elige Inline (para ver dónde queda en el diseño) o Hidden con Location: Body End (el alternador es `position: fixed`, así que acaba en el mismo sitio de todos modos).

<Picture formats={["webp"]} fallbackFormat="webp" src={imag1} alt="Editor de Carrd con el elemento Embed añadido y Type en Code" />

La captura muestra la ubicación Head del Paso 1. Para este segundo embed, elige Inline, o mantén Hidden y cambia la ubicación a Body End.

En el editor el embed sigue invisible. Publica para verlo. Luego elige uno de los tres estilos de alternador de abajo y pégalo en este segundo embed.

El bloque `<style>` de cada opción lleva los colores oscuros, y se carga con este segundo embed en el body. Si aún pillas un breve frame claro en una conexión lenta, recorta la parte `<style>…</style>` de tu opción elegida y pégala en el embed de Head del Paso 1, debajo del código existente. El botón y el `<script>` se quedan aquí.

### Opción 1: Botón Alternador Flotante

Mi opción por defecto. Un pequeño botón fijo en la esquina, funciona con cualquier diseño y no estorba. Esta versión alterna un único emoji sol/luna, mantiene `aria-pressed` sincronizado, limita las transiciones para que el primer pintado sea instantáneo, respeta `prefers-reduced-motion`, sobrevive a los errores de almacenamiento en modo privado, sigue los cambios de tema del sistema en vivo y añade una transición de fundido cruzado con view-transition donde el navegador lo soporta.

```html
<style>
  /* Light mode (default) colors */
  :root {
    --dm-bg-primary: #ffffff;
    --dm-bg-secondary: #f8fafc;
    --dm-text-primary: #1e293b;
    --dm-text-secondary: #64748b;
    --dm-border: #e2e8f0;
    --dm-accent: #3b82f6;
  }

  /* Dark mode colors */
  [data-theme="dark"] {
    --dm-bg-primary: #0f172a;
    --dm-bg-secondary: #1e293b;
    --dm-text-primary: #f1f5f9;
    --dm-text-secondary: #94a3b8;
    --dm-border: #334155;
    --dm-accent: #60a5fa;
  }

  /* Apply colors to Carrd elements */
  [data-theme="dark"] body,
  [data-theme="dark"] #wrapper,
  [data-theme="dark"] #main,
  [data-theme="dark"] section {
    background-color: var(--dm-bg-primary) !important;
  }

  [data-theme="dark"] h1,
  [data-theme="dark"] h2,
  [data-theme="dark"] h3,
  [data-theme="dark"] h4,
  [data-theme="dark"] p,
  [data-theme="dark"] span,
  [data-theme="dark"] li,
  [data-theme="dark"] a:not(.button) {
    color: var(--dm-text-primary) !important;
  }

  /* Style form inputs */
  [data-theme="dark"] input,
  [data-theme="dark"] textarea,
  [data-theme="dark"] select {
    background-color: var(--dm-bg-secondary) !important;
    color: var(--dm-text-primary) !important;
    border-color: var(--dm-border) !important;
  }

  /* Smooth transitions, scoped so the first paint is instant */
  @media (prefers-reduced-motion: no-preference) {
    [data-theme-ready] body,
    [data-theme-ready] #wrapper,
    [data-theme-ready] #main,
    [data-theme-ready] section,
    [data-theme-ready] h1,
    [data-theme-ready] h2,
    [data-theme-ready] h3,
    [data-theme-ready] h4,
    [data-theme-ready] p,
    [data-theme-ready] span,
    [data-theme-ready] li,
    [data-theme-ready] a:not(.button),
    [data-theme-ready] input,
    [data-theme-ready] textarea {
      transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
    }
  }

  /* Toggle button */
  .theme-toggle {
    position: fixed;
    bottom: 25px;
    left: 25px;
    width: 50px;
    height: 50px;
    border-radius: 50%;
    border: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;
    z-index: 9999;
    background: var(--dm-bg-secondary);
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
    transition: transform 0.2s, background-color 0.3s;
  }

  .theme-toggle:hover {
    transform: scale(1.1);
  }

  .theme-toggle:focus-visible {
    outline: 2px solid var(--dm-accent);
    outline-offset: 2px;
  }
</style>

<button class="theme-toggle" id="themeToggle" aria-label="Toggle dark mode" aria-pressed="false">
  <span aria-hidden="true">☀️</span>
</button>

<script>
(function () {
  var toggle = document.getElementById('themeToggle');

  function apply(theme) {
    document.documentElement.setAttribute('data-theme', theme);
    document.documentElement.setAttribute('data-theme-ready', '');
    toggle.setAttribute('aria-pressed', theme === 'dark' ? 'true' : 'false');
    toggle.firstElementChild.textContent = theme === 'dark' ? '🌙' : '☀️';
  }

  function current() {
    return document.documentElement.getAttribute('data-theme') || 'light';
  }

  apply(current());

  toggle.addEventListener('click', function () {
    var next = current() === 'dark' ? 'light' : 'dark';
    try { localStorage.setItem('theme', next); } catch (e) {}
    if (document.startViewTransition) {
      document.startViewTransition(function () { apply(next); });
    } else {
      apply(next);
    }
  });

  window.matchMedia('(prefers-color-scheme: dark)')
    .addEventListener('change', function (e) {
      var saved = null;
      try { saved = localStorage.getItem('theme'); } catch (err) {}
      if (!saved) apply(e.matches ? 'dark' : 'light');
    });
})();
</script>
```

Notas sobre las partes clave:

- Las transiciones solo se aplican después de que `data-theme-ready` se establezca en la raíz, así que el tema inicial no se anima.
- `document.startViewTransition` es una mejora progresiva (Chrome/Edge 111+, Firefox 144+, Safari 18+). Los navegadores sin soporte simplemente cambian al instante.
- El listener de cambio del sistema solo actúa cuando el visitante no tiene elección guardada. Una vez que pulsa el alternador, su elección manda hasta que la borre.
- Los emoji se renderizan de forma distinta en Windows y en Android antiguos. Si quieres iconos con píxeles consistentes, sustituye los caracteres `☀️` / `🌙` por formas SVG en línea de sol y luna.

**Comprueba:** pulsa el alternador y los colores deberían fundirse. Recarga y el tema debería mantenerse (`localStorage.theme` contiene `light` o `dark`). Navega con Tab hasta el botón y deberías ver el anillo de foco, con `aria-pressed` cambiando en DevTools → Elements.

### Opción 2: Interruptor en la Cabecera

Un interruptor tipo píldora que puedes colocar cerca de la cabecera. Las mismas correcciones compartidas de la Opción 1: tratamiento del texto `a:not(.button)`, transiciones limitadas a `[data-theme-ready]`, `try/catch` alrededor del almacenamiento y un listener de cambio del sistema.

Una trampa común en snippets antiguos de este patrón: `*, *::before, *::after { transition: … }`. Eso anima toda la página en el primer pintado y lastra los diseños pesados. La regla de abajo está limitada a propósito. Si toda tu página se anima al cargar, dejaste la regla de transición global.

```html
<style>
  /* Light mode colors */
  :root {
    --dm-bg: #ffffff;
    --dm-bg-alt: #f3f4f6;
    --dm-text: #111827;
    --dm-text-muted: #6b7280;
    --dm-border: #e5e7eb;
  }

  /* Dark mode colors */
  [data-theme="dark"] {
    --dm-bg: #111827;
    --dm-bg-alt: #1f2937;
    --dm-text: #f9fafb;
    --dm-text-muted: #9ca3af;
    --dm-border: #374151;
  }

  /* Apply to page */
  [data-theme="dark"] body,
  [data-theme="dark"] #wrapper,
  [data-theme="dark"] #main,
  [data-theme="dark"] section {
    background-color: var(--dm-bg) !important;
  }

  [data-theme="dark"] h1,
  [data-theme="dark"] h2,
  [data-theme="dark"] h3,
  [data-theme="dark"] h4,
  [data-theme="dark"] p,
  [data-theme="dark"] span,
  [data-theme="dark"] li,
  [data-theme="dark"] a:not(.button) {
    color: var(--dm-text) !important;
  }

  [data-theme="dark"] input,
  [data-theme="dark"] textarea {
    background-color: var(--dm-bg-alt) !important;
    color: var(--dm-text) !important;
    border-color: var(--dm-border) !important;
  }

  /* Transitions scoped to post-load state */
  @media (prefers-reduced-motion: no-preference) {
    [data-theme-ready] body,
    [data-theme-ready] #wrapper,
    [data-theme-ready] #main,
    [data-theme-ready] section,
    [data-theme-ready] h1,
    [data-theme-ready] h2,
    [data-theme-ready] h3,
    [data-theme-ready] h4,
    [data-theme-ready] p,
    [data-theme-ready] span,
    [data-theme-ready] li,
    [data-theme-ready] a:not(.button),
    [data-theme-ready] input,
    [data-theme-ready] textarea {
      transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
    }
  }

  /* Toggle switch container */
  .switch-container {
    position: fixed;
    top: 20px;
    right: 20px;
    display: flex;
    align-items: center;
    gap: 10px;
    z-index: 9999;
    background: var(--dm-bg-alt);
    padding: 8px 15px;
    border-radius: 50px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  }

  .switch-label {
    font-size: 18px;
  }

  .switch {
    position: relative;
    width: 56px;
    height: 28px;
  }

  .switch input {
    opacity: 0;
    width: 0;
    height: 0;
  }

  .switch input:focus-visible + .switch-slider {
    outline: 2px solid #3b82f6;
    outline-offset: 2px;
  }

  .switch-slider {
    position: absolute;
    cursor: pointer;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: #e5e7eb;
    border-radius: 28px;
    transition: 0.3s;
  }

  .switch-slider:before {
    position: absolute;
    content: "";
    height: 22px;
    width: 22px;
    left: 3px;
    bottom: 3px;
    background-color: white;
    border-radius: 50%;
    transition: 0.3s;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
  }

  input:checked + .switch-slider {
    background-color: #3b82f6;
  }

  input:checked + .switch-slider:before {
    transform: translateX(28px);
  }
</style>

<div class="switch-container">
  <span class="switch-label" aria-hidden="true">☀️</span>
  <label class="switch">
    <input type="checkbox" id="themeSwitch" role="switch" aria-label="Toggle dark mode">
    <span class="switch-slider"></span>
  </label>
  <span class="switch-label" aria-hidden="true">🌙</span>
</div>

<script>
(function () {
  var themeSwitch = document.getElementById('themeSwitch');

  function apply(theme) {
    document.documentElement.setAttribute('data-theme', theme);
    document.documentElement.setAttribute('data-theme-ready', '');
    themeSwitch.checked = (theme === 'dark');
    themeSwitch.setAttribute('aria-checked', theme === 'dark' ? 'true' : 'false');
  }

  function current() {
    return document.documentElement.getAttribute('data-theme') || 'light';
  }

  apply(current());

  themeSwitch.addEventListener('change', function () {
    var next = themeSwitch.checked ? 'dark' : 'light';
    try { localStorage.setItem('theme', next); } catch (e) {}
    apply(next);
  });

  window.matchMedia('(prefers-color-scheme: dark)')
    .addEventListener('change', function (e) {
      var saved = null;
      try { saved = localStorage.getItem('theme'); } catch (err) {}
      if (!saved) apply(e.matches ? 'dark' : 'light');
    });
})();
</script>
```

El checkbox oculto lleva `role="switch"` y `aria-label="Toggle dark mode"`, con `aria-checked` sincronizado. Sin esa etiqueta no tiene nombre accesible.

### Opción 3: Alternador de Icono Minimalista

Un pequeño círculo con borde que intercambia sol y luna. Variables de paleta con nombres más ligeros si prefieres ese estilo. Las mismas correcciones compartidas que las otras dos.

```html
<style>
  /* Theme colors */
  :root {
    --page-bg: #ffffff;
    --page-bg-alt: #f9fafb;
    --page-text: #18181b;
    --page-text-muted: #71717a;
  }

  [data-theme="dark"] {
    --page-bg: #18181b;
    --page-bg-alt: #27272a;
    --page-text: #fafafa;
    --page-text-muted: #a1a1aa;
  }

  [data-theme="dark"] body,
  [data-theme="dark"] #wrapper,
  [data-theme="dark"] #main,
  [data-theme="dark"] section {
    background-color: var(--page-bg) !important;
  }

  [data-theme="dark"] h1,
  [data-theme="dark"] h2,
  [data-theme="dark"] h3,
  [data-theme="dark"] h4,
  [data-theme="dark"] p,
  [data-theme="dark"] span,
  [data-theme="dark"] li,
  [data-theme="dark"] a:not(.button) {
    color: var(--page-text) !important;
  }

  [data-theme="dark"] input,
  [data-theme="dark"] textarea {
    background-color: var(--page-bg-alt) !important;
    color: var(--page-text) !important;
    border-color: var(--page-text-muted) !important;
  }

  @media (prefers-reduced-motion: no-preference) {
    [data-theme-ready] body,
    [data-theme-ready] #wrapper,
    [data-theme-ready] #main,
    [data-theme-ready] section,
    [data-theme-ready] h1,
    [data-theme-ready] h2,
    [data-theme-ready] h3,
    [data-theme-ready] h4,
    [data-theme-ready] p,
    [data-theme-ready] span,
    [data-theme-ready] li,
    [data-theme-ready] a:not(.button),
    [data-theme-ready] input,
    [data-theme-ready] textarea {
      transition: background-color 0.25s ease, color 0.25s ease;
    }
  }

  /* Minimal toggle */
  .minimal-toggle {
    position: fixed;
    top: 20px;
    right: 20px;
    width: 44px;
    height: 44px;
    border: 2px solid var(--page-text);
    border-radius: 50%;
    background: transparent;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 20px;
    z-index: 9999;
    transition: transform 0.3s, background-color 0.3s;
  }

  .minimal-toggle:hover {
    background: var(--page-bg-alt);
    transform: rotate(20deg);
  }

  .minimal-toggle:focus-visible {
    outline: 2px solid var(--page-text-muted);
    outline-offset: 2px;
  }

  .minimal-toggle .sun-icon {
    display: block;
  }

  .minimal-toggle .moon-icon {
    display: none;
  }

  [data-theme="dark"] .minimal-toggle .sun-icon {
    display: none;
  }

  [data-theme="dark"] .minimal-toggle .moon-icon {
    display: block;
  }
</style>

<button class="minimal-toggle" id="minimalToggle" aria-label="Toggle dark mode" aria-pressed="false">
  <span class="sun-icon" aria-hidden="true">☀️</span>
  <span class="moon-icon" aria-hidden="true">🌙</span>
</button>

<script>
(function () {
  var minimalToggle = document.getElementById('minimalToggle');

  function apply(theme) {
    document.documentElement.setAttribute('data-theme', theme);
    document.documentElement.setAttribute('data-theme-ready', '');
    minimalToggle.setAttribute('aria-pressed', theme === 'dark' ? 'true' : 'false');
  }

  function current() {
    return document.documentElement.getAttribute('data-theme') || 'light';
  }

  apply(current());

  minimalToggle.addEventListener('click', function () {
    var next = current() === 'dark' ? 'light' : 'dark';
    try { localStorage.setItem('theme', next); } catch (e) {}
    if (document.startViewTransition) {
      document.startViewTransition(function () { apply(next); });
    } else {
      apply(next);
    }
  });

  window.matchMedia('(prefers-color-scheme: dark)')
    .addEventListener('change', function (e) {
      var saved = null;
      try { saved = localStorage.getItem('theme'); } catch (err) {}
      if (!saved) apply(e.matches ? 'dark' : 'light');
    });
})();
</script>
```

**Comprueba (las tres opciones):** el Tab del teclado debería llegar al alternador con un anillo de foco visible. Es la parte que la mayoría de snippets se saltan y la que hace el control usable sin ratón.

## Personalizar los Colores del Modo Oscuro en Carrd

Las variables CSS de la parte superior de la Opción 1 (y el conjunto `--page-*` de la Opción 3) son los únicos colores que necesitas tocar.

<Tabs>
<Tab name="Variables CSS (usadas en esta guía)">
Dos bloques. Valores claros en `:root`, valores oscuros en `[data-theme="dark"]`:

```css
:root {
  --dm-bg-primary: #ffffff;     /* Main background */
  --dm-bg-secondary: #f8fafc;   /* Secondary background (cards, inputs) */
  --dm-text-primary: #1e293b;   /* Main text color */
  --dm-text-secondary: #64748b; /* Muted text */
  --dm-border: #e2e8f0;         /* Border color */
  --dm-accent: #3b82f6;         /* Accent/link color */
}

[data-theme="dark"] {
  --dm-bg-primary: #0f172a;     /* Dark background */
  --dm-bg-secondary: #1e293b;   /* Slightly lighter dark */
  --dm-text-primary: #f1f5f9;   /* Light text */
  --dm-text-secondary: #94a3b8; /* Muted light text */
  --dm-border: #334155;         /* Dark border */
  --dm-accent: #60a5fa;         /* Lighter accent for contrast */
}
```

Funciona en todas partes y te da control total sobre cada tema. El resto de la guía asume esta configuración.
</Tab>
<Tab name="Alternativa light-dark()">
Define cada color una vez y deja que `color-scheme` elija el ganador:

```css
:root {
  --dm-bg-primary: light-dark(#ffffff, #0f172a);
  --dm-bg-secondary: light-dark(#f8fafc, #1e293b);
  --dm-text-primary: light-dark(#1e293b, #f1f5f9);
  --dm-accent: light-dark(#3b82f6, #60a5fa);
}
```

Menos CSS, pero solo en navegadores nuevos. El alternador igualmente tiene que invertir `color-scheme` (el Paso 1 ya lo establece). Usa esto si no te importan las versiones antiguas de Safari.
</Tab>
</Tabs>

<Notice type="info" title="Soporte de light-dark() en navegadores">
Chrome 123+, Firefox 120+, Safari 17.5+ (Baseline 2024, comprobado en septiembre de 2026). Se puede usar ahora que el Paso 1 establece `color-scheme`. Mantén la versión de variables CSS si necesitas Safari antiguo.
</Notice>

Para más personalización CSS de copiar y pegar en Carrd, mira los [diseños personalizados de temporizador de cuenta regresiva en Carrd](https://www.bitdoze.com/es/estilos-cuenta-regresiva-carrd/).

### Consejos de Diseño para el Modo Oscuro

Prueba el contraste. El texto debe seguir siendo legible sobre el fondo oscuro. Pasa la paleta por un comprobador de contraste cuando tengas dudas.

No uses negro puro. `#000000` es duro a la vista. Usa grises oscuros como `#0f172a` o `#1e293b` en su lugar.

Aclara tu color de acento. Los colores que funcionan sobre blanco suelen verse apagados sobre oscuro. `#3b82f6` se convierte en `#60a5fa` en la paleta de arriba por exactamente esa razón.

Revisa cada sección. Recorre toda la página en ambos temas. Algunos elementos necesitarán selectores extra (siguiente sección).

Ten en cuenta las imágenes. Los logos y gráficos diseñados para fondos blancos pueden necesitar una segunda versión, un filtro o un marco sutil.

## Elementos e Imágenes Específicos de Carrd

Carrd genera selectores estructurales estables: `#wrapper`, `#main` y `.inner` están presentes en los sitios publicados (verificado en el HTML en bruto de un ejemplo publicado en septiembre de 2026). Los nombres de clase específicos de cada plantilla varían, así que los elementos rebeldes necesitan un apuntado extra.

```css
/* Target specific Carrd elements */
[data-theme="dark"] .inner,
[data-theme="dark"] .container,
[data-theme="dark"] [class*="style1"],
[data-theme="dark"] [class*="style2"] {
  background-color: var(--dm-bg-primary) !important;
  color: var(--dm-text-primary) !important;
}

/* Target Carrd buttons (ul.buttons > li > a.button); a:not(.button) above only covers text links */
[data-theme="dark"] .buttons .button {
  background-color: var(--dm-accent) !important;
}

/* Target Carrd icons (ul.icons > li > a > svg) */
[data-theme="dark"] .icons svg {
  filter: brightness(0) invert(1);
}
```

<Notice type="warning" title="Varianza entre plantillas">
Los nombres de clase `style1`…`style5` difieren según la plantilla, y trucos como `[data-theme="dark"] .inner > * { background: transparent !important }` pueden borrar los fondos de botones y tarjetas en algunas plantillas. Prueba en la tuya. Si los botones pierden su fondo en modo oscuro, quita esa regla primero. Nada de esto es universal. Verifica cada override en tu página publicada.
</Notice>

Las imágenes de fondo no responden a los overrides de `background-color`. Si una sección se niega a oscurecer, la culpable es probablemente una imagen de fondo. Cámbiala, o atenúala con una capa superpuesta:

```css
[data-theme="dark"] img {
  opacity: 0.9;
  filter: brightness(0.95);
}

/* Or give images a subtle frame */
[data-theme="dark"] img {
  border: 1px solid var(--dm-border);
  border-radius: 8px;
}
```

Si tienes una cabecera sticky, espera un choque: una barra de cabecera todavía clara en una página por lo demás oscura. La guía del [sticky header para Carrd](https://www.bitdoze.com/add-stickey-header-carrd/) muestra qué selectores tocar para que la barra se oscurezca con el resto.

## Posicionar el Botón Alternador

Cambia la posición editando estas propiedades CSS en tu clase del alternador:

Abajo a la izquierda (por defecto en la Opción 1):

```css
bottom: 25px;
left: 25px;
```

Arriba a la derecha:

```css
top: 25px;
right: 25px;
```

Abajo a la derecha:

```css
bottom: 25px;
right: 25px;
```

Sobre el z-index: los snippets usan `z-index: 9999` para que el alternador flote sobre el contenido. Eso puede chocar con el botón de menú móvil y los iconos sociales de Carrd. Si el alternador tapa algo pulsable, baja el z-index o mueve el botón a la esquina opuesta. Lo mismo si además tienes un control fijo de volver arriba. Mira [nuestra guía para añadir un botón de volver arriba en Carrd](https://www.bitdoze.com/carrd-back-to-top-button/) y evita que los dos se apilen en una esquina. En diseños móviles en particular, comprueba dos veces que nada quede bajo el alternador después de [crear una barra de navegación responsive en Carrd](https://www.bitdoze.com/carrd-mobile-navbar/).

## Probar y Solucionar Problemas del Alternador de Modo Oscuro en Carrd

Los embeds no renderizan nada en el editor, así que cada comprobación de abajo se hace en el sitio publicado.

<ListCheck>
<ul>
<li>Publica y luego abre el sitio en una ventana de incógnito en un sistema configurado en oscuro: el tema se aplica sin destello blanco</li>
<li>Pulsa el alternador y recarga: la elección persiste en `localStorage` (clave `theme`)</li>
<li>La consola de DevTools está limpia (sin `Uncaught SecurityError` de `localStorage` en modo privado)</li>
<li>El Tab del teclado llega al alternador y el anillo de foco es visible</li>
<li>`aria-pressed` (o `aria-checked` en la Opción 2) cambia en el panel Elements al alternar</li>
<li>Sin elección guardada, cambia el tema del SO en vivo: la página lo sigue</li>
</ul>
</ListCheck>

Si algo se comporta mal:

<Accordion label="El destello blanco sigue apareciendo" group="troubleshooting" expanded="true">
El bootstrap del head falta, está en la ubicación equivocada (debe ser Style: Hidden, Location: Head), o estás viendo una copia en caché. Confirma que el embed existe con el estilo y la ubicación correctos, luego fuerza la recarga o prueba en una ventana privada. Verifica también una vez en tu propio sitio publicado que el código del head se ejecuta antes del pintado. El orden de inyección de Carrd merece una única comprobación de extremo a extremo, ya que el editor no te lo muestra. Si el embed de head está bien y queda un destello corto, mueve el bloque `<style>` del alternador también al embed de head (ver Paso 2).
</Accordion>

<Accordion label="El alternador hace clic pero nada cambia" group="troubleshooting">
Tres sospechosos habituales: un error de script en la consola (pega de nuevo todo el bloque de código; las etiquetas sin cerrar son la causa común), los selectores CSS oscuros que no coinciden con tu plantilla, o el sitio que no se publicó de verdad después de tu edición. Mira la consola primero, luego inspecciona el elemento `<html>`. `data-theme` debería cambiar al hacer clic.
</Accordion>

<Accordion label="Los botones o las tarjetas perdieron su fondo en modo oscuro" group="troubleshooting">
Es la regla `[data-theme="dark"] .inner > * { background: transparent !important }` haciendo de las suyas. Depende de la plantilla y suele ser demasiado agresiva. Quítala y dale a `.button` y a los contenedores de tarjetas sus propios overrides de fondo en su lugar.
</Accordion>

<Accordion label="El alternador tapa el menú móvil o los iconos sociales" group="troubleshooting">
`z-index: 9999` más una posición en esquina se solapa con la interfaz de Carrd en pantallas pequeñas. Baja el z-index, o recoloca el alternador en una esquina libre (ver la sección de posicionamiento). Prueba al ancho de un móvil, no solo en escritorio.
</Accordion>

<Accordion label="Algunas secciones nunca cambian de color" group="troubleshooting">
O la plantilla usa clases `styleN` que tus selectores no cubren, o el fondo de la sección es una imagen. Inspecciona el elemento, añade su clase al bloque CSS oscuro, y cambia o atenúa las imágenes de fondo. Los iconos SVG en línea también ignoran los overrides de color. Recolóralos con reglas `fill`/`stroke` o un `filter`.
</Accordion>

<Accordion label="La preferencia no se guarda o hay Uncaught SecurityError en la consola" group="troubleshooting">
Los modos de navegación privada pueden bloquear `localStorage`. El código envuelve cada lectura/escritura en `try/catch`, así que el alternador sigue funcionando durante la sesión. Simplemente no puede recordar la elección. No hay nada que arreglar; la degradación es intencionada.
</Accordion>

<Notice type="info" title="Volver atrás">
Nada fuera de los dos embeds queda modificado. Borra ambos elementos Embed y publica: el sitio vuelve a su estado original. Para limpiar la preferencia atascada de un visitante, abre DevTools → Application → Local Storage y elimina la clave `theme`.
</Notice>

## FAQ: Preguntas sobre el Modo Oscuro en Carrd

<Accordion label="¿Carrd tiene modo oscuro integrado?" group="faq" expanded="true">
No. A septiembre de 2026 no existe ninguna función de cambio de tema en Carrd. Revisé la documentación y el changelog. Un embed de código personalizado es el camino, y esta guía es la vía de copiar y pegar.
</Accordion>

<Accordion label="¿Por qué mi sitio Carrd parpadea en blanco antes de cargar el modo oscuro?" group="faq">
Eso es FOUC (flash of unstyled content), en este caso un destello del tema equivocado. Un embed Inline único aplica el modo oscuro desde JavaScript después del primer pintado, así que los usuarios de modo oscuro ven blanco primero. El bootstrap Hidden → Head del Paso 1 establece `data-theme` antes del pintado y elimina el destello.
</Accordion>

<Accordion label="¿Qué plan de Carrd necesito para el modo oscuro?" group="faq">
Pro Standard (19 $/año para 10 sitios) o superior, porque los Embeds son el mecanismo de entrega. Pro Lite (9 $/año) no tiene embeds en absoluto. Pro Plus (49 $/año) solo merece la pena si necesitas sus ajustes avanzados. Carrd ofrece una prueba Pro gratuita de 7 días sin tarjeta si solo quieres montar esto una vez. Los precios son de septiembre de 2026; revísalos en carrd.co/pro.
</Accordion>

<Accordion label="¿Funciona el alternador de modo oscuro en un sitio Carrd de varias secciones?" group="faq">
Sí. Las secciones hechas con el elemento Page de Carrd comparten un único documento, así que `data-theme` y `localStorage` persisten mientras los visitantes se mueven. Un alternador de modo oscuro en Carrd cubre todo el sitio. Si además quieres una acción flotante de la misma familia de botones fijos, [añade un botón de chat de WhatsApp a Carrd](https://www.bitdoze.com/carrd-whatsapp-button/).
</Accordion>

<Button link="https://go.bitdoze.com/carrd" text="Prueba Carrd.co" />

## Conclusión

El modo oscuro se espera a estas alturas, sobre todo por los visitantes que navegan de noche. El montaje completo son dos embeds y un snippet de alternador. Unos 10 minutos si pegas con cuidado. Empieza con la Opción 1 (el botón flotante) si no sabes qué estilo encaja. Funciona con cualquier diseño y no estorba.

Un par de pasos siguientes una vez el alternador esté en marcha:

- [Conecta un dominio personalizado a tu sitio Carrd](https://www.bitdoze.com/carrd-add-domain/) y deja de repartir la URL `*.carrd.co`
- [Añade una tabla de precios a Carrd](https://www.bitdoze.com/carrd-add-pricing-table/) si la página vende algo
- Recorre el resto de la lista de tutoriales de Carrd al principio de este post. Los popups, el scroll suave y los menús flotante y lateral siguen el mismo patrón de embed