Comenzamos un nuevo capítulo en el que vamos a trabajar de manera más profunda con los estilos en Qwik para complementarlo junto con todo lo que hemos aprendido hasta ahora.
Para poder trabajar en este capítulo es fundamental haber leído y entendido los capítulos anteriores, principalmente el correspondiente al de trabajo por componentes.
Recordad, que a medida que se va avanzando, algunos conceptos se darán por sabidos y no se detallarán.
Una vez realizada la introducción os voy a exponer los puntos que trabajaremos en este capítulo.
useStyles$()
y useStylesScoped$()
Probablemente hayas oído hablar (y conoces por experiencias previas) acerca de los estilos CSS. ¿Para qué sirven exactamente?
En resumen, se podría decir que servirán para personalizar el aspecto de una web y que se transforme en algo que nos entre más por los ojos cuando estamos navegando con cualquier dispositivo en una página web.
Vale, entendido, ¿entonces que tiene de especial en Qwik trabajar con estilos respecto a otras tecnologías? No es muy diferente a la hora de trabajar, pero vamos a tener en cuenta algunos aspectos para hacerlo correctamente sin liarnos mucho la cabeza y sin aplicar cambios que salgan “caros” en los tiempos de ejecución.
Os daré las claves necesarias para aplicar estilos de una manera u otra y luego ya estará en vuestras manos hacer magia con la apariencia de vuestros proyectos, que para eso tenéis mucha imaginación y arte.
Por eso, profundizaremos sobre las diferentes formas de aplicar CSS en Qwik.
¡Comencemos!
Para empezar empezar a trabajar con los aspectos teórico-prácticos en los estilos, debemos de crear un proyecto nuevo tal y como se ha explicado anteriormente y se recomienda llamar al directorio 05-01-styles-global que será el primer apartado que trabajaremos.
Muchas aplicaciones usan una hoja de estilos global para restablecer el navegador y/o definir estilos globales.
Esta es una buena práctica, pero no se recomienda usarla para diseñar nuestros componentes.
Qwik está optimizado para permitir que el navegador simplemente descargue los estilos que se necesitan para la vista actual mediante la carga perezosa ó diferida (lazy-loading).
Si usamos una hoja de estilo global, todos los estilos se descargarán en la primera carga, incluso si no son necesarios para la vista actual y eso hará que no estemos aplicando correctamente el potencial que nos proporciona Qwik con la carga diferida.
Usaremos estilos globales para trabajar con elementos comunes del core de nuestro proyecto, ya que serán apartados que si se van a usar desde la primera carga como pueden ser títulos, estructuras de listas, columnas, etc. Estos elementos serán necesarios y será correcto cargarlos desde la primera carga.
Ahora os voy a mostrar una situación, donde nos encontramos cargando estilos en línea, como este ejemplo:
export const MyComponent = () => {
return (
<div style="color: red">
My Component
</div>
);
}
Tenemos el problema que con este enfoque vamos a cargar los estilos dos veces (ó más, dependiendo si estamos aplicando un mismo estilo en diferentes componentes, donde aplicaríamos cargas innecesarias) ocurriendo lo siguiente.
¿Cómo solucionar esto?
Para solucionar este problema trabajando correctamente y sin cargas extras, necesitaremos cargar los estilos independientemente del componente más a nivel global mediante esta dos formas:
import 'referencia-fichero.css'
en el src/root.tsx
.useStyles$()
(mucho mejor opción) que es un hook de carga de estilos con carga diferida (Lazy Load) que obtiene los estilos solamente si es necesario (porque lo vamos a usar) evitando cargarlo dos veces en caso de hidratación SSR.Vamos a empezar con el fichero global.css
, sin usar useStyles$()
y luego ya iremos desgranando las diferentes opciones poco a poco y con ejemplos, para entenderlo todo mucho mejor. ¡Paso a paso!
En el ejemplo con el que estamos trabajando, que es basado en el proyecto que se crea con el comando del CLI (Empty app
) que nos proporciona el equipo de Qwik, tenemos en src/root.tsx
lo siguiente donde se cargarán los estilos globales con el fichero global.css
que se encuentra en src/global.css
:
import { component$ } from '@builder.io/qwik';
...
1.- Cargando el contenido del fichero CSS de manera global
import './global.css';
export default component$(() => {
return (
<QwikCityProvider>
...
<body lang="en">
<RouterOutlet />
<ServiceWorkerRegister />
</body>
</QwikCityProvider>
);
});
Cualquier cambio que hagamos en ese fichero src/global.css
se aplicará a cualquier ruta que usemos en nuestro proyecto ya que estaremos incoporando el contenido de estos en el componente <RouterOutlet />
perteneciente a <QwikCityProvider />
para la carga de las rutas visto en Routing (Enrutamiento).
Automáticamente, al cargar el fichero src/global.css, Qwik intentará alinear este archivo en modo de producción, si la cantidad de CSS es inferior a 10 KB. Si el archivo tiene más de 10 KB, se cargará como un archivo separado.
Para ver como se comporta el proyecto, vamos a iniciar el proyecto con npm start
y esto es lo que nos debe de aparecer (Usando Empty App
):
Ahora mismo se está cargando el contenido del fichero src/routes/index.tsx
que contiene este código:
import { component$ } from '@builder.io/qwik';
import type { DocumentHead } from '@builder.io/qwik-city';
export default component$(() => {
return (
<>
<h1>Hi</h1>
<p>
Can't wait to see what you build with qwik!
<br />
Happy coding.
</p>
</>
);
});
Analizándolo, podemos ver que tenemos dos selectores principales:
h1
: Título principal.p
: Párrafo.Cambiamos el contenido HTML de este componente, dejándolo de la siguiente forma:
import { component$ } from '@builder.io/qwik';
import type { DocumentHead } from '@builder.io/qwik-city';
export default component$(() => {
return (
<>
<h1>Home </h1>
<p>
Home Page
</p>
</>
);
});
Probamos a añadir al final del fichero src/global.css
una regla en los estilos para cambiar la apariencia de h1
y ver su funcionamiento.
h1 {
color: red;
}
Guardamos y podemos observar que ya ha cambiado el color del elemento h1
, por lo que ya estamos aplicando correctamente el uso de los estilos globales.
Como recomendación, añadiremos los estilos en este fichero siempre y cuando estemos seguros al 100% que se van a usar esos estilos desde el inicio.
Si en algún caso particular tenemos dudas, lo haremos mediante useStyles$()
para añadirlo globalmente de manera diferida a medida que va cargando el contenido necesario.
Lo desarrollado hasta este momento:
Abriendo el proyecto 05-01-styles-global podemos encontrar lo desarrollado hasta este punto.
El enlace lo tenéis a continuación:
https://shorten-up.vercel.app/_uYIb61aDx
En este momento vamos a trabajar con el hook useStyles$ y necesitaremos crear el segundo proyecto llamado 05-02-styles-usestyles para centrarnos en este aspecto.
Ya sabemos la manera de cargar los estilos globales, sin hacerlo de manera diferida realizándolo directamente en la primera carga inicial.
Ahora lo que se quiere es trabajar con estilos globales que se vayan añadiendo poco a poco, de manera diferida a medida de nuestras necesidades, solo cargándose para los casos necesarios en los componentes que se vayan cargando en el proceso, para aplicar buenas prácticas.
Ahí es donde entra en juego el hook useStyles$()
.
useStyles$
El enlace a la documentación con todos los detalles y posibles futuros cambios, lo podéis consultar en el siguiente enlace:
Para añadir unos estilos globalmente de manera diferida haremos uso del hook useStyles$()
.
Con este hook debemos de realizar lo siguiente y haremos los cambios en src/root.tsx
para usar el fichero src/global.css
que ya conocemos.
Añadimos el import
correspondiente al hook useStyles$()
import { component$, useStyles$ } from '@builder.io/qwik';
Reemplazamos la línea donde obtenemos el CSS del fichero src/global.css
import './global.css'
Por
import globalStyles from './global.css?inline'
globalStyles
? ¿Podríamos poner otro nombre?SI, podría ser cualquier nombre, al final sería el nombre del contenedor que tiene todo el contenido de estilos en formato de cadena de texto cargados desde el fichero seleccionado.
?inline
al final de la referencia del fichero CSS?Esto es algo que tenemos que implementar cuando trabajamos con Vite para importarlo como una cadena de texto, si no, no funcionará (Podéis probarlo sin añadir ?inline
). Recordad que hay agregar el parámetro de consulta en línea a la importación, así: importar estilos desde ./<nuestra-hoja-de-estilos>.css?inline
Teniendo estos últimos detalles claros, lo que nos queda es pasarle el valor del contenedor con los estilos al hook useStyles$()
, dentro del componente donde estamos trabajando, quedando finalmente así:
// 1.- Añadimos el hook useStyles$
import { component$, useStyles$ } from '@builder.io/qwik';
import { QwikCityProvider, RouterOutlet, ServiceWorkerRegister } from '@builder.io/qwik-city';
import { RouterHead } from './components/router-head/router-head';
// 2.- Cargamos los estilos en línea
import globalStyles from './global.css?inline';
export default component$(() => {
// 3.- Los añadimos para cargarlos de manera diferida cuando este disponible el componente
useStyles$(globalStyles)
return (
<QwikCityProvider>
...
</QwikCityProvider>
);
});
Guardamos y comprobamos que todo sigue igual, como antes.
Se sigue manteniendo el color rojo de la fuente del elemento h1
, con lo que se puede afirmar que ya está cogiendo bien la información.
Esta configuración hará que independientemente a la ruta que estemos accediendo siempre cargue estos estilos sin hacer recargas extras para coger la misma información.
Siempre que carguemos los estilos de esta manera (haciendo uso de useStyles$
) se añadirán en el selector head
(Cabecera) de la página principal donde estará presente en todo momento para su uso a nivel global, independientemente de la ruta en la que nos encontremos.
¿Cómo podemos verlo? Para acceder aquí seleccionamos Inspeccionar
para acceder a las Herramientas del desarrollador
en nuestro navegador favorito (en mi caso Google Chrome) y dentro en Elementos
:
Si vamos al fichero src/global.css
, podemos observar que en efecto, está cargando bien su contenido:
Podemos experimentar haciendo un cambio en vivo desde el navegador en el apartado donde están los estilos de src/global.css
. Probamos cambiando el estilo del background-color
en el body
a un azul por ejemplo, añadiendo background-color: blue
.
Para poder hacerlo debemos de hacer doble click sobre el texto donde están las reglas CSS aplicadas:
Y cuando tengamos habilitada la opción de editar, añadimos background-color: blue
dentro del elemento body
:
body {
...
background-color: blue;
...
}
Y aplicando los cambios, cambiará el fondo pero solo será manera temporal hasta que refresquemos desde el navegador o cambiemos de ruta haciendo que coja su configuración inicial, para mostrar un fondo blanco y seguir aplicando lo que tenemos establecido en el fichero de manera global.
Este será el resultado actual en /
después de haber cambiado el estilo a mano en el navegador:
Una vez aclarado este primer aspecto, vamos a observar que pasaría si navegamos a una nueva ruta, que en estos momentos no tenemos creada, debemos de crear el fichero index.tsx
dentro de src/routes/second
con el siguiente contenido:
import { component$ } from '@builder.io/qwik';
export default component$(() => {
return (
<div>
<h1>Second page</h1>
<p>Content second page</p>
</div>
);
});
Guardamos y si se da el caso que se refresca la página principal /
volvemos a hacer el cambio a mano, para seguir con lo que estamos viendo.
Ahora escribimos la ruta adicional (ejecutando como MPA y no SPA) que tenemos /second
y al ejecutar veremos como se restablece la configuración cargando la información original de src/global.css
aplicando los estilos iniciales (los asignados antes de hacer el cambio a mano):
Con esto se puede ver el comportamiento de nuestra aplicación con los estilos globales y las consecuencias de modificarlo a mano.
Ahora que lo tenemos más claro, vamos a aplicar que TODOS los h1 estén en rojo con borde azul punteado.
Volvemos al fichero src/global.css
y añadimos lo siguiente:
h1 {
color: red;
border: 2px dotted blue;
}
Guardamos y recargamos /
, observando dentro de Herramientas de Desarrollador
en Elementos
que ya coge los nuevos estilos (1
) y que se aplica (2
):
Añadimos en src/routes/index.tsx
el componente <Link />
con la ruta /second
para navegar desde la página principal y así no recargar la página desde el navegador (modo SPA) y ver como se comporta.
También añadimos el elemento a
para ir al mismo destino, solo que esté recargará toda la página (modo MPA).
import { component$ } from '@builder.io/qwik';
import { Link } from '@builder.io/qwik-city';
export default component$(() => {
return (
<>
...
<Link href="/second">Second Page (SPA)</Link> /
<a href="/second">Second Page (MPA) - Recarga de nuevo todo</a>
</>
)}
)
Hacemos lo mismo en el fichero que corresponde a la ruta /second
ubicado en src/routes/second/index.tsx
, asignando el path para ir a la ruta /
.
import { component$ } from '@builder.io/qwik';
import { Link } from '@builder.io/qwik-city';
export default component$(() => {
return (
<>
...
<Link href="/">Home (SPA)</Link> /
<a href="/">Home (MPA) - Recarga de nuevo todo</a>
</>
)}
)
Ahora tenemos las dos formas de navegar, en forma de SPA con <Link />
y en MPA con a
.
Para probarlo en acción en los dos casos, vamos a las Herramientas de desarrollador
, en el apartado de Elementos
cambiamos el color de la fuente de h1
por el color naranja (orange
):
Si estamos en /
como en la imagen, hacemos click en Second Page (SPA)
y lo que ocurre es que vamos a navegar SIN RECARGAR completamente el contenido del navegador:
Navegaremos a la ruta /second
y ahora ocurrirá algo que no ocurría antes (cuando introduciamos la ruta a mano, actuando como si fuese MPA), que se mantienen los estilos por estar navegando en modo SPA y no en modo MPA.
Así muchísimo mejor, evitamos cargas innecesarias y seguiremos trabajando con los estilos que definimos con la mayor eficiencia posible.
Podemos comprobar haciendo click en el elemento a
(que tiene el texto Home (MPA) - Recarga de nuevo todo
) y ahí podremos ver que recargará todo de nuevo y desapareciendo el color naranja del título a favor de volver a verse el color rojo definido en src/global.css
.
Hay que tener en cuenta siempre ese detalle, el de saber como navegamos, si mediante SPA ó MPA.
Os recomiendo que lo probéis varias veces hasta que lo interioricéis.
Hemos trabajado con la carga de los estilos desde el inicio, aunque fuese en diferido, se hacía siempre al inicio.
En este punto vamos a implementar un componente hijo llamado <PlusInfo />
en (src/routes/index.tsx
) que se podrá agregar haciendo click en el botón de acción en la página principal, donde añadiremos unos estilos mediante el hook useStyles$()
.
Añadimos los estilos en una cadena de texto:
export const PlusInfoCss = `
.plus-info {
margin-top: 1em;
border: 1px solid red;
padding: 1em;
display: block;
}
`;
Y luego añadimos el componente en el mismo fichero, que lo cargaremos con una acción de click:
import { component$, useStyles$ } from '@builder.io/qwik';
export const PlusInfo = component$(() => {
useStyles$(PlusInfoCss);
return (
<div class="plus-info">
<div>More info...</div>
</div>
);
});
Ahora lo que nos queda es añadir un estado con useStore
(que explicaré su funcionamiento más al detalle en el capítulo de Gestión de estados, donde se aprenden la snociones sobre la gestión de estados) y un botón, junto con el componente que se mostrará cuando el valor show
sea true
. Aprovechamos y quitamos el elemento a
, que no es necesario.
import { component$, useStore, useStyles$ } from '@builder.io/qwik';
import { Link } from '@builder.io/qwik-city';
/*
Estilos de PlusInfo y el componente PlusInfo
*/
export default component$(() => {
const store = useStore({show: false});
return (
<>
...
<br/><br/>
{store.show ? <PlusInfo /> : null}
<button onClick$={() => (store.show = !store.show)}>Show Plus Info</button>
</>
);
});
Y todo junto, quedará de la siguiente manera:
import { component$, useStore, useStyles$ } from "@builder.io/qwik";
import { Link, type DocumentHead } from "@builder.io/qwik-city";
export const PlusInfoCss = `
.plus-info {
margin-top: 1em;
border: 1px solid red;
padding: 1em;
display: block;
}
`;
export const PlusInfo = component$(() => {
useStyles$(PlusInfoCss);
return (
<div class="plus-info">
<div>More info...</div>
</div>
);
});
export default component$(() => {
const store = useStore({ show: false });
return (
<>
<h1>Home </h1>
<p>Home Page</p>
<br/><br/>
<Link href="/second">Second Page</Link>
<br/><br/>
{store.show ? <PlusInfo /> : null}
<button onClick$={() => (store.show = !store.show)}>
Show Plus Info
</button>
</>
);
});
La apariencia actual que tenemos es la siguiente:
Second
en estructura./second
, tanto en forma de SPA como MPA,que ya estaba incoporado.<PlusInfo />
, ya que por defecto no se carga y con ello no cargará la clase .plus-info
que mostrará un recuadro rojo.Si vamos a las Herramientas de Desarrollador
, al apartado de Elementos
podemos observar los estilos que tenemos cargados.
Por el momento solo están añadidos los estilos correspondientes a src/global.css
, cuya referencia es q:style="xm30tf-0"
(esto puede que sea diferente en vuestro caso) pero no los estilos de la clase .plus-info
que aparecerá seguido a este siempre y cuando carguemos primera vez el componente <PlusInfo />
pulsando (3) (NO PULSAR TODAVÍA). Por el momento NO ESTÁN cargados.
Antes de pulsar en Show Plus Info
, vamos a añadir unos cambios en src/routes/second/index.ts
sustituyendo lo siguiente:
import { component$ } from '@builder.io/qwik';
export default component$(() => {
return (
<div>
<h1>Second page</h1>
<p>Content second page</p>
<Link href="/">Home Page (SPA)</Link> /
<a href="/">Home Page (MPA) - Recarga de nuevo todo</a>
</div>
);
});
Aquí encontramos que se añade el componente <PlusInfoSecond />
, que a diferencia de la ruta principal NO CARGAMOS ningún estilo.
Esto nos permite que se pueda observar como se comporta la carga de los estilos en la otra ruta si cargásemos o no cargásemos el componente <PlusInfo />
en la ruta inicial.
En el caso de hacerlo, se añadirían los estilos de con la clase .plus-info
para usarlos en esta ruta (/second
) y si no, no tendríamos opción (esto no es el comportamiento habitual, se está implementando así a modo de demostración su funcionamiento):
import { component$ } from '@builder.io/qwik';
import { Link } from '@builder.io/qwik-city';
export const PlusInfoSecond = component$(() => {
return (
<div class="plus-info">
<div>More info...</div>
</div>
);
});
export default component$(() => {
return (
<div>
<h1>Second page</h1>
<p>Content second page</p>
<PlusInfoSecond />
<Link href="/">Home Page (SPA)</Link> /
<a href="/">Home Page (MPA) - Recarga de nuevo todo</a>
</div>
);
});
Volvemos a la ruta inicial (/
) y sin tocar nada más, vamos a navegar a la segunda página:
Estando en la segunda página, tenemos cargado el componente <PlusInfoSecond />
que contiene la clase .plus-info
, como en el componente <PlusInfo />
, pero que no muestra el borde rojo ya que no se han cargado los estilos previamente en la ruta inicial.
Esto es lo lógico, ya que en la ruta inicial NO hemos pulsado Show Plus Info
y por lo tanto, no se añaden globalmente los estilos correspondientes a plus-info
.
Volvemos a la página principal mediante el enlace Home (SPA)
.
Y ahora si pulsamos en Show Plus Info
.
Debido a que no formaba parte del SSR renderizado previamente el componente <PlusInfo />
se muestra por primera vez el componente que carga los estilos con la clase .plus-info
(3) después de haber hecho click en Show Plus Info
(1) aplicándose el borde rojo (2) como se puede apreciar en la siguiente imagen.
¿Qué pasará ahora si navegamos a la página /second
desde el enlace Second Page (SPA)
?
(Pensad un par de segundos y mirar la imagen anterior y recordad que estamos navegando en modo SPA y no MPA)
Al ir a la nueva página, como ya tenemos los estilos de la clase .plus-info
cargados por la ejecución de la acción para cargar <PlusInfo/>
en la ruta inicial, los tenemos disponibles.
Esto hará que teniendo esa clase ya cargada ahora si se visualice con un borde rojo en el contenido de <PlusInfoSecond />
Teniendo claro esto, pasamos al useStylesScoped$()
que es similar a este hook pero con alguna diferencia destacable que se explicará a continuación.
Abriendo el proyecto 05-02-styles-usestyles podemos encontrar lo desarrollado hasta este punto.
El enlace lo tenéis a continuación:
https://shorten-up.vercel.app/2HGaxfoYi8
En el apartado anterior, hemos visto cómo los estilos se pueden cargar de forma diferida a medida que se necesitan, usando la función useStyles$()
, con el caso inicial de los estilos globales y con la clase .plus-info
que cargaba de manera diferida SI renderizábamos el componente <PlusInfo />
en la ruta inicial.
Los estilos del navegador son globales y se aplicarán a todos los elementos del DOM y por otro lado, ajeno a esto, Qwik también proporciona una forma de cargar estilos que será de manera especifica para un componente específico de manera aislada.
¿Cómo funcionará este nuevo hook?
Se generará una clase única para cada componente y luego se insertará esa identificación de clase única en la hoja de estilo.
Usamos el hook useStylesScoped$()
para cargar y aplicar un estilo concreto que encontramos en varios apartados comunes, para aplicar una personalización en un componente específico únicamente.
Comenzamos trabajando con dos componentes que tienen una clase con el mismo nombre pero diferentes colores de fondo.
Si seguimos usando useStyles$()
al cargar los estilos tanto de uno como del otro con esta función, los dos estilos chocan, lo que da como resultado un comportamiento indeseable. Aplicará el estilo del componente que está en segunda posición (en el orden de ejecución de arriba hacia abajo), por coger el estado global con el valor más actual.
Veamos y analicemos el problema.
Seguimos con lo trabajado en el apartado anterior, quitando lo que corresponde a <PlusInfo />
y <PlusInfoSecond />
.
Accedemos a src/routes/index.tsx
y creamos estos dos componentes usando useStyles$()
y aplicando un color de fondo diferente a los dos:
export const OrangeComponent = component$(() => {
useStyles$(`
.bg {
background-color: orange;
}
`);
return (
<div class="bg">
<div>OrangeComponent</div>
</div>
);
});
export const GreenComponent = component$(() => {
useStyles$(`
.bg {
background-color: green;
}`
);
return (
<div class="bg">
<div>Green Componente</div>
</div>
);
});
Añadimos los componentes dentro del contenido HTML:
export default component$(() => {
return (
<>
<h1>Home </h1>
<p>Home Page</p>
<GreenComponent />
<OrangeComponent />
<Link href="/second">Second Page (SPA)</Link> /
<a href="/second">Second Page (MPA) - Recarga de nuevo todo</a>
</>
);
});
Y este será el resultado:
¿Por qué el fondo aparece en color naranja en ambos casos? Esto se debe a que estamos aplicando estilos globales y el componente <OrangeComponent />
se encuentra al final en el orden establecido. Dado que su color de fondo es naranja, este color se actualiza y se aplica a todos los componentes con la clase .bg mediante CSS.
Podemos ver que es así, si cambiamos el orden, ahora poniendo primero <OrangeComponent>
y posteriormente <GreenComponent>
. Lo que debe de pasar es que se deben de aplicar los estilos de <GreenComponent>
cuya clase .bg
tendrá un fondo de color verde:
export default component$(() => {
return (
<>
<h1>Home </h1>
<p>Home Page</p>
<OrangeComponent />
<GreenComponent />
<Link href="/second">Second Page (SPA)</Link> /
<a href="/second">Second Page (MPA) - Recarga de nuevo todo</a>
</>
);
});
Observando el resultado, pasa lo mismo, pero como hemos invertido el orden ahora cogerá los estilos del componente <GreenComponent />
.
Viendo el problema vamos a darle solución y lo único que debemos de hacer en estos casos, para los componentes en los que queremos aislar los estilos aplicando los específicos es usar la función useStylesScoped$()
.
Es tan sencillo como cambiar useStyles$()
por useStylesScoped$()
quedando así en los dos casos, para aislar sus estilos a nivel componente:
import { component$, useStylesScoped$ } from '@builder.io/qwik';
export const OrangeComponent = component$(() => {
useStylesScoped$(`
.bg {
background-color: orange;
}
`);
return (
<div class="bg">
<div>OrangeComponent</div>
</div>
);
});
export const GreenComponent = component$(() => {
useStylesScoped$(`
.bg {
background-color: green;
}
`);
return (
<div class="bg">
<div>Green Componente</div>
</div>
);
});
Ahora recargando la página, deberían de aparecer los componentes aplicando sus estilos propios:
Si observamos a los elementos de la página, a diferencia de lo que se reflejaba antes es que en la parte donde se asigna la clase .bg
se está asignando una nueva clase con el valor del q-style
de cada componente específico.
Por ejemplo, observando el componente <OrangeComponent />
Así conseguiremos personalizar específicamente los estilos de un componente aun teniendo el mismo nombre de clase.
Llegados a este punto ya hemos trabajado con las dos funciones principales para aplicar estilos a nivel global (useStyles$
) y a nivel especifico de componente (useStylesScoped$
) desde estilos definidos en una cadena de texto.
Lo que vamos a hacer en los siguiente apartados es definir esos estilos aplicando el CSS en Javascript y mediante el uso de Styled Components
(componentes estilizados).
Abriendo el proyecto 05-03-styles-usestylesscoped podemos encontrar lo desarrollado hasta este punto.
El enlace lo tenéis a continuación:
https://shorten-up.vercel.app/XhhAl4Dkvi
Qwik tiene compatibilidad con CSS dentro de Javascript como primera clase utilizando styled-vanilla-extract
(https://github.com/wmertens/styled-vanilla-extract), que nos va a proporcionar una solución extremadamente eficiente sin tiempo de ejecución donde podremos aplicar CSS dentro de Javascript.
Para poder aprovecharnos de esto, debemos de integrar todas las herramientas que vienen con ello para usarlo en Qwik:
npm run qwik add styled-vanilla-extract
Lo que nos muestra al ejecutar el comando es lo siguiente:
Seleccionamos la opción Yes looks good, finish update!
y cuando pase un instante y efectué todas las instalaciones y configuraciones, mostrará algo similar a esto:
Este mensaje está unido a la versión actual de Qwik
Una vez instaladas las dependencias y efectuadas las configuraciones, antes de definir nuestra primera clase CSS dentro de Javascript podemos observar que se ha generado una nueva ruta en src/routes
llamada styled-flower
para ver la estructura de como trabaja el CSS dentro de Javascript:
Podemos entrar dentro y ver como está por si tenemos curiosidad. Yo no lo haré, me centraré en base a lo que se aplica ahí aplicando un ejemplo para modificar el componente <OrangeComponent />
que hemos implementado usando la función useStylesScoped$()
en el apartado anterior.
Creamos dentro de src
el directorio styles
y dentro de este un fichero llamado colors.css.ts
.
La ruta quedará definida para ese fichero en src/styles/colors.css.ts
y se mostrará de la siguiente forma:
Dentro del fichero colors.css.ts
definimos un bloque de estilos con color naranja donde le asignamos un nombre como por ejemplo orangeClass
:
import { style } from 'styled-vanilla-extract/qwik';
export const orangeClass = style({
display: 'block',
width: '100%',
height: '500px',
background: 'orange',
});
Como se puede observar, para poder aplicar el CSS dentro del código Javascript (Typescript en este caso) debemos de importar la función style
desde el paquete que hemos instalado recientemente:
import { style } from 'styled-vanilla-extract/qwik';
Y una vez que ya tenemos esto, implementamos enviando un objeto como argumento, donde implementamos las propiedades de CSS como el ancho del elemento, color de fondo…
export const orangeClass = style({ // <=======
display: 'block', // Una propiedad
width: '100%',
height: '500px',
background: 'orange',
});
Es muy sencillo, simplemente importamos la constante definida en src/styles/colors.css.ts
con el valor constante orangeClass
y lo aplicamos introduciendo el valor mediante binding en la propiedad class
.
Siguiendo con lo que hemos trabajado en el apartado anterior, nos dirigimos a la ruta inicial (src/routes/index.tsx
) de nuestro proyecto y realizamos la siguiente implementación:
// Traemos el valor constante de CSS aplicado en Javascript
import { orangeClass } from "~/styles/colors.css";
Eliminamos la función useStylesScoped$()
con el contenido de la clase .bg
y sustituyendo la class
por nuestra nuevo valor CSS en Javascript
...
import { orangeClass } from '~/styles/colors.css';
export const OrangeComponent = component$(() => {
return (
<div class={orangeClass}>
<div>OrangeComponent</div>
</div>
);
});
...
El código quedará de la siguiente forma:
import { component$, useStylesScoped$ } from '@builder.io/qwik';
import { DocumentHead, Link } from '@builder.io/qwik-city';
import { orangeClass } from '~/styles/colors.css';
export const OrangeComponent = component$(() => {
return (
<div class={orangeClass}>
<div>OrangeComponent</div>
</div>
);
});
export const GreenComponent = component$(() => {
useStylesScoped$(`
.bg {
background-color: green;
}`);
return (
<div class="bg">
<div>Green Componente</div>
</div>
);
});
export default component$(() => {
...
});
...
Ahora es el momento de ver los resultados, el componente <OrangeComponent />
recordad que ahora va a tener una altura fija y que aparecerá más expandido verticalmente:
Implementamos una variante para el color verde en src/styles/colors.css.ts
modificando también la altura fija de orangeClass
a 200px:
import { style } from 'styled-vanilla-extract/qwik';
export const orangeClass = style({
display: 'block',
width: '100%',
height: '200px',
background: 'orange',
});
export const greenClass = style({
display: 'block',
width: '100%',
height: '200px',
background: 'green',
});
Implementando greenClass
dentro del componente <GreenComponent />
:
...
import { greenClass, orangeClass } from '~/styles/colors.css';
export const OrangeComponent = component$(() => {
return (
<div class={orangeClass}>
<div>OrangeComponent</div>
</div>
);
});
export const GreenComponent = component$(() => {
return (
<div class={greenClass}>
<div>Green Componente</div>
</div>
);
});
...
Y se mostrará de la siguiente forma:
Si os habéis fijado, tenemos en común prácticamente todas las propiedades excepto el color. ¿Como podemos crear una opción para reutilizar las propiedades del bloque?
Recordad, es trabajar con Javascript, por lo que podemos crear un objeto independiente y aplicar esas propiedades que se usan en las dos clases. Vamos a por ello implementando de la siguiente forma:
import { style } from 'styled-vanilla-extract/qwik';
// Objeto con propiedades en común
const contenBlockClass = {
display: 'block',
width: '100%',
height: '200px',
};
export const orangeClass = style({
...contenBlockClass, // Aplicando las propiedades de contentBlockClass
background: 'orange',
});
export const greenClass = style({
...contenBlockClass, // Aplicando las propiedades de contentBlockClass
background: 'green',
});
Guardamos y comprobamos:
El resultado es el mismo, por lo que hemos conseguido refactorizar el contenido del bloque y reutilizarlo para los dos elementos, pudiendo hacerlo un número indeterminado de veces con diferentes colores a nuestro gusto.
Como nota adicional aun siendo una opción muy popular, este modo de trabajar NO ES LA MEJOR OPCIÓN para Qwik. No está optimizado para el rendimiento en tiempo de ejecución y no tienen un buen soporte de transmisión SSR, lo que lleva a un rendimiento degradado del servidor y del cliente. ¿Por qué lo enseño? Creo que está bien enseñar todas las opciones y luego que cada profesional saque sus propias conclusiones.
Pasamos al último apartado, donde trabajaremos con los styled components
(Componentes con estilos).
Abriendo el proyecto 05-04-styles-css-in-js podemos encontrar lo desarrollado hasta este punto.
El enlace lo tenéis a continuación:
https://shorten-up.vercel.app/xp79cA07v1
Los styled components
son una herramienta popular en ReactJS
que se utiliza para escribir CSS dentro de Javascript, que es lo que se ha trabajado en el punto anterior.
En este caso hay una diferencia mínima, que ahora en vez de trabajar con objetos, trabajaremos con sintaxis de componentes con estilo Qwik. De esta manera conseguiremos resolver el problema del coste en el tiempo de ejecución que se daba en el punto anterior.
Seguiremos con el código del punto anterior
Para trabajar con ello, debéis de ejecutar el comando (si no lo habéis hecho) que se ha ejecutado anteriormente para extraer elemento estilizados con Vanilla Javascript:
npm run qwik add styled-vanilla-extract
Una vez todo instalado y configurado nos dirigimos al fichero colors.css.ts
que lo encontramos en src/styles
y definimos el estilo mediante los styled components
junto con el siguiente código:
import { style, styled} from 'styled-vanilla-extract/qwik';
export const RedBox = styled.div`
display: block;
width: 100%;
height: 100px;
background: red;
`;
... (Otro estilos)
Ahora tenemos un nuevo elemento, que será el componente <RedBox />
que lo añadiremos en src/routes/index.tsx
junto con los otros dos elementos, para ver como se comporta:
import { greenClass, orangeClass, RedBox } from '~/styles/colors.css';
...
export default component$(() => {
return (
<>
<h1>Home </h1>
<p>Home Page</p>
<RedBox />
<OrangeComponent />
<GreenComponent />
<Link href="/second">Second Page (SPA)</Link> /
<a href="/second">Second Page (MPA) - Recarga de nuevo todo</a>
</>
);
});
Y el resultado será el siguiente:
Y ahora surge la duda de, ¿Cómo puedo añadir información dentro del styled components
llamado <RedBox />
?
Muy fácil, mediante la adición de contenido abriendo el selector como un nuevo bloque HTML cambiando de lo siguiente:
<RedBox />
A lo siguiente:
<RedBox>
¡¡Aquí el contenido con el fondo rojo!!
</RedBox>
Y su resultado será el siguiente:
Con esto, ya aprenderíamos otro concepto de como trabajar con estilos en Qwik. Recordad que haciendo uso de los styled components resolveremos el problema del coste en el tiempo de ejecución que se daba en el punto anterior, ¡¡Qué en este caso sería 0!!
Abriendo el proyecto 05-05-styles-styled-components podemos encontrar lo desarrollado hasta este punto.
El enlace lo tenéis a continuación:
https://shorten-up.vercel.app/lX2nNu4bdv
A continuación, os propongo una pequeña actividad super fácil, la de convertir los componentes <OrangeComponent />
y <GreenComponent/>
en styled components, para resolver el problema que están dando actualmente con el tiempo de ejecución.
El resultado visual debe de ser el mismo, pero optimizando el apartado mencionado del tiempo de ejecución. Sin mirar el resultado, espero que lo intentéis, si habéis entendido todo bien, es super sencillo.
Para poder tener disponibles lo cambios realizados en este apartado, os dejo el código a continuación cuya app será 05-06-styles-finish-practice.
Al finalizar este capítulo, deberíamos de ser capaces de:
global.css
.useStyles$
y useStylesScoped$
.css-in-js
) y styled components
(Componentes con estilo) para los casos que se amolden mejor a nuestro proyecto.En este capítulo hemos avanzado un poco más con Qwik y con lo que hemos aprendido podríamos perfectamente crear proyectos sencillos, sin mucha lógica en el que podríamos ir practicando el tema de layouts, enrutado (Routing), componentes, estilos,…
¿Qué podríamos hacer? Un buen ejercicio para practicar estos aspectos es coger plantillas HTML puras y adaptarlas a un proyecto de Qwik (o cualquier otra tecnología, por si trabajamos en Angular, React,…).
Un sitio para descargar plantillas HTML de manera gratuita y usarlas para nuestros propósitos educativos o incluso para iniciar un nuevo proyecto lo podéis encontrar en el siguiente enlace:
Con el objetivo de afianzar todo lo aprendido hasta ahora, en el siguiente capítulo haremos una práctica paso a paso, para convertir una plantilla HTML en un proyecto de Qwik.
Con esto vamos a conseguir asentar e interiorizar todos los conocimientos con algo más real.
Posteriormente, al finalizar la práctica veremos como funciona el apartado SSR (Server Side Rendering) de Qwik.