Global configuration
CDS 4 lets you set default prop values — globally, for a specific component, or only inside a given subtree. You describe "what buttons look like in our product" once and stop repeating the same props in every template.
Setup
Default values are set through the defaults option of createCds. The global key affects every component; a key named after a component affects only that one.
import { createApp } from 'vue';
import '@central-design-system/components/styles';
import { createCds, components, directives } from '@central-design-system/components';
import App from './App.vue';
const cds = createCds({
components,
directives,
defaults: {
global: {
size: 'sm'
},
CdsButton: {
appearance: 'secondary'
},
CdsTextInput: {
hideDetails: 'auto'
}
}
});
createApp(App).use(cds);Important. Props in defaults are written in camelCase: hideDetails, not hide-details.
A component key is its name from defineOptions({ name }) — CdsButton, CdsTextInput, CdsCardActions and so on. Subcomponent names (CdsCardTitle, CdsListItem) work exactly like root component names.
Resolution order
For every prop, CDS takes the first value that applies:
- the prop explicitly passed in the template;
defaults.<ComponentName>.<prop>from the nearest defaults context;defaults.global.<prop>;- the default value declared in the component itself.
An explicitly passed prop always wins — defaults can never override the template.
Changing defaults at runtime
createCds returns an instance whose defaults live in a reactive ref. You can change them on the fly — every mounted component recalculates:
const cds = createCds({ components, directives });
// for example, turn on compact mode based on a user setting
cds.defaults.value = {
...cds.defaults.value,
global: { size: 'sm' }
};Contextual defaults
Defaults can be set for components nested inside another component. For example, to make every button inside a card secondary by default:
createCds({
defaults: {
CdsCard: {
CdsButton: { appearance: 'secondary' }
}
}
});The rule is simple: a capitalized key is the name of a nested component, a lowercase key is a prop of the current component. Nesting is not limited to one level:
createCds({
defaults: {
CdsCard: {
CdsCardActions: {
CdsButton: { appearance: 'tertiary', size: 'sm' }
}
}
}
});The library uses the same mechanism internally so that grouping components can hand their settings down to their children:
CdsButtonGroupsets theappearance,color,baseColor,sizeprops on nestedCdsButtons;CdsTagGroupsets thecolor,activeColor,disabled,filterprops onCdsTag;CdsSelectionControlGrouppasses settings to the controls, with the target component name set by thedefaultsTargetprop (CdsCheckbox,CdsRadioand so on);CdsTabs,CdsSteps,CdsList,CdsBreadcrumbs,CdsButtonToggle,CdsExpandablePanels,CdsOtpInputwork the same way.
That's why <CdsButtonGroup size="sm"> makes every button inside small — you don't have to set size on each of them.
Global class and style
Besides props, a component's defaults can carry class and style. It's a convenient way to apply product-specific rules to every instance of a component without touching the templates:
createCds({
defaults: {
CdsButton: {
class: 'my-button'
}
}
});Any value Vue accepts works — a string, an object, an array:
createCds({
defaults: {
CdsButton: {
style: [{ textTransform: 'none' }]
}
}
});Unlike ordinary props, class and style are not overwritten by the template but merged with it. So <CdsButton class="mt-2"> gets both my-button and mt-2.
Important. class and style only work under a specific component's key. In global they are ignored.
Aliases
An alias is the same component registered under a different name with its own defaults. Aliases are a handy way to assemble "product" variants of components.
import { createCds, components, directives, CdsButton } from '@central-design-system/components';
const cds = createCds({
components,
directives,
aliases: {
CdsButtonPrimary: CdsButton,
CdsButtonDanger: CdsButton
},
defaults: {
CdsButtonPrimary: {
appearance: 'primary',
class: 'my-button--primary'
},
CdsButtonDanger: {
appearance: 'primary',
color: 'red',
class: 'my-button--danger'
}
}
});Aliases are registered globally, so you can use them in templates right away:
<template>
<CdsButtonPrimary>Save</CdsButtonPrimary>
<CdsButtonDanger class="ml-2">Delete</CdsButtonDanger>
</template>Important. An alias does not inherit the base component's defaults. If the configuration has defaults.CdsButton, it will not apply to <CdsButtonPrimary> — defaults are looked up by the alias name. Move shared settings into global or duplicate them under the alias key.
Defaults for part of the application
To set defaults for a subtree rather than globally, use the CdsDefaultsProvider component. The Controls panel in Storybook is built on it too: the values from the panel are applied to the example's components as defaults.
<script setup lang="ts">
import { CdsButton, CdsDefaultsProvider } from '@central-design-system/components';
</script>
<template>
<CdsDefaultsProvider :defaults="{ CdsButton: { appearance: 'tertiary', size: 'sm' } }">
<CdsButton>Small tertiary button</CdsButton>
</CdsDefaultsProvider>
</template>| Prop | Type | Purpose |
|---|---|---|
defaults | DefaultsOptions | Defaults for the subtree; merged with the inherited ones by default |
scoped | boolean | Don't mix with the parent defaults — only the ones set here apply |
reset | number | string | Go N levels up the defaults chain |
root | boolean | string | Fall back to the root defaults from createCds; with a string — to a named group inside them |
disabled | boolean | Provide nothing — the subtree gets the parent defaults as they are |
The typical use for scoped is an "island" inside someone else's environment: enclosing groups (CdsButtonGroup, say) should not impose their settings on your block.
Your own components
Your application's own components can take part in the defaults system too. Pass their props through useDefaults:
<script setup lang="ts">
import { useDefaults } from '@central-design-system/components';
const _props = defineProps<{ foo?: string }>();
const props = useDefaults(_props, 'MyComponent');
</script>
<template>
<div>I am {{ props.foo }}</div>
</template>useDefaults takes two arguments, both optional:
props— the props object returned bydefineProps;name— the component name used to look up the key indefaults. If omitted, the name fromdefineOptions({ name })is used, and failing that, the file name.
Now the component is configurable from the shared configuration:
createCds({
defaults: {
MyComponent: { foo: 'bar' }
}
});Important. In the template, refer to props.foo, not foo. useDefaults returns a proxy over the props, and destructured defineProps variables bypass it:
-<div>I am {{ foo }}</div>
+<div>I am {{ props.foo }}</div>Nested defaults in your own components
To make your component hand defaults down to its children (the way CdsButtonGroup does), just call useDefaults() with no arguments — the nested keys from its defaults section go into the child context:
<script setup lang="ts">
import { useDefaults } from '@central-design-system/components';
useDefaults();
</script>
<template>
<div class="my-panel">
<slot />
</div>
</template>createCds({
defaults: {
MyPanel: {
CdsButton: { appearance: 'tertiary' }
}
}
});If the children's defaults depend on the component's props, use provideDefaults directly:
<script setup lang="ts">
import { provideDefaults } from '@central-design-system/components';
import { toRef } from 'vue';
const props = defineProps<{ size?: 'sm' | 'md' }>();
provideDefaults({
CdsButton: {
size: toRef(() => props.size)
}
});
</script>
<template>
<div class="my-toolbar">
<slot />
</div>
</template>Blueprint — a configuration preset
If several applications need the same set of options, extract it into a blueprint: a plain createCds options object that the rest of the configuration is layered on top of.
import { createCds, components, directives } from '@central-design-system/components';
import type { Blueprint } from '@central-design-system/components';
const productBlueprint: Blueprint = {
theme: { defaultTheme: 'b2bLight' },
defaults: {
CdsButton: { appearance: 'secondary' }
}
};
const cds = createCds({
blueprint: productBlueprint,
components,
directives,
// overrides the value from the blueprint
defaults: {
CdsButton: { size: 'sm' }
}
});Options are merged deeply (mergeDeep), so in the example above the buttons keep appearance: 'secondary' from the preset and gain size: 'sm'.