HTML / Twig
Semantic HTML
Section titled “Semantic HTML”Use semantic elements to give meaning to the document structure. Avoid <div> and <span> when a more descriptive element fits.
<!-- Good --><header class="site-header"> <nav class="nav">...</nav></header><main class="main"> <article class="card">...</article> <aside class="sidebar">...</aside></main><footer class="site-footer">...</footer>
<!-- Bad --><div class="site-header"> <div class="nav">...</div></div>Common semantic elements: <header>, <nav>, <main>, <section>, <article>, <aside>, <footer>, <figure>, <time>.
Interactive elements
Section titled “Interactive elements”Use <button> for actions and <a> for navigation. Never use a <div> or <span> as a clickable element.
<!-- Good --><button class="button" type="button">Open menu</button><a class="card__link" href="/about">Read more</a>
<!-- Bad --><div class="button" onclick="openMenu()">Open menu</div>Accessibility
Section titled “Accessibility”Always provide a text label for interactive elements. Use aria-label when visible text is absent.
<!-- Good — visible text --><button class="button">Close</button>
<!-- Good — icon-only button with aria-label --><button class="button button--icon" aria-label="Close menu"> <svg>...</svg></button>
<!-- Bad — no label --><button class="button button--icon"> <svg>...</svg></button>Add alt text to all images. Use an empty alt="" for purely decorative images.
<!-- Good — descriptive alt --><img src="team.jpg" alt="The Instance Studio team at their Melbourne office" />
<!-- Good — decorative image --><img src="divider.svg" alt="" />
<!-- Bad — no alt --><img src="team.jpg" />Indentation
Section titled “Indentation”Use 4 spaces.
Lowercase and double quotes [Prettier enforced]
Section titled “Lowercase and double quotes [Prettier enforced]”Use lowercase for all tags and attributes. Use double quotes for all attribute values.
<!-- Good --><section class="hero"> <h1 class="hero__title">Heading</h1></section>
<!-- Bad --><SECTION CLASS='hero'> <H1 CLASS='hero__title'>Heading</H1></SECTION>Closing tags
Section titled “Closing tags”Close all HTML tags. Use the self-closing syntax for void elements.
<!-- Good --><img src="..." alt="..." /><input type="text" />
<!-- Bad --><img src="..." alt="..."><input type="text">Avoid inline styles
Section titled “Avoid inline styles”Use CSS classes instead. Inline styles bypass the design system and are hard to override.
<!-- Good --><section class="hero hero--dark">...</section>
<!-- Bad --><section style="background: #000; color: #fff;">...</section>Spacing [Prettier enforced]
Section titled “Spacing [Prettier enforced]”Keep spacing clean and consistent inside Twig tags.
{# Good #}{{ post.title }}{% if post.title %}
{# Bad #}{{post.title}}{%if post.title%}No logic in templates
Section titled “No logic in templates”Twig templates should only render data — no queries, no processing. All data must come from $context.
{# Good — data passed from PHP #}{% for member in team_members %} <p>{{ member.name }}</p>{% endfor %}
{# Bad — fetching data in Twig #}{% for member in function('get_posts', {post_type: 'team'}) %} <p>{{ member.post_title }}</p>{% endfor %}Partials with include
Section titled “Partials with include”Pass only the variables a partial needs using with {}. This makes the data flow explicit.
{% include 'partials/card.twig' with { title: post.title, image: post.thumbnail, link: post.link} %}set for reuse
Section titled “set for reuse”Use {% set %} to avoid repeating the same expression.
{# Good #}{% set imageUrl = post.thumbnail.src %}<img src="{{ imageUrl }}" srcset="{{ imageUrl }}?w=400 400w, {{ imageUrl }}?w=800 800w" />
{# Bad — expression repeated #}<img src="{{ post.thumbnail.src }}" srcset="{{ post.thumbnail.src }}?w=400 400w" />Conditional class names
Section titled “Conditional class names”Use the clsx Twig function to build class strings conditionally.
<article class="{{ clsx('card', isFeatured and 'card--featured', isLarge and 'card--large') }}">
