688 lines
30 KiB
HTML
688 lines
30 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Vibn AI Templates — UI showcase</title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=DM+Serif+Display:ital@0;1&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="vibn-ai-templates/tokens.css">
|
|
<style>
|
|
html, body { margin: 0; padding: 0; min-height: 100%; background: #f0eee9; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif; }
|
|
</style>
|
|
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
|
|
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
|
|
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
|
|
<script type="text/babel" src="design-canvas.jsx"></script>
|
|
<script type="text/babel" src="vibn-ai-templates/icons.jsx"></script>
|
|
<script type="text/babel" src="vibn-ai-templates/components.jsx"></script>
|
|
<script type="text/babel" src="vibn-ai-templates/shells.jsx"></script>
|
|
|
|
<script type="text/babel">
|
|
const { DesignCanvas, DCSection, DCArtboard,
|
|
Button, IconButton, Field, Input, Textarea, Select, FieldGroup,
|
|
Checkbox, Radio, Switch, Card, CardHeader, Divider,
|
|
Badge, Avatar, AvatarStack, Tabs, Table, Modal, Banner, KBD, Spinner,
|
|
SidebarShell, TopbarShell, RailShell,
|
|
AuthCenteredShell, AuthSplitShell, AuthGlassShell,
|
|
Icon, icons, VibnMark } = window;
|
|
|
|
// ─── Section helpers ────────────────────────────────────────
|
|
const SubHeading = ({ children }) => (
|
|
<div style={{
|
|
fontSize: "var(--text-xs)", color: "var(--text-3)",
|
|
letterSpacing: "0.08em", textTransform: "uppercase",
|
|
fontWeight: 500, marginBottom: 10,
|
|
}}>{children}</div>
|
|
);
|
|
|
|
const ThemeFrame = ({ theme, children }) => (
|
|
// Themed wrapper — note: the artboard contents must be wrapped in
|
|
// a theme class so all CSS-var reads inside re-bind to that theme.
|
|
<div className={`theme-${theme}`} style={{ width: "100%", height: "100%" }}>
|
|
<div className="vibn-app" style={{ width: "100%", height: "100%", overflow: "auto" }}>
|
|
{children}
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
// ─── 1 · Foundations / token swatches ───────────────────────
|
|
const Foundations = ({ theme }) => (
|
|
<div style={{ padding: 32 }}>
|
|
<h1 style={{
|
|
margin: 0, fontFamily: "var(--font-display)",
|
|
fontSize: "var(--text-3xl)", letterSpacing: "-0.02em", fontWeight: 500,
|
|
}}>Theme · {theme}</h1>
|
|
<p style={{ color: "var(--text-2)", marginTop: 6, fontSize: "var(--text-md)" }}>
|
|
Same components, four CSS-variable themes. Tokens, type and surfaces.
|
|
</p>
|
|
|
|
<div style={{ marginTop: 28, display: "grid", gridTemplateColumns: "1fr 1fr", gap: 20 }}>
|
|
<Card>
|
|
<CardHeader title="Surface" subtitle="Page chrome + cards"/>
|
|
<div style={{ display: "grid", gridTemplateColumns: "repeat(5, 1fr)", gap: 10 }}>
|
|
{[
|
|
["--bg", "Page bg"],
|
|
["--surface", "Card"],
|
|
["--surface-2", "Card alt"],
|
|
["--surface-alt", "Sidebar"],
|
|
["--border", "Border"],
|
|
].map(([v, l]) => (
|
|
<div key={v}>
|
|
<div style={{
|
|
height: 56, borderRadius: "var(--radius)",
|
|
background: `var(${v})`, border: "1px solid var(--border)",
|
|
}}/>
|
|
<div style={{ fontSize: 11, color: "var(--text-3)", marginTop: 6 }}>{l}</div>
|
|
<div style={{ fontSize: 10, fontFamily: "var(--font-mono)", color: "var(--text-3)" }}>{v}</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader title="Accents & semantics"/>
|
|
<div style={{ display: "grid", gridTemplateColumns: "repeat(5, 1fr)", gap: 10 }}>
|
|
{[
|
|
["--accent", "Accent"],
|
|
["--accent-2", "Accent 2"],
|
|
["--success", "Success"],
|
|
["--warn", "Warn"],
|
|
["--danger", "Danger"],
|
|
].map(([v, l]) => (
|
|
<div key={v}>
|
|
<div style={{ height: 56, borderRadius: "var(--radius)", background: `var(${v})` }}/>
|
|
<div style={{ fontSize: 11, color: "var(--text-3)", marginTop: 6 }}>{l}</div>
|
|
<div style={{ fontSize: 10, fontFamily: "var(--font-mono)", color: "var(--text-3)" }}>{v}</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader title="Type scale"/>
|
|
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
|
|
{[
|
|
["Display · 38", { fontSize: 38, fontWeight: 500, fontFamily: "var(--font-display)", letterSpacing: "-0.02em" }],
|
|
["Heading · 22", { fontSize: 22, fontWeight: 600, letterSpacing: "-0.01em" }],
|
|
["Body · 13", { fontSize: 13 }],
|
|
["Caption · 11", { fontSize: 11, color: "var(--text-3)" }],
|
|
["Mono · 12", { fontFamily: "var(--font-mono)", fontSize: 12 }],
|
|
].map(([l, s], i) => (
|
|
<div key={i} style={{ ...s }}>{l} — Modern SaaS, designed for everyone.</div>
|
|
))}
|
|
</div>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader title="Radii, shadows, motion"/>
|
|
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 12, marginBottom: 14 }}>
|
|
{["sm", "", "lg"].map(s => (
|
|
<div key={s} style={{
|
|
height: 50,
|
|
background: "var(--surface-2)",
|
|
border: "1px solid var(--border)",
|
|
borderRadius: `var(--radius${s ? `-${s}` : ""})`,
|
|
display: "flex", alignItems: "center", justifyContent: "center",
|
|
fontSize: 11, color: "var(--text-3)",
|
|
}}>radius-{s || "default"}</div>
|
|
))}
|
|
</div>
|
|
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 12 }}>
|
|
{[
|
|
["shadow-sm", "var(--shadow-sm)"],
|
|
["shadow", "var(--shadow)"],
|
|
["shadow-lg", "var(--shadow-lg)"],
|
|
].map(([l, sh]) => (
|
|
<div key={l} style={{
|
|
height: 50, background: "var(--surface)",
|
|
border: "1px solid var(--border)", borderRadius: "var(--radius)",
|
|
boxShadow: sh, display: "flex", alignItems: "center", justifyContent: "center",
|
|
fontSize: 11, color: "var(--text-3)",
|
|
}}>{l}</div>
|
|
))}
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
// ─── 2 · Form atoms ─────────────────────────────────────────
|
|
const FormAtoms = () => {
|
|
const [tab, setTab] = React.useState("Account");
|
|
const [sw1, setSw1] = React.useState(true);
|
|
const [sw2, setSw2] = React.useState(false);
|
|
const [chk, setChk] = React.useState(true);
|
|
const [seg, setSeg] = React.useState("Week");
|
|
return (
|
|
<div style={{ padding: 32 }}>
|
|
<h1 style={{
|
|
margin: 0, fontFamily: "var(--font-display)", fontSize: "var(--text-2xl)",
|
|
fontWeight: 500, letterSpacing: "-0.02em",
|
|
}}>Forms & buttons</h1>
|
|
|
|
<div style={{ marginTop: 24, display: "grid", gridTemplateColumns: "1fr 1fr", gap: 20 }}>
|
|
<Card>
|
|
<CardHeader title="Buttons" subtitle="Variants, sizes, states"/>
|
|
<SubHeading>Variants</SubHeading>
|
|
<div style={{ display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 16 }}>
|
|
<Button>Primary</Button>
|
|
<Button variant="secondary">Secondary</Button>
|
|
<Button variant="ghost">Ghost</Button>
|
|
<Button variant="destructive">Delete</Button>
|
|
<Button loading>Loading</Button>
|
|
<Button disabled>Disabled</Button>
|
|
</div>
|
|
<SubHeading>Sizes & icons</SubHeading>
|
|
<div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
|
|
<Button size="sm" leadingIcon={<Icon name="plus" size={12}/>}>New deal</Button>
|
|
<Button>Sign in <Icon name="arrow" size={13}/></Button>
|
|
<Button size="lg" variant="secondary">Get a demo</Button>
|
|
<IconButton name="bell" label="Notifications"/>
|
|
<IconButton name="settings" variant="secondary" label="Settings"/>
|
|
</div>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader title="Fields" subtitle="Input, hint, error, password"/>
|
|
<Field label="Work email" hint="We'll send a 6-digit code.">
|
|
<Input value="mira@acme.io" leadingIcon={<Icon name="inbox" size={14}/>} autofocus/>
|
|
</Field>
|
|
<Field label="Password">
|
|
<Input type="password" value="••••••••••"
|
|
trailingIcon={<Icon name="eye" size={14}/>}/>
|
|
</Field>
|
|
<Field label="Workspace name" error="That name is taken.">
|
|
<Input value="lattice" invalid/>
|
|
</Field>
|
|
<Field label="Notes" optional>
|
|
<Textarea placeholder="Anything we should know?" rows={3}/>
|
|
</Field>
|
|
<Field label="Role">
|
|
<Select value="Admin" options={["Owner", "Admin", "Member", "Guest"]}/>
|
|
</Field>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader title="Controls"/>
|
|
<SubHeading>Switches</SubHeading>
|
|
<div style={{ display: "flex", flexDirection: "column", gap: 14, marginBottom: 14 }}>
|
|
<Switch checked={sw1} onChange={setSw1}
|
|
label="Email me digests" hint="Weekly summary every Monday at 9am."/>
|
|
<Switch checked={sw2} onChange={setSw2}
|
|
label="Show beta features"/>
|
|
</div>
|
|
<SubHeading>Checkboxes & radios</SubHeading>
|
|
<div style={{ display: "flex", flexDirection: "column", gap: 10, marginBottom: 14 }}>
|
|
<Checkbox checked={chk} onChange={setChk} label="I agree to the Terms" hint="And the Privacy Policy."/>
|
|
<Checkbox checked indeterminate label="Select some items"/>
|
|
<div style={{ display: "flex", gap: 16 }}>
|
|
<Radio checked={true} label="Monthly"/>
|
|
<Radio checked={false} label="Yearly · save 20%"/>
|
|
</div>
|
|
</div>
|
|
<SubHeading>Segmented</SubHeading>
|
|
<FieldGroup options={["Day", "Week", "Month", "Quarter"]} value={seg} onChange={setSeg}/>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader title="Tabs"/>
|
|
<SubHeading>Underline</SubHeading>
|
|
<Tabs items={[
|
|
{ label: "Account" }, { label: "Members", count: 8 },
|
|
{ label: "Billing" }, { label: "API" },
|
|
]} active={tab} onChange={setTab}/>
|
|
<div style={{ marginTop: 14 }}>
|
|
<SubHeading>Pill</SubHeading>
|
|
<Tabs variant="pill" items={[
|
|
{ label: "Day" }, { label: "Week" }, { label: "Month" }, { label: "Year" },
|
|
]} active="Week"/>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
// ─── 3 · Display atoms ──────────────────────────────────────
|
|
const DisplayAtoms = () => {
|
|
const [modalOpen, setModalOpen] = React.useState(false);
|
|
return (
|
|
<div style={{ padding: 32 }}>
|
|
<h1 style={{
|
|
margin: 0, fontFamily: "var(--font-display)", fontSize: "var(--text-2xl)",
|
|
fontWeight: 500, letterSpacing: "-0.02em",
|
|
}}>Display & feedback</h1>
|
|
|
|
<div style={{ marginTop: 24, display: "grid", gridTemplateColumns: "1fr 1fr", gap: 20 }}>
|
|
<Card>
|
|
<CardHeader title="Badges & avatars"/>
|
|
<SubHeading>Tones</SubHeading>
|
|
<div style={{ display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 16 }}>
|
|
<Badge>Neutral</Badge>
|
|
<Badge tone="accent" dot>Accent</Badge>
|
|
<Badge tone="success" dot>Active</Badge>
|
|
<Badge tone="warn" dot>Invited</Badge>
|
|
<Badge tone="danger" dot>Suspended</Badge>
|
|
<Badge tone="info">v4.2.1</Badge>
|
|
</div>
|
|
<SubHeading>Avatars</SubHeading>
|
|
<div style={{ display: "flex", gap: 14, alignItems: "center" }}>
|
|
<Avatar name="Mira Reyes" size={24}/>
|
|
<Avatar name="Theo Roux" size={32}/>
|
|
<Avatar name="Devi Patel" size={40} status="online"/>
|
|
<Avatar name="Sun Kim" size={48} status="busy"/>
|
|
<AvatarStack items={[
|
|
{name:"Mira Reyes"},{name:"Theo Roux"},{name:"Devi Patel"},
|
|
{name:"Sun Kim"},{name:"Ade Nwosu"},{name:"Linnea Berg"},
|
|
{name:"Jamal Frost"}
|
|
]}/>
|
|
</div>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader title="Banners"/>
|
|
<Banner title="Workspace upgrade pending" tone="warn"
|
|
action={<Button size="sm" variant="secondary">Review</Button>}>
|
|
1 invitation hasn't been accepted yet — sent 3 days ago.
|
|
</Banner>
|
|
<div style={{ height: 10 }}/>
|
|
<Banner tone="success" title="Saved">Your changes were saved.</Banner>
|
|
<div style={{ height: 10 }}/>
|
|
<Banner tone="danger" title="Couldn't connect">Please check your network and try again.</Banner>
|
|
</Card>
|
|
|
|
<Card style={{ gridColumn: "span 2" }}>
|
|
<CardHeader title="Table" subtitle="Members of the workspace"
|
|
action={<Button size="sm" leadingIcon={<Icon name="plus" size={12}/>}>Invite</Button>}/>
|
|
<Table
|
|
columns={[
|
|
{ key: "name", label: "Name", render: r => (
|
|
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
|
<Avatar name={r.name} size={26}/>
|
|
<div>
|
|
<div style={{ fontWeight: 500 }}>{r.name}</div>
|
|
<div style={{ fontSize: 11, color: "var(--text-3)" }}>{r.email}</div>
|
|
</div>
|
|
</div>
|
|
)},
|
|
{ key: "role", label: "Role", render: r => <Badge tone="accent">{r.role}</Badge> },
|
|
{ key: "status", label: "Status", render: r =>
|
|
<Badge dot tone={r.status === "Active" ? "success" :
|
|
r.status === "Invited" ? "warn" : "danger"}>{r.status}</Badge> },
|
|
{ key: "last", label: "Last active" },
|
|
{ key: "act", label: "", align: "right", width: 32,
|
|
render: () => <IconButton name="more" size="sm" label="More"/> },
|
|
]}
|
|
rows={[
|
|
{ id: 1, name: "Mira Reyes", email: "mira@vibn.co", role: "Owner", status: "Active", last: "now" },
|
|
{ id: 2, name: "Theo Roux", email: "theo@vibn.co", role: "Admin", status: "Active", last: "12 min" },
|
|
{ id: 3, name: "Devi Patel", email: "devi@vibn.co", role: "Admin", status: "Active", last: "1 hour" },
|
|
{ id: 4, name: "Linnea Berg", email: "linnea@vibn.co", role: "Member", status: "Invited", last: "—" },
|
|
{ id: 5, name: "Elin Roos", email: "elin@vibn.co", role: "Member", status: "Suspended", last: "14 days" },
|
|
]}
|
|
selectable
|
|
selected={[1, 2]}
|
|
/>
|
|
</Card>
|
|
|
|
<Card style={{ gridColumn: "span 2" }}>
|
|
<CardHeader title="Modal"/>
|
|
<div style={{ display: "flex", gap: 10, alignItems: "center" }}>
|
|
<Button onClick={() => setModalOpen(true)}>Open modal</Button>
|
|
<span style={{ fontSize: 12, color: "var(--text-3)" }}>
|
|
Press <KBD>⌘ + Enter</KBD> to confirm
|
|
</span>
|
|
</div>
|
|
<Modal
|
|
open={modalOpen} onClose={() => setModalOpen(false)}
|
|
title="Delete workspace?"
|
|
description="This will permanently remove all data in Lattice Studio. This action cannot be undone."
|
|
footer={<>
|
|
<Button variant="secondary" onClick={() => setModalOpen(false)}>Cancel</Button>
|
|
<Button variant="destructive">Yes, delete it</Button>
|
|
</>}
|
|
>
|
|
<Field label="Type the workspace name to confirm">
|
|
<Input placeholder="lattice-studio"/>
|
|
</Field>
|
|
</Modal>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
// ─── 4 · In-product shells ──────────────────────────────────
|
|
const SidebarDemo = () => (
|
|
<SidebarShell
|
|
brand={{ name: "Lattice Studio" }}
|
|
sections={[
|
|
{ items: [
|
|
{ id: "home", label: "Home", icon: "home" },
|
|
{ id: "inbox", label: "Inbox", icon: "inbox", count: 12 },
|
|
{ id: "tasks", label: "Tasks", icon: "check", count: 3 },
|
|
]},
|
|
{ title: "Views", items: [
|
|
{ id: "co", label: "Companies", icon: "building", active: true },
|
|
{ id: "people", label: "People", icon: "people" },
|
|
{ id: "deals", label: "Opportunities", icon: "target" },
|
|
]},
|
|
{ title: "Tools", items: [
|
|
{ id: "i", label: "Insights", icon: "bar" },
|
|
{ id: "f", label: "Automations", icon: "workflow"},
|
|
{ id: "d", label: "Docs", icon: "doc" },
|
|
]},
|
|
{ title: "Admin", items: [
|
|
{ id: "s", label: "Settings", icon: "settings" },
|
|
]},
|
|
]}
|
|
user={{ name: "Mira Reyes", email: "mira@vibn.co" }}
|
|
>
|
|
<div style={{ padding: 28 }}>
|
|
<h1 style={{ margin: 0, fontSize: 26, fontWeight: 600 }}>Companies</h1>
|
|
<p style={{ color: "var(--text-2)", fontSize: 13, marginTop: 6 }}>
|
|
248 records · last sync 4 minutes ago
|
|
</p>
|
|
<div style={{ marginTop: 18 }}>
|
|
<Banner tone="info" title="Vibn 4.0 is live">
|
|
Workspace-wide rollout begins next Monday. Read the changelog →
|
|
</Banner>
|
|
</div>
|
|
</div>
|
|
</SidebarShell>
|
|
);
|
|
|
|
const TopbarDemo = () => {
|
|
const [tab, setTab] = React.useState("Activity");
|
|
return (
|
|
<TopbarShell
|
|
brand={{ name: "Lattice" }}
|
|
breadcrumb={[
|
|
{ avatar: "Mira Reyes", label: "mira-reyes" },
|
|
{ label: "northstar-logistics", badge: "Pro" },
|
|
]}
|
|
tabs={[
|
|
{ label: "Overview" }, { label: "Activity", count: 18 },
|
|
{ label: "People" }, { label: "Notes" }, { label: "Files" },
|
|
]}
|
|
activeTab={tab}
|
|
onTabChange={setTab}
|
|
user={{ name: "Mira Reyes" }}
|
|
>
|
|
<div style={{ padding: 32 }}>
|
|
<h1 style={{ margin: 0, fontSize: 28, fontWeight: 600, letterSpacing: "-0.02em" }}>Northstar Logistics</h1>
|
|
<div style={{ display: "flex", gap: 8, marginTop: 8 }}>
|
|
<Badge tone="success" dot>Customer</Badge>
|
|
<Badge tone="accent">Tier 1</Badge>
|
|
<Badge>EMEA</Badge>
|
|
</div>
|
|
<div style={{ marginTop: 24, display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 16 }}>
|
|
{[
|
|
{ l: "Pipeline", v: "€146k", s: "+€12k 30d"},
|
|
{ l: "Closed-won", v: "€220k", s: "lifetime"},
|
|
{ l: "Health", v: "82", s: "stable"},
|
|
].map(k => (
|
|
<Card key={k.l} padding={18}>
|
|
<div style={{ fontSize: 11, color: "var(--text-3)", textTransform: "uppercase", letterSpacing: "0.06em" }}>{k.l}</div>
|
|
<div style={{ fontSize: 24, fontWeight: 600, marginTop: 6 }}>{k.v}</div>
|
|
<div style={{ fontSize: 11, color: "var(--text-2)", marginTop: 2 }}>{k.s}</div>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</TopbarShell>
|
|
);
|
|
};
|
|
|
|
const RailDemo = () => (
|
|
<RailShell
|
|
brand={{ name: "Vibn" }}
|
|
items={[
|
|
{ id: "home", icon: "home" },
|
|
{ id: "inbox", icon: "inbox", badge: 9 },
|
|
{ id: "co", icon: "building" },
|
|
{ id: "ppl", icon: "people" },
|
|
{ id: "deals", icon: "target", badge: 2 },
|
|
]}
|
|
activeRail="co"
|
|
secondaryTitle="Companies"
|
|
secondary={
|
|
<div>
|
|
{["Northstar Logistics", "Halcyon", "Kestrel", "Mossbank", "Verra", "Brooke Foods"].map((n, i) => (
|
|
<div key={n} style={{
|
|
padding: "8px 10px", borderRadius: "var(--radius-sm)", fontSize: 13,
|
|
background: i === 0 ? "var(--surface-alt)" : "transparent",
|
|
color: i === 0 ? "var(--text)" : "var(--text-2)",
|
|
display: "flex", alignItems: "center", gap: 10, cursor: "pointer",
|
|
}}>
|
|
<Avatar name={n} size={22}/>
|
|
<span style={{ flex: 1 }}>{n}</span>
|
|
{i === 0 && <Badge tone="success" dot>active</Badge>}
|
|
</div>
|
|
))}
|
|
</div>
|
|
}
|
|
user={{ name: "Mira Reyes" }}
|
|
>
|
|
<div style={{ padding: 32 }}>
|
|
<h1 style={{ margin: 0, fontSize: 24, fontWeight: 600 }}>Northstar Logistics</h1>
|
|
<p style={{ color: "var(--text-2)", fontSize: 13, marginTop: 6 }}>
|
|
Customer since Aug 2024 · 6 people · €146k pipeline
|
|
</p>
|
|
</div>
|
|
</RailShell>
|
|
);
|
|
|
|
// ─── 5 · Auth shells ────────────────────────────────────────
|
|
const SocialRow = () => (
|
|
<div style={{ display: "flex", gap: 8 }}>
|
|
<Button variant="secondary" full>Google</Button>
|
|
<Button variant="secondary" full>Microsoft</Button>
|
|
<Button variant="secondary" full>SSO</Button>
|
|
</div>
|
|
);
|
|
|
|
const AuthCenteredDemo = () => (
|
|
<AuthCenteredShell brand={{ name: "Lattice" }}>
|
|
<h1 style={{
|
|
margin: 0, fontFamily: "var(--font-display)",
|
|
fontSize: "var(--text-xl)", fontWeight: 600, letterSpacing: "-0.01em",
|
|
}}>Welcome back</h1>
|
|
<p style={{ fontSize: 13, color: "var(--text-2)", margin: "6px 0 22px" }}>
|
|
Sign in to your Lattice workspace.
|
|
</p>
|
|
<SocialRow/>
|
|
<Divider label="or with email"/>
|
|
<Field label="Email"><Input value="mira@lattice.co" autofocus/></Field>
|
|
<Field label="Password"><Input type="password" value="••••••••••"
|
|
trailingIcon={<Icon name="eye" size={14}/>}/></Field>
|
|
<Button full>Sign in <Icon name="arrow" size={13}/></Button>
|
|
<div style={{ fontSize: 12, textAlign: "center", marginTop: 18, color: "var(--text-2)" }}>
|
|
New here? <span style={{ color: "var(--text)", fontWeight: 500 }}>Create an account</span>
|
|
</div>
|
|
</AuthCenteredShell>
|
|
);
|
|
|
|
const AuthSplitDemo = () => (
|
|
<AuthSplitShell
|
|
brand={{ name: "Lattice" }}
|
|
hero={{
|
|
badge: "Lattice 4.0 · agents that draft for you",
|
|
headline: "The workspace where good ideas compound.",
|
|
sub: "One luminous surface for docs, canvases, contacts and pipelines.",
|
|
quote: {
|
|
body: "Replaced three tools in our first week. Lattice is what every CRM should have been.",
|
|
author: "Devi Patel", role: "Head of Sales, Halcyon",
|
|
},
|
|
}}
|
|
>
|
|
<h1 style={{
|
|
margin: 0, fontFamily: "var(--font-display)", fontSize: 26,
|
|
fontWeight: 600, letterSpacing: "-0.02em",
|
|
}}>Sign in to Lattice</h1>
|
|
<p style={{ fontSize: 13, color: "var(--text-2)", margin: "6px 0 22px" }}>
|
|
Welcome back. Pick how you'd like to continue.
|
|
</p>
|
|
<SocialRow/>
|
|
<Divider label="or with email"/>
|
|
<Field label="Email"><Input value="mira@lattice.co" autofocus/></Field>
|
|
<Field label="Password"><Input type="password" value="••••••••••"
|
|
trailingIcon={<Icon name="eye" size={14}/>}/></Field>
|
|
<Button full>Sign in <Icon name="arrow" size={13}/></Button>
|
|
<div style={{ marginTop: 18 }}>
|
|
<Banner tone="info" title="SAML / SSO for your company?"
|
|
action={<Button size="sm" variant="ghost">Use SSO →</Button>}>
|
|
Single sign-on is available on the Pro plan.
|
|
</Banner>
|
|
</div>
|
|
</AuthSplitShell>
|
|
);
|
|
|
|
const AuthGlassDemo = () => (
|
|
<AuthGlassShell brand={{ name: "Lattice" }} eyebrow="BETA · early access">
|
|
<h1 style={{
|
|
margin: 0, fontFamily: "var(--font-display)", fontSize: 32,
|
|
fontWeight: 500, letterSpacing: "-0.03em",
|
|
}}>Start your workspace.</h1>
|
|
<p style={{ fontSize: 14, color: "var(--text-2)", margin: "10px 0 22px" }}>
|
|
Free for 10 people. No card. 60 seconds to set up.
|
|
</p>
|
|
<SocialRow/>
|
|
<Divider label="or with email"/>
|
|
<Field label="Work email"><Input value="mira@lattice.co" autofocus/></Field>
|
|
<Field label="Password" hint="10+ chars · 1 number · 1 symbol">
|
|
<Input type="password" value="••••••••••" trailingIcon={<Icon name="eye" size={14}/>}/>
|
|
</Field>
|
|
<Checkbox checked label="I agree to Vibn's Terms and Privacy Policy."
|
|
style={{ margin: "4px 0 16px" }}/>
|
|
<Button full>Create my workspace <Icon name="arrow" size={13}/></Button>
|
|
</AuthGlassShell>
|
|
);
|
|
|
|
// ─── App: design canvas with all 4 themes side-by-side ──────
|
|
const W = 1300, H = 800;
|
|
const themes = ["minimal", "dark", "glass", "editorial"];
|
|
|
|
function App() {
|
|
return (
|
|
<DesignCanvas>
|
|
<DCSection
|
|
id="foundations"
|
|
title="Foundations"
|
|
subtitle="The same token surfaces, in four themes. tokens.css is the source of truth."
|
|
>
|
|
{themes.map(t => (
|
|
<DCArtboard key={t} id={`f-${t}`} label={`${t}`} width={W} height={H}>
|
|
<ThemeFrame theme={t}><Foundations theme={t}/></ThemeFrame>
|
|
</DCArtboard>
|
|
))}
|
|
</DCSection>
|
|
|
|
<DCSection
|
|
id="forms"
|
|
title="Forms & buttons"
|
|
subtitle="Button variants, every field type, controls, tabs."
|
|
>
|
|
{themes.map(t => (
|
|
<DCArtboard key={t} id={`forms-${t}`} label={`${t}`} width={W} height={H}>
|
|
<ThemeFrame theme={t}><FormAtoms/></ThemeFrame>
|
|
</DCArtboard>
|
|
))}
|
|
</DCSection>
|
|
|
|
<DCSection
|
|
id="display"
|
|
title="Display & feedback"
|
|
subtitle="Badges, avatars, banners, table, modal."
|
|
>
|
|
{themes.map(t => (
|
|
<DCArtboard key={t} id={`display-${t}`} label={`${t}`} width={W} height={H + 100}>
|
|
<ThemeFrame theme={t}><DisplayAtoms/></ThemeFrame>
|
|
</DCArtboard>
|
|
))}
|
|
</DCSection>
|
|
|
|
<DCSection
|
|
id="shells-app"
|
|
title="In-product shells · Sidebar"
|
|
subtitle="One shell, four themes."
|
|
>
|
|
{themes.map(t => (
|
|
<DCArtboard key={t} id={`side-${t}`} label={`Sidebar · ${t}`} width={W} height={H}>
|
|
<ThemeFrame theme={t}><SidebarDemo/></ThemeFrame>
|
|
</DCArtboard>
|
|
))}
|
|
</DCSection>
|
|
|
|
<DCSection
|
|
id="shells-topbar"
|
|
title="In-product shells · Topbar"
|
|
subtitle="Breadcrumb + ⌘K + tabs."
|
|
>
|
|
{themes.map(t => (
|
|
<DCArtboard key={t} id={`top-${t}`} label={`Topbar · ${t}`} width={W} height={H}>
|
|
<ThemeFrame theme={t}><TopbarDemo/></ThemeFrame>
|
|
</DCArtboard>
|
|
))}
|
|
</DCSection>
|
|
|
|
<DCSection
|
|
id="shells-rail"
|
|
title="In-product shells · Rail"
|
|
subtitle="Icon rail + secondary panel."
|
|
>
|
|
{themes.map(t => (
|
|
<DCArtboard key={t} id={`rail-${t}`} label={`Rail · ${t}`} width={W} height={H}>
|
|
<ThemeFrame theme={t}><RailDemo/></ThemeFrame>
|
|
</DCArtboard>
|
|
))}
|
|
</DCSection>
|
|
|
|
<DCSection
|
|
id="auth-centered"
|
|
title="Auth shells · Centered card"
|
|
subtitle="Sign-in card on a soft background."
|
|
>
|
|
{themes.map(t => (
|
|
<DCArtboard key={t} id={`auth-c-${t}`} label={`Centered · ${t}`} width={W} height={H}>
|
|
<ThemeFrame theme={t}><AuthCenteredDemo/></ThemeFrame>
|
|
</DCArtboard>
|
|
))}
|
|
</DCSection>
|
|
|
|
<DCSection
|
|
id="auth-split"
|
|
title="Auth shells · Split hero"
|
|
subtitle="Storytelling panel on the left, form on the right."
|
|
>
|
|
{themes.map(t => (
|
|
<DCArtboard key={t} id={`auth-s-${t}`} label={`Split · ${t}`} width={W} height={H}>
|
|
<ThemeFrame theme={t}><AuthSplitDemo/></ThemeFrame>
|
|
</DCArtboard>
|
|
))}
|
|
</DCSection>
|
|
|
|
<DCSection
|
|
id="auth-glass"
|
|
title="Auth shells · Glass card"
|
|
subtitle="Frosted card on a vibrant backdrop."
|
|
>
|
|
{themes.map(t => (
|
|
<DCArtboard key={t} id={`auth-g-${t}`} label={`Glass · ${t}`} width={W} height={H}>
|
|
<ThemeFrame theme={t}><AuthGlassDemo/></ThemeFrame>
|
|
</DCArtboard>
|
|
))}
|
|
</DCSection>
|
|
</DesignCanvas>
|
|
);
|
|
}
|
|
|
|
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
|
|
</script>
|
|
</body>
|
|
</html>
|