Controlar colores con JavaScript y TinyColor

En este artículo veremos que crear gamas cromáticas con programación es posible, aprende como controlar colores con JavaScript y TinyColor para tu aplicación web.

https://bgrins.github.io/TinyColor/ “TinyColor is a small, fast library for color manipulation and conversion in JavaScript."

See the Pen Controlar colores con JavaScript y TinyColor by Danivalldo (@Danivalldo) on CodePen.

¿Quieres aprender a hacer este ejercicio? Salta directament al tutorial

Cualquiera que haya visto a un buen diseñador UI trabajar, eligiendo y mezclando colores para definir una paleta cromática, estará de acuerdo conmigo, en que parece más un superpoder que una habilidad.

La teoría del color encierra una enorme complejidad. Algo tan aparentemente trivial como combinar colores, en realidad tiene implicaciones matemáticas, de percepción, física e incluso psicológicas.

gama de colores
ejemplo de gama de colores

Por ese motivo, nace de la mano de Brian Grinstead, TinyColor. Una micro (pero no por ello menos potente) librería que tiene como objetivo simplificar la manipulación de los colores

TinyColor agrega una capa de abstracción. Permite a los desarrolladores dar como input un color, y obtener acceso a múltiples características y propiedades de ese mismo. Sin tener que preocuparse de las funciones matemáticas que la librería ejecuta a bajo nivel. 

Controlar colores con JavaScript y TinyColor a través de su API

Ya són más de 4000 estrellas las que ha acumulado en su repositorio de GitHub. Donde podemos descargarnos una versión comprimida de unos 15Kb de peso. O bien, ejecutando el comando “npm install tinycolor2” para integrarla directamente en nuestro proyecto.

Una vez dentro de nuestro directorio npm_modules, ya está lista para ser importada y empezar a utilizarla. Para ello, solo hace falta pasarle como argumento un color y ésta nos devolverá una instancia con la que vamos a interactuar.

Cabe destacar la versatilidad en los formatos del input de color. Grinstead y su equipo hicieron un excelente trabajo de “regular expresions”. Ya que, tal y como se explica en la documentación, TinyColor admite cadenas de texto en múltiples codificaciones. Tales como la representación hexadecimal, funciones rgb, o espacios de color como HSLA o HSVA.

Si te interesa saber mas acerca de las expresiones regulares, te dejamos un enlace al vistazo que hicimos a la librería SuperExpressive

Crear regex con JavaScript y SuperExpressive

Por supuesto, también se puede entregar un objeto JSON con los valores propios de un espacio de color determinado.

Entre los múltiples métodos que expone la API, encontramos funcionalidades como “.getBrightness()”. Para obtener el índice de luminosidad del color representado entre 0 y 255. O “.isLight()”, para conocer si ese color está más cerca del blanco o del negro absoluto.

Además de los métodos de consulta, encontramos otros de manipulación. Estos pueden ser concatenados entre sí. De esta forma, se puede incrementar la saturación de un color, reducir su luminancia y exportar el resultado en una sola línea de código.

Una documentación clara y concisa

Unos pocos minutos de lectura de la documentación, son suficientes para hacer evidente la cantidad de aplicaciones en las que ésta herramienta puede ser fundamental. 

Por enumerar algunas, se podría utilizar para mejorar la legibilidad en términos de accesibilidad de un texto. Siguiendo las recomendaciones de la W3C, como los ratios de contraste.

O por otra parte, se podría crear una aplicación que ayude a artistas de múltiples disciplinas a definir gamas cromáticas. Basadas en distintas relaciones dentro del círculo cromático, como la tríada o los colores complementarios.

En realidad, la librería ya dispone de métodos para tales efectos, como “.isReadable” o “.triad” respectivamente.

Para ver en acción TinyColor, podemos acceder a su landing page. Tras elegir un color base, inmediatamente aparecen muchas de las funcionalidades que la librería ofrece.

Empecemos a controlar los colores con JavaScript y TinyColor

La mejor forma de aprender una librería es implementándola en un proyecto propio. Así que para conocer un poco mejor TinyColor, vamos a crear una herramienta. Esta va a generar una paleta de colores relativos a un color elegido.

Para ello vamos a empezar con la parte HTML.

Nuestro “layout” base se compone de una zona de selección del color con un selector color-picker que vamos a customizar. Seguidamente vamos a definir otra zona dónde cargaremos las distintas variaciones de color que nos dé TinyColor, agrupadas por Tríada, Tétrada, Complementarios, Monocromático y Análogos.

  <body>
    <div>
      <h1>TinyColor.js</h1>
      <h2>
        a small, fast library for color manipulation and conversion in JavaScript.
      </h2>
      <div class="custom-color-picker">
        <input type="color" class="color-picker">
        <span class="tool-tip">
          Elíge un color
        </span>
      </div>
    </div>
    <h3>Tríada</h3>
    <div class="colors-container triad-container"></div>
    <h3>Tétrada</h3>
    <div class="colors-container tetrad-container"></div>
    <h3>Complementarios</h3>
    <div class="colors-container complements-container"></div>
    <h3>Monocromático</h3>
    <div class="colors-container monochromatic-container"></div>
    <h3>Análogos</h3>
    <div class="colors-container analogous-container"></div>
  </body>

A continuación, vamos a definir los estilos CSS,  empezando por cargar una tipografía directamente de Google Fonts y definiendo los estilos básicos en el body. Con el objetivo de presentar un “color-picker” más atractivo, definimos nuestros propios estilos del selector y ocultamos el date-picker nativo. Finalmente, para los bloques que van a actuar como cajas de color, generamos los selectores CSS y sus propiedades.

@import url("https://fonts.googleapis.com/css2?family=Outfit:wght@100;400;700&display=swap");
body {
  margin: 0;
  padding: 0;
  font-family: "Outfit", sans-serif;
  text-align: center;
  font-weight: 400;
  padding: 40px;
}
.custom-color-picker {
  border: solid 1px #000;
  height: 100px;
  width: 100px;
  border-radius: 50%;
  // background-color: #bada55;
  cursor: pointer;
  position: relative;
  display: block;
  margin: 80px auto 0 auto;
  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
  .color-picker {
    visibility: hidden;
    opacity: 0;
    pointer-events: none;
  }
  &:hover {
    .tool-tip {
      animation: none;
      opacity: 0;
    }
  }
  .tool-tip {
    opacity: 1;
    transition: opacity 0.2s ease-in-out;
    animation: shake 3s cubic-bezier(0.36, 0.07, 0.19, 0.97) infinite;
    position: absolute;
    top: -40px;
    left: 0;
    width: 100%;
    background-color: #fff;
    color: #000;
    font-weight: 400;
    padding: 5px;
    border-radius: 4px;
    box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
    &:after {
      content: "";
      position: absolute;
      left: 50%;
      margin-left: -8px;
      bottom: -5px;
      background-color: #fff;
      transform: rotate(45deg);
      width: 10px;
      height: 10px;
    }
  }
}
.colors-container {
  display: flex;
  .color-block {
    flex: 1;
    height: 200px;
    margin: 0 10px;
    display: block;
    border: solid 1px #000;
    display: flex;
    justify-content: center;
    align-items: center;
  }
}
@keyframes shake {
  10%,
  90% {
    transform: translate3d(0, 0, 0);
  }
  20%,
  80% {
    transform: translate3d(0, 0, 0);
  }
  30%,
  50%,
  70% {
    transform: translate3d(0, -4px, 0);
  }
  40%,
  60% {
    transform: translate3d(0, 4px, 0);
  }
}

Programación del comportamiento con JavaScript

Una vez completada la parte HTML y CSS, podemos empezar a trabajar el comportamiento con Javascript, y por consiguiente, utilizar la librería para nuestro propósito. Para ello, empezamos cargando la librería y definiendo una série de variables y funciones.

Guardamos un conjunto de referencias a elementos de nuestro DOM mediante querySelector, para su posterior uso.

const colorPicker = document.querySelector(".color-picker");
const customColorPicker = document.querySelector(".custom-color-picker");
const triadContainer = document.querySelector(".triad-container");
const tetradContainer = document.querySelector(".tetrad-container");
const monochromaticContainer = document.querySelector(
  ".monochromatic-container"
);
const analogousContainer = document.querySelector(".analogous-container");
const complementsContainer = document.querySelector(".complements-container");

Al cargar la página, queremos que se genere un color aleatorio, para eso definiremos un objeto y TinyColor le asignará un valor “random”.

let color = tinyColor(tinyColor.random());

Declaramos una función llamada applyColorsToContainer para poder generar los elementos de color con un borde adecuado dentro de cada contenedor. 

const applyColorsToContainer = (container, colors, borderColor) => {
  container.innerHTML = "";
  colors.map((color) => {
    const colorElement = document.createElement("div");
    const labelElement = document.createElement("span");
    labelElement.classList.add("color-label");
    labelElement.innerText = color.toHexString();
    colorElement.classList.add("color-block");
    colorElement.style.borderColor = borderColor.toHexString();
    colorElement.style.backgroundColor = color.toHexString();
    const labelColor = color.clone();
    labelElement.style.color = labelColor.isDark()
      ? labelColor.brighten(30).toHexString()
      : labelColor.darken(30).toHexString();
    container.appendChild(colorElement);
    colorElement.appendChild(labelElement);
  });
};

Seguidamente, declaramos la función “onChangeMainColor”. Por un lado va a definir la luminosidad del texto, dependiendo de si el color elegido es oscuro o no. Y por el otro, va a generar las distintas relaciones cromáticas y a llamar a “applyColorsToContainer” para cada una de ellas.

const onChangeMainColor = (_color) => {
  const color = tinyColor(_color);
  const fontColor = color.clone();
  if (fontColor.isDark()) {
    fontColor.brighten(50);
  } else {
    fontColor.darken(50);
  }
  customColorPicker.style.backgroundColor = color.toHexString();
  customColorPicker.style.borderColor = fontColor.toHexString();
  document.body.style.backgroundColor = color.toHexString();
  document.body.style.color = fontColor.toHexString();
  applyColorsToContainer(triadContainer, color.triad(), fontColor);
  applyColorsToContainer(tetradContainer, color.tetrad(), fontColor);
  applyColorsToContainer(
    monochromaticContainer,
    color.monochromatic(),
    fontColor
  );
  applyColorsToContainer(analogousContainer, color.analogous(), fontColor);
  applyColorsToContainer(
    complementsContainer,
    color.splitcomplement(),
    fontColor
  );
};

Finalmente seteamos el valor del color-picker, ejecutamos por primera vez “onChangeMainColor”, y asignamos los eventos correspondientes, a nuestros selectores de color. 

colorPicker.setAttribute("value", color.toHexString());
onChangeMainColor(color.toHexString());
customColorPicker.addEventListener("click", () => {
  colorPicker.click();
});
colorPicker.addEventListener("change", (event) => {
  onChangeMainColor(event.target.value);
});

Una herramienta más en nuestra navaja suíza de recursos JavaScript

Para mí, TinyColor es una librería del calibre de Dayjs. Tras utilizarla una vez, se convierte en una de esas herramientas que guardas en tu “navaja suiza” de recursos. 

Se tratan de librerías que ocupan muy poco, pero que resuelven muchísimo. Cumpliendo a la perfección con el objetivo de todas las librerías. Agregar una capa de abstracción que consigue simplificar conceptos, que de otra forma, serían demasiado confusos para trabajar con ellos.

Este ha sido un pequeño vistazo a la librería TinyColor. A continuación os dejo enlaces a más recursos, así como el ejercicio subido al repositorio de Github de “Librerías Js”.

Nos vemos pronto, un abrazo desarrolladores!

Deja un comentario