limn
    ↑↓ ChooseEnter OpenEsc Close
    GitHub

    Components/States

    Chip

    An inline pill for a count, a label or a state: a tag, a plan name, "Online". It is passive, with no pointer and no focus, unless you give it a remove button, which is then its only control.

    Native componentGuide source

    Preview

    CodeSource
    fn view(palette: ComponentPalette) -> Element {
        Element::row(vec![
            Chip::new("rust", palette).render(),
            Chip::new("draft", palette).render(),
        ])
        .gap(8.0)
    }

    Examples

    Each one is the library drawing it. Press, type and hover.

    Labels2 / 9
    CodeSource
    row(CHIP_TONES.iter().map(|(tone, label)| Chip::new(*label, palette).tone(*tone).render()).collect())
    Labels3 / 9
    CodeSource
    row(CHIP_TONES.iter().map(|(tone, label)| Chip::new(*label, palette).tone(*tone).outline().render()).collect())
    Labels4 / 9
    CodeSource
    row(vec![
        Chip::new("Design", palette).color(Color::rgb(138, 92, 246)).render(),
        Chip::new("Research", palette).color(Color::rgb(20, 160, 150)).render(),
        Chip::new("Design", palette).color(Color::rgb(138, 92, 246)).outline().render(),
        Chip::new("Research", palette).color(Color::rgb(20, 160, 150)).outline().render(),
    ])
    Labels5 / 9
    CodeSource
    row(vec![
        Chip::new("Dense", palette).size(ControlSize::Dense).render().centered_y(),
        Chip::new("Compact", palette).size(ControlSize::Compact).render().centered_y(),
        Chip::new("Regular", palette).size(ControlSize::Regular).render().centered_y(),
        Chip::new("Spacious", palette).size(ControlSize::Spacious).render().centered_y(),
        Chip::new("3", palette).size(ControlSize::Dense).render().centered_y(),
        Chip::new("3", palette).size(ControlSize::Spacious).render().centered_y(),
    ])
    Labels6 / 9
    CodeSource
    row(vec![
        Chip::new("Online", palette).tone(AlertTone::Success).dot().render(),
        Chip::new("Failed", palette).tone(AlertTone::Error).dot().outline().render(),
        Chip::new("Idle", palette).dot().render(),
        Chip::new("main", palette).icon(Icon::GitBranch).render(),
        Chip::new("Verified", palette).tone(AlertTone::Info).icon(Icon::Check).render(),
    ])
    Labels7 / 9
    CodeSource
    row({
        // The tags still shown are one choice; each remove button chooses the list without its own.
        let shown: Vec<&str> = cx.state.choice("chip-tags", CHIP_TAGS).split(',').filter(|tag| !tag.is_empty()).collect();
        let mut tags: Vec<Element> = shown
            .iter()
            .map(|tag| {
                let rest: Vec<&str> = shown.iter().copied().filter(|other| other != tag).collect();
                let chip = Chip::new(*tag, palette).remove_action(format!("chip-tags={}", rest.join(",")));
                match *tag {
                    "urgent" => chip.tone(AlertTone::Warning),
                    "review" => chip.outline(),
                    _ => chip,
                }
                .render()
                .centered_y()
            })
            .collect();
        tags.push(Button::new(format!("chip-tags={CHIP_TAGS}"), "Restore", palette).variant(ButtonVariant::Outline).size(ControlSize::Dense).render());
        tags
    })
    Labels8 / 9
    CodeSource
    row(vec![
        Chip::new("rust", palette).remove_action("chip-disabled-rust").disabled().render(),
        Chip::new("urgent", palette).tone(AlertTone::Warning).remove_action("chip-disabled-urgent").disabled().render(),
        Chip::new("Offline", palette).dot().outline().disabled().render(),
    ])
    Labels9 / 9
    CodeSource
    row(vec![
        Chip::new("7", palette).render(),
        Chip::new("128", palette).render(),
        Chip::new("Reparaci\u{f3}n \u{b7} a label that is long for a chip", palette).tone(AlertTone::Info).remove_action("chip-long").render(),
    ])
    Wrapping1 / 3
    CodeSource
    boxed(Element::row(roles()).gap(6.0).wrap(), 240.0)
    Wrapping2 / 3
    CodeSource
    boxed(Element::row(roles()).gap(6.0).line_gap(12.0).wrap(), 360.0)
    Wrapping3 / 3
    CodeSource
    boxed(Element::row(wrap_tags).gap(6.0).wrap(), 240.0)

    Chip

    An inline pill for a count, a label or a state: a tag, a plan name, “Online”. It is passive, with no pointer and no focus, unless you give it a remove button, which is then its only control.

    Import

    use limn::{AlertTone, Chip, Component, ControlSize, Icon};

    Usage

    A chip needs a label and the palette. Place it in a row, so its width follows its text.

    fn view(palette: ComponentPalette) -> Element {
        Element::row(vec![
            Chip::new("rust", palette).render(),
            Chip::new("draft", palette).render(),
        ])
        .gap(8.0)
    }

    The fill is palette.selected with a border of palette.border. A label that is a single digit, with nothing before or after it, is drawn as a circle. A chip is as wide as its label: it does not wrap or truncate.

    A removable chip reports a click, and the host drops the tag:

    // view
    for (index, tag) in state.tags.iter().enumerate() {
        row.push(Chip::new(tag, palette).remove_action(format!("tag-remove:{index}")).render());
    }
    
    // update
    UiAction::Clicked { id } => {
        if let Some(index) = id.strip_prefix("tag-remove:").and_then(|index| index.parse::<usize>().ok()) {
            state.tags.remove(index);
        }
    }

    Outline

    Chip::new("Pro plan", palette).outline().render()

    outline draws the border only, with no fill, for a label that should sit quietly on its surface.

    Tones

    Chip::new("Deployed", palette).tone(AlertTone::Success).render()
    Chip::new("Failed", palette).tone(AlertTone::Error).outline().render()

    tone says a state. Its color is the one an Alert of the same tone gives its icon, AlertColors::for_tone(tone, palette).accent, so the two agree in every theme.

    ChipFillBorderLabel
    TonedA tint of the color over palette.surfaceA stronger tintThe color, at a contrast of 4.5 or more on the fill
    Toned, outlineNoneThe colorThe color, at a contrast of 4.5 or more on palette.surface

    AlertTone::Neutral is the chip without a tone. The color alone does not say the state to everyone: let the label say it.

    Color

    Chip::new("Design", palette).color(Color::rgb(138, 92, 246)).render()

    color takes a color of your own and draws it as a tone is drawn. The label is moved toward white or black until it reads on the fill, and the border of an outline chip until it shows on the surface, so one color serves both themes. The later of tone and color is the one used.

    Sizes

    Chip::new("beta", palette).size(ControlSize::Dense).render()

    size sets the height and the text together.

    SizeHeightText
    Dense1810
    Compact2011
    Regular (the chip without a size)2211
    Spacious2612

    Height

    Chip::new("3", palette).height(18.0).render()

    height sets the height alone, never lower than 16 px, and leaves the text as it is. Called after size, it changes that size’s height.

    Icon and dot

    Chip::new("main", palette).icon(Icon::GitBranch).render()
    Chip::new("Online", palette).tone(AlertTone::Success).dot().render()

    icon draws an icon before the label, and dot a circle 6 px across: the mark of a state such as “online” or “failed”. Both take the label’s color, so a toned chip has a toned dot. A chip has one or the other: the later call is the one drawn.

    Removable

    Chip::new("rust", palette).remove_action("tag-remove:rust").render()

    remove_action adds a round close button after the label that reports UiAction::Clicked with the ID you give. The chip does not remove itself: leave it out of the next view. Only the button is a target; a click on the label does nothing.

    Disabled

    Chip::new("rust", palette).remove_action("tag-remove:rust").disabled().render()
    Chip::new("rust", palette).remove_action("tag-remove:rust").enabled(state.can_edit).render()

    A disabled chip is drawn in palette.muted, keeping its fill and border. Its remove button stays in place and is passive: no hit region, no focus, nothing reported. A chip without a remove button is only muted.

    Many chips

    Element::row(tags).gap(6.0).wrap()

    A row with wrap moves the chips that do not fit its width to the next line, so a long list of tags or roles grows downward instead of out of its box. line_gap sets the space between the lines. See Layout.

    Keyboard

    A chip without a remove button takes no keys.

    KeyEffect
    TabMoves the focus to the remove button, and on from it
    Enter, SpaceReports the remove action

    Motion

    The remove button’s fill under the pointer changes over tokens().motion.hover_ms.

    Actions

    ActionWhen
    UiAction::Clicked { id }The remove button is clicked or activated with Enter or Space; id is the one given to remove_action

    Accessibility

    The remove button is a button named Labels::remove followed by the chip’s label, such as “Remove rust”, in the language of the labels you set. Disabled, it is still announced, as disabled. The chip itself has no node: its label is text like any other, and a dot or an icon says nothing to the platform, so the label must carry the state.

    Chip or Badge

    A chip is a pill in the flow of a row: it has its own place and its own label. A Badge is a mark over the corner of another element, such as an unread count on a bell: it takes no room and is nothing without the element it marks. Use a chip to label a thing beside it, a badge to mark a control.

    Avatar and Kbd are the other passive labels shown beside it in the gallery: an account photo or the initial of a name, and the keycaps of a shortcut.

    API reference

    The guide covers composition, options and actions. Generate the complete Rust type and builder reference from the checkout:

    cargo doc -p limn --open

    Component API overview · State and actions

    The example is drawn by crates/limn-gallery/src/pages/feedback.rs.