- Container queries
- :has
- :nth-child of
- text-wrap:balance
- initial-letter
- Dynamic viewport units (dvh and dvw…)
- vmin and vmax
- New CSS Color Methods
- color-mix
- Css Nesting
- Cascading Layers
- :is() and :where()
- @scope
- Trigonometric Functions in CSS
- Individual transform properties
- Subgrid
- Masonory layout
- writing-mode
- New boolean css properties
- writing-mode
- aspect-ratio
- content-visibility
- object-view-box
- Media Query Range Syntax
- mix-blend-mode
- Css filter
- backdrop-filter
- min max fit-content
- conic-gradien
- accent-color
- currentColor
- Color profiles for gradients
- Scroll Snap
- overscroll behavior
- touch-action
- scroll-behavior
- scrollbar-gutter
- line-clamp
- Variable fonts
- font-palette
- prefers-reduced-motion
- prefers-color-scheme
- prefers-reduced-data
- color-scheme
- prefers-contrast
- :focus-visible
- min(), max() и clamp()
- ::marker
- image-set
- ::backdrop
- popover
CSS container queries
Previously, when creating adaptability on the site, we could rely on the width of the vieport. This, in turn, did not always help to solve the problems of adaptability.
Example:
We have an image element above text below. @media request example – If the viewport width is greater than 46rem, the component should switch to the horizontal version (text on the right of the image on the left).
@media (min-width: 46rem) { .c-article--horizontal { display: flex; flex-wrap: wrap; } .c-article > * + * { margin-top: 0; } .c-article__thumb { margin-right: 1rem; } }
Consider that we want to use the standard .c-c-card in the main section. What will happen? Well, the map will expand to the width of its parent and therefore end up too big.
This is a problem and we can solve it with container requests.
We need to tell the component that if the immediate width of the parent element is greater than 400px, it needs to switch to a horizontal style. The CSS will look like this:
.o-grid__item { container-type: inline-size; container-name: article-with-image; } .c-article { /* The default style */ } @container article-with-image (min-width: 400px) { .c-article { /* The styles that will make the article horizontal** ** instead of a card style.. */ } }
with @supports tag
@supports not (container-type: inline-size) { .c-article { /* The styles that will make the article horizontal** ** instead of a card style.. */ } } @supports (container-type: inline-size) { @container article-with-image (width > 700px) { .c-article { /* The styles that will make the article horizontal** ** instead of a card style.. */ } } }
With CSS container requests, we can solve the above problem and create a stretchable component. This means that we can nest a component in a narrow parent and it will become a stacked version, and in a wide parent it will become a horizontal version. Again, all elements are independent of the viewport width.
Although the above works, it’s better to give the container a name to avoid various unpleasant situations and confusion.
.o-grid__item { container-type: inline-size; container-name: grid-item; }
Now we can add the container name next to @grid-item like this:
@container grid-item (min-width: 400px) { .c-article { display: flex; align-items: center; } }
Browser support – https://caniuse.com/css-container-queries
:has
The :has selector in CSS allows you to select elements that contain certain children that match the given selector.
/* Selects all elements <div>, which contain <p> with classs "highlight" */ div:has(p.highlight) { background-color: yellow; }
Browser support – https://caniuse.com/css-has
:nth-child of
Searching for a child element
li:nth-child(2 of .red) { }
This code selects the second element of the list with the class “red” and applies styles or CSS rules to it.
text-wrap:balance
The text-wrap: balance CSS property is used to set the balance of text within an element.
When this property is applied to a block element, the text inside it will be evenly spaced across the width of the block to fill all the available space.
This is especially useful when working with multiline text where you need to avoid extra space or text overflow.
Why is text-wrap special?
The main difference between `text-wrap: balance` and other properties, such as `text-wrap: normal` or `text-wrap: wrap` , is that it tends to balance the text within the width of the block.
Instead of just wrapping words on a new line like `text-wrap: normal` does, or breaking words apart to wrap like `text-wrap: wrap` , `text-wrap: balance` tries to evenly distribute text across the width of the block to create a more harmonious appearance.
This can be useful, for example, when creating newspaper columns or fixed-width blocks of text where uniform space filling is required.
Browser support – https://caniuse.com/css-text-wrap-balance
initial-letter
The initial-letter property specifies what the first letter should be and how many lines it should span.
This property is found in newspapers where the first letter is larger than the rest of the content.
.example::first-letter { -webkit-initial-letter: 2; initial-letter: 1; color: #8ebf42; font-weight: bold; margin-right: .60em; }
Browser support – https://caniuse.com/css-initial-letter
Dynamic viewport units
So far, 100vh has been the maximum height you could get for your CSS styles.
The problem with 100vh is that it can actually display more content than is initially available on the screen. A practical example you might think of is the Safari browser for iOS.
svh and svw
Small viewport units, denoted sv*, are aligned to the “small viewport”. This module takes into account dynamically changing UI elements from the user agent and essentially
allows you to fill the size of the viewport when the document’s visible content is at its smallest size and the UI elements at its largest
.
An example would be the default Safari UI layout when the page loads. The address bar at the bottom is expanded and takes up quite a lot of space. In this state, user agent UI elements use the maximum available space, and your page’s visible content has the minimum visible size.
.error { width: 40em; height: auto; max-width: 100svh; max-height: 100svh; }
lvh and lvw
Unlike a small viewport, a large viewport results in a maximum value that fills the visible page when the UI is at its smallest option and the page content is at its largest. If this spec sounds familiar, it might be because the current implementation for “vh” and “vw” acts the same way.
A practical example would be the Safari view when you scroll a bit on any scrollable page. As you’ll notice, the address bar shrinks, resulting in the smallest visible area of the user interface. Your content is given the maximum possible space in this state.
header { width: 100lvw; height: 75lvh; background-image: url(cover-illo.jpg); background-size: cover; }
dvh and dvw
You may have already guessed that the units of a dynamic viewport can change as the user scrolls the page.
They cover the largest viewport and at least the smallest viewport. This value can be very handy if you want to align the height of the viewport but always keep your content visible even if the user is scrolling.
Imagine a user starts scrolling a page on a smartphone, at which point the search bar and navigation buttons are hidden. Anything that has a dynamic viewport will start resizing. And so every time you scroll the page. This can significantly affect performance, but even if not, then visually it may not be the most pleasant effect.
vmin and vmax
The vmin CSS unit is used to define the dimensions of an element relative to the smaller value between the width and height of the viewport. The value of 1vmin is 1% of the smaller value between the width and height of the viewport.
div { width: 50vmin; /* The element will take up 50% of the smaller value between the viewport's width and height */ }
This allows you to create elements that will be proportional and adjust to the dimensions of the visible area, regardless of how the size is measured (width or height).
The vmax unit in CSS is used to size an element relative to the larger value between the width and height of the viewport. The value of 1vmax is 1% of the larger value between the width and height of the viewport.
div { width: 50vmax; /* The element will take up 50% of the larger value between the viewport's width and height */ }
Browser support – https://caniuse.com/viewport-units
New CSS Color Methods
In CSS, we most often write colors through rgb() or hex – but this has happened historically. The new CSS Color 4 specification will allow us to describe colors through new methods.
oklch() is a new way to define colors in CSS. In oklch(L C H) or oklch(L C H / a), the components from the abbreviation are deciphered as follows:
- L, lightness – brightness (0%–100%), passed as the eye sees it, unlike L in hsl();
- C, chroma – saturation, varies from gray to the most intense shade;
- H, hue – hue, rotation angle on the color wheel (0–360);
- a, alpha – opacity (0–1 or 0%–100%).
a:hover { /* blue */ background: oklch(45% 0.26 264); /* white */ color: oklch(100% 0 0); /* black with 50% opacity */ color: oklch(0% 0 0 / 50%); }
The OKLCH format advantages
Unlike rgb() or hex (#ca0000), OKLCH is easy to read. By the numbers inside oklch(), we can easily determine the recorded color. This is similar to the convenience of the HSL format, but it does not convey the brightness as a person sees it.
oklch() is better for changing colors than hsl(). It correctly conveys brightness – it does not change when changing hue (remember the unexpected results of darken() in Sass due to the use of HSL).
Due to predictable brightness, OKLCH has much better accessibility. This is very important when creating palettes for design systems.
Many newer devices (eg from Apple) support more colors than older sRGB monitors. OKLCH allows these P3 colors to be used.
For OKLCH, when selecting L, C and H, there is a risk of getting a color that goes beyond the limits of the screen. Although browsers will try to find the closest supported color, we should check the result in a color mixer.
Browser support – https://caniuse.com/mdn-css_types_color_oklch
Color mix
The functional color-mix() notation takes two
color-mix(in lch, plum, pink); color-mix(in lch, plum 40%, pink); color-mix(in srgb, #34c9eb 20%, white); color-mix(in hsl longer hue, hsl(120 100% 50%) 20%, white);
color-mix(method, color1[ p1], color2[ p2])
Browser support – https://caniuse.com/?search=color-mix
CSS Nesting
Before nesting appeared, each selector had to be declared separately from each other. This leads to repetition, a large volume of style sheets and the disparity of the creation process.
Before:
.nesting { color: hotpink; } .nesting > .is { color: rebeccapurple; } .nesting > .is > .awesome { color: deeppink; }
Once nested, selectors can be continued and their associated style rules can be grouped inside:
.nesting { color: hotpink; > .is { color: rebeccapurple; > .awesome { color: deeppink; } } }
@support to check if the nesting selector can be parsed.
@supports (selector(&)) { /* code */ }
Browser support – https://caniuse.com/css-nesting
Cascading Layers
One of the most common confusions in CSS is specificity when writing styles. For example, changing the display value of an element will never work if another element in the cascade overrides it due to its higher specificity. Or when some element has !important. This usually happens when the codebase grows and we don’t structure our CSS in a way that avoids (or reduces) these issues.
To overcome cascading and specificity issues, we need to be careful where we write CSS. In small projects, everything can be fine, but in large projects, the task can be time consuming. As a result, various methods began to appear for organizing CSS and, accordingly, reducing the problems with the cascade.
Cascading layers allow you to control the specificity and order of rules in stylesheets.
Read more: Cascading Layers
:is() and :where()
The :is() and :where() pseudo-classes are already well supported by evergreen browsers.
You can pass multiple selectors in parentheses. The difference is how they work with specificity
:is() takes into account the specificity of the resulting selectors, for example, the rule :is(.class, #id) will have selector specificity by id.
:where() always has zero specificity, and this fact can be used, for example, to set up reset styles or base styles that are meant to be overridden in the future.
For example, I like to remove the list-style if the list element has a role=”list” attribute (so that assistive technologies still treat it as a list).
But adding an attribute selector increases specificity, so you’ll have to add it again to override styles. Instead, you can use :where() to create a zero-specific selector that is very easy to override.
:where(ul, ol):where([role="list"]) { ... }
@scope
Instead of simply relying on source order and specificity, we now have the ability to override styles based on proximity.
Guess what color the button will be in the following examples:
@scope (.blue) { button { background-color: blue; } } @scope (.green) { button { background-color: green; } } @scope (.red) { button { background-color: red; } }
First
<div class="red"> <div class="green"> <div class="blue"> <button>Click</button> </div> </div> </div>
Second
Just look at your closest ancestor. In example 1, the button is blue. In example 2, the button is red.
Set the lower limit for the selector
Sometimes you want to style a component without styling certain things nested within it.
@scope (.component) to (.content) { p { color: red; } }
Trigonometric Functions in CSS
In addition to the CSS math functions, the trigonometric functions sin(), cos(), tan(), asin(), acos(), atan(), and atan2() have been added.
Individual transform properties
And now, there is some news in the world of CSS Transforms: individual transform properties are enabled by default in Safari Technology Preview 117. This means that, as in Firefox and Chrome Canary, you can now use the new translate, rotate and scale CSS properties to specify what have so far been functions of the transform property, including 3D operations.
div.transform-property { transform: translate(100px, 100px) rotate(180deg) scale(2); } div.individual-properties { translate: 100px 100px; rotate: 180deg; scale: 2; }
Subgrid
Level 2 of the CSS Grid Layout specification includes a subgrid value for grid-template-columns and grid-template-rows.
When you add display: grid to a grid container, only direct children become grid items and can then be placed on the grid you created. The children of these elements are displayed in the normal flow.
You can “nest” grids by making a grid item a grid container. However, such grids are independent of the parent grid and of each other, that is, they do not take the track sizes from the parent grid. This makes nested grid elements difficult to align with the main grid.
If you set the subgrid value to grid-template-columns , grid-template-rows , or both, instead of creating a new list of tracks, the subgrid will use the tracks defined on the parent element.
For example, if you use grid-template-columns: subgrid and the subgrid spans three column lanes of the parent, the subgrid will have three column lanes the same size as the parent grid.
Gaps are inherited, but can also be overridden by a different gap value. Row names can be passed from the parent element to the subgrid, and the subgrid can also declare its own row names.
Browser support – https://caniuse.com/?search=subgrid
Masonory layout
Level 3 of the CSS grid layout specification includes a masonry value for grid-template-columns and grid-template-rows. This guide details what masonry layout is and how to use it.
Masonry layout is a layout method where one axis uses a typical strict grid layout, most often columns, and the other a masonry layout. On the masonry axis, rather than sticking to a strict grid with gaps being left after shorter items, the items in the following row rise up to completely fill the gaps.
.container { display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); grid-template-rows: masonry; }
Browser support – https://caniuse.com/?search=grid-template-rows%3A%20masonry%3B
writing-mode
p { writing-mode: horizontal-tb; }
horizontal-tb – sets the text direction horizontally from left to right and top to bottom.
vertical-rl – sets the text direction vertically from top to bottom and right to left.
vertical-lr – sets the direction of the text vertically from top to bottom and left to right.
Browser support – https://caniuse.com/css-writing-mode
New boolean css properties
The main idea behind boolean properties is that we won’t be using the CSS direction properties as is.
Instead, we will bind to the direction specified in the HTML. Properties that allow you to do this are called boolean properties. Look at an example:
We have a card containing an avatar and text. In the left-to-right layout, the field is located to the right of the avatar and vice versa, in the right-to-left layout, the field is reflected and located to the left. This is how it is written without boolean properties:
.avatar { margin-right: 1rem; } html[dir="rtl"] .avatar { margin-right: 0; margin-left: 1rem; }
Imagine we are doing this for a large project; too much code. But boolean properties solve the problem.
.avatar { margin-inline-end: 1rem; }
When using boolean properties, you usually see inline or block. Let’s see what they mean.
The meaning of inline and block changes depending on the writing mode. In languages like English, inline is horizontal and block is vertical. In languages like Japanese, the opposite is true: inline is vertical, block is horizontal.
The inset-inline-end property in left-to-right layouts is the right side equivalent, and in right-to-left layouts the left side equivalent for right-to-left layouts. The code above could be written like this:
.menu__toggle { position: absolute; right: 1rem; top: 1rem; } html[dir="rtl"] .menu__toggle { right: auto; left: 1rem; }
The code above can be written like this:
.menu__toggle { position: absolute; inset-inline-end: 1rem; top: 1rem; }
aspect-ratio
Property sets the aspect ratio for the element.
- auto – aspect ratio is calculated automatically.
- width / height – the aspect ratio is always considered relative to the width and height of the block.
- auto width / height – combined.
When writing together, the width / height ratio takes precedence, but when an element has its own aspect ratio, it will apply
To avoid image distortion, you can use the object-fit property.
content-visibility
The content-visibility property, launching in Chromium 85, may be one of the most effective new CSS properties for improving page loading performance. content-visibility allows the user agent to skip rendering work on an element, including layout and painting, until it is needed. Because rendering is skipped if most of your content is off-screen, using the content-visibility property greatly speeds up the user’s initial loading time. It also allows you to interact with screen content faster.
Read more – content visibility
object-view-box
The object-view-box property specifies the “viewport” for an element, much like the <svg viewbox> attribute does, allowing the element’s content to be enlarged or cropped.
It allows us to crop and resize HTML elements such as <img> or <video>.
The property takes the value
<em><basic-shape-rect> = <inset()> | <rect()> | <xywh()>.</em>
img { aspect-ratio: 1; width: 300px; object-view-box: inset(25% 20% 15% 0%); }
The value of inset() will be calculated from the original height and width of the image.
This will help us to draw a rectangle as if inside the image and specify 4 sides, as for the margin or padding properties.
Values 25%, 20%, 15% and 0% are responsible for the top, right, bottom and left, respectively.
Media Query Range Syntax
Media Queries Level 4 specification has introduced a new syntax for targeting a range of viewport widths using common mathematical comparison operators — things like <, >, and = — that make more sense syntactically while writing less code.
@media (min-width: 600px) { .element { /* Style away! */ } } @media (width >= 600px) { .element { /* Style away! */ } }
@media (min-width: 400px) and (max-width: 1000px) { /* etc. */ } @media (400px <= width <= 1000px) { /* etc. */ } ---
mix-blend-mode
The mix-blend-mode property determines how the selected element’s colors blend with underlying layers.
mix-blend-mode: normal; mix-blend-mode: multiply; mix-blend-mode: screen; mix-blend-mode: overlay; mix-blend-mode: darken; mix-blend-mode: lighten; mix-blend-mode: color-dodge; mix-blend-mode: color-burn; mix-blend-mode: hard-light; mix-blend-mode: soft-light; mix-blend-mode: difference; mix-blend-mode: exclusion; mix-blend-mode: hue; mix-blend-mode: saturation; mix-blend-mode: color; mix-blend-mode: luminosity;
Css filter
The filter property allows you to apply various artistic effects to elements. Commonly used on images to blur them, increase contrast, convert to black and white, etc.
filter: none | blur() | brightness() | contrast() | drop-shadow() | grayscale() | hue-rotate() | invert() | opacity() | saturate() | sepia() | url();
backdrop-filter
The backdrop-filter property allows you to apply visual effects – such as blurring or shifting the background color – behind an element.
/* Keyword value */ backdrop-filter: none; /* url filter in svg */ backdrop-filter: url(commonfilters.svg#filter); /* values <filter-function> */ backdrop-filter: blur(2px); backdrop-filter: brightness(60%); backdrop-filter: contrast(40%); backdrop-filter: drop-shadow(4px 4px 10px blue); backdrop-filter: grayscale(30%); backdrop-filter: hue-rotate(120deg); backdrop-filter: invert(70%); backdrop-filter: opacity(20%); backdrop-filter: sepia(90%); backdrop-filter: saturate(80%); /* multiple filters */ backdrop-filter: url(filters.svg#filter) blur(4px); /* global values */ backdrop-filter: inherit; backdrop-filter: initial; backdrop-filter: unset;
min max fit-content
When the size of the content determines the size of an element, we call it the intrinsic or natural size.
By limiting the block size to a certain size, we run into content overflow, which is the flip side of external resizing.
However, we can fix the unwanted behavior to improve the layout by defining the inner size of the element using keywords:
min-content is the smallest size a block can take without overflowing its content.
For horizontal content, min-content uses the length of the widest part of the content in the element’s block and automatically sets its length value to the width of the block. Content in this case includes text and resources such as images and videos.
.block_2 { width: min-content; }
Here, with min-content, the longest word in the content determines the size of the field; this is the inner minimum width of the box.
Min-content is a valid value for the grid and flexbox size properties. The flex-basis property sets the size of the content box for flexbox. This makes min-content an ideal value for automatically getting the window’s internal minimum size. In this case, we are using flex-basis: min-content.
Similarly, in a grid system, we can assign the min-content keyword to the grid-template-rows or grid-template-columns properties to get the internal minimum box size. Consider the following code:
<div class="grid"> <header class="header"> <!-- ... --> </header> <div class="content"> <!-- ... --> </div> <div class="footer"> <!-- ... --> </div> </div>
.grid { display: grid; grid-template-rows: min-content auto min-content; height: 100vh; }
This way we get the content’s internal minimum height without causing overflow.
max-content
max-content represents the ideal block size along the given axis, given infinite space available.
In other words, max-content represents the size that the block must contain all of its unwrapped content, otherwise the block will overflow. Let’s apply max-content to the element’s size:
<div id="container"> <div class="item1">Lorem ipsum dolor, sit amet consectetur adipisicing elit.</div> <div class="item2">Lorem ipsum dolor, sit amet consectetur adipisicing elit.</div> </div>
Here, the first element automatically assumes a default width, thus taking up as much space as the container allows. But by applying the max-content value to the same block, we get the exact size of the block’s content.
The value of the max-content keyword is perfect for situations where we need the maximum width of the content to determine the size of the field.
Consider a grid layout structure where we apply max-content to the grid columns:
<div class="container"> <div>Lorem</div> <div> Lorem ipsum dolor sit amet </div> <div>Lorem ipsum dolor</div> </div>
.container { display: grid; grid-template-columns: 1fr max-content max-content; }
This will display the column with the max-content value taking the size of the content, while the column with fr takes up the remaining available space.
However, in the case where the parent element or child element cannot fit the size of the box element, then we will get an overflow.
In such a situation, we must adjust the content of the box to match the container’s available space. This is where the fit-content keyword comes in.
fit-content
Depending on the size of the container element, when applying fit-content to a box element inside a container, the box uses either the maximum content size, the minimum content size, or the available container as the ideal size.
Given infinite space available, max-content determines the ideal size of the box element. However, when the viewport is narrower, the available space becomes the size of the block to prevent overflow as long as the block uses min-content. If we go back to our example by applying fit-content to the box element, we get this:
.item1 { width: fit-content; }
The field uses the available space but never expands beyond max-content , and when the viewport narrows, the field never expands beyond min-content.
The fit-content() function allows developers to define a maximum allowed width for an element’s size.
This CSS feature often determines the size of the grid’s columns and rows using grid-template-columns and grid-template-rows respectively. The fit-content() function takes a percentage or length unit as an argument:
fit-content(percentage | length)
<div class="container"> <div>Lorem ipsum dolor</div> <div> Lorem ipsum dolor, sit consectetur adipisicing elit.! </div> <div>Lorem</div> </div>
.container { display: grid; grid-template-columns: fit-content(200px) fit-content(250px) auto; /* ... */ }
In the first column of the grid layout, we passed 200px as an argument, hence the column has a maximum allowed width of 200px and the second column has a maximum allowed width of 250px. The third column occupies the remaining space of the container because it is set to auto.
conic-gradient
The conic-gradient function is used to set the background as a conic gradient.
In a conical gradient, the colors rotate in a circle around the center point, like the hands of a clock (or like in a pie chart).
.element { background-image: conic-gradient(#3590eb, #ee82cf); }
By default, the beginning of the gradient (the first color in the list) will be a line through the center and top points of the element (like the hands of a circular clock at 12:00) and then the colors will follow the circle clockwise.
The compact notation white, black actually consists of several defaults: starting angle, center position, and color coordinates:
.element { background-image: conic-gradient(from 0turn at 50% 50%, white 0%, black 100%); }
To create hard transitions, without a smooth gradient, you can set two points for each color (the boundaries of their beginning and end), and the next color should start from the same point where the previous one stopped.
conic-gradient(#fff 25%, #bbb 25% 50%, #666 50% 75%, #000 75%).
accent-color
The accent-color property specifies the accent color for some form elements. In this case, the accent is called a bright color that stands out in contrast against the background color.
An accent color is typically used to grab the user’s attention and highlight important elements on a web page.
Browser support – https://caniuse.com/?search=accent-color
currentColor
The currentColor keyword can be used as a value for a CSS property that accepts a color. For example background-color. The browser will replace currentColor with the current value of the color property.
Let’s take a small piece of text, give it the desired text color and add a shadow. In the place where you want to set the color of the shadow, use the currentColor keyword:
.element { color: darkblue; box-shadow: 0 0 5px currentColor; }
Browser support – https://caniuse.com/currentcolor
Color profiles for gradients
Now, when creating a gradient, you can set a color profile for it. To do this, use the keyword in hls in oklch, etc.
.block { linear-gradiebt(in hls to right, blue, red) }
Scroll Snap
CSS Scroll Snap is a CSS module that introduces scroll anchor positions that define the scroll positions at which the scroll container’s scroll port can end after the scroll operation has completed.
The use of Scroll Snapping is to set the scroll-snap-type property on the container element and the scroll-snap-align property on the child elements.
The scrolling of the container element is done with the child elements you define. In this simplest case it would look like this:
<div class="container"> <section class="child"></section> <section class="child"></section> <section class="child"></section> <p>...</p> </div>
.container { scroll-snap-type: y mandatory; } .child { scroll-snap-align: start; }
Container properties:
- scroll-snap-type
- scroll padding
- scroll-padding-top
- scroll-padding-right
- scroll-padding-bottom
- scroll-padding-left
- scroll-padding-inline
- scroll-padding-inline-start
- scroll-padding-inline-end
- scroll-padding-block
- scroll-padding-block-start
- scroll-padding-block-end
Child properties:
- scroll-snap-align
- scroll-margin
- scroll-margin-top
- scroll-margin-right
- scroll-margin-bottom
- scroll-margin-left
- scroll-margin-inline
- scroll-margin-inline-start
- scroll-margin-inline-end
- scroll-margin-block-start
- scroll-margin-inline-end
- scroll-snap-stop
scroll-snap-type: “mandatory” vs “proximity”
The “mandatory” value defines the behavior that whenever the user stops scrolling, the browser should return it to the anchor point.
The value of “proximity” is less strict – it means that the browser can return to the anchor point if it feels appropriate. In my experience, if this value is set, it fires if scrolling stops within a few hundred pixels of the anchor point.
See the Pen
Scroll-snap-type "Mandatory" vs "Proximity" by Max Kohler (@maxakohler)
on CodePen.
scroll-padding
By default, when scrolling, child elements will be pressed to the very edges of the container. You can change this by setting the container’s scroll-padding property. It has the same syntax as the padding property.
This can be useful if your layout has elements that might interfere with the content, such as a fixed header.
scroll-snap-align
This property allows you to specify which side of the element should be pressed against the container. The property has three possible values: “start“, “center“, and “end“.
The values are relative to the scroll direction. If you’re scrolling the element vertically, “start” implies the top edge of the element, if horizontally, the left edge. The “center” and “end” values work the same way. You can set a different value for each scroll direction by separating them with a space.
scroll-snap-stop: “normal” vs “always”
By default, scroll binding occurs only when the user stops scrolling. This means that while scrolling, some anchor points can potentially be skipped.
This behavior can be changed by setting the property “scroll-snap-stop: always” to any child element. This causes the scrollable container to stop on that element before the user can continue scrolling.
overscroll behavior
The overscroll-behavior property determines what the browser does when it reaches the edge of the scroll area. It is short for overscroll-behavior-x and overscroll-behavior-y
You may also have noticed that when a scrolling dialog box is placed on top of a scrolling page, when the dialog box’s scroll boundary is reached, the underlying page starts to scroll. This is called scroll chaining.
overscroll-behavior: auto; /* default */ overscroll-behavior: contain; overscroll-behavior: none; /* Two values */ overscroll-behavior: auto contain;
- auto – the default scroll overflow behavior happens as usual.
- contain – the default scroll overflow behavior is observed within an element that is set to this value (e.g. “bounce” or refresh effects), but the scroll chain does not occur for adjacent scroll areas, e.g. underlying elements will not scroll.
- none – scroll chaining does not occur for adjacent scroll regions, and the default scroll overflow behavior is prevented.
touch-action
The touch-actionCSS property specifies how a touchscreen user can control the element’s area (for example, by using the browser’s built-in zoom functions).
touch-action: auto; touch-action: none; touch-action: pan-x; touch-action: pan-left; touch-action: pan-right; touch-action: pan-y; touch-action: pan-up; touch-action: pan-down; touch-action: pinch-zoom; touch-action: manipulation;
- none – disables all types of interactions. You can handle events using JavaScript.
- auto – allows all types of interaction (default).
- manipulation – the element can be moved and zoomed. It’s short for pan-x pan-y pinch-zoom. Other non-standard gestures, like double tap, are prohibited.
- pan-x – the element can be panned along the x-axis with one finger. This is shorthand for pan-left and pan-right values. Can be used in conjunction with pan-y, pan-up, pan-down and pinch-zoom.
- pan-y – the element can be moved along the y-axis with one finger. This is shorthand for pan-up and pan-down values. Can be used in conjunction with pan-x, pan-left, pan-right and pinch-zoom.
- pan-left – the element can only be moved if you start moving from the left edge (experiment).
- pan-right – the element can only be moved if you start moving from the right edge (experiment).
- pan-down – the element can only be moved if you start moving from the bottom up (experiment).
- pan-up – the element can only be moved if you start moving from top to bottom (experiment).
- pinch-zoom – the element can be zoomed in with a pinch. Can be combined with pan-x, pan-left, pan-right, pan-y, pan-up, pan-down.
scroll-behavior
When you click on an anchor link, the browser instantly jumps to the target block. The same thing happens if you change the position of the scrollbar using JS.
You can use the smooth value to change the default behavior. It will make scrolling smooth. But in other situations, such as when the user scrolls the mouse wheel, the scroll-behavior property will not affect the behavior of the browser.
Controls the scrolling behavior within a block. The smooth value makes it smooth.
html { scroll-behavior: smooth; }
Possible scroll-behavior values:
- auto – default value, instant scrolling;
- smooth — smooth scrolling.
scrollbar-gutter
scrollbar-gutter solves the issue of content jumping around when the scrollbar appears or hides. This issue usually appears when the content height changes or a modal appears.
scrollbar-gutter has 3 possible values:
- auto – default value. If overflow: scroll or overflow: auto is set, and the content overflows, then the padding for the scrollbar will appear.
- stable – the padding for the scrollbar appears if the overflow value is hidden, scroll or auto, regardless of whether the content causes overflow or not.
- stable both-edges – the same as stable, but padding will be created from two opposite sides. When scrolling vertically – right and left, when scrolling horizontally – top and bottom.
In all cases, no padding will be created if the scrollbar is an overlay.
In our case, we use scrollbar-gutter: stable so that when we hide the scroll through overflow: hidden, we have an indent instead of a scrollbar and the content remains in place.
html, body { scrollbar-gutter: stable; }
line-clamp
The line-clamp property truncates text to a certain number of lines. This is a shorthand property for:
max lines
block-overflow
line-clamp: normal |
Variable fonts
A variable font is a single font file that can behave like many different fonts. A variable font is a single file in which the scatter of styles is limited to only the thinnest and boldest weights. Between them, the designer can independently choose any position he likes.
If you look inside the font, then each character has reference points on which it is built. And, if the number of these points is the same in thin and bold styles, the program can connect them and calculate the intermediate positions of the points. Of course, font development is not so simple and straightforward, and often we have to specify more than two base styles, but this does not affect the user experience in any way.
Read more – Variable fonts
font-palette
The font-palette property allows you to specify one of the many palettes contained in a font that the user agent should use for the font. Users can also override the values in the palette or create a new palette using the @font-palette-values rule.
/* Using a font-defined palette */ font-palette: normal; /* Using a user-defined palette */ font-palette: --one;
Specifies the default color palette or default glyph coloring (set by the font creator) to be used for the font. With this setting, the font palette with index 0 is rendered.
- light – specifies the first palette in the font that corresponds to “light” to be used for the font. Some fonts contain metadata that determines whether the palette is applicable to light (near white) backgrounds. If the font does not have this metadata, the light value behaves like normal.
- dark – specifies the first palette in the font that matches “dark” to be used for the font. Some fonts contain metadata that determines whether the palette is appropriate for dark (near black) backgrounds. If the font does not have this metadata, the value behaves like normal.
Browser support – https://caniuse.com/css-font-palette
prefers-reduced-motion
One of the values of the @media directive to check the user’s animation playback settings.
Most modern operating systems allow the user to adjust the animation settings in the settings. The prefers-reduced-motion media query allows you to determine whether animation is disabled or reduced in the system and apply CSS styles that take this into account.
Prefers-reduced-motion can be used to slow down or completely disable animation.
prefers-reduced-motion has two values:
- no-preference – default animation settings.
- reduce – animation disabled.
In the example, we add smooth scrolling only for users who do not have animations disabled at the system level.
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }
we can play the animation only if the user has no preferences in showing the animation:
@media (prefers-reduced-motion: no-preference) { .button { animation: shake 300ms linear infinite both; } }
If you have a lot of animation-related CSS, you can move the styles for playing it into a separate file and not load it to users who have opted out of animation:
<link rel="stylesheet" href="animations.css" media="(prefers-reduced-motion: no-preference)">
Browser support – https://caniuse.com/?search=prefers-reduced-motion
prefers-color-scheme
The prefers-color-scheme media query is processed based on the user’s OS color scheme preferences. For example, if you have selected dark mode in your OS settings, all dark mode enabled websites will be rendered appropriately using the CSS rules defined by the prefers-color-scheme media query.
There are two values for a property:
- light – preferences for the page with light mode;
- dark – preferences for a page with dark mode.
Let’s say we want to switch the mode of our site to a dark scheme. For such a case, the code can be formatted like this:
/* styles for standard light theme */ .element { background-color: #fff; color: #000; } /* if the user has switched the theme to dark, execute the media query: */ @media (prefers-color-scheme: dark) { .element { background-color: #000; color: #fff; } }
Browser support – https://caniuse.com/?search=prefers-color-scheme
prefers-reduced-data
The CSS media feature is used to detect if the user has requested web content that uses less internet traffic.
- no preference – Indicates that the user has not made the preference known to the system. This keyword value evaluates to false in a boolean context.
- reduce – indicates that the user has expressed a preference for light alternative content.
Browser support – https://caniuse.com/?search=prefers-reduced-data
color-scheme
The usual choice of color schemes of the operating system: “light” and “dark” or “day mode” and “night mode“. When the user selects one of these color schemes, the operating system makes adjustments to the user interface.
This includes form controls, scrollbars, and the CSS system color values used.
color-scheme: normal; color-scheme: light; color-scheme: dark; color-scheme: light dark; color-scheme: only light;
- normal – specifies that the element is not aware of any color schemes and should therefore be rendered using the browser’s default color scheme.
- light – specifies that the element can be displayed using the light color scheme of the operating system.
- dark – specifies that the element can be displayed using the operating system’s dark color scheme.
- only – prevents the user agent from overriding an element’s color scheme. Can be used to disable the color override caused by Chrome’s automatic dark theme by applying color-scheme: only light; for a particular element or :root.
Browser support – https://caniuse.com/?search=color-scheme
prefers-contrast
One of the values of the @media directive to validate user settings. Tracks the choice of contrast settings in the system.
@media (prefers-contrast: more) {
.image {
border: 3px solid black;
}
prefers-contrast has several values:
- no-preference – default contrast settings.
- more – contrast is increased.
- less – contrast is reduced.
- custom – forced color mode selected. For example, Windows High Contrast Mode.
Browser support – https://caniuse.com/?search=prefers-contrast
:focus-visible
The :focus-visible pseudo-class is used when you really need to visually highlight the elements in focus.
When the focus is on the button, we will show a white stroke. Note that we have reset the styles for :focus, so if you set the focus to the button by clicking on it, there will be no stroke. But if you use Tab, then the stroke will appear.
button:focus { outline: none; } .button:focus-visible { border: 2px solid #FFFFFF; outline: none; }
min(), max() и clamp()
These CSS features expand our ability to create dynamic layouts and design components that are more flexible than before. They can be used to adjust container element sizes, fonts, padding, and more.
Read more – min(), max() и clamp() what do and when use. With examples
::marker
::marker is a pseudo-element responsible for the marker field. It contains, for example, list markers.
Let’s create an unordered <ul> list and set the dot color to blue for each <li> element:
<ul> <li>Example1</li> <li>Example2</li> <li>Example3</li> </ul>
li::marker { color: #2e9aff; }
The content of this marker field can be controlled using properties:
- list-style-type;
- list-style-image;
- list-style-position.
There is also a list-style shortcut that allows you to set values for all of these properties at the same time.
image-set
The image-set() CSS function allows you to list multiple background images with conditions on which the browser will decide which one is the most appropriate to download at the moment.
div { background-image: image-set( url("puppy@1x.webp") type("image/webp") 1x, url("puppy@2x.webp") type("image/webp") 2x, url("puppy@1x.png") type("image/png") 1x, url("puppy@2x.png") type("image/png") 2x ); }
With fallback:
div { /* Fallback */ background-image: url("puppy.png"); /* For Chrome and Safari */ background-image: -webkit-image-set( url("puppy@2x.png") 2x, url("puppy@1x.png") 1x ); /* For Firefox */ background-image: image-set( url("puppy@2x.avif") type("image/avif") 2x, url("puppy@1x.avif") type("image/avif") 1x, url("puppy@2x.webp") type("image/webp") 2x, url("puppy@1x.webp") type("image/webp") 1x, url("puppy@2x.png") 2x, url("puppy@1x.png") 1x ); }
Browser support – https://caniuse.com/css-image-set
::backdrop
The pseudo-element is displayed below the topmost element in the stack on the z-axis, but above all other elements on the page, if any. Normally, ::backdrop is used to dim a page to highlight a photo or dialog box that is displayed on top of the dimming.
Currently only works with modals created with the <dialog> element:
dialog { width: 300px; } dialog::backdrop { background: rgba(0,0,0,0.7); }
popover
Instead of managing all these complexities on your own, you can let the browser handle them with the popover attribute and the following set of functions. HTML popover features:
Promotion to the top layer. The popups will be displayed on a separate layer above the rest of the page, so you don’t have to fiddle with the z-index.
Close on click function outside of content. Clicking outside the popup area closes the popup and returns focus.
Default focus control. When a popup is opened, focus is switched to the content inside the popup.
Available keyboard bindings. Pressing the esc key closes the popup and returns focus.
Available component bindings. The semantic connection of the popup element with the popup trigger.
<button popovertarget="my-popover"> Open Popover </button>
<div id="my-popover" popover> <p>I am a popover with more information.<p> </div>
This popup can be used to show additional information or as a widget.
By default, as in the previous code snippet, setting the popover with popovertarget means that the button or element that opens the popup will toggle it between open and closed states. However, you can still create explicit popups with popovertargetaction. This overrides the default toggle action. popovertargetaction options include:
- popovertargetaction=”show” – shows a popup.
- popovertargetaction=”hide” – hides the popup.
Using popovertargetaction=”hide” you can create a “close” button inside the popup
<button popovertarget="my-popover" popovertargetaction="hide"> <span aria-hidden=”true”>❌</span> <span class="sr-only">Close</span> </button>
The dialog element opened with dialog.showModal is an experience that requires explicit user interaction to close the modal. Popover supports click-to-close outside of content. The dialog element is not. Dialog makes the rest of the page inactive, popover doesn’t.