Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto Prefix replacement #1199

Closed
lukeapage opened this issue Feb 25, 2013 · 25 comments
Closed

Auto Prefix replacement #1199

lukeapage opened this issue Feb 25, 2013 · 25 comments

Comments

@lukeapage
Copy link
Member

A really popular issue is the ability to have variable properties.. but the main/only use-case I've seen is prefixes.

I saw that stylus supports automatically expanding properties with prefixes..

For cases where the syntax is different we suggest people use a mixin for that property and alter the arguments to work with different versions.

For cases where the syntax is the same, I suggest we have

-*-gradient: blah;

which is transformed to a set of prefixes

-webkit-gradient: blah;
-ms-gradient: blah;
-mozilla-gradient: blah;

which can be overridden with a option

--prefixes=ms,webkit
@jonschlinkert
Copy link
Contributor

In cases where the syntax is the same, where would the prefixes be created/managed? My thinking is that vendor prefixes are technically only on properties that are not uniformly implemented and standardized across browsers, and new properties are proposed all the time. So at any given point, many properties have different levels of support with different browsers. Consider CSS masks, filter effects etc. etc. Some properties would only get one or two prefixes, and others might get five, is there a way (or desire) to manage that?

@lukeapage
Copy link
Member Author

if you want to manage that, I think it should not be less' job but you should write a mixin for each property.

For complex properties like gradient I would not recommend this feature - it is more for simple properties where the syntax is unlikely to change and therefore a browser implementing a previously un-implemented prefixed property is highly unlikely to hurt (and will instead mean the website is just ready). I think a way of deciding what browsers should be switched on for what properties is not desired (and I think that if it is, this feature should probably be abandoned).

This ties into the big complaints a while back that people kept only using webkit prefixes and mozilla started recognising some webkit prefixes.

@SomMeri
Copy link
Member

SomMeri commented Feb 27, 2013

Just an idea: what about putting prefixes into variable and then use interpolation like syntax?

@prefixes: ms, webkit;
-@{prefixes}*-gradient: blah;

could compile to

-ms-gradient: blah;
-webkit-gradient: blah;

It would be flexible and does not require entirely new syntax.

@SomMeri
Copy link
Member

SomMeri commented Mar 10, 2013

Browsers ignore a declaration with an unknown property. As long as that is valid, I have a workaround. It is possible to create mixin that adds vendor prefixes to declarations.

The idea is to combine

  • invalid property declaration,
  • escaping,
  • and string interpolation.

We can use interpolated escaped value to close invalid property with semicolon and append vendor prefixed declaration to it: hack: 1 ~"; -moz-@{declaration}";

Sample input:

 /* The mixin adds vendor prefixes into property declaration. Supported 
     prefixes: -moz-, -webkit-

 Parameters:
   * @declaration: string with property declaration
 */
 .vendorPrefixes(@declaration) {
   // vendor prefix: mozilla
   hack: 1 ~"; -moz-@{declaration}";

   // vendor prefix: webkit
   hack: 1 ~"; -webkit-@{declaration}";

   // vendor prefix: ...
 }

 #usePlace {
   // pass the declaration about to be vendor as a string parameter
   .vendorPrefixes("gradient: blah");
 }

Less.js-1.3.3 output:

#usePlace {
  hack: 1 ; -moz-gradient: blah;
  hack: 1 ; -webkit-gradient: blah;
}

Browsers should ignore hack: 1 ; part.

@lukeapage
Copy link
Member Author

@SomMeri that is a work-around but ideally we would like to develop less so that you don't need to hack around it.

I still think the simplest is just *

-*-gradient: blah;

and we auto replace * with a configurable list of properties.. no interpolation of property names, no hack, no hassle...

We could even keep the less language the same and just add an option that if on, and you use any prefix, it auto adds in the other ones...

@jonschlinkert
Copy link
Contributor

👍

@SomMeri
Copy link
Member

SomMeri commented Mar 14, 2013

@lukeapage I was mostly sort of proud about finding that trick and wanted to brag. I'm definitely not saying that you should not implement the feature, just that there is usable workaround in the meantime if someone really want.

The option to add all prefixes everywhere there is at least one prefix might do too much through. People might need all prefixes on some places and only some of them on other places. And if a portion of an old style sheet already have them listed all, they may not want to multiply them into all times all.

@matthew-dean
Copy link
Member

The problem with -*-gradient: blah; is that you don't necessarily always want to use the same prefixes. Browsers unprefix certain CSS properties at different times.

I've been evaluating a lot of LESS mixing libraries lately, and their implementation has taught me a lot about what's missing in the LESS language. This came up because I see a ton of CSS3 mixins (in almost every library) that have huge blocks of repeating property declarations.

But, as I said, it's not always the SAME prefixes. I was actually going to come here and propose something almost identical to what @SomMeri had (referring to this):

@prefixes: ms, webkit;
-@{prefixes}*-gradient: blah;

The asterisk is an interesting way to do it. But yeah, same idea: I was thinking you could define your list of prefixes once (either globally or per block or both), and use that to make a single property declaration.

Fortunately, I think the web is evolving to where prefixes will eventually (mostly) be a thing of the past, but right now, they're still an unfortunate reality, and one of the biggest reasons people use a LESS mixin library to begin with.

@matthew-dean
Copy link
Member

Even better, let's go one step further!

{-ms-, -webkit-}*gradient: blah;   // Is it more semantically correct for dashes to be within the braces?
// Is the asterisk needed, or does this make sense:
{-ms-, -webkit-}gradient: blah;

By that, I mean, make an alternate allowable syntax that allows you to inline the prefixes. Would make it really easy to read, with the assumption that it will convert the above to:

-ms-gradient: blah;
-webkit-gradient: blah;
gradient: blah;

Unless somehow it needs to be more explicit to include the non-prefixed value, but not sure how you would define that, since it's essentially "nothing". Nor can I think of cases where you would use prefixed values only.... but I may not be thinking of cases.

Also, you'd want to support:

    background-image: {-moz-, -webkit-, -o-}*radial-gradient(center, @color1 0%, @color2 100%);  // AND/OR
    background-image: @{prefixes}*radial-gradient(center, @color1 0%, @color2 100%); 

So, the parser would read this syntax as: "Create a copy of this declaration substituting each member of the list, and finally a copy without any members of the list.

@matthew-dean
Copy link
Member

Ooo!

And THEN I could do:

h1 {
  border{-top, -bottom}*: 4px solid #9a603d;
}

I love this new syntax that doesn't exist yet! lol... Thanks, @SomMeri! ^_^

@jonschlinkert
Copy link
Contributor

Okay, before eveyone hates me hear me out lol... I really don't see a need for this at all for vendor prefixes alone. Not even a little bit. To reiterate what I said early on: #1199 (comment), given that prefixes are different for many properties, how would this really end up educing any work for developers? Presumably, this would only be valuable when you're not using a mixin, right? Because when you are using mixins, you don't need this. You've already created a mixin once (one time, that's all... no need for reducing labor there), where any time saved in not having to write out each property is lost again in having to create "mixin-specific-vendor-specific-property-variables", which won't even work for things like background gradients because the vendors don't even use the same syntax. Why not just use a nice clean mixin which is "pre-loaded" one time with the properties you require?

Now that I got that out... to @lukeapage's point in the original comment:

the main/only use-case I've seen is prefixes.

Aha! I think I have a much better use case for this, and it goes something like this: "CSS Custom Properties for Cascading Variables Module Level 1".

Love it or hate it (and I personally hate it) this is happening, so custom (variable) properties is probably one of the most important features Less can implement to prepare for this. For example (from the CSS spec):

:root {
  main-color: #06c;
  accent-color: #006;
}
/* The rest of the CSS file */
#foo h1 {
  color: var(main-color);
}

How does Less.js respond to this? I'm not entirely sure, but what I'm thinking is that we might want to approach this differently than what we're thinking so far on this Issue, and we should consider the syntax both for creating custom properties in Less.js, as well as how Less.js might generate custom properties from Less variables to help users prepare for "CSS Custom Properties for Cascading Variables Module Level 1". I guess I see this discussion as addressing only one side of what has become a two-sided coin.

So maybe we need a feature or additional option that converts this:

@main-color: #06c;

into this:

:root {
  main-color: #06c;
}

I'll think about this more but I wanted to at least throw this out to see what kinds of ideas come back.

@matthew-dean
Copy link
Member

Hmm... I see your point, but look at it in the reverse. You're saying: this isn't required because mixin libraries can take care of it. What I'm saying is that in MANY CASES, the mixin libraries would not be required if it was supported in LESS syntax. In other words, nowadays we have to install a mixin library just to handle prefix cases. For me, that's usually the ONLY reason I need a LESS library. And the library itself is full of repeated code. AND, there's a bunch of cases where, even though it defines, say, a gradient with prefixes, it doesn't work because there's one or more gradients or background images or whatever.

Sure, I could modify the mixin library (and never update it), or make my own, and try to cover all custom use cases for using CSS3 properties. But I'd rather just declare my CSS3 properties inline without something else to manage. I'd rather that LESS be smarter and cause less DRY, which is one of the core goals.

Prefixes may be the only use case, but they're not a small use case. They're used all the time, and often used in cases that no mixin library elegantly covers.

@jonschlinkert
Copy link
Contributor

@MatthewDL

the mixin libraries would not be required if it was supported in LESS syntax.

What?!? Lol, okay so you and I are doing vastly different things with Less and CSS. I used to use gradients a lot more, but even when I did they only made up a fraction of the styles in my projects. What about:

  • typography
  • grids
  • transitions

I probably have 10 to 15 mixins for every one that has a gradient.

@jonschlinkert
Copy link
Contributor

@MatthewDL

For me, that's usually the ONLY reason I need a LESS library

Fair enough, I'd be surprised if that was the case for "most" users. We need to think about the Less community and maintaining a code base that is reliable and delivers consistent results.

@jonschlinkert
Copy link
Contributor

@MatthewDL

But I'd rather just declare my CSS3 properties inline without something else to manage.

I've asked before but didn't get a response. Since many properties have different vendor prefixes, where will this "list" of prefixes-per-property be stored, enabling you to "just declare [your] CSS3 properties inline without something else to manage"?

Should Less.js commit to staying up to date with the latest properties and prefixes? I suppose we could use a config store (e.g. https://github.com/yeoman/configstore), or something similar so that the long list of prefixed properties is user-editable (just in case you happen to work on mobile projects or something, where you would only want specific prefixes). If Less.js isn't maintaining the list, how/where will the prefixes and their corresponding (up-to-date) properties be stored?

IMO, implementing "custom properties" is a much better solution, because you could easily grab a "custom property lib" that has all of the prefixed properties you want, you could edit it to your heart's desire, it wouldn't be specific to the narrow use case that you're focusing on, and it wouldn't make Less.js responsible for maintaining a list of valid, up-to-date, vendor-prefixed-properties AND having to provide users with a way to override those when they don't want specific prefixes to be generated.

Not to mention, there are a number of great plugins for most text editors that do this if it's something you want. When I first learned CSS, my text editor automatically generated the prefixed properties for me. Then I discovered Less (and learned that many of the auto-generated properties I had been using were wrong or out of date).

If that doesn't work, you could also use: http://leaverou.github.io/prefixfree/. Which, btw, if we were going to support this idea, it would probably be wise to consider leveraging an existing lib that is already familiar with the gotchas of doing this.

I'd rather that LESS be smarter and cause less DRY, which is one of the core goals.

I wasn't aware of that goal.

@matthew-dean
Copy link
Member

The list isn't stored anywhere. It's declared via variables or inline, so the rest about having less.js maintain anything seems invalid.

Prefix-free will not work. Different projects require different prefixes per declaration based on browser support for that project.

Probably forcing use of mixins is not the worst idea, as those are easily updated when browser support changes. I was just hoping to be lazy.

I wasn't aware of that goal.

Yep. It's why mixins exist, after all. And variables. Declare once, use multiple times. It was recognized as one of the reasons for LESS (or SASS) when it first came on the scene: http://coding.smashingmagazine.com/2010/12/06/using-the-less-css-preprocessor-for-smarter-style-sheets/

@jonschlinkert
Copy link
Contributor

The list isn't stored anywhere. It's declared via variables or inline, so the rest about having less.js maintain anything seems invalid.

No, to be clear, this makes your desire to not have to do the work of creating them invalid. I've been perfectly clear in saying that Less.js should NOT maintain anything, it's a bad idea.

It's declared via variables or inline

This is the exact point I've been making. Unless we maintain the properties and vendor prefixes, developers will not gain anything or decrease work with this feature, you will still have to declare these properties somewhere. Why duplicate something that is already resolved in Less? Even if Less.js did maintain these it would result in the same output as mixins, so it wouldn't make anything DRYer. Bottom line: no net benefit.

Yep.

Nope. Smashing magazine is great and all, but it seemed you were implying that it was a "core goal" of Less.js. I understand now that you meant it's a core goal of preprocessors in general, which may or may not be the case. If it is a core goal of Less.js, being that this is a community project if you have those core goals documented somewhere, please share them with the rest of us so that we can get on the same page.

@Soviut
Copy link

Soviut commented May 22, 2013

I agree, this is complicating things for perceived simplicity. There certainly are plenty of cases where list unrolling would by handy, but there are way too many edge cases to make it predictable.

I think the real hurdle is variable interpolation within properties. If that was simpler, building a reusable mixin for vendor prefixes would be simple:

.prefix(@prop, @value) {
    -webkit-@prop: @value;
    -ms-@prop: @value;
    -mozilla-@prop: @value;
}

a {
    .prefix("border-radius", 5px);
}

Similarly, that prefix mixin could be used inside other mixins to make them more DRY.

@jonschlinkert
Copy link
Contributor

I think the real hurdle is variable interpolation within properties. If that was simpler, building a reusable mixin for vendor prefixes would be simple

👍 well said.

And building from what I said here #1199 (comment), we really need to be helping developers prepare for changes in the CSS spec. Making vendor prefixes easier to manage is a small matter compared to some of the upcoming features that Less.js will need to adapt, adopt or navigate around. Like CSS variables (seriously, var-? what are they thinking?).

@Soviut
Copy link

Soviut commented May 22, 2013

Agreed. Vendor prefixes are basically a stopgap measure that will become less and less important as time goes on.

@matthew-dean
Copy link
Member

you will still have to declare these properties somewhere

Sure, it's just more concise syntax.

But, note I've already agreed with you. Forcing mixins is probably a good pattern for maintainability. But, having said that, even the prefix mixin example is flawed because as I mentioned, not all CSS3 properties require the same prefixes, depending on what current browsers have unprefixed, and which browsers you're supporting for your web app. But, for many users, that pattern may work since extra declarations will be dropped.

However, it may be that for the time being, the default scenario of requiring a CSS3 prefix library is the best current stopgap.

So, I suggest we close this issue.

@matthew-dean
Copy link
Member

Based on the fact that we're going to move forward with variable interpolation within properties, then, as has been mentioned, I think this is now solved. Because you will essentially be able to do this.

@prefix-moz: true;
@prefix-webkit: true;

.moz-prefix(@prop, @value) when (@prefix-moz) {
  -moz-@{prop}: @value;
}
.webkit-prefix(@prop, @value) when (@prefix-webkit) {
  -webkit-@{prop}: @value;
}
.border-radius(@radius) {
  .webkit-prefix("border-radius", @radius);
  .moz-prefix("border-radius", @radius);
  border-radius: @radius;
}
.box {
  .border-radius(10px);
}

That's all the control and configuration you need in the world. Putting vars into properties was really only the missing step. So closing.

@jonschlinkert
Copy link
Contributor

I think this should continue on #36. right?

Also, @matthew-dean nice mixin example, I can't wait to see how many amazing mixins pop up as a result of this feature.

Before we get ahead of ourselves though, @lukeapage do you agree that this should/can be solved with interpolated properties? We have not had the benefit of your input on this.

@bassjobsen
Copy link
Contributor

The grunt auto prefixer (https://github.com/nDmitry/grunt-autoprefixer) got its data from the caniuse database (https://www.npmjs.org/package/caniuse-db) and allow you to set the browsers option.

Can this built in the Less compiler just like the sourcemap option? Which should give option like:
--autoprefix: false (default)
--autoprefix: true (running with target some default browser version)
--autoptefix: ['last 2 version', 'ie 8', 'ie 9'] (custom browser support versions)

I think that when Less can support things like compression and sourcemaps, support autoprefixing also fits here. A potential benefit can be that you are enabled to run autoprexing before sourcemaps and compression.

@seven-phases-max
Copy link
Member

@bassjobsen This was (relatively) recently discussed once more in still open #1917.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants