diff --git a/Makefile b/Makefile index e4460aff..1b6d8461 100644 --- a/Makefile +++ b/Makefile @@ -45,6 +45,11 @@ FAICONS_WHEEL = faicons-$(FAICONS_VERSION)-py3-none-any.whl PLOTNINE_VERSION=0.0.0 PLOTNINE_WHEEL=plotnine-$(PLOTNINE_VERSION)-py3-none-any.whl +# libsass is built in gadenbuie/libsass-python +# NOTE: Update https://github.com/gadenbuie/libsass-python/blob/dev/.github/workflows/pyodide.yml +# Pyodide, Emscripten, or Python versions change here. +LIBSASS_WHEEL=libsass-0.23.0-cp312-abi3-pyodide_2024_0_wasm32.whl + VENV = venv PYBIN = $(VENV)/bin @@ -169,7 +174,8 @@ pyodide_packages_local: $(BUILD_DIR)/shinylive/pyodide/$(HTMLTOOLS_WHEEL) \ $(BUILD_DIR)/shinylive/pyodide/$(SHINY_WHEEL) \ $(BUILD_DIR)/shinylive/pyodide/$(SHINYWIDGETS_WHEEL) \ $(BUILD_DIR)/shinylive/pyodide/$(FAICONS_WHEEL) \ - $(BUILD_DIR)/shinylive/pyodide/$(PLOTNINE_WHEEL) + $(BUILD_DIR)/shinylive/pyodide/$(PLOTNINE_WHEEL) \ + $(BUILD_DIR)/shinylive/pyodide/$(LIBSASS_WHEEL) $(BUILD_DIR)/shinylive/pyodide/$(HTMLTOOLS_WHEEL): $(PACKAGE_DIR)/$(HTMLTOOLS_WHEEL) mkdir -p $(BUILD_DIR)/shinylive/pyodide @@ -200,6 +206,11 @@ $(BUILD_DIR)/shinylive/pyodide/$(PLOTNINE_WHEEL): $(PACKAGE_DIR)/$(PLOTNINE_WHEE rm -f $(BUILD_DIR)/shinylive/pyodide/plotnine*.whl cp $(PACKAGE_DIR)/$(PLOTNINE_WHEEL) $(BUILD_DIR)/shinylive/pyodide/$(PLOTNINE_WHEEL) +$(BUILD_DIR)/shinylive/pyodide/$(LIBSASS_WHEEL): $(PACKAGE_DIR)/$(LIBSASS_WHEEL) + mkdir -p $(BUILD_DIR)/shinylive/pyodide + rm -f $(BUILD_DIR)/shinylive/pyodide/libsass*.whl + cp $(PACKAGE_DIR)/$(LIBSASS_WHEEL) $(BUILD_DIR)/shinylive/pyodide/$(LIBSASS_WHEEL) + $(BUILD_DIR)/export_template/index.html: export_template/index.html mkdir -p $(BUILD_DIR)/export_template cp export_template/index.html $(BUILD_DIR)/export_template/index.html @@ -301,6 +312,10 @@ $(PACKAGE_DIR)/$(PLOTNINE_WHEEL): $(PYBIN) $(PACKAGE_DIR)/plotnine $(PYBIN)/pip install -e $(PACKAGE_DIR)/plotnine[build] . $(PYBIN)/activate && cd $(PACKAGE_DIR)/plotnine && make dist && mv dist/*.whl ../$(PLOTNINE_WHEEL) +$(PACKAGE_DIR)/$(LIBSASS_WHEEL): $(PYBIN) $(PACKAGE_DIR)/$(LIBSASS_WHEEL) + rm -f $(PACKAGE_DIR)/libsass*.whl + curl --fail -L https://pkg.garrickadenbuie.com/libsass-python/$(LIBSASS_WHEEL) -o $(PACKAGE_DIR)/$(LIBSASS_WHEEL) + ## Update the shinylive_lock.json file, based on shinylive_requirements.json update_packages_lock: $(PYBIN) $(BUILD_DIR)/shinylive/pyodide $(PYBIN)/pip install -r requirements-dev.txt diff --git a/examples/index.json b/examples/index.json index 26a81105..90cdd137 100644 --- a/examples/index.json +++ b/examples/index.json @@ -19,7 +19,8 @@ "insert_ui", "input_update", "extra_packages", - "fetch" + "fetch", + "brand" ] }, { diff --git a/examples/python/brand/Monda-OFL.txt b/examples/python/brand/Monda-OFL.txt new file mode 100644 index 00000000..4a58f717 --- /dev/null +++ b/examples/python/brand/Monda-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2021 The Monda Project Authors (https://github.com/googlefonts/mondaFont) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/examples/python/brand/Monda.ttf b/examples/python/brand/Monda.ttf new file mode 100644 index 00000000..458b865a Binary files /dev/null and b/examples/python/brand/Monda.ttf differ diff --git a/examples/python/brand/_brand.yml b/examples/python/brand/_brand.yml new file mode 100644 index 00000000..2dd320bf --- /dev/null +++ b/examples/python/brand/_brand.yml @@ -0,0 +1,100 @@ +# Learn more about brand.yml at https://posit-dev.github.io/brand-yml +meta: + name: + full: "Retro Arcade Brand" + short: "RetroArc" + link: + home: https://retroarc.example.com + mastodon: https://mastodon.social/@retroarc + github: https://github.com/retroarc + linkedin: https://linkedin.com/company/retroarc + twitter: https://twitter.com/retroarc + facebook: https://facebook.com/retroarc + +# This example doesn't include a brand logo +# See: https://posit-dev.github.io/brand-yml/brand/logo.html +# logo: brand-yml.png + +color: + palette: + pink: "#E83E8C" + blue: "#007BFF" + cyan: "#17A2B8" + teal: "#20C997" + green: "#28A745" + yellow: "#FFD700" + orange: "#FF7F50" + red: "#FF3333" + purple: "#6F42C1" + indigo: "#6610F2" + black: "#1A1A1A" + white: "#F8F8F8" + foreground: black + background: white + primary: purple + success: green + info: cyan + warning: yellow + danger: orange + light: white + dark: black + +typography: + fonts: + - family: Quantico + source: google + weight: [700] + style: [normal, italic] + display: swap + - family: Monda + source: file + files: + - path: Monda.ttf + weight: 400..700 + - family: Share Tech Mono + source: bunny + weight: 400 + style: normal + display: swap + base: + family: Monda + size: 17px + weight: 400 + line-height: 1.5 + headings: + family: Quantico + weight: 400 + line-height: 1.2 + style: normal + monospace: + family: Share Tech Mono + size: 0.9em + weight: 400 + monospace-inline: + family: Share Tech Mono + # size: 0.9em + weight: 400 + color: yellow + background-color: "#1a1a1add" + monospace-block: + family: Share Tech Mono + size: 1.1em + weight: 400 + color: green + background-color: black + line-height: 1.4 + link: + weight: 400 + background-color: purple + color: white + decoration: "underline" + +defaults: + bootstrap: + defaults: + my-pink: "$brand-pink" + shiny: + theme: + preset: shiny + rules: | + .navbar-brand { color: $my-pink } diff --git a/examples/python/brand/_colors.scss b/examples/python/brand/_colors.scss new file mode 100644 index 00000000..618ba867 --- /dev/null +++ b/examples/python/brand/_colors.scss @@ -0,0 +1,116 @@ +// https://github.com/twbs/bootstrap/blob/v5.3.3/site/assets/scss/_colors.scss + +.bd-blue-100 { color: color-contrast($blue-100); background-color: $blue-100; } +.bd-blue-200 { color: color-contrast($blue-200); background-color: $blue-200; } +.bd-blue-300 { color: color-contrast($blue-300); background-color: $blue-300; } +.bd-blue-400 { color: color-contrast($blue-400); background-color: $blue-400; } +.bd-blue-500 { color: color-contrast($blue-500); background-color: $blue-500; } +.bd-blue-600 { color: color-contrast($blue-600); background-color: $blue-600; } +.bd-blue-700 { color: color-contrast($blue-700); background-color: $blue-700; } +.bd-blue-800 { color: color-contrast($blue-800); background-color: $blue-800; } +.bd-blue-900 { color: color-contrast($blue-900); background-color: $blue-900; } + +.bd-indigo-100 { color: color-contrast($indigo-100); background-color: $indigo-100; } +.bd-indigo-200 { color: color-contrast($indigo-200); background-color: $indigo-200; } +.bd-indigo-300 { color: color-contrast($indigo-300); background-color: $indigo-300; } +.bd-indigo-400 { color: color-contrast($indigo-400); background-color: $indigo-400; } +.bd-indigo-500 { color: color-contrast($indigo-500); background-color: $indigo-500; } +.bd-indigo-600 { color: color-contrast($indigo-600); background-color: $indigo-600; } +.bd-indigo-700 { color: color-contrast($indigo-700); background-color: $indigo-700; } +.bd-indigo-800 { color: color-contrast($indigo-800); background-color: $indigo-800; } +.bd-indigo-900 { color: color-contrast($indigo-900); background-color: $indigo-900; } + +.bd-purple-100 { color: color-contrast($purple-100); background-color: $purple-100; } +.bd-purple-200 { color: color-contrast($purple-200); background-color: $purple-200; } +.bd-purple-300 { color: color-contrast($purple-300); background-color: $purple-300; } +.bd-purple-400 { color: color-contrast($purple-400); background-color: $purple-400; } +.bd-purple-500 { color: color-contrast($purple-500); background-color: $purple-500; } +.bd-purple-600 { color: color-contrast($purple-600); background-color: $purple-600; } +.bd-purple-700 { color: color-contrast($purple-700); background-color: $purple-700; } +.bd-purple-800 { color: color-contrast($purple-800); background-color: $purple-800; } +.bd-purple-900 { color: color-contrast($purple-900); background-color: $purple-900; } + +.bd-pink-100 { color: color-contrast($pink-100); background-color: $pink-100; } +.bd-pink-200 { color: color-contrast($pink-200); background-color: $pink-200; } +.bd-pink-300 { color: color-contrast($pink-300); background-color: $pink-300; } +.bd-pink-400 { color: color-contrast($pink-400); background-color: $pink-400; } +.bd-pink-500 { color: color-contrast($pink-500); background-color: $pink-500; } +.bd-pink-600 { color: color-contrast($pink-600); background-color: $pink-600; } +.bd-pink-700 { color: color-contrast($pink-700); background-color: $pink-700; } +.bd-pink-800 { color: color-contrast($pink-800); background-color: $pink-800; } +.bd-pink-900 { color: color-contrast($pink-900); background-color: $pink-900; } + +.bd-red-100 { color: color-contrast($red-100); background-color: $red-100; } +.bd-red-200 { color: color-contrast($red-200); background-color: $red-200; } +.bd-red-300 { color: color-contrast($red-300); background-color: $red-300; } +.bd-red-400 { color: color-contrast($red-400); background-color: $red-400; } +.bd-red-500 { color: color-contrast($red-500); background-color: $red-500; } +.bd-red-600 { color: color-contrast($red-600); background-color: $red-600; } +.bd-red-700 { color: color-contrast($red-700); background-color: $red-700; } +.bd-red-800 { color: color-contrast($red-800); background-color: $red-800; } +.bd-red-900 { color: color-contrast($red-900); background-color: $red-900; } + +.bd-orange-100 { color: color-contrast($orange-100); background-color: $orange-100; } +.bd-orange-200 { color: color-contrast($orange-200); background-color: $orange-200; } +.bd-orange-300 { color: color-contrast($orange-300); background-color: $orange-300; } +.bd-orange-400 { color: color-contrast($orange-400); background-color: $orange-400; } +.bd-orange-500 { color: color-contrast($orange-500); background-color: $orange-500; } +.bd-orange-600 { color: color-contrast($orange-600); background-color: $orange-600; } +.bd-orange-700 { color: color-contrast($orange-700); background-color: $orange-700; } +.bd-orange-800 { color: color-contrast($orange-800); background-color: $orange-800; } +.bd-orange-900 { color: color-contrast($orange-900); background-color: $orange-900; } + +.bd-yellow-100 { color: color-contrast($yellow-100); background-color: $yellow-100; } +.bd-yellow-200 { color: color-contrast($yellow-200); background-color: $yellow-200; } +.bd-yellow-300 { color: color-contrast($yellow-300); background-color: $yellow-300; } +.bd-yellow-400 { color: color-contrast($yellow-400); background-color: $yellow-400; } +.bd-yellow-500 { color: color-contrast($yellow-500); background-color: $yellow-500; } +.bd-yellow-600 { color: color-contrast($yellow-600); background-color: $yellow-600; } +.bd-yellow-700 { color: color-contrast($yellow-700); background-color: $yellow-700; } +.bd-yellow-800 { color: color-contrast($yellow-800); background-color: $yellow-800; } +.bd-yellow-900 { color: color-contrast($yellow-900); background-color: $yellow-900; } + +.bd-green-100 { color: color-contrast($green-100); background-color: $green-100; } +.bd-green-200 { color: color-contrast($green-200); background-color: $green-200; } +.bd-green-300 { color: color-contrast($green-300); background-color: $green-300; } +.bd-green-400 { color: color-contrast($green-400); background-color: $green-400; } +.bd-green-500 { color: color-contrast($green-500); background-color: $green-500; } +.bd-green-600 { color: color-contrast($green-600); background-color: $green-600; } +.bd-green-700 { color: color-contrast($green-700); background-color: $green-700; } +.bd-green-800 { color: color-contrast($green-800); background-color: $green-800; } +.bd-green-900 { color: color-contrast($green-900); background-color: $green-900; } + +.bd-teal-100 { color: color-contrast($teal-100); background-color: $teal-100; } +.bd-teal-200 { color: color-contrast($teal-200); background-color: $teal-200; } +.bd-teal-300 { color: color-contrast($teal-300); background-color: $teal-300; } +.bd-teal-400 { color: color-contrast($teal-400); background-color: $teal-400; } +.bd-teal-500 { color: color-contrast($teal-500); background-color: $teal-500; } +.bd-teal-600 { color: color-contrast($teal-600); background-color: $teal-600; } +.bd-teal-700 { color: color-contrast($teal-700); background-color: $teal-700; } +.bd-teal-800 { color: color-contrast($teal-800); background-color: $teal-800; } +.bd-teal-900 { color: color-contrast($teal-900); background-color: $teal-900; } + +.bd-cyan-100 { color: color-contrast($cyan-100); background-color: $cyan-100; } +.bd-cyan-200 { color: color-contrast($cyan-200); background-color: $cyan-200; } +.bd-cyan-300 { color: color-contrast($cyan-300); background-color: $cyan-300; } +.bd-cyan-400 { color: color-contrast($cyan-400); background-color: $cyan-400; } +.bd-cyan-500 { color: color-contrast($cyan-500); background-color: $cyan-500; } +.bd-cyan-600 { color: color-contrast($cyan-600); background-color: $cyan-600; } +.bd-cyan-700 { color: color-contrast($cyan-700); background-color: $cyan-700; } +.bd-cyan-800 { color: color-contrast($cyan-800); background-color: $cyan-800; } +.bd-cyan-900 { color: color-contrast($cyan-900); background-color: $cyan-900; } + +.bd-gray-100 { color: color-contrast($gray-100); background-color: $gray-100; } +.bd-gray-200 { color: color-contrast($gray-200); background-color: $gray-200; } +.bd-gray-300 { color: color-contrast($gray-300); background-color: $gray-300; } +.bd-gray-400 { color: color-contrast($gray-400); background-color: $gray-400; } +.bd-gray-500 { color: color-contrast($gray-500); background-color: $gray-500; } +.bd-gray-600 { color: color-contrast($gray-600); background-color: $gray-600; } +.bd-gray-700 { color: color-contrast($gray-700); background-color: $gray-700; } +.bd-gray-800 { color: color-contrast($gray-800); background-color: $gray-800; } +.bd-gray-900 { color: color-contrast($gray-900); background-color: $gray-900; } + +.bd-white { color: color-contrast($white); background-color: $white; border: 2px solid $body-color;} +.bd-black { color: color-contrast($black); background-color: $black; } +.bd-foreground { color: $body-bg; background-color: $body-color; } +.bd-background { color: $body-color; background-color: $body-bg; border: 2px solid $body-color;} \ No newline at end of file diff --git a/examples/python/brand/about.txt b/examples/python/brand/about.txt new file mode 100644 index 00000000..9b2d5023 --- /dev/null +++ b/examples/python/brand/about.txt @@ -0,0 +1,2 @@ +Branded Theming +Using brand.yml diff --git a/examples/python/brand/app.py b/examples/python/brand/app.py new file mode 100644 index 00000000..9a2a2f15 --- /dev/null +++ b/examples/python/brand/app.py @@ -0,0 +1,349 @@ +from pathlib import Path + +import matplotlib.pyplot as plt +import numpy as np +from shiny import App, render, ui +from shiny.ui._theme_brand import bootstrap_colors + +# Learn more: https://posit-dev.github.io/brand-yml +theme = ui.Theme.from_brand(__file__) + +# theme = ui.Theme() # Uncomment this line to compare with the standard Shiny theme + +theme.add_rules((Path(__file__).parent / "_colors.scss").read_text()) + +app_ui = ui.page_navbar( + ui.nav_panel( + "Input Output Demo", + ui.layout_sidebar( + ui.sidebar( + ui.input_slider("slider1", "Numeric Slider Input", 0, 11, 11), + ui.input_numeric("numeric1", "Numeric Input Widget", 30), + ui.input_date("date1", "Date Input Component", value="2024-01-01"), + ui.input_switch("switch1", "Binary Switch Input", True), + ui.input_radio_buttons( + "radio1", + "Radio Button Group", + choices=["Option A", "Option B", "Option C", "Option D"], + ), + ui.input_action_button("action1", "Action Button"), + ), + ui.div( + "This example uses ", + ui.tags.a("brand.yml", href="https://posit-dev.github.io/brand-yml"), + " for a brand-themed app. Try editing the ", + ui.tags.code("_brand.yml"), + " file to change the theme.", + class_="alert alert-info", + ), + ui.layout_columns( + ui.value_box( + "Metric 1", + "100", + theme="primary", + ), + ui.value_box( + "Metric 2", + "200", + theme="secondary", + ), + ui.value_box( + "Metric 3", + "300", + theme="info", + ), + ), + ui.card( + ui.card_header("Plot Output"), + ui.output_plot("plot1"), + ), + ui.card( + ui.card_header("Text Output"), + ui.output_text_verbatim("out_text1"), + ), + ), + ), + ui.nav_panel( + "Widget Gallery", + ui.layout_column_wrap( + ui.card( + ui.card_header("Button Variants"), + ui.input_action_button("btn1", "Default"), + ui.input_action_button("btn2", "Primary", class_="btn-primary"), + ui.input_action_button("btn3", "Secondary", class_="btn-secondary"), + ui.input_action_button("btn4", "Info", class_="btn-info"), + ui.input_action_button("btn5", "Success", class_="btn-success"), + ui.input_action_button("btn6", "Warning", class_="btn-warning"), + ui.input_action_button("btn7", "Danger", class_="btn-danger"), + ), + ui.card( + ui.card_header("Radio Button Examples"), + ui.input_radio_buttons( + "radio2", + "Standard Radio Group", + ["Selection 1", "Selection 2", "Selection 3"], + ), + ui.input_radio_buttons( + "radio3", + "Inline Radio Group", + ["Option 1", "Option 2", "Option 3"], + inline=True, + ), + ), + ui.card( + ui.card_header("Checkbox Examples"), + ui.input_checkbox_group( + "check1", + "Standard Checkbox Group", + ["Item 1", "Item 2", "Item 3"], + ), + ui.input_checkbox_group( + "check2", + "Inline Checkbox Group", + ["Choice A", "Choice B", "Choice C"], + inline=True, + ), + ), + ui.card( + ui.card_header("Select Input Widgets"), + ui.input_selectize( + "select1", + "Selectize Input", + ["Selection A", "Selection B", "Selection C"], + ), + ui.input_select( + "select2", + "Multiple Select Input", + ["Item X", "Item Y", "Item Z"], + multiple=True, + ), + ), + ui.card( + ui.card_header("Text Input Widgets"), + ui.input_text("text1", "Text Input"), + ui.input_text_area( + "textarea1", + "Text Area Input", + "Default text content for the text area widget", + ), + ui.input_password("password1", "Password Input"), + ), + width=300, + heights_equal=False, + ), + ), + ui.nav_panel( + "Colors", + ui.fill.as_fill_item( + ui.div( + ui.div(ui.output_ui("ui_colors"), class_="container-sm"), + class_="overflow-y-auto", + ) + ), + ), + ui.nav_panel( + "Documentation", + ui.fill.as_fill_item( + ui.div( + ui.div( + ui.markdown( + """ + _Just in case it isn't obvious, this text was written by an LLM._ + + # Component Documentation + + The Shiny for Python framework, available at [shiny.posit.co/py](https://shiny.posit.co/py/), + provides a comprehensive set of UI components for building interactive web applications. These + components are designed with **consistency and usability** in mind, making it easier for + developers to create professional-grade applications. + + Our framework implements the `ui.page_navbar()` component as the primary navigation structure, + allowing for intuitive organization of content across multiple views. Each view can contain + various input and output elements, managed through the `ui.card()` container system. + + ## Component Architecture + + *The architecture of our application framework* emphasizes modularity and reusability. Key + components like `ui.value_box()` and `ui.layout_column_wrap()` work together to create + structured, responsive layouts that adapt to different screen sizes. + +
Component | +Implementation | +Use Case | +Status | +
---|---|---|---|
Value Box | +ui.value_box() |
+ Metric Display | +Production Ready | +
Card | +ui.card() |
+ Content Container | +Production Ready | +
Layout | +ui.layout_column_wrap() |
+ Component Organization | +Production Ready | +
Navigation | +ui.page_navbar() |
+ Page Structure | +Production Ready | +