Files
leptos-shadcn-ui/docs/architecture.md

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

  1. Component Isolation: Each component is a separate crate for maximum modularity
  2. Framework Parity: Consistent API across all supported frameworks
  3. Theme Support: Default and New York variants for all components
  4. Type Safety: Leverage Rust's type system for component properties
  5. 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 structure
  • yew_component.hbs - Yew component structure
  • dioxus_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.