All skills
grasmash avatar

/drupal-at-your-fingertips

@76e6435

Comprehensive Drupal patterns from "Drupal at Your Fingertips" by Selwyn Polit. Covers 50+ topics including services, hooks, forms, entities, caching, testing, and more.

Use this Skill: https://skilld.dev/gh/grasmash/drupal-claude-skills/drupal-at-your-fingertips

This session only. Nothing lands on disk.

referencestwig.md

≈2.5k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Twig Templates

Source: Drupal at Your Fingertips - twig Author: Selwyn Polit

Quick reference for Twig templating in Drupal with practical code examples.


Core Concept

Twig is Drupal's template engine for rendering HTML. Templates use .html.twig extension and follow a suggestion hierarchy. Use content.field_name for rendered output (with labels/formatting) and node.field_name.value for raw values.

Twig Syntax

Three syntaxes:

{# This is a comment #}

{{ variable }}  {# Output variable (auto-escaped) #}

{% if condition %}  {# Logic statement #}
  ...
{% endif %}

Template Naming & Suggestions

Naming pattern - Most specific to least:

{# Node templates #}
node--article--123.html.twig       {# Specific node ID #}
node--article--full.html.twig      {# Content type + view mode #}
node--article.html.twig            {# Content type #}
node--full.html.twig               {# View mode #}
node.html.twig                     {# Base template #}

{# Field templates #}
field--field-tags--article.html.twig  {# Field + content type #}
field--field-tags.html.twig           {# Field name #}
field--entity-reference.html.twig     {# Field type #}
field.html.twig                       {# Base template #}

{# Views templates #}
views-view--my-view--page.html.twig   {# View + display #}
views-view--my-view.html.twig         {# View name #}
views-view.html.twig                  {# Base template #}

Enable debug mode to see suggestions in HTML comments.


Variable Access Patterns

Two ways to access fields:

Prefix Purpose Example
content. Rendered output with wrappers {{ content.field_image }}
node. Raw field values {{ node.field_image.0.target_id }}

Content prefix (rendered with labels/formatting):

{{ content.field_image }}
{{ content.field_title }}
{{ content.body }}

Node prefix (raw values):

{{ node.title.value }}
{{ node.field_tags.0.target_id }}
{{ node.field_date.value|date('Y-m-d') }}

Common Field Types

Text fields:

{{ content.field_description }}
{{ node.field_description.value }}
{{ node.field_description.0.value }}

Entity reference fields:

{# Get referenced entity ID #}
{{ node.field_author.0.target_id }}

{# Get referenced entity label #}
{{ node.field_author.entity.label }}

{# Get referenced entity field #}
{{ node.field_author.entity.field_bio.value }}

Boolean fields:

{% if content.field_featured['#items'].0.value %}
  <span class="featured">Featured</span>
{% endif %}

Date fields:

{# Rendered date #}
{{ content.field_publish_date }}

{# Format raw date #}
{{ node.field_publish_date.value|date('F j, Y') }}
{{ node.field_publish_date.0.value|date('Y-m-d') }}

{# Node created/changed #}
{{ node.created.value|date('Y-m-d') }}
{{ node.changed.value|date('Y-m-d') }}

Link fields:

{# Rendered link #}
{{ content.field_website }}

{# Link parts #}
<a href="{{ node.field_website.0.url }}">
  {{ node.field_website.0.title }}
</a>

File fields:

{# Rendered file #}
{{ content.field_document }}

{# File URL and name #}
{% for file in node.field_document %}
  <a href="{{ file.entity.uri.value|file_url }}">
    {{ file.entity.filename.value }}
  </a>
{% endfor %}

Image fields:

{# Rendered image #}
{{ content.field_image }}

{# Image URL #}
{{ node.field_image.entity.uri.value|file_url }}

{# Alt text #}
{{ node.field_image.alt }}

Twig Filters

Essential Drupal filters:

Filter Purpose Example
|t Translate `{{ 'Hello'
|raw Output unescaped HTML {{ content.body|raw }}
|striptags Remove HTML tags {{ content.body|striptags }}
|render Render arrays {{ content.field_x|render }}
|without('field') Exclude fields {{ content|without('field_x') }}
|file_url File URI to URL {{ uri|file_url }}
|date('format') Format dates {{ date|date('Y-m-d') }}
|clean_class HTML class names {{ label|clean_class }}
|join('+') Join array {{ ids|join('+') }}
|length Array/string length {{ items|length }}
|upper, |lower Case transform {{ title|upper }}

Common patterns:

{# Check if field has content #}
{% if content.body|render|striptags|trim is not empty %}
  {{ content.body }}
{% endif %}

{# Remove specific tags #}
{{ content.field_text|striptags('<b>,<a>')|raw }}

{# Format class name #}
<div class="{{ node.bundle|clean_class }}">

Twig Functions

Drupal functions:

{# Generate URLs #}
<a href="{{ url('entity.node.canonical', {node: 123}) }}">Node Link</a>
<a href="{{ path('entity.node.canonical', {node: 123}) }}">Relative Path</a>

{# Render views #}
{{ drupal_view('my_view', 'block_1') }}
{{ drupal_view('my_view', 'block_1', arg1, arg2) }}

{# Check view has results #}
{% if drupal_view_result('my_view', 'block_1')|length %}
  {{ drupal_view('my_view', 'block_1') }}
{% endif %}

{# Render blocks #}
{{ drupal_block('system_branding_block') }}
{{ drupal_block('views_block:my_view-block_1') }}

{# Render specific field #}
{{ drupal_field('field_tags', 'node') }}

{# Debug output #}
{{ dump(variable) }}
{{ kint(variable) }}

Control Structures

Conditionals:

{% if content.field_featured %}
  <div class="featured">{{ content.field_featured }}</div>
{% elseif content.field_promoted %}
  <div class="promoted">{{ content.field_promoted }}</div>
{% else %}
  <div class="regular">Regular content</div>
{% endif %}

Loops:

{% for item in items %}
  {{ item }}

  {% if loop.first %}First{% endif %}
  {% if loop.last %}Last{% endif %}
  {{ loop.index }}  {# 1-indexed #}
  {{ loop.index0 }} {# 0-indexed #}
{% endfor %}

{# Loop with separator #}
{% for tag in node.field_tags %}
  {% if not loop.first %}, {% endif %}
  {{ tag.entity.label }}
{% endfor %}

Setting variables:

{% set classes = ['node', node.bundle, 'view-mode-' ~ view_mode] %}
<div{{ attributes.addClass(classes) }}>

{% set name = 'Hello !name'|t({'!name': user.name}) %}

Attributes & Classes

Add classes:

{% set classes = ['article', 'featured'] %}
<div{{ attributes.addClass(classes) }}>

{# Conditional classes #}
{% set classes = [
  'node',
  node.bundle|clean_class,
  node.isPromoted() ? 'promoted',
  'view-mode-' ~ view_mode|clean_class,
] %}
<div{{ attributes.addClass(classes) }}>

Attribute operations:

{{ attributes.setAttribute('data-id', node.id) }}
{{ attributes.removeClass('unwanted-class') }}
{{ attributes.removeAttribute('id') }}

{% if attributes.hasClass('check-this') %}
  ...
{% endif %}

Including Templates

Include partial templates:

{# Include from theme #}
{% include '@mytheme/partials/header.html.twig' %}

{# Include with variables #}
{% include '@mytheme/partials/card.html.twig' with {
  'title': node.title.value,
  'image': node.field_image
} %}

{# Include only these variables #}
{% include '@mytheme/partials/item.html.twig' with {
  'item': item
} only %}

Preprocessing

Add variables in .theme file:

function mytheme_preprocess_node(&$variables) {
  $node = $variables['node'];

  // Add custom variable
  $variables['author_name'] = $node->getOwner()->getDisplayName();

  // Add formatted date
  $variables['formatted_date'] = \Drupal::service('date.formatter')
    ->format($node->getCreatedTime(), 'custom', 'F j, Y');
}

Use in template:

<div class="author">{{ author_name }}</div>
<div class="date">{{ formatted_date }}</div>

Debugging

Enable debug mode (sites/development.services.yml):

parameters:
  twig.config:
    debug: true
    auto_reload: true
    cache: false

Debug functions:

{# Basic dump #}
{{ dump(variable) }}

{# Dump specific properties #}
{{ dump(node.title) }}
{{ dump(node.field_tags.0) }}

{# Kint (requires Devel module) #}
{{ kint(node) }}

View template suggestions (in HTML source):

<!-- FILE NAME SUGGESTIONS:
   * node--article--123.html.twig
   * node--article--full.html.twig
   x node--article.html.twig
   * node--full.html.twig
   * node.html.twig
-->

Regions

Define in mytheme.info.yml:

regions:
  header: Header
  primary_menu: 'Primary Menu'
  content: Content
  sidebar_first: 'Left Sidebar'
  footer: Footer

Use in templates:

{% if page.header %}
  <header>{{ page.header }}</header>
{% endif %}

{% if page.sidebar_first %}
  <aside class="sidebar">{{ page.sidebar_first }}</aside>
{% endif %}


Key Guidelines

✅ Use template suggestions - Most specific first ✅ Use content. for rendered output - Includes labels/wrappers ✅ Use node. for raw values - Direct field access ✅ Use filters for safety - |t, |striptags, |clean_class ✅ Enable debug mode - During development ✅ Use preprocessing - For complex logic ✅ Check field existence - Use {% if content.field_name %} ✅ Use Twig Tweak module - For drupal_view, drupal_block

❌ Don't use PHP in templates - Preprocess instead ❌ Don't forget |t filter - For translatable strings ❌ Don't use |raw carelessly - Security risk ❌ Don't access nodes directly - Use provided variables ❌ Don't cache templates in dev - Enable auto_reload ❌ Don't forget to clear cache - After adding templates ❌ Don't mix content. and node. - Choose one approach


Full documentation: https://drupalatyourfingertips.com/twig

Source: SKILL.md on GitHub

1 warning16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill is a comprehensive documentation resource for Drupal development, providing patterns, code examples, and best practices for various Drupal APIs. No security issues were detected.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer7mo

    55/55 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 76e6435. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 4 months ago

README badge

README badge for grasmash/drupal-claude-skills/drupal-at-your-fingertips