Skip to content

Display & Platform ​

Central Design System exposes reactive information about the screen (breakpoints, viewport size, mobile mode) and the platform (browser, OS, device type) through a single composable — useDisplay. Breakpoints are the same in JavaScript and in styles (the cds-breakpoint-* SCSS mixins), so responsive logic in script and CSS always agrees.

Breakpoints ​

BreakpointWidth range (px)Start widthGrid columns
xs0 … 67104
sm672 … 10556728
md1056 … 1311105616
lg1312 … 1583131216
xl1584 … ∞158416

The values have a single source:

  • JS — defaultDisplayOptions.thresholds (src/composables/useDisplay/internal/useDisplay.constants.ts);
  • SCSS — $grid-breakpoints (src/styles/utilities/grid/_config.scss).

In JavaScript — useDisplay ​

The useDisplay composable returns the reactive state of the screen and the platform. It works out of the box as long as the application is initialized through createCds (which provides cds:display).

ts
import { useDisplay } from '@central-design-system/components';

const display = useDisplay();

display.width.value; // current viewport width, px
display.name.value; // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
display.mobile.value; // true when width < mobileBreakpoint ('sm' by default)

Available flags ​

FlagMeaning
xs / sm / md / lg / xlexactly one is active — the current breakpoint
smUp / mdUp / lgUp / xlUpwidth ≥ the breakpoint's start width ("and up")
smDown / mdDown / lgDown / xlDownthe breakpoint and everything below it ("and down")
namename of the current breakpoint
mobilemobile mode (based on mobileBreakpoint)
mobileBreakpointcurrent mobile mode threshold
width / heightviewport size, px
thresholdscurrent threshold map
platformplatform/browser flags (see Platform)
update()force a recalculation of size and platform

For example, mdUp is true when width ≥ 1056, and smDown when width < 1056 (that is, it covers the xs and sm ranges).

Example in a component ​

tsx
import { useDisplay } from '@central-design-system/components';

const { mobile, mdUp } = useDisplay();

defineRender(() => (
  <div class={{ 'is-mobile': mobile.value }}>
    {mdUp.value ? <DesktopLayout /> : <MobileLayout />}
  </div>
));

Mobile mode at the prop level ​

Components that mix in defineDisplayProps() accept the mobile and mobileBreakpoint props — use them to override the mobile mode threshold locally:

html
<!-- Mobile mode kicks in below 1056px (md) instead of the global 'sm' -->
<CdsSlideGroup mobile-breakpoint="md" />

Overriding the thresholds globally ​

Thresholds and mobileBreakpoint are configured at initialization:

ts
import { createCds } from '@central-design-system/components';

app.use(createCds({
  display: {
    mobileBreakpoint: 'md',
    thresholds: { xs: 0, sm: 600, md: 960, lg: 1280, xl: 1920 }
  }
}));

If you change the thresholds in JS, keep them in sync in SCSS ($grid-breakpoints) — otherwise responsiveness in script and styles will drift apart.

In SCSS — the cds-breakpoint-* mixins ​

The mixins come from a public entry point:

scss
@use '@central-design-system/components/styles/breakpoints' as breakpoint;

In plain Dart Sass (without a bundler), use the pkg scheme and the Node importer: @use 'pkg:@central-design-system/components/styles/breakpoints' as breakpoint;.

cds-breakpoint-up($name) — "and up" ​

scss
.card {
  display: none;

  // applies at min-width: 1056px (md and up)
  @include breakpoint.cds-breakpoint-up(md) {
    display: block;
  }
}

cds-breakpoint-down($name) — "and down" ​

scss
.sidebar {
  // applies at max-width: 1055.98px — that is, on sm and xs
  @include breakpoint.cds-breakpoint-down(sm) {
    display: none;
  }
}

cds-breakpoint-down('<name>') covers the breakpoint itself and everything below it (the upper bound is the start of the next breakpoint minus 0.02px). No media query is generated for the largest breakpoint (xl) — "xl and down" is every width.

cds-breakpoint-between($lower, $upper) — a range ​

scss
.banner {
  // min-width: 672px and max-width: 1056px
  @include breakpoint.cds-breakpoint-between(sm, md) {
    padding: 16px;
  }
}

Both mixins also accept a number instead of a name: cds-breakpoint-up(900px).

Other mixins and functions ​

  • cds-breakpoint($name) — an alias for cds-breakpoint-up.
  • cds-largest-breakpoint — media query for the largest breakpoint.
  • breakpoint-next($name) / breakpoint-prev($name) — the neighboring breakpoint.
  • is-smallest-breakpoint($name) — whether $name is the smallest one.
  • largest-breakpoint-name() — name of the largest breakpoint.

JS ↔ CSS correspondence ​

The useDisplay flags and the mixins' media queries cover identical width ranges — that's what lets you write consistent responsive logic in script and styles.

JS (useDisplay)SCSS mixinWidth range
smUpcds-breakpoint-up(sm)≥ 672
mdUpcds-breakpoint-up(md)≥ 1056
lgUpcds-breakpoint-up(lg)≥ 1312
xlUpcds-breakpoint-up(xl)≥ 1584
smDowncds-breakpoint-down(sm)< 1056
mdDowncds-breakpoint-down(md)< 1312
lgDowncds-breakpoint-down(lg)< 1584
xlDowncds-breakpoint-down(xl)every width

Important (migrating from CDS 3). In CDS 3 the semantics of cds-breakpoint-down in SCSS differed from JS: down('sm') meant "strictly below sm" (only xs), whereas smDown in JS means "sm and down". In CDS 4 the CSS is aligned with JS: down('<name>') now always means "<name> and down". If you are porting styles from CDS 3, shift the down() argument one step down (old down('md') ⇒ new down('sm')). See the Migration Guide.

Platform ​

useDisplay().platform is a reactive object with device, OS and browser flags. Detection is based on navigator.userAgent and touch support.

ts
import { useDisplay } from '@central-design-system/components';

const { platform } = useDisplay();

if (platform.value.ios) {
  // special behavior for iOS
}

const isTouchDevice = platform.value.touch;

Fields of the platform object ​

Every field is a boolean.

FieldMeaning
androidan Android device
iosan iOS device (iPhone / iPad / iPod)
cordovathe app runs in Cordova
electronthe app runs in Electron
chromeChrome browser
edgeEdge browser
firefoxFirefox browser
operaOpera browser
winWindows OS
macmacOS
linuxLinux OS
touchthe device supports touch
ssrthe value was computed on the server (SSR)

Example: a class on the app root ​

tsx
import { useDisplay } from '@central-design-system/components';

const { platform } = useDisplay();

defineRender(() => (
  <div
    class={{
      'platform--ios': platform.value.ios,
      'platform--android': platform.value.android,
      'platform--touch': platform.value.touch
    }}
  >
    <slot />
  </div>
));

Careful with user-agent sniffing. Detecting the browser or OS from userAgent is unreliable (the string can be spoofed, and new browsers may not be recognized). Use platform for progressive enhancement — say, a fix specific to iOS Safari — not as the only gate in front of a feature. For feature detection, prefer capability checks ('ontouchstart' in window and the like).

SSR ​

During server-side rendering userAgent is unavailable, so platform.ssr === true and the rest of the flags are false. After hydration the values can be recalculated by calling display.update() (which recalculates both size and platform). Initial sizes for SSR are set through the second argument of createDisplay(options, ssr) or the ssr option in createCds.