18

I want to add a CSS class to a label on a page that is using Drupal 8 Form API. I have been unable to find a reference online on how to apply that. I have used the following workaround, but it produces some strange results.

public function buildForm(array $form, FormStateInterface $form_state)
{
    $form['label1']  = array(
        '#type' => 'label',
        '#title' => $this->t('QUESTIONNAIRE'),
        '#id'         => 'lbl1',
        '#prefix'     => '<div class="caption1">',
        '#suffix'     => '</div>',
    ) ;

and the HTML rendered is:

<div class="caption1"><label for="lbl1" class="control-label">
<div class="caption1"></div>QUESTIONNAIRE
  </label>

Not only is the div statement in the wrong place but it is rendered twice.

I had found postings from several years ago that show this was not possible, but I am hoping that since then, and with D8, it has been fixed. I do not want to do it with prefix/suffix, but as a separate array element.

PS: this site is Drupal 8.0.0-rc2

0

6 Answers 6

19

I know this is an old thread, but for anyone Googling.

The clue to this is in template_preprocess_form_element().

$element += [
    '#title_display' => 'before',
    '#wrapper_attributes' => [],
    '#label_attributes' => [],
  ];

#label_attributes is a standard attribute array, so very easy to set a class with ['class' => ['my-class', 'other-class']]

#title_display takes 3 values:

  • before: The label is output before the element. This is the default.

  • after: The label is output after the element. For example, this is used for radio and checkbox #type elements.

  • invisible: Labels are critical for screen readers to enable them to properly navigate through forms but can be visually distracting. This property hides the label for everyone except screen readers.
  • attribute: Set the title attribute on the element to create a tooltip but output no label element. This is supported only for checkboxes and radios
3
  • 1
    +1 #label_attributes works with a field in a programmatically created form e.g. adding this line to the form field definition: '#label_attributes' => array( 'class' => array( 'green' ) ), Nov 9, 2020 at 22:12
  • This is the best, simplest. There's no reason to be implementing hooks and preprocess functions. You will need to find the appropriate place to set the value, especially when working with fields. Your end result may require an incredibly silly and long offset like $form['field_name']['widget'][0]['value']['#label_attributes']['class'][] = 'required'; Jul 8, 2022 at 18:12
  • This worked for me after modifying my theme implementation.
    – Daniel
    Nov 22 at 1:28
8

I just checked this and I don't believe it is possible to add a class directly to a label element.

As you most probably know, classes are normally added with #attributes as follows:

 $form['foo'] = array(
  '#type' => 'textfield',
  '#title' => 'Foo',
  '#attributes' => array('class' => array('first-class', 'second-class')),
);

However, I just tested and #attributes does not add classes to a Label element.

Is it possible for you to add a wrapper form element, give a class to it and then style your label based on the fact that it is a child of the wrapper element? Like this:

$form['container'] = array(
  '#type' => 'container',
  '#attributes' => array('class' => array('your-class')),
);
$form['container']['foo'] = array(
  '#type' => 'textfield',
  '#title' => 'Foo',
);

Now, this will render the example textfield (and its label) inside of a DIV element that has your-class i.e. you can style you label:

.your-class label {
  /* your CSS here */
}
6

There are several options to do this in Drupal >=8.0.0. These all really revolve around template overrides in a theme, but a module should be able to implement template preprocess hooks defined by other modules

  1. The simplest, but non-dynamic option is to override form-element-label.html.twig directly. This could work if all labels will get the form-control class.
  2. Following along these lines, implementing template_preprocess_form_element_label would allow you to do the same thing and add form-control class to attributes without overriding the template.
  3. You could also implement template_preprocess_form_element, and add logic to not overwrite $variables['label'], but take its values from some defined key on the form element array.
2

For submit buttons we can add class like below :

$form['actions']['submit']['#attributes']['class'][] = 'use-ajax-submit';

1

Cleanest option I found is as per @mradcliffe's #3 suggestion above. Eg in your form definition -

$form['distance'] = [
        '#type' => 'select',
        '#title' => 'Distance',
        '#required' => true,
        '#options' => [
            '10' => '10 Miles',
            '25' => '25 Miles',
            '50' => '50 Miles',
            '100' => '100 Miles'
        ],
        '#label_classes' => [
            'some-label-class'
        ]
    ];

Then in a custom module implement hook_preprocess_form_element:

/**
* Implementation of hook_preprocess_form_element
* @param $variables
*/
function your_module_preprocess_form_element(&$variables)
{
    if(isset($variables['element']['#label_classes'])) {
        $variables['label']['#attributes']['class'] = $variables['element']['#label_classes'];
    }
}

Note this will override any label classes Drupal wants to add. In my case that is fine. Code above can be altered to prevent this if necessary.

1

To complete @Nate answer, if you want to add theses classes to an existing form, you can do it in hook_form_alter :

function your_module_form_alter(&$form, FormStateInterface &$form_state, $form_id)
{
    // for a textfield
    $form['distance']['widget'][0]['value']['#label_classes'] = ['some-label-class'];
    // for a radio field
    $form['country']['widget']['#label_classes'] = ['some-label-class'];
}

And then use the hook_preprocess_form_element for the text field or a hook_preprocess_fieldset for the radio field :

/**
 * Implements hook_preprocess_hook().
 */
function your_module_preprocess_fieldset(&$variables)
{
  if(isset($variables['element']['#label_classes'])) {
    foreach ($variables['element']['#label_classes'] as $class) {
      $variables['legend']['attributes']->addClass($class);
    }
  }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.