Only this pageAll pages
Powered by GitBook
1 of 15

Español

Loading...

Loading...

Api

Loading...

guías

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

@atomico

Soporte

Estrategias de estructuración de repositorio con Atomico al crear sistemas de diseño.

Le invito a conocer 2 estrategias de estructuración de tu repositorio para tu proyecto como sistemas de diseño.

Puede ser confuso abordar a el desarrollo de un sistema de diseño sin saber como estructurar nuestro proyecto, la estructura de nuestro repositorio definirá en gran medida el formato de publicación de su sistema de diseño sea:

  1. Monorepositorio versionado a nivel de componente

  2. Monorepositorio versionado a nivel de sistema de diseño

¿Por qué monorepositorio para su sistemas de diseño?

Es la forma más eficiente de asilar las herramientas que gira entorno a nuestro sistema de diseño.

Recomendaciones para su monorepositorio

1. Aislé su Storybook como package.

Esto mantendrá limpio el workspaces, ejemplo:

packages
├─ components
└─ storybook 

2. Adjunte las historias para Storybook en a nivel de componente.

Storybook puede consumir las historias fuera de su repositorio

packages
├─ components
│  └─ my-button
│     └─ src
│        └─ define.stories.tsx
└─ storybook 

Monorepositorio versionado a nivel de componente

Trata de un repositorio que posee un sistema de diseño versionados por componentes

Es la estrategia más habitual al momento de crear sistemas de diseño, ya que permite individualizar los cambios a nivel de componente:

Ventajas

Gracias a que cada componente posee su package.json se lograra:

  • Versionamiento: Cada package estará versionado, ejemplo @ds/button@1.0.0, esto permite que podamos agregar mejoras o corregir problemas apuntando solo a la versión del componente afectado.

  • Mantenimiento: Al ser un package, podemos individualizar sus dependencias y pruebas, lo que facilita el mantenimiento.

  • Abstracción: Trata de que cada componente aislara en su package los slots de composición(Subcomponentes que únicamente dependen del componente principal, ejemplo el tag option depende del tag select) y alguna otra utilidad especifica del componente.

Desventajas

Las siguientes desventajas son prácticas:

  1. Mayor lentitud al crear nuevos componentes, ya que cada componente requerirá ser un nuevo package.

  2. Mayor dificultad de centralización de componentes en un solo package, ya que cada cambio requerirá actualizar el package principal y adjuntar la nueva exportación.

Las desventajas prácticas pueden ser reparadas mediante herramientas de CLI que automatizan la generación de archivos y centralización de packages

Ejemplo de estructura

packages
├─ components
│  ├─ my-button
│  │  └─ package.json
│  ├─ my-input
│  │  └─ package.json
│  └─ my-card
│     └─ package.json
└─ storybook
   └─ package.json

Ejemplo de importación por componente

import { Button } from "@ds/my-button";

Ejemplo de importación por sistema de componentes

import { Button } from "@ds/components";

Atomico

Una microlibrería inspirada en React Hooks, diseñada y optimizada para la creación de webcomponentes.

import { c } from "atomico"; // 2.5kB

function component({ name }) {
  return <host shadowDom>Hello, {name}</host>;
}

component.props = {
  name: String,
};

customElements.define("my-component", c(component));
import { Props, c } from "atomico"; // 2.5kB

function component({ name }:Props<typeof component.props>) {
  return <host shadowDom>Hello, {name}</host>;
}

component.props = {
  name: String,
};

customElements.define("my-component", c(component));
import { c, html } from "atomico"; // 3.0kB


function component({ name }) {
  return html`<host shadowDom>Hello, ${name}</host>`;
}

component.props = {
  name: String,
};

customElements.define("my-component", c(component));
import { c, html } from "https://unpkg.com/atomico"; // 4.0kB


function component({ name }) {
  return html`<host shadowDom>Hello, ${name}</host>`;
}

component.props = {
  name: String,
};

customElements.define("my-component", c(component));

Atomico simplifica el aprendizaje, flujo de trabajo y el mantenimiento al crear webcomponents y lo logra con :

  1. Interfaces escalable y reutilizable: con Atomico el código es más simple y podrás aplicar practicas que faciliten la reutilización de tu código.

  2. Comunicación abierta: su webcomponent podrá comunicar su estado sea por eventos, propiedades o métodos.

  3. Agnóstico: su webcomponent servirá en cualquier librería compatible con la web, ejemplo React, Vue o Svelte.

Api

Performance: Atomico posee un performance comparativo a niveles de Svelte, ganando la tercera posición en performance según en una comparativa de 55 librerías entre las cuales esta React, Vue, Stencil y Lit.

webcomponents.dev
🧬Props(Propiedades)
✨Sistemas de diseño con Atomico

Props(Propiedades)

Las props en Atomico son la forma de asociar al webcomponent propiedades y atributos reactivos que detonan la lógica o interfaz del webcomponent.

Sintaxis

Toda función que represente el webcomponent podrá asociar el objeto estático props para la declaración de propiedades y atributos reactivos, ejemplo:

import { c } from "atomico";

function component() {
  return <host />;
}

component.props = {
  // Declaracion simple
  value1: String,
  // Declaracion estructurada
  value2: {
    type: String,
    reflect: true,
    attr: "advaceprop",
    value: "default string",
    event: {
      type: "UpdateAdvanceProp",
      bubbles: true,
    },
  },
};

customElement.define("web-component", c(component));

Considere que:

  1. los nombre de prop en formato Camel Case será traducido a para su uso como atributo al formato Kebab Case, este comportamiento puede ser modificado mediante la propiedad "attr" al usar una declaración estructurada.

  2. Las declaraciones estructuradas requieren mínimamente la propiedad "type".

  3. No todos los tipos pueden usar la propiedades "reflect".

  4. La forma de declarar la propiedad "value" puede variar según el tipo.

Declaraciones simple

Las declaraciones simples permiten asociar solo la validación de tipo.

component.props = {
  propString: String,
  propNumber: Number,
  propObject: Object,
  propArray: Array,
  propBool: Boolean,
  propCallback: Function,
};

Declaraciones estructuradas

Mejora la definición añadiendo declaraciones utilitarias, permitiendo por ejemplo reflejar el valor de la propiedad como atributos, emitir automáticamente eventos o asociar valores por defecto.** Recuerde este tipo de declaraciones requieren mínimamente el uso de la propiedad type.**

Prop.type

// valid declaration
component.props = { myName: String };
// valid declaration
component.props = { myName: { type: String } };
Tipo
Soporta la propiedad "reflect"

String

✔️

Number

✔️

Boolean

✔️

Object

✔️

Array

✔️

Promise

❌

Symbol

❌

Function

❌

Prop.reflect

Si la propiedad "reflect" se define como true se refleja su valor como atributo del webcomponent, esto es útil para la declaración de estados del CSS, ejemplo:

component.props = {
  checked: {
    type: Boolean,
    reflect: true,
  },
};

Prop.event

Permite despachar un evento automatico ante el cambio de valor del prop, ejemplo:

component.props = {
  value: {
    type: String,
    event: {
      type: "change",
      bubbles: true,
      composed: true,
      detail: "any value",
      cancelable: true,
    },
  },
};
// listener
nodeComponent.addEventListener("change", handler);

Donde :

  • event.type : String - opcional, nombre del evento a emitir ante el cambio de la prop

  • event.bubbles : Boolean - opcional, indica que el evento puede ser escuchado por los contenedores.

  • event.detail : Any - opcional, permite adjuntar informacion custom del evento

  • event.cancelable : Boolean - opcional, indica que el evento puede ser cancelado por algun oyente

  • event.composed : Boolean - opcional, permite que el evento sobrepase el limite del shadow-root

Prop.attr

Permite modificar el atributo que por defecto genera atomico, ejemplo:

component.props = {
    value: {
        type: String,
        attr: "my-value"
    }
}

la declaración anterior define que el atributo "my-value" refleja su estado en la prop "value".

Prop.value

Atomico permite la definición de valores de inicio por defectos de las props.

WebComponents.props = {
  valueNormal: {
    type: Number,
    value: 100,
  },
  valueObject: {
    type: Object,
    value: () => ({}),
  },
};

La asociación de callback como value permiten generar valores únicos para cada instancia del webcomponent, esto es útil con los tipos Object y Array ya que elimina las referencias entre instancias.

Reactividad en el scope del webcomponent

Atomico elimina el uso de "this" dado su enfoque funcional, pero añade el hook useProp el que permite referenciar una prop para su uso con una sintaxis es similar a useState, ejemplo:

function component() {
  const [message, setMessage] = useProp("message");
  return (
    <host>
      Hello, {message}
      <input oninput={({ target }) => setMessage(target.value)} />
    </host>
  );
}

component.props = { message: String };

Storybook

Atomico hoy ofrece un set de utilidades para mejorar la creacion de sistemas de diseño usando Storybook.

  1. @atomico/vite

  2. @atomico/postcss-tokens

Las propiedades especiales del evento son las conocidas EventInit , puede conocer mas detalle en la

documentación adjunta
@atomico/storybook

Comenzando con Webcomponents

Esta guía conocerás lo esencial para comenzar a desarrollar webcomponents con Atomico

Gracias por estar aquí e iniciarte con Atomico. Hablemos un poco de lo que hoy ofrece atomico:

  1. Agilidad de desarrollo, el enfoque funcional de Atomico simplifica el código en todas las etapas de desarrollo.

  2. Ligero por dentro y por fuera, Atomico te permite crear un componente con menos código y con un bajo impacto de dependencias 3kb Aproximadamente.

Entendamos el como luce un webcomponent creado con Atomico:

// IMPORTACIÓN
import { c, html, css } from "atomico";

// WEBCOMPONENT
function component({ message }) {
  return html`<host shadowDom>${message}</host>`;
}

// PROPIEDADES Y ATRIBUTOS DEL WEBCOMPONENTE
component.props = {
  message: String,
};

// APARIENCIA DEL WEBCOMPONENTE
component.styles = css`
  :host {
    font-size: 30px;
  }
`;

// DEFINICION DEL WEBCOMPONENT COMO ETIQUETA
customElements.define("my-component", c(component));

Analicemos el código por partes...

Importación

import { c, html, css } from "atomico";

¿Qué hemos importado?

  1. c: Función que transforma el componente funcional en un customElement estándar.

  2. html: Función para declarar la plantilla de nuestro componente, también puedes usar JSX.

  3. css: Función que permite crear el CSSStyleSheet(CSS) para nuestro componente siempre y cuando este declare el shadowDom.

Webcomponent

function component({ message }) {
  return html`<host shadowDom>${message}</host>`;
}

Nuestra función component recibe todas las props(Propiedades y Atributos) declaradas en component.props, la función component declara toda la lógica y plantilla del webcomponent. Una regla importante dentro de Atomico es "todo componente creado con Atomico debe siempre retornar el tag <host>".

Propiedades y atributos reactivos del webcomponent

Atomico detecta las prop(Propiedades y Atributos) del componente gracias a la asociación del objeto props, esto mediante el uso de índices y valores te permite definir:

  1. índice: Nombre de la propiedad y atributo.

  2. Valor: tipo de la prop.

component.props = {
  message: String,
};

Del ejemplo podemos inferir que Atomico creará en nuestro webcomponente una propiedad y atributo llamada mensaje y esta solo puede recibir valores del tipo String.

Apariencia del webcomponent.

Atomico detecta los estilos estáticos de tu componente gracias a la asociación de la propiedad styles:

component.styles = css`
  :host {
    font-size: 30px;
  }
`;

styles acepta valores CSSStyleSheet(CSS) de forma individual o en lista, el retorno de la función css es un CSSStyleSheet estándar, por lo que puede ser compartido fuera de Atomico.

Definición de tu webcomponent

customElements.define("my-component", c(component));

Para crear nuestro customElement estándar deberemos entregar nuestro componente funcional a la función c del módulo de Atomico, la función c generará como retorno un customElement que puede ser definido o extendido.

Ejemplo

Monorepositorio versionado a nivel de sistema de diseño

Trata de un repositorio que posee un componentes versionados a nivel de sistema de diseño.

Trata de un repositorio que posee un componentes versionados a nivel de sistema de diseño esto quiere decir que cualquier cambio a nivel de componente se publicara como una nueva versión del sistema de diseño.

Ventajas

  1. Mayor agilidad al crear nuevos componentes, ya que no deberemos crear un nuevo package para un nuevo componente.

  2. Facil centralización de todo el sistema de diseño, bastara con un fichero components para definir que se exporta a nivel de sistema de diseño.

Desventajas

  1. No podremos individualizar las dependencias por componente, ya que estas estarán atadas al package de todo el sistema de diseño.

Ejemplo de estructura:

Ejemplo de importación por componente

Ejemplo de importación por sistema de componentes

Sistemas de diseño con Atomico

Es grato saber que hoy te enfrentas al desafío de implementar un sistema de diseño con Atomico, espero que esta guía te ayude a abordar:

La siguiente guía está en desarrollo

  1. Abstracción de componentes.

  2. Composición. (Slots)

  3. Distribución.

  4. Documentación.

Realmente rapido, Atomico posee un en el browser y una experiencia de desarrollo ágil.

buen performance
packages
├─ components
│  ├─ src 
│  │  ├─ my-button
│  │  ├─ my-input
│  │  ├─ my-card
│  │  └─ components.{ts,js,tsx,jsx}
│  └─ package.json
└─ storybook
   └─ package.json
import { Button } from "@ds/components/button";
import { Button } from "@ds/components";
Estrategias de estructuración de repositorio con Atomico al crear sistemas de diseño.
Storybook
Tokens (CSS custom properties)

@atomico/storybook

Mejora la experiencia de desarrollo y documentación en Storybook de los webcomponents creados con Atomico

@atomico/storybook posee como objetivo:

  1. Entregar un decorador para renderizar el JSX/HTML y serializar el codigo de las historias.

  2. Entregar un generador de argTypes/args segun el componente dado.

Decorador para renderizar el JSX/HTML

import { decorador } from "@atomico/storybook";

decorator permite renderizar el JSX/HTML usando el render de Atomico, este también permite serializar el JSX y HTML declarado en su historia, ejemplo:

Ejemplo de renderización y serialización del JSX

La serialización del JSX ocurre solo si se defineparameters.docs.source = 'jsx' por historia o global en .storybook/preview.js.

Ejemplo de renderización y serialización del HTML

Generador de argTypes/args según el componente dado

import { define } from "@atomico/storybook";
import { Button } from "./button";

export default {
    title: "Component/Button"
    ...define(Button) 
}

Ejemplo

Esta funcionalidad solo es compatible con versiones >= atomico@1.70.0 gracias a la

característica de nombres automáticos según definición del componente

Slots

El uso de slots mejora la composición permitiéndote reflejar el contenido expuesto en el lightDOM del componente en el shadowDOM de este, ejemplo:

::slotted(<selector>)

El selector slotted permite la manipulación del contenido expuesto como slot del lightDOM desde el shadowDOM, ejemplo:

Auto slot

Con Atomico puedes definir un slot por defecto para tus componentes, esto es útil si buscas mantener cierta consistencia de composición sin la necesidad de declarar el uso de slot, ejemplo:

<my-component>
    <my-component-header></my-component-header>
    <my-component-footer></my-component-footer>
</my-component>

Para definir un slot por defecto solo debes declarar en las props la prop slot, ejemplo:

myComponentHeader.props = {
    slot: { type:String, value: "header"}
}

myComponentFooter.props = {
    slot: { type:String, value: "footer"}
}

Considere esto practico solo si la composición esta apalancada al contenedor, esto no evita que ud modifique la propiedad slot desde la instancia del componente

Slot condicional.

import { useRef } from "atomico";
import { useSlot } from "@atomico/hooks/use-slot";

function component(){
    const ref = useRef();
    const childNodes = useSlot(ref);
    return <host shadowDom>
        <header style={{
            display: childNodes.length? "block": "none"
        }}>
            <slot name="header" ref={ref}/>
        </header>
    </host>
}

Atomico ofrece un hook dentro del package ** **que anexa el evento a una referencia, este le permitirá ocultar slots si estos no declaran contenido, ejemplo:

@atomico/hooks/use-slot
slotchange

Tokens (CSS custom properties)

Los tokens son valores configurables de nuestro sistema de diseño representados usando css custom properties, en esta guía encontraras recomendaciones de uso para que tu proyecto sea más escale y configurable sin complicaciones

CSS custom properties

Contexto a nivel de :root

El seudo selector :root nos permite definir tokens que podrán ser accedidos a nivel de todo el documento incluso shadowDOM, para aprovechar esta ventaja lo ideal es mantener el siguiente patrón.

un documento theme sea CSS o JS que defina todos los tokens como custom property a nivel de :root, ejemplo:

:root{
    --my-design-system--color-primary: red;
}

Te invito a notar que para este caso hemos asociado el como prefijo de nombre la cadena--my-design-system, la idea de esto es minimizar conflicto y definir un namespace que identifique las css custom properties que nos pertenecen a nivel de sistema de diseño.

Contexto a nivel de :host

El seudo selector :host nos permite encapsular estilos que apuntan a la instancia de nuestro webcomponent, gracias a :host podemos facilitar el acceso a las css custom properties provenientes de :root, esto es práctico ya que facilita el mantenimiento y es automatizable, ejemplo:

:host{
    --color-primary: var(--my-design-system--color-primary);
}

LogoWebComponents.devWebComponents.dev