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

& {...} nested class as a mixin – unspecified behaviour #2453

Open
tomek-he-him opened this issue Feb 16, 2015 · 6 comments
Open

& {...} nested class as a mixin – unspecified behaviour #2453

tomek-he-him opened this issue Feb 16, 2015 · 6 comments

Comments

@tomek-he-him
Copy link

This works as expected:

.foo {
  .bar.baz;
}

.bar {
  &.baz {
    some: stuff;
  }
}
$ lessc this
.foo {
  some: stuff;
}
.bar.baz {
  some: stuff;
}

And that fails:

.foo {
  .bar.baz;
}

.bar {
  & {
    &.baz {
      some: stuff;
    }
  }
}
$ lessc that
NameError: .bar.baz is undefined in .../that on line 2, column 5:
1 .foo {
2     .bar.baz;
3 }

Funnily, it works when the declarations are the other way round:

.bar {
  & {
    &.baz {
      some: stuff;
    }
  }
}

.foo {
  .bar.baz;
}
$ lessc the-other-way-round
.bar.baz {
  some: stuff;
}
.foo {
  some: stuff;
}

I'm using lessc 2.4.0 (Less Compiler) [JavaScript].

@seven-phases-max
Copy link
Member

See #996 (A more close example can be found in #1410).

Hmm, no. I think I'll reconsider this (it's most likely the same internal reason for both but the effect itself is too different to treat it as a duplicate). So I'm reopening it for further consideration.

@seven-phases-max
Copy link
Member

Notice the following example (logically equal to the one above) does not work at all regardless of the definition order:

& {
    .baz {
        some: stuff;
    }
}

.foo {
    .baz; // err: undefined
}

So this is more like unspecified behaviour.
Technically & {...} itself is not supposed to expose its internals as mixins at all (just like it does not expose its variables) but only to generate corresponding CSS selectors. Otherwise it breaks its outer scopes. So at first glance I'd say there're might be contridictive opinions if the .bar { & { .baz ... example should also work at all regardless of the definition order.


P.S.
Now I remembered this is pretty much the same example as in #1877 (comment) (merged to #2072 as closely depended language spec./decision).

@tomek-he-him
Copy link
Author

@seven-phases-max but .bar { & { &.baz { ... should be equal to .bar.baz { ... both as compiled CSS and as a mixin. Isn't it so?

@seven-phases-max
Copy link
Member

both as compiled CSS and as a mixin

Not quite. It's only guaranteed to generate proper CSS selector. But re-use of CSS rulesets as mixins, especially when it comes to nesting and complex selector hierarchies, is relatively indirect/supplementary feature. (With certain limitations and possible contradictory requirements. After all even just the basic fact that .bar.baz is considered to be a valid mixin call syntax (and not just some undocumented/unintended bogus) was debatable not so long ago. And it's still very minimal nesting/namespaces support documented).)


Btw., regardless of above (this issue certainly needs some decision as of #1877/#2072), speaking of the use-cases: just out of curiosity, with this and the prev. issue you opened I see you're relying on that "CSS classes as mixins" stuff quite often, is there some special reason for this? The trick is that in today's Less state it's quite suspicious: from maintenance/development perspective/point-of-view this is usually not the best design (unless it's dictated by some external factors), usually extend (or "explicit mixins as mixins" when extend is not applicable) serve common use-cases much better...

@tomek-he-him
Copy link
Author

is there some special reason for this?

Actually I got really excited when I first heard about extend, but TBH I didn't find it as reliable as mixins at the time. For example, this fails silently, and I must admit I have no idea why: (update: seems to be #2200)

.foo {
  &:extend(.bar-baz);
}

.bar {
  &-baz {
    some: stuff;
  }
}
$ lessc this
.bar-baz {
  some: stuff;
}

Thank you for the advice regarding explicit mixins. I did consider that, but decided against at first. We have a fat CSS project (think Twitter Bootstrap) with a modular structure. The styles are often nested a couple of parent-selector levels deep, to keep things DRY.

Every declaration belongs to a specific module. Other modules can help themselves by pulling that in and referencing it as a mixin. A module shouldn't care whether other modules need its declarations or not.

I also want to keep declarations for a context directly in the context, without an additional abstraction in between. To avoid this:

.text.bold {
  .text-bold();
}

That's theory. But after struggling with this for a while, I think I'll take your advice after all, until these decisions have been taken. And until #1177 is there. Thanks again :)

mhewson referenced this issue in rei/rei-cedar-vue-2 May 27, 2016
This reverts commit 460835f, reversing
changes made to 5c2e15c.
mhewson referenced this issue in rei/rei-cedar-vue-2 May 27, 2016
This reverts commit 8740976, reversing
changes made to 460835f.
@seven-phases-max
Copy link
Member

seven-phases-max commented Apr 22, 2017

Remarking it as a bug (after all recent discussions in various threads (e.g. #2072), a more strict "neither vars nor mixins should ever leak out of & {}" opinion goes favorable).
I.e both #2 and #3 examples in the initial post should result in a error regardless of the definition order.

@seven-phases-max seven-phases-max changed the title Can't use a deeply nested class as a mixin – when the class is declared later & {...} nested class as a mixin – unspecified behaviour Feb 19, 2018
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

3 participants