Joshua's Docs - CSS and General Styling - Cheatsheet, Common Issues, Etc.
Light

Resources

UI Inspiration

Different frameworks and tools

Cheatsheet


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)

Here are some great resources on using CSS properties in this way (and more advanced) ways:

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

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 like abcd .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>

🔗 CodePen Demo


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 the value 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>

🔗 CodePen Demo


Grid Layout

Some tools:

Flexbox Layout

Flexbox Tools

These are tools that help with building, debugging, and understanding flex based layouts.

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:

  • How to force a flex child to take up 100% height (of its parent)
    • For specific children: instead of height: 100% on children, replace with align-self: stretch
    • For all children, on the parent use align-items: stretch
    • More answers here
  • 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.
  • 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 or margin-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
  • How to make an isolated overflow child align to the left or right if it is the only item in a row?
  • Why are my images (<img>) getting squished inside flex?
    • Change image's align-self to center or start
    • See this SO
  • Text based element in flex is taking up too much width when wrapping text
    • Hard to get around
    • Setting width or inline-size to min-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 of calc()
    • Workaround: use transform: translate to offset

Boilerplate CSS

I've moved the content that was here to my snippets folder 👉 snippets/css

Markdown Source Last Updated:
Sun Jan 21 2024 03:43:33 GMT+0000 (Coordinated Universal Time)
Markdown Source Created:
Mon Aug 19 2019 17:06:24 GMT+0000 (Coordinated Universal Time)
© 2024 Joshua Tzucker, Built with Gatsby
Feedback