Mastering HubL: From Basics to Advanced Techniques

Dennis Edson, Evelyn Olsh, and BJ Chaikowski walk through HubL from basics to macros, covering if statements, loops, and CRM data. They demo a color-palette generator that converts RGB to HSL and applies CSS variables across themes and modules.
Chapters
Sam sa.
Welcome. How is everybody doing? So welcome again to the this month developer hack. This time it will be about mastering Hubble and going from basics to advanced techniques. And as usual we have some pretty nice expert insights. So before we start we have a friendly reminder that if you want to apply as a Developer Hug speaker, this is the URL. So developer-hug.com speak pretty simple form with some questions and after that we will get back to you and also. Info about the next event. It will be about using HubSpot webhooks. So pretty exciting topic and we will be sharing more information on the upcoming days. And just for you, it will take place on the 20th June so you can already mark your calendars.
Thank you, Anton. I'm incredibly honored to introduce our guest speakers of this webinar. We are joined today by Evelyn Olsh, creative technologist at Cosmic, and BJ Chaikowski, senior software engineer at HubSpot. And if we take a look at the agenda, we have just finished the introduction and we're going to start off with the changelog updates done by Dennis Edson. Of course, after that Anton will take over and he will discuss if statements and for loops. And after that, Evelyn will learn us everything about HubSpot macros and might share a few secrets as well. BJ will finish our webinar with a color calculation demo using Hubble. And after that we might have a bit of time left for a Q and A. So let's start it off with Dennis.
Thank you, Tune and Anton. All right, I'm going to kick you all off the stage so I can have a little privacy up here. Bring my slides up. All right, folks, let's move on to the next slide. And I want to tell you a few things happening in the developer space at HubSpot, starting with something that I'm happy came back. Custom fonts have made a triumphant return to CTAs. You might have remembered in the legacy CTA builder that you could have custom fonts. We removed them in the new one and we brought them back because you all spoke and we listen. Moving right along, folks. You all deal with AI and you probably know about the content remix and you're wondering how you as a developer can handle this. This is a sneak peek. This isn't available yet. And that QR code there will get you to a form where you'll have an opportunity to be part of a private beta. Not sure when that's going to start yet, but if you go ahead and sign up that form, it'll get you on there. This is going to allow you to structure your templates so you can better inform AI on how to construct the content that goes onto your web pages. Next up, just an FYI, we have a sunset on July 5th of the V1, the cookie consent banner. Why is this important to you as a developer? The structure of the HTML is going to change a bit, so if you are targeting specific elements within it that might break. So take advantage of this now and go ahead and update that as soon as possible. And with that I'm going to move it back right on over to Anton, who has some great stuff on the Hubble. Take it away, my friend.
Thanks, Dennis. So one of the most important things a developer can do and should do is of course putting in some logic into his or her code. So one possible thing is the so called if statement and it can be used as a boolean, so is true or false and also it can be used for checking values. And to do that it's a great thing to pair it with choices and other data fields and if we take a look at some example code. So also this whole code will be available in the GitHub account and we will share this link later. And so the most basic if statement is if X, right? And this is the shortened boolean statement and it's the same that if you would write it like this, right? So if X is true, do something if it's true, else. So if it's false, do something if it's false. And like I've mentioned, you can also put in some values. So if X is value A, do this, otherwise do that and you can combine it with an so called l if statement. And this means you can combine that into if X is value, do something. If X is value number two, do something. And as a fallback just using the else and how this looks.
Is.
This. So for instance, I have a pretty simple checkbox here. So if show button, a button will appear and then I can put in a button label and whatever I have put in into this group because this group is linked to the show button statement. And on the other hand we have a so called unless statement which is basically the same but different. So it will be exactly the other way around. So unless X is true, do something and else. So if it's true, do something. So it's like the if statement but reversed. And in this case if you take a look, we have the show button enabled and we have the group if button and if I disable it we will get the unless button group visible and the unless button will appear. And let's, let's talk about the values. So this is the code I was talking about right now. So if module, show button, do this and nothing else. So no fallback. This is the code for the unless statement. And here we have the value based, so it looks a bit like this. So if module, if button, label, button label is not empty. And what this means is if this button label is empty, do not show the button. And once I type in something, we see that a button appears, right? And after that we have the whole elif or advanced if statement where we can control what will be shown. So in this case, super simple, I have a choice field with few options, in this case a button, and we'll see a button appears. If I select an image, an image will appear text, and if nothing is selected, then you don't get an input option and something will be displayed. As a fallback. And of course, it's a very common question in the community, can you use CRM data as a developer in some way? The answer is yes, yes. And it's great to pair with if statements. So in this case I have added a CRM object and based off that, it's like, okay, select the contact, let's select myself and we'll see. It will print out my name. And if I type in something like special link for, it will also put this in front of my name so you can personalize the experience of the visitor or user. And this is done like this. So again, an if statement, if the property, If the first name of the property equals, in this case Anton, add the first name to the button. If it's not equal, Anton, just print out the label. And this is about if statements and unless statements. Another very powerful thing are for loops. So a loop is very good to use for repeating content layouts and such. It can be nested, which can be very great from time to time. It can also use external data. But very important, loops or loop data can't be accessed from outside the loop. So everything you want to access from the loop must be written inside the loop. And I've also prepared some examples for the loops. So the most basic thing or syntax is for X in Y. Do something for each reputation of Y and we have again some examples and they look like this. So I can just create a bunch of instances of this whole content and I can of course modify it individually. Then you can obviously add classes to that. So for instance, you have three buttons and the second one should get the secondary class. So a different styling. So this is possible as well. And this can be achieved by doing something like that. So we have button and then we are just passing this value which looks like this. So again a choice field styled as a radio button and I have primary as label and primary as the value. Fun fact. You can also put the whole SVG code and HTML codes right into the value field and therefore create quite amazing and easy to use SVG icon libraries and such. And if you want to limit the user, you can also use something that is called cycling in this case. What I'm doing here is we don't have a styling option, but every time I add a new instance it will cycle through the same classes. And this can be done by the cycle option and then what should be used first and then in the second or odd and even options of this loop.
And.
Yeah, also you can jump into global styling so you can pass data from outside the loop into the loop. So for instance, I add two buttons with globe. Why isn't it working? Okay, but. Actually it should get the same class. But it's not about that. What I was. What I would like to show you is if I select secondary here, both buttons get the secondary class. So again you can pass data from outside into the loop, but not the other way around. And also you don't have to use or you are not limited into modules or templates. You can also use it in let's say CSS files and create a bunch of CSS with ease. And how this may look like is something like that. And this is actually something from a thing that I'm currently developing. So I have a few objects and arrays set before the loop. So I have breakpoints, spacings and different steps. And this is the whole for loop. So it looks like a mess, but there are let's say 55 lines of code. And if we enable show output, it will print us everything. So it's a combination of for loops, if statements unless statements and such. And it will create basically a margin and padding framework. Right, because you can then use MTB MD025 and I think you get the point, right? And just in comparison, we have 55 lines of code and it will print us almost 300 lines of ready to use CSS classes. So it's a great thing to put stress out of your work. Right? Because if you would write all the classes by yourself, I would assume you would be sitting quite a while on that. And yeah, again, this whole code will be in the GitHub account. And if you have questions, feel free to reach out and I will pass it to Evelyn.
All right, thank you for the pass. Thanks for the introduction as well. Great stuff there. Great stuff. Anyway, hey everybody, my name is Evelyn Alsh. I'm the creative technologist at Cosmic. We're a consultancy and HubSpot partner located in Portland, Oregon that specializes in supporting nonprofit organizations in growing their revenues so they can grow their impact. I personally specialize in helping our clients get up and running on content Hub with personalized custom built HubSpot themes. And that's where this Hubble and macro expertise comes into play. So macros, you might be familiar with them as that thing that you've seen in other people's code, or maybe the Hubble feature from the second half of that one documentation page that you kind of read but you skipped because you felt like you kind of already knew it all. Yeah, that one. Or maybe if you're particularly well traveled as a Hubble dev, maybe you've written a couple, but probably they're not really like a core part of your tool set. At least that's what I've seen to have seen in my opinion. Macros are one of the coolest and most powerful features of Hubble, and they're also one of the most underutilized. So today I'm going to run through some basics of how macros work, give you some ideas of how you can use them in your projects, and then I'm going to show you some things that you probably didn't know they can do and maybe some ways that you hadn't thought of using them before. We're going to start with a very basic introduction to macros, and that is going to be this macro. If you have encountered a single Hubble macro in your entire life, it's probably this one. That's because it's in the documentation. If you've read anything, this is probably what you've read. But let's go ahead and do a quick anatomical rundown of macros just so that we know what we're referring to as we talk about this later. First we have the macros definition block tags that's going to consist of these two keywords, macro and end macro. These are going to be wrapped in these smiley face tags so you're not going to use the double Lego hands. Over here you have the macro's name. This is what you're going to refer to the macro by and how you're going to call it. So in this case, the name of the macro is transforetransition. Over here we have the macros parameters. This is where you pass in any data that you want to use within the macro. This is how you define what you can pass in universally. Similarly to loops and other scoped blocks in Hubble. Macros can reach out to their context, but they cannot pass things back out to it. Also, when you import a macro, you will want to be able to pass it specific values, be able to set the name so that you don't have to say, like, have a variable named the same thing in every template. Right here we've got the macro definition. That's where you're going to have basically everything that the macro does. This is going to be your logic, this is going to be what it renders. Pretty intuitive, I imagine. Down here we've got the invocation. So, so this is what most of your invocations are going to look like. It's going to be the name of the macro, it's going to be your parameters, and you're going to probably wrap it in double Lego hands and that will just render it in place. However, that's not the only way that you can call macros. Macros actually return a string value. So you can use a macro basically anywhere that you would otherwise use a string. So right here you can see that we set the output of this macro to a variable. Comes in handy. And down here, this is an example of what the macro outputs. It's pretty simple in this case. It just takes your input value and it stamps it a couple times over to add some prefixes for using this for CSS compatibility. So this is probably what most people think of when they think of how to use a macro just kind of as a little abbreviation, as something like a stamp that you can just use over and over again. Save you a little bit of hassle, save you a little bit of time in. All right, we're going to look at a couple other macro features here. In another simple sample, right here we have a macro for a Basic Elevation card. And this uses one of the more powerful features that macros have to offer. And that is going to be macro callers. You'll see that in this macro definition we have this function that is just caller. It doesn't have a parameter passed in. And this is something that you are not going to be able to use outside of macros. What the caller does is, well, let's go ahead and go down here to the invocation. You'll see that this macro invocation is not using the same format as the previous macro invocation. Instead, our macro is using these call and end call block tags. Then right here we have the macro that we're calling parameters we're wanting to pass in. In between these two call tags, we have a whole bunch of content. Basically what a caller is is it is a second sub, like anonymous sub macro that is defined within your macro definition. And then anything that you put in between these call blocks, that can be anything, you can define variables, you can do loops, logic, if statements, etc. You can hit the CRM, you can hit PUB, DB. All of that is going to evaluate and then it is going to be placed wherever that caller function is within your macro definition. An interesting thing about callers that once again may come in handy is in a caller you can access macros and variables that would otherwise be privately scoped inside of the macro. Right here you'll see we have this level parameter that we've passed into the macro. Normally you wouldn't be able to access this outside of the macro, but here in the caller, this will actually succeed. This will print whatever this parameter's value is. That's something that can be useful if you're, say, using callers to loop through a certain type of data and you want to have a different format, you want to pass a certain format for that data in the caller, you can still reference your parameters, you can still reference things that you have set inside of the macro. So now that we've looked at the very basics of how macros are structured and how they operate, let's look at a pattern that I'm a really big fan of, which is using macros to power componentry in your themes. So we're going to start by looking at this basic link card. This is what we're going to be working with over the course of these demonstrations. This is a super basic link card. You all pretty much know what it is. You've got your featured image, you've got your title, you have a description, hover over it, there's a link. Just to prove that this thing works and isn't totally fake, I'm just going to slam a bunch of text in there and it changes. Nothing surprising. So let's go ahead and look at how that's defined. So macro definition. We're going to go over the anatomy again real quick, I promise I'm not going to belabor it too much. We have our opening block, our definition block, we have our name, and we have all of our parameters. One thing you'll See, here is something that I like to do. It's definitely optional, but I think it's something that will save you more time than it costs overall, especially if you work with other people. When you are making your themes, that is, creating some documentation of what your macro is supposed to do and the parameters that you're passing into it. Here you can see that I say what data type we're supposed to pass in. We have the name and we have a brief description of what that parameter does. This is hugely helpful in making sure that other people can actually use your macros, because you may have a perfect idea of what they are, but somebody who didn't write them may look at all this and be like, I have no idea. The same way as working with anybody else's code, I am not going to go into depth with the actual markup of this link card. That's not really what this is about. This will be in the GitHub if you want to read it later. But it's a basic link card. We have our article, we have some stuff passed in for an image, whatever. Now let's go ahead and look at what using this macro looks like. Here we have a module for that link card, and this is the preview that we were looking at earlier. Let's take a quick look again at how we're using this macro. Right here we have our import statement at the top of the. At the top of the markup. Now, this is something that is important about managing your macros, is that you can store macros all in one file. You can store them in different files, and if you want to use them elsewhere in your project, you're going to have to import them somehow and make them available to the context, the context that you want to call it in. There's a couple of ways to do this. I know some people prefer to import all of their macros in their base template, and that should inherit down to every other template and all the modules that are used within it. I personally prefer to import macros on a file by file basis like this, just because it means that you don't have to worry about context, you don't have to worry about inheritance. You always know what context your macro is seeing, and you always know that it's there. Down here we have our invocation, and pretty much nothing about this is any different than that very first macro that we looked at. It's just simply we're putting it in the Lego hands and we are passing in our module fields and that creates this link card. You might be looking at this and thinking, well, okay, you've already made this thing a Mac. You've already made this thing a module. Modules are portable. Why would we want to use a macro for this? You are right. Modules are incredibly powerful and they have a lot of portability built into them. You can embed them in templates, you can use them in the drag and drop builder. They have fields so you can edit things. But there are a few limitations to modules that you'll probably run across when using them to build themes, especially if you use design systems and like to reuse your component tree a lot. Let's go ahead and take a look at one of those limitations and how using macros can help us resolve that and work around that. So right here, let's say you wanted to make a module like this. This is just a simple carousel of these link cards. Now, if we go back to Design Manager and we look at this, here's an example of writing that link card module without using a macro. We have all of our markup in here. Now, if we wanted to use this, if we wanted to build our link card like this, and then we wanted to build this carousel, we actually wouldn't be able to leverage this module to generate those link cards. And that's because modules cannot contain other modules. So if we built our link card like this, and then we wanted to have a carousel of this same component, we would have to duplicate our markup and start maintaining it in two places. That's not too bad when you're setting it up initially, but as your code base starts to grow and as you are maintaining things as you want to make changes, it means that you're having to make the same change. You're having to duplicate it in multiple places. And that gives you the opportunity to introduce errors, to forget things, to slip, to forget that something exists. It adds a lot of potential for mistakes into the maintenance of your theme. So if we have this extracted to a macro, if we built this as a macro like we did previously, here's what our link card. This is what our carousel of link cards looks like. Our module, same as the single one. We have our import up here, where we're importing the link card down here, we don't have to worry about the link cards. We don't have to worry about its markup at all. We don't really have to worry. We have no concerns. We just call it and we pass in the field data from our repeater group. That means that when we make changes to this Link card in the future if we want to make design updates. A great example of this is if you're the kind of dev who uses tailwind css. Instead of having to go and switch out all of those tailwind classes in five places, it's all contained within one. That is, you know, that that's cool. This is a way that I really like to use. I really like to use macros, but I did promise you guys a little bit more. So let's go ahead and look at something that is not quite so standard, maybe not something that you've encountered before. So we're going to start by looking at this one card. Now, this might look almost exactly the same as the previous link card, and that is because it is almost exactly the same. But you're going to notice a few things are different here. You're going to see that there's a subheading that takes a parameter we're passing in additional parameters. And down here you'll see you can also change the text and the read more text. And that's also something that takes an additional parameter. Something else that you may not see right now is right here we have this required heading. So if we delete this, we get an error message. Instead of rendering without the heading, we're going to get an informative error message that tells us where the source of the error is. It's going to tell us what parameter is missing its data, and it'll also tell us about what type of that is that data is supposed to be. So let's go ahead and take a look at how we're doing that. So if we move here, we're going to start with the invocation. And this should look pretty similar. This is almost identical to the last module we were looking at or the previous link card module we were looking at. Except here in the invocation, you're going to notice there's one big difference. This is going to be the first undocumented feature of several that we're going to be looking at over the course of this demonstration, and that is keyworded parameters. Here you'll see that we are assigning our passed in values to a keyword, and these keywords match the parameter names in the definition. That means we can pass our parameters in any order. This is a huge boon to the usability of macros because we. One of the big pains with them is as you get like, you know, say 20 parameters or something on a macro, which can happen, it gets really hard to remember what order they're in. Which ones are where, which ones are optional? If you pass them in keyworded, you can pass them in any order. You don't have to pass optional ones. And it makes it a lot more descriptive, it makes it a lot easier to read. For somebody else who's looking at this macro invocation, they actually know that, like, oh, this is the subheading, as opposed to passing them just by value, where you have to know the fourth one is the subheading. This also means that when you make changes to macros, you don't have to go back and change all of their invocations. If you are passing them with keywords, the order no longer matters. So that means that they become infinitely more flexible. Let's look at some differences in how this is defined as well, though. So here, once again, we have our same definition, our same documentation at the top. But you'll see here in the definition, these parameters have values set while we're defining them. And that's going to be the second undocumented macro feature that we're looking at today, and that is default parameters. Hubble macros have default parameters. So here, any of these parameters that we've defined, if these are omitted from the invocation, they will default to these values that we set right here. I want most of them to be empty string so that they evaluate to false and that will power errors later, but errors and reporting later. But here you can see, oh, if we don't pass in a read more label, it'll just be read more. Once again, something very powerful for something very, very powerful for making macros more usable and more flexible. Now let's look at what's making that error message happen as we scroll down. You'll see here that we have all of our previous macro logic and markup wrapped in this validateparams macro call. You can take my word for it. This is all identical to the previous one. It's just contained within this macro. So looking at this validateparams macro, we'll see that it takes the macro name, and then it takes a series of parameters that are identical to the parameters of the macro. And you'll also see that there are some things that are pretty similar to this documentation up here. Each one of the parameters that we're passing into this validateparameters macro takes the parameter name as a keyword, it takes the current value of that parameter, it takes the data type it's supposed to be, and then it takes a boolean indicating whether or not it's supposed to be required. Let's go ahead and move down to the definition of this validateparameters macro and see what we're doing with all that information. Here's our definition and the keen eyed of. You will notice one thing that seems a little bit off about this definition. That is that this definition only has a single parameter. But up there when we were calling it, we passed in a dozen parameters. That's going to be the third undocumented macro feature we're going to be looking at today, and that is kwargs and var args. We're going to skip over some things that we'll look at those in a second. But right now let's just go ahead and focus on this flow for loop. You'll see that this loop is cycling through this kwargs item, this dict. Now, this kwargs dict is a dict that takes in any sort of keyworded parameters that you pass to a macro that aren't a part of the definition. So up there we only defined the macro name. So that means in this case this kwargs is going to contain all of the parameters that we passed in that give us the details about the parameters that we are trying to validate. This one contains all of your keyworded arguments. If you're not using keyworded arguments and instead you're using value based positional arguments, there is the var args array that will also exist. Both of these always exist in a macro, they're just always there. But if you don't pass any parameters that aren't defined in the parameter definitions, they'll just be empty. So that's really cool because this basically means you can write macros with the intent that they can take an arbitrary number of parameters of different types, different names. It opens up the opportunity for you to make macros that are a lot more general use for things like this, where we're using them for things where we're using them for error correction and using them for type enforcement, which is something that you generally wouldn't expect out of a templating language. Okay, now we're going to look at some of these internal macros. I mentioned it earlier. Macros can have other macros within their scope that are only accessible within their scope. This first macro, nothing very special. This just takes in some, some error data and spits out an error message based on some simple logic. But this macro is a really cool one. And this is a way to use macros that is very novel and super handy for making your code more Readable. This require type macro, it effectively returns a boolean. Earlier, I said that macros always return strings, but there is one very special string that is different from all the other strings. And if you've ever taken a programming course, you're going to know that that special string is the empty string. And it is possible for macros to return empty strings and that empty string will evaluate to false when used in a comparison. So if we want to have any sort of boolean logic, any test that returns a true or a false, or we can contain that within a macro as long as we are able to return an empty string. And that's where yet another undocumented feature is going to come up, I guess, kind of two of them. And that is going to be the do and endo tags and also these white space control brackets. The white space control brackets are pretty simple. You just add this hyphen on the kind of like on the other side of the percent sign from the curly bracelet. What this does is it tells this to collapse this white space before this. Here it says collapse any white space after this. Here it's removing the new line. Here it's removing the index. Now, the do and end do tags are interesting because anything within these tags will evaluate, but none of it will print or render to your final output. If I were to type anything within here, if I were to put any content within this, it just wouldn't print. This is useful for containing logic that you don't want to actually have any sort of output. Through judicious use of this white space control, we're able to put this type matches variable. We're able to output that here at the very end. If this evaluates to false here, that means it will return an empty string. And that means that if we do an evaluation, if we do a comparison against this macro and it returns false, if it returns the empty string, it will return false. So again, that's a really cool way to use macros, not just for rendering things on the page, but for logic wrapping up real quick. We'll just go down to the very bottom, we'll see what this macro is actually putting out, and you'll see really what this macro mostly does at its heart is it's an if statement that says if there's errors, render an error. If not, render the card. Pretty simple stuff. And guess what? We're using a macro to print the error. Also that's down here. You can take a look at that on GitHub. So that's basically everything. Before I Go. Though I do have a couple of caveats or things that I want to mention about using macros and some of these features that I've demonstrated today. Just to make sure that you're not. Well, I'm not teaching you how to shoot yourself in the foot. So the first thing, use any of these undocumented features at your own risk. If they're not documented, they're not officially supported, and if they break, that's tough cookies for you. Number two, don't go too crazy with macros. Macros are really great for abstracting things and making your development experience easier, but they do come with a little bit of computational extent, and having too many of them can slow down your page rendering, which means that your site is going to be slower for your users. If you are really heavily using macros, make sure as much as possible that you are checking that your pages can pre render using the HSDEBUG URL query when you're viewing your pages. If your pages can pre render, that computational expense is going to be a lot less significant. It's going to have less of an impact on your user experience. That's everything I've got. Thanks so much for listening and I suppose I'll pass things on to BJ now.
Thank you, Evelyn, for giving away all of HubSpot secrets for free. No, that was amazing. Thank you. And thank you, Anton, for setting a good stage for us all. We've got a little fun situation here. Let me set the stage. I'm going to put us all in a world where we are creating a new theme. And our goal for this theme is that we're trying to make the end user's experience as easy as possible to make this website look good. Which means that we're trying to make it very difficult for them to mess up, which, as we are all aware of, is ever too present and ever too easy to make happen. However, we're going to be hyper focusing specifically on just the idea of colors. I've got a rather complicated, but also really fun solution for trying to generate a color palette based off of just a single theme color field. So we're going to look into that a little bit and then we're going to see how it trickles down into modules across your theme. And we're also going to look at how to override that at a page level and a module level. Buckle up. We got a lot to cover in a very short amount of time.
Here we go.
First thing I got is just a little page that we're looking at this is contained inside of a child theme that's currently piggybacking off of our growth theme. Now the idea, as I mentioned before, is that we're going to want to be able to go into our theme settings and specifically change this. Let me switch to the right page and specifically just target one single color here, which is going to be our primary color. And we want that to affect our whole entire color palette that's being generated. The idea here is that we're changing one color and it's affecting three total colors. If we look right now, we've got this stone blue set, but we can change that to say, this purple. We'll notice now that our whole entire color palette and our whole entire feeling for this website changes in a way. We're super powering turbocharging our theme settings. Now. The nifty thing about this and some of the complicated pieces is that we've got a color that we're setting here, but we're generating three colors. Or we're actually generating two colors. But we need our algorithm to be smart enough to know which two colors to generate. As an example here, we are currently selecting our dark color and it's being put in the dark color slot. So. So our algorithm knows to generate the two midtone and the light tone if we switch to say, lighter color here. Now the color that we've selected is in the middle and we're generating a dark tone and a light tone. And if we choose an ugly yellow, it's usually hard to create a good color palette off of this we're generating. Or we're pre selecting the light color and we're generating the mid tone and the dark tone. Pretty cool. And again, this is trickling down into our module. So all of our components of our module are using these color variables, right? And now we're dealing with two colors that aren't at all connected to our theme. So how in the world are we referencing those colors that are being generated on the fly? Let's take a look. Switching over to VS code here. Surprise, surprise. We're leveraging some macros to help us out before we do this. If you all look under your seat. Just kidding. It's not going to happen. Now it's for the live audience that's not here. But as mentioned before, all of this code is going to be available online. So I'm going to speed through it because it's kind of complicated color math that even I don't understand. But we'll get to the juice. So up Here we're using a macro that is basically taking an RGB value from our primary color and it is converting it into hsl. For those of us who don't know, HSL stands for hue saturation and lightness, and. Yeah, hue saturation and lightness. That way we can. As you can see, in our design, we're dealing with a monochrome color palette. Everything is in this color hue spectrum that we've selected here. All that we're doing to generate these new tones is adjusting the lightness and the saturation value. It's making it a little bit easier on me for this presentation. Yeah, we've got our first macro here that's converting our RGB value into an HSL value. There's a lot of complicated color maths happening here. Then we're finally getting output with an HSL string. We then are passing that HSL string into our color palette generator. Basically what we're doing here is we're just looking at the lightness value to determine whether or not we're gener or whether or not the color that has been selected in theme settings is the dark, the medium, or the light tone. Based off of that, we know what the other two tones are that we need to generate. There's just some logic in here to handle that. At the end of this macro, we are creating some color, some CSS properties. I like to colloquially refer to them as CSS variables, though I believe it makes a little bit more sense to everybody. The way that CSS variables work for those of us who don't know is.
That.
We write them in a similar way that we would write any other kind of CSS property. For instance, we can contain it or put it on an element like the HTML element. We could go one level higher, which is actually root, right, which is just a little bit more global. But as you can see, we could write all that same stuff out there. However, I didn't do it that way. So CSS properties, again, we're going to define them in a very similar way that we would just say, like, display, right? So for all practical purposes, these things are doing a very similar thing. However, we need to prefix a variable with two dashes, dash, dash, and then any arbitrary name. So for the case of this, I'm using color primary, color dark, color medium, and color light, right? And now that isn't fundamentally setting any colors on it, right? So it's not doing the same thing as if I were to do do background color. Dark, This would fundamentally be changing the background color on the HTML itself on that element. This is setting a variable that trickles down throughout the dominant that I can then access anywhere inside of this HTML element. Let's take a look at what that actually looks like. We're going to take a look at the CSS for our color palette blocks, which is this little module that I have set up here. I've got three little divs, and those divs are just the color dark div, a color light, and a color medium. All that I'm doing to access those variables that I've set are setting a background color attribute, and then I am saying var. And that is how we syntactically reference a variable when we're going to use the variable, then I'm putting in the variable name on the inside of it. That is what is getting us this color palette that we're seeing here. Now, this works pretty nicely. As we can see right from our theme settings, it's trickling down again. I can change the color. I get a new color palette altogether. Now, what if I want to override this on the page level, Right? Say we're building a landing page, and this blue hue tone looks great for the rest of the website, but I want my landing page to inherit a different color scheme, right? Well, let's take a look. We're going to go back to our page editor. As we can see, we're still inheriting this blue. Now, it's not the same blue that I left with because I didn't publish those changes. This is still working, but these are the defaults that I preset this to. Now, if we want to change at a page level this, I can click on this module and I can say, let's override the dark tone, right? I only want to override, in this case, just a specific color and I want to override. Oh, no, the dark tone isn't connected to something very obvious. Let's not override the dark tone. Instead, we're going to override the medium tone here. So here I have selected a new medium tone, and this medium tone is now trickling down throughout my entire new web page. Now, if I went to an old webpage, it would still be using the theme settings, but here we can see that we're easily overriding it. If I click override on all three of these, I now have a completely different color palette that's trickling down throughout my entire page again. Now, how are we doing that again? Let's go take a look at this color palette module that we have. So here, this is that same module, this is this color palette box module up here. All that I'm doing is I'm doing an if check to see if the user has selected any of these overrides. If the medium dark or light override is on there, then what I'm going to be doing is creating that same color block that we were using in our macro. The same style block here. Whoops. And I'm just redefining whatever variables are actually being set. Right? So it's really cool is this is another way of writing an if condition, right? So instead of using an if block, if you have a simple one liner, you can tack an if at the end of it and specify whatever your condition is. And then you don't have to if else, if, else, if else. I think that this just makes it look a little bit cleaner.
Here.
We're overriding it on HTML again. If we wanted to get more specific, we could override this on the body class as well. And it would do the exact same thing, but here overriding it on the HTML class. Cool. That's awesome. We can override the entire page. But what if we want to override the page level overrides and and we want to just change it on a specific module? Well, let's take a look at what that might look like here. We want to say, hey, this dark red tone down here is pretty cool, but I would prefer if it were like a super bright blue. Awesome. This is totally accessible. We love that. As we can see here, I've overridden this dark tone. Right? But it's not overriding on the module beneath it. Right? And this is because of how CSS variables work. So let's go take a little look at this example here. We're going to dive into our card module, we're going to look at the module itself, and here we can see that if our user has specified an override for that color. All that I'm doing is I'm inlining that same style declaration. I'm declaring a CSS variable on the actual module here. Something that I could do as well, which would be totally valid is I could say, pretend for an instance that this is css. I could say cscard. And then we would want to namespace this to the actual module. We want to add some because we wouldn't want this to spill into other classes. So we'd want to add some specific name to this, which we could do like that ID equals spell right name. I could define this this way, color dark, right? And then specify My dark color here. So this two different ways of writing the exact same thing.
Right.
So what we're saying here, instead of applying this on the HTML element, where as we were back here, this is being applied to our HTML element, we're now applying this specifically to the CS card, which within this module, this little name bracket is a way of uniquely identifying the module instance, which is pretty cool. Another free little snippet for you. Yeah, so that gives us module level overrides, which is pretty darn cool. Right. So again we've got theme level overrides. So I can set this back to our blue and these are going to stay that red.
Boom, boom.
Down here we're pulling that dark blue tone that we've set on our theme level. And then here, this is our dark color. We're grabbing the overridden on the module color. Right. Again, I'll change this to something else entirely. Purple, a little bit more legible that way. Yeah. So yeah, there's a really cool intersectionality point between Hubble and css. Should we want to make it that way. I would really advocate for everyone who's creating themes and modules in particular to really try to make it as easy for your end user as possible. One other quick note that I do have tucked away inside of here that's a little hidden gem is this odd fields js file fields.js is another way of writing fields. I'm not going to talk about it a lot, but it'll be inside. I'm not going to talk about it at all really, but it'll be inside of the repo and you can pick it apart. It's basically a way of writing fields using JavaScript. If you're writing a lot of things that are compartmentalized and componentized, it's easy to keep things together and consistent. It's also a really easy way to not have to write a bunch of JSON if you're building complicated modules or theme. This is just an easier way to think about it all. With that, I'm going to pass it back over, I think to Dennis.
Thank you, bj. I'll bring everyone back on stage. We do not have time for a Q and A, but that is perfectly fine because this is some amazing free can content. I do want to just re state that we do have a new hug event coming up next month that is going to be on webhooks. This content is going to be available on the YouTubes which you should be subscribed to right now. We will also have all the code uploaded to the GitHub repo and I will make sure that that is available in the description for all of this. If you do have questions in the future, find us in the developer Slack. We love it. Come talk with us and have a great day y'.
All.










