-
Notifications
You must be signed in to change notification settings - Fork 1
UI Kit and Components
The UI Kit & Components system within the SecondLayer frontend (lexwebapp/) is a structured, reusable design system built on TailwindCSS with a variants-and-sizes pattern. It provides accessible, type-safe components that ensure visual consistency across the legal tech platform, supporting features such as legal document analysis, court case searching, and real-time AI chat interactions.
The architecture uses centralized design tokens defined in the Tailwind configuration, a component-level styles/types separation pattern, and Framer Motion for animations. All components are TypeScript-first with exported prop interfaces.
Design tokens are defined in lexwebapp/tailwind.config.js rather than a separate tokens file. The Tailwind config extends the default theme with project-specific values.
The project uses a custom claude namespace for its core color system, based on the Zinc scale:
| Token | Value | Usage |
|---|---|---|
claude.bg |
#FAFAFA |
Page background |
claude.sidebar |
#F4F4F5 |
Sidebar background (Zinc-100) |
claude.accent |
#18181B |
Primary CTA, focus rings (Zinc-900) |
claude.text |
#18181B |
Body text (Zinc-900) |
claude.subtext |
#71717A |
Secondary text (Zinc-500) |
claude.border |
#E4E4E7 |
Borders, dividers (Zinc-200) |
claude.user |
#F1F1F4 |
User message bubble background |
| Token | Value |
|---|---|
fontFamily.serif |
"Crimson Pro", serif |
fontFamily.sans |
Inter, sans-serif |
Prose (long-form text) styling uses the @tailwindcss/typography plugin with custom CSS variable overrides matching the Zinc color system.
| Token | Description |
|---|---|
elevation-0 |
None |
elevation-1 |
Subtle (cards, inputs) |
elevation-2 |
Medium (dropdowns, popovers) |
elevation-3 |
Prominent (modals, overlays) |
input-focus |
Input focus ring (rgba(24,24,27,0.08)) |
| Token | Effect |
|---|---|
fade-in |
0.3s ease-out translateY(10px) to 0 |
The UI library is located at lexwebapp/src/components/ui/. Each component follows a consistent file structure:
ComponentName/
index.ts # Re-exports
ComponentName.tsx # Implementation
ComponentName.types.ts # TypeScript interfaces
ComponentName.styles.ts # Style utility functions
All components are exported from lexwebapp/src/components/ui/index.ts organized by category:
graph TD
TW[Tailwind Config] --> UI[UI Component Library]
UI --> Basic[Basic Components]
UI --> Layout[Layout Components]
UI --> Form[Form Components]
UI --> Nav[Navigation Components]
UI --> Feed[Feedback Components]
Basic --> Button
Basic --> Input
Basic --> Card
Layout --> Modal
Layout --> Badge
Layout --> ConfirmModal
Form --> Checkbox
Form --> Switch
Nav --> Tabs
Feed --> Spinner
Supports variant-and-size composition with loading states and icon slots.
| Prop | Type | Default |
|---|---|---|
variant |
'primary' | 'secondary' | 'outline' | 'ghost' | 'danger' |
'primary' |
size |
'sm' | 'md' | 'lg' |
'md' |
isLoading |
boolean |
false |
fullWidth |
boolean |
false |
leftIcon |
ReactNode |
-- |
rightIcon |
ReactNode |
-- |
Style composition: base classes (flex, rounded-lg, transition, focus ring) + variant classes + size classes, combined via the getButtonClasses() utility.
Three-variant input with label, error, helper text, and icon support.
| Prop | Type | Default |
|---|---|---|
variant |
'default' | 'filled' | 'flushed' |
'default' |
size |
'sm' | 'md' | 'lg' |
'md' |
label |
string |
-- |
error |
string |
-- |
helperText |
string |
-- |
leftIcon / rightIcon
|
ReactNode |
-- |
fullWidth |
boolean |
false |
Composition-based card using Card, CardHeader, CardBody, CardFooter sub-components.
| Prop | Type | Default |
|---|---|---|
variant |
'default' | 'outlined' | 'elevated' |
'default' |
hoverable |
boolean |
false |
Animated modal with Framer Motion, Escape key close, backdrop click, and scroll lock.
| Prop | Type | Default |
|---|---|---|
size |
'sm' | 'md' | 'lg' | 'xl' | 'full' |
'md' |
isOpen |
boolean |
-- |
onClose |
() => void |
-- |
title |
string |
-- |
showCloseButton |
boolean |
true |
closeOnBackdrop |
boolean |
true |
A specialized confirmation dialog built on Framer Motion with optional text input. Supports 'default' and 'danger' variants. Used for destructive action confirmations throughout the app.
Semantic status indicators with dot support.
| Prop | Type | Default |
|---|---|---|
variant |
'default' | 'success' | 'error' | 'warning' | 'info' |
'default' |
size |
'sm' | 'md' | 'lg' |
'md' |
dot |
boolean |
false |
Accessible checkbox with custom styling using peer selectors. Supports label and error props. Uses claude-accent for the checked state.
Toggle switch with three sizes (sm, md, lg). Uses peer-based CSS transitions for the sliding thumb animation. Supports label prop.
Compound component pattern using React Context: Tabs, TabList, Tab, TabPanel. Features:
- Controlled and uncontrolled modes (
value/onChangeordefaultTab) - Animated active indicator via Framer Motion
layoutId - Icon and badge support on individual tabs
- Animated panel transitions
Loading indicator using the Lucide Loader icon with CSS spin animation.
| Prop | Type | Default |
|---|---|---|
size |
'sm' | 'md' | 'lg' | 'xl' |
'md' |
color |
'primary' | 'white' | 'gray' |
'primary' |
Beyond the base UI kit, the project implements domain-specific components for AI-driven legal analysis. These live at lexwebapp/src/components/ (top level, not inside ui/).
The message system is split into a message/ module:
-
Message(index.tsx) -- top-level component rendering user/assistant messages with rich metadata (decisions, analytics, citations, documents, thinking steps, execution plans, cost summaries) -
UserMessage-- user query bubble with edit capability -
AssistantMessage-- assistant response with Markdown rendering and embedded structured data -
MarkdownContent-- Markdown formatter with legal code highlighting
Manages the scrolling message list with auto-scroll behavior (pauses when user scrolls up) and integrates the SelectionToolbar for text selection actions.
An expandable component that displays the AI's reasoning chain progressively. Shows step titles with completion indicators and expandable content sections. Animates with Framer Motion.
Displays court decisions with:
- Case number, court name, date
- Summary text and relevance percentage
- Status badge (
active,overturned,modified) - Document type indicator and external link
Renders statistical case outcome data:
- Total cases with satisfied/rejected/partial breakdown (percentages)
- Trend indicator (up/down/stable)
- AI interpretation text
Displays API cost tracking information for the current request.
Renders execution plans showing which tools the AI intends to use, for user review before execution.
The frontend organizes feature-specific components into sub-directories:
| Module | Location | Purpose |
|---|---|---|
chat/ |
Chat-specific UI (tool selector, file attachments, prompt manager, tabs for decisions/documents/regulations) | |
message/ |
Message rendering pipeline | |
right-panel/ |
Evidence and results panel (search results, documents render here) | |
sidebar/ |
Navigation sidebar | |
billing/ |
Subscription and payment UI | |
consultation/ |
Legal consultation interface | |
legislation/ |
Legislation viewer components | |
contracts/ |
Contract management UI | |
admin/ |
Admin panel components | |
onboarding/ |
User onboarding flow | |
charts/ |
Data visualization components | |
video-call/ |
Video consultation UI |
All base UI components implement accessibility features:
- Keyboard Navigation: Tab navigation, Enter/Space activation, Escape for modals
-
Focus Management: Visible focus rings (
focus:ring-2 focus:ring-offset-2), scroll lock on modal open -
Screen Reader Support:
aria-labelon close buttons, semantic HTML (label/inputpairing viahtmlFor) -
Peer Selectors: Checkbox and Switch use
peer sr-onlypattern for custom styling while maintaining native input accessibility
import { Card, CardBody, Input, Button, Badge, Tabs, TabList, Tab, TabPanel } from '../components/ui';
function SearchForm() {
return (
<Card variant="elevated">
<CardBody className="space-y-4">
<Input
label="Search court decisions"
variant="default"
leftIcon={<SearchIcon />}
fullWidth
/>
<div className="flex gap-2">
<Badge variant="info">Civil</Badge>
<Badge variant="warning">Criminal</Badge>
</div>
<Button variant="primary" fullWidth>
Search
</Button>
</CardBody>
</Card>
);
}