Search code examples
htmlcssspecifications

Are all CSS rules allowed on all HTML elements and can they all be inherited?


Given that certain CSS rules are tailored to specific HTML elements (some examples - object-fit for <img>, <video>, etc.; stroke, fill and other SVG rules for various SVG elements) and do not quite make sense when used on other elements:

  1. is it legal to apply any valid CSS rule to any HTML element?
  2. can any rule be inherited?
  3. what is the inherited value, particularly in cases when it's inherited from an element where using the specific property would not make sense?

My specific problem is this. I am developing a button component with modular CSS. Inside this button I may have an SVG element for displaying an icon. I want to have the ability to apply certain styles to the icons (namely, stroke and fill) from outside the component whilst retaining proper encapsulation (so deep selectors are a no-no).

To solve the problem I set the SVG to inherit stroke and fill from the button. If I now apply these properties to the button, the icon inherits them and I get the desired result!

However, this got me wondering whether what I am doing is valid and whether other browsers would behave the same.

This may seem common-sense but a definite answer is surprisingly difficult to find. If someone could point me to an excerpt from the spec and perhaps even to some support tables, I'd be happy!


Showing a 100% accurate example is a bit too much given how I write my code, but basically I am asking if this is valid:

.icon {
  stroke: inherit;
  fill: inherit;
}

.button {
  stroke: #FFF;
  fill: #999;
}
<button class="button">
  <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18">
    <g stroke-width="2" transform="translate(1 1)">
      <path d="M14 2L2 14"/>
      <circle cx="8" cy="8" r="8"/>
    </g>
  </svg>
  <span>My button</span>
</button>


Solution

  • Temani's answer was very helpful and pointed me in the right direction. After a bit of research I think I can now post a more elaborate and properly referenced answer to my own question.

    Is it legal to apply any valid CSS rule to any HTML element?

    Yes, it is. This is stated quite clearly in the CSS2.1 spec: https://www.w3.org/TR/CSS2/about.html#applies-to (CSS3 is a module-based backwards-compatible extension of CSS2.1, so this still applies). It says:

    All elements are considered to have all properties, but some properties have no rendering effect on some types of elements. For example, the 'clear' property only affects block-level elements.

    All elements are considered to have all properties implicitly means that any property can be set on any element.

    Some properties have no rendering effect on some elements means that some elements simply ignore certain properties when rendering (this does not mean that they can't be inherited, read on).

    Can any rule be inherited?

    To answer this question, let's look at the CSS Values and Units Module Level 3 spec: https://www.w3.org/TR/css3-values/#common-keywords. It states that:

    All CSS properties also accept the CSS-wide keyword values as the sole component of their property value.

    There are three CSS-wide keyword values: initial, inherit and unset.

    The spec is essentially saying that any property can use inherit as its value, thus the answer to the question is yes. The important detail, however, is how the inherited value is determined. As Temani has already pointed out, it's not as straightforward as it may seem.

    How are inherited values computed?

    A great read on cascading and inheritance is the CSS Cascading and Inheritance Level 3 spec: https://www.w3.org/TR/css-cascade-3/. It states that:

    The inherited value of a property on an element is the computed value of the property on the element’s parent element.

    Based on the spec, here's my simplified version of the process of determining the computed value for any property on any element:

    1. Collect all the declared values for a property on an element - i.e. all the relevant and valid CSS declarations found in developer stylesheets and style attributes, user agent stylesheets, etc.

    2. Determine the cascaded value - the winning value of all the declared values (this is where rules are prioritised and a single one comes out on top).

    3. If there is no cascaded value, use the inherited value if the property is inherited.

    4. If the property is not inherited (see this question for a list of properties that are inherited by default: Which CSS properties are inherited?), use the initial value. Each and every property has an initial value according to the spec (e.g. the initial font-size is medium; worth noting that it is not an absolute value like 16px!).

    5. At this point a property will have some value assigned to it, but it may be a relative value, i.e. dependant on some other CSS properties (e.g. a percentage value depends on some absolute value, such as the parent's absolute dimensions). At this step a value will be resolved as far as possible so that it is unambiguous and ready for inheritance.

      It is important to note that just like the spec defined an initial value for every property, it also defines how to derive the computed value. E.g. MDN specified this computed value for font-size:

    as specified, but with relative lengths converted into absolute lengths

    So medium remains medium, but 1.2em becomes, say 19.2px (if the parent's font size is 16px).

    The important bit

    The same Cascading and Inheritance spec points out that:

    The computed value exists even when the property does not apply (as defined by the “Applies To” line). However, some properties may change how they determine the computed value based on whether the property applies to the element.

    So every element will have a computed value for every property, but may not use it (the so-called used value, which is the next step after computed value, is none). The computed value is what will be inherited.

    What all this means for my SVG button example:

    1. I am definitely allowed to set stroke and fill on a <button>. It will ignore this property when rendering itself, but will pass it on to its children, if required.
    2. The child SVG will inherit these properties by default (I don't even need to explicitly specify that).
    3. The computed values for stroke and fill do not have any caveats so I can expect the correct colour to be inherited (and it is!).