prettier: No line break insertions in short CSS selectors

Prettier 1.14.2 Playground link

--parser babylon
--no-semi

I suggest reconfiguring the changes made in #1962 so as to only insert line breaks in CSS selectors when they exceed a given length. Otherwise, you end up with unnecessarily long files in cases such as the one below.

Input:

import styled from 'styled-components'

import mediaQuery from '../../utils/mediaQuery'

const Participants = styled.section`
  padding: 2rem;
  background: ${props => props.theme.mainWhite};
  display: grid;
  grid-gap: 2rem;
  h1, h2, h3, h4 {
    width: 100%;
    padding: 0.5rem 2rem;
    background: ${props => props.theme.lightGreen};
  }
  ${mediaQuery.phone} {
    h1, h2, h3, h4 {
      font-size: 1rem;
    }
  }
  ${mediaQuery.tablet} {
    grid-gap: 1rem;
    h1, h2, h3, h4 {
      grid-column: 1/-1;
    }
  }
  ${mediaQuery.minTablet} {
    h1, h2, h3, h4 {
      white-space: nowrap;
      grid-row: 2;
    }
  }
`

export default Participants

Output:

import styled from "styled-components"

import mediaQuery from "../../utils/mediaQuery"

const Participants = styled.section`
  padding: 2rem;
  background: ${props => props.theme.mainWhite};
  display: grid;
  grid-gap: 2rem;
  h1,
  h2,
  h3,
  h4 {
    width: 100%;
    padding: 0.5rem 2rem;
    background: ${props => props.theme.lightGreen};
  }
  ${mediaQuery.phone} {
    h1,
    h2,
    h3,
    h4 {
      font-size: 1rem;
    }
  }
  ${mediaQuery.tablet} {
    grid-gap: 1rem;
    h1,
    h2,
    h3,
    h4 {
      grid-column: 1/-1;
    }
  }
  ${mediaQuery.minTablet} {
    h1,
    h2,
    h3,
    h4 {
      white-space: nowrap;
      grid-row: 2;
    }
  }
`

export default Participants

Expected behavior:

No insertion of line breaks in CSS selectors of length less than, say, 80.

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 27
  • Comments: 36 (14 by maintainers)

Most upvoted comments

@lydell I’m a little surprised that this issue is even debated. I was under the impression that the point of prettier is not to be simple in and of itself but to produce ideally formatted code. I can’t imagine anyone would consider this to be ideally formatted,

h1,
h2,
h3,
h4,
h5, 
h6 {
  margin: 0;
}

especially when compared to this

h1, h2, h3, h4, h5, h6 {
  margin: 0;
}

Breaking code onto multiple lines only once it exceeds a certain length is such a common practice. I don’t see why it would be more difficult or less useful with CSS selectors than anywhere else.

@lydell It’s a shame really. I understand that you’re looking for an overwhelming consensus before taking any action since you probably have more important things to focus on.

But with Preitter being an already opinionated formatter, I was at least hoping that even though this thread lacks popularity, that the change request shares similar values that were already made on other languages for virtually the same scenario in Javascript. I don’t understand why SCSS is being treated differently.

I’d least hope that we can look at this change request objectively and see that implementing it would not only make sense for Preitter, but add consistency across different languages.

I think we can close issue, printing in one line unreadable

I think we should keep things as they are. Keep it simple! As of this writing, there are no 👍s on this issue.

@Jason-2020 I was having the same issue, and was about to have all my .css files be ignored by prettier, when I stumbled upon the ignore comment, when I was looking for a way to do it.

THANK YOU. Now my meyer-reset doesn’t look like complete shit every-time I add it to a project.

before (disgusting) Pasted_Image_7_23_21__11_54_PM

after (better) Pasted_Image_7_23_21__11_52_PM

I think we can close issue, printing in one line unreadable

To say the one-line code examples given above are unreadable is a bit of an over exaggeration.

I think @j-f1 idea would be a great solution to this.

Let’s go with the heuristic of not automatically breaking selectors that consist of just element selectors.

This would undeniably be better default behavior than the current implementation. And since this is a relevant topic, I still think it’s worth considering offering a config for this, whether it be toggling multiline for style selectors or setting some kind of max-len as referenced here #5937

It can sometimes be more preferable to read/write:

.heading, h1, h2 {
   margin: 0;
}

as opposed to:

.heading
h1,
h2 {
  margin: 0;
}

Prettier has handled this for Javascript, so what I am proposing is something similar for CSS too. For example, it’s generally more preferable to read/write the following:

const { Header, Footer, Nav } = require('./layout');

as opposed to:

const {
  Header,
  Footer,
  Nav,
} = require('./layout');

Hello from 2021! Have there been any updates or workarounds for this since the last post?

I feel it makes better sense to split to multiple lines when it goes beyond a certain number of characters as pointed out by @SYNTAG at https://github.com/prettier/prettier/issues/5086#issuecomment-469507962, @janosh at https://github.com/prettier/prettier/issues/5086#issue-359655142, @j-f1 at https://github.com/prettier/prettier/issues/5086#issuecomment-420960646

Also, as per the official Prettier - Options page, it says

For readability we recommend against using more than 80 characters:

In code styleguides, maximum line length rules are often set to 100 or 120. However, when humans write code, they don’t strive to reach the maximum number of columns on every line. Developers often use whitespace to break up long lines for readability. In practice, the average line length often ends up well below the maximum.

Prettier, on the other hand, strives to fit the most code into every line. With the print width set to 120, prettier may produce overly compact, or otherwise undesirable code.

So why not apply the same logic to CSS as well? Put as many as 80 characters and only split when the code is untidy instead of splitting for each selector which makes it tough to scroll or decreases readability in the case of long CSS files. I feel an option to choose between the two styles would be nice to have.

@nickbclifford It seems unlikely that Prettier will change its behavior here. But a simple workaround is to use the CSS pseudo-classes :is and :where (they only differ in specificity).

Prettier will not break inside these selectors:

:is(h1, h2, h3, h4, h5, h6) {
  margin: 0;
}

If your selectors have a common parent or child, I wouldn’t even consider this a workaround but the new best practice:

Old CSS

section h1,
section h2,
section h3,
section h4,
section h5,
section h6 {
  margin: 0;
}

New CSS

section :is(h1, h2, h3, h4, h5, h6) {
  margin: 0;
}

Browser support is 90% for :is and 81% for :where so unless you still care about IE, you can start using at least :is right away. 😎

Is there any workaround?

Here are some more real examples of scenarios where it’s nicer to have one line:

*, *:before, *:after {
  box-sizing: border-box;
}
ul, ol {
    margin: 0;
    padding: 0;
    list-style: none;
}
h1, h2, h3, h4, h5, h6 {
  margin: 0;
}

There are a lot of cases like this where it’s simpler to keep things compact. Putting each selector on its own line is nice, but only really necessary when you start selecting more things.

Let’s go with the heuristic of not automatically breaking selectors that consist of just element selectors.

So is it possible to reconfigure this behavior?

@Jason-2020 I was having the same issue, and was about to have all my .css files be ignored by prettier, when I stumbled upon the ignore comment, when I was looking for a way to do it.

@Shirobachi So we can have the following in css:

/* prettier-ignore */
p, h3, ul {
  margin: 0;
  padding: 0;
}

@janosh and the following in javascript:

import styled from "styled-components";

import mediaQuery from "../../utils/mediaQuery";

// prettier-ignore
const Participants = styled.section`
  padding: 2rem;
  background: ${props => props.theme.mainWhite};
  display: grid;
  grid-gap: 2rem;
  h1, h2, h3, h4 {
    width: 100%;
    padding: 0.5rem 2rem;
    background: ${props => props.theme.lightGreen};
  }
  ${mediaQuery.phone} {
    h1, h2, h3, h4 {
      font-size: 1rem;
    }
  }
  ${mediaQuery.tablet} {
    grid-gap: 1rem;
    h1, h2, h3, h4 {
      grid-column: 1/-1;
    }
  }
  ${mediaQuery.minTablet} {
    h1, h2, h3, h4 {
      white-space: nowrap;
      grid-row: 2;
    }
  }
`

export default Participants;

Is a bit annoying to have to add a comment, and it has to be added before every block of code, or abstract syntax tree as referred to by prettier docs, that is to be excluded from formatting.

Ah, I see. It was decided early on to print every selector on its own line: #2057

Just a thought, has anyone tried to PR this idea? I wasn’t able to find anything and 10000% understand the want to make sure a PR will be accepted before writing the code for it, but I wonder if that’s the main thing holding this up. Clearly there’s support for this being a setting, which also means those who are fans of the current use could still have their way. Even keep it as the default.

Thanks for digging that up! Interestingly, that issue also didn’t receive any upvotes. Seems like there is low interest in CSS selectors in general. FWIW, I believe making that change was a misstep.