feat: Add ui.icon() for inline SVG icons (FontAwesome + Bootstrap)#2165
Draft
elnelson575 wants to merge 12 commits intomainfrom
Draft
feat: Add ui.icon() for inline SVG icons (FontAwesome + Bootstrap)#2165elnelson575 wants to merge 12 commits intomainfrom
elnelson575 wants to merge 12 commits intomainfrom
Conversation
Right now, py-shiny requires the faicons package in order to use icons in shiny functions. This gets irritating. We should support icons through a native shiny function. Reference the info in this function: #2159 It should support FontAwesome and Bootstrap Icons at minimum, potentially other icon libraries if they are popular enough. The function signature should be similar to https://github.com/rstudio/bsicons icon function signatures but more pythonic if appropriate.
elnelson575
commented
Feb 18, 2026
… docs - Add fa_icons.json (Font Awesome Free 6.x, 1852 icons) to git so the FA icon path works in clean checkouts - Fix numeric size test assertions to verify proper px units - Update example apps to demonstrate both FA and BS icons - Add update process documentation to _icon_data/__init__.py - Add CHANGELOG entry for ui.icon() - Delete stale ADR (decision superseded by implementation) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- BS icons with a11y="semantic" now always set aria-label: uses title if provided, otherwise derives it from the icon name (hyphens → spaces) and emits a warning suggesting an explicit title - Fix test assertions for numeric size to verify px units are present - Add comment noting FA positioning defaults match faicons.icon_svg() for drop-in migration compatibility Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…hrough - Add explicit `variant` parameter to icon() for FA style selection (replaces overloaded `style` kwarg) - Remove pre-extraction of stroke/fill_opacity/stroke_width/stroke_opacity; these now pass through as SVG presentation attributes on the element - Merge user-supplied `style` kwarg into generated CSS rather than overwriting - BS icons: same SVG attribute passthrough, simpler extraction Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…sistency - Add `icon` to shiny.express.ui exports (was missing — express example app crashed) - Fix double semicolon in FA style merge (css() already adds trailing semicolon) - Standardize aria-hidden to hyphenated form in _icon_bs (was using underscore) - Add warning when variant is passed with lib="bs" (was silently ignored) - Fix import to use public .css package boundary instead of private ._css_unit submodule - Remove dead code branches in FA size logic (size always sets both height and width) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implementation: - Remove html_escape() from title/aria-label construction in both FA and BS icons — htmltools escapes text and attribute values automatically on render; pre-escaping caused double-escaped output (e.g. &amp; in rendered HTML) Tests: - Merge redundant test_icon_bs_defaults_to_decorative into test_icon_bs_a11y_decorative - Replace 3 separate semantic-size tests with a single @pytest.mark.parametrize covering all 7 sizes (xs, sm, md, xl were previously untested) - Remove thin example-section tests that only asserted isinstance(icon, Tag) - Add gap coverage: BS variant warning, FA semantic a11y without title, FA title+decorative coexistence, FA/BS class_ parameter, FA style kwarg merge (no double semicolon), HTML special-char escaping in title/aria-label Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- FA: add brand variants (python, twitter), custom fill colors - BS: add custom fill, CSS class styling (text-warning/success/danger) - Sizes: add semantic sizes (xs→2xl) alongside CSS units and numeric px - A11y: show decorative pattern (label the container), semantic with title (both FA and BS), and semantic without title (derives label from icon name) - In-context: buttons with filled/colored icons Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…veAspectRatio Implementation: - Remove dead _parse_length_unit / _CSS_LENGTH_UNITS / re import (unused since size logic was simplified in an earlier commit) - Fix preserveAspectRatio guard: was always True (height/width always assigned); now only set when size is explicitly provided - FA semantic a11y without title now warns (matching BS behaviour); previously silently fell back to icon_data['label'] with no signal to the caller - Correct docstring: remove broken aria_label kwarg example (aria_label + aria-hidden='true' produces invalid ARIA); point callers to a11y='semantic' - Fix size type annotation on _icon_fa/_icon_bs: Optional[CssUnit] -> Optional[IconSize] to match public API signature - Add BS layout defaults (matching FA): margin-left:auto, margin-right:0.2em, position:relative, vertical-align:-0.125em, overflow:visible Tests: - Add test_icon_bs_default_margins, test_icon_bs_default_position, test_icon_bs_custom_margins - Add test_icon_fa_semantic_a11y_without_title_warns - Update test_icon_fa_semantic_a11y_without_title_uses_icon_label to assert warning is emitted Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2159
Summary
ui.icon()for rendering FontAwesome and Bootstrap Icons as inline SVGsfaiconsdependency)icon(name, *, lib="fa", size=None, fill=None, class_=None, id=None, variant=None, **kwargs)lib:"fa"(default) or"bs"variant: selects FontAwesome style ("solid","regular","brands", etc.)size: semantic sizes ("xs","sm","lg","xl","2xl", etc.) or any CSS unit ("2em","24px")stroke,fill_opacity, etc.) pass through via**kwargsaria-hidden) and semantic (aria-label) modesfaicons.icon_svg()— same layout defaults (margin_left,margin_right,position)Test plan
tests/pytest/test_icon.pycovers BS and FA icons, all params, a11y modes, unknown icon errors, semantic sizes, stroke/fill attrsshiny/api-examples/icon/app-core.pyandapp-express.pyui.icon("star")works in both core and express appsui.icon("github", variant="brands")renders correctlyui.icon("star", lib="bs", variant="solid")emits a warning🤖 Generated with Claude Code