Pseudo Namespacing #440

Closed
AdamBrodzinski opened this Issue Jul 4, 2012 · 17 comments

Projects

None yet

9 participants

The lack of true namespacing and scope within CSS is a real problem when trying to build plugins or re-usable CSS modules.

A popular solution is to add a pseudo namespace with a ns- prefix. For example if you're working on a Calendar widget you could namespace the days class with cal-days. This tends to be quite cumbersome after a while.

I've typed up of a proposed solution to apply a pseudo namespace to the parent selectors. The & operator could also be integrated here, using something like &days{...}, but omitting a & would really clean up the code visually.

Thoughts?

SASS:

@namespace = cal {
    .container {
        width: 100px;
        height: 100px;
        background: #f5f5f5;

        a {
            text-decoration: none;
        }
    }

    .numbers {
        font-size: 10px;
        padding: 3px;
    }

    #days {
        font-size: 12px;
        font-weight: bold;
    }   
}

CSS:

.cal-container {
    width: 100px;
    height: 100px;
    background: #f5f5f5;
}

.cal-container a {
    text-decoration: none;
}

.cal-numbers {
    font-size: 10px;
    padding: 3px;
}

#cal-days {
    font-size: 12px;
    font-weight: bold;
}

At first glance I quite like it. I have no idea what the language implications of implementing something that is though. Like can you double or triple or more namespace? How does @extend ing work when it goes in and out of a namespace?

thierryk commented Jul 7, 2012

I don't do preprocessors (yeah, I know…). But if I did, I would question the need of a @rule for this. What about simply starting with: prefix- { … }. i.e:

 cal- { … } 

The trailing "-" in there should prevent any collision...

verekia commented Jul 8, 2012

I like the idea of having a built-in namespacing feature but just like @chriscoyier I don't know the Sass implementation enough to tell if it's conceivable.

Also, in terms of syntax, this could be an alternative too:

@namespace mynamespace {

}

It is really close to the current mixins syntax (without parameters):

@mixin mymixin {

}

So I guess the implementation would be easier with this one.

That being said, there is already a way to add some modularity to namespaces via string interpolation:

$ns: fruits;

.#{$ns}-banana {
    color: yellow;
}

.#{$ns}-strawberry {
    color: red;
}

Which outputs:

.fruits-banana {
  color: yellow; }

.fruits-strawberry {
  color: red; }

It's a little bit verbose but does the job :)

I like the idea, but please make it "logical", so not like this:

@namespace = cal {
  .something {
    color: #fff;
  }
}

For me this would seem to generate: cal.something

But this:

@namespace = .cal {
  -something {
    color: #fff;
  }
}

This would generate: .cal-something ;-)

@chriscoyier That's a good question, I'm really not sure how @extend works on a low level... I wish I had enough foo to contribute to this though! :/

@thierryk A lot of SASS's extra features are used with the @ sign... such as @extend, @include.

@verekia , I do like the @namespace mynamespace {...} you proposed better, less is more!

@ReinierK I think my JavaScript mindset was triggering that thinking for syntax :) @chriseppstein Commented via twitter about possibly using the & symbol for this use case and referenced this: #286 so the syntax

@AdamBrodzinski Thanks! I don't know why I thought I had to reply to your "request" on twitter when obviously I know nothing about the subject :)

@thierryk Maybe someday you'll switch to dark side... I mean the pre-processor side =)

Contributor
nex3 commented Jul 13, 2012

I'm not sure this is the right axis along which to work towards modularity.

The reason you want to namespace classes is presumably because you're writing a style module that will be used in multiple places alongside many other style modules. You don't want the classes defined by your module to conflict with other modules' classes, or with unrelated classes that ultimately end up on the HTML page.

However, I think the way style modules will ultimately end up looking won't involve concrete classes at all. Once Sass 3.2 is released with placeholder selectors, I think the correct way to write a module like this will be pervasive use of those. The client stylesheet would use @extend to attach your styles to its selectors, and it would know enough about the holistic structure of the stylesheets to avoid collisions.

The upcoming redesign of @import should also include support for avoiding name conflicts between separate libraries, so even if two modules define %container it won't be a problem.

@chriseppstein, what are your thoughts on this?

Hi Nathan,

First off, thanks so much for creating and maintaing SASS... it makes writing CSS fun again. Also I really wish I could contribute to this with code, but i'm not there yet. I just wanted to kick around this idea that methodologies like SMACSS (which I currently use) and BEM use this heavily.

Yes, some of the concern is class naming collision between 3rd party libraries but I more less use namespacing internally on my site. Placeholder selectors seem to solve 3rd party modules brillantly, looks great!

My main reasons for psudeo namespacing:

• Increases selector performance
• Decreases selector specificity
• Easier to override
• Easier to tell where that style is used '.hdr-foo' is somewhere in the header

Chris mentioned via twitter that in SASS 3.3 we might get something to use the & to prefix the classes for this use case. From what I understand it would be like this:

.cal- {

    &numbers {
        font-size: 10px;
        padding: 3px;
    }  

}

// would output 

.cal-numbers {
    font-size: 10px;
    padding: 3px;
}

// which currently in SASS 3.1 outputs

.cal- numbers {        //notice space before numbers
    font-size: 10px;
    padding: 3px;
}

A real example is a site that i'm currently building has a showcase section that is re-used throughout the site. It has 5 products across from left to right on a slider. The main container is .shwc and the details of that item are called .shwc-desc, as opposed to using .shwc dl dt and .shwc dl dd , I can just use one class .shwc-desc .

Placeholders or not SASS should still include some way to namespace for people working with OOCSS and to match LESS's namespaces. Sometimes modularity is not even the point, sometimes the goal is just to group classes or mixins, like say create a gradient namespace that would include mixins like vertical, horizontal etc. and then you'd be able to call gradient-vertical or gradient-horizontal while still keeping the ability to change the namespace later on.

Contributor
nex3 commented Jul 20, 2012

I'm not suggesting a completely namespaceless world. I'm just saying that prefixing every name with <namespace>- is not the best way to go about it. I'd prefer a more Python-style approach where each file is conceptually a module, with some sort of namespacing automatically applied. This allows you to avoid worrying about name conflicts within any of your own code without needing any additional nesting. The particulars of this are still very much up in the air, of course.

Ah, that would be even better!!

I'm not too familiar with Python but i'll have to read up on that.

Owner

I feel like #286 provides enough flexibility to DRY up class-based namespaces and @snookca (a big proponent of this coding style) agreed with me on twitter when this came up a while back.

A module system is absolutely needed for globals like top-level variables, mixins, functions to avoid naming collisions. This is happening more and more in the community and needs to be addressed in the next 6mo-1yr.

Owner

Closing this as it's about CSS class namespacing -- not a module system.

Contributor
pdaoust commented Sep 11, 2013

Let me know if these following thoughts are too different from the intent of the original issue... to me, they seem related, so here goes:

This may be an edge case for some, but for me it's a fairly common issue: let's say we use classes (or some other sort of attribute) on the root element to denote a certain context. Let's say we're using Modernizr, for instance, and it's added the .touch class to the <html> element. But we're also using IE conditional classes to shim fake media query support into IE 8 and below. Now we've got an element that's got two classes, perhaps something like <html class="touch size-large">. (Ignore for a moment that this is an impossible situation -- IE8 with touch support?!)

Now I've written a mixin that targets certain contexts -- media queries, namespace classes on the root element, etc.

@mixin contextify($media-query, $selector) {
    @if $media-query {
        @media #{$media-query} { @content; }
    }
    @if $selector {
        #{$selector} & { @content; }
    }
}

We don't know where the end-user is going to use this mixin -- perhaps they'll end up nesting contexts. If their contexts only ever use media queries, that's all well and good, because Sass supports media query bubbling. But if we start nesting things like this:

$pocket-query: "(max-width: 25em)";
$pocket-selector: ".pocket";
$touch-query: "(pointer: coarse)";
$touch-selector: ".touch";

@include contextify($pocket-query, $pocket-selector) {
    .menu a {
        display: block;
        color: red;

        @include contextify($touch-query, $touch-selector) {
            // Make sure the menu items are big enough to tap with fat fingers
            min-height: 44px;
        }
    }
}

At this point, it starts creating classes like .touch .pocket .menu a, whereas it should be .touch.pocket .menu a. This is expected, cuz it's what Sass is supposed to do. But I'd love to see some sort of 'selector bubbling', in which multiple classes became concatenated. This would be a nightmare to create, I think, because how would Sass determine what was supposed to bubble with what?

A couple solutions:

  1. The :root element can only exist once in a document, and people generally put all their contexty classes (viewport size, browser version, feature support) on the root element. Perhaps Sass could support :root bubbling, so that

    :root.touch {
        :root.pocket { color: red; }
    }

    would become

    :root.touch.pocket { color: red; }

    Nice, but not terribly versatile, if you wanted to add your fallback classes to the body element instead.

  2. Alternatively, it could look for elements like html, body, and main, which can only appear once in a document. However, that forces Sass to know too much about the document structure.

  3. Made-up pseudo-elements as markers for combining bubble-able selectors! This idea is totally wacky, so I don't feel bad if you say it's terrible :-) This would take the form of:

    :::my-made-up-pseudo-class.touch {
        :::my-made-up-pseudo-class.pocket { color: red; }
    }

    which would compile to

    .touch.pocket { color: red; }

    This trivial example can already be done:

    .touch {
        &.pocket { color: red; }
    }

    But that only works when you can guarantee the ordering of nested selectors, which might not be the case when some other author is using your fancy contextualize mixin. Made-up pseudo-elements could solve that problem.

Just a braindump here. Dunno if anything is useful, but I sorely desire some sort of mechanism like this.

Contributor
nex3 commented Sep 12, 2013

I wouldn't want to make anything that looks like a selector bubble like a media query. Selector nesting has a very well-defined meaning, and it would be very confusing and ad-hoc to change that meaning if certain special selectors were used.

A better solution to this probably uses the script parent selector support that was recently added to the master branch. That would allow you to access and inspect the parent selector and check if you want to add your class to it or not.

Contributor
pdaoust commented Sep 18, 2013

Yeah, I figured I was shooting for the moon with that idea -- thought I'd suggest it anyway :-) So I'm curious about the script parent selector -- I've been following the @at-root discussion, and I also saw mention that we'll be allowed to go #{&}, which is awesome -- is that second example what you're talking about?

Update: I tried the master branch, and holy cow, combining the two new features -- scriptable parent selector and the @at-root directive -- I've created something very much like a bubble-able selector. Thanks very much for your awesome work!

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