emoji – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Wed, 14 Dec 2022 18:58:42 +0000 en-US hourly 1 https://wordpress.org/?v=6.2.2 https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/07/star.png?fit=32%2C32&ssl=1 emoji – CSS-Tricks https://css-tricks.com 32 32 45537868 A Serene CSS Dappled Light Effect https://css-tricks.com/css-dappled-light-effect/ https://css-tricks.com/css-dappled-light-effect/#comments Wed, 19 Jan 2022 22:46:39 +0000 https://css-tricks.com/?p=360899 There’s a serene warmth to the early evening sunlight peaking through rustling leaves. Artists use dappled light to create a soft, hypnotic effect.

Bedford Dwellings by Ron Donoughe (2013)

We can create the same sort of dappled light effect in …


A Serene CSS Dappled Light Effect originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
There’s a serene warmth to the early evening sunlight peaking through rustling leaves. Artists use dappled light to create a soft, hypnotic effect.

An oil painting of a tall rectangular orange building with six windows, two by two, and a faint full-width fence in front of it. There is a similar building off in the distance. A tall birch tree is in the foreground with light green and yellow leaves, casting the dappled light effect that is being covered in this article. The shadows cover the green grass between the tree and building, and they extend to the building.
Bedford Dwellings by Ron Donoughe (2013)

We can create the same sort of dappled light effect in web design, using it on photos and illustrations to add that magic touch to what might otherwise be drab walls of content to bring them back to life.

I’ll give you one easy, quick way to add this effect… with just CSS.

Before we get into the code, it’s important to know the composition of dappled light. It’s made up of large spots — circular or elliptical — of light that are intercepted by the shadows cast by the foliage. Basically the light that slips past leaves, branches and so forth. Sometimes the shadows create crisp edges, but are more often blurred since we’re talking about light that passes though many, less defined spaces that diffuse and distort the light as it casts shadows from a further distance than, say, your own stark shadow on a nearby wall from direct sunlight.

Here’s the difference in the appearance of a white wall with and without lit by dappled light:

A side-by-side comparison of the same white brick surface, the left showing the CSS dappled light effect compared to no shadows.
The effect creates splashes of light and shadow.

I’m going to recreate the dappled light effect with both plain text and fun emojis, applying CSS shadows and blends to mimic nature. I’ll cover alternative methods too.

Setting the scene

We’ll use text — letters from the alphabet, special characters, emojis, etc. — to create the shapes of light. And by light, I mean pale, translucent colors. Again, we’re for a dappled light effect rather than something that’s sharp, crisp, or stark.

It’s best to choose characters that are elliptical or oblong in some way — the spots produced by dappled light comes in a variety of shapes. You’ll have to go with your best judgement here to get exactly what you’re going for. Me? I’m using 🍃, 🍂, \ because they are elliptical, oblong, and slanted — a bit of chaos and unpredictability for an otherwise serene effect.

I’m wrapping those in paragraphs that are contained in a .backdrop parent element:

<div class="backdrop">
  <p class="shapes">🍃</p>
  <p class="shapes">🍂</p>
  <p class="shapes">\</p>
</div>

I’m using the parent element as the surface where the dappled light and shadows are cast, applying a background image for its texture. And not only am I giving the surface an explicit width and height, but also setting hidden overflow on it so I’m able to cast shadows that go beyond the surface without revealing them. The objects that cast the dappled light effect are aligned in the middle of the backdrop’s surface, thanks to CSS grid:

.backdrop {
  background: center / cover no-repeat url('image.jpeg');
  width: 400px; height: 240px;
  overflow: hidden;
  display: grid;
}
.backdrop > * {
  grid-area: 1/1;
}

I find that it’s OK if the shapes aren’t aligned exactly on top of one another as long as they overlap in a way that gets the dappled light effect you want. So no pressure to do exactly what I’m doing here to position things in CSS. In fact, I encourage you to try playing with the values to get different patterns of dappled light!

Styling the dappled light in CSS

These are the key properties the emojis should have — transparent color, black semi-transparent background (using the alpha channel in rgba()), blurry white text-shadow with a nice large font-size, and finally, a mix-blend-mode to smooth things out.

.shapes {
  color:  transparent;
  background-color: rgba(0, 0, 0, 0.3); // Use alpha transparency
  text-shadow: 0 0 40px #fff; // Blurry white shadow
  font: bolder 320pt/320pt monospace;
  mix-blend-mode: multiply;
}

mix-blend-mode sets how an element’s colors blend with that of its container element’s content. The multiply value causes the backdrop of an element to show through the element’s light colors and keeps dark colors the same, making for a nicer and more natural dappled light effect.

Refining colors and contrast

I wanted the background-image on the backdrop to be a bit brighter, so I also added filter: brightness(1.6). Another way to do this is with background-blend-mode instead, where all the different backgrounds of an element are blended and, instead of adding the emojis as separate elements, we add them as background images.

Notice that I used a different emoji in that last example as well as floralwhite for some color that’s less intense than pure white for the light. Here’s one of the emoji background images unwrapped:

<svg xmlns='http://www.w3.org/2000/svg'> 
  <foreignObject width='400px' height='240px'> 
    <div xmlns='http://www.w3.org/1999/xhtml' style=
      'font: bolder 720pt/220pt monospace;
       color: transparent;
       text-shadow: 0 0 40px floralwhite;
       background: rgba(0, 0, 0, 0.3);'
    >
      🌾
    </div> 
  </foreignObject> 
</svg>

If you want to use your own images for the shapes, ensure the borders are blurred to create a soft light. The CSS blur() filter can be handy for the same sort of thing. I also used CSS @supports to adjust the shadow blur value for certain browsers as a fallback.

Now let’s circle back to the first example and add a few things:

<div class="backdrop">
  <p class="shapes">🍃</p>
  <p class="shapes">🍂</p>
  <p class="shapes">\</p>
</div>

<p class="content">
  <img width="70px" style="float: left; margin-right: 10px;" src="image.jpeg" alt="">
  Top ten tourists spots for the summer vacation <br><br><i style="font-weight: normal;">Here are the most popular places...</i>
</p>

.backdrop and .shapes are basically the same styles as before. As for the .content, which also sits on top of the .backdrop, I added isolation: isolate to form a new stacking context, excluding the element from the blending as a refining touch.

Animating the light source

I also decided to add a simple CSS animation with @keyframes that get applied to the .backdrop on :hover:

.backdrop:hover > .shapes:nth-of-type(1){
  animation: 2s ease-in-out infinite alternate move;
}
.backdrop:hover > .shapes:nth-of-type(2):hover{
  animation: 4s ease-in-out infinite alternate move-1;
}

@keyframes move {
  from {
    text-indent: -20px;
  }
  to {
    text-indent: 20px;
  }
}
@keyframes move-1 {
  from {
    text-indent: -60px;
  }
  to {
    text-indent: 40px;
  }
}

Animating the text-indent property on the emojis products a super subtle bit of movement — the kind you might expect from clouds moving overhead that change the direction of the light. Just a touch of class, you know.

Wrapping up

There we have it! We drew some inspiration from nature and art to mimic one of those partly cloudy days where the sun shines through trees and bushes, projecting dappled light and shadow spots against a surface. And we did all of it with a small handful of CSS and a few emoji.

The key was how we applied color on the emoji. Using an extra blurry text-shadow in a light color sets the light, and a semi-transparent background-color defines the shadow spots. From there, all we had to do was ensure the backdrop for the light and shadows used a realistic texture with enough contrast to see the dappled light effect in action.


A Serene CSS Dappled Light Effect originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/css-dappled-light-effect/feed/ 8 360899
Icon Glassmorphism Effect in CSS https://css-tricks.com/icon-glassmorphism-effect-in-css/ https://css-tricks.com/icon-glassmorphism-effect-in-css/#comments Mon, 08 Nov 2021 14:57:42 +0000 https://css-tricks.com/?p=322098 I recently came across a cool effect known as glassmorphism in a Dribble shot. My first thought was I could quickly recreate it in a few minutes if I just use some emojis for the icons without wasting time …


Icon Glassmorphism Effect in CSS originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I recently came across a cool effect known as glassmorphism in a Dribble shot. My first thought was I could quickly recreate it in a few minutes if I just use some emojis for the icons without wasting time on SVG-ing them.

Animated gif. Shows a nav bar with four grey icons. On :hover/ :focus, a tinted icon slides and rotates, partly coming out from behind the grey one. In the area where they overlap, we have a glassmorphism effect, with the icon in the back seen as blurred through the semitransparent grey one in front.
The effect we’re after.

I couldn’t have been more wrong about those “few minutes” — they ended up being days of furiously and frustratingly scratching this itch!

It turns out that, while there are resources on how to CSS such an effect, they all assume the very simple case where the overlay is rectangular or at most a rectangle with border-radius. However, getting a glassmorphism effect for irregular shapes like icons, whether these icons are emojis or proper SVGs, is a lot more complicated than I expected, so I thought it would be worth sharing the process, the traps I fell into and the things I learned along the way. And also the things I still don’t understand.

Why emojis?

Short answer: because SVG takes too much time. Long answer: because I lack the artistic sense of just drawing them in an image editor, but I’m familiar with the syntax enough such that I can often compact ready-made SVGs I find online to less than 10% of their original size. So, I cannot just use them as I find them on the internet — I have to redo the code to make it super clean and compact. And this takes time. A lot of time because it’s detail work.

And if all I want is to quickly code a menu concept with icons, I resort to using emojis, applying a filter on them in order to make them match the theme and that’s it! It’s what I did for this liquid tab bar interaction demo — those icons are all emojis! The smooth valley effect makes use of the mask compositing technique.

Animated gif. Shows a white liquid navigation bar with five items, one of which is selected. The selected one has a smooth valley at the top, with a dot levitating above it. It's also black, while the non-selected ones are grey in the normal state and beige in the :hover/ :focus state. On clicking another icon, the selection smoothly changes as the valley an the levitating dot slide to always be above the currently selected item.
Liquid navigation.

Alright, so this is going to be our starting point: using emojis for the icons.

The initial idea

My first thought was to stack the two pseudos (with emoji content) of the navigation links, slightly offset and rotate the bottom one with a transform so that they only partly overlap. Then, I’d make the top one semitransparent with an opacity value smaller than 1, set backdrop-filter: blur() on it, and that should be just about enough.

Now, having read the intro, you’ve probably figured out that didn’t go as planned, but let’s see what it looks like in code and what issues there are with it.

We generate the nav bar with the following Pug:

- let data = {
-   home: { ico: '🏠', hue: 200 }, 
-   notes: { ico: '🗒️', hue: 260 }, 
-   activity: { ico: '🔔', hue: 320 }, 
-   discovery: { ico: '🧭', hue: 30 }
- };
- let e = Object.entries(data);
- let n = e.length;

nav
  - for(let i = 0; i > n; i++)
    a(href='#' data-ico=e[i][1].ico style=`--hue: ${e[i][1].hue}deg`) #{e[i][0]}

Which compiles to the HTML below:

<nav>
  <a href='#' data-ico='🏠' style='--hue: 200deg'>home</a>
  <a href='#' data-ico='🗒️' style='--hue: 260deg'>notes</a>
  <a href='#' data-ico='🔔' style='--hue: 320deg'>activity</a>
  <a href='#' data-ico='🧭' style='--hue: 30deg'>iscovery</a>
</nav>

We start with layout, making our elements grid items. We place the nav in the middle, give links explicit widths, put both pseudos for each link in the top cell (which pushes the link text content to the bottom cell) and middle-align the link text and pseudos.

body, nav, a { display: grid; }

body {
  margin: 0;
  height: 100vh;
}

nav {
  grid-auto-flow: column;
  place-self: center;
  padding: .75em 0 .375em;
}

a {
  width: 5em;
  text-align: center;
  
  &::before, &::after {
    grid-area: 1/ 1;
    content: attr(data-ico);
  }
}
Screenshot. Shows the four menu items lined up in a row in the middle of the page, each item occupying a column, all columns having the same width; with emojis above the link text, both middle-aligned horizontally.
Firefox screenshot of the result after we got layout basics sorted.

Note that the look of the emojis is going to be different depending on the browser you’re using view the demos.

We pick a legible font, bump up its size, make the icons even bigger, set backgrounds, and a nicer color for each of the links (based on the --hue custom property in the style attribute of each):

body {
  /* same as before */
  background: #333;
}

nav {
  /* same as before */
  background: #fff;
  font: clamp(.625em, 5vw, 1.25em)/ 1.25 ubuntu, sans-serif;
}

a {
  /* same as before */
  color: hsl(var(--hue), 100%, 50%);
  text-decoration: none;
  
  &::before, &::after {
    /* same as before */
    font-size: 2.5em;
  }
}
Screenshot. Shows the same layout as before, only with a prettier and bigger font and even bigger icons, backgrounds and each menu item having a different color value based on its --hue.
Chrome screenshot of the result (live demo) after prettifying things a bit.

Here’s where things start to get interesting because we start differentiating between the two emoji layers created with the link pseudos. We slightly move and rotate the ::before pseudo, make it monochrome with a sepia(1) filter, get it to the right hue, and bump up its contrast() — an oldie but goldie technique from Lea Verou. We also apply a filter: grayscale(1) on the ::after pseudo and make it semitransparent because, otherwise, we wouldn’t be able to see the other pseudo through it.

a {
  /* same as before */
  
  &::before {
    transform: 
      translate(.375em, -.25em) 
      rotate(22.5deg);
    filter: 
      sepia(1) 
      hue-rotate(calc(var(--hue) - 50deg)) 
      saturate(3);
  }
	
  &::after {
    opacity: .5;
    filter: grayscale(1);
  }
}
Screenshot. Same nav bar as before, only now the top icon layer is grey and semitransparent, while the bottom one is slightly offset and rotated, mono in the specified --hue.
Chrome screenshot of the result (live demo) after differentiating between the two icon layers.

Hitting a wall

So far, so good… so what? The next step, which I foolishly thought would be the last when I got the idea to code this, involves setting a backdrop-filter: blur(5px) on the top (::after) layer.

Note that Firefox still needs the gfx.webrender.all and layout.css.backdrop-filter.enabled flags set to true in about:config in order for the backdrop-filter property to work.

Animated gif. Shows how to find the flags mentioned above (gfx.webrender.all and layout.css.backdrop-filter.enabled) in order to ensure they are set to true. Go to about:config, start typing their name in the search box and double click their value to change it if it's not set to true already.
The flags that are still required in Firefox for backdrop-filter to work.

Sadly, the result looks nothing like what I expected. We get a sort of overlay the size of the entire top icon bounding box, but the bottom icon isn’t really blurred.

Screenshot collage. Shows the not really blurred, but awkward result with an overlay the size of the top emoji box after applying the backdrop-filter property. This happens both in Chrome (top) and in Firefox (bottom).
Chrome (top) and Firefox (bottom) screenshots of the result (live demo) after applying backdrop-filter.

However, I’m pretty sure I’ve played with backdrop-filter: blur() before and it worked, so what the hairy heck is going on here?

Screenshot. Shows a working glassmorphism effect, created via a control panel where we draw some sliders to get the value for each filter function.
Working glassmorphism effect (live demo) in an older demo I coded.

Getting to the root of the problem

Well, when you have no idea whatsoever why something doesn’t work, all you can do is take another working example, start adapting it to try to get the result you want… and see where it breaks!

So let’s see a simplified version of my older working demo. The HTML is just an article in a section. In the CSS, we first set some dimensions, then we set an image background on the section, and a semitransparent one on the article. Finally, we set the backdrop-filter property on the article.

section { background: url(cake.jpg) 50%/ cover; }

article {
  margin: 25vmin;
  height: 40vh;
  background: hsla(0, 0%, 97%, .25);
  backdrop-filter: blur(5px);
}
Screenshot. Shows a working glassmorphism effect, where we have a semitransparent box on top of its parent one, having an image background.
Working glassmorphism effect (live demo) in a simplified test.

This works, but we don’t want our two layers nested in one another; we want them to be siblings. So, let’s make both layers article siblings, make them partly overlap and see if our glassmorphism effect still works.

<article class='base'></article>
<article class='grey'></article>
article { width: 66%; height: 40vh; }

.base { background: url(cake.jpg) 50%/ cover; }

.grey {
  margin: -50% 0 0 33%;
  background: hsla(0, 0%, 97%, .25);
  backdrop-filter: blur(5px);
}
Screenshot collage. Shows the case where we have a semitransparent box on top of its sibling having an image background. The top panel screenshot was taken in Chrome, where the glassmorphism effect works as expected. The bottom panel screenshot was taken in Firefox, where things are mostly fine, but the blur handling around the edges is really weird.
Chrome (top) and Firefox (bottom) screenshots of the result (live demo) when the two layers are siblings.

Everything still seems fine in Chrome and, for the most part, Firefox too. It’s just that the way blur() is handled around the edges in Firefox looks awkward and not what we want. And, based on the few images in the spec, I believe the Firefox result is also incorrect?

I suppose one fix for the Firefox problem in the case where our two layers sit on a solid background (white in this particular case) is to give the bottom layer (.base) a box-shadow with no offsets, no blur, and a spread radius that’s twice the blur radius we use for the backdrop-filter applied on the top layer (.grey). Sure enough, this fix seems to work in our particular case.

Things get a lot hairier if our two layers sit on an element with an image background that’s not fixed (in which case, we could use a layered backgrounds approach to solve the Firefox issue), but that’s not the case here, so we won’t get into it.

Still, let’s move on to the next step. We don’t want our two layers to be two square boxes, we want then to be emojis, which means we cannot ensure semitransparency for the top one using a hsla() background — we need to use opacity.

.grey {
  /* same as before */
  opacity: .25;
  background: hsl(0, 0%, 97%);
}
Screenshot. Shows the case where we have a subunitary opacity on the top layer in order to make it semitransparent, instead of a subunitary alpha value for the semitransparent background.
The result (live demo) when the top layer is made semitransparent using opacity instead of a hsla() background.

It looks like we found the problem! For some reason, making the top layer semitransparent using opacity breaks the backdrop-filter effect in both Chrome and Firefox. Is that a bug? Is that what’s supposed to happen?

Bug or not?

MDN says the following in the very first paragraph on the backdrop-filter page:

Because it applies to everything behind the element, to see the effect you must make the element or its background at least partially transparent.

Unless I don’t understand the above sentence, this appears to suggest that opacity shouldn’t break the effect, even though it does in both Chrome and Firefox.

What about the spec? Well, the spec is a huge wall of text without many illustrations or interactive demos, written in a language that makes reading it about as appealing as sniffing a skunk’s scent glands. It contains this part, which I have a feeling might be relevant, but I’m unsure that I understand what it’s trying to say — that the opacity set on the top element that we also have the backdrop-filter on also gets applied on the sibling underneath it? If that’s the intended result, it surely isn’t happening in practice.

The effect of the backdrop-filter will not be visible unless some portion of element B is semi-transparent. Also note that any opacity applied to element B will be applied to the filtered backdrop image as well.

Trying random things

Whatever the spec may be saying, the fact remains: making the top layer semitransparent with the opacity property breaks the glassmorphism effect in both Chrome and Firefox. Is there any other way to make an emoji semitransparent? Well, we could try filter: opacity()!

At this point, I should probably be reporting whether this alternative works or not, but the reality is… I have no idea! I spent a couple of days around this step and got to check the test countless times in the meanwhile — sometimes it works, sometimes it doesn’t in the exact same browsers, wit different results depending on the time of day. I also asked on Twitter and got mixed answers. Just one of those moments when you can’t help but wonder whether some Halloween ghost isn’t haunting, scaring and scarring your code. For eternity!

It looks like all hope is gone, but let’s try just one more thing: replacing the rectangles with text, the top one being semitransparent with color: hsla(). We may be unable to get the cool emoji glassmorphism effect we were after, but maybe we can get such a result for plain text.

So we add text content to our article elements, drop their explicit sizing, bump up their font-size, adjust the margin that gives us partial overlap and, most importantly, replace the background declarations in the last working version with color ones. For accessibility reasons, we also set aria-hidden='true' on the bottom one.

<article class='base' aria-hidden='true'>Lion 🧡</article>
<article class='grey'>Lion 🖤</article>
article { font: 900 21vw/ 1 cursive; }

.base { color: #ff7a18; }

.grey {
  margin: -.75em 0 0 .5em;
  color: hsla(0, 0%, 50%, .25);
  backdrop-filter: blur(5px);
}
Screenshot collage. Shows the case where we have a semitransparent text layer on top of its identical solid orange text sibling. The top panel screenshot was taken in Chrome, where we get proper blurring, but it's underneath the entire bounding box of the semitransparent top text, not limited to just the actual text. The bottom panel screenshot was taken in Firefox, where things are even worse, with the blur handling around the edges being really weird.
Chrome (top) and Firefox (bottom) screenshots of the result (live demo) when we have two text layers.

There are couple of things to note here.

First, setting the color property to a value with a subunitary alpha also makes emojis semitransparent, not just plain text, both in Chrome and in Firefox! This is something I never knew before and I find absolutely mindblowing, given the other channels don’t influence emojis in any way.

Second, both Chrome and Firefox are blurring the entire area of the orange text and emoji that’s found underneath the bounding box of the top semitransparent grey layer, instead of just blurring what’s underneath the actual text. In Firefox, things look even worse due to that awkward sharp edge effect.

Even though the box blur is not what we want, I can’t help but think it does make sense since the spec does say the following:

[…] to create a “transparent” element that allows the full filtered backdrop image to be seen, you can use “background-color: transparent;”.

So let’s make a test to check what happens when the top layer is another non-rectangular shape that’s not text, but instead obtained with a background gradient, a clip-path or a mask!

Screenshot collage. Shows the case where we have semitransparent non-rectangular shaped layers (obtained with three various methods: gradient background, clip-path and mask) on top of a rectangular siblings. The top panel screenshot was taken in Chrome, where things seem to work fine in the clip-path and mask case, but not in the gradient background case. In this case, everything that's underneath the bounding box of the top element gets blurred, not just what's underneath the visible part. The bottom panel screenshot was taken in Firefox, where, regardless of the way we got the shape, everything underneath its bounding box gets blurred, not just what's underneath the actual shape. Furthermore, in all three cases we have the old awkward sharp edge issue we've had in Firefox before
Chrome (top) and Firefox (bottom) screenshots of the result (live demo) when the top layer is a non-rectangular shape.

In both Chrome and Firefox, the area underneath the entire box of the top layer gets blurred when the shape is obtained with background: gradient() which, as mentioned in the text case before, makes sense per the spec. However, Chrome respects the clip-path and mask shapes, while Firefox doesn’t. And, in this case, I really don’t know which is correct, though the Chrome result does make more sense to me.

Moving towards a Chrome solution

This result and a Twitter suggestion I got when I asked how to make the blur respect the text edges and not those of its bounding box led me to the next step for Chrome: applying a mask clipped to the text on the top layer (.grey). This solution doesn’t work in Firefox for two reasons: one, text is sadly a non-standard mask-clip value that only works in WebKit browsers and, two, as shown by the test above, masking doesn’t restrict the blur area to the shape created by the mask in Firefox anyway.

/* same as before */

.grey {
  /* same as before */
  -webkit-mask: linear-gradient(red, red) text; /* only works in WebKit browsers */
}
Chrome screenshot. Shows two text and emoji layers partly overlapping. The top one is semitransparent, so through it, we can see the layer underneath blurred (by applying a backdrop-filter on the top one).
Chrome screenshot of the result (live demo) when the top layer has a mask restricted to the text area.

Alright, this actually looks like what we want, so we can say we’re heading in the right direction! However, here we’ve used an orange heart emoji for the bottom layer and a black heart emoji for the top semitransparent layer. Other generic emojis don’t have black and white versions, so my next idea was to initially make the two layers identical, then make the top one semitransparent and use filter: grayscale(1) on it.

article { 
  color: hsla(25, 100%, 55%, var(--a, 1));
  font: 900 21vw/ 1.25 cursive;
}

.grey {
  --a: .25;
  margin: -1em 0 0 .5em;
  filter: grayscale(1);
  backdrop-filter: blur(5px);
  -webkit-mask: linear-gradient(red, red) text;
}
Chrome screenshot. Shows two text and emoji layers partly overlapping. The top one is semitransparent, so through it, we can see the layer underneath blurred (by applying a backdrop-filter on the top one). The problem is that applying the grayscale filter on the top semitransparent layer not only affects this layer, but also the blurred area of the layer underneath.
Chrome screenshot of the result (live demo) when the top layer gets a grayscale(1) filter.

Well, that certainly had the effect we wanted on the top layer. Unfortunately, for some weird reason, it seems to have also affected the blurred area of the layer underneath. This moment is where to briefly consider throwing the laptop out the window… before getting the idea of adding yet another layer.

It would go like this: we have the base layer, just like we have so far, slightly offset from the other two above it. The middle layer is a “ghost” (transparent) one that has the backdrop-filter applied. And finally, the top one is semitransparent and gets the grayscale(1) filter.

body { display: grid; }

article {
  grid-area: 1/ 1;
  place-self: center;
  padding: .25em;
  color: hsla(25, 100%, 55%, var(--a, 1));
  font: 900 21vw/ 1.25 pacifico, z003, segoe script, comic sans ms, cursive;
}

.base { margin: -.5em 0 0 -.5em; }

.midl {
  --a: 0;
  backdrop-filter: blur(5px);
  -webkit-mask: linear-gradient(red, red) text;
}

.grey { filter: grayscale(1) opacity(.25) }
Chrome screenshot. Shows two text and emoji layers partly overlapping. The top one is semitransparent grey, so through it, we can see the layer underneath blurred (by applying a backdrop-filter on a middle, completely transparent one).
Chrome screenshot of the result (live demo) with three layers.

Now we’re getting somewhere! There’s just one more thing left to do: make the base layer monochrome!

/* same as before */

.base {
  margin: -.5em 0 0 -.5em;
  filter: sepia(1) hue-rotate(165deg) contrast(1.5);
}
Chrome screenshot. Shows two text and emoji layers partly overlapping. The bottom one is mono (bluish in this case) and blurred at the intersection with the semitransparent grey one on top.
Chrome screenshot of the result (live demo) we were after.

Alright, this is the effect we want!

Getting to a Firefox solution

While coding the Chrome solution, I couldn’t help but think we may be able to pull off the same result in Firefox since Firefox is the only browser that supports the element() function. This function allows us to take an element and use it as a background for another element.

The idea is that the .base and .grey layers will have the same styles as in the Chrome version, while the middle layer will have a background that’s (via the element() function) a blurred version of our layers.

To make things easier, we start with just this blurred version and the middle layer.

<article id='blur' aria-hidden='true'>Lion 🦁</article>
<article class='midl'>Lion 🦁</article>

We absolutely position the blurred version (still keeping it in sight for now), make it monochrome and blur it and then use it as a background for .midl.

#blur {
  position: absolute;
  top: 2em; right: 0;
  margin: -.5em 0 0 -.5em;
  filter: sepia(1) hue-rotate(165deg) contrast(1.5) blur(5px);
}

.midl {
  --a: .5;
  background: -moz-element(#blur);
}

We’ve also made the text on the .midl element semitransparent so we can see the background through it. We’ll make it fully transparent eventually, but for now, we still want to see its position relative to the background.

Firefox screenshot. Shows a blurred mono (bluish in this case) text and emoji element below everything else. 'Everything else' in this case is another text and emoji element that uses a semitransparent color so we can partly see through to the background which is set to the blurred element via the element() function.
Firefox screenshot of the result (live demo) when using the blurred element #blur as a background.

We can notice a one issue right away: while margin works to offset the actual #blur element, it does nothing for shifting its position as a background. In order to get such an effect, we need to use the transform property. This can also help us if we want a rotation or any other transform — as it can be seen below where we’ve replaced the margin with transform: rotate(-9deg).

Firefox screenshot. Shows a slightly rotated blurred mono (bluish in this case) text and emoji element below everything else. 'Everything else' in this case is another text and emoji element that uses a semitransparent color so we can partly see through to the background which is set to the slightly rotated blurred element via the element() function.
Firefox screenshot of the result (live demo) when using transform: rotate() instead of margin on the #blur element.

Alright, but we’re still sticking to just a translation for now:

#blur {
  /* same as before */
  transform: translate(-.25em, -.25em); /* replaced margin */
}
Firefox screenshot. Shows a slightly offset blurred mono (bluish in this case) text and emoji element below everything else. 'Everything else' in this case is another text and emoji element that uses a semitransparent color so we can partly see through to the background which is set to the slightly offset blurred element via the element() function. This slight offset means the actual text doesn't perfectly overlap with the background one anymore.
Firefox screenshot of the result (live demo) when using transform: translate() instead of margin on the #blur element.

One thing to note here is that a bit of the blurred background gets cut off as it goes outside the limits of the middle layer’s padding-box. That doesn’t matter at this step anyway since our next move is to clip the background to the text area, but it’s good to just have that space since the .base layer is going to get translated just as far.

Firefox screenshot. Shows a slightly offset blurred mono (bluish in this case) text and emoji element below everything else. 'Everything else' in this case is another text and emoji element that uses a semitransparent color so we can partly see through to the background which is set to the slightly offset blurred element via the element() function. This slight offset means the actual text doesn't perfectly overlap with the background one anymore. It also means that the translated background text may not fully be within the limits of the padding-box anymore, as highlighted in this screenshot, which also shows the element boxes overlays.
Firefox screenshot highlighting how the translated #blur background exceeds the limits of the padding-box on the .midl element.

So, we’re going to bump up the padding by a little bit, even if, at this point, it makes absolutely no difference visually as we’re also setting background-clip: text on our .midl element.

article {
  /* same as before */
  padding: .5em;
}

#blur {
  position: absolute;
  bottom: 100vh;
  transform: translate(-.25em, -.25em);
  filter: sepia(1) hue-rotate(165deg) contrast(1.5) blur(5px);
}

.midl {
  --a: .1;
  background: -moz-element(#blur);
  background-clip: text;
}

We’ve also moved the #blur element out of sight and further reduced the alpha of the .midl element’s color, as we want a better view at the background through the text. We’re not making it fully transparent, but still keeping it visible for now just so we know what area it covers.

Firefox screenshot. Shows a text and emoji element that uses a semitransparent color so we can partly see through to the background which is set to a blurred element (now positioned out of sight) via the element() function. This slight offset means the actual text doesn't perfectly overlap with the background one anymore. We have also clipped the background of this element to the text, so that none of the background outside it is visible. Even so, there's enough padding room so that the blurred background is contained within the padding-box.
Firefox screenshot of the result (live demo) after clipping the .midl element’s background to text.

The next step is to add the .base element with pretty much the same styles as it had in the Chrome case, only replacing the margin with a transform.

<article id='blur' aria-hidden='true'>Lion 🦁</article>
<article class='base' aria-hidden='true'>Lion 🦁</article>
<article class='midl'>Lion 🦁</article>
#blur {
  position: absolute;
  bottom: 100vh;
  transform: translate(-.25em, -.25em);
  filter: sepia(1) hue-rotate(165deg) contrast(1.5) blur(5px);
}

.base {
  transform: translate(-.25em, -.25em);
  filter: sepia(1) hue-rotate(165deg) contrast(1.5);
}

Since a part of these styles are common, we can also add the .base class on our blurred element #blur in order to avoid duplication and reduce the amount of code we write.

<article id='blur' class='base' aria-hidden='true'>Lion 🦁</article>
<article class='base' aria-hidden='true'>Lion 🦁</article>
<article class='midl'>Lion 🦁</article>
#blur {
  --r: 5px;
  position: absolute;
  bottom: 100vh;
}

.base {
  transform: translate(-.25em, -.25em);
  filter: sepia(1) hue-rotate(165deg) contrast(1.5) blur(var(--r, 0));
}
Firefox screenshot. Shows two text and emoji layers slightly offset from one another. The .base one, first in the DOM order, is made mono with a filter and slightly offset to the top left with a transform. The .midl one, following it in DOM order, has semitransparent text so that we can see through to the text clipped background, which uses as a background image the blurred version of the mono, slightly offset .base layer. In spite of DOM order, the .base layer still shows up on top.
Firefox screenshot of the result (live demo) after adding the .base layer.

We have a different problem here. Since the .base layer has a transform, it’s now on top of the .midl layer in spite of DOM order. The simplest fix? Add z-index: 2 on the .midl element!

Firefox screenshot. Shows two text and emoji layers slightly offset from one another. The .base one, first in the DOM order, is made mono with a filter and slightly offset to the top left with a transform. The .midl one, following it in DOM order, has semitransparent text so that we can see through to the text clipped background, which uses as a background image the blurred version of the mono, slightly offset .base layer. Having explicitly set a z-index on the .midl layer, it now shows up on top of the .base one.
Firefox screenshot of the result (live demo) after fixing the layer order such that .base is underneath .midl.

We still have another, slightly more subtle problem: the .base element is still visible underneath the semitransparent parts of the blurred background we’ve set on the .midl element. We don’t want to see the sharp edges of the .base layer text underneath, but we are because blurring causes pixels close to the edge to become semitransparent.

Screenshot. Shows two lines of blue text with a red outline to highlight the boundaries of the actual text. The text on the second line is blurred and it can be seen how this causes us to have semitransparent blue pixels on both sides of the red outline - both outside and inside.
The blur effect around the edges.

Depending on what kind of background we have on the parent of our text layers, this is a problem that can be solved with a little or a lot of effort.

If we only have a solid background, the problem gets solved by setting the background-color on our .midl element to that same value. Fortunately, this happens to be our case, so we won’t go into discussing the other scenario. Maybe in another article.

.midl {
  /* same as before */
  background: -moz-element(#blur) #fff;
  background-clip: text;
}
Firefox screenshot. Shows two text and emoji layers slightly offset from one another. The .base one, first in the DOM order, is made mono with a filter and slightly offset to the top left with a transform. The .midl one, following it in DOM order, has semitransparent text so that we can see through to the text clipped background, which uses as a background image the blurred version of the mono, slightly offset .base layer. Having explicitly set a z-index on the .midl layer and having set a fully opaque background-color on it, the .base layer now lies underneath it and it isn't visible through any semitransparent parts in the text area because there aren't any more such parts.
Firefox screenshot of the result (live demo) after ensuring the .base layer isn’t visible through the background of the .midl one.

We’re getting close to a nice result in Firefox! All that’s left to do is add the top .grey layer with the exact same styles as in the Chrome version!

.grey { filter: grayscale(1) opacity(.25); }

Sadly, doing this doesn’t produce the result we want, which is something that’s really obvious if we also make the middle layer text fully transparent (by zeroing its alpha --a: 0) so that we only see its background (which uses the blurred element #blur on top of solid white) clipped to the text area:

Firefox screenshot. Shows two text and emoji layers slightly offset from one another. The .base one, first in the DOM order, is made mono with a filter and slightly offset to the top left with a transform. The .midl one, following it in DOM order, has transparent text so that we can see through to the text clipped background, which uses as a background image the blurred version of the mono, slightly offset .base layer. Since the background-color of this layer coincides to that of their parent, it is hard to see. We also have a third .grey layer, the last in DOM order. This should be right on top of the .midl one, but, due to having set a z-index on the .midl layer, the .grey layer is underneath it and not visible, in spite of the DOM order.
Firefox screenshot of the result (live demo) after adding the top .grey layer.

The problem is we cannot see the .grey layer! Due to setting z-index: 2 on it, the middle layer .midl is now above what should be the top layer (the .grey one), in spite of the DOM order. The fix? Set z-index: 3 on the .grey layer!

.grey {
  z-index: 3;
  filter: grayscale(1) opacity(.25);
}

I’m not really fond of giving out z-index layer after layer, but hey, it’s low effort and it works! We now have a nice Firefox solution:

Firefox screenshot. Shows two text and emoji layers partly overlapping. The bottom one is mono (bluish in this case) and blurred at the intersection with the semitransparent grey one on top.
Firefox screenshot of the result (live demo) we were after.

Combining our solutions into a cross-browser one

We start with the Firefox code because there’s just more of it:

<article id='blur' class='base' aria-hidden='true'>Lion 🦁</article>
<article class='base' aria-hidden='true'>Lion 🦁</article>
<article class='midl' aria-hidden='true'>Lion 🦁</article>
<article class='grey'>Lion 🦁</article>
body { display: grid; }

article {
  grid-area: 1/ 1;
  place-self: center;
  padding: .5em;
  color: hsla(25, 100%, 55%, var(--a, 1));
  font: 900 21vw/ 1.25 cursive;
}

#blur {
  --r: 5px;
  position: absolute;
  bottom: 100vh;
}

.base {
  transform: translate(-.25em, -.25em);
  filter: sepia(1) hue-rotate(165deg) contrast(1.5) blur(var(--r, 0));
}

.midl {
  --a: 0;
  z-index: 2;
  background: -moz-element(#blur) #fff;
  background-clip: text;
}

.grey {
  z-index: 3;
  filter: grayscale(1) opacity(.25);
}

The extra z-index declarations don’t impact the result in Chrome and neither does the out-of-sight #blur element. The only things that this is missing in order for this to work in Chrome are the backdrop-filter and the mask declarations on the .midl element:

backdrop-filter: blur(5px);
-webkit-mask: linear-gradient(red, red) text;

Since we don’t want the backdrop-filter to get applied in Firefox, nor do we want the background to get applied in Chrome, we use @supports:

$r: 5px;

/* same as before */

#blur {
  /* same as before */
  --r: #{$r};
}

.midl {
  --a: 0;
  z-index: 2;
  /* need to reset inside @supports so it doesn't get applied in Firefox */
  backdrop-filter: blur($r);
  /* invalid value in Firefox, not applied anyway, no need to reset */
  -webkit-mask: linear-gradient(red, red) text;
  
  @supports (background: -moz-element(#blur)) { /* for Firefox */
    background: -moz-element(#blur) #fff;
    background-clip: text;
    backdrop-filter: none;
  }
}

This gives us a cross-browser solution!

Chrome (top) and Firefox (bottom) screenshot collage of the text and emoji glassmorphism effect for comparison. The blurred backdrop seems thicker in Chrome and the emojis are obviously different, but the result is otherwise pretty similar.
Chrome (top) and Firefox (bottom) screenshots of the result (live demo) we were after.

While the result isn’t the same in the two browsers, it’s still pretty similar and good enough for me.

What about one-elementing our solution?

Sadly, that’s impossible.

First off, the Firefox solution requires us to have at least two elements since we use one (referenced by its id) as a background for another.

Second, while the first thought with the remaining three layers (which are the only ones we need for the Chrome solution anyway) is that one of them could be the actual element and the other two its pseudos, it’s not so simple in this particular case.

For the Chrome solution, each of the layers has at least one property that also irreversibly impacts any children and any pseudos it may have. For the .base and .grey layers, that’s the filter property. For the middle layer, that’s the mask property.

So while it’s not pretty to have all those elements, it looks like we don’t have a better solution if we want the glassmorphism effect to work on emojis too.

If we only want the glassmorphism effect on plain text — no emojis in the picture — this can be achieved with just two elements, out of which only one is needed for the Chrome solution. The other one is the #blur element, which we only need in Firefox.

<article id='blur'>Blood</article>
<article class='text' aria-hidden='true' data-text='Blood'></article>

We use the two pseudos of the .text element to create the base layer (with the ::before) and a combination of the other two layers (with the ::after). What helps us here is that, with emojis out of the picture, we don’t need filter: grayscale(1), but instead we can control the saturation component of the color value.

These two pseudos are stacked one on top of the other, with the bottom one (::before) offset by the same amount and having the same color as the #blur element. This color value depends on a flag, --f, that helps us control both the saturation and the alpha. For both the #blur element and the ::before pseudo (--f: 1), the saturation is 100% and the alpha is 1. For the ::after pseudo (--f: 0), the saturation is 0% and the alpha is .25.

$r: 5px;

%text { // used by #blur and both .text pseudos
  --f: 1;
  grid-area: 1/ 1; // stack pseudos, ignored for absolutely positioned #base
  padding: .5em;
  color: hsla(345, calc(var(--f)*100%), 55%, calc(.25 + .75*var(--f)));
  content: attr(data-text);
}

article { font: 900 21vw/ 1.25 cursive }

#blur {
  position: absolute;
  bottom: 100vh;
  filter: blur($r);
}

#blur, .text::before {
  transform: translate(-.125em, -.125em);
  @extend %text;
}

.text {
  display: grid;
	
  &::after {
    --f: 0;
    @extend %text;
    z-index: 2;
    backdrop-filter: blur($r);
    -webkit-mask: linear-gradient(red, red) text;

    @supports (background: -moz-element(#blur)) {
      background: -moz-element(#blur) #fff;
      background-clip: text;
      backdrop-filter: none;
    }
  }
}

Applying the cross-browser solution to our use case

The good news here is our particular use case where we only have the glassmorphism effect on the link icon (not on the entire link including the text) actually simplifies things a tiny little bit.

We use the following Pug to generate the structure:

- let data = {
-   home: { ico: '🏠', hue: 200 }, 
-   notes: { ico: '🗒️', hue: 260 }, 
-   activity: { ico: '🔔', hue: 320 }, 
-   discovery: { ico: '🧭', hue: 30 }
- };
- let e = Object.entries(data);
- let n = e.length;

nav
  - for(let i = 0; i < n; i++)
    - let ico = e[i][1].ico;
    a.item(href='#' style=`--hue: ${e[i][1].hue}deg`)
      span.icon.tint(id=`blur${i}` aria-hidden='true') #{ico}
      span.icon.tint(aria-hidden='true') #{ico}
      span.icon.midl(aria-hidden='true' style=`background-image: -moz-element(#blur${i})`) #{ico}
      span.icon.grey(aria-hidden='true') #{ico}
      | #{e[i][0]}

Which produces an HTML structure like the one below:

<nav>
  <a class='item' href='#' style='--hue: 200deg'>
    <span class='icon tint' id='blur0' aria-hidden='true'>🏠</span>
    <span class='icon tint' aria-hidden='true'>🏠</span>
    <span class='icon midl' aria-hidden='true' style='background-image: -moz-element(#blur0)'>🏠</span>
    <span class='icon grey' aria-hidden='true'>🏠</span>
    home
  </a>
  <!-- the other nav items -->
</nav>

We could probably replace a part of those spans with pseudos, but I feel it’s more consistent and easier like this, so a span sandwich it is!

One very important thing to notice is that we have a different blurred icon layer for each of the items (because each and every item has its own icon), so we set the background of the .midl element to it in the style attribute. Doing things this way allows us to avoid making any changes to the CSS file if we add or remove entries from the data object (thus changing the number of menu items).

We have almost the same layout and prettified styles we had when we first CSS-ed the nav bar. The only difference is that now we don’t have pseudos in the top cell of an item’s grid; we have the spans:

span {
  grid-area: 1/ 1; /* stack all emojis on top of one another */
  font-size: 4em; /* bump up emoji size */
}

For the emoji icon layers themselves, we also don’t need to make many changes from the cross-browser version we got a bit earlier, though there are a few lttle ones.

First off, we use the transform and filter chains we picked initially when we were using the link pseudos instead of spans. We also don’t need the color: hsla() declaration on the span layers any more since, given that we only have emojis here, it’s only the alpha channel that matters. The default, which is preserved for the .base and .grey layers, is 1. So, instead of setting a color value where only the alpha, --a, channel matters and we change that to 0 on the .midl layer, we directly set color: transparent there. We also only need to set the background-color on the .midl element in the Firefox case as we’ve already set the background-image in the style attribute. This leads to the following adaptation of the solution:

.base { /* mono emoji version */
  transform: translate(.375em, -.25em) rotate(22.5deg);
  filter: sepia(1) hue-rotate(var(--hue)) saturate(3) blur(var(--r, 0));
}

.midl { /* middle, transparent emoji version */
  color: transparent; /* so it's not visible */
  backdrop-filter: blur(5px);
  -webkit-mask: linear-gradient(red 0 0) text;
  
  @supports (background: -moz-element(#b)) {
    background-color: #fff;
    background-clip: text;
    backdrop-filter: none;
  }
}

And that’s it — we have a nice icon glassmorphism effect for this nav bar!

Chrome (top) and Firefox (bottom) screenshot collage of the emoji glassmorphism effect for comparison. The emojis are obviously different, but the result is otherwise pretty similar.
Chrome (top) and Firefox (bottom) screenshots of the desired emoji glassmorphism effect (live demo).

There’s just one more thing to take care of — we don’t want this effect at all times; only on :hover or :focus states. So, we’re going to use a flag, --hl, which is 0 in the normal state, and 1 in the :hover or :focus state in order to control the opacity and transform values of the .base spans. This is a technique I’ve detailed in an earlier article.

$t: .3s;

a {
  /* same as before */
  --hl: 0;
  color: hsl(var(--hue), calc(var(--hl)*100%), 65%);
  transition: color $t;
  
  &:hover, &:focus { --hl: 1; }
}

.base {
  transform: 
    translate(calc(var(--hl)*.375em), calc(var(--hl)*-.25em)) 
    rotate(calc(var(--hl)*22.5deg));
  opacity: var(--hl);
  transition: transform $t, opacity $t;
}

The result can be seen in the interactive demo below when the icons are hovered or focused.

What about using SVG icons?

I naturally asked myself this question after all it took to get the CSS emoji version working. Wouldn’t the plain SVG way make more sense than a span sandwich, and wouldn’t it be simpler? Well, while it does make more sense, especially since we don’t have emojis for everything, it’s sadly not less code and it’s not any simpler either.

But we’ll get into details about that in another article!


Icon Glassmorphism Effect in CSS originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/icon-glassmorphism-effect-in-css/feed/ 6 322098
How to Create CSS Charts With Interesting Shapes, Glyphs and Emoji https://css-tricks.com/how-to-create-css-charts-with-interesting-shapes-glyphs-and-emoji/ https://css-tricks.com/how-to-create-css-charts-with-interesting-shapes-glyphs-and-emoji/#comments Mon, 21 Jun 2021 14:32:45 +0000 https://css-tricks.com/?p=342448 Let’s forego the usual circles and bars we typically see used in charts for more eccentric shapes. With online presentations more and more common today, a quick way to spruce up your web slides and make them stand out is …


How to Create CSS Charts With Interesting Shapes, Glyphs and Emoji originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Let’s forego the usual circles and bars we typically see used in charts for more eccentric shapes. With online presentations more and more common today, a quick way to spruce up your web slides and make them stand out is to give the charts a shapely makeover 🪄

I’ll show you how to create charts with interesting shapes using glyphs, CSS shapes, and emojis with minimal effort.

Let’s start with a simple example.

Using glyphs

<div id="chart">
  <div id="chart-shape">⬠</div>
  <div id="chart-value"></div> 
</div>
#chart {
  width: 300px; 
  height: 300px;
  display: grid;
  background: white;
}
#chart * {
  height: inherit;
  grid-area: 1 / 1;
}

We first give the chart some dimensions and stack the two div inside it by assigning them to the same grid cell. They can be stacked up using any other way, too — with position property, for instance.

Look at the HTML above one more time. One of the divs has a pentagon symbol — the chart shape we want. I added that symbol using the “Emoji and Symbols” keyboard, though it can also be done with the HTML entity value for pentagon, &#x2B20;, inside the div.

The div with the symbol is then styled with CSS font properties as well as a desired chart color. It’s large enough and centered.

#chart-shape {
  font: 300px/300px serif;
  text-align: center; 
  color: limegreen;
}

To the second div in the HTML contains a conic gradient background image. The percentage of the gradient represents the visual value of the chart. The same div also has mix-blend-mode: screen;.

#chart-value {
  background: conic-gradient(transparent 75%, darkseagreen 75%);
  mix-blend-mode: screen;
}

The mix-blend-mode property blends colors inside an element with its backdrop. The screen blend mode value causes a lighter blend to come through. A lighter green shows through the portion where the darkseagreen colored part of the conic gradient overlaps with the limegreen colored pentagram, while the rest of the darskseagreen gradient disappears against the white backdrop of the chart.

An alternative to adding the chart shape in the HTML is to add it as another background layer in CSS and use background-blend-mode instead of mix-blend-mode. However, the code for a chart shape inside the CSS can be less legible for a quick glance. So it’s up to you to see where it’ll be easier for you to add the chart shape in: HTML or CSS. You’ve both options.

#chart {
  width: 300px; 
  height: 300px;
  background:
  url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><foreignObject width='300px' height='100%'><div xmlns='http://www.w3.org/1999/xhtml' style='font:300px/300px serif;color:limegreen;text-align: center;background:white'>⬠</div></foreignObject></svg>"),
  conic-gradient(transparent 75%, darkseagreen 75%);
  background-blend-mode: screen;
}

The pentagon symbol is added as a background image in addition to the conic gradient. Then, finally, the property-value pair background-blend-mode: screen; kicks in and the result looks same as the previous demo.

The pentagon background image is created by embedding the HTML with the pentagon symbol () into an SVG that is embedded into a data URL.

<!-- Unwrapped SVG code from the Data URL -->
<svg xmlns='http://www.w3.org/2000/svg'>
  <foreignObject width='300px' height='100%'>
    <div xmlns='http://www.w3.org/1999/xhtml' 
         style='
          font:300px/300px serif;
          color:limegreen;
          text-align: center;
          background:white;'>
          ⬠
    </div>
  </foreignObject>
</svg>

Which becomes this in CSS:

background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><foreignObject width='300px' height='100%'><div xmlns='http://www.w3.org/1999/xhtml' style='font:300px/300px serif;color:limegreen;text-align: center;background:white'>⬠</div></foreignObject></svg>");

Using CSS shapes

Next, let’s use CSS shapes in place of symbols. CSS shapes are primarily created with the use of border properties. We have a collection of CSS shapes in our archive for your reference.

Here’s a set of properties that can create a simple triangle shape in an element we’ll later add to the SVG, replacing the symbol:

border: 150px solid white; 
border-bottom: 300px solid lime; 
border-top: unset;

When combined with the conic gradient and the background blend, the result is:

<div id="chart"></div>
#chart {
  width: 300px;
  height: 300px;
  background:
  url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><foreignObject width='300px' height='100%'><html xmlns='http://www.w3.org/1999/xhtml'><div style='border:150px solid white; border-bottom:300px solid lime; border-top:unset'></div><div style='border:150px solid transparent; border-bottom:300px solid white; border-top:unset; transform:scale(0.8) translateY(-360px);'></div></html></foreignObject></svg>"),
  conic-gradient(transparent 75%, darkseagreen 75%);
  background-blend-mode: screen;
}

To restrict the design to the border, a smaller white triangle was added to the design.

<!-- Unwrapped SVG code from the Data URL -->
<svg xmlns='http://www.w3.org/2000/svg'>
  <foreignObject width='300px' height='100%'>
   <html xmlns='http://www.w3.org/1999/xhtml'>
    /* green triangle */
    <div style='
         border: 150px solid white; 
         border-bottom: 300px solid lime; 
         border-top: unset'></div>
    /* smaller white triangle */
    <div style='
         border: 150px solid transparent; 
         border-bottom: 300px solid white; 
         border-top: unset; 
         transform: scale(0.8) translateY(-360px);'></div>
   </html>
  </foreignObject>
</svg>

Which, again, becomes this in CSS:

background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><foreignObject width='300px' height='100%'><html xmlns='http://www.w3.org/1999/xhtml'><div style='border:150px solid white; border-bottom:300px solid lime; border-top:unset'></div><div style='border:150px solid transparent; border-bottom:300px solid white; border-top:unset; transform:scale(0.8) translateY(-360px);'></div></html></foreignObject></svg>");

Using emojis

Will emojis work with this approach to charts? You bet it will! 🥳

A block-colored emoji is fed into the SVG image the same way the HTML symbols are. The block color of the emoji is created by giving it a transparent color value, followed by adding a desired color as text-shadow. I covered this technique in another post.

<div id="chart"></div>
#chart {
  width: 300px; 
  height: 300px;
  background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><foreignObject width='300px' height='300px'><body style='margin:0;text-align:center;color:transparent;' xmlns='http://www.w3.org/1999/xhtml'><div style='text-shadow: 0 0 limegreen;font:200px/300px serif;background:white;'>🍏</div><div style='text-shadow:0 0 white;font:170px/300px serif;position:relative;top:-300px;'>🍏</div></body></foreignObject></svg>"),
  conic-gradient(transparent 64%, darkseagreen 64%);
  background-blend-mode: screen;
}

Just as with the last demo, a smaller white apple shape is added at the center to create the border design.

<!-- Unwrapped SVG code from the Data URL -->
<svg xmlns='http://www.w3.org/2000/svg'>
  <foreignObject width='300px' height='300px'>
    <body xmlns='http://www.w3.org/1999/xhtml' style='
          margin: 0;
          text-align: center;
          color:transparent;'>
       /* green apple shape */
       <div style='
            text-shadow: 0 0 limegreen; 
            font-size: 200px; 
            background: white;'>🍏</div>
       /* smaller white apple shape */
       <div style='
            text-shadow:0 0 white; 
            font-size: 170px; 
            position: relative; 
            top: -300px;'>🍏</div>
    </body>
  </foreignObject>
</svg>

I added the two divs inside the <body> element so the repeating style properties of the divs are declared only once in the body element. The divs will then automatically inherit those properties.

Chris had the idea to animate the conic gradient — its percent value to be specific — using the CSS @property (supported in Chrome at the time of writing this article), and it just has the most beautiful effect on the design. @property is a CSS at-rule that explicitly defines a custom CSS property. In supported browsers, when a custom property is defined using @property it can be animated.

@property --n {
  syntax: '<percentage>';
  inherits: true;
  initial-value: 30%;
}
#chart {
  width: 300px; 
  height: 300px;
  --n: 30%;  /*declaration for browsers with no @property support */
  background: 
    url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><foreignObject width='300px' height='300px'><body style='margin:0;text-align:center;color:transparent;' xmlns='http://www.w3.org/1999/xhtml'><div style='text-shadow: 0 0 limegreen;font:200px/300px serif;background:white;'>🍏</div><div style='text-shadow:0 0 white;font:170px/300px serif;position:relative;top:-300px;'>🍏</div></body></foreignObject></svg>"),
    conic-gradient(transparent var(--n), darkseagreen var(--n));
  background-blend-mode: screen;
  transition: --n 2s ease-in-out	
}
#chart:hover { --n: 70%; }

The chart above will change its value on hover. In Chrome, the change will look animated.

And although it won’t be as smooth as CSS animation, you can also try animating the gradient using JavaScript. The following JavaScript will cause a somewhat similar animating effect as the above when the cursor moves over the chart.

const chart = document.querySelector('#chart')
chart.onpointerover = ()=>{
  var i = 30,
      timer = setInterval(()=> {
        if (i < 70)
          chart.style.setProperty('--n', i++ + '%')
        else clearInterval(timer)
      }, 10)
}
chart.onpointerout = ()=>{
  var i = 70,
      timer = setInterval(()=> {
        if (i >= 30) 
          chart.style.setProperty('--n', i-- + '%')
        else clearInterval(timer)
      }, 10)
}

When trying your own designs, keep in mind how the different blend modes work. I used the screen blend mode in all my demos just to keep things simple. But with different blend modes, and different backdrop colors, you’ll get varying results. So, I recommend going deeper into blend modes if you haven’t already.

Also, if you want to exclude an element’s color from the final result, try isolation: isolate; on the element — the browser will ignore that backdrop color when applying the blend.

And even though there are all kinds of unusual and quirky shapes we can use in any wild colors we want, always be mindful of the legibility by making the chart value large and clear enough to read.


How to Create CSS Charts With Interesting Shapes, Glyphs and Emoji originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/how-to-create-css-charts-with-interesting-shapes-glyphs-and-emoji/feed/ 1 342448
Creating Colorful, Smart Shadows https://css-tricks.com/creating-colorful-smart-shadows/ https://css-tricks.com/creating-colorful-smart-shadows/#comments Tue, 04 May 2021 14:19:48 +0000 https://css-tricks.com/?p=339742 A bona fide CSS trick from Kirupa Chinnathambi here. To match a colored shadow with the colors in the background-image of an element, you inherit the background in a pseudo-element, kick it behind the original, then blur and filter it. …


Creating Colorful, Smart Shadows originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
A bona fide CSS trick from Kirupa Chinnathambi here. To match a colored shadow with the colors in the background-image of an element, you inherit the background in a pseudo-element, kick it behind the original, then blur and filter it.

.colorfulShadow {
  position: relative;
}

.colorfulShadow::after {
  content: "";
  width: 100%;
  height: 100%;
  position: absolute;
  background: inherit;
  background-position: center center;
  filter: drop-shadow(0px 0px 10px rgba(0, 0, 0, 0.50)) blur(20px);
  z-index: -1;
}

Negative z-index is always a yellow flag for me as that only works if there are no intermediary backgrounds. But the trick holds. There would always be some other way to layer the backgrounds (like a <span> or whatever).

For some reason this made me think of a demo I saw (I can’t remember who to credit!). Emojis had text-shadow on them, which really made them pop. And those shadows could also be colorized to a similar effect.

To Shared LinkPermalink on CSS-Tricks


Creating Colorful, Smart Shadows originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/creating-colorful-smart-shadows/feed/ 1 339742
Creating CSS Shapes with Emoji https://css-tricks.com/creating-css-shapes-with-emoji/ https://css-tricks.com/creating-css-shapes-with-emoji/#comments Fri, 23 Oct 2020 22:36:16 +0000 https://css-tricks.com/?p=323780 CSS Shapes is a standard that lets us create geometric shapes over floated elements that cause the inline contents — usually text — around those elements to wrap along the specified shapes.

Such a shaped flow of text looks good …


Creating CSS Shapes with Emoji originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
CSS Shapes is a standard that lets us create geometric shapes over floated elements that cause the inline contents — usually text — around those elements to wrap along the specified shapes.

Such a shaped flow of text looks good in editorial designs or designs that work with text-heavy contents to add some visual relief from the chunks of text.

Here’s an example of CSS Shape in use:

The shape-outside property specifies the shape of a float area using either one of the basic shape functions — circle(), ellipse(), polygon() or inset() — or an image, like this:

Inline content wraps along the right side of a left-floated element, and the left side of a right-floated element.

In this post, we’ll use the concept of CSS Shapes with emoji to create interesting text-wrapping effects. Images are rectangles. Many of the shapes we draw in CSS are also boxy or at least limited to standard shapes. Emoji, on the other hand, offers neat opportunities to break out of the box!

Here’s how we’ll do it: We’ll first create an image out of an emoji, and then float it and apply a CSS Shape to it.

I’ve already covered multiple ways to convert emojis to images in this post on creative background patterns. In that I said I wasn’t able to figure out how to use SVG <text> to do the conversion, but I’ve figured it out now and will show you how in this post.  You don’t need to have read that article for this one to make sense, but it’s there if you want to see it.

Let’s make an emoji image

The three steps we’re using to create an emoji image are:

  • Create an emoji-shaped cutout in SVG
  • Convert the SVG code to a DataURL by URL encoding and prefixing it with data:image/svg+xml
  • Use the DataURL as the url() value of an element’s background-image.

Here’s the SVG code that creates the emoji shaped cutout:

<svg width='150px' height='150px' xmlns='http://www.w3.org/2000/svg'> 
  <clipPath id='emojiClipPath'> 
    <text x='0' y='130px' font-size='130px'>🦕</text> 
  </clipPath> 
  <text x='0' y='130px' font-size='130px' clip-path='url(#emojiClipPath)'>🦕</text>
</svg>

What’s happening here is we’re providing a <text> element with an emoji character for a <clipPath>. A clip path is an outline of a region to be kept visible when that clip path is applied to an element. In our code, that outline is the shape of the emoji character.

Then the emoji’s clip path is referenced by a <text> element carrying the same emoji character, using its clip-path property, creating a cutout in the shape of the emoji.

Now, we convert the SVG code to a DataURL. You can URL encode it by hand or use online tools (like this one!) that can do it for you.

Here’s the resulted DataURL, used as the url() value for the background image of an .emoji element in CSS:

.emoji {
  background: url("data:image/svg+xml,<svg width='150px' height='150px' xmlns='http://www.w3.org/2000/svg'> <clipPath id='emojiClipPath'> <text x='0' y='130px'  font-size='130px'>🦕</text> </clipPath> <text x='0' y='130px' font-size='130px' clip-path='url(%23emojiClipPath)'>🦕</text></svg>");
}

If we were to stop here and give the .emoji element dimensions, we’d see our character displayed as a background image:

Now let’s turn this into a CSS Shape

We can do this in two steps:

  • Float the element with the emoji background
  • Use the DataURL as the url() value for the element’s shape-outside property
.emoji {
  --image-url: url("data:image/svg+xml,<svg width='150px' height='150px' xmlns='http://www.w3.org/2000/svg'> <clipPath id='emojiClipPath'> <text x='0' y='130px'  font-size='130px'>🦕</text> </clipPath> <text x='0' y='130px'  font-size='130px' clip-path='url(#emojiClipPath)'>🦕</text></svg>");
  background: var(--image-url);
  float: left;
  height: 150px;
  shape-outside: var(--image-url);
  width: 150px;
  margin-left: -6px; 
}

We placed the DataURL in a custom property, --image-url, so we can easily refer it in both the background and the shape-outside properties without repeating that big ol’ string of encoded SVG multiple times.

Now, any inline content near the floated .emoji element will flow in the shape of the emoji. We can adjust things even further with margin or shape-margin to add space around the shape.

If you want a color-blocked emoji shape, you can do that by applying the clip path to a <rect> element in the SVG:

<svg width='150px' height='150px' xmlns='http://www.w3.org/2000/svg'> 
    <clipPath id='emojiClipPath'> 
        <text x='0' y='130px' font-size='130px'>🦕</text> 
    </clipPath> 
    <rect x='0' y='0' fill='green' width='150px' height='150px' clip-path='url(#emojiClipPath)'/> 
</svg>

The same technique will work with letters!

Just note that Firefox doesn’t always render the emoji shape. We can work around that by updating the SVG code.

<svg xmlns='http://www.w3.org/2000/svg' width='150px' height='150px'>
  <foreignObject width='150px' height='150px'>
    <div xmlns='http://www.w3.org/1999/xhtml' style='width:150px;height:150px;line-height:150px;text-align:center;color:transparent;text-shadow: 0 0 black;font-size:130px;'>🧗</div>
  </foreignObject>
</svg>

This creates a block-colored emoji shape by making the emoji transparent and giving it text-shadow with inline CSS. The <div> containing the emoji and inline CSS style is then inserted into a <foreignObject> element of SVG so the HTML <div> code can be used inside the SVG namespace. The rest of the code in this technique is same as the last one.

Now we need to center the shape

Since CSS Shapes can only be applied to floated elements, the text flows either to the right or left of the element depending on which side it’s floated. To center the element and the shape, we’ll do the following:

  • Split the emoji in half
  • Float the left-half of the emoji to the right, and the right-half to the left
  • Put both sides together!

One caveat to this strategy: if you’re using running sentences in the design, you’ll need to manually align the letters on both sides.

Here’s what we’re aiming to make:

First, we see the HTML for the left and right sides of the design. They are identical.

<div id="design">
  <p id="leftSide">A C G T A <!-- more characters --> C G T A C G T A C G T <span class="emoji"></span>A C G <!-- more characters --> C G T </p>
  <p id="rightSide">A C G T A <!-- more characters --> C G T A C G T A C G T <span class="emoji"></span>A C G <!-- more characters --> C G T </p>
</div>

p#leftSide and p#rightSide inside #design are arranged side-by-side in a grid.

#design {
  border-radius: 50%; /* A circle */
  box-shadow: 6px 6px 20px silver;
  display: grid; 
  grid: "1fr 1fr"; /* A grid with two columns */
  overflow: hidden;
  width: 400px; height: 400px;
}

Here’s the CSS for the emoji:

span.emoji {
  filter: drop-shadow(15px 15px 5px green);
  shape-margin: 10px;
  width: 75px; 
  height: 150px;
}

/* Left half of the emoji */
p#leftSide>span.emoji {
  --image-url:url("data:image/svg+xml,<svg width='150px' height='150px' xmlns='http://www.w3.org/2000/svg'> <clipPath id='emojiClipPath'> <text x='0' y='130px'  font-size='130px'>🦎</text> </clipPath> <rect x='0' y='0' width='150px' height='150px' clip-path='url(%23emojiClipPath)'/></svg>");
  background-image: var(--image-url);
  float: right;
  shape-outside: var(--image-url);
}

/* Right half of the emoji */
p#rightSide>span.emoji {
  --image-url:url("data:image/svg+xml,<svg width='150px' height='150px' xmlns='http://www.w3.org/2000/svg'> <clipPath id='emojiClipPath'> <text x='-75px' y='130px'  font-size='130px'>🦎</text> </clipPath> <rect x='0' y='0' width='150px' height='150px' clip-path='url(%23emojiClipPath)'/></svg>");
  background-image: var(--image-url);
  float: left;
  shape-outside: var(--image-url);
}

The width of the <span> elements that hold the emoji images (span.emoji) is 75px whereas the width of the SVG emoji images is 150px. This automatically crops the image in half when displayed inside the spans.

On the right side of the design, with the left-floated emoji (p#rightSide>span.emoji), we need to move the emoji halfway to the left to show the right-half, so the x value in the <text> in the DataURL is changed to 75px. That’s the only difference in the DataURLs from the left and right sides of the design.

Here’s that result once again:


That’s it! You can try the above method to center any CSS Shape as long as you can split the element up into two and put the halves back together with CSS.


Creating CSS Shapes with Emoji originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/creating-css-shapes-with-emoji/feed/ 3 323780
Changing Emoji Skin Tones Programmatically https://css-tricks.com/changing-emoji-skin-tones-programmatically/ Thu, 17 Sep 2020 14:52:14 +0000 https://css-tricks.com/?p=321233 So, you know how many emoji have different skin tones? Emoji skin tones are extremely popular, especially over text and on social media. The raised black fist emoji (✊🏿) was voted “The Most 2020 Emoji” by Emojipedia’s World Emoji Awards.…


Changing Emoji Skin Tones Programmatically originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
So, you know how many emoji have different skin tones? Emoji skin tones are extremely popular, especially over text and on social media. The raised black fist emoji (✊🏿) was voted “The Most 2020 Emoji” by Emojipedia’s World Emoji Awards.

Each tone is a modifier and many emoji are made up of modifiers and base encodings that map to specific characters. Unfortunately, not every emoji library supports modifiers. But, given their popularity, emoji skin tone modifiers are more than a “nice to have” feature. Plus, they’re a smart way to work because they allow us to write more modular and efficient code.

So that’s what we’re doing in this article: figure out how to work with emoji modifiers programmatically. This way, if you’re ever stuck without skin tone support — or want to create custom variations of other emoji — then you’ll know how!

Meet the Fitzpatrick scale

Skin tone modifiers were officially added to emoji in 2015 as part of Unicode 8.0. They are based on the Fitzpatrick scale, which is a formal classification of human skin tones. The following chart shows how the emoji characters match to Fitzpatrick types:

Skin tone characterFitzpatrick type
🏻1-2
🏼3
🏽4
🏾5
🏿6

In the simplest use case, when one of these characters is appended to an emoji that supports skin tone modifiers, it will change the skin tone of the emoji.

Another way to say that: 👶 +🏽 = 👶🏽

Applying skin tone modifiers with CSS

To swap between emoji skin tones using CSS, we would start with the base emoji character (👶) and then append the skin tone using the ::after pseudo-selector.

In addition to using the rendered emoji characters, we could use the Unicode hex codes instead:

Removing and swapping skin tone modifiers with JavaScript

What if the emoji you’re working with has already had a skin tone modifier applied? For that, we’ll need to move beyond CSS. Here’s an example using JavaScript:

What’s going on here? First, we start with a baby emoji with Fitzpatrick Type 4. We then pass it into the function removeModifier, which searches for any of the skin tone modifiers and removes it from the string. Now that we have the emoji without a modifier, we can add whichever modifier we like.

While this approach works with many emoji, we run into problems when other modifiers are introduced. That’s why we now need to talk about…

Working with ZWJ sequences

Zero width joiner (ZWJ) sequences are like the compound words of Unicode. They consist of two or more emoji joined by the zero width joiner, U+200D.

ZWJ sequences are most commonly used to add gender modifiers to emoji. For example, a person lifting weights, plus ZWJ, plus the female sign, equals a woman lifting weights (️🏋️ + ♀︎ = 🏋️‍♀️).

There’s a few important things to need to keep in mind when working with ZWJ sequences:

  • The sequences are only recommendations. They come from the Unicode Consortium and are not guaranteed to be supported on every platform. If they are not supported by a platform, then a fallback sequence of regular emoji will be displayed instead.
  • Skin tone modifiers, if present, must be included after the emoji but before the ZWJ.
  • Some ZWJ sequences include multiple emoji that each have different skin tone modifiers.

Given this information, we need to make the following changes to the previous code example:

  • The skin tone modifiers need to be inserted immediately after any base emoji rather than simply being appended to the end of the emoji.
  • If there are multiple emoji in a ZWJ sequence that have skin tone modifiers, then the modifiers will need to be replaced for each of those emoji.

Limitations

From this example, you may notice the limitation of consistency. The editor view shows each of the characters in a ZWJ sequence separately, with exception to the skin tone modifiers, which are immediately applied to their corresponding emoji. The console or results views, on the other hand, will attempt render the character for the entire sequence.

Support for this will vary by platform. Some editors may attempt to render ZWJ sequences, and not all browsers will support the same sets of ZWJ sequences.

Additionally, adding skin tones in a ZWJ sequence requires knowing what’s being used as the base emoji. While this would be relatively simple in a situation where the emoji are provided by a known collection, things become more difficult if we want to be able to handle arbitrary input from a user.

Also, be aware that the CSS solutions in this post are not compatible with ZWJ sequences.

Questions to guide development

I put some questions together you may want to ask yourself when you’re designing a system that needs to handle emoji skin tone modifiers:

  • Do I have control over which emoji my system will interact with?
  • Does my emoji library have information about which emoji support skin tone modifiers?
  • Does my system need to add, remove, or change the modifiers?
  • Does my platform support ZWJ sequences? If so, which ones?
  • Does my system need to support ZWJ sequences with multiple skin tone modifiers?

Hopefully, between the answers to these questions and the examples we’ve looked at here, you’ll have everything you need to support emoji skin tone modifiers in situations where you need them.


Changing Emoji Skin Tones Programmatically originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
321233
Excluding Emojis From Transparent Text Clipping https://css-tricks.com/excluding-emojis-from-transparent-text-clipping/ https://css-tricks.com/excluding-emojis-from-transparent-text-clipping/#comments Wed, 02 Sep 2020 18:22:04 +0000 https://css-tricks.com/?p=320156 CSS-Tricks has this pretty cool way of styling hovered links. By default, the text is a fairly common blue. But hover of the links, and they’re filled with a linear gradient.

😍

Pretty neat, right? And the trick isn’t all …


Excluding Emojis From Transparent Text Clipping originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
CSS-Tricks has this pretty cool way of styling hovered links. By default, the text is a fairly common blue. But hover of the links, and they’re filled with a linear gradient.

😍

Pretty neat, right? And the trick isn’t all that complicated. On hover…

  • give the link a linear gradient background,
  • clip the background to the text, and
  • give the text a transparent fill so the background shows through.

It looks like this in CSS:

a {
  color: #007db5;
}

a:hover {
  background: linear-gradient(90deg,#ff8a00,#e52e71);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

Notice the -webkit- prefix, which is required for now. There’s a little more to the actual implementation here on CSS-Tricks, but this little bit gets us what we’re looking for.

But that’s not the point here. Just the other day, Brad Westfall phoned in to let us know that this technique also takes effect on emojis which, like any other text, gets a transparent fill on hover.

He noticed it happening on a link in one of our posts.

Not the worst thing. And it totally makes sense. I mean, an emoji is a glyph like any other text in a font file, right? They just happen to be a color font and take on the form of an image. Of course they would be treated like any other glyph in a situation like this where we’re hallowing out the fill color.

But if keeping the color in tact on emojis is a requirement, that can be resolved by wrapping the emoji in a span and setting its fill back to its initial state.

But who wants to write a span every time an emoji happens to pop up in a link? 👎

If you’re looking for a CSS solution, we’re kinda out of luck. That said, the CSS Fonts Module Level 4 specification includes a definition for a proposed font-variation-emoji property. However, there’s not much on it (that I can find) at the moment and it doesn’t appear to be designed for this sort of thing, A quick skim of some discussion related to the proposal suggests it’s more about the way some browsers or systems automatically convert Unicode to emoji and how to control that behavior.

There’s also the proposed definition of font-palette in the same draft spec which seems like a way to control color fonts — that’s what emojis are at the end of the day. But this isn’t the solution, either.

It seems the only way to prevent an emoji’s fill from being changed without a span is some sort of JavaScript solution. Look at services like WordPress, Dropbox, Facebook and Twitter. They all implement their own custom emoji sets. And what do they use? Images.

Yeah, along with a lot of divs and such!

That would be one way to do it. If the emoji is replaced with an image (an SVG in this specific example), then that would certainly exclude it from being filled along with the link text.

Or, hey, why not prevent ourselves from getting into the situation at all and place that dang thing outside of the link?

That’s probably the route we should have taken all along. But an emoji might not come at the beginning or end of a link, but somewhere in the middle. It just underscores the point that there are cases where having some sort of control here could come in handy.


Excluding Emojis From Transparent Text Clipping originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/excluding-emojis-from-transparent-text-clipping/feed/ 6 320156
Weekly Platform News: Emoji String Length, Issues with Rounded Buttons, Bundled Exchanges https://css-tricks.com/weekly-platform-news-emoji-string-length-issues-with-rounded-buttons-bundled-exchanges/ https://css-tricks.com/weekly-platform-news-emoji-string-length-issues-with-rounded-buttons-bundled-exchanges/#comments Thu, 19 Sep 2019 18:29:42 +0000 https://css-tricks.com/?p=296196 In this week’s roundup, the string length of two emojis is not always equal, something to consider before making that rounded button, and we may have a new way to share web apps between devices, even when they are offline.…


Weekly Platform News: Emoji String Length, Issues with Rounded Buttons, Bundled Exchanges originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
In this week’s roundup, the string length of two emojis is not always equal, something to consider before making that rounded button, and we may have a new way to share web apps between devices, even when they are offline.

The JavaScript string length of emoji characters

A single rendered emoji can have a JavaScript string length of up to 7 if it contains additional Unicode scalar values that represent a skin tone modifier, gender specification, and multicolor rendering.

(via Henri Sivonen)

An accessibility issue with rounded buttons

Be aware that applying CSS border-radius to a


Weekly Platform News: Emoji String Length, Issues with Rounded Buttons, Bundled Exchanges originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/weekly-platform-news-emoji-string-length-issues-with-rounded-buttons-bundled-exchanges/feed/ 8 296196
Emojis as Icons https://css-tricks.com/emojis-as-icons/ Wed, 11 Jul 2018 19:07:51 +0000 http://css-tricks.com/?p=273843 There are lots of unicode symbols that make pretty good icons already, like arrows (←), marks (✘), and objects (✂︎).You can already colorize these like a normal font glyph. Then, there are emojis, those full-color suckers we all know about. …


Emojis as Icons originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
There are lots of unicode symbols that make pretty good icons already, like arrows (←), marks (✘), and objects (✂︎).You can already colorize these like a normal font glyph. Then, there are emojis, those full-color suckers we all know about. What if you could take just the shape of an emoji and use it like a normal glyph? You can!

Preethi Sam shows how:

.icon {
  color: transparent;
  text-shadow: 0 0 #ec2930;
}

Plus, an alternate technique using background-clip. Note that emojis render differently across platforms, so careful!

The original source for this has been moved and redirects to a completely different domain. It has been removed to prevent anyone from landing somewhere fishy.


Emojis as Icons originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
273843
It All Started With Emoji: Color Typography on the Web https://css-tricks.com/it-all-started-with-emoji-color-typography-on-the-web/ https://css-tricks.com/it-all-started-with-emoji-color-typography-on-the-web/#comments Tue, 15 May 2018 14:02:37 +0000 http://css-tricks.com/?p=270743

“Typography on the web is in single color: characters are either black or red, never black and red …Then emoji hit the scene, became part of Unicode, and therefore could be expressed by characters — or “glyphs” in font terminology.


It All Started With Emoji: Color Typography on the Web originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>

“Typography on the web is in single color: characters are either black or red, never black and red …Then emoji hit the scene, became part of Unicode, and therefore could be expressed by characters — or “glyphs” in font terminology. The smiley, levitating businessman and the infamous pile of poo became true siblings to letters, numbers and punctuation marks.”

Roel Nieskens

Using emojis in code is easy. Head over to emojipedia and copy and paste one in.

In HTML:

Or in CSS:

And JavaScript, too:

(Alternatively, you can specify emoji with a Unicode codepoint.)

However, you might run into a problem…

Lost in translation: Emoji’s consistency problem

The diversity of emoji across platforms might not sound like a major problem. However, these sometimes radical inconsistencies leave room for drastic miscommunication. Infamously, the “grinning face with smiling eyes” emoji ends up as a pained grimace on older Apple systems.

A row of ten yellow smiling emoji showing the difference in how different browsers and applications render the same character. Some are simple smiles and other are large grins that show off teeth.
This was such a big deal that even The Washington Post covered it.

A harmless and playful watergun emoji might show up as a deadly firearm.

A text message with two chat bubbles. The top bubble shows a watergun next to the text and the bottom bubble shows a handgun.
Courtesy of Emojipedia.

And who knows how many romances were dashed by Google’s utterly bizarre hairy heart emoji?

A row of four hearts in dots, beveled yellow, flat yellow and pink with hair strands, respectively.
🤮This has since been rectified by Google

Unicode standardizes what each emoji should represent with a terse description but the visual design is down to the various platforms.

Color fonts to the rescue!

The solution? Use an emoji font. Adobe has released a font called EmojiOne and Twitter open-sourced Twemoji. More are likely to follow.

@font-face {
  font-family: 'emoji';
  src: url('emojione-svg.woff2')
  format('woff2');
}

If a user types into an HTML input or textarea, they will see your fancy custom emoji. ❤️

An input in Firefox.

Emoji fonts also have the benefit of avoiding the pixelation seen in scaled-up raster emoji. If you happen to want really large emoji, an SVG-in-Opentype font is clearly the superior choice.

On the left, a standard Apple dogface emoji looking pixelated. On the right, smooth SVG-in-Opentype emoji characters from Emojione and Twemoji, respectively. Unicode (right) clearly doesn’t specify a breed!

Browser support

Confusingly, color fonts aren’t one standard but rather four 🙄. OpenType is the font format used on the web. When emoji were added to unicode, the big players realized that multi-color support needed to be added to OpenType in some way. Different companies came up with a diversity of solutions. The fonts are still .ttf, .woff or .woff2 — but internally they’re a bit different. I pieced together this support table using a tool called Chromacheck:

Chrome Safari Edge Firefox
SVG-in-Opentype
COLR/CPAL
SBIX
CBDT/CBLC

We’ve learned why color fonts were invented. But it’s not all about emoji…

Multicoloured alphabets

A screenshot of the Gilbert font using bright duotone colors on each character.
Gilbert font

Color fonts are a new technology, so you won’t find that many typefaces to choose from as of yet. If you want to try one out that’s free and open source, Bungee by David Jonathan Ross is a great choice.

While some fonts provide full emoji support and others offer a multicolor alphabet, Ten Mincho — a commercial font from Adobe — takes a different tack. In the words of its marketing material, the font holds “a little surprise tucked away in the glyphs.” Of the 2,666 emoji in the Unicode Standard, Ten Mincho offers a very limited range in a distinctive Japanese style.

The adorable custom emoji set of Ten Mincho

Emoji have become a predominant mode of human communication. Over 60 million emoji are used on Facebook every single day. On Messenger, the number is even more astonishing, at five billion per day. If you’re building any sort of messaging app, getting emoji right really matters.


It All Started With Emoji: Color Typography on the Web originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/it-all-started-with-emoji-color-typography-on-the-web/feed/ 1 270743