@titan-design/react-ui 0.10.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 +416 -18
- package/dist/index.d.ts +416 -18
- package/dist/index.js +2512 -1307
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2480 -1299
- package/dist/index.mjs.map +1 -1
- package/dist/{pages-DyEx-lBf.d.ts → pages-B1hqnGIC.d.ts} +149 -15
- package/dist/{pages-D7hOD4Vj.d.mts → pages-CvsFqNtx.d.mts} +149 -15
- package/dist/pages.d.mts +1 -1
- package/dist/pages.d.ts +1 -1
- package/dist/pages.js +1644 -1575
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +1645 -1576
- 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 +8 -3
- 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 +258 -0
- package/src/components/custom/Fatigue/DualGhostSpark.test.tsx +138 -0
- package/src/components/custom/Fatigue/DualGhostSpark.tsx +208 -0
- package/src/components/custom/Fatigue/FatigueLights.stories.tsx +74 -0
- package/src/components/custom/Fatigue/FatigueLights.test.tsx +33 -0
- package/src/components/custom/Fatigue/FatigueLights.tsx +83 -0
- package/src/components/custom/Fatigue/GhostBand.test.tsx +235 -0
- package/src/components/custom/Fatigue/GhostBand.tsx +246 -0
- package/src/components/custom/Fatigue/GhostBloom.tsx +132 -0
- package/src/components/custom/Fatigue/GhostSpark.stories.tsx +175 -0
- package/src/components/custom/Fatigue/GhostSpark.test.tsx +45 -0
- package/src/components/custom/Fatigue/GhostSpark.tsx +126 -0
- package/src/components/custom/Fatigue/LiveFatigueCard.stories.tsx +126 -0
- package/src/components/custom/Fatigue/LiveFatigueCard.test.tsx +28 -0
- package/src/components/custom/Fatigue/LiveFatigueCard.tsx +86 -0
- package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +95 -0
- package/src/components/custom/Fatigue/LiveFatiguePanel.tsx +151 -0
- package/src/components/custom/Fatigue/README.md +93 -0
- package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +94 -0
- package/src/components/custom/Fatigue/RomProgressionChart.test.tsx +31 -0
- package/src/components/custom/Fatigue/RomProgressionChart.tsx +132 -0
- package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +226 -0
- package/src/components/custom/Fatigue/VelocityHero.stories.tsx +70 -0
- package/src/components/custom/Fatigue/VelocityHero.test.tsx +26 -0
- package/src/components/custom/Fatigue/VelocityHero.tsx +46 -0
- package/src/components/custom/Fatigue/VerdictHero.stories.tsx +59 -0
- package/src/components/custom/Fatigue/VerdictHero.test.tsx +49 -0
- package/src/components/custom/Fatigue/VerdictHero.tsx +79 -0
- package/src/components/custom/Fatigue/fatigue-mock.test.ts +59 -0
- package/src/components/custom/Fatigue/fatigue-mock.ts +268 -0
- package/src/components/custom/Fatigue/fatigue-model.ts +129 -0
- package/src/components/custom/Fatigue/fatigue-tokens.test.ts +70 -0
- package/src/components/custom/Fatigue/fatigue-tokens.ts +180 -0
- package/src/components/custom/Fatigue/index.ts +41 -0
- package/src/components/custom/Fatigue/tempo-pacing.test.ts +176 -0
- package/src/components/custom/Fatigue/tempo-pacing.ts +112 -0
- 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/DualVelocityStrip.test.tsx +685 -0
- package/src/components/custom/Workout/ExerciseCard.test.tsx +7 -7
- package/src/components/custom/Workout/ExerciseCard.tsx +5 -3
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +11 -5
- 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 +62 -16
- 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/SegmentedBar.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.test.tsx +43 -0
- package/src/components/custom/Workout/SessionRail.tsx +63 -18
- package/src/components/custom/Workout/SetBar.tsx +3 -3
- package/src/components/custom/Workout/SetRow.test.tsx +12 -9
- package/src/components/custom/Workout/SetRow.tsx +9 -2
- 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.compact.stories.tsx +191 -0
- package/src/components/custom/Workout/VelocityStrip.dual.stories.tsx +186 -0
- package/src/components/custom/Workout/VelocityStrip.expanded.stories.tsx +296 -0
- package/src/components/custom/Workout/VelocityStrip.hero.stories.tsx +259 -0
- package/src/components/custom/Workout/VelocityStrip.stories.tsx +217 -250
- package/src/components/custom/Workout/VelocityStrip.test.tsx +199 -79
- package/src/components/custom/Workout/VelocityStrip.tsx +953 -609
- 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/index.ts +3 -0
- package/src/components/custom/Workout/setHeadingKit.tsx +14 -8
- package/src/components/custom/Workout/velocity-story-kit.tsx +438 -0
- package/src/components/custom/charts/SetBarChart.test.tsx +275 -0
- package/src/components/custom/charts/SetBarChart.tsx +651 -0
- package/src/components/custom/charts/live-rep-growth.ts +91 -0
- package/src/components/custom/index.ts +1 -0
- 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/test/raw-color-patterns.test.ts +71 -0
- package/src/theme/ColorPalettes.stories.tsx +874 -0
- package/src/theme/ColorPrimitives.stories.tsx +348 -0
- 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 +125 -0
- package/src/theme/color-story-kit.tsx +170 -0
- 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/components/custom/Workout/VelocityStrip.modalities.stories.tsx +0 -254
- package/src/components/custom/Workout/VelocityStrip.responsive.stories.tsx +0 -145
- package/src/theme/ColorSystem.stories.tsx +0 -508
- package/src/theme/Colors.stories.tsx +0 -323
- package/src/theme/shadows.stories.tsx +0 -523
- package/src/theme/shadows.ts +0 -439
|
@@ -17,13 +17,34 @@
|
|
|
17
17
|
* - interactive-* : Hover/focus/active/disabled states
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
import { primitiveColors as p, primitiveRamps as ramp, greyRamp, discreteRainbow } from './primitives'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* GREY MAPPING (TD-07.14) — how the old cool scales resolved onto `greyRamp`.
|
|
24
|
+
*
|
|
25
|
+
* Every grey below came off `grey` (pure R=G=B) or `neutral` (cool, R−B
|
|
26
|
+
* down to −26). They were snapped by NEAREST L*, not by matching step numbers,
|
|
27
|
+
* because the two old scales were numbered incompatibly: `grey` ran
|
|
28
|
+
* INVERTED and compressed (0 = #6E6E6E lightest, 900 = #101010 darkest), so
|
|
29
|
+
* `grey[400]` and `greyRamp[400]` have nothing to do with each other.
|
|
30
|
+
*
|
|
31
|
+
* grey 200→800 300→900 400→925 500→950
|
|
32
|
+
* neutral 50→50 100→100 300→200 400→400 500→600 600→700 900→950
|
|
33
|
+
*
|
|
34
|
+
* Surfaces move ΔE 1.9–4.8 — at or near imperceptible. The TEXT roles move
|
|
35
|
+
* further (8.6–12.8), and that is the intended part of the change: they are
|
|
36
|
+
* picking up the warmth the surfaces already had.
|
|
37
|
+
*
|
|
38
|
+
* Light gets its own column because strict L*-nearest collapsed `neutral[50]`
|
|
39
|
+
* and `[100]` onto one step, flattening surface-elevated into surface-raised.
|
|
40
|
+
* Light is still deferred as a design question — there is no `surfaceRampLight`.
|
|
41
|
+
*
|
|
42
|
+
* `border-default`, `-subtle` and `-strong` are GONE, replaced by `hairline-*`.
|
|
43
|
+
* Once the planes became ramp steps, every value dark enough to read as a
|
|
44
|
+
* border was also a plane's fill. Alpha composites instead of colliding.
|
|
45
|
+
*
|
|
46
|
+
* `text-tertiary` on dark is the one role that ignores L*-nearest; see it.
|
|
47
|
+
*/
|
|
27
48
|
|
|
28
49
|
// Light mode semantic colors (default)
|
|
29
50
|
export const semanticColorsLight = {
|
|
@@ -61,9 +82,9 @@ export const semanticColorsLight = {
|
|
|
61
82
|
'status-error-dark': ramp.red[700],
|
|
62
83
|
'status-error-subtle': ramp.red[50],
|
|
63
84
|
|
|
64
|
-
'status-error-vivid':
|
|
65
|
-
'status-error-vivid-light':
|
|
66
|
-
'status-error-vivid-dark':
|
|
85
|
+
'status-error-vivid': ramp.red[600], // vivid alert red
|
|
86
|
+
'status-error-vivid-light': ramp.red[500],
|
|
87
|
+
'status-error-vivid-dark': ramp.red[700],
|
|
67
88
|
'status-error-vivid-subtle': 'rgba(255, 71, 87, 0.12)',
|
|
68
89
|
|
|
69
90
|
'status-warning': ramp.amber[300],
|
|
@@ -91,7 +112,7 @@ export const semanticColorsLight = {
|
|
|
91
112
|
'result-degrade-dark': '#b30000',
|
|
92
113
|
'result-inconclusive': '#9E9A97', // Gray - no clear result
|
|
93
114
|
'result-inconclusive-light': 'rgba(158, 154, 151, 0.12)',
|
|
94
|
-
'result-neutral':
|
|
115
|
+
'result-neutral': greyRamp[600], // Neutral baseline
|
|
95
116
|
|
|
96
117
|
// Text on result backgrounds (on-result-*)
|
|
97
118
|
'on-result-improve': p.white,
|
|
@@ -114,43 +135,34 @@ export const semanticColorsLight = {
|
|
|
114
135
|
// Text colors (text-*)
|
|
115
136
|
'text-primary': '#121828',
|
|
116
137
|
'text-secondary': '#65748B',
|
|
117
|
-
'text-tertiary':
|
|
138
|
+
'text-tertiary': greyRamp[400],
|
|
118
139
|
'text-disabled': 'rgba(55, 65, 81, 0.48)',
|
|
119
140
|
'text-inverse': p.white,
|
|
120
|
-
'text-link':
|
|
121
|
-
'text-link-hover':
|
|
141
|
+
'text-link': ramp.blue[600],
|
|
142
|
+
'text-link-hover': ramp.blue[700],
|
|
122
143
|
|
|
123
144
|
// Surface colors (surface-*) - for elevated containers
|
|
124
145
|
'surface-base': p.white,
|
|
125
|
-
'surface-elevated':
|
|
126
|
-
'surface-raised':
|
|
146
|
+
'surface-elevated': greyRamp[50], // slightly off-white for elevated cards
|
|
147
|
+
'surface-raised': greyRamp[100], // light gray for raised cards
|
|
127
148
|
'surface-overlay': p.white,
|
|
128
|
-
'surface-input':
|
|
129
|
-
// Deepest pressed pit (TD-surface-tokens, S-3) — light-mode counterpart of
|
|
130
|
-
// the dark ramp's `inset` plane; a placeholder pairing, not part of the
|
|
131
|
-
// dark-ramp investigation (surface.contract.test.ts is dark-mode only).
|
|
132
|
-
'surface-inset': p.neutral[300],
|
|
149
|
+
'surface-input': greyRamp[50], // Input field background (filled variant)
|
|
133
150
|
|
|
134
151
|
// Background colors (background-*)
|
|
135
152
|
'background-base': '#EBEBEB',
|
|
136
153
|
'background-default': p.white,
|
|
137
|
-
'background-subtle':
|
|
138
|
-
// Frame/bezel chrome
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
// mode (see dark-mode note on `surface-inset` above).
|
|
142
|
-
'background-frame': p.neutral[400],
|
|
154
|
+
'background-subtle': greyRamp[50],
|
|
155
|
+
// Frame/bezel chrome — top bar + side nav shell, one step below
|
|
156
|
+
// `background-base`. Placeholder pairing for light mode, which is deferred.
|
|
157
|
+
'background-frame': greyRamp[400],
|
|
143
158
|
|
|
144
159
|
// Border colors (border-*)
|
|
145
|
-
'border-
|
|
146
|
-
'border-
|
|
147
|
-
'border-
|
|
148
|
-
'border-
|
|
149
|
-
'border-focus':
|
|
150
|
-
'border-input':
|
|
151
|
-
'border-input-hover': p.neutral[400], // Input field border on hover
|
|
152
|
-
'border-input-focus': p.blue[600], // Input field border on focus
|
|
153
|
-
'border-input-error': p.red[600], // Input field border on error
|
|
160
|
+
'border-prominent': greyRamp[400], // high-visibility divider
|
|
161
|
+
'border-focus': ramp.blue[600],
|
|
162
|
+
'border-input': greyRamp[200], // Input field border
|
|
163
|
+
'border-input-hover': greyRamp[400], // Input field border on hover
|
|
164
|
+
'border-input-focus': ramp.blue[600], // Input field border on focus
|
|
165
|
+
'border-input-error': ramp.red[600], // Input field border on error
|
|
154
166
|
|
|
155
167
|
// Alpha hairline separators (surface-independent — composite toward black on
|
|
156
168
|
// light surfaces, mirroring the dark-mode white-alpha family). See §4/S-2.
|
|
@@ -170,7 +182,7 @@ export const semanticColorsLight = {
|
|
|
170
182
|
'divider': '#E8E9EB',
|
|
171
183
|
|
|
172
184
|
// Avatar default
|
|
173
|
-
'avatar-background':
|
|
185
|
+
'avatar-background': greyRamp[600],
|
|
174
186
|
'avatar-text': p.white,
|
|
175
187
|
} as const
|
|
176
188
|
|
|
@@ -210,9 +222,9 @@ export const semanticColorsDark = {
|
|
|
210
222
|
'status-error-dark': ramp.red[700],
|
|
211
223
|
'status-error-subtle': 'rgba(209, 67, 67, 0.12)',
|
|
212
224
|
|
|
213
|
-
'status-error-vivid':
|
|
214
|
-
'status-error-vivid-light':
|
|
215
|
-
'status-error-vivid-dark':
|
|
225
|
+
'status-error-vivid': ramp.red[600],
|
|
226
|
+
'status-error-vivid-light': ramp.red[500],
|
|
227
|
+
'status-error-vivid-dark': ramp.red[700],
|
|
216
228
|
'status-error-vivid-subtle': 'rgba(255, 71, 87, 0.12)',
|
|
217
229
|
|
|
218
230
|
'status-warning': ramp.amber[300],
|
|
@@ -240,7 +252,7 @@ export const semanticColorsDark = {
|
|
|
240
252
|
'result-degrade-dark': '#b30000',
|
|
241
253
|
'result-inconclusive': '#9E9A97',
|
|
242
254
|
'result-inconclusive-light': 'rgba(158, 154, 151, 0.16)',
|
|
243
|
-
'result-neutral':
|
|
255
|
+
'result-neutral': greyRamp[400],
|
|
244
256
|
|
|
245
257
|
// Text on result backgrounds
|
|
246
258
|
'on-result-improve': p.white,
|
|
@@ -260,13 +272,20 @@ export const semanticColorsDark = {
|
|
|
260
272
|
'data-10': discreteRainbow[22],
|
|
261
273
|
|
|
262
274
|
// Text colors - inverted for dark mode
|
|
263
|
-
'text-primary':
|
|
264
|
-
'text-secondary':
|
|
265
|
-
|
|
275
|
+
'text-primary': greyRamp[50],
|
|
276
|
+
'text-secondary': greyRamp[400],
|
|
277
|
+
// Deliberately ONE step lighter than the L*-nearest match (`greyRamp[600]`).
|
|
278
|
+
// The colour this replaced failed WCAG large-text outright on the three
|
|
279
|
+
// lightest planes — 2.96 / 2.72 / 2.49 against elevated / raised / overlay —
|
|
280
|
+
// and `600` carried that failure forward almost exactly (2.93 / 2.70 / 2.47).
|
|
281
|
+
// `500` clears 3:1 everywhere (3.32–5.34). Contrast beats colour fidelity for
|
|
282
|
+
// text roles; borders and surfaces still use strict L*-nearest. See
|
|
283
|
+
// semantic-contrast.test.ts, which fails if this is moved back down.
|
|
284
|
+
'text-tertiary': greyRamp[500],
|
|
266
285
|
'text-disabled': 'rgba(255, 255, 255, 0.38)',
|
|
267
|
-
'text-inverse':
|
|
286
|
+
'text-inverse': greyRamp[950],
|
|
268
287
|
'text-link': '#828DF8',
|
|
269
|
-
'text-link-hover':
|
|
288
|
+
'text-link-hover': ramp.blue[400],
|
|
270
289
|
|
|
271
290
|
// Surface colors - dark backgrounds — warm-tapered DERIVED ramp (TD-surface-tokens,
|
|
272
291
|
// S-1, re-spaced S-3). Shipped verbatim from `deriveSurfaceRamp()` — see
|
|
@@ -274,50 +293,79 @@ export const semanticColorsDark = {
|
|
|
274
293
|
// and `surface-elevated` are distinct (were both #191919 pre-S-1); `surface-input`
|
|
275
294
|
// tracks one plane above `surface-base`, same relative position as before the remap.
|
|
276
295
|
// `background-base` backs `Surface level="background"` (SurfaceContext.SURFACE_LEVEL_TOKEN),
|
|
277
|
-
// so it takes the ramp's
|
|
278
|
-
//
|
|
279
|
-
//
|
|
280
|
-
//
|
|
281
|
-
'surface-
|
|
282
|
-
'surface-
|
|
283
|
-
'surface-
|
|
284
|
-
'surface-overlay': surf.overlay, // overlay surface (#373635, L*22.5 — hero/popover)
|
|
285
|
-
'surface-input': surf.elevated, // input surface (#2C2A28 — one plane above base)
|
|
286
|
-
'surface-inset': surf.inset, // deepest pressed pit (#13100D, L*4.5 — sub-shell well)
|
|
296
|
+
// so it takes the ramp's shell role. The deepest plane is `background-frame`,
|
|
297
|
+
// which is also the `SurfaceLevel` floor a pressed surface clamps at.
|
|
298
|
+
'surface-base': greyRamp[925], // main surface (#252321, L*13.9)
|
|
299
|
+
'surface-elevated': greyRamp[900], // elevated surface (#2C2A28, L*17.2 — nav/rail)
|
|
300
|
+
'surface-raised': greyRamp[875], // raised surface (#31302F, L*19.9 — cards)
|
|
301
|
+
'surface-overlay': greyRamp[850], // overlay surface (#373635, L*22.7 — hero/popover)
|
|
302
|
+
'surface-input': greyRamp[900], // input surface (#2C2A28 — one plane above base)
|
|
287
303
|
|
|
288
304
|
// Background colors — same ramp, the frame/shell end of it.
|
|
289
|
-
'background-base':
|
|
290
|
-
'background-default':
|
|
291
|
-
'background-subtle':
|
|
292
|
-
// Frame/bezel chrome
|
|
293
|
-
// `background-base
|
|
294
|
-
// ramp
|
|
295
|
-
//
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
//
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
//
|
|
302
|
-
//
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
305
|
+
'background-base': greyRamp[950], // shell (#1C1916, L*9 — Surface level="background")
|
|
306
|
+
'background-default': greyRamp[925], // main background (#252321, L*13.9 — matches surface-base)
|
|
307
|
+
'background-subtle': greyRamp[900], // subtle background (#2C2A28, L*17.2 — matches surface-elevated)
|
|
308
|
+
// Frame/bezel chrome — the top bar + side nav shell, one step BELOW
|
|
309
|
+
// `background-base`. It used to be described as sitting OUTSIDE the ramp; it
|
|
310
|
+
// is simply the ramp's last step now, and the floor `<Surface pressed>` clamps at.
|
|
311
|
+
'background-frame': greyRamp[975], // frame / bezel (#100D0A, L*3.8)
|
|
312
|
+
|
|
313
|
+
// Border colors — solid dark borders are RETIRED (TD-07.14). Not a preference — a structural
|
|
314
|
+
// consequence of one ramp. Every step from 850 to 975 is now a surface plane,
|
|
315
|
+
// so a solid border dark enough to read as a border necessarily equals some
|
|
316
|
+
// plane's fill, which is exactly the collision `surface.contract.test.ts` R2
|
|
317
|
+
// forbids. The only non-plane steps left in that band are 800 and 700, and
|
|
318
|
+
// borders there jump from L*~10-25 to L*~28-38 — a restyle, not a migration.
|
|
319
|
+
//
|
|
320
|
+
// So separation moves to the alpha hairlines, which dark mode already called
|
|
321
|
+
// "the primary separation cue". Being alpha, they composite against whatever
|
|
322
|
+
// plane they land on and cannot collide with any of them — the problem stops
|
|
323
|
+
// existing rather than being re-solved per plane. The three solid tokens were
|
|
324
|
+
// deleted outright; consumers point at the `hairline-*` family below.
|
|
325
|
+
//
|
|
326
|
+
// `border-prominent` stays SOLID: it is the one border meant to be seen
|
|
327
|
+
// outright (4 call sites, high-visibility dividers), and grey-800 is not a
|
|
328
|
+
// plane, so it keeps its job without collision.
|
|
329
|
+
'border-prominent': greyRamp[800], // high-visibility divider
|
|
307
330
|
'border-focus': '#828DF8',
|
|
308
|
-
'border-input':
|
|
309
|
-
'border-input-hover':
|
|
331
|
+
'border-input': greyRamp[700],
|
|
332
|
+
'border-input-hover': greyRamp[600],
|
|
310
333
|
'border-input-focus': '#828DF8',
|
|
311
|
-
'border-input-error':
|
|
334
|
+
'border-input-error': ramp.red[500],
|
|
312
335
|
|
|
313
336
|
// Alpha hairline separators — the primary separation cue (§4/S-2). Self-
|
|
314
337
|
// normalizing: composites toward white by ~the same amount on ANY plane, so
|
|
315
338
|
// one family works at every elevation instead of per-surface border tokens.
|
|
316
339
|
// Shadows are demoted to floating-overlay use only (not shipped as a fill
|
|
317
340
|
// separator here — see elevation.ts, follow-up S-4).
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
341
|
+
//
|
|
342
|
+
// RETUNED on the wall (VW-99, S-6). The original .06/.09/.14 were set at a
|
|
343
|
+
// desk. On the panel at ~3 m `subtle` rendered but was indiscernible, and
|
|
344
|
+
// `default` — the load-bearing one, with no solid-border fallback since
|
|
345
|
+
// TD-07.14 — went weak on the lightest plane. `strong` read cleanly with room
|
|
346
|
+
// to spare, which is what made raising the whole family viable.
|
|
347
|
+
//
|
|
348
|
+
// Each tier steps up onto the next MEASURED-GOOD rung rather than an
|
|
349
|
+
// interpolated one: the new `subtle` is the old `default`, and the new
|
|
350
|
+
// `default` is the old `strong` — a value just confirmed legible on all four
|
|
351
|
+
// planes. Spacing goes 1 : 1.56 : 2.33, against the original 1 : 1.5 : 2.33.
|
|
352
|
+
//
|
|
353
|
+
// RUN 2 (VW-99): all three tiers were seen on all four planes — the retune
|
|
354
|
+
// worked — but `subtle` was still "a bit hard to read", so the whole family
|
|
355
|
+
// took a further +.01. That is a comfort margin on a passing row, not a fix
|
|
356
|
+
// for a failing one, which is why it is a flat offset: the 1 : 1.56 : 2.33
|
|
357
|
+
// spacing that the run validated is preserved rather than re-derived.
|
|
358
|
+
//
|
|
359
|
+
// Keep these to TWO decimals. react-native-web quantises alpha to 8 bits, so
|
|
360
|
+
// a 3-decimal value silently rounds on the way to the DOM (.135 renders as
|
|
361
|
+
// .13) — the token would say one thing and paint another.
|
|
362
|
+
//
|
|
363
|
+
// DARK ONLY. The light family above composites toward BLACK on light planes,
|
|
364
|
+
// which this run says nothing about — do not mirror these numbers into it
|
|
365
|
+
// without its own verification.
|
|
366
|
+
'hairline-subtle': 'rgba(255, 255, 255, 0.10)',
|
|
367
|
+
'hairline-default': 'rgba(255, 255, 255, 0.15)',
|
|
368
|
+
'hairline-strong': 'rgba(255, 255, 255, 0.22)',
|
|
321
369
|
|
|
322
370
|
// Interactive states
|
|
323
371
|
'interactive-hover': 'rgba(255, 255, 255, 0.04)',
|
|
@@ -328,10 +376,10 @@ export const semanticColorsDark = {
|
|
|
328
376
|
'interactive-disabled-text': 'rgba(255, 255, 255, 0.26)',
|
|
329
377
|
|
|
330
378
|
// Divider
|
|
331
|
-
'divider':
|
|
379
|
+
'divider': 'rgba(255, 255, 255, 0.09)',
|
|
332
380
|
|
|
333
381
|
// Avatar default
|
|
334
|
-
'avatar-background':
|
|
382
|
+
'avatar-background': greyRamp[700],
|
|
335
383
|
'avatar-text': p.white,
|
|
336
384
|
} as const
|
|
337
385
|
|
package/src/utils/colors.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* color math for more perceptually consistent results across hues.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
export { lighten, darken } from '../theme/
|
|
8
|
+
export { lighten, darken } from '../theme/color-utils'
|
|
9
9
|
|
|
10
10
|
export type StatusType = 'success' | 'error' | 'warning' | 'info'
|
|
11
11
|
export type ResultType = 'improve' | 'degrade' | 'inconclusive' | 'neutral'
|
package/tailwind.config.js
CHANGED
|
@@ -119,7 +119,6 @@ module.exports = {
|
|
|
119
119
|
raised: 'var(--color-surface-raised)',
|
|
120
120
|
overlay: 'var(--color-surface-overlay)',
|
|
121
121
|
input: 'var(--color-surface-input)',
|
|
122
|
-
inset: 'var(--color-surface-inset)',
|
|
123
122
|
},
|
|
124
123
|
// Background colors
|
|
125
124
|
background: {
|
|
@@ -140,11 +139,9 @@ module.exports = {
|
|
|
140
139
|
hover: 'var(--color-text-link-hover)',
|
|
141
140
|
},
|
|
142
141
|
},
|
|
143
|
-
// Border colors
|
|
142
|
+
// Border colors — DEFAULT/subtle/strong are RETIRED (TD-07.14); use the
|
|
143
|
+
// `hairline` family below for separation.
|
|
144
144
|
border: {
|
|
145
|
-
DEFAULT: 'var(--color-border-default)',
|
|
146
|
-
subtle: 'var(--color-border-subtle)',
|
|
147
|
-
strong: 'var(--color-border-strong)',
|
|
148
145
|
prominent: 'var(--color-border-prominent)',
|
|
149
146
|
focus: 'var(--color-border-focus)',
|
|
150
147
|
input: {
|
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Set modalities — every per-rep strength set-type rendered by the REAL
|
|
3
|
-
* {@link VelocityStrip}. One strip model for every set type: it defaults to the
|
|
4
|
-
* target rep count as grey slots, overshoot grows the strip, and open-ended sets
|
|
5
|
-
* (AMRAP · the myo tail) carry a persistent cyan "continue" slot. Each card
|
|
6
|
-
* carries a `Collapse` accordion showing how to configure the `set` prop for that
|
|
7
|
-
* view. Promoted from the Lab/Explorations "Set Modalities" specimen once the
|
|
8
|
-
* vocabulary hardened into VelocityStrip's `set` descriptor.
|
|
9
|
-
*/
|
|
10
|
-
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
11
|
-
import { View, Text } from 'react-native'
|
|
12
|
-
import { VelocityStrip, type VelocitySet } from './VelocityStrip'
|
|
13
|
-
import { Collapse, CollapseButton, CollapseContent } from '../../ui/collapse/Collapse'
|
|
14
|
-
import { SET_STRIP_VARIABLE_COLOR as CYAN } from './SetStrip'
|
|
15
|
-
import {
|
|
16
|
-
GREY,
|
|
17
|
-
velColor,
|
|
18
|
-
T_PRIMARY,
|
|
19
|
-
T_SECONDARY,
|
|
20
|
-
T_TERTIARY,
|
|
21
|
-
INSET,
|
|
22
|
-
BORDER_SUBTLE,
|
|
23
|
-
Page,
|
|
24
|
-
} from './setHeadingKit'
|
|
25
|
-
|
|
26
|
-
const INTER = 'Inter, sans-serif'
|
|
27
|
-
const VAR_EDGE = '#22465F' // cyan-800 — the continue-slot outline
|
|
28
|
-
const CYAN_TEXT = '#22D3EE'
|
|
29
|
-
|
|
30
|
-
/** Pretty-print a value as a compact TS literal (single-line arrays). */
|
|
31
|
-
function formatValue(value: unknown): string {
|
|
32
|
-
if (Array.isArray(value)) {
|
|
33
|
-
if (value.length > 0 && Array.isArray(value[0])) {
|
|
34
|
-
return `[${value.map((v) => formatValue(v)).join(', ')}]`
|
|
35
|
-
}
|
|
36
|
-
return `[${(value as number[]).join(', ')}]`
|
|
37
|
-
}
|
|
38
|
-
return typeof value === 'string' ? `'${value}'` : String(value)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** The exact `set` prop, pretty-printed as a TS object for the accordion. */
|
|
42
|
-
function formatSet(set: VelocitySet): string {
|
|
43
|
-
const body = Object.entries(set)
|
|
44
|
-
.map(([key, value]) => ` ${key}: ${formatValue(value)},`)
|
|
45
|
-
.join('\n')
|
|
46
|
-
return `set={{\n${body}\n}}`
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
type Modality = {
|
|
50
|
-
name: string
|
|
51
|
-
alias: string
|
|
52
|
-
tags: string[]
|
|
53
|
-
def: string
|
|
54
|
-
useWhen: string
|
|
55
|
-
set: VelocitySet
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Representative velocities per colour bucket, matching the exploration's VEL map
|
|
59
|
-
// (g/a/o/r → the real velColor() ramp: fastest green → slowest red).
|
|
60
|
-
const G = 1.1
|
|
61
|
-
const A = 0.85
|
|
62
|
-
const O = 0.6
|
|
63
|
-
const R = 0.4
|
|
64
|
-
|
|
65
|
-
const MODS: Modality[] = [
|
|
66
|
-
{
|
|
67
|
-
name: 'Straight set',
|
|
68
|
-
alias: '3 × 10',
|
|
69
|
-
tags: ['fixed reps'],
|
|
70
|
-
def: 'A fixed number of reps at a fixed load — the baseline every other type varies from. Done reps colour; the remainder to the planned count sits grey.',
|
|
71
|
-
useWhen: 'A prescribed rep count at a fixed load — the default set.',
|
|
72
|
-
set: { type: 'straight', velocities: [G, A, A, O, O, R], planned: 10 },
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
name: 'Rep-range',
|
|
76
|
-
alias: '8–12',
|
|
77
|
-
tags: ['bounded reps'],
|
|
78
|
-
def: 'A floor and a ceiling — committed reps to the floor (grey todo), then a variable window (cyan) to the ceiling. A done variable rep is a real rep and colours normally.',
|
|
79
|
-
useWhen: 'A hypertrophy set prescribed as a range (e.g. 8–12) rather than a fixed count.',
|
|
80
|
-
set: { type: 'range', velocities: [G, A, A, O, O, O, R, R, A], floor: 8, max: 12 },
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
name: 'AMRAP',
|
|
84
|
-
alias: 'as many reps as possible',
|
|
85
|
-
tags: ['open-ended'],
|
|
86
|
-
def: 'One set, no cap — the target is a floor to beat. Velocity decay is the story; the trailing cyan slot never closes.',
|
|
87
|
-
useWhen: 'A final "leave nothing" set, or a rep-out test where the count is the outcome.',
|
|
88
|
-
set: { type: 'amrap', velocities: [G, G, A, A, O, O, O, R, R, R], target: 8 },
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
name: 'Drop set',
|
|
92
|
-
alias: 'strip / dropset',
|
|
93
|
-
tags: ['load drops', 'open-ended'],
|
|
94
|
-
def: 'Reps to failure at a load, then DROP the load (no rest) and continue — 1–3 drops. Sub-loads split by a WIDE notch gap.',
|
|
95
|
-
useWhen: 'Extending a set past failure by shedding load — the notch marks each weight drop.',
|
|
96
|
-
set: { type: 'drop', subloads: [[G, A, O, O, R], [A, O, R], [O, R]] },
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
name: 'Myo-reps',
|
|
100
|
-
alias: 'rest-pause',
|
|
101
|
-
tags: ['clusters', 'open-ended'],
|
|
102
|
-
def: 'Activation set to near-failure, then rest-pause mini-clusters at the SAME load until a cluster fails. Open tail → cyan continue.',
|
|
103
|
-
useWhen: 'High-density hypertrophy: one activation set milked for extra clusters.',
|
|
104
|
-
set: { type: 'myo', activation: [G, A, A, O, O, R], clusters: [[A, O, R], [O, R]], open: true },
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
name: 'Cluster set',
|
|
108
|
-
alias: 'intra-set rest',
|
|
109
|
-
tags: ['fixed reps', 'intra-rest'],
|
|
110
|
-
def: 'A fixed rep count at a HIGH load, broken by short intra-set rests so every rep stays fast — power, not failure. Fixed count → no cyan tail.',
|
|
111
|
-
useWhen: 'Strength/power work where each rep must stay fast; rests are planned, count is fixed.',
|
|
112
|
-
set: { type: 'cluster', velocities: [G, G, G, A, G, G, A, G], groupSize: 2, planned: 10 },
|
|
113
|
-
},
|
|
114
|
-
]
|
|
115
|
-
|
|
116
|
-
function tagStyle(open: boolean) {
|
|
117
|
-
return {
|
|
118
|
-
fontFamily: INTER,
|
|
119
|
-
fontSize: 10,
|
|
120
|
-
fontWeight: '600' as const,
|
|
121
|
-
letterSpacing: 0.5,
|
|
122
|
-
textTransform: 'uppercase' as const,
|
|
123
|
-
color: open ? CYAN_TEXT : T_TERTIARY,
|
|
124
|
-
borderWidth: 1,
|
|
125
|
-
borderColor: open ? VAR_EDGE : '#2A2A2E',
|
|
126
|
-
borderRadius: 999,
|
|
127
|
-
paddingVertical: 3,
|
|
128
|
-
paddingHorizontal: 8,
|
|
129
|
-
overflow: 'hidden' as const,
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function ConfigAccordion({ set, useWhen }: { set: VelocitySet; useWhen: string }) {
|
|
134
|
-
return (
|
|
135
|
-
<Collapse>
|
|
136
|
-
<CollapseButton className="px-0 py-2 rounded-none">
|
|
137
|
-
<Text style={{ fontFamily: INTER, fontSize: 11, fontWeight: '700', letterSpacing: 0.4, color: CYAN_TEXT }}>
|
|
138
|
-
How to configure this view
|
|
139
|
-
</Text>
|
|
140
|
-
</CollapseButton>
|
|
141
|
-
<CollapseContent className="px-0">
|
|
142
|
-
<View style={{ backgroundColor: '#0A0A0A', borderWidth: 1, borderColor: '#2A2A2E', borderRadius: 8, padding: 12, gap: 8 }}>
|
|
143
|
-
<Text style={{ fontFamily: 'monospace', fontSize: 11, lineHeight: 17, color: T_SECONDARY }}>
|
|
144
|
-
{`<VelocityStrip\n variant="mini"\n ${formatSet(set).replace(/\n/g, '\n ')}\n/>`}
|
|
145
|
-
</Text>
|
|
146
|
-
<Text style={{ fontFamily: INTER, fontSize: 12, color: T_TERTIARY, lineHeight: 17 }}>
|
|
147
|
-
Use when: {useWhen}
|
|
148
|
-
</Text>
|
|
149
|
-
</View>
|
|
150
|
-
</CollapseContent>
|
|
151
|
-
</Collapse>
|
|
152
|
-
)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function Card({ m }: { m: Modality }) {
|
|
156
|
-
return (
|
|
157
|
-
<View style={{ backgroundColor: INSET, borderWidth: 1, borderColor: '#2A2A2E', borderRadius: 12, padding: 18, gap: 12, width: 460 }}>
|
|
158
|
-
<View style={{ flexDirection: 'row', alignItems: 'baseline', justifyContent: 'space-between', flexWrap: 'wrap', gap: 8 }}>
|
|
159
|
-
<Text style={{ fontFamily: INTER, fontSize: 16, fontWeight: '700', color: T_PRIMARY }}>
|
|
160
|
-
{m.name} <Text style={{ fontSize: 12, fontWeight: '500', color: T_TERTIARY }}>{m.alias}</Text>
|
|
161
|
-
</Text>
|
|
162
|
-
<View style={{ flexDirection: 'row', gap: 6 }}>
|
|
163
|
-
{m.tags.map((t) => {
|
|
164
|
-
const open = /open-ended/.test(t)
|
|
165
|
-
return (
|
|
166
|
-
<Text key={t} style={tagStyle(open)}>
|
|
167
|
-
{t}
|
|
168
|
-
</Text>
|
|
169
|
-
)
|
|
170
|
-
})}
|
|
171
|
-
</View>
|
|
172
|
-
</View>
|
|
173
|
-
<Text style={{ fontFamily: INTER, fontSize: 13, color: T_SECONDARY, lineHeight: 19 }}>{m.def}</Text>
|
|
174
|
-
<View style={{ paddingVertical: 6 }}>
|
|
175
|
-
<VelocityStrip variant="mini" set={m.set} />
|
|
176
|
-
</View>
|
|
177
|
-
<View style={{ height: 1, backgroundColor: BORDER_SUBTLE }} />
|
|
178
|
-
<ConfigAccordion set={m.set} useWhen={m.useWhen} />
|
|
179
|
-
</View>
|
|
180
|
-
)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function Principle({ k, title, body }: { k: string; title: string; body: string }) {
|
|
184
|
-
return (
|
|
185
|
-
<View style={{ flex: 1, backgroundColor: INSET, borderWidth: 1, borderColor: '#2A2A2E', borderRadius: 10, padding: 16, gap: 7, minWidth: 220 }}>
|
|
186
|
-
<Text style={{ fontFamily: INTER, fontSize: 10, fontWeight: '600', letterSpacing: 1.2, textTransform: 'uppercase', color: CYAN_TEXT }}>{k}</Text>
|
|
187
|
-
<Text style={{ fontFamily: INTER, fontSize: 14, fontWeight: '600', color: T_PRIMARY }}>{title}</Text>
|
|
188
|
-
<Text style={{ fontFamily: INTER, fontSize: 12.5, color: T_SECONDARY, lineHeight: 18 }}>{body}</Text>
|
|
189
|
-
</View>
|
|
190
|
-
)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function Swatch({ color, label, outlined }: { color: string; label: string; outlined?: boolean }) {
|
|
194
|
-
return (
|
|
195
|
-
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 7 }}>
|
|
196
|
-
<View style={{ width: 22, height: 10, borderRadius: 2, backgroundColor: color, ...(outlined ? { borderWidth: 1, borderColor: VAR_EDGE } : {}) }} />
|
|
197
|
-
<Text style={{ fontFamily: INTER, fontSize: 12, color: T_SECONDARY }}>{label}</Text>
|
|
198
|
-
</View>
|
|
199
|
-
)
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const meta: Meta = {
|
|
203
|
-
title: 'Workout/DataViz/VelocityStrip/Set Modalities',
|
|
204
|
-
parameters: { layout: 'fullscreen' },
|
|
205
|
-
}
|
|
206
|
-
export default meta
|
|
207
|
-
type Story = StoryObj
|
|
208
|
-
|
|
209
|
-
export const StripVocabulary: Story = {
|
|
210
|
-
name: 'One strip vocabulary · every set type',
|
|
211
|
-
render: () => (
|
|
212
|
-
<Page
|
|
213
|
-
title="Set modalities — the VelocityStrip set-type vocabulary"
|
|
214
|
-
note="Every per-rep strength set-type, rendered by the real VelocityStrip via its `set` prop. One strip model: defaults to the target rep count (grey slots) · overshoot grows the strip · open-ended sets (AMRAP · the myo tail) carry a persistent cyan 'continue' slot. Real velocity colours + the cyan variable token. Open each card's accordion for the exact `set` configuration and a 'use when' note."
|
|
215
|
-
>
|
|
216
|
-
<View style={{ flexDirection: 'row', gap: 14, flexWrap: 'wrap', maxWidth: 940 }}>
|
|
217
|
-
<Principle k="default" title="Target reps as slots" body="A set opens as N grey placeholder slots — the prescribed count. Each done rep fills its slot with its velocity colour." />
|
|
218
|
-
<Principle k="overshoot" title="Extra reps grow it" body="Beat the prescription and the strip keeps adding slots. The plan is a floor to draw against, not a cap." />
|
|
219
|
-
<Principle k="open-ended" title="A cyan continue tail" body="AMRAP and the myo tail carry a persistent cyan slot; new reps insert before it — it argues 'there's more'." />
|
|
220
|
-
</View>
|
|
221
|
-
|
|
222
|
-
<View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 14, alignItems: 'center', marginTop: 4 }}>
|
|
223
|
-
<Swatch color={velColor(G)} label="fastest" />
|
|
224
|
-
<Swatch color={velColor(A)} label="fast" />
|
|
225
|
-
<Swatch color={velColor(O)} label="moderate" />
|
|
226
|
-
<Swatch color={velColor(R)} label="slow" />
|
|
227
|
-
<Swatch color={GREY} label="planned / to-do" />
|
|
228
|
-
<Swatch color={CYAN} label="variable · continue" outlined />
|
|
229
|
-
</View>
|
|
230
|
-
|
|
231
|
-
<View style={{ height: 1, backgroundColor: BORDER_SUBTLE, marginVertical: 4 }} />
|
|
232
|
-
|
|
233
|
-
<View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 16 }}>
|
|
234
|
-
{MODS.map((m) => (
|
|
235
|
-
<Card key={m.name} m={m} />
|
|
236
|
-
))}
|
|
237
|
-
</View>
|
|
238
|
-
</Page>
|
|
239
|
-
),
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
export const ActiveSetExpanded: Story = {
|
|
243
|
-
name: 'Straight set · expanded spotlight',
|
|
244
|
-
render: () => (
|
|
245
|
-
<Page
|
|
246
|
-
title="Straight set — the expanded active-set spotlight"
|
|
247
|
-
note="The expanded VelocityStrip for a live straight set: done reps as velocity-height bars, the planned remainder as short grey stubs, and the mean · loss summary derived from the done reps."
|
|
248
|
-
>
|
|
249
|
-
<View style={{ width: 520, gap: 10 }}>
|
|
250
|
-
<VelocityStrip set={{ type: 'straight', velocities: [1.12, 1.05, 0.98, 0.9, 0.82, 0.7], planned: 10 }} expanded />
|
|
251
|
-
</View>
|
|
252
|
-
</Page>
|
|
253
|
-
),
|
|
254
|
-
}
|