Progreso y estado. Lo que tienen en común: los tres pueden mentir si el número que reciben no es el real.
Tres indicadores. El del medio es el interesante: existe un elemento nativo (<progress>) y se descartó midiendo, no por comodidad. Todo el color sale de tokens y está medido en los dos temas (lib/theme-contrast.test.ts).
El de Subframe se dimensiona por font-size, y eso ya costó: al cambiar el icono de una fila del menú por el spinner, la etiqueta se movía de lado en el instante del clic (está escrito en components/NavLink.tsx:112). Aquí el tamaño es una caja explícita en la rejilla de 4 px: 12, 16 y 24.
El anillo se dibuja en currentColor. El de Subframe fija text-brand-600, y medido, ese anillo dentro de un botón de marca da 1.0000:1 — el mismo color, invisible. Fíjate en la fila de abajo: el mismo componente, cuatro tintas, sin una prop de color.
Este componente dibuja, no decide: el retardo de 1 s y el mínimo de permanencia viven en lib/usePendingIndicator.ts, que es el único sitio del portal que puede decidirlo — por eso los dos botones de arriba tardan un segundo en enseñar el suyo aunque ya estén ocupados. Y bajo prefers-reduced-motion el giro no se apaga: se sustituye por un latido. Apagarlo dejaría un arco quieto, que no dice «trabajando» — y como el spinner es mudo para un lector de pantalla, ese usuario se quedaría sin ninguna señal. (Esa parte es recomendación medida, no norma: SC 2.3.3 no aplica aquí, porque exige que la animación la dispare una interacción.)
Para verlo: en GNOME, Configuración → Accesibilidad → Reducir animación; en macOS, Accesibilidad → Pantalla → Reducir movimiento. El anillo deja de girar y empieza a latir.
La única vía estándar de colorear un <progress> es accent-color, y es un solo color. Para pintar relleno y pista por separado hacen falta ::-webkit-progress-value y ::-moz-progress-bar, que MDN marca literalmente «Not part of any standard»; y appearance: base, que es el arreglo en camino, no lo soporta todavía ningún navegador. Esta barra necesita dos tokens que además se invierten con el tema. Tampoco ahorra accesibilidad: el rol progressbar exige nombre accesible igual («Accessible Name Required: True», ARIA 1.2).
Por eso es neutral-300 y no el neutral-100 de Subframe: contra la página en tema claro, ese -100 mide 1.0567 — la pista no existe. Y el relleno no se puede aclarar para compensar: brand-500 contra esta pista da 2.2402, bajo el 3:1 de SC 1.4.11. Baja la barra al 4 % arriba y mira si sigues viendo dónde termina.
lib/progress.ts fija la invariante del repo: un porcentaje solo puede existir cuando el recuento de trabajo hecho es real. Una barra indeterminada en el kit sería la puerta de atrás — quien no tiene porcentaje la usaría «para que se vea algo», y una barra promete «esto de avance», no «sigo vivo». Por eso percent es obligatorio y <Progress /> no compila. El de Subframe tiene el defecto exacto al revés: value = 30como valor por defecto, o sea que una barra sin props dibuja un 30 % inventado.
Cuando no se sabe la posición, lo honesto es el inventario del trabajo más el tiempo transcurrido (components/StepProgress.tsx); y si no se sabe ni eso, un Loader.
Relleno -100 + tinta -800, uniforme. El de Subframe usa -800 en cuatro tonos y neutral-700 en el quinto sin que nada lo explique. El glifo se dimensiona con una regla CSS sobre el svg, no por font-size: así vale para las dos librerías de iconos que conviven hoy (los Feather* se declaran en em, los de lucide en píxeles).
El tipo no deja escribir una teja que no lo haya decidido: name (la anuncia) o decorative (desaparece del árbol). Las dos de abajo se ven idénticas y suenan distinto. El de Subframe es un div con un svg dentro y cero tratamiento ARIA, así que lo que se oye lo decide el lector de pantalla, no nosotros. Y el icono va aria-hidden siempre, también cuando la teja tiene nombre: es un nodo ajeno que puede traer su propio <title>, y si no se leería «llave, Key Vault».
Medido contra la página en tema claro, warning-100 da 1.0466 y success-100 1.0323: el cuadro prácticamente no existe y lo único que queda es la forma del icono. No es un descuido que se arregle aquí — es la banda pálida comprimida del tema claro, fichada como deuda medida, y cerrarla obliga a re-tonar las tintas de acento. Cambia a tema oscuro y compara: ahí los mismos cuadros sí se ven.