CSS setup and structure#
Import Eufemia's styles once, choose the brand, then use the package structure below when integrating with existing CSS.
Importing CSS#
To include the packages dnb-ui-core, ui-theme-basis and ui-theme-components in a Node.js based environment (given you have a CSS loader in place), do this:
Style package structure: The theme package ui-theme-components includes both component and Eufemia Forms styles (Field, Value, Form layout, Wizard, etc.).
// This includes the `dnb-ui-core`, `ui-theme-components` and `ui-theme-basis`import '@dnb/eufemia/style'
Select a theme#
The above import is a shorthand for the DNB main theme. It is equivalent to the following import:
// This is identical to `import '@dnb/eufemia/style'`import '@dnb/eufemia/style/core'import '@dnb/eufemia/style/themes/ui'
To import another theme, replace the second import:
// This imports the sbanken theme insteadimport '@dnb/eufemia/style/core'import '@dnb/eufemia/style/themes/sbanken'
Runtime theme switching#
If your application needs to switch theme at runtime, preload the relevant theme CSS files and update the active theme through your application state.
Importing styles from within JavaScript#
import '@dnb/eufemia/style/dnb-ui-core.min.css'import '@dnb/eufemia/style/themes/ui/ui-theme-components.min.css'import '@dnb/eufemia/style/themes/ui/ui-theme-basis.min.css'
Importing styles from within CSS#
@import url('@dnb/eufemia/style/dnb-ui-core.min.css');@import url('@dnb/eufemia/style/themes/ui/ui-theme-components.min.css');@import url('@dnb/eufemia/style/themes/ui/ui-theme-basis.min.css');
Legacy import#
The legacy import import '@dnb/eufemia/style/basis' scopes global css so it does not affect the whole page. But requires that
you place a wrapper element with class .dnb-core-style around all Eufemia elements. And may causes some css specificity issues.
If possible, it should be replaced with import '@dnb/eufemia/style/core' that attaches the same css to the body tag instead.
Read more about how to deal with existing styles.
import '@dnb/eufemia/style/basis' // replaced by '@dnb/eufemia/style/core'import '@dnb/eufemia/style/themes/ui'
Single Component only#
It is possible to import a single CSS Style of a single component at once:
// Imports the core css for the themeimport '@dnb/eufemia/style/dnb-ui-core.min.css'import '@dnb/eufemia/style/themes/ui/ui-theme-basis.min.css'// Imports only the Button CSS and Main DNB Themeimport '@dnb/eufemia/components/button/style/dnb-button.min.css'import '@dnb/eufemia/components/button/style/themes/dnb-button-theme-ui.min.css'
Main Packages#
- dnb-ui-core: includes the DNB Main Styles like helper classes, HTML and body reset (normalize).
- We have an alternative package, dnb-ui-basis, that can be used if your page has other CSS that conflicts with Eufemia. It does the same but adds resets to the
.dnb-core-styleclass instead of thebodyelement, so Eufemia styling can be scoped to just a part of your page. NB: requires a.dnb-core-stylewrapper class.
- We have an alternative package, dnb-ui-basis, that can be used if your page has other CSS that conflicts with Eufemia. It does the same but adds resets to the
For black paper output and pagination, import the optional print styles after your core and brand styles and apply .dnb-print to printable content. This single class supplies pure black text, borders and form outlines without CSS backgrounds in either screen color scheme. Follow the printable apps guide to handle app navigation and page layout. Add the separate document styles with .dnb-document when you also need document typography and spacing.
Theme Packages#
- ui-theme-basis: includes css variables, fonts, optional class selectors for elements, optional default spacing, default typography.
- ui-theme-components: includes all the styles for components and Eufemia Forms (Field, Value, Form layout, Wizard, etc.). Importing
@dnb/eufemia/styleorui-theme-componentsgives you both component and Forms styles. - dnb-ui-components: component styles only.
Additional Theme Packages#
- ui-theme-properties: includes only the CSS Custom Properties. You can find this package inside
/style/themes/ui/.- There is also a JavaScript file containing the same properties.
- ui-theme-extensions: includes all the styles (and themes) for extensions (not shown in the Diagram).
All the CSS packages are ready to use, minified CSS files. You will find the main style here: @dnb/eufemia/style/dnb-ui-core.min.css
Individual styles#
Additionally, it is also possible to import a style and theme for every single component separately. You can find the styles here: @dnb/eufemia/components/{button}/style/dnb-{button}.min.css. Read more about how to import a single-component style.
CSS Structure Diagram#
The following Diagram gives an overall overview how the packages are structured.
How to deal with existing styles#
The dnb-ui-core package includes some styles that affect the global scope (<body> reset). To avoid interference with existing styles, such as a header or menu, you could use only the dnb-ui-basis package in combination with other packages like ui-theme-basis and ui-theme-components.
Core Style#
To have the Eufemia Core styles inside a wrapper anyway, simply use the following helper class: .dnb-core-style:
<body><p>I'm not Eufemia</p><!-- Wrapper to have correct Eufemia css reset and styles --><div id="app" class="dnb-core-style"><h1 class="dnb-h--xx-large">I have an Eufemia Style</h1><p class="dnb-p">👉 Me as well</p></div></body>
CSS Specificity#
Element resets use :where(.dnb-core-style) for their wrapper selector. A component class can override defaults such as font size, line height, and margins without adding .dnb-core-style to its selector.
Some typography and spacing rules still include .dnb-core-style in their selectors. When overriding those rules, you may need to include it to match their specificity.
For Styled Components you do it this way:
import { P } from '@dnb/eufemia'const MyElement = styled(P)`.dnb-core-style & {margin-top: 3rem;}color: var(--color-sky-blue);`
In CSS you simply do it this way:
.dnb-core-style .my-element {margin-top: 3rem;color: var(--color-sky-blue);}
Spacing for Articles#
To ensure more flexibility in styling, all margins/spacings are reset to zero. However, we often need default spacing defined, such as margin on HTML elements like headings or paragraphs.
To use the default DNB spacings, define a CSS class called .dnb-spacing.
HTML elements inside this container will then have default spacing. This is especially helpful for article-like pages.
<article class="dnb-spacing"><!-- DNB spacings --><h1 class="dnb-h--xx-large">e.g., I now have Eufemia spacing (margin)</h1><p class="dnb-p">👉 Me as well</p></article>
The styles for .dnb-spacing are included in the package: ui-theme-basis.
For more details, check out the source file: spacing.scss.
A list of all CSS properties (variables)#
Beside the portal documentation with related tables and additional information, you may have a look at the CSS file containing the custom properties (CSS variables), as well as a JavaScript file that is auto-generated from the CSS data.
The UI theme's properties.scss keeps concrete rem values synced from the Figma export and can be imported on its own. Each theme loads its generated typography values through theme-mapping.scss. The generated properties.js files include the values for each brand. Font sizes and line heights do not change in dark mode. None of these imports requires a separate token stylesheet.
Access CSS properties (variables) in JavaScript#
import properties from '@dnb/eufemia/style/themes/ui/properties.js'const seaGreenColor = properties['--color-sea-green']const basisFontSize = properties['--font-size-basis']
Using Tailwind with Eufemia#
Tailwind can act as a utility layer on top of (or beside) Eufemia. Keep Eufemia as the single source of tokens, colors, spacing, typography, CSS reset, etc.
Guidelines:
- Keep Eufemia resets: use either
@dnb/eufemia/style(global) or the scoped combobasis + themes. - Disable Tailwind's
preflight(its reset) to avoid collisions. - Let Tailwind generate utilities and its theme layer.
You can import the pre-generated properties-tailwind.css files that contain all Eufemia CSS custom properties in Tailwind-compatible format:
/* Import Tailwind layers */@layer eufemia, theme, utilities;/* Import Eufemia styles */@import '@dnb/eufemia/style/dnb-ui-core.min.css' layer(eufemia);@import '@dnb/eufemia/style/themes/ui/ui-theme-components.min.css'layer(eufemia);@import '@dnb/eufemia/style/themes/ui/ui-theme-basis.min.css'layer(eufemia);/* Import Tailwind base, theme and utilities layers */@import 'tailwindcss/theme.css' layer(theme);@import 'tailwindcss/utilities.css' layer(utilities);/* Import Eufemia properties in Tailwind format */@import '@dnb/eufemia/style/themes/ui/properties-tailwind.css'layer(utilities);/* Import Eufemia design tokens in Tailwind format */@import '@dnb/eufemia/style/themes/ui/tokens-tailwind.css' layer(utilities);
Tailwind-Compatible CSS Variables#
The properties-tailwind.css files contain Eufemia's CSS custom properties transformed to be compatible with Tailwind CSS. This transformation makes Eufemia properties available as Tailwind theme variables.
Available Namespaces#
The following CSS variable namespaces are available in Tailwind-compatible format:
--color-*- Color variables (e.g.,--color-black,--color-sea-green,--color-sky-blue)--font-*- Font family variables (e.g.,--font-default,--font-heading) - converted from--font-family-*--font-weight-*- Font weight variables (e.g.,--font-weight-default,--font-weight-medium)--text-*- Font size variables (e.g.,--text-small,--text-large) - converted from--font-size-*--leading-*- Line height variables (e.g.,--leading-small,--leading-medium) - converted from--line-height-*--spacing-*- Spacing variables (e.g.,--spacing-small,--spacing-large)--shadow-*- Box shadow variables (e.g.,--shadow-default,--shadow-sharp)--ease-*- Transition timing function variables (e.g.,--ease-default) - converted from--easing-*--breakpoint-*- Responsive breakpoint variables (e.g.,--breakpoint-small,--breakpoint-large) - converted from--layout-*
Brand-Specific Variables#
For brand-specific properties, variables are transformed to include the brand identifier after the namespace.
For Sbanken, the following namespaces are available:
--color-sb-*- (e.g.,--color-sb-purple,--color-sb-green)--font-weight-sb-*- (e.g.,--font-weight-sb-default,--font-weight-sb-medium)--text-sb-*- (e.g.,--text-sb-small,--text-sb-large)--spacing-sb-*- (e.g.,--spacing-sb-small,--spacing-sb-large)--shadow-sb-*- (e.g.,--shadow-sb-small,--shadow-sb-medium)--ease-sb-*- (e.g.,--ease-sb-default)--breakpoint-sb-*- (e.g.,--breakpoint-sb-small,--breakpoint-sb-large)
This makes Eufemia properties directly usable with Tailwind's utility classes while maintaining brand-specific variants.
Semantic Design Tokens#
In addition to the theme properties above, Eufemia provides design tokens via tokens-tailwind.css. Color tokens express semantic roles (e.g., "background for an action element") and reference foundation colors. Radius tokens provide brand values.
The --token- prefix from tokens.scss is stripped. Color tokens map to Tailwind's --color-* namespace:
--color-background-*- Background colors (e.g.,--color-background-action,--color-background-error-subtle)--color-text-*- Text colors (e.g.,--color-text-neutral,--color-text-action)--color-icon-*- Icon colors (e.g.,--color-icon-neutral,--color-icon-action)--color-stroke-*- Border/stroke colors (e.g.,--color-stroke-action,--color-stroke-error)--color-decorative-*- Decorative colors (e.g.,--color-decorative-first-base,--color-decorative-second-muted)--color-component-*- Component-specific tokens (e.g.,--color-component-button-background-action)
This allows usage like:
<div class="bg-background-action text-text-neutral border-stroke-action">Styled with semantic tokens</div>
The tokens-tailwind.css files are available for all themes: ui, sbanken, and carnegie.
For Tailwind font-size and line-height utilities, use the --text-* and --leading-* values from properties-tailwind.css.
For example, leading-basis uses --leading-basis, the Tailwind equivalent of --line-height-basis.
Z-Index Scale#
Eufemia defines a centralized z-index scale using CSS Custom Properties. This ensures consistent layering across all components and makes the stacking order discoverable and adjustable from a single place.
The following tokens are defined on :root and control the stacking order of Eufemia components:
| Token | Default value | Usage |
|---|---|---|
--z-index-section | 1 | Content sections |
--z-index-dropdown | 100 | Inline dropdowns, drawer-lists |
--z-index-popover | 1000 | Popovers, floating elements |
--z-index-tooltip | 1100 | Tooltips (always above popovers) |
--z-index-modal | 3000 | Dialogs, drawers, full overlays |
Each component also exposes its own CSS Custom Property that references the centralized token. This preserves backward compatibility for consumers who override the component-level property directly.
| Component | Component property | References |
|---|---|---|
| Section | --section-z-index | var(--z-index-section) |
| Popover | --popover-z-index | var(--z-index-popover) |
| Tooltip | --tooltip-z-index | var(--z-index-tooltip) |
| Modal | --modal-z-index | var(--z-index-modal) |
You can override the global z-index scale to adjust the layering for your application:
:root {--z-index-modal: 9999;}
Or override a single component's z-index:
:root {--modal-z-index: 5000;}
Known styling and CSS issues#
- Safari, both on mobile and desktop, has a problem where we combine
border-radiuswith the usage ofinsetin abox-shadow. The solution for now is to not useinset, which results in an outer border. This is not ideal as we don't follow the UX guidelines for these browsers. We have an SCSS mixin handling this for us:@mixin fakeBorder.
Removing unused CSS (beta)#
Beta: The Vite plugin, style manifest, and lower-level optimizer helpers are beta APIs and may change in a future release.
Eufemia's JavaScript is tree-shakeable, but import '@dnb/eufemia/style' includes every component's CSS. Eufemia components also compose classes at runtime, so removing individual selectors based only on literal source text can delete styles that are actually needed.
Vite plugin (recommended)#
Install PurgeCSS as a development dependency, then add the Eufemia CSS optimizer after your framework plugin:
npm install --save-dev purgecss# oryarn add --dev purgecss# orpnpm add --dev purgecss
// vite.config.tsimport { defineConfig } from 'vite'import react from '@vitejs/plugin-react'import { eufemiaCssOptimizer } from '@dnb/eufemia/style/vite-plugin.js'export default defineConfig({plugins: [react(), eufemiaCssOptimizer()],})
PurgeCSS is an optional peer dependency of @dnb/eufemia, so applications that do not use the optimizer do not install it. No separate PurgeCSS configuration or build command is required after installing it. Run your normal production build:
yarn build
The plugin scans src once for Eufemia imports, expands every detected component with its style and render dependencies, and removes unused component blocks during Vite's CSS transform pipeline. Vite therefore calculates source maps, minification, filenames, and content hashes from the optimized CSS.
The beta style manifest is generated when the Eufemia package is prepared and ships as @dnb/eufemia/style/style-manifest.json; it is not committed as source. Consumers load the manifest from the installed package automatically.
The optimizer only processes Eufemia's aggregate component, fragment, extension, and Forms styles. It leaves application CSS unchanged, and preserves dnb-ui-core and theme basis CSS because they contain global resets, accessibility rules, fonts, design tokens, shared element styles, and other foundation styles that cannot be safely inferred from component imports. The build prints the detected components and before/after Eufemia CSS size.
Use sources when application code lives outside src, extensions when it uses other source-file extensions, or components when usage is selected dynamically and cannot be detected statically:
plugins: [react(),eufemiaCssOptimizer({sources: ['app', 'packages/shared'],extensions: ['ts', 'tsx', 'mdx'],// Or manage detection explicitly:// components: ['autocomplete', 'button'],}),]
Automatic detection covers named and aliased imports, deep imports, namespace imports, direct re-exports, public element aliases, and Eufemia Forms. Forms detection includes Field/Value, RegisteredField/RegisteredValue, member barrels, the compatibility default export from extensions/forms/Forms, namespace usage such as Forms.Field.Upload, straightforward destructuring, and Forms deep imports. Member-specific dependencies are retained only for the members in use. If a recognized Forms namespace is used through an unresolved computed or indirect member pattern, the optimizer conservatively keeps all member-specific Forms dependencies.
Detection scans files on disk before the build. It cannot follow components through local multi-hop re-export chains, arbitrary assignment chains, computed component names, or source generated only in a virtual module. Include generated files in sources when they exist on disk, and use components to explicitly retain components for other dynamic or generated usage.
Advanced: non-Vite builds#
For other build systems, createSafelist and protectWhereSelectors are available as lower-level integration APIs. createSafelist loads the shipped manifest, detects imported components, expands their transitive dependencies, and returns PurgeCSS-compatible greedy patterns. protectWhereSelectors preserves Eufemia's nested :where(:not(…)) and :is(:not(…)) rules, which PurgeCSS cannot retain through a safelist alone. Always apply it to Eufemia's aggregate CSS before passing the CSS to PurgeCSS:
import { readFile } from 'node:fs/promises'import { createRequire } from 'node:module'import { PurgeCSS } from 'purgecss'import {createSafelist,protectWhereSelectors,} from '@dnb/eufemia/style/optimizer.js'const require = createRequire(import.meta.url)const { greedy } = createSafelist({ sources: ['src'] })const cssPath =require.resolve('@dnb/eufemia/style/themes/ui/ui-theme-components.min.css')const eufemiaCss = await readFile(cssPath, 'utf8')const [result] = await new PurgeCSS().purge({content: ['src/**/*.{ts,tsx,js,jsx,mdx}'],css: [{ raw: protectWhereSelectors(eufemiaCss, greedy) }],safelist: { greedy },})
The sources option controls Eufemia import detection, while PurgeCSS's content option controls literal selector scanning. Write or otherwise pass result.css to the rest of your build. Keep dnb-ui-core and theme basis CSS outside the purge input.
Always include the .js extension when importing the published plugin or optimizer helper.