← back to Dw Theme Boost Fix
snippets/layout.stack.liquid
193 lines
{% doc %}
Copyright © 2025 Archetype Themes LP. All rights reserved.
A versatile layout component that provides flexible stacking of elements in either vertical or horizontal orientation.
Supports responsive gap sizing, flex wrapping, justification and alignment controls, and can be reversed.
The component is highly customizable through props while maintaining consistent spacing and layout behavior.
@param {string} slot - The slot content to wrap
@param {string} [as] - The HTML element to use for the stack (default: 'div')
@param {string} [id] - ID attribute
@param {string} [classlist] - Additional classes to add
@param {string} [attributes] - Additional attributes
@param {string} [style] - Inline styles to apply
@param {string} [gap] <responsive> ('none'|'3xs'|'2xs'|'xs'|'sm'|'md'|'lg'|'xl'|'2xl') - Gap size between items, default: 'none'
@param {string} [gap_row] <responsive> ('none'|'3xs'|'2xs'|'xs'|'sm'|'md'|'lg'|'xl'|'2xl') - Gap size between rows
@param {string} [gap_col] <responsive> ('none'|'3xs'|'2xs'|'xs'|'sm'|'md'|'lg'|'xl'|'2xl') - Gap size between columns
@param {boolean} [horizontal] <responsive> - Whether the stack should be horizontal (default: false)
@param {boolean} [reverse] <responsive> - Whether the stack should be reversed (default: false)
@param {string} [justify] <responsive> - Controls how items are positioned along the main axis, options: 'start', 'center', 'end', 'space-around', 'space-between', 'space-evenly'
@param {string} [align] <responsive> - Controls how items are aligned along the cross axis, options: 'start', 'center', 'end', 'stretch', 'baseline'
@param {string} [wrap] - Controls flex wrapping (default: 'wrap'), options: 'wrap', 'nowrap', 'wrap-reverse'
@example
{% render 'layout.stack', slot: content, horizontal: true, reverse: true, gap: 'xs' %}
{% enddoc %}
{% render 'style.primitive-tokens' %}
{% render 'style.normalize' %}
{% stylesheet %}
/* Public Layout Stack Tokens */
:root {
/* Layout Stack Gap Sizes - These are assigned by the value passed to the 'gap' param */
--layout-stack-gap-size-none: 0px;
--layout-stack-gap-size-3xs: var(--gap-size-3xs);
--layout-stack-gap-size-2xs: var(--gap-size-2xs);
--layout-stack-gap-size-xs: var(--gap-size-xs);
--layout-stack-gap-size-sm: var(--gap-size-sm);
--layout-stack-gap-size-md: var(--gap-size-md);
--layout-stack-gap-size-lg: var(--gap-size-lg);
--layout-stack-gap-size-xl: var(--gap-size-xl);
--layout-stack-gap-size-2xl: var(--gap-size-2xl);
/* Layout Stack Default Layout */
--layout-stack-margin: 0;
}
{% endstylesheet %}
{% liquid
assign slot = slot | default: ''
assign as = as | default: 'div'
assign id = id | default: ''
assign classlist = classlist | default: '' | append: ' layout-stack' | strip
assign wrap = wrap | default: 'wrap'
assign align = align | default: ''
assign attributes = attributes | default: ''
assign style = style | default: ''
assign breakpoints = 'xs,sm,md,lg,xl' | split: ','
# Flex Direction w/ Breakpoints
assign horizontal_xs = horizontal_xs | default: horizontal | default: false
assign horizontal_breakpoint_map = null | default: xs: horizontal_xs, sm: horizontal_sm, md: horizontal_md, lg: horizontal_lg, xl: horizontal_xl
assign reverse_xs = reverse_xs | default: reverse | default: false
assign reverse_breakpoint_map = null | default: xs: reverse_xs, sm: reverse_sm, md: reverse_md, lg: reverse_lg, xl: reverse_xl
assign flex_directions = ''
for breakpoint in breakpoints
assign horizontal_value = horizontal_breakpoint_map[breakpoint]
assign reverse_value = reverse_breakpoint_map[breakpoint]
unless horizontal_value == nil and reverse_value == nil
if horizontal_value
if reverse_value
assign flex_directions = flex_directions | append: 'row-reverse' | append: ','
else
assign flex_directions = flex_directions | append: 'row' | append: ','
endif
else
if reverse_value
assign flex_directions = flex_directions | append: 'column-reverse' | append: ','
else
assign flex_directions = flex_directions | append: 'column' | append: ','
endif
endif
else
assign flex_directions = flex_directions | append: '' | append: ','
endunless
endfor
assign flex_directions = flex_directions | split: ','
assign flex_direction_breakpoint_map = null | default: xs: flex_directions[0], sm: flex_directions[1], md: flex_directions[2], lg: flex_directions[3], xl: flex_directions[4]
# Gap w/ Breakpoints
assign gap_xs = gap_xs | default: gap | default: 'none'
assign gap_breakpoint_map = null | default: xs: gap_xs, sm: gap_sm, md: gap_md, lg: gap_lg, xl: gap_xl
assign gap_row_xs = gap_row_xs | default: gap_row | default: ''
assign gap_row_breakpoint_map = null | default: xs: gap_row_xs, sm: gap_row_sm, md: gap_row_md, lg: gap_row_lg, xl: gap_row_xl
assign gap_col_xs = gap_col_xs | default: gap_col | default: ''
assign gap_col_breakpoint_map = null | default: xs: gap_col_xs, sm: gap_col_sm, md: gap_col_md, lg: gap_col_lg, xl: gap_col_xl
# Justify w/ Breakpoints
assign justify_xs = justify_xs | default: justify | default: 'start'
assign justify_breakpoint_map = null | default: xs: justify_xs, sm: justify_sm, md: justify_md, lg: justify_lg, xl: justify_xl
# Align w/ Breakpoints
assign align_xs = align_xs | default: align | default: 'stretch'
assign align_breakpoint_map = null | default: xs: align_xs, sm: align_sm, md: align_md, lg: align_lg, xl: align_xl
# Wrap w/ Breakpoints
assign wrap_xs = wrap_xs | default: wrap | default: 'wrap'
assign wrap_breakpoint_map = null | default: xs: wrap_xs, sm: wrap_sm, md: wrap_md, lg: wrap_lg, xl: wrap_xl
%}
<layout-stack class="{{ classlist }}">
<template shadowrootmode="open">
<style>
:host {
display: contents;
}
.layout-stack {
/* Layout & Typography */
--_flex-direction: var(--_flex-direction--xs);
--_justify: var(--_justify--xs);
--_align: var(--_align--xs);
--_margin: var(--layout-stack-margin);
display: flex;
flex-direction: var(--_flex-direction);
gap: var(--_gap);
flex-wrap: var(--_wrap);
justify-content: var(--_justify);
align-items: var(--_align);
text-align: var(--_align);
margin: var(--_margin);
padding: 0;
list-style: none;
::slotted(*) {
max-width: 100%;
}
}
</style>
{% render 'utility.breakpoint-style', property: '_gap', selector: '.layout-stack', value_type: 'property', value_prefix: '--layout-stack-gap-size-', values: gap_breakpoint_map %}
{% render 'utility.breakpoint-style', property: '_row-gap', selector: '.layout-stack', value_type: 'property', value_prefix: '--layout-stack-gap-size-', values: gap_row_breakpoint_map %}
{% render 'utility.breakpoint-style', property: '_column-gap', selector: '.layout-stack', value_type: 'property', value_prefix: '--layout-stack-gap-size-', values: gap_col_breakpoint_map %}
{% render 'utility.breakpoint-style', property: '_flex-direction', selector: '.layout-stack', values: flex_direction_breakpoint_map %}
{% render 'utility.breakpoint-style', property: '_justify', selector: '.layout-stack', values: justify_breakpoint_map %}
{% render 'utility.breakpoint-style', property: '_align', selector: '.layout-stack', values: align_breakpoint_map %}
{% render 'utility.breakpoint-style', property: '_wrap', selector: '.layout-stack', values: wrap_breakpoint_map %}
<{{ as }} class="layout-stack" style="{{ style }}" {{ attributes }}>
<slot></slot>
</{{ as }}>
</template>
{{ slot }}
</layout-stack>
{% javascript %}
class LayoutStack extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.attachShadowRoots(this);
}
attachShadowRoots(element) {
if (this.shadowRoot) {
return;
}
element.querySelectorAll("template[shadowrootmode]").forEach(template => {
const mode = template.getAttribute("shadowrootmode");
const shadowRoot = template.parentNode.attachShadow({ mode });
shadowRoot.appendChild(template.content);
template.remove();
this.attachShadowRoots(shadowRoot);
});
}
}
customElements.define('layout-stack', LayoutStack);
{% endjavascript %}