CSS and Next.js updates, web accessibility principles, dynamic font scaling and efficient typing for redux-thunk

Artur V, lead frontend developer
Artur V, lead frontend developer
Sep 26, 2023
15 minutes
Stereotypes that developers are mostly reserved introverts are sometimes not stereotypes at all. Therefore, in order to motivate the team and instill knowledge sharing within the company, we decided to regularly organize Family Frontend Meetup.
The CEO and devrel have Napoleonic plans - they want to do a completely public event later. Sharing knowledge and expanding your circle of acquaintances have always been useful for growth and development. But this is all in the future. In the meantime, we share the topics and findings that we discussed in the summer.

Which component to choose for image processing?

We have a project where we need to load and process a large number of images. We were looking for ways to improve this process.
As a result, blurhash technology was chosen to implement smooth loading of images. Instead of displaying boring gray rectangles during loading, we show users a blurred preview of the future image, and then load it in its entirety.

What's new with Next.js?

Enough time has passed since the release of the latest version to test it on new projects.
Here are the differences we noticed:
New application router (app directory), created based on React server components. Maintains loading status, errors and more.
New way of sampling data. Instead of initialization functions, a function like fetch(API_URL, { cache: 'no-store', next: { revalidate: 10 } }), with which you can work with the cache, perform revalidation and many other things
Each component can be made server or client. By default, all components are server-based. Components can be rendered on the server separately from each other with their own loading state. Data sampling can be done at the component level.
Improved optimization of the <Image /> component and fonts using @next/font.
Nevertheless, testing the new version of the framework on a real project showed that despite the advantages and all sorts of improvements, there are a number of disadvantages, and quite serious ones. At least we decided not to use version 13 in commercial projects and wait for a more stable version. Let's tell you why.
What we didn't like:
Completely new application structure. It takes a lot of resources to learn.
Changes in next router. The router in its usual sense was divided into separate hooks, for example: useParams, usePathname, useSearchParams. The router itself is a set of methods for working with navigation, and the methods are partially truncated. Do you still remember the shallow parameter in router.push? Now he's gone.
Localization. We use the popular i18next library in our projects. In the current Next.js implementation, localization integration looks extremely cumbersome. In previous versions, it was necessary to create a configuration file with a couple of lines, wrap the application entry point in a special wrapper imported from the library, and create the localization files themselves. Now we are forced to use an additional library, configure a middleware file, write an additional hook and a bunch of additional logic.
CSS-in-JS currently only works with client-side components; many reused components had to be rewritten in SCSS.
Redux. In previous versions, it was possible to implement caching and bind the server to the client via getInitialProps, or by using hydration when using getServerSideProps. In the current version, no solution for implementing caching was found.
You can read more here:

New CSS Features Overview

We have explored the latest advances in CSS, including scroll-snap, @container, @layer and CSS nesting. We looked at how to improve the user experience using innovative styling options and layout techniques.
Read more here:
scroll-snap — allows you to make a simple slider in a couple of lines. Sometimes in a mobile adaptation you need to make a regular slider, but you wouldn’t want to use a third-party library or write heavy logic for this task. A new CSS feature comes to the rescue, which allows you to implement smooth switching of slides using the overflow property.
The parent block receives the styles:
overflow-x: auto и scroll-snap-type: x mandatory
Child elements receive styles:
scroll-snap-stop: always и scroll-snap-align: start
The output is the following:
@container — is a long-awaited feature that has recently become supported by all major browsers. It helps to make adaptation and not only that, depending on the parameters of a certain block, and not just based on the viewport values.

Why might this be needed?

For example, you have one card that should look different in two different places, the first thing that comes to mind is to pass a certain key through props, which can be used to navigate and change the component. It does, but now there is a more elegant solution using native css.
Let's make the parent block a container:

container: cart-popup-container / inline-size;
Let's adapt the element inside the container:
@container cart-popup-container (width < 475px) {
	color: black;
	font-size: 14;
You can see an example in the photo below:
As you can see, the same card is used in two different places, with the help of @container we changed it to suit our needs in the popup on the top right.
You can read more here:
Сss layers — are an additional way to control css cascading. Any styles declared outside the layer will override styles declared in the layer, regardless of their specificity.
You can read more here:
CSS Nesting — Google developers have been working for many years on implementing nesting in native CSS as a replacement for preprocessors. At the moment, it is already supported by the latest versions of the Chrome, Safari and Firefox browsers, which prompted us to discuss the prospects of this feature and its possible implementation in our projects.

Main points:

You should always use & (ampersand), you can’t just put a selector inside.
The ability to style the states of an element, its pseudo-elements and child elements, using the style attribute.
Lack of many features and functions available in scss.
We lose the need for additional manipulations in the form of installing and configuring the preprocessor in your working environment.
Too crude to use on real projects.
You can read more here:

How to implement dynamic font scaling?

We looked at an interesting technique for adapting fonts for different screens using dynamic scaling. This approach helps improve the user experience across devices with minimal developer effort.
This approach for adapting fonts in web applications uses CSS rules and media queries to dynamically adjust the font size depending on the viewport width. It allows you to create responsive behavior for fonts so that they scale optimally and remain readable on different devices and screens.
The formula looks like this:

calc([min font-size] + ([max font-size] - [min font-size]) * ((curr viewport width - [min viewport width]) / ([max viewport width] - [min viewport width])));
Example of use in a real project:

html {
  font-size: 10px;

  @media only screen and (min-width: 1113px) and (max-width: 1440px) {
    font-size: calc(6.5px + 3.5 * ((100vw - 1113px) / (1440 - 1113)));

  @media only screen and (min-width: 835px) and (max-width: 1112px) {
    font-size: calc(7.5px + 2.5 * ((100vw - 835px) / (1112 - 835)));
  @media only screen and (max-width: 834px) {
    font-size: calc(10px + 1.5 * ((100vw - 375px) / (834 - 375)));

body {
  font-size: 1.6rem;

What are the “Web Accessibility Guidelines”?

Web accessibility principles help ensure inclusivity for all users. Their popularity is growing every year, so the most important points were discussed at the meetup.
We have identified 7 main approaches for our projects:
Anything a user can do with a mouse click can be done with a keyboard. Adds the :focus-visible pseudo-class to handle keyboard focus.
Each input must be associated with a label tag, which allows screen readers to read the purpose of the input.
Role attribute + tabIndex attribute for creating interactive elements with non-interactive HTML tags. Situations arise when you need to make some component clickable, for example a product card. Instead of wrapping it in a <button/> tag, you can add the attributes described above and get an interactive, clickable, semantically correct element.
aria-label and aria-hidden to add a description of the element for screen readers and hide the element. A live example of use is a link to a social network in the form of an svg icon. The screen reader automatically reads the content of the <a> tag, but if there is no content, then there is nothing to read. In this situation, you can use aria-label so that various robots understand where the link leads and inform the user about it. The aria-hidden attribute can be used to hide non-interactive content from the Accessibility API. For example, inside such a link we have an image that the robot will try to read. To avoid this situation, we use this attribute.
The alt attribute must be present in every image. There is nothing to explain here. Just an axiom that needs to be remembered. This attribute is indexed and read by all robots; if it is absent, the image becomes invisible.
role="alert" or role="status". Used to notify users when notifications and errors occur. When using “alert”, the screen reader interrupts reading any other information and urgently reads out the content that appears; when using “status”, the content is read out after completing current tasks.
<dialog/> as modal windows. If you replace div with dialog, you can get the ability to open/close a modal window out of the box, including using the keyboard. For example, you can press esc to close.

Efficient typing for redux-thunk and use of condition

When typing redux-thunk, it's easiest to use any, but we've explored how you can get the typing right and organize your code better so that it's safe and clear when you're working with asynchronous actions when using redux-thunk.
This can be useful, for example, for scenarios where executing an action or generating a payload is only required under certain conditions.
Below is an example of a typed thunk.
With a generic passed to createAsyncThunk, you can describe the return value, the parameters passed, and a third optional parameter that can be used to customize the behavior of your Thunk.
This option allows you to attach additional data or configuration to the payload creation function and affect how your asynchronous operation is processed.
interface ServiceParams {
  	sessionToken: CookieValueTypes;

  export type AsyncThunkApi = {
rejectValue: T;
state: AppState;

  export type AsyncThunkCachedResponse = {
data: T;
key: string;

  export const fetchNews = createAsyncThunk<
 	async (thunkParams, thunkApi) => {
const { rejectWithValue, dispatch, getState } = thunkApi;

try {
// your logic…
} catch (e) {
return rejectWithValue(String(e));
   condition: (thunkParams, thunkApi) => {
const { getState } = thunkApi;
const key = JSON.stringify(thunkParams);
const news = getNewsByKey(getState().news, key);

if (news) {
    return false;
condition — is an option to cancel the thunk before the payload creator is called. This can be useful, for example, to cancel a query that is already in progress or if the query has already been executed in advance and the result is already in storage.
You can read more here
If you are interested in such reviews or development articles, follow us on Twitter and stay tuned for updates