0

I have localized Bulma theme following the instructions which were as simple as creating a new directory called 'libraries' under main directory (httpdocs) and putting the sources files in it. No problem with that.

  • I have been trying to create a sub theme which I named "bulma_sub".

bulma_sub is located under themes' folder (where the bulma base theme is).

  • there are three files in bulma_sub folder with the following set-up.

bulma_sub.info.yml

name: bulma_sub
description: 'subtheme based on bulma'
type: theme
base theme: bulma
core_version_requirement: ^8.8 || ^9
package: Core

regions:
  header: 'Header'
  navbar_branding: 'Branding'
  navbar_social: 'Social'
  header_search: 'Search'
  header_tabs: 'Tabs'
  primary_menu: 'Primary menu'
  secondary_menu: 'Secondary menu'
  highlighted: 'Highlighted'
  help: 'Help'
  content: 'Content'
  sidebar_first: 'Sidebar First'
  sidebar_second: 'Sidebar Second'
  tile_one: 'Tile 1'
  tile_two: 'Tile 2'
  tile_three: 'Tile 3'
  tile_four: 'Tile 4'
  tile_five: 'Tile 5'
  bottom: 'Bottom'
  footer: 'Footer'

libraries:
  - bulma_sub/global

# Information added by Drupal.org packaging script on 2020-10-27
version: '8.x-1.0-rc1'
project: 'bulma'
datestamp: 1603840308

bulma_sub.libraries.yml

    global:
      css:
        theme:
          css/overrides.css: {}
          css/style.css: {}
      dependencies:
        - core/jquery
        - core/jquery.once
        - core/drupal

bulma_sub.theme file (same as the source file)

<?php

/**
 * @file
 * The primary PHP file for the Drupal Bulma base theme.
 */

use Drupal\block\Entity\Block;
use Drupal\bulma\Bulma;
use Drupal\bulma\Bulmaswatch;
use Drupal\file\Entity\File;

/**
 * Implements template_preprocess_page().
 */
function bulma_preprocess_page(&$variables) {

  // Site slogan as separate variable.
  $site_config = \Drupal::config('system.site');
  $variables['site_slogan'] = $site_config->get('slogan');
}

/**
 * Implements template_preprocess_file_link().
 */
function bulma_preprocess_file_link(&$variables) {
  /** @var \Drupal\Core\Entity\Entity\File $file */
  $file = ($variables['file'] instanceof File) ? $variables['file'] : File::load($variables['file']->fid);

  $variables['icon'] = Bulma::getFileIcon($file);
}

/**
 * Implements hook_page_attachments_alter().
 */
function bulma_page_attachments_alter(&$page) {

  // Force IE to use Chrome Frame if installed.
  $page['#attached']['html_head'][] = [
    [
      '#type' => 'html_tag',
      '#tag' => 'meta',
      '#attributes' => [
        'http-equiv' => 'X-UA-Compatible',
        'content' => 'IE=edge,chrome=1',
      ],
    ],
    'chrome_frame',
  ];

  // Remove image toolbar in IE.
  $page['#attached']['html_head'][] = [
    [
      '#type' => 'html_tag',
      '#tag' => 'meta',
      '#attributes' => [
        'http-equiv' => 'ImageToolbar',
        'content' => 'false',
      ],
    ],
    'ie_image_toolbar',
  ];

  // Alter viewport.
  $page['#attached']['html_head'][] = [
    [
      '#type' => 'html_tag',
      '#tag' => 'meta',
      '#attributes' => [
        'name' => 'viewport',
        'content' => 'width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no',
      ],
    ],
    'viewport',
  ];
}

/**
 * Implements bulma_preprocess_HOOK() for breadcrumb.
 */
function bulma_preprocess_breadcrumb(&$variables) {
  // Load Bulma class from settings.
  $classes = array_merge(['breadcrumb'], Bulma::multiSettings('breadcrumb'));
  foreach ($classes as $class) {
    $variables['attributes']['class'][] = $class;
  }
  $variables['attributes']['role'] = 'navigation';
  $variables['attributes']['aria-labelledby'] = 'system-breadcrumb';
}

/**
 * Implements theme_menu_local_tasks().
 */
function bulma_preprocess_menu_local_tasks(&$variables) {

  // Load Bulma class from settings.
  $bulma_tabs_class = Bulma::multiSettings('tabs');

  if (!empty($bulma_tabs_class)) {
    // Set variables.
    foreach ($bulma_tabs_class as $key => $class) {
      $variables[str_replace('is-', '', $key)] = $class;
    }

  }

}

/**
 * Implements hook_form_alter().
 */
function bulma_form_alter(&$form, $form_state, $form_id) {

  // Check for actions buttons, group them.
  if (isset($form['actions'])) {
    $form['actions']['#attributes']['class'][] = 'field';
    $form['actions']['#attributes']['class'][] = 'is-grouped';

    // Loop trough array to get link type.
    // Better approach maybe to use https://www.drupal.org/project/button_link.
    foreach ($form['actions'] as $key => $action) {
      if (isset($action['#type'])) {
        if ($action['#type'] == 'link') {
          $name = (string) $action['#title'];

          // Check if button should be colorized.
          $button_color = Bulma::colorizeButton($name);

          // Add button class.
          if (!empty($button_color)) {
            foreach ($button_color as $class) {
              $form['actions'][$key]['#attributes']['class'][] = $class;
            }
          }
        }

      }
    }
  }

  switch ($form_id) {
    case 'node_preview_form_select':
      $form['backlink']['#options']['attributes']['class'][] = 'button';
      break;
  }

}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function bulma_form_search_form_alter(&$form, $form_state, $form_id) {

  // Remove label.
  $form['basic']['keys']['#title_display'] = 'invisible';

  // Update the placeholder.
  $form['basic']['keys']['#placeholder'] = t('Enter your keywords');

  // Wrap class to combine form.
  $form['basic']['#attributes']['class'] = 'field has-addons';

}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function bulma_form_search_block_form_alter(&$form, $form_state, $form_id) {

  // Update the placeholder, remove title - prevent double icon.
  $form['keys']['#title'] = '';
  $form['keys']['#placeholder'] = t('Search');

}

/**
 * Implements hook_theme_suggestions_HOOK_alter().
 */
function bulma_theme_suggestions_form_alter(array &$suggestions, array $variables) {
  if ($variables['element']['#form_id'] === 'search_block_form') {
    $suggestions[] = 'form__search_block_form';
  }
}

/**
 * Implements template_preprocess_block().
 */
function bulma_preprocess_block(&$variables) {

  // Block as panel style.
  if (Bulma::singleSetting('general.block')) {
    if ($variables['configuration']['label_display']) {
      $variables['block_panel_style'] = TRUE;
    }
  }

  // Customize the ID and classes of the main menu block.
  $main_menu = Bulma::singleSetting('general.menu');
  $plugin_id = 'system_menu_block:' . $main_menu;
  // Match blocks by plugin ID to support Bulma and any active subtheme.
  if (!empty($variables['elements']['#plugin_id']) && $variables['elements']['#plugin_id'] == $plugin_id) {
    $variables['attributes']['id'] = 'navbar-menu';
    $variables['attributes']['class'][] = 'navbar-end';
  }

  // Set a region variable for use in templates.
  if (isset($variables['elements']['#id'])) {
    $block = Block::load($variables['elements']['#id']);
    $variables['region'] = $block->getRegion();
  }
}

/**
 * Implements template_preprocess_table().
 */
function bulma_preprocess_table(&$variables) {
  // Load existing classes.
  $existing_class = [];
  if (isset($variables['attributes']['class'])) {
    $existing_class = $variables['attributes']['class'];
  }

  // Load Bulma specific table classes from theme settings.
  $bulma_table_class = Bulma::multiSettings('table');

  // Merge classes.
  if (!empty($bulma_table_class)) {
    $variables['attributes']['class'] = array_merge($existing_class, $bulma_table_class);
  }

}

/**
 * Implements hook_theme_suggestions_HOOK_alter().
 */
function bulma_theme_suggestions_links_alter(array &$suggestions, array $variables) {
  $link_type = $variables['theme_hook_original'];

  // We want only affect node and comment links.
  $target_output = ['links__node', 'links__comment'];

  if (in_array($link_type, $target_output)) {
    $suggestions[] = 'links__bulma';
  }

}

/**
 * Implements hook_theme_suggestions_HOOK_alter().
 */
function bulma_theme_suggestions_input_alter(array &$suggestions, array $variables) {

  // Define defaults for exclusion for rendering as bulma input.
  $exclude_input = [
    'hidden',
    'token',
    'radio',
    'checkbox',
    'submit',
    'image_button',
  ];

  // Submit button needs have render as button element.
  if ($variables['element']['#type'] == 'submit') {
    $suggestions[] = 'input__button__bulma';
  }

  // Add theme suggestions for others input elements.
  if (!in_array($variables['element']['#type'], $exclude_input)) {

    // Default wrapped input.
    $suggestions[] = 'input__bulma';
  }

}

/**
 * Implements template_preprocess_hook().
 */
function bulma_preprocess_input__bulma(&$variables) {
  // Make sure we have data to work from.
  if (empty($variables['element']['#type']) || empty($variables['element']['#title'])) {
    return;
  }
  // Field name and type.
  $type = $variables['element']['#type'];
  $name = (string) $variables['element']['#title'];

  // Check if we use icons.
  if (Bulma::singleSetting('general.icon')) {
    $variables['icon'] = Bulma::iconMatch($name, $type);

  }

  // Size of input buttons.
  $input_size = Bulma::singleSetting('bulma_elements_input_size');

  if ($input_size != 'none') {
    $variables['attributes']['class'][] = $input_size;
  }

}

/**
 * Implements template_preprocess_hook().
 */
function bulma_preprocess_input__button__bulma(&$variables) {

  // Field name and type.
  $name = $variables['element']['#value'];
  $type = $variables['element']['#type'];

  // We need render label inside button tag.
  $variables['label'] = $variables['element']['#value'];

  // Bulma submit classes.
  $variables['bulma_button_classes'] = [];

  // Check if button should be colorized.
  $button_color = Bulma::colorizeButton($name);

  if (!empty($button_color)) {
    $variables['bulma_button_classes'] = $button_color;
  }

  // Check if we use icons.
  if (Bulma::singleSetting('general.icon')) {
    // Define default if not matched.
    $variables['icon'] = Bulma::iconMatch($name, $type);

    // Hide label for search button.
    if ($variables['icon'] == 'search') {
      $variables['label'] = FALSE;
    }
  }

}

/**
 * Implements hook_theme_suggestions_HOOK_alter().
 */
function bulma_theme_suggestions_menu_alter(array &$suggestions, array $variables) {

  // Navbar menu.
  $bulma_nav = Bulma::singleSetting('general.menu');

  // Check if any menu is selected to be nav menu.
  if ($bulma_nav != 'none') {
    if ($variables['menu_name'] === $bulma_nav) {
      $suggestions[] = 'menu__nav_bulma';
    }
  }

}

/**
 * Implements template_preprocess_form_element().
 */
function bulma_preprocess_form_element(&$variables) {

  // Render the form element inside the label for checkbox and radio.
  if (in_array($variables['element']['#type'], ['checkbox', 'radio'])) {
    $variables['label']['#children'] = $variables['children'];
    unset($variables['children']);
  }

}

/**
 * Implements template_preprocess_form_element_label().
 */
function bulma_preprocess_form_element_label(&$variables) {

  // Check additional theme labels settings.
  $bulma_labels = Bulma::multiSettings('label');

  if (!empty($bulma_labels)) {
    foreach ($bulma_labels as $key => $value) {
      $variables[$key] = $value;
    }
  }

}

/**
 * Implements template_preprocess_page_title().
 */
function bulma_preprocess_page_title(&$variables) {
  $variables['title_attributes']['class'] = 'title';
}

/**
 * Implements template_preprocess_views_view_table().
 */
function bulma_preprocess_views_view_table(&$variables) {

  // Define defaults for variables.
  $variables['bordered'] = FALSE;
  $variables['striped'] = FALSE;
  $variables['narrow'] = FALSE;
  $variables['table'] = TRUE;

  // Load Bulma specific table classes from theme settings.
  $bulma_table_class = Bulma::multiSettings('table');

  if (!empty($bulma_table_class)) {
    foreach ($bulma_table_class as $class) {
      $variables[str_replace('is-', '', $class)] = TRUE;
    }
  }
}

/**
 * Implements hook_library_info_alter().
 */
function bulma_library_info_alter(&$libraries, $extension) {
  // Swap in Bulmaswatch theme's CSS.
  if ($extension === 'bulma' &&
    isset($libraries['global']) &&
    ($theme = theme_get_setting('cdn.bulmaswatch.theme'))
  ) {
    // If bulmaswatch is configured to use the default theme, use Bulma directly.
    $cdn_data = Bulma::getCdnData();
    $properties = [];
    if ($theme === 'default') {
      $bulma_file = '/css/bulma.css';
      if (Bulma::isLocal()) {
        $css_file = '/libraries/bulma' . $bulma_file;
      }
      else {
        $css_file = $cdn_data['api']['bulma']['file_root'] . $bulma_file;
        $properties['type'] = 'external';
      }
    }
    else {
      $properties['minified'] = TRUE;
      $themes = Bulmaswatch::getThemes();
      if (isset($themes[$theme]['local_css'])) {
        $css_file = $themes[$theme]['local_css'];
      }
      else {
        $css_file = $cdn_data['api']['bulmaswatch']['file_root'] . "/{$theme}/bulmaswatch.min.css";
        $properties['type'] = 'external';
      }
    }
    $libraries['global']['css']['base'] = [
      $css_file => $properties,
    ];
  }

}

File structure

libraries
|_bulma
  |_css
    |_bulma.css
  |_sass

themes
|_bulma
  |_assets
    |_bulma.style.css
    |_bulma.style.scss
    |_overrides.css
    |_overrides
|_bulma_sub
  |_css
    |_overrides.css
    |_style.css

The sites shows up without any css applied. And also the Home link is duplicated. Any ideas?

And I get this error

Fatal error: Cannot redeclare bulma_library_info_alter() (previously declared in C:\Users\Y430\Sites\devdesktop\drupal-8.9.1\themes\bulma\bulma.theme:416) in C:\Users\Y430\Sites\devdesktop\drupal-8.9.1\themes\bulma_sub\bulma_sub.theme on line 416

if I copy bulma.theme file to bulma_sub folder. I didn't face such issue when I created a sub-theme based on bootstraps before!

1 Answer 1

1

You need to carefully read the creating a subtheme guide.

The fatal error is because you just cut-and-paste the bulma.theme file from the main theme to the sub theme. This means that all the functions in the main theme are included twice-- once in the main theme, and once in the subtheme (because you have an exact copy in the subtheme).

You have two options to fix this.

  1. Remove all the function calls in bulma_sub.theme, or
  2. Rename the function calls to match the subtheme.

For example,

function bulma_preprocess_views_view_table(&$variables) should be function bulma_sub_preprocess_views_view_table(&$variables) {

Normally, you would do 1 (remove all the function calls in the subtheme) because those function calls will still get called from the main theme, so you only need them in your subtheme if you actually want to do something different than the main theme.

You wrote:

there are three files in bulma_sub folder with the following set-up.

You will also need to put the CSS you want to use (that you defined in bulma_sub.libraries.yml) in the subtheme folder.

3
  • In bootstrap theme I put an empty style.css file under sub-theme's directory and when I wanted to change something I just wrote the class name of that particular css attribute and added to style.css. I didn't have to copy all the css content to my style.css file. So, in the above case do I have to copy everythin inside the base theme's css file into my custom style.css file and make changes there? Commented Nov 23, 2020 at 17:43
  • @KarelCapek You need to read the documentation; I said that you had to include a CSS file based on the fact that you defined a library that included a CSS file, but you noted that you didn't actually include that file in your theme. Different themes have different requirements; the theme should document this, but less popular themes like Bulma may not (Bootstrap is the most popular theme on drupal.org, so it is documented). If you can't find the documentation and are lost, ask in the theme's issue queue, or look for another theme. Commented Nov 23, 2020 at 18:17
  • @KarelCapek I use Drulma (drupal.org/project/drulma), which is also Bulma-based and has a Drush command to automatically generate a subtheme if you install the companion module. Commented Nov 23, 2020 at 18:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.