Skip to content

Commit 9d76579

Browse files
feat: Add keep_at_rules option (#485)
* feat: Add `keep_at_rules` option * refactor(keep-at-rules): refactor keep-at-rules * docs(keep-at-rules): Add docs * fix(keep-at-rules): Remove double space * fix(keep-at-rules): Update bindings * feat(keep-at-rules): Handle keep-at-rules flag in main.rs * feat(keep-at-rules): Add back pushing space when parsing at_rule block * feat(keep-at-rules): Add test for CLI * feat(keep-at-rules): Add failing test * chore: Handle corner case Signed-off-by: Dmitry Dygalo <[email protected]> * chore(ruby): Update lockfile Signed-off-by: Dmitry Dygalo <[email protected]> * chore(ruby): Update lockfile Signed-off-by: Dmitry Dygalo <[email protected]> --------- Signed-off-by: Dmitry Dygalo <[email protected]> Co-authored-by: Dmitry Dygalo <[email protected]>
1 parent 96a47bc commit 9d76579

34 files changed

+580
-137
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
### Added
6+
7+
- `keep_at_rules` option [#265](https://github.com/Stranger6667/css-inline/issues/265)
8+
59
## [0.16.0] - 2025-07-16
610

711
### Added

README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ fn main() -> css_inline::Result<()> {
126126
- `inline_style_tags`. Specifies whether to inline CSS from "style" tags. Default: `true`
127127
- `keep_style_tags`. Specifies whether to keep "style" tags after inlining. Default: `false`
128128
- `keep_link_tags`. Specifies whether to keep "link" tags after inlining. Default: `false`
129+
- `keep_at_rules`. Specifies whether to keep "at-rules" (starting with `@`) after inlining. Default: `false`
129130
- `base_url`. The base URL used to resolve relative URLs. If you'd like to load stylesheets from your filesystem, use the `file://` scheme. Default: `None`
130131
- `load_remote_stylesheets`. Specifies whether remote stylesheets should be loaded. Default: `true`
131132
- `cache`. Specifies cache for external stylesheets. Default: `None`
@@ -157,7 +158,8 @@ The `data-css-inline="ignore"` attribute also allows you to skip `link` and `sty
157158
```
158159

159160
Alternatively, you may keep `style` from being removed by using the `data-css-inline="keep"` attribute.
160-
This is useful if you want to keep `@media` queries for responsive emails in separate `style` tags:
161+
This is useful if you want to keep `@media` queries for responsive emails in separate `style` tags.
162+
Such tags will be kept in the resulting HTML even if the `keep_style_tags` option is set to `false`.
161163

162164
```html
163165
<head>
@@ -169,7 +171,20 @@ This is useful if you want to keep `@media` queries for responsive emails in sep
169171
</body>
170172
```
171173

172-
Such tags will be kept in the resulting HTML even if the `keep_style_tags` option is set to `false`.
174+
Another possibility is to set `keep_at_rules` option to `true`. At-rules cannot be inlined into HTML therefore they
175+
get removed by default. This is useful if you want to keep at-rules, e.g. `@media` queries for responsive emails in
176+
separate `style` tags but inline any styles which can be inlined.
177+
Such tags will be kept in the resulting HTML even if the `keep_style_tags` option is explicitly set to `false`.
178+
179+
```html
180+
<head>
181+
<!-- With keep_at_rules=true "color:blue" will get inlined into <h1> but @media will be kept in <style> -->
182+
<style>h1 { color: blue; } @media (max-width: 600px) { h1 { font-size: 18px; } }</style>
183+
</head>
184+
<body>
185+
<h1>Big Text</h1>
186+
</body>
187+
```
173188

174189
If you'd like to load stylesheets from your filesystem, use the `file://` scheme:
175190

bindings/c/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
### Added
6+
7+
- `keep_at_rules` option [#265](https://github.com/Stranger6667/css-inline/issues/265)
8+
59
## [0.16.0] - 2025-07-16
610

711
### Changed

bindings/c/README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ Possible configurations:
153153
- `inline_style_tags`. Specifies whether to inline CSS from "style" tags. Default: `true`
154154
- `keep_style_tags`. Specifies whether to keep "style" tags after inlining. Default: `false`
155155
- `keep_link_tags`. Specifies whether to keep "link" tags after inlining. Default: `false`
156+
- `keep_at_rules`. Specifies whether to keep "at-rules" (starting with `@`) after inlining. Default: `false`
156157
- `base_url`. The base URL used to resolve relative URLs. If you'd like to load stylesheets from your filesystem, use the `file://` scheme. Default: `NULL`
157158
- `load_remote_stylesheets`. Specifies whether remote stylesheets should be loaded. Default: `true`
158159
- `cache`. Specifies caching options for external stylesheets. Default: `NULL`
@@ -186,7 +187,8 @@ The `data-css-inline="ignore"` attribute also allows you to skip `link` and `sty
186187
```
187188

188189
Alternatively, you may keep `style` from being removed by using the `data-css-inline="keep"` attribute.
189-
This is useful if you want to keep `@media` queries for responsive emails in separate `style` tags:
190+
This is useful if you want to keep `@media` queries for responsive emails in separate `style` tags.
191+
Such tags will be kept in the resulting HTML even if the `keep_style_tags` option is set to `false`.
190192

191193
```html
192194
<head>
@@ -198,7 +200,20 @@ This is useful if you want to keep `@media` queries for responsive emails in sep
198200
</body>
199201
```
200202

201-
Such tags will be kept in the resulting HTML even if the `keep_style_tags` option is set to `false`.
203+
Another possibility is to set `keep_at_rules` option to `true`. At-rules cannot be inlined into HTML therefore they
204+
get removed by default. This is useful if you want to keep at-rules, e.g. `@media` queries for responsive emails in
205+
separate `style` tags but inline any styles which can be inlined.
206+
Such tags will be kept in the resulting HTML even if the `keep_style_tags` option is explicitly set to `false`.
207+
208+
```html
209+
<head>
210+
<!-- With keep_at_rules=true "color:blue" will get inlined into <h1> but @media will be kept in <style> -->
211+
<style>h1 { color: blue; } @media (max-width: 600px) { h1 { font-size: 18px; } }</style>
212+
</head>
213+
<body>
214+
<h1>Big Text</h1>
215+
</body>
216+
```
202217

203218
You can also cache external stylesheets to avoid excessive network requests:
204219

bindings/c/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ pub struct CssInlinerOptions {
8282
pub keep_style_tags: bool,
8383
/// Keep "link" tags after inlining.
8484
pub keep_link_tags: bool,
85+
/// Keep "at-rules" after inlining.
86+
pub keep_at_rules: bool,
8587
/// Whether remote stylesheets should be loaded or not.
8688
pub load_remote_stylesheets: bool,
8789
/// Cache for external stylesheets.
@@ -183,6 +185,7 @@ pub extern "C" fn css_inliner_default_options() -> CssInlinerOptions {
183185
inline_style_tags: true,
184186
keep_style_tags: false,
185187
keep_link_tags: false,
188+
keep_at_rules: false,
186189
base_url: ptr::null(),
187190
load_remote_stylesheets: true,
188191
cache: std::ptr::null(),
@@ -225,6 +228,7 @@ impl TryFrom<&CssInlinerOptions> for InlineOptions<'_> {
225228
inline_style_tags: value.inline_style_tags,
226229
keep_style_tags: value.keep_style_tags,
227230
keep_link_tags: value.keep_link_tags,
231+
keep_at_rules: value.keep_at_rules,
228232
base_url: match base_url {
229233
Some(url) => Some(Url::parse(url).map_err(|_| InlineOptionsError::InvalidUrl)?),
230234
None => None,

bindings/java/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
### Added
6+
7+
- `keep_at_rules` option [#265](https://github.com/Stranger6667/css-inline/issues/265)
8+
59
## [0.16.0] - 2025-07-16
610

711
### Changed

bindings/java/README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ public class ConfigExample {
146146
- **`setInlineStyleTags(boolean)`** - Inline CSS from `<style>` tags (default: `true`)
147147
- **`setKeepStyleTags(boolean)`** - Keep `<style>` tags after inlining (default: `false`)
148148
- **`setKeepLinkTags(boolean)`** - Keep `<link>` tags after inlining (default: `false`)
149+
- **`setKeepAtRules(boolean`** - Keep `at-rules` (starting with `@`) after inlining (default: `false`)
149150
- **`setBaseUrl(String)`** - Base URL for resolving relative URLs (default: `null`)
150151
- **`setLoadRemoteStylesheets(boolean)`** - Load external stylesheets (default: `true`)
151152
- **`setExtraCss(String)`** - Additional CSS to inline (default: `null`)
@@ -215,7 +216,8 @@ The `data-css-inline="ignore"` attribute also allows you to skip `link` and `sty
215216
```
216217

217218
Alternatively, you may keep `style` from being removed by using the `data-css-inline="keep"` attribute.
218-
This is useful if you want to keep `@media` queries for responsive emails in separate `style` tags:
219+
This is useful if you want to keep `@media` queries for responsive emails in separate `style` tags.
220+
Such tags will be kept in the resulting HTML even if the `keep_style_tags` option is set to `false`.
219221

220222
```html
221223
<head>
@@ -227,7 +229,20 @@ This is useful if you want to keep `@media` queries for responsive emails in sep
227229
</body>
228230
```
229231

230-
Such tags will be kept in the resulting HTML even if the `keepStyleTags` option is set to `false`.
232+
Another possibility is to set `keep_at_rules` option to `true`. At-rules cannot be inlined into HTML therefore they
233+
get removed by default. This is useful if you want to keep at-rules, e.g. `@media` queries for responsive emails in
234+
separate `style` tags but inline any styles which can be inlined.
235+
Such tags will be kept in the resulting HTML even if the `keep_style_tags` option is explicitly set to `false`.
236+
237+
```html
238+
<head>
239+
<!-- With keep_at_rules=true "color:blue" will get inlined into <h1> but @media will be kept in <style> -->
240+
<style>h1 { color: blue; } @media (max-width: 600px) { h1 { font-size: 18px; } }</style>
241+
</head>
242+
<body>
243+
<h1>Big Text</h1>
244+
</body>
245+
```
231246

232247
## Performance
233248

bindings/java/src/main/java/org/cssinline/CssInlineConfig.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ public class CssInlineConfig {
1111
/** Keep "link" tags after inlining. */
1212
public final boolean keepLinkTags;
1313

14+
/** Keep "at-rules" after inlining. */
15+
public final boolean keepAtRules;
16+
1417
/** Whether remote stylesheets should be loaded or not. */
1518
public final boolean loadRemoteStylesheets;
1619

@@ -27,11 +30,12 @@ public class CssInlineConfig {
2730
public final int preallocateNodeCapacity;
2831

2932
private CssInlineConfig(boolean inlineStyleTags, boolean keepStyleTags, boolean keepLinkTags,
30-
boolean loadRemoteStylesheets, String baseUrl, String extraCss, int cacheSize,
33+
boolean keepAtRules, boolean loadRemoteStylesheets, String baseUrl, String extraCss, int cacheSize,
3134
int preallocateNodeCapacity) {
3235
this.inlineStyleTags = inlineStyleTags;
3336
this.keepStyleTags = keepStyleTags;
3437
this.keepLinkTags = keepLinkTags;
38+
this.keepAtRules = keepAtRules;
3539
this.loadRemoteStylesheets = loadRemoteStylesheets;
3640
this.baseUrl = baseUrl;
3741
this.extraCss = extraCss;
@@ -46,6 +50,7 @@ public static class Builder {
4650
private boolean inlineStyleTags = true;
4751
private boolean keepStyleTags = false;
4852
private boolean keepLinkTags = false;
53+
private boolean keepAtRules = false;
4954
private boolean loadRemoteStylesheets = true;
5055
private String baseUrl = null;
5156
private String extraCss = null;
@@ -94,6 +99,17 @@ public Builder setKeepLinkTags(boolean b) {
9499
return this;
95100
}
96101

102+
/**
103+
* Keep "at-rules" after inlining.
104+
*
105+
* @param b true to preserve at-rules, false to remove them
106+
* @return this builder instance for method chaining
107+
*/
108+
public Builder setKeepAtRules(boolean b) {
109+
this.keepAtRules = b;
110+
return this;
111+
}
112+
97113
/**
98114
* Whether remote stylesheets should be loaded or not.
99115
*
@@ -168,7 +184,7 @@ public Builder setPreallocateNodeCapacity(int cap) {
168184
* @return a new immutable configuration instance
169185
*/
170186
public CssInlineConfig build() {
171-
return new CssInlineConfig(inlineStyleTags, keepStyleTags, keepLinkTags, loadRemoteStylesheets, baseUrl,
187+
return new CssInlineConfig(inlineStyleTags, keepStyleTags, keepLinkTags, keepAtRules, loadRemoteStylesheets, baseUrl,
172188
extraCss, cacheSize, preallocateNodeCapacity);
173189
}
174190
}

bindings/java/src/main/rust/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ fn build_inliner(
7474
let inline_style_tags = env.get_bool_field(&cfg, "inlineStyleTags")?;
7575
let keep_style_tags = env.get_bool_field(&cfg, "keepStyleTags")?;
7676
let keep_link_tags = env.get_bool_field(&cfg, "keepLinkTags")?;
77+
let keep_at_rules = env.get_bool_field(&cfg, "keepAtRules")?;
7778
let load_remote_stylesheets = env.get_bool_field(&cfg, "loadRemoteStylesheets")?;
7879
let cache_size = env.get_int_field(&cfg, "cacheSize")?;
7980
let preallocate_node_capacity = env.get_int_field(&cfg, "preallocateNodeCapacity")?;
@@ -84,6 +85,7 @@ fn build_inliner(
8485
.inline_style_tags(inline_style_tags)
8586
.keep_style_tags(keep_style_tags)
8687
.keep_link_tags(keep_link_tags)
88+
.keep_at_rules(keep_at_rules)
8789
.load_remote_stylesheets(load_remote_stylesheets)
8890
.extra_css(extra_css.map(Cow::Owned))
8991
.preallocate_node_capacity(preallocate_node_capacity as usize);

bindings/javascript/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
### Added
6+
7+
- `keep_at_rules` option [#265](https://github.com/Stranger6667/css-inline/issues/265)
8+
59
## [0.16.0] - 2025-07-16
610

711
### Changed

0 commit comments

Comments
 (0)