Skip to content
academia.sh

Lesson 08 / 26

Inheritance and Default Values

How the value of a property no declaration reaches is determined; inherited and non-inherited properties, initial value, and the inherit, initial, unset, and revert keywords.

Contents

The cascade picks the winner among declarations that match an element. One problem remains: what happens if no declaration matches at all? If no rule writes color to a cell in the measurement table, what is that cell’s text color?

The gap is filled by two separate mechanisms. Some properties inherit their value from the parent element; the rest fall back to a defined initial value. This lesson separates the two mechanisms, gives the reason behind which property is subject to which one, and defines the keywords that call each of them explicitly.

Inheritance Works Down the Tree

Inheritance is a property’s value passing from parent element to child element down the document tree. Not every property inherits; which property inherits is part of the property’s definition, and it is not arbitrary.

Roughly: properties that determine the appearance of text inherit — color, font-family, font-size, font-weight, line-height, text-align, letter-spacing, visibility. Properties that determine the geometry of a box do not inherit — margin, padding, border, width, height, background, display, position.

The reason for the distinction is usefulness. Setting a section’s typeface once and having all the text inside it take it on is an ordinary thing to want; giving a section a border and wanting every paragraph inside it to also get a border is not. Inheritance is open in decisions where repetition is ordinary.

The inherited value is the parent element’s computed value, not the text written in the source. If a parent element has font-size: 150% written on it, what passes to the child is not the text 150%, but that percentage’s resolved result. This distinction is handled with numbers in the units lesson.

Initial Value Is Not the Browser Default

If no declaration reaches a non-inherited property, the initial value is used. This value is written into the property’s definition and is independent of implementation.

There is a distinction here that is commonly confused. A div element producing a block box does not come from the display property’s initial value — the display property’s initial value is inline. A div element being block is because the browser’s own default style file has div { display: block } written in it.

The distinction can be summarized as: the initial value comes from the language’s definition, the browser default comes from a style file. The first takes effect not when no rule is written, but when no rule wins; the second is a declaration capable of winning and, as the previous lesson showed, is the weakest source in the cascade.

Running Value Resolution

The following program takes declarations that won the cascade as input and walks down the tree, resolving each element’s value for four properties. Each line ends with where the value came from.

// inheritance.mjs — runs value resolution down the tree
const INHERITED = new Set(["color", "font-family", "line-height", "text-align", "visibility"]);
const INITIAL = {
  "color": "canvastext", "font-family": "serif", "line-height": "normal",
  "text-align": "start", "visibility": "visible", "border-color": "currentcolor",
  "background-color": "transparent",
};

// winning declarations (cascade result) — node path -> {property: value}
const winners = {
  "body":                         { "color": "#1c2733", "font-family": "system-ui" },
  "body/main":                    {},
  "body/main/table":              { "text-align": "right", "border-color": "#d5dbe0" },
  "body/main/table/tr":           {},
  "body/main/table/tr/td.name":   { "text-align": "start" },
  "body/main/table/tr/td.missing":{ "color": "inherit", "text-align": "unset",
                                     "border-color": "unset", "font-family": "initial" },
};

const tree = { name: "body", children: [ { name: "main", children: [ { name: "table", children: [
  { name: "tr", children: [ { name: "td.name", children: [] }, { name: "td.missing", children: [] } ] },
] } ] } ] };

const PROPERTIES = ["color", "font-family", "text-align", "border-color"];

function resolve(node, path, parentValues) {
  const fullPath = path ? `${path}/${node.name}` : node.name;
  const written = winners[fullPath] ?? {};
  const values = {};
  for (const prop of PROPERTIES) {
    let v = written[prop];
    let source;
    if (v === undefined) {
      // no declaration: take from parent if inherited, else initial value
      if (INHERITED.has(prop) && parentValues) { v = parentValues[prop]; source = "inherited"; }
      else { v = INITIAL[prop]; source = "initial"; }
    } else if (v === "inherit") {
      v = parentValues ? parentValues[prop] : INITIAL[prop]; source = "inherit";
    } else if (v === "initial") {
      v = INITIAL[prop]; source = "initial-keyword";
    } else if (v === "unset") {
      if (INHERITED.has(prop) && parentValues) { v = parentValues[prop]; source = "unset->inherited"; }
      else { v = INITIAL[prop]; source = "unset->initial"; }
    } else {
      source = "declared";
    }
    values[prop] = v;
    console.log(`  ${prop.padEnd(12)} = ${String(v).padEnd(12)} (${source})`);
  }
  for (const c of node.children) {
    console.log(`${fullPath}/${c.name}`);
    resolve(c, fullPath, values);
  }
}

console.log("body");
resolve(tree, "", null);
body
  color        = #1c2733      (declared)
  font-family  = system-ui    (declared)
  text-align   = start        (initial)
  border-color = currentcolor (initial)
body/main
  color        = #1c2733      (inherited)
  font-family  = system-ui    (inherited)
  text-align   = start        (inherited)
  border-color = currentcolor (initial)
body/main/table
  color        = #1c2733      (inherited)
  font-family  = system-ui    (inherited)
  text-align   = right        (declared)
  border-color = #d5dbe0      (declared)
body/main/table/tr
  color        = #1c2733      (inherited)
  font-family  = system-ui    (inherited)
  text-align   = right        (inherited)
  border-color = currentcolor (initial)
body/main/table/tr/td.name
  color        = #1c2733      (inherited)
  font-family  = system-ui    (inherited)
  text-align   = start        (declared)
  border-color = currentcolor (initial)
body/main/table/tr/td.missing
  color        = #1c2733      (inherit)
  font-family  = serif        (initial-keyword)
  text-align   = right        (unset->inherited)
  border-color = currentcolor (unset->initial)

Three observations follow from the output.

color and font-family were only declared on the body element, and carried down five generations to the cells. A declaration written at the root of the tree affects the entire subtree.

border-color was #d5dbe0 at the table but dropped back to currentcolor at the row. Border color is not an inherited property; a border color given to the table does not pass to the cells. This is why setting up a border on a table requires writing a rule to the cells as well.

text-align became right at the table and inherited down to the row and cells; a declaration on the td.name element overrode it to start. Inheritance does not hold once there is a declaration on the child element — inheritance is the weakest source and is always overridden by a declaration.

Four Keywords

There are four keywords that can be written to any property’s value. The output’s last block shows three of the four.

inherit — forces even a non-inherited property to be taken from the parent element. color: inherit was written on td.missing, and the value came from above. This is meaningful on a non-inherited property; writing border-color: inherit is a way to pull the border color from above.

initial — returns to the property’s initial value. In the output, the cell with font-family: initial dropped to the value serif; the inheritance of system-ui from above was cut. This result is often not the one desired: initial goes back to the language’s definition, not to the page’s design.

unset — behaves conditionally: if the property is an inherited one, it acts like inherit; if not, like initial. In the output, text-align: unset dropped to inheritance, border-color: unset to the initial value. This is the answer to the question “what would this be if this declaration had never been written at all.”

revert — is the fourth, and answers a different question: it returns the value to what a weaker source would have won. Writing revert in author style means going back, on that property, to the browser default. The difference from initial shows up here: writing color: initial on a link takes the color to the language’s initial value, while color: revert takes it to the color the browser has defined for links.

All four can be written to every property at once with the all shorthand. all: revert isolates an element from author style; all: unset keeps inherited properties and resets the rest.

Value Stages

The term “computed value,” which came up in this section, needs to be defined. A declaration’s value goes through several stages from what is written to what ends up on screen:

Stage What happens
declared value The text written in the source
cascaded value The declaration that won the cascade
specified value If there is no winner, filled in with inheritance or initial value
computed value Relative units resolved, the form passed on by inheritance
used value The final form once layout has been performed

The stage carried by inheritance is the computed value. If a parent element has font-size: 1.5em written on it, the child does not inherit the text 1.5em, it inherits that expression’s resolved result — otherwise it would be multiplied again at every generation, and the font size would grow exponentially.

The used-value stage, on the other hand, waits for layout. In the declaration width: 50%, the computed value is still a percentage; the exact number only emerges once the containing box’s width is known. This stage will be covered in detail in the box model topic.

Using Inheritance in the Station Page

/* station.css — step 7: using inheritance as a base */
body {
  color: #1c2733;
  font-family: system-ui, sans-serif;
  line-height: 1.5;
}

/* form controls do not inherit typography by default */
input, select, textarea, button {
  font: inherit;
  color: inherit;
}

.measurement-table { border-collapse: collapse; }
.measurement-table th,
.measurement-table td { border-block-end: 1px solid #d5dbe0; }

The second rule closes an exception. Form controls do not take their typography from the document; they carry their own default typefaces. Writing font: inherit removes this exception and ties the controls to the document’s typography.

The third group shows that, because border color is not inherited, the border has to be written to the cells separately — the direct consequence of the behavior seen in the output’s fourth block.

Summary

  • A property either inherits or, if there is no declaration, falls back to its initial value; which applies is written in the property’s definition, and text properties inherit while box-geometry properties do not.
  • The initial value comes from the language’s definition; the browser’s default appearance comes from a style file and is the weakest source in the cascade. display: block is not an initial value, it is a declaration written in that file.
  • Inheritance is the weakest source: if there is a declaration on the child element for that property, inheritance does not apply.
  • inherit forces the value from above, initial returns to the language’s initial value, unset picks between the two based on the property’s kind, revert returns to the value of a weaker source.
  • What inheritance carries is the computed value; relative units are resolved before being carried, otherwise they would be multiplied again at every generation.

Next Step

Throughout this topic, which elements rules reach and which value wins have been resolved. The value is settled, but what that value means has not been established yet. Where on a cell does padding: 8px add eight pixels? What is the total width of a box given a width? The next topic defines the model every element turns into a box, and works through that box’s sections with arithmetic.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close