Here you will find information on how you can use different tools to build Vanilla into production HTML and CSS.
To get set up with Sass, add the sass and vanilla-framework packages to your project dependencies:
yarn add sass vanilla-framework
In the script that builds the CSS in your package.json, you should include the path to node_modules when looking for @imports. In this example, we have called the build script "build-css":
"build-css": "sass -w --load-path=node_modules src:dist --style=compressed"
Make a folder src/, create a file inside called style.scss and import Vanilla:
@import "vanilla-framework";
@include vanilla;
Now run yarn build-css, which will convert any Sass files in the src/ folder to CSS in the dist/ folder. In this case, src/style.scss will compile to dist/style.css, which can then be safely included in an HTML file. Your project's folder structure should now look something like this:
To watch for changes in your Sass files, add the following script to your package.json:
"watch-css": "yarn build-css && sass --load-path=node_modules -w src:dist --style=compressed"
Now if you open an extra terminal and run yarn watch-css, the CSS will be rebuilt every time your Sass files are edited and saved.
A variety of Vanilla's components and patterns are offered as Jinja macros, which may be useful to you if you build sites using the Jinja templating engine. These macros can help abstract away some of the complexity of Vanilla's HTML, making producing complex page layouts simpler and faster.
In order to pull Vanilla's macros into your project, you may need to expose them to your webserver or templating engine. An example of this using Flask and Jinja might look like the following:
from flask import Flask
from jinja2 import ChoiceLoader, FileSystemLoader
app = Flask(__name__)
# ChoiceLoader attempts loading templates from each path in successive order
loader = ChoiceLoader([
FileSystemLoader('templates'),
FileSystemLoader('node_modules/vanilla-framework/templates/')
])
# Loader supplied to jinja_loader overwrites default jinja_loader
app.jinja_loader = loader
After making the macros available to your webserver/templating engine, see the individual component/pattern docs for import and usage instructions.
Some macros use Jinja namespaces to allow for more powerful state management. Jinja namespaces were introduced in Jinja v2.10 - please ensure you are using a compatible version of Jinja.
Some macros support attribute forwarding, which allows you to pass structured attributes to specific elements within the macro. This enables macros to apply more structure to the markup they produce and reduce the amount of boilerplate code you need to write.
For example, consider a pattern that accepts images as raw HTML, which are wrapped by the macro in a container that expects the image to have a specific class.
A user would be required to apply that class manually, as with the p-image-container__image class in the example below:
{{ vf_linked_logo_section(
title_text="Come for PostgreSQL, get security and support for your entire stack",
links=[
{
"href": "#",
"text": "Learn more ›",
"label": "Kubeflow",
"image_html": '
<img
{#- we need to apply this class manually, or layouts will be broken -#}
class="p-image-container__image custom-class"
src="https://assets.ubuntu.com/v1/cd89477e-kubeflow-logo-container-vert-fill.png"
alt=""
width="432"
height="481"
/>
'
},
{#- more links... -#}
Going forward, some patterns will support a structured dictionary of attributes that are applied to specific elements within the macro. This allows the macro to handle more business logic, such as applying required classes to elements.
The macro can apply its own structure to markup, and forward your attributes, allowing you to pass additional attributes without needing to know the internal structure of the macro. An example snippet that produces equivalent markup to the above, using attribute forwarding, is shown below:
{{ vf_linked_logo_section(
title_text="Come for PostgreSQL, get security and support for your entire stack",
links=[
{
"href": "#",
"text": "Learn more ›",
"label": "Kubeflow",
"image_attrs": {
"src": "https://assets.ubuntu.com/v1/cd89477e-kubeflow-logo-container-vert-fill.png",
{#-
Class is no longer required - macro will apply p-image-container__image
We can still pass additional classes if needed
-#}
"class": "custom-class",
"alt": "",
"width": "432",
"height": "481"
}
},
Any _attrs parameter in a macro should be treated as an attribute forwarding parameter.
It applies all attributes to the targeted element, unless otherwise specified in the documentation for that macro.
The macro may also apply its own attributes to the element, which will be merged with the attributes you pass in.
If you use a templating tool that generates raw HTML, you should switch to an output mode that outputs element attributes as a dictionary or object, if available.
For example, canonicalwebteam.image-template supports an output_mode="attrs" option that outputs image attributes as a dictionary, rather than raw HTML.
Webpack is used to compile JavaScript modules, and can be used to inject Vanilla styles to the DOM. To get set up using Vanilla with Webpack, add the webpack and vanilla-framework packages to your project dependencies:
yarn add webpack webpack-cli vanilla-framework
You'll also need to install the required loaders for compiling and bundling Vanilla's Sass files:
yarn add node-sass css-loader sass-loader style-loader
Make a folder src/, create a file inside called style.scss and import Vanilla:
@import 'vanilla-framework';
@include vanilla;
Now, create a webpack.config.js file at your project's root, and write the following:
var path = require('path');
module.exports = {
mode: 'development',
entry: './src/app.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(scss)$/,
use: [
{
// Adds CSS to the DOM by injecting a `<style>` tag
loader: 'style-loader'
},
{
// Interprets `@import` and `url()` like `import/require()` and will resolve them
loader: 'css-loader'
},
{
// Loads a SASS/SCSS file and compiles it to CSS
loader: 'sass-loader',
}
]
}
]
}
};
Create a file called app.js inside the src/ folder, and write the following:
import './style.scss';
Add a build script to your package.json to run the webpack command:
"build": "webpack"
Now run the command with yarn build, which will bundle the code and put in a a dist/ folder. At the project root, create an index.html with the following code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Vanilla using Webpack</title>
</head>
<body>
<section class="p-strip">
<div class="row--25-75">
<div class="col">
<h1>Hello Vanilla!</h1>
<p>This page has been built using Webpack!</p>
</div>
</div>
</section>
<script src="./dist/bundle.js"></script>
</body>
</html>
Opening this index.html file should display a title in Vanilla styling, despite not having any CSS.
To enable hot module replacement, you need to add webpack-dev-server to your list of packages:
yarn add webpack-dev-server
And add a "start" script to your package.json to run it:
"start": "webpack serve --open"
Now when you run yarn start, the Webpack development server will start at http://localhost:8080/ by default. It should show the same index.html file as before, however now you can edit the styles in src/style.scss and the page will automatically refresh to reflect the changes.
Your project's folder structure should now look something like this:
To provide the same browser support as Vanilla, you should also include autoprefixer in the build pipeline via postcss-loader.
For more options on configuring Webpack, for example minification and autoprefixing, refer to the Webpack documentation.
To get started with gulp-sass, add the following packages to your project:
yarn add gulp gulp-sass vanilla-framework
Create a file called gulpfile.js in your main directory and write:
var gulp = require('gulp');
var sass = require('gulp-sass');
gulp.task('build-css', function() {
return gulp.src('./src/*.scss')
.pipe(sass({ includePaths: ['./node_modules'] }))
.pipe(gulp.dest('./dist'));
});
gulp.task('watch-css', function() {
gulp.watch('./src/*.scss', ['build-css']);
});
Make a folder src/, create a file inside called style.scss and import Vanilla:
@import vanilla-framework;
@include vanilla;
Now run gulp build-css, which will convert any Sass files in the src/ folder to CSS in the dist/ folder. In this case, src/style.scss will compile to dist/style.css, which can then be safely included in an HTML file. Your project's folder structure should now look something like this:
If you open an extra terminal and run gulp watch-css, the CSS will be rebuilt every time your Sass files are edited and saved.
To provide the same browser support as Vanilla, you should also include autoprefixer in the build pipeline via gulp-autoprefixer.
For more options on configuring gulp-sass, for example minification and autoprefixing, refer to the gulp-sass documentation.
Creating a submodule in the git repo does not add all the code to the project but includes a reference and path to include the framework. You may find this method useful if you're planing to host on Github Pages.
Run this command at the root of your project (replacing vX.X.X with the release you wish to use)
git submodule add -- git@github.com:canonical/vanilla-framework.git _sass/vanilla-framework && cd _sass/vanilla-framework && git checkout vX.X.X
By running the following command it will pull down the framework into the correct location.
git submodule update
Vanilla Framework's JavaScript modules complement the CSS framework by providing the interactive behavior for components that require it. While Vanilla's CSS handles all the visual styling, some components need JavaScript to function properly - such as switching between tab panels, toggling navigation menus, or handling form interactions.
The main benefits of using Vanilla's JavaScript modules are:
These modules are entirely optional - if you're building static pages or handling interactivity with your own JavaScript, you can use Vanilla's CSS styling without any JavaScript. However, for components that require interactive behavior, Vanilla's JavaScript ensures everything works as intended.
Note: Vanilla's ESM (ES Module) support is currently being expanded. While the module system is in place, only a limited set of components have JavaScript modules available at this time.
For modern projects utilizing a module bundler like esbuild, Webpack, or Rollup, you can import the modules directly from the installed package.
This is the recommended approach, as it allows your bundler to perform tree-shaking and include only the necessary code.
// Import the 'tabs' module
import {tabs} from 'vanilla-framework/js';
// Initialize the tabs component
tabs.initTabs('[role="tablist"]');
If you need access to everything or prefer a single import statement, you can import all modules into a single namespace.
// Import all modules and assign them to the 'vanilla' object
import * as vanilla from 'vanilla-framework/js';
If your project does not use a bundler (e.g., using a simple server or static HTML files), you need to manually copy the necessary JavaScript files into your project path and load them as native ES modules.
Copy the required module files from node_modules into a local directory that your web server can access. The exact path may vary based on your project setup.
Example build script (e.g., in package.json):
"build-vanilla-js": "mkdir -p static/js/modules/vanilla-framework/js && cp -r node_modules/vanilla-framework/templates/js static/js/modules/vanilla-framework"
Reference the individual module files in your HTML using a <script type="module"> tag, pointing to the location where you copied the files.
Example HTML:
<script type="module" src="/static/js/modules/vanilla-framework/js/tabs.js"></script>