amnu3387

amnu3387 OP

Nice, thanks for sharing. I had thought about using nimble_parsec but I would need to learn it properly so just started doing by hand with what I know. Your parsec version does look more organised and less code :slight_smile:

Should I change the title? The project itself is cssex but for putting it here on the forum I chose to write it as ExCSS and just saw that you named yours that.

CSSEx is almost fully featured, I’m writing now the custom functions part and some helpers for using them. It’s missing comments which is basic.

At this point specific validations can be added to every part if it was needed or wanted (I might work on adding further validations incrementally, like asserting that if you’re in a [prop=“val”], you’re using valid =, =~, ^=, equality variant, etc). Outside of proper css spec, adding further things like tree-shaking would be almost “trivial” to if there was already something to extract a hierarchy of css from an html page.

I did read parts of the specification for css2 but the difference when parsing CSS from something as cssex or sass , is that the rules are stricter on CSS and in this you depend on further things inside the blocks to output and validate - once you introduce the concatenation & and you’re inside a block everything goes because almost any character can concatenate to the top level chain to produce a valid selector.
So if you want to verify it you need to evaluate it after the fact (and it also depends if any more concatenation elements are inside this selector as well as they would tag into its trail, because it can be arbitrarily nested).

Then outputting that into css, the same with simple nesting like having media queries inside selectors, you can’t generate the css rule in place, as you have to put the previous selector chain inside the media block and the contents of the media block inside this selector. Then if you do @includes, that brings another file in place that can also be inside arbitrary blocks… Then it needs variables and interpolation and these have to be tossed around too, plus keeping the lines & columns information and parsing step.

At that point you need to carry a bag of things as you parse and store them in other format than plain binary, but it would be interesting to see a fuller implementation just with parsec to see how it ends up looking.

I looked at the tests but I didn’t understand how those are supposed to be run. I’m just writing small chunks of relevant cssex and assert on the output, and some errors/warnings. Once I have the functions finished I’ll translate a fairly complex sass stylesheet (a small css framework I usually use) into cssex to do a final bigger test of correctness and others of speed (as it is I’m seeing significant different between dart-sass 0.15s to cssex 0.01s but these weren’t scientific). Maybe I could use those tests to assert on the generated CSS.

0
Post #11
olafura

olafura

The reason why I did a full parser is mostly because if you introduce some of the nesting rules it’s hard to know how to apply them without knowing what structure you have.
https://github.com/postcss/postcss-nested

I also wanted to try building a proper css parser by the spec and because GitHub - acammack-r7/erlang-css: A library for parsing and serializing CSS · GitHub failed on some css code I tested it with.

I don’t think anyone is using my library so I don’t mind any name conflict since I haven’t really mentioned it anywhere I believe and it’s kind of a niche until it is really solid and has a lot of examples.

0
Post #12
olafura

olafura

Nice I didn’t know there was a spec for nesting will have to look at that:

https://github.com/csstools/postcss-nesting

1
Post #13
amnu3387

amnu3387 OP

Neither did I. And I just noticed a pretty significant oversight on mine that thankfully is easily correctable.

Anyway, why introduce a character & to indicate that it’s a plain nest? I mean what else could it be if it’s not a rule (<text><colon><text><semicolon>)?

And I think would be great if they went for nested @media expressions (but not media inside media as that doesn’t make sense, well, it could make sense but would get overall more confusing and easy to create impossible to meet rules)

NOTICE : In a future version of this project, nesting at-rules like @media may be deprecated, as they are not part of the nesting proposal. In a comment, a CSSWG member expressed interest in handling nested @media while handling selector nesting. So deprecating nested at-rules has been temporarily delayed.

But media inside plain selector blocks in some cases it allows you to see the whole element styling in one single place.

I’ll give a look at the draft because if they implement their & then I need to find another valid char for the concatenation x)

It’s pretty useful specially if it’s done to the spec I think.

0
Post #14
amnu3387

amnu3387 OP

So I did a bit more reading on the draft.

It’s interesting and I think it covers what nesting needs to be but I’m not sure about using only &. I’m going to use sass/scss as the comparison (but I haven’t went to confirm their spec is exactly as this though so I might be slightly off here):

I always use and thought of & as being specifically concatenation.
This means that

button {
      &.btn-red { background-color: red; }
      &.btn-blue { background-color: blue; }

      span { margin-left: 10px; }
}

Produces:

button.btn-red { ... }
button.btn-blue { ... }
button span { ... }

So & “glues” the selector described afterwards to the parent(s) selector chain(s) up to its opening. This would be used just to describe things where selectors change the styling of the parent.

A non & selector does simple CSS selection when nested. This selector when “inside” the parent(s) selector chain(s). And is used just to facilitate the styling of child elements of the selector up to it.

I think this is less ambiguous. They also suggest adding an additional @nested at-rule to combat the readability issues,

While direct nesting looks nice, it is somewhat fragile. Some valid nesting selectors, like .foo &, are disallowed, and editing the selector in certain ways can make the rule invalid unexpectedly. As well, some people find the nesting challenging to distinguish visually from the surrounding declarations.

because in their proposal you can use &.concatenate and & .not-concatenated. and they’ll have different css meanings while being hard to spot the space between the & and the selector chain. (and my parser right now does the same thing although it’s technically wrong for how I see it used, it’s an easy fix nonetheless and I think I will implement it in this stricter sense too)

The other point about invalid nesting, single trailing & could be taken care of by being stricter where a nesting can be placed - e.g. only at the beginning of a non top level selector, and no spaces between it and the selector - translated to warning about invalid rules automatically when that occurs - which means it’s always a glue me up operation. This would also make IDE’s, parsers and additional tooling life’s easier.

I see they have a GitHub repo to provide feedback and discussion I might take this there to see their thoughts on it.

0
Post #15
amnu3387

amnu3387 OP

Yeap, closed with “wont-fix”. Which is funny given it’s a draft for a spec extension.
I sometimes don’t understand how this works. It’s not like there’s no prior art. Sass is from 2006, less is from 2009, they’re the most widely used pre-processors and introduced this idea.

Along comes the opportunity a new spec and instead of using the ideas that prompted the spec, they change them to the point they’re incompatible with what people already showed they preferred, while still introducing complexity (or more) and worse readability.

2
Post #16
tj0

tj0

Totally looking forward to your library so that I can get rid of some more webpack and npm craziness. Just yesterday I tried to install some project that depended on yarn and even after an npm install it gave an amusingly opaque error which I failed to resolved.

I got confused reading through the issue likely since I’m still a novice at css. The example on the SASS site for parent selector, they actually used a space on the dir=rtl line. Maybe it’s meant more as search and replace rather than concatenation?

0
Post #17
amnu3387

amnu3387 OP

Thanks! It’s almost feature complete. Another of the reasons I decided to write it was also because I was trying to setup an old nuxt project that used node-sass and couldn’t get libsass to work on ubuntu (maybe for a good reason, but was still pretty annoying trying first to fix that - ultimately went with the new js based sass). If there’s any utilities you would like to see available tell me and I can look into adding them.

I think the real reason why they don’t consider the option (besides having everything lined up and docs written, etc) is because it changes how the parsing has to be done inside a block. Right now in css2 if you’re inside a block and you reach a colon : per the spec you can be sure that you’re in an attribute.

If you are now to add the ability to nest selectors and want to keep that unambiguity, due to the fact that pseudo-selectors & pseudo-classes both use colons, e.g. :hover, you need to somehow signal the line you’re starting inside the block, because otherwise when reaching that colon it would be ambiguous if it was an attribute or a selector. So you “can’t” have a naked nested selector without doing some changes on a parser that was relying on the colon.

So they introduced a similar approach to scss with the ampersand token. But in scss you have & and can have a naked selector too, and it does nesting as well, but has a different meaning from the &.

div {
      &:hover { color: red; }
      button:hover { color: green; }
}
// this results in
div:hover { color: red; }
div button:hover { color: green; }

So the “parent” selector basically appends the selector to the previous chain, while a naked declaration just adds another selector to the chain.

In css3 it will be:

div {
      &:hover { color: red; }
      & button:hover { color: green; }
}

And the difference is that space between & and the selector - and this is were I dislike the proposal because it makes it harder to distinguish between two different outcomes in the css rule. It also means that in a sequence of nested comma-separated selectors, all of them have to be prefixed by &, while otherwise you could write &.concatened, .non-concatenated { ... }. And then there’s another @ rule being introduced (@nest) that is less strict than the &.

Anyway, first world problems for sure but I think overall the scss syntax is way cleaner, and given that the parsers are fairly outnumbered by the people writing css (maybe 50, between browser engines and css pre-post processors?), that a new spec that required a change on the parsers side to instead decide at the end of the declaration if it’s an attribute or a selector wouldn’t be the end of world.

0
Post #18
tj0

tj0

I think I get it now. Looks like the following line of the spec:

To be nest-prefixed, a nesting selector must be the first simple selector in the first compound selector of the selector. If the selector is a list of selectors, every complex selector in the list must be nest-prefixed for the selector as a whole to nest-prefixed.

From their example 4:

.foo {
  color: red;
  .bar { color: blue; }
}
/* Invalid because there’s no nesting selector */

The SASS syntax for nesting is pretty useful for nesting, you hardly have to think about it.

nav {
  ul {
    margin: 0;
    padding: 0;
    list-style: none;
  }
}

I’m pretty confused myself why someone would try to modify syntax that seems to be working from 15 years ago, but there must be some esoteric reason.

Thanks for the explanation.

Yah, I’m considering removing the bundler and just concatenating minified files also. One of my pet peeves is coming back to side projects, cloning them, and finding out they no longer can be built. From my limited experience, it appears that anything involving the NPM ecosystem has maybe a longevity of a year before maintenance is needed for code rot.

0
Post #19
amnu3387

amnu3387 OP

Yep, I’m also not in the loop so don’t know what has to be taken into consideration and limitations or who are the stakeholders. Still annoyed though.

Yeah, some times it’s a bit finicky - but to be honest with so much complexity in tooling, sprawl in packages, etc, I’m amazed it usually just kinda works - though personally I don’t think this is a sane default for “web-dev” work.

0
Post #20