get-shit-pretty 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +15 -9
- package/agents/{gsp-accessibility-auditor.md → gsp-auditor.md} +17 -14
- package/agents/gsp-brand-auditor.md +97 -0
- package/agents/gsp-brand-strategist.md +64 -27
- package/agents/{gsp-design-engineer.md → gsp-builder.md} +12 -9
- package/agents/gsp-campaign-director.md +50 -11
- package/agents/gsp-codebase-scanner.md +171 -0
- package/agents/gsp-critic.md +18 -13
- package/agents/gsp-designer.md +126 -0
- package/agents/gsp-identity-designer.md +90 -0
- package/agents/gsp-project-researcher.md +102 -0
- package/agents/gsp-researcher.md +54 -18
- package/agents/gsp-reviewer.md +66 -0
- package/agents/gsp-scoper.md +103 -0
- package/agents/gsp-system-architect.md +91 -26
- package/agents/gsp-verbal-strategist.md +84 -0
- package/bin/install.js +161 -5
- package/commands/gsp/brand-audit.md +116 -0
- package/commands/gsp/brand-discover.md +17 -0
- package/commands/gsp/brand-identity.md +200 -0
- package/commands/gsp/brand-patterns.md +223 -0
- package/commands/gsp/brand-research.md +99 -0
- package/commands/gsp/brand-strategy.md +140 -0
- package/commands/gsp/brand-system.md +17 -0
- package/commands/gsp/brand-verbal.md +94 -0
- package/commands/gsp/brand.md +9 -83
- package/commands/gsp/brief.md +142 -0
- package/commands/gsp/build.md +49 -41
- package/commands/gsp/critique.md +140 -0
- package/commands/gsp/design.md +65 -50
- package/commands/gsp/discover.md +17 -0
- package/commands/gsp/doctor.md +319 -0
- package/commands/gsp/help.md +85 -38
- package/commands/gsp/identity.md +18 -0
- package/commands/gsp/launch.md +55 -35
- package/commands/gsp/new-project.md +5 -86
- package/commands/gsp/new.md +237 -0
- package/commands/gsp/plan.md +18 -0
- package/commands/gsp/progress.md +58 -26
- package/commands/gsp/research.md +91 -34
- package/commands/gsp/review.md +115 -59
- package/commands/gsp/strategy.md +18 -0
- package/commands/gsp/system.md +8 -65
- package/commands/gsp/update.md +102 -0
- package/commands/gsp/verbal.md +18 -0
- package/package.json +2 -2
- package/prompts/01-design-system-architect.md +35 -3
- package/prompts/03-ui-ux-pattern-master.md +11 -1
- package/prompts/09-design-to-code-translator.md +9 -0
- package/prompts/10-project-scoper.md +51 -0
- package/prompts/11-deliverable-reviewer.md +58 -0
- package/prompts/12-project-researcher.md +57 -0
- package/references/brand-archetypes.md +151 -0
- package/references/brand-prism.md +138 -0
- package/references/chunk-format.md +48 -0
- package/references/design-trends.md +47 -0
- package/references/positioning-frameworks.md +197 -0
- package/references/questioning.md +1 -1
- package/references/trends/aurora-gradients.md +245 -0
- package/references/trends/bento-grid.md +473 -0
- package/references/trends/claymorphism.md +232 -0
- package/references/trends/dark-mode-oled.md +282 -0
- package/references/trends/glassmorphism.md +455 -0
- package/references/trends/kinetic-typography.md +277 -0
- package/references/trends/liquid-glass.md +236 -0
- package/references/trends/micro-interactions.md +307 -0
- package/references/trends/neubrutalism.md +276 -0
- package/references/voice-tone.md +193 -0
- package/scripts/gsp-statusline.js +1 -1
- package/templates/branding/brief.md +74 -0
- package/templates/branding/config.json +26 -0
- package/templates/branding/roadmap.md +43 -0
- package/templates/branding/state.md +29 -0
- package/templates/changelog.md +4 -0
- package/templates/codebase-inventory.md +71 -0
- package/templates/exports-index.md +93 -0
- package/templates/manifest.md +19 -0
- package/templates/phases/brief.md +53 -0
- package/templates/phases/build.md +24 -48
- package/templates/phases/critique.md +68 -0
- package/templates/phases/design.md +54 -32
- package/templates/phases/discover.md +60 -0
- package/templates/phases/identity.md +78 -0
- package/templates/phases/launch.md +48 -55
- package/templates/phases/research.md +75 -47
- package/templates/phases/review.md +27 -75
- package/templates/phases/strategy.md +67 -0
- package/templates/phases/system.md +84 -78
- package/templates/phases/verbal.md +63 -0
- package/templates/{project.md → projects/brief.md} +13 -17
- package/templates/projects/config.json +32 -0
- package/templates/projects/roadmap.md +59 -0
- package/templates/{state.md → projects/state.md} +19 -9
- package/agents/gsp-spec-engineer.md +0 -121
- package/agents/gsp-ui-designer.md +0 -59
- package/commands/gsp/spec.md +0 -88
- package/templates/config.json +0 -26
- package/templates/phases/brand.md +0 -60
- package/templates/phases/spec.md +0 -46
- package/templates/roadmap.md +0 -62
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# Kinetic Typography
|
|
2
|
+
|
|
3
|
+
> Text that moves — scroll-triggered animations, character-level effects, and responsive text transforms that make typography the hero element.
|
|
4
|
+
|
|
5
|
+
Last verified: 2026-03-04
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Visual Characteristics
|
|
10
|
+
|
|
11
|
+
- Text as the primary visual element, not decoration
|
|
12
|
+
- Scroll-triggered reveals and staggered animations
|
|
13
|
+
- Character and word-level motion with precise timing
|
|
14
|
+
- Marquee/horizontal scroll text for atmosphere
|
|
15
|
+
- Scale and emphasis transitions for key messages
|
|
16
|
+
- Requires JavaScript for character splitting and scroll detection
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## CSS Implementation
|
|
21
|
+
|
|
22
|
+
### Scroll-Reveal (Word by Word)
|
|
23
|
+
|
|
24
|
+
```css
|
|
25
|
+
.kinetic-word {
|
|
26
|
+
opacity: 0;
|
|
27
|
+
transform: translateY(20px);
|
|
28
|
+
transition: opacity 0.4s ease-out, transform 0.4s ease-out;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.kinetic-word.visible {
|
|
32
|
+
opacity: 1;
|
|
33
|
+
transform: translateY(0);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Stagger: 60ms per word */
|
|
37
|
+
.kinetic-word:nth-child(1) { transition-delay: 0ms; }
|
|
38
|
+
.kinetic-word:nth-child(2) { transition-delay: 60ms; }
|
|
39
|
+
.kinetic-word:nth-child(3) { transition-delay: 120ms; }
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Character Split + Stagger
|
|
43
|
+
|
|
44
|
+
```css
|
|
45
|
+
.kinetic-char {
|
|
46
|
+
display: inline-block;
|
|
47
|
+
opacity: 0;
|
|
48
|
+
transform: translateY(100%);
|
|
49
|
+
animation: char-reveal 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@keyframes char-reveal {
|
|
53
|
+
to { opacity: 1; transform: translateY(0); }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.kinetic-char { animation-delay: calc(var(--char-index) * 30ms); }
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Horizontal Scroll Text (Marquee)
|
|
60
|
+
|
|
61
|
+
```css
|
|
62
|
+
.kinetic-marquee {
|
|
63
|
+
display: flex;
|
|
64
|
+
white-space: nowrap;
|
|
65
|
+
animation: scroll-x 20s linear infinite;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@keyframes scroll-x {
|
|
69
|
+
from { transform: translateX(0); }
|
|
70
|
+
to { transform: translateX(-50%); }
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Animation Timing Reference
|
|
75
|
+
|
|
76
|
+
| Effect | Duration | Easing | Stagger |
|
|
77
|
+
|--------|----------|--------|---------|
|
|
78
|
+
| Word reveal | 400ms | `ease-out` | 60ms/word |
|
|
79
|
+
| Char reveal | 500ms | `cubic-bezier(0.16, 1, 0.3, 1)` | 30ms/char |
|
|
80
|
+
| Line slide-up | 600ms | `cubic-bezier(0.16, 1, 0.3, 1)` | 100ms/line |
|
|
81
|
+
| Marquee | 15-25s | `linear` | N/A |
|
|
82
|
+
| Scale emphasis | 300ms | `cubic-bezier(0.34, 1.56, 0.64, 1)` | N/A |
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Implementation Guide
|
|
87
|
+
|
|
88
|
+
### Step-by-step
|
|
89
|
+
|
|
90
|
+
1. Choose your effect type: word reveal, character split, line slide, or marquee
|
|
91
|
+
2. For word/character effects: split text into `<span>` elements using JavaScript
|
|
92
|
+
3. Set `--char-index` or `--word-index` CSS custom property on each span for stagger timing
|
|
93
|
+
4. Use `IntersectionObserver` to trigger animations when elements enter the viewport
|
|
94
|
+
5. Apply animation class (`.visible`) when the element intersects
|
|
95
|
+
6. For marquee: duplicate the text content to create a seamless loop
|
|
96
|
+
7. Set `aria-hidden="true"` on duplicate marquee content
|
|
97
|
+
|
|
98
|
+
### Progressive Enhancement
|
|
99
|
+
|
|
100
|
+
```css
|
|
101
|
+
/* Baseline: all text visible immediately */
|
|
102
|
+
.kinetic-word,
|
|
103
|
+
.kinetic-char {
|
|
104
|
+
opacity: 1;
|
|
105
|
+
transform: none;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* Enhanced: only animate when JS is available */
|
|
109
|
+
.js-loaded .kinetic-word,
|
|
110
|
+
.js-loaded .kinetic-char {
|
|
111
|
+
opacity: 0;
|
|
112
|
+
transform: translateY(20px);
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Add `js-loaded` class to `<html>` via JavaScript on load to enable animations only when JS is available.
|
|
117
|
+
|
|
118
|
+
### Framework Notes
|
|
119
|
+
|
|
120
|
+
#### React + Framer Motion
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
import { motion, useInView } from 'framer-motion';
|
|
124
|
+
import { useRef } from 'react';
|
|
125
|
+
|
|
126
|
+
export function KineticHeading({ text }: { text: string }) {
|
|
127
|
+
const ref = useRef(null);
|
|
128
|
+
const isInView = useInView(ref, { once: true });
|
|
129
|
+
const words = text.split(' ');
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<h2 ref={ref} className="text-5xl font-bold">
|
|
133
|
+
{words.map((word, i) => (
|
|
134
|
+
<motion.span
|
|
135
|
+
key={i}
|
|
136
|
+
className="inline-block mr-3"
|
|
137
|
+
initial={{ opacity: 0, y: 20 }}
|
|
138
|
+
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
|
139
|
+
transition={{ delay: i * 0.06, duration: 0.4, ease: 'easeOut' }}
|
|
140
|
+
>
|
|
141
|
+
{word}
|
|
142
|
+
</motion.span>
|
|
143
|
+
))}
|
|
144
|
+
</h2>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
#### GSAP ScrollTrigger
|
|
150
|
+
|
|
151
|
+
```js
|
|
152
|
+
import { gsap } from 'gsap';
|
|
153
|
+
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
|
154
|
+
gsap.registerPlugin(ScrollTrigger);
|
|
155
|
+
|
|
156
|
+
// Word-by-word reveal on scroll
|
|
157
|
+
gsap.from('.kinetic-word', {
|
|
158
|
+
y: 20,
|
|
159
|
+
opacity: 0,
|
|
160
|
+
stagger: 0.06,
|
|
161
|
+
duration: 0.4,
|
|
162
|
+
ease: 'power2.out',
|
|
163
|
+
scrollTrigger: {
|
|
164
|
+
trigger: '.kinetic-container',
|
|
165
|
+
start: 'top 80%',
|
|
166
|
+
toggleActions: 'play none none none',
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### CSS Scroll-Driven Animations (New Spec)
|
|
172
|
+
|
|
173
|
+
```css
|
|
174
|
+
/* Native CSS scroll-linked animation — Chrome 115+ */
|
|
175
|
+
.kinetic-word {
|
|
176
|
+
animation: reveal linear both;
|
|
177
|
+
animation-timeline: view();
|
|
178
|
+
animation-range: entry 0% entry 50%;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@keyframes reveal {
|
|
182
|
+
from { opacity: 0; transform: translateY(20px); }
|
|
183
|
+
to { opacity: 1; transform: translateY(0); }
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
#### React Native
|
|
188
|
+
|
|
189
|
+
Kinetic typography is achievable with `react-native-reanimated` for scroll-linked animations. Character splitting requires manual `<Text>` wrapping. No CSS `animation-timeline` equivalent exists — use `useAnimatedScrollHandler` from Reanimated.
|
|
190
|
+
|
|
191
|
+
### Common Pitfalls
|
|
192
|
+
|
|
193
|
+
1. **Text inaccessible without JS**: always ensure text is visible by default. Apply animation styles only after JS loads. Never set `opacity: 0` in base CSS for content text.
|
|
194
|
+
2. **Excessive DOM nodes**: character-level splitting creates one `<span>` per character. A 500-character paragraph becomes 500+ DOM nodes. Limit character splitting to headings and short phrases.
|
|
195
|
+
3. **Replay fatigue**: scroll-triggered animations that replay every time the user scrolls past create annoyance. Use `{ once: true }` with IntersectionObserver.
|
|
196
|
+
4. **Marquee without duplication**: a single-copy marquee shows a gap. Duplicate the content and use `translateX(-50%)` for seamless looping.
|
|
197
|
+
5. **Missing reduced-motion support**: kinetic typography is the most motion-heavy trend — always provide a complete `prefers-reduced-motion` fallback.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Examples Gallery
|
|
202
|
+
|
|
203
|
+
| Site | What They Do Well | Screenshot Description |
|
|
204
|
+
|------|-------------------|----------------------|
|
|
205
|
+
| Apple (product pages) | Word-by-word scroll reveal with scale emphasis on key specs ("A17 Pro") | iPhone page with staggered text revealing performance numbers |
|
|
206
|
+
| Locomotive | Scroll-driven character reveal with split animations and parallax text layers | Agency homepage with large kinetic headings |
|
|
207
|
+
| Awwwards — Aristide Benoist | Award-winning character-by-character reveal with custom easing per letter | Portfolio with immersive typographic scroll experience |
|
|
208
|
+
| Stripe | Subtle word-level fade-in on scroll with precise stagger timing | Features page with clean staggered text reveals |
|
|
209
|
+
| Awwwards — Rejouice | Full-page kinetic typography as the primary navigation and storytelling device | Single-page experience driven entirely by animated text |
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Accessibility
|
|
214
|
+
|
|
215
|
+
- **Content visibility**: text must be readable without JavaScript. Never hide content behind `opacity: 0` in base CSS.
|
|
216
|
+
- **Reduced motion**: replace all animation with instant visibility for `prefers-reduced-motion: reduce`
|
|
217
|
+
- Stop marquee animations
|
|
218
|
+
- Remove character-level stagger — show full text immediately
|
|
219
|
+
- Replace slide/scale with simple opacity fade (200ms max)
|
|
220
|
+
- **Screen readers**: ensure `aria-hidden="true"` on duplicate marquee text. Use `aria-label` on animated containers if text is fragmented across many spans.
|
|
221
|
+
- **Focus order**: character-split text should not create individual tab stops. Use `tabindex="-1"` on internal spans.
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Performance
|
|
226
|
+
|
|
227
|
+
- **DOM cost**: character splitting creates N DOM nodes per character. Keep to headings and short phrases — never split body paragraphs
|
|
228
|
+
- **GSAP vs CSS**: GSAP ScrollTrigger adds ~25KB (minified). CSS scroll-driven animations are zero-JS but limited to Chrome 115+
|
|
229
|
+
- **Animate only `transform` and `opacity`**: these are GPU-composited. Avoid animating `font-size`, `letter-spacing`, or `width`
|
|
230
|
+
- **IntersectionObserver**: more performant than scroll event listeners for triggering reveals
|
|
231
|
+
- **Marquee**: uses `transform: translateX()` — GPU-composited, low cost even on mobile
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## When to Use / When to Avoid
|
|
236
|
+
|
|
237
|
+
### Use When
|
|
238
|
+
- Hero sections, section intros with key messages
|
|
239
|
+
- Single impactful phrases or headlines
|
|
240
|
+
- Scroll-driven storytelling and narrative pages
|
|
241
|
+
- Marketing/landing pages where typography is the hero
|
|
242
|
+
|
|
243
|
+
### Avoid When
|
|
244
|
+
- Body text, paragraphs, or long-form content
|
|
245
|
+
- Navigation elements, form labels
|
|
246
|
+
- Frequently revisited pages where animation becomes annoying
|
|
247
|
+
- Content that needs to be immediately readable (alerts, errors, CTAs)
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Design Tokens
|
|
252
|
+
|
|
253
|
+
```json
|
|
254
|
+
{
|
|
255
|
+
"kinetic": {
|
|
256
|
+
"word-reveal-duration": "400ms",
|
|
257
|
+
"word-reveal-stagger": "60ms",
|
|
258
|
+
"word-reveal-easing": "ease-out",
|
|
259
|
+
"char-reveal-duration": "500ms",
|
|
260
|
+
"char-reveal-stagger": "30ms",
|
|
261
|
+
"char-reveal-easing": "cubic-bezier(0.16, 1, 0.3, 1)",
|
|
262
|
+
"line-slide-duration": "600ms",
|
|
263
|
+
"line-slide-stagger": "100ms",
|
|
264
|
+
"marquee-duration": "20s",
|
|
265
|
+
"scale-emphasis-duration": "300ms",
|
|
266
|
+
"scale-emphasis-easing": "cubic-bezier(0.34, 1.56, 0.64, 1)",
|
|
267
|
+
"reduced-motion-duration": "200ms"
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Related
|
|
275
|
+
|
|
276
|
+
- [Neubrutalism](./neubrutalism.md) — great pairing for bold, expressive text effects
|
|
277
|
+
- [Micro-Interactions](./micro-interactions.md) — complementary animation system for non-text elements
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# Liquid Glass
|
|
2
|
+
|
|
3
|
+
> Apple's 2025 design language featuring semi-transparent surfaces with light refraction, dynamic blur, and fluid morphing — the first major Apple UI overhaul in 10 years.
|
|
4
|
+
|
|
5
|
+
Last verified: 2026-03-04
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Visual Characteristics
|
|
10
|
+
|
|
11
|
+
- Semi-transparent surfaces that refract and bend background content
|
|
12
|
+
- Dynamic light response — surfaces react to scroll, tilt, and ambient light
|
|
13
|
+
- Fluid shape transitions — UI elements morph between states like water
|
|
14
|
+
- Layered depth with specular highlights simulating real glass curvature
|
|
15
|
+
- Soft, organic edges — no hard borders
|
|
16
|
+
- Deployed across iOS 26, macOS Tahoe, iPadOS, visionOS from WWDC 2025
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## CSS Implementation
|
|
21
|
+
|
|
22
|
+
### Core Surface
|
|
23
|
+
|
|
24
|
+
Apple's native implementation uses system-level compositing unavailable to web. This CSS approximation achieves ~80% of the visual effect:
|
|
25
|
+
|
|
26
|
+
```css
|
|
27
|
+
.liquid-glass {
|
|
28
|
+
position: relative;
|
|
29
|
+
backdrop-filter: blur(2px) saturate(180%);
|
|
30
|
+
-webkit-backdrop-filter: blur(2px) saturate(180%);
|
|
31
|
+
background: rgba(255, 255, 255, 0.15);
|
|
32
|
+
border: 1px solid rgba(255, 255, 255, 0.8);
|
|
33
|
+
border-radius: 2rem;
|
|
34
|
+
box-shadow:
|
|
35
|
+
0 8px 32px rgba(31, 38, 135, 0.2),
|
|
36
|
+
inset 0 4px 20px rgba(255, 255, 255, 0.3);
|
|
37
|
+
overflow: hidden;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* Inner light reflection layer */
|
|
41
|
+
.liquid-glass::after {
|
|
42
|
+
content: '';
|
|
43
|
+
position: absolute;
|
|
44
|
+
inset: 0;
|
|
45
|
+
backdrop-filter: blur(1px);
|
|
46
|
+
background: rgba(255, 255, 255, 0.1);
|
|
47
|
+
border-radius: 2rem;
|
|
48
|
+
opacity: 0.6;
|
|
49
|
+
box-shadow:
|
|
50
|
+
inset -10px -8px 0px -11px rgba(255, 255, 255, 1),
|
|
51
|
+
inset 0px -9px 0px -8px rgba(255, 255, 255, 1);
|
|
52
|
+
filter: blur(1px) drop-shadow(10px 4px 6px black) brightness(115%);
|
|
53
|
+
pointer-events: none;
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### SVG Distortion Filter (Refraction Effect)
|
|
58
|
+
|
|
59
|
+
True liquid glass distortion requires SVG filters — CSS alone cannot achieve light bending:
|
|
60
|
+
|
|
61
|
+
```html
|
|
62
|
+
<svg style="position: absolute; width: 0; height: 0;">
|
|
63
|
+
<filter id="liquid-distortion">
|
|
64
|
+
<feTurbulence type="fractalNoise" baseFrequency="0.008 0.008"
|
|
65
|
+
numOctaves="3" seed="1" result="noise" />
|
|
66
|
+
<feGaussianBlur in="noise" stdDeviation="2" result="blurred-noise" />
|
|
67
|
+
<feDisplacementMap in="SourceGraphic" in2="blurred-noise"
|
|
68
|
+
scale="70" xChannelSelector="R" yChannelSelector="G" />
|
|
69
|
+
<feSpecularLighting in="blurred-noise" surfaceScale="3"
|
|
70
|
+
specularConstant="0.75" specularExponent="20" result="specular">
|
|
71
|
+
<fePointLight x="-50" y="-100" z="200" />
|
|
72
|
+
</feSpecularLighting>
|
|
73
|
+
<feComposite in="specular" in2="SourceGraphic" operator="in" />
|
|
74
|
+
</filter>
|
|
75
|
+
</svg>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Apply: `filter: url(#liquid-distortion);`
|
|
79
|
+
|
|
80
|
+
### Animation Values
|
|
81
|
+
- Transition timing: `cubic-bezier(0.175, 0.885, 0.32, 2.2)` (springy/elastic)
|
|
82
|
+
- Morph duration: 300-500ms
|
|
83
|
+
- Blur transition: 200ms ease-out
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Implementation Guide
|
|
88
|
+
|
|
89
|
+
### Step-by-step
|
|
90
|
+
|
|
91
|
+
1. Add a visually rich background layer (gradient, image, or aurora) — liquid glass needs content to refract
|
|
92
|
+
2. Create the glass container with `position: relative` and `overflow: hidden`
|
|
93
|
+
3. Apply `backdrop-filter: blur(2px) saturate(180%)` with `-webkit-` prefix
|
|
94
|
+
4. Set translucent fill: `rgba(255,255,255, 0.15)` for light, `rgba(0,0,0, 0.2)` for dark
|
|
95
|
+
5. Add the `::after` pseudo-element for the inner light reflection
|
|
96
|
+
6. For refraction: include the SVG filter inline and apply via `filter: url(#liquid-distortion)`
|
|
97
|
+
7. Add spring-based transition for morph states
|
|
98
|
+
|
|
99
|
+
### Progressive Enhancement
|
|
100
|
+
|
|
101
|
+
```css
|
|
102
|
+
/* Baseline: solid frosted card */
|
|
103
|
+
.liquid-glass {
|
|
104
|
+
background: rgba(255, 255, 255, 0.85);
|
|
105
|
+
border-radius: 2rem;
|
|
106
|
+
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* Layer 1: backdrop blur when supported */
|
|
110
|
+
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
|
|
111
|
+
.liquid-glass {
|
|
112
|
+
background: rgba(255, 255, 255, 0.15);
|
|
113
|
+
backdrop-filter: blur(2px) saturate(180%);
|
|
114
|
+
-webkit-backdrop-filter: blur(2px) saturate(180%);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Reduced transparency preference */
|
|
119
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
120
|
+
.liquid-glass {
|
|
121
|
+
background: rgba(255, 255, 255, 0.90);
|
|
122
|
+
backdrop-filter: none;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Framework Notes
|
|
128
|
+
|
|
129
|
+
#### React + Tailwind CSS
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
export function LiquidGlass({ children }: { children: React.ReactNode }) {
|
|
133
|
+
return (
|
|
134
|
+
<div className="relative overflow-hidden rounded-[2rem] bg-white/15 backdrop-blur-sm backdrop-saturate-[180%] border border-white/80 shadow-[0_8px_32px_rgba(31,38,135,0.2),inset_0_4px_20px_rgba(255,255,255,0.3)]">
|
|
135
|
+
{/* Inner reflection via absolute overlay */}
|
|
136
|
+
<div className="absolute inset-0 rounded-[2rem] bg-white/10 opacity-60 blur-[1px] pointer-events-none" />
|
|
137
|
+
<div className="relative z-10">{children}</div>
|
|
138
|
+
</div>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### React Native
|
|
144
|
+
|
|
145
|
+
Liquid glass is not directly achievable in React Native. Use `@react-native-community/blur` for a simplified frosted glass approximation. The SVG distortion filter has no React Native equivalent.
|
|
146
|
+
|
|
147
|
+
#### Vanilla CSS
|
|
148
|
+
|
|
149
|
+
Use the CSS + SVG filter approach from the implementation section. Define glass properties as custom properties for theme switching.
|
|
150
|
+
|
|
151
|
+
### Common Pitfalls
|
|
152
|
+
|
|
153
|
+
1. **Nested backdrop-filters**: stacking multiple `backdrop-filter` elements causes compounding blur and severe GPU cost. Limit to one glass layer per viewport section.
|
|
154
|
+
2. **Missing `-webkit-` prefix**: Safari requires `-webkit-backdrop-filter`. Without it, the surface appears opaque on all Apple devices.
|
|
155
|
+
3. **SVG filter on mobile Safari**: `feDisplacementMap` renders inconsistently on iOS Safari (WebKit bug). Disable the SVG distortion filter on mobile and rely on backdrop-filter only.
|
|
156
|
+
4. **No background content**: liquid glass over a flat solid color is invisible. Always ensure a rich visual layer behind the glass.
|
|
157
|
+
5. **Animating blur values**: transitioning `backdrop-filter` blur triggers compositing on every frame. Animate `opacity` or `transform` instead.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Examples Gallery
|
|
162
|
+
|
|
163
|
+
| Site | What They Do Well | Screenshot Description |
|
|
164
|
+
|------|-------------------|----------------------|
|
|
165
|
+
| Apple iOS 26 / macOS Tahoe | The canonical reference — system-wide liquid glass with real-time refraction and adaptive tint | Control Center, notifications, and app chrome all use dynamic glass |
|
|
166
|
+
| Apple Vision Pro UI | Liquid glass in spatial computing — panels float in 3D space with real depth-of-field blur | Floating app windows with environment showing through |
|
|
167
|
+
| The General Intelligence Company | High-blur glass navigation over pixel-art backdrop — demonstrates glass over illustration | Nav bar with vivid background visible through |
|
|
168
|
+
| Raycast (macOS app) | Launcher panels use frosted glass with subtle light reflection matching system appearance | Command palette floating over desktop content |
|
|
169
|
+
| Amie Calendar | Meeting cards and sidebar use liquid-glass-inspired translucency with light edge highlights | Calendar grid with frosted event cards |
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Accessibility
|
|
174
|
+
|
|
175
|
+
- **Contrast**: text on glass surfaces must meet WCAG 4.5:1 minimum. Increase fill opacity or add `text-shadow: 0 1px 3px rgba(0,0,0,0.3)` when contrast is insufficient
|
|
176
|
+
- **Reduced transparency**: respect `prefers-reduced-transparency: reduce` — replace glass with solid, opaque backgrounds
|
|
177
|
+
- **Reduced motion**: disable morph animations and SVG distortion for `prefers-reduced-motion: reduce`
|
|
178
|
+
- **Focus indicators**: glass surfaces swallow default focus rings — use `outline: 3px solid rgba(255,255,255,0.9)` with `outline-offset: 3px`
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Performance
|
|
183
|
+
|
|
184
|
+
- **Backdrop-filter cost**: ~15-25% more GPU than opaque surfaces. Scales with element area.
|
|
185
|
+
- **SVG filter cost**: `feDisplacementMap` + `feSpecularLighting` is expensive. Limit to 1-2 elements per page.
|
|
186
|
+
- **Avoid**: blur > 20px, nested backdrop-filters, animating blur values
|
|
187
|
+
- **Optimize**: use `transform: translateZ(0)` for GPU compositing, `isolation: isolate` to contain stacking context
|
|
188
|
+
- **Mobile**: reduce or remove SVG distortion filter entirely on mobile devices
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## When to Use / When to Avoid
|
|
193
|
+
|
|
194
|
+
### Use When
|
|
195
|
+
- Navigation bars, toolbars, floating panels
|
|
196
|
+
- Modal overlays, action sheets
|
|
197
|
+
- Cards layered over rich visual backgrounds (photography, gradients, aurora)
|
|
198
|
+
- Apple ecosystem-aligned products seeking visual consistency
|
|
199
|
+
|
|
200
|
+
### Avoid When
|
|
201
|
+
- Text-heavy content areas — transparency reduces sustained reading comfort
|
|
202
|
+
- Data tables, forms — functionality over aesthetics
|
|
203
|
+
- Performance-critical mobile experiences on mid-range devices
|
|
204
|
+
- Flat-color backgrounds where the glass effect is invisible
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Design Tokens
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"liquid-glass": {
|
|
213
|
+
"fill-light": "rgba(255, 255, 255, 0.15)",
|
|
214
|
+
"fill-dark": "rgba(0, 0, 0, 0.20)",
|
|
215
|
+
"blur": "2px",
|
|
216
|
+
"saturate": "180%",
|
|
217
|
+
"border-light": "rgba(255, 255, 255, 0.8)",
|
|
218
|
+
"border-dark": "rgba(255, 255, 255, 0.15)",
|
|
219
|
+
"radius": "2rem",
|
|
220
|
+
"shadow": "0 8px 32px rgba(31, 38, 135, 0.2)",
|
|
221
|
+
"shadow-inset": "inset 0 4px 20px rgba(255, 255, 255, 0.3)",
|
|
222
|
+
"reflection-opacity": "0.6",
|
|
223
|
+
"transition-timing": "cubic-bezier(0.175, 0.885, 0.32, 2.2)",
|
|
224
|
+
"morph-duration": "400ms",
|
|
225
|
+
"blur-transition": "200ms"
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Related
|
|
233
|
+
|
|
234
|
+
- [Glassmorphism](./glassmorphism.md) — predecessor with simpler frosted glass, no refraction
|
|
235
|
+
- [Aurora Gradients](./aurora-gradients.md) — ideal background layer under liquid glass
|
|
236
|
+
- [Dark Mode OLED](./dark-mode-oled.md) — surface token adaptation needed for dark variant
|