Adding a Custom CSS Class to Unpublished Menu Items in Drupal 8

We had a requirement to indicate weather a menu item was unpublished when a user who has access to unpublished content was logged in. Drupal will not display the menu item if the user doesn't have access, but if the user does have access, it will display the menu item like normal. We needed to make it clear to the user that the menu item is representing something unpublished and will not be shown to anonymous visitors. To do this we wanted to put a dashed border around the menu item. The best way we thought of doing this was to add a specific class to the menu item if the item was not published. Below is what I came up with to put into our module's .module file or our theme's .theme file.

 * Implements hook_preprocess_HOOK().
function mymodule_preprocess_menu(&$variables) {

  foreach ($variables['items'] as &$item) {

    // We only want to look at internal links.
    if (!$item['url']->isExternal() && $item['url']->isRouted()) {
      // RouteParameters gets the NID of the linked item.
      $routeParameters = $item['url']->getRouteParameters();
      $nid = $routeParameters['node'];

      // It is possible that an internal menu URL does not reference a node.
      if (!empty($nid)) {
        // Check the node status.
        $query = \Drupal::entityQuery('node');
        $query->condition('status', 0);
        $query->condition('nid', $nid);
        $entity = $query->execute();

        if (!empty($entity)) {
          // add .menu-unpublished css class

Adding some CSS to the theme to target .menu-unpublished gives the result we're looking for:

Unpublished menu items is outlined in blue dashed border

This is great. The next thing I needed to do was to make sure screen readers know this is an unpbulished page as well. Here sighted users can see the difference, but screen readers can't tell the difference. To do that we wanted to add some markup to the link text. I broke that out in another blog post: Adding HTML to a menu item in Drupal 8.