Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/add-no-inline-styles-rule.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@biomejs/biome": patch
---

Added the nursery rule [`noInlineStyles`](https://biomejs.dev/linter/rules/no-inline-styles/). The rule disallows the use of inline `style` attributes in HTML and the `style` prop in JSX, including `React.createElement` calls. Inline styles make code harder to maintain and can interfere with Content Security Policy.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added the rule for HTML and JSX only so far since those are the languages we would like to have covered at Dune. Should this already be extended to Vue, Svelte and Astro? Or in a follow up?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am also not sure if this PR is properly adding the rule in the correct way across languages like HTML and JSX.

Copy link
Contributor

@dyc3 dyc3 Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this already be extended to Vue, Svelte and Astro?

It should just work because you've already implemented it for HTML, but you can add snapshot tests for those to verify.

I am also not sure if this PR is properly adding the rule in the correct way across languages like HTML and JSX.

As long as the rule has the same name in js_analyze and html_analyze, you should be good.

28 changes: 28 additions & 0 deletions crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions crates/biome_configuration/src/analyzer/linter/rules.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions crates/biome_diagnostics_categories/src/categories.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

89 changes: 89 additions & 0 deletions crates/biome_html_analyze/src/lint/nursery/no_inline_styles.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use biome_analyze::{
Ast, Rule, RuleDiagnostic, RuleSource, context::RuleContext, declare_lint_rule,
};
use biome_console::markup;
use biome_html_syntax::HtmlAttribute;
use biome_rowan::AstNode;
use biome_rule_options::no_inline_styles::NoInlineStylesOptions;

declare_lint_rule! {
/// Disallow the use of inline styles on elements.
///
/// Inline styles are specified using the `style` attribute directly on an element.
/// They make code harder to maintain and override, prevent reusability of styling, and
/// can be a security concern when implementing a strict Content Security Policy (CSP).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the bottom of the docs, or here, we should have a link to CSP. Possibly from MDN website

///
/// Instead of inline styles, use CSS classes defined in external stylesheets or
/// `<style>` blocks. This promotes separation of concerns and makes styles easier
/// to manage, reuse, and override.
///
/// ## Examples
///
/// ### Invalid
///
/// ```html,expect_diagnostic
/// <div style="color: red;"></div>
/// ```
///
/// ```html,expect_diagnostic
/// <button style="background: blue; color: white;">Click</button>
/// ```
///
/// ### Valid
///
/// ```html
/// <div class="text-red"></div>
/// ```
///
/// ```html
/// <button class="btn btn-primary">Click</button>
/// ```
///
pub NoInlineStyles {
version: "next",
name: "noInlineStyles",
language: "html",
sources: &[
RuleSource::HtmlEslint("no-inline-styles").same(),
],
recommended: false,
}
}

impl Rule for NoInlineStyles {
type Query = Ast<HtmlAttribute>;
type State = ();
type Signals = Option<Self::State>;
type Options = NoInlineStylesOptions;

fn run(ctx: &RuleContext<Self>) -> Self::Signals {
let attribute = ctx.query();
let name = attribute.name().ok()?;
let name_token = name.value_token().ok()?;

if name_token.text_trimmed() == "style" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Attributes are case insensitive. Use to_lowercase_cow and add some tests for different variants of style e.g. STYLE

return Some(());
}

None
}

fn diagnostic(ctx: &RuleContext<Self>, _state: &Self::State) -> Option<RuleDiagnostic> {
let attribute = ctx.query();
Some(
RuleDiagnostic::new(
rule_category!(),
attribute.range(),
markup! {
"Avoid using the "<Emphasis>"style"</Emphasis>" attribute."
},
)
.note(markup! {
"Inline styles make code harder to maintain, override, and can interfere with Content Security Policy."
})
.note(markup! {
"Use a CSS class instead."
}),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Invalid cases - should trigger the rule -->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be our magic comment <!-- should generate diagnostics -->


<div style="color: red;"></div>

<button style="background: blue; color: white;">Click</button>

<p style="margin: 0; padding: 10px;">Paragraph</p>

<span style="">Empty inline style</span>

<img style="width: 100px;" src="image.png" alt="Image">

<a style="text-decoration: none;" href="#">Link</a>
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
---
source: crates/biome_html_analyze/tests/spec_tests.rs
expression: invalid.html
---
# Input
```html
<!-- Invalid cases - should trigger the rule -->

<div style="color: red;"></div>

<button style="background: blue; color: white;">Click</button>

<p style="margin: 0; padding: 10px;">Paragraph</p>

<span style="">Empty inline style</span>

<img style="width: 100px;" src="image.png" alt="Image">

<a style="text-decoration: none;" href="#">Link</a>

```

# Diagnostics
```
invalid.html:3:6 lint/nursery/noInlineStyles ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

i Avoid using the style attribute.

1 │ <!-- Invalid cases - should trigger the rule -->
2 │
> 3 │ <div style="color: red;"></div>
│ ^^^^^^^^^^^^^^^^^^^
4 │
5 │ <button style="background: blue; color: white;">Click</button>

i Inline styles make code harder to maintain, override, and can interfere with Content Security Policy.

i Use a CSS class instead.

i This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.


```

```
invalid.html:5:9 lint/nursery/noInlineStyles ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

i Avoid using the style attribute.

3 │ <div style="color: red;"></div>
4 │
> 5 │ <button style="background: blue; color: white;">Click</button>
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6 │
7 │ <p style="margin: 0; padding: 10px;">Paragraph</p>

i Inline styles make code harder to maintain, override, and can interfere with Content Security Policy.

i Use a CSS class instead.

i This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.


```

```
invalid.html:7:4 lint/nursery/noInlineStyles ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

i Avoid using the style attribute.

5 │ <button style="background: blue; color: white;">Click</button>
6 │
> 7 │ <p style="margin: 0; padding: 10px;">Paragraph</p>
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
8 │
9 │ <span style="">Empty inline style</span>

i Inline styles make code harder to maintain, override, and can interfere with Content Security Policy.

i Use a CSS class instead.

i This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.


```

```
invalid.html:9:7 lint/nursery/noInlineStyles ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

i Avoid using the style attribute.

7 │ <p style="margin: 0; padding: 10px;">Paragraph</p>
8 │
> 9 │ <span style="">Empty inline style</span>
│ ^^^^^^^^
10 │
11 │ <img style="width: 100px;" src="image.png" alt="Image">

i Inline styles make code harder to maintain, override, and can interfere with Content Security Policy.

i Use a CSS class instead.

i This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.


```

```
invalid.html:11:6 lint/nursery/noInlineStyles ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

i Avoid using the style attribute.

9 │ <span style="">Empty inline style</span>
10 │
> 11 │ <img style="width: 100px;" src="image.png" alt="Image">
│ ^^^^^^^^^^^^^^^^^^^^^
12 │
13 │ <a style="text-decoration: none;" href="#">Link</a>

i Inline styles make code harder to maintain, override, and can interfere with Content Security Policy.

i Use a CSS class instead.

i This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.


```

```
invalid.html:13:4 lint/nursery/noInlineStyles ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

i Avoid using the style attribute.

11 │ <img style="width: 100px;" src="image.png" alt="Image">
12 │
> 13 │ <a style="text-decoration: none;" href="#">Link</a>
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14 │

i Inline styles make code harder to maintain, override, and can interfere with Content Security Policy.

i Use a CSS class instead.

i This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.


```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Valid cases - should NOT trigger the rule -->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be our magic comment <!-- should not generate diagnostics -->


<div class="text-red"></div>

<button class="btn btn-primary">Click</button>

<p class="no-margin padded">Paragraph</p>

<div id="container"></div>

<span data-style="not-inline">Not inline style</span>
Loading