Skip to main content
 —  James Oakley
Radix Drupal theme logo

This is the third post in a series. I wrote about my experiences moving this website from Drupal 7 to Drupal 10, and then zeroed in on theming the site using Radix as a base theme. Out of the box, Radix subthemes don't generate sidebar regions for block placement, something many websites want.

As a way to illustrate theme development using Radix, and as a recipe for a common site-building requirement, this post will walk through how to add sidebars to a Radix subtheme.

You might find it helpful to go to Home > Administration > Configuration > Development >  Development settings, and check the box "Twig development mode". This makes Drupal include a load of HTML comments in your page so that you can see which Twig components are being included where. It can be very helpful trying to find what files to adjust to get the changes you want.

Step 1: Install Radix

composer require drupal/radix
drush then radix

Step 2: Create a subtheme

For the purpose of illustration, I'm going to name my subtheme cherry. You'll obviously pick your own name, so throughout the recipe simply substitute your name for mine.

In this and the next few steps I'll follow the Radix theme getting started guide.

drush --include="web/themes/contrib/radix" radix:create cherry
drush then cherry -y
drush config-set system.theme default cherry -y
cd web/themes/custom/cherry

Before you go any further, refresh your memory from the previous post about the wisdom of creating a copy of what templates and (base theme) components look like at this moment in time, so you can merge in any base theme changes later.

Step 3: Initialise the required node packages

Note: You may not need to run nvm use if your server already has npm enabled and ready to go. If you get an error that nvm is not found, and you've installed NodeJS as you need to, try moving to the next line npm install. If that doesn't work, then check your NodeJS installation, beyond the scope of this post.

nvm use
npm install
cp .env.example .env.local
# Update DRUPAL_BASE_URL in newly created .env.local file to match your local environment URL.

That last step requires you to edit .env.local manually. You only need to do this if you wish to use Browsersync to work on your theme while the site is loaded in a browser.

Step 4: Generate the initial CSS

npm run watch

At this point, if you clear your caches, you should be able to view your site using the cherry theme. It won't look much, because the Radix theme assumes you'll design yourself, something I described in my previous post.

Assuming that works, you'll see your website, with black text on a white background, with few margins between elements, and little colouring besides obvious things like links in blue.

For minor tweaks of .scss files, you'd leave the watch process running so that the CSS is automatically recompiled as you work. You'd have to do your edits in a new console session. However we're making some bigger changes, so it will be easier to exit watch and come back to it.

Ctrl+C

Step 5: Declare your sidebar regions

We're not going to be able to do anything with sidebars unless Drupal is told that our theme has those regions to work with.

The .info.yml file for a theme declares all the regions that the theme makes available for block placement.

So go into cherry.info.yml in a text editor. You'll see the section headed regions:, where each line is indented by two spaces. Assuming your cherry theme is freshly created (as per this example), there will be navbar_branding, navbar_left, navbar_right, header, content, page_bottom, and footer. Below footer, create two new lines, so the regions: section ends thus:

  footer: Footer
  sidebar_first: 'Sidebar First'
  sidebar_second: 'Sidebar Second'

Step 6: Create a new component

We're going to create a new component for our sidebars. This is an elegant way to keep all the twig and css code for sidebars in one place, and when you come to setting the styling for your sidebars it will make your site a whole lot more maintainable. It also gives an opportunity for this post to illustrate how to do create and use a brand new component.

We'll use the CLI tool that Radix ships with. That CLI may not be in your path. By all means fiddle around with your $PATH, but (for now) I'll just reference it by its relative path. Assuming you're still in the directory for your cherry theme, run the following command:

./node_modules/.bin/drupal-radix-cli generate

You'll be asked:

◆  What is the name of your component?

Just type sidebars and press Enter.

That's it. Look in the components folder of your subtheme, and there's a folder there named sidebars.

For documentation purposes, you may as well edit the sidebars.component.yml file. The last three lines, by default, read as follows:

name: sidebars
status: experimental
description: 'The sidebars component auto-generated by drupal-radix-cli'

Change to

name: sidebars
status: experimental
description: 'Render sidebar_first and sidebar_second'
slots:
 sidebars:
   title: Sidebars
   description: 'The sidebars'

Step 7: Create the HTML for your sidebars

We'll create the HTML to include your sidebars in your site in a moment. First, let's create the HTML for them.

Simply edit the file sidebars.twig to read as follows:

{#
/**
* @file
* Template for sidebars component.
*/
#}
{% if page.sidebar_first %}
<div class="col-sm-6 col-md-2 order-md-1" id="sidebar_first">{{ page.sidebar_first }}</div>
{% endif %}
{% if page.sidebar_second %}
<div class="col-sm-6 col-md-2 order-md-5" id="sidebar_second">{{ page.sidebar_second }}</div>
{% endif %}

What does this do?

The {% if %} sections tell the Twig engine to render some HTML only if an expression evaluates to true. So the first three lines only kick in if page.sidebar_first is true (which, in this context, means non-empty). In other words, do what's inside the {% if %} ... {% endif %} section only if the sidebar_first region actually has some content.

If that's the case, we want to render the content of the region. That's this bit:

{{ page.sidebar_first }}

But we want them to be laid out as sidebars on the site, so we surround them in <div> tags. Why we want those particular classes will become clear later.

We do this for the sidebar_first region, then for the sidebar_second region. Whenever you ask the theme to display the contents of the sidebars component, you'll get the first sidebar, then the second sidebar, such as aren't empty.

Note that the <div> tags are not indented inside the {% if %} code blocks. That is deliberate, but counter-intuitive if you're used to indenting nested code. Intending there inserts whitespace into the rendered output at a crucial place. The whitespace can get displayed on the final page. It only becomes a single space, but it's enough to make the Bootstrap columns a whisker wider than they actually are. This then means all the columns don't fit into the single row, and your second sidebar ends up underneath.

You might expect we'd want to render sidebar_first first, then the central content part of the page, then the sidebar_second. That is indeed how we want things to appear on screen. However, Bootstrap lets you shift columns around so that the order on screen is not the same as the order in the HTML sourcecode for the page. Our HTML will contain the content first, then both the sidebars, but will display those three sections on screen in the way you'd expect.

We do it this way for two reasons. First, for SEO reasons, you want the content that is specific to each page to come high up the HTML source. You don't want a search engine to give the contents of your first sidebar as how it thinks the webpage begins. Second, on smaller screens, we want both sidebars to come after the text, because it's not just search engines that are most interested in the main content (people are too).

Again, I'll explain in a moment how we achieve this.

Step 8: Alter "Page Content" to include our sidebars

Here's where using your "Twig development mode" helps you. You discover that the Radix component that inserts the <main> HTML tag is the component page-content. So this is the one we'll need to alter if we want to have sidebars included inside that tag.

So our subtheme now needs to have its own copy of the Radix page-content component. This will enable our webpages to use the cherry page-content, not the radix page-content, and so have sidebars.

To bring across the Radix page-content ready for customising, we again use the Radix CLI tool. Last time, we used its generate function. This time, we use the add function.

./node_modules/.bin/drupal-radix-cli add

With generate, we typed the name of our new component. add is for bringing across a component that Radix already defines, so we pick from a list rather than typing the name. Sadly, you can't start typing to find the component you want; you have to use the arrow keys. So when you see:

◆  Pick a Radix component to add to your theme.

press the down arrow until "Page Content" is selected, then press Enter.

Now, your components folder has a page-content folder as well as a sidebars one. We're going to edit the twig file for page-content, so it includes our sidebars.

We're going to make use of the Bootstrap framework, specifically its responsive grids, to do that. Again, a tutorial on that is outside the scope of this post. At the moment, there's a load of HTML inside that <main> tag. We're going to declare that <main> is a Bootstrap grid container, and wrap all of that content, so that inside <main> there is a Bootstrap row. Within that row there will be (i) a column for the content (i.e., for everything currently inside the <main> tag), and (ii) columns for our sidebars. We already included some col- classes on the <div> wrapping each of the sidebars, so that work is done. We just need to wrap the current <main> content in a row and then a column, and render our sidebars.

Let's do that. Open up page-content.twig in a text editor.

Under the <main> line, add the following two lines of code

<div class="row">
<div class="page__header_content col-md-8 order-md-3">

Once again, notice that these <div> tags must be against the left-margin, not indented with spaces or tabs.

Above the closing </main> tag line, add the following three lines of code:

</div>
{% include 'cherry:sidebars' %}
</div>

So, before the previous <main> content, we open a row and open a column. After the previous <main> content we close the column, include our sidebars (which have columns of their own), and then close the row.

Lastly, we need to add the class "container" to the <main> tag. So change the <main> line to use  the Twig addClass function. Change it from:

<main{{content_attributes.addClass(page_main_classes)}}>

to:

<main{{content_attributes.addClass(page_main_classes).addClass("container")}}>

The <main> section of the twig file now looks like this:

<main{{content_attributes.addClass(page_main_classes).addClass("container")}}>
 <div class="row">
<div class="page__header_content col-md-8 order-md-3">
       {% if page.header %}
               <header class="page__header">
                       <div {{ page_header_container_attributes.addClass(page_header_container_classes) }}>
                               {% block page_header %}
                                       {{ page.header }}
                               {% endblock %}
                       </div>
               </header>
       {% endif %}
       {% if page.content %}
   <div class="page__content" id="main-content">
     <div {{ page_content_container_attributes.addClass(page_content_container_classes) }}>
       {% block page_inner_content %}
         {{ page.content }}
       {% endblock %}
     </div>
   </div>
 {% endif %}
   </div>
   {% include 'cherry:sidebars' %}
 </div>
</main>

Step 9: A note on responsiveness

We can now see how the magic we discussed earlier works. You'll recall that the HTML source code runs in this order: Content, Sidebar First, Sidebar Second. The page will appear on screen as Sidebar First, Content, Sidebar Second. Unless, that is, you're on a small screen, in which case the sidebars appear after the content.

We used the responsive Bootstrap classes order-md-*. This allows you to specify the order in which grid columns will appear on screen. Sidebar First prints first with an an order of 1 (order-md-1), then Content (order-md-3), then Sidebar Second (order-md-5). So they'll display in the order we want.

The -md bit of those classes tells Bootstrap to use the responsive version of ordering. (Read about responsive breakpoints). Unless you've changed the dimensions of your breakpoints, -md stands for medium and means screens at 768px or wider. So we're saying to display the content in the order it appears in the HTML (Content, Sidebar First, Sidebar Second) unless we're at least 768px wide. If we're wide enough, display in the order indicated by those classes (Sidebar First, Content, Sidebar Second).

Then we decide how wide we want each of our (up to) 3 columns to be. Bootstrap grids have 12 columns, so (simplifying slightly) if you want a column to take up half the row you tell the column to be 6 units wide. The content column has the class col-md-8, which says it will be 8 columns wide. That's 2/3 of the page, but it's only that if we're 768px or wider (-md again). If we're narrower than that, the default behaviour is to use the full page width for the content. The sidebars will not only not be re-ordered on small screens; they'll appear below the main content.

Last thing to notice: The sidebars also have another column width class on them.

class="col-sm-6 col-md-2 order-md-1"

So, if we're medium or wider, each sidebar is two units wide. 2 sidebars (2 units each), plus the content (8 units) gives your full page-width of 12.

But if you're not medium or wider, then the sidebars would be full-width, and stack below each other. That may be what you want on a really small screen. But if we're size "small" or above (576px), there's room to put the two sidebars next to each other, below the main content. That's what col-sm-6 does. But if we're narrower than "small", we stack the page - Content, then Sidebar First, then Sidebar Second.

Step 10: Call our custom page-content

Here's another reminder to make sure you've read my previous post. Once you've created a custom component, you need to call it. Here's an opportunity to illustrate what I was saying.

Having made all those changes, if you run npm run watch, clear caches, and refresh the page, you won't see any sidebars. Why not?

Because the page-content component is called by the page component. Our cherry theme is still using the Radix page component, and that calls the Radix page-content component. So our cherry page-content component is never being used.

To fix that, we next need to customise the page component, so that our site uses a page that calls our page-content, not the Radix one.

This is very similar to what we just did for page-content, only the changes are really simple this time. Run:

./node_modules/.bin/drupal-radix-cli add

This time, we choose page as the component to add. Our components directory now contains page, page-content and sidebars. Within the page folder, edit page.twig. Find the line that looks like this:

{% include 'radix:page-content' with {

Change radix to cherry:

{% include 'cherry:page-content' with {

And that's it.

Guess what. It still doesn't work. And you guessed it. Our site is still calling the Radix page component, not the cherry one, so the cherry page component never gets called. We are nearly there, I promise.

This time, the solution is different. We've actually hit the top of the tree; there is no component that calls the page component. It's template time. Remember (previous post, anyone?) that a Radix subtheme only contains the new and overidden components, but contains its own copy of every template. So look inside the ./templates/page folder, and edit the file page.html.twig. We're now working with the Drupal theme engine, as this is where it will look for a template for a whole webpage, and the filename it will expect.

Once you've opened page.html.twig in a text editor, you can probably see what line to change. But just in case it's not obvious, you change

{% include 'radix:page' %}

to

{% include 'cherry:page' %}

Step 11: Test

That's nearly it. Nearly enough, that it's time to test

npm run watch
drush cr

Reload your site. You should have Sidebar First and Sidebar Second regions, where you can place blocks. Give it a try.

Step 12: Enhancement for only one sidebar

Now we know it works, let's make it better.

If you only have one contentful sidebar, it will have width 2 and the content will have width 8. This totals 10, so the two together only span 5/6 of the width of the screen. If you only have a left sidebar, that's not too bad: you just have an unnecessarily large right margin and waste a bit of the screen. But if you only have a right sidebar, it will look odd. The right sidebar needs to be at the right of the page, but instead it will look arbitrarily indented.

We can fix this. We need to detect how many sidebars any given page will actually use. We then set the width of the content column to 8 (if both sidebars have content), 10 (if only one does), and 12 (if none of them do).

We're going to use some Twig code. We'll create a variable that holds the required column width of the Content column. We'll then use that variable, instead of a static number 8. Go to ./components/page-content, and open page-content.twig in a text editor.

Above the <main> tag, add the following three lines of Twig code.

{% set mdwidth = 12 %}
{% set mdwidth = mdwidth - (page.sidebar_first ? 2 : 0) %} 
{% set mdwidth = mdwidth - (page.sidebar_second ? 2 : 0) %}

It's fairly obvious what this does. We create a variable called mdwidth, and initialise it to 12. If the first sidebar has content, we deduct 2. If the second sidebar has content, we deduct 2.

Then, change the line that wraps the page content in a Bootstrap column. If you followed the instructions above, it currently looks like this:

<div class="page__header_content col-md-8 order-md-3">

Change it to this:

<div class="page__header_content col-md-{{ mdwidth }} order-md-3">

Save. npm run watch. drush cr. Reload page.

Now try only having a second sidebar. The content should be 10 units wide so that the second sidebar is flush with the right of the page. Try having no active sidebars for a page. The content should take the full width.

Step 13: Homepage

We're nearly done.

The ./templates/page has two Twig templates: page.html.twig and page--front.html.twig. The theming engine assumes you may want your site front page to be themed differently from the rest of the site, so it uses a different template. If you want your sidebars on the front page too, you need to edit page--front.html.twig as we did at the end of Step 10.

Step 14: CSS

Finally, get your sidebars looking how you want them. Maybe you want to add margins, shading, borders, spacing where there are multiple blocks in one sidebar, and so on.

Edit ./components/sidebars/sidebars.scss, and put whatever you want in there.

Voilà

And there you are: Your Radix subtheme has sidebars. This post has been lengthy because it goes through all the steps, but actually it's pretty simple:

  • Install Radix.
  • Create a subtheme and intialise.
  • Alter the .info.yml for the theme to declare two sidebar regions.
  • Create a subtheme component, and set its twig to render your sidebars if they have content.
  • Override the page-content component to display the sidebars in a Bootstrap grid row.
  • Override the page component to call your custom page-content component, and the page template to call your custom page component.
  • Add some magic so the page uses its full width even if there's only one sidebar in use.
  • CSS styling
Blog Category:
Add new comment
The content of this field is kept private and will not be shown publicly.