Configure your theme
How to create and configure your own and unique theme and the different variants.
Format
To apply a custom theme you should play with the classes
, fixedClasses
, and variants
props.
The classes
and fixedClasses
props usually expects an string
with a CSS class (but accepts any of the formats accepted by Vue) for single-tag components (inputs, button, etc.) and an object
for more complex components (modals, datepicker, etc) (see component docs for details).
The variants
prop expects an object where the keys represent the variant name and the key values the classes that will be used when that variant is applied.
Example for a single-tag component:
import Vue from 'vue'
import VueTailwind from 'vue-tailwind'
import TButton from 'vue-tailwind/dist/t-button'
const settings = {
't-button': {
component: TButton,
props: {
// The fixed classes will never change and will be merged with the `classes` value or the active variant
fixedClasses: 'focus:outline-none focus:shadow-outline inline-flex items-center transition ease-in-out duration-150',
// Classes used when any variant is active
classes: 'text-white bg-blue-600 hover:bg-blue-500 focus:border-blue-700 active:bg-blue-700 text-sm font-medium border border-transparent px-3 py-2 rounded-md',
variants: {
// A red variant of the button (applied when `<t-button variant="error" />`)
error: 'text-white bg-red-600 hover:bg-red-500 focus:border-red-700 active:bg-red-700 text-sm font-medium border border-transparent px-3 py-2 rounded-md',
// A green variant of the button (applied when `<t-button variant="success" />`)
success: 'text-white bg-green-600 hover:bg-green-500 focus:border-green-700 active:bg-green-700 text-sm font-medium border border-transparent px-3 py-2 rounded-md',
// ...unlimited variants
}
// ...More settings
}
},
}
Vue.use(VueTailwind, settings)
Example for a complex component:
import Vue from 'vue'
import VueTailwind from 'vue-tailwind'
import TAlert from 'vue-tailwind/dist/t-alert'
const settings = {
't-alert': {
component: TAlert,
props: {
// The fixed classes will never change and will be merged with the `classes` value or the active variant
fixedClasses: {
wrapper: 'rounded p-4 flex text-sm border-l-4',
body: 'flex-grow',
close: 'ml-4 rounded',
closeIcon: 'h-5 w-5 fill-current'
},
classes: {
wrapper: 'bg-blue-100 border-blue-500',
body: 'text-blue-700',
close: 'text-blue-700 hover:text-blue-500 hover:bg-blue-200',
closeIcon: 'h-5 w-5 fill-current'
},
variants: {
danger: {
wrapper: 'bg-red-100 border-red-500',
body: 'text-red-700',
close: 'text-red-700 hover:text-red-500 hover:bg-red-200'
// Notice that I am not defining the `closeIcon` class since we only
// need to write the classes we want to override
},
}
}
},
}
Vue.use(VueTailwind, settings)
Variants
The variants are the heart of the theming system you can create as many variants as you need, the variants expect the same format as the single classes
attribute grouped by your custom variant name:
Single tag elements
const settings = {
't-input': {
component: TInput,
props: {
classes: 'border-2 block w-full rounded text-gray-800 p-3',
variants: {
error: 'border-2 block w-full rounded text-red-500 border-red-500 p-3'
inmenu: 'border-transparent bg-gray-500 text-gray-900 p-2'
funny: '...',
// ...etc
}
}
},
// ...etc
}
Multiple tag elements
const settings = {
't-alert': {
component: TAlert,
props: {
classes: {
wrapper: 'rounded bg-blue-100 p-4 flex text-sm border-l-4 border-blue-500',
body: 'flex-grow text-blue-700',
close: 'text-blue-700 hover:text-blue-500 hover:bg-blue-200 ml-4 rounded',
closeIcon: 'h-5 w-5 fill-current'
},
variants: {
error: {
wrapper: 'rounded bg-red-100 p-4 flex text-sm border-l-4 border-red-500',
body: 'flex-grow text-red-700',
close: 'text-red-700 hover:text-red-500 hover:bg-red-200 ml-4 rounded',
closeIcon: 'h-5 w-5 fill-current'
},
big: {
// Hey! you only need to override the parts you
// want to change from the default classes
body: 'text-2xl font-bold flex-grow text-blue-700',
}
}
}
},
// ....
}
Fixed classes
When you create a theme, especially one with many variants, you will notice that you will start dealing with many repeated classes that all the variants have in common.
To make it easier to handle those classes, you can use the fixedClasses
prop. The classes defined on this prop will be merged into the currently active class of the component.
For example, let's say that you need all your buttons to have a small transition-related class, and you know all of them will have a shadow. Consider these two alternatives:
const settings = {
't-button': {
component: TButton,
props: {
classes: 'transform ease-in-out duration-100 shadow bg-orange-500,
variants: {
secondary: 'transform ease-in-out duration-100 shadow underline text-orange-500',
// ...
}
}
},
't-alert': {
component: TAlert,
props: {
classes: {
wrapper: 'rounded flex flex text-sm w-full border-l-4 p-4 bg-blue-100 border-blue-700',
body: 'flex-grow text-blue-700',
close: 'p-2 ml-4 rounded text-blue-700 hover:text-blue-600',
closeIcon: 'h-5 w-5 fill-current'
},
variants: {
error: {
wrapper: 'rounded flex flex text-sm w-full border-l-4 p-4 bg-red-100 border-red-700',
body: 'flex-grow text-red-700',
close: 'p-2 ml-4 rounded text-red-700 hover:text-red-600',
closeIcon: 'h-5 w-5 fill-current'
},
// ...
}
}
},
}
const settings = {
't-button': {
component: TButton,
props: {
fixedClasses: 'transform ease-in-out duration-100 shadow',
classes: 'bg-orange-500,
variants: {
secondary: 'underline text-orange-500',
// ...
}
}
},
't-alert': {
component: TAlert,
props: {
fixedClasses: {
wrapper: 'rounded flex flex text-sm w-full border-l-4 p-4',
body: 'flex-grow ',
close: 'p-2 ml-4 rounded',
closeIcon: 'h-5 w-5 fill-current '
},
classes: {
wrapper: 'bg-blue-100 border-blue-700',
body: 'text-blue-700',
close: 'text-blue-700 hover:text-blue-600',
closeIcon: ''
},
variants: {
error: {
wrapper: 'bg-red-100 border-red-700',
body: 'text-red-700',
close: 'text-red-700 hover:text-red-600',
},
// ...
}
}
},
// ....
}
Vue.use(VueTailwind, settings)
For the examples above, with both alternatives, the results will look like this:
<!-- <t-button>Press me</tbutton> -->
<button class="duration-100 ease-in-out transform bg-orange-500 shadow">Press me</button>
<!-- <t-alert show>Hello world</talert> -->
<div class="flex w-full p-4 text-sm bg-blue-100 border-l-4 border-blue-700 rounded">
<div class="flex-grow text-blue-700">
Hello world
</div>
<button type="button" class="p-2 ml-4 text-blue-700 rounded hover:text-blue-600">
<svg class="w-5 h-5 fill-current">...</svg>
</button>
</div>
<!-- <t-button variant="secondary">Press me</tbutton> -->
<button class="text-orange-500 underline duration-100 ease-in-out transform shadow">Press me</button>
<!-- <t-alert variant="error" show>Hello world</talert> -->
<div class="flex w-full p-4 text-sm bg-red-100 border-l-4 border-red-700 rounded">
<div class="flex-grow text-red-700">
Hello world
</div>
<button type="button" class="p-2 ml-4 text-red-700 rounded hover:text-red-600">
<svg class="w-5 h-5 fill-current">...</svg>
</button>
</div>
Wrapped inputs
Customize the select
, radio
and checkbox
components may be a bit tricky, you may need to create some custom CSS properties or use something like the tailwind form plugin (that basically do that for you).
As an alternative solution to give you more flexibility, those components include a wrapped
prop/setting that, when set it will wrap the HTML tags on a DIV
. The specific case of the select
tag will also add a custom SVG icon that can replace the default chevron.
To be more illustrative see how the select is rendered with and without the wrapped
prop:
Without wrapped
setting (default):
<t-select />
<!-- (or <t-select :wrapped="false" />) -->
<!-- Renders: -->
<select></select>
Wrapped:
<t-select wrapped />
<!-- (or <t-select :wrapped="true" />) -->
<!-- Renders: -->
<div>
<select></select>
<span>
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path clip-rule="evenodd" fill-rule="evenodd" d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"></path>
</svg>
</span>
</div>
Configure
You can define the component as wrapped
when you install the plugin or by using the component prop:
Global configuration:
const settings = {
't-select': {
component: TSelect,
props: {
wrapped: true,
// classes, variants, etc...
}
},
// ...
}
Vue.use(VueTailwind, settings)
Use the prop
<t-select wrapped />
Classes for wrapped elements
When the components are wrapped the format expected for the variants
, classes
, and fixedClasses
settings also change.
Instead of simple class, it now expects an object of classes for every HTML tag inside the component (like the multiple tags components)
Consider the example of the t-select
component:
Not wrapped:
const settings = {
't-select': {
component: TSelect,
props: {
classes: 'bg-white focus:outline-none focus:shadow-outline border border-gray-300 rounded py-2 px-4 block w-full leading-normal'
// Variants and fixed classes in the same `single` format ...
}
},
// ...
}
Vue.use(VueTailwind, settings)
Wrapped:
const settings = {
't-select': {
component: TSelect,
props: {
wrapped: true,
classes: {
wrapper: 'relative',
input: 'appearance-none bg-white focus:outline-none focus:shadow-outline border border-gray-300 rounded py-2 px-4 block w-full leading-normal bg-none',
arrowWrapper: 'pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700',
arrow: 'fill-current h-4 w-4',
}
// Variants and fixed classes in the same `object` format ...
}
},
// ...
}
Vue.use(VueTailwind, settings)
By using the settings above this is how the select will look like:
Use component props
In some cases, you will need to define the theme of a specific component directly, maybe the theme is determined by the user, is dynamic for some reason, is more practical, or is just a personal preference.
All the components come with the variants
, classes
, and fixedClasses
props that expect the value in the same format as the theme.
Take a look at the following examples:
Single tag element
<t-input
classes="border-2 block w-full rounded text-gray-800 p-3"
:variants="{
error: 'border-2 block w-full rounded text-red-500 border-red-500 p-3'
inmenu: 'border-transparent bg-gray-500 text-gray-900 p-2'
funny: '...',
}"
variant="funny"
/>
Multiple tag element
<t-alert
:classes="{
wrapper: 'rounded bg-blue-100 p-4 flex text-sm border-l-4 border-blue-500',
body: 'flex-grow text-blue-700',
close: 'text-blue-700 hover:text-blue-500 hover:bg-blue-200 ml-4 rounded',
closeIcon: 'h-5 w-5 fill-current'
}"
:variants="{
error: {
wrapper: 'rounded bg-red-100 p-4 flex text-sm border-l-4 border-red-500',
body: 'flex-grow text-red-700',
close: 'text-red-700 hover:text-red-500 hover:bg-red-200 ml-4 rounded',
},
big: {
body: 'text-2xl font-bold flex-grow text-blue-700',
}
}"
variant="error"
/>