feat(ui): Improve login card design and theme customization
Refactor the `Button` component, add new `LoginCard` component, update `ThemePicker` component, edit global CSS styles to include card background and foreground and style the login page, including the logo and login form.
This commit is contained in:
parent
9f9c2157f5
commit
4f4e73318a
11 changed files with 122 additions and 29 deletions
|
@ -39,6 +39,8 @@
|
|||
--active-secondary: oklch(0.4254 0.133 272.15);
|
||||
--disabled-secondary: oklch(0.4937 0.1697 271.26 / 0.5);
|
||||
|
||||
--card: var(--neutral-800);
|
||||
|
||||
/* ------------------- */
|
||||
|
||||
--foreground: oklch(0.13 0.028 261.692);
|
||||
|
@ -99,7 +101,7 @@
|
|||
}
|
||||
|
||||
@theme inline {
|
||||
--transparent: var(--transpatent);
|
||||
--transparent: var(--transparent);
|
||||
|
||||
--color-neutral-000: var(--neutral-000);
|
||||
--color-neutral-100: var(--neutral-100);
|
||||
|
@ -114,12 +116,12 @@
|
|||
--color-neutral-800: var(--neutral-800);
|
||||
--color-neutral-900: var(--neutral-900);
|
||||
|
||||
--background: var(--neutral-750);
|
||||
--base: var(--neutral-800);
|
||||
--text: var(--neutral-000);
|
||||
--text-alt: var(--neutral-900);
|
||||
--background-disabled: var(--neutral-500);
|
||||
--text-disabled: var(--neutral-700);
|
||||
--color-background: var(--neutral-750);
|
||||
--color-base: var(--neutral-800);
|
||||
--color-text: var(--text);
|
||||
--color-text-alt: var(--text-alt);
|
||||
--color-background-disabled: var(--neutral-500);
|
||||
--color-text-disabled: var(--neutral-700);
|
||||
--radius: 0.688rem;
|
||||
|
||||
--color-primary: var(--primary);
|
||||
|
@ -224,7 +226,7 @@
|
|||
--neutral-900: oklch(0 0 0);
|
||||
|
||||
--background: var(--neutral-750);
|
||||
--base: var(--neutral-800);
|
||||
--base: var(--neutral-750);
|
||||
--text: var(--neutral-000);
|
||||
--text-alt: var(--neutral-900);
|
||||
--background-disabled: var(--neutral-500);
|
||||
|
@ -240,12 +242,12 @@
|
|||
--active-secondary: oklch(0.4471 0.15 271.61);
|
||||
--disabled-secondary: oklch(0.6065 0.213 271.11 / 0.4);
|
||||
|
||||
--card: var(--neutral-750);
|
||||
|
||||
/* ------------------- */
|
||||
|
||||
--foreground: oklch(0.985 0.002 247.839);
|
||||
|
||||
--card: oklch(0.21 0.034 264.665);
|
||||
|
||||
--card-foreground: oklch(0.985 0.002 247.839);
|
||||
|
||||
--popover: oklch(0.21 0.034 264.665);
|
||||
|
|
|
@ -2,13 +2,17 @@ import { auth, providerMap } from '@/auth';
|
|||
import SSOLogin from '@/components/user/sso-login-button';
|
||||
import LoginForm from '@/components/user/login-form';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Button } from '@/components/custom-ui/button';
|
||||
import Image from 'next/image';
|
||||
import { Separator } from '@/components/custom-ui/separator';
|
||||
import Logo from '@/components/logo';
|
||||
|
||||
import '@/app/globals.css';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
} from '@/components/custom-ui/login-card';
|
||||
import { ThemePicker } from '@/components/user/theme-picker';
|
||||
import {
|
||||
HoverCard,
|
||||
|
@ -30,14 +34,9 @@ export default async function LoginPage() {
|
|||
<ThemePicker />
|
||||
</div>
|
||||
<div>
|
||||
<Card className='w-[350px] max-w-screen'>
|
||||
<CardHeader>
|
||||
<Logo
|
||||
colorType='colored'
|
||||
logoType='secondary'
|
||||
width={200}
|
||||
height={200}
|
||||
></Logo>
|
||||
<Card className='w-[350px] max-w-screen;'>
|
||||
<CardHeader className='grid place-items-center'>
|
||||
<Logo colorType='colored' logoType='secondary'></Logo>
|
||||
</CardHeader>
|
||||
<CardContent className='gap-6 flex flex-col items-center'>
|
||||
<LoginForm />
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { signOut } from '@/auth';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Button } from '@/components/custom-ui/button';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Button } from '@/components/ui/button';
|
||||
import { Button } from '@/components/custom-ui/button';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
|
|
|
@ -15,8 +15,9 @@ const buttonVariants = cva(
|
|||
'bg-secondary text-text-alt shadow-xs hover:bg-hover-secondary active:bg-active-secondary',
|
||||
outline:
|
||||
'border-2 border-primary bg-transparent text-text shadow-xs hover:bg-primary hover:border-neutral-000 hover:border-1.5 hover:text-neutral-000 active:bg-active-primary',
|
||||
destructive:
|
||||
/*destructive:
|
||||
'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
|
||||
*/
|
||||
ghost:
|
||||
'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
|
||||
link: 'text-text underline-offset-4 hover:underline',
|
92
src/components/custom-ui/login-card.tsx
Normal file
92
src/components/custom-ui/login-card.tsx
Normal file
|
@ -0,0 +1,92 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
function Card({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot='card'
|
||||
className={cn(
|
||||
'bg-card flex flex-col gap-6 py-6 shadow-[4px_4px_9px_9px_rgba(0,0,0,0.25)] rounded-[11px]',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot='card-header'
|
||||
className={cn(
|
||||
'@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot='card-title'
|
||||
className={cn('leading-none font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot='card-description'
|
||||
className={cn('text-muted-foreground text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot='card-action'
|
||||
className={cn(
|
||||
'col-start-2 row-span-2 row-start-1 self-start justify-self-end',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot='card-content'
|
||||
className={cn('px-6', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot='card-footer'
|
||||
className={cn('flex items-center px-6 [.border-t]:pt-6', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardFooter,
|
||||
CardTitle,
|
||||
CardAction,
|
||||
CardDescription,
|
||||
CardContent,
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
import { Button } from '@/components/ui/button';
|
||||
import { Button } from '@/components/custom-ui/button';
|
||||
|
||||
import { IconProp } from '@fortawesome/fontawesome-svg-core';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
@ -12,7 +12,7 @@ export function IconButton({
|
|||
children: React.ReactNode;
|
||||
} & React.ComponentProps<typeof Button>) {
|
||||
return (
|
||||
<Button type='button' variant='default' {...props}>
|
||||
<Button type='button' variant='secondary' {...props}>
|
||||
<FontAwesomeIcon icon={icon} className='mr-2' />
|
||||
{children}
|
||||
</Button>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { signIn } from '@/auth';
|
||||
import LabeledInput from '@/components/labeled-input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Button } from '@/components/custom-ui/button';
|
||||
import { AuthError } from 'next-auth';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Button } from '../ui/button';
|
||||
import { Button } from '../custom-ui/button';
|
||||
import Link from 'next/link';
|
||||
|
||||
export function RedirectButton({
|
||||
|
|
|
@ -22,7 +22,6 @@ export default function SSOLogin({
|
|||
type='submit'
|
||||
variant='secondary'
|
||||
icon={faOpenid}
|
||||
icon={faOpenid}
|
||||
>
|
||||
Login with {providerDisplayName}
|
||||
</IconButton>
|
||||
|
|
|
@ -4,7 +4,7 @@ import * as React from 'react';
|
|||
import { Moon, Sun } from 'lucide-react';
|
||||
import { useTheme } from 'next-themes';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Button } from '@/components/custom-ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue