Careful development of a high-quality design

Post pobrano z: Careful development of a high-quality design

Developing a design project is a unique and creative process, a symbiosis of the work of the customers and designers in London. Implementing a design project carried out to order in the interior design studio depends on many nuances: the number of changes made during the work, the speed of response to customer changes, and the availability of finishing materials in stock. Studio specialists usually take two to three months to develop a design project. The advantage of working with a creative interior design studio is that you can order the development of your design project according to your own unique needs and, if necessary, make changes during the execution of the design project. Thus, you will be able to organize your time as rationally as possible and calmly watch how an experienced team creates a unique interior solution in their studio. Wherever you are, studio managers are always ready to help and solve any problems related to contact designers. They strive to coordinate the work related to the project, which provides the opportunity to achieve a positive result in the shortest possible time. When designing the interior of another room, apartment, country house, or commercial building, only professionals can harmoniously organize the space between functionality, aesthetics, and ergonomics, so it is worth using the services of qualified designers with considerable experience in this area. Experienced specialists know all the tricks and nuances of working with light, textures, and textiles so that you can get the best from every design solution. A designer is someone who knows everything about fashion trends in interior design and tries to adapt them as best as possible to the individuality and taste of clients, thus bringing originality to their design projects.

What are the prices for interior design?

Creating a development project plan is free and requires nothing. It is essential to meet and decide the process of creating an interior following your taste, preferences, and wishes. The purpose of the first meeting is to get to know the client, determine the room’s dimensions, create a general concept of the planned design, and select all the nuances and characteristics of the given space. The finished room fully corresponds to the conceived idea for the project implementation to bring aesthetic satisfaction. It is essential to decide on the tastes and wishes of the client. The first visit to the object is necessary because this visit allows the team to immediately see the next steps for the successful implementation of the project conceived by the client. Then the development of several critical options for interior design takes place. If you like the plan, a contract is signed for the execution of the assigned works. An author’s solution is a service of a design studio that helps to achieve the desired results after the implementation of an interior design project. Using this option ensures the compatibility of the out-of-the-box visualization solution and saves valuable time.

It is creating an interior design that inspires admiration!

An impressive combination of comfort and elegance, details and unchanging style, aesthetics, and functionality are the main requirements that a team of qualified designers fulfills when creating an interior design. Find out how you live, your plans for the future, why you need repairs, and what tasks you entrust to designers to make the most original interior design. This is the only way to set priorities and create a solution that inspires you to new achievements. London is a city where dreams come true. The interior design of London flats should be appropriate: entirely in line with the taste and preferences, habits, taste, temperament, character, and lifestyle of the owner. Before starting work on the project, it is essential to know the likes and wishes of the client. The style can be coordinated with the designers. Technical tasks also require planning engineering and technical solutions in the project. These include underfloor heating, ventilation, or air conditioning.

Stages of work on an interior design project

Thanks to the team of experienced designers, the work processes are transparent and controlled. The contractor creates an action plan, which the client manages. He requests a schedule of work stages and decisions made by designers in the design project. As a rule, the design functions corresponding to the design project are assigned by the customer – in the author supervision process, the designer initiates the project. Following the developed draft project, the contractors (according to their forecasts) estimated their work performance. This is, for example, the manufacture and installation of stretch ceilings, air conditioning and ventilation systems, home theaters, doors, stairs, and walls. The design studio develops unique interior items to order.

The post Careful development of a high-quality design appeared first on Designer Daily: graphic and web design blog.

The Double Emphasis Thing

Post pobrano z: The Double Emphasis Thing

I used to have this boss who loved, loved, loved, loved to emphasize words. This was way back before we used a WYSIWYG editors and I’d have to handcode that crap.

<p>
  I used to have this boss who <em>loved</em>, <strong>loved</strong>, 
  <strong><em>loved</em></strong>, <strong><em><u>loved</u></em></strong> 
  to emphasize words.
</p>

(Let’s not go into the colors he used for even MOAR emphasis.)

Writing all that markup never felt great. The effort it took, sure, whatever. But is it even a good idea to add overload content with double — or more! — emphases?

Different tags convey different emphasis

For starters, the <strong> and <em> tags are designed for different uses. We got them back in HTML5, where:

So, <strong> gives the content more weight in the sense it suggests that the content in it is important or urgent. Think of a warning:

Warning: The following content has been flagged for being awesome.

It might be tempting to reach for <em> to do the same thing. Italicized text can be attention-grabbing after all. But it’s really meant as a hint to use more emphasis when readingt the content in it. For example, here are two versions of the same sentence with the emphasis in different locations:

<p>I ate the <em>entire</em> plate of burritos.</p>
<p>I ate the entire <em>plate</em> of burritos.</p>

Both examples stress emphasis, but on different words. And they would sound different if you were to read them out loud. That makes <em> a great way to express tone in your writing. It changes the meaning of the sentence in a way that <strong> does not.

Visual emphasis vs. semantic emphasis

Those are two things you gotta weigh when emphasizing content. Like, there are plenty of instances where you may need to italicize content without affecting the meaning of the sentence. But those can be handled with other tags that render italics:

  • <i>: This is the classic one! Before HTML5, this was used to stress emphasis with italics all over the place. Now, it’s purely used to italicize content visually without changing the semantic meaning.
  • <cite>: Indicating the source of a fact or figure. (“Source: CSS-Tricks“)
  • <address>: Used to mark up contact information, not only physical addresses, but things like email addresses and phone numbers too. (
    howdy@example.com

    )

It’s going to he the same thing with <strong>. Rather than using it for styling text you want to look heavier, it’s a better idea to use the classic <b> tag for boldfacing to avoid giving extra signficance to content that doesn’t need it. And remember, some elements like headings are already rendered in bold, thanks to the browser’s default styles. There’s no need to add even more strong emphasis.

Using italics in emphasized content (and vice versa)

There are legitimate cases where you may need to italicize part of a line that’s already emphasized. Or maybe add emphasis to a bit of text that’s already italicized.

A blockquote might be a good example. I’ve seen plenty of times where they are italicized for style, even though default browser styles don’t do it:

blockquote {
  font-style: italic;
}

What if we need to mention a movie title in that blockquote? That should be italicized. There’s no stress emphasis needed, so an <i> tag will do. But it’s still weird to italicize something when it’s already rendered that way:

<blockquote>
  This movie’s opening weekend performance offers some insight in
  to its box office momentum as it fights to justify its enormous 
  budget. In its first weekend, <i>Avatar: The Way of Water</i> made 
  $134 million in North America alone and $435 million globally.
</blockquote>

In a situation where we’re italicizing something within italicized content like this, we’re supposed to remove the italics from the nested element… <i> in this case.

blockquote i {
  font-style: normal;
}

Container style queries will be super useful to nab all these instances if we get them:

blockquote {
  container-name: quote;
  font-style: italic;
}

@container quote (font-style: italic) {
  em, i, cite, address {
    font-style: normal;
  }
}

This little snippet evaluates the blockquote to see if it’s font-style is set to italic. If it is, then it’ll make sure the <em>, <i>, <cite>, and <address> elements are rendered as normal text, while retaining the semantic meaning if there is one.

But back to emphasis within emphasis

I wouldn’t nest <strong> inside <em> like this:

<p>I ate the <em><strong>entire</strong></em> plate of burritos.</p>

…or nest <em> inside <strong> instead:

<p>I ate the <em><strong>entire</strong></em> plate of burritos.</p>

The rendering is fine! And it doesn’t matter what order they’re in… at least in modern browsers. Jennifer Kyrnin mentions that some browsers only render the tag nearest to the text, but I didn’t bump into that anywhere in my limited tests. But something to watch for!

The reason I wouldn’t nest one form of emphasis in another is because it simply isn’t needed. There is no grammar rule that calls for it. Like exclamation points, one form of emphasis is enough, and you ought to use the one that matches what you’re after whether it’s visual, weight, or announced emphasis.

And even though some screen readers are capable of announcing emphasized content, they won’t read the markup with any additional importance or emphasis. So, no additional accessibility perks either, as far as I can tell.

But I really want all the emphasis!

If you’re in the position where your boss is like mine and wants ALL the emphasis, I’d reach for the right HTML tag for the type of emphasis, then apply the rest of the styles with a mix of tags that don’t affect semantics with CSS to help account for anything browser styles won’t handle.

<style>
  /* If `em` contains `b` or `u` tags */
  em:has(b, u) {
    color: #f8a100;
  }
</style>

<p>
  I used to have this boss who <em>loved</em>, <strong>loved</strong>, 
  <strong><em>loved</em></strong>, <strong><em><u>loved</u></em></strong> 
  to emphasize words.
</p>

I might even do it with the <strong> tag too as a defensive measure:

/* If `em` contains `b` or `u` tags */
em:has(b, u),
/* If `strong` contains `em` or `u` tags */
strong:has(i, u) {
  color: #f8a100;
}

As long as we’re playing defense, we can identify errors where emphases are nested within emphases by highlighting them in red or something:

/* Highlight semantic emphases within semantic emphases */
em:has(strong),
strong:has(em) {
  background: hsl(0deg 50% 50% / .25);
  border: 1px dashed hsl(0deg 50% 50% / .25);
}

Then I’d probably use that snippet from the last section that removes the default italic styling from an element when it is nested in another italiczed element.

Anything else?

Mayyyyybe:


The Double Emphasis Thing originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

A Fancy Hover Effect For Your Avatar

Post pobrano z: A Fancy Hover Effect For Your Avatar

Do you know that kind of effect where someone’s head is poking through a circle or hole? The famous Porky Pig animation where he waves goodbye while popping out of a series of red rings is the perfect example, and Kilian Valkhof actually re-created that here on CSS-Tricks a while back.

I have a similar idea but tackled a different way and with a sprinkle of animation. I think it’s pretty practical and makes for a neat hover effect you can use on something like your own avatar.

CodePen Embed Fallback

See that? We’re going to make a scaling animation where the avatar seems to pop right out of the circle it’s in. Cool, right? Don’t look at the code and let’s build this animation together step-by-step.

The HTML: Just one element

If you haven’t checked the code of the demo and you are wondering how many divs this’ll take, then stop right there, because our markup is nothing but a single image element:

<img src="" alt="">

Yes, a single element! The challenging part of this exercise is using the smallest amount of code possible. If you have been following me for a while, you should be used to this. I try hard to find CSS solutions that can be achieved with the smallest, most maintainable code possible.

I wrote a series of articles here on CSS-Tricks where I explore different hover effects using the same HTML markup containing a single element. I go into detail on gradients, masking, clipping, outlines, and even layout techniques. I highly recommend checking those out because I will re-use many of the tricks in this post.

An image file that’s square with a transparent background will work best for what we’re doing. Here’s the one I’m using if you want start with that.

Designed by Cang

I’m hoping to see lots of examples of this as possible using real images — so please share your final result in the comments when you’re done so we can build a collection!

Before jumping into CSS, let’s first dissect the effect. The image gets bigger on hover, so we’ll for sure use transform: scale() in there. There’s a circle behind the avatar, and a radial gradient should do the trick. Finally, we need a way to create a border at the bottom of the circle that creates the appearance of the avatar behind the circle.

Let’s get to work!

The scale effect

Let’s start by adding the transform:

img {
  width: 280px;
  aspect-ratio: 1;
  cursor: pointer;
  transition: .5s;
}
img:hover {
  transform: scale(1.35);
}
CodePen Embed Fallback

Nothing complicated yet, right? Let’s move on.

The circle

We said that the background would be a radial gradient. That’s perfect because we can create hard stops between the colors of a radial gradient, which make it look like we’re drawing a circle with solid lines.

img {
  --b: 5px; /* border width */

  width: 280px;
  aspect-ratio: 1;
  background:
    radial-gradient(
      circle closest-side,
      #ECD078 calc(99% - var(--b)),
      #C02942 calc(100% - var(--b)) 99%,
      #0000
    );
  cursor: pointer;
  transition: .5s;
}
img:hover {
  transform: scale(1.35);
}

Note the CSS variable, --b, I’m using there. It represents the thickness of the “border” which is really just being used to define the hard color stops for the red part of the radial gradient.

CodePen Embed Fallback

The next step is to play with the gradient size on hover. The circle needs to keep its size as the image grows. Since we are applying a scale() transformation, we actually need to decrease the size of the circle because it otherwise scales up with the avatar. So, while the image scales up, we need the gradient to scale down.

Let’s start by defining a CSS variable, --f, that defines the “scale factor”, and use it to set the size of the circle. I’m using 1 as the default value, as in that’s the initial scale for the image and the circle that we transform from.

Here is a demo to illustrate the trick. Hover to see what is happening behind the scenes:

CodePen Embed Fallback

I added a third color to the radial-gradient to better identify the area of the gradient on hover:

radial-gradient(
  circle closest-side,
  #ECD078 calc(99% - var(--b)),
  #C02942 calc(100% - var(--b)) 99%,
  lightblue
);

Now we have to position our background at the center of the circle and make sure it takes up the full height. I like to declare everything directly on the background shorthand property, so we can add our background positioning and make sure it doesn’t repeat by tacking on those values right after the radial-gradient():

background: radial-gradient() 50% / calc(100% / var(--f)) 100% no-repeat;

The background is placed at the center (50%), has a width equal to calc(100%/var(--f)), and has a height equal to 100%.

Nothing scales when --f is equal to 1 — again, our initial scale. Meanwhile, the gradient takes up the full width of the container. When we increase --f, the element’s size grows — thanks to the scale() transform — and the gradient’s size decreases.

Here’s what we get when we apply all of this to our demo:

CodePen Embed Fallback

We’re getting closer! We have the overflow effect at the top, but we still need to hide the bottom part of the image, so it looks like it is popping out of the circle rather than sitting in front of it. That’s the tricky part of this whole thing and is what we’re going to do next.

The bottom border

I first tried tackling this with the border-bottom property, but I was unable to find a way to match the size of the border to the size to the circle. Here’s the best I could get and you can immediately see it’s wrong:

CodePen Embed Fallback

The actual solution is to use the outline property. Yes, outline, not border. In a previous article, I show how outline is powerful and allows us to create cool hover effects. Combined with outline-offset, we have exactly what we need for our effect.

The idea is to set an outline on the image and adjust its offset to create the bottom border. The offset will depend on the scaling factor the same way the gradient size did.

CodePen Embed Fallback

Now we have our bottom “border” (actually an outline) combined with the “border” created by the gradient to create a full circle. We still need to hide portions of the outline (from the top and the sides), which we’ll get to in a moment.

Here’s our code so far, including a couple more CSS variables you can use to configure the image size (--s) and the “border” color (--c):

img {
  --s: 280px; /* image size */
  --b: 5px; /* border thickness */
  --c: #C02942; /* border color */
  --f: 1; /* initial scale */

  width: var(--s);
  aspect-ratio: 1;
  cursor: pointer;
  border-radius: 0 0 999px 999px;
  outline: var(--b) solid var(--c);
  outline-offset: calc((1 / var(--f) - 1) * var(--s) / 2 - var(--b));
  background: 
    radial-gradient(
      circle closest-side,
      #ECD078 calc(99% - var(--b)),
      var(--c) calc(100% - var(--b)) 99%,
      #0000
    ) 50% / calc(100% / var(--f)) 100% no-repeat;
  transform: scale(var(--f));
  transition: .5s;
}
img:hover {
  --f: 1.35; /* hover scale */
}

Since we need a circular bottom border, we added a border-radius on the bottom side, allowing the outline to match the curvature of the gradient.

The calculation used on outline-offset is a lot more straightforward than it looks. By default, outline is drawn outside of the element’s box. And in our case, we need it to overlap the element. More precisely, we need it to follow the circle created by the gradient.

Diagram of the background transition.

When we scale the element, we see the space between the circle and the edge. Let’s not forget that the idea is to keep the circle at the same size after the scale transformation runs, which leaves us with the space we will use to define the outline’s offset as illustrated in the above figure.

Let’s not forget that the second element is scaled, so our result is also scaled… which means we need to divide the result by f to get the real offset value:

Offset = ((f - 1) * S/2) / f = (1 - 1/f) * S/2

We add a negative sign since we need the outline to go from the outside to the inside:

Offset = (1/f - 1) * S/2

Here’s a quick demo that shows how the outline follows the gradient:

CodePen Embed Fallback

You may already see it, but we still need the bottom outline to overlap the circle rather than letting it bleed through it. We can do that by removing the border’s size from the offset:

outline-offset: calc((1 / var(--f) - 1) * var(--s) / 2) - var(--b));
CodePen Embed Fallback

Now we need to find how to remove the top part from the outline. In other words, we only want the bottom part of the image’s outline.

First, let’s add space at the top with padding to help avoid the overlap at the top:

img {
  --s: 280px; /* image size */
  --b: 5px;   /* border thickness */
  --c: #C02942; /* border color */
  --f: 1; /* initial scale */

  width: var(--s);
  aspect-ratio: 1;
  padding-block-start: calc(var(--s)/5);
  /* etc. */
}
img:hover {
  --f: 1.35; /* hover scale */
}
CodePen Embed Fallback

There is no particular logic to that top padding. The idea is to ensure the outline doesn’t touch the avatar’s head. I used the element’s size to define that space to always have the same proportion.

Note that I have added the content-box value to the background:

background:
  radial-gradient(
    circle closest-side,
    #ECD078 calc(99% - var(--b)),
    var(--c) calc(100% - var(--b)) 99%,
    #0000
  ) 50%/calc(100%/var(--f)) 100% no-repeat content-box;

We need this because we added padding and we only want the background set to the content box, so we must explicitly tell the background to stop there.

Adding CSS mask to the mix

We reached the last part! All we need to do is to hide some pieces, and we are done. For this, we will rely on the mask property and, of course, gradients.

Here is a figure to illustrate what we need to hide or what we need to show to be more accurate

Showing how the mask applies to the bottom portion of the circle.

The left image is what we currently have, and the right is what we want. The green part illustrates the mask we must apply to the original image to get the final result.

We can identify two parts of our mask:

  • A circular part at the bottom that has the same dimension and curvature as the radial gradient we used to create the circle behind the avatar
  • A rectangle at the top that covers the area inside the outline. Notice how the outline is outside the green area at the top — that’s the most important part, as it allows the outline to be cut so that only the bottom part is visible.

Here’s our final CSS:

img {
  --s: 280px; /* image size */
  --b: 5px; /* border thickness */
  --c: #C02942; /* border color */
  --f: 1; /* initial scale */

  --_g: 50% / calc(100% / var(--f)) 100% no-repeat content-box;
  --_o: calc((1 / var(--f) - 1) * var(--s) / 2 - var(--b));

  width: var(--s);
  aspect-ratio: 1;
  padding-top: calc(var(--s)/5);
  cursor: pointer;
  border-radius: 0 0 999px 999px;
  outline: var(--b) solid var(--c);
  outline-offset: var(--_o);
  background: 
    radial-gradient(
      circle closest-side,
      #ECD078 calc(99% - var(--b)),
      var(--c) calc(100% - var(--b)) 99%,
      #0000) var(--_g);
  mask:
    linear-gradient(#000 0 0) no-repeat
    50% calc(-1 * var(--_o)) / calc(100% / var(--f) - 2 * var(--b)) 50%,
    radial-gradient(
      circle closest-side,
      #000 99%,
      #0000) var(--_g);
  transform: scale(var(--f));
  transition: .5s;
}
img:hover {
  --f: 1.35; /* hover scale */
}

Let’s break down that mask property. For starters, notice that a similar radial-gradient() from the background property is in there. I created a new variable, --_g, for the common parts to make things less cluttered.

--_g: 50% / calc(100% / var(--f)) 100% no-repeat content-box;

mask:
  radial-gradient(
    circle closest-side,
    #000 99%,
    #0000) var(--_g);

Next, there’s a linear-gradient() in there as well:

--_g: 50% / calc(100% / var(--f)) 100% no-repeat content-box;

mask:
  linear-gradient(#000 0 0) no-repeat
    50% calc(-1 * var(--_o)) / calc(100% / var(--f) - 2 * var(--b)) 50%,
  radial-gradient(
    circle closest-side,
    #000 99%,
    #0000) var(--_g);

This creates the rectangle part of the mask. Its width is equal to the radial gradient’s width minus twice the border thickness:

calc(100% / var(--f) - 2 * var(--b))

The rectangle’s height is equal to half, 50%, of the element’s size.

We also need the linear gradient placed at the horizontal center (50%) and offset from the top by the same value as the outline’s offset. I created another CSS variable, --_o, for the offset we previously defined:

--_o: calc((1 / var(--f) - 1) * var(--s) / 2 - var(--b));

One of the confusing things here is that we need a negative offset for the outline (to move it from outside to inside) but a positive offset for the gradient (to move from top to bottom). So, if you’re wondering why we multiply the offset, --_o, by -1, well, now you know!

Here is a demo to illustrate the mask’s gradient configuration:

CodePen Embed Fallback

Hover the above and see how everything move together. The middle box illustrates the mask layer composed of two gradients. Imagine it as the visible part of the left image, and you get the final result on the right!

Wrapping up

Oof, we’re done! And not only did we wind up with a slick hover animation, but we did it all with a single HTML <img> element. Just that and less than 20 lines of CSS trickery!

Sure, we relied on some little tricks and math formulas to reach such a complex effect. But we knew exactly what to do since we identified the pieces we needed up-front.

Could we have simplified the CSS if we allowed ourselves more HTML? Absolutely. But we’re here to learn new CSS tricks! This was a good exercise to explore CSS gradients, masking, the outline property’s behavior, transformations, and a whole bunch more. If you felt lost at any point, then definitely check out my series that uses the same general concepts. It sometimes helps to see more examples and use cases to drive a point home.

I will leave you with one last demo that uses photos of popular CSS developers. Don’t forget to show me a demo with your own image so I can add it to the collection!

CodePen Embed Fallback

A Fancy Hover Effect For Your Avatar originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Caching Data in SvelteKit

Post pobrano z: Caching Data in SvelteKit

My previous post was a broad overview of SvelteKit where we saw what a great tool it is for web development. This post will fork off what we did there and dive into every developer’s favorite topic: caching. So, be sure to give my last post a read if you haven’t already. The code for this post is available on GitHub, as well as a live demo.

This post is all about data handling. We’ll add some rudimentary search functionality that will modify the page’s query string (using built-in SvelteKit features), and re-trigger the page’s loader. But, rather than just re-query our (imaginary) database, we’ll add some caching so re-searching prior searches (or using the back button) will show previously retrieved data, quickly, from cache. We’ll look at how to control the length of time the cached data stays valid and, more importantly, how to manually invalidate all cached values. As icing on the cake, we’ll look at how we can manually update the data on the current screen, client-side, after a mutation, while still purging the cache.

This will be a longer, more difficult post than most of what I usually write since we’re covering harder topics. This post will essentially show you how to implement common features of popular data utilities like react-query; but instead of pulling in an external library, we’ll only be using the web platform and SvelteKit features.

Unfortunately, the web platform’s features are a bit lower level, so we’ll be doing a bit more work than you might be used to. The upside is we won’t need any external libraries, which will help keep bundle sizes nice and small. Please don’t use the approaches I’m going to show you unless you have a good reason to. Caching is easy to get wrong, and as you’ll see, there’s a bit of complexity that’ll result in your application code. Hopefully your data store is fast, and your UI is fine allowing SvelteKit to just always request the data it needs for any given page. If it is, leave it alone. Enjoy the simplicity. But this post will show you some tricks for when that stops being the case.

Speaking of react-query, it was just released for Svelte! So if you find yourself leaning on manual caching techniques a lot, be sure to check that project out, and see if it might help.

Setting up

Before we start, let’s make a few small changes to the code we had before. This will give us an excuse to see some other SvelteKit features and, more importantly, set us up for success.

First, let’s move our data loading from our loader in +page.server.js to an API route. We’ll create a +server.js file in routes/api/todos, and then add a GET function. This means we’ll now be able to fetch (using the default GET verb) to the /api/todos path. We’ll add the same data loading code as before.

import { json } from "@sveltejs/kit";
import { getTodos } from "$lib/data/todoData";

export async function GET({ url, setHeaders, request }) {
  const search = url.searchParams.get("search") || "";

  const todos = await getTodos(search);

  return json(todos);
}

Next, let’s take the page loader we had, and simply rename the file from +page.server.js to +page.js (or .ts if you’ve scaffolded your project to use TypeScript). This changes our loader to be a “universal” loader rather than a server loader. The SvelteKit docs explain the difference, but a universal loader runs on both the server and also the client. One advantage for us is that the fetch call into our new endpoint will run right from our browser (after the initial load), using the browser’s native fetch function. We’ll add standard HTTP caching in a bit, but for now, all we’ll do is call the endpoint.

export async function load({ fetch, url, setHeaders }) {
  const search = url.searchParams.get("search") || "";

  const resp = await fetch(`/api/todos?search=${encodeURIComponent(search)}`);

  const todos = await resp.json();

  return {
    todos,
  };
}

Now let’s add a simple form to our /list page:

<div class="search-form">
  <form action="/list">
    <label>Search</label>
    <input autofocus name="search" />
  </form>
</div>

Yep, forms can target directly to our normal page loaders. Now we can add a search term in the search box, hit Enter, and a “search” term will be appended to the URL’s query string, which will re-run our loader and search our to-do items.

Search form

Let’s also increase the delay in our todoData.js file in /lib/data. This will make it easy to see when data are and are not cached as we work through this post.

export const wait = async amount => new Promise(res => setTimeout(res, amount ?? 500));

Remember, the full code for this post is all on GitHub, if you need to reference it.

Basic caching

Let’s get started by adding some caching to our /api/todos endpoint. We’ll go back to our +server.js file and add our first cache-control header.

setHeaders({
  "cache-control": "max-age=60",
});

…which will leave the whole function looking like this:

export async function GET({ url, setHeaders, request }) {
  const search = url.searchParams.get("search") || "";

  setHeaders({
    "cache-control": "max-age=60",
  });

  const todos = await getTodos(search);

  return json(todos);
}

We’ll look at manual invalidation shortly, but all this function says is to cache these API calls for 60 seconds. Set this to whatever you want, and depending on your use case, stale-while-revalidate might also be worth looking into.

And just like that, our queries are caching.

Cache in DevTools.

Note make sure you un-check the checkbox that disables caching in dev tools.

Remember, if your initial navigation on the app is the list page, those search results will be cached internally to SvelteKit, so don’t expect to see anything in DevTools when returning to that search.

What is cached, and where

Our very first, server-rendered load of our app (assuming we start at the /list page) will be fetched on the server. SvelteKit will serialize and send this data down to our client. What’s more, it will observe the Cache-Control header on the response, and will know to use this cached data for that endpoint call within the cache window (which we set to 60 seconds in put example).

After that initial load, when you start searching on the page, you should see network requests from your browser to the /api/todos list. As you search for things you’ve already searched for (within the last 60 seconds), the responses should load immediately since they’re cached.

What’s especially cool with this approach is that, since this is caching via the browser’s native caching, these calls could (depending on how you manage the cache busting we’ll be looking at) continue to cache even if you reload the page (unlike the initial server-side load, which always calls the endpoint fresh, even if it did it within the last 60 seconds).

Obviously data can change anytime, so we need a way to purge this cache manually, which we’ll look at next.

Cache invalidation

Right now, data will be cached for 60 seconds. No matter what, after a minute, fresh data will be pulled from our datastore. You might want a shorter or longer time period, but what happens if you mutate some data and want to clear your cache immediately so your next query will be up to date? We’ll solve this by adding a query-busting value to the URL we send to our new /todos endpoint.

Let’s store this cache busting value in a cookie. That value can be set on the server but still read on the client. Let’s look at some sample code.

We can create a +layout.server.js file at the very root of our routes folder. This will run on application startup, and is a perfect place to set an initial cookie value.

export function load({ cookies, isDataRequest }) {
  const initialRequest = !isDataRequest;

  const cacheValue = initialRequest ? +new Date() : cookies.get("todos-cache");

  if (initialRequest) {
    cookies.set("todos-cache", cacheValue, { path: "/", httpOnly: false });
  }

  return {
    todosCacheBust: cacheValue,
  };
}

You may have noticed the isDataRequest value. Remember, layouts will re-run anytime client code calls invalidate(), or anytime we run a server action (assuming we don’t turn off default behavior). isDataRequest indicates those re-runs, and so we only set the cookie if that’s false; otherwise, we send along what’s already there.

The httpOnly: false flag is also significant. This allows our client code to read these cookie values in document.cookie. This would normally be a security concern, but in our case these are meaningless numbers that allow us to cache or cache bust.

Reading cache values

Our universal loader is what calls our /todos endpoint. This runs on the server or the client, and we need to read that cache value we just set up no matter where we are. It’s relatively easy if we’re on the server: we can call await parent() to get the data from parent layouts. But on the client, we’ll need to use some gross code to parse document.cookie:

export function getCookieLookup() {
  if (typeof document !== "object") {
    return {};
  }

  return document.cookie.split("; ").reduce((lookup, v) => {
    const parts = v.split("=");
    lookup[parts[0]] = parts[1];

    return lookup;
  }, {});
}

const getCurrentCookieValue = name => {
  const cookies = getCookieLookup();
  return cookies[name] ?? "";
};

Fortunately, we only need it once.

Sending out the cache value

But now we need to send this value to our /todos endpoint.

import { getCurrentCookieValue } from "$lib/util/cookieUtils";

export async function load({ fetch, parent, url, setHeaders }) {
  const parentData = await parent();

  const cacheBust = getCurrentCookieValue("todos-cache") || parentData.todosCacheBust;
  const search = url.searchParams.get("search") || "";

  const resp = await fetch(`/api/todos?search=${encodeURIComponent(search)}&cache=${cacheBust}`);
  const todos = await resp.json();

  return {
    todos,
  };
}

getCurrentCookieValue('todos-cache') has a check in it to see if we’re on the client (by checking the type of document), and returns nothing if we are, at which point we know we’re on the server. Then it uses the value from our layout.

Busting the cache

But how do we actually update that cache busting value when we need to? Since it’s stored in a cookie, we can call it like this from any server action:

cookies.set("todos-cache", cacheValue, { path: "/", httpOnly: false });

The implementation

It’s all downhill from here; we’ve done the hard work. We’ve covered the various web platform primitives we need, as well as where they go. Now let’s have some fun and write application code to tie it all together.

For reasons that’ll become clear in a bit, let’s start by adding an editing functionality to our /list page. We’ll add this second table row for each todo:

import { enhance } from "$app/forms";
<tr>
  <td colspan="4">
    <form use:enhance method="post" action="?/editTodo">
      <input name="id" value="{t.id}" type="hidden" />
      <input name="title" value="{t.title}" />
      <button>Save</button>
    </form>
  </td>
</tr>

And, of course, we’ll need to add a form action for our /list page. Actions can only go in .server pages, so we’ll add a +page.server.js in our /list folder. (Yes, a +page.server.js file can co-exist next to a +page.js file.)

import { getTodo, updateTodo, wait } from "$lib/data/todoData";

export const actions = {
  async editTodo({ request, cookies }) {
    const formData = await request.formData();

    const id = formData.get("id");
    const newTitle = formData.get("title");

    await wait(250);
    updateTodo(id, newTitle);

    cookies.set("todos-cache", +new Date(), { path: "/", httpOnly: false });
  },
};

We’re grabbing the form data, forcing a delay, updating our todo, and then, most importantly, clearing our cache bust cookie.

Let’s give this a shot. Reload your page, then edit one of the to-do items. You should see the table value update after a moment. If you look in the Network tab in DevToold, you’ll see a fetch to the /todos endpoint, which returns your new data. Simple, and works by default.

Saving data

Immediate updates

What if we want to avoid that fetch that happens after we update our to-do item, and instead, update the modified item right on the screen?

This isn’t just a matter of performance. If you search for “post” and then remove the word “post” from any of the to-do items in the list, they’ll vanish from the list after the edit since they’re no longer in that page’s search results. You could make the UX better with some tasteful animation for the exiting to-do, but let’s say we wanted to not re-run that page’s load function but still clear the cache and update the modified to-do so the user can see the edit. SvelteKit makes that possible — let’s see how!

First, let’s make one little change to our loader. Instead of returning our to-do items, let’s return a writeable store containing our to-dos.

return {
  todos: writable(todos),
};

Before, we were accessing our to-dos on the data prop, which we do not own and cannot update. But Svelte lets us return our data in their own store (assuming we’re using a universal loader, which we are). We just need to make one more tweak to our /list page.

Instead of this:

{#each todos as t}

…we need to do this since todos is itself now a store.:

{#each $todos as t}

Now our data loads as before. But since todos is a writeable store, we can update it.

First, let’s provide a function to our use:enhance attribute:

<form
  use:enhance={executeSave}
  on:submit={runInvalidate}
  method="post"
  action="?/editTodo"
>

This will run before a submit. Let’s write that next:

function executeSave({ data }) {
  const id = data.get("id");
  const title = data.get("title");

  return async () => {
    todos.update(list =>
      list.map(todo => {
        if (todo.id == id) {
          return Object.assign({}, todo, { title });
        } else {
          return todo;
        }
      })
    );
  };
}

This function provides a data object with our form data. We return an async function that will run after our edit is done. The docs explain all of this, but by doing this, we shut off SvelteKit’s default form handling that would have re-run our loader. This is exactly what we want! (We could easily get that default behavior back, as the docs explain.)

We now call update on our todos array since it’s a store. And that’s that. After editing a to-do item, our changes show up immediately and our cache is cleared (as before, since we set a new cookie value in our editTodo form action). So, if we search and then navigate back to this page, we’ll get fresh data from our loader, which will correctly exclude any updated to-do items that were updated.

The code for the immediate updates is available at GitHub.

Digging deeper

We can set cookies in any server load function (or server action), not just the root layout. So, if some data are only used underneath a single layout, or even a single page, you could set that cookie value there. Moreoever, if you’re not doing the trick I just showed manually updating on-screen data, and instead want your loader to re-run after a mutation, then you could always set a new cookie value right in that load function without any check against isDataRequest. It’ll set initially, and then anytime you run a server action that page layout will automatically invalidate and re-call your loader, re-setting the cache bust string before your universal loader is called.

Writing a reload function

Let’s wrap-up by building one last feature: a reload button. Let’s give users a button that will clear cache and then reload the current query.

We’ll add a dirt simple form action:

async reloadTodos({ cookies }) {
  cookies.set('todos-cache', +new Date(), { path: '/', httpOnly: false });
},

In a real project you probably wouldn’t copy/paste the same code to set the same cookie in the same way in multiple places, but for this post we’ll optimize for simplicity and readability.

Now let’s create a form to post to it:

<form method="POST" action="?/reloadTodos" use:enhance>
  <button>Reload todos</button>
</form>

That works!

UI after reload.

We could call this done and move on, but let’s improve this solution a bit. Specifically, let’s provide feedback on the page to tell the user the reload is happening. Also, by default, SvelteKit actions invalidate everything. Every layout, page, etc. in the current page’s hierarchy would reload. There might be some data that’s loaded once in the root layout that we don’t need to invalidate or re-load.

So, let’s focus things a bit, and only reload our to-dos when we call this function.

First, let’s pass a function to enhance:

<form method="POST" action="?/reloadTodos" use:enhance={reloadTodos}>
import { enhance } from "$app/forms";
import { invalidate } from "$app/navigation";

let reloading = false;
const reloadTodos = () => {
  reloading = true;

  return async () => {
    invalidate("reload:todos").then(() => {
      reloading = false;
    });
  };
};

We’re setting a new reloading variable to true at the start of this action. And then, in order to override the default behavior of invalidating everything, we return an async function. This function will run when our server action is finished (which just sets a new cookie).

Without this async function returned, SvelteKit would invalidate everything. Since we’re providing this function, it will invalidate nothing, so it’s up to us to tell it what to reload. We do this with the invalidate function. We call it with a value of reload:todos. This function returns a promise, which resolves when the invalidation is complete, at which point we set reloading back to false.

Lastly, we need to sync our loader up with this new reload:todos invalidation value. We do that in our loader with the depends function:

export async function load({ fetch, url, setHeaders, depends }) {
    depends('reload:todos');

  // rest is the same

And that’s that. depends and invalidate are incredibly useful functions. What’s cool is that invalidate doesn’t just take arbitrary values we provide like we did. We can also provide a URL, which SvelteKit will track, and invalidate any loaders that depend on that URL. To that end, if you’re wondering whether we could skip the call to depends and invalidate our /api/todos endpoint altogether, you can, but you have to provide the exact URL, including the search term (and our cache value). So, you could either put together the URL for the current search, or match on the path name, like this:

invalidate(url => url.pathname == "/api/todos");

Personally, I find the solution that uses depends more explicit and simple. But see the docs for more info, of course, and decide for yourself.

If you’d like to see the reload button in action, the code for it is in this branch of the repo.

Parting thoughts

This was a long post, but hopefully not overwhelming. We dove into various ways we can cache data when using SvelteKit. Much of this was just a matter of using web platform primitives to add the correct cache, and cookie values, knowledge of which will serve you in web development in general, beyond just SvelteKit.

Moreover, this is something you absolutely do not need all the time. Arguably, you should only reach for these sort of advanced features when you actually need them. If your datastore is serving up data quickly and efficiently, and you’re not dealing with any kind of scaling problems, there’s no sense in bloating your application code with needless complexity doing the things we talked about here.

As always, write clear, clean, simple code, and optimize when necessary. The purpose of this post was to provide you those optimization tools for when you truly need them. I hope you enjoyed it!


Caching Data in SvelteKit originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

The truth about CSS selector performance

Post pobrano z: The truth about CSS selector performance

Geez, leave it to Patrick Brosset to talk CSS performance in the most approachable and practical way possible. Not that CSS is always what’s gunking up the speed, or even the lowest hanging fruit when it comes to improving performance.

But if you’re looking for gains on the CSS side of things, Patrick has a nice way of sniffing out your most expensive selectors using Edge DevTools:

  • Crack open DevTools.
  • Head to the Performance Tab.
  • Make sure you have the “Enable advanced rendering instrumentation” option enabled. This tripped me up in the process.
  • Record a page load.
  • Open up the “Bottom-Up” tab in the report.
  • Check out your the size of your recalculated styles.

DevTools with Performance tab open and a summary of events.

From here, click on one of the Recalculated Style events in the Main waterfall view and you’ll get a new “Selector Stats” tab. Look at all that gooey goodness!

Now you see all of the selectors that were processed and they can be sorted by how long they took, how many times they matched, the number of matching attempts, and something called “fast reject count” which I learned is the number of elements that were easy and quick to eliminate from matching.

A lot of insights here if CSS is really the bottleneck that needs investigating. But read Patrick’s full post over on the Microsoft Edge Blog because he goes much deeper into the why’s and how’s, and walks through an entire case study.

To Shared LinkPermalink on CSS-Tricks


The truth about CSS selector performance originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.