Introduction
Designers often want cleaner link styling while keeping links discoverable. This guide provides concrete, production-ready techniques to remove or alter link underlines using CSS, with explicit patterns for Rails 7 apps, Tailwind CSS v3 workflows, and ActionMailer email templates. Examples include scoped SCSS, Tailwind utility integrations, accessible focus states, security notes for external links, and troubleshooting steps for common pitfalls (specificity, user-agent styles, Turbo partial updates, and email client quirks).
Understanding the Default Underline Behavior
Why underlines exist and what to replace them with
Underlines are a legacy affordance that provides an immediate visual indicator of clickability. When you remove the underline you must replace that affordance with equally clear cues for people using low-vision, keyboard navigation, or color-impaired perception.
- Affordance: Underlines are a rapid visual signal that text is a link.
- Fallback for color issues: Underlines help when link color contrast is marginal.
- Context matters: preserve or reintroduce underlines in dense text, small fonts, or compact UI areas.
WCAG & Contrast Guidelines
Follow the Web Content Accessibility Guidelines (WCAG) when changing link styles. The authoritative source for WCAG guidance is the W3C: W3C. In practice this means:
- Ensure link text color meets WCAG contrast thresholds against its background (WCAG 2.1 AA: at least 4.5:1 for normal text and 3:1 for large text). Use a contrast tool such as WebAIM to verify color combinations.
- If you remove the underline, provide an additional visible state (underline on hover, bold weight, background highlight) so contrast-dependent users still perceive links as interactive.
Using CSS to Remove Underlines: The Text Decoration Property
Core technique (scoped)
Keep link-reset rules scoped to classes or components — avoid a global a { text-decoration: none; }. Use an explicit focus indicator for keyboard users and test with assistive technologies.
/* scoped approach (recommended) */
.link-no-underline {
text-decoration: none;
color: #0077cc; /* verify this color meets contrast per WCAG */
transition: color 150ms ease-in-out, box-shadow 150ms ease-in-out;
}
.link-no-underline:hover { color: #005fa3; text-decoration: underline; }
.link-no-underline:focus-visible {
outline: none;
box-shadow: 0 0 0 4px rgba(0,119,204,0.12);
border-radius: 4px;
}
Security tip: for any link that opens a new tab include rel="noopener noreferrer" with target="_blank" to prevent tabnabbing. Accessibility tip: test focus states with real keyboards and screen readers (VoiceOver, NVDA).
Best Practices for Link Design Without Underlines
Maintain a clear visual hierarchy
Replace underlines with other persistent or interactive cues: stronger color contrast, weight changes, background highlights, icons, or underline-on-hover. For links that act like buttons, style them as buttons using clear affordances (background, padding, and border-radius).
/* example: navigation links */
.nav__link { text-decoration: none; color: #0a66c2; font-weight: 600; }
.nav__link:hover, .nav__link:active { color: #084b8a; text-decoration: underline; }
.nav__link:focus-visible { box-shadow: 0 0 0 3px rgba(10,102,194,0.15); }
Accessibility and security checklist:
- Visible
:focus-visiblestate for keyboard users. - External links:
rel="noopener noreferrer"+target="_blank". - Avoid removing outlines without providing a replacement indicator.
- Manual testing: keyboard-only navigation and popular screen readers.
Responsive Design: Ensuring Links Look Great on All Devices
More than media queries — advanced responsive techniques
Beyond basic media queries, use these patterns to improve discoverability and touch interaction across devices, particularly when using Tailwind CSS v3 in Rails 7 projects:
- Responsive utilities: with Tailwind, toggle underline and padding at breakpoints (e.g.,
md:no-underline sm:underline) to reintroduce affordances on small screens. - Group-based states: use
groupandgroup-hoverto reveal icons or background highlights when siblings are hovered — useful in compact navigation lists. - Increase hit area with pseudo elements or inline-block elements instead of relying solely on text size; ensure touch targets are ~48 CSS pixels when possible.
- Container queries (where supported) let you change link presentation based on component size rather than viewport size — useful for components reused in narrow sidebars and wide content areas.
- Progressive enhancement: reintroduce underlines or button-like styles in contexts where other cues are unavailable (dense lists, small font sizes).
/* increase tappable area with pseudo-element */
.link-no-underline { position: relative; }
.link-no-underline::after {
content: "";
position: absolute;
inset: -6px; /* expands hit area without changing layout */
}
Testing: use device emulators and physical devices. In Rails apps, verify Tailwind breakpoint classes are compiled correctly and that any server-side rendering or partial replacement keeps responsive classes intact.
Link Styling in Ruby on Rails Projects
Using link_to and view helpers (practical patterns)
Add semantic classes via the link_to helper instead of targeting all anchors globally. Add visually hidden labels for icon-only links and prefer component-scoped styles.
# app/views/users/_profile.html.erb
<%= link_to "View profile", user_path(@user), class: "link-no-underline" %>
# accessible icon link
<%= link_to user_path(@user), class: "link-no-underline" do %>
<span class="icon-user" aria-hidden="true"></span>
<span class="sr-only">View profile</span>
<% end %>
Provide an .sr-only utility (example below) and include this SCSS in your component stylesheet or Tailwind layer.
/* visually hidden utility */
.sr-only { position: absolute !important; width: 1px !important; height: 1px !important; padding: 0 !important; margin: -1px !important; overflow: hidden !important; clip: rect(0, 0, 0, 0) !important; white-space: nowrap !important; border-width: 0 !important; }
Asset placement & Tailwind in Rails 7
Common placements depending on your Rails 7 setup:
- Sprockets: component SCSS in
app/assets/stylesheets/components/_links.scssand import fromapplication.scss. - Import maps / esbuild: keep component styles in
app/javascript/stylesand import into your build chain. - Tailwind v3: use utility classes for most link styles; extract repeated patterns into @apply-based components or Tailwind components in your
tailwind.config.js.
Emails & ActionMailer considerations
Email clients have inconsistent CSS support. For transactional emails use inline styles for critical states or use an inliner such as the premailer-rails gem to inline styles before delivery. Always validate the rendered HTML produced by mailers in several clients (web and desktop).
# Gemfile
gem 'premailer-rails'
# app/views/user_mailer/welcome.html.erb
<a href="<%= root_url %>" style="text-decoration:none;color:#007BFF;">Visit site</a>
Common pitfalls in large Rails apps
- Global anchor resets remove site-wide discoverability — prefer scoped classes.
- Specificity wars: avoid
!important; increase specificity or refactor the cascade. - Turbo/Hotwire: reapply JS-driven behaviors after partial updates (listen for
turbo:loadorturbo:frame-loadevents). - Mailer CSS inlining — always validate the final HTML output from the mailer pipeline.
Tailwind v3 Integration Example
The example below shows a responsive, accessible link styled using Tailwind CSS v3 utilities directly in a Rails link_to. This creates a button-like link on small screens and a compact, underline-on-hover link on larger screens. Tailwind focus utilities produce a consistent focus ring for keyboard users.
# app/views/shared/_cta_link.html.erb
<%= link_to 'Get started', signup_path, class: "no-underline text-blue-600 font-semibold hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 rounded px-2 py-1 sm:inline-block md:px-4 lg:px-3 sm:bg-transparent sm:text-inherit md:bg-transparent lg:bg-transparent sm:px-0 md:px-4" %>
Explanation of key utilities used (Tailwind v3):
no-underline— removes default underline at the base breakpoint.hover:underline— reintroduces underline on hover for discoverability.focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2— accessible focus ring for keyboard users.- Responsive padding (
sm: md:) — changes tappable area across breakpoints.
Tip: if you use Tailwind component extraction, move the repeated utility set into a custom component class in @layer components inside your Tailwind stylesheet for reusability and easier testing.
Interactive Playground (copy-paste demo)
Copy the HTML/CSS below into any online editor such as CodePen or into a local HTML file to try the pattern interactively.
<!-- demo.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<style>
.demo-link { text-decoration: none; color: #0a66c2; transition: color .15s ease; }
.demo-link:hover { text-decoration: underline; color: #084b8a; }
.demo-link:focus-visible { outline: none; box-shadow: 0 0 0 4px rgba(10,102,194,0.12); border-radius: 6px; }
@media (max-width: 480px) { .demo-link { display: inline-block; padding: 12px 16px; background:#0a66c2; color: #fff; border-radius:6px; } }
</style>
</head>
<body>
<p>Normal flow link: <a href="#" class="demo-link">Read more</a></p>
<p>Icon link (screen-reader text): <a href="#" class="demo-link"><span aria-hidden="true">🔗</span><span class="sr-only">Open resource</span></a></p>
</body>
</html>
Try toggling viewport width to see the mobile-friendly button-like behavior. This immediate feedback helps validate touch targets and focus states before rolling changes to production.
Conclusion: Enhancing User Experience with Custom Link Styles
Removing underlines can produce a cleaner UI without harming usability if you supply equivalent cues: color contrast that meets WCAG thresholds, clear focus indicators, hover feedback, and adequate touch targets. In Rails 7 with Tailwind v3, prefer utility classes on link_to, encapsulate repeated patterns into Tailwind components, and inline critical styles for emails using premailer-rails when necessary.
/* Recommended pattern: visible feedback without baseline underline */
.link-no-underline { text-decoration: none; color: #007BFF; }
.link-no-underline:hover { text-decoration: underline; }
.link-no-underline:focus-visible { box-shadow: 0 0 0 3px rgba(0,123,255,0.15); }
Before deploying, run keyboard-only and screen reader checks, test mailers in multiple clients, and validate color contrast with a trusted tool.
Key Takeaways
- Scope underline removal with classes (e.g.,
.link-no-underline) rather than a global reset. - Always provide a visible
:focus-visiblestate and other interactive cues (hover, weight, background). - For Rails, add classes using
link_to, manage styles in component SCSS or Tailwind v3 files, and inline critical email styles withpremailer-rails. - Test on mobile/touch, keyboard-only navigation, and email clients; reintroduce underlines in dense or small-text contexts if needed.
Frequently Asked Questions
- How can I remove the underline from all links on my website?
- Technically you can use
a { text-decoration: none; }, but that removes a widespread affordance. It is better to apply a scoped class like.link-no-underlinewhere appropriate and ensure alternative affordances are present. - What if my links still show underlines despite the CSS change?
- Inspect computed styles in browser devtools. Check for user-agent styles, higher-specificity selectors, or CSS loaded later in the cascade. Ensure your stylesheet is included after vendor styles and that selectors are specific enough.
- Can I add hover effects to links after removing the underline?
- Yes. Use hover and focus styles (color change, underline on hover, background highlight) for clear interactive feedback. On touch devices, prefer larger hit targets or convert inline links into full-width, button-like controls.
