We want to get rid of SCSS (#8), but rather than just throwing out the features of SCSS entirely, I want us to have a more minimal CSS preprocessor.
Declaration nesting is the most obvious feature, and worth doing on its own. Turns out there is a draft spec for this now, so maybe it would be good to mimic its syntax: https://drafts.csswg.org/css-nesting/
(Except, this syntax looks stupid, and seems to be working around a bunch of problems I have literally never had with SCSS. Maybe we can just...support this syntax, but not require it.)
Arithmetic expressions are convenient too.
Nesting
I really don't see the point of requiring & at the beginning of every nested rule. The draft indicates that parsing ambiguities are to blame, and that large amounts of lookahead are required to disambiguate things in some cases, making it unsuitable for runtime. I propose that we simply not do stupid ambiguous CSS and get some nice features out of the deal.
I propose that it works like so.
Parsing rules vs. declarations
Broadly speaking, I think it should be pretty easy to tell the difference by just looking ahead to a ; or a {. This is probably somewhat naive though? In any case, I think we can come up with a Good Enough solution for this. TODO.
Nesting syntax
In a nested declaration, any occurrences of & will be replaced with :is(<parent selector>).
Any nested declaration that does not contain & will be implicitly prefixed by & (with a space!) before processing step 1.
Notice that the :hover rule is like & :hover instead of &:hover. I don't want to be disambiguating between pseudo-classes and other selectors all the time. If you want &:hover, write it yourself.
Some expressions could and should be simplified, such as any case where an :is contains only a single selector and occurs at the beginning of an expression. For example:
// This:
:is(.foo .bar) .baz { ... }
// Could be this:
.foo .bar .baz { ... }
// But this:
.baz :is(.foo .bar) { ... }
// could not, because the semantics are different.
(Unless this messes with selector precedence in some way? But I hope not.)
There are probably many other little optimizations to discover as we go.
Media queries
SCSS allows you to define media queries inside declarations. This is quite convenient, and shouldn't be hard to deal with as long as we can parse the @media stuff.
Variables & expressions
Preprocessor variables and expressions are occasionally quite useful. For example, Tachyons (a CSS utility library we use) defines several SCSS variables for all the preset spacing it uses. We can (and often do) reuse those variables in our own utility style declarations:
// In Tachyons
$spacing-medium:1rem!default;// In our SCSS
.cg3{column-gap:$spacing-medium;}
Furthermore, it's occasionally useful to do some simple arithmetic with these values. It would be great to keep this, but we must at all costs avoid the very stupid decisions that the SCSS people are making as a result of fully parsing every damn CSS rule.
In our preprocessor, any compile-time expression should be clearly delimited using some syntax that is invalid in CSS, e.g. #expr($spacing-medium / 2).
Supporting only +, -, *, and / would be fine for now. It should track units on values and ensure that they match whenever operators are used.
There is the unfortunate matter that - is used in CSS names all over the place, and SCSS allows it for variable names. I'm not that attached to it though, we're doing our own preprocessor and I wouldn't really care if it was disallowed in variable names, so that - is unambiguous in expressions. Alternatively, I guess we could require spaces around operators in expressions, but that seems worse to me.
It's convenient to be able to define variables inside declarations and have them be scoped to that declaration. Keeps names from conflicting. Shouldn't be too hard to support.
Custom functions
We really don't need the ability to define functions in the CSS source, but it would be nice to be able to write our own custom functions here and there. For example, we have a SCSS function px2rem that converts a pixel value to rem (assuming 1rem is 16px). That would be nice to preserve in the form #px2rem(60px).
Other syntax
Line comments are allowed and converted to block comments. No minification, who cares, gzip exists.
We want to get rid of SCSS (#8), but rather than just throwing out the features of SCSS entirely, I want us to have a more minimal CSS preprocessor.
Declaration nesting is the most obvious feature, and worth doing on its own. Turns out there is a draft spec for this now, so maybe it would be good to mimic its syntax: https://drafts.csswg.org/css-nesting/
(Except, this syntax looks stupid, and seems to be working around a bunch of problems I have literally never had with SCSS. Maybe we can just...support this syntax, but not require it.)
Arithmetic expressions are convenient too.
## Nesting
I really don't see the point of requiring `&` at the beginning of every nested rule. The draft indicates that parsing ambiguities are to blame, and that large amounts of lookahead are required to disambiguate things in some cases, making it unsuitable for runtime. I propose that we simply not do stupid ambiguous CSS and get some nice features out of the deal.
I propose that it works like so.
### Parsing rules vs. declarations
Broadly speaking, I think it should be pretty easy to tell the difference by just looking ahead to a `;` or a `{`. This is probably somewhat naive though? In any case, I think we can come up with a Good Enough solution for this. TODO.
### Nesting syntax
1. In a nested declaration, any occurrences of `&` will be replaced with `:is(<parent selector>)`.
2. Any nested declaration that does not contain `&` will be implicitly prefixed by `& ` (with a space!) before processing step 1.
3. Recursion do its thing
Example:
```
.foo {
.bar, .baz & {
color: red;
:hover {
color: blue;
}
}
}
```
```
:is(.foo) .bar, .baz :is(.foo) {
color: red;
}
:is(:is(.foo) .bar, .baz :is(.foo)) :hover {
color: blue;
}
```
Notice that the `:hover` rule is like `& :hover` instead of `&:hover`. I don't want to be disambiguating between pseudo-classes and other selectors all the time. If you want `&:hover`, write it yourself.
Some expressions could and should be simplified, such as any case where an `:is` contains only a single selector and occurs at the beginning of an expression. For example:
```
// This:
:is(.foo .bar) .baz { ... }
// Could be this:
.foo .bar .baz { ... }
// But this:
.baz :is(.foo .bar) { ... }
// could not, because the semantics are different.
```
(Unless this messes with selector precedence in some way? But I hope not.)
There are probably many other little optimizations to discover as we go.
### Media queries
SCSS allows you to define media queries inside declarations. This is quite convenient, and shouldn't be hard to deal with as long as we can parse the `@media` stuff.
## Variables & expressions
Preprocessor variables and expressions are occasionally quite useful. For example, [Tachyons](https://tachyons.io/) (a CSS utility library we use) defines several SCSS variables for all the preset spacing it uses. We can (and often do) reuse those variables in our own utility style declarations:
```scss
// In Tachyons
$spacing-medium: 1rem !default;
// In our SCSS
.cg3 { column-gap: $spacing-medium; }
```
Furthermore, it's occasionally useful to do some simple arithmetic with these values. It would be great to keep this, but we must at all costs avoid the [very stupid decisions](https://sass-lang.com/documentation/breaking-changes/slash-div) that the SCSS people are making as a result of fully parsing every damn CSS rule.
In our preprocessor, any compile-time expression should be clearly delimited using some syntax that is invalid in CSS, e.g. `#expr($spacing-medium / 2)`.
Supporting only `+`, `-`, `*`, and `/` would be fine for now. It should track units on values and ensure that they match whenever operators are used.
There is the unfortunate matter that `-` is used in CSS names all over the place, and SCSS allows it for variable names. I'm not that attached to it though, we're doing our own preprocessor and I wouldn't really care if it was disallowed in variable names, so that `-` is unambiguous in expressions. Alternatively, I guess we could require spaces around operators in expressions, but that seems worse to me.
It's convenient to be able to define variables inside declarations and have them be scoped to that declaration. Keeps names from conflicting. Shouldn't be too hard to support.
### Custom functions
We really don't need the ability to define functions in the CSS source, but it would be nice to be able to write our own custom functions here and there. For example, we have a SCSS function `px2rem` that converts a pixel value to `rem` (assuming `1rem` is `16px`). That would be nice to preserve in the form `#px2rem(60px)`.
## Other syntax
Line comments are allowed and converted to block comments. No minification, who cares, gzip exists.
Here's a test corpus...this isn't necessarily 100% authoritative but I think it shows what we'd like to do, roughly. (It would be fine if it doesn't get exactly this output, since some of the handling of :is is pretty subtle.)
We want to get rid of SCSS (#8), but rather than just throwing out the features of SCSS entirely, I want us to have a more minimal CSS preprocessor.
Declaration nesting is the most obvious feature, and worth doing on its own. Turns out there is a draft spec for this now, so maybe it would be good to mimic its syntax: https://drafts.csswg.org/css-nesting/
(Except, this syntax looks stupid, and seems to be working around a bunch of problems I have literally never had with SCSS. Maybe we can just...support this syntax, but not require it.)
Arithmetic expressions are convenient too.
Nesting
I really don't see the point of requiring
&
at the beginning of every nested rule. The draft indicates that parsing ambiguities are to blame, and that large amounts of lookahead are required to disambiguate things in some cases, making it unsuitable for runtime. I propose that we simply not do stupid ambiguous CSS and get some nice features out of the deal.I propose that it works like so.
Parsing rules vs. declarations
Broadly speaking, I think it should be pretty easy to tell the difference by just looking ahead to a
;
or a{
. This is probably somewhat naive though? In any case, I think we can come up with a Good Enough solution for this. TODO.Nesting syntax
&
will be replaced with:is(<parent selector>)
.&
will be implicitly prefixed by&
(with a space!) before processing step 1.Example:
Notice that the
:hover
rule is like& :hover
instead of&:hover
. I don't want to be disambiguating between pseudo-classes and other selectors all the time. If you want&:hover
, write it yourself.Some expressions could and should be simplified, such as any case where an
:is
contains only a single selector and occurs at the beginning of an expression. For example:(Unless this messes with selector precedence in some way? But I hope not.)
There are probably many other little optimizations to discover as we go.
Media queries
SCSS allows you to define media queries inside declarations. This is quite convenient, and shouldn't be hard to deal with as long as we can parse the
@media
stuff.Variables & expressions
Preprocessor variables and expressions are occasionally quite useful. For example, Tachyons (a CSS utility library we use) defines several SCSS variables for all the preset spacing it uses. We can (and often do) reuse those variables in our own utility style declarations:
Furthermore, it's occasionally useful to do some simple arithmetic with these values. It would be great to keep this, but we must at all costs avoid the very stupid decisions that the SCSS people are making as a result of fully parsing every damn CSS rule.
In our preprocessor, any compile-time expression should be clearly delimited using some syntax that is invalid in CSS, e.g.
#expr($spacing-medium / 2)
.Supporting only
+
,-
,*
, and/
would be fine for now. It should track units on values and ensure that they match whenever operators are used.There is the unfortunate matter that
-
is used in CSS names all over the place, and SCSS allows it for variable names. I'm not that attached to it though, we're doing our own preprocessor and I wouldn't really care if it was disallowed in variable names, so that-
is unambiguous in expressions. Alternatively, I guess we could require spaces around operators in expressions, but that seems worse to me.It's convenient to be able to define variables inside declarations and have them be scoped to that declaration. Keeps names from conflicting. Shouldn't be too hard to support.
Custom functions
We really don't need the ability to define functions in the CSS source, but it would be nice to be able to write our own custom functions here and there. For example, we have a SCSS function
px2rem
that converts a pixel value torem
(assuming1rem
is16px
). That would be nice to preserve in the form#px2rem(60px)
.Other syntax
Line comments are allowed and converted to block comments. No minification, who cares, gzip exists.
bvisness referenced this issue 9 months agoHere's a test corpus...this isn't necessarily 100% authoritative but I think it shows what we'd like to do, roughly. (It would be fine if it doesn't get exactly this output, since some of the handling of
:is
is pretty subtle.)