Thursday, 3 March 2022

Build Membership Businesses with Memberful

(This is a sponsored post.)

What would your business be like if you sold memberships? It might be more than fun to think about, in fact, it might just be transformative. With membership, would you include little add-ons or perks for your biggest fans? Or could it become the entire core of what you do? Likewise, what might you help your clients build?

Whatever you decide to do (or perhaps what a client has hired you to do), you can get it done with Memberful. It’s the best software for building membership businesses, used by the biggest creators on the web.

Memberful is for developers.

You have a goal in mind: to build a great member-powered web experience. But you’re a developer and want to build how you want to build. Good. Memberful is here to support that.

Memberful maintains a full-featured GraphQL API (complete with GraphiQL explorer), webhooks, and OAth Single Sign-on to make it easy to integrate seamlessly with any stack.

Your main site in Rails? No problem. You wanna do a Jamstack thing? Perfect. Do you have a React-powered app through and through? That’ll work.

You don’t have to build entirely from scratch.

For example, Memberful maintains a best-in-class WordPress plugin that easily gates content and adds membership to your website. So if you happen to use WordPress as your CMS, or are thinking of using it to build your website, you’ve got a first-class integration to work with.

If you’re looking to add membership to your existing business, you’ll want a solution that works with your existing technology, so you can launch a new revenue stream without rebuilding your entire tech stack.

You don’t even need to build a website to use Memberful.

If you’re not looking to gate access to content on a custom website, you won’t need a website at all to use Memberful. You can use Memberful’s hosted landing page feature to sell your memberships, and then deliver your member benefits like a private podcast, protected downloads, and newsletters right through the platform — no website required.

Say you already use other tools for your business. Maybe you do all your emailing with MailChimp. Great! Memberful fully integrates with MailChimp. Is Discord your community hub? No problem, Memberful seamlessly integrates with Discord, meaning you can offer paid subscribers perks that are unique to Discord, things like special channels or access roles.

What are some common use cases for Memberful?

  • Private Podcasts
  • Subscription Newsletters
  • Selling digital goods (or physical!)
  • Building communities
  • Membership-driven educational courses

Memberful handles the hard stuff so you can focus on what you do best, while earning revenue quickly. Even advanced features like gift subscriptions, coupons, referrals, free and paid trials and more, are right there for you to take advantage of.

Who handles all the emails?

Memberful does, and you get 100% control of the brand. Yet another thing you won’t need to worry about, which can otherwise be an awful lot of work and technical debt. I’m talking transactional emails here, like signup confirmation emails, welcome emails, forgot password emails, etc.

How does the money work?

Memberful has a free plan to get your feet wet. You can build 2 subscription plans and do website integrations. The PRO plan starts at $25/month and lowers transaction fees to 4.9% down from 10% on the free plan.

Where do those subscription payments go? You connect your Stripe account, so that money goes there, which is the best-in-business payment provider. That means you can offer additional features like Apple Pay and Google Pay which are great for lowering checkout friction for potential customers.

Have questions? Memberful has always-real-human based support at all times to help you.


Build Membership Businesses with Memberful originally published on CSS-Tricks. You should get the newsletter.



from CSS-Tricks https://ift.tt/C7gDoaZ
via IFTTT

Wednesday, 2 March 2022

7 Fresh Links on Performance For March 2022

I have a handful of good links to articles about performance that are burning a hole in my bookmarks folder, and wanna drop them here to share.

Screenshot of the new WebPageTest homepage, a tool for testing performance metrics.
The new WebPageTest website design

7 Fresh Links on Performance For March 2022 originally published on CSS-Tricks. You should get the newsletter.



from CSS-Tricks https://ift.tt/oethqrm
via IFTTT

How to Make a “Raise the Curtains” Effect in CSS

“Raise the curtains” is what I call an effect where the background goes from dark to light on scroll, and the content on top also goes from light to dark while in a sticky position.

Here’s an example where I used the effect on a real-life project:

Want to know how it’s done? I will take you behind the curtain and show you how to raise it, with nothing but HTML and CSS.

Let’s start with the HTML

What we’re making is sort of a simplified “raise the curtain” effect like this:

Showing the raise the curtains effect from dark blue to wheat.
The background and text both change color while scrolling over the element.

I’m keeping things simple for the sake of clarity, but we can stub this out with three elements:

<div class="curtain">
  <div class="invert">
    <h2>Section title</h2>
  </div>
</div>

First, we need a container for the curtain, which we’ll give a .curtain class. Then, inside the .curtain, we have the an .invert child element that will serve as our “sticky” box. And, finally, we have the content inside this box — a good old-fashioned <h2> element for this specific example.

Let’s set up some CSS variables

There are three values we know we’ll need upfront. Let’s make CSS variables out of them so it’s easy to write them into our styles and easily change them later if we need to.

  • --minh – The height of the container
  • --color1 – The light color
  • --color2 – The dark color
:root {
  --minh: 98vh;
  --color1: wheat;
  --color2: midnightblue;
}

Time to draw the curtain

Next, we can define our .curtain element using the following techniques:

  • A linear-gradient for the “split” background
  • min-height for the extra space at the bottom of the container

We use the ::after pseudo-element to add the extra space to the bottom. This way, our “sticky” content will actually stick to the container while scrolling past the ::after element. It’s an illusion.

.curtain {
  /** create the "split" background **/
  background-image: linear-gradient(to bottom, var(--color2) 50%, var(--color1) 50%);
}

/** add extra space to the bottom (need this for the "sticky" effect) **/
.curtain::after {
  content: "";
  display: block;
  min-height: var(--minh);
}

Making sticky content

Next up, we need to make our content “sticky” in the sense that it sits perfectly inside the container as the background and text swap color values. In fact, we already gave the .curtain‘s child element an .invert class that we can use as the sticky container.

Stay with me for a moment — here’s how this is going to play out:

  • position: sticky and top define the stickiness and where it sticks.
  • mix-blend-mode: difference blends the color of the content inside the <h2> element into the .curtain‘s background gradient.
  • display: flex centers the content for presentation.
  • min-height defines the height of the container and allows for the extra space at the bottom.
  • color sets the color of the h2 heading.

Now to put that into CSS code!

.invert {
  /** make the content sticky **/
  position: sticky;
  top: 20px;

  /** blend the content with the contrast effect **/
  mix-blend-mode: difference;

  /** center the content **/
  display: flex;
  align-items: center;
  justify-content: center;
  
  /** set the minimum height of the section **/
  min-height: var(--minh);
}

h2 {
  /** set the color of the text **/
  color: var(--color1);
}

There are many things going on here, so let’s explain each one of them.

First, we have a sticky position that is self-explanatory and flexbox to help center the content. Nothing new or particularly tricky about this.

The content’s height is set using CSS variable and the value is the same height value as the .curtain::after pseudo-element.

The mix-blend-mode: difference declaration blends our content with the background. The difference value is complicated, but you might visualize it like inverted text color against the background. Here’s a nice demo from the CSS-Tricks Almanac showing off the different mix-blend-mode values:

To make the blending work, we need to set the color of our heading. In this case, we’re assigning a light color value (wheat) to the --color1 variable.

“Raise the Curtains” Demo

Gotchas

I experienced a few problems while working out the details of the “raise the curtain” effect. If you want to add images to the “sticky” content, for example, avoid using images that don’t look good when their colors are inverted. Here’s a quick demo where I made a simple SVG and transparent PNG image, and it looks good.

Another gotcha: there’s no way to set mix-blend-mode: difference on specific child elements, like headings, while avoiding the effect on images. I discovered there are several reasons why it doesn’t work, the first of which is that position: sticky cancels the blending.

The same goes when using something like transform: skewY on the container to add a little “tilt” to things. I suspect other properties don’t play well with the blending, but I didn’t go that far to find out which ones.

Here’s the demo without scrolling that removes the troubling properties:

Curtain call!

I enjoyed building this component, and I always love it when I can accomplish something using only HTML and CSS, especially when they work smoothly on every browser.

What will make with it? Is there a different way you would approach a “raise the curtain” effect like this? Let me know in the comments!


How to Make a “Raise the Curtains” Effect in CSS originally published on CSS-Tricks. You should get the newsletter.



from CSS-Tricks https://ift.tt/Mn7KmbI
via IFTTT

Tuesday, 1 March 2022

CSS Custom Highlight API: The Future of Highlighting Text Ranges on the Web

Styling ranges of text in software is a very useful thing to be able to do. Thankfully, we have the CSS Custom Highlight API to look forward to because it represents the future of styling text ranges on the web.

Animation screenshot of the CSS Custom Highlight API demo.

One example: if you’ve ever used text editing software like Google Docs, Word, or Dropbox Paper, you’ll see they detect spelling and grammar errors and displaying nice little squiggly underlines below them to attract attention. Code editors like VS Code do the same for code errors.

Another very common use case for highlighting text is search and highlight, where you’re given a text input box and typing in it searches matching results on the page, and highlights them. Try pressing Ctrl/⌘+ F in your web browser right now and type in some text from this article.

The browser itself often handles these styling situations. Editable areas (like a <textarea>) get spelling squiggles automatically. The find command highlights found text automatically.

But what about when we want to do this type of styling ourselves? Doing this on the web has been a common problem for a long time. It has probably costed many people a lot more time than it should have.

This isn’t a simple problem to solve. We aren’t just wrapping text in a <span> with a class and applying some CSS. Indeed, this requires being able to correctly highlight multiple ranges of text across an arbitrarily complex DOM tree, and possibly crossing the boundaries of DOM elements.

There are two common solutions to this, including:

  1. styling text range pseudo-elements, and
  2. creating your own text highlighting system.

We’ll review them first and then take a look at the upcoming CSS Custom Highlight API that can change it all. but if you’re

Potential Solution #1: Style-able Text Ranges

Probably the most well-known style-able text range is the user selection. When you use your pointing device to select a piece of text in a web page, a Selection object is automatically created. In fact, try selecting text on this page right now, and then run document.getSelection() in the DevTools console. You should see location information about the selected text.

DevTools window showing the position of the current selection in the console.

It turns out that you can also create a text selection programmatically from JavaScript. Here is an example:

// First, create a Range object.
const range = new Range();

// And set its start and end positions.
range.setStart(parentNode, startOffset);
range.setEnd(parentNode, endOffset);

// Then, set the current selection to this range.
document.getSelection().removeAllRanges();
document.getSelection().addRange(range);

The last piece of the puzzle is to style this range. CSS has a pseudo-element called ::selection to do just that, and it’s supported across all browsers.

::selection {
  background-color: #f06;
  color: white;
}

Here is an example using this technique to highlight all words in a page one after the other:

On top of the ::selection pseudo-element, there are a number of other pseudo-elements:

  • ::target-text selects the text that has been scrolled to in browsers that support the scroll-to-text feature. (MDN)
  • ::spelling-error selects text that is flagged by the browser as containing a spelling error. (MDN)
  • ::grammar-error selects text that is flagged by the browser as containing a grammar error. (MDN)

Unfortunately browser support isn’t great here and although these ranges are useful in each of their own right, they can’t be used to style custom pieces of text — only browser-predefined ones

So the user text selection is nice because it’s relatively simple to put in place and doesn’t change the DOM of the page. Indeed, Range objects are essentially coordinates of segments in the page, rather than HTML elements that need to be created to exist.

One major drawback, however, is that creating a selection resets whatever the user has already manually selected. Try selecting text in the demo above to test this. You’ll see how it goes away as soon as the code moves the selection somewhere else.

Potential Solution #2: Custom Highlighting System

This second solution is pretty much the only thing you can do if using the Selection object is insufficient for you. This solution revolves around doing everything yourself, using JavaScript to insert new HTML elements in the DOM where you want the highlighting to appear.

Unfortunately, this means way more JavaScript to write and maintain, not to mention it forces the browser to re-create the layout of the page whenever the highlighting changes. Plus, there are complicated edge cases, for example, when you want to highlight a piece of text that spans across multiple DOM elements.

Illustration showing a line of HTML with an emphasis element and a strong element with a bright yellow highlight running through them.

Interestingly, CodeMirror and Monaco (the JavaScript text editor library that powers VS Code) have their own highlighting logic. They use a slightly different approach where the highlights are contained in a separate part of the DOM tree. The lines of text and the highlighted segments are rendered in two different places in the DOM which are then positioned over each other. If you inspect the DOM sub-tree that contains the text, there are no highlights. This way, the highlights can be re-rendered without impacting the lines of text and having to introduce new elements within them.

Overall, it feels like a browser-powered highlighting feature is missing. Something that would help solve all of these drawbacks (no interference with user text selection, multi-selection support, simple code) and be faster than custom-made solutions.

Fortunately, that’s what we’re here to talk about!

Enter the CSS Custom Highlight API

The CSS Custom Highlight API is a new W3C specification (currently in Working Draft status) that makes it possible to style arbitrary text ranges from JavaScript! The approach here is very similar to the user text selection technique we reviewed earlier. It gives developers a way to create arbitrary ranges, from JavaScript, and then style them using CSS.

Creating Ranges of Text

The first step is to create the ranges of text that you want to highlight. which can be done using a Range in JavaScript. So, like we did when setting the current selection:

const range = new Range();
range.setStart(parentNode, startOffset);
range.setEnd(parentNode, endOffset);

It’s worth noting that the setStart and setEnd methods work differently if the node passed as the first argument is a text node or not. For text nodes, the offset corresponds to the number of characters within the node. For other nodes, the offset corresponds to the number of child nodes within the parent node.

Also worth noting is that setStart and setEnd aren’t the only ways to describe where a range starts and ends. Take a look at the other methods available on the Range class to see other options.

Creating Highlights

The second step consists in creating Highlight objects for the ranges created in that last step. A Highlight object can receive one or more Ranges. So if you want to highlight a bunch of pieces of text in exactly the same way, you should probably create a single Highlight object and initialize it with all of the Ranges that correspond to these pieces of text.

const highlight = new Highlight(range1, range2, ..., rangeN);

But you can also create as many Highlight objects as you need. For example, if you are building a collaborative text editor where each user gets a different text color, then you can create one Highlight object per user. Each object can then be styled differently, as we’ll see next.

Registering Highlights

Now Highlight objects on their own don’t do anything. They first need to be registered in what is called the highlight registry. This is done by using the CSS Highlights API. The registry works like a map where you can register new highlights by giving them names, as well as remove highlights (or even clear the entire registry).

Here is how to register a single highlight.

CSS.highlights.set('my-custom-highlight', highlight);

Where my-custom-highlight is the name of your choosing and highlight is a Highlight object created in the last step.

Styling Highlights

The final step is to actually style the registered highlights. This is done with the new CSS ::highlight() pseudo-element, using the name you chose when registering the Highlight object (which is my-custom-highlight in our example above).

::highlight(my-custom-highlight) {
  background-color: yellow;
  color: black;
}

It’s worth noting that, just like ::selection, a subset of CSS properties only can be used with the ::highlight() pseudo-element:

Updating Highlights

There are multiple ways to update highlighted text on the page.

For example, you can clear the highlight registry altogether with CSS.highlights.clear() and then start again from the beginning. Or, you can also update the underlying ranges without having to re-create any of the objects all. For this, use the range.setStart and range.setEnd methods again (or any of the other Range methods) and the highlights will be re-painted by the browser.

But, the Highlight object works like a JavaScript Set, so this means you also add new Range objects to an existing Highlight with highlight.add(newRange) or remove a Range with highlight.delete(existingRange).

Third, you can also add or remove specific Highlight objects from the CSS.highlights registry. Since this API works like a JavaScript Map, you can set and delete to update the currently registered Highlights.

Browser Support

The specification for the CSS Custom Highlight API is relatively new and its implementation in browsers is still incomplete. So, although this is going to be a very useful addition to the web platform, it’s not quite ready for production use.

The Microsoft Edge team is implementing the CSS Custom Highlight API in Chromium at the moment. In fact, the feature can already be used in Canary versions right now by enabling the Experimental Web Platform features flag (under about:flags). There is currently no firm plan as to when the feature will ship in Chrome, Edge, and other Chromium-based browsers, but it’s getting very close.

The API is also supported in Safari 99+ but behind an experiment flag (Develop → Experimental Features → Highlight API), and the interface is a little bit different in that it uses StaticRange objects instead.

Firefox does not support the API yet, though you can read Mozilla’s position about it for more information.

Demo

Speaking of Microsoft Edge, they have a demo set up where you can take the CSS Custom Highlight API for a test drive. But Before trying the demo, be sure you’re using either Chrome or Edge Canary with the Experimental Web Platform features flag in the about:flags page.

/button View the demo

The demo uses the Custom Highlight API to highlight ranges of text in the page based on what you type in the search field at the top of the page.

After the page loads, JavaScript code retrieves all the text nodes in the page (using a TreeWalker) and when the user types in the search field, the code iterates over these nodes until it finds matches. Those matches are then used to create Range objects, which are then highlighted with the Custom Highlight API.

Closing Thoughts

So, is this new browser-provided highlighting API really worth it? Totally!

For one, even if the CSS Custom Highlight API may seem a bit complicated at first (i.e. having to create ranges, then highlights, then registering them, and finally styling them), it’s still way simpler than having to create new DOM elements and insert them in the right places.

More importantly, browser engines can style these ranges very, very fast.

The reason only a subset of CSS properties is allowed to be used with the ::highlight() pseudo-element is that the subset only contains properties that can be applied by the browser very effectively without having to recreate the layout of the page. Highlighting ranges of text by inserting new DOM elements in the page around them requires the engine to do much more work.

But don’t take my word for it. Fernando Fiori, who worked on the API, created this nice performance comparison demo. On my computer, the CSS Custom Highlight API performs on average 5✕ as fast as the DOM-based highlighting.

With Chromium and Safari experimental support already here, we’re getting close to something that can be used in production. I can’t wait for browsers to support the Custom Highlight API consistently and see what features this will unlock!


CSS Custom Highlight API: The Future of Highlighting Text Ranges on the Web originally published on CSS-Tricks. You should get the newsletter.



from CSS-Tricks https://ift.tt/VbcgUpO
via IFTTT

Ahmad Shadeed: Use Cases For CSS fit-content

Ahmad Shadeed covers the CSS fit-content sizing keyword. It’s useful! It just doesn’t come up super often. I find myself using min-content a lot more, like when setting up the height of a grid-template-row.

The fit-content keyword is actually closely related to min-content and max-content — it just has a little heuristic it follows that Ahmad nicely illustrates as a flow chart.

Ahmad Shadeed's flow chat illustrating the way browsers handle the CSS fit-content keyword.
“Use Cases For CSS fit-content” by Ahmad Shadeed

My favorite use case is covered here: sizing a <figure> with fit-content, so that it neatly wraps around the <img>. That way, even if the image doesn’t fill the parent space, and it can remain block-level.

We also covered PPK’s deep dive on fit-content last year. One of the key takeaways for understanding it is knowing that is it essentially a shorthand way of writing:

.box {
  width: fit-content;

  /* ... is the same as ... */
  width: auto;
  min-width: min-content;
  max-width: max-content;
}

To Shared Link — Permalink on CSS-Tricks


Ahmad Shadeed: Use Cases For CSS fit-content originally published on CSS-Tricks. You should get the newsletter.



from CSS-Tricks https://ift.tt/eAMg1j6
via IFTTT

Monday, 28 February 2022

IE Down, Edge Up… Global Browser Usage Stats Are for Cocktail Parties and Conference Slides

I enjoy articles like Hartley Charlton’s “Microsoft Edge Looks Set to Overtake Safari as World’s Second Most Popular Desktop Browser.” It’s juicy! We know these massive players in the browser market care very much about their market share, so when one passes another it’s news. Like an Olympic speed skater favored for the gold getting a bronze instead, or the like.

Microsoft Edge is now used on 9.54 percent of desktops worldwide, a mere 0.3 percent behind Apple’s Safari, which stands at 9.84 percent. Google Chrome continues to hold first place with an overwhelming 65.38 percent of the market. Mozilla Firefox takes fourth place with 9.18 percent.

In January 2021, Safari held a 10.38 percent market share and appears to be gradually losing users to rival browsers over time. If the trend continues, Apple is likely to slip to third or fourth place in the near future.

Scoping the data down even by continent is entirely different. Like in Europe, Edge has already passed Safari, but in North America, the gap is still 5%.

Source: MacRumors.com

What does it matter to you or me? Nothing, I hope. These global stats should mean very little to us, outside a little casual nerdy cocktail party chatter. Please don’t make decisions about what to support and not support based on global statistics. Put some kind of basic analytics in place on your site, get data from actual visits, and make choices on that data. That’s the only data that matters.

Alan Dávalos’ “The baseline for web development in 2022” paints a picture of what we should be supporting based again on global browser usage statistics.

Globally, IE’s current market share is under 0.5%. And even in Japan, which has a higher market share of IE compared to other countries, IE’s market share is close to 2% and has a downward tendency.

Until now we kept supporting IE due to its market share. But now, there are basically no good reasons to keep supporting IE.

Again it seems so bizarre to me that any of us would make a choice on what to support based on a global usage statistic. Even when huge players make choices, they do it based on their own data. When Google “dropped” IE 11 (they still serve a perfectly fine baseline experience), they “did the math.” WordPress, famously powering somewhere in the “a third of the whole internet” range, factored in usage of their own product.

Even if you’re building a brand new product and trying to make these choices, you’ll have analytic data soon enough, and can make future-facing support choices based on that as it rolls in.


IE Down, Edge Up… Global Browser Usage Stats Are for Cocktail Parties and Conference Slides originally published on CSS-Tricks. You should get the newsletter.



from CSS-Tricks https://ift.tt/ZM17Sbv
via IFTTT

Web Component Pseudo-Classes and Pseudo-Elements are Easier Than You Think

We’ve discussed a lot about the internals of using CSS in this ongoing series on web components, but there are a few special pseudo-elements and pseudo-classes that, like good friends, willingly smell your possibly halitotic breath before you go talk to that potential love interest. You know, they help you out when you need it most. And, like a good friend will hand you a breath mint, these pseudo-elements and pseudo-classes provide you with some solutions both from within the web component and from outside the web component — the website where the web component lives.

I’m specifically referring to the ::part and ::slotted pseudo-elements, and the :defined, :host, and :host-context pseudo-classes. They give us extra ways to interact with web components. Let’s examine them closer.

Article series

The ::part pseudo-element

::part, in short, allows you to pierce the shadow tree, which is just my Lord-of-the-Rings-y way to say it lets you style elements inside the shadow DOM from outside the shadow DOM. In theory, you should encapsulate all of your styles for the shadow DOM within the shadow DOM, i.e. within a <style> element in your <template> element.

So, given something like this from the very first part of this series, where you have an <h2> in your <template>, your styles for that <h2> should all be in the <style> element.

<template id="zprofiletemplate">
  <style>
    h2 {
      font-size: 3em;
      margin: 0 0 0.25em 0;
      line-height: 0.8;
    }
    /* other styles */
  </style>
  <div class="profile-wrapper">
    <div class="info">
      <h2>
        <slot name="zombie-name">Zombie Bob</slot>
      </h2>
      <!-- other zombie profile info -->
    </div>
</template>

But sometimes we might need to style an element in the shadow DOM based on information that exists on the page. For instance, let’s say we have a page for each zombie in the undying love system with matches. We could add a class to profiles based on how close of a match they are. We could then, for instance, highlight a match’s name if he/she/it is a good match. The closeness of a match would vary based on whose list of potential matches is being shown and we won’t know that information until we’re on that page, so we can’t bake the functionality into the web component. Since the <h2> is in the shadow DOM, though, we can’t access or style it from outside the shadow DOM meaning a selector of zombie-profile h2 on the matches page won’t work.

But, if we make a slight adjustment to the <template> markup by adding a part attribute to the <h2>:

<template id="zprofiletemplate">
  <style>
    h2 {
      font-size: 3em;
      margin: 0 0 0.25em 0;
      line-height: 0.8;
    }
    /* other styles */
  </style>
  <div class="profile-wrapper">
    <div class="info">
      <h2 part="zname">
        <slot name="zombie-name">Zombie Bob</slot>
      </h2>
      <!-- other zombie profile info -->
    </div>
</template>

Like a spray of Bianca in the mouth, we now have the superpowers to break through the shadow DOM barrier and style those elements from outside of the <template>:

/* External stylesheet */
.high-match::part(zname) {
  color: blue;
}
.medium-match::part(zname) {
  color: navy;
}
.low-match::part(zname) {
  color: slategray;
}

There are lots of things to consider when it comes to using CSS ::part. For example, styling an element inside of a part is a no-go:

/* frowny-face emoji */
.high-match::part(zname) span { ... }

But you can add a part attribute on that element and style it via its own part name.

What happens if we have a web component inside another web component, though? Will ::part still work? If the web component appears in the page’s markup, i.e. you’re slotting it in, ::part works just fine from the main page’s CSS.

<zombie-profile class="high-match">
  <img slot="profile-image" src="https://assets.codepen.io/1804713/leroy.png" />
  <span slot="zombie-name">Leroy</span>
  <zombie-details slot="zdetails">
    <!-- Leroy's details -->
  </zombie-details>
</zombie-profile>

But if the web component is in the template/shadow DOM, then ::part cannot pierce both shadow trees, just the first one. We need to bring the ::part into the light… so to speak. We can do that with an exportparts attribute.

To demonstrate this we’ll add a “watermark” behind the profiles using a web component. (Why? Believe it or not this was the least contrived example I could come up with.) Here are our templates: (1) the template for <zombie-watermark>, and (2) the same template for <zombie-profile> but with added a <zombie-watermark> element on the end.

<template id="zwatermarktemplate">
  <style>
    div {
    text-transform: uppercase;
      font-size: 2.1em;
      color: rgb(0 0 0 / 0.1);
      line-height: 0.75;
      letter-spacing: -5px;
    }
    span {
      color: rgb( 255 0 0 / 0.15);
    }
  </style>
  <div part="watermark">
    U n d y i n g  L o v e  U n d y i n g  L o v e  U n d y i n g  L o v e  <span part="copyright">©2 0 2 7 U n d y i n g  L o v e  U n L t d .</span>
  <!-- Repeat this a bunch of times so we can cover the background of the profile -->
  </div> 
</template>
<template id="zprofiletemplate">
  <style>
    ::part(watermark) {
      color: rgb( 0 0 255 / 0.1);
    }
    /* More styles */
  </style>
  <!-- zombie-profile markup -->
  <zombie-watermark exportparts="copyright"></zombie-watermark>
</template>
<style>
  /* External styles */
  ::part(copyright) {
    color: rgb( 0 100 0 / 0.125);
  }
</style>

Since ::part(watermark) is only one shadow DOM above the <zombie-watermark>, it works fine from within the <zombie-profile>’s template styles. Also, since we’ve used exportparts="copyright" on the <zombie-watermark>, the copyright part has been pushed up into the <zombie-profile>‘s shadow DOM and ::part(copyright) now works even in external styles, but ::part(watermark) will not work outside the <zombie-profile>’s template.

We can also forward and rename parts with that attribute:

<zombie-watermark exportparts="copyright: cpyear"></zombie-watermark>
/* Within zombie-profile's shadow DOM */

/* happy-face emoji */
::part(cpyear) { ... }

/* frowny-face emoji */
::part(copyright) { ... }

Structural pseudo-classes (:nth-child, etc.) don’t work on parts either, but you can use pseudo-classes like :hover. Let’s animate the high match names a little and make them shake as they’re lookin’ for some lovin’. Okay, I heard that and agree it’s awkward. Let’s… uh… make them more, shall we say, noticeable, with a little movement.

.high::part(name):hover {
  animation: highmatch 1s ease-in-out;
}

The ::slotted pseudo-element

The ::slotted CSS pseudo-element actually came up when we covered interactive web components. The basic idea is that ::slotted represents any content in a slot in a web component, i.e. the element that has the slot attribute on it. But, where ::part pierces through the shadow DOM to make a web component’s elements accessible to outside styles, ::slotted remains encapsulated in the <style> element in the component’s <template> and accesses the element that’s technically outside the shadow DOM.

In our <zombie-profile> component, for example, each profile image is inserted into the element through the slot="profile-image".

<zombie-profile>
  <img slot="profile-image" src="photo.jpg" /> 
  <!-- rest of the content -->
</zombie-profile>

That means we can access that image — as well as any image in any other slot — like this:

::slotted(img) {
  width: 100%;
  max-width: 300px;
  height: auto;
  margin: 0 1em 0 0;
}

Similarly, we could select all slots with ::slotted(*) regardless of what element it is. Just beware that ::slotted has to select an element — text nodes are immune to ::slotted zombie styles. And children of the element in the slot are inaccessible.

The :defined pseudo-class

:defined matches all defined elements (I know, surprising, right?), both built-in and custom. If your custom element is shuffling along like a zombie avoiding his girlfriend’s dad’s questions about his “living” situation, you may not want the corpses of the content to show while you’re waiting for them to come back to life errr… load.

You can use the :defined pseudo-class to hide a web component before it’s available — or “defined” — like this:

:not(:defined) {
  display: none;
}

You can see how :defined acts as a sort of mint in the mouth of our component styles, preventing any broken content from showing (or bad breath from leaking) while the page is still loading. Once the element’s defined, it’ll automatically appear because it’s now, you know, defined and not not defined.

I added a setTimeout of five seconds to the web component in the following demo. That way, you can see that <zombie-profile> elements are not shown while they are undefined. The <h1> and the <div> that holds the <zombie-profile> components are still there. It’s just the <zombie-profile> web component that gets display: none since they are not yet defined.

The :host pseudo-class

Let’s say you want to make styling changes to the custom element itself. While you could do this from outside the custom element (like tightening that N95), the result would not be encapsulated, and additional CSS would have to be transferred to wherever this custom element is placed.

It’d be very convenient then to have a pseudo-class that can reach outside the shadow DOM and select the shadow root. That CSS pseudo-class is :host.

In previous examples throughout this series, I set the <zombie-profile> width from the main page’s CSS, like this:

zombie-profile {
  width: calc(50% - 1em);
}

With :host, however, I can set that width from inside the web component, like this:

:host {
  width: calc(50% - 1em);
}

In fact, there was a div with a class of .profile-wrapper in my examples that I can now remove because I can use the shadow root as my wrapper with :host. That’s a nice way to slim down the markup.

You can do descendant selectors from the :host, but only descendants inside the shadow DOM can be accessed — nothing that’s been slotted into your web component (without using ::slotted).

Showing the parts of the HTML that are relevant to the :host pseudo-element.

That said, :host isn’t a one trick zombie. It can also take a parameter, e.g. a class selector, and will only apply styling if the class is present.

:host(.high) {
  border: 2px solid blue;
}

This allows you to make changes should certain classes be added to the custom element.

You can also pass pseudo-classes in there, like :host(:last-child) and :host(:hover).

The :host-context pseudo-class

Now let’s talk about :host-context. It’s like our friend :host(), but on steroids. While :host gets you the shadow root, it won’t tell you anything about the context in which the custom element lives or its parent and ancestor elements.

:host-context, on the other hand, throws the inhibitions to the wind, allowing you to follow the DOM tree up the rainbow to the leprechaun in a leotard. Just note that at the time I’m writing this, :host-context is unsupported in Firefox or Safari. So use it for progressive enhancement.

Here’s how it works. We’ll split our list of zombie profiles into two divs. The first div will have all of the high zombie matches with a .bestmatch class. The second div will hold all the medium and low love matches with a .worstmatch class.

<div class="profiles bestmatch">
  <zombie-profile class="high">
    <!-- etc. -->
  </zombie-profile>
  <!-- more profiles -->
</div>

<div class="profiles worstmatch">
  <zombie-profile class="medium">
    <!-- etc. -->
  </zombie-profile>
  <zombie-profile class="low">
    <!-- etc. -->
  </zombie-profile>
  <!-- more profiles -->
</div>

Let’s say we want to apply different background colors to the .bestmatch and .worstmatch classes. We are unable to do this with just :host:

:host(.bestmatch) {
  background-color: #eef;
}
:host(.worstmatch) {
  background-color: #ddd;
}

That’s because our best and worst match classes are not on our custom elements. What we want is to be able to select the profiles’s parent elements from within the shadow DOM. :host-context pokes past the custom element to match the, er, match classes we want to style.

:host-context(.bestmatch) {
  background-color: #eef;
}
:host-context(.worstmatch) {
  background-color: #ddd;
}

Well, thanks for hanging out despite all the bad breath. (I know you couldn’t tell, but above when I was talking about your breath, I was secretly talking about my breath.)

How would you use ::part, ::slotted, :defined, :host, and :host-context in your web component? Let me know in the comments. (Or if you have cures to chronic halitosis, my wife would be very interested in to hear more.)


Web Component Pseudo-Classes and Pseudo-Elements are Easier Than You Think originally published on CSS-Tricks. You should get the newsletter.



from CSS-Tricks https://ift.tt/6fZ8sR4
via IFTTT

Passkeys: What the Heck and Why?

These things called  passkeys  sure are making the rounds these days. They were a main attraction at  W3C TPAC 2022 , gained support in  Saf...