XF 2.0 Help with Multiple xf-default fallbacks in LESS templates

pegasus

Well-known member
I am attempting to use xf-default with multiple fallback values:
If my custom content-color is empty, use contentBase's color; if that's empty, use textColor; if that's empty use a hard-coded color.

My LESS template is currently similar to the following:
Code:
@_contentColorCustom: xf-default(@xf-my_content--color, @xf-contentBase--color);
@_contentColorText: xf-default(@_contentColorCustom, @xf-textColor);
@_contentColor: xf-default(@_contentColorText, #000000);

.test {
color: @_contentColor;
}
Expected result, something like:
Code:
.test {
color: #141414;
}

However, the resulting output is more like:
Code:
.test {
color: xf-default(xf-default(#141414, #141414), #000000);
}
with the raw xf-defaults showing like that.
And I have tried nesting the xf-defaults directly:
Code:
@_contentColor: xf-default(xf-default(xf-default(@xf-my_content--color, @xf-contentBase--color), @xf-textColor), #000000);
The result was the same.
I have also tried having all the fallback values in a single xf-default:
Code:
@_contentColor: xf-default(@xf-my_content--color, @xf-contentBase--color, @xf-textColor, #000000);
Which showed as:
Code:
.test {
color: #141414, #141414, #000000;
}

What is the correct way to achieve this, or is this a bug?
 
It's sort of neither -- xf-default just doesn't work exactly like you're hoping. A lot of @xf- stuff is really pre-processor based (as they may include less expressions themselves). The xf-something() functions often are as well.

xf-default() is specifically designed to prevent possible LESS parser errors should a user empty a value (that had something by default). It's not really designed as a generic fallback system. As a matter of fact, it's a specific regex:
Code:
/
   (?<=[^a-z0-9-])
   xf-default\(\s*
   @xf-(?P<prop> [a-z0-9_]+(?!-[a-z0-9_]) (\--[a-z0-9_-]+)? )
   \s*,\s*
   (?P<fallback> (?> [^()]* (\( (?P>fallback) \))? )+ )
   \s*\)
/ix

In your example, even if @_contentColor were empty, it wouldn't trigger a LESS error -- these errors only really happen when you end up needing to pass a value, which is now empty, into a function.

I'm not sure if this is a simplified test case, but I'm struggling a little to see what you're after, though if you feel like you need a complicated set of options to define a color like this, it may be worth just creating a style property for the color specifically (such that if someone blanks it out entirely, that's not something that they should expect to work).
 
This is a simplified test case. I am currently using custom style properties and I have been able to work around the issue by just using a XenForo style property in xf-default which has a value in the default style, even if that property may not be semantically correct for the context (but using incorrect semantics can lead to problems on sites with customized styles). I also needed to pass the result through xf-diminish to generate a related border color; if the property was set to blank, this resulted in a parser error.

I have had people ask me questions in the past like, "Why doesn't this use the semantic XenForo style property?" while also saying "I want this to be a separate style property so I can customize it." I was hoping that there would be some way to satisfy both without opening up the user to parser errors which they don't see immediately.

Thank you for explaining that the xf-default route won't work though; you have saved me more time trying to figure out what I was doing wrong. Do you think that it may be possible to achieve this with different syntax, such as xf:if? Or would this need to be pre-calculated in a class-extension and stuffed into a fake style property (something I have done for more complex calculations)?
 
Without seeing the exact situation, it's hard to be too specific with a recommendation. But I would:

Create a style property for this specific color. In this situation, you can default it to a palette color (or some sort of mix), though if there is a "basic" color that fits semantically, then it makes sense to use that instead. You'll see this approach taken throughout the default style properties. By definition, the style property you create should be "semantic" to your usage.

Then you can reference it in CSS like:
Code:
color: @xf-myColorProperty;
// or, if you require a fallback explicitly
color: xf-default(@xf-myColorProperty, @xf-paletteColor1); // obviously change the color to whatever is likely to roughly make sense

border-color: xf-diminish(xf-default(@xf-myColorProperty, @xf-paletteColor1));
(Instead of a color, we often use "transparent" as a fallback, though that isn't really ideal for a text color.)

Note that with the color example, the blank value won't cause a parser error (and it will just effectively be ignored).

Your original example had several layers of fallbacks, but to me, I'm not sure what the benefit is over just explicitly defining the necessary color. Style customization always creates some level of challenge when new components come in, so if someone would like to change the color, by defining an explicit property, you get a much more direct approach to this.
 
Top Bottom