HTML renderer#
HTMLRenderer produces a self-contained HTML fragment: a <div class="handcalcs"> with
its own small, scoped stylesheet. It needs no math engine, so it works in any web page,
email or app.
from handcalcs.renderers import HTMLRenderer
## Bolt group
# Shear is shared equally between the bolts.
n = 4 # bolts
V_f = 120 # kN
V_b = V_f / n
f = 1; g = 2; h = 4
if V_b <= 40:
status = "OK"
Bolt group
Shear is shared equally between the bolts.
| f | = | 1 | g | = | 2 | h | = | 4 |
<div class="handcalcs"><style>.handcalcs{--hc-accent:#0B5F66;--hc-accent-dark:#91CFD4;--hc-guide-1:#85B8BD;--hc-guide-2:#D5B386;--hc-guide-3:#A8B0C7;}:is([data-jp-theme-light="false"],.vscode-dark,.vscode-high-contrast,[data-theme="dark"]) .handcalcs{--hc-accent-text:var(--hc-accent-dark);--hc-tint:color-mix(in srgb,var(--hc-accent) 30%,transparent);--hc-highlight:color-mix(in srgb,var(--hc-accent) 38%,transparent);}.handcalcs{--hc-gap:0.35em;--hc-indent:1.5em;--hc-param-gap:2em;--hc-eq-gap:0.3em;--hc-accent-text:var(--hc-accent);--hc-tint:color-mix(in srgb,var(--hc-accent) 9%,transparent);--hc-highlight:color-mix(in srgb,var(--hc-accent) 12%,transparent);--hc-muted:color-mix(in srgb,currentColor 60%,transparent);--hc-chip:color-mix(in srgb,currentColor 6%,transparent);display:flex;flex-direction:column;gap:var(--hc-gap);line-height:1.6;font-variant-numeric:tabular-nums;}.handcalcs :where(.hc-block,.hc-body){display:flex;flex-direction:column;gap:var(--hc-gap);}.handcalcs :where(.hc-body){margin-inline-start:0.55em;padding-inline-start:calc(var(--hc-indent) - 0.55em);border-inline-start:2px solid var(--hc-guide);--hc-guide:var(--hc-guide-1);}.handcalcs :where(.hc-body .hc-body){--hc-guide:var(--hc-guide-2);}.handcalcs :where(.hc-body .hc-body .hc-body){--hc-guide:var(--hc-guide-3);}.handcalcs :where(.hc-body .hc-body .hc-body .hc-body){--hc-guide:var(--hc-guide-1);}.handcalcs :where(.hc-body .hc-body .hc-body .hc-body .hc-body){--hc-guide:var(--hc-guide-2);}.handcalcs :where(.hc-line,.hc-header,.hc-comment){margin-block:0;}.handcalcs :where(.hc-header){background:var(--hc-tint);border-radius:4px;padding:0.1em 0.5em;}.handcalcs :where(.hc-header)::before{content:"\25B8";color:var(--hc-accent-text);margin-inline-end:0.4em;}.handcalcs :where(.hc-kw){color:var(--hc-accent-text);}.handcalcs :where(.hc-result){color:var(--hc-accent-text);background:var(--hc-highlight);font-weight:600;padding:0 0.25em;border-radius:3px;-webkit-box-decoration-break:clone;box-decoration-break:clone;}.handcalcs :where(.hc-note){color:var(--hc-muted);}.handcalcs :where(.hc-check){font-weight:700;}.handcalcs :where(.hc-pass){color:#1A7F37;}.handcalcs :where(.hc-fail){color:#CF222E;}.handcalcs :where(.hc-import){align-self:start;font-family:var(--jp-code-font-family,ui-monospace,SFMono-Regular,Menlo,Consolas,monospace);font-size:0.85em;color:var(--hc-muted);background:var(--hc-chip);padding:0.05em 0.5em;border-radius:3px;}.handcalcs :where(.hc-comment,.hc-note) :where(code){font-size:0.9em;background:var(--hc-chip);padding:0 0.25em;border-radius:3px;}.handcalcs table.hc-params{margin:0;border:0;border-collapse:separate;border-spacing:0;table-layout:auto;width:auto;align-self:center;font-size:inherit;color:inherit;font-variant-numeric:tabular-nums;background:var(--hc-tint);border-radius:4px;padding:0.1em 0.6em;}.handcalcs table.hc-params.hc-multiline{align-self:start;background:none;border-radius:0;padding:0 0 0 0.5em;border-inline-start:2px solid var(--hc-accent-text);}.handcalcs table.hc-params tr:nth-child(n){background:none;}.handcalcs table.hc-params td{padding:0;border:0;vertical-align:baseline;line-height:inherit;white-space:normal;max-width:none;}.handcalcs table.hc-params td.hc-param-id{text-align:right;padding-inline-end:var(--hc-eq-gap);}.handcalcs table.hc-params td.hc-param-eq{text-align:center;}.handcalcs table.hc-params td.hc-param-val{text-align:left;padding-inline-start:var(--hc-eq-gap);}.handcalcs table.hc-params td.hc-param-gap{width:var(--hc-param-gap);}.handcalcs table.hc-params td.hc-param-comment{text-align:left;padding-inline-start:var(--hc-eq-gap);}.handcalcs :where(.hc-pre){white-space:pre;}.handcalcs :where(.hc-matrix){display:inline-table;vertical-align:middle;border-inline:1px solid currentColor;border-radius:0.2em;padding:0 0.15em;}.handcalcs :where(.hc-matrix-row){display:table-row;}.handcalcs :where(.hc-matrix-cell){display:table-cell;text-align:right;padding:0 0.3em;line-height:1.2;}</style><h1>Bolt group</h1><p class="hc-comment">Shear is shared equally between the bolts.</p><div class="hc-line">n = 4 <span class="hc-note">(bolts)</span></div><div class="hc-line">V<sub>f</sub> = 120 <span class="hc-note">(kN)</span></div><div class="hc-line">V<sub>b</sub> = V<sub>f</sub> / n = 120 / 4 = <span class="hc-result">30</span></div><table class="hc-params"><tr><td class="hc-param-id">f</td><td class="hc-param-eq">=</td><td class="hc-param-val">1</td><td class="hc-param-comment"></td><td class="hc-param-gap"></td><td class="hc-param-id">g</td><td class="hc-param-eq">=</td><td class="hc-param-val">2</td><td class="hc-param-comment"></td><td class="hc-param-gap"></td><td class="hc-param-id">h</td><td class="hc-param-eq">=</td><td class="hc-param-val">4</td><td class="hc-param-comment"></td></tr></table><div class="hc-block"><div class="hc-header"><span class="hc-kw">Since</span> (V<sub>b</sub> ≤ 40) → (30 ≤ 40) <span class="hc-kw">is True:</span></div><div class="hc-body"><div class="hc-line">status = “OK”</div></div></div></div>
A fuller example#
Headings and prose, a parameter grid, a long equation broken with -m, a governing
branch, and a pass/fail check, all in one calculation:
## Steel beam
# Lateral-torsional buckling resistance of a simply supported span.
# hc: -h -c 3
E = 200000 # MPa
G = 77000 # MPa
L = 6000 # mm
I_y = 160e6 # mm⁴
J = 133e3 # mm⁴
C_w = 200e9 # mm⁶
### Elastic critical moment
M_u = pi / L * sqrt(E * I_y * G * J + (pi * E / L)**2 * I_y * C_w) # hc: -m
M_p = 285e6 # N mm
if M_u > 0.67 * M_p:
M_r = 1.15 * 0.9 * M_p * (1 - 0.28 * M_p / M_u) # hc: -m
else:
M_r = 0.9 * M_u
M_f = 210e6 # N mm
M_f <= M_r # hc: --check
Steel beam
Lateral-torsional buckling resistance of a simply supported span.
| E | = | 200000 | (MPa) | G | = | 77000 | (MPa) | L | = | 6000 | (mm) | ||
| Iy | = | 1.6e+08 | (mm⁴) | J | = | 1.33e+05 | (mm⁴) | Cw | = | 2e+11 | (mm⁶) |
Elastic critical moment
<div class="handcalcs"><style>.handcalcs{--hc-accent:#0B5F66;--hc-accent-dark:#91CFD4;--hc-guide-1:#85B8BD;--hc-guide-2:#D5B386;--hc-guide-3:#A8B0C7;}:is([data-jp-theme-light="false"],.vscode-dark,.vscode-high-contrast,[data-theme="dark"]) .handcalcs{--hc-accent-text:var(--hc-accent-dark);--hc-tint:color-mix(in srgb,var(--hc-accent) 30%,transparent);--hc-highlight:color-mix(in srgb,var(--hc-accent) 38%,transparent);}.handcalcs{--hc-gap:0.35em;--hc-indent:1.5em;--hc-param-gap:2em;--hc-eq-gap:0.3em;--hc-accent-text:var(--hc-accent);--hc-tint:color-mix(in srgb,var(--hc-accent) 9%,transparent);--hc-highlight:color-mix(in srgb,var(--hc-accent) 12%,transparent);--hc-muted:color-mix(in srgb,currentColor 60%,transparent);--hc-chip:color-mix(in srgb,currentColor 6%,transparent);display:flex;flex-direction:column;gap:var(--hc-gap);line-height:1.6;font-variant-numeric:tabular-nums;}.handcalcs :where(.hc-block,.hc-body){display:flex;flex-direction:column;gap:var(--hc-gap);}.handcalcs :where(.hc-body){margin-inline-start:0.55em;padding-inline-start:calc(var(--hc-indent) - 0.55em);border-inline-start:2px solid var(--hc-guide);--hc-guide:var(--hc-guide-1);}.handcalcs :where(.hc-body .hc-body){--hc-guide:var(--hc-guide-2);}.handcalcs :where(.hc-body .hc-body .hc-body){--hc-guide:var(--hc-guide-3);}.handcalcs :where(.hc-body .hc-body .hc-body .hc-body){--hc-guide:var(--hc-guide-1);}.handcalcs :where(.hc-body .hc-body .hc-body .hc-body .hc-body){--hc-guide:var(--hc-guide-2);}.handcalcs :where(.hc-line,.hc-header,.hc-comment){margin-block:0;}.handcalcs :where(.hc-header){background:var(--hc-tint);border-radius:4px;padding:0.1em 0.5em;}.handcalcs :where(.hc-header)::before{content:"\25B8";color:var(--hc-accent-text);margin-inline-end:0.4em;}.handcalcs :where(.hc-kw){color:var(--hc-accent-text);}.handcalcs :where(.hc-result){color:var(--hc-accent-text);background:var(--hc-highlight);font-weight:600;padding:0 0.25em;border-radius:3px;-webkit-box-decoration-break:clone;box-decoration-break:clone;}.handcalcs :where(.hc-note){color:var(--hc-muted);}.handcalcs :where(.hc-check){font-weight:700;}.handcalcs :where(.hc-pass){color:#1A7F37;}.handcalcs :where(.hc-fail){color:#CF222E;}.handcalcs :where(.hc-import){align-self:start;font-family:var(--jp-code-font-family,ui-monospace,SFMono-Regular,Menlo,Consolas,monospace);font-size:0.85em;color:var(--hc-muted);background:var(--hc-chip);padding:0.05em 0.5em;border-radius:3px;}.handcalcs :where(.hc-comment,.hc-note) :where(code){font-size:0.9em;background:var(--hc-chip);padding:0 0.25em;border-radius:3px;}.handcalcs table.hc-params{margin:0;border:0;border-collapse:separate;border-spacing:0;table-layout:auto;width:auto;align-self:center;font-size:inherit;color:inherit;font-variant-numeric:tabular-nums;background:var(--hc-tint);border-radius:4px;padding:0.1em 0.6em;}.handcalcs table.hc-params.hc-multiline{align-self:start;background:none;border-radius:0;padding:0 0 0 0.5em;border-inline-start:2px solid var(--hc-accent-text);}.handcalcs table.hc-params tr:nth-child(n){background:none;}.handcalcs table.hc-params td{padding:0;border:0;vertical-align:baseline;line-height:inherit;white-space:normal;max-width:none;}.handcalcs table.hc-params td.hc-param-id{text-align:right;padding-inline-end:var(--hc-eq-gap);}.handcalcs table.hc-params td.hc-param-eq{text-align:center;}.handcalcs table.hc-params td.hc-param-val{text-align:left;padding-inline-start:var(--hc-eq-gap);}.handcalcs table.hc-params td.hc-param-gap{width:var(--hc-param-gap);}.handcalcs table.hc-params td.hc-param-comment{text-align:left;padding-inline-start:var(--hc-eq-gap);}.handcalcs :where(.hc-pre){white-space:pre;}.handcalcs :where(.hc-matrix){display:inline-table;vertical-align:middle;border-inline:1px solid currentColor;border-radius:0.2em;padding:0 0.15em;}.handcalcs :where(.hc-matrix-row){display:table-row;}.handcalcs :where(.hc-matrix-cell){display:table-cell;text-align:right;padding:0 0.3em;line-height:1.2;}</style><h1>Steel beam</h1><p class="hc-comment">Lateral-torsional buckling resistance of a simply supported span.</p><table class="hc-params"><tr><td class="hc-param-id">E</td><td class="hc-param-eq">=</td><td class="hc-param-val">200000</td><td class="hc-param-comment"><span class="hc-note">(MPa)</span></td><td class="hc-param-gap"></td><td class="hc-param-id">G</td><td class="hc-param-eq">=</td><td class="hc-param-val">77000</td><td class="hc-param-comment"><span class="hc-note">(MPa)</span></td><td class="hc-param-gap"></td><td class="hc-param-id">L</td><td class="hc-param-eq">=</td><td class="hc-param-val">6000</td><td class="hc-param-comment"><span class="hc-note">(mm)</span></td></tr><tr><td class="hc-param-id">I<sub>y</sub></td><td class="hc-param-eq">=</td><td class="hc-param-val">1.6e+08</td><td class="hc-param-comment"><span class="hc-note">(mm⁴)</span></td><td class="hc-param-gap"></td><td class="hc-param-id">J</td><td class="hc-param-eq">=</td><td class="hc-param-val">1.33e+05</td><td class="hc-param-comment"><span class="hc-note">(mm⁴)</span></td><td class="hc-param-gap"></td><td class="hc-param-id">C<sub>w</sub></td><td class="hc-param-eq">=</td><td class="hc-param-val">2e+11</td><td class="hc-param-comment"><span class="hc-note">(mm⁶)</span></td></tr></table><h2>Elastic critical moment</h2><div class="hc-line">M<sub>u</sub> = π / L · √(E · I<sub>y</sub> · G · J + (π · E / L)<sup>2</sup> · I<sub>y</sub> · C<sub>w</sub>) = 3.1416 / 6000 · √(200000 · 1.6e+08 · 77000 · 1.33e+05 + (3.1416 · 200000 / 6000)<sup>2</sup> · 1.6e+08 · 2e+11) = <span class="hc-result">4.3134e+08</span></div><div class="hc-line">M<sub>p</sub> = 2.85e+08 <span class="hc-note">(N mm)</span></div><div class="hc-block"><div class="hc-header"><span class="hc-kw">Since</span> (M<sub>u</sub> > 0.67 · M<sub>p</sub>) → (4.3134e+08 > 0.67 · 2.85e+08) <span class="hc-kw">is True:</span></div><div class="hc-body"><div class="hc-line">M<sub>r</sub> = 1.15 · 0.9 · M<sub>p</sub> · (1 - 0.28 · M<sub>p</sub> / M<sub>u</sub>) = 1.15 · 0.9 · 2.85e+08 · (1 - 0.28 · 2.85e+08 / 4.3134e+08) = <span class="hc-result">2.404e+08</span></div></div></div><div class="hc-line">M<sub>f</sub> = 2.1e+08 <span class="hc-note">(N mm)</span></div><div class="hc-line">M<sub>f</sub> ≤ M<sub>r</sub> = 2.1e+08 ≤ 2.404e+08 <span class="hc-check hc-pass">✓</span></div></div>
Using the output#
Insert the string anywhere in a page. It inherits the page’s font and color, and its styles never override yours (see Styling the HTML output).
from handcalcs import HandCalcs
from handcalcs.renderers import HTMLRenderer
fragment = HandCalcs(HTMLRenderer())(source)
page = f"<html><body><main>{fragment}</main></body></html>"
For a page of its own, give a destination. The file is a complete HTML page
(<!DOCTYPE html>, a <head> with the character set and a title, the calculation in
the <body>), from the renderer’s preamble and postamble:
HandCalcs(HTMLRenderer())(source, destination="beam.html")
In Jupyter, %hc_default_renderer html displays it with IPython.display.HTML.
Output structure#
<div class="handcalcs">
<style>/* scoped rules */</style>
<h1>Bolt group</h1>
<p class="hc-comment">Shear is shared equally between the bolts.</p>
<div class="hc-line">n = 4 (bolts)</div>
<table class="hc-params">...</table>
<div class="hc-block">
<div class="hc-header">Since ... is True:</div>
<div class="hc-body">
<div class="hc-line">status = "OK"</div>
</div>
</div>
</div>
Class |
Element |
|---|---|
|
The container. Holds the CSS custom properties. |
|
One rendered line (a calculation, parameter or import). |
|
A paragraph of comment lines. |
|
An |
|
A parameter grid (a |
|
Added to the table of a |
|
Pre-formatted text (whitespace kept). |
Headings are <h1> (for ##) to <h6>. The headings and text sizes come from the host
page.
Conventions#
Names: Greek letters as Unicode characters;
_makes a<sub>subscript, and further subscripts follow after a comma (N<sub>pl,Rd</sub>; nested with thesubscriptsoption);__is a space,___a literal underscore.hat,bar,dot,ddot,primeandstaradd a hat, an overbar, a dot, two dots, a prime or a superscript*to the part before them. All of this is onename:symrule,swap_identifier. See naming variables.Operators:
*→·,**→<sup>,sqrt→√,<=→≤.Numbers use the format code as-is (
1.2e+05stays as written). Strings are shown in curly quotes and HTML-escaped.