Skip to content

Fix: draw component borders with outline instead of ring (Safari border rendering at non-100% zoom)#30190

Open
aniketkatkar97 wants to merge 5 commits into
mainfrom
fix-ring-border-issue
Open

Fix: draw component borders with outline instead of ring (Safari border rendering at non-100% zoom)#30190
aniketkatkar97 wants to merge 5 commits into
mainfrom
fix-ring-border-issue

Conversation

@aniketkatkar97

@aniketkatkar97 aniketkatkar97 commented Jul 17, 2026

Copy link
Copy Markdown
Member

Fixes #30188

What's the problem

In Safari, component borders thin out — and at some zoom levels vanish entirely — at any zoom other than 100%. It looks Safari-only, but isn't quite.

The design system never drew these borders with a real border. It used Tailwind's ring-*, which compiles to a box-shadow, and WebKit does not pixel-snap box-shadows. At a fractional device pixel ratio (Safari reports devicePixelRatio: 1.7 at 85% zoom on Retina) a 1px ring cannot occupy a whole number of device pixels, so it is antialiased across two columns and renders washed out or not at all. border and outline are snapped and never degrade.

Chromium degrades rings too — it just never falls far enough to notice, which is why only Safari gets reported. Peak border darkness sampled from rendered pixels across a 50–150% zoom sweep (42 = full strength for the token, 0 = invisible):

border ring outline
WebKit @1x 42..42 0..42 (vanishes) 42..42
Chromium @1x 42..42 21..42 (dims ~50%) 42..42
WebKit @2x 42..42 42..42 42..42

What changed

ring-* is replaced with outline (or border) as the border primitive across the design system — ~120 sites in 85 files — with no intended visual change at 100% zoom.

Why outline and not border: a border occupies layout. On controls whose height is content-driven it would add 2px and make them grow on focus (1px → 2px). outline is layout-neutral, and an inset outline is pixel-identical to the ring it replaces.

Three patterns, by situation:

Situation Treatment
Element's outline is free tw:outline-1 tw:-outline-offset-1 tw:outline-<token>
Element's outline is already the focus ring (buttons, checkbox, radio, toggle, tags, tabs, slider) border moves to an ::after overlay (borderAfter in utils/tailwindClasses) — an element gets exactly one outline, and these must show border and focus ring simultaneously
Suppressors (ring-0) outline-0 / after:outline-0must flip with the core change, or the suppressor becomes a no-op and borders reappear

Offsets matter: ring-inset-outline-offset-1; no ring-inset → offset 0 (a non-inset ring draws outward). Getting this wrong shifts an edge by 1px.

Verification

  • Pre-flight: every ring colour compile-checked to confirm its outline-* counterpart dereferences to the same value — 41 tokens, zero mismatches. ring-* and outline-* use different theme namespaces, so this was not a given.
  • Pixel parity (ring vs ::after outline, identical geometry): WebKit @2x differs by 1 pixel at delta 1/255 — visually identical at working zoom. WebKit @1.7 differs at max delta 42 — that difference is the bug being fixed.
  • Confirmed in real Safari that an inset outline and a real border are indistinguishable at every zoom, while the ring is not.
  • Core + app tsc, ESLint, Prettier all clean. The two ring-coupled tests in DataQualityDashboard.test.tsx pass unmodified.

Rings deliberately left in place

Each would be a visual change to convert, so they aren't:

  • ring-offset-* halos (color-picker-field, icon-picker-field) — ring-offset-color fills the gap; outline-offset leaves it transparent. Not reproducible. Also 2–3 rings compete for one outline there.
  • Avatar consumers (OwnerReveal, OwnerStackOverflow) — Avatar already uses an outline; a second would clobber it. Wants Avatar's contrastBorder API instead.
  • ring-secondary with no ring-width (AgentCard, RunHistoryDrawer) — renders nothing today; converting would make an invisible border appear.
  • ring-0 on Card (OntologyExplorer, DataQualityDashboard) — Card uses a real border and never had a ring, so it is already inert. Converting would kill Card's focus outline.

Docs

docs/colors.md §2.3.1 documents the rule, the measurements, the ring→outline translation table, and the gotchas that make this easy to get wrong: outline-hidden erases an outline border; unlayered LESS outline: none beats Tailwind utilities regardless of specificity; transition-shadow does not animate an outline; and overriding a Button's border needs after:outline-*, since outline-* silently sets its focus colour instead. Summarised in both CLAUDE.md files and added to the anti-pattern cheat sheet so the pattern isn't reintroduced.

Reviewer notes

  • Visual parity is the acceptance test. At 100% nothing should look different. Worth a pass over button variants (× hover/disabled/focus), checkbox/radio/toggle, tabs, badges, table cards and dropdowns — in light and dark.
  • Focus indicators must survive on every ::after component — that's the whole reason for the overlay approach and the main regression risk.
  • Then the actual fix: Safari at 85% zoom / dpr 1.7, borders hold.
  • ::before was already taken on button.tsx / social-button.tsx (inner gradient), hence ::after.

Before:

Screenshot 2026-07-17 at 5 52 37 PM Screenshot 2026-07-17 at 5 56 06 PM Screenshot 2026-07-17 at 5 57 54 PM

After:

Screenshot 2026-07-17 at 5 52 41 PM Screenshot 2026-07-17 at 5 56 17 PM Screenshot 2026-07-17 at 5 58 11 PM

Greptile Summary

This PR replaces ring-based component edges with pixel-snapped outlines or borders. The main changes are:

  • Migrates design-system borders away from box-shadow rings.
  • Preserves focus indicators with pseudo-element border overlays.
  • Updates suppressors, transitions, documentation, and application overrides.
  • Removes the inactive 2xl avatar ring class.

Confidence Score: 5/5

This looks safe to merge.

  • The latest avatar update removes an inactive unprefixed ring class.
  • The reported double-edge behavior is not reachable after this change.
  • No blocking issues remain in the updated code.

Important Files Changed

Filename Overview
openmetadata-ui-core-components/src/main/resources/ui/src/components/base/avatar/base-components/avatar-company-icon.tsx Removes the inactive 2xl ring token while keeping the active avatar edge as an outline.
openmetadata-ui-core-components/src/main/resources/ui/src/utils/tailwindClasses.ts Provides shared pseudo-element outline classes for controls that reserve their own outline for focus.
openmetadata-ui/src/main/resources/ui/docs/colors.md Documents the ring-to-outline migration rules and focus-border constraints.

Reviews (4): Last reviewed commit: "Merge branch 'main' into fix-ring-border..." | Re-trigger Greptile

Context used (3)

  • Context used - CLAUDE.md (source)
  • Context used - openmetadata-ui-core-components/CLAUDE.md (source)
  • Context used - AGENTS.md (source)

Copilot AI review requested due to automatic review settings July 17, 2026 14:08

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added safe to test Add this label to run secure Github workflows on PRs UI UI specific issues labels Jul 17, 2026
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 65%
65.22% (75791/116193) 48.98% (45159/92196) 49.84% (13673/27433)

…drop dead ring class, fix import order

- OwnerStackOverflow: drop tw:outline-hidden — it set outline-style:none and
  erased the outline that now draws the tooltip border (all browsers).
- avatar-company-icon: remove dead unprefixed ring-[1.67px] from the 2xl size.
  It generates no CSS under prefix(tw), but contradicts the no-rings rule and
  would spring to life if the prefix bug is fixed.
- ManageMenuButton: run organize-imports so the added borderAfter import is
  sorted, fixing the UI Checkstyle lint-src failure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 17, 2026 16:13

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@gitar-bot

gitar-bot Bot commented Jul 17, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 1 resolved / 1 findings

Replaces Tailwind ring utilities with outline styles to resolve Safari rendering issues at non-100% zoom levels. The fix also addresses an issue where outline-hidden erroneously erased tooltip borders.

✅ 1 resolved
Bug: outline-hidden erases the new outline border on owners tooltip

📄 openmetadata-ui/src/main/resources/ui/src/components/common/OwnerLabel/OwnerStackOverflow.component.tsx:124
The tooltip class was converted from tw:ring-1 tw:ring-secondary_alt tw:outline-hidden to tw:outline-1 tw:outline-secondary_alt tw:outline-hidden. Now that the border is drawn with outline, the retained tw:outline-hidden sets outline-style: none and erases that border entirely (in every browser, not just Safari) — exactly the gotcha documented in this PR's own colors.md §2.3.1 and CLAUDE.md. The sibling conversion in nav-account-card.tsx correctly dropped outline-hidden; this site did not. Remove tw:outline-hidden so the 1px border renders.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

Copilot AI review requested due to automatic review settings July 20, 2026 04:24

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 20, 2026 13:14
@open-metadata open-metadata deleted a comment from github-actions Bot Jul 20, 2026

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@gitar-bot

gitar-bot Bot commented Jul 20, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 1 resolved / 1 findings

Replaces Tailwind ring utilities with outline styles to resolve Safari rendering issues at non-100% zoom levels. The fix also addresses an issue where outline-hidden erroneously erased tooltip borders.

✅ 1 resolved
Bug: outline-hidden erases the new outline border on owners tooltip

📄 openmetadata-ui/src/main/resources/ui/src/components/common/OwnerLabel/OwnerStackOverflow.component.tsx:124
The tooltip class was converted from tw:ring-1 tw:ring-secondary_alt tw:outline-hidden to tw:outline-1 tw:outline-secondary_alt tw:outline-hidden. Now that the border is drawn with outline, the retained tw:outline-hidden sets outline-style: none and erases that border entirely (in every browser, not just Safari) — exactly the gotcha documented in this PR's own colors.md §2.3.1 and CLAUDE.md. The sibling conversion in nav-account-card.tsx correctly dropped outline-hidden; this site did not. Remove tw:outline-hidden so the 1px border renders.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

🔴 Playwright Results — 2 failure(s), 33 flaky

✅ 4532 passed · ❌ 2 failed · 🟡 33 flaky · ⏭️ 99 skipped

Shard Passed Failed Flaky Skipped
🟡 Shard 1 436 0 4 16
✅ Shard 2 11 0 0 0
🟡 Shard 3 821 0 11 8
🔴 Shard 4 819 1 3 18
🔴 Shard 5 833 1 3 9
🟡 Shard 6 786 0 2 46
🟡 Shard 7 826 0 10 2

Genuine Failures (failed on all attempts)

Features/Glossary/GlossaryTermRelationsGraphNested.spec.ts › viewing a child term: parent appears as a 1-hop neighbour via parentOf edge (shard 4)
�[31mTest timeout of 60000ms exceeded.�[39m
Pages/CustomProperties.spec.ts › Set & Update all CP types on chart (shard 5)
�[31mTest timeout of 300000ms exceeded.�[39m
🟡 33 flaky test(s) (passed on retry)
  • Features/TagsSuggestion.spec.ts › should decline requested tags for an api endpoint request schema field (shard 1, 1 retry)
  • Pages/Lineage/LineageRightPanel.spec.ts › Verify custom properties tab IS visible for supported type: metric (shard 1, 1 retry)
  • Pages/SearchSettings.spec.ts › Latest preview config wins when a superseded request resolves late (shard 1, 1 retry)
  • Flow/SearchRBAC.spec.ts › a fully denied user sees neither asset type when browsing (shard 1, 1 retry)
  • Features/BulkEditEntity.spec.ts › Glossary Term (Nested) (shard 3, 1 retry)
  • Features/BulkImportWithDotInName.spec.ts › Full import cycle with dot in service name (shard 3, 1 retry)
  • Features/ColumnBulkOperations.spec.ts › should clear individual filter and update URL (shard 3, 1 retry)
  • Features/ContextCenterArchive.spec.ts › full document lifecycle: folder expand icon, upload, delete, restore, and permanent delete (shard 3, 1 retry)
  • Features/ContextCenterArchive.spec.ts › archive page lazy-loads more rows on scroll within its own scroll container (shard 3, 1 retry)
  • Features/ContextCenterArticles.spec.ts › Article list cards, recently viewed widget, and pagination work (shard 3, 1 retry)
  • Features/ContextCenterArticles.spec.ts › Article edit persistence and unsaved title behavior are correct (shard 3, 2 retries)
  • Features/ContextCenterDocument.spec.ts › move document to folder via card menu shows folder name on the card (shard 3, 1 retry)
  • Features/ContextCenterMemories.spec.ts › adding a linked asset in edit mode shows entity badge on the row (shard 3, 1 retry)
  • Features/DataQuality/BundleSuiteBulkOperations.spec.ts › Create new Bundle Suite with bulk selected test cases (shard 3, 1 retry)
  • Features/DataQuality/TestCaseImportExportBasic.spec.ts › should show validation errors for invalid CSV (shard 3, 1 retry)
  • Features/Glossary/GlossaryTermRelationsGraphNested.spec.ts › viewing a child term: parentOf edge is rendered between parent and child (shard 4, 1 retry)
  • Features/MetricBulkImportExportEdit.spec.ts › MetricListPage unchecking header checkbox clears the selection bar (shard 4, 1 retry)
  • Features/SearchExport.spec.ts › Export queues a background job and downloads from the jobs tray (shard 4, 1 retry)
  • Features/UserProfileOnlineStatus.spec.ts › Should show "Active recently" for users active within last hour (shard 5, 1 retry)
  • Pages/DataContractsSemanticRules.spec.ts › Validate DataProduct Rule Any_In (shard 5, 1 retry)
  • Pages/DataProductAndSubdomains.spec.ts › Data products under different subdomains (shard 5, 1 retry)
  • Pages/Entity.spec.ts › Domain Propagation (shard 6, 1 retry)
  • Pages/ExplorePageRightPanel_KnowledgeCenter.spec.ts › Should remove user owner for knowledgeCenter (shard 6, 1 retry)
  • Pages/ExplorePageRightPanel.spec.ts › Should verify deleted user not visible in owner selection for table (shard 7, 1 retry)
  • Pages/ExplorePageRightPanel.spec.ts › Should allow Data Steward to edit owners for databaseSchema (shard 7, 1 retry)
  • Pages/Glossary.spec.ts › Approve and reject glossary term from Glossary Listing (shard 7, 1 retry)
  • Pages/GlossaryImportExport.spec.ts › Check for Circular Reference in Glossary Import (shard 7, 2 retries)
  • Pages/Lineage/DataAssetLineage.spec.ts › Column lineage for dashboard -> container (shard 7, 1 retry)
  • Pages/Lineage/LineageFilters.spec.ts › Verify Impact Analysis service filter selection (shard 7, 1 retry)
  • Pages/Lineage/LineageRightPanel.spec.ts › Verify custom properties tab is NOT visible for pipelineService in platform lineage (shard 7, 1 retry)
  • ... and 3 more

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Component borders thin out or disappear in Safari at non-100% zoom (ring/box-shadow is not pixel-snapped by WebKit)

3 participants