├── .Rbuildignore ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── R ├── tailwind.R ├── tailwind_prose.R └── use_tailwind.R ├── README.Rmd ├── README.md ├── inst ├── example_config │ ├── custom_colors.js │ └── forum_and_typography.js ├── rmarkdown │ └── templates │ │ ├── tailwind │ │ ├── skeleton │ │ │ └── skeleton.Rmd │ │ └── template.yaml │ │ └── tailwind_prose │ │ ├── skeleton │ │ └── skeleton.Rmd │ │ └── template.yaml └── templates │ ├── tailwind │ └── tailwind.html │ └── tailwind_prose │ └── tailwind_prose.html ├── man ├── tailwind.Rd ├── tailwind_prose.Rd └── use_tailwind.Rd ├── tailwindr.Rproj └── vignettes ├── .gitignore ├── images └── tailwind_prose.png └── tailwind_prose.Rmd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^LICENSE\.md$ 4 | ^README\.Rmd$ 5 | inst/dev/* 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | inst/dev 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: tailwindr 2 | Type: Package 3 | Title: TailwindCSS to Rmd 4 | Version: 0.1.0 5 | Authors@R: 6 | c(person(given = "Kyle", 7 | family = "Butts", 8 | role = c("aut","cre"), 9 | email = "kyle.butts@colorado.edu", 10 | comment = c(ORCID = "0000-0002-9048-8059"))) 11 | Maintainer: The package maintainer 12 | Description: Allows TailwindCSS and Tailwind Typography to be used in Rmd files 13 | License: MIT + file LICENSE 14 | Encoding: UTF-8 15 | LazyData: true 16 | Imports: 17 | rmarkdown, 18 | knitr, 19 | htmltools, 20 | xfun, 21 | glue, 22 | stringr 23 | Roxygen: list(markdown = TRUE) 24 | RoxygenNote: 7.1.1 25 | VignetteBuilder: knitr 26 | URL: https://github.com/kylebutts/tailwindr 27 | BugReports: https://github.com/kylebutts/tailwindr/issues 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2021 2 | COPYRIGHT HOLDER: tailwindr authors 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2021 tailwindr authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(tailwind) 4 | export(tailwind_prose) 5 | export(use_tailwind) 6 | -------------------------------------------------------------------------------- /R/tailwind.R: -------------------------------------------------------------------------------- 1 | #' TailwindCSS in Rmd documents 2 | #' 3 | #' @details 4 | #' # About Tailwind 5 | #' 6 | #' TailwindCSS is a utility-based design framework that makes designing simple. 7 | #' It follows the `atomic css` framework that says 1 class = 1 task. For example, 8 | #' the `py-4` class adds `4rem` of padding in the y direction. `text-gray-500` 9 | #' sets the text to the color gray-500 which is part of Tailwind's beautiful 10 | #' default color scheme. `font-semibold` sets the font weight to 600. 11 | #' 12 | #' For responsive design, you can add prefixes to class names. For example 13 | #' `px-2 md:px-4` increases the x-direction padding on medium or larger screens. 14 | #' There are a ton of cool features, for example 15 | #' `bg-gradient-to-r from-yellow-400 via-red-500 to-pink-500` crates a gradient 16 | #' background from yellow-400 to pink-500 passing via red-500 (see it here: 17 | #' \url{https://tailwindcss.com/docs/background-image#linear-gradients}). 18 | #' 19 | #' For complete documentation, see \url{https://tailwindcss.com/docs/} 20 | #' 21 | #' # Tailwind Typography 22 | #' 23 | #' Uses Tailwind Typography. This is an opinionated css framework that creates 24 | #' beautiful text-based documents that work incredibly well for .Rmd documents. 25 | #' For more information visit 26 | #' \url{https://github.com/tailwindlabs/tailwindcss-typography}. 27 | #' 28 | #' # `self_contained` Option 29 | #' There are two option for compiling CSS: 30 | #' 31 | #' 1. `self_contained: true` 32 | #' Requires Node (npm) to be installed on system. This option will post_process 33 | #' the knitted document and create the custom tailwind css. For example, 34 | #' css classes with the `@apply` tag will be compiled and tailwind will be loaded. 35 | #' 36 | #' The parameter `slim_css` uses PostCSS to only include the css classes that 37 | #' appear in the final html document. This is great for keeping files very 38 | #' small, but bad if you are trying to edit through chrome or firefox for example. 39 | #' I recommend putting `slim_css: false` into the yaml while developing and 40 | #' `slim_css: true` when ready to finish. 41 | #' 42 | #' It is possible to pass a custom tailwind configuration using the standard 43 | #' format (\url{https://tailwindcss.com/docs/configuration}). This will be 44 | #' passed to the node script as required. 45 | #' 46 | #' 2. `self_contained: false` 47 | #' Does not require node (npm) to be installed. Instead of post_processing the 48 | #' css, instead this option will use Tailwind Just-in-time Compiler which allows 49 | #' css to be generated as needed in the document. This requires internet 50 | #' connection, though. This is great for opening documents and trying out 51 | #' classes with Chrome/Firefox Developer Tools. For more infomration on the 52 | #' Just-in-time feature, see 53 | #' \url{https://beyondco.de/blog/tailwind-jit-compiler-via-cdn}. 54 | #' 55 | #' Tailwind configuration is also possible in this mode. However, it requires 56 | #' a non-standard config file. See `tailwindr::use_tailwind` for details. 57 | #' 58 | #' # Custom CSS 59 | #' 60 | #' Custom css can use the `@apply` directives that come with tailwind to easily 61 | #' compile set of classes. See 62 | #' \url{https://tailwindcss.com/docs/functions-and-directives#apply} for 63 | #' more details. It just **has** to be passed to the use_tailwind function if 64 | #' you want to use the `@apply` directive. 65 | #' 66 | #' ## Example css 67 | #' 68 | #' For example, we could create a custom button with class btn. And create a 69 | #' blue and red variant 70 | #' 71 | #' ```{css} 72 | #' /* File: style.css */ 73 | #' 74 | #' .btn { 75 | #' @apply font-bold py-2 px-4 rounded; 76 | #' } 77 | #' .btn-blue { 78 | #' @apply bg-blue-500 hover:bg-blue-700 text-white; 79 | #' } 80 | #' .btn-red { 81 | #' @apply bg-red-500 hover:bg-red-700 text-white; 82 | #' } 83 | #' ``` 84 | #' 85 | #' Custom css is possible by passing objects to the `css` yaml parameter. 86 | #' Importantly, you can use the `@apply` directives that come with tailwind 87 | #' to easily compile set of classes. See 88 | #' \url{https://tailwindcss.com/docs/functions-and-directives#apply} 89 | #' for more details. 90 | #' 91 | #' 92 | #' 93 | #' 94 | #' @param highlight Syntax highlighting style. Supported styles include 95 | #' "default", "tango", "pygments", "kate", "monochrome", "espresso", "zenburn", 96 | #' "haddock", and "textmate". Pass NULL to prevent syntax highlighting. 97 | #' @param slim_css Whether or not to include entirety of TailwindCSS or not. See 98 | #' Details for more information. 99 | #' @param self_contained Produce a standalone HTML file with no external 100 | #' dependencies, using data URIs to incorporate the contents of linked scripts, 101 | #' stylesheets, images, and videos. Note that if true, this requires Node (npm) 102 | #' to be installed on the system 103 | #' @param css CSS files to include. See Details for more details on using 104 | #' `@apply`. 105 | #' @param tailwind_config Custom tailwind config file. 106 | #' If `self_contained` is true, this is the standard config format described 107 | #' by \url{https://tailwindcss.com/docs/configuration} 108 | #' If `self_contained` is false, then you should use a config for the JIT CDN 109 | #' version following the details in `tailwindr::use_tailwind`. 110 | #' @param clean_supporting Logical. Whether or not to clear supporting files. 111 | #' Default is TRUE. 112 | #' @param template Pandoc template to use for rendering. Pass `NULL` to use 113 | #' built-in tailwand template. See `example` folder in source code for example 114 | #' of using Tailwind CSS in template. 115 | #' @param ... Additional arguments passed to `rmarkdown::html_document` base_format. 116 | #' 117 | #' @export 118 | #' 119 | tailwind = function(highlight = "zenburn", css = NULL, tailwind_config = NULL, slim_css = FALSE, self_contained = TRUE, clean_supporting = TRUE, template = NULL, ...) { 120 | 121 | # Store output_dir 122 | output_dir <- "" 123 | files_dir <- "" 124 | compiled_css = c("tailwind_compiled.css") 125 | 126 | # Copy css files (for manual compression later) 127 | pre_processor <- function(metadata, input_file, runtime, knit_meta, files_dir, output_dir) { 128 | # copy supplied output_dir (for use in post-processor) 129 | files_dir <<- files_dir 130 | output_dir <<- output_dir 131 | 132 | invisible(NULL) 133 | } 134 | 135 | # Post processor 136 | # PurgeCSS 137 | post_processor <- function(metadata, input_file, output_file, clean, verbose) { 138 | 139 | output_str <- xfun::read_utf8(output_file) 140 | # output_dir from pre_processor is already defined 141 | 142 | # Input css 143 | css_idx <- which(stringr::str_detect(output_str, "")) 144 | # If no input css, put before 145 | if(length(css_idx) == 0) { 146 | css_idx <- which(stringr::str_detect(output_str, "")) - 1 147 | } 148 | 149 | 150 | if(self_contained) { 151 | # tailwind_prose.css 152 | # this ensures tailwind is loaded 153 | if(!file.exists(file.path(output_dir, "tailwind_prose.css"))) { 154 | cat(' 155 | @tailwind base; 156 | @tailwind components; 157 | @tailwind utilities; 158 | ', file = file.path(output_dir, "tailwind_prose.css")) 159 | } 160 | 161 | # tailwind.config.js 162 | # custom config 163 | if(!is.null(tailwind_config)) { 164 | file.copy( 165 | from = file.path(files_dir, tailwind_config), 166 | to = file.path(output_dir, "tailwind.config.js"), 167 | overwrite = TRUE 168 | ) 169 | } 170 | # default config 171 | else { 172 | if(slim_css) { 173 | if(!file.exists(file.path(output_dir, "tailwind.config.js"))) { 174 | cat(paste0('module.exports = { 175 | purge: { 176 | enabled: true, 177 | content: ["', output_file, '"] 178 | }, 179 | darkMode: false, 180 | theme: { 181 | extend: {}, 182 | }, 183 | variants: { 184 | extend: {}, 185 | }, 186 | plugins: [ 187 | require("@tailwindcss/typography") 188 | ], 189 | }'), file = file.path(output_dir, "tailwind.config.js")) 190 | } 191 | } else { 192 | if(!file.exists(file.path(output_dir, "tailwind.config.js"))) { 193 | cat(paste0('module.exports = { 194 | purge: { 195 | }, 196 | darkMode: false, 197 | theme: { 198 | extend: {}, 199 | }, 200 | variants: { 201 | extend: {}, 202 | }, 203 | plugins: [ 204 | require("@tailwindcss/typography") 205 | ], 206 | }'), file = file.path(output_dir, "tailwind.config.js")) 207 | } 208 | } 209 | } 210 | 211 | # postcss.config.js 212 | if(!file.exists(file.path(output_dir, "postcss.config.js"))) { 213 | cat('// postcss.config.js 214 | module.exports = { 215 | plugins: { 216 | tailwindcss: { }, 217 | autoprefixer: {}, 218 | } 219 | }', file = file.path(output_dir, "postcss.config.js")) 220 | } 221 | 222 | command <- paste0( 223 | # change directory to output directory 224 | "cd ", output_dir, ";", 225 | # install modules 226 | "npm list tailwindcss || npm install tailwindcss@latest;", 227 | "npm list postcss || npm install postcss@latest;", 228 | "npm list autoprefixer || npm install autoprefixer@latest;", 229 | "npm list @tailwindcss/typography || npm install @tailwindcss/typography;", 230 | # Run postcss on css files 231 | "cat ", file.path(output_dir, "tailwind_prose.css"), " ", paste(css, collapse = " "), " | ", 232 | "postcss > tailwind_compiled.css" 233 | ) 234 | system(command) 235 | 236 | # Create URI of style 237 | css_uri <- htmltools::tags$link( 238 | href = xfun::base64_uri(file.path(output_dir, "tailwind_compiled.css")), 239 | rel = "stylesheet" 240 | ) 241 | 242 | if(clean_supporting) { 243 | file.remove( 244 | file.path(output_dir, "postcss.config.js"), 245 | file.path(output_dir, "tailwind_prose.css"), 246 | file.path(output_dir, "tailwind_compiled.css") 247 | ) 248 | if(file.path(files_dir, "tailwind.config.js") != file.path(output_dir, "tailwind.config.js")) { 249 | file.remove(file.path(output_dir, "tailwind.config.js")) 250 | } 251 | } 252 | 253 | 254 | output_str <- append(output_str, 255 | as.character(css_uri), 256 | after = css_idx) 257 | } 258 | if(!self_contained) { 259 | # Default config 260 | if(!is.null(tailwind_config)) { 261 | tailwind_config = system.file("example_config", "forum_and_typography.js", package = "tailwindr") 262 | } 263 | 264 | tailwind_str <- tailwindr::use_tailwind( 265 | css = css, 266 | tailwindConfig = tailwind_config 267 | ) 268 | 269 | output_str <- append(output_str, 270 | as.character(tailwind_str), 271 | after = css_idx) 272 | } 273 | 274 | xfun::write_utf8(output_str, output_file) 275 | output_file 276 | } 277 | 278 | 279 | # Allow custom templates 280 | if(!is.null(template)) template <- system.file(files_dir, template) 281 | if(is.null(template)) template <- system.file("templates/tailwind/tailwind.html", package = "tailwindr") 282 | 283 | # https://github.com/rstudio/rmarkdown/blob/0af6b3556adf6e393b2da23c66c695724ea7bd2d/R/html_notebook.R 284 | # generate actual format 285 | base_format <- rmarkdown::html_document( 286 | fig_width = 8, fig_height = 4, 287 | theme = NULL, highlight = highlight, css = compiled_css, 288 | self_contained = self_contained, 289 | template = template, 290 | ... 291 | ) 292 | 293 | rmarkdown::output_format( 294 | knitr = tailwind_knitr_options(), 295 | pandoc = NULL, 296 | base_format = base_format, 297 | pre_processor = pre_processor, 298 | post_processor = post_processor, 299 | clean_supporting = FALSE 300 | ) 301 | } 302 | 303 | 304 | # From https://github.com/rstudio/rmarkdown/blob/0af6b3556adf6e393b2da23c66c695724ea7bd2d/R/html_notebook.R 305 | # This uses the default knit hooks plus a custom one for figures 306 | # 307 | tailwind_knitr_options <- function() { 308 | 309 | # save original hooks from knitr and restore after we've stored requisite 310 | # hooks for our output format 311 | # This prevents permanently changing knitr hooks outside of this output_format 312 | saved_hooks <- get_knitr_hook_list() 313 | on.exit(set_knitr_hook_list(saved_hooks), add = TRUE) 314 | 315 | 316 | # use 'render_markdown()' to set defatul hooks 317 | # note: this is why we had to do the above 318 | knitr::render_markdown() 319 | 320 | # store original hooks and annotate in format 321 | knit_hooks <- knitr::knit_hooks$get() 322 | 323 | # Update knit_hook for "plot" 324 | knit_hooks$plot = function(x, options) { 325 | cap <- options$fig.cap # figure caption 326 | tags <- htmltools::tags 327 | 328 | style <- paste( 329 | c(sprintf('width: %s', options$out.width), 330 | sprintf('height: %s', options$out.height)), 331 | collapse = '; ' 332 | ) 333 | 334 | if(options$dev == "svg" | options$dev == "svglite") { 335 | as.character(tags$figure( 336 | tags$div(style = style, 337 | # cat svg to HTML 338 | htmltools::HTML(paste(readLines(x), collapse = '')) 339 | ), 340 | tags$figcaption(cap) 341 | )) 342 | } else { 343 | as.character(tags$figure( 344 | tags$img(src = x, alt = cap, style = style), 345 | tags$figcaption(cap) 346 | )) 347 | } 348 | } 349 | 350 | 351 | # return as knitr options 352 | rmarkdown::knitr_options(knit_hooks = knit_hooks) 353 | } 354 | 355 | # From https://github.com/rstudio/rmarkdown/blob/0af6b3556adf6e393b2da23c66c695724ea7bd2d/R/util.R 356 | get_knitr_hook_list <- function(hook_names = NULL) { 357 | if (is.null(hook_names)) 358 | hook_names <- c("knit_hooks", "opts_chunk", "opts_hooks", "opts_knit") 359 | knitr_ns <- asNamespace("knitr") 360 | hook_list <- lapply(hook_names, function(hook_name) { 361 | hooks <- get(hook_name, envir = knitr_ns, inherits = FALSE) 362 | hooks$get() 363 | }) 364 | names(hook_list) <- hook_names 365 | hook_list 366 | } 367 | set_knitr_hook_list <- function(hook_list) { 368 | knitr_ns <- asNamespace("knitr") 369 | enumerate(hook_list, function(hook_name, hook_value) { 370 | hook <- get(hook_name, envir = knitr_ns, inherits = FALSE) 371 | hook$set(hook_value) 372 | }) 373 | } 374 | enumerate <- function(data, f, ...) { 375 | lapply(seq_along(data), function(i) { 376 | f(names(data)[[i]], data[[i]], ...) 377 | }) 378 | } 379 | 380 | # if LHS is NULL, return the RHS 381 | `%n%` = function(x, y) if (is.null(x)) y else x 382 | -------------------------------------------------------------------------------- /R/tailwind_prose.R: -------------------------------------------------------------------------------- 1 | #' TailwindCSS with Tailwind Typography in Rmd documents 2 | #' 3 | #' @details 4 | #' # About Tailwind 5 | #' 6 | #' TailwindCSS is a utility-based design framework that makes designing simple. 7 | #' It follows the `atomic css` framework that says 1 class = 1 task. For example, 8 | #' the `py-4` class adds `4rem` of padding in the y direction. `text-gray-500` 9 | #' sets the text to the color gray-500 which is part of Tailwind's beautiful 10 | #' default color scheme. `font-semibold` sets the font weight to 600. 11 | #' 12 | #' For responsive design, you can add prefixes to class names. For example 13 | #' `px-2 md:px-4` increases the x-direction padding on medium or larger screens. 14 | #' There are a ton of cool features, for example 15 | #' `bg-gradient-to-r from-yellow-400 via-red-500 to-pink-500` crates a gradient 16 | #' background from yellow-400 to pink-500 passing via red-500 (see it here: 17 | #' \url{https://tailwindcss.com/docs/background-image#linear-gradients}). 18 | #' 19 | #' For complete documentation, see \url{https://tailwindcss.com/docs/} 20 | #' 21 | #' # Tailwind Typography 22 | #' 23 | #' Uses Tailwind Typography. This is an opinionated css framework that creates 24 | #' beautiful text-based documents that work incredibly well for .Rmd documents. 25 | #' For more information visit 26 | #' \url{https://github.com/tailwindlabs/tailwindcss-typography}. 27 | #' 28 | #' # `self_contained` Option 29 | #' There are two option for compiling CSS: 30 | #' 31 | #' 1. `self_contained: true` 32 | #' Requires Node (npm) to be installed on system. This option will post_process 33 | #' the knitted document and create the custom tailwind css. For example, 34 | #' css classes with the `@apply` tag will be compiled and tailwind will be loaded. 35 | #' 36 | #' The parameter `slim_css` uses PostCSS to only include the css classes that 37 | #' appear in the final html document. This is great for keeping files very 38 | #' small, but bad if you are trying to edit through chrome or firefox for example. 39 | #' I recommend putting `slim_css: false` into the yaml while developing and 40 | #' `slim_css: true` when ready to finish. 41 | #' 42 | #' It is possible to pass a custom tailwind configuration using the standard 43 | #' format (\url{https://tailwindcss.com/docs/configuration}). This will be 44 | #' passed to the node script as required. 45 | #' 46 | #' 2. `self_contained: false` 47 | #' Does not require node (npm) to be installed. Instead of post_processing the 48 | #' css, instead this option will use Tailwind Just-in-time Compiler which allows 49 | #' css to be generated as needed in the document. This requires internet 50 | #' connection, though. This is great for opening documents and trying out 51 | #' classes with Chrome/Firefox Developer Tools. For more infomration on the 52 | #' Just-in-time feature, see 53 | #' \url{https://beyondco.de/blog/tailwind-jit-compiler-via-cdn}. 54 | #' 55 | #' Tailwind configuration is also possible in this mode. However, it requires 56 | #' a non-standard config file. See `tailwindr::use_tailwind` for details. 57 | #' 58 | #' # Custom CSS 59 | #' 60 | #' Custom css can use the `@apply` directives that come with tailwind to easily 61 | #' compile set of classes. See 62 | #' \url{https://tailwindcss.com/docs/functions-and-directives#apply} for 63 | #' more details. It just **has** to be passed to the use_tailwind function if 64 | #' you want to use the `@apply` directive. 65 | #' 66 | #' ## Example css 67 | #' 68 | #' For example, we could create a custom button with class btn. And create a 69 | #' blue and red variant 70 | #' 71 | #' ```{css} 72 | #' /* File: style.css */ 73 | #' 74 | #' .btn { 75 | #' @apply font-bold py-2 px-4 rounded; 76 | #' } 77 | #' .btn-blue { 78 | #' @apply bg-blue-500 hover:bg-blue-700 text-white; 79 | #' } 80 | #' .btn-red { 81 | #' @apply bg-red-500 hover:bg-red-700 text-white; 82 | #' } 83 | #' ``` 84 | #' 85 | #' Custom css is possible by passing objects to the `css` yaml parameter. 86 | #' Importantly, you can use the `@apply` directives that come with tailwind 87 | #' to easily compile set of classes. See 88 | #' \url{https://tailwindcss.com/docs/functions-and-directives#apply} 89 | #' for more details. 90 | #' 91 | #' 92 | #' 93 | #' 94 | #' @param highlight Syntax highlighting style. Supported styles include 95 | #' "default", "tango", "pygments", "kate", "monochrome", "espresso", "zenburn", 96 | #' "haddock", and "textmate". Pass NULL to prevent syntax highlighting. 97 | #' @param slim_css Whether or not to include entirety of TailwindCSS or not. See 98 | #' Details for more information. 99 | #' @param self_contained Produce a standalone HTML file with no external 100 | #' dependencies, using data URIs to incorporate the contents of linked scripts, 101 | #' stylesheets, images, and videos. Note that if true, this requires Node (npm) 102 | #' to be installed on the system 103 | #' @param css CSS files to include. See Details for more details on using 104 | #' `@apply`. 105 | #' @param tailwind_config Custom tailwind config file. 106 | #' If `self_contained` is true, this is the standard config format described 107 | #' by \url{https://tailwindcss.com/docs/configuration} 108 | #' If `self_contained` is false, then you should use a config for the JIT CDN 109 | #' version following the details in `tailwindr::use_tailwind`. 110 | #' @param clean_supporting Logical. Whether or not to clear supporting files. 111 | #' Default is TRUE. 112 | #' @param template Pandoc template to use for rendering. Pass `NULL` to use 113 | #' built-in tailwind template (or simply don't pass anything). 114 | #' See `example` folder in source code for example of using Tailwind CSS in 115 | #' template. Note you should use `
` to use 116 | #' Tailwind Typography! 117 | #' @param ... Additional arguments passed to `rmarkdown::html_document` base_format. 118 | #' 119 | #' @export 120 | #' 121 | tailwind_prose = function(highlight = "zenburn", css = NULL, tailwind_config = NULL, slim_css = FALSE, self_contained = TRUE, clean_supporting = TRUE, template = NULL, ...) { 122 | 123 | # Store output_dir 124 | output_dir <- "" 125 | files_dir <- "" 126 | compiled_css = c("tailwind_compiled.css") 127 | 128 | # Copy css files (for manual compression later) 129 | pre_processor <- function(metadata, input_file, runtime, knit_meta, files_dir, output_dir) { 130 | # copy supplied output_dir (for use in post-processor) 131 | files_dir <<- files_dir 132 | output_dir <<- output_dir 133 | 134 | invisible(NULL) 135 | } 136 | 137 | 138 | # Post processor 139 | # PurgeCSS 140 | post_processor <- function(metadata, input_file, output_file, clean, verbose) { 141 | 142 | output_str <- xfun::read_utf8(output_file) 143 | # output_dir from pre_processor is already defined 144 | 145 | # Input css 146 | css_idx <- which(stringr::str_detect(output_str, "")) 147 | 148 | # If no input css, put before 149 | if(length(css_idx) == 0) { 150 | css_idx <- which(stringr::str_detect(output_str, "")) - 1 151 | } 152 | 153 | if(self_contained) { 154 | # tailwind_prose.css 155 | # this ensures tailwind is loaded 156 | if(!file.exists(file.path(output_dir, "tailwind_prose.css"))) { 157 | cat(' 158 | @tailwind base; 159 | @tailwind components; 160 | @tailwind utilities; 161 | ', file = file.path(output_dir, "tailwind_prose.css")) 162 | } 163 | 164 | # tailwind.config.js 165 | # custom config 166 | if(!is.null(tailwind_config)) { 167 | file.copy( 168 | from = file.path(files_dir, tailwind_config), 169 | to = file.path(output_dir, "tailwind.config.js"), 170 | overwrite = TRUE 171 | ) 172 | } 173 | # default config 174 | else { 175 | if(slim_css) { 176 | if(!file.exists(file.path(output_dir, "tailwind.config.js"))) { 177 | cat(paste0('module.exports = { 178 | purge: { 179 | enabled: true, 180 | content: ["', output_file, '"] 181 | }, 182 | darkMode: false, 183 | theme: { 184 | extend: {}, 185 | }, 186 | variants: { 187 | extend: {}, 188 | }, 189 | plugins: [ 190 | require("@tailwindcss/typography") 191 | ], 192 | }'), file = file.path(output_dir, "tailwind.config.js")) 193 | } 194 | } else { 195 | if(!file.exists(file.path(output_dir, "tailwind.config.js"))) { 196 | cat(paste0('module.exports = { 197 | purge: { 198 | }, 199 | darkMode: false, 200 | theme: { 201 | extend: {}, 202 | }, 203 | variants: { 204 | extend: {}, 205 | }, 206 | plugins: [ 207 | require("@tailwindcss/typography") 208 | ], 209 | }'), file = file.path(output_dir, "tailwind.config.js")) 210 | } 211 | } 212 | } 213 | 214 | # postcss.config.js 215 | if(!file.exists(file.path(output_dir, "postcss.config.js"))) { 216 | cat('// postcss.config.js 217 | module.exports = { 218 | plugins: { 219 | tailwindcss: { }, 220 | autoprefixer: {}, 221 | } 222 | }', file = file.path(output_dir, "postcss.config.js")) 223 | } 224 | 225 | command <- paste0( 226 | # change directory to output directory 227 | "cd ", output_dir, ";", 228 | # install modules 229 | "npm list tailwindcss || npm install tailwindcss@latest;", 230 | "npm list postcss || npm install postcss@latest;", 231 | "npm list autoprefixer || npm install autoprefixer@latest;", 232 | "npm list @tailwindcss/typography || npm install @tailwindcss/typography;", 233 | # Run postcss on css files 234 | "cat ", file.path(output_dir, "tailwind_prose.css"), " ", paste(css, collapse = " "), " | ", 235 | "postcss > tailwind_compiled.css" 236 | ) 237 | system(command) 238 | 239 | # Create URI of style 240 | css_uri <- htmltools::tags$link( 241 | href = xfun::base64_uri(file.path(output_dir, "tailwind_compiled.css")), 242 | rel = "stylesheet" 243 | ) 244 | 245 | if(clean_supporting) { 246 | file.remove( 247 | file.path(output_dir, "postcss.config.js"), 248 | file.path(output_dir, "tailwind_prose.css"), 249 | file.path(output_dir, "tailwind_compiled.css") 250 | ) 251 | if(file.path(files_dir, "tailwind.config.js") != file.path(output_dir, "tailwind.config.js")) { 252 | file.remove(file.path(output_dir, "tailwind.config.js")) 253 | } 254 | } 255 | 256 | 257 | output_str <- append(output_str, 258 | as.character(css_uri), 259 | after = css_idx) 260 | } 261 | if(!self_contained) { 262 | # Default config 263 | if(!is.null(tailwind_config)) { 264 | tailwind_config = system.file("example_config", "forum_and_typography.js", package = "tailwindr") 265 | } 266 | 267 | tailwind_str <- tailwindr::use_tailwind( 268 | css = css, 269 | tailwindConfig = tailwind_config 270 | ) 271 | 272 | output_str <- append(output_str, 273 | as.character(tailwind_str), 274 | after = css_idx) 275 | } 276 | 277 | xfun::write_utf8(output_str, output_file) 278 | output_file 279 | } 280 | 281 | # Allow custom templates 282 | if(!is.null(template)) template <- system.file(files_dir, template) 283 | if(is.null(template)) template <- system.file("templates/tailwind_prose/tailwind_prose.html", package = "tailwindr") 284 | 285 | 286 | # https://github.com/rstudio/rmarkdown/blob/0af6b3556adf6e393b2da23c66c695724ea7bd2d/R/html_notebook.R 287 | # generate actual format 288 | base_format <- rmarkdown::html_document( 289 | fig_width = 8, fig_height = 4, 290 | theme = NULL, highlight = highlight, css = compiled_css, 291 | self_contained = self_contained, 292 | template = template, 293 | ... 294 | ) 295 | 296 | rmarkdown::output_format( 297 | knitr = tailwind_prose_knitr_options(), 298 | pandoc = NULL, 299 | base_format = base_format, 300 | pre_processor = pre_processor, 301 | post_processor = post_processor, 302 | clean_supporting = FALSE 303 | ) 304 | } 305 | 306 | 307 | # From https://github.com/rstudio/rmarkdown/blob/0af6b3556adf6e393b2da23c66c695724ea7bd2d/R/html_notebook.R 308 | # This uses the default knit hooks plus a custom one for figures 309 | tailwind_prose_knitr_options <- function() { 310 | 311 | # save original hooks from knitr and restore after we've stored requisite 312 | # hooks for our output format 313 | # This prevents permanently changing knitr hooks outside of this output_format 314 | saved_hooks <- get_knitr_hook_list() 315 | on.exit(set_knitr_hook_list(saved_hooks), add = TRUE) 316 | 317 | 318 | # use 'render_markdown()' to set defatul hooks 319 | # note: this is why we had to do the above 320 | knitr::render_markdown() 321 | 322 | # store original hooks and annotate in format 323 | knit_hooks <- knitr::knit_hooks$get() 324 | 325 | # Update knit_hook for "plot" 326 | knit_hooks$plot = function(x, options) { 327 | cap <- options$fig.cap # figure caption 328 | tags <- htmltools::tags 329 | 330 | style <- paste( 331 | c(sprintf('width: %s', options$out.width), 332 | sprintf('height: %s', options$out.height)), 333 | collapse = '; ' 334 | ) 335 | 336 | if(options$dev == "svg" | options$dev == "svglite") { 337 | as.character(tags$figure( 338 | tags$div(style = style, 339 | # cat svg to HTML 340 | htmltools::HTML(paste(readLines(x), collapse = '')) 341 | ), 342 | tags$figcaption(cap) 343 | )) 344 | } else { 345 | as.character(tags$figure( 346 | tags$img(src = x, alt = cap, style = style), 347 | tags$figcaption(cap) 348 | )) 349 | } 350 | } 351 | 352 | 353 | # return as knitr options 354 | rmarkdown::knitr_options(knit_hooks = knit_hooks) 355 | } 356 | 357 | # From https://github.com/rstudio/rmarkdown/blob/0af6b3556adf6e393b2da23c66c695724ea7bd2d/R/util.R 358 | get_knitr_hook_list <- function(hook_names = NULL) { 359 | if (is.null(hook_names)) 360 | hook_names <- c("knit_hooks", "opts_chunk", "opts_hooks", "opts_knit") 361 | knitr_ns <- asNamespace("knitr") 362 | hook_list <- lapply(hook_names, function(hook_name) { 363 | hooks <- get(hook_name, envir = knitr_ns, inherits = FALSE) 364 | hooks$get() 365 | }) 366 | names(hook_list) <- hook_names 367 | hook_list 368 | } 369 | set_knitr_hook_list <- function(hook_list) { 370 | knitr_ns <- asNamespace("knitr") 371 | enumerate(hook_list, function(hook_name, hook_value) { 372 | hook <- get(hook_name, envir = knitr_ns, inherits = FALSE) 373 | hook$set(hook_value) 374 | }) 375 | } 376 | enumerate <- function(data, f, ...) { 377 | lapply(seq_along(data), function(i) { 378 | f(names(data)[[i]], data[[i]], ...) 379 | }) 380 | } 381 | 382 | # if LHS is NULL, return the RHS 383 | `%n%` = function(x, y) if (is.null(x)) y else x 384 | -------------------------------------------------------------------------------- /R/use_tailwind.R: -------------------------------------------------------------------------------- 1 | #' TailwindCSS in Rmarkdown and Shiny applications 2 | #' 3 | #' @details 4 | #' 5 | #' # About Tailwind 6 | #' 7 | #' TailwindCSS is a utility-based design framework that makes designing simple. 8 | #' It follows the `atomic css` framework that says 1 class = 1 task. For example, 9 | #' the `py-4` class adds `4rem` of padding in the y direction. `text-gray-500` 10 | #' sets the text to the color gray-500 which is part of Tailwind's beautiful 11 | #' default color scheme. `font-semibold` sets the font weight to 600. 12 | #' 13 | #' For responsive design, you can add prefixes to class names. For example 14 | #' `px-2 md:px-4` increases the x-direction padding on medium or larger screens. 15 | #' There are a ton of cool features, for example 16 | #' `bg-gradient-to-r from-yellow-400 via-red-500 to-pink-500` crates a gradient 17 | #' background from yellow-400 to pink-500 passing via red-500 (see it here: 18 | #' \url{https://tailwindcss.com/docs/background-image#linear-gradients}). 19 | #' 20 | #' For complete documentation, see \url{https://tailwindcss.com/docs/} 21 | #' 22 | #' # Tailwind Just-in-time Compiler 23 | #' 24 | #' However, the complete set of tailwind css classes is massive (~15mb), so 25 | #' you don't want to load all of these. That is where Tailwind's new Just in 26 | #' Time compiling comes in. It will only load the css classes you use, as you 27 | #' use them. So if your shiny app renders ui dynamically, it will load whenever 28 | #' the UI is rendered. 29 | #' 30 | #' This is all possible thanks to the company Beyond Code who created a browser 31 | #' version of Tailwind Just in Time. See 32 | #' \url{https://beyondco.de/blog/tailwind-jit-compiler-via-cdn}. 33 | #' 34 | #' # Custom CSS 35 | #' 36 | #' Custom css can use the `@apply` directives that come with tailwind to easily 37 | #' compile set of classes. See 38 | #' \url{https://tailwindcss.com/docs/functions-and-directives#apply} for 39 | #' more details. It just **has** to be passed to the use_tailwind function if 40 | #' you want to use the `@apply` directive. 41 | #' 42 | #' ## Example css 43 | #' 44 | #' For example, we could create a custom button with class btn. And create a 45 | #' blue and red variant 46 | #' 47 | #' ```{css} 48 | #' /* File: style.css */ 49 | #' 50 | #' .btn { 51 | #' @apply font-bold py-2 px-4 rounded; 52 | #' } 53 | #' .btn-blue { 54 | #' @apply bg-blue-500 hover:bg-blue-700 text-white; 55 | #' } 56 | #' .btn-red { 57 | #' @apply bg-red-500 hover:bg-red-700 text-white; 58 | #' } 59 | #' ``` 60 | #' 61 | #' # Custom Tailwind Config 62 | #' 63 | #' Custom configuration of tailwind is also possible. If you know Tailwind, 64 | #' things will look slightly different. You need to create a variable 65 | #' `window.tailwindConfig` which will contain the JSON file and you must 66 | #' call `window.tailwindCSS.refresh();` after creating window.tailwindConfig. 67 | #' An example is in the `inst/example_config` folder 68 | #' 69 | #' If you want to use custom modules, for example TailwindTypography, note that 70 | #' you need to use the browser-version (cdn version) and you have to layout 71 | #' the config file in a specific way. 72 | #' `inst/examples/03-modules` in the github repository. 73 | #' 74 | #' ## Example config with custom color 75 | #' 76 | #' Creating color scale is easy with 77 | #' \url{https://javisperez.github.io/tailwindcolorshades/?alice=0990af} 78 | #' 79 | #' ```{js} 80 | #' // File: tailwind.config.js 81 | #' 82 | #' // Define config file 83 | #' window.tailwindConfig = { 84 | #' theme: { 85 | #' extend: { 86 | #' colors: { 87 | #' 'daisy': { 88 | #' '50': '#fefcf6', 89 | #' '100': '#fdfaec', 90 | #' '200': '#faf2d0', 91 | #' '300': '#f7e9b4', 92 | #' '400': '#f1d97c', 93 | #' '500': '#EBC944', 94 | #' '600': '#d4b53d', 95 | #' '700': '#b09733', 96 | #' '800': '#8d7929', 97 | #' '900': '#736221' 98 | #' } 99 | #' } 100 | #' }, 101 | #' } 102 | #' } 103 | #' 104 | #' // Refresh so tailwind JIT CDN can see the config 105 | #' window.tailwindCSS.refresh(); 106 | #' ``` 107 | #' 108 | #' 109 | #' ## Example config with Tailwind Typography and Tailwind Forms 110 | #' ```{js} 111 | #' // File: tailwind.config.js 112 | #' 113 | #' // Load custom CDN modules 114 | #' import tailwindcssTypography from 'https://cdn.skypack.dev/@tailwindcss/typography'; 115 | #' import tailwindcssForms from 'https://cdn.skypack.dev/@tailwindcss/forms'; 116 | #' 117 | #' // Define config file 118 | #' window.tailwindConfig = { 119 | #' plugins: [ 120 | #' tailwindcssTypography, 121 | #' tailwindcssForms 122 | #' ] 123 | #' }; 124 | #' 125 | #' // Refresh so tailwind JIT CDN can see the config 126 | #' window.tailwindCSS.refresh(); 127 | #' 128 | #' ``` 129 | #' 130 | #' @param css Optional. Path to ".css" file. Can use @apply tags from Tiny. 131 | #' @param tailwindConfig Optional. Path to ".js" file containing configuration. 132 | #' Requires two things: 133 | #' 1. The json config needs to be defined as `window.tailwindConfig = {...}` 134 | #' 2. After defining the config, use `window.tailwindCSS.refresh();` 135 | #' See details section `Custom Tailwind Config`for more details on format. 136 | #' 137 | #' @export 138 | use_tailwind = function(css = NULL, tailwindConfig = NULL) { 139 | 140 | # Check files exists 141 | if(!is.null(css)) { 142 | for(i in seq_along(css)) { 143 | if(!file.exists(css[i])) { 144 | stop(glue::glue("File: {css[i]} doesn't exist")) 145 | } 146 | } 147 | } 148 | if(!is.null(tailwindConfig)) { 149 | if(!file.exists(tailwindConfig)) { 150 | stop(glue::glue("File: {tailwindConfig} doesn't exist")) 151 | } 152 | } 153 | 154 | # Initialize html elements 155 | 156 | # tailwindcss-jit 157 | html_cdn <- list(htmltools::HTML("\n")) 158 | 159 | html_css <- NULL 160 | html_config <- NULL 161 | 162 | # Custom CSS 163 | # Requires a css file (no style tags!) 164 | if(!is.null(css)) { 165 | html_css <- lapply(css, function(x) { 166 | htmltools::HTML( 167 | paste0( 168 | "", 171 | collapse = "\n" 172 | ) 173 | ) 174 | }) 175 | } 176 | 177 | # Custom config 178 | # Requires: 179 | # 1. window.tailwindConfig = json object of config 180 | # 2. call window.tailwindCSS.refresh(); 181 | if(!is.null(tailwindConfig)) { 182 | html_config <- list( 183 | htmltools::HTML( 184 | paste0( 185 | "\n", 186 | "" 189 | ) 190 | ), 191 | htmltools::HTML( 192 | "" 193 | ) 194 | ) 195 | } 196 | 197 | htmltools::tagList( 198 | c( 199 | html_cdn, 200 | html_config, 201 | html_css 202 | ) 203 | ) 204 | } 205 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r, include = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "man/figures/README-", 12 | out.width = "100%" 13 | ) 14 | ``` 15 | 16 | # tailwindr 17 | 18 | 19 | 20 | 21 | The goal of tailwindr is to bring TailwindCSS to RMarkdown 22 | 23 | ## Installation 24 | 25 | You can install the development version from [GitHub](https://github.com/) with: 26 | 27 | ``` r 28 | # install.packages("devtools") 29 | devtools::install_github("kylebutts/tailwindr") 30 | ``` 31 | 32 | 33 | ## Tailwind CSS 34 | 35 | Tailwind CSS is a *utility-based* CSS framework that allows really quick and incredibly customizable styling of html all through classes. Here are some example classes 36 | 37 | - `my-4` which sets the margin top and bottom to size `4` (Tailwind has sizes that are consistent across classes. 4 happens to be `1rem`) 38 | - `shadow-sm`/`shadow-md`/`shadow-lg`/`shadow-xl` set a drop shadow on divs 39 | - `text-left`/`text-center`/`text-right` left/center/right- align text. 40 | - `w-#/12` sets a column of width #/12 (similar to bootstrap's grid) 41 | - [Much, much more](https://tailwindcss.com/docs/) 42 | 43 | Writing css in Tailwind is incredibly easy too, with the [`@apply`](https://tailwindcss.com/docs/functions-and-directives#apply) directive. For example, lets say you want to create a blue button class, say `.btn-blue`. I can use the `@apply` directive to autmoatically use a bunch of TailwindCSS utility classes: 44 | 45 | ```css 46 | .btn-blue { 47 | @apply bg-blue-500 hover:bg-blue-700 text-white; 48 | } 49 | ``` 50 | 51 | After compiling the css, setting `class = "btn-blue"` is equivalent to setting `class = "bg-blue-500 hover:bg-blue-700 text-white"`. 52 | 53 | 54 | ## `tailwind_prose` Output Format 55 | 56 | The `tailwindr::tailwind_prose` Rmarkdown output format makes beautiful typography easy by using uses the css framework TailwindCSS and Tailwing Typography. For more information visit . 57 | 58 | To use this Rmd template, you can use the "From Template" option in RStuidio or use the following in your preamble: 59 | 60 | ``` 61 | --- 62 | title: "Template Title" 63 | author: "Your Name" 64 | date: "`r format(Sys.Date(), '%B %d, %Y')`" 65 | output: 66 | tailwindr::tailwind_prose: 67 | highlight: zenburn 68 | self_contained: false 69 | slim_css: TRUE 70 | css: [] 71 | --- 72 | ``` 73 | 74 | 75 | ### Example Output 76 | 77 | View it live by clicking on the image 78 | 79 | 80 | 81 | 82 | 83 | ### Custom templates 84 | 85 | You can also write custom templates to surround the Rmd output in any HTML you want. Look at for an example, or view it live at 86 | 87 | 88 | ### Details 89 | 90 | The parameter 'slim_css' uses PostCSS to strip TailwindCSS to include *only* the css classes that *appear* in the final html document. This is great for keeping files very small, but bad if you are trying to edit through chrome or firefox for example. I recommend putting 'slim_css: false' into the yaml while developing and 'slim_css: true' when ready to finish. This requires [node](https://www.npmjs.com/) (npm) to be installed on your system, but the output document automatically handles compiling all the css. 91 | 92 | Custom css is possible by passing objects to the 'css' yaml parameter. Importantly, you can use the `@apply` directives that come with tailwind to easily compile set of classes. See for more details. You can create and link css files just as normal, but now you can use all the Tailwind css classes by using `@apply`. When you hit Knit, it will automatically process the CSS for you. 93 | 94 | You can also type custom html in Rmd files, so the possibilities are endless... 95 | 96 | 97 | ## `tailwind` Output Format 98 | 99 | There is also a `tailwindr::tailwind` output format that can be used for more general design. You can write custom html templates and pass them to the `template` YAML option. The output format works the same as `tailwindr::tailwind_prose` but doesn't include Tailwind Typography. 100 | 101 | ## Credits 102 | 103 | - [TailwindCSS](https://tailwindcss.com/) and [Tailwind Typography](https://github.com/tailwindlabs/tailwindcss-typography) 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # tailwindr 5 | 6 | 7 | 8 | 9 | The goal of tailwindr is to bring TailwindCSS to RMarkdown 10 | 11 | ## Installation 12 | 13 | You can install the development version from 14 | [GitHub](https://github.com/) with: 15 | 16 | ``` r 17 | # install.packages("devtools") 18 | devtools::install_github("kylebutts/tailwindr") 19 | ``` 20 | 21 | ## Tailwind CSS 22 | 23 | Tailwind CSS is a *utility-based* CSS framework that allows really quick 24 | and incredibly customizable styling of html all through classes. Here 25 | are some example classes 26 | 27 | - `my-4` which sets the margin top and bottom to size `4` (Tailwind 28 | has sizes that are consistent across classes. 4 happens to be 29 | `1rem`) 30 | - `shadow-sm`/`shadow-md`/`shadow-lg`/`shadow-xl` set a drop shadow on 31 | divs 32 | - `text-left`/`text-center`/`text-right` left/center/right- align 33 | text. 34 | - `w-#/12` sets a column of width #/12 (similar to bootstrap’s grid) 35 | - [Much, much more](https://tailwindcss.com/docs/) 36 | 37 | Writing css in Tailwind is incredibly easy too, with the 38 | [`@apply`](https://tailwindcss.com/docs/functions-and-directives#apply) 39 | directive. For example, lets say you want to create a blue button class, 40 | say `.btn-blue`. I can use the `@apply` directive to autmoatically use a 41 | bunch of TailwindCSS utility classes: 42 | 43 | ``` css 44 | .btn-blue { 45 | @apply bg-blue-500 hover:bg-blue-700 text-white; 46 | } 47 | ``` 48 | 49 | After compiling the css, setting `class = "btn-blue"` is equivalent to 50 | setting `class = "bg-blue-500 hover:bg-blue-700 text-white"`. 51 | 52 | ## `tailwind_prose` Output Format 53 | 54 | The `tailwindr::tailwind_prose` Rmarkdown output format makes beautiful 55 | typography easy by using uses the css framework TailwindCSS and Tailwing 56 | Typography. For more information visit 57 | . 58 | 59 | To use this Rmd template, you can use the “From Template” option in 60 | RStuidio or use the following in your preamble: 61 | 62 | --- 63 | title: "Template Title" 64 | author: "Your Name" 65 | date: "August 07, 2021" 66 | output: 67 | tailwindr::tailwind_prose: 68 | highlight: zenburn 69 | self_contained: false 70 | slim_css: TRUE 71 | css: [] 72 | --- 73 | 74 | ### Example Output 75 | 76 | View it live by clicking on the image 77 | 78 | 79 | 80 | 81 | ### Custom templates 82 | 83 | You can also write custom templates to surround the Rmd output in any 84 | HTML you want. Look at 85 | 86 | for an example, or view it live at 87 | 88 | 89 | ### Details 90 | 91 | The parameter ‘slim_css’ uses PostCSS to strip TailwindCSS to include 92 | *only* the css classes that *appear* in the final html document. This is 93 | great for keeping files very small, but bad if you are trying to edit 94 | through chrome or firefox for example. I recommend putting ‘slim_css: 95 | false’ into the yaml while developing and ‘slim_css: true’ when ready to 96 | finish. This requires [node](https://www.npmjs.com/) (npm) to be 97 | installed on your system, but the output document automatically handles 98 | compiling all the css. 99 | 100 | Custom css is possible by passing objects to the ‘css’ yaml parameter. 101 | Importantly, you can use the `@apply` directives that come with tailwind 102 | to easily compile set of classes. See 103 | for more 104 | details. You can create and link css files just as normal, but now you 105 | can use all the Tailwind css classes by using `@apply`. When you hit 106 | Knit, it will automatically process the CSS for you. 107 | 108 | You can also type custom html in Rmd files, so the possibilities are 109 | endless… 110 | 111 | ## `tailwind` Output Format 112 | 113 | There is also a `tailwindr::tailwind` output format that can be used for 114 | more general design. You can write custom html templates and pass them 115 | to the `template` YAML option. The output format works the same as 116 | `tailwindr::tailwind_prose` but doesn’t include Tailwind Typography. 117 | 118 | ## Credits 119 | 120 | - [TailwindCSS](https://tailwindcss.com/) and [Tailwind 121 | Typography](https://github.com/tailwindlabs/tailwindcss-typography) 122 | -------------------------------------------------------------------------------- /inst/example_config/custom_colors.js: -------------------------------------------------------------------------------- 1 | // Define config file 2 | window.tailwindConfig = { 3 | theme: { 4 | extend: { 5 | colors: { 6 | 'daisy': { 7 | '50': '#fefcf6', 8 | '100': '#fdfaec', 9 | '200': '#faf2d0', 10 | '300': '#f7e9b4', 11 | '400': '#f1d97c', 12 | '500': '#EBC944', 13 | '600': '#d4b53d', 14 | '700': '#b09733', 15 | '800': '#8d7929', 16 | '900': '#736221' 17 | }, 18 | 'ruby': { 19 | '50': '#faf4f3', 20 | '100': '#f5e9e8', 21 | '200': '#e6c9c5', 22 | '300': '#d7a8a1', 23 | '400': '#b8665b', 24 | '500': '#9A2515', 25 | '600': '#8b2113', 26 | '700': '#741c10', 27 | '800': '#5c160d', 28 | '900': '#4b120a' 29 | }, 30 | 'alice': { 31 | '50': '#f3f8fa', 32 | '100': '#e7f2f4', 33 | '200': '#c3dde5', 34 | '300': '#9fc9d5', 35 | '400': '#58a1b5', 36 | '500': '#107895', 37 | '600': '#0e6c86', 38 | '700': '#0c5a70', 39 | '800': '#0a4859', 40 | '900': '#083b49' 41 | }, 42 | 'coral': { 43 | '50': '#fef8f4', 44 | '100': '#fef0e9', 45 | '200': '#fcdbc8', 46 | '300': '#fac5a6', 47 | '400': '#f69964', 48 | '500': '#F26D21', 49 | '600': '#da621e', 50 | '700': '#b65219', 51 | '800': '#914114', 52 | '900': '#773510' 53 | }, 54 | 'kelly': { 55 | '50': '#f9faf7', 56 | '100': '#f3f4ee', 57 | '200': '#e0e4d5', 58 | '300': '#cdd4bb', 59 | '400': '#a8b389', 60 | '500': '#829356', 61 | '600': '#75844d', 62 | '700': '#626e41', 63 | '800': '#4e5834', 64 | '900': '#40482a' 65 | }, 66 | 'purple': { 67 | '50': '#fafafb', 68 | '100': '#f6f5f7', 69 | '200': '#e8e5ea', 70 | '300': '#dbd6dd', 71 | '400': '#bfb7c4', 72 | '500': '#a498aa', 73 | '600': '#948999', 74 | '700': '#7b7280', 75 | '800': '#625b66', 76 | '900': '#504a53' 77 | }, 78 | 'slate': { 79 | '50': '#f5f6f6', 80 | '100': '#eaeded', 81 | '200': '#ccd3d3', 82 | '300': '#adb9b9', 83 | '400': '#6f8484', 84 | '500': '#314F4F', 85 | '600': '#2c4747', 86 | '700': '#253b3b', 87 | '800': '#1d2f2f', 88 | '900': '#182727' 89 | } 90 | } 91 | }, 92 | } 93 | } 94 | 95 | // Refresh so tailwind JIT CDN can see the config 96 | window.tailwindCSS.refresh(); 97 | -------------------------------------------------------------------------------- /inst/example_config/forum_and_typography.js: -------------------------------------------------------------------------------- 1 | // Custom CDN modules 2 | import tailwindcssTypography from 'https://cdn.skypack.dev/@tailwindcss/typography'; 3 | import tailwindcssForms from 'https://cdn.skypack.dev/@tailwindcss/forms'; 4 | 5 | // Define config file 6 | window.tailwindConfig = { 7 | plugins: [ 8 | tailwindcssTypography, 9 | tailwindcssForms 10 | ] 11 | }; 12 | 13 | // Refresh so tailwind JIT CDN can see the config 14 | window.tailwindCSS.refresh(); 15 | -------------------------------------------------------------------------------- /inst/rmarkdown/templates/tailwind/skeleton/skeleton.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Template Title" 3 | author: "Your Name" 4 | date: "`r format(Sys.Date(), '%B %d, %Y')`" 5 | output: 6 | tailwindr::tailwind: 7 | highlight: zenburn 8 | self_contained: false 9 | slim_css: TRUE 10 | css: [] 11 | --- 12 | 13 | ```{r setup, include=FALSE} 14 | knitr::opts_chunk$set(echo = TRUE) 15 | ``` 16 | 17 | ## Adding an RMarkdown Template 18 | 19 | This file is what a user will see when they select your template. Make sure that you update the fields in the yaml header. In particular you will want to update the `output` field to whatever format your template requires. 20 | 21 | This is a good place to demonstrate special features that your template provides. Ideally it should knit out-of-the-box, or at least contain clear instructions as to what needs changing. 22 | 23 | Finally, be sure to remove this message! 24 | -------------------------------------------------------------------------------- /inst/rmarkdown/templates/tailwind/template.yaml: -------------------------------------------------------------------------------- 1 | name: TailwindCSS 2 | description: > 3 | TailwindCSS 4 | create_dir: FALSE 5 | -------------------------------------------------------------------------------- /inst/rmarkdown/templates/tailwind_prose/skeleton/skeleton.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Template Title" 3 | author: "Your Name" 4 | date: "`r format(Sys.Date(), '%B %d, %Y')`" 5 | output: 6 | tailwindr::tailwind_prose: 7 | highlight: zenburn 8 | self_contained: false 9 | slim_css: TRUE 10 | css: [] 11 | --- 12 | 13 | ```{r setup, include = FALSE} 14 | knitr::opts_chunk$set( 15 | collapse = TRUE, 16 | comment = "#>", 17 | dpi = 300, 18 | out.width = "100%" 19 | ) 20 | ``` 21 | 22 | -------------------------------------------------------------------------------- /inst/rmarkdown/templates/tailwind_prose/template.yaml: -------------------------------------------------------------------------------- 1 | name: Tailwind Prose 2 | description: > 3 | TailwindCSS with Prose 4 | create_dir: FALSE 5 | -------------------------------------------------------------------------------- /inst/templates/tailwind/tailwind.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | $title$ 4 | 5 | 6 | 7 | 8 | 9 | $if(highlightjs)$ 10 | 11 | 14 | 15 | 20 | 27 | $endif$ 28 | 29 | $if(highlighting-css)$ 30 | 31 | 34 | $endif$ 35 | 36 | 37 | 38 | 39 | 40 | 41 | $for(include-before)$ 42 | $include-before$ 43 | $endfor$ 44 | 45 | 46 | 47 | 48 |
49 | 50 |
51 |
52 |
53 |

$title$

54 |

$subtitle$

55 |
56 |
57 | By $author$ · 58 |
Published on
59 |
60 |
61 |
62 | 63 | 64 | $body$ 65 | 66 |
67 | 68 | 69 | 70 | $for(include-after)$ 71 | $include-after$ 72 | $endfor$ 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /inst/templates/tailwind_prose/tailwind_prose.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | $title$ 4 | 5 | 6 | 7 | 8 | 9 | $if(highlightjs)$ 10 | 11 | 14 | 15 | 20 | 27 | $endif$ 28 | 29 | $if(highlighting-css)$ 30 | 31 | 34 | $endif$ 35 | 36 | 37 | 38 | 39 | 40 | 41 | $for(include-before)$ 42 | $include-before$ 43 | $endfor$ 44 | 45 | 46 | 47 | 48 |
49 | 50 |
51 |
52 |
53 |

$title$

54 |

$subtitle$

55 |
56 |
57 | By $author$ · 58 |
Published on
59 |
60 |
61 |
62 | 63 | 64 |
65 | $body$ 66 |
67 | 68 | 69 |
70 | 71 | 72 | 73 | $for(include-after)$ 74 | $include-after$ 75 | $endfor$ 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /man/tailwind.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tailwind.R 3 | \name{tailwind} 4 | \alias{tailwind} 5 | \title{TailwindCSS in Rmd documents} 6 | \usage{ 7 | tailwind( 8 | highlight = "zenburn", 9 | css = NULL, 10 | tailwind_config = NULL, 11 | slim_css = FALSE, 12 | self_contained = TRUE, 13 | clean_supporting = TRUE, 14 | template = NULL, 15 | ... 16 | ) 17 | } 18 | \arguments{ 19 | \item{highlight}{Syntax highlighting style. Supported styles include 20 | "default", "tango", "pygments", "kate", "monochrome", "espresso", "zenburn", 21 | "haddock", and "textmate". Pass NULL to prevent syntax highlighting.} 22 | 23 | \item{css}{CSS files to include. See Details for more details on using 24 | \verb{@apply}.} 25 | 26 | \item{tailwind_config}{Custom tailwind config file. 27 | If \code{self_contained} is true, this is the standard config format described 28 | by \url{https://tailwindcss.com/docs/configuration} 29 | If \code{self_contained} is false, then you should use a config for the JIT CDN 30 | version following the details in \code{tailwindr::use_tailwind}.} 31 | 32 | \item{slim_css}{Whether or not to include entirety of TailwindCSS or not. See 33 | Details for more information.} 34 | 35 | \item{self_contained}{Produce a standalone HTML file with no external 36 | dependencies, using data URIs to incorporate the contents of linked scripts, 37 | stylesheets, images, and videos. Note that if true, this requires Node (npm) 38 | to be installed on the system} 39 | 40 | \item{clean_supporting}{Logical. Whether or not to clear supporting files. 41 | Default is TRUE.} 42 | 43 | \item{template}{Pandoc template to use for rendering. Pass \code{NULL} to use 44 | built-in tailwand template. See \code{example} folder in source code for example 45 | of using Tailwind CSS in template.} 46 | 47 | \item{...}{Additional arguments passed to \code{rmarkdown::html_document} base_format.} 48 | } 49 | \description{ 50 | TailwindCSS in Rmd documents 51 | } 52 | \section{About Tailwind}{ 53 | TailwindCSS is a utility-based design framework that makes designing simple. 54 | It follows the \verb{atomic css} framework that says 1 class = 1 task. For example, 55 | the \code{py-4} class adds \verb{4rem} of padding in the y direction. \code{text-gray-500} 56 | sets the text to the color gray-500 which is part of Tailwind's beautiful 57 | default color scheme. \code{font-semibold} sets the font weight to 600. 58 | 59 | For responsive design, you can add prefixes to class names. For example 60 | \verb{px-2 md:px-4} increases the x-direction padding on medium or larger screens. 61 | There are a ton of cool features, for example 62 | \verb{bg-gradient-to-r from-yellow-400 via-red-500 to-pink-500} crates a gradient 63 | background from yellow-400 to pink-500 passing via red-500 (see it here: 64 | \url{https://tailwindcss.com/docs/background-image#linear-gradients}). 65 | 66 | For complete documentation, see \url{https://tailwindcss.com/docs/} 67 | } 68 | 69 | \section{Tailwind Typography}{ 70 | Uses Tailwind Typography. This is an opinionated css framework that creates 71 | beautiful text-based documents that work incredibly well for .Rmd documents. 72 | For more information visit 73 | \url{https://github.com/tailwindlabs/tailwindcss-typography}. 74 | } 75 | 76 | \section{\code{self_contained} Option}{ 77 | There are two option for compiling CSS: 78 | \enumerate{ 79 | \item \code{self_contained: true} 80 | Requires Node (npm) to be installed on system. This option will post_process 81 | the knitted document and create the custom tailwind css. For example, 82 | css classes with the \verb{@apply} tag will be compiled and tailwind will be loaded. 83 | } 84 | 85 | The parameter \code{slim_css} uses PostCSS to only include the css classes that 86 | appear in the final html document. This is great for keeping files very 87 | small, but bad if you are trying to edit through chrome or firefox for example. 88 | I recommend putting \code{slim_css: false} into the yaml while developing and 89 | \code{slim_css: true} when ready to finish. 90 | 91 | It is possible to pass a custom tailwind configuration using the standard 92 | format (\url{https://tailwindcss.com/docs/configuration}). This will be 93 | passed to the node script as required. 94 | \enumerate{ 95 | \item \code{self_contained: false} 96 | Does not require node (npm) to be installed. Instead of post_processing the 97 | css, instead this option will use Tailwind Just-in-time Compiler which allows 98 | css to be generated as needed in the document. This requires internet 99 | connection, though. This is great for opening documents and trying out 100 | classes with Chrome/Firefox Developer Tools. For more infomration on the 101 | Just-in-time feature, see 102 | \url{https://beyondco.de/blog/tailwind-jit-compiler-via-cdn}. 103 | } 104 | 105 | Tailwind configuration is also possible in this mode. However, it requires 106 | a non-standard config file. See \code{tailwindr::use_tailwind} for details. 107 | } 108 | 109 | \section{Custom CSS}{ 110 | Custom css can use the \verb{@apply} directives that come with tailwind to easily 111 | compile set of classes. See 112 | \url{https://tailwindcss.com/docs/functions-and-directives#apply} for 113 | more details. It just \strong{has} to be passed to the use_tailwind function if 114 | you want to use the \verb{@apply} directive. 115 | \subsection{Example css}{ 116 | 117 | For example, we could create a custom button with class btn. And create a 118 | blue and red variant\if{html}{\out{
}}\preformatted{ /* File: style.css */ 119 | 120 | .btn \{ 121 | @apply font-bold py-2 px-4 rounded; 122 | \} 123 | .btn-blue \{ 124 | @apply bg-blue-500 hover:bg-blue-700 text-white; 125 | \} 126 | .btn-red \{ 127 | @apply bg-red-500 hover:bg-red-700 text-white; 128 | \} 129 | }\if{html}{\out{
}} 130 | 131 | Custom css is possible by passing objects to the \code{css} yaml parameter. 132 | Importantly, you can use the \verb{@apply} directives that come with tailwind 133 | to easily compile set of classes. See 134 | \url{https://tailwindcss.com/docs/functions-and-directives#apply} 135 | for more details. 136 | } 137 | } 138 | 139 | -------------------------------------------------------------------------------- /man/tailwind_prose.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tailwind_prose.R 3 | \name{tailwind_prose} 4 | \alias{tailwind_prose} 5 | \title{TailwindCSS with Tailwind Typography in Rmd documents} 6 | \usage{ 7 | tailwind_prose( 8 | highlight = "zenburn", 9 | css = NULL, 10 | tailwind_config = NULL, 11 | slim_css = FALSE, 12 | self_contained = TRUE, 13 | clean_supporting = TRUE, 14 | template = NULL, 15 | ... 16 | ) 17 | } 18 | \arguments{ 19 | \item{highlight}{Syntax highlighting style. Supported styles include 20 | "default", "tango", "pygments", "kate", "monochrome", "espresso", "zenburn", 21 | "haddock", and "textmate". Pass NULL to prevent syntax highlighting.} 22 | 23 | \item{css}{CSS files to include. See Details for more details on using 24 | \verb{@apply}.} 25 | 26 | \item{tailwind_config}{Custom tailwind config file. 27 | If \code{self_contained} is true, this is the standard config format described 28 | by \url{https://tailwindcss.com/docs/configuration} 29 | If \code{self_contained} is false, then you should use a config for the JIT CDN 30 | version following the details in \code{tailwindr::use_tailwind}.} 31 | 32 | \item{slim_css}{Whether or not to include entirety of TailwindCSS or not. See 33 | Details for more information.} 34 | 35 | \item{self_contained}{Produce a standalone HTML file with no external 36 | dependencies, using data URIs to incorporate the contents of linked scripts, 37 | stylesheets, images, and videos. Note that if true, this requires Node (npm) 38 | to be installed on the system} 39 | 40 | \item{clean_supporting}{Logical. Whether or not to clear supporting files. 41 | Default is TRUE.} 42 | 43 | \item{template}{Pandoc template to use for rendering. Pass \code{NULL} to use 44 | built-in tailwind template (or simply don't pass anything). 45 | See \code{example} folder in source code for example of using Tailwind CSS in 46 | template. Note you should use \verb{
} to use 47 | Tailwind Typography!} 48 | 49 | \item{...}{Additional arguments passed to \code{rmarkdown::html_document} base_format.} 50 | } 51 | \description{ 52 | TailwindCSS with Tailwind Typography in Rmd documents 53 | } 54 | \section{About Tailwind}{ 55 | TailwindCSS is a utility-based design framework that makes designing simple. 56 | It follows the \verb{atomic css} framework that says 1 class = 1 task. For example, 57 | the \code{py-4} class adds \verb{4rem} of padding in the y direction. \code{text-gray-500} 58 | sets the text to the color gray-500 which is part of Tailwind's beautiful 59 | default color scheme. \code{font-semibold} sets the font weight to 600. 60 | 61 | For responsive design, you can add prefixes to class names. For example 62 | \verb{px-2 md:px-4} increases the x-direction padding on medium or larger screens. 63 | There are a ton of cool features, for example 64 | \verb{bg-gradient-to-r from-yellow-400 via-red-500 to-pink-500} crates a gradient 65 | background from yellow-400 to pink-500 passing via red-500 (see it here: 66 | \url{https://tailwindcss.com/docs/background-image#linear-gradients}). 67 | 68 | For complete documentation, see \url{https://tailwindcss.com/docs/} 69 | } 70 | 71 | \section{Tailwind Typography}{ 72 | Uses Tailwind Typography. This is an opinionated css framework that creates 73 | beautiful text-based documents that work incredibly well for .Rmd documents. 74 | For more information visit 75 | \url{https://github.com/tailwindlabs/tailwindcss-typography}. 76 | } 77 | 78 | \section{\code{self_contained} Option}{ 79 | There are two option for compiling CSS: 80 | \enumerate{ 81 | \item \code{self_contained: true} 82 | Requires Node (npm) to be installed on system. This option will post_process 83 | the knitted document and create the custom tailwind css. For example, 84 | css classes with the \verb{@apply} tag will be compiled and tailwind will be loaded. 85 | } 86 | 87 | The parameter \code{slim_css} uses PostCSS to only include the css classes that 88 | appear in the final html document. This is great for keeping files very 89 | small, but bad if you are trying to edit through chrome or firefox for example. 90 | I recommend putting \code{slim_css: false} into the yaml while developing and 91 | \code{slim_css: true} when ready to finish. 92 | 93 | It is possible to pass a custom tailwind configuration using the standard 94 | format (\url{https://tailwindcss.com/docs/configuration}). This will be 95 | passed to the node script as required. 96 | \enumerate{ 97 | \item \code{self_contained: false} 98 | Does not require node (npm) to be installed. Instead of post_processing the 99 | css, instead this option will use Tailwind Just-in-time Compiler which allows 100 | css to be generated as needed in the document. This requires internet 101 | connection, though. This is great for opening documents and trying out 102 | classes with Chrome/Firefox Developer Tools. For more infomration on the 103 | Just-in-time feature, see 104 | \url{https://beyondco.de/blog/tailwind-jit-compiler-via-cdn}. 105 | } 106 | 107 | Tailwind configuration is also possible in this mode. However, it requires 108 | a non-standard config file. See \code{tailwindr::use_tailwind} for details. 109 | } 110 | 111 | \section{Custom CSS}{ 112 | Custom css can use the \verb{@apply} directives that come with tailwind to easily 113 | compile set of classes. See 114 | \url{https://tailwindcss.com/docs/functions-and-directives#apply} for 115 | more details. It just \strong{has} to be passed to the use_tailwind function if 116 | you want to use the \verb{@apply} directive. 117 | \subsection{Example css}{ 118 | 119 | For example, we could create a custom button with class btn. And create a 120 | blue and red variant\if{html}{\out{
}}\preformatted{ /* File: style.css */ 121 | 122 | .btn \{ 123 | @apply font-bold py-2 px-4 rounded; 124 | \} 125 | .btn-blue \{ 126 | @apply bg-blue-500 hover:bg-blue-700 text-white; 127 | \} 128 | .btn-red \{ 129 | @apply bg-red-500 hover:bg-red-700 text-white; 130 | \} 131 | }\if{html}{\out{
}} 132 | 133 | Custom css is possible by passing objects to the \code{css} yaml parameter. 134 | Importantly, you can use the \verb{@apply} directives that come with tailwind 135 | to easily compile set of classes. See 136 | \url{https://tailwindcss.com/docs/functions-and-directives#apply} 137 | for more details. 138 | } 139 | } 140 | 141 | -------------------------------------------------------------------------------- /man/use_tailwind.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/use_tailwind.R 3 | \name{use_tailwind} 4 | \alias{use_tailwind} 5 | \title{TailwindCSS in Rmarkdown and Shiny applications} 6 | \usage{ 7 | use_tailwind(css = NULL, tailwindConfig = NULL) 8 | } 9 | \arguments{ 10 | \item{css}{Optional. Path to ".css" file. Can use @apply tags from Tiny.} 11 | 12 | \item{tailwindConfig}{Optional. Path to ".js" file containing configuration. 13 | Requires two things: 14 | \enumerate{ 15 | \item The json config needs to be defined as \code{window.tailwindConfig = {...}} 16 | \item After defining the config, use \code{window.tailwindCSS.refresh();} 17 | See details section \verb{Custom Tailwind Config}for more details on format. 18 | }} 19 | } 20 | \description{ 21 | TailwindCSS in Rmarkdown and Shiny applications 22 | } 23 | \section{About Tailwind}{ 24 | TailwindCSS is a utility-based design framework that makes designing simple. 25 | It follows the \verb{atomic css} framework that says 1 class = 1 task. For example, 26 | the \code{py-4} class adds \verb{4rem} of padding in the y direction. \code{text-gray-500} 27 | sets the text to the color gray-500 which is part of Tailwind's beautiful 28 | default color scheme. \code{font-semibold} sets the font weight to 600. 29 | 30 | For responsive design, you can add prefixes to class names. For example 31 | \verb{px-2 md:px-4} increases the x-direction padding on medium or larger screens. 32 | There are a ton of cool features, for example 33 | \verb{bg-gradient-to-r from-yellow-400 via-red-500 to-pink-500} crates a gradient 34 | background from yellow-400 to pink-500 passing via red-500 (see it here: 35 | \url{https://tailwindcss.com/docs/background-image#linear-gradients}). 36 | 37 | For complete documentation, see \url{https://tailwindcss.com/docs/} 38 | } 39 | 40 | \section{Tailwind Just-in-time Compiler}{ 41 | However, the complete set of tailwind css classes is massive (~15mb), so 42 | you don't want to load all of these. That is where Tailwind's new Just in 43 | Time compiling comes in. It will only load the css classes you use, as you 44 | use them. So if your shiny app renders ui dynamically, it will load whenever 45 | the UI is rendered. 46 | 47 | This is all possible thanks to the company Beyond Code who created a browser 48 | version of Tailwind Just in Time. See 49 | \url{https://beyondco.de/blog/tailwind-jit-compiler-via-cdn}. 50 | } 51 | 52 | \section{Custom CSS}{ 53 | Custom css can use the \verb{@apply} directives that come with tailwind to easily 54 | compile set of classes. See 55 | \url{https://tailwindcss.com/docs/functions-and-directives#apply} for 56 | more details. It just \strong{has} to be passed to the use_tailwind function if 57 | you want to use the \verb{@apply} directive. 58 | \subsection{Example css}{ 59 | 60 | For example, we could create a custom button with class btn. And create a 61 | blue and red variant\if{html}{\out{
}}\preformatted{ /* File: style.css */ 62 | 63 | .btn \{ 64 | @apply font-bold py-2 px-4 rounded; 65 | \} 66 | .btn-blue \{ 67 | @apply bg-blue-500 hover:bg-blue-700 text-white; 68 | \} 69 | .btn-red \{ 70 | @apply bg-red-500 hover:bg-red-700 text-white; 71 | \} 72 | }\if{html}{\out{
}} 73 | } 74 | } 75 | 76 | \section{Custom Tailwind Config}{ 77 | Custom configuration of tailwind is also possible. If you know Tailwind, 78 | things will look slightly different. You need to create a variable 79 | \code{window.tailwindConfig} which will contain the JSON file and you must 80 | call \code{window.tailwindCSS.refresh();} after creating window.tailwindConfig. 81 | An example is in the \code{inst/example_config} folder 82 | 83 | If you want to use custom modules, for example TailwindTypography, note that 84 | you need to use the browser-version (cdn version) and you have to layout 85 | the config file in a specific way. 86 | \code{inst/examples/03-modules} in the github repository. 87 | \subsection{Example config with custom color}{ 88 | 89 | Creating color scale is easy with 90 | \url{https://javisperez.github.io/tailwindcolorshades/?alice=0990af}\if{html}{\out{
}}\preformatted{ // File: tailwind.config.js 91 | 92 | // Define config file 93 | window.tailwindConfig = \{ 94 | theme: \{ 95 | extend: \{ 96 | colors: \{ 97 | 'daisy': \{ 98 | '50': '#fefcf6', 99 | '100': '#fdfaec', 100 | '200': '#faf2d0', 101 | '300': '#f7e9b4', 102 | '400': '#f1d97c', 103 | '500': '#EBC944', 104 | '600': '#d4b53d', 105 | '700': '#b09733', 106 | '800': '#8d7929', 107 | '900': '#736221' 108 | \} 109 | \} 110 | \}, 111 | \} 112 | \} 113 | 114 | // Refresh so tailwind JIT CDN can see the config 115 | window.tailwindCSS.refresh(); 116 | }\if{html}{\out{
}} 117 | } 118 | 119 | \subsection{Example config with Tailwind Typography and Tailwind Forms}{\if{html}{\out{
}}\preformatted{ // File: tailwind.config.js 120 | 121 | // Load custom CDN modules 122 | import tailwindcssTypography from 'https://cdn.skypack.dev/@tailwindcss/typography'; 123 | import tailwindcssForms from 'https://cdn.skypack.dev/@tailwindcss/forms'; 124 | 125 | // Define config file 126 | window.tailwindConfig = \{ 127 | plugins: [ 128 | tailwindcssTypography, 129 | tailwindcssForms 130 | ] 131 | \}; 132 | 133 | // Refresh so tailwind JIT CDN can see the config 134 | window.tailwindCSS.refresh(); 135 | 136 | }\if{html}{\out{
}} 137 | } 138 | } 139 | 140 | -------------------------------------------------------------------------------- /tailwindr.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 4 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /vignettes/images/tailwind_prose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kylebutts/tailwindr/e0d87b605fc82f225b0146630af57aec3e33cf49/vignettes/images/tailwind_prose.png -------------------------------------------------------------------------------- /vignettes/tailwind_prose.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Tailwind Prose" 3 | author: "Kyle Butts" 4 | date: "`r format(Sys.Date(), '%B %d, %Y')`" 5 | output: 6 | tailwindr::tailwind_prose: 7 | highlight: zenburn 8 | self_contained: false 9 | slim_css: TRUE 10 | --- 11 | 12 | 13 | ```{r setup, include = FALSE} 14 | knitr::opts_chunk$set( 15 | collapse = TRUE, 16 | comment = "#>", 17 | dpi = 300, 18 | out.width = "100%" 19 | ) 20 | library(tidyverse) 21 | ``` 22 | 23 | ## Tailwind Prose 24 | 25 | [Tailwind Typography](https://github.com/tailwindlabs/tailwindcss-typography) is TailwindCSS automatic styling for articles generated using md. The `tailwindr::tailwind_prose` sets up the document to surround the content with an `
` tag with `class = "prose"`. This is all that needs to be done and Tailwind Typography will make beautiful documents. 26 | 27 | ### Text style 28 | 29 | > Great and inspirational blockquote goes here 30 | 31 | **Bold font** and *italics* are supported as well as `code` too. 32 | 33 | 34 | ### Code blocks 35 | For example, code blocks are displayed beautifully 36 | 37 | ```{r,} 38 | fixest::feols(hp ~ mpg, mtcars) 39 | ``` 40 | 41 | 42 | 43 | ### Including Plots 44 | 45 | You can also embed plots with beautiful captions. 46 | 47 | ```{r penguins, results='hide', warning=FALSE, message=FALSE, echo=TRUE, fig.show="asis", fig.width = 8, fig.height = 4, dpi = 300, out.width = "100%", fig.cap = c("Figure: plot of `penguins` dataset from package `palmerpenguins`")} 48 | library(palmerpenguins) 49 | data(penguins) 50 | 51 | ggplot(data = penguins, aes(x = flipper_length_mm)) + 52 | geom_histogram(aes(fill = species), alpha = 0.5, position = "identity") + 53 | scale_fill_manual(values = c("darkorange","darkorchid","cyan4")) + 54 | labs(x = "Flipper Length (mm)", y = "Count", fill = "Species") + 55 | theme_minimal() 56 | ``` 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | ### TailwindCSS 65 | 66 | You can also use TailwindCSS within this document. For example 67 | 68 |
69 |
70 | 71 | 72 | 73 |
74 |
75 |

76 | Warning this statistical method can be a bit tricky. There is a subtle 77 | assumption that you should really think about and failing to do so will be 78 | a bad thing, so do a good thing and think about it. 79 |

80 |
81 |
82 | 83 | The html code can be inserted directly into the Rmd file as follows. 84 | ```html 85 |
86 |
87 | 88 | 89 | 90 |
91 |
92 |

93 | Warning this statistical method can be a bit tricky. There is a subtle 94 | assumption that you should really think about and failing to do so will be 95 | a bad thing, so do a good thing and think about it. 96 |

97 |
98 |
99 | ``` 100 | 101 | However, for reproducability and clean looking code, you can write helper functions to generate the html directly for you. Here is a function that will automatically create the above html. Add the options `results = 'asis'` and `echo = FALSE` to the code chunk to display the results as HTML without displaying the code. 102 | 103 | ```{r} 104 | library(htmltools) 105 | 106 | warning_box <- function(text = "") { 107 | div(class="flex items-center p-2 rounded-lg shadow-sm bg-yellow-200", 108 | # SVG Logo 109 | div(class = "p-3 mr-4", 110 | HTML(' 111 | 112 | ') 113 | ), 114 | # Text 115 | div(p(class = "text-lg text-grey-700", text)) 116 | ) 117 | 118 | } 119 | 120 | warning_box(text = "Example warning text goes here") 121 | ``` 122 | 123 | Often times once you design a component, it becomes super easy to make variants, for example: 124 | 125 | ```{r, results='asis'} 126 | graph_box <- function(text = "") { 127 | div(class="flex items-center p-2 rounded-lg shadow-sm bg-pink-200", 128 | # SVG Logo 129 | div(class = "p-3 mr-4", 130 | HTML(' 131 | 132 | ') 133 | ), 134 | # Text 135 | div(p(class = "text-lg text-grey-700", text)) 136 | ) 137 | 138 | } 139 | 140 | code_box <- function(text = "") { 141 | div(class="flex items-center p-2 rounded-lg shadow-sm bg-purple-200", 142 | # SVG Logo 143 | div(class = "p-3 mr-4", 144 | HTML(' 145 | 146 | ') 147 | ), 148 | # Text 149 | div(p(class = "text-lg text-grey-700", text)) 150 | ) 151 | 152 | } 153 | 154 | graph_box(text = "Here is a data driven example") 155 | ``` 156 | 157 | ```{r, results = 'asis'} 158 | code_box(text = "Here is an example of code") 159 | ``` 160 | 161 | By the way, the svg icons are created from the fantastic package [`icons`](https://github.com/mitchelloharawild/icons) 162 | 163 | 164 | --------------------------------------------------------------------------------