Hidden dependencies
Sarah Lim, Northwestern University
Update, September 2019: This Gist detailed an early idea which formed the basis for a major research project. For more information, you can read the resulting full paper, which received Best Paper Honorable Mention at UIST 2018. A tool based on this research is now available on Firefox Nightly.
What are hidden dependencies?
Hidden dependencies between CSS properties are a common source of beginner confusion. For instance, a user might write the following code in order to vertically align some text within a <div>
:
<div class="container">
Here is some text, which should be vertically aligned.
</div>
.container {
height: 100px; /* larger than the auto text height */
vertical-align: middle;
}
Unfortunately, writing vertical-align: middle;
doesn't do anything: the page remains unchanged, with or without this property. However, adding the hidden dependency display: table-cell;
makes the page appear as expected:
.container {
height: 100px;
vertical-align: middle;
display: table-cell; /* now the text is vertically centered */
}
This solution isn't necessarily the best way to vertically align text, but it does illustrate a common pitfall for CSS beginners: hidden dependencies between properties on a particular element (and sometimes two different elements, such as a parent and child).
To make matters worse, many properties with hidden dependencies have descriptive names, such as vertical-align
, z-index
, justify-content
, width
. These implied visual effects lull beginners into a false sense of security: one might reasonably conclude that setting width: 50%;
will resize a box of text, only to write
<span class="text">Resize me to 50%!</span>
.text {
width: 50%;
}
and experience frustration when the CSS has no effect. Here, our problem is that <span>
tags have a default computed value of display: inline;
, thus width
has no effect. Stated otherwise, there is a hidden dependency between the width
property and a value of display: block | inline-block;
(or the element could also be replaced, but that's a separate case).
While experienced developers have internalized common hidden dependencies through years of trial-and-error, acquiring this knowledge is daunting to beginners: the spec is rarely useful for gaining intuition into the 80% of cases, and online wisdom tends to be dispersed across disjoint sources. In the meantime, beginners struggle to construct accurate mental models for basic CSS functionality, often chalking up styling to "wizardry" or "hacks" (which is a fair assessment in many cases, but not all of them).
Current tools offer little support to developers debugging ineffective CSS. Our research aims to improve this situation, by developing techniques and interfaces for inferring and presenting hidden dependencies. (For those curious, you can read about our core mechanism, visual regression pruning. We've since made significant progress generalizing the technique to more interesting applications, but the core concept remains the same.)
As one example of how such blue-sky tooling might work, imagine a beginning CSS developer who wants to stack two elements in an overlapping fashion.
- They Google "stack elements CSS," and see
z-index
in the title of a top result. They vaguely recall seeingz-index: -1;
declared somewhere before, so they go back to their editor and add that line. - The page output does not change.
- Detecting a file edit with no consequence, two things happen:
- The editor analyzes the change and detects the
"z-index"
property was added, with a grammatically valid value. - The browser looks at the computed styles for the selected element, and notices that the requisite dependency
position: absolute | relative | fixed | sticky;
is not satisfied.
- The editor analyzes the change and detects the
- In conjunction, the tools suggest adding
position: relative;
to the source code.
While we aren't there yet, this kind of supportive tooling is exactly what our research aims to provide.
Common hidden dependencies
What follows is a very incomplete list of common hidden dependencies. Each corresponds to an empirical learning barrier encountered by multiple novice CSS developers in our lab studies. We have chosen to focus on this subset of examples while prototyping tools for making dependencies explicit, because they correspond to frequently-desired styling outcomes in practice.
You can help in two ways:
- Suggest new dependencies, with a particular focus on those which have tripped you up personally sometime over your career, or those you've encountered with reasonable frequency (e.g. on StackOverflow, while mentoring interns, etc.).
- In this stage of our research, we are particularly interested in hidden dependencies for properties unrelated to layout or the box model. That is, which properties alter the visual appearance of an element without changing its size, position, etc., and what are some hidden dependencies for those properties?
- Provide corrections and elaborations to the listed dependencies, many of which are simplified to convey general ideas, leaving out edge cases (or designating them as such).
z-index
depends on ONE of:position: absolute | relative | fixed | sticky;
applied to the same elementdisplay: flex;
applied to the parent element
top
depends on:position: absolute | relative | fixed | sticky;
applied to the same element
left
depends on:position: absolute | relative | fixed | sticky;
applied to the same element- Edge cases:
- If
right
is specified:direction: ltr;
applied to the same element
- If
bottom
depends on:position: absolute | relative | fixed | sticky;
applied to the same element- Edge cases:
- If
top
is specified:height: auto | 100%;
applied to the same element
- If
right
depends on:position: absolute | relative | fixed | sticky;
applied to the same element- Edge cases:
- If
left
is specified:direction: rtl;
applied to the same element
- If
Corollary: If an element has
position: relative;
and no other dependent positioning modifiers (top
,left
,z-index
, etc.), it very likely serves as a positioned ancestor for another positioned descendant.
position: sticky;
requiresposition: relative;
and thus does not apply to<thead>
or<tr>
, only<th>
(source)
vertical-align
depends on:display: table-cell;
applied to the same element- UNLESS
display: flex;
is applied to the parent element
margin: 0 auto;
(to center an element) depends on:- NOT having
float: left | right;
applied to the same element - NOT having
display: flex | inline-flex;
applied to the same element- This is a slightly more complex edge case, omitted for simplicity
- NOT having
flex-direction, flex-row, flex-wrap, justify-content, align-content, align-items
depend on:display: flex | inline-flex;
applied to the same element
flex, flex-grow, flex-shrink, align-self, order
depend on:display: flex | inline-flex;
applied to the parent element
height
depends on:- NOT having
min-height
applied to the same element - NOT having
max-height
applied to the same element - NOT being
display: inline | table-*;
(this is a simplification, to avoid explaining non-replaced elements) - If percentage value:
height
applied to the parent element (or the root element, ifposition: absolute;
is applied to the same element)
- NOT having
width
depends on:- NOT having
min-width
applied to the same element - NOT having
max-width
applied to the same element - NOT being
display: inline | table-*;
(this is a simplification, to avoid explaining non-replaced elements)
- NOT having
- (from Josh Comeau)
margin-bottom
andmargin-top
depend on:display: block | inline-block;
applied to the same element
- (from Emily Eisenberg)
text-overflow: ellipsis
depends onwidth
constrained (in pixels), and evaluated (i.e.display: inline-block | block
also set)overflow: hidden
applied to the same elementwhite-space: nowrap
applied to the same element
willChange: transform
can break z-ordering/parenting (from Kevin Barabash)position: absolute
depends onposition
of parent element/(nearest positioned ancestor) (from Kevin Barabash)transform
can breakz-index
,position: fixed
(from Josh Comeau)overflow
,overflow-x
,overflow-y
(from Michail Yasonik)background-attachment
,background-repeat
andbackground-position
depend on abackground-image
(from Michail Yasonik)- CSS Grid (from Michail Yasonik)
- Ply: A Visual Web Inspector for Learningfrom Professional Webpages documents further research into implicit dependencies. I also gave a talk on this work (implicit dependencies start at 6:59).
- Firefox Nightly is adding a tool based on this research.
The behavior of
position: absolute
depends on the what theposition
of the parent is, e.g. if the parent isposition: relative
than the child will be absolutely positioned relative to the container instead of the page.