stylus: Stylus should not overwrite CSS built-in functions, such as min and max

In CSS, there are built-in functions, such as min and max. Unfortunately, Stylus has the exactly same built-in functions, That’s bad and error prone.

I wanted to write a CSS max to responsively, dynamically calculate the height of an image from the current view port:

.myclass
        height: max(30vh, 50vw)

But it did not work as expected. After quite some time and debugging the generated CSS-code and then searching the Stylus docs, I found out, that Stylus has an internal function of the same name maxthat is called here.

So, stylus produced the following output:

.myclass {height:50vw;}

This is crazy stupid! Stylus cannot know the size of 1vw nor 1vhat compile time! It should at least abort with an error, instead of silently producing rubbish!

So,to solve my problem, I finally got the correct result with the following work-around:

.myclass
    height: @css{max(30vh, 50vw)}

Please rename or remove the Stylus functions max and min, and any other Stylus function that shadows a CSS built-in function!

Why not prepend all Stylus built-in functions with a prefix, such as stylus_min, __min or _min?

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 16
  • Comments: 25 (2 by maintainers)

Most upvoted comments

Stylus should just rename these specific functions, e.g. prepend ‘stylus-’ to them. Projects have to adapt. Which is not difficult, just run a regular expression over the files.

Good catch, and a use case I certainly hadn’t thought about!

Personally though, I prefer the dry syntax philosophy of Stylus, which would somewhat be undermined by some Stylus-specific prefix to memorise for some or all BIFs. Not to mention the problems around renaming BIFs now, when thousands of existing Stylus-based projects may rely on these being BIFs rather than CSS4 functions.

One solution could be to detect whether a min() and max() function contains run-time units like vw and vh, and return a CSS literal when it does, or compile it as a Stylus BIF when it doesn’t.

Another solution which could give the author more certainty about expected behaviour would be to have a more compact CSS literal syntax, maybe with backticks (`)?

.myclass
    height: `max(30vh, 50vw)`

Would either of these be useful to you?

@vendethiel wrote:

That’s CSS4. Stylus dates back to CSS3 or even before.

I see. Interestingly, the feature is already well supported in most browsers.

In that case, what about an upgrade of Stylus with regard to CSS4?

BTW, future changes of the standard and compiler specific extensions are the reason, why in C/C++, it is best practice to prepend precompiler macros with a prefix. I suggest to do the same here.

@nilslindemann

Stylus should just rename these specific functions, e.g. append ‘stylus-’ to them. Projects have to adapt. Which is not difficult, just run a regular expression over the files.

Yes, and all these suggestions, I mean that someFn() is always the CSS built-in, and the Stylus fn would be some of: $someFn or someFn!() or stylus.someFn(), is a Stylus major version bump, right. — So one could do these regex-replace when one had time (and not get a minor version bump major surprise :- ))

@disco0

handle css builtin function calls with a ! postfix

I like that idea. I think it should work the other way around: Stylus functions should get a ! suffix. Otherwise the original problem will remain — that Stylus unexpectedly does something different, when one types max(), than what one thought.

Because, if someone writes max(...) or calc(...), they typically have in mind the CSS built-in, not the Stylus function.

Like @nilslindemann wrote:

This is a bug. Shadowing standard CSS functions is bad.

***

someFunc!(...) is the approach Rust has taken for macros, and … apparently ! works fine in their case. someFunc!(...) looks pretty nice too, syntax highlighting wise? With the ! in a bit red / different color. Maybe there could be a Stylus color scheme recommendation that the ! should be bold / a bit different color. So it was easy to notice.

Personally I like all of $someFn(...) and someFn!(...) and stylus.someFn(...). (And letting just someFn() be the CSS built-in.)

@kajmagnus, prepending $ sounds good to me. I’d recommend some kind of prefix for built in Stylus functions, that prevents also conflicts with future CSS standards.

It helps me to write things side by side, just looking at them.

So here is an overview of the suggestions made so far, in order of appearance per category. The first is the Stylus call, the second the CSS call. “+” denotes my preference (which is not hard science).

These work currently:

  • max(30vh, 50vw) vs @css{max(30vh, 50vw)} +
  • max(30vh, 50vw) vs "max(30vh, 50vw)" % null
  • max(30vh, 50vw) vs "max(30vh, 50vw)" % ()

These change the CSS call:

  • max(30vh, 50vw) vs `max(30vh, 50vw)` +
  • max(30vh, 50vw) vs [max(30vh, 50vw)]
  • max(30vh, 50vw) vs @css max(30vh, 50vw)
  • max(30vh, 50vw) vs max!(30vh, 50vw)
  • max(30vh, 50vw) vs _max(30vh, 50vw)

These change the Stylus call:

  • stylus_max(30vh, 50vw) vs max(30vh, 50vw)
  • __max(30vh, 50vw) vs max(30vh, 50vw)
  • _max(30vh, 50vw) vs max(30vh, 50vw)
  • $max(30vh, 50vw) vs max(30vh, 50vw) +
  • math.max(30vh, 50vw) vs max(30vh, 50vw)
  • stylus-max(30vh, 50vw) vs max(30vh, 50vw)
  • max!(30vh, 50vw) vs max(30vh, 50vw) +
  • stylus.max(30vh, 50vw) vs max(30vh, 50vw) +
  • Max(30vh, 50vw) vs max(30vh, 50vw)
  • MAX(30vh, 50vw) vs max(30vh, 50vw) +

Other possibilities:

  • max(30vh, 50vw) vs max(30vh, 50vw) (call the proper function based on parameters)

@vendethiel we know that Sass went this route, but I believe it is potentially confusing.

Hello,

Where are we ? Can we hope to see these compatibility problems that accumulate between Stylus and modern CSS corrected?

I dug around a bit in the Stylus files, min() max() is here, but generally speaking all functions should be renamed. It would not be very difficult to start all over again (but time-consuming no doubt) and Stylus would become compatible with the future rules.

Of course, such a backward compatibility-breaking change would be the subject of a new major release. However, I think that this evolution is essential: not respecting the standard CSS as valid in Stylus is a certain guarantee for the death of the project.

We could also prefix with an underscore, which would be a quick and easy change:

.myclass
  height: _max(30vh, 50vw)

Indeed, I don’t think that CSS functions are ever pefixed with an underscore, however I think that would correspond well to the spirit of the Stylus syntax.

This would be very welcome improvement for css functions in general—after some experiments in the past with calc I now usually just end up writing sprintf operators like @nilslindemann (ps: "calc(...)"%() also works, believe this is the shortest form).

Haven’t looked at the parser code in a long time, and I don’t remember how whitespace is handled between a function identifier and its call parens, but how difficult would it be to handle css builtin function calls with a ! postfix (or maybe the other way around)? e.g.:

// one calc
calc(...)
// another calc
calc!(...)

I like the separation—it’d be pretty explicit what you mean, and (at least for now, using normal function declaration syntax,) it’d be impossible to declare a similar <ident>! style function which is a nice, if not maybe overkill, namespacing. Its also entirely isolated from other identifiers unlike a $ sigil or stylus- prefix (the former of which I use on all my variables, so not my pick here). Otherwise math probably works

@groenroos

Would either of these be useful to you?

Personally I like @css{ ... } better,

because 1) also readers not yet that familiar with Stylus, understand what it means. Whilst a backtick — I might have thought that 2) it got copied literally to the generated CSS, and might have wanted to remove it. After all, "..." is copied as is (the quotes intact, get copied too, right). And 3) maybe a future CSS feature will make use of backticks in some way? Which would then collide with Stylus.

One solution could be to detect whether a min() and max() function contains run-time units like vw and vh, and return a CSS literal when it does, or compile it as a Stylus BIF when it doesn’t

Seems like a more complex solution to me. Then, if min & max somehow didn’t do what I hoped, I’d need to wonder if maybe maybe there was something wrong with Stylus’ heuristics for computing or keeping-as-is min(..) and max(..). Might break if new CSS versions introduce new units.

@mwaeckerlin

what about an upgrade of Stylus with regard to CSS4?

I like that idea. What about Stylus 4.0 for CSS 4+, where all built-in Stylus functions and names were prefixed by $. So it’d be $max and $min. But of course (?) it must still be possible for Stylus utility libraries or application specific Stylus styles, to define one’s own functions without $ :- )

stylus_min is OK too — doesn’t look so nice, but is new-developer-friendly, & better than bugs  :- ) — I just noticed, after some weeks, that min(...) had been computed & removed by Stylus, and was absent in my web app.