About

Friday, April 30, 2021

You Can Label a JavaScript `if` Statement

Labels are a feature that have existed since the creation of JavaScript. They aren’t new! I don’t think all that many people know about them and I’d even argue they are a bit confusing. But, as we’ll see, labels can be useful in very specific instances.

But first: A JavaScript label should not be confused with an HTML <label>, which is a completely different thing!

A JavaScript label is a way to name a statement or a block of code. Typically: loops and conditional statements. That allows you to break or continue the labeled statement from within. To apply a label to a statement, start the statement with label: and whatever you put as “label” will be the label you can reference later.

Here’s the basic syntax for a label:

let x = 0;
// Label a loop as "myLoop"
myLoop:
while (true) {
  if (x >= 10) {
    // This will cause "myLoop" to end.
    break myLoop;
  }
  x++;
}

Labels are only an internal reference to a statement and are not something that can be looked up, exported, or stored in a value. They also do not conflict with variable names, so if you really want to confuse people, you can have a loop and a variable be the same name! Please don’t do this —  future you, and anyone else that has to read your code will appreciate it. The use cases for labels are limited, but incredibly powerful in the right hands.

A brief intro to break and continue

Let’s back up a bit and talk about break and continue. A break statement will terminate the currently running loop or conditional statement. It is most commonly used in a switch statement to end a case, but it can also be used to end an if statement early, or also to cause a for or while loop to end and stop looping. It’s a great way to escape out of a conditional statement or end a loop early.

Here’s a basic example of break in use:

const x = 1;
switch(x) {
  case 1:
    console.log('On your mark!');
    break; // Doesn't check the rest of the switch statement if 1 is true
  case 2:
    console.log('Get set!');
    break; // Doesn't check the rest of the switch statement if 2 is true
  case 3:
    console.log('GO!');
    break; // Doesn't check the rest of the switch statement if 3 is true
}
// logs: 'On your mark!'

Similarly, the continue statement can be used with loops to end the current iteration early, and start the next run of the loop. This will only work inside of loops however.

Here’s continue in use:

for (let x = 0; x &< 10; x++) {
  if (x !== 5) continue; // If the number isn't five, go to the next pass of the loop.
  console.log(x);
}
// logs: 5

Using a label with break

Typically, a use case for labels comes up when you get into nested statements of any kind. Using them with break can stop a deeply nested loop or conditional and make it stop immediately.

Let’s get to that title of this blog post!

// Our outer if statement
outerIf: 
if (true) {
  // Our inner if statement
  innerIf:
  if (true) {
    break outerIf; // Immediately skips to the end of the outer if statement
  }
  console.log('This never logs!');
}

There you have it, you can label an if statement.

Using a label with continue

There have been times where I have made a nested loop and wanted to skip over some iterations of the outer loop while inside the inner loop. I usually wind up breaking the inner loop, then checking to see if I’m in the state I want to skip, then continue the outer loop. Being able to simplify that code down into an easier to read statement is great!

let x = 0;
outerLoop:
while (x < 10) {
  x++;
  for (let y = 0; y < x; y++) {
    // This will jump back to the top of outerLoop
    if (y === 5) continue outerLoop;
    console.log(x,y);
  }
  console.log('----'); // This will only happen if x < 6
}

Block statements and labels

Block statements in JavaScript are a way to scope your const and let variables to only a portion of your code. This can be useful if you want to localize some variables without having to create a function. The (big) caveat to this is that block statements are invalid in strict mode, which is what ES modules are by default.

Here’s a labeled block statement:

// This example throws a syntax error in an ES module
const myElement = document.createElement('p');
myConditionalBlock: {
  const myHash = window.location.hash;
  // escape the block if there is not a hash.
  if (!myHash) break myConditionalBlock;
  myElement.innerText = myHash;
}
console.log(myHash); // undefined
document.body.appendChild(myElement);

Real world usage

It took me a while to come up with a reason to use a label in everyday production code. This might be a bit of a stretch, but a place where a label in JavaScript might come in handy is to escape early from a loop while inside a switch statement. Since you can break while in a switch, being able to apply a label to a loop that ends it early could potentially make your code more efficient.

Here’s how we might use that in a calculator app:

const calculatorActions = [
  { action: "ADD", amount: 1 },
  { action: "SUB", amount: 5 },
  { action: "EQ" },
  { action: "ADD", amount: 10 }
];
    
let el = {};
let amount = 0;
calculate: while (el) {
  // Remove the first element of the calculatorActions array
  el = calculatorActions.shift();
  switch (el.action) {
    case "ADD":
      amount += el.amount;
      break; // Breaks the switch
    case "SUB":
      amount -= el.amount;
      break; // Breaks the switch
    case "EQ":
      break calculate; // Breaks the loop
    default:
      continue calculate; // If we have an action we don't know, skip it.
  }
}

This way, we can bail out of the calculate loop when a condition is matched rather than allowing the script to continue!

Conclusion

It’s rare that you will need to use a JavaScript label. In fact, you can lead a very fulfilling career without ever knowing that this exists. But, on the offhand chance you find that one place where this syntax helps out, you’re now empowered to use it.


The post You Can Label a JavaScript `if` Statement appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.



source https://css-tricks.com/you-can-label-a-javascript-if-statement/

Sending Large Files

I’ve got a podcast that will be 10 years old this coming January! Most of those episodes have one or more guests (plus me and Dave). Despite fancy modern options for recording podcasts with guests, like Riverside.fm or Zencastrl where guests don’t have to worry about recording their own audio, we haven’t made the leap to one of those yet.

We have the guests record their own audio locally (typically Quicktime Player or Windows Voice Recorder) because that way our editor can make the most of the editing process. At the end of the show, our guest has a file that is ~100MB that they need to send over to us.

How that handoff happens isn’t always completely obvious. Typically we don’t share a Slack with our guests, but when we do, that works for sharing large files like that. Even a Nitro-boosted Discord won’t take a file that big, though. I’d say 70% of the time, our guests chuck the file into their Dropbox and create a sharing link for us to download it. From there, it’s probably Google Drive 20% of the time, and the last 10% is some random thing.

That last 10% is stuff like uploading the file to a web server or file storage service the guest controls and they link us up to the file from there. If we were smarter, we’d probably use “File Request” links on Dropbox or Box.

I usually say something like, Send us that file however you like to send large files, because I don’t want to be too prescriptive about what service someone uses. You never know if someone has a particular aversion to some specific tech or company. I would always mention Firefox Send because it was meant for one-off file sending and I find people generally like and trust Mozilla. Alas, Firefox Send shut down.

Unfortunately, some abusive users were beginning to use Firefox Send to ship malware and conduct phishing attacks. When this problem was reported, we stopped the service. Please see the Mozilla Blog for more details on why this service was discontinued.

I guess it’s responsible to try to shut down bad behavior, but of course it was used for bad behavior. Dickwads use any and every service on the entire internet for bad behavior. The real answer, probably, is that it was just a little random side project that didn’t make any money and they didn’t feel like investing the time and money into fixing it. Fair enough, but of course that always costs you trust points. What else is on the chopping block?

I ran across Wormhole the other day which looks like a direct, if not better, replacement. It uses end-to-end encrypted and has some nice UX touches, like getting a share link before the upload is complete. It doesn’t say anything about how they intend to pay for it and support it long-term, but I’d guess the cost is somewhat minimal as they only host the files for 24 hours. They also don’t say if they intend to prevent bad behavior or if it’s just a free-for-all. Even with all the encryption and whatnot, I would imagine if a site like Google or Twitter found that tons of wormhole.app URLs had malware on them, they’d be blacklisted. That wouldn’t stop people from using it but it certainly stops people from finding it too. I did hear from Feross on this, and they have ideas to fight bad behavior if it comes to that.

The thing I’m the most surprised by is that we don’t get more emails where the email service itself just hosts the file. That might sound silly, as email is notorious for not accepting very large file attachments, but that has changed over the years with some of the big players. When you select a file that’s larger than 25MB in Gmail, it’ll offer to upload it to Google Drive and automatically share it with the person you’re sending the email to. iCloud does largely the same thing with Mail Drop.

Me, I use Dropbox quite a bit, but rarely for sharing one-off files. If I want to make sure I have a copy in perpetuity, sometimes I’ll even use a personal Amazon S3 bucket. But mostly I’ll just upload it to Droplr, which I’ve used for ages just for this kind of thing.


The post Sending Large Files appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.



source https://css-tricks.com/sending-large-files/

How to Monitor Core Web Vitals and Take Action with Raygun

Raygun is an error and performance monitoring software for websites and mobile apps. In the case of websites, you install their JavaScript snippet onto your site, which takes 2 seconds, and now you’ve got monitoring in place. Why? Well now you can watch the performance of your site, not just in a single report of one test, but historical dashboards, tracking that performance over time.

This is Real User Monitoring (RUM)

RUM is regarded as better data than the alternative, which is running synthetic tests. Imagine running a performance test against a headless browser. Useful, but fake. Better is to measure how real people are experiencing your site, which is exactly how Raygun does it.

When you log into Raygun, you’ll see high-level trends as to how your site is performing, with the ability to dig deeper into specific pages and individual user sessions.

Now with Core Web Vitals

Google’s latest user experience metrics, Largest Contentful Paint, First Input Delay, and Cumulative Layout Shift are now directly in Raygun:

What I found particularly cool about this is that you don’t have to necessarily pick which pages you want to track CWV on, they’ll be tracked on all pages you have Raygun installed on. I imagine for most sites, that’s all pages, so now you’ll have CWV (and all other performance information) on every page of your site. So rather than picking-and-choosing a handful of pages to test, and risking there being outlier pages that behave slowly, you’ve got full coverage.

Filter to What You Need

You’re going to have a lot of data with Raygun, and that’s a very good thing. But that doesn’t mean it has to be overwhelming or you can’t find exactly what you need. Say you’ve heard from a user that the site is behaving slowly for them in Firefox, you can filter for Firefox and look into that.

Take Action

What makes Raygun really useful is having all of the information you need to take action, with access to waterfall timelines, session information, and instance level diagnostics. This means you don’t just monitor what your CWV scores are, you can actively improve them.

Crash Reporting

We’ve mostly talked about performance reporting here, but note that Raygun is an error reporting tool as well. That is significant, as it means you don’t need to reach for a separate service for that vital need. You get your performance and crash reporting information in the same place.


Find issues. Fix issues. Watch your site improve. After your free trial, pricing starts at just $8/month for plans that have Real User Monitoring with CWV.


The post How to Monitor Core Web Vitals and Take Action with Raygun appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.



source https://css-tricks.com/how-to-monitor-core-web-vitals-and-take-action-with-raygun/

Thursday, April 29, 2021

List Markers and String Styles

Lists—we’ve all worked with them in one form or another. I’m talking about HTML’s <ol> and <ul>. Much of the time, because we desire styling control, we turn off the list’s markers completely with list-style-type: none, and start styling from there. Other times, we choose from a very limited set of unordered list markers, such as disc, circle, or square; or a (much) wider range of ordered list markers. We might even, from time to time, supply the URL of an image to be used.

But what if we want to style the markers differently than the contents of the list items? That’s always been difficult at best. Now, thanks to the ::marker pseudo-element, it’s a whole lot easier. You don’t get the full range of CSS to apply to the markers, but there’s still a great deal that can be done.

::marker is available in Firefox and, thanks to work by Igalia, Chrome as well.

Consider this list:

By default, that will yield an ordered list numbered from 1 to 5, using Arabic numerals (1, 2, 3, etc.), each followed by a dot (period), all of which will match the text contents in font face, size, style, color, and so on.

If you had a design direction that required making the numbers smaller or a different color, you’d have to manually create that effect by suppressing the markers and using the ::before pseudo-element and CSS counters and negative text indenting and… well, it would take a scientist to explain it all.

Enter ::marker. Add these styles to the above list, and you’ll get the result shown after.

That’s all you need!

Before you go tearing off to rewrite all your CSS, though, beware: the properties you can apply via ::marker are fairly limited at the moment. As of February 2021, the properties that markers should recognize are:

  • All font properties (font-face, font-size, etc.)
  • The white-space property
  • The color property
  • The internationalization properties text-combine-upright, unicode-bidi, and direction
  • The content property
  • All animation and transition properties

There are some additions in some browsers, but almost all of the additions relate to text styling, not the box model. So if you were thinking you could put all your list numbers into circles with shaded backgrounds, ::marker won’t get you there—you’ll have to return to the hackfest of ::before generated content. For now, anyway: the specification explicitly says more properties may be permitted for ::marker in the future.

There’s also a limitation around white-space, which has rendering bugs in varying browsers. Chrome, for example, treats all whitespace in markers as white-space: pre as the specification says, but won’t let you change it. This should be fixed when Chrome’s LayoutNG (Next Generation) ships, but not until then. Firefox, on the other hand, ignores any white-space values, and treats whitespace like normal-flow text by default.

With those limits in mind, you can still jazz up your markers with the content property. Instead of numbers followed by a period, you can put each number in brackets with a combination of counters and strings.

Note the space after the closing bracket in the content value. That’s included to provide a little bit of space between the marker and the list content. Ordinarily you might think to use a marking or padding, but as we saw earlier, those properties can’t be applied with ::marker. Which is frustrating! Also note the CSS counter list-item. That wasn’t defined anywhere else in the CSS—it’s a built-in counter that all browsers (that understand CSS counters) use to count list items, like those in ordered lists. You can use it in your CSS as well!

If all you want to do is change the text content of a list marker and don’t care about changing any of its styles, you can do that with ::marker, or you can do it with the new cross-browser support for string values on the list-style-type property.

li.warning {
  list-style-type:"⚠";
}

So that’s what’s new in the world of list markers. It might not be something you need to do often, but if you ever do, it’s good to know that the capabilities in this area have increased, and stand to be even better in the future. Let us know if you come up with some clever markers!


The post List Markers and String Styles appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.



source https://css-tricks.com/list-markers-and-string-styles/

Your Team is Not “Them”

This post was written for engineering managers, but anyone is welcome to read it.

Let’s talk for a moment about how we talk about our teams. This might not seem like something that needs a whole article dedicated to it, but it’s actually quite crucial. The way that we refer to our teams sends signals: to stakeholders, to your peers, to the team itself, and even to ourselves. In addressing how we speak about our teams, we’ll also talk about accountability.

I have noticed shared similarities in those folks I consider good managers whose teams deliver well, and those who don’t. It starts with how they communicate about their teams.

Your team is “we”

There can be a perception that as a manager of an organization you are in control at all times. Part of that control can invariably be perceived as how you appear to be in charge, are competent, or how you personally perform. Due to that, some bad behaviors can arise- not due to malice, but due to fear. For this reason, it can be tempting to take credit for success and avoid credit when there is failure.

The irony is that the more that you try to hold on to these external perceptions, the more it will slip away. Why? Because the problems you are solving as a manager really aren’t about you.

Your team is “we”. You are a driving force of that team, no matter how high up the hierarchy chain. What happens on that team is your responsibility. When you speak about your org, you should include yourself in the statement.

When your team succeeds in something though, then you can praise them and leave yourself out of it. Here’s an example:

They really pulled this project over the line, despite the incredibly tight project timeline. Everyone showed up and was driven throughout the engagement. They did a fantastic job.

However, if the team failed at something, the pronoun is then I:

I didn’t recognize how tight this turnaround was and failed to prioritize the team’s time well. I need to reconvene with everyone so we can come up with a better plan.

And never, ever them:

They didn’t adhere to this tight timeline. They just weren’t able to get this project over the line.

Do you see how the last example shirks responsibility for what occurred? Too often I will hear managers relieve themselves of their duties when shit hits the fan, and that is exactly when a manager needs to step up, and dive in to the problems that are their responsibility.

Photo by Marvin Meyer on Unsplash

The wider organization

There is another piece of this too, and it impacts how your team operates. It’s that your job is not to be the ambassador of who you manage and think of every other group as separate. You’re part of a larger system. A company is composed of groups, but those groups can only be successful if they’re working together, not if they are protecting their own org at all costs.

I admit I didn’t fully understand the depth of this until I read Patrick Lencioni’s great book The Advantage, thanks to Dalia Havens, a peer at Netlify. In the book, Lencioni talks about how organizational health, not “being smart”, as the biggest key to success. Plenty of smart people with good ideas build companies and see them fail. Success lies in being able to work together.

Fundamentally, other groups at the company are not separate from your group, rather that you’re all part of one whole. The Leadership Team is also a team, and should be treated as your team. How you speak about this team is equally important.

As such, when we talk about successes and failures of any groups, these should also be shared. There should be a sense that you’re all working towards a common goal together, and every group contributes to it. Within a leadership team there should be trust and vulnerability to own their part so that the whole organization can operate at its best.

And, yes, the leadership team as well

You may see where I’m going with this: when you talk about the leadership team, this is “we” too. You can’t speak to your team about decisions that were made at a table with your peers and boss and say “they decided something you don’t agree with” even if you don’t agree. You were there, ideally you took part in that decision, when you talk about that team, presenting them as “we” is important as well.

Why? Because as a manager, our job is to try as much as we can to drive balance and clarity. It’s confusing and disorienting to hear a manager talk about a leadership team they are on as though they aren’t a part of it and not take accountability for what’s happening there. Your reports themselves can’t effect change at that level, so if you don’t own your involvement in the leadership group, you can demoralize your staff and make them feel distrustful of other parts of the company. This can have an effect where folks demonize other teams and their initiatives, which as we discussed is ultimately unhealthy.

Saying “we” holds you accountable to your team for leadership decisions that you are a part of, which is how it should be. If people on your team have issues with the direction, it’s also your responsibility to own that conversation and next steps, as a liaison to the leadership team.

There are of course, some small instances when this might not be appropriate. Something that really goes against your core values that you fought strongly against can make this untenable. I would say those instances should ideally be very infrequent, or unfortunately you may need to pursue another place to work.

Speaking about the Leadership Team in Practice

Here’s how this works in practice, using an example of conveying a decision at the leadership level to the people who report to you:

The leadership team decided that we need to ship at least 3 features this quarter so I guess that’s what we have to figure out to do.

Versus:

One of the key OKRs this quarter is that we as a company need to double the signups to our platform. We’ve done some calculations that show we can almost certainly get there by shipping 3 features, so let’s all talk about what we can do within our group to make that possible. If you’re curious, we can chat through what initiatives other groups are doing to support this as well.

The first is not just passive, but demotivating. I have made the mistake of using this approach when I want to be liked by my employees and for them to think of me as a peer. But we’re not peers, I have a responsibility to them.

You’ll note in the second approach, we also explained the reasoning behind the decision. I’ve noticed personally that when I have to hold myself accountable to the decision, I care a bit more that people understand the reasoning behind it. This is a very good thing for the morale on your team! Which is arguably one of your most important jobs.

The last line in the second approach also opens up discussion- since you’re taking ownership of the decision, you’re also owning that you know about other pieces of the puzzle, and show a willingness to dive in with your team.

What if you make a mistake?

We all do! Management can be difficult and it’s impossible to be perfect all the time. Try not to beat yourself up, but perhaps show a bit more thoughtfulness next time. I’ve made lots of mistakes as well. It’s not a stick to beat up yourself or others, but a lesson learned to be as mindful as possible and promote a better working environment.


We communicate to our teams, peers, and stakeholders whether or not we’re taking responsibility as a true leader in these moments. We communicate whether we’ll approach a problem with humility, and a desire to collaborate and improve. This may seem to be a detail, but it’s a powerful piece of leading an organization.


The post Your Team is Not “Them” appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.



source https://css-tricks.com/your-team-is-not-them/

Wednesday, April 28, 2021

How to Create Actions for Selected Text With the Selection API

Click, drag, release: you’ve just selected some text on a webpage — probably to copy and paste it somewhere or to share it. Wouldn’t it be cool if selecting that text revealed some options that make those tasks easier? That’s what a selection menu does.

You may already be familiar with selection menus if you’ve ever used an online editor. When you select text, options to format the selection might float above it. In fact, I’m writing this draft in an editor that does exactly this.

Let’s see how we can create a selection menu like this using JavaScript’s Selection API. The API gives us access to the space and content of the selected area on a webpage. This way we can place the selection menu exactly above the selected text and get access to the selected text itself.

Here’s an HTML snippet with some sample text:

<article>
  <h1>Select over the text below</h1> 
  <p>Cascading Style Sheets (CSS) is a style sheet language used for describing the presentation of a document written in a markup language such as HTML. CSS is a cornerstone technology of the World Wide Web, alongside HTML and JavaScript. CSS is designed to enable the separation of presentation and content, including layout, colors, and fonts. This separation can improve content accessibility, provide more flexibility and control in the specification of presentation characteristics. </p>
</article>
<template><span id="control"></span></template>

There’s a <template> tag at the end there. The <span> inside it is our selection menu control. Anything inside a <template> tag is not rendered on the page until it’s later added to the page with JavaScript. We’ll add the selection menu control to the page when user selects text. And when the user selects that text, our selection menu will prompt the user to tweet it.

Here’s the CSS to style it:

#control {
    background-image: url("data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 width='40px' height='40px'><foreignObject width='40px' height='40px'><div xmlns='http://www.w3.org/1999/xhtml' style='width:40px;height:40px;line-height:40px;text-align:center;color:transparent;text-shadow: 0 0 yellow, 2px 4px black, -1px -1px black;font-size:35px;'>💬</div></foreignObject></svg>");
  cursor: pointer;
  position: absolute;
  width: 40px;
  height: 40px;
}
#control::before{
  background-color: black;
  color: white;
  content: " tweet this! ";
  display: block;
  font-weight: bold;
  margin-left: 37px;
  margin-top: 6px;
  padding: 2px;
  width: max-content;
  height: 20px;
}

Check out this article to learn how I used an emoji (💬) for the background image.

So far, the sample text is ready, and the selection menu control has been styled. Let’s move on to the JavaScript. When a selection is made, we’ll get the size and position of the selected area on the page. We then use those measurements to assign the position of the selection menu control at the top-middle of the selected area.

var control = document.importNode(document.querySelector('template').content, true).childNodes[0];
document.querySelector('p').onpointerup = () => {
  let selection = document.getSelection(), text = selection.toString();
  if (text !== "") {
    let rect = selection.getRangeAt(0).getBoundingClientRect();
    control.style.top = `calc(${rect.top}px - 48px)`;
    control.style.left = `calc(${rect.left}px + calc(${rect.width}px / 2) - 40px)`;
    control['text']= text; 
    document.body.appendChild(control);
  }
}

In this code, we first get a copy of the selection menu control inside <template>, then assign it to the control variable.

Next, we write the handler function for the onpointerup event of the element carrying the sample text. Inside the function, we get the selection and the selected string using document.getSelection(). If the selected string is not empty, then we get the selected area’s size and position, via getBoundingClientRect(), and place it in the rect variable.

Using rect, we calculate and assign the top and left positions of the control. This way, the selection menu control is placed a little above the selected area and centered horizontally. We’re also assigning the selected string to a user-defined property of control. This will later be used to share the text.

And, finally, we add control to the webpage using appendChild(). At this point, if we select some of the sample text on the page, the selection menu control will appear on the screen.

Now we get to code what happens when the selection menu control is clicked. In other words, we’re going to make it so that the text is tweeted when the prompt is clicked.

control.addEventListener('pointerdown', oncontroldown, true);

function oncontroldown(event) {
  window.open(`https://twitter.com/intent/tweet?text=${this.text}`)
  this.remove();
  document.getSelection().removeAllRanges();
  event.stopPropagation();
}

When the control is clicked, a tab opens with Twitter’s “New Tweet” page, complete with the selected text ready to go.

After the tweet prompt, the selection menu control is no longer needed and is removed, along with any selection made on the page. The way that the pointerdown event cascades further down the DOM tree is also stopped at this point.

We also need an event handler for the onpointerdown event of the page:

document.onpointerdown = ()=> {    
  let control = document.querySelector('#control');
  if (control !== null) {control.remove();document.getSelection().removeAllRanges();}
}

Now the control and any selection made on the page are removed when clicking anywhere on the page but the selection menu control.

Demo

Here’s a more prettified version that Chris put together:

And here’s an example showing more than one control in the selection menu:

About that <template>

It’s not totally necessary that we use it. Instead, you can also try simply hiding and showing the control some other way, like the hidden HTML attribute or the CSS display. You can also build a selection menu control in the JavaScript itself. The coding choices will depend on how efficiently you execute them, and their fallbacks, if needed, as well as how they fit in with your application.

Some UI/UX advice

While this is a nice effect, there are a couple of things to consider when using it to ensure a good user experience. For example, avoid injecting your own text into the text selection — you know, like appending a link back to your site in the auto-generated tweet. It’s intrusive and annoying. If there’s any reason to do that, like adding the source citation, let the user see a preview of the final text before posting it. Otherwise, the user might be confused or surprised by the addition.

One more thing: It’s best if the menu control is out of the way. We don’t want it covering up too much of the surrounding content. That sort of thing adds up to CSS “data loss” and we want to avoid that.

Bottom line: Understand why your users need to select text on your website and add the controls in a way that gets out of the way of what they’re trying to do.


The post How to Create Actions for Selected Text With the Selection API appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.



source https://css-tricks.com/how-to-create-actions-for-selected-text-with-the-selection-api/

Shared Element Transitions

I was just Hoping for Better Native Page Transitions, and Bramus commented that Chrome is working on something. Looks like it has some fresh enthusiasm for it, as there is a brand new repo, and you can literally test it in Chrome Canary.

The repo is moved over here, and I love the name. “Shared elements” is clutch here. It’s not just like a slide-out, slide-in, or a star wipe, it’s being able to move individual elements to new places. Shawn pointed out that Sarah’s article makes this ability super clear:

I’ll drop the code snippet from the current README here as its really cool:

<style>
#e1, #e2, #e3, #newE1, #newE2, #newE3 {
  contain: paint;
}
</style>
...
<script>
function handleTransition() {
  document.documentTransition.prepare({
    rootTransition: "reveal-left",
    duration: 300,
    sharedElements: [e1, e2, e3]
  }).then(() => {
    changeBodyBackground();
    document.documentTransition.start({ sharedElements: [newE1, newE2, newE3] }).then(
      () => console.log("transition finished"));
  });
}
...
</script>

Note you don’t have to deal with updating the URL or anything, that would just automatically happen (I guess?).

While I was chatting about this, Alex Riviere pointed out to me that pre-Chromium Edge had (proprietary) page transitions:

<meta http-equiv="Page-Enter"
      content="RevealTrans(Duration=0.600, Transition=6)">

Whaaaat. Christian Schaefer has a post lamenting what we lost when we lost Trident:

As the name implies, such a filter would smoothly transition the user from page to page upon navigation, instead of having pages appear as abruptly as we are used to. There was an extensive list of transition filters you could choose from by referencing them via number:

Wipe up, wipe down, random dissolve, split horizontal out, etc. No star wipes though, incredibly. And definitely no “shared element transitions”


The post Shared Element Transitions appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.



source https://css-tricks.com/shared-element-transitions/

Tuesday, April 27, 2021

A Complete Guide to Custom Properties

A custom property is most commonly thought of as a variable in CSS.

.card {
  --spacing: 1.2rem;
  padding: var(--spacing);
  margin-bottom: var(--spacing);
}

Above, --spacing is the custom property with 1.2rem as the value and var(--spacing) is the variable in use.

Perhaps the most valuable reason to use them: not repeating yourself (DRY code). In the example above, I can change the value 1.2rem in one place and have it affect two things. This brings something programming languages do to CSS.

There is a good bit to know about custom properties, so let’s get into it.

Why care about CSS Custom Properties?

  1. They help DRY up your CSS. That is “Don’t Repeat Yourself.” Custom properties can make code easier to maintain because you can update one value and have it reflected in multiple places. Careful though, overdoing abstraction can make have the opposite effect and make code less understandable.
  2. They are particularly helpful for things like creating color themes on a website.
  3. They unlock interesting possibilities in CSS.
  4. The fact that they can be updated in JavaScript opens up even more interesting doors.

Naming custom properties

Custom properties must be within a selector and start with two dashes (--):

/* Nope, not within a selector */
--foo: 1;

body {
  /* No, 0 or 1 dash won't work */
  foo: 1;
  -foo: 1; 

  /* Yep! */
  --foo: 1;

  /* OK, but they're different properties */
  --FOO: 1;
  --Foo: 1;
  
  /* Totally fine */
  --mainColor: red;
  --main-color: red;

  /* Special characters are a no */
  --color@home: red;
  --black&blue: black;
  --black^2: black;
}

Best to stick with letters, numbers, and dashes while making sure the custom property is defined inside of a valid selector.

Properties as properties

You can set the value of a custom property with another custom property:

html {
  --red: #a24e34;
  --green: #01f3e6;
  --yellow: #f0e765;

  --error: var(--red);
  --errorBorder: 1px dashed var(--red);
  --ok: var(--green);
  --warning: var(--yellow);
}

Some people like doing it this way because it allows the name of a custom property to be descriptive and then used in another property with a more functional name, again helping keep things DRY. It can even help make the functional names more readable and understandable.

Valid values for custom properties

Custom properties are surprisingly tolerant when it comes to the values they accept.

Here are some basic examples that you’d expect to work, and do.

body {
  --brand-color: #990000;
  --transparent-black: rgba(0, 0, 0, 0.5);
  
  --spacing: 0.66rem;
  --max-reading-length: 70ch;
  --brandAngle: 22deg;

  --visibility: hidden;
  --my-name: "Chris Coyier";
}

See that? They can be hex values, color functions, units of all kinds, and even strings of text.

But custom properties don’t have to be complete values like that. Let’s look at how useful it can be to break up valid CSS values into parts we can shove into custom properties.

Breaking up values

You can use custom properties to break up multi-part values.

Let’s imagine you’re using a color function, say rgba(). Each color channel value in there can be its own custom property. That opens up a ton of possibilities, like changing the alpha value for a specific use case, or perhaps creating color themes.

Splitting colors

Take HSL color, for example. We can split it up into parts, then very easily adjust the parts where we want. Maybe we’re working with the background color of a button. We can update specific parts of its HSL makeup when the button is hovered, in focus, or disabled, without declaring background on any of those states at all.

button {
  --h: 100;
  --s: 50%;
  --l: 50%;
  --a: 1;

  background: hsl(var(--h) var(--s) var(--l) / var(--a));
}
button:hover { /* Change the lightness on hover */
  --l: 75%;
}
button:focus { /* Change the saturation on focus */
  --s: 75%;
}
button[disabled] {  /* Make look disabled */
  --s: 0%;
  --a: 0.5;
}

By breaking apart values like that, we can control parts of them in a way we never could before. Just look at how we didn’t need to declare all of the HSL arguments to style the hover, focus and disabled state of a button. We simply overrode specific HSL values when we needed to. Pretty cool stuff!

Shadows

box-shadow doesn’t have a shorthand property for controlling the shadow’s spread on its own. But we could break out the box-shadow spread value and control it as a custom property (demo).

button {
  --spread: 5px;
  box-shadow: 0 0 20px var(--spread) black;
}
button:hover {
  --spread: 10px;
}

Gradients

There is no such thing as a background-gradient-angle (or the like) shorthand for gradients. With custom properties, we can change just change that part as if there was such a thing.

body {
  --angle: 180deg;
  background: linear-gradient(var(--angle), red, blue);
}
body.sideways {
  --angle: 90deg;
}

Comma-separated values (like backgrounds)

Any property that supports multiple comma-separated values might be a good candidate for splitting values too, since there is no such thing as targeting just one value of a comma-separated list and changing it alone.

/* Lots of backgrounds! */
background-image:
  url(./img/angles-top-left.svg),
  url(./img/angles-top-right.svg),
  url(./img/angles-bottom-right.svg),
  url(./img/angles-bottom-left.svg),
  url(./img/bonus-background.svg);

Say you wanted to remove just one of many multiple backgrounds at a media query. You could do that with custom properties like this, making it a trivial task to swap or override backgrounds.

body {
  --bg1: url(./img/angles-top-left.svg);
  --bg2: url(./img/angles-top-right.svg);
  --bg3: url(./img/angles-bottom-right.svg);
  --bg4: url(./img/angles-bottom-left.svg);
  --bg5: url(./img/bonus-background.svg);
  
  background-image: var(--bg1), var(--bg2), var(--bg3), var(--bg4);
}
@media (min-width: 1500px) {
  body {
    background-image: var(--bg1), var(--bg2), var(--bg3), var(--bg4), var(--bg5);
  }
}

Grids

We’re on a roll here, so we might as well do a few more examples. Like, hey, we can take the grid-template-columns property and abstract its values into custom properties to make a super flexible grid system:

.grid {
  display: grid;
  --edge: 10px;
  grid-template-columns: var(--edge) 1fr var(--edge);
}
@media (min-width: 1000px) {
  .grid {
     --edge: 15%;
   }
}

Transforms

CSS will soon get individual transforms but we can get it sooner with custom properties. The idea is to apply all the transforms an element might get up front, then control them individually as needed:

button {
  transform: var(--scale, scale(1)) var(--translate, translate(0));
}
button:active {
  --translate: translate(0, 2px);
}
button:hover {
  --scale: scale(0.9);
}

Concatenation of unit types

There are times when combining parts of values doesn’t work quite how you might hope. For example, you can’t make 24px by smashing 24 and px together. It can be done though, by multiplying the raw number by a number value with a unit.

body {
  --value: 24;
  --unit: px;
  
  /* Nope */
  font-size: var(--value) + var(--unit);
  
  /* Yep */
  font-size: calc(var(--value) * 1px);

  /* Yep */
  --pixel_converter: 1px;
  font-size: calc(var(--value) * var(--pixel_converter));
}

Using the cascade

The fact that custom properties use the cascade is one of the most useful things about them.

You’ve already seen it in action in many of the examples we’ve covered, but let’s put a point on it. Say we have a custom property set pretty “high up” (on the body), and then set again on a specific class. We use it on a specific component.

body {
  --background: white;
}
.sidebar {
  --background: gray;
}
.module {
  background: var(--background);
}

Then say we’ve got practical HTML like this:

<body> <!-- --background: white -->

  <main>
    <div class="module">
      I will have a white background.
    </div>
  <main>

  <aside class="sidebar"> <!-- --background: gray -->
    <div class="module">
      I will have a gray background.
    </div>
  </aside>

</body>
Three CSS rulesets, one for a body, sidebar and module. the background custom property is defined as white on body and gray on sidebar. The module calls the custom property and shows an orange arrow pointing to the custom property defined in the sidebar since it is the nearest ancestor.
For the second module, .sidebar is a closer ancestor than body, thus --background resolves to gray there, but white in other places.

The “module” in the sidebar has a gray background because custom properties (like many other CSS properties) inherit through the HTML structure. Each module takes the --background value from the nearest “ancestor” where it’s been defined in CSS.

So, we have one CSS declaration but it’s doing different things in different contexts, thanks to the cascade. That’s just cool.

This plays out in other ways:

button {
  --foo: Default;
}
button:hover {
  --foo: I win, when hovered;
  /* This is a more specific selector, so re-setting 
     custom properties here will override those in `button` */
}

Media queries don’t change specificity, but they often come later (or lower) in the CSS file than where the original selector sets a value, which also means a custom property will be overridden inside the media query:

body {
  --size: 16px;
  font-size: var(--size);
}
@media (max-width: 600px) {
  body {
    --size: 14px;
  } 
}

Media queries aren’t only for screen sizes. They can be used for things like accessibility preferences. For example, dark mode:

body {
  --bg-color: white; 
  --text-color: black;

  background-color: var(--bg-color);
  color: var(--text-color);
}

/* If the user's preferred color scheme is dark */
@media screen and (prefers-color-scheme: dark) {
  body {
    --bg-color: black;
    --text-color: white;
  }
}

The :root thing

You’ll often see custom properties being set “at the root.” Here’s what that means:

:root {
  --color: red;
}

/* ...is largely the same as writing: */
html {
  --color: red;
}

/* ...except :root has higher specificity, so remember that! */

There is no particularly compelling reason to define custom properties like that. It’s just a way of setting custom properties as high up as they can go. If you like that, that’s totally fine. I find it somehow more normal-feeling to apply them to the html or body selectors when setting properties I intend to make available globally, or everywhere.

There is also no reason you need to set variables at this broad of a scope. It can be just as useful, and perhaps more readable and understandable, to set them right at the level you are going to use them (or fairly close in the DOM tree).

.module {
  --module-spacing: 1rem;
  --module-border-width: 2px;

  border: var(--module-border-width) solid black;
}

.module + .module {
  margin-top: var(--module-spacing);
}

Note that setting a custom property on the module itself means that property will no longer inherit from an ancestor (unless we set the value to inherit). Like other inherited properties, there are sometimes reasons to specify them in place (at the global level), and other times we want to inherit them from context (at the component level). Both are useful. What’s cool about custom properties is that we can define them in one place, inherit them behind the scenes and apply them somewhere completely different. We take control of the cascade!

Combining with !important

You can make an !important modifier within or outside of a variable.

.override-red {
  /* this works */
  --color: red !important;  
  color: var(--color);

  /* this works, too */
  --border: red;
  border: 1px solid var(--border) !important;
}

Applying !important to the --color variable, makes it difficult to override the value of the --color variable, but we can still ignore it by changing the color property. In the second example, our --border variable remains low-specificity (easy to override), but it’s hard to change how that value will be applied to the border itself.

Custom property fallbacks

The var() function is what allows for fallback values in custom properties.

Here we’re setting a scale() transform function to a custom property, but there is a comma-separated second value of 1.2. That 1.2 value will be used if --scale is not set.

.bigger {
  transform: scale(var(--scale, 1.2));
}

After the first comma, any additional commas are part of the fallback value. That allows us to create fallbacks with comma-separated values inside them. For example, we can have one variable fall back to an entire stack of fonts:

html {
  font-family: var(--fonts, Helvetica, Arial, sans-serif);
}

We can also provide a series of variable fallbacks (as many as we want), but we have to nest them for that to work:

.bigger {
  transform: scale(var(--scale, var(--second-fallback, 1.2));
}

If --scale is undefined, we try the --second-fallback. If that is also undefined, we finally fall back to 1.2.

Using calc() and custom properties

Even more power of custom properties is unlocked when we combine them with math!

This kind of thing is common:

main {
  --spacing: 2rem;
}

.module {
  padding: var(--spacing);
}

.module.tight {
  /* divide the amount of spacing in half */
  padding: calc(var(--spacing) / 2)); 
}

We could also use that to calculate the hue of a complementary color:

html {
  --brand-hue: 320deg;
  --brand-color: hsl(var(--brand-hue), 50%, 50%);
  --complement: hsl(calc(var(--brand-hue) + 180deg), 50%, 50%);
}

calc() can even be used with multiple custom properties:

.slider {
  width: calc(var(--number-of-boxes) * var(--width-of-box));
}

Deferring the calc()

It might look weird to see calculous-like math without a calc():

body {
  /* Valid, but the math isn't actually performed just yet ... */
  --font-size: var(--base-font-size) * var(--modifier);

  /* ... so this isn't going to work */
  font-size: var(--font-size);
}

The trick is that as long as you eventually put it in a calc() function, it works fine:

body {
  --base-font-size: 16px;
  --modifier: 2;
  --font-size: var(--base-font-size) * var(--modifier);

  /* The calc() is "deferred" down to here, which works */
  font-size: calc(var(--font-size));
}

This might be useful if you’re doing quite a bit of math on your variables, and the calc() wrapper becomes distracting or noisy in the code.

@property

The @property “at-rule” in CSS allows you to declare the type of a custom property, as well its as initial value and whether it inherits or not.

It’s sort of like you’re creating an actual CSS property and have the ability to define what it’s called, it’s syntax, how it interacts with the cascade, and its initial value.

@property --x {
  syntax: '<number>';
  inherits: false;
  initial-value: 42;
}
Valid Types
  • length
  • number
  • percentage
  • length-percentage
  • color
  • image
  • url
  • integer
  • angle
  • time
  • resolution
  • transform-list
  • transform-function
  • custom-ident (a custom identifier string)

This means that the browser knows what kind of value it is dealing with, rather than assuming everything is a string. That means you can animate things in ways you couldn’t otherwise.

For example, say you have a star-shaped icon that you want to spin around with @keyframes and rotate with a transform. So you do this:

.star {
  --r: 0deg;
  transform: rotate(var(--r));
  animation: spin 1s linear infinite;
}

@keyframes spin {
  100% {
    --r: 360deg;
  }
}

That actually won’t work, as the browser doesn’t know that 0deg and 360deg are valid angle values. You have to define them as an <angle> type with @property for that to work.

@property --angle {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

.star {
  --r: 0deg;
  transform: rotate(var(--r));
  animation: spin 1s linear infinite;
}

@keyframes spin {
  100% {
    --r: 360deg;
  }
}
Demo

Commas in values

This can be a smidge confusing. Maybe not so much this:

html {
  --list: 1, 2, 3;
}

But below, you’ll need a sharp eye to realize the fallback value is actually 1.2, 2. The first comma separates the fallback, but all the rest is part of the value.

html {
  transform: scale(var(--scale, 1.2, 2));
}

Learn more about fallbacks above ⮑

Advanced usage

The Raven is a technique that emulates container queries using math and custom properties. Be prepared, this goes from 0-100 in complexity right out of the gate!

Demo

Resize this demo to see a grid of inline-block elements change number of columns from 4 to 3 to 1.

Here’s a few more favorite examples that show off advanced usage of custom properties:

The initial and whitespace trick

Think of @media queries and how when one thing changes (e.g. the width of the page) you can control multiple things. That’s kind of the idea with this trick. You change one custom property and control multiple things.

The trick is that the value of initial for a custom property will trigger a fallback, while an empty whitespace value will not. For the sake of explanation, it let’s define two globally-scoped custom properties, ON and OFF:

:root {
  --ON: initial;
  --OFF: ;
}

Say we have a “dark” variation class which sets a number of different properties. The default is --OFF, but can be flipped to --ON whenever:

.module {
  --dark: var(--OFF);
}

.dark { /* could be a media query or whatever */
  --dark: var(--ON);
}

Now you can use --dark to conditinally set values that apply only when you’ve flipped --dark to --ON. Demo:

Lea Verou has a great writeup that covers all of this.

Inline styles

It’s totally legit to set a custom property in HTML with an inline style.

<div style="--color: red;"></div>

That will, like any inline style, have a very high level of specificity.

This can be super useful for when the HTML might have access to some useful styling information that would be too weird/difficult to put into a static CSS file. A good example of that is maintaining the aspect ratio of an element:

<div style="--aspect-ratio: 16 / 9;"></div>

Now I can set up some CSS to make a box of that exact size wherever I need to. The full writeup on that is here, but here’s CSS that uses trickery like the ol’ padded box applied to a pseudo element which pushes the box to the desired size:

[style*="--aspect-ratio"] > :first-child {
  width: 100%;
}
[style*="--aspect-ratio"] > img {  
  height: auto;
} 
@supports (--custom: property) {
  [style*="--aspect-ratio"] {
    position: relative;
  }
  [style*="--aspect-ratio"]::before {
    content: "";
    display: block;
    padding-bottom: calc(100% / (var(--aspect-ratio)));
  }  
  [style*="--aspect-ratio"] > :first-child {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
  }  
}

But hey, these days, we have a native aspect-ratio property in CSS, so setting that in the inline style might make more sense going forward.

<div style="aspect-ratio: 16 / 9;"></div>

Hovers and pseudos

There is no way to apply a :hover style (or other pseudo classes/elements) with inline styles. That is, unless we get tricky with custom properties. Say we want custom hover colors on some boxes — we can pass that information in as a custom property:

<div style="--hover-color: red;"><div>
<div style="--hover-color: blue;"><div>
<div style="--hover-color: yellow;"><div>

Then use it in CSS which, of course, can style a link’s hover state:

div:hover {
  background-color: var(--hover-color);
}

/* And use in other pseudos! */
div:hover::after {
  content: "I am " attr(style);
  border-color: var(--hover-color);
}

Custom properties and JavaScript

JavaScript can set the value of a custom property.

element.style.setProperty('--x', value);

Here’s an example of a red square that is positioned with custom properties, and JavaScript updates those custom property values with the mouse position:

Typically you think of JavaScript passing values to CSS to use, which is probably 99% of usage here, but note that you can pass things from CSS to JavaScript as well. As we’ve seen, the value of a custom property can be fairly permissive. That means you could pass it a logical statement. For example:

html {
  --logic: if (x > 5) document.body.style.background = "blue";
}

Then grab that value and execute it in JavaScript:

const x = 10;

const logic = getComputedStyle(document.documentElement).getPropertyValue(
  "--logic"
);

eval(logic);

Custom properties are different than preprocessor variables

Say you’re already using Sass, Less, or Stylus. All those CSS preprocessors offer variables and it’s one of the main reasons to have them as part of your build process.

// Variable usage in Sass (SCSS)
$brandColor: red;

.marketing {
  color: $brandColor;
}

So, do you even need to bother with native CSS custom properties then? Yes, you should. Here’s why in a nutshell:

  • Native CSS custom properties are more powerful then preprocessor variables. Their integration with the cascade in the DOM is something that preprocessor variables will never be able to do.
  • Native CSS custom properties are dynamic. When they change (perhaps via JavaScript, or with a media query), the browser repaints what it needs to. Preprocessor variables resolve to a value when they’re compiled and stay at that value.
  • Going with a native feature is good for the longevity of your code. You don’t need to preprocess native CSS.

I cover this in much more detail in the article “What is the difference between CSS variables and preprocessor variables?”

To be totally fair, there are little things that preprocessor variables can do that are hard or impossible with custom properties. Say you wanted to strip the units off a value for example. You can do that in Sass but you’ll have a much harder time with custom properties in CSS alone.

Can you preprocess custom properties?

Kinda. You can do this, with Sass just to pick one popular preprocessor:

$brandColor: red;
body {
  --brandColor: $brandColor;
}

All that’s doing is moving a Sass variable to a custom property. That could be useful sometimes, but not terribly. Sass will just make --brandColor: red; there, not process the custom property away.

If a browser doesn’t support custom properties, that’s that. You can’t force a browser to do what custom properties do by CSS syntax transformations alone. There might be some kind of JavaScript polyfill that parses your CSS and replicates it, but I really don’t suggest that.

The PostCSS Custom Properties plugin, though, does do CSS syntax transforms to help. What it does is figure out the value to the best of it’s ability, and outputs that along with the custom property. So like:

:root {
  --brandColor: red;
}
body {
  color: var(--brandColor);
}

Will output like this:

:root {
  --brandColor: red;
}
body {
  color: red;
  color: var(--brandColor);
}

That means you get a value that hopefully doesn’t seem broken in browsers that lack custom property support, but does not support any of the fancy things you can do with custom properties and will not even attempt to try. I’m a bit dubious about how useful that is, but I think this is about the best you can do and I like the spirit of attempting to not break things in older browsers or newer browsers.

Availiability

Another thing that is worth noting about the difference between is that with a CSS preprocessor, the variables are available only as you’re processing. Something like $brandColor is meaningless in your HTML or JavaScript. But when you have custom properties in use, you can set inline styles that use those custom properties and they will work. Or you can use JavaScript to figure out their current values (in context), if needed.

Aside from some somewhat esoteric features of preprocessor variables (e.g. some math possibilities), custom properties are more capable and useful.

Custom properties and Web Components (Shadow DOM)

One of the most common and practical ways to style of Web Components (e.g. a <custom-component> with shadow DOM) is by using custom properties as styling hooks.

The main point of the shadow DOM is that it doesn’t “leak” styles in or out of it, offering style isolation in a way that nothing else offers, short of an <iframe>. Styles do still cascade their way inside, I just can’t select my way inside. This means custom properties will slide right in there.

Here’s an example:

Another common occurrence of the shadow DOM is with SVG and the <use> element.

Video: “CSS Custom Properties Penetrate the Shadow DOM”

Browser support

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Firefox IE Edge Safari
49 31 No 16 10

Mobile / Tablet

Android Chrome Android Firefox Android iOS Safari
90 87 90 10.0-10.2

You can preprocess for deeper browser support, with heavy limitations.

@supports

If you would like to write conditional CSS for when a browser supports custom properties or not:

@supports (--custom: property) {
  /* Isolated CSS for browsers that DOES support custom properties, assuming it DOES support @supports */
}

@supports not (--custom: property) {
  /* Isolated CSS for browsers that DON'T support custom properties, assuming it DOES support @supports */
}

Credit

Thanks to Miriam Suzanne for co-authoring this with me!


The post A Complete Guide to Custom Properties appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.



source https://css-tricks.com/a-complete-guide-to-custom-properties/