Skip to content

full screen visualization¤

we don't focus much on mobile data science, but maybe we should. after testing accessible table visualizations on mobile i realize there a huge opportunity to have a tactile experience with data. there are some style and UX choices we can make to improve these outcome. one challenge i've had it visualization moving while i am touching them. i think a good solution for this challenge is the ability to full screen a visualization and restrict the non-ground in the figure.

ultimately, the fullscreen transitions our visual and tactile landscape from fixed dimensions to fluid dimensions. an secondary outcome of the fullscreen mode is that it reduces the visual cognitive accessibility of the content.

the dialog technique sticks more strictly to the cognitive acessibility of the content.

both techniques are explored. these examples might lack keyboard accessibility with exiting fullscreen mode or dialogs. the Esc should work.

javascript fullscreen api approach¤

    import nbconvert_a11y.table
    df = DataFrame(index=numpy.arange(100) * numpy.pi / 180 * 10)
    table = df.assign(x=df.index.map(numpy.cos), y=df.index.map(numpy.sin)).table( options=dict(max_rows=100))
    table.html.table.attrs.update({"class": "xy"})
    table.html.form.append(
        DataFrame.table.new("button", "fullscreen", onclick="event.preventDefault(); this.parentElement.parentElement.querySelector(`table`).requestFullscreen()")
    )
    before = table.html.__copy__()
    table
pandas dataframe with 100 rows, 2 columns with 1 index levels and 1 columns levels.
None x y
0.000 1.000 0.000
0.175 0.985 0.174
0.349 0.940 0.342
0.524 0.866 0.500
0.698 0.766 0.643
0.873 0.643 0.766
1.047 0.500 0.866
1.222 0.342 0.940
1.396 0.174 0.985
1.571 0.000 1.000
1.745 -0.174 0.985
1.920 -0.342 0.940
2.094 -0.500 0.866
2.269 -0.643 0.766
2.443 -0.766 0.643
2.618 -0.866 0.500
2.793 -0.940 0.342
2.967 -0.985 0.174
3.142 -1.000 0.000
3.316 -0.985 -0.174
3.491 -0.940 -0.342
3.665 -0.866 -0.500
3.840 -0.766 -0.643
4.014 -0.643 -0.766
4.189 -0.500 -0.866
4.363 -0.342 -0.940
4.538 -0.174 -0.985
4.712 -0.000 -1.000
4.887 0.174 -0.985
5.061 0.342 -0.940
5.236 0.500 -0.866
5.411 0.643 -0.766
5.585 0.766 -0.643
5.760 0.866 -0.500
5.934 0.940 -0.342
6.109 0.985 -0.174
6.283 1.000 -0.000
6.458 0.985 0.174
6.632 0.940 0.342
6.807 0.866 0.500
6.981 0.766 0.643
7.156 0.643 0.766
7.330 0.500 0.866
7.505 0.342 0.940
7.679 0.174 0.985
7.854 0.000 1.000
8.029 -0.174 0.985
8.203 -0.342 0.940
8.378 -0.500 0.866
8.552 -0.643 0.766
8.727 -0.766 0.643
8.901 -0.866 0.500
9.076 -0.940 0.342
9.250 -0.985 0.174
9.425 -1.000 0.000
9.599 -0.985 -0.174
9.774 -0.940 -0.342
9.948 -0.866 -0.500
10.123 -0.766 -0.643
10.297 -0.643 -0.766
10.472 -0.500 -0.866
10.647 -0.342 -0.940
10.821 -0.174 -0.985
10.996 -0.000 -1.000
11.170 0.174 -0.985
11.345 0.342 -0.940
11.519 0.500 -0.866
11.694 0.643 -0.766
11.868 0.766 -0.643
12.043 0.866 -0.500
12.217 0.940 -0.342
12.392 0.985 -0.174
12.566 1.000 -0.000
12.741 0.985 0.174
12.915 0.940 0.342
13.090 0.866 0.500
13.265 0.766 0.643
13.439 0.643 0.766
13.614 0.500 0.866
13.788 0.342 0.940
13.963 0.174 0.985
14.137 0.000 1.000
14.312 -0.174 0.985
14.486 -0.342 0.940
14.661 -0.500 0.866
14.835 -0.643 0.766
15.010 -0.766 0.643
15.184 -0.866 0.500
15.359 -0.940 0.342
15.533 -0.985 0.174
15.708 -1.000 0.000
15.882 -0.985 -0.174
16.057 -0.940 -0.342
16.232 -0.866 -0.500
16.406 -0.766 -0.643
16.581 -0.643 -0.766
16.755 -0.500 -0.866
16.930 -0.342 -0.940
17.104 -0.174 -0.985
17.279 0.000 -1.000
min -1.000 -1.000
max 1.000 1.000
diff 2.000 2.000

%%
## dialog approach

the dialog approach reduces the axcessibility object model to the modal area only.
as a result, we reduce the palette of visual and nonvisual controls. this is good for focus.

    new = before.__copy__()
    new.append(DataFrame.table.new("dialog", before.figure.__copy__(), **{"class": "xy"}))
    new.table.decompose()
    new.form.insert_before(new.dialog)
    new.button.attrs.update(onclick="event.preventDefault(); this.parentElement.parentElement.querySelector(`dialog`).showModal()")
    HTML(new)
pandas dataframe with 100 rows, 2 columns with 1 index levels and 1 columns levels.
Nonexy
0.0001.0000.000
0.1750.9850.174
0.3490.9400.342
0.5240.8660.500
0.6980.7660.643
0.8730.6430.766
1.0470.5000.866
1.2220.3420.940
1.3960.1740.985
1.5710.0001.000
1.745-0.1740.985
1.920-0.3420.940
2.094-0.5000.866
2.269-0.6430.766
2.443-0.7660.643
2.618-0.8660.500
2.793-0.9400.342
2.967-0.9850.174
3.142-1.0000.000
3.316-0.985-0.174
3.491-0.940-0.342
3.665-0.866-0.500
3.840-0.766-0.643
4.014-0.643-0.766
4.189-0.500-0.866
4.363-0.342-0.940
4.538-0.174-0.985
4.712-0.000-1.000
4.8870.174-0.985
5.0610.342-0.940
5.2360.500-0.866
5.4110.643-0.766
5.5850.766-0.643
5.7600.866-0.500
5.9340.940-0.342
6.1090.985-0.174
6.2831.000-0.000
6.4580.9850.174
6.6320.9400.342
6.8070.8660.500
6.9810.7660.643
7.1560.6430.766
7.3300.5000.866
7.5050.3420.940
7.6790.1740.985
7.8540.0001.000
8.029-0.1740.985
8.203-0.3420.940
8.378-0.5000.866
8.552-0.6430.766
8.727-0.7660.643
8.901-0.8660.500
9.076-0.9400.342
9.250-0.9850.174
9.425-1.0000.000
9.599-0.985-0.174
9.774-0.940-0.342
9.948-0.866-0.500
10.123-0.766-0.643
10.297-0.643-0.766
10.472-0.500-0.866
10.647-0.342-0.940
10.821-0.174-0.985
10.996-0.000-1.000
11.1700.174-0.985
11.3450.342-0.940
11.5190.500-0.866
11.6940.643-0.766
11.8680.766-0.643
12.0430.866-0.500
12.2170.940-0.342
12.3920.985-0.174
12.5661.000-0.000
12.7410.9850.174
12.9150.9400.342
13.0900.8660.500
13.2650.7660.643
13.4390.6430.766
13.6140.5000.866
13.7880.3420.940
13.9630.1740.985
14.1370.0001.000
14.312-0.1740.985
14.486-0.3420.940
14.661-0.5000.866
14.835-0.6430.766
15.010-0.7660.643
15.184-0.8660.500
15.359-0.9400.342
15.533-0.9850.174
15.708-1.0000.000
15.882-0.985-0.174
16.057-0.940-0.342
16.232-0.866-0.500
16.406-0.766-0.643
16.581-0.643-0.766
16.755-0.500-0.866
16.930-0.342-0.940
17.104-0.174-0.985
17.2790.000-1.000
min-1.000-1.000
max1.0001.000
diff2.0002.000

dialog approach

the dialog approach reduces the axcessibility object model to the modal area only. as a result, we reduce the palette of visual and nonvisual controls. this is good for focus.

new = before.__copy__()
new.append(DataFrame.table.new("dialog", before.figure.__copy__(), **{"class": "xy"}))
new.table.decompose()
new.form.insert_before(new.dialog)
new.button.attrs.update(onclick="event.preventDefault(); this.parentElement.parentElement.querySelector(`dialog`).showModal()")
HTML(new)

marginalia¤

  • i'll have to test this with the mobile emulator
  • need to think about landscape and portrait mode.

  • this approach might overlap with email too because you design for 600px, uses inline css instead

  • https://css-tricks.com/html-email-accessibility/
%%
## css

```css
:root {
    --w: 600px; --h: 400px;}
table:fullscreen, dialog.xy[open] {
    --w: 100vw; --h: 100vh;
    overflow: auto;
}
table.xy {
    box-sizing: border-box;
    height: var(--h); width: var(--w);
    display: block; position: relative;
    tbody {
        height: var(--h); width: var(--w);
        display: block;
        tr {
            --x: calc((var(--0) - var(--0-min)) / var(--0-diff) * var(--w));
            --y: calc((var(--1) - var(--1-min)) / var(--1-diff) * var(--h));
            display: block; position: absolute;
            top: var(--y); left: var(--x);
            transform: translate(-50%, -50%);
        }
    }
}
dialog.xy {
    display: contents;
    &[open] {height: 100vh; width: 100vw;}
}
.xy thead,
.xy tfoot,
.xy caption,
.nv {
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
}
```

css

:root {
    --w: 600px; --h: 400px;}
table:fullscreen, dialog.xy[open] {
    --w: 100vw; --h: 100vh;
    overflow: auto;
}
table.xy {
    box-sizing: border-box;
    height: var(--h); width: var(--w);
    display: block; position: relative;
    tbody {
        height: var(--h); width: var(--w);
        display: block;
        tr {
            --x: calc((var(--0) - var(--0-min)) / var(--0-diff) * var(--w));
            --y: calc((var(--1) - var(--1-min)) / var(--1-diff) * var(--h));
            display: block; position: absolute;
            top: var(--y); left: var(--x);
            transform: translate(-50%, -50%);
        }
    }
}
dialog.xy {
    display: contents;
    &[open] {height: 100vh; width: 100vw;}
}
.xy thead,
.xy tfoot,
.xy caption,
.nv {
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
}