firmeVolver

Cómo está hecho

Un sitio construido sobre una sola línea.

Nombre

Firme, porque eso es lo que dice.

Una transacción queda en firme cuando ya no puede cambiar. Eso es lo único que este producto hace: decirte qué está en firme y qué todavía no. El nombre es la promesa en una palabra colombiana, y no una metáfora náutica que había que traducir.

Se investigó antes de decidir: qué dicen los estudios y los críticos de marca en 2026, qué colores y tonos ya tienen dueño entre las apps de plata en Colombia, y qué construye confianza en un sector en el que la gente desconfía por defecto. Los tres informes están en el repositorio, en docs/brand.

Concepto

La línea.

Una sola línea horizontal, y siempre la dibujan los datos: es el cero de la banda que abre cada pantalla, la regla de cada tabla, la base sobre la que se apoya la marca. Arriba, lo que entró; abajo, lo que salió; lo que todavía no está en firme se dibuja punteado. No hay agua, ni cielo, ni ninguna imagen que no signifique un número.

El único movimiento que la marca reclama como suyo es el asentamiento: cuando llega el extracto y el mes queda en firme, la línea punteada se vuelve continua y las cifras vuelven a contarse.

Tipografía

Besley × Atkinson Hyperlegible Next × Fragment Mono.

Besley (variable, OFL), un slab Clarendon — la letra de los membretes bancarios y los extractos impresos — para los titulares, en un peso propio (580) que nadie más despacha. Atkinson Hyperlegible Next, del Braille Institute, para cada palabra de la interfaz: se diseñó para que ninguna letra se confunda con otra, que es exactamente lo que se le pide a un producto de plata. Fragment Mono para cada cifra, tabular y con el cero tachado; nunca para etiquetas.

Las fuentes se sirven desde el propio sitio, subconjuntadas a latín + español. Ningún CDN.

Color

Papel, tinta, dos significados.

Papel #FBFAF7 y tinta #0F1A22, con rampas de texto verificadas AA. El cromo es monocromo: tinta sobre papel, papel sobre tinta. De noche el papel se apaga (#0E1216) y la línea la carga la luz; es el mismo instrumento con menos luz, no otra piel.

Solo dos colores significan algo. El verde es plata que entra. La señal (#D8641A) es «falla / pide algo de ti» y llega siempre con un punto y una palabra; nunca es un botón ni un título. La paleta categórica de ocho posiciones se validó sobre papel, tinta y noche — separación para daltonismo, contraste ≥ 3:1 — y nunca aparece en el cromo.

Instrumento

Un espacio de trabajo, no una página.

La pantalla es el instrumento: una barra arriba, tres paneles que se desplazan cada uno por su cuenta, una barra de estado abajo con el registro de la última pasada. Nada queda debajo del pliegue; la página nunca se desplaza. A la izquierda, los meses, cada uno con su neto sobre una línea; en el centro, el libro como una tabla densa — treinta y dos píxeles por fila, un mes entero a la vista — agrupado por categoría, por comercio o movimiento a movimiento, con la banda como cabecera del panel; a la derecha, lo que seleccionaste: la lectura de la ventana cuando no hay nada, y si no, la fila, el comercio, la persona o la categoría con todo lo que Firme sabe.

La banda es un SVG en unidades virtuales (viewBox 1000 × 256, estirado sin conservar proporción, trazos que no escalan) con lo que salió pintado como una aguada de tinta bajo el cero, y encima una capa HTML con las etiquetas y los controles; un clic elige un mes, un arrastre varios. El libro se opera desde el teclado — j y k mueven el cursor, Enter abre o selecciona, c cambia la categoría en la propia fila, g seguido de c, m o v cambia la agrupación, ? muestra la hoja — y cada estado es una URL: la ventana, la agrupación, el camino abierto, la fila seleccionada, los filtros. ⌘K abre una hoja que busca en el libro o le habla a Firme con la misma caja.

Para el mes que corre, el instrumento dice hacia dónde va, no solo por dónde ha ido: los cobros comprometidos que aún no han caído, la nómina que llega en su ritmo, y un cierre estimado que solo se afirma hasta donde los extractos permiten medir el ritmo — la tarjeta sobre los días que su extracto cubre, la cuenta sobre lo normal si el suyo no ha llegado, y la frase dice cuál. La matriz pone cada categoría contra cada mes, el libro entero en una pantalla.

Movimiento

Nada secuestra el scroll.

Dos registros y uno por defecto: lo productivo dura entre 150 y 240 ms con una sola curva; lo expresivo se reserva para el asentamiento. Un solo bucle requestAnimationFrame para todo lo que se mueve con el scroll o el tiempo; los suscriptores leen una posición muestreada una vez por cuadro. La suavidad viene de interpolar valores derivados (el nivel del agua, un contador), no de mover el documento: el teclado, «buscar en la página» y la restauración de scroll del router siguen funcionando.

Las revelaciones usan IntersectionObserver y un atributo de datos; los números se asientan dígito a dígito en ≤ 900 ms. Con movimiento reducido, todo aparece ya asentado.

Datos

Contrato primero.

Cada respuesta del API se valida con zod en el borde; los montos viajan como centavos enteros y se formatean con una función que coincide byte a byte con el backend en Go. El modo mock (MSW) sirve el producto completo con las mismas formas, los mismos documentos de error RFC 7807 y los mismos 422 que el API real; la identidad habla con Cognito por su protocolo JSON público, sin SDK.

Cada movimiento llega con su categoría ya resuelta por el API — una regla de comercio, o una recategorización tuya que se reaplica a todo el historial — y cuando Firme no sabe, la fila dice «Sin categoría», nunca adivina. El libro completo se descarga una vez por sesión y se indexa en memoria: cambiar de ventana en la línea es una recomputación síncrona, sin una sola espera.

Pila

Vite 8 · React 19 · TypeScript estricto.

TanStack Router (rutas por archivo, parámetros de búsqueda tipados con zod, transiciones de vista solo entre pantallas de la app) y TanStack Query. Tailwind v4 sobre tokens CSS escritos a mano. Gráficos propios en SVG con d3-shape y d3-scale; cada gráfico lleva una tabla gemela solo para lectores de pantalla. Radix únicamente para diálogos, menús y la hoja de comandos. Vitest + Testing Library; Playwright en escritorio, Pixel 7, de noche y sobre el demo publicado, con axe en WCAG A/AA como puerta en cada ruta y cada estado abierto.

El demo público es esta misma aplicación compilada con base /demo/: el mismo bucket, un comportamiento propio en CloudFront que la marca como no indexable, y un service worker que responde con los datos de ejemplo. Un despliegue real se niega a llevar el demo; el del demo se niega a llevar Cognito.