Skip to content

Drupal Theme Anatomy — .info.yml, Twig Templates and Libraries

DodaTech Updated 2026-06-27 9 min read

In this tutorial, you'll learn the anatomy of a Drupal theme — from the .info.yml file and Twig templates to the libraries.yml for CSS and JavaScript and the regions configuration that defines where content appears.

What You'll Learn

  • The complete folder structure of a Drupal theme
  • How the .info.yml file defines a theme's identity and capabilities
  • Configuring regions where blocks and content appear
  • Defining asset libraries in libraries.yml for CSS and JavaScript
  • Understanding template files and their naming conventions

Why It Matters

Every Drupal theme, whether custom or contributed, follows the same structural conventions. Understanding theme anatomy lets you create custom themes, modify existing ones, and troubleshoot layout issues. Without this knowledge, you are limited to what contributed themes provide. With it, you can build any design you imagine. The theme layer is where Drupal's backend content meets the frontend presentation, and knowing how it works gives you complete control over your site's appearance.

Real-World Use

A design agency builds custom Drupal themes for university clients. Each theme follows the same structural foundation: a .info.yml file declares the theme, a libraries.yml file loads the CSS framework (Bootstrap or Tailwind), Twig templates render the HTML, and regions define where navigation, content, and sidebars appear. This consistent anatomy lets the agency quickly spin up new themes while maintaining code quality and performance standards across projects.

Learning Path

flowchart LR
  A[URL Aliases] --> B[Theme Anatomy]
  B --> C[Installing Themes]
  C --> D[Twig Templating]
  D --> E[Sub-themes]
  E --> F[Template Suggestions]
  F --> G[Asset Libraries]
  G --> H[Module Management]

Theme Folder Structure

A Drupal theme lives in the themes directory. Custom themes go in themes/custom, contributed themes in themes/contrib.

themes/custom/mytheme/
├── mytheme.info.yml
├── mytheme.libraries.yml
├── mytheme.breakpoints.yml
├── mytheme.schema.yml
├── screenshot.png
├── logo.svg
├── css/
│   ├── style.css
│   ├── components/
│   │   └── header.css
│   └── layout/
│       └── grid.css
├── js/
│   ├── main.js
│   └── navigation.js
├── templates/
│   ├── page.html.twig
│   ├── node.html.twig
│   ├── block.html.twig
│   ├── field.html.twig
│   ├── region.html.twig
│   └── system/
│       ├── breadcrumb.html.twig
│       └── pager.html.twig
├── images/
│   └── background.jpg
└── config/
    └── install/
        └── mytheme.settings.yml

The .info.yml File

The .info.yml file is the most important file in a theme. It declares the theme to Drupal and contains all metadata and configuration.

# mytheme.info.yml
name: 'My Custom Theme'
type: theme
core_version_requirement: ^10 || ^11
description: 'A custom Drupal theme built for the university website.'
package: Custom
version: 1.0.0

base theme: false

libraries:
  - mytheme/global
  - mytheme/fonts

regions:
  header: 'Header'
  primary_menu: 'Primary Menu'
  secondary_menu: 'Secondary Menu'
  page_top: 'Page Top'
  page_bottom: 'Page Bottom'
  highlighted: 'Highlighted'
  featured_top: 'Featured Top'
  breadcrumb: 'Breadcrumb'
  content: 'Content'
  sidebar_first: 'Left Sidebar'
  sidebar_second: 'Right Sidebar'
  footer: 'Footer'

regions_hidden:
  - page_top
  - page_bottom

Key Properties

  • name: Human-readable name shown on the Appearance page
  • type: Must be theme for themes
  • core_version_requirement: Specifies which Drupal versions the theme supports
  • base theme: Set to false for a standalone theme, or the machine name of a base theme for sub-themes
  • libraries: Asset libraries loaded on every page
  • regions: Named areas where blocks can be placed
  • regions_hidden: Regions that should not appear in the block layout UI

The .libraries.yml File

The libraries file defines CSS and JavaScript assets that can be loaded selectively.

# mytheme.libraries.yml
global:
  version: 1.0
  css:
    base:
      css/base/elements.css: {}
    layout:
      css/layout/grid.css: {}
    component:
      css/components/header.css: {}
      css/components/navigation.css: {}
      css/components/footer.css: {}
    theme:
      css/style.css: {}
  js:
    js/main.js: {}
    js/navigation.js:
      attributes:
        defer: true

fonts:
  version: 1.0
  css:
    theme:
      //fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap:
        type: external
        minified: true

homepage:
  version: 1.0
  css:
    component:
      css/components/hero.css: {}
  js:
    js/hero-animation.js: {}

CSS Categories (SMACSS)

Drupal organizes CSS using SMACSS categories:

  • base: Reset styles, element defaults (CSS reset, normalize)
  • layout: Grid systems, page structure, container widths
  • component: Reusable UI components (buttons, cards, headers)
  • state: State-specific styles (active, disabled, hidden)
  • theme: Theme-specific visual customization (colors, fonts)

JavaScript Options

  • header: Set to true or omit to load in <head>
  • footer: Set to true to load before closing </body>
  • attributes: HTML attributes like defer or async
  • minified: Set to true for already-minified files

Declaring Libraries in .info.yml

Any library defined in .libraries.yml can be loaded globally by listing it in .info.yml:

libraries:
  - mytheme/global
  - mytheme/fonts

To load a library on specific pages only, use the Twig attach_library() function in the template:

{{ attach_library('mytheme/homepage') }}

Regions Configuration

Regions define where content appears on the page. The page.html.twig template renders regions in the order defined.

regions:
  header: 'Header'
  primary_menu: 'Primary Menu'
  highlighted: 'Highlighted'
  content: 'Content'
  sidebar_first: 'Left Sidebar'
  sidebar_second: 'Right Sidebar'
  footer: 'Footer'

Each region renders in page.html.twig using the page variable:

<header>
  {{ page.header }}
  {{ page.primary_menu }}
</header>

<main>
  {{ page.highlighted }}
  <div class="content-wrapper">
    <article>
      {{ page.content }}
    </article>
    <aside>
      {{ page.sidebar_first }}
      {{ page.sidebar_second }}
    </aside>
  </div>
</main>

<footer>
  {{ page.footer }}
</footer>

Template Files

Templates use Twig syntax and control how each piece of content renders.

Essential Templates

  • page.html.twig — The overall page layout wrapper
  • node.html.twig — Renders individual content items (articles, pages)
  • block.html.twig — Renders blocks placed in regions
  • field.html.twig — Renders individual fields on entities
  • region.html.twig — Renders a region containing blocks
  • html.html.twig — The HTML document wrapper

Template Suggestions

Drupal uses a suggestion system to allow template overrides for specific content:

node.html.twig                    → Default node template
node--article.html.twig           → Template for Article nodes
node--article--full.html.twig     → Template for Article nodes in full view mode
node--article--teaser.html.twig   → Template for Article nodes in teaser view
node--123.html.twig               → Template for a specific node by ID

Preprocess Functions

Preprocess functions in the .theme file let you add variables to templates.

<?php
// mytheme.theme

function mytheme_preprocess_node(&$variables) {
  $node = $variables['node'];
  // Add a custom variable based on node type.
  if ($node->bundle() === 'article') {
    $variables['is_article'] = true;
    $variables['reading_time'] = ceil(str_word_count(strip_tags($node->body->value)) / 200);
  }
}

function mytheme_preprocess_page(&$variables) {
  // Add active theme path variable.
  $variables['theme_path'] = \Drupal::service('extension.list.theme')->getPath('mytheme');
}

Breakpoints Configuration

Breakpoints define Responsive Design thresholds for responsive image styles.

# mytheme.breakpoints.yml
mytheme.small:
  label: Small
  media: '(min-width: 0px)'
  weight: 0
  multipliers:
    - 1x
mytheme.medium:
  label: Medium
  media: '(min-width: 768px)'
  weight: 1
  multipliers:
    - 1x
mytheme.large:
  label: Large
  media: '(min-width: 1024px)'
  weight: 2
  multipliers:
    - 1x
    - 2x

Theme Schema

Schema files define the structure of theme settings, enabling the settings form UI.

# mytheme.schema.yml
mytheme.settings:
  type: theme_settings
  label: 'My Custom Theme settings'
  mapping:
    font_family:
      type: string
      label: 'Font Family'
    container_width:
      type: string
      label: 'Container Width'
    show_sidebar:
      type: boolean
      label: 'Show Sidebar'
    accent_color:
      type: string
      label: 'Accent Color'
    social_links:
      type: sequence
      label: 'Social Links'
      sequence:
        type: mapping
        mapping:
          platform:
            type: string
            label: 'Platform'
          url:
            type: uri
            label: 'URL'

Theme Screenshot

A screenshot.png image at the root of the theme folder appears on the Appearance page. The recommended size is 293 pixels wide by 68 pixels tall.

# Generate a screenshot using ImageMagick:
convert -size 293x68 gradient:blue screenshot.png

Theme Hooks

The hook_theme function in your .theme file defines custom theme hooks that templates can implement.

<?php
function mytheme_theme($existing, $type, $theme, $path) {
  return [
    'mytheme_card' => [
      'variables' => [
        'title' => NULL,
        'body' => NULL,
        'image' => NULL,
        'link' => NULL,
      ],
      'template' => 'templates/card',
    ],
  ];
}

This creates a card.html.twig template that can be called from other templates:

{{ include('mytheme-card', {
  title: 'Hello World',
  body: 'This is a card component.',
  image: 'card-image.jpg',
  link: url('entity.node.canonical', {'node': 123})
}) }}

Common Mistakes

  1. Missing the .info.yml file: Without this file, Drupal does not recognize the theme at all. Double-check the filename matches your theme's machine name.

  2. Incorrect regions array format: Regions must use machine names as keys and human-readable labels as values. A missing region causes blocks assigned to that region to disappear.

  3. Not clearing cache after adding templates: Drupal caches template information. After adding new template files, clear the cache with drush cr to see changes.

  4. Using wrong CSS category in libraries.yml: Putting component styles in the base category can override other theme styles. Use the correct SMACSS category for each file.

  5. Forgetting to declare libraries in .info.yml: Defining a library in libraries.yml does not automatically load it on every page. It must be listed in the libraries key of .info.yml or attached via attach_library() in Twig.

Practice Questions

  1. What is the purpose of the .info.yml file in a Drupal theme, and what are five key properties it must define?

  2. How do SMACSS categories (base, layout, component, state, theme) help organize CSS in a Drupal theme?

  3. What happens if a block is placed in a region that is not defined in the theme's .info.yml file?

  4. Challenge: Design a theme structure for a magazine website. Define the folder layout, choose five regions, create a .info.yml file, a .libraries.yml with at least three libraries (global, magazine, homepage), and describe which template files you would override to customize article pages.

FAQ

What is the .info.yml file in a Drupal theme?

The .info.yml file declares a theme to Drupal. It contains the theme name, description, version, base theme reference, libraries, regions, and other configuration. Every theme must have this file to be recognized by Drupal.

What are Drupal theme regions?

Regions are named areas in a page layout where blocks can be placed. Common regions include header, content, sidebar, and footer. Themes define their own regions in the .info.yml file, and blocks are assigned to regions on the Block Layout page.

Where do I put custom CSS in a Drupal theme?

CSS files go in the theme's css/ directory and are declared in the .libraries.yml file. Use SMACSS categories: base, layout, component, state, and theme. Load them globally via .info.yml or selectively via attach_library() in Twig.

What template files does a Drupal theme need?

The minimum required template is optional — Drupal renders content even without template files. Common templates include page.html.twig, node.html.twig, block.html.twig, and field.html.twig. Templates are discovered automatically based on naming conventions.

How do I add JavaScript to a Drupal theme?

Add JS files to the js/ directory, declare them in .libraries.yml with appropriate dependencies, and load them globally in .info.yml or selectively via attach_library() in Twig templates.

Mini Project

Goal: Create a basic custom theme structure.

  1. Create a folder themes/custom/mytheme/
  2. Create mytheme.info.yml with name, type, description, and three regions: header, content, footer
  3. Create mytheme.libraries.yml with a global library containing style.css and main.js
  4. Add screenshot.png (293x68 pixels)
  5. Create templates/page.html.twig that renders the header, content, and footer regions
  6. Create templates/node.html.twig that renders the node title and body
  7. Enable the theme on the Appearance page
  8. Verify the theme works and blocks can be placed in your custom regions

What's Next

Now that you understand theme anatomy, proceed to installing and managing themes to learn about contributed themes and base themes. After that, explore Twig templating in depth.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro