17 KiB
Architecture Documentation
This document provides a comprehensive overview of the Rust shadcn/ui architecture, design patterns, and implementation strategies.
System Overview
Rust shadcn/ui is a modular, multi-framework UI component library that provides shadcn/ui components for Rust web applications. The architecture emphasizes type safety, performance, and framework flexibility while maintaining design consistency.
Core Principles
- Component Isolation: Each component is a separate crate for maximum modularity
- Framework Parity: Consistent API across all supported frameworks
- Theme Support: Default and New York variants for all components
- Type Safety: Leverage Rust's type system for component properties
- Performance: Minimal runtime overhead and optimal bundle sizes
Workspace Architecture
Cargo Workspace Structure
shadcn-ui/
├── Cargo.toml # Workspace configuration
├── Cargo.lock # Dependency lockfile
├── packages/ # Core packages
│ ├── shadcn/ # CLI tool
│ ├── registry/ # Component registry
│ ├── component-generator/ # Code generation
│ ├── test-utils/ # Testing utilities
│ ├── leptos/ # Leptos components
│ └── yew/ # Yew components
├── book-examples/ # Documentation examples
├── docs/ # Project documentation
└── tests/ # Integration tests
Package Organization
Core Infrastructure
packages/shadcn/
- CLI tool for project management and component generation
- Built with clap for argument parsing
- Supports multiple subcommands (generate, init, add, diff)
packages/registry/
- Component metadata and registry management
- Centralizes component information across frameworks
- Provides component discovery and validation
packages/component-generator/
- Handlebars-based code generation system
- Framework-agnostic templates with specific implementations
- Supports multiple output formats and customization
packages/test-utils/
- Shared testing utilities across all components
- Framework-agnostic testing patterns
- Performance benchmarking tools
Framework Packages
packages/leptos/
leptos/
├── Cargo.toml # Framework-level dependencies
├── src/
│ ├── lib.rs # Re-exports all components
│ └── components/ # Shared utilities
└── {component}/ # Individual component crates
├── Cargo.toml
├── src/
│ ├── lib.rs # Public API
│ ├── default.rs # Default theme
│ └── new_york.rs # New York theme
└── README.md
packages/yew/
yew/
├── Cargo.toml # Framework-level dependencies
├── src/
│ ├── lib.rs # Re-exports all components
│ └── components/ # Shared utilities
└── {component}/ # Individual component crates
├── Cargo.toml
├── src/
│ ├── lib.rs # Public API
│ ├── default.rs # Default theme
│ └── new_york.rs # New York theme
└── README.md
Component Architecture
Design Patterns
1. Component Isolation Pattern
Each component is implemented as a separate Cargo crate:
Benefits:
- Independent versioning and dependency management
- Granular imports (users only import what they need)
- Parallel compilation and development
- Clear separation of concerns
Implementation:
// packages/leptos/button/Cargo.toml
[package]
name = "shadcn-ui-leptos-button"
version = "0.1.0"
edition = "2021"
[dependencies]
leptos = "0.6"
tailwind_fuse = "0.3"
2. Theme Variant Pattern
Components support multiple design themes through a dual-module approach:
// src/lib.rs - Public API
pub mod default;
pub mod new_york;
// Re-export default theme at crate root
pub use default::*;
// Named export for alternative theme
pub use new_york as new_york;
Usage:
// Default theme (implicit)
use shadcn_ui_leptos_button::Button;
// New York theme (explicit)
use shadcn_ui_leptos_button::new_york::Button;
3. Styling Composition Pattern
Dynamic CSS class composition using tailwind_fuse:
#[derive(TwClass)]
#[tw(
class = "inline-flex items-center justify-center rounded-md font-medium",
variants(
variant(
default = "bg-primary text-primary-foreground hover:bg-primary/90",
destructive = "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline = "border border-input bg-background hover:bg-accent",
),
size(
default = "h-10 px-4 py-2",
sm = "h-9 rounded-md px-3",
lg = "h-11 rounded-md px-8",
)
)
)]
pub struct ButtonClass {
pub variant: ButtonVariant,
pub size: ButtonSize,
}
4. Props Pattern
Consistent property handling across frameworks:
Leptos:
#[component]
pub fn Button(
#[prop(optional)] variant: MaybeProp<ButtonVariant>,
#[prop(optional)] size: MaybeProp<ButtonSize>,
#[prop(optional)] disabled: MaybeProp<bool>,
#[prop(optional)] class: MaybeProp<String>,
children: Children,
) -> impl IntoView
Yew:
#[derive(Properties, PartialEq)]
pub struct ButtonProps {
#[prop_or_default]
pub variant: ButtonVariant,
#[prop_or_default]
pub size: ButtonSize,
#[prop_or_default]
pub disabled: bool,
#[prop_or_default]
pub class: String,
pub children: Children,
}
Framework Integration
Leptos Integration
Key Features:
- Signal-based reactivity
- Server-side rendering (SSR) support
- Hydration-friendly components
view!macro for declarative UI
Architecture Patterns:
Reactive State Management:
#[component]
pub fn Toggle() -> impl IntoView {
let (checked, set_checked) = create_signal(false);
let toggle_class = create_memo(move |_| {
ToggleClass {
pressed: checked.get(),
size: ToggleSize::Default,
}
});
view! {
<button
type="button"
role="switch"
aria-checked=move || checked.get()
class=toggle_class
on:click=move |_| set_checked.update(|c| *c = !*c)
>
// Component implementation
</button>
}
}
SSR Compatibility:
// Ensure components work in SSR environments
#[component]
pub fn Component() -> impl IntoView {
// Use create_effect for client-only side effects
create_effect(move |_| {
#[cfg(feature = "hydrate")]
{
// Client-only code
}
});
view! { /* SSR-safe markup */ }
}
Yew Integration
Key Features:
- Component-based architecture
- Virtual DOM diffing
- WebAssembly optimization
- Hook-based state management
Architecture Patterns:
Component State:
#[function_component]
pub fn Toggle() -> Html {
let checked = use_state(|| false);
let toggle_class = use_memo(
{
let checked = checked.clone();
move |_| ToggleClass {
pressed: *checked,
size: ToggleSize::Default,
}
},
(),
);
let onclick = {
let checked = checked.clone();
Callback::from(move |_| {
checked.set(!*checked);
})
};
html! {
<button
type="button"
role="switch"
aria-checked={checked.to_string()}
class={classes!(&*toggle_class)}
{onclick}
>
// Component implementation
</button>
}
}
Performance Optimization:
// Use memo for expensive computations
let complex_calculation = use_memo(
|props| expensive_function(&props.data),
props.clone(),
);
// Use callback for event handlers
let onclick = use_callback(
|(event, props)| handle_click(event, &props.data),
props.clone(),
);
Styling Architecture
TailwindCSS Integration
Design System Foundation:
- Consistent spacing scale (0.25rem increments)
- Type scale with semantic naming
- Color system with semantic tokens
- Component-specific utility classes
CSS Custom Properties:
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96%;
--secondary-foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 210 40% 96%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
}
Dark Mode Support:
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
/* ... */
}
Component Styling Patterns
Base Component Classes:
#[derive(TwClass)]
#[tw(class = "base-classes")]
pub struct ComponentClass {
// Variant properties
}
Variant System:
#[derive(TwClass)]
#[tw(
class = "base-classes",
variants(
// Visual variants
variant(
default = "default-styles",
secondary = "secondary-styles",
destructive = "destructive-styles",
),
// Size variants
size(
sm = "small-styles",
default = "default-styles",
lg = "large-styles",
)
),
// Default variants
defaults(
variant = default,
size = default
)
)]
Responsive Design:
// Mobile-first responsive classes
"w-full sm:w-auto md:w-64 lg:w-80"
// Container queries (when supported)
"@container/sidebar:md:w-64"
Code Generation System
Template Architecture
Handlebars Template System:
{{!-- Base template structure --}}
use {{framework}}::*;
use tailwind_fuse::*;
{{#if has_props}}
#[derive({{prop_derives}})]
pub struct {{component_name}}Props {
{{#each props}}
{{#if optional}}#[prop_or_default]{{/if}}
pub {{name}}: {{prop_type}},
{{/each}}
}
{{/if}}
{{framework_component_macro}}
pub fn {{component_name}}(
{{#each props}}
{{#if @first}}{{else}},{{/if}}
{{render_prop_parameter this}}
{{/each}}
) -> {{return_type}} {
{{render_component_body}}
}
Framework-Specific Templates:
leptos_component.hbs- Leptos component structureyew_component.hbs- Yew component structuredioxus_component.hbs- Dioxus component structure (planned)
Configuration System:
pub struct ComponentConfig {
pub name: String,
pub framework: Framework,
pub theme_variants: Vec<String>,
pub props: HashMap<String, PropConfig>,
pub dependencies: Vec<String>,
}
Testing Architecture
Framework Testing Patterns
Leptos Testing:
#[cfg(test)]
mod tests {
use super::*;
use leptos::*;
use leptos_dom::*;
#[test]
fn test_button_render() {
let _ = create_runtime();
let button = Button(ButtonProps {
variant: ButtonVariant::Default,
children: "Click me".into(),
..Default::default()
});
// Assert component properties
assert!(button.is_ok());
}
}
Yew Testing:
#[cfg(test)]
mod tests {
use super::*;
use yew::prelude::*;
use yew::html;
#[test]
fn test_button_render() {
let props = ButtonProps {
variant: ButtonVariant::Default,
children: html! { "Click me" }.into(),
..Default::default()
};
let button = html! { <Button ..props /> };
// Test rendering
assert!(!button.is_empty());
}
}
Integration Testing
Component Integration:
// tests/integration/button_test.rs
use shadcn_ui_leptos_button::Button;
use shadcn_ui_yew_button::Button as YewButton;
#[test]
fn test_component_parity() {
// Test that Leptos and Yew buttons have equivalent APIs
// and render similar output structures
}
Performance Considerations
Bundle Size Optimization
Tree Shaking:
- Each component as separate crate enables fine-grained imports
- Users only bundle components they actually use
- Framework-specific optimizations applied automatically
Lazy Loading:
// Dynamic imports for large components
use leptos_router::*;
#[component]
pub fn App() -> impl IntoView {
view! {
<Router>
<Route
path="/dashboard"
view=|| {
// Lazy load heavy dashboard components
provide_context(DashboardContext::new());
view! { <DashboardPage /> }
}
/>
</Router>
}
}
Runtime Performance
Memoization Patterns:
// Leptos
let expensive_calculation = create_memo(move |_| {
expensive_function(props.data())
});
// Yew
let expensive_calculation = use_memo(
|props| expensive_function(&props.data),
props.clone(),
);
Event Handler Optimization:
// Avoid recreating handlers on every render
let onclick = create_callback(move |event| {
// Handler logic
});
Security Considerations
XSS Prevention
HTML Sanitization:
// Always sanitize user-provided HTML content
use ammonia::clean;
let safe_html = clean(&user_input);
Attribute Validation:
// Validate and sanitize component attributes
pub fn sanitize_class_name(class: &str) -> String {
class.chars()
.filter(|c| c.is_alphanumeric() || *c == '-' || *c == '_')
.collect()
}
Dependency Security
Supply Chain Security:
- Pin dependency versions in Cargo.lock
- Regular security audits with
cargo audit - Minimal dependency footprint
- Prefer well-maintained, popular crates
Accessibility Architecture
ARIA Integration
Component Accessibility:
view! {
<button
type="button"
role="switch"
aria-checked=move || checked.get()
aria-describedby=description_id
class=toggle_class
>
<span class="sr-only">
{move || if checked.get() { "On" } else { "Off" }}
</span>
</button>
}
Screen Reader Support:
- Semantic HTML elements by default
- ARIA labels and descriptions
- Focus management
- Keyboard navigation support
Keyboard Navigation
Focus Management:
use leptos_use::*;
#[component]
pub fn Dialog(show: ReadSignal<bool>) -> impl IntoView {
let dialog_ref = create_node_ref::<html::Div>();
// Trap focus within dialog when open
let _ = use_focus_trap(dialog_ref, show);
// Return focus to trigger when closed
let _ = use_focus_return(dialog_ref, show);
view! {
<div node_ref=dialog_ref>
// Dialog content
</div>
}
}
Documentation Architecture
API Documentation
Rust Doc Standards:
/// A button component that triggers an action or event.
///
/// The button component supports multiple variants and sizes, and can be
/// customized with additional CSS classes.
///
/// # Examples
///
/// ```rust
/// use shadcn_ui_leptos_button::*;
///
/// let button = view! {
/// <Button variant=ButtonVariant::Primary>
/// "Click me"
/// </Button>
/// };
/// ```
///
/// # Accessibility
///
/// The button component includes proper ARIA attributes and keyboard
/// navigation support by default.
#[component]
pub fn Button(/* ... */) -> impl IntoView {
// Implementation
}
Component Examples:
/// # Component Variants
///
/// ```rust
/// // Primary button
/// view! { <Button variant=ButtonVariant::Primary>"Primary"</Button> }
///
/// // Secondary button
/// view! { <Button variant=ButtonVariant::Secondary>"Secondary"</Button> }
///
/// // Disabled button
/// view! { <Button disabled=true>"Disabled"</Button> }
/// ```
Living Documentation
Storybook Integration:
- Interactive component explorer
- Visual regression testing
- Design system documentation
- Accessibility testing integration
Future Architecture Considerations
Framework Expansion
Dioxus Support:
- Cross-platform component compilation
- Native mobile and desktop support
- Shared business logic patterns
Web Components:
- Framework-agnostic distribution
- Custom element registration
- Progressive enhancement
Advanced Features
Animation System:
- CSS transition integration
- JavaScript animation coordination
- Performance-optimized animations
Theme System Evolution:
- Runtime theme switching
- Custom theme generation
- Design token automation
Developer Experience:
- Hot module replacement
- Component debugging tools
- Performance profiling integration
This architecture provides a solid foundation for scalable, maintainable, and performant UI component development across multiple Rust web frameworks.