Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improvements to styles and implementations #41

Merged
merged 34 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
27ff496
reconfigure post processing
tonyfast Feb 20, 2024
fca93b5
tighten css display
tonyfast Feb 20, 2024
c48b202
toc, summary template updates
tonyfast Feb 20, 2024
c4ec723
rm redundant toc entries
tonyfast Feb 20, 2024
d36cdbc
header and toc resources can be defined ahead fo time
tonyfast Feb 21, 2024
2e20190
improve toc creaTION
tonyfast Feb 21, 2024
ca84a33
add activity log label to dom
tonyfast Feb 21, 2024
e02db9a
change the line height so pre respects line height
tonyfast Feb 21, 2024
d811bdc
change include logic statements so they are slightly less confusing
tonyfast Feb 21, 2024
3b0ce77
change how anchor is exposed
tonyfast Feb 22, 2024
afe0aab
tweaks to overflow
tonyfast Feb 22, 2024
c59904d
add description to meta
tonyfast Feb 22, 2024
83cb2f5
triple A text
tonyfast Feb 22, 2024
fd34370
comment
tonyfast Feb 22, 2024
f5e5b48
rm bad css
tonyfast Feb 22, 2024
057292a
per request add more font size options to the settings
tonyfast Feb 23, 2024
3000734
rm aria describedby
tonyfast Feb 23, 2024
04cb854
add the ability to suppress scrolling for better digital magnifier ex…
tonyfast Feb 23, 2024
13b2c86
configure visibility switch
tonyfast Feb 23, 2024
f5bed67
add fullscreen button
tonyfast Feb 24, 2024
e7d5835
better dark mode handling
tonyfast Feb 24, 2024
7c1f4fb
name horizontal scrolling and change impl accordingly
tonyfast Feb 24, 2024
6ca696b
wrap pre for easier navigation without horizontal scroling
tonyfast Feb 24, 2024
148923f
default to showing visibility widget
tonyfast Feb 25, 2024
dfd7e3d
always include slide type
tonyfast Feb 25, 2024
5169488
wrap textarea
tonyfast Feb 25, 2024
b74271b
fullscreen button
tonyfast Feb 25, 2024
6b67aec
textarea resize
tonyfast Feb 25, 2024
eccb299
with resize fix
tonyfast Feb 26, 2024
dfdb5e2
<fit cell width when horizotnally expanded
tonyfast Feb 26, 2024
d4066a1
reorder options and leave note that this should be configurable
tonyfast Feb 26, 2024
043306d
more scroll handling. what a suck of a day
tonyfast Feb 26, 2024
cdb717c
tighten up execution counts
tonyfast Feb 28, 2024
5a0e79c
fix bad label, tests run local
tonyfast Mar 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 38 additions & 43 deletions nbconvert_a11y/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
)
Expand Down Expand Up @@ -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
Expand All @@ -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")


Expand Down Expand Up @@ -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(
Expand All @@ -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):
Expand All @@ -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"""<a href="#{id}">{header.string}</a>""").body.a
link = soupify(f"""<a href="#{id}">{header.text}</a>""").body.a
header.clear()
header.append(link)

Expand Down Expand Up @@ -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
Expand Down
26 changes: 17 additions & 9 deletions nbconvert_a11y/templates/a11y/base.html.j2
Original file line number Diff line number Diff line change
Expand Up @@ -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 %}
<meta name="description" content="{{nb.metadata.description}}">{% endif %}
<meta name="color-scheme" content="light">
{# sa11y needs to be loaded before requirejs #}
{% if resources.include_sa11y %}{% include "a11y/components/sa11y.html.j2"%}{% endif %}
Expand All @@ -24,7 +26,7 @@ the notebook experiennce from browse to edit/focus mode.
{% include "a11y/static/style.css" %}
</style>
{% for theme in ["light", "dark"] %}
<style type="text/css" id="nb-{{theme}}-theme" media="{% if loop.index - 1%}not {% endif %}screen">
<style type="text/css" id="nb-{{theme}}-highlight" media="{% if loop.index - 1%}not {% endif %}screen">
{% include "a11y/static/theme/{}.css" .format(resources.code_theme.format(theme)) %}
</style>
{% endfor %}
Expand All @@ -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"%}

<body class="wcag-{{resources.wcag_priority.lower()}}">
<header>
<a href="#1" id="TOP">skip to main content</a>
{{site_navigation | default("")}}
{%- if resources.include_summary -%}{% include "a11y/components/nb-summary.html.j2"%}{%- endif -%}
<a href="#1" id="TOP">skip to main content</a><br />
{% if resources.include_settings %}
<button onclick="openDialog()" aria-controls="nb-settings" accesskey=",">settings</button>
{% endif %}
<button onclick="fullScreen()" id="nb-fullscreen">fullscreen</button>

{{resources.header | default("")}}
{%- if resources.include_summary -%}{% include "a11y/components/nb-summary.html.j2" %}{%- endif -%}
{%- if resources.include_toc -%}{% include "a11y/components/toc.html.j2" %}{%- endif -%}
<dialog id="nb-metadata">
<form method="dialog">
Expand All @@ -58,12 +69,10 @@ the notebook experiennce from browse to edit/focus mode.
{# skip to top is needed for long notebooks.
it is difficult to access for keyboard users. #}
<footer>
{{resources.footer}}
{% include "a11y/components/nb-toolbar.html.j2" %}
{# make the individual settings discoverable before all the settings
when using shift+tab #}
{% if resources.include_settings %}
<button onclick="openDialog()" aria-controls="nb-settings" accesskey=",">settings</button>
{% endif %}
{% if resources.include_visibility %}<button aria-controls="nb-visibility-dialog" onclick="openDialog()"
accesskey="-">show/hide</button>
{% endif %}
Expand All @@ -75,8 +84,7 @@ the notebook experiennce from browse to edit/focus mode.
{% if resources.include_visibility %}{% include "a11y/components/visibility.html.j2"%}{% endif %}
{% if resources.include_help %}{% include "a11y/components/help.html.j2" %}{% endif %}
{% if resources.include_axe %}{% include "a11y/components/audit.html.j2" %}{% endif %}
{{footer}}
{{activity_log()}}
{{activity_log(id=True)}}
<a href="#TOP" accesskey="0">skip to top</a>
</footer>
{% if resources.include_upload %}{% block template_element %}{% endblock %}{% endif %}
Expand Down
34 changes: 20 additions & 14 deletions nbconvert_a11y/templates/a11y/components/cell.html.j2
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@
{% from "a11y/components/displays.html.j2" import cell_display_priority with context %}

{% macro cell_anchor(i, cell_type, execution_count=None, outputs=None, hidden=False)%}
{% if not resources.exclude_anchors_links %}
<a class="nb-anchor{% if resources.hide_anchor_links%} visually-hidden{% endif %}" href="#{{i}}" id="{{i}}"
aria-labelledby="nb-cell-label {{i}}" {% if resources.accesskey_navigation and isinstance(i, int) and (i < 10)
%}accesskey="{{i}}" {% endif %} aria-describedby="nb-{{cell_type}}-label nb-cell-label {% if cell_type != "
markdown".strip() %}cell-{{i}}-loc nb-loc-label{% endif %}{% if cell_type==" code" .strip() and execution_count %}
cell-{{i}}-outputs-len{% endif %}" {{hide(hidden)}}>{{i}}</a>
{% endif %}
cell-{{i}}-outputs-len{% endif %}" {{hide(hidden or resources.exclude_anchor_links)}}>{{i}}</a>
{% endmacro %}

{% macro cell_form(i, cell_type, hidden=True) %}
{# the cell form acts a formal reference for each cell. as a form, each cell can handle a submission process
that would include talking to the kernel. #}
<form class="nb-toolbar" id="cell-{{i}}" name="cell-{{i}}" aria-labelledby="cell-{{i}}-source-label" {{hide(hidden)}}>
<form class="nb-toolbar" id="cell-{{i}}" name="cell-{{i}}" aria-labelledby="cell-{{i}}-exec-label" {{hide(hidden)}}>
<fieldset>
<legend>actions</legend>
<button type="submit">Run Cell</button>
Expand All @@ -38,20 +36,21 @@ that would include talking to the kernel. #}
{% endmacro %}

{% macro cell_execution_count(i, execution_count, hidden=False) %}
<label id="cell-{{i}}-source-label" class="nb-execution_count" {{hide(hidden)}}>
<span>{{resources.prompt_in}}</span>
<label class="nb-execution_count" {{hide(hidden)}}>
<span id="cell-{{i}}-in-label">{{resources.prompt_in}}</span>
<span aria-hidden="true">{{resources.prompt_left}}</span>
<span>{{execution_count}}</span>
<span id="cell-{{i}}-exec-label">{{execution_count}}</span>
<span aria-hidden="true">{{resources.prompt_right}}</span>
</label>
{% endmacro %}


{% macro cell_source(i, source, cell_type, execution_count, hidden=False) %}
<textarea class="nb-source" form="cell-{{i}}" id="cell-{{i}}-source" name="source"
rows="{{source.splitlines().__len__()}}" aria-labelledby="cell-{{i}}-source-label nb-source-label"
rows="{{source.splitlines().__len__()}}" aria-labelledby="cell-{{i}}-in-label cell-{{i}}-exec-label nb-source-label"
readonly>{{source}}</textarea>
<div role="group" aria-labelledby="nb-source-label" aria-roledescription="highlighted">
{# should say something like highlight source #}
<div role="group" aria-labelledby="cell-{{i}}-in-label cell-{{i}}-exec-label" aria-roledescription="highlighted">
{{highlight(source, cell_type)}}
</div>
{% endmacro %}
Expand All @@ -69,15 +68,15 @@ that would include talking to the kernel. #}
</dialog>
{% endmacro %}

{%- macro cell_output(i, cell, source, outputs, cell_type, execution_count, hidden=False) -%}
{%- macro cell_output(i, cell, source, outputs, cell_type, metadata, execution_count, hidden=False) -%}
{% set CODE = cell_type == "code" %}
{% if CODE %}
{% if execution_count %}
<span class="nb-outputs visually-hidden" id="cell-{{i}}-outputs-len">{{outputs.__len__()}} outputs.</span>
{% endif %}
{# the following span should get its own column in the table #}
<fieldset
class="nb-outputs{% if cell.metadata.get('scrolled')%} nb-scrolled{% endif %}{% if cell.metadata.get('collapsed')%} nb-collapsed{% endif %}"
class="nb-outputs{% if metadata.get('scrolled')%} nb-scrolled{% endif %}{% if metadata.get('collapsed')%} nb-collapsed{% endif %}"
{{hide(hidden)}} data-outputs="{{outputs.__len__()}}">
<legend id="cell-{{i}}-outputs-label" aria-describedby="nb-outputs-desc" {{hide(not
resources.global_content_filter.include_output_prompt)}}>
Expand All @@ -92,9 +91,16 @@ that would include talking to the kernel. #}
{{cell_display_priority(i, outputs, cell)}}
{% endif %}
</fieldset>
{% else %}
{%- else %}
{%- set html = metadata.get("data", {}).get("text/html") %}
{%- if html -%}
{# if there is a data bundle in the metadata then we can use that
instead of computing the markup #}
{{html}}
{%- else -%}
{{ markdown(source) | strip_files_prefix }}
{% endif %}
{%- endif -%}
{%- endif -%}
{%- endmacro -%}

{% macro cell_loc(i, cell, tag="span") %}
Expand All @@ -112,7 +118,7 @@ that would include talking to the kernel. #}
{{cell_cell_type(loop.index, cell.cell_type, hidden=True)}}
{{cell_source(loop.index, cell.source, cell.cell_type, cell.execution_count, hidden=cell.cell_type != "code")}}
{{cell_loc(loop.index, cell)}}
{{cell_output(loop.index, cell, cell.source, cell.outputs, cell.cell_type, cell.execution_count)}}
{{cell_output(loop.index, cell, cell.source, cell.outputs, cell.cell_type, cell.metadata, cell.execution_count)}}
{{cell_metadata(loop.index, cell.metadata, hidden=True)}}
</{{tag}}>
{% endmacro%}
2 changes: 1 addition & 1 deletion nbconvert_a11y/templates/a11y/components/core.html.j2
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ at some point effort should be put into separating context dependent and indepen
<details class="log">
<summary><span {% if id %}id="nb-activity-log-label" {% endif %}>activity log</span></summary>
</details>
<table aria-live="polite">
<table aria-live="polite" aria-labelledby="nb-activity-log-label">
<tr>
<th hidden>time</th>
<td>message</td>
Expand Down
4 changes: 0 additions & 4 deletions nbconvert_a11y/templates/a11y/components/nb-summary.html.j2
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
{%- 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"%}
<section aria-labelledby="nb-summary-label">
<details>
<summary id="nb-summary-label" aria-describedby="{{describedby}}">notebook summary</summary>
Expand Down
19 changes: 17 additions & 2 deletions nbconvert_a11y/templates/a11y/components/settings.html.j2
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ settings provides multiple screen reader navigation techniques when the dialog i

{% from "a11y/components/core.html.j2" import h, radiogroup, select, activity_log, input_number, checkbox, dialog_close
%}
{% set font_sizes = {"xx-small": "xx-small",
"x-small": "x-small",
"small": "small",
"medium": "medium",
"large": "120%",
"x-large": "150%",
"xx-large": "200%",
"xxx-large": "300%",
"xxxx-large": "400%",
"xxxxx-large": "500%"}
%}
<dialog id="nb-settings">
<form name="settings">
{{h(1, "accessibility settings")}}
Expand All @@ -13,22 +24,26 @@ settings provides multiple screen reader navigation techniques when the dialog i
<li>
{{h(2, "notebook layout")}}
<ul aria-labelledby="nb-notebook-layout-label">
{# the best implementation would make these configurable for folks who change them often. #}
<li>{{select("color scheme", {"light mode": "light", "dark mode": "dark"})}}</li>
<li>{{input_number("margin", 5, min=0, max=40, step=5, extra_title="%")}}</li>
<li>{{checkbox("horizontal scrolling", False)}}</li>
<li>{{input_number("line height", 1.5, min=0.5, max=3, step=0.1)}}</li>
{# screen overflow exists for digital magnifier users to follow long horizontal lines of text. #}
<li>{{select("cell navigation", default=resources.table_pattern.__name__.lower(), values="list table
region presentation".split(), disabled="grid
treegrid
tree".split())}}</li>
{% set priority = {"Triple A": "AAA","Double A": "AA", "Single A": "A"}%}
<li>{{select("accessibility priority", priority, resources.wcag_priority)}}</li>
<li>{{input_number("margin", 5, min=0, max=40, step=5, extra_title="%")}}</li>
<li>{{checkbox("accesskey navigation", True)}}</li>
</ul>
</li>
<li>

{{h(2, "font settings")}}
<ul aria-labelledby="nb-font-settings-label">
<li>{{select("font size", "xx-small x-small small medium large x-large xx-large".split(),
<li>{{select("font size", font_sizes,
"medium")}}</li>
<li>
{{select("font family", "serif sans-serif".split())}}
Expand Down
2 changes: 1 addition & 1 deletion nbconvert_a11y/templates/a11y/components/toc.html.j2
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
<summary id="toc"><span id="nb-toc">table of contents</span></summary>
{# the table of contents is populated in python. #}
<a id="nb-toc-start"></a>
<ol></ol>
{{resources.toc|default("")}}
</details>
</section>
Loading
Loading