CSS: The bad bits and how to avoid them
joeforshaw.com
joeforshaw.com
You could put the style right in the HTML tag, or on the page where it's used, but with dynamic pages (and static stylesheets) both options will cost you bandwidth.
And they split up your styles into separate places.
So putting the style on the id is the right thing to do, and I don't understand why the author is against it.
If I need my footer to look a certain way, then #footer {} is exactly what you should do.
Not add some bogus class based style just in case someone decides we need two footers on one page.
If you don't have much resuable stuff on your site, that's a code smell in itself
It can be reusable and yet unique on each page. And those should get id's, and are not rare at all.
> The percentage of things on a page that are "singletons" is very small compared to reusable widgets.
That isn't necessarily true. Obviously there are different types of pages, but a typical page will have a number of elements on it that are each unique. (They might appear on more than one page, but on any particular page, they are unique.)
Give them id's, that's what they are for.
I _think_ what's going on here is that, in the, excuse my French, _enterprise_ sector it's very common to have some sort of an end-user configurable, enormous form (or dashboard or whatever) full of widgets that are not in any way unique. In fact in many places it's so common, that for a certain section of developers it might be hard to imagine that the opposite is actually quite common.
This is _a lot_ of "web" development. Often done by developers who, to be honest, would rather be doing this in QT (eww) or something.
If you have a choice, use class, then if you need to modify a specific use of the class, use id.
I would still use .footer instead of #footer. Of course you should consider <footer> as well.
Why?
Of course, you can still do this if you want to. To quote Chris Rock, "you can drive a car with your feet if you want to." But if you work on a large code base that lots of developers have touched, and lots of future developers will touch, you run into these issues all the time. And the bugs that come about can be tough to spot and to fix.
Either remove that, or add the id to the selector with the style.
For me it's a matter of doing things the same way so when I need to make an update to the code in a year it's intuitive.
"Be kind to your future self" is my motto.
Way more self describing. No team member will ask which one is reusable and which one is not.
<div class="footer special enterprise">
.footer.special.enterprise {
background-color: blue;
}If you made it a class, I would assume I can reuse it elsewhere.
Now it's ony true for stuff you can't reuse, but unless you are building a boostrap competitors, there are actually a lot of things you never reuse in a real website.
My co-worker wanted to grab by id and attach listeners, but I was convinced that wouldn't work. I wanted to listen at the top level and filter down by id/class/etc...
The first production test was <redacted>.com/login. It had a login widget on the navbar and a login widget in the main content section. Both the username and the password field had the same id in the navbar and the content. So the addon did the right thing on the navbar, but not in the content, and we had to go back and redo it thanks to id reuse.
My point is, elements with ids are rarely unique. I've never regretted using a class instead of an id. I've regretted using an id instead of a class. And I really regretted third parties using ids instead of classes.
Please don't use ids. Your unique elements won't be for long, I guarantee it.
I get where you're coming from, but I don't think it's a good reason to avoid classes. That's what they're there for.
My simple rule for id vs class (as a non-expert) is if you say 'this is the x' e.g. 'this is the footer/header/navigation-panel' then it's an Id, if you would say 'this is a y' e.g. 'this the a product/list-item/image' then it's a class.
Essentially it's benchmarking a handful of branches inside getElementById that do the map lookup for an Element [1] against the handful of branches inside getElementsByClassName that do the map lookup for a NodeList [2]. They both look pretty similar to me in branch and map lookup count, so I'm not sure where the difference is, but it's not something that would impact real content since on a real page getElementsByClassName's cost is related to typical behavior being O(N) instead of O(1) like getElementById. Browsers have a ton of caching around it though, so getElementsByClassName can look like O(1) in micro benchmarks.
You could could alter the benchmark to do getElementsByClassName("my-class")[0] to test the scanning but browsers cache that too, so you'd need to actually modify the document between each run of the benchmark somehow (ex. adding or removing an element) to invalidate the cache.
Typical content that uses getElementById will be faster than content that uses getElementsByClassName, but it's hard to show that in a benchmark. :)
[1] https://cs.chromium.org/chromium/src/third_party/WebKit/Sour...
[2] https://cs.chromium.org/chromium/src/third_party/WebKit/Sour...
Shared stylistic properties for components use class names. The properties that make a navbar look like a navbar(color, font, and anything that applies to children) would be specified under ".nav" and the same would apply for the body under ".body". Anything that's stylistically-specific to
Shared variations of components are handled with modifiers. An example would be ".nav--dark".
Any one-off variations are handled with parent-scoped class names like ".article__nav", since I treat even that topmost element as a component. (or a block if you're thinking in BEM).
I've found that keeping "style" and "layout" CSS properties separate helps keep components flexible and untangled from a given page layout. The differentiation between IDs and class names makes this more convenient because it becomes obvious what parts of my CSS are intended for.
But what about the internal layout of components? If the children of a component are supposed to change their sizing and positioning at different breakpoints, then how are you supposed to achieve this while keeping components agnostic about the page layout?
It's a tricky problem, but I solve this with Sass mixins. If a component has different internal layouts for mobile and tablet screens, as an example, I write mixins to define those states using the same naming convention that I would use with class names and IDs. If I was using a grid system, I would define them as "@nav--lg", "@nav--md", "@nav--sm", "@nav--xs", etc. Then underneath the page layout, in this case "#article", I would include those mixins under the chosen breakpoints. That way, all layout can be handled by IDs in stylesheets dedicated to page layouts.
It just so happens that I have a Codepen that demonstrates this idea:
https://codepen.io/Ravenstine/pen/xpYoWv
The only part that is different is that I made the masthead and the footer not a part of "#article". If you don't plan on having components like that behave differently depending on the page they are on, that's probably appropriate.
<div class="my-block my-block__my-element my-block__my-element--my-modifier">
becomes <div my-block="my-element my-modifier">
This solves for specificity, and uses built-in CSS features instead of a "manual" workaround. <div data-my-block="my-element my-modifier">
It's still shorter, more readable and avoids repetitions. The difference becomes even more pronounced as the complexity of your styling goes up.To be fair, I don't know the latest performance figures on attribute selection.
The problem with this argument is that just a few years ago everything ReactJS does now was the wrong way, but for some reason today, it's the best way. So which is it?
It seems to me, that unless it's broken, or goes against an explicit rule from the browser/code designers of html/css etc... it's totally valid. And if it doesn't "feel correct", then maybe it's a trend waiting to happen. <sigh>inline javascript styles</sigh>
As far as I know right now, custom attributes goes against the html standard.
Inline styles for one... created with javascript no less. This was totally verboten not long ago. But then became a "best practice" only because React started doing it.
<div class="block">
<div class="block__item">
I am an item
</div>
<div class="block__item block__item--danger">
I am a dangerous item
</div>
</div>
Your simplification relies on an HTML hack and can potentially be more confusing when modifier names start colliding.https://www.smashingmagazine.com/2013/08/semantic-css-with-i...
This helped me deliver maintainable and reliable stylesheets that other devs like.
Most importantly using only class names you will gain :
1. Your HTML / CSS is easier to maintain.
2. Is faster to develop.
3. Is faster to refactor.
4. Is more portable.
5. Is faster for the browser.
I even wrote a blog post about it [1].
I actually prefer nice semantic HTML but I admit that's not always possible.
I wouldn't recommend long and complex names either, but with something like tachyons, they're not long or complex, there are simply long lists of orthogonal classes.
This is the go-to article I send to newbies struggling with z-index. https://hackernoon.com/my-approach-to-using-z-index-eca67feb...
So much more logical to write and maintain than plain CSS for a site of any reasonable complexity. You can still apply global styles where they make sense (e.g. styling all body text) and making use of JavaScript patterns for reusing common chunks of styling feels much more robust than the CSS equivalent.
As someone who can do CSS but doesn’t do so that often, I can confirm that it’s getting better with things like Flexbox and Grid, but it is still full of maddening quirks! I do have a lot of respect for people who are CSS experts.
That’s my only real sticking point here.
Vertical height of text comes up a lot in CSS. It seems many people do not make liberal enough use of the line-height property. Line height is a magic bullet when you need some text to take up the correct amount of space.
With it, and your element styled to inline-block, of float left, or however you are using it. You are able to modify the font size later on without having any effect on the element's surroundings. It's also automatically centered vertically and you can change it without much effort.
Not enough people using line-height.
SASS, BAM, OOCSS, etc are all good practices. You can/will abuse those as well without proper understanding and planning.
I've seen far too many bad languages come and go to believe that anymore. People used to say that about PHP and VB6 too.
PHP eventually stopped with the "only bad programmers use me badly" and grew up, and VB6 died. A win in both cases.
CSS could be far better; and bad practices won't disappear until doing it the right way is easier than doing it the wrong way.
-tooling is hard e.g. to detect unused styles
-scope problems are a pain to debug
-some constructs are awkward (e.g. tooltips with :before and positioning styles)
-vertical text behaves unexpected wrt box model
-variables are suboptimal
There are probably a lot more.
2. Sure, I understand the discipline necessary to prevent that is difficult in a team setting. I would say that likely at reaching that point something went wrong before then. That seems a tooling problem, not a language problem. How many languages provide features out-of-the-box that assist with discovering unused code without some form of third-party exploring that space?
3. In what way? What scope problems? I can think of several bad practices that lead to scope problems. I can think of examples of two people following two different best practices creating such problems.
4. I know it's not exactly a fair thing to say, but I don't have those issues with the example you provide. I'm sure there's plenty of examples of showing how to do it.
5. Flex. But, granted, old practices didn't handle vertical issues well because originally HTML wasn't intended for that.
6. In what way? Do you mean variables from a pre-processor or CSS custom properties?
I'm sure there are more, just like almost all languages have their quirks and issues that make it difficult to address if not understood. Do you have any other examples that we could discuss? Are there any specific use cases where you are having difficulties that I might be able to help out with?
$some-element-z-index: 1 + max(
$other-element-z-index,
$should-be-below-z-index
);
This way the relationship between elements that should be below or above is more explicit.In my head, it works a bit like a spreadsheet. You update the z-index value of an element somewhere below, and all the z-indexes that depend on it update their value too.
One of the things I've been doing recently that helps debugging a lot is to group the attributes within a css class into three, separated by a line.
1) Things that control where the item shows up.
2) Things that control the overall appearance of the item, like size and background.
3) Things that control appearance of things within the item, like font color.
[data-controller="user_controller"][data-action="new"] {
.header {
background-color: blue;
}
}
This method keeps you organized and completely removes the biggest problem most people have with CSS.see https://docs.google.com/presentation/d/1r7QXGYOLCh4fcUq0jDdD...
and
https://medium.com/@asyncmax/the-right-way-to-bundle-your-as...
This stems from the fact that I have inherited the task of re-writing a very large (at least for one person, I feel) task of re-writing an internal framework that is really gotten too burdensome to add anything to. I have been contemplating instead of trying to get crazy with a ton of different selectors where you end up with a pattern like this:
``` .selector .selector1 li, .selector .selector1 li a { some-style: some-value; } ```
Which I find A little crazy compared to something like this:
``` [class*="selector selector1] { some-style: some value; } ```
or my person favorite:
``` ul > li > a { color: blue; } ```
While these examples are I think very simplistic (I don't wanna junk up the page with tons of stuff) I've met alot of resistance to this idea, and splunking other frameworks for inspiration (Shout out to https://bulma.io ! I like their work, its really wonderful FYI.) I don't see alot of this.
While the resistance I've met from others who have some input on this project (and rightly so, they're also part of our stellar in house design team) seem to resist this idea, but haven't articulated why.
I was wondering if it comes down to: is there actually an issue with using these heavily vs some of the older semantics or, which I feel is quite possible, people have done things for so long one way its hard to see another way?
While its seemingly less verbose sometimes (not always) I feel like the new selectors give you a way to combat specificity problems by controlling when context around your classes is better than having ton of different classes and nesting those over and over.
Am i missing something?
ul > li > a
.selector .selector1 li a
The system will check ALL anchor tags and move left until it has completely matched the rule. This makes the above selectors extremely expensive (perfomance-wise) and can cause slow page-loads. Tag selectors and universal selectors are generally a bad idea for this same reason.And using inheritance to select elements indirectly is actually better for performance than directly selecting the inheriting elements. For example, if you want to set typography for an entire page, instead of selecting all p and h1 tags, just select the body tag.
Here's a link to a more in-depth explanation of all of the above with examples:
You could probably construct situations with very deep selector relationships and thousands of matching elements where the ancestor walking adds up but the other costs of computing style often end up being more important than the selector matching in many scenarios. Not that you shouldn't aim for simpler selectors of course.
(Usually the problems with expensive selectors come from side effects of dynamic changes to the document. ex. Some selectors will make the browser recompute hundreds of elements when only changing something small because the browser isn't tracking that relationship in a precise way.)
Please don't. Tags have semantics for important reasons. Even if you're removing one of those by overriding the default styles, the semantics of tags are still used e.g. for screenreaders. It's not a mistake that HTML 5 added a ton of new semantic tags like <article>, <header>, <footer>, <aside> and <nav>.
If my understanding is correct, the argument is to use
<style>
.my-article {
background-color: fuchsia;
}
</style>
<article class="my-article">...</article>
rather than <style>
article {
background-color: fuchsia;
}
</style>
<article>...</article>This is an odd statement, considering that the C is CSS is "cascading". Maybe it's a perspective issue, because I really enjoy the cascading part of CSS. Don't you think it's better that we _don't_ have to set a color for each element/child element individually?
[1]https://hacks.mozilla.org/2017/08/inside-a-super-fast-css-en...
I don't think a solid argument can be made that because the CSS rendering engine is complicated that it's not performant. Chrome documentation says it's possible to target 60fps with CSS/JS animations.[0]
[0] https://developers.google.com/web/fundamentals/performance/r...
EDIT: Perhaps by implementing the whole browser in WASM? Would that even make sense?
Modular CSS I think is a huge step forward - I totally agree that global scope is killer. As much as people love to hate on CSS-in-JS, I enjoy using styled-components a lot. I've noticed since using it I've been composing CSS instead of inheriting or overriding styles.
This is just the nature of web development - almost every tech in the stack is flawed, but tools and best practices make some sanity out of the chaos.
.rabbit.drivingACar.onIce.withRocketBoosters {
color: pink; /* pretty specific */
}If you're talking about the difference of:
.a { ... } .b { ... } .ab { ... }
<div class="a b"></div> <div class="ab"></div>
It's probably not going to matter a lot. It's two map lookups vs one, and some associated overhead. Probably better to optimize elsewhere. :)
Instead of quitting front-end dev, you might also consider using https://www.styled-components.com/
Styled-components solves all of the issues the author highlights in his writeup.
Bonus: styled-components works both on web and on React Native! So you don't have to throw out all your stuff to participate in the post-browser future...
Isn't that the case with Javascript?
Object internal state is generally true, if you're using prototypal objects. Use function closures when you need to internalize something.
Speaking of which, anything hidden via closure is truly hidden and can't be overridden. Anything public can be overridden, excepting objects frozen via the ES5 spec's Object.freeze method.
I generally enjoy most aspects of JS, and generally detest working with CSS, in large part because I delegate interacting with many browser APIs to JS libraries, whereas there's no escaping the awfulness that is years of backwards compatibility with incompatible vendor specs and CSS implementations.
I suspect I'll be happier once CSS Grids are universally supported (properly) among all of the browsers I need to target, and use postcss for the rest.
z-index: 2147483647;z-index: 2147483648 !important;
Maybe I am stupid here, but doesn't SCSS just render out to CSS?
Saying scss is pointless because it outputs regular css is the same as saying that es6 is pointless because it can be represented as regular es5.
Let's be reasonable, that's more like comparing CSS 2 to CSS 3. :P
Everything I've read and discussed with others in the industry, SCSS seems to be useful for far large projects with many designers and programmers stepping over eachother. I work for a smaller company, and we build very clean CSS, and would not benefit at all from SCSS.
Recently a friend at another medium sized company switched to SCSS "because". Seriously, it was one designer that liked one feature from some library that required some build tool that required SCSS... So they all have to learn SCSS now. No other reason.
This list also assumes the use of a CSS pre-processor.
I have learned to simply accept css for what it is: An unholy legacy kludge we have to live with. Just don't let's speculate on the millions and millions of wasted man-hours over the years.
Welcome to css-tricks.com [0], where you will learn all the current trends. I am a regular reader for things just like this. Where you just want to know what's new and where things are going. Plus they have an amazing stockpile of CSS examples.
Second only to Mozilla Developer Network. [1]