1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
//! This module contains runtime support for the macros and documents their usage.
//!
//! There are two syntaxes available: [string literal] and [inline].
//!
//! # String Literal
//!
//! This syntax is simliar to the [`format!`] macro from the standard library.
//!
//! The first argument of this syntax is a string literal followed by an argument list. This macro
//! will replace `${arg}` with the argument in the argument list when creating the AST.
//!
//! This syntax supports interpolation on values of style attributes, selectors, `@supports` and
//! `@media` rules.
//!
//! Interpolated strings are denoted with `${ident}` and any type that implements [`Display`] can be
//! used as value. Only named argument are supported at this moment.
//!
//! If you do need to print a `${` sequence, you may use `$${` to escape to a `${`.
//!
//! ### Example
//! ```css
//! content: "$${}";
//! ```
//!
//! Will be turned into:
//! ```css
//! content: "${}";
//! ```
//!
//! ### Note:
//!
//! `$${` escape can only present where `${` is valid in the css stylesheet.
//!
//! ## Example
//!
//! ```
//! use stylist::{css, Style};
//! use yew::prelude::*;
//!
//! let s = css!(
//! r#"
//! color: ${color};
//!
//! span, ${sel_div} {
//! background-color: blue;
//! }
//!
//! @media screen and ${breakpoint} {
//! display: flex;
//! }
//! "#,
//! color = "red",
//! sel_div = "div.selected",
//! breakpoint = "(max-width: 500px)",
//! );
//!
//! let style = Style::new(s).expect("Failed to create style");
//!
//! // Example Output:
//! // .stylist-fIEWv6EP {
//! // color: red;
//! // }
//! // stylist-fIEWv6EP span, .stylist-fIEWv6EP div.selected {
//! // background-color: blue;
//! // }
//! // @media screen and (max-width: 500px) {
//! // .stylist-fIEWv6EP {
//! // display: flex;
//! // }
//! // }
//! println!("{}", style.get_style_str());
//! ```
//!
//! # Inline
//!
//! You may also directly inline a stylesheet in the macro.
//!
//! Like in string interpolation syntax, interpolated values are allowed in most places through the
//! `${expr}` syntax. In distinction, the braces contain a rust expression of any type implementing
//! [`Display`] will be evaluated in the surrounding context.
//!
//! ## Known Limitations
//!
//! ### Dimensions
//!
//! Due to the tokenizer of the Rust complier, there are some quirks with literals. For example,
//! `4em` would be tokenized as a floating point literal with a missing exponent and a suffix of
//! `m`. To work around this issue, use string interpolation as in `${"4em"}`. Similarly, some color
//! hash-tokens like `#44444e` as misinterpreted, use the same workaround here: `${"#44444e"}`.
//!
//! ### Arithmetic operators in `calc()`
//!
//! The `+` operator inside `calc()` expressions is not supported in the inline syntax.
//! For example, `top: calc(100vh - 60px + 1rem);` will fail to compile. Use the string literal
//! syntax instead: `css!("top: calc(100vh - 60px + 1rem);")`.
//!
//! Note that `-` works.
//!
//! ### Descendant Selectors
//!
//! The stable Rust tokenizer also currently offers no way to inspect whitespace between tokens, as
//! tracked in [the Span inspection API issue](https://github.com/rust-lang/rust/issues/54725). This means that, e.g. the two
//! selectors `.class-a.class-b` and `.class-a .class-b` can not be differentiated. **The macro errs
//! on side of the former input without any spaces.** If you meant to write the latter, use
//! `.class-a *.class-b`.
//!
//! To be more specific, a space is inserted between two tokens `L R` iff (regardless of the space
//! being present in the macro input):
//! - `L` is either a closing bracket `)}]`, an identifier `red`, a literal string `"\e600"` or
//! number `3px`, or the '*' character.
//! - `R` is either an identifier, a literal string or number, the '*' or '#' character.
//!
//! Spacing around interpolation is ignored regardless.
//!
//! Be aware that the above is subject to change once the Span API is stabilized. To avoid future
//! rewriting, use spacing in your source code that follows the same rules. Refer to the associated [bug report](https://github.com/futursolo/stylist-rs/issues/41)
//! to discuss this limitation and offer additional suggestions
//!
//! ### Identifier (Edition 2021)
//!
//! In Rust edition 2021, you cannot have an identifier before `#`.
//! In other words, a selector like `.class#id` will no longer compile in Rust edition 2021 (See:
//! [Reserving Syntax](https://doc.rust-lang.org/edition-guide/rust-2021/reserving-syntax.html) in Rust
//! 2021 Edition Guide).
//!
//! Stylist previously treats them as if there is a descendant selector in between (`.class#id` is
//! interpreted as `.class #id`).
//!
//! ## Note
//!
//! This syntax provides more precise error locations and advanced diagnostics information.
//!
//! ## Example
//!
//! ```
//! use stylist::{Style, css};
//! use yew::prelude::*;
//!
//! let max_width_cuttoff = "500px";
//! let primary_color = "red";
//! let s = css!(
//! color: ${primary_color};
//!
//! span, ${"div.selected"} {
//! background-color: blue;
//! }
//!
//! @media screen and (max-width: ${max_width_cuttoff}) {
//! display: flex;
//! }
//! );
//!
//! let style = Style::new(s).expect("Failed to create style");
//!
//! // Example Output:
//! // .stylist-fIEWv6EP {
//! // color: red;
//! // }
//! // stylist-fIEWv6EP span, .stylist-fIEWv6EP div.selected {
//! // background-color: blue;
//! // }
//! // @media screen and (max-width: 500px) {
//! // .stylist-fIEWv6EP {
//! // display: flex;
//! // }
//! // }
//! println!("{}", style.get_style_str());
//! ```
//!
//! ## Security Notice
//!
//! Stylist currently does not check or escape the content of interpolated strings. It is possible
//! to pass invalid strings that would result in an invalid stylesheet. In debug mode, if feature
//! `parser` is enabled, Stylist will attempt to parse the stylesheet again after interpolated
//! strings are substituted with its actual value to check if the final stylesheet is valid.
//!
//! [string literal]: #string-literal
//! [inline]: #inline
//! [`Display`]: std::fmt::Display