CSS next steps: flow, repel, blocks
A few minor, but significant changes to the site today. I say minor because the actual amount of code added will be pretty minimal in the end, but the impact is big.
As I rebuild the site, I am adhering to the CUBE CSS methodology, which hopefully means I’ll have less CSS in the end that does more. One of the keys to CUBE is Composition, which is the most exciting and fascinating to me. I saw the light of simple layout compositions years ago when I read Every Layout. I still reference it today and it has changed the way I look at and work on the web.
I’ve also been working my way through Andy Bell’s Complete CSS course lately, which furthers the lessons from Every Layout (Andy Bell co-authored Every Layout with Heydon Pickering) and promotes the principles of CUBE CSS.
These first few updates to the CSS stem directly from lessons learned from these two sources.
Flow
Andy says the flow layout utility is his favorite threes line of CSS and I’m inclined to agree.
.flow > * + * {
margin-block-start: var(--flow-space, 1em);
}
I firmly believe that individual elements or components should not apply margins to themselves, but rather their margins should be managed by composition utilities. This is what flow does: it creates the space around direct children elements. That’s all it does. That’s all it needs to do.
I took this a step further and added some CUBE CSS exceptions to create some easy-to-use semantic variations, like so:
.flow:where([data-flow="s"]) > * {
--flow-space: var(--space-s);
}
.flow:where([data-flow="m"]) > * {
--flow-space: var(--space-m);
}
.flow:where([data-flow="l"]) > * {
--flow-space: var(--space-l);
}
I define the --flow-space variable on the direct children instead of the .flow class itself because I was finding that if you have a .flow as a direct child of another .flow and they use different --flow-space values, then the child .flow will use the spacing of its children, not the parent.
I use the :where selector to target the correct exception without adding to the entire selector’s specificity. This allows me to keep the specificity at 0,1,0 instead of 0,2,0.
A small reset
Speaking of individual elements not adding their own margin, I added the tiniest of reset rules to my existing global selector:
*,
*::after,
*::before {
box-sizing: border-box;
margin: 0;
}
Begone margin, we don’t need you here.
Repel
Another layout utility I shamelessly stole from Andy Bell is repel. Again, dead simple in its definition but extremely useful.
.repel {
display: flex;
flex-wrap: wrap;
gap: var(--repel-gap, var(--gutter));
align-items: var(--repel-vertical-alignment, flex-start);
justify-content: space-between;
}
This pushes two elements away from each other. I use it in the header where the site title and navigation get pushed apart from each other. I also use the nowrap version to separate the post titles from their publish dates on the posts landing page.
Sticky footer
Not like position: sticky but rather a footer that is always at the bottom of the browser window, even if the page content doesn’t fill the full height of the window. I adapted this from a solution on MDN.
<html>
<head>
<!-- head stuff -->
</head>
<body>
<div class="wrapper center">
<!-- site content -->
</div>
<!-- site script bundle -->
<script></script>
</body>
</html>
.wrapper {
display: grid;
grid-template-rows: auto 1fr auto;
gap: var(--space-xl);
min-block-size: 100vh;
> :first-child {
margin-block-start: var(--gutter);
}
> :last-child {
margin-block-end: var(--gutter);
}
}
Since the div that is the wrapper is also the div where I use my center layout utility—which sets the box-sizing to content-box—I added the bits about :first-child and :last-child to create space at the top and bottom of the page. If I added margin or padding to the wrapper class itself, the footer would require scrolling on pages where the content doesn’t take up the full window height. This is because the min-block-size is set to 100vh to ensure the page takes up the height needed (and means I don’t have to set any block sizes on html or body).
Is this a Block or a Composition utility? It could go either way. It is specific to the context of the entire site layout, so I lean Block, since I can’t imagine how I would use this in other situations.
Odds and sods
A few other things I added were a no-shrink utility that sets flex-shrink to 0. This is useful when you don’t want things to get smushed in a repel or other flex layouts.
I also added weekday as an attribute to the local-time custom element, so I could add the weekday to the individual post pages.
And finally, I added the footer at the bottom of all pages (and the reason I added that wrapper block above) that includes the copyright and a way to get in contact with me. I also made everything a Creative Commons license because why not? Information should be freely available.