Day 17: Styling a search box like Google’s

TL;DR

To style a search box like the one on Google.com, write the HTML and CSS code as in this CodePen demo that I’ve made. Then you’ll have a user experience shown in the GIF animation below:

The user experience that this article will build from scratch

Intro

I’m making a web app that improves the user experience of Google Maps. Its core feature is to allow the user to save a place with their own note in the rich text format. (Google Maps only allows plain text notes.)

To save a place, the user first needs to search for that place on the map. Consequently, a search box is one of the essential UI components of the web app I’m making.

I want the search box to look like the one the user sees after visiting google.com:

Google.com landing page (screenshot by the author)

Everyone is familiar with Google’s search box appearance. By making my own app’s search box resemble the one by Google, the user will immediately figure out that it is a search box, rather than the standard text field.

This is a UI design tip derived from what Jakob Nielsen claimed back in 2005:

”In user testing, people tell us that they want search on websites and intranets to work like X , where X is their favorite major search engine.” (Nielsen 2005)

The implementation of this UI design with HTML/CSS turns out to be rather tricky, however. This article describes the solution I’ve reached by trial and error. It involves various tips to style any search box. I hope it will be helpful for you one day.

Let’s start with HTML.

1. HTML code for a search box

The semantic HTML element for a search box is <input type="search">. One benefit, compared to <input type="text">, is that Chrome and Safari users will see the delete button at the end of the search box once they enter text in it (Coyier 2021 and MDN Contributors 2022).

Chrome’s search box delete button (Screenshot by the author)
Safari‘s search box delete button (Screenshot by the author)

But a few more attributes need to be added to the <input> element:

<input
aria-label="Search for a place on the map"
autocomplete="off"
inputmode="search"
placeholder="Enter place name or address"

type="search"
/>

First of all, I use aria-label to label the search box, to allow screen reader users to tell what the <input> element is for. I don't use <label> here, because a magnifying glass icon will play a role of the label for sighted users (cf. MDN Contributors 2022).

Second, I turn off the browser’s default autocomplete feature with autocomplete="off". This is because I’m going to render Google Maps API’s autocomplete suggestions directly below the search box. The input element's own autocomplete suggestions would then overlap the suggestions from Google Maps.

Third, for smartphone users, the keyboard that appears after tapping the search box is altered with inputmode="search", which changes the return button label into something more appropriate for search. See Oliff (2019) for detail.

Finally, the placeholder attribute specifies the text to be shown in the search box before the user clicks it. For the text content, I follow MDN Contributors (2022), who recommends "a word or short phrase that demonstrates the expected type of data".

2. SVG code for a magnifying glass icon

I borrow a magnifying glass icon from Material Icons. Downloading an SVG file and removing unnecessary pieces of code in it, I get this:

<svg aria-hidden="true" viewBox="0 0 24 24">
<path
d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"
/>
</svg>

I’ve added aria-hidden="true", because there is no point telling the presence of a magnifying glass icon to screen reader users. They know what the <input> element is for, by listening to its aria-label attribute value (see Section 1 above).

3. Wrapping the <svg> and <input> elements with a <div>

Google puts the magnifying glass icon inside of the search box. To follow this UI design, the icon needs to be “absolutely positioned” relative to the search box.

For this purpose, we need to wrap the <input type="search"> and <svg> elements with a <div> element that is going to be "relatively positioned". (See Section 7 below for detail.)

Let the <div>’s class name is searchbox. So the entire HTML code is as follows:

<div class="searchbox">
<svg aria-hidden="true" viewBox="0 0 24 24">
<path
d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"
/>
</svg>
<input
aria-label="Search for a place on the map"
autocomplete="off"
inputmode="search"
placeholder="Enter place name or address"
type="search"
/>
</div>

Now it’s time to work on CSS.

CSS stylesheets are often difficult to maintain, because you will for sure forget what role each CSS declaration plays in the construction of UI.

To ease this difficulty, I chunk CSS declarations by purpose. Let’s start with the color palette.

4. Setting up the color palette

I use three colors to style the search box.

  1. Dark gray for the search box’s border, the magnifying glass icon, and the user’s input text
  2. Light gray for the placeholder text
  3. Light blue for the search box’s border when it is focused.

Set these colors as CSS variables at the root element so other UI components elsewhere in the web app may also use them:

html {
--dark-gray: rgb(90, 90, 90);
--light-gray: rgb(148, 148, 148);
--focus-blue: rgb(69, 159, 189);
}

5. Shaping the search box

Next, I’m going to shape the search box, by setting its size and drawing the border.

5.1 Deriving the height from a minimum touch target size

The search box’s height needs to be large enough that the user will find it easy to tap to start typing text in it.

Google recommends the 48x48 pixel area as a minimum touch target size, because:

“The 48x48 pixel area corresponds to around 9mm, which is about the size of a person’s finger pad area.” (Gash et al. 2020)

So I use 48px for the height of <input type="search">.

.searchbox {
--target-size: 48px;
--box-height: var(--target-size);
height: var(--box-height);
}
.searchbox input[type='search'] {
height: 100%;
}

Here I use CSS variables to record the reasoning behind styling so that my future self will quickly understand the CSS code written a long while ago.

I could directly set height for <input[type="search"]>. But it's not intuitive, at least for me, to guess the parent element's height from its children. I prefer defining height for the parent and letting it cascade down the DOM tree.

5.2 Drawing the search box’s border

The border width of a search box is, in my view, best set to be 2px. A 1px border is too thin for the user to notice the search box. Being thicker than 2px will make the search box look unsophisticated.

Let’s draw the border now:

.searchbox {
--target-size: 48px;
--box-height: var(--target-size);
--border-width: 2px; /* ADDED */
height: var(--box-height);
}
.searchbox input[type='search'] {
border: var(--border-width) solid var(--dark-gray); /* ADDED */
height: 100%;
}

To replicate Google’s search box shape, the both sides of the box need to be semi-circles. That is, border-radius needs to be half the height of the box:

.searchbox {
--target-size: 48px;
--box-height: var(--target-size);
--border-width: 2px;
--border-radius: calc(var(--box-height) / 2); /* ADDED */
height: var(--box-height);
}
.searchbox input[type='search'] {
border: var(--border-width) solid var(--dark-gray);
border-radius: var(--border-radius); /* ADDED */
height: 100%;
}

I define the border radius as a CSS variable, because this value will be used for another purpose later (see Section 7.2 below).

5.3 Setting the width of the search box

For mobile devices, the search box should be as wide as possible, with some side margins to keep a distance from the screen edges.

For wide screens like laptops, however, the search box shouldn’t be as wide as screens, just like the paragraphs of text shouldn’t. A very long line of text is difficult to read.

Given this consideration, the use of max-width is appropriate to set the width of a search box.

Google’s search box is 561px wide for widest screens. I don’t know where this magic number comes from. But I just follow this “convention”:

.searchbox {
--target-size: 48px;
--box-height: var(--target-size);
--border-width: 2px;
--border-radius: calc(var(--box-height) / 2);
height: var(--box-height);
max-width: 561px; /* ADDED */
}
.searchbox input[type='search'] {
border: var(--border-width) solid var(--dark-gray);
border-radius: var(--border-radius);
height: 100%;
width: 100%; /* ADDED */
}

The width of <input type="search"> has to be 100%, to override the browser's default style.

6. Styling the sub-components of our search box

Our search box has two sub-components: the magnifying glass icon and the search box text.

The standard approach would be to assign a class to each sub-component to define its style, like searchbox__icon in the BEM convention.

But I’d rather use the descendant combinator, that is,

.searchbox svg {
/* Style for the magnifying glass icon */
}
.searchbox input[type='search'] {
/* Style for the search box text */
}

This way, the CSS code self-explains that the set of CSS declarations is defined for a subcomponent of the search box, rather than an independent component. It also saves me from the hassle of coming up with extra class names. Naming is the hardest thing to do in coding.

6.1 Styling the magnifying glass icon

By trial and error, I’ve reached a conclusion that the magnifying glass icon looks best when it’s sized three-quarters of the search box’s height:

.searchbox {
--icon-size: calc(var(--box-height) * 3 / 4);
}
.searchbox svg {
fill: var(--dark-gray);
height: var(--icon-size);
width: var(--icon-size);
}

With the fill property, I match the icon color with the search box border.

6.2 Styling the search box text

The <input> element does not inherit the font property values from its parent. So we need to define the font style explicitly.

.searchbox input[type='search'] {
-webkit-appearance: none; /* for Safari */
color: var(--dark-gray);
font-family: 'Noto Sans', Verdana, sans-serif;
font-size: 1rem;
}

Safari ignores font-size for <input type="search">. To disable this behavior, we need -webkit-appearance: none. See Coyier (2021).

The text color is set so it will match the icon color as well as the search box border color.

We also need to style the placeholder text. The font style gets inherited; but the text color does not. To style the placeholder text, use the ::placeholder pseudo element:

.searchbox input[type='search']::placeholder {
color: var(--light-gray);
opacity: 1;
}

If the placeholder text has the same color as the search box border, it can give a wrong impression that text is already filled in. A convention is that the placeholder text is in a lighter shade of gray than the search box border.

But the color property alone isn't enough. Firefox's default style makes the placeholder text translucent. The opacity: 1 corrects this.

As we’ve seen by now, the default style of <input type="search"> is inconsistent across browsers in an unexpected way. This makes it tricky to style a search box. We'll see more examples of it below.

7. Positioning the icon and the text field within the search box

The next step is to adjust the positioning of sub-components inside the search box.

7.1 Placing the icon inside the box

I need to overlap the icon above the search box. The standard CSS technique for overlapping is the relatively-absolute positioning (see James 2017 for a beginner-friendly introduction):

.searchbox {
position: relative;
}
.searchbox svg {
position: absolute;
}

This is why I had to wrap the icon and the search box with a <div> element (see Section 3 above).

7.2 Horizontal spacing

Now let’s set the horizontal margins. First, I want the icon to sit half the border radius from the left edge of the search box:

.searchbox {
--side-margin: calc(var(--border-radius) / 2); /* ADDED */
position: relative;
}
.searchbox svg {
position: absolute;
left: var(--side-margin); /* ADDED */
}

Then, I want the icon to be optically center-aligned between the left edge of the search box and the left edge of the text in the search box (placeholder text or user input text).

.searchbox {
--side-margin: calc(var(--border-radius) / 2);
position: relative;
}
.searchbox svg {
position: absolute;
left: var(--side-margin);
}
/* ADDED from here */
.searchbox input[type='search'] {
padding-left: calc(var(--side-margin) + var(--icon-size) + 4px);
}

Optical center-alignment is an important technique for graphic designers. Most characters and icons have a different amount of white space on each side. For the magnifying glass icon, it has more white space to its right, because the thin handle sticks out to its bottom-right. For human eyes to see the icon center-aligned, then, we need less white space to the right of the icon.

By trial and error, I find 4px is the magic number for the right-hand white space to achieve the optical center-alignment of the icon.

To implement this spacing, we need to set the value of padding-left to be the sum of the icon's width and its side margins. This is why I have the following CSS declaration:

padding-left: calc(var(--side-margin) + var(--icon-size) + 4px);

Finally, I need to set the space between the right edge of the search box and the end of text (or the delete button which appears in Chrome and Safari once the user enters text; see Section 10 below for detail).

A symmetric appearance always looks decent to our eyes. So I go for the same amount of space as the left margin for the icon:

.searchbox {
--side-margin: calc(var(--border-radius) / 2);
position: relative;
}
.searchbox svg {
position: absolute;
left: var(--side-margin);
}
.searchbox input[type='search'] {
padding-left: calc(var(--side-margin) + var(--icon-size) + 4px);
padding-right: var(--side-margin); /* ADDED */
}

The above CSS code works perfect except for Safari, which inserts a little horizontal space to the left of the text field. The padding property does not override this spacing.

A solution to this is provided by Lines 136–138 of RESS, a modern CSS reset stylesheet by Linhares (2022):

input[type='search']::-webkit-search-decoration {
-webkit-appearance: none;
}

The <input type="search"> has a few obscure pseudo elements for Webkit browsers like this one (Peter 2010). We'll see another one in action in Section 10 below.

7.3 Vertical spacing

I want the magnifying glass icon to be vertically center-aligned within the search box.

Usually, the flexbox is the easiest way to vertically center-align items. In my case, however, I use the relatively-absolute positioning technique for the icon. So it’s best to set the top and bottom values to be equal:

.searchbox {
--side-margin: calc(var(--border-radius) / 2);
--icon-vertical-margin: calc(
(var(--box-height) - var(--icon-size)) / 2
); /* ADDED */

position: relative;
}
.searchbox svg {
position: absolute;
left: var(--side-margin);
top: var(--icon-vertical-margin); /* ADDED */
bottom: var(--icon-vertical-margin); /* ADDED */
}

We tend to think that styling is done by now. For interactive elements like a search box, however, there is one more important thing to style: the focus state.

8. Styling focus state

8.1 Using border and box-shadow to style the focus ring

The focus ring is usually styled with the outline property. But I want the focus ring to glow, as in Coyier (2012)'s code snippet, like this:

A focus ring that glows (screenshot by the author)

To achieve this style, I use the combination of border and box-shadow:

.searchbox input[type='search']:focus {
border-color: var(--focus-blue);
box-shadow: 0px 0px 5px var(--focus-blue);
}

When we style the focus ring with properties other than outline, however, Soueidan (2021) recommends adding a transparent outline as a fallback for Forced Color Modes, which will remove the focus ring styled with background, border, and/or box-shadow properties. Following her suggestion, I add:

.searchbox input[type='search']:focus {
border-color: var(--focus-blue);
box-shadow: 0px 0px 5px var(--focus-blue);
outline: 1px solid transparent; /* ADDED */
}

Explicitly styling the outline property has an additional benefit: it removes the browser's default focus ring, which is usually done with outline: none.

9. Allowing user to tap the icon for focusing the search box

At this point, styling the search box is essentially done. However, I notice one thing unsatisfactory.

When I click the magnifying glass icon, expecting the search box to be focused, nothing actually happens. This is because the magnifying glass icon is rendered on top of the search box, preventing the user from tapping the search box.

To solve this issue, the icon needs to be rendered beneath the search box:

.searchbox svg {
z-index: -1;
}
.searchbox input[type='search'] {
background: transparent;
}

Since <div class="searchbox"> is relatively positioned (see Section 7 above), it creates its own stacking context (Comeau 2022). By setting z-index to be -1, the <svg> element goes beneath all the child elements of the <div> (i.e. the <input type="search"> in our case).

But the <input> element, by default, has a white background, hiding the <svg> element beneath. So we need to make it transparent.

Limitations

This solution won’t work if the search box needs a distinct background color, though.

If you know an alternative solution that allows a non-transparent background for the search box, please let me know.

10. Customizing the text delete button style for Chrome and Safari

As described in Section 1 above, one benefit of using <input type="search"> instead of <input type="text"> is that, once the user enters text, Chrome and Safari will render a delete button in the search box so the user can delete the entire text by clicking it.

But the default style of the delete button differs between Chrome and Safari, and it is inconsistent with the rest of my web app’s design.

Chrome’s search box delete button (Screenshot by the author)
Safari’s search box delete button (Screenshot by the author)

To customize the style of the delete button, we can use the ::-webkit-search-cancel-button pseudo element (Coyier (2010)):

.searchbox input[type='search']::-webkit-search-cancel-button {
/* Remove default */
-webkit-appearance: none;
/* Now your own custom styles */
background-image: url("data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='rgb(148, 148, 148)' %3E%3Cpath d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z' /%3E%3C/svg%3E%0A");
height: calc(var(--box-height) / 2);
width: calc(var(--box-height) / 2);
}

First, remove the browser’s default style with -webkit-appearance: none;. Then, we can use the background, height, and width properties to shape our own delete button.

In the above CSS code, I use an SVG image of the cross icon from Material Icons. I edit the SVG code so that the color of the icon is set with fill="rgb(148, 148, 148)".

Then, following Coyier (2013), I convert the SVG code into a Data URL to be used as a background-image value, by using the URL-encoder for SVG.

Finally, I scale the icon to half the size of the search box’s height.

Limitations

This approach works if we do not need to care about the dark mode. To switch the color between light and dark modes, we need to replace the entire data URL. This requires quite a bit of code duplication.

If you know a better approach to customize the delete button, please let me know.

11. Removing the flash of gray background when tapping the search box on iOS Safari

Here’s one final gotcha of a search box. This time, it’s iOS Safari.

By default, <input type="search"> will flash the grey background when the user taps it on iOS Safari. This is unnecessary, because tapping a search box will prompt the appearance of a keyboard at the bottom of the screen. We do not need an additional feedback to the user for indicating that the search box is now ready to be typed in.

To disable this feature, use an obscure Webkit-specific CSS property:

input[type='search'] {
-webkit-tap-highlight-color: transparent;
}

See Apple Developer (2016) for detail.

Final words

That’s all about styling a search box like Google’s. Here’s the demo with CodePen.

I’m surprised by the sheer number of things to remember for styling a very simple search box…

References

Comeau, Josh W. (2022) “What the heck, z-index??”, joshwcomeau.com, Jan 27, 2022.

Coyier, Chris (2010) “WebKit HTML5 Search Inputs”, CSS-Tricks, Aug 25, 2010.

Coyier, Chris (2012) “Glowing Blue Input Highlights”, CSS-Tricks, Apr 11, 2012.

Coyier, Chris (2013) “Using SVG”, CSS-Tricks, Mar 5, 2013.

Coyier, Chris 2021 “What do you get for using a search input type?”, CSS-Tricks, Nov 17, 2021.

Gash, Dave, Meggin Kearney, Rachel Andrew, Rob Dodson, and Patrick H. Lauke (2020) “Accessible tap targets”, web.dev, Mar 31, 2020.

James, Oliver (2017) “Advanced Positioning”, Interneting Is Hard, 2017.

Linhares, Filipe (2022) “ress”, GitHub, last updated on Mar 9, 2022.

MDN Contributors (2022) “”, MDN Web Docs, last modified on Aug 9, 2022.

Nielsen, Jakob (2005) “Mental Models For Search Are Getting Firmer”, Nielsen Norman Group, May 8, 2005.

Oliff (2019) “Everything You Ever Wanted to Know About inputmode”, CSS-Tricks, May 17, 2019.

Peter 2010 “It’s not enough. To get rid of superfluous padding, you need…”, CSS-Tricks, Aug 26, 2010.

Soueidan, Sara (2021) “A guide to designing accessible, WCAG-compliant focus indicators”, sarasoueidan.com, Aug 13, 2021.

--

--