8bitcn UI
pagelogin-pagesource: ok · hit
8-bit Login Page
A simple 8-bit login page component
npx shadcn add "https://www.8bitcn.com/r/login-page.json"free
Where it goes
login
Use cases
saas-landing
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · lightRegistry dependencies
buttoncardinputlabel
Files7 · source cached 10/8/2026, 2:10:10 PM
app/login/page.tsx
import { GalleryVerticalEnd } from "lucide-react";
import Image from "next/image";
import { LoginForm } from "@/components/ui/8bit/blocks/login-form";
export default function LoginPage() {
return (
<div className="grid min-h-svh lg:grid-cols-2">
<div className="flex flex-col gap-4 p-6 md:p-10">
<div className="flex justify-center gap-2 md:justify-start">
{/** biome-ignore lint/a11y/useValidAnchor: We're linking to the top of the page */}
<a className="flex items-center gap-2 font-medium" href="#">
<div className="flex size-6 items-center justify-center rounded-md bg-primary text-primary-foreground">
<GalleryVerticalEnd className="size-4" />
</div>
Acme Inc.
</a>
</div>
<div className="flex flex-1 items-center justify-center">
<div className="w-full max-w-md">
<LoginForm />
</div>
</div>
</div>
<div className="relative hidden bg-muted lg:block">
<Image
alt="Login background"
className="absolute inset-0 h-full w-full object-cover dark:brightness-[0.2] dark:grayscale"
height={1200}
src="/placeholder.svg"
width={1200}
/>
</div>
</div>
);
}
components/ui/8bit/blocks/login-form.tsx
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/8bit/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/8bit/card";
import { Input } from "@/components/ui/8bit/input";
import { Label } from "@/components/ui/8bit/label";
export function LoginForm({
className,
...props
}: React.ComponentPropsWithoutRef<"div">) {
return (
<div className={cn("flex flex-col gap-6", className)} {...props}>
<Card>
<CardHeader>
<CardTitle className="text-2xl">Login</CardTitle>
<CardDescription className="text-xs">
Enter your email below to login to your account
</CardDescription>
</CardHeader>
<CardContent>
<form>
<div className="flex flex-col gap-6">
<div className="grid gap-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div className="grid gap-2">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-2">
<Label htmlFor="password">Password</Label>
<a
href="#"
className="inline-block text-xs underline-offset-4 hover:underline"
>
Forgot password?
</a>
</div>
<Input id="password" type="password" required />
</div>
<Button type="submit" className="w-full">
Login
</Button>
<Button variant="outline" className="w-full">
Login with Google
</Button>
</div>
<div className="mt-4 text-center text-xs">
Don't have an account?{" "}
<a href="#" className="underline underline-offset-4">
Sign up
</a>
</div>
</form>
</CardContent>
</Card>
</div>
);
}
components/ui/8bit/button.tsx
import { type VariantProps, cva } from "class-variance-authority";
import { Slot } from "radix-ui";
import type { ButtonHTMLAttributes, Ref } from "react";
import { Button as ShadcnButton } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import "@/components/ui/8bit/styles/retro.css";
export const buttonVariants = cva("", {
variants: {
font: {
normal: "",
retro: "retro",
},
variant: {
default: "bg-foreground",
destructive: "bg-foreground",
outline: "bg-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "",
sm: "",
lg: "",
icon: "",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
});
export interface BitButtonProps
extends ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
ref?: Ref<HTMLButtonElement>;
}
interface ButtonDecorationsProps {
size: BitButtonProps["size"];
variant: BitButtonProps["variant"];
}
function ButtonDecorations({ size, variant }: ButtonDecorationsProps) {
return (
<span
aria-hidden="true"
className="pointer-events-none contents"
data-slot="button-decorations"
>
{variant !== "ghost" && variant !== "link" && size !== "icon" && (
<>
{/* Pixelated border */}
<span className="absolute -top-1.5 left-1.5 h-1.5 w-1/2 bg-foreground dark:bg-ring" />
<span className="absolute -top-1.5 right-1.5 h-1.5 w-1/2 bg-foreground dark:bg-ring" />
<span className="absolute -bottom-1.5 left-1.5 h-1.5 w-1/2 bg-foreground dark:bg-ring" />
<span className="absolute -bottom-1.5 right-1.5 h-1.5 w-1/2 bg-foreground dark:bg-ring" />
<span className="absolute top-0 left-0 size-1.5 bg-foreground dark:bg-ring" />
<span className="absolute top-0 right-0 size-1.5 bg-foreground dark:bg-ring" />
<span className="absolute bottom-0 left-0 size-1.5 bg-foreground dark:bg-ring" />
<span className="absolute right-0 bottom-0 size-1.5 bg-foreground dark:bg-ring" />
<span className="absolute top-1.5 -left-1.5 h-[calc(100%-12px)] w-1.5 bg-foreground dark:bg-ring" />
<span className="absolute top-1.5 -right-1.5 h-[calc(100%-12px)] w-1.5 bg-foreground dark:bg-ring" />
{variant !== "outline" && (
<>
{/* Top shadow */}
<span className="absolute top-0 left-0 h-1.5 w-full bg-foreground/20" />
<span className="absolute top-1.5 left-0 h-1.5 w-3 bg-foreground/20" />
{/* Bottom shadow */}
<span className="absolute bottom-0 left-0 h-1.5 w-full bg-foreground/20" />
<span className="absolute right-0 bottom-1.5 h-1.5 w-3 bg-foreground/20" />
</>
)}
</>
)}
{size === "icon" && (
<>
<span className="absolute top-0 left-0 h-[5px] w-full bg-foreground md:h-1.5 dark:bg-ring" />
<span className="absolute bottom-0 h-[5px] w-full bg-foreground md:h-1.5 dark:bg-ring" />
<span className="absolute top-1 -left-1 h-1/2 w-[5px] bg-foreground md:w-1.5 dark:bg-ring" />
<span className="absolute bottom-1 -left-1 h-1/2 w-[5px] bg-foreground md:w-1.5 dark:bg-ring" />
<span className="absolute top-1 -right-1 h-1/2 w-[5px] bg-foreground md:w-1.5 dark:bg-ring" />
<span className="absolute -right-1 bottom-1 h-1/2 w-[5px] bg-foreground md:w-1.5 dark:bg-ring" />
</>
)}
</span>
);
}
function Button({
asChild = false,
children,
className,
font,
size,
variant,
...props
}: BitButtonProps) {
const decorations = <ButtonDecorations size={size} variant={variant} />;
return (
<ShadcnButton
{...props}
className={cn(
"rounded-none active:translate-y-1 transition-transform relative inline-flex items-center justify-center gap-1.5 border-none",
size === "icon" && "mx-1 my-0",
font !== "normal" && "retro",
className
)}
size={size}
variant={variant}
asChild={asChild}
>
{asChild ? <Slot.Slottable>{children}</Slot.Slottable> : children}
{decorations}
</ShadcnButton>
);
}
export { Button };
components/ui/8bit/card.tsx
import { type VariantProps, cva } from "class-variance-authority";
import { cn } from "@/lib/utils";
import {
Card as ShadcnCard,
CardAction as ShadcnCardAction,
CardContent as ShadcnCardContent,
CardDescription as ShadcnCardDescription,
CardFooter as ShadcnCardFooter,
CardHeader as ShadcnCardHeader,
CardTitle as ShadcnCardTitle,
} from "@/components/ui/card";
import "@/components/ui/8bit/styles/retro.css";
export const cardVariants = cva("", {
variants: {
font: {
normal: "",
retro: "retro",
},
},
defaultVariants: {
font: "retro",
},
});
export interface BitCardProps
extends React.ComponentProps<"div">,
VariantProps<typeof cardVariants> {
asChild?: boolean;
}
function Card({ className, font, ...props }: BitCardProps) {
return (
<div
className={cn(
"relative bg-card text-card-foreground border-y-6 border-foreground dark:border-ring p-0!",
className
)}
>
<ShadcnCard
{...props}
className={cn(
"rounded-none border-0 w-full! h-full flex flex-col bg-card text-card-foreground shadow-none",
font !== "normal" && "retro",
className
)}
/>
<div
className={cn("absolute inset-0 border-x-6 -mx-1.5 border-inherit pointer-events-none")}
aria-hidden="true"
/>
</div>
);
}
function CardHeader({ ...props }: BitCardProps) {
const { className, font } = props;
return (
<ShadcnCardHeader
className={cn(font !== "normal" && "retro", className)}
{...props}
/>
);
}
function CardTitle({ ...props }: BitCardProps) {
const { className, font } = props;
return (
<ShadcnCardTitle
className={cn(font !== "normal" && "retro", className)}
{...props}
/>
);
}
function CardDescription({ ...props }: BitCardProps) {
const { className, font } = props;
return (
<ShadcnCardDescription
className={cn(font !== "normal" && "retro", className)}
{...props}
/>
);
}
function CardAction({ ...props }: BitCardProps) {
const { className, font } = props;
return (
<ShadcnCardAction
className={cn(font !== "normal" && "retro", className)}
{...props}
/>
);
}
function CardContent({ ...props }: BitCardProps) {
const { className, font } = props;
return (
<ShadcnCardContent
className={cn("flex-1", font !== "normal" && "retro", className)}
{...props}
/>
);
}
function CardFooter({ ...props }: BitCardProps) {
const { className, font } = props;
return (
<ShadcnCardFooter
data-slot="card-footer"
className={cn(font !== "normal" && "retro", className)}
{...props}
/>
);
}
export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardAction,
CardDescription,
CardContent,
};
components/ui/8bit/input.tsx
import { type VariantProps, cva } from "class-variance-authority";
import { cn } from "@/lib/utils";
import { Input as ShadcnInput } from "@/components/ui/input";
import "@/components/ui/8bit/styles/retro.css";
export const inputVariants = cva("", {
variants: {
font: {
normal: "",
retro: "retro",
},
},
defaultVariants: {
font: "retro",
},
});
export interface BitInputProps
extends React.InputHTMLAttributes<HTMLInputElement>,
VariantProps<typeof inputVariants> {
asChild?: boolean;
}
function Input({ ...props }: BitInputProps) {
const { className, font } = props;
return (
<div
className={cn(
"relative border-y-6 border-foreground dark:border-ring !p-0 flex items-center",
className
)}
>
<ShadcnInput
{...props}
className={cn(
"rounded-none ring-0 !w-full",
font !== "normal" && "retro",
className
)}
/>
<div
className="absolute inset-0 border-x-6 -mx-1.5 border-foreground dark:border-ring pointer-events-none"
aria-hidden="true"
/>
</div>
);
}
export { Input };
components/ui/8bit/label.tsx
"use client";
import type * as React from "react";
import type * as LabelPrimitive from "@radix-ui/react-label";
import { type VariantProps, cva } from "class-variance-authority";
import { cn } from "@/lib/utils";
import { Label as ShadcnLabel } from "@/components/ui/label";
import "@/components/ui/8bit/styles/retro.css";
export const inputVariants = cva("", {
variants: {
font: {
normal: "",
retro: "retro",
},
},
defaultVariants: {
font: "retro",
},
});
interface BitLabelProps
extends React.ComponentProps<typeof LabelPrimitive.Root>,
VariantProps<typeof inputVariants> {
asChild?: boolean;
}
function Label({ className, font, ...props }: BitLabelProps) {
return (
<ShadcnLabel
className={cn(className, font !== "normal" && "retro")}
{...props}
/>
);
}
export { Label };
components/ui/8bit/styles/retro.css
@import url("https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap");
.retro {
font-family:
"Press Start 2P",
system-ui,
-apple-system,
sans-serif;
line-height: 1.5;
letter-spacing: 0.5px;
}
.pixelated {
image-rendering: pixelated;
image-rendering: crisp-edges;
}