08/19/2022

CMS Pages with Page Builder and Hyva

Author: Tomasz

Page Builder comes with a set of tools that helps you build your storefront pages. The big advantage for you as a developer is the possibility to create new content types. It lets you to customize Page Builder to suit your needs. In our case we would like to create a new control with the help of the Adobe documentation and use Tailwind CSS to style our control. It retains all the benefits of using Hyvä Themes.

About Page Builder

Page Builder is the default content management system that comes with Magento Adobe Commerce. It has an easy-to-use interface that requires no technical knowledge about coding. The drag-and-drop page design allows to rapidly create pages, update them and maintain responsive design. The live preview allows seeing how the content will look on the storefront. As a result, store owners and managers are able to build pages independently, save time and keep updated content.

Everything we need to use Page Builder on our project is Magento updated up to version 2.4.3. Using this tool does not force you to rebuild all existing pages because the content created in WYSIWYG will be transferred to Page Builder as HTML Code. It is built-in content type. We are still able to edit our previous content.

Page Builder “HTML Code”

Editing content

Hyva Themes

Hyvä Themes is a new way to build out storefronts for Magento 2. Hyvä uses two modern dependencies: Tailwind CSS and Alpine.js. In effect, the Magento frontend is very light and our site loads faster. This is important because performance affects the SEO ranking.

I recommend updating your Hyvä version to 1.1.9 otherwise you will need to install an additional Page Builder module for compatibility with Hyvä. So now we can use Page Builder with Hyvä and keep all the benefits. If you would like to find out more information about Page Builder and Hyvä Themes, just watch “Hyvä Meetup”. John Hughes explains how the module for Hyvä works, mentioning rebuilt styles, keeping performance benefits, and overriding content types.

Let’s have a look at how we can create our own content type. For this, we are going to use an example from the Page Builder documentation. Please follow the instructions and compare with my code. I hope my advice helps you build Quote controls quicker.

1. Introduction

We start with short information about our example. The first step to building a content type is to create a new module, located in the directory app/code. If you use Visual Studio Code I recommend you use the MagentoWizard extension for that.

This extension automates some repeating tasks. In this section, we will find out more about the next creation steps. Pay attention to the file structure. There are many catalogs and it is very easy to make a mistake. You do not need all of them. We are going to pick only what is needed to create the first control in order not to make it complicated.

2. Configuration

Configuration files include a bunch of things. You are going to know them deeper but for now, compare my configuration with that from the documentation.

view/adminhtml/pagebuilder/content_type/example_quote.xml

1 <?xml version="1.0"?>
2 <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_PageBuilder:etc/content_type.xsd">
3    <type name="example_quote"
4        label="Quote"
5        menu_section="elements"
6        component="Magento_PageBuilder/js/content-type"
7        preview_component="Magento_PageBuilder/js/content-type/preview"
8        master_component="Magento_PageBuilder/js/content-type/master"
9        form="pagebuilder_example_quote_form"
10        icon="icon-pagebuilder-heading"
11        sortOrder="21"
12        translate="label">
13    <children default_policy="deny"/>
14    <parents default_policy="deny">
15      <parent name="column" policy="allow"/>
16    </parents>
17    <appearances>
18        <appearance name="default"
19                  default="true"
20                  preview_template="Example_PageBuilderQuote/content-type/example_quote/default/preview"
21                  master_template="Example_PageBuilderQuote/content-type/example_quote/default/master"
22                  reader="Magento_PageBuilder/js/master-format/read/configurable">
23        <elements>
24        </elements>
25        </appearance>
26    </appearances>
27    </type>
28 </config>

Our configuration is very similar. I changed preview_component in Magento_PageBuilder/js/content-type/preview in line 7 to keep this example simple. You can find out more information about components in the section Add components and consider using them.

Please pay attention to lines 20 and 21, and make sure your paths are correct, it is very easy to make a mistake in a nested catalog.

3. Add templates

As the documentation says, appearance defines how the content type appears on the Admin stage preview.html and the storefront master.html. In our case, we have got the default appearance but we are not limited to using only one. We can create more, it depends on what effect we want to get. It makes content types more flexible. Notice the appearance name is the default one, which means our templates are located in the default catalog. I skipped other appearances, as a result, we have got fewer files and catalogs.

Let’s compare preview templates.

view/adminhtml/web/template/content-type/example-quote/default/preview.html

1 <div attr="data.main.attributes"
2     ko-style="data.main.style"
3     class="pagebuilder-content-type"
4     css="data.main.css"
5     event="{ mouseover: onMouseOver, mouseout: onMouseOut }, mouseoverBubble: false">
6    <render args="getOptions().template"></render>
7    <blockquote attr="data.quote.attributes"
8         ko-style="data.quote.style"
9         class="quote"
10         css="data.quote.css"
11         data-bind="liveEdit: { field: 'quote_text', placeholder: $t('Enter Quote') }"></blockquote>
12    <div
13        class="quote-author"
14        attr="data.author.attributes"
15        ko-style="data.author.style"
16        css="data.author.css"
17        data-bind="liveEdit: { field: 'quote_author', placeholder: $t('Enter Authorss') }"></div>
18    <div 
19        class="quote-title"
20        attr="data.author_title.attributes"
21        ko-style="data.author_title.style"
22        css="data.author_title.css"
23        data-bind="liveEdit: { field: 'quote_author_desc', placeholder: $t('Enter Description') }"></div>
24 </div>

I changed the closing tags from <render args="getOptions().template" /> to <render args="getOptions().template"></render> in line 7. I will explain this in Common Issues below. If you jump to the Add styles section for a moment you can see the data flow between the template, configuration, and form field.

4. Add components

We use default preview and master components in the file example_quote.xml in lines 7 and 8. In our case, we will skip this step, but I am convinced you will come back here later because this section shows how we can customize the options menu for each content type. How does it work? We just define our own preview component in example_quote.xml to extend the default component.

5. Add a form

This section provides a lot of information but it is very important. When we add a form we allow the user to customize the entire control and our content. Follow the instructions in the documentation. This part is clear. You need to create new files and update example_quote.xml.

Data flow

Before we start styling, I would like you to pay attention to the data flow. The diagram in the Add styles section is an example of how CSS binding works. In Add templates we can read some information about the node’s attributes in templates. How do they correspond with elements from the configuration and form fields. It is easy to make a mistake. I suggest you focus on this case. It lets you avoid issues with data binding or loading templates. I encourage you to create a new element on your own. I hope the diagram below helps you imagine how it works.

6. Add styles

I mentioned we want to use Tailwind CSS instead of Less CSS on the frontend. We keep admin styles using fewer CSS files based on the documentation but in my file, I renamed the CSS classes and changed the colors a little bit. Their names correspond with Tailwind CSS classes. In the preview Page Builder uses styles from adminhtml/web/css/source/content-type/example-quote/_import.less. On the frontend we use Tailwind CSS so our additional classes affects the design. You can see my changes below:

adminhtml/web/css/source/content-type/example-quote/_import.less

1 blockquote {
2    display: block;
3    font-size: 1.2em;
4    margin: 1em;
5    padding: 0.5em 10px;
6    quotes: "\201C" "\201D" "\2018" "\2019";
7    text-decoration: none;
8    word-break: normal !important;
9    line-height: 1.5;
10    padding: 0;
11    font-weight: 300;
12    &:before {
13      content: open-quote;
14      font-size: 2.6em;
15      margin-right: 0.14em;
16      vertical-align: -0.4em;
17      line-height: 0;
18      margin-left: -0.45em;
19      font-weight: 300;
20    }
21    &:after {
22      content: close-quote;
23      font-size: 0;
24      line-height: 0;
25      margin-left: 0;
26    }
27 }
28 div {
29  &.quote-author {
30    padding-right: 30px;
31    font-size: 14px;
32    color: #666666;
33    margin-left: 1.1em;
34    word-break: normal !important;
35  }
36  &.quote-title {
37    padding-right: 30px;
38    font-size: 14px;
39    color: #999999;
40    margin-left: 1.1em;
41    font-weight: 300;
42    word-break: normal !important;
43  }
44 }
45.text-black-500 { color: #000000; &:before { color: #000000; } }
46.text-blue-500 { color: #3B82F6; &:before { color: #3B82F6; } }
47.text-green-500 { color: #22C55E; &:before { color: #22C55E; } }
48.text-red-500 { color: #EF4444; &:before { color: #EF4444; } }
49.text-purple-500 { color: #A808F7; &:before { color: #A808F7; } }

Now we have to change the values for the options in pagebuilder_example_quote_form.xml, as shown in the snippet below:

1 <field name="quote_css" sortOrder="40" formElement="select">
2    <argument name="data" xsi:type="array">
3        <item name="config" xsi:type="array">
4            <item name="default" xsi:type="string">text-black-500</item>
5        </item>
6    </argument>
7    <settings>
8        <dataType>text</dataType>
9        <label translate="true">Quote Color</label>
10    </settings>
11    <formElements>
12        <select>
13            <settings>
14                <options>
15                    <option name="0" xsi:type="array">
16                        <item name="value" xsi:type="string">text-black-500</item>
17                        <item name="label" xsi:type="string" translate="true">Black</item>
18                    </option>
19                    <option name="1" xsi:type="array">
20                        <item name="value" xsi:type="string">text-blue-500</item>
21                        <item name="label" xsi:type="string" translate="true">Blue</item>
22                    </option>
23                    <option name="2" xsi:type="array">
24                        <item name="value" xsi:type="string">text-green-500</item>
25                        <item name="label" xsi:type="string" translate="true">Green</item>
26                    </option>
27                    <option name="3" xsi:type="array">
28                        <item name="value" xsi:type="string">text-red-500</item>
29                        <item name="label" xsi:type="string" translate="true">Red</item>
30                    </option>
31                    <option name="4" xsi:type="array">
32                        <item name="value" xsi:type="string">text-purple-500</item>
33                        <item name="label" xsi:type="string" translate="true">Purple</item>
34                    </option>
35                </options>
36            </settings>
37        </select>
38    </formElements>
39 </field>

Now we have to create styles for the frontend. Tailwind CSS will recognize the color classes and add styles. We have to deal with the rest. I recommend creating a new CSS component named page-builder-quote.css in {PATH_TO_YOUR_THEME}/web/tailwind/components/ . We can omit Less CSS files in the catalogs frontend/web/css/source/content-type/example-quote/ and frontend/web/css/source/content-type/. You can remove them and any unneeded catalog. In PATH_TO_YOUR_THEME}/web/tailwind/tailwind-source.css add page-builder-quote.css using @import url(components/page-builder-quote.css);

To write styles for the frontend we use nesting CSS syntax like in Less files and Tailwind’s @apply directive. We don’t focus on styling in our case but I prepared some styles to show you what it may look like.

page-builder-quote.css

1 blockquote {
2    quotes: "\201C" "\201D" "\2018" "\2019";
3    @apply  block p-6 text-2xl no-underline break-normal leading-6 font-light; 
4    &:before {
5      content: open-quote;
6      @apply relative left-0 top-4 -ml-4 font-light text-5xl;
7    }
8    &:after {
9      content: close-quote;
10      font-size: 0;
11      line-height: 0;
12      @apply ml-0
13    }
14 }
15 div {
16  &.quote-author {
17    @apply pl-2 text-base font-semibold text-gray-600;
18  }
19  &.quote-title {
20    @apply pl-2 text-sm text-gray-400;
21  }
22 }

In summary, we use less CSS for styling our preview in the admin panel but for the frontend we use a stylesheet to add Tailwind CSS classes. We used the css attribute to add a class to a quote. You may notice we have access to the HTML structure in master.html. It means we can directly add Tailwind CSS classes to nodes. It is another way to apply styles. As a result, we write fewer styles in page-builder-quote.css.

Example of a master template.

1 <div attr="data.main.attributes" ko-style="data.main.style" css="data.main.css">
2   <blockquote class="quote-text block p-6 text-2xl no-underline break-normal leading-6 font-light" 
3               attr="data.quote.attributes" 
4               css="data.quote.css" 
5               ko-style="data.quote.style" 
6               html="data.quote.html">
7   </blockquote>
8   <div class="quote-author pl-2 text-base font-semibold text-gray-600" 
9        attr="data.author.attributes" 
10        ko-style="data.author.style" 
11        css="data.author.css" 
12        html="data.author.html">
13   </div>
14   <div class="quote-title pl-2 text-sm text-gray-400"
15        attr="data.author_title.attributes"
17        css="data.author_title.css"
18        html="data.author_title.html">
19   </div>
20 </div>

However, this solution has one drawback. If we save some content on our page and we want to edit it again we get a reconverted master template from the database in the preview. It means all classes will be added to nodes. It does not affect the preview because their classes are not recognized on the admin panel.

Effect

Preview

Example of the frontend

We have got almost the same view on the admin panel and the frontend. Our content type is done. We just need to create an icon and add it to the configuration file. Try to edit our control, for example by adding a custom toolbar for the quoted text. I recommend creating a few simple content types and then trying to extend them. The Adobe documentation includes topics that will help you expand your knowledge. You can check Page Builder examples to learn more from source codes.

I give you access to our own repository with this example to compare.

Custom content types are a very powerful feature of Page Builder. We are not limited to using built-in tools. You can build content types customized to your project. That will definitely improve your work.

Common issues

In my case, I use PHP 7.4 and Magento 2.4.4. When I tried to add a content type by dragging on the stage, it did not appear. I was still able to see the HTML structure when I inspected the page. To solve the issue I eliminated self-closing tags. You can read more in this thread on Github.

Related Posts

Contact with: Tomasz