今日已更新 80 条资讯 | 累计 20052 条内容
关于我们

Why your React tournament bracket breaks in Safari (and a 4 KB pure-CSS fix)

Hojayfa Rahman 2026年05月31日 20:36 3 次阅读 来源:Dev.to

You build a tournament bracket with a popular React library. In Chrome it's perfect — neat columns, clean connector lines. Then you open it on an iPhone, or in Safari, or inside your Capacitor app… and every match is crammed into the top-left corner, stacked on top of the round headers. If you've ever shipped a bracket to iOS, you've probably seen this exact bug. Here's why it happens — and a tiny library that fixes it for good. The symptom It looks fine everywhere Chromium runs (Chrome, Edge, Android WebView) and completely broken everywhere WebKit runs: Safari (macOS and iOS) iOS WKWebView Capacitor / Cordova apps Electron-on-WebKit The matches don't just shift a little — they all render at coordinate (0,0) of the bracket, piling on top of each other and the headers. The cause: SVG <foreignObject> in WebKit Most React bracket libraries — @g-loot/react-tournament-brackets , react-tournament-bracket , and friends — render the bracket as an SVG and place each match's HTML inside a <foreignObject> positioned with x / y attributes. WebKit has a long-standing bug: it ignores x , y , and transform on <foreignObject> and positions the content relative to the top-level <svg> instead of the foreignObject's own coordinates. Every match therefore collapses to the origin. And there's no CSS escape hatch — x , y , and transform are all ignored on foreignObject in Safari, so you can't nudge the content back into place. I even tried patching a library to wrap each match in a <g transform="translate(x,y)"> instead of a nested <svg x y> ; WebKit ignores ancestor transforms for foreignObject positioning too. The SVG approach is simply a dead end on WebKit. The fix: don't use SVG at all A bracket is really just columns of cards joined by connector lines — and both are expressible in plain CSS. Here's the key insight. Put each round in a flex column where every match sits in an equal flex: 1 slot. Because each round has half the matches of the previous one, a match's slot spans exactl

本文内容来源于互联网,版权归原作者所有
查看原文