Login Page
A centred login card with email/password fields, Azure AD SSO, remember-me toggle, and error state.
Components used​
Button · Input · Card · Toggle · Badge
Code​
- HTML · @primus/ui-core
- React
- Angular
<div style="min-height:500px;display:flex;align-items:center;justify-content:center;background:#f4f4f6;width:100%;padding:2rem;box-sizing:border-box">
<div class="card" style="width:100%;max-width:400px;padding:2.5rem 2.5rem 2rem;border:1px solid rgba(0,0,0,0.07);box-shadow:0 4px 24px rgba(0,0,0,0.07),0 1px 4px rgba(0,0,0,0.04);border-radius:12px">
<!-- Brand -->
<div style="text-align:center;margin-bottom:1.75rem">
<div style="display:inline-flex;align-items:center;gap:0.5rem;margin-bottom:1rem">
<div style="width:30px;height:30px;background:var(--primary);border-radius:7px;display:flex;align-items:center;justify-content:center;color:white;font-weight:800;font-size:0.8rem;letter-spacing:-0.02em">P</div>
<span style="font-weight:700;font-size:0.975rem;letter-spacing:-0.01em">Primus Admin</span>
</div>
<h2 style="margin:0 0 0.375rem;font-size:1.3rem;font-weight:700;letter-spacing:-0.02em">Sign in to your account</h2>
<p style="margin:0;font-size:0.8375rem;color:var(--muted-foreground)">Enter your credentials to continue</p>
</div>
<!-- Microsoft SSO -->
<button class="outline" style="width:100%;margin-bottom:1.25rem;display:flex;align-items:center;justify-content:center;gap:0.625rem;padding:0.625rem 1rem;font-size:0.875rem;font-weight:500;border-radius:8px;border:1px solid var(--border)">
<svg width="16" height="16" viewBox="0 0 21 21"><rect x="1" y="1" width="9" height="9" fill="#F25022"/><rect x="11" y="1" width="9" height="9" fill="#7FBA00"/><rect x="1" y="11" width="9" height="9" fill="#00A4EF"/><rect x="11" y="11" width="9" height="9" fill="#FFB900"/></svg>
Continue with Microsoft
</button>
<!-- Divider -->
<div style="display:flex;align-items:center;gap:0.75rem;margin-bottom:1.25rem">
<hr style="flex:1;border:none;border-top:1px solid var(--border);margin:0" />
<span style="font-size:0.75rem;color:var(--muted-foreground);white-space:nowrap">or sign in with email</span>
<hr style="flex:1;border:none;border-top:1px solid var(--border);margin:0" />
</div>
<!-- Fields -->
<div style="display:flex;flex-direction:column;gap:1rem;margin-bottom:1rem">
<div>
<label style="display:block;font-size:0.8125rem;font-weight:500;margin-bottom:0.375rem;color:var(--foreground)">Email address</label>
<input type="email" placeholder="name@company.com" style="width:100%;padding:0.5625rem 0.875rem;border:1px solid var(--border);border-radius:7px;font-size:0.875rem;background:var(--card);color:var(--foreground);box-sizing:border-box;outline:none" />
</div>
<div>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.375rem">
<label style="font-size:0.8125rem;font-weight:500;color:var(--foreground)">Password</label>
<a href="#" style="font-size:0.7875rem;color:var(--primary);text-decoration:none;font-weight:500">Forgot password?</a>
</div>
<input type="password" value="password123" style="width:100%;padding:0.5625rem 0.875rem;border:1px solid var(--border);border-radius:7px;font-size:0.875rem;background:var(--card);color:var(--foreground);box-sizing:border-box;outline:none" />
</div>
</div>
<!-- Error state -->
<div style="display:flex;align-items:center;gap:0.5rem;padding:0.6875rem 0.875rem;background:#fff5f5;border:1px solid #fecaca;border-radius:7px;margin-bottom:1rem">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="2.5"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
<span style="font-size:0.8125rem;color:#dc2626;font-weight:500">Invalid email or password. Please try again.</span>
</div>
<!-- Remember me toggle -->
<label style="display:flex;align-items:center;gap:0.625rem;margin-bottom:1.25rem;cursor:pointer">
<input type="checkbox" role="switch" />
<span style="font-size:0.8125rem;color:var(--foreground)">Keep me signed in for 30 days</span>
</label>
<!-- Submit -->
<button style="width:100%;padding:0.6875rem 1rem;border-radius:8px;font-size:0.9375rem;font-weight:600;justify-content:center;letter-spacing:-0.01em">Sign in</button>
<p style="text-align:center;font-size:0.7875rem;color:var(--muted-foreground);margin:1.125rem 0 0">
Don't have an account? <a href="#" style="color:var(--primary);text-decoration:none;font-weight:500">Request access</a>
</p>
</div>
</div>
import { PrimusInput, PrimusButton, PrimusToggle } from 'primus-react-ui';
import { useState } from 'react';
export function LoginPage() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [remember, setRemember] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const handleSubmit = async () => {
setLoading(true);
setError('');
try {
await signIn({ email, password, remember });
} catch {
setError('Invalid email or password. Please try again.');
} finally {
setLoading(false);
}
};
return (
<div className="auth-shell">
<div className="card" style={{ maxWidth: 400, padding: '2.5rem' }}>
<div className="auth-brand">
<h2>Sign in to your account</h2>
<p>Enter your credentials to continue</p>
</div>
<PrimusButton variant="outline" style={{ width: '100%' }}
onClick={() => signInWithMicrosoft()}>
Continue with Microsoft
</PrimusButton>
<div className="auth-divider">or sign in with email</div>
<PrimusInput label="Email address" type="email"
value={email} onChange={setEmail}
placeholder="name@company.com" />
<PrimusInput label="Password" type="password"
value={password} onChange={setPassword}
error={error} />
<PrimusToggle label="Keep me signed in for 30 days"
checked={remember} onChange={setRemember} />
<PrimusButton style={{ width: '100%' }}
loading={loading} onClick={handleSubmit}>
Sign in
</PrimusButton>
</div>
</div>
);
}
<!-- login.component.html -->
<div class="auth-shell">
<div class="card auth-card">
<div class="auth-brand">
<h2>Sign in to your account</h2>
<p>Enter your credentials to continue</p>
</div>
<primus-button variant="outline" style="width:100%"
(clicked)="signInWithMicrosoft()">
Continue with Microsoft
</primus-button>
<div class="auth-divider">or sign in with email</div>
<primus-input label="Email address" type="email"
[(value)]="email" placeholder="name@company.com">
</primus-input>
<primus-input label="Password" type="password"
[(value)]="password" [error]="error">
</primus-input>
<primus-toggle label="Keep me signed in for 30 days"
[(checked)]="remember">
</primus-toggle>
<primus-button style="width:100%"
[loading]="loading" (clicked)="onSubmit()">
Sign in
</primus-button>
</div>
</div>
Variations​
Loading state​
Set loading={true} on the Sign in button. It shows a spinner and disables all form fields automatically.
MFA step​
After credentials pass, render a second card with a 6-digit OTP field:
<label data-field>
Verification code
<input type="text" inputmode="numeric" maxlength="6"
placeholder="000000" style="letter-spacing:0.3em;text-align:center;font-size:1.25rem" />
<span data-hint>Check your authenticator app or SMS.</span>
</label>
<button style="width:100%">Verify</button>