Frequently Asked Questions

Styling <sup> And <sub> Using font-variant-position


HTML currently specifies the following default styles for sup and sub:

sub { vertical-align: sub; }
sup { vertical-align: super; }
sub, sup { line-height: normal; font-size: smaller; }

This works, but this is not very good typography. The following uses fonts the way they're meant to be, wouldn't it be much better?

sub { font-variant-position: sub; }
sup { font-variant-position: sup; }


It is indeed better typography, and even if the font does not have the dedicated glyphs, the browser is required to synthesize them, so this is look encouraging. However, we cannot switch the default rendering to that. It has issues in terms of compatibility, and more importantly, it does not handle all cases, due the possibility of nested sub/sup. While these are not overly common it does exist, and this style change would induce a rendering with an apparently different meaning.

Consider the following:

2<sup>2<sup>2</sup></sup> = 16

Even if the typography isn't great, This correctly looks like 2^2^2 = 16 with the legacy styling, but would look like 2^22=16 with the proposed change, which is wrong.

More details

There were various attempts to define font-variant-position differently to make it handle such situations, but they were rejected for being either too complex, not solving the problem correctly, or both.

The following code comes reasonably close to giving good typography in the base case, and handling some cases of nesting as well, so web page authors may want to use it if it works for their content, but was not judged sufficiently robust in the general case to be accepted as a new default styling, in part because fonts with inaccurate metrics (which are unfortunately reasonably common) may break it, and in part because it does not handle images and other non textual content in the sub/superscripts.

sub { font-variant-position: sub; }
sup { font-variant-position: super; }
:matches(sub, sup) :matches(sub, sup) {  font-size: smaller; }
/* Not using :matches() on the parent in the following 2 rules is intentional,
   it would shift too much. */
sub sub { vertical-align: sub; }
sup sup { vertical-align: super; }


Selectors that Depend on Layout


  • I would like an :overflowing pseudo class to select elements which overflow
  • I would like a :stuck pseudo class to select elements with position:sticky which are currently stuck
  • I would like a :on-screen pseudo class to select elements which are currently in the viewport


This falls into a class of problems that unlikely to be solvable in CSS: selectors in general, and pseudo classes in particular, cannot depend on layout, because otherwise they could be used to modify layout in a way that made them no longer match, which would modify the layout back to where it was, so they match again, and we get stuck in an infinite loop of contradictions.

For a simple example:

:stuck { position: static; }

Now what?

Some of the changes web developers might want to apply with a :stuck pseudo class may be safe and not trigger such loops, but selectors are a generic mechanism, and would enable this kind of contradictions.

So even though many of the problem people are trying to address using such pseudo classes are legitimate, selectors are unlikely to be the answer.

More details

It may seem that we could simply disallow/make invalid anything that could cause an infinite loop, but that doesn't actually solve the problem.

Say we did this, and explicitly disallowed setting position — as well as other properties that can change the position of the element and therefore whether it gets stuck or not, such as display, margin, float, clear, flex… — in a rule targeting a :stuck element. This is already more restrictive than many potential users of :stuck would be happy with, but this works for this case.

Next week, someone comes up with a similar circularity they want to allow (like the Toggle States proposal, which has properties interacting with :checked). We disallow that circularity too.

Now someone writes

.foo { toggle-states: 2; toggle-initial: 1; } /* makes it checked */
:checked { position: sticky; }
:stuck { toggle-states: none; }

When it's checked, it becomes sticky. (Assume the page is scrolled such that it becomes stuck immediately.) When it's stuck, it becomes uncheckable, which means it's no longer sticky, so it's not stuck, so it goes back to being checkable, and so it's checked, and so it's sticky, and so it's stuck, and so it's uncheckable…

If you add a third selector/property pair, the number of cycles you need to manage gets even larger.

The only way around this is to define that *none* of the properties that affect selectors can be used in rules using *any* of the selectors affected by properties. That ends up with a lot of confusing action-at-a-distance: it's weird that using :checked means that you can't set position: sticky any more.

Worse than being confusing, this is also breaking compatibility. Setting position:sticky inside of :checked used to be valid, but adding that other new feature means it would no longer be, so we cannot do that. In effect, if we ever add a selector that can depend on properties, then we can never add another selector that would depend on other properties ever. Which one goes first? Something that we can only do once, ever, and that will affect our ability to evolve CSS in the future, is probably a bad idea for the language.

Yet another way you could try to remediate all this would be to do a run-time detection of whether there is a loop in that particular page and disable all the selectors (or properties) involved in that loop. However, this check would have a performance impact, would still have the confusing and hard to debug “action at a distance” problem, on top of which it would require significant changes in browser architecture.

Instead of doing all of this, so far we've just short-circuited the entire debate and disallowed selectors from being affected by properties.

Why Doesn't This Argument Apply To :hover?

A common retort to the above is “we already have :hover, which has circularity issues, why can't we add this?”.

First, the fact that we've made one mistake isn't an argument for repeating the mistake. :hover *is* problematic in implementations, and we'd prefer not to add more things like it.

Second, and more important, the circularity of :hover is very “wide” - when you apply :hover rules, you get all the way thru styling, then layout, then painting, and finally to hit-testing before you realize the element isn't hovered anymore and have to go re-style. The user gets to at least see the full results of hovering before the engine has to figure things out again. This is different from most other circular pseudo-classes, which circle around after just styling or layout, before the old results are even presented to the user. This would effectively freeze the page, as the layout engine isn't even allowed to complete a single full run before it gets restarted.

Furthermore, hit-testing is easy to for UAs to “tweak” to mostly get around the hover problems - UAs generally don't rerun hit-testing until the user moves their pointer, so as soon as they stop moving it, it settles on one rendering or the other. Again, this isn't the case for other circular pseudo-classes.


Versioning CSS, Fixing Design Mistakes


  • There are quite a few things in CSS that are considered design mistakes, and that would be done differently if they were being designed to today. Can't we just fix these?
  • CSS is terrible; we need to change FOO, BAR and BLAH, to work differently.
  • Can we introduce a @css 3; or @css 3 { } rule to instruct the browser to switch to a different processing model? The old one has lots of flaws.


CSS is a widely successful technology so it's probably not that bad, but there have certainly been a number of decisions that in hindsight were wrong. Unfortunately, we are mostly stuck with these.

The point of a web browser is not to have a beautiful architecture, it is to browse the web as it exists, not as we would like it to be. Whether authors correctly used well-designed features, correctly used poorly-designed features, used features in creative or weird ways, or even accidentally depended on some bizarre behavior, is mostly irrelevant. Sites that work today need to continue to work.

For all the web's quirks, the fact that new web pages can work in old browsers (with some graceful degradation) and old web pages work in new browsers is a huge strength of the web, even if it does have the downside that we have to live with the mistakes of the past.

Occasionally, it is possible to make breaking change, when the problem it solves is major, and the breakage is very limited. But that's the exception, not the rule, and it is very hard to do, because nobody is the boss of the web and can order everyone to update. Unless everybody is convinced that breaking existing sites is a good idea, the change will not happen.

More details

Occasionally, people accept that the current model cannot be changed, but suggest that we could introduce a different model if we told the browser about it using some versioning scheme, like a @css: 3; or @css 3 { } rule.

Things along this line have been suggested and considered multiple times over the years, but ultimately rejected, as that would not work.

Any page without @css: 3; will be interpreted as css21. But initially, so will any page that has @css: 3, because browsers that don't know about it (currently all of them) will just drop this line, an render the page as usual, and so even browsers who would want to use special css3 semantics would have to render it using css21 rules, otherwise the pages would work differently in different browsers, which would be terrible.

We could instead have @css3 { /*put your stylesheet with new semantics here*/ }, as that would be ignored by old browsers, but even then every (new) browser would have to support both the old way and the new way forever, which is quite costly. For authors, it would be costly as well, as they would have to write their stylesheet twice: once using @css3{} for new browsers and once without it for old ones.

Even though everybody recognizes that some decisions made in the past are not ideal and that this causes some pain, the pain is not nearly enough to justify making everybody do twice the work.


Error Handling in Selectors, aka Breaking Pages by Making Them Work


When a selector has a syntax error in it, or when it has new syntax that old browsers don't know about, the entire selector is dropped. CSS would be more robust and maintainable if the browser only dropped to the next comma.

Take this code:

#sensible .selector,
syntax = !error,
:new-feature {
  background: red;

Because of how selectors are parsed, this is not applied at all. Wouldn't it be nicer if it could apply to elements that match #sensible .selector, also apply in up to date browsers to elements that match :new-feature, and just skip the syntax error?


Broadly speaking, this is impossible for the reasons explained in Versioning CSS, Fixing Design Mistakes: changing how CSS works breaks existing content, and that goes against everybody's interests.

more details

However, this particular case it interesting. If we did this change, all previously valid pages would continue to work, so what's the problem?

It turns out that since mistyping a selector is an easy mistake to make, lots of people have made it. Moreover, when a page doesn't quite look the way the author expected, a common strategy is to write more CSS rules until it does, rather than trying to understand why the existing rules do not create the expected result.

Then, when the page looks good, the author ships it, including all mistaken cruft that doesn't do anything. This dead code might even survive later redesigns.

Effectively, even though it is not intentional, many pages which have been fixed through additional css declarations now depend on this sort of cruft not to work. More often than not, “randomly“ changing the background, the size, the borders, or the display value of some elements in the page will break it badly. It doesn't matter that the source of the “randomness“ is the author's previous mistakes.

And so, even though most people agree that error handling at commas in selectors would be nicer, this is not something that can be changed.



faq.txt · Last modified: 2018/03/15 15:33 by tabatkins
Recent changes RSS feed Valid XHTML 1.0 Valid CSS Driven by DokuWiki