Customizing Vulk
Philosophy
Vulk is built to be an extremely modular and flexible product. Layouts have been destructured so you can easily switch between them, without breaking your page content. Therefore, page inner content is considered as an aggregate of components that get injected in the currently active layout.
Vulk ships with a Quickstarter project. The Quickstarter is a smaller project that contains only what you need to get started with a lighter and less overwhelming codebase. The recommended way of working is to open both projects, and start copying and pasting what you need from the main Vulk project to the Quickstarter project.
Hybrid Sass
my-vulk-quickstarter-project/
├── src/
│ ├── scss
│ │ ├── abstracts
│ │ ├── bulma-generated
│ │ ├── css-variables
│ │ ├── utilities
│ │ └── main.scss
│ └── main.ts
└── index.html
Vulk comes with a great paradigm change regarding the initial Bulma, in terms of Sass implementation. While everyone is familiar with traditional Sass variables like $color
, we chose to drop this format to take advantage of the power of native CSS-vars
. To support this, we had to make several major changes in the way we handle the compilation of the different color palettes.
The main Bulma framework is built with traditional Sass variables and does not support CSS-vars. We had to find a solution for this. Therefore, we decided to enhance our existing Bulma package with this Bulma plugin: https://github.com/wtho/bulma-css-vars. This plugin fully supports CSS-vars and patches the initial Bulma code base, making possible this implementation.
There are 2 variables sub-folders in Vulk: scss/bulma-generated/
and scss/css-variables
. The first one is automatically rendered by a Node.js utility and is in charge of generating all the Bulma variables, based on your configuration.
Changing the main color
If you need to change the Vulk main color, you need to go through a short compilation step. The main color is generated from an HSL format. This means that you will need to define your Vulk primary color in HSL format for it to work. Here are the different steps you need to go through:
- Choose a primary color for your project. It can be in hex or rgb format, it doesn't matter. Let's go for the example with a purple color like
#6621cf
. - In any colorpicker of your choice, transform your color into an HSL color with a value for each attribute, Hue, Saturation and Luminance. In our case this would result in
264°, 73%, 47%
. - Open
vulk/bulma-css-vars.config.js
. In that file replace the values of theprimary: hsl(153, 48, 49)
block with the values you got one step earlier. You can also change the default values of some basic Bulma variables likedark
,link
,info
etc... - Once you're done with that, you're ready to run a utility to generate all your colors.
- In your terminal, run the
pnpm build:update-bulma-colors
. - Tada! You are now done and all your new colors have been generated for you.
- Please note that you will also need to change the value of the
primary
variable insidevulk/src/scss/css-variables/_colors.scss
to complete the color setup.
CSS vars syntax
CSS variables use a different syntax than Sass variables. Declaring a new CSS variable is like this:
Declaration
// :root is an alias for html element but with higher priority
:root {
--myVariable: #fff;
}
// we can override the variable value inside a class scope
.my-red-variable {
--myVariable: red;
}
<!-- we can also override the variable value inside a style scope -->
<span style="--myVariable: blue">
<!-- ... -->
</span>
Usage
.my-variable-color {
color: var(--myVariable);
}
Overriding CSS vars
CSS variables are very flexible and can be overridden from almost everywhere, without affecting other components. For example, let's say that you have a component called <SuperButton></SuperButton>
and that component has a scoped style attribute (<style lang="scss" scoped></style>
), meaning the styles are strictly scoped to that same component. You can override the base --primary
variable inside the component without affecting any other one outside the scope of this one. For example, you can do:
<!-- SuperButton.vue -->
<template>
<button class="super-button">
<!-- ... -->
</button>
</template>
<style lang="scss" scoped>
.super-button {
--primary: blue;
}
</style>
Other Sass files
Vulk relies on the powerful Sass features and a modular structure, letting you handle complex styles in a breeze. You need to import all the SCSS partials into your core file. This is how SCSS files are organized. Partial SCSS file names always start with an underscore like this: _button.scss
. They act as chunks of code that you can import only if you need them.
Import styles in Vue
In order to load stylesheets into our application (e.g if you need to add additional styles from a node_modules
plugin), we simply need to import css
, sass
or scss
files in the src/styles.ts
file. This file is included in your bundle because it is referenced inside root index.html
file
// file: ./src/styles.ts
// ...
import "vue3-carousel/dist/carousel.css";
import "./scss/main.scss";
// ...
All imported files here are to be converted in
css
, and injected automatically inindex.html
at build and run time. The injected styles are available globally.
Bulma Integration
Classic Bulma used to be initially integrated with Vulk. This meant that when you changed the $primary
Vulk color variable, it took precedence over any Bulma related variable. Vulk now fully supports native CSS variables and dropped Sass variables support during the development phase.
Native Dark Mode
Vulk comes with a native Dark mode. This means that all components are pre-styled for dark mode. You don't have to worry about it, when you turn it on, the colors change seamlessly. Dark mode styling is made through a global .is-dark
class added to the page <html>
root element. Dark mode is toggled on the body with javascript. In another type of implementation, the body would have to be rendered by the server with the proper class before being served to the client, based on the user selection.
TIP
.is-dark
class is not restricted to <html>
element, you can add this class to any element, so all childrens will be in dark mode!
Dark Mode and CSS vars
With the introduction of CSS vars, Dark Mode is now handled at the color level. You can control how a CSS variable behaves at runtime, based on the parent classes. For example let's says we have a CSS variable like this: --color: red
. We can change the value of this color in dark mode by editing the variable, preventing us to write additional dark mode CSS code.
// Normal mode
:root {
--color: red;
}
// Dark mode
.is-dark {
--color: blue;
}
Lazyloading SCSS
my-vulk-quickstarter-project/
├── src/
│ ├── components/
│ │ └── some-component/*.vue
The components folder holds the vue components stored as chunks of reusable UI that can be inserted in all available layout types. Each element is a Vue 3 component with a <style>
element that holds required SCSS. This way, you do not load unnecessary CSS when browsing.
They are not added by default to main.scss
, instead we lazyload them:
<!-- file ./src/components/my-component/MyComponent.vue -->
<script setup lang="ts">
// ...
</script>
<template>
<!-- ... -->
</template>
<style lang="scss">
.my-component-wrapper {
/* custom scss for this component */
&:hover {
color: var(--primary);
}
}
</style>
Vue Components
Vue 3 being very powerful, we built Vulk so you don't have to worry about moving components from a folder to another or from a project to another. It is as simple as copying and pasting your components in the target folder. Because this project uses the unplugin-vite-components
, all your components are parsed and available in your pages and other components without a single import statement. We kept the components CSS out of the .vue
files so it is easier for you to explore the template styles and to adapt them to your needs.
However, we recommend that when you build your own project with Vulk, you take advantage of the .vue
files potential by scoping your styles to your component, like we do with the page components. This way, the component styles are only loaded when the component is displayed.
<script setup lang="ts">
export type MyComponentColors = 'red' | 'blue' | 'green'
export interface MyComponentsProps = {
color?: MyComponentColors
label: string
}
const props = defineProps<MyComponentsProps>();
</script>
<template>
<button class="button" :class="[props.color && `is-${props.color}`]">
{{ props.label }}
</button>
</template>
<style lang="scss" scoped>
.button {
&.is-red {
color: var(--red);
}
&.is-blue {
color: var(--blue);
}
&.is-green {
color: var(--green);
}
&.is-purple {
color: var(--purple);
}
}
</style>