Usar variables en nombres de propiedad en LESS (propiedades dinámicas / interpolación de nombre de propiedad)

Noté que inuit.css, que estaba escrito en SASS, tiene un mix de .vendor:

@mixin vendor($property, $value...){ -webkit-#{$property}:$value; -moz-#{$property}:$value; -ms-#{$property}:$value; -o-#{$property}:$value; #{$property}:$value; } 

¿Hay alguna manera de replicar esto en LESS con algunas de las características extrañas como e () y @ {}?

Actualización: MENOS> = 1.6

A partir de la versión 1.6 (ver changelog ), la interpolación del nombre de propiedad se implementa en LESS. Entonces ya no necesitas magia. (Para versiones anteriores, vea mi respuesta original.)

Tu mixin funcionaría básicamente como está:

MENOS:

 .vendor(@property; @value){ -webkit-@{property}: @value; -moz-@{property}: @value; -ms-@{property}: @value; -o-@{property}: @value; @{property}: @value; } /*example*/ .test { .vendor(transform, translateX(20px)); } 

CSS:

 .test { -webkit-transform: translateX(20px); -moz-transform: translateX(20px); -ms-transform: translateX(20px); -o-transform: translateX(20px); transform: translateX(20px); } 

Respuesta original: MENOS <1.6

Por lo que a mí respecta, menos no ha agregado soporte para las propiedades insertadas dinámicamente, que se ha discutido anteriormente en SO muchas veces, ver tal vez:

  • MENOS CSS ¿Escapa de toda la regla de CSS con diferentes prefijos?

  • Mezcla de proveedores generics

  • Mesas de esquina redondeadas con LESS

Así que la forma en que generalmente se hace es con mixins paramétricos y emparejamiento de patrones … por lo que es un poco más difícil de codificar … pero las propiedades y los diferentes proveedores a veces requieren un formato de parámetros un poco diferente, por lo que un poco más de control se agrega de esta manera.

Solución n.º 1: inyectar propiedades generadas dinámicamente en un valor de propiedades

La primera opción para una solución alternativa es un poco fea, pero lo intenté y funcionó en http://less2css.org . Entonces, lo que probé fue inyectar las propiedades creadas dinámicamente en un valor de otra propiedad que codificas duro (que acabo de dar un nombre de “proveedor” aleatorio -inj aquí y le -inj valor ect , pero es posible que quieras usar algo útil en cambio, si ya agregas un elemento desde todos los proveedores, mixin incluye)

 .vendors(@property, @value, @pre: ect) { -inj:~"@{pre}; -webkit-@{property}: @{value}; -moz-@{property}: @{value}; -ms-@{property}: @{value}; -o-@{property}: @{value}; @{property}: @{value}"; } 

Podemos intentarlo con un ejemplo, tal vez algo para que valga la pena … intentémos transformarlo brevemente:

MENOS:

 .test-class{ .vendors(transform, matrix(1,0,0,1,20,20)); .vendors(transform-origin,"10px 10px"); } 

Salida de CSS:

 .test-class { -inj: ect; -webkit-transform: matrix(1, 0, 0, 1, 20, 20); -moz-transform: matrix(1, 0, 0, 1, 20, 20); -ms-transform: matrix(1, 0, 0, 1, 20, 20); -o-transform: matrix(1, 0, 0, 1, 20, 20); transform: matrix(1, 0, 0, 1, 20, 20); -inj: ect; -webkit-transform-origin: 10px 10px; -moz-transform-origin: 10px 10px; -ms-transform-origin: 10px 10px; -o-transform-origin: 10px 10px; transform-origin: 10px 10px; } 

Esto parece producir CSS de trabajo, pero se siente kinnda mal =)


Solución 2: inyecte propiedades generadas dinámicamente en el nombre de la siguiente clase (hasta v1.3.3)

Así que jugué con esta idea un poco más … y pensé en una forma que no produce propiedades innecesarias. Inyecta las propiedades creadas dinámicamente en el nombre de la siguiente clase. Déjame mostrarte cómo lo hice funcionar:

1) Definamos una @rest proveedor general (el argumento @rest se usará para alinear bloques de proveedores múltiples más adelante)

 .vendors(@property, @value, @rest:"") { @inject:~"@{rest} -webkit-@{property}: @{value}; -moz-@{property}: @{value}; -ms-@{property}: @{value}; -o-@{property}: @{value}; @{property}: @{value};"; } 

2) Construir una clase virtual / conjunto de reglas en el que queremos que se incluyan los proveedores, pero lo hacemos como una mezcla que al final construye la siguiente clase de clase, así que realmente hacemos una mezcla que construirá recursivamente dos o más clases. Por ejemplo (utilizando el mismo ejemplo que el anterior) podemos escribir algo como esto (observe el uso de @inject en la segunda llamada a .vendor() para unir los dos bloques de proveedores):

 .test(@nextclass){ .vendors(transform, "matrix(2,0,0,2,20,20)"); .vendors(transform-origin,"10px 10px", @inject); (~"{@{inject}} .@{nextclass}"){/*next class properties*/}; } 

3) Ahora solo envolvemos este mixin en otra clase para mostrar en el CSS:

 .this-class{ .test(next-class); } 

El CSS resultante incluirá esto:

 .this-class { -webkit-transform: matrix(2, 0, 0, 2, 20, 20); -moz-transform: matrix(2, 0, 0, 2, 20, 20); -ms-transform: matrix(2, 0, 0, 2, 20, 20); -o-transform: matrix(2, 0, 0, 2, 20, 20); transform: matrix(2, 0, 0, 2, 20, 20); -webkit-transform-origin: 10px 10px; -moz-transform-origin: 10px 10px; -ms-transform-origin: 10px 10px; -o-transform-origin: 10px 10px; transform-origin: 10px 10px; } .next-class { /*next class properties*/ } 

La salida será solo en una sola línea.

Editar: para obtener un formato más agradable, puede incluir la interpolación de "\n" y "\t" JavaScript, consulte la sugerencia de Scott en los comentarios a continuación.

De esta forma, ahora puede encadenar múltiples clases utilizando la mezcla del proveedor, sin ninguna propiedad innecesaria.


Solución # 3: inyecte propiedades generadas dinámicamente en el nombre de la siguiente clase (v1.4.0) usando recursion

Estoy agregando esto porque Scott señaló en uno de los comentarios que los cambios que vienen con la versión 1.4 no permitirán la solución # 2. Pero si somos un poco ingeniosos, podemos superar este problema. Déjenos ver cuáles son los problemas de la solución anterior y solucionarlos.

1) El primer problema sería que la interpolación del selector ” (~".@{index}") { ... está en desuso”, así que tenemos que hacer la interpolación de cadenas en un paso separado. Implementar esto sería suficiente para inyectar una única .vendors desde arriba.

MENOS: (usando la sugerencia de Scottline para la nueva línea):

 @nl: `"\n\t"`; .vendors(@property, @value) { @inject:~"@{nl}-webkit-@{property}: @{value};@{nl}-moz-@{property}: @{value};@{nl}-ms-@{property}: @{value};@{nl}-o-@{property}: @{value};@{nl}@{property}: @{value};"; } .test(@nextclass){ .vendors(transform, "matrix(2,0,0,2,20,20)"); @inj: ~"{@{inject}`'\n'`} `'\n'`.@{nextclass}"; @{inj} {/*next class properties*/} } .this-class{ .test(next-class); } 

Salida de CSS:

 .this-class { -webkit-transform: matrix(2,0,0,2,20,20); -moz-transform: matrix(2,0,0,2,20,20); -ms-transform: matrix(2,0,0,2,20,20); -o-transform: matrix(2,0,0,2,20,20); transform: matrix(2,0,0,2,20,20); } .next-class { /*next class properties*/ } 

2) El segundo problema sería que “las variables en mixins ya no se ‘filtran’ a su scope de llamada”, pero noté en la versión 1.4.0 beta, que si una variable solo se introduce en una mezcla, aún se puede invocar desde el incluyendo el conjunto de reglas, por lo que con una pequeña recursividad, podría construir los bloques .vendors , y en el último paso asignarlos a una nueva variable, que luego usará para la inyección. También me emocioné y usé la nueva función de extract() introducida en esta versión de menos. Con la variable @i asignamos el nivel de recursión (cantidad de bloques de proveedores que se inyectarán).

MENOS:

 @nl: `"\n\t"`; .multi(@props,@vals,1,@inj) { @property: extract(@props, 1); @value: extract(@vals, 1); @inject:~"@{inj}@{nl}-webkit-@{property}: @{value};@{nl}-moz-@{property}: @{value};@{nl}-ms-@{property}: @{value};@{nl}-o-@{property}: @{value};@{nl}@{property}: @{value};"; } .multi(@props,@vals,@i,@inj:"") when (@i > 0) { @property: extract(@props, @i); @value: extract(@vals, @i); @injnext:~"@{inj}@{nl}-webkit-@{property}: @{value};@{nl}-moz-@{property}: @{value};@{nl}-ms-@{property}: @{value};@{nl}-o-@{property}: @{value};@{nl}@{property}: @{value};"; .multi(@props,@vals,(@i - 1),@injnext); } @properties: "transform-origin" "transform"; @values: "10px 10px" "matrix(2,0,0,2,20,20)"; // string of other properties you want to include in the same class @p: ~"@{nl}width:20px; @{nl}height:12px; @{nl}background-color:#000;"; .this-class { .multi(@properties,@values,2,@p); @inj: ~"{@{inject}`'\n'`} `'\n'`.next-class "; @{inj} {/**/} } 

Salida de CSS:

 .this-class { width:20px; height:12px; background-color:#000; -webkit-transform: matrix(2, 0, 0, 2, 20, 20); -moz-transform: matrix(2, 0, 0, 2, 20, 20); -ms-transform: matrix(2, 0, 0, 2, 20, 20); -o-transform: matrix(2, 0, 0, 2, 20, 20); transform: matrix(2, 0, 0, 2, 20, 20); -webkit-transform-origin: 10px 10px; -moz-transform-origin: 10px 10px; -ms-transform-origin: 10px 10px; -o-transform-origin: 10px 10px; transform-origin: 10px 10px; } .next-class { /*next class properties*/ } 

Ahora, esto funcionó bastante bien para mí en la versión 1.4.0 beta, pero veamos qué nos depara el futuro.

Solo quería agregar que puedes usar ‘menos’ como nombre de propiedad y el analizador lo ignorará pero agregará el rest de la cadena. De esta forma no obtendrás una inject:; vacía inject:; o inj de acuerdo. Todavía es un poco raro, pero eh … 🙂

 .prefix(@property, @value) { -:~";-webkit-@{property}: @{value}; -moz-@{property}: @{value}; -ms-@{property}: @{value}; -o-@{property}: @{value}; @{property}: @{value}"; } 

Ejemplo:

 .prefix(transition, "all .2s, color 0s"); 

Se producirá:

 -webkit-transition: all .2s, color 0; -moz-transition: all .2s, color 0; -ms-transition: all .2s, color 0; -o-transition: all .2s, color 0; transition: all .2s, color 0;