Skip to content

Angular & WC: db-navigation ally problem #3415

Open
@mfranzke

Description

@mfranzke

Which generators are impacted?

  • All
  • HTML
  • React
  • Angular
  • Vue
  • Web components
  • Power Apps

Reproduction case

The current component composition for a navigation (@db-ui/ngx-components) leads to an intermediate element between the HTML <menu> and <li> element. This is an accessibility issue which should be fixed.

Here is an example of my Angular header component:

import {Component} from '@angular/core';
import {RouterLink, RouterLinkActive} from '@angular/router';

import {
  DBBrand,
  DBHeader,
  DBNavigation,
  DBNavigationItem,
  NavigationContentDirective,
  NavigationDirective,
} from '@db-ui/ngx-components';

@Component({
  selector: 'app-header',
  styleUrls: ['./header.component.scss'],
  standalone: true,
  imports: [
    RouterLink,
    RouterLinkActive,
    DBHeader,
    DBBrand,
    DBNavigation,
    DBNavigationItem,
    NavigationDirective,
    NavigationContentDirective,
  ],
  template: `
    <db-header header>
      <db-brand brand>My App</db-brand>
      <db-navigation *dbNavigation aria-label="main-navigation">
        <db-navigation-item>
          <ng-container *dbNavigationContent>
            <a
              routerLink="/"
              [routerLinkActiveOptions]="{ exact: true }"
              ariaCurrentWhenActive="page"
              routerLinkActive="active"
              queryParamsHandling="merge"
            >
              Home
            </a>
          </ng-container>
        </db-navigation-item>
      </db-navigation>
    </db-header>
  `
})
export class HeaderComponent {
}

The rendered HTML is:

<db-header>
  <header class="db-header" id="header-6b531c74-49da-457c-ade6-000aec178593">
    <db-drawer spacing="small" class="db-header-drawer">
      <dialog class="db-drawer">
        <article class="db-drawer-container" data-spacing="small" data-rounded="true">
          <header class="db-drawer-header">
            <div class="db-drawer-header-text"></div>
            <db-button icon="cross" variant="ghost" class="button-close-drawer">
              <button class="db-button" type="button" data-icon="cross" data-variant="ghost" data-no-text="true"> Close
                Button
              </button>
            </db-button>
          </header>
          <div class="db-drawer-content">
            <div class="db-header-drawer-navigation">
              <div class="db-header-navigation">
                <db-navigation>
                  <nav class="db-navigation" id="navigation-4d11a7ba-0ea3-48cb-8e93-5aac69048dae"
                       aria-label="main-navigation">
                    <menu>
                      <db-navigation-item>
                        <li class="db-navigation-item"><a routerlink="/"
                                                          ariacurrentwhenactive="page" routerlinkactive="active"
                                                          queryparamshandling="merge" href="/" class="active"
                                                          aria-current="page"> Home </a>
                        </li>
                      </db-navigation-item>
                    </menu>
                  </nav>
                </db-navigation>
              </div>
              <div class="db-header-meta-navigation"></div>
            </div>
            <div class="db-header-secondary-action"></div>
          </div>
        </article>
      </dialog>
    </db-drawer>
    <div class="db-header-meta-navigation"></div>
    <div class="db-header-navigation-bar">
      <div class="db-header-brand-container">
        <db-brand>
          <div class="db-brand" data-icon="db">My App</div>
        </db-brand>
      </div>
      <div class="db-header-navigation-container">
        <div class="db-header-navigation">
          <db-navigation>
            <nav class="db-navigation" id="navigation-f5fce825-fbd4-49b7-b16f-218dd195a6c1"
                 aria-label="main-navigation">
              <menu>
                <db-navigation-item>
                  <li class="db-navigation-item"><a routerlink="/"
                                                    ariacurrentwhenactive="page" routerlinkactive="active"
                                                    queryparamshandling="merge" href="/" class="active"
                                                    aria-current="page"> Home </a>
                  </li>
                </db-navigation-item>
              </menu>
            </nav>
          </db-navigation>
        </div>
        <div class="db-header-primary-action"></div>
      </div>
      <div class="db-header-action-container">
        <div class="db-header-burger-menu-container">
          <db-button icon="menu" variant="ghost">
            <button class="db-button" id="header-6b531c74-49da-457c-ade6-000aec178593-burger-menu" type="button"
                    data-icon="menu" data-variant="ghost" data-no-text="true"> BurgerMenu
            </button>
          </db-button>
        </div>
        <div class="db-header-secondary-action"></div>
      </div>
    </div>
  </header>
</db-header>

Expected Behaviour

(1) I would expect to use the components in composition without having the result that an intermediate element is created. So <li> should follow directly after <menu>. (2) Alternatively <li> and <menu> should be used but instead generic elements with the roles list and listitem.

Screenshots

Bildschirmfoto 2024-11-01 um 11 43 10

Bildschirmfoto 2024-11-01 um 11 45 35

Browser version

None

Add any other context about the problem here.

Probably this will introduce a breaking change in either the resulting HTML or the component / directive usage and it's maybe also related to other implementations for Vue and / or react which should be verified as well.

A possible solution would be to require an <li> after <db-navigation> without using it in the <db-navigation-item> and make CSS selectors more flexible (so either the nav item is directly an <li> or an <li> followed by a <div>or something similar).

<db-brand brand>My App</db-brand>
  <db-navigation *dbNavigation aria-label="main-navigation">
    <li>
      <db-navigation-item> ... </db-navigation-item>
    </li>
</db-navigation>

Another solution would probably be to use native HTML within the Header / Navigation and apply classes and styles only by using directives instead of having a mix of components / directives:

    <db-header header>
      <db-brand brand>My App</db-brand>
      <menu *dbNavigation aria-label="main-navigation">
        <li *dbNavigationItem>
            <a
              routerLink="/"
              [routerLinkActiveOptions]="{ exact: true }"
              ariaCurrentWhenActive="page"
              routerLinkActive="active"
              queryParamsHandling="merge"
            >
              Home
            </a>
        </li>
      </menu>
    </db-header>

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    📋 Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions