[Sass] Refer to properties as properties, not attributes.

This commit is contained in:
Nathan Weizenbaum 2009-06-19 03:33:03 -07:00
parent 36b316a60c
commit 81900a6db5
9 changed files with 122 additions and 124 deletions

View File

@ -113,9 +113,9 @@ At its most basic,
Sass is just another way of writing CSS.
Although it's very much like normal CSS,
the basic syntax offers a few helpful features:
tabulation indicates the attributes in a rule,
indentation indicates the properties in a rule,
rather than non-DRY brackets;
and newlines indicate the end of an attribute,
and newlines indicate the end of a properties,
rather than a semicolon.
For example:
@ -181,7 +181,7 @@ In CSS, you just have to re-type it each time,
which is a nightmare when you decide to change it later.
Not so for Sass!
You can use the `!` character to set variables.
Then, if you put `=` after your attribute name,
Then, if you put `=` after your property name,
you can set it to a variable.
For example:
@ -228,7 +228,7 @@ becomes:
width: 15em; }
Taking the idea of variables a bit further are mixins.
These let you group whole swathes of CSS attributes into a single
These let you group whole bunches of CSS properties into a single
directive and then include those anywhere you want:
=blue-border

View File

@ -101,15 +101,15 @@ Available options are:
: Sets the style of the CSS output.
See the section on Output Style, above.
{#attribute_syntax-option} `:attribute_syntax`
: Forces the document to use one syntax for attributes.
{#property_syntax-option} `:property_syntax`
: Forces the document to use one syntax for properties.
If the correct syntax isn't used, an error is thrown.
`:new` forces the use of a colon or equals sign
after the attribute name.
after the property name.
For example: `color: #0f3`
or `width = !main_width`.
`:old` forces the use of a colon
before the attribute name.
before the property name.
For example: `:color #0f3`
or `:width = !main_width`.
By default, either syntax is valid.
@ -194,11 +194,8 @@ Available options are:
## CSS Rules
Rules in flat CSS have two elements:
the selector
(e.g. `#main`, `div p`, `li a:hover`)
and the attributes
(e.g. `color: #00ff00;`, `width: 5em;`).
the selector (e.g. `#main`, `div p`, `li a:hover`)
and the properties (e.g. `color: #00ff00;`, `width: 5em;`).
Sass has both of these,
as well as one additional element: nested rules.
@ -206,13 +203,13 @@ as well as one additional element: nested rules.
However, some of the syntax is a little different.
The syntax for selectors is the same,
but instead of using brackets to delineate the attributes that belong to a particular rule,
but instead of using brackets to delineate the properties that belong to a particular rule,
Sass uses indentation.
For example:
#main p
<attribute>
<attribute>
<property>
<property>
...
Like CSS, you can stretch rules over multiple lines.
@ -222,15 +219,15 @@ For example:
.users #userTab,
.posts #postsTab
<attributes>
<property>
### Attributes
### Properties
There are two different ways to write CSS attributes.
There are two different ways to write CSS properties.
The first is very similar to the how you're used to writing them:
with a colon between the name and the value.
However, Sass attributes don't have semicolons at the end;
each attribute is on its own line, so they aren't necessary.
However, Sass properties don't have semicolons at the end;
each property is on its own line, so they aren't necessary.
For example:
#main p
@ -243,10 +240,10 @@ is compiled to:
color: #00ff00;
width: 97% }
The second syntax for attributes is slightly different.
The colon is at the beginning of the attribute,
The second syntax for properties is slightly different.
The colon is at the beginning of the property,
rather than between the name and the value,
so it's easier to tell what elements are attributes just by glancing at them.
so it's easier to tell what elements are properties just by glancing at them.
For example:
#main p
@ -259,9 +256,9 @@ is compiled to:
color: #00ff00;
width: 97% }
By default, either attribute syntax may be used.
By default, either property syntax may be used.
If you want to force one or the other,
see the [`:attribute_syntax`](#attribute_syntax-option) option.
see the [`:property_syntax`](#property_syntax-option) option.
### Nested Rules
@ -320,7 +317,7 @@ by using the ampersand character `&` in your selectors.
The ampersand is automatically replaced by the parent selector,
instead of having it prepended.
This allows you to cleanly create pseudo-attributes:
This allows you to cleanly create pseudo-classes:
a
font-weight: bold
@ -361,16 +358,16 @@ Which would become:
.ie6 #main #sidebar {
margin-left: 40%; }
### Attribute Namespaces
### Property Namespaces
CSS has quite a few attributes that are in "namespaces;"
CSS has quite a few properties that are in "namespaces;"
for instance, `font-family`, `font-size`, and `font-weight`
are all in the `font` namespace.
In CSS, if you want to set a bunch of attributes in the same namespace,
In CSS, if you want to set a bunch of properties in the same namespace,
you have to type it out each time.
Sass offers a shortcut for this:
just write the namespace one,
then indent each of the sub-attributes within it.
then indent each of the sub-properties within it.
For example:
.funky
@ -635,7 +632,7 @@ is compiled to:
color: #357;
For old-style attributes, the `=` is added but the `:` is retained.
For old-style properties, the `=` is added but the `:` is retained.
For example:
:color = #123 + #234
@ -671,7 +668,7 @@ and are set like so:
!width = 5em
You can then refer to them by putting an equals sign
after your attributes:
after your properties:
#main
width = !width
@ -799,7 +796,7 @@ See {Sass::Script::Functions} for more information.
### Interpolation: `#{}`
You can also use SassScript variables in selectors
and attribute names using #{} interpolation syntax:
and property names using #{} interpolation syntax:
!name = foo
!attr = border
@ -836,7 +833,7 @@ is compiled to:
## Mixins
Mixins enable you to define groups of CSS attributes and
Mixins enable you to define groups of CSS properties and
then include them inline in any number of selectors
throughout the document. This allows you to keep your
stylesheets DRY and also avoid placing presentation
@ -965,14 +962,14 @@ For example:
/* A very awesome rule.
#awesome.rule
/* An equally awesome attribute.
/* An equally awesome property.
awesomeness: very
becomes
/* A very awesome rule. */
#awesome.rule {
/* An equally awesome attribute. */
/* An equally awesome property. */
awesomeness: very; }
You can also nest content beneath loud comments. For example:
@ -1006,7 +1003,7 @@ For example:
// A very awesome rule.
#awesome.rule
// An equally awesome attribute.
// An equally awesome property.
awesomeness: very
becomes
@ -1019,7 +1016,7 @@ For example:
// A very awesome rule
#awesome.rule
// Don't use these attributes
// Don't use these properties
color: green
font-size: 10em
color: red
@ -1045,7 +1042,7 @@ outside Rails, it's done by passing an options hash with `:style` set.
Nested style is the default Sass style,
because it reflects the structure of the document
in much the same way Sass does.
Each attribute has its own line,
Each property has its own line,
but the indentation isn't constant.
Each rule is indented based on how deeply it's nested.
For example:
@ -1069,8 +1066,8 @@ without actually reading anything.
### `:expanded`
Expanded is the typical human-made CSS style,
with each attribute and rule taking up one line.
Attributes are indented within the rules,
with each property and rule taking up one line.
Properties are indented within the rules,
but the rules aren't indented in any special way.
For example:
@ -1094,7 +1091,7 @@ Compact style, as the name would imply,
takes up less space than Nested or Expanded.
However, it's also harder to read.
Each CSS rule takes up only one line,
with every attribute defined on that line.
with every property defined on that line.
Nested rules are placed next to each other with no newline,
while groups of rules have newlines between them.
For example:

View File

@ -60,7 +60,7 @@ module Sass
class CSS
# @param template [String] The CSS code
# @option options :old [Boolean] (false)
# Whether or not to output old attribute syntax
# Whether or not to output old property syntax
# (`:color blue` as opposed to `color: blue`).
def initialize(template, options = {})
if template.is_a? IO
@ -135,14 +135,14 @@ module Sass
assert_match /\{/
node = Tree::RuleNode.new(rule)
attributes(node)
properties(node)
return node
end
# Parses a set of CSS attributes within a rule.
# Parses a set of CSS properties within a rule.
#
# @param rule [Tree::RuleNode] The parent node of the attributes
def attributes(rule)
# @param rule [Tree::RuleNode] The parent node of the properties
def properties(rule)
while @template.scan(/[^:\}\s]+/)
name = @template[0]
whitespace

View File

@ -75,11 +75,11 @@ module Sass
end
end
# The character that begins a CSS attribute.
ATTRIBUTE_CHAR = ?:
# The character that begins a CSS property.
PROPERTY_CHAR = ?:
# The character that designates that
# an attribute should be assigned to a SassScript expression.
# a property should be assigned to a SassScript expression.
SCRIPT_CHAR = ?=
# The character that designates the beginning of a comment,
@ -106,16 +106,16 @@ module Sass
# Includes named mixin declared using MIXIN_DEFINITION_CHAR
MIXIN_INCLUDE_CHAR = ?+
# The regex that matches attributes of the form <tt>name: attr</tt>.
ATTRIBUTE_NEW_MATCHER = /^[^\s:"]+\s*[=:](\s|$)/
# The regex that matches properties of the form <tt>name: prop</tt>.
PROPERTY_NEW_MATCHER = /^[^\s:"]+\s*[=:](\s|$)/
# The regex that matches and extracts data from
# attributes of the form <tt>name: attr</tt>.
ATTRIBUTE_NEW = /^([^\s=:"]+)(\s*=|:)(?:\s+|$)(.*)/
# properties of the form <tt>name: prop</tt>.
PROPERTY_NEW = /^([^\s=:"]+)(\s*=|:)(?:\s+|$)(.*)/
# The regex that matches and extracts data from
# attributes of the form <tt>:name attr</tt>.
ATTRIBUTE_OLD = /^:([^\s=:"]+)\s*(=?)(?:\s+|$)(.*)/
# properties of the form <tt>:name prop</tt>.
PROPERTY_OLD = /^:([^\s=:"]+)\s*(=?)(?:\s+|$)(.*)/
# The default options for Sass::Engine.
DEFAULT_OPTIONS = {
@ -133,9 +133,10 @@ module Sass
@template = template
# Backwards compatibility
case @options[:attribute_syntax]
when :alternate; @options[:attribute_syntax] = :new
when :normal; @options[:attribute_syntax] = :old
@options[:property_syntax] ||= @options[:attribute_syntax]
case @options[:property_syntax]
when :alternate; @options[:property_syntax] = :new
when :normal; @options[:property_syntax] = :old
end
end
@ -290,9 +291,9 @@ END
def parse_line(parent, line, root)
case line.text[0]
when ATTRIBUTE_CHAR
if line.text[1] != ATTRIBUTE_CHAR
parse_attribute(line, ATTRIBUTE_OLD)
when PROPERTY_CHAR
if line.text[1] != PROPERTY_CHAR
parse_property(line, PROPERTY_OLD)
else
# Support CSS3-style pseudo-elements,
# which begin with ::
@ -315,26 +316,26 @@ END
parse_mixin_include(line, root)
end
else
if line.text =~ ATTRIBUTE_NEW_MATCHER
parse_attribute(line, ATTRIBUTE_NEW)
if line.text =~ PROPERTY_NEW_MATCHER
parse_property(line, PROPERTY_NEW)
else
Tree::RuleNode.new(line.text)
end
end
end
def parse_attribute(line, attribute_regx)
name, eq, value = line.text.scan(attribute_regx)[0]
def parse_property(line, property_regx)
name, eq, value = line.text.scan(property_regx)[0]
if name.nil? || value.nil?
raise SyntaxError.new("Invalid attribute: \"#{line.text}\".", @line)
raise SyntaxError.new("Invalid property: \"#{line.text}\".", @line)
end
expr = if (eq.strip[0] == SCRIPT_CHAR)
parse_script(value, :offset => line.offset + line.text.index(value))
else
value
end
Tree::AttrNode.new(name, expr, attribute_regx == ATTRIBUTE_OLD ? :old : :new)
Tree::AttrNode.new(name, expr, property_regx == PROPERTY_OLD ? :old : :new)
end
def parse_variable(line)

View File

@ -16,12 +16,12 @@ module Sass::Tree
# @param name [String] See \{#name}
# @param value [String] See \{#value}
# @param attr_syntax [Symbol] `:new` if this property uses `a: b`-style syntax,
# @param prop_syntax [Symbol] `:new` if this property uses `a: b`-style syntax,
# `:old` if it uses `:a b`-style syntax
def initialize(name, value, attr_syntax)
def initialize(name, value, prop_syntax)
@name = name
@value = value
@attr_syntax = attr_syntax
@prop_syntax = prop_syntax
super()
end
@ -39,22 +39,22 @@ module Sass::Tree
# @param tabs [Fixnum] The level of indentation for the CSS
# @param parent_name [String] The name of the parent property (e.g. `text`) or nil
# @return [String] The resulting CSS
# @raise [Sass::SyntaxError] if the attribute uses invalid syntax
# @raise [Sass::SyntaxError] if the property uses invalid syntax
def to_s(tabs, parent_name = nil)
if @options[:attribute_syntax] == :old && @attr_syntax == :new
raise Sass::SyntaxError.new("Illegal attribute syntax: can't use new syntax when :attribute_syntax => :old is set.")
elsif @options[:attribute_syntax] == :new && @attr_syntax == :old
raise Sass::SyntaxError.new("Illegal attribute syntax: can't use old syntax when :attribute_syntax => :new is set.")
if @options[:property_syntax] == :old && @prop_syntax == :new
raise Sass::SyntaxError.new("Illegal property syntax: can't use new syntax when :property_syntax => :old is set.")
elsif @options[:property_syntax] == :new && @prop_syntax == :old
raise Sass::SyntaxError.new("Illegal property syntax: can't use old syntax when :property_syntax => :new is set.")
end
if value[-1] == ?;
raise Sass::SyntaxError.new("Invalid attribute: #{declaration.dump} (no \";\" required at end-of-line).", @line)
raise Sass::SyntaxError.new("Invalid property: #{declaration.dump} (no \";\" required at end-of-line).", @line)
end
real_name = name
real_name = "#{parent_name}-#{real_name}" if parent_name
if value.empty? && children.empty?
raise Sass::SyntaxError.new("Invalid attribute: #{declaration.dump} (no value).", @line)
raise Sass::SyntaxError.new("Invalid property: #{declaration.dump} (no value).", @line)
end
join_string = case style
@ -96,14 +96,14 @@ module Sass::Tree
# @return [String] An error message if the child is invalid, or nil otherwise
def invalid_child?(child)
if !child.is_a?(AttrNode) && !child.is_a?(CommentNode)
"Illegal nesting: Only attributes may be nested beneath attributes."
"Illegal nesting: Only properties may be nested beneath properties."
end
end
private
def declaration
@attr_syntax == :new ? "#{name}: #{value}" : ":#{name} #{value}"
@prop_syntax == :new ? "#{name}: #{value}" : ":#{name} #{value}"
end
end
end

View File

@ -36,26 +36,26 @@ module Sass::Tree
else
"#{' ' * (tabs - 1)}#{value} {" + (style == :compact ? ' ' : "\n")
end
was_attr = false
was_prop = false
first = true
children.each do |child|
next if child.invisible?
if style == :compact
if child.is_a?(AttrNode)
result << "#{child.to_s(first || was_attr ? 1 : tabs + 1)} "
result << "#{child.to_s(first || was_prop ? 1 : tabs + 1)} "
else
if was_attr
if was_prop
result[-1] = "\n"
end
rendered = child.to_s(tabs + 1)
rendered.lstrip! if first
result << rendered
end
was_attr = child.is_a?(AttrNode)
was_prop = child.is_a?(AttrNode)
first = false
elsif style == :compressed
result << (was_attr ? ";#{child.to_s(1)}" : child.to_s(1))
was_attr = child.is_a?(AttrNode)
result << (was_prop ? ";#{child.to_s(1)}" : child.to_s(1))
was_prop = child.is_a?(AttrNode)
else
result << child.to_s(tabs + 1) + "\n"
end

View File

@ -123,7 +123,7 @@ module Sass
result = String.new
children.each do |child|
if child.is_a? AttrNode
raise Sass::SyntaxError.new('Attributes aren\'t allowed at the root of a document.', child.line)
raise Sass::SyntaxError.new('Properties aren\'t allowed at the root of a document.', child.line)
else
next if child.invisible?
child_str = child.to_s(1)

View File

@ -79,7 +79,7 @@ module Sass::Tree
def to_s(tabs, super_rules = nil)
resolved_rules = resolve_parent_refs(super_rules)
attributes = []
properties = []
sub_rules = []
rule_separator = style == :compressed ? ',' : ', '
@ -96,12 +96,12 @@ module Sass::Tree
if child.is_a? RuleNode
sub_rules << child
else
attributes << child
properties << child
end
end
to_return = ''
if !attributes.empty?
if !properties.empty?
old_spaces = ' ' * (tabs - 1)
spaces = ' ' * tabs
if @options[:line_comments] && style != :compressed
@ -124,19 +124,19 @@ module Sass::Tree
end
if style == :compact
attributes = attributes.map { |a| a.to_s(1) }.select{|a| a && a.length > 0}.join(' ')
to_return << "#{total_rule} { #{attributes} }\n"
properties = properties.map { |a| a.to_s(1) }.select{|a| a && a.length > 0}.join(' ')
to_return << "#{total_rule} { #{properties} }\n"
elsif style == :compressed
attributes = attributes.map { |a| a.to_s(1) }.select{|a| a && a.length > 0}.join(';')
to_return << "#{total_rule}{#{attributes}}"
properties = properties.map { |a| a.to_s(1) }.select{|a| a && a.length > 0}.join(';')
to_return << "#{total_rule}{#{properties}}"
else
attributes = attributes.map { |a| a.to_s(tabs + 1) }.select{|a| a && a.length > 0}.join("\n")
end_attrs = (style == :expanded ? "\n" + old_spaces : ' ')
to_return << "#{total_rule} {\n#{attributes}#{end_attrs}}\n"
properties = properties.map { |a| a.to_s(tabs + 1) }.select{|a| a && a.length > 0}.join("\n")
end_props = (style == :expanded ? "\n" + old_spaces : ' ')
to_return << "#{total_rule} {\n#{properties}#{end_props}}\n"
end
end
tabs += 1 unless attributes.empty? || style != :nested
tabs += 1 unless properties.empty? || style != :nested
sub_rules.each do |sub|
to_return << sub.to_s(tabs, resolved_rules)
end

View File

@ -15,19 +15,19 @@ class SassEngineTest < Test::Unit::TestCase
"!a = foo(\"bar\"" => 'Expected rparen token, was end of text.',
"!a = 1 }" => 'Unexpected end_interpolation token.',
"!a = 1 }foo\"" => 'Unexpected end_interpolation token.',
":" => 'Invalid attribute: ":".',
": a" => 'Invalid attribute: ": a".',
":= a" => 'Invalid attribute: ":= a".',
"a\n :b" => 'Invalid attribute: ":b " (no value).',
"a\n b:" => 'Invalid attribute: "b: " (no value).',
"a\n :b: c" => 'Invalid attribute: ":b: c".',
"a\n :b:c d" => 'Invalid attribute: ":b:c d".',
"a\n :b=c d" => 'Invalid attribute: ":b=c d".',
"a\n :b c;" => 'Invalid attribute: ":b c;" (no ";" required at end-of-line).',
"a\n b: c;" => 'Invalid attribute: "b: c;" (no ";" required at end-of-line).',
"a\n b : c" => 'Invalid attribute: "b : c".',
"a\n b=c: d" => 'Invalid attribute: "b=c: d".',
":a" => 'Attributes aren\'t allowed at the root of a document.',
":" => 'Invalid property: ":".',
": a" => 'Invalid property: ": a".',
":= a" => 'Invalid property: ":= a".',
"a\n :b" => 'Invalid property: ":b " (no value).',
"a\n b:" => 'Invalid property: "b: " (no value).',
"a\n :b: c" => 'Invalid property: ":b: c".',
"a\n :b:c d" => 'Invalid property: ":b:c d".',
"a\n :b=c d" => 'Invalid property: ":b=c d".',
"a\n :b c;" => 'Invalid property: ":b c;" (no ";" required at end-of-line).',
"a\n b: c;" => 'Invalid property: "b: c;" (no ";" required at end-of-line).',
"a\n b : c" => 'Invalid property: "b : c".',
"a\n b=c: d" => 'Invalid property: "b=c: d".',
":a" => 'Properties aren\'t allowed at the root of a document.',
"!" => 'Invalid variable: "!".',
"!a" => 'Invalid variable: "!a".',
"! a" => 'Invalid variable: "! a".',
@ -38,7 +38,7 @@ class SassEngineTest < Test::Unit::TestCase
"!a = 2px + #ccc" => "Cannot add a number with units (2px) to a color (#cccccc).",
"!a = #ccc + 2px" => "Cannot add a number with units (2px) to a color (#cccccc).",
"& a\n :b c" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 1],
"a\n :b\n c" => "Illegal nesting: Only attributes may be nested beneath attributes.",
"a\n :b\n c" => "Illegal nesting: Only properties may be nested beneath properties.",
"a,\n :b c" => ["Rules can\'t end in commas.", 1],
"a," => "Rules can\'t end in commas.",
"a,\n!b = 1" => ["Rules can\'t end in commas.", 1],
@ -66,7 +66,7 @@ class SassEngineTest < Test::Unit::TestCase
"=a(,)" => "Mixin arguments can't be empty.",
"=a(!)" => "Mixin arguments can't be empty.",
"=a(!foo bar)" => "Invalid variable \"!foo bar\".",
"=foo\n bar: baz\n+foo" => ["Attributes aren't allowed at the root of a document.", 2],
"=foo\n bar: baz\n+foo" => ["Properties aren't allowed at the root of a document.", 2],
"a-\#{!b\n c: d" => ["Expected end_interpolation token, was end of text.", 1],
"=a(!b = 1, !c)" => "Required arguments must not follow optional arguments \"!c\".",
"=a(!b = 1)\n :a= !b\ndiv\n +a(1,2)" => "Mixin a takes 1 argument but 2 were passed.",
@ -83,7 +83,7 @@ class SassEngineTest < Test::Unit::TestCase
'@debug' => "Invalid debug directive '@debug': expected expression.",
# Regression tests
"a\n b:\n c\n d" => ["Illegal nesting: Only attributes may be nested beneath attributes.", 3],
"a\n b:\n c\n d" => ["Illegal nesting: Only properties may be nested beneath properties.", 3],
"& foo\n bar: baz\n blat: bang" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 1],
"a\n b: c\n& foo\n bar: baz\n blat: bang" => ["Base-level rules cannot contain the parent-selector-referencing character '&'.", 3],
}
@ -140,7 +140,7 @@ class SassEngineTest < Test::Unit::TestCase
def test_exception_line
to_render = <<SASS
rule
:attr val
:prop val
// comment!
:broken
@ -157,7 +157,7 @@ SASS
def test_exception_location
to_render = <<SASS
rule
:attr val
:prop val
// comment!
:broken
@ -244,21 +244,21 @@ SASS
def test_colon_only
begin
render("a\n b: c", :attribute_syntax => :old)
render("a\n b: c", :property_syntax => :old)
rescue Sass::SyntaxError => e
assert_equal("Illegal attribute syntax: can't use new syntax when :attribute_syntax => :old is set.",
assert_equal("Illegal property syntax: can't use new syntax when :property_syntax => :old is set.",
e.message)
else
assert(false, "SyntaxError not raised for :attribute_syntax => :old")
assert(false, "SyntaxError not raised for :property_syntax => :old")
end
begin
render("a\n :b c", :attribute_syntax => :new)
render("a\n :b c", :property_syntax => :new)
rescue Sass::SyntaxError => e
assert_equal("Illegal attribute syntax: can't use old syntax when :attribute_syntax => :new is set.",
assert_equal("Illegal property syntax: can't use old syntax when :property_syntax => :new is set.",
e.message)
else
assert(false, "SyntaxError not raised for :attribute_syntax => :new")
assert(false, "SyntaxError not raised for :property_syntax => :new")
end
end
@ -699,7 +699,7 @@ SASS
# Regression tests
def test_comment_beneath_attr
def test_comment_beneath_prop
assert_equal(<<RESULT, render(<<SOURCE))
.box {
border-style: solid; }