@umituz/web-dashboard 6.1.0 → 6.2.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/README.md +47 -73
- package/package.json +5 -9
- package/src/config/DashboardConfig.ts +19 -68
- package/src/domains/analytics/components/index.ts +0 -6
- package/src/domains/analytics/hooks/index.ts +0 -6
- package/src/domains/analytics/hooks/useAnalytics.ts +2 -20
- package/src/domains/analytics/index.ts +0 -10
- package/src/domains/analytics/services/AnalyticsEngineService.ts +7 -74
- package/src/domains/analytics/services/PerformanceService.ts +27 -138
- package/src/domains/analytics/services/index.ts +0 -4
- package/src/domains/analytics/types/analytics.ts +100 -270
- package/src/domains/analytics/types/index.ts +0 -6
- package/src/domains/analytics/utils/analytics.test.ts +2 -9
- package/src/domains/analytics/utils/analytics.ts +1 -22
- package/src/domains/analytics/utils/index.ts +0 -6
- package/src/domains/calendar/config.ts +0 -7
- package/src/domains/calendar/hooks/index.ts +0 -4
- package/src/domains/calendar/hooks/useCalendar.ts +7 -27
- package/src/domains/calendar/index.ts +0 -4
- package/src/domains/calendar/services/CalendarService.ts +0 -21
- package/src/domains/calendar/services/index.ts +0 -4
- package/src/domains/calendar/types/calendar.types.ts +9 -53
- package/src/domains/calendar/utils/contentItemMapper.test.ts +0 -4
- package/src/domains/calendar/utils/contentItemMapper.ts +0 -31
- package/src/domains/calendar/utils/index.ts +0 -26
- package/src/domains/layouts/components/index.ts +0 -4
- package/src/domains/layouts/hooks/dashboard.ts +0 -21
- package/src/domains/layouts/hooks/index.ts +0 -4
- package/src/domains/layouts/index.ts +0 -6
- package/src/domains/layouts/theme/default.ts +0 -12
- package/src/domains/layouts/theme/index.ts +0 -4
- package/src/domains/layouts/theme/presets.ts +0 -9
- package/src/domains/layouts/theme/utils.ts +0 -15
- package/src/domains/layouts/types/index.ts +0 -4
- package/src/domains/layouts/types/layout.ts +18 -56
- package/src/domains/layouts/types/notification.ts +4 -17
- package/src/domains/layouts/types/sidebar.ts +8 -28
- package/src/domains/layouts/types/theme.ts +23 -59
- package/src/domains/layouts/types/user.ts +9 -30
- package/src/domains/layouts/utils/dashboard.ts +4 -48
- package/src/domains/layouts/utils/index.ts +0 -4
- package/src/domains/modules/build.tsx +2 -16
- package/src/domains/modules/types.ts +8 -23
- package/src/domains/onboarding/calc/calc.test.ts +1 -9
- package/src/domains/onboarding/calc/card-stack-motion-curve.ts +0 -6
- package/src/domains/onboarding/calc/count-with-locale.ts +0 -8
- package/src/domains/onboarding/calc/duration-equal-split.ts +0 -12
- package/src/domains/onboarding/calc/elapsed-millis.ts +0 -6
- package/src/domains/onboarding/calc/progress-percentage.ts +1 -11
- package/src/domains/onboarding/calc/slider-value-bounds.ts +1 -11
- package/src/domains/onboarding/calc/step-index-clamp.ts +0 -9
- package/src/domains/onboarding/calc/swipe-gesture-curve.ts +0 -13
- package/src/domains/onboarding/components/{shared/AnimationWrapper.tsx → AnimationWrapper.tsx} +0 -4
- package/src/domains/onboarding/components/{shared/OnboardingErrorBoundary.tsx → OnboardingErrorBoundary.tsx} +7 -27
- package/src/domains/onboarding/components/OnboardingFlow.test.tsx +8 -30
- package/src/domains/onboarding/components/OnboardingFlow.tsx +13 -61
- package/src/domains/onboarding/components/OnboardingIntl.tsx +1 -11
- package/src/domains/onboarding/components/PrimaryButton.tsx +40 -0
- package/src/domains/onboarding/components/ProgressBar.tsx +32 -0
- package/src/domains/onboarding/components/QuestionWrapper.tsx +28 -0
- package/src/domains/onboarding/components/ScreenContainer.tsx +19 -0
- package/src/domains/onboarding/components/{shared/SwipeCard.test.tsx → SwipeCard.test.tsx} +1 -3
- package/src/domains/onboarding/components/{shared/SwipeCard.tsx → SwipeCard.tsx} +10 -28
- package/src/domains/onboarding/components/renderBundledIcon.tsx +7 -0
- package/src/domains/onboarding/components/screens/AccountGateScreen.tsx +7 -17
- package/src/domains/onboarding/components/screens/AppDemoScreen.tsx +4 -10
- package/src/domains/onboarding/components/screens/ComparisonScreen.tsx +12 -22
- package/src/domains/onboarding/components/screens/GoalQuestionScreen.tsx +9 -6
- package/src/domains/onboarding/components/screens/PainPointsScreen.tsx +10 -10
- package/src/domains/onboarding/components/screens/PaywallScreen.tsx +22 -38
- package/src/domains/onboarding/components/screens/PermissionPrimingScreen.tsx +14 -29
- package/src/domains/onboarding/components/screens/PersonalizedSolutionScreen.tsx +10 -23
- package/src/domains/onboarding/components/screens/PreferencesScreen.tsx +13 -27
- package/src/domains/onboarding/components/screens/ProcessingScreen.tsx +11 -27
- package/src/domains/onboarding/components/screens/SocialProofScreen.tsx +22 -51
- package/src/domains/onboarding/components/screens/TinderCardsScreen.tsx +13 -30
- package/src/domains/onboarding/components/screens/ValueDeliveryScreen.tsx +13 -29
- package/src/domains/onboarding/components/screens/WelcomeScreen.tsx +12 -32
- package/src/domains/onboarding/{components/hooks → hooks}/useOnboardingAnalytics.test.tsx +1 -1
- package/src/domains/onboarding/{components/hooks → hooks}/useOnboardingAnalytics.ts +3 -16
- package/src/domains/onboarding/{components/hooks → hooks}/useOnboardingProgress.test.tsx +2 -2
- package/src/domains/onboarding/{components/hooks → hooks}/useOnboardingProgress.ts +10 -21
- package/src/domains/onboarding/{components/hooks → hooks}/usePermissionPriming.ts +3 -10
- package/src/domains/onboarding/{components/hooks → hooks}/useReducedMotion.ts +0 -5
- package/src/domains/onboarding/index.ts +16 -45
- package/src/domains/onboarding/predicates/has-handler.ts +0 -4
- package/src/domains/onboarding/predicates/icon-name-resolves.ts +1 -17
- package/src/domains/onboarding/predicates/permission-granted.ts +0 -5
- package/src/domains/onboarding/predicates/permission-supports.ts +1 -11
- package/src/domains/onboarding/predicates/predicates.test.ts +6 -20
- package/src/domains/onboarding/predicates/screen-emits-data.ts +0 -4
- package/src/domains/onboarding/predicates/storage-available.ts +0 -8
- package/src/domains/onboarding/templates/TemplateDemoFallback.tsx +19 -39
- package/src/domains/onboarding/templates/art-onboarding.ts +1 -11
- package/src/domains/onboarding/templates/health-onboarding.ts +0 -5
- package/src/domains/onboarding/templates/platform-onboarding.ts +0 -9
- package/src/domains/onboarding/templates/video-onboarding.ts +0 -5
- package/src/domains/onboarding/test/setup.ts +1 -8
- package/src/domains/onboarding/types/config.ts +7 -18
- package/src/domains/onboarding/types/labels.ts +25 -59
- package/src/domains/onboarding/types/screens.ts +3 -11
- package/src/domains/onboarding/utils/analytics-dispatch.ts +2 -21
- package/src/domains/onboarding/utils/default-storage-key.ts +0 -1
- package/src/domains/onboarding/utils/index.ts +0 -1
- package/src/domains/onboarding/utils/local-storage-bridge.ts +1 -21
- package/src/domains/onboarding/utils/permission-bridge.ts +0 -8
- package/src/domains/onboarding/validation/flow-structure-check.ts +0 -6
- package/src/domains/onboarding/validation/permission-status-check.ts +0 -5
- package/src/domains/onboarding/validation/screen-config-check.ts +0 -5
- package/src/domains/onboarding/validation/stored-progress-check.ts +1 -10
- package/src/domains/onboarding/validation/validation.test.ts +1 -6
- package/src/domains/permissions/hasPermission.ts +0 -8
- package/src/domains/permissions/types.ts +0 -20
- package/src/domains/settings/components/index.ts +0 -4
- package/src/domains/settings/hooks/index.ts +0 -4
- package/src/domains/settings/hooks/useSettings.ts +1 -26
- package/src/domains/settings/index.ts +0 -6
- package/src/domains/settings/types/index.ts +0 -4
- package/src/domains/settings/types/settings.ts +23 -67
- package/src/domains/settings/utils/index.ts +0 -4
- package/src/domains/settings/utils/settings.ts +0 -45
- package/src/styles/dashboard.css +818 -2
- package/src/domains/onboarding/components/shared/OptionCard.tsx +0 -73
- package/src/domains/onboarding/components/shared/PrimaryButton.tsx +0 -58
- package/src/domains/onboarding/components/shared/ProgressBar.tsx +0 -51
- package/src/domains/onboarding/components/shared/QuestionWrapper.tsx +0 -36
- package/src/domains/onboarding/components/shared/ScreenContainer.tsx +0 -29
- package/src/domains/onboarding/components/shared/index.ts +0 -16
- package/src/domains/onboarding/components/shared/renderBundledIcon.tsx +0 -20
- package/src/domains/onboarding/onboarding.css +0 -1
- package/src/domains/onboarding/theme/cn.ts +0 -10
- package/src/domains/onboarding/theme/index.css +0 -131
- package/src/domains/onboarding/theme/index.ts +0 -12
- package/src/domains/onboarding/theme/tokens.ts +0 -122
- /package/src/domains/onboarding/{components/hooks → hooks}/index.ts +0 -0
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { motion } from 'framer-motion';
|
|
2
|
-
import { cn } from '../../theme/cn';
|
|
3
|
-
import { renderBundledIcon } from './renderBundledIcon';
|
|
4
|
-
import { doesIconNameResolve } from '../../predicates/icon-name-resolves';
|
|
5
|
-
import type { OnboardingOption } from '../../types/screens';
|
|
6
|
-
|
|
7
|
-
export interface OptionCardProps {
|
|
8
|
-
option: OnboardingOption;
|
|
9
|
-
selected: boolean;
|
|
10
|
-
onToggle: (id: string) => void;
|
|
11
|
-
multiSelect?: boolean;
|
|
12
|
-
className?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Selectable option card with optional icon.
|
|
17
|
-
* Single responsibility: render one option and emit toggle events.
|
|
18
|
-
*/
|
|
19
|
-
export function OptionCard({ option, selected, onToggle, multiSelect = false, className }: OptionCardProps) {
|
|
20
|
-
return (
|
|
21
|
-
<motion.button
|
|
22
|
-
type="button"
|
|
23
|
-
role={multiSelect ? 'checkbox' : 'radio'}
|
|
24
|
-
aria-checked={selected}
|
|
25
|
-
onClick={() => onToggle(option.id)}
|
|
26
|
-
whileTap={{ scale: 0.98 }}
|
|
27
|
-
className={cn(
|
|
28
|
-
'flex w-full items-center gap-3 rounded-xl border p-4 text-left transition-colors',
|
|
29
|
-
'focus:outline-none focus:ring-2 focus:ring-[rgb(var(--uob-primary-500))] focus:ring-offset-2',
|
|
30
|
-
selected
|
|
31
|
-
? 'border-[rgb(var(--uob-primary-500))] bg-[rgb(var(--uob-primary-50))]'
|
|
32
|
-
: 'border-[rgb(var(--uob-border))] bg-[rgb(var(--uob-surface))] hover:border-[rgb(var(--uob-primary-100))]',
|
|
33
|
-
className,
|
|
34
|
-
)}
|
|
35
|
-
>
|
|
36
|
-
{doesIconNameResolve(option.iconName) && (
|
|
37
|
-
<span
|
|
38
|
-
className={cn(
|
|
39
|
-
'flex h-10 w-10 shrink-0 items-center justify-center rounded-lg',
|
|
40
|
-
selected
|
|
41
|
-
? 'bg-[rgb(var(--uob-primary-500))] text-white'
|
|
42
|
-
: 'bg-[rgb(var(--uob-surface-muted))] text-[rgb(var(--uob-text-muted))]',
|
|
43
|
-
)}
|
|
44
|
-
>
|
|
45
|
-
{renderBundledIcon(option.iconName, 'h-5 w-5')}
|
|
46
|
-
</span>
|
|
47
|
-
)}
|
|
48
|
-
<span className="flex flex-1 flex-col">
|
|
49
|
-
<span className="text-base font-medium text-[rgb(var(--uob-text-primary))]">
|
|
50
|
-
{option.label}
|
|
51
|
-
</span>
|
|
52
|
-
{option.description && (
|
|
53
|
-
<span className="text-sm text-[rgb(var(--uob-text-muted))]">{option.description}</span>
|
|
54
|
-
)}
|
|
55
|
-
</span>
|
|
56
|
-
<span
|
|
57
|
-
className={cn(
|
|
58
|
-
'flex h-5 w-5 shrink-0 items-center justify-center rounded-full border-2',
|
|
59
|
-
selected
|
|
60
|
-
? 'border-[rgb(var(--uob-primary-500))] bg-[rgb(var(--uob-primary-500))] text-white'
|
|
61
|
-
: 'border-[rgb(var(--uob-border))]',
|
|
62
|
-
)}
|
|
63
|
-
aria-hidden="true"
|
|
64
|
-
>
|
|
65
|
-
{selected && (
|
|
66
|
-
<svg viewBox="0 0 12 12" className="h-3 w-3" fill="none" stroke="currentColor" strokeWidth="2">
|
|
67
|
-
<path d="M2 6 L5 9 L10 3" />
|
|
68
|
-
</svg>
|
|
69
|
-
)}
|
|
70
|
-
</span>
|
|
71
|
-
</motion.button>
|
|
72
|
-
);
|
|
73
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import type { ButtonHTMLAttributes, ReactNode } from 'react';
|
|
2
|
-
import { motion, type HTMLMotionProps } from 'framer-motion';
|
|
3
|
-
import { cn } from '../../theme/cn';
|
|
4
|
-
import { useOnboardingIntl } from '../OnboardingIntl';
|
|
5
|
-
|
|
6
|
-
export interface PrimaryButtonProps
|
|
7
|
-
extends Omit<HTMLMotionProps<'button'>, 'children'> {
|
|
8
|
-
children: ReactNode;
|
|
9
|
-
loading?: boolean;
|
|
10
|
-
fullWidth?: boolean;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Primary CTA button used across the onboarding flow.
|
|
15
|
-
* Single responsibility: render a tappable, accessible, animated button.
|
|
16
|
-
*/
|
|
17
|
-
export function PrimaryButton({
|
|
18
|
-
children,
|
|
19
|
-
loading = false,
|
|
20
|
-
fullWidth = true,
|
|
21
|
-
className,
|
|
22
|
-
disabled,
|
|
23
|
-
type,
|
|
24
|
-
...rest
|
|
25
|
-
}: PrimaryButtonProps) {
|
|
26
|
-
const { labels } = useOnboardingIntl();
|
|
27
|
-
return (
|
|
28
|
-
<motion.button
|
|
29
|
-
{...rest}
|
|
30
|
-
type={type ?? 'button'}
|
|
31
|
-
disabled={disabled || loading}
|
|
32
|
-
whileTap={{ scale: disabled || loading ? 1 : 0.98 }}
|
|
33
|
-
className={cn(
|
|
34
|
-
'inline-flex items-center justify-center rounded-xl px-5 py-3 text-base font-semibold',
|
|
35
|
-
'bg-[rgb(var(--uob-primary-500))] text-white shadow-sm transition-colors',
|
|
36
|
-
'hover:bg-[rgb(var(--uob-primary-600))] focus:outline-none focus:ring-2 focus:ring-[rgb(var(--uob-primary-500))] focus:ring-offset-2',
|
|
37
|
-
'disabled:cursor-not-allowed disabled:opacity-60',
|
|
38
|
-
fullWidth ? 'w-full' : '',
|
|
39
|
-
className,
|
|
40
|
-
)}
|
|
41
|
-
>
|
|
42
|
-
{loading ? (
|
|
43
|
-
<span className="flex items-center gap-2">
|
|
44
|
-
<span
|
|
45
|
-
className="h-4 w-4 animate-spin rounded-full border-2 border-white border-t-transparent"
|
|
46
|
-
aria-hidden="true"
|
|
47
|
-
/>
|
|
48
|
-
<span>{labels.loading}</span>
|
|
49
|
-
</span>
|
|
50
|
-
) : (
|
|
51
|
-
children
|
|
52
|
-
)}
|
|
53
|
-
</motion.button>
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Re-export the HTML button type for consumers that need to wrap a non-motion button.
|
|
58
|
-
export type { ButtonHTMLAttributes };
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import * as Progress from '@radix-ui/react-progress';
|
|
2
|
-
import { motion } from 'framer-motion';
|
|
3
|
-
import { cn } from '../../theme/cn';
|
|
4
|
-
import { computeProgressPercentage } from '../../calc/progress-percentage';
|
|
5
|
-
import { formatLabel, useOnboardingIntl } from '../OnboardingIntl';
|
|
6
|
-
|
|
7
|
-
export interface ProgressBarProps {
|
|
8
|
-
current: number;
|
|
9
|
-
total: number;
|
|
10
|
-
className?: string;
|
|
11
|
-
showLabel?: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Accessible progress bar built on Radix UI primitive.
|
|
16
|
-
* Progress math lives in `calc/progress-percentage`.
|
|
17
|
-
*/
|
|
18
|
-
export function ProgressBar({ current, total, className, showLabel = false }: ProgressBarProps) {
|
|
19
|
-
const { labels } = useOnboardingIntl();
|
|
20
|
-
const safeTotal = Math.max(1, total);
|
|
21
|
-
const safeCurrent = Math.max(0, Math.min(current, safeTotal));
|
|
22
|
-
const percentage = computeProgressPercentage(safeCurrent, safeTotal);
|
|
23
|
-
|
|
24
|
-
return (
|
|
25
|
-
<div className={cn('flex w-full flex-col gap-2', className)}>
|
|
26
|
-
{showLabel && (
|
|
27
|
-
<div className="flex items-center justify-between text-sm text-[rgb(var(--uob-text-muted))]">
|
|
28
|
-
<span aria-live="polite">
|
|
29
|
-
{formatLabel(labels.stepOfPattern, { current: safeCurrent, total: safeTotal })}
|
|
30
|
-
</span>
|
|
31
|
-
<span aria-hidden="true">{Math.round(percentage)}%</span>
|
|
32
|
-
</div>
|
|
33
|
-
)}
|
|
34
|
-
<Progress.Root
|
|
35
|
-
value={percentage}
|
|
36
|
-
aria-label={`Onboarding progress: ${Math.round(percentage)} percent`}
|
|
37
|
-
aria-valuemin={0}
|
|
38
|
-
aria-valuemax={100}
|
|
39
|
-
aria-valuenow={Math.round(percentage)}
|
|
40
|
-
className="relative h-2 w-full overflow-hidden rounded-full bg-[rgb(var(--uob-border))]"
|
|
41
|
-
>
|
|
42
|
-
<motion.div
|
|
43
|
-
initial={{ width: 0 }}
|
|
44
|
-
animate={{ width: `${percentage}%` }}
|
|
45
|
-
transition={{ duration: 0.4, ease: 'easeOut' }}
|
|
46
|
-
className="h-full rounded-full bg-[rgb(var(--uob-primary-500))]"
|
|
47
|
-
/>
|
|
48
|
-
</Progress.Root>
|
|
49
|
-
</div>
|
|
50
|
-
);
|
|
51
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
import { motion } from 'framer-motion';
|
|
3
|
-
import { cn } from '../../theme/cn';
|
|
4
|
-
|
|
5
|
-
export interface QuestionWrapperProps {
|
|
6
|
-
title: string;
|
|
7
|
-
subtitle?: string;
|
|
8
|
-
children: ReactNode;
|
|
9
|
-
className?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Consistent framing for question-style screens.
|
|
14
|
-
* Single responsibility: layout the question header + content area.
|
|
15
|
-
*/
|
|
16
|
-
export function QuestionWrapper({ title, subtitle, children, className }: QuestionWrapperProps) {
|
|
17
|
-
return (
|
|
18
|
-
<motion.div
|
|
19
|
-
initial={{ opacity: 0, y: 8 }}
|
|
20
|
-
animate={{ opacity: 1, y: 0 }}
|
|
21
|
-
exit={{ opacity: 0, y: -8 }}
|
|
22
|
-
transition={{ duration: 0.25, ease: 'easeOut' }}
|
|
23
|
-
className={cn('flex w-full flex-col gap-6', className)}
|
|
24
|
-
>
|
|
25
|
-
<header className="flex flex-col gap-2">
|
|
26
|
-
<h2 className="text-2xl font-semibold text-[rgb(var(--uob-text-primary))] sm:text-3xl">
|
|
27
|
-
{title}
|
|
28
|
-
</h2>
|
|
29
|
-
{subtitle && (
|
|
30
|
-
<p className="text-base text-[rgb(var(--uob-text-muted))]">{subtitle}</p>
|
|
31
|
-
)}
|
|
32
|
-
</header>
|
|
33
|
-
<div className="flex flex-col gap-3">{children}</div>
|
|
34
|
-
</motion.div>
|
|
35
|
-
);
|
|
36
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
import { cn } from '../../theme/cn';
|
|
3
|
-
|
|
4
|
-
export interface ScreenContainerProps {
|
|
5
|
-
children: ReactNode;
|
|
6
|
-
header?: ReactNode;
|
|
7
|
-
footer?: ReactNode;
|
|
8
|
-
className?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Standard layout for an onboarding screen.
|
|
13
|
-
* Owns safe-area, padding, header/footer slots. Per project rules this is the SOLE
|
|
14
|
-
* layout for authenticated onboarding screens — no per-page layout duplication.
|
|
15
|
-
*/
|
|
16
|
-
export function ScreenContainer({ children, header, footer, className }: ScreenContainerProps) {
|
|
17
|
-
return (
|
|
18
|
-
<div
|
|
19
|
-
className={cn(
|
|
20
|
-
'mx-auto flex min-h-screen w-full max-w-2xl flex-col gap-6 px-4 pb-8 pt-6 sm:px-6',
|
|
21
|
-
className,
|
|
22
|
-
)}
|
|
23
|
-
>
|
|
24
|
-
{header && <div className="flex flex-col gap-3">{header}</div>}
|
|
25
|
-
<main className="flex flex-1 flex-col gap-6">{children}</main>
|
|
26
|
-
{footer && <div className="flex flex-col gap-3">{footer}</div>}
|
|
27
|
-
</div>
|
|
28
|
-
);
|
|
29
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export { ProgressBar } from './ProgressBar';
|
|
2
|
-
export type { ProgressBarProps } from './ProgressBar';
|
|
3
|
-
export { QuestionWrapper } from './QuestionWrapper';
|
|
4
|
-
export type { QuestionWrapperProps } from './QuestionWrapper';
|
|
5
|
-
export { OptionCard } from './OptionCard';
|
|
6
|
-
export type { OptionCardProps } from './OptionCard';
|
|
7
|
-
export { SwipeCard } from './SwipeCard';
|
|
8
|
-
export type { SwipeCardProps } from './SwipeCard';
|
|
9
|
-
export { AnimationWrapper } from './AnimationWrapper';
|
|
10
|
-
export type { AnimationWrapperProps, ScreenTransition } from './AnimationWrapper';
|
|
11
|
-
export { ScreenContainer } from './ScreenContainer';
|
|
12
|
-
export type { ScreenContainerProps } from './ScreenContainer';
|
|
13
|
-
export { PrimaryButton } from './PrimaryButton';
|
|
14
|
-
export type { PrimaryButtonProps } from './PrimaryButton';
|
|
15
|
-
export { OnboardingErrorBoundary } from './OnboardingErrorBoundary';
|
|
16
|
-
export type { OnboardingErrorBoundaryProps } from './OnboardingErrorBoundary';
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { resolveIconByName } from '../../predicates/icon-name-resolves';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Render a bundled icon by registry name.
|
|
5
|
-
*
|
|
6
|
-
* Returns null when the name is not in the registry — callers combine it with
|
|
7
|
-
* `doesIconNameResolve` when the surrounding markup must disappear with the icon.
|
|
8
|
-
*
|
|
9
|
-
* Why a helper instead of `const Icon = resolveIconByName(name)` inside the
|
|
10
|
-
* component body? The registry lookup always yields a stable module-level
|
|
11
|
-
* component (nothing is created per render), but assigning it to a variable
|
|
12
|
-
* during render trips eslint-plugin-react-hooks' `static-components` rule at
|
|
13
|
-
* the JSX usage site. A module-scope function returning the element keeps the
|
|
14
|
-
* lint signal meaningful without suppression comments.
|
|
15
|
-
*/
|
|
16
|
-
export function renderBundledIcon(name: string | undefined, className?: string) {
|
|
17
|
-
const Icon = resolveIconByName(name);
|
|
18
|
-
if (!Icon) return null;
|
|
19
|
-
return <Icon className={className} aria-hidden="true" />;
|
|
20
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.uob-flow{--uob-font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--uob-primary-50: 238 242 255;--uob-primary-100: 224 231 255;--uob-primary-500: 99 102 241;--uob-primary-600: 79 70 229;--uob-primary-700: 67 56 202;--uob-surface: 255 255 255;--uob-surface-muted: 249 250 251;--uob-text-primary: 17 24 39;--uob-text-muted: 107 114 128;--uob-border: 229 231 235;--uob-success: 16 185 129;--uob-warning: 245 158 11;--uob-error: 239 68 68}@media (prefers-color-scheme: dark){.uob-flow{--uob-surface: 17 24 39;--uob-surface-muted: 31 41 55;--uob-text-primary: 243 244 246;--uob-text-muted: 156 163 175;--uob-border: 55 65 81}}.uob-flow,.uob-flow *,.uob-flow *:before,.uob-flow *:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:rgb(var(--uob-border))}.uob-flow h1,.uob-flow h2,.uob-flow h3,.uob-flow h4,.uob-flow h5,.uob-flow h6,.uob-flow p,.uob-flow figure,.uob-flow blockquote,.uob-flow pre{margin:0}.uob-flow ol,.uob-flow ul{list-style:none;margin:0;padding:0}.uob-flow button,.uob-flow input,.uob-flow optgroup,.uob-flow select,.uob-flow textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}.uob-flow button,.uob-flow [type=button],.uob-flow [type=reset],.uob-flow [type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}.uob-flow progress{-webkit-appearance:none;-moz-appearance:none;appearance:none}.uob-flow img,.uob-flow svg,.uob-flow video,.uob-flow canvas{display:block;vertical-align:middle}.uob-flow img,.uob-flow video{max-width:100%;height:auto}.pointer-events-none{pointer-events:none}.\!visible{visibility:visible!important}.visible{visibility:visible}.static{position:static}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.left-4{left:1rem}.right-4{right:1rem}.top-4{top:1rem}.mx-auto{margin-left:auto;margin-right:auto}.mb-4{margin-bottom:1rem}.ml-auto{margin-left:auto}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-3{margin-top:.75rem}.mt-6{margin-top:1.5rem}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-20{height:5rem}.h-24{height:6rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-32{height:8rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-72{height:18rem}.h-8{height:2rem}.h-full{height:100%}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-11{width:2.75rem}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-20{width:5rem}.w-24{width:6rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-full{width:100%}.max-w-2xl{max-width:42rem}.max-w-md{max-width:28rem}.max-w-xl{max-width:36rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-5{--tw-translate-x: 1.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-grab{cursor:grab}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-3xl{border-radius:1.5rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-\[rgb\(var\(--uob-border\)\)\]{border-color:rgb(var(--uob-border))}.border-\[rgb\(var\(--uob-error\)\)\]{border-color:rgb(var(--uob-error))}.border-\[rgb\(var\(--uob-primary-500\)\)\]{border-color:rgb(var(--uob-primary-500))}.border-\[rgb\(var\(--uob-success\)\)\]{border-color:rgb(var(--uob-success))}.border-transparent{border-color:transparent}.border-white{--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity, 1))}.border-t-transparent{border-top-color:transparent}.bg-\[rgb\(var\(--uob-border\)\)\]{background-color:rgb(var(--uob-border))}.bg-\[rgb\(var\(--uob-error\)\)\]\/10{background-color:rgb(var(--uob-error) / .1)}.bg-\[rgb\(var\(--uob-primary-50\)\)\]{background-color:rgb(var(--uob-primary-50))}.bg-\[rgb\(var\(--uob-primary-500\)\)\]{background-color:rgb(var(--uob-primary-500))}.bg-\[rgb\(var\(--uob-success\)\)\]{background-color:rgb(var(--uob-success))}.bg-\[rgb\(var\(--uob-success\)\)\]\/10{background-color:rgb(var(--uob-success) / .1)}.bg-\[rgb\(var\(--uob-surface\)\)\]{background-color:rgb(var(--uob-surface))}.bg-\[rgb\(var\(--uob-surface-muted\)\)\]{background-color:rgb(var(--uob-surface-muted))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.fill-current{fill:currentColor}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0\.5{padding:.125rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pb-8{padding-bottom:2rem}.pt-6{padding-top:1.5rem}.text-left{text-align:left}.text-center{text-align:center}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.not-italic{font-style:normal}.tracking-wide{letter-spacing:.025em}.text-\[rgb\(var\(--uob-border\)\)\]{color:rgb(var(--uob-border))}.text-\[rgb\(var\(--uob-error\)\)\]{color:rgb(var(--uob-error))}.text-\[rgb\(var\(--uob-primary-500\)\)\]{color:rgb(var(--uob-primary-500))}.text-\[rgb\(var\(--uob-primary-700\)\)\]{color:rgb(var(--uob-primary-700))}.text-\[rgb\(var\(--uob-success\)\)\]{color:rgb(var(--uob-success))}.text-\[rgb\(var\(--uob-text-muted\)\)\]{color:rgb(var(--uob-text-muted))}.text-\[rgb\(var\(--uob-text-primary\)\)\]{color:rgb(var(--uob-text-primary))}.text-\[rgb\(var\(--uob-warning\)\)\]{color:rgb(var(--uob-warning))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.underline-offset-4{text-underline-offset:4px}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.hover\:border-\[rgb\(var\(--uob-border\)\)\]:hover{border-color:rgb(var(--uob-border))}.hover\:border-\[rgb\(var\(--uob-primary-100\)\)\]:hover{border-color:rgb(var(--uob-primary-100))}.hover\:border-\[rgb\(var\(--uob-primary-500\)\)\]:hover{border-color:rgb(var(--uob-primary-500))}.hover\:bg-\[rgb\(var\(--uob-error\)\)\]:hover{background-color:rgb(var(--uob-error))}.hover\:bg-\[rgb\(var\(--uob-primary-600\)\)\]:hover{background-color:rgb(var(--uob-primary-600))}.hover\:bg-\[rgb\(var\(--uob-success\)\)\]:hover{background-color:rgb(var(--uob-success))}.hover\:bg-\[rgb\(var\(--uob-surface-muted\)\)\]:hover{background-color:rgb(var(--uob-surface-muted))}.hover\:text-white:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.hover\:underline:hover{text-decoration-line:underline}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-\[rgb\(var\(--uob-primary-500\)\)\]:focus{--tw-ring-color: rgb(var(--uob-primary-500))}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.active\:cursor-grabbing:active{cursor:grabbing}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}@media (min-width: 640px){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:text-3xl{font-size:1.875rem;line-height:2.25rem}.sm\:text-4xl{font-size:2.25rem;line-height:2.5rem}.sm\:text-5xl{font-size:3rem;line-height:1}}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { clsx, type ClassValue } from 'clsx';
|
|
2
|
-
import { twMerge } from 'tailwind-merge';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Class name composer — merges Tailwind utility classes safely.
|
|
6
|
-
* Single responsibility: build a className string without conflicts.
|
|
7
|
-
*/
|
|
8
|
-
export function cn(...inputs: ClassValue[]): string {
|
|
9
|
-
return twMerge(clsx(inputs));
|
|
10
|
-
}
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* web-onboarding styles.
|
|
3
|
-
*
|
|
4
|
-
* Isolation contract (see README "Styling"):
|
|
5
|
-
* - NO Tailwind preflight. Host applications keep their own global reset;
|
|
6
|
-
* this stylesheet must never change styles outside `.uob-flow`.
|
|
7
|
-
* - A scoped reset restores the preflight invariants the library's utility
|
|
8
|
-
* classes rely on (box-sizing, border-style, form font inheritance)
|
|
9
|
-
* inside `.uob-flow` only.
|
|
10
|
-
* - All design tokens are namespaced `--uob-*`. Rebrand by overriding them:
|
|
11
|
-
* .uob-flow { --uob-primary-500: 34 197 94; }
|
|
12
|
-
* RGB triplets (e.g. "99 102 241"), not hex — they compose with
|
|
13
|
-
* `rgb(var(--uob-primary-500) / <alpha>)` and Tailwind `<alpha-value>`.
|
|
14
|
-
* - OnboardingFlow renders the `.uob-flow` wrapper automatically.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
/* ------------------------------------------------------------------ */
|
|
18
|
-
/* Design tokens (light defaults) */
|
|
19
|
-
/* ------------------------------------------------------------------ */
|
|
20
|
-
|
|
21
|
-
.uob-flow {
|
|
22
|
-
--uob-font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
|
23
|
-
'Segoe UI', Roboto, sans-serif;
|
|
24
|
-
|
|
25
|
-
--uob-primary-50: 238 242 255;
|
|
26
|
-
--uob-primary-100: 224 231 255;
|
|
27
|
-
--uob-primary-500: 99 102 241;
|
|
28
|
-
--uob-primary-600: 79 70 229;
|
|
29
|
-
--uob-primary-700: 67 56 202;
|
|
30
|
-
|
|
31
|
-
--uob-surface: 255 255 255;
|
|
32
|
-
--uob-surface-muted: 249 250 251;
|
|
33
|
-
--uob-text-primary: 17 24 39;
|
|
34
|
-
--uob-text-muted: 107 114 128;
|
|
35
|
-
--uob-border: 229 231 235;
|
|
36
|
-
|
|
37
|
-
--uob-success: 16 185 129;
|
|
38
|
-
--uob-warning: 245 158 11;
|
|
39
|
-
--uob-error: 239 68 68;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
@media (prefers-color-scheme: dark) {
|
|
43
|
-
.uob-flow {
|
|
44
|
-
--uob-surface: 17 24 39;
|
|
45
|
-
--uob-surface-muted: 31 41 55;
|
|
46
|
-
--uob-text-primary: 243 244 246;
|
|
47
|
-
--uob-text-muted: 156 163 175;
|
|
48
|
-
--uob-border: 55 65 81;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/* ------------------------------------------------------------------ */
|
|
53
|
-
/* Scoped reset — restores preflight invariants inside `.uob-flow` */
|
|
54
|
-
/* only. Deliberately emitted BEFORE the utilities layer so utility */
|
|
55
|
-
/* classes always win on equal specificity. */
|
|
56
|
-
/* ------------------------------------------------------------------ */
|
|
57
|
-
|
|
58
|
-
.uob-flow,
|
|
59
|
-
.uob-flow *,
|
|
60
|
-
.uob-flow *::before,
|
|
61
|
-
.uob-flow *::after {
|
|
62
|
-
box-sizing: border-box;
|
|
63
|
-
border-width: 0;
|
|
64
|
-
border-style: solid;
|
|
65
|
-
border-color: rgb(var(--uob-border));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.uob-flow h1,
|
|
69
|
-
.uob-flow h2,
|
|
70
|
-
.uob-flow h3,
|
|
71
|
-
.uob-flow h4,
|
|
72
|
-
.uob-flow h5,
|
|
73
|
-
.uob-flow h6,
|
|
74
|
-
.uob-flow p,
|
|
75
|
-
.uob-flow figure,
|
|
76
|
-
.uob-flow blockquote,
|
|
77
|
-
.uob-flow pre {
|
|
78
|
-
margin: 0;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.uob-flow ol,
|
|
82
|
-
.uob-flow ul {
|
|
83
|
-
list-style: none;
|
|
84
|
-
margin: 0;
|
|
85
|
-
padding: 0;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
.uob-flow button,
|
|
89
|
-
.uob-flow input,
|
|
90
|
-
.uob-flow optgroup,
|
|
91
|
-
.uob-flow select,
|
|
92
|
-
.uob-flow textarea {
|
|
93
|
-
font-family: inherit;
|
|
94
|
-
font-size: 100%;
|
|
95
|
-
font-weight: inherit;
|
|
96
|
-
line-height: inherit;
|
|
97
|
-
color: inherit;
|
|
98
|
-
margin: 0;
|
|
99
|
-
padding: 0;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
.uob-flow button,
|
|
103
|
-
.uob-flow [type='button'],
|
|
104
|
-
.uob-flow [type='reset'],
|
|
105
|
-
.uob-flow [type='submit'] {
|
|
106
|
-
-webkit-appearance: button;
|
|
107
|
-
background-color: transparent;
|
|
108
|
-
background-image: none;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
.uob-flow progress {
|
|
112
|
-
-webkit-appearance: none;
|
|
113
|
-
appearance: none;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
.uob-flow img,
|
|
117
|
-
.uob-flow svg,
|
|
118
|
-
.uob-flow video,
|
|
119
|
-
.uob-flow canvas {
|
|
120
|
-
display: block;
|
|
121
|
-
vertical-align: middle;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
.uob-flow img,
|
|
125
|
-
.uob-flow video {
|
|
126
|
-
max-width: 100%;
|
|
127
|
-
height: auto;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/* Utilities LAST — emitted in place, after the scoped reset. */
|
|
131
|
-
@tailwind utilities;
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Design Tokens - Single Source of Truth
|
|
3
|
-
* All visual values MUST be sourced from here.
|
|
4
|
-
* Per project rules: no hardcoded colors, spacing, or font sizes anywhere else.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export const spacing = {
|
|
8
|
-
px: '1px',
|
|
9
|
-
0: '0',
|
|
10
|
-
0.5: '0.125rem',
|
|
11
|
-
1: '0.25rem',
|
|
12
|
-
1.5: '0.375rem',
|
|
13
|
-
2: '0.5rem',
|
|
14
|
-
2.5: '0.625rem',
|
|
15
|
-
3: '0.75rem',
|
|
16
|
-
3.5: '0.875rem',
|
|
17
|
-
4: '1rem',
|
|
18
|
-
5: '1.25rem',
|
|
19
|
-
6: '1.5rem',
|
|
20
|
-
7: '1.75rem',
|
|
21
|
-
8: '2rem',
|
|
22
|
-
10: '2.5rem',
|
|
23
|
-
12: '3rem',
|
|
24
|
-
16: '4rem',
|
|
25
|
-
20: '5rem',
|
|
26
|
-
24: '6rem',
|
|
27
|
-
32: '8rem',
|
|
28
|
-
} as const;
|
|
29
|
-
|
|
30
|
-
export const radius = {
|
|
31
|
-
none: '0',
|
|
32
|
-
sm: '0.25rem',
|
|
33
|
-
md: '0.5rem',
|
|
34
|
-
lg: '0.75rem',
|
|
35
|
-
xl: '1rem',
|
|
36
|
-
'2xl': '1.5rem',
|
|
37
|
-
'3xl': '2rem',
|
|
38
|
-
full: '9999px',
|
|
39
|
-
} as const;
|
|
40
|
-
|
|
41
|
-
export const typography = {
|
|
42
|
-
fontFamily: {
|
|
43
|
-
sans: 'var(--uob-font-sans, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif)',
|
|
44
|
-
},
|
|
45
|
-
fontSize: {
|
|
46
|
-
xs: ['0.75rem', { lineHeight: '1rem' }],
|
|
47
|
-
sm: ['0.875rem', { lineHeight: '1.25rem' }],
|
|
48
|
-
base: ['1rem', { lineHeight: '1.5rem' }],
|
|
49
|
-
lg: ['1.125rem', { lineHeight: '1.75rem' }],
|
|
50
|
-
xl: ['1.25rem', { lineHeight: '1.75rem' }],
|
|
51
|
-
'2xl': ['1.5rem', { lineHeight: '2rem' }],
|
|
52
|
-
'3xl': ['1.875rem', { lineHeight: '2.25rem' }],
|
|
53
|
-
'4xl': ['2.25rem', { lineHeight: '2.5rem' }],
|
|
54
|
-
'5xl': ['3rem', { lineHeight: '1.15' }],
|
|
55
|
-
'6xl': ['3.75rem', { lineHeight: '1.1' }],
|
|
56
|
-
},
|
|
57
|
-
fontWeight: {
|
|
58
|
-
normal: '400',
|
|
59
|
-
medium: '500',
|
|
60
|
-
semibold: '600',
|
|
61
|
-
bold: '700',
|
|
62
|
-
},
|
|
63
|
-
} as const;
|
|
64
|
-
|
|
65
|
-
export const zIndex = {
|
|
66
|
-
hide: -1,
|
|
67
|
-
base: 0,
|
|
68
|
-
raised: 10,
|
|
69
|
-
sticky: 20,
|
|
70
|
-
overlay: 30,
|
|
71
|
-
modal: 40,
|
|
72
|
-
toast: 50,
|
|
73
|
-
} as const;
|
|
74
|
-
|
|
75
|
-
export const motion = {
|
|
76
|
-
duration: {
|
|
77
|
-
fast: '150ms',
|
|
78
|
-
normal: '250ms',
|
|
79
|
-
slow: '400ms',
|
|
80
|
-
slower: '600ms',
|
|
81
|
-
},
|
|
82
|
-
easing: {
|
|
83
|
-
linear: 'linear',
|
|
84
|
-
in: 'cubic-bezier(0.4, 0, 1, 1)',
|
|
85
|
-
out: 'cubic-bezier(0, 0, 0.2, 1)',
|
|
86
|
-
inOut: 'cubic-bezier(0.4, 0, 0.2, 1)',
|
|
87
|
-
},
|
|
88
|
-
} as const;
|
|
89
|
-
|
|
90
|
-
export const breakpoint = {
|
|
91
|
-
sm: '640px',
|
|
92
|
-
md: '768px',
|
|
93
|
-
lg: '1024px',
|
|
94
|
-
xl: '1280px',
|
|
95
|
-
'2xl': '1536px',
|
|
96
|
-
} as const;
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Semantic color tokens — referenced via CSS custom properties.
|
|
100
|
-
* Consumers override --uob-primary-500 etc. on `.uob-flow` to rebrand
|
|
101
|
-
* without code changes. Values are space-separated RGB triplets so they
|
|
102
|
-
* compose with `rgb(var(--x) / <alpha>)`.
|
|
103
|
-
*/
|
|
104
|
-
export const colorCssVar = {
|
|
105
|
-
primary50: 'var(--uob-primary-50, 238 242 255)',
|
|
106
|
-
primary100: 'var(--uob-primary-100, 224 231 255)',
|
|
107
|
-
primary500: 'var(--uob-primary-500, 99 102 241)',
|
|
108
|
-
primary600: 'var(--uob-primary-600, 79 70 229)',
|
|
109
|
-
primary700: 'var(--uob-primary-700, 67 56 202)',
|
|
110
|
-
surface: 'var(--uob-surface, 255 255 255)',
|
|
111
|
-
surfaceMuted: 'var(--uob-surface-muted, 249 250 251)',
|
|
112
|
-
textPrimary: 'var(--uob-text-primary, 17 24 39)',
|
|
113
|
-
textMuted: 'var(--uob-text-muted, 107 114 128)',
|
|
114
|
-
border: 'var(--uob-border, 229 231 235)',
|
|
115
|
-
success: 'var(--uob-success, 16 185 129)',
|
|
116
|
-
warning: 'var(--uob-warning, 245 158 11)',
|
|
117
|
-
error: 'var(--uob-error, 239 68 68)',
|
|
118
|
-
} as const;
|
|
119
|
-
|
|
120
|
-
export type SpacingToken = keyof typeof spacing;
|
|
121
|
-
export type RadiusToken = keyof typeof radius;
|
|
122
|
-
export type FontSizeToken = keyof typeof typography.fontSize;
|
|
File without changes
|