Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'media-bubble'

Conflicts:
	doc-src/SASS_CHANGELOG.md

Closes gh-6
  • Loading branch information...
commit 30371ea96e9d89a89cad293a25eb6fa59cfc3e70 2 parents 0378c8f + 3719199
@nex3 nex3 authored
View
52 doc-src/SASS_CHANGELOG.md
@@ -97,6 +97,58 @@ is compiled to:
.salamander-icon {
background-image: url('/images/salamander.png'); }
+### `@media` Bubbling
+
+Modern stylesheets often use `@media` rules to target styles
+at certain sorts of devices, screen resolutions, or even orientations.
+They're also useful for print and aural styling.
+Unfortunately, it's annoying and repetitive to break the flow of a stylesheet
+and add a `@media` rule containing selectors you've already written
+just to tweak the style a little.
+
+Thus, Sass 3.1 now allows you to nest `@media` rules within selectors.
+It will automatically bubble them up to the top level,
+putting all the selectors on the way inside the rule.
+For example:
+
+ .sidebar {
+ width: 300px;
+ @media screen and (orientation: landscape) {
+ width: 500px;
+ }
+ }
+
+is compiled to:
+
+ .sidebar {
+ width: 300px;
+ }
+ @media screen and (orientation: landscape) {
+ .sidebar {
+ width: 500px;
+ }
+ }
+
+You can also nest `@media` directives within one another.
+The queries will then be combined using the `and` operator.
+For example:
+
+ @media screen {
+ .sidebar {
+ @media (orientation: landscape) {
+ width: 500px;
+ }
+ }
+ }
+
+is compiled to:
+
+ @media screen and (orientation: landscape) {
+ .sidebar {
+ width: 500px;
+ }
+ }
+
### Backwards Incompatibilities -- Must Read!
* When `@import` is given a path without `.sass`, `.scss`, or `.css` extension,
View
50 doc-src/SASS_REFERENCE.md
@@ -1081,6 +1081,56 @@ and you can do
and `_colors.scss` would be imported.
+### `@media` {#media}
+
+`@media` directives in Sass behave just like they do in plain CSS,
+with one extra capability: they can be nested in CSS rules.
+If a `@media` directive appears within a CSS rule,
+it will be bubbled up to the top level of the stylesheet,
+putting all the selectors on the way inside the rule.
+This makes it easy to add media-specific styles
+without having to repeat selectors
+or break the flow of the stylesheet.
+For example:
+
+ .sidebar {
+ width: 300px;
+ @media screen and (orientation: landscape) {
+ width: 500px;
+ }
+ }
+
+is compiled to:
+
+ .sidebar {
+ width: 300px;
+ }
+ @media screen and (orientation: landscape) {
+ .sidebar {
+ width: 500px;
+ }
+ }
+
+`@media` queries can also be nested within one another.
+The queries will then be combined using the `and` operator.
+For example:
+
+ @media screen {
+ .sidebar {
+ @media (orientation: landscape) {
+ width: 500px;
+ }
+ }
+ }
+
+is compiled to:
+
+ @media screen and (orientation: landscape) {
+ .sidebar {
+ width: 500px;
+ }
+ }
+
### `@extend` {#extend}
There are often cases when designing a page
View
3  lib/sass/engine.rb
@@ -7,6 +7,7 @@
require 'sass/tree/comment_node'
require 'sass/tree/prop_node'
require 'sass/tree/directive_node'
+require 'sass/tree/media_node'
require 'sass/tree/variable_node'
require 'sass/tree/mixin_def_node'
require 'sass/tree/mixin_node'
@@ -645,6 +646,8 @@ def parse_directive(parent, line, root)
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath charset directives.",
:line => @line + 1) unless line.children.empty?
Tree::CharsetNode.new(name)
+ elsif directive == "media"
+ Tree::MediaNode.new(value)
else
Tree::DirectiveNode.new(line.text)
end
View
2  lib/sass/scss/parser.rb
@@ -253,7 +253,7 @@ def use_css_import?; false; end
def media_directive
val = str {media_query_list}.strip
- block(node(Sass::Tree::DirectiveNode.new("@media #{val}")), :directive)
+ block(node(Sass::Tree::MediaNode.new(val)), :directive)
end
# http://www.w3.org/TR/css3-mediaqueries/#syntax
View
72 lib/sass/tree/media_node.rb
@@ -0,0 +1,72 @@
+module Sass::Tree
+ # A static node representing a `@media` rule.
+ # `@media` rules behave differently from other directives
+ # in that when they're nested within rules,
+ # they bubble up to top-level.
+ #
+ # @see Sass::Tree
+ class MediaNode < DirectiveNode
+ # The media query (e.g. `print` or `screen`).
+ #
+ # @return [String]
+ attr_accessor :query
+
+ # @see RuleNode#tabs
+ attr_accessor :tabs
+
+ # @see RuleNode#group_end
+ attr_accessor :group_end
+
+ # @param query [String] See \{#query}
+ def initialize(query)
+ @query = query
+ @tabs = 0
+ super('')
+ end
+
+ # @see DirectiveNode#value
+ def value
+ "@media #{query}"
+ end
+
+ # Pass on the parent if it's a RuleNode.
+ #
+ # @see Node#cssize
+ def cssize(extends, parent = nil)
+ _cssize(extends, (parent if [MediaNode, RuleNode].include?(parent.class)))
+ rescue Sass::SyntaxError => e
+ e.modify_backtrace(:filename => filename, :line => line)
+ raise e
+ end
+
+ protected
+
+ # Merge nested media queries.
+ #
+ # @see Node#_cssize
+ def _cssize(extends, parent)
+ node = super
+ media = node.children.select {|c| c.is_a?(MediaNode)}
+ node.children.reject! {|c| c.is_a?(MediaNode)}
+ media.each {|n| n.query = "#{query} and #{n.query}"}
+ (node.children.empty? ? [] : [node]) + media
+ end
+
+ # If we're passed a parent, bubble it down.
+ #
+ # @see Node#cssize
+ def cssize!(extends, parent)
+ return super unless parent.is_a?(RuleNode)
+ new_rule = parent.dup
+ new_rule.children = self.children
+ self.children = Array(new_rule.cssize(extends, self))
+ end
+
+ # @see Node#to_s
+ def _to_s(tabs)
+ str = super(tabs + self.tabs)
+ str.gsub!(/\n\Z/, '') unless style == :compressed || group_end
+ str
+ end
+ end
+end
View
10 lib/sass/tree/rule_node.rb
@@ -53,11 +53,8 @@ class RuleNode < Node
# @param rule [Array<String, Sass::Script::Node>]
# The CSS rule. See \{#rule}
def initialize(rule)
- #p rule
merged = Sass::Util.merge_adjacent_strings(rule)
- #p merged
@rule = Sass::Util.strip_string_array(merged)
- #p @rule
@tabs = 0
super()
end
@@ -200,8 +197,8 @@ def perform!(environment)
# or nil if the parent isn't a {RuleNode}
def _cssize(extends, parent)
node = super
- rules = node.children.grep(RuleNode)
- props = node.children.reject {|c| c.is_a?(RuleNode) || c.invisible?}
+ rules = node.children.select {|c| c.is_a?(RuleNode) || c.is_a?(MediaNode)}
+ props = node.children.reject {|c| c.is_a?(RuleNode) || c.is_a?(MediaNode) || c.invisible?}
unless props.empty?
node.children = props
@@ -223,7 +220,8 @@ def _cssize(extends, parent)
# or nil if the parent isn't a {RuleNode}
# @raise [Sass::SyntaxError] if the rule has no parents but uses `&`
def cssize!(extends, parent)
- self.resolved_rules = @parsed_rules.resolve_parent_refs(parent && parent.resolved_rules)
+ # It's possible for resolved_rules to be set if we've duplicated this node during @media bubbling
+ self.resolved_rules ||= @parsed_rules.resolve_parent_refs(parent && parent.resolved_rules)
super
end
View
132 test/sass/engine_test.rb
@@ -2241,6 +2241,138 @@ def test_string_interpolation_with_comma
SASS
end
+ def test_media_bubbling
+ assert_equal <<CSS, render(<<SASS)
+.foo {
+ a: b; }
+ @media bar {
+ .foo {
+ c: d; } }
+ .foo .baz {
+ e: f; }
+ @media bip {
+ .foo .baz {
+ g: h; } }
+
+.other {
+ i: j; }
+CSS
+.foo
+ a: b
+ @media bar
+ c: d
+ .baz
+ e: f
+ @media bip
+ g: h
+
+.other
+ i: j
+SASS
+
+ assert_equal <<CSS, render(<<SASS, :style => :compact)
+.foo { a: b; }
+@media bar { .foo { c: d; } }
+.foo .baz { e: f; }
+@media bip { .foo .baz { g: h; } }
+
+.other { i: j; }
+CSS
+.foo
+ a: b
+ @media bar
+ c: d
+ .baz
+ e: f
+ @media bip
+ g: h
+
+.other
+ i: j
+SASS
+
+ assert_equal <<CSS, render(<<SASS, :style => :expanded)
+.foo {
+ a: b;
+}
+@media bar {
+ .foo {
+ c: d;
+ }
+}
+.foo .baz {
+ e: f;
+}
+@media bip {
+ .foo .baz {
+ g: h;
+ }
+}
+
+.other {
+ i: j;
+}
+CSS
+.foo
+ a: b
+ @media bar
+ c: d
+ .baz
+ e: f
+ @media bip
+ g: h
+
+.other
+ i: j
+SASS
+ end
+
+ def test_double_media_bubbling
+ assert_equal <<CSS, render(<<SASS)
+@media bar and baz {
+ .foo {
+ c: d; } }
+CSS
+@media bar
+ @media baz
+ .foo
+ c: d
+SASS
+
+ assert_equal <<CSS, render(<<SASS)
+@media bar {
+ .foo {
+ a: b; } }
+ @media bar and baz {
+ .foo {
+ c: d; } }
+CSS
+.foo
+ @media bar
+ a: b
+ @media baz
+ c: d
+SASS
+ end
+
+ def test_rule_media_rule_bubbling
+ assert_equal <<CSS, render(<<SASS)
+@media bar {
+ .foo {
+ a: b;
+ e: f; }
+ .foo .baz {
+ c: d; } }
+CSS
+.foo
+ @media bar
+ a: b
+ .baz
+ c: d
+ e: f
+SASS
+ end
+
# Encodings
unless Sass::Util.ruby1_8?
Please sign in to comment.
Something went wrong with that request. Please try again.