Tab Container Demo
by Nicholas Berlette
HTML
<tab-container>
<div role="tablist">
<button type="button" id="tab-one" role="tab" tabindex="0" aria-selected="true"><span>Input (code)</span></button>
<button type="button" id="tab-two" role="tab" tabindex="-1"><span>Output (SVG)</span></button>
<button type="button" id="tab-three" role="tab" tabindex="-1"><span>About</span></button>
</div>
<div role="tabpanel" aria-labelledby="tab-one" id="panel-one">
<textarea id="input" class="input" style="width:90%; height:125px;border-color:#aaa;border-radius:4px;margin:1em 4%;"></textarea>
<button type="button" style="padding:0.4rem 0.8rem;margin:0 1.2rem 1rem;" class="render-svg">Render SVG</button>
<br><br>
<div id="output-rendered"></div>
</div>
<div role="tabpanel" aria-labelledby="tab-two" id="panel-two" hidden>
<pre><code id="output">// Enter some code in the previous panel and it will be rendered here to an SVG!</code></pre>
</div>
<div role="tabpanel" aria-labelledby="tab-three" id="panel-three" hidden>
<pre><code>Uses shiki to highlight and render code</code></pre>
</div>
</tab-container>
<script src='https://unpkg.com/[email protected]'></script>
<script src='https://unpkg.com/[email protected]'></script>
<script>
const $ = document.querySelector.bind(document);
const $$ = document.querySelectorAll.bind(document);
let highlighter, svgRenderer;
document.querySelector('.render-svg').addEventListener("click", async () => {
highlighter ??= await shiki.getHighlighter({
theme: 'nord',
preload: ["typescript", "ts", "js", "tsx", "jsx", "html", "vue-html", "xml"],
});
svgRenderer ??= await shiki.getSVGRenderer({
bg: '#2E3440',
fontFamily: 'IBM Plex Mono',
fontSize: 14
});
const code = $('#input').textContent;
if (code.length > 0) {
const tokens = highlighter.codeToThemedTokens(code, 'typescript');
const out = await svgRenderer.renderToSVG(tokens);
$('#panel-two').innerHTML = out;
$('#output-rendered').innerHTML =...
CSS
body {
max-width: 40em;
margin: 5rem auto 2rem auto;
font-family: sans-serif;
font-size: 0.8em;
}
pre, code {
font-family: 'Operator Mono SSm Lig', 'Operator Mono Lig', 'Operator Mono', 'Dank Mono', 'MonoLisa', 'Fira Code', 'Roboto Mono', 'Ubuntu Mono', monospace !important;
font-size: 1.05em;
}
/* Borrowed from https://www.w3.org/TR/wai-aria-practices/examples/tabs/tabs-1/tabs.html */
[role="tablist"] {
margin: 0 0 -.1em 1em;
overflow: visible;
}
[role="tab"] {
position: relative;
margin: 0;
padding: .45em 0.85em;
border: 1px solid hsl(219, 1%, 72%);
border-radius: .23em .23em 0 0;
box-shadow: 0 0 .4em hsl(219, 1%, 85%);
overflow: visible;
font-family: system-ui, sans-serif !important;
letter-spacing: -0.015em;
font-weight: bold;
font-size: inherit;
background: hsl(220, 20%, 94%);
transition: all 0.4s;
cursor: pointer;
text-align: center;
}
[role="tab"] > span {
text-align: center;
margin: 0 10px;
}
/* [role="tab"] > span::after {
background-color: #222;
mask-image: url("https://icns.imgix.net/cross.svg");
-webkit-mask-image: url("https://icns.imgix.net/x.svg?stroke=1px");
content: '';
width: 8px;
height: 8px;
position: absolute;
top: 55%;
right: 10px;
transform: translate(0, -50%);
border-radius: 50%;
} */
/* [role="tab"]:hover > span::after {
outline: 4px solid #fff;
} */
[role="tab"]:not([aria-selected="true"]):not(:focus):not(:hover):not(:active) {
color: #0008;
}
[role="tab"]::before {
border-radius: 8px 8px 0 0;
border-top: 3px solid #6669;
position: absolute;
left: -1px;
right: -1px;
bottom: 100%;
transition: all 0.33s;
content: '';
}
[role="tab"]:hover::before,
[role="tab"]:focus::before,
[role="tab"][aria-selected="true"]::before {
border-top-color: hsl(20, 80%, 98%);
}
[role="tab"][aria-selected="true"] {
border-radius: 0;
background: hsl(220, 43%, 99%);
outline: 0;
color: #000f;
}
[role="tab"][aria-selected="true"]:not(:focus):not(:hover)::before {
border-top-color: hsl(218,...