CSS architecturesOOCSS: Object Orientated CSSBEM: Block, Element, ModifierSMACSS: Scalable and Modular CSSITCSS: Inverted Triangle ACSSInline stylesCSS in JSCSS ModulesSpecificityCSS attributesUsage casesDefine variablesProgramming to change CSS attributesProgramming to get CSS attributesDark images when in Dark ModeGet the size of a scaled element:ImageLayoutPadding/marginFlexGridNamed area and seperate linesInternationalisation(i18n)Avoid Hardcoding of Labels in a Certain LanguageRight-to-Left LanguagesAnimationEventsReduce the animation timeScrollMedia query@container Css properties’s prefixReset css propertyCSS shapes(clip-path)Cos/sin Organzing your cssToolsPrefix CSS attributesPitfallUseful code SnapsPost-processingReadingsReference
css-protips
AllThingsSmitty • Updated Sep 6, 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" /> <input class="form__submit form__submit--disabled" type="submit" /> </form> */ .form { } .form--theme-xmas { } .form--simple { } .form__input { } .form__submit { } .form__submit--disabled { }
Block
Standalone entity that is meaningful on its own.
Examples
header
, container
, menu
, checkbox
, input
Element
A part of a block that has no standalone meaning and is semantically tied to its block.
Examples
menu item
, list item
, checkbox caption
, header title
Modifier
A flag on a block or element. Use them to change appearance or behavior.
Examples
disabled
, highlighted
, checked
, fixed
, size big
, color 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
//element/packages/theme-chalk/src/common/var.scss @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( $--color-white, $--color-primary, 10% ) !default; /* 53a8ff */ $--color-primary-light-2: mix( $--color-white, $--color-primary, 20% ) !default; /* 66b1ff */ $--color-primary-light-3: mix( $--color-white, $--color-primary, 30% ) !default; /* 79bbff */ $--color-primary-light-4: mix( $--color-white, $--color-primary, 40% ) !default; /* 8cc5ff */ $--color-primary-light-5: mix( $--color-white, $--color-primary, 50% ) !default; /* a0cfff */ $--color-primary-light-6: mix( $--color-white, $--color-primary, 60% ) !default; /* b3d8ff */ $--color-primary-light-7: mix( $--color-white, $--color-primary, 70% ) !default; /* c6e2ff */ $--color-primary-light-8: mix( $--color-white, $--color-primary, 80% ) !default; /* d9ecff */ $--color-primary-light-9: mix( $--color-white, $--color-primary, 90% ) !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.
ACSS
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.
CSS in JS
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.
Specificity
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
<p>aaa</p> <p></p> <div id="fakeId">bbb</div> <style> :is(p, #fakeId) { background-color:green; } p{ background-color:yellow; } </style>
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
currentColor
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
, andbox-shadow
/* <aside class="warning"> Warning message </aside> */ aside { border-left: 5px solid currentColor; } .warning { color: darkgreen; }
Define variables
In CSS
:root { --main-bg-color: brown; } .container { color: var(--main-bg-color); }
In JS
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 element.style.getPropertyValue("--my-var"); // get variable from wherever getComputedStyle(element).getPropertyValue("--my-var");
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 workImage
background-size
for background-image
(cover
/contain
/ values
)object-fit
for image
(contain
/cover
/fill
/scale-down
)Layout
Padding/margin
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
andgap
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](/_next/image?url=https%3A%2F%2Fwww.notion.so%2Fimage%2Fhttps%253A%252F%252Fs3-us-west-2.amazonaws.com%252Fsecure.notion-static.com%252F68ef97f2-404b-45d0-bac3-548ae90a82ec%252FUntitled.jpeg%3Ftable%3Dblock%26id%3Dd4139ab8-53a0-44cd-80d3-7d7d87286119%26cache%3Dv2&w=3840&q=75)
Flex
flexbugs
philipwalton • Updated Sep 4, 2024
Grid
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](/_next/image?url=https%3A%2F%2Fwww.notion.so%2Fimage%2Fhttps%253A%252F%252Fs3-us-west-2.amazonaws.com%252Fsecure.notion-static.com%252F163b8550-f38a-448b-9972-fc29fd359403%252FUntitled.png%3Ftable%3Dblock%26id%3D9a966d69-e242-4301-b75c-76a58b63808c%26cache%3Dv2&w=3840&q=75)
header { display: grid; grid-template-columns: 1fr auto 1fr; gap: 1rem; }
Named area and seperate lines
Seperate lines
![notion image](/_next/image?url=https%3A%2F%2Fwww.notion.so%2Fimage%2Fhttps%253A%252F%252Fs3-us-west-2.amazonaws.com%252Fsecure.notion-static.com%252F84f946bd-fce0-4859-a32e-e549e71e499e%252FUntitled.png%3Ftable%3Dblock%26id%3D65281ff4-f592-4874-a8f6-e768dddd25f7%26cache%3Dv2&w=3840&q=75)
.container { grid-template-columns: [header-start nav-start footer-start] 220px [nav-end main-start] 1fr [main-end aside-start] 220px [aside-end header-end footer-end]; grid-template-rows: [header-start] auto [header-end nav-start main-start aside-start] 1fr [nav-end main-end aside-end footer-start] auto [footer-end]; } 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; }
Names
.container { grid-template-areas: "header header header" "nav main aside" "footer footer footer"; }
.container { display: grid; grid-template-areas: "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; }
Internationalisation(i18n)
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](/_next/image?url=https%3A%2F%2Fwww.notion.so%2Fimage%2Fhttps%253A%252F%252Fs3-us-west-2.amazonaws.com%252Fsecure.notion-static.com%252Fb5a7986c-284b-4d32-a925-c08b7058d90d%252FUntitled.png%3Ftable%3Dblock%26id%3D1b206472-16fc-4ee4-9020-c2ca55214c57%26cache%3Dv2&w=3840&q=75)
![notion image](/_next/image?url=https%3A%2F%2Fwww.notion.so%2Fimage%2Fhttps%253A%252F%252Fprod-files-secure.s3.us-west-2.amazonaws.com%252F3f542fdc-ce07-4179-bf6e-7b7f20f9a6cf%252F70ca5e91-0151-4073-a549-297fa47d6559%252Fhh781509.ie9_positioning1(en-usvs.85).png%3Ftable%3Dblock%26id%3D10d3bfa0-1112-4589-aed8-806a313616f8%26cache%3Dv2&w=3840&q=75)
- offset.. is relative to it’s parent element
Animation
Events
animationstart
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.animationend
Like
animationstart
, this event will be fire once each applied animation finish.(
animation-duration
* animation-iteration-count
) + animation-delay
= event fired required timeanimationiteration
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() .....
Scroll
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
@container
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 | Compatibility |
-webkit- | WebKit engine's Safari, Blink engine's Chrome, Opera, Edge; partially supported by EdgeHTML engine's Edge |
-moz- | Gecko engine's Firefox |
-ms- | Trident engine's IE and EdgeHTML engine's Edge |
-o- | 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)
Cos/sin
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
/* || GENERAL STYLES */ body { /* … */ } h1, h2, h3, 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 { /* … */ }
Tools
Prefix CSS attributes
PostCSS is a plugin that parses CSS and adds vendor prefixes to CSS rules based on values from Can I Use.
autoprefixer
postcss • Updated Sep 5, 2024
Online review
cubic bézier curves
Pitfall
- For line elements, the height they contribute to their parent is determined by their line height, not their padding or content.
justify-content
default isflex-start
, andalign-items
’s initial value isstretch
.
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.
fixed
The 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 atransform
,perspective
, orfilter
property set to something other thannone
, or thewill-change
property is set totransform
, 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 */ }
Post-processing
Readings
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.