Created: 2025-06-10 23:39:04+02:00 Updated: 2026-09-06 19:33:36+02:00 License: CC BY-NC-SA (text), GPLv2 (code). Author: zrajm # HTML-Scented Markdown > A rough outline of the simple "format" I use to write simple web articles > (like this one). It uses Markdown (to simplify writing and proofreading). It > is rendered into HTML by the browser (to avoid a build step). And it tries to > look nice, even for users without Javascript. <toc heading=Contents class=toc upto=3> ------------------------------------------------------------------------------- I often toss together a simple page in order to explore a topic, think about, or design a thing. I cannot be bothered by details when I'm writing these (because that's a surefire way to get me distracted). Five levels down in that rabbit hole lies this web page. A while back I decided to systematize my earlier (and future) scribblings and collect them in one spot. That spot is here ([zrajm.org]), and the system is one of *laziness, impatience and hubris.*---I simply toss whatever meandering thought crosses my mind into a text file, *[that isn't really a text file](#tastes-like-markdown),* throw it onto the garbage heap that is the internet, and hope something good come of it. And here all you little trash pandas can dig it up. # Smells Like HTML The file into which I write these words smells like HTML---it has an `.html` file extension, and contains *juust enough* HTML-smelling preamble to convince your browser that it can be loaded:[1](#fn-pb1) ```html <!DOCTYPE html><meta charset=utf-8><!--*- markdown -*--> <link rel=stylesheet href=www/nojs.css><xmp> ``` At the very end you'll also find a little chunk of line noise. The task of this line is to invoke a Javascript incantation that allows your machine friend to produce a pretty document for you:[2](#fn-yaz) ```html <​/xmp><script type=module src=www/scent.js></script> ``` But the meat of the file? Yeah, that's ... *different.* # Tastes Like Markdown While the page you're currently looking at smells HTML to your browser, the human story is quite different. (To have look at the source, press **Ctrl+U**, or **⌘U** on Mac.) However, the HTML shtick is just *pretense.* If you peel off the skin and take a bite, you'll notice that this file tastes rather like plain text. The meat of the file is in [Markdown] format. This is plain text *with a few extra quirks.* It reads pretty much like an old-fashion email, or something written on a typewriter in the seventies. The benefit of this is that it is readable *like text,* reading and proofreading is a breeze. Here this template starts in earnest. See also [Page Template]. # Transmogrified by Javascript Between the Markdown and the HTML lies a layer of Javascript magic. Initially I was using [Jquery] (for DOM manipulation), and [Showdown] (for Markdown handling), but even in their slimmed down versions they're pretty hefty and contain a lot of stuff I never used. So eventually I came to write my own: * [Baremark]---A minimal Markdown parser. * [Elemental]---For DOM manipulation (replacing Jquery). * [Quotecurl]---For pretty quotes and dashes. The script [`scent.js`](scent.js) ties it all together. ## Baremark → HTML [Baremark] and [Quotecurl] are used to render the Markdown into HTML, and, additionally, the following [Baremark Addons] are also loaded. <div class=wg9z77f> <style>.wg9z77f table { white-space: nowrap }</style> | See | Description | Name | |------------------|------------------------------------|--------------------------------| | [Docs][autolink] | Turn URLs into links | [`autolink.js`][autolink.js] | | [Docs][id] | `[#id]` for named HTML anchor | [`id.js`][id.js] | | [Docs][meta] | Email-header style metadata | [`meta.js`][meta.js] | | | <tt>\^sup\^</tt> for ^superscript^ | [`sup.js`][sup.js] | | [Docs][table] | GFM-style tables | [`table.js`][table.js] | | [Docs][toc] | Table-of-contents | [`toc.js`][toc.js] | | | Remove HTML comments | [`uncomment.js`][uncomment.js] | </div> When the main script is done, a `scent:done` event is fired. Javascript that you want to run after this may be invoked from an event listener, like so: ```js document.addEventListener('scent:done', () => { // run after Markdown → HTML has finished }) ``` ### Metadata Metadata is processed by the Baremark addon [meta], and then used to generate the page header, footer and some of the page metadata (such as favicon, and page `<title>`). The following are possible values (usually, only `created`, `updated`, `license`, `favicon` and `author` is needed): * `author`---Author name. Used in the page `<title>`, header and footer. * `class`---CSS class names to add the page's `<html>` tag. Class names, `book` (default), `hanging`, or `blank` are used to set the default page [indentation mode](#indentation), but any class name may be used. * `created` (or `date`)---Creation date for the page in ISO 8601 format (as is outputted by shell command `date -Isecond`). Used in the page title. * `favicon`---Filename or URL for favicon to use for the page. (If unspecified, defaults to `/favicon.svg`.) * `lang`---Set `lang` attribute for document `<html>` tag. Default value is `en-GB` 'English, Great Britain'. (This was chosen as a default because it has a sane date defaults, and since it uses the metric system.) * `license`---If given, this value is used as-is in the page footer (preceded by 'License:'). * `title`---Specifies the title of the document. If no `title` field is specified, then Baremark tries grab the first page heading as `title` instead. Default value is `[NO TITLE]` (chosen to grab your attention, because, really, you should set a title). Used in the page title, and `<title>`. * `titleId`---If specified sets the `id` attribute of the page (`<h1>`) heading. * `updated`---Creation date for the page in ISO 8601 format (as is outputted by shell command `date -Isecond`). Used in the page title. * `years`---Copyright years. Default value is a combination of the years found in the `created` and `updated` metadata fields, so this seldom needs to be set. Used in the page footer. ## Scroll Position Restoring Since the HTML of the page is generated rather than loaded, the browser will not reliably restore set scroll position for hash fragment URLs. To solve this hash location is reset after page load, if the current scroll position is at the top of the window. (The top-of-the-window limitation is to preserve the scroll position if the user reloads the page.) ## Scroll-to-Top Button When scrolling down slightly beneath the fold, a scroll-to-top button will slide into position itself in the bottom right corner. Clicking the button will scroll to the top of the page, creating a new history event. To get back to where you came from, just click the history 'Back' (←) button. ## Wrapping of Chapters Each heading (`<h1>`...`<h6>`) and their subsequent content (that is, each chapter), is wrapped in `<div>` tags, and the `id` attribute of the corresponding `<h#>` tag is moved to the wrapper.---This is done to allow a CSS `:target` selector to style the entire chapter. **Nota Bene:** The seemingly apt `<section>` tag is nousegenerally problematic, and is best avoided altogether in HTML documents, since it's use will affect the style of any contained `<h1>` tags (specifically font size), and also result in warnings being displayed in on Firefox developer's console.---The advice given by the [Mozilla page on `<section>`] is basically to never use it if any other tag can do the job instead. The `<section>` tag was part of a suggested HTML5 *Document Outline* feature (now deprecated), which in practice was never implemented by browsers, and should therefore not be relied on ("[There Is No Document Outline Algorithm]"). [Mozilla page on `<section>`]: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/section#usage_notes [There Is No Document Outline Algorithm]: https://adrianroselli.com/2016/08/there-is-no-document-outline-algorithm.html # Skinned by CSS * [`nojs.css`](nojs.css)---Very simple stylesheet for when Javascript isn't available. Its only job is to render the raw Markdown fairly readable, and to display a "No Javascipt" warning. (This stylesheet is unloaded as soon as Javascript is up and running.) * [`scent.css`](scent.css)---A stylesheet for rendering HTML elements. It should be minimal, but also mobile friendly, and be able to handle dark mode, plus all standard HTML tags. It should also be pretty. :) ## CSS Reset My `scent.css` file starts with Matt Smith's [CSS reset for minimalists], with two small tweaks. ```css html { box-sizing: border-box } *, :before, :after { box-sizing: inherit; padding: 0; margin: 0; text-indent: 0; max-width: 100%; } ``` I've added a line to stop the inheritance of `text-indent`, which otherwise would be inserted into child *block* elements. Like this (note the extra, erroneous space in front of "tatu"): <style>.dWrfp {text-indent:2em;outline:1px solid currentColor}</style> <div class=dWrfp>Velut luna <span class=dWrfp style="display:inline-block">tatu</span> variabilis semper crescis aut decrescis vita detestabilis nunc obdurat et tunc curat ludo mentis aciem.</div> The `max-width` parameter above is there to ensure that no elements *accidentally* grow wider than the page body.---The page body width itself is overridden elsewhere to get a nice, readable line length, while tables and code blocks are overridden so that they may grow wider than the body width (though not wider than the viewport) to show as much as possible of their content on big screens. ## Fonts I haven't quite settled on this yet. I do a lot of weird stuff with writing systems. (Like [Swedish sign language transcription](/teckentranskription/intro.html), or [Canadian Aboriginal Syllabics](/toki-pona-syllabics/), or even [ASLwrite](/teckenskrift/).) And so, whatever I finally settle for will have to be able to handle this. ## Inspiration for CSS I'm sorta constantly trying to figure out what a good web page design should be. Like with most other things I'm searching for some kind of (technological) minimalism. Something that's easy to move between servers, and which doesn't have a lot of dependencies. Here's a list of stuff that I have looked to for inspiration. * [Just Enough CSS for a Blog]---Design philosophy. How to create a CSS for pretty-looking blog. * [Web Compendium: CSS: Methodologies]---Page an various approaches for CSS design. * [Web Compendium: CSS]---Describes various CSS related APIs and stuff. * [Old CSS, New CSS]---A history of CSS development. * [Matt Smith]---Contains a lot of bite-sized snippets on CSS and Javascript design. (Somewhat cumbersome to navigate.) ### List of Frameworks These are websites which have lists of CSS:s to download. I'm always looking for *uncomplicated*---solutions that doesn't require compiling, makefiles or suchlike, only plain CSS. Minimal requirements for me include: *dark mode,* and *looking great on mobile.* * [Awesome CSS Frameworks]---Github README lists a bunch of different CSS frameworks. * [CSS Bed]---List of classless CSS files, with an interface to quickly look at them. * [MVP.css]---One minimalist CSS. * [Water.css]---Another minimal classless CSS with support for dark mode & and mobile. # Using the CSS ## Indentation The indentation mode is set using HTML classes. It only affects text paragraphs, but, since the mode is inherited, a run of paragraphs may be wrapped in `<div class=MODE>` (or any other HTML tag with a `class` attribute) to set the indentation mode for all of them. To set the indentation mode for a single paragraph, one could also use an explicit `<p class=MODE>` tag. To set the default indentation for a whole document, use the [`class` metadata field](#metadata) which sets the document's `<html class=MODE>`. (If unset, `book` is assumed.) If you want to insert a blank space between paragraphs without affecting the indentation (like a scene break in a book) you can either insert an `<div></div>` tag between the paragraphs in question, or, for a longer break with a horizontal ruler, use a [Markdown dinkus][Markdown dinkus]. The following classes control paragraph indentation: > `book`---*Book indent* (default). The first paragraph after a heading, table, > illustration, or paragraph of a different type, is separated from the text > above by a vertical space. > > For subsequent `book` paragraphs (that is, for all `book` paragraphs that > are immediately follow by another `book` paragraph) the first line of text is > indented, without any vertical space between paragraphs. (Just like paragraphs > in a book.) > > <div class=hanging> > > `hanging`---*Hanging indent.* Here all lines, *except* the first one, are > indented. This is often used for glossaries, references and the like. (If a > `hanging` paragraph follows a `book` paragraph they are also separated by a > blank line.) > > <p class=contd>`contd`---*Continued indent* (for use after `hanging`). > Like an indented `book` paragraph. The whole paragraph is indented, except > the first line which is indented twice. This is useful, for example, in a > glossary where you wish to continue a hanging paragraph without outdenting > the first line. > > </div> > > `blank`---*Blank spacing.* Paragraphs are separated from each other a > vertical blank space. (This the most common type of paragraph separation on > the web.) **Note:** Inserting `<script>` or `<style>` tags between paragraphs will affect the indentation, so make sure to put these at the beginning/end of the document, or, at least, outside of a run of `<p>` tags with the same indentation mode. (E.g. placing `<script>` or `<style>` tags next to `<table>` or `<figure>` will not have any detrimental effect, as tables and figures intentionally breaks indentation anyway.) As the CSS logic implementing these indentation modes is somewhat complex, there's a separate page with [indentation test cases](tests.html), and also a page [describing the CSS logic](paragraphs.html). ## Form Elements Right now there isn't a lot in this section. However, for the `<input type=checkbox>` element there's a there's a special `class=onoff` appearance (used for the dark mode toggle). It changes a checkbox's appearance so that it looks like an on-off toggle. Like this: <input type=checkbox class=onoff> ## Dark Mode If *dark mode* is selected in the user's browser settings it will be honored by this CSS. If you want to add a dark mode override switch (allowing the user to manually switch between the modes) include the following HTML anywhere on your page: ```html <input id=darkmode type=checkbox> ``` ## Dark Mode & Images Images with filenames ending in `.png` or `.svg` (formats which are usually used for simple black-and-white drawings, diagrams and the like) are by default inverted in dark mode, while `.jpg` images (which is usually used for full-color photos) are not. This default behavior can be overridden for individual images by appending a `darkmode` URL parameter to the end of the filename. Use `?darkmode=no` to disable the dark mode color inversion (for an SVG or PNG), and `?darkmode=yes` to enable it (for a JPEG image). Non-photographic images should be drawn using black lines on transparent (or white) background for best results. ```markdown ![Diagram](diagram.svg?darkmode=no) <!-- SVG without dark mode inversion --> ![Photo](photo.jpg?darkmode=yes) <!-- JPEG with dark mode inversion --> ``` # Footnotes <div class=hanging> <div>[#fn-pb1] **1.** *HTML-Scented Markdown* grabs Markdown from the first occurrence of `<xmp>` (which I recommend), `<plaintext>` or `<textarea>`. (The tags [`<xmp>`] and [`<plaintext>`] may be deprecated, but they are still *extremely* well supported by browsers.) <p class=contd>If you use `<textarea>` as a Markdown container, then HTML entities (`&...;`) are parsed and converted into characters by the browser. This means that to get the string `&gt;` in your output, you'll have to write it as `&amp;gt;` in your Markdown. :( HTML tags may, however, be written normally (except for `</textarea>` which must be escaped, unless you want to mark the end of your document). Also, use `<textarea disabled>` to not cause confusion among users with Javascript disabled. <p class=contd>If you use `<xmp>` or `<plaintext>` you can write plain Markdown, without having to think about escaping. With `<xmp>`, however, the end tag, <nobr>`<​/xmp>`,</nobr> *cannot occur* in the text! A workaround is to insert an invisible character (e.g. zero-width space), but if used within backticks or in a codeblock, literal character has to be used. This character also remain in the output, and might confuse users who try to copy the text. With `<plaintext>` there *is* no end tag, everything from the first `<plaintext>` tag to the end of the page is included in its content. </div> [#fn-yaz] **2.** All occurrences of the string <nobr>`<​/xmp>`</nobr> on this page contain an invisible non-breaking space. As a result, if you copy/paste them, you might get a slightly different string than expected. (See footnote [1](#fn-pb1).) ------------------------------------------------------------------------------- [`<xmp>`]: https://caniuse.com/?search=xmp "Browser support for <xmp>" [`<plaintext>`]: https://caniuse.com/?search=plaintext "Browser support for <plaintext>" [Awesome CSS Frameworks]: https://github.com/troxler/awesome-css-frameworks [Baremark Addons]: /baremark/#addons [Baremark]: /baremark/ "Baremark" [CSS Bed]: https://www.cssbed.com/ [CSS reset for minimalists]: https://allthingssmitty.com/2017/01/09/css-reset-for-minimalists/ [Elemental]: /elemental/ "Elemental" [Jquery]: https://jquery.com/ "Jquery" [Just Enough CSS for a Blog]: https://niklasfasching.de/posts/just-enough-css/ [MVP.css]: https://andybrewer.github.io/mvp/ "MVP.css --- A minimalist stylesheet for HTML elements" [Markdown dinkus]: /baremark/#dinkus------ [Markdown]: https://daringfireball.net/projects/markdown/ [Matt Smith]: https://allthingssmitty.com/ [Old CSS, New CSS]: https://eev.ee/blog/2020/02/01/old-css-new-css/ "Fuzzy Notepad: Old CSS, New CSS " [Page Template]: /html-scented-markdown/template.html [Quotecurl]: /quotecurl/ [Showdown]: https://showdownjs.com/ "Showdown" [Water.css]: https://watercss.kognise.dev/ [Web Compendium: CSS: Methodologies]: https://frontaid.ch/web/css/#methodologies [Web Compendium: CSS]: https://frontaid.ch/web/css/ [autolink.js]: /baremark/addon/autolink.js "autolink.js source" [autolink]: /baremark/?addon/autolink.md "autolink Docs" [id.js]: /baremark/addon/id.js "id.js source" [id]: /baremark/?addon/id.md "id Docs" [meta.js]: /baremark/addon/meta.js "meta.js source" [meta]: /baremark/?addon/meta.md "meta Docs" [sup.js]: /baremark/addon/sup.js "sup.js source" [sup]: /baremark/?addon/sup.md "sup Docs" [table.js]: /baremark/addon/table.js "table.js source" [table]: /baremark/?addon/table.md "table Docs" [toc.js]: /baremark/addon/toc.js "toc.js source" [toc]: /baremark/?addon/toc.md "toc Docs" [uncomment.js]: /baremark/addon/uncomment.js "uncomment.js source" [uncomment]: /baremark/?addon/uncomment.md "uncomment Docs" [zrajm.org]: /