日本語

The hypersig_ui crate

Accessible components, styled after shadcn/ui

Buttons, fields, pickers, tabs, dialogs, menus, tooltips, and toasts, built on the browser's native elements and following the WAI-ARIA Authoring Practices. Each returns an El, so it takes handlers and children like any other element.

Native elements first

The browser already gives <button>, <select>, <details>, <dialog>, and popover their behavior, and the components use it as it is.

WAI-ARIA keyboard interaction

Tabs, listbox options, and menu items move with the arrow keys, Home, and End, and a combobox keeps focus in its text field.

Contrast that meets WCAG 2.2 AA

shadcn/ui's zinc theme, with a control's border at 3:1, secondary text at 4.5:1, and focus drawn as a solid outline.

A dialog in a few lines

A const handle connects the dialog with the buttons that open and close it, and the browser opens it as a modal through invoker commands.

Result

dialog.rs
use hypersig::html::{div, p};
use hypersig::style::pattern::{stack, wrap};
use hypersig::style::value::rem;
use hypersig::{Page, Signals};
use hypersig_ui::{ButtonSize, ButtonVariant, Dialog, Side, TextField, dialog_footer};
use serde::{Deserialize, Serialize};

#[derive(Signals, Serialize, Deserialize)]
pub struct Project {
    name: String,
    status: String,
}

const RENAME: Dialog<Project> = Dialog::new("rename");
const DELETE: Dialog<Project> = Dialog::new("delete");
const DETAILS: Dialog<Project> = Dialog::new("details");

pub fn page() -> Page<Project> {
    let name = Project::name();
    let status = Project::status();
    let initial = Project {
        name: "hypersig".into(),
        status: String::new(),
    };
    Page::new(
        "Project",
        initial,
    )
    .child(
        div()
            .css(stack(rem(1)))
            .child(
                div()
                    .css(wrap(rem(0.5)))
                    .child(
                        RENAME
                            .trigger(ButtonVariant::Outline, ButtonSize::Default)
                            .text("Rename"),
                    )
                    .child(
                        DETAILS
                            .trigger(ButtonVariant::Outline, ButtonSize::Default)
                            .text("Details"),
                    )
                    .child(
                        DELETE
                            .trigger(ButtonVariant::Destructive, ButtonSize::Default)
                            .text("Delete"),
                    ),
            )
            .child(p().attr("role", "status").bind_text(status))
            .child(
                RENAME.dialog(
                    "Rename project",
                    "The new name shows everywhere the project appears.",
                    div()
                        .css(stack(rem(1)))
                        .child(TextField::new(
                            "Name",
                            name,
                        ))
                        .child(
                            dialog_footer()
                                .child(
                                    RENAME
                                        .close(ButtonVariant::Outline, ButtonSize::Default)
                                        .text("Cancel"),
                                )
                                .child(
                                    RENAME
                                        .close(ButtonVariant::Default, ButtonSize::Default)
                                        .on_click(status.set("Renamed the project."))
                                        .text("Save"),
                                ),
                        ),
                ),
            )
            .child(
                DETAILS.sheet(
                    Side::Right,
                    "Project details",
                    "Created on March 3. Deployed 12 times.",
                    dialog_footer().child(
                        DETAILS
                            .close(ButtonVariant::Outline, ButtonSize::Default)
                            .text("Done"),
                    ),
                ),
            )
            .child(
                DELETE.alert_dialog(

                    "Delete this project?",
                    "The project and its deployments are removed. This cannot be undone.",
                    dialog_footer()
                        .child(
                            DELETE
                                .close(ButtonVariant::Outline, ButtonSize::Default)
                                .text("Cancel"),
                        )
                        .child(
                            DELETE
                                .close(ButtonVariant::Destructive, ButtonSize::Default)
                                .on_click(status.set("Deleted the project."))
                                .text("Delete"),
                        ),
                ),
            ),
    )
}

Every component

Part of hypersig