diff --git a/NEWS.md b/NEWS.md
index bdaba30..0a03a90 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,5 +1,12 @@
# breedverse NEWS
+## breedverse 0.3.2
+
+* Add styles to CSS
+* Add custom.js for collapsible headers
+* Add breedverse logo
+* Add header to help page
+
## breedverse 0.3.1
* Remove Qploidy while it is going throught repository transition
diff --git a/R/app_ui.R b/R/app_ui.R
index b59e714..b5a6252 100644
--- a/R/app_ui.R
+++ b/R/app_ui.R
@@ -13,12 +13,21 @@ app_ui <- function(request) {
tagList(
# Leave this function for adding external resources
golem_add_external_resources(),
+ # Dynamic sidebar color theme — only sets the :root CSS variables
+ # ── Sidebar color theme ──────────────────────────────────────────────────────
+ # Change this value to switch the active sidebar menu item color.
+ # Available options: "azure", "green", "yellow", "grey", "purple", "red"
+ # ─────────────────────────────────────────────────────────────────────────────
+ tags$head(tags$style(HTML(sprintf(
+ ":root { --sidebar-core: var(--%s-core); --sidebar-lite: var(--%s-lite); --sidebar-deep: var(--%s-deep); }",
+ "azure", "azure", "azure"
+ )))),
# Your application UI logic
bs4DashPage(
skin = "black",
bs4DashNavbar(
title = tagList(
- tags$img(src = 'www/BreedingInsight.png', height = '40', width = '50'),
+ tags$img(src = 'www/breedverse_logo.png', height = '40', width = '35'),
),
rightUi = tags$li(
class = "dropdown",
@@ -54,9 +63,11 @@ app_ui <- function(request) {
expandOnHover = TRUE,
sidebarMenu(id = "MainMenu",
flat = FALSE,
- tags$li(class = "header", style = "color: grey; margin-top: 10px; margin-bottom: 10px; padding-left: 15px;", "Menu"),
+ tags$li(class = "header","Menu"),
menuItem("Home", tabName = "welcome", icon = icon("house"),startExpanded = FALSE),
menuItem("Install modules", tabName = "install", icon = icon("share-from-square")),
+ tags$li(class = "header", "Available Modules"),
+
# conditionalPanel(
# condition = "output.qploidyInstalled == true",
@@ -66,7 +77,6 @@ app_ui <- function(request) {
conditionalPanel(
condition = "output.familiaInstalled == true",
- tags$li(class = "header", style = "color: grey; margin-top: 18px; margin-bottom: 10px; padding-left: 15px;", "Ancestry (R/familia)"),
menuItem(
"familia",
icon = icon("seedling"),
@@ -78,13 +88,11 @@ app_ui <- function(request) {
conditionalPanel(
condition = "output.allomateInstalled == true",
- tags$li(class = "header", style = "color: grey; margin-top: 18px; margin-bottom: 10px; padding-left: 15px;", "Mating Estimation (R/AlloMate)"),
menuItem("AlloMate", tabName = "allomate", icon = icon("diagram-project"))
),
conditionalPanel(
condition = "output.BIGappInstalled == true",
- tags$li(class = "header", style = "color: grey; margin-top: 18px; margin-bottom: 10px; padding-left: 15px;", "Genotype Processing"),
menuItem(
"BIGapp",
icon = icon("dna"),
@@ -102,7 +110,6 @@ app_ui <- function(request) {
),
conditionalPanel(
condition = "output.genobrewInstalled == true",
- tags$li(class = "header", style = "color: grey; margin-top: 18px; margin-bottom: 10px; padding-left: 15px;", "Marker Panel Test &\n CNV Profiles"),
menuItem(
"GenoBrew",
icon = icon("dna"),
@@ -119,27 +126,27 @@ app_ui <- function(request) {
),
footer = dashboardFooter(
right = div(
- style = "display: flex; align-items: center;", # Align text and images horizontally
+ class = "dashboard-footer-right", # Align text and images horizontally
div(
- style = "display: flex; flex-direction: column; margin-right: 15px; text-align: right;",
+ class = "dashboard-footer-text", # Style the text
div("2026 Breeding Insight"),
div("Funded by USDA through UF|IFAS")
),
div(
a(
img(src = "www/usda-logo-color.png", height = "45px"),
- style = "margin-right: 15px;"
+ class = "dashboard-footer-logo"
),
a(
- img(src = "www/cornell_seal_simple_web_b31b1b.png", height = "45px")
+ img(src = "www/IFAS.jpg", height = "45px")
),
a(
- img(src = "www/IFAS.jpg", height = "45px")
+ img(src = "www/cornell_seal_simple_web_b31b1b.png", height = "45px")
)
)
),
left = div(
- style = "display: flex; align-items: center; height: 100%;",
+ class = "dashboard-footer-left",
sprintf("v%s", as.character(utils::packageVersion("Breedverse"))))
),
dashboardBody(
@@ -255,26 +262,11 @@ golem_add_external_resources <- function() {
path = app_sys("app/www"),
app_title = "Breedverse"
),
- # Add here other external resources
- # for example, you can add shinyalert::useShinyalert()
- tags$style(HTML("
- /* Ensure box collapse/expand buttons are always on top */
- .card-tools { position: relative; z-index: 10; }
- /* Make collapse/expand icons visible on white box headers */
- .card-tools .btn-tool { color: #495057 !important; }
- .card-tools .btn-tool:hover { color: #212529 !important; }
- ")),
- tags$script(HTML("
- $(document).ready(function() {
- // On page load: mirror active class from
onto for CSS targeting
- $('#cnv_1-sample_select_tabs li.active > a').addClass('active');
-
- // After each tab switch (content already swapped): sync active on only
- $(document).on('shown.bs.tab', '#cnv_1-sample_select_tabs a[data-toggle=\"tab\"]', function(e) {
- $('#cnv_1-sample_select_tabs a[data-toggle=\"tab\"]').removeClass('active');
- $(e.target).addClass('active');
- });
- });
- "))
+ tags$link(
+ rel = "stylesheet",
+ type = "text/css",
+ href = "www/custom.css"
+ ),
+ tags$script(src = "www/custom.js")
)
}
diff --git a/R/mod_help.R b/R/mod_help.R
index 9bb508d..d53e368 100644
--- a/R/mod_help.R
+++ b/R/mod_help.R
@@ -7,12 +7,22 @@
#' @noRd
#'
#' @importFrom shiny NS tagList includeMarkdown
+#'
mod_help_ui <- function(id){
ns <- NS(id)
tagList(
fluidPage(
column(width=12),
column(width=12,
+ div(
+ style = "padding: 20px;",
+ div(
+ style = "text-align: center; margin-bottom: 25px; padding-bottom: 15px; border-bottom: 2px solid #17a2b8;",
+ tags$h2("Help Documentation", style = "color: #17a2b8; margin-bottom: 10px;"),
+ tags$p("Click a module to expand its help section.",
+ style = "color: #666; font-size: 16px;")
+ )),
+
# conditionalPanel(
# condition = "output.qploidyInstalled == true",
# box(title="Ploidy Estimation", id = "Qploidy_box",width = 12, collapsible = TRUE, collapsed = TRUE, status = "info", solidHeader = TRUE,
diff --git a/R/mod_home.R b/R/mod_home.R
index a4f65dc..e419915 100644
--- a/R/mod_home.R
+++ b/R/mod_home.R
@@ -22,6 +22,9 @@ mod_Home_ui <- function(id){
box(
title = "Breedverse", status = "info", solidHeader = FALSE, width = 12, collapsible = FALSE,
HTML(
+ "
+

+
",
paste0(
"About Breedverse. Breedverse is a user-friendly, easy-to-install interface ",
"that brings together a curated catalog of applications developed by ",
@@ -53,7 +56,7 @@ mod_Home_ui <- function(id){
Software Tools
Analysis
- Breeding Insight is funded by the U.S. Department of Agriculture (USDA) Agricultural Research Service (ARS) through Cornell University.
+ Breeding Insight is funded by the U.S. Department of Agriculture (USDA) Agricultural Research Service (ARS) through University of Florida (UF) - Institute of Food and Agricultural Sciences (IFAS).
"
diff --git a/R/run_app.R b/R/run_app.R
index 0e204a4..3355218 100644
--- a/R/run_app.R
+++ b/R/run_app.R
@@ -10,8 +10,8 @@
#' @importFrom shiny shinyApp
#' @importFrom golem with_golem_options
#'
-#' @export
-#'
+#' @export
+#'
run_app <- function(
onStart = NULL,
options = list(),
diff --git a/inst/app/www/breedverse_logo.png b/inst/app/www/breedverse_logo.png
new file mode 100644
index 0000000..6eca84a
Binary files /dev/null and b/inst/app/www/breedverse_logo.png differ
diff --git a/inst/app/www/custom.css b/inst/app/www/custom.css
index ea177d5..5cb9856 100644
--- a/inst/app/www/custom.css
+++ b/inst/app/www/custom.css
@@ -1,3 +1,350 @@
+/* ── Custom Box Colors ── */
+
+:root {
+ --azure-core: #48A9C5;
+ --azure-lite: #B5DDE8;
+ --azure-deep: #2A6576;
+ --green-core: #319B42;
+ --green-lite: #A3D9AC;
+ --green-deep: #1E5D28;
+ --yellow-core: #EFB526;
+ --yellow-lite: #F9E1A8;
+ --yellow-deep: #8F6D17;
+ --grey-core: #707372;
+ --grey-lite: #C8CACA;
+ --grey-deep: #434544;
+ --purple-core: #512C85;
+ --purple-lite: #A896C2;
+ --purple-deep: #311A50;
+ --red-core: #E43F4F;
+ --red-lite: #F2A7AE;
+ --red-deep: #8E1E2A;
+ --saddleBrown: #8B4513;
+ --rosybrown: #BC8F8F;
+ --coral: #FF7F50;
+ --lightcoral: #F08080;
+ --darkorange: #FF8C00;
+}
+
+/* Override all bs4Dash status colors with custom colors */
+
+/* Info -> Azure Core */
+.card-info:not(.card-outline) .card-header {
+ background-color: var(--azure-core) !important;
+ color: white !important;
+ border: none !important;
+ margin: -1px -1px 0 -1px !important;
+}
+.card-info:not(.card-outline) {
+ border-top: 3px solid var(--azure-core) !important;
+}
+
+/* Secondary -> Grey Core */
+.card-secondary:not(.card-outline) .card-header {
+ background-color: var(--grey-core) !important;
+ color: white !important;
+ border: none !important;
+ margin: -1px -1px 0 -1px !important;
+}
+.card-secondary:not(.card-outline) {
+ border-top: 3px solid var(--grey-core) !important;
+}
+
+/* Primary -> Azure Lite */
+.card-primary .card-header {
+ background-color: var(--azure-lite) !important;
+ color: #333 !important;
+ border: none !important;
+ margin: -1px -1px 0 -1px !important;
+}
+.card-primary:not(.card-outline) {
+ border-top: 3px solid var(--azure-lite) !important;
+}
+
+/* Success -> Green Core */
+.card-success:not(.card-outline) .card-header {
+ background-color: var(--green-core) !important;
+ color: white !important;
+ border: none !important;
+ margin: -1px -1px 0 -1px !important;
+}
+.card-success:not(.card-outline) {
+ border-top: 3px solid var(--green-core) !important;
+}
+
+/* Warning -> Yellow Core */
+.card-warning:not(.card-outline) .card-header {
+ background-color: var(--yellow-core) !important;
+ color: #333 !important;
+ border: none !important;
+ margin: -1px -1px 0 -1px !important;
+}
+.card-warning:not(.card-outline) {
+ border-top: 3px solid var(--yellow-core) !important;
+}
+
+/* Danger -> Red Core */
+.card-danger:not(.card-outline) .card-header {
+ background-color: var(--red-core) !important;
+ color: white !important;
+ border: none !important;
+ margin: -1px -1px 0 -1px !important;
+}
+.card-danger:not(.card-outline) {
+ border-top: 3px solid var(--red-core) !important;
+}
+
+/* Gray-dark -> Grey Deep */
+.card-gray-dark:not(.card-outline) .card-header {
+ background-color: var(--grey-deep) !important;
+ color: white !important;
+ border: none !important;
+ margin: -1px -1px 0 -1px !important;
+}
+.card-gray-dark:not(.card-outline) {
+ border-top: 3px solid var(--grey-deep) !important;
+}
+
+/* Gray -> Grey Lite */
+.card-gray:not(.card-outline) .card-header {
+ background-color: var(--grey-lite) !important;
+ color: #333 !important;
+ border: none !important;
+ margin: -1px -1px 0 -1px !important;
+}
+.card-gray:not(.card-outline) {
+ border-top: 3px solid var(--grey-lite) !important;
+}
+
+/* Indigo -> Purple Core */
+.card-indigo:not(.card-outline) .card-header {
+ background-color: var(--purple-core) !important;
+ color: white !important;
+ border: none !important;
+ margin: -1px -1px 0 -1px !important;
+}
+.card-indigo:not(.card-outline) {
+ border-top: 3px solid var(--purple-core) !important;
+}
+
+/* Lightblue -> Azure Deep */
+.card-lightblue:not(.card-outline) .card-header {
+ background-color: var(--azure-deep) !important;
+ color: white !important;
+ border: none !important;
+ margin: -1px -1px 0 -1px !important;
+}
+.card-lightblue:not(.card-outline) {
+ border-top: 3px solid var(--azure-deep) !important;
+}
+
+/* Navy -> Saddle Brown */
+.card-navy:not(.card-outline) .card-header {
+ background-color: var(--saddleBrown) !important;
+ color: white !important;
+ border: none !important;
+ margin: -1px -1px 0 -1px !important;
+}
+.card-navy:not(.card-outline) {
+ border-top: 3px solid var(--saddleBrown) !important;
+}
+
+/* Purple -> Purple Lite */
+.card-purple:not(.card-outline) .card-header {
+ background-color: var(--purple-lite) !important;
+ color: #333 !important;
+ border: none !important;
+ margin: -1px -1px 0 -1px !important;
+}
+.card-purple:not(.card-outline) {
+ border-top: 3px solid var(--purple-lite) !important;
+}
+
+/* Fuchsia -> Purple Deep */
+.card-fuchsia:not(.card-outline) .card-header {
+ background-color: var(--purple-deep) !important;
+ color: white !important;
+ border: none !important;
+ margin: -1px -1px 0 -1px !important;
+}
+.card-fuchsia:not(.card-outline) {
+ border-top: 3px solid var(--purple-deep) !important;
+}
+
+/* Pink -> Red Lite */
+.card-pink:not(.card-outline) .card-header {
+ background-color: var(--red-lite) !important;
+ color: #333 !important;
+ border-top-left-radius: 0.25rem !important;
+ border-top-right-radius: 0.25rem !important;
+}
+.card-pink:not(.card-outline) {
+ border-top: 3px solid var(--red-lite) !important;
+}
+
+/* Maroon -> Red Deep */
+.card-maroon:not(.card-outline) .card-header {
+ background-color: var(--red-deep) !important;
+ color: white !important;
+ border: none !important;
+ margin: -1px -1px 0 -1px !important;
+}
+.card-maroon:not(.card-outline) {
+ border-top: 3px solid var(--red-deep) !important;
+}
+
+/* Orange -> Yellow Deep */
+.card-orange:not(.card-outline) .card-header {
+ background-color: var(--yellow-deep) !important;
+ color: white !important;
+ border: none !important;
+ margin: -1px -1px 0 -1px !important;
+}
+.card-orange:not(.card-outline) {
+ border-top: 3px solid var(--yellow-deep) !important;
+}
+
+/* Lime -> Green Lite */
+.card-lime:not(.card-outline) .card-header {
+ background-color: var(--green-lite) !important;
+ color: #333 !important;
+ border: none !important;
+ margin: -1px -1px 0 -1px !important;
+}
+.card-lime:not(.card-outline) {
+ border-top: 3px solid var(--green-lite) !important;
+}
+
+/* Teal -> Yellow Lite */
+.card-teal:not(.card-outline) .card-header {
+ background-color: var(--yellow-lite) !important;
+ color: #333 !important;
+ border: none !important;
+ margin: -1px -1px 0 -1px !important;
+}
+.card-teal:not(.card-outline) {
+ border-top: 3px solid var(--yellow-lite) !important;
+}
+
+/* Olive -> Green Deep */
+.card-olive:not(.card-outline) .card-header {
+ background-color: var(--green-deep) !important;
+ color: white !important;
+ border: none !important;
+ margin: -1px -1px 0 -1px !important;
+}
+.card-olive:not(.card-outline) {
+ border-top: 3px solid var(--green-deep) !important;
+}
+
+
+/* ── ValueBox Gradient Color Overrides ── */
+
+/* Info -> Azure Core */
+.small-box.bg-gradient-info {
+ background: linear-gradient(135deg, var(--azure-core), var(--azure-deep)) !important;
+ color: white !important;
+}
+.small-box.bg-gradient-info .icon {
+ color: rgba(255,255,255,0.3) !important;
+}
+
+/* Secondary -> Grey Core */
+.small-box.bg-gradient-secondary {
+ background: linear-gradient(135deg, var(--grey-core), var(--grey-deep)) !important;
+ color: white !important;
+}
+.small-box.bg-gradient-secondary .icon {
+ color: rgba(255,255,255,0.3) !important;
+}
+
+/* Info -> Azure Lite */
+.small-box.bg-gradient-primary {
+ background: linear-gradient(135deg, var(--azure-lite), var(--azure-core)) !important;
+ color: #333 !important;
+}
+.small-box.bg-gradient-primary .icon {
+ color: rgba(51,51,51,0.3) !important;
+}
+
+/* Success -> Green Core */
+.small-box.bg-gradient-success {
+ background: linear-gradient(135deg, var(--green-core), var(--green-deep)) !important;
+ color: white !important;
+}
+.small-box.bg-gradient-success .icon {
+ color: rgba(255,255,255,0.3) !important;
+}
+
+/* Warning -> Yellow Core */
+.small-box.bg-gradient-warning {
+ background: linear-gradient(135deg, var(--yellow-core), var(--yellow-deep)) !important;
+ color: #333 !important;
+}
+.small-box.bg-gradient-warning .icon {
+ color: rgba(51,51,51,0.3) !important;
+}
+
+/* Danger -> Red Core */
+.small-box.bg-gradient-danger {
+ background: linear-gradient(135deg, var(--red-core), var(--red-deep)) !important;
+ color: white !important;
+}
+.small-box.bg-gradient-danger .icon {
+ color: rgba(255,255,255,0.3) !important;
+}
+
+/* Navy -> Saddle Brown */
+.small-box.bg-gradient-navy {
+ background: linear-gradient(135deg, var(--saddleBrown), #5d2f0a) !important;
+ color: white !important;
+}
+.small-box.bg-gradient-navy .icon {
+ color: rgba(255,255,255,0.3) !important;
+}
+
+/* Continue for all other colors... */
+.small-box.bg-gradient-indigo {
+ background: linear-gradient(135deg, var(--purple-core), var(--purple-deep)) !important;
+ color: white !important;
+}
+.small-box.bg-gradient-indigo .icon {
+ color: rgba(255,255,255,0.3) !important;
+}
+
+/* ── Sidebar Active Menu Item ── */
+/* Colors are driven by --sidebar-* variables set in app_ui.R */
+:root {
+ --sidebar-core: var(--azure-core);
+ --sidebar-lite: var(--azure-lite);
+ --sidebar-deep: var(--azure-deep);
+}
+
+.main-sidebar .nav-sidebar .nav-item.active .nav-link,
+.main-sidebar .nav-sidebar .nav-item .nav-link.active {
+ background-color: var(--sidebar-core) !important;
+ color: white !important;
+}
+
+/* Hover effect for menu items */
+.main-sidebar .nav-sidebar .nav-item .nav-link:hover {
+ background-color: var(--sidebar-lite) !important;
+ color: #333 !important;
+}
+
+/* Active menu item icon */
+.main-sidebar .nav-sidebar .nav-item.active .nav-link .nav-icon,
+.main-sidebar .nav-sidebar .nav-item .nav-link.active .nav-icon {
+ color: white !important;
+}
+
+/* For expanded menu items */
+.main-sidebar .nav-sidebar .nav-treeview .nav-item.active .nav-link,
+.main-sidebar .nav-sidebar .nav-treeview .nav-item .nav-link.active {
+ background-color: var(--sidebar-deep) !important;
+ color: white !important;
+}
+
/* ── Select Samples tabsetPanel (By Sample / By Family) */
/* inactive tab - grey background */
@@ -49,3 +396,56 @@
.main-footer a {
color: grey;
}
+
+#MainMenu .header {
+ color: grey;
+ margin-top: 10px;
+ margin-bottom: 10px;
+ padding-left: 15px;
+}
+
+/* ── Most specific selector for AdminLTE override ── */
+.nav.nav-pills.nav-sidebar.flex-column.sidebar-menu .header {
+ color: grey;
+ margin-top: 10px;
+ margin-bottom: 10px;
+ padding-left: 15px;
+}
+
+/* ── Footer styling ── */
+.dashboard-footer-right {
+ display: flex;
+ align-items: center;
+}
+
+.dashboard-footer-text {
+ display: flex;
+ flex-direction: column;
+ margin-right: 15px;
+ text-align: right;
+}
+
+.dashboard-footer-logo {
+ margin-right: 15px;
+}
+
+.dashboard-footer-left {
+ display: flex;
+ align-items: center;
+ height: 100%;
+}
+
+/* ── Ensure box collapse/expand buttons are always on top ── */
+.card-tools {
+ position: relative;
+ z-index: 10;
+}
+
+/* Make collapse/expand icons visible on white box headers */
+.card-tools .btn-tool {
+ color: #495057 !important;
+}
+
+.card-tools .btn-tool:hover {
+ color: #212529 !important;
+}
\ No newline at end of file
diff --git a/inst/app/www/custom.js b/inst/app/www/custom.js
new file mode 100644
index 0000000..f638789
--- /dev/null
+++ b/inst/app/www/custom.js
@@ -0,0 +1,45 @@
+$(document).ready(function() {
+ // Use event delegation for dynamically created elements
+ $(document).on('click', '.card-header', function(e) {
+ // Don't trigger if clicking on the actual collapse button
+ if (!$(e.target).closest('.card-tools').length) {
+ // Find the collapse button in this header and trigger click
+ var collapseBtn = $(this).find('[data-card-widget="collapse"]');
+ if (collapseBtn.length > 0) {
+ collapseBtn.trigger('click');
+ }
+ }
+ });
+
+ // Function to apply styles to card headers (both existing and new ones)
+ function styleCardHeaders() {
+ $('.card-header').css('cursor', 'pointer');
+ $('.card-tools').css('cursor', 'default');
+ }
+
+ // Apply styles initially
+ styleCardHeaders();
+
+ // Watch for new elements and apply styles
+ var observer = new MutationObserver(function(mutations) {
+ mutations.forEach(function(mutation) {
+ if (mutation.addedNodes.length > 0) {
+ styleCardHeaders();
+ }
+ });
+ });
+
+ // Start observing
+ observer.observe(document.body, {
+ childList: true,
+ subtree: true
+ });
+
+ // Your existing tab script
+ $('#cnv_1-sample_select_tabs li.active > a').addClass('active');
+
+ $(document).on('shown.bs.tab', '#cnv_1-sample_select_tabs a[data-toggle="tab"]', function(e) {
+ $('#cnv_1-sample_select_tabs a[data-toggle="tab"]').removeClass('active');
+ $(e.target).addClass('active');
+ });
+});
\ No newline at end of file