Розробка кастомної теми Drupal

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.
Розробка та обслуговування будь-яких видів сайтів:
Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка кастомної теми Drupal
Середня
~1-2 тижні
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розробка користувальницької теми Drupal

Теми Drupal — це набір Twig-шаблонів, CSS, JS та конфігураційних YAML-файлів. Розробка з нуля або на основі базової теми. Contrib-теми типу Bootstrap або Gin використовуються як стартові точки або для адміністративного інтерфейсу.

Структура теми

web/themes/custom/my_theme/
├── my_theme.info.yml          # описання теми
├── my_theme.libraries.yml     # CSS/JS бібліотеки
├── my_theme.theme             # PHP хуки теми
├── config/
│   └── install/               # конфіг, що встановлюється з темою
├── css/
│   ├── base.css
│   ├── layout.css
│   └── components/
├── js/
│   └── main.js
├── images/
├── fonts/
├── templates/
│   ├── layout/
│   │   ├── html.html.twig
│   │   └── page.html.twig
│   ├── content/
│   │   ├── node.html.twig
│   │   ├── node--article.html.twig
│   │   └── node--article--teaser.html.twig
│   ├── block/
│   │   └── block.html.twig
│   └── field/
│       └── field--body.html.twig
└── screenshot.png

my_theme.info.yml

name: Моя тема
type: theme
description: 'Користувальницька тема проекту'
core_version_requirement: ^10
base theme: stable9   # або false якщо з нуля
libraries:
  - my_theme/global
regions:
  header: Заголовок
  primary_menu: 'Основне меню'
  breadcrumb: Хлібні крошки
  highlighted: Виділено
  content: Контент
  sidebar_first: 'Бічна панель'
  footer: Футер

CSS/JS Бібліотеки

# my_theme.libraries.yml
global:
  version: VERSION
  css:
    base:
      css/base.css: {}
    layout:
      css/layout.css: {}
      css/components/header.css: {}
  js:
    js/main.js: { defer: true }
  dependencies:
    - core/drupal
    - core/jquery

# Окрема бібліотека для слайдера — завантажується тільки при потребі
slider:
  version: VERSION
  css:
    component:
      css/components/slider.css: {}
  js:
    js/slider.js: {}
  dependencies:
    - core/once

Підключуйте бібліотеки в шаблоні:

{# У шаблоні node--landing.html.twig #}
{{ attach_library('my_theme/slider') }}

Twig Шаблони

Drupal використовує ієрархію шаблонів — чим специфічніше ім'я, тим вищий пріоритет:

node.html.twig                      # всі ноди
node--article.html.twig             # тип article
node--article--full.html.twig       # тип article, вид full
node--123.html.twig                 # конкретна нода за id

Приклад переопалення шаблона статті:

{# templates/content/node--article--full.html.twig #}
<article{{ attributes.addClass('article', 'article--full') }}>
  {% if label %}
    <h1{{ title_attributes.addClass('article__title') }}>
      {{ label }}
    </h1>
  {% endif %}

  <div class="article__meta">
    {% if display_submitted %}
      <span class="article__author">{{ author_name }}</span>
      <time class="article__date" datetime="{{ date.attributes.datetime }}">
        {{ date }}
      </time>
    {% endif %}

    {% if content.field_tags %}
      <div class="article__tags">
        {{ content.field_tags }}
      </div>
    {% endif %}
  </div>

  {% if content.field_image %}
    <div class="article__cover">
      {{ content.field_image }}
    </div>
  {% endif %}

  <div class="article__body prose">
    {{ content.body }}
  </div>

  {# Рендеримо решту полів крім тих, що вже вивели #}
  {{ content|without('body', 'field_tags', 'field_image', 'links') }}
</article>

PHP Хуки в .theme Файлі

// my_theme.theme

/**
 * Додаємо змінні в шаблон сторінки.
 */
function my_theme_preprocess_page(array &$variables): void {
  $variables['site_name'] = \Drupal::config('system.site')->get('name');
  $variables['is_front'] = \Drupal::service('path.matcher')->isFrontPage();

  // Хлібні крошки з користувальницькою логікою
  $route = \Drupal::routeMatch();
  if ($node = $route->getParameter('node')) {
    $variables['node_type'] = $node->bundle();
  }
}

/**
 * Змінні для шаблона ноди.
 */
function my_theme_preprocess_node(array &$variables): void {
  $node = $variables['node'];

  if ($node->bundle() === 'article') {
    $variables['reading_time'] = my_theme_calculate_reading_time($node->get('body')->value);
  }
}

function my_theme_calculate_reading_time(string $html): int {
  $text = strip_tags($html);
  $words = str_word_count($text);
  return (int) ceil($words / 200); // 200 слів в хвилину
}

/**
 * Переопаки пропозицій шаблонів — для дебагу.
 */
function my_theme_theme_suggestions_node_alter(array &$suggestions, array $variables): void {
  $node = $variables['elements']['#node'];
  $view_mode = $variables['elements']['#view_mode'];

  // Додаємо пропозицію за типом матеріалу
  if ($node->hasField('field_material_type') && !$node->get('field_material_type')->isEmpty()) {
    $type = $node->get('field_material_type')->value;
    $suggestions[] = 'node__' . $node->bundle() . '__' . $type;
  }
}

/**
 * Alter для форми — додаємо класи.
 */
function my_theme_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {
  if ($form_id === 'contact_message_feedback_form') {
    $form['#attributes']['class'][] = 'contact-form';
    $form['actions']['submit']['#attributes']['class'][] = 'btn btn--primary';
  }
}

Адаптивні Зображення та Стилі Зображень

# config/install/image.style.article_cover.yml
langcode: en
status: true
id: article_cover
label: 'Обкладинка статті'
effects:
  uuid1:
    id: image_scale_and_crop
    data:
      anchor: center-center
      width: 1200
      height: 630

Використовуйте responsive_image замість звичайного image в шаблоні:

{% if content.field_image %}
  {{- content.field_image -}}
{% endif %}

Група адаптивних зображень налаштовується в /admin/config/media/responsive-image-style через UI або YAML.

Побудова Фронтенду (опціонально)

Якщо тема використовує npm-побудову:

// package.json
{
  "scripts": {
    "build": "postcss css/src -o css --map",
    "watch": "postcss css/src -o css --watch"
  }
}

Або Vite/Webpack якщо потрібні ES-модулі, TypeScript:

// vite.config.js
export default {
  build: {
    outDir: 'dist',
    rollupOptions: {
      input: { main: 'js/src/main.ts' },
      output: { entryFileNames: 'js/[name].js' }
    }
  },
  css: { postcss: './postcss.config.js' }
}

Дебаг Шаблонів

В settings.local.php включаємо Twig дебаг:

$config['system.performance']['css']['preprocess'] = FALSE;
$config['system.performance']['js']['preprocess'] = FALSE;
$settings['cache']['bins']['render'] = 'cache.backend.null';

// Показує імена шаблонів в HTML коментарях
$config['twig.settings']['debug'] = TRUE;

Після включення в source code сторінки будуть видні всі використані шаблони та доступні пропозиції. Це основний інструмент при розробці.

Терміни

Базова тема з шаблонами для основних типів контенту, адаптивна, з бібліотеками: 5–8 днів. З анімаціями, складним JS, адаптивними зображеннями, користувальницькими блоками: 10–15 днів.