---
title: "Tipografía canónica · IBM Plex Sans + JetBrains Mono"
parent_adr: ADR-021
date: 2026-05-09
status: accepted
owners: [martin, claude-w3-style-typography-dark]
applies_to: cis-style v6+
repo: cis/cis-style
last_review: 2026-05-09
supersedes: null
---

# typography-001 · Tipografía canónica del sistema de diseño CIS

> **Parent ADR**: [ADR-021 · Tipografía + design system governance](../../cis-plan/DECISIONS.md#2026-04-29--adr-021--tipografía--design-system-governance)
> Este documento es la **referencia de implementación** local del repositorio
> `cis-style`. ADR-021 es el documento maestro y contiene el contexto, las
> alternativas y las consecuencias completas. Acá fijamos los detalles
> mecánicos: pesos, paths, CSS imports, fallbacks y loading strategy.

## Decisión (resumen)

- **Display sans**: `IBM Plex Sans` — pesos **400, 500, 600, 700, 800**.
- **Mono**: `JetBrains Mono` — pesos **400, 500, 700**.
- **Hosting**: self-hosted en `style.innovacionsantiago.cl/v6/fonts/`. **Cero requests a Google Fonts CDN**.
- **Subset**: `latin-ext` (incluye `ñ`, `áéíóú`, `¿¡`, currency).
- **CSS imports**: `/v6/fonts/css/ibm-plex-sans.css` y `/v6/fonts/css/jetbrains-mono.css` (uno por familia).
- **Variables CSS canónicas** (definidas en `src/tokens/tokens.css` y `brands/cis/tokens.css`):
  ```css
  --cis-font-display: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --cis-font-sans:    "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --cis-font-mono:    "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  ```

## Pesos · uso semántico

| Peso | Familia | Uso |
|---|---|---|
| **400** | Plex Sans | Body, párrafos largos, listas |
| **500** | Plex Sans | Labels de form, captions con énfasis suave |
| **600** | Plex Sans | **Display headings** (`<h2>`, `<h3>`, `<h4>` de eyebrow) — semibold |
| **700** | Plex Sans | `<strong>`, `<b>`, links activos, lockups secundarios — bold |
| **800** | Plex Sans | **Impact / CTA hero** — wordmark CIS, `<h1>` editorial, números grandes — heavy |
| **400** | JetBrains Mono | `<code>`, `<pre>`, dashboards, IDs, hashes |
| **500** | JetBrains Mono | KBD, valores tabulares destacados |
| **700** | JetBrains Mono | Headings monoespaciados (raros, solo dashboards técnicos) |

Las variables responsivas de tamaño (`--fs-h1`, `--fs-h2`, `--fs-h3`) se mantienen en `tokens.css` y son ortogonales a los pesos.

## Loading strategy

1. **`font-display: swap`** — siempre. Render inmediato con fallback system, swap a Plex/JetBrains cuando llegue el woff2. Sin FOIT.
2. **Preload selectivo** en `<head>` de cada frontend. Solo los pesos críticos para el primer paint:
   ```html
   <link rel="preload"
         href="https://style.innovacionsantiago.cl/v6/fonts/ibm-plex-sans/plex-sans-600.woff2"
         as="font" type="font/woff2" crossorigin>
   <link rel="preload"
         href="https://style.innovacionsantiago.cl/v6/fonts/ibm-plex-sans/plex-sans-400.woff2"
         as="font" type="font/woff2" crossorigin>
   ```
   Pesos 500/700/800 cargan on-demand vía el `@font-face` declarado en `ibm-plex-sans.css`. JetBrains Mono se preloadea solo en frontends con dashboards/`<pre>` heavy (cis-admin, observatory, cochid-datos).
3. **Cache**: Caddy sirve `/v6/fonts/*.woff2` con `Cache-Control: public, max-age=31536000` (1 año). Filename hash en versión futura habilitaría `immutable`. Mientras tanto el ETag ya alcanza para revalidación cheap.
4. **Subset `latin-ext`**: incluye los caracteres usados en castellano + currency. Evita los ~40% extra de los archivos full Unicode.

## NO usar Google Fonts CDN

**Prohibido.** Razones:

- **Privacy**: cada visitante envía IP + UA a `fonts.googleapis.com`. Para CIS (procesa datos chilenos sensibles) es inaceptable.
- **GDPR / Ley 19.628**: jurisprudencia europea (Múnich 2022) ya consideró el uso de Google Fonts CDN como transferencia internacional ilegal de datos personales sin base legal.
- **Outage risk**: si Google Fonts cae o es bloqueado por firewall corporativo (común en Chile en redes gubernamentales), nuestros frontends pierden tipografía.
- **Reproducibilidad**: si Google cambia su CDN, su ToS o sus headers, nos rompe. Self-host elimina la dependencia.

**Excepción**: ninguna. Si encuentras `fonts.googleapis.com` o `fonts.gstatic.com` en cualquier `<link>` de un consumidor de cis-style, es un bug. Reportar en CHANNEL.md y abrir PR de remediación.

## Estado de las fuentes (deuda técnica · ✅ resuelta 2026-05-09 · W11.2)

**Status**: self-hosted LIVE. Las fuentes se sirven desde `https://style.innovacionsantiago.cl/v6/fonts/`.

### Resolución (agent-w11-fonts · 2026-05-09)

1. **Descarga**:
   - IBM Plex Sans 1.1.0 → release `@ibm/plex-sans@1.1.0` (https://github.com/IBM/plex/releases/tag/%40ibm/plex-sans%401.1.0). Tomados los archivos `complete/woff2/IBMPlexSans-{Regular,Medium,SemiBold,Bold}.woff2` (~64 KB c/u, full Unicode incluyendo latin + latin-ext + cyrillic + greek; el subset `latin-ext` separado solo ahorra ~40 KB y agrega complejidad de mantención).
   - JetBrains Mono v2.304 → release `v2.304` (https://github.com/JetBrains/JetBrainsMono/releases/tag/v2.304). Tomados `fonts/webfonts/JetBrainsMono-{Regular,Medium,Bold}.woff2` (~93 KB c/u).
2. **Paths físicos**:
   ```
   cis-style/v6/fonts/ibm-plex-sans/IBMPlexSans-{Regular,Medium,SemiBold,Bold}.woff2
   cis-style/v6/fonts/ibm-plex-sans/LICENSE.txt           # SIL OFL 1.1
   cis-style/v6/fonts/jetbrains-mono/JetBrainsMono-{Regular,Medium,Bold}.woff2
   cis-style/v6/fonts/jetbrains-mono/OFL.txt              # SIL OFL 1.1
   cis-style/v6/fonts/css/ibm-plex-sans.css               # @font-face standalone (paths relativos)
   cis-style/v6/fonts/css/jetbrains-mono.css
   ```
   Total ~576 KB committed.
3. **CSS bundling**:
   - `src/fonts/fonts.css` declara los `@font-face` con URLs **absolutas** a `https://style.innovacionsantiago.cl/v6/fonts/...` (necesario porque `cis.min.css` se sirve cross-origin a 16+ frontends).
   - `src/input.css` lo importa después de `tokens` y `brands/cis/tokens.css`.
   - `build.sh` paso 6b copia `v6/fonts/` → `dist/v6/fonts/`.
4. **Pesos servidos**:
   - **IBM Plex Sans**: 400 (Regular), 500 (Medium), 600 (SemiBold), 700 (Bold). El peso **800 mapea a Bold (700)**: IBM Plex Sans no ships ExtraBold real; su rango libre es 100..700. Cuando IBM publique Plex Variable con ExtraBold, subir el archivo y reapuntar el `@font-face` 800.
   - **JetBrains Mono**: 400, 500, 700.
5. **Smoke verificado** (2026-05-09):
   - `curl -sI https://style.innovacionsantiago.cl/v6/fonts/ibm-plex-sans/IBMPlexSans-SemiBold.woff2` → 200, `content-type: font/woff2`, `access-control-allow-origin: *`.
   - `curl -sI https://style.innovacionsantiago.cl/v6/fonts/jetbrains-mono/JetBrainsMono-Regular.woff2` → 200.

### Pendientes finos (no bloquean)

- Caddy actualmente sirve los woff2 con `Cache-Control: public, max-age=86400`. La regla `@immutable` del Caddyfile (`max-age=31536000, immutable`) no está prevaleciendo (probable interferencia Cloudflare). Cuando se quiera bajar bytes recomprados, ajustar headers en CF + Caddy. ETag funciona, así que cache revalidation ya es cheap.
- Subset `latin-ext` dedicado: deuda diferida. Actualmente shippeamos los woff2 `complete` (~64 KB Plex / ~93 KB JBMono). Para ahorrar ~40% por archivo en el futuro, regenerar con `pyftsubset` y `unicode-range: U+0000-00FF, U+0131, U+0152-0153, ...`.

## Consecuencias

- **+** Identidad visual coherente cross-ecosistema (16 brands).
- **+** Privacy-respectful por convicción CIS.
- **+** Reproducible: zero dependency externa.
- **+** Sub-brands declarativos en un solo lugar (`brands/<slug>/tokens.css`); fuentes solo se overridean con justificación documentada.
- **−** ~600KB extra hosted en `cis-style/v6/fonts/` (mitigado por cache 1 año + preload selectivo).
- **−** Deuda explícita: faltan los woff2 + el CSS `@font-face` (acción pendiente arriba).

## Cumplimiento (checklist por consumidor)

- [ ] El `<head>` del HTML **NO** carga `fonts.googleapis.com` ni `fonts.gstatic.com`.
- [ ] El `<head>` carga `https://style.innovacionsantiago.cl/v6/cis.min.css` (que importa los `@font-face` self-hosted).
- [ ] Si el frontend hace LCP heavy con tipografía custom: `<link rel="preload">` para los 1-3 pesos críticos.
- [ ] El consumidor declara `font-family` solo vía `var(--cis-font-display)` / `var(--cis-font-sans)` / `var(--cis-font-mono)` — nunca hardcoded en CSS de componente.

## Referencias

- ADR-021 maestro: `/srv/projects/cis/cis-plan/DECISIONS.md` § 2026-04-29.
- CONSTITUTION.md § 2.1 (tipografía pesos canónicos).
- VISION.md § Tipografía cita ADR-021.
- IBM Plex SIL OFL 1.1: https://github.com/IBM/plex/blob/master/LICENSE.txt
- JetBrains Mono Apache 2.0: https://github.com/JetBrains/JetBrainsMono/blob/master/OFL.txt

---
_Curated 2026-05-09 · agent-w3-style-typography-dark._
