Resources
- troubleshooting cheatsheet
- NoDesign.dev (collection of "No Design" tools)
- Elevator pitches: CSS / Styling section
UI Inspiration
- Design Collections / Reference Sites:
- Great Twitter thread list - https://twitter.com/steveschoger/status/1215673997725196288
- https://render.com/
- https://stripe.com/
- https://fleetfn.com/
Different frameworks and tools
CSS Transitions
Syntax:
div {
transition: <property> <duration> <timing-function> <delay>;
}
Example:
div {
transition: all 2s ease 1s;
}
CSS Transitions - Multiple Properties Shorthand
To declare transitions for multiple properties, with shorthand, you have two main options:
1 - Comma separate shorthand declarations:
.myClass {
transition: width .5s linear, color .5s ease-in-out;
}
2 - Overrides
.myClass {
/* Unlike the above method, this requires all props to share common base settings */
transition: all .5s linear;
/* This overrides the `all` from above */
transition-property: width, color;
}
I found this out, thanks to this S/O answer
CSS Transitions - Common Things to Remember
- You can target
transform
as a property to transition- You can use this to build a scaling transition, for example
Extracting CSS
Chrome supports extracting all the CSS of an element via (Inspect Element) -> Right Click Node -> Copy -> Copy styles
. I believe this feature was dropped and then added back - full writeup here.
There are also browser extensions too, like Snappy Snippet, or visbug.
Globals, variables, and theming
Can I use: Browser Support. Note that IE 11 has no support. You can use something like webpack to transform the dynamic variables refs into static. See this. Or you can simply define the CSS value twice, first with a non-variable value that will be the fallback, and then with the variable. On older browsers it should just use the non-variable fallback.
CSS variables (more accurately custom properties) are notated with a double-dash prefix. You can declare them at multiple levels, and are inherited from parents (scoped).
CSS:
.vendorWidget {
--primaryColor: red;
--secondaryColor: blue;
}
.vendorButton {
background-color: var(--primaryColor);
}
HTML:
<div class="vendorWidget">
<button class="vendorButton">Click Me!</button>
</div>
To emulate theming, a common approach is to declare global variables at the highest level. In CSS, there is a trick you can use instead of guessing the element with the highest specificity: just target :root
. (MDN).
:root {
--loosePaddingPx: 14px;
--tightPaddingPx: 4px;
--primaryColor-light: #BEFFFF;
/* You can mix vars and non-vars */
background-color: red;
}
CSS Variable Fallbacks:
You can define a fallback in case a variable is undefined:
.myButton {
background-color: var(--myVar,red);
}
If you want to try a few different vars, and keep falling back if undefined, you can nest them:
.myButton {
background-color: var(--myVar,var(--secondaryVar,red));
}
Note that fallbacks are not the same as polyfilling or providing defaults for if a browser does not support custom properties. You can use a polyfill library to do that, or repeat each property twice, first with a default fallback, and then with the variable, like so:
.myButton {
background-color: red;
background-color: var(--myVar,var(--secondaryVar,red));
}
Using CSS Variables in RGBA Color Properties
The easiest way (currently) to define CSS variables in a way that supports using them with rbga()
, so you can easily tweak alpha, is to declare them like so:
.selector {
--cta: #1a83d6;
/** This is the RGB of the above HEX value */
--ctaRGB: 26, 131, 214;
}
By declaring a separate version of the variable that has the RGB values as comma separated integers, you can easily pull it into places with RGBA:
.selector button {
/** Use cta color, with half opacity */
background-color: rgba(var(--ctaRGB), 0.5);
}
📄 For a more detailed look into this approach, check out this blog post
Creating Derived Colors From CSS Variables
CSS lacks built-in functions to manipulate color objects (like darken()
in SASS), but with some creative variable declaration, you can get around this. By splitting up your color value into its separate parts (either H-S-L
or R-G-B
), you can later combine them and manipulate them with ease.
:root {
--primary: #e1e0ff;
}
Or, if you wanted to be able to manipulate this color in any way you want:
:root {
/** It is a good idea to still have the full HEX as a value */
--primary: #e1e0ff;
/** RGB */
--primary-r: 225;
--primary-g: 224;
--primary-b: 255;
/** HSL */
--primary-h: 242;
--primary-s: 100%;
--primary-l: 94%;
}
Now, coming up with some derived colors is much easier:
- Equivalent to SASS's
darken($primary, 20%)
--primary-dark-20: hsl(var(--primary-h), var(--primary-s), calc(var(--primary-l) - 20%));
- Equivalent to SASS's
lighten($primary, 5%)
--primary-light-20: hsl(var(--primary-h), var(--primary-s), calc(var(--primary-l) + 5%));
- Changing opacity on the fly (example: half opacity)
--primary-opaque-50: rgba(var(--primary-r), var(--primary-g), var(--primary-b), 0.5);
- See above section too: Using CSS Variables in RGBA Color Properties
Here are some great resources on using CSS properties in this way (and more advanced) ways:
- StackOverflow:
darken()
equivalent in CSS - Una: Calculating Color: Dynamic Color Theming with Pure CSS
- Amazing resource!
- Nielsen: Generating Shades of Color (this is specifically about rgba)
Theme Switching
There are a bunch of ways to accomplish this. I've written up a comprehensive exploration of options at https://joshuatz.com/posts/2019/coding-a-css-theme-switcher-a-multitude-of-web-dev-options/
Accessing CSS Variables with JavaScript:
/**
* === Setting Values ===
*/
// This will target :root variables
document.querySelector(':root').style.setProperty('--myColor', newColor);
// Or, same thing...
document.documentElement.style.setProperty('--myColor', newColor);
/**
* === Reading Values
*/
document.documentElement.style.getPropertyValue('--myColor');
// Or, for computed values
getComputedStyle(document.querySelector(':root')).getPropertyValue('--myColor')
Using Attribute Values in CSS
There is an interesting CSS function that can retrieve values from HTML attributes for use in CSS - the attr()
function.
Unfortunately, it is rather limited (can basically only retrieve content - e.g. strings), so in most cases you are better off just using Custom Properties / CSS Variables.
Sass/Scss
- Good cheatsheet - https://codepen.io/sasstantrum/post/intro-to-sass
Sass/Scss - Importing
For SASS/SCSS, you can import files with the @import
rule, which acts similarly to the CSS version. (or use @use
).
@import '../helpers.scss';
// Extension is not mandatory
@import '../colors';
CSS Modules
CSS Modules are a neat way of providing encapsulation / scoping within CSS, which is why they are embraced by a lot of component-driven frameworks, like Svelte, React, and Vue.
CSS Modules: Breaking Out of Closure with Global
The scoped nature of CSS modules makes them great for styling a component in isolation, but sometimes you actually want your styles to bleed through to child components. For example, you might need to style a child if it shows up within a specific type of parent. Or maybe you are designing a component that takes dynamic children via slots, and you need to force your styling to pass through without knowing the children in advance.
To breakout of component isolation, you can use the :global
modifier to "switch" to the global context:
.my-scoped-style {
/* If `.my-scoped-style` appears in a child nested through a slot, these rules will not apply */
}
:global(.deeply-nested-child) {
/* This will escape encapsulation */
}
This also works with SCSS/SASS, and multiple rules can be placed under a single :global
escape hatch:
.my-scoped-style {}
:global(.deeply-nested-child) {}
:global {
.deeply-nested-child-a {}
.deeply-nested-child-b {
button {
/* */
}
}
}
Targeting the first element of something
First child, regardless of type
Use the :first-child
pseudo class / selector.
.parent div:first-child {
border-top: 1px solid black;
}
.parent div {
border-bottom: 1px solid black;
}
The above code is nice because it gives both top and bottom borders to only the first element, and then bottom borders to the rest. If I added top and bottom to all children, then elements after the first would end up with double borders (the tops and bottoms would combine).
Note: this requires that the targeted element is a direct child of the parent selector
First child of certain element type
If we have a bunch of mixed element types, first-child
would both not make sense, and might not work too. Consider this kind of setup:
<div class="root">
<div class="abcd">
<div>I'm the first child of abcd</div>
<!-- Element below is my actual target -->
<span class="target">Hello</span>
</div>
<span class="target">World.</span>
<span class="target">How are you?</span>
</div>
<style>
.target {
background-color: blue;
}
</style>
Let's say I wanted to make the first <span>
have a red background, but all the rest have blue. I can't use first-child
, because it is not the first child of div.abcd
. I could use the nth-child()
rule, like .abcd .target:nth-child(2) { background-color: red; }
, but that would require that the order always stay the same. What if I add another div in front of it?
A better option is the newer :first-of-type
pseudo selector:
.abcd span:first-of-type {
background-color: red;
}
Note that by
type
, the rule is referring to the type of element, not a selector. If you try to do something likeabcd .target:first-of-type
, it won't work, because.target
is a class selector, not a type of element.
Targeting All Children AFTER the First Child
If you have a bunch of children and you want to apply styling to only those that come after the first one, you have a great use-case for the nth-child()
pseudo selector. For example, imagine that you have a unordered list, and you want to add a border between each list item. Border-top would work, but you would have to omit the first child - which you can do so with nth-child
:
<ul>
<li>First</li>
<li>Second</li>
<li>Third</li>
<li>Last!</li>
</ul>
<style>
/** This will target the 2nd child,
* and every +1 item after (`1n+2`)
*/
ul li:nth-child(1n+2) {
border-top: 3px dashed red;
}
</style>
CSS Selectors
Unusual / Fun selectors to remember
📄 MDN: CSS Selectors
Code | What | Notes |
---|---|---|
div[data-ref*="23"] |
Attribute Selector | See "Attribute Selector Cheatsheet" below |
:not(___) |
Negation pseudo-class selector | Use to select those elements not matching selector You can use multiple comma separated selectors, just like normal Cannot nest other pseudo-selectors |
:target |
Target pseudo-class selector | Targets the element that has an id value that matches the current URL fragment (hash value).Useful for animating elements that have just been "jumped" (or scrolled) to, adding sticky offsets, etc. Given a URL of example.com#faq , :target{} would match <section id="faq"></section> . |
:scope |
Scope pseudo-class selector | The main benefit to this comes when using JavaScript; when this selector is used inside of scoped queries (e.g. myElement.querySelector() ), the :scope part acts like the element your query is scoped to.This enables certain queries that were previously impossible; such as selecting direct children of an element while inside the scope: myElement.querySelector(':scope > *') |
:nth-child |
nth-child pseudo-class selector | Target elements based on their order / position among siblings, and/or offset. |
Attribute Selector Cheatsheet
Full list and details on MDN.
Short | Practical Example | What |
---|---|---|
{elem}[attr] or [attr] |
script[async] |
Matches any input that has attribute of id, regardless of value |
[attr="value"] |
script[src="https://www.google-analytics.com/analytics.js"] |
Attribute must exactly match value |
[attr*="value"] |
script[src*="analytics"] |
Attribute must contain value |
[attr~="value"] |
[data-color-scheme~="red"] |
Attribute must be a string of whitespace separated words, and the value must exactly match one of them. |
[attr^="value" |
script[src^="https://www.google-analytics.com"] |
Attribute must start with exact value |
[attr$="value"] |
script[src$="analytics.js"] |
Attribute must end with exact value |
[attr|="value"] |
script[src|="https://www.google"] |
A little odd. Attribute must either exactly match, or start with value and be immediately followed by hyphen (- ) |
Note: You can add
i
before the closing brackets to force a case insensitive match. Like:script[src*="gOOgle"i]
Note: You can target by any attribute, including
value
. This has led to a notable CSS-only hack (HN) that can listen for specific characters and report back to an external server (bad for password fields!). Note, this only works if JS is updating thevalue
attribute as the user types. In an "uncontrolled" field, this doesn't work.
Question: [attr~="val"]
vs [attr*="val"]
Since the description for [attr~="val"]
can be a little confusing, here is a very simple demonstration of its difference and the value it brings - a table of matches:
DOM | [animals*="cat"] |
[animals~="cat"] |
---|---|---|
<div animals="dog cattle bird"> |
Yes | No |
<div animals="dog cat bird"> |
Yes | Yes |
Calculations
As usual, CSS-Tricks has an excellent guide on the topic: "A Complete Guide to Calc in CSS"
Evaluating Calculations
There are a limited number of ways to evaluate a calc()
statement and read the result:
- Apply the rule, then use JavaScript's
getComputedStyle(MY_ELEMENT)
to read the result - Apply the rule, then use the browser DevTool's computed tab
Aspect Ratios
This is now very easy to do with the aspect-ratio
property:
.vidPreview {
aspect-ratio: 16 / 9;
}
This feature now has over > 90% browser support!
The old-school way to maintain an aspect ratio is with the padding-top approach. By specifying padding-top
with a percentage equal to height / width
:
<div class="container">
<div class="content">
<!-- Actual content -->
</div>
</div>
<style>
.container {
position: relative;
width: 100%;
padding-top: calc(9 / 16 * 100%); /** 16:9 aspect ratio */
/** You could also hard-code padding-top instead of using calc() */
}
.content {
position: absolute;
top: 0;
width: 100%;
height: 100%;
}
</style>
Grid Layout
Some tools:
- Grid WYSIWYG Builder (grid.layoutit.com)
- GRID: Visual Cheatsheet
- Yoksel: Grid Cheatsheet
- Video: Fireship - "CSS Grid in 100 seconds"
Flexbox Layout
Flexbox Tools
These are tools that help with building, debugging, and understanding flex based layouts.
- Comeau: An Interactive Guide to Flexbox
- Yoksel: Flex Cheatsheet
- Interactive continuous (one-page) cheatsheet
- FLEX: Visual Cheatsheet
- Flex Playgrounds:
Flexbox Use Cases
These are collections of use-cases showing how Flexbox can be used to solve common design problems
Aligning Items in Flex Layouts
Aligning single items in a flex layout can be a little confusing, especially since there is no justify-self
property. However, this StackOverflow response is one of the best summaries of different axis alignment scenarios and how to implement them.
Flexbox Frameworks
- Flexbox Grid
- Micro-framework, based on Flexbox, which provides handy 12-column system
Flex-Forgetful
Here are some things I'm jotting down, since I commonly forget them and need a quick reminder:
- Align items (tw:
items-*
) = CROSS AXIS - Justify items (tw:
justify-*
) = Primary axis - How to force a flex child to take up 100% height (of its parent)
- For specific children: instead of
height: 100%
on children, replace withalign-self: stretch
- For all children, on the parent use
align-items: stretch
- More answers here
- For specific children: instead of
- How to force a flex child to take up the remaining space?
- Set the child to
flex-grow: 1
- If are trying to take up the remaining space in the page, you will need to adjust the parent component to also fill the page - .e.g
height: 100vh
, etc.
- Set the child to
- How to add margins between items
- In general, there is nothing preventing the use of
margin
on flex children - If you need minimum spacing between items while allowing for easy column layout, you can try some of the approaches from this StackOverflow
- The OP actually has a good trick: negative margin on flex container to offset minimum margin of container items
- If you need vertical separation between flex children that have overflowed into a new row
- You can add
margin-top
ormargin-bottom
to all flex container items (e.g..row > * {}
) - You can use the negative margin trick from above to avoid adding too much extra height to your flex container
- You can add
- In general, there is nothing preventing the use of
- How to make an isolated overflow child align to the left or right if it is the only item in a row?
- This is actually somewhat hard to do with Flex, and some might consider it not possible with just Flex, while others might have workarounds that require fixed column widths, or there might be a hackish solution that works for you.
- An alternative is to use Grid instead of Flex
- Why are my images (
<img>
) getting squished inside flex?- Change image's
align-self
tocenter
orstart
- See this SO
- Change image's
- Text based element in flex is taking up too much width when wrapping text
- Hard to get around
- Setting
width
orinline-size
tomin-content
is kind-of a workaround for this
- How to combine with an offset with
margin: auto
for centering? E.g., to place an item 20% from top of container?- You can't use
auto
inside ofcalc()
- Workaround: use
transform: translate
to offset
- You can't use
Boilerplate CSS
I've moved the content that was here to my snippets folder 👉 snippets/css