glasslikeui
v1.0.0
Published
Svelte component library with frosted glass materials
Downloads
622
Readme
GlassLikeUI
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.
Install
npm install glasslikeuiPeer dependency: svelte ^5.0.0.
Setup
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>Basic use
<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>Variants and intensity
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.
Components
| 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. |
Actions
| 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. |
Device motion (opt-in)
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.
Icons
A small SF-symbol-inspired set ships in ICONS. Register your own
before rendering:
import { registerIcons } from 'glasslikeui';
registerIcons({
'custom-symbol': '<path d="..." />'
});Accessibility
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.
Theming
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
| 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. |
Build / test
npm run package # svelte-package build
npm test # vitest once
npm run test:watch # vitest watch
npm run test:coverage # with v8 coverageLicense
MIT. See LICENSE.
