Repository: Appsilon/py_shiny_semantic (language: python)

Issue:
[General] Migrate examples to a separate repository
- [ ] Add a pull_request_template that should remind to update the components-demo application in examples repo.
- [ ] Add a link to deployed applications to the README


Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index 48e2412..bbb0ff5 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,15 @@
 
 _Create rich web applications in PyShiny using styles and components from Semantic UI._
 
+## Demo applications and examples
+
+- Semantic Components Showcase
+  - [Live Demo](https://connect.appsilon.com/py_shiny_semantic/)
+  - [Source Code](https://github.com/Appsilon/py_shiny_semantic_examples/tree/main/semantic-components)
+- T-Test Simulation Clone
+  - [Live Demo](https://connect.appsilon.com/py_shiny_ttest)
+  - [Source Code](https://github.com/Appsilon/py_shiny_semantic_examples/tree/main/semantic-ttest)
+
 ## About the project
 
 This is an early [Shiny-for-Python](https://shiny.rstudio.com/py/) implementation of [the official community fork of Semantic UI](https://fomantic-ui.com/).
diff --git a/example/__init__.py b/example/__init__.py
deleted file mode 100644
index 064ec0c..0000000
--- a/example/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from shiny import App
-
-from .main import app_server, app_ui
-
-app = App(app_ui, app_server)
diff --git a/example/_app_layout.py b/example/_app_layout.py
deleted file mode 100644
index 92f1ba2..0000000
--- a/example/_app_layout.py
+++ /dev/null
@@ -1,77 +0,0 @@
-from shiny.ui import tags
-
-from shiny_semantic.elements import container, segment
-
-
-def header():
-    onclick_callback = """
-        $('.ui.sidebar')
-            .sidebar({
-                transition: 'overlay',
-                dimPage: true,
-                blurring: true,
-            })
-            .sidebar('toggle');
-    """
-
-    return (
-        tags.div(
-            tags.div(
-                "Semantic UI + Shiny for Python",
-                class_="ui header item",
-            ),
-            tags.div(
-                tags.i(class_="hamburger icon"),
-                class_="ui basic icon button item",
-                onclick=onclick_callback,
-            ),
-            tags.div(
-                "Built by Appsilon",
-                class_="right floating item",
-            ),
-            class_="ui top fixed menu",
-            style="z-index: 103;",
-        ),
-    )
-
-
-def hero():
-    title = tags.h1(
-        "Shiny Semantic: Components Demo",
-        class_="ui inverted header",
-        style="margin-block: 5em;",
-    )
-
-    hero = segment(
-        container(title, class_="text"),
-        class_="inverted vertical masthead center aligned",
-    )
-    return hero
-
-
-def sidebar():
-    def _link(item):
-        return tags.a(
-            item,
-            href=f"#{item}",
-            class_="item",
-            onclick="$('.ui.sidebar').sidebar('toggle');",
-        )
-
-    return (
-        tags.div(
-            _link("Button"),
-            _link("Emoji"),
-            _link("Flag"),
-            _link("Icon"),
-            _link("Header"),
-            _link("Input"),
-            _link("Modal"),
-            _link("Slider"),
-            _link("Dropdown"),
-            _link("Checkbox"),
-            _link("Statistic"),
-            class_="ui left vertical menu inverted sidebar",
-            style="padding-top: 4rem;",
-        ),
-    )
diff --git a/example/main.py b/example/main.py
deleted file mode 100644
index fadde17..0000000
--- a/example/main.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from shiny.ui import tags
-
-from shiny_semantic import page_semantic
-
-from . import modules
-from ._app_layout import header, hero, sidebar
-
-app_ui = page_semantic(
-    header(),
-    sidebar(),
-    tags.div(
-        hero(),
-        modules.button.ui("button_section"),
-        modules.emoji.ui("emoji_section"),
-        modules.flag.ui("flag_section"),
-        modules.header.ui("header_section"),
-        modules.icon.ui("icon_section"),
-        modules.inputs.ui("input_section"),
-        modules.modal.ui("modal_section"),
-        modules.slider.ui("slider_section"),
-        modules.dropdown.ui("dropdown_section"),
-        modules.checkbox.ui("checkbox_section"),
-        modules.statistic.ui("statistic_section"),
-        tags.div(style="opacity: 0; height: 5rem;"),
-    ),
-    title="Shiny Semantic",
-)
-
-
-def app_server(input, output, session):
-    modules.button.server("button_section")
-    modules.emoji.server("emoji_section")
-    modules.flag.server("flag_section")
-    modules.header.server("header_section")
-    modules.icon.server("icon_section")
-    modules.inputs.server("input_section")
-    modules.modal.server("modal_section")
-    modules.slider.server("slider_section")
-    modules.dropdown.server("dropdown_section")
-    modules.checkbox.server("checkbox_section")
-    modules.statistic.server("statistic_section")
diff --git a/example/modules/__init__.py b/example/modules/__init__.py
deleted file mode 100644
index 00d536d..0000000
--- a/example/modules/__init__.py
+++ /dev/null
@@ -1,25 +0,0 @@
-__all__ = (
-    "button",
-    "checkbox",
-    "dropdown",
-    "emoji",
-    "flag",
-    "header",
-    "icon",
-    "inputs",
-    "modal",
-    "slider",
-    "statistic",
-)
-
-from . import button_module as button
-from . import checkbox_module as checkbox
-from . import dropdown_module as dropdown
-from . import emoji_module as emoji
-from . import flag_module as flag
-from . import header_module as header
-from . import icon_module as icon
-from . import input_module as inputs
-from . import modal_module as modal
-from . import slider_module as slider
-from . import statistic_module as statistic
diff --git a/example/modules/_feature_layout.py b/example/modules/_feature_layout.py
deleted file mode 100644
index e984c2a..0000000
--- a/example/modules/_feature_layout.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from shiny.ui import TagList, h2, h3, tags
-
-from shiny_semantic.elements import segment
-
-
-def feature_section(title: str, *args):
-    return TagList(
-        tags.div(id=title, style="opacity: 0; margin-bottom: 5rem;"),
-        segment(
-            h2(title, class_="ui centered header"),
-            *args,
-            class_="raised padded container",
-        ),
-    )
-
-
-def feature_subsection(title: str, *args):
-    return TagList(
-        h3(title, class_="ui header"),
-        *args,
-    )
diff --git a/example/modules/button_module.py b/example/modules/button_module.py
deleted file mode 100644
index d69c473..0000000
--- a/example/modules/button_module.py
+++ /dev/null
@@ -1,108 +0,0 @@
-from random import randint
-
-from shiny import module, reactive, render
-from shiny.ui import output_text, tags
-
-from shiny_semantic.elements import button, icon, update_button
-
-from ._feature_layout import feature_section, feature_subsection
-
-
-@module.ui
-def ui():
-    return (
-        feature_section(
-            "Button",
-            feature_subsection(
-                "Shiny Bound Inputs",
-                button("button", "Click me"),
-                tags.span(
-                    "Clicks:",
-                    output_text("n_clicks", True),
-                    class_="item",
-                ),
-                button(
-                    "update",
-                    "Update other btn's label",
-                    icon=icon("arrow left"),
-                    class_="right floated",
-                ),
-            ),
-            feature_subsection(
-                "Button Types",
-                button("btn1", "Default"),
-                button("btn2", "Primary", class_="primary"),
-                button("btn3", "Secondary", class_="secondary"),
-                button("btn4", "Basic", class_="basic"),
-                button("btn5", "Tertiary", class_="tertiary"),
-                button("btn6", "Icon", icon=icon("pause")),
-            ),
-            feature_subsection(
-                "Button Colors",
-                button("btn7", "Positive", class_="positive"),
-                button("btn8", "Negative", class_="negative"),
-                button("btn9", "Purple", class_="purple"),
-                button("btn10", "Olive", class_="olive"),
-                button("btn11", "Orange", class_="basic orange"),
-                button("btn12", "Teal", class_="tertiary teal"),
-            ),
-            feature_subsection(
-                "Button Sizes",
-                button("btn13", "Mini", class_="mini"),
-                button("btn14", "Tiny", class_="tiny"),
-                button("btn15", "Small", class_="small"),
-                button("btn16", "Medium", class_="medium"),
-                button("btn17", "Large", class_="large"),
-                button("btn18", "Big", class_="big"),
-            ),
-            feature_subsection(
-                "Button Social",
-                button(
-                    "btn19",
-                    "Facebook",
-                    icon=icon("facebook"),
-                    class_="facebook",
-                ),
-                button(
-                    "btn20",
-                    "Linkedin",
-                    icon=icon("linkedin"),
-                    class_="linkedin",
-                ),
-                button(
-                    "btn21",
-                    "Instagram",
-                    icon=icon("instagram"),
-                    class_="instagram",
-                ),
-                button(
-                    "btn22",
-                    icon=icon("whatsapp"),
-                    class_="circular whatsapp icon",
-                ),
-                button(
-                    "btn23",
-                    icon=icon("telegram"),
-                    class_="circular telegram icon",
-                ),
-                button(
-                    "btn24",
-                    icon=icon("twitter"),
-                    class_="circular twitter icon",
-                ),
-            ),
-        ),
-    )
-
-
-@module.server
-def server(input, output, session):
-    @output(id="n_clicks")
-    @render.text
-    def _():
-        return input.button()
-
-    @reactive.Effect
-    @reactive.event(input.update)
-    def _():
-        update_button("button", label=str(randint(0, 10)))
diff --git a/example/modules/checkbox_module.py b/example/modules/checkbox_module.py
deleted file mode 100644
index f765185..0000000
--- a/example/modules/checkbox_module.py
+++ /dev/null
@@ -1,131 +0,0 @@
-import random
-import string
-
-from shiny import module, reactive, render
-from shiny.ui import output_text_verbatim
-
-from shiny_semantic.elements import button, header, icon
-from shiny_semantic.modules import (
-    checkbox,
-    checkbox_group,
-    update_checkbox,
-    update_checkbox_group,
-)
-
-from ._feature_layout import feature_section, feature_subsection
-
-
-@module.ui
-def ui():
-    return feature_section(
-        "Checkbox",
-        feature_subsection(
-            "Different types",
-            checkbox("checkbox", "Checkbox", value=True),
-            output_text_verbatim("checkbox_out"),
-            checkbox("toggle", "Toggle", type="toggle"),
-            output_text_verbatim("toggle_out"),
-            checkbox("slider", "Slider", type="slider"),
-            output_text_verbatim("slider_out"),
-        ),
-        feature_subsection(
-            "Checkbox group",
-            checkbox_group(
-                id="group",
-                label="Slider group with custom values",
-                choices={"a": "One", "b": "Two", "c": "Three"},
-                selected="a",
-                type="slider",
-            ),
-            output_text_verbatim("group_out"),
-            checkbox_group(
-                id="group_radio",
-                label="Inline radio group",
-                choices=["One", "Two", "Three"],
-                type="radio",
-                position="inline",
-            ),
-            output_text_verbatim("group_radio_out"),
-        ),
-        feature_subsection(
-            "Server-side updates",
-            checkbox("checkbox_to_update", "To be updated", type="toggle"),
-            button(
-                "update_checkbox",
-                "Update label and value",
-                icon=icon("arrow left"),
-                class_="right floated",
-            ),
-            output_text_verbatim("update_single_out"),
-            checkbox_group(
-                id="group_to_update",
-                label="Group to be Updated",
-                choices=["hello", "world"],
-                selected=["hello"],
-            ),
-            header("Click each button many times!", class_="small"),
-            button("update_group_labels", "Random labels"),
-            button("update_group_values", "Random values"),
-            button("update_group_group_label", "Random group label"),
-        ),
-    )
-
-
-@module.server
-def server(input, output, session):
-    @output(id="checkbox_out")
-    @render.text
-    def _():
-        return input.checkbox()
-
-    @output(id="toggle_out")
-    @render.text
-    def _():
-        return input.toggle()
-
-    @output(id="slider_out")
-    @render.text
-    def _():
-        return input.slider()
-
-    @output(id="group_out")
-    @render.text
-    def _():
-        return input.group()
-
-    @output(id="group_radio_out")
-    @render.text
-    def _():
-        return input.group_radio()
-
-    @output(id="update_single_out")
-    @render.text
-    def _():
-        return input.checkbox_to_update()
-
-    @reactive.Effect
-    @reactive.event(input.update_checkbox)
-    def _():
-        sample = random.choices(string.ascii_uppercase, k=6)
-        label = "".join(sample)
-        value = not input.checkbox_to_update()
-        update_checkbox("checkbox_to_update", label=label, value=value)
-
-    @reactive.Effect
-    @reactive.event(input.update_group_labels)
-    def _():
-        labels = random.choices(string.ascii_letters, k=2)
-        update_checkbox_group("group_to_update", labels=labels)
-
-    @reactive.Effect
-    @reactive.event(input.update_group_values)
-    def _():
-        values = random.choices([True, False], k=2)
-        update_checkbox_group("group_to_update", values=values)
-
-    @reactive.Effect
-    @reactive.event(input.update_group_group_label)
-    def _():
-        sample = random.choices(string.ascii_letters, k=12)
-        group_label = "".join(sample)
-        update_checkbox_group("group_to_update", group_label=group_label)
diff --git a/example/modules/dropdown_module.py b/example/modules/dropdown_module.py
deleted file mode 100644
index ee97479..0000000
--- a/example/modules/dropdown_module.py
+++ /dev/null
@@ -1,71 +0,0 @@
-import random
-import string
-
-from shiny import module, reactive, render
-from shiny.ui import output_text_verbatim
-
-from shiny_semantic.elements import button, icon
-from shiny_semantic.modules import dropdown, input_select, update_select
-
-from ._feature_layout import feature_section, feature_subsection
-
-
-@module.ui
-def ui():
-    return feature_section(
-        "Dropdown",
-        feature_subsection(
-            "Selection",
-            input_select(
-                id="selection",
-                label="Dropdown Label",
-                placeholder="Select",
-                choices=["One", "Two", "Three"],
-            ),
-            button(
-                "update",
-                "Update label & choices",
-                icon=icon("up arrow"),
-                class_="right floated",
-            ),
-            output_text_verbatim("selection_out"),
-        ),
-        feature_subsection(
-            "Customizable Selection",
-            dropdown(
-                id="selection_custom",
-                placeholder="Fluid + Clearable + Searchable + Multiple",
-                choices=["hello", "world"],
-                value=["hello", "world"],
-                settings={
-                    "clearable": True,
-                },
-                class_="multiple search",
-            ),
-            output_text_verbatim("selection_custom_out"),
-        ),
-    )
-
-
-@module.server
-def server(input, output, session):
-    @output(id="selection_out")
-    @render.text
-    def _():
-        return input.selection()
-
-    @output(id="selection_custom_out")
-    @render.text
-    def _():
-        return input.selection_custom()
-
-    @reactive.Effect
-    @reactive.event(input.update)
-    def _():
-        choices = random.choices(population=string.ascii_uppercase, k=5)
-        label = "".join(choices)
-        update_select(
-            "selection",
-            choices=choices,
-            label=label,
-        )
diff --git a/example/modules/emoji_module.py b/example/modules/emoji_module.py
deleted file mode 100644
index 3ed27ec..0000000
--- a/example/modules/emoji_module.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from shiny import module
-
-from shiny_semantic.elements import emoji
-
-from ._feature_layout import feature_section, feature_subsection
-
-
-@module.ui
-def ui():
-    return feature_section(
-        "Emoji",
-        feature_subsection(
-            "Different sets",
-            emoji(emoji_name="smile"),
-            emoji(emoji_name="angry"),
-            emoji(emoji_name="bee"),
-            emoji(emoji_name="blowfish"),
-            emoji(emoji_name="apple"),
-            emoji(emoji_name="avocado"),
-            emoji(emoji_name="badminton"),
-            emoji(emoji_name="dart"),
-            emoji(emoji_name="airplane"),
-        ),
-        feature_subsection(
-            "Skin tones",
-            emoji("adult"),
-            emoji("adult_tone1"),
-            emoji("adult_tone2"),
-            emoji("adult_tone3"),
-            emoji("adult_tone4"),
-            emoji("adult_tone5"),
-        ),
-        feature_subsection(
-            "Sizes",
-            emoji("leo", class_="tiny"),
-            emoji("leo", class_="small"),
-            emoji("leo", class_="medium"),
-            emoji("leo", class_="large"),
-            emoji("leo", class_="big"),
-        ),
-    )
-
-
-@module.server
-def server(input, output, session):
-    pass
diff --git a/example/modules/flag_module.py b/example/modules/flag_module.py
deleted file mode 100644
index b0b235d..0000000
--- a/example/modules/flag_module.py
+++ /dev/null
@@ -1,36 +0,0 @@
-from shiny import module
-
-from shiny_semantic.elements import flag
-
-from ._feature_layout import feature_section, feature_subsection
-
-
-@module.ui
-def ui():
-    return feature_section(
-        "Flag",
-        feature_subsection(
-            "Countries specified with full name",
-            flag("france"),
-            flag("germany"),
-        ),
-        feature_subsection(
-            "Countries specified with ISO code",
-            flag("fr"),
-            flag("de"),
-        ),
-        feature_subsection(
-            "Flag sizes",
-            flag("un", class_="small"),
-            flag("un", class_="medium"),
-            flag("un", class_="large"),
-            flag("un", class_="big"),
-            flag("un", class_="huge"),
-            flag("un", class_="massive"),
-        ),
-    )
-
-
-@module.server
-def server(input, output, session):
-    pass
diff --git a/example/modules/header_module.py b/example/modules/header_module.py
deleted file mode 100644
index 53f17b3..0000000
--- a/example/modules/header_module.py
+++ /dev/null
@@ -1,61 +0,0 @@
-from shiny import module
-from shiny.ui import tags
-
-from shiny_semantic.elements import header, icon, subheader
-
-from ._feature_layout import feature_section, feature_subsection
-
-
-@module.ui
-def ui():
-    return feature_section(
-        "Header",
-        feature_subsection(
-            "Header Sizes",
-            header("Huge Header", class_="huge"),
-            header("Large Header", class_="large"),
-            header("Medium Header", class_="medium"),
-            header("Small Header", class_="small"),
-            header("Tiny Header", class_="tiny"),
-        ),
-        feature_subsection(
-            "Icon Header",
-            header(
-                icon("settings"),
-                "Account settings",
-                class_="big icon",
-            ),
-            header(
-                icon("users", class_="circular"),
-                "Friends",
-                class_="medium icon",
-            ),
-            header(
-                icon("plug"),
-                "Uptime Guarantee",
-                class_="tiny",
-            ),
-        ),
-        feature_subsection(
-            "Subheader",
-            header(
-                "Account Settings",
-                subheader("Manage your account settings"),
-                class_="medium",
-            ),
-            header(
-                icon("settings"),
-                tags.div(
-                    "Account Settings",
-                    subheader("Manage your preferences"),
-                    class_="content",
-                ),
-                class_="medium",
-            ),
-        ),
-    )
-
-
-@module.server
-def server(input, output, session):
-    pass
diff --git a/example/modules/icon_module.py b/example/modules/icon_module.py
deleted file mode 100644
index 4a24ced..0000000
--- a/example/modules/icon_module.py
+++ /dev/null
@@ -1,52 +0,0 @@
-from shiny import module
-
-from shiny_semantic.elements import icon
-
-from ._feature_layout import feature_section, feature_subsection
-
-
-@module.ui
-def ui():
-    return feature_section(
-        "Icon",
-        feature_subsection(
-            "Various icon sets",
-            icon("users"),
-            icon("wheelchair"),
-            icon("file"),
-            icon("map"),
-        ),
-        feature_subsection(
-            "Loading state",
-            icon("spinner", class_="loading"),
-            icon("notched circle", class_="loading"),
-            icon("asterisk", class_="loading"),
-        ),
-        feature_subsection(
-            "Size",
-            icon("home", class_="mini"),
-            icon("home", class_="tiny"),
-            icon("home", class_="small"),
-            icon("home"),
-            icon("home", class_="large"),
-            icon("home", class_="big"),
-            icon("home", class_="huge"),
-            icon("home", class_="massive"),
-        ),
-        feature_subsection(
-            "Shape and color",
-            icon("home", class_="circular"),
-            icon("home", class_="circular teal"),
-            icon("home", class_="circular inverted teal"),
-            icon("home", class_="circular colored teal"),
-            icon("home", class_="bordered"),
-            icon("home", class_="bordered teal"),
-            icon("home", class_="bordered inverted teal"),
-            icon("home", class_="bordered colored teal"),
-        ),
-    )
-
-
-@module.server
-def server(input, output, session):
-    pass
diff --git a/example/modules/input_module.py b/example/modules/input_module.py
deleted file mode 100644
index c7effac..0000000
--- a/example/modules/input_module.py
+++ /dev/null
@@ -1,73 +0,0 @@
-from shiny import module, render
-from shiny.ui import output_text, tags
-
-from shiny_semantic.elements import icon, semantic_input
-
-from ._feature_layout import feature_section, feature_subsection
-
-
-@module.ui
-def ui():
-    return feature_section(
-        "Input",
-        feature_subsection(
-            "Shiny Bound Text Input",
-            semantic_input("text", placeholder="Text input"),
-            tags.span("Text:"),
-            output_text("text_output", inline=True),
-            tags.br(),
-            tags.br(),
-            semantic_input(
-                "numeric",
-                placeholder="Numeric input with Props",
-                type="number",
-                value=0,
-                min=-10,
-                max=10,
-                step=10,
-            ),
-            tags.span("Numeric input with Props, Value:"),
-            output_text("numeric_output", inline=True),
-        ),
-        feature_subsection(
-            "States and Variations",
-            semantic_input("disabled", placeholder="Disabled", semantic_class="disabled"),
-            semantic_input("error", placeholder="Error", semantic_class="error"),
-            semantic_input("date", placeholder="Date", type="date"),
-            semantic_input("email", placeholder="Email", type="email"),
-            semantic_input("password", placeholder="Password", type="password"),
-            semantic_input("icon", placeholder="With Icon", icon=icon("users")),
-            semantic_input(
-                "label",
-                placeholder="appsilon.com",
-                semantic_label="https://",
-            ),
-            semantic_input("value", placeholder="Pre-populated", value="Some text"),
-            semantic_input("numeric", placeholder="Numeric", type="number"),
-        ),
-        feature_subsection(
-            "Sizes",
-            semantic_input("mini", placeholder="Mini", semantic_class="mini"),
-            semantic_input("tiny", placeholder="Tiny", semantic_class="tiny"),
-            semantic_input("small", placeholder="Small", semantic_class="small"),
-            semantic_input("medium", placeholder="Medium", semantic_class="medium"),
-            semantic_input("large", placeholder="Large", semantic_class="large"),
-            semantic_input("big", placeholder="Big", semantic_class="big"),
-            semantic_input("huge", placeholder="Huge", semantic_class="huge"),
-            semantic_input("massive", placeholder="Massive", semantic_class="massive"),
-            semantic_input("fluid", placeholder="Fluid", semantic_class="fluid"),
-        ),
-    )
-
-
-@module.server
-def server(input, output, session):
-    @output(id="text_output")
-    @render.text
-    def _():
-        return input.text()
-
-    @output(id="numeric_output")
-    @render.text
-    def _():
-        return input.numeric()
diff --git a/example/modules/modal_module.py b/example/modules/modal_module.py
deleted file mode 100644
index bbb9e22..0000000
--- a/example/modules/modal_module.py
+++ /dev/null
@@ -1,117 +0,0 @@
-from shiny import module, reactive, render
-from shiny.ui import output_text, tags
-
-from shiny_semantic.elements import button
-from shiny_semantic.modules import modal, modal_show
-
-from ._feature_layout import feature_section, feature_subsection
-
-
-@module.ui
-def ui():
-    return feature_section(
-        "Modal",
-        feature_subsection(
-            "Default modal",
-            button("default_modal", "Open Default Modal"),
-            tags.div("Modal result:", output_text("default_out", inline=True)),
-        ),
-        feature_subsection(
-            "Basic modal",
-            button("basic_modal", "Open Basic Modal", class_="secondary"),
-            tags.div("Modal result:", output_text("basic_out", inline=True)),
-        ),
-        feature_subsection(
-            "With custom props",
-            button("props_modal", "Open Modal", class_="teal"),
-            tags.div("Clicks:", output_text("props_out", inline=True)),
-        ),
-    )
-
-
-@module.server
-def server(input, output, session):
-    @reactive.Effect
-    @reactive.event(input.default_modal)
-    async def _():
-        await modal_show(
-            modal_ui=modal(
-                id="default_modal_ui",
-                header="Hello World",
-                content="Lorem Ipsum",
-            ),
-            shiny_input="default_modal_result",
-            session=session,
-        )
-
-    @reactive.Effect
-    @reactive.event(input.basic_modal)
-    async def _():
-        await modal_show(
-            modal_ui=modal(
-                id="basic_modal_ui",
-                header="Hello World",
-                content="Lorem Ipsum",
-                class_="basic",
-            ),
-            shiny_input="basic_modal_result",
-            session=session,
-        )
-
-    @reactive.Effect
-    @reactive.event(input.props_modal)
-    async def _():
-        await modal_show(
-            modal_ui=modal(
-                id="props_modal_ui",
-                header=tags.h3("Placeholder content"),
-                content=tags.div(
-                    tags.div(class_="square image"),
-                    tags.div(class_="line"),
-                    tags.div(class_="line"),
-                    tags.div(class_="line"),
-                    class_="ui placeholder",
-                ),
-                actions=tags.div(
-                    button(
-                        session.ns("modal_test"),
-                        "Click",
-                        class_="left floated",
-                    ),
-                    button(
-                        session.ns("modal_dismiss"),
-                        "Dismiss",
-                        class_="approve secondary",
-                    ),
-                ),
-                class_="mini",
-            ),
-            shiny_input="props_modal_result",
-            modal_props={
-                "closable": False,
-                "inverted": True,
-                "blurring": True,
-                "transition": {
-                    "showMethod": "fade down",
-                    "showDuration": 600,
-                    "hideMethod": "fade up",
-                    "hideDuration": 600,
-                },
-            },
-            session=session,
-        )
-
-    @output(id="default_out")
-    @render.text
-    def _():
-        return input.default_modal_result()
-
-    @output(id="basic_out")
-    @render.text
-    def _():
-        return input.basic_modal_result()
-
-    @output(id="props_out")
-    @render.text
-    def _():
-        return input.modal_test()
diff --git a/example/modules/slider_module.py b/example/modules/slider_module.py
deleted file mode 100644
index 7e6f273..0000000
--- a/example/modules/slider_module.py
+++ /dev/null
@@ -1,83 +0,0 @@
-import random
-
-from shiny import module, reactive, render
-from shiny.ui import output_text_verbatim
-
-from shiny_semantic.elements import button, icon
-from shiny_semantic.modules import slider, update_slider
-
-from ._feature_layout import feature_section, feature_subsection
-
-
-@module.ui
-def ui():
-    return feature_section(
-        "Slider",
-        feature_subsection(
-            "Simple",
-            slider(
-                id="slider",
-                min_value=0,
-                max_value=10,
-                start_value=5,
-            ),
-            output_text_verbatim("slider_out"),
-            button("update_value", "Update value", icon=icon("arrow up")),
-        ),
-        feature_subsection(
-            "Range",
-            slider(
-                id="slider_range",
-                min_value=-10,
-                max_value=10,
-                start_value=1,
-                end_value=4,
-            ),
-            output_text_verbatim("slider_range_out"),
-            button("update_range", "Update Range", icon=icon("arrow up")),
-        ),
-        feature_subsection(
-            "Custom",
-            slider(
-                id="slider_custom",
-                min_value=1,
-                max_value=5,
-                start_value=3,
-                step=1,
-                show_ticks=False,
-                show_labels=True,
-                custom_labels=["A", "B", "C", "D", "E", "F"],
-                class_="big red",
-            ),
-            output_text_verbatim("slider_custom_out"),
-        ),
-    )
-
-
-@module.server
-def server(input, output, session):
-    @output(id="slider_out")
-    @render.text
-    def _():
-        return input.slider()
-
-    @output(id="slider_range_out")
-    @render.text
-    def _():
-        return input.slider_range()
-
-    @output(id="slider_custom_out")
-    @render.text
-    def _():
-        return input.slider_custom()
-
-    @reactive.Effect
-    @reactive.event(input.update_value)
-    def _():
-        update_slider("slider", value=random.randint(0, 10))
-
-    @reactive.Effect
-    @reactive.event(input.update_range)
-    def _():
-        value = [random.randint(-10, 0), random.randint(0, 10)]
-        update_slider("slider_range", value=value)
diff --git a/example/modules/statistic_module.py b/example/modules/statistic_module.py
deleted file mode 100644
index 11017ef..0000000
--- a/example/modules/statistic_module.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from htmltools import TagList
-from shiny import module
-
-from shiny_semantic.elements import icon
-from shiny_semantic.views import statistic
-
-from ._feature_layout import feature_section, feature_subsection
-
-
-@module.ui
-def ui():
-    return feature_section(
-        "Statistic",
-        feature_subsection(
-            "Value first",
-            statistic(123, "subscribers"),
-        ),
-        feature_subsection(
-            "Label first",
-            statistic(123, "subscribers", value_first=False),
-        ),
-        feature_subsection(
-            "Arbitrary content",
-            statistic(
-                TagList(
-                    icon("globe americas"),
-                    "SEVEN",
-                    icon("globe asia"),
-                ),
-                "Continents on Earth",
-                class_="brown",
-            ),
-        ),
-    )
-
-
-@module.server
-def server(input, output, session):
-    pass
diff --git a/manifest.json b/manifest.json
deleted file mode 100644
index 282f022..0000000
--- a/manifest.json
+++ /dev/null
@@ -1,261 +0,0 @@
-{
-  "version": 1,
-  "locale": "en_US.UTF-8",
-  "metadata": {
-    "appmode": "python-shiny",
-    "entrypoint": "example"
-  },
-  "python": {
-    "version": "3.10.9",
-    "package_manager": {
-      "name": "pip",
-      "version": "22.3.1",
-      "package_file": "requirements.txt"
-    }
-  },
-  "files": {
-    "requirements.txt": {
-      "checksum": "9fa8f7a014c9aa227c63e228afacc23b"
-    },
-    ".github/workflows/unit-test.yml": {
-      "checksum": "bf45b6618fcd1db653dbfda64677914f"
-    },
-    ".gitignore": {
-      "checksum": "a611d760efaceaadf76feaa1b2009f72"
-    },
-    ".pre-commit-config.yaml": {
-      "checksum": "54253533a8b22786f73f8c4a31a46216"
-    },
-    ".vscode/extensions.json": {
-      "checksum": "3a542a0e563631d736f764113b4284cc"
-    },
-    ".vscode/settings.json": {
-      "checksum": "ebb09f6f9bc4ab412466ed7927eeefe4"
-    },
-    "LICENSE": {
-      "checksum": "599e523d7dbcc10c1b62ad3027e73d3f"
-    },
-    "MANIFEST.in": {
-      "checksum": "9a6b9e47a907a4b581a7d3167857e0dd"
-    },
-    "README.md": {
-      "checksum": "74b11824163489e6e16a4e9b0bcd1649"
-    },
-    "example/__init__.py": {
-      "checksum": "a0ff8d91e770939faa052a8087c7776a"
-    },
-    "example/_app_layout.py": {
-      "checksum": "9f3d8d4d349ce03e650e2053218ce3a4"
-    },
-    "example/main.py": {
-      "checksum": "312f40beb93ed0b8179dbc9e5a601d07"
-    },
-    "example/modules/__init__.py": {
-      "checksum": "af959801ae3f0fb76b392edb8ff3b155"
-    },
-    "example/modules/_feature_layout.py": {
-      "checksum": "a0aff897a1b373f93d580a0b23bb031c"
-    },
-    "example/modules/button_module.py": {
-      "checksum": "144e6720d5a6c7072a9648150b4b910a"
-    },
-    "example/modules/checkbox_module.py": {
-      "checksum": "5dffb99a3b83272c92fd91f2f4ca21b6"
-    },
-    "example/modules/dropdown_module.py": {
-      "checksum": "549ddb87d696d9f7b0cfa3834cb992da"
-    },
-    "example/modules/emoji_module.py": {
-      "checksum": "5d36cf40f6939074ba1fc757c8ebd11d"
-    },
-    "example/modules/flag_module.py": {
-      "checksum": "97c3336d212751c0aeb6aedfa1db3cd0"
-    },
-    "example/modules/header_module.py": {
-      "checksum": "0ab823fb8126b9d04d16b7773b8efe33"
-    },
-    "example/modules/icon_module.py": {
-      "checksum": "9b77dbda2bcf22dadf7e18540e3e8452"
-    },
-    "example/modules/input_module.py": {
-      "checksum": "ffa219af34e57a06fe549730c18ef11f"
-    },
-    "example/modules/modal_module.py": {
-      "checksum": "5de888f920845ef2eeba3a87a2fd2a6d"
-    },
-    "example/modules/slider_module.py": {
-      "checksum": "8d721f7c71f3000be537a628e5e5fe9e"
-    },
-    "example/modules/statistic_module.py": {
-      "checksum": "f3fdc7bd140abb4af9e139661842e3b2"
-    },
-    "pyproject.toml": {
-      "checksum": "c770e13e6a397e22741072ab8c0e062e"
-    },
-    "shiny_semantic/.DS_Store": {
-      "checksum": "ab7eef11c467acd05a2f07d6b5ef3493"
-    },
-    "shiny_semantic/__init__.py": {
-      "checksum": "a9b4b7efe8764e10c1696a30f03eb09a"
-    },
-    "shiny_semantic/_utils.py": {
-      "checksum": "9718fbbbe4a1cd191e769e089e394776"
-    },
-    "shiny_semantic/collections/__init__.py": {
-      "checksum": "d1377e22c4c857ed188046d451ea13cd"
-    },
-    "shiny_semantic/elements/__init__.py": {
-      "checksum": "80daaa4cb3dac31bdb4f8ac2e01c8b25"
-    },
-    "shiny_semantic/elements/button.py": {
-      "checksum": "a07825247835170d37c9c35033cb22bd"
-    },
-    "shiny_semantic/elements/container.py": {
-      "checksum": "7c09249a85fdbd34dfdb3e4238add0cd"
-    },
-    "shiny_semantic/elements/divider.py": {
-      "checksum": "b9926506cb50a3b98694f4a559f39ce0"
-    },
-    "shiny_semantic/elements/emoji.py": {
-      "checksum": "91625605fc1d2bd3fb8ff6a483d509fb"
-    },
-    "shiny_semantic/elements/flag.py": {
-      "checksum": "86942de0940cb35b217a6ab7a5405857"
-    },
-    "shiny_semantic/elements/header.py": {
-      "checksum": "6515d364e7e3c1fdd5a4efc728c223c3"
-    },
-    "shiny_semantic/elements/icon.py": {
-      "checksum": "2137ebbb7b2b369a14a4865c6375551b"
-    },
-    "shiny_semantic/elements/input.py": {
-      "checksum": "8dbdfc94173768e2aca3b1b9311726d3"
-    },
-    "shiny_semantic/elements/segment.py": {
-      "checksum": "fc606e5f62a9c537568ac4a210d1bd67"
-    },
-    "shiny_semantic/modules/__init__.py": {
-      "checksum": "e0f9f65e94a2a056987a93fcc8445287"
-    },
-    "shiny_semantic/modules/checkbox.py": {
-      "checksum": "b2c804231057c4e0c604f1cbda2621aa"
-    },
-    "shiny_semantic/modules/dropdown.py": {
-      "checksum": "3f86ddd448879d9d01574e388c311a04"
-    },
-    "shiny_semantic/modules/modal.py": {
-      "checksum": "c6304244d10308945f90723c85827757"
-    },
-    "shiny_semantic/modules/slider.py": {
-      "checksum": "46ba817da76c7eb9f068ae17bb50167a"
-    },
-    "shiny_semantic/page.py": {
-      "checksum": "f7cbfc3ebd6efe1a8f945ac8416173f6"
-    },
-    "shiny_semantic/types/__init__.py": {
-      "checksum": "f946017eab011e7874d6a82275bf0377"
-    },
-    "shiny_semantic/views/__init__.py": {
-      "checksum": "c37041a125c6aa89c81aee3ff86f2060"
-    },
-    "shiny_semantic/views/statistic.py": {
-      "checksum": "1d20854be83d135973495da1aa3373aa"
-    },
-    "shiny_semantic/www/.DS_Store": {
-      "checksum": "194577a7e20bdcc7afbb718f502c134c"
-    },
-    "shiny_semantic/www/bindings/semanticButton.js": {
-      "checksum": "2d51628f7a0bf8382d84ac50583ab20c"
-    },
-    "shiny_semantic/www/bindings/semanticCheckbox.js": {
-      "checksum": "76d8bdfe772191cd8aaf64dfd5b6b26a"
-    },
-    "shiny_semantic/www/bindings/semanticCheckboxGroup.js": {
-      "checksum": "890e0fe9d6f9d3143b9c7d89d78bba53"
-    },
-    "shiny_semantic/www/bindings/semanticDropdown.js": {
-      "checksum": "7ad122444070d04ff3261b2191f3ebfa"
-    },
-    "shiny_semantic/www/bindings/semanticModal.js": {
-      "checksum": "107c8dd8332b0c0a9d9f4467f340e2e7"
-    },
-    "shiny_semantic/www/bindings/semanticSlider.js": {
-      "checksum": "13f80de8ce67adcab12cacb7d67c7189"
-    },
-    "shiny_semantic/www/semantic/fomantic.min.css": {
-      "checksum": "fd8dc4162de9da54f90dae1b354627d3"
-    },
-    "shiny_semantic/www/semantic/fomantic.min.js": {
-      "checksum": "d2e5d96148b907a10a14541f7e0e99c7"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/Lato-Bold.woff": {
-      "checksum": "d878b6c29b10beca227e9eef4246111b"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/Lato-Bold.woff2": {
-      "checksum": "cccb897485813c7c256901dbca54ecf2"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/Lato-BoldItalic.woff": {
-      "checksum": "9c7e4e9eb485b4a121c760e61bc3707c"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/Lato-BoldItalic.woff2": {
-      "checksum": "0b6bb6725576b072c5d0b02ecdd1900d"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/Lato-Italic.woff": {
-      "checksum": "f28f2d6482446544ef1ea1ccc6dd5892"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/Lato-Italic.woff2": {
-      "checksum": "4eb103b4d12be57cb1d040ed5e162e9d"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/Lato-Regular.woff": {
-      "checksum": "27bd77b9162d388cb8d4c4217c7c5e2a"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/Lato-Regular.woff2": {
-      "checksum": "bd03a2cc277bbbc338d464e679fe9942"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/LatoLatin-Bold.woff": {
-      "checksum": "f323edeea9058ded98148eefffb1794a"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/LatoLatin-Bold.woff2": {
-      "checksum": "3ff66ada9b57d10c17e415222486ec21"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/LatoLatin-BoldItalic.woff": {
-      "checksum": "2538f760e9fb5f5ec46f70facba55ac5"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/LatoLatin-BoldItalic.woff2": {
-      "checksum": "e3efadc700b8e6364a98fda1b2e2b784"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/LatoLatin-Italic.woff": {
-      "checksum": "6b654922e092ce676796215a3bb19620"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/LatoLatin-Italic.woff2": {
-      "checksum": "5e75c678391d787696bb192db689fcaf"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/LatoLatin-Regular.woff": {
-      "checksum": "586ec8dc57c264cffafa044f8603e7bb"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/LatoLatin-Regular.woff2": {
-      "checksum": "3cd36578026d6c12eddf9a5c671a251d"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/brand-icons.woff": {
-      "checksum": "1a575a4138e5f366474f0e7c5bd614a5"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/brand-icons.woff2": {
-      "checksum": "ed311c7a0ade9a75bb3ebf5a7670f31d"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/icons.woff": {
-      "checksum": "d745348d289b149026921f197929a893"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/icons.woff2": {
-      "checksum": "d824df7eb2e268626a2dd9a6a741ac4e"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/outline-icons.woff": {
-      "checksum": "d1d7e3b4c219fde0f7376c6facfd7149"
-    },
-    "shiny_semantic/www/semantic/themes/default/assets/fonts/outline-icons.woff2": {
-      "checksum": "b91d376b8d7646d671cd820950d5f7f1"
-    },
-    "shiny_semantic/www/shiny-semantic-bindings.js": {
-      "checksum": "c93d4a96ee04165b7b1c147269f4091f"
-    }
-  }
-}


Test patch:
diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml
index 04ba425..8f630bf 100644
--- a/.github/workflows/unit-test.yml
+++ b/.github/workflows/unit-test.yml
@@ -11,17 +11,19 @@ jobs:
 
     steps:
       - uses: actions/checkout@v3
+
       - name: Set up Python ${{ matrix.python-version }}
         uses: actions/setup-python@v4
         with:
           python-version: ${{ matrix.python-version }}
+
       - name: Install dependencies
         run: |
           python -m pip install --upgrade pip
           pip install -e ".[dev]"
+
       - name: Lint shiny_semantic package
         run: black --check --line-length=100 shiny_semantic
-      - name: Lint example app
-        run: black --check --line-length=100 example
+
       - name: Test with unittest
         run: python -m unittest discover tests