Tutorial Less: 9. Mixins condicionales

Logo less

En Less las estructuras condicionales se consiguen mediante los CSS guards y Mixin guards, lo que viene a ser css/mixins protegidos que parece una cosa extraña pero no son más que mixins o estilos condicionales, de ahí lo de protegidos, porque solo se usan cuando se cumplan las condiciones y sino es como que no existiesen.

Aplicar condicionales a cualquier selector CSS en Less

Aunque el nombre del articulo sea Mixins condicionales es más por mantener un guion que por otra cosa porque en realidad el uso de los condicionales no está limitado a los mixins por lo que se pueden usar de igual modo con cualquier selector CSS e incluso con el operador & que puede ser útil para agrupar varios selectores con una condición o simplemente para hacer solo condicionales algunas de las propiedades de una clase por ejemplo.

Por lo tanto la sintaxis es la misma y la forma en la que funcionan también y si bién en principio puede parecer que ofrecen un menor grado de flexibilidad ya que los mixins pueden recibir parámetros no hay que perder de vista que dentro de un mixin también se pueden usar.

Sintaxis de los condicionales en Less

En vez de usar la típica estructura if-else a la que estamos habituados los desarrolladores de Less optaron por mantener la estructura declarativa de CSS en la medida de los posible y por eso los condicionales siguen el estilo de las @media queries, pero bueno todo es acostumbrarse que tampoco es que sea muy distinto porque al fin y al cabo una condición es una condición.

La sintaxis es muy simple, después del selector/mixin tenemos que escribir when y entre paréntesis la condición que tiene que cumplirse, si se quiere que se aplique en el caso de que no se cumpla la condición entonces hay que añadir not después del when.


// Se pueden hacer las condiciones sobre los parametros del mixin
.miMixin(@ancho) when (@ancho >= 50px) {...}

// Podemos usar las variables que este en el ambito del selector
.miClase when (@color = red) {...}

& when (@dia = lunes) {
  .c1{...}
  #i1{...}
}

.miMixin2() when not (@color = red) {
  width: @ancho;
  height: @alto;

  & when (@alto > @ancho) {
    width: @alto;
    height: @ancho;
  }
}

Ejemplo del uso de condicionales en Less

El uso de condicionales puede aportar mucha flexibilidad a los estilos y permitirnos tener bajo control múltiples escenarios sin prácticamente esfuerzo pero podemos caer también en su abuso si intentamos verlos como la primera opción para cualquier cosa.

Vamos a ver un ejemplo más o menos real para hacernos una idea de para qué casos están bien, para cuales puede ser aceptable y donde es una opción demasiado rebuscada.

La idea del ejemplo es construir un mensaje emergente que nos permita definir sus colores en función del tipo (informativo, error, …) en el que además se puedan mostrar botones o no dependiendo de si son necesarios, también queremos garantizar que los colores de las fuentes siempre sean legibles independientemente de los colores de fondo que tengamos y que el padding de los botones si son muy pequeños no quede demasiado grande pero manteniendo unos valores predefinidos para evitar que crezca mucho si son muy grandes.

Empezamos por los colores en del tipo, podríamos usar un condicional por cada tipo pero usando simplemente variables podemos hacerlo y así podemos tener infinitos tipos o sino una clase para cada tipo y listo, este sería un ejemplo de complicarse la vida.

Que se puedan mostrar los botones o no ya nos puede generar más dudas porque es un claro if-else, pero ¿Y porque no crear un mixin con los botones visibles y otro en el que estén ocultos? En estos casos se puede usar sobre todo con vistas a poder usar la misma estructura si prevemos que las condiciones se pueden volver más complejas, si está claro que es lo más lejos que va a llegar como mucho deberíamos un mixin en el que el patrón fuese la condición (los que vimos en el anterior post).

Para garantizar que la fuente sea legible sería directamente una locura, no se puede poner un condicional por cada color, para eso ya tenemos funciones de Less.

Y finalmente el caso en el que su uso es la única opción, si queremos que dentro de un rango de tamaño determinado se aplique un padding especifico no hay otra opción si no queremos crear una clase para cada tamaño concreto, porque haciendo operaciones con las variables podemos hacer que el padding fuese progresivo pero no que de saltos.

Y el ejemplo podría quedar así, con un condicional para el padding aplicado sobre el CSS y otro para el tema de los botones sobre un mixin que como decía si se va a quedar en eso la condición no es la mejor opción.


// Variables
@okColor: #3c3;
@errorColor: #f22;
@anchoBotones: 150px; //@anchoBotones: 70px;

// Mixins
.contenedor(@colorBorde; @colorFondo) {
  border: 2px solid darken(@colorBorde, 40%);
  border-radius: 5px;
  width: 20%;
  margin: 50px auto;
  padding: 10px 20px;
  background: @colorFondo;
  .mensaje {
    text-align: center;
    color: #aaa;
  }
}

// Contenedor con botones de confirmacion
.contenedor(@colorBorde; @colorFondo; @tipo) when (@tipo = confirmacion) {
  .contenedor(@colorBorde; @colorFondo);
  .botones {
    text-align: center;
    margin: 30px auto 10px;
  }
}

// Contenedor sin botones de confirmacion
.contenedor(@colorBorde; @colorFondo; @tipo) when not (@tipo = confirmacion) {
  .contenedor(@colorBorde; @colorFondo);
  .botones {
    display: none;
  }
}

.boton(@color; @ancho: 100px) {
  margin: 5px;
  width: @ancho;
  // Si el ancho del boton es mayor de 100px se le aplica el padding 
  & when (@ancho >= 100px) {
    padding: 10px 20px;
  }

  & when (@ancho < 100px) {
    padding: 5px 10px;
  }
  
  border-radius: 5px;
  background: linear-gradient(@color, darken(@color, 40%));
  color: contrast(darken(@color, 40%));
  border: 2px solid darken(@color, 40%);
  
  &:hover {
    background: linear-gradient(45deg, @color, darken(@color, 40%));
  }
}


// Estilos finales
body {
  background: rgb(29, 31, 32);  
}

.dialogo {
  //.contenedor(@okColor; #111; aviso);
  .contenedor(@errorColor; #111; confirmacion);
  
  .botonAceptar {
    .boton(@okColor; @anchoBotones);
  }

  .botonCancelar {
    .boton(@errorColor; @anchoBotones);
  }
}

See the Pen Mixins condicionales Less by Ivan Salas (@isc7) on CodePen.

Operadores de comparación en Less

Los operadores de comparación son los tipicos >, >=, =, <=, <, no existe el operador distinto != ni <> pero para este propósito podemos usar el no para hacer la negación del igual.


.iguales(@var1; @var2) when (@var1 = @var2) {color: green;}
.distintos(@var1; @var2) when not (@var2 = @var1) {color: red;}
.mayor(@var1; @var2) when (@var1 > @var2) {width: @var1;}
.mayorIgual(@var1; @var2) when (@var1 >= @var2) {color: green;}
.menor(@var1; @var2) when (@var1 < @var2) {height: @var1}
.menorIgual(@var1; @var2) when (@var1 <= @var2) {color: red;}

Operadores logicos en Less

En Less tenemos el and, el not pero no tenemos el or, bueno sí que hay or pero no como tal con su palabra reservada, la forma de utilizarlo es poniendo las condiciones separadas por comas.


.todosIguales(@var1; @var2; @var3) when (@var1 = @var2) and (@var2 = @var3) {color: green;}
.algunosIguales(@var1; @var2; @var3) when (@var1 = @var2), (@var2 = @var3), (@var1 = @var3) {color: green;}

Funciones para comprobar el tipo de variables en Less

Además de los operador también se pueden evaluar las condiciones en función del tipo de las variables o de la unidad que tienen y que pueden ser útiles cuando tenemos algún mixin que pueda tener un comportamiento distinto dependiendo de lo que esté recibiendo.


.a(@var) when (iscolor(@var)) { // es un color!!}
.a(@var) when (iskeyword(@var)) { // es una keyword!!} 
.a(@var) when (isnumber(@var)) { // es un numero!!} 
.a(@var) when (isruleset(@var)) { // es un conjunto de reglas!!} 
.a(@var) when (isstring(@var)) { // es un string!!} 
.a(@var) when (isurl(@var)) { // es una url!!} 

.b(@var) when (isnumber(@var)) &&  (isem(@var)) { // es una medida en ems!!}
.b(@var) when (isnumber(@var)) &&  (ispercentage(@var)) { // es una medida en porcentaje!!} 
.b(@var) when (isnumber(@var)) &&  (ispixel(@var)) { // es una medida en pixels!!} 
.b(@var) when (isnumber(@var)) &&  (isunit(@var, px)) { // la unidad de la medida son pixeles!!} 

En el próximo post seguiremos con los mixins recursivos que podremos usar para hacer bucles para generar clases dinámicamente como los grids por ejemplo.