
Nick Strayer || Part IV: Styling a Shiny Wordle App with CSS || RStudio
00:00 Introduction 00:44 Switching from verbatimTextOutput to uiOutput 01:42 Switching from renderText to HTML DOM elements 03:17 In-line styling with divs 07:30 Converting individual letters from block elements to adjacent grids with CSS grid 08:56 Adding CSS at the head of the UI variable in Shiny with tags$head (and wrapping with HTML!) 10:36 CSS targeting of the background color 12:24 Link: Complete Guide to CSS Grid 14:05 Moving text position within each individual div using CSS classes 16:48 Creating a gap between grid elements 17:13 Rounding border edges for letter grids 19:00 Formatting letter grid background color to indicate result "correctness" 21:30 Increasing font size 23:37 Updating the legend to use color, not text indicators 26:40 Adjusting padding to improve app aesthetic 28:08 Formatting the app UI with justified centering 31:56 Adjusting the text input and Go button 34:07 Why Flexbox is the right tool for this task 35:09 Exploring Flexbox Dev Tools in Chrome 39:14 Adjusting the colors of letter grids using Inspect Element 40:40 Making text bold with font-weight 41:04 Hint on how to approach formatting the keyboard In final installment of this four-part series, RStudio's Nick Strayer walks through using CSS to stylize our Shiny Wordle app. Code + word list: https://github.com/wch/shiny-wordle Check out the full Shiny app here: https://winston.shinyapps.io/wordle/ You can learn more about Shiny here: https://shiny.rstudio.com/ Got questions? The RStudio Community site is a great place to get assistance: https://community.rstudio.com/ Content: Nick Strayer (@NicholasStrayer) Animation, design, and editing: Jesse Mostipak (@kierisi) Music: Lakal by Blue Dot Sessions Wordle: https://www.powerlanguage.co.uk/wordle/
image: thumbnail.jpg
Transcript#
This transcript was generated automatically and may contain errors.
So yeah, this is basically what it's going to be, just like the grid layout. I took the colors actually from the app, the hex colors, so they're actually the hex colors. And yeah, basically also changing this input to be down here, just make it a little bit closer to what the actual app is.
And so basically when going through this, like seeing the original app, I always use testy for some reason, because test is not five letters, right? Winston built it with a verbatim text output, which is easy and good. But if we want to style it, we're going to start wanting to get it into like HTML DOM elements.
Switching to uiOutput and render UI
And so basically taking this result output here, and instead of having it be verbatim text output, we're going to do UI output, call it result also, and then we can get rid of this verbatim text output. And so that itself should work. It just doesn't give you the nice, it's just like text, but that works just replacing that.
But what we eventually want to do is get it into a bunch of individual divs so that we can put a nice border around each letter, color it, do all that stuff. So right now, the way that it works, output result is a render text. We're going to need to change that and looks like there's a V apply that makes a character. So it's basically assuming it's a character vector and then it pastes everything together here.
So what we are going to do is make this not a V apply, but an L apply so that it can just be a list. By doing that, we need to get rid of this and then format result just to like make sure it's working. We can just wrap it in the div, tags div function, save that.
We can run it, but it gives us this output and that's just because we're still doing this paste here. So we can get rid of that by just returning out str, reload it. Now we're getting an error. That's because, like I said, we need to change this because right now render text is trying to paste everything together because it says I'm putting out text and we're just giving it a list of divs. So that's not going to work.
Building divs for each letter
Now it's working. This is a div. We can check that though to make sure just do like an inline style. I like to do outline like is my default, like check to see if something's working. So I do outline one pixel solid salmon because it's my favorite CSS color. And so we see we are indeed getting a div.
The div has the whole guests in it so we can do shiny as our second guests. So we get two divs are stacking on top of each other. That's good. It's pretty much what we want, but in here the div body is still just this text pasted together. So we should update format result because div takes that as the input. So we need to make this not return just a string, but return a list of divs.
So we can do this by right now it loops in a for loop over the letters in a given guess. And so builds this out STR. So we're going to replace out STR with out divs. We're gonna make that a tag list, which is basically just a fancy list that is works well with tags like div and stuff.
And then in here we can say out divs, the ith element is going to be equal to a new div is this internal section here. Get rid of all of these out strings and just return out divs. So in theory, this should now, yep.
So now it's behaving differently because by default divs themselves like to stack on top of each other. They don't go in line. It's because they're at block level CSS elements. So that basically says the next one I'm going down instead of over.
But one of the things that we can do here is put some style around this. So we could say instead of making a bunch of divs, we could just do the one time this down here and have this if statement, give us the inside text. So the idea here is to do a single div call so that way we can do stuff like put a class on it without having to repeat that class like a million times.
So then once we've assigned letter text in this, then that this if statement assigns letter text based on how we did the result. So that we can just take that letter text and put it in this div, just quickly make sure it worked. Okay. It looks like it worked.
In-line styling with divs
So then can do some inline styles here. So we'll do outline one pixel solid, blanched almond is another good CSS color. So now we can see we have this wrapping div. Each individual letter within here is taking up the whole span that's because of these block elements. So I think a good way to proceed, since we want that grid, we want them to sit next to each other.
Just try width, 50 pixels height, 50 pixels. Just see what this gives us. So they're boxes now, but they're still stacked. So I guess if we wanted to make them sit next to each other, like they should, there's a few different ways we can do this.
Converting to CSS grid
I think the way that I want to do it, just because I am familiar with it is to use CSS grid. So CSS grid is basically a way of saying, I want you to lay this stuff out in a different way and you can say like, you know, I want to fill in this many columns and this many rows. And so this works really well here because we know we have five columns.
First, we should probably, like right now we're building our styles and these inline styles and that works fine for just like a couple of things, but it's going to get pretty unwieldy very quickly. So what we can do is include some CSS and there's a bunch of different ways to do this. The easiest way is to just put the stuff in the head of the UI.
So that's tags dollar head says anything that goes within this gets placed into the head of the UI HTML was generated, which kind of part of the page that doesn't actually show up but you can put resources like CSS and JavaScript in there. Just know that you have to put your style so you can do tags that style within that. And then from there, there's one more thing that you have to remember, and this bites me like every time I do this is that you have to wrap this because we're going to write it in text inline to wrap it in this HTML function.
And it just allows us to use characters that CSS likes, such as the like less than or greater than signs, which would by default get translated into like weird Unicode character things. But CSS actually wants them. So we need to wrap it in HTML or we will have very confusing bugs that will take a long time to understand.
So we need to wrap it in HTML or we will have very confusing bugs that will take a long time to understand.
So in theory, tags head tag style. Now we can just start writing CSS in there and it'll apply to our page. So we can test that again by targeting this result because UI output creates a div that shoves all of your UI into it. And then the ID of that is the ID that you give it.
So we can target that real quick and say CSS rule targeting, targeting result and say let's say background color, which is another, since we're already using outlines, let's use background color. And for background color, we can do light gray, I believe that's the right way. So let's see if this works. Did not. Okay. So background color, light gray is probably not the right one. Let's just do steel blue. There we go. Okay. So this is div result, which contains all of our stuff as background. So we're successfully targeting it with this CSS rule.
So what we're going to do is go back and do this CSS grid stuff so that we can get it to lay out how we want it to, because we have each letter being a div. We want them to sit five next to each other and then wrap down to the next row. So for each guest. So we can do display grid, which is how you say like, okay, this div within it, use the grid layout engine. And then we can do grid template columns, repeat. So we're saying repeat this thing five times five and 50 pixels. So that is saying make me a grid that has columns that are defined as five columns of 50 pixels.
Complete guide to CSS grid
This is a syntax that's kind of hard to understand. And I know it because I work with it all the time, but there's a really good resource, which is this complete guide to CSS grid, which is where I go for almost everything. It gives you all sorts of things. So if you ever forget anything with CSS grid, this is a fantastic resource for it.
So going back here, we're saying it's going to be a grid. It's going to have five columns, each 50 pixels wide, so we can save and reload. And it's not doing what we want it to, but interestingly, there is a border around this that is the color that we put of this div, it's the salmon border. And so we actually don't want that div. We just want the results dumped right in there. So we can just get rid of that div in general so that all of our divs, yeah, there we go.
So we were wrapping it in a div and CSS grid says, I'm going to, you know, I don't know about the children. I only care about the first child. I don't care about the children of your children. So the div within it will be a column and then the next one is another column and so on. So in the way that we were doing it, we were wrapping all of the letters in a single div. And so I didn't actually know to put them how we want them, but now it is.
So now we see the five values and their little boxes here. We can do another guest just to see if it wraps how we want it to. Let's get shiny. It does. So now we have five new guests down here, five. And all we're doing is building these divs.
Moving text position with CSS classes
And so what we should do now is obviously this text is up in the upper left and that's not great. So we need to target these individual divs. And before what we were doing is this inline styles, but that's not great. So we're going to take that out and bring it back up to our CSS chunk. So we can call, there's a couple of ways we could do this. We could give this div a class itself and then refer to that class up here. Or we could say result and then div, which is saying like of all of the divs that are children of result, this is the style I want to apply. But I think that what we want to do is make a new class just to be a little bit more readable.
So we could say guest letter. We're going to paste in that style that we had before, save that. We need to go down here and put the div class in, but let's run it without that first just to see. So yeah, we get there laid out in that order, but they don't have the style. So now in theory, to get that style back, we should just say class equals guest letter. So that should give this div the class of guest letter. So that should apply this. So let's see if that works. Did. Okay, cool.
So now we want to style these guys, we could just do it right there, which is nice. So within that, we could say, you know, let's, first thing I think we're going to want to do is put this actual letter in the center of the box, so it's not wasting all that space. So there's a bunch of different ways to center something in CSS. It's like kind of like the, uh, one of those problems in computer science along with like caching, uh, aligning stuff in CSS. The easiest way to do it that I'm aware of is to do display grid. So say this, this child element also has a display grid and then place content center. And that basically just centers everything completely within the div.
So now we have everything perfectly centered. So that's nice. So it's starting to look a little bit better. One thing that we can do to make it a little bit more like the wordle thing is have them not butted up right against each other. So there's a property on the grid, CSS grid, it's called gap, say gap, and we'll set it to like five pixels. So that's basically saying, place these items of the grid five pixels away from each other. Cool. So now they have that gap, it's getting better.
Rounding border edges
We can round the edges a little bit and say border radius. Also say that's five pixels. So that should just say, I'm going to round the edges, but I did it wrong. Ooh, you know what? I need to do border, not outline, border radius, border. There we go. Border on newer browsers, the outline will respect this border radius, but on older browsers, such as the built-in viewer in RStudio, you need to use the border, which is kind of confusing that there's border and outline, but just there is at this point.
So now that we have that, things are looking pretty good. I think the next step is going to be, right now we're using this indicator of like wrapped in parentheses means that it's in the wrong place, but in the word and then square brackets mean that it's correct. So that's probably not the best way to do that, given that the actual Wordle app does it in color. So we should start doing it in color.
Coloring letters with CSS classes
And we're going to do that with another class. So if we notice this down here, the way that our results come, each thing has a result and it's either correct in Word or not in Word. And so if we use that, we can use that directly as a class on our div itself. So within here, format result class is going to be paste of guest letter, and then also the result itself and that we reload the app.
I'm going to pop this out into the browser real quick so that we can use the inspect panel just to see if this is working. So we can see now this element here, this S has a, is a div with the class of guests dash letter and in dash Word. So now that we have that class, we can use our CSS like we were doing before to target it and make it look how it should.
So we could just say dot in Word, and I believe, let's see, in the Wordle app, he uses like an orange. That's, we could say in Word background color is orange, red, a little bit more intense than we maybe want, but let's try that. Cool. So now that's colored correctly. There's the not in Word, I believe it's the other class that we have. Yeah, not in Word. So the other letters here, not in Word background color, call light gray. I think I did it right this time where light gray does not have a hyphen, fix this indentation, it's just one single word, wonderful.
So we can get rid of that border that's getting really distracting. And then we should probably say color is white and color on a element, confusingly, not the background color that's set with background color or background color is the color of the text. So this should, when we have these darker background colors, we kind of want white, maybe gray is not the right color, we should use something darker, just pure gray. Okay. So now we see the letter pops out a little bit. I think we should increase the size of the font a little bit. So do font size, maybe make it 20 pixels. Cool. That's even better.
We still have this like decoration here, actually, we should figure out a letter that is in the correct place. So correct, I think is the style we need to give it. Correct. Yes. Okay. Correct. Yes. Okay. So now that can be done, background, color, do forest green, that's a good one. Reload app, tests, wonderful.
Still have this, let's see if we can figure out a word that has, let's just do shiny. Okay. So we still have this like decoration there. So we need to get rid of that or we should get rid of that just to make it look nicer. The nice thing about this is that this whole, if loop that we were doing here before, we don't need it all because we just need the letter. So we can just take that letter, put it there and get rid of that. So simpler code, reload the app. Wonderful.
So that is starting to actually look like the correct stuff, which is nice. So I guess one thing that we need to fix now is the fact that this legend doesn't mean anything anymore. So we should fix that.
So this is just text here, which means that the letter is correct. So what we can do is we can use a span element class equals guest letter. And I think that this is going to be a little bit much though, because we'll say it means the letter is correct. So correct. This is going to make this 50 by 50 and that's not what we want. Yeah. So that's a little bit too big.
So we should figure out how to get this sizing not specifically tied to that. So all the sizing stuff, width, 50 pixels, height, 50 pixels, font size, 20 pixels. We only want that to apply to this result board, but we still want to be able to use this other stuff down here. So I'm going to use the CSS rule that I talked about before, which is we can say of the result, anything that is a child of it, that has the class guest letter, there we can put these styles and that means those styles should only apply to the guest letters that are a child of this results div, but we get this now, that's not great.
Probably also want to put this grid stuff here. Okay. There we go. Now X, still not great. So maybe we should do padding, four pixels. Padding just basically says like, give me some space around the content within this element. So yeah, there we go. I think that's good enough, pretty much.
And so we can now replace this other one down here, span X class equals guest letter and in word class comma, cool. So that now our legend matches. There's a little bit of like, I don't like, this is butting up against that. So we can fix that pretty easily because we know that we have this result div. So we could just say, let's put padding on that thing too. So padding is 15 pixels. That should give us 15 pixels of space. Cool. So that looks pretty good.
Centering the app layout
I think right now this input stuff is above it, but in the actual Wordle, it's below it. So we should move it down. So it's a text input. It should be as simple as just dropping it down there. Reload app. Cool. We still have this, so maybe we want to put it beneath that too. Cool. It's looking pretty good.
I think I really want this go button to be next to it instead of beneath it. So actually, you know what, maybe first we will, right now everything's shoved up against the left of the screen, but I think that's a little bit, maybe not the best. So we want to basically make everything sit in the center, you know, on a phone, it'll be like it is now, but on a big screen like the viewer right here, that's not ideal.
But to do that, we need to go back into the HTML and I'm going to see what the, like, how are we going to target this whole app? So we have the body of the thing, and then we have this div, and that div contains the whole app and it's class is container fluid, which may make sense because we're using this fluid page here. And so what we can do is target this container fluid class and just give it a maximum width and that should keep things nice and centered.
So it's a container fluid, and then max, you know, let's start with width. Width is 500 pixels. So now things are like pretty much in the center, which is nice. That's the nice thing. Fluid page will automatically keep itself centered, which is nice. If it didn't, we would have to control that.
The problem that we run into is that will cause issues. And so we don't actually want width. We want max width. And so max width says, like, you know, follow the width of the page, you know, like the width of your iPhone screen or your computer screen, unless that's bigger than 500 pixels. So most of the time that's going to be bigger than 500 pixels, but in the case where it's smaller, it will let it shrink, at least in theory. So let's just see if that does what I said it would. So yeah, it's shrinking now. It's not letting itself get too big, so that's nice. So cool. It's looking pretty good right now.
I think one thing we might want to do is this board screen is not centered. We're just going to give ourselves some context. This helps to, like, view what's going on in the app so we can decide if something is centered. So it's not centered. So we can try to make it centered. And that's justify content. This is another one of those, you have to look it up a million times. I got it right on the first guess. I almost never done that.
If you look at that CSS grid cheat sheet, there's a bunch of different ways to align the things in your grid. And yeah, so here we go. So justify, and you can justify a self, you can justify items. So you get lucky, basically. Almost no one ever remembers it, but I got lucky and remembered it there. So now this is lined up, which is the way that it should be. So that's good. Let's do one more guess just to make sure. Cool. It's working how it should, that's nice.
Fixing the input and go button layout
So I think the last thing that I want to do is fix this input so that the input is the go button is here, because we're just kind of wasting space. So to do that, like we had before, we had the letters that just stacked on top of each other. It's because they're just this block set up. We don't actually let them know that we want them to be next to each other. And so we can group them together just by wrapping them in a div.
So you can say div, this is just a UI, bring them up into that div. So this shouldn't change anything right now. It didn't. So they're still right there. Let's give this div a class so that we can start targeting it with our CSS, say input wrapper, make sure that we're, take that input wrapper down here, dot input wrapper, outline one pixel solid. Let's do, just do forest green. There's only so many CSS colors I can keep in my head. Cool. So now we see that we do have this div there, and we have the individual elements in it.
So like we did before, we can do display grid. Let's see what that gives us. Does not exactly do what we might want, but let's do a different way. Let's do display flex, and flex box is another layout algorithm. Now they're next to each other. Flex box is basically, if you want to line things up next to each other and make them align in the proper way, flex box is what you want to use. So this is kind of a perfect scenario for flex box.
So a couple things stand out as not good right now. One is the go button is really tall compared to this. So, and then also they're right up next to each other. I don't think we want them next to each other. So let's do that same way of selecting a child. So we could say input wrapper and then button, which is the element, selecting a button element. We could just say margin left, give it something like 10 pixels. So in theory, that should take this button and basically make it 10 pixels from the thing that it's closest to on the left, so that's nice. So that's better. But now I don't like that they're like this at all. That's kind of gross.
So what we can do is go into our browser's dev tools, because just like CSS grid, CSS flex box has all sorts of fun settings that no one ever remembers, but the dev tools within Chrome have a nice little tool for working with them. So you can do things like align your content and you just play with this little thing right here and make it look like you want it to. So let's do that centered it. So let's say justify content space around align content. Doesn't do anything for us, so we can kind of just ignore it. Align items centers. Now they're like pretty close to being centered.
So I'm going to copy this CSS from here, put it here so I don't forget it because I will forget it quickly and make sure that it works. Okay. It did. Let's go back in here again. That looks pretty good. But if you're like me, you're going to get driven insane by the fact that this button is not actually lined up with the input and that the text input.
And that's because you can see that there's this like weird, there's a margin on the bottom of that input that is not on the bottom of the button. And so that causes it to be misaligned. And so what we can do is since we already have something targeting the button, go back in here. Look at this. It's a shiny input container form group. It's a div. So what we can do is say like, let me get in my input wrapper div. So this is saying I'm going to target the div that is a child of input wrapper. So that should be this text input and then we can just say margin bottom is zero. Don't need to put like pixels or anything because zero doesn't need units. Reload it. And now it sticks there. So at least it's lined up on the bottom.
I think we do align items, flex and this, okay. So flex end says in your vertical alignment of these items in this box, put it at the end. You have to prefix it with flex here because of older browsers need that. Newer browsers, you could just say end, but they also understand flex end. So it's safe to put that there. So now we pretty much got what we want. They're lined up how we want them.
And then we can actually justify content space around is not what we want because we already spaced those things out. So let's put it just by content center. So now they're centered the button space that we already did with our margin is there. That's nice. And then we just got to get rid of this. Like there's a space on the top of this, but not on the bottom. So we dealt with that before by putting padding on it. So let's just say padding. We only need padding on the bottom here. Cool.
And then take out this outline and, okay, let's see one tests that is looking pretty decent. Oh, we got to fix it. I got to get rid of that one outline that I was so eagerly adding that helps to actually look at it, how it will be just to make sure that there's not some weird CSS funkiness going on. So that's pretty good.
Stealing the real Wordle colors
I think the thing that I would do next is these colors are not right. That like orange, red is like very, like in your face aggressive. So I'm just go and use this handy dandy CSS inspector tool that I've been using, go to the world app, daily word game. And so we see that these are the colors and we can just do that inspect element and then background color of our color, correct. But there's an easier way to, or some might say steel colors. So we use this, you see this little eyedropper that I dropped her over something. It'll show up the color right there. We can copy that.
So forest green gets placed by this hex color. And then the gray that he uses is this hex color. And last, this is hardly an orange. It's more like golden rod, maybe use that, reload it. That looks correct. So cool. I think that's pretty good. Maybe one thing that we could do is make the text bold. So font weight, old, make it pop out a little bit more. Kind of gets lost as a, and cool.
So I'm just go and use this handy dandy CSS inspector tool that I've been using, go to the world app, daily word game. But there's an easier way to, or some might say steel colors.
I think that that gets us pretty far. We can, you know, we can tackle this keyboard, but we would basically just repeat the same steps that we did for this.

