Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/vufind-org/vufind/llms.txt

Use this file to discover all available pages before exploring further.

VuFind’s theme system lets you control every visual aspect of your discovery portal — from colors and typography to page layout and template markup — without touching core files. Themes are organized as directories under themes/ and can extend one another through a parent/child relationship, inheriting assets and templates while selectively overriding only what you need.

Built-in themes

VuFind ships with three themes that form a hierarchy:

root

The base of the hierarchy. Sets up view helpers, template resolution, and the icon system. Never use this theme directly; it provides the infrastructure all other themes build on. Its extends key is false.

bootstrap5

The primary HTML5 theme built on Bootstrap 5. It extends root, bundles jQuery, Popper, Bootstrap JS, Font Awesome 4.7, and the full VuFind JavaScript suite. Provides a complete but lightly styled interface.

sandal5

The default theme shipped with VuFind. It extends bootstrap5 and applies a “flat” design with additional SCSS to give the interface a more opinionated, polished look. Set as the default in config.ini.
The inheritance chain is: your theme → sandal5 → bootstrap5 → root.

Setting the active theme

Open local/config/vufind/config.ini (copy from config/vufind/config.ini if it does not exist yet) and locate the [Site] section:
config.ini
[Site]
; Available standard themes: bootstrap5, sandal5
theme = sandal5
You can also configure a separate theme for the Admin module and expose alternate themes to end users via query parameter:
config.ini
; Optional: different theme for the Admin module
;admin_theme = sandal

; Allow users to switch themes via ?ui=theme1
;alternate_themes = theme1:myTheme1,theme2:myTheme2

; Show a theme picker dropdown to users
;selectable_themes = "standard:Standard Theme"
Changes to the theme setting take effect immediately; no cache flush is required unless you also have asset minification enabled.

Theme directory structure

Each theme lives in its own subdirectory under themes/. The bootstrap5 theme illustrates the full layout:
themes/
bootstrap5/
css/
images/
js/
scss/
templates/
tools/
theme.config.php
package.json
DirectoryPurpose
css/Compiled CSS files (output of Grunt/Sass)
scss/SCSS source files; compiled.scss is the Grunt entry point
js/JavaScript files
images/Theme images and icons
templates/Laminas/Zend view scripts (.phtml)
theme.config.phpDeclares extends, CSS/JS assets, view helpers, icons

The theme.config.php file

Every theme must have a theme.config.php that returns a PHP array. At minimum it needs an extends key:
theme.config.php
<?php
return [
    'extends' => 'bootstrap5',
];
The sandal5 theme uses exactly this. The bootstrap5 theme is more complete, registering CSS files, JS files with priority ordering, favicons, view helper aliases, and icon sets:
theme.config.php
<?php
return [
    'extends' => 'root',
    'css' => [
        ['file' => 'compiled.css'],
        ['file' => 'print.css', 'media' => 'print'],
    ],
    'js' => [
        ['file' => 'vendor/jquery.min.js', 'priority' => 110],
        ['file' => 'vendor/bootstrap.min.js', 'priority' => 140],
        ['file' => 'common.js', 'priority' => 310],
        // ...
    ],
    'favicon' => 'vufind-favicon.ico',
    'icons' => [
        'defaultSet' => 'FontAwesome',
        // ...
    ],
];

Creating a custom theme

1

Create the theme directory

Create a new directory under themes/. The local_theme_example directory in the VuFind source is a working reference you can copy:
mkdir -p themes/mytheme/scss themes/mytheme/css themes/mytheme/images themes/mytheme/templates
2

Write theme.config.php

Declare which built-in theme your theme extends. Most custom themes extend bootstrap5 directly to get full control, or extend sandal5 to inherit its flat styling:
themes/mytheme/theme.config.php
<?php
return [
    'extends' => 'bootstrap5',
];
3

Create your SCSS entry point

Grunt compiles any theme that has scss/compiled.scss. The local_theme_example pattern is to import a custom.scss that overrides Bootstrap variables before importing the parent theme’s SCSS:
themes/mytheme/scss/custom.scss
/* Override Bootstrap variables before importing the base theme */
$primary: #0045a1;
$navbar-dark-color: white;

@import "bootstrap"; /* Pulls in bootstrap5 theme's SCSS base */

/* Add your own rules below */
body {
  font-size: 14px;
  color: #333;
}
themes/mytheme/scss/compiled.scss
@import "custom";
The @import "bootstrap" in custom.scss resolves through the theme inheritance path that Grunt builds from theme.config.php. This prevents Bootstrap from being included twice in the final CSS.
4

Compile SCSS with Grunt

From the VuFind root directory, compile all themes’ SCSS to CSS:
grunt scss
During active development, use the watch task to recompile automatically on file save:
grunt watch:scss
Grunt scans every themes/*/scss/compiled.scss and outputs a compiled css/compiled.css alongside it.
5

Activate your theme

Add the theme name to your local config.ini:
local/config/vufind/config.ini
[Site]
theme = mytheme

Overriding templates

When VuFind resolves a template, it walks the theme inheritance chain from child to parent and uses the first match it finds. To override a template, copy it from the parent theme into your theme’s templates/ directory and modify it there. For example, to override the search results header:
cp themes/bootstrap5/templates/search/results.phtml themes/mytheme/templates/search/results.phtml
The bootstrap5 theme provides 69 template subdirectories covering every part of the interface, from admin/ and auth/ to search/, record/, and myresearch/.
Keep overridden templates to a minimum. Each copied template becomes a maintenance burden because upstream bug fixes and new features in the parent template will not be inherited automatically.

Asset compilation with Grunt

VuFind uses Grunt with grunt-dart-sass for SCSS compilation. The Gruntfile.js in the project root handles discovery and compilation of all themes automatically.
cd themes/bootstrap5
npm install
cd ../..
npm install
Grunt resolves SCSS @import paths by traversing the extends chain in theme.config.php, so imports like @import "bootstrap" automatically resolve to the correct parent theme file.

Asset pipeline (minification)

VuFind supports optional asset concatenation and minification, configured in the [Site] section of config.ini:
config.ini
; Enable JS minification in production, disable in development
asset_pipeline = "development:off; production:js,css"
Clear local/cache/public/ after changing asset_pipeline settings so the new configuration takes effect.

Using mixins

In addition to the parent/child extends relationship, VuFind supports mixins — lightweight theme fragments that inject only JS or CSS. The local_mixin_example theme demonstrates this pattern with a mixin.config.php instead of theme.config.php:
themes/local_mixin_example/mixin.config.php
<?php
return [
    'js' => ['mixin-popup.js'],
];
Reference a mixin from your theme’s theme.config.php to include its assets alongside your own, without creating a full inheritance relationship.
The bootstrap5 theme configures Font Awesome 4.7 as the default icon set and defines aliases for every icon used across VuFind. To override an icon in your theme, add an icons key to theme.config.php with only the aliases you want to change:
themes/mytheme/theme.config.php
<?php
return [
    'extends' => 'bootstrap5',
    'icons' => [
        'aliases' => [
            'search' => 'FontAwesome:magnifying-glass',
            'cart'   => 'FontAwesome:bag-shopping',
        ],
    ],
];
The bootstrap5 theme supports configurable sticky page elements. Enable them in theme.config.php under stickyElements, for example to make the search bar sticky on non-mobile screens:
themes/mytheme/theme.config.php
<?php
return [
    'extends' => 'bootstrap5',
    'stickyElements' => [
        ['selector' => '.search.container.navbar'],
    ],
];
You can specify a separate theme for the Admin module so your institutional branding does not interfere with administrative pages:
local/config/vufind/config.ini
[Site]
theme       = mytheme
admin_theme = sandal5

Build docs developers (and LLMs) love