Generate classname from variable in template In the template file for a view with grouped rows, I would like to add the group title as class name, but the rendered class name from the title variable is just _ on every group. This is the generated HTML markup for the group whose title is 1.
&lt;h3 class=&quot;views-group group-_&quot;&gt;1&lt;/h3&gt;

What's wrong here?
This is the template file content.
{#
/**
* @file
* Default theme implementation to display a view of unformatted rows.
*
* Available variables:
* - title: The title of this group of rows. May be empty.
* - rows: A list of the view's row items.
*   - attributes: The row's HTML attributes.
*   - content: The row's content.
* - view: The view object.
* - default_row_class: A flag indicating whether default classes should be
*   used on rows.
*
* @see template_preprocess_views_view_unformatted()
*
* @ingroup themeable
*/
#}
{% if title %}
  {%
    set classes = [
      'views-group',
      'group-' ~ title|clean_class,
    ]
  %}
  &lt;h3{{ content_attributes.addClass(classes) }}&gt;{{ title }}&lt;/h3&gt;
{% endif %}
{% for row in rows %}
  {%
    set row_classes = [
      default_row_class ? 'views-row',
    ]
  %}
  &lt;div{{ row.attributes.addClass(row_classes) }}&gt;
    {{- row.content -}}
  &lt;/div&gt;
{% endfor %}
Valid CSS identifiers (for example, element names, classes, and IDs in selectors) cannot start with a digit, or a hyphen followed by a digit. Html::cleanCssIdentifier(), which is called when a CSS identifier is created from a string, contains the following code.
  // Valid characters in a CSS identifier are:
  // - the hyphen (U+002D)
  // - a-z (U+0030 - U+0039)
  // - A-Z (U+0041 - U+005A)
  // - the underscore (U+005F)
  // - 0-9 (U+0061 - U+007A)
  // - ISO 10646 characters U+00A1 and higher
  // We strip out any character not in the above list.
  $identifier = preg_replace('/[^\\x{002D}\\x{0030}-\\x{0039}\\x{0041}-\\x{005A}\\x{005F}\\x{0061}-\\x{007A}\\x{00A1}-\\x{FFFF}]/u', '', $identifier);

  // Identifiers cannot start with a digit, two hyphens, or a hyphen followed by a digit.
  $identifier = preg_replace([
    '/^[0-9]/',
    '/^(-[0-9])|^(--)/',
  ], [
    '_',
    '__',
  ], $identifier);

As a digit is only replaced when it's the first character, or the second character when the first character is a hyphen, it's enough to  first concatenate a string that is an acceptable CSS identifier, as in the following code.
{% if title %}
  {%
    set classes = [
      'views-group',
      ('group-' ~ title)|clean_class,
    ]
  %}
  &lt;h3{{ content_attributes.addClass(classes) }}&gt;{{ title }}&lt;/h3&gt;
{% endif %}
{% for row in rows %}
  {%
    set row_classes = [
      default_row_class ? 'views-row',
    ]
  %}
  &lt;div{{ row.attributes.addClass(row_classes) }}&gt;
    {{- row.content -}}
  &lt;/div&gt;
{% endfor %}

The only difference with the code shown in the question is using ('group-' ~ title)|clean_class, instead of 'group-' ~ title|clean_class. The parentheses are necessary because | takes precedence over ~.
That is the only change needed to avoid that the first digit in the node title is replaced with an underscore. There is no need to change the code to add a single CSS class instead of two CSS classes.