@titan-design/react-ui 0.13.0 → 0.14.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/README.md +14 -0
- package/dist/bodymap.js +129 -76
- package/dist/bodymap.js.map +1 -1
- package/dist/bodymap.mjs +129 -76
- package/dist/bodymap.mjs.map +1 -1
- package/dist/index.d.mts +117 -26
- package/dist/index.d.ts +117 -26
- package/dist/index.js +677 -450
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +673 -451
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-_3Ajpiqu.d.ts → pages-CsjsUn0G.d.ts} +30 -13
- package/dist/{pages-CpW3OB3g.d.mts → pages-ZbT_pD_U.d.mts} +30 -13
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +676 -701
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +676 -701
- package/dist/pages.mjs.map +1 -1
- package/dist/theme/index.d.mts +31 -7
- package/dist/theme/index.d.ts +31 -7
- package/dist/theme/index.js +84 -20
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +83 -21
- package/dist/theme/index.mjs.map +1 -1
- package/dist/theme/tokens-css.js +84 -20
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +84 -20
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/dist/theme/tokens.d.mts +56 -7
- package/dist/theme/tokens.d.ts +56 -7
- package/dist/theme/tokens.js +72 -20
- package/dist/theme/tokens.js.map +1 -1
- package/dist/theme/tokens.mjs +71 -21
- package/dist/theme/tokens.mjs.map +1 -1
- package/docs/render-testing-pattern.md +130 -0
- package/package.json +1 -1
- package/src/arch/Architecture.stories.tsx +3 -1
- package/src/arch/arch-graph.json +74 -72
- package/src/components/custom/ActiveWork/InitiativeBrief.tsx +2 -1
- package/src/components/custom/CircularTimer/CircularTimer.stories.tsx +8 -3
- package/src/components/custom/EmptyState/EmptyState.stories.tsx +1 -1
- package/src/components/custom/Gauge/Gauge.stories.tsx +4 -1
- package/src/components/custom/Gauge/Gauge.tsx +3 -1
- package/src/components/custom/Scatter/Scatter.stories.tsx +10 -7
- package/src/components/custom/Scatter/Scatter.tsx +6 -4
- package/src/components/custom/Sidebar/Sidebar.stories.tsx +1 -1
- package/src/components/custom/Table/Table.stories.tsx +1 -1
- package/src/components/custom/Table/Table.tsx +2 -2
- package/src/components/custom/Treemap/Treemap.stories.tsx +13 -10
- package/src/components/custom/Treemap/Treemap.tsx +4 -1
- package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +2 -1
- package/src/components/custom/Workout/BaseBadge.stories.tsx +11 -7
- package/src/components/custom/Workout/BaseBadge.tsx +3 -2
- package/src/components/custom/Workout/BodyMap.tsx +5 -3
- package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +13 -3
- package/src/components/custom/Workout/BodyMapDetailPanel.tsx +5 -3
- package/src/components/custom/Workout/CapacityBandChart.stories.tsx +2 -1
- package/src/components/custom/Workout/CapacityBandChart.tsx +5 -3
- package/src/components/custom/Workout/DeviationBar.test.tsx +5 -2
- package/src/components/custom/Workout/DeviationBar.tsx +5 -2
- package/src/components/custom/Workout/DualVelocityStrip.stories.tsx +7 -6
- package/src/components/custom/Workout/DualVelocityStrip.test.tsx +29 -16
- package/src/components/custom/Workout/ExerciseCard.stories.tsx +8 -8
- package/src/components/custom/Workout/ExerciseCard.test.tsx +39 -19
- package/src/components/custom/Workout/ExerciseCard.tsx +92 -167
- package/src/components/custom/Workout/ExerciseCardHeading.stories.tsx +89 -6
- package/src/components/custom/Workout/ExerciseCardHeading.test.tsx +189 -1
- package/src/components/custom/Workout/ExerciseCardHeading.tsx +167 -18
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +3 -2
- package/src/components/custom/Workout/ExerciseHeading.tsx +192 -47
- package/src/components/custom/Workout/ExerciseIndicator.stories.tsx +6 -5
- package/src/components/custom/Workout/InputBar.tsx +4 -2
- package/src/components/custom/Workout/IntensityBar.tsx +4 -1
- package/src/components/custom/Workout/MesoCard.tsx +2 -1
- package/src/components/custom/Workout/MesoStatusCard.tsx +11 -10
- package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +8 -3
- package/src/components/custom/Workout/PrHistoryModal.stories.tsx +12 -2
- package/src/components/custom/Workout/README.md +58 -12
- package/src/components/custom/Workout/ReadinessCheck.tsx +4 -2
- package/src/components/custom/Workout/RestTimer.stories.tsx +13 -2
- package/src/components/custom/Workout/RestTimer.tsx +4 -2
- package/src/components/custom/Workout/SegmentedBar.stories.tsx +16 -12
- package/src/components/custom/Workout/SegmentedBar.tsx +4 -3
- package/src/components/custom/Workout/SetStrip.stories.tsx +3 -2
- package/src/components/custom/Workout/SetsRepsLoad.tsx +23 -9
- package/src/components/custom/Workout/Sparkline.stories.tsx +13 -10
- package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +2 -1
- package/src/components/custom/Workout/StrengthTrendChart.tsx +8 -6
- package/src/components/custom/Workout/TempoDisplay.stories.tsx +4 -3
- package/src/components/custom/Workout/TempoDisplay.tsx +2 -0
- package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +3 -2
- package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +1 -1
- package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +40 -0
- package/src/components/custom/Workout/VelocityStrip.test.tsx +48 -6
- package/src/components/custom/Workout/VelocityStrip.tsx +23 -14
- package/src/components/custom/Workout/ZoneTrack.stories.tsx +3 -1
- package/src/components/custom/Workout/exerciseRowState.ts +29 -0
- package/src/components/custom/Workout/index.ts +12 -2
- package/src/components/custom/Workout/metricText.stories.tsx +9 -8
- package/src/components/custom/Workout/metricText.tsx +4 -2
- package/src/components/custom/Workout/setHeadingKit.tsx +12 -10
- package/src/components/custom/Workout/velocity-story-kit.tsx +2 -2
- package/src/components/custom/charts/README.md +5 -0
- package/src/components/custom/charts/SetBarChart.test.tsx +16 -0
- package/src/components/custom/charts/SetBarChart.tsx +14 -5
- package/src/components/custom/charts/flatBarGeometry.test.ts +41 -0
- package/src/components/custom/charts/flatBarGeometry.ts +32 -0
- package/src/components/custom/stepper/Stepper.tsx +8 -6
- package/src/components/ui/alert/Alert.stories.tsx +14 -4
- package/src/components/ui/alert/Alert.tsx +24 -3
- package/src/components/ui/button/Button.tsx +12 -12
- package/src/components/ui/checkbox/Checkbox.tsx +2 -2
- package/src/components/ui/data-row/DataRow.stories.tsx +6 -2
- package/src/components/ui/drawer/Drawer.tsx +4 -1
- package/src/components/ui/icon-box/IconBox.tsx +8 -6
- package/src/components/ui/indicator/Indicator.stories.tsx +4 -3
- package/src/components/ui/list-item/ListItem.stories.tsx +7 -4
- package/src/components/ui/modal/Modal.tsx +2 -0
- package/src/components/ui/pill/Pill.stories.tsx +53 -0
- package/src/components/ui/pill/Pill.tsx +10 -6
- package/src/components/ui/popover/Popover.stories.tsx +1 -1
- package/src/components/ui/progress/Progress.tsx +9 -6
- package/src/components/ui/radio/Radio.tsx +1 -1
- package/src/components/ui/section/Section.stories.tsx +10 -10
- package/src/components/ui/select/Select.tsx +2 -1
- package/src/components/ui/spinner/Spinner.tsx +3 -1
- package/src/components/ui/stack/Stack.stories.tsx +7 -7
- package/src/components/ui/surface/Surface.stories.tsx +75 -28
- package/src/components/ui/switch/Switch.tsx +1 -1
- package/src/components/ui/tabs/Tabs.tsx +4 -2
- package/src/components/ui/toast/Toast.tsx +6 -4
- package/src/components/ui/toolbar-button/ToolbarButton.tsx +13 -6
- package/src/stories/PresetShowcase.stories.tsx +1 -1
- package/src/test/no-raw-color.test.ts +39 -0
- package/src/theme/Alpha.stories.tsx +103 -0
- package/src/theme/ProposedTokensVW82.stories.tsx +414 -0
- package/src/theme/config.ts +12 -0
- package/src/theme/global.css +15 -3
- package/src/theme/subtle-tone-weight.test.ts +163 -0
- package/src/theme/tailwind-var-opacity.test.ts +163 -0
- package/src/theme/tokens/primitives.ts +33 -0
- package/src/theme/tokens/semantic-hex-guard.test.ts +322 -0
- package/src/theme/tokens/semantic.ts +56 -20
- package/src/utils/colors.alpha-adoption.test.ts +408 -0
- package/tailwind.config.js +50 -9
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
2
|
import { View } from 'react-native'
|
|
3
3
|
import { Indicator } from './Indicator'
|
|
4
|
+
import { primitiveRamps } from '../../../theme/tokens/primitives'
|
|
4
5
|
|
|
5
6
|
const meta: Meta<typeof Indicator> = {
|
|
6
7
|
title: 'Components/Atoms/Indicator',
|
|
@@ -69,9 +70,9 @@ export const WithRing: Story = {
|
|
|
69
70
|
export const CustomColor: Story = {
|
|
70
71
|
render: () => (
|
|
71
72
|
<View className="flex-row gap-4 items-center">
|
|
72
|
-
<Indicator size="md" customColor=
|
|
73
|
-
<Indicator size="md" customColor=
|
|
74
|
-
<Indicator size="md" customColor=
|
|
73
|
+
<Indicator size="md" customColor={primitiveRamps.red[400]} />
|
|
74
|
+
<Indicator size="md" customColor={primitiveRamps.cyan[300]} />
|
|
75
|
+
<Indicator size="md" customColor={primitiveRamps.blue[400]} glow />
|
|
75
76
|
</View>
|
|
76
77
|
),
|
|
77
78
|
}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
ListItemTrailing,
|
|
8
8
|
ListItemDivider,
|
|
9
9
|
} from './ListItem'
|
|
10
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
10
11
|
|
|
11
12
|
function UserIcon({ size = 20 }: { size?: number; className?: string }) {
|
|
12
13
|
return (
|
|
@@ -15,12 +16,14 @@ function UserIcon({ size = 20 }: { size?: number; className?: string }) {
|
|
|
15
16
|
width: size,
|
|
16
17
|
height: size,
|
|
17
18
|
borderRadius: size / 2,
|
|
18
|
-
backgroundColor: '
|
|
19
|
+
backgroundColor: 'var(--color-avatar-background)',
|
|
19
20
|
justifyContent: 'center',
|
|
20
21
|
alignItems: 'center',
|
|
21
22
|
}}
|
|
22
23
|
>
|
|
23
|
-
<Text style={{ color: '
|
|
24
|
+
<Text style={{ color: 'var(--color-avatar-text)', fontSize: size * 0.5, fontWeight: '600' }}>
|
|
25
|
+
U
|
|
26
|
+
</Text>
|
|
24
27
|
</View>
|
|
25
28
|
)
|
|
26
29
|
}
|
|
@@ -50,7 +53,7 @@ function LockIcon({ size = 20 }: { size?: number; className?: string }) {
|
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
function ChevronRight({ size = 16 }: { size?: number }) {
|
|
53
|
-
return <Text style={{ color:
|
|
56
|
+
return <Text style={{ color: greyRamp[400], fontSize: size }}>{'>'}</Text>
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
const meta: Meta<typeof ListItem> = {
|
|
@@ -133,7 +136,7 @@ export const SettingsList: Story = {
|
|
|
133
136
|
<ListItemIcon icon={WifiIcon} />
|
|
134
137
|
<ListItemContent title="Wi-Fi" subtitle="HomeNetwork" />
|
|
135
138
|
<ListItemTrailing>
|
|
136
|
-
<Text style={{ color: '
|
|
139
|
+
<Text style={{ color: 'var(--color-status-success)', fontSize: 12 }}>Connected</Text>
|
|
137
140
|
</ListItemTrailing>
|
|
138
141
|
</ListItem>
|
|
139
142
|
</View>
|
|
@@ -92,6 +92,8 @@ export function Modal({
|
|
|
92
92
|
onPress={handleBackdropPress}
|
|
93
93
|
className={cn(
|
|
94
94
|
'flex-1 items-center justify-center',
|
|
95
|
+
// VW-82: scrims stay raw — Tailwind v3 drops the `/n` modifier on a
|
|
96
|
+
// `var()` colour, so a translucent role has to be its own token (proposed).
|
|
95
97
|
backdropBlur ? 'bg-black/30 web:backdrop-blur-sm' : 'bg-black/50',
|
|
96
98
|
backdropClassName
|
|
97
99
|
)}
|
|
@@ -2,6 +2,8 @@ import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
|
2
2
|
import { View } from 'react-native'
|
|
3
3
|
import { Pill } from './Pill'
|
|
4
4
|
import { Indicator } from '../indicator'
|
|
5
|
+
import { Surface } from '../surface'
|
|
6
|
+
import { Typography } from '../../custom/Typography'
|
|
5
7
|
|
|
6
8
|
const meta: Meta<typeof Pill> = {
|
|
7
9
|
title: 'Components/Atoms/Pill',
|
|
@@ -54,6 +56,57 @@ export const AllTones: Story = {
|
|
|
54
56
|
),
|
|
55
57
|
}
|
|
56
58
|
|
|
59
|
+
/**
|
|
60
|
+
* The same row on both planes a subtle pill actually lands on. A `-subtle` fill is
|
|
61
|
+
* alpha, so it composites against whatever is behind it: the raised card lifts every
|
|
62
|
+
* capsule and costs each label contrast. It is the harder case and the one AW-133 is
|
|
63
|
+
* measured against, so judge the tone weight here, not only on the page plane.
|
|
64
|
+
*/
|
|
65
|
+
export const OnBothPlanes: Story = {
|
|
66
|
+
parameters: {
|
|
67
|
+
docs: {
|
|
68
|
+
description: {
|
|
69
|
+
story:
|
|
70
|
+
'Page plane (`grey-925`) above, raised card (`grey-875`) below. Every tone loses ' +
|
|
71
|
+
'roughly 0.8-1.0 of contrast ratio on the card. `brand` is the deliberate exception ' +
|
|
72
|
+
'from AW-133 — its label stays the exact `orange[400]` brand hue rather than ' +
|
|
73
|
+
'levelling with the other five, which leaves it the one tone under AA here.',
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
render: () => (
|
|
78
|
+
<View className="gap-4">
|
|
79
|
+
<View className="gap-2">
|
|
80
|
+
<Typography variant="caption" color="tertiary">
|
|
81
|
+
page plane · grey-925
|
|
82
|
+
</Typography>
|
|
83
|
+
<View className="flex-row gap-2 flex-wrap">
|
|
84
|
+
<Pill tone="brand">Brand</Pill>
|
|
85
|
+
<Pill tone="brand-secondary">Accent</Pill>
|
|
86
|
+
<Pill tone="success">Success</Pill>
|
|
87
|
+
<Pill tone="warning">Warning</Pill>
|
|
88
|
+
<Pill tone="error">Error</Pill>
|
|
89
|
+
<Pill tone="info">Info</Pill>
|
|
90
|
+
</View>
|
|
91
|
+
</View>
|
|
92
|
+
|
|
93
|
+
<Surface level="raised" className="gap-2 rounded-xl border-hairline p-4">
|
|
94
|
+
<Typography variant="caption" color="tertiary">
|
|
95
|
+
raised card · grey-875
|
|
96
|
+
</Typography>
|
|
97
|
+
<View className="flex-row gap-2 flex-wrap">
|
|
98
|
+
<Pill tone="brand">Brand</Pill>
|
|
99
|
+
<Pill tone="brand-secondary">Accent</Pill>
|
|
100
|
+
<Pill tone="success">Success</Pill>
|
|
101
|
+
<Pill tone="warning">Warning</Pill>
|
|
102
|
+
<Pill tone="error">Error</Pill>
|
|
103
|
+
<Pill tone="info">Info</Pill>
|
|
104
|
+
</View>
|
|
105
|
+
</Surface>
|
|
106
|
+
</View>
|
|
107
|
+
),
|
|
108
|
+
}
|
|
109
|
+
|
|
57
110
|
export const AllSizes: Story = {
|
|
58
111
|
render: () => (
|
|
59
112
|
<View className="flex-row gap-2 items-center">
|
|
@@ -83,13 +83,17 @@ const toneStyles: Record<PillVariant, Record<PillTone, string>> = {
|
|
|
83
83
|
// host is, and an alpha fill composites by the same amount on all of them.
|
|
84
84
|
// A `surface-*` fill would vanish on the plane it names. Borderless like the
|
|
85
85
|
// six coloured tones — the fill carries the capsule, not a ring.
|
|
86
|
+
// Label comes from `on-*-subtle`, not the base tone token: the base is tuned to
|
|
87
|
+
// carry a white label as a solid fill, which left error and accent unreadable
|
|
88
|
+
// here (AW-133). The on-subtle tokens level the family at one weight.
|
|
86
89
|
neutral: 'bg-hairline-subtle border-transparent text-text-secondary',
|
|
87
|
-
brand: 'bg-brand-primary-subtle border-transparent text-brand-primary',
|
|
88
|
-
'brand-secondary':
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
brand: 'bg-brand-primary-subtle border-transparent text-on-brand-primary-subtle',
|
|
91
|
+
'brand-secondary':
|
|
92
|
+
'bg-brand-secondary-subtle border-transparent text-on-brand-secondary-subtle',
|
|
93
|
+
success: 'bg-status-success-subtle border-transparent text-on-status-success-subtle',
|
|
94
|
+
warning: 'bg-status-warning-subtle border-transparent text-on-status-warning-subtle',
|
|
95
|
+
error: 'bg-status-error-subtle border-transparent text-on-status-error-subtle',
|
|
96
|
+
info: 'bg-status-info-subtle border-transparent text-on-status-info-subtle',
|
|
93
97
|
},
|
|
94
98
|
outline: {
|
|
95
99
|
neutral: 'border-hairline text-text-secondary',
|
|
@@ -160,7 +160,7 @@ export const WithFormContent: Story = {
|
|
|
160
160
|
</View>
|
|
161
161
|
</PopoverCloseButton>
|
|
162
162
|
<View className="px-3 py-1.5 rounded-md bg-brand-primary">
|
|
163
|
-
<Text className="text-
|
|
163
|
+
<Text className="text-on-brand-primary text-sm font-medium">Save</Text>
|
|
164
164
|
</View>
|
|
165
165
|
</View>
|
|
166
166
|
</View>
|
|
@@ -56,13 +56,16 @@ const colorStyles: Record<ProgressColor, string> = {
|
|
|
56
56
|
info: 'bg-status-info',
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
// `-muted` rather than `-subtle`: the track is structural — it has to show the
|
|
60
|
+
// bar's full extent behind the fill — and the lightest rung disappears on the
|
|
61
|
+
// dark base. Replaces a `/20` modifier that emitted no rule (VW-308).
|
|
59
62
|
const trackColorStyles: Record<ProgressColor, string> = {
|
|
60
|
-
primary: 'bg-brand-primary
|
|
61
|
-
secondary: 'bg-brand-secondary
|
|
62
|
-
success: 'bg-status-success
|
|
63
|
-
error: 'bg-status-error
|
|
64
|
-
warning: 'bg-status-warning
|
|
65
|
-
info: 'bg-status-info
|
|
63
|
+
primary: 'bg-brand-primary-muted',
|
|
64
|
+
secondary: 'bg-brand-secondary-muted',
|
|
65
|
+
success: 'bg-status-success-muted',
|
|
66
|
+
error: 'bg-status-error-muted',
|
|
67
|
+
warning: 'bg-status-warning-muted',
|
|
68
|
+
info: 'bg-status-info-muted',
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
/**
|
|
@@ -191,7 +191,7 @@ export function Radio({
|
|
|
191
191
|
)}
|
|
192
192
|
>
|
|
193
193
|
{/* Inner dot */}
|
|
194
|
-
{isChecked && <View className={cn('rounded-full bg-
|
|
194
|
+
{isChecked && <View className={cn('rounded-full bg-on-brand-primary', inner)} />}
|
|
195
195
|
</View>
|
|
196
196
|
|
|
197
197
|
{/* Label */}
|
|
@@ -16,7 +16,7 @@ export const Default: Story = {
|
|
|
16
16
|
<Section>
|
|
17
17
|
<SectionHeader title="Recent Workouts" />
|
|
18
18
|
<SectionContent>
|
|
19
|
-
<Text style={{ color: '
|
|
19
|
+
<Text style={{ color: 'var(--color-text-secondary)' }}>Workout items would go here</Text>
|
|
20
20
|
</SectionContent>
|
|
21
21
|
</Section>
|
|
22
22
|
),
|
|
@@ -27,7 +27,7 @@ export const WithSubtitle: Story = {
|
|
|
27
27
|
<Section>
|
|
28
28
|
<SectionHeader title="Workout Stats" subtitle="Last 7 days" />
|
|
29
29
|
<SectionContent>
|
|
30
|
-
<Text style={{ color: '
|
|
30
|
+
<Text style={{ color: 'var(--color-text-secondary)' }}>Stats content</Text>
|
|
31
31
|
</SectionContent>
|
|
32
32
|
</Section>
|
|
33
33
|
),
|
|
@@ -38,10 +38,10 @@ export const WithTrailingAction: Story = {
|
|
|
38
38
|
<Section>
|
|
39
39
|
<SectionHeader
|
|
40
40
|
title="Exercises"
|
|
41
|
-
trailing={<Text style={{ color: '
|
|
41
|
+
trailing={<Text style={{ color: 'var(--color-text-link)', fontSize: 13 }}>View All</Text>}
|
|
42
42
|
/>
|
|
43
43
|
<SectionContent>
|
|
44
|
-
<Text style={{ color: '
|
|
44
|
+
<Text style={{ color: 'var(--color-text-secondary)' }}>Exercise list</Text>
|
|
45
45
|
</SectionContent>
|
|
46
46
|
</Section>
|
|
47
47
|
),
|
|
@@ -53,10 +53,10 @@ export const FullExample: Story = {
|
|
|
53
53
|
<SectionHeader
|
|
54
54
|
title="Performance"
|
|
55
55
|
subtitle="Average velocity by set"
|
|
56
|
-
trailing={<Text style={{ color: '
|
|
56
|
+
trailing={<Text style={{ color: 'var(--color-text-link)', fontSize: 13 }}>Details</Text>}
|
|
57
57
|
/>
|
|
58
58
|
<SectionContent className="rounded-lg bg-surface-elevated p-4">
|
|
59
|
-
<Text style={{ color: '
|
|
59
|
+
<Text style={{ color: 'var(--color-text-secondary)' }}>Chart or data visualization</Text>
|
|
60
60
|
</SectionContent>
|
|
61
61
|
</Section>
|
|
62
62
|
),
|
|
@@ -68,7 +68,7 @@ export const MultipleSections: Story = {
|
|
|
68
68
|
<Section>
|
|
69
69
|
<SectionHeader title="Today" subtitle="3 exercises" />
|
|
70
70
|
<SectionContent className="rounded-lg bg-surface-elevated p-4">
|
|
71
|
-
<Text style={{ color: '
|
|
71
|
+
<Text style={{ color: 'var(--color-text-secondary)' }}>Today's workout data</Text>
|
|
72
72
|
</SectionContent>
|
|
73
73
|
</Section>
|
|
74
74
|
|
|
@@ -76,17 +76,17 @@ export const MultipleSections: Story = {
|
|
|
76
76
|
<SectionHeader
|
|
77
77
|
title="This Week"
|
|
78
78
|
subtitle="12 sets completed"
|
|
79
|
-
trailing={<Text style={{ color: '
|
|
79
|
+
trailing={<Text style={{ color: 'var(--color-text-link)', fontSize: 13 }}>See All</Text>}
|
|
80
80
|
/>
|
|
81
81
|
<SectionContent className="rounded-lg bg-surface-elevated p-4">
|
|
82
|
-
<Text style={{ color: '
|
|
82
|
+
<Text style={{ color: 'var(--color-text-secondary)' }}>Weekly summary</Text>
|
|
83
83
|
</SectionContent>
|
|
84
84
|
</Section>
|
|
85
85
|
|
|
86
86
|
<Section>
|
|
87
87
|
<SectionHeader title="Personal Records" />
|
|
88
88
|
<SectionContent className="rounded-lg bg-surface-elevated p-4">
|
|
89
|
-
<Text style={{ color: '
|
|
89
|
+
<Text style={{ color: 'var(--color-text-secondary)' }}>PR list</Text>
|
|
90
90
|
</SectionContent>
|
|
91
91
|
</Section>
|
|
92
92
|
</View>
|
|
@@ -140,6 +140,7 @@ export function Select<T extends string = string>({
|
|
|
140
140
|
accessibilityState={{ expanded: isOpen, disabled: isDisabled }}
|
|
141
141
|
className={cn(
|
|
142
142
|
'flex-row items-center justify-between px-4 py-2.5 rounded-md border',
|
|
143
|
+
// VW-82: scrim-style fill, left raw (see Modal.tsx).
|
|
143
144
|
variant === 'filled' ? 'bg-black/30' : 'bg-surface-base',
|
|
144
145
|
isInvalid
|
|
145
146
|
? 'border-border-input-error'
|
|
@@ -227,7 +228,7 @@ function SelectOption<T>({ option }: SelectOptionComponentProps<T>) {
|
|
|
227
228
|
selected ? 'bg-brand-primary border-brand-primary' : 'border-hairline-strong'
|
|
228
229
|
)}
|
|
229
230
|
>
|
|
230
|
-
{selected && <Text className="text-
|
|
231
|
+
{selected && <Text className="text-on-brand-primary text-xs text-center">✓</Text>}
|
|
231
232
|
</View>
|
|
232
233
|
)}
|
|
233
234
|
<Text
|
|
@@ -2,6 +2,7 @@ import React from 'react'
|
|
|
2
2
|
import { ActivityIndicator, View, type ViewProps } from 'react-native'
|
|
3
3
|
import { cn } from '../../../utils/cn'
|
|
4
4
|
import { SPINNER_PRIMARY, SPINNER_SECONDARY } from '../../../theme/extracted-colors-ui'
|
|
5
|
+
import { primitiveColors } from '../../../theme/tokens/primitives'
|
|
5
6
|
|
|
6
7
|
export type SpinnerSize = 'sm' | 'md' | 'lg' | 'xl'
|
|
7
8
|
export type SpinnerColor = 'primary' | 'secondary' | 'white' | 'default'
|
|
@@ -27,7 +28,8 @@ const sizeMap: Record<SpinnerSize, 'small' | 'large'> = {
|
|
|
27
28
|
const colorMap: Record<SpinnerColor, string> = {
|
|
28
29
|
primary: SPINNER_PRIMARY,
|
|
29
30
|
secondary: SPINNER_SECONDARY,
|
|
30
|
-
white:
|
|
31
|
+
white: primitiveColors.white,
|
|
32
|
+
// VW-82: Tailwind gray-500; no titan grey matches. Left raw, proposed in the PR.
|
|
31
33
|
default: '#6B7280',
|
|
32
34
|
}
|
|
33
35
|
|
|
@@ -35,7 +35,7 @@ type Story = StoryObj<typeof Stack>
|
|
|
35
35
|
function Box({ children }: { children: string }) {
|
|
36
36
|
return (
|
|
37
37
|
<View className="rounded bg-brand-primary px-4 py-2">
|
|
38
|
-
<Text className="text-sm text-
|
|
38
|
+
<Text className="text-sm text-on-brand-primary">{children}</Text>
|
|
39
39
|
</View>
|
|
40
40
|
)
|
|
41
41
|
}
|
|
@@ -76,23 +76,23 @@ export const Vertical: Story = {
|
|
|
76
76
|
export const WithAlignment: Story = {
|
|
77
77
|
render: () => (
|
|
78
78
|
<VStack gap={6}>
|
|
79
|
-
<Text className="text-sm font-medium text-
|
|
79
|
+
<Text className="text-sm font-medium text-text-secondary">justify="between"</Text>
|
|
80
80
|
<HStack gap={2} justify="between" className="w-full">
|
|
81
81
|
<Box>A</Box>
|
|
82
82
|
<Box>B</Box>
|
|
83
83
|
<Box>C</Box>
|
|
84
84
|
</HStack>
|
|
85
85
|
|
|
86
|
-
<Text className="text-sm font-medium text-
|
|
86
|
+
<Text className="text-sm font-medium text-text-secondary">align="center"</Text>
|
|
87
87
|
<HStack gap={2} align="center">
|
|
88
88
|
<View className="rounded bg-brand-primary px-4 py-1">
|
|
89
|
-
<Text className="text-sm text-
|
|
89
|
+
<Text className="text-sm text-on-brand-primary">Short</Text>
|
|
90
90
|
</View>
|
|
91
91
|
<View className="rounded bg-brand-primary px-4 py-4">
|
|
92
|
-
<Text className="text-sm text-
|
|
92
|
+
<Text className="text-sm text-on-brand-primary">Tall</Text>
|
|
93
93
|
</View>
|
|
94
94
|
<View className="rounded bg-brand-primary px-4 py-2">
|
|
95
|
-
<Text className="text-sm text-
|
|
95
|
+
<Text className="text-sm text-on-brand-primary">Medium</Text>
|
|
96
96
|
</View>
|
|
97
97
|
</HStack>
|
|
98
98
|
</VStack>
|
|
@@ -114,7 +114,7 @@ export const GapScale: Story = {
|
|
|
114
114
|
<VStack gap={6}>
|
|
115
115
|
{([0, 1, 2, 4, 6, 8, 12] as const).map((g) => (
|
|
116
116
|
<VStack key={g} gap={1}>
|
|
117
|
-
<Text className="text-xs text-
|
|
117
|
+
<Text className="text-xs text-text-secondary">gap={g}</Text>
|
|
118
118
|
<HStack gap={g}>
|
|
119
119
|
<Box>A</Box>
|
|
120
120
|
<Box>B</Box>
|
|
@@ -3,6 +3,8 @@ import { View, Text } from 'react-native'
|
|
|
3
3
|
import { Surface } from './Surface'
|
|
4
4
|
import { useOnSurfaceColor, type SurfaceLevel } from './SurfaceContext'
|
|
5
5
|
import type { ElevationLevel } from '../../../theme/elevation'
|
|
6
|
+
import { semanticColorsLight, semanticColorsDark } from '../../../theme/tokens/semantic'
|
|
7
|
+
import { primitiveRamps } from '../../../theme/tokens/primitives'
|
|
6
8
|
|
|
7
9
|
const meta: Meta<typeof Surface> = {
|
|
8
10
|
title: 'Components/Atoms/Surface',
|
|
@@ -56,11 +58,18 @@ export const Default: Story = {
|
|
|
56
58
|
elevation: 2,
|
|
57
59
|
theme: 'dark',
|
|
58
60
|
},
|
|
59
|
-
render: (args)
|
|
60
|
-
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
render: function Render(args) {
|
|
62
|
+
return (
|
|
63
|
+
<Surface {...args} style={{ padding: 24 }}>
|
|
64
|
+
<Text style={{ color: useOnSurfaceColor('primary') }}>Default Surface</Text>
|
|
65
|
+
</Surface>
|
|
66
|
+
)
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** On-surface elevation label — resolves from whichever Surface encloses it. */
|
|
71
|
+
function ElevationLabel({ level }: { level: ElevationLevel }) {
|
|
72
|
+
return <Text style={{ color: useOnSurfaceColor('primary') }}>Elevation {level}</Text>
|
|
64
73
|
}
|
|
65
74
|
|
|
66
75
|
// Every level is a ramp plane. Negative levels recess, 0 sits flat on the page,
|
|
@@ -71,7 +80,7 @@ export const ElevationLevels: Story = {
|
|
|
71
80
|
<Surface level="base" style={{ gap: 16, padding: 24 }}>
|
|
72
81
|
{([-2, -1, 0, 1, 2, 3, 4, 5] as ElevationLevel[]).map((level) => (
|
|
73
82
|
<Surface key={level} elevation={level} style={{ padding: 16 }}>
|
|
74
|
-
<
|
|
83
|
+
<ElevationLabel level={level} />
|
|
75
84
|
</Surface>
|
|
76
85
|
))}
|
|
77
86
|
</Surface>
|
|
@@ -79,25 +88,58 @@ export const ElevationLevels: Story = {
|
|
|
79
88
|
}
|
|
80
89
|
|
|
81
90
|
export const WithGlow: Story = {
|
|
82
|
-
render: ()
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
91
|
+
render: function Render() {
|
|
92
|
+
const onSurface = useOnSurfaceColor('primary')
|
|
93
|
+
return (
|
|
94
|
+
<View style={{ gap: 16, padding: 24 }}>
|
|
95
|
+
<Surface
|
|
96
|
+
elevation={2}
|
|
97
|
+
glowColor={primitiveRamps.orange[400]}
|
|
98
|
+
glowIntensity="subtle"
|
|
99
|
+
style={{ padding: 16 }}
|
|
100
|
+
>
|
|
101
|
+
<Text style={{ color: onSurface }}>Subtle Orange Glow</Text>
|
|
102
|
+
</Surface>
|
|
103
|
+
<Surface
|
|
104
|
+
elevation={2}
|
|
105
|
+
glowColor={primitiveRamps.orange[400]}
|
|
106
|
+
glowIntensity="medium"
|
|
107
|
+
style={{ padding: 16 }}
|
|
108
|
+
>
|
|
109
|
+
<Text style={{ color: onSurface }}>Medium Orange Glow</Text>
|
|
110
|
+
</Surface>
|
|
111
|
+
<Surface
|
|
112
|
+
elevation={2}
|
|
113
|
+
glowColor={primitiveRamps.orange[400]}
|
|
114
|
+
glowIntensity="strong"
|
|
115
|
+
style={{ padding: 16 }}
|
|
116
|
+
>
|
|
117
|
+
<Text style={{ color: onSurface }}>Strong Orange Glow</Text>
|
|
118
|
+
</Surface>
|
|
119
|
+
<Surface
|
|
120
|
+
elevation={2}
|
|
121
|
+
glowColor={semanticColorsDark['status-success']}
|
|
122
|
+
glowIntensity="medium"
|
|
123
|
+
style={{ padding: 16 }}
|
|
124
|
+
>
|
|
125
|
+
<Text style={{ color: onSurface }}>Green Glow (Success)</Text>
|
|
126
|
+
</Surface>
|
|
127
|
+
<Surface
|
|
128
|
+
elevation={2}
|
|
129
|
+
glowColor={semanticColorsDark['status-error']}
|
|
130
|
+
glowIntensity="medium"
|
|
131
|
+
style={{ padding: 16 }}
|
|
132
|
+
>
|
|
133
|
+
<Text style={{ color: onSurface }}>Red Glow (Error)</Text>
|
|
134
|
+
</Surface>
|
|
135
|
+
</View>
|
|
136
|
+
)
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** On-surface plane label — resolves from whichever Surface encloses it. */
|
|
141
|
+
function NamedPlaneLabel({ level }: { level: SurfaceLevel }) {
|
|
142
|
+
return <Text style={{ color: useOnSurfaceColor('primary') }}>level="{level}"</Text>
|
|
101
143
|
}
|
|
102
144
|
|
|
103
145
|
// The named-plane model: flat, full-bleed grey planes straight from the
|
|
@@ -108,7 +150,7 @@ export const NamedPlanes: Story = {
|
|
|
108
150
|
<View style={{ gap: 12, padding: 24 }}>
|
|
109
151
|
{(['background', 'base', 'elevated', 'raised', 'overlay'] as SurfaceLevel[]).map((level) => (
|
|
110
152
|
<Surface key={level} level={level} style={{ padding: 16 }}>
|
|
111
|
-
<
|
|
153
|
+
<NamedPlaneLabel level={level} />
|
|
112
154
|
</Surface>
|
|
113
155
|
))}
|
|
114
156
|
</View>
|
|
@@ -199,12 +241,17 @@ export const OnSurfaceText: Story = {
|
|
|
199
241
|
),
|
|
200
242
|
}
|
|
201
243
|
|
|
244
|
+
/** On-surface elevation label for the forced-light demo below. */
|
|
245
|
+
function LightElevationLabel({ level }: { level: ElevationLevel }) {
|
|
246
|
+
return <Text style={{ color: useOnSurfaceColor('primary') }}>Light Elevation {level}</Text>
|
|
247
|
+
}
|
|
248
|
+
|
|
202
249
|
export const LightTheme: Story = {
|
|
203
250
|
render: () => (
|
|
204
|
-
<View style={{ gap: 16, padding: 24, backgroundColor: '
|
|
251
|
+
<View style={{ gap: 16, padding: 24, backgroundColor: semanticColorsLight['background-base'] }}>
|
|
205
252
|
{([0, 1, 2, 3, 4, 5] as ElevationLevel[]).map((level) => (
|
|
206
253
|
<Surface key={level} elevation={level} theme="light" style={{ padding: 16 }}>
|
|
207
|
-
<
|
|
254
|
+
<LightElevationLabel level={level} />
|
|
208
255
|
</Surface>
|
|
209
256
|
))}
|
|
210
257
|
</View>
|
|
@@ -72,7 +72,7 @@ export const Switch = forwardRef<View, SwitchProps>(function Switch(
|
|
|
72
72
|
{/* The thumb is a raised control resting in its track: one plane of lift. */}
|
|
73
73
|
<View
|
|
74
74
|
className={cn(
|
|
75
|
-
'rounded-full bg-
|
|
75
|
+
'rounded-full bg-on-brand-primary transition-transform',
|
|
76
76
|
styles.thumb,
|
|
77
77
|
isChecked && styles.translate
|
|
78
78
|
)}
|
|
@@ -169,7 +169,9 @@ export function Tab({ index = 0, isDisabled = false, className, children }: TabP
|
|
|
169
169
|
),
|
|
170
170
|
'soft-rounded': cn(
|
|
171
171
|
'px-4 py-2 rounded-full',
|
|
172
|
-
isActive
|
|
172
|
+
isActive
|
|
173
|
+
? 'bg-brand-primary text-on-brand-primary'
|
|
174
|
+
: 'text-text-secondary web:hover:text-text-primary'
|
|
173
175
|
),
|
|
174
176
|
}
|
|
175
177
|
|
|
@@ -191,7 +193,7 @@ export function Tab({ index = 0, isDisabled = false, className, children }: TabP
|
|
|
191
193
|
'font-medium',
|
|
192
194
|
isActive
|
|
193
195
|
? variant === 'soft-rounded'
|
|
194
|
-
? 'text-
|
|
196
|
+
? 'text-on-brand-primary'
|
|
195
197
|
: 'text-text-primary'
|
|
196
198
|
: 'text-text-secondary'
|
|
197
199
|
)}
|
|
@@ -157,24 +157,26 @@ interface ToastItemProps extends ToastConfig {
|
|
|
157
157
|
onClose: () => void
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
// `-subtle` is the wash ladder's lightest rung. It replaces a `/10` opacity
|
|
161
|
+
// modifier, which emitted no rule at all against a var()-backed token (VW-308).
|
|
160
162
|
const statusStyles: Record<ToastStatus, { bg: string; border: string; icon: string }> = {
|
|
161
163
|
success: {
|
|
162
|
-
bg: 'bg-status-success
|
|
164
|
+
bg: 'bg-status-success-subtle',
|
|
163
165
|
border: 'border-status-success',
|
|
164
166
|
icon: '✓',
|
|
165
167
|
},
|
|
166
168
|
error: {
|
|
167
|
-
bg: 'bg-status-error
|
|
169
|
+
bg: 'bg-status-error-subtle',
|
|
168
170
|
border: 'border-status-error',
|
|
169
171
|
icon: '✕',
|
|
170
172
|
},
|
|
171
173
|
warning: {
|
|
172
|
-
bg: 'bg-status-warning
|
|
174
|
+
bg: 'bg-status-warning-subtle',
|
|
173
175
|
border: 'border-status-warning',
|
|
174
176
|
icon: '⚠',
|
|
175
177
|
},
|
|
176
178
|
info: {
|
|
177
|
-
bg: 'bg-status-info
|
|
179
|
+
bg: 'bg-status-info-subtle',
|
|
178
180
|
border: 'border-status-info',
|
|
179
181
|
icon: 'ℹ',
|
|
180
182
|
},
|
|
@@ -2,10 +2,11 @@ import React, { useState, useCallback, createContext, useContext } from 'react'
|
|
|
2
2
|
import { View, Text, Pressable, type ViewProps, StyleSheet, Platform } from 'react-native'
|
|
3
3
|
import { cn } from '../../../utils/cn'
|
|
4
4
|
import { getHoverColors } from '../../../theme'
|
|
5
|
-
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
5
|
+
import { greyRamp, primitiveColors } from '../../../theme/tokens/primitives'
|
|
6
6
|
import { resolveColor } from '../../../theme/resolve-color'
|
|
7
7
|
import { getPressedRecessShadow } from '../../../theme/elevation'
|
|
8
8
|
import { liftStyle } from '../../../theme/lift'
|
|
9
|
+
import { alpha } from '../../../utils/colors'
|
|
9
10
|
import { Surface, useSurfaceMode } from '../surface'
|
|
10
11
|
|
|
11
12
|
export type ToolbarButtonVariant = 'default' | 'raised'
|
|
@@ -16,6 +17,14 @@ interface ToolbarButtonContextType {
|
|
|
16
17
|
setIsOpen: (open: boolean) => void
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Idle icon/label tone. VW-82: no token resolves to this — it sits between
|
|
22
|
+
* `text-secondary` (#A29F9D) and `text-primary` (#F9F6F3) and is cooler than
|
|
23
|
+
* either, so snapping it to the warm grey ramp is a visual change awaiting the
|
|
24
|
+
* proposed `on-control-idle` role rather than a silent swap.
|
|
25
|
+
*/
|
|
26
|
+
const ICON_IDLE = '#D1D1D1'
|
|
27
|
+
|
|
19
28
|
const ToolbarButtonContext = createContext<ToolbarButtonContextType>({
|
|
20
29
|
isOpen: false,
|
|
21
30
|
setIsOpen: () => {},
|
|
@@ -51,9 +60,7 @@ export interface ToolbarButtonProps extends ViewProps {
|
|
|
51
60
|
className?: string
|
|
52
61
|
}
|
|
53
62
|
|
|
54
|
-
// Base button colours.
|
|
55
|
-
// charcoal steps that survived the grey migration only because they were plain
|
|
56
|
-
// strings rather than scale references.
|
|
63
|
+
// Base button colours.
|
|
57
64
|
const BUTTON_BG = greyRamp[800]
|
|
58
65
|
|
|
59
66
|
// Calculate hover colors using color math
|
|
@@ -137,7 +144,7 @@ export function ToolbarButton({
|
|
|
137
144
|
// Determine icon color based on state
|
|
138
145
|
const getIconColor = () => {
|
|
139
146
|
if (isActive === true) return resolveColor('brand-primary')
|
|
140
|
-
return showActive ?
|
|
147
|
+
return showActive ? primitiveColors.white : ICON_IDLE
|
|
141
148
|
}
|
|
142
149
|
|
|
143
150
|
/**
|
|
@@ -246,7 +253,7 @@ export function ToolbarButton({
|
|
|
246
253
|
const styles = StyleSheet.create({
|
|
247
254
|
// Disabled - flat gray background, no shadows
|
|
248
255
|
disabledBg: {
|
|
249
|
-
backgroundColor:
|
|
256
|
+
backgroundColor: alpha(primitiveColors.white, 0.12),
|
|
250
257
|
...Platform.select({
|
|
251
258
|
web: { boxShadow: 'none' } as any,
|
|
252
259
|
default: { shadowOpacity: 0, elevation: 0 },
|