4 puntos por GN⁺ 2026-03-30 | 1 comentarios | Compartir por WhatsApp
  • Pretext es una biblioteca pura de JavaScript/TypeScript que calcula la altura y la disposición de líneas de texto multilínea sin acceder al DOM, con soporte tanto para navegador como para entorno de servidor
  • Al no usar APIs de medición del DOM como getBoundingClientRect, elimina el costo del reflow de layout y garantiza precisión mediante una lógica propia de medición basada en el motor de fuentes
  • A través de las APIs prepare() / layout(), preprocesa el texto y realiza un cálculo rápido de altura con operaciones puramente aritméticas usando datos de ancho en caché
  • Soporta emoji, texto de dirección mixta (bidi) y varios idiomas, y ofrece los mismos resultados también en Canvas·SVG·WebGL·renderizado del lado del servidor
  • Es un motor de texto de alto rendimiento útil para implementar layouts de UI precisos, como scroll con virtualización, validación de desbordamiento de texto y colocación de texto flotante

Descripción general

  • Pretext es una biblioteca pura de JavaScript/TypeScript para la medición y maquetación de texto multilínea, con soporte para DOM, Canvas, SVG y también renderizado del lado del servidor
  • No usa APIs de medición del DOM (getBoundingClientRect, offsetHeight, etc.), por lo que elimina el costo del reflow de layout
  • Ofrece precisión y alto rendimiento mediante una lógica propia de medición basada en el motor de fuentes del navegador
  • Soporta todos los idiomas, emoji y texto de dirección mixta (bidi), y también maneja diferencias entre navegadores

Instalación y demo

Funciones principales

  • Pretext ofrece dos formas principales de uso
  • 1. Medición de la altura de párrafos sin acceder al DOM

    • prepare() preprocesa el texto, normaliza espacios, separa segmentos, aplica reglas de glue y realiza mediciones basadas en canvas para devolver un opaque handle
    • layout() usa datos de ancho en caché para calcular la altura y la cantidad de líneas con operaciones puramente aritméticas
    • Con el mismo texto y configuración, no es necesario volver a llamar a prepare(), y al redimensionar solo hace falta ejecutar de nuevo layout()
    • Con la opción { whiteSpace: 'pre-wrap' }, conserva tal cual los espacios, tabulaciones (\t) y saltos de línea (\n)
    • Resultado de benchmark: prepare() aprox. 19 ms (sobre 500 textos), layout() aprox. 0.09 ms
    • El valor de altura devuelto puede aprovecharse en funciones de UI como las siguientes
      • Cálculo preciso de altura en virtualización y occlusion culling
      • Sistemas de layout basados en JS (por ejemplo, masonry o estructuras similares a flexbox)
      • Validación de desbordamiento de texto basada en IA
      • Mantener la posición de scroll al cargar texto
  • 2. Construcción manual del layout de párrafos

    • prepareWithSegments() genera datos por segmento
    • layoutWithLines() devuelve el texto y la información de ancho de cada línea en un ancho fijo
    • walkLineRanges() recorre el ancho y el rango de cursor de cada línea sin crear cadenas de texto
      • Ejemplo: permite hacer ajuste de layout tipo búsqueda binaria para probar varios anchos y encontrar una cantidad adecuada de líneas y altura
    • layoutNextLine() realiza el layout línea por línea cuando cada línea tiene un ancho distinto
      • Ejemplo: colocación de texto flotante para hacer fluir texto alrededor de imágenes
    • Este enfoque también puede aplicarse igual en Canvas, SVG, WebGL y renderizado del lado del servidor

Resumen de la API

  • API para medición básica

    • prepare(text, font, options?): analiza y mide el texto, y devuelve un handle para pasar a layout()
    • layout(prepared, maxWidth, lineHeight): calcula la altura del texto y la cantidad de líneas según el ancho y alto de línea dados
  • API para layout manual

    • prepareWithSegments(text, font, options?): devuelve datos a nivel de segmento
    • layoutWithLines(prepared, maxWidth, lineHeight): incluye texto, ancho e información de cursor de cada línea
    • walkLineRanges(prepared, maxWidth, onLine): pasa por callback el ancho y el rango de cursor de cada línea
    • layoutNextLine(prepared, start, maxWidth): realiza el layout en forma de iterador por línea
    • Incluye definiciones de tipos LayoutLine, LayoutLineRange, LayoutCursor
  • Otras utilidades

    • clearCache(): reinicia la caché interna
    • setLocale(locale?): establece la configuración regional y reinicia la caché (sin afectar el estado existente)

Limitaciones y consideraciones

  • Pretext no es un motor completo de renderizado tipográfico
  • Propiedades CSS objetivo por defecto
    • white-space: normal
    • word-break: normal
    • overflow-wrap: break-word
    • line-break: auto
  • Al usar { whiteSpace: 'pre-wrap' }, conserva espacios, tabulaciones y saltos de línea, y aplica tab-size: 8
  • En macOS, la fuente system-ui no es adecuada para la precisión de layout(), por lo que se recomienda usar un nombre de fuente explícito
  • Debido a overflow-wrap: break-word, en anchos muy estrechos puede haber saltos dentro de una palabra, aunque la división se hace solo a nivel de grapheme

Desarrollo

  • Consulta DEVELOPMENT.md para el entorno y los comandos de desarrollo

Contribuciones y contexto

  • Retoma ideas del proyecto text-layout de Sebastian Markbage
  • La arquitectura evolucionó a partir del shaping basado en canvas measureText, el manejo bidi de pdf.js y el diseño de line breaking en streaming

1 comentarios

 
GN⁺ 2026-03-30
Comentarios de Hacker News
  • Este proyecto es realmente impresionante
    Resuelve el problema de calcular de forma eficiente la altura de texto con saltos de línea sin renderizarlo realmente en una página web
    Almacena en caché el ancho y la altura de segmentos divididos por palabras, e implementa directamente el algoritmo de salto de línea del navegador
    Es una tarea muy difícil por el manejo de caracteres diversos como guiones, emoji y chino, además de las diferencias de renderizado entre navegadores, incluido Safari
    Usa el dataset corpora y la página de pruebas de precisión para comparar con navegadores reales

    • Yo también hice algo parecido. Es una versión simple de 2 KB llamada uWrap.js, implementada sin IA
      Con texto ASCII, mi código tarda 80 ms y pretext 2200 ms
      Aún no he probado la precisión, pero planeo hacerlo esta noche
      Ya hay PR abiertas de mejora de rendimiento en issue #18
    • Los motores de maquetación de texto son famosamente difíciles
      Yo también sufrí antes intentando renderizar texto multilínea en canvas
      Este proyecto es mucho más útil porque está conectado directamente con el DOM
      Ejemplo: demo de Scrawl
    • Por la explicación, en realidad parece que renderiza los segmentos en canvas para medirlos
      Podría ser más lento que una API nativa, y no se puede garantizar que use la misma lógica que el renderizado no-canvas del navegador
    • En la práctica, no implementa directamente el algoritmo de renderizado de texto del navegador
      Funciona renderizando en canvas y luego midiendo, y más bien ofrece una API para analizar la maquetación de texto
    • Cuando hacía subtítulos dinámicos para videos de Remotion, sufrí mucho por calcular la altura del texto; esta librería parece que sería de gran ayuda
  • Esta es una función que de verdad se esperaba desde hace mucho tiempo
    Desde antes era difícil implementar bien cosas como acordeones responsivos
    El patrón de evolución de la web siempre ha sido ① aparecen requisitos complejos → ② hacks con JS/CSS → ③ estandarización
    Esta vez, siento que es una etapa 2 bien hecha, no solo un hack
    En RESEARCH.md incluso estudiaron en detalle las diferencias de medición de emoji entre navegadores
    Será difícil de mantener, pero parece un gran punto de inflexión para la evolución de la web

    • Los acordeones responsivos ya se pueden hacer con CSS, pero aun así hacía falta una API así
      Esta vez resulta interesante que la IA se usó activamente en el desarrollo. Parece que la mayor parte se implementó usando el agente de Cursor
  • Según el autor de la librería, hicieron que Claude Code y Codex aprendieran datos de ground truth del navegador e iteraron mediciones durante varias semanas
    Ver este tuit relacionado
    Parece que también se usó Autoresearch en parte

  • Me gustó especialmente el ejemplo de reflow basado en shapes
    Quise aplicarlo a Ensō(enso.sonnet.io), pero me contuve para mantener la simplicidad
    El ejemplo de acordeón también se puede implementar con interpolate-size de CSS
    Ver el artículo de Josh Comeau
    El ejemplo de burbujas de texto se puede implementar de forma similar con text-wrap: balance | pretty

    • Pero balance o pretty no lo resuelven por completo
      Muchas veces no quieres igualar la longitud de las líneas
      Issue relacionado de CSSWG: #191
    • text-wrap ayuda a ajustar la cantidad de palabras por línea, pero el problema del margen derecho sigue ahí
  • pretext no usa canvas.measureText directamente, sino que si pasas el texto y los atributos por una API de JS, calcula automáticamente la maquetación
    Antes había que usar measureText directamente o portar harfbuzz al navegador
    Más que un avance técnico revolucionario, parece el resultado de combinar bien piezas existentes
    Aun así, me da curiosidad la diferencia frente a Skia-wasm / Canvaskit

    • La diferencia es simple. pretext no es wasm
    • Skia es un motor de renderizado enorme. Proporciona una API de renderizado independiente del dispositivo como Flutter
      pretext se diferencia en que implementó renderizado de glifos en TypeScript puro con ayuda de IA
      Se siente como la diferencia entre implementar ffmpeg directamente en C y llamarlo desde Dart
      Este tipo de intento muestra nuevas posibilidades para el FOSS del lado del cliente
    • Si el autor tiene razón, esto traerá grandes cambios para los frameworks GUI web y los editores de texto enriquecido
  • El año pasado hice un sistema de composición para folletos impresos con HTML, y para manejar saltos de línea y evitar líneas huérfanas calculaba repetidamente los límites de cajas con la Selection API
    Todavía funciona bien, pero tiene un hack off-by-one cuyo motivo no entiendo
    La función de generación iterativa de líneas de pretext es realmente bienvenida

  • En Fedora + Firefox todos los demos se ven rotos
    Ejemplo: captura de pantalla

    • Esto demuestra que este tipo de proyecto al final consiste en perseguir casos límite sin fin
  • Este tipo de función en realidad debería existir como API estándar del navegador
    Me pregunto cómo se hace una solicitud de funcionalidad al W3C y si existe algo como votación de la comunidad

    • Ya existe la propuesta de Font Metrics API
      Pero los fabricantes de navegadores no la priorizan
      Actualmente Chrome está más enfocado en APIs relacionadas con IA
  • El motor Sciter ya tiene la función Graphics.Text
    Es un elemento de renderizado de texto basado en canvas al que se le pueden aplicar directamente estilos CSS

  • La búsqueda de texto del navegador (Ctrl+F) no funciona bien en listas con scroll virtual
    Para resolver este problema, quizá haga falta una nueva API de “Search” en lugar de JS

    • Existía la Virtual Scroller API, pero casi no ha avanzado
      Proyectos relacionados: Display Locking, documentación de MDN
    • La búsqueda nativa solo recorre nodos que existen en el DOM
      Los elementos fuera de pantalla de una lista virtualizada no están en el DOM, por lo que no se pueden buscar
      Para resolverlo haría falta un nuevo contrato del navegador que integre selección, foco, posición de scroll y navegación entre coincidencias
      Pero es poco probable que los sitios lo usen de forma consistente