Skip to content

Styles, font and dark mode

Three things make the components look right: the stylesheet, the font and, if you want it, the dark theme.

Import @beyul/sulmu-ui/styles.css once, at the root of the app, before the app’s own CSS:

src/index.tsx
import '@beyul/sulmu-ui/styles.css'

The stylesheet is precompiled. It contains the design tokens as CSS variables (--su-colors-brand-600) and the classes of every variant of every component (.su-button). Nothing is generated at runtime.

In a Module Federation app, import it in the host only.

  • It has no CSS reset. It does not touch body, headings, links or any element that is not a library component. Components set their own margins and font.
  • It only sets box-sizing: border-box on its own elements (those with an su- class), so components size themselves the same way with or without a reset in the app.
  • Everything is inside CSS cascade layers (reset, base, tokens, recipes, utilities). Styles that are not in a layer always win over layered styles, so the app’s own CSS overrides the library without any specificity tricks. See Styling and extending.

The components use Plus Jakarta Sans. The library does not load it; the app does. The font token is "Plus Jakarta Sans Variable", "Plus Jakarta Sans", ui-sans-serif, system-ui, ..., so either name works.

With Fontsource (what the Tourism Suite shell uses):

Terminal window
npm install @fontsource-variable/plus-jakarta-sans
import '@fontsource-variable/plus-jakarta-sans'

Or with Google Fonts, in the HTML head:

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap"
rel="stylesheet"
/>

Library components set their own font family. To use the same font for the rest of the app:

body {
font-family: var(--su-fonts-sans);
color: var(--su-colors-fg-default);
background: var(--su-colors-bg-canvas);
}

The dark theme is switched on by data-theme="dark" on an ancestor of the components:

<html data-theme="dark">
document.documentElement.dataset.theme = isDark ? 'dark' : 'light'
  • Put the attribute on <html>. Dialogs, menus, selects, popovers, tooltips and toasts are rendered in a portal at the end of <body>, so an attribute on a wrapper inside the app would not reach them.
  • Only the semantic colours change. The colour ramps reverse (brand.100 becomes the darkest shade), and every purpose colour such as bg.surface follows.
  • The library does not read prefers-color-scheme. Decide in the app and set the attribute.

This site uses the same switch: the theme picker in the header sets data-theme on <html>, and every example follows it.

Using the tokens with Panda CSS (optional)

Section titled “Using the tokens with Panda CSS (optional)”

An app that uses Panda CSS for its own layout can share the tokens by adding the preset:

panda.config.ts
import { defineConfig } from '@pandacss/dev'
import { sulmuPreset } from '@beyul/sulmu-ui/preset'
export default defineConfig({
presets: ['@pandacss/preset-base', sulmuPreset],
prefix: 'su',
})

This is optional. Without Panda, use the CSS variables directly.

Next: build a first screen.