La odisea de construir Fluent UI para Svelte La odisea de construir Fluent UI para Svelte
La mayoría de las apps de escritorio hechas con tecnologías web parecen páginas disfrazadas: interfaces plásticas, cero accesibilidad y nada nativas. Quería el diseño real de Microsoft Fluent 2 en mis apps con Tauri y Svelte, pero no existía nada a la altura. Así que me metí en la boca del lobo para construirlo yo mismo.
Published: 15 de octubre de 2026 - Updated: 16 de octubre de 2026
Author: Jorge Acosta
Reading Time: 9mins

Todo empezó con una frustración muy específica...

Estaba diseñando/construyendo aplicaciones de escritorio multiplataforma usando Tauri (y evaluando Electron) con Svelte. La promesa de estas tecnologías es increíble, pero había un problema enorme: la interfaz de usuario.
Si miras la mayoría de las aplicaciones hechas con tecnologías web para escritorio ahí afuera, se nota a kilómetros que son páginas web disfrazadas.

Están llenas de interfaces extrañas que no respetan las convenciones del sistema operativo, con cero accesibilidad (intenta usarlas solo con el teclado y sufrirás) y, siendo honestos, muchas veces son visualmente feas o se sienten "plásticas".
Yo no quería eso. Quería que mis aplicaciones en Windows y escritorio lucieran, se sintieran y se comportaran como una auténtica aplicación de Microsoft (Microsoft si lees esto, no estoy intentando... suplantar/robar/quebrantar/hacerme pasar/o cualquier termino fancy/legal que tus abogados inventen... usar tu imagen, es que FluentUI/WinUI es genial!). Quería la estética limpia, las capas, la precisión... basicamente todo lo que ofrece FluentUI 2. (Pero carajos, como es de complejo!)
Hasta hace poco conversaba con personas del equipo de mantenedores de Svelte. Y de verdad... Que chat tan fantastico. Conversaciones sobre
A11y, la manera correcta de categorizar componentes sin mezclarlos con la logica del negocio... Un poco de pelea sobreAI... creo que deberia hacer esto mas seguido 😁😁 realmente ayudo a reafirmar mi postura sobre ela11yyfluentui
La búsqueda infructuosa y el laberinto de la documentación

Pensé: "Seguro alguien ya resolvió esto para Svelte". Busqué por todo GitHub y npm. Encontré proyectos abandonados hace años en Svelte 3 (que de hecho lo llegue usar en un Hackaton de Midu.dev hace mucho tiempo, pero no gane el premio 🥲), wrappers incompletos de Web Components que pesaban una tonelada, o librerías que imitaban vagamente el estilo antiguo de Windows 10 (Metro/Fluent 1) pero sin alma ni mantenimiento.
Si quería Fluent 2 real en Svelte, tenía que construirlo yo mismo. 😣
Y ahí empezó el verdadero dolor de cabeza: descifrar la documentación oficial de Microsoft.
Entender Fluent UI a nivel de ingeniería es meterse en la madriguera del lobo
Tienes especificaciones de diseño en Figma, documentación repartida entre Fluent 2 Web, WinUI 3 y el repositorio monstruoso de @fluentui/react-components. Y todos mencionan cosas diferentes para el mismo componente! Incluso tienen nombres distintos en cada documentacion.
Pasé semanas haciendo ingeniería inversa: destripando cómo Microsoft calcula cada token de color, las capaz, cómo manejan las curvas de animación, qué tamaños exactos de padding y border-radius hacen que un botón o un input se sienta "nativo" de Windows 11 y no como un clon barato de Bootstrap (Sin ofender jajaja).
Lo que empezó como "solo necesito unos cuantos componentes para mi app de Tauri" se convirtió en meses de sudor, noches en vela y cientos de horas invertidas. Así nació FluentUI Svelte.
Actualmente el proyecto está en su versión v0.6 (Beta). Funciona de maravilla en producción, aunque todavía hay muchas cosas por pulir y agregar. Pero antes de llegar a la v1.0, quiero compartir la complejidad real de lo que hay debajo del capó.
1. Integración real: Pensando en Svelte, no en React
Traducir la arquitectura mental de React (donde vive el Fluent UI oficial de Microsoft) a Svelte fue el primer gran muro. En React todo son hooks, contextos pesados y renders virtuales. Portar eso literalmente a Svelte habría sido un insulto al compilador. Y puedes encontrar intentos de hacer esto ahi afuera.
Reescribí cada patrón desde cero para que se sintiera 100% nativo:
- Reactividad: Aprovechando el poder de las Runes (
$state,$derived,$effect) para que el estado de cada componente sea quirúrgico. En una app de escritorio (Tauri/Electron), la fluidez a 60fps no es negociable. - Composición limpia con Snippets y TypeScript: Nada de configuraciones oscuras. Quería que al importar un componente en tu IDE, el tipado estricto y el autocompletado te dijeran exactamente qué hacer sin tener que vivir pegado a la documentación. (Aun necesita retoques 😅)
- Simplificar el Layering de FluentUI: Si investigas como funciona el UI te daras cuenta que usan una ideologia de apilamiento de capaz (Layering) que la combinacion de cada una resulta en el efecto que todos conocemos. Fue especialmente retador y aqui va un secreto que ya no sera secreto: intente construir FluentUI/WinUI hace mas de 6 años. Pero en ese momento era imposible recrear los efectos con solo CSS sin recurrir a la reciente (2020) implementacion de CSS Houdini asi que lo descarte.
- Componentes no opinionados: El hoy en dia casi todas las librerias estan pensadas para que sean componentes con soporte a ser
opinionados(puedes desentrañar sus tripas a tu gusto desde tu repositorio), pero esa idelogia no va bien cuando lo que quieres es que tu UI sea literalmente FluentUI o Material Design o tal vez LiquidGlass, solo quieres tomar un componente al que le digas que sucede cuando el usuario interacciona con el sin preocuparte dea11yni decontrasteni de nada que no sea "que hace ese boton cuando le dan click" y tal vez agregarle un icono. (No me mal entiendan, en muchas ocaciones que una libreria seaopinionadaes excelente idea). Pero confluentui-sveltequeria absorber esa complejidad... Tu solo te encargas de "que hace" el componente.
2. Accesibilidad (a11y): Lo que separa un juguete de una herramienta profesional
Una de mis mayores quejas con las librerías UI "bonitas" que abundan en la web es que ignoran por completo la accesibilidad. En una aplicación de escritorio real, el usuario experto vive en el teclado: usa Tab, Shift+Tab, Escape, las flechas direccionales y Enter por memoria muscular.

Aquí es donde se fue más del 60% del tiempo de desarrollo:
- Navegación por teclado real (Roving Tabindex & Focus Trap): Construir un
Dialog, unMenuo unosTabsvisualmente toma un par de horas. Hacer que el foco quede atrapado dentro de un modal, que al cerrarse regrese exactamente al botón que lo invocó, o que las flechas recorran los ítems saltando los deshabilitados... eso toma días de pruebas y errores. Microsoft tiene una libreria para esto:tabster... Pero para saber porque esta libreria no fue la que use para construirfluentui-sveltepuedes ver:TabSpot de la locura a la salvacion - WAI-ARIA sin fricción: Gestión automática de roles,
aria-expanded,aria-selected,aria-controlsy lectores de pantalla para que la accesibilidad venga de fábrica, no como un "extra" opcional. - Focus Visible al estilo Microsoft: Implementación exacta del anillo de foco de alto contraste de Fluent, que solo aparece cuando navegas con teclado (
:focus-visible) y no ensucia la interfaz cuando haces clic con el mouse.
3. CSS al límite: Exprimiendo los últimos features de la plataforma
Para lograr que una interfaz se sienta nativa sin llenar el hilo principal de JavaScript con cálculos de estilos, aposté todo al CSS moderno.

- El sistema de Tokens de Fluent 2: Mapeé la arquitectura completa de Design Tokens de Microsoft a Custom Properties de CSS. Superficies, fondos neutros, colores de marca, sombras de elevación y curvas de desaceleración. Cambiar de modo claro a oscuro es instantáneo.
- Selectores de nueva generación (
:has(),:is(),:where()): Gracias a:has(), los contenedores padres pueden reaccionar al estado de sus hijos (por ejemplo, cambiar el borde de un contenedor completo cuando el<input>interno tiene el foco o un estado de error) con cero líneas de JavaScript. Y con:where()mantengo la especificidad baja para que nunca tengas que pelear usando!important. (Aun trabajo en esto :v ) - CSS moderno y APIs nativas: Uso de CSS Nesting, funciones de color dinámicas y aprovechamiento de las capacidades de los navegadores modernos (como los Webviews actualizados en Tauri/Edge WebView2) para lograr transiciones y posicionamientos ultra ligeros. Tambien
color-mix,field-sizing: content,mask,backdrop-filtery varios otros. Bueno... el unico componente en toda la libreria que tiene un Polyfill es elExpander(pronto lo eliminare).

4. Documentación, CI/CD y el nacimiento de Propsmith

Nadie usa una librería que no entiende cómo integrar. Construir fluentui-svelte.pages.dev/docs fue prácticamente desarrollar un segundo proyecto en paralelo.
Y aquí me topé con una pesadilla clásica de mantenimiento: la duplicación de tipos.
Si cambiaba una propiedad en un componente de Svelte o actualizaba una interfaz de TypeScript, tenía que ir manualmente a la documentación a actualizar las tablas de propiedades, los valores por defecto y las descripciones. Hacer esto dos o tres veces por cada uno de los decenas de componentes no solo era agotador, sino una receta segura para tener documentación desactualizada.
Creando mi propia herramienta: Propsmith
Como no quería pasar mi vida reescribiendo lo mismo tres veces, tuve que hacer una pausa e ingeniármelas para crear otra herramienta open source desde cero: Propsmith.
Propsmith es un extractor de tipos diseñado para leer directamente el código fuente y las definiciones de TypeScript, extraer las propiedades, tipos, valores por defecto y comentarios, y transformarlos en datos estructurados listos para consumir en la documentación.
Construir un extractor de tipos tiene su magia oscura (lidiar con ASTs, tipos complejos, uniones e interfaces heredadas es bastante tricky), pero una vez afinado, funciona de maravilla.
Esto:
/** @propsmith Button */
export type ButtonProps = {
/** The visual size of the button. */
size?: Sizes;
/** Whether the button is disabled. @default false */
disabled?: boolean;
/**
* Visual style of the button.
* @deprecated Use `variant` instead.
*/
appearance?: string;
};
export type Sizes = "small" | "medium" | "large";Se convierte en esto:
| Name | Type | Default | Description |
| ---------------- | -------------------------- | ------- | ------------------------------------------------------------------ |
| `size` | `"small"` | `"large"` | | The visual size of the button. |
| `disabled` | `boolean` | `false` | Whether the button is disabled. |
| ~~`appearance`~~ | `string` | | Visual style of the button. **Deprecated:** Use `variant` instead. |Ahora, el código fuente es la única fuente de la verdad:
- Escribo o modifico el componente y sus tipos una sola vez.
- En el pipeline de CI/CD, Propsmith entra en acción automáticamente y extrae toda la metadata de las props.
- Se ejecutan las validaciones estáticas (
svelte-check), el empaquetado para Vite/SvelteKit y todo se despliega en Cloudflare Pages con la documentación siempre 100% sincronizada al último commit.
5. El camino hacia la v1.0 (y cómo puedes sumarte)
Llegar a esta versión v0.6 ha sido una maratón brutal (y los nuevo 32 issues me daran trabajo). Hoy puedo usar FluentUI Svelte en mis propios proyectos y sentir ese orgullo de ver una aplicación en ventana que parece salida directamente de los laboratorios de diseño de Redmond, con el peso pluma y la velocidad de Svelte.
Pero un sistema de diseño nunca está realmente "terminado". Todavía hay bugs que cazar, casos borde de accesibilidad que perfeccionar y nuevos componentes en la lista de espera.
Si alguna vez has sentido esa misma frustración al crear apps en Svelte o Tauri/Electron, te invito a probarlo:
- Explora el proyecto: en GitHub
- Revisa la documentación: Sitio oficial
- Mira el roadmap y los Issues: Milestone v1
Ya sea que lo uses en tu próxima app, abras un issue cuando algo se rompa, o te animes a enviar un PR, bienvenido a bordo. Hagamos que las aplicaciones hechas con Svelte tengan la interfaz de nivel profesional que se merecen.
PD: Abri mi cuenta en GitHub Sponsors, si esta entre tus posibilidades ayudarme con una donacion, te lo agradecere mucho! Mi GitHub Sponsors Aqui
I build products for the web, experiment with ideas, and document what I learn along the way. This blog is my personal notebook of projects, tools, interfaces, and everything I discover while trying to create better experiences. Some posts come from real problems I’ve faced while building, others come from simple curiosity and the desire to understand how things work.
Have something you want to build? Tell me about your idea — maybe we can create something interesting together.