CSS
Cool stuff
- https://animista.net/ — CSS animation tool
- https://fffuel.co/pppalette/ — color palette generator
- https://picocss.com/ — good looking defaults for semantic HTML
- https://youtu.be/JCJUPJ_zDQ4 — Jake Archibald explains the Page Transitions API
Axioms, exceptions, and utilities
These ideas mostly come from Every Layout. I highly encourage you to purchase your own copy to gain context and go deeper!
- https://every-layout.dev/rudiments/composition/
- https://every-layout.dev/rudiments/global-and-local-styling/
- https://every-layout.dev/rudiments/axioms/
First, write axiomatic styles. What are the style rules that elements should follow by default? Use the unqualified * selector to apply those styles.
Second, add exceptions to the rule. It is important that the exceptions are still very much global in nature and not part of any specific context. For example, here is how we could set up the font-family rule in an axiomatic + exceptions way:
* {
font-family: var(--font-primary);
}
figcaption {
font-family: var(--font-sans);
}
pre,
code {
font-family: var(--font-code);
}DO NOT create context-specific exceptions. For example, maybe we want a different font size for h2 elements that are rendered within my sidebar. It's tempting to add a CSS rule like this:
/* don't do this */
.sidebar h2 {
font-size: var(--font-size-md);
}There is too much context here, and this approach won't scale. For granular, context-dependent exceptions like this, it's better to reach for utility classes.
.font-size\:md {
font-size: var(--font-size-md);
}<div class="sidebar">
<h2 class="font-size:md">The title</h2>
</div>This is a scalable approach to using utility classes.
NOTE
Systems that are utility-class-first tend to rely on applying many of those classes to most of the elements in the document. This is distinctly different from those systems because our utility classes declare exceptions, not the default look of each thing.
The role of CSS custom properties is to share values between your axiomatic + exceptions CSS rules and utility classes.
TIP
Seriously, there is so much more amazing guidance, examples, and practical knowledge at https://every-layout.dev. Go look at it and buy yourself a copy. I refer to it all the time.
The :has selector
https://ishadeed.com/article/css-has-parent-selector/
This selects elements that contains a match for the argument selector. E.g.
/*
* If a card component contains an image,
* set flex-direction to column
*/
.card:has(.card-image) {
flex-direction: column;
}Any CSS selector can go in the :has() argument.
TIP
I found a solution to the "Five star" coding challenge using only <input type="radio"> and CSS with the :has() selector. You can view that here: https://gist.github.com/awmottaz/ba7ecf8faa2f6bfb354d8e42cae281ab
Customizing list markers
Safari does not support the content property in the ::marker pseudoelement. https://bugs.webkit.org/show_bug.cgi?id=204163
Scroll snapping
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Scroll_Snap
TL;DR
.list {
scroll-snap-type: x mandatory;
/* sample list style:
display: flex;
overflow: auto;
*/
}
.list .item {
scroll-snap-align: start;
/* sample item style:
flex-shrink: 0;
*/
}Examples:
Break out of padding
Say you've got a card or other region with padding on it, and you want to include an element that breaks out of that padding to take the full width of the region.
Wrap it with a div that has negative margin equal to the padding size.
To make things easier to manage, you can use CSS custom properties.
<div class="card">
<!-- card contents -->
<div class="full-bleed">
<!-- full width things go here -->
</div>
</div>.card {
--card-padding: 20px;
padding: var(--card-padding);
/* other card styles here */
}
.full-bleed {
margin-left: calc(-1 * var(--card-padding, 0));
margin-right: calc(-1 * var(--card-padding, 0));
}
.full-bleed > * {
width: 100%;
}Responsive font sizing
Inspired by Miriam Suzanne's presentation: Responsive Type Doesn’t Have To Be Complicated.
Goal: respect the user's choices for how they've configured the default font size, and only deviate from that in a controlled way.
html {
/* This is my preferred font size as the "designer".
* The neat thing is, you can define this however you want,
* including changing its definition based on different
* breakpoints. Yes, pixel values are FINE in this case
* because we are not actually trampling over the user's
* configured preferences.
*/
--fs-pref: 18px + 1vi;
/* This now changes the definition of `rem`.
* The clamp function is used to contain our preferred
* font size within a reasonable range of the user's
* configured font size.
*/
font-size: clamp(1em, var(--fs-pref), 1.5em);
/* Choose whatever scale here you like. Maybe you even
* want this scale to change based on breakpoints.
*/
--fs-scale: 1.2;
/* These custom properties define our type scale. The
* naming convention comes from the <absolute-size>
* keywords in CSS.
* https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/absolute-size
*
* Note that the larger sizes fully apply the scale, but
* smaller sizes tend to shrink too fast, so we attenuate
* the scale effect on the smaller range.
*/
--xx-small: calc(1em * pow(var(--fs-scale), -1.8 ));
--x-small: calc(1em * pow(var(--fs-scale), -1.2 ));
--small: calc(1em * pow(var(--fs-scale), -0.6 ));
--medium: calc(1em * pow(var(--fs-scale), 0 ));
--large: calc(1em * pow(var(--fs-scale), 1 ));
--x-large: calc(1em * pow(var(--fs-scale), 2 ));
--xx-large: calc(1em * pow(var(--fs-scale), 3 ));
--xxx-large: calc(1em * pow(var(--fs-scale), 4 ));
}Don't use percentage-valued height
The default height: auto of block-level elements means that it will look at the height of its content to know what its own height should be.
Let's say you have this:
<div class="outer">
<div class="inner"></div>
<!-- possibly other elements here -->
</div>and you set .inner to have height: 100%. That 100% is calculated off of the height of .outer. But if .outer is using its default height: auto, then it will calculate that by measuring its descendent elements, including .inner. We have a circular dependency, and the result is that the height of .inner will be 0px — even if the calculated height of .outer is non-zero from the other descendent elements.
If you want to use a percentage-valued height, then .outer must have a definite height that does not depend on the height of its contents. That usually means either:
- Every ancestor element, up to and including
htmlandbody, is using a percentage-valuedheight - The ancestor element has a fixed height (e.g.
<N>px)
NOTE
It's worth noting that this is a simplification of how the layout algorithm works and there are many more factors to consider depending on what other styles are applied to .outer and .inner. The percentage-valued height is actually calculated off the element's containing block, which is not always the parent element.
You can play with this "height collapse" in this CodePen: https://codepen.io/tonymottaz/pen/raNmVwr
Automatic icons for external links
Credit to Chris Ferdinandi for this tip.
TIP
Make sure to replace example.com with your own site's URL.
[href^="http"]:not(.btn, :has(svg)):not(
[href^="https://example.com"],
[href^="http://localhost"]
)::after {
background-color: currentColor;
content: "" / "(external link)";
display: inline-block;
margin-block-end: -0.1ch;
margin-inline-start: 0.5ch;
mask: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5"/><path fill-rule="evenodd" d="M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0z"/></svg>');
mask-size: cover;
height: 0.8em;
width: 0.8em;
}