Solar Pool Heating

Post pobrano z: Solar Pool Heating

Are you trying to maximize the use of your pool? Have you been contemplating solar heating for your pool, but are unsure where to begin? You need look no further! Here, we will discuss the advantages of solar pool heating and the numerous ways to get started.

Understand the Benefits of Solar Heating for Pools

Solar pool heating can be an effective and efficient method for year-round pool enjoyment. In addition to providing warm water for swimming, it reduces energy expenses by utilizing the sun’s natural energy. Solar pool heaters are simple to install and maintain, as well as far less expensive than conventional heating systems. Here are some of the primary advantages of solar pool heating:

Solar pool heating system is significantly less expensive than conventional heating systems, making them an excellent option to save money on your energy expenses.

Solar pool heaters utilize the sun’s natural energy, which is free and sustainable. This reduces your carbon footprint and increases the eco-friendliness of your pool.

Using a solar heater, the swimming season can be extended by up to two months. This allows you to use your pool during the cooler months.

Solar pool heaters are straightforward to install and need little upkeep. This makes them perfect for people who lack the resources or time to implement more complex systems.

Choose the Appropriate Solar Pool Heaters

Choosing the appropriate solar heater for your pool is crucial to ensure that it is heated efficiently and safely. Consider the size of your pool, the environment where you reside, and your budget when picking a solar heater.

You may need to invest in a more powerful solar warmer for larger pools. In frigid climates, you may need to invest in a solar heating system that is more sophisticated and intended to withstand extreme temperatures. There are still choices available that can deliver enough heat to keep your pool comfortable without breaking the bank if you are on a tight budget.

Before making a final decision, you should conduct research and check user reviews. Ensure that you are aware of the characteristics and limitations of each product, and that it is compatible with your pool and environment. Additionally, it is essential to evaluate the installation process and whether or not you will want expert assistance.

Once you’ve selected the ideal solar heater for your pool, you’ll be able to enjoy year-round warm swimming!

Solar Pool Heater Installation

The installation of a solar pool heater is a pretty straightforward process that can be accomplished within two days. Before beginning, it is essential to understand the system’s fundamental components and how they interact. Install the solar collectors on the roof or another suitable location where they will get direct sunlight. Ensure that the collectors are secured securely so that they do not shift or move over time.

Next, connect the pump and filter system to guarantee that the hot water created by the collectors circulates throughout the pool properly. Finally, you must connect the solar collectors to the pool itself using pipes. Installing a solar pool heater can allow you to enjoy longer swimming seasons and reduced energy expenditures.

Maintain and Clean the Solar Heater

Maintaining and cleaning your solar heater is essential to ensuring that it continues to heat your pool efficiently and effectively. Regularly examining your solar heater for signs of deterioration and removing any buildup of dirt and debris can help maintain its optimal performance. Inspect the seals on the pipes, valves, and other solar system components to ensure they are in good shape.

By carefully maintaining your solar heater, you can ensure that it functions as efficiently as possible for many years. To get the most out of your solar heater, you must frequently inspect it and remove any debris or buildup from the panels and pipes. This will help maintain your pool water warm and appealing throughout the entire season.

Insulate the Swimming Pool and Piping to Preserve Heat

When heating a swimming pool using a solar heater, it is crucial to insulate the pool and pipe system. Insulation reduces heat loss from the pool and piping system and increases the efficiency of your solar heater. Foam insulation is the most frequent type of insulation for pools and piping systems because it provides excellent protection against heat loss without requiring excessive space.

A reflective substance, such as aluminum foil, can also be used to reflect the solar heater’s heat back into the pool and pipework. With sufficient insulation, you may enjoy a comfortable and warm swimming pool for longer durations.

Adjust the Solar Heater’s Temperature Settings

Adjusting the temperature settings on your solar pool heater is essential for achieving efficient pool heating. The solar heater should be set to a suitable swimming temperature, and the temperature should be adjusted seasonally to allow for changing weather.

Once the desired temperature has been set, you will need to monitor it to verify that it is continuously maintained. You can do this manually by monitoring the pool thermometer, or you can invest in a pool automation system that enables remote temperature control and monitoring.

In addition, ensure that the solar heater is not set too high, since this might cause excessive evaporation and lengthen the time required to heat the pool. With the correct temperature settings, you may have a pleasant swimming experience throughout the entire season.

Examine the Pool and Piping System for Leaks

The proper operation of your solar heating system requires that you inspect your pool and pipe system for leaks. Pool and pipe leaks can lead to decreased efficiency and increased energy expenses. To check for leaks, you should routinely inspect the pool and its piping. Check for indicators of water loss, such as moist areas or algae growth.

If you detect any leaks in your solar heating system, it is crucial to repair them immediately to prevent further harm. Before undertaking any repairs, you should first turn off the pump and heater. Then, using a patch kit, plug the pool or pipe leak. If the repair fails, it may be necessary to replace the damaged section of pipe or liner.

Additionally, it is essential to examine all connections between the solar heating system and the pool or pipe. Ensure that all connections are tight and clear of any dirt or debris that could lead to a leak. Additionally, you must ensure that all valves are closed when not in use and open when water flow is desired.

By routinely inspecting for and correcting any leaks, you can ensure that your solar heating system is operating effectively and efficiently.

Extend Swimming Season with a Solar Heater

The optimal solar pool heater can lengthen the swimming season. A solar heater may keep your pool warm during the cooler months by utilizing the sun’s free energy. Solar heaters are inexpensive, simple to install and maintain, and temperature-adjustable. With the correct solar heater, you may swim in your pool throughout the year.

Optimize the Performance of Your SolarPool Heater

Maximizing the effectiveness of a solar heater is crucial for maintaining a regularly heated pool and minimal heating costs. Understanding how a system operates and taking efforts to enhance it are essential for optimizing efficiency.

You may enhance efficiency and reduce energy expenses by properly insulating the pool and piping system, adjusting the temperature settings on the heater, and checking for leaks. In addition, regular cleaning and maintenance of the solar heater will contribute to its continued functionality. With a few easy measures, you can maintain your solar heater’s efficiency and enjoy a warm pool throughout the year.

Compare Costs of Solar vs. Traditional Heating Systems

When it comes to pool heating, you may ask if a solar heater is the best option for you. It is essential to evaluate the prices of a solar heating system with a conventional heater. Typically, solar pool heaters are more expensive to install than conventional warmers. However, they are frequently more economical in the long run.

Solar pool heaters require no energy source other than the sun, so you do not have to worry about burning expensive fuels or purchasing expensive power. Solar pool heaters can also save maintenance expenses. Solar heaters, unlike conventional heaters, include no moving parts, thus they do not require frequent repairs or replacements. In addition to extending the swimming season and reducing energy expenditures over time, solar heaters can lengthen the swimming season.

Consider considerations such as installation costs, maintenance expenses, and energy savings when comparing the prices of solar and conventional heating systems. Ensure that you conduct research and consult with a specialist to identify the best solution for your needs. With the proper system, you may enjoy a warmer pool while saving money over time.

The post Solar Pool Heating appeared first on LatestSolarNews.

Animating CSS Grid (How To + Examples)

Post pobrano z: Animating CSS Grid (How To + Examples)

I’m pleased to shine a light on the fact that the CSS grid-template-rows and grid-template-columns properties are now animatable in all major web browsers! Well, CSS Grid has technically supported animations for a long time, as it’s baked right into the CSS Grid Layout Module Level 1 spec.

But animating these grid properties only recently gained supported by all three major browsers. Shall we take a look at a few examples to get the creative juices flowing?

Example 1: Expanding sidebar

First of all, this is what we’re talking about:

CodePen Embed Fallback

A simple two-column grid. Now, before, you might not have built this using CSS Grid because animations and transitions weren’t supported, but what if you wanted the left column — perhaps a sidebar navigation — to expand on hover? Well, now that’s possible.

I know what you’re thinking: “Animating a CSS property? Easy peasy, I’ve been doing it for years!” Me too. However, I ran into an interesting snag while experimenting with a particular use case.

So, we want to transition the grid itself (specifically grid-template-columns, which is set on the .grid class in the example). But the left column (.left) is the selector that requires the :hover pseudo-class. While JavaScript can solve this conundrum easily — thanks, but no thanks — we can accomplish it with CSS alone.

Let’s walk through the whole thing, starting with the HTML. Pretty standard stuff really… a grid with two columns.

<div class="grid">
  <div class="left"></div>
  <div class="right"></div>
</div>

Putting the cosmetic CSS aside, you’ll first need to set display: grid on the parent container (.grid).

.grid {
  display: grid;
}

Next, we can define and size the two columns using the grid-template-columns property. We’ll make the left column super narrow, and later increase its width on hover. The right column takes up the rest of the remaining space, thanks to the auto keyword.

.grid {
  display: grid;
  grid-template-columns: 48px auto;
}

We know we’re going to animate this thing, so let’s go ahead and throw a transition in there while we’re at it so the change between states is smooth and noticeable.

.grid {
  display: grid;
  grid-template-columns: 48px auto;
  transition: 300ms; /* Change as needed */
}

That’s it for the .grid! All that’s left is to apply the hover state. Specifically, we’re going to override the grid-template-columns property so that the left column takes up a greater amount of space on hover.

This alone isn’t all that interesting, although it’s awesome that animations and transitions are supported now in CSS Grid. What’s more interesting is that we can use the relatively new :has() pseudo-class to style the parent container (.grid) while the child (.left) is hovered.

.grid:has(.left:hover) {
  /* Hover styles */
}

In plain English this is saying, “Do something to the .grid container if it contains an element named .left inside of it that is in a hover state.” That’s why :has() is often referred to as a “parent” selector. We can finally select a parent based on the children it contains — no JavaScript required!

So, let’s increase the width of the .left column to 30% when it is hovered. The .right column will continue to take up all the leftover space:

.grid {
  display: grid;
  transition: 300ms;
  grid-template-columns: 48px auto;
}

.grid:has(.left:hover) {
  grid-template-columns: 30% auto;
}

We could use CSS variables as well, which may or may not look cleaner depending on your personal preferences (or you might be using CSS variables in your project anyway):

.grid {
  display: grid;
  transition: 300ms;
  grid-template-columns: var(--left, 48px) auto;
}

.grid:has(.left:hover) {
  --left: 30%;
}

I love that CSS grids can be animated now, but the fact that we can build this particular example with just nine lines of CSS is even more astounding.

Here’s another example by Olivia Ng — similar concept, but with content (click on the nav icon):

CodePen Embed Fallback

Example 2: Expanding Panels

CodePen Embed Fallback

This example transitions the grid container (the column widths) but also the individual columns (their background colors). It’s ideal for providing more content on hover.

It’s worth remembering that the repeat() function sometimes produces buggy transitions, which is why I set the width of each column individually (i.e. grid-template-columns: 1fr 1fr 1fr).

Example 3: Adding Rows and Columns

CodePen Embed Fallback

This example animatedly “adds” a column to the grid. However — you guessed it — this scenario has a pitfall too. The requirement is that the “new” column mustn’t be hidden (i.e. set to display: none), and CSS Grid must acknowledge its existence while setting its width to 0fr.

So, for a three-column grid — grid-template-columns: 1fr 1fr 0fr (yes, the unit must be declared even though the value is 0!) transitions into grid-template-columns: 1fr 1fr 1fr correctly, but grid-template-columns: 1fr 1fr doesn’t. In hindsight, this actually makes perfect sense considering what we know about how transitions work.

Here’s another example by Michelle Barker — same concept, but with an extra column and lot more pizzazz. Make sure to run this one in full-screen mode because it’s actually responsive (no trickery, just good design!).

CodePen Embed Fallback

A few more examples

Because why not?

This “Animated Mondrian” is the original proof of concept for animated CSS grids by Chrome DevRel. The grid-row‘s and grid-column‘s utilize the span keyword to create the layout you see before you, and then the grid-template-row’s and grid-template-column‘s are animated using a CSS animation. It’s nowhere near as complex as it looks!

CodePen Embed Fallback

Same concept, but with more of that Michelle Barker pizzazz. Could make a nice loading spinner?

CodePen Embed Fallback

Wrapping up with a bit of nostalgia (showing my age here), the not-very-griddy animated CSS grid by Andrew Harvard. Again — same concept — it’s just that you can’t see the other grid items. But don’t worry, they’re there.

CodePen Embed Fallback

Animating CSS Grid (How To + Examples) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Getting Started With SvelteKit

Post pobrano z: Getting Started With SvelteKit

SvelteKit is the latest of what I’d call next-gen application frameworks. It, of course, scaffolds an application for you, with the file-based routing, deployment, and server-side rendering that Next has done forever. But SvelteKit also supports nested layouts, server mutations that sync up the data on your page, and some other niceties we’ll get into.

This post is meant to be a high-level introduction to hopefully build some excitement for anyone who’s never used SvelteKit. It’ll be a relaxed tour. If you like what you see, the full docs are here.

In some ways this is a challenging post to write. SvelteKit is an application framework. It exists to help you build… well, applications. That makes it hard to demo. It’s not feasible to build an entire application in a blog post. So instead, we’ll use our imaginations a bit. We’ll build the skeleton of an application, have some empty UI placeholders, and hard-coded static data. The goal isn’t to build an actual application, but instead to show you how SvelteKit’s moving pieces work so you can build an application of your own.

To that end, we’ll build the tried and true To-Do application as an example. But don’t worry, this will be much, much more about seeing how SvelteKit works than creating yet another To-Do app.

The code for everything in this post is available at GitHub. This project is also deployed on Vercel for a live demo.

Creating your project

Spinning up a new SvelteKit project is simple enough. Run npm create svelte@latest your-app-name in the terminal and answer the question prompts. Be sure to pick “Skeleton Project” but otherwise make whatever selections you want for TypeScript, ESLint, etc.

Once the project is created, run npm i and npm run dev and a dev server should start running. Fire up localhost:5173 in the browser and you’ll get the placeholder page for the skeleton app.

Basic routing

Notice the routes folder under src. That holds code for all of our routes. There’s already a +page.svelte file in there with content for the root / route. No matter where in the file hierarchy you are, the actual page for that path always has the name +page.svelte. With that in mind, let’s create pages for /list, /details, /admin/user-settings and admin/paid-status, and also add some text placeholders for each page.

Your file layout should look something like this:

Initial files.

You should be able to navigate around by changing URL paths in the browser address bar.

Browser address bar with localhost URL.

Layouts

We’ll want navigation links in our app, but we certainly don’t want to copy the markup for them on each page we create. So, let’s create a +layout.svelte file in the root of our routes folder, which SvelteKit will treat as a global template for all pages. Let’s and add some content to it:

<nav>
  <ul>
    <li>
      <a href="/">Home</a>
    </li>
    <li>
      <a href="/list">To-Do list</a>
    </li>
    <li>
      <a href="/admin/paid-status">Account status</a>
    </li>
    <li>
      <a href="/admin/user-settings">User settings</a>
    </li>
  </ul>
</nav>

<slot />

<style>
  nav {
    background-color: beige;
  }
  nav ul {
    display: flex;
  }
  li {
    list-style: none;
    margin: 15px;
  }
  a {
    text-decoration: none;
    color: black;
  }
</style>

Some rudimentary navigation with some basic styles. Of particular importance is the <slot /> tag. This is not the slot you use with web components and shadow DOM, but rather a Svelte feature indicating where to put our content. When a page renders, the page content will slide in where the slot is.

And now we have some navigation! We won’t win any design competitions, but we’re not trying to.

Horizontal navigation with light yellow background.

Nested layouts

What if we wanted all our admin pages to inherit the normal layout we just built but also share some things common to all admin pages (but only admin pages)? No problem, we add another +layout.svelte file in our root admin directory, which will be inherited by everything underneath it. Let’s do that and add this content:

<div>This is an admin page</div>

<slot />

<style>
  div {
    padding: 15px;
    margin: 10px 0;
    background-color: red;
    color: white;
  }
</style>

We add a red banner indicating this is an admin page and then, like before, a <slot /> denoting where we want our page content to go.

Our root layout from before renders. Inside of the root layout is a <slot /> tag. The nested layout’s content goes into the root layout’s <slot />. And finally, the nested layout defines its own <slot />, into which the page content renders.

If you navigate to the admin pages, you should see the new red banner:

Red box beneath navigation that says this is an admin page.

Defining our data

OK, let’s render some actual data — or at least, see how we can render some actual data. There’s a hundred ways to create and connect to a database. This post is about SvelteKit though, not managing DynamoDB, so we’ll “load” some static data instead. But, we’ll use all the same machinery to read and update it that you’d use for real data. For a real web app, swap out the functions returning static data with functions connecting and querying to whatever database you happen to use.

Let’s create a dirt-simple module in lib/data/todoData.ts that returns some static data along with artificial delays to simulate real queries. You’ll see this lib folder imported elsewhere via $lib. This is a SvelteKit feature for that particular folder, and you can even add your own aliases.

let todos = [
  { id: 1, title: "Write SvelteKit intro blog post", assigned: "Adam", tags: [1] },
  { id: 2, title: "Write SvelteKit advanced data loading blog post", assigned: "Adam", tags: [1] },
  { id: 3, title: "Prepare RenderATL talk", assigned: "Adam", tags: [2] },
  { id: 4, title: "Fix all SvelteKit bugs", assigned: "Rich", tags: [3] },
  { id: 5, title: "Edit Adam's blog posts", assigned: "Geoff", tags: [4] },
];

let tags = [
  { id: 1, name: "SvelteKit Content", color: "ded" },
  { id: 2, name: "Conferences", color: "purple" },
  { id: 3, name: "SvelteKit Development", color: "pink" },
  { id: 4, name: "CSS-Tricks Admin", color: "blue" },
];

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

export async function getTodos() {
  await wait();

  return todos;
}

export async function getTags() {
  await wait();

  return tags.reduce((lookup, tag) => {
    lookup[tag.id] = tag;
    return lookup;
  }, {});
}

export async function getTodo(id) {
  return todos.find(t => t.id == id);
}

A function to return a flat array of our to-do items, a lookup of our tags, and a function to fetch a single to-do (we’ll use that last one in our Details page).

Loading our data

How do we get that data into our Svelte pages? There’s a number of ways, but for now, let’s create a +page.server.js file in our list folder, and put this content in it:

import { getTodos, getTags } from "$lib/data/todoData";

export function load() {
  const todos = getTodos();
  const tags = getTags();

  return {
    todos,
    tags,
  };
}

We’ve defined a load() function that pulls in the data needed for the page. Notice that we are not await-ing calls to our getTodos and getTags async functions. Doing so would create a data loading waterfall as we wait for our to-do items to come in before loading our tags. Instead, we return the raw promises from load, and SvelteKit does the necessary work to await them.

So, how do we access this data from our page component? SvelteKit provides a data prop for our component with data on it. We’ll access our to-do items and tags from it using a reactive assignment.

Our List page component now looks like this.

<script>
  export let data;
  $: ({ todo, tags } = data);
</script>

<table cellspacing="10" cellpadding="10">
  <thead>
    <tr>
      <th>Task</th>
      <th>Tags</th>
      <th>Assigned</th>
    </tr>
  </thead>
  <tbody>
    {#each todos as t}
    <tr>
      <td>{t.title}</td>
      <td>{t.tags.map((id) => tags[id].name).join(', ')}</td>
      <td>{t.assigned}</td>
    </tr>
    {/each}
  </tbody>
</table>

<style>
  th {
    text-align: left;
  }
</style>

And this should render our to-do items!

Five to-do items in a table format.

Layout groups

Before we move on to the Details page and mutate data, let’s take a peek at a really neat SvelteKit feature: layout groups. We’ve already seen nested layouts for all admin pages, but what if we wanted to share a layout between arbitrary pages at the same level of our file system? In particular, what if we wanted to share a layout between only our List page and our Details page? We already have a global layout at that level. Instead, we can create a new directory, but with a name that’s in parenthesis, like this:

File directory.

We now have a layout group that covers our List and Details pages. I named it (todo-management) but you can name it anything you like. To be clear, this name will not affect the URLs of the pages inside of the layout group. The URLs will remain the same; layout groups allow you to add shared layouts to pages without them all comprising the entirety of a directory in routes.

We could add a +layout.svelte file and some silly <div> banner saying, “Hey we’re managing to-dos”. But let’s do something more interesting. Layouts can define load() functions in order to provide data for all routes underneath them. Let’s use this functionality to load our tags — since we’ll be using our tags in our details page — in addition to the list page we already have.

In reality, forcing a layout group just to provide a single piece of data is almost certainly not worth it; it’s better to duplicate that data in the load() function for each page. But for this post, it’ll provide the excuse we need to see a new SvelteKit feature!

First, let’s go into our list page’s +page.server.js file and remove the tags from it.

import { getTodos, getTags } from "$lib/data/todoData";

export function load() {
  const todos = getTodos();

  return {
    todos,
  };
}

Our List page should now produce an error since there is no tags object. Let’s fix this by adding a +layout.server.js file in our layout group, then define a load() function that loads our tags.

import { getTags } from "$lib/data/todoData";

export function load() {
  const tags = getTags();

  return {
    tags,
  };
}

And, just like that, our List page is rendering again!

We’re loading data from multiple locations

Let’s put a fine point on what’s happening here:

  • We defined a load() function for our layout group, which we put in +layout.server.js.
  • This provides data for all of the pages the layout serves — which in this case means our List and Details pages.
  • Our List page also defines a load() function that goes in its +page.server.js file.
  • SvelteKit does the grunt work of taking the results of these data sources, merging them together, and making both available in data.

Our Details page

We’ll use our Details page to edit a to-do item. First, let’s add a column to the table in our List page that links to the Details page with the to-do item’s ID in the query string.

<td><a href="/details?id={t.id}">Edit</a></td>

Now let’s build out our Details page. First, we’ll add a loader to grab the to-do item we’re editing. Create a +page.server.js in /details, with this content:

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

export function load({ url }) {
  const id = url.searchParams.get("id");

  console.log(id);
  const todo = getTodo(id);

  return {
    todo,
  };
}

Our loader comes with a url property from which we can pull query string values. This makes it easy to look up the to-do item we’re editing. Let’s render that to-do, along with functionality to edit it.

SvelteKit has wonderful built-in mutation capabilities, so long as you use forms. Remember forms? Here’s our Details page. I’ve elided the styles for brevity.

<script>
  import { enhance } from "$app/forms";

  export let data;

  $: ({ todo, tags } = data);
  $: currentTags = todo.tags.map(id => tags[id]);
</script>

<form use:enhance method="post" action="?/editTodo">
  <input name="id" type="hidden" value="{todo.id}" />
  <input name="title" value="{todo.title}" />

  <div>
    {#each currentTags as tag}
    <span style="{`color:" ${tag.color};`}>{tag.name}</span>
    {/each}
  </div>

  <button>Save</button>
</form>

We’re grabbing the tags as before from our layout group’s loader and the to-do item from our page’s loader. We’re grabbing the actual tag objects from the to-do’s list of tag IDs and then rendering everything. We create a form with a hidden input for the ID and a real input for the title. We display the tags and then provide a button to submit the form.

If you noticed the use:enhance, that simply tells SvelteKit to use progressive enhancement and Ajax to submit our form. You’ll likely always use that.

How do we save our edits?

Notice the action="?/editTodo" attribute on the form itself? This tells us where we want to submit our edited data. For our case, we want to submit to an editTodo “action.”

Let’s create it by adding the following to the +page.server.js file we already have for Details (which currently has a load() function, to grab our to-do):

import { redirect } from "@sveltejs/kit";

// ...

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

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

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

    throw redirect(303, "/list");
  },
};

Form actions give us a request object, which provides access to our formData, which has a get method for our various form fields. We added that hidden input for the ID value so we could grab it here in order to look up the to-do item we’re editing. We simulate a delay, call a new updateTodo() method, then redirect the user back to the /list page. The updateTodo() method merely updates our static data; in real life you’d run some sort of update in whatever datastore you’re using.

export async function updateTodo(id, newTitle) {
  const todo = todos.find(t => t.id == id);
  Object.assign(todo, { title: newTitle });
}

Let’s try it out. We’ll go to the List page first:

List page with to-do-items.

Now let’s click the Edit button for one of the to-do items to bring up the editing page in /details.

Details page for a to-do item.

We’re going to add a new title:

Changing the to-do title in an editable text input.

Now, click Save. That should get us back to our /list page, with the new to-do title applied.

The edited to-do item in the full list view.

How did the new title show up like that? It was automatic. Once we redirected to the /list page, SvelteKit automatically re-ran all of our loaders just like it would have done regardless. This is the key advancement that next-gen application frameworks, like SvelteKit, Remix, and Next 13 provide. Rather than giving you a convenient way to render pages then wishing you the best of luck fetching whatever endpoints you might have to update data, they integrate data mutation alongside data loading, allowing the two to work in tandem.

A few things you might be wondering…

This mutation update doesn’t seem too impressive. The loaders will re-run whenever you navigate. What if we hadn’t added a redirect in our form action, but stayed on the current page? SvelteKit would perform the update in the form action, like before, but would still re-run all of the loaders for the current page, including the loaders in the page layout(s).

Can we have more targeted means of invalidating our data? For example, our tags were not edited, so in real life we wouldn’t want to re-query them. Yes, what I showed you is just the default forms behavior in SvelteKit. You can turn the default behavior off by providing a callback to use:enhance. Then SvelteKit provides manual invalidation functions.

Loading data on every navigation is potentially expensive, and unnecessary. Can I cache this data like I do with tools like react-query? Yes, just differently. SvelteKit lets you set (and then respect) the cache-control headers the web already provides. And I’ll be covering cache invalidation mechanisms in a follow-on post.

Everything we’ve done throughout this article uses static data and modifies values in memory. If you need to revert everything and start over, stop and restart the npm run dev Node process.

Wrapping up

We’ve barely scratched the surface of SvelteKit, but hopefully you’ve seen enough to get excited about it. I can’t remember the last time I’ve found web development this much fun. With things like bundling, routing, SSR, and deployment all handled out of the box, I get to spend more time coding than configuring.

Here are a few more resources you can use as next steps learning SvelteKit:


Getting Started With SvelteKit originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

More Real-World Uses for :has()

Post pobrano z: More Real-World Uses for :has()

The :has() pseudo-class is, hands-down, my favorite new CSS feature. I know it is for many of you as well, at least those of you who took the State of CSS survey. The ability to write selectors upside down gives us more superpowers I’d never thought possible.

I say “more superpowers” because there have already been a ton of really amazing clever ideas published by a bunch of super smart people, like:

This article is not a definitive guide to :has(). It’s also not here to regurgitate what’s already been said. It’s just me (hi 👋) jumping on the bandwagon for a moment to share some of the ways I’m most likely to use :has() in my day-to-day work… that is, once browser support is good enough. (Firefox is the last holdout, but will support it, which is imminent.)

When that does happen, you can bet I’ll start using :has() all over the place. Here are some real-world examples of things I’ve built recently and thought to myself, “Gee, this’ll be so much nicer once :has() is fully supported.”

Avoid having to reach outside your JavaScript component

Have you ever built an interactive component that sometimes needs to affect styles somewhere else on the page? Take the following example, where <nav> is a mega menu, and opening it changes the colors of the <header> content above it.

I feel like I need to do this kind of thing all the time.

This particular example is a React component I made for a site. I had to “reach outside” the React part of the page with document.querySelector(...) and toggle a class on the <body>, <header>, or another component. That’s not the end of the world, but it sure feels a bit yuck. Even in a fully React site (a Next.js site, say), I’d have to choose between managing a menuIsOpen state way higher up the component tree, or do the same DOM element selection — which isn’t very React-y.

With :has(), the problem goes away:

header:has(.megamenu--open) {
  /* style the header differently if it contains 
    an element with the class ".megamenu--open"
  */
}

No more fiddling with other parts of the DOM in my JavaScript components!

Better table striping UX

Adding alternate row “stripes” to your tables can be a nice UX improvement. They help your eyes keep track of which row you’re on as you scan the table.

But in my experience, this doesn’t work great on tables with just two or three rows. If you have, for example, a table with three rows in the <tbody> and you’re “striping” every “even” row, you could end up with just one stripe. That’s not really worth a pattern and might have users wondering what’s so special about that one highlighted row.

Using this technique where Bramus uses :has() to apply styles based on the number of children, we can apply table stripes when there are more than, say, three rows:

CodePen Embed Fallback

What to get fancier? You could also decide to only do this if the table has at least a certain number of columns, too:

table:has(:is(td, th):nth-child(3)) {
  /* only do stuff if there are three or more columns */
}

Remove conditional class logic from templates

I often need to change a page layout depending on what’s on the page. Take the following Grid layout, where the placement of the main content changes grid areas depending on whether there’s a sidebar present.

Layout with left sidebar above a layout with no sidebar.

That’s something that might depend on whether there are sibling pages set in the CMS. I’d normally do this with template logic to conditionally add BEM modifier classes to the layout wrapper to account for both layouts. That CSS might look something like this (responsive rules and other stuff omitted for brevity):

/* m = main content */
/* s = sidebar */
.standard-page--with-sidebar {
  grid-template-areas: 's s s m m m m m m m m m';
}
.standard-page--without-sidebar {
  grid-template-areas: '. m m m m m m m m m . .';
}

CSS-wise, this is totally fine, of course. But it does make the template code a little messy. Depending on your templating language it can get pretty ugly to conditionally add a bunch of classes, especially if you have to do this with lots of child elements too.

Contrast that with a :has()-based approach:

/* m = main content */
/* s = sidebar */
.standard-page:has(.sidebar) {
  grid-template-areas: 's s s m m m m m m m m m';
}
.standard-page:not(:has(.sidebar)) {
  grid-template-areas: '. m m m m m m m m m . .';
}

Honestly, that’s not a whole lot better CSS-wise. But removing the conditional modifier classes from the HTML template is a nice win if you ask me.

It’s easy to think of micro design decisions for :has()like a card when it has an image in it — but I think it’ll be really useful for these macro layout changes too.

Better specificity management

If you read my previous article, you’ll know I’m a stickler for specificity. If, like me, you don’t want your specificity scores blowing out when adding :has() and :not() throughout your styles, be sure to use :where().

That’s because the specificity of :has() is based on the most specific element in its argument list. So, if you have something like an ID in there (why, I’m not sure!), your selector is going to be tough to override in the cascade.

On the other hand, the specificity of :where() is always zero, never adding to the specificity score.

/* specificity score: 0,1,0.
  Same as a .standard-page--with-sidebar 
  modifier class
*/
.standard-page:where(:has(.sidebar)) {
  /* etc */
}

The future’s bright

These are just a few things I can’t wait to be able to use in production. The CSS-Tricks Almanac has a bunch of examples, too. What are you looking forward to doing with :has()? What sort of some real-world examples have you run into where :has() would have been the perfect solution?


More Real-World Uses for :has() originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

How to Transition to Manifest V3 for Chrome Extensions

Post pobrano z: How to Transition to Manifest V3 for Chrome Extensions

While I am not a regular Chrome extension programmer, I have certainly coded enough extensions and have a wide enough web development portfolio to know my way around the task. However, just recently, I had a client reject one of my extensions as I received feedback that my extension was “outdated”.

As I was scrambling to figure out what was wrong, I swept my embarrassment under the carpet and immediately began my deep dive back into the world of Chrome Extensions. Unfortunately, information on Manifest V3 was scarce and it was difficult for me to understand quickly what this transition was all about.

Needless to say, with a pending job, I had to painstakingly navigate my way around Google’s Chrome Developer Documentation and figure things out for myself. While I got the job done, I did not want my knowledge and research in this area to go to waste and decided to share what I wish I could have had easy access to in my learning journey.

Why the transition to Manifest 3 is important

Manifest V3 is an API that Google will use in its Chrome browser. It is the successor to the current API, Manifest V2, and governs how Chrome extensions interact with the browser. Manifest V3 introduces significant changes to the rules for extensions, some of which will be the new mainstay from V2 we were used to.

The transition to Manifest V3 can be summarized as such:

  1. The transition has been ongoing since 2018.
  2. Manifest V3 will officially begin rolling out in January 2023.
  3. By June 2023, extensions that run Manifest V2 will no longer be available on the Chrome Web Store.
  4. Extensions that do not comply with the new rules introduced in Manifest V3 will eventually be removed from the Chrome Web Store.

One of the main goals of Manifest V3 is to make users safer and improve the overall browser experience. Previously, many browser extensions relied on code in the cloud, meaning it could be difficult to assess whether an extension was risky. Manifest V3 aims to address this by requiring extensions to contain all the code they will run, allowing Google to scan them and detect potential risks. It also forces extensions to request permission from Google for the changes they can implement on the browser.

Staying up-to-date with Google’s transition to Manifest V3 is important because it introduces new rules for extensions that aim to improve user safety and the overall browser experience, and extensions that do not comply with these rules will eventually be removed from the Chrome Web Store.

In short, all of your hard work in creating extensions that used Manifest V2 could be for naught if you do not make this transition in the coming months.

January 2023 June 2023 January 2024
Support for Manifest V2 extensions will be turned off in Chrome’s Canary, Dev, and Beta channels. The Chrome Web Store will no longer allow Manifest V2 extensions to be published with visibility set to Public. The Chrome Web Store will remove all remaining Manifest V2 extensions.
Manifest V3 will be required for the Featured badge in the Chrome Web Store. Existing Manifest V2 extensions that are published and publically visible will become unlisted. Support for Manifest 2 will end for all of Chrome’s channels, including the Stable channel, unless the Enterprise channel is extended.

The key differences between Manifest V2 and V3

There are many differences between the two, and while I highly recommend that you read up on Chrome’s “Migrating to Manifest V3” guide, here is a short and sweet summary of key points:

  1. Service workers replace background pages in Manifest V3.
  2. Network request modification is handled with the new declarativeNetRequest API in Manifest V3.
  3. In Manifest V3, extensions can only execute JavaScript that is included within their package and cannot use remotely-hosted code.
  4. Manifest V3 introduces promise support to many methods, though callbacks are still supported as an alternative.
  5. Host permissions in Manifest V3 are a separate element and must be specified in the "host_permissions" field.
  6. The content security policy in Manifest V3 is an object with members representing alternative content security policy (CSP) contexts, rather than a string as it was in Manifest V2.

In a simple Chrome Extension’s Manifest that alters a webpage’s background, that might look like this:

// Manifest V2
{
  "manifest_version": 2,
  "name": "Shane's Extension",
  "version": "1.0",
  "description": "A simple extension that changes the background of a webpage to Shane's face.",
  "background": {
    "scripts": ["background.js"],
    "persistent": true
  },
  "browser_action": {
    "default_popup": "popup.html"
  },
  "permissions": [ "activeTab", ],
  "optional_permissions": ["<all_urls>"]
}
// Manifest V3
{
  "manifest_version": 3,
  "name": "Shane's Extension",
  "version": "1.0",
  "description": "A simple extension that changes the background of a webpage to Shane's face.",
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_popup": "popup.html"
  },
  "permissions": [ "activeTab", ],
  "host_permissions": [ "<all_urls>" ]
}

If you find some of the tags above seem foreign to you, keep reading to find out exactly what you need to know.

How to smoothly transition to Manifest V3

I have summarized the transition to Manifest V3 in four key areas. Of course, while there are many bells and whistles in the new Manifest V3 that need to be implemented from the old Manifest V2, implementing changes in these four areas will get your Chrome Extension well on the right track for the eventual transition.

The four key areas are:

  1. Updating your Manifest’s basic structure.
  2. Modify your host permissions.
  3. Update the content security policy.
  4. Modify your network request handling.

With these four areas, your Manifest’s fundamentals will be ready for the transition to Manifest V3. Let’s look at each of these key aspects in detail and see how we can work towards future-proofing your Chrome Extension from this transition.

Updating your Manifest’s basic structure

Updating your manifest’s basic structure is the first step in transitioning to Manifest V3. The most important change you will need to make is changing the value of the "manifest_version" element to 3, which determines that you are using the Manifest V3 feature set.

One of the major differences between Manifest V2 and V3 is the replacement of background pages with a single extension service worker in Manifest V3. You will need to register the service worker under the "background" field, using the "service_worker" key and specify a single JavaScript file. Even though Manifest V3 does not support multiple background scripts, you can optionally declare the service worker as an ES Module by specifying "type": "module", which allows you to import further code.

In Manifest V3, the "browser_action" and "page_action" properties are unified into a single "action" property. You will need to replace these properties with "action" in your manifest. Similarly, the "chrome.browserAction" and "chrome.pageAction" APIs are unified into a single “Action” API in Manifest V3, and you will need to migrate to this API.

// Manifest V2
"background": {
  "scripts": ["background.js"],
  "persistent": false
},
"browser_action": {
  "default_popup": "popup.html"
},
// Manifest V3
"background": {
  "service_worker": "background.js"
},
"action": {
  "default_popup": "popup.html"
}

Overall, updating your manifest’s basic structure is a crucial step in the process of transitioning to Manifest V3, as it allows you to take advantage of the new features and changes introduced in this version of the API.

Modify your host permissions

The second step in transitioning to Manifest V3 is modifying your host permissions. In Manifest V2, you specify host permissions in the "permissions" field in the manifest file. In Manifest V3, host permissions are a separate element, and you should specify them in the "host_permissions" field in the manifest file.

Here is an example of how to modify your host permissions:

// Manifest V2
"permissions": [ 
  "activeTab", 
  "storage", 
  "http://www.css-tricks.com/", 
  ":///*" 
]
// Manifest V3
"permissions": [ 
  "activeTab", 
  "scripting", 
  "storage"
],
"host_permissions": [
  "http://www.css-tricks.com/" 
],
"optional_host_permissions": [ 
  ":///*" 
]

Update the content security policy

In order to update the CSP of your Manifest V2 extension to be compliant with Manifest V3, you will need to make some changes to your manifest file. In Manifest V2, the CSP was specified as a string in the "content_security_policy" field of the manifest.

In Manifest V3, the CSP is now an object with different members representing alternative CSP contexts. Instead of a single "content_security_policy" field, you will now have to specify separate fields for "content_security_policy.extension_pages" and "content_security_policy.sandbox", depending on the type of extension pages you are using.

You should also remove any references to external domains in the "script-src", "worker-src", "object-src", and "style-src" directives if they are present. It is important to make these updates to your CSP in order to ensure the security and stability of your extension in Manifest V3.

// Manifest V2
"content_security_policy": "script-src 'self' https://css-tricks.com; object-src 'self'"
// Manfiest V3
"content_security_policy.extension_pages": "script-src 'self' https://example.com; object-src
'self'",
"content_security_policy.sandbox": "script-src 'self' https://css-tricks.com; object-src 'self'"

Modify your network request handling

The final step in transitioning to Manifest V3 is modifying your network request handling. In Manifest V2, you would have used the chrome.webRequest API to modify network requests. However, this API is replaced in Manifest V3 by the declarativeNetRequest API.

To use this new API, you will need to specify the declarativeNetRequest permission in your manifest and update your code to use the new API. One key difference between the two APIs is that the declarativeNetRequest API requires you to specify a list of predetermined addresses to block, rather than being able to block entire categories of HTTP requests as you could with the chrome.webRequest API.

It is important to make these changes in your code to ensure that your extension continues to function properly under Manifest V3. Here is an example of how you would modify your manifest to use the declarativeNetRequest API in Manifest V3:

// Manifest V2
"permissions": [
  "webRequest",
  "webRequestBlocking"
]
// Manifest V3
"permissions": [
  "declarativeNetRequest"
]

You will also need to update your extension code to use the declarativeNetRequest API instead of the chrome.webRequest API.

Other aspects you need to check

What I have covered is just the tip of the iceberg. Of course, if I wanted to cover everything, I could be here for days and there would be no point in having Google’s Chrome Developers guides. While what I covered will have you future-proofed enough to arm your Chrome extensions in this transition, here are some other things you might want to look at to ensure your extensions are functioning at the top of their game.

  • Migrating background scripts to the service worker execution context: As mentioned earlier, Manifest V3 replaces background pages with a single extension service worker, so it may be necessary to update background scripts to adapt to the service worker execution context.
  • Unifying the **chrome.browserAction** and **chrome.pageAction** APIs: These two equivalent APIs are unified into a single API in Manifest V3, so it may be necessary to migrate to the Action API.
  • Migrating functions that expect a Manifest V2 background context: The adoption of service workers in Manifest V3 is not compatible with methods like chrome.runtime.getBackgroundPage(), chrome.extension.getBackgroundPage(), chrome.extension.getExtensionTabs(), and chrome.extension.getViews(). It may be necessary to migrate to a design that passes messages between other contexts and the background service worker.
  • Moving CORS requests in content scripts to the background service worker: It may be necessary to move CORS requests in content scripts to the background service worker in order to comply with Manifest V3.
  • Migrating away from executing external code or arbitrary strings: Manifest V3 no longer allows the execution of external logic using chrome.scripting.executeScript({code: '...'}), eval(), and new Function(). It may be necessary to move all external code (JavaScript, WebAssembly, CSS) into the extension bundle, update script and style references to load resources from the extension bundle, and use chrome.runtime.getURL() to build resource URLs at runtime.
  • Updating certain scripting and CSS methods in the Tabs API: As mentioned earlier, several methods move from the Tabs API to the Scripting API in Manifest V3. It may be necessary to update any calls to these methods to use the correct Manifest V3 API.

And many more!

Feel free to take some time to get yourself up to date on all the changes. After all, this change is inevitable and if you do not want your Manifest V2 extensions to be lost due to avoiding this transition, then spend some time arming yourself with the necessary knowledge.

On the other hand, if you are new to programming Chrome extensions and looking to get started, a great way to go about it is to dive into the world of Chrome’s Web Developer tools. I did so through a course on Linkedin Learning, which got me up to speed pretty quickly. Once you have that base knowledge, come back to this article and translate what you know to Manifest V3!

So, how will I be using the features in the new Manifest V3 going forward?

Well, to me, the transition to Manifest V3 and the removal of the chrome.webRequest API seems to be shifting extensions away from data-centric use cases (such as ad blockers) to more functional and application-based uses. I have been staying away from application development lately as it can get quite resource-intensive at times. However, this shift might be what brings me back!

The rise of AI tools in recent times, many with available-to-use APIs, has sparked tons of new and fresh SaaS applications. Personally, I think that it’s coming at a perfect time with the shift to more application-based Chrome extensions! While many of the older extensions may be wiped out from this transition, plenty of new ones built around novel SaaS ideas will come to take their place.

Hence, this is an exciting update to hop on and revamp old extensions or build new ones! Personally, I see many possibilities in using APIs that involve AI being used in extensions to enhance a user’s browsing experience. But that’s really just the tip of the iceberg. If you’re looking to really get into things with your own professional extensions or reaching out to companies to build/update extensions for them, I would recommend upgrading your Gmail account for the benefits it gives in collaborating, developing, and publishing extensions to the Chrome Web Store.

However, remember that every developer’s requirements are different, so learn what you need to keep your current extensions afloat, or your new ones going!


How to Transition to Manifest V3 for Chrome Extensions originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Affinity Photo for Beginners

Post pobrano z: Affinity Photo for Beginners

Learn how to get started with Affinity Photo in this beginner-friendly course! We’ll cover all the essential tools you need to know to create different photo and text effects. 

Jump straight to the Affinity Photo skill you want to learn:

What You’ll Learn

  • What are Personas in Affinity Photo?
  • What are adjustment and live filter layers in Affinity Photo?
  • What are live filter layers in Affinity Photo?
  • How to remove a background in Affinity Photo
  • How to create a text effect in Affinity Photo

About Your Instructor

Abbey Esparza

I’m a mixed media artist and freelancer with over ten years of experience in digital art. In the last five years of my career, I have found a passion for sharing my knowledge and creating educational content showing that anyone can be a successful artist.

abbey esparzaabbey esparzaabbey esparza

1. Introduction 

1.1 How to Customize Your Preferences in Affinity Photo 

Watch video lesson (3 mins)

There’s no better place to start than with our user interface, known as the „Studio” in Affinity Photo. We’ll be covering how to best set up your Preferences for max performance, as well as customizing your studio.

Affinity Photo PreferencesAffinity Photo PreferencesAffinity Photo Preferences

1.2 What Are Personas in Affinity Photo?

Watch video lesson (1 min)

Have you ever wondered what Personas are in Affinity Photo? They are less complicated than they sound. We’ll cover what Personas are, how to switch between them, and when you might use one over another. 

Personas in Affinity Photo Personas in Affinity Photo Personas in Affinity Photo 

Quick Tip! How to Create a T-Shirt Mockup

2. How to Use Layers, Masks, & Adjustments  

2.1 How to Use Basic Layers in Affinity Photo

Watch video lesson (3 mins)

Layers are essential in any photo editor, and Affinity Photo is no different. We’ll learn the basics of layers, including how to:

  • create Pixel layers
  • select multiple layers at once
  • toggle layer visibility 
  • adjust layer opacity 

We’ll also learn all about Nesting layers. Think of it like Russian nesting dolls, but with layers. 

Basic LayersBasic LayersBasic Layers

2.2 How to Use Adjustment Layers & Layer Masks in Affinity Photo

Watch video lesson (5 mins)

Now that we know how to move and adjust our layers, it’s time to learn about adjustment layers and layer masks. An Adjustment Layer is exactly what it sounds like: an image adjustment that is on its own layer.

Below we can see all of the different adjustments that Affinity Photo has. We’ll be using the Recolor layer as our beginning example, before we move on to bigger photo effects. 

Adjustment LayersAdjustment LayersAdjustment Layers

We will then learn about layer masks, how to create them and how they work. A layer mask is used to hide a portion of a layer, while still giving us the option to bring back what we hid at any time.

Layer MasksLayer MasksLayer Masks

Quick Tip! How to Use LUTs

2.3 How to Use Layer Modes & Live Filter Layers in Affinity Photo

Watch video lesson (3 mins)

Layer modes change how a layer will interact with all the layers below it. We’ll be using them to create both photo and text effects, but first we have to cover the basics. 

Layer Modes Layer Modes Layer Modes

Last but not least, some of the most powerful features in Affinity Photo are Live Layers. They’re the last layer type we’ll be covering. To start, we’ll use a single Motion Blur live layer to create a vibrant lens flare effect. 

Live Filter LayersLive Filter LayersLive Filter Layers

Quick Tip! How to Create a Halftone Effect

2.4 How to Create a Hand-Drawn Pencil Effect in Affinity Photo

Watch video lesson (3 mins)

With the basics of layers covered, we can use everything we’ve learned to create a hand-drawn pencil effect.

We’ll be using a mixture of: 

  • Layer Modes
  • Live Layers 
  • Adjustment Layers

This will make this effect almost completely non-destructive! 

How to Create a Hand-Drawn Pencil EffectHow to Create a Hand-Drawn Pencil EffectHow to Create a Hand-Drawn Pencil Effect

Quick Tip! How to Create a Painted Filter Effect  

3. How to Use Brushes in Affinity Photo

3.1 How to Install Brushes in Affinity Photo

Watch video lesson (2 mins)

We’ve barely used our brushes, but they’re one of Affinity Photo’s best features. There’s no better place to start than by installing custom brushes.  

Did you know that a lot of premade Photoshop bushes are compatible with Affinity Photo? If it’s a stamp-style brush, it will work in both programs! 

3.2 How to Create & Edit Brushes in Affinity Photo

Watch video lesson (2 mins)

If you can’t find a premade brush, then Affinity Photo makes it easier than ever to create your own. We’ll take a look at how to create both an Image brush and Intensity brush. 

Creating & Editing Brushes Creating & Editing Brushes Creating & Editing Brushes 

3.3 How to Create an Easy Watercolor Effect in Affinity Photo

Watch video lesson (2 mins)

After covering the basics of brushes, we can use what we’ve learned to turn our colored pencil drawing into a water painting!

Easy Watercolor Effect Easy Watercolor Effect Easy Watercolor Effect 

Quick Tip!  How to Change Hair Color

4. How to Combine Images in Affinity Photo

4.1 How to Remove Backgrounds 

Watch video lesson (3 mins)

Removing backgrounds is something all photo editors have to do at some point. We’ll be using the Selection Brush tool combined with layer masks to quickly remove a subject from their background. 

How to Remove Backgrounds How to Remove Backgrounds How to Remove Backgrounds 

4.2 How to Refine Masks & Extract Hair in Affinity Photo

Watch video lesson (2 mins)

The initial selection and mask are almost never perfect. This is where the Refine settings come in. We’ll use them to:

  • smooth edges
  • select and refine hair 
  • perfect our layer mask

We’ll also discuss when you may or may not want to use certain settings.

How to Refine Masks & Extract HairHow to Refine Masks & Extract HairHow to Refine Masks & Extract Hair

4.3 How to Extract Shadows in Affinity Photo

Watch video lesson (2 mins)

But what about shadows? There’s a neat trick we can use for images on studio backdrops using Layer Modes Adjustments and Layer Masks. This technique will let us use the original background of a photo for an accurate shadow.

Always try to use the original shadows in a photo. When extracting or creating shadows, the Multiply layer mode is going to be the best tool for the job. 
Extracting Shadows Extracting Shadows Extracting Shadows 

4.4 How to Make the Final Adjustments

Watch video lesson (3 mins)

Now, it’s time to combine everything we’ve learned to turn this image into a proper photo manipulation. First, we’ll cover simple color correction using a Brightness/Contrast adjustment layer to blend the initial two images together. 

Next, we’ll shift the hues of the image to be much more creative and eye-catching. We’ll do this by targeting the greens of both the background and the subject with an HSL adjustment layer. 

HSL adjustment layer.HSL adjustment layer.HSL adjustment layer.

Lastly, we’ll use a global Clarity live filter and a Curves adjustment to tie the two images together and give everything a consistent style. 

Curves adjustmentCurves adjustmentCurves adjustment

Quick Tip! How to Create a Glow Effect 

5. How to Create Text & Edit Type in Affinity Photo

5.1 How to Use Custom Fonts in Affinity Photo

Watch video lesson (1 min)

Custom fonts are a must for any designer or artist. In order to create a holographic text effect, we’ll first need to download and install the Devant Horgen font.  

5.2 How to Adjust and Edit Text in Affinity Photo

Watch video lesson (2 mins)

Before applying text effects, we have to cover some of the basic text settings, including:

  • Font Size
  • Font Color
  • Text Alignment
  • Kerning
Adjusting and Editing Text Adjusting and Editing Text Adjusting and Editing Text 

5.3 How to Create a Hologram Text Effect in Affinity Photo

Watch video lesson (4 mins)

By using the Smudge tool combined with different colored text on different layer modes, we can create a trippy warped text effect.

We’ll then bring the text effect together with one last Halftone live layer, which we’ll turn into a hologram effect using some easy settings. 

Hologram Text EffectHologram Text EffectHologram Text Effect

6. Conclusion 

Watch video lesson (1 min)

When it comes to Affinity Photo, it’s all about slowly learning how to mix and match tools to create what you want to make. In this final video, I tell you why I chose the basics that I did, explain why I might have left out certain tools, and tell you where you might want to go from here.

Affinity Photo for BeginnersAffinity Photo for BeginnersAffinity Photo for Beginners

Want even more assets to follow along with? Get unlimited downloads of photos, fonts, and add-ons on Envato Elements! Choose from thousands of Affinity Photo assets, with no daily download limits.

Learn More About Affinity Photo

Still want to learn more about Affinity Photo? Here are a few videos you’ll love: