::initial (Set your own initial styles for CSS properties)


#1

It has become quite common to define box-sizing: border-box to the * selector and also to include reset styles to all or many elements. This not only has slight performance issues but is also a bit limited (I’ll explain).

So I’m proposing an ::inital selector.

On it’s own, it would affect all elements.

Let’s do some examples.

::initial {
  box-sizing: border-box;
}

would do basically the same as

* {
  box-sizing: border-box;
}

*:before,
*:after {
  box-sizing: border-box;
}

But it doesn’t need to parse through each and every element first, overwriting the browsers default behaviour. Instead the browser simply overwrites it’s own default styles for all elements and be done with it.

This one is nice:

::initial {
    transition-timing-function: ease-in-out;
}

If you like all your transitions to use this timing function, you don’t have to set it every time or go with the * selector again. The problem with * would be that you can’t use transition: name; because it would then go back to the default behaviour of “ease”.

For Sass-users, this would be similar:

$base-timing-function: "ease" !default; // overwrite this to your needs

@mixin transition($property, $timing-function: $base-timing-function) {
  transition: $property, $timing-function;
}

element {
  @include transition(property);
}

Let’s use the :matches() aka :any() selector to be more specific (in a modern way)

:matches(article, aside, header, footer, [...])::inital {
  display: block;
}

There are many usecases for this and it would be a good performance bump. What do you say?


#2

I think this is a nice idea! +1


#3

The idea is wonderful. Especially useful for CSS Custom Properties if you want to specify they should not inherit. Currently, this is a major pain point. Thanks for coming up with this!


#4

Makes sense, but as far as priority, I’m not sure how significant it is. There are many more high-value features I’d place ahead of this.


#5

If I understand correctly, the only difference between ::initial and *, ::before, ::after would be performance? If so, I don’t think this should be a author-visible feature. We can just teach browsers to recognize the universal selector and optimize it as appropriate, if they don’t already.


#6

I don’t think this is true, Simon. @initial would modify the “initial” value of the property browser-wise. This is quite different from overriding the property at the lowest declaration-wise level.

Example:

* { --fc-grid-template-columns: none; }
body > main { --fc-grid-template-columns: 1fr auto 300px 1fr; }

@media(...) { body > main { --fc-grid-template-columns: initial; } }

would make --fc-grid-template-columns invalid, whereas

@initial { --fc-grid-template-columns: none; }
body > main { --fc-grid-template-columns: 1fr auto 300px 1fr; }

@media(...) { body > main { --fc-grid-template-columns: initial; } }

would make it return to none.

Also, as new pseudo-elements get added (eg: ::before(2)), you don’t have to add continuously more code to support them. This avoids code duplication and, ultimately, prevent it from becoming quickly obsolete.


#7

In case of the box-sizing example, yes, we should not create a new selector for *. It would only be performance (though important) and less code. The real point of ::inital is explained after that. Especially when working with shorthands it can become quite useful.


#8

You seem to have a good handle on how this could work — want to team up with @devatrox to put a proposal together for the CSS WG?


#9

This sounds great! I have no experience in writing proper proposals. Could probably use some help on this.


#10
  1. Don’t be scared, just write up what you think is useful. Most people are nice, even in disagreement. When they’re not I and others will be happy to bash their skulls in (but nicely).
  2. Think of use cases over technical solution, they’re far more important. Notably be sure to explain well how this enables things that would fail with * (and not just performance-wise).
  3. I get the impression that @FremyCompany is thinking more of an at-rule than a pseudo. That makes sense to me too (but I’ll let you folks sort that out).
  4. Ask for review, share it, etc.

#11

I would be happy to work on that with you.

As a starting point, I think we would need use cases. We already have two, given this thread: setting an initial value for custom properties that isn’t “invalid” and changing the default “box-sizing” mode (this is a really good one, because it is mentioned as one of the big css mistakes in http://wiki.csswg.org/ideas/mistakes). Can you think about anything else?

If you want to send me a mail, please feel free to do so btw (francois.remy.dev@outlook.com).


#12

I’m not convinced these use cases justify a new feature.

setting an initial value for custom properties that isn’t “invalid”

What it the use case for that? In your earlier example with --fc-grid-template-columns: initial, it looks like you want to use initial as a variable that you can redefine… But we already have a mechanism for variables you can re-define: custom properties.

changing the default “box-sizing” mode

What’s wrong with * { box-sizing: border-box } ? Performance doesn’t count: browsers can optimize * just as well as @initial.


#13

The setting of partial properties (e.g. the transition-timing-function from the OP) is a definite use case.


#14

Again, I don’t see the difference with a * selector. transition-timing-function is totally a property of its own, in addition to being one of the subproperties of the transition shorthand.


#15

Quoting from the OP:


#16

Oh, right. This has deeper implications on implementations than I first realized. At the moment, shorthand property declarations can be expanded into the equivalent multiple longhand declarations as soon as they’re parsed. (Including defaulting to the initial value for components that were omitted.) If the initial value can be change later (including by a different stylesheet), it means we have to keep around at most an half-parsed/expanded representation of shorthands. This could have a significant complexity and performance cost.


#17

No you don’t. You can still expand the shorthand, you’d just need to fill in the unspecified longhands with “initial” rather than the literal initial value. That’s completely compatible with the spec and doesn’t require any additional processing (you’d just resolve it into the actual initial value at specified-time).


#18

Thank you! I will contact you soon on that. I am on vacation right now so don’t expect anything in the next few days.

We’ll see if the use cases can really justify this new selector/at-rule and adress the points against it by @SimonSapin . Performance might not be as major as I had in mind at first? But it wasn’t why I came up with it in the first place.


#19

Setting “better” initial values so that shorthand properties don’t change some properties to initial values you don’t want: that’s a use case I have.

In cases where there is no specific difference from *, *::before, *::after {…} except performance, I still find that a purposeful @initial rule would make the code more explicit. It might also be picked up by CSS preprocessors in some ways?

Another use case I have, from the top of my mind:

@initial {
  background-repeat: no-repeat;
}

One possible big user error:

@initial {
  /* Text would get exponentially big: */
  font-size: 125%;
  /* Text would get exponentially small, and quickly invisible: */
  font-size: 62.5%;
}

There might be other properties that would wreak similar havoc if used in @initial.


#20

This would not give exponentially big/small text (unlike * { font-size: … }). The font-size property is inherited, so when nothing is specified for a given element, the inherited value (typically the computed value of the parent, which for font-size is after resolving percentages) is used, not the initial. The initial value is only used as “inherited value” for the root element.