diff --git a/.Rbuildignore b/.Rbuildignore
index 9afffbe..47795d1 100644
--- a/.Rbuildignore
+++ b/.Rbuildignore
@@ -11,3 +11,8 @@ tests
^_pkgdown\.yml$
^docs$
^pkgdown$
+^doc$
+^Meta$
+^codecov\.yml$
+.covrignore
+^\.github$
diff --git a/.covrignore b/.covrignore
new file mode 100644
index 0000000..855dd6b
--- /dev/null
+++ b/.covrignore
@@ -0,0 +1 @@
+R/zzz.R
diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml
index e7ad052..74d66e5 100644
--- a/.github/workflows/R-CMD-check.yaml
+++ b/.github/workflows/R-CMD-check.yaml
@@ -22,11 +22,11 @@ jobs:
- uses: r-lib/actions/setup-r@v1
- name: Install dependencies
run: |
- install.packages(c("remotes", "rcmdcheck"))
+ install.packages(c("remotes", "rcmdcheck", "knitr", "rmarkdown"))
remotes::install_deps(dependencies = TRUE)
shell: Rscript {0}
- name: Check
run: |
options(crayon.enabled = TRUE)
- rcmdcheck::rcmdcheck(args = "--no-manual", error_on = "error")
+ rcmdcheck::rcmdcheck(args = c("--no-manual", "--ignore-vignettes", "--no-build-vignettes", "--as-cran"), build_args = c("--no-manual", "--no-build-vignettes"), error_on = "error")
shell: Rscript {0}
diff --git a/.github/workflows/deploy-docs.yaml b/.github/workflows/deploy-docs.yaml
new file mode 100644
index 0000000..32c0cf0
--- /dev/null
+++ b/.github/workflows/deploy-docs.yaml
@@ -0,0 +1,32 @@
+name: Upload and invalidate docs Website
+
+on:
+ push:
+ branches:
+ - main
+ - master
+ paths:
+ - docs/**
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@master
+ - uses: jakejarvis/s3-sync-action@master
+ with:
+ args: --acl public-read --follow-symlinks --delete
+ env:
+ AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
+ AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ AWS_REGION: 'us-west-1'
+ SOURCE_DIR: 'docs'
+ DEST_DIR: 'imola'
+ - uses: chetan/invalidate-cloudfront-action@v2
+ env:
+ DISTRIBUTION: ${{ secrets.DISTRIBUTION }}
+ PATHS: "/imola/*"
+ AWS_REGION: "us-east-1"
+ AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml
new file mode 100644
index 0000000..8e498db
--- /dev/null
+++ b/.github/workflows/test-coverage.yaml
@@ -0,0 +1,29 @@
+# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
+# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
+on:
+ push:
+ branches: [main, master]
+ pull_request:
+ branches: [main, master]
+
+name: test-coverage
+
+jobs:
+ test-coverage:
+ runs-on: macos-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: r-lib/actions/setup-r@v2
+ with:
+ r-version: 'release'
+ - uses: r-lib/actions/setup-pandoc@v2
+ - uses: r-lib/actions/setup-r-dependencies@v2
+ with:
+ extra-packages: |
+ covr
+ rvest
+ rstudio/webshot2
+ - name: Test coverage
+ run: |
+ covr::codecov()
+ shell: Rscript {0}
diff --git a/.gitignore b/.gitignore
index 1955b11..8585f4f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,5 +2,6 @@
.Rhistory
.RData
.Ruserdata
-docs
**/rsconnect
+/doc/
+/Meta/
diff --git a/DESCRIPTION b/DESCRIPTION
index 9fb4af3..fdadbd9 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,7 +1,7 @@
Package: imola
Type: Package
Title: CSS Layouts (Grid and Flexbox) Implementation for R/Shiny
-Version: 0.3.2
+Version: 0.4.0
Authors@R: person("Pedro", "Silva", email = "pedrocoutinhosilva@gmail.com",
role = c("aut", "cre"))
Description: Allows easy creation of CSS layouts (grid and flexbox)
@@ -10,6 +10,8 @@ License: MIT + file LICENSE
URL: https://github.com/pedrocoutinhosilva/imola
Encoding: UTF-8
LazyData: true
+VignetteBuilder:
+ knitr
Imports:
shiny,
htmltools,
@@ -18,5 +20,10 @@ Imports:
glue,
yaml
Suggests:
- testthat
+ testthat (>= 3.0.0),
+ rvest,
+ devtools,
+ covr
+Roxygen: list(markdown = TRUE)
RoxygenNote: 7.1.2
+Config/testthat/edition: 3
diff --git a/NAMESPACE b/NAMESPACE
index 9e49b75..62d65af 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -7,6 +7,7 @@ export(flexPanel)
export(gridPage)
export(gridPanel)
export(listTemplates)
+export(makeTemplate)
export(registerBreakpoint)
export(registerTemplate)
export(setBreakpointSystem)
diff --git a/R/breakpoints.R b/R/breakpoints.R
index 7049347..d5a1a59 100644
--- a/R/breakpoints.R
+++ b/R/breakpoints.R
@@ -6,6 +6,7 @@
#' systems can be found under getOption("imola.breakpoints")
#'
#' @return A named list of media breakpoints options.
+#' @keywords breakpoints
#' @export
setBreakpointSystem <- function(system) {
options(imola.mediarules = getOption("imola.breakpoints")[[system]])
@@ -16,18 +17,20 @@ setBreakpointSystem <- function(system) {
#' registerBreakpoint() and unregisterBreakpoint() functions.
#'
#' @return A named list of media breakpoints options.
+#' @keywords breakpoints
#' @export
activeBreakpoints <- function() {
getOption("imola.mediarules")
}
-#' Adds a new breakpoint entry to the currelty active media breakpoints.
+#' Adds a new breakpoint entry to the current active media breakpoints.
#'
#' @param name The name of the entry to remove
#' @param min The minimum screen width (in pixels) when the rule is active
#' @param max The maximum screen width (in pixels) when the rule is active
#'
#' @return No return value, called for side effects
+#' @keywords breakpoints
#' @export
registerBreakpoint <- function(name, min = NULL, max = NULL) {
rules <- getOption("imola.mediarules")
@@ -41,6 +44,7 @@ registerBreakpoint <- function(name, min = NULL, max = NULL) {
#' @param name The name of the entry to remove
#'
#' @return No return value, called for side effects
+#' @keywords breakpoints
#' @export
unregisterBreakpoint <- function(name) {
rules <- getOption("imola.mediarules")
diff --git a/R/flex.R b/R/flex.R
index 89979dd..6e65f1a 100644
--- a/R/flex.R
+++ b/R/flex.R
@@ -1,88 +1,155 @@
-#' Create a panel with a CSS flexbox layout
-#'
-#' @param ... Elements to include within the panel
-#' @param template The name of the template to use as a base for the grid.
-#' See listTemplates() and registerTemplate() for more information.
-#' @param direction Direction of the flow of elements in the panel. Accepts a
-#' valid css 'flex-direction' value (row | row-reverse | column |
-#' column-reverse) By default the 'row' value is used.
-#' Supports named list for breakpoints.
-#' @param wrap Should elements be allowed to wrap into multiple lines. Accepts
-#' a valid css 'flex-wrap' value (nowrap | wrap | wrap-reverse). By default
-#' the value 'wrap' is used.
-#' Supports named list for breakpoints.
+#' Create a flexbox based panel
+#'
+#' @param ... Tag attributes (named arguments) and children (unnamed arguments).
+#' A named argument with an `NA` value is rendered as a boolean attributes.
+#' Named arguments can be used to provide additional values to the container
+#' of the grid.
+#'
+#' For a full list of valid HTML attributes check visit
+#' \url{https://www.w3schools.com/tags/ref_attributes.asp}.
+#'
+#' Children may include any combination of:
+#' * Other tags objects
+#' * [HTML()] strings
+#' * [htmlDependency()]s
+#' * Single-element atomic vectors
+#' @param template The name of the template to use as a base for the grid, or
+#' the resulting value from using makeTemplate() to generate a template
+#' object.
+#'
+#' See `listTemplates()` and `registerTemplate()` for more information.
+#' @param direction Direction of the flow of elements in the panel.
+#'
+#' Accepts a valid css `flex-direction` value (`row` | `row-reverse` |
+#' `column` | `column-reverse`).
+#'
+#' By default the `row` value is used. Supports breakpoints.
+#' @param wrap Should elements be allowed to wrap into multiple lines.
+#'
+#' Accepts a valid css `flex-wrap` value (`nowrap` | `wrap` | `wrap-reverse`).
+#'
+#' By default the value `wrap` is used. Supports breakpoints.
#' @param justify_content Defines the alignment along the main axis. Accepts a
-#' valid css 'justify-content' value (flex-start | flex-end | center |
-#' space-between | space-around | space-evenly | start | end | left | right).
-#' By default the value 'flex-start' is used.
-#' Supports named list for breakpoints.
+#' valid css `justify-content` value (`flex-start` | `flex-end` | `center` |
+#' `space-between` | `space-around` | `space-evenly` | `start` |
+#' `end` | `left` | `right`).
+#'
+#' By default the value `flex-start` is used. Supports breakpoints.
#' @param align_items Defines the default behavior for how flex items are laid
-#' out along the cross axis on the current line. Accepts a valid css
-#' 'align-items' value (stretch | flex-start | flex-end | center | baseline |
-#' first baseline | last baseline | start | end | self-start | self-end).
-#' By default the value 'stretch' is used.
-#' Supports named list for breakpoints.
+#' out along the cross axis on the current line.
+#'
+#' Accepts a valid css `align-items` value (`stretch` | `flex-start` |
+#' `flex-end` | `center` | `baseline` | `first baseline` | `last baseline` |
+#' `start` | `end` | `self-start` | `self-end`).
+#'
+#' By default the value `stretch` is used. Supports breakpoints.
#' @param align_content Aligns a flex container’s lines within when there is
-#' extra space in the cross-axis. Accepts a valid css align-content' value
+#' extra space in the cross-axis.
+#'
+#' Accepts a valid css `align-content` value
#' (flex-start | flex-end | center | space-between | space-around |
#' space-evenly | stretch | start | end | baseline | first baseline |
-#' last baseline). By default the value 'flex-start' is used.
-#' Supports named list for breakpoints.
-#' @param gap Defines the space between elements in the panel. Defaults to 0.
-#' Supports named list for breakpoints.
-#' @param flex A vector of valid css 'flex' values. Defines how elements in the
-#' panel can grow and shrink. Each entry of the vector will afect the nth
-#' child of the panel, meaning that it can be at maximum the lenght of the
-#' elements in the panel. If smaller the vector will be repeated until the
-#' pattern affects all elements in the panel. NA can also be used as a entry
-#' of the vector to skip adding a rule to specific elements. By default c(1)
-#' is used, meaning all elements can grow and shrink as required, at the same
-#' rate. See notes for more information.
-#' Supports named list for breakpoints.
+#' last baseline).
+#'
+#' By default the value 'flex-start' is used. Supports breakpoints.
+#' @param gap Defines the space between elements in the panel. Controls both the
+#' space between rows and columns.
+#'
+#' Accepts a css valid value, or 2 values separated by a space (if using
+#' diferent values for row and column spacing).
+#'
+#' By default the value `0` is used. Supports breakpoints.
+#' @param flex A vector of valid css 'flex' values for the child elements.
+#' Defines how elements in the panel can grow, shrink and their initial size.
+#'
+#' Arguments that target child elements individually require a vector of
+#' values instead of a single value, with each entry of the vector affecting
+#' the nth child element.
+#'
+#' If the given vector has less entries that the number
+#' of child elements, the values will be repeated until the pattern affects
+#' all elements in the panel. If the number of entries given is more that the
+#' number of child elements, exceeding entries will be ignored. NA can also be
+#' used as a entry to skip adding a rule to a specific nth element.
+#'
+#' Accepts a valid css `flex` value.
+#'
+#' By default c(1) is used, meaning all elements can grow and shrink as
+#' required, at the same rate. Supports breakpoints.
#' @param grow A vector of valid css 'flex-grow' values. Defines the rate of
-#' how elements can grow. Entries will overwrite the 'flex' values, and can
-#' be used make more targeted rules. Each entry of the vector will afect the
-#' nth child of the panel, meaning that it can be at maximum the lenght of
-#' the elements in the panel. If smaller the vector will be repeated until
-#' the pattern affects all elements in the panel. NA can also be used as a
-#' entry of the vector to skip adding a rule to specific elements. By default
-#' c() is used, meaning values from the flex argument will be used. See notes
-#' for more information.
-#' Supports named list for breakpoints.
+#' how elements can grow. Entries will overwrite the nth 'flex' value,
+#' and can be used make more targeted rules.
+#'
+#' Entries will overwrite the 'flex' values, and can
+#' be used make more targeted rules.
+#'
+#' Arguments that target child elements individually require a vector of
+#' values instead of a single value, with each entry of the vector affecting
+#' the nth child element.
+#'
+#' If the given vector has less entries that the number
+#' of child elements, the values will be repeated until the pattern affects
+#' all elements in the panel. If the number of entries given is more that the
+#' number of child elements, exceeding entries will be ignored. NA can also be
+#' used as a entry to skip adding a rule to a specific nth element.
+#'
+#' By default NULL is used, meaning values from the flex argument will be
+#' used instead. Supports breakpoints.
#' @param shrink A vector of valid css 'flex-shrink' values. Defines the rate
-#' of how elements can shrink. Entries will overwrite the 'flex' values, and
-#' can be used make more targeted rules. Each entry of the vector will afect
-#' the nth child of the panel, meaning that it can be at maximum the lenght
-#' of the elements in the panel. If smaller the vector will be repeated until
-#' the pattern affects all elements in the panel. NA can also be used as a
-#' entry of the vector to skip adding a rule to specific elements. By default
-#' c() is used, meaning values from the flex argument will be used. See notes
-#' for more information.
-#' Supports named list for breakpoints.
+#' of how elements can shrink. Entries will overwrite the nth 'flex' value,
+#' and can be used make more targeted rules.
+#'
+#' Arguments that target child elements individually require a vector of
+#' values instead of a single value, with each entry of the vector affecting
+#' the nth child element.
+#'
+#' If the given vector has less entries that the number
+#' of child elements, the values will be repeated until the pattern affects
+#' all elements in the panel. If the number of entries given is more that the
+#' number of child elements, exceeding entries will be ignored. NA can also be
+#' used as a entry to skip adding a rule to a specific nth element.
+#'
+#' By default NULL is used, meaning values from the flex argument will be
+#' used instead. Supports breakpoints.
#' @param basis A vector of valid css 'flex-basis' values. Defines the base
-#' size of elements. Entries will overwrite the 'flex' values, and can be
-#' used make more targeted rules. Each entry of the vector will afect the nth
-#' child of the panel, meaning that it can be at maximum the lenght of the
-#' elements in the panel. If smaller the vector will be repeated until the
-#' pattern affects all elements in the panel. NA can also be used as a entry
-#' of the vector to skip adding a rule to specific elements. By default c()
-#' is used, meaning values from the flex argument will be used. See notes for
-#' more information.
-#' Supports named list for breakpoints.
+#' size of elements. Entries will overwrite the nth 'flex' value,
+#' and can be used make more targeted rules.
+#'
+#' Arguments that target child elements individually require a vector of
+#' values instead of a single value, with each entry of the vector affecting
+#' the nth child element.
+#'
+#' If the given vector has less entries that the number
+#' of child elements, the values will be repeated until the pattern affects
+#' all elements in the panel. If the number of entries given is more that the
+#' number of child elements, exceeding entries will be ignored. NA can also be
+#' used as a entry to skip adding a rule to a specific nth element.
+#'
+#' By default NULL is used, meaning values from the flex argument will be
+#' used instead. Supports breakpoints.
#' @param breakpoint_system Optional Media breakpoints to use. Will default to
#' the current active breakpoint system.
#' @param id The panel id. A randomly generated one is used by default.
+#' You cannot have more than one element with the same id in an HTML document.
+#'
+#' @details Behaves similar to a normal HTML tag, but provides helping
+#' arguments that simplify the way flexbox css can be created from shiny.
+#'
+#' @note When creating responsive layouts based on media rules, for most css
+#' arguments a named list can be passed instead of a single value.
+#'
+#' The names in the list can be any of the registered breakpoints available in
+#' `activeBreakpoints()`, of on the provided `breakpoint_system` argument.
+#' Current global `activeBreakpoints()` can be changed using
+#' `setBreakpointSystem()`.
#'
-#' @details Behaves similar to normal HTML div tags, but simplifies
-#' the way css flexbox can be used from shiny.
+#' In a similar fashion, the current `activeBreakpoints()` can also be
+#' modified with the `registerBreakpoint()` and `unregisterBreakpoint()`.
#'
-#' @note When creating responsive layouts based on media rules, for most
-#' arguments a named list can be passed instead of a single value. The names
-#' in the list can be any of the registered breakpoints available in
-#' activeBreakpoints(). Breakpoints can also be modified with the
-#' registerBreakpoint() and unregisterBreakpoint() functions. Arguments that
-#' allow this are: direction | wrap | justify_content | align_items |
-#' align_content | gap | flex | grow | shrink | basis.
+#' It is recomended to define the breakpoint system for the application
+#' globally before UI definitions, but the `breakpoint_system` in panel
+#' functions allows for more flexibility when it comes to reuse components
+#' from other projects.
#'
#' @note See
#' \url{https://css-tricks.com/snippets/css/a-guide-to-flexbox/}
@@ -93,6 +160,7 @@
#'
#' @examples
#' if (interactive()) {
+#' library(shiny)
#' library(imola)
#' flexPanel(
#' div("example content"),
@@ -105,6 +173,7 @@
#' @importFrom htmltools HTML
#'
#' @return An HTML tagList.
+#' @keywords flex panel
#' @export
flexPanel <- function(...,
template = NULL,
@@ -156,9 +225,21 @@ flexPanel <- function(...,
)
}
-#' Create a page a with CSS flexbox layout
+#' Create a flexbox based page
#'
-#' @param ... Elements to include within the page
+#' @param ... Tag attributes (named arguments) and children (unnamed arguments).
+#' A named argument with an `NA` value is rendered as a boolean attributes.
+#' Named arguments can be used to provide additional values to the container
+#' of the grid.
+#'
+#' For a full list of valid HTML attributes check visit
+#' \url{https://www.w3schools.com/tags/ref_attributes.asp}.
+#'
+#' Children may include any combination of:
+#' * Other tags objects
+#' * [HTML()] strings
+#' * [htmlDependency()]s
+#' * Single-element atomic vectors
#' @param title The browser window title (defaults to the host URL of the page)
#' @param fill_page Flag to tell the page if it should adjust the page to
#' adjust and fill the browser window size
@@ -175,19 +256,23 @@ flexPanel <- function(...,
#'
#' @examples
#' if (interactive()) {
+#' library(shiny)
#' library(imola)
-#' flexPage(
+#' ui <- flexPage(
#' title = "A flex page",
-#' div(class = "area-1"),
-#' div(class = "area-2"),
-#' div(class = "area-3")
+#' div(class = "area-1", "Area 1 content"),
+#' div(class = "area-2", "Area 2 content"),
+#' div(class = "area-3", "Area 3 content")
#' )
-#' }
#'
+#' server <- function(input, output) {}
+#' shinyApp(ui, server)
+#' }
#' @importFrom shiny tagList tags bootstrapLib
#' @importFrom magrittr "%>%"
#'
#' @return A UI definition that can be passed to the [shinyUI] function.
+#' @keywords flex page
#' @export
flexPage <- function(...,
title = NULL,
diff --git a/R/generators.R b/R/generators.R
index 282e0d0..beb3aa9 100644
--- a/R/generators.R
+++ b/R/generators.R
@@ -8,6 +8,7 @@
#' @importFrom magrittr "%>%"
#' @importFrom magrittr "%<>%"
#'
+#' @keywords internal generators
#' @return A string with all placeholders replaced.
generateGridCSS <- function(attributes, id, unique_areas, breakpoint_system) {
mapping <- getOption("imola.settings")$property_mapping
@@ -40,6 +41,7 @@ generateGridCSS <- function(attributes, id, unique_areas, breakpoint_system) {
#'
#' @importFrom magrittr "%<>%"
#'
+#' @keywords internal generators
#' @return A vector of valid css strings.
generateGridAreaCSS <- function(areas, id) {
styles <- c()
@@ -68,6 +70,7 @@ generateGridAreaCSS <- function(areas, id) {
#'
#' @importFrom magrittr "%>%"
#'
+#' @keywords internal generators
#' @return A string with all placeholders replaced.
generateFlexCSS <- function(attributes,
id,
@@ -118,6 +121,7 @@ generateFlexCSS <- function(attributes,
#' @importFrom magrittr "%>%"
#' @importFrom magrittr "%<>%"
#'
+#' @keywords internal generators
#' @return A string with all placeholders replaced.
generateFlexChildrenCSS <- function(attributes,
id,
@@ -175,6 +179,7 @@ generateFlexChildrenCSS <- function(attributes,
#'
#' @importFrom magrittr "%<>%"
#'
+#' @keywords internal generators
#' @return A vector of valid css strings.
generateCSSPropertyStyles <- function(value, property, id, breakpoint_system) {
if (is.null(value)) {
diff --git a/R/grid.R b/R/grid.R
index 8ebe278..0351cd4 100644
--- a/R/grid.R
+++ b/R/grid.R
@@ -4,8 +4,9 @@
#' named arguments using the grid area name will be added to that grid area.
#' If no named arguments or areas are used, non attribute elements will be
#' added to existing grid cells based on their order.
-#' @param template The name of the template to use as a base for the grid.
-#' See listTemplates() and registerTemplate() for more information.
+#' @param template The name of the template to use as a base for the grid, or
+#' the resulting value from using makeTemplate() to generate a template
+#' object. See listTemplates() and registerTemplate() for more information.
#' @param areas A list of vectors with area names, or a vector or strings
#' representing each row of the grid. Each element should contain
#' the names, per row, of each area of the grid. Expected values follow the
@@ -35,12 +36,21 @@
#' @param gap The space (in a valid css size) between each grid cell.
#' Supports named list for breakpoints.
#' @param align_items The cell behavior according to the align-items css
-#' property. Defaults to stretch.
-#' Supports named list for breakpoints.
+#' property. Aligns grid items along the block (column) axis.
+#'
+#' Accepts a valid css `align-items` value
+#' (`start` | `end` | `center` | `stretch`)
+#'
+#' By default the `stretch` value is used. Supports breakpoints.
+#' @param justify_items The cell behavior according to the justify-items css
+#' property. Aligns grid items along the inline (row) axis.
+#'
+#' Accepts a valid css `justify-items` value
+#' (`start` | `end` | `center` | `stretch`)
+#'
+#' By default the `stretch` value is used. Supports breakpoints.
#' @param auto_fill Should the panel stretch to fit its parent size (TRUE), or
#' should its size be based on its children element sizes (FALSE).
-#' @param justify_items The cell behavior according to the justify-items css
-#' property. Defaults to stretch.
#' Supports named list for breakpoints.
#' @param breakpoint_system Optional Media breakpoints to use. Will default to
#' the current active breakpoint system.
@@ -85,6 +95,7 @@
#' @importFrom shiny tagAppendAttributes tagAppendChild
#'
#' @return An HTML tagList.
+#' @keywords grid panel
#' @export
gridPanel <- function(...,
template = NULL,
@@ -163,6 +174,7 @@ gridPanel <- function(...,
#' @importFrom magrittr "%>%"
#'
#' @return A UI definition that can be passed to the [shinyUI] function.
+#' @keywords grid page
#' @export
gridPage <- function(...,
title = NULL,
diff --git a/R/normalize.R b/R/normalize.R
index fd96bfc..2370d7c 100644
--- a/R/normalize.R
+++ b/R/normalize.R
@@ -2,6 +2,7 @@
#'
#' @param attributes The values to process
#'
+#' @keywords internal normalizer
#' @return A named list.
normalizeAttributes <- function(attributes) {
for (attribute in names(attributes)) {
@@ -18,8 +19,9 @@ normalizeAttributes <- function(attributes) {
#' @param simplify Boolean flag if the attribute should be simplified into
#' single strings.
#'
+#' @keywords internal normalizer
#' @return A named list.
-normalizeAttribute <- function(attribute, simplify = TRUE) {
+normalizeAttribute <- function(attribute, simplify = FALSE) {
if (is.null(attribute)) {
return(attribute)
}
@@ -40,5 +42,5 @@ normalizeAttribute <- function(attribute, simplify = TRUE) {
} else {
breakpoint
}
- }, simplify = FALSE)
+ }, simplify = simplify)
}
diff --git a/R/templates.R b/R/templates.R
index ab48cc4..aecf0ef 100644
--- a/R/templates.R
+++ b/R/templates.R
@@ -8,6 +8,7 @@
#' of all types are returned,
#'
#' @return A named list of css templates and specific values.
+#' @keywords templates
#' @export
listTemplates <- function(type = NULL) {
if (is.null(type)) {
@@ -32,26 +33,59 @@ listTemplates <- function(type = NULL) {
#' by default it will simply use the current active system but a built in
#' or custom system can also be passed. You ca find built in breakpoint
#' systems under getOption("imola.breakpoints")
-#' @param export A file name to export the template to. Allows exporting
-#' templates as a yaml file for future usage.
#'
#' @importFrom utils modifyList
#' @importFrom yaml write_yaml
#'
#' @return No return value, called for side effects
+#' @keywords templates
#' @export
-registerTemplate <- function(type, name, ..., breakpoint_system = activeBreakpoints(), export = NULL) {
+registerTemplate <- function(type, name, ..., breakpoint_system = activeBreakpoints()) {
listTemplates <- listTemplates()
listTemplates[[type]][[name]] <- modifyList(
list(...),
list(breakpoint_system = breakpoint_system)
)
+ options(imola.templates = listTemplates)
+}
+
+#' Returns a imola template as an object for future use. Depending on the
+#' given type, the template will then be available to be passed as an
+#' argument to a panel or page function of that specific type. Templates are
+#' collections of arguments that can be grouped and stored for later usage
+#' via the "template" argument of panel and page functions.
+#'
+#' @param type The type of css grid for which the template can be used
+#' @param ... Collection of valid arguments that can be passed to a panel of
+#' the given type (see gridPanel() and FlexPanel() for all options)
+#' @param breakpoint_system Optional breakpoint system to use in the template.
+#' by default it will simply use the current active system but a built in
+#' or custom system can also be passed. You ca find built in breakpoint
+#' systems under getOption("imola.breakpoints")
+#' @param export A path ending in a file name with a .yaml extension to export
+#' the template to. Allows exporting templates as a yaml file for
+#' future usage.
+#'
+#' @importFrom utils modifyList
+#'
+#' @return No return value, called for side effects
+#' @keywords templates
+#' @export
+makeTemplate <- function(type, ..., breakpoint_system = activeBreakpoints(), export = NULL) {
+ template <- modifyList(
+ list(...),
+ list(
+ type = type,
+ breakpoint_system = breakpoint_system
+ )
+ )
+
if (!is.null(export)) {
- yaml::write_yaml(listTemplates[[type]][[name]], paste0(export, ".yaml"))
+ yaml::write_yaml(template, export)
}
- options(imola.templates = listTemplates)
+ template
}
#' Deletes an existing css template from the available list of templates for the
@@ -63,6 +97,7 @@ registerTemplate <- function(type, name, ..., breakpoint_system = activeBreakpoi
#' @param name The name of the tempalte to remove.
#'
#' @return No return value, called for side effects
+#' @keywords templates
#' @export
unregisterTemplate <- function(type, name) {
listTemplates <- listTemplates()
@@ -78,25 +113,39 @@ unregisterTemplate <- function(type, name) {
#'
#' @param attributes The manually given attribute values that will take priority
#' during the merge.
-#' @param template The name of the template to merge.
+#' @param template The name of the template to merge, or the resulting value
+#' from using makeTemplate() to generate a template object.
#' @param defaults The default values of the grid callback.
#' @param type The type of css grid of the template.
#'
#' @return A named list of css attributes that can be used to generate a html
#' element style rules of the given type.
+#' @keywords templates
#' @export
applyTemplate <- function(attributes, template, defaults, type) {
if (is.null(template)) {
return(attributes)
}
- if (is.null(getOption("imola.templates")[[type]][[template]])) {
+ if (!is.list(template) &&
+ is.null(getOption("imola.templates")[[type]][[template]])) {
+
messages <- getOption("imola.settings")$string_templates$messages
stop(messages$missing_template %>%
stringTemplate(template = template, type = type))
}
- options <- getOption("imola.templates")[[type]][[template]]
+ if (is.list(template)) {
+ if(!identical(template$type, type)) {
+ messages <- getOption("imola.settings")$string_templates$messages
+ stop(messages$wrong_template_type %>%
+ stringTemplate(template_type = template$type, type = type))
+ }
+
+ options <- template
+ } else {
+ options <- getOption("imola.templates")[[type]][[template]]
+ }
for (name in names(options)) {
manual_value <- attributes[[name]]
diff --git a/R/utils.R b/R/utils.R
index 2b5a4b4..1dd0d61 100644
--- a/R/utils.R
+++ b/R/utils.R
@@ -9,6 +9,7 @@
#' @importFrom stringi stri_rand_strings
#'
#' @return A valid CSS id.
+#' @keywords internal utils
generateID <- function() {
generated_id <- Sys.time() %>%
as.integer() %>%
@@ -32,6 +33,7 @@ generateID <- function() {
#' @importFrom shiny htmlTemplate
#'
#' @return A string with all placeholders replaced.
+#' @keywords internal utils
stringTemplate <- function(string, ...) {
string %>%
htmlTemplate(text_ = ., ...) %>%
@@ -54,6 +56,7 @@ stringTemplate <- function(string, ...) {
#' @importFrom magrittr "%>%"
#'
#' @return A valid CSS string.
+#' @keywords internal utils
stringCSSRule <- function(template, ...) {
getOption("imola.settings")$string_templates[[template]] %>%
stringTemplate(...)
@@ -72,6 +75,7 @@ stringCSSRule <- function(template, ...) {
#' @importFrom shiny tagAppendAttributes
#'
#' @return A list of HTML elements.
+#' @keywords internal utils
processContent <- function(content, areas) {
for (name in stri_remove_empty(names(content))) {
if (name %in% areas) {
@@ -89,6 +93,7 @@ processContent <- function(content, areas) {
#' @param property The target css property for which the value will be used.
#'
#' @return string containing a valid css value.
+#' @keywords internal utils
valueToCSS <- function(value, property) {
if (property == "grid-template-areas") {
value %<>%
@@ -109,6 +114,7 @@ valueToCSS <- function(value, property) {
#' @importFrom glue glue
#'
#' @return A valid glue::glue template string to be processed later.
+#' @keywords internal utils
mediaRuleTemplate <- function(options) {
if (is.null(options$min) && is.null(options$max)) {
return("{{rules}}")
@@ -134,6 +140,7 @@ mediaRuleTemplate <- function(options) {
#' @importFrom yaml read_yaml
#'
#' @return A list object containing the content of the settings YAML file
+#' @keywords internal utils
readSettingsFile <- function(file) {
file %>%
paste0("settings/", ., ".yml") %>%
diff --git a/README.md b/README.md
index a60fd10..643e623 100644
--- a/README.md
+++ b/README.md
@@ -1,164 +1,60 @@
-
-
-# imola
+# imola
[](https://CRAN.R-project.org/package=imola)
+[](https://codecov.io/gh/pedrocoutinhosilva/imola?branch=main)
[](https://CRAN.R-project.org/package=imola)
[](https://CRAN.R-project.org/package=imola)
[](https://CRAN.R-project.org/package=imola)
-Bridging the gap between R/shiny and CSS layouts (grid and flexbox)!
+An interface to create grid and flexbox CSS layouts for your R/Shiny dashboards, directly from R.
-Demo (and example layouts): https://sparktuga.shinyapps.io/imolatemplates/
+Imola (named after the first city ever to be given a technical blueprint by Leonardo da Vinci) aims at giving you more layout creation options directly in R/shiny, without the hassle of having to create custom CSS every time.
-If you're familiar with CSS, you might have felt by now that layouts in shiny can be very awkward to set up.
+##### CSS Layouts in shiny, made simple
+You can now easily leverage typical CSS layouts (grid and Flexbox) directly in your UI functions, including support for media breakpoints to fit different screen sizes and devices.
-While R/shiny does give you access to bootstrap's row and column system, these do have some limitation and changing layouts usually requires having to rebuild a large portion of the UI. As web development improved, this system also showed to not always be the most flexible, specially when trying to replicate a design or mockup not using this 12 column system, making it challenging to achieve without a lot of additional custom styling.
+##### Built in templates, or create your own
+Save your favorite layouts for later use via the existing templating system and simply use it in as many elements as you need.
-Imola (named after the first city ever to be given a technical blueprint by Leonardo da Vinci) aims at giving you more layout creation options directly in R/shiny, without the hassle of having to create custom CSS every time.
+If layout creation isn't your thing, imola also comes with a built in collection of templates for traditionally used web layouts, making it even easier to spice up your dashboards!
-With imola you can easily leverage typical CSS layouts (grid and Flexbox) directly in your UI functions, including media breakpoints to fit different screen sizes and devices.
+##### Demos to get you started
+You can find a few deployed demos showcasing some of the power of imola:
-You can also save your favorite layouts to use later via a templating system that allows you to define a layout, name it, and simply use it in as many elements as you need. If creating isn't your thing, imola also comes with a built in collection of templates traditionally used web layouts, making it even easier to spice up your dashboards!
+- Built in template layouts: https://sparktuga.shinyapps.io/imolatemplates/
-# installation
-1 - Install the package:
+---
+
+## installation
+###### 1 - Install the package:
-from CRAN:
```R
+# Install released version from CRAN
install.packages('imola')
-```
-from github:
-```R
+# Or the most recent development version from github:
devtools::install_github('pedrocoutinhosilva/imola')
```
-2 - Include the library into your project:
+###### 2 - Include the library in your project:
```R
# global.R
library(imola)
```
+You are now ready to go!
-You are now ready to go! Check the usage section for some examples and more information on how to use the diferent grid and flex functions to get started!
-
-# Usage
-
-The bread and butter of imola are the `gridPanel()` and `flexPanel()` functions. These allow you to replace any HTML tag that serves as a container (`div`, `section`, `main`, `nav`, ...) with a tag that uses one of the specific css layout systems (grid or flexbox).
-
-If you are not familiar with these layout systems, I definitely recommend [this article](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Relationship_of_Grid_Layout) to get you up to speed.
+Looking for help on how to start? Make sure to check the built in examples and vignettes for help and usage examples.
-The basic difference between CSS Grid Layout and CSS Flexbox Layout is that flexbox was designed for layout in one dimension - either a row or a column. Grid was designed for two-dimensional layout - rows, and columns at the same time. This means if you want fine control over columns and rows, you should aim at using grid, if you only care about one of the dimensions, flex will most likely work just fine.
+---
-## Grid
-The grid family focuses on providing support for the CSS Grid standard. In imola you can find 2 functions that allow you to create a new grid component. `gridPanel()` and `gridPage()`. `gridPage()` is simply a wrapper for `gridPanel()` that allows you to create a page UI element without the need of using any of the built in shiny functions.
+## Usage
-If you're interested in more information about the full array of options in the CSS grid standard from the CSS side, I recommend [this article](https://css-tricks.com/snippets/css/complete-guide-grid/) to get you started.
-
-## Flex
-The flex family focuses on providing support for the CSS flexbox standard. In imola you can find 2 functions that allow you to create a new grid component. `flexPanel()` and `flexPage()`. Similar to what happens in the grid family, `flexPage()` is simply a wrapper for `flexPanel()` that allows you to create a page UI element without the need of using any of the built in shiny functions.
-
-If you're interested in more information about the full array of options in the CSS flexbox standard from the CSS side, I recommend [this article](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) to get you started.
-
-# Breakpoints
-Breakpoints are a way to adjust your layouts to different screen sizes. As the screen size gets larger or smaller, it is often required to adjust the position or size of the different elements in a page to make sure things dont appear broken or out of place.
-
-Depending on the CSS framework that is being used in your project, different systems are in place to allow this to be a bit more automated, if you are familiar with base shiny, you might have even used these systems without realizing, by using the `fluidRow()` function. `fluidRow()` will trigger layout changes to your `columns` at specific screen sizes, based on [bootstrap 3](https://shiny.rstudio.com/articles/layout-guide.html) breakpoints (The base CSS framework in shiny).
-
-While the `fluidRow()` solution is quite easy to use, it also comes with many constrains and does not allow for a very fine control of these layout changes. If complex enough layouts, you might even be required to write additional CSS to add new behavior or specific elements or screen sizes.
-
-Imola takes a slightly different approach to breakpoints; Out of the box it uses the same breakpoints as base shiny (bootstrap 3) and for each function named attribute you are able to pass either a value for that attribute or a named list of different values for different breakpoints.
-
-As a practical example, lets say we have the following grid areas in a gridPanel():
-
-```R
-# as a gridPanel() argument
-areas = c(
- "area1 area1 area1",
- "area2 area3 area3",
- "area2 area3 area3"
-)
-# or
-areas = list(
- c("area1", "area1", "area1"),
- c("area2", "area3", "area3"),
- c("area2", "area3", "area3")
-)
-```
-This grid contains 3 areas (`area1`, `area2`, `area3`), with `area2` clearly serving a sidebar. Viewing this grid on a small screen could lead to a very small sidebar, so one solution is to modify this grid specifically for a breakpoint that targets mobile.
-
-As mentioned before, shiny and imola, by default use the bootstrap 3 breakpoint system, that contains a few different breakpoints:
-
-
-
-We can see what names imola expects for each of these using either `activeBreakpoints()` or checking the option directly with `getOption("imola.breakpoints")$bootstrap`
-
-In out case, we want to target small devices, so we target these via `xs`, and build our grid argument as a named list instead.
-```R
-# as a gridPanel() argument
-areas = list(
- default = c(
- "area1 area1 area1",
- "area2 area3 area3",
- "area2 area3 area3"
- ),
- xs = c(
- "area1",
- "area2",
- "area3"
- )
-)
-```
-NOTE: Imola reserves the special `default` name for values that are used by default, outside of any given breakpoint boundaries (`default` is the rule, breakpoints overwrite `default` for specific screen sizes).
-
-# Templates
-Very often during development it is also common that multiple elements share the same layout. In order to easily reuse any layout you create, imola also includes a simple template engine.
-
-In order to save a template you can use the `registerTemplate()` function. This function requires a 'type' of template (grid or flex), a name to identify the template later, any named arguments that can be passed to the gridPanel or flexPanel function (depending on the type) and, optionally, a breakpoint_system to use if you plan on adding any responsive attributes to the template (If no breakpoint_system is given, the current active system is used to register the template).
-
-After a template being registered, you can then simple fill in the `template` argument on any of the panel or page functions with the name of the template. You can also adjust the template for a specific panel by providing any named arguments in addition to the template name. Any named argument that exists in the template will be orewriten by the named argument on the function call.
-
-Lets say we would like to save our previous areas as a template, and use it. We could use registerTemplate():
-
-```R
-#in global.R
-registerTemplate("grid", "mytemplate",
- areas = list(
- default = c(
- "area1 area1 area1",
- "area2 area3 area3",
- "area2 area3 area3"
- ),
- xs = c(
- "area1",
- "area2",
- "area3"
- )
- )
-)
-```
-
-We can then use this template to create multiple grid panels in our application:
-```R
-#in ui.R
-gridPanel(
- id = "somePanel"
- template = "mytemplate"
- area1 = div("area 1 content"),
- area2 = div("area 2 content"),
- area3 = div("area 3 content"),
-)
-
-gridPanel(
- id = "anotherPanel"
- template = "mytemplate"
- area1 = div("different area 1 content"),
- area2 = div("different area 2 content"),
- area3 = div("different area 3 content"),
-)
-```
+Check `vignette("imola")` on how to get started, and other follow up vignettes for even more information regarding css flexbox and grid and their imola counterparts.
-You can register as many templates as you want, but keep in mind that each type + name combo must be unique. You can also remove templates using `unregisterTemplate()` if needed. For a full list of registered templates, you can use `listTemplates()`.
+Looking for a specific topic to jump into? Use the following vignettes for a quick start:
-By default imola also includes some ready to use templates these will also be listed under `listTemplates()`
+- `vignette("imola-flexbox")` for details on `flexPanel()` and `flexPage()`.
+- `vignette("imola-grid")` for details on `gridPanel()` and `gridPage()`.
+- `vignette("imola-templates")` for information on using imola's templating engine.
+- `vignette("imola-breakpoints")` for information on using imola's breakpoint systems.
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 0000000..04c5585
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,14 @@
+comment: false
+
+coverage:
+ status:
+ project:
+ default:
+ target: auto
+ threshold: 1%
+ informational: true
+ patch:
+ default:
+ target: auto
+ threshold: 1%
+ informational: true
diff --git a/docs/404.html b/docs/404.html
new file mode 100644
index 0000000..d06d2e6
--- /dev/null
+++ b/docs/404.html
@@ -0,0 +1,118 @@
+
+
+
YEAR: 2021 +COPYRIGHT HOLDER: Pedro Coutinho Silva ++ +
test
+vignettes/getting-started.Rmd
+ getting-started.Rmd
When designing for the web, it is important to keep in mind what diferent users will reach you in diferent devices with diferent screen sizes and orientations.
+In the early days of web design, pages were built to target a particular screen size. If the user had a larger or smaller screen than the designer expected, results ranged from unwanted scrollbars to overly long line lengths, and poor use of space.
+As more diverse screen sizes became available, the concept of responsive web design (RWD) appeared, a set of practices that allows web pages to alter their layout and appearance to suit different screen widths, resolutions, etc.
+The term responsive design was coined by Ethan Marcotte in 2010 and described the use of multiple techniques:
+Fluid grids, something which was already being explored by Gillenwater, and can be read up on in Marcotte’s article, Fluid Grids (published in 2009 on A List Apart).
Fluid images. Using a very simple technique of setting the max-width property to 100%, images would scale down smaller if their containing column became narrower than the image’s intrinsic size, but never grow larger. This enables an image to scale down to fit in a flexibly-sized column, rather than overflow it.
The third key component was the media query. Media Queries enable the type of layout switch that Cameron Adams had previously explored using JavaScript, using only CSS. Rather than having one layout for all screen sizes, the layout could be changed. Sidebars could be repositioned for the smaller screen, or alternate navigation could be displayed.
Responsive design was only able to emerge due to the media query. The Media Queries Level 3 specification became a Candidate Recommendation in 2009, meaning that it was deemed ready for implementation in browsers.
+Media Queries allow us to run a series of tests (e.g. whether the user’s screen is greater than a certain width, or a certain resolution) and apply CSS selectively to style the page appropriately for the user’s needs.
+By defining a set “points” where these Media Queries will apply its diferent rules, we are effectively creating breakpoints where the styling and layout of the page changes. Many frontend frameworks reuse a set of tested and tried breakpoints, making that set of breakpoints its breakpoint system
.
Example bootstrap 5 breakpoints: - xs: Screen width from 0 to 576px - sm: Screen width above 576px - md: Screen width above 768px - lg: Screen width above 992px - xl: Screen width above 1200px - xxl: Screen width above 1400px
+Keep in mind that users expect any website to be perfectly complementary with every single device they own – desktop, tablet, or mobile. If a website’s responsive design does not align with a certain device resolution (especially a commonly used device), the site is at risk of missing out on a segment of its target audience. Avoid this by investing time and research into defining breakpoints at the beginning of a project.
+The amount of effort that goes into defining responsive breakpoints is directly proportional to the experience of the end-user.
+Depending on the CSS framework that is being used in your project, different systems are in place to allow this to be a bit more automated, if you are familiar with base shiny, you might have even used these systems without realizing, by using the fluidRow()
function. fluidRow()
will trigger layout changes to your columns
at specific screen sizes, based on bootstrap 3 breakpoints (The base CSS framework in shiny).
While the fluidRow()
solution is quite easy to use, it also comes with many constrains and does not allow for a very fine control of these layout changes. For complex enough layouts, you might even need to write additional CSS to add new behavior or specific elements or screen sizes.
Imola takes a slightly different approach to breakpoints; Out of the box it uses the same breakpoints as base shiny (bootstrap 3) and for each function named attribute you are able to pass either a value for that attribute or a named list of different values for different breakpoints.
+As a practical example, lets say we have the following grid areas in a gridPanel():
+
+# as a gridPanel() argument
+areas = c(
+ "area1 area1 area1",
+ "area2 area3 area3",
+ "area2 area3 area3"
+)
+# or
+areas = list(
+ c("area1", "area1", "area1"),
+ c("area2", "area3", "area3"),
+ c("area2", "area3", "area3")
+)
This grid contains 3 areas (area1
, area2
, area3
), with area2
serving a sidebar. Viewing this grid on a small screen could lead to a very small sidebar, so one solution is to modify this grid specifically for a breakpoint that targets mobile.
As mentioned before, shiny and imola, by default, use the bootstrap 3 breakpoint system, that contains a few different breakpoints:
+We can see what names imola expects for each of these using either activeBreakpoints()
or checking the option directly with getOption("imola.breakpoints")$bootstrap3
Back to our case, we want to target small devices, we target these via xs
, and build our grid argument as a named list instead.
+# as a gridPanel() argument
+areas = list(
+ default = c(
+ "area1 area1 area1",
+ "area2 area3 area3",
+ "area2 area3 area3"
+ ),
+ xs = c(
+ "area1",
+ "area2",
+ "area3"
+ )
+)
All gridPanel()
and flexPanel()
arguments that affect the styling of the panel allow this behavior for the use of breakpoints. See each function documentation for more details.
By default, the bootstrap 3 breakpoint system is active and returned when activeBreakpoints()
is called. This means that with no configuration, the available names to use when using the breakpoint system in other arguments should be (“xs” “sm” “md” “lg” “xl”)
You can manually list available breakpoints, in the active breakpoint system using:
+names(activeBreakpoints())
+1] "xs" "sm" "md" "lg" "xl"
+ [
+# or explore the full details of the system with
+activeBreakpoints()
If you would like to switch to a diferent breakpoint system, imola comes with a few diferent built in systems based on popular css frameworks:
+names(getOption("imola.breakpoints"))
+1] "bootstrap3" "bootstrap5" "bulma" "foundation" [
Each system has its own breakpoint naming, based on the framework they originate in:
+# Bootstrap 5 system
+names(getOption("imola.breakpoints")[["bootstrap5"]])
+1] "sm" "md" "lg" "xl" "xxl"
+ [
+# bulma system
+names(getOption("imola.breakpoints")[["bulma"]])
+1] "tablet" "desktop" "widescreen" "fullhd"
+ [
+# foundation system
+names(getOption("imola.breakpoints")[["foundation"]])
+1] "tablet" "desktop" "widescreen" "fullhd" [
The active system can be changed using setBreakpointSystem()
.
It is also possible to extend the currently active system (activeBreakpoints()
) with additional breakpoints with the registerBreakpoint()
and unregisterBreakpoint()
.
# add a new breakpoint
+registerBreakpoint("mycustombreakpoint", min = 300, max = 500)
+
+names(activeBreakpoints())
+1] "xs" "sm" "md" "lg" "xl" "mycustombreakpoint" [
It is recomended to define the breakpoint system for the application globally before UI definitions, but the breakpoint_system
argument in panel functions allows for more flexibility when it comes to reuse components from other projects.
NOTE: Imola reserves the special default
name for values that are used by default, outside of any given breakpoint boundaries (default
is the rule, breakpoints overwrite default
for specific screen sizes).
Develop for mobile-first – By developing and designing mobile-first content, the developer and designer receive multiple benefits. It is more difficult to simplify a desktop experience for mobile screens than it is to expand a mobile view for desktop screens. When a design is mobile-first, developers address what is most necessary, and can then make additions to match the preferences of desktop users.
Always keep major breakpoints in mind. THis usually means common screen sizes (480px, 768px, 1024px, and 1280px).
Before choosing major breakpoints, use website analytics to discern the most commonly used devices from which your site is accessed. Add breakpoints for those screen sizes first.
An intelligent method is to hide or display elements at certain breakpoints. If necessary, switch content or features at breakpoints. For example, consider implementing off-canvas navigation for smaller screens and a typical navigation bar for larger ones.
Don’t define standard breakpoints for responsive design on the basis of device size. The primary objective of responsive design breakpoints is to display content in the best possible way. So, let the content be the guide. Add a breakpoint when the content and design requires it.
The imola flex functions family focuses on providing support for the CSS flexbox standard. In imola you can find 2 functions that allow you to create a new grid component. flexPanel()
and flexPage()
.
Similar to what happens in the grid family, flexPage()
is simply a wrapper for flexPanel()
that allows you to create a page UI element without the need of using any of the built in shiny functions.
If you’re interested in more information about the full array of options in the CSS flexbox standard from the CSS side, I recommend this article to get you started.
+In imola, the main functions used to generate flex containers are flexPanel()
and flexPage()
. There’s a lot of arguments going on here, so lets go over what each option can do:
+flexPanel(
+ ...,
+ template = NULL,
+ direction = "row",
+ wrap = "nowrap",
+ justify_content = "flex-start",
+ align_items = "stretch",
+ align_content = "flex-start",
+ gap = 0,
+ flex = c(1),
+ grow = NULL,
+ shrink = NULL,
+ basis = NULL,
+ breakpoint_system = activeBreakpoints(),
+ id = generateID()
+)
+flexPage(
+ ..., # Any argument that can be passed to flexPanel()
+ title = NULL,
+ fill_page = TRUE,
+ dependency = bootstrapLib()
+)
Tag attributes (named arguments) and children (unnamed arguments). A named argument with an NA
value is rendered as a boolean attribute.
Named arguments can be used to provide additional values to the container of the grid.
+Visit https://www.w3schools.com/tags/ref_attributes.asp for a list of valid HTML attributes.
+Children may include any combination of:
+...
main purpose is to add content to your panel, but it can also be used to tweak your HTML generation, allowing a very similar behavior to traditional HTML tag functions provided by htmltools
, and letting you customize your HTML tag. By default using these functions behaves similar to using div()
, with additional styles being added to meet our layout expectations.
This means if we ignore the extra styling created by imola, the following call:
+ +will generate the following HTML:
+<div attribute="foo" bar>
+<p>some content</p>
+ </div>
The name of the template to use as a base for the grid, or the resulting value from using makeTemplate()
to generate a template object.
When passing a string as a value if the template is not registered you will get a error message. Valid template strings are either built in or created using registerTemplate()
.
A quick way to see all registered templates (for flex functions) is to use names(listTemplates("flex"))
:
# names(listTemplates("flex"))
+# Default built in flex templates
+
+1] "one-three-alternate" "one-two-alternate" "small-large-small" "three-one-alternate" "three-row" "three-two-alternate"
+ [7] "two-one-alternate" "two-row" "two-three-alternate" [
Templates that you register also become valid values for the template argument, and will also be displayed when listing available template names:
+registerTemplate("flex", "mycustomtemplate",
+direction = "column"
+
+ )
+# names(listTemplates("flex"))
+1] "one-three-alternate" "one-two-alternate" "small-large-small" "three-one-alternate" "three-row" "three-two-alternate"
+ [7] "two-one-alternate" "two-row" "two-three-alternate" "mycustomtemplate" [
Any listed template name can then be used as a value, regardless if built in or custom:
+
+flexPanel(template = "two-three-alternate")
Visit https://sparktuga.shinyapps.io/imolatemplates/ for a full list of imola’s built in templates.
+Note: See listTemplates()
and registerTemplate()
documentation for more information on those functions, or vignette("imola-templates")
for a full breakdown regarding templates in imola.
Direction of the flow of elements in the panel.
+Accepts a valid css flex-direction
value (row | row-reverse | column | column-reverse).
1
+2
+3
+4
+5
+By default the row
value is used. Supports named list for breakpoints. See vignette("imola-breakpoints")
for more on breakpoints.
Should elements be allowed to wrap into multiple lines.
+Accepts a valid css flex-wrap
value (nowrap | wrap | wrap-reverse).
1
+2
+3
+4
+5
+By default the value wrap
is used. Supports named list for breakpoints. See vignette("imola-breakpoints")
for more on breakpoints.
Defines the alignment along the main axis.
+Accepts a valid css justify-content
value (flex-start | flex-end | center | space-between | space-around | space-evenly | start | end | left | right).
1
+2
+3
+4
+5
+By default the value flex-start
is used. Supports named list for breakpoints. See vignette("imola-breakpoints")
for more on breakpoints.
NOTE: Since imola
uses flex = c("1")
as a default value for flex, you might not see any changes setting a custom value for justify_content
out of the box. You can set flex = c("0 1 auto")
to allow children elements to control their own size on the main axis.
Defines the default behavior for how flex items are laid out along the cross axis on the current line.
+Accepts a valid css align-items
value (stretch | flex-start | flex-end | center | baseline | first baseline | last baseline | start | end | self-start | self-end).
1
+2
+3
+4
+5
+By default the value stretch
is used. Supports named list for breakpoints. See vignette("imola-breakpoints")
for more on breakpoints.
Aligns a flex container’s lines within when there is extra space in the cross-axis.
+Accepts a valid css align-content
value (flex-start | flex-end | center | space-between | space-around | space-evenly | stretch | start | end | baseline | first baseline | last baseline).
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+By default the value flex-start
is used. Supports named list for breakpoints. See vignette("imola-breakpoints")
for more on breakpoints.
NOTE: Since imola
uses flex = c("1")
as a default value for flex, you might not see any changes setting a custom value for align_content
out of the box. You can set flex = c("0 1 auto")
to allow children elements to control their own size on the main axis. You might also be interested in using wrap = wrap
to allow elements to spawn multiple lines to take full advantage of align_content
.
Controls the space between items. It applies that spacing only between items not on the outer edges. The behavior could be thought of as a minimum gutter, as if the gutter is bigger somehow (because of something like justify-content: space-between;) then the gap will only take effect if that space would end up smaller.
+The gap argument controls both the row gap and the column gap at the same time, making its css equivalent gap
, row-gap
and column-gap
. If a single value is given it is used for both row and column gap, but a pair of values separated by a space can also be used for controling these independently.
Accepts a valid value in css values ("0"
, "10px"
, "20%"
, "0.5rem"
), or a pair of values separated by space ("10px 20px"
, "5% 10%"
, "10px 5%"
).
By default the value 0
is used. Supports named list for breakpoints. See vignette("imola-breakpoints")
for more on breakpoints.
A vector of valid css ‘flex’ values for the child elements. Shorthand for grow
, shrink
and basis
, with the second and third parameters being optional. This means that any compination of 1, 2 or 3 css values for grow
, shrink
and basis
are valid.
Arguments that target child elements individually require a vector of values instead of a single value, with each entry of the vector affecting the nth child element. As an example c(1, 2, 1)
will set the flex value of the first child to 1
, the second to 2
and the thrid to 1
.
If the given vector has less entries that the number of child elements, the values will be repeated until the pattern affects all elements in the panel. If the number of entries given is more that the number of child elements, exceeding entries will be ignored. NA can also be used as a entry to skip adding a rule to a specific nth element.
+It is usually recomended to use flex instead of the individual parameters, this is because when omiting some of them, flex will actually set the other values intelligently.
+An example of this is using a flex value of 1
is actually equivalent to 1 1 0%
(grow = 1
, shrink = 1
, basis = 0%
).
element 1 | +element 2 | +
+ + | ++ + | +
+ + | ++ + | +
+ + | ++ + | +
1
+2
+By default the value c(1)
is used, meaning a value of 1
for every child. You might notice that this is diferent from the pure css default, but makes more sense in the context of shiny dashboards. To recover the default css behavior, use c("0 1 auto")
as a value.
Check the following sections for grow
, shrink
and basis
for more details. Supports named list for breakpoints. See vignette("imola-breakpoints")
for more on breakpoints.
Defines the ability for a flex item to grow if necessary. It accepts a unitless value that serves as a proportion. It dictates what amount of the available space inside the flex container the item should take up.
+If all items have flex-grow set to 1, the remaining space in the container will be distributed equally to all children. If one of the children has a value of 2, that child would take up twice as much of the space either one of the others (or it will try, at least).
+1
+2
+3
+4
+5
+By default the value NULL is used, meaning it defaults to the value provided in the flex
argument, which fallsback to 1
for each element.
This defines the ability for a flex item to shrink if necessary. It accepts a unitless value that serves as a proportion. It dictates what amount of the available space inside the flex container the item should take up.
+If all items have flex-shrink set to 1, all elements will shrink at an equal rate. If one element has a larged shrink value, it will shrink at a faster rate that the other elements (depending on the diference between the given values).
+1
+2
+3
+4
+5
+By default the value NULL is used, meaning it defaults to the value provided in the flex
argument, which fallsback to 1
for each element.
Note: For shrink
to have any effect, it requires some size (either width
or basis
) to be set on the children elements.
Defines the default size of an element before the remaining space is distributed. It can be a length (e.g. 20%, 5rem, etc.) or a keyword.
+The auto
keyword means “look at my width or height property to use as a basis value for calculations”
By default the value NULL is used, meaning it defaults to the value provided in the flex
argument, meaning 0%
for each element.
Optional Media breakpoints to use. Will default to the current active breakpoint system.
+For other arguments that support breakpoints, instead of simply passing them a value, you can also provide a named list of valid values.
+The names used in that list can be any of the registered breakpoints available in the provided breakpoint_system
argument (defaults to activeBreakpoints()
), as well as the reserved keyord default
.
See vignette("imola-breakpoints")
for more details on breakpoints.
The panel ID. A randomly generated one is used by default. Providing your own ID will allow you to target the generated HTML tag via CSS or JavaScript if needed.
+General rules regarding HTML Ids apply, including the fact that duplicated Ids are not allowed.
+For a full list of details on the HTML ID attribute, check https://www.w3schools.com/html/html_id.asp
+The browser window title (defaults to the host URL of the page). This is the name that appears on the browser tab.
+Flag to tell the page if it should adjust the page to adjust and fill the browser window size.
+When set to true this will force the grid to be at least as tall as the available browser window. This makes the container stretch if the content is smaller, while still allowing it to grow behond the height of the viewport if necessary.
+The set of web dependencies. This value can be a htmlDependency, for example the shiny bootstrap one (the default) or a tagList with diferent dependencies. Useful if you are using a diferent UI framework of package with its own required dependencies (or that requires you to supress bootstrap dependencies)
+vignettes/imola-getting-started.Rmd
+ imola-getting-started.Rmd
The imola grid functions family focuses on providing support for the CSS Grid standard. In imola you can find 2 functions that allow you to create a new grid component. gridPanel()
and gridPage()
.
Similar to what happens in the flex family, gridPage()
is simply a wrapper for gridPanel()
that allows you to create a page UI element without the need of using any of the built in shiny functions.
Grid is a powerful spec that, when combined with other parts of CSS such as flexbox, can help you create layouts that were previously impossible to build in CSS.
+If you’re interested in more information about the full array of options in the CSS grid standard from the CSS side, I recommend this article to get you started.
+In imola, the main functions used to generate flex containers are gridPanel()
and gridPage()
. There’s a lot of arguments going on here, so lets go over what each option can do:
+gridPanel(
+ ...,
+ template = NULL,
+ areas = NULL,
+ rows = NULL,
+ columns = NULL,
+ gap = NULL,
+ align_items = "stretch",
+ justify_items = "stretch",
+ auto_fill = TRUE,
+ breakpoint_system = activeBreakpoints(),
+ id = generateID()
+)
+gridPage(
+ ..., # Any argument that can be passed to gridPanel()
+ title = NULL,
+ fill_page = TRUE,
+ dependency = bootstrapLib()
+)
CSS Grid Layout introduces a two-dimensional grid system to CSS. Grids can be used to lay out major page areas or small user interface elements.
+A grid
can be compared to a set of horizontal and vertical lines defining columns and rows. Elements can be placed onto the grid within these column and row lines. You can think of it as a table where each cell can be identified via its row and column position, with elements being able to span any adjacent number of cells.
The dividing lines that make up the structure of the grid. They can be either vertical (“column grid lines”) or horizontal (“row grid lines”) and reside on either side of a row or column.
The space between two adjacent grid lines. You can think of them as the columns or rows of the grid.
+The size of these tracks can be either fixed (using a fixed size unit like pixels) or flexible (using percentages or the new fr unit).
The space between two adjacent row and two adjacent column grid lines. It’s a single “unit” of the grid.
The total space surrounded by four grid lines. A grid area may be composed of any number of grid cells, and depending on how the grid is configured, identified by its bonding lines indexes or its name. For a grid area to be valid, its cells MUST create a rectangular area. Single cell areas are also valid.
+You can place items into a precise location on the grid using line numbers, names or by targeting an area of the grid. Grid also contains an algorithm to control the placement of items not given an explicit position on the grid.
NOTE: By default named grid lines are not officially supported with imola
.
However, if you are interested in using named lines, you could use the css string syntax for the rows
and columns
and manually add the child elements css for grid-column-start
, grid-column-end
, grid-row-start
, grid-row-end
, instead of using named arguments with areas
.
The same applies to positioning child elements using cell indexes. While there are no specific argument to add row and column indexes to a child element, you can always add these manually via css or the styles
argument of HTML tag functions in R.
Tag attributes (named arguments) and children (unnamed arguments). A named argument with an NA
value is rendered as a boolean attribute.
Named arguments can be used to provide additional values to the container of the grid.
+Visit https://www.w3schools.com/tags/ref_attributes.asp for a list of valid HTML attributes.
+Children may include any combination of:
+...
main purpose is to add content to your panel, but it can also be used to tweak your HTML generation, allowing a very similar behavior to traditional HTML tag functions provided by htmltools
, and letting you customize your HTML tag. By default using these functions behaves similar to using div()
, with additional styles being added to meet our layout expectations.
If you defined named areas in the areas
argument, you can also pass named arguments here using those area names to specify which area that child element will be added into.
The name of the template to use as a base for the grid, or the resulting value from using makeTemplate()
to generate a template object.
When passing a string as a value if the template is not registered you will get a error message. Valid template strings are either built in or created using registerTemplate()
.
A quick way to see all registered templates (for flex functions) is to use names(listTemplates("flex"))
:
# names(listTemplates("grid"))
+# Default built in grid templates
+
+1] "grail-left-sidebar" "grail-right-sidebar" "header-left-sidebar" "header-sidebar-right" "holy-grail"
+ [6] "sidebar-left" "sidebar-right" [
Templates that you register also become valid values for the template argument, and will also be displayed when listing available template names:
+registerTemplate("grid", "mycustomtemplate",
+areas = c(
+ "area-1",
+ "area-2",
+ "area-3"
+
+ )
+ )
+# names(listTemplates("grid"))
+1] "grail-left-sidebar" "grail-right-sidebar" "header-left-sidebar" "header-sidebar-right" "holy-grail"
+ [6] "sidebar-left" "sidebar-right" "mycustomtemplate" [
Any listed template name can then be used as a value, regardless if built in or custom:
+
+gridPanel(template = "grail-left-sidebar")
Visit https://sparktuga.shinyapps.io/imolatemplates/ for a full list of imola’s built in templates.
+Note: See listTemplates()
and registerTemplate()
documentation for more information on those functions, or vignette("imola-templates")
for a full breakdown regarding templates in imola.
Defines a grid template by referencing the names of the grid areas which are specified with the css grid-area property (in imola this is done with named arguments as part of ...
).
Repeating the name of a grid area causes the content to span those cells. For a grid area to be valid, its cells MUST create a rectangular area (Single cell areas are also valid). A period signifies an empty cell. The syntax itself provides a visualization of the structure of the grid.
+
+gridPanel(
+ areas = list(
+ c("header", "header", "header", "header"),
+ c("main", "main", ".", "sidebar"),
+ c("footer", "footer", "footer", "footer")
+ ),
+ header = div(class = ".item-a "),
+ main = div(class = ".item-b "),
+ sidebar = div(class = ".item-c "),
+ footer = div(class = ".item-d ")
+)
As with other arguments, a string variant is also available if you prefer to stay closer to the css syntax.
+
+gridPanel(
+ areas = c(
+ "header header header header",
+ "main main . sidebar",
+ "footer footer footer footer"
+ ),
+ header = div(class = ".item-a "),
+ main = div(class = ".item-b "),
+ sidebar = div(class = ".item-c "),
+ footer = div(class = ".item-d ")
+)
In either case, the size of the grid will be determined by the given value for areas
. The number of columns
will be the length of each value (in the string syntax, this will be the number of names separated by spaces), and the number of rows
the number of values given. This means the above example will generate a grid with 4 columns and 3 rows.
It is also important to note that for the areas
argument to be valid, the lenght of each value given should be equal (number of elements in each vector for the R
syntax, number of names between each space on the string
syntax).
Both of the above example will generate the following css rules:
+.item-a {
+grid-area: header;
+
+ }.item-b {
+grid-area: main;
+
+ }.item-c {
+grid-area: sidebar;
+
+ }.item-d {
+grid-area: footer;
+
+ }
+.panel {
+grid-template-areas:
+ "header header header header"
+ "main main . sidebar"
+ "footer footer footer footer";
+ }
When used together with rows
and columns
, areas
provides the names of the areas for grid items, while rows
and columns
provide the sizes. If either rows
and columns
are not provided, sizes will be automatically defined based on the dimensions of the areas
argument.
Defines the rows of the grid with a space-separated list of values. The values represent the track size, and the space between them represents the grid line.
+In imola, you can use either the css notation (a string with values separated by a space) or a vector notation (a vector where each element is a value). Both notations are valid, but if you plan on using specific css functions such as repeat()
, the single string css notation is recomended.
+# both notations produce the same results
+# css notation
+rows = "50px 50px 2fr 1fr"
+
+# R vector notation
+rows = c("50px", "50px", "2fr", "1fr")
Defines the columns of the grid with a space-separated list of values. The values represent the track size, and the space between them represents the grid line.
+In imola, you can use either the css notation (a string with values separated by a space) or a vector notation (a vector where each element is a value). Both notations are valid, but if you plan on using specific css functions such as repeat()
, the single string css notation is recomended.
+# both notations produce the same results
+# css notation
+columns = "50px 50px 2fr 1fr"
+
+# R vector notation
+columns = c("50px", "50px", "2fr", "1fr")
Controls the space between items. It applies that spacing only between items not on the outer edges. The behavior could be thought of as a minimum gutter, as if the gutter is bigger somehow (because of something like justify-content: space-between;) then the gap will only take effect if that space would end up smaller.
+The gap argument controls both the row gap and the column gap at the same time, making its css equivalent gap
, row-gap
and column-gap
. If a single value is given it is used for both row and column gap, but a pair of values separated by a space can also be used for controling these independently.
Accepts a valid value in css values ("0"
, "10px"
, "20%"
, "0.5rem"
), or a pair of values separated by space ("10px 20px"
, "5% 10%"
, "10px 5%"
).
By default the value 0
is used. Supports named list for breakpoints. See vignette("imola-breakpoints")
for more on breakpoints.
Aligns grid items along the inline (row) axis (as opposed to justify_items
which aligns along the block (column) axis). This value applies to all grid items inside the container.
Possible values for this property include: - stretch – fills the whole height of the cell (this is the default) - start – aligns items to be flush with the start edge of their cell - end – aligns items to be flush with the end edge of their cell - center – aligns items in the center of their cell
+Note: Together, align_items
and justify_items
completely cover the place-items
css property, making direct coverage from imola for place-items
redundact and therefore not provided in the grid
functions.
Aligns grid items along the inline (row) axis (as opposed to align_items
which aligns along the inline (row) axis). This value applies to all grid items inside the container.
Possible values for this property include: - stretch – fills the whole width of the cell (this is the default) - start – aligns items to be flush with the start edge of their cell - end – aligns items to be flush with the end edge of their cell - center – aligns items in the center of their cell
+Note: Together, align_items
and justify_items
completely cover the place-items
css property, making direct coverage from imola for place-items
redundact and therefore not provided in the grid
functions.
Flag to tell the grid container should automatically. By default if using grid specific fractionary units (fr
) if can happen that sometimes the grid does not behave as expected when it comes to its size.
When set to true this will force the panel to be stretched to fit the size of the parent element where it is being added. Defaults to TRUE
. If content is being stretched in unexpected ways, set it to FALSE
to allow the content (or rows
and columns
defined sizes) to control the grid size instead.
Optional Media breakpoints to use. Will default to the current active breakpoint system.
+For other arguments that support breakpoints, instead of simply passing them a value, you can also provide a named list of valid values.
+The names used in that list can be any of the registered breakpoints available in the provided breakpoint_system
argument (defaults to activeBreakpoints()
), as well as the reserved keyord default
.
See vignette("imola-breakpoints")
for more details on breakpoints.
The panel ID. A randomly generated one is used by default. Providing your own ID will allow you to target the generated HTML tag via CSS or JavaScript if needed.
+General rules regarding HTML Ids apply, including the fact that duplicated Ids are not allowed.
+For a full list of details on the HTML ID attribute, check https://www.w3schools.com/html/html_id.asp
+The browser window title (defaults to the host URL of the page). This is the name that appears on the browser tab.
+Flag to tell the page if it should adjust the page to adjust and fill the browser window size.
+When set to true this will force the grid to be at least as tall as the available browser window. This makes the container stretch if the content is smaller, while still allowing it to grow behond the height of the viewport if necessary.
+The set of web dependencies. This value can be a htmlDependency, for example the shiny bootstrap one (the default) or a tagList with diferent dependencies. Useful if you are using a diferent UI framework of package with its own required dependencies (or that requires you to supress bootstrap dependencies)
+Very often during development it is also common that multiple elements share the same layout. In order to easily reuse any layout you create, imola also includes a simple template engine.
+In order to save a template you can use the registerTemplate()
function. This function requires a ‘type’ of template (grid or flex), a name to identify the template later, any named arguments that can be passed to the gridPanel or flexPanel function (depending on the type) and, optionally, a breakpoint_system to use if you plan on adding any responsive attributes to the template (If no breakpoint_system is given, the current active system is used to register the template).
After a template being registered, you can then simple fill in the template
argument on any of the panel or page functions with the name of the template. You can also adjust the template for a specific panel by providing any named arguments in addition to the template name. Any named argument that exists in the template will be orewriten by the named argument on the function call.
Lets say we would like to save our previous areas as a template, and use it. We could use registerTemplate():
+
+registerTemplate("grid", "mytemplate",
+ areas = list(
+ default = c(
+ "area1 area1 area1",
+ "area2 area3 area3",
+ "area2 area3 area3"
+ ),
+ xs = c(
+ "area1",
+ "area2",
+ "area3"
+ )
+ )
+)
We can then use this template to create multiple grid panels in our application:
+
+#in ui
+gridPanel(
+ id = "somePanel",
+ template = "mytemplate",
+ area1 = div("area 1 content"),
+ area2 = div("area 2 content"),
+ area3 = div("area 3 content"),
+)
+
+gridPanel(
+ id = "anotherPanel",
+ template = "mytemplate",
+ area1 = div("different area 1 content"),
+ area2 = div("different area 2 content"),
+ area3 = div("different area 3 content"),
+)
You can register as many templates as you want, but keep in mind that each type + name combo must be unique. You can also remove templates using unregisterTemplate()
if needed. For a full list of registered templates, you can use listTemplates()
.
By default imola also includes some ready to use templates these will also be listed under listTemplates()
.
You can also create templates as R objects and feed that object to the template
argument instead, this can be useful if you do not want to make a template globally accesible (For example for usage only in a single module, or for generating this programatically based on other factors).
In that case, you can use the makeTemplate()
function in a similar way to registerTemplate()
, but assign it to an R object (or invoke it directly on the template
argument).
+templateObject <- makeTemplate("grid",
+ areas = list(
+ default = c(
+ "area1 area1 area1",
+ "area2 area3 area3",
+ "area2 area3 area3"
+ ),
+ xs = c(
+ "area1",
+ "area2",
+ "area3"
+ )
+ )
+)
+# in ui
+gridPanel(
+ id = "somePanel",
+ template = templateObject,
+ area1 = div("area 1 content"),
+ area2 = div("area 2 content"),
+ area3 = div("area 3 content"),
+)
+
+gridPanel(
+ id = "anotherPanel",
+ template = templateObject,
+ area1 = div("different area 1 content"),
+ area2 = div("different area 2 content"),
+ area3 = div("different area 3 content"),
+)
vignettes/imola-why-flex-and-grid.Rmd
+ imola-why-flex-and-grid.Rmd
If you’re been around web development since the 90s, you might remember how different web design used to look.
+Solutions were often complicated, hacky and unintuitive work arounds were common place, and often considered best practices.
+As the web evolved, so did the layout (and styling) options available, maturing and changing to make what used to be convoluted solutions into simpler and more straightforward approaches.
+So how was life before CSS we know now came to be? How were page layouts setup? Why is grid and flexbox considered so much better than the previous solutions, and why are both currently used side by side instead of one over the other?
+Join me for a trip though memory lane as we look into how web layouts evolved since the beginning of the web into the current solutions we now use.
+The original proposals for HTML started in 1989, and lasted until about February 1997, the practical realisation of HTML was about presentation on screens. This included published recommendations, browsers & development tools, and actual web sites.
+At this point web developers where mostly focused on document sharing, so elements focused much more on document requirements such as headers, fonts and paragraphs. In 1993, some style sheet control of layout was proposed, but it would still take some time for it to become mainstream.
+Around this time (as well as during the reign of tables for layout purposes) the HTML document structure dictated the page layout, and the though that you would need more that sequencial content, was barely even considered.
+Early HTML specs state that “The elements within the BODY element are in the order in which they should be presented to the reader”.
+In 1991 ViolaWWW grew into the most sophisticated of the early Web browsers. It is claimed to have been the first browser with support for style sheets, tables, and nest-able HTML elements. Followed in 1993 a proposal was made for “columns” in a presentation language for the web. This proposal did not directly turn into CSS. “The styles defined specify the recommended behaviour of HTML objects in terms of: …. page layout … column ….”.
+These were the initial works of tables in HMTL, and finally in 1997, HTML 3.2 was finally released as a W3C Recommendation. It stated that tables “can be used to markup tabular material or for layout purposes”… And oh boy did people took it literally. Tables became the base for most web pages and considered the go to when structuring content. It was a wild time of invisible gifs used for spacing, convoluted markup and many more terrifying things.
+<!-- html -->
+<table>
+<tr>
+ <td colspan="2">
+ <h1>header</h1>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <ul>
+ <li><a>Home</a></li>
+ <li><a>About</a></li>
+ <li><a>Contact</a></li>
+ </ul>
+ </td>
+ <td>
+ <h2>Title</h2>
+ <p>content</p>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2">
+ <p>footer</p>
+ </td>
+ </tr>
+ </table>
+ header+ |
+ |
+ + | +
+ Title+content + |
+
+ footer + |
+
Eventually a new CSS property made its way to the top, float
. The idea of float was simply, you have an element and need text to wrap around it, float that element and you get exactly that! Simple right? Well…
If you couple this with fixed size for each floated element, a large dose of space clearing techniques, and the CSS position property one could achieve A LOT. This was the conclusion that some clever developers reached in the early 2000’s, allowing multi-column layouts to be created and becoming the go to for these times. Eventually even frameworks started poping out that would hide these hacks behind a css classes and HTML structures, making life even easier. Bootstrap 3 is a great example of such framework that, even now, can be found in millions of websites (even though its newest iteration, bootstrap 4, no longer uses this approach and instead relies on flexbox for its layout system).
+<!-- html -->
+<header>Header</header>
+<main>Main article</main>
+<aside>List of links and news</aside>
+<footer>Copyright information</footer>
/* css */
+
+ main {float: left;
+ width: 60%;
+ margin: 0 5%;
+
+ }
+ aside {width: 25%;
+ margin-left: 70%;
+
+ }
+ footer {clear: left;
+ }
The introduction of the display: flex; property around 2012 was a real step forward, solving many existing layout problems and making web developers and designers generally quite happy.
+This was the true first CSS property to be 100% developed to be focused on layouts, making its way into modern web development quite fast.
+It is specially powerful for page layouts that can defined primarily in terms of either columns or rows. for instance, if we want to align a few elements in a single row, display: flex; provide us with a modern and easy way to do so.
+Not only that, flex has built in control of other properties such as alignment and viual order of the elements without having to change the HTML markup directly. As HTML becomes more and more semantic based, specially with the release of HTML 5, this becomes even more important since it allows us to fully decomple the presentation layer from the content.
+<!-- html -->
+<header>Header</header>
+<div id="main">
+<article>Article</article>
+ <nav>Nav</nav>
+ <aside>Aside</aside>
+ </div>
+<footer>Footer</footer>
/* css */
+#main {
+display: flex;
+
+ }#main > nav,
+#main > aside {
+flex: 0 0 20%;
+
+ }#main > article {
+flex: 1;
+ order: 1;
+
+ }#main > nav {
+order: 3;
+
+ }#main > aside {
+order: 2;
+ }
At this moment in time display: grid;
is the only CSS property intended for building flexible responsive grid layouts. This sort of layout requires a layout area which can be manipulated in two dimensions – both horizontally and vertically – and this is exactly what CSS Grid does.
While display: flex;
gives you full control over either columns or rows, Grid goes one step forward and can handle both rows and columns, meaning that it will always align items to the horizontal and vertical tracks you have set up. Grid is mostly defined on the container, not the children as it is with flexbox.
<!-- html -->
+<div class="page-wrap">
+<header class="page-header">
+
+ Header</header>
+ <nav class="page-nav">
+
+ Nav</nav>
+ <main class="page-main">
+ <article>
+ <p>Article</p>
+ </article>
+ </main>
+ <aside class="page-sidebar">
+
+ Aside</aside>
+ <footer class="page-footer">
+
+ Footer</footer>
+ </div>
/* css */
+.page-wrap {
+display: grid;
+ grid-template-columns: minmax(10px, 1fr) minmax(10px, 3fr);
+ grid-template-rows: min-content min-content 1fr min-content;
+
+ }
+.page-header {
+grid-column: 1/-1;
+
+ }
+.page-sidebar {
+grid-column: 1/2;
+ grid-row: 2/4;
+
+ }
+.page-nav {
+grid-column: 2/3;
+
+ }
+.page-main {
+grid-column: 2/3;
+
+ }
+.page-footer {
+grid-column: 1/-1;
+ }
Article
+So Grid wins!
+Well, No.
+Flexbox and Grid serve different purposes and are both incredibly useful. Sometimes flex is the best solution, sometimes grid makes it simpler, sometimes any of them works.
+Flex and Grid also work very well together, you can easily put a Flex element within a Grid element and vice versa. The important thing to decide in each case is which layout system is best suited for your case.
+When it comes to which approach to use, there are some general rules to make life easier:
+A common rule of thumb here is to use Grid for full page layouts and Flex for everything else, or to consider whether the component you plan to build is one-dimensional (Flex) or two-dimensional (Grid).
+For a more in depth dive into Grid and Flex, make sure to check the vignettes for each for them:
+vignette("imola-flexbox")
for details on Flex.vignette("imola-grid")
for details on Grid.If you’re familiar with CSS, you might have felt by now that layouts in shiny can be awkward to set up.
+While R/shiny does give you direct access to bootstrap’s row and column system, these do have some limitation and changing layouts usually requires having to rebuild a large portion of the UI.
+As web development improved, this system also showed to not always be the most flexible, specially when trying to replicate a design or mockup not using this 12 column system, making it challenging to achieve without a lot of additional custom styling.
+The bread and butter of imola are the gridPanel()
and flexPanel()
functions. These allow you to replace any HTML tag that serves as a container (div
, section
, main
, nav
, …) with a tag that uses one of the specific css layout systems (grid or flexbox).
If you are not familiar with these layout systems, I definitely recommend this article to get you up to speed.
+The basic difference between CSS Grid Layout and CSS Flexbox Layout is that flexbox was designed for layout in one dimension - either a row or a column. Grid was designed for two-dimensional layout - rows, and columns at the same time.
+This means if you want fine control over columns and rows, you should aim at using grid, if you only care about one of the dimensions, flex will most likely work just fine.
+If you are looking for more information on a specific system in imola, make sure to check the following for an in depth view:
+vignette("imola-flexbox")
for details on flexPanel()
and flexPage()
.vignette("imola-grid")
for details on gridPanel()
and gridPage()
.vignette("imola-templates")
for information on using imola’s templating engine.vignette("imola-breakpoints")
for information on using imola’s breakpoint systems.The grid family focuses on providing support for the CSS Grid standard. In imola you can find 2 functions that allow you to create a new grid component. gridPanel()
and gridPage()
. gridPage()
is simply a wrapper for gridPanel()
that allows you to create a page UI element without the need of using any of the built in shiny functions.
If you’re interested in more information about the full array of options in the CSS grid standard from the CSS side, I recommend this article to get you started.
+For more on grid with imola, check vignette("imola-grid")
.
The flex family focuses on providing support for the CSS flexbox standard. In imola you can find 2 functions that allow you to create a new grid component. flexPanel()
and flexPage()
. Similar to what happens in the grid family, flexPage()
is simply a wrapper for flexPanel()
that allows you to create a page UI element without the need of using any of the built in shiny functions.
If you’re interested in more information about the full array of options in the CSS flexbox standard from the CSS side, I recommend this article to get you started.
+For more on flex with imola, check vignette("imola-flex")
.
Breakpoints are a way to adjust your layouts to different screen sizes. As the screen size gets larger or smaller, it is often required to adjust the position or size of the different elements in a page to make sure things dont appear broken or out of place.
+Depending on the CSS framework that is being used in your project, different systems are in place to allow this to be a bit more automated, if you are familiar with base shiny, you might have even used these systems without realizing, by using the fluidRow()
function. fluidRow()
will trigger layout changes to your columns
at specific screen sizes, based on bootstrap 3 breakpoints (The base CSS framework in shiny).
While the fluidRow()
solution is quite easy to use, it also comes with many constrains and does not allow for a very fine control of these layout changes. For complex enough layouts, you might even be required to write additional CSS to add new behavior for specific elements or screen sizes.
Imola takes a slightly different approach to breakpoints; Out of the box it uses the same breakpoints as base shiny (bootstrap 3) and for each attribute you are able to pass either a value for that attribute or a named list of different values for different breakpoints.
+As a practical example, lets say we have the following grid areas in a gridPanel():
+
+# as a gridPanel() argument
+areas = c(
+ "area1 area1 area1",
+ "area2 area3 area3",
+ "area2 area3 area3"
+)
+# or
+areas = list(
+ c("area1", "area1", "area1"),
+ c("area2", "area3", "area3"),
+ c("area2", "area3", "area3")
+)
This grid contains 3 areas (area1
, area2
, area3
), with area2
clearly serving a sidebar. Viewing this grid on a small screen could lead to a very small sidebar, so one solution is to modify this grid specifically for a breakpoint that targets mobile.
As mentioned before, shiny and imola, by default use the bootstrap 3 breakpoint system, that contains a few different breakpoints:
+Screenshot
+We can see what names imola expects for each of these using either activeBreakpoints()
or checking the option directly with getOption("imola.breakpoints")$bootstrap
In out case, we want to target small devices, so we target these via xs
, and build our grid argument as a named list instead.
+# as a gridPanel() argument
+areas = list(
+ default = c(
+ "area1 area1 area1",
+ "area2 area3 area3",
+ "area2 area3 area3"
+ ),
+ xs = c(
+ "area1",
+ "area2",
+ "area3"
+ )
+)
NOTE: Imola reserves the special default
name for values that are used by default, outside of any given breakpoint boundaries (default
is the rule, breakpoints overwrite default
for specific screen sizes).
For more about breakpoints systems with imola, check vignette("imola-breakpoints")
.
Very often during development it is also common that multiple elements share the same layout. In order to easily reuse any layout you create, imola also includes a simple template engine.
+In order to save a template you can use the registerTemplate()
function. This function requires a ‘type’ of template (grid or flex), a name to identify the template later, any named arguments that can be passed to the gridPanel or flexPanel function (depending on the type) and, optionally, a breakpoint_system to use if you plan on adding any responsive attributes to the template (If no breakpoint_system is given, the current active system is used to register the template).
After a template being registered, you can then simple fill in the template
argument on any of the panel or page functions with the name of the template. You can also adjust the template for a specific panel by providing any named arguments in addition to the template name. Any named argument that exists in the template will be orewriten by the named argument on the function call.
Lets say we would like to save our previous areas as a template, and use it. We could use registerTemplate():
+
+#in global.R
+registerTemplate("grid", "mytemplate",
+ areas = list(
+ default = c(
+ "area1 area1 area1",
+ "area2 area3 area3",
+ "area2 area3 area3"
+ ),
+ xs = c(
+ "area1",
+ "area2",
+ "area3"
+ )
+ )
+)
We can then use this template to create multiple grid panels in our application:
+#in ui.R
+gridPanel(
+id = "somePanel"
+ template = "mytemplate"
+ area1 = div("area 1 content"),
+ area2 = div("area 2 content"),
+ area3 = div("area 3 content"),
+
+ )
+gridPanel(
+id = "anotherPanel"
+ template = "mytemplate"
+ area1 = div("different area 1 content"),
+ area2 = div("different area 2 content"),
+ area3 = div("different area 3 content"),
+ )
You can register as many templates as you want, but keep in mind that each type + name combo must be unique. You can also remove templates using unregisterTemplate()
if needed. For a full list of registered templates, you can use listTemplates()
.
By default imola also includes some ready to use templates these will also be listed under listTemplates()
For more on templates with imola, check vignette("imola-templates")
.
Learn how to get started with imola.
+As the web evolved, so did its layout solutions. This article explores what came before and why flexbox and grid became the current defacto solutions for layout generation.
+Getting started with CSS Flexbox and imola.
+Getting started with CSS grid and imola.
+Demos
+Getting started with imola’s layout templates.
+Getting started with imola’s breakpoint systems.
+element 1 | +element 2 | +
+ | + |
+ | + |
+ | + |
1
+2
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+1
+2
+3
+4
+5
+1
+2
+3
+4
+5
+1
+2
+3
+4
+5
+1
+2
+3
+4
+5
+1
+2
+3
+4
+5
+1
+2
+3
+4
+5
+1
2
3
4
5
1
2
3
4
5
1
2
3
4
5
1
2
3
4
5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
1
2
3
4
5
1
2
3
4
5
.item { flex: flex-grow [flex-shrink] [flex-basis]; }
+elemento 1 | elemento 2 |
1
2
1
2
3
4
5
Article
+
+ header+ |
+ |
+ + | +
+ Title+content + |
+
+ footer + |
+