Aller au contenu

Blocks

A Block is composed of three main components:

  • HTML – Defines the structure and layout of your block.
  • CSS (Optional) – Contains custom styles that apply only within the block.
  • Data Structure – The associated Data Structure that provides the dynamic data for the block.

Before creating or editing the HTML template, you must first select a Data Structure from the ones already defined.


Render Variables

To render a variable, simply wrap its name within double curly braces: {{variableName}}.

For example, given the following Data Structure:

{
  "title$L": "string",
  "subtitle$L": "string",
  "description$PL": "string",
  "imageProduct": "image",
  "button1": "button",
  "button2": "button"
}

You can use these variables directly in your HTML template:

Images

<img src="{{imageProduct}}" alt="mockup">

Text

<h1 class="max-w-2xl mb-4 text-4xl md:text-5xl xl:text-6xl">{{title$L}}</h1>
Note: The variable title$L is a multilingual text variable (see Multilanguage Text). It automatically displays the text in the language currently active in the Website Renderer.

To include a button (or link), reference the variable name within {{}}, followed by a semicolon ; and your HTML code. For example:

{{button1; 
    <button class="buttonColor mr-4 focus:ring-4 font-medium rounded-lg text-base px-5 py-4 me-2 mb-2 focus:outline-none border">
        [text]
    </button>
}}

The button data also includes an icon and description, which can be rendered using [icon] and [description]. For example:

{{button1; 
    <button class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">
        <div class="flex">
            <div class="mr-3">
                <p class="text-lg font-semibold">[text]</p>
                <p class="mt-1 text-sm">[description]</p>
            </div>
            <div class="flex items-center justify-end">
                [icon]
            </div>  
        </div>  
    </button>
}}

If you have already defined custom Button Themes or Link Themes, you can use them instead of writing a custom HTML template.

To use a predefined theme:

{{button1}}

You can then select the desired theme in the “Edit Button Data” form, within the section where the block is used.

Alternatively, you can specify the theme directly in the block’s HTML template:

{{button1; theme=yourThemeName}}

Hidden Function

The hidden feature allows you to conditionally hide a button (or link) based on the value of its type.
To use it, include the hidden=TYPE attribute within the block definition.

Example:

Hide the button when its type is set to Disabled.

{{button1; 
    <button class="buttonColor mr-4 focus:ring-4 font-medium rounded-lg text-base px-5 py-4 me-2 mb-2 focus:outline-none border">
        [text]
    </button>;
    hidden=Disabled;
}}

Groups

If a variable is part of a group, prefix the variable name with the group name followed by a forward slash /.

Example Data Structure

Data Structure

{
    "group1": {
        "title$L": "string",
        "description$PL": "string",
        "nestedGroup": {
            "additionalInfo$PL": "string",
            "price": "number"
        }
    }
}

You can render variables within groups as follows:

  {{group1/title$L}}
  {{group1/nestedGroup/additionalInfo$PL}}

Loops

You can create loops and sub-loops (up to one nested level) within a block.
This feature is especially useful when handling repetitive content, such as galleries with a customizable number of images, or dynamic header/footer menus.

Loops can be defined when the data structure used in a block contains arrays.

Creating a Loop

  • Start a loop: Use {{#each arrayName}}
  • End a loop: Use {{/each arrayName}}
  • Access array items:
  • If the array contains only one variable, use {{arrayName.this}}
  • If the array contains multiple variables, use {{arrayName.variableName}}

All loop content is placed between the opening and closing loop tags.

Accessing Index

You can access the index of a loop or sub-loop using special keywords within {{}}:

  • Use @index to get the index of the current loop.
  • Use @indexParent to get the index of the parent loop when inside a nested loop.

These can be placed anywhere inside the loop block.
(Refer to Example 3 for a practical demonstration.)

Loop Filters

You can render only a specific index of a loop by using the filter= keyword, followed by the desired index number.

Example:

Data Structure

{
  "products": [
    {
      "title$L": "string"
    }
  ]
}

Template

{{#each products; filter=0}}
  <h1>{{products.title$L}}</h1>
{{/each products}}


Loop Filters with Global Data Blocks

When a Block is used as a Global Data Block, you can take advantage of the @indexFilter keyword to dynamically render a specific loop index defined in the section where that block is displayed.

This enables a more flexible CMS setup — you can create multiple blocks that share the same Data Structure, and each block can:

  • Display different variables, and
  • Render only a specific index from an array within the Global Data Block.

Example:

{{#each products; filter=@indexFilter}}
  <h1>{{products.title$L}}</h1>
{{/each products}}

Then in the section where this block is used and is connected to a Global Data Block in the Edit Settings we can set the indexFilter:


Example 1: Single Loop (Array with One Variable)

A simple example of a footer section.

Data Structure

{
  "webSiteName": "string",
  "description$PL": "string",
  "copyrights$PL": "string",
  "links": ["link"]
}

Template

<footer class="p-4 md:p-8 lg:p-10">
  <div class="mx-auto max-w-screen-xl text-center">
    <a href="#" class="flex justify-center items-center text-2xl font-semibold titleColor">
      {{webSiteName}}
    </a>
    <p class="my-6">
      {{description$PL}}
    </p>
    <ul class="flex flex-wrap justify-center items-center mb-6 linkColor">
      {{#each links}}
      <li>
        {{links.this;<a class="mr-4 cursor-pointer hover:underline md:mr-6">[text]</a>}}
      </li>
      {{/each links}}
    </ul>
    <span class="text-sm sm:text-center">
      <a href="#" class="hover:underline">{{webSiteName}}</a> - {{copyrights$PL}}
    </span>
  </div>
</footer>


Example 2: Single Loop (Array with Multiple Variables)

A simple example of a FAQ section (no styling, animation, or script for simplicity).

Data Structure

{
  "faq": [
    {
      "question$L": "string",
      "answer$PL": "string"
    }
  ]
}

Template

<section>
  {{#each faq}}
  <div class="border-b border-slate-200">
    <div>
      {{faq.question$L}}
    </div>
    <div>
      <div class="pb-5 text-sm">
        {{faq.answer$PL}}
      </div>
    </div>
  </div>
  {{/each faq}}
</section>


Example 3: Nested Loop

An example of a footer section containing nested sections and links.

Data Structure

{
  "webSiteName": "string",
  "description$PL": "string",
  "copyrights$PL": "string",
  "footerSections": [
    {
      "name$L": "string",
      "links": ["link"]
    }
  ]
}

Template

<footer class="bg-white dark:bg-gray-900">
  <div class="mx-auto w-full max-w-screen-xl p-4 py-6 lg:py-8">
    <div class="md:flex md:justify-between">
      <div class="mb-6 md:mb-0">
        <a href="#" class="flex items-center">
          <span class="self-center text-2xl font-semibold whitespace-nowrap dark:text-white">
            {{webSiteName}}
          </span>
        </a>
      </div>

      <div class="grid grid-cols-2 gap-8 sm:gap-6 sm:grid-cols-3">
        {{#each footerSections}}
        <div>
          <h2 class="mb-6 text-sm font-semibold text-gray-900 uppercase dark:text-white">
            {{footerSections.name$L}}
          </h2>
          <ul class="text-gray-500 dark:text-gray-400 font-medium">
            {{#each footerSections.links}}
            <li id="{{@indexParent}}.{{@index}}" class="mb-4">
               {{footerSections.links.this;<a class="hover:underline"> [text]</a>}}
            </li>
            {{/each footerSections.links}}
          </ul>
        </div>
        {{/each footerSections}}
      </div>
    </div>

    <hr class="my-6 border-gray-200 sm:mx-auto dark:border-gray-700 lg:my-8" />

    <div class="sm:flex sm:items-center sm:justify-between">
      <span class="text-sm text-gray-500 sm:text-center dark:text-gray-400">
        {{copyrights$PL}}
      </span>
    </div>
  </div>
</footer>


Language Menu

Your can add the language menu anywhere in the block by writing:

{{langMenu}}

You can also use normal buttons/links to change the language.


Theme Switcher (Light/Dark Mode)

Your can add a theme switcher (Dark/Light Mode) anywhere in the block by writing:

{{themeSwitch}}

You can also use normal buttons/links to switch theme.


Use of IF

The Web Builder provides a lightweight conditional syntax called IF that allows you to show, hide, or dynamically insert values directly inside HTML attributes or content.

IF Syntax

{{IF(condition);value_if_true}}

  • condition: A logical expression evaluated by the builder.
  • value_if_true: Inserted only when the condition is true.
  • If the condition is false, nothing is inserted.

Supported operators

  • == — equal
  • != — not equal
  • && — logical AND
  • || — logical OR

Where IF can be used

  • Inside HTML attributes
  • Inside class lists
  • Inside element content
  • Anywhere template interpolation ({{ }}) is allowed

Example

<label class="{{IF(fields.type!='checkbox');'hidden'}} flex items-center h-full cursor-pointer">
    <input 
        id="fieldc-{{@index}}" 
        required="{{IF(fields.type=='checkbox' && fields.required$B==true);'true'}}" 
        type="checkbox" 
        name="{{IF(fields.type=='checkbox');fields.name$L}}" 
        class="{{IF(fields.type!='checkbox');'hidden'}} sr-only peer">
    <span class="custom-checkbox"></span>
    <span class="ml-3 text-sm">{{fields.name$L}}</span>
</label>

CSS

The Web Builder is largely compatible with Tailwind. However, you can also add custom CSS to your block using the CSS editor. You can define your own classes, and a useful feature is the ability to redefine these classes within a Theme's Block—for example, to adjust styling, colors, backgrounds, etc.—to increase the reusability of the block.

Please read CSS Priorities for learning CSS priorities works on the web builder


Special Attributes

onview-animate

If you want to apply classes (for smooth animations) only when a block becomes visible in the viewport, you can use the onview-animate attribute. This attribute can be added to any HTML element.

For example, suppose you’ve defined the following classes in your CSS. Since these classes can be reused across multiple blocks, it’s recommended to define them in the Global CSS rather than within a specific block’s CSS:

@keyframes fRight {
  0% {
    opacity: 0;
    transform: translateX(-20rem);
  }
  100% {
    opacity: 1;
    transform: translateX(0);
  }
}

.fade-right {
  opacity: 0;
  animation: fRight 1s ease-in-out forwards;
}

.animation-delay-010 {
  animation-delay: 0.10s;
}

Then, you can use it in your HTML template like this:

<h1 onview-animate="fade-right animation-delay-010 visible" class="invisible titleColor themeFont-1 max-w-2xl mb-4 text-4xl md:text-5xl xl:text-6xl w-full">
  {{title$L}}
</h1>
In this example, the h1 element is initially invisible. When it enters the viewport (i.e., when the user scrolls to that part of the page), the classes specified in onview-animate are automatically applied, triggering the animation.


Script

Blocks are implemented as HTML5 Web Components, meaning the scripts defined within a block’s HTML template run inside the component’s Shadow DOM. When you use the keyword document in your script, it refers to the Shadow DOM of that specific block—not the global HTML document.

If you need to access the global document (which is generally not recommended as it may cause unexpected behavior), you can use document$ instead of document.

Example

Data Structure

{
  "faq":[{
    "question$L":"string",
    "answer$PL":"string"
  }]
}

Template

<section class="primaryColors secondaryColors neutralColors background hidden-loading pt-14">
    <div class="max-w-screen-xl px-4 pb-8 mx-auto lg:pb-24 lg:px-6 ">
        <div class="max-w-screen-md mx-auto">
            <div id="accordion-flush" data-accordion="collapse" data-active-classes="accordionButtonActiveBg accordionButtonActiveColor" data-inactive-classes="accordionButtonColor">
            {{#each faq}}
              <div class="border-b border-slate-200">
                <button id="wttoggleAccordion{{@index}}" class="question w-full flex justify-between items-center py-5">
                  <span>{{faq.question$L}}</span>
                  <span id="icon-{{@index}}" class="transition-transform duration-300">
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4">
                      <path d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z" />
                    </svg>
                  </span>
                </button>
                <div id="content-{{@index}}" class="answer max-h-0 overflow-hidden transition-all duration-300 ease-in-out">
                  <div class="pb-5 text-sm">
                    {{faq.answer$PL}}
                  </div>
                </div>
              </div> 
              <script>
                document.getElementById("wttoggleAccordion{{@index}}").addEventListener('click', ()=>{toggleAccordion({{@index}})});
              </script>
            {{/each faq}}
            </div>
        </div>
    </div>
</section>

<script>
 function toggleAccordion(index) {
    //console.log("clicked button at index:", index);
    const content = document.getElementById(`content-${index}`);
    const icon = document.getElementById(`icon-${index}`);
    //console.log("content:", content);
    //console.log("icon:",icon);

    // SVG for Minus icon
    const minusSVG = `
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4">
        <path d="M3.75 7.25a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5Z" />
      </svg>
    `;

    // SVG for Plus icon
    const plusSVG = `
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4">
        <path d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z" />
      </svg>
    `;

    // Toggle the content's max-height for smooth opening and closing
    if (content.style.maxHeight && content.style.maxHeight !== '0px') {
      content.style.maxHeight = '0';
      icon.innerHTML = plusSVG;
    } else {
      content.style.maxHeight = content.scrollHeight + 'px';
      icon.innerHTML = minusSVG;
    }
  }
</script>