Saltar al contenido principal
Tema

2 formas de hacer estilos dinámicos en Svelte

4 minutos de lectura

Índice

Son recurrentes los casos donde es necesario hacer css dinámico usando javascript y en nuestro caso en Svelte, normalmente se suelen usar estilos en línea para modificar las propiedades que necesitemos que sean dinámicas, pero este método tiene problemas de especificidad, rendimiento, es limitado y en general se trata de evitar lo más posible. Por esto es que en este artículo veremos como hacer estilos dinámicos con y sin estilos en línea en Svelte.

Barra de progreso

Para ilustrar ambos métodos, haremos una barra de progreso desde cero, crearemos un div con un p y otro div que será la barra de progreso.

1<div class="progress">
2 <p class="progress__label">Loading</p>
3 <div class="progress__bar" />
4</div>

Vamos a crear una variable con el valor del progreso y usaremos un intervalo para simular el progreso de la tarea.

1<script>
2 let progress = 0;
3
4 setInterval(() => {
5 // Aumenta en uno
6 progress++;
7
8 // Si llega a 100 reiniciamos el valor
9 if (progress === 100) progress = 0;
10 }, 100);
11</script>

Colocamos la variable en el párrafo y en la barra de progreso junto con los atributos de accesibilidad necesarios.

El atributo role="progressbar" nos sirve para especificar a los lectores de pantalla que este elemento se trata de una barra de progreso, aria-valuenow tiene el objetivo de establecer el progreso de la tarea y por tanto es el atributo que tenemos que modificar dinamicamente, aria-valuemin determina el valor mínimo que puede tener la barra de progreso y aria-valuemax el valor máximo. También hacemos uso del atributo aria-labelledby donde debemos colocar el id del elemento que queramos que actué como un label.

1<div class="progress">
2 <p class="progress__label" id="progress-label">Loading {progress}%</p>
3 <div
4 class="progress__bar"
5 aria-labelledby="progress-label"
6 role="progressbar"
7 aria-valuenow={progress}
8 aria-valuemin="0"
9 aria-valuemax="100"
10 />
11</div>

Luego de tener esto listo, procedemos a hacer los estilos que tendrá la barra de progreso.

1<style>
2 .progress {
3 display: flex;
4 flex-direction: column;
5 justify-content: center;
6 align-items: center;
7 text-align: center;
8 height: 100%;
9 }
10
11 .progress__label {
12 font-size: 1.5rem;
13 font-weight: bold;
14 }
15
16 .progress__bar,
17 .progress__bar::before {
18 height: 20px;
19 border-radius: 0.5rem;
20 }
21
22 .progress__bar {
23 position: relative;
24 background-color: #eee;
25 width: 90%;
26 max-width: 320px;
27 margin-left: auto;
28 margin-right: auto;
29 }
30
31 .progress__bar::before {
32 content: "";
33 position: absolute;
34 top: 0;
35 left: 0;
36 background-color: #4169e1;
37 }
38</style>

Para que la barra de progreso funcioné, tenemos que modificar el valor de la propiedad width del pseudoelemento before de la barra de progreso.

Con estilos en línea

Para hacerlo con estilos en línea usamos una variable css para el ancho que luego modificaremos con el atributo style.

1.progress__bar::before {
2 content: "";
3 position: absolute;
4 top: 0;
5 left: 0;
6 background-color: #4169e1;
7
8 /* Agregamos la variable */
9 width: var(--progress);
10}
1<div class="progress">
2 <p class="progress__label" id="progress-label">Loading {progress}%</p>
3
4 <!-- Modificamos el atributo style con la variable -->
5 <div
6 style="--progress: {progress}"
7 class="progress__bar"
8 aria-labelledby="progress-label"
9 role="progressbar"
10 aria-valuenow={progress}
11 aria-valuemin="0"
12 aria-valuemax="100"
13 />
14</div>

Es una solución valida y sencilla, pero existe otra forma que es digna de ser presentada.

Sin estilos en línea

Para no usar estilos en línea lo que haremos será crear un elemento style e insertarlo en el head, Svelte nos proporcioná el elemento especial <svelte:head> para añadir elementos al head. Para añadir los estilos que serán dinámicos tendremos que usar la etiqueta especial {@html string} esta recibe un string con html, un comportamiento muy similar al de innerHtml y de esa forma obtenemos el resultado esperado.

1<svelte:head>
2 {@html `<style>
3 .progress__bar::before{
4 width: ${progress}%;
5 }
6 </style>`}
7</svelte:head>
8
9<div class="progress">
10 <p class="progress__label" id="progress-label">Loading {progress}%</p>
11 <div
12 class="progress__bar"
13 aria-labelledby="progress-label"
14 role="progressbar"
15 aria-valuenow={progress}
16 aria-valuemin="0"
17 aria-valuemax="100"
18 />
19</div>

De esta manera podemos obtener lo mismo pero sin los problemas que conlleva usar estilos en línea y los beneficios de poder usar selectores.

Haciendo un componente

Para poder reutilizar el código de esta técnica podemos hacer un componente bastante sencillo.

1<!-- Recibe la prop de css, está será donde tendremos que pasarle el css -->
2<script>
3 export let css = "";
4</script>
5
6<!-- Si le pasamos un string que no este vació, agrega el elemento con el css. -->
7<svelte:head>
8 {#if css.trim()}
9 {@html `<style>${css}</style>`}
10 {/if}
11</svelte:head>

Y podemos usarlo de esta forma.

1<Style css={`
2 .progress__bar::before{
3 width: ${progress}%;
4 }
5`} />

Resultado

Y obtenemos una barra de progreso accesible y con estilos dinámicos de una manera muy sencilla y limpia gracias a Svelte 🎉.

Barra de progreso completandose