diff --git a/nbconvert_a11y/exporter.py b/nbconvert_a11y/exporter.py index c333f074..9f5da633 100644 --- a/nbconvert_a11y/exporter.py +++ b/nbconvert_a11y/exporter.py @@ -112,9 +112,7 @@ class A11yExporter(PostProcess, HTMLExporter): include_toc = Bool( True, help="collect a table of contents of the headings in the document" ).tag(config=True) - include_summary = Bool( - True, help="collect notebook properties into a summary" - ).tag(config=True) + include_summary = Bool(True, help="collect notebook properties into a summary").tag(config=True) wcag_priority = Enum( ["AAA", "AA", "A"], "AA", help="the default inital wcag priority to start with" ).tag(config=True) @@ -124,11 +122,11 @@ class A11yExporter(PostProcess, HTMLExporter): include_cell_index = Bool( True, help="show the ordinal cell index, typically this is ignored from notebooks." ).tag(config=True) - include_visibility = Bool(False, help="include visibility toggle").tag(config=True) + include_visibility = Bool(True, help="include visibility toggle").tag(config=True) include_upload = Bool(False, help="include template for uploading new content").tag(config=True) allow_run_mode = Bool(False, help="enable buttons for a run mode").tag(config=True) hide_anchor_links = Bool(False).tag(config=True) - exclude_anchor_links = Bool(True).tag(config=True) + exclude_anchor_links = Bool(False).tag(config=True) code_theme = Enum(list(THEMES), "gh-high", help="an accessible pygments dark/light theme").tag( config=True ) @@ -187,7 +185,7 @@ def init_resources(self, resources=None): resources["include_help"] = self.include_help resources["include_toc"] = self.include_toc resources["include_summary"] = self.include_summary - resources["include_visibility"] = self.include_upload + resources["include_visibility"] = self.include_visibility resources["include_upload"] = self.include_upload resources["wcag_priority"] = self.wcag_priority resources["accesskey_navigation"] = self.accesskey_navigation @@ -212,14 +210,12 @@ def from_notebook_node(self, nb, resources=None, **kw): def post_process_html(self, body): """A final pass at the exported html to add table of contents, heading links, and other a11y affordances.""" soup = soupify(body) - describe_main(soup) heading_links(soup) - details = soup.select_one("""[aria-labelledby="nb-toc"] details""") - if details: - details.extend(soupify(toc(soup)).body.children) - for x in details.select("ul"): - x.name = "ol" - details.select_one("ol").attrs["aria-labelledby"] = "nb-toc" + if self.include_toc: + details = soup.select_one("""[aria-labelledby="nb-toc"] details""") + if details: + if not details.select_one("nav"): + details.append(toc(soup)) return soup.prettify(formatter="html5") @@ -270,7 +266,7 @@ def highlight(code, lang="python", attrs=None, experimental=True): lang = lang or pygments.lexers.get_lexer_by_name(lang or "python") formatter = pygments.formatters.get_formatter_by_name( - "html", debug_token_types=True, title=f"{lang} code", wrapcode=True + "html", debug_token_types=False, title=f"{lang} code", wrapcode=True ) try: return pygments.highlight( @@ -287,31 +283,39 @@ def soupify(body: str) -> BeautifulSoup: return BeautifulSoup(body, features="html5lib") -def mdtoc(html): +def toc(html): """Create a table of contents in markdown that will be converted to html""" - import io - toc = io.StringIO() + toc = BeautifulSoup(features="html.parser") + toc.append(nav := toc.new_tag("nav")) + nav.append(ol := toc.new_tag("ol")) + last_level = 1 + headers = set() for header in html.select(".cell :is(h1,h2,h3,h4,h5,h6)"): + if header in headers: + continue + headers.add(header) id = header.attrs.get("id") if not id: - from slugify import slugify - - if header.string: - id = slugify(header.string) - else: - continue - + continue # there is missing logistics for managely role=heading # adding code group semantics will motivate this addition level = int(header.name[-1]) - toc.write(" " * (level - 1) + f"* [{header.string}](#{id})\n") - return toc.getvalue() - - -def toc(html): - """Create an html table of contents""" - return get_markdown(mdtoc(html)) + if last_level > level: + for l in range(level, last_level): + last_level -= 1 + ol = ol.parent.parent + elif last_level < level: + for l in range(last_level, level): + last_level += 1 + ol.append(li := toc.new_tag("li")) + li.append(ol := toc.new_tag("ol")) + ol.append(li := toc.new_tag("li")) + li.append(a := toc.new_tag("a")) + a.append(header.text) + a.attrs.update(href=f"#{id}") + + return toc def heading_links(html): @@ -321,12 +325,12 @@ def heading_links(html): if not id: from slugify import slugify - if header.string: - id = slugify(header.string) + if header.text: + id = slugify(header.text) else: continue - link = soupify(f"""{header.string}""").body.a + link = soupify(f"""{header.text}""").body.a header.clear() header.append(link) @@ -362,15 +366,6 @@ def count_code_cells(nb): return len([None for x in nb.cells if x["cell_type"] == "code"]) -def describe_main(soup): - """Add REFIDs to aria-describedby""" - x = soup.select_one("#toc > details > summary") - if x: - x.attrs["aria-describedby"] = soup.select_one("main").attrs["aria-describedby"] = ( - "nb-cells-count-label nb-cells-label nb-code-cells nb-code-cells-label nb-ordered nb-loc nb-loc-label" - ) - - def is_ordered(nb) -> str: """Measure if the notebook is ordered""" start = 0 diff --git a/nbconvert_a11y/templates/a11y/base.html.j2 b/nbconvert_a11y/templates/a11y/base.html.j2 index 5c57534a..c7ac7280 100644 --- a/nbconvert_a11y/templates/a11y/base.html.j2 +++ b/nbconvert_a11y/templates/a11y/base.html.j2 @@ -13,6 +13,8 @@ the notebook experiennce from browse to edit/focus mode. {% set title = nb.metadata.get('title', resources['metadata']['name']) | escape_html_keep_quotes %} {%- block html_head_js -%} +{% if nb.metadata.description %} +{% endif %} {# sa11y needs to be loaded before requirejs #} {% if resources.include_sa11y %}{% include "a11y/components/sa11y.html.j2"%}{% endif %} @@ -24,7 +26,7 @@ the notebook experiennce from browse to edit/focus mode. {% include "a11y/static/style.css" %} {% for theme in ["light", "dark"] %} - {% endfor %} @@ -33,12 +35,21 @@ the notebook experiennce from browse to edit/focus mode. {% block extra_css %}{% endblock %} {% block body_header %} +{%- set title = nb.metadata.get("title") -%} +{%- set description = nb.metadata.get("description") -%} +{% set describedby = "nb-ct-total nb-cells-label nb-ct-code nb-state nb-ct-loc nb-loc-label +nb-ct-outputs nb-outputs-label"%}