@phreshos/react-ui 0.1.8 → 0.1.9
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/README.md +47 -206
- package/dist/appearance-provider.d.ts +17 -0
- package/dist/appearance-provider.js +33 -0
- package/dist/button.d.ts +2 -2
- package/dist/button.js +7 -6
- package/dist/flex.js +3 -3
- package/dist/grid.js +3 -3
- package/dist/layout.d.ts +2 -2
- package/dist/layout.js +2 -2
- package/dist/main.d.ts +1 -1
- package/dist/main.js +1 -1
- package/dist/radius.d.ts +3 -3
- package/dist/radius.js +4 -4
- package/dist/scale.d.ts +3 -3
- package/dist/scale.js +1 -1
- package/dist/spacing.d.ts +4 -4
- package/dist/spacing.js +4 -4
- package/dist/surface.d.ts +22 -22
- package/dist/surface.js +22 -18
- package/package.json +3 -3
- package/dist/theme-provider.d.ts +0 -15
- package/dist/theme-provider.js +0 -20
package/README.md
CHANGED
|
@@ -1,228 +1,69 @@
|
|
|
1
1
|
# `@phreshos/react-ui`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Environment-neutral React components and visual interpretation for PhreshOS.
|
|
4
|
+
The package depends on React and Core contracts, not on either execution SDK.
|
|
4
5
|
|
|
5
|
-
##
|
|
6
|
+
## Appearance composition
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
React UI accepts an explicit `ThemeProperties` snapshot and does not require a
|
|
12
|
-
running PhreshOS environment:
|
|
13
|
-
|
|
14
|
-
```tsx
|
|
15
|
-
import { standardTheme } from "@phreshos/core"
|
|
16
|
-
import { Button, Flex, ThemeProvider } from "@phreshos/react-ui"
|
|
17
|
-
|
|
18
|
-
function Example() {
|
|
19
|
-
return <ThemeProvider theme={standardTheme}>
|
|
20
|
-
<Flex align="center" gap="small">
|
|
21
|
-
<Button onPress={() => console.log("save")}>Save</Button>
|
|
22
|
-
</Flex>
|
|
23
|
-
</ThemeProvider>
|
|
24
|
-
}
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Package status
|
|
28
|
-
|
|
29
|
-
This package is one component of a larger architecture that remains under
|
|
30
|
-
active testing. Its public surface is intentionally small and will grow only
|
|
31
|
-
as component contracts are established. It is usable outside a running
|
|
32
|
-
system, though its standard Theme contract comes from `@phreshos/core`.
|
|
33
|
-
|
|
34
|
-
The library is being built up from behavior contracts rather than from a
|
|
35
|
-
primitive dependency's component catalog. `Button` uses React Aria Components
|
|
36
|
-
for normalized pointer, keyboard, focus, disabled, and pending behavior,
|
|
37
|
-
without exposing that library as the public design language. Components still
|
|
38
|
-
under evaluation may compare React Aria Components against Base UI privately,
|
|
39
|
-
in tests.
|
|
40
|
-
|
|
41
|
-
The library's future icon language has a tree-shakeable public subpath,
|
|
42
|
-
`@phreshos/react-ui/icons`. That subpath deliberately exports nothing until an
|
|
43
|
-
icon source and its contracts have been selected.
|
|
44
|
-
|
|
45
|
-
`Grid` and `Flex` are appearance-neutral layout primitives. They preserve
|
|
46
|
-
native element properties, styles, and refs while naming the layout decisions
|
|
47
|
-
that recur throughout an interface:
|
|
48
|
-
|
|
49
|
-
```tsx
|
|
50
|
-
<Grid columns="repeat(auto-fit, minmax(12rem, 1fr))" gap="1rem">
|
|
51
|
-
...
|
|
52
|
-
</Grid>
|
|
53
|
-
|
|
54
|
-
<Flex align="center" justify="between" gap={12} wrap>
|
|
55
|
-
...
|
|
56
|
-
</Flex>
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Numeric gaps are expressed in pixels. Grid dimensions may be positive integer
|
|
60
|
-
counts or native CSS track expressions, leaving responsive behavior to CSS
|
|
61
|
-
rather than introducing a second breakpoint system.
|
|
62
|
-
|
|
63
|
-
Inside a `ThemeProvider`, React UI derives its own spacing levels from the
|
|
64
|
-
Theme's concrete default spacing:
|
|
65
|
-
|
|
66
|
-
```tsx
|
|
67
|
-
<Flex gap="small">...</Flex>
|
|
68
|
-
<Grid gap="large">...</Grid>
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
Structures that own native spacing pass the explicit Theme value to React UI's
|
|
72
|
-
general derivation hook:
|
|
8
|
+
React UI receives complete unresolved `Appearance` plus one effective
|
|
9
|
+
`"light" | "dark"` Theme:
|
|
73
10
|
|
|
74
11
|
```tsx
|
|
75
|
-
import {
|
|
76
|
-
|
|
77
|
-
const spacing = useScale(theme.spacing)
|
|
78
|
-
|
|
79
|
-
<section style={{ gap: spacing.large }} />
|
|
80
|
-
```
|
|
12
|
+
import { standardAppearance } from "@phreshos/core"
|
|
13
|
+
import { AppearanceProvider, Button, Surface } from "@phreshos/react-ui"
|
|
81
14
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
settings enable. The material uses a deterministic 64×64 micro-pattern derived
|
|
88
|
-
from the former shader grain; it creates no canvas, WebGL context, or shared
|
|
89
|
-
texture:
|
|
90
|
-
|
|
91
|
-
```tsx
|
|
92
|
-
<Surface className="grid rounded-xl shadow-lg">
|
|
93
|
-
...
|
|
94
|
-
</Surface>
|
|
95
|
-
|
|
96
|
-
<Surface color="strong" grain="large">...</Surface>
|
|
97
|
-
<Surface color="#101114" grain={0.2} backdrop={4} opacity={0.9}>...</Surface>
|
|
98
|
-
<Surface distortion={70} waves={8} ripples={4} saturation={1.4} brightness={1.04}>...</Surface>
|
|
15
|
+
export function Example() {
|
|
16
|
+
return <AppearanceProvider appearance={standardAppearance} theme="light">
|
|
17
|
+
<Surface><Button>Continue</Button></Surface>
|
|
18
|
+
</AppearanceProvider>
|
|
19
|
+
}
|
|
99
20
|
```
|
|
100
21
|
|
|
101
|
-
`
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
their semantic levels or direct values, and remain bounded by Core's Theme
|
|
106
|
-
limits. Radius and foreground remain ordinary Theme styles. Grain intensity
|
|
107
|
-
controls tonal distance while grain amount controls retained cell density.
|
|
108
|
-
Each Surface owns one uniform one-pixel inset edge derived from its resolved
|
|
109
|
-
material color; consumers supply only their radius and do not redraw the edge.
|
|
110
|
-
When either is zero, Surface creates no grain pattern or paths. Refraction and
|
|
111
|
-
native frost use independent backdrop layers so blur does not soften the
|
|
112
|
-
displaced image. Enabled organic, wave, and ripple fields are combined
|
|
113
|
-
mathematically before one displacement pass; each zero-valued field is absent,
|
|
114
|
-
and the filter and refraction layer are absent when all three are zero.
|
|
115
|
-
Backdrop blur emits no CSS function at zero. Neutral saturation and brightness
|
|
116
|
-
at one are also omitted. Animation defaults to zero; only a Surface with
|
|
117
|
-
visible grain and a positive rate joins the internal document clock, while
|
|
118
|
-
every texture and seed remains local to its own Surface.
|
|
119
|
-
|
|
120
|
-
The Theme stores unrestricted CSS background, foreground, and accent sources.
|
|
121
|
-
React UI derives the fixed `subtle`, `soft`, `base`, `strong`, and `intense`
|
|
122
|
-
treatments from any supplied color, preserving the value exactly at `base`.
|
|
123
|
-
`useColor(value)` memoizes that calculation without implicitly choosing a
|
|
124
|
-
Theme property. CSS performs the nearby mixing in OKLCH, and the System retains
|
|
125
|
-
only the concrete Theme color:
|
|
126
|
-
|
|
127
|
-
```tsx
|
|
128
|
-
import { useColor } from "@phreshos/react-ui"
|
|
129
|
-
|
|
130
|
-
const colors = useColor(theme.accent)
|
|
131
|
-
|
|
132
|
-
<strong style={{ color: colors.strong }} />
|
|
133
|
-
```
|
|
22
|
+
`useAppearance()` returns the unresolved value. `useTheme()` returns only the
|
|
23
|
+
effective mode. `useResolveTheme(themed)` resolves one property at the point
|
|
24
|
+
where it is consumed. Components follow the same rule, so the provider never
|
|
25
|
+
collapses Appearance into a second retained object.
|
|
134
26
|
|
|
135
|
-
|
|
136
|
-
levels are derived from the Theme's concrete radius through the same
|
|
137
|
-
`scale()` rule used for spacing, while numbers and CSS values remain explicit
|
|
138
|
-
overrides:
|
|
27
|
+
An application using the Client SDK composes the packages explicitly:
|
|
139
28
|
|
|
140
29
|
```tsx
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
Structures whose native element owns the shape derive from the explicit
|
|
146
|
-
radius value through the same React UI hook:
|
|
30
|
+
const appearance = useSystemAppearance()
|
|
31
|
+
const theme = useSystemTheme()
|
|
147
32
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const radius = useScale(theme.radius)
|
|
152
|
-
|
|
153
|
-
<section style={{ borderRadius: radius.large }} />
|
|
33
|
+
return <AppearanceProvider appearance={appearance} theme={theme}>
|
|
34
|
+
{children}
|
|
35
|
+
</AppearanceProvider>
|
|
154
36
|
```
|
|
155
37
|
|
|
156
|
-
|
|
157
|
-
control treatment matches the desktop's Start and sign-out controls, letting
|
|
158
|
-
the surrounding Theme material remain visible. It derives spacing and radius
|
|
159
|
-
from the Theme while keeping a single activation path across pointer, Enter,
|
|
160
|
-
and Space input:
|
|
38
|
+
## Levels
|
|
161
39
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
```
|
|
40
|
+
`useScale(value)` and `useColor(value)` derive semantic UI levels from one
|
|
41
|
+
concrete value. They do not select an Appearance property or read an
|
|
42
|
+
environment. Components resolve the property they need first and then derive
|
|
43
|
+
their local level.
|
|
167
44
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
45
|
+
Layout primitives accept native values without a provider. Semantic gaps and
|
|
46
|
+
radii require Appearance because their concrete source is `spacing` or
|
|
47
|
+
`radius`.
|
|
171
48
|
|
|
172
|
-
|
|
173
|
-
`standardTheme`, so the library remains usable without either environment SDK.
|
|
174
|
-
A Program can adapt its
|
|
175
|
-
observable Host value at the application boundary:
|
|
49
|
+
## Surface
|
|
176
50
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
51
|
+
`Surface` is the shared visual material. It accepts native `div` properties
|
|
52
|
+
plus local overrides for color, grain, grain amount, backdrop blur, opacity,
|
|
53
|
+
distortion, waves, ripples, saturation, and brightness. Omitted controls derive
|
|
54
|
+
from the resolved Appearance. A zero-valued optional effect is omitted from the
|
|
55
|
+
rendered material so disabled work costs nothing.
|
|
180
56
|
|
|
181
|
-
|
|
182
|
-
|
|
57
|
+
Each Surface owns its SVG material and border. Backdrop refraction and frost
|
|
58
|
+
remain separate compositor layers. Radius and foreground resolve from
|
|
59
|
+
Appearance; elevation stays with the surrounding layout.
|
|
183
60
|
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
<HostProvider provide={["theme"]} fallback={null}>
|
|
188
|
-
<ThemedApplication>{children}</ThemedApplication>
|
|
189
|
-
</HostProvider>
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
## Standing requirements
|
|
193
|
-
|
|
194
|
-
- Components preserve one recognizable visual identity.
|
|
195
|
-
- Props that select a semantic treatment accept only the values documented by
|
|
196
|
-
that component. Explicit native styles and supported CSS spacing, radius,
|
|
197
|
-
and color values remain available where the component contract allows them.
|
|
198
|
-
- `ThemeProvider` requires an explicit `theme` prop; it never discovers an
|
|
199
|
-
environment SDK or silently selects a global theme.
|
|
200
|
-
- The provider applies a replacement `theme` value immediately to its
|
|
201
|
-
descendants.
|
|
202
|
-
- Providers are scoped and nestable. The nearest `ThemeProvider` supplies the
|
|
203
|
-
complete theme for its descendants without affecting its parent or
|
|
204
|
-
siblings.
|
|
205
|
-
- Accessibility, keyboard behavior, focus, and form behavior are contractual.
|
|
206
|
-
- Components must work inside structurally isolated Program iframes.
|
|
207
|
-
- Public types and JSDoc are part of the product.
|
|
208
|
-
|
|
209
|
-
## Development
|
|
210
|
-
|
|
211
|
-
```bash
|
|
212
|
-
bun install --frozen-lockfile
|
|
213
|
-
bun run verify
|
|
214
|
-
```
|
|
61
|
+
## Components
|
|
215
62
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
and
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
candidates are not part of the package's public surface. Field covers labeling,
|
|
224
|
-
descriptions, validation, native states, and value changes. Select covers
|
|
225
|
-
collections, keyboard input, disabled options, form submission, and cleanup.
|
|
226
|
-
Dialog covers modal semantics, focus, dismissal, nesting, state changes, and
|
|
227
|
-
cleanup. Context Menu covers invocation, focus, actions, disabled items,
|
|
228
|
-
dismissal, and cleanup.
|
|
63
|
+
- `Surface`: shared material container.
|
|
64
|
+
- `Button`: normalized React Aria action with pending and disabled states.
|
|
65
|
+
- `Flex` and `Grid`: small layout primitives that preserve native props.
|
|
66
|
+
- `AppearanceProvider`, `useAppearance`, `useTheme`, `useResolveTheme`:
|
|
67
|
+
environment-neutral appearance composition.
|
|
68
|
+
- `useScale`, `useColor`, `resolveSpacing`, `resolveRadius`: explicit visual
|
|
69
|
+
derivation helpers.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { Appearance, Theme, ThemedValue } from "@phreshos/core";
|
|
3
|
+
/** Provides unresolved Appearance and one effective Theme to a React subtree. */
|
|
4
|
+
export declare function AppearanceProvider({ appearance, children, theme }: AppearanceProviderProps): import("react").JSX.Element;
|
|
5
|
+
/** Returns the complete unresolved Appearance supplied by the nearest provider. */
|
|
6
|
+
export declare function useAppearance(): Appearance;
|
|
7
|
+
/** Returns the effective Theme supplied by the nearest provider. */
|
|
8
|
+
export declare function useTheme(): Theme;
|
|
9
|
+
/** Resolves one themed value only where it is consumed. */
|
|
10
|
+
export declare function useResolveTheme<Value, DarkValue extends Value = never>(value: ThemedValue<Value, DarkValue>): Exclude<Value, undefined>;
|
|
11
|
+
/** Internal optional read for primitives that also accept direct values. */
|
|
12
|
+
export declare function useAppearanceIfAvailable(): Appearance | null;
|
|
13
|
+
export interface AppearanceProviderProps {
|
|
14
|
+
readonly appearance: Appearance;
|
|
15
|
+
readonly children: ReactNode;
|
|
16
|
+
readonly theme: Theme;
|
|
17
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
const missing = Symbol("AppearanceProvider");
|
|
4
|
+
const AppearanceContext = createContext(missing);
|
|
5
|
+
const ThemeContext = createContext(missing);
|
|
6
|
+
/** Provides unresolved Appearance and one effective Theme to a React subtree. */
|
|
7
|
+
export function AppearanceProvider({ appearance, children, theme }) {
|
|
8
|
+
return _jsx(AppearanceContext.Provider, { value: appearance, children: _jsx(ThemeContext.Provider, { value: theme, children: children }) });
|
|
9
|
+
}
|
|
10
|
+
/** Returns the complete unresolved Appearance supplied by the nearest provider. */
|
|
11
|
+
export function useAppearance() {
|
|
12
|
+
const appearance = useContext(AppearanceContext);
|
|
13
|
+
if (appearance === missing)
|
|
14
|
+
throw new Error("useAppearance() requires an AppearanceProvider");
|
|
15
|
+
return appearance;
|
|
16
|
+
}
|
|
17
|
+
/** Returns the effective Theme supplied by the nearest provider. */
|
|
18
|
+
export function useTheme() {
|
|
19
|
+
const theme = useContext(ThemeContext);
|
|
20
|
+
if (theme === missing)
|
|
21
|
+
throw new Error("useTheme() requires an AppearanceProvider");
|
|
22
|
+
return theme;
|
|
23
|
+
}
|
|
24
|
+
/** Resolves one themed value only where it is consumed. */
|
|
25
|
+
export function useResolveTheme(value) {
|
|
26
|
+
const theme = useTheme();
|
|
27
|
+
return (theme === "dark" && "dark" in value ? value.dark : value.light);
|
|
28
|
+
}
|
|
29
|
+
/** Internal optional read for primitives that also accept direct values. */
|
|
30
|
+
export function useAppearanceIfAvailable() {
|
|
31
|
+
const appearance = useContext(AppearanceContext);
|
|
32
|
+
return appearance === missing ? null : appearance;
|
|
33
|
+
}
|
package/dist/button.d.ts
CHANGED
|
@@ -15,11 +15,11 @@ export interface ButtonProps extends NativeButtonProps, RadiusProps {
|
|
|
15
15
|
readonly pending?: boolean;
|
|
16
16
|
/** Runs once for a normalized pointer, Enter, or Space activation. */
|
|
17
17
|
readonly onPress?: () => void;
|
|
18
|
-
/** Derives the Button's spacing from
|
|
18
|
+
/** Derives the Button's spacing from Appearance's concrete default. */
|
|
19
19
|
readonly size?: ScaleLevel;
|
|
20
20
|
/** Additional native styles that do not replace the Button's identity. */
|
|
21
21
|
readonly style?: CSSProperties;
|
|
22
22
|
}
|
|
23
|
-
/**
|
|
23
|
+
/** An Appearance-aware action with normalized pointer and keyboard behavior. */
|
|
24
24
|
export declare const Button: import("react").ForwardRefExoticComponent<ButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
25
25
|
export {};
|
package/dist/button.js
CHANGED
|
@@ -3,12 +3,13 @@ import { forwardRef } from "react";
|
|
|
3
3
|
import { Button as AriaButton } from "react-aria-components";
|
|
4
4
|
import { scale } from "./scale.js";
|
|
5
5
|
import { resolveRadius } from "./radius.js";
|
|
6
|
-
import {
|
|
7
|
-
/**
|
|
6
|
+
import { useAppearance, useResolveTheme } from "./appearance-provider.js";
|
|
7
|
+
/** An Appearance-aware action with normalized pointer and keyboard behavior. */
|
|
8
8
|
export const Button = forwardRef(function Button({ children, disabled = false, pending = false, onPress, radius = "medium", size = "medium", style, type = "button", ...properties }, ref) {
|
|
9
|
-
const
|
|
10
|
-
const spacing = scale(
|
|
11
|
-
const borderRadius = resolveRadius(radius,
|
|
9
|
+
const appearance = useAppearance();
|
|
10
|
+
const spacing = scale(useResolveTheme(appearance.spacing), size);
|
|
11
|
+
const borderRadius = resolveRadius(radius, appearance);
|
|
12
|
+
const foreground = useResolveTheme(appearance.foreground);
|
|
12
13
|
return _jsx(AriaButton, { ...properties, ref: ref, type: type, isDisabled: disabled, isPending: pending, onPress: onPress, style: ({ isFocusVisible, isHovered, isPressed }) => buttonStyle({
|
|
13
14
|
borderRadius,
|
|
14
15
|
disabled,
|
|
@@ -18,7 +19,7 @@ export const Button = forwardRef(function Button({ children, disabled = false, p
|
|
|
18
19
|
pending,
|
|
19
20
|
size,
|
|
20
21
|
spacing,
|
|
21
|
-
foreground
|
|
22
|
+
foreground,
|
|
22
23
|
style
|
|
23
24
|
}), children: children });
|
|
24
25
|
});
|
package/dist/flex.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef } from "react";
|
|
3
3
|
import { alignment, justification, resolveGap } from "./layout.js";
|
|
4
|
-
import {
|
|
4
|
+
import { useAppearanceIfAvailable } from "./appearance-provider.js";
|
|
5
5
|
/** A predictable Flexbox container with no visual appearance of its own. */
|
|
6
6
|
export const Flex = forwardRef(function Flex({ align, direction, gap, inline = false, justify, style, wrap, ...properties }, ref) {
|
|
7
|
-
const
|
|
7
|
+
const appearance = useAppearanceIfAvailable();
|
|
8
8
|
return _jsx("div", { ...properties, ref: ref, style: {
|
|
9
9
|
...style,
|
|
10
10
|
display: inline ? "inline-flex" : "flex",
|
|
11
11
|
alignItems: alignment(align) ?? style?.alignItems,
|
|
12
12
|
flexDirection: direction ?? style?.flexDirection,
|
|
13
13
|
flexWrap: wrap === undefined ? style?.flexWrap : wrap === "reverse" ? "wrap-reverse" : wrap ? "wrap" : "nowrap",
|
|
14
|
-
gap: gap === undefined ? style?.gap : resolveGap(gap,
|
|
14
|
+
gap: gap === undefined ? style?.gap : resolveGap(gap, appearance),
|
|
15
15
|
justifyContent: justification(justify) ?? style?.justifyContent
|
|
16
16
|
} });
|
|
17
17
|
});
|
package/dist/grid.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef } from "react";
|
|
3
3
|
import { alignment, justification, resolveGap, tracks } from "./layout.js";
|
|
4
|
-
import {
|
|
4
|
+
import { useAppearanceIfAvailable } from "./appearance-provider.js";
|
|
5
5
|
/** A predictable CSS Grid container with no visual appearance of its own. */
|
|
6
6
|
export const Grid = forwardRef(function Grid({ align, columns, flow, gap, inline = false, justify, rows, style, ...properties }, ref) {
|
|
7
|
-
const
|
|
7
|
+
const appearance = useAppearanceIfAvailable();
|
|
8
8
|
return _jsx("div", { ...properties, ref: ref, style: {
|
|
9
9
|
...style,
|
|
10
10
|
display: inline ? "inline-grid" : "grid",
|
|
11
11
|
alignItems: alignment(align) ?? style?.alignItems,
|
|
12
|
-
gap: gap === undefined ? style?.gap : resolveGap(gap,
|
|
12
|
+
gap: gap === undefined ? style?.gap : resolveGap(gap, appearance),
|
|
13
13
|
gridAutoFlow: flow ?? style?.gridAutoFlow,
|
|
14
14
|
gridTemplateColumns: tracks(columns, "columns") ?? style?.gridTemplateColumns,
|
|
15
15
|
gridTemplateRows: tracks(rows, "rows") ?? style?.gridTemplateRows,
|
package/dist/layout.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CSSProperties } from "react";
|
|
2
|
-
import type {
|
|
2
|
+
import type { Appearance } from "@phreshos/core";
|
|
3
3
|
import { type Spacing } from "./spacing.js";
|
|
4
4
|
/** Cross-axis alignment shared by layout containers. */
|
|
5
5
|
export type LayoutAlignment = "start" | "center" | "end" | "stretch" | "baseline";
|
|
@@ -10,5 +10,5 @@ export type LayoutGap = Spacing;
|
|
|
10
10
|
export declare function alignment(value: LayoutAlignment | undefined): CSSProperties["alignItems"] | undefined;
|
|
11
11
|
export declare function justification(value: LayoutJustification | undefined): CSSProperties["justifyContent"] | undefined;
|
|
12
12
|
/** Resolves semantic spacing while preserving explicit CSS gap values. */
|
|
13
|
-
export declare function resolveGap(value: LayoutGap | undefined,
|
|
13
|
+
export declare function resolveGap(value: LayoutGap | undefined, appearance: Appearance | null): CSSProperties["gap"];
|
|
14
14
|
export declare function tracks(value: number | string | undefined, property: "columns" | "rows"): string | undefined;
|
package/dist/layout.js
CHANGED
|
@@ -21,8 +21,8 @@ export function justification(value) {
|
|
|
21
21
|
return value === undefined ? undefined : justifications[value];
|
|
22
22
|
}
|
|
23
23
|
/** Resolves semantic spacing while preserving explicit CSS gap values. */
|
|
24
|
-
export function resolveGap(value,
|
|
25
|
-
return resolveSpacing(value,
|
|
24
|
+
export function resolveGap(value, appearance) {
|
|
25
|
+
return resolveSpacing(value, appearance);
|
|
26
26
|
}
|
|
27
27
|
export function tracks(value, property) {
|
|
28
28
|
if (value === undefined || typeof value === "string")
|
package/dist/main.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Components enter this surface only after their implementation-independent
|
|
5
5
|
* behavior contract has been established by the package's tests.
|
|
6
6
|
*/
|
|
7
|
-
export {
|
|
7
|
+
export { AppearanceProvider, useAppearance, useResolveTheme, useTheme, type AppearanceProviderProps } from "./appearance-provider.js";
|
|
8
8
|
export { Flex, type FlexProps } from "./flex.js";
|
|
9
9
|
export { Grid, type GridProps } from "./grid.js";
|
|
10
10
|
export { Surface, type SurfaceColor, type SurfaceProps } from "./surface.js";
|
package/dist/main.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Components enter this surface only after their implementation-independent
|
|
5
5
|
* behavior contract has been established by the package's tests.
|
|
6
6
|
*/
|
|
7
|
-
export {
|
|
7
|
+
export { AppearanceProvider, useAppearance, useResolveTheme, useTheme } from "./appearance-provider.js";
|
|
8
8
|
export { Flex } from "./flex.js";
|
|
9
9
|
export { Grid } from "./grid.js";
|
|
10
10
|
export { Surface } from "./surface.js";
|
package/dist/radius.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { CSSProperties } from "react";
|
|
2
|
-
import type {
|
|
2
|
+
import type { Appearance, Shapeable } from "@phreshos/core";
|
|
3
3
|
import { type ScaleLevel } from "./scale.js";
|
|
4
|
-
/**
|
|
4
|
+
/** An Appearance-derived level, pixel value, or explicit CSS corner radius. */
|
|
5
5
|
export type Radius = ScaleLevel | number | (string & {});
|
|
6
6
|
/** Shared semantic corner-radius capability for React UI components. */
|
|
7
7
|
export interface RadiusProps extends Shapeable<Radius> {
|
|
8
8
|
}
|
|
9
9
|
/** Resolves a Radius while preserving explicit CSS and pixel values. */
|
|
10
|
-
export declare function resolveRadius(value: Radius | undefined,
|
|
10
|
+
export declare function resolveRadius(value: Radius | undefined, appearance: Appearance | null): CSSProperties["borderRadius"];
|
package/dist/radius.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { isScaleLevel, scale } from "./scale.js";
|
|
2
2
|
/** Resolves a Radius while preserving explicit CSS and pixel values. */
|
|
3
|
-
export function resolveRadius(value,
|
|
3
|
+
export function resolveRadius(value, appearance) {
|
|
4
4
|
if (!isScaleLevel(value))
|
|
5
5
|
return value;
|
|
6
|
-
if (!
|
|
7
|
-
throw new Error("A semantic radius requires
|
|
8
|
-
return scale(
|
|
6
|
+
if (!appearance)
|
|
7
|
+
throw new Error("A semantic radius requires an AppearanceProvider");
|
|
8
|
+
return scale(appearance.radius.light, value);
|
|
9
9
|
}
|
package/dist/scale.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/** A visual level derived around one concrete
|
|
1
|
+
/** A visual level derived around one concrete Appearance value. */
|
|
2
2
|
export type ScaleLevel = "xsmall" | "small" | "medium" | "large" | "xlarge";
|
|
3
|
-
/** Every visual level derived around one concrete
|
|
3
|
+
/** Every visual level derived around one concrete Appearance value. */
|
|
4
4
|
export type NumericScale = Readonly<Record<ScaleLevel, number>>;
|
|
5
5
|
/** Returns whether a value names a React UI visual level. */
|
|
6
6
|
export declare function isScaleLevel(value: unknown): value is ScaleLevel;
|
|
@@ -10,5 +10,5 @@ export declare function scale(value: number, level: ScaleLevel): number;
|
|
|
10
10
|
export declare function numericScale(value: number): NumericScale;
|
|
11
11
|
/** Derives a multiplier while preserving its neutral value of one. */
|
|
12
12
|
export declare function scaleMultiplier(value: number, level: ScaleLevel): number;
|
|
13
|
-
/** Returns every visual level derived from one concrete
|
|
13
|
+
/** Returns every visual level derived from one concrete Appearance value. */
|
|
14
14
|
export declare function useScale(value: number): NumericScale;
|
package/dist/scale.js
CHANGED
|
@@ -28,7 +28,7 @@ export function numericScale(value) {
|
|
|
28
28
|
export function scaleMultiplier(value, level) {
|
|
29
29
|
return 1 + (value - 1) * factors[level];
|
|
30
30
|
}
|
|
31
|
-
/** Returns every visual level derived from one concrete
|
|
31
|
+
/** Returns every visual level derived from one concrete Appearance value. */
|
|
32
32
|
export function useScale(value) {
|
|
33
33
|
return useMemo(() => numericScale(value), [value]);
|
|
34
34
|
}
|
package/dist/spacing.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { CSSProperties } from "react";
|
|
2
|
-
import type {
|
|
2
|
+
import type { Appearance } from "@phreshos/core";
|
|
3
3
|
import { type ScaleLevel } from "./scale.js";
|
|
4
|
-
/**
|
|
4
|
+
/** An Appearance-derived level, pixel value, or explicit CSS spacing value. */
|
|
5
5
|
export type Spacing = ScaleLevel | number | (string & {});
|
|
6
6
|
/** Resolves spacing while preserving explicit CSS and pixel values. */
|
|
7
|
-
export declare function resolveSpacing(value: ScaleLevel,
|
|
8
|
-
export declare function resolveSpacing(value: Spacing | undefined,
|
|
7
|
+
export declare function resolveSpacing(value: ScaleLevel, appearance: Appearance | null): number;
|
|
8
|
+
export declare function resolveSpacing(value: Spacing | undefined, appearance: Appearance | null): CSSProperties["gap"];
|
package/dist/spacing.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { isScaleLevel, scale } from "./scale.js";
|
|
2
|
-
export function resolveSpacing(value,
|
|
2
|
+
export function resolveSpacing(value, appearance) {
|
|
3
3
|
if (!isScaleLevel(value))
|
|
4
4
|
return value;
|
|
5
|
-
if (!
|
|
6
|
-
throw new Error("Semantic spacing requires
|
|
7
|
-
return scale(
|
|
5
|
+
if (!appearance)
|
|
6
|
+
throw new Error("Semantic spacing requires an AppearanceProvider");
|
|
7
|
+
return scale(appearance.spacing.light, value);
|
|
8
8
|
}
|
package/dist/surface.d.ts
CHANGED
|
@@ -1,51 +1,51 @@
|
|
|
1
1
|
import type { ComponentPropsWithoutRef } from "react";
|
|
2
2
|
import { type ColorLevel } from "./color.js";
|
|
3
3
|
import { type ScaleLevel } from "./scale.js";
|
|
4
|
-
/**
|
|
4
|
+
/** An Appearance-derived treatment or direct CSS color. */
|
|
5
5
|
export type SurfaceColor = ColorLevel | (string & {});
|
|
6
6
|
/** Native div properties plus controls for the locally owned material. */
|
|
7
7
|
export type SurfaceProps = Omit<ComponentPropsWithoutRef<"div">, "color" | "opacity"> & Readonly<{
|
|
8
|
-
/**
|
|
8
|
+
/** Appearance-derived treatment or direct CSS material color. */
|
|
9
9
|
color?: SurfaceColor;
|
|
10
|
-
/**
|
|
10
|
+
/** Appearance-derived level or direct grain intensity from zero to one. */
|
|
11
11
|
grain?: ScaleLevel | number;
|
|
12
|
-
/**
|
|
12
|
+
/** Appearance-derived level or direct retained grain amount from zero to one. */
|
|
13
13
|
grainAmount?: ScaleLevel | number;
|
|
14
|
-
/**
|
|
14
|
+
/** Appearance-derived level or direct backdrop blur from zero to 24 CSS pixels. */
|
|
15
15
|
backdrop?: ScaleLevel | number;
|
|
16
|
-
/**
|
|
16
|
+
/** Appearance-derived level or direct material opacity from zero to one. */
|
|
17
17
|
opacity?: ScaleLevel | number;
|
|
18
|
-
/**
|
|
18
|
+
/** Appearance-derived level or direct organic displacement from zero to 140 pixels. */
|
|
19
19
|
distortion?: ScaleLevel | number;
|
|
20
|
-
/**
|
|
20
|
+
/** Appearance-derived level or direct directional displacement from zero to 40 pixels. */
|
|
21
21
|
waves?: ScaleLevel | number;
|
|
22
|
-
/**
|
|
22
|
+
/** Appearance-derived level or direct ripple displacement from zero to 40 pixels. */
|
|
23
23
|
ripples?: ScaleLevel | number;
|
|
24
|
-
/**
|
|
24
|
+
/** Appearance-derived level or direct backdrop saturation multiplier. */
|
|
25
25
|
saturation?: ScaleLevel | number;
|
|
26
|
-
/**
|
|
26
|
+
/** Appearance-derived level or direct backdrop brightness multiplier. */
|
|
27
27
|
brightness?: ScaleLevel | number;
|
|
28
28
|
}>;
|
|
29
29
|
/** Contains content above locally owned Surface material layers. */
|
|
30
|
-
export declare const Surface: import("react").ForwardRefExoticComponent<Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "
|
|
31
|
-
/**
|
|
30
|
+
export declare const Surface: import("react").ForwardRefExoticComponent<Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "opacity" | "color"> & Readonly<{
|
|
31
|
+
/** Appearance-derived treatment or direct CSS material color. */
|
|
32
32
|
color?: SurfaceColor;
|
|
33
|
-
/**
|
|
33
|
+
/** Appearance-derived level or direct grain intensity from zero to one. */
|
|
34
34
|
grain?: ScaleLevel | number;
|
|
35
|
-
/**
|
|
35
|
+
/** Appearance-derived level or direct retained grain amount from zero to one. */
|
|
36
36
|
grainAmount?: ScaleLevel | number;
|
|
37
|
-
/**
|
|
37
|
+
/** Appearance-derived level or direct backdrop blur from zero to 24 CSS pixels. */
|
|
38
38
|
backdrop?: ScaleLevel | number;
|
|
39
|
-
/**
|
|
39
|
+
/** Appearance-derived level or direct material opacity from zero to one. */
|
|
40
40
|
opacity?: ScaleLevel | number;
|
|
41
|
-
/**
|
|
41
|
+
/** Appearance-derived level or direct organic displacement from zero to 140 pixels. */
|
|
42
42
|
distortion?: ScaleLevel | number;
|
|
43
|
-
/**
|
|
43
|
+
/** Appearance-derived level or direct directional displacement from zero to 40 pixels. */
|
|
44
44
|
waves?: ScaleLevel | number;
|
|
45
|
-
/**
|
|
45
|
+
/** Appearance-derived level or direct ripple displacement from zero to 40 pixels. */
|
|
46
46
|
ripples?: ScaleLevel | number;
|
|
47
|
-
/**
|
|
47
|
+
/** Appearance-derived level or direct backdrop saturation multiplier. */
|
|
48
48
|
saturation?: ScaleLevel | number;
|
|
49
|
-
/**
|
|
49
|
+
/** Appearance-derived level or direct backdrop brightness multiplier. */
|
|
50
50
|
brightness?: ScaleLevel | number;
|
|
51
51
|
}> & import("react").RefAttributes<HTMLDivElement>>;
|
package/dist/surface.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef, useCallback, useId, useLayoutEffect, useRef } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { appearanceLimits } from "@phreshos/core";
|
|
4
4
|
import { color as deriveColor } from "./color.js";
|
|
5
5
|
import { isScaleLevel, scale, scaleMultiplier } from "./scale.js";
|
|
6
6
|
import { SurfaceMaterial } from "./surface-material.js";
|
|
7
|
-
import {
|
|
7
|
+
import { useAppearance, useResolveTheme } from "./appearance-provider.js";
|
|
8
8
|
const layerStyle = {
|
|
9
9
|
position: "absolute",
|
|
10
10
|
inset: 0,
|
|
@@ -13,7 +13,11 @@ const layerStyle = {
|
|
|
13
13
|
};
|
|
14
14
|
/** Contains content above locally owned Surface material layers. */
|
|
15
15
|
export const Surface = forwardRef(function Surface({ backdrop, brightness, children, color, distortion, grain, grainAmount, opacity, ripples, saturation, style, waves, ...properties }, forwardedRef) {
|
|
16
|
-
const
|
|
16
|
+
const appearance = useAppearance();
|
|
17
|
+
const background = useResolveTheme(appearance.background);
|
|
18
|
+
const foreground = useResolveTheme(appearance.foreground);
|
|
19
|
+
const radius = useResolveTheme(appearance.radius);
|
|
20
|
+
const surface = useResolveTheme(appearance.surface);
|
|
17
21
|
const identity = `phresh-surface-${useId().replaceAll(":", "")}`;
|
|
18
22
|
const element = useRef(null);
|
|
19
23
|
const capture = useCallback((node) => {
|
|
@@ -23,17 +27,17 @@ export const Surface = forwardRef(function Surface({ backdrop, brightness, child
|
|
|
23
27
|
else if (forwardedRef)
|
|
24
28
|
forwardedRef.current = node;
|
|
25
29
|
}, [forwardedRef]);
|
|
26
|
-
const resolved = resolveSurface({ backdrop, brightness, color, distortion, grain, grainAmount, opacity, ripples, saturation, waves },
|
|
30
|
+
const resolved = resolveSurface({ backdrop, brightness, color, distortion, grain, grainAmount, opacity, ripples, saturation, waves }, background, surface);
|
|
27
31
|
useLayoutEffect(() => {
|
|
28
32
|
const surface = element.current;
|
|
29
33
|
if (surface)
|
|
30
34
|
return prepareSurfaceLayout(surface);
|
|
31
35
|
});
|
|
32
36
|
return _jsxs("div", { ...properties, ref: capture, style: {
|
|
33
|
-
borderRadius:
|
|
34
|
-
color:
|
|
37
|
+
borderRadius: radius,
|
|
38
|
+
color: foreground,
|
|
35
39
|
...style
|
|
36
|
-
}, children: [resolved.refracts && _jsx(BackdropLayer, { name: "refraction", filter: `url("#${identity}-distortion")`, zIndex: -3 }), resolved.frost && _jsx(BackdropLayer, { name: "frost", filter: resolved.frost, zIndex: -2 }), _jsx(SurfaceBorder, { color: resolved.material.color, opacity: resolveScale("large", resolved.material.opacity,
|
|
40
|
+
}, children: [resolved.refracts && _jsx(BackdropLayer, { name: "refraction", filter: `url("#${identity}-distortion")`, zIndex: -3 }), resolved.frost && _jsx(BackdropLayer, { name: "frost", filter: resolved.frost, zIndex: -2 }), _jsx(SurfaceBorder, { color: resolved.material.color, opacity: resolveScale("large", resolved.material.opacity, appearanceLimits.surface.opacity) }), _jsx(SurfaceMaterial, { identity: identity, ...resolved.material }), children] });
|
|
37
41
|
});
|
|
38
42
|
/** Draws one uniform inset edge from the same color as the Surface material. */
|
|
39
43
|
function SurfaceBorder({ color, opacity }) {
|
|
@@ -55,19 +59,19 @@ function BackdropLayer({ filter, name, zIndex }) {
|
|
|
55
59
|
WebkitBackdropFilter: filter
|
|
56
60
|
} });
|
|
57
61
|
}
|
|
58
|
-
function resolveSurface(values,
|
|
62
|
+
function resolveSurface(values, background, surface) {
|
|
59
63
|
const material = {
|
|
60
|
-
color: resolveColor(values.color,
|
|
61
|
-
distortion: resolveScale(values.distortion,
|
|
62
|
-
grain: resolveScale(values.grain,
|
|
63
|
-
grainAmount: resolveScale(values.grainAmount,
|
|
64
|
-
opacity: resolveScale(values.opacity,
|
|
65
|
-
ripples: resolveScale(values.ripples,
|
|
66
|
-
waves: resolveScale(values.waves,
|
|
64
|
+
color: resolveColor(values.color, background),
|
|
65
|
+
distortion: resolveScale(values.distortion, surface.distortion, appearanceLimits.surface.distortion),
|
|
66
|
+
grain: resolveScale(values.grain, surface.grain, appearanceLimits.surface.grain),
|
|
67
|
+
grainAmount: resolveScale(values.grainAmount, surface.grainAmount, appearanceLimits.surface.grainAmount),
|
|
68
|
+
opacity: resolveScale(values.opacity, surface.opacity, appearanceLimits.surface.opacity),
|
|
69
|
+
ripples: resolveScale(values.ripples, surface.ripples, appearanceLimits.surface.ripples),
|
|
70
|
+
waves: resolveScale(values.waves, surface.waves, appearanceLimits.surface.waves)
|
|
67
71
|
};
|
|
68
|
-
const backdrop = resolveScale(values.backdrop,
|
|
69
|
-
const saturation = resolveMultiplier(values.saturation,
|
|
70
|
-
const brightness = resolveMultiplier(values.brightness,
|
|
72
|
+
const backdrop = resolveScale(values.backdrop, surface.backdrop, appearanceLimits.surface.backdrop);
|
|
73
|
+
const saturation = resolveMultiplier(values.saturation, surface.saturation, appearanceLimits.surface.saturation);
|
|
74
|
+
const brightness = resolveMultiplier(values.brightness, surface.brightness, appearanceLimits.surface.brightness);
|
|
71
75
|
const frost = [
|
|
72
76
|
backdrop === 0 ? "" : `blur(${backdrop}px)`,
|
|
73
77
|
saturation === 1 ? "" : `saturate(${saturation})`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phreshos/react-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "React components for coherent PhreshOS Program interfaces.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"prepack": "node --run test && node --run build"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
|
-
"@phreshos/core": "^0.1.
|
|
55
|
+
"@phreshos/core": "^0.1.17",
|
|
56
56
|
"react": "^19.2.0",
|
|
57
57
|
"react-dom": "^19.2.0"
|
|
58
58
|
},
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@base-ui/react": "^1.7.0",
|
|
64
|
-
"@phreshos/core": "^0.1.
|
|
64
|
+
"@phreshos/core": "^0.1.17",
|
|
65
65
|
"@testing-library/dom": "^10.4.1",
|
|
66
66
|
"@testing-library/react": "^16.3.0",
|
|
67
67
|
"@testing-library/user-event": "^14.6.1",
|
package/dist/theme-provider.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
2
|
-
import type { ThemeProperties } from "@phreshos/core";
|
|
3
|
-
/** Provides the nearest Theme value to one React subtree. */
|
|
4
|
-
export declare function ThemeProvider({ children, theme }: ThemeProviderProps): import("react").JSX.Element;
|
|
5
|
-
/** Returns the complete snapshot supplied by the nearest ThemeProvider. */
|
|
6
|
-
export declare function useTheme(): ThemeProperties;
|
|
7
|
-
/** Internal optional read used by primitives with both raw and themed values. */
|
|
8
|
-
export declare function useThemeIfAvailable(): ThemeProperties | null;
|
|
9
|
-
/** Properties accepted by ThemeProvider. */
|
|
10
|
-
export interface ThemeProviderProps {
|
|
11
|
-
/** Content that receives this Theme instead of any outer Theme. */
|
|
12
|
-
readonly children: ReactNode;
|
|
13
|
-
/** Complete Theme value for this subtree. */
|
|
14
|
-
readonly theme: ThemeProperties;
|
|
15
|
-
}
|
package/dist/theme-provider.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, useContext } from "react";
|
|
3
|
-
const missing = Symbol("ThemeProvider");
|
|
4
|
-
const ThemeContext = createContext(missing);
|
|
5
|
-
/** Provides the nearest Theme value to one React subtree. */
|
|
6
|
-
export function ThemeProvider({ children, theme }) {
|
|
7
|
-
return _jsx(ThemeContext.Provider, { value: theme, children: children });
|
|
8
|
-
}
|
|
9
|
-
/** Returns the complete snapshot supplied by the nearest ThemeProvider. */
|
|
10
|
-
export function useTheme() {
|
|
11
|
-
const properties = useContext(ThemeContext);
|
|
12
|
-
if (properties === missing)
|
|
13
|
-
throw new Error("useTheme() requires a ThemeProvider");
|
|
14
|
-
return properties;
|
|
15
|
-
}
|
|
16
|
-
/** Internal optional read used by primitives with both raw and themed values. */
|
|
17
|
-
export function useThemeIfAvailable() {
|
|
18
|
-
const properties = useContext(ThemeContext);
|
|
19
|
-
return properties === missing ? null : properties;
|
|
20
|
-
}
|