Build Fluid Themes
Build fast, flexible themes at scale using Liquid templating language, JSON sections, and modern web technologies including HTML, CSS, and JavaScript.
Build a new theme - Create a new theme based on Fluid's root themes (Fluid, Vox, or Base)
Customize a theme - Update the look and feel of an existing theme to tailor it to your company's unique brand and requirements
Table of Contents
- Getting Started
- Key Concepts
- Architecture
- Layouts
- Templates
- Sections
- Blocks
- Settings
- Config
- Locales
- Best Practices
Getting Started
Overview
Fluid themes are a package of template files, building blocks, and supporting assets that define how your company's pages look and function. The theme system provides multiple levels of customization, from simple CSS variable changes to complete template overrides.
Quick Start
- Choose a Root Theme: Select from Fluid, Vox, or Base themes
- Clone for Customization: Create a company-specific copy
- Configure Variables: Set brand colors, fonts, and spacing
- Customize Templates: Override specific page layouts as needed
- Publish: Activate your theme for all users
The theme system handles the technical implementation behind the scenes, allowing you to focus on design and customization.
Key Concepts
Themes vs Application Themes
Legacy Themes - Simple theme selection system for basic switching Application Themes - Full-featured system with template management, versioning, and customization
Theme Hierarchy
- Root Themes: Platform-provided base themes (
fluid,vox,base) - Company Themes: Company-specific themes that clone and customize root themes
- Template Inheritance: Company themes can override specific templates while inheriting others
Template Types
Templates in Fluid themes handle different content types and page layouts:
| Template Type | Purpose | Examples |
|---|---|---|
| Content Templates | Render specific content items | product, medium, library |
| Page Templates | Handle page types | home_page, shop_page, cart_page |
| Layout Components | Provide page structure | navbar, footer, layouts |
| Structural Components | Reusable template parts | sections, components, config |
Architecture
Theme-Company Relationships
Template Resolution Priority
- URL Parameter Override -
?template_id=123(highest priority) - Content-Specific Assignment - Individual content items with assigned templates
- Company Theme Default - Default templates in company's active theme
- Root Theme Fallback - Inherit from root theme if company theme lacks template
Request Flow
User Request → Controller → Template Resolution → Variable Population → Rendering → Response
Layouts
Layouts provide the structural wrapper for your content, defining the overall page structure including HTML document structure, navigation, and footer elements. The layout file (theme.liquid) serves as the foundation of your theme and is located in the layout folder. This file contains:
- Common elements (headers, footers)
- Shared JavaScript files
- Global CSS styles
- Other repeated theme elements
Layout Folder Structure
theme/ ├── layout/ │ ├── theme.liquid ├── .. :
content_for_layout variables
Dynamically returns content based on the current template.
Include {{ content_for_layout }} in your layout files between the <body> and </body> HTML tags.
content_for_header variables
Returns the content of 'app/views/layouts/_theme_header.html.slim' which includes all metadata, scripts, global variables, and other required data by Fluid.
Include {{ content_for_header }} in your layout files between the <head> and </head> HTML tags.
Important: You must include the {{ content_for_header }} and {{ content_for_layout }} in your layout files for the theme to render correctly.
Layout Structure
<!DOCTYPE html> <html lang="{{ request.locale.iso_code }}"> <head> {{ content_for_header }} <style> :root { --primary-color: {{ settings.primary_color }}; --font-family: {{ settings.font_family }}; } </style> </head> <body> <!-- Navigation --> {'%' section 'navbar' '%'} <!-- Main content area --> {{ content_for_layout }} <!-- Footer --> {'%' section 'footer' '%'} <!-- JavaScript --> <script src="{{ 'theme.js' | asset_url }}"></script> </body> </html>
Layout Assignment
Templates can specify their layout using:
Liquid Templates:
{'%' layout 'theme' '%'}
<!-- Template content -->
Custom Layouts
Create specialized layouts for different page types:
<!-- layouts/minimal.liquid --> <!DOCTYPE html> <html> <head> <title>{{ page_title }}</title> {{ theme.global_stylesheet_with_variables | raw }} </head> <body class="minimal-layout"> {{ content_for_layout }} </body> </html>
To use a different layout from the default theme.liquid, add the {'%' layout "custom_layout_name" '%'} tag at on your template file:
{'%' layout "minimal" '%'}
Disabling layout
To render a template without any layout, use {'%' layout none '%'} on your template file:
{'%' layout none '%'}
<!DOCTYPE html>
<html>
<head>
{{ content_for_header }}
</head>
<body>
<h1>Welcome to the Dashboard</h1>
</body>
</html>
You will still have access to content_for_header variable which includes all necessary metadata, scripts and stylesheets required to run the page smoothly.
Templates
Templates define the structure and content for specific page types. Fluid supports both Liquid and JSON template formats.
Liquid Templates
Traditional server-side templating using Liquid syntax:
{'%' comment '%'}
Product page template
{'%' endcomment '%'}
<article class="product-page">
<header class="product-header">
<h1>{{ product.name }}</h1>
<div class="product-price">
{{ product.price | money }}
</div>
{'% if product.tags.size > 0 %'}
<div class="product-tags">
{{ product.tags | join: ', ' }}
</div>
{'% endif %'}
</header>
<div class="product-gallery">
{'% for image in product.images %'}
<img src="{{ image.url | img_url: '600x400' }}"
alt="{{ image.alt }}" />
{'% endfor %'}
</div>
<div class="product-description">
{{ product.description }}
</div>
{'% if product.available %'}
<button class="btn-purchase" data-product-id="{{ product.id }}">
{{ 'products.add_to_cart' | t }}
</button>
{'%' endif '%'}
</article>
Available Template Types
product- Individual product pagesmedium- Media post pagesenrollment_pack- Enrollment pack pageshome_page- Homepage layoutshop_page- Shop/catalog pagescart_page- Shopping cartpage- Custom pagesnavbar- Navigation headerfooter- Page footersections- Reusable sectionslayouts- Page wrapper layoutscomponents- Resuable blocksjoin_page- List of enrollment packs pagecollection_page- List of collections pagecollection- Individual collection pagecategory_page- List of categories page
Navbar and Footer templates
Navbar and Footer templates are located in the navbar and footer folders respectively. They are referenced in the layout as:
{'%' section 'navbar' '%'}
{'%' section 'footer' '%'}
theme/ : ├── navbar/ │ ├── default/ │ │ ├── index.liquid │ │ └── styles.css ├── footer/ │ ├── default/ │ │ ├── index.liquid │ │ └── styles.css └── ... :
Sections
Sections are reusable components that can be added to templates. Each section has its own Liquid template and JSON schema defining available settings.
Section Template Structure
<!-- sections/product_header.liquid --> <div class="product-header" style="background-color: {{ section.settings.background_color }};"> <div class="product-header-content"> {'%' if section.settings.show_vendor and product.vendor '%'} <div class="product-vendor">{{ product.vendor }}</div> {'%' endif '%'} <h1 class="product-title product-title--{{ section.settings.title_size }}"> {{ product.name }} </h1> {'%' if section.settings.show_price '%'} <div class="product-price"> {{ product.price | money }} </div> {'%' endif '%'} </div> </div> {'%' schema '%'} { "name": "Product Header", "settings": [ { "type": "checkbox", "id": "show_vendor", "label": "Show vendor", "default": true }, { "type": "checkbox", "id": "show_price", "label": "Show price", "default": true }, { "type": "select", "id": "title_size", "label": "Title size", "options": [ {"value": "small", "label": "Small"}, {"value": "medium", "label": "Medium"}, {"value": "large", "label": "Large"} ], "default": "medium" }, { "type": "color", "id": "background_color", "label": "Background color", "default": "#ffffff" } ] } {'%' endschema '%'}
Using Sections in Templates
{'%' section 'main_product', id: 'main_product' '%'}
{'%' section 'related_products', id: 'related_products' '%'}
{'%' schema '%'}
{
"sections": {
"main_product": {
"type": "main_product",
"settings": {
"show_breadcrumb": true
}
},
"related_products": {
"type": "related_products",
"settings": {
"heading": "Products",
"heading_size": "h1"
}
}
}
}
{'%' endschema '%'}
Section Processing
- Section Resolution: Each section
typemaps to a Liquid template - Settings Injection: Section settings merged with schema defaults
- Block Processing: Dynamic blocks processed with individual settings
- Template Rendering: Section template rendered with populated variables
Blocks
Blocks are dynamic content elements within sections that can be added, removed, and reordered. They provide flexibility for content creators to customize sections.
Block Definition in Schema
{ "name": "Testimonials Section", "settings": [ { "type": "text", "id": "section_title", "label": "Section Title", "default": "What Our Customers Say" } ], "blocks": [ { "type": "testimonial", "name": "Customer Testimonial", "limit": 10, "settings": [ { "type": "text", "id": "customer_name", "label": "Customer Name" }, { "type": "textarea", "id": "testimonial_text", "label": "Testimonial" }, { "type": "image_picker", "id": "customer_photo", "label": "Customer Photo" } ] } ] }
Using Blocks in Templates
{ "sections": { "testimonials": { "type": "testimonials_section", "settings": { "section_title": "Customer Reviews" }, "blocks": { "testimonial_1": { "type": "testimonial", "settings": { "customer_name": "John Doe", "testimonial_text": "Great product!", "customer_photo": "photo.jpg" } } }, "block_order": ["testimonial_1"] } } }
Block Rendering in Liquid
<!-- sections/testimonials_section.liquid --> <div class="testimonials"> <h2>{{ section.settings.section_title }}</h2> {'%' for block in section.blocks '%'} <div class="testimonial" data-fluid-section-block-id="{{ block.id }}" data-fluid-section-block-type="{{ block.type }}"> {'%' case block.type '%'} {'%' when 'testimonial' '%'} <blockquote>{{ block.settings.testimonial_text }}</blockquote> <cite>{{ block.settings.customer_name }}</cite> {'%' when 'divider' '%'} <hr class="testimonial-divider"> {'%' endcase '%'} </div> {'%' endfor '%'} </div>
Block Characteristics
- Dynamic Quantity: Add/remove blocks as needed
- Reorderable: Drag blocks to change sequence
- Type-Specific: Different block types have different settings
- Limited: Optional
limitproperty restricts maximum blocks
Settings
Settings define configurable options for themes, sections, and blocks. They automatically generate form interfaces for customization.
Setting Types
| Type | Description | Example |
|---|---|---|
text | Single-line text input | Product title, headings |
textarea | Multi-line text input | Descriptions, long content |
select | Dropdown with options | Layout styles, sizes |
checkbox | Boolean toggle | Show/hide elements |
color | Color picker | Brand colors, backgrounds |
range | Slider with min/max | Font sizes, spacing |
number | Numeric input | Quantities, dimensions |
url | URL input with validation | Links, external resources |
image_picker | Asset selection | Images, icons |
Theme-Level Settings (CSS Variables)
{ "primary_color": "#2c5aa0", "secondary_color": "#f8f9fa", "font_family": "Inter, sans-serif", "heading_font": "Playfair Display, serif", "border_radius": "8px", "container_width": "1200px" }
Section Settings Example
{ "name": "Hero Section", "settings": [ { "type": "text", "id": "heading", "label": "Hero Heading", "default": "Welcome to Our Store" }, { "type": "textarea", "id": "description", "label": "Hero Description", "default": "Discover amazing products" }, { "type": "select", "id": "layout", "label": "Layout Style", "options": [ {"value": "centered", "label": "Centered"}, {"value": "left", "label": "Left Aligned"}, {"value": "right", "label": "Right Aligned"} ], "default": "centered" }, { "type": "color", "id": "background_color", "label": "Background Color", "default": "#ffffff" }, { "type": "range", "id": "padding", "label": "Section Padding", "min": 0, "max": 100, "step": 10, "unit": "px", "default": 40 }, { "type": "checkbox", "id": "show_button", "label": "Show Call-to-Action Button", "default": true }, { "type": "image_picker", "id": "background_image", "label": "Background Image" } ] }
Using Settings in Templates
<section class="hero hero--{{ section.settings.layout }}" style="background-color: {{ section.settings.background_color }}; padding: {{ section.settings.padding }}px 0;"> {'%' if section.settings.background_image '%'} <div class="hero-background"> <img src="{{ section.settings.background_image | img_url: '1920x1080' }}" alt="Hero background" /> </div> {'%' endif '%'} <div class="hero-content"> <h1>{{ section.settings.heading }}</h1> <p>{{ section.settings.description }}</p> {'%' if section.settings.show_button '%'} <a href="#" class="btn btn-primary">Learn More</a> {'%' endif '%'} </div> </section>
Config
Configuration templates define global theme settings and provide structured data for theme customization.
Theme Configuration Structure
{ "name": "Fluid Theme Configuration", "settings": [ { "type": "header", "content": "Colors" }, { "type": "color", "id": "primary_color", "label": "Primary Color", "default": "#2c5aa0" }, { "type": "color", "id": "secondary_color", "label": "Secondary Color", "default": "#6c757d" }, { "type": "header", "content": "Typography" }, { "type": "font_picker", "id": "font_family", "label": "Body Font", "default": "Inter, sans-serif" }, { "type": "font_picker", "id": "heading_font", "label": "Heading Font", "default": "Playfair Display, serif" }, { "type": "header", "content": "Layout" }, { "type": "range", "id": "container_width", "label": "Container Max Width", "min": 960, "max": 1600, "step": 40, "unit": "px", "default": 1200 }, { "type": "select", "id": "border_radius", "label": "Border Radius Style", "options": [ {"value": "0px", "label": "Square"}, {"value": "4px", "label": "Slightly Rounded"}, {"value": "8px", "label": "Rounded"}, {"value": "16px", "label": "Very Rounded"} ], "default": "8px" } ] }
Global Settings Access
<!-- Access theme variables in any template --> <div class="container" style="max-width: {{ settings.container_width }};"> <h1 style="font-family: {{ settings.heading_font }}; color: {{ settings.primary_color }};"> {{ page.title }} </h1> </div>
CSS Variable Integration
<!DOCTYPE html> <html> <head> {{ content_for_header }} <style> :root { --primary-color: {{ settings.primary_color }}; --secondary-color: {{ settings.secondary_color }}; --font-family: {{ settings.font_family }}; --heading-font: {{ settings.heading_font }}; --border-radius: {{settings.border_radius }}; --container-width: {{ settings.container_width }}; } </style> </head> <body> {{ content_for_layout }} </body> </html>
.container { max-width: var(--container-width); font-family: var(--font-family); } .btn-primary { background-color: var(--primary-color); border-radius: var(--border-radius); }
Locales
Localization support enables themes to display content in multiple languages and adapt to different regional preferences.
Translation Files Structure
Fluid themes use JSON locale files organized by components and sections for better maintainability:
// locales/en.json { "cart_page": { "checkout": "Checkout", "order_summary": "Order Summary", "shipping": "Shipping estimate", "ships_in": "Ships in 3-5 business days", "shopping_cart": "Shopping Cart", "subtotal": "Subtotal", "tax": "Tax estimate", "total": "Order total" }, "components": { "comment_form": { "email": "Email", "full_name": "Full Name", "leave_info": "Please leave your information so {{ company }} will know who you are. You only need to do this once.", "placeholder": "Add your comment", "post": "Post", "want_to_leave_comment": "Want to leave a comment?" } }, "navbar": { "button_1": "Button 1", "button_2": "Button 2", "countries": "Countries", "country": "Country", "language": "Language", "shop": "Shop", "enrollments": "Enrollments", "view_my_site": "View MySite" }, "product": { "add_to_cart": "Add to Cart", "buy_now": "Buy Now", "details": "Details", "free_shipping": "Free Shipping over {{amount}}", "quantity": "Quantity", "reviews": "{{reviews}} reviews", "stars": "{{stars}} stars", "one_time_purchase": "One time purchase", "subscribe": "Subscribe" }, "shared": { "button": "Button", "medium_heading": "Medium length heading goes here", "short_heading": "Short heading here", "tagline": "Tagline", "view_all": "View All" } }
// locales/es.json { "cart_page": { "checkout": "Finalizar Compra", "order_summary": "Resumen del Pedido", "shipping": "Estimación de envÃo", "ships_in": "Se envÃa en 3-5 dÃas hábiles", "shopping_cart": "Carrito de Compras", "subtotal": "Subtotal", "tax": "Estimación de impuestos", "total": "Total del pedido" }, "components": { "comment_form": { "email": "Correo electrónico", "full_name": "Nombre completo", "leave_info": "Por favor deja tu información para que {{ company }} sepa quién eres. Solo necesitas hacer esto una vez.", "placeholder": "Añade tu comentario", "post": "Publicar", "want_to_leave_comment": "¿Quieres dejar un comentario?" } }, "navbar": { "button_1": "Botón 1", "button_2": "Botón 2", "countries": "PaÃses", "country": "PaÃs", "language": "Idioma", "shop": "Tienda", "enrollments": "Inscripciones", "view_my_site": "Ver Mi Sitio" }, "product": { "add_to_cart": "Añadir al Carrito", "buy_now": "Comprar Ahora", "details": "Detalles", "free_shipping": "EnvÃo gratis por encima de {{amount}}", "quantity": "Cantidad", "reviews": "{{reviews}} reseñas", "stars": "{{stars}} estrellas", "one_time_purchase": "Compra única", "subscribe": "Suscribirse" }, "shared": { "button": "Botón", "medium_heading": "Encabezado de longitud media aquÃ", "short_heading": "Encabezado corto aquÃ", "tagline": "Eslogan", "view_all": "Ver Todo" } }
Using Translations in Templates
<!-- Basic translation --> <button class="btn-primary">{{ 'product.add_to_cart' | t }}</button> <!-- Translation with variables --> <p class="price">{{ 'product.free_shipping' | t: amount: '$50' }}</p> <!-- Component-based translations --> <div class="comment-form"> <label>{{ 'components.comment_form.email' | t }}</label> <input type="email" placeholder="{{ 'components.comment_form.placeholder' | t }}"> <button>{{ 'components.comment_form.post' | t }}</button> </div> <!-- Navigation translations --> <nav class="navbar"> <a href="/shop">{{ 'navbar.shop' | t }}</a> <a href="/enrollments">{{ 'navbar.enrollments' | t }}</a> <button>{{ 'navbar.button_1' | t }}</button> </nav> <!-- Cart page translations --> <div class="cart-summary"> <h2>{{ 'cart_page.shopping_cart' | t }}</h2> <div class="total">{{ 'cart_page.total' | t }}: ${{ cart.total }}</div> <button>{{ 'cart_page.checkout' | t }}</button> </div>
Assets
Theme assets are stored in the theme's assets folder. These files are specific to the theme and support its functionality and appearance.
Asset Folder Structure
theme/ ├── assets/ │ ├── custom.js │ ├── banner.jpg │ ├──logo.png │ └──... │
Note: For files that need to be accessed across multiple themes, you can use global files stored in Sidenav > Website > Files.
File Types
Assets can be categorized into two types:
Binary Files
- Can only be uploaded, not edited
- Examples: images, fonts, videos
- Upload only through file selection
Non-Binary Files
- Can be uploaded or created from scratch
- Can be edited after creation
- Examples:
- CSS files
- JavaScript files
- JSON files
- Creation options:
- Upload existing file
- Create blank file with proper extension
Referencing Assets
All files uploaded to the assets folder are automatically processed through Filestack. A CDN URL is generated for each file.These URLs can be referenced throughout your theme
To reference assets in your theme files, use the asset_url filter:
{{ 'filename.ext' | asset_url }}
Example
<link rel="stylesheet" href="{{ 'custom.css' | asset_url }}"> <script src="{{ 'custom.js' | asset_url }}"></script> <img src="{{ 'banner.jpg' | asset_url }}" alt="Banner">
Best Practices
Performance Optimization
Image Optimization:
<!-- Responsive images with proper sizing --> <img src="{{ image.url | img_url: '600x400' }}" srcset="{{ image.url | img_url: '300x200' }} 300w, {{ image.url | img_url: '600x400' }} 600w, {{ image.url | img_url: '1200x800' }} 1200w" sizes="(max-width: 768px) 300px, (max-width: 1200px) 600px, 1200px" alt="{{ image.alt }}" loading="lazy" />
CSS and JavaScript Loading:
<!-- Critical CSS inline, non-critical async --> <style> /* Critical above-the-fold styles */ .header, .hero { /* styles */ } </style> <link rel="preload" href="{{ 'theme.css' | asset_url }}" as="style" onload="this.onload=null;this.rel='stylesheet'"> <!-- Defer non-critical JavaScript --> <script src="{{ 'theme.js' | asset_url }}" defer></script>
Accessibility
Semantic HTML:
<article class="product" role="main"> <header> <h1>{{ product.name }}</h1> </header> <nav aria-label="Product images"> <!-- Image gallery --> </nav> <section aria-label="Product information"> <p>{{ product.description }}</p> </section> </article>
ARIA Labels and Screen Reader Support:
<button class="btn-cart" aria-label="{{ 'products.add_to_cart' | t }}: {{ product.name }}" data-product-id="{{ product.id }}"> <span aria-hidden="true">🛒</span> {{ 'products.add_to_cart' | t }} </button> <div class="price" aria-label="{{ 'products.price' | t }}"> <span class="sr-only">{{ 'products.price' | t }}: </span> {{ product.price | money }} </div>
Mobile-First Design
<!-- Mobile-optimized navigation --> <nav class="navbar"> <div class="navbar-brand"> <a href="/">{{ company.name }}</a> </div> <button class="navbar-toggle" aria-label="{{ 'general.menu' | t }}" data-toggle="mobile-menu"> <span class="hamburger-icon"></span> </button> <div class="navbar-menu" id="mobile-menu"> <!-- Navigation items --> </div> </nav>
Version Control
Theme Development Workflow:
- Create feature branches for template changes
- Test changes in preview mode before publishing
- Use version control for template content
- Maintain audit trails for customizations
Template Organization:
themes/
├── layouts/
│ ├── theme.liquid
│ └── minimal.liquid
├── templates/
│ ├── product.liquid
│ ├── home_page.json
│ └── cart_page.liquid
├── sections/
│ ├── product_header.liquid
│ ├── product_gallery.liquid
│ └── testimonials.liquid
└── config/
└── settings_schema.json
Liquid Filters Reference
Fluid themes provide powerful Liquid filters for processing and formatting data:
Image Processing:
{{ image.url | img_url: '300x200' }} <!-- Resize to 300x200 -->
{{ image.url | img_url: '300x200', crop: 'center' }} <!-- Crop from center -->
Text Processing:
{{ text | truncate: 150 }} <!-- Limit to 150 characters -->
{{ text | strip_html }} <!-- Remove HTML tags -->
{{ text | capitalize }} <!-- Capitalize first letter -->
Formatting:
{{ price | money }} <!-- Format as currency -->
{{ date | date: '%B %d, %Y' }} <!-- Format date -->
{{ number | number }} <!-- Format number with commas -->
Arrays and Objects:
{{ array | join: ', ' }} <!-- Join array elements -->
{{ array | first }} <!-- Get first element -->
{{ array | size }} <!-- Get array length -->
Asset Management:
{{ 'theme.css' | asset_url }} <!-- Get theme asset URL -->
{{ 'logo.png' | asset_url }} <!-- Get image asset URL -->
JSON:
{{ object | json }} <!-- Convert object to JSON string -->
Translation:
{{ 'general.welcome' | t }} <!-- Translate using locale file -->
{{ 'cart.items_count' | t: count: 3 }} <!-- Pass variables to translation -->