WebDev4R: Using HTML & CSS with {htmltools}

WebDev
Getting Started with CSS is really easy. All you need is a set of CSS keywords and a basic understanding of how to use them. I’ll show you how to get started.
Author

Albert Rapp

Published

March 7, 2024

In the last installment, we learned about the web inspector of any modern web browser. This gave us insights into the structure of a website and the CSS styles that are applied to it. In today’s blog post, we want to build a little something with HTML and CSS ourselves.

And to do it in an R fashion, we will use the htmltools package. This makes it easy to create HTML and CSS code with R. And more importantly, we can focus on just the structure of the HTML tags nested into one another and setting CSS keywords. We don’t have to worry about the overhead of setting up a <body> tag etc.

So after this blog post, you will be able to create a simple HTML and CSS structure with R. And you’ll be equipped with a whole bunch of CSS keywords to style your HTML elements. As always, if you enjoy video content more, you can watch the corresponding video on my YouTube channel:

What we build today

Before we start building, let me show you what we’re going for. It’s nothing fancy. It’s just a div container that contains a headline, a paragraph, a button and a whole bunch of CSS styles. All of that is something I have to do to reimagine the sales page of my data visualization course. So we might as well make a learning experience out of it. Here’s how it looks:

Insightful Data Visualizations for "Uncreative" R Users

Leverage {ggplot2} to make charts that communicate effectively without being a design expert.

Communicate insights now

The structure of the HTML

First, what we want to to is to load the htmltools package and then create a div container with it.

library(htmltools)
div('This is a div container')

What you’d get if you execute this code in your console, is your console displaying

<div>This is a div container</div>

That’s nice to see the structure of the HTML. But it’s not particularly nice if we want to see the rendered version. Luckily, the htmltools package has a function called browsable() that we can use to see the rendered version of the HTML in our RStudio Viewer.

div('This is a div container') |> 
  browsable()
This is a div container


Neat. Now we can iterate inside of RStudio until the output looks like we want. Here, what we want to do is to add a headline, a paragraph and a button to the div container. By now you should know that the corresponding tags for these are h1, p and, well, we haven’t covered buttons yet, have we? But here’s secret for you: In a lot of cases, what looks like a button is actually just a hyperlink with a bunch of CSS styles applied to it. Therefore, we’ll need an a tag.

div(
  h1('This is a headline'),
  p('This is a paragraph'),
  a(
    'This is a button', 
    href = 'https://arapp.thinkific.com/courses/insightful-data-visualizations-for-uncreative-r-users'
  )
) |> 
  browsable()

This is a headline

This is a paragraph

This is a button


Notice that I have inserted the href argument into the a tag. This will simply use the href attribute of the a tag to create a link. In HTML code, this looks like

<a href="https://arapp.thinkific.com/courses/insightful-data-visualizations-for-uncreative-r-users">This is a button</a>

In any case, the output looks a bit crude. But that’s to be expected. After all, we haven’t applied any CSS styles to the tags yet.

Fill content and prepare for CSS

Next, let us fill the content of the tags with something more meaningful.

div(
  h1('Insightful Data Visualizations for "Uncreative" R Users'),
  p(
    "Leverage",
    "{ggplot2}",
    "to make charts that communicate effectively",
    "without being a design expert."
  ),
  a(
    'Communicate insights now', 
    href = 'https://arapp.thinkific.com/courses/insightful-data-visualizations-for-uncreative-r-users'
  )
) |> 
  browsable()

Insightful Data Visualizations for "Uncreative" R Users

Leverage {ggplot2} to make charts that communicate effectively without being a design expert.

Communicate insights now


Notice that I have put the text for the paragraph onto separate lines. The reason for that is that each of these lines will need some custom styling. But more on that later.

For now, notice that in the output the extra lines are not reflected. The reason for that is that all of the tags inside of {htmltools} use only this ... catchall argument. This means that any tag function just accepts whatever is passed to it and the docs tell us that all unnamed arguments simply become “children” of the tag. For a p tag, this means that all of the text is just put into one paragraph.

But there’s more. Named arguments are used to set attributes of the tag. That’s what we have seen in action with href already. You see, there we passed in a named argument (href is the name). So, if we want to add the style attribute to a tag, we can do the same thing but with style.

div(
  h1(
    'Insightful Data Visualizations for "Uncreative" R Users',
    style = css(
      
    )
  ),
  p(
    "Leverage",
    "{ggplot2}",
    "to make charts that communicate effectively",
    "without being a design expert.",
    style = css(
      
    )
  ),
  a(
    'Communicate insights now', 
    href = 'https://arapp.thinkific.com/courses/insightful-data-visualizations-for-uncreative-r-users',
    style = css(
      
    )
  ),
  style = css(
    
  )
) |> 
  browsable()

Insightful Data Visualizations for "Uncreative" R Users

Leverage {ggplot2} to make charts that communicate effectively without being a design expert.

Communicate insights now


Here, I have just inserted the style argument into each tag because I already now that every tag will need a bit of styling. Crucially, I have used the css() function to set the style attribute. This is a function from the htmltools package that allows us to set CSS keywords in R.

You know, normally we have to pass a string to the style attribute of a tag. Something like color:blue; font-size:24px; font-weight: 600;. That’s because CSS works that way. But this is tediuos to write and hard to read. So instead, we use the helper function that translates a list of CSS keywords into a string. Have a look.

css(
  color = 'blue',
  font_size = '24px',
  font_weight = 600
)
## [1] "color:blue;font-size:24px;font-weight:600;"

Much more legible, right? But did you notice the font_size keyword? The output of that was font-size (with a dash - instead of an underscore _). The latter is the correct CSS variable name. But R would not like it if we used a dash in a variable name. Hence, the css() functions lets us use an underscore and then translates it into a dash.

Apply CSS keywords

Now, let’s fill the css() functions with some actual CSS keywords. You have already seen some in the last blog post and here I’ve also already used color, font_size and font_weight. They’re probably easy to understand so that’s why I didn’t explain them before. But just to be sure, I’ll include some explanations on all keywords in the next sections.

Text and font keywords

Let’s begin our journey into more CSS keywords with the keywords that relate to text and fonts. The ones we are going to use here are

  • font_family: Set this to a font family name (actually it’s a bit more complicated but for this blog post it’s okay)
  • font_size: Set this to a font size (e.g. 24px)
  • font_weight: Set this to a font weight (e.g. 600 for bold and 300 for regular)
  • line_height: Set this to a line height (e.g. 1.3)
  • text_decoration: Set this to a text decoration (e.g. none to remove underlines from links)
div(
  h1(
    'Insightful Data Visualizations for "Uncreative" R Users',
    style = css(
      font_family = 'Libre Baskerville',
      font_size = '42px',
      font_weight = 600,
      line_height = 1.3
    )
  ),
  p(
    "Leverage",
    "{ggplot2}",
    "to make charts that communicate effectively",
    "without being a design expert.",
    style = css(
      font_size = '26px',
      line_height = 1.3
    )
  ),
  a(
    "Communicate insights now", 
    href = "https://arapp.thinkific.com/courses/insightful-data-visualizations-for-uncreative-r-users", 
    style = css(
      font_size = '24px',
      font_weight = 600,
      text_decoration = 'none'
    )
  ),
  style = css(
    font_family = 'Source Sans Pro'
  )
) |> 
  browsable()

Insightful Data Visualizations for "Uncreative" R Users

Leverage {ggplot2} to make charts that communicate effectively without being a design expert.

Communicate insights now


This is a great start to remind yourself of the concept of specificity. You see, the font_family keyword is applied to the whole div container and set to Source Sans Pro. But the h1 tag has a font_family keyword set to Libre Baskerville. Thus, the h1 tag will have a different font family than the rest of the div container because the style instruction that we specified in the h1 tag are more specific than the ones in the div container.

Color variables

Next, let us bring some colors into the mix. The variables that we need for that are

  • color: Set this to a text color (e.g. blue)
  • background: Set this to a background color (e.g. #0454a4) or to a function like linear-gradient(45deg, #bcd7e1, #c7dbf0) that creates a gradient instead of a solid color. The 45deg is the angle of the gradient and the two colors are the start and end color of the gradient.
div(
  h1(
    'Insightful Data Visualizations for "Uncreative" R Users',
    style = css(
      font_family = 'Libre Baskerville',
      font_size = '42px',
      font_weight = 600,
      line_height = 1.3
    )
  ),
  p(
    "Leverage",
    "{ggplot2}",
    "to make charts that communicate effectively",
    "without being a design expert.",
    style = css(
      font_size = '26px',
      line_height = 1.3
    )
  ),
  a(
    "Communicate insights now", 
    href = "https://arapp.thinkific.com/courses/insightful-data-visualizations-for-uncreative-r-users", 
    style = css(
      font_size = '24px',
      font_weight = 600,
      text_decoration = 'none',
      background = '#0454a4',                  ### <- Change here
      color = 'white',                         ### <- Change here
    )
  ),
  style = css(
    font_family = 'Source Sans Pro',
    background = "linear-gradient(45deg, #bcd7e1, #c7dbf0)", ### <- Change here
    color = '#333333',                         ### <- Change here
  )
) |> 
  browsable()

Insightful Data Visualizations for "Uncreative" R Users

Leverage {ggplot2} to make charts that communicate effectively without being a design expert.

Communicate insights now


Margins, paddings and displays

Finally, let’s add some spacing and positioning to the mix. The keywords that we need for that are

  • margin: Set this to a margin (e.g. 20px)
  • padding: Set this to a padding (e.g. 20px)

I can already hear you say “But what’s the difference between margin and padding?”. I’m glad you asked young padawan. At first glance, they seem to do the same thing. And I found them confusing when I first learned about it. But here’s the difference:

  • margin sets the space between the border of the element and the next element
  • padding sets the space between the border of the element and the content inside of the element

So, what we want to do in the next is to move the texts further into the div container. It’s just plain ugly when the border of a rectangle is right there where the text is at. This is clear case of using padding on the div container. Let’s do that.

There are multiple ways to use the padding keyword. The most common ones are

  1. padding: 20px; sets the padding to 20px on all sides
  2. padding: 20px 10px; sets the padding to 20px on the top and bottom and 10px on the left and right
  3. padding: 20px 20px 10px 5px; sets the padding to 20px on the top, 20px on the right, 10px on the bottom and 5px on the left

And if you want to change just a single side, you can use padding-top, padding-right, padding-bottom and padding-left instead of padding. Here, let’s try to set the padding to 50px on the top and bottom and 30px on the left and right.

div(
  h1(
    'Insightful Data Visualizations for "Uncreative" R Users',
    style = css(
      font_family = 'Libre Baskerville',
      font_size = '42px',
      font_weight = 600,
      line_height = 1.3
    )
  ),
  p(
    "Leverage",
    "{ggplot2}",
    "to make charts that communicate effectively",
    "without being a design expert.",
    style = css(
      font_size = '26px',
      line_height = 1.3
    )
  ),
  a(
    "Communicate insights now", 
    href = "https://arapp.thinkific.com/courses/insightful-data-visualizations-for-uncreative-r-users", 
    style = css(
      font_size = '24px',
      font_weight = 600,
      text_decoration = 'none',
      background = '#0454a4',
      color = 'white',
    )
  ),
  style = css(
    font_family = 'Source Sans Pro',
    background = "linear-gradient(45deg, #bcd7e1, #c7dbf0)",
    color = '#333333',
    padding = '50px 30px' ### <- Change here
  )
) |> 
  browsable()

Insightful Data Visualizations for "Uncreative" R Users

Leverage {ggplot2} to make charts that communicate effectively without being a design expert.

Communicate insights now


Okay, this worked but it’s not perfect. The top and bottom padding doesn’t look really symmetric. What’s going on there?

And honestly, I don’t know. At least that was my initial reaction. But then I used the web inspector to see what’s going on. If you hover over the h1 tag, you will see that there is a margin applied to it. You can see that by the orange space that appears around the h1 tag.

And if you scroll down in the styles panel, you can actually see a diagramm that tells you that the margin is not zero.

Notice that this also shows you how paddings do not auto-collapse. What I mean by that is that the padding of the div container and the padding of the h1 tag seem to be added together resulting in a huge gap. But that’s not what we want. Hence, if we want to make the padding symmetric, we have to set the margin of the h1 tag to zero.

div(
  h1(
    'Insightful Data Visualizations for "Uncreative" R Users',
    style = css(
      font_family = 'Libre Baskerville',
      font_size = '42px',
      font_weight = 600,
      line_height = 1.3,
      margin = '0px' ### <- Change here
    )
  ),
  p(
    "Leverage",
    "{ggplot2}",
    "to make charts that communicate effectively",
    "without being a design expert.",
    style = css(
      font_size = '26px',
      line_height = 1.3
    )
  ),
  a(
    "Communicate insights now", 
    href = "https://arapp.thinkific.com/courses/insightful-data-visualizations-for-uncreative-r-users", 
    style = css(
      font_size = '24px',
      font_weight = 600,
      text_decoration = 'none',
      background = '#0454a4',
      color = 'white',
    )
  ),
  style = css(
    font_family = 'Source Sans Pro',
    background = "linear-gradient(45deg, #bcd7e1, #c7dbf0)",
    color = '#333333',
    padding = '50px 30px'
  )
) |> 
  browsable()

Insightful Data Visualizations for "Uncreative" R Users

Leverage {ggplot2} to make charts that communicate effectively without being a design expert.

Communicate insights now


Ah much better. Notice that the paragraph is now much closer to the headline. We could have just set margin-top in <h1> to zero and still left the bottom margin but we can want to modify the margins of the paragraph anyway. So it’s all good.

div(
  h1(
    'Insightful Data Visualizations for "Uncreative" R Users',
    style = css(
      font_family = 'Libre Baskerville',
      font_size = '42px',
      font_weight = 600,
      line_height = 1.3,
      margin = '0px'
    )
  ),
  p(
    "Leverage",
    "{ggplot2}",
    "to make charts that communicate effectively",
    "without being a design expert.",
    style = css(
      font_size = '26px',
      line_height = 1.3,
      margin_bottom = '20px',            ### <- Change here
      margin_top = '15px',               ### <- Change here
    )
  ),
  a(
    "Communicate insights now", 
    href = "https://arapp.thinkific.com/courses/insightful-data-visualizations-for-uncreative-r-users", 
    style = css(
      font_size = '24px',
      font_weight = 600,
      text_decoration = 'none',
      background = '#0454a4',
      color = 'white',
    )
  ),
  style = css(
    font_family = 'Source Sans Pro',
    background = "linear-gradient(45deg, #bcd7e1, #c7dbf0)",
    color = '#333333',
    padding = '50px 30px'
  )
) |> 
  browsable()

Insightful Data Visualizations for "Uncreative" R Users

Leverage {ggplot2} to make charts that communicate effectively without being a design expert.

Communicate insights now


Next, let us deal with our ugly button. We can apply the same trick and use the padding keyword to make the button look a bit more appealing.

div(
  h1(
    'Insightful Data Visualizations for "Uncreative" R Users',
    style = css(
      font_family = 'Libre Baskerville',
      font_size = '42px',
      font_weight = 600,
      line_height = 1.3,
      margin = '0px'
    )
  ),
  p(
    "Leverage",
    "{ggplot2}",
    "to make charts that communicate effectively",
    "without being a design expert.",
    style = css(
      font_size = '26px',
      line_height = 1.3,
      margin_bottom = '20px',
      margin_top = '15px',
    )
  ),
  a(
    "Communicate insights now", 
    href = "https://arapp.thinkific.com/courses/insightful-data-visualizations-for-uncreative-r-users", 
    style = css(
      font_size = '24px',
      font_weight = 600,
      text_decoration = 'none',
      background = '#0454a4',
      color = 'white',
      padding = '15px',                      ### <- Change here
    )
  ),
  style = css(
    font_family = 'Source Sans Pro',
    background = "linear-gradient(45deg, #bcd7e1, #c7dbf0)",
    color = '#333333',
    padding = '50px 30px'
  )
) |> 
  browsable()

Insightful Data Visualizations for "Uncreative" R Users

Leverage {ggplot2} to make charts that communicate effectively without being a design expert.

Communicate insights now


Nice, our button looks a bit more appealing now. But notice how the space between the paragraph and the button decreased quite a lot. You’d think that the 20px margin-bottom of the paragraph would have prevented that. But somehow the padding isn’t taken into account when the margin is calculated. Have a look how the padding of the button and the margin of the paragraph overlap.

And the reason for that is simple but it can drive you mad if you don’t know what you’re looking for. The thing is that the a tag is an inline element. And inline elements behave differently. Their paddings certainly don’t seem to respect the margin keyword of adjacent blocks. So that’s why we have to tell the inline element to behave like a block element instead. We do that with the display keyword.

div(
  h1(
    'Insightful Data Visualizations for "Uncreative" R Users',
    style = css(
      font_family = 'Libre Baskerville',
      font_size = '42px',
      font_weight = 600,
      line_height = 1.3,
      margin = '0px'
    )
  ),
  p(
    "Leverage",
    "{ggplot2}",
    "to make charts that communicate effectively",
    "without being a design expert.",
    style = css(
      font_size = '26px',
      line_height = 1.3,
      margin_bottom = '20px',
      margin_top = '15px',
    )
  ),
  a(
    "Communicate insights now", 
    href = "https://arapp.thinkific.com/courses/insightful-data-visualizations-for-uncreative-r-users", 
    style = css(
      font_size = '24px',
      font_weight = 600,
      text_decoration = 'none',
      background = '#0454a4',
      color = 'white',
      padding = '15px',
      display = 'block'                     ### <- Change here
    )
  ),
  style = css(
    font_family = 'Source Sans Pro',
    background = "linear-gradient(45deg, #bcd7e1, #c7dbf0)",
    color = '#333333',
    padding = '50px 30px'
  )
) |> 
  browsable()

Insightful Data Visualizations for "Uncreative" R Users

Leverage {ggplot2} to make charts that communicate effectively without being a design expert.

Communicate insights now


Well that worked quite nicely. The padding of the button is now respected and the space between the paragraph and the button is back to normal. Check it out in the web inspector if you don’t believe me.

But as cool as that is, the button now fills the whole line. That’s not what we want. But that’s how blocks behave: They fill the whole line they’re in. That was the reason why we used an inline element in the first place. Because inline elements don’t fill the whole line.

If only we could have a block element and an inline element both at once. Something like inline-block element. That would be convenient. And it’s a good thing that we can actually do that with the display keyword as well.

div(
  h1(
    'Insightful Data Visualizations for "Uncreative" R Users',
    style = css(
      font_family = 'Libre Baskerville',
      font_size = '42px',
      font_weight = 600,
      line_height = 1.3,
      margin = '0px'
    )
  ),
  p(
    "Leverage",
    "{ggplot2}",
    "to make charts that communicate effectively",
    "without being a design expert.",
    style = css(
      font_size = '26px',
      line_height = 1.3,
      margin_bottom = '20px',
      margin_top = '15px',
    )
  ),
  a(
    "Communicate insights now", 
    href = "https://arapp.thinkific.com/courses/insightful-data-visualizations-for-uncreative-r-users", 
    style = css(
      font_size = '24px',
      font_weight = 600,
      text_decoration = 'none',
      background = '#0454a4',
      color = 'white',
      padding = '15px',
      display = 'inline-block'                     ### <- Change here
    )
  ),
  style = css(
    font_family = 'Source Sans Pro',
    background = "linear-gradient(45deg, #bcd7e1, #c7dbf0)",
    color = '#333333',
    padding = '50px 30px'
  )
) |> 
  browsable()

Insightful Data Visualizations for "Uncreative" R Users

Leverage {ggplot2} to make charts that communicate effectively without being a design expert.

Communicate insights now


Miscellaneous keywords

Finally, let’s learn two more keywords that are quite useful. One is border_radius and the other is box_shadow. With the former we can make the button and the div container look a bit more appealing by rounding off their edges. And with the latter we can give a little bit of depth to the button. The arguments for box_shadow are x_offset, y_offset, blur_radius and shadow color. You can play around with these to get the shadow that you want.

div(
  h1(
    'Insightful Data Visualizations for "Uncreative" R Users',
    style = css(
      font_family = 'Libre Baskerville',
      font_size = '42px',
      font_weight = 600,
      line_height = 1.3,
      margin = '0px'
    )
  ),
  p(
    "Leverage",
    "{ggplot2}",
    "to make charts that communicate effectively",
    "without being a design expert.",
    style = css(
      font_size = '26px',
      line_height = 1.3,
      margin_bottom = '20px',
      margin_top = '15px',
    )
  ),
  a(
    "Communicate insights now", 
    href = "https://arapp.thinkific.com/courses/insightful-data-visualizations-for-uncreative-r-users", 
    style = css(
      font_size = '24px',
      font_weight = 600,
      text_decoration = 'none',
      background = '#0454a4',
      color = 'white',
      padding = '15px',
      display = 'inline-block',
      border_radius = '8px',                         ### <- Change here
      box_shadow = '5px 5px 10px rgba(0, 0, 0, 0.6)' ### <- Change here
    )
  ),
  style = css(
    font_family = 'Source Sans Pro',
    background = "linear-gradient(45deg, #bcd7e1, #c7dbf0)",
    color = '#333333',
    padding = '50px 30px',
    border_radius = '8px'                            ### <- Change here
  )
) |> 
  browsable()

Insightful Data Visualizations for "Uncreative" R Users

Leverage {ggplot2} to make charts that communicate effectively without being a design expert.

Communicate insights now


More tags

Finally, we can finish our little project by nesting a few more tags into the paragaph. You see, what’s currently still missing is

  • the boldness of the “without being a design expert” part and
  • the modifications of the “{ggplot2}” part.

For the former we can use a <b> tag. That’s html for bold. But in {htmltools} there’s no b function. The reason for that is that the b tag is just a shortcut for <strong>. So we could use that one. Alternatively, we could use the long form tags$b(). You see all tags can be accessed via tags$ but for the most common ones like h1, p and a there are the shortcut functions that we have used so far.

div(
  h1(
    'Insightful Data Visualizations for "Uncreative" R Users',
    style = css(
      font_family = 'Libre Baskerville',
      font_size = '42px',
      font_weight = 600,
      line_height = 1.3,
      margin = '0px'
    )
  ),
  p(
    "Leverage",
    "{ggplot2}",
    "to make charts that communicate effectively",
    tags$b("without being a design expert."),          ### <- Change here
    style = css(
      font_size = '26px',
      line_height = 1.3,
      margin_bottom = '20px',
      margin_top = '15px',
    )
  ),
  a(
    "Communicate insights now", 
    href = "https://arapp.thinkific.com/courses/insightful-data-visualizations-for-uncreative-r-users", 
    style = css(
      font_size = '24px',
      font_weight = 600,
      text_decoration = 'none',
      background = '#0454a4',
      color = 'white',
      padding = '15px',
      display = 'inline-block',
      border_radius = '8px',
      box_shadow = '5px 5px 10px rgba(0, 0, 0, 0.6)'
    )
  ),
  style = css(
    font_family = 'Source Sans Pro',
    background = "linear-gradient(45deg, #bcd7e1, #c7dbf0)",
    color = '#333333',
    padding = '50px 30px',
    border_radius = '8px'
  )
) |> 
  browsable()

Insightful Data Visualizations for "Uncreative" R Users

Leverage {ggplot2} to make charts that communicate effectively without being a design expert.

Communicate insights now


Finally, what’s left to do is to make the “{ggplot2}” part into an inline text that we can modify. The HTML tag for that is the <span> tag.

div(
  h1(
    'Insightful Data Visualizations for "Uncreative" R Users',
    style = css(
      font_family = 'Libre Baskerville',
      font_size = '42px',
      font_weight = 600,
      line_height = 1.3,
      margin = '0px'
    )
  ),
  p(
    "Leverage",
    span(                                      ### <- Change here
      "{ggplot2}",
      style = css(
        font_weight = 600,
        font_size = '28px',
        font_family = 'Source Code Pro',
        color = '#0454a4',
        padding = '5px'
      )
    ),
    "to make charts that communicate effectively",
    tags$b("without being a design expert."),
    style = css(
      font_size = '26px',
      line_height = 1.3,
      margin_bottom = '20px',
      margin_top = '15px',
    )
  ),
  a(
    "Communicate insights now", 
    href = "https://arapp.thinkific.com/courses/insightful-data-visualizations-for-uncreative-r-users", 
    style = css(
      font_size = '24px',
      font_weight = 600,
      text_decoration = 'none',
      background = '#0454a4',
      color = 'white',
      padding = '15px',
      display = 'inline-block',
      border_radius = '8px',
      box_shadow = '5px 5px 10px rgba(0, 0, 0, 0.6)'
    )
  ),
  style = css(
    font_family = 'Source Sans Pro',
    background = "linear-gradient(45deg, #bcd7e1, #c7dbf0)",
    color = '#333333',
    padding = '50px 30px',
    border_radius = '8px'
  )
) |> 
  browsable()

Insightful Data Visualizations for "Uncreative" R Users

Leverage {ggplot2} to make charts that communicate effectively without being a design expert.

Communicate insights now


Conclusion

Hooray. With that we have finished our little project and learned tons of CSS keywords along the way. If you found this helpful, here are some other ways I can help you:


Stay in touch

If you enjoyed this post, then you may also like my weekly 3-minute newsletter. Every week, I share insights on data visualization, statistics and Shiny web app development. Reading time: 3 minutes or less. Subscribe at

You can also support my work with a coffee