[{"data":1,"prerenderedAt":256},["ShallowReactive",2],{"blog-post-es-ES-building-eighteen-apps-with-one-design-system":3},{"post":4,"translated":247},{"id":5,"title":6,"author":7,"body":8,"date":241,"description":242,"draft":243,"extension":244,"meta":245,"minutes":246,"navigation":247,"path":248,"seo":249,"stem":250,"tags":251,"__hash__":255},"blog_es\u002Fblog\u002Fbuilding-eighteen-apps-with-one-design-system.md","Construir dieciocho aplicaciones con un solo sistema de diseño","Erik Bjerke",{"type":9,"value":10,"toc":235},"minimark",[11,20,27,32,43,49,53,56,59,102,119,123,134,155,218,221,225,228,231],[12,13,14,15,19],"p",{},"Un ecosistema de dieciocho aplicaciones hecho por un equipo muy pequeño solo\nfunciona si casi nada de la interfaz se escribe dos veces. ",[16,17,18],"code",{},"Numori-UI"," es cómo lo\nconseguimos: una biblioteca de componentes en Tailwind 4 y Vue 3 con la que se\nmonta cada interfaz de Numori, incluida esta web.",[12,21,22,23,26],{},"No empezó siendo una biblioteca. Empezó como ",[16,24,25],{},"components\u002Fui\u002F"," dentro de Numori\nNotes, y ahí se quedó un año antes de que la sacáramos.",[28,29,31],"h2",{"id":30},"extraerla-consistió-sobre-todo-en-borrar-cosas","Extraerla consistió sobre todo en borrar cosas",[12,33,34,35,38,39,42],{},"La aplicación de notas tenía veintiocho componentes en esa carpeta.\nAproximadamente un tercio tenía alguna propiedad que existía para exactamente un\npunto de uso. Un ",[16,36,37],{},"Modal"," con un booleano\n",[16,40,41],{},"hideCloseButtonOnMobileWhenScrolled"," no es un sistema de diseño, es un registro\nde cambios.",[12,44,45,46,48],{},"La regla con la que nos quedamos: una propiedad se gana su sitio si dos pantallas\nsin relación la necesitan, o si quitarla obliga a quien la consume a reimplementar\nel componente. Todo lo demás pasó a ser un slot. ",[16,47,37],{}," bajó de once propiedades\na seis más un slot, y los puntos de uso se acortaron en lugar de alargarse.",[28,50,52],{"id":51},"la-accesibilidad-es-la-razón-para-extraerla-no-un-extra","La accesibilidad es la razón para extraerla, no un extra",[12,54,55],{},"Este fue el resultado realmente útil y no era el que buscábamos.",[12,57,58],{},"Cuando el manejo del teclado vive en cada pantalla, se hace bien en las pantallas\nen las que alguien estaba pensando en ello y se olvida en todas las demás. Nuestra\naplicación de notas tenía un modal que atrapaba el foco y un diálogo de ajustes que\nno, porque se escribieron con cuatro meses de diferencia.",[12,60,61,62,65,66,69,70,73,74,77,78,81,82,85,86,89,90,93,94,97,98,101],{},"Mover ese comportamiento al componente lo vuelve estructural. ",[16,63,64],{},"Toggle"," renderiza un\n",[16,67,68],{},"\u003Cbutton role=\"switch\">"," de verdad con ",[16,71,72],{},"aria-checked",", así que la activación por\nteclado viene gratis de la plataforma en lugar de venir de un manejador\n",[16,75,76],{},"@keydown.space"," que alguien recordó poner. ",[16,79,80],{},"Button"," acepta ",[16,83,84],{},"ariaLabel",",\n",[16,87,88],{},"ariaChecked",", ",[16,91,92],{},"role"," y ",[16,95,96],{},"tabindex"," como propiedades de primera clase, porque la\nalternativa (bajar a un ",[16,99,100],{},"\u003Cbutton>"," en crudo cuando necesitas un atributo ARIA) es\nexactamente cómo se acaba esquivando un sistema de diseño.",[12,103,104,105,107,108,93,111,114,115,118],{},"La parte honesta: no está terminado. ",[16,106,37],{}," todavía no atrapa el foco ni define\n",[16,109,110],{},"role=\"dialog\"",[16,112,113],{},"aria-modal",", y no se cierra con Escape. Son carencias reales,\nestán anotadas, y esta web deliberadamente no usa ",[16,116,117],{},"UiModal"," para nada importante\nhasta que se resuelvan.",[28,120,122],{"id":121},"tree-shaking-porque-una-web-de-marketing-no-debería-cargar-una-biblioteca-de-formularios","Tree-shaking, porque una web de marketing no debería cargar una biblioteca de formularios",[12,124,125,126,129,130,133],{},"La biblioteca no publica CSS precompilado ni un fichero barril que arrastre con\ntodo. Importa ",[16,127,128],{},"UiKbd"," y te llevas 0,4 kB. Importa ",[16,131,132],{},"UiButton"," y te llevas 16 kB, de\nlos cuales unos 7 kB son la tabla de rutas de iconos.",[12,135,136,137,140,141,89,144,147,148,150,151,154],{},"Ese último número es la razón por la que esta web delega los iconos en\n",[16,138,139],{},"@nuxt\u002Ficon",". La biblioteca incluye los treinta y seis glifos de MDI que necesitan\nsus propios componentes; una web necesita ",[16,142,143],{},"mdi:github",[16,145,146],{},"mdi:robot-off-outline"," y\ntreinta más que ningún componente usa. Así que el módulo de Nuxt de ",[16,149,18],{},"\nacepta una opción ",[16,152,153],{},"icons.component"," y entrega el renderizado de iconos a lo que la\naplicación anfitriona ya tenga:",[156,157,162],"pre",{"className":158,"code":159,"language":160,"meta":161,"style":161},"language-js shiki shiki-themes github-light github-dark","numoriUi: {\n  prefix: 'Ui',\n  icons: { component: 'Icon' },\n}\n","js","",[16,163,164,177,192,212],{"__ignoreMap":161},[165,166,169,173],"span",{"class":167,"line":168},"line",1,[165,170,172],{"class":171},"sScJk","numoriUi",[165,174,176],{"class":175},"sVt8B",": {\n",[165,178,180,183,186,190],{"class":167,"line":179},2,[165,181,182],{"class":171},"  prefix",[165,184,185],{"class":175},": ",[165,187,189],{"class":188},"sZZnC","'Ui'",[165,191,85],{"class":175},[165,193,195,198,201,204,206,209],{"class":167,"line":194},3,[165,196,197],{"class":171},"  icons",[165,199,200],{"class":175},": { ",[165,202,203],{"class":171},"component",[165,205,185],{"class":175},[165,207,208],{"class":188},"'Icon'",[165,210,211],{"class":175}," },\n",[165,213,215],{"class":167,"line":214},4,[165,216,217],{"class":175},"}\n",[12,219,220],{},"Los sistemas de diseño suelen hacer esto al revés e insisten en ser los dueños de\nla capa de iconos.",[28,222,224],{"id":223},"qué-costó","Qué costó",[12,226,227],{},"Dos semanas y una clase de error genuinamente molesta. Como los componentes se\nconsumen como ficheros Vue en crudo y no como un paquete precompilado, hay que\ndecirle a Vite que no los preempaquete ni los externalice. Ahora son tres líneas en\nun plugin de Vite, y primero fueron una tarde de desconcierto.",[12,229,230],{},"Mereció la pena. La siguiente aplicación arranca con una interfaz funcional,\naccesible y traducida desde el primer día, y una corrección de accesibilidad llega\na dieciocho aplicaciones a la vez en lugar de solo a aquella sobre la que alguien\nabrió una incidencia.",[232,233,234],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":161,"searchDepth":194,"depth":194,"links":236},[237,238,239,240],{"id":30,"depth":179,"text":31},{"id":51,"depth":179,"text":52},{"id":121,"depth":179,"text":122},{"id":223,"depth":179,"text":224},"2026-07-21","Numori UI empezó como una carpeta de componentes dentro de la aplicación de notas. Extraerla nos enseñó más sobre accesibilidad que sobre APIs de componentes.",false,"md",{},7,true,"\u002Fblog\u002Fbuilding-eighteen-apps-with-one-design-system",{"title":6,"description":242},"blog\u002Fbuilding-eighteen-apps-with-one-design-system",[252,253,254],"ingeniería","sistema-de-diseño","accesibilidad","xoVS2YyB62PVAhbdHc1X-hv6C9vqqXSi6QfdOqWTKQg",1788170792449]