/** * Showcase · case4 — Login (ログイン) * * The reference-design auth surface, served at `/showcase/case4-login`. * Built ENTIRELY from real @godxjp/ui primitives — the design handoff * (UI Kit.html §04 + Handy login) recreated as a "skeleton" (intent + look), * not a transcription of the prototype DOM. * * Composition map (prototype block → @godxjp/ui primitive): * page shell ............. AuthShell (the unauthenticated root shell) * comfortable density .... AuthShell's own control tier → 44px touch floor * locale + theme toggle .. AuthShell `actions` (banner, inline end) * two-column measure ..... AuthShell `measure="wide"` → 64rem content slot * split brand panel ...... Card composition, hidden < lg (mobile-first) * auth card .............. Card (shadow-lg — the one resting shadow login allows) * SSO buttons ............ Button(outline) full-width + brand SVG slot * email / password ....... FormField + Input / PasswordInput * "forgot" link .......... FormField labelAddon (right-aligned ghost link) * divider "または" ....... Separator pair + center text * footer ................. muted 利用規約 · プライバシーポリシー links * * DNA applied: comfortable density (44px), small headings (20/18/14/13), * 14px/1.7 body, fixed color signaling (the single --primary action = ログイン), * 1px-border surfaces, no resting shadow EXCEPT the auth card (documented * exception), quiet factual copy, no emoji, ASCII quotes in code-like labels. */ import * as React from "react"; import { Languages, Monitor, Moon, Sun } from "lucide-react"; import { Button, Heading, Text } from "@godxjp/ui/general"; import { FormField, Input, PasswordInput, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, ToggleGroup, ToggleGroupItem, } from "@godxjp/ui/data-entry"; import { Avatar, AvatarFallback, Card, CardContent, CardHeader, CardTitle, CardDescription, Descriptions, } from "@godxjp/ui/data-display"; import { AuthShell, Flex, ResponsiveGrid, Separator } from "@godxjp/ui/layout"; // ── The single allowed multi-color brand mark (Google "G") ───────────────────── // Icons normally inherit currentColor; a third-party brand mark is the documented // exception (ui-kit-surfaces §5). lucide has no Google glyph, so the official // 4-color path is inlined — the only place a non-token fill appears. function GoogleMark() { return ( ); } // ── Brand lockup ("勤" mark + Acme · 勤怠 wordmark) ─────────────────────────── function BrandLockup({ size = "md" }: { size?: "md" | "lg" }) { return (
Acme{" "} · {" "} 勤怠 勤怠管理プラットフォーム
); } const LOCALES: Array<{ code: string; label: string }> = [ { code: "ja", label: "日本語" }, { code: "en", label: "English" }, { code: "vi", label: "Tiếng Việt" }, ]; export default function LoginShowcase() { // Independent local state (Rule #6) — controlled + uncontrolled both shown: // theme/locale are controlled (value + onValueChange), the fields are uncontrolled // with seeded defaultValue so the filled state is visible at rest (Rule #2). const [locale, setLocale] = React.useState("ja"); const [theme, setTheme] = React.useState("light"); return ( } /* Page-level controls, pinned to the banner's inline end. */ actions={ <> v && setTheme(v)} variant="outline" size="sm" aria-label="テーマを切り替え" > } > {/* Centered auth area. Split brand panel + card sit side-by-side from lg. */} {/* Split brand panel — hidden on mobile (mobile-first), shown from lg. */} {/* Auth card — the one surface allowed a resting shadow (shadow-lg). */} ログイン 勤怠管理プラットフォームへようこそ {/* SSO buttons — outline, full-width, 44px (comfortable). */} {/* Divider "または" — Separator pair + centered label. */} または {/* Email — uncontrolled, seeded so the filled state shows at rest. */}
{ e.preventDefault(); }} > {/* Password — labelAddon hosts the right-aligned "忘れた場合" link. */} お忘れの場合 } > {/* The single --primary action of the view. */}
ログインすると{" "} {" "} ·{" "} {" "} に同意したものとみなされます。
); }