Skip to content

[EUILink] Add an explicit underline offset - #10248

Merged
andreadelrio merged 4 commits into
elastic:mainfrom
andreadelrio:link-ants
Oct 29, 2026
Merged

andreadelrio merged 4 commits into
elastic:mainfrom
andreadelrio:link-ants

Conversation

@andreadelrio

@andreadelrio andreadelrio commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • What: Added text-underline-offset: 0.15em to EuiLink's underline styles (euiLinkCSS).
  • Why: EuiLink never set an underline offset, so browsers used the font's default position. In Chrome and Safari that leaves only a ~1px gap between the text and the dotted underline at every font size, which blurs into the letters after anti-aliasing and makes the dots look stuck to the text. It's most visible in Kibana for links inside EuiText size="xs" (12px).
Before After
Kibana (Lens flyout, 100% zoom)¹ image image
EUI docs (EuiLink) image image

¹ Kibana "after" screenshot applies text-underline-offset: 0.15em to .euiLink via browser DevTools.

  • How:
    • The offset is in em so it scales with font size: ~2px at 12–16px, and proportionally more for larger text (e.g. links in headings).
    • The offset persists across all states (default dotted, hover/focus solid, focus thick), so the underline doesn't move on hover.
    • euiLinkCSS is also used for plain <a> tags inside EuiText / EuiMarkdownFormat, and interactive EuiBreadcrumbs render EuiLink, so all of those get the fix with no extra changes.
    • Other underlines in EUI (hover underlines on EuiButtonEmpty, badges, side nav, etc.) are solid hover-only affordances and were left as they are.
    • The underline position should be fixed in CSS rather than through font changes: Kibana (Inter 3.x) and the docs site (Inter 4) both showed the issue, and both measured the same with the fix.

Gap between text and underline

Font size Chrome Safari Firefox
12px 1px → 2px 1px → 2px 2px → 2px
14px 1px → 2px 1px → 2px 2px → 2px
16px 1px → 2px 1px → 2px 3px → 2px

Before → after, measured on a regular (1×) screen. On Retina screens, 12px text can land half a pixel off because of how browsers round.

Same at 12px, 14px and 16px. With the fix, every browser gets the same ~2px gap, and the gap grows with larger text.

Other values considered
Value 12px 14px 16px Notes
0.125em 1–2px 2px 2px Makes Firefox worse than today at 12px (2px → 1px)
0.15em 2px 2px 2px Consistent everywhere
0.2em 2–3px 3px 3px Starts to look detached at 14–16px
2px 2px 2px 2px Same as 0.15em at these sizes, but doesn't scale with larger text

Ranges show differences between browsers. Measured in Chrome, Firefox and Safari (via Playwright) at 1× and 2× device pixel ratio.

API Changes

component / parent prop / child change description
N/A No API changes

Impact Assessment

  • 🔴 Breaking changes — None.
  • 💅 Visual changes — The underline on every EuiLink (and plain links inside EuiText / EuiMarkdownFormat) sits slightly lower: ~1px further from the text in Chrome and Safari. Consumers with their own text-underline-offset override are unaffected; overrides of text-decoration alone keep the new offset.
  • 🧪 Test impact — No Jest snapshot changes (full suite passes, 2957 snapshots). VRT references that show links may need updating.
  • 🔧 Hard to integrate — No.

Impact level: 🟢 Low

Release Readiness

  • Documentation: No docs changes needed.
  • Figma: {link if the Figma link component should be updated to match}
  • Migration guide: Not needed; no API changes.
  • Adoption plan: Not a new feature.

QA instructions for reviewer

  • Open the EuiLink and EuiText pages in the docs site or Storybook.
  • At 12px (EuiText size="xs"), 14px (size="s") and 16px (default), confirm the dotted underline has a visible gap below the text and doesn't touch the letters.
  • Hover and focus a link: the underline turns solid (thicker on focus) without moving up or down.
  • Check a disabled link still has no underline.
  • Check words with descenders (g, p, y): the underline should still break around them.
  • Check a link that wraps onto multiple lines.
  • Check an external link (with the icon) and an EuiBreadcrumbs link.
  • Repeat in light and dark mode, in Chrome, Safari and Firefox.

Checklist before marking Ready for Review

Reviewer checklist

  • Approved Impact Assessment — Acceptable to merge given the consumer impact.
  • Approved Release Readiness — Docs, Figma, and migration info are sufficient to ship.

@andreadelrio andreadelrio changed the title Add an explicit to EuiLink [EUILink] Add an explicit underline offset Oct 27, 2026
@elastic-vault-github-plugin-prod

Copy link
Copy Markdown

📷 122 visual difference(s) found

Look at the visual diff below. If everything is expected, run Approve visual changes to update baselines, re-run the job or make appropriate fixes.

See the visual regression testing wiki for more information.

Expand to review

euibasictable (18 differences)

StoryDiff %BeforeAfterDiff
dark mode desktop 0.02%
expanded nested table desktop 0.03%
expanded nested table without background desktop 0.03%
expanded row desktop 0.03%
high contrast mobile desktop 0.03%
large expanded row desktop 0.01%
marked row desktop 0.03%
panelled desktop 0.04%
playground desktop 0.03%
dark mode mobile 0.09%
expanded nested table mobile 0.09%
expanded nested table without background mobile 0.10%
expanded row mobile 0.10%
high contrast mobile mobile 0.10%
large expanded row mobile 0.07%
marked row mobile 0.10%
panelled mobile 0.10%
playground mobile 0.10%

euibreadcrumbs (2 differences)

StoryDiff %BeforeAfterDiff
dark mode desktop 1.26%
playground desktop 1.26%

euicollapsiblenav (2 differences)

StoryDiff %BeforeAfterDiff
full header pattern desktop <0.01%
full header pattern mobile 0.02%

euicombobox (4 differences)

StoryDiff %BeforeAfterDiff
groups desktop <0.01%
nested options groups desktop <0.01%
groups mobile 0.01%
nested options groups mobile 0.01%

euidatagrid (50 differences)

StoryDiff %BeforeAfterDiff
cell actions desktop 0.14%
cell expansion popover desktop 0.14%
column actions desktop 0.14%
custom header content desktop 0.14%
draggable columns desktop 0.14%
header visibility desktop 0.06%
playground desktop 0.14%
virtualization desktop 0.03%
visible columns desktop 0.20%
compact desktop 0.14%
expanded desktop 0.08%
horizontal lines desktop 0.09%
minimal desktop 0.14%
playground desktop 0.09%
row classes desktop 0.09%
auto desktop 0.08%
custom line height control column desktop 0.07%
custom line height desktop 0.03%
custom row heights desktop 0.06%
line count 1 desktop 0.04%
line count desktop 0.08%
static height desktop 0.03%
no toolbar desktop 0.14%
render custom toolbar desktop 0.14%
toolbar visibility options desktop 0.14%
cell actions mobile 0.24%
cell expansion popover mobile 0.23%
column actions mobile 0.20%
custom header content mobile 0.24%
draggable columns mobile 0.24%
header visibility mobile 0.12%
playground mobile 0.24%
virtualization mobile 0.12%
visible columns mobile 0.20%
compact mobile 0.29%
expanded mobile 0.14%
horizontal lines mobile 0.17%
minimal mobile 0.24%
playground mobile 0.17%
row classes mobile 0.17%
auto mobile 0.21%
custom line height control column mobile 0.23%
custom line height mobile 0.13%
custom row heights mobile 0.18%
line count 1 mobile 0.15%
line count mobile 0.26%
static height mobile 0.13%
no toolbar mobile 0.24%
render custom toolbar mobile 0.24%
toolbar visibility options mobile 0.24%

euiemptyprompt (8 differences)

StoryDiff %BeforeAfterDiff
high contrast desktop 0.06%
high contrast transparent desktop 0.06%
page template desktop 0.01%
playground desktop 0.06%
high contrast mobile 0.06%
high contrast transparent mobile 0.06%
page template mobile 0.04%
playground mobile 0.06%

euiform (2 differences)

StoryDiff %BeforeAfterDiff
custom content desktop 0.29%
custom content mobile 1.14%

euiheader (6 differences)

StoryDiff %BeforeAfterDiff
elastic navigation pattern desktop <0.01%
playground desktop 0.07%

Table truncated - see the Buildkite annotation for the full diff.

@andreadelrio
andreadelrio marked this pull request as ready for review October 28, 2026 00:27
@andreadelrio
andreadelrio requested a review from a team as a code owner October 28, 2026 00:27
Copilot AI balanced review requested due to automatic review settings October 28, 2026 00:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The link underline-spacing update has no active review issues.

No open issues 🎉

What changed in this PR

Adds an explicit underline offset to shared EuiLink styles, improving separation between text and underlines at small font sizes without changing the public API.

Changes:

  • Sets text-underline-offset: 0.15em so spacing scales with font size and remains consistent across interaction states.
  • Adds a changelog entry documenting the visual fix.
File Description
packages/​eui/​src/​components/​link/​link.styles.ts Adds the underline offset and an explanatory comment.
packages/​eui/​changelogs/​upcoming/​10248.md Documents the link underline spacing fix.

Review effort: Balanced

@elastic-vault-github-plugin-prod

Copy link
Copy Markdown

📷 122 visual difference(s) found

Look at the visual diff below. If everything is expected, run Approve visual changes to update baselines, re-run the job or make appropriate fixes.

See the visual regression testing wiki for more information.

Expand to review

euibasictable (18 differences)

StoryDiff %BeforeAfterDiff
dark mode desktop 0.02%
expanded nested table desktop 0.03%
expanded nested table without background desktop 0.03%
expanded row desktop 0.03%
high contrast mobile desktop 0.03%
large expanded row desktop 0.01%
marked row desktop 0.03%
panelled desktop 0.04%
playground desktop 0.03%
dark mode mobile 0.09%
expanded nested table mobile 0.09%
expanded nested table without background mobile 0.10%
expanded row mobile 0.10%
high contrast mobile mobile 0.10%
large expanded row mobile 0.07%
marked row mobile 0.10%
panelled mobile 0.10%
playground mobile 0.10%

euibreadcrumbs (2 differences)

StoryDiff %BeforeAfterDiff
dark mode desktop 1.26%
playground desktop 1.26%

euicollapsiblenav (2 differences)

StoryDiff %BeforeAfterDiff
full header pattern desktop <0.01%
full header pattern mobile 0.02%

euicombobox (4 differences)

StoryDiff %BeforeAfterDiff
groups desktop <0.01%
nested options groups desktop <0.01%
groups mobile 0.01%
nested options groups mobile 0.01%

euidatagrid (50 differences)

StoryDiff %BeforeAfterDiff
cell actions desktop 0.14%
cell expansion popover desktop 0.14%
column actions desktop 0.14%
custom header content desktop 0.14%
draggable columns desktop 0.14%
header visibility desktop 0.06%
playground desktop 0.14%
virtualization desktop 0.03%
visible columns desktop 0.20%
compact desktop 0.14%
expanded desktop 0.08%
horizontal lines desktop 0.09%
minimal desktop 0.14%
playground desktop 0.09%
row classes desktop 0.09%
auto desktop 0.08%
custom line height control column desktop 0.07%
custom line height desktop 0.03%
custom row heights desktop 0.06%
line count 1 desktop 0.04%
line count desktop 0.08%
static height desktop 0.03%
no toolbar desktop 0.14%
render custom toolbar desktop 0.14%
toolbar visibility options desktop 0.14%
cell actions mobile 0.24%
cell expansion popover mobile 0.23%
column actions mobile 0.20%
custom header content mobile 0.24%
draggable columns mobile 0.24%
header visibility mobile 0.12%
playground mobile 0.24%
virtualization mobile 0.12%
visible columns mobile 0.20%
compact mobile 0.29%
expanded mobile 0.14%
horizontal lines mobile 0.17%
minimal mobile 0.24%
playground mobile 0.17%
row classes mobile 0.17%
auto mobile 0.21%
custom line height control column mobile 0.23%
custom line height mobile 0.13%
custom row heights mobile 0.18%
line count 1 mobile 0.15%
line count mobile 0.26%
static height mobile 0.13%
no toolbar mobile 0.24%
render custom toolbar mobile 0.24%
toolbar visibility options mobile 0.24%

euiemptyprompt (8 differences)

StoryDiff %BeforeAfterDiff
high contrast desktop 0.06%
high contrast transparent desktop 0.06%
page template desktop 0.01%
playground desktop 0.06%
high contrast mobile 0.06%
high contrast transparent mobile 0.06%
page template mobile 0.04%
playground mobile 0.06%

euiform (2 differences)

StoryDiff %BeforeAfterDiff
custom content desktop 0.29%
custom content mobile 1.14%

euiheader (6 differences)

StoryDiff %BeforeAfterDiff
elastic navigation pattern desktop <0.01%
playground desktop 0.07%

Table truncated - see the Buildkite annotation for the full diff.

@angeles-mzb
angeles-mzb self-requested a review October 28, 2026 08:34
Comment thread packages/eui/changelogs/upcoming/10248.md Outdated

@angeles-mzb angeles-mzb left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested different links and sizes in docs and storybook and they look better in terms of readability.

I did notice that on keyboard navigation on Chrome the focus ring is now too close to the underline. But since trying to fix that will probably take us back where we started (with an underline to close to the text) and since it is noticeable mainly for Chrome I think changes should be ok:

Chrome:

Before After
Image Image

Safari:

Before After
Image Image

Firefox:

Before After
Image Image

kibanamachine and others added 2 commits October 29, 2026 14:10
Co-authored-by: Ángeles Martínez Barrio <angeles.martinezbarrio@elastic.co>
@infra-vault-gh-plugin-prod

Copy link
Copy Markdown

💚 Build Succeeded

History

@infra-vault-gh-plugin-prod

Copy link
Copy Markdown

💚 Build Succeeded

History

@andreadelrio
andreadelrio merged commit 37352f6 into elastic:main Oct 29, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants