Bracket Hover
[ ] brackets that slide in and fade on hover — 100% CSS navigation effect, no JavaScript, compatible with any framework.
You'll get access to the interactive demo with a free account.
This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Navigation has 36 effects, including 4 free. Explore the category →
3 usage examples



How it works
The effect relies entirely on the ::before and ::after pseudo-elements of the .link-bracket class. Each pseudo-element holds a character ("[" and "]") initially hidden (opacity: 0) and inset (translateX(+10px) for ::before, translateX(-10px) for ::after). On hover, a single transition: .3s brings both properties to their final values — the brackets slide in and appear simultaneously.
The spacing between the bracket and the text is handled by the combination of margin-right/margin-left (0 px at rest → 5 px on hover) and the return to translateX(0). Both properties animate together in the same transition, producing an opening motion — as if the brackets were spreading away from the word rather than simply appearing beside it.
The text color transitions in parallel from --text-secondary (#a1a1aa) to --text-primary (#ffffff) via transition: color .3s on .nav-link-demo. Both transitions (text + brackets) share the same duration, synchronising them without extra code.
No JavaScript is used. Simply add the .link-bracket class to any inline element — <a>, <span>, <li> — that already has the base styles, and the effect activates through pure CSS. Zero dependencies, zero bundle.
Accessibility
- prefers-reduced-motion absent: the source code contains no
prefers-reduced-motionmedia query. The animation (slide + fade) fires regardless of system preferences. For motion-sensitive users, add@media (prefers-reduced-motion: reduce) { .link-bracket::before, .link-bracket::after { transition: opacity .3s; transform: none; } }. - Resting contrast:
#a1a1aaon#0a0a0f— ratio ≈ 7.9:1, above WCAG AA (4.5:1) and AAA (7:1) thresholds for body text. On hover (#ffffff), ratio = 21:1. - The demo elements are non-focusable
<span>tags. In production, use native<a href>links: they are keyboard-focusable, support:visited, and are correctly announced by screen readers — the effect CSS applies unchanged. - No
aria-*attributes are present in the demo. For a main navigation, wrap the links in a<nav aria-label="Main navigation">and usearia-current="page"on the active link. - The brackets generated by
::before/::afterare purely decorative. They are ignored by screen readers (CSS pseudo-elements are not exposed in the accessibility tree).
Browser compatibility
Requires CSS pseudo-elements and transitions — supported in all modern browsers since 2013. Zero JavaScript dependency.
On browsers without CSS transition support, the brackets appear and disappear instantly on hover — the effect still works, with no errors.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<nav class="nav-demo">
<a class="nav-link-demo link-bracket" href="#">Home</a>
<a class="nav-link-demo link-bracket" href="#">Services</a>
<a class="nav-link-demo link-bracket" href="#">Contact</a>
</nav>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| --text-secondary | #a1a1aa | Link color at rest. Adapt to your brand palette — maintain a ratio ≥ 4.5:1 against the background. |
| --text-primary | #ffffff | Text and bracket color on hover. |
| --space-8 | 2rem | Gap between navigation items. Reduce to 1rem for a compact vertical list. |
| --space-2 | 0.5rem | Vertical padding on each link — enlarges the click area. |
| transition | .3s | Animation duration for brackets and color. Set in .link-bracket::before, .link-bracket::after and .nav-link-demo. Lower to .15s for a snappier feel, raise to .5s for a softer one. |
| translateX (rest) | 10px / -10px | Initial slide distance of the brackets — translateX(10px) in .link-bracket::before, translateX(-10px) in .link-bracket::after. Increase for a more pronounced slide. |
| margin (hover) | 5px | Gap between bracket and text on hover — margin-right: 5px in .link-bracket:hover::before, margin-left: 5px in .link-bracket:hover::after. |
| content | "[" / "]" | Characters used as brackets — swap for "«" / "»", "<" / ">", or "(" / ")" for stylistic variations. |
FAQ
opacity, transform, and transition. No script is included. You can integrate it into any framework (React, Vue, Svelte, Angular) without a dedicated component: just apply the CSS classes to your navigation elements.content property in .link-bracket::before (e.g. content: "«") and .link-bracket::after (e.g. content: "»"). Works with any Unicode character, emoji, or short string. Adjust margin-right/margin-left on hover if the replacement character is wider than the original bracket.nav-link-demo link-bracket classes directly to your <a> tags. The demo uses <span> for neutrality, but in production native links are preferred: they are keyboard-focusable, tracked as visited, and correctly announced by screen readers. The CSS effect applies identically.