Skip to content

Introduction

UI Suite

UI Styles is part of UI Suite.

Design systems generally provide helpers and utilities CSS classes to apply on any element to handle generic CSS effects like background color, text effects, spacing, etc.

UI Styles allows:

  • developers to define styles (simple lists of concurrent CSS classes) from modules and themes in my_theme.ui_styles.yml files
  • site builders to use those styles depending on the modules enabled ( submodules of UI Styles or modules of its ecosystem).

The module generates a styles library page to be used as documentation for content editors or as a showcase for business and clients.

Example usage

Example of plugin declarations in the YML files.

Simple example

colors_background_color:
  label: Background color
  description: Similar to the contextual text color classes, easily set the background of an element to any contextual class.
  options:
    bg-primary: Primary
    bg-secondary: Secondary
    bg-success: Success
    bg-danger: Danger
    bg-warning: Warning
    bg-info: Info
    bg-light: Light
    bg-dark: Dark
    bg-white: White
    bg-transparent: Transparent
  previewed_with:
    - border
    - p-2

Where: * colors_background_color is the plugin ID * label is used in the library and the layout builder forms * description is only used in the library * options is the list of all classes and labels of the style * previewed_with is a list of classes used in the library to pimp the preview

Example with previewed_with per option

text_color:
  label: Text color
  category: Color
  previewed_with:
    - fr-p-1v
  options:
    fr-text-action-high--blue-france: Action-high blue France
    fr-text-inverted--blue-france:
      label: Inverted blue France
      previewed_with:
        - fr-background-action-high--blue-france

For the option fr-text-inverted--blue-france the preview classes fr-p-1v and fr-background-action-high--blue-france will be cumulated.

Example with description per option

typography:
  label: Typography
  description: "Material Design's text sizes and styles were developed to balance content density and reading comfort under typical usage conditions. https://m2.material.io/develop/web/components/typography"
  options:
    mdc-typography--headline1:
      label: Headline 1.
      description: The largest text on the screen, reserved for short, important text or numerals.
    mdc-typography--headline2: Headline 2
    mdc-typography--headline3: Headline 3
    mdc-typography--headline4: Headline 4
    mdc-typography--headline5: Headline 5
    mdc-typography--headline6: Headline 6

It is also possible to specify a description on an option.

Managing plugin form rendering

Styles can be rendered with different form elements in the interface depending on the style declaration in the YML file.

There are three different source plugins available by default to manage the rendering of the form element: * On/Off checkbox: Automatically applied when there is only one option defined * Toolbar: Automatically applied when there are several options with icons associated to all the options * Select: Always applicable

Example rendering an On/Off checkbox

clearfix:
  category: "Float"
  label: "Clearfix"
  description: "Easily clear floats by adding <code>.clearfix</code> to the parent element."
  options:
    clearfix: "Clearfix"

There is only one option, so the plugin source On/Off checkbox will be used to render the style form element in the UI.

Example rendering a Toolbar

borders_border:
  category: "Borders"
  label: "Border"
  description: "Use it to add an element's borders. Choose from all borders or one at a time."
  options:
    border:
      label: "Additive All"
      icon: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktYm9yZGVyLW91dGVyIiB2aWV3Qm94PSIwIDAgMTYgMTYiPgogIDxwYXRoIGQ9Ik03LjUgMS45MDZ2LjkzOGgxdi0uOTM4em0wIDEuODc1di45MzhoMVYzLjc4aC0xem0wIDEuODc1di45MzhoMXYtLjkzOHpNMS45MDYgOC41aC45Mzh2LTFoLS45Mzh6bTEuODc1IDBoLjkzOHYtMUgzLjc4djF6bTEuODc1IDBoLjkzOHYtMWgtLjkzOHptMi44MTMgMHYtLjAzMUg4LjVWNy41M2gtLjAzMVY3LjVINy41M3YuMDMxSDcuNXYuOTM4aC4wMzFWOC41em0uOTM3IDBoLjkzOHYtMWgtLjkzOHptMS44NzUgMGguOTM4di0xaC0uOTM4em0xLjg3NSAwaC45Mzh2LTFoLS45Mzh6TTcuNSA5LjQwNnYuOTM4aDF2LS45Mzh6bTAgMS44NzV2LjkzOGgxdi0uOTM4em0wIDEuODc1di45MzhoMXYtLjkzOHoiLz4KICA8cGF0aCBkPSJNMCAwdjE2aDE2VjB6bTEgMWgxNHYxNEgxeiIvPgo8L3N2Zz4K"
    border-top:
      label: "Additive Top"
      icon: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktYm9yZGVyLXRvcCIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBkPSJNMCAwdjFoMTZWMHptMSAyLjg0NHYtLjkzOEgwdi45Mzh6bTYuNS0uOTM4di45MzhoMXYtLjkzOHptNy41IDB2LjkzOGgxdi0uOTM4ek0xIDQuNzE5VjMuNzhIMHYuOTM4aDF6bTYuNS0uOTM4di45MzhoMVYzLjc4aC0xem03LjUgMHYuOTM4aDFWMy43OGgtMXpNMSA2LjU5NHYtLjkzOEgwdi45Mzh6bTYuNS0uOTM4di45MzhoMXYtLjkzOHptNy41IDB2LjkzOGgxdi0uOTM4ek0uNSA4LjVoLjQ2OXYtLjAzMUgxVjcuNTNILjk2OVY3LjVILjV2LjAzMUgwdi45MzhoLjV6bTEuNDA2IDBoLjkzOHYtMWgtLjkzOHptMS44NzUgMGguOTM4di0xSDMuNzh2MXptMS44NzUgMGguOTM4di0xaC0uOTM4em0yLjgxMyAwdi0uMDMxSDguNVY3LjUzaC0uMDMxVjcuNUg3LjUzdi4wMzFINy41di45MzhoLjAzMVY4LjV6bS45MzcgMGguOTM4di0xaC0uOTM4em0xLjg3NSAwaC45Mzh2LTFoLS45Mzh6bTEuODc1IDBoLjkzOHYtMWgtLjkzOHptMS44NzUgMGguNDY5di0uMDMxaC41VjcuNTNoLS41VjcuNWgtLjQ2OXYuMDMxSDE1di45MzhoLjAzMXpNMCA5LjQwNnYuOTM4aDF2LS45Mzh6bTcuNSAwdi45MzhoMXYtLjkzOHptOC41LjkzOHYtLjkzOGgtMXYuOTM4em0tMTYgLjkzN3YuOTM4aDF2LS45Mzh6bTcuNSAwdi45MzhoMXYtLjkzOHptOC41LjkzOHYtLjkzOGgtMXYuOTM4em0tMTYgLjkzN3YuOTM4aDF2LS45Mzh6bTcuNSAwdi45MzhoMXYtLjkzOHptOC41LjkzOHYtLjkzOGgtMXYuOTM4ek0wIDE2aC45Njl2LS41SDF2LS40NjlILjk2OVYxNUguNXYuMDMxSDB6bTEuOTA2IDBoLjkzOHYtMWgtLjkzOHptMS44NzUgMGguOTM4di0xSDMuNzh2MXptMS44NzUgMGguOTM4di0xaC0uOTM4em0xLjg3NS0uNXYuNWguOTM4di0uNUg4LjV2LS40NjloLS4wMzFWMTVINy41M3YuMDMxSDcuNXYuNDY5em0xLjg3NS41aC45Mzh2LTFoLS45Mzh6bTEuODc1IDBoLjkzOHYtMWgtLjkzOHptMS44NzUgMGguOTM4di0xaC0uOTM4em0xLjg3NS0uNXYuNUgxNnYtLjk2OWgtLjVWMTVoLS40Njl2LjAzMUgxNXYuNDY5eiIvPgo8L3N2Zz4K"
    border-bottom:
      label: "Additive Bottom"
      icon: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktYm9yZGVyLWJvdHRvbSIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBkPSJNLjk2OSAwSDB2Ljk2OWguNVYxaC40NjlWLjk2OUgxVi41SC45Njl6bS45MzcgMWguOTM4VjBoLS45Mzh6bTEuODc1IDBoLjkzOFYwSDMuNzh2MXptMS44NzUgMGguOTM4VjBoLS45Mzh6TTcuNTMxLjk2OVYxaC45MzhWLjk2OUg4LjVWLjVoLS4wMzFWMEg3LjUzdi41SDcuNXYuNDY5ek05LjQwNiAxaC45MzhWMGgtLjkzOHptMS44NzUgMGguOTM4VjBoLS45Mzh6bTEuODc1IDBoLjkzOFYwaC0uOTM4em0xLjg3NSAwaC40NjlWLjk2OWguNVYwaC0uOTY5di41SDE1di40NjloLjAzMXpNMSAyLjg0NHYtLjkzOEgwdi45Mzh6bTYuNS0uOTM4di45MzhoMXYtLjkzOHptNy41IDB2LjkzOGgxdi0uOTM4ek0xIDQuNzE5VjMuNzhIMHYuOTM4aDF6bTYuNS0uOTM4di45MzhoMVYzLjc4aC0xem03LjUgMHYuOTM4aDFWMy43OGgtMXpNMSA2LjU5NHYtLjkzOEgwdi45Mzh6bTYuNS0uOTM4di45MzhoMXYtLjkzOHptNy41IDB2LjkzOGgxdi0uOTM4ek0uNSA4LjVoLjQ2OXYtLjAzMUgxVjcuNTNILjk2OVY3LjVILjV2LjAzMUgwdi45MzhoLjV6bTEuNDA2IDBoLjkzOHYtMWgtLjkzOHptMS44NzUgMGguOTM4di0xSDMuNzh2MXptMS44NzUgMGguOTM4di0xaC0uOTM4em0yLjgxMyAwdi0uMDMxSDguNVY3LjUzaC0uMDMxVjcuNUg3LjUzdi4wMzFINy41di45MzhoLjAzMVY4LjV6bS45MzcgMGguOTM4di0xaC0uOTM4em0xLjg3NSAwaC45Mzh2LTFoLS45Mzh6bTEuODc1IDBoLjkzOHYtMWgtLjkzOHptMS44NzUgMGguNDY5di0uMDMxaC41VjcuNTNoLS41VjcuNWgtLjQ2OXYuMDMxSDE1di45MzhoLjAzMXpNMCA5LjQwNnYuOTM4aDF2LS45Mzh6bTcuNSAwdi45MzhoMXYtLjkzOHptOC41LjkzOHYtLjkzOGgtMXYuOTM4em0tMTYgLjkzN3YuOTM4aDF2LS45Mzh6bTcuNSAwdi45MzhoMXYtLjkzOHptOC41LjkzOHYtLjkzOGgtMXYuOTM4em0tMTYgLjkzN3YuOTM4aDF2LS45Mzh6bTcuNSAwdi45MzhoMXYtLjkzOHptOC41LjkzOHYtLjkzOGgtMXYuOTM4ek0wIDE1aDE2djFIMHoiLz4KPC9zdmc+Cg=="
    border-start:
      label: "Additive Start"
      icon: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktYm9yZGVyLWxlZnQiIHZpZXdCb3g9IjAgMCAxNiAxNiI+CiAgPHBhdGggZD0iTTAgMHYxNmgxVjB6bTEuOTA2IDFoLjkzOFYwaC0uOTM4em0xLjg3NSAwaC45MzhWMEgzLjc4djF6bTEuODc1IDBoLjkzOFYwaC0uOTM4ek03LjUzMS45NjlWMWguOTM4Vi45NjlIOC41Vi41aC0uMDMxVjBINy41M3YuNUg3LjV2LjQ2OXpNOS40MDYgMWguOTM4VjBoLS45Mzh6bTEuODc1IDBoLjkzOFYwaC0uOTM4em0xLjg3NSAwaC45MzhWMGgtLjkzOHptMS44NzUgMGguNDY5Vi45NjloLjVWMGgtLjk2OXYuNUgxNXYuNDY5aC4wMzF6TTcuNSAxLjkwNnYuOTM4aDF2LS45Mzh6bTcuNSAwdi45MzhoMXYtLjkzOHpNNy41IDMuNzgxdi45MzhoMVYzLjc4aC0xem03LjUgMHYuOTM4aDFWMy43OGgtMXpNNy41IDUuNjU2di45MzhoMXYtLjkzOHptNy41IDB2LjkzOGgxdi0uOTM4ek0xLjkwNiA4LjVoLjkzOHYtMWgtLjkzOHptMS44NzUgMGguOTM4di0xSDMuNzh2MXptMS44NzUgMGguOTM4di0xaC0uOTM4em0yLjgxMyAwdi0uMDMxSDguNVY3LjUzaC0uMDMxVjcuNUg3LjUzdi4wMzFINy41di45MzhoLjAzMVY4LjV6bS45MzcgMGguOTM4di0xaC0uOTM4em0xLjg3NSAwaC45Mzh2LTFoLS45Mzh6bTEuODc1IDBoLjkzOHYtMWgtLjkzOHptMS44NzUgMGguNDY5di0uMDMxaC41VjcuNTNoLS41VjcuNWgtLjQ2OXYuMDMxSDE1di45MzhoLjAzMXpNNy41IDkuNDA2di45MzhoMXYtLjkzOHptOC41LjkzOHYtLjkzOGgtMXYuOTM4em0tOC41LjkzN3YuOTM4aDF2LS45Mzh6bTguNS45Mzh2LS45MzhoLTF2LjkzOHptLTguNS45Mzd2LjkzOGgxdi0uOTM4em04LjUuOTM4di0uOTM4aC0xdi45Mzh6TTEuOTA2IDE2aC45Mzh2LTFoLS45Mzh6bTEuODc1IDBoLjkzOHYtMUgzLjc4djF6bTEuODc1IDBoLjkzOHYtMWgtLjkzOHptMS44NzUtLjV2LjVoLjkzOHYtLjVIOC41di0uNDY5aC0uMDMxVjE1SDcuNTN2LjAzMUg3LjV2LjQ2OXptMS44NzUuNWguOTM4di0xaC0uOTM4em0xLjg3NSAwaC45Mzh2LTFoLS45Mzh6bTEuODc1IDBoLjkzOHYtMWgtLjkzOHptMS44NzUtLjV2LjVIMTZ2LS45NjloLS41VjE1aC0uNDY5di4wMzFIMTV2LjQ2OXoiLz4KPC9zdmc+Cg=="
    border-end:
      label: "Additive End"
      icon: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktYm9yZGVyLXJpZ2h0IiB2aWV3Qm94PSIwIDAgMTYgMTYiPgogIDxwYXRoIGQ9Ik0uOTY5IDBIMHYuOTY5aC41VjFoLjQ2OVYuOTY5SDFWLjVILjk2OXptLjkzNyAxaC45MzhWMGgtLjkzOHptMS44NzUgMGguOTM4VjBIMy43OHYxem0xLjg3NSAwaC45MzhWMGgtLjkzOHpNNy41MzEuOTY5VjFoLjkzOFYuOTY5SDguNVYuNWgtLjAzMVYwSDcuNTN2LjVINy41di40Njl6TTkuNDA2IDFoLjkzOFYwaC0uOTM4em0xLjg3NSAwaC45MzhWMGgtLjkzOHptMS44NzUgMGguOTM4VjBoLS45Mzh6TTE2IDBoLTF2MTZoMXpNMSAyLjg0NHYtLjkzOEgwdi45Mzh6bTYuNS0uOTM4di45MzhoMXYtLjkzOHpNMSA0LjcxOVYzLjc4SDB2LjkzOGgxem02LjUtLjkzOHYuOTM4aDFWMy43OGgtMXpNMSA2LjU5NHYtLjkzOEgwdi45Mzh6bTYuNS0uOTM4di45MzhoMXYtLjkzOHpNLjUgOC41aC40Njl2LS4wMzFIMVY3LjUzSC45NjlWNy41SC41di4wMzFIMHYuOTM4aC41em0xLjQwNiAwaC45Mzh2LTFoLS45Mzh6bTEuODc1IDBoLjkzOHYtMUgzLjc4djF6bTEuODc1IDBoLjkzOHYtMWgtLjkzOHptMi44MTMgMHYtLjAzMUg4LjVWNy41M2gtLjAzMVY3LjVINy41M3YuMDMxSDcuNXYuOTM4aC4wMzFWOC41em0uOTM3IDBoLjkzOHYtMWgtLjkzOHptMS44NzUgMGguOTM4di0xaC0uOTM4em0xLjg3NSAwaC45Mzh2LTFoLS45Mzh6TTAgOS40MDZ2LjkzOGgxdi0uOTM4em03LjUgMHYuOTM4aDF2LS45Mzh6TTAgMTEuMjgxdi45MzhoMXYtLjkzOHptNy41IDB2LjkzOGgxdi0uOTM4ek0wIDEzLjE1NnYuOTM4aDF2LS45Mzh6bTcuNSAwdi45MzhoMXYtLjkzOHpNMCAxNmguOTY5di0uNUgxdi0uNDY5SC45NjlWMTVILjV2LjAzMUgwem0xLjkwNiAwaC45Mzh2LTFoLS45Mzh6bTEuODc1IDBoLjkzOHYtMUgzLjc4djF6bTEuODc1IDBoLjkzOHYtMWgtLjkzOHptMS44NzUtLjV2LjVoLjkzOHYtLjVIOC41di0uNDY5aC0uMDMxVjE1SDcuNTN2LjAzMUg3LjV2LjQ2OXptMS44NzUuNWguOTM4di0xaC0uOTM4em0xLjg3NSAwaC45Mzh2LTFoLS45Mzh6bTEuODc1IDBoLjkzOHYtMWgtLjkzOHoiLz4KPC9zdmc+Cg=="
  previewed_with:
    - p-2
    - bg-light
    - border-dark
  weight: -80

There are several options and all options are defined with a label AND an icon, so the plugin source Toolbar will be used to render the style form element in the UI.

Example rendering a Select

Select source plugin is always applicable, and two types of rendering are available.

Select with preview:

colors_color:
  category: "Text"
  label: "Color"
  description: "Convey meaning through color with a handful of color utility classes."
  options:
    text-primary: "Primary"
    text-primary-emphasis: "Primary emphasis"
    text-secondary: "Secondary"
    text-secondary-emphasis: "Secondary emphasis"
    text-success: "Success"
    text-success-emphasis: "Success emphasis"
    text-danger: "Danger"
    text-danger-emphasis: "Danger emphasis"
    text-warning: "Warning"
    text-warning-emphasis: "Warning emphasis"
    text-info: "Info"
    text-info-emphasis: "Info emphasis"
    text-light: "Light"
    text-light-emphasis: "Light emphasis"
    text-dark: "Dark"
    text-dark-emphasis: "Dark emphasis"
    text-body: "Body"
    text-body-emphasis: "Body emphasis"
    text-body-secondary: "Body secondary"
    text-body-tertiary: "Body tertiary"
    text-white: "White"
    text-black: "Black"
    text-black-50: "Black 50 (deprecated)"
    text-white-50: "White 50 (deprecated)"
    text-muted: "Muted (deprecated)"
    text-reset: "Reset"
  weight: -98

There are several options defined solely by their labels. The select options will be displayed with a preview on the left.

Classic select without preview:

spacing_gap:
  category: "Spacing"
  label: "Gap"
  description: "When using display: grid or display: flex, you can make use of gap utilities on the parent element. This can save on having to add margin utilities to individual children of a grid or flex container. Gap utilities are responsive by default."
  options:
    gap-0: "0"
    gap-1: "1"
    gap-2: "2"
    gap-3: "3"
    gap-4: "4"
    gap-5: "5"
  previewed_as: hidden
  weight: -58

The property previewed_as with a value containing hidden (hidden, widget_hidden for examples) disables the select with preview rendering.

Best practices

Options are mutually exclusive

When declaring the styles, if you can combine many values of a style, that means it is different styles.

The goal is to have each option of one specific style plugin to be mutually exclusive.

Do

borders_border:
category: "Borders"
label: "Border"
options:
  border: "Additive All"
  border-top: "Additive Top"
  border-end: "Additive End"
  border-bottom: "Additive Bottom"
  border-start: "Additive Start"

borders_border_subtractive:
category: "Borders"
label: "Border subtractive"
options:
  border-0: "All"
  border-top-0: "Top"
  border-end-0: "End"
  border-bottom-0: "Bottom"
  border-start-0: "Start"

Don't

borders_border:
category: "Borders"
label: "Border"
options:
  border: "Additive All"
  border-top: "Additive Top"
  border-end: "Additive End"
  border-bottom: "Additive Bottom"
  border-start: "Additive Start"
  border-0: "Subtractive All"
  border-top-0: "Subtractive Top"
  border-end-0: "Subtractive End"
  border-bottom-0: "Subtractive Bottom"
  border-start-0: "Subtractive Start"

Should I do a component or a style?

If the CSS class is only for layout purpose

  • Defines props in the component that will be used as layout.

If the CSS class is "standalone"

  • Create a style

If the CSS class needs specific HTML markup