Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### New features

* Added `ui.icon()` for rendering FontAwesome and Bootstrap Icons as inline SVGs. Both icon sets are bundled directly with Shiny — no external packages required. Supports semantic sizes (`"xs"` through `"2xl"`), fill color, custom CSS classes, and accessibility modes (decorative by default, semantic with `a11y="semantic"`). (#2165)

* Added toolbar component with `ui.toolbar()`, `ui.toolbar_input_button()`, `ui.toolbar_input_select()`, `ui.toolbar_divider()`, `ui.toolbar_spacer()`, `ui.update_toolbar_input_button()`, and `ui.update_toolbar_input_select()`. Toolbars are compact UI containers designed for small form elements suitable for card headers, footers, and other constrained spaces. They support flexible alignment (left/right), custom spacing and width, icon-only or labeled buttons with optional tooltips, select inputs with grouped choices, visual dividers for separating elements, and flexible spacers for split layouts. Server-side updates allow dynamic modification of button and select properties. (#2155)


Expand Down
114 changes: 114 additions & 0 deletions shiny/api-examples/icon/app-core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
from shiny import App, ui

app_ui = ui.page_fluid(
ui.h2("Icon Examples"),
ui.layout_columns(
ui.card(
ui.card_header("FontAwesome Icons"),
ui.p("Default library — solid style:"),
ui.icon("star"),
ui.icon("gear"),
ui.icon("user"),
ui.br(),
ui.p("Brand icons (variant='brands'):"),
ui.icon("github", variant="brands"),
ui.icon("python", variant="brands"),
ui.icon("twitter", variant="brands"),
ui.br(),
ui.p("Custom fill color:"),
ui.icon("star", fill="gold"),
ui.icon("heart", fill="crimson"),
ui.icon("circle-check", fill="green"),
),
ui.card(
ui.card_header("Bootstrap Icons"),
ui.p("Basic icons (lib='bs'):"),
ui.icon("star", lib="bs"),
ui.icon("gear", lib="bs"),
ui.icon("person", lib="bs"),
ui.br(),
ui.p("Custom fill color:"),
ui.icon("star-fill", lib="bs", fill="gold"),
ui.icon("heart-fill", lib="bs", fill="crimson"),
ui.br(),
ui.p("Styled with CSS classes:"),
ui.icon("exclamation-triangle-fill", lib="bs", size="1.25em", class_="text-warning"),
ui.icon("check-circle-fill", lib="bs", size="1.25em", class_="text-success"),
ui.icon("x-circle-fill", lib="bs", size="1.25em", class_="text-danger"),
),
),
ui.layout_columns(
ui.card(
ui.card_header("Sizes"),
ui.p("Semantic sizes (same scale for both libs):"),
*[ui.icon("circle", lib="bs", size=sz) for sz in ("xs", "sm", "md", "lg", "xl", "2xl")],
ui.br(),
ui.p("CSS units:"),
ui.icon("gear", size="1em"),
ui.icon("gear", size="2em"),
ui.icon("gear", size="3em"),
ui.br(),
ui.p("Numeric (px):"),
ui.icon("gear", lib="bs", size=16),
ui.icon("gear", lib="bs", size=32),
ui.icon("gear", lib="bs", size=48),
),
ui.card(
ui.card_header("Accessibility"),
ui.p(
"Decorative (default) — hidden from screen readers. "
"Label the surrounding element instead:"
),
ui.input_action_button(
"btn_delete",
ui.icon("trash"),
aria_label="Delete item",
),
ui.br(),
ui.br(),
ui.p("Semantic — icon conveys meaning directly:"),
ui.icon(
"circle-exclamation",
size="2em",
title="Warning",
a11y="semantic",
),
ui.icon(
"exclamation-triangle",
lib="bs",
size="2em",
title="Warning",
a11y="semantic",
),
ui.br(),
ui.p("Semantic without title — label derived from icon name:"),
ui.icon("heart-fill", lib="bs", a11y="semantic"),
),
),
ui.layout_columns(
ui.card(
ui.card_header("Icons in Buttons"),
ui.p("FontAwesome:"),
ui.input_action_button("btn_fa", "Save", icon=ui.icon("floppy-disk")),
ui.input_action_button(
"btn_fa2", "Delete", icon=ui.icon("trash", fill="crimson")
),
ui.br(),
ui.br(),
ui.p("Bootstrap Icons:"),
ui.input_action_button(
"btn_bs", "Save", icon=ui.icon("floppy", lib="bs")
),
ui.input_action_button(
"btn_bs2", "Delete", icon=ui.icon("trash", lib="bs")
),
),
),
)


def server(input, output, session):
pass


app = App(app_ui, server)
103 changes: 103 additions & 0 deletions shiny/api-examples/icon/app-express.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
from shiny.express import ui

ui.h2("Icon Examples")

with ui.layout_columns():
with ui.card():
ui.card_header("FontAwesome Icons")
ui.p("Default library — solid style:")
ui.icon("star")
ui.icon("gear")
ui.icon("user")
ui.br()
ui.p("Brand icons (variant='brands'):")
ui.icon("github", variant="brands")
ui.icon("python", variant="brands")
ui.icon("twitter", variant="brands")
ui.br()
ui.p("Custom fill color:")
ui.icon("star", fill="gold")
ui.icon("heart", fill="crimson")
ui.icon("circle-check", fill="green")

with ui.card():
ui.card_header("Bootstrap Icons")
ui.p("Basic icons (lib='bs'):")
ui.icon("star", lib="bs")
ui.icon("gear", lib="bs")
ui.icon("person", lib="bs")
ui.br()
ui.p("Custom fill color:")
ui.icon("star-fill", lib="bs", fill="gold")
ui.icon("heart-fill", lib="bs", fill="crimson")
ui.br()
ui.p("Styled with CSS classes:")
ui.icon("exclamation-triangle-fill", lib="bs", size="1.25em", class_="text-warning")
ui.icon("check-circle-fill", lib="bs", size="1.25em", class_="text-success")
ui.icon("x-circle-fill", lib="bs", size="1.25em", class_="text-danger")

with ui.layout_columns():
with ui.card():
ui.card_header("Sizes")
ui.p("Semantic sizes (same scale for both libs):")
for sz in ("xs", "sm", "md", "lg", "xl", "2xl"):
ui.icon("circle", lib="bs", size=sz)
ui.br()
ui.p("CSS units:")
ui.icon("gear", size="1em")
ui.icon("gear", size="2em")
ui.icon("gear", size="3em")
ui.br()
ui.p("Numeric (px):")
ui.icon("gear", lib="bs", size=16)
ui.icon("gear", lib="bs", size=32)
ui.icon("gear", lib="bs", size=48)

with ui.card():
ui.card_header("Accessibility")
ui.p(
"Decorative (default) — hidden from screen readers. "
"Label the surrounding element instead:"
)
ui.input_action_button(
"btn_delete",
ui.icon("trash"),
aria_label="Delete item",
)
ui.br()
ui.br()
ui.p("Semantic — icon conveys meaning directly:")
ui.icon(
"circle-exclamation",
size="2em",
title="Warning",
a11y="semantic",
)
ui.icon(
"exclamation-triangle",
lib="bs",
size="2em",
title="Warning",
a11y="semantic",
)
ui.br()
ui.p("Semantic without title — label derived from icon name:")
ui.icon("heart-fill", lib="bs", a11y="semantic")

with ui.layout_columns():
with ui.card():
ui.card_header("Icons in Buttons")
ui.p("FontAwesome:")
ui.input_action_button("btn_fa", "Save", icon=ui.icon("floppy-disk"))
ui.input_action_button(
"btn_fa2", "Delete", icon=ui.icon("trash", fill="crimson")
)
ui.br()
ui.br()
ui.p("Bootstrap Icons:")
ui.input_action_button(
"btn_bs", "Save", icon=ui.icon("floppy", lib="bs")
)
ui.input_action_button(
"btn_bs2", "Delete", icon=ui.icon("trash", lib="bs")
)
2 changes: 2 additions & 0 deletions shiny/express/ui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
fill,
help_text,
hide_toast,
icon,
hover_opts,
include_css,
include_js,
Expand Down Expand Up @@ -217,6 +218,7 @@
"dblclick_opts",
"help_text",
"hover_opts",
"icon",
"include_css",
"include_js",
"input_action_button",
Expand Down
3 changes: 3 additions & 0 deletions shiny/ui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
)
from ._chat import Chat, chat_ui
from ._download_button import download_button, download_link
from ._icon import icon
from ._include_helpers import include_css, include_js
from ._input_action_button import input_action_button, input_action_link
from ._input_check_radio import (
Expand Down Expand Up @@ -230,6 +231,8 @@
# _download_button
"download_button",
"download_link",
# _icon
"icon",
# _plot_output_opts
"brush_opts",
"click_opts",
Expand Down
Loading