Nested Opacity Inheritance in QML and CSS – How does this work again?

  • infoFull Post Details
    info_outlineClick for Full Post Details
    Date Posted:
    Feb. 22, 2019
    Last Updated:
    Feb. 22, 2019
  • classTags
    classClick for Tags
    Tags:

The Problem:

I’m brand new to QML, and so when I started on my first project and couldn’t get nested opacity inheritance to work like I was expecting it to, I immediately blamed QML and thought “this would be so much easier with CSS”. However, after reflecting for a second and firing up a Codepen to test, I realized I was wrong, and I had incorrectly remembered how CSS handles the opacity property in nested elements (hint, it is the same as QML in this case).

The problem is pretty basic; in both CSS and QML, if you nest a component (CSS people, think “element”), the child element ends up somewhat inheriting the opacity of the parent, to the effect that the child can not have an opacity that is more than the parents opacity. In practice, this means that by only manipulating the opacity property, a nested component can be less opaque than the parent, but not more opaque. Here is a super simple example:

Works as expected: Nested element opacity = 0.5, parent element opacity = 1.0

CSS - Child Opacity Less Than Parent - Blending Example

Doesn’t work as expected: Nested element opacity = 1.0, parent element opacity = 0.5

CSS - Child Opacity Greater Than Parent - Parent Overriding Child

You can see in the example above where the nested component has an opacity of 1, you might think that the child should appear with completely solid background, while the parent has a transparent red background. However, due to the way that both CSS and QML handle blending, relative values, rendering nodes, and other technicalities beyond the scope of this post, the practical result is that, in actuality, the child ends up with an opacity capped at that of the parent, so both appear as 0.5.

Here is a fun little demo I set up to show how this works with CSS. Use the slider to change the opacity, and the button to swap which elements are opaque.

Another way to think of it that might make more sense is by referring to opacity as a relative percentage – in this case, if the parent is set to 50% opacity, and the child is set to 100%, 100% of the parent (50%) is still just 50%, so both end up with the same value.

You could also think of a piece of paper with two squares printed on it, one is half as “bright” as the other. If you submerge the whole paper in a solution that fades it to be half as visible, now both squares are half as visible as they were before (50%) and one square is still half as “bright” relative to the other, which means it really is 25% visible (0.5 * 0.5).


The Solution:

One of the easiest solutions, in both CSS and QT / QML, is to just avoid nesting when you can. Here is a comparison showing how you can have a component appear within another with a higher opacity when it is really a sibling, vs when it is a child:

CSS and QML - Nested Opacity vs Sibling Opacity - Simple Example

But what if, understandably, you really want to leave you components in a nested structure, but still get nested opacity to work? Well, there is a few options, but the easiest, and my favorite, is one that works for both CSS and QML – simply use RGBA (RGB-ALPHA) instead of opacity! This is actually the solution that I use throughout this website, and I use it so often that I forgot I do it, and is the reason why when I started using opacity instead of RGBA in QT I had to remind myself of why I’m always using it.

Here is how you might use RGBA to display the solid green rectangle inside the transparent red one, like above, with CSS (codepen link):

CSS - Using RGBA for Nested Opacity in Elements - Quick Example

Or with QML (full code repo here):

 

QML - Using RGBA for Nested Opacity in Components - Quick Example


A cool thing in QML – Reusing colors with RGBA

One thing that is very neat about QML that you can’t really do in CSS (at least not in pure CSS, I’m not talking about Sass or JSX) is referencing values from other components. A cool application of this in combination with RGBA is telling QML to use the same color as another component, but fade it a bit. For example, this is a great way to achieve a nice glow effect around an element without having to keep hardcoding in colors. This also allows you to keep the same opacity in a nested component while allowing for a dynamic color (e.g. if the component color is bound to a C++ value).

QML - Using Reference Component Color for RGBA Opacity Effect - Example


Further options in QML:

QML has some additional options for altering how inherited opacity works, such as the “layer.enabled” property, or shader effects. However, at least in my view, these are all much more complicated and with potential side effects that far eclipse the simple implementation of RGBA.

Another quick solution similar to RGBA though, is use a quad color code.

Leave a Reply

Your email address will not be published. Required fields are marked *