I was working on a Foundation 5 project that turned out to have an outdated _global.scss component. I was trying to get range sliders working, but they were mysteriously not. Turns out, I was missing the following 2 lines of CSS:
meta.foundation-data-attribute-namespace {
font-family: false;
}
Can someone explain these lines to me? The Docs say this:
// Used to provide media query values for javascript components.
// Forward slash placed around everything to convince PhantomJS to read the value.
meta.foundation-mq-small {
font-family: "/" + unquote($small-up) + "/";
width: lower-bound($small-range);
}
meta.foundation-mq-medium {
font-family: "/" + unquote($medium-up) + "/";
width: lower-bound($medium-range);
}
meta.foundation-mq-large {
font-family: "/" + unquote($large-up) + "/";
width: lower-bound($large-range);
}
meta.foundation-mq-xlarge {
font-family: "/" + unquote($xlarge-up) + "/";
width: lower-bound($xlarge-range);
}
meta.foundation-mq-xxlarge {
font-family: "/" + unquote($xxlarge-up) + "/";
width: lower-bound($xxlarge-range);
}
meta.foundation-data-attribute-namespace {
font-family: #{$namespace};
}
Thanks.
Some of Foundation's JavaScript needs to know about the different media query ranges definitions.
It is particularly true for some components such as interchange that is literally about things behaving differently depending on the viewport.
But foundation also lets you customize what the different breakpoints are.
In order to make have the value set in one single place, foundation uses this little trick of adding the media query breakpoint definition of a meta with a matching class name.
Here's how it works, for the breakpoint $small-up (it's the same for the others):
Let's say you keep the default value: $small-up: only screen.
The JS in foundation will do the following:
create a <meta> tag with the class .foundation-mq-small
read the value of the CSS property font-family
now it knows what is the definition of the $small-up media query
it's now accessible to the different foundation js components that may need it
You can see how it's done in foundation.js
The reason why it's using font-family is that it's one of the rare CSS properties that doesn't have a defined set of values. If they had used the property text-align, the browser would have ignored the invalid value and the JS would have gotten something like left in return.
Related
I am trying to use CSS variables in media query and it does not work.
:root {
--mobile-breakpoint: 642px;
}
#media (max-width: var(--mobile-breakpoint)) {
}
From the spec,
The var() function can be used in place of any part of a value in
any property on an element. The var() function can not be used as
property names, selectors, or anything else besides property values.
(Doing so usually produces invalid syntax, or else a value whose
meaning has no connection to the variable.)
So no, you can't use it in a media query.
And that makes sense. Because you can set --mobile-breakpoint e.g. to :root, that is, the <html> element, and from there be inherited to other elements. But a media query is not an element, it does not inherit from <html>, so it can't work.
This is not what CSS variables are trying to accomplish. You can use a CSS preprocessor instead.
As Oriol has answered, CSS Variables Level 1’s var() cannot currently be used in media queries. However, there have been recent developments that will address this problem. Once CSS Environment Variables Module Level 1 is standardized and implemented, we’ll be able to use env() variables in media queries in all modern browsers.
The CSS Working Group (CSSWG) codified env() in a new standard (currently at a draft stage): the CSS Environment Variables Module Level 1 (see this GitHub comment and this comment for more info). The draft calls out variables in media queries as an explicit use case:
Because environment variables don’t depend on the value of anything drawn from a particular element, they can be used in places where there is no obvious element to draw from, such as in #media rules, where the var() function would not be valid.
If you read the specification and have a concern, or if you want to voice your support for the media-query use case, you can do so in issue #2627, in issue #3578, or in any CSS GitHub issue labeled with “css-env-1”.
GitHub issue #2627 and GitHub issue #3578 are devoted to custom environmental variables in media queries.
Original answer from 2017-11-09:
Recently, the CSS Working Group decided that CSS Variables Level 2 will support user-defined environment variables using env(), and they will try to make them be valid in media queries. The Group resolved this after Apple first proposed standard user-agent properties, shortly before the official announcement of iPhone X in September 2017 (see also WebKit: “Designing Websites for iPhone X” by Timothy Horton). Other browser representatives then agreed they would be generally useful across many devices, such as television displays and ink printing with bleed edges. (env() used to be called constant(), but that has now been deprecated. You might still see articles that refer to the old name, such as this article by Peter-Paul Koch.) After some weeks passed, Cameron McCormack of Mozilla realized that these environment variables would be usable in media queries, and Tab Atkins, Jr. of Google then realized that user-defined environment variables would be especially useful as global, non-overridable root variables usable in media queries. Now, Dean “Dino” Jackson of Apple will join Atkins in editing Level 2.
You can subscribe to updates on this matter in w3c/csswg-drafts GitHub issue #1693 (for especially relevant historical details, expand the meeting logs embedded in the CSSWG Meeting Bot’s resolutions and search for “MQ”, which stands for “media queries”).
What you can do however is #media query your :root statement!
:root {
/* desktop vars */
}
#media screen and (max-width: 479px) {
:root {
/* mobile vars */
}
}
Totally works in Chrome, Firefox and Edge at least the latest production versions as of this posting.
One limitation: if you need to access the value as a variable – for example to use in calculations elsewhere – you will need to have a variable, and it requires defining the variable in two places: the media query and variable declaration.
Apparently it's just not possible to use native CSS variables like that. It's one of the limitations.
A clever way to use it is to change your variables in the media-query, to impact all your style. I recommend this article.
:root {
--gutter: 4px;
}
section {
margin: var(--gutter);
}
#media (min-width: 600px) {
:root {
--gutter: 16px;
}
}
One way to achieve what you want is using npm package postcss-media-variables.
If you are fine with using npm packages then you can take a look documentation for same here:
postcss-media-variables
Example
/* input */
:root {
--min-width: 1000px;
--smallscreen: 480px;
}
#media (min-width: var(--min-width)) {}
#media (max-width: calc(var(--min-width) - 1px)) {}
#custom-media --small-device (max-width: var(--smallscreen));
#media (--small-device) {}
The level 5 specification of media queries define Custom Media Queries that does almost what you are looking for. It allows you to define breakpoint similar to how you do with CSS variables and later use them in different places.
Example from the specification:
#custom-media --narrow-window (max-width: 30em);
#media (--narrow-window) {
/* narrow window styles */
}
#media (--narrow-window) and (script) {
/* special styles for when script is allowed */
}
There is still no support for this actually so we have to wait before using this feature.
Short Answer
You can use JavaScript to change the value of media queries and set it to the value of a css variable.
// get value of css variable
getComputedStyle(document.documentElement).getPropertyValue('--mobile-breakpoint'); // '642px'
// search for media rule
var mediaRule = document.styleSheets[i].cssRules[j];
// update media rule
mediaRule.media.mediaText = '..'
Long Answer
I wrote a small script which you can include on your page. It replaces every media rule with a value of 1px with the value of the css variable --replace-media-1px, rules with value 2px with --replace-media-2px and so on. This works for the media queries with, min-width, max-width, height, min-height and max-height even when they are connected using and.
JavaScript:
function* visitCssRule(cssRule) {
// visit imported stylesheet
if (cssRule.type == cssRule.IMPORT_RULE)
yield* visitStyleSheet(cssRule.styleSheet);
// yield media rule
if (cssRule.type == cssRule.MEDIA_RULE)
yield cssRule;
}
function* visitStyleSheet(styleSheet) {
try {
// visit every rule in the stylesheet
var cssRules = styleSheet.cssRules;
for (var i = 0, cssRule; cssRule = cssRules[i]; i++)
yield* visitCssRule(cssRule);
} catch (ignored) {}
}
function* findAllMediaRules() {
// visit all stylesheets
var styleSheets = document.styleSheets;
for (var i = 0, styleSheet; styleSheet = styleSheets[i]; i++)
yield* visitStyleSheet(styleSheet);
}
// collect all media rules
const mediaRules = Array.from(findAllMediaRules());
// read replacement values
var style = getComputedStyle(document.documentElement);
var replacements = [];
for (var k = 1, value; value = style.getPropertyValue('--replace-media-' + k + 'px'); k++)
replacements.push(value);
// update media rules
for (var i = 0, mediaRule; mediaRule = mediaRules[i]; i++) {
for (var k = 0; k < replacements.length; k++) {
var regex = RegExp('\\((width|min-width|max-width|height|min-height|max-height): ' + (k+1) + 'px\\)', 'g');
var replacement = '($1: ' + replacements[k] + ')';
mediaRule.media.mediaText = mediaRule.media.mediaText.replace(regex, replacement);
}
}
CSS:
:root {
--mobile-breakpoint: 642px;
--replace-media-1px: var(--mobile-breakpoint);
--replace-media-2px: ...;
}
#media (max-width: 1px) { /* replaced by 642px */
...
}
#media (max-width: 2px) {
...
}
You can build a media query programmatically using matchMedia:
const mobile_breakpoint = "642px";
const media_query = window.matchMedia(`(max-width: ${mobile_breakpoint})`);
function toggle_mobile (e) {
if (e.matches) {
document.body.classList.add("mobile");
} else {
document.body.classList.remove("mobile");
}
}
// call the function immediately to set the initial value:
toggle_mobile(media_query);
// watch for changes to update the value:
media_query.addEventListener("change", toggle_mobile);
Then, instead of using a media query in your CSS file, apply the desired rules when body has the mobile class:
.my-div {
/* large screen rules */
}
.mobile .my-div {
/* mobile screen rules */
}
As you can read other answers, still not possible to do so.
Someone mentioned custom environmental variables (similar to custom css variables env() instead of var()), and the principle is sound, though there are still 2 major issues:
weak browser support
so far there is no way to define them (but probably will be in the future, as this is so far only an unofficial draft)
I have an Angular component that generates mat-checkbox dynamically at runtime and I need to change the individual background of each checkbox differently with different color and I don't (won't) have the information before hand, only available at runtime.
I have the following ng-template for the checkboxes:
<ng-template #renderCheckbox let-id="id" let-attr="attr">
<mat-checkbox
[checked]="attr.show"
[color]="'custom-' + id"
(change)="onChange($event.checked, attr)">
{{attr.name}}
</mat-checkbox>
</ng-template>
where, attr in the template has the following interface type, these infomation are pulled from Highcharts' series and I didn't want to hardcode the color.
interface LinkedSeriesAttributes {
id: string;
name: string;
index: number;
color: string;
checked: boolean;
}
Since there is no way to create css classes before hand and there is no way to directly apply color to the mat-checkbox, I could only generate the <style>...</style> right at the beginning of my template.
In my component, I have code that will generate the style which would give me something like this:
.mat-checkbox.mat-custom-hello.mat-checkbox-checked .mat-checkbox-background::before {
color: #6E8BC3 !important;
}
.mat-checkbox.mat-custom-world.mat-checkbox-checked .mat-checkbox-background::before {
color: #9ED6F2 !important;
}
...
However, I tried various ways to dump it inside <style> without success. I tried:
<style>{{ dynamicCSSStyles }}</style>
Which, my IDE shows that's an error with the curly braces, although it compiled fine and ran without errors, I got nothing, can't even see the <style> tag.
I also tried to include <style> inside my dynamicCSSStyles variable, and angular just dumped the whole thing out as text...
What's the correct way to generate a <style> in Angular.
I've found a REALLY dirty way of "making this work" but it causes Angular to keep adding the <style> back into the DOM.
First, set encapsulation to ViewEncapsulation.None.
Second, create a function to generate the <style> tag the old fashion way with an id:
updateDynsmicStyleNode() {
const id = 'dynamic-css-styles';
const nativeElm = this.elmRef.nativeElement;
const existing = nativeElm.querySelector(`style#${id}`);
if (!existing) {
const styleTag = document.createElement('style');
styleTag.setAttribute('id', id);
styleTag.innerHTML = this.dynamicCSSStyles;
nativeElm.prepend(styleTag);
} else {
existing.innerHTML = this.dynamicCSSStyles;
}
}
Third, call our function in ngAfterViewChecked:
ngAfterViewChecked() {
this.updateDynsmicStyleNode();
}
I mean while this worked, it is really bad, since moving the mouse around the screen would cause Angular to just continuously reinsert the <style> tag.
Does anyone know some other way more legit to archive this? LOL
You can use ngClass or [class] attribute. Since you can have the styles ready from the component.ts file.
You can do something like this:
Way 1: If you already know what the dynamic ids might be, (like if it always will be 'hello' and 'world')
let dynamicClasses = {};
// Once you get some classes from your logic, you can add them to the object above
dynamicClasses['hello'] = 'custom-hello';
dynamicClasses['world'] = 'custom-world';
// Then in HTML
<mat-checkbox [ngClass]="dynamicClasses"></mat-checkbox>
Way 2: If you dont know what the classes also might be, like if its not always be hello or world, then create a method and call it where required, you might need to do something similar to #codenamezero said.
I am trying to find a way to use alternative headers in Razor Web Pages without using two _SiteLayout pages, with each _SiteLayout rendering a different _header page.
I’m trying to achieve this - If the default.cshtml page is called use header-1, if any other page is called use header-2.
I have tried all sorts of different logic with no joy, including: IsCurrentPage, Request.QueryString, Request.Url; and CurrentPage.Name.
E.G.
#if ((Request.QueryString["Default"] == null))
{
#RenderPage("/shared/_header-1.cshtml")
}
else
{
#RenderPage("/shared/_header-2.chtml")
}
And
#{
var pageUrl = this.Request.Url;
}
#if (pageUrl = "http://mycompany/Default.cshtml/") {
#RenderPage("/shared/_header-1.cshtml");
}
else
{
#RenderPage("/shared/_header-2.cshtml");
}
Does anyone have a simple method to achieve this please?
Although, I spent a long time on this, not long after posting I found an answer thanks to: Erik Philips
Add to _SiteLayout:
#if (IsSectionDefined("customHeader"))
{
#RenderSection("customHeader")
}
else
{
#RenderPage("/shared/_header.cshtml")
}
Add to default page
#section customHeader{
This is custom header
}
The common header doesn't get called in the Default page, because customHeader is specified instead; whereas, all other pages use the normal header
This question already has answers here:
CSS3's attr() doesn't work in major browsers
(5 answers)
Closed 5 years ago.
width: attr(data-width);
I want to know if there's any way it's possible to set a css value using HTML5's data- attribute the same way that you can set css content. Currently it doesn't work.
HTML
<div data-width="600px"></div>
CSS
div { width: attr(data-width) }
There is, indeed, prevision for such feature, look http://www.w3.org/TR/css3-values/#attr-notation
This fiddle should work like what you need, but will not for now.
Unfortunately, it's still a draft, and isn't fully implemented on major browsers.
It does work for content on pseudo-elements, though.
You can create with javascript some css-rules, which you can later use in your styles: http://jsfiddle.net/ARTsinn/vKbda/
var addRule = (function (sheet) {
if(!sheet) return;
return function (selector, styles) {
if (sheet.insertRule) return sheet.insertRule(selector + " {" + styles + "}", sheet.cssRules.length);
if (sheet.addRule) return sheet.addRule(selector, styles);
}
}(document.styleSheets[document.styleSheets.length - 1]));
var i = 101;
while (i--) {
addRule("[data-width='" + i + "%']", "width:" + i + "%");
}
This creates 100 pseudo-selectors like this:
[data-width='1%'] { width: 1%; }
[data-width='2%'] { width: 2%; }
[data-width='3%'] { width: 3%; }
...
[data-width='100%'] { width: 100%; }
Note: This is a bit offtopic, and not really what you (or someone) wants, but maybe helpful.
As of today, you can read some values from HTML5 data attributes in CSS3 declarations. In CaioToOn's fiddle the CSS code can use the data properties for setting the content.
Unfortunately it is not working for the width and height (tested in Google Chrome 35, Mozilla Firefox 30 & Internet Explorer 11).
But there is a CSS3 attr() Polyfill from Fabrice Weinberg which provides support for data-width and data-height. You can find the GitHub repo to it here: cssattr.js.
Is there a way to style an ID based on a specific word in the ID name?
If I have something like this:
<div id="name-of-id.1234">Something</div>
<div id="name-of-id.5678">Something</div>
<div id="name-of-id.4321">Something</div>
Normally I'd style it like this:
div#name-of-id\.1234,
div#name-of-id\.5678,
div#name-of-id\.4321 {
color: #F0F;
}
But I'd MUCH RATHER do something like this:
div[# contains the word "name-of-id"] {
color: #F0F;
}
Is there a way to target a specific word in an ID like that?
I have very limited access to the html - I can add scripts/styles to the layout, but that's about it.
Use the CSS3 prefix substring matching attribute selector:
div[id^="name-of-id"] {
color: #F0F;
}
It is supported by all current browsers. For support in older version of IE, use the Selectivizr polyfill. There is also a selector for suffixes ([id$="..."]) and for general substrings ([id*="..."]).
If you can add javascript (and you use jQuery), you could add something like this:
$('div').each(function(){
if(this.id.match('name-of-id')) {
$(this).addClass('someClass');
}
});
Without jQuery, you could do:
var elems = document.getElementsByTagName('div');
for(var i=0; i<elems.length; i++) {
if(this.id.match('name-of-id')) {
this.className = this.className + 'someClass';
}
}
And then style them with a class:
.someClass {
/* your CSS styles */
}
Granted, running $('div') would be slow (as far as javascript is concerned) if your page contains a lot of them, so if you could narrow that selector down, this might be a more viable solution.
More to the point, there isn't a method I'm aware of to match partial ID names in CSS alone.