CSS Variables and String concatenation - css

I'm trying to use CSS variables to generate a dynamic path.
Example:
:root {
--fonts-path: "/path/to/font";
}
#font-face {
font-family: "FontName";
src: url(var(--fonts-path) + "/FontName-Light.woff") format('woff');
font-weight: 100;
}
html {
font-family: 'Metric', Arial, sans-serif;
}
This is failing with a not-found module 'var(--hpe-fonts-path', this is the webpack log:
ERROR in ./~/css-loader?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!./~/postcss-loader!./src/theme/fonts.css
Module not found: Error: Cannot resolve module 'var(--fonts-path' in /Users/project-sample/src/theme
# ./~/css-loader?sourceMap&modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!./~/postcss-loader!./src/theme/fonts.css 6:83-114 6:234-265 6:403-434 6:576-607
Any pointers?

I see some problems with your approach:
#font-face rules don't inherit CSS variables set on :root.
You can't use + to concatenate CSS strings. See string concatenation in css
Not all implementations support var() inside url() yet.

A similar issue is described in this post:
CSS native variables not working in media queries
where the user tries to use a variable inside a #font-face rule.
As described in the accepted answer (https://stackoverflow.com/a/40723269/4879632) you cannot use variables inside of rules, because the variables are inherited by :root (html) child elements. # Rules (font faces, media queries, etc) are not elements, so they do not inherit from the root. Thus, you cannot reference a variable from there.

Try incluiding the url inside the variable, This works in a background context, but not sure if it does inside font-face
:root {
--url:url("https://picsum.photos/id/1/200/300");
}
.box {
background:var(--url);
}

You can't concatenate variable in CSS (refer here):
Since you can't concatenate in CSS (aside from the content property), CSS variables can not be concatenated. This means, for example, you can't combine a CSS variable that is a number with a unit.
// No version of this will work
div {
--height: 100;
height: var(--height) + 'px';
}

Related

How to find css classes (in e.g. chrome dev tools) that use a specific font-family?

I'm currently in the process of updating all my websites from using webfonts to hosting the fonts locally by myself. This process is a little bit frustrating, because I often can't find the css classes of the webfonts. At the moment, it's more a "try and error" kind of thing, where I'm just klicking trough the google chrome dev tools and looking for the corresponding css classes. So I was wondering if there is a simple way to look in a published website via browser for the css classes of a specific font family? (I cannot search for the classes in the IDE, because in this use case the websites where developed with webflow)
EDIT: The websites in question were created with a "building block" system called "Webflow". There, the fonts are selected via graphical interfaces. Now the problem is that somewhere in these old and huge web pages there are CSS classes that use the "Lato webfont". I want to replace this font, but I can't search for used fonts in this graphical interface. What I can search for are the CSS classes. So my idea was to use the Chrome Dev Tools to find out which CSS classes used the Lato font to ultimately replace it.
Find css rules by properties
If you can't edit you site's css files globally you might at least get some sort of "cheat sheet" containing all selectors matching certain property values.
let cssRules = getCssRules();
let filterLato400 = findRulesByProperties(cssRules, {
"font-family": "Lato",
});
console.log(filterLato400);
let filterLato400Italic = findRulesByProperties(cssRules, {
"font-family": "Lato",
"font-weight": 400,
"font-style": "italic"
});
console.log(filterLato400Italic);
//get all css rules in document
function getCssRules() {
let cssText = "";
let rules = [
...(document.styleSheets[0].rules || document.styleSheets[0].cssRules)
];
let cssArr = [];
rules.forEach(function(rule) {
let selector = rule.selectorText;
let cssText = rule.cssText;
if (selector && cssText) {
let properties = cssText
.replace(selector, "")
.replace(/[{}]/g, "")
.split(";")
.map((val) => {
return val.trim();
})
.filter(Boolean)
.map((vals) => {
return vals.split(":");
});
cssArr.push({
selector: selector,
properties: properties
});
}
});
return cssArr;
}
//filter css rules by properties
function findRulesByProperties(css, filters) {
let classList = [];
css.forEach(function(rule) {
let selector = rule.selector;
let props = rule.properties;
let vals = [];
let valsFilter = [];
for (let key in filters) {
let filterName = key;
let filterValue = filters[key];
valsFilter.push(filterValue.toString());
}
for (let i = 0; i < props.length; i++) {
let prop = props[i];
let propName = prop[0];
let propValue = prop[1].trim();
if (valsFilter.indexOf(propValue) != -1) {
vals.push(propValue);
}
}
if (vals.length == valsFilter.length) {
classList.push(selector)
}
});
return `results ${classList.length}: ${classList.join(", ")} || match: ${JSON.stringify(filters)}`;
}
body {
font-family: Georgia;
}
h1 {
font-family: "Lato";
font-weight: 700;
}
h2 {
font-family: "Lato";
font-weight: 400;
}
.classLato400 {
font-family: "Lato";
font-weight: 400;
}
.classLato400italic {
font-family: "Lato";
font-weight: 400;
font-style: italic;
}
.classLato700 {
font-family: "Lato";
font-weight: 700;
}
.classRoboto400 {
font-family: "Roboto";
font-weight: 400;
}
In the above example we're searching for all rules containing font-family:Lato (and other filters like font-weight or font-style).
You could paste your main css file in the snippet to get a list of selectors matching all criteria.
Replace external #font-face rules
If I got you right and your ultimate goal is to replace externally hosted font files with local ones (e.g. to improve GDPR compliance), you don't need to get every css font-family class reference.
The most important part are the #font-face rules that are actually responsive for downloading font files.
OK that's not perfectly correct since a font file won't be downloaded unless some DOM element uses this particular font-family.
In other words, your css might actually contain a plethora of unused font-families – on the other hand if they aren't used anywhere they won't be downloaded (so browsers have a default lazyloading method for fonts).
Example: you need to replace google webfonts with locally hosted fonts
Open your devTools and switch to the "Font" tab
Now you can see a list of all downloaded font files as well as their origin (URL) and their "Initiator" – the source file, that initiated the file download. Usually this would be a <link> stylesheet reference or an #import rule within your css, but it can also be a javaScript font loader method.
By inspecting the "URL" column, we can clearly see if a font is loaded from an external host.
Clicking the "Initiator" row/entry will open the file triggering the download – this will either be a file (like a .css) you want to completely remove or just a portion of a css file (take a closer look at #font-face rules, especially the src properties).
Following the google webfonts use case/example
(actually pretty similar to other font delivery services)
obviously we need to get local copies of my previously externally hosted font files –
google web font helper might be helpful to get a ready-to-go #font-face css and a download package including all needed font files.
we need to delete all css files or #font-face or #import rules that are still referring to external file sources and replace them with custom local font file urls.
Possible shortcuts to remove external font file references:
Check your HTML/template head for undesired elements like these
(so containing an external URL like "fonts.googleapis.com"):
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght#400;700" rel="stylesheet">
or within inline <style> tags for #import rules like #import url('https://fonts.googleapis.com/css2?family=Roboto:wght#400;700')
or similar #import rules within your main css file – they should usually be found at the top of your css code.
Delete these references and replace with custom #font-face rules like so (example is based on google web font helper output using "Roboto" font-family and font-weights 400+700 ... regular and bold).
/* roboto-regular - latin */
#font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url('fonts/roboto-v30-latin-regular.woff2') format('woff2');
}
/* roboto-700 - latin */
#font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
src: url('fonts/roboto-v30-latin-700.woff2') format('woff2');
}
Inspect the network tab once again
If everthing is working fine we should see the locally retrieved font files for each style (e.g. regular, bold, italic, bold italic etc.)
If not: double check your file paths!
Seriously, this is probably the most common source of errors. (e.g "../fonts/" or "./fonts/" or just "fonts/").

Workaround for CSS variables in IE?

I'm currently developing a web application in Outsystems in which I have the need to customize the CSS, in which I'm using variables. I need to guarantee the app works cross-browser, including in Internet Explorer. IE doesn't support CSS variables, as you can see in the picture below from this source.
Since I have to use CSS variables, is there any workaround for the usage of variables in IE?
Yes there is a way, the same way you make any css compatible: use a specific css fallback that is supported by the browser.
body {
--text-color: red;
}
body {
color: red; /* default supported fallback style */
color: var(--text-color); /* will not be used by any browser that doesn't support it, and will default to the previous fallback */
}
This solution is incredibly redundant and 'almost' defeats the purpose of css variables....BUT it is necessary for browser compatibility. Doing this would essentially make the css variables useless but I implore you to still use them because it will serve as an important reminder to the fact that these values are referenced elsewhere and need to be updated in all cases, otherwise you forget to update every related occurrence of 'color' and then you have inconsistent styling because relevant css values are out of sync. The variable will serve more as a comment but a very important one.
There is a polyfill, which enables almost complete support for CSS variables in IE11:
https://github.com/nuxodin/ie11CustomProperties
(i am the author)
The script makes use of the fact that IE has minimal custom properties support where properties can be defined and read out with the cascade in mind.
.myEl {-ie-test:'aaa'} // only one dash allowed! "-"
then read it in javascript:
getComputedStyle( querySelector('.myEl') )['-ie-test']
From the README:
Features
handles dynamic added html-content
handles dynamic added , -elements
chaining --bar:var(--foo)
fallback var(--color, blue)
:focus, :target, :hover
js-integration:
style.setProperty('--x','y')
style.getPropertyValue('--x')
getComputedStyle(el).getPropertyValue('--inherited')
Inline styles: <div ie-style="--color:blue"...
cascade works
inheritance works
under 3k (min+gzip) and dependency-free
Demo:
https://rawcdn.githack.com/nuxodin/ie11CustomProperties/b851ec2b6b8e336a78857b570d9c12a8526c9a91/test.html
In case someone comes across this, has a similar issue where I had it set like this.
a {
background: var(--new-color);
border-radius: 50%;
}
I added the background colour before the variable so if that didn't load it fell back on the hex.
a {
background: #3279B8;
background: var(--new-color);
border-radius: 50%;
}
Yes, so long as you're processing root-level custom properties (IE9+).
GitHub: https://github.com/jhildenbiddle/css-vars-ponyfill
NPM: https://www.npmjs.com/package/css-vars-ponyfill
Demo: https://codepen.io/jhildenbiddle/pen/ZxYJrR
From the README:
Features
Client-side transformation of CSS custom properties to static values
Live updates of runtime values in both modern and legacy browsers
Transforms <link>, <style>, and #import CSS
Transforms relative url() paths to absolute URLs
Supports chained and nested var() functions
Supports var() function fallback values
Supports web components / shadow DOM CSS
Watch mode auto-updates on <link> and <style> changes
UMD and ES6 module available
TypeScript definitions included
Lightweight (6k min+gzip) and dependency-free
Limitations
Custom property support is limited to :root and :host declarations
The use of var() is limited to property values (per W3C specification)
Here are a few examples of what the library can handle:
Root-level custom properties
:root {
--a: red;
}
p {
color: var(--a);
}
Chained custom properties
:root {
--a: var(--b);
--b: var(--c);
--c: red;
}
p {
color: var(--a);
}
Nested custom properties
:root {
--a: 1em;
--b: 2;
}
p {
font-size: calc(var(--a) * var(--b));
}
Fallback values
p {
font-size: var(--a, 1rem);
color: var(--b, var(--c, var(--d, red)));
}
Transforms <link>, <style>, and #import CSS
<link rel="stylesheet" href="/absolute/path/to/style.css">
<link rel="stylesheet" href="../relative/path/to/style.css">
<style>
#import "/absolute/path/to/style.css";
#import "../relative/path/to/style.css";
</style>
Transforms web components / shadow DOM
<custom-element>
#shadow-root
<style>
.my-custom-element {
color: var(--test-color);
}
</style>
<div class="my-custom-element">Hello.</div>
</custom-element>
For the sake of completeness: w3c specs
Hope this helps.
(Shameless self-promotion: Check)
Make a seperate .css file for your variables. Copy/paste the contents of the variable.css file to the end of your main.css file. Find and replace all the variable names in the main.css file to the hex code for those variables. For example: ctrl-h to find var(--myWhiteVariable) and replace with #111111.
Side note: if you keep the :root{ } in the main.css file and just comment it out, you can use that to track those hex codes later if you want to update your fallback colors.
Another way to do it is declaring colors in a JS file (in my case I'm using react) and then just use the variable you defined in the JS file.
For example:
in globals.js
export const COLORS = {
yellow: '#F4B400',
yellowLight: '#F4C849',
purple: '#7237CC',
purple1: '#A374EB',
}
in your file
import { COLORS } from 'globals'
and then just use COLORS.yellow, COLORS.purple, etc.
body {
--text-color : red; /* --text-color 정의 */
}
body {
color: var(--text-color, red); /* --text-color 정의되지 않으면 red로 대체됨 */
}
body {
color: var(--text-color, var(--text-color-other, blue));
/* --text-color, --text-color-other 가 정의되지 않으면 blue로 대체됨 */
}
There is no way yet in "normal" css but take a look at sass/scss or less.
here is a scss example
$font-stack: Helvetica, sans-serif;
$primary-color: #333;
body {
font: 100% $font-stack;
color: $primary-color;
}
I recommend setting your css variables as sass variables, then using sass interpolation to render the color in your elements.
:root {
--text-color: #123456;
}
$text-color: var(--text-color);
body {
color: #{$text-color};
}
If im not wrong there is a workaround, the CSS #ID Selector. Which should work for IE > 6 I guess.. So you can
.one { };
<div class="one">
should work as
#one {};
<div id="one">

How do I write font in LESS CSS?

I'm in a bit of a pickle. I just picked up WinLess (the compiler I'm using) about two days ago and I've just vaguely learned the basis of LESS. Anyways, I'm having a problem with this bit of code:
// Font
#verdana: font-family:"verdana";
#sans: font-family:"sans-serif";
When I do compile this I get this message:
ParseError: Unrecognized input in on line 4, column 3:
3 // Font
4 #verdana: font-family:"verdana";
5 #sans: font-famlit:"sans-serif";
Can anyone give me a hand? Thanks!
For LESS 1.7+
They have now added rulesets that work like this:
#verdana: {font-family:"verdana"};
.myClass {
#verdana();
}
Note the syntax: you pass a bracketed {} set of properties, and access it with the parenthesis () after the variable name, much like a mixin. As you can see, it also functions a lot like a mixin (similar to lucian's answer), but it has the added value that it can be passed as an argument, so this is possible:
LESS
#verdana: {font-family:"verdana"};
.myMix(#font) {
#font();
}
.test {
.myMix(#verdana);
}
CSS Output
.test {
font-family: "verdana";
}
You can declare it like this: .myfont{ font-family:"some family, some generic family"} and then use it like this: div{ .myfont; }
i only know it in this way :
#variable_name: 'individual_font_name', Verdana, sans-serif;
a font variable with a name, a font name like "My_Verdana" and the main font and the second as example for a failure of the main-font.
hope this helps, greetings.

LESS: mixin with non-class ruleset

In LESS, I am trying to define the button.c2 ruleset, to be applied to <button class="c2">...</button> elements. This ruleset is mainly based on the button.c1 ruleset. However, the following code produces a ParseError:
button.c2 {
button.c1;// Trying to import a ruleset
... // additional rules, such as font-size: 120%;
}
It seems to me that the ParseError is caused by the fact that the imported ruleset does not refer to a class or ID ("button.c1" does not start with a "." or a "#"). From the LESS documentation:
Any CSS class or id ruleset can be mixed-in that way.
Why is there such a limitation? Is there any way around it?
The limitation might just be ease of parsing, since . or # don't show up as the first character of a normal style rule the parser automatically knows that those should be mixed in.
You could get around it by defining .c1 as a mixin and using it for both buttons:
.c1() {
// c1 rules
}
button.c1 {
.c1;
}
button.c2 {
.c1;
// additional rules
}
However, coming up in LESS 1.4.0 are :extend selectors, which will allow you to do what you want. The syntax would be:
button.c2:extend(button.c1) {
// additional rules
}

css framework and color schemes

I can't find a CSS frameowrk that lets me plugin my own color scheme.
For example, in my current project I imported blueprint/screen.css. To change the color of the font, I have to change body { color..}, h2 { color..}, h3 {color..}, etc.
Isn't there something out there that provides nice css defaults, but also lets may play around with color schemes?
You can try http://lesscss.org/.
It allows you to use things such as variables in your CSS, which sounds like exactly what you're after:
// LESS
#color: #4D926F;
#header {
color: #color;
}
h2 {
color: #color;
}
Create your own CSS file that loads after Blueprint. You'll need to redefine everything, but the Blueprint defaults aren't far off from the browser defaults.
If you have multiple color schemes you want to quickly switch out, set the class on your <html> tag. Then use your CSS file to define custom styles for each.
CSS:
.theme1 body {
font-family: Tahoma;
color: #500;
}
.theme2 body {
font-family: Verdana;
color: #050;
}
For the first theme:
<html class="theme1">
For the second theme:
<html class="theme2">
Couldn't you just edit the blueprint/screen.css file? You could also use something like SASS and then create all the defaults at the top of the stylesheet and then have it go throughout the stylesheet when it renders it.

Resources