El concepto de extensión | Programar Plus

Los tres preprocesadores CSS más populares son compatibles ampliar. No tengo ningún dato, pero en mi experiencia, esta función es confusa al principio y, por lo tanto, un poco infrautilizada. Pensé que podríamos hablar un poco sobre cómo funciona, por qué y cuándo usarlo, algunos trucos y cómo podría ser en el futuro.

El concepto básico

Herencia. Quiero que este selector herede los estilos de este selector.

Uso básico

Usaré Sass aquí. Cubriremos todos los preprocesadores más adelante.

.foo {
  color: red;
}
.bar {
  @extend .foo;
}

Producción:

.foo, .bar {
  color: red;
}

Tenga en cuenta que no “va a obtener” los estilos de .foo e insértelos a continuación en .bar, duplicando así los estilos. En lugar de eso la coma se separa el selector original, aplicando los estilos a ambos de manera más eficiente.

Relación con Mixin

A menudo puede lograr el mismo resultado con un mixin.

@mixin stuff {
  color: red;
}
.foo {
  @include stuff;
} 
.bar {
  @include stuff;
}

Esto colocará esos estilos en ambos selectores. Esto a menudo es más fácil de entender y, honestamente, tiene menos “trampas”, por lo que es más común verlo. Tenga en cuenta que la salida mueve los estilos a ambos lugares:

.foo {
  color: red;
}
.bar {
  color: red;
}

Lo cual, aunque no es un gran problema (especialmente porque el texto repetitivo es particularmente fácil de aplastar para gzip), es menos eficiente.

Sin embargo, los mixins pueden hacer cosas que no se pueden extender, es decir, tomar parámetros y procesarlos / usarlos en la salida.

@mixin padMasterJ ($param: 10px) {
  padding: $param;
}

Extend no puede hacer eso, entonces una regla de oro es: cada vez que use una mezcla sin parámetro, una extensión será más eficiente. Por supuesto, hay excepciones a todas las reglas, que veremos más adelante.

Otra cosa a tener en cuenta es que todos los selectores de nivel superior son ampliables. Los usuarios de MENOS entenderán eso de manera más instintiva porque en MENOS todos los selectores son también mixins, lo cual es extraño para los usuarios de Sass.

El camino de Sass

Hemos usado Sass hasta ahora, pero observemos algo específico sobre cómo Sass maneja Extend. También amplía todos los selectores anidados.

.module {
  padding: 10px;
  h3 {
    color: red; 
  }
}

.news {
  @extend .module;
}

Salidas:

.module, .news {
  padding: 10px;
}
.module h3, .news h3 {
  color: red;
}

Una limitación de Sass extend es que no puede extender un selector anidado. Aquí hay un ejemplo de eso:

.header {
  h3 {
    color: red; 
  }
}

.special-header {
  /* Error: can't extend nested selectors */
  @extend .header h3;
}

El camino MENOS

MENOS usos &:extend. Para transferir nuestro ejemplo súper simple, se vería así:

.foo {
  color: red;
}
.bar {
  &:extend(.foo);
}

Qué salidas:

.foo,
.bar {
  color: red;
}

Lo notable de LESS extend es que no extiende los selectores anidados de forma predeterminada. Entonces, si hacemos esto:

.module {
  padding: 10px;
  h3 {
    color: red; 
  }
}

.news {
  &:extend(.module);
}

El h3 no se extenderá y generará:

.module,
.news {
  padding: 10px;
}
.module h3 {
  color: red;
}

Sin embargo, si agrega el all palabra clave, como:

.news {
  &:extend(.module all);
}

Extenderá todo:

.module,
.news {
  padding: 10px;
}
.module h3,
.news h3 {
  color: red;
}

También puede especificar un selector anidado para extender en lugar del all palabra clave.

El estilo del lápiz óptico

Usos de la aguja @extend y funciona muy similar a Sass.

.module 
  padding 10px
  h3 
    color red

.news 
  @extend .module

Salidas:

.module,
.news {
  padding: 10px;
}
.module h3,
.news h3 {
  color: #f00;
}

La única diferencia notable de Sass es que puede extender un selector anidado. Me gusta:

.header 
  padding 10px
  h3 
    color red

.special-header 
  @extend .header h3

Te conseguirá:

.header {
  padding: 10px;
}
.header h3,
.special-header {
  color: #f00;
}

Ampliación de marcadores de posición

Sass y Stylus tienen selectores de marcador de posición. En Sass:

%grid-1-2 {
  float: left;
  width: 50%;
}

En Stylus:

$grid-1-2
  float left
  width 50%

Esto es algo tan pequeño. Simplemente no genera el marcador de posición en el CSS (por lo tanto, “marcador de posición”), solo le permite extenderlos. La gran ventaja aquí es que puede usar esquemas de nombres internos que no afectan sus esquemas de nombres externos. Las clases de cuadrícula son un buen ejemplo de esto. Nombres como “grid-1-2” y “grid-1-3” son grandes nombres internamente, pero no grandes nombres de clases HTML reales. Con los marcadores de posición, puede mantenerlos internos.

.main-content {
  @extend %grid-2-3;
}
.sidebar {
  @extend %grid-1-3;
}

Cuidado con el orden de selección

Debido a la reescritura del selector, ocasionalmente puede encontrarse con un caso en el que el selector se reescribe antes que otro selector que lo anula. Tenga en cuenta que la especificidad nunca cambiará, pero cuando la especificidad es exactamente la misma en dos selectores, el que se encuentra más abajo en el CSS final “gana”.

Por ejemplo:

.one {
   color: red;
}
.two {
   color: green;
}
.three {
   @extend .one;
}

Entonces tienes un elemento como este:

<div class="three two">test</div>

Puede asumir que el nombre de la clase tres “gana” porque es el más bajo en el Sass, extiende .one, y así el color será rojo. Pero .three en realidad se vuelve a escribir arriba .two:

.one, .three {
  color: red;
}

.two {
  color: green;
}

Dado que el elemento también tiene el nombre de la clase .two, el color es realmente verde en la reproducción final.

Cuidado con la salida mega

Aquí hay un escenario muy razonable cubierto por Nicole Sullivan:

  1. Creas una clase de marcador de posición para clearfix
  2. Creas una clase de módulo genérico que extiende clearfix
  3. Creas otras clases de módulo que extienden el módulo (el “encadenamiento” se extiende)
%clearfix {
  &:before,
  &:after {
    content: " ";
    display: table;
  }
  &:after {
    clear: both;
  }
}

.module {
  padding: 10px;
  @extend %clearfix;
  h3 {
    color: red;
    @extend %clearfix;
    span {
      float: right; 
    }
  }
}

.sports {
  @extend .module;
}

.news {
  @extend .module; 
}

La salida solo para clearfix comienza a parecer un poco espesa:

.module:before, .sports:before, 
.news:before, .module h3:before, 
.sports h3:before, .news h3:before, 
.module:after, .sports:after, 
.news:after, .module h3:after, 
.sports h3:after, .news h3:after {
  content: " ";
  display: table;
}
.module:after, .sports:after, 
.news:after, .module h3:after, 
.sports h3:after, .news h3:after {
  clear: both;
}

Y eso es solo un par de clases. Estoy seguro de que todo un gran proyecto podría volverse mucho más complicado. No es que no vaya a funcionar, es solo que podría ser mejor con una clase real y soltarla en el HTML según sea necesario. O un mixin podría incluso producir menos código en algunos casos dependiendo de qué tan profundo sea el anidamiento.

La regla general es: elija la técnica que requiera el menor resultado final (o que funcione mejor para usted).

Tenga cuidado con las consultas de medios

Ninguno de los idiomas le permite extender desde dentro de una consulta de medios a un selector definido fuera de una consulta de medios.

Esto esta bien:

@media (max-width: 100px) {
  .module {
    padding: 10px;
  }
  .news {
    @extend .module;
  }
}

Esto no funcionará:

.module {
  padding: 10px;
}

@media (max-width: 100px) {
  .news {
    @extend .module;
  }
}

Es la naturaleza del selector en movimiento / reescritura. Si sacó ese selector de la consulta, ya no está respetando la intención del autor (se aplicaría independientemente de la consulta de medios). Quizás las reglas podrían copiarse adentro, pero eso no respeta la intención de extender. Sass 3.3 tendrá una forma de lidiar con eso, pero la verdadera respuesta es la compatibilidad con el navegador nativo.

El futuro de Extend

Extend es bastante poderoso en la capa del preprocesador, pero sería mucho más poderoso en el nivel del navegador. No más selectores separados por comas, mega resultados, problemas de orden de origen, problemas de consulta de medios… nada de eso. El navegador simplemente sabe aplicar las reglas del otro selector a este. Por lo que escuché, los poderes necesarios para convencerlos de que esta era una buena idea, pero ahora están convencidos, y es una cuestión de escribir todas las especificaciones y todo eso. Pero no tengo evidencia de eso para vincularme.

Quizás podría ser:

.module {
  padding: 10px;
}
.news {
  !extend .module;
}

No estoy seguro … Dudo que se pueda usar @extend porque esas @words ya tienen un significado especial (envuelven bloques condicionalmente). Y funciones como extender () son generalmente para valores, no propiedades. Territorio algo nuevo / extraño para el idioma nativo.

Me encantaría escuchar todos tus pensamientos sobre extender. Si lo usa, cómo lo usa, otros problemas que haya encontrado, cómo se imagina que funciona de forma nativa, etc.