Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

This article is a summary of learning and using Stylus, an expressive, dynamic, and robust CSS preprocessor.

Characteristics of the

Colon optional optional comma, semicolon optional, optional brackets, variables, interpolation, mixed writing, and arithmetic, casts, dynamic import, conditions, choice of iteration, nested, parent reference, variable, function calls, lexical scope, built-in functions, internal language function, optional compression, image inline optional, executable Stylus, robust error reporting, single – and multi-line comments, CSS literals, character escapes, TextMate bindings, and more.

use

Global installation

$ npm install stylus -g
Copy the code

Compiling a single file

stylus demo.styl
Copy the code

Compile the entire folder

Current folder under stylusCopy the code

Dynamically listen to the entire folder

stylus -w
Copy the code

Dynamically listen to the entire folder and compress

stylus -w --compress
Copy the code

The basic grammar

The selector

indentation

The Stylus syntax is based on indentation; Spaces have significant meaning and use indentation and indentation instead of curly braces {and}.

body
  color white
Copy the code

The above code corresponds to:

body {
  color: #fff;
}
Copy the code

Can be separated by a colon to make reading easier:

body
  color: white
Copy the code

A set of rules

Stylus, like CSS, allows you to define attributes for multiple selectors simultaneously using commas.

textarea.input
  border 1px solid #eee
Copy the code

Using a new line has the same effect:

textarea
input
  border 1px solid #eee
Copy the code

Is equal to:

textarea.input {
  border: 1px solid #eee;
}
Copy the code

The only exception to this rule is selectors that look like attributes. For example, foo bar baz below might be an attribute or a selector.

foo bar baz
> input
  border 1px solid
Copy the code

To solve this problem, put a comma at the end:

foo bar baz,
form input,
> a
  border 1px solid
Copy the code

The parent reference

The character & points to the parent selector. In this example, two selectors (textarea and input) change the color value on the :hover pseudo-class selector

textarea
input
  color #A7A7A7
  &:hover
    color # 000
Copy the code

Is equal to:

textarea.input {
  color: #a7a7a7;
}
textarea:hover.input:hover {
  color: # 000;
}
Copy the code

In this example, Internet Explorer uses parent references and mixed writing to achieve a 2px border.

  box-shadow()
    -webkit-box-shadow arguments
    -moz-box-shadow arguments
    box-shadow arguments
    html.ie8 &,
    html.ie7 &,
    html.ie6 &
      border 2px solid arguments[length(arguments) - 1]

  body
    #login
      box-shadow 1px 1px 3px #eee
Copy the code

Results:

  body #login {
    -webkit-box-shadow: 1px 1px 3px #eee;
    -moz-box-shadow: 1px 1px 3px #eee;
    box-shadow: 1px 1px 3px #eee;
  }
  html.ie8 body #login.html.ie7 body #login.html.ie6 body #login {
    border: 2px solid #eee;
  }
Copy the code

disambiguation

A padding-n-like expression may be interpreted as either a subtraction operation or a unary minus attribute. To avoid this ambiguity, wrap the expression in parentheses:

pad(n)
  padding (- n)

body
  pad(5px)
Copy the code

Results:

body {
  padding: -5px;
}
Copy the code

However, this is only true in a function (because the function also acts as a mix or callback with the return value). For example, the following is OK:

body
  padding -5px
Copy the code

Unquote () can handle attribute values that Stylus cannot:

filter unquote('progid:DXImageTransform.Microsoft.BasicImage(rotation=1)')
Copy the code

Results:

filter progid:DXImageTransform.Microsoft.BasicImage(rotation=1)
Copy the code

variable

variable

We can specify the expression as a variable and then use it throughout the style:

font-size = 14px

body
  font font-size Arial, sans-seri
Copy the code

Results:

body {
  font: 14px Arial, sans-serif;
}
Copy the code

Variables can even form a list of expressions:

font-size = 14px
font = font-size "Lucida Grande", Arial

body
  font font sans-serif
Copy the code

Results:

body {
  font: 14px "Lucida Grande", Arial sans-serif;
}
Copy the code

Identifiers (variable names, functions, etc.) may also include the $character. Such as:

$font-size = 14px
body {
  font: $font-size sans-serif;
}
Copy the code

Attributes to find

Stylus has another cool unique feature that allows you to define reference properties without assigning values to variables. Here is a good example of an element centered horizontally and vertically (typically using percentages and margin negative values), as follows:

#logo
  position: absolute
  top: 50%
  left: 50%
  width: w = 150px
  height: h = 80px
  margin-left: -(w / 2)
  margin-top: -(h / 2)
Copy the code

Instead of using the variables w and h here, we simply prefix the @ character before the attribute name to access the value corresponding to that attribute name:

#logo
  position: absolute
  top: 50%
  left: 50%
  width: 150px
  height: 80px
  margin-left: -(@width / 2)
  margin-top: -(@height / 2)
Copy the code

In addition, use cases define attributes conditionally based on other attributes. In the following example, we specify a z-index value of 1 by default, but only if z-index is not specified before:

position(a)
  position: arguments
  z-index: 1 unless @z-index

#logo
  z-index: 20
  position: absolute

#logo2
  position: absolute
Copy the code

The property bubbles up the stack until it is found, or returns null if the property is undecided. In this example, the @color value is blue.

body
  color: red
  ul
    li
      color: blue
      a
        background-color: @color
Copy the code

The interpolation

The interpolation

Stylus supports inserting values by enclosing expressions with {} characters that become part of the identifier. For example, -webkit-{‘border’ + ‘-radius’} is equivalent to -webkit-border-radius. A good example is the private prefix attribute extension:

vendor(prop, args)
  -webkit-{prop} args
  -moz-{prop} args
  {prop} args

border-radius(a)
  vendor('border-radius', arguments)

box-shadow(a)
  vendor('box-shadow', arguments)

button
  border-radius 1px 2px / 3px 4px
Copy the code

Results:

button {
  -webkit-border-radius: 1px 2px / 3px 4px;
  -moz-border-radius: 1px 2px / 3px 4px;
  border-radius: 1px 2px / 3px 4px;
}
Copy the code

Selector interpolation

Interpolation can also work on selectors. For example, we could specify the height of the first five rows of the table as follows:

table
  for row in 1 2 3 4 5
    tr:nth-child({row})
      height: 10px * row
Copy the code

Results:

table tr:nth-child(1) {
  height: 10px;
}
table tr:nth-child(2) {
  height: 20px;
}
table tr:nth-child(3) {
  height: 30px;
}
table tr:nth-child(4) {
  height: 40px;
}
table tr:nth-child(5) {
  height: 50px;
}
Copy the code

The operator

Operator priority

The following table lists the operators’ priorities, from highest to lowest:

[]! ~ + - is defined ** * / % + - ... . The < = > = < >in== is ! = is not isnt isa
&& and || or
?:
= := ?= += -= *= /= %=
not
if unless
Copy the code

Unary operator

The following unary operators are available,! , not, -, + and ~.

!0              // => true!!!!!0             // => false
!1              // => false!!!!!5px           // => true
-5px            // => -5px
--5px           // => 5px
not true        // => false
not not true    // => true
Copy the code

The logical operator NOT has a lower priority, so the following example can be replaced:

a = 0
b = 1
!a and !b       // => false: (! a) and (! b)
Copy the code

To use:

not a or b      // => false; not (a or b)
Copy the code

Binary operator

The subscript operator [] allows us to get the inner value of an expression by index. Parenthesis expressions can act as tuples (e.g. (15px 5px), (1, 2, 3)).

The following example uses error-handling tuples (and demonstrates the versatility of the structure) :

add(a, b)
  if a is a 'unit' and b is a 'unit'
    a + b
  else
    (error 'A and B must be units! ')

body
  padding add(1.'5')              // => padding: error "A and B have to be units.";
  padding add(1.'5') [0]           // => padding: error;
  padding add(1.'5') [0] == error  // => padding: true;
  padding add(1.'5') [1]           // => padding: "A and B have to be units.";
Copy the code

Here’s a more complicated example. Now, we call the built-in error() function, which returns an error message when the identifier (the first value) equals error.

if (val = add(1.'5'))[0] == error
  error(val[1])
Copy the code

The scope of. .

Also provides the contain boundary operator (..) And range operators (…) , see the following expression:

1.5                              // => 1 2 3 4 5
1.5                             // => 1 2 3 4
Copy the code

Add:+ -

Binary multiplicative operations convert units, or use default literal values. For example, 5s-2px is 3s.

15px - 5px                        // => 10px
5 - 2                             / / = > 3
5in - 50mm                        / / = > 3.031 in
5s - 1000ms                       // => 4s
20mm + 4in                        / / = > 121.6 mm
"foo " + "bar"                    // => "foo bar"
"num " + 15                       // => "num 15"
Copy the code

Battles:/ * %

2000ms + (1s * 2)                 // => 4000ms
5s / 2                            / / = > 2.5 s
4 % 2                             / / = > 0
Copy the code

When using/in attribute values, you must enclose parentheses. Otherwise/is treated literally (CSS line-height is supported).

font: 14px/1.5;                   // However, this is equivalent to 14px ÷ 1.5:
font: (14px/1.5);                 // Only the/operator requires this.
Copy the code

Index: * *

Index operators:

2 ** 8                            / / = > 256
Copy the code

Operation of equality and relation:= = ! = > = < = > <

The equality operator can be used to equate units, colors, strings, and even identifiers. This is such a powerful concept that even arbitrary identifiers (like Wahoo) can be used as atoms. The function can return yes and no instead of true and false (although it is not recommended).

5= =5                            // => true
10 > 5                            // => true
#fff= =#fff                      // => true
true == false                     // => false
wahoo == yay                      // => false
wahoo == wahoo                    // => true
"test"= ="test"                  // => true
true is true                      // => true
'hey' is not 'bye'                // => true
'hey' isnt 'bye'                  // => true
(foo bar) == (foo bar)            // => true
(1 2 3) = = (1 2 3)                // => true
(1 2 3) = = (1 1 3)                // => false
Copy the code

Only exact values are matched, for example, 0 == false and null == False return false.

Alias:

  • = =: is
  • ! =: is not
  • ! =: isnt

True and false

Stylus is true for almost everything, including suffixed units, even 0%, 0px, etc. However, 0 itself is false in arithmetic, and expressions (or “lists”) of length greater than 1 are considered true.

True examples:

0%
0px
1px
-1
-1px
hey
'hey'
(0 0 0)
(' ' ' ')
Copy the code

False examples:

0
null
false
Copy the code

Logical operators:&& ||or

Logical operators && and | | alias is and/or. They have the same priority.

5 && 3                                / / = > 3
0 || 5                                / / = > 5
0 && 5                                / / = > 0
#fff is a 'rgba' and 15 is a 'unit'   // => true
Copy the code

Presence operator:in

Check to see if the left-hand side is in the right-hand expression. A simple example:

nums = 1 2 3
1 in nums                             // => true
5 in nums                             // => false
Copy the code

Some undefined identifiers:

words = foo bar baz
bar in words                          // => true
HEY in words                          // => false
Copy the code

Tuples also apply:

vals = (error 'one') (error 'two')
error in vals                         // => false
(error 'one') in vals                 // => true
(error 'two') in vals                 // => true
(error 'something') in vals           // => false
Copy the code

Examples of mixed writing:

pad(types = padding, n = 5px)
  if padding in types
    padding n
  if margin in types
    margin n

body
  pad()
body
  pad(margin)
body
  pad(padding margin, 10px)
Copy the code

Corresponds to:

body {
  padding: 5px;
}
body {
  margin: 5px;
}
body {
  padding: 10px;
  margin: 10px;
}
Copy the code

Conditional assignment:? = : =

Conditional assignment operator? = (alias? 🙂 makes it necessary to define variables without destroying old values (if any). This operator can be extended to binary operations with the ternary is defined. For example, the following functions are the same:

color := white
color? = whitecolor = color is defined ? color : white
Copy the code

If we use the equal sign =, we simply assign.

color = white
color = black
color                             // => black
Copy the code

But when used? =, the second assignment is invalid:

color = white
color? = blackcolor                             // => white
Copy the code

Example check:is a

The Stylus provides a binary operator called is A for type checking.

15 is a 'unit'                    // => true
#fff is a 'rgba'                  // => true
15 is a 'rgba'                    // => false
Copy the code

Alternatively, we can use the type() built-in function.

type(#fff)= ='rgba'              // => true
Copy the code

Note: color is the only special case, true when the left is an RGBA or HSLA node.

Variable definition:is defined

This pseudo-binary operator is empty on the right and computes on the left. Used to check whether a variable has been assigned a value.

foo is defined                    // => false
foo = 15px
foo is defined                    // => true
#fff is defined                   // => 'invalid "is defined" check on non-variable #fff'
Copy the code

Alternatively, we can do this active lookup using the built-in lookup(name) method.

name = 'blue'
lookup('light-' + name)           // => null
light-blue = #80e2e9
lookup('light-' + name)           // => #80e2e9
Copy the code

This operator is necessary because an undefined identifier is still true. Such as:

body
  if ohnoes
    padding 5px
Copy the code

When undefined, the following CSS is generated:

body {
  padding: 5px;
}
Copy the code

Obviously, this is not what we want, so it’s safe to write:

body
  if ohnoes is defined
    padding 5px
Copy the code

Three yuan

Ternary operators work just as they do in most languages. Operators (conditional expressions, true expressions, and false expressions) for three operation objects.

num = 15
num ? unit(num, 'px') : 20px                // => 15px
Copy the code

casting

Instead of the terse built-in unit() function, the syntax (expr) unit can be used to enforce suffixes.

body
  n = 5
  foo: (n)em
  foo: (n)%
  foo: (n + 5)%
  foo: (n * 5)px
  foo: unit(n + 5.The '%')
  foo: unit(5 + 180 / 2, deg)
Copy the code

Color operation

Color manipulation provides a concise, expressive way to change its composition. For example, we can apply each RGB:

#0e0 + #0e0                                 // => #0f0
Copy the code

Another example is adjusting color brightness by increasing or decreasing the hundred points. Bright color, plus; Dark, minus.

# 888 + 50%                                  // => #c3c3c3
# 888 - 50%                                  / / = > # 444
Copy the code

We can also adjust the hue by adding or subtracting the hue. For example, red increases by 65deg and becomes yellow.

#f00 + 50deg                                // => #ffd500
Copy the code

Values are properly fixed. For example, we can “rotate” the hue by 180 degrees, and if the current value is 320deg, it will become 140deg.

We may also adjust several values (including alpha) at once, by using RGB (), RGBA (), HSL (), or HSLA ():

#f00 - rgba(100.0.0.0.5)                    / / = > rgba (155,0,0,0.5)
Copy the code

Formatted string

The formatted string % can be used to generate literal values by passing arguments to the built-in s() method.

'X::Microsoft::Crap(%s)' % #fc0             // => X::Microsoft::Crap(#fc0)
Copy the code

Multiple values need to be enclosed:

'-webkit-gradient(%s, %s, %s)' % (linear (0 0) (0 100%))      // => -webkit-gradient(linear, 0 0, 0 100%)
Copy the code

Mix writing

Mixed with

Blending is the same as the method of function definition, but the application is quite different. For example, here is the border-radius(n) method defined, which is called as a mixin (i.e., as a state call, not an expression). When called in the border-radius() selector, the attribute is extended and copied in the selector.

border-radius(n)
  -webkit-border-radius n
  -moz-border-radius n
  border-radius n

form input[type=button]
  border-radius(5px)
Copy the code

Compiled into:

form input[type=button] {
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  border-radius: 5px;
}
Copy the code

With mixin writing, parentheses can be ignored entirely.

border-radius(n)
  -webkit-border-radius n
  -moz-border-radius n
  border-radius n

form input[type=button]
  border-radius 5px
Copy the code

Notice that we mixed the border-radius writing as a property, not a recursive function call.

Further, we can use arguments as a local variable to pass expressions that can contain multiple values.

border-radius(a)
  -webkit-border-radius arguments
  -moz-border-radius arguments
  border-radius arguments
Copy the code

Now, we can pass values like this: border-radius 1px 2px / 3px 4px Another nice application is specific private prefix support — IE transparency for example:

support-for-ie ? = trueopacity(n)
  opacity n
  if support-for-ie
    filter unquote('progid:DXImageTransform.Microsoft.Alpha(Opacity=' + round(n * 100) + ') ')

#logo
  &:hover
    opacity 0.5
Copy the code

Apply colours to a drawing as follows:

#logo:hover {
  opacity: 0.5;
  filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50);
}
Copy the code

The parent reference

Mixed writing can take advantage of the parent reference character &, inheriting the father’s work rather than building its own nest.

For example, we want to create a stripe table with stripe(even, odd). Both even and odd provide default color values, and each line also specifies background-color. We can reference tr with & in tr nesting to provide even color.

stripe(even = #fff, odd = #eee)
 tr
   background-color odd
   &.even
   &:nth-child(even)
       background-color even
Copy the code

Then, use mixed writing as follows:

table
  stripe()
  td
    padding 4px 10px

table#users
  stripe(# 303030.# 494848)
  td
    color white
Copy the code

In addition, stripe() is defined without a parent reference:

stripe(even = #fff, odd = #eee)
  tr
    background-color odd
  tr.even
  tr:nth-child(even)
    background-color even
Copy the code

If you like, you can call stripe() as an attribute.

stripe #fff # 000
Copy the code

Mixed writing of mixed writing

Mixed writing can take advantage of other mixed writing, based on their own properties and selectors. For example, let’s create inline comma-list() (via inline-list()) and comma-separated unordered lists.

inline-list(a)
  li
    display inline

comma-list(a)
  inline-list()
  li
    &:after
      content ', '
    &:last-child:after
      content ' '

ul
  comma-list()
Copy the code

Render:

ul li:after {
  content: ",";
}
ul li:last-child:after {
  content: "";
}
ul li {
  display: inline;
}
Copy the code

function

function

Stylus’s power lies in its built-in language function definitions. The definition is the same as mixins; It can return a value.

The return value

For example, the method of adding two values

add(a, b)
  a + b

body
  padding add(10px.5)
Copy the code

Render:

body {
  padding: 15px;
}
Copy the code

The default parameters

In Stylus, you can set default parameters. Such as:

add(a, b = a)
  a + b

add(10.5)                        / / = > 15
add(10)                           / / = > 20
Copy the code

Since the argument defaults to assignment, we can use a function call as the default:

add(a, b = unit(a, px))
  a + b
Copy the code

The body of the function

You can take the simple add() method one step further. Change the units to PX with built-in unit(), because assignments are made to each argument, so unit conversions can be ignored.

add(a, b = a)
  a = unit(a, px)
  b = unit(b, px)
  a + b

add(15%.10deg)                 / / = > 25
Copy the code

Multiple return values

Functions in Stylus can return multiple values, just as you assign multiple values to variables. For example, here is a valid assignment:

sizes = 15px 10px

sizes[0]                        // => 15px
Copy the code

Similarly, we can return multiple values in a function:

sizes(a)
 15px 10px

sizes(a)[0]                      // => 15px
Copy the code

The small exception is when the return value is an identifier. For example, the following looks like an attribute assigned to the Stylus (because there is no operator).

swap(a, b)
  b a
Copy the code

To avoid ambiguity, use parentheses, or the return keyword.

swap(a, b)
  (b a)

swap(a, b)
  return b a
Copy the code

conditions

Suppose we want to create a function called stringish() that determines whether the argument is a string. Check if val is a string or indent (like a character). Use yes and no instead of true and false as follows.

stringish(val)
  if val is a 'string' or val is a 'ident'
    yes
  else
    no
Copy the code

Use:

stringish('yay') == yes                 // => true
stringish(yay) == yes                   // => true
stringish(0) == no                      // => true
Copy the code

Note: Yes and no are not Boolean values.

Another example:

compare(a, b)
  if a > b
    higher
  else if a < b
    lower
  else
    equal

/ / use:
compare(5.2)                             // => higher
compare(1.5)                             // => lower
compare(10.10)                           // => equal
Copy the code

The alias

Give the function a different name, such as add() above add alias plus(), as follows:

plus = add

plus(1.2)                                / / = > 3
Copy the code

Variable function

You can pass a function as a variable to a new function. For example, invoke() accepts functions as arguments, so you can pass Add () as well as sub().

invoke(a, b, fn)
  fn(a, b)

add(a, b)
  a + b

body
  padding invoke(5.10, add)
  padding invoke(5.10, sub)
Copy the code

Results:

body {
  padding: 15;
  padding: -5;
}
Copy the code

parameter

Arguments are local variables that all function bodies have, containing all arguments passed.

Such as:

sum(a)
  n = 0
  for num in arguments
    n = n + num

sum(1.2.3.4.5)                              / / = > 15
Copy the code

Hash sample

Next, we define the get(hash, key) method, which returns the key value or null. We iterate over each key value pair and return the corresponding value if the key value matches.

get(hash, key)
  return pair[1] if pair[0] == key for pair in hash
Copy the code

The following example demonstrates that Stylus expressions, which look like language functions, have greater flexibility.

hash = (one 1) (two 2) (three 3)
get(hash, two)                            / / = > 2
get(hash, three)                          / / = > 3
get(hash, something)                      // => null
Copy the code

Keyword parameter

Keyword parameter

Stylus supports keyword parameters, or kwargs. Allows you to reference parameters according to the related parameter name.

The following examples are functionally the same. However, we can place keyword arguments anywhere in the list. The remaining untyped parameters will apply to parameters that have not yet been satisfied.

body {
  color: rgba(255.200.100.0.5);
  color: rgba(red: 255, green: 200, blue: 100, alpha: 0.5);
  color: rgba(alpha: 0.5, blue: 100, red: 255.200);
  color: rgba(alpha: 0.5, blue: 100.255.200);
}
Copy the code

Is equal to:

body {
  color: rgba(255.200.100.0.5);
  color: rgba(255.200.100.0.5);
  color: rgba(255.200.100.0.5);
  color: rgba(255.200.100.0.5);
}
Copy the code

To view arguments accepted in a function or mixed writing, use the p() method.

p(rgba)
Copy the code

Generation:

inspect: rgba(red, green, blue, alpha)
Copy the code

annotation

annotation

Stylus supports three types of annotations, single-line annotations, multi-line annotations, and multi-line buffered annotations.

Single-line comments

Like JavaScript, double slashes are not output in CSS.

// I am a comment!
body
  padding 5px // padding
Copy the code

Multiline comment

Multi-line comments look a bit like regular CSS comments and are only printed if the COMPRESS option is not enabled.

/* * The given value is combined with */
Copy the code

Multi-line buffer comments

Similar to multi-line comments, except that at the beginning, it is /*! This is equivalent to telling the Stylus to ignore the direct output when it compresses.

/ *! * The given numeric combination */
Copy the code

To be continued… , please continue to read Stylus Learning and Using (Ii)