Skip to content

guards on css styles #748

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

Closed
jfojtl opened this issue Apr 3, 2012 · 27 comments
Closed

guards on css styles #748

jfojtl opened this issue Apr 3, 2012 · 27 comments

Comments

@jfojtl
Copy link

jfojtl commented Apr 3, 2012

I'd like to use guards on concrete CSS like this:

.rule when (@width < 400px) {
  color: red;
}

When @width variable is greater then 400px, the .rule would be hidden in generated CSS. Nowadays I need to do something like:

.rule (@w) when (@w < 400px) {
  color: red;
}

.rule {
  .rule(@width);
}
@souldreamer
Copy link

👍

@lukeapage
Copy link
Member

Could you explain? Isn't this possible? Or you want @width derived from the element(don't see how this is possible). Could you include a compilable example?

@lukeapage
Copy link
Member

Sorry, I understand now. +1 from me.

@matthew-dean
Copy link
Member

Since classes get interpreted as mixins anyway, this seems like an obvious win. +1

@matthew-dean
Copy link
Member

Note that I've proposed extending this syntax to property / value and inline mixin declarations in #1293.

@lukeapage
Copy link
Member

I'm not sure about the additions.. I think we should have 2 bugs and do
them separately. The basics (guards on normal css blocks) is imo more
important and more straight forward extension and syntactic sugar.

@matthew-dean
Copy link
Member

Makes sense.

@lukeapage
Copy link
Member

Lots of fun on this one

  1. If you mixin the styles but it has a guard, does it stop the styles
    being mixed in if the condition evaluates false - I say yes, so it acts
    just like a mixin with a guard, except it is present by default
  2. In the case above, is the condition evaluated purely in the context of
    its declaration - I say yes
  3. If you extend a guarded class that fails condition, do we output
    extended version or not? I say not
  4. Is the guard on the selector or ruleset? If selector you can have 2
    different guards on a block, controlling selector output. I say selector.

Have made good progress on implementing, need another couple of hours.

@extemporalgenome
Copy link
Contributor

@lukeapage can you explain #4 in greater detail (regarding the selector vs ruleset guard distinction)?

@lukeapage
Copy link
Member

my unit tests have (something like) this in at the moment...

.light when (lightness(@a) > 50%),
.dark when (lightness(@a) < 50%) {
    color: black;
}
@a: #444;
.i-see {
    .light;
    .dark;
}

extend is per selector because we decided it was unintuitive that it covered all of the selectors.. I think when should be the same.

p.s. probably need a better test since color: black will be removed if output twice but the idea is you would get

.dark {
    color: black;
}
.i-see {
    color: black;
}

@matthew-dean
Copy link
Member

Ah, thanks for the example. If I understand correctly, then I'm in agreement with everything you've outlined.

@extemporalgenome
Copy link
Contributor

@lukeapage thanks for the update, though I'm still not clear on the distinction. What would be the output if the guard were on the ruleset instead? And is @a an implicit argument to the selector when mixed into .i-see based on its scope, or is @a always loaded from the scope of the mixin's declaration (I believe you specified that it should be place-of-declaration). In other words, what would happen if everything else where the same, but .i-see were defined as:

.i-see {
    @a: #7f7f7f; // trick question, lightness should be exactly 50%
    .light;
    .dark;
}

@lukeapage
Copy link
Member

guard on ruleset =

.light
.dark when (lightness(@a) < 50%) {
    color: black;
}
@a: #444;
.i-see {
    .light;
    .dark;
}

and no output....

@extemporalgenome thats point 2.. it should have no effect

@extemporalgenome
Copy link
Contributor

Ah. I had completely misunderstood, believing that ruleset vs selector was an issue in determining what the output would be when a guard applies, not a matter of what the guard applies to. I agree entirely that it should be per-selector. One could always simulate per-ruleset by doing:

.light, .dark {
    & when (lightness(@a) < 50%) {
        color: black;
    }
}

Note that this also implies that the following:

.colorize(@a) when (lightness(@a) < 50%)  { color: black; }
.colorize(@a) when (lightness(@a) >= 50%) { color: white; }

could be equivalently specified as:

.colorize(@a) {
    & when (lightness(@a) < 50%)  { color: black; }
    & when (lightness(@a) >= 50%) { color: white; }
}

@lukeapage
Copy link
Member

yep, true.. I think its looking neat

@matthew-dean
Copy link
Member

Yeah, this feature is a slam-duck. Thanks, Luke. ^_^

EDIT: Er... that's slam DUNK. lol

@jonschlinkert
Copy link
Contributor

@matthew-dean, why would you do such a thing?! poor duck lol

@jonschlinkert
Copy link
Contributor

@extemporalgenome very nice examples!

@Soviut
Copy link

Soviut commented Jun 21, 2013

Nice! +1

@ESchuderer
Copy link

I'd love something like this:

@responsive: true;

@media (max-width: 200px) when (@responsive) {
  width: 80%;
}

Instead of

.responsive () when (@responsive) {
  @media (max-width: 200px) {
    width: 80%;
  }
}
.responsive;

@lukeapage
Copy link
Member

@initart please open a new issue

@lukeapage
Copy link
Member

Hrmm

.a when (@a = 1), (@b = 2) {

Vs

.b when (@a = 1), .c {

The trouble is I don't know if, a comma is part of the when or the next selector.

Options:

  1. only allow when on a single selector
  2. try to parse as a when condition, fall back on selector - downside is if we have a selector that looks like a guard condition its ambiguous and its ambiguous to someone reading the less
  3. change the syntax a little (could add more brackets, but then it differs from mixins)

I will try option 1 as it can always be expanded to option 2 or 3.

@extemporalgenome
Copy link
Contributor

I haven't looked into it much, but I get the sense that parsing and lexing are a combined step. That probably isn't the case with all LESS implementations (or at least all potential implementations), and when they are separate processing steps, the distinction you mention is completely unambiguous with a single-token lookahead: if it's a parentheses, then continue parsing the 'when' clause, otherwise parse a selector.

I'd suggest that in all cases we avoid changing syntax to account for implementation details (because, as mentioned, these kinds of issues may be trivially solvable with a different parse model).

@lukeapage
Copy link
Member

@extemporalgenome this has nothing to do with implementation..

It isn't that simple..

.a when (@a = 1), not (@b = 2) {
.a when (@a = 1), not /*comment*/ (@b = 2) {

also I'm concerned that if the guard syntax is ever extended, we are in a hole.

also, it isn't just whether the parser can parse it.. I did say in option 2 that I can quite easily backtrack, I have a concern that a syntax that is ambiguous to the parser without look-ahead is also ambiguous to someone using less.. I don''t think the resultant less looks nice if people are using or conditions AND multiple selectors.

@extemporalgenome
Copy link
Contributor

Ah, I see what you mean. Since you demonstrated a comment in there, and CSS comments are preserved, how and where would that comment appear in the output, if at all (since afaik, guard expressions are not part of any CSS proposal or spec)?

@lukeapage
Copy link
Member

Either it wouldn't appear or it would appear inbetween the selector and
start of the block.. that is what happens elsewhere - the comment bubbles
up to the resultant css.

And I'm not sure comments are supported in exactly that place in less.js at
the moment - but they should be/will be at some point.

@lukeapage
Copy link
Member

this is implemented. I also added test cases for this curious piece of less

& when (@a = 1) {
    .basically.an.if.block {
        color: green;
    }
}

which is syntactic sugar for the previous

.if() when (@a = 1) {
    .basically.an.if.block {
        color: green;
    }
}
.if();

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

8 participants