My Go-To CSS Pseudo-Element Use Cases
By ngockhoi96 on
Minutes read: 12 min read
Hey fellow developers! In the vast world of CSS, pseudo-elements like ::before, ::after, ::first-letter, and ::placeholder stand out as unsung heroes. They empower us to craft intricate UI details, add functional flair, and style specific content parts—all without cluttering our HTML with unnecessary elements. This commitment to clean, semantic markup is a cornerstone of professional frontend development, and pseudo-elements are key to achieving it while delivering sophisticated user experiences.
Today, I’m excited to share a curated collection of my most relied-upon pseudo-element techniques. These aren’t just neat tricks; they’re battle-tested solutions that solve common UI challenges, enhance aesthetics, and even boost performance. Let’s dive in and explore how you can leverage them in your own projects!
Highlighting interactive elements like links on hover is a fundamental UX pattern. However, a naive font-weight change can trigger layout shifts, negatively impacting Core Web Vitals (specifically Cumulative Layout Shift - CLS). Pseudo-elements offer a refined, CLS-friendly solution.
The Core Concept:
Instead of altering the original text’s properties, we introduce a ::before pseudo-element containing the same text content. This pseudo-element, styled with the bolder font-weight, is absolutely positioned directly atop the original text. It’s initially transparent (opacity: 0) and gracefully fades in on hover. The original element’s dimensions remain unchanged, thus preventing any layout jank.
Implementation Insights:
The provided HTML snippet uses a data-content attribute on the anchor tag to hold the text for the pseudo-element. The CSS then uses content: attr(data-content); to pull this text into the ::before element. Absolute positioning ensures precise overlay, while pointer-events: none; is crucial to allow clicks and other interactions to pass through to the underlying anchor tag.
HTML:
<p> This is my personal blog, <a href="[https://blog.ngockhoi96.dev](https://blog.ngockhoi96.dev)" data-content="blog.ngockhoi96.dev" > blog.ngockhoi96.dev </a> where I share my thoughts and experiences.</p>CSS:
a { position: relative; text-decoration: none; color: green;}
a::before { content: attr( data-content ); /* Pulls text from the 'data-content' attribute */ position: absolute; top: 0; left: 0; width: auto; /* Or match parent's width if necessary */ color: green; /* Or your desired hover color */ font-weight: 600; /* The heavier font weight for the hover state */ opacity: 0; /* Hidden by default */ transition: opacity 0.1s ease-in-out; pointer-events: none; /* Allows clicks to pass through to the actual link below */}
a:hover::before { opacity: 1; /* Fade in on hover */}Key Technical Takeaways:
- Performance: This technique is excellent for performance as
opacitytransitions are highly optimized by browsers (often GPU-accelerated). - Accessibility: Ensure the
colorprovides sufficient contrast in both normal and “hovered” (pseudo-element visible) states. - Maintainability: Using
data-contentkeeps the duplicated text within the HTML, which can be good for SEO or if the text is dynamic. For static text, you could also hardcode it in thecontentproperty, butattr()is more flexible.
He said, That’s interesting.
. HAL answers: I’m sorry, Dave. I’m afraid I can’t do that.
Avian carriers can provide high delay, low throughput, and low altitude service. The connection topology is limited to a single point-to-point path for each carrier, used with standard carriers, but many carriers can be used without significant interference with each other, outside early spring. This is because of the 3D ether space available to the carriers, in contrast to the 1D ether used by IEEE802.3. The carriers have an intrinsic collision avoidance system, which increases availability.
“That’s interesting, I didn’t know that. I thought it was just a joke.”
— ngockhoi96, bebebe
How does setRequestLocale work?
This is the detailed content that can be shown or hidden. It was initially open.
test-code, test-highlight, test-bold, test-link are all supported.
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dolorum eaque, doloribus atque vero fugiat iure dicta, quisquam earum aliquam ipsa eligendi quas temporibus libero nesciunt aperiam aliquid. Similique, iure temporibus!
- Item 1
- Item 2
- Item 3
function example() { console.log('This is an example code block');}Example 1: Standard Left-Aligned Table
| Feature | rem | em |
|---|---|---|
| Relative To | Font-size of the root (<html>) element | Font-size of the direct parent element |
| Use Case | Consistent spacing and sizing across the entire site | Sizing elements relative to their immediate context |
| Advantage | Predictable and easy to manage for global style changes | Useful for modular components that scale internally |
| Common Pitfall | (none) | Can lead to compounding issues if nested deeply |
Example 2: Table with Different Alignments
| Item | Availability | Price (USD) |
|---|---|---|
| RGB Keyboard | In Stock | 95.00 |
| Wireless Mouse | In Stock | 49.50 |
| 4K Monitor | Out of Stock | 399.99 |
| USB-C Hub | In Stock | 72.25 |
This example shows a standard table with headers at the top.
| Country | Capital | Flag |
|---|---|---|
| Vietnam | Hanoi | 🇻🇳 |
| Japan | Tokyo | 🇯🇵 |
| France | Paris | 🇫🇷 |
This example shows how to use the same components to create a vertical table, which is great for displaying properties of a single item. Note the use of scope="row" for accessibility.
| Name | Astro Framework |
|---|---|
| Type | Static Site Generator |
| Website | astro.build |
This table showcases a full data structure including a caption, header, body, and footer, along with colspan and rowspan attributes for complex layouts.
| Region | Product | Jan | Feb | Mar |
|---|---|---|---|---|
North America | Widgets | 150 | 180 | 210 |
| Gadgets | 120 | 130 | 145 | |
| Europe | Widgets | 110 | 100 | 130 |
| Total Revenue: | 380 | 410 | 485 | |

Standard HTML list bullets often lack the visual finesse required by modern designs. While the ::marker pseudo-element is gaining traction for direct styling of list markers, ::before offers greater flexibility for more complex custom bullet designs, including icons or intricate shapes.
The Core Concept:
We begin by stripping the default list styling using list-style: none;. Then, each <li> element’s ::before pseudo-element is employed to inject and style our custom marker. This marker can range from a simple Unicode character or SVG icon to more elaborate CSS-generated shapes.
Implementation Insights:
The <li> is set to position: relative; to act as a containing block for its ::before pseudo-element, which is then position: absolute;. This allows precise placement of the custom marker. padding-left on the <li> carves out space for the marker, preventing text overlap.
HTML:
<ul> <li> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </li> <li> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </li> <li> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </li></ul>CSS:
ul { list-style: none; /* Removes default browser bullets */ padding-left: 0; /* Reset default padding if you want full control */}
ul li { position: relative; /* For positioning the ::before pseudo-element */ padding-left: 24px; /* Creates space for our custom bullet */ margin-bottom: 0.5rem; /* Optional: for spacing between list items */}
ul li::before { content: '\u2022'; /* Your custom bullet character. Try '\u2794', '\u2713', '\u2605', etc. */ position: absolute; top: -1px; /* Adjust for precise vertical alignment */ left: 8px; /* Adjust for horizontal placement */ /* You can also style the bullet's color, size, etc. */ /* color: blueviolet; */ /* font-weight: bold; */}Key Takeaways:
list-style: none; clears the defaults, and then position: relative on the li with position: absolute on its ::before gives you precise control over the bullet’s appearance and placement.
Instead of a static text-decoration: underline, a more dynamic and visually appealing approach is to create an underline that animates on hover. Pseudo-elements are perfect for this.
The Idea:
We’ll use an ::before (or ::after) pseudo-element styled as a thin line. It will be positioned absolutely beneath the link text, starting with a width of 0. On hover, we’ll animate its width to 100%.
Implementation:
HTML:
<p> Lorem ipsum dolor sit amet, consectetur <a href="#">adipiscing elit</a>, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>CSS:
a { position: relative; display: inline-block; /* Allows for proper positioning of the absolute pseudo-element */ text-decoration: none; /* Remove default underline */ color: blueviolet; padding-bottom: 2px; /* Optional: Small space so underline isn't touching text */}
a::before { content: ''; position: absolute; left: 0; bottom: -2px; /* Position it just below the text (adjust as needed) */ width: 0; height: 2px; background-color: blueviolet; border-radius: 2px; transition: width 0.2s ease-in-out;}
a:hover::before { width: 100%;}Key Takeaways:
The combination of position: relative on the link, position: absolute on the pseudo-element, and a CSS transition on the width property creates this slick effect.
It’s good UX to visually signify when a link will navigate the user away from the current site. An SVG icon added with a pseudo-element is a clean and scalable way to do this.
The Idea:
We target links (e.g., those with target="_blank" or a specific class) and use their ::after pseudo-element. This pseudo-element won’t have text content but will use mask-image (or background-image) to display an SVG icon. CSS custom properties can make it easily themable.
Implementation:
(Ensure you have an SVG for the external link icon. For this example, I’ll use an inline data URI for a common external link icon, which you can replace with a path to your SVG file like url('/assets/external-link.svg'))
HTML:
<p> Lorem ipsum dolor sit amet, consectetur <a href="https://google.com" target="_blank" rel="noopener noreferrer" class="external-link" > adipiscing elit </a >, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>CSS:
.external-link { --clr-primary: blueviolet; /* CSS variable for icon/text color */ --icon-size: 0.875em; /* CSS variable for icon size */
/* display: inline-block; if needed for consistent vertical alignment */ color: var(--clr-primary); text-decoration: underline; text-decoration-thickness: auto; text-underline-offset: 0.1em;}
.external-link:hover { text-decoration: none;}
.external-link[target='_blank']::after { content: ''; display: inline-flex; align-items: center; margin-left: 4px; width: var(--icon-size); height: var(--icon-size); background-color: var(--clr-primary); /* SVG Data URI for an external link icon. Replace with your own SVG path if preferred. */ mask-image: url("data:image/svg+xml,%3Csvg xmlns='[http://www.w3.org/2000/svg](http://www.w3.org/2000/svg)' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M10 6V8H5V19H16V14H18V19C18 20.1 17.1 21 16 21H5C3.9 21 3 20.1 3 19V8C3 6.9 3.9 6 5 6H10ZM19 2H12V4H17L11.5 9.5L12.92 10.92L18.41 5.41L18 10H20V3C20 2.45 19.55 2 19 2Z'/%3E%3C/svg%3E"); mask-size: cover; /* Ensures the icon covers the area */ mask-repeat: no-repeat;}Key Takeaways:
Using mask-image with background-color allows you to color a monochrome SVG icon directly with CSS, making it highly adaptable to your site’s theme.
A “drop cap” is a large, decorative initial letter of a paragraph, often seen in books and magazines. The ::first-letter pseudo-element makes this typographic flourish straightforward to implement.
The Idea:
We simply target the ::first-letter pseudo-element of a specific paragraph class and apply styles to increase its size, change its color, and adjust its positioning.
Implementation:
HTML:
<article> <p class="drop-cap"> Scientists exploring the depths of Monterey Bay unexpectedly encountered a rare and unique species of dragonfish. This species is the rarest of its species. </p></article>CSS:
.drop-cap::first-letter { font-size: 3rem; font-weight: 700; color: orange; float: left; /* Allows the rest of the text to wrap around it */ line-height: 0.8; /* Adjust to pull subsequent lines closer vertically. Varies by font. */ padding-right: 0.2em; /* Space between the drop cap and the rest of the text */ padding-top: 0.1em; /* Fine-tune top spacing */ /* You may need to adjust margin/padding depending on the font used */}Key Takeaways:
::first-letter is the dedicated pseudo-element for this. float: left; is crucial for the text wrapping behavior, and you’ll often need to fine-tune line-height and padding for a polished look.
Default placeholder text in form inputs can be quite plain. The ::placeholder pseudo-element gives us control over its appearance.
The Idea:
Target the ::placeholder pseudo-element of an input field to customize properties like its color, font-weight, and font-style.
Implementation:
HTML:
<div class="form-item"> <label for="email">Your email</label> <input type="email" id="email" placeholder="example@gmail.com" /></div>CSS:
.form-item { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1rem; /* Spacing for multiple form items */}
.form-item label { font-weight: 600; font-size: 1.125rem;}
.form-item input { height: 2.5rem; /* A bit more height for easier interaction */ font-size: 1rem; padding-inline: 0.75rem; border: 1px solid #ccc; border-radius: 4px;}
.form-item input::placeholder { color: #aaa; /* A common light grey for placeholders */ font-weight: 500; /* Your original weight */ font-style: italic; /* Often placeholders are italicized for distinction */ opacity: 1; /* Ensure full opacity, some browsers might reduce it by default */}
/* For broader compatibility with older browsers, you might see prefixes, but ::placeholder itself is widely supported now. e.g., input::-webkit-input-placeholder { ... }*/Key Takeaways:
::placeholder provides a simple yet effective way to ensure your form elements align with your site’s overall design language. Be mindful of color contrast for accessibility.
And there you have it - some of my most-used CSS pseudo-element techniques! They offer a fantastic way to add polish and functionality to web interfaces without compromising the integrity of your HTML.
What are your favorite pseudo-element tricks or use cases? I’d love to hear about them in the comments!
Adding a gradient overlay to an image is a common design technique used to improve text legibility when text is placed over an image, or simply for aesthetic effect. Pseudo-elements make this easy without needing extra HTML wrapper elements.