You can get the classes by passing a mock component to the rootClasses
function, which is exported by the theme file (typically formkit.theme.[js|ts]
):
import { rootClasses } from "./formkit.theme" // <---- adjust to location of your theme file
const mockButton = { props: { family: 'button', type: 'button' } } as unknown as FormKitNode // remove the `as ...` if not typescript
const buttonClasses = rootClasses('input', mockButton)
This is how Forkit gets the classes, except that it uses the actual nodes. The returned object looks like this:
{
"appearance-none": true,
"[color-scheme:light]": true,
...
}
You can use it directly in the :class
prop or turn it into a list of strings when necessary:
const classString = Object.keys(buttonClasses).filter(key => buttonClasses[key]).join(' ')
You can also inject the rootClasses
through the formkit config:
import { configSymbol } from "@formkit/vue";
const config = inject(configSymbol)
config?.rootClasses(...)
But you'll probably want to keep the formkit element structure, as it impacts appearance. The straight-forward approach would be to override the element of a FormKit button using sections-schema:
<FormKit
type="button"
label="My Link"
:sections-schema="{
input: { $el: 'a' },
}"
href="..."
/>
Now an <a>
is rendered instead of a <button>
. This works without fumbling around with the rootClasses
, but not with components like RouterLink, and it will also put the button attributes (like type="button"
) on the anchor, and you'll have to add the :section-schema
prop on every link button.
To use a component, you can define a custom input, where you set your own template and register it with formkit. When setting family: button
, most (but annoyingly not all) button classes are inherited. Here is an example:
// formkit.config.ts
import { defaultConfig } from "@formkit/vue";
import { rootClasses } from "./formkit.theme";
import { createInput } from '@formkit/vue'
const buttonFamilyLink = createInput({
$cmp: 'RouterLink', // render a component
props: {
class: '$classes.input', // use the classes for the 'input' section
},
children: '$text', // put content of `text` prop into link
bind: '$attrs', // inherit attributes (like href, target, etc.)
}, {
family: 'button', // inherit button styles
props: ['text'], // register new `text` prop on FormKit component
})
export default defaultConfig({
config: {
rootClasses,
},
inputs: {
buttonFamilyLink // register new input
}
});
Now you can use it through the FormKit
component:
<FormKit
type="buttonFamilyLink"
to="..."
text="My Link"
/>
Internally, formkit passes the component to rootClasses
, which uses the family
and type
props to resolve the classes (you can explore this in your template file). But since type
is not "button"
anymore, those classes (for background and hover) are missing.
Still, this is probably the "cleanest" approach, i.e. without using rootClasses
, but it needs manual adjustment with the missing classes.
To get all button
classes, you have to apply them manually, using rootClasses
as described in the beginning. Here is an example with RouterLink
:
const routerLink = createInput({
props: {
ctx: '$node.context', // pass node context to inner component
rootClasses: '$node.config.rootClasses', // rootClasses is also available on the node
},
$cmp: {
props: ['ctx', 'rootClasses'],
setup(props) {
const linkProps = {
...props.ctx.attrs,
class: props.rootClasses('input', mockButton), // set the classes retrieved from `rootClasses`
}
const children = props.ctx.text
return () => h(RouterLink, linkProps, children)
}
},
}, {
props: ['text'] // register new `text` prop on FormKit component
})
This can be registered and used as above.
Here is a sandbox with the examples. Hope it helps!
onclick
event listener, and you can trigger navigation programmatically..formkit-${sectionName}
classes in addition to the Tailwind classes if you're using theRegenesis
theme from themes.formkit.com. also, from any node'scontext
object you can get theclasses
object and apply them how you'd like. soclasses.outer
will be that node'souter
section class list.