Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

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. GPLv3 licensed.

Install

npm install glasslikeui

Peer 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) or clear. Clear requires a <GlassDimLayer /> companion for legibility.
  • intensity -- subtle, standard (default), or prominent. 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 filled / outlined / plain / tinted / destructive / glass / glassProminent.
IconButton plain / filled.
Toggle iOS-style switch over a native checkbox.
Badge default / accent.
Text Dynamic-Type-aware text.
Label Icon + title pair with SwiftUI label styles.
List / ListRow Grouped list with insets and swipe actions.
HStack / VStack / ZStack / Grid / Spacer / Divider / ScrollView Layout primitives.
SymbolImage SVG icon renderer.

Glass shape

Glass takes a shape prop mirroring SwiftUI's glassEffect(_:in:). It overrides cornerRadius entirely.

<Glass shape="capsule">...</Glass>
<Glass shape="circle">...</Glass>
  • rect (default) -- uses the cornerRadius token.
  • capsule -- fully rounded ends.
  • circle -- 50% radius, locked to a 1:1 box.

Buttons get the same treatment through the glass and glassProminent variants, which read their blur and saturation from the shared intensity tokens rather than duplicating them.

Swipe actions and pull to refresh

ListRow accepts a swipeActions snippet, mirroring SwiftUI's .swipeActions(edge:allowsFullSwipe:):

<ListRow allowsFullSwipe onFullSwipe={() => remove(item)}>
  {#snippet children()}{item.title}{/snippet}
  {#snippet swipeActions()}
    <button onclick={() => remove(item)}>Delete</button>
  {/snippet}
</ListRow>

ScrollView accepts an onRefresh handler, mirroring .refreshable. The indicator stays up until the returned promise settles -- including when it rejects:

<ScrollView onRefresh={async () => await reload()}>
  ...
</ScrollView>

Both are also available as standalone actions for your own markup. They drive --swipe-x / --refresh-pull and data-swipe-state / data-refresh-phase; style off those.

Actions

Export Purpose
scrollEdge Fade a scroll container at edges meeting glass chrome.
dragSnap Detent-based drag gesture (used by Sheet).
swipeActions Swipe a row aside to reveal actions.
refreshable Pull-to-refresh gesture.
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 returns denied without showing the prompt.
  • HTTPS -- http://localhost works 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 off
  • prefers-contrast: more -- thicker borders, higher opacity
  • prefers-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 coverage

License

GNU General Public License v3.0 or later (GPL-3.0-or-later). Copyright (c) 2026 Beau Ayres. See LICENSE.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages