Overview
Source: ./src/documentation/development/interactive-component-slots.mdx
Interactive component slots
This guide provides patterns and best practices for implementing interactive components using the OTTO Components slot-based light DOM architecture.
The type of interactive component you create depends on the native HTML element you use as slotted content. For consistency, the interactive element goes in the default slot, while additional content (labels, hints, etc.) goes in named slots or within the default slot.
All interactive components follow a consistent pattern: you provide the native HTML element (<button>, <input>, <a>, etc.) as slotted content, and the component handles styling, layout, and coordinated behavior.
Why light DOM? Read Interactive elements and Shadow DOM to understand the benefits of this approach.
Skip to:
- Secondary actions
- Marking content as checked
- Checkbox-like components
- Radio button-like components
- Switch-like components
- Text-field-like components
- Textarea-like components
- Select-like components
- Button-like components
- Link-like components
- Masked-link-like components
- Link-switching-like components
- Current-marked components
Secondary actions
Some components need more than one interactive element in a slot.
Mark any additional interactive element with the oc-secondary-action CSS class so it is:
- Excluded from the component's own click/activation handling — clicking the secondary element only triggers its own handler instead of also activating the host.
- Still included in interactive state tracking — hovering the secondary element still updates the host's
hoverstyling, so the whole component still looks and feels interactive.
Example
<oc-card-v3>
<a href="/product/123" aria-label="Product"></a>
<div slot="content">
...
<button class="oc-secondary-action">Secondary Action</button>
...
</div>
</oc-card-v3>
Marking content as checked
Marking an element as "checked" is not a single pattern. The correct native element and attribute depend on the selection behavior you need.
| Behavior | Element and attribute | Selection scope |
|---|---|---|
| Multiple independent selections | <input type="checkbox"> with checked |
Each checkbox toggles independently. |
| Exactly one selection out of a group | <input type="radio"> with name and checked |
The browser enforces single selection within a name group. |
| A single toggle without a native input element | <button role="switch"> with aria-checked |
One element toggles on or off. Coordinate the aria-checked value in your component logic. |
| The currently active item in a custom-built set | <button aria-current> or <a aria-current> |
You mark and coordinate the current item yourself. The browser does not enforce exclusivity. |
Use checked and name for form-related, submittable selections.
Use aria-checked or aria-current for UI-only selection state that does not submit as form data, such as filter chips or navigation items.
Checkbox-like components
To create a checkbox-like component, use the <input type="checkbox"> element as slotted content.
Use this pattern when multiple items in a set can be selected independently of each other.
Example
<oc-foo>
<input type="checkbox" />
</oc-foo>
Key features
- Native form integration (the checkbox participates in form submission automatically)
- Browser autofill and password manager support
- Native validation (
required,pattern, etc.) - Full control over checkbox attributes (
checked,disabled,name,value, etc.)
Radio button-like components
To create a radio button-like component, use the <input type="radio"> element as slotted content.
Group radio buttons using the name attribute for native single-selection behavior.
Use this pattern when exactly one item out of a group must be selected, and you want the browser to enforce that constraint.
Example
<oc-foo>
<input type="radio" name="option" value="1" />
</oc-foo>
Key features
- Native radio button grouping via the
nameattribute - Automatic single-selection behavior
- Native keyboard navigation (arrow keys)
- Full control over radio attributes (
checked,disabled,name,value, etc.)
Switch-like components
To create a switch-like component, use a <button role="switch" aria-checked="true"> or <button role="switch" aria-checked="false"> element as slotted content.
Use this pattern for a single, independent on-or-off selection that does not need native form submission or grouping, such as a settings toggle or a filter chip.
Unlike <input type="checkbox">, the <button> element does not manage its checked state natively.
Your component logic must update the aria-checked attribute when the user interacts with the button.
Example
<oc-foo>
<button role="switch" aria-checked="false">Option</button>
</oc-foo>
Key features
- Communicates toggle state to assistive technology through
aria-checked - Does not participate in form submission, so use it for UI-only state
- Requires you to update
aria-checkedin your component's event handling - Does not have native grouping. Coordinate exclusivity yourself if needed
Text-field-like components
To create a text-field-like component, use the <input> element with the appropriate type attribute (text, email, password, etc.) as slotted content.
Example
<oc-foo>
<input type="text" />
</oc-foo>
Key features
- Support for all input types (
text,email,password,tel,url,number, etc.) - Native HTML5 validation attributes (
required,pattern,minlength,maxlength, etc.) - Browser autofill and password manager support
- Full control over input attributes (
autocomplete,placeholder,readonly, etc.)
Textarea-like components
To create a textarea-like component, use the native <textarea> element as slotted content.
Example
<oc-foo>
<textarea></textarea>
</oc-foo>
Key features
- Native textarea behavior (resizing, line breaks, etc.)
- Native validation attributes (
required,minlength,maxlength, etc.) - Full control over textarea attributes (
rows,cols,wrap, etc.)
Select-like components
To create a select-like component, use the native <select> element with nested <option> and <optgroup> elements as slotted content.
Example
<oc-foo>
<select>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</select>
</oc-foo>
Key features
- Native select behavior and keyboard navigation
- Support for
<optgroup>and<option>elements - Native validation (
required, etc.) - Full control over select attributes (
multiple,size,disabled, etc.)
Button-like components
To create a button-like component, use either the native <button> element or an <a> element as slotted content, depending on whether the action triggers form submission or navigation.
Example
<oc-foo>
<button type="button">Click me</button>
</oc-foo>
Key features
- Support for both
<button>and<a>elements - Native form submission behavior
- Full control over button attributes (
type,disabled,name,value, etc.) - Proper semantic HTML for different use cases
Link-like components
To create a link-like component, use the native <a> element as slotted content.
This ensures search engines can discover and index your links.
Example
<oc-foo>
<a href="/destination">My link</a>
</oc-foo>
Key features
- Native link behavior and keyboard navigation
- Full control over link attributes (
target,rel,download, etc.) - SEO-friendly (search engines can crawl and index the
hrefin light DOM) - Support for all native link features
Masked-link-like components
To create a masked-link-like component, use the data-masked-ref attribute to provide a Base64-encoded URL.
This technique prevents search engines from indexing certain URLs while keeping them navigable for users.
Example
<!-- User navigates to: /internal/page -->
<oc-foo>
<div role="link" tabindex="0" data-masked-ref="L2ludGVybmFsL3BhZ2U=">Masked link</div>
</oc-foo>
How it works
The data-masked-ref attribute contains the Base64-encoded URL (/internal/page → L2ludGVybmFsL3BhZ2U=).
When hovered, focused, or touched by the user, the link is decoded.
When to use
- Pages marked as
noindexby the SEO team - URLs not relevant for search engine indexing
When not to use
- Public pages that should be indexed by search engines
- Navigation that requires SEO visibility
Link-switching-like components
To manage product variation URLs (for example, color options) and prevent search engines from indexing multiple variations as separate pages, use the data-masked-ref attribute to provide a Base64-encoded URL for the variation while keeping a clean URL in the href for SEO.
Example
<!-- User navigates to: /jacke-x/?farbe=blau -->
<!-- Crawler sees: /jacke-x -->
<oc-foo>
<a data-masked-ref="L2phY2tlLXgvP2ZhcmJlPWJsYXU=" href="/jacke-x">Blue Jacket</a>
</oc-foo>
How it works
The component shows the href value (clean URL) to search engines while users navigate to the decoded data-masked-ref value (URL with parameters).
Links switch on hover, focus, or touch events.
When to use
- Product variation URLs (colors, sizes, styles, etc.)
- Managing how search engines consolidate similar pages
- Preventing duplicate content issues
When not to use
- Pages without SEO concerns
Current-marked components
To mark a <button> or <a> element as the currently active item in a custom-built set, use the aria-current attribute.
Use this pattern for sets that you build and coordinate yourself, such as filter chips, tabs, or navigation menus, where no single native input element expresses "currently active."
aria-current differs from aria-checked and checked: it communicates which item is currently active or in view, rather than which items a user selected.
The browser does not enforce exclusivity for aria-current. Your component logic must ensure only one item carries the current value within a set.
Example
<!-- Button marked as current -->
<oc-foo>
<button aria-current="true">Option</button>
</oc-foo>
<!-- Link marked as current -->
<oc-foo>
<a aria-current="page" href="/current-page">Option</a>
</oc-foo>
Key features
- Communicates the current item to assistive technology through
aria-current - Supports both
<button>(for example,aria-current="true") and<a>(for example,aria-current="page") elements - Does not participate in form submission
- Requires you to coordinate exclusivity across the set in your component's logic
When to use
- Highlighting the active tab, filter, or navigation item in a set you control
- Indicating the current page among a set of links
When not to use
- Selections that must submit as form data. Use
<input type="checkbox">or<input type="radio">instead - Independent on-or-off toggles without the concept of a current item. Use switch-like components instead