Providing components from a module or theme

Last updated on
21 July 2023

Providing components from a module or theme via Component Schema involves these basic steps:

  1. Define properties that will be reused across multiple components.
  2. Provide your components, typically in a components sub-directory in your module or theme.
  3. For each component, write a componentname.variables.yml file.
  4. Write the component as, at minimum, a Twig template.
  5. Finally, detail the component in a modulename.component_schema.yml or themename.component_schema.yml file.

Below we provide more detail on these steps. It's recommended to read the documentation on defining component properties before starting in here.

For purposes of this guide, we'll take as an example a component for the Bulma CSS framework. Full working code for this example can be found in the Bulma Components module, which is built on Component Schema.

One of the elements Bulma provides is a button. At its most basic, a button requires only a single class, button:

<button class="button">Button</button>

Besides <button> a button can also apply to other tags such as an <a> tag:

<a class="button">Button</a>

So one of the properties of a button is the tag used for the outermost element. In our component, this will be a $tag variable passed to the component template.

A button can have a color applied via one of a set of color classes (is-primary, is-link, and so on). So our component will have a $color variable of type string, leading to a rendered element like:

<button class="button is-primary">Primary</button>

Some of the properties of a button are best modelled as boolean variables, such as whether the button is outlined or is rounded. Two further variables in our component will be $is_outlined and $is_rounded, both of type boolean.

And so on. Since there are many properties of a Bulma button, there are many different variables we'll want to define for our component.

Define properties that will be reused across multiple components

Many of the properties that apply to a button are also relevant to various other components. Rather than repeat similar configuration in multiple places, we can take advantage of type inheritance that's built into Component Schema by defining and using a set of specialized and reusable types.

We do this in a .yml file in a component/schema directory--just the came way as you can provide configuration schema/metadata in a config/schema directory.

In our case, color is a property that's used across many Bulma components, so it's an ideal use case for a dedicated type.

bulma_color:
  label: 'Color'
  description: 'Set to one of a large range of colors'
  nullable: true
  type: string_attribute_provider
  options:
    is-white: white
    is-black: black
    is-light: light
    is-dark: dark
    is-primary: primary
    is-info: info
    is-link: link
    is-success: success
    is-warning: warning
    is-danger: danger
    is-black-bis: black-bis
    is-black-ter: black-ter
    is-grey-darker: grey-darker
    is-grey-dark: grey-dark
    is-grey: grey
    is-grey-light: grey-light
    is-grey-lighter: grey-lighter
    is-white-ter: white-ter
    is-white-bis: white-bis
    is-primary-light: primary-light
    is-link-light: link-light
    is-info-light: info-light
    is-success-light: success-light
    is-warning-light: warning-light
    is-danger-light: danger-light
    is-primary-dark: primary-dark
    is-link-dark: link-dark
    is-info-dark: info-dark
    is-success-dark: success-dark
    is-warning-dark: warning-dark
    is-danger-dark: danger-dark
  provides_class: true

Provide your components, typically in a components sub-directory in your module or theme

Use the Components! module to support this directory as providing templates by registering the path 'components' in your module's or theme's .info.yml file. Put each component in a sub-directory of components. For example, a button component would be in components/button. Alternately, for more complex component sets, nest the components in an intermediate directory. For example, if you're using atomic design, you might use components/atoms/componentname or similar. In our case we'll  follow the pattern used in Bulma documentation, where most components are classed as "layout", "form", "elements", or (yes, confusingly in our case) "components". So a button component would be at components/elements/button.

For each component, write a componentname.variables.yml file

This is where we pull together definitions for all the properties in our component. In many cases, this will be as simple as referencing a property we've already defined as a specialized type.

Here's the definition of a Bulma button component's properties (slightly simplified for purposes of demonstration). See the Bulma button component documentation for more info on each of the properties.

attributes:
  type: bulma_attributes
tag:
  type: bulma_tag
  default: 'button'
url:
  label: 'URL'
  description: 'Path or URL the button links to'
  nullable: true
  type: string_attribute_provider
  provides_attribute: true
  provided_name: 'href'
  ui_type: field
size:
  type: bulma_size
  documentation_url: https://bulma.io/documentation/elements/button/#sizes
  preview: is-medium
color:
  type: bulma_color
  documentation_url: https://bulma.io/documentation/elements/button/#colors
  preview: is-danger
button_label:
  label: 'Label'
  description: 'The text to be used for the button'
  nullable: true
  type: label
  preview: 'My button'
state:
  label: 'State'
  description: 'The state of the button'
  nullable: true
  type: string_attribute_provider
  options:
    is-hovered: hovered
    is-focused: focused
    is-active: active
    is-loading: loading
    is-static: static
  provides_class: true
  documentation_url: https://bulma.io/documentation/elements/button/#states
is_light:
  type: bulma_is_light
  label: 'Is light'
  description: 'Use the light version of a button color'
  documentation_url: https://bulma.io/documentation/elements/button/#colors
is_selected:
  label: 'Is selected'
  description: 'Display a button above any siblings'
  nullable: true
  type: boolean_attribute_provider
  provides_class: true
  provided_value: is-selected
  documentation_url: https://bulma.io/documentation/elements/button/#list-of-buttons
is_rounded:
  type: bulma_is_rounded
  description: 'Make a button rounded'
  documentation_url: https://bulma.io/documentation/elements/button/#styles
  preview: true
is_loading:
  label: 'Is loading'
  description: 'Turn a button into its loading version'
  nullable: true
  type: boolean_attribute_provider
  provides_class: true
  provided_value: is-loading
  documentation_url: https://bulma.io/documentation/elements/button/#states
is_outlined:
  label: 'Is outlined'
  description: 'Outline a button'
  nullable: true
  type: boolean_attribute_provider
  provides_class: true
  provided_value: is-outlined
  documentation_url: https://bulma.io/documentation/elements/button/#states
disabled:
  label: 'Disabled'
  description: 'Disable the button, preventing the user from interacting with it'
  nullable: true
  type: boolean_attribute_provider
  provides_attribute: true
  provided_name: 'disabled'
  provided_value: true
  documentation_url: https://bulma.io/documentation/elements/button/#states
is_fullwidth:
  type: bulma_is_fullwidth
  description: 'Make a buttom take the full width available'
  documentation_url: https://bulma.io/documentation/elements/button/#displays
is_inverted:
  label: 'Is inverted'
  description: 'Make text color becomes the background color, and vice-versa'
  nullable: true
  type: boolean_attribute_provider
  provides_class: true
  provided_value: is-inverted
  documentation_url: https://bulma.io/documentation/elements/button/#styles

Write the component as, at minimum, a Twig template

You could name this file componentname.html.twig. You may also wish to include assets such as SCSS/CSS or JavaScript.

In Component Schema, a Twig template begins with two lines that register the component type and call processing, see relevant documentation. From there, the template is very simple: the tag that's been passed in, the attributes, and the closing tag. Since all the other properties are attribute providers of one sort or another, all the rest of the complexity is handled by the process_component() call.

{% set component_type = 'bulma_button' %}
{% process_component() %}
<{{ tag }}{{ attributes }}></{{ tag }}>

Optionally, you can also provide a template to be used as an example of the component on a Styeguide page. See the button component in the Bulma Components module for several examples templates.

Detail the component in a modulename.component_schema.yml or themename.component_schema.yml file

Having produced a component and defined its variables, it remains to register it - and your other components - with Component Schema. You do this in a Yaml file in the base directory of your module or theme.

If your component itself uses a CSS class on the outermost HTML element, you can provide this by specifying an html_class property--in our case, 'button'.

components:
  bulma_button:
    label: 'Button'
    description: 'The classic button, in different colors, sizes, and states'
    styleguide_template: '@bulma_components/elements/button/_example-buttons-in-columns.html.twig'
    component_template: '@bulma_components/elements/button/button.html.twig'
    variables_yml: components/elements/button/button.variables.yml
    group: 'Bulma - Elements'
    documentation_url: https://bulma.io/documentation/elements/button
    html_class: 'button'
    takes_ui: true

Help improve this page

Page status: No known problems

You can: