CSS is hard no matter how good you are at it
aha.ioThe problem is caused by vertical-align. The default value for vertical-align is `baseline`. The flexbox container inside pill2 "hides" the inner text, so pill2 is treated as having no text at all (for layout purposes). This is treated the same as a font having zero height. So the browser aligns a 16px font (or whatever) with a 0px font, and it comes out misaligned.
My two solutions in order of preference would be:
1. Get rid of the inner flexbox, so the layout engine can see through to the text inside and use its baseline for alignment. Rely on the pill's parent for vertical alignment. I don't think I've ever come across a double-nested <span><span></span></span> (or div) that couldn't be flattened out.
2. Give .pill `vertical-align: middle` so its baseline is not used for alignment at all--but then the other non-pill items will need `vertical-align: middle` too in order to line up.
The author went the opposite way as #1 and added dummy text inside the pill, but outside the flex. Since the text is not inside a block element, the layout engine can see it. It has the same font size as the outer text, so with `vertical-align: baseline` both baselines line up again.
I want to thank both you and the original article author for bringing this issue up, because until this article and this comment I never really understood the use of the vertical-align CSS property. This is the perfect example to illustrate the actual use of this property. Playing around with the author's codepen and seeing how it changed when I added the vertical-align CSS property really unlocked something for me.
I'm glad I could help!
> The flexbox container inside pill2 "hides" the inner text, so pill2 is treated as having no text at all (for layout purposes). This is treated the same as a font having zero height. So the browser aligns a 16px font (or whatever) with a 0px font, and it comes out misaligned.
That’s actually not what happened.
Images have their baseline at the bottom edge, so Pill 1 is simply aligning its text to the bottom of the <img> element which is the first item in Pill 2.
You can confirm this behavior by enlarging the image, you can see Pill 1 shifting vertically to align the text baseline to the new bottom edge of the image.
You can also swap the order of the <img> and <span>Pill 2</span> so that the text goes first. You will see that they will now align despite the fact that the inner text is supposed to be “hidden”.
An easy way to solve this is to give .wrapper a hidden pseudo element with a bit of text so it becomes the first item to be used for alignment.
.wrapper:before { content:"a"; width:0; visibility:hidden; }
You can also just set the image as background to avoid the <img> element altogether.
`vertical-align: middle` would also likely give them better preferred alignment the first time a "pill" thinks it needs a second line of text and sometimes when too many pills exist for available width and the container starts to spill to another row.
I understand why `vertical-align: baseline` is the default, but it seems like such a bad default for all the ways designers like to (over) use Flexbox. (Especially because designers really love the baseline alignment at first but often can't seem to think two and four dimensionally enough when designs meet real world constraints and user data and need to expand/contract/shift to available space.)
But flexbox is much newer than vertical-align.
As I said, I know why it is the default. There's nothing that would have stopped `display: flex` having a different default `vertical-align` given `vertical-align` original use cases mostly didn't overlap anyway, but that's obviously armchair quarterbacking and we have the CSS we have.
That's a much better solution than altering the content.
This is correct. See here for some more examples:
- https://codepen.io/romellem/pen/rNZbPZQ
TL;DR: whenever you are using `display: inline-block`, be aware of `vertical-aign` issues that may crop up.
CSS has been hard. Modern CSS is amazingly more straightforward. As others have pointed out here, this is a problem of trying to apply new functionality (and more modern CSS) to a legacy codebase. Obviously there will be weird issues there. If the entire parent component had been designed from top to bottom using flex/grid mechanics, I doubt a problem like this would have presented itself after the fact.
You are absolutely right here. This problem arose because the surrounding code was much older. I would have liked to reformat it, but that would have been a huge lift compared to what I was trying to achieve.
That's been my experience as well... Any CSS issue that was not immediately obvious, in my experience, has always had more to do with insane markup and javascript than with css.
Just to give a fictitious example (inspired by real events)... I wouldn't really expect a Label react component to be made up of 26 nested html elements, 80% of which coming from third party libraries. Stuff like that can be hard to style, but that's just because there's inappropriate abstractions involved.
"Modern CSS" defined as ... what, precisely?
Keyword searches seem to pull up an unstructured soup of random essays and 'tips-n-tricks' type articles.
To me, that means flexbox and grids. All of my past css formatting issues are solved with those two things.
It's always been a time sink for me. Besides using frameworks, does anyone have any suggestions on how to develop a css file without getting into the neverending hacks that require hacks to fix the hacks?
I have yet to see a long term css file that's not a birds nest of fixes.
Sass helps with reusable code. Bootstrap and standards also help a bit. Bulma, PureCSS, Tailwind (Pro), Concise. These provide a good starting point.
My issue is, ever since 1997, the spec hasn’t really lent itself to composability. Just a list of definitions. Maybe some shareable effects but not idempotent in and of themselves. From tables, to floating divs, to grid 960, to flexbox. We keep inventing new ways of dealing with the shortcomings of the standard.
Inheritance and composition should be paramount. Sass and Less help but still fall short of the ideal state.
When I encounter a “this isn’t lining up” problem. I think about the composition first before I think about styling. Should I put it in a div and give that the right properties that I can then shim a span in there with the rest of the required style? Or do I need a new style definition to achieve the look. I try to avoid creating new styles if I can.
> inheritance and composition
Also known as the C in ”Cascading Style Sheets”. Embrace the cascade instead of having every class fully represent a component-piece.
I know what the C stands for, while it does cascade, it’s not inheritance and it always lends itself to “why does this have this style? I didn’t want that!”.
It's absolutely inheritance.
.animal { font-weight: bold; } .animal.bear { color: brown; } <div class="animal bear">...</div>
There is no one silver bullet to clean up CSS. I think much of the problem is that many developers think of clean CSS as an afterthought. The same level of care and organization should be applied to CSS as you would apply to the rest of your code.
I have found that there are many tools out there:
- Design systems - Web components - Frameworks - Naming conventions - Higher level transpiled languages
Tools are meant to be used together, not in isolation. Any sufficiently complex app or page can benefit from mixing and matching these tools. At Aha! we have a design system, we use the BEM naming convention, we leverage web components, utility classes, and LESS.
With so many tools you can definitely get yourself into a mess unless you apply each one appropriately. That is where experience, discipline, and good old fashioned code reviews will help prevent a CSS nightmare file.
It depends on what you're building. For a simple website that only uses a few tags, a single file should be enough, but that's no challenge.
I think the problem starts with the phrase "a css file". If you're building something complex, you gotta bite the bullet and split things up.
If you're building apps, one way is doing self-contained components, which share absolutely nothing between them. You can have a "base CSS" for the page that is very small, just a few lines (plus maybe a reset), but the rest of the style should be "owned" by components and scoped to them using whatever methodology you choose.
Another possibility is going the completely opposite direction and having only helper classes (like Tailwind) that know absolutely nothing about the elements they're styling.
I don't believe in a middle ground. A lot of messes start by having a single CSS block that is responsible for styling 20 different parts of the screen with a very complex CSS selector targeting multiple components. This is like a COMEFROM in INTERCAL. I don't think you can't have your cake and eat it, except in toy projects. If you want to have a single-source-of-truth for colors, you gotta use variables (SASS, native CSS variables, etc), or a helper class.
Working with frameworks also requires care when you want to slightly change the default appearance, and a lot of people abuse !important for this. You don't have to do it.
To avoid !important when you want to "extend" some third-party CSS, use specificity rather than !important. Have a root class, for example .my-app-with-custom-style (it can be applied to the body tag or some other root element), and then extend each block of the framework using the root class + the same selector. For example, if your framework is .b-button.main-btn and you want to extend it, extend it with .my-app-with-custom-style .b-button.main-btn. This way specificity wins and you don't need !important.
Tailwind * tries * to resolve this, but is probably just a different bird's nest.
Frameworks are going from the frying pan to the fire.
There are numerous React frameworks that supply a "widget set" either of web page elements, UI controls or both that work out of the box.
If you (your client, your employer, ...) like the way these look out of the box they are a great way to build something quickly and not have to understand the underlying CSS.
If you are required to customize the look, however, or you want to combine components from various sources and have them look consistent you have to understand all the concepts of the frameworks, the frameworks the frameworks depend on and CSS.
The problem with Tailwind is that it is write-once code. You don't get any leverage.
Ideally as you write more code you get more reuse and writing future code becomes faster. In theory CSS classes act like this too. As you have more (well designed) CSS classes then writing future CSS becomes easier because you can reuse the past work.
Tailwind breaks this benefit that by encouraging you to write your CSS with glorified styles.
Well, tailwind itself is very reusable, probably the epitome of reusable CSS.
And code that uses Tailwind is reusable as long as you use components (React, Vue, Svelte, WebComponents, CSS components with @apply, it doesn't matter). Those are reusable.
Trying to use Tailwind without components, in a CSS-garden style page for example, is just trying to fit a square peg in a round hole. It's just the wrong tool for it.
Tailwind is a bird's nest in one place instead of a bird's nest in two places with hidden abstractions.
Are you building sites or apps? I got to the point a long time ago where I just didn't care to cover every single design need and just made my own mini-framework.
Ask ChatGPT to do it for you. It does surprisingly well.
Some people prefer to actually understand the code they deploy.
The code it generates is pretty understandable
For what it's worth, this is a relatively easy problem to solve with flexbox these days.
My advice for those who constantly complain about CSS: give it the respect you give things like programming languages or SQL. You're not just understanding a text file, you're understanding the way browsers interpret and render styles. You probably need an expert to "just do what you want to do", and if you don't have an expert available you'll need to make do. Just like anything other sufficiently complex development problem.
Flexbox is way better than what there was before, I'll grant that. Grid too.
> I work with a team that has accumulated a lot of CSS experience. Generally I don't think there is anything they cannot handle. So I jumped on a couple calls to work through the issue. They were just as confused as I was. The page in question had a lot of moving parts.
Not to say CSS isn't hard, but they encountered a pretty basic problem in CSS. I'm baffled that it took them multiple meetings to come to such a janky fix. Why they couldn't just open up DevTools and troubleshoot it?
In my experience, front-end engineering has gotten so easy the last few years that there's now a whole generation of quite adept programmers that also manage to have very limited understanding of basic webdev concepts. I can't tell you how many times I have been told "we don't need a CSS framework - we're using React!" only to be dragged into a meeting to explain to how to clear floats.
All this to say, if you are doing any amount of front-end engineering, it probably pays to have some old-timer who remembers how to build webapps before flexbox was a thing.
Ouch. I just realised I am old.
EDIT See comment above, mostly related to vertical-align things, not flexbox. The author's point still stands: CSS is hard.
Smells like baseline / vertical alignment issues. The author hones in at the end,
<div style="display: inline-block">
<span style="display: flex; align-items: center">
<span><!-- empty --></span>
<span>Text</span>
</span>
</div>
The presences of that empty tag is what changes the alignment. This also requires the flex container to be wrapper in an inline-block element, and for the flex container to align-items: center.The flexbox [spec][1] talks about how flex items become "blockified," but I don't see anything that indicates empty children which demonstrate this weird behavior.
I have NEVER been able to understand why it took so long for css to get to grid.
2d layout was something that was “solved” by desktop application layout mechanisms like 25+ years ago. One would have thought given all the attention paid to web apps at the dawn of “web 2.0” that this problem would have been licked fast, but no, instead we had 15 years of floundering with floats and the proliferation of turgid frameworks like bootstrap to get by.
So much confusion and suffering just because there was no will power to straighten this stuff out into something sane and cogent.
What do you mean by "get to grid"? Have people come back around to the idea that tables weren't so bad?
I enjoyed web development quite a lot in the '90s, but after CSS came along it was an endless source of frustration, and I gave up on it all.
> Have people come back around to the idea that tables weren't so bad?
In a way, yes, I think so.
Grid has similarities to tables. These similarities are not accidental. They're a consequence of thinking about layout in terms of rows and columns. I recall back in the day the purists always decreed that tables were supposed to be semantic construct and not used for layout. Pragmatists just ignored that advice for years because there was nothing but a lot of obstacles if you went "table-less" for layout.
But yeah, tables "just worked" for layout despite what purists said. Now we have "grid" and it's part of css, so it's "OK" now for the purists.
I remember this comic strip from 2005, it captured the essence of the situation perfectly: https://web.archive.org/web/20100125163107/http://okcancel.c...
What language/tech should CSS have looked to?
Take your pick. Java Swing GridBagLayout? Anything that allows you to reason cogently about layout in terms of rows and columns?
I just wonder what CSS implementers were thinking when people started replacing desktop application with web-apps and they could see that application developers could zip through a dialog layout in minutes whereas the same thing in a browser would take endless effort and workarounds.
I’d go further. Declarative programming is deceptively hard. It only seems easier at first
I never enjoyed making graphs with most APIs whether it was products like Matlab and IDL or xvgr, not to mention pyplot, highcharts, etc.
I was initially apprehensive about making plots with D3.js but once I got over the hump it was such a breath of fresh air. Instead of the usual second guessing, head scratching and "you can't get here from there" it was just... easy! Want bar charts by date where the weekends are a different color... easy!
I'd also point out the endless complaining people have about things like puppet, terraform, kube files and such. I always found that building out cloud infrastructure with Java or Python programs that use the AWS/Azure API and write bash scripts that boot up on the machines or Python programs was... Easy! Often I'd have complicated systems up-and-running and checked into git while people were still fighting with leaking abstractions with various value-subtracting tools that never absolve you from knowing bash.
What you need is a system that implements some Grammar of Graphics (Leland Wilkinson, 2000; sometimes abbreviated "GoG" or "GG"), like ggplot2, vega, or something like that. Although I'm a bit weary of systems that have "their own version" of Wilkinson's GoG but that don't necessarily have the same rigorous correctness proofs.
D3 is awesome but not trivial: I consider it a more low-level tool than any GoG.
no fan of ggplot2, one of the few ‘higher level’ libs i do like is seaborne as it has some actual wisdom baked in.
i think one d3.js succeeds so well because it treats scales as functions, which is what they really are. as such it gives you the tools to implement ‘grammar of graphics’. Also it is giving you full control of the attributes of svg and other objects and puts them at the service of the GoG viewpoint as opposed to offering you declarative access to an impoverished and inevitably flawed toolbox.
Yeah, even though GoG was designed and is usually (or always? is there a counter-example?) implemented with the OOP paradigm, I've always found that its principles were better abstracted with functions and combinators. It's a product of its time.
I believe the original GoG theory/specification does not suffer from the same impoverishment and flaws, it having been formally proven and all... But the same cannot be said of any particular implementation "merely inspired by it". I would very much like to implement it in a pure FP language like haskell (and/or perhaps even proven correct with Coq) some day, that would be fun.
Myself, I think ggplot2 is one of the more solid and least-crippled implementations. Unfortunately, my distaste for the R language is such that I hope I never have to use it again.
No, these days CSS is super easy.
It makes fairly complex things very easy. One does not have to be a graphics programmer or good at math to perform very powerful operations.
I agree. The problem that people are having is that CSS has completely changed within the past 5 years, especially positioning, as all of the modules have been implemented. Things that once involved elaborate and fragile rituals to perform are now trivial to write. If you knew CSS 10 years ago and never went back to survey it since, you don't really know CSS now.
I finally stopped claiming I knew CSS until I took a few months to work through a top to bottom overview. None of the skills I had 10 years ago apply to modern CSS; I had to give up every habit. This is a good time to do a review, because CSS development seems to have slowed down considerably, and browsers are all pretty good.
However, CSS has become very big, and web development teams should really start respecting it as a specialty in itself, and making sure they have at least one person who knows it all. Even if they use tailwind.
https://www.w3.org/TR/css-flexbox-1/#flex-baselines
first/last main-axis baseline set
When the inline axis of the flex container matches its main axis, its baselines are determined as follows:
[...]
Otherwise, if the flex container has at least one flex item, the flex container’s first/last main-axis baseline set is generated from the alignment baseline of the startmost/endmost flex item. [...]
It uses the first flex item (the first child controlled by the flexbox) to set the baseline for the flexbox.(for https://codepen.io/jsteel64/pen/rNrPYNQ) The first img sets the baseline for the .wrapper, which sets it for the pill, and then two pills are aligned according to their baselines.
And yes, anything that has to do with baselines is a source of pain and bugs, it sucks.
I agree with exactly one sentence in the comment thread so far. "CSS requires experience"
If you're interested in actually learning css, and don't just want to complain about how poorly written css is bad, this website is invaluable and provides concrete examples of how to build layouts and style elements.
I agree, but I'd even say that becoming pro at CSS is orders of magnitude easier than becoming pro at your average programming language. I think engineers just tend to not take the task seriously, and don't apply themselves enough.
You just need to actually understand the box object model and a handful of common gotchas.
CSS is essentially declarative programming for several different black-box systems.
How selectors are written has exponential performance implications. Using the wrong type of transform might make your page forego hardware acceleration, or use more battery on a mobile device.
At least typical programming languages are targeting a known set of environments and devices with known characteristics.
You definitely need to understand a lot more than the box model and a handful of gotchas.
Update: done.
https://codepen.io/mikemaccana/pen/WNgWmdY
Please note I didn't bother extracting the icons out the design (it's 11PM and I should be in bed) so just used emojis.
Choices:
- Box model border-box (padding inside box, most CSS resets do this for you now)
- Grid by default because we don't need flexing.
- No margins, at all. Space between items and their parents are determined by padding, space between items and their siblings are determined by 'gap'.
- No issues with vertical centering because items are aligned for the middle.
- CSS borders on the right of each grid each cell for the lines. We'd need a lot less HTML/CSS if the lines were removed BTW since many of items inside the cell have their own borders, part of good design is knowing the costs of your designs.
- Took 36 mins.
Original: F*k it. Live coding. Come watch: https://codepen.io/mikemaccana/pen/WNgWmdY
The "trick" is to employ progressive enhancements.
Also, the less CSS, the better. + no shame in using !important sometimes.
All in all, CSS requires experience
!important is like global variables
Sure they have their place, but goddamnit do some people abuse them and make huge messes with them :(
Better off repeating classnames (.foo.foo { color: pink; }) than using !important.
For me grid and flexbox brought balance to the force. Master these and CSS will feel far more accessible.
The fix in the article seems very strange to me. For something like a horizontal list of tags/labels, you would want to use flexbox. Using `display: inline-block` and the default text spacing creates an inconsistent layout.
Based on some of the comments here, it seems like the browser 'defaults' for styles are not what you would intuit.
This is something I run into with flexbox. The tool is great, but the default mode is almost never what I want.
What worked for me is tailwind + making almost everything display:flex/grid.
Not a fan of tailwind but grid by default is indeed a good basis for a design system and solves this easily.
Grid isn't a good solution for this particular problem because the layout depends on the intrinsic size of the content. Grid is good for when you want the container to define the layout and have the content flow into it. Flex is good for when you want the layout to adapt to the content, like in this case.
Flex is good when you need flexing. A single row grid, with variable width cells is simpler.
See elsewhere in the thread where I solved this and / or submit a fork your own version using flex.
Here's a fork.
https://codepen.io/mrdanimal/pen/VwGNOQw?editors=0100
As far as I could tell there was no visual change. It's not drastically different, but once you make everything flex, it's possible to remove a few of the grid properties.
(Off topic: can you please remove my name from your fork? Thanks)
Sure. I’m AFK but I’ll get it in a little while.
I've never had much trouble with CSS but I also just never try to use the cascading aspect of it outside of fonts. I just write either CSS directly onto components I'm using or I just use tailwind.
Not sure why you are getting downvote. I absolutely agree - avoid the C in CSS and your life will be much better.
Definitely. I love using BEM when you have to nest things. If you stick to strict BEM, you will generally avoid having nesting more than 1 or 2 levels deep.
CSS is terrible for positioning elements and unnecessarily complicated for a problem that is actually not that hard. If only browsers provided alternatives to the classic dom.
I always thought there should be some kind of visual effects database.
I'm looking to do X with Y tool. here are all the things it could be called in other tools. and/or layman terms.
The problem this article is pointing out is that you could have that database and still struggle.
It is easy to make a database of effects that work in isolation, for that matter GPT-4 will do a great job writing CSS to make isolated visual effects.
It’s when you need to put those visual effects to work inside of a real application or web site that you find it doesn’t work or rather almost works because of very fine details in how HTML and CSS work. Often you find you can move something forcibly a few pixels this way or that way but what works for the personal web site will be a nightmare for a complicated application that has multiple parts that multiple people are working on because you’ll need more hacks to deal with the consequences of those hacks.
It is real, global, and comprehensive understanding we need, not just another cheat sheet.
What tactics do employ when a CSS issue has stumped you?
`position: relative`, `overflow: hidden`, `height: 100%`, `line-height: 1` and `margin: 0` solve a surprising percentage of CSS issues in my experience.
!important generally.
The retort is that you should rely on specificity, by way of selectors.
Good thing that we have the specificity checker, that makes sure that the semantics of your application of styles remain sound if you change the structure of the document in ways that could affect the selectors!
Oh wait, there's no such thing, bummer.
Here's a quick fix: use `align-items: baseline` instead of center in your wrapper class
No, this is terrible CSS. grid handles this perfectly.
I have a hard time differentiating when I should use grid and when I should use flex-box. In this case, setting up a grid seems like overkill.
Seems more like a browser default issue than a tool choice issue.
If you need flexing, use flex box.
You probably don't need flexing.
/If you don't know what flexing is/, you should use grid.
line-height ?