I Put SwiftUI Liquid Glass on Everything and It Looked Terrible
Last week I did something irresponsible. I took the tab bar in a two-year-old SwiftUI app, swapped one material for another, and watched it go from "fine" to "WWDC keynote" in the simulator.
The build took four minutes. The screenshot went to no one in particular. I was in love.
Then I put it on an actual iPhone, walked outside, and couldn't read a single label on it.
This is the story of how I learned that SwiftUI Liquid Glass — the new material system in iOS 26 — is genuinely great, and also why you should not do what I did. There's real code in here. The mistakes are free.
What Liquid Glass actually is
It's not a fancier blur. Apple's old materials, .ultraThinMaterial and friends, were basically a blur layer with an opacity slider sitting on top of whatever was behind them. Liquid Glass does refraction, light, distortion. Surfaces look like they have physical depth, like there's actual glass between your finger and the content.
That part is real. I'll give Apple that.
The problem is what the demos never show you: glass needs something interesting behind it to look good. Apple's marketing shots float panels over colorful wallpapers. My tab bar floats over a near-white background at sixty percent brightness. Glass over beige is just beige with a shipping label on it.
Here's the code I was so proud of:
TabView {
// ...
}
.toolbarBackground(.liquidGlass, for: .tabBar)
One line. This is the version everyone posts. This is also the version I ended up reverting.
The readability trap
iOS 26 ships adaptive text styles for glass surfaces: .liquidPrimary, .liquidSecondary, and so on. They're supposed to adjust their contrast to whatever the glass is refracting. And they do. The catch is that "whatever the glass is refracting" was, in my case, an empty white background.
Secondary text on glass over white is white-on-white with better manners.
The honest fix isn't a color tweak. It's asking whether that surface needed glass at all. Tab bars and toolbars float over real content, so glass works there. A settings card that mostly sits over a blank page? It's going to be hard to read, and you'll spend an afternoon fighting the tint to make it legible. I did.
The performance trap
Then I got ambitious. Cards. Every row in the list. A little .background(.liquidGlass) here, a subtle clipShape there — glass everywhere, because if some is good, all is better, right?
On a phone with a recent chip, it felt fine. I barely noticed the scroll stutter.
On an iPhone 11 from the drawer, the list was visibly struggling. Liquid Glass isn't a cheap effect. It's re-rendering refraction for every surface, every frame, while the content behind it moves. Put twenty glass cards in a ScrollView and you're asking the GPU to do its best work over and over, for free.
Apple's own guidance is to use it sparingly, on floating elements. Not on everything. I read that after I shipped it, obviously.
// Me, hour two. Don't do this on older devices.
ForEach(entries) { entry in
EntryCard()
.background(.liquidGlass)
.clipShape(RoundedRectangle(cornerRadius: 20))
}
The simulator lies
Two things the Simulator will happily hide from you. First, refraction barely renders there — glass looks flat and clean, so every contrast problem I described was invisible until I ran on hardware. Second, widgets. If you're adopting the new widget materials, .containerBackground(for: .widget) is the API you want. And if anything in your widget's tree still sets an opaque Color background, iOS 26 just draws black. No warning, no error. Just a very mysterious widget.
// Widget background, the way it's supposed to work:
.containerBackground(for: .widget) {
Rectangle().fill(.liquidGlass(thickness: .medium))
}
Test on a real device. I can't stress this enough. The simulator is not the app.
GlassEffectContainer, the thing nobody told me about
Put two glass views next to each other and you get two separate panels, each with its own edges. It looks like someone glued stickers on a window. GlassEffectContainer fixes that — it merges adjacent glass into one shared surface, and suddenly the toolbar button group looks intentional instead of accidental.
GlassEffectContainer {
Button { share() } label: { Image(systemName: "square.and.arrow.up") }
.glassEffect(.regular.interactive())
Button { add() } label: { Image(systemName: "plus") }
.glassEffect(.regular.interactive())
}
This is the kind of thing that separates "trying the API" from "using the design language." There's a dozen of these small behaviors, and none of them are in the first tutorial you'll find.
What I'd actually ship
Glass on the tab bar, the toolbar, and maybe one floating action button. Everything else stays on .ultraThinMaterial or plain backgrounds. And when I do use it, I keep a fallback so the app doesn't collapse on older systems:
var body: some View {
if #available(iOS 26, *) {
content.glassEffect()
} else {
content.background(.ultraThinMaterial)
}
}
Liquid Glass is good. It's the first time in years a UI framework made me want to restyle an app just for the sake of it. But it's a material, not a personality. Use it where it earns its cost.
I saved the container pattern and the fallback wrapper in Snippet Ark so the next time I'm tempted to glass-ify a settings screen, I start from the version I know works instead of the screenshot version.