@moller/gnist-themes v3.1.0
Themes for @moller/design-system
@moller/gnist-themes
is a library containing themes, design tokens and atomic css used by @moller/design-system
,
but which can also be used on its own. It is based on vanilla-extract under the hood,
and requires an extra compilation step. See Setup for details.
Overview
This library exposes the following parts: themes, tokens, atoms, colors, and typography
Themes
A theme is a css class which sets a number of variables. Developers can refer to these through Tokens.
Basic usage:
import { bilholdLight } from "@moller/gnist-themes/themes/bilholdLight.css.js";
// add the class to the <body> element either inline (e.g in a Next layout) or programatically
document.body.classList.add(bilholdLight);
These themes currently exist and can be referred to in the way described above
audi
autoria
bilholdLight
brandless
cupra
dahles
gumpen
mollerBil
skodaDark
vw
Tokens
Tokens allow you to programatically look up css variables based on our design token structure, in a type safe manner.
Example:
import { tokens } from "@moller/gnist-themes/tokens.css.js";
const color = tokens.color.primary; // "var(--moller-color-primary)"
const size = tokens.size.xxl; // "var(--moller-size-xxl)"
More usefully, these can be interpolated directly in a styled-components
or vanilla-extract
style.
Atoms
Atoms define a set of atomic css classes which can be looked up at runtime. This allows reuse of frequently used basic styling across components and apps. As a bonus, things like colors, sizes and spacing are defined in terms of our design tokens.
The atoms
function simply returns a string containing the css class names required.
Note: The current set of atoms is quite small. More atoms will be added as components are reimplemented. You can also suggest useful atoms to the design system team on Slack or Teams!
Example:
import { atoms } from "@moller/gnist-themes/atoms.css.js";
export const DangerComponent = ({ children }: PropsWithChildren) => (
<div
className={atoms({
display: "flex",
backgroundColor: "error-container", // refers to a color token
paddingX: "xs", // refers to a spacing token
})}
>
{children}
</div>
);
Colors
The colors
module exports helpers for setting colors.
Box colors
boxColors
simply retrieves the correct atoms for setting backgroundColor
, color
, and borderColor
for a given color type.
E.g. for the color primary
we want primary
background-color, and on-primary
color & border-color.
Example:
import { style } from "@vanilla-extract/css";
import { tokens } from "@moller/gnist-themes/tokens.css.js";
import { boxColors } from "@moller/gnist-themes/colors.css.js";
const box = style([
boxColors["primary-container"],
{ borderWidth: tokens.stroke.medium },
]);
Typography
The typography
module exports helpers for setting correct typography styles (responsiveTypography
and densityTypography
),
and a css class for setting global text styles.
Responsive typography
responsiveTypography
helps you retrieve css classes for the typography styles that change between small/medium/large based on viewport size.
It is a record which lets you look up the class names to set for the given typography style. It uses atoms
under the hood.
Some examples:
import { style } from "@vanilla-extract/css";
import { atoms } from "@moller/gnist-themes/atoms.css.js";
import { responsiveTypography } from "@moller/gnist-themes/typography.css.js"
const someStyle = style([
atoms({ display: "flex", padding: "xs" }),
responsiveTypography.lead,
])
// or inline
<div className={responsiveTypography.description}>{children}</div>
Density typography
densityTypography
helps you create a recipe (for use with @vanilla-extract/recipe
) for the given typography styles, which has the density: "default" | "compact"
variants,
and sets the correct css classes. It uses atoms
under the hood.
It is a record which, when given a typography style, returns an object with the variants
prop which is suitable for merging into a recipe.
Example:
import { recipe } from "@vanilla-extract/recipes";
import { atoms } from "@moller/gnist-themes/atoms.css.js";
import { densityTypography } from "@moller/gnist-themes/typography.css.js";
const action = recipe({
base: atoms({ display: "block" }),
...densityTypography.action,
});
const className = recipe({ density: "default" });
Combining with other variants:
import { recipe } from "@vanilla-extract/recipes";
import { atoms } from "@moller/gnist-themes/atoms.css.js";
import { densityTypography } from "@moller/gnist-themes/typography.css.js";
const box = recipe({
base: [atoms({ display: "block" })],
variants: {
color: {
primary: boxColors["primary-container"],
secondary: boxColors["secondary-container"],
},
...densityTypography.notice.variants,
},
defaultVariants: { color: "primary", density: "default" },
});
const className = box({ color: "secondary", density: "compact" });
Global text styles
These should be added to the root element (e.g. <body>
) in order to set the default typography styles and colors.
Technically, this is a list of class names.
Usually, you would also apply the theme to this same element, as variables need to be defined for the styles to work.
Example of setting both:
import { bilholdLight } from "@moller/gnist-themes/themes.css.js";
import { globalTextStyles } from "@moller/gnist-themes/typography.css.js";
document.body.classList.add(bilholdLight);
globalTextStyles.forEach((c) => {
document.body.classList.add(c);
});
// In a Next layout you could rather do something like this
//...more layout
<body className={classNames(bilholdLight, globalTextStyles)}>{children}</body>;
//...more layout
Setup
The library is based on vanilla-extract, which allows pre-compiling the styles to static css files. However, in our
case we defer compilation to the consuming application. This allows the consumer to compose the tokens and atoms defined
here in their own vanilla-extract
based styles.
This means a compilation step for vanilla-extract
must be added to your build configuration.
Vite
npm install -D @vanilla-extract/vite-plugin @vanilla-extract/esbuild-plugin
Your vite.config.ts
might look like this:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";
import { vanillaExtractPlugin as veEsbuildPlugin } from "@vanilla-extract/esbuild-plugin";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), vanillaExtractPlugin()],
optimizeDeps: {
esbuildOptions: {
// Handle vanilla-extract .css.js files during Vite dev mode optimization
// This prevents error "Styles were unable to be assigned to a file." in dev mode
// See https://github.com/vanilla-extract-css/vanilla-extract/discussions/1051
plugins: [veEsbuildPlugin({ runtime: true })],
},
},
});
Next.js
npm install -D @vanilla-extract/next-plugin
Your next.config.mjs
might look like this
import { createVanillaExtractPlugin } from "@vanilla-extract/next-plugin";
const withVanillaExtract = createVanillaExtractPlugin();
/** @type {import('next').NextConfig} */
const nextConfig = {
// ...your configuration here...
};
export default withVanillaExtract(nextConfig);
29 days ago
1 month ago
1 month ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
3 months ago
4 months ago
4 months ago
6 months ago