Facebook actually hides ‘dummy’ DOM nodes between the ‘Sponsored’ text. These values are entirely random characters, with a random number of DOM nodes between them. Invisible characters. At this point our CSS ad blocker is completely broken. There is no way for us to possibly code every possible value in CSS.
We’ve covered this before when Mike Pan noted it. Looks like it’s evolved a bit since then, getting even a little tricker.
I just opened my Facebook and selected “Copy Outer HTML” on the word “Sponsored”:
I guess we shouldn’t be terribly surprised at Facebook being user-hostile. I can imagine a workplace environment where fighting against ad blockers is turned into this fun kinda cat-and-mouse technological tennis match. But what they are fighting against is people wanting to exert a little control over what they allow into their eyes, ears, and brains.
It’s worth noting that nothing else in the DOM helps identify a post as an ad. So in that sense it’s just like how Google has evolved SERPs in how ads look just like organic results aside from a tiny “AD” before the URL.
We run sponsored posts here on CSS-Tricks too, so please feel free to hold our feet to the fire of accountability if you feel sponsored posts aren’t clear enough.
One Sunday morning, I woke up a little earlier than I would’ve liked to, thanks to the persistent buzzing of my phone. I reached out, tapped into Facebook Messenger, and joined the conversation. Pretty soon my attention went from the actual conversations to the funky gradient effect of the message bubbles containing them. Let me show you what I mean:
This is a new feature of Messenger, which allows you to choose a gradient instead of a plain color for the background of the chat messages. It’s currently available on the mobile application as well as Facebook’s site, but not yet on Messenger’s site. The gradient appears “fixed” so that chat bubbles appear to change background color as they scroll vertically.
I thought this looked like something that could be done in CSS, so… challenge accepted!
Let’s walk through my thought process as I attempted to recreate it and explain the CSS features that were used to make it work. Also, we’ll see how Facebook actually implemented it (spoiler alert: not the way I did) and how the two approaches compare.
Getting our hands dirty
First, let’s look at the example again to see what exactly it is that we’re trying to achieve here.
In general, we have a pretty standard messaging layout: messages are divided into bubbles going from top to bottom, ours on the right and the other people in the chat on the left. The ones on the left all have a gray background color, but the ones on the right look like they’re sharing the same fixed background gradient. That’s pretty much it!
Step 1: Set up the layout
This part is pretty simple: let’s arrange the messages in an ordered list and apply some basic CSS to make it look more like an actual messaging application:
<ol class="messages"> <li class="ours">Hi, babe!</li> <li class="ours">I have something for you.</li> <li>What is it?</li> <li class="ours">Just a little something.</li> <li>Johnny, it’s beautiful. Thank you. Can I try it on now?</li> <li class="ours">Sure, it’s yours.</li> <li>Wait right here.</li> <li>I’ll try it on right now.</li> </ol>
When it comes to dividing the messages to the left and the right, my knee-jerk reaction was to use floats. We could use float: left for messages on the left and float: right for messages on the right to have them stick to different edges. Then, we’d apply clear: both to on each message so they stack. But there’s a much more modern approach — flexbox!
We can use flexbox to stack the list items vertically with flex-direction: column and tell all the children to stick to the left edge (or “align the cross-start margin edges of the flex children with cross-start margin edges of the lines,” if you prefer the technical terms) with align-items: flex-start. Then, we can overwrite the align-items value for individual flex items by setting align-self: flex-end on them.
What, you mean you couldn’t visualize the code based on that? Fine, here’s how that looks:
The key clue here is mix-blend-mode, which is a CSS property that allows us to control how the content of an element blends in with what’s behind it. It’s a feature that has been present in Photoshop and other similar tools for a while, but is fairly new to the web. There’s an almanac entry for the property that explains all of its many possible values.
One of the values is screen: it takes the values of the pixels of the background and foreground, inverts them, multiplies them, and inverts them once more. This results in a color that is brighter than the original background color.
The description can seem a little confusing, but what it essentially means is that if the background is monochrome, wherever the background is black, the foreground pixels are shown fully and wherever it is white, white remains.
With mix-blend-mode: screen;</code on the foreground, we'll see more of the foreground as the background is darker.</figcaption></figure>
So, for our purposes, the background will be the chat window itself and the foreground will contain an element with the desired gradient set as the background that’s positioned over the background. Then, we apply the appropriate blend mode to the foreground element and restyle the background. We want the background to be black in places where we want the gradient to be shown and white in other places, so we’ll style the bubbles by giving them a plain black background and white text. Oh, and let’s remember to add <code>pointer-events: none
to the foreground element so the user can interact with the underlying text.
At this point, I also changed the original HTML a little. The entire chat is a wrapper in an additional container that allows the gradient to stay “fixed” over the scrollable part of the chat:
Now the gradient is being shown where the text bubbles are under it! However, we only want it to be shown over our bubbles — the ones along the right edge. A hint to how that can be achieved is hidden in MDN’s description of the mix-blend-mode property:
The mix-blend-mode CSS property sets how an element’s content should blend with the content of the element’s parent and the element’s background.
That’s right! The background. Of course, the effect only takes into account the HTML elements that are behind the current element and have a lower stack order. Fortunately, the stacking order of elements can easily be changed with the z-index property. So all we have to do is to give the chat bubbles on the left a higher z-index than that of the foreground element and they will be raised above it, outside of the influence of mix-blend-mode! Then we can style them however we want.
Let’s talk browser support
At the time of writing, mix-blend-mode is not supported at all in Internet Explorer and Edge. In those browsers, the gradient is laid over the whole chat and others’ bubbles appear on top of it, which is not an ideal solution.
This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.
Mobile / Tablet
So, this is what we get in unsupported browsers:
Fortunately, all the browsers that support mix-blend-mode also support CSS Feature Queries. Using them allows us to write fallback styles for unsupported browsers first and include the fancy effects for the browsers that support them. This way, even if a user can’t see the full effect, they can still see the whole chat and interact with it:
Here’s the final Pen with the full effect and fallback styles:
Turns out that Facebook’s solution is almost the opposite of what we’ve covered here. Instead of laying the gradient over the chat and cutting holes in it, they apply the gradient as a fixed background image to the whole chat. The chat itself is filled with a whole bunch of empty elements with white backgrounds and borders, except where the gradient should be visible.
The final HTML rendered by the Facebook Messenger React app is pretty verbose and hard to navigate, so I recreated a minimal example to demonstrate it. A lot of the empty HTML elements can be switched for pseudo-elements instead:
As you can see, the end result looks similar to the mix-blend-mode solution, but with a little bit of extra markup. Additionally, their approach provides more flexibility for rich content, like images and emojis . The mix-blend-mode approach doesn’t really work if the background is anything but monochrome and I haven’t been able to come up with a way to “raise” inner content above the gradient or get around this limitation in another way.
Because of this limitation, it’s wiser to use Facebook’s approach in an actual chat application. Still, our solution using mix-blend-mode showcases an interesting way to use one of the most under-appreciated CSS properties in modern web design and hopefully it has given you some ideas on what you could do with it!