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

Rules #1

Closed
MoOx opened this issue Dec 4, 2014 · 56 comments
Closed

Rules #1

MoOx opened this issue Dec 4, 2014 · 56 comments
Assignees
Labels
type: enhancement a new feature that isn't related to rules

Comments

@MoOx
Copy link
Contributor

MoOx commented Dec 4, 2014

Vocabulary reference http://apps.workflower.fi/vocabs/css/en

Updated list


Rules

General (tied to the stylesheet itself)

  • no-missing-eof-newline: Disallow missing end-of-file newline
  • no-multiple-empty-lines: Disallow multiple empty white lines
  • no-eol-no-whitespace: Disallow whitespace at the end of line
  • indentation: Specify whitespace for indentation
  • max-line-length: Specify maximum line length

String (ref)

  • string-quotes: "double"|"single" Specify quotes around strings
    • "double" - strings must be doubled quoted
    • "single" - strings must be single quoted

Number (ref)

  • number-leading-zero: "always"|"never" Require or disallow a leading zero before number
    • "always" - a number must have a leading zero
    • "never" - a number must not have a leading zero
  • number-no-trailing-zeros: Disallow trailing zeros (e.g. 5.0px)
  • number-zero-length-no-unit: Disallow units for zero length values
  • number-max-precision: int Maximum number of digits after the "." in a number

Function (ref)

  • function-comma-space-after: "always"|"never"
  • function-comma-space-before: "always"|"never"
  • function-parentheses-inside-space: "always"|"never" Specify space inside parentheses (after opening, before closing)
  • function-space-after: "always"|"never" Specify space between the closing parenthesis of a function and the next value
  • function-token-no-space: Disallow space between the function's name and its opening parenthesis
  • function-calc-no-unspaced-operator: Disallow operators without space on both sides in calc()
  • function-url-quotes: "double", "single", "none" Specify quotes around URLs (URLs, unlike strings, can be unquoted)
    • "double" - URLs must be doubled quoted
    • "single" - URLs must be single quoted
    • "none" - URLs must be unquoted

Color (ref)

  • color-hex-length: "short"|"long"
  • color-hex-case: "lower"|"upper"
  • color-no-invalid-hex: Disallow invalid hex
  • color-no-named: Disallow named colors
  • color-no-hex: Disallow hex colors
  • color-function-blacklist: array Disallowed color functions e.g. ["rgba", "hsl", "hwb"]
  • color-function-whitelist: array Only allowed color functions e.g. ["gray", "color", "device-cmyk"]

:root

  • root-no-standard-properties: Disallow use of standard properties in :root

Rule

  • rule-no-single-line: Disallow single line rule-sets
  • rule-properties-order: 1 dimension array, string (preset) - "alphabetical" Order of properties within a rule
  • rule-trailing-semicolon: "always"|"never" Trailing semicolon at the end of a rule
    • "always" - there must be a trailing semicolon
    • "never" - there must not be a trailing semicolon
  • rule-no-duplicate-properties: Disallow duplicate properties within a rule
  • rule-nested-empty-line-before: Require or disallow an empty line before nested rules
  • rule-single-line-max-length: int The maximum length of a single line rule
  • rule-single-line-max-declarations: int The maximum number of declaration within a single line rule

At-rule rules

  • at-rule-no-vendor-prefix: Disallow vendor prefixes in @rules
  • at-rule-empty-line-before: Require or disallow an empty line before at-rules

Custom media (ref)

  • custom-media-pattern: string Preset or regex pattern to use to check names are matching a given pattern

Media query

  • media-query-parenthesis-inside-space: "always"|"never"
  • media-query-list-comma-space-after: "always"|"never"
  • media-query-list-comma-space-before: "always"|"never"
  • media-query-list-comma-newline-after: "always"|"never"
  • media-query-list-comma-newline-before: "always"|"never"

Media feature

  • media-feature-colon-space-after: "always"|"never"
  • media-feature-colon-space-before: "always"|"never"
  • media-feature-range-operator-space-after: "always"|"never"
  • media-feature-range-operator-space-before: "always"|"never"

Media feature

  • media-feature-colon-space-after: "always"|"never"
  • media-feature-colon-space-before: "always"|"never"
  • media-feature-range-operator-space-after: "always"|"never"
  • media-feature-range-operator-space-before: "always"|"never"

Media feature name

  • media-feature-name-no-vendor-prefix: "always"|"never" Disallow vendor prefixes for media feature names

Selector

  • selector-combinator-space-before: string Specify what space should be used before and after combinator
  • selector-combinator-space-after: string Specify what space should be used before and after combinator
  • selector-delimiter-space-after: "always"|"never"
  • selector-delimiter-space-before: "always"|"never"
  • selector-delimiter-newline-after: "always"|"never"
  • selector-delimiter-newline-before: "always"|"never"
  • selector-no-vendor-prefix: Disallow vendor prefixes in selectors
  • selector-pseudo-element-notation: Specify that pseudo element selectors need one or two
  • selector-root-no-composition: Disallow use of :root selector with others (in list, complex, or compound selectors)
  • selector-no-id: Disallow use of id in selectors e.g: #id
  • selector-no-type: Disallow use of type in selectors e.g: div
  • selector-no-universal: Disallow use of universal selector in selectors e.g: *
  • selector-no-attribute: Disallow use of attributes in selectors e.g: [attr]
  • selector-no-combinator: Disallow use of combinator in selectors
  • selector-no-qualified: Disallow use of qualified selectors e.g: div#id
  • selector-no-pseudo-class: Disallow use of pseudo-class in selectors
  • selector-no-pseudo-element: Disallow use of pseudo-element in selectors
  • selector-no-delimiter: Disallow use of delimiter in selectors
  • selector-pattern: string regex (eg: /\.([a-z]+-)?[A-Z][a-z]+(-[a-z][a-zA-Z]+)(--[a-z][a-zA-Z]+)/ to match .org-Block-eleMent--modifier

Custom selector (ref)

  • custom-selector-pattern: string Regex pattern to use to check names are matching a given pattern

Block

  • block-no-empty: Disallow empty blocks
  • block-opening-brace-space-after: "always"|"never"|"always-single-line"|"never-single-line"|"always-multi-line"|"never-multi-line"
  • block-opening-brace-space-before: "always"|"never"|"always-single-line"|"never-single-line"|"always-multi-line"|"never-multi-line"
  • block-opening-brace-newline-after: "always"|"never"|"always-multi-line"|"never-multi-line"
  • block-opening-brace-newline-before: "always"|"never"|"always-single-line"|"never-single-line"|"always-multi-line"|"never-multi-line"
  • block-closing-brace-space-after: "always"|"never"|"always-single-line"
  • block-closing-brace-space-before: "always"|"never"|"always-single-line"|"never-single-line"|"always-multi-line"|"never-multi-line"
  • block-closing-brace-newline-after: "always"|"never"|"always-single-line"|"never-single-line"|"always-multi-line"|"never-multi-line"
  • block-closing-brace-newline-before: "always"|"never"|"always-multi-line"|"never-multi-line"

Nesting Block

  • nesting-block-opening-brace-space-before: "always"|"never"|"always-single-line"|"never-single-line"|"always-multi-line"|"never-multi-line"
  • nesting-block-opening-brace-newline-before: "always"|"never"|"always-single-line"|"never-single-line"|"always-multi-line"|"never-multi-line"

Declaration rules

  • declaration-no-important: Disallow the use of !important
  • declaration-bang-space-after: "always"|"never"
  • declaration-bang-space-before: "always"|"never"
  • declaration-colon-space-after: "always"|"never"
  • declaration-colon-space-before: "always"|"never"
  • declaration-semicolon-space-after: "always"|"never"|"always-single-line"|"never-single-line"
  • declaration-semicolon-space-before: "always"|"never"|"always-single-line"|"never-single-line"
  • declaration-semicolon-newline-after: "always"|"never"|"always-multi-line"|"never-multi-line"
  • declaration-semicolon-newline-before: "always"|"never"|"always-multi-line"|"never-multi-line"

Unit

  • unit-blacklist: array Disallowed units e.g. ["px", "pt", "cm"]
  • unit-whitelist: array Only allowed units e.g. ["%", "rem", "em"]

Property

  • property-no-vendor-prefix: Disallow vendor prefixes in properties
  • property-blacklist: array Disallowed properties e.g. ["tranform", "background-size"]
  • property-whitelist: array Only allowed properties e.g. ["height", "width", "font-size"]
  • property-unit-blacklist: object Disallowed units for specific properties e.g. {"width": ["%"], "height": ["%"]}
  • property-unit-whitelist: object Only allowed units for specific properties e.g. {"font-size": ["em", "rem"]}

Custom property

  • custom-property-no-outside-root: Disallow custom properties outside of :root
  • custom-property-pattern: string Regex pattern to use to check names are matching a given pattern

Value

  • value-no-vendor-prefix: Disallow vendor prefixes in values

Value list

  • value-list-comma-space-after: "always"|"never"|"always-single-line"|"never-single-line"
  • value-list-comma-space-before: "always"|"never"|"always-single-line"|"never-single-line"
  • value-list-comma-newline-after: "always"|"never"|"always-multi-line"|"never-multi-line"
  • value-list-comma-newline-before: "always"|"never"|"always-multi-line"|"never-multi-line"

Comment

  • comment-space-inside: string - `"always"|"never"
  • comment-empty-line-before: Require or disallow an empty line before comments

To think about Mistakes

  • Float or absolutely positioned elements don't need display: block
  • Absolutely positioned elements will ignore float
@MoOx
Copy link
Contributor Author

MoOx commented Dec 9, 2014

Some rules idea (most of them are based on @necolas postcss-bem-linter)

  • check component naming convention rules (based on a given pattern? eg: {Block}-{element}--{modifier}}) (selector & keyframes name) (bem-pattern - regexp)
  • check custom properties place (root-only-custom-properties - boolean)
  • check root doesn't contains rules (root-no-standard-properties - boolean)
  • check BEM indentation (bem-indentation - int (number of spaces))
.B {}
  .B-e {}
    .B-e--m {} /* ? */
  • check properties order (from a given list?) (properties-order - Array)
  • forbidden properties (properties-forbidden - Array)
  • authorized properties (properties-authorized - Array)
  • forbidden units (units-forbidden - Array (["px"]) | Object ({px: {fontSize}))
  • authorized units (units-authorized - same format at forbidden)

@MoOx
Copy link
Contributor Author

MoOx commented Dec 9, 2014

Obviously we might take some stuff from csslint https://github.com/CSSLint/csslint/wiki/Rules

@lexich
Copy link

lexich commented Dec 9, 2014

My main headache is a lot of nesting rules when peoples use preprocessors.

.welcome .to .css > .hell ul > li .etc{}

Css linter with source maps support with this checker will be very usefull for me.

@davidtheclark
Copy link
Contributor

I'd take a look at everything available in SCSS-Lint: https://github.com/causes/scss-lint/blob/master/lib/scss_lint/linter/README.md

Lots of good stuff there.

@yisibl
Copy link

yisibl commented Dec 10, 2014

Use ColorKeyword is better!

Good color keyword

color: green;
Bad: hexadecimal color

color: #0f0;

@necolas
Copy link

necolas commented Dec 10, 2014

why reimplement another existing module (bem linter)?

@MoOx
Copy link
Contributor Author

MoOx commented Dec 11, 2014

@necolas the idea behind this is to make an eslint like for css (csslint seems dead & not likely to have a second life..). I want something really configurable that might also have the role of a code style checker (like jscs).
We can consider relying on postcss-bem-linter for the rules it can handle (if we can make things configurable).

@MoOx
Copy link
Contributor Author

MoOx commented Dec 11, 2014

FYI, I've added a (pretty long) list in the description of the issue.

@MoOx MoOx changed the title API & rules rules Dec 11, 2014
@davidtheclark
Copy link
Contributor

Great list!

A couple of others (drawn from SCSS-Lint) that I'd be interested in using (trying to match your naming pattern):

  • rule-duplicate-property-allowed - bool
  • rule-vendor-prefix-allowed - bool
  • selector-qualified-type-allowed - bool (or maybe even just selector-type-allowed)

And for a couple of the rules you mentioned (and vendor-prefixes) I think we'd probably want to be able to have a preset option (e.g. preset property order list, preset selector-matching list).

@MoOx
Copy link
Contributor Author

MoOx commented Dec 11, 2014

I've added those 3 rules as

  • properties-allow-duplicates - bool
  • property-allow-vendor-prefix - bool (allow/disallow vendor prefix if non prefixed property)
  • selector-allow-qualified - bool

Good idea for some presets.

@necolas
Copy link

necolas commented Dec 12, 2014

there's already csscomb too. maybe contribute to that first

@MoOx
Copy link
Contributor Author

MoOx commented Dec 12, 2014

I have already checked csscomb, but I don't really like the parser (gonzales "pe").
There is no api to easily read or manipulate the AST.
From what I've see, gonzales pe AST seems to go deeper that stuff like Rework or PostCSS by parsing values, unit, hexa etc, but I still think PostCSS can be an easier choice to make plugins easy to do.

@necolas
Copy link

necolas commented Dec 20, 2014

plus csscomb also looks like it isn't being developed anymore. for example, i opened this issue 6 months ago - csscomb/csscomb.js#245 - and had to write a module to provide reporting output. i assume they don't have time to maintain it, so probably worth writing a new tool that is easier for the community to patch / extend.

some feedback:

newline-between-rules - bool

blankline? newline is not the same. or you could it could also accept an integer where the default is 2.

rule-one-line-property-space-before - bool
rule-one-line-property-space-after - bool
selector-indentation-pseudo-classes - bool, int (number of spaces)
selector-indentation-pseudo-element - bool, int (number of spaces)
value-allow-duplicate - bool
value-body-background - bool (ref)

what are these exactly?

value-unitless-zero - bool

presumably only for 0 values that can be unitless in css?

bem-indentation - int (number of spaces)

what is this?

including support for 'excludes' (and maybe 'severity') would also be useful, like: https://github.com/causes/scss-lint

@MoOx
Copy link
Contributor Author

MoOx commented Dec 27, 2014

Indeed blankline is not newline. So I changed newline-between-rules to int.

  • rule-one-line-property-space-before & rule-one-line-property-space-after
.oneline {(space)prop: value(space)}
  • eg for selector-indentation-pseudo-classes & element
.class {}
  .class:hover {} /* indented */
  .class::before {} /* indented */
  • value-allow-duplicate
.selector {
   background: red;
   background: linear-gradient(...)
}
  • value-body-background would just check there is a specified value for the background body (see why it's recommanded here http://nota-bene.org/Background-and-foreground-colours)
  • value-unitless-zero will be a check for 0 that must be unitless (no 0px, 0em etc.).
  • bem-indentation
.Block {}

  .Block--modifier {}

  .Block-element {}

    .Block-element--modifier {}

This should be exploded into several rules like indentation-selector-bem-element, indentation-selector-bem-modifier

Maybe all indentation related rules should start with indent-* (eg indentation-selector-pseudo-classes)?

  • For the interface & options I think I will take a lot from eslint (so exclude & severity will be here). http://eslint.org/docs/

@necolas
Copy link

necolas commented Dec 27, 2014

ok thanks

rule-one-line-property-space-before & rule-one-line-property-space-after

Could that not be handled by the options for space before property and space before closing brace? Probably don't need special options.

value-allow-duplicate

the example you gave is a duplicate property, not value

@MoOx
Copy link
Contributor Author

MoOx commented Dec 28, 2014

You are probably right for the rule with the name too long to type.
And indeed it should be property-allow-duplicate.
Will update list tomorrow.

@MoOx
Copy link
Contributor Author

MoOx commented Dec 29, 2014

List updated.

I made some change based on http://apps.workflower.fi/vocabs/css/en

Also

  • bem-pattern to selector-class-bem-pattern to get something more clear. Maybe this can be improved again.
  • newlines-between-rules -> statements-separation (btw should we prefix all rules by what they check? For example should we name this rule statements-separation or separation-between-statement? I'm doing the prefix thing for everything but maybe it should be done the other way?
  • all *-space-before have been renamed to *-before, with type changed from bool to int (number of spaces) or string. Maybe an int to number of space is a bad idea. What about rules with -before/after for newlines? Any comments?

For most of the rules, I've tried to separate them according to their scope (statements, rules, properties, values...) except for indentation. Not sure if it's a good thing. Any thoughts ?

@jeddy3
Copy link
Member

jeddy3 commented Dec 29, 2014

This all looks great, thanks!

Is it possible to make the allow interface a little more consistent? I find it a bit confusing at the moment as some rules default to true, others to false and some to null e.g.

  • value-trailing-semicolon - bool (default true)
  • value-allow-important - bool (default false)
  • rule-allow-empty - bool

Perhaps take a page out of the JSCS book and use the require and disallow approach i.e. all valid CSS passes the linter by default, but the developer can then enable the rules she/he needs to make it more restrictive. So, the three rules above would become:

  • value-require-trailing-semicolon - bool (default false)
  • value-disallow-important - bool (default false)
  • rule-disallow-empty - bool (default false)

Note how all three rules now have false for default.

And the other boolean rules would look something like:

  • value-disallow-leading-zero - bool (default false)
  • value-disallow-unitless-zero - bool (default false)
  • value-disallow-trailing-zero - bool (default false)
  • selector-require-one-per-line - bool (default false)
  • selector-disallow-id - bool (eg: #id) (default false)
  • selector-disallow-type - bool (eg: div) (default false)
  • selector-disallow-universal - bool (eg: *) (default false)
  • selector-disallow-attribute - bool (eg: [attr]) (default false)
  • selector-disallow-qualified - bool (eg: div#id) (default false)
  • selector-disallow-pseudo-class - bool (default false)
  • selector-disallow-pseudo-element - bool (default false)
  • selector-disallow-combinator - bool (default false)
  • selector-disallow-delimiter - bool (default false)
  • selector-disallow-descendant - bool (default false)
  • value-require-body-background - bool (default false)
  • value-require-color-keywords - bool (default false)
  • rule-disallow-one-line-property - bool (default false)
  • rule-require-one-line-property-space-before - bool (default false)
  • rule-require-one-line-property-space-after - bool (default false)
  • value-require-valid-hexa - bool (default false)
  • require-newline-eof - bool (default false)
  • custom-properties-require-in-root - bool (default false) ??? [scoped to “custom-properties?”]
  • root-disallow-standard-properties - bool (default false) ???

Note again how everything now defaults to false.

As the indentation rules can take a string as well as a bool, the require/disallow approach is not needed here. All the rules default to false though:

  • indentation-selector-class-bem-element - bool or int (default false)
  • indentation-selector-class-bem-modifier - bool or int (default false)
  • indentation-selector-pseudo-classes - bool or int (default false)
  • indentation-selector-pseudo-element - bool or int (default false)
  • indentation-rule - bool or int (default false)

How does that look? The linter is now permissive by default and becomes more restrictive as rules are enabled.

@MoOx
Copy link
Contributor Author

MoOx commented Dec 29, 2014

Yeah sounds good this way (I'm using jscs everywhere).

I was thinking about that this morning when I see trailing-semicolon rule :)

Everything disabled by default seems a good idea, but I'm sure some lazy people would like to get predefined configuration. How can we handle that?

I like something simpler like eslint with no-* (disallow) or * (require). More short & easier to spot. Don't you think? I you see some *-no-* you know it's a rule for disallowing something otherwise it's a requirement.

That could give us something like

  • selector-one-per-line - bool (default false)
  • value-trailing-semicolon - bool (default false)
  • value-no-important - bool (default false)
  • rule-no-empty - bool (default false)

@jeddy3
Copy link
Member

jeddy3 commented Dec 29, 2014

I like something simpler like eslint with no-* (disallow) or * (require). More short & easier to spot. Don't you think?

Looks good to me. require/disallow feels more explicit, but perhaps too verbose. I like how the ESLint rules documentation uses "require" and "disallow" consistently to describe each of the rules e.g.

  • "no-ternary - disallow the use of ternary operators (off by default)"
  • "func-names - require function expressions to have a name (off by default)"

So, perhaps using "no-* (disallow) or * (require)" for the rule names, and using "require" and "disallow" in the documentation is the best of both worlds e.g.

  • rule-no-empty - disallow empty rule-sets (off by default)
  • selector-one-per-line - require one selector per line (off by default)
  • value-trailing-semicolon - require a trailing semicolon after values, including within the last declaration (off by default)
  • value-no-important - disallow the use of !important (off by default)

Everything disabled by default seems a good idea, but I'm sure some lazy people would like to get predefined configuration. How can we handle that?

Perhaps in the same way as JSCS does by providing presets? Two for CSS that spring to mind are:

You could bundle a companion stylelint preset, but I think it should be separate and as comprehensively documented as the two guides above. The rules within Style Guides tend to be very subjective and distancing the linter from this subjectiveness might help with the clarity and longevity of the project. The linter documentation can then be narrowly focused on describing what the rule does without getting sucked into the murky world of rationalising why (dis)enabling a rule is subjectively better than not. Does that make sense, I can elaborate on it if I've done a terrible job explaining what I mean? :)

@MoOx
Copy link
Contributor Author

MoOx commented Dec 30, 2014

Indeed managing presets will represent an amount of work I don't want to handle in a near future. So we will skip that for now. Probably just some example + links to some others presets.

I just updated a part of the list to match the "no-* (disallow) or * (require)" & also deleted & renamed some rules to make it easier to read.

@jeddy3
Copy link
Member

jeddy3 commented Dec 30, 2014

The rules are starting to come together nicely! I really like how they are grouped by things like selector, rule and value.

all *-space-before have been renamed to *-before, with type changed from bool to int (number of spaces) or string. Maybe an int to number of space is a bad idea. What about rules with -before/after for newlines? Any comments?

I understand what you mean now. I agree that allowing an int as well as a string seems to be a bad idea as it feels unnecessarily confusing. A string offers all the flexibility needed and with a consistent interface. The user can then throw anything they want in there, be it:

  • "" = enforce no white space
  • " " = enforce one space
  • " " = enforce two spaces
  • "/n" = enforce newline
  • "/n/n" = enforce two newlines aka a blankline
  • "/t" = enforce tab
  • "/n/t" = enforce new line and tab
  • "/n " = enforce newline and two spaces

Is that what you were thinking?

I thought I’d try to mock-up a Style Guide preset to help us sanity-check the rules so far. I thought of mocking up the Idiomatic CSS styleguide first as it’s very well-known, but I went with the SUITCSS one instead as it is a stricter subset of the Idiomatic one. Doing the mock-up uncovered a few things that were not as clear as they could be with the rule names…

Again, I really like how the rules are grouped. Some of the terms on that vocabs site were new to me, so I had to look at syntax part of the spec to get my head around it. It uncovered a few things that might be mis-grouped or named:

  • value-trailing-semicolon -> declaration-block-trailing-semicolon
    As semicolons belong to declaration blocks, rather than values.
  • rule-no-duplicates-properties -> declaration-block-no-duplicate-properties
    As a rule-set/rule can be something like a media query, which can contain nested rule-sets/rules (hence more chance of duplicated properties). Whereas a declaration block can only contain declarations.
  • rule-properties-order -> declaration-block-properties-order
    As above, it’s more specific.
  • rule-one-line-property -> rule-no-single-line-declaration
    Addition of ‘no’ to match other “disallow” rules. Changed "one" to “single” as it's clearer?
  • rule-one-line-property-before -> rule-single-line-declaration-before
    As it’s a declaration, not a property in the rule-set/rule.
  • rule-one-line-property-after -> rule-single-line-declaration-after
    As above.

What do you think?

A few others queries:

  • I understand value-string-quotes and value-url-quotes, but what is value-quotes for?
  • Selectors can have quotes too, as in .selector-3[type="text"]. Can we add a rule for that? e.g selector-attribute-quotes: '"', "'", ""
  • It looks like you can have prefixes on all sorts of stuff; including properties (e.g. transform:), selectors (e.g. :fullscreen) and at-rules (e.g. keyframe). So, is it worth having something similar to property-no-vendor-prefix for other things? e.g. selector-no-vendor-prefix and at-rule-no-vendor-prefix
  • What about trailing white space? Can we add something like no-end-of-line-white-space to "General Rules"?
  • How do we handle declarations that have multiple values and the user wishes to put them some of them on separate lines? e.g.
.selector {
  background-image:
    linear-gradient(#fff, #ccc),
    linear-gradient(#f3c, #4ec);
  box-shadow: 1px 1px 1px #000, 2px 2px 1px 1px #ccc inset;
}

Perhaps by allowing an array of strings for the *-before/after rules? i.e.

declaration-coma-before: ""; // no space
declaration-coma-after: [" ", "/n"]; // space or new line
  • Has enforcing one declaration per line disappeared? Perhaps it can be handled with the *-before*/*-after approach, in the same way as selector-delimiter-before/after works? e.g. declaration-block-semicolon-before and declaration-block-semicolon-after
  • I think we're missing *-before/after rules for colons in declarations? e.g. declaration-colon-before and declaration-colon-after

So, with these things in mind, here's the mock-up of the SUITCSS preset:

newline-eof: false,
no-end-of-line-white-space: true,

root-no-standard-properties: true

rule-single-line-declaration-before: " ",
rule-single-line-declaration-after: " ",

selector-attribute-quotes: '"',
selector-combinator-before: " ",
selector-combinator-after: " ",
selector-delimiter-before: "",
selector-delimiter-after: "/n",

block-brace-opening-before: " ",
block-brace-opening-after: [" ", "/n"], // space (for single-line rule-set) or newline (for normal)?
block-brace-closing-before: [" ", ""], // space (for single-line rule-set) or no space (for normal)?
block-brace-closing-after: "", // is this where separation should be handled?

declaration-block-properties-order: "alphabetical",
declaration-block-semicolon-before: "",
declaration-block-semicolon-after: [" ", "/n"] // space (for single-line rule-set) or newline (for normal)?
declaration-block-trailing-semicolon: true,

declaration-colon-before: "", // no space`,
declaration-colon-after: [" ", "/n"], // space or new line`
declaration-coma-before: "", // no space`
declaration-coma-after: [" ", "/n"], // space or new line`

value-string-quotes: '"',
value-url-quotes: '"',
value-comma-before: "",
value-comma-after: " ",
value-bang-before: " ",
value-bang-after: "",
value-unitless-zero: true,

I haven't added any indentation or separation rules yet, as I'm finding them a little confusing.

  • What's the difference between indentation and indentation-rule?
  • Aren't Rule-sets also Statements so don't the current statement-separation and rule-set-separation overlap?

I was struggling to get my head around what are the rules needed for something like?:

/** @define Excerpt; use strict */

@import "suitcss-utils-layout";
@import "./Button";

/**
 * Content excerpts. Agnostic of image size, and with a clear call to action.
 */

:root {
  --Excerpt-padding: 20px;
  --Excerpt-color: orange;
}

.Excerpt {
  padding: var(--Excerpt-padding);
}

@media (--mq-wide) {
  .Excerpt {
    color: var(--Excerpt-color);
  }
}

i.e. where Statements have a blank line between, unless they are at-rule containing a @import. Comments also have blank lines. I'm not sure how best to approach the separation-* rules...

The separation/indentation thing feels like the last small hurdle as everything else is looking great! :)

@necolas
Copy link

necolas commented Dec 30, 2014

FWIW, if it's too much work to get the idiomatic-css or suitcss styles, i'd be happy to remove some of the more idiosyncratic aspects of those style guides. I'd rather have something easily enforceable with tools than make tool authors bend-over-backwards to support every little detail of a style.

@davidtheclark
Copy link
Contributor

Follow up to @jeddy3 and other comments above:

  • Maybe no-vendor-prefixes should exist as a General rule instead of a Property rule, because it does, as @jeddy3 noted, apply to various syntactic types.
  • I agree that rule-sets are statements, so those two rules overlap. I'd suggest that there be an option to enforce a separation between statements unless one statement has no content except another statement (e.g. media query containing a rule set). SCSS-Lint does it this way. (We could also of course have the option to enforce the separation even if the wrapping statement has no additional content.)
  • I'm hoping value-quotes means you can set a single rule that applies to all quotes in values instead of also setting the specific url or string quotes (?). Considering that, as @jeddy3 noted, there are also quotes in attributes, maybe that should be abstracted to a General Rule, quotes?
  • One thing I do not like about SCSS-Lint is that linters are generally enabled by default. I'd be in favor of stylelint disabling everything by default, as suggested above, with presets for those who don't want to make all the decisions themselves.

@MoOx
Copy link
Contributor Author

MoOx commented Dec 31, 2014

no-vendor-prefixes lgtm like @davidtheclark said.

statement-separation is a more generic rule than rule-set-separation. Make sense to me to get high level rules & more specific that override (like for indentation, or quotes).

Btw, generic quotes should be just quotes according to your comments guys.

Also, indentation is generic & indentation-rule will be for rule (before/after). Maybe we should just have indentation as int, & indentation-* as bool (enable/disable) to make this simple ?

@jeddy3 the problem for -before/after with custom string will be general indentation of the code

.A { /* {: before " ", after "\n" */

}  { /* }: before "\n", after "\n\n" */

  .A-b {  { /* {: before " ", after "\n" */

  }  { /* }: before "\n" + INDENTATION, after "\n" */

Here you might see a problem. Should we accept that some rules might rely on some others? (In this case ...brace-before, using indentation(-*).

Ok for:

  • declaration-block-trailing-semicolon
  • declaration-block-no-duplicate-properties
  • declaration-block-properties-order

For no-end-of-line-white-space what about something simple like no-eol-whitespace? Maybe to get something clear we could also do:

  • eof-newline (require newline at end of file)
  • eol-no-whitespace (no whitespace at end of line)

For the point about comma & multiples lines

How do we handle declarations that have multiple values and the user wishes to put them some of them on separate lines? e.g.

In you example, for background-image there is coma in the function with no \n & coma to separate gradient with \n. Not easy to spot. Maybe we can postpone this issue for later? Seems not a big deal to me.

Has enforcing one declaration per line disappeared? Perhaps it can be handled with the -before/*-after approach, in the same way as selector-delimiter-before/after works? e.g. declaration-block-semicolon-before and declaration-block-semicolon-after

I was thinking about that but didn't take time to write this up :)

Also I think we can just get ride of that

rule-one-line-property -> rule-no-single-line-declaration

We should get something like declaration-block-single-line-declaration-length (or *-max-declarations) to allow user to choose how many declarations for a one line syntax we tolerate. Note here that a rule like that will have to work in conjunction with block-brace-* rules. Not sure the [singleline, newline] thing make sense.
What if the user want to allow this two cases as valid

/* eg with declaration-block-single-line-declaration-length === 2 */

/* valid */
a {
   b: c;
   d: e;
}

/* valid */
a { b: c; d: e }


/* INVALID */
a { b: c; d: e; f: g }

@jeddy3
Copy link
Member

jeddy3 commented Dec 31, 2014

All those *-before/after and indentation rules issues are head-scratching stuff! I have one high-level query though, that I'd like to ask first, before I try to get my head around the *-before/after and indentation issues.

statement-separation is a more generic rule than rule-set-separation. Make sense to me to get high level rules & more specific that override (like for indentation, or quotes).

Is having general rules that can be overridden by more specific ones a good idea?

One of ESLint’s philosophies is: “Every rule is standalone”, and it looks like the “Working with Rules” section of the ESLint Developer Guide goes into a bit more detail about it. I think they do it because keeping each rule discrete helps to keep the linter pluggable and the rule explicitly understandable to the user?

Btw, generic quotes should be just quotes according to your comments guys.

So, using quote as an example…


Firstly, having general rules does not seem keep rules explicitly understandable:

Given:

quote: '"'

Am I right in thinking that this will enforce the following rules?:

  1. All strings within values must be doubled quoted
  2. All urls within values must be doubled quoted
  3. All attribute selectors must be doubled quoted

Is that too much stuff hidden behind a fairly harmless looking rule?


Secondly, and perhaps more importantly, does overriding something create a dependency between to rules?:

Given:

quote: '"' // general
selector-attribute-quote: "" // specific override

i.e.

  1. All strings within values must be doubled quoted (general)
  2. All urls within values must be doubled quoted (general)
  3. All attribute selectors must not be quoted (specific override)

Doesn't the quote rule now need to be aware that the selector-attribute-quote rule has been enabled (and what value it has), otherwise it’ll return an error when given:?

.selector[type=text] {
  a: b;
}

Wouldn’t that mean, if general rules were allowed, the linter would need to track which rules are dependent on which other rules?


Thirdly, even if all the rules are standalone, the linter won't need to manage conflicts between rules?:

Given:

property-blacklist: ["color", "width"]
property-whitelist: ["color", "height"]

And

.selector {
  color: blue;
  height: 10px;
  width: 5px;
}

The following errors would be returned:

file.css:2:3 - "color" is blacklisted // thrown by the property-blacklist rule 
file.css:4:3 - "width" is not whitelisted // thrown by the property-whitelist rule 
file.css:4:3 - "width" is blacklisted // thrown by the property-blacklist rule 

Which is good right? As it’s then down the user to correct their choice of rules so they no longer clash. The linter doesn’t need to get involved in sorting out clashing rules even if they are standalone.


Finally, keeping rules discrete/standalone could be advantageous to the rule developer. For example, a standalone, narrowly-focused rule like selector-attribute-quote would have:

  • Simple code logic e.g.
    1. If rule is enabled
    2. Get all selectors
    3. Filter out selectors that are not attribute ones
    4. Check that the attribute is single quoted, double quoted or not quoted depending on the option.
  • Focused test fixtures
    • .selector[type=text]
    • .selector[type='text']
    • .selector[type="text"]

I think it might be possible to get great mileage out of starting with this standalone philosophy and only tackling the interdependent rules later.

Here is everything I can think of that is definitely a standalone rule:

// File rules
eof-newline
eol-no-whitespace

// Rule-set rules
rule-set-no-empty

// At-rule rules
at-rule-no-vendor-prefix

// Custom media rules
custom-media-pattern

// :root rules
root-no-standard-properties: true

// Selector rules
selector-attribute-quotes
selector-no-vendor-prefix
selector-pattern
selector-no-id
selector-no-type
selector-no-universal
selector-no-attribute
selector-no-qualified
selector-no-pseudo-class
selector-no-pseudo-element
selector-no-combinator
selector-no-delimiter
selector-no-descendant
selector-max-length
selector-max-specificity

// Custom selector rules
custom-selector-pattern

// Block rules
block-indent // I think this is standalone...

// Declaration block rules
declaration-block-properties-order
declaration-block-trailing-semicolon
declaration-block-no-duplicate-properties

// Declaration rules
declaration-no-important // previously "value-no-important"

// Property rules
property-no-vendor-prefix
property-blacklist
property-whitelist

// Custom property rules
custom-property-root-restriction
custom-property-pattern

// Value rules
value-string-quotes
value-url-quotes
value-unitless-zero
value-no-leading-zero
value-no-trailing-zero
value-color-keywords-prefered
value-validate-hexa
value-unit-blacklist
value-unit-whitelist
value-body-background

That’s a lot of stuff and, it looks like, none of it needs the complexity of interdependent rules. (All the *-before and *-after rules might also be standalone (if they don't try to handle indentation), but I can't fully wrap my head around them yet).

So, keeping rules focused and standalone to begin with seems like a win-win to me. Rules are explicitly understandable to the user, and the Linter doesn’t need to keep track of what rules rely on other rules, or manage conflicts.

Does that make sense, or have I got myself confused thinking about this too much? :)

@MoOx
Copy link
Contributor Author

MoOx commented Dec 31, 2014

Indeed, we should make each rule standalone. Simpler to develop, simpler for testing, simpler to understand.
What about "simply" create rules with complex options ?

I'm dropping this as an idea:

{
  indentation: {
    "default": 1,
    "char": "\t", 
    "selector-class-bem-element": true
    "selector-class-bem-modifier": false
    "selector-pseudo-classes": 2,
    "selector-pseudo-element": true
    "rule": true
  }
}

@rafaelrinaldi
Copy link
Contributor

I really like the idea here, great stuff!

I currently use esformatter for JS and SCSS linter for SCSS.

Just one thing, I think that naming conventions such as BEM, SMACSS, and stuff like Compass or Bourbon could be "pluggable". This way Stylelinter would have plugins to handle non-CSS stuff (which makes more sense to me compared to presets?).

@ben-eb
Copy link

ben-eb commented Jun 8, 2015

@davidtheclark I think you would be best to use eachInside for these. I wrote some methods on top of it to filter the nodes based on type, but implementing something like selector-no-universal is this trivial:

parser.eachInside(function (selector) {
  if (selector.type === 'universal' && opts.noUniversal) {
    // ... your logic for warning the user
  }
});

I'm going to rework this iterator to be safe for removing nodes whilst iterating, like PostCSS does, but the parser itself is pretty solid right now. Just need more tools to work with the AST I reckon. 😄

@davidtheclark
Copy link
Contributor

@ben-eb Awesome. Just let us know when you think it's finalized enough for us to work with.

@MoOx I guess I don't understand why you would not use delimiters because you're using classes. Couldn't you have something like:

.my-component__thing--odd,
.my-component__otherpart {
  color: pink;
}

.my-component__thing--even {
  background: oragen;
}

I don't recall seeing a rule that you must have only one selector per rule -- maybe I missed it?

@ben-eb
Copy link

ben-eb commented Jun 8, 2015

@davidtheclark I just published 0.0.5 on npm which has safe AST iteration on each and eachInside which I think should be enough to work with for your use case. No API docs yet but I'll be working on those and looking to publish a 1.0.0 soon with usage instructions - for now just check the tests directory which has tons of examples. Also the sooner you start working with the module, you can identify issues before I stabilise the API. 😄

@ben-eb
Copy link

ben-eb commented Jun 10, 2015

@davidtheclark Current API: https://github.com/postcss/postcss-selector-parser/blob/master/API.md - if you have any suggestions, please let me know. postcss/postcss-selector-parser#5

@davidtheclark
Copy link
Contributor

Thanks @ben-eb. At some point here I'll have a few minutes and I'll try to write some selector linting rules to give it a shot.

@jeddy3
Copy link
Member

jeddy3 commented Jun 14, 2015

Updated list to reflect decisions in #178

@davidtheclark
Copy link
Contributor

@jeddy3 : Because of insupportable length of this issue, I suggest that we close it and open new issues for any of the rules listed way up above that we definitely want to implement or urge fantasy contributors to implement. What do you think?

@jeddy3
Copy link
Member

jeddy3 commented Oct 16, 2015

@davidtheclark Makes sense to me. I admit I'll be a little sad to see it go though as it's been with us since the start. Kind of like saying good bye to a comfy sofa that you've had for years, but has out-lived its usefulness :)

I'll pick out the ones I'd like to see. Then, if you can do the same then close the issue.

@jeddy3
Copy link
Member

jeddy3 commented Oct 16, 2015

I'm done :)

FYI, I replaced color-function-blacklist/whitelist with the more generic function-blacklist/whitelist to bring it more in line with the other -blacklist and -whitelist rules.

@davidtheclark
Copy link
Contributor

Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement a new feature that isn't related to rules
Development

No branches or pull requests

9 participants