OTTODesign System

Code

Best Practices

Storybook group: Development · Sidebar path: Development/Best Practices · Extracted 28.09.2026

Overview

Source: ./src/documentation/development/best-practices.mdx

Best practices

This page describes the rules you must follow when styling and using Otto Components. Adhering to these rules ensures that your code remains compatible across component updates and prevents unexpected visual regressions.

Skip to:

CSS variables

Otto Components expose a set of documented CSS custom properties that you can use to customize their appearance.

Only use documented CSS variables

Only override CSS variables that are explicitly listed in a component's documentation. These are the stable, public API for visual customization.

/* Correct: override a documented CSS variable */
oc-button-v1 {
  --oc-button-background-color: purple;
}
Never override design tokens

Design tokens (prefixed with --oc-base-, --oc-semantic-, or --oc-component-) are internal implementation details. Overriding them causes unintended side effects across many components and will break when tokens are updated.

/* Wrong: do not override design tokens */
oc-button-v1 {
  --oc-component-button-primary-background-color: purple;
  --oc-semantic-color-text-interactive: red;
  --oc-base-color-blue-100: green;
}

Why? Design tokens are shared across the entire OTTO ecosystem. Overriding them locally causes cascading side effects and breaks the visual contract between components.

Colors: CSS variables over attributes

When a component supports color customization, prefer setting colors through CSS variables rather than HTML attributes. CSS variables integrate with the browser's cascade and inheritance, which makes them the correct mechanism for theming and dynamic color changes.

Prefer CSS variables over color attributes

Some components expose deprecated color attributes (e.g. background-color, color, icon-color) for historical reasons. Always use the documented CSS variable equivalent instead.

<!-- Wrong: setting color via a deprecated attribute -->
<oc-icon-button-v3 background-color="#FFCCE2"></oc-icon-button-v3>

<!-- Correct: setting color via a CSS variable -->
<oc-icon-button-v3 style="--background-color: #FFCCE2"></oc-icon-button-v3>
/* Correct: applying color via CSS */
oc-icon-button-v3 {
  --background-color: #FFCCE2;
}

Note:

CSS variables participate in the cascade and can be overridden at any level of the DOM tree. This makes them suitable for theming, dark mode, and runtime style changes. Attribute-based color APIs are deprecated and will be removed in future versions.

Prefer design tokens over raw hex values

When specifying a color value — whether through a CSS variable, inline style, or any other mechanism — always use a design token instead of a raw hex value. Design tokens are semantic, consistent, and automatically adapt when the design system is updated.

/* Wrong: using a raw hex value */
oc-icon-button-v3 {
  --background-color: #FFCCE2;
}

/* Correct: using a design token */
oc-icon-button-v3 {
  --background-color: var(--oc-semantic-color-brand-subtle);
}

Note:

The preferred way of using colors is via design tokens instead of hex values.

Host element dimensions

The host element is the custom element tag itself (e.g. <oc-button-v1>). It acts as the outer shell of the component.

Allowed: outer dimensions on the host element

You may control how the component fits into the surrounding layout by setting outer dimension properties on the host element.

Allowed properties
width, height
min-width, max-width, min-height, max-height
margin
flex (as a flex child, e.g. flex: 1)
align-self, justify-self
grid-area, grid-column, grid-row
/* Correct: control outer dimensions of the host element */
oc-card-v3 {
  width: 100%;
  margin-bottom: 16px;
  flex: 1 1 auto;
}
Not allowed: inner dimensions on the host element

Do not apply properties that affect the component's internal layout or spacing. These properties interfere with how the component renders its own internal structure.

Prohibited properties
padding
border
border-radius
outline
box-shadow
overflow
/* Wrong: do not set inner dimensions on the host element */
oc-card-v3 {
  padding: 16px;
  border: 1px solid red;
  border-radius: 8px;
}

Why? Inner dimension properties are controlled by the component's internal styles. Overriding them breaks the component's visual design and may cause layout issues after updates.

Slot element dimensions

Slot elements are the direct children you place inside a component via its slots (e.g. <div slot="title">).

Not allowed: outer dimensions on slot elements

Do not apply properties that control how the slot element is positioned within the component's internal layout. The component manages the placement and sizing of its slots internally.

Prohibited properties
width, height
margin
flex (as a flex child)
align-self, justify-self
grid-area, grid-column, grid-row
/* Wrong: do not control outer dimensions of slot elements */
oc-card-v3 [slot="title"] {
  width: 200px;
  margin-top: 8px;
}
Allowed: inner dimensions on slot elements

You may style the visual appearance of the content inside a slot element.

Allowed properties
padding
border
border-radius
outline
box-shadow
background
color, font, text-*
/* Correct: style inner appearance of slot content */
oc-card-v3 [slot="title"] {
  padding: 4px 8px;
  border-radius: 4px;
  background-color: var(--oc-semantic-color-background-highlight);
}

Why? The component controls how it sizes and positions its own slots. Applying outer dimension properties to slot elements overrides this internal layout and causes unpredictable results after component updates.

Positioning

Allowed: positioning on the host element

You may apply any positioning properties to the host element to integrate it into your page layout.

/* Correct: use positioning on the host element */
oc-card-v3 {
  position: relative;
}

.card-grid oc-card-v3 {
  position: sticky;
  top: 0;
}

Accepted positioning-related properties on the host element:

Allowed properties
position (relative, absolute, sticky, fixed)
top, right, bottom, left, inset
z-index
display (flex, grid, block, inline-block, …)
flex-direction, flex-wrap, gap, align-items, justify-content (when the host is itself a flex/grid container)
Not allowed: positioning on slot elements

Do not apply positioning properties to elements placed into slots. The component controls the placement of its slot content, and overriding it breaks the internal layout.

/* Wrong: do not apply positioning to slot elements */
oc-banner-v1 [slot="actions"] {
  display: flex;
  position: absolute;
  top: 0;
}

Why? Positioning properties on slot elements conflict with the component's internal layout logic. This causes visual regressions and is not guaranteed to work correctly across component versions.

Component attributes

Otto Components expose attributes and properties as their public configuration API. Use these to control component behaviour, not CSS hacks or DOM manipulation.

Use attributes for configuration

Always use the documented attributes to configure a component.

<!-- Correct: configure via attributes -->
<oc-banner-v1 type="hint" size="200" headline="Some headline"> Message text </oc-banner-v1>
Never manipulate the shadow DOM directly

Do not query, modify, or rely on elements inside the shadow root. The internal DOM structure of a component is private and may change in any release.

// Wrong: never access shadow DOM internals
const inner = document.querySelector("oc-button-v1").shadowRoot.querySelector("button");
inner.style.background = "red";