This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import * as React from "react";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const appCardVariants = cva(
|
||||
"rounded-surface border transition-all duration-200",
|
||||
{
|
||||
variants: {
|
||||
level: {
|
||||
1: "border-border bg-surface-1 text-foreground shadow-card",
|
||||
2: "border-border bg-surface-2 text-foreground shadow-sm",
|
||||
3: "border-border/90 bg-surface-3 text-foreground shadow-panel",
|
||||
inverse: "border-transparent bg-surface-inverse text-surface-inverse-foreground shadow-lg",
|
||||
},
|
||||
padding: {
|
||||
none: "",
|
||||
sm: "p-4",
|
||||
md: "p-6",
|
||||
lg: "p-8",
|
||||
},
|
||||
interactive: {
|
||||
true: "hover:-translate-y-0.5 hover:border-border-strong hover:shadow-md",
|
||||
false: "",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
level: 1,
|
||||
padding: "none",
|
||||
interactive: false,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export interface AppCardProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
VariantProps<typeof appCardVariants> {}
|
||||
|
||||
const AppCard = React.forwardRef<HTMLDivElement, AppCardProps>(
|
||||
({ className, level, padding, interactive, ...props }, ref) => (
|
||||
<Card
|
||||
ref={ref}
|
||||
className={cn(
|
||||
appCardVariants({
|
||||
level,
|
||||
padding,
|
||||
interactive,
|
||||
}),
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
);
|
||||
|
||||
AppCard.displayName = "AppCard";
|
||||
|
||||
export { AppCard, appCardVariants };
|
||||
@@ -0,0 +1,32 @@
|
||||
import * as React from "react";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center rounded-pill border px-2.5 py-1 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "border-transparent bg-primary text-primary-foreground",
|
||||
secondary: "border-transparent bg-secondary text-secondary-foreground",
|
||||
outline: "border-border bg-surface-1 text-foreground",
|
||||
success: "border-transparent bg-status-success-soft text-status-success",
|
||||
warning: "border-transparent bg-status-warning-soft text-status-warning",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export interface BadgeProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
VariantProps<typeof badgeVariants> {}
|
||||
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants };
|
||||
@@ -0,0 +1,55 @@
|
||||
import * as React from "react";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-nested text-sm font-medium ring-offset-background transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground shadow-xs hover:brightness-95",
|
||||
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
||||
outline: "border border-border bg-surface-1 text-foreground shadow-xs hover:border-border-strong hover:bg-surface-2",
|
||||
ghost: "text-muted-foreground hover:bg-muted hover:text-foreground",
|
||||
link: "rounded-none text-primary underline-offset-4 hover:underline",
|
||||
destructive: "bg-destructive text-destructive-foreground shadow-xs hover:brightness-95",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2",
|
||||
sm: "h-9 px-3",
|
||||
lg: "h-11 px-6",
|
||||
icon: "h-10 w-10",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export interface ButtonProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {
|
||||
asChild?: boolean;
|
||||
}
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "button";
|
||||
|
||||
return (
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Button.displayName = "Button";
|
||||
|
||||
export { Button, buttonVariants };
|
||||
@@ -0,0 +1,65 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"rounded-surface border border-border bg-surface-1 text-card-foreground shadow-card",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
);
|
||||
|
||||
Card.displayName = "Card";
|
||||
|
||||
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("flex flex-col gap-1.5 p-6", className)} {...props} />
|
||||
),
|
||||
);
|
||||
|
||||
CardHeader.displayName = "CardHeader";
|
||||
|
||||
const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<h3
|
||||
ref={ref}
|
||||
className={cn("text-2xl font-semibold leading-none tracking-tight", className)}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
);
|
||||
|
||||
CardTitle.displayName = "CardTitle";
|
||||
|
||||
const CardDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<p ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
|
||||
));
|
||||
|
||||
CardDescription.displayName = "CardDescription";
|
||||
|
||||
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
||||
),
|
||||
);
|
||||
|
||||
CardContent.displayName = "CardContent";
|
||||
|
||||
const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
|
||||
),
|
||||
);
|
||||
|
||||
CardFooter.displayName = "CardFooter";
|
||||
|
||||
export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle };
|
||||
@@ -0,0 +1,21 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
||||
({ className, type, ...props }, ref) => (
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
"flex h-10 w-full rounded-nested border border-input bg-surface-1 px-3 py-2 text-sm text-foreground ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
);
|
||||
|
||||
Input.displayName = "Input";
|
||||
|
||||
export { Input };
|
||||
@@ -0,0 +1,17 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
function Label({
|
||||
className,
|
||||
...props
|
||||
}: React.LabelHTMLAttributes<HTMLLabelElement>) {
|
||||
return (
|
||||
<label
|
||||
className={cn("text-sm font-medium text-foreground/90", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Label };
|
||||
@@ -0,0 +1,26 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type SeparatorProps = React.HTMLAttributes<HTMLDivElement> & {
|
||||
orientation?: "horizontal" | "vertical";
|
||||
};
|
||||
|
||||
function Separator({
|
||||
className,
|
||||
orientation = "horizontal",
|
||||
...props
|
||||
}: SeparatorProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"shrink-0 bg-border",
|
||||
orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Separator };
|
||||
@@ -0,0 +1,114 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type TabsContextValue = {
|
||||
value: string;
|
||||
setValue: (value: string) => void;
|
||||
};
|
||||
|
||||
const TabsContext = React.createContext<TabsContextValue | null>(null);
|
||||
|
||||
function useTabsContext() {
|
||||
const context = React.useContext(TabsContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error("Tabs components must be used within Tabs.");
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
type TabsProps = {
|
||||
defaultValue: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function Tabs({ defaultValue, children, className }: TabsProps) {
|
||||
const [value, setValue] = React.useState(defaultValue);
|
||||
|
||||
return (
|
||||
<TabsContext.Provider value={{ value, setValue }}>
|
||||
<div className={cn("w-full", className)}>{children}</div>
|
||||
</TabsContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
function TabsList({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"inline-flex h-auto flex-wrap items-center gap-2 rounded-surface border border-border bg-surface-2 p-1.5",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
type TabsTriggerProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||
value: string;
|
||||
};
|
||||
|
||||
function TabsTrigger({
|
||||
className,
|
||||
value,
|
||||
onClick,
|
||||
...props
|
||||
}: TabsTriggerProps) {
|
||||
const { value: activeValue, setValue } = useTabsContext();
|
||||
const isActive = activeValue === value;
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center rounded-nested px-3 py-2 text-sm font-medium text-muted-foreground transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||
isActive && "bg-surface-1 text-foreground shadow-xs",
|
||||
className,
|
||||
)}
|
||||
onClick={(event) => {
|
||||
setValue(value);
|
||||
onClick?.(event);
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
type TabsContentProps = React.HTMLAttributes<HTMLDivElement> & {
|
||||
value: string;
|
||||
};
|
||||
|
||||
function TabsContent({
|
||||
className,
|
||||
value,
|
||||
children,
|
||||
...props
|
||||
}: TabsContentProps) {
|
||||
const { value: activeValue } = useTabsContext();
|
||||
|
||||
if (activeValue !== value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"mt-6 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { Tabs, TabsContent, TabsList, TabsTrigger };
|
||||
@@ -0,0 +1,20 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Textarea = React.forwardRef<HTMLTextAreaElement, React.ComponentProps<"textarea">>(
|
||||
({ className, ...props }, ref) => (
|
||||
<textarea
|
||||
className={cn(
|
||||
"flex min-h-[112px] w-full rounded-nested border border-input bg-surface-1 px-3 py-2 text-sm text-foreground ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
);
|
||||
|
||||
Textarea.displayName = "Textarea";
|
||||
|
||||
export { Textarea };
|
||||
@@ -0,0 +1,522 @@
|
||||
import { Layers3, Type } from "lucide-react";
|
||||
|
||||
import { AppCard } from "@/components/ui/app-card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
|
||||
type UiKitShowcaseProps = {
|
||||
localeKey: "de" | "en";
|
||||
};
|
||||
|
||||
function UiKitSection({
|
||||
title,
|
||||
description,
|
||||
children,
|
||||
}: {
|
||||
title: string;
|
||||
description: string;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<AppCard>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-xl">{title}</CardTitle>
|
||||
<CardDescription>{description}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>{children}</CardContent>
|
||||
</AppCard>
|
||||
);
|
||||
}
|
||||
|
||||
export function UiKitShowcase({ localeKey }: UiKitShowcaseProps) {
|
||||
const copy =
|
||||
localeKey === "de"
|
||||
? {
|
||||
title: "UI Kit",
|
||||
intro:
|
||||
"Zentrale Referenz fuer globale Oberflaechen, Steuerelemente und verschachtelte Ebenen.",
|
||||
cards: "Cards",
|
||||
buttons: "Buttons",
|
||||
inputs: "Inputs",
|
||||
badges: "Badges",
|
||||
tabs: "Tabs",
|
||||
surfaces: "Surfaces",
|
||||
typography: "Typography",
|
||||
headers: "Headers",
|
||||
cardsDesc:
|
||||
"Aussenflaechen, innere Karten und verschachtelte Ebenen mit einheitlicher Hierarchie.",
|
||||
buttonsDesc:
|
||||
"Globale Button Varianten und Groessen aus einer zentralen Quelle.",
|
||||
inputsDesc:
|
||||
"Gemeinsame Feldzustande mit derselben inneren Radius- und Oberflaechenlogik.",
|
||||
badgesDesc:
|
||||
"Kleine Statusmarken mit minimaler, konsistenter Formensprache.",
|
||||
surfacesDesc:
|
||||
"Die gesamte Oberflaechenhierarchie basiert auf Level 1 bis 3 und einer inversen Ebene.",
|
||||
typographyDesc:
|
||||
"Typografische Grundstufen fuer Seiten, Sektionen und Metainformationen.",
|
||||
headersDesc:
|
||||
"Header Muster fuer Seiten und Sektionen innerhalb des Systems.",
|
||||
tabsDesc:
|
||||
"Tabs bleiben in einer Route und verwenden dieselbe verschachtelte Steuerlogik.",
|
||||
outerCard: "Aeussere Card",
|
||||
surfaceLevel: "Surface Level 1",
|
||||
innerCard: "Innere Card",
|
||||
nestedContent: "Verschachtelte Inhalte nutzen den zweiten Radius und eine weichere Oberflaeche.",
|
||||
nestedSurface: "Verschachtelte Ebene",
|
||||
nestedSurfaceText: "Mehr Tiefe ohne einen neuen Component Stil zu erfinden.",
|
||||
interactiveOuterCard: "Interaktive aeussere Card",
|
||||
hoverText: "Das Hover Verhalten bleibt an dieselbe Oberflaechenebene gebunden.",
|
||||
inverseOuterCard: "Inverse aeussere Card",
|
||||
inverseOuterCardText: "Nur verwenden, wenn ein starker Kontrastblock wirklich noetig ist.",
|
||||
default: "Standard",
|
||||
secondary: "Sekundaer",
|
||||
outline: "Outline",
|
||||
ghost: "Ghost",
|
||||
destructive: "Destruktiv",
|
||||
linkButton: "Link Button",
|
||||
small: "Klein",
|
||||
large: "Gross",
|
||||
iconButton: "Icon Button",
|
||||
disabled: "Deaktiviert",
|
||||
defaultInput: "Standard Input",
|
||||
placeholderText: "Platzhaltertext",
|
||||
filledInput: "Gefuellter Input",
|
||||
filledValue: "Gefuellter Wert",
|
||||
disabledInput: "Deaktivierter Input",
|
||||
disabledState: "Deaktivierter Zustand",
|
||||
focusPreview: "Focus Vorschau",
|
||||
focusedLook: "Fokussierte Ansicht",
|
||||
errorPreview: "Fehler Vorschau",
|
||||
invalidValue: "Ungueltiger Wert",
|
||||
textarea: "Textarea",
|
||||
textareaValue: "Laengerer Feldzustand fuer mehrzeilige Inhalte.",
|
||||
success: "Erfolg",
|
||||
warning: "Warnung",
|
||||
first: "Erste",
|
||||
second: "Zweite",
|
||||
third: "Dritte",
|
||||
firstTabText: "Inhalt des ersten Tabs innerhalb einer Level 1 Oberflaeche.",
|
||||
secondTabText: "Inhalt des zweiten Tabs mit derselben globalen Hierarchie.",
|
||||
thirdTabText: "Inhalt des dritten Tabs ohne lokale Sonderstile.",
|
||||
surfaceOne: "Surface 1",
|
||||
surfaceOneText: "Aeussere und primaere Container.",
|
||||
surfaceTwo: "Surface 2",
|
||||
surfaceTwoText: "Innere und unterstuetzende Panels.",
|
||||
surfaceThree: "Surface 3",
|
||||
surfaceThreeText: "Mehr Betonung ohne neue Styling-Zweige.",
|
||||
inverse: "Inverse",
|
||||
inverseText: "Reserviert fuer bewusst eingesetzte Kontrastbloecke.",
|
||||
eyebrow: "Eyebrow",
|
||||
pageHeading: "Seitenueberschrift",
|
||||
pageHeadingText: "Begleitender Absatz fuer primaere Seiteneinfuehrungen und Zusammenfassungen.",
|
||||
sectionHeading: "Abschnittsueberschrift",
|
||||
sectionHeadingText: "Fuer gruppierte Bereiche innerhalb einer aeusseren Seitenoberflaeche.",
|
||||
strongSupportingText: "Betonter Begleittext",
|
||||
pageHeader: "Seitenheader",
|
||||
outerSurfaceHeader: "Header der aeusseren Oberflaeche",
|
||||
outerSurfaceHeaderText: "Dieses Muster wird fuer Seitensektionen mit staerkerer Hierarchie genutzt.",
|
||||
sectionHeader: "Abschnittsheader",
|
||||
nestedBlockHeading: "Ueberschrift des verschachtelten Blocks",
|
||||
nestedBlockHeadingText: "Innerhalb einer aeusseren Card verwenden, wenn Inhalte bereits gruppiert sind."
|
||||
}
|
||||
: {
|
||||
title: "UI Kit",
|
||||
intro:
|
||||
"Central reference for global surfaces, controls, and nested interface levels.",
|
||||
cards: "Cards",
|
||||
buttons: "Buttons",
|
||||
inputs: "Inputs",
|
||||
badges: "Badges",
|
||||
tabs: "Tabs",
|
||||
surfaces: "Surfaces",
|
||||
typography: "Typography",
|
||||
headers: "Headers",
|
||||
cardsDesc:
|
||||
"Outer surfaces, inner cards, and nested levels with one consistent hierarchy.",
|
||||
buttonsDesc:
|
||||
"Global button variants and sizes from one shared source of truth.",
|
||||
inputsDesc:
|
||||
"Shared field states using the same inner radius and surface logic.",
|
||||
badgesDesc:
|
||||
"Small status markers with a minimal and consistent shape language.",
|
||||
surfacesDesc:
|
||||
"The full surface hierarchy is built on level 1 through 3 and one inverse layer.",
|
||||
typographyDesc:
|
||||
"Typography foundations for pages, sections, and supporting copy.",
|
||||
headersDesc:
|
||||
"Header patterns for pages and sections inside the system.",
|
||||
tabsDesc:
|
||||
"Tabs stay within one route and use the same nested control logic.",
|
||||
outerCard: "Outer card",
|
||||
surfaceLevel: "Surface level 1",
|
||||
innerCard: "Inner card",
|
||||
nestedContent: "Nested content uses the secondary radius and a softer surface.",
|
||||
nestedSurface: "Nested surface",
|
||||
nestedSurfaceText: "Deeper emphasis without inventing a new component style.",
|
||||
interactiveOuterCard: "Interactive outer card",
|
||||
hoverText: "Hover behavior stays attached to the same surface level.",
|
||||
inverseOuterCard: "Inverse outer card",
|
||||
inverseOuterCardText: "Use only when a strong contrast block is truly needed.",
|
||||
default: "Default",
|
||||
secondary: "Secondary",
|
||||
outline: "Outline",
|
||||
ghost: "Ghost",
|
||||
destructive: "Destructive",
|
||||
linkButton: "Link button",
|
||||
small: "Small",
|
||||
large: "Large",
|
||||
iconButton: "Icon button",
|
||||
disabled: "Disabled",
|
||||
defaultInput: "Default input",
|
||||
placeholderText: "Placeholder text",
|
||||
filledInput: "Filled input",
|
||||
filledValue: "Filled value",
|
||||
disabledInput: "Disabled input",
|
||||
disabledState: "Disabled state",
|
||||
focusPreview: "Focus-style preview",
|
||||
focusedLook: "Focused look",
|
||||
errorPreview: "Error-style preview",
|
||||
invalidValue: "Invalid value",
|
||||
textarea: "Textarea",
|
||||
textareaValue: "Longer field state for multiline content.",
|
||||
success: "Success",
|
||||
warning: "Warning",
|
||||
first: "First",
|
||||
second: "Second",
|
||||
third: "Third",
|
||||
firstTabText: "First tab content inside a level 1 surface.",
|
||||
secondTabText: "Second tab content using the same global hierarchy.",
|
||||
thirdTabText: "Third tab content without local custom styling.",
|
||||
surfaceOne: "Surface 1",
|
||||
surfaceOneText: "Outer and primary containers.",
|
||||
surfaceTwo: "Surface 2",
|
||||
surfaceTwoText: "Inner and supporting panels.",
|
||||
surfaceThree: "Surface 3",
|
||||
surfaceThreeText: "Raised emphasis without new styling branches.",
|
||||
inverse: "Inverse",
|
||||
inverseText: "Reserved for deliberate high-contrast blocks.",
|
||||
eyebrow: "Eyebrow",
|
||||
pageHeading: "Page heading",
|
||||
pageHeadingText: "Supporting paragraph text for primary page introductions and summaries.",
|
||||
sectionHeading: "Section heading",
|
||||
sectionHeadingText: "Use this for grouped blocks inside an outer page surface.",
|
||||
strongSupportingText: "Strong supporting text",
|
||||
pageHeader: "Page header",
|
||||
outerSurfaceHeader: "Outer surface header",
|
||||
outerSurfaceHeaderText: "This pattern is used for page-level sections that need stronger hierarchy.",
|
||||
sectionHeader: "Section header",
|
||||
nestedBlockHeading: "Nested block heading",
|
||||
nestedBlockHeadingText: "Use inside an outer card when content is already grouped."
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-section">
|
||||
<AppCard level={3}>
|
||||
<CardContent className="flex flex-col gap-4 p-6 lg:p-10">
|
||||
<div className="inline-flex w-fit items-center gap-2 rounded-pill border border-border bg-surface-1 px-3 py-1 text-xs font-semibold uppercase tracking-[0.14em] text-foreground/80">
|
||||
<Layers3 className="h-3.5 w-3.5 text-brand-secondary" />
|
||||
{copy.title}
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<h1 className="text-3xl font-semibold tracking-tight text-foreground sm:text-5xl">
|
||||
{copy.title}
|
||||
</h1>
|
||||
<p className="max-w-3xl text-base text-muted-foreground sm:text-lg">
|
||||
{copy.intro}
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
|
||||
<Tabs defaultValue="cards">
|
||||
<TabsList>
|
||||
<TabsTrigger value="cards">{copy.cards}</TabsTrigger>
|
||||
<TabsTrigger value="buttons">{copy.buttons}</TabsTrigger>
|
||||
<TabsTrigger value="inputs">{copy.inputs}</TabsTrigger>
|
||||
<TabsTrigger value="badges">{copy.badges}</TabsTrigger>
|
||||
<TabsTrigger value="tabs">{copy.tabs}</TabsTrigger>
|
||||
<TabsTrigger value="surfaces">{copy.surfaces}</TabsTrigger>
|
||||
<TabsTrigger value="typography">{copy.typography}</TabsTrigger>
|
||||
<TabsTrigger value="headers">{copy.headers}</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="cards">
|
||||
<UiKitSection title={copy.cards} description={copy.cardsDesc}>
|
||||
<div className="grid gap-6 lg:grid-cols-2">
|
||||
<AppCard level={1}>
|
||||
<CardContent className="space-y-4 p-6">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-muted-foreground">{copy.outerCard}</p>
|
||||
<h3 className="mt-1 text-lg font-semibold text-foreground">{copy.surfaceLevel}</h3>
|
||||
</div>
|
||||
<AppCard level={2}>
|
||||
<CardContent className="space-y-3 p-5">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-muted-foreground">{copy.innerCard}</p>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
{copy.nestedContent}
|
||||
</p>
|
||||
</div>
|
||||
<AppCard level={3}>
|
||||
<CardContent className="space-y-2 p-4">
|
||||
<p className="text-sm font-medium text-foreground">{copy.nestedSurface}</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{copy.nestedSurfaceText}
|
||||
</p>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
|
||||
<div className="grid gap-4">
|
||||
<AppCard level={1} interactive>
|
||||
<CardContent className="p-5">
|
||||
<p className="text-sm font-medium text-muted-foreground">{copy.interactiveOuterCard}</p>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
{copy.hoverText}
|
||||
</p>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
<AppCard level="inverse">
|
||||
<CardContent className="p-5">
|
||||
<p className="text-sm font-medium text-surface-inverse-foreground/80">
|
||||
{copy.inverseOuterCard}
|
||||
</p>
|
||||
<p className="mt-2 text-sm text-surface-inverse-foreground/88">
|
||||
{copy.inverseOuterCardText}
|
||||
</p>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
</div>
|
||||
</div>
|
||||
</UiKitSection>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="buttons">
|
||||
<UiKitSection title={copy.buttons} description={copy.buttonsDesc}>
|
||||
<div className="grid gap-6 lg:grid-cols-2">
|
||||
<AppCard level={2}>
|
||||
<CardContent className="flex flex-wrap gap-3 p-5">
|
||||
<Button>{copy.default}</Button>
|
||||
<Button variant="secondary">{copy.secondary}</Button>
|
||||
<Button variant="outline">{copy.outline}</Button>
|
||||
<Button variant="ghost">{copy.ghost}</Button>
|
||||
<Button variant="destructive">{copy.destructive}</Button>
|
||||
<Button variant="link">{copy.linkButton}</Button>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
<AppCard level={2}>
|
||||
<CardContent className="flex flex-wrap items-center gap-3 p-5">
|
||||
<Button size="sm">{copy.small}</Button>
|
||||
<Button>{copy.default}</Button>
|
||||
<Button size="lg">{copy.large}</Button>
|
||||
<Button size="icon" aria-label={copy.iconButton}>
|
||||
<Layers3 className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button disabled>{copy.disabled}</Button>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
</div>
|
||||
</UiKitSection>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="inputs">
|
||||
<UiKitSection title={copy.inputs} description={copy.inputsDesc}>
|
||||
<div className="grid gap-6 lg:grid-cols-2">
|
||||
<AppCard level={2}>
|
||||
<CardContent className="grid gap-4 p-5">
|
||||
<Label className="grid gap-2">
|
||||
{copy.defaultInput}
|
||||
<Input placeholder={copy.placeholderText} />
|
||||
</Label>
|
||||
<Label className="grid gap-2">
|
||||
{copy.filledInput}
|
||||
<Input defaultValue={copy.filledValue} />
|
||||
</Label>
|
||||
<Label className="grid gap-2">
|
||||
{copy.disabledInput}
|
||||
<Input disabled defaultValue={copy.disabledState} />
|
||||
</Label>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
<AppCard level={2}>
|
||||
<CardContent className="grid gap-4 p-5">
|
||||
<Label className="grid gap-2">
|
||||
{copy.focusPreview}
|
||||
<Input
|
||||
defaultValue={copy.focusedLook}
|
||||
className="ring-2 ring-ring ring-offset-2"
|
||||
readOnly
|
||||
/>
|
||||
</Label>
|
||||
<Label className="grid gap-2">
|
||||
{copy.errorPreview}
|
||||
<Input
|
||||
defaultValue={copy.invalidValue}
|
||||
className="border-destructive text-destructive focus-visible:ring-destructive"
|
||||
readOnly
|
||||
/>
|
||||
</Label>
|
||||
<Label className="grid gap-2">
|
||||
{copy.textarea}
|
||||
<Textarea defaultValue={copy.textareaValue} />
|
||||
</Label>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
</div>
|
||||
</UiKitSection>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="badges">
|
||||
<UiKitSection title={copy.badges} description={copy.badgesDesc}>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<Badge>{copy.default}</Badge>
|
||||
<Badge variant="secondary">{copy.secondary}</Badge>
|
||||
<Badge variant="outline">{copy.outline}</Badge>
|
||||
<Badge variant="success">{copy.success}</Badge>
|
||||
<Badge variant="warning">{copy.warning}</Badge>
|
||||
</div>
|
||||
</UiKitSection>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="tabs">
|
||||
<UiKitSection title={copy.tabs} description={copy.tabsDesc}>
|
||||
<AppCard level={2}>
|
||||
<CardContent className="space-y-4 p-5">
|
||||
<Tabs defaultValue="first">
|
||||
<TabsList>
|
||||
<TabsTrigger value="first">{copy.first}</TabsTrigger>
|
||||
<TabsTrigger value="second">{copy.second}</TabsTrigger>
|
||||
<TabsTrigger value="third">{copy.third}</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="first">
|
||||
<AppCard level={1}>
|
||||
<CardContent className="p-4 text-sm text-muted-foreground">
|
||||
{copy.firstTabText}
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
</TabsContent>
|
||||
<TabsContent value="second">
|
||||
<AppCard level={1}>
|
||||
<CardContent className="p-4 text-sm text-muted-foreground">
|
||||
{copy.secondTabText}
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
</TabsContent>
|
||||
<TabsContent value="third">
|
||||
<AppCard level={1}>
|
||||
<CardContent className="p-4 text-sm text-muted-foreground">
|
||||
{copy.thirdTabText}
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
</UiKitSection>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="surfaces">
|
||||
<UiKitSection title={copy.surfaces} description={copy.surfacesDesc}>
|
||||
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
<AppCard level={1}>
|
||||
<CardContent className="p-5">
|
||||
<p className="font-medium">{copy.surfaceOne}</p>
|
||||
<p className="mt-2 text-sm text-muted-foreground">{copy.surfaceOneText}</p>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
<AppCard level={2}>
|
||||
<CardContent className="p-5">
|
||||
<p className="font-medium">{copy.surfaceTwo}</p>
|
||||
<p className="mt-2 text-sm text-muted-foreground">{copy.surfaceTwoText}</p>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
<AppCard level={3}>
|
||||
<CardContent className="p-5">
|
||||
<p className="font-medium">{copy.surfaceThree}</p>
|
||||
<p className="mt-2 text-sm text-muted-foreground">{copy.surfaceThreeText}</p>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
<AppCard level="inverse">
|
||||
<CardContent className="p-5">
|
||||
<p className="font-medium text-surface-inverse-foreground">{copy.inverse}</p>
|
||||
<p className="mt-2 text-sm text-surface-inverse-foreground/88">{copy.inverseText}</p>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
</div>
|
||||
</UiKitSection>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="typography">
|
||||
<UiKitSection title={copy.typography} description={copy.typographyDesc}>
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-medium uppercase tracking-[0.14em] text-muted-foreground">
|
||||
{copy.eyebrow}
|
||||
</p>
|
||||
<h1 className="text-4xl font-semibold tracking-tight text-foreground">
|
||||
{copy.pageHeading}
|
||||
</h1>
|
||||
<p className="max-w-2xl text-base text-muted-foreground">
|
||||
{copy.pageHeadingText}
|
||||
</p>
|
||||
</div>
|
||||
<AppCard level={2}>
|
||||
<CardContent className="space-y-3 p-5">
|
||||
<h2 className="text-2xl font-semibold text-foreground">{copy.sectionHeading}</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{copy.sectionHeadingText}
|
||||
</p>
|
||||
<p className="text-sm font-medium text-foreground/82">{copy.strongSupportingText}</p>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
</div>
|
||||
</UiKitSection>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="headers">
|
||||
<UiKitSection title={copy.headers} description={copy.headersDesc}>
|
||||
<div className="grid gap-6 lg:grid-cols-2">
|
||||
<AppCard level={3}>
|
||||
<CardContent className="space-y-3 p-6">
|
||||
<div className="inline-flex w-fit items-center gap-2 rounded-pill border border-border bg-surface-1 px-3 py-1 text-xs font-semibold uppercase tracking-[0.14em] text-foreground/80">
|
||||
<Type className="h-3.5 w-3.5 text-brand-primary" />
|
||||
{copy.pageHeader}
|
||||
</div>
|
||||
<h2 className="text-3xl font-semibold tracking-tight text-foreground">
|
||||
{copy.outerSurfaceHeader}
|
||||
</h2>
|
||||
<p className="text-base text-muted-foreground">
|
||||
{copy.outerSurfaceHeaderText}
|
||||
</p>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
<AppCard level={2}>
|
||||
<CardContent className="space-y-3 p-5">
|
||||
<p className="text-sm font-medium text-muted-foreground">{copy.sectionHeader}</p>
|
||||
<h3 className="text-xl font-semibold text-foreground">{copy.nestedBlockHeading}</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{copy.nestedBlockHeadingText}
|
||||
</p>
|
||||
</CardContent>
|
||||
</AppCard>
|
||||
</div>
|
||||
</UiKitSection>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user