#311: Event Tracking & Appcues

Marie & Chris talk about the brand new event tracking feature in Appcues. We’re still pretty stoked we can even afford Appcues, since it’s super pricey software for our company. The fact that they just released event tracking dashboards makes it feel much more affordable though, because this unlocks super valuable information for us instantly. It’s like we get what would get by also buying super expensive analytics software, only it’s rolled into what we already pay for.

Sponsor: Netlify

High five to Netlify for the sponsorship. This is a tiny part of Netlify, but just think about how cool Netlify Drop is. For all sorts of people, but particularly for beginners and teachers, this little feature makes the web feel real. Imagine you take a quick class learning HTML and CSS, and make a “fake” site on your local computer. That might feel kinda neat, but then you drop it on Netlify Drop, and it becomes an honest-to-god real website on the internet that anyone in the world can see. That’s big.

Timejumps

  • 01:41 What does Appcues do?
  • 03:21 Build vs Buy
  • 06:30 Deciding what and who to measure
  • 08:15 Sponsor: Netlify
  • 10:06 Cost of Appcues
  • 13:58 What we’ve found from Appcues
  • 17:53 Event tracking inside CodePen
  • 26:34 Finding out about bottom events

The post #311: Event Tracking & Appcues appeared first on CodePen Blog.

How to Perform IP Address Lookup with Google Sheets

Websites can determine the visitor’s geographic location using their IP address and serve more relevant content. For example, a weather website may use your IP address to estimate your approximate location and provide weather forecast for your current city automatically. A currency exchange website can determine your default currency based on your country which is detected from your IP address.

Google Sheets - IP 2 Location

There are free web IP lookup services, ip2c.org for example, that will reveal the country of your client’s IP address with a simple HTTP request. We internally use that service at Digital Inspiration to determine the payment service provider on the checkout page.

Bulk IP Lookup with Google Sheets

IP2Location is another good alternative that retrieves more detailed geolocation information for any IP address. The IP location lookup service can retrieve the client’s country, city name, region, the ISP name and more.

If you have a bulk list of IP addresses, you can use Google Sheets to estimate the corresponding geographic details for each of the addresses in few easy steps:

  1. Click here to make a copy of the Google Sheet for performing IP lookups in bulk.

  2. Paste the list of IP addresses in column A, one per row. The lookup service works for both IPv4 and IPv6 addresses.

  3. Enter your key in cell E1. If you have a small list of IP address, use demo as the key or get your own API key from ip2location.com.

  4. Click the Run button, authorize the script and watch as the geographic details and ISP names are populated in the sheet.

IP2Location Web Service Demo

How IP2Location Script Works

Internally, the Google Sheet uses the IP2location web service with Google Apps Script to transform IP addresses into geographic region.

It uses the UrlFetchApp service to perform multiple HTTP requests in a single batch for improved performance. Here’s the full source code:

const ip2location = () => {
  // Get all the input data from Google Sheet
  const ss = SpreadsheetApp.getActiveSheet();
  const data = ss.getDataRange().getDisplayValues();

  // Use your own API key or use demo key
  const apiKey = data[0][4] || "demo";

  // Generate API URL for IP address
  const getUri_ = (ipAddress) => {
    const API_URL = "https://api.ip2location.com/v2";
    return `${API_URL}/?ip=${ipAddress}&key=${apiKey}&package=ws4`;
  };

  const requests = [];

  for (let r = 2; r < data.length; r++) {
    const [ipAddress, countryName] = data[r];
    // Only process rows where the country is blank
    if (ipAddress && !countryName) {
      requests.push({ url: getUri_(ipAddress), rowNumber: r + 1 });
    }
  }

  // Make API calls in bulk using the UrlFetchApp service
  UrlFetchApp.fetchAll(requests).forEach((content, i) => {
    // Parse the JSON response
    const { city_name, country_name, isp, response } = JSON.parse(content);

    // If the response is populated, the API call failed
    if (response) throw new Error(response);

    // Write the response data to Google Sheet
    const values = [[country_name, region_name, city_name, isp]];
    ss.getRange(requests[i].rowNumber, 2, 1, 4).setValues(values);
  });

  // Flush all changes
  SpreadsheetApp.flush();
};

Build Even Faster Quarkus Applications With fast-jar

Quarkus is already fast, but what if you could make inner loop development with the supersonic, subatomic Java framework even faster? Quarkus 1.5 introduced fast-jar, a new packaging format that supports faster startup times. Starting in Quarkus 1.12, this great feature became the default packaging format for Quarkus applications. This article introduces you to the fast-jar format and how it works.

Note: The ninth annual global Java developer productivity report found that more developers are implementing business applications with Quarkus. Quarkus’s support for live coding with fast startup and response times lets developers focus more on business logic implementations rather than wasting time on jobs such as recompiling and redeploying code and continuously restarting the runtime environment.

Should You Use GitOps?

The last decade of programming has seen a number of revolutionary transformations. One has arisen from a monolithic application to a cloud-based microservices running in containers. Another one has come from a set of practices around the DevOps methodology to align development and operation teams into a shared work process. And it’s not finished, introduced in 2017 by Weaveworks, the GitOps methodology is becoming the new standard to move faster in production in a reliable/secure way while continuing to bring teams to work closer.

What Is GitOps?

GitOps is another methodology of work that aims to optimize the time/effort between the developers and the operation team members. The main component of the GitOps methodology is obviously Git, a versioning source control tool acting as a single source of truth for declarative infrastructure and application configurations.

Introduction to Spring Boot and JDBCTemplate: JDBC Template

As with any programming language, Java has several tools to make easy integration between the language and the database. There are several tools such as Hibernate, Eclipse Link, JPA specification, and so on. However, the ORM brings several issues, and sometimes it does not sense to use it and then use it a Java Communication layer or JDBC. This tutorial will cover a way to simplify the code with JDBC using the Spring JDBC Template.

Mapping frameworks such as ORM reduces a lot of boilerplate, reduces the duplicated code, avoids bugs, and doesn’t reinvent the wheel. However, an Object-relational impedance mismatch that brings a set of conceptual and technical difficulties is often encountered when an RDBMS is being served by an application program written in an object-oriented programming language.

FSE Outreach Round #5: Venturing out a Query Quest

The Full Site Editing (FSE) outreach program is chugging along. Since December, it has called for and completed four rounds of testing. The latest round asks volunteers to provide feedback on the Query block, arguably one of the most crucial pieces of the FSE-puzzle.

Automattic Developer Relations Wrangler Anne McCarthy has been overseeing the program since its inception. Each round of testing asks participants to follow along with a set of instructions while testing a specific feature related to FSE. They can then provide feedback on what does or does not work. Thus far, the program has tested and identified issues for template-editing mode, building a custom homepage, creating a 404 page, and wrangling a restaurant-themed header.

Volunteers for the program should install the latest version of the Gutenberg plugin and the TT1 Blocks theme. Participation is open to all, and further details are available through the announcement post.

The latest test is all about the Query block — McCarthy is dubbing it a “Query Quest.”

“Not many blocks get an entire milestone dedicated to them but the Query Block did!” she wrote. “As the name implies, this is a pretty powerful block allowing you to display posts/pages on your site and customize them as you see fit. For example, you could easily use this block to show off all of your favorite recipes by setting it up to show a specific category of posts.”

Generally, theme authors will primarily work with this block. However, for those end-users who will inevitably want to customize post output on their sites, they may need to at least have some basic familiarity with it or its block variations.

Building With the Query Block

Following the instructions for the testing round netted fairly consistent results between the editor and front end. Each step walks participants through the process of assembling a two-column page with posts from separate categories. Within just a few minutes, I built a few demo posts with custom categories named “Veggie Garden” and “Fruit Trees” (side note: these are pics of my plants). I sped through the process with no issues.

Adding two Query blocks to the WordPress editor, each in their own columns.
Using Columns to output two category-based Query blocks.

However, I am a bit of a pro at this point with the Query block. It is one feature I have been eyeing at least every week for months.

The two primary issues I ran into were with the “read more” link and spacing. For the more-link, it simply did not appear on the front end. When viewing the source code, the wrapper HTML was there, but the actual text was nowhere to be found.

As for spacing, this is more of a theme problem. I have harped on this issue in past testing rounds, and it is the same ol’ tune. TT1 Blocks failed to produce consistent spacing between the front and back end. One example is when using the Post Featured Image block followed by the Post Excerpt block. In the editor, there is little whitespace between the two. On the front end, there is ample room.

Some might say it is the most vital part of page building — nailing down the layout. I have voiced my concerns ad nauseam on spacing, so I have nothing new to report on the subject.

I decided to take a few extra steps and move beyond the basic testing instructions. Because it is springtime, I have been enjoying the outdoors a bit more as of late. I wanted to spruce up my Query block design. I wrapped the initial design in a Cover block with a garden-related background image, dropped in some header and intro text, and created boxes for my posts with the Group block. With a splash of color, some font-size changes, and a Spacer block here and there, I built something with a tiny bit more personality.

A cover block with background image behind a two-columned posts list.
Custom layout with the Query, Cover, and Columns blocks.

Testing never has to be boring. I encourage participants to grab inspiration from their own lives as they venture out on their Query Quest.

While I have my complaints about the site editor and realize we are miles away from the long-term vision, it is also amazing what is now possible. Even six months ago, building something as simple as this was not happening. More and more each day, I believe a public beta of the site editor and other FSE components in WordPress 5.8 is not such a bad idea.

Generating (and Solving!) Sudokus in CSS

I love to make CSS do stuff it shouldn’t. It’s the type of problem-solving brain training you’d get building a calculator in Minecraft, except you probably won’t get a job working with Minecraft Redstone no matter how good you get at that, whereas CSS skills are worth actual money, and many generalist programmers are scared of CSS, so studying it can be a way to stand out from the pack. Also, when you’ve done the impossible with CSS, all normal CSS tasks seem easy.

I’ve read interesting discussions on the web about whether CSS is a Turing complete language and whether CSS and HTML qualify as programming languages. I haven’t decided, but I can say that in the quest to support common UI patterns in a standard way, some of the newer CSS features blur the line between styling and functionality.

Challenging ourselves to solve logical problems with only CSS and HTML can force us to spend quality time with some of the newish, programing-like features of CSS, such as custom properties and logical functions. It still wasn’t clear how these could be used to build a Sudoku solver using only CSS, but as crazy as the idea sounded, the constraint-based logic of Sudoku seemed like it might be compatible with the declarative nature of CSS, so I wasn’t shocked to find someone else claimed to have built a “CSS3 Sudoku solution solver.” As it turned out, this was more like a sudoku validator in CSS than a solver. It also used a tiny bit of JavaScript to work with textboxes.

After days of valiantly trying to build a full Sudoku solver and generator app in pure CSS, I learned three things.

  1. You can unit test Sass functions and mixins, which is awesome. If you’re heavily using and reusing these Sass features, which is what they are meant for, they become as mission-critical and as scary to change as any other part of your codebase. They deserve tests around them.
  2. Chrome DevTools shows an infinite spinner of death when you throw 50MB of Sass-generated CSS at it.
  3. Maybe it’s impossible to translate something like this Python script into pure CSS. Maybe.

However, we can achieve a Sudoku solver and generator app for 16-square Sudoku which you can play with below, then we’ll break down how its features work. Where is your god now, simple puzzle intended for young children?

The value selector

Since we’re experimenting with CSS, we are contractually obligated to include something visually interesting, though nothing too over-the-top as Sudoku players seem to appreciate a UI that stays out of the way. In my opinion, the way you select numbers on some of the Sudoku apps could be more intuitive, so I decided to apply the radial menu UI pattern, which dates all the way back to days of black and white Macintosh and is still popular in modern video games. Someone actually built a nice pure CSS library for radial menus, but I got a crush on React Planet as I love the way it captures both selecting an item with the circle around it, and how it attractively displays the available actions. I wanted to see if I could build a similar effect with just CSS.

I took some of the dashed circle code from this Pen and then made the numbers out of labels using the old border-radius: 50% trick, then I used absolute positioning to make the numbers “stick” to the correct point on the dashed circle even when the animation makes it change size.

.context .number.top {
  background: green;
  margin-left: auto;
  margin-right: auto;
  left: 0;
  right: 0;
  top: -12.5px;
}

.context .number.left {
  background: orange;
  margin-top: auto;
  margin-bottom: auto;
  top: 0;
  bottom: 0;
  left: -12.5px;
}

The animation fades the number picker in while making its z-index higher so it becomes clickable. We are also animating the top and left margin from 50% to zero so the circle expands from the center to fill the available space.

@keyframes bounce-out {
  0% {
    z-index: -1;
    width: 35%;
    height: 35%;
    margin-left: 50%;
    margin-top: 50%;
    opacity: 0;
  }
  100% {
    z-index: 2;
    opacity: 1;
    width: var(--circle-radius);
    height: var(--circle-radius);
  }
}

Then, to simulate bouncy physics similar to React Planet, I use a cubic-bezier() function on the animation. The website easings.net was a big help in making easing functions easy.

.context {
  animation: bounce-out cubic-bezier(.68,-0.6,.32, 2.5) .5s forwards;        
}

Both the selection of values and the behavior of opening the value selector for the selected square operate using radio button hacks, to remember which values were selected and achieve mutual exclusivity. CSS-Tricks has an excellent article on checkbox and radio button hacks, so I won’t repeat that information here, but I will show how we set CSS variables at the level of the Sudoku CSS grid based on checkboxes, as it’s central to the way this experiment works.

As we are using variables, we can get the same behavior when a value is set, regardless of whether it’s the user checking a box to specify a value, or the puzzle generator setting that same value for the square. There are many combinations of squares and values, so we are using Sass rather than writing all the combinations out by hand. We are also creating separate bit values for each value-square combination, and another custom property to tell us whether the square is unsolved. That’s because CSS gives us limited ability to compare one value to another (it’s possible but can be tricky). We are defining these values in a way that might look a bit odd at first, but will make life easier for us when it comes to validating whether a set of Sudoku square values is solvable or not.

@for $i from 1 through 16 {
  @for $j from 1 through 4 {
    #select-#{$j}-value-square-#{$i}:checked ~ .sudoku  {
       --square-#{$i}-unsolved: 0; 
       --square-#{$i}-equals-#{$j}: 1;
    }
  }
}

Validating the grid

Doctor Google tells us that even with only 16 squares, there are four billion possible combinations of four numbers. But a program that brute forces all these combinations and outputs the ones that are valid according to the rules of Sudoku shows that there are only 288 valid solutions in 4×4 Sudoku, which is a big difference from the number of possible valid solutions in a 9×9 grid. With only 288 possible solutions, this is where Sass can really come into its own. I’m still not sure if CSS is a Turing complete language, but Sass is, and it gives us some proper data structures, such as lists. With a bit of regex magic we can transform the list of valid 4×4 puzzles linked above into a Sass-powered two-dimensional list!

$solutions: ((1,2,3,4,3,4,1,2,2,1,4,3,4,3,2,1),(3,1,2,4,2,4,1,3,1,3,4,2,4,2,3,1),(1,2,3,4,3,4,1,2,2,3,4,1,4,1,2,3),/*...many lines later...*/(2,4,3,1,3,1,4,2,4,2,1,3,1,3,2,4),(4,3,2,1,2,1,4,3,3,4,1,2,1,2,3,4));

Sweet! If our CSS hack were a multi-tier application, this would be our database. Validation could have used the same approach of checking row and column values like the 9×9 validator we saw in the introduction, but since we know the answer it seems like we shouldn’t need to bother checking blocks and columns and rows. Instead, we can check whether the entered numbers could still be a valid puzzle or not. In pseudocode this might look something like:

foreach (s in squares) 
{
  if (solutionsContains(s.value, s.index) or s.isUnsolved())
  {
    showValidationError();
  } 
} 

Remember we created those weird variables whenever a square value is selected?

--square-#{$i}-unsolved: 0;        
--square-#{$i}-equals-#{$j}: 1;

So now we have answers to both questions in the condition in line 3 of the pseudocode above, but how can we do a logical OR operator in CSS? There’s a great article on CSS-Tricks about using calc() to simulate logic operators in CSS, and I’m not sure I would have even thought of some of the code in my Sudoku solver without it, but some of the formulas explained in that article get a bit unwieldy, especially if you want to do nested ANDs and ORs with more than two operands. For example, we need the CSS equivalent of this pseudocode:

if ((squareOneEqualsOne and squareTwoEqualsTwo /*...*/ and squareSixteenEqualsOne) or (squareOneEqualsOne and squareTwoEqualsThree /*...*/ and squareSixteenEqualsOne))
  {
    sudokuIsValid();
  } 
}

Well, that article showing how to do logic using calc() was written in 2019. Nowadays, in addition to calc(), we have the well-supported min() and max() math functions which meet our needs even better. If you Google “CSS min, max and clamp” (the last of which is just convenient sugar for a combination of min() and max()), you’ll find many examples are showing how they can be used to simplify fluid typography. That’s one compelling use case, but you can use these math functions anywhere you’d use a number, which adds a lot of power to CSS. For example, if you pass bit flag variables to CSS min(), that’s equivalent to AND. If you pass the same flags to CSS max(), that’s equivalent to OR. We can prove this using the following truth tables.

ABA AND Bmin(A, B)
0000
1000
0100
1111
ABA OR Bmax(A, B)
0000
1011
0111
1111

We can get pretty sophisticated with that, especially when you add the helpful fact that we are allowed to do anything calc() can do within min() and max(). CSS just took a step closer to being its own weird scripting language. Now we can implement the condition in our validation pseudocode above in CSS. (In practice, we generate this from Sass since it’s very repetitive.)

.sudoku { 
  --square-1-matches-puzzle-1: max(var(--square-1-unsolved), var(--square-1-equals-1, 0));
  --square-2-matches-puzzle-1: max(var(--square-2-unsolved), var(--square-2-equals-2, 0));
  /*...*/
  --square-16-matches-puzzle-1: max(var(--square-16-unsolved), var(--square-16-equals-1, 0));
  --puzzle-1-found: min(var(--square-1-matches-puzzle-1), 
  /*...*/ 
  var(--square-16-matches-puzzle-1));
  --solution-found: max(var(--puzzle-1-found), /*...*/ var(--puzzle-288-found));
}

By checking if each square is either unsolved or has a value that exists in the same position in one of our pre-calculated solutions from the Sass 2D list, we can produce a variable that tells us whether the currently defined squares exist in a valid 4×4 sudoku puzzle. Now as long as we can find something numeric that will drive a behavior in CSS, we can base that CSS behavior on --solution-found. For example, to make our grid turn red when it’s invalid we can put this in each square:

.square {
  color: rgb(calc(255 * (1 - var(--solution-found))), 0, 0);
}

Not every CSS property can be driven by a number, but many can, and both z-index and opacity are especially versatile CSS properties for this usage. Other behaviors can be trickier but often achievable. For example, I was a bit stuck thinking about how to trigger the shake animation for an invalid grid with just a numeric bit flag property so that the grid would shake any time it became invalid, but this is a great example of how hacking CSS forces you to read the specs and understand the edge cases for each property. I found my solution on this page about animation-duration.

A value of 0s, which is the default value, indicates that no animation should occur.

So we can base animation duration of the shake animation on --solution-found and remove the animation each time a number is clicked using the :active pseudo-class to make the animation replay any time the solution becomes invalid, and do nothing otherwise.

#select-#{$j}-value-square-#{$i}:active  { 
  animation: none;
}

 #select-#{$j}-value-square-#{$i}:checked ~ .sudoku {
  animation: shake cubic-bezier(.36,.07,.19,.97) calc((clamp(0, 1 - var(--solution-found), 1)) * 1s) forwards;
}

A pure CSS Sudoku app would probably be impossible if we didn’t have CSS custom properties, and they are more powerful than they may seem at first glance. The way they get reevaluated and update the UI whenever a property they depend on changes is like a simpler version of the reactivity you get from a fancy JavaScript framework like Vue. It’s fair to say reactivity is built right into the browser in the form of CSS variables!

Now that we have this approach for validation and our stylesheet knows the solution in its subconscious any time we set valid values in our Sudoku, we are close to implementing the solver!

Solving every 4×4 Sudoku

Remember when we introduced these intermediate variables?

.sudoku { 
  --puzzle-1-found: min(var(--square-1-matches-puzzle-1), 
  /*...*/ 
  var(--square-16-matches-puzzle-1));
}

That wasn’t only to make the validation code easier to write and understand. Knowing which of the 288 possible puzzle(s) are matched allows us to write the solver!

#no-solution {
  z-index: 1;
  color: red;
}
@for $solution-index from 1 through 288 {
label[for=solution-#{$solution-index}] {
  cursor: pointer;
  z-index: calc(var(--puzzle-#{$solution-index}-found) * #{$solution-index});
}

#solution-#{$solution-index}:checked ~ .sudoku  {
  @for $square from 1 through 16 {
    --square-#{$square}-solution:"#{nth(nth($solutions, $solution-index), $square)}";
    --square-#{$square}-color: grey;
    --auto-#{$square}: 1;
  }

I put the optional plural in the word “puzzle(s)” above because, if the user hasn’t filled out many squares, it’s possible there are multiple valid solutions. I dig solvers like this JavaScript one that can quickly produce a solution even if you haven’t specified enough values for a human to be able to solve it without guessing.

The trick in my CSS solver is that while the “solve” button looks like a single button, it’s actually 288 radio button labels stacked one on top of the other — but all of them look the same. Imagine a stack of cards: they all have the same design on the back, but different values on the front. The solver logic is putting the card with the solution on the top of the pile with z-index, so if you pick it up and read the other side of it, you will always have the correct solution. It still works if there are multiple correct solutions, because the solution that comes later in our list of valid answers will be placed on top, since we calculate the z-index by multiplying the flag by $solution-index. If no solutions are matched, the z-index of all the solve buttons will be zero and, since the disabled version of the button with the “invalid puzzle” message has z-index of one, it will appear on top. If puzzle number one is the solution, we will still see the puzzle one button, since the invalid button comes earlier in the HTML.

Stacking context can behave unexpectedly if you haven’t read up on it, so this is is a nice illustration of one of the non-obvious stacking behaviors.

Generating puzzles

We can think of the generating puzzles as another version of the solver with extra requirements.

  1. Some random squares need to be left unsolved when the puzzle generator button is pressed.
  2. The combination of randomly unsolved squares and a correct solution should be different each time the generator button is pressed.
  3. Pressing the solve button should reveal the complete solution.
  4. If the user manually solves the generated puzzle, we’d like to reward them with a victory screen that gives feedback about how fast they solved it.

CSS has no random() function (though Sass does), so it might not be obvious how we can get a different behavior each time we push the same button. But the solver explanation above was a bit of a spoiler as it already does something similar with a button that looks like a single element but is actually different depending on the current valid solution.

The question with the “generate” button is how we can get an unpredictable result each time we click. Full credit to Alvaro Montoro for his article on CSS-Tricks about how to generate seemingly random values with just CSS. The combination of radio button hacks and animating the stacking order seems to work nicely. I tried hard to see if I could do it without extra markup, but I concluded that this approach is the best and the simplest. To reuse the deck of cards analogy from the solver explanation, it’s like the deck of puzzle cards is invisibly shuffling all the time so that whenever you take a card, you discover it has a different face.

We can combine this pseudo randomness with the actual randomness offered by the Sass random() function to give our Sudoku game replay value.

@for $j from 0 through 287 {
  label[for=generate#{$j}] { 
    animation-delay: #{$j * .35s}; 
  }
  label[for=generate#{$j}]:active:after {
    z-index: 300;
    width: 100%;
  }
  #generate#{$j}:checked ~ .sudoku { 
    $blockCounts: (1: 2, 2: 2, 3: 3, 4: 2);
    $shuffleSquares: (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);

     @for $square from 1 through 16 {
       $index1: random(16);
       $index2: random(16);

       $temp: nth($shuffleSquares, $index1);
       $shuffleSquares: set-nth($shuffleSquares, $index1, nth($shuffleSquares, $index2));
       $shuffleSquares: set-nth($shuffleSquares, $index2, $temp);
     }

     @each $square in $shuffleSquares {
       $row: ceil($square/4);
      $column: 1 + ($square - 1) % 4;

       $block: if($row &lt; 3, 1, 3) + if($column < 3, 0, 1);
       $count: map-get($blockCounts, $block);
       $val: nth(nth($solutions, $j + 1), $square);

       --square-#{$square}-solution-#{$val}: 1;

       @if ($count > 0) {
         $blockCounts: map-merge($blockCounts, ($block: $count - 1));
        --square-#{$square}-unsolved: 0; 
        --square-#{$square}-equals-#{$val}: 1;

        @for $other-value from 1 through 4 {
          @if ($other-value != $val) {
            --square-#{$square}-equals-#{$other-value}: 0;   
          }
        }

        --square-#{$square}-color: grey;
        --auto-#{$square}: 1;
      }
    } 
  }
}

For each “block” (that’s Sudoku-speak for those 4×4 sections of the Sudoku grid with the thick border around them), we use Sass to randomly choose two out of four squares to solve, except for one “gimme” square which only has one unsolved square. Since the validation logic and solver logic uses the variables rather than being directly based on which values were checked using the value selector, the validation and solving logic both behave the same way. That means generated values are treated the same as if the user had individually selected each value.

The solving timer

Here’s the timer ticking through the first eleven seconds.

We’ll dive into the CSS for the solving timer in a moment, but let’s first show what one of the digits looks like without CSS overflow set to hidden, and with a green border around the element to show the part that would be visible to the user at each step of the animation.

We are using an infinitely repeating keyframes animation to shift the list of possible digits one character to the left at a desired interval (we use a monospaced font so that we can be sure each character will occupy the same exact width). The seconds digit will go from zero up to nine, and the next digit should only go up to five, increasing once per ten seconds before both digits of the seconds need to reset to zero.

Each digit is animating using the same technique you can use to animate a spritesheet in CSS, except instead of animatedly shifting an image background to achieve an animation effect, we are shifting a pseudo element containing the possible digits.

As with many tasks in CSS, there is more than one way to make an animated counter in CSS. But some don’t work cross-browser and really demand a preprocessor to keep the code succinct. I like my approach because it’s fairly short. CSS does the heavy lifting to figure out when and how to move to the next digit. All the markup needs to do is create a placeholder where each digit goes, giving us some freedom for how we present our timer.

Here’s the markup:

<div class="stopwatch">  
  <div class="symbol"></div>
  <div class="symbol">:</div>
  <div class="symbol"></div>
  <div class="symbol"></div>
</div>

…and the CSS:

.stopwatch {
  text-align: center;
  font-family: monospace;
  margin-bottom: 10px;
}

.symbol {
  width: 1ch;
  overflow: hidden;
  display: inline-flex;
  font-size: 5ch;
}

.symbol:nth-child(1)::after {
  animation: tens 3600s steps(6, end) infinite;
  content: '012345';
}

.symbol:nth-child(2)::after {
  animation: units 600s steps(10, end) infinite;
  content: '0123456789';
}

.symbol:nth-child(4)::after {
  animation: tens 60s steps(6, end) infinite;
  content: '012345';
}

.symbol:nth-child(5)::after {
  animation: units 10s steps(10, end) infinite;
  content: '0123456789';
}

@keyframes units {
  to {
    transform: translateX(-10ch);
  }
}

@keyframes tens {
  to {
    transform: translateX(-6ch);
  }
}

You might notice that the counter starts again from the beginning after an hour. That’s because all of the iteration counts are set to infinite . We could fix it, but I figure if someone spends an hour solving one of these, they have bigger problems than a children’s Sudoku puzzle. 😛

What would be unfair, though, is if we allowed the same timer to just keep ticking even when the user generates a fresh puzzle. Can we make it reset? It turns out we’ve solved that problem already in the first step in this article, where we removed, and conditionally re-added, the animation for our number selector using the :active pseudo-class. This time, it’s actually simpler because every time we hit the “generate” button, we want to remove the animation on all the digits to take them back to zero. Then the animation will start again when the radio button is no longer active. So, it’s only one line of CSS we need to make the timer reset each time we generate!

input[name=generate]:active ~ .stopwatch .symbol::after {
  animation: none;
}

Sudokumeter™️

Even when the puzzle is solved, I want to offer replay value by giving the player visual feedback on their time performance and challenging them to solve puzzles faster. I also want to reward you for making it this far through the article by giving you a minimalist circular gauge you could reuse in your own projects. Here’s a standalone Pen with the circular gauge for you to experiment:

We’re applying the same principles used in the win screen from the game except, in this Pen, the rating displayed is controlled with radio button hacks, whereas in the game it’s controlled by animation that slowly moves to a lower rating as time passes. The gauge in the game is hidden using zero opacity and is only displayed (and paused) when we detect that the puzzle has been manually solved.

Let’s explain how we create the illusion of a semi-circle that’s divided into two sides by color. It’s actually a full CSS circle with its bottom half hidden using overflow: hidden.

We apply the two colors using a pseudo-element that fills half of the <div>.

Then we cut a hole in the middle to make a donut, using another circle filled with the game’s background color, and center that inside the larger circle using flexbox.

Next, hide half of it by making the size of the container half as tall as the full circle and, again, using overflow: hidden.

Now, if we rotate our donut, it looks like the gauge is filling up with green or losing green, depending on whether we rotate our donut by negative or positive degrees!

We’d like to put labels on both ends of the gauge and a description in between them, and it turns out flexbox is an elegant solution:

#rating {
  font-size: 30px;
  display: flex;
  width: 300px;
  justify-content: space-between;
}

Here’s the markup:

<div id="rating">
  <div id="turtle">🐢</div>
  <div id="feedback"></div>
  <div id="rabbit">🐇</div>
</div>

That’s all we need to position our labels. If the rating <div> is the width of the diameter of our circle, flexbox will position the emoji labels at the ends and the description in the center of the circle!

As for controlling what the description says, it’s similar to the trick we used for our timer, except this time we do it vertically rather than horizontally since the feedback descriptions are of variable length. But they are always the same height.

Conclusion

I opened this article with questions about whether CSS is a programming language. It’s hard to argue the logic we were able to implement using just CSS wasn’t programming, but some of it is unusual usage of CSS to say the least. As with many things in the tech world, the answer seems to be “it depends,” and as much as we learned about CSS through this experiment, we’ve also illustrated that programming is as much about the mindset as the tech.

No CSS hacking article is complete without the disclaimer that although we’ve shown we can implement sophisticated logic in CSS and learn a lot in the process, most of the time we probably shouldn’t do this in production, because of maintainability, accessibility, and some other words ending with “ility.”

But we also saw that some things — such as what I think of as the built-in reactivity you get with CSS variables — are quite convenient in CSS but might require us to go through hoops in JavaScript, and probably use a framework. By pushing the limits of CSS we ended up creating a circular gauge that I believe could reasonably be used in a production app, and might even be the right thing compared to reaching for some JavaScript widget that might be heavy and do more than we really need.

On my wishlist for the CSS Sudoku app is a reset button. Currently, if you want to start a new Sudoku game, you have to refresh the page. That’s an inherent limitation of radio button hacks which makes CSS hacking different to conventional programming. At one stage, I believed I found a solution when I thought animations could be used to set CSS variables — but it turns out that is part of CSS Houdini and only supported in Chromium-based browsers. If and when that’s supported everywhere, it will open a Pandora’s box of hacks and be a lot of fun. In a future article, I may even explore why this innocuous feature we have in Chrome is a game changer for CSS hacking.

The jury is still out on whether a full 81-square Sudoku solver is possible in CSS, but if you’re curious to find out, leave your feedback in the comments. If enough people want it, we may go down that rabbit hole together and see what dark corners of CSS we can illuminate in the process.


The post Generating (and Solving!) Sudokus in CSS appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Building a Settings Component

This is a tremendous CSS-focused tutorial from Adam Argyle. I really like the “just for gap” concept here. Grid is extremely powerful, but you don’t have to use all its abilities every time you reach for it. Here, Adam reaches for it for very light reasons like using it as an in-between border alternative as well as more generic spacing. I guess he’s putting money where his mouth is in terms of gap superseding margin!

I also really like calling out Una Kravet’s awesome name for flexible grids: RAM. Perhaps you’ve seen the flexible-number-of-columns trick with CSS grid? The bonus trick here (which I first saw from Evan Minto) is to use min(). That way, not only are large layouts covered, but even the very smallest layouts have no hard-coded minimum (like if 100% is smaller than 10ch here):

.el {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(10ch, 100%), 35ch));
}

There is a ton more trickery in the blog post. The “color pops” with :focus-within is fun and clever. So much practical CSS in building something so practical! 🧡 more blog posts like this, please. Fortunately, we don’t have to wait, as Adam has other component-focused posts like this one on Tabs and this one on Sidenav.

Direct Link to ArticlePermalink


The post Building a Settings Component appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

php save json to csv

Hello i'm working on a small project that i have to retrieve movies data from a json API, then save the result into a csv.
i'm able to display data with php eco but when i'm trying to write csv file i get the following
Warning: array_keys() expects parameter 1 to be array, object given on line 8
Warning: fputcsv() expects parameter 2 to be array, null given on line 9
Warning: array_flip() expects parameter 1 to be array, null given on line 10
Warning: array_merge(): Argument #1 is not an array on line 11
Warning: fputcsv() expects parameter 2 to be array, null given 11

$toplink = "https://api.themoviedb.org/3/movie/top_rated?api_key=3356bddf22b611f259916342a1a3ed50"
$toprated = file_get_contents($toplink);
   $toprated = json_decode($toprated);
   $top = $toprated->results;
    $header = false;
    foreach ($top as $row){
        if (empty($header)){
            $header = array_keys($row);
            fputcsv($fp, $header);
            $header = array_flip($header);
          }
        fputcsv($fp, array_merge($header, $row));
    }
    fclose($fp);
 ?>

Namecheap Review

If you’re looking for affordable website hosting backed by customer support and impressive uptime, you’ll find all of those components in Namecheap. Both a domain registration and a website hosting provider, Namecheap also offers private email plans.

While it has some of the most affordable web hosting plans available, it does have some downfalls. Still, Namecheap is a practical option with the features to support you whether you’re just starting out with your first site or are looking for specialized VPN hosting.

Namecheap Pros and Cons

Pros

  • Guaranteed uptime
  • Many plan options
  • Offers EasyWP
  • 24/7 customer support

Cons

  • No phone support
  • Charge for SSL certificates
  • Questionable speeds
Compare The Best Web Hosting Providers
We reviewed dozens of web hosting providers and narrowed them down to the best options.
See Top Picks

How Namecheap Compares to Top Web Hosting Providers

As a budget web hosting provider, Namecheap has plenty of competition.

Namecheap offers a variety of plans and functionality that can support both the beginning website owner with little technical knowledge to the more advanced techie looking for full control over their hosting. In terms of pricing, Namecheap is on par with platforms like DreamHost and Hostinger. Hostinger offers slightly better value than Namecheap, especially if you’ve chosen a Namecheap plan that doesn’t include a free SSL certificate.

Other factors, like a 99.99% uptime and 24/7 customer support, are the same between Namecheap and Hostinger.

If you’re looking for the all-around best web hosting provider, then Bluehost needs to be at the top of your list. Its shared hosting is incredibly affordable, while 24/7 support is available by both phone and live chat. Analytics and SEO tools make it easier to get the most out of your site.

And if it’s superior customer service you’re after, then consider SiteGround. While its plan prices do increase after their introductory periods, SiteGround has fabulous customer reviews. All of its shared hosting plans encompass WordPress, and their ultra-fast servers ensure your site loads quickly and reliably.

Namecheap Uptime

When choosing a website host, it’s essential to consider the uptime that the provider delivers. Downtime can cost you sales, lose you visitors, and means that you could miss out on important leads.

Namecheap offers impressive uptime guarantees, though those guarantees depend on the account type. Reseller and VPS accounts are guaranteed to have 99.9% monthly uptime. Its shared, business, and dedicated server accounts are backed by a 100% uptime guarantee.

Namecheap also stands behind these guarantees, offering a service cycle extension in exchange for service outages. Customers receive one full day of hosting for each hour of downtime exceeding the agreement in their plans. Namecheap will provide up to one month of free hosting per year via this policy.

While the free hosting won’t replace lost sales, business, or leads, it does indicate that Namecheap is motivated to avoid downtime.

Namecheap Security

After investing so much work into a site, it’s equally as important to invest in keeping that site secure and protected. Namecheap offers an impressive array of security solutions.

Namecheap customers receive many of these security features free of charge. Two-factor authentication with U2F, TOTP, and OneTouch is provided for free to maximize account protection and customer data.

Every domain registration or transfer also includes one year of the WhoisGuard privacy protection service. This service keeps your information out of the Whois database, so you don’t receive annoying spam calls and emails.

SSL certificates can help to protect customers against identity theft, and Namecheap offers SSL certificates starting at $4.88 per year. SSL certificates are included for free for one year with shared hosting plans.

If you’re looking for additional security, you’ll find plenty of options with Namecheap. Its VPN service can let you work securely when you’re on public WiFi, so you can maintain your privacy while browsing or gaming.

And its PremiumDNS feature works with any domain and delivers faster DNS performance. Designed for businesses, it closes security gaps and helps to prevent attacks so your site stays online and you stay in business.

Namecheap Speed

Fast speeds are essential in keeping visitors on your website and in delivering a quality user experience. While there are many adjustments you can make to maximize your site’s speed, you’ll always be somewhat dependent on your host’s speed.

Namecheap states that it uses the “latest and fastest” Dell and Supermicro web servers. SSD drives are protected with RAID, and each server is equipped with at least 16 Intel Xeon CPU cores and 64 GB of Ram to increase capacity and enhance performance.

Namecheap references placing a “lighter load” on its computers to give your site “breathing room” during busy times, but customer reviews suggest that while Namecheap speeds are decent, they aren’t the fastest ones you’ll find.

You’ll need to consider how much this might impact your site. A large, complex site filled with interactive elements or videos might see a significant decrease in performance when hosted by Namecheap. A smaller, more basic site without so many elements might perform just fine.

Namecheap Plan Options

The same hosting plan doesn’t work for all businesses. If you’re just starting out with a brand-new website and are expecting a few hundred visitors a month, you won’t need the same plan that a well-established business with multiple sites and more than 50,000 visitors a month needs.

Namecheap offers an assortment of plans to help you find just what you need without creating so many options that things get overwhelming. Pricing is upfront and easily understood, and Namecheap will even suggest appropriate plans based on whether you’re an online beginner, a small business, or a large business.

Because the hosting plans clearly outline how many monthly visitors they’ll support and how much storage comes with each plan, you’ll know exactly what you’re getting. Discounts are available on services for your first year’s worth of services for most plans.

Namecheap Customer Service

Namecheap offers multiple support options. The Help Center offers a robust collection of guides, blogs, how-to videos, and other detailed information about the Namecheap products. FAQs and step-by-step instructions are also sorted by product, ensuring you can quickly find the information you need.

The Help Center also houses the Namecheap status updates. These updates detail server maintenance details so site owners can monitor site performance and stay informed about completed and upcoming changes.

Customers who need additional support can submit a ticket or use Namecheap’s live chat function. Namecheap offers 24/7 customer support, but the apparent lack of phone support could make addressing urgent issues difficult.

Namecheap Migration

Migrating a site to a new host can be an overwhelming process when you haven’t done it before, but a good hosting provider will be able to help you through the process. A great provider will take care of just about everything for you.

That’s where Namecheap stands out. This hosting provider will perform the entire site migration for you. You’ll need to provide Namecheap with your hosting account credentials, allowing support team members to log in and migrate your site over. The credentials that you send through a support ticket are protected by SSL encryption.

Namecheap guarantees that your site will be migrated within 24 hours and that it will experience no more than 15 minutes of downtime. This guarantee and precise timeline let you plan ahead and schedule the migration for a time when you know you’ll have lower traffic. Namecheap offers a credit equal to your first shared hosting purchase if this guarantee isn’t met.

All Namecheap hosting migration is free.

Namecheap Shared Hosting

Shared hosting is one of the most common types of website hosting. With a shared hosting plan, your site will use the same server as many other sites, and you’ll get part of the server’s computing capabilities.

Namecheap offers three shared hosting plans that all include a free website builder, unmetered bandwidth, and domain name and privacy protection. These plans are backed by a 30-day money-back guarantee.

All shared hosting plans include 50 free PositiveSSL certificates for one year, and they’re automatically installed, saving you time and ensuring your site is ready to go. The cPanel puts you in control of your website, ideal for building and customizing your site. cPanel is one of the most popular control panels, so you or your web developer may already be familiar with it.

Namecheap WordPress Hosting

If you’re looking to get a WordPress site up and running quickly, Namecheap might be the way to go. Namecheap uses EasyWP so you can bypass the cPanel, creating a new site, or managing all of your WordPress sites from this single dashboard. EasyWP makes it easy to access your files, perform a one-click backup and one-click restore, and easily connect any domain.

According to Namecheap, its WordPress speeds are three times faster than standard WordPress speeds you’ll experience with traditional shared hosting options.

Namecheap’s three WordPress plans are suitable for many different site owners. The EasyWP Starter plan includes 10 GB of storage and supports 50,000 visitors a month, so it’s plenty powerful for startup businesses. The upper tier, the EasyWP Supersonic plan, delivers 100 GB of storage and supports 500,000 visitors a month. It also includes a free SSL certificate, so you’ll have everything you need to support your business website.

Namecheap Reseller Hosting

Namecheap’s reseller hosting is available in three packages, each one delivering a free cPanel account, and unmetered bandwidth. Plans are also backed by a 30-day money-back guarantee.

The cPanel dashboard allows you to manage everything about your site. Reseller accounts can add unlimited domains and subdomains, and the unmetered bandwidth is ideal for those heavy-traffic days or seasons.

Namecheap also offers a suite of customization tools. White-label marketing tools let you truly customize your site to your needs, while the WHMCS can automate and streamline your billing and support.

Namecheap VPS Hosting

Similar to shared hosting, with VPS hosting, you’ll share one server with other users. But VPS hosting is closer to dedicated hosting since you have access to a separate disk partition and certain computing resources.

Namecheap offers two VPS hosting plans that allow you to choose your server management, that allow you to transfer existing websites to Namecheap, and that feature top security. You’ll also have root access and will be able to select your operating system.

If you have technical experience, you can fully customize your VPS and can install a cPanel if you wish. Solid State Drive storage is fash and leads to increased website speed and performance. Hardware and software firewalls help to keep your data safe, and the servers are backed up routinely.

Namecheap Domain Registration

In addition to its website hosting services, Namecheap also offers domain registration services that can help you to start a brand-new site.

Customers who already have an idea of what they want their domain to be can use the traditional search function to find available and related domains. Namecheap offers upfront pricing about its domain registration, renewal, and transfer rates, so customers can compare options at a glance and decide what’s right for them.

Namecheap’s bulk domain name search option delivers even more options, and is ideal for businesses who may be trying to identify relevant domains, who are trying to keep similar domains out of competitors’ hands, or who are in the business of buying and selling domains.

The bulk domain name search lets you enter up to 5,000 terms at once for a single search. This helpful tool increases your search power and can help you discover new domain ideas and opportunities. You can upload keywords in a convenient spreadsheet and be looking at your search results in seconds.

Namecheap offers several other perks that make its domain registration services stand out. An all-in-one package option bundles together a free domain with web hosting. Monthly coupons available right on the site offer discounts on domain registration and transfer. Customers with more than 50 active domains on Namecheap can join the VIP Rewards Club to get discounts on domains. Namecheap even maintains a search option that identifies 99-cent domain names for customers looking for a bargain.

With many different domain purchase options and helpful tools like the bulk domain name search and the 99-cent domain name search, Namecheap is an excellent option if you’re looking to buy additional domains.

Namecheap Email

An email address that features your business domain puts a professional touch on all of your correspondence, and Namecheap offers three ad-free private email plans. Each plan includes a custom domain-based email, two-factor authentication, anti-spam protection, and more.

Web-based email browser support makes it easy to access your email, and the Pro and Ultimate plans include full mobile sync support so you can check your email on the go.

Namecheap offers a free two-month trial for private email plans.

Compare The Best Web Hosting Providers
We reviewed dozens of web hosting providers and narrowed them down to the best options.
See Top Picks

Namecheap offers some of the lowest rates that you’ll find for website hosting. Thanks to its domain registration and email services, it can function as a full-service platform for your business, keeping your domain, website, and email centralized on one platform.

While Namecheap stands out with its clear pricing and wide array of plan options, its lack of phone customer service and the cost of its SSL certificates are strikes against the company. If you want to save money with your web hosting, Namecheap is worth considering.

Many of its plans are backed by 30-day money-back guarantees, so you can feel confident in giving Namecheap a try.

Going “Meta GSAP”: The Quest for “Perfect” Infinite Scrolling

I‘m not sure how this one came about. But, it‘s a story. This article is more about grokking a concept, one that’s going to help you think about your animations in a different way. It so happens that this particular example features infinite scrolling — specifically the “perfect” infinite scroll for a deck of cards without duplicating any of them.

Why am I here? Well, this all started from a tweet. A tweet that got me thinking about layouts and side-scrolling content.

I took that concept and used it on my site. And it’s still there in action at the time of writing.

Then I got to thinking more about gallery views and side-scrolling concepts. We hopped on a livestream and decided to try and make something like the old Apple “Cover Flow” pattern. Remember it?

My first thoughts for making this assumed I‘d make this so it works without JavaScript, as it does in the demo above, in a way that uses “progressive enhancement.” I grabbed Greensock and ScrollTrigger, and off we went. I came away from that work pretty disappointed. I had something but couldn‘t quite get infinite scrolling to work how the way I wanted. The “Next” and “Previous” buttons didn’t want to play ball. You can see it here, and it requires horizontal scrolling.

So I opened up a new thread on the Greensock forum. Little did I know I was about to open myself up to some serious learning! We solved the issue with the buttons. But, being me, I had to ask whether something else was possible. Was there a “clean” way to do infinite scrolling? I‘d tried something on stream but had no luck. I was curious. I’d tried a technique like that used in this pen which I created for the ScrollTrigger release.

The initial answer was that it is kinda tricky to do:

The hard part about infinite things on scroll is that the scroll bar is limited while the effect that you’re wanting is not. So you have to either loop the scroll position like this demo (found in the ScrollTrigger demos section) or hook directly into the scroll-related navigation events (like the wheel event) instead of actually using the actual scroll position.

I figured that was the case and was happy to leave it “as-is.” A couple of days passed and Jack dropped a reply that kinda blew my mind when I started digging into it. And now, after a bunch of going through it, I’m here to share the technique with you.

Animate anything

One thing that is often overlooked with GSAP, is that you can animate almost anything with it. This is often because visual things are what spring to mind when thinking about animation — the actual physical movement of something. Our first thought isn’t about taking that process to a meta-level and animating from a step back.

But, think about animation work on a larger scale and then break it down into layers. For example, you play a cartoon. The cartoon is a collection of compositions. Each composition is a scene. And then you have the power to scrub through that collection of compositions with a remote, whether it’s on YouTube, using your TV remote, or whatever. There are almost three levels to what is happening.

And this is the trick we need for creating different types of infinite loops. This is the main concept right here. We animate the play head position of a timeline with a timeline. And then we can scrub that timeline with our scroll position.

Don‘t worry if that sounds confusing. We’re going to break it down.

Going “meta”

Let‘s start with an example. We’re going to create a tween that moves some boxes from left to right. Here it is.

Ten boxes that keep going left to right. That’s quite straightforward with Greensock. Here, we use fromTo and repeat to keep the animation going. But, we have a gap at the start of each iteration. We’re also using stagger to space out the movement and that’s something that will play an important role as we continue.

gsap.fromTo('.box', {
  xPercent: 100
}, {
  xPercent: -200,
  stagger: 0.5,
  duration: 1,
  repeat: -1,
  ease: 'none',
})

Now comes the fun part. Let’s pause the tween and assign it to a variable. Then let’s create a tween that plays it. We can do this by tweening the totalTime of the tween, which allows us to get or set the tween’s playhead tween, while considering repeats and repeat delays.

const SHIFT = gsap.fromTo('.box', {
  xPercent: 100
}, {
  paused: true,
  xPercent: -200,
  stagger: 0.5,
  duration: 1,
  repeat: -1,
  ease: 'none',
})

const DURATION = SHIFT.duration()

gsap.to(SHIFT, {
  totalTime: DURATION,
  repeat: -1,
  duration: DURATION,
  ease: 'none',
})

This is our first “meta” tween. It looks exactly the same but we’re adding another level of control. We can change things on this layer without affecting the original layer. For example, we could change the tween ease to power4.in. This completely changes the animation but without affecting the underlying animation. We’re kinda safeguarding ourselves with a fallback.

Not only that, we might choose to repeat only a certain part of the timeline. We could do that with another fromTo, like this:

The code for that would be something like this.

gsap.fromTo(SHIFT, {
  totalTime: 2,
}, {
  totalTime: DURATION - 1,
  repeat: -1,
  duration: DURATION,
  ease: 'none'
})

Do you see where this is going? Watch that tween. Although it keeps looping, the numbers flip on each repeat. But, the boxes are in the correct position.

Achieving the “perfect” loop

If we go back to our original example, there’s a noticeable gap between each repetition.

Here comes the trick. The part that unlocks everything. We need to build a perfect loop.

Let‘s start by repeating the shift three times. It’s equal to using repeat: 3. Notice how we’ve removed repeat: -1 from the tween.

const getShift = () => gsap.fromTo('.box', {
  xPercent: 100
}, {
  xPercent: -200,
  stagger: 0.5,
  duration: 1,
  ease: 'none',
})

const LOOP = gsap.timeline()
  .add(getShift())
  .add(getShift())
  .add(getShift())

We’ve turned the initial tween into a function that returns the tween and we add it to a new timeline three times. And this gives us the following.

OK. But, there’s still a gap. Now we can bring in the position parameter for adding and positioning those tweens. We want it to be seamless. That means inserting each each set of tweens before the previous one ends. That’s a value based on the stagger and the amount of elements.

const stagger = 0.5 // Used in our shifting tween
const BOXES = gsap.utils.toArray('.box')
const LOOP = gsap.timeline({
  repeat: -1
})
  .add(getShift(), 0)
  .add(getShift(), BOXES.length * stagger)
  .add(getShift(), BOXES.length * stagger * 2)

If we update our timeline to repeat and watch it (while adjusting the stagger to see how it affects things)…

You‘ll notice that there‘s a window in the middle there that creates a “seamless” loop. Recall those skills from earlier where we manipulated time? That’s what we need to do here: loop the window of time where the loop is “seamless.”

We could try tweening the totalTime through that window of the loop.

const LOOP = gsap.timeline({
  paused: true,
  repeat: -1,
})
.add(getShift(), 0)
.add(getShift(), BOXES.length * stagger)
.add(getShift(), BOXES.length * stagger * 2)

gsap.fromTo(LOOP, {
  totalTime: 4.75,
},
{
  totalTime: '+=5',
  duration: 10,
  ease: 'none',
  repeat: -1,
})

Here, we’re saying tween the totalTime from 4.75 and add the length of a cycle to that. The length of a cycle is 5. And that’s the middle window of the timeline. We can use GSAP’s nifty += to do that, which gives us this:

Take a moment to digest what‘s happening there. This could be the trickiest part to wrap your head around. We’re calculating windows of time in our timeline. It’s kinda hard to visualize but I’ve had a go.

This is a demo of a watch that takes 12 seconds for the hands go round once. It‘s looped infinitely with repeat: -1 and then we‘re using fromTo to animate a specific time window with a given duration. If you, reduce the time window to say 2 and 6, then change the duration to 1, the hands will go from 2 o‘clock to 6 o’clock on repeat. But, we never changed the underlying animation.

Try configuring the values to see how it affects things.

At this point, it’s a good idea to put together a formula for our window position. We could also use a variable for the duration it takes for each box to transition.

const DURATION = 1
const CYCLE_DURATION = BOXES.length * STAGGER
const START_TIME = CYCLE_DURATION + (DURATION * 0.5)
const END_TIME = START_TIME + CYCLE_DURATION

Instead of using three stacked timelines, we could loop over our elements three times where we get the benefit of not needing to calculate the positions. Visualizing this as three stacked timelines is a neat way to grok the concept, though, and a nice way to help understand the main idea.

Let’s change our implementation to create one big timeline from the start.

const STAGGER = 0.5
const BOXES = gsap.utils.toArray('.box')

const LOOP = gsap.timeline({
  paused: true,
  repeat: -1,
})

const SHIFTS = [...BOXES, ...BOXES, ...BOXES]

SHIFTS.forEach((BOX, index) => {
  LOOP.fromTo(BOX, {
    xPercent: 100
  }, {
    xPercent: -200,
    duration: 1,
    ease: 'none',
  }, index * STAGGER)
})

This is easier to put together and gives us the same window. But, we don’t need to think about math. Now we loop through three sets of the boxes and position each animation according to the stagger.

How might that look if we adjust the stagger? It will squish the boxes closer together.

But, it’s broken the window because now the totalTime is out. We need to recalculate the window. Now’s a good time to plug in the formula we calculated earlier.

const DURATION = 1
const CYCLE_DURATION = STAGGER * BOXES.length
const START_TIME = CYCLE_DURATION + (DURATION * 0.5)
const END_TIME = START_TIME + CYCLE_DURATION

gsap.fromTo(LOOP, {
  totalTime: START_TIME,
},
{
  totalTime: END_TIME,
  duration: 10,
  ease: 'none',
  repeat: -1,
})

Fixed!

We could even introduce an “offset” if we wanted to change the starting position.

const STAGGER = 0.5
const OFFSET = 5 * STAGGER
const START_TIME = (CYCLE_DURATION + (STAGGER * 0.5)) + OFFSET

Now our window starts from a different position.

But still, this isn’t great as it gives us these awkward stacks at each end. To get rid of that effect, we need to think about a “physical” window for our boxes. Or think about how they enter and exit the scene.

We’re going to use document.body as the window for our example. Let’s update the box tweens to be individual timelines where the boxes scale up on enter and down on exit. We can use yoyo and repeat: 1 to achieve entering and exiting.

SHIFTS.forEach((BOX, index) => {
  const BOX_TL = gsap
    .timeline()
    .fromTo(
      BOX,
      {
        xPercent: 100,
      },
      {
        xPercent: -200,
        duration: 1,
        ease: 'none',
      }, 0
    )
    .fromTo(
      BOX,
      {
        scale: 0,
      },
      {
        scale: 1,
        repeat: 1,
        yoyo: true,
        ease: 'none',
        duration: 0.5,
      },
      0
    )
  LOOP.add(BOX_TL, index * STAGGER)
})

Why are we using a timeline duration of 1? It makes things easier to follow. We know the time is 0.5 when the box is at the midpoint. It‘s worth noting that easing won’t have the effect we usually think of here. In fact, easing will actually play a part in how the boxes position themselves. For example, an ease-in would bunch the boxes up on the right before they move across.

The code above gives us this.

Almost. But, our boxes disappear for a time in the middle. To fix this, let’s introduce the immediateRender property. It acts like animation-fill-mode: none in CSS. We’re telling GSAP that we don’t want to retain or pre-record any styles that are being set on a box.

SHIFTS.forEach((BOX, index) => {
  const BOX_TL = gsap
    .timeline()
    .fromTo(
      BOX,
      {
        xPercent: 100,
      },
      {
        xPercent: -200,
        duration: 1,
        ease: 'none',
        immediateRender: false,
      }, 0
    )
    .fromTo(
      BOX,
      {
        scale: 0,
      },
      {
        scale: 1,
        repeat: 1,
        zIndex: BOXES.length + 1,
        yoyo: true,
        ease: 'none',
        duration: 0.5,
        immediateRender: false,
      },
      0
    )
  LOOP.add(BOX_TL, index * STAGGER)
})

That small change fixes things for us! Note how we’ve also included z-index: BOXES.length. That should safeguard us against any z-index issues.

There we have it! Our first infinite seamless loop. No duplicate elements and perfect continuation. We’re bending time! Pat yourself on the back if you’ve gotten this far! 🎉

If we want to see more boxes at once, we can tinker with the timing, stagger, and ease. Here, we have a STAGGER of 0.2 and we’ve also introduced opacity into the mix.

The key part here is that we can make use of repeatDelay so that the opacity transition is quicker than the scale. Fade in over 0.25 seconds. Wait 0.5 seconds. Fade back out over 0.25 seconds.

.fromTo(
  BOX, {
    opacity: 0,
  }, {
    opacity: 1,
    duration: 0.25,
    repeat: 1,
    repeatDelay: 0.5,
    immediateRender: false,
    ease: 'none',
    yoyo: true,
  }, 0)

Cool! We could do whatever we want with those in and out transitions. The main thing here is that we have our window of time that gives us the infinite loop.

Hooking this up to scroll

Now that we have a seamless loop, let’s attach it to scroll. For this we can use GSAP’s ScrollTrigger. This requires an extra tween to scrub our looping window. Note how we’ve set the loop to be paused now, too.

const LOOP_HEAD = gsap.fromTo(LOOP, {
  totalTime: START_TIME,
},
{
  totalTime: END_TIME,
  duration: 10,
  ease: 'none',
  repeat: -1,
  paused: true,
})

const SCRUB = gsap.to(LOOP_HEAD, {
  totalTime: 0,
  paused: true,
  duration: 1,
  ease: 'none',
})

The trick here is to use ScrollTrigger to scrub the play head of the loop by updating the totalTime of SCRUB. There are various ways we could set up this scroll. We could have it horizontal or bound to a container. But, what we‘re going to do is wrap our boxes in a .boxes element and pin that to the viewport. (This fixes its position in the viewport.) We’ll also stick with vertical scrolling. Check the demo to see the styling for .boxes which sets things to the size of the viewport.

import ScrollTrigger from 'https://cdn.skypack.dev/gsap/ScrollTrigger'
gsap.registerPlugin(ScrollTrigger)

ScrollTrigger.create({
  start: 0,
  end: '+=2000',
  horizontal: false,
  pin: '.boxes',
  onUpdate: self => {
    SCRUB.vars.totalTime = LOOP_HEAD.duration() * self.progress
    SCRUB.invalidate().restart()
  }
})

The important part is inside onUpdate. That’s where we set the totalTime of the tween based on the scroll progress. The invalidate call flushes any internally recorded positions for the scrub. The restart then sets the position to the new totalTime we set.

Try it out! We can go back and forth in the timeline and update the position.

How cool is that? We can scroll to scrub a timeline that scrubs a timeline that is a window of a timeline. Digest that for a second because that‘s what’s happening here.

Time travel for infinite scrolling

Up to now, we‘ve been manipulating time. Now we’re going to time travel!

To do this, we‘re going to use some other GSAP utilities and we‘re no longer going to scrub the totalTime of LOOP_HEAD. Instead, we’re going to update it via proxy. This is another great example of going “meta” GSAP.

Let’s start with a proxy object that marks the playhead position.

const PLAYHEAD = { position: 0 }

Now we can update our SCRUB to update the position. At the same time, we can use GSAP’s wrap utility, which wraps the position value around the LOOP_HEAD duration. For example, if the duration is 10 and we provide the value 11, we will get back 1.

const POSITION_WRAP = gsap.utils.wrap(0, LOOP_HEAD.duration())

const SCRUB = gsap.to(PLAYHEAD, {
  position: 0,
  onUpdate: () => {
    LOOP_HEAD.totalTime(POSITION_WRAP(PLAYHEAD.position))
  },
  paused: true,
  duration: 1,
  ease: 'none',
})

Last, but not least, we need to revise ScrollTrigger so it updates the correct variable on the SCRUB. That’s position, instead of totalTime.

ScrollTrigger.create({
  start: 0,
  end: '+=2000',
  horizontal: false,
  pin: '.boxes',
  onUpdate: self => {
    SCRUB.vars.position = LOOP_HEAD.duration() * self.progress
    SCRUB.invalidate().restart()
  }
})

At this point we’ve switched to a proxy and we won’t see any changes.

We want an infinite loop when we scroll. Our first thought might be to scroll to the start when we complete scroll progress. And it would do exactly that, scroll back. Although that‘s what we want to do, we don’t want the playhead to scrub backwards. This is where totalTime comes in. Remember? It gets or sets the position of the playhead according to the totalDuration which includes any repeats and repeat delays.

For example, say the duration of the loop head was 5 and we got there, we won‘t scrub back to 0. Instead, we will keep scrubbing the loop head to 10. If we keep going, it‘ll go to 15, and so on. Meanwhile, we‘ll keep track of an iteration variable because that tells us where we are in the scrub. We’ll also make sure that we only update iteration when we hit the progress thresholds.

Let’s start with an iteration variable:

let iteration = 0

Now let’s update our ScrollTrigger implementation:

const TRIGGER = ScrollTrigger.create({
  start: 0,
  end: '+=2000',
  horizontal: false,
  pin: '.boxes',
  onUpdate: self => {
    const SCROLL = self.scroll()
    if (SCROLL > self.end - 1) {
      // Go forwards in time
      WRAP(1, 1)
    } else if (SCROLL < 1 && self.direction <; 0) {
      // Go backwards in time
      WRAP(-1, self.end - 1)
    } else {
      SCRUB.vars.position = (iteration + self.progress) * LOOP_HEAD.duration()
      SCRUB.invalidate().restart() 
    }
  }
})

Notice how we‘re now factoring iteration into the position calculation. Remember that this gets wrapped with the scrubber. We‘re also detecting when we hit the limits of our scroll, and that’s the point where we WRAP. This function sets the appropriate iteration value and sets the new scroll position.

const WRAP = (iterationDelta, scrollTo) => {
  iteration += iterationDelta
  TRIGGER.scroll(scrollTo)
  TRIGGER.update()
}

We have infinite scrolling! If you have one of those fancy mice with the scroll wheel that you can let loose on, give it a go! It’s fun!

Here’s a demo that displays the current iteration and progress:

Scroll snapping

We‘re there. But, there are always ”nice to haves” when working on a feature like this. Let’s start with scroll snapping. GSAP makes this easy, as we can use gsap.utils.snap without any other dependencies. That handles snapping to a time when we provide the points. We declare the step between 0 and 1 and we have 10 boxes in our example. That means a snap of 0.1 would work for us.

const SNAP = gsap.utils.snap(1 / BOXES.length)

And that returns a function we can use to snap our position value.

We only want to snap once the scroll has ended. For that, we can use an event listener on ScrollTrigger. When the scroll ends, we are going to scroll to a certain position.

ScrollTrigger.addEventListener('scrollEnd', () => {
  scrollToPosition(SCRUB.vars.position)
})

And here’s scrollToPosition:

const scrollToPosition = position => {
  const SNAP_POS = SNAP(position)
  const PROGRESS =
    (SNAP_POS - LOOP_HEAD.duration() * iteration) / LOOP_HEAD.duration()
  const SCROLL = progressToScroll(PROGRESS)
  TRIGGER.scroll(SCROLL)
}

What are we doing here?

  1. Calculating the point in time to snap to
  2. Calculating the current progress. Let’s say the LOOP_HEAD.duration() is 1 and we’ve snapped to 2.5. That gives us a progress of 0.5 resulting in an iteration of 2, where 2.5 - 1 * 2 / 1 === 0.5 . We calculate the progress so that it’s always between 1 and 0.
  3. Calculating the scroll destination. This is a fraction of the distance our ScrollTrigger can cover. In our example, we’ve set a distance of 2000 and we want a fraction of that. We create a new function progressToScroll to calculate it.
const progressToScroll = progress =>
  gsap.utils.clamp(1, TRIGGER.end - 1, gsap.utils.wrap(0, 1, progress) * TRIGGER.end)

This function takes the progress value and maps it to the largest scroll distance. But we use a clamp to make sure the value can never be 0 or 2000. This is important. We are safeguarding against snapping to these values as it would put us in an infinite loop.

There is a bit to take in there. Check out this demo that shows the updated values on each snap.

Why are things a lot snappier? The scrubbing duration and ease have been altered. A smaller duration and punchier ease give us the snap.

const SCRUB = gsap.to(PLAYHEAD, {
  position: 0,
  onUpdate: () => {
    LOOP_HEAD.totalTime(POSITION_WRAP(PLAYHEAD.position))
  },
  paused: true,
  duration: 0.25,
  ease: 'power3',
})

But, if you played with that demo, you‘ll notice there‘s an issue. Sometimes when we wrap around inside the snap, the playhead jumps about. We need to account for that by making sure we wrap when we snap — but, only when it’s necessary.

const scrollToPosition = position => {
  const SNAP_POS = SNAP(position)
  const PROGRESS =
    (SNAP_POS - LOOP_HEAD.duration() * iteration) / LOOP_HEAD.duration()
  const SCROLL = progressToScroll(PROGRESS)
  if (PROGRESS >= 1 || PROGRESS < 0) return WRAP(Math.floor(PROGRESS), SCROLL)
  TRIGGER.scroll(SCROLL)
}

And now we have infinite scrolling with snapping!

What next?

We’ve completed the groundwork for a solid infinite scroller. We can leverage that to add things, like controls or keyboard functionality. For example, this could be a way to hook up “Next” and “Previous” buttons and keyboard controls. All we have to do is manipulate time, right?

const NEXT = () => scrollToPosition(SCRUB.vars.position - (1 / BOXES.length))
const PREV = () => scrollToPosition(SCRUB.vars.position + (1 / BOXES.length))

// Left and Right arrow plus A and D
document.addEventListener('keydown', event => {
  if (event.keyCode === 37 || event.keyCode === 65) NEXT()
  if (event.keyCode === 39 || event.keyCode === 68) PREV()
})

document.querySelector('.next').addEventListener('click', NEXT)
document.querySelector('.prev').addEventListener('click', PREV)

That could give us something like this.

We can leverage our scrollToPosition function and bump the value as we need.

That’s it!

See that? GSAP can animate more than elements! Here, we bent and manipulated time to create an almost perfect infinite slider. No duplicate elements, no mess, and good flexibility.

Let’s recap what we covered:

  • We can animate an animation. 🤯
  • We can think about timing as a positioning tools when we manipulate time.
  • How to use ScrollTrigger to scrub an animation via proxy.
  • How to use some of GSAP’s awesome utilities to handle logic for us.

You can now manipulate time! 😅

That concept of going “meta” GSAP opens up a variety of possibilities. What else could you animate? Audio? Video? As for the ”Cover Flow” demo, here’s where that went!


The post Going “Meta GSAP”: The Quest for “Perfect” Infinite Scrolling appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Getting the value of a specific ID.

Hello,

As the title says, my question is: how I can get the values for specific ID? For example, my plugin creates a PDF that automatically fills in with information that I get using API from my CRM platform. So, what I am trying to do now, is that in my contract template I have a section where the information must be filled in with custom attributes information (NAME of the custom attribute ID 20 = VALUE of the custom attribute ID 20).

Down below is a foreach code that get me all the custom attributes at once:

foreach ($response['attributes'] as $set) {
    echo "ID: {$set['id']},\r\n"
    . "Client ID: {$set['clientId']},\r\n"
    . "Custom Attribute ID: {$set['customAttributeId']},\r\n"
    . "Name: {$set['name']},\r\n"
    . "Key: {$set['key']},\r\n"
    . "Value: {$set['value']},\r\n"
    . "Client Zone Visible: " . ($set['clientZoneVisible'] ? "true" : "false") . "\r\n";
}

This is the message that I get from the foreach code when I am accessing the plugin page on my platform:

ID: 11, Client ID: 1238, Custom Attribute ID: 18, Name: USER, Key: user, Value: CT565244, Client Zone Visible: true ID: 12, Client ID: 1238, Custom Attribute ID: 19, Name: PAROLA, Key: parola, Value: qwerty1234, Client Zone Visible: true ID: 13, Client ID: 1238, Custom Attribute ID: 20, Name: Serie C.I., Key: serieCI, Value: KZ, Client Zone Visible: true ID: 14, Client ID: 1238, Custom Attribute ID: 21, Name: Numar C.I., Key: numarCI, Value: 565244, Client Zone Visible: true ID: 15, Client ID: 1238, Custom Attribute ID: 22, Name: CNP, Key: cnp, Value: 5010214261989, Client Zone Visible: true ID: 16, Client ID: 1238, Custom Attribute ID: 23, Name: Emis de, Key: emisDe, Value: SPCLEP Navodari, Client Zone Visible: true ID: 17, Client ID: 1238, Custom Attribute ID: 24, Name: Data emiterii, Key: dataEmiterii, Value: 2019-02-21, Client Zone Visible: true

And here is the HTML code where I want to get the name and value for custom attribute ID X:

&nbsp;<strong>Seria:&nbsp;</strong>SERIA BULETIN // CUSTOM ATTRIBUTE ID 20
&nbsp;<strong>NR.:&nbsp;</strong>NR. BULETIN // CUSTOM ATTRIBUTE ID 21
&nbsp;<strong>CNP:&nbsp;</strong>COD NUMERIC PERSONAL // CUSTOM ATTRIBUTE ID 22

Here is the code for $response contents:

// API doRequest - Client Information & Custom Attributes
$response = UCRMAPIAccess::doRequest(sprintf('clients/%d', $clientId),
    'GET',
    [
        'fullAddress' => $cFAddress,
        'firstName' => $cFName,
        'lastName' => $cLName,
        'companyTaxId' => $cCompanyTaxID,
        'companyRegistrationNumber' => $cCompanyRegistrationNumber,
        'city' => $cCity,
        'street1' => $cStreet1,
        'street2' => $cStreet2,
        'organizationName' => $cOrganizationName,
        'invoiceStreet1' => $cInvoiceStreet1,
        'invoiceStreet2' => $cInvoiceStreet2,
        'invoiceCity' => $cInvoiceCity,
        'invoiceZipCode' => $cInvoiceZipCode,
        'attributes' => $cAttributes = [
            'name' => $cAttrName,
            'value' => $cAttrValue,
            'key' => $cAttrKey,
            'id' => $cAttributeID,
        ],
    ]
);

Overflow Issues In CSS

If you’re a front-end developer, you may have come across horizontal scrollbar issues, especially on mobile. Because there are many causes of scrollbar problems, there is no straightforward solution. Some issues can be fixed quickly, and some need a little debugging skill.

What Is an Overflow Issue?

Before discussing overflow issues, we should ascertain what one is. An overflow issue occurs when a horizontal scrollbar unintentionally appears on a web page, allowing the user to scroll horizontally. It can be caused by different factors.

It could occur because of unexpectedly wide content or a fixed-width element that is wider than the viewport. We will explore all of the causes in this article.

How to Spot Overflow

An important part of solving this issue is noticing it in the first place. If we know when and where it happens, we can home in on that part of a web page. There are different ways to detect overflow, from manually scrolling to the left or right or by using JavaScript.

Let’s explore the ways to detect overflow.

Scrolling to the Left or Right

The first way to discover an overflow issue is by scrolling the page horizontally. If you’re able to scroll, this is a warning that something is wrong with the page.

Using JavaScript to Find Elements Wider Than the Body

We can add a snippet to the browser console to show any elements wider than the body. This is handy for pages with a lot of elements.

var docWidth = document.documentElement.offsetWidth;

[].forEach.call(
  document.querySelectorAll('*'),
  function(el) {
    if (el.offsetWidth > docWidth) {
      console.log(el);
    }
  }
);

CSS Outline to the Rescue

Applying CSS’ outline to all elements on the page gives us a hint about elements that go beyond the page’s body.

* {
    outline: solid 1px red;
}

Even better, Addy Osmani has a script that adds an outline to each element on the page with a random color.

[].forEach.call($$(""),function(a){a.style.outline="1px solid #"+(~~(Math.random()(1<<24))).toString(16)})

Overflow Label in Firefox

Firefox has a helpful feature that tells you which elements are causing overflow. Hopefully, other browsers will add this!

Deleting Page Elements

Another common way is to open the browser’s DevTools and start deleting elements one by one. Once the issue disappears, then the section you’ve just deleted is probably the cause. I found this method useful in cases where you’ve identified the issue but don’t know why it’s happening.

Once you’ve found where the overflow is happening, then it will be easier to make a reduced test case for further debugging.

Common Overflow Issues

Fixed-Width Elements

One of the most common causes of overflow is fixed-width elements. Generally speaking, don’t fix the width of any element that should work at multiple viewport sizes.

.element {
    /* Don’t do this */
    width: 400px;
}

Using Flexbox Without Wrapping

As useful as Flexbox is, not allowing items to wrap to a new line when no space is available is risky.

.parent {
    display: flex;
}

Here, flex items might cause horizontal overflow in case the space isn’t enough to fit them all in one line:

Make sure to use flex-wrap: wrap when the flex parent is supposed to work at different viewport sizes.

.parent {
    display: flex;
    /* Do this */
    flex-wrap: wrap;
}

CSS Grid

When you’re using CSS grid, designing responsively is important. Take the following grid:

.wrapper {
    display: grid;
    grid-template-columns: 1fr 300px 1fr;
    grid-gap: 1rem;
}

The example above will work great unless the viewport is narrower than 300 pixels. If it is, then overflow will occur.

To avoid such an issue, use grid only when enough space is available. We can use a CSS media query like so:

.wrapper {
    display: grid;
    grid-template-columns: 1fr;
    grid-gap: 1rem;
}

@media (min-width: 400px) {
    .wrapper {
        grid-template-columns: 1fr 300px 1fr;
    }
}

Long Words

Another common reason for overflow is a long word that doesn’t fit in the viewport. This happens more on mobile because of the viewport’s width.

To fix this, we need to use the overflow-wrap property.

.article-content p {
  overflow-wrap: break-word;
}

I’ve written a detailed article on handling both short and long content with CSS.

This fix is particularly useful with user-generated content. A perfect example of this is a comments thread. A user might paste a long URL in their comment, and this should be handled with the overflow-wrap property.

Minimum Content Size in CSS Flexbox

Another interesting cause of overflow is the minimum content size in Flexbox. What does this mean?

According to the specification:

“By default, flex items won’t shrink below their minimum content size (the length of the longest word or fixed-size element). To change this, set the min-width or min-height property.”

This means that a flex item with a long word won’t shrink below its minimum content size.

To fix this, we can either use an overflow value other than visible, or we can set min-width: 0 on the flex item.

.card__name {
    min-width: 0;
    overflow-wrap: break-word;
}

Minimum Content Size in CSS Grid

As with Flexbox, we have the same concept of minimum content size with CSS Grid. However, the solution is a bit different. CSS-Tricks refers to it as “grid blowout”.

Let’s explore the issue. Suppose we have a wrapper with an aside and a main section laid out with CSS grid.

.wrapper {
    display: grid;
    grid-template-columns: 248px 1fr;
    grid-gap: 40px;
}

Also, we have a scrolling section in the main section, for which I’ve used flexbox.

.section {
    display: flex;
    gap: 1rem;
    overflow-x: auto;
}

Notice that I didn’t add flex-wrap, because I want the flex items to be on the same line. This didn’t work, however, and it’s causing horizontal overflow.

To fix this, we need to use minmax() instead of 1fr. This way, the main element’s minimum content size won’t be auto.

.wrapper {
  display: grid;
  grid-template-columns: 248px minmax(0, 1fr);
  grid-gap: 40px;
}

Negative Margins

An element positioned off screen can cause overflow. Usually, that is because the element has a negative margin.

In the following example, we have an element with a negative margin, and the document’s language is English (i.e. left to right).

.element {
    position: absolute;
    right: -100px;
}

Interestingly, when the element is positioned on the opposite side, there is no overflow. Why is that?

I faced this issue lately and wrote about it. It turns out that this behavior is intentional. According to the CSS specification:

“UAs must clip the scrollable overflow area of scroll containers on the block-start and inline-start sides of the box (thereby behaving as if they had no scrollable overflow on that side).”

For an English document, the inline-start side is the left side, so any element positioned off-screen on the left will be clipped, and thus there will be no overflow.

If positioning an element off screen is really necessary, make sure to apply overflow: hidden to the parent to avoid any overflow.

Images Without max-width

If you don’t take care of large images ahead of time, you will see overflow. Make sure to set max-width: 100% on all images.

img {
    max-width: 100%;
}

Viewport Units

Using 100vw does have a downside, which is that it can cause overflow when the scrollbar is visible. On macOS, 100vw is fine and won’t cause horizontal scroll.

On Windows, scrollbars are always visible by default, so overflow will occur.

The reason for this is that with the value 100vw, there is no awareness of the width of the browser’s vertical scrollbar. As a result, the width will be equal to 100vw plus the scrollbar’s width. Unfortunately, there is no CSS fix to that.

However, we can use JavaScript to measure the viewport’s width excluding the scrollbar.

function handleFullWidthSizing() {
  const scrollbarWidth = window.innerWidth - document.body.clientWidth

  document.querySelector('myElement').style.width = calc(100vw - ${scrollbarWidth}px)
}

Injected Ads

Ads injected on page load can cause overflow if they’re wider than their parent. Add overflow-x: hidden to the parent element to prevent this.

Double-check every ad on the website to ensure that it’s not causing overflow.

Is Applying overflow-x: hidden to body a Good Idea?

Opting for overflow-x: hidden is like putting on a bandage without addressing the problem. If you have overflow, then it’s better to solve the root issue.

Moreover, applying overflow-x: hidden to the body element is not a good idea because position: sticky won’t work if a parent has overflow-x: hidden.

How to Avoid Overflow in CSS

Below are things to check to reduce overflow issues in CSS. I hope you find it useful!

Test With Real Content

Nothing beats testing with real content on a website. In doing so, you ensure that the layout can handle different varieties of content.

Account for User-Generated Content

For a component like a comments thread, account for cases in which the user will paste a long URL or type a long word, as explained above.

Use CSS Grid and Flexbox Carefully

As useful as CSS grid and flexbox are, they can easily cause overflow if used incorrectly. As we discussed, not using flex-wrap: wrap can cause overflow, as can grid-template-columns: 1fr 350px when the screen is narrower than 350 pixels.

Further Reading on SmashingMag:

Page view based on database field value

Hello, I created a function to show data related to a database called skede with fields "name" "surname" "age" in a page called vive.php
This is the code

function mostraskede(){
$ricercaskede = query('SELECT * FROM skede ORDER BY id LIMIT 1 OFFSET 0');
conferma($ricercaskede);
while ($row = fetch_array($ricercaskede)){
    //echo $row['titolo'];
    $skede = <<<STRINGA_SKD
    <!-- Hero -->
   Parte in html ecc.ecc.

I find myself in the need to show in the vive.php page (the code below) only the skede that the value "luca" appears in the "name" field. On another page, "marina" value and so on.

<?php require_once("config.php"); ?>
<?php require_once("header.php"); ?>
<?php mostraskede();  ?>
<?php require_once("footer.php"); ?>  

I don't know how to enter the code.
Can someone help me? Thank you

How to Optimize Core Web Vitals for WordPress (Ultimate Guide)

Do you want to optimize core web vitals for WordPress?

Core Web Vitals is an initiative by Google which helps website owners improve user experience and quality of their websites. These signals are crucial for the success of any website.

In this guide, we’ll show you how to easily optimize Google Core Web Vitals for WordPress without any special technical skills.

Optimizing Google Core Web Vitals for your WordPress website

Here is a quick overview of what we’ll cover in this guide.

What are Google Core Web Vitals?

Google Core Web Vitals are a set of website performance metrics that Google considers important in a website’s overall user experience. These web vital scores will be a part of Google’s overall page experience score that will impact your SEO rankings.

The truth is that nobody likes a slow-loading website including Google.

Even if your website loads fast, it may not be fully functional for users to do what they want to do or access the information they need.

This is what Web Vitals helps you measure. How quickly does your website loads, becomes visible, and is ready for your users?

Core web vitals

To do that, Google uses three quality tests (Web Vitals).

  • Largest Contentful Paint (LCP)
  • First Input Delay (FID)
  • Cumulative Layout Shift (CLS)

Now the names of these tests may sound too technical but what they do is quite easy to understand.

Let’s see how each Web Vitals test works, what they measure, and how you can improve your score..

Largest Contentful Paint – LCP

Largest Contentful Paint or the LCP, looks for how quickly the main content (whether it is an image, article, or description) becomes visible to the users.

For example, your website might load fast, but the largest content may not appear on the screen as quickly as the rest of the page.

Other speed test tools will give you a high score, but from user’s point of view, the page is still slow.

This is why Google measures the LCP as part of their web vital score, so website owners can have a more clear understanding.

First Input Delay (FID)

First Input Delay (FID) measures the time it takes a user’s browser to actually be able to begin processing event handlers in response to a user’s interaction.

In plain english, let’s suppose a user is on your contact form page. They fill out the form and click on the Submit button. FID, will measure how quickly your website processes that interaction.

An even simpler example would be the time from when a user clicks on a link to the time their browser starts processing the next sequence of events.

Cumulative Layout Shift (CLS)

Cumulative Layout Shift (CLS) measures the time it takes for a website to become visually stable.

As a website loads, some elements take more time to load than others. During this time, your website’s content may keep moving on the screen.

For instance, if a user is reading a paragraph on a mobile device and a video embed loads above it, this makes the entire content move down. This can be really frustrating if a user was trying to accomplish an action such as adding a product to cart where the button shift down due to other items moving on the page.

How to Test Your Google Core Web Vitals Score

The easiest way to test your Google Core Web Vitals Score is by using the Page Speed Insights tool. Simply enter the URL you want to test and click on the Analyze button.

Using Page Speed Insights tool to view the core Web Vitals score

The core vital results are displayed under the section titled ‘Field Data’ section.

Core Web Vitals report example

To make it simpler, you will see a message at the top saying ‘[…] field data shows that this page passes the Core Web Vitals assessment’.

In the chart below, you can view the actual score of all three core vitals. Here is how much you need to score to pass the core Web Vitals tests for each item.

  • Largest Contentful Paint (LCP) – 2.5 seconds
  • First Input Delay (FID) – Less than 100 milliseconds
  • Cumulative Layout Shift (CLS) – Less than 0.1

How to View Google Core Web Vitals for Full Website?

Now Page Speed Insights tool allows you to check an individual page. If the page you are checking is the root of your domain name, then you can also click on the ‘Show Origin Summary’ checkbox.

Origin Summary Score

This will show you the score for all pages served from this origin.

However, to really drill down deep, you can access the Core Web Vitals report in your Google Search Console dashboard as well.

Core web vitals in Google Search Console

This allows you to see how many URLs on your website passed the tests, which URLs need improvement, and which pages have a poor score.

To get even more detailed reports for Web Vitals, you can use the lighthouse speed test by going to Web.dev Measure tool, or by using the built-in test inside Google Chrome browser.

Simply open a website in Chrome, right click anywhere on the screen, and then select the Inspect option. In the tabs, you will see an option called Lighthouse.

Test Web Vitals in Google Chrome

After that, click the Generate Report button.

Note: You must do the Chrome test in Incognito mode for the most accurate results. Otherwise your browser extensions may negatively impact the core web vital score it shows you.

Why are Core Web Vitals Important?

Core Web Vitals are important because they reflect how your website performs for the users. It is focused not just on the faster loading of a website but on how quickly users can actually use it.

According to a recent study, a 1 second delay in page load time can lead to 7% loss in conversions, 11% fewer page views, and 16% decrease in customer satisfaction.

StrangeLoop study

That’s why it is crucial to optimize your website for speed and performance. However, most performance measuring tools didn’t really account for the quality of user experience.

A faster website with poor user experience is still costing you conversions, fewer page views, and poor customer satisfaction. Improving core Web Vitals helps you remedy that.

User experience is also an important factor in SEO rankings. Google has already announced that starting in May 2021 the search algorithm update will include page experience as one of the ranking factors.

That being said, let’s see how you can easily improve your core Web vitals to offer a better user experience on your website.

How to Improve Your Core Web Vitals in WordPress (7 Tips)

Improving your core Web Vitals score in WordPress is not that difficult. Using some essential performance optimization tips you can easily pass the Web Vitals score.

1. Optimize Your WordPress Hosting

Your WordPress hosting company plays the most significant role in your website’s performance.

They are able to optimize their servers for WordPress which gives your website rock-solid platform to build upon.

We recommend using SiteGround for a high-performance website. They are one of the officially recommended WordPress hosting companies, and we use SiteGround for WPBeginner website.

SiteGround

To give your website the performance boost it needs, SiteGround uses Google Cloud Platform for their servers along with ultrafast PHP.

Their SG Optimizer plugin is used by over a million websites. It automatically makes further performance enhancements and turns on built-in caching which does everything WP Rocket does and more.

It’s important to note, that their SG Optimizer plugin only works on SiteGround hosting accounts, and these performance optimizations are available for all plans including the lowest option.

If you’re using another WordPress hosting provider, then we recommend using WP Rocket along with few other tools to achieve better core web vitals score.

WP Rocket is the best WordPress caching plugin on the market. It allows you to easily set up caching on your WordPress website without going into any technical details of server management.

2. Improving Largest Content Paintful (LCP) Score

As mentioned earlier, the Largest Content Paintful (LCP) is literally the largest content part within the viewport of a page. For instance, on a blog post, this could be the featured image or the article text.

The quicker this content loads the higher your LCP score would be.

How do you know which content is considered the largest by the test? Well, you need to scroll down to the test results and expand the ‘Largest Contentful Paint element’ tab.

Largest Content Paintful element

You’ll see the elements considered for the LCP score. If it is a larger image, then you can try replacing it with a smaller image or an image with lower file size and quality. See our guide on how to optimize images for web performance.

If it is text, then you can try breaking it into paragraphs and headings.

3. Improving First Input Delay (FID) Score

First Input Delay score measures the time between a user clicking on something on your website and their browsers starting processing elements.

The most important tip to improve that is by using a better web hosting or even managed WordPress hosting platform.

Another easy way to improve FID score is by using a caching plugin like WP Rocket. It comes with a built-in feature that allows you to optimize file delivery.

First you would need to install and activate the WP Rocket plugin. For more details, see our step by step guide on how to install a WordPress plugin.

After that, go to Settings » WP Rocket page and switch to the File Optimization tab.

File Optimization in WP Rocket

Scroll down to the bottom of the page and check the box next to the ‘Load JavaScript deferred’ option.

Defer JavaScript

Don’t forget to click on the Save Changes button to store your changes.

Deferring JavaScript allows your website to load without waiting for JavaScript to be loaded. This improves First Input Delay (FID) Score for pages where JavaScript may be the cause.

4. Improving Cumulative Layout Shift (CLS) Score

Cumulative Layout Shift (CLS) score is affected when different elements on a web page are loading slowly and making other elements on the screen move.

You can view which elements are affecting the CLS score by expanding the ‘Avoid large layout shifts’ tab in the Page Speed Insights results.

Layout shift elements

This will show you the elements that are causing the most layout shift impact during page load.

To make sure that the visual layout of your page does not shift as other items load, you need to tell browsers about the dimensions (width and height) of the elements like images, video embeds, Ads such as Google AdSense, and more.

WordPress automatically adds height and width attributes to images you add. However, you can still check all other media particularly embeds to make sure that all of them have height and width attributes.

One way to do that is by using the Inspect Tool. Simply right-click in your browser and select Inspect to open the developer console.

You can then point and click on different page elements to highlight their source code. There, you can see if the element has width and height attributes defined.

Inspect height and width attributes

5. Eliminate Render Blocking Elements

Render blocking elements are the elements that are slower to load but are blocking other elements from loading first. This affects your overall Web Vitals score and user experience on your website.

Page Speed Insights results will show you the render blocking elements. These are usually JavaScript or CSS files added by your WordPress plugins, third-party tools like Google Analytics, Facebook Pixel, Google Ads, and more.

Render blocking elements

However, most such elements are programmatically added to your site by different plugins or theme. This makes it harder for a beginner user to remove or properly load them.

We have a step by step guide on how to easily eliminate render blocking elements in WordPress without messing with any code on your website.

6. Properly Size Images in WordPress

Another common cause of lower core Web Vitals score is very large images. Many WordPress users upload high-resolution images to their websites which take longer to load and in most cases are not necessary.

Optimized vs Unoptimized Images in WordPress

This becomes even more problematic for users on mobile devices. Your responsive WordPress theme and WordPress will automatically fit the image to user’s mobile screen but they would still be loading a larger file.

We have a detailed guide on how to properly optimize images for your WordPress website without losing quality or affecting the performance.

7. Use a CDN to Server to Improve Web Vitals Score

CDN or content delivery network are third-party services that allow you to serve static content of your website from multiple servers around the globe.

This allows users to download those static files like images and CSS from servers that are nearest to them. It also reduces load on your website which can then continue loading other elements.

You can use a cloud firewall app like Sucuri which comes with a built-in CDN service. Sucuri also helps you block malicious and spam requests which further frees up your website resources.

You can also use Cloudflare free CDN as an alternative. It comes with a basic firewall protection and CDN service that would improve your website’s web vitals score.

We hope this guide helped you learn how to optimize core web vitals for WordPress. Another important aspect of good user experience is security. We recommend that you follow our WordPress security checklist to make sure that your website performance is not affected by spam or DDoS attacks.

You may also want to see our comparison of best video editing software and best webinar platforms to create performance optimized media content that doesn’t slow down your website speed.

If you liked this article, then please subscribe to our YouTube Channel for WordPress video tutorials. You can also find us on Twitter and Facebook.

The post How to Optimize Core Web Vitals for WordPress (Ultimate Guide) appeared first on WPBeginner.