Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for inline styles #6833

Open
yoyo837 opened this issue May 6, 2023 · 24 comments
Open

Add support for inline styles #6833

yoyo837 opened this issue May 6, 2023 · 24 comments
Labels
status: needs discussion triage needs further discussion

Comments

@yoyo837
Copy link
Contributor

yoyo837 commented May 6, 2023

What steps are needed to reproduce the bug?

<p style="--swipe-height: 100%">
  <span class="adswipe-item">
    123
  </span>
</p>

What Stylelint configuration is needed to reproduce the bug?

module.exports = {
  extends: [
    'stylelint-config-standard',
    'stylelint-config-html/vue',
    'stylelint-config-html',
    'stylelint-config-recommended-vue/scss',
    'stylelint-config-recommended-vue',
    'stylelint-prettier/recommended',
  ],
  overrides: [
    {
      files: ['**/*.vue'],
      customSyntax: 'postcss-html',
    },
    {
      files: ['**/*.scss'],
      customSyntax: 'postcss-scss',
    },
    {
      files: ['**/*.less'],
      customSyntax: 'postcss-less',
    },
  ],
  plugins: ['stylelint-scss'],
  rules: {
    'length-zero-no-unit': null,
    'function-no-unknown': [
      true,
      {
        ignoreFunctions: ['constant'],
      },
    ],
    'selector-pseudo-class-no-unknown': [
      true,
      {
        ignorePseudoClasses: ['first'],
      },
    ],
    'no-descending-specificity': null,
    'font-family-no-missing-generic-family-keyword': null,
    'no-invalid-position-at-import-rule': [
      true,
      {
        ignoreAtRules: ['use', 'tailwind'],
      },
    ],
    'selector-class-pattern': null,
    'at-rule-no-unknown': [
      true,
      {
        ignoreAtRules: [
          'mixin',
          'include',
          'extend',
          'each',
          'use',
          'tailwind',
          'layer',
          'apply',
          'if',
          'else',
          'at-root',
        ],
      },
    ],
    'scss/at-rule-no-unknown': [
      true,
      {
        ignoreAtRules: [
          'mixin',
          'include',
          'extend',
          'each',
          'use',
          'tailwind',
          'layer',
          'apply',
          'if',
          'else',
          'at-root',
        ],
      },
    ],
    'selector-pseudo-element-no-unknown': [
      true,
      {
        ignorePseudoElements: ['v-deep'],
      },
    ],
    'color-function-notation': null,
    'import-notation': null,
  },
};

How did you run Stylelint?

Cli with stylelint '**/*.vue'

Which version of Stylelint or dependencies are you using?

15.6.1

What did you expect to happen?

No problems to be reported

What actually happened?

custom-property-empty-line-before is reported.

Does the bug relate to non-standard syntax?

No response

Proposal to fix the bug

No response

@yoyo837
Copy link
Contributor Author

yoyo837 commented May 6, 2023

I'd be more than happy to try to fix it, and it would be even better if someone could give me some guidance. Thanks.

@ybiquitous
Copy link
Member

@yoyo837 Thanks for the report with the reproducible example. I can confirm the error with this minimal reproducible demo:

<a style="--swipe-height: 100%">
</a>
{
  "customSyntax": "postcss-html",
  "rules": {
    "custom-property-empty-line-before": "always"
  }
}

Since this error seems specific for only HTML syntax, I think we can avoid the error by just turning the rule off like this:

{
  "overrides": [
    {
      "files": ["**/*.html"],
      "rules": {
        "custom-property-empty-line-before": null
      }
    }
  ]
}

What do you think?

@ybiquitous ybiquitous added the status: needs discussion triage needs further discussion label May 7, 2023
@yoyo837
Copy link
Contributor Author

yoyo837 commented May 7, 2023

In vue files, the above solution will also cause custom-property-empty-line-before not working to lint the css in <style>.

@ybiquitous
Copy link
Member

@yoyo837 Does this case in a demo match a .vue case that you mentioned?

@yoyo837
Copy link
Contributor Author

yoyo837 commented May 8, 2023

See my case in a demo.

@ybiquitous
Copy link
Member

@yoyo837 Handling the two cases you provided seems difficult to me. 🤔

<template>
  <!-- Should not report custom-property-empty-line-before -->
  <p style="--swipe-height: 100%;">123</p>
</template>

<style lang="scss">
  a {
    // Should report `custom-property-empty-line-before` if there is no empty line
    --swipe-height: 100%;
  }
</style>

<p style="--swipe-height: 100%;">123</p>

Because this is an HTML syntax, not standard CSS. Styelint built-in rules handle only standard CSS.

<style lang="scss">a {...}</style>

This code should NOT be reported because of except: ['first-nested'] of stylelint-config-standard:

{
	'custom-property-empty-line-before': [
		'always',
		{
			except: ['after-custom-property', 'first-nested'],
			ignore: ['after-comment', 'inside-single-line-block'],
		},
	],
}

https://github.com/stylelint/stylelint-config-standard/blob/5aa5dd57ac814edd98c656da35f2ccc99a64d433/index.js#L36-L42

What do you think?

@yoyo837
Copy link
Contributor Author

yoyo837 commented May 8, 2023

Currently we can turn off the custom-property-empty-line-before rule for vue files to skip this problem.

Its difficult point is that it is an inline style of html, so this is considered a bug for stylelint you think? Or some point that can be improved, can stylelint do something for it?

In fact, there should be similar situations in other rules.

@ybiquitous
Copy link
Member

I think this is not a bug of Stylelint because postcss-html produces the following AST:

$ node -e 'require("postcss-html").parse(`<p style="--height: 100%;">123</p>`).walkDecls(console.log)'
<ref *1> Declaration {
  raws: { before: '', between: ': ' },
  type: 'decl',
  parent: Root {
    raws: {
      semicolon: true,
      after: '',
      codeBefore: '<p style="',
      codeAfter: '">123</p>'
    },
    type: 'root',
    nodes: [ [Circular *1] ],
    source: {
      input: [Input],
      start: [Object],
      inline: true,
      lang: 'css',
      syntax: [Object]
    },
    lastEach: 2,
    indexes: { '2': 0 },
    document: Document {
      raws: {},
      type: 'document',
      nodes: [Array],
      source: [Object],
      [Symbol(isClean)]: false,
      [Symbol(my)]: true
    },
    [Symbol(isClean)]: false,
    [Symbol(my)]: true
  },
  source: {
    start: { offset: 10, line: 1, column: 11 },
    input: Input {
      css: '--height: 100%;',
      hasBOM: false,
      id: '<input css NMF65H>',
      [Symbol(fromOffsetCache)]: [Array]
    },
    end: { offset: 24, line: 1, column: 25 }
  },
  prop: '--height',
  value: '100%',
  [Symbol(isClean)]: false,
  [Symbol(my)]: true
} 0

Please note that the Declaration node's raws.before property has a value '':

raws: { before: '', between: ': ' }

In this case, the custom-property-empty-line-before rule doesn't consider having an empty line before this decl node:

const hasEmptyLineBefore = hasEmptyLine(decl.raws.before);

I think a package using postcss-html should deal with this case, instead of the Stylelint built-in rule.

@yoyo837
Copy link
Contributor Author

yoyo837 commented May 8, 2023

However, postcss-html does not care about lint, actually, there is no blank line at before of the html style code. It seems a bit far-fetched to let the AST of postcss-html contain a blank line.

What if stylelint can judge that the style is from postcss-html parsed and ignore this rule to cover this issue? Just an idea.

@yoyo837
Copy link
Contributor Author

yoyo837 commented May 8, 2023

I mean, custom-property-empty-line-before should be a meaningless rule for inline styles.

<!-- 
postcss-html gives us a blank line? 
If non-first css properties in inline styles, it How? 
 -->
<p style="color:red;--swipe-height: 100%;">123</p>

@ybiquitous
Copy link
Member

We can see minimal reproduction with this demo using postcss-html.

Also, I find a workaround to fix this problem:

--- a/lib/rules/custom-property-empty-line-before/index.js
+++ b/lib/rules/custom-property-empty-line-before/index.js
@@ -78,7 +78,7 @@ const rule = (primary, secondaryOptions, context) => {
 			if (
 				optionsMatches(secondaryOptions, 'ignore', 'inside-single-line-block') &&
 				parent != null &&
-				(isAtRule(parent) || isRule(parent)) &&
+				(isAtRule(parent) || isRule(parent) || 'document' in parent) &&
 				isSingleLineString(blockString(parent))
 			) {
 				return;

This workaround uses the document property of the Root node assigned by postcss-html. It may be easy to break due to dependency on the postcss-html's internal, though... 🤔

https://github.com/ota-meshi/postcss-html/blob/b2ccc99070377f881b30b44c80032d45e5512258/lib/html/parse-styles.js#L166

@yoyo837
Copy link
Contributor Author

yoyo837 commented May 8, 2023

Thanks for the code analysis, I think before talking about how to fix it with code, we should first confirm that where it should be fixed, in stylelint or not. What do you think?

@ybiquitous
Copy link
Member

Ideally, I don't think Stylelint's built-in rules should fix the problem due to an HTML inline style syntax, because the built-in rules focus on standard CSS syntax.

A workaround like #6833 (comment) may be possible, but there is no assurance with other syntaxes.

@yoyo837
Copy link
Contributor Author

yoyo837 commented May 8, 2023

Ok, Thanks for your reply.

Ref: #5999

@yoyo837
Copy link
Contributor Author

yoyo837 commented May 8, 2023

Maybe we can do something like #4726, Is this the same as what you said?

@ybiquitous
Copy link
Member

Maybe we can do something like #4726, Is this the same as what you said?

No. isFirstNodeOfRoot() cannot fix this problem. The util can fix:

<a style="--foo: 1em;"></a>

but it cannot fix:

<a style="--foo: 1em; --bar: 1em;"></a>

The workaround that I mentioned on #6833 (comment) ignores decl nodes in style="--foo: 1em;" with ignore: ['inside-single-line-block'] secondary option. But the workaround implicitly assumes postcss-html AST, so it may be fragile.

@ybiquitous
Copy link
Member

FYI. #5999 was closed because we planned to remove custom-property-empty-line-before as a stylistic rule (ref: migration guide to 15.0.0). But we found out Prettier didn't address the rule, so we stopped removing it.

@yoyo837
Copy link
Contributor Author

yoyo837 commented May 8, 2023

Ok, so should this issue be marked as won't fix? Because there is no better solution. 🤔️

@ybiquitous
Copy link
Member

Let's keep it open to prevent someone from reporting similar issues for a while. Also, possibly, a solution may be found.

@unikitty37
Copy link

Is there a way to disable the rule for that specific line in Svelte and Vue components? Using the quick fix in VSCode doesn't produce anything very useful:

<p style="--swipe-height: 100%">

just becomes

<p style="--swipe-height: 100%">  /* stylelint-disable-line custom-property-empty-line-before */

which not only wrecks the output, but doesn't actually disable the rule. Similarly, choosing "disable for the entire file" just puts that comment on line 1 — so it will still be treated as HTML and rendered!

While the example above could be moved to a style definition, my code contains

<header style="--icon-size: {size}">

which has to be there because Svelte variables can be interpolated into HTML but not CSS.

I don't want to disable the rule globally, but I do need to mark this line as being OK, as passing a Stylelint check is required to pass CI.

That said, setting CSS variables reactively is the only time inline styles are used in this project, so I'd be happy with a way of telling Stylelint to completely ignore inline styles. Is that possible?

@yoyo837
Copy link
Contributor Author

yoyo837 commented May 9, 2023

@unikitty37 Yeah, What you want is also what I want, we can not do that by any code in this moment.

@unikitty37
Copy link

@yoyo837 I've managed to work around it for Svelte.

Instead of style="--icon-size: {size}", using style:--icon-size={size} looks sufficiently non-CSS-like for Stylelint to ignore it.

Looking at the Vue docs, maybe this would work? (I assume you're on Vue 3…)

<script>
  const styleObject = reactive({ "--swipe-height": "100%" })
</script>

<p :style="styleObject"></p>

@yoyo837
Copy link
Contributor Author

yoyo837 commented May 10, 2023

@unikitty37 Yes, this is a works solution, but it restricts me from writing style freely.

@jeddy3 jeddy3 changed the title custom-property-empty-line-before reports incorrectly when inline styles Add support for inline styles Jun 24, 2023
@Mouvedia
Copy link
Contributor

related: ota-meshi/postcss-html#38

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: needs discussion triage needs further discussion
Development

No branches or pull requests

4 participants