-
Notifications
You must be signed in to change notification settings - Fork 566
/
advanced-ui.Rmd
585 lines (396 loc) · 31.1 KB
/
advanced-ui.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
# (PART\*) Mastering user interfaces {-}
# Advanced UI {#advanced-ui}
```{r include=FALSE}
knit_print.shiny.tag <- function(x, ...) {
knit_print(as.character(x))
}
library(htmltools)
library(magrittr)
```
## Introduction
The native languages of the web are HTML (for content), CSS (for styling), and JavaScript (for behaviour). Shiny is designed to be accessible for R users who aren't familiar with any of those languages. But if you do speak these languages, you can take full advantage of them with Shiny to customize your apps or extend the Shiny framework.
The previous chapter showed you the higher-level UI functions that come with Shiny. In this chapter, we'll take that a couple of steps deeper by learning how our R code gets translated into the HTML that's ultimately sent to the browser. Armed with that knowledge, you'll be able to add arbitrary HTML and CSS to your Shiny apps with ease.
If you don't know HTML, and aren't keen to learn, you can safely skip this chapter. If you don't know HTML today but may be inclined to learn it, you should be able to make sense of this chapter and understand the relationship between Shiny UI and HTML. If you are familiar with HTML already, you should find this chapter extremely useful--and you can skip over the sections that introduce HTML and CSS.
### Outline {-}
* Section \@ref(introduction-to-html) is a quick but gentle guide to the basics of HTML. It won't turn you into a web designer, but you'll at least be able to understand the rest of the chapter.
* Section \@ref(generating-html) introduces tag objects, and the family of functions that produce them. These functions give us all the flexibility of writing raw HTML, while still retaining the power and syntax of R.
* Section \@ref(using-css) demonstrates several ways of incorporating custom CSS into your app (for example, to customize fonts and colors).
* Section \@ref(dependencies) introduces HTML dependency objects, which can be used to tell Shiny about JavaScript/CSS dependency files that should be used.
## HTML 101 {#introduction-to-html}
To understand how UI functions in R work, let's first talk about HTML, in case you're not familiar with it (or its cousin, XML). If you've worked with HTML before, feel free to skip to Section \@ref(generating-html).
HTML is a _markup language_ for describing web pages. A markup language is just a document format that contains plain text content, plus embedded instructions for annotating, or "marking up", specific sections of that content. These instructions can control the appearance, layout, and behaviour of the text they mark up, and also provide structure to the document.
Here's a simple snippet of HTML:
```html
This time I <em>really</em> mean it!
```
The `<em>` and `</em>` markup instructions indicate that the word `really` should be displayed with special <strong>em</strong>phasis (italics):
```{r echo=FALSE}
tags$blockquote(HTML("This time I <em>really</em> mean it!"))
```
`<em>` is an example of a _start tag_, and `</em>` (note the slash character) is an example of an _end tag_.
### Inline formatting tags
`em` is just one of many HTML tags that are used to format text:
* `<strong>...</strong>` makes text <strong>bold</strong>
* `<u>...</u>` makes text <u>underlined</u>
* `<s>...</s>` makes text <s>strikeout</s>
* `<code>...</code>` makes text <code>monospaced</code>
### Block tags
Another class of tags is used to wrap entire blocks of text. You can use `<p>...</p>` to break text into distinct paragraphs, or `<h3>...</h3>` to turn a line into a subheading.
```{css echo=FALSE}
.sourceCode.html {
white-space: pre-wrap !important;
}
```
```html
<h3>Chapter I. Down the Rabbit-Hole</h3>
<p>Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, ‘and what is the use of a book,’ thought Alice ‘without pictures or conversations?’</p>
<p>So she was considering in her own mind (as well as she could, for the hot day made her feel very sleepy and stupid), whether the pleasure of making a daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly a White Rabbit with pink eyes ran close by her.</p>
```
When rendered, this HTML looks like:
```{r echo=FALSE}
tags$blockquote(
HTML("<h3>Chapter I. Down the Rabbit-Hole</h3>
<p>Alice was beginning to get very tired of sitting by her sister on the bank, and of having nothing to do: once or twice she had peeped into the book her sister was reading, but it had no pictures or conversations in it, ‘and what is the use of a book,’ thought Alice ‘without pictures or conversations?’</p>
<p>So she was considering in her own mind (as well as she could, for the hot day made her feel very sleepy and stupid), whether the pleasure of making a daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly a White Rabbit with pink eyes ran close by her.</p>")
)
```
### Tags with attributes
Some tags need to do more than just demarcate some text. An `<a>` (for "anchor") tag is used to create a hyperlink. It's not enough to just wrap `<a>...</a>` around the link's text, as you also need to specify where the hyperlink points to.
Start tags let you include _attributes_ that customize the appearance or behaviour of the tag. In this case, we'll add an `href` attribute to our `<a>` start tag:
```html
<p>Learn more about <strong>Shiny</strong> at <a href="https://shiny.rstudio.com">this website</a>.</p>
```
```{r echo=FALSE}
tags$blockquote(HTML('<p>Learn more about <strong>Shiny</strong> at <a href="https://shiny.rstudio.com">this website</a>.</p>'))
```
There are [dozens of attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes) that all tags accept, and hundreds of attributes that are specific to particular tags. You don't have to worry about memorizing them all--even full-time web developers don't do that. There are two attributes that are used constantly, though.
The `id` attribute uniquely identifies a tag in a document. That is, no two tags in a single document should share the same `id` value, and each tag can have zero or one `id` value. As far as the web browser is concerned, the `id` attribute is completely optional and has no intrinsic effect on the appearance or behaviour of the rendered tag. However, it's incredibly useful for identifying a tag for special treatment by CSS or JavaScript, and as such, plays a crucial role for Shiny apps.
The `class` attribute provides a way of classifying tags in a document. Unlike `id`, any number of tags can have the same class, and each tag can have multiple classes (space separated). Again, classes don't have an intrinsic effect, but are hugely helpful for using CSS or JavaScript to target groups of tags.
In the following example, we've given a `<p>` tag an id and two classes:
```html
<p id="storage-low-message" class="message warning">Storage space is running low!</p>
```
```{r echo=FALSE}
tags$blockquote(HTML('<p id="storage-low-message" class="message warning">Storage space is running low!</p>'))
```
Here, the `id` and `class` values have had no discernible effect. But we could, for example, write CSS that any elements with the `message` class should appear at the top of the page, and that any elements with the `warning` class should have a yellow background and bold text; and we could write JavaScript that automatically dismisses the message if the storage situation improves.
### Parents and children
In the example above, we have a `<p>` tag that contains some text that contains `<strong>` and `<a>` tags. We can refer to `<p>` as the _parent_ of `<strong>`/`<a>`, and `<strong>`/`<a>` as the _children_ of `<p>`. And naturally, `<strong>` and `<a>` are called _siblings_.
It's often helpful to think of tags and text as forming a tree structure:
```
<p>
├── "Learn more about"
├── <strong>
│ └── "Shiny"
├── "at"
├── <a href="...">
│ └── "this website"
└── "."
```
### Comments
Just as you can use the `#` character to comment out a line of R code, HTML lets you comment out parts of your web page. Use `<!--` to start a comment, and `-->` to end one. Anything between these delimiters will be ignored during the rendering of the web page, although it will still be visible to anyone who looks at your raw HTML by using the browser's View Source command.
```html
<p>This HTML will be seen.</p>
<!-- <p>This HTML will not.</p> -->
<!--
<p>
Nor will this.
</p>
-->
```
```{r echo=FALSE}
tags$blockquote(HTML("<p>This HTML will be seen.</p>
<!-- <p>This HTML will not.</p> -->
<!--
<p>
Nor will this.
</p>
-->"))
```
### Escaping
Any markup language like HTML, where there are characters that have special meaning, needs to provide a way to "escape" those special characters--that is, to insert a special character into the document without invoking its special meaning.
For example, the `<` character in HTML has a special meaning, as it indicates the start of a tag. What if you actually want to insert a `<` character into the rendered document--or, let's say, an entire `<p>` tag?
```html
<p>In HTML, you start paragraphs with "<p>" and end them with "</p>".</p>
```
```{r echo=FALSE}
tags$blockquote(HTML('<p>In HTML, you start paragraphs with "<p>" and end them with "</p>".</p>'))
```
That doesn't look as we intended at all! The browser has no way of knowing that we meant the outer `<p>` and `</p>` to be interpreted as markup, and the inner `<p>` and `</p>` to be interpreted as text.
Instead, we need to escape the inner tags so they become text. The escaped version of `<` is `<`, and `>` is `>`.
```html
<p>In HTML, you start paragraphs with "<p>" and end them with "</p>".</p>
```
```{r echo=FALSE}
tags$blockquote(HTML('<p>In HTML, you start paragraphs with "<p>" and end them with "</p>".</p>'))
```
(Yes, escaped characters look pretty ugly. That's just how it is.)
Each escaped character in HTML starts with `&` and ends with `;`. There are lots of valid sequences of characters that go between, but besides `lt` (less than) and `gt` (greater than), the only one you're likely to need to know is `amp`; `&` is how you insert a `&` character into HTML.
Escaping `<`, `>`, and `&` is mandatory if you don't want them interpreted as special characters; other characters can be expressed as escape sequences, but it's generally not necessary. Escaping `<`, `>`, and `&` is so common and crucial that every web framework contains a function for doing it (in our case it's `htmltools::htmlEscape`), but as we'll see in a moment, Shiny will usually do this for you automatically.
### HTML tag vocabulary
That concludes our whirlwind tour of HTML syntax. This is all you'll need to know to follow the discussion on the pages that follow.
The much larger part of learning HTML is getting to know the actual tags that are available to you, what attributes they offer, and how they work with each other. Fortunately, there are scores of excellent, free HTML tutorials and references online; Mozilla's *[Introduction to HTML](https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML)* is one example.
## Generating HTML with tag objects {#generating-html}
With this background knowledge in place, we can now talk about how to write HTML using R. To do this, we'll use the htmltools package. The htmltools package started life as a handful of functions in Shiny itself, and was later spun off as a standalone package when its usefulness for other packages -- like `rmarkdown` and `htmlwidgets` -- became evident.
In `htmltools`, we create the same trees of parent tags and child tags/text as in raw HTML, but we do so using R function calls instead of angle brackets. For example, this HTML from an earlier example:
```html
<p id="storage-low-message" class="message warning">Storage space is running low!</p>
```
would look like this in R:
```{r}
library(htmltools)
p(id="storage-low-message", class="message warning", "Storage space is running low!")
```
This function call returns a tag object; when printed at the console, it displays its raw HTML code, and when included in Shiny UI, its HTML becomes part of the user interface.
Look carefully and you'll notice:
* The `<p>` tag has become a `p()` *function call*, and the end tag is gone. Instead, the end of the `<p>` tag is indicated by the function call's closing parenthesis.
* The `id` and `class` attributes have become _named_ arguments to `p()`.
* The text contained within `<p>...</p>` has become a string that is passed as an _unnamed_ argument to `p()`.
Let's break down each of these bullets further.
### Using functions to create tags
The htmltools package exports the `p` function for the `<p>` tag, but because there are scores of valid HTML tags, it doesn't export a function for each one. Only the most common HTML tags have a function directly exposed in the htmltools namespace: `<p>`, `<h1>` through `<h6>`, `<a>`, `<br>`, `<div>`, `<span>`, `<pre>`, `<code>`, `<img>`, `<strong>`, `<em>`, and `<hr>`. When writing these tags, you can simply use the tag name as the function name, e.g. `div()` or `pre()`.
To write all other tags, prefix the tag name with `tags$`. For example, to create a `<ul>` tag, there's no dedicated `ul()` function, but you can call `tags$ul()`. The `tags` object is a named list that htmltools provides, and it comes preloaded with almost all of the valid tags in the HTML5 standard.
When writing a lot of HTML from R, you may find it tiresome to keep writing `tags$`. If so, you can use the `withTags` function to wrap an R expression, wherein you can omit the `tags$` prefix. In the following code, we call `ul()` and `li()`, whereas these would normally be `tags$ul()` and `tags$li()`.
```{r}
withTags(
ul(
li("Item one"),
li("Item two")
)
)
```
Finally, in some relatively obscure cases, you may find that not even `tags` supports the tag you have in mind; this may be because the tag is newly added to HTML and has not been incorporated into htmltools yet, or because it's a tag that isn't defined in HTML per se but is still understood by browsers (e.g. the `<circle>` tag from SVG). In these cases, you can fall back to the `tag()` (singular) function and pass it any tag name.
```{r}
tag("circle", list(cx="10", cy="10", r="20", stroke="blue", fill="white"))
```
(Notice that the `tag()` function alone needs its attribute and children wrapped in a separate `list()` object. This is a historical quirk, don't read into it.)
### Using named arguments to create attributes
When calling a tag function, any named arguments become HTML attributes.
```{r eval=FALSE}
# From https://getbootstrap.com/docs/3.4/javascript/#collapse
a(class="btn btn-primary", `data-toggle`="collapse", href="#collapseExample",
"Link with href"
)
```
```{r echo=FALSE}
# From https://getbootstrap.com/docs/3.4/javascript/#collapse
tags$a(class="btn btn-primary", `data-toggle`="collapse", href="#collapseExample",
"Link with href"
) %>% print(browse=FALSE)
```
The preceding example includes some attributes with hyphens in their names. Be sure to quote such names using backticks, or single or double quotes. Quoting is also permitted, but not required, for simple alphanumeric names.
Generally, HTML attribute values should be single-element character vectors, as in the above example. Other simple vector types like integers and logicals will be passed to `as.character()`.
Another valid attribute value is `NA`. This means that the attribute should be included, but without an attribute value at all:
```{r eval=FALSE}
tags$input(type = "checkbox", checked = NA)
```
```{r echo=FALSE}
tags$input(type = "checkbox", checked = NA) %>% print(browse=FALSE)
```
You can also use `NULL` as an attribute value, which means the attribute should be ignored (as if the attribute wasn't included at all). This is helpful for conditionally including attributes.
```{r eval=FALSE}
is_checked <- FALSE
tags$input(type = "checkbox", checked = if (is_checked) NA)
```
```{r echo=FALSE}
is_checked <- FALSE
tags$input(type = "checkbox", checked = if (is_checked) NA) %>% print(browse=FALSE)
```
### Using unnamed arguments to create children
Tag functions interpret unnamed arguments as children. Like regular HTML tags, each tag object can have zero, one, or more children; and each child can be one of several types of objects:
#### Tag objects
Tag objects can contain other tag objects. Trees of tag objects can be nested as deeply as you like.
```{r}
div(
p(
strong(
a(href="https://example.com", "A link")
)
)
)
```
#### Plain text
Tag objects can contain plain text, in the form of single-element character vectors.
```{r}
p("I like turtles.")
```
Note that character vectors of longer length are not supported. Use the `paste` function to collapse such vectors to a single element.
```{r}
str(LETTERS)
div(paste(LETTERS, collapse = ", "))
```
One important characteristic of plain text is that htmltools assumes you want to treat all characters as _plain_ text, including characters that have special meaning in HTML like `<` and `>`. As such, any special characters will be automatically escaped:
```{r}
div("The <strong> tag is used to create bold text.")
```
#### Verbatim HTML {#verbatim-html}
Sometimes, you may have a string that should be interpreted as HTML; similar to plain text, except that special characters like `<` and `>` should retain their special meaning and be treated as markup. You can tell htmltools that these strings should be used verbatim (not escaped) by wrapping them with `HTML()`:
```{r}
html_string <- "I just <em>love</em> writing HTML!"
div(HTML(html_string))
```
**Warning:** Be very careful when using the `HTML()` function! If the string you pass to it comes from an untrusted source, either directly or indirectly, it could compromise the security of your Shiny app via an extremely common type of security vulnerability known as Cross-Site Scripting (XSS).
For example, you may ask a user of your app to provide their name, store that value in a database, and later display that name to a different user of your app. If the name is wrapped in `HTML()`, then the first user could provide a fragment of malicious HTML code in place of their name, which would then be served by Shiny to the second user. While I'm not aware of any such attacks having ever happened with a Shiny app, similar attacks have been carried out against companies as tech-savvy as Facebook, eBay, and Microsoft.
It is safe to use `HTML()` if your Shiny app will only ever be run locally for a single user (not deployed on a server for multiple users), or if you are absolutely sure you know where the HTML string came from and that its contents are safe.
#### Lists
While each call to a tag function can have as many unnamed arguments as you want, you can also pack multiple children into a single argument by using a `tagList()`. The following two code snippets will generate identical results:
```{r}
tags$ul(
tags$li("A"),
tags$li("B"),
tags$li("C")
)
tags$ul(
tagList(
tags$li("A"),
tags$li("B"),
tags$li("C")
)
)
```
In most cases, you can use `list()` instead of `tagList()`; there's only a few places where the difference is important. One place where a list is used is when generating HTML programmatically in a loop. For example, the snippet below uses `lapply` to simplify the previous example:
```{r}
tags$ul(
lapply(LETTERS[1:3], tags$li)
)
```
#### NULL
You can use `NULL` as a tag child. `NULL` children are similar to `NULL` attributes; they're simply ignored, and are only supported to make conditional child items easier to express.
In this example, we use `show_beta_warning` to decide whether or not to show a warning; if not, the result of the `if` clause will be `NULL`.
```{r}
show_beta_warning <- FALSE
div(
h3("Welcome to my Shiny app!"),
if (show_beta_warning) {
div(class = "alert alert-warning", role = "alert",
"Warning: This app is in beta; some features may not work!"
)
}
)
```
#### Mix and match
Tag functions can be called with any number of unnamed arguments, and different types of children can be used within a single call.
```{r}
div(
"Text!",
strong("Tags!"),
HTML("Verbatim <span>HTML!</span>"),
NULL,
tagList(
"Lists!"
)
)
```
## Customizing with CSS {#using-css}
In the previous section, we talked about HTML, the markup language that specifies the structure and content of the page. Now we'll talk about Cascading Style Sheets (CSS), the language that specifies the visual style and layout of the page. Similar to our treatment of HTML, we'll give you an extremely superficial introduction to CSS—just enough to be able to parse the syntax visually, not enough to write your own. Then we'll talk about the mechanisms available in Shiny for adding your own CSS.
### Introduction to CSS
As we saw in the previous sections, we use HTML to create a tree of tags and text. CSS lets us specify directives that control how that tree is rendered; each directive is called a _rule_.
Here's some example CSS that includes two rules: one that causes all `<h3>` tags (level 3 headings) to turn red and italic, and one that hides all `<div class="alert">` tags in the `<footer>`:
```css
h3 {
color: red;
font-style: italic;
}
footer div.alert {
display: none;
}
```
The part of the rule that precedes the opening curly brace is the _selector_; in this case, `h3`. The selector indicates which tags this rule applies to.
The parts of the rule inside the curly braces are _properties_. This particular rule has two properties, each of which is terminated by a semicolon.
#### CSS selectors
The particular selector we used in these cases (`h3` and `footer div.alert`) are very simple, but selectors can be quite complex. You can select tags that match a specific `id` or `class`, select tags based on their parents, select tags based on whether they have sibling tags. You can combine such criteria together using "and", "or", and "not" semantics.
Here are some extremely common selector patterns:
* `.foo` - All tags whose `class` attributes include `foo`
* `div.foo` - All `<div>` tags whose `class` attributes include `foo`
* `#bar` - The tag whose `id` is `bar`
* `div#content p:not(#intro)` - All `<p>` tags inside the `<div>` whose `id` is `content`, except the `<p>` tag whose `id` is `intro`
A full introduction to CSS selectors is outside the scope of this chapter, but if you're interested in learning more, the Mozilla [tutorial on CSS selectors](https://developer.mozilla.org/en-US/docs/Learn/CSS/Introduction_to_CSS/Selectors) is a good place to start.
#### CSS properties
The syntax of CSS properties is very simple and intuitive. What's challenging about CSS properties is that there are so darn many of them. There are dozens upon dozens of properties that control typography, margin and padding, word wrapping and hyphenation, sizing and positioning, borders and shadows, scrolling and overflow, animation and 3D transforms... the list goes on and on.
Here are some examples of common properties:
* `font-family: Open Sans, Helvetica, sans-serif;`
Display text using the Open Sans typeface, if it's available; if not, fall back first to Helvetica, and then to the browser's default sans serif font.
* `font-size: 14pt;`
Set the font size to 14 point.
* `width: 100%; height: 400px;`
Set the width to 100% of the tag's container, and the height to a fixed 400 pixels.
* `max-width: 800px;`
Don't let the tag grow beyond 800 pixels wide.
Most of the effort in mastering CSS is in knowing what properties are available to you, and understanding when and how to use them. Again, the rest of this large topic is outside of our scope, but you can learn more from Mozilla's [CSS resources page](https://developer.mozilla.org/en-US/docs/Web/CSS).
### Including custom CSS in Shiny
Shiny gives you several options for adding custom CSS into your apps. Which method you choose will depend on the amount and complexity of your CSS.
#### Inline style tag with `tags$style`
If you have one or two very simple rules, the easiest way to add CSS is by inserting a `<style>` tag, using `tags$style()`. This can go almost anywhere in your UI.
```{r}
library(shiny)
ui <- fluidPage(
tags$style(HTML("
body, pre { font-size: 18pt; }
"))
)
```
The `HTML()` is only strictly necessary if your CSS code includes special characters like `<`, `>`, or `&`; those characters will be escaped by default, which will lead to invalid CSS. I like to add it as a matter of habit, though you should heed the warning about untrusted input in Section \@ref(verbatim-html).
#### Standalone CSS file with `includeCSS`
The second method is to write a standalone `.css` file, and use the `includeCSS` function to add it to your UI. If you're writing more than a couple of lines of CSS, this gives you the benefit of being able to use a text editor that knows how to interpret CSS. Most text editors, including RStudio IDE, will be able to provide syntax highlighting and autocompletion when editing `.css` files.
For example, if you have a `custom.css` file sitting next to your `app.R`, you can do something like this:
```{r eval=FALSE}
ui <- fluidPage(
includeCSS("custom.css"),
... # the rest of your UI
)
```
The `includeCSS` call will return a `<style>` tag, whose body is the content of `custom.css`.
If you wish for this CSS to be hoisted into the page's `<head>` tag, you can call `tags$head(includeCSS("custom.css"))`, though in practice it doesn't make a lot of difference either way.
#### Standalone CSS file with `<link>` tag
Perhaps you have a large amount of CSS in a standalone `.css` file, and you don't want it to be inserted directly into the HTML page, as `includeCSS` does. You can also choose to serve up the `.css` file at a separate URL, and link to it from your UI.
To do this, create a `www` subdirectory in your application directory (the same directory that contains `app.R`) and put your CSS file there—for example, `www/custom.css`. Then add the following line to your UI:
```{r eval=FALSE}
tags$head(tags$link(rel="stylesheet", type="text/css", href="custom.css"))
```
(Note that the `href` attribute should _not_ include `www`; Shiny makes the contents of the `www` directory available at the root URL path.)
This approach makes sense for CSS that is intended for a specific Shiny app. If you are writing CSS for a reusable component, especially one in an R package, keep reading to learn about HTML dependencies.
## Managing JavaScript/CSS dependencies {#dependencies}
One of the best things about working with HTML is the absolutely mindboggling number of open source JavaScript and/or CSS libraries that you can incorporate into your pages. Such libraries can be as simple as a few dozen styling rules in a single .css file, or as complex as 670KB of minified JavaScript for [visualizing spatial data using WebGL](https://docs.mapbox.com/mapbox-gl-js/overview/).
Whether simple or complex, the first step in using any JavaScript/CSS library is referencing its .js and/or .css files from your HTML. Any well-documented library will have a "Getting Started" section that includes a snippet of HTML, including `<script>` and/or `<link>` tags, that need to be pasted into the special `<head>` section of the webpage.
This puts the onus on the webpage author to know about all the JavaScript/CSS dependencies that are used by any element of the page. For traditional web developers, that's totally normal. But for Shiny (and R Markdown, and htmlwidgets), we wanted to free R users from having to think very much about JavaScript/CSS dependencies at all.
For example, the `sliderInput` that comes with Shiny happens to be implemented using a JavaScript library called [ionRangeSlider](http://ionden.com/a/plugins/ion.rangeSlider/). With the traditional approach, if you wanted to add a `sliderInput` to your Shiny app, you'd also have to make a separate declaration somewhere in your UI to load the .js and .css files for ionRangeSlider. And you'd have to make sure that you only make that declaration once per page, lest you accidentally load a library twice, which can cause some libraries to stop working.
For Shiny, we wanted instead to have R users simply call `sliderInput()`, and let us sort out the necessary dependencies automatically. In fact, we want R users to be able to combine whatever UI objects they want, and just not think about dependencies at all.
To make this possible, we created a first-class object for HTML dependencies.
An HTML dependency object is a description of a single JavaScript/CSS library. As a Shiny app author, you generally don't create these directly. Instead, these objects are created by package authors who want to make reusable UI functions that depend on external JavaScript/CSS—like `sliderInput()`. If you're such a package author, you absolutely should be using HTML dependency objects rather than calling `tags$link()`, `tags$script()`, or `includeCSS` directly.
### Creating HTML dependency objects
To form such an object, you call `htmlDependency` with the following arguments:
1. `name`: The name of the library.
2. `version`: The version number of the library. (If two or more HTML dependency objects are found with the same name but different version numbers, only the one with the latest version number will be loaded.)
3. `src`: A single location where the library's resources (JavaScript, CSS, and/or images) can be found. This is usually an absolute filesystem path, but can also be a web URL. For resources that are bundled into an R package, you can provide a relative filesystem path (relative to the installed package directory, i.e. `system.file(package="pkgname")`) if you provide an additional `package` argument.
4. `script`: Relative paths to JavaScript files that should be loaded.
5. `stylesheet`: Relative paths to CSS files that should be loaded.
Here's an example from the [leaflet.mapboxgl](https://github.com/rstudio/leaflet.mapboxgl) package:
```{r eval=FALSE}
mapbox_gl_dependency <- htmlDependency(
"mapbox-gl",
"0.53.1",
src = "node_modules/mapbox-gl/dist",
package = "leaflet.mapboxgl",
script = "mapbox-gl.js",
stylesheet = "mapbox-gl.css",
all_files = FALSE
)
```
In this example, the leaflet.mapboxgl package has a `node_modules/mapbox-gl/dist` subdirectory that contains two files: `mapbox-gl.js` and `mapbox-gl.css`.
For this particular library, we could also have pointed to these hosted URLs:
https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.1/mapbox-gl.js
https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.1/mapbox-gl.css
We can do this by passing `src=c(href="https://api.tiles.mapbox.com/mapbox-gl-js/v0.53.1/")` and removing the `package` argument. However, be aware that a small but significant number of Shiny users do so from networks that are disconnected from the wider Internet for security reasons, and for those users, this dependency would fail to load.
### Using HTML dependency objects
Once you've created an HTML dependency object, using it is straightforward.
If you have a function that returns a tag object, have that function return a tag object and dependency instead, using a `tagList`.
```{r}
create_mapbox_map <- function(map_id) {
tagList(
div(id = map_id),
mapbox_gl_dependency
)
}
```
You can bundle any number of dependencies with your HTML this way; just add additional dependency arguments to the `tagList`.
TODO: Exercises?