Question

CSS ::before and ::after on Input Elements

htmlcsspseudo-elementcss-content

Question

I am trying to add an image after a text <input> using the CSS :after pseudo-element, but it does not appear. The same CSS works on a <span>.

.mystyle::after {
  content: url("smiley.gif");
}

.mystyle {
  color: red;
}

This works as expected:

<span class="mystyle">buuu!</span> some more

However, this only changes the input text color; the image does not appear:

<input class="mystyle" type="text" value="someValue">

Why does ::after not work on the input element? Is there another pseudo-selector or CSS-only approach, especially when I cannot add a wrapper <span> because the input is generated by a third-party control?

Short Answer

You will learn why ::before and ::after generated content works on ordinary elements such as <span> but is not reliably supported on form inputs. You will also learn practical alternatives, including styling a nearby element, using a background image when appropriate, and adding a separate indicator with JavaScript or a control-provided template.

Concept

::before and ::after create generated content. Conceptually, the browser inserts that content as the first or last child of the selected element.

.notice::after {
  content: " New";
}

For an ordinary container such as a <span>, <div>, or <button>, the browser has a normal content area where generated content can be placed.

An <input> is different. It is a replaced element: the browser or operating system renders much of its visible control rather than exposing ordinary HTML child content for CSS to lay out. In addition, <input> is a void element, so it cannot contain child elements in HTML.

Because of this, generated-content pseudo-elements on <input> are not something you can depend on. Some browsers may show partial or inconsistent behavior for some input types, but cross-browser CSS should treat input::before and input::after as unsupported.

Use the modern ::before and ::after spelling in new CSS. The older :before and :after syntax is retained for compatibility, but changing one colon to two does not make an input support generated content.

Mental Model

Think of ::before and ::after as two small seats placed inside an element:

  • ::before is the first seat.
  • The element's normal content sits in the middle.
  • ::after is the last seat.

A <span> is like an open box, so CSS can put a seat before or after its text.

An <input> is like a sealed, prebuilt device. You can change its outside appearance—such as its border, color, width, and background—but you cannot reliably place extra content inside it as if it were a normal container. Put the smiley beside the device instead of trying to place it inside its sealed structure.

Take Quiz

Syntax and Examples

Generated content is defined with content on ::before or ::after.

.message::after {
  content: " ✓";
  color: green;
}
<span class="message">Saved</span>

The browser renders Saved ✓ because <span> can contain normal inline content.

For an input, place a separate element after it when you control the markup:

<div class="field">
  <input id="username" type="text" value="someValue">
  <span class="field-icon" aria-hidden="true">🙂</span>
</div>
.field {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.field-icon {
  color: red;
}

The icon is a real sibling element, not generated content on the input. This is predictable and accessible when the icon is decorative and marked with aria-hidden="true".

If the desired result is a decorative image inside the input's visual area, a background can sometimes be suitable:

.search-input {
  background-image: url("search-icon.svg");
  background-repeat: no-repeat;
  background-position: right 0.6rem center;
  background-size: 1rem;
  padding-right: 2.2rem;
}
<input class="search-input" type="search" placeholder="Search">

The extra right padding prevents typed text from overlapping the background image.

Step by Step Execution

Consider this working example:

<span class="status">Uploading</span>
.status::after {
  content: "...";
  color: steelblue;
}

Step by step:

  1. The browser creates the <span> element containing the text Uploading.
  2. The selector .status::after matches the generated after-content position of that span.
  3. content: "..." tells CSS to generate three dots.
  4. The result is visually similar to this conceptual structure:
<span class="status">
  Uploading
  <!-- generated after-content: ... -->
</span>

Now compare an input:

<input class="status" type="text" value="Uploading">
.status::after {
  content: "...";
}
  1. The input's displayed value is rendered by the form control implementation.
  2. The input cannot have child nodes in HTML.
  3. CSS has no dependable normal-content location in which to place the generated ::after text.
  4. Therefore, do not expect the dots to render after the input.

Real World Use Cases

Generated content is useful when the added content is presentational and belongs naturally to a normal element:

  • Add a required marker after a <label>.
  • Add an external-link arrow after links that open another site.
  • Add separators between navigation items.
  • Add quotation marks around a pull quote.
  • Show a decorative status symbol next to text.
label.required::after {
  content: " *";
  color: #c00;
}

For form controls, common alternatives include:

  • A separate validation message after the input.
  • An icon element positioned beside or over a field.
  • A background icon for a search, calendar, or password field.
  • A component supplied by a UI library that provides input adornments.

For example, validation feedback should usually be real text, not only a generated symbol:

<p id="email-error" class="error">Enter a valid email address.</p>
<input aria-describedby="email-error" type="email">

This makes the message available to assistive technologies.

Take Quiz

Real Codebase Usage

In production code, developers normally avoid relying on pseudo-elements directly on native inputs. Instead, they use one of these patterns.

A field container supplied by the application or component library

<div class="form-field form-field--invalid">
  <input id="email" type="email" aria-invalid="true">
  <span class="form-field__icon" aria-hidden="true">!</span>
</div>

The container can use position: relative, while the icon is positioned independently of the input.

Adjacent-sibling styling

If the third-party control generates an element immediately after the input, style that sibling instead:

<input class="third-party-input" aria-invalid="true">
<span class="validation-icon" aria-hidden="true">⚠</span>
.third-party-input[aria-invalid="true"] + .validation-icon {
  display: inline-block;
  color: #b00020;
}

This only works when the DOM structure actually contains a suitable following sibling.

Background decoration

A background image is useful for a noninteractive, nonessential icon. Ensure there is enough padding and maintain sufficient contrast.

Third-party control extension points

Many controls provide options such as className, containerClass, template, render, suffix, endAdornment, or a callback that runs after rendering. Prefer these documented extension points over fragile CSS assumptions.

JavaScript DOM insertion

If you cannot change the generated HTML but are allowed to run JavaScript, create a real sibling after the control once it exists. This is more reliable than trying to force ::after onto the input.

Common Mistakes

Expecting ::after to work on every element

This is unreliable:

input::after {
  content: "🙂";
}

<input> is a replaced, void form control. Use a sibling, container, or background instead.

Thinking :after versus ::after fixes the issue

input:after { content: "🙂"; }
input::after { content: "🙂"; }

Both selectors refer to the same pseudo-element concept. ::after is the modern syntax, but neither form provides dependable generated content on an input.

Forgetting content

A pseudo-element generally needs content to exist:

/* Broken: there is no generated content */
.note::after {
  color: red;
}
/* Correct */
.note::after {
  content: "!";
  color: red;
}

Using content: url(...) for meaningful information

Generated images and decorative symbols may not be communicated clearly to screen-reader users. If the image conveys an error, success, or required action, provide real text or an accessible label.

Adding a background icon without reserving space

/* The typed text can overlap the icon */
input {
  background: url("search.svg") no-repeat right center;
}

Add padding on the icon side:

input {
  background: url("search.svg") no-repeat right 0.5rem center;
  padding-right: 2rem;
}

Assuming an adjacent-sibling selector can select a previous element

CSS can select a following sibling with input + span, but it cannot use that selector to select a previous sibling. Plan the markup order or use a container when possible.

Comparisons

TechniqueWorks reliably on <input>?Best use
input::before / input::afterNoDo not use for native input decorations
span::before / span::afterYesDecorative content around text or normal containers
Input background-imageYesDecorative icon inside the visual field area
Real sibling elementYesIcons, feedback, and content that needs predictable layout
Wrapper/containerYesPositioning icons and coordinating input states
JavaScript DOM insertionYes, if permittedExtending third-party generated markup
:after::after
Legacy single-colon syntaxModern pseudo-element syntax
Widely supported for compatibilityPreferred in new CSS
Does not solve input limitationsDoes not solve input limitations
Take Quiz

Cheat Sheet

  • Use ::before and ::after to generate presentational content.
  • A pseudo-element normally requires content:
.item::after { content: " →"; }
  • Prefer ::after over the legacy :after spelling.
  • Do not rely on ::before or ::after for <input> elements.
  • <input> is both a void element and a replaced form control.
  • For an icon inside an input's visual area, use background-image and matching padding.
  • For a reliable icon or message next to an input, use a real sibling or wrapper element.
  • Use real text and appropriate ARIA relationships for validation or other meaningful feedback.
  • If third-party markup cannot be changed, look for its template, render callback, container class, or post-render JavaScript hook.

FAQ

Can I use ::after on an input element?

You should not rely on it. Native inputs are replaced elements, and generated content on them is not consistently supported.

Why does ::after work on a span but not on an input?

A <span> has normal content that can be surrounded by generated content. An <input> is a void, browser-rendered form control and does not expose a dependable child-content area.

Does using :after instead of ::after fix the problem?

No. :after is the legacy spelling and ::after is the modern spelling. The limitation is the input element, not the number of colons.

How can I put an icon inside a text input with CSS?

Use a background-image, position it with background-position, and add padding on that side so entered text does not cover it.

How can I place an icon after a third-party input when I cannot add a wrapper?

Check whether the control offers a template, suffix/adornment option, render callback, or container class. If it does not, JavaScript can insert a real sibling after the input if modifying the DOM is allowed.

Is a background image appropriate for an error icon?

It can be decorative, but do not make an icon the only error signal. Provide visible error text and connect it to the input with aria-describedby when appropriate.

Can I use ::after on a button?

Usually yes, because a <button> has normal content and can contain child content. This differs from <input type="button">, which is still an input control.

Take Quiz

Mini Project

Description

Build a search field with a decorative search icon displayed inside the input area. This demonstrates a reliable CSS alternative to input::after: a background image plus padding.

Goal

Create a search input whose typed text never overlaps its search icon.

Requirements

Use an <input type="search"> element. Add a search icon with a CSS background image. Position the icon on the right side of the input. Add enough right padding for the icon. Keep a visible label for accessibility.

Take Quiz

Keep learning

Related questions

View more