Seguimos con este taller de react con hooks y typescript para sobrellevar el confinamiento y de paso, espero, aprender algo chulo. En la entrada anterior dejamos preparada toda la estructura, vamos ahora darle algo de alegría : )
Un componente con estilo
Vamos con nuestro primer componente transversal, la cabecera, que nos servirá para empezar a trabajar los estilos. Recuerda que este tipo de componentes lo situamos en src/components, donde podemos incluirlo entre los compos relacionados con el layout. Como no recibe ningún tipo de propiedad, no se diferencia apenas de los componentes sencillos del react clásico.
// src/components/layout/main-header.tsx import React from 'react'; export const MainHeader: React.FC = () => { return ( <header className="main-header" role="banner" data-qa="mainHeader"> <h1 className="main-header__title">Dogs!</h1> <small className="main-header__aside">a canine application</small> </header> ); };
El atributo data-qa="mainHeader" nos servirá más adelante, cuando comencemos con los tests e2e. Además, lleva unas clases que siguen la metodología BEM (bloque - elemento - modificador). Vamos a prepararlas siguiendo, recuerda, el patrón 7 x 1 de SASS.
En el directorio assets/scss/base, preparamos un archivo para los colores, que pueden ser estos o los que sean.
/* src/assets/scss/base/_colors.scss */ $base-theme-lighten-01: #eceff1; $base-theme-lighten-02: #cfd8dc; $base-theme-lighten-03: #b0bec5; $base-theme: #607d8b; $base-theme-darken-01: #546e7a; $base-theme-darken-02: #37474f; $base-theme-darken-03: #263238; $danger: #c62828; $warning: #ff9100; $info: #0277bd; $success: #76ff03;
Añadimos unas fuentes, que de nuevo pueden ser las que se quieran.
/* src/assets/scss/base/_fonts.scss */ @font-face { font-family: 'dogs-theme'; src: url('../../fonts/Spartan-Medium.ttf') format('truetype'); } @font-face { font-family: 'dogs-theme-semi-bold'; src: url('../../fonts/Spartan-SemiBold.ttf') format('truetype'); font-weight: bold; }
Incorporamos una sombras rollito materialize también el directorio base.
/* src/assets/scss/base/_shadows.scss */ @mixin depth-02 { box-shadow: 0 2px 2px 0 rgba(0,0,0,0.14), 0 3px 1px -2px rgba(0,0,0,0.12), 0 1px 5px 0 rgba(0,0,0,0.2); } @mixin depth-03 { box-shadow: 0 4px 5px 0 rgba(0,0,0,0.14), 0 1px 10px 0 rgba(0,0,0,0.12), 0 2px 4px -1px rgba(0,0,0,0.3); } @mixin depth-04 { box-shadow: 0 8px 17px 2px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2); }
Añadimos, también en /base, algo para normalizar los estilos, que no voy a copiar por lo extenso, como las normalize.css.
Y ya en el directorio layout preparamos los estilos de la cabecera principal.
/* src/assets/scss/layout/_header.scss */ .main-header { padding: 1rem; background: $base-theme-darken-03; text-align: center; font-weight: bold; border-bottom: 20px solid $base-theme-darken-01; box-sizing: border-box; &__title { font-size: 2.5rem; line-height: 160%; color: $base-theme-lighten-01; } &__aside { font-size: 1.5rem; color: $base-theme-lighten-02; } }
Lo importamos todo en index.scss.
/* src/assets/scss/index.scss */ @import 'base/reset'; @import 'base/colors'; @import 'base/fonts'; @import 'base/shadows'; @import 'layout/header';
Y ya solo nos falta añadir los estilos y el componente en App.tsx.
// src/App.tsx import React from 'react'; import { BrowserRouter as Router } from 'react-router-dom'; import { Routes } from './routes/routes'; import './assets/scss/index.scss'; import { MainHeader } from './components/layout/main-header'; const App: React.FC = () => { return ( <div> <MainHeader/> <Router> <Routes></Routes> </Router> </div> ); } export default App;
Nuestra aplicación ahora debería verse más o menos así.

Nota: Hay otras maneras de trabajar los estilos en React, como los styledComponents, pero por experiencia, sobre todo a la que se trata de un proyecto grande, creo que es mucho más saludable y efectivo el sistema clásico de trabajar con Sass.
Not found
Vamos ahora con la vista más sencilla que tenemos entre manos, el equivalente a la página 404, pero antes vamos a preparar otro componente transversal, que podemos llamar Message, que nos va a servir para mostrar mensajes, que en este caso será de error.
Este componente ya sí que recibe unas propiedades -el mensaje a mostrar y el tipo de mensaje que es (error, warning, success....). Y como estamos usando typeScript, en lugar de PropTypes, vamos a tiparlas en un interfaz, que no hace falta sacar fuera del componente. Algo así:
// src/components/ui/message.tsx import React from 'react'; interface IntMessage { type: string; message?: string; } const Message: React.FC<IntMessage> = (props) => { return ( <div className={`message message--${props.type}`} data-qa="messages" role="complementary"> { props.message && props.message !== '' ? props.message : 'Something sinister has happened' } </div> ); }; export default Message;
Nota: A mí me gusta comenzar el nombre de los interfaces con la partícula Int, que así luego me queda más claro, pero tú mismo...
Le damos un par de estilos.
/* src/assets/scss/components/message.scss */ .message { padding: 1rem; font-size: 1rem; font-weight: bold; @include depth-04; &--error { color: $danger; border: 1px solid $danger; } &--warning { color: $warning; border: 1px solid $warning; } &--info { color: $info; border: 1px solid $info; } &--success { color: $success; border: 1px solid $success; } }
Incluimos esos estilos en las importaciones del index.scss.
/* src/assets/scss/index.scss */ @import 'base/reset'; @import 'base/colors'; @import 'base/fonts'; @import 'layout/header'; @import 'components/message';
Y ahora podemos refactorizar la vista not-found.tsx para incluir el nuevo componente Message.
// src/views/not-found/view-not-found.tsx import React from 'react'; import Message from '../../components/ui/message'; const ViewNotFound: React.FC = () => { return ( <section className="page"> <Message type="warning" message="Page not found" /> </section> ); } export default ViewNotFound;
Además, vamos a añadirle una clase "page", común a todas las vistas, que añadimos a las scss.
/* src/assets/scss/layout/_pages.scss */ .page { margin: 2rem; }
Y, claro está, añadimos este parcial en el index.scss.
/* src/assets/scss/index.scss */ ... @import 'layout/header'; @import 'layout/pages'; ...
Bueno, pues si ahora ponemos en la url cualquier dirección, como no la tenemos indicada en el router que definimos en la entrada anterior, debería cargarse la página de not-found con el componente Message. Algo así:

Vale, pues de momento vamos a dejarlo aquí.
Mucho ánimo a tod@s en estos días, que de esta vamos a salir más pronto que tarde.