Skip to main content

Styling

For styling, we recommend using CSS Modules and CSS variables (powered by postcss). In this tutorial, we'll update the Header and decorate our Pokedex pages a bit.

For the Header component, replace the header with Pokedex and add a pokeball image.

⌛ Update the Header component:

shared/header/Header.tsx
import React from 'react';

export const Header = () => (
<div className={styles.Header}>
<img
alt="pokeball"
src="https://upload.wikimedia.org/wikipedia/commons/5/53/Pok%C3%A9_Ball_icon.svg"
width={25}
height={25}
/>
<h1>
Pokedex
</h1>
</div>
);

Let's work with CSS Modules on the example of our Header component.

⌛ Add styles to the header:

shared/header/Header.module.css
.Header {
display: flex;
align-items: center;
}

.Header img {
margin-right: 5px;
}

Let's try to change the fonts in our application. You can add new resources to the pages of the application through the RENDER_SLOTS token, alternative ways and examples can be found in the module render documentation.

⌛ Connect Lato from Google Fonts in the createApp providers:

index.ts
import {
RENDER_SLOTS,
ResourceType,
ResourceSlot,
} from '@tramvai/tokens-render';

createApp({
name: 'pokedex',
modules: [...modules],
providers: [
...providers,
{
provide: RENDER_SLOTS,
multi: true,
useValue: {
type: ResourceType.style,
slot: ResourceSlot.HEAD_CORE_STYLES,
payload: 'https://fonts.googleapis.com/css2?family=Lato&display=swap',
}
},
],
actions: [...actions],
bundles: {...bundles},
});

⌛ And replace the fonts with Lato by connecting a new CSS at the entry point of the application:

global.module.css
html {
font-family: 'Lato', sans-serif;
}
tip

For more efficient font loading and no layout shifting after downloading the required font, we recommend keeping these fonts in the application itself, and adding preload tags for the main fonts, for example:

const provider = {
provide: RENDER_SLOTS,
multi: true,
useValue: {
type: ResourceType.preloadLink,
slot: ResourceSlot.HEAD_CORE_SCRIPTS,
payload: 'https://fonts.gstatic.com/s/lato/v20/S6uyw4BMUTPHjx4wXiWtFCc.woff2',
attrs: {
as: 'font',
type: 'font/woff2',
crossOrigin: 'anonymous',
},
},
};

It remains to add CSS variables, for demonstration we will add a variable with the primary color for headers, and use it in the header.

⌛ Add a CSS variable to the main stylesheet:

global.module.css
:root {
--color-header: hsl(215deg 85% 35%);
}

html {
font-family: 'Lato', sans-serif;
}

⌛ Use a new variable in Header.module.css:

shared/header/Header.module.css
.Header {
display: flex;
align-items: center;
}

.Header img {
margin-right: 5px;
}

.Header h1 {
color: var(--color-header);
}

By default, tramvai has postcss-custom-properties plugin, which can do fallback for CSS variables. Unfortunately, postcss-loader + css-loader process the files one by one, and in our example the plugin will just cut variables from global.module.css as not used. This behavior can be changed with option preserve: true, but in this case the plugin will not perform its main functions.

An alternative option - add global variables directly to the plugin, through the property importFrom, where you can pass a js or css file with a list of variables.

tip

An alternative for CSS variables is to use a variable syntax created specifically for CSS Modules, documentation. But such variables are completely static.

⌛ Update the postcss-custom-properties plugin settings in postcss.js:

postcss.js
module.exports = {
plugins: [
require('postcss-nested'),
require('postcss-modules-values-replace'),
require('postcss-custom-properties')({
// force the plugin to keep variables that it thinks are not used
preserve: true,
}),
require('postcss-import'),
require('postcss-custom-media')({
preserve: false,
}),
],
};

After refreshing the page, we will see the dark blue header `Pokedex'!

Conclusion

That's all for now!

We learned how to create a new tramvai application, work with depencency injection, routing and global state, load data and style the application.

You can familiarize yourself with framework features like API routing, try integration with react-query, integrate our awesome test utilities for unit and e2e testing into the application.