Browsing Tag: CSS

    web design

    Create Responsive Image Effects With CSS Gradients And aspect-ratio — Smashing Magazine

    02/23/2021

    About The Author

    Stephanie Eckles is a front-end focused SWE at Microsoft. She’s also the author of ModernCSS.dev which provides modern solutions to old CSS problems as in-depth …
    More about
    Stephanie

    A classic problem in CSS is maintaining the aspect ratio of images across related components, such as cards. The newly supported aspect-ratio property in combination with object-fit provides a remedy to this headache of the past! Let’s learn to use these properties, in addition to creating a responsive gradient image effect for extra flair.

    To prepare for our future image effects, we’re going to set up a card component that has a large image at the top followed by a headline and description. The common problem with this setup is that we may not always have perfect control over what the image is, and more importantly to our layout, what its dimensions are. And while this can be resolved by cropping ahead of time, we can still encounter issues due to responsively sized containers. A consequence is uneven positions of the card content which really stands out when you present a row of cards.

    Another previous solution besides cropping may have been to swap from an inline img to a blank div that only existed to present the image via background-image. I’ve implemented this solution many times myself in the past. One advantage this has is using an older trick for aspect ratio which uses a zero-height element and sets a padding-bottom value. Setting a padding value as a percent results in a final computed value that is relative to the element’s width. You may have also used this idea to maintain a 16:9 ratio for video embeds, in which case the padding value is found with the formula: 9/16 = 0.5625 * 100% = 56.26%. But we’re going to explore two modern CSS properties that don’t involve extra math, give us more flexibility, and also allow keeping the semantics provided by using a real img instead of an empty div.

    First, let’s define the HTML semantics, including use of an unordered list as the cards’ container:

    <ul class="card-wrapper">
      <li class="card">
        <img src="http://www.smashingmagazine.com/" alt="http://www.smashingmagazine.com/">
        <h3>A Super Wonderful Headline</h3>
        <p>Lorem ipsum sit dolor amit</p>
      </li>
      <!-- additional cards -->
    </ul>
    

    Next, we’ll create a minimal set of baseline styles for the .card component. We’ll set some basic visual styles for the card itself, a quick update to the expected h3 headline, then essential styles to begin to style the card image.

    .card {
      background-color: #fff;
      border-radius: 0.5rem;
      box-shadow: 0.05rem 0.1rem 0.3rem -0.03rem rgba(0, 0, 0, 0.45);
      padding-bottom: 1rem;
    }
    
    .card > :last-child {
      margin-bottom: 0;
    }
    
    .card h3 {
      margin-top: 1rem;
      font-size: 1.25rem;
    }
    
    img {
      border-radius: 0.5rem 0.5rem 0 0;
      width: 100%;
    }
    
    img ~ * {
      margin-left: 1rem;
      margin-right: 1rem;
    }
    

    The last rule uses the general sibling combinator to add a horizontal margin to any element that follows the img since we want the image itself to be flush with the sides of the card.

    And our progress so far leads us to the following card appearance:

    One card with the baseline styles previously described applied and including an image from Unsplash of a dessert on a small plate next to a hot beverage in a mug
    One card with the baseline styles previously described applied and including an image from Unsplash of a dessert on a small plate next to a hot beverage in a mug. (Large preview)

    Finally, we’ll create the .card-wrapper styles for a quick responsive layout using CSS grid. This will also remove the default list styles.

    .card-wrapper {
      list-style: none;
      padding: 0;
      margin: 0;
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(30ch, 1fr));
      grid-gap: 1.5rem;
    }
    

    Note: If this grid technique is unfamiliar to you, review the explanation in my tutorial about modern solutions for the 12-column grid.

    With this applied and with all cards containing an image with a valid source path, our .card-wrapper styles give us the following layout:

    Three cards are shown in a row due to the card wrapper layout styles applied. Each card has a unique image that has different natural aspect ratios, with the last card having a vertically oriented image that is more than twice the height of the other card images
    Three cards are shown in a row due to the card wrapper layout styles applied. Each card has a unique image that has different natural aspect ratios, with the last card having a vertically oriented image that is more than twice the height of the other card images. (Large preview)

    As demonstrated in the preview image, these baseline styles aren’t enough to properly contain the images given their varying natural dimensions. We’re in need of a method to constrain these images uniformly and consistently.

    Enable Uniform Image Sizes with object-fit

    As noted earlier, you may previously have made an update in this scenario to change the images to be added via background-image instead and used background-size: cover to handle nicely resizing the image. Or you may have tried to enforce cropping ahead of time (still a worthy goal since any image size reduction will improve performance!).

    Now, we have the property object-fit available which enables an img tag to act as the container for the image. And, it comes with a cover value as well that results in a similar effect as the background image solution, but with the bonus of retaining the semantics of an inline image. Let’s apply it and see how it works.

    We do need to pair it with a height dimension for extra guidance on how we want the image container to behave (recall we had already added width: 100%). And we’re going to use the max() function to select either 10rem or 30vh depending on which is larger in a given context, which prevents the image height from shrinking too much on smaller viewports or when the user has set a large zoom.

    img {
      /* ...existing styles */
      object-fit: cover;
      height: max(10rem, 30vh);
    }
    

    Bonus Accessibility Tip: You should always test your layouts with 200% and 400% zoom on desktop. While there isn’t currently a zoom media query, functions like max() can help resolve layout issues. Another context this technique is useful is spacing between elements.

    With this update, we’ve definitely improved things, and the visual result is as if we’d use the older background image technique:

    The three-card images now appear to have a uniform height and the image contents are centered within the image as if it was a container
    The three-card images now appear to have a uniform height and the image contents are centered within the image as if it was a container. (Large preview)

    Responsively Consistent Image Sizing With aspect-ratio

    When using object-fit by itself, one downside is that we still need to set some dimension hints.

    An upcoming property (currently available in Chromium browsers) called aspect-ratio will enhance our ability to consistently size images.

    Using this property, we can define a ratio to resize the image instead of setting explicit dimensions. We’ll continue to use it in combination with object-fit to ensure these dimensions only affect the image as a container, otherwise, the image could appear distorted.

    Here is our full updated image rule:

    img {
      border-radius: 0.5rem 0.5rem 0 0;
      width: 100%;
      object-fit: cover;
      aspect-ratio: 4/3;
    }
    

    We’re going to start with an image ratio of 43 for our card context, but you could choose any ratio. For example, 11 for a square, or 169 for standard video embeds.

    Here are the updated cards, although it will probably be difficult to notice the visual difference in this particular instance since the aspect ratio happens to closely match the appearance we achieved by setting the height for object-fit alone.

    The three-card images have identical width and height dimensions, which are slightly different than the previous object-fit solution
    The three-card images have identical width and height dimensions, which are slightly different than the previous object-fit solution. (Large preview)

    Setting an aspect-ratio results in the ratio being maintained as the elements grow or shrink, whereas when only setting object-fit and height the image ratio will constantly be in flux as the container dimensions change.

    Adding Responsive Effects With CSS Gradients And Functions

    OK, now that we know how to setup consistently sized images, let’s have some fun with them by adding a gradient effect!

    Our goal with this effect is to make it appear as though the image is fading into the card content. You may be tempted to wrap the image in its own container to add the gradient, but thanks to the work we’ve already done on the image sizing, we can work out how to safely do it on the main .card.

    The first step is to define a gradient. We’re going to use a CSS custom property to add in the gradient colors to enable easily swapping the gradient effect, starting with a blue to pink. The last color in the gradient will always be white to maintain the transition into the card content background and create the “feathered” edge.

    .card {
      --card-gradient: #5E9AD9, #E271AD;
    
      background-image: linear-gradient(
        var(--card-gradient),
        white max(9.5rem, 27vh)
      );
      /* ...existing styles */
    }
    

    But wait — is that a CSS max() function? In a gradient? Yes, it’s possible, and it’s the magic that makes this gradient effective responsively!

    However, if I were to add a screenshot, we wouldn’t actually see the gradient having any effect on the image yet. For that, we need to bring in the mix-blend-mode property, and in this scenario we’ll use the overlay value:

    img {
      /* ...existing styles */
      mix-blend-mode: overlay;
    }
    

    The mix-blend-mode property is similar to applying the layer blending styles available in photo manipulation software like Photoshop. And the overlay value will have the effect of allowing the medium tones in the image to blend with the gradient behind it, leading to the following result:

    Each card image has a gradient blending effect that starts with a light blue at the top, that blends to a reddish pink, and then ends by feathering into a white prior to the rest of the card text content
    Each card image has a gradient blending effect that starts with a light blue at the top, that blends to a reddish pink, and then ends by feathering into a white prior to the rest of the card text content. (Large preview)

    Now, at this point, we are relying on the aspect-ratio value alone to resize the image. And if we resize the container and cause the card layout to reflow, the changing image height causes inconsistencies in where the gradient fades to white.

    So we’ll add a max-height property as well that also uses the max() function and contains values slightly greater than the ones in the gradient. The resulting behavior is that the gradient will (almost always) correctly line up with the bottom of the image.

    img {
      /* ...existing styles */
      max-height: max(10rem, 30vh);
    }
    

    It’s important to note that adding a max-height alters the aspect-ratio behavior. Instead of always using the exact ratio, it will be used only when there’s enough allotted space given the new extra constraint of the max-height.

    However, aspect-ratio will still continue to ensure the images resize consistently as was the benefit over only object-fit. Try commenting out aspect-ratio in the final CodePen demo to see the difference it’s making across container sizes.

    Since our original goal was to enable consistently responsive image dimensions, we’ve still hit the mark. For your own use case, you may need to fiddle with the ratio and height values to achieve your desired effect.

    Alternate: mix-blend-mode And Adding A Filter

    Using overlay as the mix-blend-mode value was the best choice for the fade-to-white effect we were looking for, but let’s try an alternate option for a more dramatic effect.

    We’re going to update our solution to add a CSS custom property for the mix-blend-mode value and also update the color values for the gradient:

    .card {
      --card-gradient: tomato, orange;
      --card-blend-mode: multiply;
    }
    
    img {
      /* ...existing styles */
      mix-blend-mode: var(--card-blend-mode);
    }
    

    The multiply value has a darkening effect on mid-tones, but keeps white and black as is, resulting in the following appearance:

    Each card image has a strong orange tint from the new gradient that starts goes from a red-orange to pure orange. White areas are still white and black areas are still black
    Each card image has a strong orange tint from the new gradient that starts goes from a red-orange to pure orange. White areas are still white and black areas are still black. (Large preview)

    While we’ve lost the fade and now have a hard edge on the bottom of the image, the white part of our gradient is still important to ensure that the gradient ends prior to the card content.

    One additional modification we can add is the use of filter and, in particular, use the grayscale() function to remove the image colors and therefore have the gradient be the only source of image coloring.

    img {
      /* ...existing styles */
      filter: grayscale(100);
    }
    

    Using the value of grayscale(100) results in complete removal of the image’s natural colors and transforming it into black and white. Here’s the update for comparison with the previous screenshot of its effect when using our orange gradient with multiply:

    Now each card image still has the orange gradient but all other color is removed and replaced by shades of gray
    Now each card image still has the orange gradient but all other color is removed and replaced by shades of gray. (Large preview)

    Use aspect-ratio As A Progressive Enhancement

    As previously mentioned, currently aspect-ratio is only supported in the latest version of Chromium browsers (Chrome and Edge). However, all browsers support object-fit and that along with our height constraints results in a less-ideal but still acceptable result, seen here for Safari:

    The card image height is capped, but each card has a slightly different realized height
    The card image height is capped, but each card has a slightly different realized height. (Large preview)

    Without aspect-ratio functioning, the result here is that ultimately the image height is capped but the natural dimensions of each image still lead to some variance between card image heights. You may want to instead change to adding a max-height or make use of the max() function again to help make a max-height more responsive across varying card sizes.

    Extending The Gradient Effects

    Since we defined the gradient color stops as a CSS custom property, we have ready access to change them under different contexts. For example, we might change the gradient to more strongly feature one of the colors if the card is hovered or has one of its children in focus.

    First, we’ll update each card h3 to contain a link, such as:

    <h3><a href="http://www.smashingmagazine.com/">A Super Wonderful Headline</a></h3>
    

    Then, we can use one of our newest available selectors — :focus-within — to alter the card gradient when the link is in focus. For extra coverage of possible interactions, we’ll couple this with :hover. And, we’ll reuse our max() idea to assign a single color to take over coverage of the image portion of the card. The downside to this particular effect is that gradient stops and color changes aren’t reliably animateable — but they will be soon thanks to CSS Houdini.

    To update the color and add the new color stop, we just need to re-assign the value of --card-gradient within this new rule:

    .card:focus-within,
    .card:hover {
      --card-gradient: #24a9d5 max(8.5rem, 20vh);
    }
    

    Our max() values are less than the original in use for white to maintain the feathered edge. If we used the same values, it would meet the white and create a clearly straightedge separation.

    In creating this demo, I originally tried an effect that used transform with scale for a zoom-in effect. But I discovered that due to mix-blend-mode being applied, the browser would not consistently repaint the image which caused an unpleasant flickering. There will always be trade-offs in requesting the browser perform CSS-only effects and animations, and while it’s very cool what we can do, it’s always best to check the performance impact of your effects.

    Have Fun Experimenting!

    Modern CSS has given us some awesome tools for updating our web design toolkits, with aspect-ratio being the latest addition. So go forth, and experiment with object-fit, aspect-ratio, and adding functions like max() into your gradients for some fun responsive effects! Just be sure to double-check things cross-browser (for now!) and across varying viewports and container sizes.

    Here is the CodePen including the features and effects we reviewed today:

    See the Pen [Responsive Image Effects with CSS Gradients and aspect-ratio](https://codepen.io/smashingmag/pen/WNoERXo) by Stephanie Eckles.

    See the Pen Responsive Image Effects with CSS Gradients and aspect-ratio by Stephanie Eckles.

    Looking for more? Make sure you check out our CSS Guide here on Smashing →

    Smashing Editorial
    (vf, il)

    Source link

    web design

    Managing CSS Z-Index In Large Projects — Smashing Magazine

    02/08/2021

    About The Author

    Steven Frieson is a front end developer who loves to solve problems for people, whether they’re website users or fellow developers. He is interested in many …
    More about
    Steven

    Wrangling z-index values is a difficult task for many developers. Here is an easy-to-implement mini-framework based on existing conventions that brings clarity and confidence to working with z-index.

    There are several articles that explain z-index (here’s a good one), since it continues to trip up developers of all experience levels. I do not think that the number of articles is a sign that none of them do a good job at explaining it, but that there are a lot of developers out there and just because one developer read and understood the article doesn’t necessarily mean that everyone on their team read and understands it now. While taking the time to better understand how z-index (or any piece of technology) works will definitely set you up to work with it better, we can also take another approach: make it easier to work with z-index.

    We use abstractions and conventions to hide away the tricky and error-prone parts, which in turn makes it easier for everyone who needs to do the same task. I had the opportunity to attempt to make z-index easier to work with for my team while working on a redesign of our company’s website. The system I designed allowed my team to implement the entire UI while never having to question what a certain z-index value was used for, what number to use when adding a new z-index declaration, or how to fix stacking bugs that crept into the system.

    Common Solution

    The most common system I’ve seen for managing z-index values — other than no system — is setting several general-use values, each separated by an arbitrary number. This solution definitely tames z-index issues, but as I’ve worked on teams that use this system there still seems to be confusion about how to use it properly. Here is an example from the Bootstrap documentation.

    $zindex-dropdown:       1000;
    $zindex-sticky:         1020;
    $zindex-fixed:          1030;
    $zindex-modal-backdrop: 1040;
    $zindex-modal:          1050;
    $zindex-popover:        1060;
    $zindex-tooltip:        1070;
    

    Bootstrap defines z-index values in Sass variables like $zindex-dropdown, $zindex-sticky, and $zindex-fixed. Those names seem pretty straight forward, but when a developer goes to choose a value for a feature they’re working on, there could be confusion as to which value is most appropriate for their use. They end up asking, “Is what I’m implementing a ‘dropdown’ or a ‘popover’?” which can easily be debated and may not have a clear answer.

    A second issue I see with this solution is that the actual values for the variables might seem confusing or lead to insecurity. This solution leaves space in between each value to give developers space to add their own values in between if necessary. Bootstrap defines seven values separated by increments of 10, starting at 1000 and ending at 1070.

    Many questions could come to mind when reading this:

    “Why start at 1000?

    “Is there anything less than 1000?”

    “Where is 1010? Is it a bug? Is something else using it?”

    “Why was 10 chosen? What if I need more than 9 values to go in between?”

    Though I’ve never actually needed these “what if” questions answered, they can add insecurity and confusion to a system that already seems magical and misunderstood. Can we remove all of these concerns, allowing the developer to easily and accurately choose the z-index value they need?

    A New Solution

    Since working on a redesign gave my team a fresh start, this was one common issue we wanted to see if we could avoid. To align with our general coding standards, my goals for managing z-index was to avoid magic numbers and to make it easier for every team member to confidently contribute. The second goal of making it easier for others is vague, so I focused on trying to solve these common issues:

    • People often choose arbitrarily large z-index values;
    • z-index bug fixes often result in a new z-index bug;
    • The relationship between z-index values is difficult to trace.

    Let’s look at solutions for each of these issues that I was able to apply, leaning on conventions and using existing technologies.

    Giving Z-Index Values Semantics

    One reason people often choose arbitrarily large z-index values is because they don’t know the z-index value of the item above which they are trying to place a new item. Once they find an arbitrarily high value that works, they leave it instead of finding an optimal value. Later on, when someone finds this value they have no idea why it is what it is, and even the original author may have forgotten.

    z-index: 9999;

    The solution for fixing “magic numbers” like this is by using a named constant instead. While naming the value alone does not give us much more value than the class name does, when we put our z-index constants together, their relationship starts to become explicit.

    To remove the magic numbers, I first started defining all of our z-index values in a JavaScript file. I used a JavaScript file since our application was using a CSS-in-JS solution, though this and the ideas in this article can be implemented with styling preprocessors like Sass variables as well as in CSS using custom properties.

    export const backdrop = 0;
    export const openButton = 1;
    export const dropdown = 2;

    With z-index constants, the CSS value has little more meaning, and the actual value is obscured away.

    css`
      z-index: ${backdrop};
    `;

    This also makes the original value easy to find, revealing the related constants, but there is a further improvement that can be made. We know by how z-index works that these values are related to each other, so we can change our constants to make that more apparent.

    export const backdrop = 0;
    export const openButton = backdrop + 1;
    export const dropdown = openButton + 1;

    Using simple arithmetic, we can use the previous constants to make the next constant. Taking this idea one step further to further eliminate ambiguity, I added some utility constants to make these definitions read more like a sentence.

    const base = 0;
    const above = 1;
    
    export const backdrop = base;
    export const openButton = above + backdrop;
    export const dropdown = above + openButton;

    Now when someone sees a line like z-index: ${dropdown}; they can look find the dropdown’s definition and read, “The dropdown is above the open button.”

    This makes future maintenance of the constants easier. Whenever you have a new value to add, you can be confident that you are adding it to the right place.

    export const backdrop = base;
    export const openButton = above + backdrop;
    export const dropdown = above + openButton;
    export const closeButton = above + dropdown; // new

    Deleting values is easy too, but you need to remember to update any other values that are dependent on it. Using JavaScript, the linter highlighted this for me.

    Stacking bug tickets often show up that say something like, “the dropdown is overlapping with the button when it should be underneath.” When coming across these, the fix is as simple as swapping the relationship pointers in the definitions.

    export const backdrop = base;
    export const dropdown = above + backdrop;
    export const openButton = above + dropdown;
    export const closeButton = above + dropdown; // ???

    Now that we’ve swapped the z-index order, we notice another potential bug before we even check the browser. The close button might now conflict with the open button. You can now have the necessary conversations to resolve bugs before anyone sees a problem in production.

    One extra piece I found to be helpful in rare situations was a utility for placing items below others. To avoid mixing above and below, I made the rule that below should only be used for negative values.

    const base = 0;
    const above = 1;
    const below = -1;
    
    export const backdrop = below + dropdown;
    export const dropdown = below + button;
    export const button = base;

    In this system, every z-index value is only as high as it needs to be, and since it’s dynamically chosen, you aren’t concerned with what the value actually is.

    You can also delete and add values knowing with confidence how it will affect the other stacked elements.

    Once our application ended up with a dozen or so z-index constants, though it started to become a little bit confusing having a long flat list.

    Organizing By Stacking Context

    When thinking about stacking context and how the values of each stacking context are independent of others, it sounded like other front-end solutions for effective scoping. I drew similarities to other JavaScript modules, components, atomic design, and BEM. They are all trying to solve similar problems of how we can independently scope concerns, keeping them from affecting other areas.

    Taking inspiration from BEM, I made a naming convention for our constants to better organize the values and bring more order to the flat list of constants. The format I ended up using had a template like this: z<Context><Element>.

    The z portion is a prefix denoting the fact that the value is meant to be used in z-index declarations, considering we had other constants defined in our JavaScript files like color variables.

    The<Context> portion is replaced with the name stacking context the constant belongs to. This is similar to the “block” in BEM and in practice almost always shares the same name as the component being styled. The main exception is the root HTML stacking context that is used for page region stacking.

    The final portion of the format, <Element> is for the name of the specific item to be positioned in the stacking context and is most similar to “element” in BEM.

    Here is a full example of what this naming convention could look like when added to what we’ve talked about previously. For an interactive version, you can play around with the same example in a CodePen:

    See the Pen [Managing CSS Z-Index in Large Projects Demo](https://codepen.io/smashingmag/pen/xxEvepz) by Steven Frieson.

    See the Pen Managing CSS Z-Index in Large Projects Demo by Steven Frieson.

    For other language versions, check out this repository.

    // Utils
    const base = 0;
    const above = 1; // use this for all values above the base
    const below = -1; // and this for all values below the base
    
    // Page Layout
    export const zLayoutNavigation = above + base;
    export const zLayoutFooter = above + base;
    export const zLayoutModal = above + zLayoutNavigation;
    export const zLayoutPopUpAd = above + zLayoutModal;
    
    // NavMenu
    export const zNavMenuBackdrop = below + base;
    export const zNavMenuPopover = above + base;
    export const zNavMenuToggle = above + zNavMenuPopover;

    Now that our constants are organized by their stacking context, we can quickly see which values are related and where a new value or fix should go.

    Taking It Further

    That is essentially the specification of how this should work. Considering this solution was only designed with one application in mind, there are some further steps that could be taken to make it more mature and support more use cases. Some of these ideas are more specific to the language it’s being implemented in but most ideas carry over.

    One area that could possibly be improved around what is being shipped to the browser. When I implemented this, the framework we were using did not give us much control over the build tools, so the JavaScript file of all the definitions was bundled with the application. This did not have a measurable performance impact on our application, but you may have noticed that all of the values could be computed at compile time. An implementation using Sass would end up not shipping any of the Sass variables to the client, but instead, insert the derived z-index value in the CSS. For JS and CSS solutions, build tools like Webpack and PostCSS, respectively, could help do the same.

    The way the solution evolved as I worked on it, the z-index constants ended up all in one file. This ended up being a great way to see all of the z-index values across the application at once, making it easier to quickly glance for any possible stacking conflicts. They were also filed away with other styling constants like colors and typography, so it made sense to originally have them all defined together. As the file grew though, it started to be internally organized by stacking context as explained above. Since the stacking contexts often mapped to a component, it started feeling like each set of constants could be collocated with their component. This would bring all the normal benefits of collocation, being closer to the files they’re used in, causing less friction when needing to add, edit, and remove constants. We never refactored it, but that seems like a viable option to explore.

    One additional piece has to do with ergonomics and developer experience. The constants are all exported as a flat list at the moment. The naming convention took inspiration from BEM, but Sass and JavaScript allow us to use other ways to organize our data. Sass maps or JavaScript Objects or Maps could have been used to organize the values hierarchically. If we had all the values in a z object, it could have led to shorter import statements. We could have gone further to have an object per stacking context as well leading to a usage style more like z.layout.navigation. Different tools like TypeScript could guard against making typos here, though this might be more effort than it’s worth.

    Our Results

    The system as spelled out was successfully implemented and deployed to our production applications. Checking back in on the objectives, we definitely got rid of the magic numbers. As far as developer ease and confidence goes, my team was able to easily add new z-index values and fix pre- and post-launch bugs without fear that the changes would introduce new bugs. On multiple occasions, we fixed bugs before they were filed.

    We also were able to avoid the issue of coming across a random z-index: 9999;. Though the application had sticky headers, floating action buttons, dropdowns, modals, pop up ads, and more in the stacking context, the highest value we had was 5. Even then, no one knew it since the values were all abstracted away from us in variables.

    Solving developer experience issues resulted in a z-index mini-framework, helping people make the correct decision with less effort and move more quickly.

    Something else we noticed was that sometimes we were assigning a z-index when we did not necessarily need one. Since stacking contexts can be created by several different properties, declarations like position: sticky; can act in a similar manner as setting z-index: 1;. In those cases, we continued to add a constant and declaration anyway. Keeping them allowed for better consistency in the system instead of allowing there to be special cases, which would degrade confidence about whether everything was working correctly or not. Keeping the constant list complete aided in understanding the system and set us up better for rearranging the values when necessary.

    What It Doesn’t Solve

    Like any framework, there are parts that it doesn’t not do a good job at solving for you. Naming things is still hard, and this framework slightly exacerbates the problem by requiring that you name all of your z-index values. Even still, we found that the gained clarity overcame the chore of having to name them all.

    This system also does not necessarily help you figure out which stacking context a bug is in. Coming across a z-index bug where you don’t know where the new stacking context is created or where the z-index value is set is not solved by this framework, but once you have found the issue, the path to making the correct fix is clear.

    Using It In Your App

    The ideas shared here should be actionable in most applications depending on your styling solution and browser support. Migrating to use this system is not very risky since stacking contexts are already scoped individually; you can migrate one context as it already exists at a time. Changing to use these conventions forces you to describe more clearly what you already have in your app, shining a light on what might currently seem like a dark, scary corner.

    If your z-index values are in a state where you are unsure about most or all of them, then the best way to convert to this system will probably be to start by creating a constant for each value in a single list in a single file. As the stacking contexts become more clear, you can start grouping them and renaming them (if necessary) to conform to the naming convention.

    My team was not working with any external CSS libraries or frameworks that included z-index values, but that could possibly add some difficulty to adopting this system. Hopefully, the utilities are configurable enough to deal with most uses and to even incorporate the third-party values into the system.

    Finally, all of the examples here have been written as a single file of z-index values, but you could collocate these values with the component to make an even stronger connection between the two. Using a file naming convention will make it easier to find all of the values throughout the application.

    Try It Out Yourself

    If you are having trouble wrangling z-index values on your site and end up trying out these suggestions, I would love to hear about your experience. This mini-framework was developed over just a few months and has only been used in one production codebase, so there are certainly unexplored use cases and opinions that could be added or tweaked.

    Smashing Editorial
    (vf, yk, il)

    Source link

    web design

    Things You Can Do With CSS Today — Smashing Magazine

    02/01/2021

    About The Author

    An independent designer and front-end developer who’s trying to make everyone’s experience on the web better with a focus on progressive enhancement and …
    More about
    Andy

    The present and future of CSS are very bright indeed and if you take a pragmatic, progressive approach to your CSS, then things will continue to get better and better on your projects, too. In this article, we’ll look into masonry layout, :is selector, clamp(), ch and ex units, updated text decoration, and a few other useful CSS properties.

    CSS is great and getting better all the time. Over recent years, especially, it has evolved really fast, too. Understandably, some of the really handy powers CSS gives you might have slipped you by because of this, so in this article, I’m going to show you some really handy stuff you can do with modern CSS today, and also share some stuff that we can look forward to in the future.

    Let’s dig in.

    Masonry Layout

    Masonry layouts became very popular with Pinterest, Tumblr and Unsplash, and up until recently, we tended to rely on JavaScript to assist with our layout, which is almost never a good idea.

    Sure, you can use CSS multicol pretty darn effectively to achieve a masonry layout, but that approach can be problematic with tabbed-focus as it lays content out in columns. This creates a disconnect between the visual layout and the tabbing index.

    Fast forward to today (well, very shortly in the future) and a masonry layout is pretty trivial, thanks to an update to CSS Grid. Here’s a complete masonry layout, with gutters, in 6 lines of CSS:

    .masonry {
      display: grid;
      grid-template-columns: repeat(4, 1fr);
      grid-template-rows: masonry;
      grid-gap: 1rem;
    }

    The magic is in grid-template-rows set as masonry, which turns it into the “masonry axis”, thus providing the “filled in” layout we’ve all come accustomed to.

    Let’s expand on this and explore a quick demo of creating a responsive masonry layout. Using a slightly modified version of the above CSS, we can replace the grid-template-columns line to use this auto grid method instead:

    .masonry {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
      grid-template-rows: masonry;
      grid-gap: 1rem;
    }

    The minmax() function allows us to define what the smallest size is for our items, which for us, is 16rem. Then we tell minmax() what the maximum size should be for each item. We declare that as 1fr, which takes 1 portion of the remaining available space.

    This definition of grid-template-columns allows our layout to break and stack if it runs out of horizontal space which the masonry axis then automatically sorts our remaining elements for us.

    Note: Right now, masonry is only working in Firefox Nightly, or behind a flag, but the grid layout will still work perfectly in non-supporting browsers, making it a decent progressive enhancement target.

    See the Pen [Native Masonry Layout With CSS Grid](https://codepen.io/smashingmag/pen/OJbJzVB) by Andy Bell.

    See the Pen Native Masonry Layout With CSS Grid by Andy Bell.

    Rachel Andrew wrote a great article about CSS Grid Masonry and you can also read the CSS Grid Layout Module Level 3 editor’s draft here for technical details.

    Masonry support is currently very low, but as anything on the web, working out what your minimum viable experience is, then building up with progressive enhancement is a resilient way to build things. If you must use a masonry layout, though: I would recommend sticking with the tried-and-tested Masonry.js for now, but stick a ticket in your backlog to replace with native CSS in the future!

    Resources

    The :is Selector

    I imagine a lot of us have had to write some gnarly CSS like this in the past:

    .post h1,
    .post h2,
    .post h3 {
        line-height: 1.2;
    }
    
    .post img,
    .post video {
        width: 100%;
    }

    Thankfully, CSS has got our back again with the :is pseudo-class.

    That CSS can now be hugely simplified into this instead:

    .post :is(h1, h2, h3) {
        line-height: 1.2;
    }
    
    .post :is(img, video) {
        width: 100%;
    }

    When things get more complex, it gets even more useful, because you can chain other selectors, such as :not and :first-child, just like in the following demo:

    See the Pen [:is selector demo](https://codepen.io/smashingmag/pen/rNMXYGx) by Andy Bell.

    See the Pen :is selector demo by Andy Bell.

    The :is() pseudo-class works by taking a passed selector list then translating it into an expanded selector list for us. This allows us to write more compact code and for the browser to do what it does already.

    If you have a complex project where specificity is crucial, then the :where() pseudo-class could be useful. The main difference between :is() and :where() is that :where() has zero specificity, whereas the :is() pseudo-class uses the most specific selector in the passed selectors collection. This becomes useful if you think that rules set in your :is() block might need to be overridden out-of-context. This MDN article shows a great example of that.

    This :is() pseudo-class has fantastic browser support — aside from IE11 and Opera Mini — so I would absolutely recommend that you start using it today. I would suggest caution with the :where() pseudo-class, though, because right now, only Firefox and Safari support it.

    Resources

    Logical CSS Functions For Sizing

    Responsive design has evolved into intrinsic design over the years as designers rightly push the boundaries of design on the web. There have been lots of hacks in the past — especially with fluid typography — that have been rather fragile, to put it lightly.

    We do have some really useful CSS functions that help with sizing: min(), max() and clamp(). The min() function gets the smallest value from two passed parameters and max() does the opposite: grabs the largest value.

    The clamp() function is even handier as it allows you to pass a minimum, a maximum and an ideal value. Because of this “locking”, ideal value, clamp() is being used more and more in fluid typography, like Dave Rupert’s legendary FitText because you get a guaranteed baseline, which prevents unpredictable outcomes. It’s the basis of all of these functions because if you set a good baseline as the minimum for min() and a good baseline as the maximum in max(), you’re getting that needed flexibility, insured by a sensible level of control.

    These logical functions are way more useful than that though. Here’s a demo where I’m using them not just for a bit of fluid typography sizing, but also to size an avatar image effectively.

    See the Pen [Min and Clamp demo](https://codepen.io/smashingmag/pen/YzGmEee) by Andy Bell.

    See the Pen Min and Clamp demo by Andy Bell.

    In the demo, I’m using min() to size the image and also, calculate the border-radius in the same way. It’s incredibly subtle, but really helps to achieve high design detail on the web, which is great!

    Una Kravets has written a fantastically useful article on the use cases of these functions. I also use it to create a flexible wrapper.

    Resources

    Specific Responsive Units For Typography

    There are so many units in CSS that all cater to specific use cases. Did you know that there are units specifically for typography? Of course em and rem are font-size related, but ch and ex are based on the size of the letters themselves.

    The ch unit is equal to the width of the 0 character of your rendered font in its size. This scales with the font too, so it’s a really handy way to limit the width of your text, which helps with readability. Also, keep in mind that in proportional typefaces, 1ch is usually wider than the average character width, often by around 20-30%.

    The ex unit is equal to the height of the lowercase x character — also known as the “x-height” in more traditional typography. This is really useful for working accurately and responsively with the vertical axis of your typography. One really handy use case for this is making an SVG icon the same height as your text.

    In the following demo, I’ve solved two problems with these units. First, I’ve limited the text length with ch and then used the ex unit to position a <sup> and <sub> element, more effectively. This has long been a pain in web design!

    See the Pen [CH and EX units demo](https://codepen.io/smashingmag/pen/YzGmELa) by Andy Bell.

    See the Pen CH and EX units demo by Andy Bell.

    Resources

    Updated Text Decoration Control

    Text decoration is no longer boring. You can do loads now, thanks to some updates in Text Decoration Level 4. My favourite trick with this is creating a highlight style with text-decoration-thickness, text-decoration-skip-ink and text-decoration-color.

    See the Pen [Text decoration demo](https://codepen.io/smashingmag/pen/WNGVXKV) by Andy Bell.

    See the Pen Text decoration demo by Andy Bell.

    I also like using these new properties to better control underline thickness for heading elements, as they can get pretty heavy in certain fonts.

    I strongly recommend you watch this video by Jen Simmons where, as always, she explains CSS properties in a friendly easy-to-understand manner.

    Resources

    Scroll Margin

    This snippet of CSS will vastly improve your websites:

    [id] {
      scroll-margin-top: 2ex;
    }

    When a browser skips to an element with an id — often a heading in a long article like this one — the targeted element would sit flush to the top of the viewport. Not only did this not look great, but it caused issues for fixed headers too.

    This property — scroll-margin-top — is the antidote to all of that and is incredibly useful. Check out this demo where I combine it with smooth scrolling:

    See the Pen [Scroll margin demo](https://codepen.io/smashingmag/pen/XWjvzop) by Andy Bell.

    See the Pen Scroll margin demo by Andy Bell.

    Resources

    Aspect Ratio

    If there was ever something we needed desperately in responsive design, it was native aspect ratio. This is especially needed for embedded media, such as YouTube videos. There’s long been the ol’ padding hack for these containers, but a hack should only be a temporary thing.

    Thankfully, we will have aspect-ratio support in major browsers soon.

    If you enable layout.css.aspect-ratio.enabled in a Chromium browser, the following demos will be a perfect square and a perfectly responsive YouTube video, respectively:

    Square (1:1)

    Below is a square that’s always going to keep the same aspect ratio, 1:1 — achieve by defining aspect-ratio: 1 / 1.

    See the Pen [Perfect square with aspect ratio](https://codepen.io/smashingmag/pen/zYKgPbw) by Andy Bell.

    See the Pen Perfect square with aspect ratio by Andy Bell.

    Video (16:9)

    For videos, a square would be a quite uncommon format. Instead, we can use 16:9 be defining aspect-ratio: 16 / 9 on the box.

    See the Pen [Perfect video embed with aspect ratio](https://codepen.io/smashingmag/pen/oNzKoOq) by Andy Bell.

    See the Pen Perfect video embed with aspect ratio by Andy Bell.

    Even though aspect-ratio isn’t quite here yet, you should definitely start thinking about it — especially with images, as the following is likely to appear in all browsers as default styles, and is already in Firefox (69 onwards):

    img, input[type="image"], video, embed, iframe, marquee, object, table {
      aspect-ratio: attr(width) / attr(height);
    }

    This is going to be really helpful in reducing page load jank because elements like <img /> will generate a correctly sized box for themselves before they load. My advice is to start adding width and height attributes to elements in the above code sample to give your users a better loading experience. You can find more details about this particular issue on MDN.

    Also, speaking about useful articles: take a look at a handy article about the aspect-ratio unit here on Smashing Magazine, written by Rachel Andrew — I highly recommend you to read it.

    Resources

    Content-Visibility And Contains-Intrinsic-Size

    The last one on our tour is content visibility and how it can give us a huge performance boost. Because CSS lets you pretty much do anything, a browser has to calculate everything to render one single element. If you have a huge, complex page, it can result in some reasonably sluggish render and paint times.

    The new content-visibility and contains-intrinsic-size properties have arrived to help this and they are great.

    With content-visibility: auto, you can tell the browser not to worry about rendering the elements in there while they are outside of the viewport, which can have a massive impact on initial loading speeds. The only problem is that the element with content-visibility: auto set on it loses its height, so we set contains-intrinsic-size to something like 0 400px to hint at what sort of size the element will be when it’s loaded.

    These properties allow the browser to skip the initial rendering and instead, as the elements with content-visibility: auto set on them scroll near the viewport, the browser will start to render them. Proper progressive loading!

    This video by Jake Archibald demos it really well:

    Jake Archibald gives a whirlwind tour in a video presenting the new features and proposals to help users improve the performance of their pages
    A talk by Jake Archibald explaining some of the useful CSS features that were released in Chrome recently. Notably, content-visibility.

    You can also read this great article, too.

    Resources

    Wrapping Up And What’s Coming Up

    That’s a pretty cool new CSS, right? There’s loads more arriving soon and loads in the long-term pipeline too. We can look forward to Media Queries Level 5 which let us target the current ambient light level and whether or not the user prefers reduced data.

    We’ve also got CSS Nesting in draft, which will give us Sass-like nesting capabilities like this:

    .my-element {
        background: red;
    
        & p {
            background: yellow;
        }
    }

    We’re getting even more control too, with font metrics override descriptors and Cascade Level 5, which introduces layers to the cascade. Prototyping is happening with container queries too!

    Lastly, there are some cool new tricks on the horizon, like scroll-linked animations, which will open the door wide-open to a new generation of creative work on the web.

    In conclusion, the present and future of CSS are very bright indeed and if you take a pragmatic, progressive approach to your CSS: things will continue to get better and better on your projects too.

    Smashing Editorial
    (vf, yk, il)

    Source link

    web design

    Native CSS Masonry Layout In CSS Grid — Smashing Magazine

    11/02/2020

    About The Author

    Rachel Andrew is not only Editor in Chief of Smashing Magazine, but also a web developer, writer and speaker. She is the author of a number of books, including …
    More about
    Rachel
    Andrew

    There is now a specification for native CSS masonry layout, as part of the Grid Layout spec. In this article, Rachel Andrew explains how it works with the help of a couple of demos you can try out in Firefox Nightly.

    A Level 3 of the CSS Grid specification has been published as an Editor’s Draft, this level describes a way to do Masonry layout in CSS. In this article, I’ll explain the draft spec, with examples that you can try out in Firefox Nightly. While this is a feature you won’t be able to use in production right now, your feedback would be valuable to help make sure it serves the requirements that you have for this kind of layout. So let’s take a look.

    What Is A Masonry Layout?

    A masonry layout is one where items are laid out one after the other in the inline direction. When they move onto the next line, items will move up into any gaps left by shorter items in the first line. It’s similar to a grid layout with auto-placement, but without sticking to a strict grid for the rows.

    The most well-known example of masonry is on Pinterest, and you will sometimes hear people refer to the layout as a “Pinterest layout”.

    An example layout from the Pintrest website
    Pintrest

    There are a number of JavaScript tools to help you create this kind of layout, such as David DeSandro’s Masonry plugin.

    Can’t We Already Do This In CSS?

    We can come close to a masonry layout in a couple of ways. The closest way to achieve the look of this type of layout is to use Multi-column Layout. In the example below, you see something which looks visually like a masonry layout. However, the order of the boxes runs down the columns. Therefore, if the first items have the highest priority (e.g. if this were search results), then the apparent first items in the top row aren’t actually the ones that came back first.

    See the Pen Masonry Multicol example by Rachel Andrew (@rachelandrew) on CodePen.

    See the Pen Masonry Multicol example by Rachel Andrew (@rachelandrew) on CodePen.

    When designers first saw Grid layout, they often thought that auto-placement along with the dense packing mode might achieve masonry. While you can fill all of the gaps in this way, the layout is still a grid and therefore there is no way to cause items to rise up into the gaps left by shorter items.

    See the Pen Masonry autoflow example by Rachel Andrew (@rachelandrew) on CodePen.

    See the Pen Masonry autoflow example by Rachel Andrew (@rachelandrew) on CodePen.

    Therefore, in order to achieve masonry, it still requires JavaScript. Doing layout with JavaScript — in particular with the large number of items that often benefit from this type of layout — is never going to perform well. I initially noted that web developers were asking for the feature back in January 2017, and while I have some concerns as to whether this really is a grid thing (and also the potential for accessibility problems due to content reordering), I am glad it is moving forward.

    The Masonry Feature Of Grid Layout

    This is a new specification, so things may well change before this ships in more browsers. However, we are in a nice position in that there is already an implementation of Masonry in Firefox. Get a copy of Firefox Nightly, and enable the layout.css.grid-template-masonry-value.enabled flag in about:config to play with it. Once you have done that and returned to this page using Firefox, all the demos will work.

    To use masonry layout, one of your grid axes needs to have the value masonry. This will then be referred to as the masonry axis, the other axis will have rows or column tracks defined as normal, this will be the grid axis. The CSS below creates a four-column grid, with the rows set to masonry. The masonry items will be displayed in the four columns of my grid axis.

    .container {
      display: grid;
      grid-template-columns: repeat(4, 1fr);
      grid-template-rows: masonry;
    }
    
    A simple masonry layout
    Our Pure CSS Masonry Layout

    That’s all you need to do to get a simple masonry layout. Using Firefox, you can see that in the CodePen example below.

    See the Pen Basic CSS Masonry by Rachel Andrew (@rachelandrew) on CodePen.

    See the Pen Basic CSS Masonry by Rachel Andrew (@rachelandrew) on CodePen.

    We could stop there, however, adding masonry into CSS Grid means that we might expect some other grid things to work even when we are in a masonry layout. Therefore, the spec needs to define those things.

    Behavior On The Grid Axis

    The axis which has defined tracks behaves in exactly the same way as a regular grid. Therefore you can size tracks, name lines, and use alignment in the same way that you would in a regular grid.

    You can also position items using line-based placement on this axis. These will be placed first before the masonry items are placed. In the next example, I have placed the image with a caption of 5 between the line named box-start and the line named box-end. The masonry items are placed around it.

    See the Pen Masonry with positioned item by Rachel Andrew (@rachelandrew) on CodePen.

    See the Pen Masonry with positioned item by Rachel Andrew (@rachelandrew) on CodePen.

    It is also possible to span tracks as normal on the grid axis. In the next example, I have some elements that have a class of landscape. These items are spanning two column tracks when placed in the masonry layout.

    See the Pen Masonry spanning example by Rachel Andrew (@rachelandrew) on CodePen.

    See the Pen Masonry spanning example by Rachel Andrew (@rachelandrew) on CodePen.

    The masonry-auto-flow property

    The masonry specification adds some additional properties to Grid layout. The masonry-auto-flow property is not yet in the Firefox implementation. When implemented this property will give you control over the flow of items in the masonry layout.

    Using masonry-auto-flow: next will place the item in the next location on the grid axis rather than packing it into the column with the most space as happens by default.

    Using masonry-auto-flow: ordered will cause masonry to ignore items with a definite placement and lay the items out using order-modified document order; that is, in the order that they are in the document unless ordered with the order property.

    The justify-tracks and align-tracks properties

    These properties work to some extent in Firefox Nightly at the time of writing. These are additional alignment properties for masonry layouts. If you have masonry in the block direction then you can use align-tracks, if you have masonry in the inline direction use justify-tracks.

    If you have extra space in your grid container in the dimension being laid out using masonry, you will then discover that the items align to the start of the container. The initial value of align-tracks (in our case with masonry being created for rows) is start.

    These properties work alongside align-content and justify-content. To show how, I have an example where the grid container has a height of 200vh. I have set the align-tracks value to end.

    If align-content is normal (which it will be if I have not added the property), then the masonry tracks will end up at the end of the container.

    See the Pen Masonry align-tracks by Rachel Andrew (@rachelandrew) on CodePen.

    See the Pen Masonry align-tracks by Rachel Andrew (@rachelandrew) on CodePen.

    If I add align-content: start, the masonry tracks return to the start of the container. However, the “rough edges” of the layout are now at the top rather than the bottom because the masonry tracks are aligned to the end.

    A simple masonry layout aligned to the end of the container
    Aligning the masonry items to the end

    Note: You can use any of the values used for align-content for align-tracks and justify-tracks. There are some nice examples in the spec of different combinations.

    If you set align-tracks: stretch, then any auto-sized items in the layout will stretch. The masonry effect is retained, but anything with a definite size on that axis will not be stretched out of shape.

    See the Pen Masonry align-tracks: stretch by Rachel Andrew (@rachelandrew) on CodePen.

    See the Pen Masonry align-tracks: stretch by Rachel Andrew (@rachelandrew) on CodePen.

    The align-tracks and justify-tracks properties can take multiple values. One for each track in the grid axis. This means that in our four-track grid we could have the first track stretching, the second aligned to start, the third aligned to end, and the fourth aligned to center.

    This did not seem to work at the time of writing in Firefox.

    The spec details that if there are fewer values than tracks, the remaining tracks will use the final specified value. If there are more values than tracks, additional ones will be ignored.

    Fallback Behavior

    The inclusion of this feature into the grid specification has a definite benefit where creating a fallback layout is concerned. As masonry behaves in a similar way to auto-placement, if a browser doesn’t support masonry then regular auto-placement can be used instead. This is likely to create the gaps in the layout as seen in the earlier example, but is certainly not terrible.

    You can see this in action by looking at any of the demos so far using a browser with no support for masonry. You still get a layout. If you wanted to do something entirely different then you could check for support for masonry with feature queries. You could perhaps do the layout with multicol for non-supporting browsers.

    @supports (grid-template-rows: masonry) {
      /* masonry code here */
    }
    

    If the masonry layout is vital then you could check for masonry support using CSS.supports and only use the JavaScript masonry script if there is no support. This would mean that as browsers implement native masonry they would lose the overhead of the scripted version, but it would be there as a polyfill.

    Potential Accessibility Concerns

    While masonry in CSS is exciting, it is yet another place where content reordering and a disconnection of the document order from the visual order may happen. As I noted on a recent issue that was raised, I feel that we are creating exciting layout possibilities and then needing to tell people to be very careful how they use them.

    I’ve written about this problem in Grid, content reordering, and accessibility. I hope that as we move forward with this specification, there are also renewed efforts to find a good way forward with regard to content vs. display order.

    Your Feedback Is Needed

    We are really lucky to not only have this new spec, but to have a browser implementation to test it in. If you have examples where you have used masonry, why not try replacing your JavaScript with the grid version and see if it works for you? If you run into problems or can’t do something you were able to do in your previous implementation, please let the CSSWG know by raising an issue.

    While things are in an experimental state, this is your chance to help influence any changes and point out any problems. So please do, and help make this really great feature even better!

    Smashing Editorial
    (il)

    Source link

    web design

    A CSS Grid Framework For Shopify Collection Pages — Smashing Magazine

    09/22/2020

    About The Author

    Liam is a Developer Community Manager at Shopify, based in Ireland, and specializes in front end development, Liquid, and working with themes. He is passionate …
    More about
    Liam

    In this article, we’ll be looking at how to set up a grid layout for products on your collection pages, and how to use Shopify’s section settings to create customizable options in the online store editor.

    CSS Grid has become an increasingly popular technique for applying a layout to pages amongst other CSS frameworks. Developers can take advantage of this system to reduce complexity and define clear style rules. As explained in my Shopify blog post on getting started with a CSS grid layout, a CSS Grid framework can be easily implemented on Shopify themes to design responsive page layouts based on rows and columns.

    All pages of a Shopify online store can adopt CSS Grid, but one obvious touchpoint of any e-commerce site that can benefit from a robust and clean grid layout is the collection page. On collection pages, it feels natural that products are organized in a grid format, with rows and columns. So, if an option for creating a robust grid arrangement with a simple set of rules is possible, it’s worth exploring for your custom theme projects.

    Note: To get an idea of how this could look for your clients and so you can follow along with this CSS Grid tutorial, I’ve set up a test store which you can use to see the approach I’ve outlined in this tutorial.

    Creating A Basic Collection Page Layout

    Working with CSS Grid on a Shopify collection page will operate in very much the same way as how Grid works on a custom section—something we explored in the CSS grid blog article. Thankfully, Shopify has excellent CSS grid support. The biggest difference when implementing a grid system on a collection page is that you won’t need to assign a class to each individual item. Note that if you aren’t extremely advanced with CSS, we recommend you read over our intro to CSS guide before going further.

    Now, since products are automatically outputted in a loop as repeatable content items, it’s possible to apply the same class to all products that are associated with a collection. But first, let’s look at an example of a collection page with no styling.

    If you start off with a basic collection page setup, you’d likely have markup that looks like the following:

    <h1>{{ collection.title }}</h1>
      {% for product in collection.products %}
        <a href="{{ product.url | within: collection }}">
          <img src="http://www.smashingmagazine.com/{{ product.featured_image.src" img_url: '300x' }}" alt="{{ product.featured_image.alt | escape }}">
        </a>
        <a href="{{ product.url | within: collection }}">{{ product.title }}</a>
          <p>{{ product.price | money }}</p>
      {% unless product.available %}<br><strong>sold out</strong>{% endunless %}
      {% endfor %}
    

    This will output the collection name as a header, and display the collection’s associated products with their image, name, and price. Without any styling, these products will appear in a vertical row by default. The size of the product images will be 300 pixels, as defined by the img_url filter.

    To apply a CSS Grid framework to this group of products, you’ll first want to wrap the collection for loop in one main grid container, which is considered the parent container. Next, you can wrap the code for each individual product (the children) within its own individual container.

    Once these containers are added, the markup would appear as:

    <h1>{{ collection.title }}</h1>
      <div class="grid-collection">
        {% for product in collection.products %}
          <div class="grid-product">
            <a href="{{ product.url }}">
              <img src="http://www.smashingmagazine.com/{{ product.featured_image.src" img_url: '300x' }}" alt="{{ product.featured_image.alt | escape }}">
            </a>
            <a href="{{ product.url }}">{{ product.title }}</a>
              <p>{{ product.price | money }}</p>
              {% unless product.available %}<br><strong>sold out</strong>{% endunless %}
          </div>
        {% endfor %}
      </div>
    

    Applying The CSS Grid Framework Styling To The Collection Page

    Now that we have a basic collection page with a hierarchy of containers, you can divide the products into a grid layout by applying styles to the classes you’ve created. In the themes stylesheet file, you can add the following:

    .grid-collection {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    }
    
    .grid-product {
      display: grid;
    }
    

    Now, when you navigate to the collection page, you should see the products appearing in a grid, fitting into the available space on the screen.

    A preview of the collection page
    (Large preview)

    As well as adding display: grid, you’ll notice we’re also using the grid-template-columns property, which can be used to define how many columns appear within the grid. Instead of defining a fixed value, we can use the repeat notation to create a rule that our products should appear as many times as they can fit inside the Grid.

    Within the functional notation, auto-fit is displaying as many items on the line as possible, so on a full screen, we will see as many products appearing as there is space on the buyers screen. Finally, with minmax, we set up a rule that each cell should be a minimum of 300 pixels, and a maximum of one fraction of the grid-container.

    When using this property, we need to ensure that the size defined in the minmax function matches, or is larger than, the size defined by the img_url Liquid filter in our markup. If the minmax function contains a smaller pixel size, you’ll see that product images become cut off as they won’t have enough space within the defined cell.

    Once our basic grid is appearing as expected, we can add additional CSS to tidy up the layout by adding margin space and positioning the products on the center of the page. If you’d like the gap between your columns and rows to be the same, you can define both with the gap property, rather than defining each separately.

    Once this is all set up, your stylesheet will look like this:

    .grid-collection {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
      gap: 1px;
      margin: 1em;
      background-color: white;
    }
    
    .grid-product {
      display: grid;
      justify-content: center;
      padding: 10px;
      color: white;
      line-height: 1;
      border-radius: 5px;
    }
    

    While this is a simple example of how a CSS Grid framework can be applied to a collection page, I’d recommend that you experiment with different parameters to suit your client’s images and existing brand imagery. You can also use this approach to create grids on other pages, like the cart and adjust based on its unique characteristics.

    Adding Customizable Grid Options

    The above approach works well for a grid that will display columns of products based on the size of the screen. But, what if you want to give the merchant some control over how the grid is represented?

    In some cases your clients may want to customize the product page, and dictate how many products appear.

    If your markup is contained in a section file, you can create section settings that will allow clients to customize the grid from the online store editor. A configuration of settings that allows your client to select a number of products on a row could look like this:

    {% schema %}
    
    {
        "name": "Collection",
        "settings": [
      {
        "type": "select",
        "id":  "product_number",
        "label": "Number of products per row",
        "options": [
            {
            "value": "two",
            "label": "two"
            },
            {
            "value": "three",
            "label": "three"
            },
            {
            "value": "four",
            "label": "four"
            }
          ]
        }
      ]
    }
    
    {% endschema %}
    

    You can see here that the setting has a type of select which will output a drop down option on the online store editor. There is also a label property to describe the setting.

    The id property will not be visible on the editor, but we can reference this to create a variable. A common use-case for variables created with section objects is to reference them within the markup to change class names based on what settings are selected.

    To achieve this effect, we can use Liquid to output the value that is selected on the online store editor, as an attribute of the section object. This object will be expressed as {{ section.settings.product_number }}, and will output whichever value is the selected option.

    One way of looking at it is that the id we assigned in the section setting becomes a “placeholder” for the value in the selected option.

    Then, we can take this object and append it to the class name of the collection. This will allow the class name to change based on the selected option, and you can create different CSS rules for each class name.

    When we append the variable to the existing collection class name it will look like:

    <div class="grid-collection-{{ section.settings.product_number }}">
    

    Here you can see that the section object references the id of the section setting. The value that is outputted by this section object is determined by the value selected on the online store editor. For example, if “three” is selected on our drop down box, this would cause the markup to output as:

    <div class="grid-collection-three">
    

    Now we can move back to our stylesheet and set up different CSS rules for grid-collection-two, grid-collection-three, and grid-collection-four. These would look like:

    .grid-collection-two {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 1px;
      margin: 1em;
      background-color: white;
    }
    
    .grid-collection-three {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: 1px;
      margin: 1em;
      background-color: white;
    }
    
    .grid-collection-four {
      display: grid;
      grid-template-columns: repeat(4, 1fr);
      gap: 1px;
      margin: 1em;
      background-color: white;
    }
    

    The grid-template-columns property determines how many columns will appear within the grid, and as a result, how many products will appear in a row on the collection page. So, each class will have a different value for the grid-template-columns property, that corresponds with its unique class name.

    Now when a client navigates to the online store editor and selects an option for “Number of products per row”, the grid will adjust to reflect this:

    Preview of the online store editor

    Finally, we can add media queries so that there are different CSS Grid rules for smaller screens. This will avoid the grid appearing with too many columns of products on smaller devices, which would result in products appearing off-screen.

    Each variation of the collection-grid class can be assigned different rules where the grid will drop to two or one columns. When this is set up on your stylesheet, it could look like this:

    @media screen and (max-width: 992px) {
      .grid-collection-two {
        grid-template-columns: repeat(2, 1fr);
      }
    }
    
    @media screen and (max-width: 600px) {
      .grid-collection-two {
        grid-template-columns: repeat(1, 1fr);
      }
    }
    
    @media screen and (max-width: 992px) {
      .grid-collection-three {
        grid-template-columns: repeat(2, 1fr);
      }
    }
    
    @media screen and (max-width: 600px) {
      .grid-collection-three {
        grid-template-columns: repeat(1, 1fr);
      }
    }
    
    @media screen and (max-width: 992px) {
      .grid-collection-four {
        grid-template-columns: repeat(2, 1fr);
      }
    }
    
    @media screen and (max-width: 600px) {
      .grid-collection-four {
        grid-template-columns: repeat(1, 1fr);
      }
    }
    

    It’s likely that you’ll need to adjust the pixel sizes and values for the img_url filter based on the specific requirements of your client and the images they’re using. However, this method will show you how to get started using a CSS Grid system for collection pages on your own custom theme builds.

    Expanding The Grid

    Once you’ve applied a CSS Grid to your collection pages, you can start to consider other areas on your Shopify themes where robust website layouts may apply. As an example, it’s possible to create image gallery sections in a grid, and add irregular shaped cells for variety.

    There are a range of opportunities when using CSS Grid on Shopify, and each one potentially adds further value to your theme projects. With the help of this article, you can expand the CSS Grid framework to all of your theme projects.

    Smashing Editorial
    (ra, il)

    Source link

    web design

    How To Configure Application Color Schemes With CSS Custom Properties — Smashing Magazine

    08/11/2020

    About The Author

    Artur is a Software Engineer with a strong frontend and testing focus. He lives and works in Grodno, Belarus, and is passionate about web technologies and all …
    More about
    Artur

    In this article, Artur Basak introduces a modern approach on how to set up CSS Custom Properties that respond to the application colors. The idea of dividing colors into three levels can be quite useful: a palette (or scheme), functional colors (or theme), and component colors (local scope).

    Variables are a basic tool that help organize colors on a project. For a long time, front-end engineers used preprocessor variables to configure colors on a project. But now, many developers prefer the modern native mechanism for organizing color variables: CSS Custom Properties. Their most important advantage over preprocessor variables is that they work in realtime, not at the compilation stage of the project, and have support for the cascade model which allows you to use inheritance and redefinition of values on the fly.

    When you’re trying to organize an application color scheme, you can always place all custom properties that relate to color in the root section, name them, and use it in all needed places.

    See the Pen [Custom Properties for Colors](https://codepen.io/smashingmag/pen/RwaNqxW) by Artur Basak.

    See the Pen Custom Properties for Colors by Artur Basak.

    That’s an option, but does it help you to resolve issues of application theming, white labeling, a brand refresh, or organizing a light or dark mode? What if you need to adjust the color scheme to increase contrast? With the current approach, you will have to update each value in your variables.

    In this article, I want to suggest a more flexible and resistant approach on how to split color variables using custom properties, which can solve many of these issues.

    Setup Color Palette

    The coloring of any website begins with the setup of a color scheme. Such a scheme is based on the color wheel. Usually, only a few primary colors form the basis of a palette, the rest are derived colors — tones and mid-tones. Most often, the palette is static and does not change while the web application is running.

    According to the color theory, there are only a few options for color schemes:

    • Monochromatic scheme (one primary color)
    • Complementary scheme (two primary colors)
    • Triad scheme (three primary colors)
    • Tetradic scheme (four primary colors)
    • Adjacent pattern (two or three primary colors)

    For my example, I will generate a triad color scheme using the Paletton service:

    Color wheel with the established triadic scheme: variation of green, blue and red.
    Paletton Service: Triadic Color Scheme. (Large preview)

    I now have three main colors. On the basis of these, I will calculate the tones and mid-tones (the HSL format in combination with the calc function is a very useful tool for this). By changing the lightness value, I can generate several additional colors for the palette.

    See the Pen [HSL Palette](https://codepen.io/smashingmag/pen/OJNPaQW) by Artur Basak.

    See the Pen HSL Palette by Artur Basak.

    Now if the palette is modified, then it will be necessary to change only the value of the primary colors. The rest will be recalculated automatically.

    If you prefer HEX or RGB formats, then it does not matter; a palette can be formed at the stage of compiling the project with the corresponding functions of the preprocessor (e.g. with SCSS and the color-adjust function). As I’ve mentioned before, this layer is mostly static; it’s extremely rare that the palette may be changed in a running application. That’s why we can calculate it with preprocessors.

    Note: I recommend also generating both HEX literal and RGB for each color. This will allow playing with the alpha channel in the future.

    See the Pen [SCSS Palette](https://codepen.io/smashingmag/pen/oNxgQqv) by Artur Basak.

    See the Pen SCSS Palette by Artur Basak.

    The palette level is the only level where the color is encoded directly in the variable names, i.e. we can uniquely identify the color by reading the name.

    Define Theme Or Functional Colors

    Once the palette is done, the next step is the level of functional colors. At this level, the value of the color is not so important as its purpose, the function it performs, and what it exactly colorizes. For example, the primary or app brand color, border color, color of the text on a dark background, the color of the text on a light background, button background color, link color, hover link color, hint text color, and so on.

    These are extremely common things for almost any website or application. We can say that such colors are responsible for a certain color theme of the application. Also, the values of such variables are taken strictly from the palette. Thus, we can easily change application themes by simply operating with different color palettes.

    Below, I have created three typical UI controls: a button, a link, and an input field. They are colored using functional variables that contain values from the palette that I previously generated above. The main functional variable that is responsible for the application theme (conditional brand) is the primary color variable.

    Using the three buttons at the top, you can switch themes (change the brand color for controls). The change occurs by using the appropriate CSSOM API (setProperty).

    See the Pen [Functional Colors](https://codepen.io/smashingmag/pen/poyvQLL) by Artur Basak.

    See the Pen Functional Colors by Artur Basak.

    This approach is convenient not only for theming but also for configuring individual web pages. For example, on the zubry.by website, I used a common stylesheet and a functional variable --page-color to colorize the logo, headings, controls, and text selection for all pages. And in the own styles of each page, I just redefined this variable to set the page its individual primary color.

    3 web pages of ZUBRY.BY website: stamps page, postcards page and cards page.
    ZUBRY.BY website where each page has individual primary color. (Large preview)

    Use Component Colors

    Large web projects always contain decomposition; we split everything into small components and reuse them in many places. Each component usually has its own style meaning it doesn’t matter what we used to decompose BEM or CSS Modules, or another approach; it’s important that each such piece of code can be called local scope and reused.

    In general, I see the point in using color variables at the component level in two cases.

    The first is when components that according to application style guide are repeated with different settings, e.g. buttons for different needs like primary (brand) button, secondary button, tertiary, and so on.

    Different button styles for Tispr application.
    Tispr application styleguide. Buttons. (Large preview)

    The second is when components that have several states with different colors, e.g. button hover, active and focus states; normal and invalid states for input or select field, and so on.

    A more rare case when component variables may come in handy is the functionality of a “white label”. The “white label” is a service feature that allows the user to customize or brand some part of the user interface to improve the experience of interacting with their clients. For example, electronic documents that a user shares with his customers through a service or email templates. In this case, the variables at the component level will help to configure certain components separately from the rest of the color theme of the application.

    In the example below, I’ve now added controls for customizing colors of the primary (brand) button. Using color variables of the component level we can configure UI controls separately from each other.

    See the Pen [Component Colors](https://codepen.io/smashingmag/pen/LYNEXdw) by Artur Basak.

    See the Pen Component Colors by Artur Basak.

    How To Determine What Level A Variable Has?

    I came across the question of how to understand what can be put in the root (theme or functional level), and what to leave at the level of a component. This is an excellent question that is difficult to answer without seeing the situation you are working with.

    Unfortunately, the same approach as in programming does not work with colors and styles, if we see three identical pieces of code then we need to refactor it.

    Color can be repeated from component to component, but this does not mean that it is a rule. There can be no relation between such components. For example, the border of the input field and the background of the primary button. Yes, in my example above that’s the case, but let’s check following example:

    See the Pen [Color Split: Only Palette](https://codepen.io/smashingmag/pen/YzqPRLX) by Artur Basak.

    See the Pen Color Split: Only Palette by Artur Basak.

    The dark gray color is repeated — this is the border of the input field, the fill color of the close icon, and the background of the secondary button. But these components are in no way connected with each other. If the border color of the input field changes, then we will not change the background of the secondary button. For such a case we must keep here just the variable from the palette.

    UI Controls: buttons, link, head and regular texts, input field
    Application style guide example. (Large preview)

    What about green? We can clearly define it as the primary or brand color, most likely, if the color of the main button changes, then the color of the link and header of the first level will also change.

    What about red? Invalid state of input fields, error messages, and the destructive buttons will have the same color at the whole application level. This is a pattern. Now I can define several common functional variables in the root section:

    See the Pen [Color Split: Functional Level](https://codepen.io/smashingmag/pen/MWyYzGX) by Artur Basak.

    See the Pen Color Split: Functional Level by Artur Basak.

    Regarding the level of component colors, we can easily identify components that can be customized using custom properties.

    The button is repeated with different settings, the background color and text for different use cases change — primary, secondary, tertiary, destructive or negative case.

    The input field has two states — incorrect and normal, where the background and border colors differ. And so, let’s put these settings into color variables at the level of the corresponding components.

    For the rest of the components, it is not necessary to define local color variables, this will be redundant.

    See the Pen [Color Split: Component Level](https://codepen.io/smashingmag/pen/BaKyGVR) by Artur Basak.

    See the Pen Color Split: Component Level by Artur Basak.

    You need to dive into the pattern language of your project, which is, probably, being developed by the design team and UX. Engineers must fully understand the whole concept of a visual language, only then we can determine what is common and should live on a functional level, and what should remain in the local scope of visibility.

    But everything is not so complicated, there are obvious things. The general background of the page, the background, and color of the main text, in most cases this is what sets the theme of your application. It is extremely convenient to collect such things that are responsible for the configuration of a particular mode (like dark or light mode).

    Why Not Put Everything In The Root Section?

    I had such an experience. On Lition project, the team and I were faced with the fact that we needed to support IE11 for the web application, but not for the website and landings. A common UI Kit was used between the projects, and we decided to put all the variables in the root, this will allow us to redefine them at any level.

    And also with this approach for the web application and IE11 case, we simply passed the code through the following post-processor plugin and transformed these variables into literals for all UI components in the project. This trick possible only if all variables were defined in the root section because the post-processor can’t understand the specifics of the cascade model.

    Main page of Lition web-site with opened browser dev tools
    Lition SSR web-site. All variables in the root section. (Large preview)

    Now I understand that this was not the right way. Firstly, if you put component colors into the root section, then you break the separation of concerns principle. As a result, you can end up with redundant CSS in the stylesheet. For example, you have the folder of components where each component has its own styles. You also have a common stylesheet where you describe color variables in the root section. You decide to remove the button component; in this case, you must remember to also remove the variables associated with the button from the common styles file.

    Secondly, this is not the best solution in terms of performance. Yes, a color change causes only the process of a repaint, not reflow/layout, this in itself is not too costly, but when you make some changes at the highest level, you will use more resources to check the entire tree than when these changes are in a small local area. I recommend reading the performance benchmark of CSS variables from Lisi Linhart for more details.

    On my current project Tispr, the team and I use split and do not dump everything in the root, on the high level only a palette and functional colors. Also, we are not afraid of IE11, because this problem is solved by the corresponding polyfill. Just install npm module ie11-custom-properties and import library into your application JS bundle:

    // Use ES6 syntax
    import "ie11-custom-properties";
    // or CommonJS
    require('ie11-custom-properties');

    Or add module by script tag:

    <script async src="http://www.smashingmagazine.com/./node_modules/ie11-custom-properties/ie11CustomProperties.js">

    Also, you can add the library without npm via CDN. The work of this polyfill is based on the fact that IE11 has minimal support for custom properties, where properties can be defined and read based on the cascade. This is not possible with properties starting with double dashes, but possibly with a single dash (the mechanism similar to vendor prefixes). You can read more about this in the repository documentation, as well as get acquainted with some limits. Other browsers will ignore this polyfill.

    Below is a palette of the Tispr web application as well as the controls of the “white label” functionality for the e-documents (such as user contracts, invoices, or proposals).

    Grid with following columns: color, color name, color HEX, color RGB.
    Tispr Styleguide: Color Palette. (Large preview)
    Custom color picker UI component
    Tispr Styleguide: Brand Picker for White Label functionality. (Large preview)

    Why Not Store Color Variables On The JavaScript Side?

    Another reasonable question: why not store the palette and function variables in JavaScript code? This can also be dynamically changed and later redefined colors through inline styles. This could be an option, but most likely this approach would be less optimal since you need to have access to certain elements and change their color properties. With CSS variables, you will only change a single property, i.e. the variable value.

    In JavaScript, there are no native functions or API for working with colors. In the CSS Color Module 5, there will be many opportunities to make derived colors or somehow calculate them. From the perspective of the future, CSS Custom Properties are richer and more flexible than JS variables. Also, with JS variables, there will be no possibility to use inheritance in cascade and that’s the main disadvantage.

    Conclusion

    Splitting colors into three levels (palette, functional, and component) can help you be more adaptive to changes and new requirements while working on a project. I believe that CSS Custom Properties are the right tool for organizing color split — it does not matter what you use for styling: pure CSS, preprocessors, or CSS-in-JS approach.

    I came to this approach through my own experience, but I’m not alone. Sara Soueidan described in her article a similar approach in which she split variables into global and component levels.

    I would also like to suggest reading the Lea Verou’s article where she describes possible cases of applying CSS variables (not only in terms of color).

    Smashing Editorial
    (ra, yk, il)

    Source link

    web design

    How To Configure Application Color Schemes With CSS Custom Properties — Smashing Magazine

    08/11/2020

    About The Author

    Artur is a Software Engineer with a strong frontend and testing focus. He lives and works in Grodno, Belarus, and is passionate about web technologies and all …
    More about
    Artur

    In this article, Artur Basak introduces a modern approach on how to set up CSS Custom Properties that respond to the application colors. The idea of dividing colors into three levels can be quite useful: a palette (or scheme), functional colors (or theme), and component colors (local scope).

    Variables are a basic tool that help organize colors on a project. For a long time, front-end engineers used preprocessor variables to configure colors on a project. But now, many developers prefer the modern native mechanism for organizing color variables: CSS Custom Properties. Their most important advantage over preprocessor variables is that they work in realtime, not at the compilation stage of the project, and have support for the cascade model which allows you to use inheritance and redefinition of values on the fly.

    When you’re trying to organize an application color scheme, you can always place all custom properties that relate to color in the root section, name them, and use it in all needed places.

    See the Pen [Custom Properties for Colors](https://codepen.io/smashingmag/pen/RwaNqxW) by Artur Basak.

    See the Pen Custom Properties for Colors by Artur Basak.

    That’s an option, but does it help you to resolve issues of application theming, white labeling, a brand refresh, or organizing a light or dark mode? What if you need to adjust the color scheme to increase contrast? With the current approach, you will have to update each value in your variables.

    In this article, I want to suggest a more flexible and resistant approach on how to split color variables using custom properties, which can solve many of these issues.

    Setup Color Palette

    The coloring of any website begins with the setup of a color scheme. Such a scheme is based on the color wheel. Usually, only a few primary colors form the basis of a palette, the rest are derived colors — tones and mid-tones. Most often, the palette is static and does not change while the web application is running.

    According to the color theory, there are only a few options for color schemes:

    • Monochromatic scheme (one primary color)
    • Complementary scheme (two primary colors)
    • Triad scheme (three primary colors)
    • Tetradic scheme (four primary colors)
    • Adjacent pattern (two or three primary colors)

    For my example, I will generate a triad color scheme using the Paletton service:

    Color wheel with the established triadic scheme: variation of green, blue and red.
    Paletton Service: Triadic Color Scheme. (Large preview)

    I now have three main colors. On the basis of these, I will calculate the tones and mid-tones (the HSL format in combination with the calc function is a very useful tool for this). By changing the lightness value, I can generate several additional colors for the palette.

    See the Pen [HSL Palette](https://codepen.io/smashingmag/pen/OJNPaQW) by Artur Basak.

    See the Pen HSL Palette by Artur Basak.

    Now if the palette is modified, then it will be necessary to change only the value of the primary colors. The rest will be recalculated automatically.

    If you prefer HEX or RGB formats, then it does not matter; a palette can be formed at the stage of compiling the project with the corresponding functions of the preprocessor (e.g. with SCSS and the color-adjust function). As I’ve mentioned before, this layer is mostly static; it’s extremely rare that the palette may be changed in a running application. That’s why we can calculate it with preprocessors.

    Note: I recommend also generating both HEX literal and RGB for each color. This will allow playing with the alpha channel in the future.

    See the Pen [SCSS Palette](https://codepen.io/smashingmag/pen/oNxgQqv) by Artur Basak.

    See the Pen SCSS Palette by Artur Basak.

    The palette level is the only level where the color is encoded directly in the variable names, i.e. we can uniquely identify the color by reading the name.

    Define Theme Or Functional Colors

    Once the palette is done, the next step is the level of functional colors. At this level, the value of the color is not so important as its purpose, the function it performs, and what it exactly colorizes. For example, the primary or app brand color, border color, color of the text on a dark background, the color of the text on a light background, button background color, link color, hover link color, hint text color, and so on.

    These are extremely common things for almost any website or application. We can say that such colors are responsible for a certain color theme of the application. Also, the values of such variables are taken strictly from the palette. Thus, we can easily change application themes by simply operating with different color palettes.

    Below, I have created three typical UI controls: a button, a link, and an input field. They are colored using functional variables that contain values from the palette that I previously generated above. The main functional variable that is responsible for the application theme (conditional brand) is the primary color variable.

    Using the three buttons at the top, you can switch themes (change the brand color for controls). The change occurs by using the appropriate CSSOM API (setProperty).

    See the Pen [Functional Colors](https://codepen.io/smashingmag/pen/poyvQLL) by Artur Basak.

    See the Pen Functional Colors by Artur Basak.

    This approach is convenient not only for theming but also for configuring individual web pages. For example, on the zubry.by website, I used a common stylesheet and a functional variable --page-color to colorize the logo, headings, controls, and text selection for all pages. And in the own styles of each page, I just redefined this variable to set the page its individual primary color.

    3 web pages of ZUBRY.BY website: stamps page, postcards page and cards page.
    ZUBRY.BY website where each page has individual primary color. (Large preview)

    Use Component Colors

    Large web projects always contain decomposition; we split everything into small components and reuse them in many places. Each component usually has its own style meaning it doesn’t matter what we used to decompose BEM or CSS Modules, or another approach; it’s important that each such piece of code can be called local scope and reused.

    In general, I see the point in using color variables at the component level in two cases.

    The first is when components that according to application style guide are repeated with different settings, e.g. buttons for different needs like primary (brand) button, secondary button, tertiary, and so on.

    Different button styles for Tispr application.
    Tispr application styleguide. Buttons. (Large preview)

    The second is when components that have several states with different colors, e.g. button hover, active and focus states; normal and invalid states for input or select field, and so on.

    A more rare case when component variables may come in handy is the functionality of a “white label”. The “white label” is a service feature that allows the user to customize or brand some part of the user interface to improve the experience of interacting with their clients. For example, electronic documents that a user shares with his customers through a service or email templates. In this case, the variables at the component level will help to configure certain components separately from the rest of the color theme of the application.

    In the example below, I’ve now added controls for customizing colors of the primary (brand) button. Using color variables of the component level we can configure UI controls separately from each other.

    See the Pen [Component Colors](https://codepen.io/smashingmag/pen/LYNEXdw) by Artur Basak.

    See the Pen Component Colors by Artur Basak.

    How To Determine What Level A Variable Has?

    I came across the question of how to understand what can be put in the root (theme or functional level), and what to leave at the level of a component. This is an excellent question that is difficult to answer without seeing the situation you are working with.

    Unfortunately, the same approach as in programming does not work with colors and styles, if we see three identical pieces of code then we need to refactor it.

    Color can be repeated from component to component, but this does not mean that it is a rule. There can be no relation between such components. For example, the border of the input field and the background of the primary button. Yes, in my example above that’s the case, but let’s check following example:

    See the Pen [Color Split: Only Palette](https://codepen.io/smashingmag/pen/YzqPRLX) by Artur Basak.

    See the Pen Color Split: Only Palette by Artur Basak.

    The dark gray color is repeated — this is the border of the input field, the fill color of the close icon, and the background of the secondary button. But these components are in no way connected with each other. If the border color of the input field changes, then we will not change the background of the secondary button. For such a case we must keep here just the variable from the palette.

    UI Controls: buttons, link, head and regular texts, input field
    Application style guide example. (Large preview)

    What about green? We can clearly define it as the primary or brand color, most likely, if the color of the main button changes, then the color of the link and header of the first level will also change.

    What about red? Invalid state of input fields, error messages, and the destructive buttons will have the same color at the whole application level. This is a pattern. Now I can define several common functional variables in the root section:

    See the Pen [Color Split: Functional Level](https://codepen.io/smashingmag/pen/MWyYzGX) by Artur Basak.

    See the Pen Color Split: Functional Level by Artur Basak.

    Regarding the level of component colors, we can easily identify components that can be customized using custom properties.

    The button is repeated with different settings, the background color and text for different use cases change — primary, secondary, tertiary, destructive or negative case.

    The input field has two states — incorrect and normal, where the background and border colors differ. And so, let’s put these settings into color variables at the level of the corresponding components.

    For the rest of the components, it is not necessary to define local color variables, this will be redundant.

    See the Pen [Color Split: Component Level](https://codepen.io/smashingmag/pen/BaKyGVR) by Artur Basak.

    See the Pen Color Split: Component Level by Artur Basak.

    You need to dive into the pattern language of your project, which is, probably, being developed by the design team and UX. Engineers must fully understand the whole concept of a visual language, only then we can determine what is common and should live on a functional level, and what should remain in the local scope of visibility.

    But everything is not so complicated, there are obvious things. The general background of the page, the background, and color of the main text, in most cases this is what sets the theme of your application. It is extremely convenient to collect such things that are responsible for the configuration of a particular mode (like dark or light mode).

    Why Not Put Everything In The Root Section?

    I had such an experience. On Lition project, the team and I were faced with the fact that we needed to support IE11 for the web application, but not for the website and landings. A common UI Kit was used between the projects, and we decided to put all the variables in the root, this will allow us to redefine them at any level.

    And also with this approach for the web application and IE11 case, we simply passed the code through the following post-processor plugin and transformed these variables into literals for all UI components in the project. This trick possible only if all variables were defined in the root section because the post-processor can’t understand the specifics of the cascade model.

    Main page of Lition web-site with opened browser dev tools
    Lition SSR web-site. All variables in the root section. (Large preview)

    Now I understand that this was not the right way. Firstly, if you put component colors into the root section, then you break the separation of concerns principle. As a result, you can end up with redundant CSS in the stylesheet. For example, you have the folder of components where each component has its own styles. You also have a common stylesheet where you describe color variables in the root section. You decide to remove the button component; in this case, you must remember to also remove the variables associated with the button from the common styles file.

    Secondly, this is not the best solution in terms of performance. Yes, a color change causes only the process of a repaint, not reflow/layout, this in itself is not too costly, but when you make some changes at the highest level, you will use more resources to check the entire tree than when these changes are in a small local area. I recommend reading the performance benchmark of CSS variables from Lisi Linhart for more details.

    On my current project Tispr, the team and I use split and do not dump everything in the root, on the high level only a palette and functional colors. Also, we are not afraid of IE11, because this problem is solved by the corresponding polyfill. Just install npm module ie11-custom-properties and import library into your application JS bundle:

    // Use ES6 syntax
    import "ie11-custom-properties";
    // or CommonJS
    require('ie11-custom-properties');

    Or add module by script tag:

    <script async src="http://www.smashingmagazine.com/./node_modules/ie11-custom-properties/ie11CustomProperties.js">

    Also, you can add the library without npm via CDN. The work of this polyfill is based on the fact that IE11 has minimal support for custom properties, where properties can be defined and read based on the cascade. This is not possible with properties starting with double dashes, but possibly with a single dash (the mechanism similar to vendor prefixes). You can read more about this in the repository documentation, as well as get acquainted with some limits. Other browsers will ignore this polyfill.

    Below is a palette of the Tispr web application as well as the controls of the “white label” functionality for the e-documents (such as user contracts, invoices, or proposals).

    Grid with following columns: color, color name, color HEX, color RGB.
    Tispr Styleguide: Color Palette. (Large preview)
    Custom color picker UI component
    Tispr Styleguide: Brand Picker for White Label functionality. (Large preview)

    Why Not Store Color Variables On The JavaScript Side?

    Another reasonable question: why not store the palette and function variables in JavaScript code? This can also be dynamically changed and later redefined colors through inline styles. This could be an option, but most likely this approach would be less optimal since you need to have access to certain elements and change their color properties. With CSS variables, you will only change a single property, i.e. the variable value.

    In JavaScript, there are no native functions or API for working with colors. In the CSS Color Module 5, there will be many opportunities to make derived colors or somehow calculate them. From the perspective of the future, CSS Custom Properties are richer and more flexible than JS variables. Also, with JS variables, there will be no possibility to use inheritance in cascade and that’s the main disadvantage.

    Conclusion

    Splitting colors into three levels (palette, functional, and component) can help you be more adaptive to changes and new requirements while working on a project. I believe that CSS Custom Properties are the right tool for organizing color split — it does not matter what you use for styling: pure CSS, preprocessors, or CSS-in-JS approach.

    I came to this approach through my own experience, but I’m not alone. Sara Soueidan described in her article a similar approach in which she split variables into global and component levels.

    I would also like to suggest reading the Lea Verou’s article where she describes possible cases of applying CSS variables (not only in terms of color).

    Smashing Editorial
    (ra, yk, il)

    Source link

    web design

    Modern CSS Techniques To Improve Legibility — Smashing Magazine

    07/22/2020

    About The Author

    Edoardo is a Web Developer at Chialab. He developed his interests for typography and accessibility working on various EdTech products and some institutional …
    More about
    Edoardo

    In this article, we cover how we can improve websites legibility using some modern CSS techniques, great new technologies like variable fonts and putting into practise what we learned from doing scientific researches.

    We can read in many ways, and there are many different types of readers, each with their own needs, skills, language, and, above all, habits. Reading a novel at home is different from reading it on the train, just as reading a newspaper is different from browsing its online version. Reading, like any other activity, requires practice for someone to become fast and efficient. Basically, we read better those things that we are used to reading the most.

    Which aspects should we take into consideration when designing and developing for reading? How can we create accessible, comfortable, inclusive experiences for all readers, including the most challenged and those affected by dyslexia?

    Articles Dedicated To Accessibility

    At Smashing, we believe a good website is an accessible website, one which is available to everyone, no matter how they browse the web. We’ve highlighted just some of the many articles that we’re sure will help you create more accessible sites and web apps. Explore more articles →

    Spaces, Words, Sentences, And Paragraphs

    Units

    On a web page, many units are available for us to adjust the font size of text. Understanding which unit to use is essential to setting the structure of a whole reading section. The reflowable nature of the web requires us to consider several aspects, such as the size of the viewport and the user’s reading preferences.

    For this reason, the most suitable choices are generally em and rem, which are font-specific units. For example, setting the margins between paragraphs using ems helps to preserve the vertical rhythm as the text size changes. However, this can be a problem when a serif font is alternated with a sans-serif within a section. In fact, at the same font size, fonts can appear optically very different. Traditionally, the height of the lowercase “x” character (the x-height) is the reference for determining the apparent size of a character.

    The comparison between the “d” and “x” glyphs of three different fonts at the same size reveal that their heights of the “x” (and therefore their optically size) are totally different
    At the same font size, characters will optically appear very different. (Large preview)

    Using the font-size-adjust rule, we can, therefore, optically render fonts of the same size, because the property will match the heights of the lowercase letters. Unfortunately, this property is currently available only in Firefox and in Chrome and Edge behind a flag, but it can be used as progressive enhancement using the @support check:

    @supports (font-size-adjust: 1;) {
        article {
            font-size-adjust: 0.5;
        }
    }
    

    It also helps with the swap from the fallback font to the one loaded remotely (for example, using Google Fonts).

    There are two articles. When switching the main font, the first article largely increase its length, since the font size is not adjusted to the x height, while the second one changes almost seamlessly
    The first example shows how switching the font works normally. In the second one, we are using font-size-adjust to make the swap more comfortable. (Large preview)

    Optimal Line Height

    We think typography is black and white. Typography is really white […] It is the space between the blacks that really makes it.

    — Massimo Vignelli, Helvetica, 2007

    Because typography is more a matter of “whites” than ”blacks”, when we apply this notion to the design of a website or web application, we must take into account special features such as line height, margins between paragraphs, and line breaks.

    Setting the font size by relying on the x-height helps with optimizing the line height. The default line height in browsers is 1.2 (a unitless value is relative to the font size), which is the optimal value for Times New Roman but not for other fonts. We must also consider that line spacing does not grow linearly with the font size and that it depends on various factors like the type of the text. By testing some common fonts for long-form reading, combined with sizes from 8 to 14 points, we were able to deduce that, on paper, the ratio between the x-height and the optimal line spacing is 37.6.

    A graph shows the relation between the ratio of x height and line height (y axis) and the ratio of x height and ascenders (x axis), with a downward trend from 38.1 to 35.8 fo the first ratio while increasing values of the x axis
    Acceptable line-spacing ranges. (Large preview)

    Compared to reading on paper, screen reading generally requires more spacing between lines. Therefore, we should adjust the ratio to 32 for digital environments. In CSS, this empirical value can be translated into the following rule:

    p {
        line-height: calc(1ex / 0.32);
    }
    

    In the right reading contexts, this rule sets an optimal line height for both serif and sans-serif fonts, even when typographical tools are not available or when a user has set a font that overwrites the one chosen by the designer.

    Define The Scale

    Now that we have adjusted the font size and used the ex unit to calculate the line height, we need to define the typographical scale in order to correctly set the spacing between paragraphs and to provide a good rhythm to the reading. As said before, line spacing does not grow linearly but varies according to the type of text. For titles with a large font size, for example, we should consider a higher ratio for the line height.

    article h1 {
        font-size: 2.5em;
        line-height: calc(1ex / 0.42);
        margin: calc(1ex / 0.42) 0;
    }
    
    article h2 {
        font-size: 2em;
        line-height: calc(1ex / 0.42);
        margin: calc(1ex / 0.42) 0;
    }
    
    article h3 {
        font-size: 1.75em;
        line-height: calc(1ex / 0.38);
        margin: calc(1ex / 0.38) 0;
    }
    
    article h4 {
        font-size: 1.5em;
        line-height: calc(1ex / 0.37);
        margin: calc(1ex / 0.37) 0;
    }
    
    article p {
        font-size: 1em;
        line-height: calc(1ex / 0.32);
        margin: calc(1ex / 0.32) 0;
    }
    

    Letter And Word Spacing

    When working on legibility, we must also consider readers who are challenged, such as those with dyslexia and learning disabilities. Developmental dyslexia affects reading, and discussion and research regarding the causes are still ongoing. It is important to make use of scientific studies to understand the effects that visual and typographic variables have on reading.

    For example, in a study that my company followed (“Testing Text Readability of Dyslexia-Friendly Fonts”), there was clear evidence that the glyph shapes of high-legibility fonts do not really assist reading, but wider spacing between characters (kerning) does. This finding was confirmed by another study on the effectiveness of increased spacing (“How the Visual Aspects Can Be Crucial in Reading Acquisition: The Intriguing Case of Crowding and Developmental Dyslexia”).

    These studies suggest that we should exploit the dynamism and responsiveness of web pages by offering more effective tools, such as controls for handling spacing. A common technique when enlarging the size of characters is to adjust the spacing between letters and words through CSS properties such as letter-spacing and word-spacing.

    See the Pen [Letter and word spacing](https://codepen.io/smashingmag/pen/KKVbOoE) by Edoardo Cavazza.

    See the Pen Letter and word spacing by Edoardo Cavazza.

    The problem with this is that letter-spacing acts unconditionally and breaks the kerning of the font, leading the page to render nonoptimal spaces.

    Alternatively, we can use variable fonts to gain more control over font rendering. Font designers can parameterize spacing in a variable and non-linear way, and can determine how the weight and shape of a glyph can better adapt to the habits of the reader. In the following example, using the Amstelvar font, we are able to increase the optical size as well as spacing and contrast, as intended by the designer.

    See the Pen [The optical size in variable fonts](https://codepen.io/smashingmag/pen/VweqoRM) by Edoardo Cavazza.

    See the Pen The optical size in variable fonts by Edoardo Cavazza.

    The Web.dev article “Introduction to Variable Fonts on the Web” has more detail on what variable fonts are and how to use them. And check out the Variable Fonts tool to see how they work.

    Width And Alignment

    To optimize reading flow, we also have to work on the width of the paragraph, which is the number of characters and spaces on a line. While reading, our eye focuses on about eight letters in a foveatio (i.e. the operation that is activated when we look at an object), and it is able to handle only a few consecutive repetitions. Therefore, line breaks are crucial: The moment of moving one’s focus from the end of a line to the beginning of the next is one of the most complex operations in reading and must be facilitated by keeping the right number of characters per type of text. For a basic paragraph, a common length is about 60 to 70 characters per line. This value can be easily set with the ch unit by assigning a width to the paragraph:

    p {
        width: 60ch;
        max-width: 100%;
    }
    

    Justification also plays an important role in reading across lines. Hyphenation support for languages is not always optimal in the various browsers; therefore, it must be checked. In any case, avoid justified text in the absence of hyphenation because the horizontal spacing that would be created would be an obstacle to reading.

    /* The browser correctly supports hyphenation */
    p[lang=”en”] {
        text-align: justify;
        hyphens: auto;
    }
    
    /* The browser does NOT correctly support hyphenation */
    p[lang=”it”] {
        text-align: left;
        hyphens: none;
    }
    

    Manual hyphenation can be used for languages that do not have native support. There are several algorithms (both server- and client-side) that can inject the &hyphen; entity within words, to instruct browsers where the token can be broken. This character would be invisible, unless it is located at the end of the line, whereupon it would render as a hyphen. To activate this behavior, we need to set the hyphens: manual CSS rule.

    Foreground Contrast

    The contrast of characters and words with the background is fundamental to legibility. The WCAG has defined guidelines and constraints for different standards (A, AA, AAA) governing the contrast between text and background. Contrast can be calculated with different tools, both for design and development environments. Keep in mind that automated validators are support tools and do not guarantee the same quality as a real test.

    By using CSS variables and a calc statement, we can dynamically calculate the color that offers the best contrast with the background. In this way, we can offer the user different types of contrast (sepia, light gray, night mode, etc.), by converting the whole theme according to the background color.

    article {
        --red: 230;
        --green: 230;
        --blue: 230;
        --aa-brightness: (
            (var(--red) * 299) +
            (var(--green) * 587) +
            (var(--blue) * 114)
        ) / 1000;
        --aa-color: calc((var(--aa-brightness) - 128) * -1000);
        background: rgb(var(--red), var(--green), var(--blue));
        color: rgb(var(--aa-color), var(--aa-color), var(--aa-color));
    }
    

    See the Pen [Automatic text contrast](https://codepen.io/smashingmag/pen/zYrygyr) by Edoardo Cavazza.

    See the Pen Automatic text contrast by Edoardo Cavazza.

    In addition, with the introduction and cross-browser support of the prefer-color-scheme media query, it becomes even easier to manage the switch from light to dark theme, according to user preference.

    @media (prefers-color-scheme: dark) {
        article {
            --red: 30;
            --green: 30;
            --blue: 30;
        }
    }
    

    Going Forward

    Designing and developing for optimal reading require a lot of knowledge and the work of many professionals. The more this knowledge is spread across the team, the better off users will be. Below are some points to lead us to good results.

    For Designers

    • Consider semantic structure as being part of the project, rather than a technical detail;
    • Document layout and font metrics, especially the why’s and how’s of your choices. They will help developers to correctly implement the design;
    • Reduce typographic variables as much as possible (fewer families, styles, and variants).

    For Developers

    • Learn the principles of typography in order to understand the design decisions made and how to implement them;
    • Use units relative to font size to implement responsive layouts (paddings, margins, gaps) that scale to user preferences;
    • Avoid unrestrained manipulation of font metrics. Legibility might suffer when font constraints are not respected.

    For Teams

    • Read and understand the principles of the WCAG;
    • Consider inclusion and accessibility as part of the project (rather than separate issues).

    Reading is a complex activity. Despite the many resources on web typography and the academic papers that identify areas for improvements, there is no magical recipe for achieving good legibility. The number of variables to consider might seem overwhelming, but many of them are manageable.

    We can set the optimal line height of a paragraph using the ex unit, as well as set a paragraph’s width using the ch unit, in order to respect the user’s preferred browser settings for font size and family. We can use variable fonts to adjust the spacing between letters and words, and we can manipulate the stroke of glyphs to increase contrast, helping readers with visual impairments and dyslexia. We can even automatically adjust text contrast using CSS variables, giving the user their preferred theme.

    All of these help us to build a dynamic web page whose legibility is optimized according to the user’s needs and preferences. Finally, given that every little implementation or technological detail can make a huge difference, it is still essential to test users’ reading performance using the final artifact.

    Smashing Editorial
    (ra, yk, al, il)

    Source link

    web design

    Modern CSS Techniques To Improve Legibility — Smashing Magazine

    07/22/2020

    About The Author

    Edoardo is a Web Developer at Chialab. He developed his interests for typography and accessibility working on various EdTech products and some institutional …
    More about
    Edoardo

    In this article, we cover how we can improve websites legibility using some modern CSS techniques, great new technologies like variable fonts and putting into practise what we learned from doing scientific researches.

    We can read in many ways, and there are many different types of readers, each with their own needs, skills, language, and, above all, habits. Reading a novel at home is different from reading it on the train, just as reading a newspaper is different from browsing its online version. Reading, like any other activity, requires practice for someone to become fast and efficient. Basically, we read better those things that we are used to reading the most.

    Which aspects should we take into consideration when designing and developing for reading? How can we create accessible, comfortable, inclusive experiences for all readers, including the most challenged and those affected by dyslexia?

    Articles Dedicated To Accessibility

    At Smashing, we believe a good website is an accessible website, one which is available to everyone, no matter how they browse the web. We’ve highlighted just some of the many articles that we’re sure will help you create more accessible sites and web apps. Explore more articles →

    Spaces, Words, Sentences, And Paragraphs

    Units

    On a web page, many units are available for us to adjust the font size of text. Understanding which unit to use is essential to setting the structure of a whole reading section. The reflowable nature of the web requires us to consider several aspects, such as the size of the viewport and the user’s reading preferences.

    For this reason, the most suitable choices are generally em and rem, which are font-specific units. For example, setting the margins between paragraphs using ems helps to preserve the vertical rhythm as the text size changes. However, this can be a problem when a serif font is alternated with a sans-serif within a section. In fact, at the same font size, fonts can appear optically very different. Traditionally, the height of the lowercase “x” character (the x-height) is the reference for determining the apparent size of a character.

    The comparison between the “d” and “x” glyphs of three different fonts at the same size reveal that their heights of the “x” (and therefore their optically size) are totally different
    At the same font size, characters will optically appear very different. (Large preview)

    Using the font-size-adjust rule, we can, therefore, optically render fonts of the same size, because the property will match the heights of the lowercase letters. Unfortunately, this property is currently available only in Firefox and in Chrome and Edge behind a flag, but it can be used as progressive enhancement using the @support check:

    @supports (font-size-adjust: 1;) {
        article {
            font-size-adjust: 0.5;
        }
    }
    

    It also helps with the swap from the fallback font to the one loaded remotely (for example, using Google Fonts).

    There are two articles. When switching the main font, the first article largely increase its length, since the font size is not adjusted to the x height, while the second one changes almost seamlessly
    The first example shows how switching the font works normally. In the second one, we are using font-size-adjust to make the swap more comfortable. (Large preview)

    Optimal Line Height

    We think typography is black and white. Typography is really white […] It is the space between the blacks that really makes it.

    — Massimo Vignelli, Helvetica, 2007

    Because typography is more a matter of “whites” than ”blacks”, when we apply this notion to the design of a website or web application, we must take into account special features such as line height, margins between paragraphs, and line breaks.

    Setting the font size by relying on the x-height helps with optimizing the line height. The default line height in browsers is 1.2 (a unitless value is relative to the font size), which is the optimal value for Times New Roman but not for other fonts. We must also consider that line spacing does not grow linearly with the font size and that it depends on various factors like the type of the text. By testing some common fonts for long-form reading, combined with sizes from 8 to 14 points, we were able to deduce that, on paper, the ratio between the x-height and the optimal line spacing is 37.6.

    A graph shows the relation between the ratio of x height and line height (y axis) and the ratio of x height and ascenders (x axis), with a downward trend from 38.1 to 35.8 fo the first ratio while increasing values of the x axis
    Acceptable line-spacing ranges. (Large preview)

    Compared to reading on paper, screen reading generally requires more spacing between lines. Therefore, we should adjust the ratio to 32 for digital environments. In CSS, this empirical value can be translated into the following rule:

    p {
        line-height: calc(1ex / 0.32);
    }
    

    In the right reading contexts, this rule sets an optimal line height for both serif and sans-serif fonts, even when typographical tools are not available or when a user has set a font that overwrites the one chosen by the designer.

    Define The Scale

    Now that we have adjusted the font size and used the ex unit to calculate the line height, we need to define the typographical scale in order to correctly set the spacing between paragraphs and to provide a good rhythm to the reading. As said before, line spacing does not grow linearly but varies according to the type of text. For titles with a large font size, for example, we should consider a higher ratio for the line height.

    article h1 {
        font-size: 2.5em;
        line-height: calc(1ex / 0.42);
        margin: calc(1ex / 0.42) 0;
    }
    
    article h2 {
        font-size: 2em;
        line-height: calc(1ex / 0.42);
        margin: calc(1ex / 0.42) 0;
    }
    
    article h3 {
        font-size: 1.75em;
        line-height: calc(1ex / 0.38);
        margin: calc(1ex / 0.38) 0;
    }
    
    article h4 {
        font-size: 1.5em;
        line-height: calc(1ex / 0.37);
        margin: calc(1ex / 0.37) 0;
    }
    
    article p {
        font-size: 1em;
        line-height: calc(1ex / 0.32);
        margin: calc(1ex / 0.32) 0;
    }
    

    Letter And Word Spacing

    When working on legibility, we must also consider readers who are challenged, such as those with dyslexia and learning disabilities. Developmental dyslexia affects reading, and discussion and research regarding the causes are still ongoing. It is important to make use of scientific studies to understand the effects that visual and typographic variables have on reading.

    For example, in a study that my company followed (“Testing Text Readability of Dyslexia-Friendly Fonts”), there was clear evidence that the glyph shapes of high-legibility fonts do not really assist reading, but wider spacing between characters (tracking) does. This finding was confirmed by another study on the effectiveness of increased spacing (“How the Visual Aspects Can Be Crucial in Reading Acquisition: The Intriguing Case of Crowding and Developmental Dyslexia”).

    These studies suggest that we should exploit the dynamism and responsiveness of web pages by offering more effective tools, such as controls for handling spacing. A common technique when enlarging the size of characters is to adjust the spacing between letters and words through CSS properties such as letter-spacing and word-spacing.

    See the Pen [Letter and word spacing](https://codepen.io/smashingmag/pen/KKVbOoE) by Edoardo Cavazza.

    See the Pen Letter and word spacing by Edoardo Cavazza.

    The problem with this is that letter-spacing acts unconditionally and breaks the tracking of the font, leading the page to render nonoptimal spaces.

    Alternatively, we can use variable fonts to gain more control over font rendering. Font designers can parameterize spacing in a variable and non-linear way, and can determine how the weight and shape of a glyph can better adapt to the habits of the reader. In the following example, using the Amstelvar font, we are able to increase the optical size as well as spacing and contrast, as intended by the designer.

    See the Pen [The optical size in variable fonts](https://codepen.io/smashingmag/pen/VweqoRM) by Edoardo Cavazza.

    See the Pen The optical size in variable fonts by Edoardo Cavazza.

    The Web.dev article “Introduction to Variable Fonts on the Web” has more detail on what variable fonts are and how to use them. And check out the Variable Fonts tool to see how they work.

    Width And Alignment

    To optimize reading flow, we also have to work on the width of the paragraph, which is the number of characters and spaces on a line. While reading, our eye focuses on about eight letters in a foveatio (i.e. the operation that is activated when we look at an object), and it is able to handle only a few consecutive repetitions. Therefore, line breaks are crucial: The moment of moving one’s focus from the end of a line to the beginning of the next is one of the most complex operations in reading and must be facilitated by keeping the right number of characters per type of text. For a basic paragraph, a common length is about 60 to 70 characters per line. This value can be easily set with the ch unit by assigning a width to the paragraph:

    p {
        width: 60ch;
        max-width: 100%;
    }
    

    Justification also plays an important role in reading across lines. Hyphenation support for languages is not always optimal in the various browsers; therefore, it must be checked. In any case, avoid justified text in the absence of hyphenation because the horizontal spacing that would be created would be an obstacle to reading.

    /* The browser correctly supports hyphenation */
    p[lang="en"] {
        text-align: justify;
        hyphens: auto;
    }
    
    /* The browser does NOT correctly support hyphenation */
    p[lang="it"] {
        text-align: left;
        hyphens: none;
    }
    

    Manual hyphenation can be used for languages that do not have native support. There are several algorithms (both server- and client-side) that can inject the &hyphen; entity within words, to instruct browsers where the token can be broken. This character would be invisible, unless it is located at the end of the line, whereupon it would render as a hyphen. To activate this behavior, we need to set the hyphens: manual CSS rule.

    Foreground Contrast

    The contrast of characters and words with the background is fundamental to legibility. The WCAG has defined guidelines and constraints for different standards (A, AA, AAA) governing the contrast between text and background. Contrast can be calculated with different tools, both for design and development environments. Keep in mind that automated validators are support tools and do not guarantee the same quality as a real test.

    By using CSS variables and a calc statement, we can dynamically calculate the color that offers the best contrast with the background. In this way, we can offer the user different types of contrast (sepia, light gray, night mode, etc.), by converting the whole theme according to the background color.

    article {
        --red: 230;
        --green: 230;
        --blue: 230;
        --aa-brightness: calc((
            (var(--red) * 299) +
            (var(--green) * 587) +
            (var(--blue) * 114)
        ) / 1000;
        --aa-brightness: calc((
        (var(--red) * 299) +
        (var(--green) * 587) +
        (var(--blue) * 114)
    ) / 1000);
    
    
        --aa-color: calc((var(--aa-brightness) - 128) * -1000);
        background: rgb(var(--red), var(--green), var(--blue));
        color: rgb(var(--aa-color), var(--aa-color), var(--aa-color));
    }
    

    See the Pen [Automatic text contrast](https://codepen.io/smashingmag/pen/zYrygyr) by Edoardo Cavazza.

    See the Pen Automatic text contrast by Edoardo Cavazza.

    In addition, with the introduction and cross-browser support of the prefer-color-scheme media query, it becomes even easier to manage the switch from light to dark theme, according to user preference.

    @media (prefers-color-scheme: dark) {
        article {
            --red: 30;
            --green: 30;
            --blue: 30;
        }
    }
    

    Going Forward

    Designing and developing for optimal reading require a lot of knowledge and the work of many professionals. The more this knowledge is spread across the team, the better off users will be. Below are some points to lead us to good results.

    For Designers

    • Consider semantic structure as being part of the project, rather than a technical detail;
    • Document layout and font metrics, especially the why’s and how’s of your choices. They will help developers to correctly implement the design;
    • Reduce typographic variables as much as possible (fewer families, styles, and variants).

    For Developers

    • Learn the principles of typography in order to understand the design decisions made and how to implement them;
    • Use units relative to font size to implement responsive layouts (paddings, margins, gaps) that scale to user preferences;
    • Avoid unrestrained manipulation of font metrics. Legibility might suffer when font constraints are not respected.

    For Teams

    • Read and understand the principles of the WCAG;
    • Consider inclusion and accessibility as part of the project (rather than separate issues).

    Reading is a complex activity. Despite the many resources on web typography and the academic papers that identify areas for improvements, there is no magical recipe for achieving good legibility. The number of variables to consider might seem overwhelming, but many of them are manageable.

    We can set the optimal line height of a paragraph using the ex unit, as well as set a paragraph’s width using the ch unit, in order to respect the user’s preferred browser settings for font size and family. We can use variable fonts to adjust the spacing between letters and words, and we can manipulate the stroke of glyphs to increase contrast, helping readers with visual impairments and dyslexia. We can even automatically adjust text contrast using CSS variables, giving the user their preferred theme.

    All of these help us to build a dynamic web page whose legibility is optimized according to the user’s needs and preferences. Finally, given that every little implementation or technological detail can make a huge difference, it is still essential to test users’ reading performance using the final artifact.

    Smashing Editorial
    (ra, yk, al, il)

    Source link

    web design

    CSS Transitions In Vuejs And Nuxtjs — Smashing Magazine

    07/10/2020

    About The Author

    Front-end developer based in Lagos, Nigeria. He enjoys converting designs into code and building things for the web.
    More about
    Timi

    Transitions are a nice way to remove, change, or update data in an application because their occurrence adds a nice effect and is good for the user experience. In this tutorial, we’ll look at the different ways to apply transitions in both Vue.js and Nuxt.js applications.

    Transitions are a module of CSS that lets you create gradual transitions between the values of specific CSS properties. The behavior of these transitions can be controlled by specifying their timing function, duration, and other attributes. Using these transitions in your applications and websites create a better visual experience and sometimes draws and holds the user’s attention while a piece of information is being introduced to or leaving the screen. According to Can I Use, transitions are supported by most browsers, although there are some minor issues with Internet Explorer and Safari.

    Data on support for the css-transitions feature across the major browsers from caniuse.com
    Source: caniuse.com. (Large preview)

    Vue.js is an open-source JavaScript framework for building client-facing web applications and single-page applications (SPA). One of the features of this framework is the ability to add transitions to an app seamlessly, to switch between either pages or components, and we’re going to look at how to do that in this tutorial.

    Nuxt.js is also a JavaScript framework, built on top of Vue.js (and often referred to as a framework of a framework), for building server-side web applications, static-generated websites, as well as SPAs. It works the same as Vue.js, so if you know Vue.js, you shouldn’t have many issues getting started with Nuxt.js. Nuxt.js comes with two properties for adding transitions to an app, and we’re going to cover those as well in this tutorial.

    This tutorial requires basic knowledge of either Vue.js or Nuxt.js. All of the code snippets in this tutorial can be found on GitHub.

    What Is A Transition?

    According to the Oxford Dictionary, a transition can be defined as:

    “A passage in a piece of writing that smoothly connects two topics or sections to each other.

    The process or a period of changing from one state or condition to another.”

    In terms of physics, a transition is defined thus:

    “A change of an atom, nucleus, electron, etc. from one quantum state to another, with emission or absorption of radiation.”

    From these definitions, we get an idea on what a transition is. The definitions all involve two different things or states. In terms of code, a transition is not so different.

    What Is A CSS Transition?

    According to Mozilla’s web documentation:

    “CSS Transitions is a module of CSS that lets you create gradual transitions between the values of specific CSS properties. The behavior of these transitions can be controlled by specifying their timing function, duration, and other attributes.”

    This means we can define a CSS transition as: the change in the CSS property of one or more elements from one value to another.

    The CSS transition property enables us to add a transition effect to any valid element. It consists of up to four other properties (five, if we count the transition property itself) that can be used individually or combined as a shorthand. Each property has a different function.

    transition-property

    The transition-property accepts the name of the CSS property that we want to watch out for changes on and whose change process we want to transition. It looks like this:

    .btn {
      width: 200px;
      height: 50px;
      transition-property: width;
      background-color: red;
      color: #fff;
      border: 0;
    }
    

    But this property does not do anything without the next property.

    transition-duration

    The transition-duration property specifies the time that the change of the element(s) in the transition-property should go on for. This property is required in order for the transition to work. If it is not set (with a value greater than 0s), then the default value of 0s would mean it would not run. So, let’s set a duration for this transition:

    .btn {
      width: 200px;
      transition-property: width;
      transition-duration: 2s;
      background-color: red;
      color: #fff;
      border: 0;
    }
    

    Here, we have an element with a class name of btn that has a width of 200px. We are using both the transition-property and the transition-duration properties here. What this means is, “Hey, CSS, watch out for when the width property changes, and when this happens, let the effect take 2s to change.”

    So, if we have a button with a class name of btn, then the index.html file would look like this:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>CSS Transitions</title>
        <link rel="stylesheet" href="./assets/styles.css">
    </head>
    <body>
        <Section>
            <h1>Hi CSS Transitions</h1>
            <button class="btn">Hover on me to see a cool transition</button>
        </Section>
    </body>
    </html>
    

    Here, we have an HTML file that contains a button with a class that has transition-property and transition-duration properties watching out for changes to the element’s width.

    One thing to note is that, in order for the transition on our button to work, we have to actually change the width of that element, either by manually adjusting the width with the developer tools in the browser, by using one of the CSS pseudo-classes, or by using JavaScript. For the purpose of this tutorial, we’re going to use the CSS pseudo-class :hover to change the width of the button:

    // existing styles
    .btn:hover {
      width: 300px;
    }

    Now, if we hover over this button, we should see the width of the button gradually increase over the set time, which is 2s.

    See the Pen transition-property and transition-duration by Timi Omoyeni (@timibadass)
    on CodePen.

    transition-timing-function

    The transition-timing-function property determines the speed at which the transition effect occurs. Five values are available for this property:

    • ease
      This (the default) specifies a transition effect that starts slowly, then gets fast, then ends slowly.
    • linear
      This specifies a transition effect with the same speed from start to end.
    • ease-in
      This specifies a transition effect with a slow start.
    • ease-out
      This specifies a transition effect with a slow end.
    • ease-in-out
      This specifies a transition effect with a slow start and end.
    • cubic-bezier(n,n,n,n)
      This lets you define your own values in a cubic-bezier function.

    So, if we add ease-in to our button, we should notice the speed at which the width and height change, compared to the speed at which the button returns to its normal state. Here is our updated styles.css sheet:

    .btn {
      width: 200px;
      height: 50px;
      transition-property: width;
      transition-duration: 2s;
      transition-timing-function: ease-in;
      background-color: red;
      color: #fff;
      border: 0;
    }
    

    If we want a more dramatic speed effect or the freedom to set a specific speed effect, we can use cubic-bezier(n,n,n,n):

    btn {
        width: 200px;
        height: 50px;
        transition-property: width;
        transition-duration: 2s;
        transition-timing-function: cubic-bezier(0.075, 0.82, 0.165, 1);
        background-color: red;
        color: #fff;
        border: 0;
    }
    

    See the Pen transition-timing-function by Timi Omoyeni (@timibadass)
    on CodePen.

    One interesting thing about this value is that you can edit it directly in the browser using the developer tools.

    cubic bezier in dev tools.
    Cubic bezier in the browser’s developer tools. (Large preview)

    If you click on the highlighted part of your developer tools, you’ll get an interface to modify the cubic-bezier options:

    cubic bezier interface highlighted in yellow.
    Cubic bezier interface highlighted in yellow. (Large preview)

    As you move the two points around, the values of (n,n,n,n) change, and you’ll see a representation (highlighted in red) of how the speed effect will appear. This can be very useful when you have a specific speed effect in mind.

    transition-delay

    The transition-delay property sets how long (in seconds) the transition has to wait before its effect starts to occur. This time is different from the transition-duration property, which specifies how long the transition effect will take place.

    .btn {
      width: 200px;
      height: 50px;
      transition-property: width;
      transition-duration: 2s;
      transition-timing-function: cubic-bezier(0.075, 0.82, 0.165, 1);
      transition-delay: 5s;
      background-color: red;
      color: #fff;
      border: 0;
    }
    

    If you try this in the browser, you will notice a delay before the element’s width starts to change. This is because of the transition-delay property and value that we have set.

    See the Pen transition-delay by Timi Omoyeni (@timibadass)
    on CodePen.

    Shorthand Property

    The individual transition properties can be tedious to use. For this reason, we have the shorthand property: transition. It accepts all of the properties in a defined order:

    {
      transition: a b c d;
    }

    Here, the letters correspond as follows:

    • a: transition-property
    • b: transition-duration
    • c: transition-timing-function
    • d: transition-delay

    We can refactor our existing transition to work using this shorthand property:

    // from
    .btn {
      width: 200px;
      height: 50px;
      transition-property: width;
      transition-duration: 2s;
      transition-timing-function: cubic-bezier(0.075, 0.82, 0.165, 1);
      transition-delay: 1s;
      background-color: red;
      color: #fff;
      border: 0;
    }
    
    // to
    .btn {
      width: 200px;
      height: 50px;
      transition: width 2s cubic-bezier(0.075, 0.82, 0.165, 1) 1s;
      background-color: red;
      color: #fff;
      border: 0;
    }
    

    If we try this code in the browser, we will get the same transition effect that we got when we used the individual properties.

    See the Pen using shorthand property by Timi Omoyeni (@timibadass)
    on CodePen.

    Transitions In Vue.js

    Vue.js comes with two different ways to add transitions to an application. This doesn’t mean we cannot use transitions the CSS way. It just means that Vue.js’ developers have built on top of CSS to make it easier to use transitions. Let’s look at them one by one.

    Transitioning Individual Elements and Components

    One way we can use transitions in Vue.js is by wrapping the transition component around an element or component, using any of the following:

    • conditional rendering ( using v-if),
    • conditional display (using v-show),
    • dynamic components,
    • component root nodes.

    When we are developing an application, there are instances when we want to display data to the user depending on a value (such as a boolean). Here’s an example of how this works, taken from the index.vue file:

    <template>
      <div>
        <p v-if="show">Now you see me!</p>
        <p v-else>Now you don't!</p>
        <button @click="show = !show">click me</button>
      </div>
    </template>
    <script>
    export default {
     data() {
       return {
         show: true
       }
     }
    }
    </script>
    <style>
    </style>
    

    We have added two paragraphs to this page that appear depending on the value of show. We also have a button that changes the value of show when clicked. We’ll add this page to our App.vue file by importing it like so:

    <template>
      <div id="app">
        <Index />
      </div>
    </template>
    <script>
    import Index from "./components/index.vue";
    export default {
      name: 'App',
      components: {
        Index
      }
    }
    </script>
    

    If we open the browser, we should see our paragraph and button:

    vue landing page when show is true.
    Vue.js landing page in default state. (Large preview)

    Right now, clicking the button changes only the value of show, which causes the visible text to change:

    landing page when show is false.
    Landing page when button is clicked. (Large preview)

    Adding a transition to this paragraph can be done by wrapping both paragraphs in the transition component. This component accepts a name prop, which is very important for the transition to work.

    <template>
      <div>
        <transition name="fade">
          <p v-if="show">Now you see me!</p>
          <p v-else>Now you don't!</p>
        </transition>
        <button @click="show = !show">click me</button>
      </div>
    </template>
    

    This name tells Vue.js which transition to apply to the elements or components inside this transition component. At this point, if we click on the button, we would still not notice any transition because we have yet to add the configuration for our transition in the form of CSS classes.

    One thing to note is that, when using a transition between two elements of the same tag, we need to specify a key attribute on each element in order for the transition to occur.

    <template>
      <div>
        <transition name="fade">
          <p v-if="show" key="visible">Now you see me!</p>
          <p v-else key="notVisible">Now you don't!</p>
        </transition>
        <button @click="show = !show">click me</button>
      </div>
    </template>
    

    Vue.js has six transition classes that are applied to the elements or components inside the transition component, and each of these classes represents a state in the transition process. We’re going to look at only a few of them.

    v-enter

    The v-enter class represents the “starting state for enter”. This is the point at which a condition (v-if or v-else) has been met and the element is about to be made visible. At this point, the class has been added to the element, and it is removed once the element has been added. The name prop (in this case, fade) attached to the transition component is prefixed to this class name, but without the v. This v can be used by default if name is not provided. Thus, we can add this class to our index.vue file:

    <style>
    p {
      color: green;
    }
    
    .fade-enter{
      color: red;
      transform: translateY(20px);
    }
    </style>
    

    First, we add a color of green to all of the paragraphs on the page. Then, we add our first transition class, fade-name. Inside this class, we change the color to red, and we make use of the transform and translateY property to move the paragraph by 20px along the y-axis (vertically). If we try clicking on the button again, we will notice that very little or no transition takes place during the switch because we need to add this next class we’ll look at.

    v-enter-active

    The v-enter-active class represents the “whole entering” state of a transitioning element. It means that this class is added just before the element is inserted or becomes visible, and it is removed when the transition has ended. This class is important for v-enter to work because it can be used to add the CSS transition property to the class, together with its properties (transition-property, transition-duration, transition-timing-function, and transition-delay), some of which are needed in order for the transition effect to work. Let’s add this class to our app and see what happens:

    .fade-enter-active {
      transition: transform .3s cubic-bezier(1.0, 0.5, 0.8, 1.0), color .5s cubic-bezier(1.0, 0.5, 0.8, 1.0);
    }
    

    See the Pen vue transition enter state by Timi Omoyeni (@timibadass)
    on CodePen.

    Now, if we click on the button, we will notice the transition of the color and the position of each of the texts as they come into view. But the transition from visible to hidden isn’t smooth enough because no transition is happening.

    v-leave-active

    The v-leave-active class represents the entire state in which an element changes from visible to hidden. This means that this class is applied from the moment an element starts to leave the page, and it is removed once the transition ends. This class is important in order for a leave transition to be applied because it takes in the CSS transition property, which also takes in other transition properties. Let’s add this to our app and see what happens:

    .fade-leave-active {
      transition: transform 1s cubic-bezier(1.0, 0.5, 0.8, 1.0), color 1s cubic-bezier(1.0, 0.5, 0.8, 1.0);
    }
    

    See the Pen vue transition leave active state by Timi Omoyeni (@timibadass)
    on CodePen.

    When we click on the button now, we will notice that the element that should leave waits for approximately 2 seconds before disappearing. This is because Vue.js is expecting the next class with this transition to be added in order for it to take effect.

    v-leave-to

    The v-leave-to transition represents the “leaving” state, meaning the point at which an element starts to leave and its transition is triggered. It is added one frame after a leaving transition is triggered, and removed when the transition or animation finishes. Let’s add this class to our app and see what happens:

    .fade-leave-to {
      transform: translateX(100px);
      color: cyan;
    }
    

    Clicking on the button, we will notice that each element that is leaving is sliding to the right as the color changes.

    See the Pen vue transition leave to state by Timi Omoyeni (@timibadass)
    on CodePen.

    Now that we understand how transitions work in Vue.js, here’s an image that brings it all together:

    classification of vue transition classes
    Classification of Vue.js transition classes. (Large preview)

    Finally, notice the not-so-smooth transition that occurs during the enter and leave states of elements that are transitioning. This is because Vue.js’ transitions occur simultaneously. Vue.js has a mode prop that helps us achieve a very smooth transition process. This prop accepts one of the following values:

    • in-out
      The new element transitions in first, and then, when it’s complete, the current element transitions out.
    • out-in
      The current element transitions out first, and then, when complete, the new element transitions in.

    If we add this mode to our index.vue file and try again, we should see a better transition:

    <template>
      <div>
        <transition name="fade" appear mode="out-in">
          <p v-if="show" key="visible">Now you see me!</p>
          <p v-else key="notVisible">Now you don't!</p>
        </transition>
        <button @click="transitionMe">click me</button>
      </div>
    </template>
    

    Now, if we click on the button, we will notice that one element leaves before another enters. This is a result of the mode we have selected for this transition. If we try the other mode, we will get a different behavior.

    See the Pen vue transition with mode by Timi Omoyeni (@timibadass)
    on CodePen.

    List Transitions

    If you ever try adding transitions to more than one element at a time using the transition component, an error will get printed to the console:

    Vue transition error printed in console.
    Vue.js error printed in console. (Large preview)

    This is because the transition component is not meant to render more than one element at a time. If we want to transition two or more elements at a time or render a list (using v-for), we make use of the transition-group component. This component also accepts a name prop, but it has some differences from the transition component, including the following:

    • A key attribute is required for each element inside this component.
    • There is no need for the mode prop because more than one element would be rendered at a time.
    • A span element is rendered by default, but it can be modified by specifying a tag prop when defining the transition-group component. Let’s look at an example (in our listTransition.vue file):
        <template>
      <div>
        <h1>List/Group Transition</h1>
        <ul>
          <li v-for="user in users" :key="user.id">
            {{user.name}}
            <button>Remove</button>
          </li>
        </ul>
      </div>
    </template>
    <script>
    export default {
      data() {
        return {
          users: [
            {
              name: "Vuejs",
              id: 1
            },
            {
              name: "Vuex",
              id: 2
            },
            {
              name: "Router",
              id: 3
            }
          ]
        };
      }
    };
    </script>
    <style>
    </style>
    

    Here, we have an array of users, which we loop through using v-for, displaying the name in our template section. In order to be able to view this list, we need to import this component into the App.vue page:

    <template>
      <div id="app">
        <Index />
        <listTransition />
      </div>
    </template>
    <script>
    import Index from "./components/index.vue";
    import listTransition from "./components/listTransition.vue";
    export default {
      name: "App",
      components: {
        Index,
        listTransition
      }
    };
    </script>
    

    Note that when using the transition-group component, instead of wrapping our list with a ul tag (or any tag we have in mind), we wrap it around the transition-group component and add the tag to the tag prop, like this:

    <template>
      <div>
        <h1>List/Group Transition</h1>
        <transition-group name="slide-fade" tag='ul'>
          <li v-for="user in users" :key="user.id">
            {{user.name}}
            <button>Remove</button>
          </li>
        </transition-group>
      </div>
    </template>
    <script>
    export default {
      data() {
        return {
          users: [
            {
              name: "Vuejs",
              id: 1
            },
            {
              name: "Vuex",
              id: 2
            },
            {
              name: "Router",
              id: 3
            }
          ]
        };
      }
    };
    </script>
    <style>
    .slide-fade-enter-active {
      transition: transform 0.3s cubic-bezier(1, 0.5, 0.8, 1),
        color 0.5s cubic-bezier(1, 0.5, 0.8, 1);
    }
    .slide-fade-leave-active {
      transition: transform 1s cubic-bezier(1, 0.5, 0.8, 1),
        color 1s cubic-bezier(1, 0.5, 0.8, 1);
    }
    .slide-fade-enter {
      color: mediumblue;
      transform: translateY(20px);
    }
    .slide-fade-leave-to {
      transform: translateX(100px);
      color: cyan;
    }
    </style>
    

    Here, we have replaced the ul tag with the transition-group component, and added the ul as the tag prop value. If we inspect the updated page in the developer tools, we will see that the list is being wrapped in the element that we specified in the tag prop (that is, ul).

    ul tag highlighted in dev tools.
    The ul tag highlighted in the developer tools. (Large preview)

    We have also added a transition name prop with a value of slide-fade to this component, with style rules below in the style section that follow this naming convention. For this to work, we need to add the following lines of code to our file:

    <template>
      <div>
        <h1>List/Group Transition</h1>
        <transition-group name="slide-fade" tag="ul">
          <li v-for="user in users" :key="user.id">
            {{user.name}}
            <button @click="removeUser(user.id)">Remove</button>
          </li>
        </transition-group>
      </div>
    </template>
    <script>
    export default {
      // ...
      methods: {
        removeUser(id) {
          let users = this.users.filter(user => user.id !== id);
          this.users = users;
        }
      }
    };
    </script>
      

    In the template section, we add a click event to each button in the loop and pass the user.id to the removeUser method attached to this click event. We then create this function in the script section of our file. This function accepts an id as argument. Then, we run through our existing users and filter out the user with the id passed into this function. When this is done, we save our new array of users to the data of our page.

    At this point, if you click on any of the buttons for the users, a transition effect will be applied as the user is being removed from the list.

    See the Pen Vue list transition by Timi Omoyeni (@timibadass)
    on CodePen.

    Transitions In Nuxt.js:

    Adding transitions to a Nuxt.js application is quite different from how you might be used to in Vue.js. In Nuxt.js, the transition component is automatically added to the application for you. All you need to do is one of the following.

    Add It to Individual Page Component

    Nuxt.js allows us to add transitions to an individual page component seamlessly. This transition is applied while the user is navigating to this page. All we have to do is add a transition property to the script section of the component. This property can be a string, a function, or an object. Some of the properties it accepts are:

    Like Vue.js, Nuxt.js has a default name that gets assigned to a transition class if no name is provided, and it is called page. Let’s see how this works when we add it to our application in transition.vue:

    <template>
      <div>
        <p>
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Labore libero
          odio, asperiores debitis harum ipsa neque cum nulla incidunt explicabo ut
          eaque placeat qui, quis voluptas. Aut necessitatibus aliquam veritatis.
        </p>
        <nuxt-link to="/">home</nuxt-link>
      </div>
    </template>
    <script>
    export default {
      transition: {
        name: "fade",
        mode: "out-in"
      },
      data() {
        return {
          show: true
        };
      }
    };
    </script>
    <style>
    p {
      color: green;
    }
    .fade-enter-active {
      transition: transform 0.3s cubic-bezier(1, 0.5, 0.8, 1),
        color 0.5s cubic-bezier(1, 0.5, 0.8, 1);
    }
    .fade-leave-active {
      transition: transform 1s cubic-bezier(1, 0.5, 0.8, 1),
        color 1s cubic-bezier(1, 0.5, 0.8, 1);
    }
    .fade-enter {
      color: mediumblue;
      transform: translateY(20px);
    }
    .fade-leave-to {
      transform: translateX(100px);
      color: cyan;
    }
    </style>
    

    On this page, we’ve displayed “lorem ipsum” in the template section. We’ve also added the transition property, to which we have passed an object whose name is set to fade and whose mode is set to out-in. Finally, in the style section, we’ve added some styles that control the transition as the user navigates between this page and another.

    In order for this transition to work, we have to navigate to /transition, but we would not notice any transition if we manually enter this route in our browser. So, let’s add a link to this page on the index.vue page.

    <template>
      <div class="container">
        <div>
          // ..
          <nuxt-link to="/transition">next page</nuxt-link>
        </div>
      </div>
    </template>
    

    Now, if we click the link on either of the two pages, we will notice a sliding transition as the browser moves to and from the /transition route.

    pageTransition

    Adding transitions to individual pages can be challenging if we want to add them to all of the pages in the application. That’s where pageTransition comes in. This property allows us to add a general configuration for all of our pages in the nuxt.config.js file. This property accepts both a string and object as an option. Let’s see how that works in nuxt.config.js:

    export default {
        // ...
        /*
         ** Global CSS
         */
        css: [
            '~/assets/transition.css'
        ],
        pageTransition: {
            name: "fade",
            mode: "out-in"
        },
    }
    

    Here, we’ve added the link to a CSS file, which we’ll create shortly. We have also added the pageTransition property to the file, along with with its configuration. Now, let’s create our CSS file, transition.css, and add the following styles to it:

    .fade-enter-active {
        transition: transform 0.3s cubic-bezier(1, 0.5, 0.8, 1), color 0.5s cubic-bezier(1, 0.5, 0.8, 1);
    }
    .fade-leave-active {
        transition: transform 1s cubic-bezier(1, 1, 1, 1), color 1s cubic-bezier(1, 0.5, 0.8, 1);
    }
    .fade-enter {
        color: mediumblue;
        transform: translateY(20px);
    }
    .fade-leave-to {
        transform: translate3d(-500px, -300px 400px);
        color: cyan;
    }
    

    We’ve added the classes and styles that will be applied to the transition between one route and the other. If we get rid of the transition configuration from the transition.vue page and try to navigate between the two pages, we will get a transition effect.

    layoutTransition

    The layoutTransition property enables us to apply transitions based on the layout that the page is on. It works the same way as pageTranslation, except that it works based on layout. The default transition name is layout. Here’s an example of how it works, in nuxt.config.js:

    export default {
        // ...
        /*
         ** Global CSS
         */
        css: [
            '~/assets/transition.css'
        ],
        layoutTransition: {
            name: "fade",
            mode: "out-in"
        },
    }
    

    Note that fade has to be the name of the layout in order for the transition to work with its layout. Let’s create this new layout in newLayout.vue to see what I mean:

    <template>
      <!-- Your template -->
      <div>
        <h1>new layout</h1>
      </div>
    </template>
    <script>
    export default {
      layout: "blog"
      // page component definitions
    };
    </script>
    

    Conclusion

    We have learned about CSS transitions and how to create them using the transition properties individually (transition-property, transition-duration, transition-timing-function, and transition-delay) and using the shorthand transition property. We have also covered how to apply these transitions in both Vue.js and Nuxt.js. But that’s not all. Vue.js has more ways for us to apply transitions in an application:

    Smashing Editorial
    (ks, ra, yk, il)

    Source link