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
| Breakpoint | Width range (px) | Start width | Grid columns |
|---|---|---|---|
xs | 0 … 671 | 0 | 4 |
sm | 672 … 1055 | 672 | 8 |
md | 1056 … 1311 | 1056 | 16 |
lg | 1312 … 1583 | 1312 | 16 |
xl | 1584 … ∞ | 1584 | 16 |
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).
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
| Flag | Meaning |
|---|---|
xs / sm / md / lg / xl | exactly one is active — the current breakpoint |
smUp / mdUp / lgUp / xlUp | width ≥ the breakpoint's start width ("and up") |
smDown / mdDown / lgDown / xlDown | the breakpoint and everything below it ("and down") |
name | name of the current breakpoint |
mobile | mobile mode (based on mobileBreakpoint) |
mobileBreakpoint | current mobile mode threshold |
width / height | viewport size, px |
thresholds | current threshold map |
platform | platform/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
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:
<!-- 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:
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:
@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"
.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"
.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
.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 forcds-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$nameis 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 mixin | Width range |
|---|---|---|
smUp | cds-breakpoint-up(sm) | ≥ 672 |
mdUp | cds-breakpoint-up(md) | ≥ 1056 |
lgUp | cds-breakpoint-up(lg) | ≥ 1312 |
xlUp | cds-breakpoint-up(xl) | ≥ 1584 |
smDown | cds-breakpoint-down(sm) | < 1056 |
mdDown | cds-breakpoint-down(md) | < 1312 |
lgDown | cds-breakpoint-down(lg) | < 1584 |
xlDown | cds-breakpoint-down(xl) | every width |
Important (migrating from CDS 3). In CDS 3 the semantics of
cds-breakpoint-downin SCSS differed from JS:down('sm')meant "strictly belowsm" (onlyxs), whereassmDownin JS means "smand 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 thedown()argument one step down (olddown('md')⇒ newdown('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.
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.
| Field | Meaning |
|---|---|
android | an Android device |
ios | an iOS device (iPhone / iPad / iPod) |
cordova | the app runs in Cordova |
electron | the app runs in Electron |
chrome | Chrome browser |
edge | Edge browser |
firefox | Firefox browser |
opera | Opera browser |
win | Windows OS |
mac | macOS |
linux | Linux OS |
touch | the device supports touch |
ssr | the value was computed on the server (SSR) |
Example: a class on the app root
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
userAgentis unreliable (the string can be spoofed, and new browsers may not be recognized). Useplatformfor 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 windowand 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.