@titan-design/react-ui 0.11.0 → 0.12.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/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 +24 -21
- package/dist/index.d.ts +24 -21
- 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 +409 -415
- package/dist/theme/index.d.ts +409 -415
- 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/package.json +1 -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/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/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 +57 -44
- 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/tokens/primitives.ts +62 -115
- 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
|
@@ -1,523 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
3
|
-
import { View, Text } from 'react-native'
|
|
4
|
-
import {
|
|
5
|
-
neumorphicShadows,
|
|
6
|
-
getHoverColors,
|
|
7
|
-
type ShadowIntensity,
|
|
8
|
-
} from './shadows'
|
|
9
|
-
|
|
10
|
-
const meta: Meta = {
|
|
11
|
-
title: 'Foundations/Shadows',
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export default meta
|
|
15
|
-
type Story = StoryObj
|
|
16
|
-
|
|
17
|
-
// Sample button component to demonstrate shadows
|
|
18
|
-
interface ShadowBoxProps {
|
|
19
|
-
label: string
|
|
20
|
-
style: any
|
|
21
|
-
bgColor?: string
|
|
22
|
-
textColor?: string
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function ShadowBox({ label, style, bgColor, textColor }: ShadowBoxProps) {
|
|
26
|
-
return (
|
|
27
|
-
<View
|
|
28
|
-
className={bgColor ? undefined : 'bg-surface-elevated'}
|
|
29
|
-
style={[
|
|
30
|
-
bgColor ? { backgroundColor: bgColor } : undefined,
|
|
31
|
-
{
|
|
32
|
-
paddingVertical: 12,
|
|
33
|
-
paddingHorizontal: 20,
|
|
34
|
-
borderRadius: 8,
|
|
35
|
-
minWidth: 120,
|
|
36
|
-
alignItems: 'center',
|
|
37
|
-
},
|
|
38
|
-
style,
|
|
39
|
-
]}
|
|
40
|
-
>
|
|
41
|
-
<Text className={textColor ? undefined : 'text-text-primary font-semibold text-sm'} style={textColor ? { color: textColor, fontWeight: '600', fontSize: 14 } : undefined}>
|
|
42
|
-
{label}
|
|
43
|
-
</Text>
|
|
44
|
-
</View>
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Section header
|
|
49
|
-
function SectionHeader({ children }: { children: string }) {
|
|
50
|
-
return (
|
|
51
|
-
<Text className="text-text-secondary text-sm font-semibold mb-3 mt-6">
|
|
52
|
-
{children}
|
|
53
|
-
</Text>
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Subsection header
|
|
58
|
-
function SubHeader({ children }: { children: string }) {
|
|
59
|
-
return (
|
|
60
|
-
<Text className="text-text-secondary text-xs mb-2 mt-4">
|
|
61
|
-
{children}
|
|
62
|
-
</Text>
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export const NeumorphicShadows: Story = {
|
|
67
|
-
render: () => (
|
|
68
|
-
<View className="p-6">
|
|
69
|
-
<Text className="text-text-primary text-xl font-bold mb-2">
|
|
70
|
-
Neumorphic Shadows
|
|
71
|
-
</Text>
|
|
72
|
-
<Text className="text-text-secondary text-sm mb-6">
|
|
73
|
-
Raised = floating above surface, Pressed = pushed into surface
|
|
74
|
-
</Text>
|
|
75
|
-
|
|
76
|
-
{/* Charcoal surface */}
|
|
77
|
-
<View className="bg-surface-elevated p-6 rounded-xl mb-6">
|
|
78
|
-
<Text className="text-text-secondary text-xs mb-4">
|
|
79
|
-
Surface: Charcoal (#2C2C2C)
|
|
80
|
-
</Text>
|
|
81
|
-
|
|
82
|
-
<SubHeader>Raised (element floats above surface)</SubHeader>
|
|
83
|
-
<View style={{ flexDirection: 'row', gap: 16, flexWrap: 'wrap' }}>
|
|
84
|
-
<ShadowBox label="Subtle" style={neumorphicShadows.charcoal.raised.subtle} />
|
|
85
|
-
<ShadowBox label="Medium" style={neumorphicShadows.charcoal.raised.medium} />
|
|
86
|
-
<ShadowBox label="Strong" style={neumorphicShadows.charcoal.raised.strong} />
|
|
87
|
-
</View>
|
|
88
|
-
|
|
89
|
-
<SubHeader>Pressed (element pushed into surface)</SubHeader>
|
|
90
|
-
<View style={{ flexDirection: 'row', gap: 16, flexWrap: 'wrap' }}>
|
|
91
|
-
<ShadowBox label="Subtle" style={neumorphicShadows.charcoal.pressed.subtle} />
|
|
92
|
-
<ShadowBox label="Medium" style={neumorphicShadows.charcoal.pressed.medium} />
|
|
93
|
-
<ShadowBox label="Strong" style={neumorphicShadows.charcoal.pressed.strong} />
|
|
94
|
-
</View>
|
|
95
|
-
</View>
|
|
96
|
-
|
|
97
|
-
{/* Code example */}
|
|
98
|
-
<View className="bg-surface-base p-4 rounded-lg">
|
|
99
|
-
<Text className="text-text-secondary text-[11px] font-mono">
|
|
100
|
-
{`import { neumorphicShadows } from '@titan-design/react-ui'
|
|
101
|
-
|
|
102
|
-
// Apply to a View
|
|
103
|
-
<View style={neumorphicShadows.charcoal.raised.subtle} />
|
|
104
|
-
<View style={neumorphicShadows.charcoal.pressed.medium} />
|
|
105
|
-
|
|
106
|
-
/* Raised - soft shadow + sharp rim light */
|
|
107
|
-
box-shadow:
|
|
108
|
-
2px 2px 4px rgba(0,0,0,0.5),
|
|
109
|
-
-2px -2px 1px rgba(255,255,255,0.12);
|
|
110
|
-
|
|
111
|
-
/* Pressed - inset shadow + thin rim light */
|
|
112
|
-
box-shadow:
|
|
113
|
-
inset 2px 2px 4px rgba(0,0,0,0.6),
|
|
114
|
-
inset -1px -1px 1px rgba(255,255,255,0.12);`}
|
|
115
|
-
</Text>
|
|
116
|
-
</View>
|
|
117
|
-
</View>
|
|
118
|
-
),
|
|
119
|
-
decorators: [
|
|
120
|
-
(Story) => (
|
|
121
|
-
<View className="bg-background-base min-h-screen p-4">
|
|
122
|
-
<Story />
|
|
123
|
-
</View>
|
|
124
|
-
),
|
|
125
|
-
],
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export const IntensityLevels: Story = {
|
|
129
|
-
render: () => (
|
|
130
|
-
<View className="p-6">
|
|
131
|
-
<Text className="text-text-primary text-xl font-bold mb-2">
|
|
132
|
-
Shadow Intensity Levels
|
|
133
|
-
</Text>
|
|
134
|
-
<Text className="text-text-secondary text-sm mb-6">
|
|
135
|
-
subtle (1px/2px) → medium (2px/4px) → strong (2px/5px)
|
|
136
|
-
</Text>
|
|
137
|
-
|
|
138
|
-
<View className="bg-surface-elevated p-6 rounded-xl">
|
|
139
|
-
<SubHeader>Raised Intensities</SubHeader>
|
|
140
|
-
<View style={{ flexDirection: 'row', gap: 24, marginBottom: 24 }}>
|
|
141
|
-
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
142
|
-
<ShadowBox label="Subtle" style={neumorphicShadows.charcoal.raised.subtle} />
|
|
143
|
-
<Text className="text-text-secondary text-[10px]">1px / 2px blur</Text>
|
|
144
|
-
</View>
|
|
145
|
-
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
146
|
-
<ShadowBox label="Medium" style={neumorphicShadows.charcoal.raised.medium} />
|
|
147
|
-
<Text className="text-text-secondary text-[10px]">2px / 4px blur</Text>
|
|
148
|
-
</View>
|
|
149
|
-
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
150
|
-
<ShadowBox label="Strong" style={neumorphicShadows.charcoal.raised.strong} />
|
|
151
|
-
<Text className="text-text-secondary text-[10px]">2px / 5px blur</Text>
|
|
152
|
-
</View>
|
|
153
|
-
</View>
|
|
154
|
-
|
|
155
|
-
<SubHeader>Pressed Intensities (thin 1px rim highlight)</SubHeader>
|
|
156
|
-
<View style={{ flexDirection: 'row', gap: 24 }}>
|
|
157
|
-
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
158
|
-
<ShadowBox label="Subtle" style={neumorphicShadows.charcoal.pressed.subtle} />
|
|
159
|
-
<Text className="text-text-secondary text-[10px]">1px / 2px blur</Text>
|
|
160
|
-
</View>
|
|
161
|
-
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
162
|
-
<ShadowBox label="Medium" style={neumorphicShadows.charcoal.pressed.medium} />
|
|
163
|
-
<Text className="text-text-secondary text-[10px]">2px / 4px blur</Text>
|
|
164
|
-
</View>
|
|
165
|
-
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
166
|
-
<ShadowBox label="Strong" style={neumorphicShadows.charcoal.pressed.strong} />
|
|
167
|
-
<Text className="text-text-secondary text-[10px]">2px / 5px blur</Text>
|
|
168
|
-
</View>
|
|
169
|
-
</View>
|
|
170
|
-
</View>
|
|
171
|
-
</View>
|
|
172
|
-
),
|
|
173
|
-
decorators: [
|
|
174
|
-
(Story) => (
|
|
175
|
-
<View className="bg-background-base min-h-screen p-4">
|
|
176
|
-
<Story />
|
|
177
|
-
</View>
|
|
178
|
-
),
|
|
179
|
-
],
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Interactive button using color math - adapts to any background brightness
|
|
183
|
-
interface ColorMathButtonProps {
|
|
184
|
-
surface: 'charcoal' | 'neutralDark' | 'neutralLight'
|
|
185
|
-
bgColor: string
|
|
186
|
-
textColor?: string
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function ColorMathButton({ surface, bgColor, textColor = '#fff' }: ColorMathButtonProps) {
|
|
190
|
-
const [isActive, setIsActive] = React.useState(false)
|
|
191
|
-
const [isHovered, setIsHovered] = React.useState(false)
|
|
192
|
-
|
|
193
|
-
const shadows = neumorphicShadows[surface]
|
|
194
|
-
const hoverColors = getHoverColors(bgColor, 'medium')
|
|
195
|
-
|
|
196
|
-
const getShadowStyle = () => {
|
|
197
|
-
if (isActive) {
|
|
198
|
-
return isHovered ? shadows.pressedHover.medium : shadows.pressed.medium
|
|
199
|
-
}
|
|
200
|
-
return isHovered ? shadows.raisedHover.medium : shadows.raised.medium
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Use color math for hover - automatically adapts to brightness!
|
|
204
|
-
const getBgColor = () => {
|
|
205
|
-
if (!isHovered) return bgColor
|
|
206
|
-
return isActive ? hoverColors.pressed : hoverColors.raised
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return (
|
|
210
|
-
<View
|
|
211
|
-
// @ts-expect-error - web events
|
|
212
|
-
onMouseEnter={() => setIsHovered(true)}
|
|
213
|
-
onMouseLeave={() => setIsHovered(false)}
|
|
214
|
-
onClick={() => setIsActive(!isActive)}
|
|
215
|
-
style={[
|
|
216
|
-
{
|
|
217
|
-
backgroundColor: getBgColor(),
|
|
218
|
-
paddingVertical: 12,
|
|
219
|
-
paddingHorizontal: 20,
|
|
220
|
-
borderRadius: 8,
|
|
221
|
-
minWidth: 100,
|
|
222
|
-
alignItems: 'center',
|
|
223
|
-
cursor: 'pointer',
|
|
224
|
-
transition: 'box-shadow 0.2s ease, background-color 0.15s ease',
|
|
225
|
-
} as any,
|
|
226
|
-
getShadowStyle(),
|
|
227
|
-
]}
|
|
228
|
-
>
|
|
229
|
-
<Text style={{ color: textColor, fontWeight: '600', fontSize: 14 }}>
|
|
230
|
-
{isActive ? 'Active' : 'Inactive'}
|
|
231
|
-
</Text>
|
|
232
|
-
</View>
|
|
233
|
-
)
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
export const SurfaceVariants: Story = {
|
|
238
|
-
render: function Render() {
|
|
239
|
-
// Show calculated hover colors for each surface
|
|
240
|
-
const charcoalHover = getHoverColors('#3C3C3C', 'medium')
|
|
241
|
-
const neutralDarkHover = getHoverColors('#374151', 'medium')
|
|
242
|
-
const neutralLightHover = getHoverColors('#E5E7EB', 'medium')
|
|
243
|
-
|
|
244
|
-
return (
|
|
245
|
-
<View className="p-6">
|
|
246
|
-
<Text className="text-text-primary text-xl font-bold mb-2">
|
|
247
|
-
Surface Variants with Color Math
|
|
248
|
-
</Text>
|
|
249
|
-
<Text className="text-text-secondary text-sm mb-6">
|
|
250
|
-
Hover colors calculated using HSV - just pass any background color!
|
|
251
|
-
</Text>
|
|
252
|
-
|
|
253
|
-
{/* Charcoal Surface */}
|
|
254
|
-
<View className="bg-surface-elevated p-6 rounded-xl mb-4">
|
|
255
|
-
<Text className="text-text-secondary text-xs mb-2">Charcoal Surface (#2C2C2C)</Text>
|
|
256
|
-
<Text className="text-text-secondary text-[10px] mb-4">
|
|
257
|
-
Hover: raised → {charcoalHover.raised}, pressed → {charcoalHover.pressed}
|
|
258
|
-
</Text>
|
|
259
|
-
<View style={{ flexDirection: 'row', gap: 16 }}>
|
|
260
|
-
<ColorMathButton surface="charcoal" bgColor="#3C3C3C" />
|
|
261
|
-
<ColorMathButton surface="charcoal" bgColor="#3C3C3C" />
|
|
262
|
-
</View>
|
|
263
|
-
</View>
|
|
264
|
-
|
|
265
|
-
{/* Neutral Dark Surface */}
|
|
266
|
-
<View className="bg-surface-elevated p-6 rounded-xl mb-4">
|
|
267
|
-
<Text className="text-text-secondary text-xs mb-2">Neutral Dark Surface (#1F2937)</Text>
|
|
268
|
-
<Text className="text-text-secondary text-[10px] mb-4">
|
|
269
|
-
Hover: raised → {neutralDarkHover.raised}, pressed → {neutralDarkHover.pressed}
|
|
270
|
-
</Text>
|
|
271
|
-
<View style={{ flexDirection: 'row', gap: 16 }}>
|
|
272
|
-
<ColorMathButton surface="neutralDark" bgColor="#374151" />
|
|
273
|
-
<ColorMathButton surface="neutralDark" bgColor="#374151" />
|
|
274
|
-
</View>
|
|
275
|
-
</View>
|
|
276
|
-
|
|
277
|
-
{/* Neutral Light Surface */}
|
|
278
|
-
<View className="bg-surface-elevated p-6 rounded-xl">
|
|
279
|
-
<Text className="text-text-secondary text-xs mb-2">Neutral Light Surface (#E5E7EB)</Text>
|
|
280
|
-
<Text className="text-text-secondary text-[10px] mb-4">
|
|
281
|
-
Hover: raised → {neutralLightHover.raised}, pressed → {neutralLightHover.pressed}
|
|
282
|
-
</Text>
|
|
283
|
-
<View style={{ flexDirection: 'row', gap: 16 }}>
|
|
284
|
-
<ColorMathButton surface="neutralLight" bgColor="#E5E7EB" textColor="#374151" />
|
|
285
|
-
<ColorMathButton surface="neutralLight" bgColor="#E5E7EB" textColor="#374151" />
|
|
286
|
-
</View>
|
|
287
|
-
</View>
|
|
288
|
-
|
|
289
|
-
{/* Code example */}
|
|
290
|
-
<View className="bg-surface-base p-4 rounded-lg mt-4">
|
|
291
|
-
<Text className="text-text-secondary text-[11px] font-mono">
|
|
292
|
-
{`import { getHoverColors, lighten, darken } from '@titan-design/react-ui'
|
|
293
|
-
|
|
294
|
-
// Get hover colors for a button background
|
|
295
|
-
const hover = getHoverColors('#3C3C3C', 'medium')
|
|
296
|
-
// → { raised: '#414141', pressed: '#373737' }
|
|
297
|
-
|
|
298
|
-
// Or use individual functions (HSV-based)
|
|
299
|
-
lighten('#3C3C3C', 0.02) // Increase V by 0.02
|
|
300
|
-
darken('#3C3C3C', 0.02) // Decrease V by 0.02`}
|
|
301
|
-
</Text>
|
|
302
|
-
</View>
|
|
303
|
-
</View>
|
|
304
|
-
)
|
|
305
|
-
},
|
|
306
|
-
decorators: [
|
|
307
|
-
(Story) => (
|
|
308
|
-
<View className="bg-background-base min-h-screen p-4">
|
|
309
|
-
<Story />
|
|
310
|
-
</View>
|
|
311
|
-
),
|
|
312
|
-
],
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
export const HoverStates: Story = {
|
|
316
|
-
render: () => (
|
|
317
|
-
<View className="p-6">
|
|
318
|
-
<Text className="text-text-primary text-xl font-bold mb-2">
|
|
319
|
-
Hover State Shadows
|
|
320
|
-
</Text>
|
|
321
|
-
<Text className="text-text-secondary text-sm mb-6">
|
|
322
|
-
Hover reduces shadow depth to indicate interactivity (removes rim light, halves offset/blur)
|
|
323
|
-
</Text>
|
|
324
|
-
|
|
325
|
-
<View className="bg-surface-elevated p-6 rounded-xl">
|
|
326
|
-
<SubHeader>Raised → Raised Hover (medium intensity)</SubHeader>
|
|
327
|
-
<View style={{ flexDirection: 'row', gap: 24, marginBottom: 24 }}>
|
|
328
|
-
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
329
|
-
<ShadowBox label="Raised" style={neumorphicShadows.charcoal.raised.medium} />
|
|
330
|
-
<Text className="text-text-secondary text-[10px]">Normal</Text>
|
|
331
|
-
</View>
|
|
332
|
-
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
333
|
-
<ShadowBox label="Raised Hover" style={neumorphicShadows.charcoal.raisedHover.medium} />
|
|
334
|
-
<Text className="text-text-secondary text-[10px]">Hover</Text>
|
|
335
|
-
</View>
|
|
336
|
-
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
337
|
-
<ShadowBox label="Flat" style={neumorphicShadows.charcoal.flat} />
|
|
338
|
-
<Text className="text-text-secondary text-[10px]">Alt: Flat</Text>
|
|
339
|
-
</View>
|
|
340
|
-
</View>
|
|
341
|
-
|
|
342
|
-
<SubHeader>Pressed → Pressed Hover (medium intensity)</SubHeader>
|
|
343
|
-
<View style={{ flexDirection: 'row', gap: 24, marginBottom: 24 }}>
|
|
344
|
-
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
345
|
-
<ShadowBox label="Pressed" style={neumorphicShadows.charcoal.pressed.medium} />
|
|
346
|
-
<Text className="text-text-secondary text-[10px]">Normal</Text>
|
|
347
|
-
</View>
|
|
348
|
-
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
349
|
-
<ShadowBox label="Pressed Hover" style={neumorphicShadows.charcoal.pressedHover.medium} />
|
|
350
|
-
<Text className="text-text-secondary text-[10px]">Hover</Text>
|
|
351
|
-
</View>
|
|
352
|
-
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
353
|
-
<ShadowBox label="Flat" style={neumorphicShadows.charcoal.flat} />
|
|
354
|
-
<Text className="text-text-secondary text-[10px]">Alt: Flat</Text>
|
|
355
|
-
</View>
|
|
356
|
-
</View>
|
|
357
|
-
|
|
358
|
-
<SubHeader>All Intensities - Raised Hover</SubHeader>
|
|
359
|
-
<View style={{ flexDirection: 'row', gap: 16, marginBottom: 24 }}>
|
|
360
|
-
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
361
|
-
<ShadowBox label="Subtle" style={neumorphicShadows.charcoal.raisedHover.subtle} />
|
|
362
|
-
</View>
|
|
363
|
-
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
364
|
-
<ShadowBox label="Medium" style={neumorphicShadows.charcoal.raisedHover.medium} />
|
|
365
|
-
</View>
|
|
366
|
-
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
367
|
-
<ShadowBox label="Strong" style={neumorphicShadows.charcoal.raisedHover.strong} />
|
|
368
|
-
</View>
|
|
369
|
-
</View>
|
|
370
|
-
|
|
371
|
-
<SubHeader>All Intensities - Pressed Hover</SubHeader>
|
|
372
|
-
<View style={{ flexDirection: 'row', gap: 16 }}>
|
|
373
|
-
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
374
|
-
<ShadowBox label="Subtle" style={neumorphicShadows.charcoal.pressedHover.subtle} />
|
|
375
|
-
</View>
|
|
376
|
-
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
377
|
-
<ShadowBox label="Medium" style={neumorphicShadows.charcoal.pressedHover.medium} />
|
|
378
|
-
</View>
|
|
379
|
-
<View style={{ alignItems: 'center', gap: 8 }}>
|
|
380
|
-
<ShadowBox label="Strong" style={neumorphicShadows.charcoal.pressedHover.strong} />
|
|
381
|
-
</View>
|
|
382
|
-
</View>
|
|
383
|
-
</View>
|
|
384
|
-
|
|
385
|
-
<View className="bg-surface-base p-4 rounded-lg mt-4">
|
|
386
|
-
<Text className="text-text-secondary text-[11px] font-mono">
|
|
387
|
-
{`/* Hover shadows - reduced depth, rim light preserved */
|
|
388
|
-
|
|
389
|
-
/* Raised Hover (medium) - rim offset stays same to prevent "shrink" */
|
|
390
|
-
box-shadow:
|
|
391
|
-
1px 1px 2px rgba(0,0,0,0.5), /* reduced dark shadow */
|
|
392
|
-
-2px -2px 1px rgba(255,255,255,0.12); /* same rim offset */
|
|
393
|
-
|
|
394
|
-
/* Pressed Hover (medium) */
|
|
395
|
-
box-shadow:
|
|
396
|
-
inset 1px 1px 2px rgba(0,0,0,0.6), /* reduced dark shadow */
|
|
397
|
-
inset -1px -1px 1px rgba(255,255,255,0.12); /* same rim offset */
|
|
398
|
-
|
|
399
|
-
/* Alternative: Flat (no shadow) */
|
|
400
|
-
box-shadow: none;`}
|
|
401
|
-
</Text>
|
|
402
|
-
</View>
|
|
403
|
-
</View>
|
|
404
|
-
),
|
|
405
|
-
decorators: [
|
|
406
|
-
(Story) => (
|
|
407
|
-
<View className="bg-background-base min-h-screen p-4">
|
|
408
|
-
<Story />
|
|
409
|
-
</View>
|
|
410
|
-
),
|
|
411
|
-
],
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
export const InteractiveDemo: Story = {
|
|
415
|
-
render: function Render() {
|
|
416
|
-
const [activeStates, setActiveStates] = React.useState({
|
|
417
|
-
subtle: false,
|
|
418
|
-
medium: false,
|
|
419
|
-
strong: false,
|
|
420
|
-
})
|
|
421
|
-
const [hoveredStates, setHoveredStates] = React.useState({
|
|
422
|
-
subtle: false,
|
|
423
|
-
medium: false,
|
|
424
|
-
strong: false,
|
|
425
|
-
})
|
|
426
|
-
|
|
427
|
-
const intensities: ShadowIntensity[] = ['subtle', 'medium', 'strong']
|
|
428
|
-
const bgColor = '#3C3C3C'
|
|
429
|
-
const hoverColors = getHoverColors(bgColor, 'medium')
|
|
430
|
-
|
|
431
|
-
const getShadowStyle = (intensity: ShadowIntensity) => {
|
|
432
|
-
const isActive = activeStates[intensity]
|
|
433
|
-
const isHovered = hoveredStates[intensity]
|
|
434
|
-
|
|
435
|
-
if (isActive) {
|
|
436
|
-
return isHovered
|
|
437
|
-
? neumorphicShadows.charcoal.pressedHover[intensity]
|
|
438
|
-
: neumorphicShadows.charcoal.pressed[intensity]
|
|
439
|
-
}
|
|
440
|
-
return isHovered
|
|
441
|
-
? neumorphicShadows.charcoal.raisedHover[intensity]
|
|
442
|
-
: neumorphicShadows.charcoal.raised[intensity]
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
const getBgColor = (intensity: ShadowIntensity) => {
|
|
446
|
-
const isActive = activeStates[intensity]
|
|
447
|
-
const isHovered = hoveredStates[intensity]
|
|
448
|
-
if (!isHovered) return bgColor
|
|
449
|
-
return isActive ? hoverColors.pressed : hoverColors.raised
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
const getStateLabel = (intensity: ShadowIntensity) => {
|
|
453
|
-
const isActive = activeStates[intensity]
|
|
454
|
-
const isHovered = hoveredStates[intensity]
|
|
455
|
-
if (isHovered) return isActive ? 'Pressed + Hover' : 'Raised + Hover'
|
|
456
|
-
return isActive ? 'Pressed' : 'Raised'
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
return (
|
|
460
|
-
<View className="p-6">
|
|
461
|
-
<Text className="text-text-primary text-xl font-bold mb-2">
|
|
462
|
-
Interactive Neumorphic Buttons
|
|
463
|
-
</Text>
|
|
464
|
-
<Text className="text-text-secondary text-sm mb-6">
|
|
465
|
-
Click to toggle. Hover colors calculated via color math (adapts to brightness).
|
|
466
|
-
</Text>
|
|
467
|
-
|
|
468
|
-
<View className="bg-surface-elevated p-8 rounded-xl">
|
|
469
|
-
<View className="flex-row gap-8 justify-center">
|
|
470
|
-
{intensities.map((intensity) => (
|
|
471
|
-
<View key={intensity} className="items-center gap-3">
|
|
472
|
-
<Text className="text-text-secondary text-xs capitalize">
|
|
473
|
-
{intensity}
|
|
474
|
-
</Text>
|
|
475
|
-
<View
|
|
476
|
-
// @ts-expect-error - web events
|
|
477
|
-
onMouseEnter={() => setHoveredStates(s => ({ ...s, [intensity]: true }))}
|
|
478
|
-
onMouseLeave={() => setHoveredStates(s => ({ ...s, [intensity]: false }))}
|
|
479
|
-
onClick={() => setActiveStates(s => ({ ...s, [intensity]: !s[intensity] }))}
|
|
480
|
-
style={[
|
|
481
|
-
{
|
|
482
|
-
backgroundColor: getBgColor(intensity),
|
|
483
|
-
paddingVertical: 16,
|
|
484
|
-
paddingHorizontal: 24,
|
|
485
|
-
borderRadius: 12,
|
|
486
|
-
cursor: 'pointer',
|
|
487
|
-
transition: 'box-shadow 0.2s ease, background-color 0.15s ease, transform 0.15s ease',
|
|
488
|
-
transform: activeStates[intensity] ? 'scale(0.98)' : 'scale(1)',
|
|
489
|
-
minWidth: 120,
|
|
490
|
-
alignItems: 'center',
|
|
491
|
-
} as any,
|
|
492
|
-
getShadowStyle(intensity),
|
|
493
|
-
]}
|
|
494
|
-
>
|
|
495
|
-
<Text className="text-text-primary font-bold text-sm">
|
|
496
|
-
{activeStates[intensity] ? 'Active' : 'Inactive'}
|
|
497
|
-
</Text>
|
|
498
|
-
</View>
|
|
499
|
-
<Text className="text-text-secondary text-[10px]">
|
|
500
|
-
{getStateLabel(intensity)}
|
|
501
|
-
</Text>
|
|
502
|
-
</View>
|
|
503
|
-
))}
|
|
504
|
-
</View>
|
|
505
|
-
|
|
506
|
-
<View className="mt-6 items-center">
|
|
507
|
-
<Text className="text-text-secondary text-[11px]">
|
|
508
|
-
{bgColor} → raised: {hoverColors.raised} | pressed: {hoverColors.pressed}
|
|
509
|
-
</Text>
|
|
510
|
-
</View>
|
|
511
|
-
</View>
|
|
512
|
-
</View>
|
|
513
|
-
)
|
|
514
|
-
},
|
|
515
|
-
decorators: [
|
|
516
|
-
(Story) => (
|
|
517
|
-
<View className="bg-background-base min-h-screen p-4">
|
|
518
|
-
<Story />
|
|
519
|
-
</View>
|
|
520
|
-
),
|
|
521
|
-
],
|
|
522
|
-
}
|
|
523
|
-
|