SDC Variants
apps/docs/src/content/docs/drupal/sdc/variants Click to copy apps/docs/src/content/docs/drupal/sdc/variants SDC variants let site builders and content editors choose between visual treatments of the same content pattern — an editorial card vs. a featured card, a compact staff profile vs. a full one. This guide documents three variant strategies that work together in HELiX-based SDCs.
Strategy 1: HELiX Component Variant Props
Section titled “Strategy 1: HELiX Component Variant Props”The simplest variant mechanism passes a variant value directly to a HELiX component’s variant attribute. The component handles all visual changes internally through Shadow DOM styles and CSS custom properties.
Define the prop in component.yml
Section titled “Define the prop in component.yml”props: type: object properties: card_variant: type: string title: Card Visual Style enum: [default, featured, compact] default: default description: Passed to hx-card's variant attribute.Use it in the template
Section titled “Use it in the template”{# components/article-teaser/article-teaser.twig #}<hx-card variant="{{ card_variant|default('default') }}"> {# ... #}</hx-card>Pass it from a parent template
Section titled “Pass it from a parent template”{# node--article--featured.html.twig — uses featured card #}{% include 'mytheme:article-teaser' with { title: node.label, url: url, card_variant: "featured", image: content.field_hero_image,} only %}{# node--article--teaser.html.twig — uses default card #}{% include 'mytheme:article-teaser' with { title: node.label, url: url, card_variant: 'default',} only %}Strategy 2: CSS Classes for Layout Variants
Section titled “Strategy 2: CSS Classes for Layout Variants”When a variant changes layout (not just color/shadow), add a CSS class to the SDC’s wrapper element and define the layout variant in the SDC’s CSS file.
Define a layout_variant prop
Section titled “Define a layout_variant prop”props: type: object properties: layout: type: string title: Card Layout enum: [card, horizontal, minimal] default: card description: Controls card layout orientation.Apply the class in the template
Section titled “Apply the class in the template”{# components/staff-profile/staff-profile.twig #}<div class="staff-profile staff-profile--{{ layout|default('card') }}"> <hx-card variant="default"> {# ... #} </hx-card></div>CSS for layout variants
Section titled “CSS for layout variants”/* Default: stacked (photo above content) */.staff-profile--card .staff-profile__photo { display: flex; justify-content: center; padding-bottom: var(--hx-space-4);}
/* Horizontal: photo beside content */.staff-profile--horizontal { display: grid; grid-template-columns: 80px 1fr; gap: var(--hx-space-4); align-items: start;}
.staff-profile--horizontal .staff-profile__photo { grid-row: 1 / 3; padding: 0;}
/* Minimal: name and role only, no photo */.staff-profile--minimal hx-avatar { display: none;}Strategy 3: Variant-Specific Templates (Twig Template Suggestions)
Section titled “Strategy 3: Variant-Specific Templates (Twig Template Suggestions)”For variants with substantially different markup structure, use Drupal’s template suggestion system to load a separate Twig file.
Add template suggestions in a preprocess function
Section titled “Add template suggestions in a preprocess function”/** * Implements hook_preprocess_node(). */function mytheme_preprocess_node(array &$variables): void { $node = $variables['node']; $view_mode = $variables['view_mode'];
// Add suggestion: node--[bundle]--[view-mode].html.twig // Already provided by Drupal, but you can add SDC-specific suggestions: $variables['theme_hook_suggestions'][] = 'node__' . $node->bundle() . '__' . $view_mode . '__helix';}Separate template for a “featured” variant
Section titled “Separate template for a “featured” variant”{# node--article--featured--helix.html.twig #}{# Uses a full-bleed card layout with larger media area #}{% include 'mytheme:article-featured' with { title: node.label, url: url, summary: content.body[0]['#text']|striptags|trim|slice(0, 400), category: node.field_category.entity.label, image: content.field_hero_image, card_variant: "default",} only %}Requires a separate article-featured SDC for the full-bleed layout.
Theme Variants
Section titled “Theme Variants”Different editorial themes (healthcare, research, editorial) can override the variant prop values exposed by SDCs to produce context-appropriate styling without changing SDC code.
Define a theme_context prop
Section titled “Define a theme_context prop”props: type: object properties: theme_context: type: string title: Theme Context enum: [editorial, healthcare, research] default: editorialDerive component variant from theme context
Section titled “Derive component variant from theme context”{# components/article-teaser/article-teaser.twig #}{% set variant_map = { 'editorial': 'default', 'healthcare': 'default', 'research': 'featured',} %}<hx-card variant="{{ variant_map[theme_context]|default('default') }}"> {# ... #}</hx-card>Set theme context in a preprocess function
Section titled “Set theme context in a preprocess function”/** * Implements hook_preprocess_node(). */function mytheme_preprocess_node(array &$variables): void { $node = $variables['node'];
// Map content type to theme context. $context_map = [ 'article' => 'editorial', 'clinical_resource' => 'healthcare', 'research_publication' => 'research', ];
$variables['helix_theme_context'] = $context_map[$node->bundle()] ?? 'editorial';}{# node--article--teaser.html.twig #}{% include 'mytheme:article-teaser' with { title: node.label, url: url, theme_context: helix_theme_context,} only %}Responsive Variants
Section titled “Responsive Variants”Responsive variants change component presentation based on viewport. Use CSS custom properties and container queries rather than separate variant props.
Container-query-driven responsiveness
Section titled “Container-query-driven responsiveness”/* Enable container queries on the SDC root */.article-teaser-container { container-type: inline-size; container-name: article-teaser;}
/* Compact layout in small containers */@container article-teaser (max-width: 360px) { hx-card { --hx-card-padding: var(--hx-space-3); }
.article-teaser__summary { display: none; }}
/* Full layout in wider containers. hx-card's exposed image hook is `--hx-card-image-aspect-ratio` (not a fixed `image-height` token). For pixel-pinned image heights, style the slotted image yourself — the slotted content lives in light DOM and inherits page CSS. */@container article-teaser (min-width: 600px) { hx-card { --hx-card-image-aspect-ratio: 16 / 9; } hx-card [slot='image'] img { height: 240px; width: 100%; object-fit: cover; }}{# Wrap in a container-query root #}<div class="article-teaser-container"> {% include 'mytheme:article-teaser' with { ... } only %}</div>Viewport-based via a Twig prop
Section titled “Viewport-based via a Twig prop”For cases where server-side logic controls the variant (e.g., different layouts for mobile REST API responses vs. full page renders):
props: type: object properties: compact: type: boolean title: Compact Mode description: Reduce card density for narrow contexts. default: false<hx-card variant="{{ compact ? 'default' : 'default' }}" class="{{ compact ? 'article-teaser--compact' : '' }}"> {% if not compact and summary %} <p>{{ summary|escape }}</p> {% endif %}</hx-card>Combining Variant Strategies
Section titled “Combining Variant Strategies”A production SDC typically combines all three strategies:
props: properties: # Strategy 1: HELiX component variant card_variant: type: string enum: [default, featured, compact] default: default
# Strategy 2: Layout CSS class variant layout: type: string enum: [card, horizontal] default: card
# Strategy 3: Theme context (drives template selection or variant mapping) theme_context: type: string enum: [editorial, healthcare, research] default: editorial
# Responsive: compact flag compact: type: boolean default: false{% set resolved_variant = card_variant|default('default') %}{% if theme_context == 'healthcare' and card_variant is not defined %} {% set resolved_variant = 'default' %}{% endif %}
<div class="article-teaser article-teaser--{{ layout|default('card') }}{% if compact %} article-teaser--compact{% endif %}"> <hx-card variant="{{ resolved_variant }}"> {# ... #} </hx-card></div>Best Practices
Section titled “Best Practices”- Prefer HELiX variant props for color/shadow/elevation changes — they are design-system governed and accessible by default.
- Use CSS classes for layout changes — flex vs. grid, portrait vs. landscape, compact vs. full.
- Use separate templates only when markup structure differs substantially (different slots, different element hierarchy).
- Avoid more than 3–4 variant props per SDC — complexity grows exponentially. If you have 8 variants, consider splitting into two SDCs.
- Document enum values in component.yml descriptions. Site builders using Layout Builder need to understand what each value does without reading the template.
Related
Section titled “Related”- SDC Architecture — Two-layer model, file structure, registration
- SDC Composition Patterns — Full article-teaser and staff-profile examples
- Theming — CSS custom properties for cross-component styling