Elegir tema y afinar shikiConfig
El resaltado de fábrica es un punto de partida, no una jaula. En markdown.shikiConfig eliges el tema con theme, montas un par claro/oscuro con themes, decides con defaultColor qué color se escribe en línea y cuál se deja a CSS, controlas el ajuste de línea con wrap y registras gramáticas propias con langs y langAlias. Afinar esa configuración es decidir la apariencia global del código sin escribir un solo estilo a mano.
Shiki viene encendido, pero su apariencia por defecto —el tema github-dark sobre todo el sitio— rara vez es la última palabra. La configuración vive en markdown.shikiConfig, y allí se toman las decisiones que tiñen cada bloque de código del proyecto: qué tema usar, cómo servir a la vez un modo claro y uno oscuro, qué hacer con las líneas largas y cómo enseñar a Shiki lenguajes que no conoce de fábrica. Dominar ese objeto es pasar de aceptar la apariencia por defecto a diseñarla, y hacerlo de forma centralizada, en un único sitio del que bebe todo el Markdown y el MDX.
- Fijar un tema global con
themey reconocer los temas empaquetados con Shiki. - Servir un par claro/oscuro con
themesy las variables CSS que Shiki emite. - Decidir con
defaultColorqué color va en línea y cuál queda para CSS, y ajustarwrap. - Registrar lenguajes y alias propios con
langsylangAlias.
theme: un tema para todo el sitio
El ajuste más directo es theme, que aplica un único tema a todos los bloques. Su valor puede ser el nombre de cualquiera de los temas que Shiki empaqueta —herederos de VS Code— o un objeto de tema completo si quieres uno propio.
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
markdown: {
shikiConfig: {
theme: 'dracula',
},
},
});
El catálogo integrado es amplio: github-dark y github-light, dracula, nord, monokai, one-dark-pro, la familia catppuccin-*, y muchos más. Como un tema no es más que una tabla que mapea ámbitos de la gramática a colores, cambiarlo reescribe la paleta de todo el código sin tocar una sola línea de contenido. Y si ningún tema del catálogo te convence, theme acepta un objeto JSON con la misma forma que un tema de VS Code, de modo que puedes cargar el de tu editor y tener paridad exacta entre lo que escribes y lo que publicas.
themes y el par claro/oscuro
Un sitio moderno suele ofrecer modo claro y oscuro, y aquí theme se queda corto porque fija un solo color por token. La solución es themes, un objeto con al menos las claves light y dark. Cuando lo usas, Shiki no escribe un color fijo: emite, por cada token, un juego de variables CSS en línea —--shiki-light, --shiki-dark, y sus equivalentes de fondo --shiki-light-bg, --shiki-dark-bg—, de modo que una sola generación de HTML contiene los dos temas a la vez.
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
markdown: {
shikiConfig: {
themes: {
light: 'github-light',
dark: 'github-dark',
},
},
},
});
La elegancia del truco está en que el trabajo caro —tokenizar el código, decidir qué es cada pieza— se hace una vez, y solo la decisión barata —qué columna de colores mostrar— se aplaza al cliente mediante CSS. El HTML es más pesado, porque lleva dos colores por token en lugar de uno, pero sigue sin llevar JavaScript: el cambio de tema es puro CSS sobre variables ya calculadas.
defaultColor, wrap y los ajustes finos
Con dos temas surge una pregunta: ¿cuál se ve si el navegador aún no ha aplicado ninguna clase de modo? Eso lo decide defaultColor. Su valor 'light' —el de fábrica— escribe además el color del tema claro como estilo en línea normal, para que el bloque se vea correctamente sin CSS alguno; 'dark' hace lo propio con el oscuro; y false no escribe ningún color por defecto, solo las variables, delegando por completo en tu CSS la elección. Poner defaultColor: false es la opción más limpia cuando controlas tú el cambio de modo, porque evita que un color por defecto compita con el que quieres imponer.
shikiConfig: {
themes: { light: 'github-light', dark: 'github-dark' },
defaultColor: false,
wrap: true,
},
Con defaultColor: false, el cambio de tema es una regla de CSS que selecciona qué variable gana según tu estrategia de modo oscuro. Basta con apuntar a la clase .astro-code y a sus <span> internos.
/* Modo claro por defecto: usa --shiki-light */
.astro-code,
.astro-code span {
color: var(--shiki-light);
background-color: var(--shiki-light-bg);
}
/* Cuando el documento entra en oscuro, cambia a --shiki-dark */
html.dark .astro-code,
html.dark .astro-code span {
color: var(--shiki-dark) !important;
background-color: var(--shiki-dark-bg) !important;
}
El otro ajuste cotidiano es wrap, que gobierna las líneas largas. Con wrap: false —el valor por defecto— las líneas no se parten y el bloque muestra una barra de desplazamiento horizontal; con wrap: true las líneas se ajustan al ancho disponible y saltan de renglón; y con wrap: null Shiki no impone ningún estilo de desbordamiento, dejando que tu CSS decida por completo. La elección es de legibilidad: el desplazamiento preserva la forma exacta del código, el ajuste evita que el lector tenga que desplazarse pero rompe el alineado visual de las líneas.
Todo lo que pongas en markdown.shikiConfig gobierna cada bloque de Markdown y MDX del proyecto desde un único punto. Cambiar el tema del sitio entero, activar el par claro/oscuro o alternar el ajuste de línea es editar este objeto y reconstruir, no revisar página por página. Esa centralización es la misma virtud que ya viste en las colecciones de contenido: la política vive en un fichero, no dispersa por cientos de documentos.
langs y langAlias: lenguajes a medida
Shiki empaqueta cientos de gramáticas, pero a veces necesitas una que no está —un lenguaje de dominio propio, un formato de configuración raro— o quieres que un identificador inventado reutilice una gramática existente. Para lo primero está langs, un array donde registras gramáticas TextMate propias en forma de objeto JSON. Para lo segundo está langAlias, un mapa que hace que un nombre nuevo se comporte como un lenguaje ya conocido.
shikiConfig: {
langs: [
// objeto de gramatica TextMate propio, cargado desde un JSON
miGramatica,
],
langAlias: {
cjs: 'javascript',
conf: 'ini',
dockerfile: 'docker',
},
},
Con ese langAlias, un bloque etiquetado como cjs se resalta con la gramática de JavaScript, y uno etiquetado conf con la de archivos ini. Es la forma barata de dar cobertura a nombres que tus lectores usarán aunque no sean identificadores oficiales, sin escribir ni mantener una gramática nueva. Y cuando de verdad necesitas describir un lenguaje que no existe en el catálogo, langs te deja añadirlo sin salir de la configuración, con la misma forma de gramática que entiende VS Code.
theme
Un unico tema para todo el sitio, del catalogo integrado o un objeto JSON propio.
themes
Un par claro y oscuro que Shiki emite como variables CSS por cada token.
defaultColor
Que color se escribe en linea y cual se deja a tu CSS, o false para decidirlo todo por CSS.
wrap
Ajuste de linea: false desplaza, true parte el renglon, null lo deja a tu hoja de estilos.
flowchart TD
CFG[shikiConfig con themes] --> TOK[Shiki tokeniza una sola vez]
TOK --> VARS[emite variables por token]
VARS --> L[--shiki-light]
VARS --> D[--shiki-dark]
DEF{defaultColor} -->|light| L
DEF -->|dark| D
DEF -->|false| CSS[tu CSS elige la variable]
style CFG fill:#89b4fa,color:#11111b
style CSS fill:#a6e3a1,color:#11111bLa configuración de Shiki enseña, casi sin quererlo, una de las lecciones más duraderas de la ingeniería de software: la separación entre significado y apariencia. Cuando la gramática recorre tu código no le pinta colores; le asigna ámbitos, categorías semánticas que dicen qué es cada pieza —una palabra clave, el nombre de una función, una cadena literal—. Esa es la fase del significado, y es cara, porque exige entender de verdad la estructura del lenguaje. El tema, en cambio, es una tabla trivial: una función que a cada ámbito le asocia un color. Es la fase de la apariencia, y es barata, porque una vez conocido el ámbito, elegir su tono es una simple consulta. Ver esto con claridad reordena todo lo demás. Cambiar de github-dark a dracula no vuelve a analizar tu código: reusa el mismo mapa de ámbitos y solo intercambia la tabla de colores. Servir claro y oscuro a la vez no duplica el análisis: tokeniza una vez y adjunta dos columnas de la tabla, dejando que el cliente elija cuál mirar con una regla de CSS. Incluso el cambio de modo en vivo, que parecería exigir recalcular, no ejecuta ni una línea de resaltador: solo alterna qué variable CSS gana. La moraleja trasciende a Shiki. Siempre que separes la parte cara y semántica de un cálculo —el qué es esto— de la parte barata y cosmética —el cómo se ve—, ganas la libertad de compartir la primera y aplazar la segunda hasta el último momento responsable, hasta el punto exacto donde la decisión es más informada y menos costosa. El análisis se comparte porque el significado no cambia; la presentación se difiere porque su elección es tardía y ligera. Diseñar shikiConfig bien es, en pequeño, practicar esa disciplina: decidir qué pertenece al significado computado una vez, y qué pertenece a la apariencia elegida al final. Quien interioriza esa frontera deja de pensar en “colores del código” y empieza a pensar en capas, y esa forma de pensar reaparecerá en cada sistema serio que construya.
- Cambia el
themeúnico por un parthemescon un tema claro y uno oscuro, y comprueba en el HTML que aparecen las variables--shiki-lighty--shiki-dark. - Pon
defaultColor: falsey escribe la regla de CSS que hace ganar a--shiki-darkcuando el documento está en modo oscuro. - Alterna
wrapentrefalse,trueynullcon un bloque de líneas muy largas y describe qué cambia en cada caso. - Añade un
langAliasque haga que un identificador inventado —por ejemploenv— se resalte con la gramática de otro lenguaje, y verifica que el color aparece.