Simulating Mouse Movement

If you’ve ever had to display an interactive animation during a live talk or a class, then you may know that it’s not always easy to interact with your slides and while talking.

This happened to me when I needed to show this particles demo to my students. I didn’t want to have to stay next to my computer to move my mouse in order to show off the demo.

See the Pen
Particles (on move)
by Louis Hoebregts (@Mamboleoo)
on CodePen.

If you do not interact with the iframe, you will see nothing but a blank space. As soon as you start moving your mouse or your finger, you can see the animation.

For that reason, I created the same demo but I used some extra code to simulate someone interacting with the demo.

See the Pen
Particles (fake)
by Louis Hoebregts (@Mamboleoo)
on CodePen.

Simplex noise

The trick here is to use an algorithm that will generate “smooth” random positions. If we use a classic random function, the fake mouse will be at a purely random position on every frame. What we want is to have a position on every frame that is directly linked to the previous one. Thankfully, there is a technique that does exactly what we need: Simplex noise (or more commonly known as Perlin noise).

Let’s take a look at this image where the height of each column is defined with random values on top, and values from Simplex noise algorithm below.

You can quickly notice that the bottom graph seems much smoother because every column height is connected to the previous one. Those graphs are only showing one dimension (the x-axis, from left to right) but with Simplex noise you can get values in multiples dimensions. In our case, we will need two dimensions for the X and Y coordinates of the fake mouse we’re simulating.

If you are more interested to know how Simplex noise works, check out the video “I.5: Perlin Noise – The Nature of Code” by Daniel Shiffman

Get noise coordinates

The first thing we need to make our demo work is to implement a script that generates noise. In my case, I’m using this script by Seph.

Once the noise script is loaded, we can start using it on every frame to make our mouse move.

I will be using an image of a mouse for the demos that I put on position: fixed; with a class .mouse, but you could animate anything else for your own projects.

So, let’s take a look at the code:

// We retrieve the image from the DOM
const el = document.querySelector('.mouse'); // The render function is called on every frame
function render (a) { // The a variable is the amount of milliseconds since we started our script // Get a noise value based on the elapsed time to get a new value on every frame // This noise algorithm is returning values between [-1, 1] so we need to map them to [0, 1] by adding one to the value and dividing it by 2 const noiseX = (noise.simplex2(0, a*0.0005) + 1) / 2; // We get another noise value for the y axis but because we don't want the same value than x, we need to use another value for the first parameter const noiseY = (noise.simplex2(1, a*0.0005) + 1) / 2; // Convert the noise values from [0, 1] to the size of the window const x = noiseX * window.innerWidth; const y = noiseY * window.innerHeight; // Apply the x & y coordinates on our element el.style.transform = `translate(${x}px, ${y}px)`; // Call the render function once the browser is ready to make it an infinite loop requestAnimationFrame(render);
} // Ask the browser to call render to start our animation
requestAnimationFrame(render);

Here is the result we get with the above script:

See the Pen
Virtual user 1
by Louis Hoebregts (@Mamboleoo)
on CodePen.

Allow interactivity

With the current code, we are not allowed to interact with our demo anymore. Let’s add a bit more code to use our real mouse position when we interact with the demo and switch back to a fake mouse as soon as we stop.

const el = document.querySelector('.mouse');
let lastMove = 0; // When the mouse is being moved
function onMouseMove (e) { // Get the x and y coordinates x = e.clientX; y = e.clientY; // Save the last move time lastMove = Date.now();
} // Update the mouse position based on x & y
function updateMouse (x, y) { el.style.transform = `translate(${x}px, ${y}px)`;
} function render (a) { // Check if last move was more than 500ms ago if (Date.now() - lastMove > 500) { // Generate a fake mouse position ... updateMouse(x, y); }
} // Listen to mouse events
window.addEventListener('mousemove', onMouseMove);

Now, if you move your mouse, the fake mouse will follow yours. If you stop moving for 500ms, the fake mouse will start moving again.

See the Pen
Virtual user 3
by Louis Hoebregts (@Mamboleoo)
on CodePen.

Customized movement

The speed of the mouse can be updated by changing the value of the third parameter. So far, we are setting this value by taking the elapsed time multiplied by 0.0005, which is equal to a/2000.

// Define a speed ratio
const speed = a * 0.0005;
// Use the speed
const noiseX = (noise.simplex3(1, 0, speed) + 1) / 2;

We can also add a bit more randomness in the changes of direction by adding more noise from its position.

let random = 0;
function render (a) { ... // Update the random value random += 0.1; // Compute a x random offset based on the window width const randX = noise.simplex3(1, 0, random) * window.innerWidth * 0.1; // Compute a y random offset based on the window height const randY = noise.simplex3(3, 0, random) * window.innerHeight * 0.1; // Define the x & y values based on (noise * screen) + randomness const x = noiseX * innerWidth + randX; const y = noiseY * innerHeight + randY; ...
}

Play with the inputs to see how speed and randomly calculated values can influence the fake mouse movement.

See the Pen
Virtual user 4
by Louis Hoebregts (@Mamboleoo)
on CodePen.

More mice

Now that we have created one fake mouse, why not create 500 of them?

See the Pen
Virtual user 5
by Louis Hoebregts (@Mamboleoo)
on CodePen.

I now use this trick for almost all my demos. I think it’s really cool to be able to display a project without using a video or being forced to keep moving the mouse randomly while trying to talk about the demo.

If you have any questions or remarks, please leave a comment below or ping me on Twitter.

The post Simulating Mouse Movement appeared first on CSS-Tricks.

Using the Web Speech API for Multilingual Translations

Since the early days of science fiction, we have fantasized about machines that talk to us. Today it is commonplace. Even so, the technology for making websites talk is still pretty new.

We can make our pages on the web talk using the SpeechSynthesis part of the Web Speech API. This is still considered an experimental technology but it has great support in the latest versions of Chrome, Safari, and Firefox.

The fun part for me is using this technology with foreign languages. For that, Mac OSX and most Windows installations have great support on all browsers. Chrome loads a set of voices remotely, so if your operating system does not have international voices installed, just use Chrome. We’re going to walk through a three-step process to create a page that speaks the same text in multiple languages. Some of the basic code is derived from documentation found here but the final product adds some fun features and can be viewed at my Polyglot CodePen here.

Screen shot of the completed Polyglot app with a menu of languages.

Step 1: Start Simple

Let’s create a basic page with a <textarea> for the text we want the page to speak and include a button to click to trigger the speech.

<div id="wrapper"> <h1>Simple Text To Speech</h1> <p id="warning">Sorry, your browser does not support the Web Speech API.</p> <textarea id="txtFld">I love the sound of my computer-generated voice.</textarea> <label for="txtFld">Type text above. Then click the Speak button.</label> <div> <button type="button" id="speakBtn">Speak</button> <br> <p>Note: For best results on a Mac, use the latest version of Chrome, Safari, or FireFox. On Windows, use Chrome.</p> </div>
</div>

The paragraph with ID warning will be shown only if the JavaScript detects no support for the Web Speech API. Also, note the ID values for the textarea and the button as we will use those in our JavaScript.

Feel free to style the HTML any way you’d like. You’re also free to work off the demo I created:

See the Pen
Text-To-Speech Part 1
by Steven Estrella (@sgestrella)
on CodePen.

Adding a style rule for the disabled state of the button is a good idea to avoid confusion for the few people who still use incompatible browsers, like the now-quaint Internet Explorer. Also, let’s use a style rule to hide the warning by default so we can control when it’s actually needed.

button:disabled { cursor: not-allowed; opacity: 0.3;
} #warning { color: red; display: none; font-size: 1.4rem;
}

Now on to the JavaScript! First, we add two variables to serve as references to the “Speak” button that triggers the speech and to the <textarea> element. An event listener at the bottom of the code tells the document to wait until the DOM elements load before calling the init() function. I used a handy utility function I call “qs” that is defined at the bottom of the code. It is a shortcut alternative to document.querySelector and it selects whatever selector value I pass to it and returns an object reference. Then we’ll add an event listener to the speakBtn object to make the button call the talk() function.

The talk() function creates a new instance of the SpeechSynthesisUtterance object that is part of the Web Speech API. It adds the text from the <textarea>(using ID txtFld) to the text property. Then the utterance is passed to the speechSynthesis method of the window object and we hear the spoken text. The specific voice you hear will vary by browser and operating system. On my Mac, for example, my default language is set to American English and the default voice for English is Alex. In Step 2, we will add code to create a menu to help the user choose voices for all available languages.

let speakBtn, txtFld; function init() { speakBtn = qs("#speakBtn"); txtFld = qs("#txtFld"); speakBtn.addEventListener("click", talk, false); if (!window.speechSynthesis) { speakBtn.disabled = true; qs("#warning").style.display = "block"; }
} function talk() { let u = new SpeechSynthesisUtterance(); u.text = txtFld.value; speechSynthesis.speak(u);
} // Reusable utility functions
function qs(selectorText) { // Saves lots of typing for those who eschew jQuery return document.querySelector(selectorText);
} document.addEventListener('DOMContentLoaded', function (e) { try {init();} catch (error) { console.log("Data didn't load", error); }
});

Step 2: A Menu of International Voices

If we want to use anything other than the default language and speaking voice, we will have to add a bit more code. So that’s what we’re going tackle next.

We’re going to add a select element to hold the menu of voice options:

<h1>Multilingual Text To Speech</h1>
<div class="uiunit"> <label for="speakerMenu">Voice: </label> <select id="speakerMenu"></select> speaks <span id="language">English.</span> <!-- etc. -->
</div>

Before we create the code to populate the menu options, we should take care of the code that will help us connect language codes to their corresponding names. Each language is identified by a two-letter code such as “en” for English or “es” for Español (Spanish). We will take a simple list of these codes and their corresponding languages and make an array of objects of the form: {"code": "pt", "name": "Portuguese"}. Then we’ll need a utility function to help us search an array of objects for the value of a given property. We will use it in a few minutes to quickly find the language name that matches the language code of the selected voice. Copy the code below so that the two functions are just above and just below the // Generic Utility Functions comment.

function getLanguageTags() { let langs = ["ar-Arabic","cs-Czech","da-Danish","de-German","el-Greek","en-English","eo-Esperanto","es-Spanish","et-Estonian","fi-Finnish","fr-French","he-Hebrew","hi-Hindi","hu-Hungarian","id-Indonesian","it-Italian","ja-Japanese","ko-Korean","la-Latin","lt-Lithuanian","lv-Latvian","nb-Norwegian Bokmal","nl-Dutch","nn-Norwegian Nynorsk","no-Norwegian","pl-Polish","pt-Portuguese","ro-Romanian","ru-Russian","sk-Slovak","sl-Slovenian","sq-Albanian","sr-Serbian","sv-Swedish","th-Thai","tr-Turkish","zh-Chinese"]; let langobjects = []; for (let i=0;i<langs.length;i++) { let langparts = langs[i].split("-"); langobjects.push({"code":langparts[0],"name":langparts[1]}); } return langobjects;
} // Generic Utility Functions
function searchObjects(array, prop, term, casesensitive = false) { // Searches an array of objects for a given term in a given property // Returns an array of only those objects that test positive let regex = new RegExp(term, casesensitive ? "" : "i"); let newArrayOfObjects = array.filter(obj => regex.test(obj[prop])); return newArrayOfObjects;
}

Now we can build out the options for the select element using JavaScript. We need to declare variables at the top of our JavaScript to hold references to the #speakerMenu select element, the #language span element, the array of synthesized voices (allVoices), an array of codes to identify the languages (langtags), and a place to keep track of the currently selected voice (voiceIndex). Add those just after the two variable declarations we created in Step 1.

let speakBtn, txtFld, speakerMenu, language, allVoices, langtags;
let voiceIndex = 0;

The updated init() function sets some additional references to the #speakerMenu and the #language span and places all the language codes into an array of objects called langtags. The feature detection part of the code changes here, too. If the Web Speech API is supported, the setUpVoices() function is called. Also, for Chrome, we have to listen for changes to the loaded voices and repeat the setup when needed. Chrome polls the available voices every time you switch between one of its remote voices (the ones listed with the Google prefix while you are in Chrome) and all the other voices which are stored locally in the user’s operating system.

function init() { speakBtn = qs("#speakBtn"); txtFld = qs("#txtFld"); speakerMenu = qs("#speakerMenu"); language = qs("#language"); langtags = getLanguageTags(); speakBtn.addEventListener("click", talk, false); speakerMenu.addEventListener("change", selectSpeaker, false); if (window.speechSynthesis) { if (speechSynthesis.onvoiceschanged !== undefined) { // Chrome gets the voices asynchronously so this is needed speechSynthesis.onvoiceschanged = setUpVoices; } setUpVoices(); // For all the other browsers } else{ speakBtn.disabled = true; speakerMenu.disabled = true; qs("#warning").style.display = "block"; }
}

The setUpVoices() function gets an array of what are called SpeechSynthesisVoice objects by calling the getVoices() method of the speechSynthesis object. This is done in our code using the getAllVoices() function. Unfortunately, I have found that the speechSynthesis.getVoices() method sometimes returns duplicates in the list, so I devoted nine lines of code to eliminate the those. Finally, at the end of getAllVoices(), I added a unique identifier number to each of the SpeechSynthesisVoice objects. That will help us in Step 3 when we need to filter the list of voices to only show voices for a given language. When complete, the allVoices array will contain objects that look like the ones below. Each object has id, voiceURI, name, and lang attributes. The localService attribute indicates whether the code for the voice is stored on the user’s computer or remotely on Google’s servers. Notice the lang attribute. The value consists of a two-letter language code (e.g. “es” for Spanish) followed by a dash and a region code (e.g. “MX” for Mexico). This identifies the language and regional accent of each voice.

{id:48, voiceURI:"Paulina", name:"Paulina", lang: "es-MX", localService:true},
{id:52, voiceURI:"Samantha", name:"Samantha", lang: "en-US", localService:true},
{id:72, voiceURI:"Google Deutsch", name:"Google Deutsch", lang: "de-DE", localService:false}

The last line of setUpVoices() calls a function to create the list of options that will appear in the #speakerMenu select element. The value of the id attribute for each voice is placed in the value attribute for the option. The name and lang attributes are the visible text items that appear in each option along with “(premium)” for those voices that are marked that way on some operating systems and browsers.

function setUpVoices() { allVoices = getAllVoices(); createSpeakerMenu(allVoices);
} function getAllVoices() { let voicesall = speechSynthesis.getVoices(); let vuris = []; let voices = []; voicesall.forEach(function(obj,index) { let uri = obj.voiceURI; if (!vuris.includes(uri)) { vuris.push(uri); voices.push(obj); } }); voices.forEach(function(obj,index) {obj.id = index;}); return voices;
} function createSpeakerMenu(voices) { let code = ; voices.forEach(function(vobj,i) { code += `<option value=${vobj.id}>`; code += `${vobj.name} (${vobj.lang})`; code += vobj.voiceURI.includes(".premium") ? ' (premium)' : ; code += `</option>`; }); speakerMenu.innerHTML = code; speakerMenu.selectedIndex = voiceIndex;
}

You might recall that in the init() function, we had set up an event listener to call selectSpeaker() whenever the speakerMenu changes. The selectSpeaker() function stores the selectedIndex of the #speakerMenu select element. Next, it gets the value of the selected item which will be an integer that corresponds to the index of that voice in the allVoices() array. So, now we have retrieved the SpeechSynthesisVoice we want. We then grab the first two letters of the lang attribute (e.g. “en,” “es,” “ru,” “de,” “fr”) and use that code to search the langtags array of language objects to find the appropriate language name. The searchObjects() function returns an array that will likely have only one entry. Regardless, the first entry (langcodeobj[0]) is all we need. Finally, we assign that name to the innerHTML attribute of the language span and it shows on the screen as expected.

// Code for when the user selects a speaker
function selectSpeaker() { voiceIndex = speakerMenu.selectedIndex; let sval = Number(speakerMenu.value); let voice = allVoices[sval]; let langcode = voice.lang.substring(0,2); let langcodeobj = searchObjects(langtags, "code", langcode); language.innerHTML = langcodeobj[0].name;
}

The only thing left for Step 2 to be complete is to make sure the talk() function works when we click the “Speak” button. Modify the talk() function to add attributes to the utterance to control which voice and language are used and how fast to speak the text. In my testing, a rate range of 0.5 to 2 works reliably well. I found that a rate below 0.5 has no effect. I think 0.8 works as a nice default for many languages, but as we’ll see in Step 3, there’s an easy way to let the user decide.

function talk() { let sval = Number(speakerMenu.value); let u = new SpeechSynthesisUtterance(); u.voice = allVoices[sval]; u.lang = u.voice.lang; u.text = txtFld.value; u.rate = 0.8; speechSynthesis.speak(u);
}

That’s it for Step 2! Here’s the result of what we’ve done so far:

See the Pen
Text-To-Speech Part 2
by Steven Estrella (@sgestrella)
on CodePen.

Play around with it a bit. Sometimes it is fun to type an English phrase and then assign a French or German speaker to say it. Conversely, if you want to hear your worst first-year Spanish student, type a Spanish phrase and assign it to be spoken by an English voice.

Step 3: The Complete Polyglot

We’re in the final stretch! Some of the things we do in this step will be bits of polish to the UI but there are some functional things we need to do as well to button everything up. specifically, we’re going to:

  • Create a menu of available language options
  • Allow users to define the speed of the speech
  • Define a default phrase in the textarea that translates on language selection

Here’s what we’re looking at:

We’re adding a dropdown menu, speech rate setting, and a default phrase.

In the HTML, we’re going to add a new <select> element for the language menu and a number input (which will be used later to set the rate of speech). Notice we have deleted the #language span as it is no longer relevant once the language menu is working.

<div class="uiunit"> <label for="languageMenu">Language: </label> <select id="languageMenu"> <option selected value="all">Show All</option> </select>
</div> <div class="uiunit"> <label for="speakerMenu">Voice: </label><select id="speakerMenu"></select>
</div> <div class="uiunit"> <label for="rateFld">Speed: </label> <input type="number" id="rateFld" min="0.5" max="2" step="0.1" value="0.8" />
</div>

In the JavaScript, we will need to modify the variable declarations. We will keep track of all dialects in the allLanguages array and just the main languages in the primaryLanguages array. The langhash and langcodehash arrays will serve as hash tables so we can quickly get a language name when all we know is the two-letter language code and vice versa. We should only need to setup the languages menu once so a Boolean flag for initialSetup will come in handy.

let speakBtn, txtFld, speakerMenu, allVoices, langtags;
let voiceIndex = 0;
let allLanguages, primaryLanguages, langhash, langcodehash;
let rateFld, languageMenu, blurbs;
let initialSetup = true;
let defaultBlurb = "I enjoy the traditional music of my native country.";

In the new init() function, let’s remove the line language = qs("#language"); then add the new code as seen here to create the blurbs, reference the rateFld number input and languageMenu select, and create hash tables for looking up language names and tags.

function init() { // ...keep existing content but delete language = qs("#language"); createBlurbs(); rateFld = qs("#rateFld"); languageMenu = qs("#languageMenu"); languageMenu.addEventListener("change", selectLanguage, false); langhash = getLookupTable(langtags, "name"); langcodehash = getLookupTable(langtags, "code"); if (window.speechSynthesis) { // ...keep existing content } else{ // ...keep existing content languageMenu.disabled = true; }
}

The setUpVoices() function needs some work to accommodate the new languages menu and to trigger the filterVoices() function which we will use now to populate the #speakerMenu element. Also, we’re going to add the new functions: getAllLanguages() and getPrimaryLanguages(). The first one assembles an array of the unique values for the lang attribute found in the allVoices array of objects. Notice the return statement uses the spread operator combined with a new Set object to ensure that the returned array has no duplicates. The getPrimaryLanguages() function returns an array of the two-letter country codes. That makes a smaller list of just the main languages without reference to regional dialects.

function setUpVoices() { allVoices = getAllVoices(); allLanguages = getAllLanguages(allVoices); primaryLanguages = getPrimaryLanguages(allLanguages); filterVoices(); if (initialSetup && allVoices.length) { initialSetup = false; createLanguageMenu(); }
} function getAllLanguages(voices) { let langs = []; voices.forEach(vobj => { langs.push(vobj.lang.trim()); }); return [...new Set(langs)];
} function getPrimaryLanguages(langlist) { let langs = []; langlist.forEach(vobj => { langs.push(vobj.substring(0,2)); }); return [...new Set(langs)];
}

The setUpVoices() function calls two additional functions. The filterVoices() function gets the two-letter language code from the current value of the #languageMenu select menu and uses it to filter the allVoices array and return only the available voice options for the chosen language. It then passes that array to the createSpeakerMenu() function (unchanged from Step 2) which populates the #speakerMenu with options. Then filterVoices() gets the blurb associated with the chosen language and places it in the textarea where it can be edited or replaced.

And, in case Chrome rebuilds this menu, the stored voiceIndex is used to restore the current selection. Next the createLanguageMenu() function uses our hash tables to create the needed menu options for the languageMenu select element. The selectLanguage() function is triggered whenever the user chooses a language. It then triggers filterVoices() and sets the #speakerMenu to display the first available option.

function filterVoices() { let langcode = languageMenu.value; voices = allVoices.filter(function (voice) { return langcode === "all" ? true : voice.lang.indexOf(langcode + "-") >= 0; }); createSpeakerMenu(voices); let t = blurbs[languageMenu.options[languageMenu.selectedIndex].text]; txtFld.value = t ? t : defaultBlurb; speakerMenu.selectedIndex = voiceIndex;
} function createLanguageMenu() { let code = `<option selected value="all">Show All</option>`; let langnames = []; primaryLanguages.forEach(function(lobj,i) { langnames.push(langcodehash[lobj.substring(0,2)].name); }); langnames.sort(); langnames.forEach(function(lname,i) { let lcode = langhash[lname].code; code += `<option value=${lcode}>${lname}</option>`; }); languageMenu.innerHTML = code;
} function selectLanguage() { filterVoices(); speakerMenu.selectedIndex = 0;
}

In the utility functions section of the code toward the bottom, add the following code. This generic little utility will help you the next time you need to create a lookup table for an array of objects. In our case, we will use this to allow us to easily match a language code with its corresponding language name and vice versa.

function getLookupTable(objectsArray, propname) { return objectsArray.reduce((accumulator, currentValue) => (accumulator[currentValue[propname]] = currentValue, accumulator),{});
}

I added an array of text phrases, each of which is a translation of the English phrase, “I enjoy the traditional music of my native country.” The language it’s displayed in will correspond to what’s selected in the language men.

Here we see the beauty of UTF-8 on full display. Above the getLanguagesTags() function, let’s add the code that generates all those translated blurbs. I only read Spanish, English, some Portuguese, and very little German, so I have to take on faith that Google Translate is providing accurate translations for the rest. If any of these is your native language, feel free to leave corrections in the comments.

function createBlurbs() { blurbs = { "Arabic" : "أنا أستمتع بالموسيقى التقليدية لبلدي الأم.", "Chinese" : "我喜歡我祖國的傳統音樂。", "Czech" : "Mám rád tradiční hudbu mé rodné země.", "Danish" : "Jeg nyder den traditionelle musik i mit hjemland.", "Dutch" : "Ik geniet van de traditionele muziek van mijn geboorteland.", "English" : "I enjoy the traditional music of my native country.", "Finnish" : "Nautin kotimaassani perinteistä musiikkia.", "French" : "J'apprécie la musique traditionnelle de mon pays d'origine.", "German" : "Ich genieße die traditionelle Musik meiner Heimat.", "Greek" : "Απολαμβάνω την παραδοσιακή μουσική της πατρίδας μου.", "Hebrew" : "אני נהנה מהמוסיקה המסורתית של מולדתי.", "Hindi" : "मैं अपने मूल देश के पारंपरिक संगीत का आनंद लेता हूं।", "Hungarian" : "Élvezem az én hazám hagyományos zenéjét.", "Indonesian" : "Saya menikmati musik tradisional negara asal saya.", "Italian" : "Mi piace la musica tradizionale del mio paese natale.", "Japanese" : "私は母国の伝統音楽を楽しんでいます。", "Korean" : "나는 내 조국의 전통 음악을 즐긴다.", "Norwegian Bokmal" : "Jeg liker den tradisjonelle musikken i mitt hjemland.", "Polish" : "Lubię tradycyjną muzykę mojego kraju.", "Portuguese" : "Eu gosto da música tradicional do meu país natal.", "Romanian" : "Îmi place muzica tradițională din țara mea natală.", "Russian" : "Мне нравится традиционная музыка моей родной страны.", "Slovak" : "Mám rád tradičnú hudbu svojej rodnej krajiny.", "Spanish" : "Disfruto de la música tradicional de mi país natal.", "Swedish" : "Jag njuter av traditionell musik i mitt hemland.", "Thai" : "ฉันเพลิดเพลินกับดนตรีดั้งเดิมของประเทศบ้านเกิดของฉัน", "Turkish" : "Ülkemdeki geleneksel müzikten zevk alıyorum." };
}

There’s one last thing: the numeric input for controlling the playback speed of the speech. Modify the talk() function to get the speech rate from the number input and we’re good to go!

Here’s the final product:

function talk() { ...// no changes except for the rateFld.value reference u.rate = Number(rateFld.value); speechSynthesis.speak(u);
}

See the Pen
Polyglot: Text-To-Speech in Multiple Languages
by Steven Estrella (@sgestrella)
on CodePen.

A Real World Application

My interest in this technology started many years ago in 1990 when I created a 26-lesson curriculum as part of my dissertation. It was delivered using my first programming language, HyperCard, on a Macintosh Plus which had a primitive text-to-speech feature. I used that feature to provide some feedback to the user while they progressed through the material. More recently, in 2018, I created a free progressive web app called Buenos Verbos that helps Spanish language students search and filter a database of 766 verbs. The chosen verb is then fully conjugated and the user can click the forms to hear them spoken. So perhaps web pages might like to talk and with some imagination you may find reasons to encourage them. The question is: what will you make your website say next?

The post Using the Web Speech API for Multilingual Translations appeared first on CSS-Tricks.

Inline SVG… Cached

I wrote that using inline <svg> icons makes for the best icon system. I still think that’s true. It’s the easiest possible way to drop an icon onto a page. No network request, perfectly styleable.

But inlining code has some drawbacks, one of which is that it doesn’t take advantage of caching. You’re making the browser read and process the same code over and over as you browse around. Not that big of a deal. There are much bigger performance fish to fry, right? But it’s still fun to think about more efficient patterns.

Scott Jehl wrote that just because you inline something doesn’t mean you can’t cache it. Let’s see if Scott’s idea can extend to SVG icons.

Starting with inline SVG

Like this…

<!DOCTYPE html>
<html lang="en"> <head> <title>Inline SVG</title> <link rel="stylesheet" href="/styles/style.css">
</head> <body> ... <svg width="24" height="24" viewBox="0 0 24 24" class="icon icon-alarm" xmlns="http://www.w3.org/2000/svg"> <path id="icon-alarm" d="M11.5,22C11.64,22 11.77,22 11.9,21.96C12.55,21.82 13.09,21.38 13.34,20.78C13.44,20.54 13.5,20.27 13.5,20H9.5A2,2 0 0,0 11.5,22M18,10.5C18,7.43 15.86,4.86 13,4.18V3.5A1.5,1.5 0 0,0 11.5,2A1.5,1.5 0 0,0 10,3.5V4.18C7.13,4.86 5,7.43 5,10.5V16L3,18V19H20V18L18,16M19.97,10H21.97C21.82,6.79 20.24,3.97 17.85,2.15L16.42,3.58C18.46,5 19.82,7.35 19.97,10M6.58,3.58L5.15,2.15C2.76,3.97 1.18,6.79 1,10H3C3.18,7.35 4.54,5 6.58,3.58Z"></path> </svg>

It’s weirdly easy to toss text into browser cache as a file

In the above HTML, the selector .icon-alarm will fetch us the entire chunk of <svg> for that icon.

const iconHTML = document.querySelector(".icon-alarm").outerHTML;

Then we can plunk it into the browser’s cache like this:

if ("caches" in window) { caches.open('static').then(function(cache) { cache.put("/icons/icon-wheelchair.svg", new Response( iconHTML, { headers: {'Content-Type': 'image/svg+xml'} } )); }
}

See the file path /icons/icon-wheelchair.svg? That’s kinda just made up. But it really will be put in the cache at that location.

Let’s make sure the browser grabs that file out of the cache when it’s requested

We’ll register a Service Worker on our pages:

if (navigator.serviceWorker) { navigator.serviceWorker.register('/sw.js', { scope: '/' });
}

The service worker itself will be quite small, just a cache matcher:

self.addEventListener("fetch", event => { let request = event.request; event.respondWith( caches.match(request).then(response => { return response || fetch(request); }) );
});

But… we never request that file, because our icons are inline.

True. But what if other pages benefitted from that cache? For example, an SVG icon could be placed on the page like this:

<svg class="icon"> <use xlink:href="/icons/icon-alarm.svg#icon-alarm" /> </svg>

Since /icons/icon-alarm.svg is sitting there ready in cache, the browser will happily pluck it out of cache and display it.

(I was kind of amazed this works. Edge doesn’t like <use> elements that link to files, but that’ll be over soon enough.)

And even if the file isn’t in the cache, assuming we actually chuck this file on the file system likely the result of some kind of “include” (I used Nunjucks on the demo).

But… <use> and inline SVG aren’t quite the same

True. What I like about the above is that it’s making use of the cache and the icons should render close to immediately. And there are some things you can style this way — for example, setting the fill on the parent icon should go through the shadow DOM that the <use> creates and colorize the SVG elements within.

Still, it’s not the same. The shadow DOM is a big barrier compared to inline SVG.

So, enhance them! We could asynchronously load a script that finds each SVG icon, Ajaxs for the SVG it needs, and replaces the <use> stuff…

const icons = document.querySelectorAll("svg.icon"); icons.forEach(icon => { const url = icon.querySelector("use").getAttribute("xlink:href"); // Might wanna look for href also fetch(url) .then(response => response.text()) .then(data => { // This is probably a bit layout-thrashy. Someone smarter than me could probably fix that up. // Replace the <svg><use></svg> with inline SVG const newEl = document.createElement("span"); newEl.innerHTML = data; icon.parentNode.replaceChild(newEl, icon); // Remove the <span>s const parent = newEl.parentNode; while (newEl.firstChild) parent.insertBefore(newEl.firstChild, newEl); parent.removeChild(newEl); });
});

Now, assuming this JavaScript executes correctly, this page has inline SVG available just like the original page did.

Demo & Repo

Accessibility Events

“There isn’t some way to know when—…?”

There is always a pause here. The client knows what they’re asking, and I know what they’re asking, but putting it into words—saying it out loud—turns unexpectedly difficult.

In the moments before the asking, it was a purely technical question—no different from “can we do this when a user is on their phone.” But there’s always a pause, because this question doesn’t come easy; not like all the other questions about browsers and connection speeds did. A phrase like “in an assisted browsing context” doesn’t spring to mind as readily as “on a phone,” “in Internet Explorer,” or “on a slow connection.” The former, well, that’s something I would say—a phrase squarely in the realm of accessibility consultants. The latter the client can relate to. They have a phone, they’ve used other browsers, they’ve been stuck with slow internet connections.

“There isn’t some way to know when—… a user is… using something like a screen reader…?”

An easy question that begets a complicated answer is standard fare for almost any exchange with a web developer. This answer has, for a long time, been a refreshing deviation from that norm: “no, we can’t.”

The matter is, I’ll offer, technically impossible; computers, you see, can’t talk to each other that way. Often, there’s a palpable relief here: “no” to the technical part; “no” to the the computers part. That is, of course, all they had meant to ask. I truly believe that.

Even if we could, I’ll explain, we wouldn’t really want to. Forking our codebase that way would put more burden on us maintainers, not less. There’s an easy parallel to the “when they’re on a phone” conversation, here; one we’ve surely had already. We can never know a user’s browsing context for certain, and making assumptions will only get us and our users into trouble. Whenever a feature, component, or new design treatment was added or changed, we’d be left having all the same conversations around how to translate it over to the “accessible” experience. If those features aren’t essential in the first place, well, are they worth having at all? If those features are essential—well, we’ll still need to find a way to make them work in both contexts.

It could seem like an enticing option for our users, at first glance: an enhanced, fully-featured website, on the one hand, a fully accessible alternative experience on the other. That unravels with even the slightest examination, though: if the fully-featured website isn’t accessible, the accessible website won’t be fully featured. By choosing to have the “accessible experience” deviate from the “real website,” we end up drawing a sharper line between those two definitions, and we nudge the “accessible experience” closer to an afterthought—limited and frustratingly out-of-sync with the “real” website, like so many dedicated mobile sites quickly became.

There’s never any disagreement, here. Again: this is all relatable. We’ve all found ourselves inescapably opted into using the “mobile” version of a website at some point. We’ve been here before as users; we’ve made these mistakes before as developers. We know better now.

But this isn’t a strictly technical question. This isn’t as simple as browser features and screen sizes—a question of one privileged browsing context or another. Technical questions come easy. Partway through the asking—in the hesitation, in the pause, in the word stumbled over—what was meant to be a mundane development question became something much more fraught. Because there was a word that fit.

“Is there a way we can know when a user has a disability?”

The easy “no” felt empowering; a cop-out. “It doesn’t matter; it can’t be done” in response to a deeply fraught question was an unexpected balm for both the asked and the answered. There was, again, that palpable relief—”no” to the technical part; “no” to the the computers part. That was, of course, all they had meant to ask.

We no longer have that easy answer. In iOS 12.2 and MacOS 10.14.4, a toggle switch has appeared in Apple’s VoiceOver preferences, innocuously labeled “accessibility events.” It was rolled out to no fanfare—short of a brief mention in Apple’s iPhone User Guide—and we’re still not sure how it’s meant to be used. The most generous interpretation of the intention behind this feature is that it was developed with the same intention as a “UA string”-style identifier for users browsing via VoiceOver.

We do know this much: when this setting is enabled—and it is, by default—your browser will identify you as using VoiceOver to help you browse the web. If you’re using Apple’s VoiceOver, both your phone and your computer will broadcast your assumed disability to the entire internet, unless and until you specifically tell it to stop.

If you’re not furious at this change, you should be—not just for what it means for users, but what it foists upon you. Apple has burdened you with the knowledge that, now, yes, you can know whether a user has a disability. We can use this information to serve up a limited alternative version of a website, into which we can very easily opt people of a protected class. And once we choose to start listening for “accessibility events,” well, we can capture that information, as anything else broadcast to the web. A user’s disability can and will be reduced to a single data point—a cold, impersonal true, inexorably bound to their name, stored in a database, perhaps destined to be sold, leaked, passed along to insurance providers, reduced to a targeted marketing opportunity. All under the auspice of inclusivity.

At some point, the developers responsible for the “accessibility events” feature were, I’m certain, asked whether such a feature were possible. Their answer was “yes.” I don’t doubt that they meant well. I’m just as certain that, in the moment, it felt like the right answer; a technical solution to a technical problem, and a simple matter of browsing context.

Someday—not far in the future, I trust—I’ll be asked a similar question. It will be asked hesitantly, haltingly. The pauses will ring all too familiar. I will no longer have the easy, familiar comfort of technical impossibility—no easy “no” to insulate me from the uncomfortable conversations I should have been having with clients all along. Now, there’s no technical reason that I can’t know whether a user is using “something like a screen reader.” I—my clients, their databases, their organizations, their parent companies, their partners, their VC funders, their advertisers, and so on unto infinity—can absolutely know when a user is disabled.

But I won’t play a part in helping to propagate the mistake Apple’s developers made. I’ll let my answer hang heavy and uncomfortable in the air: no. Not because we can’t—we can. Not because we shouldn’t, though, no, we still shouldn’t. No—now, I will allow the word to become as coarse as I had always wanted it to be, because I no longer have the cold comfort of “well, technically” to hide behind.

No.

 

The post Accessibility Events appeared first on CSS-Tricks.

Edge Goes Chromium: What Does it Mean for Front-End Developers?

In December 2018, Microsoft announced that Edge would adopt Chromium, the open source project that powers Google Chrome. Many within the industry reacted with sadness at the loss of browser diversity. Personally, I was jubilant. An official release date has yet to be announced, but it will be at some point this year. With its release, a whole host of HTML, JavaScript and CSS features will have achieved full cross-browser support.

The preview build is now available for Windows, and coming soon for Mac.

Not so long ago, I penned an article titled “The Long Slow Death of Internet Explorer.” Some of us are lucky enough have abandoned that browser already. But it wasn’t the only thing holding us back. Internet Explorer was the browser we all hated and Edge was meant to be its much-improved replacement. Unfortunately, Edge itself was quite the laggard. EdgeHTML is a fork of Trident, the engine that powered Internet Explorer. Microsoft significantly under-invested in Edge. The apple didn’t fall far from the tree. Edge’s User Voice website was a nice idea, allowing developers to vote for which features they wanted to be implemented. Unfortunately, as Dave Rupert put it, voting on the site was “like throwing coins in a wishing well.” The most requested features were left unimplemented for years.

There are a lot of features that pre-Chromium Edge doesn’t currently support but are available in other modern browsers and, once they’ve made the switch, we’ll be able to use them. Many of them can’t be polyfilled or worked around, so this release is a big deal.

Features we can look forward to using

So just what are those features, exactly? Let’s outline them right here and start to get excited about all the new things we’ll be able to do.

Custom Elements and Shadow DOM

Together, custom elements and shadow DOM allow developers to define custom, reusable and encapsulated components. A lot of people were asking for this one. People have been voting for its implementation since 2014, and we’re finally getting it.

HTML details and summary elements

The <details> and <summary> elements are part of HTML5 and have been supported since 2011 in Chrome. Used together, the elements generate a simple widget to show and hide content. While it is trivial to implement something similar using JavaScript, the <details> and <summary> elements work even when JavaScript is disabled or has failed to load.

See the Pen
details/summary
by CSS GRID (@cssgrid)
on CodePen.

Javascript Font Loading API

This one means a lot to some people. All modern browsers now support the CSS font-display property. However, you still might want to load your fonts with JavaScript. Font-loading monomaniac Zach Leatherman has an explainer of why you might want to load fonts with JavaScript even though we now have broad support for font-display. Ditching polyfills for this API is important because this JavaScript is, according to Zach:

[…] usually inlined in the critical path. The time spent parsing and executing polyfill JavaScript is essentially wasted on browsers that support the native CSS Font Loading API.”

In an article from 2018, Zach lamented:

[…] browser-provided CSS Font Loading API has pretty broad support and has been around for a long time but is confoundedly still missing from all available versions of Microsoft Edge.”

No longer!

JavaScript flat and flatMap

Most easily explained with a code snippet, flat() is useful when you have an array nested inside another array.

const things = ['thing1', 'thing2', ['thing3', ['thing4']]]
const flattenedThings = things.flat(2); // Returns ['thing1', 'thing2', 'thing3', 'thing4']

As its name suggests, flatMap() is equivalent to using both the map() method and flat().

These methods are also supported in Node 11. 🎉

JavaScript TextEncoder and TextDecoder

TextEncoder and TextDecoder are part of the encoding spec. They look to be useful when working with streams.

JavaScript Object rest and object spread

These are just like rest and spread properties for arrays.

const obj1 = { a: 100, b: 2000
} const obj2 = { c: 11000, d: 220
} const combinedObj = {...obj1, ...obj2} // {a: 100, b: 2000, c: 11000, d: 220}

JavaScript modules: dynamic import

Using a function-like syntax, dynamic imports allow you to lazy-load ES modules when a user needs them.

button.addEventListener("click", function() { import("./myModule.js").then(module => module.default());
});

CSS background-blend-mode property

background-blend-mode brings Photoshop style image manipulation to the web.

CSS prefers-reduced-motion media query

I can’t help feeling that not making people feel sick should be the default of a website, particularly as not all users will be aware that this setting exists. As animation on the web becomes more common, it’s important to recognize that animation can cause causes dizziness, nausea and headaches for some users.

CSS caret-color property

Admittedly a rather trivial feature, and one that could have safely and easily been used as progressive enhancement. It lets you style the blinking cursor in text input fields.

8-digit hex color notation

It’s nice to have consistency in a codebase. This includes sticking to either
the RGB, hexadecimal or HSL color format. If your preferred format is hex, then you had a problem because it required a switch to rgba() any time you needed to define transparency. Hex can now include an alpha (transparency) value. For example, #ffffff80 is equivalent to rgba(255, 255, 255, .5). Arguably, it’s not the most intuitive color format and has no actual benefit over rgba().

Intrinsic sizing

I’ve not seen as much hype or excitement for intrinsic sizing as some other new CSS features, but it’s the one I’m personally hankering for the most. Intrinsic sizing determines sizes based on the content of an element and introduces three new keywords into CSS: min-content, max-content and fit-content(). These keywords can be used most places that you would usually use a length, like height, width, min-width, max-width, min-height, max-height, grid-template-rows, grid-template-columns, and flex-basis.

CSS text-orientation property

Used in conjunction with the writing-mode property, text-orientation, specifies the orientation of text, as you might expect.

See the Pen
text-orientation: upright
by CSS GRID (@cssgrid)
on CodePen.

CSS :placeholder-shown pseudo-element

placeholder-shown was even available in Internet Explorer, yet somehow never made it into Edge… until now. UX research shows that placeholder text should generally be avoided. However, if you are using placeholder text, this is a handy way to apply styles conditionally based on whether the user has entered any text into the input.

CSS place-content property

place-content is shorthand for setting both the align-content and justify-content.

See the Pen
place-content
by CSS GRID (@cssgrid)
on CodePen.

CSS will-change property

The will-change property can be used as a performance optimization, informing the browser ahead of time that an element will change. Pre-Chromium Edge was actually good at handling animations performantly without the need for this property, but it will now have full cross-browser support.

CSS all property

https://css-tricks.com/almanac/properties/a/all/”>all is a shorthand for setting all CSS properties at once.

For example, setting button { all: unset; } is equivalent to:

button { background: none; border: none; color: inherit; font: inherit; outline: none; padding: 0;
}

Sadly, though, the revert keyword still hasn’t been implemented anywhere other than Safari, which somewhat limits the mileage we can get out of the all property.

CSS Shapes and Clip Path

Traditionally, the web has been rectangle-centric. It has a box model, after all. While we no longer need floats for layout, we can use them creatively for wrapping text around images and shapes with the shape-outside property. This can be combined with the clip-path property, which brings the ability to display an image inside a shape.

Clippy is an online clip-path editor

CSS :focus-within pseudo-class

If you want to apply special styles to an entire form when any one of its inputs are in focus, then :focus-within is the selector for you.

CSS contents keyword

This is pretty much essential if you’re working with CSS grid. This had been marked as “not planned” by Edge, despite 3,920 votes from developers.

For both flexbox and grid, only direct children become flex items or grid items, respectively. Anything that is nested deeper cannot be placed using flex or grid-positioning. In the words of the spec, when display: contents is applied to a parent element, “the element must be treated as if it had been replaced in the element tree by its contents,” allowing them to be laid out with a grid or with flexbox. Chris goes into a more thorough explanation that’s worth checking out.

There are, unfortunately, still some bugs with other browser implementations that affect accessibility.

The future holds so much more promise

We’ve only looked at features that will be supported by all modern browsers when Edge makes the move to Chromium. That said, the death of legacy Edge also makes a lot of other features feel a lot closer. Edge was the only browser dragging its feet on the Web Animation API and that showed no interest in any part of the Houdini specs, for example.

Credit: https://ishoudinireadyyet.com

The impact on browser testing

Testing in BrowserStack (left) and various browser apps on my iPhone (right)

Of course, the other huge plus for web developers is less testing. A lot of neglected Edge during cross-browser testing, so Edge users were more likely to have a broken experience. This was the main reason Microsoft decided to switch to Chromium. If your site is bug-free in one Chromium browser, then it’s probably fine in all of them. In the words of the Edge team, Chromium will provide “better web compatibility for our customers and less-fragmentation of the web for all web developers.” The large variety of devices and browsers makes browser testing one of the least enjoyable tasks that we’re responsible for as front-end developers. Edge will now be available for macOS users which is great for the many of us who work on a Mac. A subscription to BrowserStack will now be slightly less necessary.

Do we lose anything?

To my knowledge, the only feature that was supported everywhere except Chrome is SVG color fonts, which will no longer work in the Edge browser. Other color font formats (COLR, SBIX, CBDT/CBLC) will continue to work though.

What about other browsers?

Admittedly, Edge wasn’t the last subpar browser. All the features in this article are unsupported in Internet Explorer, and always will be. If you have users in Russia, you’ll need to support Yandex. If you have users in Africa, you’ll need to support Opera Mini. If you have users in China, then UC and QQ will be important to test against. If you don’t have these regional considerations, there’s never been a better time to ditch support for Internet Explorer and embrace the features the modern web has to offer. Plenty of PC users have stuck with Internet Explorer purely out of habit. Hopefully, a revamped Edge will be enough to tempt them away. An official Microsoft blog entry titled “The perils of using Internet Explorer as your default browser” concluded that, “Internet Explorer is a compatibility solution…developers by and large just aren’t testing for Internet Explorer these days.” For its remaining users, the majority of the web must look increasingly broken. It’s time to let it die.

Is Google a megalomaniac?

Life is about to get easier for web developers, yet the response to the Microsoft’s announcement was far from positive. Mozilla, for one, had a stridently pessimistic response, which accused Microsoft of “officially giving up on an independent shared platform for the internet.” The statement described Google as having “almost complete control of the infrastructure of our online lives” and a “monopolistic hold on unique assets.” It concluded that “ceding control of fundamental online infrastructure to a single company is terrible.”

Many have harked back to the days of IE6, the last time a browser achieved such an overwhelming market share. Internet Explorer, having won the browser war, gave in to total stagnation. Chrome, by contrast, ceaselessly pushes new features. Google participates actively with the web standards bodies the W3C and the WHATWG. Arguably though, it has an oversized influence in these bodies and the power to dictate the future shape of the web. Google Developer Relations does have a tendency to hype features that have shipped only in Chrome.

From competition to collaboration

Rather than being the new IE, Edge can help innovate the web forward. While it fell behind in many areas, it did lead the way for CSS grid, CSS exclusions, CSS regions and the new HTML imports spec. In a radical departure from historical behavior, Microsoft have become one of the world’s largest supporters of open source projects. That means all major browsers are now open source. Microsoft have stated that they intend to become a significant contributor to Chromium — in fact, they’ve already racked up over 300 merges. This will help Edge users, but will also benefit users of Chrome, Opera, Brave, and other Chromium-based browsers.

The post Edge Goes Chromium: What Does it Mean for Front-End Developers? appeared first on CSS-Tricks.

Undefined: The Third Boolean Value

I wanted to implement a notification message in one of my projects, similar to what you’d see in Google Docs while a document is saving. In other words, a message shows up indicating that the document is saving every time a change is made. Then, once the changes are saved, the message becomes: “All changes saved in Drive.”

Let’s take a look at how we might do that using a boolean value, but actually covering three possible states. This definitely isn’t the only way to do this, and frankly, I’m not even sure if it’s the best way. Either way, it worked for me.

Here’s an example of that “Saving…” state:

Screenshot of the Google Docs header with a message that says the document is saving.
The “Saving” notification displays any time a change is made to the document.

…and the “Saved” state:

Screenshot of the the message reading All Changes Saved after saving has completed.
The “Saved” notification displays once a change or set of changes has completed.

Using a Boolean value for to define the state was my immediate reaction. I could have a variable called isSaving and use it to render a conditional string in my template, like so:

let isSaving;

…and in the template:

<span>{{ isSaving ? ‘Saving...’ : ‘All changes saved’ }}</span>

Now, whenever we start saving, we set the value to true and then set it to false whenever no save is in progress. Simple, right?

There is a problem here, though, and it’s a bit of a UX issue. The default message is rendered as “All changes saved.” When the user initially lands on the page, there is no saving taking place and we get the “Saved” message even though no save ever happened. I would prefer showing nothing until the first change triggers the first “Saving” message.

This calls for a third state in our variable: isSaving. Now the question becomes: do we change the value to a string variable as one of the three states? We could do that, but what if we could get the third state in our current boolean variable itself?

isSaving can take two values: true or false. But what is the value directly after we have declared it in the statement: let isSaving;? It’s undefined because the value of any variable is undefined when it’s declared, unless something is assigned to it. Great! We can use that initial undefined value to our advantage… but, this will require a slight change in how we write our condition in the template.

The ternary operator we are using evaluates to the second expression for anything that can’t be converted to true. The values undefined and false both are not true and, hence, resolve as false for the ternary operator. Even an if/else statement would work a similar way because else is evaluated for anything that isn’t true. But we want to differentiate between undefined and false . This is fixable by explicitly checking for false value, too, like so:

<span>
{{ isSaving === true ? ‘Saving...’ : (isSaving === false ? ‘All changes saved’: ‘’) }}
</span>

We are now strictly checking for true and false values. This made our ternary operator a little nested and difficult to read. If our template supports if/else statements, then we can refactor the template like this:

<span>
{% if isSaving === true %} Saving...
{% elseif isSaving === false %} All changes saved
{% endif %}
</span>

Aha! Nothing renders when the variable is neither true nor false — exactly what we want!

The post Undefined: The Third Boolean Value appeared first on CSS-Tricks.

Decaying Sites

Websites have a tendency to decay all by themselves. Link rot, they call it. Unpaid domain name registrations. Companies that have gone out of business. Site owners that have lost interest. What’s sadder than a 404? Landing on a holding page of a URL that used to exist, but now has fallen into the hands of some domain hoarder after it expired, hoping someone will pay a premium to get it back.

That stuff is no fun. But what about sites that are totally still around, just old? What kind of fun things could we do to indicate oldness that’s, like, on purpose?

On the CodePen blog, we call out blog posts that haven’t been updated in at least a couple of years. We update documentation, sure, but we tend to leave blog posts alone as a historical record. So, we’re pretty clear about that:

<?php if (get_the_modified_date("Y") < 2017) { ?> <p class="callout"><strong>Heads up!</strong> This blog post hasn't been updated in over 2 years. CodePen is an ever changing place, so if this post references features, you're probably better off checking the <a href="/documentation/">docs</a>. Get in touch with <a href="https://codepen.io/support/">support</a> if you have further questions.</p>
<?php } ?>

We style it up like a little warning:

But what if it was less obvious? What if the text just kinda started going all to crap? Words falling off their lines and going out of focus? The older the content, the more decay:

See the Pen
Decayed Text
by Chris Coyier (@chriscoyier)
on CodePen.

What if you let a site decaye on purpose? Say, perhaps, you’re holding oto client work and the client hasn’t paid their bill. Dragoi Ciprian has a little idea (repo) for that. You set the due date and deadline:

var due_date = new Date('2017-02-27');
var days_deadline = 60;

Here’s a demo of that. As I write, I’m 30 days into a 90-day deadline. If the demo looks blank to you, well, I guess I should have paid my bill so this code could have been removed 😉

See the Pen
not-paid Demo
by Chris Coyier (@chriscoyier)
on CodePen.

Or maybe the screen could kinda flash red, like you’re getting hit in a video game.

Dave once mentioned this would be a cool browser extension, like the browser window could flash red when certain bad things are happening, like layout jank.

Or you could get all glitchy! (This demo is click-to-load, fast colors and motion warning.)

See the Pen
Glitchy loader
by Nathaniel Inman (@NathanielInman)
on CodePen.

See the Pen
CSS Glitched Text
by Lucas Bebber (@lbebber)
on CodePen.

Perhaps rather than basing things off a payment due date or the age of the content, these effects come into play based on how long it’s been since the site’s dependencies have been updated. Or at least had some kind of deployment push.


This is only sorta tangentially related, but it reminds me of the very, very scary game Lose/Lose:

Lose/Lose is a video-game with real life consequences. Each alien in the game is created based on a random file on the players [sic] computer. If the player kills the alien, the file it is based on is deleted. If the players [sic] ship is destroyed, the application itself is deleted.

Although touching aliens will cause the player to lose the game, and killing aliens awards points, the aliens will never actually fire at the player. This calls into question the player’s mission, which is never explicitly stated, only hinted at through classic game mechanics. Is the player supposed to be an aggressor? Or merely an observer, traversing through a dangerous land?

The post Decaying Sites appeared first on CSS-Tricks.

Fixed Headers, On-Page Links, and Overlapping Content, Oh My!

Let’s take a basic on-page link:

<a href="#section-two">Section Two</a>

When clicked, the browser will scroll itself to the element with that ID: <section id="section-two"></section>. A browser feature as old as browsers themselves, just about.

But as soon as position: fixed; came into play, it became a bit of an issue. The browser will still jump to bring the newly targeted element into view, but that element may be obscured by a fixed position element, which is pretty bad UX.

I called this “headbutting the browswer window” nearly 10 years ago, and went over some possible solutions. Nicolas Gallager documented five different techniques. I’m even using a fixed position header here in v17 of CSS-Tricks, and I don’t particularly love any of those techniques. I sort of punted on it and added top padding to all my <h3> elements, which is big enough for the header to fit there.

There is a new way though! Finally!

Šime Vidas documented this in Web Platform News. There are a bunch of CSS properties that go together as part of CSS scroll snapping, but it turns out that scroll-padding and scroll-margin can be used outside of a scroll snapping container.

body { scroll-padding-top: 70px; /* height of sticky header */
}

This only works in Chromium browsers:

See the Pen
Scroll Padding on Fixed Postion Headers
by Chris Coyier (@chriscoyier)
on CodePen.

This is such a useful thing we should hoot and holler for WebKit and Firefox to do it.

The post Fixed Headers, On-Page Links, and Overlapping Content, Oh My! appeared first on CSS-Tricks.

What Are Design Tokens?

I’ve been hearing a lot about design tokens lately, and although I’ve never had to work on a project that’s needed them, I think they’re super interesting and worth jotting down a few notes about. As I understand it, the general idea is this: design tokens are an agnostic way to store variables such as typography, color, and spacing so that your design system can be shared across platforms like iOS, Android, and regular ol’ websites.

Design tokens are starting to gain a bit of momentum in the design systems community, but they’re not an entirely new concept. There’s a great talk with Jina Anne and Jon Levine from 2016 where they talk about how design tokens are used in the Lightning Design System at Salesforce. They describe the complexity of the world we’re living in where a single organization that is building multiple web apps and native applications need to feel and look the same whilst not slowing down the development team. Jina also has made an in-depth video course about design tokens and in the preview for that she writes:

With design tokens, you can capture low-level values and then use them to create the styles for your product or app. You can maintain a scalable and consistent visual system for UI development.

Let’s take just one example: you probably have a typographic scale that you want to be identical across a bunch of platforms. Instead of storing the values for that scale in a CSS file and replicating them in every app or website, they can be stored in a JSON file that will then be transformed into the code needed for all those other platforms. Something like this:

{ "global": { "type": "token", "category": "typography" }, "aliases": { "TYPE_SIZE_SM": { "value": "14px" }, "TYPE_SIZE_MD": { "value": "25px" }, "TYPE_SIZE_LG": { "value": "44px" } },

You could write your own code to take this JSON file and convert it into all the variables you might need, for example, a Sass file would depend upon these tokens and might consume them as variables to be used elsewhere in a web app. One example of a tool that can do a lot of the hard work for us is Amazon’s Style Dictionary and here’s an example of how that works in practice:

I think this is ridiculously neat stuff. And I can see how it saves a ton of duplicate code and confusion across multiple teams since it serves as a single source of truth as opposed to having several codebases that have the same design requirements and their own stylesheets to maintain. Cristiano Rastelli also wrote about managing design tokens with Style Dictionary a little while ago and goes into a lot more depth on how to get started.

Your source of truth doesn’t even have to be a JSON file! In a post from earlier this year, Pavel Laptev shows us how to make these design tokens in Figma and, by using their API, abstract those values out of design mockups and use them in a codebase.

Pavel broke up his Figma doc into separate pages for his grid, spacers, palette and typography like this:

Right now, it seems like this requires a ton of effort to set up, but I reckon that tools like Sketch and Figma are only going to make stuff like this way easier for us in the near future – they probably want the source of truth to be in their specific design tool instead of some other tool.

The last thing I want to mention is this post by Brent Jackson where he wrote up some thoughts about interoperability when it comes to design systems. Specifically, he argues that there should be a specification for design tokens so that any CSS-in-JS library could consume that code in any format or style:

Design system tokens are meant to be flexible and work cross-platform, which means different teams, different implementations, and different libraries will name things differently. This is where this specification would fit in. A lot of interoperability could be realized, if we all, for example, named our color palette colors and named the font sizes we use fontSizes. What you do beyond that and what data format you use to store these values, is up to you. It’s trivial to convert JSON to ES modules to YAML or even TOML, if that’s your thing. It’s also just a data structure, so transforming between other data structures (e.g. design tools or a GraphQL API) should also be possible. This standard also wouldn’t try to solve the extremely complex problems of how to name the colors themselves.

Brent then went ahead and created a theme specification to solve this very problem. It looks like having a single standard for writing all our variables and settings would help us if we wanted to switch from one CSS-in-JS library to another, or if we wanted to switch to some other system that we haven’t imagined yet.

Anyway, I believe that design tokens are only starting to become mainstream and their popularity is about to increase as these tools, workflows, and standards become better with time — it’s all thoroughly exciting stuff!

The post What Are Design Tokens? appeared first on CSS-Tricks.

Make it hard to screw up driven development

Development is complicated. Our job is an ongoing battle between getting the job done and doing that job in a safe, long-lasting way.

Developers say things like, “I’m just going to do this quick and dirty first,” because it’s taken as fact that if you code anything quickly, it not only will be prone to mistakes, but that you’ll be deliberately not honoring established conventions and skipping tasks that make for more solid code.

There is probably no practical way to make it impossible to write sloppy, bad code, but it is fascinating to consider how tooling has evolved to make it harder.

Let’s get all Poka-yoke on development.

The obvious ones are automated code quality tools.

Say you’re writing JavaScript. ESLint is a mega-popular tool that looks at your code as you are writing it and lets you know about issues.

ESLint is configurable and those configurations can be enforced to a team’s liking. If you’d prefer to use some strong and established conventions, I believe the most popular out there is AirBnbs configuration.

There are alternatives to everything, of course. This post isn’t so much about a comprehensive tooling list as it is about considering the types of tools that help us push us toward writing better code. That said, stylelint is good for CSS, PHP_CodeSniffer is good for PHP, and Rubocop is good for Ruby.

Prettier is in a similar, but unique category. It is like a “beautifier” for your code, in that it helps you reformat it not only to look good but to follow team conventions (e.g. single quotes! Two space indentions!) as well. The most common way to use Prettier is that it runs as you save the file. So perhaps you write quickly and don’t worry about formatting as much, because it happens for you the second you save. There is an interesting side-benefit of quality here as Prettier can fail, and if it does, you have a problem in the syntax of your code you need to fix. Super useful.

Prettier failing.

I’m intrigued by tools like Sonarlint, Code Climate, and Resharper that look, to me, essentially like linters, but deliver only a best-practice analysis rather than configuring things yourself. It also claims to understand your code at a deeper level. Webhint and Deepscan look similarly interesting. Feel free to correct me if I have this wrong because I haven’t gotten a chance to use any of them yet.

Taking linting a step further, you can make passing lint tests a requirement before files can even be committed into Git. Git hooks are the ticket here, and the most popular tool for managing them is Husky.

Similarly, actual tests are powerful preventers of bad code.

It’s always smart to write tests. Deploying code that breaks features is embarrassing, a waste of time, and can negatively impact your business. Yet we do it all too often. The whole point of tests is to prevent that.

Things like Jest for JavaScript and RSpec for Ruby are useful, and considered unit testing. It’s work! You manually write functions that expect certain results. I expect that if I call a function with these parameters it returns this value!

Test-Driven Development (TDD) is a practice in which you write the test before you write the actual code that does the thing you’re trying to do. It’s a nice way to work if you can pull it off, as you’ve got code coverage from the get-go.

Another type of automated testing is integration (also known as end-to-end) testing. I’m a fan of Cypress for that. It simulates a user actually using a browser. Go to this URL! Click this! Fill out this field and submit the form! Does this thing exist now? Is the URL what it’s supposed to be? Is this other thing visible? That kind of testing is powerful in that a lot of things have to be going right for these to pass, so there is a ton of implied testing.

As a CSS kinda guy, I’m also a fan of tests that watch to make sure the site looks how it’s supposed to look and there aren’t unintended consequences of styling changings. Percy is awesome for that (see our video).

And while we’re talking about all the different types of automated testing you can do, there are all sorts of tools to automate some level of accessibiilty testing. Plus, there are tools like Calibre and SpeedCurve that automate Lighthouse for watching performance.

Languages and language features that help us, wittingly or not

Take JSX, for example. It’s entirely possible to write bad HTML in JSX, but you can’t write broken HTML. The component will error out entirely and you’ll know as you’re working. That’s not even close to the reason JSX exists, but I find it an interesting side effect. I’ve fixed many bugs in my career that had to do with malformed HTML causing problems, ranging from tiny side effects to massive layout blunders.

Prettier is catching the problem here, but we’d see an error in the console if this compiled and went to the browser.

Similarly, a tool like Emmet can help generate valid HTML. I use Emmet all the time, and didn’t even think of that until it was mentioned to me.

I also think of React features, like PropTypes, that throw errors when missing or unexpected data is thrown at them. Not to mention you can configure your linter to yell at you if you’re missing the PropType. That’s pretty powerful testing to be enforced for a fairly small amount of labor (compared to, say, writing a test). You can even force them to help with accessibility.

It would be impossible to not mention TypeScript here. One of the major points of using TypeScript is code safety. The fact that it’s getting huge (listen to Laurie Voss on this) points to the fact that we want to enforce that safety. I remember when Angular 2 came out, there were long, solid explanations as to why. People also talk about the tooling improvements you get with TypeScript: advanced autocompletion, navigation, and refactoring. They are all, in a way, also about code safety — having the editor help you write correct file names and function names. TypeScript or not, any sort of autocomplete/IntelliSense is great to have.

The whole idea of this post came from me thinking about how GraphQL has this “you can’t screw it up” quality to it. You can’t ask for data that isn’t there, as it will error right as you’re working with it — and then you’ll fix it. And you can’t get back data that you aren’t expecting, as you’ve described exactly what you want back and that’s what GraphQL does. It’s not that you can’t write bad code that uses GraphQL or write a bad GraphQL implementation, but the technology sort of encourages better code and I’m fascinated by that.

CSS-in-JS, while that’s probably too broad a term generally, applies to this discussion. Most of the solutions on that spectrum involve some kind of style scoping, and style scoping provides this “you can’t screw it up” topic we’re focusing on. You can’t cause unintended side effects when the selector you’ve just written compiles to something you’ve never hand-written, like .SpecificComponent_root_34lkj4x.

Your co-workers are an awesome line of defense

First, give y’allselves a system. Nothing goes to the master branch directly, and everything has to be a Merge/Pull Request. That gives you a spot to talk about code quality — not to mention a place where you can run a suite of automated tests before the code is dangerously close to production.

GitLab has a concept of approvers for a Merge Request. You pick some people that have to approve the branch before it can be merged.

GitHub has the same concept with protected branches. Perhaps the best thing you can do to prevent bad code is to widen the responsibility. There is always a risk this just becomes a glance-at-the-code-for-two-seconds-and-give-it-a-👍 motion, but that’s on y’all to make sure reviews are taken seriously. I’ve seen lots of value in a requirement that many sets of eyeballs need to be on code before it goes out. “Given enough eyeballs, all bugs are shallow” and all that.


We’ll always be screwing up code, but we can also always be finding ways not to.

The post Make it hard to screw up driven development appeared first on CSS-Tricks.