Skip to content

gtap-dev/html

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 

Repository files navigation

Twig & HTML <guide-lines />

A mostly reasonable approach to Twig

This is a superset of the Official SensioLabs Twig Standards.

Resources

Filters

  • 2.1 String tokens to be used inside the replace filter should be marked with percentage signs.

    {# Bad #}
    {{ sidekicks|replace('{{ robin }}', 'Dick Grayson') }}
    
    {# Good #}
    {{ sidekicks|replace('%robin%', 'Jason Todd') }}

  • 2.2 Do not use the default filter for default data.

    Default data should come from context. Use default filter for logic only.

    {# Bad #}
    {{ url|default('#') }}
    
    {# Good #}
    {{ url|default(data.url) }}

  • 2.3 Use merge filter to add to arrays or objects.

    Note that you need to make sure the array/object is iterable.

    {# Bad #}
    {% include '@button' with { data: { text: data.button.text, icon: '#arrow-right' } } %}
    
    {# Good #}
    {% include '@button' with { data: data.button|merge({ icon: '#arrow-right' }) } %}

  • 2.4 Escape data and values in HTML attributes with the escape filter.

    {# Bad #}
    <button data-name="{{ data.name }}">
    
    {# Good #}
    <button data-name="{{ data.name|escape('html_attr') }}">

  • 2.5 If possible, avoid transforming data with filters. Transform data beforehand, in PHP.

Functions

  • 3.1 Use dump function to debug data.

    Wrap the output with a pre tag to make it easier to read.

    <pre>
      {{ dump(user) }}
    </pre>

Operators

  • 4.1 Do not use Math operators.

    Math is calculation logic that should stay in PHP.

    As an example, instead of the mod operator, use batching.

    {# Bad #}
    {% for row in items %}
      {% if loop.index == 1 or (loop.index % 3) == 1 %}
        {# do something with every third item #}
      {% endif %}
    {% endfor %}
    
    {# Good #}
    {% for row in items|batch(3) %}
      {% for column in row %}
        {% if loop.index == 1 %}
          {# do something with every third item #}
        {% endif %}
      {% endfor %}
    {% endfor %}

Variables

  • 5.1 Use variables to store common values in templates.

    Instead of doing the same template logic in multiple instances, save the values to variables with set tags.

Comments

  • 6.1 Use Twig comments to convey important implementation information to other developers.

    Twig comments will be compiled away, HTML comments are transferred and visible in source to all users.

    {# Bad #}
    <!-- TODO: id should not be static -->
    <input id="input-1" />
    
    {# Good #}
    {# TODO: id should not be static #}
    <input id="input-1" />

Whitespace

  • 9.1 Place 1 space before the leading brace.

    {# Bad #}
    {% set dog={
        'age': '1 year',
        'breed': 'Bernese Mountain Dog',
    } %};
    
    {# Good #}
    {% set dog = {
        'age': '1 year',
        'breed': 'Bernese Mountain Dog',
    } %};

  • 9.2 Place 1 space before the opening parenthesis in control statements (if, for etc.). Place no space between the argument list and the function name in function calls and declarations.

    {# Bad #}
    {% if(isJedi) %}
        {% set enemy = 'sith' %}
    {% endif %}
    
    {# Good #}
    {% if (isJedi) %}
        {% set enemy = 'sith' %}
    {% endif %}
    
    {# Bad #}
    {{ jedi|default ('Yoda') }}
    
    {# Good #}
    {{ jedi|default('Yoda') }}

  • 9.3 Set off operators with spaces.

    {# Bad #}
    {% set x=y+5 %}
    
    {# Good #}
    {% set x = y + 5 %}

  • 9.4 End files with a single newline character.

    {# Bad #}
    {{ stuff }}
    {# Bad #}
    {{ stuff }}↵
    ↵
    {# Good #}
    {{ stuff }}↵

  • 9.5 Do not pad your blocks with blank lines.

    {# Bad #}
    {%
    
      set foo = 'bar'
    
    %}
    
    {# Bad #}
    {% if baz %}
    
      {{ qux }}
    {% elseif %}
      {{ foo }}
    
    {% endif %}
    
    {# Good #}
    {% if baz %}
      {{ qux }}
    {% elseif %}
      {{ foo }}
    {% endif %}

  • 9.6 Do not add spaces inside parentheses.

    {# Bad #}
    {{ foo|default( 'foo' ) }}
    
    {# Good #}
    {{ foo|default('foo') }}

  • 9.7 Do not add spaces inside brackets.

    {# Bad #}
    {% set foo = [ 1, 2, 3 ] %}
    {{ foo.0 }}
    
    {# Good #}
    {% set foo = [1, 2, 3] %}
    {{ foo.0 }}

  • 9.8 Add spaces inside curly braces.

    {# Bad #}
    {% set foo = {clark: 'kent'} %}
    
    {# Good #}
    {% set foo = { clark: 'kent' } %}

  • 9.9 Avoid having everything in one line.

    Why? This ensures readability and maintainability.

    {# Bad #}
    {% if victory|default %}{{ congratulations }}{% else %}{{ failure }}{% endif %}
    
    {# Good #}
    {% if victory|default %}
        {{ congratulations }}
    {% else %}
        {{ failure }}
    {% endif %}

  • 9.10 Avoid having too much attributes in single line.

    Why? This ensures readability and maintainability.

    {# Bad #}
    <img loading="lazy" src="{{ placeholderSrc }}" data-srcset="{{ data.srcset }}" data-sizes="auto" alt="{{ data.alt }}" {% if data.width %}width="{{ data.width }}"{% endif %} {% if data.title %}title="{{ data.title }}"{% endif %} class="image__img lazyload">
    
    {# Good #}
    <img
        loading="lazy"
        src="{{ placeholderSrc }}"
        data-srcset="{{ data.srcset }}"
        data-sizes="auto"
        alt="{{ data.alt }}"
        {% if data.width %}width="{{ data.width }}"{% endif %}
        {% if data.title %}title="{{ data.title }}"{% endif %}
        class="image__img lazyload"
    >

  • 9.11 Avoid having too much logic in a single HTML attribute.

    Why? This ensures readability and maintainability.

    Instead, split the logic to a separate variable block with whitespace control.

    {# Bad #}
    <div class="textfield{% if modifier %} {{ modifier }}{% endif %}{% if class %} {{ class }}{% endif %}{% if data.isInvalid %} is-invalid{% endif %}{% if data.isDisabled %} is-disabled{% endif %}{% if data.icon %} textfield--icon{% endif %}"></div>
    
    {# Good #}
    {% set BEM -%}
      textfield
      {% if modifier %} {{ modifier }}{% endif %}
      {%- if class %} {{ class }}{% endif %}
      {%- if data.isInvalid %} is-invalid{% endif %}
      {%- if data.isDisabled %} is-disabled{% endif %}
      {%- if data.icon %} textfield--icon{% endif %}
    {% endset %}
    <div class="{{ BEM }}"></div>

  • 9.12 Indenting nested logic and blovks

    For consistency, everything that is nested should be indented by one level more than its context. (Even if it breaks HTML indentation)

    {# Bad #}
    {% if data.title %}
    {{ data.title }}
    {% endif %}
    
    <div class="row__one">{{ data.rowOne }}</div>
    {% if data.rowTwo %}
    <div class="row__two">{{ data.rowTwo }}</div>
    {% endif %}
    
    {# Good #}
    {% if data.title %}
        {{ data.title }}
    {% endif %}
    
    <div class="row__one">{{ data.rowOne }}</div>
    {% if data.rowTwo %}
        <div class="row__two">{{ data.rowTwo }}</div>
    {% endif %}

Stylistic rules

  • 10.1 Leading commas: No.

    {# Bad #}
    {% set story = [
      'once'
    , 'upon'
    , 'a'
    , 'time'
    ] %}
    
    {# Good #}
    {% set story = [
      once,
      upon,
      aTime,
    ] %}
    
    {# Bad #}
    {% set hero = {
        'firstName': 'Ada'
      , 'lastName': 'Lovelace'
      , 'birthYear': '1815'
      , 'superPower': 'computers'
    } %}
    
    {# Good #}
    {% set hero = {
      'firstName': 'Ada',
      'lastName': 'Lovelace',
      'birthYear': '1815',
      'superPower': 'computers'
    } %}

  • 10.2 Use double quotes for HTML attributes, single quotes for everything else.

    {# Bad #}
    <input value='bad' />
    
    {# Good #}
    <input value="good" />
    <input value="\"good\"" />
    
    {# Bad #}
    {% set hero = "bad" %}
    
    {# Good #}
    {% set hero = 'good' %}

Naming conventions

  • 11.1 Use camelCase to name variables.

    {# Bad #}
    {% set story_of_my_life = 'cry baby cry' %}
    {% set storyofmylife = 'cry baby cry' %}
    
    {# Good #}
    {% set storyOfMyLife = 'cry baby cry' %}

Known issues

You can use majority of twig functions, but there are some restrictions in styleguide, because we use Twig.js adapter.

  • 13.1 Include inside macro not working very well. Example:

    {% macro li(item, class, icon) %}
        <li class="pagination__item {{ class }}">
            <a href="{{ item.url }}" class="pagination__link">
                {% include '@icon' with { name: icon } %}
                {{ item.text }}
            </a>
        </li>
    {% endmacro %}
    
    {{ ul.li(data.item, 'pagination__item--first', 'arrow') }}

    You can fix this issue by sending whole icon component into macro. Example:

        {% macro li(item, class, icon) %}
            <li class="pagination__item {{ class }}">
                <a href="{{ item.url }}" class="pagination__link">
                    {{ icon }}
                    {{ item.text }}
                </a>
            </li>
        {% endmacro %}
    
        {% set icon %}
            {% include '@icon' with { name: 'arrow' } %}
        {% endset %}
        {{ ul.li(data.item, 'pagination__item--first', icon) }}

    It's not nice but will work.

General

  • 14.1 When deciding to choose whether to use a certain Twig functionality, make sure it is supported by both Twig PHP and Twig.js as we generally use both in our projects.

  • 14.2 If data existence is questionable, use twig if tag.

    {# Bad #}
    <div class="textfield__error">
        {{ data.error }}
    </div>
    
    {# Bad #}
    <div class="textfield__error">
        {% if data.error %}
            {{ data.error }}
        {% endif %}
    </div>
    
    {# Good (no unnecessary html) #}
    {% if data.error %}
        <div class="textfield__error">
            {{ data.error }}
        </div>
    {% endif %}

  • 14.3 Use data.something only if data really comes from back-end and is changeable

    Example if button icon is fixed and admin can't change it.
    
    <button type="button">
        {{ data.text }}
        {% include '@icon' with { name: icon, class: 'button__icon', modifier: '' } %}
    </button>

HTML

  • 15.1 Use semantic HTML elements, wherever appropriate.

    This is to ensure basic usability of the website for all people.

    {# Bad #}
    {# Clickable div that does not have role button nor is tabbable. #}
    <div onclick="">
        {{ data.text }}
    </div>
    <a href="#">this is a button that has interactivity via JS and no real href</a>
    
    {# Good #}
    <button onclick="">
        {{ data.text }}
    </button>
    <button>this is a button that has interactivity via JS</button>

  • 15.3 Make sure your content order is semantic.

    Sometimes due to design quirks HTML structure cannot be semantically correct. In these cases you can hide the wrongfully placed elements using aria-hidden="true" and add visually hidden elements to semantically appropriate places, thus making content order correct and accessible.

    {# Bad #}
    <section>
        <div class="content">...</div>
        <h2>Section title</h2>
        <div class="content">...</div>
    </section>
    
    {# Better #}
    <section>
        <h2 class="h-visually-hidden">Section title</h2>
        <div class="content">...</div>
        <h2 aria-hidden="true">Section title</h2>
        <div class="content">...</div>
    </section>
    
    {# Best #}
    {# Work with your designer to make sure the design order follows semantic order! #}
    <section>
        <h2>Section title</h2>
        <div class="content">...</div>
        <div class="content">...</div>
    </section>

  • 15.4 Use appropriate ARIA behavior on interactive components that do not exist natively in HTML.

    For example: alerts, dialogs, tooltips, accordions, tabs, autocomplete inputs do not have native HTML elements that convey their behavior and semantics to assistive technology.

    These kinds of interactive components need to have appropriate ARIA roles and JavaScript functionality to work properly for everyone.

    Please explore the WAI-ARIA Authoring Practises document for specific examples and explanations.

  • 15.5 Avoid using target="_blank" on links.

    Do not force people to new tabs/windows when it is not strictly necessary. People who want to open links in new tabs usually know how to do this themselves.

    See this CSS-Tricks article for more explanations.

    If you absolutely must use target="_blank" on links, use rel="noreferrer with it.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published