Home | Clases | Sass y SCSS | Reglas y directivas básicas

Reglas y directivas básicas


Introducción

En este apartado introduciremos las reglas y directivas básicas, también conocidas como at-rules:

  • Importación (@import) y Parciales (Partials)
  • Mixins (@mixin) e Includes (@include)
  • Funciones (@function)
  • Herencia y extensión (@extend)
  • Gestión de errores (@error y @warn)
  • Depuración (@debug)
  • Des-anidamiento (@at-root)


Importación (@import) y Parciales (Partials)

Sass/SCSS extiende la regla @import de CSS añadiendo la capacidad de importar tanto hojas de estilo CSS como hojas de Sass/CSS, permitiendo así el acceso a mixins, funciones y variables de dichas hojas y la combinación de todas ellas en una sola.

Además, Sass/SCSS realiza toda la importación durante la compilación, en vez de obligar al navegador a hacer una petición HTTP por cada @import como hace CSS.

La sintaxis es similar a la de CSS, salvo que en este caso podremos realizar varios @import a la vez, separando las rutas de los archivos por comas:

SCSS

// archivo foundation/_code.scss
code {
padding: .25em;
line-height: 0;
}

// archivo foundation/_lists.scss
ul, ol {
text-align: left;

& & {
padding: {
bottom: 0;
left: 0;
}
}
}

// archivo style.scss
@import 'foundation/code', 'foundation/lists';


Sass

// archivo foundation/_code.sass
code
padding: .25em
line-height: 0

// archivo foundation/_lists.sass
ul, ol
text-align: left

& &
padding:
bottom: 0
left: 0

// archivo style.sass
@import foundation/code, foundation/lists


CSS

code {
padding: .25em;
line-height: 0;
}

ul, ol {
text-align: left;
}
ul ul, ol ol {
padding-bottom: 0;
padding-left: 0;
}


Nota: Las rutas de los archivos se escriben en formato url, usando / incluso en Windows. Por otra parte, no es necesario el uso de ./ para @import relativos, pues estos siempre están disponibles.


Como convención, los ficheros Sass/SCSS pensados para ser importados y no compilados directamente suelen empezar su nombre por _, como el anterior _code.scss. Este tipo de archivos reciben el nombre de partials y ese _ le dice a las herramientas de Sass que no lo compilen por sí mismo. No obstante, como hemos visto en el ejemplo anterior, no es necesario poner el _ para importar el partial.

Si creamos un archivo _index.scss o _index.sass en un directorio, cuando importamos dicho directorio dicho fichero será cargado en su lugar, lo cual puede resultar util para mejorar la estructura de nuestras hojas y partials:

SCSS

// foundation/_code.scss
code {
padding: .25em;
line-height: 0;
}

// foundation/_lists.scss
ul, ol {
text-align: left;

& & {
padding: {
bottom: 0;
left: 0;
}
}
}

// foundation/_index.scss
@import 'code', 'lists';

// style.scss
@import 'foundation';


Sass

// foundation/_code.sass
code
padding: .25em
line-height: 0

// foundation/_lists.sass
ul, ol
text-align: left

& &
padding:
bottom: 0
left: 0

// foundation/_index.sass
@import code, lists

// style.sass
@import foundation


CSS

code {
padding: .25em;
line-height: 0;
}

ul, ol {
text-align: left;
}
ul ul, ol ol {
padding-bottom: 0;
padding-left: 0;
}



Si bien los @import están pensados para ser utilizados al principio de la hoja, es posible anidarlos dentro de reglas de estilo u otras directivas:

SCSS

// _theme.scss
pre, code {
font-family: 'Source Code Pro', Helvetica, Arial;
border-radius: 4px;
}

// style.scss
.theme-sample {
@import "theme";
}


Sass

// _theme.sass
pre, code
font-family: 'Source Code Pro', Helvetica, Arial
border-radius: 4px

// style.sass
.theme-sample
@import theme


CSS

.theme-sample pre, .theme-sample code {
font-family: 'Source Code Pro', Helvetica, Arial;
border-radius: 4px;
}



Por supuesto, también es posible importar hojas de estilo CSS mediante Sass/SCSS, si bien hay que acordarse de no utilizar la extensión .css en el @import:

SCSS

// code.css
code {
padding: .25em;
line-height: 0;
}

// style.scss
@import 'code';


Sass

// code.css
code {
padding: .25em;
line-height: 0;
}

// style.sass
@import code


CSS

code {
padding: .25em;
line-height: 0;
}



Y para finalizar, dado que @import ya estaba definido en CSS; Sass/SCSS necesita una forma de compilar los @import de CSS, de modo que se considerarán @import de CSS aquellos que cumplan alguna de las siguientes reglas:

  • Aquéllos cuya URL acabe en .css
  • Aquéllos cuya URL empiece por http:// o https://
  • Aquéllos cuya URL esté escrita como url( )
  • Aquéllos que incluyan media queries.


SCSS

@import "theme.css";
@import "http://fonts.googleapis.com/css?family=Droid+Sans";
@import url(theme);
@import "landscape" screen and (orientation: landscape);


Sass

@import "theme.css"
@import "http://fonts.googleapis.com/css?family=Droid+Sans"
@import url(theme)
@import "landscape" screen and (orientation: landscape)


CSS

@import url(theme.css);
@import "http://fonts.googleapis.com/css?family=Droid+Sans";
@import url(theme);
@import "landscape" screen and (orientation: landscape);



Mixins (@mixin) e Includes (@include)

Los mixins nos permiten definir estilos para ser re-utilizables en todas las hojas de estilo o ser distribuidos en forma de librerías.

La sintaxis para declararlos es de tipo @mixin <nombre> { ... } o de tipo @mixin nombre(<argumentos...>) { ... }, mientras que la sintaxis para llamarlos será utilizando @include <nombre> o bien @include <nombre>(<argumentos...>)

SCSS

@mixin reset-list {
margin: 0;
padding: 0;
list-style: none;
}

@mixin horizontal-list {
@include reset-list;

li {
display: inline-block;
margin: {
left: -2px;
right: 2em;
}
}
}

nav ul {
@include horizontal-list;
}


Sass

@mixin reset-list
margin: 0
padding: 0
list-style: none


@mixin horizontal-list
@include reset-list

li
display: inline-block
margin:
left: -2px
right: 2em




nav ul
@include horizontal-list



CSS

nav ul {
margin: 0;
padding: 0;
list-style: none;
}
nav ul li {
display: inline-block;
margin-left: -2px;
margin-right: 2em;
}



Como hemos comentado, los mixins pueden contener argumentos, que pueden ser obligatorios, opcionales (si se le incluye un valor por defecto) o incluir palabras clave o keywords (como cuando vamos a necesitar el nombre del argumento para una estructura condicional):

SCSS

//Argumentos obligatorios
@mixin rtl($property, $ltr-value, $rtl-value) {
#{$property}: $ltr-value;

[dir=rtl] & {
#{$property}: $rtl-value;
}
}

.sidebar {
@include rtl(float, left, right);
}

//Argumentos opcionales
@mixin replace-text($image, $x: 50%, $y: 50%) {
text-indent: -99999em;
overflow: hidden;
text-align: left;

background: {
image: $image;
repeat: no-repeat;
position: $x $y;
}
}

.mail-icon {
@include replace-text(url("/images/mail.svg"), 0);
}

//Argumentos con keywords
@mixin square($size, $radius: 0) {
width: $size;
height: $size;

@if $radius != 0 {
border-radius: $radius;
}
}

.avatar {
@include square(100px, $radius: 4px);
}


Sass

/Argumentos obligatorios
@mixin rtl($property, $ltr-value, $rtl-value)
#{$property}: $ltr-value

[dir=rtl] &
#{$property}: $rtl-value



.sidebar
@include rtl(float, left, right)

/Argumentos opcionales
@mixin replace-text($image, $x: 50%, $y: 50%)
text-indent: -99999em
overflow: hidden
text-align: left

background:
image: $image
repeat: no-repeat
position: $x $y

.mail-icon
@include replace-text(url("/images/mail.svg"), 0)

/Argumentos con keywords
@mixin square($size, $radius: 0)
width: $size
height: $size

@if $radius != 0
border-radius: $radius



.avatar
@include square(100px, $radius: 4px)



CSS

/Argumentos obligatorios
nav ul {
margin: 0;
padding: 0;
list-style: none;
}
nav ul li {
display: inline-block;
margin-left: -2px;
margin-right: 2em;
}

/Argumentos opcionales
.sidebar {
float: left;
}
[dir=rtl] .sidebar {
float: right;
}

/Argumentos con keywords
.mail-icon {
text-indent: -99999em;
overflow: hidden;
text-align: left;
background-image: url("/images/mail.svg");
background-repeat: no-repeat;
background-position: 0 50%;
}



También es posible incluir argumentos arbitrarios, de modo que si al declarar el mixin no sabemos cuantos argumentos va a necesitar, podemos añadir al ultimo de ellos unos puntos suspensivos, lo cual indicará que todos los que lleguen a partir de ese confirmarán una lista con el nombre de ese argumento:

SCSS

//Ejemplo 1
@mixin order($height, $selectors...) {
@for $i from 0 to length($selectors) {
#{nth($selectors, $i + 1)} {
position: absolute;
height: $height;
margin-top: $i * $height;
}
}
}

@include order(150px, "input.name", "input.address", "input.zip");

//Ejemplo 2
@mixin syntax-colors($args...) {
@debug keywords($args); // (string: #080, comment: #800, $variable: $60b)

@each $name, $color in keywords($args) {
pre span.stx-#{$name} {
color: $color;
}
}
}

@include syntax-colors(
$string: #080,
$comment: #800,
$variable: #60b,
)


Sass

//Ejemplo 1
@mixin order($height, $selectors...)
@for $i from 0 to length($selectors)
#{nth($selectors, $i + 1)}
position: absolute
height: $height
margin-top: $i * $height




@include order(150px, "input.name", "input.address", "input.zip")

//Ejemplo 2
@mixin syntax-colors($args...)
@debug keywords($args) // (string: #080, comment: #800, $variable: $60b)

@each $name, $color in keywords($args)
pre span.stx-#{$name}
color: $color




@include syntax-colors($string: #080, $comment: #800, $variable: #60b);


CSS

//Ejemplo 1
input.name {
position: absolute;
height: 150px;
margin-top: 0px;
}

input.address {
position: absolute;
height: 150px;
margin-top: 150px;
}

input.zip {
position: absolute;
height: 150px;
margin-top: 300px;
}

//Ejemplo 2
pre span.stx-string {
color: #080;
}

pre span.stx-comment {
color: #800;
}

pre span.stx-variable {
color: #60b;
}



Para finalizar, además de recibir argumentos, un mixin puede recibir bloques enteros de estilos o content blocks, lo cual se debe declarar en el mixin con la directiva @content. Al llamar al mixin, pasaremos dicho block content entre llaves.

SCSS

//Ejemplo sin argumentos
@mixin hover {
&:not([disabled]):hover {
@content;
}
}

.button {
border: 1px solid black;
@include hover {
border-width: 2px;
}
}

/Ejemplo con argumentos
@mixin media($types...) {
@each $type in $types {
@media #{$type} {
@content($type);
}
}
}

@include media(screen, print) using ($type) {
h1 {
font-size: 40px;
@if $type == print {
font-family: Calluna;
}
}
}


Sass

/Ejemplo sin argumentos
@mixin hover
&:not([disabled]):hover
@content



.button
border: 1px solid black
@include hover
border-width: 2px

/Ejemplo con argumentos
@mixin media($types...)
@each $type in $types
@media #{$type}
@content($type)




@include media(screen, print) using ($type)
h1
font-size: 40px
@if $type == print
font-family: Calluna


CSS

/Ejemplo sin argumentos
.button {
border: 1px solid black;
}
.button:not([disabled]):hover {
border-width: 2px;
}

/Ejemplo con argumentos
@media screen {
h1 {
font-size: 40px;
}
}
@media print {
h1 {
font-size: 40px;
font-family: Calluna;
}
}



Funciones (@function)

Las funciones nos van a permitir definir operaciones complejas que podremos abstraer de una manera sencilla y utilizar en cualquier otra parte de la hoja.

Se definen mediante la directiva @function <nombre>(<argumentos...>) { ... } y solo puede contener sentencias universales (variables, directivas de control o directivas @error, @warn y @debug).

Los argumentos funcionan de manera similar a los mixins, tal y como muestran los siguientes ejemplos:

SCSS

//Argumentos obligatorios
@function pow($base, $exponent) {
$result: 1;
@for $_ from 1 through $exponent {
$result: $result * $base;
}
@return $result;
}

.sidebar {
float: left;
margin-left: pow(4, 3) * 1px;
}

//Argumentos opcionales
@function invert($color, $amount: 100%) {
$inverse: change-color($color, $hue: hue($color) + 180);
@return mix($inverse, $color, $amount);
}

$primary-color: #036;
.header {
background-color: invert($primary-color, 80%);
}

//Argumentos con keywords
$primary-color: #036;
.banner {
background-color: $primary-color;
color: scale-color($primary-color, $lightness: +40%);
}

//Argumentos como listas
@function min($numbers...) {
$min: null;
@each $number in $numbers {
@if $min == null or $number < $min {
$min: $number;
}
}
@return $min;
}

.micro {
width: min(50px, 30px, 100px);
}


Sass

//Argumentos obligatorios
@function pow($base, $exponent)
$result: 1
@for $_ from 1 through $exponent
$result: $result * $base

@return $result


.sidebar
float: left
margin-left: pow(4, 3) * 1px


//Argumentos opcionales
@function invert($color, $amount: 100%)
$inverse: change-color($color, $hue: hue($color) + 180)
@return mix($inverse, $color, $amount)


$primary-color: #036
.header
background-color: invert($primary-color, 80%)


//Argumentos con keywords
$primary-color: #036
.banner
background-color: $primary-color
color: scale-color($primary-color, $lightness: +40%)


//Argumentos como listas
@function min($numbers...)
$min: null
@each $number in $numbers
@if $min == null or $number < $min
$min: $number


@return $min


.micro
width: min(50px, 30px, 100px)



CSS

//Argumentos obligatorios
.sidebar {
float: left;
margin-left: 64px;
}

//Argumentos opcionales
.header {
background-color: #523314;
}

//Argumentos con keywords
.banner {
background-color: #036;
color: #0a85ff;
}

//Argumentos como listas
.micro {
width: 30px;
}


Importante: La directiva @return que hemos visto en algunos de los ejemplos anteriores indica, como en otros lenguajes, que le ejecución finaliza y se devuelve el resultado. Esta directiva es obligatoria y solo se puede utilizar dentro de @function.


Herencia y extensión (@extend)

Hay muchos casos en los que nos encontramos en la necesidad de que una clase tenga todos los estilos de otra, además de algo específico para ella. Para estos casos, utilizamos la directiva @extend mediante la sintaxis @extend <selector>.

Esto nos permitirá modificar reglas de una forma más sencilla y óptima que con un mixin:

SCSS

//Ejemplo 1
.error {
border: 1px #f00;
background-color: #fdd;

&--serious {
@extend .error;
border-width: 3px;
}
}

//Ejemplo 2
.error:hover {
background-color: #fee;
}

.error--serious {
@extend .error;
border-width: 3px;
}


Sass

//Ejemplo 1
.error
border: 1px #f00
background-color: #fdd

&--serious
@extend .error
border-width: 3px

//Ejemplo 2
.error:hover
background-color: #fee


.error--serious
@extend .error
border-width: 3px



CSS

//Ejemplo 1
.error, .error--serious {
border: 1px #f00;
background-color: #fdd;
}
.error--serious {
border-width: 3px;
}

//Ejemplo 2
.error:hover, .error--serious:hover {
background-color: #fee;
}

.error--serious {
border-width: 3px;
}



El uso de @extend tiene algunas limitaciones, ya que no es posible usarlo en selectores encadenados, como .message.info o .main .info, si no solo en selectores sencillos como p o .info. Tampoco es posible utilizarlos dentro de media-queries (@media):

SCSS

.alert {
@extend .message.info;
// ^^^^^^^^^^^^^
// Error: Write @extend .message, .info instead.

@extend .main .info;
// ^^^^^^^^^^^
// Error: write @extend .info instead.
}

@media screen and (max-width: 600px) {
.error--serious {
@extend .error;
// ^^^^^^
// Error: ".error" was extended in @media, but used outside it.
}
}

.error {
border: 1px #f00;
background-color: #fdd;
}


Sass

.alert
@extend .message.info
// ^^^^^^^^^^^^^
// Error: Write @extend .message, .info instead.

@extend .main .info
// ^^^^^^^^^^^
// Error: write @extend .info instead.

@media screen and (max-width: 600px)
.error--serious
@extend .error
// ^^^^^^
// Error: ".error" was extended in @media, but used outside it.



.error
border: 1px #f00
background-color: #fdd




Gestión de errores (@error y @warn)

El uso de la directiva @error está recomendado cuando trabajamos con @mixin y @function para asegurarnos de que los argumentos y resultados tienen los valores y unidades adecuados. Si así lo hemos definido, la Consola nos mostrará dicho mensaje:

SCSS

@mixin reflexive-position($property, $value) {
@if $property != left and $property != right {
@error "La propiedad #{$property} debe ser left o right.";
}

$left-value: if($property == right, initial, $value);
$right-value: if($property == right, $value, initial);

left: $left-value;
right: $right-value;
[dir=rtl] & {
left: $right-value;
right: $left-value;
}
}

.sidebar {
@include reflexive-position(top, 12px);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Error: La propiedad top debe ser left o right.
}



Sass

@mixin reflexive-position($property, $value)
@if $property != left and $property != right
@error "La propiedad #{$property} debe ser left o right."


$left-value: if($property == right, initial, $value)
$right-value: if($property == right, $value, initial)

left: $left-value
right: $right-value
[dir=rtl] &
left: $right-value
right: $left-value



.sidebar
@include reflexive-position(top, 12px)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Error: La propiedad top debe ser left o right.





De igual modo, el uso de la directivas @warn está recomendado cuando trabajamos con @mixin y @function para asegurarnos de que los argumentos no están obsoletos o de que estamos utilizando la librería de forma no muy óptima. Si así lo hemos definido, la Consola nos mostrará dicho mensaje:

SCSS

$known-prefixes: webkit, moz, ms, o;

@mixin prefix($property, $value, $prefixes) {
@each $prefix in $prefixes {
@if not index($known-prefixes, $prefix) {
@warn "Prefijo desconocido #{$prefix}.";
}

-#{$prefix}-#{$property}: $value;
}
#{$property}: $value;
}

.tilt {
// Oops, hemos escrito "webkit" en vez de "wekbit"
@include prefix(transform, rotate(15deg), wekbit ms);
}

// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Warning: Prefijo desconocido wekbit.


Sass

$known-prefixes: webkit, moz, ms, o

@mixin prefix($property, $value, $prefixes)
@each $prefix in $prefixes
@if not index($known-prefixes, $prefix)
@warn "Prefijo desconocido #{$prefix}."


-#{$prefix}-#{$property}: $value

#{$property}: $value


.tilt
// Oops, hemos escrito "webkit" en vez de "wekbit"
@include prefix(transform, rotate(15deg), wekbit ms)


// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Warning: Prefijo desconocido wekbit.


CSS

.tilt {
-wekbit-transform: rotate(15deg);
-ms-transform: rotate(15deg);
transform: rotate(15deg);
}



Depuración (@debug)

Tal y como hemos utilizado en muchos ejemplos, la directiva @debug <expresión> nos permite imprimir el valor de la expresión en la Consola con propósitos de desarrollo, incluyendo además el nombre del fichero y la línea a la que hace referencia.

SCSS

@mixin inset-divider-offset($offset, $padding) {
$divider-offset: (2 * $padding) + $offset;
@debug "divider offset: #{$divider-offset}";

margin-left: $divider-offset;
width: calc(100% - #{$divider-offset});
}


Sass

@mixin inset-divider-offset($offset, $padding)
$divider-offset: (2 * $padding) + $offset
@debug "divider offset: #{$divider-offset}"

margin-left: $divider-offset
width: calc(100% - #{$divider-offset})


El mensaje de la Consola será del tipo:


test.scss:3 Debug: divider offset: 132px



Des-anidamiento (@at-root)

La directiva @at-root <selector> { ... } nos permite des-anidar los elementos contenidos dentro de dicho selector, de modo que se compilen a nivel root en el documento

SCSS

//Ejemplo 1
@mixin unify-parent($child) {
@at-root #{selector-unify(&, $child)} {
@content;
}
}

.wrapper .field {
@include unify-parent("input") {
/* ... */
}
@include unify-parent("select") {
/* ... */
}
}

//Ejemplo 2
@media print {
.page {
width: 8in;

@at-root (without: media) {
color: #111;
}

@at-root (with: rule) {
font-size: 1.2em;
}
}
}


Sass

//Ejemplo 1
@mixin unify-parent($child)
@at-root #{selector-unify(&, $child)}
@content



.wrapper .field
@include unify-parent("input")
/* ...

@include unify-parent("select")
/* ...

//Ejemplo 2
@media print
.page
width: 8in

@at-root (without: media)
color: #111


@at-root (with: rule)
font-size: 1.2em



CSS

//Ejemplo 1
.wrapper input.field {
/* ... */
}
.wrapper select.field {
/* ... */
}

//Ejemplo 2
@media print {
.page {
width: 8in;
}
}
.page {
color: #111;
}
.page {
font-size: 1.2em;
}



Referencias


Fecha de publicación: 15/04/2019
Asignaturas: aplicaciones webdiseño de interfaces web
Temas: css css3 sass scss less preprocesadores
Utilizamos cookies propias y de terceros para mejorar su experiencia en la navegación. Al seguir navegando entendemos que acepta su uso.
Si lo desea, consulte nuestras políticas de privacidad y cookies
ENTENDIDO
[X] Cerrar

Contacta conmigo


[X] Cerrar

Acceso alumnos