Hemanth.HM

A Computer Polyglot, CLI + WEB ♥'r.

CSS Highlight Pseudo-elements

| Comments

The CSS Pseudo-Elements Level 4 (CSS4) provides an ability to select highlighted content, these represent a portions of a document that needs particular status and are typically styled differently to indicate that status to the user.

We have the below Highlight Pseudo-elements:

  • ::selection: the portion of a document that has been selected as the target

  • ::target-text: represents text directly targetted by the document URL’s fragment

  • ::spelling-error: content that has been flagged by the user agent as misspelled.

  • ::grammar-error: content that has been flagged by the user agent as grammatically incorrect.

Note: A future level of CSS may introduce ways to create custom highlight pseudo-elements.

Let us see few quick examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
::selection {
  background-color: #ff69b4;
}

::target-text {
  background-color: #ff69b4;
}

::spelling-error {
  text-decoration: underline wavy red;
}

::grammer-error {
  text-decoration: underline wavy green;
}

P.S: Modern browsers are yet to implement the last 3 of these pesduo elements.

Nullish Coalescing vs Logical Or

| Comments

Whenever I talk about ?? people have asked me the difference between Logical OR (||) vs Nullish coalescing (??) operators, so here is a quick post.

Logical Assignment Operators in JavaScript

| Comments

Logical assignment operators in JavaScript combine Logical Operators and Assignment Expressions.

1
2
3
//"Or Or Equals"
x ||= y;
x || (y = z);
1
2
3
// "And And Equals"
x &&= y;
x && (y = z);
1
2
3
// "QQ Equals"
x ??= y;
x ?? (y = z);

So, say you have a function updateID it can vary in the below ways:

1
2
3
4
5
6
7
8
9
10
11
const updateID = user => {

  // We can do this
  if (!user.id) user.id = 1

  // Or this
  user.id = user.id || 1

  // Or use logical assignment operator.
  user.id ||= 1
}

You could also use it with ??

1
2
3
4
5
6
function setOpts(opts) {
  opts.cat ??= 'meow'
  opts.dog ??= 'bow';
}

setOpts({ cat: 'meow' })

This is on stage-4 and you should be able to use it today!

I am happy that I was able to co-champion this proposal.

7 Years ago it was just a thought!

history

Reactive Context in Svelte

| Comments

The context API in svelte provides a way for components to communicate to each other without passing around data, functions props, or dispatching events!

Let us see a quick example:

1
2
3
4
5
6
7
8
// Parent has something like:
<script>
import { setContext } from 'svelte'

const state = {}

setContext('state', state)
</script>
1
2
3
4
5
6
// In the descendants/child components
<script>
import { getContext } from 'svelte'

const state = getContext('state')
</script>

That was easy, but say you have sistuation where the context need to be update later in time, to emulate we could setTimeout(() => {setContext('state', {})},1000) this would not reflect either in the parent or the child component.

store comes to our resuse in these cases:

1
2
3
4
5
6
7
8
9
// In the parent component:
import {setContext, getContext} from 'svelte';
import {writable} from 'svelte/store';

let state = writable({color: 'hotpink'});
setContext('state', state);

setTimeout(() => {$state = {color: 'red'}},2000);
// ^ Setting the store value after a timeout with assigment operation.
1
2
3
4
5
// In the child component:
import {getContext} from 'svelte';
const state = getContext('state')

// Access it like any other state variable, in this case `$state`

Here is a quick example:


This is explictily mentoined in the API docs too:

Context is not inherently reactive. If you need reactive values in context then you can pass a store into context, which will be reactive.

Handling Multiple Inputs FrontEnd Frameworks

| Comments

I just wanted to look into few frameworks on how they are handling multiple inputs particularly relating to the same value, if picked React, Vue and Svelte for this case study:

Say, you were to pick up some veggies from your salad today:

1
let veggies = ["🥬","🌽","🌶","🥦","🍠"];

It would be better to have array of objects for topping like [{id: '', label: '', value: ''}] but for the ease of the demo, let us stick with an array for the toppings. We need to create a simple group of inputs of type checkbox.

First up React with 🎣:

Crux of handling those multiple checkboxs:

1
2
3
4
5
6
7
8
 const [checkedItems, setCheckedItems] = useState({});

  const handleChange = event => {
    setCheckedItems({
      ...checkedItems,
      [event.target.name]: event.target.checked
    });
  };

It's Vue's trun:

This the best I could do, thanks to GreenJello for the quick review.

The key component here is v-model:

1
<input type="checkbox" v-model="veggie.selected"> 

Finally, Svelte:

The key factor here is bind:group:

1
<input type=checkbox bind:group={selectedveggies} value={veggie}>

Contoller vs Magic vs Concise code I shall leave it to the reader discretion and choice!

Top Level Await Node REPL

| Comments

One can use await keyword outside of async functions with Top-level await and the happy news is you can use it today in node and REPL.

As of today, if we execute the bleow peice of code in node env:

1
await Promise.resolve(42);

We would see a: SyntaxError: await is only valid in async function

So, we would normally mitigate this by wrapping it in an async function:

1
2
3
4
(async function() {
  const answer = await Promise.resolve(42);
  // answer would be 42
}());

But with top level await it get's easier we could just do const answer = await Promise.resolve(42); note that this will only work at the top level of modules only.

Well, Node.js v14.3.0 was released with the support of top-level await! But we always had a flag to enabled top-level await on the REPL, ever since node v10!

The --experimental-repl-await enables experimental top-level await keyword support in REPL, so all you have to do is start the REPL with the flag node --experimental-repl-await and that's it.

And within the script you could do something like:

1
2
3
4
// home.mjs
export function home() {
  return 'Hello home!';
}
1
2
3
// index.mjs
const home = await import('./foo.mjs');
console.log(home());

You could execute this using node --experimental-top-level-await.

TLDR: For REPL we would need --experimental-repl-await and for mjs scripts --experimental-top-level-await as of today.

Contact Picker API

| Comments

Native mobile apps always had this privilege of using the contact picker with ease and now web can do that too!

First up, Check if the browser support this API:

1
const hasContactPicker = "contacts" in navigator && "ContactsManager" in window;

invoke select method:

1
2
3
4
5
6
7
8
9
10
11
12
13
const properties = "name, tel, email, address, icon".split(",");
const options = { multiple: true };

const fetchContact = async evn => {
  try {
    const contacts = await navigator.contacts.select(properties, options);
    // ^ [{},{}]
  } catch (err) {
    //bombed
  }
};

contactButton.addEventListener("click", evn => fetchContact(evn));

contacts would be an array of objects with keys being the requested properties and values being the corresponding contact details.

Note:

  • This API works only on top level window.

  • User gesture is required before invoking the select method!

View the demo here and the source below:

WebSocketStream API

| Comments

WebSocketStream API came into play to make things easier with WebScoket API, which has the below limitations:

  • Ergonomics isn't great.
  • The important feature of backpressure is missing.
  • The onmessage event will keep firing until the page becomes completely unresponsive.
  • To find out when it is safe to start sending messages again, it is necessary to poll bufferedAmount.

Goals of WebSocketStream API:

  • Provide a WebSocket API that supports backpressure
  • Provide a modern, ergonomic, easy-to-use WebSocket API
  • Allow for future extension

^ From the explainer.

Sample code:

1
2
3
4
5
6
7
8
(async () => {
  const wss = new WebSocketStream("wss://echo.websocket.org/");
  const { readable, writable } = await wss.connection;
  const reader = readable.getReader();
  await writable.getWriter().write("echo");
  const { value, done } = await reader.read();
  console.log(value, done);
})();

DEMO:

DOM Element Timing API

| Comments

Measuring and monitoring the rendering timestamps of DOM elements is an important measure to improve and avoid regressions in page loads.

The Element Timing API, helps us to measure this with ease and it supports the below elements:

  • img elements.

  • image elements inside an svg.

  • video element's poster image.

  • Elements with a background-image.

  • Groups of text nodes.

Make sure the DOM element of interest has elementtiming content attribute:

1
2
3
<img src="" elementtiming="cat-pic" />
<p elementtiming="imp-doc">I need to watch this</p>
<!-- So on -->

Observe the element:

1
2
3
4
5
6
7
8
9
10
11
12
const observer = new PerformanceObserver(list => {
  let perfEntries = list.getEntries().forEach(function(entry) {
    if (entry.identifier) {
      if (entry.renderTime) {
        console.log(`${entry.renderTime}ms to render!`);
      } else {
        console.log(`${entry.loadTime}ms to load!`);
      }
    }
  });
});
observer.observe({ type: "element", buffered: true });

renderTime and loadTime will be DOMHighResTimeStamp time stamp in milliseconds.

Note: renderTime may not be available if the element is an image and it's loaded cross-origin without the Timing-Allow-Origin header.

DEMO:

Detect Dark Mode

| Comments

Detecting if the user preference is light or dark color theme is pretty easy with the prefers-color-scheme CSS media feature, below code gives more details on the same.

Detecting the prefered color scheme in JS:

1
2
const isDarkMode = window.matchMedia
    && window.matchMedia('(prefers-color-scheme: dark)').matches;

Or maybe a more generic method:

1
2
3
4
5
6
7
8
9
const checkType = type => window.matchMedia
    && window.matchMedia(`(prefers-color-scheme: ${type})`).matches);

const mode = {
    is : {
        dark: checkType('dark'),
        light: checkType('light')
    }
};
1
2
mode.is.dark; // true
mode.is.light; // false

matchMedia(query) returns a new MediaQueryList object with the parsed results of the specified media query string.

Detecting the prefered color scheme in CSS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* Light mode */
@media (prefers-color-scheme: light) {
    body {
        background-color: #000;
        color: white;
    }
}

/* Dark mode */
@media (prefers-color-scheme: dark) {
    body {
        background-color: #000;
        color: white;
    }
}

Note for the spec:

User preferences can also vary by medium. For example, a user may prefer dark themes on a glowing screen, but light themes when printing (to save ink and/or because inked text on blank paper prints better than blank letterforms knocked out of an inked background). UAs are expected to take such variances into consideration so that prefers-color-scheme reflects preferences appropriate to the medium rather than preferences taken out of context.

Handling prefered color changes:

1
2
3
4
5
window.matchMedia('(prefers-color-scheme: dark)')
    .addListener((e) => {
        const isDarkMode = e.matches;
        console.log(`${isDarkMode ? '🌚' : '🌞'}.`);
    });

Force setting dark mode:

1
2
3
4
5
6
7
8
9
10
11
12
13
const forceDarkMode = () => [...document.styleSheets[0].rules].forEach(rule => {
     const mediaText = rule.media && rule.media.mediaText || [];
     const hasColorScheme = mediaText.includes("prefers-color-scheme");
     const hasLightScheme = hasColorScheme && mediaText.includes("light");

     // Remove light mode if.
     if (hasLightScheme) {
         rule.media.deleteMedium("(prefers-color-scheme: light)");
     }

     // Append Dark mdoe.
     rule.media && rule.media.appendMedium("(prefers-color-scheme: dark)");
 });