Tag: Components

Interactive Web Components Are Easier Than You Think

In my last article, we saw that web components aren’t as scary as they seem. We looked at a super simple setup and made a zombie dating service profile, complete with a custom <zombie-profile> element. We reused the element for each profile and populated each one with unique info using the <slot> element.

Here’s how it all came together.

That was cool and a lot of fun (well, I had fun anyway…), but what if we take this idea one step further by making it interactive. Our zombie profiles are great, but for this to be a useful, post-apocalyptic dating experience you’d want to, you know, “Like” a zombie or even message them. That’s what we’re going to do in this article. We’ll leave swiping for another article. (Would swiping left be the appropriate thing for zombies?)

This article assumes a base level of knowledge about web components. If you’re new to the concept, that’s totally fine — the previous article should give you everything you need. Go ahead. Read it. I’ll wait. *Twiddles thumbs* Ready? Okay.

First, an update to the original version

Let’s pause for one second (okay, maybe longer) and look at the ::slotted() pseudo element. It was brought to my attention after the last article went out (thanks, Rose!) and it solves some (though not all) of the encapsulation issues I encountered. If you recall, we had some CSS styles outside of the component’s <template> and some inside a <style> element within the <template>. The styles inside the <template> were encapsulated but the ones outside were not.

But that’s where ::slotted comes into play. We declare an element in the selector like so:

::slotted(img) {   width: 100%;   max-width: 300px;   height: auto;   margin: 0 1em 0 0; }

Now, any <img> element placed in any slot will be selected. This helps a lot!

But this doesn’t solve all of our encapsulation woes. While we can select anything directly in a slot, we cannot select any descendant of the element in the slot. So, if we have a slot with children — like the interests section of the zombie profiles — we’re unable to select them from the <style> element. Also, while ::slotted has great browser support, some things (like selecting a pseudo element, e.g., ::slotted(span)::after) will work in some browsers (hello, Chrome), but won’t work in others (hello, Safari).

So, while it’s not perfect, ::slotted does indeed provide more encapsulation than what we had before. Here’s the dating service updated to reflect that:

Back to interactive web components!

First thing I’d like to do is add a little animation to spice things up. Let’s have our zombie profile pics fade in and translate up on load.

When I first attempted this, I used img and ::slotted(img) selectors to directly animate the image. But all I got was Safari support. Chrome and Firefox would not run the animation on the slotted image, but the default image animated just fine. To get it working, I wrapped the slot in a div with a .pic class and applied the animation to the div instead.

.pic {   animation: picfadein 1s 1s ease-in forwards;   transform: translateY(20px);   opacity: 0; }  @keyframes picfadein {   from { opacity: 0; transform: translateY(20px); }   to { opacity: 1; transform: translateY(0); } }

“Liking” zombies

Wouldn’t it be something to “Like” that cute zombie? I mean from the user’s perspective, of course. That seems like something an online dating service ought to have at the very least.

We’ll add a checkbox “button” that initiates a heart animation on click. Let’s add this HTML at the top of the .info div:

<input type="checkbox" id="trigger"><label class="likebtn" for="trigger">Like</label>

Here’s a heart SVG I pulled together. We know that Zombies love things to be terrible, so their heart will be an eye searing shade of chartreuse:

<svg viewBox="0 0 160 135" class="heart" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2"><path d="M61 12V0H25v12H12v13H0v36h12v13h13v12h12v12h12v12h12v13h13v12h12v-12h13v-13h11V98h13V86h-1 13V74h12V61h12V25h-12V12h-12V0H98v12H85v13H74V12H61z" fill="#7aff00"/></svg>

Here’s the important bits of the CSS that are added to the template’s <style> element:

#trigger:checked + .likebtn {   /* Checked state of the .likebtn. Flips the foreground/background color of the unchecked state. */   background-color: #960B0B;   color: #fff; }  #trigger {   /* With the label attached to the input with the for attribute, clicking the label checks/unchecks the box, so we can remove the checkbox. */   display: none; }  .heart {   /* Start the heart off so small it's nigh invisible */   transform: scale(0.0001); }  @keyframes heartanim {   /* Heart animation */   0% { transform: scale(0.0001); }   50% { transform: scale(1); }   85%, 100% { transform: scale(0.4); } }  #trigger:checked ~ .heart {   /* Checking the checkbox initiates the animation */   animation: 1s heartanim ease-in-out forwards; }

Pretty much standard HTML and CSS there. Nothing fancy or firmly web-component-ish. But, hey, it works! And since it’s technically a checkbox, it’s just as easy to “unlike” a zombie as it is to “Like” one.

Messaging zombies

If you’re a post-apocalyptic single who’s ready to mingle, and see a zombie whose personality and interests match yours, you might want to message them. (And, remember, zombies aren’t concerned about looks — they’re only interested in your braaains.)

Let’s reveal a message button after a zombie is “Liked.” The fact that the Like button is a checkbox comes in handy once again, because we can use its checked state to conditionally reveal the message option with CSS. Here’s the HTML added just below the heart SVG. It can pretty much go anywhere as long as it’s a sibling of and comes after the #trigger element.

<button type="button" class="messagebtn">Message</button>

Once the #trigger checkbox is checked, we can bring the messaging button into view:

#trigger:checked ~ .messagebtn {   display: block; }

We’ve done a good job avoiding complexity so far, but we’re going to need to reach for a little JavaScript in here. If we click the message button, we’d expect to be able to message that zombie, right? While we could add that HTML to our <template>, for demonstration purposes, lets use some JavaScript to build it on the fly.

My first (naive) assumption was that we could just add a <script> element to the template, create an encapsulated script, and be on our merry way. Yeah, that doesn’t work. Any variables instantiated in the template get instantiated multiple times and well, JavaScript’s cranky about variables that are indistinguishable from each other. *Shakes fist at cranky JavaScript*

You probably would have done something smarter and said, “Hey, we’re already making a JavaScript constructor for this element, so why wouldn’t you put the JavaScript in there?” Well, I was right about you being smarter than me.

Let’s do just that and add JavaScript to the constructor. We’ll add a listener that, once clicked, creates and displays a form to send a message. Here’s what the constructor looks like now, smarty pants:

customElements.define('zombie-profile', class extends HTMLElement {   constructor() {     super();     let profile = document.getElementById('zprofiletemplate');     let myprofile = profile.content;     const shadowRoot = this.attachShadow({       mode: 'open'     }).appendChild(myprofile.cloneNode(true));      // The "new" code     // Grabbing the message button and the div wrapping the profile for later use     let msgbtn = this.shadowRoot.querySelector('.messagebtn'),         profileEl = this.shadowRoot.querySelector('.profile-wrapper');          // Adding the event listener     msgbtn.addEventListener('click', function (e) {        // Creating all the elements we'll need to build our form       let formEl = document.createElement('form'),           subjectEl = document.createElement('input'),           subjectlabel = document.createElement('label'),           contentEl = document.createElement('textarea'),           contentlabel = document.createElement('label'),           submitEl = document.createElement('input'),           closebtn = document.createElement('button');                // Setting up the form element. The action just goes to a page I built that spits what you submitted back at you       formEl.setAttribute('method', 'post');       formEl.setAttribute('action', 'https://johnrhea.com/undead-form-practice.php');       formEl.classList.add('hello');        // Setting up a close button so we can close the message if we get shy       closebtn.innerHTML = "x";       closebtn.addEventListener('click', function () {         formEl.remove();       });        // Setting up form fields and labels       subjectEl.setAttribute('type', 'text');       subjectEl.setAttribute('name', 'subj');       subjectlabel.setAttribute('for', 'subj');       subjectlabel.innerHTML = "Subject:";       contentEl.setAttribute('name', 'cntnt');       contentlabel.setAttribute('for', 'cntnt');       contentlabel.innerHTML = "Message:";       submitEl.setAttribute('type', 'submit');       submitEl.setAttribute('value', 'Send Message');        // Putting all the elments in the Form       formEl.appendChild(closebtn);       formEl.appendChild(subjectlabel);       formEl.appendChild(subjectEl);       formEl.appendChild(contentlabel);       formEl.appendChild(contentEl);       formEl.appendChild(submitEl);        // Putting the form on the page       profileEl.appendChild(formEl);     });   } });

So far, so good!

Before we call it a day, there’s one last thing we need to address. There’s nothing worse than that first awkward introduction, so lets grease those post-apocalyptic dating wheels by adding the zombie’s name to the default message text. That’s a nice little convenience for the user.

Since we know that the first span in the <zombie-profile> element is always the zombie’s name, we can grab it and stick its content in a variable. (If your implementation is different and the elements’s order jumps around, you may want to use a class to ensure you always get the right one.)

let zname = this.getElementsByTagName("span")[0].innerHTML;

And then add this inside the event listener:

contentEl.innerHTML = "Hi " + zname + ",\nI like your braaains...";

That wasn’t so bad, was it? Now we know that interactive web components are just as un-scary as the zombie dating scene… well you know what I mean. Once you get over the initial hurdle of understanding the structure of a web component, it starts to make a lot more sense. Now that you’re armed with interactive web component skills, let’s see what you can come up with! What other sorts of components or interactions would make our zombie dating service even better? Make it and share it in the comments.


The post Interactive Web Components Are Easier Than You Think appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , ,

Web Components Are Easier Than You Think

When I’d go to a conference (when we were able to do such things) and see someone do a presentation on web components, I always thought it was pretty nifty (yes, apparently, I’m from 1950), but it always seemed complicated and excessive. A thousand lines of JavaScript to save four lines of HTML. The speaker would inevitably either gloss over the oodles of JavaScript to get it working or they’d go into excruciating detail and my eyes would glaze over as I thought about whether my per diem covered snacks.

But in a recent reference project to make learning HTML easier (by adding zombies and silly jokes, of course), the completist in me decided I had to cover every HTML element in the spec. Beyond those conference presentations, this was my first introduction to the <slot> and <template> elements. But as I tried to write something accurate and engaging, I was forced to delve a bit deeper.

And I’ve learned something in the process: web components are a lot easier than I remember.

Either web components have come a long way since the last time I caught myself daydreaming about snacks at a conference, or I let my initial fear of them get in the way of truly knowing them — probably both.

I’m here to tell you that you—yes, you—can create a web component. Let’s leave our distractions, fears, and even our snacks at the door for a moment and do this together.

Let’s start with the <template>

A <template> is an HTML element that allows us to create, well, a template—the HTML structure for the web component. A template doesn’t have to be a huge chunk of code. It can be as simple as:

<template>   <p>The Zombies are coming!</p> </template>

The <template> element is important because it holds things together. It’s like the foundation of building; it’s the base from which everything else is built. Let’s use this small bit of HTML as the template for an <apocalyptic-warning> web component—you know, as a warning when the zombie apocalypse is upon us.

Then there’s the <slot>

<slot> is merely another HTML element just like <template>. But in this case, <slot> customizes what the <template> renders on the page.

<template>   <p>The <slot>Zombies</slot> are coming!</p> </template>

Here, we’ve slotted (is that even a word?) the word “Zombies” in the templated markup. If we don’t do anything with the slot, it defaults to the content between the tags. That would be “Zombies” in this example.

Using <slot> is a lot like having a placeholder. We can use the placeholder as is, or define something else to go in there instead. We do that with the name attribute.

<template>   <p>The <slot name="whats-coming">Zombies</slot> are coming!</p> </template>

The name attribute tells the web component which content goes where in the template. Right now, we’ve got a slot called whats-coming. We’re assuming zombies are coming first in the apocalypse, but the <slot> gives us some flexibility to slot something else in, like if it ends up being a robot, werewolf, or even a web component apocalypse.

Using the component

We’re technically done “writing” the component and can drop it in anywhere we want to use it.

<apocalyptic-warning>   <span slot="whats-coming">Halitosis Laden Undead Minions</span> </apocalyptic-warning>  <template>   <p>The <slot name="whats-coming">Zombies</slot> are coming!</p> </template>

See what we did there? We put the <apocalyptic-warning> component on the page just like any other <div> or whatever. But we also dropped a <span> in there that references the name attribute of our <slot>. And what’s between that <span> is what we want to swap in for “Zombies” when the component renders.

Here’s a little gotcha worth calling out: custom element names must have a hyphen in them. It’s just one of those things you’ve gotta know going into things. The spec (which is still in flux) prescribes that to prevent conflicts in the event that HTML releases a new element with the same name.

Still with me so far? Not too scary, right? Well, minus the zombies. We still have a little work to do to make the <slot> swap possible, and that’s where we start to get into JavaScript.

Registering the component

As I said, you do need some JavaScript to make this all work, but it’s not the super complex, thousand-lined, in-depth code I always thought. Hopefully I can convince you as well.

You need a constructor function that registers the custom element. Otherwise, our component is like the undead: it’s there but not fully alive.

Here’s the constructor we’ll use:

// Defines the custom element with our appropriate name, <apocalyptic-warning> customElements.define("apocalyptic-warning",    // Ensures that we have all the default properties and methods of a built in HTML element   class extends HTMLElement {      // Called anytime a new custom element is created     constructor() {        // Calls the parent constructor, i.e. the constructor for `HTMLElement`, so that everything is set up exactly as we would for creating a built in HTML element       super();        // Grabs the <template> and stores it in `warning`       let warning = document.getElementById("warningtemplate");        // Stores the contents of the template in `mywarning`       let mywarning = warning.content;        const shadowRoot = this.attachShadow({mode: "open"}).appendChild(mywarning.cloneNode(true));     }   });

I left detailed comments in there that explain things line by line. Except the last line:

const shadowRoot = this.attachShadow({mode: "open"}).appendChild(mywarning.cloneNode(true));

We’re doing a lot in here. First, we’re taking our custom element (this) and creating a clandestine operative—I mean, shadow DOM. mode: open simply means that JavaScript from outside the :root can access and manipulate the elements within the shadow DOM, sort of like setting up back door access to the component.

From there, the shadow DOM has been created and we append a node to it. That node will be a deep copy of the template, including all elements and text of the template. With the template attached to the shadow DOM of the custom element, the <slot> and slot attribute take over for matching up content with where it should go.

Check this out. Now we can plop two instances of the same component, rendering different content simply by changing one element.

Styling the component

You may have noticed styling in that demo. As you might expect, we absolutely have the ability to style our component with CSS. In fact, we can include a <style> element right in the <template>.

<template id="warningtemplate">   <style>     p {       background-color: pink;       padding: 0.5em;       border: 1px solid red;     }   </style>      <p>The <slot name="whats-coming">Zombies</slot> are coming!</p> </template>

This way, the styles are scoped directly to the component and nothing leaks out to other elements on the same page, thanks to the shadow DOM.

Now in my head, I assumed that a custom element was taking a copy of the template, inserting the content you’ve added, and then injecting that into the page using the shadow DOM. While that’s what it looks like on the front end, that’s not how it actually works in the DOM. The content in a custom element stays where it is and the shadow DOM is sort of laid on top like an overlay.

Screenshot of the HTML source of the zombie-warning component. The custom element is expanded in the shadow dam, including the style block, the custom element, and the template.

And since the content is technically outside the template, any descendant selectors or classes we use in the template’s <style> element will have no affect on the slotted content. This doesn’t allow full encapsulation the way I had hoped or expected. But since a custom element is an element, we can use it as an element selector in any ol’ CSS file, including the main stylesheet used on a page. And although the inserted material isn’t technically in the template, it is in the custom element and descendant selectors from the CSS will work.

apocalyptic-warning span {   color: blue; }

But beware! Styles in the main CSS file cannot access elements in the <template> or shadow DOM.

Let’s put all of this together

Let’s look at an example, say a profile for a zombie dating service, like one you might need after the apocalypse. In order to style both the default content and any inserted content, we need both a <style> element in the <template> and styling in a CSS file.

The JavaScript code is exactly the same except now we’re working with a different component name, <zombie-profile>.

customElements.define("zombie-profile",   class extends HTMLElement {     constructor() {       super();       let profile = document.getElementById("zprofiletemplate");       let myprofile = profile.content;       const shadowRoot = this.attachShadow({mode: "open"}).appendChild(myprofile.cloneNode(true));     }   } );

Here’s the HTML template, including the encapsulated CSS:

<template id="zprofiletemplate">   <style>     img {       width: 100%;       max-width: 300px;       height: auto;       margin: 0 1em 0 0;     }     h2 {       font-size: 3em;       margin: 0 0 0.25em 0;       line-height: 0.8;     }     h3 {       margin: 0.5em 0 0 0;       font-weight: normal;     }     .age, .infection-date {       display: block;     }     span {       line-height: 1.4;     }     .label {       color: #555;     }     li, ul {       display: inline;       padding: 0;     }     li::after {       content: ', ';     }     li:last-child::after {       content: '';     }     li:last-child::before {       content: ' and ';     }   </style>    <div class="profilepic">     <slot name="profile-image"><img src="https://assets.codepen.io/1804713/default.png" alt=""></slot>   </div>    <div class="info">     <h2><slot name="zombie-name" part="zname">Zombie Bob</slot></h2>      <span class="age"><span class="label">Age:</span> <slot name="z-age">37</slot></span>     <span class="infection-date"><span class="label">Infection Date:</span> <slot name="idate">September 12, 2025</slot></span>      <div class="interests">       <span class="label">Interests: </span>       <slot name="z-interests">         <ul>           <li>Long Walks on Beach</li>           <li>brains</li>           <li>defeating humanity</li>         </ul>       </slot>     </div>      <span class="z-statement"><span class="label">Apocalyptic Statement: </span> <slot name="statement">Moooooooan!</slot></span>    </div> </template>

Here’s the CSS for our <zombie-profile> element and its descendants from our main CSS file. Notice the duplication in there to ensure both the replaced elements and elements from the template are styled the same.

zombie-profile {   width: calc(50% - 1em);   border: 1px solid red;   padding: 1em;   margin-bottom: 2em;   display: grid;   grid-template-columns: 2fr 4fr;   column-gap: 20px; } zombie-profile img {   width: 100%;   max-width: 300px;   height: auto;   margin: 0 1em 0 0; } zombie-profile li, zombie-profile ul {   display: inline;   padding: 0; } zombie-profile li::after {   content: ', '; } zombie-profile li:last-child::after {   content: ''; } zombie-profile li:last-child::before {   content: ' and '; }

All together now!

While there are still a few gotchas and other nuances, I hope you feel more empowered to work with the web components now than you were a few minutes ago. Dip your toes in like we have here. Maybe sprinkle a custom component into your work here and there to get a feel for it and where it makes sense.

That’s really it. Now what are you more scared of, web components or the zombie apocalypse? I might have said web components in the not-so-distant past, but now I’m proud to say that zombies are the only thing that worry me (well, that and whether my per diem will cover snacks…)


The post Web Components Are Easier Than You Think appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

Bulletproof flag components

A clever use of CSS grid from Jay Freestone to accomplish a particular variation of the media object design pattern (where the image is centered with the title) without any magic numbers anything that isn’t flexible and resiliant.

The trick is to use an “extra” row above and below the title:

The image goes on the first three rows in the first column, and the content goes in the last three rows in the second column using named grid areas:

grid-template-areas:   'signifier .'   'signifier content'   'signifier content'   '.         content';

Read Jay’s post for a little more trickery required to make it entirely resilient.

I love the kind of post that zeroes in on the mental model behind CSS grid like this. It’s like… how can I slice up this design with arbitrary columns and rows, knowing that I can place things on arbitrary rectangular combinations of cells with any type of alignment, to best suit this design?

Direct Link to ArticlePermalink


The post Bulletproof flag components appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, ,
[Top]

Styling Web Components

Nolan Lawson has a little emoji-picker-element that is awfully handy and incredibly easy to use. But considering you’d probably be using it within your own app, it should be style-able so it can incorporated nicely anywhere. How to allow that styling isn’t exactly obvious:

What wasn’t obvious to me, though, was how to allow users to style it. What if they wanted a different background color? What if they wanted the emoji to be bigger? What if they wanted a different font for the input field?

Nolan list four possibilities (I’ll rename them a bit in a way that helps me understand them).

  1. CSS Custom Properties: Style things like background: var(--background, white);. Custom properties penetrate the Shadow DOM, so you’re essentially adding styling hooks.
  2. Pre-built variations: You can add a class attribute to the custom elements, which are easy to access within CSS inside the Shadow DOM thanks to the pseudo selectors, like :host(.dark) { background: black; }.
  3. Shadow parts: You add attributes to things you want to be style-able, like <span part="foo">, then CSS from the outside can reach in like custom-component::part(foo) { }.
  4. User forced: Despite the nothing in/nothing out vibe of the Shadow DOM, you can always reach the element.shadowRoot and inject a <style>, so there is always a way to get styles in.

It’s probably worth a mention that the DOM you slot into place is style-able from “outside” CSS as it were.

This is such a funky problem. I like the Shadow DOM because it’s the closest thing we have on the web platform to scoped styles which are definitely a good idea. But I don’t love any of those styling solutions. They all seem to force me into thinking about what kind of styling API I want to offer and document it, while not encouraging any particular consistency across components.

To me, the DOM already is a styling API. I like the scoped protection, but there should be an easy way to reach in there and style things if I want to. Seems like there should be a very simple CSS-only way to reach inside and still use the cascade and such. Maybe the dash-separated custom-element name is enough? my-custom-elemement li { }. Or maybe it’s more explicit, like @shadow my-custom-element li { }. I just think it should be easier. Constructable Stylesheets don’t seem like a step toward make it easier, either.

Last time I was thinking about styling web components, I was just trying to figure out how to it works in the first place, not considering how to expose styling options to consumers of the component.

Does this actually come up as a problem in day-to-day work? Sure does.

I don’t see any particularly good options in that thread (yet) for the styling approach. If I was Dave, I’d be tempted to just do nothing. Offer minimal styling, and if people wanna style it, they can do it however they want from their copy of the component. Or they can “force” the styles in, meaning you have complete freedom.


The post Styling Web Components appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

,
[Top]

Components: Server-Side vs. Client-Side

Building a website in 2021? I’m guessing you’re going to take a component-driven approach. It’s all the chatter these days. React and Vue are everywhere (is Angular still a thing?), while other emerging frameworks continue to attempt a push into the spotlight.

Over the last decade or so we’ve seen an explosion of frameworks and tools that help us build sites systematically using components. Early frameworks like AngularJS helped shape the generic concept of web components. Web components are also reusable bits of HTML code that are written in JavaScript and made functional by the browser. They are client-side components.

But components, in a more generic sense, have actually been around much longer. In fact, they go back to the early days of the web. They just haven’t typically been called components, though they still function as such. Server components are also reusable bits of code, but are compiled into HTML before the browser sees them. They are server-side components, and they are still very much a thing today.

Even in a world in which all it seems like we hear is “React, React, React,” both types of components are still relevant and can help us build super awesome websites. Let’s explore how client and server components differ from one another. That will give us a clearer picture of where we came from. And then we’ll have the information we need to dream about the future.

Rendering

Perhaps the biggest difference between client-side and server-side components is what makes them what they are. That is the thing that is responsible for rendering them.

Server components are rendered by — you guessed it! — the server. They aren’t typically referred to as components. They’re often called partials, includes, snippets, or templates, depending on the framework in which they are used.

Server components can take two flavors. The first is the classic approach, which is to render components in real-time based on a request from the client. See here:

Illustrated diagram of a browser making a request to an app server that requests components, which compile to a page and are returned to the app server to go back to the browser.
Server-side rendered components

The second flavor is the Jamstack approach. In this case, the entire site is compiled during a build a process, and static HTML is already available when requested by the client. See here:

An illustrated diagram showing the browser requests a page that has already been created, which consists of components that have gone through a build process from the server, then returns to the browser.
Server components on a Jamstack site have already been compiled into HTML.

In both cases, the client (i.e. your browser) never sees the distinction between your components. It simply receives a bunch of HTML from the server.

Client components, on the other hand, are rendered by — you are two-for-two and on a ROLL! — the client. They are written in JavaScript and rendered by the client (your browser). Because the server is the server and it knows all, it can know about your client components, but whether it cares enough to do anything with them depends on the framework you’re using.

Like server components, there are also two flavors of client components. The first is the more official web component, which makes use of the shadow DOM. The shadow DOM helps with encapsulating styles and other functionality (we’ll talk more about this later). Frameworks like Polymer and Stencil make use of the shadow DOM.

The more popular frameworks, like React and Vue, represent the second flavor of component, which handles DOM manipulation and scoping on their own.

Interactivity

Because server components are just HTML when they are sent to the client, if they are to be interactive on the front end, the application must load JavaScript code separately.

Consider a countdown timer. Its presentation is determined by HTML and CSS (we‘ll come back to the CSS part). But if it is to do its thing (count), it also needs some JavaScript. That means not just bringing in that JavaScript, but also having a means by which the JavaScript can attach itself to the countdown’s HTML element(s), which must either be done manually or with (yet) another framework.

An illustrated diagram showing the browser requesting both a JavaScript bundler containing the JavaScript for the component, and a page with the component's markup that both are returned to the browser.
A component’s HTML and JavaScript are separated in SSR components.

Though this may feel unnecessarily tedious (especially if you’ve been around long enough to have been forced into this approach), there are benefits to it. It is a clear separation of concerns, where server-side code lives in one place, while the functionality lives in another. And it brings only the code it needs for the interactivity (theoretically), which can lessen the burden on the browser.

With client components, the markup and interactivity tend to be tightly coupled, often in the same file or directory. While this can quickly become a mess if you’re not diligent about staying organized, one major benefit to client components is that they already have access to the client. And because they are written in JavaScript, their functionality can ship right alongside their markup (and styles).

A diagram showing the same flow as before, but both the component markup and JavaScript are included in the bundler, which then renders on the page.
Client-side components are all wrapped up in JavaScript code.

Performance

In a one-to-one comparison, server-side components tend to perform better. When the page that a browser receives contains everything it needs for presentation, it’s going to be able to deliver that presentation to the user much quicker.

Diagram showing the browser asking for a page of HTML in a single request.
Technically all you need when rendering SSR components is a single request.

Because client-side components require JavaScript, the browser must download or process additional information (often in separate files) to be able to render the component.

Same diagram as before, but a bundler is requested by the browser along with the page.
Client component often require more code and requests.

That said, client-side components are often used within the context of a larger framework. React has Gatsby and Next, while Vue has Nuxt. These frameworks have mechanisms for creating a superior in-app experience. What I mean is that, while they may be slower to load the first page you visit on a site, they can then focus their energy on delivering subsequent views extremely fast — often faster than a server-side rendered site can deliver its content.

If you’re thinking, Yeah but what about pre-rendering and…

Yes, you’re right. We’ll get there. Also, no more spoilers, please. The rest of us are along for the ride.

Languages

Server components can be written in (almost) any server-side language. This enables you to write your templates in the same language as your application’s logic. For example, applications written with Ruby on Rails use ERB templating by default, which is a form of Ruby. Thus, Rails apps use the same language for the application itself as it does for its components.

The reason client components are written in JavaScript is because that’s the language browsers parse for interactivity on a website. However, JavaScript also has server-based runtimes, the most popular of which is Node.js. That means code for client components could be written in the same language as the application, as long as the application is written with Node (or similar).

Styling (CSS)

When it comes to styling components, server-side components run into the same trouble they face with JavaScript. The styles are typically detached from the components, and require a bit of extra effort to tie styles to the elements on the page.

However, there are frameworks like Tailwind CSS that are working to make this process less painful.

Many client-side component libraries come with CSS support (or at least a pattern for styling) right out of the box. That often means including the styles in the same file as the markup and logic, which can get messy. But typically, with a little effort, you can adjust that approach to your liking.

Welcome to the (hybrid) future

Neither type of component is the answer by itself. Server-side components require additional effort in styling and interactivity that feels unnecessary when we look at the offerings of client components. But then client components have a tendency to take away from performance on the front end. And because the success of a website often depends on user engagement, a lack of performance can hurt the end result and be enough not to want to use client components.

What does that mean for a future that demands both performance and a good developer experience? More than likely, a hybrid approach.

Components are going to have to be rendered on the server side. They just are. That‘s how we optimize performance, and good performance is going to continue to be an attribute of successful websites. But, now that we’ve seen the ease of front-end logic and interactivity using frameworks, again, like React and Vue, those frameworks are here to stay (at least for awhile).

So where are we going?

I think we’re going to see these components come together in three ways in the very near future.

1. Advancement of JavaScript framework frameworks

Remember when you thought up that spoiler about pre-rendering? Well, let’s talk about it now.

Frameworks like Gatsby, Next, and Nuxt act as front-end engines built on top of component frameworks, like React and Vue. They bring together tooling to build a comprehensive front-end experience using their preferred framework. One such feature is pre-rendering, which means these engines will introspect components and then write static HTML on the page while the site is being built. Then, when users view that page, it‘s actually already there. They don’t need JavaScript to view it.

However, JavaScript comes into play through a process called hydration. After the page loads and your user sees all the (static) content, that’s when JavaScript goes to work. It takes over the components to make them interactive. This provides the opportunity to build a client-side, component-based website with some of the benefits of the server, namely performance and SEO.

These tools have gotten super popular because of this approach, and I suspect we’ll see them continue to advance.

2. Baked-in client-side pre-rendering

That’s a lot of compound words.

What I‘ve been thinking about a lot the last couple years is: Why doesn’t React (or Vue) take on server-side rendering? They do, it’s just not super easy to understand or implement without another framework to help.

On one hand, I understand the single-responsibility principle, and that these component frameworks are just ways to build client-side components. But it felt like a huge miss to delegate server-side rendering to bigger, more complex tools like Gatsby and Next (among others).

Well, React has started moving that way. Vue is already there. And Svelte has made this approach a priority from the beginning.

I think we‘re going to see a lot more development while these traditionally client-side-focused tools solve for server-side rendering. I suspect that also means we‘ll hear a little more from Svelte in the future, which seems like it’s ahead of the game in this regard.

That may also lead to the development of more competitors to bulkier tools like Gatsby and Next. For example, look at what Netlify is doing with their website. It‘s an Eleventy project that pulls in Vue components and renders them for use on the server. What it’s missing is the hydration and interactivity piece. I expect that to come together in the very near future.

3. Server-side component interactivity

And still, we can‘t discount the continued use of server-side components. The one side effect of both of the other two advancements is that they’re still using JavaScript frameworks that can feel unnecessary when you only need just a little interactivity.

There must be a simpler way to add just a little JavaScript to make a server-side component that are written in a server-side language more interactive.

Solving that problem seems to be the approach from the folks at Basecamp, who just released Hotwire, which is a means to bring some of the gains of client components to the server, using (almost) any server-side language.

I don‘t know if that means we‘re going to see competition to Hotwire emerge right away. But I do think Hotwire is going to get some attention. And that might just bring folks back to working with full-stack monolithic frameworks like Rails. (Personally, I love that Rails hasn’t become obsolete in this JavaScript-focused world. The more competition we have, the better the web gets.)

Where do you think all this component business is going? Let’s talk about it.


The post Components: Server-Side vs. Client-Side appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, ,
[Top]

Building Flexible Components With Transparency

Good thinking from Paul Herbert on the Cloudfour blog about colorizing a component. You might look at a design comp and see a card component with a header background of #dddddd, content background of #ffffff, on an overall background of #eeeeee. OK, easy enough. But what if the overall background becomes #dddddd? Now your header looks lost within it.

That darker header? Design-wise, it’s not being exactly #dddddd that’s important; it’s about looking slightly darker than the background. When that’s the case, a background of, say rgba(0, 0, 0, 0.135) is more resiliant.

That will then remain resilient against backgrounds of any kind.

Direct Link to ArticlePermalink


The post Building Flexible Components With Transparency appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

Creating UI Components in SVG

I’m thoroughly convinced that SVG unlocks a whole entire world of building interfaces on the web. It might seem daunting to learn SVG at first, but you have a spec that was designed to create shapes and yet, still has elements, like text, links, and aria labels available to you. You can accomplish some of the same effects in CSS, but it’s a little more particular to get positioning just right, especially across viewports and for responsive development.

What’s special about SVG is that all the positioning is based on a coordinate system, a little like the game Battleship. That means deciding where everything goes and how it’s drawn, as well as how it’s relative to each other, can be really straightforward to reason about. CSS positioning is for layout, which is great because you have things that correspond to one another in terms of the flow of the document. This otherwise positive trait is harder to work with if you’re making a component that’s very particular, with overlapping and precisely placed elements.

Truly, once you learn SVG, you can draw anything, and have it scale on any device. Even this very site uses SVG for custom UI elements, such as my avatar, above (meta!).

That little half circle below the author image is just SVG markup.

We won’t cover everything about SVGs in this post (you can learn some of those fundamentals here, here, here and here), but in order to illustrate the possibilities that SVG opens up for UI component development, let’s talk through one particular use case and break down how we would think about building something custom.

The timeline task list component

Recently, I was working on a project with my team at Netlify. We wanted to show the viewer which video in a series of videos in a course they were currently watching. In other words, we wanted to make some sort of thing that’s like a todo list, but shows overall progress as items are completed. (We made a free space-themed learning platform and it’s hella cool. Yes, I said hella.)

Here’s how that looks:

So how would we go about this? I’ll show an example in both Vue and React so that you can see how it might work in both frameworks.

The Vue version

We decided to make the platform in Next.js for dogfooding purposes (i.e. trying out our own Next on Netlify build plugin), but I’m more fluent in Vue so I wrote the initial prototype in Vue and ported it over to React.

Here is the full CodePen demo:

Let’s walk through this code a bit. First off, this is a single file component (SFC), so the template HTML, reactive script, and scoped styles are all encapsulated in this one file.

We’ll store some dummy tasks in data, including whether each task is completed or not. We’ll also make a method we can call on a click directive so that we can toggle whether the state is done or not.

<script> export default {   data() {     return {       tasks: [         {           name: 'thing',           done: false         },         // ...       ]     };   },   methods: {     selectThis(index) {       this.tasks[index].done = !this.tasks[index].done     }   } }; </script> 

Now, what we want to do is create an SVG that has a flexible viewBox depending on the amount of elements. We also want to tell screen readers that this a presentational element and that we will provide a title with a unique id of timeline. (Get more information on creating accessible SVGs.)

<template>   <div id="app">     <div>       <svg :viewBox="`0 0 30 $ {tasks.length * 50}`"            xmlns="http://www.w3.org/2000/svg"             width="30"             stroke="currentColor"             fill="white"            aria-labelledby="timeline"            role="presentation">            <title id="timeline">timeline element</title>         <!-- ... -->       </svg>     </div>   </div> </template>

The stroke is set to currentColor to allow for some flexibility — if we want to reuse the component in multiple places, it will inherit whatever color is used on the encapsulating div.

Next, inside the SVG, we want to create a vertical line that’s the length of the task list. Lines are fairly straightforward. We have x1 and x2 values (where the line is plotted on the x-axis), and similarly, y1 and y2.

<line x1="10" x2="10" :y1="num2" :y2="tasks.length * num1 - num2" />

The x-axis stays consistently at 10 because we’re drawing a line downward rather than left-to-right. We’ll store two numbers in data: the amount we want our spacing to be, which will be num1, and the amount we want our margin to be, which will be num2.

data() {   return {     num1: 32,     num2: 15,     // ...   } }

The y-axis starts with num2, which is subtracted from the end, as well as the margin. The tasks.length is multiplied by the spacing, which is num1.

Now, we’ll need the circles that lie on the line. Each circle is an indicator for whether a task has been completed or not. We’ll need one circle for each task, so we’ll use v-for with a unique key, which is the index (and is safe to use here as they will never reorder). We’ll connect the click directive with our method and pass in the index as a param as well.

CIrcles in SVG are made up of three attributes. The middle of the circle is plotted at cx and cy, and then we draw a radius with r. Like the line, cx starts at 10. The radius is 4 because that’s what’s readable at this scale. cy will be spaced like the line: index times the spacing (num1), plus the margin (num2).

Finally, we’ll put use a ternary to set the fill. If the task is done, it will be filled with currentColor. If not, it will be filled with white (or whatever the background is). This could be filled with a prop that gets passed in the background, for instance, where you have light and dark circles.

<circle    @click="selectThis(i)"    v-for="(task, i) in tasks"   :key="task.name"   cx="10"   r="4"   :cy="i * num1 + num2"   :fill="task.done ? 'currentColor' : 'white'"   class="select"/>

Finally, we are using CSS grid to align a div with the names of tasks. This is laid out much in the same way, where we’re looping through the tasks, and are also tied to that same click event to toggle the done state.

<template>   <div>     <div        @click="selectThis(i)"       v-for="(task, i) in tasks"       :key="task.name"       class="select">       {{ task.name }}     </div>   </div> </template>

The React version

Here is where we ended up with the React version. We’re working towards open sourcing this so that you can see the full code and its history. Here are a few modifications:

  • We’re using CSS modules rather than the SCFs in Vue
  • We’re importing the Next.js link, so that rather than toggling a “done” state, we’re taking a user to a dynamic page in Next.js
  • The tasks we’re using are actually stages of the course —or “Mission” as we call them — which are passed in here rather than held by the component.

Most of the other functionality is the same 🙂

import styles from './MissionTracker.module.css'; import React, { useState } from 'react'; import Link from 'next/link';  function MissionTracker({ currentMission, currentStage, stages }) {  const [tasks, setTasks] = useState([...stages]);  const num1 = [32];  const num2 = [15];   const updateDoneTasks = (index) => () => {    let tasksCopy = [...tasks];    tasksCopy[index].done = !tasksCopy[index].done;    setTasks(tasksCopy);  };   const taskTextStyles = (task) => {    const baseStyles = `$ {styles['tracker-select']} $ {styles['task-label']}`;     if (currentStage === task.slug.current) {      return baseStyles + ` $ {styles['is-current-task']}`;    } else {      return baseStyles;    }  };   return (    <div className={styles.container}>      <section>        {tasks.map((task, index) => (          <div            key={`mt-$ {task.slug}-$ {index}`}            className={taskTextStyles(task)}          >            <Link href={`/learn/$ {currentMission}/$ {task.slug.current}`}>              {task.title}            </Link>          </div>        ))}      </section>       <section>        <svg          viewBox={`0 0 30 $ {tasks.length * 50}`}          className={styles['tracker-svg']}          xmlns="http://www.w3.org/2000/svg"          width="30"          stroke="currentColor"          fill="white"          aria-labelledby="timeline"          role="presentation"        >          <title id="timeline">timeline element</title>           <line x1="10" x2="10" y1={num2} y2={tasks.length * num1 - num2} />          {tasks.map((task, index) => (            <circle              key={`mt-circle-$ {task.name}-$ {index}`}              onClick={updateDoneTasks(index)}              cx="10"              r="4"              cy={index * +num1 + +num2}              fill={                task.slug.current === currentStage ? 'currentColor' : 'black'              }              className={styles['tracker-select']}            />          ))}        </svg>      </section>    </div>  ); }  export default MissionTracker;

Final version

You can see the final working version here:

This component is flexible enough to accommodate lists small and large, multiple browsers, and responsive sizing. It also allows the user to have better understanding of where they are in their progress in the course.

But this is just one component. You can make any number of UI elements: knobs, controls, progress indicators, loaders… the sky’s the limit. You can style them with CSS, or inline styles, you can have them update based on props, on context, on reactive data, the sky’s the limit! I hope this opens some doors on how you yourself can develop more engaging UI elements for the web.


The post Creating UI Components in SVG appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

,
[Top]

Using Your Own Design System with KendoReact Components

Maybe you’ve already heard of (or even worked with!) KendoReact. It’s popped up in some of my day-to-day conversations, especially those about working with design systems and React. You could think of it as a component library like Bootstrap or Material Design, except the components in KendoReact are far more robust. These are interactive, state-driven components ready to start building full-blown UI’s right out of the gate (not to mention, if you want to use Bootstrap as the theme, you absolutely can).

Whenever you’re thinking about using a UI library, you need to think about the styling capabilities. Are you able to really express your brand with these? Were they meant to be styled? What is the styling experience going to be like?

Fortunately, KendoReact really makes styling a citizen of the entire UI library.

KendoReact is a collection of UI components for building sites. It’s a pretty massive one. Over 80 by my count, and that doesn’t include the child components of heavy lifters like the <Grid /> family.

Here’s one, the <DropDownList />, and just using the default theme (even that is optional):

If I want to style this, I don’t need any special proprietary skills, I can just use CSS. Here’s me forcing a whole new look onto it with different colors and fonts, with just some simple CSS:

But hey, maybe you want to do something a bit more systematized than cowboying some random override CSS. I don’t blame you. Good news: KendoReact themes are Sass-powered. So you can control a lot of the colorization and styling just by changing a few Sass variables.

They have a whole theme builder you can use right on their site that spits out exactly what you need. Say you want to start from their base theme and go from there, select the Default theme:

Then you can play with all the colors in the UI to your liking. Here’s me poking at a theme with some CSS-Tricks colors.

I can download that from the site which will give me the variables as a SCSS file that I can apply before the default theme in my build (there is a great tutorial covering how to do that over on the Telerik blog). Plus, it gives me the whole dang CSS file of the theme if I want to use it that way, which is simple and quick. Here’s me using their conversational chat widget with that theme:

Again, I can start with Bootstrap, I can start with Material, I can start with their default theme, or I can start from scratch. Styling is totally up to me. Each theme has its perks and, as you might expect, are super flexible as far as configuring colors, fonts, and other design elements.

If you really get into this, of course you’ll be consulting their docs and finding your way around there (it’s nice to know they have really comprehensive docs). It’s all pretty straightforward though, you’ll do great! If you need to get going building out a state-driven interactive interface quickly without sacrificing any customizability or power, you’ll find KendoReact is your friend.


The post Using Your Own Design System with KendoReact Components appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , ,
[Top]

Our Best Posts on Web Components

A bit of backstory on why this page exists…

I made a fancy new Gutenberg block to insert posts into other posts, so this page is an example of using that (heavily) make a meta blog post about other blog posts. For the most part, topical pages like this are best served by tag pages. For example, our tag page for Web Components has more posts and is sortable in ways this is not. This is just a way to make a curated and hand-sorted grouping of posts, which is something we’ve done for a while with “Guide Collections”. But, we’re spinning down Guide Collections and redirecting them to tag pages and pages like this because they are easier to maintain.


The post Our Best Posts on Web Components appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, ,
[Top]

The failed promise of Web Components

Lea has some words:

Perusing the components on webcomponents.org fills me with anxiety, and I’m perfectly comfortable writing JS — I write JS for a living! What hope do those who can’t write JS have? Using a custom element from the directory often needs to be preceded by a ritual of npm flugelhorn, import clownshoes, build quux, all completely unapologetically because “here is my truckload of dependencies, yeah, what”. Many steps are even omitted, likely because they are “obvious”.

When I wrote A Bit on Web Component Libraries, I was told the main thing I got wrong is that:

The idea was to make primitives that libraries could build on top of so they could ship less code. It was always the intention that you would use a library with them.

It was many years ago that HTML imports died. It was Dave’s pet peeve about Web Components for a long time. So I guess after that, it was a all-JavaScript-or-bust approach for Web Components. And I hate to say it, but it feels like it’s a lot closer to a bust than a boon.

I’m still optimistic though. Web Components can do some very cool stuff that only Web Components can do. The Shadow DOM being a big part of that. For example, I remember years ago Twitter experimented with making embedded Tweets into Web Components (instead of iframes) because it was way faster (in every way). That never manifested (🤷‍♂️), but it seemed like a damn fine idea to me.

I think the styling story is a big deal. I bet I’d reach for them at least slightly more if styling them wasn’t so weird. I saw Scott was asking about it just today and 75% of people wish there was a way to just reach into that Shadow DOM and style it from regular CSS. I get why that needs to be protected (that’s a huge point of the Shadow DOM in the first place), but having to very explicitly reach in seems like enough protection to me.

Direct Link to ArticlePermalink


The post The failed promise of Web Components appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, ,
[Top]