How to build custom search bar with HTML and CSS without JavaScript #1

Search bar is one of the most common UI components in the web. Every search engine has one. Trough search bars we interact with the web. Let’s build one for our website.

Layout

Base

To perform a search inside our website we need to send a GET request with some parameter (usually its q as in “query”). The easiest way to do this with plain HTML is by using <form> element with method="GET” and action attributes alongside with input element named q and a button with submit type.

<form action="/search"
      method="GET">
    <fieldset>
        <input type="search"
            name="q"
            placeholder="Search the website"
            required />
        <button type="submit">Search</button>
    </fieldset>
</form>
component preview

Now when user clicks submit button, browser will send a GET request to /search with query q={value}.

Reset functionality and accessibility

To give user ability to clear form inputs (in our case just the search input value) we should add input type="reset". Now by clicking on input type="reset" the value in the input type="search" will become null. Since all input elements must have accessible label element, let’s wrap our input type="search" and input type="reset" in label.

<form action="/search"
      method="GET">
    <fieldset>
        <label>
            <input type="search"
                name="q"
                placeholder="Search the website"
                required />
            <input type="reset" />
        </label>
        <button type="submit">Search</button>
    </fieldset>
</form>
component preview

Style

Images

For our search bar to have an outstanding view we will add magnifying glass icon before input type="search" and X icon instead of input type="reset" button.

<img src="data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iaGVhZGVyLXNlYXJjaF9fZm9ybV9fZ2xhc3MiCiAgICAgd2lkdGg9IjIwIiBoZWlnaHQ9IjIxIgogICAgIHZpZXdCb3g9IjAgMCAyMCAyMSIKICAgICBmaWxsPSJub25lIgogICAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgICA8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTIuMTYxNTMgOS4yNjQ3MUMyLjE2MTUzIDUuNjgyOTggNS4wNjUxIDIuNzc5NDEgOC42NDY4MyAyLjc3OTQxQzEyLjIyODYgMi43Nzk0MSAxNS4xMzIxIDUuNjgyOTggMTUuMTMyMSA5LjI2NDcxQzE1LjEzMjEgMTEuMDUyNyAxNC40MDk4IDEyLjY3MDMgMTMuMjM4NyAxMy44NDQ0QzEyLjA2MzYgMTUuMDIyNiAxMC40NDEgMTUuNzUgOC42NDY4MyAxNS43NUM1LjA2NTEgMTUuNzUgMi4xNjE1MyAxMi44NDY0IDIuMTYxNTMgOS4yNjQ3MVpNOC42NDY4MyAwLjkyNjQ3MkM0LjA0MTc1IDAuOTI2NDcyIDAuMzA4NTk0IDQuNjU5NjMgMC4zMDg1OTQgOS4yNjQ3MUMwLjMwODU5NCAxMy44Njk4IDQuMDQxNzUgMTcuNjAyOSA4LjY0NjgzIDE3LjYwMjlDMTAuNjE1MiAxNy42MDI5IDEyLjQyNTQgMTYuOTIgMTMuODUxNCAxNS43Nzk1TDE3Ljg3NDEgMTkuODAyMkMxOC4yMzU5IDIwLjE2NCAxOC44MjI1IDIwLjE2NCAxOS4xODQzIDE5LjgwMjJDMTkuNTQ2MSAxOS40NDA0IDE5LjU0NjEgMTguODUzOCAxOS4xODQzIDE4LjQ5MTlMMTUuMTYxNiAxNC40NjkzQzE2LjMwMjEgMTMuMDQzMiAxNi45ODUxIDExLjIzMzEgMTYuOTg1MSA5LjI2NDcxQzE2Ljk4NTEgNC42NTk2MyAxMy4yNTE5IDAuOTI2NDcyIDguNjQ2ODMgMC45MjY0NzJaIiBmaWxsPSJyZ2IoMTQ3LCAxNTMsIDE3MikiLz4KPC9zdmc+Cg==" width="20" height="21" />

<input type="reset" style="background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTIiCiAgICAgdmlld0JveD0iMCAwIDEyIDEyIgogICAgIGZpbGw9Im5vbmUiCiAgICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxwYXRoIGQ9Ik0xMS43MDcxIDEuNzA3MTFDMTIuMDk3NiAxLjMxNjU4IDEyLjA5NzYgMC42ODM0MTcgMTEuNzA3MSAwLjI5Mjg5M0MxMS4zMTY2IC0wLjA5NzYzMTEgMTAuNjgzNCAtMC4wOTc2MzExIDEwLjI5MjkgMC4yOTI4OTNMMTEuNzA3MSAxLjcwNzExWk0wLjI5Mjg5MyAxMC4yOTI5Qy0wLjA5NzYzMTEgMTAuNjgzNCAtMC4wOTc2MzExIDExLjMxNjYgMC4yOTI4OTMgMTEuNzA3MUMwLjY4MzQxNyAxMi4wOTc2IDEuMzE2NTggMTIuMDk3NiAxLjcwNzExIDExLjcwNzFMMC4yOTI4OTMgMTAuMjkyOVpNMTAuMjkyOSAxMS43MDcxQzEwLjY4MzQgMTIuMDk3NiAxMS4zMTY2IDEyLjA5NzYgMTEuNzA3MSAxMS43MDcxQzEyLjA5NzYgMTEuMzE2NiAxMi4wOTc2IDEwLjY4MzQgMTEuNzA3MSAxMC4yOTI5TDEwLjI5MjkgMTEuNzA3MVpNMS43MDcxMSAwLjI5Mjg5M0MxLjMxNjU4IC0wLjA5NzYzMTEgMC42ODM0MTcgLTAuMDk3NjMxMSAwLjI5Mjg5MyAwLjI5Mjg5M0MtMC4wOTc2MzExIDAuNjgzNDE3IC0wLjA5NzYzMTEgMS4zMTY1OCAwLjI5Mjg5MyAxLjcwNzExTDEuNzA3MTEgMC4yOTI4OTNaTTEwLjI5MjkgMC4yOTI4OTNMMC4yOTI4OTMgMTAuMjkyOUwxLjcwNzExIDExLjcwNzFMMTEuNzA3MSAxLjcwNzExTDEwLjI5MjkgMC4yOTI4OTNaTTExLjcwNzEgMTAuMjkyOUwxLjcwNzExIDAuMjkyODkzTDAuMjkyODkzIDEuNzA3MTFMMTAuMjkyOSAxMS43MDcxTDExLjcwNzEgMTAuMjkyOVoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=')"/>
component preview

CSS rules

Now let’s do some CSS magic. First, add classes to elements.

<form class="form">
  <fieldset class="form__fieldset">
    <label class="form__label"
         for="searchbar">
        <img class="form__glass"/>
        <input type="search"
            id="searchbar"
            name="q"
            class="form__input"/>
        <input type="reset"
            class="form__reset"/>
    </label>
    <button class="form__submit"
        type="submit">Search</button>
  </fieldset>
</form>

And finally, add some CSS for better visibility and accessibility.

input, button {
    background: none;
    outline: none;
    border: none;
    color: transparent;
    background-repeat: no-repeat;
}

.form {
    width: 100%;

    &:invalid {
        & .form__reset {
            opacity: 0;
            pointer-events: none;
        }

        & .form__button {
            pointer-events: none;
            pointer: default;
        }
    }

    &:valid {
        & .form__reset {
            opacity: 1;
            pointer-events: all;
        }
    }

    &__label {
        display: flex;
        align-items: center;

        padding: 10px 20px;

        background-color: black;
        border: 1px solid transparent;

        border-radius: 5px;

        transition: border 0.066s linear;

        &:hover {
            border: 1px solid cyan;
        }

        &:focus-within {
            border: 1px solid blue;
        }
    }

    &__fieldset {
        display: flex;
        align-items: center;
        justify-items: stretch;

        width: 100%;
    }

    &__input {
        display: inline-block;
        vertical-align: middle;

        min-width: 200px;
        width: 100%;

        background-color: transparent;
        color: white;

        &::placeholder {
            color: lightgray;
        }
    }

    &__glass {
        justify-self: start;
        vertical-align: middle;
        margin-right: 10px;
    }

    &__reset {
        justify-self: end;
        vertical-align: middle;

        cursor: pointer;

        padding: 5px;
        margin-left: 10px;
        width: 12px;
        height: 12px;

        background-position: center;
        background-size: 12px 12px;
        background-repeat: no-repeat;

        border: 1px solid transparent;

        transform-origin: center;
        transform: rotate(0);

        transition:
            border 0.066s linear,
            transform 0.066s linear,
            opacity 0.15s linear;

        @media (prefers-reduced-motion: true) {
            transition: none;
        }

        &:hover {
            transform: rotate(90deg);
        }

        &:focus {
            border: 1px solid blue;
        }
    }

    &__button {
        margin-left: 5px;
        background-color: black;
        color: white;
        align-self: stretch;
        padding: 10px 20px;

        cursor: pointer;

        border-radius: 5px;
    }
}
component preview

Result

As a result, we now have accessible searchbar with reset functionality and some fancy icons working without a single line of JavaScript. Final result available on Codepen.

$ cd ..