import { type ReactNode, type RefObject } from "react"; import { View, type StyleProp, type ViewStyle } from "react-native"; import { type ColorTokens, type GlassTokens, type ColorScheme, type Palette } from "../tokens.js"; import type { GlassLayer, GlassSurfaceProps } from "./glass-surface-props.js"; export type { GlassLayer, GlassSurfaceProps } from "./glass-surface-props.js"; export declare const GlassBlurTargetContext: import("react").Context | null>; export declare const GlassWindowBlurTargetContext: import("react").Context | null>; /** What a surface's frost reports when it refuses the plane it was handed. */ export type BlurTargetRejection = (refused: RefObject) => void; /** The nearest bridge's rejection report, or null outside a bridge (and once it has none to offer). */ export declare function useBlurTargetRejection(): BlurTargetRejection | null; /** * The Modal blur bridge: carries the page's Android capture plane into a React Native * `Modal`, so the glass surfaces inside it (a sheet, a drawer panel, your own Modal's * cards) frost the page behind the Modal. It renders no view, only context. Wrap the * Modal's content in it, outside any ``, as Drawer and * ActionSheet do: * * `{content}` * * - Android: in an Expo SDK 57 build that links Canvas's native capture module, on * Android 12 or newer, and an app-root `OverlayProvider` that fills the window (its * default `flex: 1`, outside any scroller), whose page content is the plane. The outermost provider * wins, so a Modal opened from a nested host frosts the whole page, and so does a * Modal opened over another Modal. The frost samples that page only: not the root * outlet's toasts, a Modal beneath, or the Modal's own scrim and surfaces. * - iOS and the web provide nothing (their Liquid Glass and frost need no plane), and * so does Android when no `OverlayProvider` published a plane: the surfaces render * exactly as they would without the bridge, frost or their solid skin. * - Only directly inside a Modal's content, a separate native window that never * contains the plane. Placed in the main window (around page content, or in a * react-native-screens or expo-router modal screen, which shares the main window on * Android) the plane contains its surfaces: the native frost refuses it and * says so, and every surface under the bridge then resolves its complete solid skin. * The refusal comes from the native frost once it has mounted, so the surfaces show * a tint with no blur until it arrives. */ export declare function GlassModalBlurTarget({ children }: { children?: ReactNode; }): import("react").JSX.Element; export interface GlassBlurTargetHostProps { /** The provider wrapper's style (OverlayProvider's [FILL, style]). */ style?: StyleProp; /** Attached to the optional native capture host on supported Android; * left unattached (current stays null) everywhere else. */ targetRef: RefObject; /** The overlay outlet subtree; rendered after (and never inside) the target. */ outlet: ReactNode; children?: ReactNode; /** * On a window layer, whether its page (the provider's children) is hidden from * VoiceOver and TalkBack while a modal anchored card is open; undefined on a host * that is not a window layer. iOS hides the page through the outlet itself * (`accessibilityViewIsModal` hides a view's siblings), so only Android needs the * page as a node of its own to hide. */ pageHidden?: boolean; } export declare function frostMethodProps(supportsBlurTarget: boolean): { experimentalBlurMethod?: "dimezisBlurView"; }; export declare const GLASS_INTENSITY = 80; export declare const CONTENT_INTENSITY = 64; export declare const CLEAR_INTENSITY = 4; /** A clear surface retains a light neutral veil while letting the backdrop read. */ export declare function clearSurfaceTint(tokens: ColorTokens, dark: boolean): string; export declare const BRAND_TINT_ALPHA = 0.66; export declare const BRAND_TINT_STEP = 0.02; export declare const BRAND_INK_CONTRAST = 4.5; /** * The fill a frost surface paints: `surfaceUnderFill`'s colour (the layer's tint, a `tint` * wash, a solved `brand` colour, or a clear surface's veil). Every frost path paints it OVER * its blur, then the rim: the web's backdrop filter, iOS's and Android's expo-blur BlurView * and the native capture frost, as Dark Factory's GlassSurface does on every platform (its * BlurView, then its fill). A blur samples whatever is painted beneath it, so a fill there was * blurred into the material it belongs to (no material may sample itself) and dyed by the * frost's own light or dark veil: on the iOS simulator a red tint under the frost read up to * 53 levels a channel off the same tint over it, and the dark Card's content tint up to 10, * where over the blur the fill composites on the frosted page within half a level * (glass-surface-native-frost-self-sample-1; expo-blur's dark BlurView had halved a brand * fill's brightness the same way, 2:1 for its ink where `brandTint` promised 4.5:1). Over the * blur the solved colour is the rendered one and the material still shows through what the * fill leaves. Only iOS 26's Liquid Glass keeps a fill beneath it (its host's own choice), and * takes a brand as its tintColor instead. */ export declare function frostFill(theme: { glass: GlassTokens; tokens: ColorTokens; dark: boolean; palette: Palette; scheme: ColorScheme; }, props: Pick): string; /** * The ink a skin paints on a brand fill: the foreground of the token pair the fill * belongs to (the call-to-action, primary, destructive, success or warning pair), else * the stronger of black and white. Solving the tint for a guessed ink failed where the * guess and the skin disagree: a fill both inks clear picks the stronger one, while the * skin paints its own pair's. */ export declare function brandInk(tokens: ColorTokens, brand: string): string; /** The brand colour as a translucent under-fill whose `ink` stays legible over `page`. */ export declare function brandTint(brand: string, page: string | readonly string[], ink?: string): string; /** The fill a surface's material carries (over the frost's blur, `frostFill`; beneath iOS * 26's Liquid Glass): `tint`, else the `brand` colour tinted so the ink painted on it stays * legible over the page (both read from `tokens`) and over every bed of the page's frosted * frame (`beds`, `frameBedsFor`), else the layer's own token. */ export declare function surfaceUnderFill(glass: GlassTokens, layer: GlassLayer, brand?: string, tint?: string, tokens?: ColorTokens, beds?: readonly string[]): string; /** The frost blur strength for a layer (a sheer surface stays lighter still). */ export declare function surfaceIntensity(layer: GlassLayer, sheer: boolean | undefined): number; /** Original Argus native blur calibration: 24px maps to 100, 2px to rounded 8. */ export declare const FRAME_FROST_INTENSITY: { readonly frame: 100; readonly veil: 8; }; export declare const SHEER_INTENSITY = 50; export declare const SHEER_FILL_OPACITY = 0.75; export declare const CONTRAST_BORDER_WIDTH = 1; export declare function contrastBorder(tokens: ColorTokens): ViewStyle; /** A style's border width on each side. */ export interface BorderSides { top: number; bottom: number; start: number; end: number; } /** * A style's border width on each side, resolved as React Native resolves it: a side's own * key over `borderWidth`, the logical start and end keys over the physical left and right * ones, and a physical left or right key read as the start or end side (React Native swaps * left and right in a right-to-left layout by default). */ export declare function borderSides(style: StyleProp): BorderSides; /** * The Increase Contrast border for one surface: on every side at least the hairline and * never thinner than the surface's own border there, so Increase Contrast only ever adds * weight (a 2 px radio ring or switch track stays 2 px), in `foreground`. A surface whose * border shows a STATE (`stateBorder`: a focused or errored field, an open trigger, a * focused slider knob, the current step) keeps every border colour its style names, since * that border is the state's only indicator (overwriting a field's `ring` focus border * would leave a keyboard user no focus cue at all): a full border keeps its colour all * round, and a Material 3 underline field its bottom edge, the active indicator, while the * sides it names no colour for take the contrasting border. A state style that names no * border colour has no state border to keep, so it takes the contrasting border. */ export declare function contrastBorderFor(tokens: ColorTokens, style: StyleProp, stateBorder?: boolean): ViewStyle; /** * The fill and border colours of a style turned transparent: what the material supplies * instead, under GlassBox and under a GlassPane's host (`paneStyle`). A border that shows a * state keeps its colours (CLAUDE.md, the glass model: a state border stays over the pane, * and only a resting hairline gives way to the rim). */ export declare function clearedPaint(style: StyleProp, stateBorder: boolean): ViewStyle; /** No border width on any side. */ export declare const NO_SIDES: Readonly; /** * The border widths a resting host trades for padding under glass, per side: each side's * own border, resolved as React Native resolves it (`borderSides`), except where that side's * padding is not a number (a percentage), which cannot take the border's width and keeps it. */ export declare function tradedSides(style: StyleProp): BorderSides; /** * A resting border traded for padding: every border width the style names becomes 0 and * each side's padding grows by that side's border width, with the fill and border colours * cleared (`clearedPaint`). A side whose padding is not a number keeps its border, * transparent. Where the style names a physical left or right width or padding, the * physical keys are restated beside the logical ones with the same values, so whichever of * the two a renderer ranks first gives the same box. The node keeps its size and its * content stays where it was, while its padding box grows to its whole footprint: under * GlassBox and under a GlassPane's host (`paneStyle`) the material fills that box, so it * covers the control's whole solid size (the owner's GP-1 decision). */ export declare function borderAsPadding(style: StyleProp): ViewStyle; /** * An absolutely placed child of a host whose resting border became padding, kept where solid * places it: absolute offsets count from the padding box, which grew over the border, so each * side the child is pinned to by a number gains the border width traded there (`edges`, a * physical left or right read as start or end). Returned unchanged where nothing was traded. */ export declare function placedInside(edges: BorderSides, child: StyleProp): StyleProp; /** * The material's corners: the host's corner radii and its `borderCurve`, and where the material * sits inside a kept border (`inside`) each radius less the narrower border it meets, the inner * curve of that border, so the material's corner never leaves a gap and never pokes over the * border. The curve keeps the skin's shape on the material: an iOS skin's continuous corner (a * Card, a list, the Drawer sheet) stays continuous on the clip, the fill, the frost, the rim and * Liquid Glass's own corner, where the material once drew the circular one under it. A browser * has no curve to draw, and React Native Web drops the key. */ export declare function materialCorners(shape: StyleProp, inside?: boolean): ViewStyle; export declare function GlassBox({ style, children, pointerEvents, testID, role, accessibilityLabel, onLayout, onAccessibilityEscape, stateBorder, material, solid, }: GlassSurfaceProps & { material: ReactNode; solid?: boolean; }): import("react").JSX.Element; export declare function PlainSurface({ style, children, pointerEvents, testID, role, accessibilityLabel, onLayout, onAccessibilityEscape }: GlassSurfaceProps): import("react").JSX.Element; /** * The style a host hands GlassBox: the skin's style, with the contrasting border * (`contrastBorderFor`) under Increase Contrast. Every platform host reads it, so the * accessibility ladder is one: under Reduce Transparency or Increase Contrast the material * resolves solid (`resolveMaterial`, Increase Contrast first) and GlassBox renders the * skin's own opaque fill, a semantic surface token glass never rewrites, and Increase * Contrast adds this border, keeping a state border's colour. */ export declare function contrastStyle(theme: { increasedContrast: boolean; tokens: ColorTokens; }, style: StyleProp, stateBorder?: boolean): StyleProp; export declare const MATERIAL_FILL: ViewStyle; export declare const SPECULAR_RIM: { readonly light: "inset 0 1px 1px rgba(255,255,255,0.55), inset 0 0 0 0.5px rgba(255,255,255,0.40), inset 0 -1px 1.5px rgba(0,0,0,0.06)"; readonly dark: "inset 0 1px 1px rgba(255,255,255,0.16), inset 0 0 0 0.5px rgba(255,255,255,0.10), inset 0 -1px 1.5px rgba(0,0,0,0.22)"; }; export declare function specularRim(style: StyleProp, dark: boolean, inside?: boolean): ViewStyle; export declare const RIM_BAND_PIXELS = 4096; export declare const RIM_BAND_OVERLAP = 8; /** * The native frost's specular rim (`SPECULAR_RIM`), on iOS's and Android's frost: one rim over * a surface up to `RIM_BAND_PIXELS` tall, and past it a column of bands, the first with the top * edge and corners, the last with the bottom ones, each the rim clipped to its band and running * `RIM_BAND_OVERLAP` past the edges it does not own, so the bands together draw exactly the one * rim. * * Its contract, in every frame: * * - No shadow view is ever taller than a band and its overlaps, whatever the surface's height * does before React hears of it. The bound is in each view's own style, never in the band * count: the single rim is capped at a band (`maxHeight`), each band's rim has a fixed * height, and the last band's rim keeps that height at the surface's bottom edge. So a * surface that grows in a commit the rim does not render in (a child's own state, a reflow) * never asks Core Animation for a shadow past its 8192 px limit. What it costs, for the * frames until that growth's `onLayout` lands: a grown single rim stops at a band's height, * its bottom edge across the surface there and no rim below it; past one band, the last * band's rim stays at the bottom edge and the length between it and the band above has no * rim, its top edge showing where it starts. * - It reads the surface's height in every commit it renders in, mount or update, from a * layout effect: React Native's New Architecture lays out in that commit and applies the * effect's update before the frame is drawn, as a browser does, so a surface the rim mounts * on, or grows or shrinks in a commit that re-renders its host (a DataTable given more * rows), draws its rim whole or in its bands from that commit's frame, never a rim cut off * across it. Where no height can be read there (nothing reports `offsetHeight`), it draws * nothing until the surface's first layout lands, never a guess. * - A surface no taller than a band draws the one rim, uncapped in effect: the cap only binds * past a band, so its look is `specularRim`'s own. * * Between those reads it follows `onLayout`, and bands past a shrinking surface's end are * clipped away. */ export declare function SpecularRim({ style, dark, inside }: { style: StyleProp; dark: boolean; inside?: boolean; }): import("react").JSX.Element; export declare function materialFill(style: StyleProp, inside?: boolean): ViewStyle; //# sourceMappingURL=glass-surface.shared.d.ts.map