Authentication
Sign in or sign up
A centered card with Sign in and Sign up tabs, each with Google or email, that opens the tab a link names.
<div class="grid min-h-dvh place-items-center bg-base-200 p-6">
<div class="flex w-full max-w-sm flex-col gap-6">
<a class="flex items-center gap-2 self-center font-medium" href="#">
<span class="grid size-8 place-items-center rounded-lg bg-primary text-primary-content">
<span class="icon icon-stack-2 size-5"></span>
</span>
Pelatform
</a>
<div class="card" id="account-tabs" data-plf-tabs>
<div class="card-body flex flex-col gap-6">
<div class="tabs tabs-box w-full" role="tablist" aria-label="Account">
<button class="tab flex-1" role="tab" aria-selected="true">Sign in</button>
<button class="tab flex-1" role="tab">Sign up</button>
</div>
<div class="tab-content" role="tabpanel" id="sign-in-panel">
<form class="flex flex-col gap-6" method="post">
<div class="flex flex-col gap-1">
<h1 class="text-xl font-semibold">Welcome back</h1>
<p class="text-sm text-base-content/70">Sign in to your Pelatform workspace.</p>
</div>
<a class="btn w-full" href="#" style="--plf-button-bg: #fff; --plf-button-color: #1f1f1f; --plf-button-border: #747775; --plf-button-hover-bg: #f2f2f2">
<svg class="btn-icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<path fill="#ea4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/>
<path fill="#4285f4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/>
<path fill="#fbbc05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"/>
<path fill="#34a853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/>
</svg>
Sign in with Google
</a>
<div class="flex items-center gap-3 text-xs text-base-content/70">
<span class="divider flex-1"></span>
or continue with email
<span class="divider flex-1"></span>
</div>
<div class="flex flex-col gap-4">
<div class="field">
<label class="field-label" for="sign-in-email">Email</label>
<div class="input-group">
<span class="input-group-addon">
<span class="icon icon-mail"></span>
</span>
<input class="input-group-control" id="sign-in-email" name="email" type="email" dir="ltr" autocomplete="email" placeholder="name@example.com" required>
</div>
</div>
<div class="field">
<div class="flex items-center justify-between gap-2">
<label class="field-label" for="sign-in-password">Password</label>
<a class="link link-muted text-sm" href="#">Forgot password?</a>
</div>
<div class="input-group">
<span class="input-group-addon">
<span class="icon icon-key"></span>
</span>
<input class="input-group-control" id="sign-in-password" name="password" type="password" autocomplete="current-password" required>
<button class="input-group-action input-group-addon" type="button" data-plf-password-toggle hidden></button>
</div>
</div>
</div>
<button class="btn btn-primary w-full">Sign in</button>
<p class="text-center text-sm text-base-content/70">
New to Pelatform? <button class="link" type="button" data-tab="sign-up-panel">Create an account</button>
</p>
</form>
</div>
<div class="tab-content" role="tabpanel" id="sign-up-panel">
<form class="flex flex-col gap-6" method="post">
<div class="flex flex-col gap-1">
<h1 class="text-xl font-semibold">Create your account</h1>
<p class="text-sm text-base-content/70">Start a free Pelatform workspace.</p>
</div>
<a class="btn w-full" href="#" style="--plf-button-bg: #fff; --plf-button-color: #1f1f1f; --plf-button-border: #747775; --plf-button-hover-bg: #f2f2f2">
<svg class="btn-icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<path fill="#ea4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/>
<path fill="#4285f4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/>
<path fill="#fbbc05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"/>
<path fill="#34a853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/>
</svg>
Sign up with Google
</a>
<div class="flex items-center gap-3 text-xs text-base-content/70">
<span class="divider flex-1"></span>
or continue with email
<span class="divider flex-1"></span>
</div>
<div class="flex flex-col gap-4">
<div class="field">
<label class="field-label" for="sign-up-name">Full name</label>
<div class="input-group">
<span class="input-group-addon">
<span class="icon icon-user"></span>
</span>
<input class="input-group-control" id="sign-up-name" name="name" type="text" autocomplete="name" required>
</div>
</div>
<div class="field">
<label class="field-label" for="sign-up-email">Work email</label>
<div class="input-group">
<span class="input-group-addon">
<span class="icon icon-mail"></span>
</span>
<input class="input-group-control" id="sign-up-email" name="email" type="email" dir="ltr" autocomplete="email" placeholder="name@example.com" required>
</div>
</div>
<div class="field">
<label class="field-label" for="sign-up-password">Password</label>
<div class="input-group">
<span class="input-group-addon">
<span class="icon icon-key"></span>
</span>
<input class="input-group-control" id="sign-up-password" name="password" type="password" autocomplete="new-password" required minlength="12" aria-describedby="sign-up-password-help">
<button class="input-group-action input-group-addon" type="button" data-plf-password-toggle hidden></button>
</div>
<p class="field-help" id="sign-up-password-help">At least 12 characters.</p>
</div>
<div class="choice">
<input class="checkbox" id="sign-up-terms" name="terms" type="checkbox" required>
<label class="choice-label" for="sign-up-terms">
<span>
I agree to the <a class="link" href="#">Terms</a>
and the <a class="link" href="#">Privacy Policy</a>
</span>
</label>
</div>
</div>
<button class="btn btn-primary w-full">Create account</button>
<p class="text-center text-sm text-base-content/70">
Already have an account? <button class="link" type="button" data-tab="sign-in-panel">Sign in</button>
</p>
</form>
</div>
</div>
</div>
</div>
</div>
<script>
const tabs = document.getElementById('account-tabs');
// A footer button selects the other tab and moves the focus to it.
for (const button of tabs.querySelectorAll('[data-tab]')) {
button.addEventListener('click', () => {
PLF.Tabs.getOrCreateInstance(tabs).select(button.dataset.tab);
tabs.querySelector('[aria-selected="true"]').focus();
});
}
// An address that ends in #sign-up opens the Sign up tab once the runtime has loaded; no element has that id, so the page
// does not scroll past the tabs.
document.addEventListener('DOMContentLoaded', () => {
if (location.hash === '#sign-up') PLF.Tabs.getOrCreateInstance(tabs).select('sign-up-panel');
});
</script><div class="plf:grid plf:min-h-dvh plf:place-items-center plf:bg-base-200 plf:p-6">
<div class="plf:flex plf:w-full plf:max-w-sm plf:flex-col plf:gap-6">
<a class="plf:flex plf:items-center plf:gap-2 plf:self-center plf:font-medium" href="#">
<span class="plf:grid plf:size-8 plf:place-items-center plf:rounded-lg plf:bg-primary plf:text-primary-content">
<span class="plf:icon plf:icon-stack-2 plf:size-5"></span>
</span>
Pelatform
</a>
<div class="plf:card" id="account-tabs" data-plf-tabs>
<div class="plf:card-body plf:flex plf:flex-col plf:gap-6">
<div class="plf:tabs plf:tabs-box plf:w-full" role="tablist" aria-label="Account">
<button class="plf:tab plf:flex-1" role="tab" aria-selected="true">Sign in</button>
<button class="plf:tab plf:flex-1" role="tab">Sign up</button>
</div>
<div class="plf:tab-content" role="tabpanel" id="sign-in-panel">
<form class="plf:flex plf:flex-col plf:gap-6" method="post">
<div class="plf:flex plf:flex-col plf:gap-1">
<h1 class="plf:text-xl plf:font-semibold">Welcome back</h1>
<p class="plf:text-sm plf:text-base-content/70">Sign in to your Pelatform workspace.</p>
</div>
<a class="plf:btn plf:w-full" href="#" style="--plf-button-bg: #fff; --plf-button-color: #1f1f1f; --plf-button-border: #747775; --plf-button-hover-bg: #f2f2f2">
<svg class="plf:btn-icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<path fill="#ea4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/>
<path fill="#4285f4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/>
<path fill="#fbbc05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"/>
<path fill="#34a853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/>
</svg>
Sign in with Google
</a>
<div class="plf:flex plf:items-center plf:gap-3 plf:text-xs plf:text-base-content/70">
<span class="plf:divider plf:flex-1"></span>
or continue with email
<span class="plf:divider plf:flex-1"></span>
</div>
<div class="plf:flex plf:flex-col plf:gap-4">
<div class="plf:field">
<label class="plf:field-label" for="sign-in-email">Email</label>
<div class="plf:input-group">
<span class="plf:input-group-addon">
<span class="plf:icon plf:icon-mail"></span>
</span>
<input class="plf:input-group-control" id="sign-in-email" name="email" type="email" dir="ltr" autocomplete="email" placeholder="name@example.com" required>
</div>
</div>
<div class="plf:field">
<div class="plf:flex plf:items-center plf:justify-between plf:gap-2">
<label class="plf:field-label" for="sign-in-password">Password</label>
<a class="plf:link plf:link-muted plf:text-sm" href="#">Forgot password?</a>
</div>
<div class="plf:input-group">
<span class="plf:input-group-addon">
<span class="plf:icon plf:icon-key"></span>
</span>
<input class="plf:input-group-control" id="sign-in-password" name="password" type="password" autocomplete="current-password" required>
<button class="plf:input-group-action plf:input-group-addon" type="button" data-plf-password-toggle hidden></button>
</div>
</div>
</div>
<button class="plf:btn plf:btn-primary plf:w-full">Sign in</button>
<p class="plf:text-center plf:text-sm plf:text-base-content/70">
New to Pelatform? <button class="plf:link" type="button" data-tab="sign-up-panel">Create an account</button>
</p>
</form>
</div>
<div class="plf:tab-content" role="tabpanel" id="sign-up-panel">
<form class="plf:flex plf:flex-col plf:gap-6" method="post">
<div class="plf:flex plf:flex-col plf:gap-1">
<h1 class="plf:text-xl plf:font-semibold">Create your account</h1>
<p class="plf:text-sm plf:text-base-content/70">Start a free Pelatform workspace.</p>
</div>
<a class="plf:btn plf:w-full" href="#" style="--plf-button-bg: #fff; --plf-button-color: #1f1f1f; --plf-button-border: #747775; --plf-button-hover-bg: #f2f2f2">
<svg class="plf:btn-icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<path fill="#ea4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/>
<path fill="#4285f4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/>
<path fill="#fbbc05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"/>
<path fill="#34a853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/>
</svg>
Sign up with Google
</a>
<div class="plf:flex plf:items-center plf:gap-3 plf:text-xs plf:text-base-content/70">
<span class="plf:divider plf:flex-1"></span>
or continue with email
<span class="plf:divider plf:flex-1"></span>
</div>
<div class="plf:flex plf:flex-col plf:gap-4">
<div class="plf:field">
<label class="plf:field-label" for="sign-up-name">Full name</label>
<div class="plf:input-group">
<span class="plf:input-group-addon">
<span class="plf:icon plf:icon-user"></span>
</span>
<input class="plf:input-group-control" id="sign-up-name" name="name" type="text" autocomplete="name" required>
</div>
</div>
<div class="plf:field">
<label class="plf:field-label" for="sign-up-email">Work email</label>
<div class="plf:input-group">
<span class="plf:input-group-addon">
<span class="plf:icon plf:icon-mail"></span>
</span>
<input class="plf:input-group-control" id="sign-up-email" name="email" type="email" dir="ltr" autocomplete="email" placeholder="name@example.com" required>
</div>
</div>
<div class="plf:field">
<label class="plf:field-label" for="sign-up-password">Password</label>
<div class="plf:input-group">
<span class="plf:input-group-addon">
<span class="plf:icon plf:icon-key"></span>
</span>
<input class="plf:input-group-control" id="sign-up-password" name="password" type="password" autocomplete="new-password" required minlength="12" aria-describedby="sign-up-password-help">
<button class="plf:input-group-action plf:input-group-addon" type="button" data-plf-password-toggle hidden></button>
</div>
<p class="plf:field-help" id="sign-up-password-help">At least 12 characters.</p>
</div>
<div class="plf:choice">
<input class="plf:checkbox" id="sign-up-terms" name="terms" type="checkbox" required>
<label class="plf:choice-label" for="sign-up-terms">
<span>
I agree to the <a class="plf:link" href="#">Terms</a>
and the <a class="plf:link" href="#">Privacy Policy</a>
</span>
</label>
</div>
</div>
<button class="plf:btn plf:btn-primary plf:w-full">Create account</button>
<p class="plf:text-center plf:text-sm plf:text-base-content/70">
Already have an account? <button class="plf:link" type="button" data-tab="sign-in-panel">Sign in</button>
</p>
</form>
</div>
</div>
</div>
</div>
</div>
<script>
const tabs = document.getElementById('account-tabs');
// A footer button selects the other tab and moves the focus to it.
for (const button of tabs.querySelectorAll('[data-tab]')) {
button.addEventListener('click', () => {
PLF.Tabs.getOrCreateInstance(tabs).select(button.dataset.tab);
tabs.querySelector('[aria-selected="true"]').focus();
});
}
// An address that ends in #sign-up opens the Sign up tab once the runtime has loaded; no element has that id, so the page
// does not scroll past the tabs.
document.addEventListener('DOMContentLoaded', () => {
if (location.hash === '#sign-up') PLF.Tabs.getOrCreateInstance(tabs).select('sign-up-panel');
});
</script>'use client';
import { useEffect, useId, useRef, useState, type CSSProperties } from 'react';
import {
Button, buttonClass, CardBody, Checkbox, Field, Icon, InputGroup, InputGroupAddon, InputGroupControl, InputGroupPasswordToggle, Link,
linkClass, Tab, TabContent, Tabs, TabsList,
} from '@pelatform/react';
export function Example() {
const [tab, setTab] = useState('sign-in');
const signInTab = useRef<HTMLButtonElement>(null);
const signUpTab = useRef<HTMLButtonElement>(null);
const passwordId = useId();
// An address that ends in #sign-up opens the Sign up tab.
useEffect(() => {
if (location.hash === '#sign-up') setTab('sign-up');
}, []);
// A footer button selects the other tab and moves the focus to it.
const show = (value: string) => {
setTab(value);
(value === 'sign-up' ? signUpTab : signInTab).current?.focus();
};
return (
<div className="grid min-h-dvh place-items-center bg-base-200 p-6">
<div className="flex w-full max-w-sm flex-col gap-6">
<a className="flex items-center gap-2 self-center font-medium" href="#">
<span className="grid size-8 place-items-center rounded-lg bg-primary text-primary-content">
<Icon name="stack-2" className="size-5" />
</span>
Pelatform
</a>
<Tabs className="card" value={tab} onValueChange={setTab}>
<CardBody className="flex flex-col gap-6">
<TabsList variant="box" className="w-full" aria-label="Account">
<Tab value="sign-in" className="flex-1" ref={signInTab}>Sign in</Tab>
<Tab value="sign-up" className="flex-1" ref={signUpTab}>Sign up</Tab>
</TabsList>
<TabContent value="sign-in" tabIndex={-1}>
<form className="flex flex-col gap-6" method="post">
<div className="flex flex-col gap-1">
<h1 className="text-xl font-semibold">Welcome back</h1>
<p className="text-sm text-base-content/70">Sign in to your Pelatform workspace.</p>
</div>
<a
className={`${buttonClass()} w-full`}
href="#"
style={{ '--plf-button-bg': '#fff', '--plf-button-color': '#1f1f1f', '--plf-button-border': '#747775', '--plf-button-hover-bg': '#f2f2f2' } as CSSProperties}
>
<svg className="btn-icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<path fill="#ea4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z" />
<path fill="#4285f4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z" />
<path fill="#fbbc05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z" />
<path fill="#34a853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z" />
</svg>
Sign in with Google
</a>
<div className="flex items-center gap-3 text-xs text-base-content/70">
<span className="divider flex-1" />
or continue with email
<span className="divider flex-1" />
</div>
<div className="flex flex-col gap-4">
<Field label="Email">
<InputGroup>
<InputGroupAddon>
<Icon name="mail" />
</InputGroupAddon>
<InputGroupControl name="email" type="email" dir="ltr" autoComplete="email" placeholder="name@example.com" required />
</InputGroup>
</Field>
<div className="field">
<div className="flex items-center justify-between gap-2">
<label className="field-label" htmlFor={passwordId}>Password</label>
<Link variant="muted" className="text-sm" href="#">Forgot password?</Link>
</div>
<InputGroup>
<InputGroupAddon>
<Icon name="key" />
</InputGroupAddon>
<InputGroupControl id={passwordId} name="password" type="password" autoComplete="current-password" required />
<InputGroupPasswordToggle addon />
</InputGroup>
</div>
</div>
<Button color="primary" className="w-full">Sign in</Button>
<p className="text-center text-sm text-base-content/70">
New to Pelatform? <button className={linkClass()} type="button" onClick={() => show('sign-up')}>Create an account</button>
</p>
</form>
</TabContent>
<TabContent value="sign-up" tabIndex={-1}>
<form className="flex flex-col gap-6" method="post">
<div className="flex flex-col gap-1">
<h1 className="text-xl font-semibold">Create your account</h1>
<p className="text-sm text-base-content/70">Start a free Pelatform workspace.</p>
</div>
<a
className={`${buttonClass()} w-full`}
href="#"
style={{ '--plf-button-bg': '#fff', '--plf-button-color': '#1f1f1f', '--plf-button-border': '#747775', '--plf-button-hover-bg': '#f2f2f2' } as CSSProperties}
>
<svg className="btn-icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<path fill="#ea4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z" />
<path fill="#4285f4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z" />
<path fill="#fbbc05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z" />
<path fill="#34a853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z" />
</svg>
Sign up with Google
</a>
<div className="flex items-center gap-3 text-xs text-base-content/70">
<span className="divider flex-1" />
or continue with email
<span className="divider flex-1" />
</div>
<div className="flex flex-col gap-4">
<Field label="Full name">
<InputGroup>
<InputGroupAddon>
<Icon name="user" />
</InputGroupAddon>
<InputGroupControl name="name" type="text" autoComplete="name" required />
</InputGroup>
</Field>
<Field label="Work email">
<InputGroup>
<InputGroupAddon>
<Icon name="mail" />
</InputGroupAddon>
<InputGroupControl name="email" type="email" dir="ltr" autoComplete="email" placeholder="name@example.com" required />
</InputGroup>
</Field>
<Field label="Password" help="At least 12 characters.">
<InputGroup>
<InputGroupAddon>
<Icon name="key" />
</InputGroupAddon>
<InputGroupControl name="password" type="password" autoComplete="new-password" required minLength={12} />
<InputGroupPasswordToggle addon />
</InputGroup>
</Field>
<Checkbox name="terms" required>
<span>
I agree to the <Link href="#">Terms</Link> and the <Link href="#">Privacy Policy</Link>
</span>
</Checkbox>
</div>
<Button color="primary" className="w-full">Create account</Button>
<p className="text-center text-sm text-base-content/70">
Already have an account? <button className={linkClass()} type="button" onClick={() => show('sign-in')}>Sign in</button>
</p>
</form>
</TabContent>
</CardBody>
</Tabs>
</div>
</div>
);
}'use client';
import { useEffect, useId, useRef, useState, type CSSProperties } from 'react';
import {
Button, buttonClass, CardBody, Checkbox, Field, Icon, InputGroup, InputGroupAddon, InputGroupControl, InputGroupPasswordToggle, Link,
linkClass, Tab, TabContent, Tabs, TabsList,
} from '@pelatform/react/prefixed';
export function Example() {
const [tab, setTab] = useState('sign-in');
const signInTab = useRef<HTMLButtonElement>(null);
const signUpTab = useRef<HTMLButtonElement>(null);
const passwordId = useId();
// An address that ends in #sign-up opens the Sign up tab.
useEffect(() => {
if (location.hash === '#sign-up') setTab('sign-up');
}, []);
// A footer button selects the other tab and moves the focus to it.
const show = (value: string) => {
setTab(value);
(value === 'sign-up' ? signUpTab : signInTab).current?.focus();
};
return (
<div className="plf:grid plf:min-h-dvh plf:place-items-center plf:bg-base-200 plf:p-6">
<div className="plf:flex plf:w-full plf:max-w-sm plf:flex-col plf:gap-6">
<a className="plf:flex plf:items-center plf:gap-2 plf:self-center plf:font-medium" href="#">
<span className="plf:grid plf:size-8 plf:place-items-center plf:rounded-lg plf:bg-primary plf:text-primary-content">
<Icon name="stack-2" className="plf:size-5" />
</span>
Pelatform
</a>
<Tabs className="plf:card" value={tab} onValueChange={setTab}>
<CardBody className="plf:flex plf:flex-col plf:gap-6">
<TabsList variant="box" className="plf:w-full" aria-label="Account">
<Tab value="sign-in" className="plf:flex-1" ref={signInTab}>Sign in</Tab>
<Tab value="sign-up" className="plf:flex-1" ref={signUpTab}>Sign up</Tab>
</TabsList>
<TabContent value="sign-in" tabIndex={-1}>
<form className="plf:flex plf:flex-col plf:gap-6" method="post">
<div className="plf:flex plf:flex-col plf:gap-1">
<h1 className="plf:text-xl plf:font-semibold">Welcome back</h1>
<p className="plf:text-sm plf:text-base-content/70">Sign in to your Pelatform workspace.</p>
</div>
<a
className={`${buttonClass()} plf:w-full`}
href="#"
style={{ '--plf-button-bg': '#fff', '--plf-button-color': '#1f1f1f', '--plf-button-border': '#747775', '--plf-button-hover-bg': '#f2f2f2' } as CSSProperties}
>
<svg className="plf:btn-icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<path fill="#ea4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z" />
<path fill="#4285f4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z" />
<path fill="#fbbc05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z" />
<path fill="#34a853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z" />
</svg>
Sign in with Google
</a>
<div className="plf:flex plf:items-center plf:gap-3 plf:text-xs plf:text-base-content/70">
<span className="plf:divider plf:flex-1" />
or continue with email
<span className="plf:divider plf:flex-1" />
</div>
<div className="plf:flex plf:flex-col plf:gap-4">
<Field label="Email">
<InputGroup>
<InputGroupAddon>
<Icon name="mail" />
</InputGroupAddon>
<InputGroupControl name="email" type="email" dir="ltr" autoComplete="email" placeholder="name@example.com" required />
</InputGroup>
</Field>
<div className="plf:field">
<div className="plf:flex plf:items-center plf:justify-between plf:gap-2">
<label className="plf:field-label" htmlFor={passwordId}>Password</label>
<Link variant="muted" className="plf:text-sm" href="#">Forgot password?</Link>
</div>
<InputGroup>
<InputGroupAddon>
<Icon name="key" />
</InputGroupAddon>
<InputGroupControl id={passwordId} name="password" type="password" autoComplete="current-password" required />
<InputGroupPasswordToggle addon />
</InputGroup>
</div>
</div>
<Button color="primary" className="plf:w-full">Sign in</Button>
<p className="plf:text-center plf:text-sm plf:text-base-content/70">
New to Pelatform? <button className={linkClass()} type="button" onClick={() => show('sign-up')}>Create an account</button>
</p>
</form>
</TabContent>
<TabContent value="sign-up" tabIndex={-1}>
<form className="plf:flex plf:flex-col plf:gap-6" method="post">
<div className="plf:flex plf:flex-col plf:gap-1">
<h1 className="plf:text-xl plf:font-semibold">Create your account</h1>
<p className="plf:text-sm plf:text-base-content/70">Start a free Pelatform workspace.</p>
</div>
<a
className={`${buttonClass()} plf:w-full`}
href="#"
style={{ '--plf-button-bg': '#fff', '--plf-button-color': '#1f1f1f', '--plf-button-border': '#747775', '--plf-button-hover-bg': '#f2f2f2' } as CSSProperties}
>
<svg className="plf:btn-icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<path fill="#ea4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z" />
<path fill="#4285f4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z" />
<path fill="#fbbc05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z" />
<path fill="#34a853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z" />
</svg>
Sign up with Google
</a>
<div className="plf:flex plf:items-center plf:gap-3 plf:text-xs plf:text-base-content/70">
<span className="plf:divider plf:flex-1" />
or continue with email
<span className="plf:divider plf:flex-1" />
</div>
<div className="plf:flex plf:flex-col plf:gap-4">
<Field label="Full name">
<InputGroup>
<InputGroupAddon>
<Icon name="user" />
</InputGroupAddon>
<InputGroupControl name="name" type="text" autoComplete="name" required />
</InputGroup>
</Field>
<Field label="Work email">
<InputGroup>
<InputGroupAddon>
<Icon name="mail" />
</InputGroupAddon>
<InputGroupControl name="email" type="email" dir="ltr" autoComplete="email" placeholder="name@example.com" required />
</InputGroup>
</Field>
<Field label="Password" help="At least 12 characters.">
<InputGroup>
<InputGroupAddon>
<Icon name="key" />
</InputGroupAddon>
<InputGroupControl name="password" type="password" autoComplete="new-password" required minLength={12} />
<InputGroupPasswordToggle addon />
</InputGroup>
</Field>
<Checkbox name="terms" required>
<span>
I agree to the <Link href="#">Terms</Link> and the <Link href="#">Privacy Policy</Link>
</span>
</Checkbox>
</div>
<Button color="primary" className="plf:w-full">Create account</Button>
<p className="plf:text-center plf:text-sm plf:text-base-content/70">
Already have an account? <button className={linkClass()} type="button" onClick={() => show('sign-in')}>Sign in</button>
</p>
</form>
</TabContent>
</CardBody>
</Tabs>
</div>
</div>
);
}An address that ends in #sign-up opens the Sign up tab, so a landing page can link straight to it.
<link rel="stylesheet" href="pelatform-ui.min.css">
<link rel="stylesheet" href="icons/icons.min.css">
<script src="pelatform-ui.min.js" defer></script><link rel="stylesheet" href="core/pelatform-ui-core.min.css">
<link rel="stylesheet" href="components/button.min.css">
<link rel="stylesheet" href="components/form.min.css">
<link rel="stylesheet" href="components/card.min.css">
<link rel="stylesheet" href="components/tabs.min.css">
<link rel="stylesheet" href="components/separator.min.css">
<link rel="stylesheet" href="components/link.min.css">
<link rel="stylesheet" href="icons/icons.min.css">
<script src="core/pelatform-ui-core.min.js" defer></script>
<script src="components/tabs.min.js" defer></script>
<script src="components/input-group.min.js" defer></script>
<script>document.addEventListener('DOMContentLoaded', () => PLF.init());</script><link rel="stylesheet" href="pelatform-ui.prefixed.min.css">
<link rel="stylesheet" href="icons/icons.prefixed.min.css">
<script src="pelatform-ui.min.js" defer></script><link rel="stylesheet" href="core/pelatform-ui-core.prefixed.min.css">
<link rel="stylesheet" href="components/button.prefixed.min.css">
<link rel="stylesheet" href="components/form.prefixed.min.css">
<link rel="stylesheet" href="components/card.prefixed.min.css">
<link rel="stylesheet" href="components/tabs.prefixed.min.css">
<link rel="stylesheet" href="components/separator.prefixed.min.css">
<link rel="stylesheet" href="components/link.prefixed.min.css">
<link rel="stylesheet" href="icons/icons.prefixed.min.css">
<script src="core/pelatform-ui-core.min.js" defer></script>
<script src="components/tabs.min.js" defer></script>
<script src="components/input-group.min.js" defer></script>
<script>document.addEventListener('DOMContentLoaded', () => PLF.init());</script>