Compass / compass

Compass is no longer actively maintained. Compass is a Stylesheet Authoring Environment that makes your website design simpler to implement and easier to maintain.
http://compass-style.org
Other
6.73k stars 1.18k forks source link

Cross-Browser Support Variables Only Work When Defined Before Compass Import #1435

Open Snugug opened 10 years ago

Snugug commented 10 years ago

So many Compass extensions rely upon !default variables in order to have user-land setup variables. This works in the following manner:

// $setup-variable: 2 !default;
// @mixin test (content: $setup-variable;);
@import "framework";

$setup-variable: 1;

.foo {
 @include test; // 1
}

Now with Compass's Cross-Browser Support variables, if we were to set up the variables in the same way (for instance, $browser-minimum-versions or $graceful-usage-threshold) after our @import "compass";, Compass doesn't pick up on those variables and uses the Compass defaults.

This begs the question, why does this happen only with Compass's cross-browser support variables? Should it work the other way? Can it work the other way to provide some more consistency with the expected usage of !default variables?

eoneill commented 10 years ago

Personally, I think the current implementation works well, once you figure out how things are working internally.

The intention of !default is to define your non-default values (overrides) before you import the framework (this has been a confusing concept for several developers I've worked with).

Given this behavior, my projects are usually organized something like...

@import "config";
@import "compass";

// do stuff...

where _config.scss would do something like...

$graceful-usage-threshold: 2.5 !default;
$some-other-var: true !default;

this now gives you the ability to have page/file specific overrides as well:

// my overrides
$some-other-var: false;
@import "config";
@import "compass";

// do stuff...

You'd also want to look at init-time vs invocation-time configs (i.e. configs that are immediately used when the framework initialized vs configs that are only needed when a method is invoked).

Now, to explain why you're seeing the behavior you've described here: Compass doesn't really use $graceful-usage-threshold except during initialization, where it defaults a bunch of other threshold variables (e.g. $border-radius-threshold: $graceful-usage-threshold !default;).

So, if you wanted to retroactively change the threshold for border-radius, you could do $border-radius-threshold: 2.5;, but if you want it to apply to all the other *-threshold variables that inherit from $graceful-usage-threshold, you're better off setting your preferences before you import Compass.

Hopefully this made sense :)

Snugug commented 10 years ago

Thanks for the explanation of how !default was intended to be used, however, that is not how it works. Instead, how it works is that it provides a placeholder value to be used, but should that be overwritten before it needs to be used (say, for mixins, which is what we're dealing with in Compass's cross-browser helpers), it will use that new value instead. Now if this is unexpected behavior on the part of !default, that's one thing, but it's an entirely different thing to suggest that Compass is designed toward an intended behavior that goes against a very common pattern employed by a multitude of Compass extensions.

The place where this common pattern falls down is how Compass initializes the variables. Initializing a !default that's set by a variable requires you to chain from the top instead of the bottom, which IMO is awkward. I believe it'd probably be more user-friendly if you had a pattern something like the following:

$graceful-usage-threshold: 0.1 !default;
$border-radius-threshold: null !default;

@mixin border-radius($radius) {
  $threshold: if($border-radius-threshold, $border-radius-threshold, $graceful-usage-threshold);
  …
}

This would allow for both top-down variable configuration and bottom-up configuration which is the more common pattern for Compass extensions.

lolmaus commented 10 years ago

Are those configuration variables even used? I failed to find them in code. Do they come from a obsolete or prerelease version of Compass?

@Snugug, can you provide links to actual Compass code that behaves like you described, rather than give a hypothetical example?

Snugug commented 10 years ago

@lolmaus Compass 0.13.alpha.10.

This is a general discussion on how Compass initializes and uses defaulted vars. It happens to be using the new cross-browser stuff as an example. @eoneill provided the example of how Compass works, and I provided a counter example as to how it can be re-written to support the more common default overriding paradigm

lolmaus commented 10 years ago

Note to self: do not rely on Github code search.

timkelty commented 10 years ago

This ever get anywhere? @Snugug do you know if it's still happening in 1.0 w/ support vars?

The thing I find hard about using '!default' "as-intended" is illustrated below.

The following won't work, because I'm trying to use the contrast-color function, which isn't available unit after I import Compass.

Therefore, I usually end up just importing Compass and all my extensions first, and then overriding things with my config vars.

/**
 * Config
 */

// Some Compass overrides
$contrasted-dark-default: red;
$contrasted-light-default: contrast-color($contrasted-dark-default);

// My made up vars
$base-text-color: contrast-color(green);

/**
 * App styles
 */

@import compass;
@import styles;
timkelty commented 9 years ago

@Snugug If you're using support-legacy-browser, you'll also run into problems overriding defaults with $browser-minimum-versions.

@JohnAlbin has a fix, not sure why it hasn't been merged in.