@titan-design/react-ui 0.11.0 → 0.12.1
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/dist/bodymap.js +173 -343
- package/dist/bodymap.js.map +1 -1
- package/dist/bodymap.mjs +173 -343
- package/dist/bodymap.mjs.map +1 -1
- package/dist/index.d.mts +27 -22
- package/dist/index.d.ts +27 -22
- package/dist/index.js +366 -561
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +360 -553
- package/dist/index.mjs.map +1 -1
- package/dist/pages.js +242 -407
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +242 -407
- package/dist/pages.mjs.map +1 -1
- package/dist/theme/index.d.mts +250 -1157
- package/dist/theme/index.d.ts +250 -1157
- package/dist/theme/index.js +234 -408
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +227 -399
- package/dist/theme/index.mjs.map +1 -1
- package/dist/theme/tokens-css.js +141 -140
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +141 -140
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/dist/theme/tokens.d.mts +904 -0
- package/dist/theme/tokens.d.ts +904 -0
- package/dist/theme/tokens.js +856 -0
- package/dist/theme/tokens.js.map +1 -0
- package/dist/theme/tokens.mjs +833 -0
- package/dist/theme/tokens.mjs.map +1 -0
- package/package.json +7 -1
- package/src/arch/Architecture.stories.tsx +9 -9
- package/src/components/custom/DateTime/DateTime.stories.tsx +1 -1
- package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +3 -3
- package/src/components/custom/Fatigue/FatigueLights.stories.tsx +2 -2
- package/src/components/custom/Fatigue/GhostSpark.stories.tsx +3 -3
- package/src/components/custom/Fatigue/LiveFatigueCard.tsx +1 -1
- package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +2 -2
- package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +2 -2
- package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +4 -4
- package/src/components/custom/Fatigue/VelocityHero.stories.tsx +3 -3
- package/src/components/custom/Fatigue/VerdictHero.stories.tsx +2 -2
- package/src/components/custom/Fatigue/fatigue-tokens.ts +12 -12
- package/src/components/custom/Sidebar/Sidebar.tsx +4 -4
- package/src/components/custom/Table/Table.tsx +2 -2
- package/src/components/custom/Workout/ActiveWorkoutPage.tsx +3 -3
- package/src/components/custom/Workout/BaseBadge.test.tsx +6 -1
- package/src/components/custom/Workout/BaseBadge.tsx +1 -1
- package/src/components/custom/Workout/BodyMap.tsx +2 -2
- package/src/components/custom/Workout/BodyMapDetailPanel.tsx +2 -2
- package/src/components/custom/Workout/DeviationBar.test.tsx +2 -1
- package/src/components/custom/Workout/DeviationBar.tsx +3 -2
- package/src/components/custom/Workout/ExerciseCard.test.tsx +3 -3
- package/src/components/custom/Workout/ExerciseCard.tsx +2 -2
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +4 -4
- package/src/components/custom/Workout/InputBar.tsx +2 -2
- package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +1 -1
- package/src/components/custom/Workout/LiveAuraFrame.test.tsx +1 -1
- package/src/components/custom/Workout/LiveAuraFrame.tsx +3 -3
- package/src/components/custom/Workout/MesoCard.tsx +3 -1
- package/src/components/custom/Workout/MuscleGroupChip.tsx +4 -3
- package/src/components/custom/Workout/PrHistoryModal.tsx +2 -2
- package/src/components/custom/Workout/ProgramPlanningPage.tsx +1 -1
- package/src/components/custom/Workout/README.md +1 -1
- package/src/components/custom/Workout/ReadinessCheck.tsx +2 -2
- package/src/components/custom/Workout/RestTimer.tsx +2 -2
- package/src/components/custom/Workout/S3SetTypes.stories.tsx +3 -3
- package/src/components/custom/Workout/SessionHeader.stories.tsx +2 -2
- package/src/components/custom/Workout/SessionHeader.tsx +1 -1
- package/src/components/custom/Workout/SessionRail.stories.tsx +2 -2
- package/src/components/custom/Workout/SessionRail.tsx +13 -5
- package/src/components/custom/Workout/SetBar.tsx +3 -3
- package/src/components/custom/Workout/SetRow.test.tsx +6 -3
- package/src/components/custom/Workout/StatusDot.tsx +5 -4
- package/src/components/custom/Workout/StrengthTrendChart.tsx +1 -1
- package/src/components/custom/Workout/TrainingStatusPage.tsx +2 -2
- package/src/components/custom/Workout/VelocityStrip.test.tsx +5 -2
- package/src/components/custom/Workout/VelocityStrip.tsx +4 -4
- package/src/components/custom/Workout/VolumeLandmarkBar.tsx +3 -3
- package/src/components/custom/Workout/WeightBadge.test.tsx +6 -1
- package/src/components/custom/Workout/WeightBadge.tsx +2 -1
- package/src/components/custom/Workout/WorkoutCard.tsx +3 -1
- package/src/components/custom/Workout/WorkoutPill.tsx +5 -2
- package/src/components/custom/Workout/ZoneTrack.tsx +4 -4
- package/src/components/custom/Workout/setHeadingKit.tsx +14 -8
- package/src/components/custom/Workout/velocity-story-kit.tsx +2 -2
- package/src/components/custom/charts/SetBarChart.tsx +3 -3
- package/src/components/custom/stepper/Stepper.stories.tsx +1 -1
- package/src/components/custom/stepper/Stepper.tsx +3 -3
- package/src/components/shell/DeviceMenu.tsx +2 -2
- package/src/components/shell/README.md +1 -1
- package/src/components/shell/SessionRailSpecimen.stories.tsx +1 -1
- package/src/components/shell/SideNav.tsx +1 -1
- package/src/components/shell/TopBar.tsx +1 -1
- package/src/components/ui/autocomplete/Autocomplete.stories.tsx +1 -1
- package/src/components/ui/autocomplete/Autocomplete.tsx +1 -1
- package/src/components/ui/avatar/Avatar.tsx +2 -2
- package/src/components/ui/badge/Badge.tsx +2 -2
- package/src/components/ui/card/Card.tsx +3 -3
- package/src/components/ui/checkbox/Checkbox.tsx +1 -1
- package/src/components/ui/collapse/Collapse.tsx +1 -1
- package/src/components/ui/data-row/DataRow.stories.tsx +1 -1
- package/src/components/ui/drawer/Drawer.tsx +3 -3
- package/src/components/ui/form-field/FormField.tsx +1 -1
- package/src/components/ui/help-tip/HelpTip.tsx +1 -1
- package/src/components/ui/menu/Menu.tsx +2 -2
- package/src/components/ui/pill/Pill.tsx +2 -2
- package/src/components/ui/popover/Popover.stories.tsx +2 -2
- package/src/components/ui/popover/Popover.tsx +1 -1
- package/src/components/ui/progress/Progress.tsx +3 -3
- package/src/components/ui/radio/Radio.stories.tsx +1 -1
- package/src/components/ui/radio/Radio.tsx +4 -4
- package/src/components/ui/select/Select.tsx +3 -3
- package/src/components/ui/surface/Surface.stories.tsx +3 -3
- package/src/components/ui/surface/Surface.test.tsx +32 -19
- package/src/components/ui/surface/Surface.tsx +3 -3
- package/src/components/ui/surface/SurfaceContext.ts +19 -29
- package/src/components/ui/surface/surface.contract.test.ts +58 -45
- package/src/components/ui/switch/Switch.tsx +1 -1
- package/src/components/ui/tabs/Tabs.tsx +2 -2
- package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +5 -5
- package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +1 -1
- package/src/components/ui/toolbar-button/ToolbarButton.tsx +36 -22
- package/src/stories/PresetShowcase.stories.tsx +21 -21
- package/src/theme/ColorPalettes.stories.tsx +41 -38
- package/src/theme/ColorPrimitives.stories.tsx +89 -41
- package/src/theme/Depth.stories.tsx +312 -0
- package/src/theme/DepthCalibration.stories.tsx +655 -0
- package/src/theme/ThemeProvider.tsx +1 -1
- package/src/theme/barrel.ts +7 -14
- package/src/theme/color-stories.coverage.test.ts +15 -20
- package/src/theme/color-story-kit.tsx +1 -1
- package/src/theme/color-utils.ts +197 -0
- package/src/theme/config.ts +0 -8
- package/src/theme/depth-calibration.test.tsx +97 -0
- package/src/theme/elevation.test.ts +62 -1
- package/src/theme/elevation.ts +34 -8
- package/src/theme/global.css +47 -53
- package/src/theme/grey-ramp.test.ts +166 -0
- package/src/theme/materials.test.ts +106 -0
- package/src/theme/materials.ts +211 -0
- package/src/theme/presets/audiobook.ts +4 -4
- package/src/theme/presets/types.ts +3 -3
- package/src/theme/semantic-contrast.test.ts +83 -0
- package/src/theme/token-export-guard.test.ts +104 -0
- package/src/theme/tokens/primitives.ts +63 -116
- package/src/theme/tokens/semantic.ts +130 -82
- package/src/utils/colors.ts +1 -1
- package/tailwind.config.js +2 -5
- package/src/theme/bar-paper.ts +0 -59
- package/src/theme/shadows.stories.tsx +0 -523
- package/src/theme/shadows.ts +0 -439
package/src/theme/shadows.ts
DELETED
|
@@ -1,439 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Neumorphic Shadow Utilities
|
|
3
|
-
*
|
|
4
|
-
* Neumorphism creates soft, extruded UI using dual opposing shadows.
|
|
5
|
-
* - Light source assumed from top-left
|
|
6
|
-
* - Raised elements: dark shadow bottom-right, light highlight top-left
|
|
7
|
-
* - Pressed elements: inverted with `inset` shadows
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { Platform, type ViewStyle } from 'react-native'
|
|
11
|
-
|
|
12
|
-
// ============================================================================
|
|
13
|
-
// Color Math Utilities
|
|
14
|
-
// ============================================================================
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Parse a hex color to RGB values (0-255).
|
|
18
|
-
* Supports both #RGB and #RRGGBB formats.
|
|
19
|
-
*/
|
|
20
|
-
export function hexToRgb(hex: string): { r: number; g: number; b: number } | null {
|
|
21
|
-
const cleaned = hex.replace('#', '')
|
|
22
|
-
|
|
23
|
-
let r: number, g: number, b: number
|
|
24
|
-
|
|
25
|
-
if (cleaned.length === 3) {
|
|
26
|
-
r = parseInt(cleaned[0] + cleaned[0], 16)
|
|
27
|
-
g = parseInt(cleaned[1] + cleaned[1], 16)
|
|
28
|
-
b = parseInt(cleaned[2] + cleaned[2], 16)
|
|
29
|
-
} else if (cleaned.length === 6) {
|
|
30
|
-
r = parseInt(cleaned.slice(0, 2), 16)
|
|
31
|
-
g = parseInt(cleaned.slice(2, 4), 16)
|
|
32
|
-
b = parseInt(cleaned.slice(4, 6), 16)
|
|
33
|
-
} else {
|
|
34
|
-
return null
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return { r, g, b }
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Convert RGB (0-255) to HSV (h: 0-360, s: 0-1, v: 0-1).
|
|
42
|
-
*/
|
|
43
|
-
export function rgbToHsv(r: number, g: number, b: number): { h: number; s: number; v: number } {
|
|
44
|
-
const rNorm = r / 255
|
|
45
|
-
const gNorm = g / 255
|
|
46
|
-
const bNorm = b / 255
|
|
47
|
-
|
|
48
|
-
const max = Math.max(rNorm, gNorm, bNorm)
|
|
49
|
-
const min = Math.min(rNorm, gNorm, bNorm)
|
|
50
|
-
const delta = max - min
|
|
51
|
-
|
|
52
|
-
// Value
|
|
53
|
-
const v = max
|
|
54
|
-
|
|
55
|
-
// Saturation
|
|
56
|
-
const s = max === 0 ? 0 : delta / max
|
|
57
|
-
|
|
58
|
-
// Hue
|
|
59
|
-
let h = 0
|
|
60
|
-
if (delta !== 0) {
|
|
61
|
-
if (max === rNorm) {
|
|
62
|
-
h = 60 * (((gNorm - bNorm) / delta) % 6)
|
|
63
|
-
} else if (max === gNorm) {
|
|
64
|
-
h = 60 * ((bNorm - rNorm) / delta + 2)
|
|
65
|
-
} else {
|
|
66
|
-
h = 60 * ((rNorm - gNorm) / delta + 4)
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
if (h < 0) h += 360
|
|
70
|
-
|
|
71
|
-
return { h, s, v }
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Convert HSV (h: 0-360, s: 0-1, v: 0-1) to RGB (0-255).
|
|
76
|
-
*/
|
|
77
|
-
export function hsvToRgb(h: number, s: number, v: number): { r: number; g: number; b: number } {
|
|
78
|
-
const c = v * s
|
|
79
|
-
const x = c * (1 - Math.abs((h / 60) % 2 - 1))
|
|
80
|
-
const m = v - c
|
|
81
|
-
|
|
82
|
-
let rPrime: number, gPrime: number, bPrime: number
|
|
83
|
-
|
|
84
|
-
if (h >= 0 && h < 60) {
|
|
85
|
-
rPrime = c; gPrime = x; bPrime = 0
|
|
86
|
-
} else if (h >= 60 && h < 120) {
|
|
87
|
-
rPrime = x; gPrime = c; bPrime = 0
|
|
88
|
-
} else if (h >= 120 && h < 180) {
|
|
89
|
-
rPrime = 0; gPrime = c; bPrime = x
|
|
90
|
-
} else if (h >= 180 && h < 240) {
|
|
91
|
-
rPrime = 0; gPrime = x; bPrime = c
|
|
92
|
-
} else if (h >= 240 && h < 300) {
|
|
93
|
-
rPrime = x; gPrime = 0; bPrime = c
|
|
94
|
-
} else {
|
|
95
|
-
rPrime = c; gPrime = 0; bPrime = x
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
r: Math.round((rPrime + m) * 255),
|
|
100
|
-
g: Math.round((gPrime + m) * 255),
|
|
101
|
-
b: Math.round((bPrime + m) * 255),
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Convert RGB values to hex string.
|
|
107
|
-
*/
|
|
108
|
-
export function rgbToHex(r: number, g: number, b: number): string {
|
|
109
|
-
const toHex = (n: number) => {
|
|
110
|
-
const clamped = Math.max(0, Math.min(255, Math.round(n)))
|
|
111
|
-
return clamped.toString(16).padStart(2, '0')
|
|
112
|
-
}
|
|
113
|
-
return `#${toHex(r)}${toHex(g)}${toHex(b)}`
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Lighten a color by increasing its HSV Value component.
|
|
118
|
-
*
|
|
119
|
-
* @param hex - The hex color to lighten
|
|
120
|
-
* @param amount - Amount to increase V by (0-1)
|
|
121
|
-
*/
|
|
122
|
-
export function lighten(hex: string, amount: number = 0.1): string {
|
|
123
|
-
const rgb = hexToRgb(hex)
|
|
124
|
-
if (!rgb) return hex
|
|
125
|
-
|
|
126
|
-
const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b)
|
|
127
|
-
const newV = Math.min(1, hsv.v + amount)
|
|
128
|
-
|
|
129
|
-
const newRgb = hsvToRgb(hsv.h, hsv.s, newV)
|
|
130
|
-
return rgbToHex(newRgb.r, newRgb.g, newRgb.b)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Darken a color by decreasing its HSV Value component.
|
|
135
|
-
*
|
|
136
|
-
* @param hex - The hex color to darken
|
|
137
|
-
* @param amount - Amount to decrease V by (0-1)
|
|
138
|
-
*/
|
|
139
|
-
export function darken(hex: string, amount: number = 0.1): string {
|
|
140
|
-
const rgb = hexToRgb(hex)
|
|
141
|
-
if (!rgb) return hex
|
|
142
|
-
|
|
143
|
-
const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b)
|
|
144
|
-
const newV = Math.max(0, hsv.v - amount)
|
|
145
|
-
|
|
146
|
-
const newRgb = hsvToRgb(hsv.h, hsv.s, newV)
|
|
147
|
-
return rgbToHex(newRgb.r, newRgb.g, newRgb.b)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Get hover colors for a button based on its background color.
|
|
152
|
-
* Automatically adapts intensity based on the color's luminance.
|
|
153
|
-
*
|
|
154
|
-
* @param bgColor - The button's background color (hex)
|
|
155
|
-
* @param intensity - How strong the hover effect should be ('subtle' | 'medium' | 'strong')
|
|
156
|
-
*/
|
|
157
|
-
export function getHoverColors(
|
|
158
|
-
bgColor: string,
|
|
159
|
-
intensity: 'subtle' | 'medium' | 'strong' = 'medium'
|
|
160
|
-
): { raised: string; pressed: string } {
|
|
161
|
-
const amounts = {
|
|
162
|
-
subtle: 0.01,
|
|
163
|
-
medium: 0.02,
|
|
164
|
-
strong: 0.03,
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const amount = amounts[intensity]
|
|
168
|
-
|
|
169
|
-
return {
|
|
170
|
-
raised: lighten(bgColor, amount), // Raised hover: lighten (lifting up)
|
|
171
|
-
pressed: darken(bgColor, amount), // Pressed hover: darken (pressing down)
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Check if a color is considered "dark" (for choosing text color).
|
|
177
|
-
* Uses HSV Value component for simplicity.
|
|
178
|
-
*/
|
|
179
|
-
export function isDark(hex: string): boolean {
|
|
180
|
-
const rgb = hexToRgb(hex)
|
|
181
|
-
if (!rgb) return true
|
|
182
|
-
const hsv = rgbToHsv(rgb.r, rgb.g, rgb.b)
|
|
183
|
-
return hsv.v < 0.6
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Shadow configuration for different surface colors.
|
|
188
|
-
* Each surface needs calibrated light/dark shadow colors.
|
|
189
|
-
*
|
|
190
|
-
* - dark: soft shadow for depth (bottom-right)
|
|
191
|
-
* - light: sharp rim highlight (top-left) - higher opacity for crisp edge
|
|
192
|
-
* - darker: deeper shadow for pressed/inset states
|
|
193
|
-
*/
|
|
194
|
-
export const shadowColors = {
|
|
195
|
-
// For charcoal surfaces (~#2C2C2C to #3C3C3C)
|
|
196
|
-
charcoal: {
|
|
197
|
-
dark: 'rgba(0, 0, 0, 0.5)',
|
|
198
|
-
light: 'rgba(255, 255, 255, 0.12)', // brighter for crisp rim light
|
|
199
|
-
darker: 'rgba(0, 0, 0, 0.6)',
|
|
200
|
-
},
|
|
201
|
-
// For neutral dark surfaces (~#1F2937)
|
|
202
|
-
neutralDark: {
|
|
203
|
-
dark: 'rgba(0, 0, 0, 0.4)',
|
|
204
|
-
light: 'rgba(255, 255, 255, 0.08)',
|
|
205
|
-
darker: 'rgba(0, 0, 0, 0.5)',
|
|
206
|
-
},
|
|
207
|
-
// For light surfaces (white/light gray backgrounds)
|
|
208
|
-
neutralLight: {
|
|
209
|
-
dark: 'rgba(0, 0, 0, 0.15)', // Visible dark shadow for depth on light backgrounds
|
|
210
|
-
light: 'rgba(255, 255, 255, 1)', // Bright highlight for raised effect (visible on light gray)
|
|
211
|
-
darker: 'rgba(0, 0, 0, 0.2)', // Deeper shadow for pressed states
|
|
212
|
-
},
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export type ShadowIntensity = 'subtle' | 'medium' | 'strong'
|
|
216
|
-
export type ShadowSurface = keyof typeof shadowColors
|
|
217
|
-
|
|
218
|
-
interface ShadowConfig {
|
|
219
|
-
offset: number
|
|
220
|
-
blur: number
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const intensityConfig: Record<ShadowIntensity, ShadowConfig> = {
|
|
224
|
-
subtle: { offset: 1, blur: 2 }, // very light, barely visible
|
|
225
|
-
medium: { offset: 2, blur: 4 }, // noticeable but restrained
|
|
226
|
-
strong: { offset: 2, blur: 5 }, // slightly deeper blur, same offset as medium
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Creates a raised (elevated) neumorphic shadow.
|
|
231
|
-
* Element appears to float above the surface.
|
|
232
|
-
*
|
|
233
|
-
* Uses a soft dark shadow (bottom-right) and a sharp rim light (top-left).
|
|
234
|
-
*/
|
|
235
|
-
export function createRaisedShadow(
|
|
236
|
-
surface: ShadowSurface = 'charcoal',
|
|
237
|
-
intensity: ShadowIntensity = 'medium'
|
|
238
|
-
): ViewStyle {
|
|
239
|
-
const colors = shadowColors[surface]
|
|
240
|
-
const { offset, blur } = intensityConfig[intensity]
|
|
241
|
-
|
|
242
|
-
// Rim light is sharper: same offset as shadow, but minimal blur for crisp edge
|
|
243
|
-
const rimBlur = 1 // Very tight blur for crisp rim
|
|
244
|
-
|
|
245
|
-
return Platform.select({
|
|
246
|
-
web: {
|
|
247
|
-
boxShadow: `${offset}px ${offset}px ${blur}px ${colors.dark}, -${offset}px -${offset}px ${rimBlur}px ${colors.light}`,
|
|
248
|
-
} as any,
|
|
249
|
-
default: {
|
|
250
|
-
// React Native shadow approximation (limited compared to web)
|
|
251
|
-
shadowColor: '#000',
|
|
252
|
-
shadowOffset: { width: offset, height: offset },
|
|
253
|
-
shadowOpacity: 0.3,
|
|
254
|
-
shadowRadius: blur / 2,
|
|
255
|
-
elevation: offset,
|
|
256
|
-
},
|
|
257
|
-
}) as ViewStyle
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Creates a pressed (inset) neumorphic shadow.
|
|
262
|
-
* Element appears pushed into the surface.
|
|
263
|
-
*
|
|
264
|
-
* Uses a soft dark inset shadow (top-left) and a thin sharp rim light (bottom-right).
|
|
265
|
-
*/
|
|
266
|
-
export function createPressedShadow(
|
|
267
|
-
surface: ShadowSurface = 'charcoal',
|
|
268
|
-
intensity: ShadowIntensity = 'medium'
|
|
269
|
-
): ViewStyle {
|
|
270
|
-
const colors = shadowColors[surface]
|
|
271
|
-
const { offset, blur } = intensityConfig[intensity]
|
|
272
|
-
|
|
273
|
-
// Rim light is always thin and subtle for pressed states
|
|
274
|
-
const rimOffset = 1 // Fixed thin rim
|
|
275
|
-
const rimBlur = 1 // Very tight blur for crisp edge
|
|
276
|
-
|
|
277
|
-
return Platform.select({
|
|
278
|
-
web: {
|
|
279
|
-
boxShadow: `inset ${offset}px ${offset}px ${blur}px ${colors.darker}, inset -${rimOffset}px -${rimOffset}px ${rimBlur}px ${colors.light}`,
|
|
280
|
-
} as any,
|
|
281
|
-
default: {
|
|
282
|
-
// React Native doesn't support inset shadows, approximate with darker bg
|
|
283
|
-
shadowOpacity: 0,
|
|
284
|
-
elevation: 0,
|
|
285
|
-
},
|
|
286
|
-
}) as ViewStyle
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Creates a flat (no shadow) style for hover states.
|
|
291
|
-
*/
|
|
292
|
-
export function createFlatShadow(): ViewStyle {
|
|
293
|
-
return Platform.select({
|
|
294
|
-
web: {
|
|
295
|
-
boxShadow: 'none',
|
|
296
|
-
} as any,
|
|
297
|
-
default: {
|
|
298
|
-
shadowOpacity: 0,
|
|
299
|
-
elevation: 0,
|
|
300
|
-
},
|
|
301
|
-
}) as ViewStyle
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* Creates a hover shadow for raised elements.
|
|
306
|
-
* Reduces shadow depth but keeps a subtle rim light to maintain visual boundary.
|
|
307
|
-
*/
|
|
308
|
-
export function createRaisedHoverShadow(
|
|
309
|
-
surface: ShadowSurface = 'charcoal',
|
|
310
|
-
intensity: ShadowIntensity = 'medium'
|
|
311
|
-
): ViewStyle {
|
|
312
|
-
const colors = shadowColors[surface]
|
|
313
|
-
const { offset, blur } = intensityConfig[intensity]
|
|
314
|
-
|
|
315
|
-
// Reduce shadow depth by half for hover, keep minimal rim light
|
|
316
|
-
const hoverOffset = Math.max(1, Math.round(offset / 2))
|
|
317
|
-
const hoverBlur = Math.max(1, Math.round(blur / 2))
|
|
318
|
-
const rimBlur = 1
|
|
319
|
-
|
|
320
|
-
return Platform.select({
|
|
321
|
-
web: {
|
|
322
|
-
// Keep same offsets as normal state for rim to prevent "shrinking" effect
|
|
323
|
-
boxShadow: `${hoverOffset}px ${hoverOffset}px ${hoverBlur}px ${colors.dark}, -${offset}px -${offset}px ${rimBlur}px ${colors.light}`,
|
|
324
|
-
} as any,
|
|
325
|
-
default: {
|
|
326
|
-
shadowColor: '#000',
|
|
327
|
-
shadowOffset: { width: hoverOffset, height: hoverOffset },
|
|
328
|
-
shadowOpacity: 0.2,
|
|
329
|
-
shadowRadius: hoverBlur / 2,
|
|
330
|
-
elevation: hoverOffset,
|
|
331
|
-
},
|
|
332
|
-
}) as ViewStyle
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Creates a hover shadow for pressed elements.
|
|
337
|
-
* Reduces shadow depth but keeps a subtle rim light to maintain visual boundary.
|
|
338
|
-
*/
|
|
339
|
-
export function createPressedHoverShadow(
|
|
340
|
-
surface: ShadowSurface = 'charcoal',
|
|
341
|
-
intensity: ShadowIntensity = 'medium'
|
|
342
|
-
): ViewStyle {
|
|
343
|
-
const colors = shadowColors[surface]
|
|
344
|
-
const { offset, blur } = intensityConfig[intensity]
|
|
345
|
-
|
|
346
|
-
// Reduce shadow depth by half for hover, keep minimal rim light
|
|
347
|
-
const hoverOffset = Math.max(1, Math.round(offset / 2))
|
|
348
|
-
const hoverBlur = Math.max(1, Math.round(blur / 2))
|
|
349
|
-
const rimBlur = 1
|
|
350
|
-
|
|
351
|
-
return Platform.select({
|
|
352
|
-
web: {
|
|
353
|
-
// Keep rim offset at 1px (same as pressed normal) to prevent "shrinking" effect
|
|
354
|
-
boxShadow: `inset ${hoverOffset}px ${hoverOffset}px ${hoverBlur}px ${colors.darker}, inset -1px -1px ${rimBlur}px ${colors.light}`,
|
|
355
|
-
} as any,
|
|
356
|
-
default: {
|
|
357
|
-
shadowOpacity: 0,
|
|
358
|
-
elevation: 0,
|
|
359
|
-
},
|
|
360
|
-
}) as ViewStyle
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Pre-built shadow presets for common use cases.
|
|
365
|
-
*/
|
|
366
|
-
export const neumorphicShadows = {
|
|
367
|
-
// Charcoal surface shadows (for dark toolbars, cards)
|
|
368
|
-
charcoal: {
|
|
369
|
-
raised: {
|
|
370
|
-
subtle: createRaisedShadow('charcoal', 'subtle'),
|
|
371
|
-
medium: createRaisedShadow('charcoal', 'medium'),
|
|
372
|
-
strong: createRaisedShadow('charcoal', 'strong'),
|
|
373
|
-
},
|
|
374
|
-
raisedHover: {
|
|
375
|
-
subtle: createRaisedHoverShadow('charcoal', 'subtle'),
|
|
376
|
-
medium: createRaisedHoverShadow('charcoal', 'medium'),
|
|
377
|
-
strong: createRaisedHoverShadow('charcoal', 'strong'),
|
|
378
|
-
},
|
|
379
|
-
pressed: {
|
|
380
|
-
subtle: createPressedShadow('charcoal', 'subtle'),
|
|
381
|
-
medium: createPressedShadow('charcoal', 'medium'),
|
|
382
|
-
strong: createPressedShadow('charcoal', 'strong'),
|
|
383
|
-
},
|
|
384
|
-
pressedHover: {
|
|
385
|
-
subtle: createPressedHoverShadow('charcoal', 'subtle'),
|
|
386
|
-
medium: createPressedHoverShadow('charcoal', 'medium'),
|
|
387
|
-
strong: createPressedHoverShadow('charcoal', 'strong'),
|
|
388
|
-
},
|
|
389
|
-
flat: createFlatShadow(),
|
|
390
|
-
},
|
|
391
|
-
// Neutral dark surface shadows
|
|
392
|
-
neutralDark: {
|
|
393
|
-
raised: {
|
|
394
|
-
subtle: createRaisedShadow('neutralDark', 'subtle'),
|
|
395
|
-
medium: createRaisedShadow('neutralDark', 'medium'),
|
|
396
|
-
strong: createRaisedShadow('neutralDark', 'strong'),
|
|
397
|
-
},
|
|
398
|
-
raisedHover: {
|
|
399
|
-
subtle: createRaisedHoverShadow('neutralDark', 'subtle'),
|
|
400
|
-
medium: createRaisedHoverShadow('neutralDark', 'medium'),
|
|
401
|
-
strong: createRaisedHoverShadow('neutralDark', 'strong'),
|
|
402
|
-
},
|
|
403
|
-
pressed: {
|
|
404
|
-
subtle: createPressedShadow('neutralDark', 'subtle'),
|
|
405
|
-
medium: createPressedShadow('neutralDark', 'medium'),
|
|
406
|
-
strong: createPressedShadow('neutralDark', 'strong'),
|
|
407
|
-
},
|
|
408
|
-
pressedHover: {
|
|
409
|
-
subtle: createPressedHoverShadow('neutralDark', 'subtle'),
|
|
410
|
-
medium: createPressedHoverShadow('neutralDark', 'medium'),
|
|
411
|
-
strong: createPressedHoverShadow('neutralDark', 'strong'),
|
|
412
|
-
},
|
|
413
|
-
flat: createFlatShadow(),
|
|
414
|
-
},
|
|
415
|
-
// Neutral light surface shadows
|
|
416
|
-
neutralLight: {
|
|
417
|
-
raised: {
|
|
418
|
-
subtle: createRaisedShadow('neutralLight', 'subtle'),
|
|
419
|
-
medium: createRaisedShadow('neutralLight', 'medium'),
|
|
420
|
-
strong: createRaisedShadow('neutralLight', 'strong'),
|
|
421
|
-
},
|
|
422
|
-
raisedHover: {
|
|
423
|
-
subtle: createRaisedHoverShadow('neutralLight', 'subtle'),
|
|
424
|
-
medium: createRaisedHoverShadow('neutralLight', 'medium'),
|
|
425
|
-
strong: createRaisedHoverShadow('neutralLight', 'strong'),
|
|
426
|
-
},
|
|
427
|
-
pressed: {
|
|
428
|
-
subtle: createPressedShadow('neutralLight', 'subtle'),
|
|
429
|
-
medium: createPressedShadow('neutralLight', 'medium'),
|
|
430
|
-
strong: createPressedShadow('neutralLight', 'strong'),
|
|
431
|
-
},
|
|
432
|
-
pressedHover: {
|
|
433
|
-
subtle: createPressedHoverShadow('neutralLight', 'subtle'),
|
|
434
|
-
medium: createPressedHoverShadow('neutralLight', 'medium'),
|
|
435
|
-
strong: createPressedHoverShadow('neutralLight', 'strong'),
|
|
436
|
-
},
|
|
437
|
-
flat: createFlatShadow(),
|
|
438
|
-
},
|
|
439
|
-
}
|