Skip to content

Commit 087ed3a

Browse files
committed
Add styling macro.
1 parent 5c170ad commit 087ed3a

10 files changed

Lines changed: 336 additions & 48 deletions

File tree

html-node-core/src/node/doctype.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub struct Doctype {
1111
/// The value of the doctype.
1212
///
1313
/// ```html
14-
/// <!DOCTYPE synax>
14+
/// <!DOCTYPE syntax>
1515
/// ```
1616
pub syntax: String,
1717
}

html-node-core/src/node/unsafe_text.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub struct UnsafeText {
1414
}
1515

1616
impl Display for UnsafeText {
17-
/// Unformatted text.
17+
/// Unescaped text.
1818
///
1919
/// This string is **not** HTML encoded!
2020
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {

html-node-macro/Cargo.toml

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
[package]
22
name = "html-node-macro"
3-
authors.workspace = true
4-
categories.workspace = true
3+
4+
authors.workspace = true
5+
categories.workspace = true
56
description.workspace = true
6-
edition.workspace = true
7-
homepage.workspace = true
8-
keywords.workspace = true
9-
license.workspace = true
10-
readme.workspace = true
11-
repository.workspace = true
12-
version.workspace = true
7+
edition.workspace = true
8+
homepage.workspace = true
9+
keywords.workspace = true
10+
license.workspace = true
11+
readme.workspace = true
12+
repository.workspace = true
13+
version.workspace = true
1314

1415
[package.metadata.docs.rs]
1516
all-features = true
@@ -19,12 +20,13 @@ version.workspace = true
1920
proc-macro = true
2021

2122
[dependencies]
22-
proc-macro2 = "1"
23+
proc-macro2 = "1"
2324
proc-macro2-diagnostics = { version = "0.10", default-features = false }
24-
quote = "1"
25-
rstml = { version = "0.11", default-features = false }
26-
syn = "2"
27-
syn_derive = { version = "0.1", optional = true }
25+
quote = "1"
26+
rstml = { version = "0.11", default-features = false }
27+
syn = "2"
28+
syn_derive = { version = "0.1", optional = true }
2829

2930
[features]
30-
typed = ["dep:syn_derive"]
31+
basic-css = []
32+
typed = ["dep:syn_derive"]

html-node-macro/src/lib.rs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,29 @@
77

88
mod node_handlers;
99

10-
use std::{
11-
collections::{HashMap, HashSet},
12-
os::linux::raw,
13-
};
10+
use std::collections::{HashMap, HashSet};
1411

1512
use node_handlers::{
1613
handle_block, handle_comment, handle_doctype, handle_element, handle_fragment, handle_raw_text,
1714
handle_text,
1815
};
1916
use proc_macro::TokenStream;
20-
use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
17+
use proc_macro2::{Ident, TokenStream as TokenStream2};
2118
use proc_macro2_diagnostics::Diagnostic;
22-
use quote::{quote, quote_spanned};
19+
use quote::quote;
20+
#[cfg(feature = "basic-css")]
21+
use quote::quote_spanned;
2322
use rstml::{node::Node, Parser, ParserConfig};
24-
use syn::{spanned::Spanned, Type};
23+
#[cfg(feature = "basic-css")]
24+
use syn::spanned::Spanned;
25+
use syn::Type;
2526

2627
#[proc_macro]
2728
pub fn html(tokens: TokenStream) -> TokenStream {
2829
html_inner(tokens.into(), None)
2930
}
3031

32+
#[cfg(feature = "basic-css")]
3133
#[proc_macro]
3234
pub fn style(tokens: TokenStream) -> TokenStream {
3335
style_inner(tokens.into())
@@ -178,11 +180,22 @@ fn tokenize_nodes(
178180
(token_streams, diagnostics)
179181
}
180182

183+
/// Naive conversion of a rust token stream into css content.
184+
///
185+
/// Strips all whitespace from the given tokens, concatenates them into a
186+
/// single string and returns a token stream of the given css content
187+
/// wrapped in an HTML style tag.
188+
#[cfg(feature = "basic-css")]
181189
fn style_inner(tokens: TokenStream2) -> TokenStream {
182190
let span = tokens.span();
183191
let raw_css = tokens
184192
.into_iter()
185-
.map(|t| t.to_string().split_whitespace().collect::<String>())
193+
.map(|token_tree| {
194+
token_tree
195+
.to_string()
196+
.split_whitespace()
197+
.collect::<String>()
198+
})
186199
.collect::<String>();
187200

188201
quote_spanned! { span=>

html-node-macro/src/node_handlers/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::collections::{HashMap, HashSet};
55

66
use proc_macro2::{Ident, Literal, TokenStream as TokenStream2};
77
use proc_macro2_diagnostics::{Diagnostic, SpanDiagnosticExt};
8-
use quote::{quote, quote_spanned, ToTokens};
8+
use quote::{quote, ToTokens};
99
use rstml::node::{
1010
KeyedAttribute, NodeAttribute, NodeBlock, NodeComment, NodeDoctype, NodeElement, NodeFragment,
1111
NodeName, NodeText, RawText,

html-node/Cargo.toml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ required-features = ["axum"]
2626
name = "typed_custom_attributes"
2727
required-features = ["typed"]
2828

29+
[[example]]
30+
name = "styling"
31+
required-features = ["basic-css"]
32+
2933
[dependencies]
3034
html-node-core = { version = "0.2", path = "../html-node-core" }
3135
html-node-macro = { version = "0.2", path = "../html-node-macro" }
@@ -35,7 +39,8 @@ axum = "0.6"
3539
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
3640

3741
[features]
38-
axum = ["html-node-core/axum"]
39-
pretty = ["html-node-core/pretty"]
40-
serde = ["html-node-core/serde"]
41-
typed = ["html-node-core/typed", "html-node-macro/typed"]
42+
axum = ["html-node-core/axum"]
43+
basic-css = ["html-node-macro/basic-css"]
44+
pretty = ["html-node-core/pretty"]
45+
serde = ["html-node-core/serde"]
46+
typed = ["html-node-core/typed", "html-node-macro/typed"]

html-node/examples/axum.rs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -100,18 +100,3 @@ async fn pretty() -> Pretty {
100100
</div>
101101
}))
102102
}
103-
104-
async fn css() -> Pretty {
105-
Pretty(layout(html! {
106-
{ style! {
107-
html {
108-
margin: 0;
109-
padding: 0;
110-
height: 100vh;
111-
}
112-
} }
113-
<div>
114-
<h1>Pretty</h1>
115-
</div>
116-
}))
117-
}

html-node/examples/styling.rs

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
use html_node::{html, style};
2+
3+
fn main() {
4+
bare_style();
5+
string_style();
6+
macro_style_unsupported_css();
7+
macro_style_supported_css();
8+
}
9+
10+
/// Try and directly insert CSS into the style element.
11+
///
12+
/// # Output
13+
///
14+
/// ```text
15+
/// Bare style:
16+
/// <div>
17+
/// <style>
18+
/// ul { outline : 5px solid # CCDDFF ; padding - top : 15px ; }
19+
/// </style>
20+
/// <ul>
21+
/// <li>
22+
/// one
23+
/// </li>
24+
/// <li>
25+
/// two
26+
/// </li>
27+
/// </ul>
28+
/// </div>
29+
/// ```
30+
fn bare_style() {
31+
let node = html! {
32+
<div>
33+
<style>
34+
ul {
35+
outline: 5px solid #CCDDFF;
36+
padding-top: 15px;
37+
}
38+
</style>
39+
<ul>
40+
<li>one</li>
41+
<li>two</li>
42+
</ul>
43+
</div>
44+
};
45+
46+
println!("Bare style:\n{node:#}");
47+
}
48+
49+
/// Try and insert CSS into the style element via a string.
50+
///
51+
/// # Output
52+
///
53+
/// ```text
54+
/// String style:
55+
/// <div><style>"
56+
/// ul {
57+
/// outline: 5px solid #CCDDFF;
58+
/// padding-top: 15px;
59+
/// }
60+
/// "</style><ul><li>one</li><li>two</li></ul></div>
61+
///
62+
/// Pretty string style:
63+
/// <div>
64+
/// <style>
65+
/// "
66+
/// ul {
67+
/// outline: 5px solid #CCDDFF;
68+
/// padding-top: 15px;
69+
/// }
70+
/// "
71+
/// </style>
72+
/// <ul>
73+
/// <li>
74+
/// one
75+
/// </li>
76+
/// <li>
77+
/// two
78+
/// </li>
79+
/// </ul>
80+
/// </div>
81+
/// ```
82+
fn string_style() {
83+
let node = html! {
84+
<div>
85+
<style>"
86+
ul {
87+
outline: 5px solid #CCDDFF;
88+
padding-top: 15px;
89+
}
90+
"</style>
91+
<ul>
92+
<li>one</li>
93+
<li>two</li>
94+
</ul>
95+
</div>
96+
};
97+
98+
println!("String style:\n{node}");
99+
println!("Pretty string style:\n{node:#}");
100+
}
101+
102+
/// Insert a style element and inner CSS content.
103+
///
104+
/// The macro naively strips all whitespace from the CSS content, meaning the
105+
/// shorthand version of outline as used below will still be rendered
106+
/// incorrectly. See the next example for a workaround.
107+
///
108+
/// Note that the `<style></style>` tags are inserted by the macro.
109+
///
110+
/// # Output
111+
///
112+
/// ```text
113+
/// Macro + unsupported CSS style:
114+
/// <div>
115+
/// <style>
116+
/// ul{outline:5pxsolid#CCDDFF;padding-top:15px;}
117+
/// </style>
118+
/// <ul>
119+
/// <li>
120+
/// one
121+
/// </li>
122+
/// <li>
123+
/// two
124+
/// </li>
125+
/// </ul>
126+
/// </div>
127+
/// ```
128+
fn macro_style_unsupported_css() {
129+
let node = html! {
130+
<div>
131+
{ style! {
132+
ul {
133+
outline: 5px solid #CCDDFF;
134+
padding-top: 15px;
135+
}
136+
} }
137+
<ul>
138+
<li>one</li>
139+
<li>two</li>
140+
</ul>
141+
</div>
142+
};
143+
144+
println!("Macro + unsupported CSS style:\n{node:#}");
145+
}
146+
147+
/// Insert a style element and inner CSS content, correctly.
148+
///
149+
/// Since the macro strips all whitespace, use long-form CSS properties to
150+
/// specify the needed selectors and rules.
151+
///
152+
/// Note that the `<style></style>` tags are inserted by the macro.
153+
///
154+
/// # Output
155+
///
156+
/// ```text
157+
/// Macro + CSS style:
158+
/// <div>
159+
/// <style>
160+
/// ul{outline-width:5px;outline-style:solid;outline-color:#CCDDFF;padding-top:15px;}
161+
/// </style>
162+
/// <ul>
163+
/// <li>
164+
/// one
165+
/// </li>
166+
/// <li>
167+
/// two
168+
/// </li>
169+
/// </ul>
170+
/// </div>
171+
/// ```
172+
fn macro_style_supported_css() {
173+
let node = html! {
174+
<div>
175+
{ style! {
176+
ul {
177+
outline-width: 5px;
178+
outline-style: solid;
179+
outline-color: #CCDDFF;
180+
padding-top: 15px;
181+
}
182+
} }
183+
<ul>
184+
<li>one</li>
185+
<li>two</li>
186+
</ul>
187+
</div>
188+
};
189+
190+
println!("Macro + CSS style:\n{node:#}");
191+
}

0 commit comments

Comments
 (0)