Indaga FAQs

padding es el espacio interior entre el contenido de un elemento y su borde. margin es el espacio exterior, entre el borde del elemento y los elementos vecinos. Un truco para recordarlo: el padding "empuja" el contenido hacia dentro; el margin "empuja" el elemento hacia fuera de los demás.

Un ejemplo con un botón ayuda a verlo claro:

.boton {
    padding: 10px 20px;
    margin: 15px;
    background: #1a1a1a;
    color: #fff;
}

Aquí padding: 10px 20px; aumenta el área clicable del botón y separa el texto de su propio borde (10px arriba/abajo, 20px a los lados), sin cambiar la posición del botón respecto a lo que le rodea. margin: 15px; hace justo lo contrario: separa el botón de otros elementos vecinos, sin tocar el espacio entre el texto y el borde del botón.

La notación abreviada sigue el mismo orden en ambas propiedades, empezando arriba y girando en el sentido del reloj: arriba derecha abajo izquierda. Así, padding: 10px 20px 10px 20px; equivale a padding: 10px 20px; (cuando arriba/abajo y derecha/izquierda coinciden, se pueden agrupar en solo dos valores).

Dos comportamientos que conviene tener en cuenta:

  • El padding aumenta el tamaño total del elemento (a menos que se use box-sizing: border-box), mientras que el margin nunca forma parte del tamaño del propio elemento: solo afecta al espacio que hay alrededor.
  • Los márgenes verticales se pueden "colapsar": si dos elementos seguidos tienen margin-bottom: 20px y margin-top: 20px respectivamente, el espacio real entre ambos no es de 40px, sino de 20px (se queda el mayor de los dos, no la suma). El padding nunca hace esto.

Un caso práctico donde la diferencia importa de verdad: si quieres que el fondo de color de una tarjeta llegue hasta el borde del elemento pero el texto quede separado de ese borde, usa padding. Si lo que quieres es separar una tarjeta de la siguiente, sin que el fondo de color se vea afectado, usa margin:

.tarjeta {
    background: #f7f7f7;
    padding: 20px;      /* espacio entre el fondo gris y el texto */
    margin-bottom: 24px; /* espacio entre esta tarjeta y la siguiente */
}

Flexbox es un sistema de maquetación pensado para distribuir elementos en una sola dirección (fila o columna), alineándolos y repartiendo el espacio sobrante entre ellos con facilidad. Se activa con display: flex en el contenedor. Conviene usarlo para barras de navegación, tarjetas en fila o cualquier grupo de elementos que necesite alinearse o repartirse de forma flexible; para maquetaciones en dos dimensiones (filas y columnas a la vez) suele ser mejor CSS Grid.

Un ejemplo típico, una barra de navegación con el logo a la izquierda y los enlaces a la derecha:

.navegacion {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

Aquí justify-content: space-between; empuja el primer elemento hijo hacia el extremo izquierdo y el último hacia el extremo derecho, repartiendo el espacio sobrante entre ellos. align-items: center; los alinea verticalmente entre sí, aunque tengan alturas distintas (por ejemplo, un logo más alto que el texto de un enlace). Sin necesidad de floats, ni de calcular anchos a mano.

Los dos ejes de Flexbox conviene tenerlos claros, porque casi todas las propiedades dependen de ellos:

  • Eje principal (el que marca flex-direction; por defecto, horizontal): se controla con justify-content (flex-start, center, space-between, space-around...).
  • Eje transversal (perpendicular al principal): se controla con align-items, que alinea los elementos en esa dirección.

Un segundo ejemplo, unas tarjetas en fila que se reparten el espacio disponible a partes iguales:

.tarjetas {
    display: flex;
    gap: 20px;
}

.tarjeta {
    flex: 1;
}

gap: 20px; añade separación entre las tarjetas sin tener que jugar con márgenes que luego hay que compensar en los extremos. flex: 1; en cada tarjeta hace

Es el criterio que usa el navegador para decidir qué regla aplica cuando varias afectan al mismo elemento. Cuanto más específico sea un selector, más "peso" tiene: un id pesa más que una clase, y una clase pesa más que una etiqueta HTML. Por ejemplo, #menu a gana a .navegacion a, y ambos ganan a un simple a. Cuando dos reglas tienen la misma especificidad, gana la que aparece después en el CSS.

El navegador calcula la especificidad como una puntuación con tres niveles, de más a menos peso:

  • IDs (#menu): el nivel con más peso.
  • Clases, atributos y pseudo-clases (.navegacion, [type="text"], :hover): peso intermedio.
  • Etiquetas y pseudo-elementos (a, div, ::before): el nivel con menos peso.

Un selector suma puntos de cada nivel que use. Así, #menu a tiene 1 ID + 1 etiqueta, y .navegacion a tiene 1 clase + 1 etiqueta: como un ID pesa más que cualquier cantidad de clases, el primero gana siempre, sin importar cuántas clases se añadan a la segunda regla.

Un ejemplo con un botón que recibe estilos de dos sitios distintos:

.boton {
    background: gray;
}

#cabecera .boton {
    background: black;
}

Aunque .boton aparezca después en el archivo, #cabecera .boton tiene más especificidad (1 ID + 1 clase, frente a 1 clase sola), así que el fondo final es negro. El orden en el CSS solo decide el empate cuando la especificidad es exactamente igual.

Este es justo el motivo por el que aparecen estilos que "no se aplican" aunque estén bien escritos: no es un error de sintaxis, es que otra regla, en otro punto del CSS, tiene más peso. Herramientas como el inspector del navegador muestran las reglas tachadas cuando pierden frente a otra más específica, lo cual es la forma más rápida de diagnosticar el problema.

Una recomendación práctica: cuantas menos capas de especificidad uses (evitar IDs para estilos y anidar selectores lo mínimo posible), más fácil es sobrescribir algo más adelante sin tener que aumentar la especificidad cada vez más para ganarle. Es el mismo motivo por el que conviene evitar !important salvo como último recurso: al ganar por encima de cualquier especificidad, si se abusa de él, deja de haber margen para forzar un estilo cuando de verdad hace falta.

relative desplaza el elemento respecto a su posición original en el flujo normal, sin sacarlo de ese flujo (el espacio que ocupaba sigue reservado). absolute saca el elemento del flujo por completo y lo posiciona respecto a su ancestro posicionado más cercano (el primero que tenga position distinto de static); si no hay ninguno, se posiciona respecto al documento entero. Por eso es habitual poner position: relative en el contenedor padre cuando se quiere controlar dónde "cuelga" un hijo con position: absolute.

Un ejemplo con relative a solas, para ver que el espacio original queda reservado:

.caja {
    position: relative;
    top: 10px;
    left: 20px;
}

La caja se dibuja visualmente 10px más abajo y 20px más a la derecha de donde estaría por defecto, pero el resto de elementos de la página siguen comportándose como si la caja siguiera en su sitio original: no se reajustan para "llenar" ese hueco. Es un desplazamiento puramente visual.

El caso más habitual, sin embargo, no es usar relative para desplazar algo, sino para convertir un contenedor en el "punto de referencia" de un hijo con absolute. Un ejemplo típico: una insignia sobre una tarjeta.

.tarjeta {
    position: relative;
}

.insignia {
    position: absolute;
    top: 10px;
    right: 10px;
}

Aquí .tarjeta no se mueve de sitio (el position: relative sin top/left no desplaza nada), pero al tener un position distinto de static, se convierte en el ancestro posicionado más cercano. Por eso .insignia se coloca a 10px del borde superior y 10px del borde derecho de la tarjeta, no de toda la página. Sin ese relative en el padre, la insignia buscaría el siguiente ancestro posicionado hacia arriba en el árbol, y si no encontrara ninguno, acabaría posicionándose respecto al <html> entero, probablemente en un sitio muy distinto al que se buscaba.

Esta es una fuente muy habitual de bugs de maquetación: un elemento con position: absolute que "se escapa" a una esquina inesperada de la página. La primera pregunta a hacerse siempre es: ¿tiene este elemento algún ancestro con position: relative (o absolute, o fixed) cerca? Si no lo tiene, ahí está el problema.

Dos matices más que conviene tener presentes:

  • fixed se comporta como absolute, pero siempre respecto a la ventana del navegador, no a ningún ancestro; por eso se usa para cabeceras o botones que deben quedarse fijos al hacer scroll.
  • sticky combina ambos: se comporta como relative hasta que, al hacer scroll, llega a un límite definido (por ejemplo, top: 0), momento en el que se "pega" ahí como si fuera fixed, dentro de los límites de su contenedor.

Es el modelo con el que el navegador calcula el tamaño de cada elemento: contenido, más padding, más border, más margin, de dentro hacia fuera. Por defecto, el ancho que se define con width es solo el del contenido; con box-sizing: border-box ese ancho pasa a incluir también el padding y el borde, lo cual suele hacer los cálculos más predecibles.

Un ejemplo numérico ayuda a ver por qué da tantos quebraderos de cabeza. Con el comportamiento por defecto (box-sizing: content-box):

.caja {
    width: 200px;
    padding: 20px;
    border: 5px solid black;
}

El ancho total que ocupa realmente esta caja en la página no son 200px, sino 200 + 20 + 20 + 5 + 5 = 250px (padding a ambos lados más borde a ambos lados). El margin ni siquiera entra en esta cuenta: se suma aparte, como espacio adicional fuera de esos 250px.

Con box-sizing: border-box, en cambio:

.caja {
    box-sizing: border-box;
    width: 200px;
    padding: 20px;
    border: 5px solid black;
}

Ahora los 200px ya incluyen el padding y el borde: es el navegador quien reduce el espacio disponible para el contenido (en este caso, el contenido se queda en 200 - 20 - 20 - 5 - 5 = 150px), pero la caja completa sigue midiendo exactamente 200px de ancho, sin sorpresas.

Esto se nota especialmente al maquetar varias columnas con porcentajes. Con content-box, tres columnas al 33.33% con algo de padding se desbordan y rompen la fila, porque el navegador añade el padding por encima de ese 33.33%. Con border-box, esas mismas tres columnas siguen ocupando exactamente el 33.33% cada una, padding incluido, y encajan sin desbordarse.

Por eso es tan habitual encontrarse esta regla casi al principio de cualquier hoja de estilos:

*, *::before, *::after {
    box-sizing: border-box;
}

Aplicarla a todos los elementos (incluidos los pseudo-elementos) desde el principio evita tener que acordarse de añadirla caja por caja, y es la convención que siguen la mayoría de frameworks y resets CSS modernos.

Por último, conviene recordar que el margin queda siempre fuera del cálculo del tamaño de la caja, sea content-box o border-box: nunca forma parte del "tamaño" del elemento, solo del espacio que reserva alrededor de él frente a sus vecinos (como se explicaba en la FAQ de margin y padding).

display: none elimina el elemento del flujo del documento: no ocupa espacio, como si no existiera. visibility: hidden lo oculta visualmente, pero sigue ocupando su espacio, dejando un hueco en la página. Para el acordeón de preguntas del plugin de FAQs, por ejemplo, uso display: none en las respuestas cerradas.

La diferencia se ve enseguida con tres cajas en fila, donde la del medio se oculta de una forma u otra:

.oculto-display   { display: none; }
.oculto-visibility { visibility: hidden; }

Con .oculto-display, las cajas primera y tercera se juntan, como si la del medio nunca hubiera existido: el navegador no reserva ningún espacio para ella. Con .oculto-visibility, en cambio, queda un hueco en blanco del tamaño exacto que ocupaba la caja del medio, y las otras dos no se mueven ni un píxel.

Esto marca cuál usar según el efecto que se busque:

  • display: none — para elementos que de verdad no deben afectar a la maquetación mientras estén ocultos: pestañas no seleccionadas, respuestas de un acordeón cerradas, un menú desplegable antes de abrirse.
  • visibility: hidden — para casos donde interesa reservar el espacio aunque el contenido no se vea todavía, por ejemplo un icono de "cargando" que aparecerá ahí mismo sin que el resto de la interfaz salte de sitio mientras tanto.

Un matiz importante con visibility: se hereda y se puede "recuperar" en los hijos. Si un contenedor tiene visibility: hidden, se puede volver a mostrar un elemento concreto dentro de él con visibility: visible, y ese hijo sí se verá aunque su padre siga oculto. Con display: none esto no es posible: si el padre no está en el flujo, ningún hijo puede estarlo, sin importar qué display se le ponga.

Aplicado al acordeón de preguntas del plugin de FAQs, el JavaScript alterna la clase o el estilo así:

document.querySelectorAll('.indaga-faq-pregunta').forEach(function (btn) {
    btn.addEventListener('click', function () {
        var respuesta = btn.nextElementSibling;
        var abierta = btn.getAttribute('aria-expanded') === 'true';
        respuesta.style.display = abierta ? 'none' : 'block';
    });
});

Se usa display: none a propósito, y no visibility: hidden: si se usara visibility, todas las respuestas cerradas seguirían ocupando su espacio en blanco, y la página se vería llena de huecos vacíos antes de desplegar nada, en vez de mostrar solo la lista compacta de preguntas.

Una tercera opción relacionada, aunque distinta de las dos anteriores, es opacity: 0: hace el elemento invisible pero, igual que visibility: hidden, sigue ocupando espacio y además sigue siendo clicable y accesible por teclado, a menos que se combine explícitamente con algo como pointer-events: none. Se usa sobre todo para animar apariciones y desapariciones con transition, algo que ni display ni visibility permiten animar directamente.

Son reglas condicionales que aplican estilos distintos según las características de la pantalla, sobre todo su ancho. Por ejemplo, @media (max-width: 600px) { ... } aplica esos estilos solo en pantallas de 600 píxeles o menos. Son la base del diseño responsive, para adaptar un sitio a móvil, tablet y escritorio.

Un ejemplo típico: una barra de navegación que pasa de horizontal a vertical en pantallas pequeñas.

.navegacion {
    display: flex;
    flex-direction: row;
}

@media (max-width: 600px) {
    .navegacion {
        flex-direction: column;
    }
}

Por defecto, los enlaces del menú se muestran en fila. En cuanto el ancho de la ventana baja de 600px, la media query sobrescribe esa propiedad y los apila en columna, sin necesidad de duplicar el HTML ni usar JavaScript.

Además de max-width, existe min-width, que aplica los estilos a partir de un ancho mínimo hacia arriba. Combinando ambos se pueden crear rangos concretos:

@media (min-width: 601px) and (max-width: 1024px) {
    /* estilos solo para tablet, aproximadamente */
}

Esto es lo que se conoce como breakpoints: los puntos de ancho donde el diseño cambia de comportamiento. No hay unos valores "oficiales" universales, pero es habitual definir dos o tres: uno para móvil, otro para tablet y otro para escritorio, ajustados al contenido real del sitio más que a modelos de dispositivo concretos, que cambian constantemente.

Un enfoque muy recomendable es escribir primero los estilos para móvil sin ninguna media query, y usar min-width para ir añadiendo complejidad a medida que crece la pantalla (lo que se conoce como diseño mobile-first):

.tarjetas {
    display: block; /* una columna, por defecto, para móvil */
}

@media (min-width: 768px) {
    .tarjetas {
        display: flex; /* varias columnas, a partir de tablet */
    }
}

Este orden suele dar hojas de estilo más simples que el enfoque contrario (diseñar primero para escritorio y luego ir "deshaciendo" cosas para pantallas pequeñas con max-width), porque parte de la versión más sencilla y añade complejidad progresivamente, en lugar de ir quitándola.

Las media queries no se limitan al ancho: también se pueden usar para adaptar estilos a la orientación del dispositivo (orientation: portrait), a si se va a imprimir la página (@media print), o para respetar las preferencias del sistema del usuario, como el modo oscuro con prefers-color-scheme: dark.

Es un sistema de maquetación en dos dimensiones (filas y columnas a la vez), pensado para estructuras completas de página, a diferencia de Flexbox, que trabaja mejor en una sola dirección. Se activa con display: grid en el contenedor, y columnas y filas se definen con propiedades como grid-template-columns.

Un ejemplo clásico: una galería de tarjetas repartida en tres columnas iguales.

.galeria {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 20px;
}

La unidad fr ("fracción") reparte el espacio disponible en partes proporcionales: 1fr 1fr 1fr crea tres columnas exactamente iguales, y gap: 20px; añade separación entre ellas y entre filas, sin necesidad de márgenes que luego haya que compensar en los bordes, igual que en Flexbox.

Donde Grid se nota de verdad, frente a Flexbox, es al definir columnas de anchos distintos y coordinados entre sí. Por ejemplo, una plantilla típica de página con barra lateral:

.pagina {
    display: grid;
    grid-template-columns: 250px 1fr;
    gap: 30px;
}

Esto crea dos columnas: una fija de 250px para la barra lateral, y otra que ocupa todo el espacio restante para el contenido principal. Si además hubiera varias filas (cabecera, contenido, pie), Grid mantiene esas mismas columnas alineadas verticalmente en todas las filas a la vez, algo que con Flexbox requeriría anidar varios contenedores flex por separado.

Una función muy útil es repeat(), para no repetir el mismo valor a mano:

.galeria {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
}

Y combinada con auto-fit y minmax(), se puede conseguir una cuadrícula responsive sin escribir ni una sola media query:

.galeria {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 20px;
}

Esto le dice al navegador: "encaja tantas columnas de al menos 200px como quepan, y reparte el espacio sobrante entre ellas". En una pantalla ancha caben cinco o seis columnas; en un móvil, puede que solo quepa una, y el propio navegador recalcula cuántas entran en cada momento, sin que haya que definir puntos de ruptura manualmente.

Grid también permite nombrar zonas de la plantilla, lo cual hace que el CSS sea casi autoexplicativo para maquetaciones de página completa:

.pagina {
    display: grid;
    grid-template-areas:
        "cabecera cabecera"
        "lateral  contenido"
        "pie      pie";
    grid-template-columns: 250px 1fr;
}

.cabecera  { grid-area: cabecera; }
.lateral   { grid-area: lateral; }
.contenido { grid-area: contenido; }
.pie       { grid-area: pie; }

Con este planteamiento, la propia declaración de grid-template-areas dibuja, literalmente, el esquema visual de la página, y cada bloque solo necesita indicar a qué zona pertenece, sin calcular posiciones ni tamaños a mano.

En resumen: si el contenido fluye en una sola dirección (una barra de botones, una fila de tarjetas que se envuelve), Flexbox suele bastar y es más simple. En cuanto se necesita coordinar filas y columnas a la vez (la estructura general de una página, una galería con huecos irregulares), Grid da mucho más control con menos código. No son excluyentes: es habitual usar Grid para la estructura general de la página, y Flexbox dentro de cada bloque para alinear sus elementos internos.

em es relativo al tamaño de fuente del elemento padre más cercano, lo que puede hacer que los tamaños se acumulen si hay elementos anidados. rem es siempre relativo al tamaño de fuente del elemento raíz (html), así que resulta más predecible al no depender del contexto donde se use.

El problema de acumulación con em se ve bien con listas anidadas, un caso clásico donde sorprende a cualquiera que lo prueba por primera vez:

li {
    font-size: 1.2em;
}

Si una lista tiene sublistas dentro, cada nivel de anidamiento multiplica el tamaño respecto a su propio padre, no respecto a la página: el primer nivel queda a 1.2 veces el tamaño base, el segundo nivel a 1.2 veces el tamaño del primero (es decir, 1.44 veces el original), el tercero a 1.2 veces el segundo (1.728 veces), y así sucesivamente. El texto crece en cascada sin que nadie lo haya pedido explícitamente.

Con rem, el mismo caso no tiene ese problema:

li {
    font-size: 1.2rem;
}

Da igual cuántos niveles de anidamiento haya: todos los <li> miden 1.2 veces el tamaño de fuente del <html>, ni más ni menos. Por eso rem se ha convertido en la unidad por defecto recomendada para tamaños de fuente en la mayoría de proyectos, precisamente para evitar sorpresas como la anterior.

em sigue teniendo sentido, sin embargo, cuando el objetivo es precisamente que algo escale junto con su elemento padre. Un botón es un buen ejemplo:

.boton {
    font-size: 1rem;
    padding: 0.75em 1.5em;
}

Aquí el padding se define en em a propósito, para que sea relativo al tamaño de fuente del propio botón: si en algún sitio ese botón necesita ser más grande (.boton--grande { font-size: 1.5rem; }), el padding crece proporcionalmente sin tener que recalcularlo a mano, porque escala junto con el texto que contiene.

Una diferencia práctica adicional, importante para accesibilidad: ambas unidades responden si el usuario cambia el tamaño de fuente por defecto del navegador (algo que hacen, por ejemplo, personas con dificultades de visión), a diferencia de px, que se queda fijo pase lo que pase. Por eso conviene evitar tamaños de fuente en píxeles y usar rem como base, reservando em para esos casos concretos donde interesa que algo escale junto con su contexto inmediato, como el padding de un botón o el espaciado interno de un icono.

Son palabras clave que se añaden a un selector para aplicar estilos según un estado o una posición concreta del elemento, sin necesidad de añadir clases extra en el HTML. Por ejemplo, a:hover aplica un estilo cuando el ratón pasa por encima de un enlace, y li:first-child selecciona solo el primer elemento de una lista.

Controla el orden de apilamiento de elementos que se superponen: un elemento con un z-index mayor se muestra por encima de otro con un valor menor. Solo funciona en elementos con un position distinto de static (relative, absolute, fixed o sticky).

Permite que un cambio de valor en una propiedad (color, tamaño, posición...) se anime suavemente en lugar de aplicarse de golpe. Por ejemplo, transition: transform .2s ease; hace que cualquier cambio en transform tarde dos décimas de segundo en completarse, como el icono que gira al abrir una pregunta en el plugin de FAQs.

px es una medida fija en píxeles, independiente del contexto. % es relativo al tamaño del elemento contenedor. vw y vh son relativos al ancho y alto de la ventana del navegador (1vw es el 1% del ancho visible), lo que resulta útil para que un elemento ocupe siempre una proporción de la pantalla, sea cual sea su tamaño.

Son valores reutilizables que se definen una vez y se usan en distintas reglas, como --color-principal: #1a1a1a; definido en :root, y luego usado con color: var(--color-principal);. A diferencia de los preprocesadores como Sass, son nativas del navegador y se pueden incluso cambiar dinámicamente con JavaScript.

Es un lenguaje que añade funcionalidades al CSS normal (variables, anidamiento de selectores, funciones, archivos parciales que se combinan en uno) y que luego se "compila" a CSS estándar para que el navegador lo entienda. Ayuda a organizar hojas de estilo grandes, aunque en proyectos pequeños las variables y el anidamiento nativos de CSS moderno cubren buena parte de esa necesidad sin herramientas adicionales.

Es la práctica de escribir primero los estilos pensados para pantallas pequeñas, y luego ampliar o adaptar el diseño para pantallas más grandes usando media queries con min-width. Suele dar hojas de estilo más simples que el enfoque contrario (diseñar para escritorio y luego "encoger" para móvil), porque parte de la versión más restringida y va añadiendo complejidad progresivamente.

A > B selecciona los elementos B que son hijos directos de A. A + B selecciona el elemento B que va inmediatamente después de A, al mismo nivel. A ~ B selecciona todos los elementos B que van después de A al mismo nivel, no solo el inmediato siguiente. Permiten aplicar estilos según la relación entre elementos, sin necesidad de añadir clases extra.

Controla qué ocurre cuando el contenido de un elemento no cabe dentro de su tamaño. overflow: hidden recorta lo que sobra sin mostrarlo. overflow: scroll añade barras de desplazamiento siempre. overflow: auto las añade solo si hace falta, que suele ser la opción más práctica para contenedores con contenido de longitud variable.

Es una transición suave entre dos o más colores, generada directamente por CSS sin necesidad de una imagen. Se usa normalmente como fondo, por ejemplo background: linear-gradient(to right, #1a1a1a, #444);, que crea un degradado horizontal de oscuro a gris. También existen gradientes radiales con radial-gradient().

Controla cómo se ajusta una imagen o un vídeo dentro de un contenedor de tamaño fijo, cuando sus proporciones no coinciden. object-fit: cover rellena todo el contenedor recortando lo que sobre, manteniendo la proporción. object-fit: contain muestra la imagen completa, dejando espacio vacío si hace falta. Es muy útil para galerías donde las imágenes originales tienen tamaños distintos.

Es un modificador que se añade a una propiedad para que gane a cualquier otra regla, saltándose las reglas normales de especificidad. El problema es que, si se abusa de él, cuando de verdad hace falta forzar un estilo ya no queda ningún recurso por encima, y depurar por qué un estilo no se aplica se vuelve mucho más difícil. Es preferible resolver los conflictos ajustando la especificidad del selector antes que recurrir a !important.

@font-face permite definir una fuente personalizada indicando su nombre y la ruta a los archivos de la tipografía, por ejemplo @font-face { font-family: "MiFuente"; src: url("mifuente.woff2"); }. Una vez definida, se usa como cualquier otra fuente con font-family: "MiFuente", sans-serif;, con una alternativa de reserva por si no llega a cargar.

Cambia el icono del puntero del ratón al pasar sobre un elemento, lo cual ayuda a comunicar si algo es interactivo. cursor: pointer muestra la manita típica de los enlaces, útil en elementos como botones estilizados que por defecto no la llevarían. cursor: not-allowed indica que una acción no está disponible en ese momento.

BEM (Block, Element, Modifier) es una convención para nombrar clases CSS de forma predecible: bloque__elemento--modificador. Por ejemplo, faq__pregunta--activa indica que es el elemento "pregunta" dentro del bloque "faq", con el modificador "activa". Ayuda a evitar colisiones de nombres y a entender la relación entre clases solo con leer el nombre.

Controla si los elementos dentro de un contenedor flex deben mantenerse en una sola línea, aunque no quepan (nowrap, el valor por defecto), o pasar a la línea siguiente cuando falte espacio (wrap). Es habitual combinarla con flex-wrap: wrap; en selectores de categorías o listados de tarjetas, para que se reorganicen bien en pantallas estrechas.

justify-content controla cómo se reparten los elementos a lo largo del eje principal del contenedor (normalmente horizontal), con valores como center o space-between. align-items controla cómo se alinean en el eje transversal (normalmente vertical), por ejemplo center para centrarlos verticalmente dentro de una fila.

Permiten insertar contenido generado por CSS antes o después del contenido real de un elemento, sin añadir una etiqueta extra en el HTML. Se usan junto con la propiedad content, por ejemplo .indaga-faq-icono::after { content: '+'; }, muy habituales para iconos decorativos o pequeños detalles visuales que no forman parte del contenido real.

Permite combinar unidades distintas en un único cálculo dentro de CSS, por ejemplo width: calc(100% - 40px);, que ocupa todo el ancho disponible menos 40 píxeles fijos. Evita tener que calcular ese valor a mano o depender de JavaScript para ajustes que combinan porcentajes con medidas fijas.

Es una media query que detecta si el usuario tiene activado el modo oscuro en su sistema operativo o navegador, permitiendo adaptar los estilos automáticamente: @media (prefers-color-scheme: dark) { ... }. Es la base para ofrecer un modo oscuro en una web sin necesidad de un interruptor manual, aunque también se puede combinar con uno para que el usuario elija.

Fija la proporción entre el ancho y el alto de un elemento, por ejemplo aspect-ratio: 16 / 9; para un contenedor de vídeo, de forma que el alto se calcule automáticamente a partir del ancho disponible. Sustituye al truco antiguo de usar padding-bottom en porcentaje para simular una proporción fija, como el que se usa en el wrapper responsive de los vídeos embebidos.

Fija un valor que se mantiene entre un mínimo y un máximo, pero que puede crecer o encogerse de forma fluida entre esos límites según otra medida, normalmente el ancho de la ventana. Por ejemplo, font-size: clamp(1rem, 2vw, 1.5rem); hace que el tamaño de letra escale con la pantalla, sin bajar de 1rem ni superar 1.5rem.

Añade una sombra alrededor de un elemento, definida por un desplazamiento horizontal, uno vertical, un desenfoque, opcionalmente una expansión y un color, por ejemplo box-shadow: 0 2px 6px rgba(0,0,0,0.15);. Se usa mucho para dar sensación de profundidad a tarjetas o paneles, como el que envuelve un cuadro de instrucciones en el panel de administración.

Es la técnica de combinar varias imágenes pequeñas (iconos, por ejemplo) en un único archivo de imagen, y mostrar solo el fragmento necesario en cada elemento usando background-position. Reduce el número de peticiones que el navegador tiene que hacer al servidor, aunque hoy en día muchas de esas necesidades se cubren con iconos de fuente o SVG en línea, más flexibles de colorear y escalar.

Aplica transformaciones visuales a un elemento sin afectar al flujo del resto de la página: rotarlo (rotate()), escalarlo (scale()), moverlo (translate()) o inclinarlo (skew()). Se usa, por ejemplo, en el icono "+" del acordeón de FAQs, que gira 45 grados con transform: rotate(45deg); al abrir una pregunta.

<div> es un elemento de bloque: ocupa todo el ancho disponible y empieza en una línea nueva. <span> es un elemento en línea: solo ocupa el espacio de su contenido y no rompe el flujo del texto. Por eso <div> se usa para agrupar secciones grandes, y <span> para marcar una parte concreta dentro de un párrafo.

Describe el contenido de la imagen en texto, para que lectores de pantalla puedan comunicárselo a usuarios con discapacidad visual, y para que el navegador muestre algo si la imagen no llega a cargar. Además, los buscadores lo usan para entender de qué trata la imagen, así que también ayuda al SEO.

Son etiquetas que describen el significado de su contenido, no solo su apariencia: <header>, <nav>, <main>, <article>, <footer>, en lugar de usar <div> para todo. Ayudan a los lectores de pantalla y a los buscadores a entender la estructura de la página, y hacen el código más legible para cualquiera que lo retome después, incluido tú mismo meses más tarde.

Permiten guardar información personalizada directamente en una etiqueta HTML, sin recurrir a hacks como clases falsas o atributos inventados. Por ejemplo, <button data-categoria="wordpress"> guarda el valor "wordpress" en el atributo data-categoria, y luego se puede leer desde JavaScript con elemento.dataset.categoria. Los uso, por ejemplo, en el selector de categorías del plugin de FAQs para saber qué grupo de preguntas mostrar u ocultar.

Son dos etiquetas que cumplen funciones distintas y se combinan habitualmente para mostrar código correctamente en una página web.

<pre> (preformatted text)

Le indica al navegador que respete exactamente el formato del texto tal y como está escrito: espacios múltiples, saltos de línea y tabulaciones. Por defecto, HTML colapsa todos los espacios en blanco en uno solo y reorganiza el texto en párrafos fluidos; <pre> desactiva ese comportamiento. Además, los navegadores la renderizan por defecto con una fuente monoespaciada (tipo terminal).

<code>

Es una etiqueta semántica: le indica al navegador, a los lectores de pantalla y a los buscadores que ese contenido es código. No preserva por sí sola el formato del espacio en blanco, pero sí aplica normalmente una fuente monoespaciada vía CSS del navegador.

Por qué se combinan

  • <code> sola: pierde los saltos de línea si el texto tiene varios, y semánticamente indica código en línea (inline), no un bloque completo.
  • <pre> sola: preserva el formato, pero no marca semánticamente que se trata de código (podría ser un poema, arte ASCII, etc.).
  • <pre><code>...</code></pre> juntas: se obtiene lo mejor de ambas — formato preservado (saltos de línea, indentación) y significado semántico correcto de «esto es un bloque de código».

Ejemplo práctico

sudo apt install -f
# o bien:
sudo apt install --fix-broken

Caracteres especiales a tener en cuenta

Dentro de <pre><code>, los caracteres <, > y & deben escaparse como &lt;, &gt; y &amp; si aparecen literalmente en el texto, porque de lo contrario el navegador los interpretará como HTML. Por ejemplo, un comando como comando < archivo > salida necesitaría ese escapado para mostrarse correctamente.

Diferencia con otras formas de mostrar código

Existen alternativas según el contexto: <kbd> para indicar una tecla o combinación que el usuario debe pulsar, <samp> para la salida de un programa, y <var> para variables. Pero para bloques de comandos de terminal o fragmentos de código, la combinación <pre><code> es el estándar más extendido y el más correcto semánticamente.

Es la primera línea de un documento HTML (<!DOCTYPE html>) que le indica al navegador qué versión de HTML debe usar para interpretar la página. Sin ella, algunos navegadores activan un modo de compatibilidad con páginas antiguas que puede alterar cómo se calculan tamaños y estilos.

<button> está pensado para acciones dentro de la página (enviar un formulario, abrir un menú...) y no navega a ninguna URL por sí mismo. <a> está pensado para enlazar a otra página o recurso mediante el atributo href. Usar un <a href="#"> como si fuera un botón suele dar problemas de accesibilidad y de comportamiento.

action indica a qué URL se envían los datos del formulario al enviarlo. method indica cómo se envían: GET los añade a la URL (visibles y limitados en longitud), y POST los envía en el cuerpo de la petición, más adecuado para formularios con muchos campos o datos sensibles.

Es una etiqueta (<iframe>) que incrusta otro documento HTML dentro de la página actual, como una ventana dentro de la ventana. Se usa, por ejemplo, para insertar vídeos de YouTube: la URL del vídeo se coloca en el atributo src y el iframe carga esa página de forma independiente.

id identifica un único elemento en toda la página; no debería repetirse. class se puede aplicar a varios elementos a la vez, y un mismo elemento puede tener varias clases. En CSS, un id tiene más especificidad que una class, así que sus estilos son más difíciles de sobrescribir.

<meta name="viewport" content="width=device-width, initial-scale=1.0"> le dice al navegador móvil que use el ancho real del dispositivo en lugar de simular una pantalla de escritorio y luego reducir la página. Sin ella, un sitio responsive se vería diminuto y habría que hacer zoom para leerlo en el móvil.

<ul> es una lista desordenada, donde el orden de los elementos no importa (se muestran con viñetas). <ol> es una lista ordenada, donde la posición sí tiene significado, como los pasos de una receta (se numeran automáticamente). Ambas usan <li> para cada elemento.

Es el conjunto de prácticas para que una página se pueda usar independientemente de las capacidades de quien la visita: texto alternativo en imágenes, suficiente contraste de color, navegación por teclado, etiquetas semánticas y atributos ARIA cuando hace falta. No es solo una cuestión ética: también mejora el SEO y suele hacer el sitio más robusto en general.

Con GET, los datos viajan como parte de la URL (visibles, limitados en tamaño, y quedan en el historial del navegador), lo que lo hace adecuado para búsquedas o filtros. Con POST, los datos van en el cuerpo de la petición, sin límite práctico de tamaño ni exposición en la URL, por lo que es la opción correcta para formularios que crean o modifican datos, como el alta de una FAQ.

Un enlace con target="_blank" abre la página de destino en una pestaña nueva, pero esa pestaña puede, en navegadores antiguos, acceder a window.opener y redirigir la pestaña original a una página falsa (un ataque conocido como tabnabbing). Se evita añadiendo rel="noopener noreferrer" al enlace, algo que conviene incluir siempre que se usa target="_blank" en contenido de terceros.

Permite ofrecer varias versiones de una misma imagen (distintos tamaños o formatos) y dejar que el navegador elija la más adecuada según el tamaño de pantalla o el soporte de formato, usando varias etiquetas <source> dentro con una <img> de reserva. Sirve para servir imágenes más ligeras en móvil sin tener que redimensionarlas por JavaScript.

Marca un campo como obligatorio, de forma que el navegador impide enviar el formulario si ese campo está vacío, mostrando un aviso nativo sin necesidad de JavaScript. Por ejemplo, <input type="email" required> además comprueba que el texto tenga forma de correo electrónico antes de dejar enviar el formulario.

<label for="categoria"> vinculado a <input id="categoria"> hace que hacer clic en el texto de la etiqueta active también el campo, lo cual amplía la zona clicable y facilita marcar casillas o radios pequeños. Además, los lectores de pantalla anuncian esa etiqueta al llegar al campo, así que sin ella un formulario puede resultar confuso o inutilizable para alguien que navegue por voz.

Un elemento de bloque (como <p> o <h2>) ocupa todo el ancho disponible y empieza siempre en una línea nueva. Un elemento en línea (como <strong> o <a>) solo ocupa el espacio de su contenido y se coloca dentro del flujo del texto, sin forzar un salto de línea. CSS permite cambiar este comportamiento con la propiedad display, pero por defecto cada etiqueta HTML tiene uno de los dos tipos.

Etiquetas como <meta http-equiv="Cache-Control"> pueden sugerir al navegador cómo cachear una página, aunque en la práctica el control real de la caché se suele hacer mejor desde las cabeceras HTTP del servidor, no desde el HTML. Es un motivo más por el que herramientas como Autoptimize o WP Fastest Cache gestionan la caché a nivel de servidor y de archivo, en vez de depender solo de estas etiquetas.

Es el pequeño icono que aparece en la pestaña del navegador, en marcadores y en resultados de búsqueda, identificando visualmente el sitio. Se declara con <link rel="icon" href="favicon.ico"> en el <head>, aunque WordPress también permite configurarlo desde el panel de personalización sin tocar código.

<html lang="es"> indica el idioma principal del contenido de la página. Los lectores de pantalla lo usan para elegir la pronunciación correcta, los traductores automáticos lo usan para saber desde qué idioma traducir, y los buscadores lo tienen en cuenta para mostrar resultados en el idioma adecuado según cada usuario.

Son secuencias que representan caracteres que, de escribirse directamente, el navegador interpretaría como parte del propio código HTML. Por ejemplo, &lt; representa el símbolo < y &amp; representa el símbolo &. Se usan para mostrar código HTML como texto literal en una página, tal y como se ve en ejemplos de código como los de esta FAQ.

Es un tipo de codificación para formularios, indicado con enctype="multipart/form-data", necesario cuando el formulario incluye la subida de un archivo (como en el plugin de importación de Excel). Sin este atributo, el navegador no puede enviar el contenido binario del archivo correctamente junto con el resto de los campos del formulario.

Es la comprobación automática que hace el navegador sobre ciertos campos antes de permitir enviar un formulario, usando atributos como required, type="email", min, max o pattern, sin necesidad de escribir JavaScript. No sustituye a la validación en el servidor (los datos siempre se deben volver a comprobar ahí, porque la del navegador se puede saltar), pero mejora la experiencia al avisar de errores al instante.

Permite incrustar un vídeo directamente en la página, con controles nativos del navegador, sin depender de plugins externos como antiguamente hacía falta con Flash. Admite varias fuentes con <source> para ofrecer distintos formatos, y atributos como controls, autoplay o muted para configurar su comportamiento.

Es el equivalente a <video> pero para contenido de solo sonido: reproduce archivos de audio con controles nativos del navegador, usando <source> para indicar el archivo y su formato. Al igual que con vídeo, si el navegador no soporta ninguno de los formatos indicados, se puede mostrar un texto alternativo dentro de la propia etiqueta.

Permite ofrecer varias versiones de una misma imagen, en distintos tamaños, y dejar que el navegador elija cuál descargar según el tamaño real de pantalla y su densidad de píxeles, ahorrando datos en dispositivos donde no hace falta la versión más grande. Se combina con el atributo sizes para indicarle al navegador qué tamaño va a ocupar la imagen en cada situación.

<thead> agrupa la fila o filas de cabecera de una tabla, y <tbody> agrupa las filas de datos propiamente dichas. Además de aportar significado semántico (útil para lectores de pantalla), permiten aplicar estilos distintos a la cabecera con CSS, y en tablas muy largas el navegador puede incluso mantener la cabecera visible al hacer scroll.

Crean un bloque desplegable nativo del navegador, sin necesidad de JavaScript: <summary> es el texto siempre visible en el que se hace clic, y el resto del contenido dentro de <details> se muestra u oculta automáticamente al pulsarlo. Es una alternativa interesante a construir un acordeón a mano con botones y JavaScript, aunque ofrece menos control sobre la animación de apertura.

Al añadirlo a un <a href="archivo.pdf" download>, el navegador descarga el archivo en lugar de intentar abrirlo directamente en una pestaña nueva. Se le puede dar un valor, como download="informe-2026.pdf", para sugerir un nombre distinto al del archivo original al guardarlo.

<select> crea un desplegable de opciones dentro de un formulario, y cada <option> dentro de él representa una alternativa seleccionable. Se usa, por ejemplo, en el filtro por categoría del panel de administración del plugin de FAQs, generando una <option> por cada categoría existente.

Muestra un texto de ejemplo dentro de un campo de formulario vacío, que desaparece en cuanto el usuario empieza a escribir, como "Buscar preguntas..." en el buscador del plugin de FAQs. No sustituye a una etiqueta <label> real: el placeholder desaparece al escribir, así que no debe ser la única pista sobre qué contiene el campo.

Proporciona una superficie en blanco donde se puede dibujar mediante JavaScript: formas, texto, imágenes o incluso animaciones y gráficos generados dinámicamente, usando su API de contexto 2D (o WebGL para gráficos en 3D). A diferencia de una imagen SVG, lo que se dibuja en un canvas no queda como elementos individuales manipulables, sino como píxeles.

Es una herramienta (disponible en validator.w3.org) que comprueba si el HTML de una página cumple con las especificaciones oficiales, señalando etiquetas mal cerradas, atributos incorrectos o anidamientos no válidos. Un HTML sin errores no garantiza que la página se vea bien, pero sí reduce el riesgo de que distintos navegadores la interpreten de forma distinta entre sí.

var tiene alcance de función y puede dar problemas al reutilizarse dentro de bucles o condicionales. let y const tienen alcance de bloque, más predecible: let permite reasignar el valor más adelante, mientras que const no permite reasignar la variable una vez declarada (aunque si es un objeto o array, su contenido sí se puede modificar).

Una promesa representa el resultado de una operación asíncrona (como una petición a un servidor) que todavía no ha terminado. Puede acabar en éxito (resolve) o en error (reject), y se gestiona con .then() y .catch(), o de forma más legible con async/await.

El DOM (Document Object Model) es la representación en memoria de una página HTML como un árbol de objetos, donde cada etiqueta es un nodo. JavaScript no manipula directamente el HTML del archivo, sino este árbol: cuando cambias el texto de un botón o añades una clase con classList.add(), lo que haces es modificar el DOM, y el navegador se encarga de reflejar ese cambio en pantalla.

Es el comportamiento por el cual las declaraciones de variables (var) y funciones se "suben" mentalmente al principio de su ámbito antes de ejecutar el código, aunque estén escritas más abajo. Con var esto puede dar lugar a variables con valor undefined si se usan antes de asignarles algo. let y const también sufren hoisting, pero quedan en una "zona muerta temporal" que lanza un error si se intentan usar antes de su declaración, lo cual ayuda a detectar fallos antes.

Es una señal de que algo ha ocurrido en la página: un clic, una tecla pulsada, un formulario enviado, la carga completa del documento... Se pueden "escuchar" con addEventListener(), como en boton.addEventListener("click", function () { ... }), para ejecutar código en respuesta a la interacción del usuario.

== compara valores convirtiendo tipos si hace falta ("5" == 5 es true). === compara valor y tipo a la vez, sin conversiones ("5" === 5 es false). Igual que en PHP, conviene usar siempre === para evitar comparaciones con resultados inesperados.

Es el comportamiento por el cual un evento disparado en un elemento se propaga hacia arriba, a través de sus elementos padre, hasta llegar al documento. Por ejemplo, un clic en un botón dentro de un <div> también "burbujea" hasta ese <div>. Se puede aprovechar para escuchar eventos en un contenedor en lugar de en cada elemento hijo, o detener la propagación con event.stopPropagation().

Es una forma más corta de escribir funciones, con la sintaxis (parametros) => expresion. Además de ser más concisa, no crea su propio this: hereda el del contexto donde se define, lo cual evita muchos problemas habituales al usar funciones dentro de callbacks o de métodos de arrays como map() o filter().

JSON (JavaScript Object Notation) es un formato de texto para representar datos estructurados, muy usado para intercambiar información entre el navegador y un servidor. En JavaScript, JSON.stringify(objeto) convierte un objeto o array a texto JSON, y JSON.parse(texto) hace el proceso inverso, reconstruyendo el objeto original a partir del texto.

Es la función moderna del navegador para hacer peticiones HTTP desde JavaScript, sustituyendo a XMLHttpRequest. Devuelve una promesa, así que se suele usar con async/await: const respuesta = await fetch(url); const datos = await respuesta.json();. Se usa, por ejemplo, para consultar la REST API de WordPress sin recargar la página.

Es una referencia que apunta a distintos objetos según cómo se llame a la función donde se usa. Dentro de un método de un objeto, this suele apuntar a ese objeto; en una función normal llamada suelta, puede apuntar al objeto global; en una arrow function, hereda el this del contexto donde se definió. Es una de las fuentes de confusión más habituales para quien empieza con JavaScript.

map() transforma cada elemento y devuelve un array nuevo del mismo tamaño. filter() devuelve un array solo con los elementos que cumplen una condición. reduce() recorre el array acumulando un único resultado, como una suma total. Los tres devuelven un valor nuevo sin modificar el array original.

Es un almacén de datos del navegador donde se pueden guardar pares clave-valor de texto que persisten aunque se cierre la pestaña o el navegador, a diferencia de sessionStorage, que se borra al cerrar la sesión. Se usa con localStorage.setItem("clave", "valor") y localStorage.getItem("clave"). Es lo que uso, por ejemplo con sessionStorage, en el widget de frases aleatorias para no repetir citas ya mostradas.

Es una directiva que activa una versión más rigurosa de JavaScript, que convierte en error explícito comportamientos que de otra forma fallarían en silencio, como usar una variable sin declararla. Se activa escribiendo "use strict"; al principio de un script o de una función, aunque hoy en día es automático dentro de módulos y de clases.

Es una forma compacta de escribir un if/else que devuelve un valor, con la sintaxis condicion ? valorSiVerdadero : valorSiFalso. Por ejemplo, const texto = activo ? "Sí" : "No"; asigna un texto u otro según el valor de activo, en una sola línea.

Es una sintaxis para extraer valores de un array o de un objeto y asignarlos directamente a variables, sin acceder a cada uno por separado. Por ejemplo, const {categoria, pregunta} = faq; crea dos variables, categoria y pregunta, con los valores correspondientes del objeto faq.

Permite "expandir" los elementos de un array o las propiedades de un objeto dentro de otro. Por ejemplo, const copia = [...original]; crea un array nuevo con los mismos elementos, y const combinado = {...objeto1, ...objeto2}; combina las propiedades de dos objetos en uno nuevo, sin modificar los originales.

Es una forma de escribir cadenas de texto usando comillas invertidas (` `) que permite insertar variables directamente dentro del texto con ${variable}, sin necesidad de concatenar con +. También permite escribir texto en varias líneas de forma natural, algo que las comillas normales no permiten sin caracteres de escape.

undefined es el valor que JavaScript asigna automáticamente a una variable declarada pero sin valor, o a un parámetro de función que no se ha pasado. null es un valor que alguien asigna a propósito para indicar "aquí no hay nada", de forma intencionada. Con == ambos se consideran iguales entre sí, pero con === son distintos, porque son tipos diferentes.

Permite acceder a una propiedad anidada de un objeto sin que salte un error si alguno de los niveles intermedios no existe. Por ejemplo, usuario?.direccion?.ciudad devuelve undefined en lugar de un error si usuario o direccion son null o undefined, evitando tener que comprobar cada nivel a mano.

Un array está pensado para listas ordenadas de valores, accesibles por índice numérico (lista[0]), e incluye métodos propios como push() o map(). Un objeto está pensado para agrupar datos con nombre, accesibles por clave (objeto.propiedad o objeto["propiedad"]), sin un orden numérico implícito. Un array, de hecho, es un tipo especial de objeto por debajo.

Es la forma estándar de dividir el código JavaScript en archivos independientes que se pueden importar entre sí. Un archivo exporta lo que quiere hacer disponible con export function algo() {...}, y otro archivo lo usa con import { algo } from "./archivo.js";. Facilita organizar proyectos grandes en piezas pequeñas y reutilizables, en lugar de un único script gigante.

Es una técnica para evitar que una función se ejecute demasiadas veces seguidas, retrasando su ejecución hasta que ha pasado un tiempo sin que se vuelva a llamar. Se usa mucho en buscadores en tiempo real: en vez de filtrar resultados con cada tecla pulsada, se espera, por ejemplo, 300 milisegundos desde la última pulsación antes de ejecutar la búsqueda de verdad, para no sobrecargar de trabajo al navegador o al servidor.

NaN ("Not a Number") es el valor especial que JavaScript devuelve cuando una operación matemática no produce un número válido, como "texto" * 2. Curiosamente, NaN === NaN da false, así que no se puede comprobar con una comparación normal; hay que usar Number.isNaN(valor), que sí detecta correctamente este caso.

Recorre los valores de un elemento iterable, como un array o una cadena de texto, sin necesidad de gestionar un índice manualmente. Por ejemplo, for (const faq of faqs) { console.log(faq.pregunta); } recorre cada elemento del array faqs directamente.

Recorre las claves enumerables de un objeto (o los índices de un array, aunque no es su uso recomendado). Por ejemplo, for (const clave in objeto) { console.log(clave, objeto[clave]); } muestra cada propiedad del objeto con su valor. Para arrays, en la práctica se prefiere for...of o métodos como forEach().

Es una colección de valores únicos: si se intenta añadir un valor que ya existe, simplemente se ignora, sin generar duplicados. Se crea con new Set([1, 2, 2, 3]), que da como resultado un conjunto con solo 1, 2, 3. Es útil, por ejemplo, para obtener las categorías distintas de una lista de FAQs sin tener que comprobar duplicados a mano.

Es una colección de pares clave-valor, similar a un objeto normal, pero que admite cualquier tipo de dato como clave (no solo texto) y mantiene el orden en el que se insertaron los elementos. Se crea con new Map() y se usa con métodos como set(), get() y has(), en lugar de acceder directamente con corchetes como en un objeto.

Es una función que se define y se ejecuta inmediatamente, sin necesidad de llamarla aparte, envolviéndola entre paréntesis: (function () { ... })();. Se usaba mucho antes de que existieran los módulos ES, para crear un ámbito propio y evitar que las variables internas contaminaran el ámbito global de la página.

Es la zona del código donde una variable es accesible. Las variables declaradas con let o const dentro de un bloque ({ }) solo existen dentro de ese bloque; las declaradas fuera de cualquier función son globales y accesibles desde cualquier parte. Entender el ámbito evita errores como sobrescribir sin querer una variable con el mismo nombre usada en otra parte del código.

Es una función que "recuerda" las variables del ámbito en el que fue creada, aunque esa función se ejecute después de que ese ámbito haya terminado. Por ejemplo, una función interna definida dentro de otra puede seguir accediendo a las variables de la función externa incluso después de que esta haya devuelto su resultado. Es la base de patrones como los contadores privados o el debounce.

Permite añadir, quitar o comprobar clases CSS de un elemento sin manipular directamente la cadena de texto de class. Por ejemplo, elemento.classList.add('is-active'), elemento.classList.remove('is-active') o elemento.classList.toggle('is-active') alternan una clase según si ya está presente o no, tal y como hace el selector de categorías del plugin de FAQs.

Se dispara cuando el navegador ha terminado de construir el árbol DOM a partir del HTML, sin esperar a que terminen de cargar imágenes u otros recursos externos. Es el momento adecuado para engachar los listeners de eventos de un script, como se hace en el plugin de FAQs con document.addEventListener('DOMContentLoaded', function () { ... }), para asegurarse de que los elementos ya existen en la página.

Es una función que se pasa como argumento a otra función, para que esta la ejecute en el momento adecuado, normalmente cuando termina una operación. Por ejemplo, en elemento.addEventListener('click', miFuncion), miFuncion es un callback que se ejecuta cada vez que ocurre el clic, no de inmediato al escribir esa línea.

Una ruta absoluta empieza siempre desde la raíz del sistema (/) y no depende de dónde te encuentres, por ejemplo /home/usuario/documentos/archivo.txt. Una ruta relativa parte del directorio en el que estás situado en ese momento, por ejemplo documentos/archivo.txt o ../otra_carpeta. Las rutas relativas son más cómodas para moverse rápido, pero menos fiables dentro de scripts, donde conviene usar rutas absolutas para evitar sorpresas.

Cada archivo tiene tres bloques de permisos: propietario, grupo y resto de usuarios. Dentro de cada bloque, r permite leer, w permite escribir y x permite ejecutar (o entrar, si es un directorio). Por ejemplo, rwxr-xr-- significa que el propietario puede leer, escribir y ejecutar; el grupo puede leer y ejecutar; y el resto solo puede leer. Estos permisos también se pueden representar en números con chmod, por ejemplo chmod 754 archivo.

Un proceso en primer plano ocupa la terminal: no puedes escribir otro comando hasta que termina. Un proceso en segundo plano se lanza añadiendo & al final del comando (por ejemplo, comando &), y te devuelve el control de la terminal mientras sigue ejecutándose. Con jobs puedes ver los procesos en segundo plano de la sesión actual, y con fg traer uno de vuelta a primer plano.

El pipe conecta la salida de un comando con la entrada de otro, encadenando varias herramientas pequeñas en una sola línea. Por ejemplo, cat archivo.txt | grep "error" | wc -l muestra el contenido de un archivo, filtra las líneas que contienen "error" y cuenta cuántas hay. Es una de las ideas centrales de la filosofía Unix: comandos sencillos que hacen una cosa bien, combinados entre sí.

Busca líneas que coincidan con un patrón de texto dentro de uno o varios archivos, o en la salida de otro comando a través de una tubería. Por ejemplo, grep "error" registro.log muestra solo las líneas del archivo que contienen la palabra "error". Con la opción -i ignora mayúsculas y minúsculas, y con -r busca de forma recursiva en carpetas.

apt-get es el gestor de paquetes clásico de Debian y derivadas, con muchas opciones pensadas para scripts. apt es una interfaz más moderna y pensada para uso interactivo, con una salida más clara y una barra de progreso, que combina lo más usado de apt-get y apt-cache en un solo comando. Para uso manual en terminal, apt suele ser la opción recomendada hoy en día.

Es un archivo especial que apunta a otro archivo o carpeta, similar a un acceso directo. Se crea con ln -s /ruta/original /ruta/enlace, y a partir de ahí acceder al enlace equivale a acceder al original. Es muy útil para tener una misma carpeta o archivo accesible desde varias rutas sin duplicar su contenido.

Lista todos los procesos en ejecución en el sistema, con información como el usuario que los lanzó, el uso de CPU y memoria, y el comando exacto que los inició. Se combina muy a menudo con grep para localizar un proceso concreto, por ejemplo ps aux | grep mysql.

kill envía por defecto la señal SIGTERM, que pide al proceso que termine de forma ordenada, dándole ocasión de guardar datos o liberar recursos antes de cerrarse. kill -9 envía SIGKILL, que lo mata de inmediato sin darle esa oportunidad. Conviene probar primero con kill normal, y recurrir a -9 solo si el proceso no responde.

Es el archivo donde se programan tareas para que se ejecuten automáticamente a intervalos definidos, como cada noche o cada hora. Se edita con crontab -e, y cada línea define cuándo y qué comando ejecutar, por ejemplo 0 3 * * * /ruta/script.sh lo lanza todos los días a las 3 de la madrugada.

Muestra el espacio usado y disponible en cada sistema de archivos montado, con tamaños en formato legible (-h de human-readable: KB, MB, GB en vez de bytes). Es el primer comando al que recurrir cuando un servidor empieza a fallar por falta de espacio en disco.

root es el superusuario del sistema, con permisos totales. sudo ("superuser do") permite a un usuario normal ejecutar un comando concreto con esos permisos de forma puntual, dejando constancia en el registro de quién lo hizo. Es más seguro trabajar habitualmente como usuario normal y usar sudo solo cuando hace falta, que iniciar sesión directamente como root.

Muestra las últimas líneas de un archivo y se queda "escuchando" nuevas líneas a medida que se añaden, sin tener que volver a ejecutar el comando. Se usa mucho para vigilar en tiempo real un archivo de log, por ejemplo tail -f /var/log/nginx/error.log mientras se reproduce un problema.

Es un valor con nombre disponible para los procesos del sistema, usado para configuración (rutas, idiomas, claves de acceso...) sin tenerlo que escribir en el propio código. Se puede consultar con echo $NOMBRE y definir con export NOMBRE=valor. Aplicaciones y scripts las leen habitualmente para adaptar su comportamiento sin necesidad de modificar archivos de configuración.

Busca archivos y carpetas dentro de un directorio según distintos criterios: nombre, tamaño, fecha de modificación, tipo... Por ejemplo, find /var/www -name "*.php" busca todos los archivos PHP dentro de esa carpeta y sus subcarpetas. A diferencia de grep, que busca dentro del contenido de los archivos, find busca los propios archivos.

cp copia un archivo o carpeta, dejando el original intacto y creando una copia en el destino. mv mueve el archivo al destino (o lo renombra, si el destino está en la misma carpeta con otro nombre), sin dejar copia en el origen. Ambos aceptan -r para operar sobre carpetas completas de forma recursiva.

Es un proceso que ya ha terminado de ejecutarse, pero cuya información todavía aparece en la tabla de procesos porque el proceso padre no ha leído su código de salida. No consumen CPU ni memoria real, solo una entrada en la tabla de procesos, y normalmente desaparecen solos cuando el proceso padre termina o se reinicia; muchos procesos zombis acumulados sí pueden ser síntoma de un error en la aplicación que los generó.

Muestra el tamaño total ocupado por una carpeta (-s de resumen, sin desglosar cada subcarpeta) en formato legible (-h). Por ejemplo, du -sh wp-content/uploads indica de un vistazo cuánto espacio ocupan los archivos subidos, útil para localizar qué carpeta está llenando el disco antes de recurrir a df -h para ver el total del sistema.

SSH (Secure Shell) es un protocolo para conectarse de forma cifrada a la terminal de otro ordenador o servidor a través de la red, con un comando como ssh usuario@servidor. Permite ejecutar comandos, transferir archivos (con scp o sftp) o administrar un servidor remoto como si se estuviera físicamente delante, sin que la conexión pueda ser interceptada fácilmente.

ext4 es el sistema de archivos más habitual en distribuciones Linux para el disco principal: estable, con buen rendimiento y soporte para journaling, que ayuda a recuperar datos tras un corte de luz o un fallo. Existen alternativas como XFS o Btrfs, con características distintas (Btrfs añade snapshots e integridad de datos avanzada, por ejemplo), pero para la mayoría de servidores web ext4 sigue siendo una opción sólida y de sobra probada.

Cambia el propietario (y, opcionalmente, el grupo) de un archivo o carpeta. Por ejemplo, chown www-data:www-data archivo.php asigna tanto el usuario como el grupo www-data, que suele ser el usuario bajo el que corre el servidor web, algo habitual al ajustar permisos después de subir archivos por otra vía.

Es un atajo que asocia un nombre corto a un comando más largo o más repetitivo. Por ejemplo, alias ll="ls -la" hace que escribir ll ejecute realmente ls -la. Se suelen definir en el archivo de configuración de la shell (como .bashrc) para que estén disponibles en cada sesión nueva.

/ representa la raíz absoluta del sistema de archivos, el punto de partida de cualquier ruta absoluta. ~ es un atajo para la carpeta personal del usuario actual (por ejemplo, /home/guillermo), así que ~/documentos equivale a esa ruta completa, sea cual sea el usuario que la use.

wc (word count) cuenta líneas, palabras y caracteres de un archivo o de la entrada que reciba por una tubería. wc -l archivo.txt cuenta solo las líneas, algo muy usado junto con grep para contar cuántas coincidencias hay, como en grep "error" registro.log | wc -l.

Ambos muestran en tiempo real los procesos en ejecución y su consumo de CPU y memoria. top viene preinstalado en prácticamente cualquier sistema Linux, con una interfaz de texto básica. htop es una alternativa más visual, con colores, barras de uso y la posibilidad de desplazarse o interactuar con el ratón, pero no siempre viene instalada por defecto.

Recibe datos por una tubería y los muestra en pantalla a la vez que los guarda en un archivo, sin tener que elegir entre uno u otro. Por ejemplo, comando | tee salida.log permite ver el resultado en la terminal mientras se guarda también en salida.log para revisarlo más tarde.

Realiza peticiones HTTP (y de otros protocolos) desde la terminal, mostrando la respuesta directamente, sin necesidad de un navegador. Se usa mucho para probar APIs o comprobar cómo responde una URL, por ejemplo curl -I https://indaga.net muestra solo las cabeceras de la respuesta, útil para comprobar redirecciones o códigos de estado.

Ambos descargan contenido de una URL desde la terminal, pero wget está pensado sobre todo para descargar archivos y páginas completas (incluye opciones para descargar sitios enteros de forma recursiva), mientras que curl está más orientado a interactuar con APIs y protocolos diversos, mostrando la respuesta por defecto en lugar de guardarla directamente en un archivo.

Son archivos de configuración que la shell Bash ejecuta automáticamente al iniciar una sesión, donde se suelen definir alias, variables de entorno y ajustes personales de la terminal. .bashrc se carga en sesiones interactivas normales; .bash_profile (o .profile) se carga en sesiones de inicio de sesión, como al conectarse por SSH, y muchas configuraciones hacen que uno cargue al otro para no duplicar ajustes.

Muestra la lista de comandos ejecutados anteriormente en la sesión de terminal, numerados. Se puede volver a ejecutar uno de ellos con !numero, o buscar en el historial pulsando Ctrl+R y escribiendo parte del comando, lo cual ahorra mucho tiempo cuando se repiten comandos largos con frecuencia.

Es una herramienta para sincronizar archivos y carpetas entre dos ubicaciones (locales o remotas por SSH), copiando solo lo que ha cambiado en lugar de todo de nuevo cada vez. Se usa mucho para copias de seguridad incrementales o para desplegar cambios en un servidor, por ejemplo rsync -avz carpeta_local/ usuario@servidor:/ruta/destino/.

Un servicio es un programa que se ejecuta en segundo plano de forma continua, como el servidor web o MySQL. systemctl es la herramienta para gestionar esos servicios en sistemas modernos: systemctl status mysql, systemctl restart nginx o systemctl enable servicio (para que arranque automáticamente al iniciar el sistema).

Un usuario es una cuenta individual con su propio identificador. Un grupo agrupa varios usuarios para poder asignarles permisos comunes de golpe, en lugar de configurarlos uno a uno. Un archivo pertenece siempre a un usuario y a un grupo, y los permisos rwx vistos antes se aplican de forma distinta según si quien accede es el propietario, pertenece al grupo, o es parte del resto de usuarios del sistema.

Muestra el manual de un comando directamente en la terminal, con su descripción, opciones disponibles y ejemplos de uso, por ejemplo man grep. Es la fuente de referencia más fiable para cualquier comando de Linux, siempre disponible sin conexión a internet, y la que suelo consultar como primera fuente al documentar los comandos del plugin de Comandos Linux.

Es el campo (o combinación de campos) que identifica de forma única cada fila de una tabla. No puede repetirse ni quedar vacío, y MySQL crea automáticamente un índice sobre ella, lo que acelera mucho las búsquedas por ese campo. En la mayoría de tablas de WordPress, y en las de mis propios plugins, ese campo suele ser id con AUTO_INCREMENT.

INNER JOIN devuelve solo las filas que tienen coincidencia en ambas tablas. LEFT JOIN devuelve todas las filas de la tabla de la izquierda, tengan o no coincidencia en la otra tabla (rellenando con NULL cuando no la hay). Se usa LEFT JOIN, por ejemplo, para listar todos los posts aunque alguno no tenga comentarios todavía.

Un índice es una estructura auxiliar que MySQL mantiene sobre una o varias columnas para encontrar filas rápidamente, en lugar de recorrer toda la tabla fila por fila. Es como el índice de un libro: en vez de leer página a página, vas directo a donde está lo que buscas. Tiene un coste: ocupa espacio y ralentiza un poco las inserciones, así que conviene indexar las columnas por las que se filtra o se ordena a menudo (como hice con categoria en la tabla de FAQs), no todas.

CHAR(n) reserva siempre un espacio fijo de n caracteres, rellenando con espacios si el texto es más corto. VARCHAR(n) ocupa solo el espacio que realmente necesita el texto, hasta un máximo de n caracteres. Para campos de longitud variable como nombres, títulos o categorías, VARCHAR es casi siempre la opción correcta; CHAR tiene sentido para valores de longitud fija y conocida, como un código postal o un código de país.

Es un campo que enlaza una tabla con la clave primaria de otra, garantizando que el valor referenciado existe realmente. Por ejemplo, en una tabla de comentarios, un campo post_id como clave foránea hacia la tabla de entradas evita que se pueda guardar un comentario apuntando a una entrada inexistente.

ORDER BY cambia el orden en el que se muestran las filas del resultado, sin agruparlas. GROUP BY agrupa filas que comparten un valor en una o varias columnas, normalmente para calcular algo por grupo, como SELECT categoria, COUNT(*) FROM faqs GROUP BY categoria, que cuenta cuántas preguntas hay en cada categoría.

Es un conjunto de operaciones que se ejecutan como un todo: o se completan todas, o no se aplica ninguna. Se inicia con START TRANSACTION, y se confirma con COMMIT o se deshace con ROLLBACK. Es útil cuando varias tablas deben quedar coherentes entre sí, por ejemplo al insertar un pedido y sus líneas de detalle a la vez.

DELETE borra filas una a una (se puede filtrar con WHERE) y se puede deshacer dentro de una transacción. TRUNCATE vacía la tabla entera de golpe, reinicia el contador de AUTO_INCREMENT, y en la mayoría de motores no admite WHERE ni deshacerse con ROLLBACK. Para borrar todo el contenido de una tabla, TRUNCATE suele ser más rápido.

Es una consulta guardada que se comporta como si fuera una tabla: se puede hacer SELECT sobre ella con normalidad, pero los datos reales siguen viviendo en las tablas originales. Sirve para simplificar consultas complejas que se repiten a menudo, dándoles un nombre reutilizable sin duplicar datos.

Es el proceso de organizar las tablas para reducir la información duplicada, dividiendo los datos en tablas relacionadas mediante claves foráneas en lugar de repetirlos en cada fila. Por ejemplo, en vez de escribir el nombre de una categoría en cada FAQ, se podría guardar solo su identificador y mantener el nombre en una tabla de categorías aparte.

Son dos motores de almacenamiento de MySQL. MyISAM es más simple y rápido en lecturas puras, pero no soporta transacciones ni claves foráneas. InnoDB sí soporta transacciones, claves foráneas y bloqueo a nivel de fila, lo que lo hace mucho más robusto para aplicaciones con escrituras frecuentes. Desde hace años, InnoDB es el motor por defecto en WordPress y en la práctica totalidad de proyectos nuevos.

Es una consulta SELECT anidada dentro de otra consulta, cuyo resultado se usa como parte de la consulta principal. Por ejemplo, SELECT * FROM faqs WHERE categoria IN (SELECT categoria FROM faqs WHERE activo = 0) usa el resultado de la subconsulta para filtrar la consulta exterior.

Restringe el número de filas que devuelve una consulta. LIMIT 10 devuelve como máximo 10 filas, y combinado con OFFSET (LIMIT 10 OFFSET 20) permite "saltar" las primeras filas, que es justo la base de la paginación que usa el panel de administración del plugin de FAQs.

Es una técnica en la que alguien introduce código SQL malicioso a través de un campo de entrada (un formulario, una URL...) para manipular una consulta y acceder o modificar datos sin permiso. Se evita nunca concatenando directamente valores del usuario en el SQL, y usando siempre consultas preparadas, como $wpdb->prepare() en WordPress.

Combina los resultados de dos o más consultas SELECT en un único resultado, siempre que tengan el mismo número de columnas y tipos compatibles. Por defecto, UNION elimina filas duplicadas entre los resultados combinados; si se quieren conservar los duplicados, se usa UNION ALL, que además es más rápido al no tener que comprobar duplicados.

Es un tipo de índice pensado para buscar palabras dentro de texto largo de forma más eficiente y flexible que con LIKE '%palabra%', incluyendo relevancia de los resultados. Se crea con FULLTEXT (columna) y se consulta con MATCH (columna) AGAINST ('palabra'). Para tablas pequeñas como una de FAQs, un simple LIKE suele bastar, pero en tablas de miles de artículos marca una diferencia notable.

COUNT(*) cuenta todas las filas del resultado, sin importar si alguna columna tiene valores NULL. COUNT(columna) cuenta solo las filas donde esa columna concreta no es NULL. Por eso, si una columna puede estar vacía, ambos conteos pueden dar números distintos sobre la misma tabla.

Es una columna, normalmente la clave primaria, cuyo valor MySQL asigna automáticamente al insertar una fila nueva, aumentando en uno respecto al último valor usado. Evita tener que calcular manualmente el siguiente identificador disponible, y es la base del campo id en la práctica totalidad de tablas propias, incluida la de FAQs.

columna BETWEEN 10 AND 20 es una forma más legible de escribir columna >= 10 AND columna <= 20: hacen exactamente lo mismo, incluyendo ambos extremos del rango. La diferencia es puramente de estilo y claridad, no de rendimiento ni de resultado.

Es un bloque de código SQL guardado dentro de la propia base de datos, con un nombre, que se puede ejecutar con CALL nombre_procedimiento() en lugar de repetir la misma lógica en cada script. Puede recibir parámetros y ejecutar varias instrucciones seguidas, lo que resulta útil para operaciones que se repiten mucho, aunque en proyectos pequeños suele ser más simple mantener esa lógica en el propio código PHP.

Es una acción que la base de datos ejecuta automáticamente cuando ocurre un evento determinado sobre una tabla, como BEFORE INSERT o AFTER UPDATE. Por ejemplo, se podría usar un trigger para registrar automáticamente en una tabla de auditoría cada vez que se borra una fila de otra tabla, sin tener que acordarse de hacerlo desde el código de la aplicación.

NULL significa "no hay ningún valor", ausencia total de dato. Una cadena vacía ('') es un valor de texto concreto, simplemente sin caracteres. Son conceptualmente distintos: un campo NULL no se puede comparar con = (hay que usar IS NULL), mientras que una cadena vacía sí se compara con normalidad como cualquier otro texto.

Es una herramienta web para administrar bases de datos MySQL o MariaDB sin usar la línea de comandos: permite ver y editar tablas, ejecutar consultas SQL directamente (como los scripts de este mismo plugin), importar y exportar datos, y gestionar usuarios y permisos. Es habitual encontrarla preinstalada en el panel de la mayoría de hostings compartidos.

mysqldump es una herramienta de línea de comandos que exporta la estructura y los datos de una base de datos a un archivo .sql, que luego se puede volver a importar para restaurar esa base de datos tal cual estaba. Es una de las formas más fiables de hacer copias de seguridad antes de un cambio importante, como una migración o una actualización mayor.

Es una clave primaria formada por dos o más columnas combinadas, en lugar de una sola, usada cuando ningún campo por sí solo garantiza que la fila sea única, pero la combinación de varios sí. Por ejemplo, en una tabla de inscripciones podría usarse la combinación de alumno_id y curso_id como clave compuesta, para impedir que un alumno se inscriba dos veces al mismo curso.

Indica que, al borrar una fila de la tabla "padre", MySQL debe borrar automáticamente todas las filas relacionadas en la tabla "hija" que apuntan a ella mediante la clave foránea. Por ejemplo, al borrar una entrada, sus comentarios asociados se borrarían también sin necesidad de una consulta DELETE adicional. Conviene usarlo con cuidado, porque un borrado en cascada puede eliminar más datos de los que se esperaba a simple vista.

Muestra cómo MySQL va a ejecutar realmente una consulta: qué índices usa (o si no usa ninguno), en qué orden recorre las tablas y cuántas filas estima examinar. Se antepone a cualquier SELECT, por ejemplo EXPLAIN SELECT * FROM wp_indaga_faqs WHERE categoria = 'PHP', y es la primera herramienta a la que recurrir cuando una consulta va lenta y no se sabe por qué.

Es un modelo de base de datos donde la información se organiza en tablas con filas y columnas, y donde las relaciones entre distintas tablas se establecen mediante claves (primaria y foránea), en lugar de anidar toda la información en un único documento. MySQL, MariaDB y PostgreSQL son ejemplos de sistemas de bases de datos relacionales, frente a bases de datos no relacionales como MongoDB.

Es una codificación de caracteres que, a diferencia de la antigua utf8 de MySQL (que en realidad solo soportaba 3 bytes por carácter), permite representar el conjunto completo de Unicode, incluidos los emojis y algunos caracteres poco comunes. WordPress la usa por defecto desde hace años, y conviene comprobar que tanto la base de datos como las tablas la tengan configurada para evitar errores al guardar ciertos textos.

Es la configuración de uno o varios servidores "réplica" que copian automáticamente los cambios que ocurren en un servidor "principal", manteniendo una copia sincronizada de los datos. Se usa para repartir la carga de lecturas entre varios servidores, o como parte de una estrategia de alta disponibilidad, de forma que si el servidor principal falla, una réplica pueda asumir su papel.

Es un tipo de columna que solo admite uno de una lista fija de valores de texto definidos al crear la tabla, por ejemplo estado ENUM('activo', 'inactivo', 'pendiente'). Internamente MySQL lo guarda como un número, lo que lo hace eficiente, pero cambiar la lista de valores permitidos más adelante requiere modificar la estructura de la tabla, así que conviene usarlo solo para listas realmente estables.

WHERE filtra las filas antes de agruparlas, y no puede usar funciones de agregación como COUNT() o SUM(). HAVING filtra después de aplicar GROUP BY, y sí puede usar esas funciones de agregación, por ejemplo SELECT categoria, COUNT(*) as total FROM faqs GROUP BY categoria HAVING total > 5, que solo muestra las categorías con más de 5 preguntas.

Modifica la estructura de una tabla ya existente sin necesidad de borrarla y volver a crearla: añadir o quitar columnas, cambiar su tipo, añadir índices o claves. Es la sentencia que se usa, por ejemplo, para añadir el índice único uniq_categoria_pregunta a la tabla de FAQs sin perder ninguna de las filas ya guardadas.

Una copia completa guarda absolutamente todos los datos en cada ejecución, lo que ocupa más espacio pero es la más sencilla de restaurar. Una copia incremental solo guarda los cambios desde la última copia (completa o incremental), lo que ahorra espacio y tiempo, a cambio de que restaurar requiera aplicar la copia completa más todas las incrementales posteriores en orden.

== compara solo el valor, convirtiendo tipos si hace falta (por ejemplo, "5" == 5 es true). === compara valor y tipo a la vez, sin conversiones ("5" === 5 es false). Como norma general, conviene usar siempre === para evitar comparaciones inesperadas.

Es un array donde cada valor tiene asociada una clave de texto en lugar de un índice numérico. Se usa mucho para representar datos con nombre, como la fila de una tabla:

$comando = array(
    'nombre' => 'grep',
    'ejemplo' => 'grep "texto" archivo.txt'
);
echo $comando['nombre']; // grep

Ambos incorporan el contenido de otro archivo PHP al script actual, pero se comportan distinto si el archivo no existe: include lanza un aviso (warning) y el script sigue ejecutándose; require lanza un error fatal y detiene la ejecución. Como norma general, uso require para archivos imprescindibles (como la conexión a la base de datos) e include para piezas opcionales, donde seguir sin ellas no rompe nada importante.

Es una función sin nombre que se puede guardar en una variable, pasar como argumento a otra función, o usar directamente donde se necesite, sin tener que declararla aparte. Es la base de los hooks de WordPress: cuando escribes add_action('init', function() { ... }), estás pasando una función anónima como callback. Con use ($variable) se puede además "capturar" una variable del entorno donde se definió la función.

Es un array cuyos valores son, a su vez, otros arrays, lo que permite representar datos con varios niveles. Por ejemplo, una lista de comandos con sus datos: $comandos = array(array("nombre" => "ls", "ejemplo" => "ls -la"), array("nombre" => "grep", "ejemplo" => "grep texto archivo"));. Se accede a cada nivel encadenando corchetes, como $comandos[0]["nombre"].

for recorre un rango controlado por un contador (for ($i = 0; $i < 10; $i++)), útil cuando se necesita el índice o un control preciso del avance. foreach recorre directamente los elementos de un array, sin gestionar contadores a mano (foreach ($comandos as $comando)), y es la opción más habitual y legible cuando solo hace falta iterar sobre los valores.

Es una función que acepta un número indefinido de argumentos, que PHP agrupa automáticamente en un array dentro de la función usando ... antes del nombre del parámetro. Por ejemplo, function suma(...$numeros) { return array_sum($numeros); } se puede llamar con suma(1, 2, 3) o con cualquier otra cantidad de argumentos.

Es un estilo de programación que organiza el código en clases (plantillas) y objetos (instancias de esas clases), agrupando datos y comportamiento relacionados. Una clase se define con class, sus datos son propiedades y su comportamiento son métodos. WordPress la usa, por ejemplo, en WP_Query o en WP_Widget.

Es una forma de agrupar clases, funciones y constantes bajo un espacio de nombres propio, para evitar colisiones cuando dos librerías definen algo con el mismo nombre. Se declara con namespace MiPlugin; al principio del archivo, y luego se referencia con MiPluginMiClase desde fuera. En plugins sueltos de WordPress no siempre es necesario, pero se vuelve importante cuanto más crece el proyecto.

isset() comprueba si una variable existe y no es null. empty() comprueba si una variable no existe, o si existe pero su valor se considera "vacío" ("", 0, "0", null, false, un array vacío). Por eso empty($variable) es más estricto: una variable con valor 0 pasa isset() pero no pasa empty().

Es el gestor de dependencias estándar de PHP: permite declarar en un archivo composer.json qué librerías necesita un proyecto, y Composer se encarga de descargarlas junto con sus propias dependencias en una carpeta vendor. Es habitual en proyectos PHP a medida, aunque en plugins ligeros como los que suelo montar para Indaga.net procuro evitarlo y no depender de librerías externas.

Es la estructura para capturar errores (excepciones) sin que detengan la ejecución del script. El código que puede fallar se coloca dentro de try { ... }, y si se lanza una excepción, la ejecución salta al bloque catch (Exception $e) { ... }, donde se puede gestionar el error o mostrar un mensaje adecuado en lugar de una pantalla en blanco.

Son los niveles de visibilidad de propiedades y métodos dentro de una clase. public es accesible desde cualquier parte. private solo es accesible desde dentro de la propia clase. protected es accesible desde la clase y desde las que heredan de ella, pero no desde fuera. Ayudan a proteger el estado interno de un objeto de modificaciones no controladas.

Son consultas SQL donde los valores variables se pasan por separado, en lugar de concatenarlos directamente en el texto de la consulta, evitando así inyección SQL. En WordPress se hacen con $wpdb->prepare("SELECT * FROM tabla WHERE id = %d", $id), que se encarga de escapar y dar formato correcto a cada valor antes de ejecutar la consulta.

Permite que una clase reutilice las propiedades y métodos de otra, extendiéndola con class Hijo extends Padre { ... }. La clase hija puede añadir sus propios métodos o sobrescribir los del padre, y acceder a la versión original con parent::metodo() si necesita ejecutar también el comportamiento heredado.

Una interface define qué métodos debe tener una clase, pero no cómo se implementan; una clase puede implementar varias interfaces a la vez. Una clase abstract sí puede incluir código compartido, además de métodos sin implementar que las clases hijas deben completar, pero una clase solo puede heredar de una clase abstracta. En resumen: interface para un "contrato", clase abstracta para compartir comportamiento común.

Es un mecanismo para reutilizar métodos entre clases que no comparten una jerarquía de herencia, algo que PHP no permite con herencia múltiple normal. Se define con trait y se incorpora a una clase con use NombreDelTrait;, como si sus métodos se hubieran copiado directamente dentro de la clase.

json_encode() convierte un array o un objeto PHP en una cadena de texto en formato JSON, lista para enviar a JavaScript o guardar en un archivo. json_decode() hace el proceso inverso: convierte una cadena JSON en un array o un objeto PHP; con el segundo parámetro a true devuelve un array asociativo en lugar de un objeto stdClass.

echo imprime directamente algo en la salida, en el punto donde se ejecuta. return devuelve un valor a quien llamó a la función, sin imprimir nada por sí mismo, y termina la ejecución de la función en ese punto. Una función bien diseñada normalmente debería usar return para devolver datos, y dejar que sea quien la llama quien decida si los imprime o los usa de otra forma.

Es un array asociativo con información sobre la petición HTTP actual y el servidor: la URL solicitada ($_SERVER["REQUEST_URI"]), el método usado ($_SERVER["REQUEST_METHOD"]), la IP del visitante ($_SERVER["REMOTE_ADDR"]) y muchos otros datos que PHP rellena automáticamente en cada petición, sin que el desarrollador tenga que calcularlos.

Aplica una función a cada elemento de un array y devuelve un array nuevo con los resultados, sin modificar el original. Por ejemplo, array_map("strtoupper", $palabras) devuelve todas las palabras en mayúsculas. Es el equivalente en PHP al map() de los arrays en JavaScript.

self:: siempre hace referencia a la clase donde está escrito el código, aunque se llame desde una clase hija. static:: hace referencia a la clase que realmente se está usando en tiempo de ejecución (lo que se conoce como late static binding), lo que permite que una clase hija sobrescriba un método y que ese cambio se respete incluso cuando lo llama código heredado de la clase padre.

password_hash() convierte una contraseña en una cadena cifrada de forma irreversible (con sal incluida automáticamente), y password_verify() comprueba si una contraseña introducida coincide con ese hash, sin necesidad de descifrarlo nunca. Guardar contraseñas en texto plano es un riesgo grave: si la base de datos se filtra, todas las cuentas quedan expuestas directamente, en lugar de protegidas por un cifrado que, aunque la base de datos se filtre, sigue siendo muy costoso de romper.

Son métodos con nombres reservados que PHP llama automáticamente en ciertas situaciones, sin que haya que invocarlos a mano. __construct() se ejecuta al crear un objeto con new; __get() y __set() se ejecutan al leer o asignar una propiedad que no existe directamente en la clase, lo que permite controlar ese acceso de forma personalizada.

Desde PHP 8.1, un enum es un tipo que representa un conjunto cerrado de valores posibles con nombre, por ejemplo enum Estado { case Activo; case Inactivo; }. Sustituye al patrón antiguo de usar constantes sueltas o cadenas de texto para representar opciones fijas, con la ventaja de que PHP comprueba que solo se usen los valores definidos.

Recorre un array y devuelve uno nuevo solo con los elementos que cumplen una condición, definida en una función que se le pasa como segundo argumento. Por ejemplo, array_filter($faqs, function($f) { return $f['activo'] == 1; }) devuelve solo las FAQs activas, sin modificar el array original.

Recorre un array acumulando un único resultado a partir de todos sus elementos, aplicando una función que combina el acumulado con cada valor. Por ejemplo, se puede usar para sumar el total de un array de precios, pasando una función que va sumando cada precio al total acumulado hasta el momento.

Ambos crean una constante, pero define('NOMBRE', valor) se puede usar en cualquier punto del código, incluso condicionalmente, y evalúa el valor en tiempo de ejecución. const NOMBRE = valor; se evalúa en tiempo de compilación, solo admite valores simples (no el resultado de una función), y se usa sobre todo dentro de clases o en el ámbito global de un archivo.

Devuelve el valor de la izquierda si existe y no es null; si no, devuelve el de la derecha. Por ejemplo, $nombre = $_GET['nombre'] ?? 'invitado'; evita el aviso de "índice indefinido" y asigna "invitado" si ese parámetro no llegó en la URL, en una sola línea en vez de un isset() con condicional.

Es la posibilidad de indicar explícitamente qué tipo de dato espera un parámetro de función o qué tipo devuelve, por ejemplo function suma(int $a, int $b): int { return $a + $b; }. PHP comprueba esos tipos en tiempo de ejecución y lanza un error si no coinciden, lo que ayuda a detectar fallos antes y hace el código más fácil de entender sin necesidad de leer la implementación.

error_reporting(E_ALL) le pide a PHP que informe de todos los tipos de error, incluidos avisos menores que de otra forma pasarían desapercibidos, como usar una variable no definida. Combinado con ini_set('display_errors', 1) durante el desarrollo, ayuda a detectar fallos pequeños antes de que lleguen a producción, donde en cambio conviene ocultarlos al visitante y guardarlos solo en un log.

Construye una cadena de texto sustituyendo marcadores de posición por valores, con control sobre el formato (decimales, relleno con ceros, alineación...). Por ejemplo, sprintf('Importación %s eliminada', $id) sustituye %s por el valor de $id. WordPress la usa mucho en textos traducibles, como en los mensajes de "eliminada" del plugin de FAQs.

serialize() convierte un array u objeto PHP en una cadena de texto que conserva su estructura, para poder guardarla en un campo de texto de la base de datos. unserialize() hace el proceso inverso. WordPress lo usa internamente en varias opciones, aunque hay que tener cuidado al hacer búsquedas y reemplazos directos en la base de datos, porque pueden romper el formato serializado si no se ajustan también las longitudes indicadas dentro de la cadena.

Es un mecanismo que permite cargar automáticamente el archivo de una clase la primera vez que se usa, sin tener que escribir un require por cada una al principio del script. Se configura con spl_autoload_register(), indicando una función que, dado el nombre de una clase, sepa deducir en qué archivo se encuentra. Composer genera automáticamente este autoload siguiendo el estándar PSR-4 para los proyectos que lo usan.

El tema controla el aspecto visual del sitio (maquetación, tipografías, colores). El plugin añade funcionalidad, independientemente de qué tema esté activo. Por eso, si cambias de tema, un plugin sigue funcionando igual, pero si desactivas un plugin, pierdes lo que hacía sin que el diseño se vea afectado.

Un hook es un punto del código de WordPress donde puedes "engancharte" para ejecutar tu propia función, sin tocar el núcleo. Las acciones (add_action()) ejecutan algo en un momento concreto (por ejemplo, al publicar un post). Los filtros (add_filter()) modifican un dato antes de que se muestre (por ejemplo, el contenido de una entrada) y siempre deben devolver un valor.

Es el bucle estándar que usa WordPress para recorrer y mostrar entradas, ya sea en la portada, en un archivo de categoría o en una página de resultados de búsqueda. Se apoya en funciones como have_posts() y the_post(), y dentro de él se usan las etiquetas de plantilla (the_title(), the_content()...) para pintar cada entrada. Prácticamente cualquier tema, incluido Twenty Seventeen, construye sus plantillas alrededor de este bucle.

Las entradas están pensadas para contenido cronológico y organizable: llevan fecha, se pueden clasificar por categorías y etiquetas, y aparecen en el feed RSS. Las páginas son para contenido más estático y jerárquico, como "Sobre mí" o "Aviso legal": no tienen categorías ni etiquetas, pero sí pueden tener páginas padre e hijas. Como norma práctica, si el contenido tiene sentido en una línea de tiempo, es una entrada; si es información fija de la web, es una página.

Es un tipo de contenido distinto de las entradas y páginas por defecto, pensado para estructurar información propia (por ejemplo, "Productos" o "Eventos"). Se registra con register_post_type(), normalmente en el hook init, y a partir de ahí WordPress lo trata igual que a un post: tiene su propia pantalla de administración, permalinks y, si se declara, soporte para campos personalizados.

Es un sistema de clasificación propio, similar a las categorías o etiquetas de las entradas, pero aplicado a cualquier tipo de contenido. Se registra con register_taxonomy() y se puede asociar a uno o varios post types. Por ejemplo, un custom post type "Recetas" podría tener la taxonomía "Tipo de cocina".

Es una etiqueta corta entre corchetes, como [indaga_faqs], que WordPress sustituye por el HTML que devuelve una función PHP asociada. Se registra con add_shortcode() y permite insertar funcionalidad dinámica dentro del contenido de una entrada o página sin tocar plantillas.

Es un bloque de contenido que se puede colocar en zonas concretas del tema, como la barra lateral o el pie de página, sin necesidad de tocar código. WordPress trae widgets básicos (texto, últimas entradas, menú de navegación...), y los plugins pueden registrar los suyos propios extendiendo la clase WP_Widget.

wp_register_script() declara un script (su URL, dependencias y versión) sin cargarlo todavía; sirve para tenerlo disponible y que otros plugins o el propio tema lo encolen cuando lo necesiten. wp_enqueue_script() lo declara y lo carga de inmediato en la página. En la práctica, si el script se usa siempre, basta con wp_enqueue_script() directamente.

Es el archivo donde un tema (o un child theme, como el de Indaga.net) engancha su propia lógica: registrar menús, encolar estilos y scripts, añadir filtros y acciones, crear shortcodes... Se ejecuta en cada carga del sitio, así que conviene mantenerlo organizado y no sobrecargarlo con lo que en realidad debería ser un plugin.

Es un tema que hereda la plantilla y los estilos de otro (el tema padre, en tu caso Twenty Seventeen) y permite sobrescribir solo lo necesario, en archivos propios. La ventaja es que al actualizar el tema padre no se pierden las personalizaciones, porque viven en una carpeta aparte que WordPress no toca.

Es una interfaz que expone los datos del sitio (entradas, páginas, usuarios, taxonomías...) en formato JSON a través de URLs como /wp-json/wp/v2/posts. Permite que aplicaciones externas, o el propio JavaScript del front end, lean o modifiquen contenido sin pasar por el HTML tradicional de WordPress.

Es un token de un solo uso que WordPress genera para verificar que una petición (enviar un formulario, hacer clic en un enlace de borrado...) proviene realmente de la página donde se mostró, y no de un tercero. Se crea con funciones como wp_nonce_field() y se valida con check_admin_referer() o wp_verify_nonce(), como en el panel de administración del plugin de FAQs.

wp-admin contiene el núcleo del panel de administración de WordPress: no se debe modificar, porque se sobrescribe en cada actualización. wp-content es donde vive todo lo personalizado del sitio: temas, plugins y archivos subidos (carpeta uploads). Es la única carpeta que de verdad hay que respaldar con cuidado.

Es la clase que WordPress usa internamente para consultar entradas según casi cualquier criterio: categoría, autor, rango de fechas, meta datos... Se puede instanciar directamente en una plantilla para crear un listado personalizado, por ejemplo new WP_Query(array("post_type" => "post", "posts_per_page" => 5)), sin tocar la consulta principal de la página.

Son una forma de guardar datos en caché durante un tiempo determinado, usando la misma tabla wp_options pero con expiración automática. Se guardan con set_transient("clave", $valor, 3600) (una hora, en este ejemplo) y se leen con get_transient("clave"), que devuelve false si ya caducó. Son útiles para no repetir cálculos o peticiones costosas en cada carga de página.

Un rol (administrador, editor, autor, colaborador, suscriptor) es un conjunto con nombre de capacidades concretas, como publish_posts o manage_options. En vez de comprobar el rol directamente, lo correcto es comprobar la capacidad con current_user_can("manage_options"), porque los roles se pueden personalizar y una misma capacidad puede pertenecer a varios roles.

Es un modo de instalación que permite gestionar varias webs (una red) desde una única instalación de WordPress, compartiendo núcleo, plugins y usuarios, pero con contenido y, normalmente, tablas de base de datos independientes para cada sitio. Se activa definiendo una constante en wp-config.php y es una decisión que conviene tomar desde el principio del proyecto, porque migrar a Multisite después es más laborioso.

WordPress no depende de un cron del sistema operativo para tareas programadas (como publicar una entrada programada o revisar actualizaciones): usa wp-cron, que se dispara con cada visita a la web, comprobando si hay tareas pendientes. Es cómodo porque no requiere configuración en el servidor, pero en sitios con poco tráfico las tareas pueden retrasarse; para mayor precisión se puede desactivar y sustituir por un cron real del servidor que llame a wp-cron.php a intervalos fijos.

Es el orden en el que WordPress busca qué archivo de plantilla usar para mostrar cada tipo de contenido. Para una entrada individual, por ejemplo, prueba primero single-{post-type}-{slug}.php, luego single-{post-type}.php, luego single.php, y así hasta llegar a index.php como último recurso. Entender esta jerarquía es clave para saber qué archivo tocar en un child theme.

Son pares clave-valor adicionales que se pueden asociar a una entrada, página o cualquier post type, más allá de los campos habituales como título o contenido. Se guardan en la tabla wp_postmeta y se leen con get_post_meta($post_id, "clave", true). Son la base sobre la que funcionan plugins de campos avanzados, aunque también se pueden usar directamente sin ningún plugin extra.

Es una acción que se ejecuta al principio de cada carga de una pantalla del panel de administración, antes de que se imprima cualquier HTML. Por eso es el lugar correcto para procesar formularios y hacer redirecciones con wp_safe_redirect(): si ese código se ejecuta más tarde, cuando ya se ha enviado la cabecera del admin, la redirección puede fallar en silencio, como me pasó al principio con el plugin de FAQs.

"mu" es de "must-use": son plugins colocados en la carpeta wp-content/mu-plugins que WordPress activa automáticamente en cada carga, sin que aparezcan en el listado de plugins ni se puedan desactivar desde el panel. Se usan para funcionalidad que debe estar siempre activa pase lo que pase, como la que uso para el aviso de última actualización en Indaga.net.

wp_options guarda la configuración general del sitio: desde el título hasta los ajustes de cada plugin, como pares clave-valor. Las opciones marcadas como autoload ("yes") se cargan de golpe en cada petición, aunque no se vayan a usar en esa página, así que guardar datos grandes o poco usados como autoload puede ralentizar todo el sitio; conviene marcarlos como autoload => "no" si no hace falta tenerlos siempre en memoria.

Es el archivo de configuración principal de WordPress: contiene los datos de conexión a la base de datos (nombre, usuario, contraseña, host), el prefijo de tablas, las claves de seguridad y constantes como WP_DEBUG. Se genera durante la instalación y conviene protegerlo bien, porque contiene credenciales sensibles.

Son cadenas aleatorias (AUTH_KEY, SECURE_AUTH_KEY, LOGGED_IN_KEY...) que WordPress usa para cifrar las cookies de sesión de los usuarios que han iniciado sesión. Cuanto más largas y aleatorias, más difícil resulta falsificar una cookie de sesión válida; WordPress ofrece un generador oficial para crearlas al configurar un sitio nuevo.

Es la función que declara, desde el tema, una zona donde se puede asignar un menú de navegación personalizado, por ejemplo register_nav_menu('top', 'Menú superior'). A partir de ahí, en Apariencia > Menús el usuario puede crear un menú y asignarlo a esa ubicación, que luego se imprime en la plantilla con wp_nav_menu().

Carga un fragmento de plantilla reutilizable dentro de otra plantilla, por ejemplo get_template_part('template-parts/footer', 'widgets') incluye el archivo template-parts/footer-widgets.php. Ayuda a dividir plantillas grandes en piezas más pequeñas y reutilizables, en lugar de repetir el mismo HTML en varios archivos.

Es el editor de contenido por defecto de WordPress desde la versión 5.0, que sustituye al editor clásico de un único campo de texto por bloques independientes (párrafo, imagen, lista, columnas...) que se pueden mover, configurar y combinar. Cada bloque guarda su contenido como HTML con comentarios especiales que WordPress usa para reconocerlo al volver a editar.

Es una acción que se dispara cada vez que se guarda una entrada, página o cualquier post type, ya sea al publicarla, actualizarla o guardarla como borrador. Se usa mucho para validar o procesar datos adicionales al guardar, aunque hay que tener cuidado con las llamadas duplicadas que WordPress hace en ciertos casos, comprobando por ejemplo que no se trate de un autoguardado con wp_is_post_autosave().

Es un sistema de caché en memoria (con wp_cache_get() y wp_cache_set()) pensado para guardar resultados de consultas u operaciones costosas durante una misma petición, o de forma persistente si el hosting tiene instalado algo como Redis o Memcached. Sin un backend persistente configurado, WordPress usa una caché de objetos solo válida durante esa carga de página, que igualmente evita repetir cálculos dentro de la misma petición.

Permite crear endpoints propios dentro de la REST API de WordPress, más allá de los que trae por defecto (entradas, usuarios...). Por ejemplo, se podría registrar una ruta /wp-json/indaga/v1/faqs que devuelva las preguntas frecuentes en JSON, útil si en algún momento se quisiera consumir esos datos desde una aplicación externa o desde JavaScript sin recargar la página.

Es donde WordPress guarda todos los archivos subidos a la biblioteca de medios: imágenes, PDFs, documentos... Se organiza automáticamente en subcarpetas por año y mes (uploads/2026/09/), lo cual evita tener miles de archivos sueltos en una sola carpeta y facilita localizar cuándo se subió algo.

Es una constante que se activa en wp-config.php (define('WP_DEBUG', true);) para que WordPress muestre avisos y errores de PHP que normalmente quedan ocultos, muy útil al desarrollar o depurar un plugin propio. En un sitio en producción conviene tenerlo desactivado, o combinado con WP_DEBUG_LOG para que los errores se guarden en un archivo en vez de mostrarse a los visitantes.