@djangocfg/layouts 2.1.334 → 2.1.336
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 +21 -1
- package/package.json +17 -17
- package/src/configurator/auth/defaults.ts +101 -0
- package/src/configurator/auth/index.ts +36 -0
- package/src/configurator/auth/schema.ts +205 -0
- package/src/configurator/auth/types.ts +48 -0
- package/src/configurator/index.ts +2 -0
- package/src/layouts/AppLayout/BaseApp.tsx +2 -1
- package/src/layouts/AuthLayout/AuthLayout.tsx +39 -30
- package/src/layouts/AuthLayout/README.md +77 -3
- package/src/layouts/AuthLayout/index.ts +12 -0
- package/src/layouts/AuthLayout/shells/AuthShell.tsx +80 -0
- package/src/layouts/AuthLayout/shells/CenteredShell.tsx +46 -0
- package/src/layouts/AuthLayout/shells/SplitShell.tsx +63 -0
- package/src/layouts/AuthLayout/shells/context.tsx +25 -0
- package/src/layouts/AuthLayout/shells/index.ts +9 -0
- package/src/layouts/AuthLayout/shells/types.ts +67 -0
- package/src/layouts/AuthLayout/styles/auth.css +4 -42
- package/src/layouts/AuthLayout/styles/centered-shell.css +57 -0
- package/src/layouts/AuthLayout/styles/split-shell.css +101 -0
- package/src/layouts/AuthLayout/types.ts +11 -0
- package/src/layouts/types/layout.types.ts +4 -2
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# AuthLayout
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Shell-based authentication layout with two visual variants:
|
|
4
|
+
- `centered` (default) — Apple HIG-style, frameless, centered, glow background
|
|
5
|
+
- `split` — Two-column on desktop (form + sidebar), single-column on mobile
|
|
4
6
|
|
|
5
7
|
Supports: email OTP, phone OTP, GitHub OAuth, 2FA (TOTP + backup codes).
|
|
6
8
|
|
|
7
9
|
## Usage
|
|
8
10
|
|
|
11
|
+
### Centered variant (default)
|
|
12
|
+
|
|
9
13
|
```tsx
|
|
10
14
|
import { AuthLayout } from '@djangocfg/layouts';
|
|
11
15
|
|
|
@@ -24,10 +28,41 @@ import { AuthLayout } from '@djangocfg/layouts';
|
|
|
24
28
|
</AuthLayout>
|
|
25
29
|
```
|
|
26
30
|
|
|
31
|
+
### Split variant (two-column)
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { AuthLayout } from '@djangocfg/layouts';
|
|
35
|
+
|
|
36
|
+
<AuthLayout
|
|
37
|
+
variant="split"
|
|
38
|
+
sourceUrl="https://myapp.com"
|
|
39
|
+
background={{
|
|
40
|
+
imageUrl: '/assets/signup-bg.jpg',
|
|
41
|
+
overlay: 'hsl(var(--background) / 0.6)',
|
|
42
|
+
blur: '8px',
|
|
43
|
+
}}
|
|
44
|
+
sidebar={
|
|
45
|
+
<div>
|
|
46
|
+
<blockquote>"Amazing product!"</blockquote>
|
|
47
|
+
<cite>— Jane Doe, CEO</cite>
|
|
48
|
+
</div>
|
|
49
|
+
}
|
|
50
|
+
>
|
|
51
|
+
{/* Optional: custom header */}
|
|
52
|
+
<div>
|
|
53
|
+
<h1>Get started</h1>
|
|
54
|
+
<p>Already have an account? <a href="/login">Login</a></p>
|
|
55
|
+
</div>
|
|
56
|
+
</AuthLayout>
|
|
57
|
+
```
|
|
58
|
+
|
|
27
59
|
## Props
|
|
28
60
|
|
|
29
61
|
| Prop | Type | Default | Description |
|
|
30
62
|
|---|---|---|---|
|
|
63
|
+
| `variant` | `'centered' \| 'split'` | `'centered'` | Shell layout variant |
|
|
64
|
+
| `background` | `AuthBackgroundConfig` | — | Background image/gradient/overlay/blur |
|
|
65
|
+
| `sidebar` | `ReactNode` | — | Right column content (split variant only) |
|
|
31
66
|
| `sourceUrl` | `string` | — | App URL for analytics/tracking |
|
|
32
67
|
| `redirectUrl` | `string` | `/dashboard` | Where to redirect after auth |
|
|
33
68
|
| `enableGithubAuth` | `boolean` | `false` | Show GitHub OAuth button |
|
|
@@ -40,6 +75,15 @@ import { AuthLayout } from '@djangocfg/layouts';
|
|
|
40
75
|
| `className` | `string` | — | Extra class on root element |
|
|
41
76
|
| `children` | `ReactNode` | — | Custom header (identifier step only) |
|
|
42
77
|
|
|
78
|
+
### AuthBackgroundConfig
|
|
79
|
+
|
|
80
|
+
| Prop | Type | Description |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `imageUrl` | `string` | Background image URL (loaded via `useImageLoader`) |
|
|
83
|
+
| `gradient` | `string` | CSS gradient fallback |
|
|
84
|
+
| `overlay` | `string` | Overlay color with opacity |
|
|
85
|
+
| `blur` | `string` | Backdrop blur amount (default: `8px`) |
|
|
86
|
+
|
|
43
87
|
## Callbacks
|
|
44
88
|
|
|
45
89
|
```tsx
|
|
@@ -63,9 +107,31 @@ import { AuthLayout } from '@djangocfg/layouts';
|
|
|
63
107
|
|
|
64
108
|
`children` (custom header) are only rendered on the `identifier` step — hidden on all others.
|
|
65
109
|
|
|
66
|
-
##
|
|
110
|
+
## Architecture
|
|
111
|
+
|
|
112
|
+
AuthLayout is built on a **shell pattern** (inspired by PublicLayout's navbar/footer slots):
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
AuthLayout/
|
|
116
|
+
├── shells/
|
|
117
|
+
│ ├── AuthShell.tsx — Orchestrator: loads bg image, dispatches variant
|
|
118
|
+
│ ├── CenteredShell.tsx — Apple-style frameless layout
|
|
119
|
+
│ ├── SplitShell.tsx — Two-column card layout
|
|
120
|
+
│ ├── context.tsx — Shell context (variant, hasSidebar)
|
|
121
|
+
│ └── types.ts — AuthShellVariant, AuthBackgroundConfig
|
|
122
|
+
├── components/steps/ — Auth step components (IdentifierStep, OTPStep, ...)
|
|
123
|
+
├── components/shared/ — Reusable UI primitives (AuthButton, AuthHeader, ...)
|
|
124
|
+
├── styles/
|
|
125
|
+
│ ├── auth.css — Shared form/button/input styles
|
|
126
|
+
│ ├── centered-shell.css — Centered variant layout
|
|
127
|
+
│ └── split-shell.css — Split variant layout
|
|
128
|
+
```
|
|
67
129
|
|
|
68
|
-
|
|
130
|
+
Adding a new variant:
|
|
131
|
+
1. Add variant name to `AuthShellVariant` in `shells/types.ts`
|
|
132
|
+
2. Create `<NewShell>.tsx` in `shells/`
|
|
133
|
+
3. Add CSS in `styles/new-shell.css`
|
|
134
|
+
4. Wire it up in `AuthShell.tsx`
|
|
69
135
|
|
|
70
136
|
## Context
|
|
71
137
|
|
|
@@ -76,3 +142,11 @@ import { useAuthFormContext } from '@djangocfg/layouts';
|
|
|
76
142
|
|
|
77
143
|
const { step, identifier, isLoading } = useAuthFormContext();
|
|
78
144
|
```
|
|
145
|
+
|
|
146
|
+
Shell metadata is available via `useAuthShell()`:
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
import { useAuthShell } from '@djangocfg/layouts';
|
|
150
|
+
|
|
151
|
+
const { variant, hasSidebar } = useAuthShell();
|
|
152
|
+
```
|
|
@@ -8,6 +8,14 @@ export { AuthLayout, useAuthLayoutContext } from './AuthLayout';
|
|
|
8
8
|
// Context and hooks
|
|
9
9
|
export { AuthFormProvider, useAuthFormContext } from './context';
|
|
10
10
|
|
|
11
|
+
// Shells
|
|
12
|
+
export {
|
|
13
|
+
AuthShell,
|
|
14
|
+
AuthShellProvider,
|
|
15
|
+
useAuthShell,
|
|
16
|
+
useAuthShellOptional,
|
|
17
|
+
} from './shells';
|
|
18
|
+
|
|
11
19
|
// Step components
|
|
12
20
|
export {
|
|
13
21
|
IdentifierStep,
|
|
@@ -34,4 +42,8 @@ export type {
|
|
|
34
42
|
AuthFormContextType,
|
|
35
43
|
AuthLayoutProps,
|
|
36
44
|
AuthHelpProps,
|
|
45
|
+
// Shell types
|
|
46
|
+
AuthShellVariant,
|
|
47
|
+
AuthBackgroundConfig,
|
|
48
|
+
AuthShellContextValue,
|
|
37
49
|
} from './types';
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { useMemo } from 'react';
|
|
4
|
+
|
|
5
|
+
import { useImageLoader } from '@djangocfg/ui-core/hooks';
|
|
6
|
+
|
|
7
|
+
import { AuthShellProvider } from './context';
|
|
8
|
+
import { CenteredShell } from './CenteredShell';
|
|
9
|
+
import { SplitShell } from './SplitShell';
|
|
10
|
+
import type { AuthShellProps } from './types';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* AuthShell — shared orchestrator for auth layout variants.
|
|
14
|
+
*
|
|
15
|
+
* Handles common concerns:
|
|
16
|
+
* - background image loading (via useImageLoader)
|
|
17
|
+
* - shell context registration
|
|
18
|
+
* - variant dispatch (centered | split)
|
|
19
|
+
*
|
|
20
|
+
* Variants own their own chrome (positioning, shape, background).
|
|
21
|
+
*/
|
|
22
|
+
export const AuthShell: React.FC<AuthShellProps> = ({
|
|
23
|
+
variant,
|
|
24
|
+
children,
|
|
25
|
+
background,
|
|
26
|
+
sidebar,
|
|
27
|
+
className,
|
|
28
|
+
}) => {
|
|
29
|
+
const { isLoaded: bgLoaded, hasError: bgError } = useImageLoader(background?.imageUrl);
|
|
30
|
+
|
|
31
|
+
const hasBgImage = Boolean(background?.imageUrl) && bgLoaded && !bgError;
|
|
32
|
+
|
|
33
|
+
const bgStyle = useMemo(() => {
|
|
34
|
+
if (hasBgImage && background?.imageUrl) {
|
|
35
|
+
return {
|
|
36
|
+
backgroundImage: `url(${background.imageUrl})`,
|
|
37
|
+
backgroundSize: 'cover',
|
|
38
|
+
backgroundPosition: 'center',
|
|
39
|
+
backgroundRepeat: 'no-repeat',
|
|
40
|
+
} as React.CSSProperties;
|
|
41
|
+
}
|
|
42
|
+
if (background?.gradient) {
|
|
43
|
+
return { background: background.gradient } as React.CSSProperties;
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}, [hasBgImage, background]);
|
|
47
|
+
|
|
48
|
+
const overlayStyle = useMemo(() => {
|
|
49
|
+
if (!background?.overlay) return undefined;
|
|
50
|
+
return { background: background.overlay } as React.CSSProperties;
|
|
51
|
+
}, [background?.overlay]);
|
|
52
|
+
|
|
53
|
+
const blurValue = background?.blur ?? '8px';
|
|
54
|
+
|
|
55
|
+
const shellContext = useMemo(
|
|
56
|
+
() => ({
|
|
57
|
+
variant,
|
|
58
|
+
hasSidebar: Boolean(sidebar),
|
|
59
|
+
}),
|
|
60
|
+
[variant, sidebar]
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const commonShellProps = {
|
|
64
|
+
children,
|
|
65
|
+
className,
|
|
66
|
+
bgStyle,
|
|
67
|
+
overlayStyle,
|
|
68
|
+
blurValue,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<AuthShellProvider value={shellContext}>
|
|
73
|
+
{variant === 'split' ? (
|
|
74
|
+
<SplitShell {...commonShellProps} sidebar={sidebar} />
|
|
75
|
+
) : (
|
|
76
|
+
<CenteredShell {...commonShellProps} />
|
|
77
|
+
)}
|
|
78
|
+
</AuthShellProvider>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
5
|
+
import { GlowBackground } from '@djangocfg/ui-core/components';
|
|
6
|
+
|
|
7
|
+
import type { ShellRenderProps } from './types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* CenteredShell — Apple-style frameless auth layout.
|
|
11
|
+
*
|
|
12
|
+
* Features:
|
|
13
|
+
* - Full viewport centering
|
|
14
|
+
* - Glow background (default)
|
|
15
|
+
* - Optional custom background image/gradient via shell props
|
|
16
|
+
* - No visible card chrome
|
|
17
|
+
*/
|
|
18
|
+
export const CenteredShell: React.FC<ShellRenderProps> = ({
|
|
19
|
+
children,
|
|
20
|
+
className,
|
|
21
|
+
bgStyle,
|
|
22
|
+
overlayStyle,
|
|
23
|
+
}) => {
|
|
24
|
+
const hasCustomBg = Boolean(bgStyle);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className={`auth-shell-centered ${className || ''}`}>
|
|
28
|
+
{/* Background layer */}
|
|
29
|
+
{hasCustomBg ? (
|
|
30
|
+
<>
|
|
31
|
+
<div className="auth-shell-centered__bg" style={bgStyle} />
|
|
32
|
+
{overlayStyle && (
|
|
33
|
+
<div className="auth-shell-centered__overlay" style={overlayStyle} />
|
|
34
|
+
)}
|
|
35
|
+
</>
|
|
36
|
+
) : (
|
|
37
|
+
<GlowBackground />
|
|
38
|
+
)}
|
|
39
|
+
|
|
40
|
+
{/* Content */}
|
|
41
|
+
<div className="auth-shell-centered__content">
|
|
42
|
+
{children}
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
5
|
+
import type { ShellRenderProps } from './types';
|
|
6
|
+
|
|
7
|
+
interface SplitShellProps extends ShellRenderProps {
|
|
8
|
+
sidebar?: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* SplitShell — Two-column auth layout (desktop) / single-column (mobile).
|
|
13
|
+
*
|
|
14
|
+
* Layout:
|
|
15
|
+
* - Mobile (< xl): single column, centered form, optional background
|
|
16
|
+
* - Desktop (>= xl): two columns inside a card — form left, sidebar right
|
|
17
|
+
*
|
|
18
|
+
* Background: full-bleed image/gradient with optional overlay + blur.
|
|
19
|
+
*/
|
|
20
|
+
export const SplitShell: React.FC<SplitShellProps> = ({
|
|
21
|
+
children,
|
|
22
|
+
className,
|
|
23
|
+
bgStyle,
|
|
24
|
+
overlayStyle,
|
|
25
|
+
blurValue,
|
|
26
|
+
sidebar,
|
|
27
|
+
}) => {
|
|
28
|
+
return (
|
|
29
|
+
<div className={`auth-shell-split ${className || ''}`}>
|
|
30
|
+
{/* Background layer */}
|
|
31
|
+
{bgStyle && (
|
|
32
|
+
<div className="auth-shell-split__bg" style={bgStyle} />
|
|
33
|
+
)}
|
|
34
|
+
{overlayStyle && (
|
|
35
|
+
<div
|
|
36
|
+
className="auth-shell-split__overlay"
|
|
37
|
+
style={{
|
|
38
|
+
...overlayStyle,
|
|
39
|
+
backdropFilter: `blur(${blurValue})`,
|
|
40
|
+
WebkitBackdropFilter: `blur(${blurValue})`,
|
|
41
|
+
}}
|
|
42
|
+
/>
|
|
43
|
+
)}
|
|
44
|
+
|
|
45
|
+
{/* Card container */}
|
|
46
|
+
<div className="auth-shell-split__card">
|
|
47
|
+
{/* Left: form */}
|
|
48
|
+
<div className="auth-shell-split__form">
|
|
49
|
+
<div className="auth-shell-split__form-inner">
|
|
50
|
+
{children}
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
{/* Right: sidebar (desktop only) */}
|
|
55
|
+
{sidebar && (
|
|
56
|
+
<div className="auth-shell-split__sidebar">
|
|
57
|
+
{sidebar}
|
|
58
|
+
</div>
|
|
59
|
+
)}
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { createContext, useContext } from 'react';
|
|
4
|
+
|
|
5
|
+
import type { AuthShellContextValue } from './types';
|
|
6
|
+
|
|
7
|
+
const AuthShellContext = createContext<AuthShellContextValue | undefined>(undefined);
|
|
8
|
+
|
|
9
|
+
export const AuthShellProvider: React.FC<{
|
|
10
|
+
value: AuthShellContextValue;
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
}> = ({ value, children }) => (
|
|
13
|
+
<AuthShellContext.Provider value={value}>{children}</AuthShellContext.Provider>
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
export const useAuthShell = (): AuthShellContextValue => {
|
|
17
|
+
const ctx = useContext(AuthShellContext);
|
|
18
|
+
if (!ctx) {
|
|
19
|
+
throw new Error('useAuthShell must be used within an AuthShellProvider');
|
|
20
|
+
}
|
|
21
|
+
return ctx;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const useAuthShellOptional = (): AuthShellContextValue | undefined =>
|
|
25
|
+
useContext(AuthShellContext);
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth Shell Types
|
|
3
|
+
*
|
|
4
|
+
* UI-specific configuration for auth layout shells.
|
|
5
|
+
* Auth logic types live in ../types.ts (re-exported from @djangocfg/api/auth).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { CSSProperties, ReactNode } from 'react';
|
|
9
|
+
|
|
10
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
11
|
+
// Shell Variants
|
|
12
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* - `centered` — Apple-style frameless, centered, glow background (default).
|
|
16
|
+
* - `split` — Two-column: form left, sidebar right on desktop;
|
|
17
|
+
* single-column centered on mobile.
|
|
18
|
+
*/
|
|
19
|
+
export type AuthShellVariant = 'centered' | 'split';
|
|
20
|
+
|
|
21
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
22
|
+
// Background Configuration
|
|
23
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
export interface AuthBackgroundConfig {
|
|
26
|
+
/** Background image URL (loaded via useImageLoader). */
|
|
27
|
+
imageUrl?: string;
|
|
28
|
+
/** CSS gradient fallback when no image or image fails to load. */
|
|
29
|
+
gradient?: string;
|
|
30
|
+
/** Overlay color with opacity (e.g. `hsl(var(--background) / 0.6)`). */
|
|
31
|
+
overlay?: string;
|
|
32
|
+
/** Backdrop blur amount (e.g. `8px`). */
|
|
33
|
+
blur?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
37
|
+
// Shell Props
|
|
38
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
export interface AuthShellProps {
|
|
41
|
+
variant: AuthShellVariant;
|
|
42
|
+
children: ReactNode;
|
|
43
|
+
/** Background configuration — image, gradient, overlay, blur. */
|
|
44
|
+
background?: AuthBackgroundConfig;
|
|
45
|
+
/** Slot for the right column in split variant (testimonial, branding, etc.). */
|
|
46
|
+
sidebar?: ReactNode;
|
|
47
|
+
className?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Internal props passed from AuthShell orchestrator to variant shells. */
|
|
51
|
+
export interface ShellRenderProps {
|
|
52
|
+
children: ReactNode;
|
|
53
|
+
className?: string;
|
|
54
|
+
bgStyle?: CSSProperties;
|
|
55
|
+
overlayStyle?: CSSProperties;
|
|
56
|
+
blurValue?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
60
|
+
// Shell Context
|
|
61
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
export interface AuthShellContextValue {
|
|
64
|
+
variant: AuthShellVariant;
|
|
65
|
+
/** True when sidebar slot is provided (split variant only). */
|
|
66
|
+
hasSidebar: boolean;
|
|
67
|
+
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Auth
|
|
2
|
+
* Auth Base Styles
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Design tokens from @djangocfg/ui-core.
|
|
4
|
+
* Shared across all shell variants: forms, buttons, inputs, OTP, errors, etc.
|
|
5
|
+
* Shell-specific layout styles live in centered-shell.css and split-shell.css.
|
|
7
6
|
*/
|
|
8
7
|
|
|
9
8
|
/* ===== SPRING EASINGS ===== */
|
|
@@ -17,35 +16,7 @@
|
|
|
17
16
|
--auth-radius-xs: 0.375rem;
|
|
18
17
|
}
|
|
19
18
|
|
|
20
|
-
/* ===== LAYOUT ===== */
|
|
21
|
-
|
|
22
|
-
.auth-layout {
|
|
23
|
-
position: relative;
|
|
24
|
-
min-height: 100vh;
|
|
25
|
-
min-height: 100dvh;
|
|
26
|
-
display: flex;
|
|
27
|
-
flex-direction: column;
|
|
28
|
-
align-items: center;
|
|
29
|
-
justify-content: center;
|
|
30
|
-
/* Responsive padding: 24px on mobile → 40px on desktop */
|
|
31
|
-
padding: 1.5rem clamp(1.5rem, 5vw, 2.5rem);
|
|
32
|
-
padding-bottom: max(1.5rem, env(safe-area-inset-bottom, 1rem));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/* Glow layer override — force z-index 0 behind auth content */
|
|
36
|
-
.auth-glow-layer {
|
|
37
|
-
z-index: 0;
|
|
38
|
-
pointer-events: none;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/* All direct auth content sits above the glow */
|
|
42
|
-
.auth-layout > *:not(.auth-glow-layer) {
|
|
43
|
-
position: relative;
|
|
44
|
-
z-index: 1;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
19
|
/* ===== CONTAINER ===== */
|
|
48
|
-
/* Apple HIG: No card chrome on auth screens — frameless, content-first */
|
|
49
20
|
|
|
50
21
|
.auth-container {
|
|
51
22
|
width: 100%;
|
|
@@ -85,7 +56,7 @@
|
|
|
85
56
|
height: 56px;
|
|
86
57
|
margin-bottom: 0.75rem;
|
|
87
58
|
object-fit: contain;
|
|
88
|
-
border-radius: 14px;
|
|
59
|
+
border-radius: 14px;
|
|
89
60
|
animation: authLogoIn 0.5s var(--spring-bounce) both;
|
|
90
61
|
}
|
|
91
62
|
|
|
@@ -402,7 +373,6 @@
|
|
|
402
373
|
}
|
|
403
374
|
|
|
404
375
|
/* ===== CHANNEL TOGGLE ===== */
|
|
405
|
-
/* Apple HIG segmented control */
|
|
406
376
|
|
|
407
377
|
.auth-channel-toggle {
|
|
408
378
|
display: flex;
|
|
@@ -467,7 +437,6 @@
|
|
|
467
437
|
width: 100%;
|
|
468
438
|
}
|
|
469
439
|
|
|
470
|
-
|
|
471
440
|
.auth-otp-wrapper input {
|
|
472
441
|
width: 3rem;
|
|
473
442
|
height: 3.5rem;
|
|
@@ -509,7 +478,6 @@
|
|
|
509
478
|
box-shadow: 0 2px 12px hsl(0 0% 0% / 0.1);
|
|
510
479
|
}
|
|
511
480
|
|
|
512
|
-
/* Collapsible with centered trigger (e.g. "Can't scan? Enter manually") */
|
|
513
481
|
.auth-collapsible-centered {
|
|
514
482
|
display: flex;
|
|
515
483
|
flex-direction: column;
|
|
@@ -628,7 +596,6 @@
|
|
|
628
596
|
}
|
|
629
597
|
|
|
630
598
|
/* ===== 2FA BOX ===== */
|
|
631
|
-
/* Visual differentiation for the two-factor step */
|
|
632
599
|
|
|
633
600
|
.auth-2fa-box {
|
|
634
601
|
padding: 1.5rem 1.25rem 1.25rem;
|
|
@@ -661,7 +628,6 @@
|
|
|
661
628
|
height: 1.375rem;
|
|
662
629
|
}
|
|
663
630
|
|
|
664
|
-
/* Actions row inside 2fa box — allow wrapping on very narrow screens */
|
|
665
631
|
.auth-2fa-box .auth-actions {
|
|
666
632
|
flex-wrap: wrap;
|
|
667
633
|
row-gap: 0.625rem;
|
|
@@ -698,10 +664,6 @@
|
|
|
698
664
|
/* ===== RESPONSIVE ===== */
|
|
699
665
|
|
|
700
666
|
@media (max-width: 480px) {
|
|
701
|
-
.auth-layout {
|
|
702
|
-
overflow-y: auto;
|
|
703
|
-
}
|
|
704
|
-
|
|
705
667
|
.auth-title {
|
|
706
668
|
font-size: 1.375rem;
|
|
707
669
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centered Shell Styles
|
|
3
|
+
*
|
|
4
|
+
* Apple-style frameless layout: full viewport centering, glow background.
|
|
5
|
+
* The auth form floats in the center with no visible card chrome.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
.auth-shell-centered {
|
|
9
|
+
position: relative;
|
|
10
|
+
min-height: 100vh;
|
|
11
|
+
min-height: 100dvh;
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: center;
|
|
16
|
+
/* Responsive padding: 24px mobile → 40px desktop */
|
|
17
|
+
padding: 1.5rem clamp(1.5rem, 5vw, 2.5rem);
|
|
18
|
+
padding-bottom: max(1.5rem, env(safe-area-inset-bottom, 1rem));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* Background layers */
|
|
22
|
+
.auth-shell-centered__bg {
|
|
23
|
+
position: fixed;
|
|
24
|
+
inset: 0;
|
|
25
|
+
z-index: 0;
|
|
26
|
+
pointer-events: none;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.auth-shell-centered__overlay {
|
|
30
|
+
position: fixed;
|
|
31
|
+
inset: 0;
|
|
32
|
+
z-index: 0;
|
|
33
|
+
pointer-events: none;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Glow layer override — force z-index 0 behind auth content */
|
|
37
|
+
.auth-glow-layer {
|
|
38
|
+
z-index: 0;
|
|
39
|
+
pointer-events: none;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Content sits above background */
|
|
43
|
+
.auth-shell-centered__content {
|
|
44
|
+
position: relative;
|
|
45
|
+
z-index: 1;
|
|
46
|
+
width: 100%;
|
|
47
|
+
display: flex;
|
|
48
|
+
flex-direction: column;
|
|
49
|
+
align-items: center;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* Mobile overflow handling */
|
|
53
|
+
@media (max-width: 480px) {
|
|
54
|
+
.auth-shell-centered {
|
|
55
|
+
overflow-y: auto;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Split Shell Styles
|
|
3
|
+
*
|
|
4
|
+
* Two-column auth layout (desktop) / single-column (mobile).
|
|
5
|
+
* Desktop: card with form left, sidebar right.
|
|
6
|
+
* Mobile: centered form, sidebar hidden.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
.auth-shell-split {
|
|
10
|
+
position: relative;
|
|
11
|
+
min-height: 100vh;
|
|
12
|
+
min-height: 100dvh;
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: center;
|
|
16
|
+
padding: 1rem;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* Background layers */
|
|
20
|
+
.auth-shell-split__bg {
|
|
21
|
+
position: fixed;
|
|
22
|
+
inset: 0;
|
|
23
|
+
z-index: 0;
|
|
24
|
+
pointer-events: none;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.auth-shell-split__overlay {
|
|
28
|
+
position: fixed;
|
|
29
|
+
inset: 0;
|
|
30
|
+
z-index: 0;
|
|
31
|
+
pointer-events: none;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Card container */
|
|
35
|
+
.auth-shell-split__card {
|
|
36
|
+
position: relative;
|
|
37
|
+
z-index: 1;
|
|
38
|
+
display: flex;
|
|
39
|
+
width: 100%;
|
|
40
|
+
max-width: 420px;
|
|
41
|
+
background: hsl(var(--background));
|
|
42
|
+
border-radius: var(--auth-radius);
|
|
43
|
+
outline: 1px solid hsl(var(--border));
|
|
44
|
+
box-shadow: 0 4px 24px hsl(0 0% 0% / 0.08);
|
|
45
|
+
overflow: hidden;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Form column */
|
|
49
|
+
.auth-shell-split__form {
|
|
50
|
+
flex: 1;
|
|
51
|
+
display: flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
justify-content: center;
|
|
54
|
+
padding: 2rem 1.5rem;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.auth-shell-split__form-inner {
|
|
58
|
+
width: 100%;
|
|
59
|
+
max-width: 360px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Sidebar column — hidden by default (mobile) */
|
|
63
|
+
.auth-shell-split__sidebar {
|
|
64
|
+
display: none;
|
|
65
|
+
flex: 1;
|
|
66
|
+
flex-direction: column;
|
|
67
|
+
justify-content: center;
|
|
68
|
+
padding: 2.5rem 2rem;
|
|
69
|
+
background: hsl(var(--muted) / 0.25);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Desktop: show sidebar, expand card */
|
|
73
|
+
@media (min-width: 1280px) {
|
|
74
|
+
.auth-shell-split__card {
|
|
75
|
+
max-width: 960px;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.auth-shell-split__form {
|
|
79
|
+
padding: 2.5rem;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.auth-shell-split__sidebar {
|
|
83
|
+
display: flex;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Reduced padding on smaller tablets */
|
|
88
|
+
@media (min-width: 1024px) and (max-width: 1279px) {
|
|
89
|
+
.auth-shell-split__card {
|
|
90
|
+
max-width: 880px;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.auth-shell-split__form {
|
|
94
|
+
padding: 2rem;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.auth-shell-split__sidebar {
|
|
98
|
+
display: flex;
|
|
99
|
+
padding: 2rem 1.5rem;
|
|
100
|
+
}
|
|
101
|
+
}
|