Svelte component library with frosted glass materials. Pseudo-element lensing, two-axis variant API, drag-snap sheets, View Transitions morph, accessibility-aware out of the box, cross-platform typography.
Svelte 5 only. MIT licensed.
npm install glasslikeuiPeer dependency: svelte ^5.0.0.
Mount LensFilters once near the root of your app, and import the
stylesheets you want:
<script>
import { LensFilters } from 'glasslikeui';
import 'glasslikeui/glasslikeui.css';
import 'glasslikeui/lens.css';
import 'glasslikeui/squircle.css';
import 'glasslikeui/typography.css';
</script>
<LensFilters />
<!-- rest of app -->Optional: wire browser accessibility prefs into the library's fallback attributes on non-Safari browsers:
<script>
import { onMount } from 'svelte';
import { syncAccessibilityPreferences } from 'glasslikeui';
onMount(() => syncAccessibilityPreferences());
</script><script>
import { Glass, Button } from 'glasslikeui';
</script>
<Glass variant="regular" intensity="standard">
{#snippet children()}
<h2>Frosted card</h2>
<Button variant="filled">Action</Button>
{/snippet}
</Glass>Glass (and its consumers: GlassCard, GlassSection, List,
TabView) takes two orthogonal props:
variant--regular(default) orclear. Clear requires a<GlassDimLayer />companion for legibility.intensity--subtle,standard(default), orprominent. Controls blur, edge displacement, and saturation together.
| Component | Purpose |
|---|---|
Glass |
Core frosted surface. |
GlassCard |
Header / body / footer card. |
GlassSection |
Titled section block. |
GlassEffectContainer |
Groups glass siblings with shared shadow. |
GlassDimLayer |
Required under variant="clear". |
GlassMorph |
Tag a subtree for View Transitions morph. |
LensFilters |
SVG filter defs (mount once). |
Sheet |
Bottom sheet with detents + drag-to-snap. |
NavigationBar |
Top chrome; supports largeTitle collapse. |
NavigationLink |
Pill-style link. |
TabView |
Floating tab bar. |
Button / IconButton |
filled / outlined / plain / tinted / destructive. |
Badge |
default / accent. |
Text |
Dynamic-Type-aware text. |
List / ListRow |
Grouped list with insets. |
HStack / VStack / ZStack / Grid / Spacer / Divider / ScrollView |
Layout primitives. |
SymbolImage |
SVG icon renderer. |
| Export | Purpose |
|---|---|
scrollEdge |
Fade a scroll container at edges meeting glass chrome. |
dragSnap |
Detent-based drag gesture (used by Sheet). |
deviceMotion |
Ties the glass highlight angle to device tilt. |
withGlassTransition |
Wraps a DOM update in a view transition. |
syncAccessibilityPreferences |
Bridges prefers-* media to data-attrs at :root. |
requestMotionPermission |
Triggers the iOS user-gesture permission flow. |
The motion prop on Glass ties the highlight angle to the device's
tilt. iOS Safari requires a user-gesture permission grant before
tilt events fire. You MUST call requestMotionPermission() from a
click or tap handler -- not from onMount, an effect, or module init.
<script>
import { Glass, requestMotionPermission } from 'glasslikeui';
let permission = $state('pending');
async function enable() {
permission = await requestMotionPermission();
// 'granted' | 'denied' | 'unavailable'
}
</script>
{#if permission === 'pending'}
<button onclick={enable}>Enable tilt effect</button>
{/if}
<Glass motion={permission === 'granted'}>
{#snippet children()}...{/snippet}
</Glass>Requirements:
- User gesture -- call inside
onclick/ontap/ form handler. Called outside a gesture, iOS silently returnsdeniedwithout showing the prompt. - HTTPS --
http://localhostworks for dev; plain HTTP on a real device does not expose the API. - Per-origin -- granted or denied once, then remembered. A denied decision can only be reset by the user in Settings -> Safari -> Advanced.
- Non-iOS browsers (Android Chrome, desktop Chrome / Firefox /
Safari) grant access implicitly; the helper returns
'granted'without any prompt on those. - Unsupported browsers (no
DeviceOrientationEvent) -- helper returns'unavailable'and the action is a no-op.
A small SF-symbol-inspired set ships in ICONS. Register your own
before rendering:
import { registerIcons } from 'glasslikeui';
registerIcons({
'custom-symbol': '<path d="..." />'
});Native preference handling with zero config:
prefers-reduced-transparency-- surfaces go opaque, blur offprefers-contrast: more-- thicker borders, higher opacityprefers-reduced-motion-- transitions and motion highlights off
Cross-browser fallback attributes (flip at :root):
data-reduced-transparency="true"data-contrast="more"data-reduced-motion="true"
Call syncAccessibilityPreferences() to manage these automatically.
All design tokens are CSS custom properties. Toggle dark / light with
the data-theme attribute on :root:
<html data-theme="light">...</html>Override any token via your own stylesheet, e.g.:
:root {
--color-accent: #FF6A00;
--glass-radius-lg: 28px;
}| Browser | Support |
|---|---|
| Chrome / Edge 111+ | Full. |
| Safari 16.4+ | Full, including native prefers-reduced-transparency. |
| Firefox 115+ | Full. 103-114 works without edge lensing. |
| Older | Opaque fallback via @supports not. |
| SSR | All actions guard typeof window. |
npm run package # svelte-package build
npm test # vitest once
npm run test:watch # vitest watch
npm run test:coverage # with v8 coverageMIT. See LICENSE.