Styling the good old button element

A button is one of those elements that are most often used on web pages. The tag <button></button>can be used to start a process like data output, opening a modal window, submitting a form, and so on. In the material, the translation of which we publish, we will talk about the subtleties of styling the element buttonand how to design the buttons so that they work well in any browser. In addition, most commonly used button styles will be covered here. We will talk here about some of the difficulties that arise when working with buttons.



Styles applied to default buttons


Maybe talking about “standard styles” will seem to someone to be a discussion of some elementary things, but, in fact, talking about it is quite interesting. Here is the standard stylesheet for buttons from the Google Chrome user agent.

.c-button {
    -webkit-writing-mode: horizontal-tb !important;
    -webkit-appearance: button;
    border-color: rgb(216, 216, 216) rgb(209, 209, 209) rgb(186, 186, 186);
    border-style: solid;
    border-width: 1px;
    padding: 1px 7px 2px;
    text-rendering: auto;
    color: initial;
    display: inline-block;
    text-align: start;
    margin: 0em;
    font: 400 11px system-ui;
}

And here is what a standard button looks like when the output of which does not change the default styles.


A simple button to which standard styles are applied.

First, redefine the propertyappearance. It is used for styling specific to a particular platform. The appearance of the button with this approach is based on the styles used in the user's operating system.

.c-button {
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
}

Now, after resetting the property appearance, take a look at how the buttons look in different browsers.


The appearance property of these buttons is set to none.

Next, before we move on to styling, we need to reset some more properties. This -border,border-radius,background.

.c-button {
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
  border: 0;
  border-radius: 0;
  background: #ccc;
}


Button after resetting properties

Now that we have reset the standard button styles, it's time to move on. Namely, I want to talk about how to style buttons based on the requirements for their design.

Styling regular buttons


Let's start with a basic example, talking about styling regular buttons that display only text. The following figure shows the “anatomy” of a regular button.


Text color (Text Color), font size (Font Size), background (Background), rounding corners (Roundness), indent (Padding)

.c-button {
  appearance: none;
  border: 0;
  border-radius: 5px;
  background: #4676D7;
  color: #fff;
  padding: 8px 16px;
  font-size: 16px;
}

Having the above CSS code, we can get a button similar to the one shown in the previous figure. After the basic style of the button is set, you need to take care of the appearance of the button in various states.


Normal state of the button (Normal), getting focus (Focus), hovering the mouse over the button (Hover), disabling the button (Disabled)

▍Styles applied to buttons when you mouse over them and when they get focus


In order to indicate to the user that the mouse pointer is over the button, it is important to equip the button with a style designed for this event. The same applies to changing the appearance of a button when it receives focus in a situation where a user using a keyboard works with a page.

This material indicates that it is important to add styles applied to the button when you hover over it ( hover) and when it receives focus ( focus).

When a style is hoveradded before a style focus, all styles work correctly. The problem appears when the style is focusadded before the style hover. When an element is clicked with a mouse, the style focusdoes not manifest itself in any way; only the button representation determined by the style is visible hover.


Well, if first comes the hover style, and then the focus style.

Here's what the correct style description looks like:

.c-button:hover {
  background: #1d49aa;
}

.c-button:focus {
  outline: none;
  box-shadow: 0 0 0 4px #cbd6ee;
}

▍ Minimum Button Width


In order for the button to look good, it must have a certain width. It is important to think about this in advance and take into account the long and short labels that may appear on the buttons. Due to the property, min-widthyou can set the minimum width of the button. Here is my article where this and other similar properties are discussed in detail.

Take a look at the following figure. There you can see several buttons containing labels of different lengths in English and Arabic. If you do not set the minimum width of the button, the button, when the inscription on it is short, will be too small. It is better not to allow this and use the property min-width.


It is worth setting the min-width button property

.c-button {
    min-width: 100px;
    /*  */
}

▍ Indent


An idea may look attractive whereby buttons are not assigned horizontal indentation. After all, the buttons have a certain width, which means that there will be enough space between the edges of the buttons and the inscriptions contained on them. So? No not like this. The implementation of this idea can have negative consequences in cases where the inscription on the button changes.

Take a look at the following figure.


It is recommended to assign internal indents to the buttons.

Note that if the button is not assigned internal indents, the inscription located on it can come very close to the edges. Even if the button has a propertymin-width. At the bottom of the picture, both the propertypaddingand the property are usedmin-width. This gives more confidence that the button will look good in a situation where the length of the inscription displayed on it is not known in advance.

▍Font family used for inscriptions located on buttons


Form elements, by default, do not inherit the font family assigned to <html>or <body>. It is interesting to note that I wrote 70% of this article and realized that, firstly, I did not change the font of the button used for demonstration purposes, and secondly, I did not write anything about it.

To solve this problem, the property font-familymust be set to inherit.

.c-button {
    font-family: inherit;
    /*   */
}

▍ Styling disabled buttons


In order to indicate to the user that the button is disabled, you can add an attribute to it disabledand style the button using CSS.

Here is the HTML that describes the disabled button:

<button disabled></button>

Here is the CSS code for styling such a button:

.c-button[disabled] {
    color: #d2d5db;
    background: #6c7589;
    cursor: not-allowed;
}

When the button is disabled, it cannot get focus from the keyboard and is removed from the accessibility tree of page elements.

▍External view of the mouse cursor when you hover it over the button


The standard mouse pointer over the button looks like an arrow. I like this answer on StackOverflow: “Buttons are a traditional desktop control. This is an environment in which a hand pointer was never used until the advent of the Internet era. When the same control began to be used on web pages, the developers simply tried to make the buttons look the same as in desktop applications. ”

In order to override the usual behavior of the pointer, it is recommended to change the standard cursor-arrow to a hand-shaped cursor.


Standard arrow cursor and improved hand cursor.

Now that we’ve discussed the basic issues of button styling, here’s the full CSS code that includes everything we touched on above:

.c-button {
  min-width: 100px;
  font-family: inherit;
  appearance: none;
  border: 0;
  border-radius: 5px;
  background: #4676d7;
  color: #fff;
  padding: 8px 16px;
  font-size: 1rem;
  cursor: pointer;
}

.c-button:hover {
  background: #1d49aa;
}

.c-button:focus {
  outline: none;
  box-shadow: 0 0 0 4px #cbd6ee;
}

In the following sections, we will talk about the different styles and types of buttons that you can use in your projects.

Icon Buttons


Sometimes you need a button to have a certain icon. This may be necessary in order to highlight the button, or to better inform the user about the role of the button. It is important that the icon buttons are accessible to users with disabilities.


Examples of

icon buttons In the previous image, there are icon buttons. The code below shows the attribute added to the iconaria-hidden, which allows you to remove it from the accessibility tree. I also added an attributefocusable="false"to prevent the icon from getting focus in IE.

Please note that I used the stylevertical-align: middleto align the icon and button contents vertically.

Here is the HTML code for the button in question:

<button class="c-button">
    <svg aria-hidden="true" focusable="false" width="24" height="24" viewBox="0 0 24 24"><!--    --></svg>
    Add to fav
  </button>

Here is the style code:

.c-button svg {
    display: inline-block;
    vertical-align: middle;
    fill: #fff;
    width: 20px;
    height: 20px;
    margin-right: 0.25rem;
}

What we just examined can work until we decide to hide the text of the button and make it the button on which only the icon is located. How to do it? First you need to wrap the text, for example, in an element span. Then you can do a further improvement in the appearance of the button. Here is the HTML description of the button:

<button class="c-button c-button--icon">
    <svg aria-hidden="true" focusable="false" width="24" height="24" viewBox="0 0 24 24"><!--    --></svg>
    <span>Add to fav</span>
  </button>

It may seem that now it’s enough just to hide the element spanand the job will be done:

.c-button--icon span {
    display: none;
}

Although with this approach, the text disappears and the icon remains, this is very bad in terms of accessibility of content. The button is no longer “visible” to screen readers. For example, VoiceOver on macOS reports that such a button is simply a “Button”, without any details about it. There are several solutions to this problem.

▍Using SVG Icons


I prefer to use SVG icons. It is recommended to collect all SVG descriptions of graphic elements into one file and include each icon in the tag <svg>using the element <use>. Here is an example:

<svg class="c-icon" width="32" height="32" viewBox="0 0 20 20">
    <use xlink:href="icons.svg#facebook"></use>
</svg>

Here is the code for the SVG icon, the description of which is stored in a file icons.svgand has an identifier #facebook. This approach reduces code duplication.

▍Size button with icon


Since the button text is no longer visible, and the CSS property is set for the button min-width, the width of the button will not correspond to the size of the icon. In order to take this feature into account, it is better to explicitly set the width of the icon button.


A button that has the min-width CSS property set and a button that does not have this property set

.c-button--icon {
  min-width: initial;
  text-align: center;
  padding: 10px 14px;
}

▍Visual hiding text


An element, using the popular class .sr-only, can be hidden visually by removing it from the screen, but leaving it accessible for screen readers.

Here is the HTML code:

<button class="c-button c-button--icon">
    <svg aria-hidden="true" focusable="false" width="24" height="24" viewBox="0 0 24 24"><!--    --></svg>
    <span class="sr-only">Add to fav</span>
  </button>

Here are the styles:

.sr-only {
  border: 0; 
  clip: rect(0 0 0 0); 
  -webkit-clip-path: polygon(0px 0px, 0px 0px, 0px 0px);
  clip-path: polygon(0px 0px, 0px 0px, 0px 0px);
  height: 1px; 
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  width: 1px;
  white-space: nowrap;
}

▍ Set the font size to 0


If you set the property font-sizeto a value 0, the element spanwill not occupy any screen space at all. That is, it will be hidden.

.c-button--icon span {
    font-size: 0;
}

But here I am inclined towards a solution involving the use of a class .sr-only. From my point of view, it looks more logical. The method of hiding text by setting the font size to 0, it seems to me, looks like a kind of hack.

▍Use of aria-label attribute


If there is no element in the button <span>, then there must be a way to do without it. One such way is to use an attribute aria-label. The attribute is either assigned to the element itself <button>, or to the element <svg>.

<button class="c-button c-button--icon" aria-label="Add to fav">
    <svg aria-hidden="true" focusable="false" width="24" height="24" viewBox="0 0 24 24"><!--    --></svg>
  </button>

If you want to know more about the buttons with icons - take a look at this material.

Buttons containing multiple lines of text


In some cases, it may be necessary for the button to contain two lines of text. An example of such a button is shown below.


Button containing two lines of text

Here is a button for a subscription form that contains the main and auxiliary text. How to make such a button and at the same time not to forget about its accessibility for people with disabilities? Here is the relevant HTML:

<button class="c-button">
    <span>Subscribe to our newsletter</span>
    <span>240K+ subscribers</span>
</button>

The screen reader will “voice” this button as “Subscribe to our newsletter 240K + subscribers”. When the user hears this, it can confuse him, since nothing will separate the two lines of text displayed on the button. Take a look at the screenshot of the Chrome tool to examine the availability of items.


Exploring the availability of elements in Chrome

In order not to confuse users, I would prefer to hide the second line of text from screen readers. You can do this by using the attributearia-hiddenin the corresponding element<span>:

<button class="c-button">
    <span>Subscribe to our newsletter</span>
    <span aria-hidden="true">240K+ subscribers</span>
</button>

If for some reason you cannot change the HTML markup, there is another way to add additional text to the button. I found an interesting solution to this problem on the Smashing Magazine website. It consists in placing content using pseudo-elements. With this approach, screen readers will not see anything superfluous. Here is the CSS for this solution:

.c-button--multiple span:after {
    content: "240K+ subscribers";
    display: block;
    font-weight: 400;
    margin-top: 0.25rem;
}

Links (<a>) or buttons (<button>)?


When to use links, and when - using buttons? To begin, let's talk about how they differ from each other.

▍Links


The tag <a>is a hyperlink that allows you to go to another page of the website or go to a specific section of the page viewed by the user. Together with the tag <a>, you can use the four pseudo-: :hover, :focus, :activeand :visited. If we consider links from the point of view of accessibility, then we must say that you can interact with them using the key Enteron the keyboard. The links are voiced and screen readers.

▍Buttons


An element <button type="button">by itself, without connecting JavaScript code to it, does not perform any actions. You can use pseudo-classes with it :hover, :focusand :active. If we talk about accessibility, then you can interact with the button using the keyboard keys Enterand . Screen readers "read" the contents of the buttons.

If we recall the design, it happens that a web page contains buttons of the same style, differing in terms of the HTML code used to describe them. With this in mind, CSS code for styling a class .c-buttonmust be written so that it can be used for both elements <button>and elements <a>. Consider the following example.


Using links and buttons

Note that the “buttons” on the page are styled the same way. However, the first of them is, in fact, an element<a>, and the second is an element<button>. This means that the button does not necessarily look like an element in HTML<button>.

There are two additions to the style.c-button. These are the propertiestext-decoration: none;andtext-align: center;. By default, the text of the links is underlined, so we want to change this by styling the class used. In addition, it is important for us to center the contents of the button in cases where an HTML element other than is used to describe the button<button>.

.c-button {
    /*   */
    /*     */
    text-decoration: none;
    text-align: center;
}

▍ The <button> element does not have to look like a button


I like this example, in which, taking into account the requirements of accessibility of content, the “accordion” panel is implemented. Initially, assuming JavaScript is not available, the markup rendering result looks like the one below.


Page Materials

When JavaScript is Not Available Here is the HTML code for a fragment of such markup:

<div class="accordion" data-aria-accordion>
  <h2 data-aria-accordion-heading>Heading of my panel</font></h2>
  <div data-aria-accordion-panel>
    <p>Content goes here</p>
  </div>
</div>

If JavaScript is available, the above markup will describe elements that can be collapsed and expanded. This is achieved by creating a button and adding it to the element <h2>.


Page materials in conditions when JavaScript functionality is available

In this case, we can say that using the element<button>is the right choice, since the button solves the problem of expanding and collapsing blocks of text.

▍Boot button


Suppose we have a document. We need to describe a link to download it. Which element should be used to solve this problem? Here a link will come to our aid! If you add an attribute to the link download, clicking on it will initiate the download of the relevant materials.


Link to download the document, designed as a button

Here is the code for this link:

<a href="rtl-101.pdf" download>Download PDF</a>

With this approach, we have at our disposal a link, stylized as a button, which does its job using only semantic HTML structures.

Stroke Buttons



A regular button when you hover over it becomes a button with a stroke.

The previous figure shows a button that becomes a button with a stroke when you hover over it. Which method is best used to implement a similar change in the appearance of a button? For starters, the button style should include a propertyborderthat, by default, uses a transparent color. By providing such a property, we make sure that the borders of the button, when you hover over it, do not redraw.

.c-button {
    border: 2px solid transparent;
    /*   */
}

.c-button:hover {
    background: transparent;
    color: #222;
    border-color: #4676d7;
}

Thanks to this approach, we have at our disposal a button around which, when you mouse over it, its borders are displayed. In this case, the background of the button becomes transparent.

Gradient Buttons


When I was working on an article on positioning elements, I needed a button with a gradient fill.

I needed something similar to the following figure.


Gradient button and its variant with a stroke.

Here are two options for the button - a gradient button and a button with a stroke. In order to achieve such an appearance of the button, I needed the base (gradient) button to have a transparent border. This border will only be shown for a button with a stroke.

.c-button {
      display: inline-block;
      color: #fff;
      background: linear-gradient(to bottom, #00acee, #0072e0);
      font-size: 18px;
      font-weight: 500;
      padding: 12px 26px;
      border-radius: 100px;
      /*  ,          */
      border: 3px solid transparent;
      text-decoration: none;
}

.c-button--outline {
  background: transparent;
  color: var(--color-brand-primary);
  border-color: var(--color-brand-primary);
}

Everything seems to be reasonable here, but in the course of work I ran into a strange problem. I even tried to solve it by asking Twitter users for help. This problem is presented in the following figure.


A regular button, a button with a gradient (the border is actually transparent; it is shown for color purposes only), a button with strange edges

Now that we’ve figured out what a gradient button with a transparent border looks like, let me tell you about the reasons for the above oddities .

Each element has a propertybackground-originthat defines the positioning area of ​​the wallpaper, which is set to the default valuepadding-box. With this approach, the size of the gradient is selected so that it matches the size of the element, taking into account the thickness of the border.

I tried to add a wide border to the button in order to observe what happens. Notice how the gradient repeats, and that its size matches the property of thepaddingbutton.


Button with wide borders and gradient.

To solve this problem, the positioning area of ​​the background picture must be set using the stylebackground-origin: border-box;, changing the default valuepadding-boxtoborder-box:

.c-button {
      /*   */
      background-origin: border-box;
}

Now , if you want to experiment, an example on CodePen.

Which is better - height or padding?


Each button has a height. You can control the height of a button either by setting its property explicitly heightor by setting a property paddingfor the top and bottom sides of the button. Please note that the problems that we will discuss below are typical mainly for elements <a>.

▍ Fixed height


Suppose we have a button styled as follows:

.c-button {
    /*   */
    min-width: 120px;
    height: 45px;
}

With this approach, the text is not centered. It all looks like the one below.


Button, the text of which is not aligned in the center

In order to fix this and align the text, you can either use the propertypadding, or the propertyline-height. By setting itline-heightto a value equal to or close to the height of the button, we can align the text in the center.

.c-button {
    /*   */
    min-width: 120px;
    height: 45px;
    line-height: 45px;
}

But this method has disadvantages:

  • It is not guaranteed that the button text will always be correctly centered. If you change the font, its alignment may be broken.
  • When increasing the value specified by the property font-size, you need to select a new value for the property line-height.
  • The property line-height may work differently for texts displayed in different languages. This is true for multilingual sites.
  • If there is a button with two lines of text, this method is not suitable for aligning the contents of the button.

▍ Vertical Indent


If you set the same values ​​for the padding-topand properties padding-bottom, then expect the contents of the button to be centered. The way it is? In fact, it depends on the specific situation.

There are fonts that center very well. And there are those who behave differently. Sometimes, to achieve the goal, one of the values ​​of the vertical indentation needs to be slightly changed. Take a look at the next button.


Attempting to center button text

Here is the CSS code for styling this button:

.c-button {
    /*   */
    min-width: 120px;
    padding: 16px 14px;
}

In this case, the content of the button looks slightly biased. The value of the upper indent should be slightly reduced:

.c-button {
    /*   */
    min-width: 120px;
    padding: 14px 14px 16px 14px; /* ,  , ,   */
}

Wrapping button content in a <span> tag


During the experiments, I found out that the alignment of the text of the Adobe buttons is a bit knocked down. Here is how it looks.


Centering a bit knocked down.

I examined these buttons and noticed an interesting pattern. Content is wrapped in an element<span>indicating a fixed height for the button.

<button class="c-button-adobe">
    <span>Save Changes</span>
</button>

For an <button>element <span>, the element , by default, is centered. When the height changes, the content is centered automatically, without the need for a property paddingor anything else. Here is an animated demonstration of this behavior.


Changing the height of a button and automatically centering its contents

True, if we are talking about a link that looks like a button, then the element<span>needs to be centered. In order to take this feature into account, one can use the method of arranging elementsflexbox.

.c-button {
    /*   */
    display: inline-flex; *To keep the button inline, and flex container at the same time*
    align-items: center;
    justify-content: center;
}

That's all. I want to note that this article helped me when writing this section .

Buttons inside flexbox or grid containers


Here you may have a question about what buttons have to do with flex and grid layouts. Let me clarify this question.

I worked on a section of one project. I needed to vertically center its contents. So I used the flex layout:

.c-hero {
    display: flex;
    flex-direction: column;
    justify-content: center;
}

What I did was a bit surprising.


Result of using flex layout

By default, each flex element is stretched within the width of the parent element. This is exactly what happened with the button shown in the previous figure. In order to avoid this trouble, you need to configure the propertyalign-self:

.c-button {
    /*   */
    align-self: flex-start;
}

This is how the button will look after that.


The button looks much better.

Another common problem occurs when the button is placed in a flex container, in which, by default, the property is setflex-direction: row. The height of the button will be adjusted to the height of the parent element. In order to fix this problem, you need to use the buttonalign-selfproperty or the flex container propertyalign-items.


Button stretched in height

Here is the CSS:

.c-button {
    align-self: center;
}

By setting the alignment of the button relative to the center of the container, we solve the problem. This is how the same example will now look.


Solving the problem of a stretched button

Using em units


The unit of measure emcan be used to properly configure the button sizes. This unit of measure in this situation is very useful. The unit emis equal to the font-sizeelement ( pxor rem). Take a look at the following example:

.element {
    font-size: 16px;
    padding: 0.5em;
}

In this case, the value of padding is equal 16 * 0.5 px.

Assume that the project uses buttons of three sizes: regular, medium, and large. When to specify values padding, width, height, marginused units of measurement em, it allows you to create a button, the dimensions of which is very easy to change. Here is how it looks.


Buttons of different sizes

But here are the fragments of CSS code that uses the unit of measureem:

.c-button {
  /*   */
  font-size: 1rem;
  min-width: 6.25em;
  border-radius: 0.3125em;
  padding: 0.625em 1em;
}

.c-button svg {
  width: 1.25em;
  height: 1.25em;
  margin-right: 0.25em;
}

For buttons of different types, you need to create classes that specify different font sizes:

.c-button--md {
  font-size: 22px;
}

.c-button--lg {
  font-size: 30px;
}

Here is an example of using similar buttons on CodePen

Here is my article on this topic. I recommend that you read it

Animation and Transitions


In order to make it more pleasant for the user to work with the page, it is important to think about using a transition when you hover over the button. The easiest way to do this is by organizing a background color change. Here is an example on CodePen where you can see the implementation of this idea.

Summary


I was very pleased to work on this material. It took me more than a year to write it. I am happy that it is finally published. I hope you find something here that is useful to you.

Dear readers! Do you know of any useful ways to work with buttons that are beyond the scope of this article?


All Articles