Liquid Glass
The material itself: a lens that refracts what’s behind it, with squircle bevel, specular rim, dispersion and spring physics.
Grouped glass toolbars with fixed spacers, a sliding selection lens, labelled icons and a soft scroll-edge blur.
Browser support · Real refraction (SVG displacement inside backdrop-filter) in Chromium browsers. Safari and Firefox fall back to a frosted blur with the same rim and highlights.
npx shadcn@latest add https://freedesignmd.com/r/glass-toolbar.json1/**2 * GlassToolbar — grouped Liquid Glass toolbar with a scroll edge effect.3 *4 * HIG rules baked in:5 * - Related actions share one glass capsule (a group); groups are separated by6 * fixed spacers, never stacked glass on glass.7 * - Icon groups and text groups are separate (don't mix in one capsule).8 * - Every icon has an accessibility label (also shown as a tooltip).9 * - The selected tool sits under a soft lens that slides between items.10 * - <ScrollEdge> softly blurs + fades content scrolling under a bar.11 *12 * Components:13 * <GlassToolbar label tone tooltips> — role="toolbar"; Arrow keys move between all14 * items across groups (roving tabindex).15 * <ToolbarGroup selectable value onChange> — one glass capsule.16 * <ToolbarItem id label icon? text? onClick?> — icon-only items need `label`.17 * <ToolbarSpacer flexible?> — fixed gap (default) or flexible.18 * <ScrollEdge edge="top"|"bottom" size tone> — place inside a scroll19 * container's positioned parent, above content and below the bar.20 * Browser support: refraction in Chromium; frosted glass elsewhere.21 */22import { Children, createContext, isValidElement, useContext, useEffect, useLayoutEffect, useRef, useState } from "react";23import type { CSSProperties, KeyboardEvent, ReactNode } from "react";24import { GlassScope, LiquidGlass, useGlassEnv, useSpring } from "./liquid-glass";25 26type Tone = "light" | "dark";27const ToneCtx = createContext<Tone>("light");28type GroupCtx = { selectable: boolean; value?: string; onChange?: (id: string) => void };29const GroupContext = createContext<GroupCtx>({ selectable: false });30 31const useIso = typeof window === "undefined" ? useEffect : useLayoutEffect;32 33export function GlassToolbar({34 label,35 tone = "light",36 tooltips = "below",37 children,38 className,39 style,40}: {41 label: string;42 tone?: Tone;43 /** Where icon tooltips appear: "below" for top bars, "above" for bottom bars. */44 tooltips?: "above" | "below";45 children: ReactNode;46 className?: string;47 style?: CSSProperties;48}) {49 const ref = useRef<HTMLDivElement | null>(null);50 // roving tabindex across every item in every group51 useEffect(() => {52 const el = ref.current;53 if (!el) return;54 const items = Array.from(el.querySelectorAll<HTMLElement>(".fd-gtb-item:not(:disabled)"));55 const current = items.find((i) => i.getAttribute("aria-pressed") === "true" || i.getAttribute("aria-checked") === "true") ?? items[0];56 items.forEach((i) => (i.tabIndex = i === current ? 0 : -1));57 });58 const onKey = (e: KeyboardEvent<HTMLDivElement>) => {59 const items = Array.from(ref.current?.querySelectorAll<HTMLElement>(".fd-gtb-item:not(:disabled)") ?? []);60 const i = items.indexOf(document.activeElement as HTMLElement);61 if (i < 0) return;62 const map: Record<string, number> = { ArrowRight: i + 1, ArrowLeft: i - 1, Home: 0, End: items.length - 1 };63 if (!(e.key in map)) return;64 e.preventDefault();65 const n = items[(map[e.key] + items.length) % items.length];66 items.forEach((x) => (x.tabIndex = -1));67 n.tabIndex = 0;68 n.focus();69 };70 return (71 <ToneCtx.Provider value={tone}>72 <GlassScope>73 <div ref={ref} role="toolbar" aria-label={label} className={["fd-gtb", `fd-gtb--${tone}`, `fd-gtb--tips-${tooltips}`, className ?? ""].filter(Boolean).join(" ")} style={style} onKeyDown={onKey}>74 <style>{css}</style>75 {children}76 </div>77 </GlassScope>78 </ToneCtx.Provider>79 );80}81 82export function ToolbarGroup({83 children,84 selectable = false,85 value,86 onChange,87 label,88}: {89 children: ReactNode;90 selectable?: boolean;91 value?: string;92 onChange?: (id: string) => void;93 label?: string;94}) {95 const tone = useContext(ToneCtx);96 const env = useGlassEnv();97 const inner = useRef<HTMLDivElement | null>(null);98 const lens = useRef<HTMLSpanElement | null>(null);99 const text = Children.toArray(children).some((c) => isValidElement(c) && (c.props as { text?: string }).text && !(c.props as { icon?: ReactNode }).icon);100 101 const paint = () => {102 const l = lens.current;103 if (!l) return;104 const st = env.reducedMotion ? 0 : Math.min(0.3, Math.abs(lx.velocity) * 0.0005);105 l.style.transform = `translateX(${lx.value.toFixed(2)}px) scale(${(1 + st).toFixed(4)}, ${(1 - st * 0.4).toFixed(4)})`;106 l.style.width = `${lw.value.toFixed(2)}px`;107 };108 const lx = useSpring(0, paint, { stiffness: 380, damping: 28 });109 const lw = useSpring(0, paint, { stiffness: 380, damping: 30 });110 const first = useRef(true);111 useIso(() => {112 if (!selectable) return;113 const el = inner.current?.querySelector<HTMLElement>(`[data-id="${CSS.escape(value ?? "")}"]`);114 if (!el || !lens.current) {115 if (lens.current) lens.current.style.opacity = "0";116 return;117 }118 lens.current.style.opacity = "1";119 if (first.current || env.reducedMotion) {120 lx.set(el.offsetLeft);121 lw.set(el.offsetWidth);122 first.current = false;123 } else {124 lx.to(el.offsetLeft);125 lw.to(el.offsetWidth);126 }127 }, [value, selectable, env.reducedMotion]);128 129 return (130 <GroupContext.Provider value={{ selectable, value, onChange }}>131 <LiquidGlass className={`fd-gtb-group${text ? " is-text" : ""}`} variant="regular" tone={tone} radius="capsule" role="group" aria-label={label}>132 <div ref={inner} className="fd-gtb-inner">133 {selectable && <span ref={lens} className="fd-gtb-lens" aria-hidden="true" />}134 {children}135 </div>136 </LiquidGlass>137 </GroupContext.Provider>138 );139}140 141export function ToolbarItem({142 id,143 label,144 icon,145 text,146 onClick,147 disabled,148 pressed,149 shortcut,150}: {151 id: string;152 /** Accessible name + tooltip. Required for icon-only items. */153 label: string;154 icon?: ReactNode;155 /** Text label (use text-only groups). */156 text?: string;157 onClick?: () => void;158 disabled?: boolean;159 /** Toggle state for non-selectable toggle items. */160 pressed?: boolean;161 shortcut?: string;162}) {163 const g = useContext(GroupContext);164 const selected = g.selectable ? g.value === id : pressed;165 return (166 <button167 type="button"168 className={`fd-gtb-item${text && !icon ? " is-text" : ""}`}169 data-id={id}170 aria-label={icon ? label : undefined}171 aria-pressed={selected === undefined ? undefined : selected}172 disabled={disabled}173 onClick={() => {174 if (g.selectable) g.onChange?.(id);175 onClick?.();176 }}177 >178 {icon ? <span className="fd-gtb-ico" aria-hidden="true">{icon}</span> : null}179 {text && !icon ? <span className="fd-gtb-txt">{text}</span> : null}180 {icon ? (181 <span className="fd-gtb-tip" role="presentation" aria-hidden="true">182 {label}183 {shortcut ? <kbd>{shortcut}</kbd> : null}184 </span>185 ) : null}186 </button>187 );188}189 190export function ToolbarSpacer({ flexible = false }: { flexible?: boolean }) {191 return <span className={`fd-gtb-spacer${flexible ? " is-flex" : ""}`} aria-hidden="true" />;192}193 194/** Scroll edge effect: a progressive blur + fade where content passes under a bar. */195export function ScrollEdge({ edge = "top", size = 96, tone = "light", style }: { edge?: "top" | "bottom"; size?: number; tone?: Tone; style?: CSSProperties }) {196 return (197 <div className={`fd-gtb-edge fd-gtb-edge--${edge} fd-gtb-edge--${tone}`} style={{ height: size, ...style }} aria-hidden="true">198 <style>{css}</style>199 <span style={{ "--b": "1px", "--a": "0%", "--z": "45%" } as CSSProperties} />200 <span style={{ "--b": "3px", "--a": "0%", "--z": "70%" } as CSSProperties} />201 <span style={{ "--b": "8px", "--a": "0%", "--z": "100%" } as CSSProperties} />202 </div>203 );204}205 206const css = `207.fd-gtb,.fd-gtb *,.fd-gtb *::before,.fd-gtb *::after{box-sizing:border-box}208.fd-gtb{display:flex;align-items:center;gap:0;margin:0;padding:0;background:none;border:0;color:#0B0B0F;font:500 14px/1 ui-sans-serif,-apple-system,BlinkMacSystemFont,"SF Pro Text","Inter","Segoe UI",Roboto,sans-serif;letter-spacing:normal}209.fd-gtb--dark{color:#F2F2F7}210.fd-gtb-group{flex:none;height:48px;padding:4px}211.fd-gtb-inner{position:relative;display:flex;align-items:center;gap:2px;height:100%;margin:0;padding:0}212.fd-gtb-lens{position:absolute;left:0;top:0;bottom:0;width:0;margin:0;padding:0;border-radius:999px;pointer-events:none;will-change:transform,width;213 background:rgba(11,11,15,0.08);box-shadow:inset 0 0 0 0.5px rgba(255,255,255,0.5)}214.fd-gtb--dark .fd-gtb-lens{background:rgba(255,255,255,0.16)}215.fd-gtb-spacer{display:block;flex:none;width:12px;height:1px;margin:0;padding:0}216.fd-gtb-spacer.is-flex{flex:1 1 12px}217.fd-gtb-item{position:relative;display:inline-flex;align-items:center;justify-content:center;flex:none;width:40px;height:40px;margin:0;padding:0;border:0;border-radius:999px;background:transparent;color:inherit;218 font:600 14px/1 ui-sans-serif,-apple-system,BlinkMacSystemFont,"SF Pro Text","Inter","Segoe UI",Roboto,sans-serif;letter-spacing:-0.005em;text-shadow:none;cursor:pointer;-webkit-appearance:none;appearance:none;-webkit-tap-highlight-color:transparent;219 transition:background-color var(--fd-dur-1) linear,transform var(--fd-dur-3) var(--fd-spring),color var(--fd-dur-2) var(--fd-ease-out)}220.fd-gtb-item.is-text{width:auto;padding:0 14px}221.fd-gtb-item:hover:not(:disabled){background:rgba(11,11,15,0.05)}222.fd-gtb--dark .fd-gtb-item:hover:not(:disabled){background:rgba(255,255,255,0.08)}223.fd-gtb-item:active:not(:disabled){transform:scale(0.9);transition-duration:var(--fd-dur-1)}224.fd-gtb-item[aria-pressed="true"]{color:#007AFF}225.fd-gtb--dark .fd-gtb-item[aria-pressed="true"]{color:#0A84FF}226.fd-gtb-item:disabled{opacity:0.35;cursor:default}227.fd-gtb-item:focus-visible{outline:2px solid color-mix(in srgb,#0A84FF 70%,transparent);outline-offset:2px}228.fd-gtb-ico,.fd-gtb-ico svg{display:block;width:22px;height:22px;margin:0;padding:0}229.fd-gtb-txt{display:block;margin:0;padding:0;white-space:nowrap}230.fd-gtb-tip{position:absolute;left:50%;top:calc(100% + 12px);display:flex;align-items:center;gap:8px;margin:0;padding:7px 10px;border-radius:10px;background:rgba(28,28,34,0.94);color:#F2F2F7;231 font:600 12px/1 ui-sans-serif,-apple-system,BlinkMacSystemFont,"SF Pro Text","Inter","Segoe UI",Roboto,sans-serif;letter-spacing:0;white-space:nowrap;pointer-events:none;opacity:0;transform:translate(-50%,-4px) scale(0.96);232 transition:opacity var(--fd-dur-1) linear,transform var(--fd-dur-2) var(--fd-ease-out);z-index:10;box-shadow:0 8px 20px -6px rgba(0,0,0,0.35)}233.fd-gtb--tips-above .fd-gtb-tip{top:auto;bottom:calc(100% + 12px);transform:translate(-50%,4px) scale(0.96)}234.fd-gtb-tip kbd{display:inline-block;margin:0;padding:3px 5px;border:0;border-radius:5px;background:rgba(255,255,255,0.14);color:#C7C7CC;font:500 11px/1 ui-monospace,"SF Mono","JetBrains Mono",Menlo,monospace}235.fd-gtb-item:hover .fd-gtb-tip,.fd-gtb-item:focus-visible .fd-gtb-tip{opacity:1;transform:translate(-50%,0) scale(1);transition-delay:350ms,350ms}236.fd-gtb-item:focus-visible .fd-gtb-tip{transition-delay:0ms,0ms}237.fd-gtb-edge{position:absolute;left:0;right:0;z-index:2;margin:0;padding:0;pointer-events:none;display:block}238.fd-gtb-edge--top{top:0}239.fd-gtb-edge--bottom{bottom:0}240.fd-gtb-edge span{position:absolute;inset:0;display:block;margin:0;padding:0;-webkit-backdrop-filter:blur(var(--b));backdrop-filter:blur(var(--b))}241.fd-gtb-edge--top span{-webkit-mask:linear-gradient(180deg,#000 var(--a),transparent var(--z));mask:linear-gradient(180deg,#000 var(--a),transparent var(--z))}242.fd-gtb-edge--bottom span{-webkit-mask:linear-gradient(0deg,#000 var(--a),transparent var(--z));mask:linear-gradient(0deg,#000 var(--a),transparent var(--z))}243.fd-gtb-edge--top.fd-gtb-edge--light{background:linear-gradient(180deg,rgba(247,247,249,0.85),rgba(247,247,249,0))}244.fd-gtb-edge--bottom.fd-gtb-edge--light{background:linear-gradient(0deg,rgba(247,247,249,0.85),rgba(247,247,249,0))}245.fd-gtb-edge--top.fd-gtb-edge--dark{background:linear-gradient(180deg,rgba(11,11,15,0.85),rgba(11,11,15,0))}246.fd-gtb-edge--bottom.fd-gtb-edge--dark{background:linear-gradient(0deg,rgba(11,11,15,0.85),rgba(11,11,15,0))}247@media (prefers-reduced-transparency: reduce){.fd-gtb-edge span{-webkit-backdrop-filter:none;backdrop-filter:none}}248@media (prefers-reduced-motion: reduce){.fd-gtb-item:active:not(:disabled){transform:none}.fd-gtb-tip{transition-duration:0ms}}249`;250 251/* ───────────────────────── demo: a design-doc editor ───────────────────────── */252 253const s = (d: ReactNode) => (254 <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">255 {d}256 </svg>257);258const IC = {259 sidebar: s(<><rect x="3.5" y="4.5" width="17" height="15" rx="3" /><path d="M9.5 4.5v15" /></>),260 undo: s(<path d="M9 7 4.5 11.5 9 16M5 11.5h9.5a5 5 0 0 1 0 10H12" />),261 redo: s(<path d="m15 7 4.5 4.5L15 16M19 11.5H9.5a5 5 0 0 0 0 10H12" />),262 select: s(<path d="m5.5 4 13 6.2-5.6 1.8-1.8 5.6Z" />),263 frame: s(<path d="M8 3.5v17M16 3.5v17M3.5 8h17M3.5 16h17" />),264 pen: s(<><path d="M12 19.5 19.5 12l-3-3L9 16.5Z" /><path d="m16.5 9-2-6-11 3.5L7 17.5l2-1M4 6.5l5 5" /></>),265 text: s(<path d="M5.5 6V4.5h13V6M12 4.5v15M9 19.5h6" />),266 comment: s(<path d="M5 18.5V7a2.5 2.5 0 0 1 2.5-2.5h9A2.5 2.5 0 0 1 19 7v6.5a2.5 2.5 0 0 1-2.5 2.5H9Z" />),267 bold: s(<path d="M7 4.5h6a3.5 3.5 0 0 1 0 7H7Zm0 7h7a4 4 0 0 1 0 8H7Z" />),268 italic: s(<path d="M10 4.5h8M6 19.5h8M14 4.5l-4 15" />),269 link: s(<path d="M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1" />),270 play: s(<path d="M7.5 5.5v13l11-6.5Z" />),271};272 273export function Demo() {274 const [tool, setTool] = useState("select");275 const [bold, setBold] = useState(false);276 const [italic, setItalic] = useState(true);277 const [mode, setMode] = useState("design");278 return (279 <div className="fd-gtbd">280 <style>{demoCss}</style>281 <div className="fd-gtbd-scroll" tabIndex={0} aria-label="Document canvas">282 <Canvas />283 </div>284 <ScrollEdge edge="top" size={110} />285 <ScrollEdge edge="bottom" size={110} />286 287 <div className="fd-gtbd-top">288 <GlassToolbar label="Document">289 <ToolbarGroup label="View">290 <ToolbarItem id="sidebar" label="Toggle sidebar" icon={IC.sidebar} shortcut="⌘ ." />291 </ToolbarGroup>292 <ToolbarSpacer />293 <ToolbarGroup label="History">294 <ToolbarItem id="undo" label="Undo" icon={IC.undo} shortcut="⌘ Z" />295 <ToolbarItem id="redo" label="Redo" icon={IC.redo} shortcut="⇧ ⌘ Z" disabled />296 </ToolbarGroup>297 <ToolbarSpacer flexible />298 <ToolbarGroup label="Mode" selectable value={mode} onChange={setMode}>299 <ToolbarItem id="design" label="Design" text="Design" />300 <ToolbarItem id="prototype" label="Prototype" text="Prototype" />301 <ToolbarItem id="dev" label="Dev mode" text="Dev" />302 </ToolbarGroup>303 <ToolbarSpacer flexible />304 <ToolbarGroup label="Share">305 <ToolbarItem id="play" label="Present" icon={IC.play} shortcut="⌥ ⌘ ↵" />306 </ToolbarGroup>307 <ToolbarSpacer />308 <ToolbarGroup label="Publish">309 <ToolbarItem id="share" label="Share" text="Share" />310 </ToolbarGroup>311 </GlassToolbar>312 </div>313 314 <div className="fd-gtbd-bottom">315 <GlassToolbar label="Tools" tooltips="above">316 <ToolbarGroup label="Tools" selectable value={tool} onChange={setTool}>317 <ToolbarItem id="select" label="Move" icon={IC.select} shortcut="V" />318 <ToolbarItem id="frame" label="Frame" icon={IC.frame} shortcut="F" />319 <ToolbarItem id="pen" label="Pen" icon={IC.pen} shortcut="P" />320 <ToolbarItem id="text" label="Text" icon={IC.text} shortcut="T" />321 <ToolbarItem id="comment" label="Comment" icon={IC.comment} shortcut="C" />322 </ToolbarGroup>323 <ToolbarSpacer />324 <ToolbarGroup label="Format">325 <ToolbarItem id="bold" label="Bold" icon={IC.bold} pressed={bold} onClick={() => setBold((b) => !b)} shortcut="⌘ B" />326 <ToolbarItem id="italic" label="Italic" icon={IC.italic} pressed={italic} onClick={() => setItalic((b) => !b)} shortcut="⌘ I" />327 <ToolbarItem id="link" label="Link" icon={IC.link} shortcut="⌘ K" />328 </ToolbarGroup>329 </GlassToolbar>330 </div>331 </div>332 );333}334 335function Canvas() {336 return (337 <div className="fd-gtbd-doc">338 <p className="fd-gtbd-crumb">Orbit Pay / Checkout v3 / Spec</p>339 <h1 className="fd-gtbd-h1">One-tap checkout</h1>340 <p className="fd-gtbd-lead">341 Reduce the card flow from four screens to one sheet. Target: +6.5% conversion on mobile web, no increase in chargebacks. Ships behind342 <code>checkout_v3</code> to 10% of EU traffic on 12 Nov.343 </p>344 <div className="fd-gtbd-boards">345 <div className="fd-gtbd-board">346 <span className="fd-gtbd-label">01 · Cart</span>347 <div className="fd-gtbd-phone fd-gtbd-phone--a">348 <i /> <i /> <i />349 <b />350 </div>351 </div>352 <div className="fd-gtbd-board">353 <span className="fd-gtbd-label">02 · Sheet</span>354 <div className="fd-gtbd-phone fd-gtbd-phone--b">355 <i /> <i />356 <b />357 </div>358 </div>359 <div className="fd-gtbd-board">360 <span className="fd-gtbd-label">03 · Done</span>361 <div className="fd-gtbd-phone fd-gtbd-phone--c">362 <em />363 <b />364 </div>365 </div>366 </div>367 <h2 className="fd-gtbd-h2">Flow</h2>368 <ol className="fd-gtbd-ol">369 <li>Customer taps <strong>Pay €48.20</strong>. The sheet rises with saved card and shipping prefilled.</li>370 <li>Biometric confirm. On failure, fall back to 3-D Secure inline — never a redirect.</li>371 <li>Receipt animates in place; the order number is copyable and the sheet dismisses after 2.4 s.</li>372 </ol>373 <h2 className="fd-gtbd-h2">Tokens</h2>374 <table className="fd-gtbd-table">375 <thead>376 <tr>377 <th scope="col">Token</th>378 <th scope="col">Light</th>379 <th scope="col">Dark</th>380 </tr>381 </thead>382 <tbody>383 {[384 ["accent", "#007AFF", "#0A84FF"],385 ["success", "#34C759", "#30D158"],386 ["sheet.radius", "34px", "34px"],387 ["sheet.inset", "8px", "8px"],388 ["button.height", "54px", "54px"],389 ].map(([a, b, c]) => (390 <tr key={a}>391 <td><code>{a}</code></td>392 <td>{b.startsWith("#") ? <span className="fd-gtbd-sw" style={{ background: b }} /> : null}{b}</td>393 <td>{c.startsWith("#") ? <span className="fd-gtbd-sw" style={{ background: c }} /> : null}{c}</td>394 </tr>395 ))}396 </tbody>397 </table>398 <h2 className="fd-gtbd-h2">Open questions</h2>399 <p className="fd-gtbd-p">400 Do we keep the promo-code field visible, or tuck it behind “Add code”? Data from the Lisbon pilot says the visible field costs us 1.1 points of401 conversion but raises AOV by €3.40. Leaning towards hiding it for returning customers only.402 </p>403 <p className="fd-gtbd-p">404 Apple Pay and Google Pay stay as the first option where available; the card sheet is the fallback. Klarna moves to the payment method picker.405 </p>406 </div>407 );408}409 410const F = `ui-sans-serif,-apple-system,BlinkMacSystemFont,"SF Pro Text","Inter","Segoe UI",Roboto,sans-serif`;411const D = `"SF Pro Display",ui-sans-serif,-apple-system,BlinkMacSystemFont,"Inter","Segoe UI",Roboto,sans-serif`;412const M = `ui-monospace,"SF Mono","JetBrains Mono",Menlo,monospace`;413const demoCss = `414.fd-gtbd,.fd-gtbd *,.fd-gtbd *::before,.fd-gtbd *::after{box-sizing:border-box}415.fd-gtbd{position:relative;height:100vh;min-height:640px;width:100%;margin:0;padding:0;overflow:hidden;background:#F7F7F9;color:#0B0B0F;font:400 15px/1.5 ${F};letter-spacing:normal}416.fd-gtbd-scroll{position:absolute;inset:0;overflow:auto;margin:0;padding:0;outline:none;scrollbar-width:none;417 background-color:#F7F7F9;background-image:radial-gradient(circle,#D4D4DC 1px,transparent 1.2px);background-size:22px 22px}418.fd-gtbd-scroll::-webkit-scrollbar{display:none}419.fd-gtbd-top{position:absolute;left:16px;right:16px;top:14px;z-index:3;margin:0;padding:0}420.fd-gtbd-bottom{position:absolute;left:0;right:0;bottom:22px;z-index:3;display:flex;justify-content:center;margin:0;padding:0 12px}421.fd-gtbd-doc{max-width:860px;margin:0 auto;padding:110px 32px 160px}422.fd-gtbd-doc h1,.fd-gtbd-doc h2,.fd-gtbd-doc p,.fd-gtbd-doc ol,.fd-gtbd-doc li,.fd-gtbd-doc table{margin:0;padding:0;border:0;color:inherit}423.fd-gtbd-crumb{margin:0 0 10px;font:500 13px/1.3 ${F};color:#6B6B76}424.fd-gtbd-h1{margin:0 0 14px;font:800 clamp(2.5rem,6vw,4rem)/1 ${D};letter-spacing:-0.04em;color:#0B0B0F}425.fd-gtbd-lead{margin:0 0 32px;max-width:62ch;font:400 1.25rem/1.5 ${F};letter-spacing:-0.01em;color:#3A3A44}426.fd-gtbd-doc code{margin:0 2px;padding:2px 6px;border-radius:6px;background:#E7E7EC;font:500 0.85em/1 ${M};color:#C2255C}427.fd-gtbd-boards{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;margin:0 0 40px}428.fd-gtbd-board{display:flex;flex-direction:column;gap:8px;margin:0;padding:0}429.fd-gtbd-label{font:600 12px/1 ${F};color:#6B6B76}430.fd-gtbd-phone{position:relative;aspect-ratio:9/17;margin:0;padding:22px 14px;border-radius:26px;background:#fff;box-shadow:0 0 0 1px #E2E2E8,0 20px 40px -24px rgba(20,20,40,0.35);display:flex;flex-direction:column;gap:10px;overflow:hidden}431.fd-gtbd-phone i{display:block;height:46px;border-radius:12px;background:linear-gradient(90deg,#FFD6A5 0 46px,#F2F2F7 46px)}432.fd-gtbd-phone i:nth-child(2){background:linear-gradient(90deg,#BDE0FE 0 46px,#F2F2F7 46px)}433.fd-gtbd-phone i:nth-child(3){background:linear-gradient(90deg,#CAFFBF 0 46px,#F2F2F7 46px)}434.fd-gtbd-phone b{display:block;margin-top:auto;height:44px;border-radius:999px;background:#0B0B0F}435.fd-gtbd-phone--b{background:linear-gradient(180deg,#E9EEF8,#C9D4EA)}436.fd-gtbd-phone--b::before{content:"";position:absolute;left:8px;right:8px;bottom:8px;top:42%;border-radius:22px;background:#fff;box-shadow:0 -8px 30px rgba(20,20,40,0.15)}437.fd-gtbd-phone--b i,.fd-gtbd-phone--b b{position:relative;z-index:1}438.fd-gtbd-phone--b i:first-child{margin-top:auto}439.fd-gtbd-phone--b b{margin-top:0;background:#007AFF}440.fd-gtbd-phone--c{align-items:center;justify-content:center}441.fd-gtbd-phone--c em{display:block;width:72px;height:72px;margin:auto 0 0;border-radius:50%;background:#34C759;box-shadow:inset 0 0 0 0 #fff;position:relative}442.fd-gtbd-phone--c em::after{content:"";position:absolute;left:26px;top:18px;width:16px;height:30px;border:solid #fff;border-width:0 5px 5px 0;transform:rotate(45deg)}443.fd-gtbd-phone--c b{width:100%}444.fd-gtbd-doc .fd-gtbd-h2{margin:8px 0 12px;font:700 1.5rem/1.2 ${D};letter-spacing:-0.02em;color:#0B0B0F}445.fd-gtbd-doc .fd-gtbd-ol{margin:0 0 32px;padding:0 0 0 24px;list-style:decimal outside;font:400 17px/1.6 ${F};color:#1C1C22}446.fd-gtbd-doc .fd-gtbd-ol li{margin:0 0 8px;padding:0 0 0 4px}447.fd-gtbd-doc .fd-gtbd-ol li::marker{font-weight:600;color:#6B6B76}448.fd-gtbd-ol strong{font-weight:650}449.fd-gtbd-table{width:100%;margin:0 0 32px;border-collapse:collapse;font:400 15px/1.4 ${F};background:#fff;border-radius:14px;overflow:hidden;box-shadow:0 0 0 1px #E2E2E8}450.fd-gtbd-table th{padding:10px 14px;text-align:left;font:600 12px/1.2 ${F};letter-spacing:0.04em;text-transform:uppercase;color:#6B6B76;background:#FAFAFC;border-bottom:1px solid #E7E7EC}451.fd-gtbd-table td{padding:10px 14px;border-bottom:1px solid #F0F0F3;color:#1C1C22;font:500 14px/1.3 ${M}}452.fd-gtbd-table td code{margin:0;color:#0B0B0F;background:none;padding:0}453.fd-gtbd-sw{display:inline-block;width:12px;height:12px;margin:0 8px -1px 0;border-radius:4px;box-shadow:inset 0 0 0 1px rgba(0,0,0,0.08)}454.fd-gtbd-p{margin:0 0 16px;max-width:66ch;font:400 17px/1.6 ${F};color:#1C1C22}455@media (max-width:760px){456 .fd-gtbd-top .fd-gtb > :nth-child(n+5):nth-child(-n+8){display:none}457 .fd-gtbd-top .fd-gtb > :nth-child(4){flex:1 1 12px}458 .fd-gtbd-doc{padding:96px 18px 150px}459 .fd-gtbd-boards{gap:10px}460 .fd-gtbd-phone{border-radius:18px;padding:14px 8px;gap:6px}461 .fd-gtbd-phone i{height:28px;border-radius:8px}462 .fd-gtbd-phone b{height:28px}463 .fd-gtbd-phone--c em{width:40px;height:40px}464 .fd-gtbd-phone--c em::after{left:14px;top:8px;width:10px;height:18px;border-width:0 4px 4px 0}465 .fd-gtbd-bottom .fd-gtb-item{width:36px}466}467`;The material itself: a lens that refracts what’s behind it, with squircle bevel, specular rim, dispersion and spring physics.
glass, glassProminent, clear and clear-prominent capsules in four sizes, with a springy press and a glow from the touch point.
Floating iOS 26 tab bar: a liquid lens springs between tabs, search sits apart, and it minimizes as you scroll.