@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
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
// (no global.css, no nativewind) — the bug class this primitive retires.
|
|
8
8
|
import { createContext, useContext } from 'react'
|
|
9
9
|
import { getSemanticColors, type ThemeMode } from '../../../theme/tokens/semantic'
|
|
10
|
-
import { surfaceRampDark } from '../../../theme/tokens/primitives'
|
|
11
10
|
|
|
12
11
|
type ColorToken = keyof ReturnType<typeof getSemanticColors>
|
|
13
12
|
|
|
@@ -18,32 +17,35 @@ type ColorToken = keyof ReturnType<typeof getSemanticColors>
|
|
|
18
17
|
* no new hexes. Darkest → lightest:
|
|
19
18
|
* inset (#13100D) < background (#1C1916) < base (#252321) < elevated (#2A2827) < raised (#2D2C2B) < overlay (#302F2E)
|
|
20
19
|
*
|
|
21
|
-
* `
|
|
22
|
-
* `<Surface pressed>` clamps at (see {@link pressedLevel})
|
|
23
|
-
*
|
|
24
|
-
* `
|
|
20
|
+
* `frame` is the bezel the ramp sits inside — the top bar / side nav chrome —
|
|
21
|
+
* and the floor a `<Surface pressed>` clamps at (see {@link pressedLevel}).
|
|
22
|
+
*
|
|
23
|
+
* It replaced a separate `inset` level in TD-07.14. That level was RETIRED, not
|
|
24
|
+
* renamed: at ΔE 1.10 from the frame it was an imperceptible duplicate, which is
|
|
25
|
+
* exactly why pressed (surface − 1) kept collapsing into it.
|
|
25
26
|
*/
|
|
26
|
-
export type SurfaceLevel = '
|
|
27
|
+
export type SurfaceLevel = 'frame' | 'background' | 'base' | 'elevated' | 'raised' | 'overlay'
|
|
27
28
|
|
|
28
29
|
/** On-surface neutral text roles, resolved for the current surface + theme. */
|
|
29
30
|
export type OnSurfaceRole = 'primary' | 'secondary' | 'tertiary'
|
|
30
31
|
|
|
31
|
-
// Which semantic token backs each
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
32
|
+
// Which semantic token backs each level. Values live in `semantic.ts` and stay
|
|
33
|
+
// in sync with the grey ramp — this map never carries literal hexes. Every level
|
|
34
|
+
// is present now: swapping `inset` (which had no token) for `frame` (which does)
|
|
35
|
+
// is what made this a total map and removed `surfaceBackground`'s special case.
|
|
35
36
|
export const SURFACE_LEVEL_TOKEN = {
|
|
37
|
+
frame: 'background-frame',
|
|
36
38
|
background: 'background-base',
|
|
37
39
|
base: 'surface-base',
|
|
38
40
|
elevated: 'surface-elevated',
|
|
39
41
|
raised: 'surface-raised',
|
|
40
42
|
overlay: 'surface-overlay',
|
|
41
|
-
} as const satisfies Record<
|
|
43
|
+
} as const satisfies Record<SurfaceLevel, ColorToken>
|
|
42
44
|
|
|
43
|
-
// Darkest → lightest
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
const PRESSED_RAMP = ['
|
|
45
|
+
// Darkest → lightest. A `<Surface pressed>` renders one index DOWN from its
|
|
46
|
+
// parent's level and clamps at `frame` (index 0). Kept in lockstep with the grey
|
|
47
|
+
// ramp and the surface.contract.test PLANE_ORDER.
|
|
48
|
+
const PRESSED_RAMP = ['frame', 'background', 'base', 'elevated', 'raised', 'overlay'] as const
|
|
47
49
|
|
|
48
50
|
const ON_SURFACE_TOKEN = {
|
|
49
51
|
primary: 'text-primary',
|
|
@@ -94,29 +96,17 @@ export function useOnSurfaceColor(role: OnSurfaceRole = 'primary'): string {
|
|
|
94
96
|
return getSemanticColors(useSurfaceMode())[ON_SURFACE_TOKEN[role]]
|
|
95
97
|
}
|
|
96
98
|
|
|
97
|
-
/**
|
|
98
|
-
* The inset floor hex — the ramp's deepest pit that a pressed surface clamps at.
|
|
99
|
-
* Prefers the `surface-inset` semantic token (theme-aware; being promoted in a
|
|
100
|
-
* parallel token change) and falls back to the `surfaceRampDark.inset` primitive
|
|
101
|
-
* (#13100D, dark-ramp only) until that token ships.
|
|
102
|
-
*/
|
|
103
|
-
function insetFloor(mode: ThemeMode): string {
|
|
104
|
-
const colors = getSemanticColors(mode) as Record<string, string>
|
|
105
|
-
return colors['surface-inset'] ?? surfaceRampDark.inset
|
|
106
|
-
}
|
|
107
|
-
|
|
108
99
|
/** The background hex for a surface level under a theme mode (literal hex). */
|
|
109
100
|
export function surfaceBackground(level: SurfaceLevel, mode: ThemeMode): string {
|
|
110
|
-
if (level === 'inset') return insetFloor(mode)
|
|
111
101
|
return getSemanticColors(mode)[SURFACE_LEVEL_TOKEN[level]]
|
|
112
102
|
}
|
|
113
103
|
|
|
114
104
|
/**
|
|
115
105
|
* The level a `<Surface pressed>` resolves to: its parent's level stepped one
|
|
116
106
|
* index DOWN the ramp — the symmetric twin of a raised surface stepping up —
|
|
117
|
-
* clamped at the `
|
|
107
|
+
* clamped at the `frame` floor so a pressed surface never underflows the bezel.
|
|
118
108
|
* overlay→raised, raised→elevated, elevated→base, base→background,
|
|
119
|
-
* background→
|
|
109
|
+
* background→frame, frame→frame (clamped).
|
|
120
110
|
* Pressed exposes ONE level of depth only (no pressed-2).
|
|
121
111
|
*/
|
|
122
112
|
export function pressedLevel(parent: SurfaceLevel): SurfaceLevel {
|
|
@@ -20,7 +20,7 @@ import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
|
20
20
|
* derived, not hand-picked") deliberately supersedes that with DIMINISHING
|
|
21
21
|
* steps — the frame->content jump is biggest, each plane above adds less.
|
|
22
22
|
* As of S-3 (this re-space) the steps are 4.5 / 4.5 / 3.5 / 3 / 2.5
|
|
23
|
-
* (
|
|
23
|
+
* (frame->background / background->base / base->elevated / elevated->raised /
|
|
24
24
|
* raised->overlay) — the top three widened from the original 2.5/2/1.5 taper
|
|
25
25
|
* so the content planes read as distinct without leaning on the hairline
|
|
26
26
|
* alone (R3 still carries the rest, per "lightness is a *secondary* cue").
|
|
@@ -73,10 +73,10 @@ function compositeOver(baseHex: string, overlay: string): string {
|
|
|
73
73
|
|
|
74
74
|
const dark = getSemanticColors('dark')
|
|
75
75
|
|
|
76
|
-
// The 6-plane derivation, in darkest -> lightest order.
|
|
77
|
-
//
|
|
76
|
+
// The 6-plane derivation, in darkest -> lightest order. The floor is `frame`;
|
|
77
|
+
// the old `inset` plane was retired in TD-07.14 for being ΔE 1.10 from it.
|
|
78
78
|
const RAMP_HEX = {
|
|
79
|
-
|
|
79
|
+
frame: dark['background-frame'],
|
|
80
80
|
background: dark['background-base'],
|
|
81
81
|
base: dark['surface-base'],
|
|
82
82
|
elevated: dark['surface-elevated'],
|
|
@@ -84,14 +84,14 @@ const RAMP_HEX = {
|
|
|
84
84
|
overlay: dark['surface-overlay'],
|
|
85
85
|
} as const
|
|
86
86
|
|
|
87
|
-
const PLANE_ORDER = ['
|
|
87
|
+
const PLANE_ORDER = ['frame', 'background', 'base', 'elevated', 'raised', 'overlay'] as const
|
|
88
88
|
|
|
89
89
|
describe('surface ramp contract (dark) — token-value guardrails', () => {
|
|
90
90
|
it('ships the deriveSurfaceRamp() output verbatim for the addressable planes', () => {
|
|
91
91
|
// Locked values from surface-system-north-star.md (re-spaced S-3) — see
|
|
92
92
|
// surfaceRampDark in primitives.ts for the derivation this must match
|
|
93
|
-
// byte-for-byte. background/base
|
|
94
|
-
expect(dark['
|
|
93
|
+
// byte-for-byte. background/base are unchanged by the re-space.
|
|
94
|
+
expect(dark['background-frame']).toBe('#100D0A')
|
|
95
95
|
expect(dark['background-base']).toBe('#1C1916')
|
|
96
96
|
expect(dark['surface-base']).toBe('#252321')
|
|
97
97
|
expect(dark['surface-elevated']).toBe('#2C2A28')
|
|
@@ -110,8 +110,8 @@ describe('surface ramp contract (dark) — token-value guardrails', () => {
|
|
|
110
110
|
}
|
|
111
111
|
})
|
|
112
112
|
|
|
113
|
-
it('clears ΔL* >= 4 at the two foundational steps (
|
|
114
|
-
const dL_insetToBackground = lstar(RAMP_HEX.background) - lstar(RAMP_HEX.
|
|
113
|
+
it('clears ΔL* >= 4 at the two foundational steps (frame->background, background->base)', () => {
|
|
114
|
+
const dL_insetToBackground = lstar(RAMP_HEX.background) - lstar(RAMP_HEX.frame)
|
|
115
115
|
const dL_backgroundToBase = lstar(RAMP_HEX.base) - lstar(RAMP_HEX.background)
|
|
116
116
|
expect(dL_insetToBackground).toBeGreaterThanOrEqual(4)
|
|
117
117
|
expect(dL_backgroundToBase).toBeGreaterThanOrEqual(4)
|
|
@@ -158,14 +158,10 @@ describe('surface ramp contract (dark) — token-value guardrails', () => {
|
|
|
158
158
|
dark['surface-raised'],
|
|
159
159
|
dark['surface-overlay'],
|
|
160
160
|
dark['surface-input'],
|
|
161
|
-
dark['surface-inset'],
|
|
162
161
|
])
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
'border-strong',
|
|
167
|
-
'border-prominent',
|
|
168
|
-
] as const
|
|
162
|
+
// `border-prominent` is the only solid border left; the quiet three are
|
|
163
|
+
// alpha hairlines now and cannot equal a fill.
|
|
164
|
+
const solidBorders = ['border-prominent'] as const
|
|
169
165
|
for (const token of solidBorders) {
|
|
170
166
|
expect(
|
|
171
167
|
surfaceHexes.has(dark[token]),
|
|
@@ -185,10 +181,17 @@ describe('surface ramp contract (dark) — token-value guardrails', () => {
|
|
|
185
181
|
// the S-3 ramp (default dropped 8->7 — the `overlay` plane now measures
|
|
186
182
|
// ~7.97, just under the old floor); a wall-display calibration pass (S-6)
|
|
187
183
|
// may retune the alpha values themselves.
|
|
184
|
+
//
|
|
185
|
+
// S-6 DID (VW-99). The alphas went up ×1.5 to .09/.135/.21: on the wall at
|
|
186
|
+
// ~3 m `subtle` rendered but was indiscernible, and `default` — which has had
|
|
187
|
+
// no solid-border fallback since TD-07.14 — went weak on the lightest plane.
|
|
188
|
+
// Floors below re-measured against the new alphas. Note new `subtle` now
|
|
189
|
+
// measures exactly where old `default` did (min 7.97), and the new `default`
|
|
190
|
+
// where old `strong` did (min 12.26) — each tier onto a measured-good rung.
|
|
188
191
|
const HAIRLINES = {
|
|
189
|
-
subtle: { token: 'hairline-subtle', floor:
|
|
190
|
-
default: { token: 'hairline-default', floor:
|
|
191
|
-
strong: { token: 'hairline-strong', floor:
|
|
192
|
+
subtle: { token: 'hairline-subtle', floor: 7 },
|
|
193
|
+
default: { token: 'hairline-default', floor: 12 },
|
|
194
|
+
strong: { token: 'hairline-strong', floor: 18 },
|
|
192
195
|
} as const
|
|
193
196
|
|
|
194
197
|
for (const [name, { token, floor }] of Object.entries(HAIRLINES)) {
|
|
@@ -203,30 +206,40 @@ describe('surface ramp contract (dark) — token-value guardrails', () => {
|
|
|
203
206
|
})
|
|
204
207
|
}
|
|
205
208
|
|
|
206
|
-
it('is near-constant (self-normalizing) across all planes: spread
|
|
207
|
-
// Measured spread (
|
|
208
|
-
//
|
|
209
|
-
//
|
|
210
|
-
//
|
|
211
|
-
//
|
|
209
|
+
it('is near-constant (self-normalizing) across all planes: spread stays proportional', () => {
|
|
210
|
+
// Measured RELATIVE spread ((max-min)/min): subtle 0.27, default 0.29,
|
|
211
|
+
// strong 0.29 — and, importantly, those are the same figures the OLD
|
|
212
|
+
// .06/.09/.14 family produced (0.28 / 0.27 / 0.28). Self-normalization
|
|
213
|
+
// survived the S-6 retune untouched.
|
|
214
|
+
//
|
|
215
|
+
// This assertion used to be an ABSOLUTE `spread < 4 L*`, which was the
|
|
216
|
+
// wrong metric: absolute spread scales with the alpha, so ANY strengthening
|
|
217
|
+
// fails it no matter how well the property holds. The S-6 ×1.5 retune
|
|
218
|
+
// tripped it (strong 3.46 -> 5.29) purely arithmetically. A ratio is
|
|
219
|
+
// scale-invariant, which is what "self-normalizing" actually claims.
|
|
220
|
+
//
|
|
221
|
+
// Contrast a solid border token, which swings ~5.6 -> 0 across this same
|
|
222
|
+
// ramp (see R4): the point is not a tight absolute band, it is that no
|
|
223
|
+
// plane ever loses the cue. Alpha-over-white is sublinear in L* as the
|
|
224
|
+
// base lightens, which is the R3 floor-vs-doc discrepancy noted above.
|
|
212
225
|
for (const { token } of Object.values(HAIRLINES)) {
|
|
213
226
|
const hairlineColor = dark[token as keyof typeof dark]
|
|
214
227
|
const deltas = PLANE_ORDER.map((plane) => {
|
|
215
228
|
const planeHex = RAMP_HEX[plane]
|
|
216
229
|
return lstar(compositeOver(planeHex, hairlineColor)) - lstar(planeHex)
|
|
217
230
|
})
|
|
218
|
-
|
|
231
|
+
const min = Math.min(...deltas)
|
|
232
|
+
expect((Math.max(...deltas) - min) / min, `${token} relative spread`).toBeLessThan(0.35)
|
|
219
233
|
}
|
|
220
234
|
})
|
|
221
235
|
})
|
|
222
236
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
it('border-subtle clears ΔL* >= 3 against base/elevated/raised/overlay', () => {
|
|
237
|
+
// R4 used to check that the solid `border-subtle` hex did not match the plane
|
|
238
|
+
// it sat on. Those solid borders are gone (TD-07.14) — separation is R3's
|
|
239
|
+
// alpha hairlines, which cannot collide with a plane by construction. What is
|
|
240
|
+
// left to guard is the one border still solid.
|
|
241
|
+
describe('R4 — border-prominent, the last solid border', () => {
|
|
242
|
+
it('clears ΔL* >= 3 on every content plane', () => {
|
|
230
243
|
const planes = [
|
|
231
244
|
'surface-base',
|
|
232
245
|
'surface-elevated',
|
|
@@ -234,13 +247,13 @@ describe('surface ramp contract (dark) — token-value guardrails', () => {
|
|
|
234
247
|
'surface-overlay',
|
|
235
248
|
] as const
|
|
236
249
|
for (const plane of planes) {
|
|
237
|
-
const dL = Math.abs(lstar(dark['border-
|
|
238
|
-
expect(dL, `border-
|
|
250
|
+
const dL = Math.abs(lstar(dark['border-prominent']) - lstar(dark[plane]))
|
|
251
|
+
expect(dL, `border-prominent vs ${plane}`).toBeGreaterThanOrEqual(3)
|
|
239
252
|
}
|
|
240
253
|
})
|
|
241
254
|
})
|
|
242
255
|
|
|
243
|
-
describe('R5 — background-frame
|
|
256
|
+
describe('R5 — background-frame, the ramp floor', () => {
|
|
244
257
|
it('background-frame is darker than background-base', () => {
|
|
245
258
|
expect(lstar(dark['background-frame'])).toBeLessThan(lstar(dark['background-base']))
|
|
246
259
|
})
|
|
@@ -255,14 +268,18 @@ describe('surface ramp contract (dark) — token-value guardrails', () => {
|
|
|
255
268
|
'surface-raised',
|
|
256
269
|
'surface-overlay',
|
|
257
270
|
'surface-input',
|
|
258
|
-
'surface-inset',
|
|
259
271
|
] as const
|
|
260
272
|
for (const token of otherTokens) {
|
|
261
273
|
expect(dark['background-frame'], `background-frame vs ${token}`).not.toBe(dark[token])
|
|
262
274
|
}
|
|
263
275
|
})
|
|
264
276
|
|
|
265
|
-
|
|
277
|
+
// `surface-inset` used to sit between frame and background as the darkest
|
|
278
|
+
// surface-family token. TD-07.14 retired it: at delta-E 1.10 from the frame
|
|
279
|
+
// it was an imperceptible duplicate, which is why `<Surface pressed>`
|
|
280
|
+
// (surface - 1) kept collapsing into it. `frame` is the floor now, so the
|
|
281
|
+
// rule that matters is that nothing in the surface family goes below it.
|
|
282
|
+
it('background-frame is darker than every surface-family token', () => {
|
|
266
283
|
const surfaceFamily = [
|
|
267
284
|
'surface-base',
|
|
268
285
|
'surface-elevated',
|
|
@@ -270,14 +287,10 @@ describe('surface ramp contract (dark) — token-value guardrails', () => {
|
|
|
270
287
|
'surface-overlay',
|
|
271
288
|
'surface-input',
|
|
272
289
|
] as const
|
|
273
|
-
const
|
|
290
|
+
const frameL = lstar(dark['background-frame'])
|
|
274
291
|
for (const token of surfaceFamily) {
|
|
275
|
-
expect(
|
|
292
|
+
expect(frameL, `background-frame vs ${token}`).toBeLessThan(lstar(dark[token]))
|
|
276
293
|
}
|
|
277
294
|
})
|
|
278
|
-
|
|
279
|
-
it('surface-inset is distinct from background-frame', () => {
|
|
280
|
-
expect(dark['surface-inset']).not.toBe(dark['background-frame'])
|
|
281
|
-
})
|
|
282
295
|
})
|
|
283
296
|
})
|
|
@@ -63,7 +63,7 @@ export const Switch = forwardRef<View, SwitchProps>(function Switch(
|
|
|
63
63
|
className={cn(
|
|
64
64
|
'rounded-full p-0.5 transition-colors',
|
|
65
65
|
styles.track,
|
|
66
|
-
isChecked ? 'bg-brand-primary' : 'bg-
|
|
66
|
+
isChecked ? 'bg-brand-primary' : 'bg-hairline-strong'
|
|
67
67
|
)}
|
|
68
68
|
>
|
|
69
69
|
<View
|
|
@@ -100,8 +100,8 @@ export function TabList({ children, className }: TabListProps) {
|
|
|
100
100
|
|
|
101
101
|
const variantStyles = {
|
|
102
102
|
line: orientation === 'horizontal'
|
|
103
|
-
? 'border-b border-
|
|
104
|
-
: 'border-r border-
|
|
103
|
+
? 'border-b border-hairline'
|
|
104
|
+
: 'border-r border-hairline',
|
|
105
105
|
enclosed: 'bg-surface-raised rounded-lg p-1',
|
|
106
106
|
'soft-rounded': 'bg-surface-raised rounded-full p-1',
|
|
107
107
|
}
|
|
@@ -10,7 +10,7 @@ const meta: Meta<typeof ToolbarButton> = {
|
|
|
10
10
|
argTypes: {
|
|
11
11
|
variant: {
|
|
12
12
|
control: 'select',
|
|
13
|
-
options: ['default', '
|
|
13
|
+
options: ['default', 'raised'],
|
|
14
14
|
},
|
|
15
15
|
isActive: {
|
|
16
16
|
control: 'select',
|
|
@@ -60,7 +60,7 @@ export const Default: Story = {
|
|
|
60
60
|
args: {
|
|
61
61
|
label: 'Settings',
|
|
62
62
|
icon: <SettingsIcon />,
|
|
63
|
-
variant: '
|
|
63
|
+
variant: 'raised',
|
|
64
64
|
showLabel: true,
|
|
65
65
|
},
|
|
66
66
|
}
|
|
@@ -319,7 +319,7 @@ export const CompactMode: Story = {
|
|
|
319
319
|
onPress={() => setIsCompact(!isCompact)}
|
|
320
320
|
/>
|
|
321
321
|
</View>
|
|
322
|
-
<View className="h-px bg-
|
|
322
|
+
<View className="h-px bg-hairline" />
|
|
323
323
|
<ToolbarButtonGroup gap="sm">
|
|
324
324
|
<ToolbarButton
|
|
325
325
|
label="Filter"
|
|
@@ -351,8 +351,8 @@ export const Variants: Story = {
|
|
|
351
351
|
<View>
|
|
352
352
|
<Text className="text-text-secondary text-xs mb-2">Neumorphic (with 3D inset shadows)</Text>
|
|
353
353
|
<View style={{ flexDirection: 'row', gap: 8 }}>
|
|
354
|
-
<ToolbarButton label="Inactive" icon={<SettingsIcon />} variant="
|
|
355
|
-
<ToolbarButton label="Active" icon={<SettingsIcon />} variant="
|
|
354
|
+
<ToolbarButton label="Inactive" icon={<SettingsIcon />} variant="raised" isActive={false} />
|
|
355
|
+
<ToolbarButton label="Active" icon={<SettingsIcon />} variant="raised" isActive={true} />
|
|
356
356
|
</View>
|
|
357
357
|
</View>
|
|
358
358
|
<View>
|
|
@@ -96,7 +96,7 @@ describe('ToolbarButton', () => {
|
|
|
96
96
|
|
|
97
97
|
describe('variants', () => {
|
|
98
98
|
it('renders with neumorphic variant (default)', () => {
|
|
99
|
-
render(<ToolbarButton label="Neumorphic" variant="
|
|
99
|
+
render(<ToolbarButton label="Neumorphic" variant="raised" />)
|
|
100
100
|
expect(screen.getByRole('button', { name: 'Neumorphic' })).toBeInTheDocument()
|
|
101
101
|
})
|
|
102
102
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
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
|
-
import {
|
|
4
|
+
import { getHoverColors } from '../../../theme'
|
|
5
|
+
import { greyRamp } from '../../../theme/tokens/primitives'
|
|
6
|
+
import { getSemanticColors } from '../../../theme/tokens/semantic'
|
|
5
7
|
import { resolveColor } from '../../../theme/resolve-color'
|
|
6
8
|
|
|
7
|
-
export type ToolbarButtonVariant = 'default' | '
|
|
9
|
+
export type ToolbarButtonVariant = 'default' | 'raised'
|
|
8
10
|
export type ToolbarButtonSize = 'sm' | 'md' | 'lg'
|
|
9
11
|
|
|
10
12
|
interface ToolbarButtonContextType {
|
|
@@ -47,9 +49,19 @@ export interface ToolbarButtonProps extends ViewProps {
|
|
|
47
49
|
className?: string
|
|
48
50
|
}
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
const SEMANTIC = getSemanticColors('dark')
|
|
53
|
+
|
|
54
|
+
// Base button colours. These were raw `#3C3C3C`/`#2C2C2C` literals — old cold
|
|
55
|
+
// charcoal steps that survived the grey migration only because they were plain
|
|
56
|
+
// strings rather than scale references.
|
|
57
|
+
const BUTTON_BG = greyRamp[800]
|
|
58
|
+
const MENU_BG = greyRamp[900]
|
|
59
|
+
|
|
60
|
+
// Inline depth is a HAIRLINE, not a shadow (TD-07.16). On a ~10% lightness
|
|
61
|
+
// surface a dual-opposing neumorphic shadow has no room below to read; alpha
|
|
62
|
+
// composites by the same amount over any plane, so the edge holds everywhere.
|
|
63
|
+
const EDGE = { borderWidth: 1, borderColor: SEMANTIC['hairline-default'] } as const
|
|
64
|
+
const EDGE_PRESSED = { borderWidth: 1, borderColor: SEMANTIC['hairline-strong'] } as const
|
|
53
65
|
|
|
54
66
|
// Calculate hover colors using color math
|
|
55
67
|
const hoverColors = getHoverColors(BUTTON_BG, 'medium')
|
|
@@ -100,7 +112,7 @@ export function ToolbarButton({
|
|
|
100
112
|
isActive,
|
|
101
113
|
isDisabled = false,
|
|
102
114
|
size = 'md',
|
|
103
|
-
variant = '
|
|
115
|
+
variant = 'raised',
|
|
104
116
|
tooltip,
|
|
105
117
|
showLabel = true,
|
|
106
118
|
onPress,
|
|
@@ -135,26 +147,28 @@ export function ToolbarButton({
|
|
|
135
147
|
}
|
|
136
148
|
|
|
137
149
|
// Get shadow and background styles for neumorphic variant
|
|
138
|
-
|
|
150
|
+
/**
|
|
151
|
+
* Raised/pressed treatment. Depth comes from the FILL plus a hairline edge —
|
|
152
|
+
* pressed sits darker with a stronger edge, raised sits lighter with a quieter
|
|
153
|
+
* one. It used to come from a two-layer neumorphic shadow, which the surface
|
|
154
|
+
* exploration ruled out for inline hierarchy: it needs a mid-tone background
|
|
155
|
+
* for the dark half to have somewhere to fall, and these buttons sit at ~10%
|
|
156
|
+
* lightness.
|
|
157
|
+
*/
|
|
158
|
+
const getRaisedStyle = () => {
|
|
139
159
|
if (isDisabled) {
|
|
140
160
|
return styles.disabledBg
|
|
141
161
|
}
|
|
142
|
-
|
|
143
|
-
const shadows = neumorphicShadows.charcoal
|
|
144
|
-
|
|
145
162
|
if (showActive) {
|
|
146
|
-
// Active = pressed/sunken appearance
|
|
147
163
|
return {
|
|
148
|
-
backgroundColor: isHovered ? hoverColors.pressed :
|
|
149
|
-
...
|
|
150
|
-
}
|
|
151
|
-
} else {
|
|
152
|
-
// Inactive = raised/elevated appearance
|
|
153
|
-
return {
|
|
154
|
-
backgroundColor: isHovered ? hoverColors.raised : BUTTON_BG,
|
|
155
|
-
...(isHovered ? shadows.raisedHover.subtle : shadows.raised.subtle),
|
|
164
|
+
backgroundColor: isHovered ? hoverColors.pressed : greyRamp[900],
|
|
165
|
+
...EDGE_PRESSED,
|
|
156
166
|
}
|
|
157
167
|
}
|
|
168
|
+
return {
|
|
169
|
+
backgroundColor: isHovered ? hoverColors.raised : BUTTON_BG,
|
|
170
|
+
...EDGE,
|
|
171
|
+
}
|
|
158
172
|
}
|
|
159
173
|
|
|
160
174
|
return (
|
|
@@ -180,9 +194,9 @@ export function ToolbarButton({
|
|
|
180
194
|
className
|
|
181
195
|
)}
|
|
182
196
|
style={[
|
|
183
|
-
variant === '
|
|
197
|
+
variant === 'raised' && getRaisedStyle(),
|
|
184
198
|
variant === 'default' && {
|
|
185
|
-
backgroundColor: showActive ? BUTTON_BG :
|
|
199
|
+
backgroundColor: showActive ? BUTTON_BG : greyRamp[600],
|
|
186
200
|
},
|
|
187
201
|
]}
|
|
188
202
|
>
|
|
@@ -232,7 +246,7 @@ export function ToolbarButton({
|
|
|
232
246
|
<View
|
|
233
247
|
className={cn(
|
|
234
248
|
'absolute z-50 top-full left-0 mt-1',
|
|
235
|
-
'rounded-lg shadow-lg border border-
|
|
249
|
+
'rounded-lg shadow-lg border border-hairline',
|
|
236
250
|
'min-w-[150px] overflow-hidden',
|
|
237
251
|
`bg-[${MENU_BG}]`
|
|
238
252
|
)}
|
|
@@ -24,7 +24,7 @@ function PresetSwitcher({
|
|
|
24
24
|
onSwitch: (name: string) => void
|
|
25
25
|
}) {
|
|
26
26
|
return (
|
|
27
|
-
<View className="flex-row items-center gap-3 p-4 bg-surface-elevated rounded-lg border border-
|
|
27
|
+
<View className="flex-row items-center gap-3 p-4 bg-surface-elevated rounded-lg border border-hairline">
|
|
28
28
|
<Text className="text-text-secondary text-sm font-medium">Active Preset:</Text>
|
|
29
29
|
{Object.keys(presets).map((name) => (
|
|
30
30
|
<Pressable
|
|
@@ -33,7 +33,7 @@ function PresetSwitcher({
|
|
|
33
33
|
className={`px-4 py-2 rounded-md border ${
|
|
34
34
|
activePreset === name
|
|
35
35
|
? 'bg-brand-primary border-brand-primary'
|
|
36
|
-
: 'bg-surface-base border-
|
|
36
|
+
: 'bg-surface-base border-hairline web:hover:bg-interactive-hover'
|
|
37
37
|
}`}
|
|
38
38
|
>
|
|
39
39
|
<Text
|
|
@@ -272,7 +272,7 @@ function TypographyShowcase() {
|
|
|
272
272
|
</Section>
|
|
273
273
|
|
|
274
274
|
<Section title="Monospace Font (font-mono)">
|
|
275
|
-
<View className="bg-surface-elevated rounded-lg p-4 border border-
|
|
275
|
+
<View className="bg-surface-elevated rounded-lg p-4 border border-hairline">
|
|
276
276
|
<Text className="text-sm text-text-primary font-mono">
|
|
277
277
|
{'const theme = applyThemePreset(audiobookPreset)'}
|
|
278
278
|
</Text>
|
|
@@ -368,13 +368,13 @@ function ColorPaletteShowcase() {
|
|
|
368
368
|
|
|
369
369
|
<Section title="Text Colors">
|
|
370
370
|
<View className="flex-row gap-3 flex-wrap">
|
|
371
|
-
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-elevated border border-
|
|
371
|
+
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-elevated border border-hairline">
|
|
372
372
|
<Text className="text-xs font-mono font-medium text-text-primary">text-primary</Text>
|
|
373
373
|
</View>
|
|
374
|
-
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-elevated border border-
|
|
374
|
+
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-elevated border border-hairline">
|
|
375
375
|
<Text className="text-xs font-mono font-medium text-text-secondary">text-secondary</Text>
|
|
376
376
|
</View>
|
|
377
|
-
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-elevated border border-
|
|
377
|
+
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-elevated border border-hairline">
|
|
378
378
|
<Text className="text-xs font-mono font-medium text-text-tertiary">text-tertiary</Text>
|
|
379
379
|
</View>
|
|
380
380
|
</View>
|
|
@@ -382,32 +382,32 @@ function ColorPaletteShowcase() {
|
|
|
382
382
|
|
|
383
383
|
<Section title="Surface Colors">
|
|
384
384
|
<View className="flex-row gap-3 flex-wrap">
|
|
385
|
-
<ColorSwatch label="surface-base" className="bg-surface-base border border-
|
|
386
|
-
<ColorSwatch label="surface-elevated" className="bg-surface-elevated border border-
|
|
387
|
-
<ColorSwatch label="surface-raised" className="bg-surface-raised border border-
|
|
388
|
-
<ColorSwatch label="surface-overlay" className="bg-surface-overlay border border-
|
|
389
|
-
<ColorSwatch label="surface-input" className="bg-surface-input border border-
|
|
385
|
+
<ColorSwatch label="surface-base" className="bg-surface-base border border-hairline" textClass="text-text-primary" />
|
|
386
|
+
<ColorSwatch label="surface-elevated" className="bg-surface-elevated border border-hairline" textClass="text-text-primary" />
|
|
387
|
+
<ColorSwatch label="surface-raised" className="bg-surface-raised border border-hairline" textClass="text-text-primary" />
|
|
388
|
+
<ColorSwatch label="surface-overlay" className="bg-surface-overlay border border-hairline" textClass="text-text-primary" />
|
|
389
|
+
<ColorSwatch label="surface-input" className="bg-surface-input border border-hairline" textClass="text-text-primary" />
|
|
390
390
|
</View>
|
|
391
391
|
</Section>
|
|
392
392
|
|
|
393
393
|
<Section title="Background Colors">
|
|
394
394
|
<View className="flex-row gap-3 flex-wrap">
|
|
395
|
-
<ColorSwatch label="background-base" className="bg-background-base border border-
|
|
396
|
-
<ColorSwatch label="background-default" className="bg-background-default border border-
|
|
397
|
-
<ColorSwatch label="background-subtle" className="bg-background-subtle border border-
|
|
395
|
+
<ColorSwatch label="background-base" className="bg-background-base border border-hairline" textClass="text-text-primary" />
|
|
396
|
+
<ColorSwatch label="background-default" className="bg-background-default border border-hairline" textClass="text-text-primary" />
|
|
397
|
+
<ColorSwatch label="background-subtle" className="bg-background-subtle border border-hairline" textClass="text-text-primary" />
|
|
398
398
|
</View>
|
|
399
399
|
</Section>
|
|
400
400
|
|
|
401
401
|
<Section title="Border Colors">
|
|
402
402
|
<View className="flex-row gap-3 flex-wrap">
|
|
403
|
-
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-base border-2 border-
|
|
404
|
-
<Text className="text-xs font-mono font-medium text-text-primary">
|
|
403
|
+
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-base border-2 border-hairline-default">
|
|
404
|
+
<Text className="text-xs font-mono font-medium text-text-primary">hairline-default</Text>
|
|
405
405
|
</View>
|
|
406
|
-
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-base border-2 border-
|
|
407
|
-
<Text className="text-xs font-mono font-medium text-text-primary">
|
|
406
|
+
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-base border-2 border-hairline-subtle">
|
|
407
|
+
<Text className="text-xs font-mono font-medium text-text-primary">hairline-subtle</Text>
|
|
408
408
|
</View>
|
|
409
|
-
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-base border-2 border-
|
|
410
|
-
<Text className="text-xs font-mono font-medium text-text-primary">
|
|
409
|
+
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-base border-2 border-hairline-strong">
|
|
410
|
+
<Text className="text-xs font-mono font-medium text-text-primary">hairline-strong</Text>
|
|
411
411
|
</View>
|
|
412
412
|
<View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-base border-2 border-border-focus">
|
|
413
413
|
<Text className="text-xs font-mono font-medium text-text-primary">border-focus</Text>
|
|
@@ -425,7 +425,7 @@ function ColorPaletteShowcase() {
|
|
|
425
425
|
function Section({ title, children }: { title: string; children: React.ReactNode }) {
|
|
426
426
|
return (
|
|
427
427
|
<View className="gap-3">
|
|
428
|
-
<Text className="text-lg font-semibold text-text-primary font-heading border-b border-
|
|
428
|
+
<Text className="text-lg font-semibold text-text-primary font-heading border-b border-hairline-subtle pb-2">
|
|
429
429
|
{title}
|
|
430
430
|
</Text>
|
|
431
431
|
{children}
|