diff --git a/DESCRIPTION b/DESCRIPTION index fcdee62d0..8382ac8e0 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -69,7 +69,7 @@ Config/Needs/routine: chromote, desc, renv Config/Needs/website: brio, crosstalk, dplyr, DT, ggplot2, glue, htmlwidgets, leaflet, lorem, palmerpenguins, plotly, purrr, rprojroot, rstudio/htmltools, scales, stringr, tidyr, webshot2 -Config/roxygen2/version: 8.1.0 +Config/roxygen2/version: 8.1.1 Config/testthat/edition: 3 Config/testthat/parallel: true Config/testthat/start-first: zzzz-bs-sass, fonts, zzz-precompile, theme-*, diff --git a/NEWS.md b/NEWS.md index 8609deec6..5ee1062fd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -31,6 +31,8 @@ * Added an aria-controls attribute when using bslib::page_navbar() with bslib::nav_panel() to establish an explicit relationship between the tab and the controlled tab panel. (@shelbylevel, #1354) +* `page_navbar()` now includes banner and main landmarks, making it easier for screen reader users to navigate the page. (@jcheng5, @shelbylevel, #1357) + # bslib 0.12.0 ## New features diff --git a/R/navs-legacy.R b/R/navs-legacy.R index b942156b0..dfaee1418 100644 --- a/R/navs-legacy.R +++ b/R/navs-legacy.R @@ -224,7 +224,8 @@ navs_bar_ <- function( header = NULL, footer = NULL, fluid = TRUE, - theme = NULL + theme = NULL, + page_landmarks = FALSE ) { navbar_options <- navbar_options %||% navbar_options() @@ -258,7 +259,8 @@ navs_bar_ <- function( inverse = identical(navbar_theme, "dark"), underline = navbar_options[["underline"]], fluid = fluid, - theme = theme + theme = theme, + page_landmarks = page_landmarks ) attribs <- navbar_options[["attribs"]] %||% list() @@ -267,7 +269,7 @@ navs_bar_ <- function( # user includes both, the attribute wins for being most technically-correct. attribs[["data-bs-theme"]] <- attribs[["data-bs-theme"]] %||% navbar_theme - # navbarPage_() returns a tagList() of the nav and content + # navbarPage_() returns a tagList() of the navbar and content navbar[[1]] <- tagAppendAttributes( navbar[[1]], style = if (!is.null(navbar_bg)) { @@ -300,7 +302,8 @@ navbarPage_ <- function( underline = TRUE, collapsible = FALSE, fluid = TRUE, - theme = NULL + theme = NULL, + page_landmarks = FALSE ) { # alias title so we can avoid conflicts w/ title in withTags pageTitle <- title @@ -401,7 +404,11 @@ navbarPage_ <- function( contents <- dropNulls(list(header, tabset$content, footer)) if (is.null(sidebar)) { - contentDiv <- div(class = containerClass, !!!contents) + contentDiv <- div( + class = containerClass, + role = if (page_landmarks) "main", + !!!contents + ) # If fillable is truthy, the .container also needs to be fillable if (!isFALSE(fillable)) { @@ -426,11 +433,17 @@ navbarPage_ <- function( contentDiv <- bindFillRole(contentDiv, container = TRUE, item = TRUE) } + # Keep the navbar and content as siblings for CSS and sticky positioning. + # Embedded navsets retain navigation semantics without page-level landmarks. + if (page_landmarks) { + containerDiv$name <- "nav" + navbar <- tags$header(class = navbarClass, containerDiv) + } else { + navbar <- tags$nav(class = navbarClass, role = "navigation", containerDiv) + } + # *Don't* wrap in bootstrapPage() (shiny::navbarPage()) does that part - tagList( - tags$nav(class = navbarClass, role = "navigation", containerDiv), - contentDiv - ) + tagList(navbar, contentDiv) } navbarMenu_ <- function(title, ..., menuName = title, icon = NULL, align) { diff --git a/R/page.R b/R/page.R index 26e56a054..75c700ae0 100644 --- a/R/page.R +++ b/R/page.R @@ -442,7 +442,7 @@ page_navbar <- function( } # If a sidebar is provided, we want the layout_sidebar(fill = TRUE) component - # (which is a sibling of the