a graph from a table¤
we'll use the extended chartability principles to experiment with a small graph.s
import networkx
from nbconvert_a11y.repr import get_table, TableOptions
shell.tangle.parser.fence_methods["graphviz"] = "graphviz:Source"
%%
create a graph in graphviz
    graph =\
```graphviz
graph LR {
    Compromising -- {Robust Understandable}
    Assistive -- {Perceivable Understandable}
    Flexible -- {Perceivable Operable Robust}
    Robust -- Perceivable -- Operable -- Robust [style=dashed]
    Perceivable -- Understandable -- Robust -- Perceivable [style=dashed]
}
```
    pour, caf = map(str.split, (
Percievable Operable Understandable Robust
Compromising Assistive Flexible
    ).splitlines())
then cast it to a networkx graph object 
    G = networkx.nx_pydot.read_dot(io.StringIO(graph.source))
nodes are indexes that have meaning in space. there are multiple layouts
the positions would be hidden visually and non visually
chartability = await Series(["https://raw.githubusercontent.com/Chartability/POUR-CAF/main/README.md"]).http.get()
from nbconvert_a11y.table import new
%%
    rules = chartability.apply(shell.tangle.parser.parser.render).apply(bs4.BeautifulSoup, features="lxml")[0]
    hs = "h1,h2,h3,h4,h5,h6"
    headings = collections.defaultdict(list)
    for node in rules.select(F"{hs}, :is({hs}) ~ :not({hs})"):
        if node.name in hs.split(","):
            key = node
        else:
            headings[key].append(node)
    headings = Series(headings).to_frame("body")
    headings["level"] = headings.index.map(lambda x: int(x.name.lstrip("h"))).values
    headings = headings.reset_index().set_index(headings.index.attrgetter("string").rename("Heading"))
    headings["body"] = headings["body"].apply(lambda x: new("section", *x)).apply(lambda x: new("section", *x))
    headings["description"] = headings["body"].attrgetter("string")
%%
compute different layouts for our graph
    positions = Series({
        networkx.arf_layout: networkx.arf_layout(G),
        # networkx.bipartite_layout: networkx.bipartite_layout(G, G.nodes),
        networkx.circular_layout: networkx.circular_layout(G),
        networkx.spring_layout: networkx.spring_layout(G),
        networkx.kamada_kawai_layout: networkx.kamada_kawai_layout(G),
        networkx.planar_layout: networkx.planar_layout(G),
        networkx.shell_layout: networkx.shell_layout(G),
        networkx.spectral_layout: networkx.spectral_layout(G),
        networkx.spiral_layout: networkx.spiral_layout(G),
        networkx.spring_layout: networkx.spring_layout(G),
    }).series().T
    positions.columns = positions.columns.attrgetter("__name__").str.split("_").itemgetter(0)
    positions = positions.stack().series().rename(columns=dict(zip(range(2), "xy"))).unstack()
    positions = pandas.concat(dict(info=headings.loc[positions.index]), axis=1).join(
        positions
    ).drop(columns=[("info", "body"), ("info", "index"), ("info", "level")])
    positions["info", "heuristic"] = positions.index.map(set(pour).__contains__).map(
        ["wcag", "chartability"].__getitem__
    ).values
%%
make a dataframe of the information required to present the graph nodes and edges
    df = DataFrame(index=DataFrame(G.edges).drop(columns=2).drop_duplicates().set_index([0, 1]).index.rename(list("xy")))
    df = pandas.concat([
        positions.loc[df.index.get_level_values(0)].rename(columns={"x": "x0", "y": "y0"}, level=0).drop(("info",), axis=1).reset_index(drop=True),
        positions.loc[df.index.get_level_values(1)].rename(columns={"x": "x1", "y": "y1"}, level=0).drop(("info",), axis=1).reset_index(drop=True)
    ], axis=1).set_index(df.index)
%%
show the whole thing
    section = df.pipe(get_table, id="edges", options=TableOptions(max_columns=1000))
    section.table.caption.clear()
    section.table.insert(0, new("caption", F"a graph with {G.number_of_nodes()} nodes and {G.number_of_edges()} edges."))
    section.table.attrs["class"] = "visual"
    layouts = new(
        "fieldset",
        new("legend","layouts"),
        new("label", new("input", type="checkbox", checked="", name="active", onchange=
```javascript
document.getElementById(`edges`).classList.toggle("visual", this.checked);
                         console.log(this)
```
                        ), "active"),
        *(
            new("label",
                new("input", type="radio", name="layout", **{"aria-controls": F"nodes-layout-{layout}"}, onchange=
```javascript
document.querySelectorAll(`[name=layout]`).forEach((element) => {
    var target = document.getElementById(element.getAttribute(`aria-controls`));
    target.setAttribute(`media`, element.checked ? `all` : `none`);
})
```
               ), layout) for layout in positions["x"].columns
        )   
    )
    layouts.select_one("[name=layout]").attrs["checked"] = ""
    section.form.append(layouts)
    for i, layout in enumerate(positions["x"].columns):
        section.append(new("style",
```css
#edges.visual {
    tr{
        --x0: var(--x0-%s);
        --x0-min: var(--x0-%s-min);
        --x0-max: var(--x0-%s-max);
        --x0-diff: var(--x0-%s-diff);
        --x1: var(--x1-%s);
        --x1-min: var(--x0-%s-min);
        --x1-max: var(--x0-%s-max);
        --x1-diff: var(--x0-%s-diff);
        --y0: var(--y0-%s);
        --y0-min: var(--y0-%s-min);
        --y0-max: var(--y0-%s-max);
        --y0-diff: var(--y0-%s-diff);
        --y1: var(--y1-%s);
        --y1-min: var(--y0-%s-min);
        --y1-max: var(--y0-%s-max);
        --y1-diff: var(--y0-%s-diff);
    }
}
```
        .replace("%s", layout), id=F"nodes-layout-{layout}", media=i and "none" or "all"))
    section.form.append(layouts)
    HTML(section)
%%
visual styling for the graph
```css
#edges.visual {
    --w: 400;
    --h: 400;
    width: var(--w); height: calc(200px + var(--h) * 1px);
    display: block;
    padding: 100px;
    tbody {
        position: relative;
        display: inline-block;
        width: var(--w); height: var(--h);
        tr {
            display: grid;
            grid-template-columns: 1fr;
            grid-template-rows: 1fr;
            position: absolute;
            --X0: calc((var(--x0) - var(--x0-min)) / var(--x0-diff) * var(--w));
            --Y0: calc((var(--y0) - var(--y0-min)) / var(--y0-diff) * var(--h));
            --X1: calc((var(--x1) - var(--x0-min)) / var(--x0-diff) * var(--w));
            --Y1: calc((var(--y1) - var(--y0-min)) / var(--y0-diff) * var(--h));
            --d: calc(
                pow(pow(var(--X1) - var(--X0), 2) + pow(var(--Y1) - var(--Y0), 2), .5)
            );
            --r: calc(atan2(var(--Y1) - var(--Y0), var(--X1) - var(--X0)));
            th {
                position: absolute;
                display: inline-block;
                transition: 2s all;
                &:nth-of-type(1) {
                    transform:
                        translate(
                            calc(var(--X0) * 1px), calc(var(--Y0) * 1px)
                        )
                        translate(-50%, -50%)
                        ;
                }
                &:nth-of-type(2) {
                    transform:
                        translate(
                            calc(var(--X1) * 1px), calc(var(--Y1) * 1px)
                        )
                        translate(-50%, -50%)
                        ;
                }
            }  
            td {
                display: none;
            }
            &::before {
                        content: "";
                        display: content;
                        position: absolute;
                        transform-origin: calc(1px *var(--X0)) calc(1px *var(--Y0));
                        height: 1px;
                        border: solid 2px;
                        transition: 2s all;
                        width: calc(var(--d) * 1px);
                        transform: 
                            rotate(var(--r))
                            translate(
                                calc(var(--X0) * 1px), calc(var(--Y0) * 1px)
                            )
                            ;
                    }
        }
    }
```
marginalia¤
what describes the figure and form of a graph?
the graph form has many typographical layouts that new to be considered
there are two tables, two maps, a legend of nodes, and an index of relative positions.
nodes and edges
nodes are primary foreground forms edges are second mid ground forms all other attributes are background