Code Culture for WordPress templating
General Principles
Maximum readability and clarity.
Integrity and generally accepted rules of writing.
Avoiding duplicate code.
Minimize SCSS.
Make the most of styles with Tailwind. If none of the styles correspond to what is in the figma, use the most similar one.
Allowable inaccuracy is: 10%.Isolation of elements.
This is necessary to avoid conflicts between components.
Example:
the grid must be wrapped in a container. Grid settings are written to the grid, and everything else ( margin, padding, background, etc. ) is written in the wrapper of this grid
What will it give us?
- Easily maintainable code
- Good understanding of other developers' code
- Easy onboarding of new developers
- Avoiding unexpected bugs by avoiding conflicts between components
Code Principles
Grids
It is very important to choose the most suitable method of creating grids.
List of options and situations where we use them:
- Grid - For grids with similar, proportional elements such as cards

- Flex - For grids with different content, but having no more than one row and changing orientation to column at the nearest required breakpoint. A good example of application is the Media + Text component

- Flex Offset - For the most complex grids, with columns of different proportions with different content and several rows of these columns.

Includes
- Each include should to have own wrapper
- When you pass more than one parameter, write params in a column
- The value of the parameter and the colon must be on the same level as level of colon & value in param with the longest param name.
- The colon and the value must be separated by a spaces
- Add a line break before and after the parameter column
✅ Correct
<div class="{{ component_class }}__image-wrapper">
{% include '01-atoms/images/image-a/image-a-1/image-a-1.twig' with {
parent_class : component_class,
img : img,
lazyload : true,
extra_class : image_class
} only %}
</div>
❌ Wrong
{% include '01-atoms/images/image-a/image-a-1/image-a-1.twig' with {
parent_class : component_class,
img : img,
lazyload : true,
extra_class : image_class
} only %}
❌ Wrong
{% include '01-atoms/images/image-a/image-a-1/image-a-1.twig' with { parent_class : component_class, img : img, lazyload : true, extra_class : image_class } only %}
Images Includes
We need to use aspect ratio for images everywhere where is possible in order to avoid situation when source images with different size proportion breaks our layout
- Add Tailwind Aspect Ratio to each image wrapper.
- Pass to include the next classes
w-full h-full object-cover
✅ Correct
<div class="{{ component_class }}__img-wrapper aspect-4/3"
data-aos="fade"
data-aos-easing="ease-in"
data-aos-delay="250"
>
{# Accessing WordPress image object and making them available to Twig templates #}
{% set img = TimberImage( section.image ) %}
{# Passing this to the actual image tag to make ratio work #}
{% set image_class = 'w-full h-full object-cover' %}
{% include '01-atoms/images/image-a/image-a-1/image-a-1.twig' with {
parent_class : component_class,
img : img,
lazyload : true,
extra_class : image_class
} only %}
</div>
❌ Wrong
<div class="{{ component_class }}__img-wrapper"
data-aos="fade"
data-aos-easing="ease-in"
data-aos-delay="250"
>
{# Accessing WordPress image object and making them available to Twig templates #}
{% set img = TimberImage( section.image ) %}
{# Passing this to the actual image tag to make ratio work #}
{% set image_class = 'w-full' %}
{% include '01-atoms/images/image-a/image-a-1/image-a-1.twig' with {
parent_class : component_class,
img : img,
lazyload : true,
extra_class : image_class
} only %}
</div>
Repeaters
Required elements of each repeater must be repeater, repeater-inner, repeater-item
✅ Correct
<div class="{{ component_class }}__repeater">
<div class="{{ component_class }}__repeater-inner grid gap-4 grid-cols-1 lg:grid-cols-2">
{% for item in items %}
<div class="{{ component_class }}__repeater-item">
{% include '02-molecules/cards/card-post-a/card-post-a-1.twig' with { item : item } only %}
</div>
{% endfor %}
</div>
</div>
❌ Wrong
<div class="{{ component_class }}__repeater">
{% for item in items %}
{% include '02-molecules/cards/card-post-a/card-post-a-1.twig' with { item : item } only %}
{% endfor %}
</div>
ACF
When Organism has repeater don't remove any output options
Folder structure
|── src - Main Frontend
|
| |── global - Global files
| |
| | |── js - Global JS
| | |── styles - Global SCSS
| | |
| | | |── base - SCSS Variables, Config of Fonts, Controls, Spaces, Animations, etc
| | | |── layout - Styles for the main layout of website
| | | |── vendors - Styles of libraries
| | |
| | |── twig - Global TWIG components
| | |
| | | |── base-banner - Banner which is used on each page
| | | |── base-cta - CTA which is used on each page
| | | |── base-html-head - Start of HTML with HEAD content
| |
| |── patterns - Components
| |
| | The components are built according to the atomic structure. Templates are built from organisms, organisms from molecules, molecules from atoms.
| |
| | The rule for the names of components (the rule was not followed everywhere, but for new components it must be done).
| |
| | [component-name]-[type( a, b, c, etc.)] - Component name with type
| |
| | [component-name]-[type]-[version( 1, 2, 3, etc. )] - Component version
| |
| | --------------------------------------------------------------------------
| | |
| | |── 01-atoms - Atoms ( Typography, Links, Buttons, Icons, etc. )
| | |── 02-molecules - Molecules ( Cards, Navs, Accordions, Backgrounds, Galleries, etc. )
| | |── 03-organisms - Organisms ( Blocks )
| | | |
| | | |── _base - Helpers
| | | | |
| | | | |── base-block - Base wrapper of each block
| | | | |── base-block-repeater - Connection controller for all custom blocks on the current page
| | | | |── repeatable-base.hcl.json - Internal scripts files ( skip it )
| | | |
| | | |── repeatable-custom - Custom Blocks
| | | | |
| | | | |── [block-name]-a - Block ( with type -a )
| | | | | |
| | | | | |── [block-name]-a-1 - Block Version
| | | | | | |
| | | | | | |── [block-name]-a-1-custom.twig - Custom content of the block. ( Repeaters, embeds and any custom content )
| | | | | | |── [block-name]-a-1-inner.twig - Main content of the block ( Pretitle, title, content, button, custom include )
| | | | | | |── [block-name]-a-1.twig - Filling the base wrapper of the block. Setting vertical offsets, background, text color and alignment
| | | | |
| | | | | |── index.twig - Main file of the block. Here are connected: the base wrapper of the block "base-block", the version of the block according to the data from ACF
| | | | |
| | | | |── repeatable-custom.acf.json - Adding the ACF of each block to the main ACF "Repeatable Custom". In the admin panel, when adding a block, show up pop-up with a selection of blocks. A list of these blocks is in this file.
| | | |
| | | |── repeatable-default - Default blocks ( Coming soon )
| | | |
| | | |── single - Single Blocks ( Header, Hero, Footer, Posts overview, etc. )
| | |
| | |── 04-templates - Templates ( Base, Singles, Overviews, etc. )
| | |
| | |── all.css - Imports the styles of all components
| |
| |── index.js - The root JS in which all script imports are added
| |── style.scss - The root SCSS in which the main style imports are added
General structure of TWIG files
- All code must be inside a tag
{% spaceless %}...{% endspaceless %} - After opening
{% spaceless %}, there should be a comment with a description of the variables that are imported to this component through the include of the parent component ( applies only to Molecules and Atoms ). You can see examples in finished components.
Elements naming
Name the elements according to the BEM methodology.
The name of each class ( selector ) should start with
{{ component_class }}__.
Exceptions: classes from Tailwind and other libraries.Includes wrap in a div with a class
{{ component_class }}__[include-name]-wrapperColumn container in the Main content of the block
{{ component_class }}__columnsColumn in the Main content of the block
{{ component_class }}__columnRepeater container
{{ component_class }}__repeaterContainer of repeater elements
{{ component_class }}__repeater-itemInner Container
{{ component_class }}__[component-name]-inner,
{{ component_class }}__[component-name]-inner-[index( 1, 2, 3, etc. )]
depending on the level of nesting.
✅ Correct
<div class='{{ component_class }}__title-wrapper'>
{% include '01-atoms/typography/title-a/title-a-1/title-a-1.twig' with { title : 'Title' } only %}
</div>
❌ Wrong
<div class='title-wrapper'>
{% include '01-atoms/typography/title-a/title-a-1/title-a-1.twig' with { title : 'Title' } only %}
</div>
<div class='title'>
{% include '01-atoms/typography/title-a/title-a-1/title-a-1.twig' with { title : 'Title' } only %}
</div>
<div class='{{ component_class }}-title-wrapper'>
{% include '01-atoms/typography/title-a/title-a-1/title-a-1.twig' with { title : 'Title' } only %}
</div>
✅ Correct
<div class="{{ component_class }}__repeater">
<div class="{{ component_class }}__repeater-inner grid gap-4 grid-cols-1 lg:grid-cols-2">
{% for item in items %}
<div class="{{ component_class }}__repeater-item">
{% include '02-molecules/cards/card-post-a/card-post-a-1.twig' with { item : item } only %}
</div>
{% endfor %}
</div>
</div>
❌ Wrong
<div class="{{ component_class }}__posts">
<div class="{{ component_class }}__posts-items grid gap-4 grid-cols-1 lg:grid-cols-2">
{% for item in items %}
<div class="{{ component_class }}__post">
{% include '02-molecules/cards/card-post-a/card-post-a-1.twig' with { item : item } only %}
</div>
{% endfor %}
</div>
</div>
❌ Wrong
<div class="{{ component_class }}__repeater">
<div class="{{ component_class }}__repeater__inner grid gap-4 grid-cols-1 lg:grid-cols-2">
{% for item in items %}
<div class="{{ component_class }}__repeater__item">
{% include '02-molecules/cards/card-post-a/card-post-a-1.twig' with { item : item } only %}
</div>
{% endfor %}
</div>
</div>
✅ Correct
<div class="{{ component_class }}__columns">
<div class="{{ component_class }}__column {{ component_class }}__column--1">Content</div>
<div class="{{ component_class }}__column {{ component_class }}__column--2">Content</div>
<div class="{{ component_class }}__column {{ component_class }}__column--3">Content</div>
</div>
❌ Wrong
<div class="{{ component_class }}__row">
<div class="{{ component_class }}__cell">Content</div>
<div class="{{ component_class }}__cell">Content</div>
<div class="{{ component_class }}__cell">Content</div>
</div>
Main content of the block structure
[block-name]-a-1-inner.twig - Correct structure for Main content of the block ( Pretitle, Title, Content, Image, Custom Include, Button )
Content should wrapped into 2 containers: Container and Container Inner.
Scheme:
Container -> Container Inner -> ContentWhen you need to put columns to container:
Scheme:
Container -> Container Inner -> Columns -> Column -> ContentContainer should to have
container(l-containerfor Helium < 1.1.2) class which set globalmax-widthfor content. Also you should to add content type modificator to this element ( about it below ).Inner Container with
text-alignand custommax-widthfrom ACF ( if available )Each Block by default should to contain 3 Containers type:
- Container Text - Pretitle, Title, Text, Image
- Container Custom - Custom include if it's not empty
- Container Button - Button include if it exists
- Few containers with the same. When you need to add it ( example: you need to make different
max-width) just add name it Container Text 2
{{ component_class }}__container {{ component_class }}__container--text
{{ component_class }}__container-inner
[pretitle]
[title]
[content]
[image]
{{ component_class }}__columns - If the content should be in columns
{{ component_class }}__column {{ component_class }}__column--[index( 1, 2, 3, etc. )]
[content]
{{ component_class }}__container {{ component_class }}__container--custom
{{ component_class }}__container-inner
[custom_include]
{{ component_class }}__container {{ component_class }}__container--button
{{ component_class }}__container-inner
[button]
✅ Correct
<div class="{{ component_class }}__container {{ component_class }}__container--text container container--small"> <!-- l-container for Helium < 1.1.2 -->
<div class="{{ component_class }}__container-inner mx-auto {{ alignment_classes }}" {% if section.add_max_width and section.max_width %} style="max-width: {{ section.max_width }}px"{% endif %}>
Pretitle Include
Title Include
Text Include
Image Include
<div class="{{ component_class }}__columns grid gap-4 grid-cols-1 lg:grid-cols-2">
<div class="{{ component_class }}__column {{ component_class }}__column--1">Column Content</div>
<div class="{{ component_class }}__column {{ component_class }}__column--2">Column Content</div>
</div>
</div>
</div>
{% set block_custom %}
Custom Include
{% endset %}
{% if block_custom|trim is not empty %}
<div class="{{ component_class }}__container {{ component_class }}__container--custom container"> <!-- l-container for Helium < 1.1.2 -->
<div class="{{ component_class }}__container-inner mx-auto {{ alignment_classes }}" {% if section.add_max_width and section.max_width %} style="max-width: {{ section.max_width }}px"{% endif %}>
{{ block_custom }}
</div>
</div>
{% endif %}
{% if section.add_button and section.button %}
<div class="{{ component_class }}__container {{ component_class }}__container--button container"> <!-- l-container for Helium < 1.1.2 -->
<div class="{{ component_class }}__container-inner mx-auto {{ alignment_classes }}" {% if section.add_max_width and section.max_width %} style="max-width: {{ section.max_width }}px"{% endif %}>
Button Include
</div>
</div>
{% endif %}
❌ Wrong
<div class="{{ component_class }}__wrapper container container--small"> <!-- l-container for Helium < 1.1.2 -->
<div class="{{ component_class }}__container-inner">
Pretitle Include
Title Include
Text Include
Image Include
</div>
<div class="{{ component_class }}__row grid gap-4 grid-cols-1 lg:grid-cols-2">
<div class="{{ component_class }}__cell">Column Content</div>
<div class="{{ component_class }}__cell">Column Content</div>
</div>
</div>
<div class="{{ component_class }}__wrapper-2 container"> <!-- l-container for Helium < 1.1.2 -->
<div class="{{ component_class }}__container-inner mx-auto"}>
Custom Include
</div>
</div>
<div class="{{ component_class }}__wrapper-3 container"> <!-- l-container for Helium < 1.1.2 -->
<div class="{{ component_class }}__container-inner mx-auto">
{% if section.add_button and section.button %}
Button Include
{% endif %}
</div>
</div>
Classes
If more than 4 classes must be added to the element, or the element must have different classes depending on if, use the following scheme
- Create a variable
[element_name]_classeswith an array containing only the main class of the element
{% set [element_name]_classes = [ component_class ~ '__[element-name]' ] %}
- If you need to add a modifier depending on the "if" condition
{% if data %}
{% set [element_name]_classes = [element_name]_classes|merge([ component_class ~ '__[element-name]--[modificator]' ]) %}
{% endif %}
- Add classes from Tailwind ( write in a column )
{% set [element_name]_classes = [element_name]_classes|merge([
'grid',
'grid-cols-1',
'gap-6',
'mx-auto',
]) %}
- If you want to add Tailwind classes depending on the "if" condition
{% if data %}
{% set [element_name]_classes = [element_name]_classes|merge([
'xs:grid-cols-2',
'xl:grid-cols-3',
]) %}
{% endif %}
- Assign classes to element
<div class="{{ [element_name]_classes|join(' ') }}"></div>
✅ Correct
{% set [element_name]_classes = [ component_class ~ '__element-name' ] %}
{% if data %}
{% set [element_name]_classes = [element_name]_classes|merge([ component_class ~ '__element-name--modificator' ]) %}
{% endif %}
{% set [element_name]_classes = [element_name]_classes|merge([
'grid',
'grid-cols-1',
'gap-6',
'mx-auto',
]) %}
{% if data %}
{% set [element_name]_classes = [element_name]_classes|merge([
'xs:grid-cols-2',
'xl:grid-cols-3',
]) %}
{% endif %}
<div class="{{ [element_name]_classes|join(' ') }}"></div>
❌ Wrong
{% set [element_name]_classes = [
component_class ~ '__element-name' ],
'grid',
'grid-cols-1',
'gap-6',
'mx-auto',
]) %}
{% if data %}
{% set [element_name]_classes = [element_name]_classes|merge([
'xs:grid-cols-2',
'xl:grid-cols-3',
]) %}
{% endif %}
{% if data %}
{% set [element_name]_classes = [element_name]_classes|merge([ component_class ~ '__element-name--modificator' ]) %}
{% endif %}
<div class="{{ [element_name]_classes|join(' ') }}"></div>
❌ Wrong
{% set [element_name]_classes = [ component_class ~ '__element-name' ] %}
{% if data %}
{% set [element_name]_classes = [element_name]_classes|merge([ component_class ~ '__element-name--modificator' ]) %}
{% endif %}
{% set [element_name]_classes = [element_name]_classes|merge([ 'grid', 'grid-cols-1', 'gap-6', 'mx-auto' ]) %}
{% if data %}
{% set [element_name]_classes = [element_name]_classes|merge([ 'xs:grid-cols-2', 'xl:grid-cols-3' ]) %}
{% endif %}
<div class="{{ [element_name]_classes|join(' ') }}"></div>
Class Grouping
- Write styles in the same order as in the Tailwind documentation.
- Add line breaks to separate style categories
- For side-related properties, use the following order
X / Y, Top / Right / Bottom / Left, - Group classes by breakpoints. From smallest to largest.
px-2, py-4, md:px4, md:py-8
Spaces
With the help of indentation, we can visually break the code into blocks. Before reading the text, our eye first visually divides it into blocks: Headings, paragraphs, lists, etc. Authors of books and articles specially divide the text into such blocks so that it is easier for you to orient yourself in the text and read it. We can do the same with code
Use the following indents:
0 line breaks
for components of the same level that are 1 line in size.
<li>Text</li>
<li>Text</li>
<li>Text</li>
1 line break
between parent and child components or between components of the same level, one of which has 1 line in size and the other more than 2
<ul>
<!--1-->
<li>Text</li>
<li>Text</li>
<li>Text</li>
<!--1-->
</ul>
<h3>Title</h3>
<!--1-->
<ul>
<!--1-->
<li>Text</li>
<li>Text</li>
<li>Text</li>
<!--1-->
</ul>
2 line breaks
for components of the same level with the size of 2-5 lines
<ul>
<!--1-->
<li>Text</li>
<li>Text</li>
<li>Text</li>
<!--1-->
</ul>
<!--1-->
<!--2-->
<ul>
<!--1-->
<li>Text</li>
<li>Text</li>
<li>Text</li>
<!--1-->
</ul>
3 line breaks
for components of the same level with the size of 5+ lines
<div>
<!--1-->
<h3>Title</h3>
<!--1-->
<ul>
<!--1-->
<li>Text</li>
<li>Text</li>
<li>Text</li>
<!--1-->
</ul>
<!--1-->
</div>
<!--1-->
<!--2-->
<!--3-->
<div>
<!--1-->
<h3>Title</h3>
<!--1-->
<ul>
<!--1-->
<li>Text</li>
<li>Text</li>
<li>Text</li>
<!--1-->
</ul>
<!--1-->
</div>
✅ Correct
<div>
<h3>Title</h3>
<ul>
<li>Text</li>
<li>Text</li>
<li>Text</li>
</ul>
</div>
<div>
<h3>Title</h3>
<ul>
<li>Text</li>
<li>Text</li>
<li>Text</li>
</ul>
</div>
❌ Wrong
<div>
<h3>Title</h3>
<ul>
<li>Text</li>
<li>Text</li>
<li>Text</li>
</ul>
</div>
<div>
<h3>Title</h3>
<ul>
<li>Text</li>
<li>Text</li>
<li>Text</li>
</ul>
</div>
❌ Wrong
<div>
<h3>Title</h3>
<ul>
<li>Text</li>
<li>Text</li>
<li>Text</li>
</ul>
</div>
<div>
<h3>Title</h3>
<ul>
<li>Text</li>
<li>Text</li>
<li>Text</li>
</ul>
</div>
Comments
- Try to use comments as it has been done in other components.
- In all atoms and molecules, write down the variables that are imported to the component and what information they contain. The syntax can also be seen in ready-made components
- When the components are pretty large, comment out the closing tags as follows
<div class="container">
<div class="container-inner">
...
{# Container inner end. #}
</div>
{# Container end. #}
</div>
Terms
Inner Container - a container that is inserted inside the wrapper. Grid styles ( grid or flex ) are usually configured for this element. The inner container is needed to isolate the grid settings from the main wrapper. This is necessary in order to observe the principle of element isolation
Flex Offset - a swiss army knife for creating grids / columns, which was used even by our grandpas in 2012. Allows you to create responsive grids, the columns of which contain very different content and also change their width in different ways depending on the breakpoint. Use to create columns with different (like cards) content.
How to code:
<div class="items flex flex-wrap -ml-4 gap-y-4">
<div class="item w-1/2 pl-4">Item</div>
<div class="item w-1/2 pl-4">Item</div>
<div class="item w-1/2 pl-4">Item</div>
</div>