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

Allow users to override CSS selectors #8055

Closed
pauldijou opened this issue Jun 2, 2013 · 3 comments
Closed

Allow users to override CSS selectors #8055

pauldijou opened this issue Jun 2, 2013 · 3 comments
Labels

Comments

@pauldijou
Copy link

TL;DR

Implement a way to override any Bootstrap selector using LESS selector interpolation. See full complex example at the end if you like code.

Full version

Since LESS has now a nice way to interpolate selector names (see http://lesscss.org/#-selector-interpolation), why not take advantage of it to allow users to rename any selector just like they can change any color?

The idea would be to have a file selectors.less or whatever, containing stuff like:

@btn-selector: btn;
@btn-default-selector: btn-default;
@btn-primary-selector: btn-primary;
...

And then impact other LESS files (like buttons.less):

.@{btn-selector} { ... }

.@{btn-default-selector} {
  .btn-pseudo-states(@btn-default-color, @btn-default-bg, @btn-default-border);
}

.@{btn-primary-selector} {
  .btn-pseudo-states(@btn-primary-color, @btn-primary-bg, @btn-primary-border);
}
...

Why this might be useful? One common use case would be people who prefer having "enhancements" on their elements rather than writing the same stuff multiple times. For example, having class="btn btn-primary btn-large" can be seen as "I have a button, which is a primary button and also a large button". Maybe you would prefer writing class="btn primary large" which can be seen as "I have a button which is primary and large".

How would you achieve that use the selector renaming? Just edit selectors.less with:

@btn-primary-selector: e("btn.primary");
@btn-large-selector: e("btn.large");

We need a little trick, using the e function that will only escape the string to allow a . inside the selector. This will output:

.btn.primary {
  .btn-pseudo-states(@btn-primary-color, @btn-primary-bg, @btn-primary-border);
}

.btn.large {
  padding: @padding-large-vertical @padding-large-horizontal;
  font-size: @font-size-large;
  border-radius: @border-radius-large;
}

I don't know about you, but I find it really cool. And it's fully backward compatible of course since it would compile the exact same CSS file as the current one with the correct default values in selectors.less.

What do you guys think about it? I can do the PR of course, but since I'm a lazy guy, I wanted to see if the concept could fit in Bootstrap before refactoring all that code.

What about mixins?

Hey, wait, can we use interpolated selectors as mixins?... Hemmm... Not really right now, see less/less.js#1196. That's quite sad and I really hope it will be fixed in a near future. Discovering that, I was thinking "Damn, does renaming selector is worth loosing mixins?". Obvisouly not. It is cool but mixins are cooler.

But there is already a place were Bootstrap is using selector interpolation and that's the grid! That's why you cannot use a mixin named .col-lg-1() even if the .col-lg-1 exists. Currently, in order to solve this, Bootstrap has introduce mixins like .make-row() and .make-column(@columns). So one solution would be to do the same for every components: .make-btn(), .make-label() and so on... I don't it find so bad, it makes a clear distinction between what is a mixin and what is a CSS class.

Hopefully, in the future, LESS will support mixin interpolation and those will no longer be required.

Going deeper

Ok, so that was the main concept. In the same time, I've been thinking about a few points that I'm not sure would be good or not...

One is enforcing a . before all selectors. As you can see in the previous example, there is always a . before the selector interpolation. But we could have get rid of it with:

@btn-selector: e(".btn");
@btn-default-selector: e(".btn-default");
@btn-primary-selector: e(".btn-primary");

@{btn-selector} {...}
@{btn-default-selector} {...}
@{btn-primary-selector} {...}

This would work as expected but it starts to be a bit ugly in selectors.less. The main benefit is allowing really any possible selector, including ids, but not sure this would be a good idea.

Another one it trying to take advantages of prefixes. Most of Bootstrap selectors are prefixed, like all btn-. Maybe we could try to allow user to override only the prefix if that's what they want... The code I wrote to achieve that it starting to get a bit complex in my opinion but it would be hidden from users anyway... So check a full example at the end of the issue, you can copy/paste it in http://less2css.org/ and play with it. Keep in mind that's the complex version. We could get rid of the prefix concept and only write basic string (except it would be exactly string... strings without quotes!)

Basic concept

// selectors.less
@btn-selector: btn;
@btn-default-selector: btn-default;
@btn-primary-selector: btn-primary;
@btn-large-selector: btn-large;

// Here is what the user can do to override some selectors
// Let's say he wants to write "btn primary" instead of "btn btn-primary"
@btn-primary-selector: e("btn.primary");
// Or even better
@btn-primary-selector: e("@{btn-selector}.primary");

// buttons.less
.@{btn-selector} {
  content: "The default btn class";
}
.@{btn-default-selector} {
  content: "The gray default button";
}
.@{btn-primary-selector} {
  content: "The blue primary button";
}

.@{btn-large-selector} {
  content: "The big large button";
}

Concept with prefix support

// selectors.less
// Put the dash inside the prefix so we can have prefix without dash if required
// Important: prefix can be a String since it is easier to use it that way
// but selectors should never be String otherwise it will interpolate a real
// String with quotes inside the generated CSS
@btn-prefix:"btn-";

// By convention, lets say that the default selector is the prefix
// without any strange caracter at the end
@btn-selector: ~`@{btn-prefix}.replace(/[-_]$/, "")`;

// And then, all selectors are prefixed with... the prefix!
@btn-default-selector: e("@{btn-prefix}default");
@btn-primary-selector: e("@{btn-prefix}primary");
@btn-large-selector: e("@{btn-prefix}large");

// Here is what the user can do to override some selectors
// Let's say he wants to write "btn primary" instead of "btn btn-primary"
// but keeping the "btn" value from the prefix one
@btn-primary-selector: e("@{btn-selector}.primary");

// buttons.less
.@{btn-selector} {
    content: "The default btn class";
}
.@{btn-default-selector} {
    content: "The gray default button";
}
.@{btn-primary-selector} {
    content: "The blue primary button";
}

.@{btn-large-selector} {
    content: "The big large button";
}
@Xaroth
Copy link

Xaroth commented Jun 4, 2013

This would be quite useful; especially in cases where a minimal footprint is a requirement for a project...

@mdo
Copy link
Member

mdo commented Jun 4, 2013

/cc @ckj

@mdo
Copy link
Member

mdo commented Jun 28, 2013

This kind of abstraction is crazy awesome, but not something we'll be working into BS3 at this time. Perhaps for the next major update. Thanks though! <3

@mdo mdo closed this as completed Jun 28, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants