Skip to content
choco-technologiesPublic

About

DMVS html

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

dmvs_html

License CI

HTML and CSS for dmvsi: a converter plugin that lays a page out as a browser does and describes it as a dmvsi document - todmvs makes a dmview view of it.

It has its own HTML parser, CSS cascade and layout - no browser, no dependencies - so it runs on a PC at build time and on a device. It is made for UI pages: the screens a designer (or an AI) draws in HTML with Tailwind CSS - and their JavaScript, which dmvs_js compiles into the view's variables, handlers and timers.

What it does

HTML - elements, attributes, text with character references, the end tags a page may leave out, <style>, <link rel="stylesheet">, style="", <img>, inline <svg>, <br>. <script> is not run; one that loads Tailwind's Play CDN (cdn.tailwindcss.com) turns Tailwind on, and its tailwind.config (theme.extend.fontFamily) is read.

CSS - the cascade (origins, !important, specificity, order), inheritance, custom properties and var(), calc() / min() / max() / clamp(); selectors with every combinator, attributes, :first-child, :last-child, :only-child, :root, :empty, :not(), :is(), :where(), ::before / ::after with content; @media (against the viewport), @supports, @layer, @import, @font-face (TrueType / OpenType sources). Interaction (:hover, :active, :focus, ...) never matches a still page.

Layout - block flow (with sibling margins collapsing, margin: auto), inline text broken into lines (fonts, line-height, letter-spacing, text-align, text-transform, white-space, vertical-align, inline blocks), flexbox (direction, grow, shrink, basis, gap, justify-content, align-items / -self, auto margins, order), grid (tracks of lengths, fr, auto, repeat(), spans, gap), position relative / absolute / fixed, z-index and stacking contexts, overflow (clipping, scrolling), translate().

Painting - colors (#rgb, rgb() / rgba(), hsl(), names), linear-gradient() and radial-gradient() (premultiplied as in CSS), border and border-radius, opacity, box-shadow (outer and inset), filter: blur() (e.g. a glow) and drop-shadow(), visibility.

Images - <img> with object-fit (cover, contain, fill - its aspect kept -, none, scale-down) and object-position; background-image: url() (under its gradient layers) with background-size (cover, contain, auto) and background-position; filter: blur() blurs the image. Each size and blur an image is drawn at is converted once (todmvs: a section of the image's .ini). An inline <svg> is an image of its own: its subtree is written into an SVG file of its box's size - $TMPDIR (/tmp), <page>-svg<index>.svg, the viewBox and SVG's camelCase names kept, currentColor as its color - which dmod's dmimg_svg rasterizes.

Tailwind CSS (v3, as its Play CDN generates it) - its preflight, the utilities of layout, flexbox, grid, spacing, sizing, typography, backgrounds and gradients, borders, effects, filters and transforms, the palette, arbitrary values (w-[454px], text-[11px], [color:red]), the opacity modifier (bg-white/10), sm: ... 2xl: against the viewport, !important, negative values. Their order decides between two utilities as in Tailwind's style sheet.

Fonts - the faces of @font-face matched by family, weight and style as CSS does; text is measured with them as the view will draw it. Icon fonts (Font Awesome) work through their CSS: ::before { content: "\f1eb" }.

Scripts

A script isn't run on the device. The page's <script>s and onclick code are compiled by dmvs_js into the view's code:

  • what the scripts do when they load becomes the init handler;
  • listeners become click handlers;
  • setTimeout / setInterval become the view's timers;
  • variables become the view's variables.

Anything known at conversion is computed then. See dmvs_js's docs/compiler.md for the language it compiles. dmvs_html is its host, the DOM:

const home = document.getElementById('home');   // an element (querySelector / querySelectorAll too)
let current = null;                              // a variable holding elements
function open(id) {                              // onclick="open('settings')"
    const w = document.getElementById(id);
    w.classList.add('active');                   // classList.add / remove / toggle / replace / contains
    home.style.opacity = '0.3';                  // style.<property> = a text known at conversion
    current = w;
}
const speed = document.getElementById('speed');
let kmh = 0;
const timer = setInterval(() => {                // a timer of the view
    kmh += 2;
    if (kmh >= 68) clearInterval(timer);
    speed.innerText = kmh + ' km/h';             // a text variable
}, 35);
up.addEventListener('click', () =>               // parseFloat(innerText): the number it shows
    temp.innerText = (parseFloat(temp.innerText) + 0.5).toFixed(1) + '°C');
  • Changes are laid out. A change of a class or a style gives the page with that change. How the element moves and fades becomes its group's variables, set at once or animated by its CSS transition. When it looks different it is painted in each state, shown on the class's variable. Classes an element changes together are one look. :active is its pressed look. The code calls a handler of each change, made once every change is known.

  • Element variables. A variable holding elements (current.classList.remove(...)) changes whichever element it holds, an IF for each element it may hold.

  • Texts. A text a script sets (innerText, textContent) is a text variable of 64 bytes. It is drawn on its element's first line, placed by text-align, with every character the scripts contain. Its number shadows it, so parseFloat(el.innerText) works.

  • Clicks. addEventListener('click', ...), el.onclick = ... and onclick="..." are a click handler. this is the element, and el.click() runs its handlers.

  • What scripts build as the page loads (createElement, className, innerHTML with markup, appendChild; a list rendered from an array) is part of the page. dmvs_js evaluates the loading as a browser runs it, the elements are laid out with the page, and their listeners are clicks (div.onclick = () => play(i)). A list rebuilt later stays as it was when the page loaded.

  • innerHTML set to text ('22.0&deg;') is the element's text.

What's reported and left out:

  • elements made or replaced later, while the view runs;
  • src, setAttribute;
  • a style known only at runtime;
  • a style that changes how an element looks (other than its opacity);
  • events other than clicks;
  • Date.

A class changes only the look of its element; what it moves around it stays.

Talking to the application. A page is shown by an application on dmodOS; dmod (dmvs_js) is the channel between them - dmod.prop(), get(), set(), onchange(), emit(). One thing more is said in HTML: data-dmod-width="name" makes an element as wide as the integer property name says - its laid out width times the property over data-dmod-scale (1000: per mille; data-dmod-value its initial value when no script makes the property). What is in the element is painted at its whole width and cut at the property's end: a progress bar the application moves. In a browser the page's own dmod.js does it.

Resources

Style sheets, fonts and images are files next to the page; a URL is read only where the options' maps say (todmvs -m URL=FILE). A style sheet mapped from a URL resolves its relative URLs against the URL first (so a whole CDN tree can be one directory map) and then next to its file.

todmvs ui.html -r screen \
    -m https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/=fontawesome/ \
    -m https://fonts.googleapis.com/css2=inter.css

The page is laid out in a viewport of the options' size (default 480 x 272); the view is the viewport, or the element the options name (-r).

What it does not do (yet)

  • JavaScript beyond the above (see Scripts).
  • Floats, tables (laid out as blocks), multi-line flex (flex-wrap), explicit grid placement (only spans), position: sticky (relative).
  • Rounded clipping: overflow: hidden with border-radius clips to the rectangle; transforms other than translate(); backdrop-filter, filters other than blur() and drop-shadow().
  • Kerning (as dmview); text is a little narrower or wider than a browser's.

DMVS_HTML_DUMP=<file> writes every element's box (JSON) - to compare the layout with a browser's.

Building

mkdir -p build
cd build
cmake ..
cmake --build .

Pass -DDMOD_DIR=/path/to/local/dmod to build against a local dmod checkout.

Testing

The tests convert a real page (tests/fixtures/dmodos - Tailwind CSS, Font Awesome, Inter) and compare its boxes with the ones Chrome lays it out in, and small pages of single features:

cd build
ctest --output-on-failure

License

MIT - see LICENSE. The test fixtures: see tests/fixtures/README.md.

About

DMVS html

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages