@titan-design/react-ui 0.11.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bodymap.js +173 -343
- package/dist/bodymap.js.map +1 -1
- package/dist/bodymap.mjs +173 -343
- package/dist/bodymap.mjs.map +1 -1
- package/dist/index.d.mts +27 -22
- package/dist/index.d.ts +27 -22
- package/dist/index.js +366 -561
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +360 -553
- package/dist/index.mjs.map +1 -1
- package/dist/pages.js +242 -407
- package/dist/pages.js.map +1 -1
- package/dist/pages.mjs +242 -407
- package/dist/pages.mjs.map +1 -1
- package/dist/theme/index.d.mts +250 -1157
- package/dist/theme/index.d.ts +250 -1157
- package/dist/theme/index.js +234 -408
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +227 -399
- package/dist/theme/index.mjs.map +1 -1
- package/dist/theme/tokens-css.js +141 -140
- package/dist/theme/tokens-css.js.map +1 -1
- package/dist/theme/tokens-css.mjs +141 -140
- package/dist/theme/tokens-css.mjs.map +1 -1
- package/dist/theme/tokens.d.mts +904 -0
- package/dist/theme/tokens.d.ts +904 -0
- package/dist/theme/tokens.js +856 -0
- package/dist/theme/tokens.js.map +1 -0
- package/dist/theme/tokens.mjs +833 -0
- package/dist/theme/tokens.mjs.map +1 -0
- package/package.json +7 -1
- package/src/arch/Architecture.stories.tsx +9 -9
- package/src/components/custom/DateTime/DateTime.stories.tsx +1 -1
- package/src/components/custom/Fatigue/DualGhostSpark.stories.tsx +3 -3
- package/src/components/custom/Fatigue/FatigueLights.stories.tsx +2 -2
- package/src/components/custom/Fatigue/GhostSpark.stories.tsx +3 -3
- package/src/components/custom/Fatigue/LiveFatigueCard.tsx +1 -1
- package/src/components/custom/Fatigue/LiveFatiguePanel.stories.tsx +2 -2
- package/src/components/custom/Fatigue/RomProgressionChart.stories.tsx +2 -2
- package/src/components/custom/Fatigue/SilverRedPalette.stories.tsx +4 -4
- package/src/components/custom/Fatigue/VelocityHero.stories.tsx +3 -3
- package/src/components/custom/Fatigue/VerdictHero.stories.tsx +2 -2
- package/src/components/custom/Fatigue/fatigue-tokens.ts +12 -12
- package/src/components/custom/Sidebar/Sidebar.tsx +4 -4
- package/src/components/custom/Table/Table.tsx +2 -2
- package/src/components/custom/Workout/ActiveWorkoutPage.tsx +3 -3
- package/src/components/custom/Workout/BaseBadge.test.tsx +6 -1
- package/src/components/custom/Workout/BaseBadge.tsx +1 -1
- package/src/components/custom/Workout/BodyMap.tsx +2 -2
- package/src/components/custom/Workout/BodyMapDetailPanel.tsx +2 -2
- package/src/components/custom/Workout/DeviationBar.test.tsx +2 -1
- package/src/components/custom/Workout/DeviationBar.tsx +3 -2
- package/src/components/custom/Workout/ExerciseCard.test.tsx +3 -3
- package/src/components/custom/Workout/ExerciseCard.tsx +2 -2
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +4 -4
- package/src/components/custom/Workout/InputBar.tsx +2 -2
- package/src/components/custom/Workout/LiveAuraFrame.stories.tsx +1 -1
- package/src/components/custom/Workout/LiveAuraFrame.test.tsx +1 -1
- package/src/components/custom/Workout/LiveAuraFrame.tsx +3 -3
- package/src/components/custom/Workout/MesoCard.tsx +3 -1
- package/src/components/custom/Workout/MuscleGroupChip.tsx +4 -3
- package/src/components/custom/Workout/PrHistoryModal.tsx +2 -2
- package/src/components/custom/Workout/ProgramPlanningPage.tsx +1 -1
- package/src/components/custom/Workout/README.md +1 -1
- package/src/components/custom/Workout/ReadinessCheck.tsx +2 -2
- package/src/components/custom/Workout/RestTimer.tsx +2 -2
- package/src/components/custom/Workout/S3SetTypes.stories.tsx +3 -3
- package/src/components/custom/Workout/SessionHeader.stories.tsx +2 -2
- package/src/components/custom/Workout/SessionHeader.tsx +1 -1
- package/src/components/custom/Workout/SessionRail.stories.tsx +2 -2
- package/src/components/custom/Workout/SessionRail.tsx +13 -5
- package/src/components/custom/Workout/SetBar.tsx +3 -3
- package/src/components/custom/Workout/SetRow.test.tsx +6 -3
- package/src/components/custom/Workout/StatusDot.tsx +5 -4
- package/src/components/custom/Workout/StrengthTrendChart.tsx +1 -1
- package/src/components/custom/Workout/TrainingStatusPage.tsx +2 -2
- package/src/components/custom/Workout/VelocityStrip.test.tsx +5 -2
- package/src/components/custom/Workout/VelocityStrip.tsx +4 -4
- package/src/components/custom/Workout/VolumeLandmarkBar.tsx +3 -3
- package/src/components/custom/Workout/WeightBadge.test.tsx +6 -1
- package/src/components/custom/Workout/WeightBadge.tsx +2 -1
- package/src/components/custom/Workout/WorkoutCard.tsx +3 -1
- package/src/components/custom/Workout/WorkoutPill.tsx +5 -2
- package/src/components/custom/Workout/ZoneTrack.tsx +4 -4
- package/src/components/custom/Workout/setHeadingKit.tsx +14 -8
- package/src/components/custom/Workout/velocity-story-kit.tsx +2 -2
- package/src/components/custom/charts/SetBarChart.tsx +3 -3
- package/src/components/custom/stepper/Stepper.stories.tsx +1 -1
- package/src/components/custom/stepper/Stepper.tsx +3 -3
- package/src/components/shell/DeviceMenu.tsx +2 -2
- package/src/components/shell/README.md +1 -1
- package/src/components/shell/SessionRailSpecimen.stories.tsx +1 -1
- package/src/components/shell/SideNav.tsx +1 -1
- package/src/components/shell/TopBar.tsx +1 -1
- package/src/components/ui/autocomplete/Autocomplete.stories.tsx +1 -1
- package/src/components/ui/autocomplete/Autocomplete.tsx +1 -1
- package/src/components/ui/avatar/Avatar.tsx +2 -2
- package/src/components/ui/badge/Badge.tsx +2 -2
- package/src/components/ui/card/Card.tsx +3 -3
- package/src/components/ui/checkbox/Checkbox.tsx +1 -1
- package/src/components/ui/collapse/Collapse.tsx +1 -1
- package/src/components/ui/data-row/DataRow.stories.tsx +1 -1
- package/src/components/ui/drawer/Drawer.tsx +3 -3
- package/src/components/ui/form-field/FormField.tsx +1 -1
- package/src/components/ui/help-tip/HelpTip.tsx +1 -1
- package/src/components/ui/menu/Menu.tsx +2 -2
- package/src/components/ui/pill/Pill.tsx +2 -2
- package/src/components/ui/popover/Popover.stories.tsx +2 -2
- package/src/components/ui/popover/Popover.tsx +1 -1
- package/src/components/ui/progress/Progress.tsx +3 -3
- package/src/components/ui/radio/Radio.stories.tsx +1 -1
- package/src/components/ui/radio/Radio.tsx +4 -4
- package/src/components/ui/select/Select.tsx +3 -3
- package/src/components/ui/surface/Surface.stories.tsx +3 -3
- package/src/components/ui/surface/Surface.test.tsx +32 -19
- package/src/components/ui/surface/Surface.tsx +3 -3
- package/src/components/ui/surface/SurfaceContext.ts +19 -29
- package/src/components/ui/surface/surface.contract.test.ts +58 -45
- package/src/components/ui/switch/Switch.tsx +1 -1
- package/src/components/ui/tabs/Tabs.tsx +2 -2
- package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +5 -5
- package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +1 -1
- package/src/components/ui/toolbar-button/ToolbarButton.tsx +36 -22
- package/src/stories/PresetShowcase.stories.tsx +21 -21
- package/src/theme/ColorPalettes.stories.tsx +41 -38
- package/src/theme/ColorPrimitives.stories.tsx +89 -41
- package/src/theme/Depth.stories.tsx +312 -0
- package/src/theme/DepthCalibration.stories.tsx +655 -0
- package/src/theme/ThemeProvider.tsx +1 -1
- package/src/theme/barrel.ts +7 -14
- package/src/theme/color-stories.coverage.test.ts +15 -20
- package/src/theme/color-story-kit.tsx +1 -1
- package/src/theme/color-utils.ts +197 -0
- package/src/theme/config.ts +0 -8
- package/src/theme/depth-calibration.test.tsx +97 -0
- package/src/theme/elevation.test.ts +62 -1
- package/src/theme/elevation.ts +34 -8
- package/src/theme/global.css +47 -53
- package/src/theme/grey-ramp.test.ts +166 -0
- package/src/theme/materials.test.ts +106 -0
- package/src/theme/materials.ts +211 -0
- package/src/theme/presets/audiobook.ts +4 -4
- package/src/theme/presets/types.ts +3 -3
- package/src/theme/semantic-contrast.test.ts +83 -0
- package/src/theme/token-export-guard.test.ts +104 -0
- package/src/theme/tokens/primitives.ts +63 -116
- package/src/theme/tokens/semantic.ts +130 -82
- package/src/utils/colors.ts +1 -1
- package/tailwind.config.js +2 -5
- package/src/theme/bar-paper.ts +0 -59
- package/src/theme/shadows.stories.tsx +0 -523
- package/src/theme/shadows.ts +0 -439
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type BaseBadgeProps,
|
|
9
9
|
} from './BaseBadge'
|
|
10
10
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
11
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
11
12
|
|
|
12
13
|
export type WeightBadgeSize = BaseBadgeSize
|
|
13
14
|
|
|
@@ -43,7 +44,7 @@ export function WeightBadge({
|
|
|
43
44
|
...props
|
|
44
45
|
}: WeightBadgeProps) {
|
|
45
46
|
const config = baseBadgeSizeConfig[size]
|
|
46
|
-
const textColor = isPr ? getSemanticColors('dark')['brand-primary'] :
|
|
47
|
+
const textColor = isPr ? getSemanticColors('dark')['brand-primary'] : greyRamp[400]
|
|
47
48
|
// Dumbbell reads clearly as a weight/strength metric and is unit-agnostic
|
|
48
49
|
// across estimated-1RM and N-rep-max contexts (vs. TrendingUp which implies
|
|
49
50
|
// change/trend rather than a load value).
|
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
} from './MuscleGroupChip'
|
|
9
9
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
10
10
|
|
|
11
|
+
const HAIRLINE_DEFAULT = getSemanticColors('dark')['hairline-default']
|
|
12
|
+
|
|
11
13
|
const t = getSemanticColors('dark')
|
|
12
14
|
|
|
13
15
|
export type WorkoutStatus = 'completed' | 'today' | 'upcoming'
|
|
@@ -48,7 +50,7 @@ export interface WorkoutCardProps extends ViewProps {
|
|
|
48
50
|
const COLORS = {
|
|
49
51
|
statusSuccess: t['status-success'],
|
|
50
52
|
brandPrimary: t['brand-primary'],
|
|
51
|
-
borderDefault:
|
|
53
|
+
borderDefault: HAIRLINE_DEFAULT,
|
|
52
54
|
brandPrimarySubtle: 'rgba(255,121,0,0.06)',
|
|
53
55
|
}
|
|
54
56
|
|
|
@@ -3,6 +3,9 @@ import { useEffect, useState } from 'react'
|
|
|
3
3
|
import { View, Text, Pressable, Animated, Easing, type ViewProps } from 'react-native'
|
|
4
4
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
5
5
|
import { WORKOUT_PILL_DELOAD } from '../../../theme/extracted-colors-dataviz'
|
|
6
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
7
|
+
|
|
8
|
+
const HAIRLINE_DEFAULT = getSemanticColors('dark')['hairline-default']
|
|
6
9
|
|
|
7
10
|
const t = getSemanticColors('dark')
|
|
8
11
|
|
|
@@ -35,7 +38,7 @@ const statusBorderStyles: Record<WorkoutPillStatus, Record<string, unknown>> = {
|
|
|
35
38
|
completed: { borderWidth: 1, borderColor: 'rgba(46,213,115,0.3)' },
|
|
36
39
|
current: { borderWidth: 1, borderColor: 'rgba(255,121,0,0.3)' },
|
|
37
40
|
next: { borderWidth: 1, borderColor: 'rgba(255,121,0,0.4)' },
|
|
38
|
-
upcoming: { borderWidth: 1, borderColor:
|
|
41
|
+
upcoming: { borderWidth: 1, borderColor: HAIRLINE_DEFAULT },
|
|
39
42
|
missed: { borderWidth: 1, borderColor: 'rgba(209,67,67,0.25)' },
|
|
40
43
|
deload: { borderWidth: 1, borderColor: 'rgba(186,41,150,0.3)' },
|
|
41
44
|
}
|
|
@@ -44,7 +47,7 @@ const textColors: Record<WorkoutPillStatus, string> = {
|
|
|
44
47
|
completed: t['status-success'],
|
|
45
48
|
current: t['brand-primary'],
|
|
46
49
|
next: t['brand-primary'],
|
|
47
|
-
upcoming:
|
|
50
|
+
upcoming: greyRamp[500],
|
|
48
51
|
missed: 'rgba(209,67,67,0.7)',
|
|
49
52
|
deload: WORKOUT_PILL_DELOAD,
|
|
50
53
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { useState } from 'react'
|
|
3
3
|
import { View, Text, type ViewProps, type DimensionValue, type LayoutChangeEvent } from 'react-native'
|
|
4
4
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
5
|
-
import { primitiveColors } from '../../../theme/tokens/primitives'
|
|
5
|
+
import { primitiveColors, greyRamp } from '../../../theme/tokens/primitives'
|
|
6
6
|
import { alpha } from '../../../utils/colors'
|
|
7
7
|
import { Tooltip } from '../../ui/tooltip/Tooltip'
|
|
8
8
|
|
|
@@ -13,8 +13,8 @@ function glowShadow(color: string): string {
|
|
|
13
13
|
return `0 0 5px 1px ${alpha(color, 0.3)}, 0 0 10px 3px ${alpha(color, 0.12)}`
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
/** Muted, un-reached track colour — a
|
|
17
|
-
const DEFAULT_TRACK_COLOR =
|
|
16
|
+
/** Muted, un-reached track colour — a grey step, matches the IntensityBar track family. */
|
|
17
|
+
const DEFAULT_TRACK_COLOR = greyRamp[800]
|
|
18
18
|
/** Default needle / fill-marker colour. */
|
|
19
19
|
const DEFAULT_MARKER_COLOR = primitiveColors.white
|
|
20
20
|
/** Tick mark + tick label colour. */
|
|
@@ -139,7 +139,7 @@ export interface ZoneTrackProps extends ViewProps {
|
|
|
139
139
|
trackHeight?: number
|
|
140
140
|
/** How far a needle marker overhangs the track top + bottom, in px. Overrides the `size` default (6 default / 9 wall). */
|
|
141
141
|
needleOverhang?: number
|
|
142
|
-
/** Muted colour of the track behind / beyond the fill. Default a
|
|
142
|
+
/** Muted colour of the track behind / beyond the fill. Default a grey step. */
|
|
143
143
|
trackColor?: string
|
|
144
144
|
className?: string
|
|
145
145
|
}
|
|
@@ -5,19 +5,25 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { ReactNode } from 'react'
|
|
7
7
|
import { TempoDisplay } from './TempoDisplay'
|
|
8
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
9
|
+
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
8
10
|
|
|
9
11
|
// ---- geometry
|
|
10
12
|
export const RAIL_W = 232
|
|
11
13
|
export const SET_GAP = 5
|
|
12
14
|
|
|
13
|
-
// ---- surface + text tokens (dark theme
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export const
|
|
19
|
-
export const
|
|
20
|
-
export const
|
|
15
|
+
// ---- surface + text tokens (dark theme)
|
|
16
|
+
// These were hand-copied hexes labelled with their grey/neutral steps, which
|
|
17
|
+
// is exactly how they silently stopped matching the ramps they named. They read
|
|
18
|
+
// from the tokens now, so a retune reaches them.
|
|
19
|
+
const T = getSemanticColors('dark')
|
|
20
|
+
export const GREY = greyRamp[900] // upcoming/placeholder
|
|
21
|
+
export const BORDER_SUBTLE = greyRamp[950] // row dividers
|
|
22
|
+
export const T_PRIMARY = T['text-primary']
|
|
23
|
+
export const T_SECONDARY = T['text-secondary']
|
|
24
|
+
export const T_TERTIARY = T['text-tertiary']
|
|
25
|
+
export const RAISED = greyRamp[925] // nav + heading raised plane
|
|
26
|
+
export const INSET = greyRamp[975] // sunk exercise-list column
|
|
21
27
|
// depth lives on the sunk list: inner shadow from the heading (top) + nav (left). Raised plane is flat.
|
|
22
28
|
export const INSET_SHADOW =
|
|
23
29
|
'inset 0 9px 12px -6px rgba(0,0,0,0.85), inset 7px 0 10px -6px rgba(0,0,0,0.7), inset 0 -1px 0 rgba(255,255,255,0.02)'
|
|
@@ -20,13 +20,13 @@ import type { ReactNode } from 'react'
|
|
|
20
20
|
import type { Decorator } from '@storybook/react-vite'
|
|
21
21
|
import { View, Text } from 'react-native'
|
|
22
22
|
import { VelocityStrip, DualVelocityStrip, type VelocitySet } from './VelocityStrip'
|
|
23
|
-
import {
|
|
23
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
24
24
|
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
25
25
|
|
|
26
26
|
const t = getSemanticColors('dark')
|
|
27
27
|
|
|
28
28
|
/** The real dark surface-background plane — stories sit on the token, not an ad-hoc grey. */
|
|
29
|
-
export const SURFACE_BG =
|
|
29
|
+
export const SURFACE_BG = greyRamp[950]
|
|
30
30
|
|
|
31
31
|
export const VIEW_LABEL = t['text-secondary']
|
|
32
32
|
export const VIEW_NOTE = t['text-tertiary']
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
type LayoutChangeEvent,
|
|
21
21
|
} from 'react-native'
|
|
22
22
|
import { primitiveRamps } from '../../../theme/tokens/primitives'
|
|
23
|
-
import { barPaper } from '../../../theme/
|
|
23
|
+
import { barPaper } from '../../../theme/materials'
|
|
24
24
|
import { useOnSurfaceColor, useSurface, surfaceBackground } from '../../ui/surface/SurfaceContext'
|
|
25
25
|
import { useLiveRepGrowth } from './live-rep-growth'
|
|
26
26
|
|
|
@@ -313,7 +313,7 @@ export function SetBarChart({
|
|
|
313
313
|
const flipStyle = flip ? ({ transform: [{ scaleY: -1 as number }] } as const) : null
|
|
314
314
|
|
|
315
315
|
// Planned/to-do reps + the baseline draw in a SURFACE-relative neutral (on-surface tertiary)
|
|
316
|
-
// so they stay legible on every plane instead of a fixed
|
|
316
|
+
// so they stay legible on every plane instead of a fixed grey.
|
|
317
317
|
const placeholderColor = useOnSurfaceColor('tertiary')
|
|
318
318
|
// The expanded strip's solid to-do tone: the surface plane blended toward the neutral (the same
|
|
319
319
|
// relative model), so a `todoVariant="solid"` section holds ~constant contrast on every plane.
|
|
@@ -590,7 +590,7 @@ export interface SideRailSection {
|
|
|
590
590
|
/**
|
|
591
591
|
* The ONE shared gutter/side-rail lockup used by BOTH the single hero and the diverging dual, so a
|
|
592
592
|
* single chart is pixel-identical to the dual's upper half — going dual only adds a lower section.
|
|
593
|
-
* A fixed-width gutter (34px hero / 18px rail) with the `border-
|
|
593
|
+
* A fixed-width gutter (34px hero / 18px rail) with the `border-hairline` right hairline (the vertical
|
|
594
594
|
* axis) and one rotated, height-responsive, initials-collapsing label per section (each `sectionExtent`
|
|
595
595
|
* tall). The single passes ONE section; the dual passes two (up + down wings). Rendered null when no
|
|
596
596
|
* section carries a label. The rotate lives on a wrapper sized to `sectionExtent` so `numberOfLines`
|
|
@@ -137,7 +137,7 @@ export const VerticalWithContent: Story = {
|
|
|
137
137
|
<Text className="text-text-secondary text-sm">
|
|
138
138
|
Fill in your profile details to continue.
|
|
139
139
|
</Text>
|
|
140
|
-
<View className="bg-surface-input border border-
|
|
140
|
+
<View className="bg-surface-input border border-hairline rounded-md px-3 py-2">
|
|
141
141
|
<Text className="text-text-secondary text-sm">Display Name</Text>
|
|
142
142
|
</View>
|
|
143
143
|
</View>
|
|
@@ -91,7 +91,7 @@ function StepConnector({ index }: StepConnectorProps) {
|
|
|
91
91
|
<View
|
|
92
92
|
className={cn(
|
|
93
93
|
'flex-1 h-0.5 mx-2',
|
|
94
|
-
isCompleted ? 'bg-brand-primary' : 'bg-
|
|
94
|
+
isCompleted ? 'bg-brand-primary' : 'bg-hairline'
|
|
95
95
|
)}
|
|
96
96
|
/>
|
|
97
97
|
)
|
|
@@ -101,7 +101,7 @@ function StepConnector({ index }: StepConnectorProps) {
|
|
|
101
101
|
<View
|
|
102
102
|
className={cn(
|
|
103
103
|
'w-0.5 h-8 ml-4 my-1',
|
|
104
|
-
isCompleted ? 'bg-brand-primary' : 'bg-
|
|
104
|
+
isCompleted ? 'bg-brand-primary' : 'bg-hairline'
|
|
105
105
|
)}
|
|
106
106
|
/>
|
|
107
107
|
)
|
|
@@ -171,7 +171,7 @@ export function StepIndicator({
|
|
|
171
171
|
const statusStyles: Record<StepStatus, string> = {
|
|
172
172
|
completed: 'bg-brand-primary border-brand-primary',
|
|
173
173
|
active: 'bg-brand-primary border-brand-primary',
|
|
174
|
-
upcoming: 'bg-surface-base border-
|
|
174
|
+
upcoming: 'bg-surface-base border-hairline-strong',
|
|
175
175
|
error: 'bg-status-error border-status-error',
|
|
176
176
|
}
|
|
177
177
|
|
|
@@ -43,7 +43,7 @@ export function DeviceMenu({
|
|
|
43
43
|
<PopoverTrigger>
|
|
44
44
|
<DeviceIndicator status={status} />
|
|
45
45
|
</PopoverTrigger>
|
|
46
|
-
<PopoverContent className="right-0 left-auto mt-[10px] w-[326px] bg-surface-elevated border border-
|
|
46
|
+
<PopoverContent className="right-0 left-auto mt-[10px] w-[326px] bg-surface-elevated border border-hairline-strong rounded-[10px] p-[7px]">
|
|
47
47
|
<Typography
|
|
48
48
|
variant="monoLabel"
|
|
49
49
|
color="tertiary"
|
|
@@ -52,7 +52,7 @@ export function DeviceMenu({
|
|
|
52
52
|
Devices — {boundCount} bound · {availCount} available
|
|
53
53
|
</Typography>
|
|
54
54
|
{devices.map((device, i) => (
|
|
55
|
-
<View key={device.id} className={cn(i > 0 && 'border-t border-
|
|
55
|
+
<View key={device.id} className={cn(i > 0 && 'border-t border-hairline')}>
|
|
56
56
|
<DeviceRow
|
|
57
57
|
device={device}
|
|
58
58
|
onPress={onSelectDevice ? () => onSelectDevice(device) : undefined}
|
|
@@ -19,7 +19,7 @@ SideNav ...................... organism — the 60px left rail (Shell/SideNav)
|
|
|
19
19
|
└─ (accent bar) .............. bg-brand-primary edge bar on the active item
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
Decisions locked 2026-07-08 (specimen `
|
|
22
|
+
Decisions locked 2026-07-08 (specimen `sources/design/shell/S2-sidenav/`): lucide glyphs
|
|
23
23
|
(activity · history · layers · figure) · **Plan** label (narrower than "Program") · active = **left accent
|
|
24
24
|
bar** (short, centered on the icon+label) · **60px** icon+micro-label · live cue = **muted-green label**
|
|
25
25
|
(`status-live-muted`) on the Live item while a set runs off-Live · four items, no footer, no top-nav. Fixed
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* S3 · Session Rail — SPECIMEN (rapid-prototype, not yet a hardened component).
|
|
3
3
|
*
|
|
4
4
|
* A faithful React port of the R2/C3 converged rail
|
|
5
|
-
* (
|
|
5
|
+
* (sources/design/drilldown-pass/r2-synthesis/gallery.html), brought into
|
|
6
6
|
* titan Storybook so we can iterate with the token system + real titan components + side-by-side.
|
|
7
7
|
* Raw hex lives here on purpose — it mirrors the gallery 1:1; tokenization happens at harden.
|
|
8
8
|
*
|
|
@@ -55,7 +55,7 @@ export function SideNav({
|
|
|
55
55
|
<View
|
|
56
56
|
accessibilityRole="tablist"
|
|
57
57
|
className={cn(
|
|
58
|
-
'w-[60px] items-center gap-[6px] border-r border-
|
|
58
|
+
'w-[60px] items-center gap-[6px] border-r border-hairline bg-background-base py-3',
|
|
59
59
|
className
|
|
60
60
|
)}
|
|
61
61
|
>
|
|
@@ -58,7 +58,7 @@ export function TopBar({
|
|
|
58
58
|
// shared chrome gradient (web); solid bg-surface-elevated is the native fallback
|
|
59
59
|
style={surfaceGradient.chrome() as object}
|
|
60
60
|
className={cn(
|
|
61
|
-
'h-[46px] flex-row items-center gap-[14px] px-4 bg-surface-elevated border-b border-
|
|
61
|
+
'h-[46px] flex-row items-center gap-[14px] px-4 bg-surface-elevated border-b border-hairline',
|
|
62
62
|
className
|
|
63
63
|
)}
|
|
64
64
|
>
|
|
@@ -287,7 +287,7 @@ export const FormExample: Story = {
|
|
|
287
287
|
isRequired
|
|
288
288
|
/>
|
|
289
289
|
|
|
290
|
-
<View className="pt-2 border-t border-
|
|
290
|
+
<View className="pt-2 border-t border-hairline">
|
|
291
291
|
<Text className="text-sm text-text-secondary">
|
|
292
292
|
Selected: {country ? `${countryOptions.find(c => c.value === country)?.label}` : '-'}
|
|
293
293
|
{city ? `, ${cityOptions[country!]?.find(c => c.value === city)?.label}` : ''}
|
|
@@ -238,7 +238,7 @@ export function Autocomplete<T extends string = string>({
|
|
|
238
238
|
<View
|
|
239
239
|
className={cn(
|
|
240
240
|
'absolute z-50 top-full left-0 right-0 mt-1',
|
|
241
|
-
'bg-surface-elevated rounded-md shadow-lg border border-
|
|
241
|
+
'bg-surface-elevated rounded-md shadow-lg border border-hairline',
|
|
242
242
|
'max-h-60 overflow-hidden'
|
|
243
243
|
)}
|
|
244
244
|
>
|
|
@@ -67,7 +67,7 @@ export function Avatar({
|
|
|
67
67
|
accessibilityLabel={alt || resolvedFallback || 'Avatar'}
|
|
68
68
|
className={cn(
|
|
69
69
|
'items-center justify-center rounded-full overflow-hidden',
|
|
70
|
-
!nameColor && 'bg-
|
|
70
|
+
!nameColor && 'bg-hairline-strong',
|
|
71
71
|
styles.container,
|
|
72
72
|
className
|
|
73
73
|
)}
|
|
@@ -102,7 +102,7 @@ const badgeColors: Record<string, string> = {
|
|
|
102
102
|
success: 'bg-status-success',
|
|
103
103
|
error: 'bg-status-error',
|
|
104
104
|
warning: 'bg-status-warning',
|
|
105
|
-
default: 'bg-
|
|
105
|
+
default: 'bg-hairline-strong',
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
/**
|
|
@@ -25,7 +25,7 @@ export interface BadgeProps extends ViewProps {
|
|
|
25
25
|
|
|
26
26
|
const variantColorStyles: Record<BadgeVariant, Record<BadgeColor, string>> = {
|
|
27
27
|
solid: {
|
|
28
|
-
default: 'bg-
|
|
28
|
+
default: 'bg-hairline-strong text-text-inverse',
|
|
29
29
|
primary: 'bg-brand-primary text-text-inverse',
|
|
30
30
|
secondary: 'bg-brand-secondary text-text-inverse',
|
|
31
31
|
success: 'bg-status-success text-text-inverse',
|
|
@@ -43,7 +43,7 @@ const variantColorStyles: Record<BadgeVariant, Record<BadgeColor, string>> = {
|
|
|
43
43
|
info: 'bg-status-info-subtle text-status-info',
|
|
44
44
|
},
|
|
45
45
|
outline: {
|
|
46
|
-
default: 'border border-
|
|
46
|
+
default: 'border border-hairline-strong text-text-secondary',
|
|
47
47
|
primary: 'border border-brand-primary text-brand-primary',
|
|
48
48
|
secondary: 'border border-brand-secondary text-brand-secondary',
|
|
49
49
|
success: 'border border-status-success text-status-success',
|
|
@@ -45,10 +45,10 @@ export interface CardProps extends ViewProps {
|
|
|
45
45
|
|
|
46
46
|
const variantStyles: Record<CardVariant, string> = {
|
|
47
47
|
elevated: '', // Will be set dynamically via elevation system
|
|
48
|
-
outline: 'border-2 border-
|
|
48
|
+
outline: 'border-2 border-hairline-strong', // Thicker border with stronger contrast
|
|
49
49
|
filled: '', // Will be set dynamically via elevation system
|
|
50
|
-
accent: 'border border-
|
|
51
|
-
subtle: 'border border-
|
|
50
|
+
accent: 'border border-hairline',
|
|
51
|
+
subtle: 'border border-hairline-subtle',
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/**
|
|
@@ -91,7 +91,7 @@ export const Checkbox = forwardRef<View, CheckboxProps>(function Checkbox(
|
|
|
91
91
|
styles.box,
|
|
92
92
|
isChecked || isIndeterminate
|
|
93
93
|
? 'bg-brand-primary border-brand-primary'
|
|
94
|
-
: 'bg-transparent border-
|
|
94
|
+
: 'bg-transparent border-hairline',
|
|
95
95
|
isInvalid && 'border-status-error',
|
|
96
96
|
!isDisabled && 'web:hover:border-brand-primary'
|
|
97
97
|
)}
|
|
@@ -180,7 +180,7 @@ export function Accordion({
|
|
|
180
180
|
|
|
181
181
|
return (
|
|
182
182
|
<AccordionContext.Provider value={{ openIndices, toggleIndex, allowMultiple }}>
|
|
183
|
-
<View className={cn('w-full divide-y divide-
|
|
183
|
+
<View className={cn('w-full divide-y divide-hairline', className)} {...props}>
|
|
184
184
|
{React.Children.map(children, (child, index) => {
|
|
185
185
|
if (React.isValidElement(child)) {
|
|
186
186
|
return React.cloneElement(child as React.ReactElement<any>, { index })
|
|
@@ -125,7 +125,7 @@ export function Drawer({
|
|
|
125
125
|
>
|
|
126
126
|
{/* Header */}
|
|
127
127
|
{(title || showCloseButton) && (
|
|
128
|
-
<View className="flex-row items-center justify-between px-4 py-3 border-b border-
|
|
128
|
+
<View className="flex-row items-center justify-between px-4 py-3 border-b border-hairline">
|
|
129
129
|
{title && (
|
|
130
130
|
<Text className="text-lg font-semibold text-text-primary">
|
|
131
131
|
{title}
|
|
@@ -165,7 +165,7 @@ export interface DrawerHeaderProps extends ViewProps {
|
|
|
165
165
|
export function DrawerHeader({ className, children, ...props }: DrawerHeaderProps) {
|
|
166
166
|
return (
|
|
167
167
|
<View
|
|
168
|
-
className={cn('px-4 py-3 border-b border-
|
|
168
|
+
className={cn('px-4 py-3 border-b border-hairline', className)}
|
|
169
169
|
{...props}
|
|
170
170
|
>
|
|
171
171
|
{children}
|
|
@@ -217,7 +217,7 @@ export function DrawerFooter({ className, children, ...props }: DrawerFooterProp
|
|
|
217
217
|
<View
|
|
218
218
|
className={cn(
|
|
219
219
|
'flex-row items-center justify-end gap-3',
|
|
220
|
-
'px-4 py-3 border-t border-
|
|
220
|
+
'px-4 py-3 border-t border-hairline',
|
|
221
221
|
className
|
|
222
222
|
)}
|
|
223
223
|
{...props}
|
|
@@ -116,7 +116,7 @@ export function MenuList({ children, className }: MenuListProps) {
|
|
|
116
116
|
<View
|
|
117
117
|
className={cn(
|
|
118
118
|
'absolute z-50 top-full left-0 mt-1 min-w-[160px]',
|
|
119
|
-
'bg-surface-elevated rounded-lg shadow-lg border border-
|
|
119
|
+
'bg-surface-elevated rounded-lg shadow-lg border border-hairline',
|
|
120
120
|
'py-1 overflow-hidden',
|
|
121
121
|
className
|
|
122
122
|
)}
|
|
@@ -195,7 +195,7 @@ export interface MenuDividerProps {
|
|
|
195
195
|
* Divider between menu items.
|
|
196
196
|
*/
|
|
197
197
|
export function MenuDivider({ className }: MenuDividerProps) {
|
|
198
|
-
return <View className={cn('h-px bg-
|
|
198
|
+
return <View className={cn('h-px bg-hairline my-1', className)} />
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
export interface MenuGroupProps {
|
|
@@ -27,7 +27,7 @@ export interface PillProps extends ViewProps {
|
|
|
27
27
|
|
|
28
28
|
const variantColorStyles: Record<PillVariant, Record<PillColor, string>> = {
|
|
29
29
|
subtle: {
|
|
30
|
-
default: 'bg-surface-raised border-
|
|
30
|
+
default: 'bg-surface-raised border-hairline-subtle text-text-secondary',
|
|
31
31
|
primary: 'bg-brand-primary/10 border-brand-primary/25 text-brand-primary',
|
|
32
32
|
secondary: 'bg-brand-secondary/10 border-brand-secondary/25 text-brand-secondary',
|
|
33
33
|
success: 'bg-status-success/10 border-status-success/25 text-status-success',
|
|
@@ -36,7 +36,7 @@ const variantColorStyles: Record<PillVariant, Record<PillColor, string>> = {
|
|
|
36
36
|
info: 'bg-status-info/10 border-status-info/25 text-status-info',
|
|
37
37
|
},
|
|
38
38
|
outline: {
|
|
39
|
-
default: 'border-
|
|
39
|
+
default: 'border-hairline text-text-secondary',
|
|
40
40
|
primary: 'border-brand-primary text-brand-primary',
|
|
41
41
|
secondary: 'border-brand-secondary text-brand-secondary',
|
|
42
42
|
success: 'border-status-success text-status-success',
|
|
@@ -152,12 +152,12 @@ export const WithFormContent: Story = {
|
|
|
152
152
|
<Text className="text-text-secondary text-xs">
|
|
153
153
|
Enter your new display name below.
|
|
154
154
|
</Text>
|
|
155
|
-
<View className="bg-surface-input border border-
|
|
155
|
+
<View className="bg-surface-input border border-hairline rounded-md px-3 py-2">
|
|
156
156
|
<Text className="text-text-secondary text-sm">John Doe</Text>
|
|
157
157
|
</View>
|
|
158
158
|
<View style={{ flexDirection: 'row', gap: 8, justifyContent: 'flex-end' }}>
|
|
159
159
|
<PopoverCloseButton>
|
|
160
|
-
<View className="px-3 py-1.5 rounded-md bg-surface-elevated border border-
|
|
160
|
+
<View className="px-3 py-1.5 rounded-md bg-surface-elevated border border-hairline">
|
|
161
161
|
<Text className="text-text-primary text-sm">Cancel</Text>
|
|
162
162
|
</View>
|
|
163
163
|
</PopoverCloseButton>
|
|
@@ -188,7 +188,7 @@ export function PopoverContent({ children, className }: PopoverContentProps) {
|
|
|
188
188
|
<View
|
|
189
189
|
className={cn(
|
|
190
190
|
'absolute z-50 min-w-[200px]',
|
|
191
|
-
'bg-surface-elevated rounded-lg shadow-lg border border-
|
|
191
|
+
'bg-surface-elevated rounded-lg shadow-lg border border-hairline',
|
|
192
192
|
'p-4',
|
|
193
193
|
placementStyles[placement],
|
|
194
194
|
className
|
|
@@ -230,7 +230,7 @@ export function CircularProgress({
|
|
|
230
230
|
cy={center}
|
|
231
231
|
r={radius}
|
|
232
232
|
fill="none"
|
|
233
|
-
stroke="var(--color-
|
|
233
|
+
stroke="var(--color-hairline-default)"
|
|
234
234
|
strokeWidth={strokeWidth}
|
|
235
235
|
/>
|
|
236
236
|
{/* Progress arc */}
|
|
@@ -325,7 +325,7 @@ export function ProgressSteps({
|
|
|
325
325
|
className={cn(
|
|
326
326
|
'rounded-full',
|
|
327
327
|
dot,
|
|
328
|
-
index <= currentStep ? colorStyles[color] : 'bg-
|
|
328
|
+
index <= currentStep ? colorStyles[color] : 'bg-hairline-strong'
|
|
329
329
|
)}
|
|
330
330
|
/>
|
|
331
331
|
|
|
@@ -335,7 +335,7 @@ export function ProgressSteps({
|
|
|
335
335
|
className={cn(
|
|
336
336
|
'flex-1 mx-1',
|
|
337
337
|
connector,
|
|
338
|
-
index < currentStep ? colorStyles[color] : 'bg-
|
|
338
|
+
index < currentStep ? colorStyles[color] : 'bg-hairline-strong'
|
|
339
339
|
)}
|
|
340
340
|
/>
|
|
341
341
|
)}
|
|
@@ -188,7 +188,7 @@ export const FormExample: Story = {
|
|
|
188
188
|
<Radio value="yearly">Yearly (save 20%)</Radio>
|
|
189
189
|
</RadioGroup>
|
|
190
190
|
|
|
191
|
-
<View className="pt-4 border-t border-
|
|
191
|
+
<View className="pt-4 border-t border-hairline">
|
|
192
192
|
<Text className="text-sm text-text-secondary">
|
|
193
193
|
Selected: {plan} plan, billed {billing}
|
|
194
194
|
</Text>
|
|
@@ -117,10 +117,10 @@ const sizeConfig: Record<RadioSize, { outer: string; inner: string; text: string
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
const colorStyles: Record<RadioColor, { checked: string; unchecked: string }> = {
|
|
120
|
-
primary: { checked: 'border-brand-primary bg-brand-primary', unchecked: 'border-
|
|
121
|
-
secondary: { checked: 'border-brand-secondary bg-brand-secondary', unchecked: 'border-
|
|
122
|
-
success: { checked: 'border-status-success bg-status-success', unchecked: 'border-
|
|
123
|
-
error: { checked: 'border-status-error bg-status-error', unchecked: 'border-
|
|
120
|
+
primary: { checked: 'border-brand-primary bg-brand-primary', unchecked: 'border-hairline-strong' },
|
|
121
|
+
secondary: { checked: 'border-brand-secondary bg-brand-secondary', unchecked: 'border-hairline-strong' },
|
|
122
|
+
success: { checked: 'border-status-success bg-status-success', unchecked: 'border-hairline-strong' },
|
|
123
|
+
error: { checked: 'border-status-error bg-status-error', unchecked: 'border-hairline-strong' },
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
/**
|
|
@@ -145,7 +145,7 @@ export function Select<T extends string = string>({
|
|
|
145
145
|
isInvalid
|
|
146
146
|
? 'border-border-input-error'
|
|
147
147
|
: variant === 'filled'
|
|
148
|
-
? 'border-
|
|
148
|
+
? 'border-hairline-subtle'
|
|
149
149
|
: 'border-border-input',
|
|
150
150
|
!isDisabled && !isInvalid && 'web:hover:border-border-input-hover',
|
|
151
151
|
isOpen && 'border-border-input-focus',
|
|
@@ -187,7 +187,7 @@ export function Select<T extends string = string>({
|
|
|
187
187
|
<View
|
|
188
188
|
className={cn(
|
|
189
189
|
'absolute z-50 top-full left-0 right-0 mt-1',
|
|
190
|
-
'bg-surface-elevated rounded-md shadow-lg border border-
|
|
190
|
+
'bg-surface-elevated rounded-md shadow-lg border border-hairline',
|
|
191
191
|
'max-h-60 overflow-hidden'
|
|
192
192
|
)}
|
|
193
193
|
>
|
|
@@ -229,7 +229,7 @@ function SelectOption<T>({ option }: SelectOptionComponentProps<T>) {
|
|
|
229
229
|
<View
|
|
230
230
|
className={cn(
|
|
231
231
|
'w-4 h-4 mr-3 rounded border',
|
|
232
|
-
selected ? 'bg-brand-primary border-brand-primary' : 'border-
|
|
232
|
+
selected ? 'bg-brand-primary border-brand-primary' : 'border-hairline-strong'
|
|
233
233
|
)}
|
|
234
234
|
>
|
|
235
235
|
{selected && <Text className="text-white text-xs text-center">✓</Text>}
|
|
@@ -15,8 +15,8 @@ const meta: Meta<typeof Surface> = {
|
|
|
15
15
|
},
|
|
16
16
|
level: {
|
|
17
17
|
control: 'select',
|
|
18
|
-
options: ['
|
|
19
|
-
description: 'Named
|
|
18
|
+
options: ['frame', 'background', 'base', 'elevated', 'raised', 'overlay'],
|
|
19
|
+
description: 'Named grey plane (flat background from a semantic token)',
|
|
20
20
|
},
|
|
21
21
|
pressed: {
|
|
22
22
|
control: 'boolean',
|
|
@@ -88,7 +88,7 @@ export const WithGlow: Story = {
|
|
|
88
88
|
),
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
// The named-plane model: flat, full-bleed
|
|
91
|
+
// The named-plane model: flat, full-bleed grey planes straight from the
|
|
92
92
|
// semantic surface tokens — reaching the darker nav/page shades the numeric
|
|
93
93
|
// lighten model can't. These back the shell / rail / stage.
|
|
94
94
|
export const NamedPlanes: Story = {
|