Formkit
FormKit is a powerful Vue 3 form framework that handles form structure, generation, validation, theming, submission, and error handling. Our design system leverages FormKit to deliver styled, configurable forms that align with our design principles and ensure a consistent user experience.
FormKit integrates seamlessly with our design system, providing a set of pre-styled form components that are easy to configure and customise. It supports a wide range of form controls, both basic and advanced, and allows for fine-grained control over theming and validation through its flexible configuration options.
Theming
FormKit ships with its own lightweight "Genesis" theme. Our design system extends this theme, ensuring that all FormKit variables reference corresponding design system variables.
This integration allows for a consistent look and feel across all form elements.
To use FormKit with our design system, a small CSS file is provided in the npm package: dist/components/formkit/formkit.css. This file contains the necessary styles to align FormKit components with our design system.
Configuration
To integrate FormKit with the design system, some configuration steps are necessary. This includes setting up the necessary styles, icons, and component classes.
Example Configuration:
/* FormKit imports */
import { generateClasses } from "@formkit/themes";
import { plugin, defaultConfig } from "@formkit/vue";
/* Design system icons - use named imports to keep your bundle size light */
import {
iconCheckmark,
iconRadio,
iconSearch,
} from "@coloplast/design-system/dist/exports/icons";
/* Design system styles - These can be imported globally or within components */
import "@coloplast/design-system/dist/components/formkit/formkit.css";
/* Create your Vue app and then add the FormKit plugin and configuration */
app.use(
plugin,
defaultConfig({
/* Register the design system icons to be used in FormKit inputs */
icons: {
search: iconSearch,
checkboxDecorator: iconCheckmark,
radioDecorator: iconRadio,
},
config: {
classes: generateClasses({
/* Reset button and submit styles, applying design system button classes instead */
button:
{ input: "$reset ds-button ds-button--primary" },
submit: { input: "$reset ds-button ds-button--primary" },
}),
},
}),
);
Required vs Optional Fields
When designing forms, it's important to clearly indicate which fields are required and which are optional. The design system’s recommendation is:
- Indicate optional fields when the majority of fields are required (most common scenario).
- Indicate required fields when the majority of fields are optional.
FormKit does not natively support required/optional field labels out of the box, but this functionality can be added through additional configuration.
Example configuration for required/optional labels:
/* Check if the node is a checkbox/radio node */
const isCheckboxAndRadioMultiple =
(node) =>
(node.props.type === "checkbox" || node.props.type === "radio") &&
node.props.options;
/* Update the schema to reflect the label change */
const updateNodeSchema = (
node,
requiredMode,
requiredLabel,
) => {
const schemaFn = node.props.definition.schema;
node.props.definition.schema = (sectionsSchema = {}) => {
const isRequired = node.props.parsedRules.some(
(rule) => rule.name === "required",
);
if (isRequired === (requiredMode === "required")) {
const schemaKey = isCheckboxAndRadioMultiple(node) ? "legend" : "label";
sectionsSchema[schemaKey] = {
children: ["$label", ` (${requiredLabel})`],
};
}
return schemaFn(sectionsSchema);
};
};
/* Create a plugin to handle the required/optional logic */
const addRequiredModePlugin =
(node) => {
const root = node.at("$root");
/* Pull data attribute values from the root node */
const requiredMode =
root.props.attrs["data-required-mode"];
const requiredLabel = root.props.attrs["data-required-label"];
/* Early return if we can't find the required mode data attributes */
if (
!requiredLabel?.length ||
!["optional", "required"].includes(requiredMode)
)
return;
/* Update the schema with the mode and label options */
node.on(
"created",
() => updateNodeSchema(node, requiredMode, requiredLabel),
);
};
/* Add the plugin to your FormKit config */
app.use(
defaultConfig({ plugins: [addRequiredModePlugin] }),
);
Recommended usage
For simplicity and consistency, it’s recommended to build forms using FormKit’s schema-based approach. This approach uses structured JSON data to represent the form controls and logic, allowing forms to be built in a logical and consistent manner.
Example:
<FormKit type="form">
<FormKitSchema :schema="schema" />
</FormKit>
This method allows you to construct forms through a form builder tool, reducing the need for custom renderings for each form configuration or element.