Draw a character with a mood, a status line and an optional message bubble.
Until the app sets its own status text, the status line shows the
character's line for the mood (the status column of
mascot_moods(character)). mascot_status() with no text hands the status
back to the mood.
Usage
mascot(
id,
...,
character = "tessa",
mood = "idle",
status = NULL,
bubble = NULL,
size = 160,
placement = "bottom-right",
theme = NULL,
motion = TRUE,
click = NULL
)Arguments
- id
A required non-empty string without whitespace, unique on the page. In a Shiny module use
ns("helper")in the UI and"helper"in the server;update_mascot()adds the module namespace once.- ...
Must be empty. Forces the arguments after it to be named.
- character
A character id such as
"tessa", or amascot_character()helper (Knurl and Neer are experimental). Defaults to"tessa".NULLis not accepted.- mood
A mood name or
mascot_mood()object. Defaults to"idle". Seemascot_moods()for available choices.NULLis not accepted.- status
A non-empty string or
mascot_status()object.NULL(the default) follows the mood. Text is always escaped, even insidehtmltools::HTML().- bubble
A non-empty string, an
htmltoolstag or tag list, or amascot_bubble()object.NULL(the default) shows no bubble. A plain string gets the defaults ofmascot_bubble(): five seconds and a close button. Plain text is escaped;htmltools::HTML()or tags supply HTML. Tags with dependencies or head content are not supported.- size
A finite number of CSS pixels (at least
24), a positive percentage string such as"20%", or amascot_size()object. Defaults to160. Percentages follow the parent width when inline and the window width when floating, with a 24-pixel floor. On screens at most 575.98 CSS pixels wide, floating mascots scale down to a phone size by default ("auto"), while inline mascots stay unchanged. Passmascot_size()withsmall_screen = NULLto keep the same size everywhere.NULLis not accepted.- placement
"bottom-right"(the default),"inline", a corner such as"top-left", a named list such aslist(position = "top-left", minimise = TRUE, drag = "snap"), or amascot_placement()object. Floating mascots show a minimise control by default; dragging is off. Inline mascots sit in the page flow and ignoreminimiseanddrag. If you set either one yourself, a message says so; the defaults give no message. Withdrag, visitors can also drag the minimised restore button; each drop setsinput$<id>_positionto the corner ("snap") or a list withxandy("free", experimental); saving it between visits is the app's job. Seemascot_placement().NULLis not accepted.- theme
A palette id string from
mascot_palettes(), a named list of colour roles, abslib::bs_theme()or amascot_theme()object.NULL(the default) keeps the character's own colours. A palette sets the base colours for light and dark mode;"original"selects the character's own colours. Roles you leave unset follow the character's rules.- motion
A logical flag or
mascot_motion_settings()object.TRUE(the default) allows movement, unless the visitor prefers reduced motion.FALSEstops all movement, including panels, drag snapping, minimising and restoring.NULLis not accepted.- click
What a click on the mascot does in a Shiny app.
NULL(the default) does nothing: the mascot is a picture, and keyboard focus skips it. A label that is not blank, such as"Open help", or amascot_action()object makes it a button for one action of your app. Amascot_popup_menu()object makes it open a menu, whose choice setsinput$<id>_menu. With a click, each time the visitor activates the action,input$<id>_clickis set (see Details).
Details
In mascot() and its helpers, NULL (where accepted) means none or the
default; in update_mascot() it means unchanged. An empty helper
(mascot_bubble(), mascot_status(), mascot_theme(), mascot_action() or
mascot_popup_menu()) resets that setting.
mascot() and the helpers raise an error on an invalid value;
update_mascot() warns instead and leaves the mascot unchanged. See
mascot-package for condition classes.
Use the always visible status line for a short description of the current state.
Use a bubble for a message about something that happened; it may time out.
Use mascot_bubble(..., duration = Inf) for a message the user must act on,
such as an invalid form, because a timed message can vanish before it is read.
When minimised, the status remains available to screen readers. See
mascot_placement() for restore controls and message timing.
In Shiny the mascot is also an input. input$<id> only tells the server that
the mascot has appeared; its contents are internal, so do not rely on them.
In modules, golem and rhino apps. Use the module's ns() in the UI and
its local id in the server. session$ns("helper") also works in the server;
the namespace is not added twice. In nested modules, namespace the child module's
id in the parent UI and call its server with the local child id. Inputs such
as input$helper_menu, input$helper_click, input$helper_position and
input$helper_minimised belong to that child module.
A module can put the mascot in its UI and update it from its server:
helper_ui <- function(id) {
ns <- shiny::NS(id)
mascot(ns("helper"))
}
helper_server <- function(id) {
shiny::moduleServer(id, function(input, output, session) {
update_mascot("helper", mood = "happy")
})
}The same pattern works in the function-built app_ui() of a golem app and in
rhino's app/view/ modules using shiny::moduleServer(). The package's
scripts and styles are attached through htmltools::htmlDependency(),
including with shiny::renderUI(), and shared by mascots on the page.
A built-in character's data travels the same way, once per page for each
character in use; a custom character's data stays inside its own tag.
You do not need to copy any of the package's files into your app. Try
mascot_example("golem") (requires golem) or mascot_example("rhino")
(a plain Shiny structure without rhino or box).
Showing the mascot later. To add the mascot only when it is needed, build
it in the server with shiny::renderUI() and place shiny::uiOutput() in the
UI. Updates sent before it appears are kept until it does, so the first
update_mascot() can run in the same observer:
library(shiny)
library(mascot)
ui <- fluidPage(
actionButton("start", "Start the review"),
actionButton("finish", "Finish"),
uiOutput("helper_ui")
)
server <- function(input, output, session) {
shown <- reactiveVal(FALSE)
output$helper_ui <- renderUI({
req(shown())
mascot("helper", status = "Reviewing")
})
observeEvent(input$start, {
shown(TRUE)
# Sent before the mascot exists: kept until it appears.
update_mascot("helper", mood = "thinking")
})
observeEvent(input$finish, {
update_mascot("helper", mood = "happy", status = "Review complete")
})
}
shinyApp(ui, server)The reactiveVal() makes the mascot appear once: clicking Start the
review again does not rebuild it, so it keeps its mood and position.
Clicks. With click, the mascot is a button: an action named by its
label, or a menu. Each time the visitor activates the action (a click or tap,
Enter or Space while it has keyboard focus, or a screen reader's activate
command), input$<id>_click is set to the number of activations since the
mascot appeared, so shiny::observeEvent(input$helper_click, ...) runs each
time for an action.
Opening and closing a menu never sets this input. Dragging a floating mascot
never counts as a click. See mascot_action() and mascot_popup_menu().
See also
Other mascots in Shiny:
update_mascot()
Examples
ui <- mascot("helper", mood = "happy")
ui <- mascot("small", size = 96, placement = "bottom-right")
ui <- mascot("responsive", size = mascot_size(120, small_screen = 48))
ui <- mascot("still", motion = FALSE)
ui <- mascot("helpful", click = "Open help")
if (FALSE) { # interactive()
shiny::shinyApp(
ui = shiny::fluidPage(mascot("helper")),
server = function(input, output, session) {}
)
}
