AllThingsSmittyUpdated Jun 14, 2024

CSS architectures

OOCSS: Object Orientated CSS

The idea is to identify common visual patterns and extract duplicated code blocks into reusable classes.
  • Separation of structure from skin
  • Separation of containers and content
Bootstrap is one of the most widely used CSS frameworks that leverages this design principle.

BEM: Block, Element, Modifier

BEM is a model for breaking down components, their sub-elements, and their states into discrete parts. It provides a systematic naming convention that keeps all selectors flat (no descendant selectors). Each element that gets styled has its own class name.
BEM works well with popular CSS pre-processors like Sass, which use nested rules that compile down to flat CSS selectors.
.nav {
  // block styles
	&__link {
    element styles that depend on the parent block
		&--active { 
			// modifer styles

<form class="form form--theme-xmas form--simple">
  <input class="form__input" type="text" />
    class="form__submit form__submit--disabled"
    type="submit" />

.form { }
.form--theme-xmas { }
.form--simple { }
.form__input { }
.form__submit { }
.form__submit--disabled { }
Standalone entity that is meaningful on its own.
A part of a block that has no standalone meaning and is semantically tied to its block.
menu itemlist itemcheckbox captionheader title
A flag on a block or element. Use them to change appearance or behavior.
disabledhighlightedcheckedfixedsize bigcolor yellow

SMACSS: Scalable and Modular CSS

In practice, large single-file CSS files quickly become unmanageable and hard to debug. SMACSS was a guide to categorizing the different types of CSS and was compatible with approaches like OOCSS. The main idea was to take all these class names and organize them into separate buckets, providing much-needed structure to our CSS files. Additionally, it introduced conventions around naming classes.
Base, Layout, Modules, State, Theme
@use "sass:math";
/* Element Chalk Variables */

// Special comment for theme configurator
// type|skipAutoTransla∂tion|Category|Order
// skipAutoTranslation 1

/* Transition
-------------------------- */
$--all-transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1) !default;
$--fade-transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) !default;
$--fade-linear-transition: opacity 200ms linear !default;
$--md-fade-transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1),
  opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) !default;
$--border-transition-base: border-color 0.2s
  cubic-bezier(0.645, 0.045, 0.355, 1) !default;
$--color-transition-base: color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1) !default;

/* Color
-------------------------- */
/// color|1|Brand Color|0
$--color-primary: #409eff !default;
/// color|1|Background Color|4
$--color-white: #ffffff !default;
/// color|1|Background Color|4
$--color-black: #000000 !default;
$--color-primary-light-1: mix(
) !default; /* 53a8ff */
$--color-primary-light-2: mix(
) !default; /* 66b1ff */
$--color-primary-light-3: mix(
) !default; /* 79bbff */
$--color-primary-light-4: mix(
) !default; /* 8cc5ff */
$--color-primary-light-5: mix(
) !default; /* a0cfff */
$--color-primary-light-6: mix(
) !default; /* b3d8ff */
$--color-primary-light-7: mix(
) !default; /* c6e2ff */
$--color-primary-light-8: mix(
) !default; /* d9ecff */
$--color-primary-light-9: mix(
) !default; /* ecf5ff */
/// color|1|Functional Color|1
$--color-success: #67c23a !default;
/// color|1|Functional Color|1
$--color-warning: #e6a23c !default;
/// color|1|Functional Color|1
$--color-danger: #f56c6c !default;
/// color|1|Functional Color|1
$--color-info: #909399 !default;

$--color-success-light: mix($--color-white, $--color-success, 80%) !default;
$--color-warning-light: mix($--color-white, $--color-warning, 80%) !default;
$--color-danger-light: mix($--color-white, $--color-danger, 80%) !default;
$--color-info-light: mix($--color-white, $--color-info, 80%) !default;

$--color-success-lighter: mix($--color-white, $--color-success, 90%) !default;
$--color-warning-lighter: mix($--color-white, $--color-warning, 90%) !default;
$--color-danger-lighter: mix($--color-white, $--color-danger, 90%) !default;
$--color-info-lighter: mix($--color-white, $--color-info, 90%) !default;
/// color|1|Font Color|2
$--color-text-primary: #303133 !default;
/// color|1|Font Color|2
$--color-text-regular: #606266 !default;

// Background
/// color|1|Background Color|4
$--background-color-base: #f5f7fa !default;

ITCSS: Inverted Triangle

ITCSS is a "meta-framework" for CSS, compatible with other systems, designed to help tame the chaos of CSS overriding each other unpredictably.
It is based on the concept of layering, with each progressive layer forming an inverted pyramid shape, hence the name "inverted triangle".
This methodology is influential for managing CSS files on large-scale projects. To learn more, watch a talk given by its creator.


Popular CSS libraries include ACSS, Tachyons, WindiCSS, Tailwind and many more.

Inline styles

The shift to component-based frameworks often saw styles applied inline within components. In frameworks like React, we pass a Javascript object to the style prop, which is then converted to inline styles.
This can be a cause of concern for many, as it feels like we're reverting back to the days before external stylesheets, disregarding existing best practices.
However, when it comes to components, inline styles don't suffer from the same issue of massive duplication, as they are encapsulated within the component. In addition, the fact that styles only affect the element they are applied to provides a safe way to add and modify CSS within components.
The main issue with inline styles is the lack of access to more powerful CSS features, such as pseudo selectors and media queries. This also makes it difficult to leverage shared design tokens, caching, static analysis, and pre-processing.


This resembled inline styles, but with the ability to leverage the capabilities of stylesheets.
The first wave of CSS-in-JS libraries, such as Styled Components, Emotion, and many more, gained traction in the React ecosystem. However, there was a performance tax end users were paying due to server-side rendering inefficiencies, caching issues, and client runtime costs.
The second wave of CSS-in-JS tools, like Vanilla Extract, Linaria, and Compiled, extract stylesheets out from components in a compile step. This moves much of the runtime processing that happens on users' browsers to compile time, reducing the performance tax.

CSS Modules

It depends on a bundler like Webpack to generate and ensure selectors are scoped, which keeps CSS localised within a component directory.


The :is(), :not() and :has() will use the max specificity in its parameters list
:is(p, #fakeId) {
  /* 1-0-0 */
h1:has(+ h2, > #fakeId) {
  /* 1-0-1 */
p:not(#fakeId) {
  /* 1-0-1 */
div:not(.inner, #fakeId) p {
  /* 1-0-2 */
So the p ‘s background color will be green
<div id="fakeId">bbb</div>

  :is(p, #fakeId) {

The :where() exception always has its specificity replaced with zero
:where(#defaultTheme) a {
  /* 0-0-1 */
  color: red;

footer a {
  /* 0-0-2 */
  color: blue;

CSS attributes

Often referred to as "the first CSS variable", currentColor is a value equal to the element's color property. It can be used to assign a value equal to the value of the color property to any CSS property which accepts a color value. This forces the CSS property to inherit the value of the color property.

Usage cases

  • SVG
<!-- After -->
<path fill="currentColor" d="..."/>
  • border-color, background, and box-shadow
<aside class="warning">
Warning message
aside {
  border-left: 5px solid currentColor;

.warning {
  color: darkgreen;

Define variables

:root {
  --main-bg-color: brown;

.container {
  color: var(--main-bg-color);
boxParaRule.style.setProperty("--border-width", "2px");

Programming to change CSS attributes

boxParaRule.style.setProperty("background-color", "blue");
boxParaRule.style = "background-color:blue;";
boxParaRule.style.backgroundColor = "blue";

Programming to get CSS attributes

// get variable from inline style

// get variable from wherever

Dark images when in Dark Mode

/* Apply the filter directly on the body tag */
body.dark-theme img {
  filter: brightness(0.8) contrast(1.2);

/* Or apply it via media query */
@media (prefers-color-scheme: dark) {
  img {
    filter: brightness(0.8) contrast(1.2);

Get the size of a scaled element:

we can only use getBoundingClientRect and the getComputedStyle will not work


background-size for background-image (cover/contain/ values)
object-fit for image (contain/cover/fill/scale-down)



Spacing between elements is crucial for improving the aesthetics and readability of a website. There are several ways to set spacing between elements in CSS:
  • margin and gap can be used to set the outer spacing between elements. gap is commonly used in Flexbox and Grid layouts to set equal spacing between items. But there is one prerequisite, which is that the spacing between all the items is equal.
  • padding is used to set the spacing between the container edge and its content.
  • Alignment in Flexbox and Grid layouts can also be used to set spacing between items, but it can easily cause the layout to be unattractive due to changes in the number of items. It's best to avoid alignment whenever possible to control spacing between items.
  • In some unique layout scenarios, CSS positioning can also be used to set spacing between elements.
These are just some of the common ways to set spacing in CSS. In actual use, you can also combine other CSS features, such as sibling adjacent selectors (E + F), negation selector :not(), parent selector :has(), pseudo-class selectors :last-child or :first-child, to set spacing between elements based on specific conditions. This allows the code to be more adaptable to different scenarios and environments, without having to manually modify the CSS code due to data changes.
notion image


philipwaltonUpdated Jun 12, 2024


Each cell in the same column must have the same width, and each cell in the same row must have the same height. Additionally, rows and columns cannot end prematurely; if a grid has two columns and three rows, each column must have three cells.
when center the menu at the center, the grid is a better choice.
notion image
header {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 1rem;

Named area and seperate lines

Seperate lines
notion image
.container {
    [header-start nav-start footer-start] 220px
    [nav-end main-start] 1fr
    [main-end aside-start] 220px
    [aside-end header-end footer-end];
    [header-start] auto
    [header-end nav-start main-start aside-start] 1fr
    [nav-end main-end aside-end footer-start] auto

header {
  grid-column: header-start / header-end;
  grid-row: header-start / header-end;

nav {
  grid-column: nav-start / nav-end;
  grid-row: nav-start / nav-end;

main {
  grid-column: main-start / main-end;
  grid-row: main-start / main-end;

aside {
  grid-column: aside-start / aside-end;
  grid-row: aside-start / aside-end;

footer {
  grid-column: footer-start / footer-end;
  grid-row: footer-start / footer-end;
.container {
    "header  header  header"
    "nav     main    aside"
    "footer  footer  footer";
.container {
  display: grid;
    "header   header   header"
    "nav      main     aside"
    "nav      footer   footer";

header {
  grid-area: header;

nav {
  grid-area: nav;

main {
  grid-area: main;

aside {
  grid-area: aside;

footer {
  grid-area: footer;


Avoid Hardcoding of Labels in a Certain Language

When creating UI components, it is important to allow for customisation of label strings by making them part of the component props/options. This will allow for more flexibility in the components. For example, an image carousel should have customisable labels for the prev/next buttons.

Right-to-Left Languages

For languages read from right-to-left, such as Arabic and Hebrew, the UI should be flipped horizontally. This can be done by passing in a direction prop/option. This will ensure that elements are rendered in the correct order, such as having the prev and next buttons on the right and left respectively in RTL languages.
notion image
notion image
  • offset.. is relative to it’s parent element



An animationstart event will happen for each valid keyframe animation that is applied. Typically, there will be one animationstart event for each valid animation-name identifier that is present. If there is a animation-delay, the event will fire once the delay period has expired.
Like animationstart, this event will be fire once each applied animation finish.
(animation-duration * animation-iteration-count) + animation-delay = event fired required time
The animationiteration event happens at the end of each animation iteration, before the next one starts. If animation-iteration-count is an integer, the number of animationiteration events is typically one less than the value of the animation-iteration-count property. This is true as long as the absolute value of any negative delay is less than the duration.

Reduce the animation time

Trigger re-layout:
  • clientHeight (Left, Top, Width)
  • focus()
  • getBoundingClientRect()
  • getClientRects()
  • innerText
  • offsetHeight (Left, Parent, Top, With)
  • outerText
  • scrollByLInes() .....


Here is a useful code snippet for creating a horizontally-scrolling list of items that snap to a specific position. This can be achieved using the scroll-snap-type and scroll-snap-align properties.
.snaps {
  overflow-x: scroll;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;

.snap-target {
  scroll-snap-align: center;

.snap-force-stop {
  scroll-snap-stop: always;

Media query


The @container rule is a new media query that allows you to style elements based on their container size. You can use this to change the styling of an element based on the size of the container it is in, instead of the size of the viewport. For example, you could use this to style a card differently when it is placed in a narrow panel on a larger screen.
@container panel (min-width: 10rem) {
  .card {
    padding: 2rem;
    color: red;

Css properties’s prefix

Private Prefix
WebKit engine's Safari, Blink engine's Chrome, Opera, Edge; partially supported by EdgeHTML engine's Edge
Gecko engine's Firefox
Trident engine's IE and EdgeHTML engine's Edge
Presto engine's Opera

Reset css property

The property value initial sets an element's property to the initial value specified by the W3C specification.
The property all resets all of an element's properties.
Browsers may set default styles for some elements, such as form elements, outside of the specification.Property values can come from developer-defined, user-configured, and browser-default sources.
Using all:initial is equivalent to clearing user configurations and browser-default styles.
In practice, it is preferable to reset to default styles rather than clear them.
The all:revert property restores an element's properties. Sub-element properties are reset according to the following rules:
  • Inherited properties: reset to the parent element's property value.
  • Non-inherited properties or parent element inherited properties that are not set: reset to user-configured and browser-default properties.

CSS shapes(clip-path)




Organzing your css

Create logical sections in your stylesheet. By organizing your stylesheet in this way, you can easily locate and modify different parts of the stylesheet.
Common styling

body {
  /* … */

h4 {
  /* … */

ul {
  /* … */

blockquote {
  /* … */
utility classes
/* || UTILITIES */

.nobullets {
  list-style: none;
  margin: 0;
  padding: 0;

/* … */
Then we can add everything that is used sitewide. That might be things like the basic page layout, the header, navigation styling, and so on.
/* || SITEWIDE */

.main-nav {
  /* … */

.logo {
  /* … */
specific things
/* || STORE PAGES */

.product-listing {
  /* … */

.product-box {
  /* … */


Prefix CSS attributes

PostCSS is a plugin that parses CSS and adds vendor prefixes to CSS rules based on values from Can I Use.
postcssUpdated Jun 14, 2024
Online review
cubic bézier curves


  • For line elements, the height they contribute to their parent is determined by their line height, not their padding or content.
  • justify-content default is flex-start , and align-items’s initial value is stretch.
  • transition has effect at the different time
button {
  color: magenta;
  /* From hover -> noHover (rebeccapurple->magenta) */
  transition: color 100ms ease-in;
button:hover {
  color: rebeccapurple;
  /* From nohover -> hover (magenta->rebeccapurple) */
  transition: color 2000ms ease-in;
  • translate 100% percentage value is based on its own width and height, left, top, bottom, right , margin-* using 100% percentage value is based on the parent’s width and height.
  • fixedThe element is removed from the normal document flow. It is positioned relative to the initial containing block established by the viewport, except when one of its ancestors has a transformperspective, or filter property set to something other than none, or the will-change property is set to transform, in which case that ancestor behaves as the containing block.

Useful code Snaps

Determine if the browser is compatible with the CSS feature.
// Browser support: 98% globally
@supports (display: grid) { 
  .wrapper {
    display: grid;
    grid-template-columns: 1fr 1fr;
GPU acceleration
// in tailwind, it replace translate to translate3d
'.transform-gpu': {
        transform: cssTransformValue.replace(
          'translate(var(--tw-translate-x), var(--tw-translate-y))',
          'translate3d(var(--tw-translate-x), var(--tw-translate-y), 0)'
Want to add outline or change background when focusing a child element, but without adding unnecessary effect when clicking using mouse (Not work on firefox)
.tagLabel:focus-within {
  background-color: rgb(234 179 8);
/* Solve the problem when using a mouse, it background-color also change */
.tagLabel:has(input:focus:not(:focus-visible)) {
  background-color: rgb(202 138 4);
Truncate text if it is longer than three lines.
/* 1. webkit-line-clamp will allow you to clamp text to 3 lines,
   2. It must be used in combination with either display:-webkit-box or display-webkit-inline-box and
 webkit-box-orient set to vertical
   3. In most cases you'll also need overflow set to hidden because otherwise remaining text will be visible
   More: https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-line-clamp
.max-three-lines {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
Mobile-first media queries
/* Default CSS layout for narrow screens */

@media (min-width: 480px) {
  /* CSS for medium width screens */

@media (min-width: 800px) {
  /* CSS for wide screens */

@media (min-width: 1100px) {
  /* CSS for really wide screens */




This detailed post from the Chrome teams brings together all of the headline features that landed in Chrome and across the web platform this year, including nesting, new color functions, the :has selector, linear-easing, and much much more.