¡Hola! Estamos muy cerca de lanzar el soporte para React + Vue para Tailwind UI, así que pensé que sería interesante compartir algunos de los esfuerzos detrás de escena que se han realizado para hacerlo posible.
Agarra unas palomitas...
La historia de fondo
Desde el día en que comenzamos a trabajar en Tailwind UI en algún momento a mediados de 2019, supe que, en última instancia, sería 10 veces más valioso para la gente si pudieran obtener ejemplos totalmente interactivos construidos con su framework JS favorito. Intentar que eso sucediera para el primer lanzamiento era demasiado ambicioso, así que tuvimos que averiguar cómo llegar allí paso a paso.
Decidimos centrarnos primero en HTML vanilla porque es totalmente universal, e incluso si algo como JSX sería más útil para algunas personas, ya existen muchas herramientas para convertir HTML a JSX en las que la gente podría apoyarse.
También hicimos la difícil concesión de no proporcionar ningún JS para interacciones como alternar un menú responsive o abrir y cerrar un diálogo modal en la primera versión. Sentí que cualquier cosa que proporcionáramos haría más daño que bien, porque no hay un solo framework JS que constituya la mayoría de la base de usuarios de Tailwind. Si atendiéramos a los desarrolladores de React, estaríamos dificultando su uso para el 70% de las personas que no usan React. Si atendiéramos a los desarrolladores de Vue, estaríamos dificultándolo para el 70% de las personas que no usan Vue. Si intentáramos escribirlo en JS vanilla personalizado, bueno, lo estaríamos dificultando literalmente para todos (¿en serio tienes idea de cuánto código se necesita para construir un sistema robusto de transición de entrada/salida desde cero en JS?)
Así que, en cambio, simplemente documenté los diferentes estados usando comentarios en el HTML, y dejé que el usuario final lo conectara con su framework JS favorito. Sé que a mucha gente le encanta eso de Bulma, y creo que también fue un gran enfoque para empezar.
Pero una vez que sentimos que Tailwind UI estaba bastante completo con cientos de excelentes ejemplos, decidimos que era hora de abordar el problema de JS y ver qué podíamos hacer.
¿Qué debería ser siquiera?
Como concepto abstracto, agregar "soporte de JavaScript" a Tailwind UI suena sencillo, pero cuando profundizas en los detalles, no lo es. Hay tantas decisiones que tomar sobre qué construir, y tantas concesiones que debes considerar al tratar de hacer algo útil para la mayor cantidad de personas posible.
Le di vueltas a todo el concepto en mi cabeza durante un año completo mientras trabajaba en Tailwind UI antes de tener un plan con el que estuviera contento. En última instancia, estos son los valores centrales que decidí al diseñar una solución:
- La promesa de Tailwind UI es que es solo un fragmento de código — es fácil de personalizar y adaptar editando directamente el código. Cualquier ejemplo de JS que proporcionemos debe respetar esta idea fundamental.
- El JS necesita ser actualizable. A diferencia del markup, que esperamos que la gente simplemente posea y edite a su antojo, el JS necesita provenir de
node_modulesde alguna manera, porque construir estas cosas bien es difícil, habrá errores, y queremos poder solucionarlos para la gente sin pedirles que copien un nuevo fragmento de código. Además de eso, no queremos que la gente tenga que transportar cuidadosamente 200 líneas de JS que no escribieron por su código base, y preocuparse constantemente por romper accidentalmente algún pequeño detalle de implementación por error. - Simplemente tiene que ser mejor que HTML vanilla. Al final del día, lo más importante es que mejoremos la experiencia existente para las personas que usan los frameworks JS para los que decidimos agregar soporte primero. Cada vez que me encontraba frustrado por dos concesiones contrapuestas que dificultaban hacer algo perfecto, preguntarme "¿sigue siendo esto estrictamente mejor y de ninguna manera peor para los usuarios del framework X que HTML vanilla?" proporcionó mucha claridad.
La otra cosa que fue realmente importante para mí es que nada del JS subyacente fuera propietario o específico de Tailwind UI. Para mí, Tailwind UI no es un kit de interfaz de usuario como Ant Design o Material UI — esos son grandes proyectos, pero no es lo que quería construir.
Para mí, Tailwind UI es una colección de planos, que te muestran cómo construir cosas increíbles usando herramientas que ya están disponibles para ti. Si quieres usar las cosas exactamente como vienen de fábrica, puedes hacerlo totalmente y obtendrás excelentes resultados. Pero también deberías poder usar Tailwind UI como un punto de partida útil, modificarlo hasta el extremo y terminar con algo que se sienta únicamente tuyo, incluso si te dimos un impulso al principio.
Así que antes de que pudiéramos agregar soporte de JavaScript a Tailwind UI, necesitábamos construir algunas herramientas.
Construyendo Headless UI
Hace años recuerdo haber visto la biblioteca downshift de Kent C. Dodds y pensar "hombre, este es un concepto genial — todo el comportamiento complejo está escondido en la biblioteca, pero todo el markup y el estilo reales se dejan al usuario".
Este tipo de enfoque encaja perfectamente con Tailwind filosóficamente, porque todo el objetivo de Tailwind es ayudarte a construir diseños totalmente personalizados más rápidamente. Tailwind + una biblioteca de componentes JS que abstraigan toda la lógica de navegación por teclado y accesibilidad sin incluir ninguna opinión de diseño sería una combinación muy poderosa — permitiría a los equipos que construyen interfaces de usuario totalmente personalizadas moverse casi tan rápido como los equipos que se contentan con usar frameworks opinativos y difíciles de personalizar.
Buscamos si había otras herramientas que resolvieran estos mismos problemas, y aunque había algunos proyectos increíbles en el espacio (Reach UI y Reakit especialmente en ese momento, y react-aria desde que comenzamos nuestra propia biblioteca, trabajo fenomenal de toda esa gente), finalmente decidimos que algo tan importante para nuestra empresa sería mejor construirlo y controlarlo nosotros mismos.
Hubo dos grandes razones por las que terminamos iniciando nuestro propio proyecto:
- Queríamos que las APIs funcionaran bien con una solución de estilo basada en clases como Tailwind. Muchas de las otras herramientas esperaban que escribieras CSS personalizado para apuntar a las diferentes partes de cada componente, lo cual es muy diferente del flujo de trabajo que usas para estilizar cosas con Tailwind. Queríamos diseñar algo que fuera muy amigable con las clases.
- Queríamos soportar múltiples frameworks usando una API consistente. Hay bibliotecas de React, bibliotecas de Vue, bibliotecas de Angular y otras, pero cada una es diferente, diseñada por diferentes personas con diferentes gustos. Queríamos algo que fuera lo más consistente posible de un framework a otro, para que los ejemplos específicos del framework en Tailwind UI no fueran radicalmente diferentes entre sí.
Estaba realmente emocionado por lo que íbamos a terminar al final, pero maldita sea, esto iba a ser mucho trabajo.
Poniendo la bola en marcha
Decidimos llamar a este proyecto "Headless UI" y en agosto del año pasado Robin Malfait se unió al equipo para trabajar en él a tiempo completo, casi exclusivamente.
Lo primero en lo que trabajó fue en un componente Transition para React que te permitiría agregar animaciones de entrada/salida a los elementos, completamente usando clases, y estaba muy inspirado en el componente <transition> en Vue:
<Transition show={isOpen} enter="transition-opacity duration-75" enterFrom="opacity-0" enterTo="opacity-100" leave="transition-opacity duration-150" leaveFrom="opacity-100" leaveTo="opacity-0"> Apareceré y desapareceré gradualmente</Transition>Este es un gran ejemplo de lo que quise decir antes cuando dije que realmente queríamos diseñar componentes que fueran "amigables con las clases". Este componente hace que sea realmente fácil estilizar tus transiciones de entrada/salida con clases de utilidad de Tailwind regulares, por lo que se siente como estilizar cualquier otra cosa en tu aplicación. Sin embargo, tampoco está acoplado a Tailwind de ninguna manera, ¡y puedes usar las clases que quieras!
Publicamos el primer lanzamiento público en octubre, e incluía bibliotecas de React y Vue con los primeros tres componentes:
- Botón de menú (o dropdown)
- Listbox (o select personalizado)
- Switch (o toggle)
Aterrizamos en un conjunto de APIs que usaban "componentes compuestos" para abstraer toda la complejidad mientras se comunicaban entre sí a través de context (o provide/inject en Vue).
Así es como se ve un dropdown personalizado en React:
import { Menu } from "@headlessui/react";function MyDropdown() { return ( <Menu as="div" className="relative"> <Menu.Button className="rounded bg-blue-600 px-4 py-2 text-white ...">Opciones</Menu.Button> <Menu.Items className="absolute right-0 mt-1"> <Menu.Item> {({ active }) => ( <a className={`${active && "bg-blue-500 text-white"} ...`} href="/account-settings"> Configuración de la cuenta </a> )} </Menu.Item> <Menu.Item> {({ active }) => ( <a className={`${active && "bg-blue-500 text-white"} ...`} href="/documentation"> Documentación </a> )} </Menu.Item> <Menu.Item disabled> <span className="opacity-75 ...">Invitar a un amigo (¡próximamente!)</span> </Menu.Item> </Menu.Items> </Menu> );}Notarás que para hacer cosas como estilizar el elemento de dropdown "activo", usamos una render prop (o un scoped slot en Vue):
<Menu.Item> {({ active }) => ( <a className={`${active && "bg-blue-500 text-white"} ...`} href="/documentation"> Documentación </a> )}</Menu.Item>Las render props no son tan comunes como solían ser porque los hooks han reemplazado la necesidad de ellas en muchas situaciones. Pero para este tipo de problema donde necesitas acceso al estado interno que es administrado por el componente que estás consumiendo, siguen siendo la solución correcta (¿la única?) y muy elegante.
Diseñando los componentes correctos
Después de lanzar la primera versión de Headless UI en octubre, nos pusimos manos a la obra durante un par de meses para lanzar Tailwind CSS v2.0, y luego pasamos el último mes del año enfocados en la corrección de errores y mucho mantenimiento del proyecto antes de tomar un descanso para las vacaciones.
Cuando volvimos, nos pusimos manos a la obra para trabajar en la adición de soporte para React + Vue a Tailwind UI, y lo primero que necesitábamos era auditar todo el comportamiento interactivo que necesitábamos para los ejemplos en Tailwind UI y averiguar qué abstracciones de Headless UI necesitábamos diseñar.
Este fue en realidad un trabajo bastante interesante y desafiante, porque realmente no siempre es obvio cómo una interacción específica del diseño debe mapearse a un patrón de interfaz de usuario establecido que tiene expectativas de accesibilidad conocidas.
Algunos son obvios:
- Un diálogo modal debe ser un dialog
- Un toggle debe ser un switch
- Un dropdown debe ser un menu (bueno, a veces...)
Pero algunos son mucho más complicados. Por ejemplo, ¿qué pasa con los menús móviles, el tipo de cosa que abres con un botón de hamburguesa?
Si se abre como un popup, ¿es eso un menú como un dropdown?
¿Qué pasa si se desliza desde el costado de la pantalla?
¿Qué pasa si simplemente se abre en su lugar y empuja el resto de la página hacia abajo?
Trabajamos en preguntas como esta regularmente, y llegar a buenas soluciones requirió mucha investigación y experimentación. Tenemos la suerte de tener a David Luhr en el equipo, que se ha especializado en accesibilidad durante mucho tiempo, y con su ayuda pudimos sentirnos realmente bien con las soluciones a las que llegamos.
Esto es lo que decidimos que necesitábamos para soportar los patrones que ya existían en Tailwind UI:
- Botón de Menú. Se utiliza para menús desplegables que solo contienen enlaces o botones, como un pequeño menú de acciones al final de una fila de tabla.
- Listbox. Para implementaciones personalizadas de
selectdonde deseas incluir cosas adicionales en los elementosoption. Por ejemplo, un selector de país donde pones una bandera al lado de cada país. - Switch. Para interruptores de palanca personalizados que se comportan como casillas de verificación.
- Disclosure. Para mostrar/ocultar contenido en su lugar. Piensa en preguntas frecuentes plegables. También útil para fragmentos más grandes de la interfaz de usuario, como un menú móvil que se abre en su lugar y empuja el resto de la página hacia abajo.
- Dialog. Para, bueno, ¡diálogos modales! Pero también para la navegación móvil que se desliza desde el costado de la página y otras interfaces de usuario de estilo "toma el control", incluso si no se ven como un panel tradicional centrado en la pantalla modal.
- Popover. Para paneles que aparecen encima de la página cuando haces clic en un botón. Esto es útil para menús donde necesitas mucho contenido personalizado que violaría la rigidez de los botones de menú
role="menu"regulares. Los usamos para algunos menús móviles, menús desplegables en barras de navegación y otros lugares interesantes también. Es como un híbrido de menú/disclosure. - Radio Group. Para interfaces de usuario de selección de radio personalizadas, como donde deseas un conjunto de tarjetas en las que se puede hacer clic en lugar de un aburrido círculo de radio.
Nos encontramos con toneladas de desafíos al construir estas cosas, especialmente en torno a cosas complejas como la gestión del foco, y especialmente en torno a la gestión del foco anidado.
Imagina que tienes un modal que se abre, y dentro de ese modal hay un dropdown. Abres el modal, luego abres el dropdown y presionas escape. ¿Qué pasa? Bueno, el dropdown debería cerrarse, ¿verdad?, pero el modal debería permanecer abierto.
Te garantizo que el 99% de los modales en Internet también se cerrarían en este caso, aunque no deberían. Pero el nuestro no — ¡el nuestro funciona!
Nosotros (bueno, principalmente Robin) pasamos meses trabajando en pequeños detalles como este para hacer que todo sea lo más a prueba de balas posible, y aunque estoy seguro de que debe haber errores escondidos todavía en alguna parte, donde terminamos se siente tan sólido como una roca en comparación con casi cualquier interfaz de usuario que encuentres día a día en la web.
Todavía tenemos muchos patrones nuevos que queremos agregar a Headless UI como pestañas, acordeones, tal vez incluso trago saliva un selector de fechas, y estamos ansiosos por explorar otros frameworks en el futuro (Alpine.js es el siguiente en nuestra lista), pero estamos súper orgullosos de llamar a lo que lanzamos esta semana Headless UI v1.0 y comprometernos con una API estable en el futuro.
Creemos que te va a encantar. </TimCook>
Solo la abstracción suficiente
Con las cosas de Headless UI resueltas, el siguiente gran problema fue averiguar exactamente cómo debería verse una versión React o Vue de un ejemplo existente de Tailwind UI.
Los ejemplos en Tailwind UI son fragmentos de HTML puro — encuentras algo que te gusta, copias el HTML en tu proyecto, luego lo modificas tanto como quieras, lo divides en componentes individuales, lo que quieras. No hacemos ninguna suposición sobre cómo lo vas a usar, qué elementos vas a conservar o eliminar, o cómo quieres abstraer cualquier duplicación con tus herramientas preferidas.
Esta es una decisión fácil cuando se trabaja con HTML puro — ¿qué otra opción tienes realmente? Pero al ofrecer ejemplos específicos del framework, se vuelve mucho más complicado saber exactamente qué proporcionar.
La pregunta más importante era ¿cuánto deberíamos intentar eliminar cualquier duplicación y cuáles son los enfoques correctos para hacerlo?
Tanto React como Vue son frameworks de componentes, y la forma en que reutilizas el código en tus proyectos es extrayendo bits de la interfaz de usuario en componentes que puedes usar una y otra vez.
El desafío es que crear componentes como ese siempre es muy específico del proyecto. Toma este componente de lista como ejemplo:

Totalmente componentizado en una aplicación real, el código final podría verse algo así:
<TeamList> {projectMembers.map(member => ( <TeamList.Item teamMember={member}> ))}</TeamList>Se ve súper limpio, claro, pero te está imponiendo muchas opiniones.
Por ejemplo, asume que los elementos son miembros del equipo. ¿Qué pasa si estás construyendo una aplicación de facturación y quieres usar este patrón para una lista de clientes en su lugar? ¡Demonios, podrías estar usando esto para una aplicación de apuestas deportivas y estos deberían ser equipos de béisbol, ni siquiera personas!
También hace suposiciones sobre la forma de un objeto member. Tendría que codificar que está extrayendo una propiedad name y una email, aunque tus datos podrían ser diferentes.
El otro problema es que en frameworks como Vue, solo puedes tener un componente por archivo. Esto significa que copiar un ejemplo que estaba compuesto por 4-5 subcomponentes significaría que tienes que copiar 4-5 fragmentos diferentes, crear archivos para cada uno y vincularlos todos juntos con los nombres/rutas correctos.
Para mí, algo acerca de hacer todo esto por la gente se sentía como ir demasiado lejos, al menos para el problema que estamos tratando de resolver hoy. Cuando todo está súper dividido así con APIs de props predefinidas y nombres de componentes elegidos deliberadamente, se siente como si ya no se supiera que puedes cambiarlo. Lo que me encanta de Tailwind UI es que hacer clic en la pestaña "código" se siente como abrir una pieza compleja de electrónica y ver todos los circuitos justo ahí frente a ti. Es una oportunidad de aprendizaje, y puedes leer el markup y los nombres de las clases y entender cómo funciona todo junto.
Luché con esto durante mucho tiempo, but ultimately decided that right now we were trying to solve two main problems:
- Darle a la gente código usando la sintaxis que realmente necesitan, como darle a los usuarios de React JSX en lugar de HTML para que no tengan que convertir manualmente cosas como
forahtmlFor. - Hacer que los elementos interactivos funcionen de inmediato, para que los dropdowns, menús móviles, toggles y todo lo demás estuviera listo para usar, en lugar de tener que escribir todo ese JS boilerplate tú mismo.
Decidí que la solución correcta era centrarse en resolver esos problemas y tener cuidado de no hacer nada que convirtiera a Tailwind UI en un producto diferente.
Así que esto es lo que es diferente cuando miras un ejemplo de React o Vue en comparación con la versión HTML vanilla:
- Cada ejemplo de framework usa la sintaxis correcta — los ejemplos de React usan JSX, y los ejemplos de Vue se proporcionan en la sintaxis de componente de archivo único.
- Las transiciones son reales ahora — en lugar de comentarios que te dicen qué clases agregar en cada fase de una transición, la transición simplemente está ahí, usando un componente de transición de Headless UI o el componente de transición nativo de Vue.
- Los elementos interactivos son manejados por Headless UI — verás algunas importaciones en cualquier ejemplo que requiera JS donde traemos los componentes de Headless UI requeridos y luego esos se usan directamente en el markup.
- Cualquier fragmento repetido de markup se ha convertido en bucles básicos — cualquier cosa de bucle impulsada por datos (como listas de personas o elementos de navegación) se extrae en variables simples justo ahí en el ejemplo para reducir la duplicación pero aún mantener todo junto en un solo lugar. En tus propios proyectos, reemplazarías esto con datos de una API o base de datos o lo que sea, pero mantenemos los ejemplos simples y no hacemos ninguna suposición por ti.
- Los iconos se extraen de la biblioteca Heroicons. En lugar de incrustar el SVG directamente cada vez que se usa un icono, los traemos de nuestras bibliotecas de iconos de React/Vue para mantener el markup más simple.
Aquí hay un ejemplo de cómo se ve realmente:
import { Menu, Transition } from "@headlessui/react";import { DotsVerticalIcon } from "@heroicons/react/solid";import { Fragment } from "react";const people = [ { name: "Calvin Hawkins", email: "[email protected]", image: "https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80", }, { name: "Kristen Ramos", email: "[email protected]", image: "https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80", }, { name: "Ted Fox", email: "[email protected]", image: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80", },];export default function Example() { return ( <ul className="divide-y divide-gray-200"> {people.map((person) => ( <li key={person.email} className="flex py-4"> <img className="h-10 w-10 rounded-full" src={person.image.src} alt="" /> <div className="ml-3"> <p className="text-sm font-medium text-gray-900">{person.name}</p> <p className="text-sm text-gray-500">{person.email}</p> </div> <Menu as="div" className="relative ml-3 inline-block text-left"> {({ open }) => ( <> <div> <Menu.Button className="flex items-center rounded-full bg-gray-100 text-gray-400 hover:text-gray-600 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:ring-offset-gray-100 focus:outline-none"> <span className="sr-only">Abrir opciones</span> <DotsVerticalIcon className="h-5 w-5" aria-hidden="true" /> </Menu.Button> </div> <Transition show={open} as={Fragment} enter="transition ease-out duration-100" enterFrom="transform opacity-0 scale-95" enterTo="transform opacity-100 scale-100" leave="transition ease-in duration-75" leaveFrom="transform opacity-100 scale-100" leaveTo="transform opacity-0 scale-95" > <Menu.Items static className="ring-opacity-5 absolute right-0 mt-2 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black focus:outline-none" > <div className="py-1"> <Menu.Item> {({ active }) => ( <a href="#" className={classNames( active ? "bg-gray-100 text-gray-900" : "text-gray-700", "block px-4 py-2 text-sm", )} > Ver detalles </a> )} </Menu.Item> <Menu.Item> {({ active }) => ( <a href="#" className={classNames( active ? "bg-gray-100 text-gray-900" : "text-gray-700", "block px-4 py-2 text-sm", )} > Enviar mensaje </a> )} </Menu.Item> </div> </Menu.Items> </Transition> </> )} </Menu> </li> ))} </ul> );}Sigue siendo un solo ejemplo donde puedes ver todo lo que está sucediendo a la vez, y puedes cortarlo como tenga más sentido para tu proyecto. Puedes definir tus propias APIs de props para satisfacer tus propias necesidades, nombrar las cosas como tenga más sentido para tu dominio y obtener tus datos de la manera que funcione mejor con las otras tecnologías con las que trabajas.
La máquina que lo hace funcionar
Así que así es como funciona todo desde la perspectiva del cliente, pero si tienes curiosidad sobre cómo construimos realmente estas cosas internamente, es bastante interesante y vale la pena hablar de ello.
Tailwind UI tiene como 450 ejemplos o algo así ahora, y convertir todas esas cosas a React/Vue a mano habría sido una tortura absoluta, e imposible de mantener a largo plazo. Así que necesitábamos alguna forma de automatizarlo.
Si eres como yo, toda la idea de generar automáticamente estas cosas en diferentes formatos podría hacerte estremecer. Para mí, al menos, mi reacción instintiva es simplemente "bueno, ahí va el toque humano — simplemente se sentirá como basura generada por una máquina ahora", y por supuesto eso no es aceptable para mí en absoluto — quiero estar orgulloso de las cosas que lanzamos, no sentir que tuvimos que hacer compromisos realmente feos.
Así que, como sea que hiciéramos esto, el resultado tenía que estar a la altura de nuestros estándares. Esto significaba que íbamos a tener que construir un sistema para hacer esto nosotros mismos, desde cero.
Durante los primeros 2 meses del año, Brad pasó todo su tiempo construyendo una cadena de autoría personalizada específicamente para los componentes de Tailwind UI que pudiera tomar nuestro HTML y convertirlo en código React que pareciera escrito a mano por una persona.
Así es como funciona — en lugar de crear nuestros ejemplos en HTML vanilla, los creamos en una especie de sabor personalizado de HTML lleno de elementos personalizados que finalmente transformamos a HTML vanilla usando PostHTML.
Así es como se ve uno de nuestros ejemplos de dropdown en nuestro formato de autoría interno:
<x-menu as="div" id="options-menu" class="relative inline-block text-left"> <div> <x-menu-button class="inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:ring-offset-gray-100 focus:outline-none" > Opciones <x-heroicon type="solid" name="chevron-down" class="-mr-1 ml-2 h-5 w-5" /> </x-menu-button> </div> <x-transition as="x-fragment" enter="transition ease-out duration-100" enter-start="transform opacity-0 scale-95" enter-end="transform opacity-100 scale-100" leave="transition ease-in duration-75" leave-start="transform opacity-100 scale-100" leave-end="transform opacity-0 scale-95" > <x-menu-items class="ring-opacity-5 absolute right-0 mt-2 w-56 origin-top-right rounded-md bg-white shadow-lg ring-1 ring-black focus:outline-none" > <div class="py-1"> <x-menu-item> <a href="#" class="block px-4 py-2 text-sm" x-active-class="bg-gray-100 text-gray-900" x-not-active-class="text-gray-700" > Configuración de la cuenta </a> </x-menu-item> <x-menu-item> <a href="#" class="block px-4 py-2 text-sm" x-active-class="bg-gray-100 text-gray-900" x-not-active-class="text-gray-700" > Soporte </a> </x-menu-item> <x-menu-item> <a href="#" class="block px-4 py-2 text-sm" x-active-class="bg-gray-100 text-gray-900" x-not-active-class="text-gray-700" > Licencia </a> </x-menu-item> </div> </x-menu-items> </x-transition></x-menu>Probablemente ya puedas ver por qué crear cosas de esta manera hace que sea mucho más fácil convertirlas a algo como React o Vue que simplemente escribir el HTML a mano.
Rastreamos este documento como un AST, y en realidad lo transformamos en cuatro formatos:
- El HTML vanilla que obtienes cuando copias el fragmento.
- El HTML que se inyecta en el panel de vista previa, donde usamos algo de Alpine.js muy rápido y sucio para demostrar las diferentes interacciones en el ejemplo.
- El fragmento de React para que lo copies.
- El fragmento de Vue para que lo copies.
La clave para obtener un resultado sensato es realmente tener un control total del formato de entrada. Sigue siendo un trabajo duro, pero cuando puedes codificar la intención de cada ejemplo en un formato de entrada personalizado, convertir eso a otro formato resulta mucho mejor que intentar escribir algo que pueda convertir jQuery arbitrario a React o algo así.
Todavía hay algo de magia oscura ahí dentro con expresiones regulares y todos los demás sospechosos habituales, pero en última instancia, al mantener las cosas lo más declarativas posible y ocultar la complejidad real dentro de Headless UI, estamos principalmente transformando markup, que está mucho más restringido que el código regular.
¿Cuándo sale?
El soporte de React y Vue para Tailwind UI estará disponible para todos el miércoles 14 de abril — ¡dentro de dos días! Es una actualización completamente gratuita para todos los clientes, simplemente verás aparecer un nuevo pequeño dropdown en la interfaz de usuario para cambiar el idioma del fragmento y estarás listo para empezar.
También lanzaremos Headless UI v1.0 el mismo día (por supuesto, ya que de qué otra manera funcionaría esto de Tailwind UI) junto con un nuevo sitio de documentación, así que incluso si no eres cliente de Tailwind UI, habrá muchas novedades gratuitas de código abierto para que juegues.
Gracias como siempre a todos los que apoyan nuestro trabajo en estas cosas — es en serio un regalo poder trabajar en herramientas como esta para otros desarrolladores todos los días y nos llena de satisfacción ver a la gente beneficiarse de lo que construimos.
¡Espero que disfrutes de las cosas!
– Adam