4 common CSS problems and how to solve them | Heart Internet Blog – Focusing on all aspects of the web

That moment has come. You’re posed with another task that you have to figure out with CSS. You’re probably doing all kinds of searches to find the right solution, but you’re coming up empty. Surely someone has found a solution to the same issue. Well, I can’t promise to solve all your problems, but I can provide some insights on some common problems and ways to solve them.

One of the things I really love about CSS is that there are so many ways to do similar things. It’s a medium that begs for experimentation, and if that is your jam, you’ll have plenty to explore. I get it, though, sometimes you’re just looking for the solution to the problem you have right now. Here are four common problems, and solutions, that I have seen developers struggle with over the years.

Push the footer to the bottom of the screen

 

Have you ever worked on a templated website that has that one page without much content on it? Normally there is more than enough content to push the footer offscreen or at least close enough, but not that one page. Past solutions to this problem have required a fixed height footer, which is rarely a good solution. Thanks to Flexbox, there is a simple solution to this age-old pursuit.

This example will use a simple layout, but the same concepts can be used for a more complex layout as well. The main elements we will work with are a <div> and the <footer>. The <div> in this example can house our <header> and <main> elements, or whatever else is necessary for everything except the footer.

<body>
  <div class="site-content">...</div>
  <footer class="site-footer">...</footer>
</body>

The CSS for this starts with setting the <html> and <body> elements to have a height of 100%. By default the <html> and <body> tags are only as tall as their content. This can be deceptive since setting a background on either of these elements will fill the screen, but that is their special feature over all other elements.

html,
body {
  height: 100%;
}

The next step is to change the display property on the <body> from the default block to flex. Then, we need to revert the layout back to a stacked format using flex-direction: column, which will return our .site-content to being above .site-footer instead of side-by-side. For good measure, we can also add in margin: 0 to remove the default margin from <body>, since we’ll likely want our footer and other containers to go to the edge of the screen.

body {
  margin: 0;
  display: flex;
  flex-direction: column;
}

The last bit of CSS is the key part. Add flex-grow: 2 to the .site-content element. This single property will force the <footer> to the bottom of the page by telling .site-content to take up as much space that is available after the other elements on the page take up their space.

.site-content {
  flex-grow: 2;
}

That’s all that is needed to make a forced-to-bottom footer work. Now, regardless of the screen height, the footer will appear at the bottom of the page if there isn’t enough content to push it off screen.

Create a fixed background image for mobile/touch devices

Mobile devices have had a complicated history with CSS fixed position properties. At first there was no support whatsoever, and position: fixed was treated like position: absolute. Today that largely works as expected, with a few caveats around zooming. The remaining outlier is background-attachment: fixed, which is the property that allows a background image to have a fixed position. It’s really useful for a simple parallax effect, or to give the appearance that the content is floating over the background.

This property is, for all intents and purposes, unsupported. There are characteristics of the property that are implemented in different ways in different browsers. This results in unintended and unexpected behaviour with undesirable results. The solution is to instead lean on the working aspect of fixed position in mobile browsers, position: fixed. This won’t provide all the benefits of a fixed background, as you will be limited to a single fixed background, whereas on a desktop browser you can have several without any issues.

To start, we need to create a container for our background image. Since this will be a background image for the whole page, we can use a pseudo-element on the page’s body element.

body::before {
}

Since the ::before pseudo-elements require a content property to display, we will set this to an empty string content: '';. Next, since a pseudo-element’s default display type is inline, we’ll need to set display: block. Now we have a container for our background image to live in, but it doesn’t have any dimensions yet. We need to give this container a height and width that will fill the whole screen. This is where viewport units come in very handy. The height will be set to 100vh, which means 100% of the viewport height. Then the width will be set to 100vw, or 100% of the viewport width.

body::before {
  content: '';
  display: block;
  height: 100vh;
  width: 100vw;
}

At this point, feel free to add your desired background image to the selector block. If there is any content on your page, it will be pushed down a full screen height by this new pseudo-element. That’s because the element still has the default position: static applied. We can now add in position: fixed. Since mobile browsers have implemented this to consistent degree, we can rely on it to create our fixed background. To bypass any margin or padding that our body element may have, we need to add top: 0; and left: 0; to force the pseudo-element to the top left corner and expand outward.

body::before {
  content: '';
  display: block;
  height: 100vh;
  width: 100vw;
  background: url(...);
  position: fixed;
  top: 0;
  left: 0;
}

At first glance this may seem like we are done but there is one final and crucial step. With position: fixed applied to this pseudo-element, the background will be on top of the content. The final property needed is a z-index: -1. This will pull the pseudo-element behind the body element, and therefore the content. The one thing to be aware of is if the body element has a background, either image or colour, the pseudo-element will be behind and hidden by that background.

If your code requires a background image or colour on the body element, there is a solution to address the hidden pseudo-element. Instead of setting z-index: -1 on the pseudo-element, we need to create a wrapper element that holds all the content of the body. For our purposes, we’ll create a div with a class of site-content. This .site-content element only needs two properties to pull it above the fixed pseudo-element, position: relative and a z-index with a value larger than 0.

.site-content {
  position: relative;
  z-index: 1;
} 

This approach allows for a unique experience on mobile and touch devices, and continues to work on traditional desktop browsers as well. Keep in mind that this is a device-specific solution and not an aspect of screen width. To see this in action, test on actual devices.

Style a custom checkbox

Form elements are a rather unique aspect of the web. They are the most complex elements when it comes to state, and they are the primary way to interact with a website. Even when form elements aren’t used, JavaScript is emulating them through the same APIs. The most unusual aspect of these elements is that when certain values are present, they are not fully accessible via CSS. Often times browsers will use the operating system default styles to display these elements. This is all especially true for checkboxes and radio buttons.

There are several approaches that can be made to creating custom checkboxes and radio buttons. I prefer approaches that require the least amount of HTML elements and follow accessibility best practices. Because of the complex nature these form elements have in regard to state, we have several scenarios we need to account for. To start, our HTML will be fairly standard, yet the format is essential to the CSS solution. Our input field must come before our label element, even if visually we wish for it to appear after the label text. To make sure this approach is screen reader accessible, the input must have an id and the label must have a for attribute with the same value of the id attached to the input.

<input type="checkbox" id="checkbox-1"/>
<label for="checkbox-1">...</label>

This solution relies on two special selector types provided by CSS. The first is the attribute selector. This is where an element can be found by the HTML attribute it has as opposed to a class, id, or element. This is accomplished by wrapping the attribute name in square brackets. In our case we will be targeting the type attribute found in our input field. What’s more is that attributes have numerous options to select an element based on the value of that attribute. In our case we specifically want to select elements that have a type value of checkbox. We can select these very specific elements with a selector of [type="checkbox"]. The second selector type we will be utilising is the adjacent sibling combinator. This allows us to affect the styles of the label based on the state of the checkbox input. The combinator is as simple as putting the selector of the first element, followed by a plus sign (+), and then the second element selector which will be styled.

To start, we need to hide the checkbox. Since this is an element that needs to be accessible for screen reader users, we can’t simply use display: none;, nor visibility: hidden;. Instead we need to use a group of properties that completely hides an element visually but retains the accessibility features of the property, which are necessary. There are several other takes on what properties and values to use, but this is the approach I like to take. I won’t get into much detail, but essentially the intent with this CSS is to create an element that is moved off screen that doesn’t affect scroll, nor does it have the possibility of accidentally showing up on the page should its contents change.

[type="checkbox"] {
  position: fixed;
  bottom: 100%;
  right: 100%;
  clip: rect(0 0 0 0); 
  border: 0;
  width: 1px; 
  height: 1px; 
  margin: -1px; 
  overflow: hidden; 
  padding: 0;
}

Often this group of properties is assigned a generic class, something like .visually-hidden, and can then be applied to any element that needs to exist for the purposes of aiding accessibility, but are not necessary to navigate a page visually.

Now that the checkbox is hidden, we need to create our own. Since a properly formatted label acts as a trigger for selecting and deselecting a checkbox, we can rely on that relationship to create a faux checkbox. This relationship is created by using a unique id attribute on the checkbox input and referencing that same id value on the for attribute on the label element. To create our checkbox, we will create a pseudo-element on our label. We’ll also use an adjacent sibling combinator along with the attribute selector for the checkbox, to ensure a generic yet specific scoping for our style. It’s important to note this approach is using an em unit for the size and position of the pseudo-elements. Using an em unit for the faux checkbox allows it to be sized in proportion to the label element’s font-size value.

[type="checkbox"] + label::before {
  content: '';
  display: inline-block;
  height: 1.125em;
  width: 1.125em;
  border: black 1px solid;
  box-sizing: border-box;
}

In order for the pseudo-element to be generated, we must provide a content property, but since we don’t want any content, the value can be an empty string. The remaining properties give us a simple box with a black border to the left of the label. The faux checkbox and label put right up to each other and are off centre. We can utilise Flexbox on the label to correct the alignment of these elements, and then a margin-right on the pseudo-element to provide space between the elements.

[type="checkbox"] + label {
  display: flex;
}

[type="checkbox"] + label::before {
  ...
  margin-right: 0.5rem;
}

Now that we have simple and well-aligned faux checkbox, it’s time to add a check! Right now, when you click on the checkbox or label, nothing happens. We need to create a check to display in the box, and we can do this a couple of ways. The easiest way to accomplish this is with a background image of a checkmark applied to the faux checkbox pseudo-element. This allows for the checkbox to have any shape or style desired. Likewise, this could potentially be accomplished by placing an image in the pseudo-element’s content. However, I love the look of a CSS-generated checkmark, it’s simple and versatile.

The great thing about pseudo-elements is that each DOM element has up to two. Since we used the ::before pseudo-element to create the box, we’ll now use the ::after pseudo-element to make our check. The CSS for the checkmark will also introduce a checked pseudo-class selector. This selector allows us to style based on the checked state of the checkbox. We’ll still need a similar selector structure as the ::before pseudo element with content and display properties.

[type="checkbox"]:checked + label::after {
  content: '';
  display: block;
}

To create the actual checkmark, we will add a green border to the right and bottom sides of the pseudo-element and then rotate it to look like a checkmark. We’ll also need to give the pseudo-element a height and width, since a checkmark tends to look like a backwards capital L, the height should be taller than the width.

[type="checkbox"]:checked + label::after {
  ...
  height: 0.625em;
  width: 0.25em;
  border-right: 0.125em green solid;
  border-bottom: 0.125em green solid;
  transform: rotate(45deg);
}

At this point we have successfully created a decent looking checkmark. However, the checkmark still needs to be moved to inside the checkbox. This will require using position: absolute as we need to have the two pseudo-elements overlapping. Additionally, since we will be working with positioning, we need to add position: relative to our label in order to reset the positioning coordinates to the label element.

[type="checkbox"] + label {
  ...
  position: relative;
}

Once the position: relative is in place, we accurately position the checkmark. It’s important to keep in mind when the browser renders the check mark, the positioning properties — top and left — are applied to the pseudo-element before the transform property. The result of this is some guess work and tweaking the top and left properties until the checkmark is visually aligned to the centre of the container.

[type="checkbox"]:checked + label::after {
  ...
  position: absolute;
  top: 0.125em;
  left: 0.375em;
}

The final, yet no less essential, part of creating a custom checkbox, is to provide a focus state. When a keyboard user tabs through a series of checkboxes, the checkbox input element is what highlights. Since we have visually hidden the checkbox, we need to add back in this very important accessibility feature. Just as we were able to use the checked pseudo-class selector, we can use the focus pseudo-class to detect when the checkbox has keyboard focus. There are several ways we can style a focus state, the simplest is to use an outline. My own preference is to use a box-shadow to create a focus state with high-contrast that brings extra attention to the focused element.

[type="checkbox"]:focus + label::before {
  box-shadow:
    0 0 0 2px white,
    0 0 0 4px dodgerblue; 
}

Now we have a custom checkbox. The same principles can be used to create a more scoped variation by using classes instead of attribute selectors or element selectors. The adjacent sibling combinator selector is the crux of this approach, so that must be maintained. The same approach, including the checked pseudo-class, can be used to create custom radio buttons as well.

Keep track of z-index

Are you tired of not remembering the highest z-index value on your site? Do you struggle with knowing what z-index value to use on a random element? Keeping track of z-index values can be frustrating and result in a random assortment of values with no connection to one another. I learned this trick from a co-worker and have taken it to heart. Every time I write a z-index value I know what level to set.

The key is using 100 value increments for each major use of z-index. This provides a space of 99 values that can fit in between each major increment if necessary. The default z-index is 0, so we can assume a positioned element can maintain the default. The z-index values are there if we need to correct the way elements overlap. The first z-index element should have a value of 100, the next element that needs to display above an element should get 200. In my own use it is rare to go as high as 10 increments, so my highest z-index is typically 1000.

To make this even easier, there are variables in a preprocessor tool like Sass or CSS custom properties for new browsers. The Sass version of this applies the 100 value increment to a variable name that can be easily understood.

$z1: 100;
$z2: 200;
$z3: 300;
$z4: 400;
$z5: 500;
$z6: 600;
$z7: 700;
$z8: 800;
$z9: 900;
$z10: 1000;

Then, when using the Sass variable, assigning the appropriate value is done by using a variable instead:

.demo-class {
  z-index: $z2;
}

What is really great about utilising variables like this is we can quickly use the values between our major increments with math functions. If we have an element that is closely related to .demo-class but it needs to be just behind the element in the z-index order, we don’t need to use $z1, instead we can subtract 1 from $z2.

.related-demo-class {
  z-index: $z2 - 1;
}

A similar approach can be done using the latest CSS with custom properties. These are essentially variables and we can set up the exact same system with a different syntax. Since custom property values must be set within a selector, unlike Sass variables, we need to set our values on a high level element, so they are accessible to all children elements. The top most elements are the HTML and body tags, however there is also the root pseudo-class, which has a higher specificity than the HTML element selector.

:root {
  --z1: 100;
  --z2: 200;
  --z3: 300;
  --z4: 400;
  --z5: 500;
  --z6: 600;
  --z7: 700;
  --z8: 800;
  --z9: 900;
  --z10: 1000;
}

These values are then accessed by calling them with the CSS var function.

.demo-class {
  z-index: var(--z2);
}

Likewise we can do the math with the calc CSS function to create a class that is related to .demo-class, so the z-index only needs to be one less.

.related-demo-class {
  z-index: calc(var(--z2) - 1);
}

The next time you’re tempted to just keep adding 9’s to a z-index, consider this approach for tracking your z-index values. What’s more, by using variables to define your values, a custom increment can be set. Never be uncertain about your z-index values again.

What’s next

I hope that these demos give you a start on figuring out the best solution to your specific problem. Remember CSS is a large collection of properties and values. Rarely does one property solve a problem. To solve problems with CSS, it requires understanding that these properties work together as a collective unit to create dynamic and creative solutions. Hopefully, you’re able to take the ideas behind these solutions to come up with a new way to combine other properties and solve a whole other problem. I have found that experimentation is the key to understanding and problem solving with CSS. Take what you’ve learned and mash it together to see what happens. Happy coding!

Comments

Please remember that all comments are moderated and any links you paste in your comment will remain as plain text. If your comment looks like spam it will be deleted. We're looking forward to answering your questions and hearing your comments and opinions!

Got a question? Explore our Support Database. Start a live chat*.
Or log in to raise a ticket for support.
*Please note: you will need to accept cookies to see and use our live chat service