Skip to content

SASS variables ​

CDS 4 styles are written in SASS/SCSS, and part of the internal setup — the base reset, global typography, font, grid and breakpoints — is extracted into SASS variables flagged with !default. You can override them when building your application through the standard module configuration mechanism: @use '…' with (…).

SASS variables are the compile-time configuration layer. Colors, spacing and the rest of the component design tokens live in CSS variables (--cds-*) and are configured by themes at runtime — see Theming. Start with the theme; drop down to SASS variables when you need to change the base layer itself: reset, typography, grid.

Setup ​

Vite compiles SCSS out of the box — you only need the preprocessor itself:

bash
npm install -D sass-embedded
bash
pnpm add -D sass-embedded
bash
yarn add -D sass-embedded
bash
bun add -d sass-embedded

The classic sass works too, but sass-embedded is noticeably faster. To speed up large builds, opt into the modern compiler API:

ts
// vite.config.ts
export default defineConfig({
  css: {
    preprocessorOptions: {
      scss: { api: 'modern-compiler' }
    }
  }
});

Plugging in with a configuration ​

Normally the base styles are plugged in as ready-made CSS — import '@central-design-system/components/styles'. To override SASS variables, replace that import with your own SCSS file: in SCSS the @central-design-system/components/styles entry point resolves to the sources of the base layer (the sass export condition in package.json), and your configuration is applied at compile time.

diff
// main.ts
import { createApp } from 'vue';

- import '@central-design-system/components/styles';
+ import './styles/main.scss';
scss
// src/styles/main.scss
@use '@central-design-system/components/styles' with (
  $reset: false,
  $body-font-family: ('Inter', sans-serif)
);

The component styles themselves need nothing extra: they are precompiled and ship with the components' JS chunks — both with selective and with full registration (see Tree-shaking).

Wrap a value containing commas — $body-font-family with several fonts, for instance — in parentheses, otherwise SASS reads it as several arguments.

A settings file ​

Module configuration is applied once, on the first load. If you need the values not only in the entry point but also in your application's component styles, extract them into a separate settings file and use that everywhere:

scss
// src/styles/settings.scss
@forward '@central-design-system/components/styles/settings' with (
  $body-font-family: ('Inter', sans-serif),
  $grid-gutter: 24px
);
scss
// src/styles/main.scss
@use './settings'; // configures the CDS variables
@use '@central-design-system/components/styles'; // the base is built with them
html
<!-- Component.vue -->
<style lang="scss">
@use '@/styles/settings' as s;

.card {
  gap: s.$grid-gutter;
  font-family: s.$body-font-family;
}
</style>

Every <style> block is compiled separately, so "configure it in main.scss and read it straight from the package" won't work — a direct @use '@central-design-system/components/styles/settings' in a component would return the default values. A shared settings file guarantees identical values at every compilation point.

The styles/settings entry contains variables only and emits not a single byte of CSS — it's safe to include in any number of files.

Global variables ​

VariableDefaultWhat it does
$resettrueEmit the base layer's CSS reset
$legacy-resetfalseReset for compatibility with CDS 3 markup
$enable-typographytrueGlobal typography (h1–h6, .h1–.h6 and so on)
$body-font-familyvar(--cds-font-family), 'PT Root UI VF', …body font
$font-size-rootvar(--cds-base-unit) (16px)Root font size
$line-height-root1.5Root line height
$border-color-roottransparentDefault border color
$border-style-rootsolidDefault border style
$border-radius-rootvar(--cds-border-radius-200)Base corner radius
$size-scale4pxStep of the size scale
$sizes('xs', 'sm', 'md', 'lg', 'xl')Size names
$roundedmapRadius scale (sm, md, lg, xl, pill, circle, …)

Grid and breakpoints ​

VariableDefaultWhat it does
$prefix'cds'Prefix of the grid CSS classes
$flex-grid-columns16Number of flex grid columns (12 is supported too)
$grid-gutter32pxGrid gutter
$grid-gutter-condensed1pxGutter of the condensed grid
$grid-breakpointsmapBreakpoints: width, columns and spacing for xs–xl

The grid variables belong to the same settings module, while the breakpoint and grid mixins live in a separate styles/breakpoints entry — which emits no CSS either:

scss
@use '@/styles/settings' as s;
@use '@central-design-system/components/styles/breakpoints' as bp;

.sidebar {
  gap: s.$grid-gutter;

  @include bp.cds-breakpoint-up('md') {
    width: 320px;
  }
}

SASS breakpoints affect your styles and the grid mixins. The runtime breakpoints of useDisplay are configured separately, through the display option of createCds (see Display & Platform). The two sets match by default; when you change one, don't forget the other.

Component variables and vite-plugin ​

Without the plugin, @use … with configures the base layer only: component CSS is precompiled. The @central-design-system/vite-plugin plugin lifts that limitation — it recompiles the styles of every component you use with your settings file:

ts
// vite.config.ts
import cds from '@central-design-system/vite-plugin';

export default defineConfig({
  plugins: [
    vue(),
    cds({ styles: { configFile: 'src/styles/settings.scss' } })
  ]
});

The settings file is the same, but you have to forward styles/component-settings — the aggregate that includes both global and component variables:

scss
// src/styles/settings.scss
@forward '@central-design-system/components/styles/component-settings' with (
  $border-radius-root: 0,          // global
  $button-border-radius: 9999px    // component-level
);

With the plugin you don't need to change the @central-design-system/components/styles import in main.ts — it starts meaning "the configured base layer" on its own, and component styles are rebuilt on the fly. The plugin can also auto-import components from templates (autoImport: true by default), which makes registration in createCds({ components }) optional.

What SASS can't configure ​

  • Component-local variables ($button-border-radius and friends from styles/_CdsX.variables.scss). Unavailable without the plugin: component CSS is published precompiled, so its local variables are frozen at the moment the library was built. With @central-design-system/vite-plugin they are configured through styles/component-settings — the plugin recompiles the published SCSS sources of the components with your settings file (see above).
  • Turning off individual utility classes. Instead of a toggle map, pick an entry point by content: styles/core — layers, fonts and reset only; styles/common — plus typography and helpers.
  • CSS layers. @layer cds-* is always on: the layer order is declared in the base style layer, and ordinary application CSS (outside @layer) always beats the design system's styles — there is nothing to turn off here.

Pitfalls ​

  • Variables only in the settings file. Any styles or imports of regular style files inside settings.scss get duplicated into every file that includes it. Styles belong in main.scss; the settings file holds variables, mixins and functions.
  • Configure before the module's first load. @use … with fails with a module was already loaded error if the settings module has already been loaded without a configuration higher up the chain. Include the settings file first.
  • Build speed. With ready-made CSS there is nothing to compile; with a configuration the base layer is built together with the application. The volume is small, but the difference shows on cold builds — sass-embedded and api: 'modern-compiler' keep it to a minimum.
  • Resolution assumes a bundler. Bare imports such as @use '@central-design-system/components/styles' are resolved by Vite and webpack (sass-loader) through the sass export condition. In plain sass CLI without a bundler you'll have to point the paths at the files inside node_modules by hand.