A selfie of me hiking on snowy Mt. Rainier. I am smiling while wearing my hat and sunglasses. Behind my head, in the distance, you can see the looming peak of Mt. Adams.

Mettle — ProSource Design System

Mettle is a robust design system that unifies ProSource’s digital experiences and expedites development cycles.

A high-fidelity mockup of the color system in Mettle, ProSource's design system.
My role
  • Design Engineer
  • Front-End Developer
  • Product Designer
Tools
  • AstroJS
  • Figma
  • Tailwind CSS
Timeline

May 2021–Present

Published

Overview

Met­tle is a de­sign sys­tem con­ceived to sim­plify the ideation and de­vel­op­ment cy­cles for ProSource’s web projects and unify cus­tomer ex­pe­ri­ences across all sur­faces. The name Met­tle was cho­sen af­fec­tion­ately for its dou­ble en­ten­dre: the name sounds like the word “metal,” which many tech­nol­ogy prod­ucts are cre­ated from; and, it also sig­ni­fies the plucky spirit em­bod­ied by our com­pany’s small but ef­fec­tive team.

The de­sign sys­tem is not as com­pre­hen­sive like oth­ers for large, en­ter­prise com­pa­nies. In­stead, it pro­vides a solid core upon which all de­signs, lay­outs, and vari­ants are built upon.

Goals

While it may seem per­plex­ing for such a small com­pany to adopt a project like this, I had a few goals in mind when I began:

  • Cre­ate a co­he­sive vi­sual lan­guage across ProSource’s sur­faces (at the time of writ­ing, this is 5 web­sites) and a hand­ful of email tem­plates.
  • Sim­plify front-​end de­vel­op­ment for me by draw­ing from a uni­fied source of truth.
  • De­liver a con­sis­tent and high-​quality ex­pe­ri­ence for end users.

Process

Be­cause I am a de­sign and de­vel­op­ment team of one, I opted for a base UI kit to give me a solid frame­work and ex­pe­dite the de­vel­op­ment process. Based on the amount of rec­om­men­da­tions, I used Jor­dan Hughes’ Un­ti­tled UI Figma kit as the start­ing point for Met­tle.

Ty­pog­ra­phy

I used the type scale built into Un­ti­tled UI. It is nearly iden­ti­cal to the type scale used in the pop­u­lar CSS frame­work, Tail­wind CSS. This re­duced the cog­ni­tive load mov­ing from de­sign to de­vel­op­ment and kicked off the project with a shared start­ing point.

Spac­ing

Spaces were based on the CSS unit, rems. And, as is com­m­mon with many de­signs, I used a mul­ti­plier based on 8px (1rem = 16px, or 2 × 8px). Again, Un­ti­tled UI mir­rored the base setup of Tail­wind CSS in this way, too, which al­lowed me to quickly im­ple­ment this de­sign sys­tem.

Col­ors

The color scales are to­kens rang­ing from a 50–900. Gen­er­ally, the base color would be as­signed the token 500 with shades in­cre­ment­ing up and tints decre­ment­ing down from this mid­point. The only ex­cep­tion to this rule of thumb was ac­tu­ally our pri­mary color, which is indigo.600. In ad­di­tion to our pri­mary color scale, I used a slate gray scale, two sec­on­daries, and three scales for suc­cess, error, and warn­ing UI el­e­ments.

Tak­ing it a step fur­ther, I cre­ated a few choice gra­di­ents from the gray and pri­mary (in­digo) color scales. I also cre­ated a few guide­lines for using the gray and pri­mary col­ors as back­ground and text, en­sur­ing a min­i­mum of WCAG 2.0 AA con­trast.

Front-​End De­vel­op­ment

Trans­lat­ing the de­sign sys­tem in Figma into a smooth de­vel­oper ex­pe­ri­ence was rel­a­tively easy. Using Tail­wind CSS, I cre­ated a base theme con­fig­u­ra­tion that I can im­port into any project. Both the web frame­work and email frame­work I use sup­port Tail­wind CSS out of the box, so I only needed one con­fig­u­ra­tion for both en­vi­ron­ments.

Email tem­plates using Maiz­zle

I use Maiz­zle to de­velop email tem­plates. As men­tioned, this al­lows me to use the same Tail­wind con­fig­u­ra­tion that I use for web­sites. The build script for our email tem­plates in­lines all of the cus­tom styles so that they ren­der prop­erly in var­i­ous email clients, like Out­look and Gmail.

Com­po­nent li­brary in Astro

Astro is a next-​gen web frame­work using is­land ar­chi­tec­ture to de­liver top-​notch DX with­out sac­ri­fic­ing blaz­ing fast speed. All of the web­sites I de­velop for ProSource use Astro. Build­ing a col­lec­tion of tool­ing con­fig­u­ra­tions, icons, and com­po­nents was rel­a­tively easy. In fact, I based most of the ar­chi­tec­ture on Astro’s own Site Kit repos­i­tory.

Ex­per­i­ments

As the de­sign sys­tem and front-​end li­brary ma­tures, I am sim­pli­fy­ing the amount of rep­e­ti­tion in the Figma file and GitHub repos­i­tory. In Figma, I use vari­ant prop­er­ties to stream­line prop­er­ties and states, like size, color, and hover.

In the li­brary, I am test­ing a pack­age called Class Vari­ance Au­thor­ity to stream­line the logic be­tween prop­er­ties and states.

Here’s a very early ex­am­ple for a But­ton using CVA, Tail­wind CSS, and Astro:

---
import { cva, type VariantProps } from "class-variance-authority"

const button = cva(
	{/* Base button styles */}
	"transition-colors duration-150 ease-in-out w-full text-center sm:w-auto whitespace-nowrap",
	{
		{/* Styles for button variants */}
		variants: {
			{/* Primary/secondary button types (colors) */}
			type: {
				primary:
					"bg-indigo-600 hover:bg-indigo-700 active:bg-indigo-700 text-white border border-indigo-600 hover:border-indigo-700 active:border-indigo-700 outline-none ring-0 focus:ring-4 focus:ring-indigo-100",
				secondary:
					"bg-white hover:bg-gray-50 active:bg-gray-50 text-gray-700 border border-gray-300 outline-none ring-0 focus:ring-4 focus:ring-gray-100",
			},
			{/* The size of the button, based on the Figma design system */}
			size: {
				small: "py-2 px-[14px] text-sm font-bold shadow-sm rounded-lg",
				medium: "px-4 py-[10px] text-sm font-bold shadow-sm rounded-lg",
				large: "px-[18px] py-[9px] text-base font-medium shadow-sm rounded-lg",
				xlarge: "px-5 py-3 text-base font-medium shadow-sm rounded-lg",
				xxlarge: "px-7 py-[17px] text-base font-medium shadow-lg rounded-lg",
			},
			{/* Styles if an icon is used on the button */}
			icon: {
				yes: "inline-flex gap-2",
				no: "inline-block",
			},
			{/* Styles when the button is in a disabled state */}
			disabled: {
				true: "cursor-not-allowed opacity-60",
				false: "",
			},
		},
		{/* Compound variants are a default state when using the button component */}
		compoundVariants: [{ type: "primary", size: "medium", icon: "no", disabled: false }],
	},
)

export interface Props extends VariantProps<typeof button>, astroHTML.JSX.HTMLAttributes {}

const {
	type = "primary",
	size = "medium",
	disabled = false,
	class: className = "",
	...attrs
} = Astro.props

{/* If the `href` passed to the button contains 'https:', it is considered an external link */}
attrs.href && attrs.href.toString().includes("https:")
	? (Astro.props.external = true)
	: (Astro.props.external = false)

{/* Slot management for icons before/after button text */}
const icon = Astro.slots.has("before") || Astro.slots.has("after") ? "yes" : "no"
---

<a
	href={attrs.href}
	class={button({ type, size, icon, disabled })}
	target={attrs.external ? "_blank" : ""}
	rel={attrs.external ? "nofollow noopener noreferer" : ""}
>
	<slot name="before" />
	<slot />
	<slot name="after" />
</a>

More work needs to be done to in­te­grate CVA into Met­tle, but the re­sults so far have been promis­ing.

Interested in working together?

Reach out to learn more about me and/or my work.