| Version | Tag | Status | API |
|---|---|---|---|
| v2 | <oc-block-v2> |
Stable, allowed for generation | BlockV2 |
Overview (v2)
Source: ./src/components/block/v2/Overview.mdx
Block
The block component provides a container for layout and spacing in your design. Use it to group content and control appearance through its properties.
Default variation
Story Default:
<oc-block-v2><oc-placeholder-v1 style="height: 100px;">DETACH TO REPLACE CONTENT</oc-placeholder-v1></oc-block-v2>
Configuration
Explore all available configuration options in the component configurator and see the changes affect the component in real-time.
Usage guidelines
Visit the variations page for examples of different block configurations and use cases.
Info
See the Block UX documentation for detailed user experience guidelines.
Accessibility
The block component is a presentational container and does not include interactive features. Ensure that any content placed inside the block is accessible and follows best practices for semantic HTML and ARIA attributes.
Visit the accessibility documentation for general guidelines on making your content accessible.
Configuration (v2)
Source: ./src/components/block/v2/Configuration.mdx
Block configuration
Configure the block component with the controls below and see the changes live in the preview canvas. Click the Show code button within the preview canvas to see the source code for the current component configuration.
Story Default:
<oc-block-v2><oc-placeholder-v1 style="height: 100px;">DETACH TO REPLACE CONTENT</oc-placeholder-v1></oc-block-v2>
Interactive configurator (Storybook controls); every option is listed in the API section of this file.
Migration (v2)
Source: ./src/components/block/v2/Migration.mdx
Migration from Block v1 to v2
The oc-block component has been updated from oc-block-v1 to oc-block-v2.
This migration guide provides step-by-step instructions to update your project to the latest version.
Skip to:
What's new
Web component
The block is now a proper web component instead of a CSS-only class, providing:
- Encapsulated styling
- Consistent behavior across the application
- Better integration with other Otto Components
API changes
Changed structure
| v1 (CSS class) | v2 (Web Component) | Notes |
|---|---|---|
<div class="oc-block-v1"> |
<oc-block-v2> |
Component-based |
How to migrate
Instead of using a div with the oc-block-v1 class, use the oc-block-v2 component tag to create a block.
The oc-block-v2 component provides a container for layout and spacing in your design.
Use it to group content and control appearance through its properties.
Migrate a basic block
Replace the div with the oc-block-v1 class with the oc-block-v2 component tag.
<!-- From: -->
<div class="oc-block-v1">My Block</div>
<!-- To: -->
<oc-block-v2>My Block</oc-block-v2>
API v2
Source: ./src/components/block/v2/BlockV2.API.g.mdx
Block v2 API
API: <oc-block-v2> (BlockV2)
The block component provides a container for layout and spacing in your design. Use it to group content and control appearance through its properties.
Attributes / properties
| Attribute | Type | Default | Required | Description |
|---|---|---|---|---|
elevation-level |
"canvas" | "stacked" |
"canvas" |
no | Sets the elevation level of the block. |
padding |
"default" | "full-bleed" | "no-padding" |
"default" |
no | Deprecated: Use CSS variables --custom-spacing-x and --custom-spacing-y instead.Sets the padding of the block. |
Slots
| Slot | Required | Description |
|---|---|---|
default |
yes | Sets the main content of the block. |
Events
| Event | Detail type | Description |
|---|---|---|
oc-property-change |
OcBlockV2Events["oc-property-change"] |
Whenever a property value changes, this event triggers. Use this event to track all property changes within the component. Refer to the Events documentation for more information. |
oc-mount |
{ component: string; } |
Fired when the component is mounted to the DOM. The event is fired when the onMount hook of the component is called by the runtime. |
oc-unmount |
{ component: string; } |
Fired when the component is unmounted from the DOM. The event is fired when the function returned by the onMount hook of the component is called by the runtime. |
CSS custom properties
| Custom property | Default | Description |
|---|---|---|
--oc-block-background-color |
undefined |
Sets a custom background color through a CSS variable. |
--background-color |
undefined |
Sets a custom background color through a CSS variable. Note: The preferred way of using colors is via design tokens instead of hex values. |
--custom-spacing-x |
undefined |
Sets the custom horizontal spacing of the block content. |
--custom-spacing-y |
undefined |
Sets the custom vertical spacing of the block content. |
Variations (v2)
Source: ./src/components/block/v2/Variations.mdx
Variations
Listed below are the most common variations of the block component as well as specific component variations for different use cases.
You can explore all available options using the component configurator, adjust the component to your needs, and see the changes live in a preview canvas.
Default
Story: components-block-variations--default · tags: components, block, 2, variations
Default Component
Args: defaultSlot=<oc-placeholder-v1 style="height: 100px;">DETACH TO REPLACE CONTENT</oc-placeholder-v1>
<oc-block-v2><oc-placeholder-v1 style="height: 100px;">DETACH TO REPLACE CONTENT</oc-placeholder-v1></oc-block-v2>
Demo: Custom Background Color
Story: components-block-variations--demo-custom-background-color · tags: components, block, 2, variations
Demo: Custom Background Color
Args: defaultSlot=<oc-placeholder-v1 style="height: 100px;">DETACH TO REPLACE CONTENT</oc-placeholder-v1>
<style>
.block-wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
max-width: fit-content;
}
</style>
<div class="block-wrapper">
<oc-block-v2 style="--background-color: #B4E1D7">${unsafeHTML(placeholder)}</oc-block-v2>
<oc-block-v2 style="--background-color: #C5EAFF">${unsafeHTML(placeholder)}</oc-block-v2>
<oc-block-v2 style="--background-color: #DBFB98">${unsafeHTML(placeholder)}</oc-block-v2>
<oc-block-v2 style="--background-color: #E5D5FE">${unsafeHTML(placeholder)}</oc-block-v2>
<oc-block-v2 style="--background-color: #FFD7F5">${unsafeHTML(placeholder)}</oc-block-v2>
<oc-block-v2 style="--background-color: #FEC5C6">${unsafeHTML(placeholder)}</oc-block-v2>
<oc-block-v2 style="--background-color: #FFC5A5">${unsafeHTML(placeholder)}</oc-block-v2>
<oc-block-v2 style="--background-color: #FFFAAF">${unsafeHTML(placeholder)}</oc-block-v2>
<oc-block-v2 style="--background-color: #E5D5FE">${unsafeHTML(placeholder)}</oc-block-v2>
</div>
Story source (TypeScript, verbatim from Storybook)
{
name: "Demo: Custom Background Color",
parameters: {
controls: {
disabled: true
},
chromatic: {
hideInChromatic: true
}
},
argTypes: hideControlsBadge(Metadata),
args: {
defaultSlot: placeholder
},
render() {
return html`
<style>
.block-wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
max-width: fit-content;
}
</style>
<div class="block-wrapper">
<oc-block-v2 style="--background-color: #B4E1D7">${unsafeHTML(placeholder)}</oc-block-v2>
<oc-block-v2 style="--background-color: #C5EAFF">${unsafeHTML(placeholder)}</oc-block-v2>
<oc-block-v2 style="--background-color: #DBFB98">${unsafeHTML(placeholder)}</oc-block-v2>
<oc-block-v2 style="--background-color: #E5D5FE">${unsafeHTML(placeholder)}</oc-block-v2>
<oc-block-v2 style="--background-color: #FFD7F5">${unsafeHTML(placeholder)}</oc-block-v2>
<oc-block-v2 style="--background-color: #FEC5C6">${unsafeHTML(placeholder)}</oc-block-v2>
<oc-block-v2 style="--background-color: #FFC5A5">${unsafeHTML(placeholder)}</oc-block-v2>
<oc-block-v2 style="--background-color: #FFFAAF">${unsafeHTML(placeholder)}</oc-block-v2>
<oc-block-v2 style="--background-color: #E5D5FE">${unsafeHTML(placeholder)}</oc-block-v2>
</div>
`;
}
}
Demo: Stacked Block with background color
Story: components-block-variations--demo-stacked-block-with-background-color · tags: components, block, 2, variations
Demo: Stacked Block with background color
Args: defaultSlot=<oc-placeholder-v1 style="height: 100px;">DETACH TO REPLACE CONTENT</oc-placeholder-v1>
<oc-block-v2>
<oc-block-v2 elevation-level="stacked" style="--background-color: #FFD7F5"
>${unsafeHTML(placeholder)}</oc-block-v2
>
</oc-block-v2>
Story source (TypeScript, verbatim from Storybook)
{
name: "Demo: Stacked Block with background color",
parameters: {
controls: {
disabled: true
},
chromatic: {
hideInChromatic: true
}
},
argTypes: hideControlsBadge(Metadata),
args: {
defaultSlot: placeholder
},
render() {
return html`
<oc-block-v2>
<oc-block-v2 elevation-level="stacked" style="--background-color: #FFD7F5"
>${unsafeHTML(placeholder)}</oc-block-v2
>
</oc-block-v2>
`;
}
}
Demo: Stacked Block in Canvas Block
Story: components-block-variations--demo-stacked-block-in-canvas-block · tags: components, block, 2, variations
Demo: Stacked Block in Canvas Block
Args: defaultSlot=<oc-placeholder-v1 style="height: 100px;">DETACH TO REPLACE CONTENT</oc-placeholder-v1>
<oc-block-v2>
<oc-block-v2 elevation-level="stacked">${unsafeHTML(placeholder)}</oc-block-v2>
</oc-block-v2>
Story source (TypeScript, verbatim from Storybook)
{
name: "Demo: Stacked Block in Canvas Block",
parameters: {
controls: {
disabled: true
},
chromatic: {
hideInChromatic: true
}
},
argTypes: hideControlsBadge(Metadata),
args: {
defaultSlot: placeholder
},
render() {
return html`
<oc-block-v2>
<oc-block-v2 elevation-level="stacked">${unsafeHTML(placeholder)}</oc-block-v2>
</oc-block-v2>
`;
}
}
Demo: Stacked Block in Canvas Block with background color
Story: components-block-variations--demo-stacked-block-in-canvas-block-with-background-color · tags: components, block, 2, variations
Demo: Stacked Block in Canvas Block w/ background color
Args: defaultSlot=<oc-placeholder-v1 style="height: 100px;">DETACH TO REPLACE CONTENT</oc-placeholder-v1>
<oc-block-v2 style="--background-color: #FFD7F5">
<oc-block-v2 elevation-level="stacked">${unsafeHTML(placeholder)}</oc-block-v2>
</oc-block-v2>
Story source (TypeScript, verbatim from Storybook)
{
name: "Demo: Stacked Block in Canvas Block with background color",
parameters: {
controls: {
disabled: true
},
chromatic: {
hideInChromatic: true
}
},
argTypes: hideControlsBadge(Metadata),
args: {
defaultSlot: placeholder
},
render() {
return html`
<oc-block-v2 style="--background-color: #FFD7F5">
<oc-block-v2 elevation-level="stacked">${unsafeHTML(placeholder)}</oc-block-v2>
</oc-block-v2>
`;
}
}
Demo: Stacked Block with divider
Story: components-block-variations--demo-stacked-block-with-divider · tags: components, block, 2, variations
Demo: Stacked Block with divider
Args: defaultSlot=<oc-placeholder-v1 style="height: 100px;">DETACH TO REPLACE CONTENT</oc-placeholder-v1>
<oc-block-v2 style="max-width: 400px;">
<oc-block-v2
elevation-level="stacked"
style="--custom-spacing-y: 16px; --custom-spacing-x: 0;"
>
${unsafeHTML(placeholder)}
<div style="height: 8px;"></div>
<oc-divider-v1></oc-divider-v1>
<div style="height: 8px;"></div>
${unsafeHTML(placeholder)}
</oc-block-v2>
</oc-block-v2>
Story source (TypeScript, verbatim from Storybook)
{
name: "Demo: Stacked Block with divider",
parameters: {
controls: {
disabled: true
},
chromatic: {
hideInChromatic: true
}
},
argTypes: hideControlsBadge(Metadata),
args: {
defaultSlot: placeholder
},
render() {
return html`
<oc-block-v2 style="max-width: 400px;">
<oc-block-v2
elevation-level="stacked"
style="--custom-spacing-y: 16px; --custom-spacing-x: 0;"
>
${unsafeHTML(placeholder)}
<div style="height: 8px;"></div>
<oc-divider-v1></oc-divider-v1>
<div style="height: 8px;"></div>
${unsafeHTML(placeholder)}
</oc-block-v2>
</oc-block-v2>
`;
}
}
Demo: Custom spacings
Story: components-block-variations--demo-custom-spacings · tags: components, block, 2, variations
Demo of a block component containing a custom layout with adjustable sliders for custom spacings.
<div
style="display: flex; border: 1px solid var(--oc-base-color-gray-200); width: fit-content; "
>
<oc-block-v2>
<ofc-placeholder-v1 variant="image" style="height: 126px"></ofc-placeholder-v1>
<h1 class="oc-headline-200 oc-mt-100">Some example content</h1>
<p class="oc-copy-100">
Text Lorem Ipsum Dolor Sit Amet Bla bla bla. Und so zerbröselt der Keks nunmal.
</p>
</oc-block-v2>
</div>
<div style="display: flex; flex-direction: column; gap: 16px; margin-top: 32px;">
<div style="display: flex; gap: 16px; align-items: center;">
<label for="horizontal-slider">Horizontal Spacing</label>
<input
type="range"
name="horizontal-slider"
id="horizontal-slider"
min="0"
max="64"
value="0"
/>
<span id="horizontal-slider-value">0px</span>
</div>
<div style="display: flex; gap: 16px; align-items: center;">
<label for="vertical-slider">Vertical Spacing</label>
<input
type="range"
name="vertical-slider"
id="vertical-slider"
min="0"
max="64"
value="16"
/>
<span id="vertical-slider-value">16px</span>
</div>
</div>
<script>
(() => {
const row = document.querySelector("oc-block-v2");
const horizontalSlider = document.getElementById("horizontal-slider");
const horizontalSliderValue = document.getElementById("horizontal-slider-value");
const verticalSlider = document.getElementById("vertical-slider");
const verticalSliderValue = document.getElementById("vertical-slider-value");
horizontalSlider.addEventListener("input", (event) => {
const value = event.target.value + "px";
row.style.setProperty("--custom-spacing-x", value);
horizontalSliderValue.textContent = value;
});
verticalSlider.addEventListener("input", (event) => {
const value = event.target.value + "px";
row.style.setProperty("--custom-spacing-y", value);
verticalSliderValue.textContent = value;
});
})();
</script>
Story source (TypeScript, verbatim from Storybook)
{
name: "Demo: Custom spacings",
parameters: {
controls: {
disabled: true
},
backgrounds: {
value: "frame"
},
chromatic: {
hideInChromatic: true
}
},
render: () => {
return html`
<div
style="display: flex; border: 1px solid var(--oc-base-color-gray-200); width: fit-content; "
>
<oc-block-v2>
<ofc-placeholder-v1 variant="image" style="height: 126px"></ofc-placeholder-v1>
<h1 class="oc-headline-200 oc-mt-100">Some example content</h1>
<p class="oc-copy-100">
Text Lorem Ipsum Dolor Sit Amet Bla bla bla. Und so zerbröselt der Keks nunmal.
</p>
</oc-block-v2>
</div>
<div style="display: flex; flex-direction: column; gap: 16px; margin-top: 32px;">
<div style="display: flex; gap: 16px; align-items: center;">
<label for="horizontal-slider">Horizontal Spacing</label>
<input
type="range"
name="horizontal-slider"
id="horizontal-slider"
min="0"
max="64"
value="0"
/>
<span id="horizontal-slider-value">0px</span>
</div>
<div style="display: flex; gap: 16px; align-items: center;">
<label for="vertical-slider">Vertical Spacing</label>
<input
type="range"
name="vertical-slider"
id="vertical-slider"
min="0"
max="64"
value="16"
/>
<span id="vertical-slider-value">16px</span>
</div>
</div>
<script>
(() => {
const row = document.querySelector("oc-block-v2");
const horizontalSlider = document.getElementById("horizontal-slider");
const horizontalSliderValue = document.getElementById("horizontal-slider-value");
const verticalSlider = document.getElementById("vertical-slider");
const verticalSliderValue = document.getElementById("vertical-slider-value");
horizontalSlider.addEventListener("input", (event) => {
const value = event.target.value + "px";
row.style.setProperty("--custom-spacing-x", value);
horizontalSliderValue.textContent = value;
});
verticalSlider.addEventListener("input", (event) => {
const value = event.target.value + "px";
row.style.setProperty("--custom-spacing-y", value);
verticalSliderValue.textContent = value;
});
})();
</script>
`;
},
argTypes: hideControlsBadge(Metadata)
}