detaxine-ui-cli 0.8.21

The Official CLI for Detaxine UI
detaxine-ui-cli-0.8.21 is not a library.

detaxine-ui

A Leptos + Tailwind CSS component library compiled to WebAssembly. Build modern, reactive web UIs with a full set of accessible, themeable components — no JavaScript framework required.

Crates.io Docs.rs License: MIT License: APACHE

Live Demo

https://elonaire.github.io/detaxine-ui/


Installation

New Project

Install the CLI and let it scaffold everything for you:

cargo install detaxine-ui-cli
dtx init my-app
cd my-app
trunk serve

dtx init creates a ready-to-run Leptos + Trunk project with detaxine-ui as a dependency, input.css configured, and a Tailwind binary in place.

Import re-exported crates directly from detaxine_ui in your code:

// Instead of: use leptos::prelude::*;
use detaxine_ui::leptos::prelude::*;

// Instead of: use leptos_router::components::Router;
use detaxine_ui::leptos_router::components::Router;

// Instead of: use chrono::{Local, DateTime};
use detaxine_ui::chrono::{Local, DateTime};

Consumers who already depend on these crates directly can still do so — Cargo's deduplication ensures only one copy is compiled as long as the versions are compatible.

Existing Project

Coming soon


Prerequisites

  • Rust (stable)
  • Trunk — WASM bundler for Leptos
cargo install trunk
rustup target add wasm32-unknown-unknown

Quick Start

use detaxine_ui::{components::actions::button::BasicButton, leptos::prelude::*, icondata::AiCheckCircleOutlined};

#[component]
fn App() -> impl IntoView {
    view! {
        <BasicButton
            button_text="Confirm"
            icon=Some(AiCheckCircleOutlined)
            icon_before=true
            style_ext="bg-primary text-white hover:bg-secondary px-6 py-2.5"
            onclick=Callback::new(|_| leptos::logging::log!("clicked"))
        />
    }
}

fn main() {
    mount_to_body(App)
}

Components

Actions

Component Description
BasicButton Button with optional icon, disabled state, and custom styles
ButtonGroup Inline group of buttons with shared styles and rounded ends
Carousel Sliding panel carousel with navigation and indicators

Data Display

Component Description
Badge Overlaid count or status indicator anchored to a child element
Chip Removable tag with color temperature variants
LabelTag Static color-coded label
Timeline Vertical list of timestamped steps with icon, image, or circle heads
DataTable Sortable, paginated table with optional row-level edit and delete actions
Pagination Page navigation control emitting page-change callbacks

Feedback

Component Description
BasicModal Portal-based dialog with contextual icons and configurable footer actions
Popover Viewport-aware floating panel with auto-alignment and route-change close
ProgressBar Horizontal progress bar with determinate and indeterminate modes
CircularProgress Circular progress ring with optional percentage label
Spinner Animated SVG loading spinner with optional full-screen backdrop

Forms

Component Description
InputField All standard HTML input types via the InputFieldType enum
CustomFileInput Styled file picker with chip-based selected file list
Textarea Multi-line text input
SelectInput Native select dropdown
CustomSelectInput Searchable, chip-based single or multi-select
CheckboxInputField Single checkbox input
CheckboxGroup Grouped checkboxes with shared HashSet selection state
RadioInputField Single radio input
RadioInputGroup Grouped radio inputs with shared selection state
ToggleSwitch Boolean toggle built on a hidden checkbox
DatePicker Calendar date picker with min/max and disabled date support
RichTextEditor Contenteditable rich text editor with formatting toolbar
ReactiveForm Form wrapper that auto-submits whenever all fields are valid

Navigation

Component Description
Breadcrumbs Route-derived breadcrumb trail with custom label support
Panel Collapsible panel with a clickable title bar
Collapse Accordion group of panels with optional single-open enforcement
Tabs Scrollable tabbed view with slot-based content
Stepper Multi-step form wizard with per-step validation and linear mode

Theming

detaxine-ui uses CSS custom properties for its color system. Override them in your own CSS to match your brand:

@theme {
    --color-primary:        #3b82f6;
    --color-secondary:      #6366f1;
    --color-danger:         #ef4444;
    --color-success:        #22c55e;
    --color-warning:        #f59e0b;
    --color-info:           #0ea5e9;
    --color-light-gray:     #e5e7eb;
    --color-mid-gray:       #9ca3af;
    --color-contrast-white: #ffffff;
    --color-navy:           #0f172a;
}

All components reference these variables via Tailwind utilities (bg-primary, text-danger, etc.) so a single override updates the entire library.


Style Extension

Every component exposes one or more style extension props so you can apply any Tailwind utility without forking the library:

Prop Applies to
style_ext The root or primary element
ext_input_styles The input or select element
ext_wrapper_styles The outer wrapper div
ext_label_styles The label element
container_style_ext The modal panel

Example — making a full-width danger button:

use detaxine_ui::{components::actions::button::BasicButton, leptos::prelude::*, icondata::AiCheckCircleOutlined};

#[component]
fn Example() -> impl IntoView {
    view! {
        <BasicButton
            button_text="Delete"
            style_ext="w-full bg-danger text-white hover:bg-danger/80"
        />
    }
}

Form Handling

ReactiveForm wraps your fields and fires a native submit event automatically whenever checkValidity() returns true, on every input or change event. Read form data with the standard FormData API:

use detaxine_ui::{
    components::forms::{
        checkbox::{CheckboxGroup, CheckboxOption},
        ReactiveForm, InputField, InputFieldType,
    },
    leptos::prelude::*,
};
use std::collections::HashSet;

#[component]
fn Example() -> impl IntoView {
    let selected = RwSignal::new(HashSet::new());
    let form_ref = NodeRef::new();

    let handle_submit = move |ev: SubmitEvent| {
        ev.prevent_default();
        let target = ev.target()
            .and_then(|t| t.dyn_into::<HtmlFormElement>().ok());
    
        if let Some(form) = target {
            if let Ok(form_data) = FormData::new_with_form(&form) {
                let name = form_data.get("name").as_string().unwrap_or_default();
                leptos::logging::log!("name: {}", name);
            }
        }
    };

    view! {
        <ReactiveForm form_ref=form_ref on:submit=handle_submit>
            <CheckboxGroup
                legend="Interests"
                name="interests"
                options=RwSignal::new(vec![
                    CheckboxOption::new("rust", "Rust", None),
                    CheckboxOption::new("leptos", "Leptos", None),
                ])
                selected_values=selected
            />
            <InputField field_type=InputFieldType::Email name="email" label="Email" required=true />
        </ReactiveForm>
    }
}

Modal Setup

BasicModal portals its content into a #modal-root element. Add this to your index.html before the closing </body> tag or to your root component:

<div id="modal-root"></div>

Router Requirement ⚠️

Popover, DataTable (which uses Popover internally), and Breadcrumbs all call use_location() from leptos_router. Wrap your app in a <Router> or else these components will panic at runtime:

use leptos_router::components::Router;

fn main() {
    mount_to_body(|| view! {
        <Router>
            // ... your root component
        </Router>
    })
}

Rich Text Editor — Image Upload

The RichTextEditor defaults to base64 data URLs for image insertion. Supply a custom on_image_insert callback to upload to your own storage:

use detaxine_ui::{
    components::content::richtext_editor::{ExtraFormatingOption, RichTextEditor},
    leptos::prelude::*,
    web_sys::File
};
use std::pin::Pin;

#[component]
fn Example() -> impl IntoView {
    let content = RwSignal::new("<p>Hello world!</p>".to_string());

    view! {
        <RichTextEditor
            initial_content=content
            id_attr="editor"
            name="body"
            placeholder="Write something..."
            extra_formating_options=vec![
                ExtraFormatingOption::Heading,
                ExtraFormatingOption::Lists,
                ExtraFormatingOption::CodeBlock,
                ExtraFormatingOption::ImageUpload,
            ]
            on_image_insert=Callback::new(move |file: File| {
                Box::pin(async move {
                    // upload to S3, Cloudflare R2, etc. and return the URL
                    Some("https://cdn.example.com/image.png".to_string())
                }) as Pin<Box<dyn Future<Output = Option<String>>>>
            })
        />
    }
}

Contributing

Contributions are welcome. Please open an issue before submitting a pull request for significant changes.

  1. Fork the repository
  2. Create a feature branch: git checkout -b feat/my-component
  3. Commit your changes: git commit -m "feat: add MyComponent"
  4. Push and open a pull request

License

This project is licensed under both the MIT license and the Apache License (Version 2.0). MIT — see LICENSE-MIT for details. APACHE — see LICENSE-APACHE for details.