leanrada.com notes

フィード

記事のアイキャッチ画像
Minimal CSS-only blurry image placeholders
はてなブックマークアイコン 1
leanrada.com notes
Here’s a CSS technique that produces blurry image placeholders (LQIPs) without cluttering up your markup — Only a single custom property needed!<img src="…" style="--lqip:192900">The custom property above gives you this image: Try changing the property’s value (WARNING: FLASHING) { const card = document.currentScript.parentElement; const input = card.querySelector("input"); const code = card.querySelector("code"); const preview = card.querySelector("div"); let currentValueStr = "192900"; let targetCode = null input.addEventListener("input", event => { if (!targetCode) { targetCode = Array.from(code.querySelectorAll("span")).filter(el => el.textContent.includes(currentValueStr)).slice(-1)[0] ?? code; } const lqip = Number(event.currentTarget.value); // use this page's lqip to avoid breakage if I ever update the scheme preview.style.setProperty("--my-lqip", lqip); targetCode.innerHTML = targetCode.innerHTML.replace(currentValueStr, lqip); currentValueStr = String(lqip); }); } Granted, it
1日前
記事のアイキャッチ画像
Inline rendering with document​.currentScript
leanrada.com notes
For quick and dirty rendering of simple dynamic content, you may not need the complexity of a templating language like Handlebars or a PHP backend.Let’s use the example phrase, “Come on, it’s <currentYear>”. It should result in “Come on, it’s 2025$(new Date().getFullYear())” when rendered today.You can write this directly in HTML—without IDs, classes, or querySelectors in your JS! Thanks to the document.currentScript property, we can refer to the currently running <script> element directly and go from there.So the dynamic phrase “Come on, it’s 2025$(new Date().getFullYear())” would now be written as:Come on, it’s<script> document.currentScript.replaceWith(new Date().getFullYear())</script>The script simply replaces itself with its computed value on the spot.The code’s a bit wordy though, but we can alias it to a constant like $ via $=(...n)=>document.currentScript.replaceWith(...n). Then we’d have something reminiscent of template literals in JS.Come on, it’s <script>$(new Date().getFu
24日前
記事のアイキャッチ画像
Rewriting my site in vanilla web
leanrada.com notes
I rewrote this website in vanilla HTML/CSS/JS. Here’s the story.But why?Over the years, I’ve used a bunch of libraries & frameworks to build this website, before finally making my own static site generator that I called compose-html. As the name suggests, it composes HTML files together, very much like Astro.compose-html’s READMEI like systems that have a small set of tight abstractions that are easy to understand. I’m not a fan of rigid, non-atomic concepts like “pages” and “layouts”, “themes”, “frontmatters” — I mean, these are just ‘components’ and ‘data’! I dislike those that dictate your project directory structure and coding style.If your documentation has a ‘Project structure’ section, I’m out!So I built my own simple site builder and that was nice BUT it didn’t end up making life easier. The real world is messy, and HTML more so. Simply composing pieces of HTML together isn’t that straightforward and the abstraction leaked. My compose-html framework eventually turned into a 2k
1ヶ月前
記事のアイキャッチ画像
CSS sprite sheet animations
leanrada.com notes
For RSS readers: This article contains interactive content available on the original post on leanrada.com.Check out this demo first (Click it!):Yes, it’s the Twitter heart button. This heart animation was done using an old technique called sprite sheets🡵.Interactive content: Visit the website to play with interactive content!Alternative text: spinning cloverInteractive content: Visit the website to play with interactive content!Alternative text: spinning cloverInteractive content: Visit the website to play with interactive content!Alternative text: spinning cloverOn the web sprite sheets are used mainly to reduce the amount of HTTP requests by bundling multiple images together into a single image file. Displaying a sub-image involves clipping the sheet in the appropriate coordinates.Sprite sheet / texture atlas of Minecraft blocksThe bandwidth benefit has been largely mitigated by HTTP/2 now, but sprite sheets have another purpose: animations! Displaying animations is one of the prima...
5ヶ月前
記事のアイキャッチ画像
Centering a div in a div in 2020
leanrada.com notes
tl;dr: use place-content: center on a grid container..container { display: grid; place-content: center;}Here’s how that looks like:That’s it. Two CSS rules.Yes, four years late according to caniuse. But this is apparently still not well-known today.Based on recent developments🡵, looks like we just need a few more years before we can finally get rid of the extra display rule so we can have the one CSS rule to center them all.
5ヶ月前
記事のアイキャッチ画像
I made an app to fix my motion sickness
leanrada.com notes
Last May, Apple announced a new feature called Vehicle Motion Cues for their iOS devices. It’s an overlay that can help reduce motion sickness while riding a vehicle.I have really bad motion sickness, and riding cars, buses, and trains makes me nauseous. This feature would have been a nice relief for me, but as it stands, I use Android.Instead of buying a Malus fruit device, I took the matter into my own programmer hands. I created an alternative app for Android.To be sure, I checked the patents. Apple does have one regarding a certain motion sickness solution, but it’s specifically for head-mounted displays, not handheld devices. I figured it’s because there is prior art for handheld devices, such as KineStop for Android by Urbandroid🡵.My app is called EasyQueasy. What it does is display onscreen vestibular signals that try to help prevent motion sickness. This functions as an overlay that is displayed on top of whatever you’re doing, browsing the web or watching videos.The app is op...
7ヶ月前
記事のアイキャッチ画像
Stop using ease-out in your UIs!
leanrada.com notes
For RSS readers: This article contains interactive content available on the original post on leanrada.com.Before anything, let me present you with a set of controls with no context.Press me Press me Stop using ease-out, or ease-in-out, or whatever-out, in every UI animation!There is a lot of propaganda on the internet against ease-in, saying that it's “unnatural”, “unusual”, or that it's “of the devil”. Some say that it's both “sluggish” and “abrupt”. Many pointing to ease-out as the safe, smooth, satisfying messiah of animation (including its safer kin, ease-in-out). There are even published ‘best practices’ which can be summed up to “just use ease-out”🡵. This post is here to set things straight — in a nonlinear fashion.So, why not ease-out? And what to use instead?Reason #1. It’s overusedLet’s get the weakest point out of the way. Ease out is boring because it’s everywhere. Because it’s part of the browser default ease function (which is a combination of a fast ease-in with a slow e...
9ヶ月前
記事のアイキャッチ画像
Creating a halftone effect with CSS
leanrada.com notes
For RSS readers: This article contains interactive content available on the original post on leanrada.com.Here’s a quick halftone effect (i.e. a retro printed look) using CSS with only one div at the minimum.First of all, here’s a live demo:Interactive content: Visit the website to play with interactive content!Alternative text: CSS halftone demoToggle the filter class using the checkbox above.To further illustrate the halftone effect, the following demo can vary the size of the dots and the degree to which they ‘bleed’:Interactive content: Visit the website to play with interactive content!Alternative text: CSS halftone demo Bleed There are several ways to do this in CSS. The above is a bit more advanced with 2-3 extra divs. I’ll try to show a simple method, first.Halftone basicsTo keep it simple, let’s start with a black-and-white image. It should be easy to layer in additional colors with the same principle.Interactive content: Visit the website to play with interactive content!Alte
1年前
記事のアイキャッチ画像
AI art is not generative art
leanrada.com notes
AI art is not generative art (clickbait title). While the technical definition says that one is a subset of the other, I think it’s useful to distinguish between these two categories.Why I’m writing this in the first place — Starting 2022, the term “generative art” had been progressively becoming synonymous with art produced by AI text-to-image systems. As a consumer and producer of (traditional) generative art, it was becoming a bit annoying to browse generative art content on the internet. Whether through tags like #generativeart or communities like r/generative, spaces are being flooded with AI-generated images which I and many others are not interested in. End rant.In 2024, things are a bit different. AI art is now commonly referred to as ‘AI art’. I shouldn’t have procrastinated writing this post for so long.There are also cases where generative artists are pressured to relabel their art, so as to not be mistaken for being AI (and avoid things associated with it). It’s an unfortun
1年前
記事のアイキャッチ画像
htmz story
leanrada.com notes
This post is not the usual programming post. It’s been an interesting week, I guess.I just finished my mini side-project htmz🡵, a snippet / library / microframework / whatever for HTML whose main feature was that it only weighed a total of 181 bytes. It almost fits inside a single ‘tweet’ (wait, the limit is now 280 not 140?).Here is the entire framework in its final form:<iframe hidden name=htmz onload="setTimeout(()=>document.querySelector(contentWindow.location.hash||null)?.replaceWith(...contentDocument.body.childNodes))"></iframe>See the project documentation🡵 for more info on what it does.I posted it on Hacker News🡵, went to sleep because it was 3am at that point. Then the next morning it was at the top of HN!I didn’t expect this at all. But naturally I rushed to the comments section which quickly grew too numerous for me to read. They were generally positive, and acknowledged the project’s hackyness and elegance (these adjectives usually mean opposite things). It was pretty c...
1年前
記事のアイキャッチ画像
Pure CSS single-page app routing
leanrada.com notes
You’re probably a busy person, so here’s the CSS:section:not(:target) { display: none;}Demo: Open in a new tab My AppOpen in a new tabExplanationThe :target🡵 CSS selector selects the element that is targeted by the URL fragment.Combined with :not, we can hide sections that are not referenced by the URL fragment.Just as JS routers use the fragment to hide/show sections in the DOM, this “CSS router” uses the same fragment to hide/show sections in the DOM.Experiment: Default sectionNotice that the example above doesn’t start with the Home section. The content is blank initially. This is because on initial page load we don’t have a URL fragment to begin with.We need to make an exception for the Home section.Let’s start by not hiding the #home section by default. Only hide #home if there’s a specific :target section.- section:not(:target) {+ section:not(#home, :target),+ :root:has(:target) #home { display: none; }Demo v2: Open in a new tab My AppOpen in a new tabExperiment: Nested routesOn...
1年前
記事のアイキャッチ画像
getDuolingoStreak()
leanrada.com notes
How to fetch your Duolingo streak using an unconfirmed API on duolingo.com:function getDuolingoStreak(username) { const res = await fetch( `https://www.duolingo.com/2017-06-30/users?username=${username}&fields=streak,streakData%7BcurrentStreak,previousStreak%7D%7D` ); const data = await res.json(); const userData = data.users[0]; // I didn't know which of these fields matter, so I just get the max of them. const streak = Math.max( userData?.streak ?? 0, userData?.streakData?.currentStreak?.length ?? 0, userData?.streakData?.previousStreak?.length ?? 0 ); return streak;}That’s my current max streak.I can then render this data into a card like that. I put one of these cards in the /misc/ section.Let’s look at the API itself. www.duolingo.com/2017-06-30 seems to be the API prefix, which is a bit weird. What is 2017-06-30? What happened on that date?🡵 Maybe the Duolingo team used a date-based versioning🡵 at the time?In any case, big thanks to the Duolingo team for keeping this apparently...
1年前
記事のアイキャッチ画像
Writing a single-file website with Rust
leanrada.com notes
At the company I work for, there is something called a “professional education leave”, which lets me take a day off to learn something new or attend conferences or whatever professional education means. I took the opportunity to learn the Rust🡵 programming language.I didn’t want to follow tutorials, so I thought of a project to learn with, which ended up being “make a single-file website web server”. It was inspired by the article my website is one binary by j3s🡵.Rust first impressionsfn main() { println!("Hello World!");}My first impression was that it is a mature programming language that is crazy about compile-time stuff. For example, the above println! call is a macro, which expands to something more verbose… Apparently, with macros you can also make full-blown DSL🡵s that are directly embedded in Rust code.The rustc compiler’s error messages were very good.I think I haven’t really dived deep enough to discover the standout features that I keep hearing about like memory managemen...
1年前
記事のアイキャッチ画像
2023 in review
leanrada.com notes
What happened in 2023? Stuff happened.2023 website redesign 🎨Feb 2023, I redesigned my site, with a goal of making it more personal instead of just being a mere portfolio.It came with a blog section, which made me start writing a blog.Started a blog 🖊“I’m starting a blog!I’ve done some blogging in the past (for gamedev), and I already do write-ups for my projects, so I think it’d be good to officially keep a blog!I have some ideas to populate the first few posts, then we’ll see how it goes from there.Watch this space!”First blog post on the new site (now unpublished), 24 Feb 2023Well, it went well! In 2023 I had:The hottest posts were:🔥 Sort, sweep, and prune: Collision detection algorithms🔥 Dynamic patrol behaviour in stealth games with Markov chainsIt is a nice new hobby and something I shall continue. 🙌Side projects 💻This year I launched two webapps, portabl.ink and GuhitKudlit!Portablink was just a fun experiment without much utility. I don’t have analytics on it so I don’t k...
1年前
記事のアイキャッチ画像
My personalised 55% split keyboard
leanrada.com notes
For RSS readers: This article contains interactive content available on the original post on leanrada.com.I’ve been using my Lily58 split keyboard🡵 for more than a year now. It’s a compact 58-key split keyboard with column-staggered keys, 55% the size of a standard 104-key keyboard.Thanks to the open-source firmware/framework QMK🡵, I customised it and programmed it to my satisfaction. I figured now’s a good time to post about my layout so far.The keyboard in questionQuick rundownHardware: Split keyboard with 58 keys in total. A 32×128 pixel OLED screen on each half. Both halves are connected by wire, and the whole thing is wired USB-C to the computer.Software: It runs the QMK firmware, which I used to implement 7-8 layers and several custom functions. I customised the heck out of my keyboard’s firmware.The basicsInteractive content: Visit the website to play with interactive content!Alternative text: Lily58 keyboard layout diagram Left hand: 〔Esc 〕〔 ⌃[ 〕〔 ⌃] 〕〔⌃⇧Tab〕〔⌃Tab〕〔 〕 〔 ? 〕〔 ...
1年前
記事のアイキャッチ画像
Baybayin calligraphy generator
leanrada.com notes
GuhitKudlitBaybayin calligraphy generatorGuhitKudlit🡵 is a webapp that generates baybayin calligraphy.Baybayin🡵 is an ancient writing system used in the Philippines, mostly for the Tagalog language. I’ve always been fascinated by Philippine languages, and this project was just another fruit of that curiosity.Project detailsreleased2023rolecreatorplatformWebtechJSOutput of GuhitKudlit for the word ‘guhit’The name comes from ‘guhit’ meaning line or drawing, and ‘kudlit’ meaning tick or small mark.You see, baybayin is an abugida🡵 and in baybayin, the kudlit is used to signify the vowel of a character.Chart of baybayin characters from WikimediaThe program is divided into two functions: transliteration and calligraphy. The transliteration process converts ASCII input to baybayin glyphs, and the calligraphy function draws those glyphs onto the canvas using the p5.js library🡵.TransliterationThe transliteration algorithm that converts ASCII text to baybayin glyphs is quite simple (or shoul...
1年前
記事のアイキャッチ画像
Debug CSS boxes with this bookmarklet
leanrada.com notes
Here’s a quick & simple way to show all element bounds on a page.javascript:(a=(d=document).createElement('style')).innerHTML='*{outline:solid 1px red}';d.head.append(a)The bookmarklet: ▤outlinifyExample result on threads.netNot sure how to use it? Save or drag the following link into your bookmarks:☝ Video: video of dragging the bookmarklet into the bookmarks bar | Source: /notes/debug-css-boxes-bookmarklet/drag.web.mp4 If you prefer different styles:Dashed: ▤outlinify Dotted: ▤outlinify
1年前
記事のアイキャッチ画像
Compressing a website into a URL
leanrada.com notes
For RSS readers: This article contains interactive content available on the original post on leanrada.com.This post explains how portabl.ink🡵 works. Portablink is a tool that creates self-contained compressed web pages in a single link. Check portablink project page for more info.tl;dr: It uses data URLs containing compressed data which is bundled with its own decompression instructions.Data URLsIn case you aren’t familiar with data URLs🡵, they are URLs whose contents are in the URL itself. They all start with data:. Here, let me show you with this interactive mock browser:Interactive content: Visit the website to play with interactive content!Alternative text: browser demo(Hint: edit the URL in the address bar!)When you load a data URL, the browser shows the content embedded in the URL directly.So, that’s it? Website in a URL? — Use data URLs! Easy!Wait, there’s more! The portablink tool does some more things than just putting your HTML in a data URL. The tool also compresses your c...
1年前
記事のアイキャッチ画像
Introducing portabl.ink
leanrada.com notes
I just released a small weekend project called portabl.ink🡵. It’s a tool to compress whole web pages into a self-contained portable link.Try it out online at kalabasa.github.io/portabl.ink🡵!For some examples and more details, read the project page.Next, I will write another post on how it works under-the-hood. Stay tuned!
2年前
記事のアイキャッチ画像
Sort, sweep, and prune: Part&nbsp;2
leanrada.com notes
For RSS readers: This article contains interactive content available on the original post on leanrada.com.In the first part, we figured that sorting lets us exploit the transitive property of inequality to optimise the number of pairwise tests.We ended up with - let’s call it a “simplified version”, of the full sweep-and-prune algorithm.This part explores the more sophisticated versions of sweep-and-prune.Sophisticated sip and prune.Proper sweep-and-prune 🧐Let’s see how the original version tackled the problem (Not sure which one’s original, tbh).First, sorting widthy objects.To account for the width of objects while keeping the benefits of unambiguous sort order, we track the left and the right edges of each object as two separate points.This is done by maintaining a separate array of edge points corresponding to the objects’ left & right edges.See how it works by playing with this draggable demo. The left and right edges of each ball are visualised. These edge points are stored in a...
2年前