@mrmeg/expo-ui 0.8.0 → 0.9.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/LLM_USAGE.md CHANGED
@@ -125,8 +125,9 @@ Token intent:
125
125
 
126
126
  Use `getShadowStyle()` for package surfaces that need elevation. It supports
127
127
  `base`, `soft`, `sharp`, `subtle`, `elevated`, `glow`, `glass`, `card`,
128
- `cardHover`, and `cardSubtle`, returning native shadow/elevation off web and
129
- CSS `boxShadow` on web. Use `getFocusRingStyle()` for web focus styling. Keep
128
+ `cardHover`, and `cardSubtle`, returning a cross-platform `boxShadow` value
129
+ (RN 0.85 + react-native-web 0.21 deprecate the legacy `shadow*` props). Use
130
+ `getFocusRingStyle()` for web focus styling. Keep
130
131
  web controls compact, but preserve mobile tap comfort with package controls
131
132
  that already provide native hit slop or 44px touch rows.
132
133
 
@@ -64,7 +64,7 @@ function useToggleGroupContext() {
64
64
  * ```
65
65
  */
66
66
  function ToggleGroup({ variant = "default", size = "default", children, ...props }) {
67
- const { theme } = useTheme();
67
+ const { getShadowStyle } = useTheme();
68
68
  const contextValue = React.useMemo(() => ({ variant, size }), [variant, size]);
69
69
  // Count valid children for first/last detection
70
70
  const childrenArray = React.Children.toArray(children);
@@ -89,12 +89,7 @@ function ToggleGroup({ variant = "default", size = "default", children, ...props
89
89
  alignItems: "center",
90
90
  borderRadius: spacing.radiusMd,
91
91
  // No shadow on Android - causes text background artifact
92
- ...(variant === "outline" && Platform.OS === "ios" && {
93
- shadowColor: theme.colors.overlay,
94
- shadowOffset: { width: 0, height: 1 },
95
- shadowOpacity: 0.05,
96
- shadowRadius: 2,
97
- }),
92
+ ...(variant === "outline" && Platform.OS === "ios" && getShadowStyle("subtle")),
98
93
  ...(Platform.OS === "web" && {
99
94
  width: "fit-content",
100
95
  }),
@@ -32,7 +32,7 @@ interface ExtendedColorScheme {
32
32
  * - getTextColorForBackground("#000") → "light"
33
33
  * - getContrastingColor("#f4f4f4", "#222", "#fff") → "#222"
34
34
  * - withAlpha("#336699", 0.6) → "rgba(51,102,153,0.6)"
35
- * - getShadowStyle('base') → { shadowColor, shadowOffset, ... }
35
+ * - getShadowStyle('base') → { boxShadow: "0px 1px 3px rgba(0, 0, 0, 0.1)" }
36
36
  */
37
37
  export declare function useTheme(): ExtendedColorScheme & {
38
38
  toggleTheme: () => void;
@@ -42,7 +42,7 @@ function getCachedOrCompute(key, compute) {
42
42
  * - getTextColorForBackground("#000") → "light"
43
43
  * - getContrastingColor("#f4f4f4", "#222", "#fff") → "#222"
44
44
  * - withAlpha("#336699", 0.6) → "rgba(51,102,153,0.6)"
45
- * - getShadowStyle('base') → { shadowColor, shadowOffset, ... }
45
+ * - getShadowStyle('base') → { boxShadow: "0px 1px 3px rgba(0, 0, 0, 0.1)" }
46
46
  */
47
47
  export function useTheme() {
48
48
  const userTheme = useThemeStore((s) => s.userTheme);
@@ -93,102 +93,37 @@ export function useTheme() {
93
93
  }, [setTheme, userTheme]);
94
94
  /**
95
95
  * getShadowStyle
96
- * Returns platform-appropriate shadow styles
97
- * - Web: uses CSS boxShadow through React Native Web
98
- * - Native: uses shadowColor, shadowOffset, shadowOpacity, shadowRadius, elevation
96
+ * Returns a cross-platform shadow style using the `boxShadow` style prop.
97
+ *
98
+ * RN 0.85 + react-native-web 0.21 deprecate the legacy `shadow*` props in
99
+ * favor of `boxShadow`, which is supported on both native and web. Because
100
+ * `boxShadow` has no separate opacity field, each preset's opacity is folded
101
+ * into the color's alpha via `withAlpha`. `elevation` is dropped — `boxShadow`
102
+ * renders shadows on Android in 0.85+.
99
103
  */
100
104
  const getShadowStyle = useCallback((type) => {
105
+ // Each preset: [offsetX, offsetY, blurRadius, color, opacity].
106
+ // Darker themes get a stronger alpha so shadows stay visible.
107
+ const boost = theme.dark ? 3 : 1;
108
+ const overlay = theme.colors.overlay;
101
109
  const shadowConfigs = {
102
- base: {
103
- shadowColor: theme.colors.overlay,
104
- shadowOffset: { width: 0, height: 1 },
105
- shadowOpacity: 0.1,
106
- shadowRadius: 3,
107
- elevation: 3,
108
- },
109
- soft: {
110
- shadowColor: theme.colors.overlay,
111
- shadowOffset: { width: 0, height: 4 },
112
- shadowOpacity: 0.1,
113
- shadowRadius: 6,
114
- elevation: 4,
115
- },
116
- sharp: {
117
- shadowColor: theme.colors.overlay,
118
- shadowOffset: { width: 0, height: 1 },
119
- shadowOpacity: 0.15,
120
- shadowRadius: 1,
121
- elevation: 2,
122
- },
123
- subtle: {
124
- shadowColor: theme.colors.overlay,
125
- shadowOffset: { width: 0, height: 1 },
126
- shadowOpacity: 0.05,
127
- shadowRadius: 2,
128
- elevation: 1,
129
- },
130
- elevated: {
131
- shadowColor: theme.colors.overlay,
132
- shadowOffset: { width: 0, height: 20 },
133
- shadowOpacity: 0.15,
134
- shadowRadius: 40,
135
- elevation: 16,
136
- },
137
- glow: {
138
- shadowColor: theme.colors.primary,
139
- shadowOffset: { width: 0, height: 4 },
140
- shadowOpacity: 0.4,
141
- shadowRadius: 20,
142
- elevation: 10,
143
- },
144
- glass: {
145
- shadowColor: theme.colors.overlay,
146
- shadowOffset: { width: 0, height: 4 },
147
- shadowOpacity: 0.05,
148
- shadowRadius: 30,
149
- elevation: 4,
150
- },
151
- card: {
152
- shadowColor: theme.colors.overlay,
153
- shadowOffset: { width: 0, height: 2 },
154
- shadowOpacity: 0.08,
155
- shadowRadius: 8,
156
- elevation: 4,
157
- },
158
- cardHover: {
159
- shadowColor: theme.colors.overlay,
160
- shadowOffset: { width: 0, height: 8 },
161
- shadowOpacity: 0.12,
162
- shadowRadius: 24,
163
- elevation: 8,
164
- },
165
- cardSubtle: {
166
- shadowColor: theme.colors.overlay,
167
- shadowOffset: { width: 0, height: 1 },
168
- shadowOpacity: 0.08,
169
- shadowRadius: 3,
170
- elevation: 2,
171
- },
110
+ base: { x: 0, y: 1, blur: 3, color: overlay, opacity: 0.1 },
111
+ soft: { x: 0, y: 4, blur: 6, color: overlay, opacity: 0.1 },
112
+ sharp: { x: 0, y: 1, blur: 1, color: overlay, opacity: 0.15 },
113
+ subtle: { x: 0, y: 1, blur: 2, color: overlay, opacity: 0.05 },
114
+ elevated: { x: 0, y: 20, blur: 40, color: overlay, opacity: 0.15 },
115
+ glow: { x: 0, y: 4, blur: 20, color: theme.colors.primary, opacity: 0.4 },
116
+ glass: { x: 0, y: 4, blur: 30, color: overlay, opacity: 0.05 },
117
+ card: { x: 0, y: 2, blur: 8, color: overlay, opacity: 0.08 },
118
+ cardHover: { x: 0, y: 8, blur: 24, color: overlay, opacity: 0.12 },
119
+ cardSubtle: { x: 0, y: 1, blur: 3, color: overlay, opacity: 0.08 },
120
+ };
121
+ const { x, y, blur, color, opacity } = shadowConfigs[type];
122
+ // Don't boost the glow accent — it's already a deliberate, vivid alpha.
123
+ const alpha = color === theme.colors.primary ? opacity : Math.min(opacity * boost, 1);
124
+ return {
125
+ boxShadow: `${x}px ${y}px ${blur}px ${withAlpha(color, alpha)}`,
172
126
  };
173
- const config = shadowConfigs[type];
174
- if (Platform.OS === "web") {
175
- const webShadows = {
176
- base: { boxShadow: theme.dark ? "0 1px 2px rgba(0, 0, 0, 0.45)" : "0 1px 2px rgba(0, 0, 0, 0.08)" },
177
- soft: { boxShadow: theme.dark ? "0 8px 24px rgba(0, 0, 0, 0.36)" : "0 8px 24px rgba(0, 0, 0, 0.10)" },
178
- sharp: { boxShadow: theme.dark ? "0 1px 1px rgba(0, 0, 0, 0.55)" : "0 1px 1px rgba(0, 0, 0, 0.12)" },
179
- subtle: { boxShadow: theme.dark ? "0 1px 2px rgba(0, 0, 0, 0.32)" : "0 1px 2px rgba(0, 0, 0, 0.05)" },
180
- elevated: { boxShadow: theme.dark ? "0 20px 40px rgba(0, 0, 0, 0.38)" : "0 20px 40px rgba(0, 0, 0, 0.15)" },
181
- glow: { boxShadow: `0 0 20px ${theme.colors.primary}` },
182
- glass: { boxShadow: theme.dark ? "0 4px 30px rgba(0, 0, 0, 0.32)" : "0 4px 30px rgba(0, 0, 0, 0.05)" },
183
- card: { boxShadow: theme.dark ? "0 1px 2px rgba(0, 0, 0, 0.32)" : "0 1px 3px rgba(0, 0, 0, 0.08)" },
184
- cardHover: { boxShadow: theme.dark ? "0 8px 24px rgba(0, 0, 0, 0.36)" : "0 8px 24px rgba(0, 0, 0, 0.12)" },
185
- cardSubtle: { boxShadow: theme.dark ? "0 1px 2px rgba(0, 0, 0, 0.32)" : "0 1px 3px rgba(0, 0, 0, 0.05)" },
186
- };
187
- return webShadows[type];
188
- }
189
- return Platform.select({
190
- default: config,
191
- });
192
127
  }, [theme]);
193
128
  const getFocusRingStyle = useCallback((offset = 2) => {
194
129
  if (Platform.OS !== "web") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrmeg/expo-ui",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "private": false,
5
5
  "description": "Reusable Expo and React Native UI primitives for MrMeg projects.",
6
6
  "keywords": [