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 both label and wrapper
  • label contains the label
  • wrapper 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 prefixMinimum widthCSS
sm640px@media (min-width: 640px) { ... }
md768px@media (min-width: 768px) { ... }
lg1024px@media (min-width: 1024px) { ... }
xl1280px@media (min-width: 1280px) { ... }
2xl1536px@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 in data()
  • <style> if the component is using named classes and not utility classes.

Themes also include a classes.js file that overrides any defaultClasses defined by the templates, which is useful if we want to centralize class definitions. Also, the <template> part is provided by a theme called blank 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 enabled
  • input_disabled - added when the input is disabled
  • input_sm - added when the input's size is sm
  • input_md - added when the input's size is md
  • input_lg - added when the input's size is lg.

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 and form-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 size
  • views - an object containing key value pairs for component names and their default views
  • columns - a columns object
  • addClasses - classes to add
  • removeClasses - classes to remove
  • replaceClasses - classes to replace
  • overrideClasses - classes to override
  • templates - 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:

<Vueform>
  <TextElement label="Normal" ... />
  <TextElement label="Italic" :presets="['italicLabel']" ... />
<Vueform>

... or even for forms:

<Vueform :presets="['italicLabel']">
  <TextElement label="Italic" ... />
  <TextElement label="Italic"  ... />
<Vueform>

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:

<Vueform size="sm">
  <TextElement ... />
  <TextElement size="md" ... />
</Vueform>

Will result in:

Classes modified on a higher level can also be modified on a lower level:

<Vueform :add-classes="{
  TextElement: {
    input: 'font-bold'
  }
}">
  <TextElement ... />
  <TextElement :replace-class="{
    input: {
      'font-bold': 'italic'
    }
  }" ... />
</Vueform>

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:

<Vueform>
  <TextElement ... /> <!-- Will be teal -->
  <TextElement ... /> <!-- Will be teal -->
</Vueform>

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:

<!-- 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>

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

--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

--vf-ring-width: 2px;
--vf-ring-color: #07bf9b66;

Text Sizing

Texts sizes to use through components.

// Text Sizing

--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-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-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;

Gutter

The space between elements vertically and horizontally.

// Gutter

--vf-gutter: 1rem;
--vf-gutter-sm: 0.5rem;
--vf-gutter-lg: 1rem;

Input Spacing

Spacing within input fields.

// Input Spacing

--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;

// 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;

Input Colors

Background, text, shadow and border color of inputs.

// Input Colors

--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);

Shadows

Shadows for different component parts.

// Shadows

--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

--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-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

--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'])
}
👋 Hire Vueform team for form customizations and development Learn more