{"id":31443,"date":"2025-01-20T14:12:38","date_gmt":"2025-01-20T13:12:38","guid":{"rendered":"https:\/\/www.codemotion.com\/magazine\/?p=31443"},"modified":"2025-01-20T14:12:59","modified_gmt":"2025-01-20T13:12:59","slug":"construyendo-componentes-agnosticos-con-cva-y-tailwind","status":"publish","type":"post","link":"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/","title":{"rendered":"Construyendo componentes agn\u00f3sticos con CVA y Tailwind"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\" id=\"h-tailwind-es-genial-pero-presenta-algun-punto-debil\">Tailwind es genial, pero \u00bfpresenta alg\u00fan punto d\u00e9bil?<\/h2>\n\n\n\n<p>El uso de Tailwind presenta numerosas ventajas:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Acelera el desarrollo, manteniendo las capas de contenido y presentaci\u00f3n cerca, evitando la carga cognitiva de otorgar nombre a las clases y evitando casi por completo las colisiones entre estilos.<\/li>\n\n\n\n<li>Refuerza la consistencia, mediante el uso de tokens.<\/li>\n\n\n\n<li>Produce archivos CSS peque\u00f1os, extrayendo solo aquellas clases que se usan.<\/li>\n<\/ul>\n\n\n\n<p>Sin embargo, presenta una clara desventaja en comparaci\u00f3n a un enfoque CSS tradicional, como es la repetici\u00f3n de c\u00f3digo. Para reducirla, <a href=\"https:\/\/tailwindcss.com\/docs\/utility-first#maintainability-concerns\">la propia documentaci\u00f3n de Tailwind nos sugiere el uso de componentes cuando sea posible<\/a>.<\/p>\n\n\n\n<p>Otra de las desventajas es que perdemos la sem\u00e1ntica. Cuando aplicamos la clase <code>btn--lg <\/code> a un elemento podemos intuir que le estamos aplicando la variante &#8220;lg&#8221;, cuando aplicamos las clases <code>px-6 py-4 text-lg<\/code> perdemos este contexto.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-como-solucionar-este-problema-con-react\">\u00bfComo solucionar este problema con React?<\/h2>\n\n\n\n<p>Una forma de solucionar este problema a nivel de librer\u00eda de UI (<strong>React<\/strong> en este caso) podr\u00eda ser crear un componente <code>Button<\/code> que recibiese las variantes como <code>props<\/code> y que mapease estas props a clases de <strong>Tailwind<\/strong>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> type { ReactNode } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'react'<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> sizeStyles = {\n  <span class=\"hljs-attr\">sm<\/span>: <span class=\"hljs-string\">'px-3 py-1 text-sm'<\/span>,\n  <span class=\"hljs-attr\">md<\/span>: <span class=\"hljs-string\">'px-4 py-2 text-base'<\/span>,\n  <span class=\"hljs-attr\">lg<\/span>: <span class=\"hljs-string\">'px-6 py-4 text-lg'<\/span>,\n};\n\n<span class=\"hljs-keyword\">const<\/span> intentStyles = {\n  <span class=\"hljs-attr\">primary<\/span>: <span class=\"hljs-string\">'bg-blue-600 text-blue-50'<\/span>,\n  <span class=\"hljs-attr\">secondary<\/span>: <span class=\"hljs-string\">'bg-orange-600 text-orange-50'<\/span>,\n  <span class=\"hljs-attr\">default<\/span>: <span class=\"hljs-string\">'text-slate-600 text-slate-50'<\/span>,\n};\n\ninterface Props {\n  <span class=\"hljs-attr\">size<\/span>: keyof <span class=\"hljs-keyword\">typeof<\/span> sizeStyles;\n  intent: keyof <span class=\"hljs-keyword\">typeof<\/span> intentStyles;\n  children: ReactNode;\n}\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> Button = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ size, intent, children }<\/span>) =&gt;<\/span> (\n  <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">inline-block<\/span> <span class=\"hljs-attr\">border-0<\/span> ${<span class=\"hljs-attr\">sizeStyles<\/span>&#91;<span class=\"hljs-attr\">size<\/span>]} ${<span class=\"hljs-attr\">intentStyles<\/span>&#91;<span class=\"hljs-attr\">intent<\/span>]}`}&gt;<\/span>\n    {children}\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span><\/span>\n)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Esta soluci\u00f3n:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Nos ahorra el tener que escribir todas las clases individuales de tailwind cada vez que queramos pintar un bot\u00f3n.<\/li>\n\n\n\n<li>No necesitamos recordar que <code>primary<\/code> equivale a <code>bg-blue-600 text-blue-50<\/code><\/li>\n\n\n\n<li>Mantiene los estilos centralizados, si queremos actualizarlos no tenemos que estar navegando por todos los ficheros.<\/li>\n<\/ul>\n\n\n\n<p>Ahora bien, \u00bfpodemos considerar el trabajo terminado? Ni mucho menos:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Nuestro componente no permite pasar un evento <code>onClick<\/code>, de hecho, nos gustar\u00eda poder pasar cualquier prop que pueda recibir un elemento <code>button<\/code> (y tener el soporte de TypeScript para esas props)<\/li>\n\n\n\n<li>Dentro de estas props nos gustar\u00eda poder enviar clases adicionales mediante la prop <code>className<\/code>, pero como el componente ya usa esa prop internamente queremos concatenar las clases externas e internas antes de asignarlas<\/li>\n\n\n\n<li>Al ser un componente com\u00fan, como un bot\u00f3n, probablemente nos interese poder trabajar de forma imperativa con \u00e9l, por ejemplo para poner el foco en el elemento (o quitarlo de \u00e9l). Por lo tanto tenemos que propagar la referencia con <code>forwardRef<\/code>.<\/li>\n\n\n\n<li>Podr\u00eda ser que nos interesase pintar otro elemento distinto a button, pero con la misma apariencia (componente polim\u00f3rfico)<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-por-que-es-necesario-solucionar-este-problema-usando-react\">\u00bfPor qu\u00e9 es necesario solucionar este problema usando React?<\/h2>\n\n\n\n<p>Pensemos un momento: \u00bfCu\u00e1l es la cantidad m\u00ednima de c\u00f3digo que cumple con los requisitos? Dicho de otra manera, \u00bfde cu\u00e1nto c\u00f3digo me puedo desprender evitando ciclos de procesamiento, previniendo posibles errores, etc?<\/p>\n\n\n\n<p>La realidad es que solo necesitar\u00eda un c\u00f3digo similar a este para seguir manteniendo los estilos del bot\u00f3n centralizados, para permitirme llamar a esas clases de una forma sem\u00e1ntica y para evitarme el c\u00f3digo duplicado:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> sizeStyles = {\n  <span class=\"hljs-attr\">sm<\/span>: <span class=\"hljs-string\">'px-3 py-1 text-sm'<\/span>,\n  <span class=\"hljs-attr\">md<\/span>: <span class=\"hljs-string\">'px-4 py-2 text-base'<\/span>,\n  <span class=\"hljs-attr\">lg<\/span>: <span class=\"hljs-string\">'px-6 py-4 text-lg'<\/span>,\n};\n\n<span class=\"hljs-keyword\">const<\/span> intentStyles = {\n  <span class=\"hljs-attr\">primary<\/span>: <span class=\"hljs-string\">'bg-blue-600 text-blue-50'<\/span>,\n  <span class=\"hljs-attr\">secondary<\/span>: <span class=\"hljs-string\">'bg-orange-600 text-orange-50'<\/span>,\n  <span class=\"hljs-attr\">default<\/span>: <span class=\"hljs-string\">'text-slate-600 text-slate-50'<\/span>,\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> button = ({\n  size,\n  intent,\n}: {\n  <span class=\"hljs-attr\">size<\/span>: keyof <span class=\"hljs-keyword\">typeof<\/span> sizeStyles;\n  intent: keyof <span class=\"hljs-keyword\">typeof<\/span> intentStyles;\n}) =&gt; <span class=\"hljs-string\">`inline-block border-0 <span class=\"hljs-subst\">${sizeStyles&#91;size]}<\/span> <span class=\"hljs-subst\">${intentStyles&#91;intent]}<\/span>`<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>La forma de pintar un bot\u00f3n desde cualquiera de mis componentes ahora pasar\u00eda a ser algo tan simple como <code>className={button({ intent: 'primary', size: 'lg' })}<\/code> y no tendr\u00eda que preocuparme del <em>prop-drilling<\/em> o de problemas similares.<\/p>\n\n\n\n<p>Adem\u00e1s, puesto que ahora solo estoy usando una funci\u00f3n de JavaScript, \u00bfqu\u00e9 me impedir\u00eda usar la misma soluci\u00f3n para mis componentes de <strong>Vue<\/strong>, de <strong>Svelte<\/strong> o de cualquier otra librer\u00eda o framework?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-acelerando-el-desarrollo-con-cva\">Acelerando el desarrollo con CVA<\/h2>\n\n\n\n<p><a href=\"https:\/\/cva.style\/docs\">CVA (o class-variance-authority)<\/a> es una librer\u00eda de JavaScript que nos permite abstraer el trabajo de construir componentes CSS basados en variantes. Veamos c\u00f3mo podr\u00edamos resolver el ejemplo anterior:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> { cva } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'class-variance-authority'<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> button = cva(<span class=\"hljs-string\">'inline-block border-0'<\/span>, {\n  <span class=\"hljs-attr\">variants<\/span>: {\n    <span class=\"hljs-attr\">size<\/span>: {\n      <span class=\"hljs-attr\">sm<\/span>: <span class=\"hljs-string\">'px-3 py-1 text-sm'<\/span>,\n      <span class=\"hljs-attr\">md<\/span>: <span class=\"hljs-string\">'px-4 py-2 text-base'<\/span>,\n      <span class=\"hljs-attr\">lg<\/span>: <span class=\"hljs-string\">'px-6 py-4 text-lg'<\/span>,\n    },\n    <span class=\"hljs-attr\">intent<\/span>: {\n      <span class=\"hljs-attr\">primary<\/span>: <span class=\"hljs-string\">'bg-blue-600 text-blue-50'<\/span>,\n      <span class=\"hljs-attr\">secondary<\/span>: <span class=\"hljs-string\">'bg-orange-600 text-orange-50'<\/span>,\n      <span class=\"hljs-attr\">default<\/span>: <span class=\"hljs-string\">'text-slate-600 text-slate-50'<\/span>,\n    },\n  },\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>La forma de invocar a estos estilos seguir\u00eda siendo:  <code>className={button({ intent: 'primary', size: 'lg' })}<\/code><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-extendiendo-las-clases-de-nuestro-componente\">Extendiendo las clases de nuestro componente<\/h3>\n\n\n\n<p>Si quisi\u00e9ramos aplicar clases adicionales a nuestro componente, tan solo tendr\u00edamos que hacer a trav\u00e9s de la propiedad <code>class<\/code> (tambi\u00e9n soporta <code>className<\/code> para facilitar la propagaci\u00f3n de clases en React):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">className={button({\n  <span class=\"hljs-attr\">intent<\/span>: <span class=\"hljs-string\">'primary'<\/span>,\n  <span class=\"hljs-attr\">size<\/span>: <span class=\"hljs-string\">'lg'<\/span>,\n  <span class=\"hljs-attr\">class<\/span>: <span class=\"hljs-string\">'text-blue-600'<\/span>\n})}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-extrayendo-los-tipos-de-nuestro-componente\">Extrayendo los tipos de nuestro componente<\/h3>\n\n\n\n<p>En alg\u00fan momento nos puede interesar extraer los tipos correspondientes a las variantes de nuestro componente. Para ello podemos recurrir a la <em>utility-type<\/em> <code>VariantProps<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> type { VariantProps } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'class-variance-authority'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { cva } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'class-variance-authority'<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> button = cva(<span class=\"hljs-comment\">\/* ... *\/<\/span>);\n\n<span class=\"hljs-keyword\">export<\/span> type ButtonProps = VariantProps&lt;<span class=\"hljs-keyword\">typeof<\/span> button&gt;;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Es importante rese\u00f1ar que por defecto ninguna de las variantes est\u00e1 marcada como requerida. Si quisi\u00e9ramos hacerlo a nivel de tipos tendr\u00edamos que crear una <a href=\"https:\/\/cva.style\/docs\/getting-started\/typescript#required-variants\">utility-type<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-valores-por-defecto-para-las-variantes-defaultvariants\">Valores por defecto para las variantes (defaultVariants)<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> button = cva(<span class=\"hljs-string\">'inline-block border-0'<\/span>, {\n  <span class=\"hljs-attr\">variants<\/span>: {\n    <span class=\"hljs-attr\">size<\/span>: {\n      <span class=\"hljs-attr\">sm<\/span>: <span class=\"hljs-string\">'px-3 py-1 text-sm'<\/span>,\n      <span class=\"hljs-attr\">md<\/span>: <span class=\"hljs-string\">'px-4 py-2 text-base'<\/span>,\n      <span class=\"hljs-attr\">lg<\/span>: <span class=\"hljs-string\">'px-6 py-4 text-lg'<\/span>,\n    },\n    <span class=\"hljs-attr\">intent<\/span>: {\n      <span class=\"hljs-attr\">primary<\/span>: <span class=\"hljs-string\">'bg-blue-600 text-blue-50'<\/span>,\n      <span class=\"hljs-attr\">secondary<\/span>: <span class=\"hljs-string\">'bg-orange-600 text-orange-50'<\/span>,\n      <span class=\"hljs-attr\">default<\/span>: <span class=\"hljs-string\">'text-slate-600 text-slate-50'<\/span>,\n    },\n  },\n  <span class=\"hljs-attr\">defaultVariants<\/span>: {\n    <span class=\"hljs-attr\">size<\/span>: <span class=\"hljs-string\">'md'<\/span>,\n    <span class=\"hljs-attr\">intent<\/span>: <span class=\"hljs-string\">'default'<\/span>,\n  },\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\" id=\"h-variantes-compuestas-compoundvariants\">Variantes compuestas (compoundVariants)<\/h3>\n\n\n\n<p>Imaginemos que evolucionamos nuestro componente <code>button<\/code> y definimos una nueva variante con un valor <em>boolean<\/em> llamada <code>outline<\/code>.<\/p>\n\n\n\n<p>Cuando la variante <code>outline<\/code> tenga el valor <code>true<\/code>, queremos que el borde y el texto tengan el acento de color. Cuando tenga el valor <code>false<\/code>, queremos que el acento lo tenga el color de fondo.<\/p>\n\n\n\n<p>Este acento de color vendr\u00e1 dictado por la variante <code>intent<\/code>.<\/p>\n\n\n\n<p>Para conservar las dimensiones de nuestro bot\u00f3n, queremos pintar siempre un borde (cuyo grosor vendr\u00e1 dictado por la variante <code>size<\/code>). En el caso de que la variante <code>outline<\/code> sea <code>false<\/code>,  este ser\u00e1 transparente, independientemente de la variante <code>intent<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> button = cva(<span class=\"hljs-string\">'inline-block border-0'<\/span>, {\n  <span class=\"hljs-attr\">variants<\/span>: {\n    <span class=\"hljs-attr\">size<\/span>: {\n      <span class=\"hljs-attr\">sm<\/span>: <span class=\"hljs-string\">'px-3 py-1 text-sm border'<\/span>,\n      <span class=\"hljs-attr\">md<\/span>: <span class=\"hljs-string\">'px-4 py-2 text-base border-2'<\/span>,\n      <span class=\"hljs-attr\">lg<\/span>: <span class=\"hljs-string\">'px-6 py-4 text-lg border-4'<\/span>,\n    },\n    <span class=\"hljs-attr\">outline<\/span>: {\n      <span class=\"hljs-attr\">false<\/span>: <span class=\"hljs-string\">'border-transparent'<\/span>,\n      <span class=\"hljs-attr\">true<\/span>: <span class=\"hljs-literal\">null<\/span>,\n    },\n    <span class=\"hljs-attr\">intent<\/span>: {\n      <span class=\"hljs-attr\">primary<\/span>: <span class=\"hljs-literal\">null<\/span>,\n      <span class=\"hljs-attr\">secondary<\/span>: <span class=\"hljs-literal\">null<\/span>,\n      <span class=\"hljs-attr\">default<\/span>: <span class=\"hljs-literal\">null<\/span>,\n    },\n  },\n  <span class=\"hljs-attr\">compoundVariants<\/span>: &#91;\n    {\n      <span class=\"hljs-attr\">intent<\/span>: <span class=\"hljs-string\">'primary'<\/span>,\n      <span class=\"hljs-attr\">outline<\/span>: <span class=\"hljs-literal\">false<\/span>,\n      <span class=\"hljs-attr\">class<\/span>: <span class=\"hljs-string\">'bg-blue-600 text-blue-50'<\/span>,\n    },\n    {\n      <span class=\"hljs-attr\">intent<\/span>: <span class=\"hljs-string\">'primary'<\/span>,\n      <span class=\"hljs-attr\">outline<\/span>: <span class=\"hljs-literal\">true<\/span>,\n      <span class=\"hljs-attr\">class<\/span>: <span class=\"hljs-string\">'bg-blue-50 text-blue-600 border-blue-600'<\/span>,\n    },\n    <span class=\"hljs-comment\">\/* ... *\/<\/span>\n  ],\n  <span class=\"hljs-attr\">defaultVariants<\/span>: {\n    <span class=\"hljs-attr\">size<\/span>: <span class=\"hljs-string\">'md'<\/span>,\n    <span class=\"hljs-attr\">intent<\/span>: <span class=\"hljs-string\">'default'<\/span>,\n    <span class=\"hljs-attr\">outline<\/span>: <span class=\"hljs-literal\">false<\/span>,\n  },\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-solventando-colisiones-de-clases-con-tailwind-merge\">Solventando colisiones de clases con Tailwind Merge<\/h2>\n\n\n\n<p>Por mucho cuidado que pongamos, es inevitable que en alg\u00fan momento nos enfrentemos al problema de las colisiones de clases. &#8220;\u00bfQu\u00e9 pasa si el componente declara internamente la clase <code>p-4 <\/code>y quiero sobreescribirla desde fuera?&#8221;<\/p>\n\n\n\n<p>Podr\u00edamos vernos tentados a aplicarle un important, por ejemplo <code>!p-2<\/code> pero esto no har\u00eda m\u00e1s que esconder el problema, porque si necesito extender a su vez este otro componente ya no me quedar\u00edan m\u00e1s mecanismos de escape.<\/p>\n\n\n\n<p>La librer\u00eda <a href=\"https:\/\/github.com\/dcastil\/tailwind-merge\">tailwind-merge<\/a> podr\u00eda ayudarnos en estos casos, ya que aplica la prevalencia sobre las \u00faltimas clases recibidas como argumento:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">twMerge(<span class=\"hljs-string\">'p-3'<\/span>, <span class=\"hljs-string\">'p-4'<\/span>); <span class=\"hljs-comment\">\/\/ p-4<\/span>\ntwMerge(<span class=\"hljs-string\">'block'<\/span>, <span class=\"hljs-string\">'flex'<\/span>); <span class=\"hljs-comment\">\/\/ flex<\/span>\ntwMerge(<span class=\"hljs-string\">'p-3 hover:p-4'<\/span>, <span class=\"hljs-string\">'p-2'<\/span>); <span class=\"hljs-comment\">\/\/ p-2 hover:p-4<\/span>\ntwMerge(<span class=\"hljs-string\">'p-4'<\/span>, <span class=\"hljs-string\">'p-&#91;25px]'<\/span>); <span class=\"hljs-comment\">\/\/ p-&#91;25px]<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" id=\"h-conclusion\">Conclusi\u00f3n<\/h2>\n\n\n\n<p>Como ya hemos visto, construir un componente en cualquier librer\u00eda de UI que cumpla con todos los requisitos funcionales no es una tarea liviana y en el caso de componentes puramente presentacionales no ofrece ninguna ventaja en comparaci\u00f3n al esfuerzo requerido.<\/p>\n\n\n\n<p>En casos como este, cva puede ser una alternativa interesante y eficiente, presentando adem\u00e1s la ventaja de ser compatible con cualquier framework JavaScript.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Tailwind es genial, pero \u00bfpresenta alg\u00fan punto d\u00e9bil? El uso de Tailwind presenta numerosas ventajas: Sin embargo, presenta una clara desventaja en comparaci\u00f3n a un enfoque CSS tradicional, como es la repetici\u00f3n de c\u00f3digo. Para reducirla, la propia documentaci\u00f3n de Tailwind nos sugiere el uso de componentes cuando sea posible. Otra de las desventajas es&#8230; <a class=\"more-link\" href=\"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/\">Read more<\/a><\/p>\n","protected":false},"author":254,"featured_media":31541,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","_uag_custom_page_level_css":"","_genesis_hide_title":false,"_genesis_hide_breadcrumbs":false,"_genesis_hide_singular_image":false,"_genesis_hide_footer_widgets":false,"_genesis_custom_body_class":"","_genesis_custom_post_class":"","_genesis_layout":"","footnotes":""},"categories":[10634],"tags":[10682],"collections":[],"class_list":{"0":"post-31443","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-diseno-ux","8":"tag-consejos-de-carrera","9":"entry"},"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.9 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Construyendo componentes agn\u00f3sticos con CVA y Tailwind - Codemotion Magazine<\/title>\n<meta name=\"description\" content=\"Descubre con Jos\u00e9 Manuel Lucas c\u00f3mo crear componentes reutilizables y eficientes con CVA y Tailwind CSS. \u00a1Optimiza tus proyectos ahora!\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Construyendo componentes agn\u00f3sticos con CVA y Tailwind\" \/>\n<meta property=\"og:description\" content=\"Descubre con Jos\u00e9 Manuel Lucas c\u00f3mo crear componentes reutilizables y eficientes con CVA y Tailwind CSS. \u00a1Optimiza tus proyectos ahora!\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/\" \/>\n<meta property=\"og:site_name\" content=\"Codemotion Magazine\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/Codemotion.Italy\/\" \/>\n<meta property=\"article:published_time\" content=\"2025-01-20T13:12:38+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-01-20T13:12:59+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers-.webp\" \/>\n\t<meta property=\"og:image:width\" content=\"1024\" \/>\n\t<meta property=\"og:image:height\" content=\"1024\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/webp\" \/>\n<meta name=\"author\" content=\"jmlweb\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@CodemotionIT\" \/>\n<meta name=\"twitter:site\" content=\"@CodemotionIT\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"jmlweb\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/\"},\"author\":{\"name\":\"jmlweb\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#\/schema\/person\/b38417c0c79c66c436a78f8b207c514a\"},\"headline\":\"Construyendo componentes agn\u00f3sticos con CVA y Tailwind\",\"datePublished\":\"2025-01-20T13:12:38+00:00\",\"dateModified\":\"2025-01-20T13:12:59+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/\"},\"wordCount\":947,\"publisher\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers-.webp\",\"keywords\":[\"Consejos de carrera\"],\"articleSection\":[\"Dise\u00f1o\/UX\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/\",\"url\":\"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/\",\"name\":\"Construyendo componentes agn\u00f3sticos con CVA y Tailwind - Codemotion Magazine\",\"isPartOf\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers-.webp\",\"datePublished\":\"2025-01-20T13:12:38+00:00\",\"dateModified\":\"2025-01-20T13:12:59+00:00\",\"description\":\"Descubre con Jos\u00e9 Manuel Lucas c\u00f3mo crear componentes reutilizables y eficientes con CVA y Tailwind CSS. \u00a1Optimiza tus proyectos ahora!\",\"breadcrumb\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/#primaryimage\",\"url\":\"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers-.webp\",\"contentUrl\":\"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers-.webp\",\"width\":1024,\"height\":1024},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.codemotion.com\/magazine\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Frontend\",\"item\":\"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Dise\u00f1o\/UX\",\"item\":\"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"Construyendo componentes agn\u00f3sticos con CVA y Tailwind\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#website\",\"url\":\"https:\/\/www.codemotion.com\/magazine\/\",\"name\":\"Codemotion Magazine\",\"description\":\"We code the future. Together\",\"publisher\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.codemotion.com\/magazine\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#organization\",\"name\":\"Codemotion\",\"url\":\"https:\/\/www.codemotion.com\/magazine\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2019\/11\/codemotionlogo.png\",\"contentUrl\":\"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2019\/11\/codemotionlogo.png\",\"width\":225,\"height\":225,\"caption\":\"Codemotion\"},\"image\":{\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/Codemotion.Italy\/\",\"https:\/\/x.com\/CodemotionIT\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#\/schema\/person\/b38417c0c79c66c436a78f8b207c514a\",\"name\":\"jmlweb\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.codemotion.com\/magazine\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/e9b818c4bcbc06b196d66cea92d9fe41bd5ccd255dd3943745cd801eff3f96db?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/e9b818c4bcbc06b196d66cea92d9fe41bd5ccd255dd3943745cd801eff3f96db?s=96&d=mm&r=g\",\"caption\":\"jmlweb\"},\"description\":\"Frontend Solutions Architect @ Freepik\",\"sameAs\":[\"https:\/\/jmlweb.es\"],\"url\":\"https:\/\/www.codemotion.com\/magazine\/author\/jmlweb\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Construyendo componentes agn\u00f3sticos con CVA y Tailwind - Codemotion Magazine","description":"Descubre con Jos\u00e9 Manuel Lucas c\u00f3mo crear componentes reutilizables y eficientes con CVA y Tailwind CSS. \u00a1Optimiza tus proyectos ahora!","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/","og_locale":"en_US","og_type":"article","og_title":"Construyendo componentes agn\u00f3sticos con CVA y Tailwind","og_description":"Descubre con Jos\u00e9 Manuel Lucas c\u00f3mo crear componentes reutilizables y eficientes con CVA y Tailwind CSS. \u00a1Optimiza tus proyectos ahora!","og_url":"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/","og_site_name":"Codemotion Magazine","article_publisher":"https:\/\/www.facebook.com\/Codemotion.Italy\/","article_published_time":"2025-01-20T13:12:38+00:00","article_modified_time":"2025-01-20T13:12:59+00:00","og_image":[{"width":1024,"height":1024,"url":"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers-.webp","type":"image\/webp"}],"author":"jmlweb","twitter_card":"summary_large_image","twitter_creator":"@CodemotionIT","twitter_site":"@CodemotionIT","twitter_misc":{"Written by":"jmlweb","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/#article","isPartOf":{"@id":"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/"},"author":{"name":"jmlweb","@id":"https:\/\/www.codemotion.com\/magazine\/#\/schema\/person\/b38417c0c79c66c436a78f8b207c514a"},"headline":"Construyendo componentes agn\u00f3sticos con CVA y Tailwind","datePublished":"2025-01-20T13:12:38+00:00","dateModified":"2025-01-20T13:12:59+00:00","mainEntityOfPage":{"@id":"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/"},"wordCount":947,"publisher":{"@id":"https:\/\/www.codemotion.com\/magazine\/#organization"},"image":{"@id":"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/#primaryimage"},"thumbnailUrl":"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers-.webp","keywords":["Consejos de carrera"],"articleSection":["Dise\u00f1o\/UX"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/","url":"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/","name":"Construyendo componentes agn\u00f3sticos con CVA y Tailwind - Codemotion Magazine","isPartOf":{"@id":"https:\/\/www.codemotion.com\/magazine\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/#primaryimage"},"image":{"@id":"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/#primaryimage"},"thumbnailUrl":"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers-.webp","datePublished":"2025-01-20T13:12:38+00:00","dateModified":"2025-01-20T13:12:59+00:00","description":"Descubre con Jos\u00e9 Manuel Lucas c\u00f3mo crear componentes reutilizables y eficientes con CVA y Tailwind CSS. \u00a1Optimiza tus proyectos ahora!","breadcrumb":{"@id":"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/#primaryimage","url":"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers-.webp","contentUrl":"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers-.webp","width":1024,"height":1024},{"@type":"BreadcrumbList","@id":"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/construyendo-componentes-agnosticos-con-cva-y-tailwind\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.codemotion.com\/magazine\/"},{"@type":"ListItem","position":2,"name":"Frontend","item":"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/"},{"@type":"ListItem","position":3,"name":"Dise\u00f1o\/UX","item":"https:\/\/www.codemotion.com\/magazine\/es\/frontend-es\/diseno-ux\/"},{"@type":"ListItem","position":4,"name":"Construyendo componentes agn\u00f3sticos con CVA y Tailwind"}]},{"@type":"WebSite","@id":"https:\/\/www.codemotion.com\/magazine\/#website","url":"https:\/\/www.codemotion.com\/magazine\/","name":"Codemotion Magazine","description":"We code the future. Together","publisher":{"@id":"https:\/\/www.codemotion.com\/magazine\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.codemotion.com\/magazine\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.codemotion.com\/magazine\/#organization","name":"Codemotion","url":"https:\/\/www.codemotion.com\/magazine\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.codemotion.com\/magazine\/#\/schema\/logo\/image\/","url":"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2019\/11\/codemotionlogo.png","contentUrl":"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2019\/11\/codemotionlogo.png","width":225,"height":225,"caption":"Codemotion"},"image":{"@id":"https:\/\/www.codemotion.com\/magazine\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/Codemotion.Italy\/","https:\/\/x.com\/CodemotionIT"]},{"@type":"Person","@id":"https:\/\/www.codemotion.com\/magazine\/#\/schema\/person\/b38417c0c79c66c436a78f8b207c514a","name":"jmlweb","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.codemotion.com\/magazine\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/e9b818c4bcbc06b196d66cea92d9fe41bd5ccd255dd3943745cd801eff3f96db?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e9b818c4bcbc06b196d66cea92d9fe41bd5ccd255dd3943745cd801eff3f96db?s=96&d=mm&r=g","caption":"jmlweb"},"description":"Frontend Solutions Architect @ Freepik","sameAs":["https:\/\/jmlweb.es"],"url":"https:\/\/www.codemotion.com\/magazine\/author\/jmlweb\/"}]}},"featured_image_src":"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers--600x400.webp","featured_image_src_square":"https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers--600x600.webp","author_info":{"display_name":"jmlweb","author_link":"https:\/\/www.codemotion.com\/magazine\/author\/jmlweb\/"},"uagb_featured_image_src":{"full":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers-.webp",1024,1024,false],"thumbnail":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers--150x150.webp",150,150,true],"medium":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers--300x300.webp",300,300,true],"medium_large":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers--768x768.webp",768,768,true],"large":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers-.webp",1024,1024,false],"1536x1536":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers-.webp",1024,1024,false],"2048x2048":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers-.webp",1024,1024,false],"small-home-featured":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers--100x100.webp",100,100,true],"sidebar-featured":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers--180x128.webp",180,128,true],"genesis-singular-images":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers--896x504.webp",896,504,true],"archive-featured":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers--400x225.webp",400,225,true],"gb-block-post-grid-landscape":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers--600x400.webp",600,400,true],"gb-block-post-grid-square":["https:\/\/www.codemotion.com\/magazine\/wp-content\/uploads\/2025\/01\/DALL\u00b7E-2025-01-20-14.11.55-A-vibrant-and-dynamic-illustration-featuring-developers-collaborating-in-a-futuristic-tech-workspace.-The-scene-includes-people-working-on-computers--600x600.webp",600,600,true]},"uagb_author_info":{"display_name":"jmlweb","author_link":"https:\/\/www.codemotion.com\/magazine\/author\/jmlweb\/"},"uagb_comment_info":0,"uagb_excerpt":"Tailwind es genial, pero \u00bfpresenta alg\u00fan punto d\u00e9bil? El uso de Tailwind presenta numerosas ventajas: Sin embargo, presenta una clara desventaja en comparaci\u00f3n a un enfoque CSS tradicional, como es la repetici\u00f3n de c\u00f3digo. Para reducirla, la propia documentaci\u00f3n de Tailwind nos sugiere el uso de componentes cuando sea posible. Otra de las desventajas es&#8230;&hellip;","lang":"es","_links":{"self":[{"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/posts\/31443","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/users\/254"}],"replies":[{"embeddable":true,"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/comments?post=31443"}],"version-history":[{"count":3,"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/posts\/31443\/revisions"}],"predecessor-version":[{"id":31451,"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/posts\/31443\/revisions\/31451"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/media\/31541"}],"wp:attachment":[{"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/media?parent=31443"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/categories?post=31443"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/tags?post=31443"},{"taxonomy":"collections","embeddable":true,"href":"https:\/\/www.codemotion.com\/magazine\/wp-json\/wp\/v2\/collections?post=31443"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}