Hacker News new | past | comments | ask | show | jobs | submit login
18F: CSS coding style guide (gsa.gov)
243 points by molecule on Jan 29, 2016 | hide | past | favorite | 96 comments



This is a pretty good style guide and has a lot of habits I've been practicing for many years now (even pre-sass/less).

Slightly off topic, I'm slightly bothered by the amount of trash talk CSS receives since it's pretty much the best language we've ever had to define interface styles, and it's only getting better. Maybe some of the rules about behavior are arguable, but by and large I've never met a design I couldn't get to work with some well-defined CSS.

And if you can't read nested CSS I can't imagine how you'd feel in the face of some heavy XML.


I often think the existence of CSS can be seen as the strongest criticism of XML: when designing a language to store styling attributes for HTML, they used their own heirarchical format instead of another SGML-derivative.


I think you overestimate the amount of considered, thoughtful reflection that went into creating HTML, CSS and JavaScript.


And yet they have become useful far beyond their original intended design.


No. They have prevented aeons of progress. They are an unmitigated computing tire fire that future generations are going to curse us for.


Yeah, if just full-screen Java Applets had prevailed instead of web pages we would be much better off! Seriously, the html/css/js stack has its share of haphazard design, but it has shown to be much more robust and adaptable than any of the proposed "better-designed" alternatives.


Yeah I do wish I could see a bunch of unstyled, barely functional UX "designed" by engineers who have no understanding of interfaces or design.


Future generations? Current generations curse them right now :-).


Your hyperbole is truly as excellent as your dangling prepositions. I do so love the smartness with which you declare the end of society.


I and many other have careers because of them.

If it is such a problem for you, I would suggest coming up with actual solutions instead of the Grandpa Doom visionary "prediction" you've offered which is of no value.


Well, they've been used far beyond their original intended design, at least.


What exactly is not useful about using HTML and CSS to create web pages? The entire medium of the web to create applications is already far beyond the original intended design of the browser, etc. So what makes it so bad besides that some people, who seem to think they're pretty smart, have a lot of difficulty with it?


All of it? :)

It's neither a good rendering medium, nor a good language to describe the structure of the data.


Surely you must be joking about JS at least. The language was created in 10 days.


He writes "overestimate," not "underestimate" :)


Well CSS is older than XML. SGML was considered a (meta-)markup-language, i.e. a format for adding semantic info to human-readable text. It was never considered an universal data format. As an example DSSSL, the style sheet language of SGML, was not itself a SGML language, so it is clear the SGML community never considered SGML appropriate for all kinds of data. For that matter, DTD's are not in SGML language either.

XML was initially thought of as a simpler SGML, but when it took off it started to be used for all kinds of data including data which are not based on human-readable text at all. XML is fine for markup, but needlessly verbose and complex for non-markup languages.


I think you're mistaken about DTD, they were used in SGML. (As in XML they were optional.)


I was unclear. I meant DTD's are not an SGML language.


I think CSS is the strongest criticism of CSS.


Nested selectors are some of the least maintainable language features i have ever worked with. Once the number of embedded classes, or layers of embedding gets too big, the entire file descends to a bad place.

Here is a real world example of what i am talking about, from a code base i currently work with, and don't dare touch: http://i.imgur.com/fMlvoRS.png

The idea of maintaining legacy sass with heavy embedding in years to come makes me shudder.


Nesting makes CSS easier to read and is more maintainable than having multiple similar names, especially for containers.

That one you mention is a huge problem. Hence the documentation states "Don't nest more than 3 layers deep".


That is pretty bad. Sadly I've seen much worse.

Most sites don't need variables and nestings of CSS.

I would rather see duplicate class names like this in CSS.

.wrapper1 .header {}

.wrapper1 .header h2 {}

.wrapper1 .body {}

.wrapper1 .body .article {}

.wrapper1 .body .article .blurb {}

.wrapper1 .body .article .quote {}

.wrapper1 .footer {}

Repetition doesn't bother me. In fact, it is surprisingly helpful.


This and the parent comment confuse me.

It's not the nesting in the SASS that's the problem per se, its the fact that it makes it easy to create the kind of code that you list that's the problem.

Modern good practice suggests a single classname for each item, so you're not fighting with specificity too much.

The original "bad" example doesn't even do that, its just using :before and :after etc. not nested selectors.


when you're building extremely large, reusable, single page applications, it's safer to go overboard with name-spacing via nesting.

Alternative is to create descriptive, but potentially very lengthy class names that is single level.


This is simply bad advice. The kind of nesting you showed will make the application non-maintainable. Your HTML gets completely locked down since its styling is dependent on things being nested in a certain way.


So you'd also prefer

  if wrapper and header:
    ...
  if wrapper and body:
    ...
to

  if wrapper:
    if header:
      ...
    if body:
      ...
when coding?


To be totally honest? Yes. Co-locating all the conditions makes it easier to comprehend at a glance. There are obvious drawbacks, but I lean toward this style more and more.

Consider the case below. If I want to understand what conditions are necessary for body, that code is now offscreen. I generally consider overly-nested conditions as code smell.

    if wrapper:
      ... 30 LOC ...
      if header:
        ... 30 LOC ...
      if body:
        ... 30 LOC ...


"... 30 LOC ..." followed by three or four more in the same logical chunk seems like the smell you should be worried about if fitting everything on one screen is the concern.


It's not about fitting things onto screen; it's about fitting things in brain.

If ~25 years of doing programming for money taught me something, it's that the code is written to be read and maintained by humans, and having really short, clean, one-purpose, composable functions makes your life easier both long-term and short-term.


Amen to that, friend :-) First thing on programming class in the eighties-nineties:

"Fit all functions on one screen."

At that time that was roughly 22 lines depending on editor used.


  Two If statements, no nesting

  vs.

  Three If statements, 1 level deep nesting
To me, the first option is clearly simpler and more readable. Then again, looking at the second option I notice immediately that I can dismiss it if wrapper happens to be false. But it really depends on what the other code lines contain.


You could also say that's 4 comparisons vs. 2 comparisons. Not the end of the world, but what if you have one additional level of nesting?

    if a:
      if b:
        if c:
        if d:
      if e:
        if f:
        if g:
Vs.

    if a and b and c:
    if a and b and d:
    if a and e and f:
    if a and e and g:
Or would you perhaps find

    if a and b and c:
    if d and b and a:
    if e and a and f:
    if g and e and a:
Or

    if e and f and a:
    if c and b and a:
    if g and a and e:
    if d and a and b:
In any case, now you have 12 comparisons vs. 3 comparisons. (Though this is not entirely comparable to CSS, since selector order is somewhat more restricted.)


I have never really considered repeating conditions instead of nesting. I might try doing so next time since I'm generally not happy with the depth of nesting I sometimes land myself in.

The problem I foresee with repeat conditions is that during a change, I might forget to update one or more of the repeat conditions.

Also, I wonder, do gcc and/or clang recognize repeat conditions and produce the equivalent of what nested statements would?


Python taught me some good practices in regards to this. Python's "enforcement" (or rather, strong PEP-8 suggestions) of 80 characters per line teaches you to come up with a lot of ways to avoid nesting.

The most useful one, and the one that drives me crazy when I see it, is when people write code like this:

    if something:
      value = do something
    else:
      value = do something else

    return value
These days I write code like this:

    if something:
      return do something

    return do something else
In the simple example, it's not a big deal. But once you have 30 lines of code in between each statement (and nested if statements), it gets trickier to maintain every possible code path in your head.

This isn't the same as "show all conditions at once" -- instead it's "ignore the conditions that have already been satisfied."

Seeing that we already returned out in condition #1 lets me focus more on condition #2.


Believe it or not, I once interviewed for a cryptography firm and they had me look over some code samples to improve them. Some of my improvements were just what you did. But they stopped me mid way through and said that the "standard" they are required to follow only allows one return statement and it had to be at the end of the function... Don't know what standard it was, whether their own or some 3rd party compliance.


I've heard this argument (I like a single exit point) before, but I don't really understand it, I'd love for someone to explain why they prefer it, if they do.

I think the main reason returning early works so well is that often times, when you're returning different values, it's because you quickly have an answer (often times that something is invalid) for particular inputs. Generally though, there is "real work" to be done after validating or normalizing your inputs.

If that's not true, you're probably trying to do too many things at once, and the different pieces should be broken into separate functions.


In the specific case of crypto, maybe they are concerned about leaking information through timing if they return early? Though even if you have only one exit point, that alone does not in any way guarantee that timing differences won't occur. For that, I think the same work must be done always and only at the very end it is decided whether or not the result is valid. Something like that. So maybe they have some other reason. Maybe it's just cargo cult programming.


Even worse yet is when people do

    if some_function(arg, arg2):
        result = True
    else:
        result = False

    return result
Obviously a waste of time and lines of code. Instead, do

    return some_function(arg, arg2)
Unless anything else needs to be done with the result of some_function but that's not the case I'm talking about, I'm talking about when it looks like above.


> Python's "enforcement" (or rather, strong PEP-8 suggestions) of 80 characters per line teaches you to come up with a lot of ways to avoid nesting.

> The most useful one, and the one that drives me crazy when I see it, is when people write code like this:

But... your example doesn't show any ways to avoid nesting. There's just as much nesting after as before.

Your change makes the code less tall, which is a problem I've had with Python, but it has nothing to do with nesting at all.

I'm actually fond of a third approach:

    if something:
        return do something
    else:
        return do something else
I like the ocaml-style philosophy that it's an error if your condition check isn't capable of handling all possible conditions.


I figured readers could fill in the relevant blanks. Updated example:

    if something:
      value = do something
    else:
      if something_else:
        value = do something else
      else:
        value = do a third thing
As I said in my initial post, "it helps once you have nested ifs, and multiple lines of code." Here's the change:

    if something:
      return do something

    if something_else:
      return do something else

    return do a third thing
Again, what I'm stressing here is that if you already have your result, you can return, and it makes the code easier to follow than adding a superfluous "else:" statement.


Ternary operator to the rescue:

    return something
        ? do_something ()
        : do_something_else();


Yes. It's easier to troubleshoot CSS when pre-compiled/processed code looks similar to post-compiled/processed code.

Also, it does force you to write CSS more efficiently because if you work with pre-processor language, you often don't see the bloat.


    if wrapper:
        do_thing_to_wrapper(wrapper)

    def do_thing_to_wrapper(wrapper):
        ...


The problem with those rules is the second you want to reuse your .quote class in your new .wrapper2 layout you are in for some refactor hell. In addition your specificity levels are getting pretty wonky, simple things like media queries would require duplicating the exact same nesting structure. Another issue which could effect code like this is ui performance, see this deck for more info https://speakerdeck.com/jonrohan/githubs-css-performance. Many sites won't need to worry about this last point.

Every modern CSS codebase attempts to use 1 location agnostic class per element. Usually when warning against 3 levels of nesting in preprocessors it is to maintain readability on pseudo selectors but not an encouragement to nest classes.


Really? This is pretty much bearable is not that bad as you make it sound. Is not really unmaintainable and horrible unless the code-base doesn't has a defined specification through variables and guide lines.


I guess I'm bad at CSS/SASS, but I must be missing something.

Maybe context.


Enable sourcemaps, click on the selector, and it literally tells you what line to edit. Then go to sublime, cmd+p -> filename:line number -> enter. in 5 seconds you went from finding a css problem and finding its exact line.

What that guy posted is fine (only if you have SOME sort of rules around nesting - our team has been getting along great ever since using rscss.io methodology)


> Enable sourcemaps, click on the selector, and it literally tells you what line to edit. Then go to sublime, cmd+p -> filename:line number -> enter. in 5 seconds you went from finding a css problem and finding its exact line.

Doesn’t firefox even support in-browser viewing and editing of SASS?


Agreed. Nested selectors in SASS/LESS is a classic case of something that makes writing code easier, but reading (understanding) code harder.

And I think the benefit of the ease-of-writing is minimal (especially if you're using something like BEM where you're trying to avoid cascading anyway), whereas the drawbacks in terms of understandability are super duper high.


I think they are easily abused. I don't think they should be lumped in to the untouchable feature category but definitely have some rules around their use.

I think it's always ok to use them for pseud-oclasses and I think in general if you've nested something more than 2 levels deep you probably have a sign you might need to refactor something.


I'd agree in theory, but the problem remains: will a future dev share the same foresight? As far as i can tell their spec contains no standard regarding depth or length


If one mistrusts future developers enough, one sets up linting to enforce one's own standards:

https://github.com/CSSLint/csslint


I agree and empathize, especially with your example. Just out of curiosity, do you find this more or less readable: https://gist.github.com/kaishiro/fda6d9e6cb9876944901

No right or wrong answer here, just genuinely curious.


If you placed comments on the closing curly brackets to state where they started, then that's perfectly readable. e.g

.myclass { ...30 loc ... } /* .myclass */

I really don't see this enough.


I believe the linter is the important part here. I've come to the conclusion that style guides are only useful if they come with a linter that fails builds. It's way too easy to put code into a repo that works but doesn't live up to the style guide and once code hits trunk it's a lot harder to rework it.


I'll go you one further and say it shouldn't only be optionally enforced, it should just happen. The less I have to think about style the more I can focus on design.

In the future every new language should come with a "go fmt" equivalent.


Auto-formatting is great, but it can't handle everything. For example, our ESLint config prevents modification of function params[1]. I don't think a tool could automatically reformat this into compliance without changing the behavior of the code, a human needs to look at the failure and change it.

[1]http://eslint.org/docs/rules/no-param-reassign.html


That's a ultimately a "problem" with the initial language design. If you design it with the intention of using auto formatters you won't run into these issues.

I originally didn't like braces in go, but then I realized there's just enough in there to make auto-formatting easy.


I think language design may be too strong a term for a lot of web technology. That's where you want to be using a linter and/or validator though.


Yep, also when there is an agreed upon linting config, the tooling becomes the "bad guy" on pull requests and lets humans comment on non-trival things.


I choose to see the positive here.

Yes, you can criticize the nested classes and inconsistencies of brace placement. (And you should).

But I love seeing things coming out of 18F. I think this sort of open gov computing projects are blessed.

Even if you're not 100% open source but your development is "in the open", that's a huge step towards a better service to citizens.


Somewhat related, I've been pleasantly surprised by the number of design manuals that US and other government agencies are releasing. Some notable favorites are:

* CIO: https://playbook.cio.gov/designstandards/

* CFPB: https://cfpb.github.io/design-manual/

* GOV.UK: http://govuk-elements.herokuapp.com/

Hope to see more open gov open source projects in the future.


Their modified BEM choice is surprising. I think BEM works best when the dividers have meaning. In their examples its impossible to know what is a modifier vs an...element? I don't get it from the examples.

  .accordion
  .accordion-item
  .accordion-item-selected

  .nav_bar
  .nav_bar-link
  .nav_bar-link-clicked
Though its the most ugly, I'm a fan of the original B__E--M


>Though its the most ugly, I'm a fan of the original B__E--M

I feel this is something people need to learn to look past - because I agree entirely. It looks terrible. Absolutely terrible. Names get long (but yay for autocompleters!) - but everything is readable. CSS shouldn't be aesthetic. It should be legible. Maintaining something you can't read is hell - every programmer knows this.

I also make use of prefixes to namespace. Which makes my classes even longer. [0]

But there is absolutely no question what my code is doing when I read the HTML. I know exactly where changes need to be made. My coworkers are familiar enough with my style that they know what code is safe to change and what code will have side effects. They're slowly adopting the style - although it's almost a 180 from the old style of "be as specific as possible so you don't accidentally break something someone else had worked on 8 months ago". (#NotEvenKidding .our .selectors .are > .like .this)

[0] http://csswizardry.com/2015/03/more-transparent-ui-code-with...


Agreed. There is no need to use a custom flavor of BEM.

You might be able to enforce a distinction between elements and modifiers (with elements nested below parents, while modifiers are siblings of the elements they modify) like so:

  .accordion {
    .accordion-item { }
    .accordion-item-selected { }
  }
But this requires nesting where true BEM otherwise avoids it.


Is it just me, or does the screenshot from the blog post show code with inconsistent open-brace placement near the td selector? I quickly looked at the format section but but didn't see that style anywhere.


Yup. Good eye. We've updated the post with a new image.

*I'm an 18F employee.


Yes, correct that is wrong unless the linter is set for newline style. It's not https://github.com/18F/frontend/blob/18f-pages-staging/.scss...


You're right.


It is interesting to see that they recommend againts using Bootstrap:

  18F specifically does not recommend using Bootstrap for production work because of one, 
  the difficulty in adapting its opinionated styles to bespoke design work and two, 
  its CSS style places semantic layout instructions directly in HTML classes.
https://pages.18f.gov/frontend/css-coding-styleguide/framewo...


I agree with them. I work in an academic setting where accessibility is important, and Bootstrap makes a hash of semantics, which is bad for assisted reading systems that need it for navigation.


Accessibility is important for us too. What do you use if you don't mind sharing? There is a accessibility plugin for bootstrap from Paypal [1]. Have you ever tried it? Thanks.

[1] https://github.com/paypal/bootstrap-accessibility-plugin


This is only somewhat related, but I find it really interesting that the vast majority of outfits using Sass opt for the SCSS syntax rather than the native one.

Personally I always opt for the latter, since I find it heavily reduces the amount of syntactic noise, which is a personal bugbear of mine. is the main reason for using SCSS the familiarity, or the fact it's a strict superset of CSS, or just that people generally dislike whitespace sensitivity? I can see the benefits, but it would be interesting to know if there's another angle I haven't considered.


Probably, the main reason is that it's a superset of CSS. The learning/migration path is extremely low.


No real experience here, but the documentation presents SCSS samples first with a small textual toggle button to view the Sass syntax. If I were learning Sass, that'd be a big indicator to use the SCSS syntax.


I was curious what their standards for indents were:

"Use soft-tabs with a two space indent."[0]

Ugh, I work in Gov (DOD) and we recently switched to two spaces (I'm a Java/PhP dev and have always used 4 spaces) and was hoping to use this to support a switch back, guess that's a no go...

[0] - https://pages.18f.gov/frontend/css-coding-styleguide/format/


Amusingly, the top picture on their page mixes 2- and 4-space indentation: https://18f.gsa.gov/assets/blog/css-guide/css-guide.png


I prefer tabs for indentation because then the developer can set their IDE to two space or four space or 8 space or whatever, the tab functions as an abstract unit easily convertible to "one level of indentation."


Truth be told, I had switched to tabs (4 space tabs), mostly because my JS linter complained about spaces and I didn't have very strong feelings about it (in hindsight, it's probably a simple config setting).


I never really thought to do anything like .classname.classname


Yeah that's pretty clever (I hasn't thought of it before either). Reminds me of another hack/trick I saw recently, where you just put :root in front of a selector to bump up its specificity.


Yes, I read 18F, but where are you from?


Not related to this directly, but Jez Humble just joined 18F, which I found surprising, but great.


More like SCSS coding style guide.


SCSS/SASS/LESS are NOT CSS, I wish people would stop treating them as the same.

* Caution, rant below *

I also get really annoyed when people introduce SCSS/SASS/LESS in introductory texts. Confuse a beginner right out of the gate with these template languages that were made to work around CSS, which works perfectly fine, but is just slightly inconvenient because it doesn't offer near-programming-language levels of sugar/complexity.

/rant


Is it okay to minify .css files for production or does it not really matter that much?


It's usually recommended. Source maps help you still find right line numbers and test changes, while minification+concatenation lets you write CSS across many files without worrying about HTTP requests.

Do note this practice may be discouraged as HTTP2 gains wider adoption.


> Do note this practice may be discouraged as HTTP2 gains wider adoption.

Concatenation yes, minifying is always a good thing though.


Concatenation still helps under HTTP2 due to better compression (more similar stuff in one file), but the individual files can't be cached/evicted/updated individually, you can't just load the bits you need for the current page, and you can't prioritise certain parts to load first.

What the new standard approach will be I'm not sure.


I'm guessing caching would be prioritized over the improved compression rates, except in very specific circumstances. Though we'll also see improvements there as brotli starts to replace gzip and deflate.


Interesting. THis should be helpful as I delve more into this area


Surprised to see (sass) selector nesting.


I am intrigued by 18F itself (the organization). Employees are on the "GS" scale (GS-14 thru GS-16). From the "Joining 18F" site's "Government pay grades explained" https://pages.18f.gov/joining-18f/pay-grades/

"… you cannot work at 18F on your position description for more than four years … We believe that in this industry most people aren't going to stick around for four years and beyond. They will join 18F, do their years of service in the federal government, and then return to the private (or public) sector. Due to the high-pressure nature of our work, it makes sense that people will move on after a couple of years. That helps 18F stay fresh and strong."

What the frack?!? Isn't the whole point of working for the Federal Government to have a job for life? 18F is actually worse than (non-startup) private sector jobs in that your contract says you cannot stay in the job longer than 2 (or 4) years. Fascinating.


> What the frack?!? Isn't the whole point of working for the Federal Government to have a job for life?

No, though certainly the opportunity for a long-career with a stable employer is part of the attraction for some people of some federal positions.

OTOH, even term-limited positions like those at 18F don't really conflict with this, they just mean you need to find a different federal position before the term ends, leveraging your 18F experience.

> 18F is actually worse than (non-startup) private sector jobs in that your contract says you cannot stay in the job longer than 2 (or 4) years.

Limited-term-contract, non-startup, private sector jobs where you need to line up a new gig before the contract expires to keep working are not exactly uncommon, so I'm not sure how you characterize 18F as worse than non-startup private sector jobs based on a feature it shares with many non-startup, private sector jobs.


> I'm not sure how you characterize 18F as worse than non-startup private sector jobs

Just seems like there is a mismatch between 18F's (high) cool factor and the compensation structure. These pay rates do not strike me as "contracting" or "consulting" rates. They strike me as more in line with "full time employee" pay rates at ordinary private-sector (non-startup) companies. But the job term is capped like a contract job. I don't see the financial upside to match their statement that https://pages.18f.gov/joining-18f/pay-grades/:

"Due to the high-pressure nature of our work, it makes sense that people will move on after a couple of years."

Sounds all startupy without the startup upside.


> But the job term is capped like a contract job.

I work at 18F, but am speaking for myself here. Pay rates and length of service are both tied to existing rules for federal employment. The advantage of being part of the government (and thereby following these rules) is in the possible impact of our work, like working with the FEC to build a new website with intermediate releases: https://beta.fec.gov/




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: