Let’s create a Drupal 9 theme for our Drupal website.There are a lot of base themes you can use, but in this tutorial I want to create a custom theme that has Bootstrap 5 integration via npm.
To mytheme.info.yml:
name: My theme
type: theme
base theme: false
description: 'Custom theme MyTheme'
package: Other
core_version_requirement: ^9
regions:
header: 'Header'
pre_content: 'Pre-content'
breadcrumb: Breadcrumb
highlighted: Highlighted
help: Help
content: Content
page_top: 'Page top'
page_bottom: 'Page bottom'
sidebar_first: 'First sidebar'
Now Drupal is able to recognize and enable your theme under /admin/appearance ("install and set as default").
optional: you can add a screenshot thumbnail to mytheme/screenshot.png and add it as a key to mytheme.info.yml:
screenshot: screenshot.png
Next, we want to add the proper styling (CSS & js). We do this by adding a mytheme.libraries.yml file:
global:
version: 1.X
js:
js/scripts.js: {}
css:
theme:
css/style.css: {}
Now we add the libraries file to our mytheme.info.yml:
name: My theme
type: theme
base theme: stable
description: 'Custom theme MyTheme'
package: Other
core_version_requirement: ^9
libraries:
- mytheme/global
regions:
header: 'Header'
pre_content: 'Pre-content'
breadcrumb: Breadcrumb
highlighted: Highlighted
help: Help
content: Content
page_top: 'Page top'
page_bottom: 'Page bottom'
sidebar_first: 'First sidebar'
Now you can add your required CSS and JS files:
We want to have Bootstrap 5 as a dependency and we want to use the latest. I prefer not to use other themes as my base theme and want to add the npm packages right away. I add a file custom/mytheme/package.json containing the following:
{
"name": "mytheme",
"version": "1.0.0",
"description": "Custom Drupal theme with Bootstrap 5",
"main": "gulpfile.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"postinstall": "find node_modules/ -name '*.info' -type f -delete"
},
"author": "Stefvanlooveren <stefvanlooveren.me>",
"dependencies": {
"bootstrap": "^5.0.1",
"bootstrap-icons": "^1.5.0",
"@popperjs/core": "^2.9.2"
},
"devDependencies": {
"browser-sync": "^2.26.14",
"gulp": "^4.0.2",
"gulp-autoprefixer": "^7.0.1",
"gulp-babel": "^8.0.0",
"gulp-sass": "^4.1.0",
"gulp-sass-lint": "^1.4.0",
"gulp-watch": "^5.0.1",
"gulp-uglify": "^3.0.2"
}
}
Next, we also want to create a gulpfile.js for some front-end awesomeness:
To custom/mytheme/gulpfile.js, add:
let gulp = require('gulp'),
sass = require('gulp-sass'),
autoprefixer = require('gulp-autoprefixer'),
browserSync = require('browser-sync').create(),
uglify = require('gulp-uglify');
const paths = {
scss: {
src: './scss/style.scss',
dest: './css',
watch: './scss/**/*.scss'
},
js: {
bootstrap_src: './node_modules/bootstrap/dist/js/bootstrap.min.js',
bootstrap_dest: './js/bootstrap',
popper: './node_modules/@popperjs/core/dist/umd/popper.min.js',
popper_dest: './js/popper',
theme_src: './js/src/*.js',
theme_dest: './js'
},
twig: {
watch: './templates/**/*.twig'
},
};
// Compresss theme JS
function buildjs() {
return gulp
.src([paths.js.theme_src])
.pipe(gulp.dest(paths.js.theme_dest))
.pipe(browserSync.stream());
}
// Compile sass into CSS & auto-inject into browsers
function compile() {
var sassOptions = {
outputStyle: 'expanded',
indentType: 'space',
indentWidth: 2,
linefeed: 'lf'
};
return gulp
.src([paths.scss.src], { sourcemaps: true })
// .src([paths.scss.src])
.pipe(sass(sassOptions).on('error', sass.logError))
.pipe(autoprefixer())
// .pipe(gulp.dest(paths.scss.dest))
.pipe(gulp.dest([paths.scss.dest], { sourcemaps: true }))
.pipe(browserSync.stream());
}
// Move the Bootstrap JavaScript files into our js/bootstrap folder.
function move_bootstrap_js_files() {
return gulp
.src([paths.js.bootstrap_src])
.pipe(gulp.dest(paths.js.bootstrap_dest))
.pipe(browserSync.stream());
}
// Move the Popper JavaScript files into our js/popper folder.
function move_popper_js_files() {
return gulp
.src([paths.js.popper])
.pipe(gulp.dest(paths.js.popper_dest))
.pipe(browserSync.stream());
}
// Watching scss files.
function watch() {
browserSync.init({
proxy: "bs5.localhost:8888",
open: false
});
// gulp.watch([paths.js.theme_src], buildjs).on('change', browserSync.reload);
gulp.watch([paths.js.theme_src], buildjs);
gulp.watch([paths.scss.watch], compile);
gulp.watch([paths.twig.watch]).on('change', browserSync.reload);
}
const build = gulp.series(buildjs, compile, move_bootstrap_js_files, move_popper_js_files, gulp.parallel(watch));
exports.buildjs = buildjs;
exports.compile = compile;
exports.move_bootstrap_js_files = move_bootstrap_js_files;
exports.move_popper_js_files = move_popper_js_files;
exports.watch = watch;
exports.default = build;
Now run
npm install
gulp
The result is this:
[10:49:29] Using gulpfile /var/www/html/web/themes/custom/mytheme/gulpfile.js
[10:49:29] Starting 'default'...
[10:49:29] Starting 'buildjs'...
[10:49:29] Finished 'buildjs' after 73 ms
[10:49:29] Starting 'compile'...
[10:49:29] Finished 'compile' after 84 ms
[10:49:29] Starting 'move_bootstrap_js_files'...
[10:49:29] Finished 'move_bootstrap_js_files' after 4.76 ms
[10:49:29] Starting 'move_popper_js_files'...
[10:49:29] Finished 'move_popper_js_files' after 2.84 ms
[10:49:29] Starting 'watch'...
[Browsersync] Proxying: http://mytheme.localhost:8888
[Browsersync] Access URLs:
-----------------------------------
Local: http://localhost:3000
External: http://10.10.32.2:3000
-----------------------------------
UI: http://localhost:3001
UI External: http://localhost:3001
First of all it will download Bootstrap to the node_modules and automatically copy a minified version to js/bootstrap/bootstrap.min.js.
To include it, we add a new library to custom/mytheme/mytheme.libraries.yml:
bootstrap:
version: '5.0.1'
js:
js/bootstrap/bootstrap.min.js: { minified: true }
dependencies:
- core/jquery
Don't for get to clear caches. But we are not ready yet.
To get started with including the Bootstrap styles, create a new file in custom/mytheme/scss/style.scss. Gulp will then watch for changes and copy them to custom/mytheme.css/style.css.
To the custom/mytheme/scss/style.scss file, add the bootstrap dependencies:
// --------------------------------------------------------
// 1. Include functions first (so you can manipulate colors, SVGs, calc, etc)
// --------------------------------------------------------
@import "../node_modules/bootstrap/scss/functions";
// --------------------------------------------------------
// 2. Include your variable overrides here
// --------------------------------------------------------
@import "variables"; // These override default Bootstrap variables
// --------------------------------------------------------
// 3. Include remainder of required Bootstrap stylesheets
// --------------------------------------------------------
@import "../node_modules/bootstrap/scss/variables";
@import "../node_modules/bootstrap/scss/mixins";
// --------------------------------------------------------
// 4. Include any optional Bootstrap components as you like
// --------------------------------------------------------
@import "../node_modules/bootstrap/scss/root";
@import "../node_modules/bootstrap/scss/reboot";
@import "../node_modules/bootstrap/scss/type";
@import "../node_modules/bootstrap/scss/images";
@import "../node_modules/bootstrap/scss/containers";
@import "../node_modules/bootstrap/scss/grid";
//@import "../node_modules/bootstrap/scss/tables";
@import "../node_modules/bootstrap/scss/forms";
@import "../node_modules/bootstrap/scss/buttons";
@import "../node_modules/bootstrap/scss/transitions";
//@import "../node_modules/bootstrap/scss/dropdown";
//@import "../node_modules/bootstrap/scss/button-group";
@import "../node_modules/bootstrap/scss/nav";
@import "../node_modules/bootstrap/scss/navbar";
//@import "../node_modules/bootstrap/scss/card";
//@import "../node_modules/bootstrap/scss/accordion";
@import "../node_modules/bootstrap/scss/breadcrumb";
@import "../node_modules/bootstrap/scss/pagination";
//@import "../node_modules/bootstrap/scss/badge";
@import "../node_modules/bootstrap/scss/alert";
//@import "../node_modules/bootstrap/scss/progress";
@import "../node_modules/bootstrap/scss/list-group";
@import "../node_modules/bootstrap/scss/close";
//@import "../node_modules/bootstrap/scss/toasts";
@import "../node_modules/bootstrap/scss/modal";
//@import "../node_modules/bootstrap/scss/tooltip";
//@import "../node_modules/bootstrap/scss/popover";
//@import "../node_modules/bootstrap/scss/carousel";
//@import "../node_modules/bootstrap/scss/spinners";
//@import "../node_modules/bootstrap/scss/offcanvas";
@import "../node_modules/bootstrap/scss/utilities";
// Utilities
@import "../node_modules/bootstrap/scss/utilities/api";
To the custom/mytheme/scss/_variables.scss file, add the contents of this file: this allows you to override settings as colors, grids, etc. Full control over the bootstrap template!
Clear the Drupal cache and you will have loaded all the JS and CSS dependencies you need.
So you would probably want to "bootstrapify" your typical building blocks like forms, views, navigation etc. Thijs Boots has a good starterkit on Github. You can copy the "templates" folder into custom/mytheme.
Now if you clear caches again, you will see your sidebars, buttons, navigation etc:
Shout out to this Github repository (Thijs Boots)