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 the little JavaScript that switches between them, which it works out at conversion and makes the view's variables and handlers.
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" }.
A script is not run on the device: what its click handlers do is worked out at conversion. Understood is what switches screens and toggles things:
const home = document.getElementById('home'); // an element
let current = null; // a variable the handlers set: the view's
function open(id) { // onclick="open('settings')": inlined
const w = document.getElementById(id);
w.classList.add('active'); // classList.add / remove / toggle
home.style.opacity = '0.3'; // style.<property> = ...
current = w;
}
function close() {
if (current) { // if (x), (!x), (a === b), classList.contains()
current.classList.remove('active');
home.style.opacity = '1';
current = null;
}
}Handlers are also what the page sets up when it loads:
addEventListener('click', ...) and el.onclick = ... with arrow and
function expressions (closures over their scope), on elements of
getElementById(), querySelector() / querySelectorAll() (a list's
forEach() is unrolled), getAttribute(); el.click() runs the
element's handlers. Several class changes of an element at once are one
look of it.
this in an onclick is its element. Every change is laid out - the page
with that class or that style:
- how the element moves and fades becomes its group's variables, set at
once or animated by its CSS
transition(duration and timing function) - the view switches its screens as the page does, and slides them as it does; - when it looks different (a switch on, a light off, play / pause, an icon hidden, another label) it is painted in each state of its class, shown on the class's variable;
:active(and Tailwind'sactive:) is its pressed look, shown while its box is pressed.
What is reported and left out: what the page does when it loads (timers,
Date, text a script writes), loops, events other than clicks, a style a
script sets that changes how an element looks (but its opacity). A class
changes only the look of its element - what it moves around it stays.
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.cssThe 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).
- 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: hiddenwithborder-radiusclips to the rectangle; transforms other thantranslate();backdrop-filter, filters other thanblur()anddrop-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.
mkdir -p build
cd build
cmake ..
cmake --build .Pass -DDMOD_DIR=/path/to/local/dmod to build against a local dmod checkout.
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-failureMIT - see LICENSE. The test fixtures: see tests/fixtures/README.md.