Month: April 2021

Safari 14.1 Adds Support for Flexbox Gaps

Yay, it’s here! Safari 14.1 reportedly adds support for the gap property in flexbox layouts. We’ve had grid-gap support for some time, but true to its name, it’s limited to grid layouts. Now we can use gap in either type of layout:

.container {   display: flex;   flex-flow: row wrap;   gap: 1.5rem; }

Apple’s been rather quiet about the update. I didn’t even hear about it until Eric Meyer brought it up in an email, and he only heard about it when Jen Simmons tweeted it out.

I checked the WebKit CSS Feature Status and, sure enough, it’s supported. It just wasn’t called out in the release notes. Nothing is posted to Safari’s release notes archive just yet, so maybe we’ll hear about it officially there at some point. For now, it seems that Maximiliano Firtman‘s overview of iOS 14.5 is the deepest look at what’s new.

And how timely is it that Eric recently covered how to use the gap property, not only in grid and flexbox layouts, but multi-column as well.

The post Safari 14.1 Adds Support for Flexbox Gaps appeared first on CSS-Tricks.

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


, , , , ,

Web Languages as Compile Targets

Jim Nielsen quoting Eric Bailey:

He references an example on Twitter where someone noted you can use the <details> element to “create a native HTML accordion,” to which someone responded: “this works without Bootstrap? 🤯”

What’s the problem here? From Eric:

the problem that arises from this situation is that it teaches people to think and work framework-first. When that happens, the hard-won, baked-in interoperability and, importantly, accessibility of the [web] platform is thrown away. It’s a compounding problem, as well: The more people don’t use the elements made available to us, the more the notion exists that they’re irrelevant.

I’ve seen people on GitHub routinely amazed by <details>, and assume it’s a special GitHub feature.

HTML (lol)

Seriously though, I’m not exactly sure when the perfect time to learn HTML is. Early on, for sure, but I wouldn’t blame anyone for not learning it first. I’m sure I learned it in the context of WordPress PHP templates. I’m sure a lot of people are learning it in the form of JSX or .vue files these days. That’s fine. It’s like learning to play “(Sittin’ On) The Dock of Bay” on guitar before you learn about keys and scales and voicings. But if you never circle back to the fundamentals, it’s limiting, and in the case of the web, damaging.

Direct Link to ArticlePermalink

The post Web Languages as Compile Targets appeared first on CSS-Tricks.

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


, ,

Jenny B Kowalski’s A-Z (and a-z) as Variable Letterforms

Jenny B Kowalski has been posting a-letter-a-day on Instagram exploring multi-axis variable/responsive letterforms. They are very clever in that one of the axes controls an uppercase-to-lowercase conversion, literally morphing the shape of the letters from an uppercase version to a lowercase version. The other axis is a stroke weight, which also dramatically changes the feel of the letters.

Here’s Q, one of my favorites:

She’s using p5.js, but I don’t see any reason these couldn’t be made into a variable font with custom axes.

OK here’s one more. I find the I/i very clever:

The post Jenny B Kowalski’s A-Z (and a-z) as Variable Letterforms appeared first on CSS-Tricks.

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


, , ,

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!


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.


, , ,

A great list of data visualization tools you can use on a website


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.


, , , , ,

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 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 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.


, ,

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.


, , ,

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.


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.



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=%22 width='40px' height='40px'><foreignObject width='40px' height='40px'><div xmlns='' 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(); = `calc($ {}px - 48px)`; = `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) {`$ {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.


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.


, , , ,