I am trying to create a sass map to apply an icon class to a nested element.
My elements are a div (which will have the class applied) and a span inside that "holds" the icon:
<div class="pboi-tour_adventure">
<span></span>
</div>
In actuality, it's part of an AngularJS ng-for, if that matters:
<div class="icon-sample" ng-repeat="icon in maps.icons">
<div class="{{icon}}">
<span></span>
</div>
</div>
So, following the guidance in this article, I am using a sass map and an each loop:
$poi-icons: (
tour_adventure: "\e96c",
tour_beach: "\e96d",
tour_tourist_attractions: "\e96e",
tour_tourist_infomation: "\e96f"
);
#each $name, $icon in $poi-icons {
.pboi-#{$name} {
span::before {
content: $icon;
}
}
}
My issue is that when I inspect the html, there is clearly no "before" pseudo element in the inspector, and the compiled css is
.pboi-tour_adventure span::before { content: ""; }
When I expect it to be:
.pboi-tour_adventure span::before { content: "\e96c"; }
Of course, the icon is not working because of this.
NOTE: Even if I flatten it out to match the example in the article:
#each $name, $icon in $poi-icons {
.pboi-#{$name}::before {
content: $icon;
}
}
The same thing happens.
Simplified case:
I put this into Sassmeister:
$foo: 'bar';
$icons: (
busi_ATM: 'foo',
busi_bank: $foo,
busi_business: '\e902'
);
#each $name, $icon in $icons {
.pboi-#{$name}:before {
content: $icon;
}
}
and I get
.pboi-busi_ATM:before {
content: "foo";
}
.pboi-busi_bank:before {
content: "bar";
}
.pboi-busi_business:before {
content: "";
}
Why does Sass pass through the first 2 strings but not '\e902'?
Also, and more confusing, is when I use the loop the :before shows up in the compiled CSS file, but not in the Web Inspector in the browser. This I don't get at all.
Sass converts the characters internally to the exact ligature/symbol/icon. That's the reason you are seeing a ""; in the inspector. It doesn't mean Sass is not able to parse the character. It means that those characters are not readable in the developer tools.
If for some reason you are seeing the same box in the output, then you are not setting the font properly, or your font is still trying to load. You can check the computed glyph in dev tools to verify what font is loaded.
If you still want to see the actual Unicode, then you can do a simple test and verify it. you can pass only 'e902' and append the '\' in front of it. I have attached the snippet for it
#charset 'UTF-8';
$poi-icons: (
tour_adventure: "e96c",
tour_beach: "e96d",
tour_tourist_attractions: "e96e",
tour_tourist_infomation: "e96f"
);
#each $name, $icon in $poi-icons {
.pboi-#{$name} {
span::before {
content: unquote("\"")+unquote(str-insert($icon,"\\", 1))+unquote("\"");
}
}
}
reference sass issue:
https://github.com/sass/sass/issues/1395
Related
How can I hide the symbol "#" and colon ":" of order time in the WooCommerce Admin Add New order page? I've inspected the elements in Chrome to find a class or ID but not found. I know I can hide it with this code.
add_action('admin_head', 'my_custom_style');
function my_custom_style() {
echo '<style>
div {display: none;}
</style>';
}
enter image description here
I don't see a purpose of doing this, but if you really wish to do this, you can use input name css selectors to do it.
I have used CSS and JS both to get rid of # and :.
add_action('admin_footer', 'bks_my_custom_style');
function bks_my_custom_style() {
echo '
<script>
jQuery(function() {
var text = jQuery(".order_data_column_container .form-field").contents().filter(function() {
return this.nodeType == Node.TEXT_NODE;
}).remove();
});
</script>
<style>
input[name="order_date_minute"],
input[name="order_date_hour"]
{
display: none;
}
</style>
';
}
The code goes inside functions.php file.
Tested and WORKS.
Screenshot :
I am using css variables in my angular7 application. Everything works fine on other browsers. But IE is not supporting css variables. Is there a way to make it work on IE. Can Autoprefixer do this?
color: var(--primary, #7F583F);
According to caniuse.com, of current browsers only IE, Edge (older versions) and Opera Mini do not support CSS variables. This polyfil appears to work on all three really well.
This is an attempt at a very basic CSS variables (custom properties) polyfil. In reality this is more of a partial polyfill as it will not cover variables inside of variables, DOM scoping or anything else "fancy". Just taking variables declared anywhere in the CSS and then re-parsing the CSS for var() statements and replacing them in browsers that don't natively support CSS variables.
I try to test this polyfil in IE 11 and looks like it is working with it.
/*!
* css-var-polyfill.js - v1.0.0
*
* Copyright (c) 2018 Aaron Barker <http://aaronbarker.net>
* Released under the MIT license
*
* Date: 2018-03-09
*/
let cssVarPoly = {
init: function() {
// first lets see if the browser supports CSS variables
// No version of IE supports window.CSS.supports, so if that isn't supported in the first place we know CSS variables is not supported
// Edge supports supports, so check for actual variable support
if (window.CSS && window.CSS.supports && window.CSS.supports('(--foo: red)')) {
// this browser does support variables, abort
console.log('your browser supports CSS variables, aborting and letting the native support handle things.');
return;
} else {
// edge barfs on console statements if the console is not open... lame!
console.log('no support for you! polyfill all (some of) the things!!');
document.querySelector('body').classList.add('cssvars-polyfilled');
}
cssVarPoly.ratifiedVars = {};
cssVarPoly.varsByBlock = {};
cssVarPoly.oldCSS = {};
// start things off
cssVarPoly.findCSS();
cssVarPoly.updateCSS();
},
// find all the css blocks, save off the content, and look for variables
findCSS: function() {
let styleBlocks = document.querySelectorAll('style:not(.inserted),link[rel="stylesheet"]');
// we need to track the order of the style/link elements when we save off the CSS, set a counter
let counter = 1;
// loop through all CSS blocks looking for CSS variables being set
[].forEach.call(styleBlocks, function(block) {
// console.log(block.nodeName);
let theCSS;
if (block.nodeName === 'STYLE') {
// console.log("style");
theCSS = block.innerHTML;
cssVarPoly.findSetters(theCSS, counter);
} else if (block.nodeName === 'LINK') {
// console.log("link");
cssVarPoly.getLink(block.getAttribute('href'), counter, function(counter, request) {
cssVarPoly.findSetters(request.responseText, counter);
cssVarPoly.oldCSS[counter] = request.responseText;
cssVarPoly.updateCSS();
});
theCSS = '';
}
// save off the CSS to parse through again later. the value may be empty for links that are waiting for their ajax return, but this will maintain the order
cssVarPoly.oldCSS[counter] = theCSS;
counter++;
});
},
// find all the "--variable: value" matches in a provided block of CSS and add them to the master list
findSetters: function(theCSS, counter) {
// console.log(theCSS);
cssVarPoly.varsByBlock[counter] = theCSS.match(/(--.+:.+;)/g) || [];
},
// run through all the CSS blocks to update the variables and then inject on the page
updateCSS: function() {
// first lets loop through all the variables to make sure later vars trump earlier vars
cssVarPoly.ratifySetters(cssVarPoly.varsByBlock);
// loop through the css blocks (styles and links)
for (let curCSSID in cssVarPoly.oldCSS) {
// console.log("curCSS:",oldCSS[curCSSID]);
let newCSS = cssVarPoly.replaceGetters(cssVarPoly.oldCSS[curCSSID], cssVarPoly.ratifiedVars);
// put it back into the page
// first check to see if this block exists already
if (document.querySelector('#inserted' + curCSSID)) {
// console.log("updating")
document.querySelector('#inserted' + curCSSID).innerHTML = newCSS;
} else {
// console.log("adding");
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = newCSS;
style.classList.add('inserted');
style.id = 'inserted' + curCSSID;
document.getElementsByTagName('head')[0].appendChild(style);
}
};
},
// parse a provided block of CSS looking for a provided list of variables and replace the --var-name with the correct value
replaceGetters: function(curCSS, varList) {
// console.log(varList);
for (let theVar in varList) {
// console.log(theVar);
// match the variable with the actual variable name
let getterRegex = new RegExp('var\\(\\s*' + theVar + '\\s*\\)', 'g');
// console.log(getterRegex);
// console.log(curCSS);
curCSS = curCSS.replace(getterRegex, varList[theVar]);
// now check for any getters that are left that have fallbacks
let getterRegex2 = new RegExp('var\\(\\s*.+\\s*,\\s*(.+)\\)', 'g');
// console.log(getterRegex);
// console.log(curCSS);
let matches = curCSS.match(getterRegex2);
if (matches) {
// console.log("matches",matches);
matches.forEach(function(match) {
// console.log(match.match(/var\(.+,\s*(.+)\)/))
// find the fallback within the getter
curCSS = curCSS.replace(match, match.match(/var\(.+,\s*(.+)\)/)[1]);
});
}
// curCSS = curCSS.replace(getterRegex2,varList[theVar]);
};
// console.log(curCSS);
return curCSS;
},
// determine the css variable name value pair and track the latest
ratifySetters: function(varList) {
// console.log("varList:",varList);
// loop through each block in order, to maintain order specificity
for (let curBlock in varList) {
let curVars = varList[curBlock];
// console.log("curVars:",curVars);
// loop through each var in the block
curVars.forEach(function(theVar) {
// console.log(theVar);
// split on the name value pair separator
let matches = theVar.split(/:\s*/);
// console.log(matches);
// put it in an object based on the varName. Each time we do this it will override a previous use and so will always have the last set be the winner
// 0 = the name, 1 = the value, strip off the ; if it is there
cssVarPoly.ratifiedVars[matches[0]] = matches[1].replace(/;/, '');
});
};
// console.log(ratifiedVars);
},
// get the CSS file (same domain for now)
getLink: function(url, counter, success) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.overrideMimeType('text/css;');
request.onload = function() {
if (request.status >= 200 && request.status < 400) {
// Success!
// console.log(request.responseText);
if (typeof success === 'function') {
success(counter, request);
}
} else {
// We reached our target server, but it returned an error
console.warn('an error was returned from:', url);
}
};
request.onerror = function() {
// There was a connection error of some sort
console.warn('we could not get anything from:', url);
};
request.send();
}
};
cssVarPoly.init();
:root {
--externalcolor: red;
--samename: orange;
--samename: #0f0;
--foo: green;
--FOO: #0f0;
--halfsuccess: orange;
--success: green;
--success2: #0f0;
}
html {
font-family: var(--fontsans);
}
.success {
color: green;
}
.fail {
color: red;
}
span {
display: inline-block;
margin: 5px;
}
.samename {
color: var(--samename);
}
.demo1 {
color: #f00;
color: var(--success);
}
.demo2 {
color: #f00;
color: var( --success2);
}
.demo3 {
color: #f00;
color: var(--halfsuccess);
color: var(--success);
}
.demo4 {
color: red;
border-color: #f00;
}
.inlineoverlink {
color: #f00;
}
p {
padding: var(--spacing-l);
}
.lower {
color: var(--foo);
}
.upper {
color: var(--FOO);
}
.externalcolor {
color: var(--externalcolor);
}
.fallback {
color: #f00;
color: var(--wrongname, green);
}
// for the top documentation
.supports {
color: green;
.no {
display:none;
}
}
.showforpolyfill {
display:none;
}
.cssvars-polyfilled {
.supports {
color: red;
.no {
display:inline;
}
}
.showforpolyfill {
display:inline;
}
.hideforpolyfill {
display:none;
}
}
.hide,
.hide-the-docs .documentation {
display:none;
}
/* declare some font-family stuff at bottom of file to reflect on stuff above it*/
:root {
--fontsans: arial;
}
<!-- Copy below for codepen update -->
<h1>CSS Variables Polyfill</h1>
<p>This is now managed (and available for PRs) at https://github.com/aaronbarker/css-variables-polyfill.</p>
<p>
This is an attempt at a very basic CSS variables (custom properties) polyfil. In reality this is more of a <em>partial</em> polyfill as it will not cover variables inside of variables, DOM scoping or anything else "fancy". Just taking variables declared anywhere in the CSS and
then re-parsing the CSS for var() statements and replacing them in browsers that don't natively support CSS variables.
</p>
<p>According to caniuse.com, of current browsers only IE, Edge and Opera Mini do not support CSS variables. This polyfil appears to work on all three really well. I don't see why this wouldn't work on older browsers as well, but I haven't been able to test it on them yet.</p>
<p>As far as we can tell your browser <span class="supports">does <span class="no">not</span> support</span> native CSS variables. <span class="showforpolyfill">That means if you see green tests results below, it is thanks to the polyfill :).</span> <span class="hideforpolyfill">All the green test results below are actually native CSS Variable support. Good job using a good browser :)</span></p>
<h3>Does this work on externally CSS files?</h3>
<p>Yes!</p>
<h3>Even ones loaded from another domain?</h3>
<p>To go across domain, CSS needs to be served up with <code>Access-Control-Allow-Origin:*</code> headers.</p>
</div>
Toggle documentation (for Opera Mini vs Codepen issue)
<style>
:root {
--newcolor: #0f0;
}
.inlineoverlink {
color: var(--success2);
}
</style>
<h2>Tests</h2>
<p>On mosts tests (unless otherwise noted) success will be green text. We start with a <code>color:red;</code> and then override it with a <code>color:var(--success);</code> (or similar) which is green.</p>
<ul>
<li><span class="samename">declare same variable over and over</span></li>
<li><span class="demo1">no whitespace on var() calls</span></li>
<li><span class="demo2">whitespace on var() calls</span></li>
<li><span class="demo3">Multiple variables in same call. orange means first var worked, green var worked</span></li>
<li><span class="inlineoverlink">orange if link won, green if style after link won</span></li>
<li><span class="lower">--foo: lowercase foo</span></li>
<li><span class="upper">--FOO: uppercase FOO</span></li>
<li><span class="fallback">uses fallback <code>--var(--wrongname, green)</code></span></li>
<li><span class="demo-import">css declared in an <code>#import</code></span> - not polyfilled yet. Identfied with a suggested fix, but will require a bit of a re-write (to use document.styleSheets), so haven't done it yet.</li>
</ul>
<h2>Tests on external, cross-domain file</h2>
<div class="documentation">
<p><strong>Edge</strong> appears to be working well on Edge 13. Edge 12 was having some problems.</p>
<p><strong>Opera mini</strong> seems to work well too. This demo fails because not all the page is displayed, but I think that is a codepen issue, not a polyfill issue. When the upper documentation is removed, all tests display well.</p>
<p><strong>IE 11</strong> seems to do fine.</p>
</div>
<ul>
<li><span class="demo4">Gets stuff from external .css file. Should start red and change to green on LINK load. border proves the CSS loaded, missing colors means script didn't get parsed and reinserted</span></li>
<li><span class="externalcolor">--externalcolor: should start red and change to green on LINK load</span></li>
<li><span class="externalfallback">uses fallback. should be green</span></li>
</ul>
<p>Another set of text under the test for Opera Mini testing.</p>
<!-- Copy above for codepen update -->
Testing result:
References:
(1) Codepen example link
(2) aaronbarker/css-variables-polyfill
I have a number of SASS variables that reference SVG icons. Below is a simplified example:
I would like to be able to loop through a list of strings and compose the SASS variable name in a way that's presented using the background style of the icon-checkbox class. My approach so far does not work. Can I anyone show me how/if this can be done in SASS.
$icon-trash-grey: "data:image/svg+xml;charset=UTF-8,<svg data>";
$icon-save-grey: "data:image/svg+xml;charset=UTF-8,<svg data>";
$icon-list: trash, save; // list of icon names
.icon-checkbox {
each $key in $icon-list {
background: url($icon-#{$key}-grey);
// other styles
}
}
Thanks in advance
The following SASS
$icon-trash-grey: "data:image/svg+xml;charset=UTF-8,<svg data>";
$icon-save-grey: "data:image/svg+xml;charset=UTF-8,<svg data>";
$icon-map: (
trash: $icon-trash-grey,
save: $icon-save-grey,
);
.icon{
#each $key, $value in $icon-map {
&.#{$key} {
background: url($value);
}
}
}
renders this CSS
.icon.trash {
background: url("data:image/svg+xml;charset=UTF-8,<svg data>");
}
.icon.save {
background: url("data:image/svg+xml;charset=UTF-8,<svg data>");
}
You can have a look, make edits or fork this Sassmeister
Try this :
$icon-list : (
"icon-trash-grey": "data:image/svg+xml;charset=UTF-8,<svg data>",
"icon-save-grey": "data:image/svg+xml;charset=UTF-8,<svg data>"
);
#each $key, $val in $icon-list {
.#{$key} {
background: url($val);
}
}
Is there a selector that specifies CSS to only applied when matching a specific URL or part of URL?
For example, here is my CSS stylesheet:
p {
color: green;
}
url("home.html") {
color: blue;
}
url("about.html") {
color: yellow;
}
path("/path/index*") {
color: indigo;
}
When the user visits home.html I want the home.html selector to be applied. When I'm on the about.html URL I want the about.html to be applied.
CSS media queries allow you to switch to a different set of styles when the width of the view changes. It also lets you specify a different set of styles when the user is going to view on the screen or send it to a printer.
My question again is, "Is it possible to specify a different set of styles depending on the URL or values in the URL." So it's not a question of how to do what I'm asking but if it's possible to.
I am using a CMS and it has a theme that allows you to add your own CSS. There is one stylesheet. That's it. Not two but one.
And I have one page that has specific CSS to that page and only that page. That is the origin of this question. There may be a thousand workarounds but my question is not about the workarounds.
However since this has been answered I do not mind workaround answers related to the question.
It looks like the #document rule was proposed for just this case but it was removed from CSS3 spec and planned for CSS4. From my tests it does not appear to be supported and it's not listed on caniuse at the time of this posting.
The syntax is as follows:
#document url("http://www.example.com/widgets/") {
body {
color: white;
background: tomato;
}
}
/* The above applies styles only to the page at the given URL */
#document url-prefix("http://www.example.com/widgets/") {
/*
Styles written here are applied to all URLs that
begin with 'http://www.example.com/widgets/'
*/
}
#document regexp("https:.*") {
/* Styles written here are applied to all URLs that begin with 'https:' */
}
Test code using #media query for comparison:
var styleTag = document.createElement ("style");
document.head.appendChild (styleTag);
var sheet = styleTag.sheet;
sheet.insertRule ("#media (min-width:600px) { html {color:red}}", 0);
console.log(document.styleSheets.length);
Results:
// no errors, stylesheet is added
Test code testing #document rule:
var styleTag = document.createElement ("style");
document.head.appendChild (styleTag);
var sheet = styleTag.sheet;
sheet.insertRule ("#document url('http://www.google.com') { html {color:red}}", 0);
Results:
/*
Exception: SyntaxError: An invalid or illegal string was specified
#Scratchpad/3:4:0
*/
TIL about #document thanks to #BoltClock
More info
You could attach a data-url custom attribute to an element's parent tag, either in the HTML or using JavaScript, and then query that data's value in CSS. Here's a working example:
const urlHolder = document.getElementById("url-holder");
document.getElementById("changer").onclick = () => {
urlHolder.dataset.url = "https://mywebsite.com/about"
};
#url-holder[data-url*="blog"] .url-based{
background-color: red;
}
#url-holder[data-url*="blog"] .url-based::after{
content: "You're on the blog page!"
}
.url-based::after{
content: "You're not on the blog page."
}
.box{
width: 200px;
height: 200px;
border: 2px solid black;
margin: 5px;
}
<button id="changer" type="button">Change data-url to "https://mywebsite.com/about"</button>
<div id="url-holder" data-url="https://mywebsite.com/blog">
<div class="box url-based"></div>
<div class="box not-url-based"></div>
</div>
No idea how performant such a solution would be though.
To be sad there is no pseudo classes to select element's based on URL.The only way you can do it is by adding class to the body tag or specific element and then override the CSS.
if just for only HTML, use jQuery
<script>
var currentLocation = window.location.pathname;
if (currentLocation == 'home.html'){
$('head').append('<link href="home-style.css" rel="stylesheet">');
} else if (currentLocation == 'about.html'){
$('head').append('<link href="about-style.css" rel="stylesheet">');
} else {
$('head').append('<link href="index-style.css" rel="stylesheet">');
}
</script>
If use you WordPress:
Add your theme function.php
function site_stil_script() {
if ($_SERVER['REQUEST_URI']=='/YOURPAGE/') {
wp_enqueue_style( 'theme_css', get_template_directory_uri() . '/assets/css/theme-difrent-style.css', array(), '20120208', 'all' );
} else {
wp_enqueue_style( 'theme_css', get_template_directory_uri() . '/assets/css/theme-style.css', array(), '20120208', 'all' );
}
//other lines....
}
add_action( 'wp_enqueue_scripts', 'site_stil_script' );
I'm trying to add an asterisk before the placeholders of required inputs. I was using input::-moz-placeholder:before which I found on this StackOverflow post and it was working the last time I checked in January. However, it seems that it is no longer supported. Here's what I have going:
div.interactFieldRequired {
input::-webkit-input-placeholder:before {
content:'* ';
color: $error-color;
}
input:-moz-placeholder:before {
content:'* ';
color: $error-color;
}
input::-moz-placeholder:before {
content:'* ';
color: $error-color;
}
input:-ms-input-placeholder:before {
content:'* ';
color: $error-color;
}
input::-moz-selection:before {
content:'* ';
color: $error-color;
}
input[type="text"]:before {
content:'* ';
color: $error-color;
}
}
This is working in every browser except Firefox and IE10. I cannot alter the HTML, although I can use Javascript. However, I'd prefer to do this with scss.
That is strange indeed. I haven't found any reasons why placeholder css support has been removed from Firefox - this applies for Developer Edition too. So if you are OK with JS, you can take advantage of JavaScript document.getElementsByTagName() method and loop through this list and concat() each input's placeholder to an asterisk , like so:
var asterisk = "* ",
inputList = document.getElementsByTagName("input");
for(i=0; i < inputList.length; i++) {
inputList[i].placeholder = asterisk.concat(inputList[i].placeholder);
}
It has a wide support amongst the browsers. I've made an example of that on jsfiddle
Hope this helps