Styles and Layout โ
Learn about the many ways how Vueform styles and layouts can be customized.
Size โ
We can use any element in three different sizes: sm
, md
and lg
. The md
is the default one, while we can switch to eg. sm
using size
prop:
<TextElement label="Small" placeholder="Small" size="sm" ... />
<TextElement label="Medium" placeholder="Medium" ... />
<TextElement label="Large" placeholder="Large" size="lg" ... />
Views โ
Some elements and components have alternative views, that we can use with view
option:
<!-- Default view -->
<CheckboxgroupElement :items="['Vue.js', 'React', 'AngularJS']" ... />
<!-- Tabs view -->
<CheckboxgroupElement view="tabs" :items="['Vue.js', 'React', 'AngularJS']" ... />
<!-- Blocks view -->
<CheckboxgroupElement view="blocks" :items="[
{ value: 'Vue.js', name: 'Vue.js', description: 'Vue.js framework' },
{ value: 'React', name: 'React', description: 'React framework' },
{ value: 'AngularJS', name: 'AngularJS', description: 'AngularJS framework' },
]" ... />
Views for Children โ
The view
we define for an element will be applied to each of its child components (if the view exist for them with the same name). If we want to change the views of the element's child components, we can use views
property, which overrides the elements default view
option:
<TextElement :views="{
ElementLabel: 'highlighted'
}" ... />
In this case ElementLabel_highlighted
template will be used instead of ElementLabel
for this specific element if it registered as a template.
If a component has multiple available views, they can be found at the API reference, under the components Views section.
Columns โ
We can pass an object to :columns
prop of any element to define its column widths:
<TextElement label="Label" :columns="{ container: 12, label: 3, wrapper: 12 }" ... />
Columns add grid classes (like col-6
or w-1/2
) to container
, label
and wrapper
of an element, where:
container
is the outermost DOM of the element that contains bothlabel
andwrapper
label
contains the labelwrapper
contains the custom parts of element.
Here they are more visually:
container: 12
label: 3
wrapper: 12
The value of container
defines the size of the element's container. 12
will result in full width, 6
in half, 4
in third and so on.
The value of label
defines the amount of space the label should take up within the container. If the container
is 12
and label
is 6
the label is going to take up half the space and the element's wrapper will the other half (which is calculated automatically). If the container
is 6
and label
is 6
, the label will only take up one forth and the element's wrapper the rest. In case the label
has full width (12
) the element's wrapper will also take up full space instead of being zero.
The value of wrapper
defines the size of the element's wrapper within the space left for it in the container after subtracting the size of the label. If the container
is 12
and label
is 4
the space left for the element's wrapper is 8
. In this case if the wrapper
value is 12
it will take up the full space left for the it (which is 8
) while if it is changed to 6
it will only take up half the space left for it (4
):
<TextElement label="Label" :columns="{ container: 12, label: 4, wrapper: 12 }" />
<TextElement label="Label" :columns="{ container: 12, label: 4, wrapper: 6 }" />
Note that while the size of the element's wrapper container changes the size of other extras like a description or error won't be limited to the field's space. Instead it will take up the full space left by the label:
<TextElement
label="Label"
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit"
:columns="{ container: 12, label: 4, wrapper: 6 }"
...
/>
We can set the value of columns
as a number
which case the container
will receive its value without affecting the default label
and wrapper
values:
<TextElement label="Label" :columns="6" ... /> <!-- { container: 6, label: 3, wrapper: 12 } -->
Responsive Columns โ
We can also define column values for different breakpoints using the theme system's breakpoints like sm
, md
, etc. as keys:
<TextElement label="Label" :columns="{
default: { container: 12, label: 12, wrapper: 12 },
sm: { container: 12, label: 4, wrapper: 12 },
md: 12,
lg: { container: 12, label: 2, wrapper: 12 }
}" ... />
Vueform is using a mobile-first breakpoint system to accomodate most popular CSS frameworks like Bootstrap or Tailwind CSS. The default
breakpoint is the one that will be used below the lowest breakpoint.
The default breakpoints are (based on Tailwind CSS breakpoint system):
Breakpoint prefix | Minimum width | CSS |
---|---|---|
sm | 640px | @media (min-width: 640px) { ... } |
md | 768px | @media (min-width: 768px) { ... } |
lg | 1024px | @media (min-width: 1024px) { ... } |
xl | 1280px | @media (min-width: 1280px) { ... } |
2xl | 1536px | @media (min-width: 1536px) { ... } |
Customizing Breakpoints โ
We can customize the form's breakpoints to fit our project's breakpoints.
In Tailwind Theme โ
When using tailwind
theme we don't have to do anything specific beside customizing the tailwind.config.js
to use certain breakpoints. Once we done that the same breakpoints can used for column definition.
Eg. customizing the tailwind.config.js
with the following values:
// tailwind.config.js
module.exports = {
theme: {
screens: {
'tablet': '640px',
// => @media (min-width: 640px) { ... }
'laptop': '1024px',
// => @media (min-width: 1024px) { ... }
'desktop': '1280px',
// => @media (min-width: 1280px) { ... }
},
}
}
Allows us to use the following breakpoints for columns for any Vueform element:
<TextElement label="Label" :columns="{
default: { container: 12, label: 12, wrapper: 12 },
tablet: { container: 12, label: 4, wrapper: 12 },
laptop: { container: 12, label: 3, wrapper: 12 },
desktop: { container: 12, label: 2, wrapper: 12 },
}" ... />
In Other Themes โ
In other themes like the default vueform
theme we need to customize the breakpoints via a scss variable.
The first step is to replace our original css
import with the scss
counterpart:
// eg. index.css
// Original import
@import './../node_modules/@vueform/vueform/themes/vueform/css/index.min.css';
Instead of using a css
create an scss
file and import that somewhere in your project. The scss
file should contain the following:
// eg. index.scss
// New import
@import './../node_modules/@vueform/vueform/themes/vueform/scss/index.scss';
Now we can rely on scss
variables and we can override the breakpoints:
// eg. index.scss
$grid-breakpoints: (
tablet: 640px,
laptop: 1024px,
desktop: 1280px,
);
// New import
@import './../node_modules/@vueform/vueform/themes/vueform/scss/index.scss';
This will allow us to use our custom breakpoints in any Vueform element:
<TextElement label="Label" :columns="{
default: { container: 12, label: 12, wrapper: 12 },
tablet: { container: 12, label: 4, wrapper: 12 },
laptop: { container: 12, label: 3, wrapper: 12 },
desktop: { container: 12, label: 2, wrapper: 12 },
}" ... />
Changing Classes โ
Basics โ
Each component has a template defined by the theme. The template includes 3 parts:
- the actual
<template>
of the component - the classes that can be used in the template in
defaultClasses
object indata()
<style>
if the component is using named classes and not utility classes.
Themes also include a
classes.js
file that overrides anydefaultClasses
defined by the templates, which is useful if we want to centralize class definitions. Also, the<template>
part is provided by a theme calledblank
for each theme, so they don't have to be reimplemented in different themes only when something requires structural change.
Let's take a look at the <template>
part of TextElement
component:
<!-- From: @vueform/vueform/themes/blank/templates/elements/TextElement.vue -->
<template>
<ElementLayout class="classes.container">
<template element>
<div class="classes.inputContainer">
<ElementAddon type="before" ... />
<ElementAddon type="after" ... />
<ElementLabelFloating ... />
<ElementLoader ... />
<input class="classes.input" ... />
</div>
</template>
</ElementLayout>
</template>
The template has been simplified for better readability, but what's important for us are the classes.
We can see that TextElement
uses three class names: container
, inputContainer
and input
.
These classes are defined by themes differently, here's how they look like in vueform
theme:
// From: @vueform/vueform/themes/vueform/templates/elements/TextElement.vue
{
container: '',
inputContainer: 'vf-input-group',
// ...
input: 'vf-input',
input_enabled: '',
input_disabled: '',
input_sm: 'vf-input-sm',
input_md: '',
input_lg: 'vf-input-lg',
// ...
$input: (classes, { isDisabled, Size }) => ([
classes.input,
classes[`input_${Size}`],
isDisabled ? classes.input_disabled : classes.input_enabled
])
}
And this is how tailwind
theme defines them:
// From: @vueform/vueform/themes/tailwind/classes.js
{
container: '',
inputContainer: 'w-full flex',
// ...
input: 'w-full form-border form-border-color z-1 transition-shadow addon-before:form-rounded-l-none outline-none addon-after:form-rounded-r-none',
input_enabled: 'focus:form-ring',
input_disabled: 'form-bg-disabled form-text-disabled',
input_sm: 'form-p-input-sm form-rounded-sm form-text-sm',
input_md: 'form-p-input form-rounded',
input_lg: 'form-p-input-lg form-rounded-lg form-text-lg with-floating:form-p-input-floating-lg',
// ...
$input: (classes, { isDisabled, Size }) => ([
classes.input,
classes[`input_${Size}`],
isDisabled ? classes.input_disabled : classes.input_enabled
]),
}
(The TextElement
template changed in 1.2.0
, but it's easier to grasp the concept with this example.)
We can see that input
also has conditional classes:
input_enabled
- added when the input is enabledinput_disabled
- added when the input is disabledinput_sm
- added when the input's size issm
input_md
- added when the input's size ismd
input_lg
- added when the input's size islg
.
These classes are only added to input
if their conditions are fulfilled.
If a class name is also defined as a
function
and starts with a$
it can return a dynamic value for the original class name.
Based on this, the actual <input>
field have different classes in different states.
For example in tailwind
theme the TextElement
's input
class has input_md
and input_enabled
added by default:
input: `w-full form-border form-border-color z-1 transition-shadow
addon-before:form-rounded-l-none outline-none
addon-after:form-rounded-r-none ` // from `input`
+ `form-p-input form-rounded ` // from `input_md`
+ `focus:form-ring` // from `input_enabled`
If the element becomes disabled and uses sm size it'll change to this:
input: `w-full form-border form-border-color z-1 transition-shadow
addon-before:form-rounded-l-none outline-none
addon-after:form-rounded-r-none ` // from `input`
+ `form-p-input-sm form-rounded-sm form-text-sm ` // from `input_sm`
+ `form-bg-disabled form-text-disabled` // from `input_disabled`
This is how Vueform manages the different states and appearances of components driven by external parameters like Size
or isDisabled
.
Class Helpers โ
This is how TextElement
gets rendered in the browser using tailwind
theme:
If we check the source code we'll see it's complete HTML structure:
<div class="form-col w-full">
<div class="form-row flex flex-wrap form-mb-gutter">
<label for="text" class="form-col pr-4 form-py-input-border w-3/12">
<span>Text</span>
</label>
<div class="flex-1 w-9/12">
<div class="form-col w-full"></div>
<div class="form-col w-full">
<div class="w-full flex">
<input type="text" name="text" id="text" class="w-full form-border form-border-color z-1 transition-shadow addon-before:form-rounded-l-none outline-none addon-after:form-rounded-r-none form-p-input form-rounded focus:form-ring">
</div>
</div>
<div class="form-col w-full"></div>
</div>
</div>
</div>
(The TextElement
template changed in 1.2.0
, but it's easier to grasp the concept with this example.)
We have a bunch of utility classes assigned to different DOM elements, but we have no idea which component or class name they belong to and makes it hard to customize them.
To make development easier we've introduced class helpers in 1.2.0
which prepends descriptive names to the class lists.
Let's enable classHelpers
in vueform.config.js
:
// vueform.config.js
export default {
classHelpers: true,
// ...
}
And see how our source changes:
<div class="ElementLayout.container--> form-col ElementLayout.container_md--> w-full TextElement.container-->">
<div class="ElementLayout.outerWrapper--> form-row flex flex-wrap ElementLayout.outerWrapper_single--> ElementLayout.outerWrapper_single_md--> form-mb-gutter">
<label for="text" class="ElementLabel.container--> form-col pr-4 ElementLabel.container_md--> form-py-input-border w-3/12">
<span>Text</span>
</label>
<div class="ElementLayout.innerContainer--> flex-1 w-9/12">
<div class="ElementLayout.innerWrapperBefore--> form-col w-full"></div>
<div class="ElementLayout.innerWrapper--> form-col w-full">
<div class="TextElement.inputContainer--> w-full flex">
<input type="text" name="text" id="text" class="TextElement.input--> w-full form-border form-border-color z-1 transition-shadow addon-before:form-rounded-l-none outline-none addon-after:form-rounded-r-none TextElement.input_md--> form-p-input form-rounded TextElement.input_enabled--> focus:form-ring">
</div>
</div>
<div class="ElementLayout.innerWrapperAfter--> form-col w-full"></div>
</div>
</div>
</div>
Helper classes were prepended to our class lists which contain information about where the following classes are coming from.
The first part of a highlighted class name is the name of the component, the second is the name of the class.
With this knowledge we can identify which class comes from which component's class name and now we're ready to customize them.
Add Classes โ
To add classes to the element we can use addClass
option.
Let's say we want bold letters in our input field, so add font-bold
utility class to input
:
<TextElement name="text" label="Text" default="value" :add-class="{
input: 'font-bold'
}" />
Result:
The font-bold
class got appended to the class to TextElement.input
section and now it is rendered among classes:
<input type="text" name="text" id="text" class="TextElement.input--> w-full form-border
form-border-color z-1 transition-shadow addon-before:form-rounded-l-none outline-none
addon-after:form-rounded-r-none font-bold TextElement.input_md--> form-p-input
form-rounded TextElement.input_enabled--> focus:form-ring">
When adding classes we need to define them as string
or array
. If we want to use Vue style classes, object
(conditional) values must be wrapped in an array
.
If we pass a string
or an array
directly to addClass
instead of an object
it will be appended to the element's outermost DOM.
Remove Classes โ
If we want to remove a class from a class list we can use removeClass
option.
Let's remove the border related props from the TextElement
's input
class:
<TextElement name="text" label="Text" default="value" :remove-class="{
inputContainer: ['form-border-width-input', 'form-border-color-input']
}" />
Result:
The
form-border-width-input
andform-border-color-input
utility classes are defined by Vueform's Tailwind CSS plugin and they are responsible form setting border width and color based on Tailwind.
When removing classes we need to define the list of classes to be removed in an array
.
If we pass an array
directly to removeClass
instead of an object
it will be applied to the element's outermost DOM.
Replace Classes โ
We can also replace certain classes in a class list instead of removing and adding ones with replaceClass
property.
Let's change the original form input border with a thicker black one:
<TextElement name="text" label="Text" default="value" :replace-class="{
inputContainer: {
'form-border-width-input': 'border-2',
'form-border-color-input': ['border-black', 'border-opacity-80']
}
}" />
Result:
When replacing classes we must use an object, where the keys are the original class names and the values are the replacements. The keys can only be single classes, while values can contain multiple ones in string
or an array
. If we want to use Vue style classes, object
(conditional) values must be wrapped in an array
.
If we pass an object
in an array
directly to removeClass
instead of an object
it will be applied to the element's outermost DOM.
Override Classes โ
If we want to replace an element's class completely we can use overrideClass
options.
Let's get rid of the built-in form input looks and replace it with a simple underlined input field:
<TextElement name="text" label="Text" default="value" :override-class="{
inputContainer: 'border-b-2 bg-transparent w-full transition-all',
inputContainer_default: 'border-black',
inputContainer_focused: 'border-red-500',
inputContainer_md: 'h-10',
}" />
Result:
We changed multiple classes in order to get rid of border radius and focus ring as well.
When overriding classes we can use string
or array
values. If we want to use Vue style classes, object
(conditional) values must be wrapped in an array
.
If we pass an string
or an array
directly to overrideClass
instead of an object
it will override the element's outermost DOM's class.
Modify Multiple Components โ
We can modify multiple components used by the element at the same time. For example we can add font-bold
to TextElement
's input
and ElementLabel
's container
field as well:
<TextElement name="text" label="Text" default="value" :add-classes="{
TextElement: {
input: 'font-bold'
},
ElementLabel: {
container: 'font-bold'
}
}" />
Result:
We can modify multiple classes the same way with removeClasses
, replaceClasses
and overrideClasses
as well.
Replacing Templates โ
We can replace the template of any component with templates
option. To demonstrate that let's change the ElementError
component to a custom one.
Here's how ElementError
looks like by default:
First, let's check the ElementError.vue
template at @vueform/vueform/themes/blank/templates
:
<!-- From: @vueform/vueform/themes/blank/templates/ElementError.vue -->
<template>
<div v-if="error" :class="classes.container">{{ error }}</div>
</template>
The
blank
theme is the basis of all themes. It contains the actual<template>
part of the component, while the themes only add classes and styles to them.
Let's create a custom version from that, which wraps the error message in a <b>
tag and prefixes it with a *
:
<!-- ./path/to/CustomElementError.vue -->
<template>
<div v-if="error" :class="classes.container">
<b>* {{ error }}</b>
</div>
</template>
Next let's fill in the default classes:
<!-- ./path/to/CustomElementError.vue -->
<template>
<div v-if="error" :class="classes.container">
<b>* {{ error }}</b>
</div>
</template>
<script>
export default {
data: () => ({
defaultClasses: {
container: 'text-red-500 text-lg mt-1'
}
})
}
</script>
Now let's replace the ElementError
template with our custom one using templates
option:
<template>
<div id="app">
<Vueform>
<TextElement name="text" :templates="{ ElementError }" />
</Vueform>
</div>
</template>
<script>
import { markRaw } from 'vue'
import CustomElementError from './path/to/CustomElementError.vue'
export default {
data: () => ({
ElementError: markRaw(CustomElementError),
})
}
</script>
Result:
It's important to note that the template we created is not an actual component, only a component's template and therefore it should not be registered as a component. Instead it should be passed to the view using markRaw
which removes the unnecessary reactivity from its object.
Presets โ
If we want to apply a set of rules (eg. addClasses
, templates
, size
, etc.) to certain elements or forms we can use presets.
Presets can be created at vueform.config.js
under presets
property:
// vueform.config.js
export default {
presets: {
presetName: {
// preset options
}
}
}
We can add the same options to a preset that we'd normally add to an element, including:
size
- defines the default sizeviews
- an object containing key value pairs for component names and their default viewscolumns
- a columns objectaddClasses
- classes to addremoveClasses
- classes to removereplaceClasses
- classes to replaceoverrideClasses
- classes to overridetemplates
- templates to replace.
For example this would add an extra margin to each ElementLabel
component where the preset is applied:
// vueform.config.js
export default {
presets: {
italicLabel: {
addClasses: {
ElementLabel: {
container: 'italic'
}
}
}
}
}
Later we can use the presets for an element using presets
option:
<template>
<Vueform>
<TextElement label="Normal" ... />
<TextElement label="Italic" :presets="['italicLabel']" ... />
<Vueform>
</template>
... or even for forms:
<template>
<Vueform :presets="['italicLabel']">
<TextElement label="Italic" ... />
<TextElement label="Italic" ... />
<Vueform>
</template>
The presets will be applied in consecutive order before the element/form options. This means that if a preset eg. adds a class a class it will be added before classes added with element/form's addClasses
option.
Form Customization โ
Just like for presets, the following options can be also applied on a form level:
size
views
columns
addClass
addClasses
removeClass
removeClasses
replaceClass
replaceClasses
overrideClass
overrideClasses
presets
Options defined on form level will take effect before element presets and options. Eg:
<template>
<Vueform size="sm">
<TextElement ... />
<TextElement size="md" ... />
</Vueform>
</template>
Will result in:
Classes modified on a higher level can also be modified on a lower level:
<template>
<Vueform :add-classes="{
TextElement: {
input: 'font-bold'
}
}">
<TextElement ... />
<TextElement :replace-class="{
input: {
'font-bold': 'italic'
}
}" ... />
</Vueform>
</template>
Will result in:
Global Customization โ
We can also define the the options mentioned in this chapter globally in vueform.config.js
. In config we can use usePresets
option to apply presets globally, which will take place before any other setting, global or local.
CSS Vars โ
Vueform themes use CSS vars under the hood that makes theme customizations a breeze. The different themes (eg. default, Bootstrap, Material) are in fact identicaly structurally and are using the exact same class names, their only difference is how their CSS vars are configured.
To see the available theme check out Themes page.
Global Customization โ
If we'd like to override some of the CSS vars globally we can define the following in global scope:
*, :before, :after, :root {
--vf-border-color-input: #14b8a6;
--vf-bg-input: #ccfbf1;
}
Then all of our forms will be displayed using the global CSS vars setting:
<template>
<Vueform>
<TextElement ... /> <!-- Will be teal -->
<TextElement ... /> <!-- Will be teal -->
</Vueform>
</template>
Local Customization โ
We can also override CSS vars only for certain form instance or even element. To do this we need to add a class name to the form or element and apply the CSS vars for those scopes only:
.form-teal, .element-teal {
*, :before, :after, :root {
--vf-border-color-input: #14b8a6;
--vf-bg-input: #ccfbf1;
}
}
Now if we apply those classes to forms or elements only those will be altered:
<template>
<!-- Teal form -->
<Vueform add-class="form-teal">
<TextElement ... /> <!-- Will be teal -->
<TextElement ... /> <!-- Will be teal -->
</Vueform>
<!-- Teal element -->
<Vueform>
<TextElement ... /> <!-- Will be default -->
<TextElement add-class="element-teal" ... /> <!-- Will be teal -->
</Vueform>
</template>
Available CSS Vars โ
The following CSS vars are the default values for vueform
theme and they can be overriden globally or locally.
General Colors โ
Colors used globally in Vueform components.
// Colors
*, :before, :after, :root {
--vf-primary: #07bf9b;
--vf-primary-darker: #06ac8b;
--vf-danger: #ef4444;
--vf-danger-lighter: #fee2e2;
--vf-success: #10b981;
--vf-success-lighter: #d1fae5;
--vf-gray-50: #f9fafb;
--vf-gray-100: #f3f4f6;
--vf-gray-200: #e5e7eb;
--vf-gray-300: #d1d5db;
--vf-gray-400: #9ca3af;
--vf-gray-500: #6b7280;
--vf-gray-600: #4b5563;
--vf-gray-700: #374151;
--vf-gray-800: #1f2937;
--vf-gray-900: #111827;
}
Ring Settings โ
Rings appear as an outline when input elements are focused.
// Ring Settings
*, :before, :after, :root {
--vf-ring-width: 2px;
--vf-ring-color: #07bf9b66;
}
Link Settings โ
Links can be displayed with tag: 'a'
option with StaticElements.
// Link Settings
*, :before, :after, :root {
--vf-link-color: var(--vf-primary);
--vf-link-decoration: inherit;
}
Text Sizing โ
Texts sizes to use through components.
// Text Sizing
*, :before, :after, :root {
--vf-font-size: 1rem;
--vf-font-size-sm: 0.875rem;
--vf-font-size-lg: 1rem;
--vf-font-size-small: 0.875rem;
--vf-font-size-small-sm: 0.8125rem;
--vf-font-size-small-lg: 0.875rem;
--vf-font-size-h1: 2.125rem;
--vf-font-size-h1-sm: 2.125rem;
--vf-font-size-h1-lg: 2.125rem;
--vf-font-size-h2: 1.875rem;
--vf-font-size-h2-sm: 1.875rem;
--vf-font-size-h2-lg: 1.875rem;
--vf-font-size-h3: 1.5rem;
--vf-font-size-h3-sm: 1.5rem;
--vf-font-size-h3-lg: 1.5rem;
--vf-font-size-h4: 1.25rem;
--vf-font-size-h4-sm: 1.25rem;
--vf-font-size-h4-lg: 1.25rem;
--vf-font-size-h1-mobile: 1.5rem;
--vf-font-size-h1-mobile-sm: 1.5rem;
--vf-font-size-h1-mobile-lg: 1.5rem;
--vf-font-size-h2-mobile: 1.25rem;
--vf-font-size-h2-mobile-sm: 1.25rem;
--vf-font-size-h2-mobile-lg: 1.25rem;
--vf-font-size-h3-mobile: 1.125rem;
--vf-font-size-h3-mobile-sm: 1.125rem;
--vf-font-size-h3-mobile-lg: 1.125rem;
--vf-font-size-h4-mobile: 1rem;
--vf-font-size-h4-mobile-sm: 1rem;
--vf-font-size-h4-mobile-lg: 1rem;
--vf-font-size-blockquote: 1rem;
--vf-font-size-blockquote-sm: 0.875rem;
--vf-font-size-blockquote-lg: 1rem;
--vf-line-height: 1.5rem;
--vf-line-height-sm: 1.25rem;
--vf-line-height-lg: 1.5rem;
--vf-line-height-small: 1.25rem;
--vf-line-height-small-sm: 1.125rem;
--vf-line-height-small-lg: 1.25rem;
--vf-line-height-headings: 1.2;
--vf-line-height-headings-sm: 1.2;
--vf-line-height-headings-lg: 1.2;
--vf-line-height-blockquote: 1.5rem;
--vf-line-height-blockquote-sm: 1.25rem;
--vf-line-height-blockquote-lg: 1.5rem;
--vf-letter-spacing: 0;
--vf-letter-spacing-sm: 0;
--vf-letter-spacing-lg: 0;
--vf-letter-spacing-small: 0;
--vf-letter-spacing-small-sm: 0;
--vf-letter-spacing-small-lg: 0;
--vf-letter-spacing-headings: 0;
--vf-letter-spacing-headings-sm: 0;
--vf-letter-spacing-headings-lg: 0;
--vf-letter-spacing-blockquote: 0;
--vf-letter-spacing-blockquote-sm: 0;
--vf-letter-spacing-blockquote-lg: 0;
}
Gutter โ
The space between elements vertically and horizontally.
// Gutter
*, :before, :after, :root {
--vf-gutter: 1rem;
--vf-gutter-sm: 0.5rem;
--vf-gutter-lg: 1rem;
}
Input Spacing โ
Spacing within input fields.
// Input Spacing
*, :before, :after, :root {
--vf-min-height-input: 2.375rem;
--vf-min-height-input-sm: 2.125rem;
--vf-min-height-input-lg: 2.875rem;
--vf-py-input: 0.375rem;
--vf-py-input-sm: 0.375rem;
--vf-py-input-lg: 0.625rem;
--vf-px-input: 0.75rem;
--vf-px-input-sm: 0.5rem;
--vf-px-input-lg: 0.875rem;
--vf-py-btn: 0.375rem;
--vf-py-btn-sm: 0.375rem;
--vf-py-btn-lg: 0.625rem;
--vf-px-btn: 0.875rem;
--vf-px-btn-sm: 0.75rem;
--vf-px-btn-lg: 1.25rem;
--vf-py-btn-small: 0.25rem;
--vf-py-btn-small-sm: 0.25rem;
--vf-py-btn-small-lg: 0.375rem;
--vf-px-btn-small: 0.625rem;
--vf-px-btn-small-sm: 0.625rem;
--vf-px-btn-small-lg: 0.75rem;
--vf-py-group-tabs: var(--vf-py-input);
--vf-py-group-tabs-sm: var(--vf-py-input-sm);
--vf-py-group-tabs-lg: var(--vf-py-input-lg);
--vf-px-group-tabs: var(--vf-px-input);
--vf-px-group-tabs-sm: var(--vf-px-input-sm);
--vf-px-group-tabs-lg: var(--vf-px-input-lg);
--vf-py-group-blocks: 0.75rem;
--vf-py-group-blocks-sm: 0.625rem;
--vf-py-group-blocks-lg: 0.875rem;
--vf-px-group-blocks: 1rem;
--vf-px-group-blocks-sm: 1rem;
--vf-px-group-blocks-lg: 1rem;
--vf-py-tag: 0;
--vf-py-tag-sm: var(--vf-py-tag);
--vf-py-tag-lg: var(--vf-py-tag);
--vf-px-tag: 0.4375rem;
--vf-px-tag-sm: var(--vf-px-tag);
--vf-px-tag-lg: var(--vf-px-tag);
--vf-py-slider-tooltip: 0.125rem;
--vf-py-slider-tooltip-sm: 0.0625rem;
--vf-py-slider-tooltip-lg: 0.1875rem;
--vf-px-slider-tooltip: 0.375rem;
--vf-px-slider-tooltip-sm: 0.3125rem;
--vf-px-slider-tooltip-lg: 0.5rem;
--vf-py-blockquote: 0.25rem;
--vf-py-blockquote-sm: 0.25rem;
--vf-py-blockquote-lg: 0.25rem;
--vf-px-blockquote: 0.75rem;
--vf-px-blockquote-sm: 0.75rem;
--vf-px-blockquote-lg: 0.75rem;
--vf-py-hr: 0.25rem;
// Space between addon and text input
--vf-space-addon: 0;
--vf-space-addon-sm: var(--vf-space-addon);
--vf-space-addon-lg: var(--vf-space-addon);
// Space between checkboxes & radios and their labels
--vf-space-checkbox: 0.375rem;
--vf-space-checkbox-sm: var(--vf-space-checkbox);
--vf-space-checkbox-lg: var(--vf-space-checkbox);
// Space between tags in `TagsElement`
--vf-space-tags: 0.1875rem;
--vf-space-tags-sm: var(--vf-space-tags);
--vf-space-tags-lg: var(--vf-space-tags);
// Space between the field's top and floating label
--vf-floating-top: 0rem;
--vf-floating-top-sm: 0rem;
--vf-floating-top-lg: 0.6875rem;
// Space above `StaticElement` tags
--vf-space-static-tag-1: 1rem;
--vf-space-static-tag-2: 2rem;
--vf-space-static-tag-3: 3rem;
}
Input Colors โ
Background, text, shadow and border color of inputs.
// Input Colors
*, :before, :after, :root {
--vf-bg-input: #ffffff;
--vf-bg-input-hover: var(--vf-bg-input);
--vf-bg-input-focus: var(--vf-bg-input);
--vf-bg-input-danger: var(--vf-bg-input);
--vf-bg-input-success: var(--vf-bg-input);
--vf-bg-disabled: var(--vf-gray-200);
--vf-bg-selected: rgba(17,24,39,0.05); // Used eg. when select option is hovered or a checkbox is selected in `blocks` view
--vf-bg-passive: var(--vf-gray-300); // Used as a background color for eg. slider, toggle
--vf-bg-icon: var(--vf-gray-500);
--vf-bg-danger: var(--vf-danger-lighter);
--vf-bg-success: var(--vf-success-lighter);
--vf-bg-tag: var(--vf-primary);
--vf-bg-slider-handle: var(--vf-primary);
--vf-bg-toggle-handle: #ffffff;
--vf-bg-date-head: var(--vf-gray-100);
--vf-bg-addon: transparent;
--vf-bg-btn: var(--vf-primary);
--vf-bg-btn-danger: var(--vf-danger);
--vf-bg-btn-secondary: var(--vf-gray-200);
--vf-color-input: var(--vf-gray-800);
--vf-color-input-hover: var(--vf-color-input);
--vf-color-input-focus: var(--vf-color-input);
--vf-color-input-danger: var(--vf-color-input);
--vf-color-input-success: var(--vf-color-input);
--vf-color-disabled: var(--vf-gray-400);
--vf-color-placeholder: var(--vf-gray-300);
--vf-color-passive: var(--vf-gray-700); // Used when text is displayed on passive background eg. `off` toggle
--vf-color-muted: var(--vf-gray-500); // Used for helper texts eg. element description, floating label
--vf-color-floating: var(--vf-color-muted);
--vf-color-floating-focus: var(--vf-color-floating); // Used when the input is focused
--vf-color-floating-success: var(--vf-color-floating); // Used when the input is filled with success
--vf-color-floating-danger: var(--vf-color-floating); // Used when the input has error
--vf-color-on-primary: #ffffff; // Used when text is displayed on primary color
--vf-color-danger: var(--vf-danger);
--vf-color-success: var(--vf-success);
--vf-color-tag: var(--vf-color-on-primary);
--vf-color-addon: var(--vf-color-input);
--vf-color-date-head: var(--vf-gray-700);
--vf-color-btn: var(--vf-color-on-primary);
--vf-color-btn-danger: #ffffff;
--vf-color-btn-secondary: var(--vf-gray-700);
--vf-border-color-input: var(--vf-gray-300);
--vf-border-color-input-hover: var(--vf-border-color-input);
--vf-border-color-input-focus: var(--vf-primary);
--vf-border-color-input-danger: var(--vf-border-color-input);
--vf-border-color-input-success: var(--vf-border-color-input);
--vf-border-color-checked: var(--vf-primary);
--vf-border-color-passive: var(--vf-gray-300); // Used as a border for passive states eg. `off` toggle
--vf-border-color-slider-tooltip: var(--vf-primary);
--vf-border-color-tag: var(--vf-primary);
--vf-border-color-btn: var(--vf-primary);
--vf-border-color-btn-danger: var(--vf-danger);
--vf-border-color-btn-secondary: var(--vf-gray-200);
--vf-border-color-blockquote: var(--vf-gray-300);
--vf-border-color-hr: var(--vf-gray-300);
}
Shadows โ
Shadows for different component parts.
// Shadows
*, :before, :after, :root {
--vf-shadow-input: 0px 0px 0px 0px rgba(0,0,0,0);
--vf-shadow-input-hover: 0px 0px 0px 0px rgba(0,0,0,0);
--vf-shadow-input-focus: 0px 0px 0px 0px rgba(0,0,0,0);
--vf-shadow-handles: 0px 0px 0px 0px rgba(0,0,0,0);
--vf-shadow-handles-hover: 0px 0px 0px 0px rgba(0,0,0,0);
--vf-shadow-handles-focus: 0px 0px 0px 0px rgba(0,0,0,0);
--vf-shadow-btn: 0px 0px 0px 0px rgba(0,0,0,0);
--vf-shadow-dropdown: 0px 0px 0px 0px rgba(0,0,0,0);
}
Input Borders โ
The widths and radiuses of inputs.
// Input borders
*, :before, :after, :root {
--vf-border-width-input-t: 1px;
--vf-border-width-input-r: 1px;
--vf-border-width-input-b: 1px;
--vf-border-width-input-l: 1px;
--vf-border-width-radio-t: var(--vf-border-width-input-t);
--vf-border-width-radio-r: var(--vf-border-width-input-r);
--vf-border-width-radio-b: var(--vf-border-width-input-b);
--vf-border-width-radio-l: var(--vf-border-width-input-l);
--vf-border-width-checkbox-t: var(--vf-border-width-input-t);
--vf-border-width-checkbox-r: var(--vf-border-width-input-r);
--vf-border-width-checkbox-b: var(--vf-border-width-input-b);
--vf-border-width-checkbox-l: var(--vf-border-width-input-l);
--vf-border-width-dropdown: 1px;
--vf-border-width-btn: 1px;
--vf-border-width-toggle: 0.125rem;
--vf-border-width-tag: 1px;
--vf-border-width-blockquote: 3px;
--vf-radius-input: 0.25rem;
--vf-radius-input-sm: var(--vf-radius-input);
--vf-radius-input-lg: var(--vf-radius-input);
--vf-radius-btn: var(--vf-radius-input);
--vf-radius-btn-sm: var(--vf-radius-input-sm);
--vf-radius-btn-lg: var(--vf-radius-input);
// Used for eg. list button, slider tooltip, info tooltip
--vf-radius-small: var(--vf-radius-input);
--vf-radius-small-sm: var(--vf-radius-input-sm);
--vf-radius-small-lg: var(--vf-radius-input);
// Used for larger inputs eg. textarea, editor, drag and drop, checkbox/radio blocks
--vf-radius-large: var(--vf-radius-input);
--vf-radius-large-sm: var(--vf-radius-input-sm);
--vf-radius-large-lg: var(--vf-radius-input);
--vf-radius-tag: var(--vf-radius-input);
--vf-radius-tag-sm: var(--vf-radius-input-sm);
--vf-radius-tag-lg: var(--vf-radius-input);
--vf-radius-checkbox: var(--vf-radius-input);
--vf-radius-checkbox-sm: var(--vf-radius-input-sm);
--vf-radius-checkbox-lg: var(--vf-radius-input);
--vf-radius-slider: var(--vf-radius-input);
--vf-radius-slider-sm: var(--vf-radius-input-sm);
--vf-radius-slider-lg: var(--vf-radius-input);
--vf-radius-image: var(--vf-radius-input);
--vf-radius-image-sm: var(--vf-radius-input-sm);
--vf-radius-image-lg: var(--vf-radius-input);
--vf-radius-gallery: var(--vf-radius-input);
--vf-radius-gallery-sm: var(--vf-radius-input-sm);
--vf-radius-gallery-lg: var(--vf-radius-input);
}
Input Sizes โ
Sizes of different input fields and their components.
// Input Sizes
*, :before, :after, :root {
--vf-checkbox-size: 1rem;
--vf-checkbox-size-sm: 0.875rem;
--vf-checkbox-size-lg: 1rem;
--vf-gallery-size: 6rem;
--vf-gallery-size-sm: 5rem;
--vf-gallery-size-lg: 7rem;
--vf-toggle-width: 3rem;
--vf-toggle-width-sm: 2.75rem;
--vf-toggle-width-lg: 3rem;
--vf-toggle-height: 1.25rem;
--vf-toggle-height-sm: 1.125rem;
--vf-toggle-height-lg: 1.25rem;
--vf-slider-height: 0.375rem;
--vf-slider-height-sm: 0.3125rem;
--vf-slider-height-lg: 0.5rem;
--vf-slider-height-vertical: 20rem;
--vf-slider-height-vertical-sm: var(--vf-slider-height-vertical);
--vf-slider-height-vertical-lg: var(--vf-slider-height-vertical);
--vf-slider-handle-size: 1rem;
--vf-slider-handle-size-sm: 0.875rem;
--vf-slider-handle-size-lg: 1.25rem;
--vf-slider-tooltip-distance: 0.5rem;
--vf-slider-tooltip-distance-sm: 0.375rem;
--vf-slider-tooltip-distance-lg: 0.5rem;
--vf-slider-tooltip-arrow-size: 0.3125rem;
--vf-slider-tooltip-arrow-size-sm: var(--vf-slider-tooltip-arrow-size);
--vf-slider-tooltip-arrow-size-lg: var(--vf-slider-tooltip-arrow-size);
}
Themes โ
Vueform has a couple of themes tailored to popular CSS frameworks. Themes can be installed out of the box and furhter configured with CSS vars.
Check out available themes.
Tailwind Prefix โ
If we want to use Tailwind Prefix we need to make some changes to our theme setup.
First we need to import prefix
instead of default
from Vueform's tailwind
or tailwind-material
theme and use it as a function with the prefix we want to use:
// vueform.config.js
import { prefix as tailwind } from '@vueform/vueform/themes/tailwind'
export default {
theme: tailwind('tw-'),
// ...
}
After that we need set prefix
option in tailwind.config.js
:
// tailwind.config.js
module.exports = {
// ...
prefix: 'tw-',
plugins: [
require('@vueform/vueform/tailwind')
]
}
Tailwind 2.x โ
In Tailwind CSS 2.x we need to import @vueform/vueform/tailwind-prefixer
and add is as a transform
in purge
block:
// tailwind.config.js
const prefixer = require('@vueform/vueform/tailwind-prefixer')
module.exports = {
purge: {
// ...
content: [
// ... your existing
'./vueform.config.js', // or where `vueform.config.js` is located
'./node_modules/@vueform/vueform/themes/tailwind/**/*.vue',
'./node_modules/@vueform/vueform/themes/tailwind/**/*.js',
],
transform: {
js: (content) => {
return prefixer(content, 'tw-')
}
}
},
prefix: 'tw-',
plugins: [
require('@vueform/vueform/tailwind')
]
}
Tailwind 3.x โ
In Tailwind CSS 3.x we need to import @vueform/vueform/tailwind-prefixer
adn change the content
block to an object and add files
and transform
with the prefixer:
// tailwind.config.js
const prefixer = require('@vueform/vueform/tailwind-prefixer')
module.exports = {
content: {
files: [
// ... your existing
'./vueform.config.js', // or where `vueform.config.js` is located
'./node_modules/@vueform/vueform/themes/tailwind/**/*.vue',
'./node_modules/@vueform/vueform/themes/tailwind/**/*.js',
],
transform: {
js: (content) => {
return prefixer(content, 'tw-')
}
}
},
prefix: 'tw-',
plugins: [
require('@vueform/vueform/tailwind')
]
}
Screen Sizes โ
If we are using different screen sizes than the default ones (sm
, md
, lg
, xl
, 2xl
) we can pass those as a third argument to prefixer()
:
js: (content) => {
return prefixer(
content,
'tw-',
['xs', 'sm', 'md', 'lg', 'xl', 'xxl']
)
}