From b2b262c7153cdb1849b3a413401a21cd4deac283 Mon Sep 17 00:00:00 2001 From: Johannes Kimmel Date: Fri, 21 Apr 2023 18:36:06 +0200 Subject: [PATCH] minimal example --- classless-tiny.css | 78 ++++++++++ classless.css | 381 +++++++++++++++++++++++++++++++++++++++++++++ l3config.html | 17 ++ l3config.js | 198 +++++++++++++++++++++++ 4 files changed, 674 insertions(+) create mode 100644 classless-tiny.css create mode 100644 classless.css create mode 100644 l3config.html create mode 100644 l3config.js diff --git a/classless-tiny.css b/classless-tiny.css new file mode 100644 index 0000000..32c1129 --- /dev/null +++ b/classless-tiny.css @@ -0,0 +1,78 @@ +/* Classless.css v1.0 (Tiny Inline Version)*/ + +/* Tiny Reset for block elements */ +* {box-sizing: border-box; border-spacing: 0;} +header, footer, figure, table, video, details, blockquote, +ul, ol, dl, fieldset, pre, pre > code, caption, nav { + display: block; + margin: 0.5rem 0rem 1rem; + width: 100%; + overflow: auto hidden; + text-align: left; +} +video, summary, input, select { outline:none; } +html { font: 12pt/1.6 'Open Sans', Helvetica, sans-serif; } +body { + position: relative; + margin: auto; + max-width: 50rem; + color: #433; + padding: 3.1rem 0.6rem 0; + overflow-x: hidden; +} +body > footer { margin: 10rem 0rem 0rem; } + +/* clickable stuff */ +a, summary, button, select { color: #07c; cursor: pointer; } +a { text-decoration: underline solid #d1d1d1; text-underline-position: under; } +a:hover { color: #088; border-color: #088; } + +/* common */ +td, th, pre > code { padding: 0.5rem 0.8rem; } +.card, details { padding: 0 .6rem; } +code, kbd, samp { padding: 0.2rem; font: .9em/1.4 monospace; } +code, kbd, samp, nav, .card { background: #f4f5f6; border-radius: 0.3em; } +kbd, .card, details[open] { border: 1px solid #d1d1d1; } +td, th, figcaption, caption, .card { font-size: 95%; } +td, th, hr { border: 0; border-bottom: 0.1rem solid #d1d1d1; } + +/* lists */ +ul, ol { padding-top: 0.5rem; } +li, dd { margin-bottom: 0.5rem; } +dt { font-weight: bold; } + +/* headings */ +h1, h2, h3, h4, h5 { margin: 1.5em 0 0em; line-height: 1.2em; } +h1+h2, h2+h3, h3+h4, h4+h5 { margin-top: .5em; } +h1 { font-size: 2.2em; font-weight: 300; } +h2 { font-size: 2.0em; font-weight: 300; font-variant-caps: small-caps; } +h3 { font-size: 1.5em; font-weight: 400; } +h4 { font-size: 1.1em; font-weight: 700; } +h5 { font-size: 1.2em; font-weight: 400; color: #888; } +h6 { font-size: 1.0em; font-weight: 600; display: inline; } +h6 + p { display: inline; } + +/* tables */ +td, th { text-align: right; white-space: nowrap; } +td:first-child, th:first-child { text-align: left; } +tr:hover{ background-color: #f4f5f6; } + +/* figures */ +img, svg { max-width: 100%; vertical-align: text-top; } +p>img:not(:only-child) { float: right; margin: 0 0 .5em .5em; } +figure > img { display: block; margin: 0.5em auto; } +figcaption, caption { color: #888; margin-bottom: 1rem; } +figure > *:not(:last-child) { margin: 0 0 0.4rem; } + +/*code*/ +pre > code { margin: 0; border-left: 0.4rem solid #088; } + +/* misc */ +blockquote{ border-left: 0.3rem solid #d1d1d1; padding: 1rem 1.5rem; font-style: italic; } +input { font-size: 1em; } +p>cite:before { content: ' ('; } p>cite:after {content: ') '} + +/* grid */ +.row { display: flex; margin: 0.5rem -0.5rem; align-items: stretch; } +.row [class*="col"] { padding: 0 0.5rem; flex: 1 1 100%; } +@media (max-width: 40em) { .row { flex-direction: column !important; } } diff --git a/classless.css b/classless.css new file mode 100644 index 0000000..8356b44 --- /dev/null +++ b/classless.css @@ -0,0 +1,381 @@ +/* Classless.css v1.0 + +Table of Contents: + 1. Theme Settings + 2. Reset + 3. Base Style + 4. Extras (remove unwanted) + 5. Classes (remove unwanted) +*/ + +/* 1. Theme Settings ––––––––––––––––––––-–––––––––––––– */ + + +:root, html[data-theme='light'] { + --rem: 12pt; + --width: 50rem; + --navpos: absolute; /* fixed | absolute */ + --font-p: 1em/1.7 'Open Sans', 'DejaVu Sans', FreeSans, Helvetica, sans-serif; + --font-h: .9em/1.5 'Open Sans', 'DejaVu Sans', FreeSans, Helvetica, sans-serif; + --font-c: .9em/1.4 'DejaVu Sans Mono', monospace; + --border: 1px solid var(--cmed); + --ornament: "‹‹‹ ›››"; + /* foreground | background color */ + --cfg: #433; --cbg: #fff; + --cdark: #888; --clight: #f5f6f7; + --cmed: #d1d1d1; + --clink: #07c; + --cemph: #088; --cemphbg: #0881; +} + + +/* 2. Reset –––––––––––––––––––––––––––––––––––––––––––– */ + +/* reset block elements */ +* { box-sizing: border-box; border-spacing: 0; margin: 0; padding: 0;} +header, footer, figure, table, video, details, blockquote, +ul, ol, dl, fieldset, pre, pre > code, caption { + display: block; + margin: 0.5rem 0rem 1rem; + width: 100%; + overflow: auto hidden; + text-align: left; +} +video, summary, input, select { outline:none; } + +/* reset clickable things (FF Bug: select:hover prevents usage) */ +a, button, select, summary { color: var(--clink); cursor: pointer; } + + +/* 3. Base Style ––––––––––––––––––––––––––––––––––––––– */ +html { font-size: var(--rem); background: var(--cbg); } +body { + position: relative; + margin: auto; + max-width: var(--width); + font: var(--font-p); + color: var(--cfg); + padding: 3.0rem 0.6rem 0; + overflow-x: hidden; +} +body > footer { margin: 10rem 0rem 0rem; font-size: 90%; } +p { margin: .6em 0; } + +/* links */ +a[href]{ text-decoration: underline solid var(--cmed); text-underline-position: under; } +a[href^="#"] {text-decoration: none; } +a:hover, button:not([disabled]):hover, summary:hover { + filter: brightness(92%); color: var(--cemph); border-color: var(--cemph); +} + +/* lists */ +ul, ol, dl { margin: 1rem 0; padding: 0 0 0 2em; } +li:not(:last-child), dd:not(:last-child) { margin-bottom: 0.5rem; } +dt { font-weight: bold; } + +/* headings */ +h1, h2, h3, h4, h5 { margin: 1.5em 0 .5rem; font: var(--font-h); line-height: 1.2em; clear: both; } +h1+h2, h2+h3, h3+h4, h4+h5 { margin-top: .5em; padding-top: 0; } /* non-clashing headings */ +h1 { font-size: 2.2em; font-weight: 300; } +h2 { font-size: 2.0em; font-weight: 300; font-variant: small-caps; } +h3 { font-size: 1.5em; font-weight: 400; } +h4 { font-size: 1.1em; font-weight: 700; } +h5 { font-size: 1.2em; font-weight: 400; color: var(--cfg); } +h6 { font-size: 1.0em; font-weight: 700; font-style: italic; display: inline; } +h6 + p { display: inline; } + +/* tables */ +td, th { + padding: 0.5em 0.8em; + text-align: right; + border-bottom: 0.1rem solid var(--cmed); + white-space: nowrap; + font-size: 95%; +} +thead th[colspan] { padding: .2em 0.8em; text-align: center; } +thead tr:not(:only-child) td { padding: .2em 0.8em; } +thead+tbody tr:first-child td { border-top: 0.1rem solid var(--cdark); } +td:first-child, th:first-child { text-align: left; } +tr:hover{ background-color: var(--clight); } +table img { display: block; } + +/* figures */ +img, svg { max-width: 100%; vertical-align: text-top; object-fit: cover; } +p>img:not(:only-child) { float: right; margin: 0 0 .5em .5em; } +figure > img { display: inline-block; width: auto; } +figure > img:only-of-type, figure > svg:only-of-type { max-width: 100%; display: block; margin: 0 auto 0.4em; } +figcaption, caption { font: var(--font-h); color: var(--cdark); width: 100%; } +figcaption > *:first-child, caption > *:first-child { display: inline-block; margin: 0; } +figure > *:not(:last-child) { margin-bottom: 0.4rem; } + +/* code */ +pre > code { + margin: 0; + position: relative; + padding: 0.8em; + border-left: .4rem solid var(--cemph); +} +code, kbd, samp { + padding: 0.2em; + font: var(--font-c); + background: var(--clight); + border-radius: 4px; +} +kbd { border: 1px solid var(--cmed); } + +/* misc */ +blockquote { border-left: 0.4rem solid var(--cmed); padding: 0 0 0 1rem; } +time{ color: var(--cdark); } +hr { border: 0; border-top: 0.1rem solid var(--cmed); } +nav { width: 100%; background-color: var(--clight); } +::selection, mark { background: var(--clink); color: var(--cbg); } + + +/* 4. Extra Style –––––––––––––––––––––––––––––––––––––– */ + +/* Auto Numbering: figure/tables/headings/cite */ +article { counter-reset: h2 0 h3 0 tab 0 fig 0 lst 0 ref 0 eq 0; } +article figure figcaption:before { + color: var(--cemph); + counter-increment: fig; + content: "Figure " counter(fig) ": "; +} + +/* subfigures */ +figure { counter-reset: subfig 0 } +article figure figure { counter-reset: none; } +article figure > figure { display: inline-grid; width: auto; } +figure > figure:not(:last-of-type) { padding-right: 1rem; } +article figure figure figcaption:before { + counter-increment: subfig 1; + content: counter(subfig, lower-alpha) ": "; +} + +/* listings */ +article figure pre + figcaption:before { + counter-increment: lst 1; + content: "Listing " counter(lst) ": "; +} + +/* tables */ +figure > table:only-of-type { display: table; margin: 0.5em auto !important; width: fit-content; } +article figure > table caption { display: table-caption; caption-side: bottom; } +article figure > table + figcaption:before, +article table caption:before { + color: var(--cemph); + counter-increment: tab 1; + content: "Table " counter(tab) ": "; +} + +/* headings */ +article h2, h3 { position: relative; } +article h2:before, +article h3:before { + display: inline-block; + position: relative; + font-size: 0.6em; + text-align: right; + vertical-align: baseline; + left: -1rem; + width: 2.5em; + margin-left: -2.5em; +} +article h1 { counter-set: h2; } +article h2:before { counter-increment: h2; content: counter(h2) ". "; counter-set: h3; } +article h3:before { counter-increment: h3; content: counter(h2) "." counter(h3) ". ";} +@media (max-width: 60rem) { h2:before, h3:before { display: none; } } + +/* tooltip + citation */ +article p>cite:before { + padding: 0 .5em 0 0; + counter-increment: ref; content: " [" counter(ref) "] "; + vertical-align: super; font-size: .6em; +} +article p>cite > *:only-child { display: none; } +article p>cite:hover > *:only-child, +[data-tooltip]:hover:before { + display: inline-block; z-index: 40; + white-space: pre-wrap; + position: absolute; left: 1rem; right: 1rem; + padding: 1em 2em; + text-align: center; + transform:translateY( calc(-100%) ); + content: attr(data-tooltip); + color: var(--cbg); + background-color: var(--cemph); + box-shadow: 0 2px 10px 0 black; +} +[data-tooltip], article p>cite:before { + color: var(--clink); + border: .8rem solid transparent; margin: -.8rem; +} +abbr[title], [data-tooltip] { cursor: help; } + +/* navbar */ +nav+* { margin-top: 3rem; } +body>nav, header nav { + position: var(--navpos); + top: 0; left: 0; right: 0; + z-index: 41; + box-shadow: 0vw -50vw 0 50vw var(--clight), 0 calc(-50vw + 2px) 4px 50vw var(--cdark); +} +nav ul { list-style-type: none; } +nav ul:first-child { margin: 0; padding: 0; overflow: visible; } +nav ul:first-child > li { + display: inline-block; + margin: 0; + padding: 0.8rem .6rem; +} +nav ul > li > ul { + display: none; + width: auto; + position: absolute; + margin: 0.5rem 0; + padding: 1rem 2rem; + background-color: var(--clight); + border: var(--border); + border-radius: 4px; + z-index: 42; +} +nav ul > li > ul > li { white-space: nowrap; } +nav ul > li:hover > ul { display: block; } +@media (max-width: 40rem) { + nav ul:first-child > li:first-child:after { content: " \25BE"; } + nav ul:first-child > li:not(:first-child):not(.sticky) { display: none; } + nav ul:first-child:hover > li:not(:first-child):not(.sticky) { display: block; float: none !important; } +} + +/* details/cards */ +summary>* { display: inline; } +.card, details { + display: block; + margin: 0.5rem 0rem 1rem; + padding: 0 .6rem; + border-radius: 4px; + overflow: hidden; +} +.card, details[open] { outline: 1px solid var(--cmed); } +.card>img:first-child { margin: -3px -.6rem; max-width: calc(100% + 1.2rem); } +summary:hover, details[open] summary, .card>p:first-child { + box-shadow: inset 0 0 0 2em var(--clight), 0 -.8rem 0 .8rem var(--clight); +} +.hint { --cmed: var(--cemph); --clight: var(--cemphbg); background-color: var(--clight); } +.warn { --cmed: #c11; --clight: #e221; background-color: var(--clight); } + +/* big first letter */ +article > section:first-of-type > h2:first-of-type + p:first-letter, +article > h2:first-of-type + p:first-letter, .lettrine { + float: left; + font-size: 3.5em; + padding: 0.1em 0.1em 0 0; + line-height: 0.68em; + color: var(--cemph); +} + +/* ornaments */ +section:after { + display: block; + margin: 1em 0; + color: var(--cmed); + text-align: center; + font-size: 1.5em; + content: var(--ornament); +} + +/* side menu (aside is not intended for use in a paragraph!) */ +main aside { + position: absolute; + width: 8rem; right: -8.6rem; + font-size: 0.8em; line-height: 1.4em; +} +@media (max-width: 70rem) { main aside { display: none; } } + +/* forms and inputs */ +textarea, input:not([type=range]), button, select { + font: var(--font-h); + border-radius: 4px; + border: 1.5px solid var(--cmed); + padding: 0.4em 0.8em; +} +fieldset select, input:not([type=checkbox]):not([type=radio]) { + display: block; + width: 100%; + margin: 0 0 1rem; +} +button, select { + font-weight: bold; + background-color: var(--clight); + margin: .5em; + border: 1.5px solid var(--clink); +} +button { padding: 0.4em 1em; font-size: 85%; letter-spacing: 0.1em; } +button[disabled]{ color: var(--cdark); border-color: var(--cmed); } +fieldset { border-radius: 4px; border: var(--border); padding: .5em 1em;} +textarea:hover, input:not([type=checkbox]):not([type*='ra']):hover, select:hover{ + border: 1.5px solid var(--cemph); +} +textarea:focus, input:not([type=checkbox]):not([type*='ra']):focus{ + border: 1.5px solid var(--clink); + box-shadow: 0 0 5px var(--clink); +} +p>button { padding: 0 .5em; margin: 0 .5em; } +p>select { padding: 0; margin: 0 .5em; } + + +/* 5. Bootstrap-compatible classes ––––––––––––––––––––– */ + +/* grid */ +.row { display: flex; margin: 0.5rem -0.6rem; align-items: stretch; } +.row [class*="col"] { padding: 0 0.6rem; } +.row .col { flex: 1 1 100%; } +.row .col-2 { flex: 0 0 16.66%; max-width: 16.66%;} +.row .col-3 { flex: 0 0 25%; max-width: 25%;} +.row .col-4 { flex: 0 0 33.33%; max-width: 33.33%; } +.row .col-5 { flex: 0 0 41.66%; max-width: 41.66%; } +.row .col-6 { flex: 0 0 50%; max-width: 50%; } +@media (max-width: 40rem) { .row { flex-direction: column; } } + +/* align */ +.text-left { text-align: left; } +.text-right { text-align: right; } +.text-center { text-align: center; } +.float-left { float: left !important; } +.float-right { float: right !important; } +.clearfix { clear: both; } + +/* colors */ +.text-black { color: #000; } +.text-white { color: #fff; } +.text-primary { color: var(--cemph); } +.text-secondary{ color: var(--cdark); } +.bg-white { background-color: #fff; } +.bg-light { background-color: var(--clight); } +.bg-primary { background-color: var(--cemph); } +.bg-secondary{ background-color: var(--cmed); } + +/* margins */ +.mx-auto { margin-left: auto; margin-right: auto; } +.m-0 { margin: 0 !important; } +.m-1, .mx-1, .mr-1 { margin-right: 1.0rem !important; } +.m-1, .mx-1, .ml-1 { margin-left: 1.0rem !important; } +.m-1, .my-1, .mt-1 { margin-top: 1.0rem !important; } +.m-1, .my-1, .mb-1 { margin-bottom: 1.0rem !important; } + +/* pading */ +.p-0 { padding: 0 !important; } +.p-1, .px-1, .pr-1 { padding-right: 1.0rem !important; } +.p-1, .px-1, .pl-1 { padding-left: 1.0rem !important; } +.p-1, .py-1, .pt-1 { padding-top: 1.0rem !important; } +.p-1, .py-1, .pb-1 { padding-bottom: 1.0rem !important; } + +/* be print-friendly */ +@media print { + @page { margin: 1.5cm 2cm; } + html {font-size: 9pt!important; } + body { max-width: 27cm; } + p { orphans: 2; widows: 2; } + caption, figcaption { page-break-before: avoid; } + h2, h3, h4, h5 { page-break-after: avoid;} + .noprint, body>nav, section:after { display: none; } + .row { flex-direction: row; } +} \ No newline at end of file diff --git a/l3config.html b/l3config.html new file mode 100644 index 0000000..2a9f9dc --- /dev/null +++ b/l3config.html @@ -0,0 +1,17 @@ + + + + + + + + l3config + + +

l3config

+
+
+
+
+ + diff --git a/l3config.js b/l3config.js new file mode 100644 index 0000000..52d601d --- /dev/null +++ b/l3config.js @@ -0,0 +1,198 @@ +"use strict"; + +function makeFieldset(legend) { + let fieldset = document.createElement('fieldset'); + let fieldsetlegend = document.createElement('legend'); + fieldsetlegend.innerHTML = legend; + fieldset.appendChild(fieldsetlegend); + return fieldset; +} + +class L3Section { + constructor(legend, ...inputs) { + this.legend = legend; + this.inputs = inputs; + } + node() { + let fs = makeFieldset(this.legend); + let sep = undefined; + for (const input of this.inputs) { + let div = document.createElement('div'); + div.append(...input.node()); + fs.append(div); + } + return fs; + } + values() { + this.inputs.map((input) => {input.id = input.value;}) + } + render() { + const options = this.inputs + .map(input => input.option()) + .filter(option => !!option); + + if (options.length == 0) { + return undefined + } + + console.log("options:", options) + + var compiledopts = {}; + for (const option of options) { + if (!compiledopts[option.optionName]) { + compiledopts[option.optionName] = []; + } + compiledopts[option.optionName].push(option.value) + } + console.log("compiledopts:", compiledopts) + + let optstrs = []; + for (const opt in compiledopts) { + const values = compiledopts[opt]; + console.log("values:", values); + if (values.length == 1) { + optstrs.push(`option ${opt} '${values[0]}'`); + } else { + for (const value of values) { + optstrs.push(`list ${opt} '${value}'`); + } + } + } + return `config ${this.legend}\n\t` + optstrs.join('\n\t') + } +} + +class L3Input { + constructor(label, id, optionName, attrs, datalist) { + this.label = label; + this.id = id; + this.optionName = optionName; + this.attrs = attrs; + this.datalist = datalist + this.input = undefined; + } + node() { + let newLabel = document.createElement('label'); + newLabel.setAttribute('for', this.id); + newLabel.innerHTML = this.label; + + this.input = document.createElement('input'); + this.input.setAttribute('id', this.id); + for (const attr in this.attrs) { + this.input.setAttribute(attr, this.attrs[attr]); + } + + + let ret = (this.input.type === 'radio') ? [this.input, newLabel] : [newLabel, this.input]; + + if (this.datalist) { + let datalist = document.createElement('datalist'); + let datalistid = this.id + '-datalist'; + datalist.setAttribute('id', datalistid); + this.input.setAttribute('list', datalistid); + + for (const value of this.datalist) { + let option = document.createElement('option'); + option.setAttribute('value', value); + datalist.append(option); + } + + ret.push(datalist); + } + + return ret; + + } + id() { + return this.id; + } + value() { + return this.input.value; + } + option() { + switch (this.input.type) { + case 'radio': + if (!this.input.checked || this.input.value == 'undefined') return undefined; + break; + default: + if (!this.input.value) return undefined; + break; + } + + const ret = { + optionName: this.optionName, + value: this.input.value, + }; + + console.log("option:", ret, this.optionName, this.input.value); + return ret; + } + render() { + switch (this.input.type) { + case 'radio': + if (!this.input.checked || this.input.value == 'undefined') return undefined; + break; + default: + if (!this.input.value) return undefined; + break; + } + + return `option ${this.optionName} '${this.input.value}'` + } +} + +function initForm() { + let l3configinput = document.getElementById('l3configinput'); + let form = document.createElement('form'); + l3configinput.appendChild(form); + + const L3Config = [ + new L3Section('gateway', + new L3Input('name','gatewayName', 'name', {type: 'search', required: '', placeholder: 'Router Name'}), + new L3Input('router_ip','gatewayRouterIP', 'router_ip', {type: 'text'}), + new L3Input('router_ip6','gatewayRouterIP6', 'router_ip6', {type: 'text'}), + ), + new L3Section('dns', + new L3Input('Anycast DNS','dnsAnycast', 'server', {type: 'radio', name: 'anycast', value: 'fd43:5602:29bd:ffff:1:1:1:1', checked: ''}), + new L3Input('Anycast DNS64','dnsAnycast64', 'server', {type: 'radio', name: 'anycast', value: 'fd43:5602:29bd:ffff:1:1:1:64'}), + new L3Input('Disable','dnsAnycastNone', 'server', {type: 'radio', name: 'anycast', value: undefined}), + new L3Input('server','dnsOther', 'server', {type: 'text', placeholder: 'Custom DNS Server'}), + ), + new L3Section('babelpeer', + new L3Input('vlan','babelpeerVLAN', 'vlan', {type: 'number', min: 1, max: 4094}), + new L3Input('iface','babelpeerIFACE', 'iface', {type: 'text', placeholder: 'eth0, eth0.4, ...'}), + new L3Input('type','babelpeerType', 'type', {type: 'search', placeholder: 'wired, wireless, ...'}, ['wired', 'wireless'] ), + new L3Input('rxcost','babelpeerRXCOST', 'rxcost', {type: 'number', min: 96, max: 65535, placeholder: 'LAN: 96, Tunnel: 4096, ...'}), + ), + + ]; + + form.replaceChildren(...L3Config.map((section) => section.node())); + + function handleUpdate() { + console.log(L3Config); + renderConfig(L3Config.map(section => section.render()).filter(section => !!section)); + } + + for (let input of form.getElementsByTagName('input')) { + input.addEventListener('input', handleUpdate); + } + + renderConfig(L3Config.map(section => section.render()).filter(section => !!section)); +} + +function renderConfigOption(name, value) { + return value ? `\toption ${name} '${value}'\n` : "" +} +function renderConfig(sections) { + let l3configoutput = document.getElementById('l3configoutput'); + let l3configoutputpre = document.createElement('pre'); + let code = document.createElement('code'); + l3configoutputpre.appendChild(code); + + code.innerHTML += sections.join('\n\n'); + + l3configoutput.replaceChildren(l3configoutputpre); +} + +initForm();