Tag: Elements

A Little Reminder That Pseudo Elements are Children, Kinda.

Here’s a container with some child elements:

<div class="container">   <div>item</div>   <div>item</div>   <div>item</div> </div>

If I do:

.container::before {   content: "x" }

I’m essentially doing:

<div class="container">   [[[ ::before psuedo-element here ]]]   <div>item</div>   <div>item</div>   <div>item</div> </div>

Which will behave just like a child element mostly. One tricky thing is that no selector selects it other than the one you used to create it (or a similar selector that is literally a ::before or ::after that ends up in the same place).

To illustrate, say I set up that container to be a 2×2 grid and make each item a kind of pillbox design:

.container {   display: grid;   grid-template-columns: 1fr 1fr;   grid-gap: 0.5rem; }  .container > * {   background: darkgray;   border-radius: 4px;   padding: 0.5rem; }

Without the pseudo-element, that would be like this:

Six items in a clean two-by-two grid

If I add that pseudo-element selector as above, I’d get this:

Six items in a two-by-two grid, but with a seventh item at the beginning, pushing elements over by one

It makes sense, but it can also come as a surprise. Pseudo-elements are often decorative (they should pretty much only be decorative), so having it participate in a content grid just feels weird.

Notice that the .container > * selector didn’t pick it up and make it darkgray because you can’t select a pseudo-element that way. That’s another minor gotcha.

In my day-to-day, I find pseudo-elements are typically absolutely-positioned to do something decorative — so, if you had:

.container::before {   content: "";   position: absolute;   /* Do something decorative */ }

…you probably wouldn’t even notice. Technically, the pseudo-element is still a child, so it’s still in there doing its thing, but isn’t participating in the grid. This isn’t unique to CSS Grid either. For instance, you’ll find by using flexbox that your pseudo-element becomes a flex item. You’re free to float your pseudo-element or do any other sort of layout with it as well.

DevTools makes it fairly clear that it is in the DOM like a child element:

DevTools with a ::before element selected

There are a couple more gotchas!

One is :nth-child(). You’d think that if pseduo-elements are actually children, they would effect :nth-child() calculations, but they don’t. That means doing something like this:

.container > :nth-child(2) {   background: red; }

…is going to select the same element whether or not there is a ::before pseudo-element or not. The same is true for ::after and :nth-last-child and friends. That’s why I put “kinda” in the title. If pseudo-elements were exactly like child elements, they would affect these selectors.

Another gotcha is that you can’t select a pseudo-element in JavaScript like you could a regular child element. document.querySelector(".container::before"); is going to return null. If the reason you are trying to get your hands on the pseudo-element in JavaScript is to see its styles, you can do that with a little CSSOM magic:

const styles = window.getComputedStyle(   document.querySelector('.container'),   '::before' ); console.log(styles.content); // "x" console.log(styles.color); // rgb(255, 0, 0) console.log(styles.getPropertyValue('color'); // rgb(255, 0, 0)

Have you run into any gotchas with pseudo-elements?

The post A Little Reminder That Pseudo Elements are Children, Kinda. appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,

Web Standards Meet User-Land: Using CSS-in-JS to Style Custom Elements

The popularity of CSS-in-JS has mostly come from the React community, and indeed many CSS-in-JS libraries are React-specific. However, Emotion, the most popular library in terms of npm downloads, is framework agnostic.

Using the shadow DOM is common when creating custom elements, but there’s no requirement to do so. Not all use cases require that level of encapsulation. While it’s also possible to style custom elements with CSS in a regular stylesheet, we’re going to look at using Emotion.

We start with an install:

npm i emotion

Emotion offers the css function:

import {css} from 'emotion';

css is a tagged template literal. It accepts standard CSS syntax but adds support for Sass-style nesting.

const buttonStyles = css`   color: white;   font-size: 16px;   background-color: blue;    &:hover {     background-color: purple;   } `

Once some styles have been defined, they need to be applied. Working with custom elements can be somewhat cumbersome. Libraries — like Stencil and LitElement — compile to web components, but offer a friendlier API than what we’d get right out of the box.

So, we’re going to define styles with Emotion and take advantage of both Stencil and LitElement to make working with web components a little easier.

Applying styles for Stencil

Stencil makes use of the bleeding-edge JavaScript decorators feature. An @Component decorator is used to provide metadata about the component. By default, Stencil won’t use shadow DOM, but I like to be explicit by setting shadow: false inside the @Component decorator:

@Component({   tag: 'fancy-button',   shadow: false })

Stencil uses JSX, so the styles are applied with a curly bracket ({}) syntax:

export class Button {   render() {     return <div><button class={buttonStyles}><slot/></button></div>   } }

Here’s how a simple example component would look in Stencil:

import { css, injectGlobal } from 'emotion'; import {Component} from '@stencil/core';  const buttonStyles = css`   color: white;   font-size: 16px;   background-color: blue;   &:hover {     background-color: purple;   } ` @Component({   tag: 'fancy-button',   shadow: false }) export class Button {   render() {     return <div><button class={buttonStyles}><slot/></button></div>   } }

Applying styles for LitElement

LitElement, on the other hand, use shadow DOM by default. When creating a custom element with LitElement, the LitElement class is extended. LitElement has a createRenderRoot() method, which creates and opens a shadow DOM:

createRenderRoot()  {   return this.attachShadow({mode: 'open'}); }

Don’t want to make use of shadow DOM? That requires re-implementing this method inside the component class:

class Button extends LitElement {   createRenderRoot() {       return this;   } }

Inside the render function, we can reference the styles we defined using a template literal:

render() {   return html`<button class=$ {buttonStyles}>hello world!</button>` }

It’s worth noting that when using LitElement, we can only use a slot element when also using shadow DOM (Stencil does not have this problem).

Put together, we end up with:

import {LitElement, html} from 'lit-element'; import {css, injectGlobal} from 'emotion'; const buttonStyles = css`   color: white;   font-size: 16px;   background-color: blue;   &:hover {     background-color: purple;   } `  class Button extends LitElement {   createRenderRoot() {     return this;   }   render() {     return html`<button class=$ {buttonStyles}>hello world!</button>`   } }  customElements.define('fancy-button', Button);

Understanding Emotion

We don’t have to stress over naming our button — a random class name will be generated by Emotion.

We could make use of CSS nesting and attach a class only to a parent element. Alternatively, we can define styles as separate tagged template literals:

const styles = {   heading: css`     font-size: 24px;   `,   para: css`     color: pink;   ` } 

And then apply them separately to different HTML elements (this example uses JSX):

render() {   return <div>     <h2 class={styles.heading}>lorem ipsum</h2>     <p class={styles.para}>lorem ipsum</p>   </div> }

Styling the container

So far, we’ve styled the inner contents of the custom element. To style the container itself, we need another import from Emotion.

import {css, injectGlobal} from 'emotion';

injectGlobal injects styles into the “global scope” (like writing regular CSS in a traditional stylesheet — rather than generating a random class name). Custom elements are display: inline by default (a somewhat odd decision from spec authors). In almost all cases, I change this default with a style applied to all instances of the component. Below are the buttonStyles which is how we can change that up, making use of injectGlobal:

injectGlobal` fancy-button {   display: block; } `

Why not just use shadow DOM?

If a component could end up in any codebase, then shadow DOM may well be a good option. It’s ideal for third party widgets — any CSS that’s applied to the page simply won’t break the the component, thanks to the isolated nature of shadow DOM. That’s why it’s used by Twitter embeds, to take one example. However, the vast majority of us make components for for a particular site or app and nowhere else. In that situation, shadow DOM can arguably add complexity with limited benefit.

The post Web Standards Meet User-Land: Using CSS-in-JS to Style Custom Elements appeared first on CSS-Tricks.

CSS-Tricks

, , , , , , ,
[Top]

Text Wrapping & Inline Pseudo Elements

I love posts like this. It’s just about adding a little icon to the end of certain links, but it ends up touching on a million things along the way. I think this is an example of why some people find front-end fun and some people rather dislike it.

Things involved:

  1. Cool [attribute] selectors that identify off-site links
  2. Deliberating on whether it’s OK to use additional HTML within links or not
  3. Usage of white-space
  4. Combining margin-left and padding-right to place icons into placeholder space
  5. Using custom properties to keep things easier
  6. Usage of inline SVG versus background SVG
  7. Considering inline versus inline-block
  8. Using masks

Direct Link to ArticlePermalink

The post Text Wrapping & Inline Pseudo Elements appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

The Elements of UI Engineering

I really enjoyed this post by Dan Abramov. He defines his work as a UI engineer and I especially like what he writes about his learning experience:

My biggest learning breakthroughs weren’t about a particular technology. Rather, I learned the most when I struggled to solve a particular UI problem. Sometimes, I would later discover libraries or patterns that helped me. In other cases, I’d come up with my own solutions (both good and bad ones).

It’s this combination of understanding the problems, experimenting with the solutions, and applying different strategies that led to the most rewarding learning experiences in my life. This post focuses on just the problems.

He then breaks those problems down into a dozen different areas: consistency, responsiveness, latency, navigation, staleness, entropy, priority, accessibility, internationalization, delivery, resilience, and abstraction. This is a pretty good list of what a front-end developer has to be concerned about on a day-to-day basis, but I also feel like this is perhaps the best description of what I believe my own skills are besides being “the person who cares about component design and CSS.”

I also love what Dan has to say about accessibility:

Inaccessible websites are not a niche problem. For example, in UK disability affects 1 in 5 people. (Here’s a nice infographic.) I’ve felt this personally too. Though I’m only 26, I struggle to read websites with thin fonts and low contrast. I try to use the trackpad less often, and I dread the day I’ll have to navigate poorly implemented websites by keyboard. We need to make our apps not horrible to people with difficulties — and the good news is that there’s a lot of low-hanging fruit. It starts with education and tooling. But we also need to make it easy for product developers to do the right thing. What can we do to make accessibility a default rather than an afterthought?

This is a good reminder that front-end development is not a problem to be solved, except I reckon Dan’s post is more helpful and less snarky than my take on it.

Anywho, we all want accessible interfaces so that every browser can access our work making use of beautiful and consistent mobile interactions, instantaneous performance, and a design system teams can utilize to click-clack components together with little-to-no effort. But these things are only possible if others recognize that UI and front-end development are a worthy fields.

Direct Link to ArticlePermalink

The post The Elements of UI Engineering appeared first on CSS-Tricks.

CSS-Tricks

,
[Top]

A Guide to Custom Elements for React Developers

I had to build a UI recently and (for the first time in a long while) I didn’t have the option of using React.js, which is my preferred solution for UI these days. So, I looked at what the built-in browser APIs had to offer and saw that using custom elements (aka Web Components) may just be the remedy that this React developer needed.

Custom elements can offer the same general benefits of React components without being tied to a specific framework implementation. A custom element gives us a new HTML tag that we can programmatically control through a native browser API.

Let’s talk about the benefits of component-based UI:

  • Encapsulation – concerns scoped to that component remain in that component’s implementation
  • Reusability – when the UI is separated into more generic pieces, they’re easier to break into patterns that you’re more likely to repeat
  • Isolation – because components are designed to be encapsulated and with that, you get the added benefit of isolation, which allows you scope bugs and changes to a particular part of your application easier

Use cases

You might be wondering who is using custom elements in production. Notably:

  • GitHub is using custom elements for their modal dialogs, autocomplete and display time.
  • YouTube’s new web app is built with Polymer and web components.

Similarities to the Component API

When trying to compare React Components versus custom elements, I found the APIs really similar:

  • They’re both classes that aren’t “new” and are able that extend a base class
  • They both inherit a mounting or rendering lifecycle
  • They both take static or dynamic input via props or attributes

Demo

So, let’s build a tiny application that lists details about a GitHub repository.

Screenshot of end result

If I were going to approach this with React, I would define a simple component like this:

<Repository name="charliewilco/obsidian" />

This component takes a single prop — the name of the repository — and we implement it like this:

class Repository extends React.Component {   state = {     repo: null   };    async getDetails(name) {     return await fetch(`https://api.github.com/repos/$ {name}`, {       mode: 'cors'     }).then(res => res.json());   }    async componentDidMount() {     const { name } = this.props;     const repo = await this.getDetails(name);     this.setState({ repo });   }    render() {     const { repo } = this.state;      if (!repo) {       return <h1>Loading</h1>;     }      if (repo.message) {       return <div className="Card Card--error">Error: {repo.message}</div>;     }      return (       <div class="Card">         <aside>           <img             width="48"             height="48"             class="Avatar"             src={repo.owner.avatar_url}             alt="Profile picture for $ {repo.owner.login}"           />         </aside>         <header>           <h2 class="Card__title">{repo.full_name}</h2>           <span class="Card__meta">{repo.description}</span>         </header>       </div>     );   } }

See the Pen React Demo – GitHub by Charles (@charliewilco) on CodePen.

To break this down further, we have a component that has its own state, which is the repo details. Initially, we set it to be null because we don’t have any of that data yet, so we’ll have a loading indicator while the data is fetched.

During the React lifecycle, we’ll use fetch to go get the data from GitHub, set up the card, and trigger a re-render with setState() after we get the data back. All of these different states the UI takes are represented in the render() method.

Defining / Using a Custom Element

Doing this with custom elements is a little different. Like the React component, our custom element will take a single attribute — again, the name of the repository — and manage its own state.

Our element will look like this:

<github-repo name="charliewilco/obsidian"></github-repo> <github-repo name="charliewilco/level.css"></github-repo> <github-repo name="charliewilco/react-branches"></github-repo> <github-repo name="charliewilco/react-gluejar"></github-repo> <github-repo name="charliewilco/dotfiles"></github-repo>

See the Pen Custom Elements Demo – GitHub by Charles (@charliewilco) on CodePen.

To start, all we need to do to define and register a custom element is create a class that extends the HTMLElement class and then register the name of the element with customElements.define().

class OurCustomElement extends HTMLElement {} window.customElements.define('our-element', OurCustomElement);

And we can call it:

<our-element></our-element>

This new element isn’t very useful, but with custom elements, we get three methods to expand the functionality of this element. These are almost analogous to React’s lifecycle methods for their Component API. The two lifecycle-like methods most relevant to us are the disconnectedCallBack and the connectedCallback and since this is a class, it comes with a constructor.

Name Called when
constructor An instance of the element is created or upgraded. Useful for initializing state, settings up event listeners, or creating Shadow DOM. See the spec for restrictions on what you can do in the constructor.
connectedCallback The element is inserted into the DOM. Useful for running setup code, such as fetching resources or rendering UI. Generally, you should try to delay work until this time
disconnectedCallback When the element is removed from the DOM. Useful for running clean-up code.

To implement our custom element, we’ll create the class and set up some attributes related to that UI:

class Repository extends HTMLElement {   constructor() {     super();      this.repoDetails = null;      this.name = this.getAttribute("name");     this.endpoint = `https://api.github.com/repos/$ {this.name}`         this.innerHTML = `<h1>Loading</h1>`   } }

By calling super() in our constructor, the context of this is the element itself and all the DOM manipulation APIs can be used. So far, we’ve set the default repository details to null, gotten the repo name from element’s attribute, created an endpoint to call so we don’t have to define it later and, most importantly, set the initial HTML to be a loading indicator.

In order to get the details about that element’s repository, we’re going to need to make a request to GitHub’s API. We’ll use fetch and, since that’s Promise-based, we’ll use async and await to make our code more readable. You can learn more about the async/await keywords here and more about the browser’s fetch API here. You can also tweet at me to find out whether I prefer it to the Axios library. (Hint, it depends if I had tea or coffee with my breakfast.)

Now, let’s add a method to this class to ask GitHub for details about the repository.

class Repository extends HTMLElement {   constructor() {     // ...   }    async getDetails() {     return await fetch(this.endpoint, { mode: "cors" }).then(res => res.json());   } }

Next, let’s use the connectedCallback method and the Shadow DOM to use the return value from this method. Using this method will do something similar as when we called Repository.componentDidMount() in the React example. Instead, we’ll override the null value we initially gave this.repoDetails — we’ll use this later when we start to call the template to create the HTML.

class Repository extends HTMLElement {   constructor() {     // ...   }    async getDetails() {     // ...   }    async connectedCallback() {     let repo = await this.getDetails();     this.repoDetails = repo;     this.initShadowDOM();   }    initShadowDOM() {     let shadowRoot = this.attachShadow({ mode: "open" });     shadowRoot.innerHTML = this.template;   } }

You’ll notice that we’re calling methods related to the Shadow DOM. Besides being a rejected title for a Marvel movie, the Shadow DOM has its own rich API worth looking into. For our purposes, though, it’s going to abstract the implementation of adding innerHTML to the element.

Now we’re assigning the innerHTML to be equal to the value of this.template. Let’s define that now:

class Repository extends HTMLElement {   get template() {     const repo = this.repoDetails;        // if we get an error message let's show that back to the user     if (repo.message) {       return `<div class="Card Card--error">Error: $ {repo.message}</div>`     } else {       return `       <div class="Card">         <aside>           <img width="48" height="48" class="Avatar" src="$ {repo.owner.avatar_url}" alt="Profile picture for $ {repo.owner.login}" />         </aside>         <header>           <h2 class="Card__title">$ {repo.full_name}</h2>           <span class="Card__meta">$ {repo.description}</span>         </header>       </div>       `     }   } }

That’s pretty much it. We’ve defined a custom element that manages its own state, fetches its own data, and reflects that state back to the user while giving us an HTML element to use in our application.

After going through this exercise, I found that the only required dependency for custom elements is the browser’s native APIs rather than a framework to additionally parse and execute. This makes for a more portable and reusable solution with similar APIs to the frameworks you already love and use to make your living.

There are drawbacks of using this approach, of course. We’re talking about various browser support issues and some lack of consistency. Plus, working with DOM manipulation APIs can be very confusing. Sometimes they are assignments. Sometimes they are functions. Sometimes those functions take a callback and sometimes they don’t. If you don’t believe me, take a look at adding a class to an HTML element created via document.createElement(), which is one of the top five reasons to use React. The basic implementation isn’t that complicated but it is inconsistent with other similar document methods.

The real question is: does it even out in the wash? Maybe. React is still pretty good at the things it’s designed to be very very good at: the virtual DOM, managing application state, encapsulation, and passing data down the tree. There’s next to no incentive to use custom elements inside that framework. Custom elements, on the other hand, are simply available by virtue of building an application for the browser.

Learn more

The post A Guide to Custom Elements for React Developers appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]