Tuesday, February 4, 2020

Lighthouse

After the blog conquered PageSpeed Insights, it's time to do the same with Lighthouse.

It's conveniently embedded with Chromium-based browsers and the new Edge is no different. Simply open DevTools and go to “Audits” page, which may be hidden under the “»”. There select desired categories, maybe some community plugins, and pick a target device (mobile or desktop). Then click the “Generate report” button and wait.

It will refresh the page couple of times and after a few seconds it shows scores between 0 (worst) and 100 (best) for Performance, Accessibility, Best Practices, SEO and where applicable, Progressive Web App (PWA).


...and some of them are even not faked or anything ;-)

In every section it will tell you what passed and what failed, with description how to improve it. Sometimes it's may be impossible to score 100 simply due to webhosting limitations, if you e.g. can't define headers for static files.

Tuesday, January 7, 2020

Blog tweaks and fixes

I continue to abuse this blog as my guinea pig for performance related features, like prefetching and caching. PageSpeed Insights gave it a score of 100 for both mobile and desktop, which frankly I've never seen before.

My previous guinea pig has “only” 91/99, mostly because it uses Google AdSense and Analytics (which is also problematic for CSP, just sayin'). It's weird, that Google created metrics, which his own services, targeting as much websites as possible, don't comply with.

Anyway, speaking of caching, I created a HTML cache, published from localhost, removing any dynamic server side parts and making this blog less “powered by QetriX” and more like “powered by .htaccess” :-)

For path /blog/2020/01 I have a static HTML file /data/2020-01.html and the .htaccess rule for it is:

  1. RewriteRule ([0-9]+)/([0-9]+)(/*)$ data/$1-$2.html [L]

That actually explains the PageSpeed score, it's basically a static website. And while on localhost it still takes time to load the page, the live version on webhosting loads pages instantaneously. Impressive! Another site with such speed I found is DEV.to, which uses InstantClick, and there are also some smaller sites, that use Web Workers to achieve the same.

Another trick to speed things up is to use “mousedown” event, instead of “click”. Click usually adds a around 300 ms of delay, waiting for the button to go up again, so that's what makes mousedown faster.

Drawbacks are ev.preventDefault() doesn't work as expected in mousedown, so you'll need the click event handler alongside, but it's OK, because mousedown event is not fired when using keyboard anyway.

Also with mousedown it's not possible to easily select the element, e.g. for copy/paste. This could be a pain in clickable tables, where users can't select cell contents. And you may need to detect which mouse button triggered the event, to at least allow context menu for the element, and launch the action only for the primary button (left one, in most cases).

Moving on. Another feature I did in the past, but never actually finished it, until now, is enabling pasting images from clipboard to contentEditable element, with conversion and upload to the server, where the picture is resized, compressed and optimized (if necessary and possible).

With modern APIs in the browser it's actually a piece of cake and I was surprised I can even re-use almost all the code to enable drag'n'drop image uploading (good job, whoever designed this), so I added that as well. For all the events I have only a single method:

  1. editor.addEventListener("dragenter", dragdrop); editor.addEventListener("dragleave", dragdrop); editor.addEventListener("dragover", dragdrop); editor.addEventListener("drop", dragdrop);
  2. function dragdrop(ev) {
  3. ev.preventDefault(); ev.stopImmediatePropagation(); switch (ev.type) { case "dragenter":
  4. if (ev.dataTransfer.files.length)
  5. ev.currentTarget.classList.add("dragging");
  6. break; case "dragleave":
  7. ev.currentTarget.classList.remove("dragging");
  8. break; case "drop": ev.currentTarget.classList.remove("dragging");
  9. for (let i = 0, il = ev.dataTransfer.files.length; i < il; i++) { readImage(ev.dataTransfer.files[i], ev.target); upload(ev.dataTransfer.files[i]); } break; } }

And finally, I made some fixes. I fixed disappearing tags on article edits, because I went the easy way and updated the whole JSON, but edits didn't send tags, so they got wiped out. After the fix, missing parts are filled from the original JSON using PHP's array_merge, so nothing is lost in the process, and now I can send only the actual changes.

Lastly, I had a bug in history management, where in some cases the same entry was added twice. I fixed this fairly quickly and I was even able to remove a few unnecessary lines of code. Yay!

Sunday, December 29, 2019

Dark blog

They say not everything is black and white, but this blog is. Well, it's more light gray and dark gray, but you still have the choice. This post is basically a recycled post from 2015, when I created a dark theme for Quiky. Even colors are the same! :-)

Apart from alternative styles I also had to fix some pictures to look good on both light and dark backgrounds, which was challenging for some.

I differentiated external (black/white) and internal (blue) links to tell my readers which one is going to open a new tab and which may reload the page. I know there are some folks with disabilities which prevent them to tell some colors apart, but it's not that important, so I'm fine with that.

I also wanted to animate the side navigation. Because in CSS you can't animate height, I played with max-height instead, but it looked weird – you have to specify a maximum height possible, which means the effect for closing is slightly delayed for shorter items (the effect is calculated for the declared height, not the real one, even it's only visible for the real height).

Instead of animating the list (submenu) as whole I opted for animating individual list items. The effect is still not what I wanted, but at least it's now synchronized among different submenu heights and animates nicely.

Because I write rather short posts, I decided to keep the whole month on a single page. But I found out Instapaper parses only one <article> tag on the page, so even it's semantically wrong, it's all in sections. Now the whole month saves as an article, but sorry about that extra clutter, like tags and footer :-)

Overall this approach is more Qip-like: imagine the whole page as a single article, with separated blocks for each subsection or chapter. I like it!

Sunday, December 1, 2019

Blog rewrite

After success with frontend framework I decided to use PHP Lite Framework and rewrite some of my websites, like QetriX incl. Blog and Docs. I want them to be faster, transfer less data and be overall more efficient.

I have some content platforms (mainly Quiky, Qip, Redsys and Static), that I'd like to merge into the same codebase. For years I wanted to merge old Redsys with Quiky, to make Quiky a true CMS with all the features and WYSIWYG editor. Also, I wanted Quiky to have Qip's lightning fast tags.

So one day I compiled all of my previous attempts of WYSIWYG content editors, because I wanted to reuse some of the code I wrote in the past, and I was staggered how many times I started, with first tries back in 2012.

The most active year was 2016, because I finally saw “my” concept of content blocks in action in the popular Medium content editor and its clones – notably later in Gutenberg for WordPress 5, released a year ago. Another notable mentions include Notion.

But every time I've fallen back to just plain text in textarea, due to various issues, and hoped that a bunch of neat keyboard shortcuts would satisfy me. Although I really like how Qip works, if it really did satisfy me, I wouldn't be writing this post. :-)

So, let's see, where it will stop me this time. I hope the key to success are baby steps, small incremental improvements. I'll start with a decent text editor, that is a pleasure for me to use. Everything else will be managed directly in data at the moment, and support for more advanced blocks, e.g. gallery management, will be added later.