Tag: Custom

Having a Little Fun With Custom Focus Styles

Every front-end developer has dealt or will deal with this scenario: your boss, client or designer thinks the outline applied by browsers on focused elements does not match the UI, and asks you to remove it. Or you might even be looking to remove it yourself.

So you do a little research and find out that this is strongly discouraged, because the focus outline is there for a reason: it provides visual feedback for keyboard navigation (using the Tab key), letting users who can’t use a mouse or have a visual impairment know where they are on the screen.

This button shows a focus state with Chrome’s default outline style.

That doesn’t mean you’re stuck with this outline, though. Instead of removing it, you can simply replace it with something else. That way, you’ll keep your interface accessible and get more flexibility on how it looks, so you can better match your UI.

You can start by removing the default browser outline by selecting the focused state of the element and applying outline: none. Then, you may choose from each of the options ahead to replace it:

Change the background color

This works best for elements that can be filled, such as buttons. Select the focused state of the element and apply a contrasting background color to it. The higher the contrast the better because subtle changes may not be strong enough visual cues, particularly in cases where with color blindness and low-vision.

In the example below, both background and border color change; you may pick either or both.

Click or focus with the Tab key to view how this state looks.

See the Pen
Elements replacing native outline focus with background color
by Lari (@larimaza)
on CodePen.

Change the text color

If the element has any text, you can select the focused state and change its color. This also works for icons applied with mask-image; you can select the icon as a descendant of the focused element and change its background color, like the example button below.

See the Pen
Elements replacing native outline focus with text and icon color
by Lari (@larimaza)
on CodePen.

Again, contrast is key. You may also consider using an underline on text links and making it part of the changed state because, as the Web Content Accessibility Guidelines state:

Color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element. (Level A)
Understanding Success Criterion 1.4.1

Apply a box shadow

The box-shadow property can do exactly the same job as the outline, except it’s much more powerful — you can now control its color, opacity, offset, blur radius and spread radius. And if a border-radius is specified, the box shadow follows the same rounded corners.

See the Pen
Elements replacing native outline focus with box shadow
by Lari (@larimaza)
on CodePen.

You can get really creative with this technique (seriously though, don’t do this):

See the Pen
Elements replacing native outline focus with insane box shadow
by Lari (@larimaza)
on CodePen.

This works for virtually any type of focusable element, like toggles, checkboxes, radio buttons and slides.

See the Pen
Toggle and radio button replacing native outline focus with box shadow
by Lari (@larimaza)
on CodePen.

Increase the element’s size

As an alternative to color change, you may also resort to subtle size modification as focus feedback. In this example, we’re using transform: scale.

See the Pen
Elements replacing native outline focus with transform scale
by Lari (@larimaza)
on CodePen.

The key here is subtlety. Extreme size changes may cause content reflow, not to mention a poor experience for those who prefer reduced motion.

Replicate existing hover styles

If the element already has a contrasting hover style, you can simply take that style and apply it for the focused state as well. This is a rather elegant solution, as you don’t have to add any new colors or outlines to the interface.

Here’s an example where both the focus and hover states adopt a high contrast to the background of an element’s default style:

See the Pen
Elements replacing native outline focus with hover styles
by Lari (@larimaza)
on CodePen.

Bonus: Customize the default outline

Everything we’ve looked at so far takes the assumption that we want to remove the focus outline altogether. We don’t have to! In fact, it’s a border that we can customize.

button:focus {   outline: 3px dashed orange; }

That’s shorthand and could have been written this way if we want to fine-tune the styles:

button:focus {   outline-width: 3px;   outline-style: dashed;   outline-color: orange; }

One additional superpower we have is the outline-offset property, which is separate from the outline shorthand property but can be used alongside it to change the position of the focus ring:

button:focus {   outline: 3px dashed orange;   outline-offset: 10px; }

Conclusion

You can mix and match all of these options to get custom styles that look appropriate for each component type within your interface.

And it’s worth repeating: Don’t forget to use stark color contrasts and other visual cues in addition to color when adopting custom focus states. Sure, we all want an experience that aligns with our designs, but we can adhere to good accessibility practices in the process. The W3C recommends this tool to test the contrast of colors values against the WCAG guidelines.

The post Having a Little Fun With Custom Focus Styles appeared first on CSS-Tricks.

CSS-Tricks

, , , ,

The Power (and Fun) of Scope with CSS Custom Properties

You’re probably already at least a little familiar with CSS variables. If not, here’s a two-second overview: they are really called custom properties, you set them in declaration blocks like --size: 1em and use them as values like font-size: var(--size);, they differ from preprocessor variables (e.g. they cascade), and here’s a guide with way more information.

But are we using them to their full potential? Do we fall into old habits and overlook opportunities where variables could significantly reduce the amount of code we write?

This article was prompted by a recent tweet I made about using CSS variables to create dynamic animation behavior.

Let’s look at a couple of instances where CSS variables can be used to do some pretty cool things that we may not have considered.

Basic scoping wins

The simplest and likely most common example would be scoped colors. And what’s our favorite component to use with color? The button. 😅

Consider the standard setup of primary and secondary buttons. Let’s start with some basic markup that uses a BEM syntax.

<button class="button button--primary">Primary</button> <button class="button button--secondary">Secondary</button>

Traditionally, we might do something like this to style them up:

.button {   padding: 1rem 1.25rem;   color: #fff;   font-weight: bold;   font-size: 1.25rem;   margin: 4px;   transition: background 0.1s ease; }  .button--primary {   background: hsl(233, 100%, 50%);   outline-color: hsl(233, 100%, 80%); }  .button--primary:hover {   background: hsl(233, 100%, 40%); }  .button--primary:active {   background: hsl(233, 100%, 30%); }  .button--secondary {   background: hsl(200, 100%, 50%);   outline-color: hsl(200, 100%, 80%); }  .button--secondary:hover {   background: hsl(200, 100%, 40%); }  .button--secondary:active {   background: hsl(200, 100%, 30%); }

See the Pen
Basic buttons
by Jhey (@jh3y)
on CodePen.

That’s an awful lot of code for something not particularly complex. We haven’t added many styles and we’ve added a lot of rules to cater to the button’s different states and colors. We could significantly reduce the code with a scoped variable.

In our example, the only differing value between the two button variants is the hue. Let’s refactor that code a little then. We won’t change the markup but cleaning up the styles a little, we get this:

.button {   padding: 1rem 1.25rem;   color: #fff;   font-weight: bold;   font-size: 1.25rem;   margin: 1rem;   transition: background 0.1s ease;   background: hsl(var(--hue), 100%, 50%);   outline-color: hsl(var(--hue), 100%, 80%);  } .button:hover {   background: hsl(var(--hue), 100%, 40%); }  .button:active {   background: hsl(var(--hue), 100%, 30%); }  .button--primary {   --hue: 233; }  .button--secondary {   --hue: 200; }

See the Pen
Refactoring styles with a scoped variable
by Jhey (@jh3y)
on CodePen.

This not only reduces the code but makes maintenance so much easier. Change the core button styles in one place and it will update all the variants! 🙌

I’d likely leave it there to make it easier for devs wanting to use those buttons. But, we could take it further. We could inline the variable on the actual element and remove the class declarations completely. 😲

<button class="button" style="--hue: 233;">Primary</button> <button class="button" style="--hue: 200;">Secondary</button>

Now we don’t need these. 👍

.button--primary {   --hue: 233; }  .button--secondary {   --hue: 200; }

See the Pen
Scoping w/ inline CSS variables
by Jhey (@jh3y)
on CodePen.

Inlining those variables might not be best for your next design system or app but it does open up opportunities. Like, for example, if we had a button instance where we needed to override the color.

button.button.button--primary(style=`--hue: 20;`) Overridden

See the Pen
Overridden with inline scope
by Jhey (@jh3y)
on CodePen.

Having fun with inline variables

Another opportunity is to have a little fun with it. This is a technique I use for many of the Pens I create over on CodePen. 😉

You may be writing straightforward HTML, but in many cases, you may be using a framework, like React or a preprocessor like Pug, to write your markup. These solutions allow you to leverage JavaScript to create random inline variables. For the following examples, I’ll be using Pug. Pug is an indentation-based HTML templating engine. If you aren’t familiar with Pug, do not fear! I’ll try to keep the markup simple.

Let’s start by randomizing the hue for our buttons:

button.button(style=`--hue: $ {Math.random() * 360}`) First

With Pug, we can use ES6 template literals to inline randomized CSS variables. 💪

See the Pen
Random inline CSS variable hues
by Jhey (@jh3y)
on CodePen.

Animation alterations

So, now that we have the opportunity to define random characteristics for an element, what else could we do? Well, one overlooked opportunity is animation. True, we can’t animate the variable itself, like this:

@keyframes grow {   from { --scale: 1; }   to   { --scale: 2; } }

But we can create dynamic animations based on scoped variables. We can change the behavior of animation on the fly! 🤩

Example 1: The excited button

Let’s create a button that floats along minding its own business and then gets excited when we hover over it.

Start with the markup:

button.button(style=`--hue: $ {Math.random() * 360}`) Show me attention

A simple floating animation may look like this:

@keyframes flow {   0%, 100% {     transform: translate(0, 0);   }   50% {     transform: translate(0, -25%);   } }

This will give us something like this:

See the Pen
The excited button foundation
by Jhey (@jh3y)
on CodePen.

I’ve added a little shadow as an extra but it’s not vital. 👍

Let’s make it so that our button gets excited when we hover over it. Now, we could simply change the animation being used to something like this:

.button:hover {   animation: shake .1s infinite ease-in-out; }  @keyframes shake {   0%, 100% {     transform: translate(0, 0) rotate(0deg);   }   25% {     transform: translate(-1%, 3%) rotate(-2deg);   }   50% {     transform: translate(1%, 2%) rotate(2deg);   }   75% {     transform: translate(1%, -2%) rotate(-1deg);   } }

And it works:

See the Pen
The excited button gets another keyframes definition
by Jhey (@jh3y)
on CodePen.

But, we need to introduce another keyframes definition. What if we could merge the two animations into one? They aren’t too far off from each other in terms of structure.

We could try:

@keyframes flow-and-shake {   0%, 100% {     transform: translate(0, 0) rotate(0deg);   }   25%, 75% {     transform: translate(0, -12.5%) rotate(0deg);   }   50% {     transform: translate(0, -25%) rotate(0deg);   } }

Although this works, we end up with an animation that isn’t quite as smooth because of the translation steps. So what else could we do? Let’s find a compromise by removing the steps at 25% and 75%.

@keyframes flow-and-shake {   0%, 100% {     transform: translate(0, 0) rotate(0deg);   }   50% {     transform: translate(0, -25%) rotate(0deg);   } }

It works fine, as we expected, but here comes the trick: Let’s update our button with some variables.

.button {   --y: -25;   --x: 0;   --rotation: 0;   --speed: 2; }

Now let’s plug them into the animation definition, along with the button’s animation properties.

.button {   animation-name: flow-and-shake;   animation-duration: calc(var(--speed) * 1s);   animation-iteration-count: infinite;   animation-timing-function: ease-in-out; }  @keyframes flow-and-shake {   0%, 100% {     transform: translate(calc(var(--x) * -1%), calc(var(--y) * -1%))       rotate(calc(var(--rotation) * -1deg));   }   50% {     transform: translate(calc(var(--x) * 1%), calc(var(--y) * 1%))       rotate(calc(var(--rotation) * 1deg));   } }

All is well. 👍

Let’s change those values when the button is hovered:

.button:hover {   --speed: .1;   --x: 1;   --y: -1;   --rotation: -1; }

See the Pen
The excited button with refactored keyframes & scoped variables
by Jhey (@jh3y)
on CodePen.

Nice! Now our button has two different types of animations but defined via one set of keyframes. 🤯

Let’s have a little more fun with it. If we take it a little further, we can make the button a little more playful and maybe stop animating altogether when it’s active. 😅

See the Pen
The Excited Button w/ dynamic animation 🤓
by Jhey (@jh3y)
on CodePen.

Example 2: Bubbles

Now that we’ve gone through some different techniques for things we can do with the power of scope, let’s put it all together. We are going to create a randomly generated bubble scene that heavily leverages scoped CSS variables.

Let’s start by creating a bubble. A static bubble.

.bubble {   background: radial-gradient(100% 115% at 25% 25%, #fff, transparent 33%),     radial-gradient(15% 15% at 75% 75%, #80dfff, transparent),     radial-gradient(100% 100% at 50% 25%, transparent, #66d9ff 98%);   border: 1px solid #b3ecff;   border-radius: 100%;   height: 50px;   width: 50px; }

We are using background with multiple values and a border to make the bubble effect — but it’s not very dynamic. We know the border-radius will always be the same. And we know the structure of the border and background will not change. But the values used within those properties and the other property values could all be random.

Let’s refactor the CSS to make use of variables:

.bubble {   --size: 50;   --hue: 195;   --bubble-outline: hsl(var(--hue), 100%, 50%);   --bubble-spot: hsl(var(--hue), 100%, 75%);   --bubble-shade: hsl(var(--hue), 100%, 70%);   background: radial-gradient(100% 115% at 25% 25%, #fff, transparent 33%),     radial-gradient(15% 15% at 75% 75%, var(--bubble-spot), transparent),     radial-gradient(100% 100% at 50% 25%, transparent, var(--bubble-shade) 98%);   border: 1px solid var(--bubble-outline);   border-radius: 100%;   height: calc(var(--size) * 1px);   width: calc(var(--size) * 1px); }

That’s a good start. 👍

See the Pen
Bubbles foundation
by Jhey (@jh3y)
on CodePen.

Let’s add some more bubbles and leverage the inline scope to position them as well as size them. Since we are going to start randomizing more than one value, it’s handy to have a function to generate a random number in range for our markup.

- const randomInRange = (max, min) => Math.floor(Math.random() * (max - min + 1)) + min

With Pug, we can utilize iteration to create a large set of bubbles:

- const baseHue = randomInRange(0, 360) - const bubbleCount = 50 - let b = 0 while b < bubbleCount   - const size = randomInRange(10, 50)   - const x = randomInRange(0, 100)   .bubble(style=`--x: $ {x}; --size: $ {size}; --hue: $ {baseHue}`)   - b++

Updating our .bubble styling allows us to make use of the new inline variables.

.bubble {   left: calc(var(--x) * 1%);   position: absolute;   transform: translate(-50%, 0); }

Giving us a random set of bubbles:

See the Pen
Adding bubbles
by Jhey (@jh3y)
on CodePen.

Let’s take it even further and animate those bubbles so they float from top to bottom and fade out.

.bubble {   animation: float 5s infinite ease-in-out;   top: 100%; }  @keyframes float {   from {     opacity: 1;     transform: translate(0, 0) scale(0);   }   to {     opacity: 0;     transform: translate(0, -100vh) scale(1);   } }

See the Pen
Bubbles rising together
by Jhey (@jh3y)
on CodePen.

That’s pretty boring. They all do the same thing at the same time. So let’s randomize the speed, delay, end scale and distance each bubble is going to travel.

- const randomInRange = (max, min) => Math.floor(Math.random() * (max - min + 1)) + min - const baseHue = randomInRange(0, 360) - const bubbleCount = 50 - let b = 0 while b < bubbleCount   - const size = randomInRange(10, 50)   - const delay = randomInRange(1, 10)   - const speed = randomInRange(2, 20)   - const distance = randomInRange(25, 150)   - const scale = randomInRange(100, 150) / 100   - const x = randomInRange(0, 100)   .bubble(style=`--x: $ {x}; --size: $ {size}; --hue: $ {baseHue}; --distance: $ {distance}; --speed: $ {speed}; --delay: $ {delay}; --scale: $ {scale}`)   - b++

And now, let’s update our styles

.bubble {   animation-name: float;   animation-duration: calc(var(--speed) * 1s);   animation-delay: calc(var(--delay) * -1s);   animation-iteration-count: infinite;   animation-timing-function: ease-in-out; }  @keyframes float {   from {     opacity: 1;     transform: translate(-50%, 0) scale(0);   }   to {     opacity: 0;     transform: translate(-50%, calc(var(--distance) * -1vh)) scale(var(--scale));   } }

And we will get this:

See the Pen
Random bubble scene using variable scope 😎
by Jhey (@jh3y)
on CodePen.

With around 50 lines of code, you can create a randomly generated animated scene by honing the power of the scope! 💪

That’s it!

We can create some pretty cool things with very little code by putting CSS variables to use and leveraging some little tricks.

I do hope this article has raised some awareness for the power of CSS variable scope and I do hope you will hone the power and pass it on 😎

All the demos in this article are available in this CodePen collection.

The post The Power (and Fun) of Scope with CSS Custom Properties appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Patterns for Practical CSS Custom Properties Use

I’ve been playing around with CSS Custom Properties to discover their power since browser support is finally at a place where we can use them in our production code. I’ve been using them in a number different ways and I’d love for you to get as excited about them as I am. They are so useful and powerful!

I find that CSS variables usage tends to fall into categories. Of course, you’re free to use CSS variables however you like, but thinking of them in these different categories might help you understand the different ways in which they can be used.

  • Variables. The basics, such as setting, a brand color to use wherever needed.
  • Default Values. For example, a default border-radius that can be overridden later.
  • Cascading Values. Using clues based on specificity, such as user preferences.
  • Scoped Rulesets. Intentional variations on individual elements, like links and buttons.
  • Mixins. Rulesets intended to bring their values to a new context.
  • Inline Properties. Values passed in from inline styles in our HTML.

The examples we’ll look at are simplified and condensed patterns from a CSS framework I created and maintain called Cutestrap.

A quick note on browser support

There are two common lines of questions I hear when Custom Properties come up. The first is about browser support. What browsers support them? Are there fallbacks we need to use where they aren’t supported?

The global market share that support the things we’re covering in this post is 85%. Still, it’s worth cross-referencing caniuse) with your user base to determine how much and where progressive enhancement makes sense for your project.

The second question is always about how to use Custom Properties. So let’s dive into usage!

Pattern 1: Variables

The first thing we’ll tackle is setting a variable for a brand color as a Custom Property and using it on an SVG element. We’ll also use a fallback to cover users on trailing browsers.

html {   --brand-color: hsl(230, 80%, 60%); }  .logo {   fill: pink; /* fallback */   fill: var(--brand-color); }

Here, we’ve declared a variable called --brand-color in our html ruleset. The variable is defined on an element that’s always present, so it will cascade to every element where it’s used. Long story short, we can use that variable in our .logo ruleset.

We declared a pink fallback value for trailing browsers. In the second fill declaration, we pass --brand-color into the var() function, which will return the value we set for that Custom Property.

That’s pretty much how the pattern goes: define the variable (--variable-name) and then use it on an element (var(--variable-name)).

See the Pen
Patterns for Practical Custom Properties: Example 1.0
by Tyler Childs (@tylerchilds)
on CodePen.

Pattern 2: Default values

The var() function we used in the first example can also provide default values in case the Custom Property it is trying to access is not set.

For example, say we give buttons a rounded border. We can create a variable — we’ll call it --roundness — but we won’t define it like we did before. Instead, we’ll assign a default value when putting the variable to use.

.button {   /* --roundness: 2px; */   border-radius: var(--roundness, 10px); }

A use case for default values without defining the Custom Property is when your project is still in design but your feature is due today. This make it a lot easier to update the value later if the design changes.

So, you give your button a nice default, meet your deadline and when --roundness is finally set as a global Custom Property, your button will get that update for free without needing to come back to it.

See the Pen
Patterns for Practical Custom Properties: Example 2.0
by Tyler Childs (@tylerchilds)
on CodePen.

You can edit on CodePen and uncomment the code above to see what the button will look like when --roundness is set!

Pattern 3: Cascading values

Now that we’ve got the basics under our belt, let’s start building the future we owe ourselves. I really miss the personality that AIM and MySpace had by letting users express themselves with custom text and background colors on profiles.

Let’s bring that back and build a school message board where each student can set their own font, background color and text color for the messages they post.

User-based themes

What we’re basically doing is letting students create a custom theme. We’ll set the theme configurations inside data-attribute rulesets so that any descendants — a .message element in this case — that consume the themes will have access to those Custom Properties.

.message {   background-color: var(--student-background, #fff);   color: var(--student-color, #000);   font-family: var(--student-font, "Times New Roman", serif);   margin-bottom: 10px;   padding: 10px; }  [data-student-theme="rachel"] {   --student-background: rgb(43, 25, 61);   --student-color: rgb(252, 249, 249);   --student-font: Arial, sans-serif; }  [data-student-theme="jen"] {   --student-background: #d55349;   --student-color: #000;   --student-font: Avenir, Helvetica, sans-serif; }  [data-student-theme="tyler"] {   --student-background: blue;   --student-color: yellow;   --student-font: "Comic Sans MS", "Comic Sans", cursive; }

Here’s the markup:

<section>   <div data-student-theme="chris">     <p class="message">Chris: I've spoken at events and given workshops all over the world at conferences.</p>   </div>   <div data-student-theme="rachel">     <p class="message">Rachel: I prefer email over other forms of communication.</p>   </div>   <div data-student-theme="jen">     <p class="message">Jen: This is why I immediately set up my new team with Slack for real-time chat.</p>   </div>   <div data-student-theme="tyler">     <p class="message">Tyler: I miss AIM and MySpace, but this message board is okay.</p>   </div> </section>

We have set all of our student themes using [data-student-theme] selectors for our student theme rulesets. The Custom Properties for background, color, and font will apply to our .message ruleset if they are set for that student because .message is a descendant of the div containing the data-attribute that, in turn, contains the Custom Property values to consume. Otherwise, the default values we provided will be used instead.

See the Pen
Patterns for Practical Custom Properties: Example 3.0
by Tyler Childs (@tylerchilds)
on CodePen.

Readable theme override

As fun and cool as it is for users to control custom styles, what users pick won’t always be accessible with considerations for contrast, color vision deficiency, or anyone that prefers their eyes to not bleed when reading. Remember the GeoCities days?

Let’s add a class that provides a cleaner look and feel and set it on the parent element (<section>) so it overrides any student theme when it’s present.

.readable-theme [data-student-theme] {   --student-background: hsl(50, 50%, 90%);   --student-color: hsl(200, 50%, 10%);   --student-font: Verdana, Geneva, sans-serif; }
<section class="readable-theme">   ... </section>

We’re utilizing the cascade to override the student themes by setting a higher specificity such that the background, color, and font will be in scope and will apply to every .message ruleset.

See the Pen
Patterns for Practical Custom Properties: Example 3.1
by Tyler Childs (@tylerchilds)
on CodePen.

Pattern 4: Scoped rulesets

Speaking of scope, we can scope Custom Properties and use them to streamline what is otherwise boilerplate CSS. For example, we can define variables for different link states.

a {   --link: hsl(230, 60%, 50%);   --link-visited: hsl(290, 60%, 50%);   --link-hover: hsl(230, 80%, 60%);   --link-active: hsl(350, 60%, 50%); }  a:link {   color: var(--link); }  a:visited {   color: var(--link-visited); }  a:hover {   color: var(--link-hover); }  a:active {   color: var(--link-active); }
<a href="#">Link Example</a>

Now that we’ve written out the Custom Properties globally on the <a> element and used them on our link states, we don’t need to write them again. These are scoped to our <a> element’s ruleset so they are only set on anchor tags and their children. This allows us to not pollute the global namespace.

Example: Grayscale link

Going forward, we can control the links we just created by changing the Custom Properties for our different use cases. For example, let’s create a gray-colored link.

.grayscale {   --link: LightSlateGrey;   --link-visited: Silver;   --link-hover: DimGray;   --link-active: LightSteelBlue; }
<a href="#" class="grayscale">Link Example</a>

We’ve declared a .grayscale ruleset that contains the colors for our different link states. Since the selector for this ruleset has a greater specificity then the default, these variable values are used and then applied to the pseudo-class rulesets for our link states instead of what was defined on the <a> element.

See the Pen
Patterns for Practical Custom Properties: Example 4.0
by Tyler Childs (@tylerchilds)
on CodePen.

Example: Custom links

If setting four Custom Properties feels like too much work, what if we set a single hue instead? That could make things a lot easier to manage.

.custom-link {   --hue: 30;   --link: hsl(var(--hue), 60%, 50%);   --link-visited: hsl(calc(var(--hue) + 60), 60%, 50%);   --link-hover: hsl(var(--hue), 80%, 60%);   --link-active: hsl(calc(var(--hue) + 120), 60%, 50%); }  .danger {   --hue: 350; }
<a href="#" class="custom-link">Link Example</a> <a href="#" class="custom-link danger">Link Example</a>

See the Pen
Patterns for Practical Custom Properties: Example 4.1
by Tyler Childs (@tylerchilds)
on CodePen.

By introducing a variable for a hue value and applying it to our HSL color values in the other variables, we merely have to change that one value to update all four link states.

Calculations are powerful in combination with Custom Properties since they let
your styles be more expressive with less effort. Check out this technique by Josh Bader where he uses a similar approach to enforce accessible color contrasts on buttons.

Pattern 5: Mixins

A mixin, in regards to Custom Properties, is a function declared as a Custom Property value. The arguments for the mixin are other Custom Properties that will recalculate the mixin when they’re changed which, in turn, will update styles.

The custom link example we just looked at is actually a mixin. We can set the value for --hue and then each of the four link states will recalculate accordingly.

Example: Baseline grid foundation

Let’s learn more about mixins by creating a baseline grid to help with vertical rhythm. This way, our content has a pleasant cadence by utilizing consistent spacing.

.baseline, .baseline * {   --rhythm: 2rem;   --line-height: var(--sub-rhythm, var(--rhythm));   --line-height-ratio: 1.4;   --font-size: calc(var(--line-height) / var(--line-height-ratio)); }  .baseline {   font-size: var(--font-size);   line-height: var(--line-height); }

We’ve applied the ruleset for our baseline grid to a .baseline class and any of its descendants.

  • --rhythm: This is the foundation of our baseline. Updating it will impact all the other properties.
  • --line-height: This is set to --rhythm by default, since --sub-rhythm is not set here.
  • --sub-rhythm: This allows us to override the --line-height — and subsequently, the --font-size — while maintaining the overall baseline grid.
  • --line-height-ratio: This helps enforce a nice amount of spacing between lines of text.
  • --font-size: This is calculated by dividing our --line-height by our --line-height-ratio.

We also set our font-size and line-height in our .baseline ruleset to use the --font-size and --line-height from our baseline grid. In short, whenever the rhythm changes, the line height and font size change accordingly while maintaining a legible experience.

OK, let’s put the baseline to use.

Let’s create a tiny webpage. We’ll use our --rhythm Custom Property for all of the spacing between elements.

.baseline h2, .baseline p, .baseline ul {   padding: 0 var(--rhythm);   margin: 0 0 var(--rhythm); }  .baseline p {   --line-height-ratio: 1.2; }  .baseline h2 {   --sub-rhythm: calc(3 * var(--rhythm));   --line-height-ratio: 1; }  .baseline p, .baseline h2 {   font-size: var(--font-size);   line-height: var(--line-height); }  .baseline ul {   margin-left: var(--rhythm); }
<section class="baseline">   <h2>A Tiny Webpage</h2>   <p>This is the tiniest webpage. It has three noteworthy features:</p>   <ul>     <li>Tiny</li>     <li>Exemplary</li>     <li>Identifies as Hufflepuff</li>   </ul> </section>

We’re essentially using two mixins here: --line-height and --font-size. We need to set the properties font-size and line-height to their Custom Property counterparts in order to set the heading and paragraph. The mixins have been recalculated in those rulesets, but they need to be set before the updated styling will be applied to them.

See the Pen
Patterns for Practical Custom Properties: Example 5.0
by Tyler Childs (@tylerchilds)
on CodePen.

Something to keep in mind: You probably do not want to use the Custom Property values in the ruleset itself when applying mixins using a wildcard selector. It gives those styles a higher specificity than any other inheritance that comes along with the cascade, making them hard to override without using !important.

Pattern 6: Inline properties

We can also declare Custom Properties inline. Let’s build a lightweight grid system demonstrate.

.grid {   --columns: auto-fit;    display: grid;   gap: 10px;   grid-template-columns: repeat(var(--columns), minmax(0, 1fr)); }
<div class="grid">   <img src="https://www.fillmurray.com/900/600" alt="Bill Murray" />   <img src="https://www.placecage.com/900/600" alt="Nic Cage" />   <img src="https://www.placecage.com/g/900/600" alt="Nic Cage gray" />   <img src="https://www.fillmurray.com/g/900/600" alt="Bill Murray gray" />   <img src="https://www.placecage.com/c/900/600" alt="Nic Cage crazy" />   <img src="https://www.placecage.com/gif/900/600" alt="Nic Cage gif" /> </div>

By default, the grid has equally sized columns that will automatically lay themselves into a single row.

See the Pen
Patterns for Practical Custom Properties: Example 6.0
by Tyler Childs (@tylerchilds)
on CodePen.

To control the number of columns we can set our --columns Custom Property
inline on our grid element.

<div class="grid" style="--columns: 3;">   ... </div>

See the Pen
Patterns for Practical Custom Properties: Example 6.1
by Tyler Childs (@tylerchilds)
on CodePen.


We just looked at six different use cases for Custom Properties — at least ones that I commonly use. Even if you were already aware of and have been using Custom Properties, hopefully seeing them used these ways gives you a better idea of when and where to use them effectively.

Are there different types of patterns you use with Custom Properties? Share them in the comments and link up some demos — I’d love to see them!

If you’re new to Custom Properties are are looking to level up, try playing around with the examples we covered here, but add media queries to the mix. You’ll see how adaptive these can be and how many interesting opportunities open up when you have the power to change values on the fly.

Plus, there are a ton of other great resources right here on CSS-Tricks to up your Custom Properties game in the Custom Properties Guide.

See the Pen
Thank you for Reading!
by Tyler Childs (@tylerchilds)
on CodePen.

The post Patterns for Practical CSS Custom Properties Use appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Weekly news: Truncating muti-line text, calc() in custom property values, Contextual Alternates

In this week’s roundup, WebKit’s method for truncating multi-line text gets some love, a note on calculations using custom properties, and a new OpenType feature that prevents typographic logjams.

Truncating mutli-line text

The CSS -webkit-line-clamp property for truncating multi-line text is now widely supported (see my usage guide). If you use Autoprefixer, update it to the latest version (9.6.1). Previous versions would remove -webkit-box-orient: vertical, which caused this CSS feature to stop working.

Note that Autoprefixer doesn’t generate any prefixes for you in this case. You need to use the following four declarations exactly (all are required):

.line-clamp {   overflow: hidden;   display: -webkit-box;   -webkit-box-orient: vertical;   -webkit-line-clamp: 3; /* or any other integer */ }

(via Autoprefixer)

Calculations in CSS custom property values

In CSS, it is currently not possible to pre-calculate custom property values (spec). The computed value of a custom property is its specified value (with variables substituted); therefore, relative values in calc() expressions are not “absolutized” (e.g., em values are not computed to pixel values).

:root {   --large: calc(1em + 10px); }  blockquote {   font-size: var(--large); }

It may appear that the calculation in the above example is performed on the root element, specifically that the relative value 1em is computed and added to the absolute value 10px. Under default conditions (where 1em equals 16px on the root element), the computed value of --large would be 26px.

But that’s not what’s happening here. The computed value of --large is its specified value, calc(1em + 10px). This value is inherited and substituted into the value of the font-size property on the <blockquote> element.

blockquote {   /* the declaration after variable substitution */   font-size: calc(1em + 10px); }

Finally, the calculation is performed and the relative 1em value absolute-ized in the scope of the <blockquote> element — not the root element where the calc() expression is declared.

(via Tab Atkins Jr.)

Contextual Alternates

The “Contextual Alternates” OpenType feature ensures that characters don’t overlap or collide when ligatures are turned off. You can check if your font supports this feature on wakamaifondue.com and enable it (if necessary) via the CSS font-variant-ligatures: contextual declaration.

(via Jason Pamental)

Announcing daily news on webplatform.news

I have started posting daily news for web developers on webplatform.news. Visit every day!

The post Weekly news: Truncating muti-line text, calc() in custom property values, Contextual Alternates appeared first on CSS-Tricks.

CSS-Tricks

, , , , , , , , , ,
[Top]

Managing Multiple Backgrounds with Custom Properties

One cool thing about CSS custom properties is that they can be a part of a value. Let’s say you’re using multiple backgrounds to pull off a a design. Each background will have its own color, image, repeat, position, etc. It can be verbose!

You have four images:

body {      background-position:     top 10px left 10px,     top 10px right 10px,     bottom 10px right 10px,     bottom 10px left 10px;      background-repeat: no-repeat;      background-image:     url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-top-left.svg),     url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-top-right.svg),     url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-bottom-right.svg),     url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-bottom-left.svg);    }

You want to add a fifth in a media query:

@media (min-width: 1500px) {   body {     /* REPEAT all existing backgrounds, then add a fifth. */   } }

That’s going to be super verbose! You’ll have to repeat each of those four images again, then add the fifth. Lots of duplication there.

One possibility is to create a variable for the base set, then add the fifth much more cleanly:

body {   --baseBackgrounds:      url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-top-left.svg),     url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-top-right.svg),     url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-bottom-right.svg),     url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-bottom-left.svg);    background-position:     top 10px left 10px,     top 10px right 10px,     bottom 10px right 10px,     bottom 10px left 10px;      background-repeat: no-repeat;      background-image: var(--baseBackgrounds); } @media (min-width: 1500px) {   body {     background-image:        var(--baseBackgrounds),       url(added-fifth-background.svg);   } }

But, it’s really up to you. It might make more sense and be easier manage if you made each background image into a variable, and then pieced them together as needed.

body {   --bg1: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-top-left.svg);   --bg2: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-top-right.svg);   --bg3: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-bottom-right.svg);   --bg4: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-bottom-left.svg);   --bg5: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-bottom-left.svg);      background-image: var(--bg1), var(--bg2), var(--bg3), var(--bg4); } @media (min-width: 1500px) {   body {     background-image: var(--bg1), var(--bg2), var(--bg3), var(--bg4), var(--bg5);   } }

Here’s a basic version of that, including a supports query:

See the Pen
Multiple BGs with Custom Properties
by Chris Coyier (@chriscoyier)
on CodePen.

Dynamically changing just the part of a value is a huge strength of CSS custom properties!

Note, too, that with backgrounds, it might be best to include the entire shorthand as the variable. That way, it’s much easier to piece everything together at once, rather than needing something like…

--bg_1_url: url(); --bg_1_size: 100px; --bg_1_repeat: no-repeat; /* etc. */

It’s easier to put all of the properties into shorthand and use as needed:

body {     --bg_1: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-top-left.svg) top 10px left 10px / 86px no-repeat;   --bg_2: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-top-right.svg) top 10px right 10px / 86px no-repeat;   --bg_3: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-bottom-right.svg) bottom 10px right 10px / 86px no-repeat;   --bg_4: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/angles-bottom-left.svg) bottom 10px left 10px  / 86px no-repeat;        background:     var(--bg_1), var(--bg_2),var(--bg_3),var(--bg_4); }

Like this.

The post Managing Multiple Backgrounds with Custom Properties appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Get a CSS Custom Property Value with JavaScript

Here’s a neat trick from Andy Bell where he uses CSS Custom Properties to check if a particular CSS feature is supported by using JavaScript.

Basically, he’s using the ability CSS has to check for browser support on a particular property, setting a custom property that returns a value of either 0 or 1 (Boolean!) and then informing the JavaScript to execute based on that value.

Here’s his example:

.my-component {   --supports-scroll-snap: 0; }  @supports (scroll-snap-type: x mandatory) {   .my-component {     --supports-scroll-snap: 1;   } }
const myComponent = document.querySelector('.my-component'); const isSnapSupported = getCSSCustomProp('--supports-scroll-snap', myComponent, 'boolean');

As Andy mentions, another common way to do this is to use pseudo elements on the body element and then access them with JavaScript, but this approach with @supports seems a whole lot cleaner and less hacky to me, personally. I wonder how many more intuitive things we’ll find we can do with CSS Custom Properties because this is an interesting case where CSS instructs JavaScript, instead of the other way around.

Direct Link to ArticlePermalink

The post Get a CSS Custom Property Value with JavaScript appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Breaking CSS Custom Properties out of :root Might Be a Good Idea

CSS Custom Properties have been a hot topic for a while now, with tons of great articles about them, from great primers on how they work to creative tutorials to do some real magic with them. If you’ve read more than one or two articles on the topic, then I’m sure you’ve noticed that they start by setting up the custom properties on the :root about 99% of the time.

While putting custom properties on the :root is great for things that you need to be available throughout your site, there are times when it makes more sense to scope your custom properties locally.

In this article, we’ll be exploring:

  • Why we put custom properties on the :root to begin with.
  • Why global scoping isn’t right for everything.
  • How to overcome class clashing with locally scoped custom properties

What’s the deal with custom properties and :root?

Before we jump into looking at the global scope, I think it’s worth looking at why everyone sets custom properties in the :root to begin with.

I’ve been declaring custom properties on the :root without even a second thought. Pretty much everyone does it without even a mention of why — including the official specification.

When the subject of :root is actually breached, it mentions how :root is the same as html, but with higher specificity, and that’s about it.

But does that higher specificity really matter?

Not really. All it does is select html with a higher specificity, the same way a class selector has higher specificity than an element selector when selecting a div.

:root {   --color: red; }  html {   --color: blue; }  .example {   background: var(--color);   /* Will be red because of :root's higher specificity */ }

The main reason that :root is suggested is because CSS isn’t only used to style HTML documents. It is also used for XML and SVG files.

In the case of XML and SVG files, :root isn’t selecting the html element, but rather their root (such as the svg tag in an SVG file).

Because of this, the best practice for a globally-scoped custom property is the :root. But if you’re making a website, you can throw it on an html selector and not notice a difference.

That said, with everyone using :root, it has quickly become a “standard.” It also helps separate variables to be used later on from selectors which are actively styling the document.

Why global scope isn’t right for everything

With CSS pre-processors, like Sass and Less, most of us keep variables tucked away in a partial dedicated to them. That works great, so why should we consider locally scoping variables all of a sudden?

One reason is that some people might find themselves doing something like this.

:root {   --clr-light: #ededed;   --clr-dark: #333;   --clr-accent: #EFF;   --ff-heading: 'Roboto', sans-serif;   --ff-body: 'Merriweather', serif;   --fw-heading: 700;   --fw-body: 300;   --fs-h1: 5rem;   --fs-h2: 3.25rem;   --fs-h3: 2.75rem;   --fs-h4: 1.75rem;   --fs-body: 1.125rem;   --line-height: 1.55;   --font-color: var(--clr-light);   --navbar-bg-color: var(--clr-dark);   --navbar-logo-color: var(--clr-accent);   --navbar-border: thin var(--clr-accent) solid;   --navbar-font-size: .8rem;   --header-color: var(--clr-accent);   --header-shadow: 2px 3px 4px rgba(200,200,0,.25);   --pullquote-border: 5px solid var(--clr-light);   --link-fg: var(--clr-dark);   --link-bg: var(--clr-light);   --link-fg-hover: var(--clr-dark);   --link-bg-hover: var(--clr-accent);   --transition: 250ms ease-out;   --shadow: 2px 5px 20px rgba(0, 0, 0, .2);   --gradient: linear-gradient(60deg, red, green, blue, yellow);   --button-small: .75rem;   --button-default: 1rem;   --button-large: 1.5rem; }

Sure, this gives us one place where we can manage styling with custom properties. But, why do we need to define my --header-color or --header-shadow in my :root? These aren’t global properties, I’m clearly using them in my header and no where else.

If it’s not a global property, why define it globally? That’s where local scoping comes into play.

Locally scoped properties in action

Let’s say we have a list to style, but our site is using an icon system — let’s say Font Awesome for simplicity’s sake. We don’t want to use the disc for our ul bullets — we want a custom icon!

If I want to switch out the bullets of an unordered list for Font Awesome icons, we can do something like this:

ul {   list-style: none; }  li::before {   content: "\f14a"; /* checkbox */   font-family: "Font Awesome Free 5";   font-weight: 900;   float: left;   margin-left: -1.5em; }

While that’s super easy to do, one of the problems is that the icon becomes abstract. Unless we use Font Awesome a lot, we aren’t going to know that f14a means, let alone be able to identify it as a checkbox icon. It’s semantically meaningless.

We can help clarify things with a custom property here.

ul {   --checkbox-icon: "\f14a";   list-style: none; }

This becomes a lot more practical once we start having a few different icons in play. Let’s up the complexity and say we have three different lists:

<ul class="icon-list checkbox-list"> ... </ul>  <ul class="icon-list star-list"> ... </ul>  <ul class="icon-list bolt-list"> ... </ul>

Then, in our CSS, we can create the custom properties for our different icons:

.icon-list {   --checkbox: "\f14a";   --star: "\f005";   --bolt: "\f0e7";    list-style: none; }

The real power of having locally scoped custom properties comes when we want to actually apply the icons.

We can set content: var(--icon) on our list items:

.icon-list li::before {   content: var(--icon);   font-family: "Font Awesome Free 5";   font-weight: 900;   float: left;   margin-left: -1.5em; }

Then we can define that icon for each one of our lists with more meaningful naming:

.checkbox-list {   --icon: var(--checkbox); }  .star-list {   --icon: var(--star); }  .bolt-list {   --icon: var(--bolt); }

We can step this up a notch by adding colors to the mix:

.icon-list li::before {   content: var(--icon);   color: var(--icon-color);   /* Other styles */ }

Moving icons to the global scope

If we’re working with an icon system, like Font Awesome, then I’m going to assume that we’d be using them for more than just replacing the bullets in unordered lists. As long as we’re using them in more than one place it makes sense to move the icons to the :root as we want them to be available globally.

Having icons in the :root doesn’t mean we can’t still take advantage of locally scoped custom properties, though!

:root {   --checkbox: "\f14a";   --star: "\f005";   --bolt: "\f0e7";      --clr-success: rgb(64, 209, 91);   --clr-error: rgb(219, 138, 52);   --clr-warning: rgb(206, 41, 26); }  .icon-list li::before {   content: var(--icon);   color: var(--icon-color);   /* Other styles */ }  .checkbox-list {   --icon: var(--checkbox);   --icon-color: var(--clr-success); }  .star-list {   --icon: var(--star);   --icon-color: var(--clr-warning); }  .bolt-list {   --icon: var(--bolt);   --icon-color: var(--clr-error); }

Adding fallbacks

We could either put in a default icon by setting it as the fallback (e.g. var(--icon, "/f1cb")), or, since we’re using the content property, we could even put in an error message var(--icon, "no icon set").

See the Pen
Custom list icons with CSS Custom Properties
by Kevin (@kevinpowell)
on CodePen.

By locally scoping the --icon and the --icon-color variables, we’ve greatly increased the readability of our code. If someone new were to come into the project, it will be a whole lot easier for them to know how it works.

This isn’t limited to Font Awesome, of course. Locally scoping custom properties also works great for an SVG icon system:

:root {   --checkbox: url(../assets/img/checkbox.svg);   --star: url(../assets/img/star.svg);   --baby: url(../assets/img/baby.svg); }  .icon-list {   list-style-image: var(--icon); }  .checkbox-list { --icon: checkbox; } .star-list { --icon: star; } .baby-list { --icon: baby; }

Using locally scoped properties for more modular code

While the example we just looked at works well to increase the readability of our code — which is awesome — we can do a lot more with locally scoped properties.

Some people love CSS as it is; others hate working with the global scope of the cascade. I’m not here to discuss CSS-in-JS (there are enough really smart people already talking about that), but locally scoped custom properties offer us a fantastic middle ground.

By taking advantage of locally scoped custom properties, we can create very modular code that takes a lot of the pain out of trying to come up with meaningful class names.

Let’s um, scope the scenario.

Part of the reason people get frustrated with CSS is that the following markup can cause problems when we want to style something.

<div class="card">   <h2 class="title">This is a card</h2>   <p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Libero, totam.</p>   <button class="button">More info</button> </div>  <div class="cta">   <h2 class="title">This is a call to action</h2>   <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Aliquid eveniet fugiat ratione repellendus ex optio, ipsum modi praesentium, saepe, quibusdam rem quaerat! Accusamus, saepe beatae!</p>   <button class="button">Buy now</button> </div>

If I create a style for the .title class, it will style both the elements containing the .card and .cta classes at the same time. We can use a compound selector (i.e. .card .title), but that raises the specificity which can lead to less maintainability. Or, we can take a BEM approach and rename our .title class to .card__title and .cta__title to isolate those elements a little more.

Locally scoped custom properties offer us a great solution though. We can apply them to the elements where they’ll be used:

.title {   color: var(--title-clr);   font-size: var(--title-fs); }  .button {   background: var(--button-bg);   border: var(--button-border);   color: var(--button-text); }

Then, we can control everything we need within their parent selectors, respectively:

.card {   --title-clr: #345;   --title-fs: 1.25rem;   --button-border: 0;   --button-bg: #333;   --button-text: white; }  .cta {   --title-clr: #f30;   --title-fs: 2.5rem;   --button-border: 0;   --button-bg: #333;   --button-text: white; }

Chances are, there are some defaults, or commonalities, between buttons or titles even when they are in different components. For that, we could build in fallbacks, or simply style those as we usually would.

.button {   /* Custom variables with default values */   border: var(--button-border, 0);    /* Default: 0 */   background: var(--button-bg, #333); /* Default: #333 */   color: var(--button-text, white);   /* Default: white */    /* Common styles every button will have */   padding: .5em 1.25em;   text-transform: uppercase;   letter-spacing: 1px; }

We could even use calc() to add a scale to our button, which would have the potential to remove the need for .btn-sm, btn-lg type classes (or it could be built into those classes, depending on the situation).

.button {   font-size: calc(var(--button-scale) * 1rem);   /* Multiply `--button-scale` by `1rem` to add unit */ }  .cta {   --button-scale: 1.5; }

Here is a more in-depth look at all of this in action:

See the Pen
Custom list icons with CSS Custom Properties
by Kevin (@kevinpowell)
on CodePen.

Notice in that example above that I have used some generic classes, such as .title and .button, which are styled with locally scoped properties (with the help of fallbacks). With those being setup with custom properties, I can define those locally within the parent selector, effectively giving each its own style without the need of an additional selector.

I also set up some pricing cards with modifier classes on them. Using the generic .pricing class, I set everything up, and then using modifier classes, I redefined some of the properties, such as --text, and --background, without having to worry about using compound selectors or additional classes.

By working this way, it makes for very maintainable code. It’s easy to go in and change the color of a property if we need to, or even come in and create a completely new theme or style, like the rainbow variation of the pricing card in the example.

It takes a bit of foresight when initially setting everything up, but the payoff can be awesome. It might even seem counter-intuitive to how you are used to approaching styles, but next time you go to create a custom property, try keeping it defined locally if it doesn’t need to live globally, and you’ll start to see how useful it can be.

The post Breaking CSS Custom Properties out of :root Might Be a Good Idea appeared first on CSS-Tricks.

CSS-Tricks

, , , , , ,
[Top]

Creating a Custom Element from Scratch

In the last article, we got our hands dirty with Web Components by creating an HTML template that is in the document but not rendered until we need it.

Next up, we’re going to continue our quest to create a custom element version of the dialog component below which currently only uses HTMLTemplateElement:

See the Pen
Dialog with template with script
by Caleb Williams (@calebdwilliams)
on CodePen.

So let’s push ahead by creating a custom element that consumes our template#dialog-template element in real-time.

Article Series:

  1. An Introduction to Web Components
  2. Crafting Reusable HTML Templates
  3. Creating a Custom Element from Scratch (This post)
  4. Encapsulating Style and Structure with Shadow DOM (Coming soon!)
  5. Advanced Tooling for Web Components (Coming soon!)

Creating a custom element

The bread and butter of Web Components are custom elements. The customElements API gives us a path to define custom HTML tags that can be used in any document that contains the defining class.

Think of it like a React or Angular component (e.g. <MyCard />), but without the React or Angular dependency. Native custom elements look like this: <my-card></my-card>. More importantly, think of it as a standard element that can be used in your React, Angular, Vue, [insert-framework-you’re-interested-in-this-week] applications without much fuss.

Essentially, a custom element consists of two pieces: a tag name and a class that extends the built-in HTMLElement class. The most basic version of our custom element would look like this:

class OneDialog extends HTMLElement {   connectedCallback() {     this.innerHTML = `<h1>Hello, World!</h1>`;   } }  customElements.define('one-dialog', OneDialog);

Note: throughout a custom element, the this value is a reference to the custom element instance.

In the example above, we defined a new standards-compliant HTML element, <one-dialog></one-dialog>. It doesn’t do much… yet. For now, using the <one-dialog> tag in any HTML document will create a new element with an <h1> tag reading “Hello, World!”

We are definitely going to want something more robust, and we’re in luck. In the last article, we looked at creating a template for our dialog and, since we will have access to that template, let’s utilize it in our custom element. We added a script tag in that example to do some dialog magic. let’s remove that for now since we’ll be moving our logic from the HTML template to inside the custom element class.

class OneDialog extends HTMLElement {   connectedCallback() {     const template = document.getElementById('one-dialog');     const node = document.importNode(template.content, true);     this.appendChild(node);   } }

Now, our custom element (<one-dialog>) is defined and the browser is instructed to render the content contained in the HTML template where the custom element is called.

Our next step is to move our logic into our component class.

Custom element lifecycle methods

Like React or Angular, custom elements have lifecycle methods. You’ve already been passively introduced to connectedCallback, which is called when our element gets added to the DOM.

The connectedCallback is separate from the element’s constructor. Whereas the constructor is used to set up the bare bones of the element, the connectedCallback is typically used for adding content to the element, setting up event listeners or otherwise initializing the component.

In fact, the constructor can’t be used to modify or manipulate the element’s attributes by design. If we were to create a new instance of our dialog using document.createElement, the constructor would be called. A consumer of the element would expect a simple node with no attributes or content inserted.

The createElement function has no options for configuring the element that will be returned. It stands to reason, then, that the constructor shouldn’t have the ability to modify the element that it creates. That leaves us with the connectedCallback as the place to modify our element.

With standard built-in elements, the element’s state is typically reflected by what attributes are present on the element and the values of those attributes. For our example, we’re going to look at exactly one attribute: [open]. In order to do this, we’ll need to watch for changes to that attribute and we’ll need attributeChangedCallback to do that. This second lifecycle method is called whenever one of the element constructor’s observedAttributes are updated.

That might sound intimidating, but the syntax is pretty simple:

class OneDialog extends HTMLElement {   static get observedAttributes() {     return ['open'];   }      attributeChangedCallback(attrName, oldValue, newValue) {     if (newValue !== oldValue) {       this[attrName] = this.hasAttribute(attrName);     }   }      connectedCallback() {     const template = document.getElementById('one-dialog');     const node = document.importNode(template.content, true);     this.appendChild(node);   } }

In our case above, we only care if the attribute is set or not, we don’t care about a value (this is similar to the HTML5 required attribute on inputs). When this attribute is updated, we update the element’s open property. A property exists on a JavaScript object whereas an attribute exists on an HTMLElement, this lifecycle method helps us keep the two in sync.

We wrap the updater inside the attributeChangedCallback inside a conditional checking to see if the new value and old value are equal. We do this to prevent an infinite loop inside our program because later we are going to create a property getter and setter that will keep the property and attributes in sync by setting the element’s attribute when the element’s property gets updated. The attributeChangedCallback does the inverse: updates the property when the attribute changes.

Now, an author can consume our component and the presence of the open attribute will dictate whether or not the dialog will be open by default. To make that a bit more dynamic, we can add custom getters and setters to our element’s open property:

class OneDialog extends HTMLElement {   static get boundAttributes() {     return ['open'];   }      attributeChangedCallback(attrName, oldValue, newValue) {     this[attrName] = this.hasAttribute(attrName);   }      connectedCallback() {     const template = document.getElementById('one-dialog');     const node = document.importNode(template.content, true);     this.appendChild(node);   }      get open() {     return this.hasAttribute('open');   }      set open(isOpen) {     if (isOpen) {       this.setAttribute('open', true);     } else {       this.removeAttribute('open');     }   } }

Our getter and setter will keep the open attribute (on the HTML element) and property (on the DOM object) values in sync. Adding the open attribute will set element.open to true and setting element.open to true will add the open attribute. We do this to make sure that our element’s state is reflected by its properties. This isn’t technically required, but is considered a best practice for authoring custom elements.

This does inevitably lead to a bit of boilerplate, but creating an abstract class that keeps the these in sync is a fairly trivial task by looping over the observed attribute list and using Object.defineProperty.

Now that we know whether or not our dialog is open, let’s add some logic to actually do the showing and hiding:

class OneDialog extends HTMLElement {     /** Omitted */   constructor() {     super();     this.close = this.close.bind(this);   }      set open(isOpen) {     this.querySelector('.wrapper').classList.toggle('open', isOpen);     this.querySelector('.wrapper').setAttribute('aria-hidden', !isOpen);     if (isOpen) {       this._wasFocused = document.activeElement;       this.setAttribute('open', '');       document.addEventListener('keydown', this._watchEscape);       this.focus();       this.querySelector('button').focus();     } else {       this._wasFocused && this._wasFocused.focus && this._wasFocused.focus();       this.removeAttribute('open');       document.removeEventListener('keydown', this._watchEscape);       this.close();     }   }      close() {     if (this.open !== false) {       this.open = false;     }     const closeEvent = new CustomEvent('dialog-closed');     this.dispatchEvent(closeEvent);   }      _watchEscape(event) {     if (event.key === 'Escape') {         this.close();        }   } }

There’s a lot going on here, but let’s walk through it. The first thing we do is grab our wrapper and toggle the .open class based on isOpen. To keep our element accessible, we need to toggle the aria-hidden attribute as well.

If the dialog is open, then we want to save a reference to the previously-focused element. This is to account for accessibility standards. We also add a keydown listener to the document called watchEscape that we have bound to the element’s this in the constructor in a pattern similar to how React handles method calls in class components.

We do this not only to ensure the proper binding for this.close, but also because Function.prototype.bind returns an instance of the function with the bound call site. By saving a reference to the newly-bound method in the constructor, we’re able to then remove the event when the dialog is disconnected (more on that in a moment). We finish up by focusing on our element and setting setting the focus on the proper element in our shadow root.

We also create a nice little utility method for closing our dialog that dispatches a custom event alerting some listener that the dialog has been closed.

If the element is closed (i.e. !open), we check to make sure the this._wasFocused property is defined and has a focus method and call that to return the user’s focus back to the regular DOM. Then we remove our event listener to avoid any memory leaks.

Speaking of cleaning up after ourselves, that takes us to yet another lifecycle method: disconnectedCallback. The disconnectedCallback is the inverse of the connectedCallback in that the method is called once the element is removed from the DOM and allows us to clean up any event listeners or MutationObservers attached to our element.

It just so happens we have a few more event listeners to wire up:

class OneDialog extends HTMLElement {   /** Omitted */      connectedCallback() {         this.querySelector('button').addEventListener('click', this.close);     this.querySelector('.overlay').addEventListener('click', this.close);   }      disconnectedCallback() {     this.querySelector('button').removeEventListener('click', this.close);     this.querySelector('.overlay').removeEventListener('click', this.close);   }   }

Now we have a well-functioning, mostly accessible dialog element. There are a few bits of polish we can do, like capturing focus on the element, but that’s outside the scope of what we’re trying to learn here.

There is one more lifecycle method that doesn’t apply to our element, the adoptedCallback, which fires when the element is adopted into another part of the DOM.

In the following example, you will now see that our template element is being consumed by a standard <one-dialog> element.

See the Pen
Dialog example using template
by Caleb Williams (@calebdwilliams)
on CodePen.

Another thing: non-presentational components

The <one-template> we have created so far is a typical custom element in that it includes markup and behavior that gets inserted into the document when the element is included. However, not all elements need to render visually. In the React ecosystem, components are often used to manage application state or some other major functionality, like <Provider /> in react-redux.

Let’s imagine for a moment that our component is part of a series of dialogs in a workflow. As one dialog is closed, the next one should open. We could make a wrapper component that listens for our dialog-closed event and progresses through the workflow.

class DialogWorkflow extends HTMLElement {   connectedCallback() {     this._onDialogClosed = this._onDialogClosed.bind(this);     this.addEventListener('dialog-closed', this._onDialogClosed);   }    get dialogs() {     return Array.from(this.querySelectorAll('one-dialog'));   }    _onDialogClosed(event) {     const dialogClosed = event.target;     const nextIndex = this.dialogs.indexOf(dialogClosed);     if (nextIndex !== -1) {       this.dialogs[nextIndex].open = true;     }   } }

This element doesn’t have any presentational logic, but serves as a controller for application state. With a little effort, we could recreate a Redux-like state management system using nothing but a custom element that could manage an entire application’s state in the same one that React’s Redux wrapper does.

That’s a deeper look at custom elements

Now we have a pretty good understanding of custom elements and our dialog is starting to come together. But it still has some problems.

Notice that we’ve had to add some CSS to restyle the dialog button because our element’s styles are interfering with the rest of the page. While we could utilize naming strategies (like BEM) to ensure our styles won’t create conflicts with other components, there is a more friendly way of isolating styles. Spoiler! It’s shadow DOM and that’s what we’re going to look at in the next part of this series on Web Components.

Another thing we need to do is define a new template for every component or find some way to switch templates for our dialog. As it stands, there can only be one dialog type per page because the template that it uses must always be present. So either we need some way to inject dynamic content or a way to swap templates.

In the next article, we will look at ways to increase the usability of the <one-dialog> element we just created by incorporating style and content encapsulation using the shadow DOM.

Article Series:

  1. An Introduction to Web Components
  2. Crafting Reusable HTML Templates
  3. Creating a Custom Element from Scratch (This post)
  4. Encapsulating Style and Structure with Shadow DOM (Coming soon!)
  5. Advanced Tooling for Web Components (Coming soon!)

The post Creating a Custom Element from Scratch appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

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]

Extending Google Analytics on CSS-Tricks with Custom Dimensions

The idea for this article sparked when Chris wrote this in Thank You (2018 Edition):

I almost wish our URLs had years in them because I still don’t have a way to scope analytic data to only show me data from content published this year. I can see the most popular stuff from the year, but that’s regardless of when it was published, and that’s dominated by the big guides we’ve had for years and keep updated.

I have been a long-time reader of CSS-Tricks, but have not yet had something to contribute with. Until now. Being a Google Analytics specialist by day, this was at last something I could contribute to CSS-Tricks. Let’s extend Google Analytics on CSS-Tricks!

Enter Google Analytics custom dimensions

Google Analytics gives you a lot of interesting insights about what visitors are doing on a website, just by adding the basic Google Analytics snippet to every page.

But Google Analytics is a one-size-fits-all tool.

In order to make it truly meaningful for a specific website like CSS-Tricks we can add additional meta information to our Google Analytics data.

The year an article was posted is an example of such meta data that Google Analytics does not have out of the box, but it’s something that is easily added to make the data much more useful. That’s where custom dimensions come in.

Create the custom dimension in Google Analytics

The first thing to do is create the new custom dimension. In the Google Analytics UI, click the gear icon, click Custom Definitions and then click Custom Dimensions.

Google Analytics admin interface

This shows a list of any existing custom dimensions. Click the red button to create a new custom dimension.

Custom dimensions overview

Let’s give the custom dimension a descriptive name. In this case, “year” seems quite appropriate since that’s what we want to measure.

The scope is important because it defines how the meta data should be applied to the existing data. In this case, the article year is related to each article the user is viewing, so we need to set it to the “hit” scope.

Another example would be meta data about the entire session, like if the user is logged in, that would be saved in a session-scoped custom dimension.

Alright, let’s save our dimension.

When the custom dimension is created, Google Analytics provides examples for how to implement it using JavaScript. We’re allowed up to 20 custom dimensions and each custom dimension is identified by an index. In this case, “year” is the first custom dimension, so it was created in Index 1 (see dimension1 in the JavaScript code below).

Custom dimension created at Index 1

If we had other custom dimensions defined, then those would live in another index. There is no way to change the index of a custom dimension, so take note of the one being used. A list of all indices can always be found in the overview:

That’s it, now it’s time to code!

Now we have to extract the article year in the code and add it to the payload so that it is sent to Google Analytics with the page view hit.

This is the code we need to execute, per the snippet we were provided when creating the custom dimension:

var dimensionValue = 'SOME_DIMENSION_VALUE'; ga('set', 'dimension1', dimensionValue);

Here is the tricky part. The ga() function is created when the Google Analytics snippet is loaded. In order to minimize the performance hit, it is placed at the bottom of the page on CSS-Tricks. This is what the basic Google Analytics snippet looks like:

<script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-12345-1', 'auto'); ga('send', 'pageview'); </script>

We need to set the custom dimension value after the snippet is parsed and before the page view hit is sent to Google Analytics. Hence, we need to set it here:

// ... ga('create', 'UA-12345-1', 'auto'); ga('set', 'dimension1', dimensionValue); // Set the custom dimension value ga('send', 'pageview');

This code is usually placed outside a WordPress Loop, but that’s where we would have access to meta information like the article year. Because of this, we need to store the article year in a JavaScript variable inside the loop, then reference that variable in the Google Analytics snippet when we get to the bottom of the page.

Save the article year within the loop

In WordPress, a standard loop starts here:

<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>

…and ends here:

<?php endwhile; else : ?> 	<p><?php esc_html_e( 'Sorry, no posts matched your criteria.' ); ?></p> <?php endif; ?>

Somewhere between those lines, we extract the year and save it in a JavaScript variable:

<script> 	var articleYear = "<?php the_time('Y') ?>"; </script>

Reference the article year in the Google Analytics snippet

The Google Analytics snippets is placed on all pages of the website, but the year does not make sense for all pages (e.g. the homepage). Being the good JavaScript developers that we are, we will check if the variable has been defined in order to avoid any errors.

ga('create', 'UA-68528-29', 'auto'); if (typeof articleYear !== "undefined") { 		ga('set', 'dimension1', articleYear); } ga('send', 'pageview');

That’s it! The Google Analytics page view hit will now include the article year for all pages where it is defined.

Custom dimensions do not apply to historical data

One thing to know about custom dimension — or any other modifications to your Google Analytics data — is that they only apply to new data being collected from the website. The custom dimensions described in this article was implemented in January 2019, and that means if we look at data from 2018 it will not have any data for the custom dimensions.

This is important to keep in mind for the rest of this article, when we begin to look into the data. The custom dimensions are added to all posts on CSS-Tricks, going all the way back to 2007, but we are only looking at page views that happened in 2019 — after the custom dimensions was implemented. For example, when we look at articles from 2011, we are not looking at page views in 2011 — we are looking at page views of posts from 2011 in 2019. This is important to keep in mind, when we start to look at posts from previous years.

All set? OK, let’s take a look at the new data!

Viewing the data in Google Analytics

The easiest way to see the new data is to go to Behavior → Site Content → All Pages, which will show the most viewed pages:

All Pages report

In the dropdown above the table, select “year” as a secondary dimension.

Year as secondary dimension

That gives us a table like the one below, showing the year for all articles. Notice how the homepage, which is the second most viewed page, is removed from the table because it does not have a year associated with it.

We start to get a better understanding of the website. The most viewed page (by far) is the complete guide to Flexbox which was published back in 2013. Talk about evergreen content!

Table with year as secondary dimension

Secondary is good, primary is better

OK, so the above table adds some understanding of the most viewed pages, but let’s flip the dimensions so that year is the primary dimension. There is no standard report for viewing custom dimensions as the primary dimension, so we need to create a custom report.

Custom Reports overview

Give the Custom Report a good name. Finding the metrics (blue) and dimensions (green) is easiest by searching.

Create the Custom Report

Here is what the final Custom Report should look like, with some useful metrics and dimensions. Notice how we have selected Page below Year. This will become useful in a second.

The final Custom Report

Once we hit Save, we see the aggregate numbers for all article years. 2013 is still on top, but we now see that 2011 also had some great content, which was not in the top 10 lists we previously looked at. This suggests that no single article from 2011 stood out, but in total, 2011 had some great articles that still receive a lot of views in 2019.

Aggregated numbers for article years

The percentage next to the number of page views is the percentage of the total page views. Notice how 2018 “only” accounts for 8.11% of all page views and 2019 accounts for 6.24%. This is not surprising, but shows that CSS-Tricks is partly a huge success because of the vast amount of strong reference material posted over the years, which users keep referring to.

Let’s look into 2011.

Remember how we set up the Custom Report with the Page below the Year in dimensions? This means we can now click 2011 and drill-down into that year.

It looks like a lot of almanac pages were published in 2011, which in aggregate has a lot of page views. Notice the lower-right corner where it says “1-10 of 375.” This means that 375 articles from 2011 have been viewed on the site in 2019. That is impressive!

Back to the question: Great content from 2018

Before I forget: Let’s answer that initial question from Chris.

Let’s scope the analytics data to content published this year (2018). Here are the top 10 posts:

Top 10 posts published in 2018

Understanding the two-headed beast

In Thank You (2018 Edition), Chris also wrote:

For the last few years, I’ve been trying to think of CSS-Tricks as this two-headed beast. One head is that we’re trying to produce long-lasting referential content. We want to be a site that you come to or land on to find answers to front-end questions. The other head is that we want to be able to be read like a magazine. Subscribe, pop by once a week, snag the RSS feed… whatever you like, we hope CSS-Tricks is interesting to read as a hobbyist magazine or industry rag.

Let’s dig into that with another custom dimension: Post type. CSS-Tricks uses a number of custom post types like videos, almanac entries, and snippets in addition to the built-in post types, like posts or pages.

Let’s also extract that, like we did with the article year:

<script> 	var articleYear = "<?php the_time('Y') ?>"; 	var articleType = "<?php get_post_type($ post->ID) ?>"; </script>

We’ll save it into custom dimension Index 2, which is hit-scoped just like we did with year. Now we can build a new custom report like this:

Custom post types

Now we know that blog posts account for 55% of page views, while snippets and almanac (the long-lasting referential content) account for 44%.

Now, blog posts can also be referential content, so it is safe to say that at least half of the traffic on CSS-Tricks is coming because of the referential content.

From a one-man band to a 333-author content team

When CSS-Tricks started in 2007 it was just Chris. At the time of writing, 333 authors have contributed.

Let’s see how those authors have contributed to the page views on CSS-Tricks using — you probably guessed it — another custom dimension!

<script> 	var articleYear = "<?php the_time('Y') ?>"; 	var articleAuthor = "<?php the_author() ?>"; 	var articleType = "<?php get_post_type($ post->ID) ?>"; </script>

Here are the top 10 most viewed authors in 2019.

Top 10 authors on CSS-Tricks

Let’s break this down even further by year with a secondary dimension and select 500 rows in the lower-right corner, so we get all 465 rows.

Top 10 authors and year

We can then export the data to Excel and make a pivot table of the data, counting authors per year.

Excel pivot table with count of authors per year

You like charts? We can make one with some beautiful v17 colors, showing the number of authors per year.

Authors per year

It is amazing to see the steady growth in authors contributing to CSS-Tricks per year. And given 2019 already has 33 different authors, it looks like 2019 could set a new record.

But are those new authors generating any page views?

Let’s make a new pivot chart where we compare Chris to all other authors.

Pivot table comparing page views

…and then chart that over time.

Share of page views by author per year

It definitely looks like CSS-Tricks is becoming a multi-author site. While Chris is still the #1 author, it is good to see that the constant flow of new high-quality content does not solely depend on him, which is a good trend for CSS-Tricks and makes it possible to cover a lot more topics going forward.

But what happened in 2011, you might ask? Let’s have a look. In a custom report, you can have five levels of dimensions. For now we will stick with four.

Custom report with four dimensions to drill into

Now we can click on the year 2011 and get the list of authors.

2011 authors

Hello Sara Cope! What awesome content did you write in 2011?

Sara Cope almanac pages

Looks like a lot of those almanac pages we saw earlier. Click that!

107 almanac pages by Sara Cope

Indeed, a lot of almanac pages! 107 to be exact. A lot of great content that still receives lots of page views in 2019 to boot.

Summary

Google Analytics is a powerful tool to understand what users are doing on your website, and with a little work, meta data that is specific to your website can make it extra powerful. As seen in this article, adding a few simple meta data that’s already accessible in WordPress can unlock a world of opportunities to analyze and add a whole new dimension of knowledge about the content and visitors of a site, like we did here on CSS-Tricks.


If you’re interested in another similar journey involving custom dimensions and making Google Analytics data way more useful, check out Chris Coyier and Philip Walton in Learning to Use Google Analytics More Effectively at CodePen.

The post Extending Google Analytics on CSS-Tricks with Custom Dimensions appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]