Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
151264 stories
·
33 followers

Coursera Acquires Udemy For $930 Million

1 Share
Coursera announced on Wednesday that it will acquire rival online learning platform Udemy in an all-stock deal that values the combined company at $2.5 billion, a move that brings together two of the largest U.S.-based players in an industry that has struggled since pandemic-era enrollment highs faded. Under the terms of the agreement, Udemy shareholders will receive 0.8 shares of Coursera for each share they hold, valuing Udemy at roughly $930 million. Based on Coursera's last closing price, the offer works out to $6.35 per Udemy share, an 18.3% premium. The deal is expected to close in the second half of next year, pending regulatory and shareholder approvals. The two companies are betting that a combined platform will be better positioned to pursue corporate customers seeking to retrain workers in artificial intelligence, data science and software development. Coursera has built its business on partnerships with universities and institutions to offer degree programs and professional certificates, while Udemy operates a marketplace where independent instructors sell courses directly to consumers and businesses. Both stocks have significantly underperformed this year. Udemy shares have fallen about 35% and Coursera is down roughly 7%, leaving both trading well below their post-IPO highs as investors remain cautious about competition and pricing pressure in the sector.

Read more of this story at Slashdot.

Read the whole story
alvinashcraft
16 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Build with Gemini 3 Flash, frontier intelligence that scales with you

1 Share
Gemini 3 Flash is available for developers to build with now. Learn more about this smarter, scale-ready model and how — and where — you can use it now.
Read the whole story
alvinashcraft
28 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Gemini 3 Flash comes to the Gemini app

1 Share
Introducing Gemini 3 Flash, a major upgrade to your everyday AI. It delivers next-generation intelligence at lightning speeds.For too long, AI forced a choice: big model…
Read the whole story
alvinashcraft
38 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

The 2025 Stack Overflow and Stack Exchange wrap—our top ten questions of the year!

1 Share
As 2025 comes to a close, we're sharing some of the top questions from across our entire Stack Exchange Network.
Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete

How to Manage Multiple Client Sites with WordPress Studio

1 Share

Managing multiple client sites often means juggling local setups, updates, and changes across different environments.

It works — until the workflow starts getting in your way.

Small issues, like inconsistent configurations, overwritten files, and repetitive setup tasks, can all add up and slow you down. 

WordPress Studio simplifies all of that. 

The free, open-source tool lets you spin up local sites quickly, share previews instantly, and move changes between environments without the usual hassle — helping you focus on creating rather than configuring and troubleshooting.

Here’s how you can use it to manage multiple client WordPress sites.

Step 1: Set up a new local site

Managing Multiple Client Sites with WordPress Studio

You have three options for creating a new site in WordPress Studio:

  • Start with a blank site: Create a fresh WordPress installation.
  • Start from a Blueprint: Build a preconfigured site using a “recipe.” 
  • Import from a backup: Use the backup of an existing site. 

Here are more details on those three options.

Start with a blank site

Starting with a blank site creates a fresh WordPress installation using the default out-of-the-box configuration.

This option works well for one-off builds, but Studio can save even more time once you start using Blueprints.

Start from a Blueprint 

WordPress Studio "Start from a Blueprint" option screen.

Blueprints are reusable JSON files that act as recipes for creating preconfigured local sites — they’re one of the key ways that Studio helps you save time and reduce repetitive tasks.

Instead of setting up each project from scratch, you can create Blueprints for various website types (e.g., blogs or online stores) — defining everything your site needs, from WordPress and PHP versions to themes, plugins, settings, and content.

The Studio Assistant and interactive builder help you generate these automatically — simply tell the AI-powered assistant the site configuration, and it will create the Blueprint.

Using the WordPress Studio AI assistant to create a Blueprint.

To use a Blueprint in Studio, choose “Start from a Blueprint” and either pick one of the featured options or upload your own Blueprint file.

Studio currently offers three featured Blueprints (you can preview each in WordPress Playground):

  • Quick Start: A WordPress.com-like setup with the plugins and themes included on new Business-plan sites.
  • Development: A configuration for theme and plugin developers, with debug settings and tools like Query Monitor, Plugin Check, Theme Check, and Create Block Theme.
  • Commerce: A WooCommerce-ready setup with companion plugins for building online stores.

You can also browse the WordPress Blueprints Gallery for community-created configurations.

Import a site from a backup file

Import a site from a backup

Note: Sites on the WordPress.com Business and Commerce plans don’t need to be imported from a backup. Instead, they can use the Studio Sync feature. This is more powerful and efficient than importing from a backup file. 

You can also import a WordPress site into Studio from a backup file. This is useful if you have an existing site you’d like to work on locally.

Follow these steps to import from a backup file:

Install one of the supported backup plugins, such as the free All-in-One WP Migration and Backup plugin, on the site you’d like to import into Studio.

Then, create a backup of the site (WP AdminAll-in-One WP MigrationBackupsCreate Backup).

Creating a WordPress site backup

From here, download the backup file and load it into WordPress Studio.

Importing a site backup into WordPress Studio

When you’re done working on the local version, use the plugin to create a new backup and import that backup into the live site.

Step: Configure your local site

Studio lets you configure each local site to match the hosted environment, making sure they’re compatible. 

The local site’s environment can be configured from the “Advanced settings” panel.

Create a new local site in WordPress Studio

Whether you start with a blank site, a Blueprint, or a backup, Studio lets you adjust a range of optional settings for your local environment. For example, you can:

  • Set a custom local path: Define where the site folder is stored on your computer.
  • Change the WordPress version: Use the latest release, a specific version, or a Beta/Nightly build (useful for testing upcoming releases).
  • Change the PHP version: Match your hosting environment or test different versions.
  • Use a custom domain (must end with .local): Make accessing your local site in the browser easier.
  • Use SSL to enable HTTPS: Enable HTTPS on your local site so it closely resembles a live environment. 

Tip: You can change these settings after you’ve created a site.

Edit the local WordPress site

After configuring the environment, you can also set up the tools you want Studio to use while you work.

These settings can be accessed from SettingsPreferences.

Set the local site preferences

Your preferred tools will be used when accessing the site from the “Open in…” section of the Overview tab.

WordPress Studio Overview tab

Step 3: Develop and collaborate

Once your local site is configured, you can begin developing and testing changes. 

WordPress Studio applies updates instantly as you work, so you can move quickly and collaborate without delays.

Build locally

Your local site updates in real time — whether you’re editing files, adjusting settings in WP Admin, or adding plugins and themes.

When you do need to add plugins or themes, you can install them through WP Admin just as you would on a hosted site, or drop the files directly into the site’s folders.

If you use certain plugins or themes regularly, keeping them on your computer makes adding them to each new project even faster.

Drag and drop plugin files into the local WordPress site folders

Tip: If you reuse the same plugins across projects, Blueprints (from Step 1) let you spin up sites preconfigured with your preferred plugins, themes, and settings. Studio’s AI Assistant can also help you make updates to your local sites.

Editing a local site in WordPress Studio using the AI-powered Assistant

Edit individual files

Beyond installing plugins and themes, you can also edit your site’s files directly. 

Studio gives you quick access to those files from the Overview tab.

Open local site files in VS Code

The “Open in…” section gives you quick access to the site’s files and folders. 

This is useful if you want to edit a local site’s files, including plugin or theme files, in your preferred code editor. 

Editing local WordPress site files in a code editor

Each time you edit and save a file, your local site will immediately start using the updated version — there’s no need to wait for files to upload to a server.

Tip: Our blog post on Local WordPress Development Workflows Using WordPress Studio includes a helpful section on the ideal development workflow, whether you’re creating sites, plugins, or themes.

Share a preview of a local site

Sharing a preview of a local WordPress site created in Studio

While working on a site, you can also use the preview feature to get client and collaborator feedback.

Managing mulitple client site previews in WordPress Studio

Previews are a useful addition to any workflow because they help you get more accurate feedback, faster. 

This way, your clients get to experience the site for themselves, instead of relying on inefficient screenshots or video walkthroughs. 

All you need to do is share the temporary URL with clients and team members, and they can inspect the site snapshot remotely. 

The preview feature is powered by WordPress.com and uses a temporary domain (wp.build).  

Previewing WP Admin of a local site created in WordPress Studio

The main aspects of the preview sites feature include:

  • One-time snapshot: Any changes you make to the site after creating the preview won’t be applied to the preview (unless you update it). This lets you continue to work on the local site without affecting the preview. 
  • WP Admin access: Anyone with a WordPress user account on the site can log in to the preview to access WP Admin. 
  • Free functionality: You don’t need hosting to create a preview site, just a free WordPress.com account. 
  • Create multiple preview sites: You can have up to 10 preview sites at a time.
  • Seven-day access: Preview sites are temporary and are deleted after seven days, but updating a site restarts the seven-day expiry period.

Step 4: Connect and sync with a remote site

Sync tab of WordPress Studio

After building locally, use Studio’s Sync feature to synchronize your local and hosted sites in either direction (push or pull). 

The user-friendly interface and ability to selectively sync reduce the risk of accidental overwrites that can happen when transferring files manually.

Tip: Sync is available on WordPress.com Business and Commerce plans. These plans have Jetpack enabled by default, so your hosted site can connect to Studio and use Sync without any extra setup.

Connecting a local site to a hosted site for content syncing

You can synchronize between a local site and the hosted production and staging environments.

Synchronizing with the staging site is especially useful as it lets you test your work in a private hosted environment before moving it to the live production site.

Push local site content to staging site

As Studio supports selective sync, you can push or pull only the files, folders, or database tables you need.

Selectively sync themes files between local and staging sites

Thanks to selective sync, it’s easy to push just a theme from your local site to your hosted WordPress.com site and vice versa, leaving the rest of the site intact. 

A backup is created when you initiate a sync, so you can restore your site if necessary. An email notification is also sent when the sync completes. 

Step 5: Test on staging and push changes

Now it’s time to test the site in a staging environment — a feature available to WordPress.com Business and Commerce plans. 

This gives you a safe place to identify issues before they go live. 

For the best results, follow one of these workflows after creating a WordPress.com staging site

  • Go to your Sites list in the WordPress.com dashboard. 
  • Select the site you want to create a staging site for.
  • Open the Overview tab and click the “Add staging site +” link.
WordPress.com hosting site Overview tab

You can use the switcher to change between the production and staging environments.

Switching between production and staging enviroments

See the WordPress.com documentation to find out how staging sites work

Now that your staging site is set up, here are two workflows that show how to use WordPress Studio when working on client sites:

Workflow for new sites

This workflow for building a new client site involves creating a local site, sharing a preview, pushing to the staging site, and then pushing to the production site. 

Follow these steps:

  • Create a new local site in Studio.
  • Use the Preview feature to share your work with the client.
  • Once the client approves the work, push the local site to the staging environment. 
  • After testing, push the staging site to the production environment to make it available online.
Sync content from production site to local site

WordPress.com has a built-in Coming Soon mode with a preview feature that’s useful for controlling access to sites in development. 

Workflow for existing sites

This workflow lets you update an existing live site without overwriting important content or disrupting anything outside the changes you’ve made.

Selective Sync ensures you don’t overwrite important live content — such as form submissions, comments, orders, or anything added while you were working.

For this scenario:

  • Use Studio’s Sync feature to pull the entire live site to a local site.
  • Update the local site, such as editing the theme files. 
  • Use the Preview feature to share those changes with the client.
  • After client approval, selectively sync the theme files to push them from the local site to the staging site.
  • Test the staging site. 
  • If everything works as expected, push the relevant files and folders from the staging site to the production site without syncing the database.  
Push local site content to production site

The live site now includes your theme changes, and any other updates made while you were working locally won’t be overwritten.

Step 6: Scale across multiple clients

Once your workflow is in place, WordPress Studio makes it easy to scale your process across multiple client projects. 

Instead of repeating setup work or jumping between disconnected tools, you can reuse configurations, switch between projects instantly, and keep each site organized and isolated.

Use Studio’s core actions to stay efficient as your client list grows:

  • Create: Set up a separate local site for each client — start with a fresh installation, use a Blueprint, or import a backup.
  • Switch: Move easily between projects by switching between local sites.
  • Share: Use the Preview feature to share work with clients and collaborators.
  • Sync: If you’re on WordPress.com Business or Commerce plans, use Sync to transfer work between local, staging, and production environments.

Streamline client projects with WordPress Studio

WordPress Studio is a fast, open-source, and free way to build and manage local WordPress sites. 

It helps you save time, share work with clients more effectively, and reduce errors when transferring files. 

Blueprints let you spin up consistent, pre-configured sites in seconds, reducing setup time and repetitive work — so you can receive and apply client feedback with ease.

If you’re using WordPress.com’s Business or Commerce plans, Sync adds an extra layer by letting you move work between local, staging, and production safely and with confidence.

The bottom line: No matter where the final site is hosted, Studio helps you manage multiple client projects with less overhead and more control.





Read the whole story
alvinashcraft
2 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Responsive List of Avatars Using Modern CSS (Part 2)

1 Share

Ready for the second part? If you recall, last time we worked on a responsive list of overlapping avatar images featuring a cut-out between them.

Two rows of circular avatar images. The images overlap with one another. The first row has eight images; the second row has six images.

We are still creating a responsive list of avatars, but this time it will be a circular list.

Showing two examples of circular avatar images arranged in a circle. The first example has eight images. The second example has six images.

This design is less common than the horizontal list, but it’s still a good exercise to explore new CSS tricks.

Let’s start with a demo. You can resize it and see how the images behave, and also hover them to get a cool reveal effect.

The following demo is currently limited to Chrome and Edge, but will work in other browsers as the sibling-index() and sibling-count() functions gain broader support. You can track Firefox support in Ticket #1953973 and WebKit’s position in Issue #471.

We will rely on the same HTML structure and CSS base as the example we covered in Part 1: a list of images inside a container with mask-ed cutouts. This time, however, the positions will be different.

Responsive List of Avatars Using Modern CSS

  1. Horizontal Lists
  2. Circular Lists (You are here!)

Placing Images Around a Circle

There are several techniques for placing images around a circle. I will start with my favorite one, which is less known but uses a simple code that relies on the CSS offset property.

.container {
  display: grid;
}
.container img {
  grid-area: 1/1;
  offset: circle(180px) calc(100%*sibling-index()/sibling-count()) 0deg;
}

The code doesn’t look super intuitive, but its logic is fairly straightforward. The offset property is a shorthand, so let’s write it the longhand way to see how breaks down:

offset-path: circle(180px);
offset-distance: calc(100%*sibling-index()/sibling-count());
offset-rotate: 0deg;

We define a path to be a circle with a radius of 180px. All the images will “follow” that path, but will initially be on top of each other. We need to adjust their distance to change their position along the path (i.e., the circle). That’s where offset-distance comes into play, which we combine with the sibling-index() and sibling-count() functions to create code that works with any number of elements instead of working with exact numbers.

For six elements, the values will be as follows:

100% x 1/6 = 16.67%
100% x 2/6 = 33.33%
100% x 3/6 = 50%
100% x 4/6 = 66,67%
100% x 5/6 = 83.33%
100% x 6/6 = 100%

This will place the elements evenly around the circle. To this, we add a rotation equal to 0deg using offset-rotate to keep the elements straight so they don’t rotate as they follow the circular path. From there, all we have to do is update the circle’s radius with the value we want.

That’s my preferred approach, but there is a second one that uses the transform property to combine two rotations with a translation:

.container {
  display: grid;
}
.container img {
  grid-area: 1/1;
  --_i: calc(1turn*sibling-index()/sibling-count());
  transform: rotate(calc(-1*var(--_i))) translate(180px) rotate(var(--_i));
}

The translation contains the circle radius value and the rotations use generic code that relies on the sibling-* functions the same way we did with offset-distance.

Even though I prefer the first approach, I will rely on the second one because it allows me to reuse the rotation angle in more places.

The Responsive Part

Similar to the horizontal responsive list from the last article, I will rely on container query units to define the radius of the circle and make the component responsive.

Diagram of eight circular avatar images arranged around a circle. A red dashed line indicates the size and radius of the larger circle.
.container {
  --s: 120px; /* image size */

  aspect-ratio: 1;
  container-type: inline-size;
}
.container img {
  width: var(--s);
  --_r: calc(50cqw - var(--s)/2);
  --_i: calc(1turn*sibling-index()/sibling-count());
  transform: rotate(calc(-1*var(--_i))) translate(var(--_r)) rotate(var(--_i));
}

Resize the container in the demo below and see how the images behave:

It’s responsive, but when the container gets bigger, the images are too spread out, and I don’t like that. It would be good to keep them as close as possible. In other words, we consider the smallest circle that contains all the images without overlap.

Remember what we did in the first part: we added a maximum boundary to the margin for a similar reason. We will do the same thing here:

--_r: min(50cqw - var(--s)/2, R);

I know you don’t want a boring geometry lesson, so I will skip it and give you the value of R:

S/(2 x sin(.5turn/N))

Written in CSS:

--_r: min(50cqw - var(--s)/2,var(--s)/(2*sin(.5turn/sibling-count())));

Now, when you make the container bigger, the images will stay close to each other, which is perfect:

Let’s introduce another variable for the gap between images (--g) and update the formula slightly to keep a small gap between the images.

.container {
  --s: 120px; /* image size */
  --g: 10px;  /* the gap */

  aspect-ratio: 1;
  container-type: inline-size;
}
.container img {
  width: var(--s);
  --_r: min(50cqw - var(--s)/2,(var(--s) + var(--g))/(2*sin(.5turn/sibling-count())));
  --_i: calc(1turn*sibling-index()/sibling-count());
  transform: rotate(calc(-1*var(--_i))) translate(var(--_r)) rotate(var(--_i));
}

The Cut-Out Effect

For this part, we will be using the same mask that we used in the last article:

mask: radial-gradient(50% 50% at X Y, #0000 calc(100% + var(--g)), #000);

With the horizontal list, the values of X and Y were quite simple. We didn’t have to define Y since its default value did the job, and the X value was either 150% + M or -50% - M, with M being the margin that controls the overlap. Seen differently, X and Y are the coordinates of the center point of the next or previous image in the list.

That’s still the case this time around, but the value is trickier to calculate:

Diagram of eight circular avatar images arranged around a circle. Two line segments identify an A segment in red and a B segment in green. The first segment points to the current image represented by i. The second segment points to the next image represented by i plus 1.

The idea is to start from the center of the current image (50% 50%) and move to the center of the next image (X and Y). I will first follow segment A to reach the center of the big circle and then follow segment B to reach the center of the next image.

This is the formula:

X = 50% - Ax + Bx
Y = 50% - Ay + By

Ax and Ay are the projections of the segment A on the X-axis and the Y-axis. We can use trigonometric functions to get the values.

Ax = r x sin(i);
Ay = r x cos(i);

The r represents the circle’s radius defined by the CSS variable --_r, and i represents the angle of rotation defined by the CSS variable --_i.

Same logic with the B segment:

Bx = r x sin(j);
By = r x cos(j);

The j is similar to i, but for the next image in the sequence, meaning we increment the index by 1. That gives us the following CSS calculations for each variable:

--_i: calc(1turn*sibling-index()/sibling-count());
--_j: calc(1turn*(sibling-index() + 1)/sibling-count());

And the final code with the mask:

.container {
  --s: 120px; /* image size */
  --g: 14px;  /* the gap */

  aspect-ratio: 1;
  container-type: inline-size;
}
.container img {
  width: var(--s);
  --_r: min(50cqw - var(--s)/2,(var(--s) + var(--g))/(2*sin(.5turn/sibling-count())));
  --_i: calc(1turn*sibling-index()/sibling-count());
  --_j: calc(1turn*(sibling-index() + 1)/sibling-count());
  transform: rotate(calc(-1*var(--_i))) translate(var(--_r)) rotate(var(--_i));
  mask: radial-gradient(50% 50% at
    calc(50% + var(--_r)*(cos(var(--_j)) - cos(var(--_i))))
    calc(50% + var(--_r)*(sin(var(--_i)) - sin(var(--_j)))),
      #0000 calc(100% + var(--g)), #000);
}

Cool, right? You might notice two different implementations for the cut-out. The formula I used previously considered the next image, but if we consider the previous image instead, the cut-out goes in another direction. So, rather than incrementing the index, we decrement instead and assign it to a .reverse class that we can use when we want the cut-out to go in the opposite direction:

.container img {
  --_j: calc(1turn*(sibling-index() + 1)/sibling-count());
}
.container.reverse img {
  --_j: calc(1turn*(sibling-index() - 1)/sibling-count());
}

The Animation Part

Similar to what we did in the last article, the goal of this animation is to remove the overlap when an image is hovered to fully reveal it. In the horizontal list, we simply set its margin property to 0, and we adjust the margin of the other images to prevent overflow.

This time, the logic is different. We will rotate all of the images except the hovered one until the hovered image is fully visible. The direction of the rotation will depend on the cut-out direction, of course.

Eight avatar images arranged around a circle. An arrow points to the same thing showing what happens when hovering over the avatar positioned at the top of the circle.

To rotate the image, we need to update the --_i variable, which is used as an argument for the rotate function. Let’s start with an arbitrary value for the rotation, say 20deg.

.container img {
  --_i: calc(1turn*sibling-index()/sibling-count());
}
.container:has(:hover) img {
  --_i: calc(1turn*sibling-index()/sibling-count() + 20deg);
}
.container.reverse:has(:hover) img {
  --_i: calc(1turn*sibling-index()/sibling-count() - 20deg);
}

Now, when an image is hovered, all of images rotate by 20deg. Try it out in the following demo.

Hmm, the images do indeed rotate, but the mask is not following along. Don’t forget that the mask considers the position of the next or previous image defined by --_j and the next/previous image is rotating — hence we need to also update the --_j variable when the hover happens.

.container img {
  --_i: calc(1turn*sibling-index()/sibling-count());
  --_j: calc(1turn*(sibling-index() + 1)/sibling-count());
}
.container.reverse img {
  --_j: calc(1turn*(sibling-index() - 1)/sibling-count());
}
.container:has(:hover) img {
  --_i: calc(1turn*sibling-index()/sibling-count() + 20deg);
  --_j: calc(1turn*(sibling-index() + 1)/sibling-count() + 20deg);
}
.container.reverse:has(:hover) img {
  --_i: calc(1turn*sibling-index()/sibling-count() - 20deg);
  --_j: calc(1turn*(sibling-index() - 1)/sibling-count() - 20deg);
}

That’s a lot of redundant code. Let’s optimize it a little by defining additional variables:

.container img {
  --_a: 20deg;

  --_i: calc(1turn*sibling-index()/sibling-count() + var(--_ii, 0deg));
  --_j: calc(1turn*(sibling-index() + 1)/sibling-count() + var(--_jj, 0deg));
}
.container.reverse img {
  --_i: calc(1turn*sibling-index()/sibling-count() - var(--_ii, 0deg));
  --_j: calc(1turn*(sibling-index() - 1)/sibling-count() - var(--_jj, 0deg));
}
.container:has(:hover) img {
  --_ii: var(--_a);
  --_jj: var(--_a);
}

Now the angle (--_a) is defined in one place, and I consider two intermediate variables to add an offset to the --_i and --_j variables.

The rotation of all the images is now perfect. Let’s disable the rotation of the hovered image:

.container img:hover {
  --_ii: 0deg;
  --_jj: 0deg;
}

Oops, the mask is off again! Do you see the issue?

We want to stop the hovered image from rotating while allowing the rest of the images to rotate. Therefore, the --_j variable of the hovered image needs to update since it’s linked to the next or previous image. So we should remove --_jj: 0deg and keep only --_ii: 0deg.

.container img:hover {
  --_ii: 0deg;
}

That’s a little better. We fixed the cut-out effect on the hovered image, but the overall effect is still not perfect. Let’s not forget that the hovered image is either the next or previous image of another image, and since it’s not rotating, another --_j variable needs to remain unchanged.

For the first list, it’s the variable of the previous image that should remain unchanged. For the second list, it’s the variable of the next image:

/* select previous element of hovered */
.container:not(.reverse) img:has(+ :hover),
/* select next element of hovered */
.container.reverse img:hover + * {
  --_jj: 0deg;
}

In case you are wondering how I knew to do this, well, I tried both ways and I picked the one that worked. It was either the code above or this:

.container:not(.reverse) img:hover + *,
.container.reverse img:has(+ :hover) {
  --_jj: 0deg;
}

We are getting closer! All the images behave correctly except for one in each list. Try hovering all of them to identify the culprit.

Can you figure out what we are missing? Think a moment about it.

Our list is circular, but the HTML code is not, so even if the first and last images are visually placed next to each other, in the code, they are not. We cannot link both of them using the adjacent sibling selector (+). We need two more selectors to cover those edge cases:

.container.reverse:has(:last-child:hover) img:first-child,
.container:not(.reverse):has(:first-child:hover) img:last-child {
  --_jj: 0deg;
}

Oof! We have fixed all the issues, and now our hover effect is great, but it’s still not perfect. Now, instead of using an arbitrary value for the rotation, we need to be accurate. We have to find the smallest value that removes the overlap while keeping the images as close as possible.

Showing the gap between two images at three different points. The first and third points are too close and too spaced out, respectively. The middle point is perfect with just enough space between the images.

We can get the value with some trigonometry. I’ll skip the geometry lesson again (we have enough headaches as it is!) and give you the value:

--_a: calc(2*asin((var(--s) + var(--g))/(2*var(--_r))) - 1turn/sibling-count());

Now we can say everything is perfect!

Conclusion

This one was a bit tough, right? Don’t worry if you got a bit lost with all the complex formulas. They are very specific to this example, so even if you have already forget about them, that’s fine. The goal was to explore some modern features and a few CSS tricks such as offset, mask, sibling-* functions, container query units, min()/max(), and more!

Responsive List of Avatars Using Modern CSS

  1. Horizontal Lists
  2. Circular Lists (You are here!)

Responsive List of Avatars Using Modern CSS (Part 2) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Read the whole story
alvinashcraft
2 minutes ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories