-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[request] seperate media/browser specific markup to seperate style sheet #241
Comments
related conversation: Compass/compass#664 |
As I said on the Compass issue, I haven't seen enough evidence to indicate that explicit support for targeting multiple stylesheets is substantially better than using the existing |
The solution I want is to be able to move all media queries for specific sizes into one place - preferably in the same file. At the moment I can only compile code into something like this: #Foo { width: 21.2%; }
@media screen and (max-width:600px) {
#Foo { width: 100%; }
}
@media screen and (min-width:1200px) {
#Foo { width: 15.3%; }
}
@media screen and (min-width:1600px) {
#Foo { width: 9.4%; }
}
.bar { position: fixed; width: 31.2%; }
@media screen and (max-width:600px) {
.bar { position: relative; display: inline; float: left; width: 90%; }
}
@media screen and (min-width:1200px) {
.bar { width: 75.3%; }
}
@media screen and (min-width:1600px) {
.bar { width: 79.4%; }
} Which can really bloat code after multiple selectors are produced. It would be much better for me both for size and developing if it would read like this: #Foo { width: 21.2%; }
.bar { position: fixed; width: 31.2%; }
@media screen and (max-width:600px) {
#Foo { width: 100%; }
.bar { position: relative; display: inline; float: left; width: 90%;
}
}
@media screen and (min-width:1200px) {
#Foo { width: 15.3%; }
.bar { width: 75.3%; }
}
@media screen and (min-width:1600px) {
#Foo { width: 9.4%; }
.bar { width: 79.4%; }
} Edit: sorted out formatting (my first time putting code in comments) |
@MattyBalaam It's not easy to safely re-order rules in a stylesheet. In your case I believe it works, but consider the following two chunks of CSS: @media screen and (max-width:600px) {
.foo {position: fixed}
}
.bar {position: relative}
@media screen and (max-width:600px) {
.bar {display: inline}
} .bar {position: relative}
@media screen and (max-width:600px) {
.foo {position: fixed}
.bar {display: inline}
} Under the first, |
I did post this request before I noticed the Compass thread; that is why I posted the link after. If that is a good workaround, then great! I do like the second example just above! Perhaps then this request should not be regarded as an extra addition to SASS's codebase, but a request for such use be part of the "official" documentation? |
@nex3 I see your point about some kind of intelligent re-ordering being a problem. But would there be any way of allowing this so the developer has control over where the rules are placed? So throughout the file you would be able to say something like
Obviously you may get times when rules conflict, but that would be no different from coding the CSS manually. |
I think the correct way to handle that case will be post facto optimization. I don't want to add a feature that's just there so developers can manually optimize the size of their stylesheets. |
So can I check you mean each CSS file after compilation would then need to be manually optimised? Or are you saying it could be done automatically, but some additional automated task outside sass would need to be created to do this. |
I was asked to provide a more specific usecase where this could be useful. As an aside, I really like the suggestion in the related Compass issue for an @target directive to specify what code goes where and thus not limiting this to just media queries. That being said, here's the more specific usecase: Enter SouthStreet, current one of the best-in-class toolset for Progressive Enhancement. Specifically, let's look at Enhance (Or Modernizr with yepnope support, which I prefer) and eCSSential. To understand why these tools are important, we need to remember a handful of things; first, not all media queries are width media queries, some media queries may actually change the way you want to lay out your site, loading non-essential CSS in a blocking manner is bad for front end performance, with the @target directive this will provide for more utility than just with media queries, and when it comes to RWD it's really just Progressive Enhancement in a very large scale and being able to separate out our media queries can go a long way in being future friendly. So, with this in mind, let's take a look at Enhance/Modernizr. Both utilities allow for JavaScript based checking of browser features and then performing actions based on that including conditionally loading CSS in. If one were to take this approach, we may want a totally separate layout for devices that support touch. In an ideal environment, we'd be able to do something like the following (using Modernizr's syntax): /* style.scss */
#foo {
height: 20px;
@target touch {
height: 44px;
}
} /* style.css */
#foo {
height: 20px;
} /* style-touch.css */
#foo {
height: 44px;
} yepnope({
test: Modernizr.touch,
yep: 'css/style-touch.css'
}); All of our touch styling is now pushed out to a namespaced targeted CSS file that we can load in as we please. If we're taking the eCSSentials approach, let's look at that same example, except this time we're going to combine it with the CSS Level 4 'pointer' media query: /* style.scss */
#foo {
height: 20px;
@target coarse {
height: 44px;
}
} /* style.css */
#foo {
height: 20px;
} /* style-touch.css */
#foo {
height: 44px;
} <link href="style.css">
<link href="style-touch.css" media="(pointer: coarse )"> This, when using the eCSSential style async loading, defers the loading of style-touch.css in browsers that don't support touch while those that do get it immediately (NOTE: eCSSential doesn't currently support MQs besides width ones, but the style of loading should be easily expandable to doing so). Of course, there's also the third and final usecase that comes from Media Queries in general; specifically no media query fallbacks for browsers that don't support them, and especially for IE<9. Having the power to write IE hacks inline and have them print out in a separate stylesheet just for IE seems absolutely in line with Sass's ideals. Yes there are potential render issues, but these exist even when coding by hand, and if using the @target directive, the 'all' namespace could be reserved for printing to all stylesheets to help avoid this. Could you do this all by hand with lots of creative partials and mixins? Sure you could, and that's what we're forced to do now, but having Sass handle this would be a huge time saver and make these great techniques readily available to those who aren't as skilled at the whole process that's required to do it by hand. Also, I believe this may go without saying, but I'm also a fan of concating same media queries together under a single media query, although the same potential exists for render issues there. It's hard to get around this, and truthfully you'd have the same issues coding it by hand, so I'm not really sure if there's a convincing counter argument for that issue except to say make it a default-off compile time option with a warning about potential unexpected results in the readme. |
I really don't understand what objections to |
Like I said, I really like @target. It's an elegant solution to an otherwise very complex problem, and as long as we can interpolate the target, totally extensible by mixins and functions. |
I can certainly see why you'd want to create multiple stylesheets for different browsers and different capability profiles. That's definitely a use case I'd like to support. What I don't understand is why /* style.scss */
#foo {
height: 20px;
@if $target == touch {
height: 44px;
}
} Compile the same stylesheet with different variables set and it works just like you're suggesting |
I prefer The In a nutshell: could we use |
@nex3 it's not the same. The differences:
/* style.scss */
#foo {
@if not $target { // Yuck
height: 20px;
}
@if $target == touch {
height: 44px;
}
}
|
Having used something very similar to In this project I had two environments I built separately, one for an app and one for the web. It did the trick and was a good solution but not without some headaches. @target seems like a very elegant solution to these issues. |
I'm sorry if this confuses the issue, but what about something like an @as directive; This would mark either the current node or the children of the the @as to be compiled in a separate file. @media all and (min-width: 600px) {
@as $mobile-stylesheet;
...
}
.foo {
@as $desktop-stylesheet;
...
} @each $target in desktop, mobile {
@as #{$target}.scss {
@import style.css;
}
} |
Thanks for chiming in Rich, but I think that actually is the opposite of what we're looking to do. This is a language level change, so an end user shouldn't need to write an @each statement anywhere; the idea is to just wrap a piece of code and, at a language level, have it generate a separate stylesheet with just that wrapped code, so no need for the @each, and more importantly, we don't want to flat out @import all of style, we want separate stylesheets with desecrate code. If we take the @each out, then the While I'm here though, a suggestion for either solution: the ability to specify that the output should be put in more than one spot, so something like this: @target all, ie-9, ie-8, ie-7, ie-6 {} or @if $target == 'all' or $target == 'ie-9' or $target == 'ie-8' or $target == 'ie=7' or $target == 'ie-6' {} Also, funny, now that I just wrote out those two, I would really hate to write out the @nex3, with #foo {
$target: 'print';
@include invert-colors(blue);
}
#bar {
$target: 'print';
@include invert-colors(white);
}
#baz {
$target: 'all';
@include invert-colors(red);
}
@mixin invert-colors($start-color) {
background: $start-color;
color: invert($start-color);
@if $target == 'print' and $start-color != 'white' {
background: greyscale($start-color);
color: invert(greyscale($start-color));
}
@else {
color: invert($start-color);
}
} Does Can you chain target if statements with non-target if statements? If so, what happens when you do; does it only print if the entire if statement evals to true as per normal, or will it generate the the stylesheet than continue evaluating the if statement? Essentially, the question is, what happens when you add new and unexpected functionality into preexisting mental models? Wouldn't it be better to create a new mental model for this truly new functionality? |
A bit further away from the // default.scss
#page {
min-width: 960px;
@buffer small-screen {
min-width: 0;
}
}
@import 'small-screen'; // small-screen.scss
@flush small-screen I haven't gotten to do it yet, but it's on my list. If interested, subscribe to #116. |
@Snugug I'm not particularly swayed by the idea that @mixin target($targets...) {
@each $allowed-target in $targets {
@if $target == $allowed-target {
@content;
}
}
} There's also no reserved variable names or other magic going on. There would just be a command-line argument to Sass that allowed you to set a global variable ( @chriseppstein The differences you bring up are more compelling, although I'm not entirely sure why you'd want the separate target stylesheets not to have the contents from the primary stylesheet. @scottkellum I don't understand what you mean by "isolating styles across partials." Can you give a more concrete example of how this was awkward? |
@nex3 because, in this case, you serve both stylesheets to the browser. |
Okay, but why? |
@nex3 See yepnope.js, it's a very common strategy. |
I can see why that would be useful for non-preprocessed stylesheets where it would be a pain to duplicate all the shared styles, but a preprocessor makes it easy to do so and just have one stylesheet per target. Why wouldn't you want that? |
For one, just about all of the optimization tools are designed to not have full stylesheets loaded in but only load in what's needed when, and to add on top instead of fully replacing. This is especially true for IE conditionals and low bandwidth where you specifically don't want to load in a replacing stylesheet (too large and cumbersome) and really truly only want the IE specific stuff. Practical example time: I'm building a responsive, mobile first website. I've done all of the progressive enhancement so that all of the design, coloring, images, etc… degrade nicely for IE8, but IE8 doesn't support media queries, so the "mobile" single column layout is shown instad of the "desktop" multicolumn layout. Instead of supplying IE8 and below with a complete stylesheet, I really only need to supply them with a supplemental layout stylesheet. This is, in fact, preferable as it is easier to see the difference between the two and because serving an entire new stylesheet would be a double download of all of the styles, meaning double the selectors (which IE hates), double the page blocking time, double everything. In a world where milliseconds means dollars, only loading in what's needed when it's needed is a preferable strategy to load it all and let God sort it out. |
@nex3 if there is only one dimension then the approach you've identified makes sense and is most optimal. But if there are two or more dimensions the approach falls apart as you have to permute all the dimensions in order to have a stylesheet to load that matches the exact client needs. |
@Snugug My suggestion wasn't to load two stylesheets, one basic and another basic + IE8; rather, it was to only load the basic + IE8 stylesheet on IE8. @chriseppstein I take your point. That would indeed be difficult to deal with in my suggested approach. All right, I'm sufficiently convinced that |
:) There is some discussion of a buffer/capture concept in another issue. We should sort out that use case and see if these two should be unified into a generalized feature. |
From what I've seen, I dislike the semantics of |
I'm not sold on it either, but if there's something there, they might be related. |
That's why I suggested they open a pull request, so we can discuss it more thoroughly. |
Any movement on this? |
Just chiming in that I really need/want this functionality as well! Would be SO helpful! |
it seems its currently not planned to be implemented? |
I've recently been toying with a different idea that is even more useful and powerful. |
Would love to hear more about the progress on this. Something that is more useful and powerful? |
I have a specific need for this. I'm using sass to generate my css, part of the project I'm working on requires the project to be brand-able, i.e. separate css output just for the colours and nothing else. I was sure there was a way to do this by @extends but I was wrong. In the end I created a load of variables and had to separate out all the colour info out of 20+ scss _partials to do it. Problem is, this starts off small but in the end it is going to cause a huge headache the more brands get added. Is there no way at present to keep all the sass together logically and split out portions into another file(s)? |
What about the following implementation? This is an implementation that's already possible in 3.2. INPUT :input/settings/normal.scss : // Set module specific parameters
$module : 'normal';
$screen-min : '700';
$screen-max : '1199'; input/settings/widescreen.scss : // Set module specific parameters
$module : 'widescreen';
$screen-min : '1200';
$screen-max : '1600'; input/core.scss : @media only screen and (min-width: $screen-min) and (max-width: $screen-max) {
div {
@if $module == 'normal' {
padding: 20px;
}
@if $module == 'widescreen' {
padding: 30px;
}
}
span {
@if $module == 'normal' {
padding: 60px;
}
@if $module == 'widescreen' {
padding: 45px;
}
}
} input/normal.scss : @import "input/settings/normal.scss";
@import "input/core.scss"; input/widescreen.scss : @import "input/settings/widescreen.scss";
@import "input/core.scss"; input/global.scss : @import "output/normal.css";
@import "output/widescreen.css"; OUTPUT :output/normal.css : @media only screen and (min-width: 700) and (max-width: 1199) {
div {
padding: 20px;
}
span {
padding: 60px;
}
} output/widescreen.css : @media only screen and (min-width: 1200) and (max-width: 1600) {
div {
padding: 30px;
}
span {
padding: 45px;
}
} output/global.css : @media only screen and (min-width: 700) and (max-width: 1199) {
div {
padding: 20px;
}
span {
padding: 60px;
}
}
@media only screen and (min-width: 1200) and (max-width: 1600) {
div {
padding: 30px;
}
span {
padding: 45px;
}
} |
I'd love to see a non hackish way to do this. jsledgers proposed workaround requires that you wrap everything in the file in an @if to get your separate files in multiple passes. Why not just make this easy and allow a module partial to be compiled to multiple files with @export or @target or whatever? That way you only have to wrap the things that need to be separated out in an a function/mixin rather than wrapping the whole file contents to hide/show everything. |
Hey everybody, |
I was searching for this kind of feature and landed here. But I'm not for a "@target" keyword which is, IMO, too specific. I would prefer a @file, or @output... For example: // main.scss
#foo {
.bar {
.baz {
&:hover {
color: blue;
font-weight: bold;
font-size: 12pt;
@file xmas {
color: red;
background-image: url(santa.png);
}
}
}
}
} That would produce: // main.css
#foo .bar .baz:hover {
color: blue;
font-weight: bold;
font-size: 12pt;
}
// xmas.css
#foo .bar .baz:hover {
color: red;
background-image: url(santa.png);
} |
I'm also after a feature like this, the specific use case I'm interested in is to generate an extra inline.css file which will be included inline in the page to optimize above-the-fold render speed/speed index. Pretty much exactly what @ByScripts is after. Currently I do this by commenting out the section in the file and annotating it with an 'inline' comment, then duplicating the styles in inline.scss. _some-module.scss
inline.scss
|
Hey everyone! |
after 2 years and still "under consideration"... =/ |
I have another use case for this. We're currently implementing a feature where users can customize the style of the site when they're logged in (similar to the way Twitter theming works). Instead of generating the entire style sheet with their customizations we want to create a separate, smaller sheet that only contains the colors and fonts that user has chosen, so the base style sheet is loaded in addition to their customizations. It would be much easier to do that if we can write the styles inline and extract them using something like this. |
@RoboAndie did you try https://github.com/snugug/gulp-css-target ? |
@barraponto We're going to try that soon, but we'd really prefer to have it be part of core Sass. |
Actually I was looking for exactly this! I have a Drupal project with multiple projects separated through domains. Every domain has a generally the same theme, but domain specific adjustments. I would really love something like
To split all domain specific styles to domain specific files. That way I would not have to use a domain class selector and deeper cascading. @Snugug |
When I last commented on this thread over 3 years ago, it seemed that @target was a powerful solution. Has anything changed? |
Bump?! |
It seems like postcss is actually capable of doing this. |
I've been looking for a way to accomplish this for awhile, and created a postcss plugin that implements the |
Wow @wheeyls... Looks good the post css add on. I'm going to try. For a while I have been waiting for something solid as well... |
I have to agree with Sass's docs:
@import... takes a separate (slow) HTTP request [per .css file]
. But there are times when you want to work on a single .SCSS/.SASS source doc, tag some markup as browser-specific, and have the resultant .CSS contain a conditionally-include link to a second (also auto-generated from the single source) .CSS file.Example scenario:
The main .SCSS file contains all the markup, with special notation for IE6 workarounds. Sass then splits off the IE6-tagged markup into "ie6-only.css", and inserts in main.css:
<!--[if IE 6]> @import ie6-only.css /* no need to hard-code all that IE6 crud for 7% of the website visits */<![endif]-->
Inspiration:
http://perfectionkills.com/profiling-css-for-fun-and-profit-optimization-notes/
Search for SASS ;)
Idea on how to implement:
Not sure; I don't know Sass or Ruby well enough, perhaps something like the new Placeholder Selectors, or a pseudo-selector like ":-ie6external-any". I considered to define a custom Sass function to hack my own, but I keep gravitating that a tweak to the Placeholder Selectors can do the trick. I don't see other ways in the docs how to do de-interpolate into 2+ .CSS files.
cheers
The text was updated successfully, but these errors were encountered: