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:
::beforeis the first seat.- The element's normal content sits in the middle.
::afteris 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.
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:
- The browser creates the
<span>element containing the textUploading. - The selector
.status::aftermatches the generated after-content position of that span. content: "..."tells CSS to generate three dots.- 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: "...";
}
- The input's displayed value is rendered by the form control implementation.
- The input cannot have child nodes in HTML.
- CSS has no dependable normal-content location in which to place the generated
::aftertext. - 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.
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
| Technique | Works reliably on <input>? | Best use |
|---|---|---|
input::before / input::after | No | Do not use for native input decorations |
span::before / span::after | Yes | Decorative content around text or normal containers |
Input background-image | Yes | Decorative icon inside the visual field area |
| Real sibling element | Yes | Icons, feedback, and content that needs predictable layout |
| Wrapper/container | Yes | Positioning icons and coordinating input states |
| JavaScript DOM insertion | Yes, if permitted | Extending third-party generated markup |
:after | ::after |
|---|---|
| Legacy single-colon syntax | Modern pseudo-element syntax |
| Widely supported for compatibility | Preferred in new CSS |
| Does not solve input limitations | Does not solve input limitations |
Cheat Sheet
- Use
::beforeand::afterto generate presentational content. - A pseudo-element normally requires
content:
.item::after { content: " →"; }
- Prefer
::afterover the legacy:afterspelling. - Do not rely on
::beforeor::afterfor<input>elements. <input>is both a void element and a replaced form control.- For an icon inside an input's visual area, use
background-imageand 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.
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.
Keep learning
Related questions
Add a Favicon to a Static HTML Page
Learn how to add a favicon to static HTML pages, use correct paths and icon formats, and troubleshoot browser caching and local-file issues.
Allow Only Numeric Input (0-9) in HTML Input Using jQuery
Learn how to allow only digits 0-9 in an HTML input using jQuery, with examples, validation tips, common mistakes, and best practices.
Apply CSS !important with jQuery .css()
Learn why jQuery .css() cannot set !important and how to use CSSStyleDeclaration.setProperty() to apply inline important styles safely.