Post

Drupal Cheatsheet

Drupal website install and configuration guide.

Lots of parts to building a Drupal site. Installing core, Modules and Themes. This article is documentation for setting up some powerful drupal modules and build an awesome drupal site.

Install Drupal

Here ive listed several ways to install drupal. Drupal can be installed locally or on a remote server.

Install Drupal on Digital Ocean

Digital Ocean has a OpenLiteSpeed droplet for Drupal which has

  • Composer
  • Drush
  • PHP 8.1
  • Configured to serve the web directory

Using Drush to Login

Once your drupal litespeed droplet has been installed on Digital Ocean you can use Drush to login. The Great thing about the Digital Ocean litespeed module is it already has drush installed.

1
vendor/bin/drush uli --uri http://your-site-url

Ensure to add your domain to Digital Ocean before you do the following config. Once you have installed your droplet, open the console from Digital Ocean admin.

  • The first question to answer is “what is your domain?”.
  • The second question is do you want an ssl

Its harder to configure this stuff later, so best get it right from the start.

Or you can just work from an ip address as the site is accessible from the ip address

Add the droplet and follow the prompts to add

  • Your ssh key
  • Your domain
  • SSL certificate

At this point you should be able to access the Drupal welcome screen via a browser.

The next part relates to if you already have a local drupal install and now you want to deploy it to your remote server.

Install your apps

  • Use scp to copy your composer.json file to your server to your project root
  • Run composer install in the root.

Import database

  • Activate the backup and migrate plugin and export your db from local and import your database to the remote server using backup and migrate module which should be installed on both instances.

Install your theme

  • Locally zip your web/themes/custom theme folder
  • Use scp to deploy your custom.zip to the server
  • Use unzip to unzip your custom.zip
  • Clear the cache and reload you, you should be able to see your theme loading.

Deploy files

If you see stylesheet is missing from your drupal site. You just need to deploy the files directory as we do not keep it under version control.

use scp to deploy web/sites/default/files

best to zip first.

1
zip files.zip -r files
1
scp files.zip  your_server:/var/www/html/web/sites/default/

Install Drupal Locally

There are various ways to install Drupal locally. Below are some of the ways I have installed Drupal locally in the past.

Set Up a Drupal Site Locally with DDEV:

  1. Create a New Drupal Project:
    • Open your terminal/command prompt.
    • Navigate to the directory where you want to create your Drupal project.
    • Run the following commands:
1
2
3
cd your/drupal/project-dir
ddev config --project-type=drupal --docroot=web --create-docroot
ddev composer create "drupal/recommended-project:^10"

Ensure your directory is clean and only has drupal files in it otherwise ddev composer create "drupal/recommended-project:^10" wont work!

Set PHP version

Edit .ddev/config.yaml

1
2
3
4
5
6
7
8
9
10
11
12
name: my-drupal-project
type: drupal9
docroot: web
php_version: "8.0"
webserver_type: apache-fpm
database:
  type: mariadb
  version: "10.3"
router_http_port: "8080"
router_https_port: "8443"
additional_flexible_config: false

Start DDev

Ensure your containers in Docker Desktop are running.

1
ddev start

Troubleshooting

If you get error “Could not connect to a Docker provider. Please start or install a Docker provider. For install help go to: https://ddev.readthedocs.io/en/latest/users/install/”

open docker-desktop and hit the start button then run below command again.

1
ddev config --project-type=drupal10

Fix can’t listen on port 80

Failed to start sitename: unable to listen on required ports, port 443 is already in use,

1
ddev poweroff && docker rm -f $(docker ps -aq)
1
ddev start
  1. Stop other services using the port. For instance:
1
valet stop

Now start DDev again.

1
ddev start
  1. Stop all docker containers and Restart docker
1
ddev poweroff && docker rm -f $(docker ps -aq)

Edit Docroot

Fix the error 403 Forbidden when you visit your ddev site. Update .ddev/config with a relative path to drupals root index.php.

1
vim .ddev/config.yml
1
docroot: "web"

After updating config file DDev. Ensure you have an index.php inside the web directory fixing these to issues, This will fix any 403 permission issues.

DDev important note.

Ddev drupal usage changes a little. ie:

To install module using ddev

1
ddev composer require drupal/devel

Install Drush with DDev

1
ddev composer require drush/drush

Install Drupal:

  • Access the Drupal site in your browser. The URL will typically be http://.ddev.site.
  • Follow the Drupal installation wizard:
  • Choose a language.
  • Select the installation profile (Standard is typical).
  • Enter database connection details (use db for the database host).
  • Continue with the installation process.

Configure Site:

  • After installation, you’ll be prompted to configure your Drupal site.
  • Set up the site name, admin username, password, and other settings.

Stop DDEV:

1
When you're done working on your Drupal site, you can stop the DDEV environment:
1
ddev stop

2. Install Drupal Locally using Valet

Valet comes from the Laravel world. It’s a step up from using mamp/xammp. The difference between valet and mamp is with Valet there is no UI. You run commands on the command line to start and stop valet services such as MySQL and PHP and create local website instances.

** Prerequisites **

  • Valet installed locally
  • Composer installed

Steps to install Drupal locally

3. Install quick start Drupal

** Prerequisites **

  • Composer installed

Quick start drupal is good for quick experiments it’s not recommended to use for a production build. but if you’re looking to get a quick local off the ground just to try out a plug-in or test a vanilla install it’s good for that.

Install Quick-Start Drupal locally with Composer

Running the below command will create a drupal play ground.

  • Start a local site on http://127.0.0.1:8888/
  • Install the latest version of drupal
  • Using sqlite db
  • Install the demo_uami food magazine theme
  • The admin login details will be output in the commandline for you
1
2
composer create-project drupal/recommended-project drupal
cd drupal && php -d memory_limit=256M web/core/scripts/drupal quick-start demo_umami

Restart the quick-start site

After you close it (Ctrl-C) and you want to start it again

1
php -d memory_limit=256M web/core/scripts/drupal quick-start

More information

drupal download drupal install

Drupal Settings.php file

Set the config sync directory. This will allow drush updatedb to sync your site configuration to the database. Note: for a composer installed drupal site you will have 2 settings.php files. The settings.php file you want to update is located in the web directory. You can rename the other settings.php to be settings_backup.php

1
$settings['config_sync_directory'] = '../config/sync';

Essential Drupal Modules and themes for Site Building

When creating pretty basic stock standard website. This is modules I must have and This is what I use these modules for.

Pathauto module

Automatically change the urls to be prettier https://www.drupal.org/project/pathauto

1
composer require 'drupal/pathauto:^1.13'

Linkit Module

Break up links into 2 parts. The URL and the label.

https://www.drupal.org/project/linkit

Dev version for drupal 11

1
composer require 'drupal/linkit:^6.1'

Media Library (core module)

Use existing images in media library Ensure fields are set to media rather then image.

MicroContent module

For creating global blocks

Works with drupal 11 https://www.drupal.org/project/microcontent

1
composer require 'drupal/microcontent:^2.0'

Entity Reference Display Module

Configure the display of Referenced entities

Not yet ready for D11

entity Reference display

1
composer require 'drupal/entity_reference_display:^2.0'

Paragraphs

For creating flexible / movable / resusable global blocks. Pair it with Microcontent.

1
composer require 'drupal/paragraphs:^1.18'

https://www.drupal.org/project/paragraphs/

Block field

Reference blocks created with views from the paragraphs modules

There is only a development version of this module which is not ready for Drupal 11

reference a block from a paragraph field

Development for D11 underway

block field

1
composer require 'drupal/block_field:^1.0@RC'

https://www.drupal.org/project/block_field

Backup and Migrate Module

Not ready for Drupal 11

1
composer require 'drupal/backup_migrate:^5.0'

Better Exposed Filters Module

The Better Exposed Filters module replaces the Views’ default single- or multi-select boxes with radio buttons or checkboxes, respectively. Description fields and Select All/None links can be added to exposed filters to make for a better user experience.

Dev version for Drupal 11

1
composer require 'drupal/better_exposed_filters:^7.0@beta'

https://www.drupal.org/project/better_exposed_filters

Metatag Module

  • Set certain content types to no index
  • Set individual pages to no index
  • Add seo meta tags to content

https://www.drupal.org/project/metatag

Works with Drupal 11

1
composer require 'drupal/metatag:^2.0'

Current Page Breadcrumbs Module

Breadcrumbs are native to drupal, however we need the current page Breadcrumb to display

No D11 version

1
composer require 'drupal/current_page_crumb:^1.5'

For a more advanced module that is compadible with D11.

Easy breadcrumb Module

1
composer require 'drupal/easy_breadcrumb:^2.0'

Mega Menu Module

Not compadible with D11

1
https://www.drupal.org/project/simple_megamenu

Antibot Module

Prevent forms from being submitted without JavaScript enabled

No version for drupal 11

1
composer require 'drupal/antibot:^2.0'

reCAPTCHA v3 Module

antispam

Webforms and Webforms UI Modules

forms

Bootstrap5 Themes

1
composer require 'drupal/bootstrap5:^4.0'

Drush

With a new drupal install either locally or on a remote server drush is probably one of the first modules you will want to install. It will help you do lots of other things that would otherwise be too complex to accomplish without Drush.

Get version of Drupal and PHP you are running.

1
drush status

Update sync database

The updatedb command is used to update the locate database on Unix-like systems. This database is used by the locate command to quickly find files on the filesystem.

What Does updatedb Do?

  • Scans the Filesystem: It scans directories in the filesystem to create an index of file names and their locations.
  • Updates the Database: The collected information is stored in a database file, usually located at /var/lib/mlocate/mlocate.db or a similar path.
  • Optimizes Search Performance: By maintaining an indexed list of files, it allows the locate command to retrieve results much faster than using find.

When Should You Run updatedb?

You should run drush updatedb:

  • After Adding or Deleting Many Files: If you’ve recently made significant changes to your filesystem (e.g., installed new software, moved directories, or deleted files), you may want to update the database to reflect these changes.
  • If locate Results Are Outdated: If the locate command isn’t finding files that exist or is showing files that have been deleted, running updatedb will refresh the index.
1
drush updatedb

List installed modules

1
drush pm:list --status=enabled

Remove flag to get all installed modules, regardless of status

Enable Module

1
ddev drush en paragraphs 

List Themes

1
drush pm-list --type=theme

Enable Theme

1
ddev drush theme-enable bootstrap5

Set theme as default

1
2
ddev drush cset system.theme default bootstrap5 --yes
ddev drush cr
1
cd /your/drupal/site/
1
drush uli --uri example.com

Trouble Shooting

The Drush launcher could not find a local Drush in your Drupal site.

1
vendor/bin/drush uli --uri http://your-site-url

Find Drush version

1
drush --version

Update Drush

You might need to add the ^ to the beginning of the version number ie ^12.0.2 in the composer.json file.

1
composer update drush/drush

Uninstall Drupal Module

1
drush pm:uninstall module_name

Remove the Module:

1
composer remove drupal/module_name

** same command but with ddev **

Uninstall the Module:

1
ddev drush pm:uninstall module_name

Remove the Module:

1
ddev composer remove drupal/module_name

Drupal Views

Customizing Views

Customizing views is Best done from the view admin rather then the twig files. the view admin gives you all the options to add classes and html.

view field customization

Outputting a block view

if you have generated a block with your view ensure to output it in one of the following ways

  • block layout
  • as a paragraph block

Create a Hero Banner

see sample screenshot - https://share.cleanshot.com/0rSd0qQ2

  • block type for banner
  • content type for homepage
  • view to get the fields from homepage
  • display mode to only display the required fields

https://youtu.be/sTRiJR7MiXQ?si=i6CRY6aBlNVWNJfH

  • create view
  • go to contextual filters
  • search for tax - select has taxonomy term id When the filter value is not available
  • provide a default value - Taxonomy term ID from URL.

Create a view block with fields

Create the view of content type Select the format to display fields Add specific fields to the fields section (fields need to be created for your content type) Add css class for container Set title to be none

Display content just from the current node

  • Add a contextual filter
  • Add content then ID
  • Click provide default value
  • Content id from url
  • Hit apply

as you are using the view to output content the content will also appear on the page from the fields itself on the content type. set them to disabled to hide them

Views basics

views are like a custom query in wp. you can create a page out of a view or a block

Examples

  • blog feed

Fields

you can choose what fields are added to the view

  • fields can be reordered

Formats

  • table
  • grid
  • unformatted list
  • html list

Filter

  • filter criteria of the view by taxonomy
  • filters can be exposed to users on the frontend

Duplicating a view

  • go to structure, content, duplicate view

Drupals block based page builder

It was awesome to discover that you could work with the same “block based page builder” in Drupal” using the wonderful Paragraphs module in way same way as the Wordpress ACF block based page builder.

** Prerequisites **

  • An operational Drupal site
  • The paragraphs module installed

Create paragraph types

  • Go to structure
  • Go to paragraph types

Add a paragraph type

You can create a paragraph type for:

  • Grid
  • Cards
  • Gallery
  • Text with image
  • Whatever

Add fields to your paragraph type

  • Text
  • Image

Remove labels

  • Go to /admin/structure/paragraphs_type
  • go to manage display

managedisplay option in dropdown list

  • set labels to hidden

hide labels

Attach paragraphs to the content type

  • Go to structure
  • Content types
  • Locate the basic page content type or another content type of your preference
  • Manage fields (hit the main btn label on basic page)
  • Add field
  • Select paragraph under reference revisions
  • Give it a name like “page blocks” or “cpt blocks”
  • Type of item to reference is “paragraph”
  • Set to unlimited
  • Choose to include or exclude your paragraph blocks

Back on a basic page

  • the blocks you added should be there

Config paragraph display settings

  • Go to structure
  • Go to content types
  • Go to basic page - manage form display
  • Scroll down to paragraphs / blocks
  • Hit the cog wheel to open the settings

admin of paragraphs module display settings

creating custom paragraph blocks

Custom Paragraph Blocks Drupal

If you’re already read some of my other articles related to the Drupal way of creating a modern flexible content page builder with paragraphs module [[2023-01-18-Drupals-block-based-page-builder]] then you may now want to take it a step further with this article about paragraph blocks

Setup the paragraphs directory

1
2
3
cd your_theme_folder
mkdir templates/paragraphs
cd templates/paragraphs

Use twig debug to check for filename overide suggestions

turn on twig debug

1
touch paragraphs--twig-debug-suggestion.html.twig

Copy the existing module template into your custom theme

1
cp modules/contrib/paragraphs/templates/paragraph.html.twig your_theme_folder/templates/paragraphs/paragraphs--twig-debug-suggestion.html.twig

Paragraph template initial code

code from modules/contrib/paragraphs/templates/paragraph.html.twig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{%
  set classes = [
    'paragraph',
    'paragraph--id--' ~ paragraph.id(),
    'paragraph--type--' ~ paragraph.bundle|clean_class,
    view_mode ? 'paragraph--view-mode--' ~ view_mode|clean_class,
    not paragraph.isPublished() ? 'paragraph--unpublished',
  ]
%}
{% block paragraph %}
  <div{{ attributes.addClass(classes) }}>
    {% block content %}
      {{ content }}
    {% endblock %}
  </div>
{% endblock paragraph %}

Overide with your custom code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{%
  set classes = [
    'paragraph',
    'paragraph--type--' ~ paragraph.bundle|clean_class,
    view_mode ? 'paragraph--view-mode--' ~ view_mode|clean_class,
    not paragraph.isPublished() ? 'paragraph--unpublished',
    'my-webform-paragraph-module'
  ]
%}
{% block paragraph %}
  <div{{ attributes.addClass(classes) }}>
    {% block content %}
       <div class="container">

       <div class="row">
          <div class="col-lg-8 bg-primary">
             <div class="box">
                {{content.field_form_title}}
                {{ content.field_form_intro_text }}
             </div>
          </div>

          <div class="col">
             <div class="box">
                {{content.field_webform}}
             </div>
          </div>
       </div>
       </div>

    {% endblock %}
  </div>
{% endblock paragraph %}

Managing Images in Drupal

Drupal shines when it comes to managing images. Specifically around serving an appropriate size images to the user regardless of what size the image was originally loaded into the site.

Enable media embed modules

  • swap out the default media icon module for the drupal embed media icon module

When you add images to the Text (formatted, long, with summary). you want to have a choice of image sizes for it to display at.

What image size do we need

  • wide
  • square
  • full landscape
  • portrait
  1. enable the built in media modules
  2. create styles /admin/config/media/image-styles
  3. create view modes /admin/structure/display-modes/view
  4. go to /admin/structure/media/manage/image/display/ and set your custom view mode to use your new image style

edit ckeditor

  • edit your ckeditor mode /admin/config/content/formats/
  • choose basic html or whatever editor you enabled
  • scroll down to CKEditor 5 plugin settings and choose media
  • enable user can overide default view settings
  • scroll down to embed media
  • enable the custom image sizes under ` View modes selectable in the ‘Edit media’ dialog `

embed custom image sizes in drupal ckeditor

Setting custom image sizes

Best to implement image sizes at the start of the project

Its built into the core

  • create your ideal image size
  • In the Manage Display apply the custom image size by hitting the cog wheel to your image field on your paragraph block

/admin/config/media/image-styles

** Add in your style **

  • crop in which can set a custom size

Build Drupal Pages with Content Reference Entities

A content entity based page construction technique. This technique is native to Drupal and is suitable for working with premade finished nodes of content. So you can create content once and then reference it from unlimited nodes. This is the central powerful feature of this type of website build.

Its a 3 step process

  1. Create the content type with all the required fields
  2. Create the content based on the content type
  3. Reference/pull in this content from other content types such as basic page or a parent block of some sort.

What are the benefits?

  • Global blocks (1 central source of content for a node, that can be reused in different content entities)
  • Translatable
  • Close to core implementation
  • Flexible content ordering via drag and drop

How to build blocks with content entities

Blocks are content types

  • Create a content type for a block.
  • Add a field to the block that is of type content (which is under reference)
  • Go to manage fields
  • Go to entity reference (under field type)
  • Go to edit
  • Under reference types add all the content types the field is able to refernce

Attach to a page

  • Add the content reference field to a basic page content type
  • Go to content types
  • Go to manage fields

Example

The most basic use-case for this technique is allowing blocks to be added to a basic page

  • Add a content reference field to the basic page content type
  • Allow the field to reference other content types

Blocks can reference other blocks

Create a slider block that can slide cards and or articles

  • Create a content type called slider
  • Create a content type called card
  • In the slider content type add a content reference field
  • Allow the reference field to access the Card and Artical content types

    img-description screenshot of the manage fields area of the slider block

Block display

By default the block you reference display as links to the content. This is not useful. What we need is the full content to display.

** Display the full block content **

  • Go to structure / content types
  • Select your block content type
  • Go to manage fields
  • Add field of display mode
  • Create a display mode
  • Hit save on the following screen

** Set the display option **

On the following edit field screen, under excluded display modes

  • Choose full
  • Choose include selected display modes
  • Save settings

Set the entity reference field display mode

Go back to manage fields (use the breadcrumbs at the top of the screen)

  • Under manage display
  • Locate the entity reference field
  • Under the format column choose selected display mode
  • Hit save

Now back on the frontend, rather then the block displaying a link to edit its content it will display the full content

Global Blocks with Drupal Paragraphs and Block Modules

To archive flexible page layouts made up of global block content.

Prerequisites

Key parts

  1. Create a block. it can be of a custom block type
  2. Create a paragraph type admin/structure/paragraphs_type
  3. Add a field and choose block(plugin) block value_provided by block module
  4. Set the default value to your block on the edit field settings page. set your block
  5. Add your global block to the page using paragraph module

Create a page builder

Create a page builder for your drupal pages by utilising nested paragraph blocks

Custom display modes

Drupal offers some ways to change the appearance of your content forms in the admin and also the appearance of your content on the frontend view. This article is focused on archiving different Display modes for the frontend view by showing how to create and apply a display mode

Create a display mode for the content type.

Create a custom dipslay mode for the content type ` /admin/structure/display-modes/view `

Enable the display mode for the content type.

  • Go to manage display
  • in the custom display settings
  • enable your custom display mode

Configure the fields

Enable or disable, all your fields so that your display mode reflects the content you want returned.

Configure your paragraph to use the display mode

  • On your custom paragraph Type.
  • In the mangage display,
  • The format needs to be set to rendered entity
  • Assign your new custom display mode. which is hidden under the cog wheel

Preprocess Functions

Preprocess functions are used to modify variables before they are used in the template. This allows developers to customize the output of a page without having to modify the template itself. Preprocess functions are used to add, remove, or alter variables that are used in the template.

Preprocess functions are typically used when you need to modify the variables that are used in the template. For example, if you need to add a custom variable to the template, you can use a preprocess function to do so.

Where to add these functions

themename.theme

Overview

  • preprocess functions are optional - if they can be used they will be if not they will be skipped
  • preprocess functions are Specific to a template (hook) based on naming convention
  • preprocess functions always has 1 param

When to use preprocess functions

  • Use a preprocess function to alter/remove or add variables
  • Make small alterations to the html output
  • Any logic that is more complex then a simple if / else file consider refactoring into a preprocess function to keep template files nice and clean.

Naming Convention

function THEME_preprocess_H00K()

  • HOOK is the name of the template your preprocessing variables for

The easiest way to determine what hook to use is to enable twig debug mode

Example

1
2
3
4
5
function THEMENAME_preprocess_node(&$variables) {
   $variables['simple_string'] = array(
   '#markup' => 'a simple_string'
   );
}
1
2
3
<div class = "string">
   {{simple_string}}
</div>

** Example - Add suffix to post author lable if logged in **

1
2
3
4
5
function THEMENAME_preprocess_node(&$variables) {
   if($variables['logged_in'] == TRUE && $variables['node']->getOwnerId() == $variables['user']->id()) {
      $variables['label']['#suffix'] = '-you are the author';
   }
}

** Example - Add new variable to template **

Use twig dump function to see the new variable added to the template

1
2
3
4
5
6
7
8
function THEMENAME_preprocess_node(&$variables) {
   $variables['current_user_is_owner'] = FALSE;

   if($variables['logged_in'] == TRUE && $variables['node']->getOwnerId() == $variables['user']->id()) {
      $variables['label']['#suffix'] = '-you are the author';
      $variables['current_user_is_owner'] = TRUE;
      }
}

Function Naming Conventions

function THEME_preprocess_HOOK()

1
2
**example**
function bartik_preprocess_page()

** More specific targeting example **

1
2
3
THEMENAME_preprocess_node__42(): This is the most specific version and will only be called for a node with an ID of 42.
THEMENAME_preprocess_node__article(): This would be called for any article node, but not for other node types.
THEMENAME_preprocess_node(): This will be called for all nodes.
  • The hook refers to the base name of the template file
  • Use Twig Debug to find the hook name for the template file

You can ommit the hook part and add hook as param instead, for adding a variable to every template file - warning this will be called alot

Inject var into every template

1
2
function THEMENAME_preprocess(&$variables, $hook)

Dynamically add layout class to a field

Based on a boolean value which is set in the paragraph block.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function THEME_preprocess_paragraph(&$variables) {
  // Get the paragraph type.
  $paragraph_type = $variables['paragraph']->bundle();

  if ($paragraph_type === 'cards') {
    // Add a class to the paragraph's attributes.
    $variables['attributes']['class'][] = 'container cards__wrapper';

    //dynamically add layout class to cards to set the layout
    // Access the boolean field value.
    $boolean_value = $variables['paragraph']->get('field_brand')->value;

    // Add a variable for use in the Twig template.
    $variables['is_active'] = (bool) $boolean_value;

    // Optionally, add logic based on the value.
    if ($boolean_value) {
      if (isset($variables['content']['field_card_micro_content'])) {
        // Add a custom CSS class to the field wrapper.
        $variables['content']['field_card_micro_content']['#attributes']['class'][] = 'row-cols-2';
      }
    } else {
      if (isset($variables['content']['field_card_micro_content'])) {
        // Add a custom CSS class to the field wrapper.
        $variables['content']['field_card_micro_content']['#attributes']['class'][] = 'row-cols-4';
      }
    }
  }
  }

Extensive example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
function AERO_preprocess_field(&$variables) {

  /**
   * title 1 for edge advantage
   */
    if ($variables['element']['#field_name'] == 'field_aero_edge_title_1') {
      // Pass a flag or tag type to the template.
      $variables['custom_tag'] = 'h3';
      $variables['attributes']['class'][] = 'edge-advantage__title';
    }

  /**
   * title 2 for edge advantage
   */
  if ($variables['element']['#field_name'] == 'field_aero_edge_title_2') {
    // Pass a flag or tag type to the template.
    $variables['custom_tag'] = 'h3';
    $variables['attributes']['class'][] = 'edge-advantage__title';
  }

  /**
   * title 3 for edge advantage
   */
  if ($variables['element']['#field_name'] == 'field_aero_edge_title_3') {
    // Pass a flag or tag type to the template.
    $variables['custom_tag'] = 'h3';
    $variables['attributes']['class'][] = 'edge-advantage__title';
  }

  // Add a custom CSS class to edge_text field
  if ($variables['element']['#field_name'] == 'field_aero_edge_text_1') {
    $variables['attributes']['class'][] = 'edge-advantage__text';
  }

  if ($variables['element']['#field_name'] == 'field_aero_edge_text_2') {
    $variables['attributes']['class'][] = 'edge-advantage__text';
  }

  if ($variables['element']['#field_name'] == 'field_aero_edge_text_3') {
    $variables['attributes']['class'][] = 'edge-advantage__text';
  }
}

//More efficient way to add classes to fields 

function AERO_preprocess_field(&$variables) {
  // Define a mapping of field names to CSS classes.
  $field_class_map = [
    'field_stat_number_1' => 'stats__number',
    'field_stat_number_2' => 'stats__number',
  ];

  // Check if the current field is in the map and apply the corresponding class.
  $field_name = $variables['element']['#field_name'];
  if (isset($field_class_map[$field_name])) {
    $variables['attributes']['class'][] = $field_class_map[$field_name];
  }
}

//Target paragraphs on a page

function THEMENAME_preprocess_paragraph(&$variables) {

  /**
   * Implements hook_preprocess_paragraph().
   */
    // Get the paragraph type.
    $paragraph_type = $variables['paragraph']->bundle();

    // Add a CSS class based on paragraph type.
    if ($paragraph_type === 'stats') {
      // Add a class to the paragraph's attributes.
      $variables['attributes']['class'][] = 'container stats__wrapper';
    }

    // Add a class based on specific conditions.
    if ($paragraph_type === 'image_block' && $variables['paragraph']->id() == 123) {
      $variables['attributes']['class'][] = 'special-image-block-class';
    }
}


More info

more

Microcontent

Microcontent is a drupal module for usable fragments of content that can be configured to display throughout your site.

  • Microcontent is a module
  • Microcontent is the blueprint for reusable content
  • Reference peices of microcontent from paragraphs module
  • When working with microcontent and paragraphs - create the microcontent first and then the paragraph block to reference it.

** Tips **

  • sometimes its best to set the paragraphs to locate the microcontent via checkboxes rather then autocomplete as the user may not know what options are available

** Trouble Shooting **

Paragraphs not finding the microcontent?

  • ensure the label is not disabled or hidden in the field display settings. by default the reference field locats content via the label

Display

  • Configure the display of microcontent in the admin by disabling fields in the manage display

Frontend

By default the microcontent you reference display as links to the content. This is not useful. What we need is the full content to display.

install entity reference display module

Display the full block content

  • Go to structure / content types
  • Select your paragraph type
  • Go to manage fields
  • Add field of display mode
  • Create a display mode
  • Hit save on the following screen

Set the display option

On the following edit field screen, under excluded display modes

  • Choose full
  • Choose include selected display modes
  • Save settings

Set the entity reference field display mode

Go back to manage fields (use the breadcrumbs at the top of the screen)

  • Under manage display
  • Locate the entity reference field
  • Under the format column choose selected display mode
  • Hit save

Now back on the frontend, rather then the block displaying a link to edit its content it will display the full content

Radix

Radix is a Drupal theme that evan has its own cli. Pretty cool. Radix promotes the use of self contained components. By using the radix cli, you can create your own components to override the radix components. I made this list of radix templates I find myself overriding all the time so having this list makes it easier and faster for me to override the default radix templates. I dont use the cli that much as that is meant for self contained components which i dont use.

1
drupal-radix-cli add block

Then you can override the block using the block component in your radix theme.

1
2
3
4
5
6
7
8
9
10
{#
/**
 * @file
 * Template for a block.
 */
#}

{% include 'radix:block' with {
  label: false,
} %}

Whatever option you choose from the for coming list, all the necessary files will be copied into your themes component folder to create an overriding component. That being said, you can still override these templates in the manual way by getting the theme template suggested name of the template and override the radix template with the relative code samples below.

** Prerequisites **

twig debug installed

Heading

1
2
3
4
5
6
7
8
9
  {% if title %}
    {%
      include 'radix:heading' with {
        heading_html_tag: 'h3',
        content: title
      }
    %}
  {% endif %}

Refers to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
{#
/**
 * @file
 * Heading component.
 *
 * Bootstrap Documentation
 * https://getbootstrap.com/docs/5.3/content/typography/#headings
 *
 * Full List Utility Classes
 * https://github.com/twbs/bootstrap/blob/v5.3.0/dist/css/bootstrap.css#L214
 *
 * Available properties:
 * - heading_html_tag : The HTML tag to use for the header.
 * Defaults to h1 (h1|h2|h3|h4|h5|h6)
 * - display: When you need a heading to stand out, consider using a display heading—a larger,
 * slightly more opinionated heading style.
 * - content: Content text for the heading.
 * - heading_attributes: Attributes array.
 * - heading_utility_classes: This property contains an array of utility classes.
 * - heading_link_utility_classes: This property contains an array of utility classes.
 * - title_link: Optional link for the heading. If provided, it wraps the heading.
 */
#}
{% apply spaceless %}

{% set heading_html_tag = heading_html_tag|default('h2') %}
{% set display = [display] ?? [] %}
{% set heading_attributes = heading_attributes ?: create_attribute() %}
{% set heading_classes = display|merge(heading_utility_classes ?: []) %}

{% if title_link %}
  {% set heading_link_attributes = create_attribute().setAttribute('href', title_link) %}
  {% set heading_link_classes = heading_link_utility_classes ?: [] %}
{% endif %}

{% if content %}
  <{{heading_html_tag}} {{ heading_attributes.addClass(heading_classes) }}>
    {% block heading_content %}
      {% if title_link %}
        <a {{ heading_link_attributes.addClass(heading_link_classes) }}>
          {{ content }}
        </a>
      {% else %}
        {{ content }}
      {% endif %}
    {% endblock %}
  </{{heading_html_tag}}>
{% endif %}

{% endapply %}

Field

In twig debug you should see

BEGIN OUTPUT from 'themes/custom/theme_name/templates/field/field.html.twig' and in this file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{% include 'radix:field' with {
  label_hidden: false,
  multiple: true,
  label: 'Field Label',
  items: [
    { content: '<p>Item one content</p>' },
    { content: '<p>Item two content</p>' },
    { content: '<p>Item three content</p>' }
  ],
  entity_type: 'node',
  field_name: 'field_example',
  field_type: 'text_long',
  label_display: 'above',
  attributes: create_attribute(),
  title_attributes: create_attribute()
} %}

The code from field.twig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
{#
/**
 * @file
 * Template for a field component.
 *
 * To override output, copy the "field.html.twig" from the templates directory
 * to your theme's directory and customize it, just like customizing other
 * Drupal templates such as page.html.twig or node.html.twig.
 *
 * Instead of overriding the theming for all fields, you can also just override
 * theming for a subset of fields using
 * @link themeable Theme hook suggestions. @endlink For example,
 * here are some theme hook suggestions that can be used for a field_foo field
 * on an article node type:
 * - field--node--field-foo--article.html.twig
 * - field--node--field-foo.html.twig
 * - field--node--article.html.twig
 * - field--field-foo.html.twig
 * - field--text-with-summary.html.twig
 * - field.html.twig
 *
 * Available variables:
 * - attributes: HTML attributes for the containing element.
 * - label_hidden: Whether to show the field label or not.
 * - title_attributes: HTML attributes for the title.
 * - label: The label for the field.
 * - multiple: TRUE if a field can contain multiple items.
 * - items: List of all the field items. Each item contains:
 *   - attributes: List of HTML attributes for each item.
 *   - content: The field item's content.
 * - entity_type: The entity type to which the field belongs.
 * - field_name: The name of the field.
 * - field_type: The type of the field.
 * - label_display: The display settings for the label.
 * - field_utility_classes: Additional classes to be added to the field wrapper.
  * - field_title_utility_classes: Additional classes to be added to the field title wrapper.
 *
 *
 * @see template_preprocess_field()
 */
#}
{%
  set classes = [
    'field',
    'field--name-' ~ field_name|clean_class,
    'field--type-' ~ field_type|clean_class,
    'field--label-' ~ label_display,
  ]
%}
{%
  set title_classes = [
    'field__label',
    label_display == 'visually_hidden' ? 'visually-hidden',
  ]
%}

{% if label_hidden %}
{% if multiple %}
<div{{ attributes.addClass(classes, 'field__items') }}>
{% for item in items %}
<div{{ item.attributes.addClass('field__item') }}>{{ item.content }}</div>
{% endfor %}
</div>
{% else %}
{% for item in items %}
<div{{ attributes.addClass(classes, 'field__item') }}>{{ item.content }}</div>
{% endfor %}
{% endif %}
{% else %}
<div{{ attributes.addClass(classes) }}>
<div{{ title_attributes.addClass(title_classes) }}>{{ label }}</div>
{% if multiple %}
<div class='field__items'>
{% endif %}
{% for item in items %}
<div{{ item.attributes.addClass('field__item') }}>{{ item.content }}</div>
{% endfor %}
{% if multiple %}
</div>
{% endif %}

  </div>
{% endif %}

Copy the above code into your custom file using one of the suggestions provided by twig debug

1
2
3
4
5
6
{%
  include 'radix:breadcrumb' with {
    breadcrumb: breadcrumb,
  }
%}

refers to

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{#
/**
 * @file
 * Template for a Breadcrumb component.
 *
 * Available config:
 * - breadcrumb_utility_classes: An array of utility classes.
 */
#}
{% apply spaceless %}

{%
  set breadcrumb_classes = [
    'breadcrumb',
  ]|merge(breadcrumb_utility_classes ?: [])
%}
{% set breadcrumb_attributes = attributes ?: create_attribute() %}

{% block breadcrumb %}
  {% if breadcrumb %}
    <nav aria-label="breadcrumb">
      <ol {{ breadcrumb_attributes.addClass(breadcrumb_classes) }}>
        {% for item in breadcrumb %}
          <li class="breadcrumb-item {{ not item.url ? 'active' }}">
            {% if item.url %}
              <a href="{{ item.url }}">{{ item.text }}</a>
            {% else %}
              {{ item.text }}
            {% endif %}
          </li>
        {% endfor %}
      </ol>
    </nav>
  {% endif %}
{% endblock %}

{% endapply %}

Block

1
2
3
4
5
6
7
8
9
10
{#
/**
 * @file
 * Template for a block.
 */
#}
{% include 'radix:block' with {
  html_tag: 'div',
} %}

Referring to file in radix/src/components/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{#
/**
 * @file
 * Template for a Block component.
 *
 * Available config:
 * - html_tag: The HTML tag for the block.
 * - utility_classes: An array of utility classes.
 */
#}
{% set classes = [
  'block',
  bundle ? 'block--' ~ bundle|clean_class,
  id ? 'block--' ~ id|replace({"_": "-"})|clean_class,
]|merge(utility_classes ? utility_classes : []) %}

{% if html_tag %}
  <{{ html_tag }}{{ attributes|without('id').addClass(classes) }}>
{% endif %}

  {{ title_prefix }}
  {% if label %}
    {% block label %}
      <h2{{ title_attributes }}>{{ label }}</h2>
    {% endblock %}
  {% endif %}
  {{ title_suffix }}

  {% block content %}
    {{ content }}
  {% endblock %}

{% if html_tag %}
  </{{ html_tag }}>
{% endif %}

Views

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
{#
/**
 * @file
 * Theme override for main view template.
 *
 * Available variables:
 * - attributes: Remaining HTML attributes for the element.
 * - css_name: A css-safe version of the view name.
 * - css_class: The user-specified classes names, if any.
 * - header: The optional header.
 * - footer: The optional footer.
 * - rows: The results of the view query, if any.
 * - empty: The content to display if there are no rows.
 * - pager: The optional pager next/prev links to display.
 * - exposed: Exposed widget form/info to display.
 * - feed_icons: Optional feed icons to display.
 * - more: An optional link to the next page of results.
 * - title: Title of the view, only used when displaying in the admin preview.
 * - title_prefix: Additional output populated by modules, intended to be
 *   displayed in front of the view title.
 * - title_suffix: Additional output populated by modules, intended to be
 *   displayed after the view title.
 * - attachment_before: An optional attachment view to be displayed before the
 *   view content.
 * - attachment_after: An optional attachment view to be displayed after the
 *   view content.
 * - dom_id: Unique id for every view being printed to give unique class for
 *   Javascript.
 *
 * @see template_preprocess_views_view()
 */
#}
{% apply spaceless %}

{% set views_attributes = attributes ?: create_attribute() %}
{% set views_title_attributes = views_title_attributes ?: create_attribute() %}
{% set views_header_attributes = views_header_attributes ?: create_attribute() %}
{% set views_filters_attributes = views_filters_attributes ?: create_attribute() %}
{% set views_rows_attributes = views_rows_attributes ?: create_attribute() %}
{% set views_empty_attributes = views_empty_attributes ?: create_attribute() %}
{% set views_footer_attributes = views_footer_attributes ?: create_attribute() %}
{% set views_attachment_before_attributes = views_attachment_before_attributes ?: create_attribute() %}
{% set views_attachment_after_attributes = views_attachment_after_attributes ?: create_attribute() %}
{% set views_pager_attributes = views_pager_attributes ?: create_attribute() %}
{% set views_more_attributes = views_more_attributes ?: create_attribute() %}
{% set views_feed_icons_attributes = views_feed_icons_attributes ?: create_attribute() %}

{%
  set views_classes = [
    'view',
    'view-' ~ id|clean_class,
    'view-id-' ~ id,
    'view-display-id-' ~ display_id,
    dom_id ? 'js-view-dom-id-' ~ dom_id,
    css_name ? 'view-' ~ css_name,
  ]|merge(view_view_utility_classes ?: [])
%}

{%
  set views_title_classes = [
    'view-title',
  ]|merge(views_title_utility_classes ?: [])
%}

{%
  set views_header_classes = [
    'view-header',
  ]|merge(views_header_utility_classes ?: [])
%}

{%
  set views_filters_classes = [
    'view-filters',
  ]|merge(views_filters_utility_classes ?: [])
 %}

{%
  set views_rows_classes = [
    'view-content',
  ]|merge(views_rows_utility_classes ?: [])
%}

{%
  set views_empty_classes = [
    'view-empty',
  ]|merge(views_empty_utility_classes ?: [])
%}

{%
  set views_footer_classes = [
    'view-footer',
  ]|merge(views_footer_utility_classes ?: [])
%}

{%
  set views_attachment_before_classes = [
    'attachment',
    'attachment-before',
  ]|merge(views_attachment_before_utility_classes ?: [])
%}

{%
  set views_attachment_after_classes = [
    'attachment',
    'attachment-after',
  ]|merge(views_attachment_after_utility_classes ?: [])
%}

{%
  set views_pager_classes = [
    'pager',
  ]|merge(views_pager_utility_classes ?: [])
%}

{%
  set views_more_classes = ['']|merge(views_more_utility_classes ?: [])
%}

{%
  set views_feed_icons_classes = [
    'feed-icons',
  ]|merge(views_feed_icons_utility_classes ?: [])
%}

<div {{ views_attributes.addClass(views_classes) }}>
  {% block views_view_wrapper %}
    {{ title_prefix }}
    {% block views_view_title %}
      {% if title %}
        <div {{ views_title_attributes.addClass(views_title_classes) }}>
          {{ title }}
        </div>
      {% endif %}
    {% endblock %}
    {{ title_suffix }}

    {% block views_header %}
      {% if header %}
        <div {{ views_header_attributes.addClass(views_header_classes) }}>
          {{ header }}
        </div>
      {% endif %}
    {% endblock %}

    {% block views_filters %}
      {% if exposed %}
        <div {{ views_filters_attributes.addClass(views_filters_classes) }}>
          {{ exposed }}
        </div>
      {% endif %}
    {% endblock %}

    {% block views_attachment_before %}
      {% if attachment_before %}
        <div {{ views_attachment_before_attributes.addClass(views_attachment_before_classes) }}>
          {{ attachment_before }}
        </div>
      {% endif %}
    {% endblock %}

    {% block views_rows %}
      {% if rows %}
        <div {{ views_rows_attributes.addClass(views_rows_classes) }}>
          {{ rows }}
        </div>
      {% elseif empty %}
        <div {{ views_empty_attributes.addClass(views_empty_classes) }}>
          {{ empty }}
        </div>
      {% endif %}
    {% endblock %}

    {% block views_pager %}
      {% if pager %}
        <div {{ views_pager_attributes.addClass(views_pager_classes) }}>
          {{ pager }}
        </div>
      {% endif %}
    {% endblock %}

    {% block views_attachment_after %}
      {% if attachment_after %}
        <div {{ views_attachment_after_attributes.addClass(views_attachment_after_classes) }}>
          {{ attachment_after }}
        </div>
      {% endif %}
    {% endblock %}

    {% block views_more %}
      {% if more %}
        {{ more|add_class(views_more_classes) }}
      {% endif %}
    {% endblock %}

    {% block views_footer %}
      {% if footer %}
        <div {{ views_footer_attributes.addClass(views_footer_classes) }}>
          {{ footer }}
        </div>
      {% endif %}
    {% endblock %}

    {% block views_feed_icons %}
      {% if feed_icons %}
        <div {{ views_feed_icons_attributes.addClass(views_feed_icons_classes) }}>
          {{ feed_icons }}
        </div>
      {% endif %}
    {% endblock %}
  {% endblock %}
</div>

{% endapply %}

Radix views view component

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{% include 'radix:views-view' with {
  title: 'View Title',
  header: view_header,
  footer: view_footer,
  rows: view_rows,
  empty: 'No data available',
  pager: view_pager,
  exposed: exposed_filters,
  feed_icons: feed_icons,
  more: more_link,
  attachment_before: attachment_before_content,
  attachment_after: attachment_after_content,
  css_name: 'banner',
  dom_id: 'unique-dom-id',
  view_view_utility_classes: ['bg-primary, p-5, d-flex, align-items-center']
} %}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
{#
/**
 * @file
 * Theme override for main view template.
 *
 * Available variables:
 * - attributes: Remaining HTML attributes for the element.
 * - css_name: A css-safe version of the view name.
 * - css_class: The user-specified classes names, if any.
 * - header: The optional header.
 * - footer: The optional footer.
 * - rows: The results of the view query, if any.
 * - empty: The content to display if there are no rows.
 * - pager: The optional pager next/prev links to display.
 * - exposed: Exposed widget form/info to display.
 * - feed_icons: Optional feed icons to display.
 * - more: An optional link to the next page of results.
 * - title: Title of the view, only used when displaying in the admin preview.
 * - title_prefix: Additional output populated by modules, intended to be
 *   displayed in front of the view title.
 * - title_suffix: Additional output populated by modules, intended to be
 *   displayed after the view title.
 * - attachment_before: An optional attachment view to be displayed before the
 *   view content.
 * - attachment_after: An optional attachment view to be displayed after the
 *   view content.
 * - dom_id: Unique id for every view being printed to give unique class for
 *   Javascript.
 *
 * @see template_preprocess_views_view()
 */
#}
{% apply spaceless %}

{% set views_attributes = attributes ?: create_attribute() %}
{% set views_title_attributes = views_title_attributes ?: create_attribute() %}
{% set views_header_attributes = views_header_attributes ?: create_attribute() %}
{% set views_filters_attributes = views_filters_attributes ?: create_attribute() %}
{% set views_rows_attributes = views_rows_attributes ?: create_attribute() %}
{% set views_empty_attributes = views_empty_attributes ?: create_attribute() %}
{% set views_footer_attributes = views_footer_attributes ?: create_attribute() %}
{% set views_attachment_before_attributes = views_attachment_before_attributes ?: create_attribute() %}
{% set views_attachment_after_attributes = views_attachment_after_attributes ?: create_attribute() %}
{% set views_pager_attributes = views_pager_attributes ?: create_attribute() %}
{% set views_more_attributes = views_more_attributes ?: create_attribute() %}
{% set views_feed_icons_attributes = views_feed_icons_attributes ?: create_attribute() %}

{%
  set views_classes = [
    'view',
    'view-' ~ id|clean_class,
    'view-id-' ~ id,
    'view-display-id-' ~ display_id,
    dom_id ? 'js-view-dom-id-' ~ dom_id,
    css_name ? 'view-' ~ css_name,
  ]|merge(view_view_utility_classes ?: [])
%}

{%
  set views_title_classes = [
    'view-title',
  ]|merge(views_title_utility_classes ?: [])
%}

{%
  set views_header_classes = [
    'view-header',
  ]|merge(views_header_utility_classes ?: [])
%}

{%
  set views_filters_classes = [
    'view-filters',
  ]|merge(views_filters_utility_classes ?: [])
 %}

{%
  set views_rows_classes = [
    'view-content',
  ]|merge(views_rows_utility_classes ?: [])
%}

{%
  set views_empty_classes = [
    'view-empty',
  ]|merge(views_empty_utility_classes ?: [])
%}

{%
  set views_footer_classes = [
    'view-footer',
  ]|merge(views_footer_utility_classes ?: [])
%}

{%
  set views_attachment_before_classes = [
    'attachment',
    'attachment-before',
  ]|merge(views_attachment_before_utility_classes ?: [])
%}

{%
  set views_attachment_after_classes = [
    'attachment',
    'attachment-after',
  ]|merge(views_attachment_after_utility_classes ?: [])
%}

{%
  set views_pager_classes = [
    'pager',
  ]|merge(views_pager_utility_classes ?: [])
%}

{%
  set views_more_classes = ['']|merge(views_more_utility_classes ?: [])
%}

{%
  set views_feed_icons_classes = [
    'feed-icons',
  ]|merge(views_feed_icons_utility_classes ?: [])
%}

<div {{ views_attributes.addClass(views_classes) }}>
  {% block views_view_wrapper %}
    {{ title_prefix }}
    {% block views_view_title %}
      {% if title %}
        <div {{ views_title_attributes.addClass(views_title_classes) }}>
          {{ title }}
        </div>
      {% endif %}
    {% endblock %}
    {{ title_suffix }}

    {% block views_header %}
      {% if header %}
        <div {{ views_header_attributes.addClass(views_header_classes) }}>
          {{ header }}
        </div>
      {% endif %}
    {% endblock %}

    {% block views_filters %}
      {% if exposed %}
        <div {{ views_filters_attributes.addClass(views_filters_classes) }}>
          {{ exposed }}
        </div>
      {% endif %}
    {% endblock %}

    {% block views_attachment_before %}
      {% if attachment_before %}
        <div {{ views_attachment_before_attributes.addClass(views_attachment_before_classes) }}>
          {{ attachment_before }}
        </div>
      {% endif %}
    {% endblock %}

    {% block views_rows %}
      {% if rows %}

        <div {{ views_rows_attributes.addClass(views_rows_classes) }}>
          {{ rows }}
        </div>
      {% elseif empty %}
        <div {{ views_empty_attributes.addClass(views_empty_classes) }}>
          {{ empty }}
        </div>
      {% endif %}
    {% endblock %}

    {% block views_pager %}
      {% if pager %}
        <div {{ views_pager_attributes.addClass(views_pager_classes) }}>
          {{ pager }}
        </div>
      {% endif %}
    {% endblock %}

    {% block views_attachment_after %}
      {% if attachment_after %}
        <div {{ views_attachment_after_attributes.addClass(views_attachment_after_classes) }}>
          {{ attachment_after }}
        </div>
      {% endif %}
    {% endblock %}

    {% block views_more %}
      {% if more %}
        {{ more|add_class(views_more_classes) }}
      {% endif %}
    {% endblock %}

    {% block views_footer %}
      {% if footer %}
        <div {{ views_footer_attributes.addClass(views_footer_classes) }}>
          {{ footer }}
        </div>
      {% endif %}
    {% endblock %}

    {% block views_feed_icons %}
      {% if feed_icons %}
        <div {{ views_feed_icons_attributes.addClass(views_feed_icons_classes) }}>
          {{ feed_icons }}
        </div>
      {% endif %}
    {% endblock %}
  {% endblock %}
</div>

{% endapply %}

unformatted views

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{%
  include 'radix:views-view--unformatted' with {
    title: 'View Title',
    rows: view_rows,
    row_utility_classes: [
      'col-12',
      'col-md-6',
      'col-lg-4',
    ],
    default_row_class: true,
    rows: rows|map(row => row|merge({
      attributes: row.attributes.setAttribute('data-row-color', 'purple')
    })),
  }
%}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
{#
/**
 * @file
 * Implements a template to display a view of unformatted rows.
 *
 * Available variables:
 * - title: The title of this group of rows. Can be empty.
 * - rows: An array of the view's row items. Each row contains:
 *   - attributes: The HTML attributes for the row.
 *   - content: The actual content of the row.
 * - view: The view object.
 * - default_row_class: A boolean indicating whether default row classes should be used.
 * - row_utility_classes: An array of optional utility classes to be used.
 *
 * @see template_preprocess_views_view_unformatted()
 */
#}
{% apply spaceless %}

{% block views_unformatted_title %}
  {% if title %}
    {%
      include 'radix:heading' with {
        heading_html_tag: 'h3',
        content: title
      }
    %}
  {% endif %}
{% endblock %}

{% block views_unformatted_rows %}
  {% for row in rows %}
    {%
      set row_classes = [
        default_row_class ? 'views-row',
      ]|merge(row_utility_classes ?: [])
    %}

    <div{{row.attributes.addClass(row_classes)}}>
      {{ rows }}
    </div>
  {% endfor %}
{% endblock %}

{% endapply %}

Override node

In twig debug BEGIN OUTPUT from 'themes/custom/radix_blocks/templates/content/node.html.twig'

In node.html.twig

1
{% extends "@radix/content/node.twig" %}

The code from this file which is located in themes/radix/src/components/content/node.twig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
{#
/**
 * @file
 * Template for a Node.
 */
#}
{%
  set classes = [
  'node',
  node.isPromoted() ? 'node--promoted',
  node.isSticky() ? 'node--sticky',
  not node.isPublished() ? 'node--unpublished',
  node.bundle|clean_class,
  node.bundle|clean_class ~ '--' ~ view_mode|clean_class,
]
%}
<article{{ attributes.addClass(classes) }}>
  {{ title_prefix }}
  {{ title_suffix }}

  {% block content %}
    {% if not page %}
      <h2{{ title_attributes }}>
        <a href="{{ url }}" rel="bookmark">{{ label }}</a>
      </h2>
    {% endif %}

    {% if display_submitted %}
      <footer>
        {{ author_picture }}
        <div{{ author_attributes }}>
          {% trans %}Submitted by {{ author_name }} on {{ date }}{% endtrans %}
          {{ metadata }}
        </div>
      </footer>
    {% endif %}

    <div{{ content_attributes }}>
      {{ content }}
    </div>
  {% endblock %}
</article>

Overide a paragraphs module

overide paragraphs module

Overide node title

BEGIN OUTPUT from 'core/modules/node/templates/field--node--title.html.twig'

Code from field–node–title.html.twig file

1
2
3
4
5
6
7
8
9
{% if not is_inline %}
  {% include "field.html.twig" %}
{% else %}
<span{{ attributes }}>
  {%- for item in items -%}
    {{ item.content }}
  {%- endfor -%}
</span>
{% endif %}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
{#
/**
 * @file
 * Template for Navbar component.
 *
 * Available config:
 * - navbar_container_type: false | sm | md | lg | xl | xxl | fluid
 * - placement: default | fixed-top | fixed-bottom | sticky-top
 * - navbar_expand: sm | md | lg | xl | false
 * - navbar_theme: dark | light (default: light)
 * - navbar_utility_classes: An array of utility classes.
 *
 * Available blocks:
 * - branding
 * - left
 * - right
 */
#}
{% apply spaceless %}

{% set nav_attributes = nav_attributes ?: create_attribute() %}
{% set navbar_container_attributes = navbar_container_attributes ?: create_attribute() %}

{% set placement = placement ?? '' %}
{% set navbar_expand = navbar_expand ?? 'lg' %}
{% set navbar_theme = navbar_theme ?? null %}

{%
  set navbar_container_classes = [
    navbar_container_type is null ? 'container' : '',
    navbar_container_type ? 'container' ~ (navbar_container_type ? '-' ~ navbar_container_type : '') : '',
  ]|merge(navbar_container_utility_classes ?: [])
%}

{%
  set nav_classes = [
    'navbar',
    navbar_expand ? 'navbar-expand-' ~ navbar_expand : '',
    placement,
  ]|merge(navbar_utility_classes ?: [])
%}

{% if navbar_theme %}
  {% set nav_attributes = nav_attributes.setAttribute('data-bs-theme', navbar_theme) %}
{% endif %}

<nav {{ nav_attributes.addClass(nav_classes) }}>
  <div {{ navbar_container_attributes.addClass(navbar_container_classes) }}>
    {% block branding %}
      {{ branding }}
    {% endblock %}

    {% block navbar_toggler %}
      <button class="navbar-toggler collapsed" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbar-collapse" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>
    {% endblock %}

    <div class="collapse navbar-collapse">
      {% block left %}
        {{ left }}
      {% endblock %}

      {% block right %}
        {{ right }}
      {% endblock %}
    </div>
  </div>
</nav>

{% endapply %}

This post is licensed under CC BY 4.0 by the author.